summaryrefslogtreecommitdiff
path: root/src/codec/ffmpeg
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2024-10-21 19:22:50 -0400
committerAndrew Opalach <andrew@akon.city> 2024-10-21 19:22:50 -0400
commit60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2 (patch)
tree08ff2ce7975f523112e7ad2fe4f797b4fc7db5de /src/codec/ffmpeg
parent2f9a0945bfeee3296cec3d38d094e4c49f9cb65f (diff)
downloadcamu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.tar.gz
camu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.tar.bz2
camu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.zip
Everything before initial synced list
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/codec/ffmpeg')
-rw-r--r--src/codec/ffmpeg/common.c32
-rw-r--r--src/codec/ffmpeg/common.h6
-rw-r--r--src/codec/ffmpeg/decoder.c60
-rw-r--r--src/codec/ffmpeg/decoder.h8
-rw-r--r--src/codec/ffmpeg/demuxer.c57
-rw-r--r--src/codec/ffmpeg/demuxer.h1
-rw-r--r--src/codec/ffmpeg/encoder.c126
-rw-r--r--src/codec/ffmpeg/encoder.h7
-rw-r--r--src/codec/ffmpeg/packet_ext.c38
-rw-r--r--src/codec/ffmpeg/packet_ext.h2
-rw-r--r--src/codec/ffmpeg/resampler.c173
-rw-r--r--src/codec/ffmpeg/resampler.h34
12 files changed, 202 insertions, 342 deletions
diff --git a/src/codec/ffmpeg/common.c b/src/codec/ffmpeg/common.c
index 57caff7..b7a232e 100644
--- a/src/codec/ffmpeg/common.c
+++ b/src/codec/ffmpeg/common.c
@@ -1,6 +1,38 @@
+#include <al/lib.h>
+#include <al/log.h>
+#include <libavutil/log.h>
+
#include "common.h"
void camu_ff_set_log_callback(void (*callback)(void *, int, const char *, va_list))
{
av_log_set_callback(callback);
}
+
+static char *av_log_buf = NULL;
+static s32 av_log_pos = 0;
+
+static void av_log_callback(void *userdata, int level, const char *fmt, va_list args)
+{
+ (void)userdata;
+ al_assert(av_log_buf);
+ if (level < AV_LOG_DEBUG) {
+ av_log_pos += al_vsnprintf(&av_log_buf[av_log_pos], 512, fmt, args);
+ if (av_log_buf[av_log_pos - 1] == '\n' || av_log_pos >= 512) {
+ al_log_info("ff_log", av_log_buf);
+ av_log_pos = 0;
+ }
+ }
+}
+
+void camu_ff_set_default_log_callback()
+{
+ av_log_buf = (char *)al_malloc(1024);
+ camu_ff_set_log_callback(av_log_callback);
+}
+
+
+void camu_ff_free_default_log_callback()
+{
+ al_free(av_log_buf);
+}
diff --git a/src/codec/ffmpeg/common.h b/src/codec/ffmpeg/common.h
index ded7b35..229ffb0 100644
--- a/src/codec/ffmpeg/common.h
+++ b/src/codec/ffmpeg/common.h
@@ -1,6 +1,10 @@
#pragma once
#include <al/types.h>
-#include <libavutil/log.h>
+#include <al/log.h>
+// userdata, level, fmt, args
void camu_ff_set_log_callback(void (*callback)(void *, int, const char *, va_list));
+
+void camu_ff_set_default_log_callback();
+void camu_ff_free_default_log_callback();
diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c
index 7071bea..d231b2f 100644
--- a/src/codec/ffmpeg/decoder.c
+++ b/src/codec/ffmpeg/decoder.c
@@ -1,8 +1,6 @@
-#include <libavutil/cpu.h>
#include <al/lib.h>
#include <al/log.h>
-
-//#include "../../render/renderer.h"
+#include <libavutil/cpu.h>
#include "decoder.h"
@@ -11,7 +9,8 @@ static void close_internal(struct camu_ff_decoder *av)
if (av->codec_context) avcodec_free_context(&av->codec_context);
}
-static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_stream *stream)
+static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_codec_stream *stream,
+ void (*callback)(void *, struct camu_codec_frame *), void *userdata)
{
struct camu_ff_decoder *av = (struct camu_ff_decoder *)dec;
@@ -37,49 +36,45 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend
/*
s32 cpus = av_cpu_count();
- cpus = (cpus > 16) ? 4 : cpus / 4;
- if (cpus == 0) cpus = 1;
+ if (cpus > 4) cpus = 2;
av->codec_context->thread_count = cpus;
+ av->codec_context->thread_type = FF_THREAD_SLICE;
al_log_debug("ff_decoder", "Using %i threads for decoder.", cpus);
*/
- (void)renderer;
- /*
- if (renderer && renderer->get_buffer2 && codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+#ifndef CAMU_SINK_NO_VIDEO
+ if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO && renderer && renderer->get_buffer2) {
av->codec_context->get_buffer2 = renderer->get_buffer2;
av->codec_context->opaque = renderer->opaque;
}
- */
+#else
+ (void)renderer;
+#endif
if (avcodec_open2(av->codec_context, codec, NULL) < 0) {
al_log_error("ff_decoder", "Failed to open codec (%s).", codec->name);
goto err;
}
- if (codec->long_name) {
- al_log_info("ff_decoder", "Codec: %s (%s) %ldkbps.", codec->name, codec->long_name, codecpar->bit_rate / 1000);
- } else {
- al_log_info("ff_decoder", "Codec: %s %ldkbps.", codec->name, codecpar->bit_rate / 1000);
- }
+ const char *long_name = codec->long_name ? codec->long_name : codec->name;
+ s64 kbps = codecpar->bit_rate > 0 ? codecpar->bit_rate / 1000L : 0L;
+ al_log_info("ff_decoder", "Codec: %s (%s) %ldkbps.", codec->name, long_name, kbps);
av->time_base = stream->av.stream->time_base;
- av->last_pts = 0;
- av->last_duration = 0;
+ av->duration = stream->av.stream->duration;
+ //av->last_pts = 0;
+ //av->last_duration = 0;
//av->seek_pos = -1;
+ av->callback = callback;
+ av->userdata = userdata;
+
return true;
err:
close_internal(av);
return false;
}
-static void ff_decoder_set_callback(struct camu_decoder *dec, void (*callback)(void *, struct camu_frame *), void *userdata)
-{
- struct camu_ff_decoder *av = (struct camu_ff_decoder *)dec;
- av->callback = callback;
- av->userdata = userdata;
-}
-
static s32 send_packet(struct camu_ff_decoder *av, AVPacket *pkt)
{
/*
@@ -92,16 +87,16 @@ static s32 send_packet(struct camu_ff_decoder *av, AVPacket *pkt)
s32 ret = avcodec_send_packet(av->codec_context, pkt);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
- al_log_error("ff_decoder", "Error sending packet to decoder: (%s).", av_err2str(ret));
+ al_log_error("ff_decoder", "Error sending packet to the decoder: (%s).", av_err2str(ret));
}
return ret;
}
-static s32 ff_decoder_push(struct camu_decoder *dec, struct camu_packet *packet)
+static s32 ff_decoder_push(struct camu_decoder *dec, struct camu_codec_packet *packet)
{
struct camu_ff_decoder *av = (struct camu_ff_decoder *)dec;
- return send_packet(av, (packet) ? packet->av.pkt : NULL);
+ return send_packet(av, packet ? packet->av.pkt : NULL);
}
static void ff_decoder_flush(struct camu_decoder *dec)
@@ -114,19 +109,19 @@ static s32 receive_frames(struct camu_ff_decoder *av)
{
s32 ret;
do {
- struct camu_frame *frame = al_alloc_object(struct camu_frame);
- frame->type = CAMU_FFMPEG_COMPAT;
+ struct camu_codec_frame *frame = al_alloc_object(struct camu_codec_frame);
+ frame->mode = CAMU_FFMPEG_COMPAT;
frame->av.frame = av_frame_alloc();
ret = avcodec_receive_frame(av->codec_context, frame->av.frame);
if (ret < 0) {
av_frame_free(&frame->av.frame);
al_free(frame);
+ // Checking for EAGAIN should prevent an infinite loop.
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;
- al_log_error("ff_decoder", "Error receiving packet from decoder: (%s).", av_err2str(ret));
+ al_log_error("ff_decoder", "Error receiving packet from the decoder: (%s).", av_err2str(ret));
continue;
}
- // Track pts and duration of the previous frame so we can handle multiple frames
- // in a single packet.
+ // Track pts and duration of the previous frame so we can handle multiple frames in a single packet.
//if (av->codec_context->codec_type == AVMEDIA_TYPE_AUDIO) {
// if (frame->av.frame->best_effort_timestamp < 0) {
// frame->av.frame->best_effort_timestamp = av->last_pts + av->last_duration;
@@ -158,7 +153,6 @@ struct camu_decoder *camu_ff_decoder_create(void)
{
struct camu_ff_decoder *av = al_alloc_object(struct camu_ff_decoder);
av->dec.init = ff_decoder_init;
- av->dec.set_callback = ff_decoder_set_callback;
av->dec.push = ff_decoder_push;
av->dec.process = ff_decoder_process;
av->dec.flush = ff_decoder_flush;
diff --git a/src/codec/ffmpeg/decoder.h b/src/codec/ffmpeg/decoder.h
index 45cca00..88bbd4c 100644
--- a/src/codec/ffmpeg/decoder.h
+++ b/src/codec/ffmpeg/decoder.h
@@ -1,7 +1,6 @@
#pragma once
#include <al/types.h>
-
#include <libavcodec/avcodec.h>
#include "../codec.h"
@@ -10,10 +9,11 @@ struct camu_ff_decoder {
struct camu_decoder dec;
AVCodecContext *codec_context;
AVRational time_base;
- s64 last_pts;
- s64 last_duration;
+ u64 duration;
+ //s64 last_pts;
+ //s64 last_duration;
//s64 seek_pos;
- void (*callback)(void *, struct camu_frame *);
+ void (*callback)(void *, struct camu_codec_frame *);
void *userdata;
};
diff --git a/src/codec/ffmpeg/demuxer.c b/src/codec/ffmpeg/demuxer.c
index 9b26596..7ff0dca 100644
--- a/src/codec/ffmpeg/demuxer.c
+++ b/src/codec/ffmpeg/demuxer.c
@@ -3,7 +3,7 @@
#include "demuxer.h"
#include "avio.h"
-#define BUF_SIZE 16384
+#define DEMUX_BUF_SIZE (4096 * 4)
static void close_internal(struct camu_ff_demuxer *av)
{
@@ -28,16 +28,16 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl
goto err;
}
- u8 *buf = (u8 *)av_malloc(BUF_SIZE);
- av->io_context = avio_alloc_context(buf, BUF_SIZE, 0, handle, camu_avio_read, NULL, camu_avio_seek);
+ u8 *buf = (u8 *)av_malloc(DEMUX_BUF_SIZE);
+ av->io_context = avio_alloc_context(buf, DEMUX_BUF_SIZE, 0, handle, camu_avio_read, NULL, camu_avio_seek);
if (!av->io_context) {
al_log_error("ff_demuxer", "Failed to create custom io context.");
goto err;
}
if (!cch_handle_can_seek(handle)) {
- // This should always be unset if we know we cannot seek.
- // But, should also never be unset if we *do* want to seek because it
+ // AVIO_SEEKABLE_NORMAL should always be unset if we know we cannot seek.
+ // However, it should also never be unset if we *do* want to seek because it
// disables long seeks (Not tested in a long time).
av->io_context->seekable &= ~AVIO_SEEKABLE_NORMAL;
} else {
@@ -47,12 +47,19 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl
av->format_context->pb = av->io_context;
av->format_context->flags |= AVFMT_FLAG_CUSTOM_IO;
- if (avformat_open_input(&av->format_context, "", 0, 0) < 0) {
+ AVDictionary *opts = NULL;
+ av_dict_set(&opts, "analyzeduration", "10000000", 0);
+ av_dict_set(&opts, "probesize", "20M", 0);
+
+ if (avformat_open_input(&av->format_context, "file:", NULL, &opts) < 0) {
al_log_error("ff_demuxer", "Failed to open input.");
+ av_dict_free(&opts);
goto err;
}
- if (avformat_find_stream_info(av->format_context, 0) < 0) {
+ av_dict_free(&opts);
+
+ if (avformat_find_stream_info(av->format_context, NULL) < 0) {
al_log_error("ff_demuxer", "Failed to find stream info.");
goto err;
}
@@ -70,6 +77,7 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl
for (u32 i = 0; i < av->format_context->nb_streams; i++) {
AVStream *stream = av->format_context->streams[i];
enum AVMediaType type = stream->codecpar->codec_type;
+ if (type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_VIDEO) type = AVMEDIA_TYPE_UNKNOWN;
// Try to detect attached images.
if (type == AVMEDIA_TYPE_VIDEO && GUESS_STREAM_IS_IMAGE(stream)) {
al_log_info("ff_demuxer", "Guessing that stream #%u is an image.", i);
@@ -83,12 +91,14 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl
}
al_log_info("ff_demuxer", "Setting stream #%u to a duration of %.3fs.", i, stream->duration * av_q2d(stream->time_base));
}
- if (stream->start_time < 0) stream->start_time = 0;
+ if (stream->start_time == AV_NOPTS_VALUE) stream->start_time = 0;
s64 duration = av_rescale_q(stream->duration, stream->time_base, AV_TIME_BASE_Q);
al_assert(duration >= 0);
if ((u64)duration > av->duration) av->duration = (u64)duration;
- al_array_push(av->demux.streams, ((struct camu_stream){
- .type = CAMU_FFMPEG_COMPAT,
+ al_array_push(av->demux.streams, ((struct camu_codec_stream){
+ .mode = CAMU_FFMPEG_COMPAT,
+ .type = type,
+ .duration = (u64)duration,
.av.stream = stream
}));
}
@@ -106,30 +116,15 @@ err:
return false;
}
-/*
-static AVStream *av_stream_at_index(struct camu_ff_demuxer *av, s32 index)
-{
- struct camu_stream *stream;
- al_array_foreach_ptr(av->demux.streams, i, stream) {
- if (stream->av.stream->index == index) return stream->av.stream;
- }
- return NULL;
-}
-*/
-
static bool subscribed_to_index(struct camu_demuxer *demux, s32 index)
{
- s32 subscribed;
- al_array_foreach(demux->subscribed, i, subscribed) {
- if (subscribed == index) return true;
- }
- return false;
+ return ((demux->subscribed >> index) & 1);
}
-static s32 ff_demuxer_get_packet(struct camu_demuxer *demux, struct camu_packet *packet)
+static s32 ff_demuxer_get_packet(struct camu_demuxer *demux, struct camu_codec_packet *packet)
{
struct camu_ff_demuxer *av = (struct camu_ff_demuxer *)demux;
- packet->type = CAMU_FFMPEG_COMPAT;
+ packet->mode = CAMU_FFMPEG_COMPAT;
s32 ret = 0;
do {
ret = av_read_frame(av->format_context, packet->av.pkt);
@@ -160,12 +155,13 @@ static bool ff_demuxer_seek(struct camu_demuxer *demux, u64 pos)
{
struct camu_ff_demuxer *av = (struct camu_ff_demuxer *)demux;
if (pos > av->duration) pos = av->duration;
+ al_assert(pos <= INT64_MAX);
s64 ts = av_rescale_q((s64)pos, (AVRational){ 1, 1000000 }, AV_TIME_BASE_Q);
if (av->eof) {
avformat_flush(av->format_context);
av->eof = false;
}
- if (avformat_seek_file(av->format_context, -1, INT64_MIN, ts, ts, AVSEEK_FLAG_BACKWARD) < 0) {
+ if (avformat_seek_file(av->format_context, -1, INT64_MIN, ts, ts, 0) < 0) {
return false;
}
av->seeked = true;
@@ -184,9 +180,8 @@ static void ff_demuxer_free(struct camu_demuxer **demux)
struct camu_demuxer *camu_ff_demuxer_create(void)
{
struct camu_ff_demuxer *av = al_alloc_object(struct camu_ff_demuxer);
- av->demux.type = CAMU_FFMPEG_COMPAT;
+ av->demux.mode = CAMU_FFMPEG_COMPAT;
al_array_init(av->demux.streams);
- al_array_init(av->demux.subscribed);
av->demux.init = ff_demuxer_init;
av->demux.get_packet = ff_demuxer_get_packet;
av->demux.get_duration = ff_demuxer_get_duration;
diff --git a/src/codec/ffmpeg/demuxer.h b/src/codec/ffmpeg/demuxer.h
index 8327534..68ad03e 100644
--- a/src/codec/ffmpeg/demuxer.h
+++ b/src/codec/ffmpeg/demuxer.h
@@ -3,7 +3,6 @@
#include <al/types.h>
#include <al/lib.h>
#include <al/array.h>
-
#include <libavformat/avformat.h>
#include "../codec.h"
diff --git a/src/codec/ffmpeg/encoder.c b/src/codec/ffmpeg/encoder.c
index 4617baf..83a8b55 100644
--- a/src/codec/ffmpeg/encoder.c
+++ b/src/codec/ffmpeg/encoder.c
@@ -1,3 +1,5 @@
+#include <al/lib.h>
+#include <al/log.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/codec.h>
#include <libavformat/avformat.h>
@@ -6,13 +8,12 @@
#include <libavutil/error.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
-#include <al/lib.h>
-#include <al/log.h>
#include "encoder.h"
#define BITRATE 256000
-#define FRAME_SIZE 20
+#define FRAME_DURATION 20
+#define SAMPLES_PER_FRAME (48000 / 1000.0 * FRAME_DURATION)
#define CHANNELS 2
static void free_frame_data(struct camu_ff_encoder *enc)
@@ -20,15 +21,6 @@ static void free_frame_data(struct camu_ff_encoder *enc)
if (enc->frame && enc->frame->nb_samples > 0) av_freep(&enc->frame->data[0]);
}
-static void alloc_frame_data(struct camu_ff_encoder *enc, s32 frame_size)
-{
- if (enc->frame->nb_samples >= frame_size) return;
- free_frame_data(enc);
- s32 nb_channels = enc->frame->ch_layout.nb_channels;
- av_samples_alloc(enc->frame->data, NULL, nb_channels, frame_size, enc->frame->format, 0);
- enc->frame->nb_samples = frame_size;
-}
-
bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext)
{
al_memset(enc, 0, sizeof(struct camu_ff_encoder));
@@ -51,7 +43,7 @@ bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char
av_channel_layout_default(&enc->codec_context->ch_layout, CHANNELS);
enc->codec_context->sample_fmt = AV_SAMPLE_FMT_FLT;
enc->codec_context->bit_rate = BITRATE;
- enc->codec_context->frame_size = FRAME_SIZE;
+ enc->codec_context->frame_size = SAMPLES_PER_FRAME;
enc->codec_context->time_base = (AVRational){ 1, enc->codec_context->sample_rate };
enc->codec_context->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
@@ -77,23 +69,17 @@ bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char
AVCodecContext *out_codec = enc->codec_context;
- av_channel_layout_copy(&enc->fmt.req_channel_layout, &out_codec->ch_layout);
- enc->fmt.req_sample_rate = out_codec->sample_rate;
- enc->fmt.req_format = out_codec->sample_fmt;
-
- enc->audio_fifo = av_audio_fifo_alloc(enc->fmt.req_format,
- enc->fmt.req_channel_layout.nb_channels, 1);
-
- if (!enc->audio_fifo) {
- al_log_error("ff_encoder", "Failed to alloc fifo.");
- goto err;
- }
+ enc->fmt.req.format = out_codec->sample_fmt;
+ enc->fmt.req.sample_rate = out_codec->sample_rate;
+ av_channel_layout_copy(&enc->fmt.req.channel_layout, &out_codec->ch_layout);
+ enc->fmt.req.channel_count = out_codec->ch_layout.nb_channels;
enc->frame = av_frame_alloc();
- enc->frame->nb_samples = 0;
- enc->frame->sample_rate = enc->fmt.req_sample_rate;
- av_channel_layout_copy(&enc->frame->ch_layout, &enc->fmt.req_channel_layout);
- enc->frame->format = enc->fmt.req_format;
+ enc->frame->format = enc->fmt.req.format;
+ enc->frame->sample_rate = enc->fmt.req.sample_rate;
+ av_channel_layout_copy(&enc->frame->ch_layout, &enc->fmt.req.channel_layout);
+ av_samples_alloc(enc->frame->data, NULL, enc->frame->ch_layout.nb_channels, SAMPLES_PER_FRAME, enc->frame->format, 1);
+ enc->frame->nb_samples = SAMPLES_PER_FRAME;
enc->frame->pts = 0;
return true;
@@ -104,93 +90,31 @@ err:
void camu_ff_encoder_push(struct camu_ff_encoder *enc, u8 **data, s32 sample_count)
{
- s32 alloc_size = av_audio_fifo_size(enc->audio_fifo) + sample_count;
- if (av_audio_fifo_space(enc->audio_fifo) < alloc_size) {
- if (av_audio_fifo_realloc(enc->audio_fifo, alloc_size) < 0) {
- al_assert(false);
- }
+ al_assert(sample_count == SAMPLES_PER_FRAME * CHANNELS);
+ enc->frame->data[0] = data[0];
+ s32 ret = avcodec_send_frame(enc->codec_context, enc->frame);
+ if (ret < 0 && ret != AVERROR_EOF) {
+ al_log_error("ff_encoder", "Error sending frame to the encoder: (%s).", av_err2str(ret));
+ return;
}
- av_audio_fifo_write(enc->audio_fifo, (void **)data, sample_count);
-}
-
-static bool receive_packets(struct camu_ff_encoder *enc)
-{
- s32 ret;
AVPacket pkt = { 0 };
-
- do {
- ret = avcodec_receive_packet(enc->codec_context, &pkt);
- if (ret < 0) break;
- enc->callback(enc->userdata, &pkt);
- } while (1);
-
- if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
- al_log_error("ff_encoder", "Error receiving packet from encoder: (%s).", av_err2str(ret));
- return false;
- }
-
- return true;
-}
-
-static s32 send_frame(struct camu_ff_encoder *enc, AVFrame *in_frame)
-{
- s32 ret = avcodec_send_frame(enc->codec_context, in_frame);
+ ret = avcodec_receive_packet(enc->codec_context, &pkt);
if (ret < 0 && ret != AVERROR_EOF) {
- al_log_error("ff_encoder", "Error sending frame to encoder: (%s).", av_err2str(ret));
+ al_log_error("ff_encoder", "Error receiving packet from the encoder: (%s).", av_err2str(ret));
+ return;
}
- return ret;
-}
-
-static s32 read_frame(struct camu_ff_encoder *enc, s32 frame_size)
-{
- alloc_frame_data(enc, frame_size);
- return av_audio_fifo_read(enc->audio_fifo, (void **)enc->frame->data, frame_size);
-}
-
-bool camu_ff_encoder_process(struct camu_ff_encoder *enc, bool flush)
-{
- s32 samples_available = av_audio_fifo_size(enc->audio_fifo);
- if (samples_available == 0 && flush) {
- // Flush encoder.
- send_frame(enc, NULL);
- receive_packets(enc);
- // This should guarantee that there are not packets left.
- return false;
- }
-
- s32 frame_size = enc->codec_context->frame_size;
- if (samples_available == 0 || (samples_available < frame_size && !flush)) {
- return false;
- }
-
- s32 ret;
- do {
- if (samples_available < frame_size) frame_size = samples_available;
- read_frame(enc, frame_size);
- samples_available -= frame_size;
- do {
- ret = send_frame(enc, enc->frame);
- if (ret < 0) break;
- if (!receive_packets(enc)) return false;
- } while (ret == AVERROR(EAGAIN));
- if (ret < 0) break;
- enc->frame->pts += frame_size;
- } while (samples_available > 0);
-
- // ret < 0 means encoder errored while sending frame.
- return ret == 0;
+ enc->callback(enc->userdata, &pkt);
+ enc->frame->pts += SAMPLES_PER_FRAME;
}
void camu_ff_encoder_reset(struct camu_ff_encoder *enc)
{
avcodec_flush_buffers(enc->codec_context);
- av_audio_fifo_reset(enc->audio_fifo);
}
void camu_ff_encoder_close(struct camu_ff_encoder *enc)
{
if (enc->codec_context) avcodec_free_context(&enc->codec_context);
- if (enc->audio_fifo) av_audio_fifo_free(enc->audio_fifo);
free_frame_data(enc);
if (enc->frame) av_frame_free(&enc->frame);
}
diff --git a/src/codec/ffmpeg/encoder.h b/src/codec/ffmpeg/encoder.h
index 9bfe8ea..c0d953c 100644
--- a/src/codec/ffmpeg/encoder.h
+++ b/src/codec/ffmpeg/encoder.h
@@ -5,16 +5,14 @@
#include <libavutil/audio_fifo.h>
#include <libavutil/rational.h>
-#include "resampler.h"
+#include "../codec.h"
struct camu_ff_encoder {
AVCodecContext *codec_context;
const AVOutputFormat *output_format;
- struct camu_ff_resample_fmt fmt;
-
+ struct camu_resampler_format fmt;
AVFrame *frame;
- AVAudioFifo *audio_fifo;
void (*callback)(void *, AVPacket *);
void *userdata;
@@ -22,6 +20,5 @@ struct camu_ff_encoder {
bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext);
void camu_ff_encoder_push(struct camu_ff_encoder *enc, u8 **data, s32 sample_count);
-bool camu_ff_encoder_process(struct camu_ff_encoder *enc, bool flush);
void camu_ff_encoder_reset(struct camu_ff_encoder *enc);
void camu_ff_encoder_close(struct camu_ff_encoder *enc);
diff --git a/src/codec/ffmpeg/packet_ext.c b/src/codec/ffmpeg/packet_ext.c
index 8fe4c39..c0e400d 100644
--- a/src/codec/ffmpeg/packet_ext.c
+++ b/src/codec/ffmpeg/packet_ext.c
@@ -16,6 +16,7 @@ void aki_packet_write_av_codec_parameters(struct aki_packet *packet, AVCodecPara
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->width);
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->height);
AKI_PACKET_WRITE_TYPE(packet, AVRational, codecpar->sample_aspect_ratio);
+ AKI_PACKET_WRITE_TYPE(packet, AVRational, codecpar->framerate);
AKI_PACKET_WRITE_TYPE(packet, enum AVFieldOrder, codecpar->field_order);
AKI_PACKET_WRITE_TYPE(packet, enum AVColorRange, codecpar->color_range);
AKI_PACKET_WRITE_TYPE(packet, enum AVColorPrimaries, codecpar->color_primaries);
@@ -23,18 +24,13 @@ void aki_packet_write_av_codec_parameters(struct aki_packet *packet, AVCodecPara
AKI_PACKET_WRITE_TYPE(packet, enum AVColorSpace, codecpar->color_space);
AKI_PACKET_WRITE_TYPE(packet, enum AVChromaLocation, codecpar->chroma_location);
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->video_delay);
+ AKI_PACKET_WRITE_TYPE(packet, AVChannelLayout, codecpar->ch_layout);
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->sample_rate);
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->block_align);
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->frame_size);
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->initial_padding);
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->trailing_padding);
AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->seek_preroll);
- AKI_PACKET_WRITE_TYPE(packet, AVChannelLayout, codecpar->ch_layout);
-}
-
-void aki_packet_write_av_rational(struct aki_packet *packet, AVRational rational)
-{
- AKI_PACKET_WRITE_TYPE(packet, AVRational, rational);
}
void aki_packet_write_av_codec_id(struct aki_packet *packet, enum AVCodecID codec_id)
@@ -46,15 +42,15 @@ void aki_packet_write_av_stream(struct aki_packet *packet, AVStream *stream)
{
AKI_PACKET_WRITE_TYPE(packet, s32, stream->index);
aki_packet_write_av_codec_parameters(packet, stream->codecpar);
- aki_packet_write_av_rational(packet, stream->time_base);
+ AKI_PACKET_WRITE_TYPE(packet, AVRational, stream->time_base);
AKI_PACKET_WRITE_TYPE(packet, s64, stream->duration);
AKI_PACKET_WRITE_TYPE(packet, s64, stream->start_time);
AKI_PACKET_WRITE_TYPE(packet, s64, stream->nb_frames);
if (stream->avg_frame_rate.den == 0) {
// r_frame_rate.den == 0 handled on the client.
- aki_packet_write_av_rational(packet, stream->r_frame_rate);
+ AKI_PACKET_WRITE_TYPE(packet, AVRational, stream->r_frame_rate);
} else {
- aki_packet_write_av_rational(packet, stream->avg_frame_rate);
+ AKI_PACKET_WRITE_TYPE(packet, AVRational, stream->avg_frame_rate);
}
}
@@ -68,7 +64,9 @@ void aki_packet_write_av_packet(struct aki_packet *packet, AVPacket *pkt)
AKI_PACKET_WRITE_TYPE(packet, s32, pkt->flags);
AKI_PACKET_WRITE_TYPE(packet, s32, pkt->side_data_elems);
for (s32 i = 0; i < pkt->side_data_elems; i++) {
- AKI_PACKET_WRITE_TYPE(packet, size_t, pkt->side_data[i].size);
+ // Possibly upcast size_t.
+ s64 size = (s64)pkt->side_data[i].size;
+ AKI_PACKET_WRITE_TYPE(packet, s64, size);
AKI_PACKET_WRITE_DATA(packet, pkt->side_data[i].data, pkt->side_data[i].size);
AKI_PACKET_WRITE_TYPE(packet, enum AVPacketSideDataType, pkt->side_data[i].type);
}
@@ -96,6 +94,7 @@ void aki_packet_read_av_codec_parameters(struct aki_packet *packet, AVCodecParam
AKI_PACKET_READ_TYPE(packet, s32, codecpar->width);
AKI_PACKET_READ_TYPE(packet, s32, codecpar->height);
AKI_PACKET_READ_TYPE(packet, AVRational, codecpar->sample_aspect_ratio);
+ AKI_PACKET_READ_TYPE(packet, AVRational, codecpar->framerate);
AKI_PACKET_READ_TYPE(packet, enum AVFieldOrder, codecpar->field_order);
AKI_PACKET_READ_TYPE(packet, enum AVColorRange, codecpar->color_range);
AKI_PACKET_READ_TYPE(packet, enum AVColorPrimaries, codecpar->color_primaries);
@@ -103,20 +102,13 @@ void aki_packet_read_av_codec_parameters(struct aki_packet *packet, AVCodecParam
AKI_PACKET_READ_TYPE(packet, enum AVColorSpace, codecpar->color_space);
AKI_PACKET_READ_TYPE(packet, enum AVChromaLocation, codecpar->chroma_location);
AKI_PACKET_READ_TYPE(packet, s32, codecpar->video_delay);
+ AKI_PACKET_READ_TYPE(packet, AVChannelLayout, codecpar->ch_layout);
AKI_PACKET_READ_TYPE(packet, s32, codecpar->sample_rate);
AKI_PACKET_READ_TYPE(packet, s32, codecpar->block_align);
AKI_PACKET_READ_TYPE(packet, s32, codecpar->frame_size);
AKI_PACKET_READ_TYPE(packet, s32, codecpar->initial_padding);
AKI_PACKET_READ_TYPE(packet, s32, codecpar->trailing_padding);
AKI_PACKET_READ_TYPE(packet, s32, codecpar->seek_preroll);
- AKI_PACKET_READ_TYPE(packet, AVChannelLayout, codecpar->ch_layout);
-}
-
-AVRational aki_packet_read_av_rational(struct aki_packet *packet)
-{
- AVRational r;
- AKI_PACKET_READ_TYPE(packet, AVRational, r);
- return r;
}
enum AVCodecID aki_packet_read_av_codec_id(struct aki_packet *packet)
@@ -131,11 +123,11 @@ AVStream *aki_packet_read_av_stream(AVFormatContext *format_context, const AVCod
AVStream *stream = avformat_new_stream(format_context, codec);
AKI_PACKET_READ_TYPE(packet, s32, stream->index);
aki_packet_read_av_codec_parameters(packet, stream->codecpar);
- stream->time_base = aki_packet_read_av_rational(packet);
+ AKI_PACKET_READ_TYPE(packet, AVRational, stream->time_base);
AKI_PACKET_READ_TYPE(packet, s64, stream->duration);
AKI_PACKET_READ_TYPE(packet, s64, stream->start_time);
AKI_PACKET_READ_TYPE(packet, s64, stream->nb_frames);
- stream->avg_frame_rate = aki_packet_read_av_rational(packet);
+ AKI_PACKET_READ_TYPE(packet, AVRational, stream->avg_frame_rate);
return stream;
}
@@ -152,10 +144,12 @@ AVPacket *aki_packet_read_av_packet(struct aki_packet *packet)
AKI_PACKET_READ_TYPE(packet, s32, side_data_elems);
pkt->side_data_elems = 0;
for (s32 i = 0; i < side_data_elems; i++) {
- size_t size;
u8 *data;
enum AVPacketSideDataType type;
- AKI_PACKET_READ_TYPE(packet, s32, size);
+ s64 storage_for_size;
+ AKI_PACKET_READ_TYPE(packet, s64, storage_for_size);
+ al_assert((sizeof(size_t) > 4 || storage_for_size <= INT32_MAX));
+ size_t size = (size_t)storage_for_size;
AKI_PACKET_READ_DATA(packet, size, data);
AKI_PACKET_READ_TYPE(packet, enum AVPacketSideDataType, type);
u8 *side_data = av_packet_new_side_data(pkt, type, size);
diff --git a/src/codec/ffmpeg/packet_ext.h b/src/codec/ffmpeg/packet_ext.h
index ff6e337..36b6ed5 100644
--- a/src/codec/ffmpeg/packet_ext.h
+++ b/src/codec/ffmpeg/packet_ext.h
@@ -8,13 +8,11 @@
#include <libavformat/avformat.h>
void aki_packet_write_av_codec_parameters(struct aki_packet *packet, AVCodecParameters *codecpar);
-void aki_packet_write_av_rational(struct aki_packet *packet, AVRational rational);
void aki_packet_write_av_codec_id(struct aki_packet *packet, enum AVCodecID codec_id);
void aki_packet_write_av_stream(struct aki_packet *packet, AVStream *stream);
void aki_packet_write_av_packet(struct aki_packet *packet, AVPacket *pkt);
void aki_packet_read_av_codec_parameters(struct aki_packet *packet, AVCodecParameters *codecpar);
-AVRational aki_packet_read_av_rational(struct aki_packet *packet);
enum AVCodecID aki_packet_read_av_codec_id(struct aki_packet *packet);
AVStream *aki_packet_read_av_stream(AVFormatContext *format_context, const AVCodec *codec, struct aki_packet *packet);
AVPacket *aki_packet_read_av_packet(struct aki_packet *packet);
diff --git a/src/codec/ffmpeg/resampler.c b/src/codec/ffmpeg/resampler.c
index d4b281a..00418e6 100644
--- a/src/codec/ffmpeg/resampler.c
+++ b/src/codec/ffmpeg/resampler.c
@@ -1,161 +1,108 @@
+#include <al/lib.h>
+#include <al/log.h>
#include <libavutil/channel_layout.h>
#include <libavutil/frame.h>
-#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
+#include <libavutil/opt.h>
#include <libswresample/swresample.h>
-#include <al/lib.h>
-#include <al/log.h>
#include "resampler.h"
-static bool in_req_matches(struct camu_ff_resample_fmt *fmt)
+static bool ff_resampler_init(struct camu_resampler *resamp, struct camu_resampler_format *fmt)
{
- return (fmt->in_format == fmt->req_format) &&
- (av_channel_layout_compare(&fmt->in_channel_layout, &fmt->req_channel_layout) == 0) &&
- (fmt->in_sample_rate == fmt->req_sample_rate);
-}
-
-bool camu_ff_resampler_init(struct camu_ff_resampler *resamp, struct camu_ff_resample_fmt *fmt)
-{
- al_memset(resamp, 0, sizeof(struct camu_ff_resampler));
-
- fmt->resampler_needed = !in_req_matches(fmt);
- if (!fmt->resampler_needed) return true;
+ struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp;
+ al_assert(fmt->resampler_needed);
- camu_ff_resample_fmt_copy(&resamp->fmt, fmt);
-
- SwrContext *ctx = NULL;
- s32 ret = swr_alloc_set_opts2(&ctx,
- &fmt->req_channel_layout,
- fmt->req_format,
- fmt->req_sample_rate,
- &fmt->in_channel_layout,
- fmt->in_format,
- fmt->in_sample_rate,
- 0,
- NULL);
+ camu_audio_format_copy(&av->fmt, &fmt->req);
+ s32 ret = swr_alloc_set_opts2(&av->resample_context,
+ &fmt->req.channel_layout, fmt->req.format, fmt->req.sample_rate,
+ &fmt->in.channel_layout, fmt->in.format, fmt->in.sample_rate, 0, NULL);
if (ret != 0) {
al_log_error("lav_resampler", "Failed to configure resampler context.");
return false;
}
+#ifndef _WIN32
// Slower but more accurate resampler.
- //av_opt_set_int(ctx, "resampler", SWR_ENGINE_SOXR, 0);
+ av_opt_set_int(av->resample_context, "resampler", SWR_ENGINE_SOXR, 0);
+#endif
- if (swr_init(ctx) != 0) {
- swr_free(&ctx);
- al_log_error("lav_resampler", "Failed to open resampler context.");
+ if (swr_init(av->resample_context) != 0) {
+ swr_free(&av->resample_context);
+ al_log_error("lav_resampler", "Failed to init resampler context.");
return false;
}
- resamp->resample_context = ctx;
-
return true;
}
-s32 camu_ff_resampler_sample_count(struct camu_ff_resampler *resamp, s32 in_samples)
-{
- return swr_get_out_samples(resamp->resample_context, in_samples);
- //return av_rescale_rnd(swr_get_delay(resamp->resample_context, resamp->fmt.req_sample_rate) +
- // in_samples, resamp->fmt.in_sample_rate, resamp->fmt.req_sample_rate, AV_ROUND_UP);
-}
-
-static inline void free_sample_data(struct camu_ff_resampler *resamp)
+static inline void free_sample_data(struct camu_ff_resampler *av)
{
- if (resamp->data[0]) av_freep(&resamp->data[0]);
+ if (av->data[0]) av_freep(&av->data[0]);
}
-static inline void alloc_sample_data(struct camu_ff_resampler *resamp, s32 sample_count)
+static inline void alloc_sample_data(struct camu_ff_resampler *av, s32 sample_count)
{
- if (resamp->sample_count >= sample_count) return;
- free_sample_data(resamp);
- s32 nb_channels = resamp->fmt.req_channel_layout.nb_channels;
- av_samples_alloc(resamp->data, NULL, nb_channels, sample_count, resamp->fmt.req_format, 32);
- resamp->sample_count = sample_count;
+ if (av->sample_count >= sample_count) return;
+ free_sample_data(av);
+ s32 nb_channels = av->fmt.channel_layout.nb_channels;
+ av_samples_alloc(av->data, NULL, nb_channels, sample_count, av->fmt.format, 32);
+ av->sample_count = sample_count;
}
-s32 camu_ff_resampler_convert(struct camu_ff_resampler *resamp, const u8 **in_data, s32 in_samples)
+static s32 ff_resampler_convert(struct camu_resampler *resamp, const u8 **in_data, s32 in_samples)
{
+ struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp;
al_assert(in_samples > 0);
- s32 max_resample_count = camu_ff_resampler_sample_count(resamp, in_samples);
- alloc_sample_data(resamp, max_resample_count);
- s32 resample_count = swr_convert(resamp->resample_context, resamp->data,
- max_resample_count, in_data, in_samples);
+ s32 out_resample_count = swr_get_out_samples(av->resample_context, in_samples);
+ alloc_sample_data(av, out_resample_count);
+ s32 resample_count = swr_convert(av->resample_context, av->data,
+ out_resample_count, in_data, in_samples);
if (resample_count < 0) {
- al_log_error("lav_resampler", "Failed to convert samples (%i).", resample_count);
+ al_log_error("lav_resampler", "Failed to convert samples (%d).", resample_count);
}
return resample_count;
}
-u8 **camu_ff_resampler_get_data(struct camu_ff_resampler *resamp)
+static s32 ff_resampler_flush(struct camu_resampler *resamp)
{
- return (u8 **)resamp->data;
-}
-
-void camu_ff_resampler_close(struct camu_ff_resampler *resamp)
-{
- if (resamp->resample_context) {
- swr_close(resamp->resample_context);
- swr_free(&resamp->resample_context);
+ struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp;
+ s32 resample_count = swr_convert(av->resample_context, av->data,
+ av->sample_count, NULL, 0);
+ if (swr_init(av->resample_context) != 0) {
+ swr_free(&av->resample_context);
+ al_log_error("lav_resampler", "Failed to re-init resampler context after flush.");
+ return -1;
}
- free_sample_data(resamp);
-}
-
-void camu_ff_resample_fmt_copy(struct camu_ff_resample_fmt *dest, struct camu_ff_resample_fmt *src)
-{
- dest->resampler_needed = src->resampler_needed;
- dest->in_format = src->in_format;
- av_channel_layout_copy(&dest->in_channel_layout, &src->in_channel_layout);
- dest->in_sample_rate = src->in_sample_rate;
- dest->req_format = src->req_format;
- av_channel_layout_copy(&dest->req_channel_layout, &src->req_channel_layout);
- dest->req_sample_rate = src->req_sample_rate;
-}
-
-size_t camu_ff_resample_fmt_bytes_per_sample(struct camu_ff_resample_fmt *fmt)
-{
- return (size_t)av_get_bytes_per_sample(fmt->req_format);
-}
-
-size_t camu_ff_resample_fmt_samples_to_bytes(struct camu_ff_resample_fmt *fmt, size_t samples)
-{
- s32 size;
- s32 ret = av_samples_get_buffer_size(&size, fmt->req_channel_layout.nb_channels, (s32)samples, fmt->req_format, 1);
- al_assert(ret >= 0);
- return (size_t)size;
-}
-
-size_t camu_ff_resample_fmt_samples_to_usec(struct camu_ff_resample_fmt *fmt, size_t samples)
-{
- return samples / (fmt->req_sample_rate / 1000000.0);
-}
-
-f64 camu_ff_resample_fmt_samples_to_sec(struct camu_ff_resample_fmt *fmt, size_t samples)
-{
- return samples / (f64)fmt->req_sample_rate;
-}
-
-size_t camu_ff_resample_fmt_usec_to_bytes(struct camu_ff_resample_fmt *fmt, size_t usec)
-{
- size_t samples = usec * (fmt->req_sample_rate / 1000000.0);
- return samples * (camu_ff_resample_fmt_bytes_per_sample(fmt) * fmt->req_channel_layout.nb_channels);
+ return resample_count;
}
-size_t camu_ff_resample_fmt_sec_to_bytes(struct camu_ff_resample_fmt *fmt, f64 sec)
+static u8 **ff_resampler_get_data(struct camu_resampler *resamp)
{
- size_t samples = sec * fmt->req_sample_rate;
- return samples * (camu_ff_resample_fmt_bytes_per_sample(fmt) * fmt->req_channel_layout.nb_channels);
+ struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp;
+ return (u8 **)av->data;
}
-size_t camu_ff_resample_fmt_bytes_to_usec(struct camu_ff_resample_fmt *fmt, size_t bytes)
+static void ff_resampler_free(struct camu_resampler **resamp)
{
- size_t samples = bytes / (camu_ff_resample_fmt_bytes_per_sample(fmt) * fmt->req_channel_layout.nb_channels);
- return samples / (fmt->req_sample_rate / 1000000.0);
+ struct camu_ff_resampler *av = (struct camu_ff_resampler *)*resamp;
+ if (av->resample_context) {
+ swr_close(av->resample_context);
+ swr_free(&av->resample_context);
+ }
+ free_sample_data(av);
+ al_free(av);
+ *resamp = NULL;
}
-f64 camu_ff_resample_fmt_bytes_to_sec(struct camu_ff_resample_fmt *fmt, size_t bytes)
+struct camu_resampler *camu_ff_resampler_create(void)
{
- size_t samples = bytes / (camu_ff_resample_fmt_bytes_per_sample(fmt) * fmt->req_channel_layout.nb_channels);
- return samples / ((f64)fmt->req_sample_rate);
+ struct camu_ff_resampler *av = al_alloc_object(struct camu_ff_resampler);
+ av->resamp.init = ff_resampler_init;
+ av->resamp.convert = ff_resampler_convert;
+ av->resamp.flush = ff_resampler_flush;
+ av->resamp.get_data = ff_resampler_get_data;
+ av->resamp.free = ff_resampler_free;
+ return (struct camu_resampler *)av;
}
diff --git a/src/codec/ffmpeg/resampler.h b/src/codec/ffmpeg/resampler.h
index 4f7b07d..35e1158 100644
--- a/src/codec/ffmpeg/resampler.h
+++ b/src/codec/ffmpeg/resampler.h
@@ -1,40 +1,16 @@
#pragma once
-#include <libswresample/swresample.h>
-#include <libavutil/opt.h>
#include <al/types.h>
+#include <libswresample/swresample.h>
-struct camu_ff_resample_fmt {
- bool resampler_needed;
- enum AVSampleFormat in_format;
- enum AVSampleFormat req_format;
- s32 in_channel_count;
- s32 req_channel_count;
- AVChannelLayout in_channel_layout;
- AVChannelLayout req_channel_layout;
- s32 in_sample_rate;
- s32 req_sample_rate;
-};
+#include "../codec.h"
struct camu_ff_resampler {
+ struct camu_resampler resamp;
SwrContext *resample_context;
- struct camu_ff_resample_fmt fmt;
+ struct camu_audio_format fmt;
u8 *data[AV_NUM_DATA_POINTERS];
s32 sample_count;
};
-bool camu_ff_resampler_init(struct camu_ff_resampler *resamp, struct camu_ff_resample_fmt *fmt);
-s32 camu_ff_resampler_sample_count(struct camu_ff_resampler *resamp, s32 in_samples);
-s32 camu_ff_resampler_convert(struct camu_ff_resampler *resamp, const u8 **in_data, s32 in_samples);
-u8 **camu_ff_resampler_get_data(struct camu_ff_resampler *resamp);
-void camu_ff_resampler_close(struct camu_ff_resampler *resamp);
-
-void camu_ff_resample_fmt_copy(struct camu_ff_resample_fmt *dest, struct camu_ff_resample_fmt *src);
-size_t camu_ff_resample_fmt_bytes_per_sample(struct camu_ff_resample_fmt *fmt);
-size_t camu_ff_resample_fmt_samples_to_bytes(struct camu_ff_resample_fmt *fmt, size_t samples);
-size_t camu_ff_resample_fmt_samples_to_usec(struct camu_ff_resample_fmt *fmt, size_t samples);
-f64 camu_ff_resample_fmt_samples_to_sec(struct camu_ff_resample_fmt *fmt, size_t samples);
-size_t camu_ff_resample_fmt_usec_to_bytes(struct camu_ff_resample_fmt *fmt, size_t usec);
-size_t camu_ff_resample_fmt_sec_to_bytes(struct camu_ff_resample_fmt *fmt, f64 sec);
-size_t camu_ff_resample_fmt_bytes_to_usec(struct camu_ff_resample_fmt *fmt, size_t bytes);
-f64 camu_ff_resample_fmt_bytes_to_sec(struct camu_ff_resample_fmt *fmt, size_t bytes);
+struct camu_resampler *camu_ff_resampler_create(void);