Commit 143685a4 authored by Rick Kern's avatar Rick Kern

lavc/audiotoolboxenc: fix dropped frames on iOS

AudioConverterFillComplexBuffer() doesn't always call its callback. A frame
queue is used to prevent skipped audio samples.
Signed-off-by: 's avatarRick Kern <kernrj@gmail.com>
parent ebda8216
...@@ -22,6 +22,9 @@ ...@@ -22,6 +22,9 @@
#include <AudioToolbox/AudioToolbox.h> #include <AudioToolbox/AudioToolbox.h>
#define FF_BUFQUEUE_SIZE 256
#include "libavfilter/bufferqueue.h"
#include "config.h" #include "config.h"
#include "audio_frame_queue.h" #include "audio_frame_queue.h"
#include "avcodec.h" #include "avcodec.h"
...@@ -38,8 +41,8 @@ typedef struct ATDecodeContext { ...@@ -38,8 +41,8 @@ typedef struct ATDecodeContext {
int quality; int quality;
AudioConverterRef converter; AudioConverterRef converter;
AVFrame in_frame; struct FFBufQueue frame_queue;
AVFrame new_in_frame; struct FFBufQueue used_frame_queue;
unsigned pkt_size; unsigned pkt_size;
AudioFrameQueue afq; AudioFrameQueue afq;
...@@ -449,28 +452,30 @@ static OSStatus ffat_encode_callback(AudioConverterRef converter, UInt32 *nb_pac ...@@ -449,28 +452,30 @@ static OSStatus ffat_encode_callback(AudioConverterRef converter, UInt32 *nb_pac
{ {
AVCodecContext *avctx = inctx; AVCodecContext *avctx = inctx;
ATDecodeContext *at = avctx->priv_data; ATDecodeContext *at = avctx->priv_data;
AVFrame *frame;
if (at->eof) { if (!at->frame_queue.available) {
*nb_packets = 0; if (at->eof) {
return 0; *nb_packets = 0;
return 0;
} else {
*nb_packets = 0;
return 1;
}
} }
av_frame_unref(&at->in_frame); frame = ff_bufqueue_get(&at->frame_queue);
av_frame_move_ref(&at->in_frame, &at->new_in_frame);
if (!at->in_frame.data[0]) {
*nb_packets = 0;
return 1;
}
data->mNumberBuffers = 1; data->mNumberBuffers = 1;
data->mBuffers[0].mNumberChannels = avctx->channels; data->mBuffers[0].mNumberChannels = avctx->channels;
data->mBuffers[0].mDataByteSize = at->in_frame.nb_samples * data->mBuffers[0].mDataByteSize = frame->nb_samples *
av_get_bytes_per_sample(avctx->sample_fmt) * av_get_bytes_per_sample(avctx->sample_fmt) *
avctx->channels; avctx->channels;
data->mBuffers[0].mData = at->in_frame.data[0]; data->mBuffers[0].mData = frame->data[0];
if (*nb_packets > at->in_frame.nb_samples) if (*nb_packets > frame->nb_samples)
*nb_packets = at->in_frame.nb_samples; *nb_packets = frame->nb_samples;
ff_bufqueue_add(avctx, &at->used_frame_queue, frame);
return 0; return 0;
} }
...@@ -492,20 +497,35 @@ static int ffat_encode(AVCodecContext *avctx, AVPacket *avpkt, ...@@ -492,20 +497,35 @@ static int ffat_encode(AVCodecContext *avctx, AVPacket *avpkt,
}; };
AudioStreamPacketDescription out_pkt_desc = {0}; AudioStreamPacketDescription out_pkt_desc = {0};
if ((ret = ff_alloc_packet2(avctx, avpkt, at->pkt_size, 0)) < 0)
return ret;
av_frame_unref(&at->new_in_frame);
if (frame) { if (frame) {
AVFrame *in_frame;
if (ff_bufqueue_is_full(&at->frame_queue)) {
/*
* The frame queue is significantly larger than needed in practice,
* but no clear way to determine the minimum number of samples to
* get output from AudioConverterFillComplexBuffer().
*/
av_log(avctx, AV_LOG_ERROR, "Bug: frame queue is too small.\n");
return AVERROR_BUG;
}
if ((ret = ff_af_queue_add(&at->afq, frame)) < 0) if ((ret = ff_af_queue_add(&at->afq, frame)) < 0)
return ret; return ret;
if ((ret = av_frame_ref(&at->new_in_frame, frame)) < 0)
return ret; in_frame = av_frame_clone(frame);
if (!in_frame)
return AVERROR(ENOMEM);
ff_bufqueue_add(avctx, &at->frame_queue, in_frame);
} else { } else {
at->eof = 1; at->eof = 1;
} }
if ((ret = ff_alloc_packet2(avctx, avpkt, at->pkt_size, 0)) < 0)
return ret;
out_buffers.mBuffers[0].mData = avpkt->data; out_buffers.mBuffers[0].mData = avpkt->data;
*got_packet_ptr = avctx->frame_size / at->frame_size; *got_packet_ptr = avctx->frame_size / at->frame_size;
...@@ -513,6 +533,9 @@ static int ffat_encode(AVCodecContext *avctx, AVPacket *avpkt, ...@@ -513,6 +533,9 @@ static int ffat_encode(AVCodecContext *avctx, AVPacket *avpkt,
ret = AudioConverterFillComplexBuffer(at->converter, ffat_encode_callback, avctx, ret = AudioConverterFillComplexBuffer(at->converter, ffat_encode_callback, avctx,
got_packet_ptr, &out_buffers, got_packet_ptr, &out_buffers,
(avctx->frame_size > at->frame_size) ? NULL : &out_pkt_desc); (avctx->frame_size > at->frame_size) ? NULL : &out_pkt_desc);
ff_bufqueue_discard_all(&at->used_frame_queue);
if ((!ret || ret == 1) && *got_packet_ptr) { if ((!ret || ret == 1) && *got_packet_ptr) {
avpkt->size = out_buffers.mBuffers[0].mDataByteSize; avpkt->size = out_buffers.mBuffers[0].mDataByteSize;
ff_af_queue_remove(&at->afq, out_pkt_desc.mVariableFramesInPacket ? ff_af_queue_remove(&at->afq, out_pkt_desc.mVariableFramesInPacket ?
...@@ -531,16 +554,16 @@ static av_cold void ffat_encode_flush(AVCodecContext *avctx) ...@@ -531,16 +554,16 @@ static av_cold void ffat_encode_flush(AVCodecContext *avctx)
{ {
ATDecodeContext *at = avctx->priv_data; ATDecodeContext *at = avctx->priv_data;
AudioConverterReset(at->converter); AudioConverterReset(at->converter);
av_frame_unref(&at->new_in_frame); ff_bufqueue_discard_all(&at->frame_queue);
av_frame_unref(&at->in_frame); ff_bufqueue_discard_all(&at->used_frame_queue);
} }
static av_cold int ffat_close_encoder(AVCodecContext *avctx) static av_cold int ffat_close_encoder(AVCodecContext *avctx)
{ {
ATDecodeContext *at = avctx->priv_data; ATDecodeContext *at = avctx->priv_data;
AudioConverterDispose(at->converter); AudioConverterDispose(at->converter);
av_frame_unref(&at->new_in_frame); ff_bufqueue_discard_all(&at->frame_queue);
av_frame_unref(&at->in_frame); ff_bufqueue_discard_all(&at->used_frame_queue);
ff_af_queue_close(&at->afq); ff_af_queue_close(&at->afq);
return 0; return 0;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment