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
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
Loading