@@ -4,6 +4,7 @@ ErlNifResourceType *xav_encoder_resource_type;
44
55static ERL_NIF_TERM packets_to_term (ErlNifEnv * , struct Encoder * );
66static int get_profile (enum AVCodecID , const char * );
7+ static ERL_NIF_TERM get_codec_profiles (ErlNifEnv * , const AVCodec * );
78
89ERL_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
109113clean :
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+
181211void 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
210240static 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
238284static int load (ErlNifEnv * env , void * * priv , ERL_NIF_TERM load_info ) {
239285 xav_encoder_resource_type =
0 commit comments