Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ ffmpeg_build/
_dialyzer/
.cache/
compile_commands.json
priv/
31 changes: 27 additions & 4 deletions c_src/xav/reader.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
#include "reader.h"
#include "libavutil/rational.h"
#include "utils.h"
#include <libavutil/samplefmt.h>
#include <libavutil/version.h>

#include <inttypes.h>

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
const char *driver = "dshow";
#elif defined(__APPLE__)
const char *driver = "avfoundation";
#elif defined(__linux__)
const char *driver = "v4l2";
#endif

static int init_converter(struct Reader *reader);

struct Reader *reader_alloc() {
Expand All @@ -24,7 +33,7 @@ struct Reader *reader_alloc() {
}

int reader_init(struct Reader *reader, unsigned char *path, size_t path_size, int device_flag,
enum AVMediaType media_type) {
enum AVMediaType media_type, AVRational framerate, unsigned int width, unsigned int height) {
int ret;
reader->path = XAV_ALLOC(path_size + 1);
memcpy(reader->path, path, path_size);
Expand All @@ -34,13 +43,27 @@ int reader_init(struct Reader *reader, unsigned char *path, size_t path_size, in

if (device_flag == 1) {
avdevice_register_all();
reader->input_format = av_find_input_format("v4l2");
av_dict_set(&reader->options, "framerate", "10", 0);

reader->input_format = av_find_input_format(driver);

if (framerate.den != 0 && framerate.num != 0) {
char framerate_str[32];
snprintf(framerate_str, sizeof(framerate_str), "%d/%d", framerate.num,
framerate.den);
av_dict_set(&reader->options, "framerate", framerate_str, 0);
}

if (width != 0 && height != 0) {
char resolution_str[32];
snprintf(resolution_str, sizeof(resolution_str), "%dx%d", width, height);
av_dict_set(&reader->options, "video_size", resolution_str, 0);
}
}

XAV_LOG_DEBUG("Trying to open %s", reader->path);

if (avformat_open_input(&reader->fmt_ctx, reader->path, reader->input_format, NULL) < 0) {
if (avformat_open_input(&reader->fmt_ctx, reader->path, reader->input_format, &reader->options) <
0) {
return -1;
}

Expand Down
3 changes: 2 additions & 1 deletion c_src/xav/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdlib.h>
#include <string.h>

#include "libavutil/rational.h"
#include "utils.h"

struct Reader {
Expand All @@ -29,7 +30,7 @@ struct Reader {
struct Reader *reader_alloc();

int reader_init(struct Reader *reader, unsigned char *path, size_t path_size, int device_flag,
enum AVMediaType media_type);
enum AVMediaType media_type, AVRational framerate, unsigned int width, unsigned int height);

int reader_next_frame(struct Reader *reader);

Expand Down
32 changes: 29 additions & 3 deletions c_src/xav/xav_reader.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ static int init_video_converter(struct XavReader *xav_reader, AVFrame *frame);
ErlNifResourceType *xav_reader_resource_type;

ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
if (argc != 6) {
if (argc != 9) {
return xav_nif_raise(env, "invalid_arg_count");
}

Expand Down Expand Up @@ -53,6 +53,32 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
return xav_nif_raise(env, "invalid_out_channels");
}

const ERL_NIF_TERM *framerate_elements;
int framerate_elements_arity;
AVRational framerate;
if (!enif_get_tuple(env, argv[6], &framerate_elements_arity, &framerate_elements)) {
return xav_nif_raise(env, "invalid_framerate_tuple");
}
if (framerate_elements_arity != 2) {
return xav_nif_raise(env, "invalid_framerate_tuple_size");
}
if (!enif_get_int(env, framerate_elements[0], &framerate.num)){
return xav_nif_raise(env, "invalid_framerate_num");
}
if (!enif_get_int(env, framerate_elements[1], &framerate.den)){
return xav_nif_raise(env, "invalid_framerate_den");
}

int width;
if (!enif_get_int(env, argv[7], &width) || width < 0) {
return xav_nif_raise(env, "invalid_width");
}

int height;
if (!enif_get_int(env, argv[8], &height) || height < 0) {
return xav_nif_raise(env, "invalid_height");
}

struct XavReader *xav_reader =
enif_alloc_resource(xav_reader_resource_type, sizeof(struct XavReader));
xav_reader->reader = NULL;
Expand All @@ -67,7 +93,7 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
return xav_nif_raise(env, "couldnt_allocate_reader");
}

int ret = reader_init(xav_reader->reader, bin.data, bin.size, device_flag, media_type);
int ret = reader_init(xav_reader->reader, bin.data, bin.size, device_flag, media_type, framerate, width, height);

if (ret == -1) {
return xav_nif_error(env, "couldnt_open_avformat_input");
Expand Down Expand Up @@ -301,7 +327,7 @@ void free_xav_reader(ErlNifEnv *env, void *obj) {
}
}

static ErlNifFunc xav_funcs[] = {{"new", 6, new},
static ErlNifFunc xav_funcs[] = {{"new", 9, new},
{"next_frame", 1, next_frame, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"seek", 2, seek, ERL_NIF_DIRTY_JOB_CPU_BOUND}};

Expand Down
30 changes: 26 additions & 4 deletions lib/xav/reader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ defmodule Xav.Reader do
out_channels: [
type: :pos_integer,
doc: "The output number of channels of the audio samples"
]
],
framerate: [
type: {:tuple, [:non_neg_integer, :non_neg_integer]},
default: {0,0},
doc:
"the framerate a a 2-element tuple with the first element beign the nominator and the second element the denominator. Will only be used when reading from a device."
],
width: [type: :non_neg_integer, default: 0, doc: "the width of the device resolution. Only used when reading from a device."],
height: [type: :non_neg_integer,default: 0, doc: "the height of the device resolution. Only used when reading from a device."]
]

@type t() :: %__MODULE__{
Expand Down Expand Up @@ -66,8 +74,16 @@ defmodule Xav.Reader do
Creates a new audio/video reader.

Both reading from a file and from a video camera are supported.
In case of using a video camera, the v4l2 driver is required, and FPS are
locked to 10.

Linux:
In case of using a video camera, the v4l2 driver is required.

macOS:
In case of using a video camera, the avfoundation driver is required.
The name of the device can be found with `ffmpeg -f avfoundation -list_devices true -i ""`

Windows:
In case of using a video camera, the dshow driver is required.

Microphone input is not supported.

Expand Down Expand Up @@ -142,14 +158,20 @@ defmodule Xav.Reader do
defp do_create_reader(path, opts) do
out_sample_rate = opts[:out_sample_rate] || 0
out_channels = opts[:out_channels] || 0
framerate = opts[:framerate]
width = opts[:width]
height = opts[:height]

case Xav.Reader.NIF.new(
path,
to_int(opts[:device?]),
to_int(opts[:read]),
opts[:out_format],
out_sample_rate,
out_channels
out_channels,
framerate,
width,
height
) do
{:ok, reader, in_format, out_format, in_sample_rate, out_sample_rate, in_channels,
out_channels, bit_rate, duration, codec} ->
Expand Down
2 changes: 1 addition & 1 deletion lib/xav/reader_nif.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule Xav.Reader.NIF do
:ok = :erlang.load_nif(path, 0)
end

def new(_path, _device, _video, _out_format, _out_sample_rate, _out_channels),
def new(_path, _device, _video, _out_format, _out_sample_rate, _out_channels, _framerate, _width, _height),
do: :erlang.nif_error(:undef)

def next_frame(_reader), do: :erlang.nif_error(:undef)
Expand Down