Skip to content

Commit a0003fd

Browse files
authored
Add support for audio encoding (#35)
* audio encoder support * fix credo warnings * fix channel layout issues * delete channel assignment line * add test * add channels option to decoder * fix failing tests
1 parent 6e20e12 commit a0003fd

18 files changed

Lines changed: 358 additions & 78 deletions

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ XAV_VIDEO_CONVERTER_SO = $(PRIV_DIR)/libxavvideoconverter.so
1414
# XAV_DEBUG_LOGS = -DXAV_DEBUG=1
1515

1616
DECODER_HEADERS = $(XAV_DIR)/xav_decoder.h $(XAV_DIR)/decoder.h $(XAV_DIR)/video_converter.h $(XAV_DIR)/audio_converter.h $(XAV_DIR)/utils.h $(XAV_DIR)/channel_layout.h
17-
DECODER_SOURCES = $(XAV_DIR)/xav_decoder.c $(XAV_DIR)/decoder.c $(XAV_DIR)/video_converter.c $(XAV_DIR)/audio_converter.c $(XAV_DIR)/utils.c
17+
DECODER_SOURCES = $(XAV_DIR)/xav_decoder.c $(XAV_DIR)/decoder.c $(XAV_DIR)/video_converter.c $(XAV_DIR)/audio_converter.c $(XAV_DIR)/utils.c $(XAV_DIR)/channel_layout.c
1818

19-
ENCODER_HEADERS = $(XAV_DIR)/xav_encoder.h $(XAV_DIR)/encoder.h $(XAV_DIR)/utils.h
20-
ENCODER_SOURCES = $(XAV_DIR)/xav_encoder.c $(XAV_DIR)/encoder.c $(XAV_DIR)/utils.c
19+
ENCODER_HEADERS = $(XAV_DIR)/xav_encoder.h $(XAV_DIR)/encoder.h $(XAV_DIR)/utils.h $(XAV_DIR)/channel_layout.h
20+
ENCODER_SOURCES = $(XAV_DIR)/xav_encoder.c $(XAV_DIR)/encoder.c $(XAV_DIR)/utils.c $(XAV_DIR)/channel_layout.c
2121

2222
READER_HEADERS = $(XAV_DIR)/xav_reader.h $(XAV_DIR)/reader.h $(XAV_DIR)/video_converter.h $(XAV_DIR)/audio_converter.h $(XAV_DIR)/utils.h $(XAV_DIR)/channel_layout.h
2323
READER_SOURCES = $(XAV_DIR)/xav_reader.c $(XAV_DIR)/reader.c $(XAV_DIR)/video_converter.c $(XAV_DIR)/audio_converter.c $(XAV_DIR)/utils.c

c_src/xav/channel_layout.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include "channel_layout.h"
2+
3+
int xav_get_channel_layout(const char *name, struct ChannelLayout *layout) {
4+
#if LIBAVUTIL_VERSION_MAJOR >= 58
5+
if (av_channel_layout_from_string(&layout->layout, name) < 0) {
6+
return 0;
7+
}
8+
#else
9+
layout->layout = av_get_channel_layout(name);
10+
if (layout->layout == 0) {
11+
return 0;
12+
}
13+
#endif
14+
15+
return 1;
16+
}
17+
18+
int xav_get_channel_layout_from_context(struct ChannelLayout *layout, const AVCodecContext *ctx) {
19+
#if LIBAVUTIL_VERSION_MAJOR >= 58
20+
return av_channel_layout_copy(&layout->layout, &ctx->ch_layout);
21+
#else
22+
layout->layout = ctx->channel_layout;
23+
return 0;
24+
#endif
25+
}
26+
27+
int xav_set_channel_layout(AVCodecContext *ctx, struct ChannelLayout *layout) {
28+
#if LIBAVUTIL_VERSION_MAJOR >= 58
29+
return av_channel_layout_copy(&ctx->ch_layout, &layout->layout);
30+
#else
31+
ctx->channel_layout = layout->layout;
32+
return 0;
33+
#endif
34+
}
35+
36+
int xav_set_default_channel_layout(struct ChannelLayout *layout, int channels) {
37+
#if LIBAVUTIL_VERSION_MAJOR >= 58
38+
av_channel_layout_default(&layout->layout, channels);
39+
#else
40+
layout->layout = av_get_default_channel_layout(channels);
41+
#endif
42+
return 0;
43+
}
44+
45+
int xav_set_frame_channel_layout(AVFrame *frame, struct ChannelLayout *layout) {
46+
#if LIBAVUTIL_VERSION_MAJOR >= 58
47+
return av_channel_layout_copy(&frame->ch_layout, &layout->layout);
48+
#else
49+
frame->channel_layout = layout->layout;
50+
return 0;
51+
#endif
52+
}

c_src/xav/channel_layout.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
#ifndef CHANNEL_LAYOUT_H
2-
#define CHANNEL_LAYOUT_H
1+
#ifndef XAV_CHANNEL_LAYOUT_H
2+
#define XAV_CHANNEL_LAYOUT_H
3+
#include <libavcodec/avcodec.h>
34
#include <libavutil/channel_layout.h>
45

56
struct ChannelLayout {
@@ -9,4 +10,10 @@ struct ChannelLayout {
910
uint64_t layout;
1011
#endif
1112
};
12-
#endif
13+
14+
int xav_get_channel_layout(const char *name, struct ChannelLayout *layout);
15+
int xav_get_channel_layout_from_context(struct ChannelLayout *layout, const AVCodecContext *ctx);
16+
int xav_set_channel_layout(AVCodecContext *ctx, struct ChannelLayout *layout);
17+
int xav_set_default_channel_layout(struct ChannelLayout *layout, int channels);
18+
int xav_set_frame_channel_layout(AVFrame *frame, struct ChannelLayout *layout);
19+
#endif

c_src/xav/decoder.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct Decoder *decoder_alloc() {
1313
return decoder;
1414
}
1515

16-
int decoder_init(struct Decoder *decoder, const AVCodec *codec) {
16+
int decoder_init(struct Decoder *decoder, const AVCodec *codec, int channels) {
1717
decoder->media_type = codec->type;
1818
decoder->codec = codec;
1919

@@ -22,6 +22,12 @@ int decoder_init(struct Decoder *decoder, const AVCodec *codec) {
2222
return -1;
2323
}
2424

25+
if (codec->type == AVMEDIA_TYPE_AUDIO && channels != -1) {
26+
struct ChannelLayout ch_layout;
27+
xav_set_default_channel_layout(&ch_layout, channels);
28+
xav_set_channel_layout(decoder->c, &ch_layout);
29+
}
30+
2531
decoder->frame = av_frame_alloc();
2632
if (!decoder->frame) {
2733
return -1;
@@ -32,11 +38,7 @@ int decoder_init(struct Decoder *decoder, const AVCodec *codec) {
3238
return -1;
3339
}
3440

35-
if (avcodec_open2(decoder->c, decoder->codec, NULL) < 0) {
36-
return -1;
37-
}
38-
39-
return 0;
41+
return avcodec_open2(decoder->c, decoder->codec, NULL);
4042
}
4143

4244
int decoder_decode(struct Decoder *decoder, AVPacket *pkt, AVFrame *frame) {

c_src/xav/decoder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <libswresample/swresample.h>
33

44
#include "audio_converter.h"
5+
#include "channel_layout.h"
56
#include "utils.h"
67

78
#define MAX_FLUSH_BUFFER 16
@@ -16,7 +17,7 @@ struct Decoder {
1617

1718
struct Decoder *decoder_alloc();
1819

19-
int decoder_init(struct Decoder *decoder, const AVCodec *codec);
20+
int decoder_init(struct Decoder *decoder, const AVCodec *codec, int channels);
2021

2122
int decoder_decode(struct Decoder *decoder, AVPacket *pkt, AVFrame *frame);
2223

c_src/xav/encoder.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,27 @@ int encoder_init(struct Encoder *encoder, struct EncoderConfig *config) {
2323
return -1;
2424
}
2525

26-
encoder->c->width = config->width;
27-
encoder->c->height = config->height;
28-
encoder->c->pix_fmt = config->format;
29-
encoder->c->time_base = config->time_base;
26+
if (encoder->codec->type == AVMEDIA_TYPE_VIDEO) {
27+
encoder->c->width = config->width;
28+
encoder->c->height = config->height;
29+
encoder->c->pix_fmt = config->format;
30+
encoder->c->time_base = config->time_base;
3031

31-
if (config->profile != FF_PROFILE_UNKNOWN) {
32-
encoder->c->profile = config->profile;
33-
}
32+
if (config->gop_size > 0) {
33+
encoder->c->gop_size = config->gop_size;
34+
}
3435

35-
if (config->gop_size > 0) {
36-
encoder->c->gop_size = config->gop_size;
36+
if (config->max_b_frames >= 0) {
37+
encoder->c->max_b_frames = config->max_b_frames;
38+
}
39+
} else {
40+
encoder->c->sample_fmt = config->sample_format;
41+
encoder->c->sample_rate = config->sample_rate;
42+
xav_set_channel_layout(encoder->c, &config->channel_layout);
3743
}
3844

39-
if (config->max_b_frames >= 0) {
40-
encoder->c->max_b_frames = config->max_b_frames;
45+
if (config->profile != FF_PROFILE_UNKNOWN) {
46+
encoder->c->profile = config->profile;
4147
}
4248

4349
AVDictionary *opts = NULL;

c_src/xav/encoder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "channel_layout.h"
12
#include "utils.h"
23
#include <libavcodec/avcodec.h>
34

@@ -15,10 +16,13 @@ struct EncoderConfig {
1516
int width;
1617
int height;
1718
enum AVPixelFormat format;
19+
enum AVSampleFormat sample_format;
1820
AVRational time_base;
1921
int gop_size;
2022
int max_b_frames;
2123
int profile;
24+
int sample_rate;
25+
struct ChannelLayout channel_layout;
2226
};
2327

2428
struct Encoder *encoder_alloc();

c_src/xav/utils.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,11 @@ ERL_NIF_TERM xav_nif_packet_to_term(ErlNifEnv *env, AVPacket *packet) {
9393
enif_make_atom(env, packet->flags & AV_PKT_FLAG_KEY ? "true" : "false");
9494
return enif_make_tuple(env, 4, data_term, dts, pts, is_keyframe);
9595
}
96+
97+
int xav_get_nb_channels(const AVFrame *frame) {
98+
#if LIBAVUTIL_VERSION_MAJOR >= 58
99+
return frame->ch_layout.nb_channels;
100+
#else
101+
return frame->channels;
102+
#endif
103+
}

c_src/xav/utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ ERL_NIF_TERM xav_nif_video_frame_to_term(ErlNifEnv *env, AVFrame *frame);
2626
ERL_NIF_TERM xav_nif_audio_frame_to_term(ErlNifEnv *env, uint8_t **out_data, int out_samples,
2727
int out_size, enum AVSampleFormat out_format, int pts);
2828
ERL_NIF_TERM xav_nif_packet_to_term(ErlNifEnv *env, AVPacket *packet);
29+
int xav_get_nb_channels(const AVFrame *frame);

c_src/xav/xav_decoder.c

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ void free_frames(AVFrame **frames, int size) {
1313
}
1414

1515
ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
16-
if (argc != 6) {
16+
if (argc != 7) {
1717
return xav_nif_raise(env, "invalid_arg_count");
1818
}
1919

2020
ERL_NIF_TERM ret;
2121
char *codec_name = NULL;
2222
char *out_format = NULL;
23+
int channels;
2324

2425
// resolve codec
2526
if (!xav_nif_get_atom(env, argv[0], &codec_name)) {
@@ -37,8 +38,13 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
3738
goto clean;
3839
}
3940

41+
if (!enif_get_int(env, argv[1], &channels)) {
42+
ret = xav_nif_raise(env, "failed_to_get_int");
43+
goto clean;
44+
}
45+
4046
// resolve output format
41-
if (!xav_nif_get_atom(env, argv[1], &out_format)) {
47+
if (!xav_nif_get_atom(env, argv[2], &out_format)) {
4248
ret = xav_nif_raise(env, "failed_to_get_atom");
4349
goto clean;
4450
}
@@ -61,25 +67,25 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
6167

6268
// resolve other params
6369
int out_sample_rate;
64-
if (!enif_get_int(env, argv[2], &out_sample_rate)) {
70+
if (!enif_get_int(env, argv[3], &out_sample_rate)) {
6571
ret = xav_nif_raise(env, "invalid_out_sample_rate");
6672
goto clean;
6773
}
6874

6975
int out_channels;
70-
if (!enif_get_int(env, argv[3], &out_channels)) {
76+
if (!enif_get_int(env, argv[4], &out_channels)) {
7177
ret = xav_nif_raise(env, "invalid_out_channels");
7278
goto clean;
7379
}
7480

7581
int out_width;
76-
if (!enif_get_int(env, argv[4], &out_width)) {
82+
if (!enif_get_int(env, argv[5], &out_width)) {
7783
ret = xav_nif_raise(env, "failed_to_get_int");
7884
goto clean;
7985
}
8086

8187
int out_height;
82-
if (!enif_get_int(env, argv[5], &out_height)) {
88+
if (!enif_get_int(env, argv[6], &out_height)) {
8389
ret = xav_nif_raise(env, "failed_to_get_int");
8490
goto clean;
8591
}
@@ -102,7 +108,7 @@ ERL_NIF_TERM new (ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
102108
goto clean;
103109
}
104110

105-
if (decoder_init(xav_decoder->decoder, codec) != 0) {
111+
if (decoder_init(xav_decoder->decoder, codec, channels)) {
106112
ret = xav_nif_raise(env, "failed_to_init_decoder");
107113
goto clean;
108114
}
@@ -341,21 +347,13 @@ static int init_audio_converter(struct XavDecoder *xav_decoder) {
341347
}
342348

343349
struct ChannelLayout in_chlayout, out_chlayout;
344-
#if LIBAVUTIL_VERSION_MAJOR >= 58
345-
in_chlayout.layout = xav_decoder->decoder->c->ch_layout;
346-
if (xav_decoder->out_channels == 0) {
347-
out_chlayout.layout = in_chlayout.layout;
348-
} else {
349-
av_channel_layout_default(&out_chlayout.layout, xav_decoder->out_channels);
350-
}
351-
#else
352-
in_chlayout.layout = xav_decoder->decoder->c->channel_layout;
350+
xav_get_channel_layout_from_context(&in_chlayout, xav_decoder->decoder->c);
351+
353352
if (xav_decoder->out_channels == 0) {
354-
out_chlayout.layout = in_chlayout.layout;
353+
xav_get_channel_layout_from_context(&out_chlayout, xav_decoder->decoder->c);
355354
} else {
356-
out_chlayout.layout = av_get_default_channel_layout(xav_decoder->out_channels);
355+
xav_set_default_channel_layout(&out_chlayout, xav_decoder->out_channels);
357356
}
358-
#endif
359357

360358
return audio_converter_init(xav_decoder->ac, in_chlayout, xav_decoder->decoder->c->sample_rate,
361359
xav_decoder->decoder->c->sample_fmt, out_chlayout, out_sample_rate,
@@ -393,7 +391,7 @@ void free_xav_decoder(ErlNifEnv *env, void *obj) {
393391
}
394392
}
395393

396-
static ErlNifFunc xav_funcs[] = {{"new", 6, new},
394+
static ErlNifFunc xav_funcs[] = {{"new", 7, new},
397395
{"decode", 4, decode, ERL_NIF_DIRTY_JOB_CPU_BOUND},
398396
{"flush", 1, flush, ERL_NIF_DIRTY_JOB_CPU_BOUND},
399397
{"pixel_formats", 0, pixel_formats},

0 commit comments

Comments
 (0)