Skip to content

Commit b7efda3

Browse files
Add support for MacOS devices in reader (#42)
* feat: add support for avfoundation (macOS) * feat: add framerate and resolution selection to device input * remove custom os flag from Makefile * refactor: move driver definitions to top of file * feat: remove default settings for framerate and video_size * refactor: use explixit defined for every os flag
1 parent 1fefebd commit b7efda3

File tree

6 files changed

+86
-13
lines changed

6 files changed

+86
-13
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ ffmpeg_build/
3131
_dialyzer/
3232
.cache/
3333
compile_commands.json
34+
priv/

c_src/xav/reader.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
#include "reader.h"
2+
#include "libavutil/rational.h"
23
#include "utils.h"
34
#include <libavutil/samplefmt.h>
45
#include <libavutil/version.h>
56

67
#include <inttypes.h>
78

9+
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
10+
const char *driver = "dshow";
11+
#elif defined(__APPLE__)
12+
const char *driver = "avfoundation";
13+
#elif defined(__linux__)
14+
const char *driver = "v4l2";
15+
#endif
16+
817
static int init_converter(struct Reader *reader);
918

1019
struct Reader *reader_alloc() {
@@ -24,7 +33,7 @@ struct Reader *reader_alloc() {
2433
}
2534

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

3544
if (device_flag == 1) {
3645
avdevice_register_all();
37-
reader->input_format = av_find_input_format("v4l2");
38-
av_dict_set(&reader->options, "framerate", "10", 0);
46+
47+
reader->input_format = av_find_input_format(driver);
48+
49+
if (framerate.den != 0 && framerate.num != 0) {
50+
char framerate_str[32];
51+
snprintf(framerate_str, sizeof(framerate_str), "%d/%d", framerate.num,
52+
framerate.den);
53+
av_dict_set(&reader->options, "framerate", framerate_str, 0);
54+
}
55+
56+
if (width != 0 && height != 0) {
57+
char resolution_str[32];
58+
snprintf(resolution_str, sizeof(resolution_str), "%dx%d", width, height);
59+
av_dict_set(&reader->options, "video_size", resolution_str, 0);
60+
}
3961
}
4062

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

43-
if (avformat_open_input(&reader->fmt_ctx, reader->path, reader->input_format, NULL) < 0) {
65+
if (avformat_open_input(&reader->fmt_ctx, reader->path, reader->input_format, &reader->options) <
66+
0) {
4467
return -1;
4568
}
4669

c_src/xav/reader.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <stdlib.h>
1111
#include <string.h>
1212

13+
#include "libavutil/rational.h"
1314
#include "utils.h"
1415

1516
struct Reader {
@@ -29,7 +30,7 @@ struct Reader {
2930
struct Reader *reader_alloc();
3031

3132
int reader_init(struct Reader *reader, unsigned char *path, size_t path_size, int device_flag,
32-
enum AVMediaType media_type);
33+
enum AVMediaType media_type, AVRational framerate, unsigned int width, unsigned int height);
3334

3435
int reader_next_frame(struct Reader *reader);
3536

c_src/xav/xav_reader.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ static int init_video_converter(struct XavReader *xav_reader, AVFrame *frame);
66
ErlNifResourceType *xav_reader_resource_type;
77

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

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

56+
const ERL_NIF_TERM *framerate_elements;
57+
int framerate_elements_arity;
58+
AVRational framerate;
59+
if (!enif_get_tuple(env, argv[6], &framerate_elements_arity, &framerate_elements)) {
60+
return xav_nif_raise(env, "invalid_framerate_tuple");
61+
}
62+
if (framerate_elements_arity != 2) {
63+
return xav_nif_raise(env, "invalid_framerate_tuple_size");
64+
}
65+
if (!enif_get_int(env, framerate_elements[0], &framerate.num)){
66+
return xav_nif_raise(env, "invalid_framerate_num");
67+
}
68+
if (!enif_get_int(env, framerate_elements[1], &framerate.den)){
69+
return xav_nif_raise(env, "invalid_framerate_den");
70+
}
71+
72+
int width;
73+
if (!enif_get_int(env, argv[7], &width) || width < 0) {
74+
return xav_nif_raise(env, "invalid_width");
75+
}
76+
77+
int height;
78+
if (!enif_get_int(env, argv[8], &height) || height < 0) {
79+
return xav_nif_raise(env, "invalid_height");
80+
}
81+
5682
struct XavReader *xav_reader =
5783
enif_alloc_resource(xav_reader_resource_type, sizeof(struct XavReader));
5884
xav_reader->reader = NULL;
@@ -67,7 +93,7 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
6793
return xav_nif_raise(env, "couldnt_allocate_reader");
6894
}
6995

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

7298
if (ret == -1) {
7399
return xav_nif_error(env, "couldnt_open_avformat_input");
@@ -301,7 +327,7 @@ void free_xav_reader(ErlNifEnv *env, void *obj) {
301327
}
302328
}
303329

304-
static ErlNifFunc xav_funcs[] = {{"new", 6, new},
330+
static ErlNifFunc xav_funcs[] = {{"new", 9, new},
305331
{"next_frame", 1, next_frame, ERL_NIF_DIRTY_JOB_CPU_BOUND},
306332
{"seek", 2, seek, ERL_NIF_DIRTY_JOB_CPU_BOUND}};
307333

lib/xav/reader.ex

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,15 @@ defmodule Xav.Reader do
3030
out_channels: [
3131
type: :pos_integer,
3232
doc: "The output number of channels of the audio samples"
33-
]
33+
],
34+
framerate: [
35+
type: {:tuple, [:non_neg_integer, :non_neg_integer]},
36+
default: {0,0},
37+
doc:
38+
"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."
39+
],
40+
width: [type: :non_neg_integer, default: 0, doc: "the width of the device resolution. Only used when reading from a device."],
41+
height: [type: :non_neg_integer,default: 0, doc: "the height of the device resolution. Only used when reading from a device."]
3442
]
3543

3644
@type t() :: %__MODULE__{
@@ -66,8 +74,16 @@ defmodule Xav.Reader do
6674
Creates a new audio/video reader.
6775
6876
Both reading from a file and from a video camera are supported.
69-
In case of using a video camera, the v4l2 driver is required, and FPS are
70-
locked to 10.
77+
78+
Linux:
79+
In case of using a video camera, the v4l2 driver is required.
80+
81+
macOS:
82+
In case of using a video camera, the avfoundation driver is required.
83+
The name of the device can be found with `ffmpeg -f avfoundation -list_devices true -i ""`
84+
85+
Windows:
86+
In case of using a video camera, the dshow driver is required.
7187
7288
Microphone input is not supported.
7389
@@ -142,14 +158,20 @@ defmodule Xav.Reader do
142158
defp do_create_reader(path, opts) do
143159
out_sample_rate = opts[:out_sample_rate] || 0
144160
out_channels = opts[:out_channels] || 0
161+
framerate = opts[:framerate]
162+
width = opts[:width]
163+
height = opts[:height]
145164

146165
case Xav.Reader.NIF.new(
147166
path,
148167
to_int(opts[:device?]),
149168
to_int(opts[:read]),
150169
opts[:out_format],
151170
out_sample_rate,
152-
out_channels
171+
out_channels,
172+
framerate,
173+
width,
174+
height
153175
) do
154176
{:ok, reader, in_format, out_format, in_sample_rate, out_sample_rate, in_channels,
155177
out_channels, bit_rate, duration, codec} ->

lib/xav/reader_nif.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule Xav.Reader.NIF do
99
:ok = :erlang.load_nif(path, 0)
1010
end
1111

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

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

0 commit comments

Comments
 (0)