-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add audio and video converters. Fix audio resampling. (#8)
- Loading branch information
Showing
27 changed files
with
795 additions
and
496 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
#include <libavutil/channel_layout.h> | ||
#include <libavutil/opt.h> | ||
#include <libavutil/samplefmt.h> | ||
#include <libswresample/swresample.h> | ||
#include <stdint.h> | ||
|
||
#include "audio_converter.h" | ||
#include "channel_layout.h" | ||
#include "utils.h" | ||
|
||
struct AudioConverter *audio_converter_alloc() { | ||
struct AudioConverter *converter = | ||
(struct AudioConverter *)XAV_ALLOC(sizeof(struct AudioConverter)); | ||
converter->swr_ctx = NULL; | ||
return converter; | ||
} | ||
|
||
int audio_converter_init(struct AudioConverter *c, struct ChannelLayout in_chlayout, | ||
int in_sample_rate, enum AVSampleFormat in_sample_fmt, | ||
struct ChannelLayout out_chlayout, int out_sample_rate, | ||
enum AVSampleFormat out_sample_fmt) { | ||
c->swr_ctx = swr_alloc(); | ||
c->in_sample_rate = in_sample_rate; | ||
c->out_sample_rate = out_sample_rate; | ||
c->out_chlayout = out_chlayout; | ||
c->out_sample_fmt = out_sample_fmt; | ||
|
||
#if LIBAVUTIL_VERSION_MAJOR >= 58 | ||
av_opt_set_chlayout(c->swr_ctx, "in_chlayout", &in_chlayout.layout, 0); | ||
av_opt_set_chlayout(c->swr_ctx, "out_chlayout", &out_chlayout.layout, 0); | ||
#else | ||
av_opt_set_channel_layout(c->swr_ctx, "in_channel_layout", in_chlayout.layout, 0); | ||
av_opt_set_channel_layout(c->swr_ctx, "out_channel_layout", out_chlayout.layout, 0); | ||
#endif | ||
|
||
av_opt_set_int(c->swr_ctx, "in_sample_rate", in_sample_rate, 0); | ||
av_opt_set_int(c->swr_ctx, "out_sample_rate", out_sample_rate, 0); | ||
|
||
av_opt_set_sample_fmt(c->swr_ctx, "in_sample_fmt", in_sample_fmt, 0); | ||
av_opt_set_sample_fmt(c->swr_ctx, "out_sample_fmt", out_sample_fmt, 0); | ||
|
||
return swr_init(c->swr_ctx); | ||
} | ||
|
||
int audio_converter_convert(struct AudioConverter *c, AVFrame *src_frame, uint8_t ***out_data, | ||
int *out_samples, int *out_size) { | ||
|
||
#if LIBAVUTIL_VERSION_MAJOR >= 58 | ||
int out_nb_channels = c->out_chlayout.layout.nb_channels; | ||
#else | ||
int out_nb_channels = av_get_channel_layout_nb_channels(c->out_chlayout.layout); | ||
#endif | ||
|
||
uint8_t **out_data_tmp = NULL; | ||
int max_out_nb_samples = swr_get_out_samples(c->swr_ctx, src_frame->nb_samples); | ||
int out_bytes_per_sample = av_get_bytes_per_sample(c->out_sample_fmt); | ||
|
||
// Some parts of ffmpeg require buffers to by divisible by 32 | ||
// to use fast/aligned SIMD routines - this is what align option is used for. | ||
// See https://stackoverflow.com/questions/35678041/what-is-linesize-alignment-meaning | ||
// Because we return the binary straight to the Erlang, we can disable it. | ||
int ret = av_samples_alloc_array_and_samples(&out_data_tmp, NULL, out_nb_channels, | ||
max_out_nb_samples, c->out_sample_fmt, 1); | ||
|
||
if (ret < 0) { | ||
XAV_LOG_DEBUG("Couldn't allocate array for out samples."); | ||
return ret; | ||
} | ||
|
||
*out_samples = swr_convert(c->swr_ctx, out_data_tmp, max_out_nb_samples, | ||
(const uint8_t **)src_frame->data, src_frame->nb_samples); | ||
|
||
if (*out_samples < 0) { | ||
XAV_LOG_DEBUG("Couldn't convert samples: %d", *out_samples); | ||
av_freep(&out_data_tmp[0]); | ||
return -1; | ||
} | ||
|
||
XAV_LOG_DEBUG("Converted %d samples per channel", *out_samples); | ||
|
||
*out_size = *out_samples * out_bytes_per_sample * out_nb_channels; | ||
|
||
*out_data = out_data_tmp; | ||
|
||
return 0; | ||
} | ||
|
||
void audio_converter_free(struct AudioConverter **converter) { | ||
if (*converter != NULL) { | ||
struct AudioConverter *c = *converter; | ||
|
||
if (c->swr_ctx != NULL) { | ||
swr_free(&c->swr_ctx); | ||
} | ||
|
||
XAV_FREE(c); | ||
*converter = NULL; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#ifndef CONVERTER_H | ||
#define CONVERTER_H | ||
#include <libavutil/channel_layout.h> | ||
#include <libswresample/swresample.h> | ||
#include <stdint.h> | ||
|
||
#include "channel_layout.h" | ||
|
||
struct AudioConverter { | ||
SwrContext *swr_ctx; | ||
int64_t in_sample_rate; | ||
int64_t out_sample_rate; | ||
struct ChannelLayout out_chlayout; | ||
enum AVSampleFormat out_sample_fmt; | ||
}; | ||
|
||
struct AudioConverter *audio_converter_alloc(void); | ||
|
||
int audio_converter_init(struct AudioConverter *c, struct ChannelLayout in_chlayout, | ||
int in_sample_rate, enum AVSampleFormat in_sample_fmt, | ||
struct ChannelLayout out_chlayout, int out_sample_rate, | ||
enum AVSampleFormat out_sample_fmt); | ||
|
||
/** | ||
* Converts AVFrame to the output format. | ||
* | ||
* @param c audio converter | ||
* @param src_frame decoded source frame | ||
* @param out_data buffer where audio samples are written after convertion. | ||
* We always convert to the packed format, so only *out_data[0] is set. | ||
* It will be initialized internally and has to be freed with av_freep(&(*out_data[0])). | ||
* @param out_samples number of samples per channel in out_data buffer. | ||
* @param out_size size of out_buffer in bytes. | ||
* This is the same as *out_samples * bytes_per_sample(out_format) * out_channels | ||
* @return 0 on success and negative value on error. | ||
*/ | ||
int audio_converter_convert(struct AudioConverter *c, AVFrame *src_frame, uint8_t ***out_data, | ||
int *out_samples, int *out_size); | ||
|
||
void audio_converter_free(struct AudioConverter **converter); | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#ifndef CHANNEL_LAYOUT_H | ||
#define CHANNEL_LAYOUT_H | ||
#include <libavutil/channel_layout.h> | ||
|
||
struct ChannelLayout { | ||
#if LIBAVUTIL_VERSION_MAJOR >= 58 | ||
AVChannelLayout layout; | ||
#else | ||
uint64_t layout; | ||
#endif | ||
}; | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,23 @@ | ||
#include <libavcodec/avcodec.h> | ||
#include <libswresample/swresample.h> | ||
|
||
#include "audio_converter.h" | ||
#include "utils.h" | ||
|
||
struct Decoder { | ||
enum AVMediaType media_type; | ||
AVFrame *frame; | ||
AVPacket *pkt; | ||
const AVCodec *codec; | ||
AVCodecContext *c; | ||
SwrContext *swr_ctx; | ||
|
||
const char *out_format_name; | ||
|
||
uint8_t *rgb_dst_data[4]; | ||
int rgb_dst_linesize[4]; | ||
|
||
uint8_t **frame_data; | ||
int *frame_linesize; | ||
}; | ||
|
||
struct Decoder *decoder_alloc(); | ||
|
||
int decoder_init(struct Decoder *decoder, const char *codec); | ||
|
||
int decoder_decode(struct Decoder *decoder, AVPacket *pkt, AVFrame *frame); | ||
|
||
void decoder_free(struct Decoder *decoder); | ||
void decoder_free_frame(struct Decoder *decoder); | ||
|
||
void decoder_free(struct Decoder **decoder); |
Oops, something went wrong.