[FFmpeg-cvslog] avcodec/wmaprodec: add gapless support
Paul B Mahol
git at videolan.org
Thu Sep 2 19:01:16 EEST 2021
ffmpeg | branch: master | Paul B Mahol <onemda at gmail.com> | Thu Sep 2 01:09:36 2021 +0200| [61c2c9ef8e66920c8ba308e8fa9f36ae602f8245] | committer: Paul B Mahol
avcodec/wmaprodec: add gapless support
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=61c2c9ef8e66920c8ba308e8fa9f36ae602f8245
---
libavcodec/wmaprodec.c | 158 +++++++++++++++++++++++++++++++------------------
1 file changed, 99 insertions(+), 59 deletions(-)
diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c
index 66271c4037..14a48db45a 100644
--- a/libavcodec/wmaprodec.c
+++ b/libavcodec/wmaprodec.c
@@ -88,6 +88,7 @@
#include <inttypes.h>
+#include "libavutil/audio_fifo.h"
#include "libavutil/ffmath.h"
#include "libavutil/float_dsp.h"
#include "libavutil/intfloat.h"
@@ -192,6 +193,8 @@ typedef struct WMAProDecodeCtx {
uint8_t dynamic_range_compression; ///< frame contains DRC data
uint8_t bits_per_sample; ///< integer audio sample size for the unscaled IMDCT output (used to scale to [-1.0, 1.0])
uint16_t samples_per_frame; ///< number of samples to output
+ uint16_t trim_start; ///< number of samples to skip at start
+ uint16_t trim_end; ///< number of samples to skip at end
uint16_t log2_frame_size;
int8_t lfe_channel; ///< lfe channel index
uint8_t max_num_subframes;
@@ -246,9 +249,9 @@ typedef struct XMADecodeCtx {
AVFrame *frames[XMA_MAX_STREAMS];
int current_stream;
int num_streams;
- float samples[XMA_MAX_CHANNELS][512 * 64];
- int offset[XMA_MAX_STREAMS];
+ AVAudioFifo *samples[2][XMA_MAX_CHANNELS];
int start_channel[XMA_MAX_STREAMS];
+ int trim_start, trim_end;
} XMADecodeCtx;
/**
@@ -417,10 +420,7 @@ static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx, int nu
}
/** frame info */
- if (avctx->codec_id != AV_CODEC_ID_WMAPRO)
- s->skip_frame = 0;
- else
- s->skip_frame = 1; /* skip first frame */
+ s->skip_frame = 1; /* skip first frame */
s->packet_loss = 1;
s->len_prefix = (s->decode_flags & 0x40);
@@ -1462,23 +1462,14 @@ static int decode_frame(WMAProDecodeCtx *s, AVFrame *frame, int *got_frame_ptr)
ff_dlog(s->avctx, "drc_gain %i\n", s->drc_gain);
}
- /** no idea what these are for, might be the number of samples
- that need to be skipped at the beginning or end of a stream */
if (get_bits1(gb)) {
- int av_unused skip;
-
- /** usually true for the first frame */
- if (get_bits1(gb)) {
- skip = get_bits(gb, av_log2(s->samples_per_frame * 2));
- ff_dlog(s->avctx, "start skip: %i\n", skip);
- }
-
- /** sometimes true for the last frame */
- if (get_bits1(gb)) {
- skip = get_bits(gb, av_log2(s->samples_per_frame * 2));
- ff_dlog(s->avctx, "end skip: %i\n", skip);
- }
+ if (get_bits1(gb))
+ s->trim_start = get_bits(gb, av_log2(s->samples_per_frame * 2));
+ if (get_bits1(gb))
+ s->trim_end = get_bits(gb, av_log2(s->samples_per_frame * 2));
+ } else {
+ s->trim_start = s->trim_end = 0;
}
ff_dlog(s->avctx, "BITSTREAM: frame header length was %i\n",
@@ -1641,9 +1632,6 @@ static int decode_packet(AVCodecContext *avctx, WMAProDecodeCtx *s,
s->samples_per_frame * sizeof(*s->channel[i].out) >> 1);
}
- /* TODO: XMA should output 128 samples only (instead of 512) and WMAPRO
- * maybe 768 (with 2048), XMA needs changes in multi-stream handling though. */
-
s->eof_done = 1;
s->packet_done = 1;
*got_frame_ptr = 1;
@@ -1781,6 +1769,33 @@ static int decode_packet(AVCodecContext *avctx, WMAProDecodeCtx *s,
if (s->packet_loss)
return AVERROR_INVALIDDATA;
+ if (s->trim_start && avctx->codec_id == AV_CODEC_ID_WMAPRO) {
+ AVFrame *frame = data;
+
+ if (s->trim_start < frame->nb_samples) {
+ for (int ch = 0; ch < frame->channels; ch++)
+ frame->extended_data[ch] += s->trim_start * 4;
+
+ frame->nb_samples -= s->trim_start;
+ } else {
+ *got_frame_ptr = 0;
+ }
+
+ s->trim_start = 0;
+ }
+
+ if (s->trim_end && avctx->codec_id == AV_CODEC_ID_WMAPRO) {
+ AVFrame *frame = data;
+
+ if (s->trim_end < frame->nb_samples) {
+ frame->nb_samples -= s->trim_end;
+ } else {
+ *got_frame_ptr = 0;
+ }
+
+ s->trim_end = 0;
+ }
+
return get_bits_count(gb) >> 3;
}
@@ -1814,34 +1829,40 @@ static int xma_decode_packet(AVCodecContext *avctx, void *data,
XMADecodeCtx *s = avctx->priv_data;
int got_stream_frame_ptr = 0;
AVFrame *frame = data;
- int i, ret, offset = INT_MAX;
+ int i, ret, eof;
if (!s->frames[s->current_stream]->data[0]) {
+ avctx->internal->skip_samples = 64;
s->frames[s->current_stream]->nb_samples = 512;
- if ((ret = ff_get_buffer(avctx, s->frames[s->current_stream], 0)) < 0) {
+ if ((ret = ff_get_buffer(avctx, s->frames[s->current_stream], 0)) < 0)
+ return ret;
+ } else if (s->frames[s->current_stream]->nb_samples != 512) {
+ avctx->internal->skip_samples = 64;
+ av_frame_unref(s->frames[s->current_stream]);
+ s->frames[s->current_stream]->nb_samples = 512;
+ if ((ret = ff_get_buffer(avctx, s->frames[s->current_stream], 0)) < 0)
return ret;
- }
}
/* decode current stream packet */
ret = decode_packet(avctx, &s->xma[s->current_stream], s->frames[s->current_stream],
&got_stream_frame_ptr, avpkt);
-
- if (got_stream_frame_ptr && s->offset[s->current_stream] >= 64) {
- got_stream_frame_ptr = 0;
- ret = AVERROR_INVALIDDATA;
- }
+ eof = s->xma[s->current_stream].eof_done;
+ if (s->xma[s->current_stream].trim_start)
+ s->trim_start = s->xma[s->current_stream].trim_start;
+ if (s->xma[s->current_stream].trim_end)
+ s->trim_end = s->xma[s->current_stream].trim_end;
/* copy stream samples (1/2ch) to sample buffer (Nch) */
if (got_stream_frame_ptr) {
int start_ch = s->start_channel[s->current_stream];
- memcpy(&s->samples[start_ch + 0][s->offset[s->current_stream] * 512],
- s->frames[s->current_stream]->extended_data[0], 512 * 4);
+ const int nb_samples = s->frames[s->current_stream]->nb_samples;
+ void *left[1] = { s->frames[s->current_stream]->extended_data[0] };
+ void *right[1] = { s->frames[s->current_stream]->extended_data[1] };
+
+ av_audio_fifo_write(s->samples[0][start_ch + 0], left, nb_samples);
if (s->xma[s->current_stream].nb_channels > 1)
- memcpy(&s->samples[start_ch + 1][s->offset[s->current_stream] * 512],
- s->frames[s->current_stream]->extended_data[1], 512 * 4);
- s->offset[s->current_stream]++;
+ av_audio_fifo_write(s->samples[1][start_ch + 1], right, nb_samples);
} else if (ret < 0) {
- memset(s->offset, 0, sizeof(s->offset));
s->current_stream = 0;
return ret;
}
@@ -1851,6 +1872,7 @@ static int xma_decode_packet(AVCodecContext *avctx, void *data,
* (at start there is one packet per stream, then interleave non-linearly). */
if (s->xma[s->current_stream].packet_done ||
s->xma[s->current_stream].packet_loss) {
+ int nb_samples = INT_MAX;
/* select stream with 0 skip_packets (= uses next packet) */
if (s->xma[s->current_stream].skip_packets != 0) {
@@ -1870,34 +1892,37 @@ static int xma_decode_packet(AVCodecContext *avctx, void *data,
}
/* all other streams skip next packet */
- for (i = 0; i < s->num_streams; i++) {
+ for (i = 0; i < s->num_streams; i++)
s->xma[i].skip_packets = FFMAX(0, s->xma[i].skip_packets - 1);
- }
- /* copy samples from buffer to output if possible */
for (i = 0; i < s->num_streams; i++) {
- offset = FFMIN(offset, s->offset[i]);
+ int start_ch = s->start_channel[s->current_stream];
+
+ nb_samples = FFMIN(nb_samples, av_audio_fifo_size(s->samples[0][start_ch]));
}
- if (offset > 0) {
+
+ /* copy samples from buffer to output if possible */
+ if (nb_samples > 4096 || eof) {
int bret;
- frame->nb_samples = 512 * offset;
+ if (eof) {
+ nb_samples -= FFMIN(nb_samples, s->trim_end + s->trim_start - 128 - 64);
+ s->trim_end = s->trim_start = 0;
+ }
+ frame->nb_samples = nb_samples;
+ if (frame->nb_samples <= 0)
+ return ret;
if ((bret = ff_get_buffer(avctx, frame, 0)) < 0)
return bret;
- /* copy samples buffer (Nch) to frame samples (Nch), move unconsumed samples */
for (i = 0; i < s->num_streams; i++) {
- int start_ch = s->start_channel[i];
- memcpy(frame->extended_data[start_ch + 0], s->samples[start_ch + 0], frame->nb_samples * 4);
- if (s->xma[i].nb_channels > 1)
- memcpy(frame->extended_data[start_ch + 1], s->samples[start_ch + 1], frame->nb_samples * 4);
-
- s->offset[i] -= offset;
- if (s->offset[i]) {
- memmove(s->samples[start_ch + 0], s->samples[start_ch + 0] + frame->nb_samples, s->offset[i] * 4 * 512);
- if (s->xma[i].nb_channels > 1)
- memmove(s->samples[start_ch + 1], s->samples[start_ch + 1] + frame->nb_samples, s->offset[i] * 4 * 512);
- }
+ const int start_ch = s->start_channel[s->current_stream];
+ void *left[1] = { frame->extended_data[0] };
+ void *right[1] = { frame->extended_data[1] };
+
+ av_audio_fifo_read(s->samples[0][start_ch + 0], left, nb_samples);
+ if (s->xma[s->current_stream].nb_channels > 1)
+ av_audio_fifo_read(s->samples[1][start_ch + 1], right, nb_samples);
}
*got_frame_ptr = 1;
@@ -1961,6 +1986,13 @@ static av_cold int xma_decode_init(AVCodecContext *avctx)
if (start_channels != avctx->channels)
return AVERROR_INVALIDDATA;
+ for (int i = 0; i < XMA_MAX_CHANNELS; i++) {
+ s->samples[0][i] = av_audio_fifo_alloc(avctx->sample_fmt, 1, 64 * 512);
+ s->samples[1][i] = av_audio_fifo_alloc(avctx->sample_fmt, 1, 64 * 512);
+ if (!s->samples[0][i] || !s->samples[1][i])
+ return AVERROR(ENOMEM);
+ }
+
return ret;
}
@@ -1975,6 +2007,11 @@ static av_cold int xma_decode_end(AVCodecContext *avctx)
}
s->num_streams = 0;
+ for (i = 0; i < XMA_MAX_CHANNELS; i++) {
+ av_audio_fifo_free(s->samples[0][i]);
+ av_audio_fifo_free(s->samples[1][i]);
+ }
+
return 0;
}
@@ -1989,9 +2026,9 @@ static void flush(WMAProDecodeCtx *s)
s->packet_loss = 1;
s->skip_packets = 0;
s->eof_done = 0;
+ s->skip_frame = 1;
}
-
/**
*@brief Clear decoder buffers (for seeking).
*@param avctx codec context
@@ -2008,14 +2045,17 @@ static void xma_flush(AVCodecContext *avctx)
XMADecodeCtx *s = avctx->priv_data;
int i;
+ for (i = 0; i < XMA_MAX_CHANNELS; i++) {
+ av_audio_fifo_reset(s->samples[0][i]);
+ av_audio_fifo_reset(s->samples[1][i]);
+ }
+
for (i = 0; i < s->num_streams; i++)
flush(&s->xma[i]);
- memset(s->offset, 0, sizeof(s->offset));
s->current_stream = 0;
}
-
/**
*@brief wmapro decoder
*/
More information about the ffmpeg-cvslog
mailing list