Skip to content

Commit 6e20e12

Browse files
authored
Get encoder by name (#34)
1 parent 6a2bea3 commit 6e20e12

10 files changed

Lines changed: 180 additions & 71 deletions

File tree

c_src/xav/encoder.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ struct Encoder *encoder_alloc() {
1616
}
1717

1818
int encoder_init(struct Encoder *encoder, struct EncoderConfig *config) {
19-
encoder->codec = avcodec_find_encoder(config->codec);
20-
if (!encoder->codec) {
21-
return -1;
22-
}
19+
encoder->codec = config->codec;
2320

2421
encoder->c = avcodec_alloc_context3(encoder->codec);
2522
if (!encoder->c) {
@@ -44,7 +41,7 @@ int encoder_init(struct Encoder *encoder, struct EncoderConfig *config) {
4441
}
4542

4643
AVDictionary *opts = NULL;
47-
if (config->codec == AV_CODEC_ID_HEVC) {
44+
if (strcmp(encoder->codec->name, "libx265") == 0) {
4845
char x265_params[256] = "log-level=warning";
4946
if (config->gop_size > 0) {
5047
sprintf(x265_params + strlen(x265_params), ":keyint=%d", config->gop_size);

c_src/xav/encoder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct Encoder {
1111

1212
struct EncoderConfig {
1313
enum AVMediaType media_type;
14-
enum AVCodecID codec;
14+
const AVCodec *codec;
1515
int width;
1616
int height;
1717
enum AVPixelFormat format;

c_src/xav/utils.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@ int xav_nif_get_atom(ErlNifEnv *env, ERL_NIF_TERM term, char **value) {
3535
return 1;
3636
}
3737

38+
int xav_nif_get_string(ErlNifEnv *env, ERL_NIF_TERM term, char **value) {
39+
ErlNifBinary bin;
40+
if (!enif_inspect_binary(env, term, &bin)) {
41+
return 0;
42+
}
43+
44+
char *str_value = (char *)XAV_ALLOC((bin.size + 1) * sizeof(char *));
45+
memcpy(str_value, bin.data, bin.size);
46+
str_value[bin.size] = '\0';
47+
48+
*value = str_value;
49+
return 1;
50+
}
51+
3852
ERL_NIF_TERM xav_nif_audio_frame_to_term(ErlNifEnv *env, uint8_t **out_data, int out_samples,
3953
int out_size, enum AVSampleFormat out_format, int pts) {
4054
ERL_NIF_TERM data_term;

c_src/xav/utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ ERL_NIF_TERM xav_nif_ok(ErlNifEnv *env, ERL_NIF_TERM data_term);
2121
ERL_NIF_TERM xav_nif_error(ErlNifEnv *env, char *reason);
2222
ERL_NIF_TERM xav_nif_raise(ErlNifEnv *env, char *msg);
2323
int xav_nif_get_atom(ErlNifEnv *env, ERL_NIF_TERM term, char **value);
24+
int xav_nif_get_string(ErlNifEnv *env, ERL_NIF_TERM term, char **value);
2425
ERL_NIF_TERM xav_nif_video_frame_to_term(ErlNifEnv *env, AVFrame *frame);
2526
ERL_NIF_TERM xav_nif_audio_frame_to_term(ErlNifEnv *env, uint8_t **out_data, int out_samples,
2627
int out_size, enum AVSampleFormat out_format, int pts);

c_src/xav/xav_decoder.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,13 @@ ERL_NIF_TERM list_decoders(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
303303
while ((codec = av_codec_iterate(&iter))) {
304304
if (av_codec_is_decoder(codec)) {
305305
ERL_NIF_TERM name = enif_make_atom(env, codec->name);
306-
ERL_NIF_TERM long_name = enif_make_string(env, codec->long_name, ERL_NIF_LATIN1);
306+
ERL_NIF_TERM codec_name = enif_make_atom(env, avcodec_get_name(codec->id));
307+
ERL_NIF_TERM long_name = codec->long_name
308+
? enif_make_string(env, codec->long_name, ERL_NIF_LATIN1)
309+
: enif_make_string(env, "", ERL_NIF_LATIN1);
307310
ERL_NIF_TERM media_type = enif_make_atom(env, av_get_media_type_string(codec->type));
308311

309-
ERL_NIF_TERM desc = enif_make_tuple3(env, name, long_name, media_type);
312+
ERL_NIF_TERM desc = enif_make_tuple4(env, codec_name, name, long_name, media_type);
310313
result = enif_make_list_cell(env, desc, result);
311314
}
312315
}

c_src/xav/xav_encoder.c

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ ErlNifResourceType *xav_encoder_resource_type;
44

55
static ERL_NIF_TERM packets_to_term(ErlNifEnv *, struct Encoder *);
66
static int get_profile(enum AVCodecID, const char *);
7+
static ERL_NIF_TERM get_codec_profiles(ErlNifEnv *, const AVCodec *);
78

89
ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
910
if (argc != 2) {
@@ -15,32 +16,22 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
1516
encoder_config.max_b_frames = -1;
1617
encoder_config.profile = FF_PROFILE_UNKNOWN;
1718

18-
char *codec = NULL, *format = NULL, *profile = NULL;
19+
char *codec_name = NULL, *format = NULL, *profile = NULL;
20+
int codec_id = 0;
1921

2022
ErlNifMapIterator iter;
2123
ERL_NIF_TERM key, value;
2224
char *config_name = NULL;
2325
int err;
2426

25-
if (!xav_nif_get_atom(env, argv[0], &codec)) {
27+
if (!xav_nif_get_atom(env, argv[0], &codec_name)) {
2628
return xav_nif_raise(env, "failed_to_get_atom");
2729
}
2830

2931
if (!enif_is_map(env, argv[1])) {
3032
return xav_nif_raise(env, "failed_to_get_map");
3133
}
3234

33-
if (strcmp(codec, "h264") == 0) {
34-
encoder_config.media_type = AVMEDIA_TYPE_VIDEO;
35-
encoder_config.codec = AV_CODEC_ID_H264;
36-
} else if (strcmp(codec, "h265") == 0 || strcmp(codec, "hevc") == 0) {
37-
encoder_config.media_type = AVMEDIA_TYPE_VIDEO;
38-
encoder_config.codec = AV_CODEC_ID_HEVC;
39-
} else {
40-
ret = xav_nif_raise(env, "failed_to_resolve_codec");
41-
goto clean;
42-
}
43-
4435
enif_map_iterator_create(env, argv[1], &iter, ERL_NIF_MAP_ITERATOR_FIRST);
4536

4637
while (enif_map_iterator_get_pair(env, &iter, &key, &value)) {
@@ -64,7 +55,9 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
6455
} else if (strcmp(config_name, "max_b_frames") == 0) {
6556
err = enif_get_int(env, value, &encoder_config.max_b_frames);
6657
} else if (strcmp(config_name, "profile") == 0) {
67-
err = xav_nif_get_atom(env, value, &profile);
58+
err = xav_nif_get_string(env, value, &profile);
59+
} else if (strcmp(config_name, "codec_id") == 0) {
60+
err = enif_get_int(env, value, &codec_id);
6861
} else {
6962
ret = xav_nif_raise(env, "unknown_config_key");
7063
goto clean;
@@ -79,14 +72,25 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
7972
enif_map_iterator_next(env, &iter);
8073
}
8174

75+
if (strcmp(codec_name, "nil") == 0) {
76+
encoder_config.codec = avcodec_find_encoder((enum AVCodecID)codec_id);
77+
} else {
78+
encoder_config.codec = avcodec_find_encoder_by_name(codec_name);
79+
}
80+
81+
if (!encoder_config.codec) {
82+
ret = xav_nif_raise(env, "unknown_codec");
83+
goto clean;
84+
}
85+
8286
encoder_config.format = av_get_pix_fmt(format);
8387
if (encoder_config.format == AV_PIX_FMT_NONE) {
8488
ret = xav_nif_raise(env, "unknown_format");
8589
goto clean;
8690
}
8791

8892
if (profile) {
89-
encoder_config.profile = get_profile(encoder_config.codec, profile);
93+
encoder_config.profile = get_profile(encoder_config.codec->id, profile);
9094
if (encoder_config.profile == FF_PROFILE_UNKNOWN) {
9195
ret = xav_nif_raise(env, "invalid_profile");
9296
goto clean;
@@ -107,8 +111,8 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
107111
enif_release_resource(xav_encoder);
108112

109113
clean:
110-
if (!codec)
111-
XAV_FREE(codec);
114+
if (!codec_name)
115+
XAV_FREE(codec_name);
112116
if (!format)
113117
XAV_FREE(format);
114118
if (!config_name)
@@ -178,6 +182,32 @@ ERL_NIF_TERM flush(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
178182
return packets_to_term(env, xav_encoder->encoder);
179183
}
180184

185+
ERL_NIF_TERM list_encoders(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
186+
ERL_NIF_TERM result = enif_make_list(env, 0);
187+
188+
const AVCodec *codec = NULL;
189+
void *iter = NULL;
190+
191+
while ((codec = av_codec_iterate(&iter))) {
192+
if (av_codec_is_encoder(codec)) {
193+
ERL_NIF_TERM name = enif_make_atom(env, codec->name);
194+
ERL_NIF_TERM codec_name = enif_make_atom(env, avcodec_get_name(codec->id));
195+
ERL_NIF_TERM long_name = codec->long_name
196+
? enif_make_string(env, codec->long_name, ERL_NIF_LATIN1)
197+
: enif_make_string(env, "", ERL_NIF_LATIN1);
198+
ERL_NIF_TERM media_type = enif_make_atom(env, av_get_media_type_string(codec->type));
199+
ERL_NIF_TERM codec_id = enif_make_int64(env, codec->id);
200+
ERL_NIF_TERM profiles = get_codec_profiles(env, codec);
201+
202+
ERL_NIF_TERM desc =
203+
enif_make_tuple6(env, codec_name, name, long_name, media_type, codec_id, profiles);
204+
result = enif_make_list_cell(env, desc, result);
205+
}
206+
}
207+
208+
return result;
209+
}
210+
181211
void free_xav_encoder(ErlNifEnv *env, void *obj) {
182212
XAV_LOG_DEBUG("Freeing XavEncoder object");
183213
struct XavEncoder *xav_encoder = (struct XavEncoder *)obj;
@@ -208,32 +238,48 @@ static ERL_NIF_TERM packets_to_term(ErlNifEnv *env, struct Encoder *encoder) {
208238
}
209239

210240
static int get_profile(enum AVCodecID codec, const char *profile_name) {
211-
if (codec == AV_CODEC_ID_H264) {
212-
if (strcmp(profile_name, "constrained_baseline") == 0) {
213-
return FF_PROFILE_H264_CONSTRAINED_BASELINE;
214-
} else if (strcmp(profile_name, "baseline") == 0) {
215-
return FF_PROFILE_H264_BASELINE;
216-
} else if (strcmp(profile_name, "main") == 0) {
217-
return FF_PROFILE_H264_MAIN;
218-
} else if (strcmp(profile_name, "high") == 0) {
219-
return FF_PROFILE_H264_HIGH;
220-
}
241+
const AVCodecDescriptor *desc = avcodec_descriptor_get(codec);
242+
const AVProfile *profile = desc->profiles;
243+
244+
if (profile == NULL) {
245+
return FF_PROFILE_UNKNOWN;
221246
}
222247

223-
if (codec == AV_CODEC_ID_HEVC) {
224-
if (strcmp(profile_name, "main") == 0) {
225-
return FF_PROFILE_HEVC_MAIN;
226-
} else if (strcmp(profile_name, "main_10") == 0) {
227-
return FF_PROFILE_HEVC_MAIN_10;
228-
} else if (strcmp(profile_name, "main_still_picture") == 0) {
229-
return FF_PROFILE_HEVC_MAIN_STILL_PICTURE;
248+
while (profile->profile != FF_PROFILE_UNKNOWN) {
249+
if (strcmp(profile->name, profile_name) == 0) {
250+
break;
230251
}
252+
253+
profile++;
254+
}
255+
256+
return profile->profile;
257+
}
258+
259+
static ERL_NIF_TERM get_codec_profiles(ErlNifEnv *env, const AVCodec *codec) {
260+
ERL_NIF_TERM result = enif_make_list(env, 0);
261+
262+
const AVCodecDescriptor *desc = avcodec_descriptor_get(codec->id);
263+
const AVProfile *profile = desc->profiles;
264+
265+
if (profile == NULL) {
266+
return result;
267+
}
268+
269+
while (profile->profile != FF_PROFILE_UNKNOWN) {
270+
ERL_NIF_TERM profile_name = enif_make_string(env, profile->name, ERL_NIF_LATIN1);
271+
result = enif_make_list_cell(env, profile_name, result);
272+
273+
profile++;
231274
}
232275

233-
return FF_PROFILE_UNKNOWN;
276+
return result;
234277
}
235278

236-
static ErlNifFunc xav_funcs[] = {{"new", 2, new}, {"encode", 3, encode}, {"flush", 1, flush}};
279+
static ErlNifFunc xav_funcs[] = {{"new", 2, new},
280+
{"encode", 3, encode},
281+
{"flush", 1, flush},
282+
{"list_encoders", 0, list_encoders}};
237283

238284
static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) {
239285
xav_encoder_resource_type =

lib/xav.ex

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
defmodule Xav do
22
@moduledoc File.read!("README.md")
33

4+
@type encoder :: %{
5+
codec: atom(),
6+
name: atom(),
7+
long_name: String.t(),
8+
media_type: atom(),
9+
profiles: [String.t()]
10+
}
11+
12+
@type decoder :: %{
13+
codec: atom(),
14+
name: atom(),
15+
long_name: String.t(),
16+
media_type: atom()
17+
}
18+
419
@doc """
520
Get all available pixel formats.
621
@@ -24,17 +39,35 @@ defmodule Xav do
2439

2540
@doc """
2641
List all decoders.
27-
28-
The result is a list of 3-element tuples `{name, long_name, media_type}`:
29-
* `name` - The short name of the decoder.
30-
* `long_name` - The long name of the decoder.
31-
* `media_type` - The media type of the decoder.
3242
"""
33-
@spec list_decoders() :: [{name :: atom(), long_name :: String.t(), media_type :: atom()}]
43+
@spec list_decoders() :: [decoder()]
3444
def list_decoders() do
3545
Xav.Decoder.NIF.list_decoders()
36-
|> Enum.map(fn {name, long_name, media_type} ->
37-
{name, List.to_string(long_name), media_type}
46+
|> Enum.map(fn {codec, name, long_name, media_type} ->
47+
%{
48+
codec: codec,
49+
name: name,
50+
long_name: List.to_string(long_name),
51+
media_type: media_type
52+
}
53+
end)
54+
|> Enum.reverse()
55+
end
56+
57+
@doc """
58+
List all encoders.
59+
"""
60+
@spec list_encoders() :: [encoder()]
61+
def list_encoders() do
62+
Xav.Encoder.NIF.list_encoders()
63+
|> Enum.map(fn {family_name, name, long_name, media_type, _codec_id, profiles} ->
64+
%{
65+
codec: family_name,
66+
name: name,
67+
long_name: List.to_string(long_name),
68+
media_type: media_type,
69+
profiles: profiles |> Enum.map(&List.to_string/1) |> Enum.reverse()
70+
}
3871
end)
3972
|> Enum.reverse()
4073
end

0 commit comments

Comments
 (0)