[FFmpeg-cvslog] avcodec/wmaprodec: support multichannel XMA stream configurations
bnnm
git at videolan.org
Mon Oct 9 18:06:39 EEST 2017
ffmpeg | branch: master | bnnm <bananaman255 at gmail.com> | Tue Oct 3 23:49:00 2017 +0200| [1954e625b1d6bcb8de0fdc2c89f0a62f2fea7328] | committer: Paul B Mahol
avcodec/wmaprodec: support multichannel XMA stream configurations
Signed-off-by: bnnm <bananaman255 at gmail.com>
Now accepts any combination of 1/2ch streams, described in the RIFF chunks/extradata
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=1954e625b1d6bcb8de0fdc2c89f0a62f2fea7328
---
libavcodec/wmaprodec.c | 151 ++++++++++++++++++++++++++++++++-----------------
libavformat/wavdec.c | 20 ++++---
2 files changed, 112 insertions(+), 59 deletions(-)
diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c
index 5c99628c52..77a49c9db8 100644
--- a/libavcodec/wmaprodec.c
+++ b/libavcodec/wmaprodec.c
@@ -106,6 +106,9 @@
#define MAX_SUBFRAMES 32 ///< max number of subframes per channel
#define MAX_BANDS 29 ///< max number of scale factor bands
#define MAX_FRAMESIZE 32768 ///< maximum compressed frame size
+#define XMA_MAX_STREAMS 8
+#define XMA_MAX_CHANNELS 8
+#define XMA_MAX_CHANNELS_STREAM 2
#define WMAPRO_BLOCK_MIN_BITS 6 ///< log2 of min block size
#define WMAPRO_BLOCK_MAX_BITS 13 ///< log2 of max block size
@@ -215,7 +218,7 @@ typedef struct WMAProDecodeCtx {
uint8_t drc_gain; ///< gain for the DRC tool
int8_t skip_frame; ///< skip output step
int8_t parsed_all_subframes; ///< all subframes decoded?
- uint8_t skip_packets;
+ uint8_t skip_packets; ///< packets to skip to find next packet in a stream (XMA1/2)
/* subframe/block decode state */
int16_t subframe_len; ///< current subframe length
@@ -235,11 +238,13 @@ typedef struct WMAProDecodeCtx {
} WMAProDecodeCtx;
typedef struct XMADecodeCtx {
- WMAProDecodeCtx xma[4];
- AVFrame *frames[4];
+ WMAProDecodeCtx xma[XMA_MAX_STREAMS];
+ AVFrame *frames[XMA_MAX_STREAMS];
int current_stream;
- float samples[8][512 * 64];
- int offset[4];
+ int num_streams;
+ float samples[XMA_MAX_CHANNELS][512 * 64];
+ int offset[XMA_MAX_STREAMS];
+ int start_channel[XMA_MAX_STREAMS];
} XMADecodeCtx;
/**
@@ -306,7 +311,7 @@ static av_cold int get_rate(AVCodecContext *avctx)
*@param avctx codec context
*@return 0 on success, -1 otherwise
*/
-static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx)
+static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx, int num_stream)
{
uint8_t *edata_ptr = avctx->extradata;
unsigned int channel_mask;
@@ -333,18 +338,30 @@ static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx)
for (i = 0; i < avctx->extradata_size; i++)
av_log(avctx, AV_LOG_DEBUG, "[%x] ", avctx->extradata[i]);
av_log(avctx, AV_LOG_DEBUG, "\n");
- if (avctx->codec_id == AV_CODEC_ID_XMA2 && (!avctx->extradata || avctx->extradata_size >= 6)) {
+
+ if (avctx->codec_id == AV_CODEC_ID_XMA2 && avctx->extradata_size == 34) { /* XMA2WAVEFORMATEX */
+ s->decode_flags = 0x10d6;
+ s->bits_per_sample = 16;
+ channel_mask = 0; //AV_RL32(edata_ptr+2); /* not always in expected order */
+ if ((num_stream+1) * XMA_MAX_CHANNELS_STREAM > avctx->channels) /* stream config is 2ch + 2ch + ... + 1/2ch */
+ s->nb_channels = 1;
+ else
+ s->nb_channels = 2;
+ } else if (avctx->codec_id == AV_CODEC_ID_XMA2) { /* XMA2WAVEFORMAT */
s->decode_flags = 0x10d6;
- channel_mask = avctx->extradata ? AV_RL32(edata_ptr+2) : 0;
s->bits_per_sample = 16;
- } else if (avctx->codec_id == AV_CODEC_ID_XMA1) {
+ channel_mask = 0; /* would need to aggregate from all streams */
+ s->nb_channels = edata_ptr[32 + ((edata_ptr[0]==3)?0:8) + 4*num_stream + 0]; /* nth stream config */
+ } else if (avctx->codec_id == AV_CODEC_ID_XMA1) { /* XMAWAVEFORMAT */
s->decode_flags = 0x10d6;
s->bits_per_sample = 16;
- channel_mask = 0;
- } else if (avctx->codec_id == AV_CODEC_ID_WMAPRO && avctx->extradata_size >= 18) {
+ channel_mask = 0; /* would need to aggregate from all streams */
+ s->nb_channels = edata_ptr[8 + 20*num_stream + 17]; /* nth stream config */
+ } else if (avctx->codec_id == AV_CODEC_ID_WMAPRO && avctx->extradata_size >= 18) {
s->decode_flags = AV_RL16(edata_ptr+14);
channel_mask = AV_RL32(edata_ptr+2);
s->bits_per_sample = AV_RL16(edata_ptr);
+ s->nb_channels = avctx->channels;
if (s->bits_per_sample > 32 || s->bits_per_sample < 1) {
avpriv_request_sample(avctx, "bits per sample is %d", s->bits_per_sample);
@@ -355,12 +372,6 @@ static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx)
return AVERROR_PATCHWELCOME;
}
- if (avctx->codec_id != AV_CODEC_ID_WMAPRO && avctx->channels > 2) {
- s->nb_channels = 2;
- } else {
- s->nb_channels = avctx->channels;
- }
-
/** generic init */
s->log2_frame_size = av_log2(avctx->block_align) + 4;
if (s->log2_frame_size > 25) {
@@ -421,6 +432,10 @@ static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx)
av_log(avctx, AV_LOG_ERROR, "invalid number of channels %d\n",
s->nb_channels);
return AVERROR_INVALIDDATA;
+ } else if (avctx->codec_id != AV_CODEC_ID_WMAPRO && s->nb_channels > XMA_MAX_CHANNELS_STREAM) {
+ av_log(avctx, AV_LOG_ERROR, "invalid number of channels per XMA stream %d\n",
+ s->nb_channels);
+ return AVERROR_INVALIDDATA;
} else if (s->nb_channels > WMAPRO_MAX_CHANNELS) {
avpriv_request_sample(avctx,
"More than %d channels", WMAPRO_MAX_CHANNELS);
@@ -566,7 +581,7 @@ static av_cold int wmapro_decode_init(AVCodecContext *avctx)
{
WMAProDecodeCtx *s = avctx->priv_data;
- return decode_init(s, avctx);
+ return decode_init(s, avctx, 0);
}
/**
@@ -1750,14 +1765,17 @@ static int xma_decode_packet(AVCodecContext *avctx, void *data,
AVFrame *frame = data;
int i, ret, offset = INT_MAX;
+ /* decode current stream packet */
ret = decode_packet(avctx, &s->xma[s->current_stream], s->frames[s->current_stream],
&got_stream_frame_ptr, avpkt);
+ /* copy stream samples (1/2ch) to sample buffer (Nch) */
if (got_stream_frame_ptr) {
- memcpy(&s->samples[s->current_stream * 2 + 0][s->offset[s->current_stream] * 512],
+ 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);
- if (avctx->channels > 1)
- memcpy(&s->samples[s->current_stream * 2 + 1][s->offset[s->current_stream] * 512],
+ 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]++;
} else if (ret < 0) {
@@ -1766,58 +1784,57 @@ static int xma_decode_packet(AVCodecContext *avctx, void *data,
return ret;
}
+ /* find next XMA packet's owner stream, and update.
+ * XMA streams find their packets following packet_skips
+ * (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 bret;
-
- if (s->xma[s->current_stream].skip_packets == 0) {
- ;
- } else if (s->xma[0].skip_packets == 0 && avctx->channels >= 2) {
- s->current_stream = 0;
- } else if (s->xma[1].skip_packets == 0 && avctx->channels >= 4) {
- s->current_stream = 1;
- } else if (s->xma[2].skip_packets == 0 && avctx->channels >= 6) {
- s->current_stream = 2;
- } else if (s->xma[3].skip_packets == 0 && avctx->channels == 8) {
- s->current_stream = 3;
- } else {
+
+ /* select stream with 0 skip_packets (= uses next packet) */
+ if (s->xma[s->current_stream].skip_packets != 0) {
int min[2];
min[0] = s->xma[0].skip_packets;
min[1] = i = 0;
- for (i = 1; i < avctx->channels / 2; i++) {
+ for (i = 1; i < s->num_streams; i++) {
if (s->xma[i].skip_packets < min[0]) {
- min[1] = i;
min[0] = s->xma[i].skip_packets;
+ min[1] = i;
}
}
s->current_stream = min[1];
}
- for (i = 0; i < avctx->channels / 2; i++) {
+ /* all other streams skip next packet */
+ for (i = 0; i < s->num_streams; i++) {
s->xma[i].skip_packets = FFMAX(0, s->xma[i].skip_packets - 1);
}
- for (i = 0; i < (avctx->channels + 1) / 2; i++) {
+ /* copy samples from buffer to output if possible */
+ for (i = 0; i < s->num_streams; i++) {
offset = FFMIN(offset, s->offset[i]);
}
-
if (offset > 0) {
+ int bret;
+
frame->nb_samples = 512 * offset;
if ((bret = ff_get_buffer(avctx, frame, 0)) < 0)
return bret;
- for (i = 0; i < (avctx->channels + 1) / 2; i++) {
- memcpy(frame->extended_data[i * 2 + 0], s->samples[i * 2 + 0], frame->nb_samples * 4);
- if (avctx->channels > 1)
- memcpy(frame->extended_data[i * 2 + 1], s->samples[i * 2 + 1], frame->nb_samples * 4);
+ /* 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[i * 2 + 0], s->samples[i * 2 + 0] + frame->nb_samples, s->offset[i] * 4 * 512);
- if (avctx->channels > 1)
- memmove(s->samples[i * 2 + 1], s->samples[i * 2 + 1] + frame->nb_samples, s->offset[i] * 4 * 512);
+ 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);
}
}
@@ -1831,13 +1848,40 @@ static int xma_decode_packet(AVCodecContext *avctx, void *data,
static av_cold int xma_decode_init(AVCodecContext *avctx)
{
XMADecodeCtx *s = avctx->priv_data;
- int i, ret;
+ int i, ret, start_channels = 0;
- if (avctx->channels <= 0 || avctx->channels > 8)
+ if (avctx->channels <= 0 || avctx->extradata_size == 0)
return AVERROR_INVALIDDATA;
- for (i = 0; i < (avctx->channels + 1) / 2; i++) {
- ret = decode_init(&s->xma[i], avctx);
+ /* get stream config */
+ if (avctx->codec_id == AV_CODEC_ID_XMA2 && avctx->extradata_size == 34) { /* XMA2WAVEFORMATEX */
+ s->num_streams = (avctx->channels + 1) / 2;
+ } else if (avctx->codec_id == AV_CODEC_ID_XMA2 && avctx->extradata_size >= 2) { /* XMA2WAVEFORMAT */
+ s->num_streams = avctx->extradata[1];
+ if (avctx->extradata_size != (32 + ((avctx->extradata[0]==3)?0:8) + 4*s->num_streams)) {
+ av_log(avctx, AV_LOG_ERROR, "Incorrect XMA2 extradata size\n");
+ return AVERROR(EINVAL);
+ }
+ } else if (avctx->codec_id == AV_CODEC_ID_XMA1 && avctx->extradata_size >= 4) { /* XMAWAVEFORMAT */
+ s->num_streams = avctx->extradata[4];
+ if (avctx->extradata_size != (8 + 20*s->num_streams)) {
+ av_log(avctx, AV_LOG_ERROR, "Incorrect XMA1 extradata size\n");
+ return AVERROR(EINVAL);
+ }
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Incorrect XMA config\n");
+ return AVERROR(EINVAL);
+ }
+
+ /* encoder supports up to 64 streams / 64*2 channels (would have to alloc arrays) */
+ if (avctx->channels > XMA_MAX_CHANNELS || s->num_streams > XMA_MAX_STREAMS) {
+ avpriv_request_sample(avctx, "More than %d channels in %d streams", XMA_MAX_CHANNELS, s->num_streams);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ /* init all streams (several streams of 1/2ch make Nch files) */
+ for (i = 0; i < s->num_streams; i++) {
+ ret = decode_init(&s->xma[i], avctx, i);
if (ret < 0)
return ret;
s->frames[i] = av_frame_alloc();
@@ -1848,6 +1892,8 @@ static av_cold int xma_decode_init(AVCodecContext *avctx)
return AVERROR(ENOMEM);
}
+ s->start_channel[i] = start_channels;
+ start_channels += s->xma[i].nb_channels;
}
return ret;
@@ -1858,7 +1904,7 @@ static av_cold int xma_decode_end(AVCodecContext *avctx)
XMADecodeCtx *s = avctx->priv_data;
int i;
- for (i = 0; i < avctx->channels / 2; i++) {
+ for (i = 0; i < s->num_streams; i++) {
decode_end(&s->xma[i]);
av_frame_free(&s->frames[i]);
}
@@ -1894,7 +1940,8 @@ static void xma_flush(AVCodecContext *avctx)
{
XMADecodeCtx *s = avctx->priv_data;
int i;
- for (i = 0; i < (avctx->channels + 1) / 2; i++)
+
+ for (i = 0; i < s->num_streams; i++)
flush(&s->xma[i]);
memset(s->offset, 0, sizeof(s->offset));
diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c
index 81dbc9f16e..b016185a1b 100644
--- a/libavformat/wavdec.c
+++ b/libavformat/wavdec.c
@@ -180,9 +180,9 @@ static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st)
static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st)
{
AVIOContext *pb = s->pb;
- int num_streams, i, channels = 0;
+ int version, num_streams, i, channels = 0;
- if (size < 44)
+ if (size < 36)
return AVERROR_INVALIDDATA;
*st = avformat_new_stream(s, NULL);
@@ -193,13 +193,17 @@ static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st)
(*st)->codecpar->codec_id = AV_CODEC_ID_XMA2;
(*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW;
- avio_skip(pb, 1);
+ version = avio_r8(pb);
+ if (version != 3 && version != 4)
+ return AVERROR_INVALIDDATA;
num_streams = avio_r8(pb);
- if (size < 40 + num_streams * 4)
+ if (size != (32 + ((version==3)?0:8) + 4*num_streams))
return AVERROR_INVALIDDATA;
avio_skip(pb, 10);
(*st)->codecpar->sample_rate = avio_rb32(pb);
- avio_skip(pb, 12);
+ if (version == 4)
+ avio_skip(pb, 8);
+ avio_skip(pb, 4);
(*st)->duration = avio_rb32(pb);
avio_skip(pb, 8);
@@ -213,9 +217,11 @@ static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st)
return AVERROR_INVALIDDATA;
avpriv_set_pts_info(*st, 64, 1, (*st)->codecpar->sample_rate);
- if (ff_alloc_extradata((*st)->codecpar, 34))
+
+ avio_seek(pb, -size, SEEK_CUR);
+ av_freep(&(*st)->codecpar->extradata);
+ if (ff_get_extradata(s, (*st)->codecpar, pb, size) < 0)
return AVERROR(ENOMEM);
- memset((*st)->codecpar->extradata, 0, 34);
return 0;
}
More information about the ffmpeg-cvslog
mailing list