Skip to content

Commit

Permalink
Get encoder by name (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
gBillal authored Feb 11, 2025
1 parent 6a2bea3 commit 6e20e12
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 71 deletions.
7 changes: 2 additions & 5 deletions c_src/xav/encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ struct Encoder *encoder_alloc() {
}

int encoder_init(struct Encoder *encoder, struct EncoderConfig *config) {
encoder->codec = avcodec_find_encoder(config->codec);
if (!encoder->codec) {
return -1;
}
encoder->codec = config->codec;

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

AVDictionary *opts = NULL;
if (config->codec == AV_CODEC_ID_HEVC) {
if (strcmp(encoder->codec->name, "libx265") == 0) {
char x265_params[256] = "log-level=warning";
if (config->gop_size > 0) {
sprintf(x265_params + strlen(x265_params), ":keyint=%d", config->gop_size);
Expand Down
2 changes: 1 addition & 1 deletion c_src/xav/encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct Encoder {

struct EncoderConfig {
enum AVMediaType media_type;
enum AVCodecID codec;
const AVCodec *codec;
int width;
int height;
enum AVPixelFormat format;
Expand Down
14 changes: 14 additions & 0 deletions c_src/xav/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ int xav_nif_get_atom(ErlNifEnv *env, ERL_NIF_TERM term, char **value) {
return 1;
}

int xav_nif_get_string(ErlNifEnv *env, ERL_NIF_TERM term, char **value) {
ErlNifBinary bin;
if (!enif_inspect_binary(env, term, &bin)) {
return 0;
}

char *str_value = (char *)XAV_ALLOC((bin.size + 1) * sizeof(char *));
memcpy(str_value, bin.data, bin.size);
str_value[bin.size] = '\0';

*value = str_value;
return 1;
}

ERL_NIF_TERM xav_nif_audio_frame_to_term(ErlNifEnv *env, uint8_t **out_data, int out_samples,
int out_size, enum AVSampleFormat out_format, int pts) {
ERL_NIF_TERM data_term;
Expand Down
1 change: 1 addition & 0 deletions c_src/xav/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ ERL_NIF_TERM xav_nif_ok(ErlNifEnv *env, ERL_NIF_TERM data_term);
ERL_NIF_TERM xav_nif_error(ErlNifEnv *env, char *reason);
ERL_NIF_TERM xav_nif_raise(ErlNifEnv *env, char *msg);
int xav_nif_get_atom(ErlNifEnv *env, ERL_NIF_TERM term, char **value);
int xav_nif_get_string(ErlNifEnv *env, ERL_NIF_TERM term, char **value);
ERL_NIF_TERM xav_nif_video_frame_to_term(ErlNifEnv *env, AVFrame *frame);
ERL_NIF_TERM xav_nif_audio_frame_to_term(ErlNifEnv *env, uint8_t **out_data, int out_samples,
int out_size, enum AVSampleFormat out_format, int pts);
Expand Down
7 changes: 5 additions & 2 deletions c_src/xav/xav_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,13 @@ ERL_NIF_TERM list_decoders(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
while ((codec = av_codec_iterate(&iter))) {
if (av_codec_is_decoder(codec)) {
ERL_NIF_TERM name = enif_make_atom(env, codec->name);
ERL_NIF_TERM long_name = enif_make_string(env, codec->long_name, ERL_NIF_LATIN1);
ERL_NIF_TERM codec_name = enif_make_atom(env, avcodec_get_name(codec->id));
ERL_NIF_TERM long_name = codec->long_name
? enif_make_string(env, codec->long_name, ERL_NIF_LATIN1)
: enif_make_string(env, "", ERL_NIF_LATIN1);
ERL_NIF_TERM media_type = enif_make_atom(env, av_get_media_type_string(codec->type));

ERL_NIF_TERM desc = enif_make_tuple3(env, name, long_name, media_type);
ERL_NIF_TERM desc = enif_make_tuple4(env, codec_name, name, long_name, media_type);
result = enif_make_list_cell(env, desc, result);
}
}
Expand Down
118 changes: 82 additions & 36 deletions c_src/xav/xav_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ ErlNifResourceType *xav_encoder_resource_type;

static ERL_NIF_TERM packets_to_term(ErlNifEnv *, struct Encoder *);
static int get_profile(enum AVCodecID, const char *);
static ERL_NIF_TERM get_codec_profiles(ErlNifEnv *, const AVCodec *);

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

char *codec = NULL, *format = NULL, *profile = NULL;
char *codec_name = NULL, *format = NULL, *profile = NULL;
int codec_id = 0;

ErlNifMapIterator iter;
ERL_NIF_TERM key, value;
char *config_name = NULL;
int err;

if (!xav_nif_get_atom(env, argv[0], &codec)) {
if (!xav_nif_get_atom(env, argv[0], &codec_name)) {
return xav_nif_raise(env, "failed_to_get_atom");
}

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

if (strcmp(codec, "h264") == 0) {
encoder_config.media_type = AVMEDIA_TYPE_VIDEO;
encoder_config.codec = AV_CODEC_ID_H264;
} else if (strcmp(codec, "h265") == 0 || strcmp(codec, "hevc") == 0) {
encoder_config.media_type = AVMEDIA_TYPE_VIDEO;
encoder_config.codec = AV_CODEC_ID_HEVC;
} else {
ret = xav_nif_raise(env, "failed_to_resolve_codec");
goto clean;
}

enif_map_iterator_create(env, argv[1], &iter, ERL_NIF_MAP_ITERATOR_FIRST);

while (enif_map_iterator_get_pair(env, &iter, &key, &value)) {
Expand All @@ -64,7 +55,9 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
} else if (strcmp(config_name, "max_b_frames") == 0) {
err = enif_get_int(env, value, &encoder_config.max_b_frames);
} else if (strcmp(config_name, "profile") == 0) {
err = xav_nif_get_atom(env, value, &profile);
err = xav_nif_get_string(env, value, &profile);
} else if (strcmp(config_name, "codec_id") == 0) {
err = enif_get_int(env, value, &codec_id);
} else {
ret = xav_nif_raise(env, "unknown_config_key");
goto clean;
Expand All @@ -79,14 +72,25 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
enif_map_iterator_next(env, &iter);
}

if (strcmp(codec_name, "nil") == 0) {
encoder_config.codec = avcodec_find_encoder((enum AVCodecID)codec_id);
} else {
encoder_config.codec = avcodec_find_encoder_by_name(codec_name);
}

if (!encoder_config.codec) {
ret = xav_nif_raise(env, "unknown_codec");
goto clean;
}

encoder_config.format = av_get_pix_fmt(format);
if (encoder_config.format == AV_PIX_FMT_NONE) {
ret = xav_nif_raise(env, "unknown_format");
goto clean;
}

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

clean:
if (!codec)
XAV_FREE(codec);
if (!codec_name)
XAV_FREE(codec_name);
if (!format)
XAV_FREE(format);
if (!config_name)
Expand Down Expand Up @@ -178,6 +182,32 @@ ERL_NIF_TERM flush(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
return packets_to_term(env, xav_encoder->encoder);
}

ERL_NIF_TERM list_encoders(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
ERL_NIF_TERM result = enif_make_list(env, 0);

const AVCodec *codec = NULL;
void *iter = NULL;

while ((codec = av_codec_iterate(&iter))) {
if (av_codec_is_encoder(codec)) {
ERL_NIF_TERM name = enif_make_atom(env, codec->name);
ERL_NIF_TERM codec_name = enif_make_atom(env, avcodec_get_name(codec->id));
ERL_NIF_TERM long_name = codec->long_name
? enif_make_string(env, codec->long_name, ERL_NIF_LATIN1)
: enif_make_string(env, "", ERL_NIF_LATIN1);
ERL_NIF_TERM media_type = enif_make_atom(env, av_get_media_type_string(codec->type));
ERL_NIF_TERM codec_id = enif_make_int64(env, codec->id);
ERL_NIF_TERM profiles = get_codec_profiles(env, codec);

ERL_NIF_TERM desc =
enif_make_tuple6(env, codec_name, name, long_name, media_type, codec_id, profiles);
result = enif_make_list_cell(env, desc, result);
}
}

return result;
}

void free_xav_encoder(ErlNifEnv *env, void *obj) {
XAV_LOG_DEBUG("Freeing XavEncoder object");
struct XavEncoder *xav_encoder = (struct XavEncoder *)obj;
Expand Down Expand Up @@ -208,32 +238,48 @@ static ERL_NIF_TERM packets_to_term(ErlNifEnv *env, struct Encoder *encoder) {
}

static int get_profile(enum AVCodecID codec, const char *profile_name) {
if (codec == AV_CODEC_ID_H264) {
if (strcmp(profile_name, "constrained_baseline") == 0) {
return FF_PROFILE_H264_CONSTRAINED_BASELINE;
} else if (strcmp(profile_name, "baseline") == 0) {
return FF_PROFILE_H264_BASELINE;
} else if (strcmp(profile_name, "main") == 0) {
return FF_PROFILE_H264_MAIN;
} else if (strcmp(profile_name, "high") == 0) {
return FF_PROFILE_H264_HIGH;
}
const AVCodecDescriptor *desc = avcodec_descriptor_get(codec);
const AVProfile *profile = desc->profiles;

if (profile == NULL) {
return FF_PROFILE_UNKNOWN;
}

if (codec == AV_CODEC_ID_HEVC) {
if (strcmp(profile_name, "main") == 0) {
return FF_PROFILE_HEVC_MAIN;
} else if (strcmp(profile_name, "main_10") == 0) {
return FF_PROFILE_HEVC_MAIN_10;
} else if (strcmp(profile_name, "main_still_picture") == 0) {
return FF_PROFILE_HEVC_MAIN_STILL_PICTURE;
while (profile->profile != FF_PROFILE_UNKNOWN) {
if (strcmp(profile->name, profile_name) == 0) {
break;
}

profile++;
}

return profile->profile;
}

static ERL_NIF_TERM get_codec_profiles(ErlNifEnv *env, const AVCodec *codec) {
ERL_NIF_TERM result = enif_make_list(env, 0);

const AVCodecDescriptor *desc = avcodec_descriptor_get(codec->id);
const AVProfile *profile = desc->profiles;

if (profile == NULL) {
return result;
}

while (profile->profile != FF_PROFILE_UNKNOWN) {
ERL_NIF_TERM profile_name = enif_make_string(env, profile->name, ERL_NIF_LATIN1);
result = enif_make_list_cell(env, profile_name, result);

profile++;
}

return FF_PROFILE_UNKNOWN;
return result;
}

static ErlNifFunc xav_funcs[] = {{"new", 2, new}, {"encode", 3, encode}, {"flush", 1, flush}};
static ErlNifFunc xav_funcs[] = {{"new", 2, new},
{"encode", 3, encode},
{"flush", 1, flush},
{"list_encoders", 0, list_encoders}};

static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) {
xav_encoder_resource_type =
Expand Down
49 changes: 41 additions & 8 deletions lib/xav.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
defmodule Xav do
@moduledoc File.read!("README.md")

@type encoder :: %{
codec: atom(),
name: atom(),
long_name: String.t(),
media_type: atom(),
profiles: [String.t()]
}

@type decoder :: %{
codec: atom(),
name: atom(),
long_name: String.t(),
media_type: atom()
}

@doc """
Get all available pixel formats.
Expand All @@ -24,17 +39,35 @@ defmodule Xav do

@doc """
List all decoders.
The result is a list of 3-element tuples `{name, long_name, media_type}`:
* `name` - The short name of the decoder.
* `long_name` - The long name of the decoder.
* `media_type` - The media type of the decoder.
"""
@spec list_decoders() :: [{name :: atom(), long_name :: String.t(), media_type :: atom()}]
@spec list_decoders() :: [decoder()]
def list_decoders() do
Xav.Decoder.NIF.list_decoders()
|> Enum.map(fn {name, long_name, media_type} ->
{name, List.to_string(long_name), media_type}
|> Enum.map(fn {codec, name, long_name, media_type} ->
%{
codec: codec,
name: name,
long_name: List.to_string(long_name),
media_type: media_type
}
end)
|> Enum.reverse()
end

@doc """
List all encoders.
"""
@spec list_encoders() :: [encoder()]
def list_encoders() do
Xav.Encoder.NIF.list_encoders()
|> Enum.map(fn {family_name, name, long_name, media_type, _codec_id, profiles} ->
%{
codec: family_name,
name: name,
long_name: List.to_string(long_name),
media_type: media_type,
profiles: profiles |> Enum.map(&List.to_string/1) |> Enum.reverse()
}
end)
|> Enum.reverse()
end
Expand Down
Loading

0 comments on commit 6e20e12

Please sign in to comment.