+static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf,
+ int *buf_size, int type,
+ uint32_t **lace_buf, int *laces)
+{
+ int res = 0, n, size = *buf_size;
+ uint8_t *data = *buf;
+ uint32_t *lace_size;
+
+ if (!type) {
+ *laces = 1;
+ *lace_buf = av_mallocz(sizeof(int));
+ if (!*lace_buf)
+ return AVERROR(ENOMEM);
+
+ *lace_buf[0] = size;
+ return 0;
+ }
+
+ assert(size > 0);
+ *laces = *data + 1;
+ data += 1;
+ size -= 1;
+ lace_size = av_mallocz(*laces * sizeof(int));
+ if (!lace_size)
+ return AVERROR(ENOMEM);
+
+ switch (type) {
+ case 0x1: /* Xiph lacing */
+ {
+ uint8_t temp;
+ uint32_t total = 0;
+ for (n = 0; res == 0 && n < *laces - 1; n++) {
+ while (1) {
+ if (size == 0) {
+ res = AVERROR_EOF;
+ break;
+ }
+ temp = *data;
+ lace_size[n] += temp;
+ data += 1;
+ size -= 1;
+ if (temp != 0xff)
+ break;
+ }
+ total += lace_size[n];
+ }
+ if (size <= total) {
+ res = AVERROR_INVALIDDATA;
+ break;
+ }
+
+ lace_size[n] = size - total;
+ break;
+ }
+
+ case 0x2: /* fixed-size lacing */
+ if (size % (*laces)) {
+ res = AVERROR_INVALIDDATA;
+ break;
+ }
+ for (n = 0; n < *laces; n++)
+ lace_size[n] = size / *laces;
+ break;
+
+ case 0x3: /* EBML lacing */
+ {
+ uint64_t num;
+ uint64_t total;
+ n = matroska_ebmlnum_uint(matroska, data, size, &num);
+ if (n < 0) {
+ av_log(matroska->ctx, AV_LOG_INFO,
+ "EBML block data error\n");
+ res = n;
+ break;
+ }
+ data += n;
+ size -= n;
+ total = lace_size[0] = num;
+ for (n = 1; res == 0 && n < *laces - 1; n++) {
+ int64_t snum;
+ int r;
+ r = matroska_ebmlnum_sint(matroska, data, size, &snum);
+ if (r < 0) {
+ av_log(matroska->ctx, AV_LOG_INFO,
+ "EBML block data error\n");
+ res = r;
+ break;
+ }
+ data += r;
+ size -= r;
+ lace_size[n] = lace_size[n - 1] + snum;
+ total += lace_size[n];
+ }
+ if (size <= total) {
+ res = AVERROR_INVALIDDATA;
+ break;
+ }
+ lace_size[*laces - 1] = size - total;
+ break;
+ }
+ }
+
+ *buf = data;
+ *lace_buf = lace_size;
+ *buf_size = size;
+
+ return res;
+}
+
+static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska,
+ MatroskaTrack *track, AVStream *st,
+ uint8_t *data, int size, uint64_t timecode,
+ uint64_t duration, int64_t pos)
+{
+ int a = st->codec->block_align;
+ int sps = track->audio.sub_packet_size;
+ int cfs = track->audio.coded_framesize;
+ int h = track->audio.sub_packet_h;
+ int y = track->audio.sub_packet_cnt;
+ int w = track->audio.frame_size;
+ int x;
+
+ if (!track->audio.pkt_cnt) {
+ if (track->audio.sub_packet_cnt == 0)
+ track->audio.buf_timecode = timecode;
+ if (st->codec->codec_id == AV_CODEC_ID_RA_288) {
+ if (size < cfs * h / 2) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Corrupt int4 RM-style audio packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
+ for (x = 0; x < h / 2; x++)
+ memcpy(track->audio.buf + x * 2 * w + y * cfs,
+ data + x * cfs, cfs);
+ } else if (st->codec->codec_id == AV_CODEC_ID_SIPR) {
+ if (size < w) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Corrupt sipr RM-style audio packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
+ memcpy(track->audio.buf + y * w, data, w);
+ } else {
+ if (size < sps * w / sps) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Corrupt generic RM-style audio packet size\n");
+ return AVERROR_INVALIDDATA;
+ }
+ for (x = 0; x < w / sps; x++)
+ memcpy(track->audio.buf +
+ sps * (h * x + ((h + 1) / 2) * (y & 1) + (y >> 1)),
+ data + x * sps, sps);
+ }
+
+ if (++track->audio.sub_packet_cnt >= h) {
+ if (st->codec->codec_id == AV_CODEC_ID_SIPR)
+ ff_rm_reorder_sipr_data(track->audio.buf, h, w);
+ track->audio.sub_packet_cnt = 0;
+ track->audio.pkt_cnt = h * w / a;
+ }
+ }
+
+ while (track->audio.pkt_cnt) {
+ int ret;
+ AVPacket *pkt = av_mallocz(sizeof(AVPacket));
+ if (!pkt)
+ return AVERROR(ENOMEM);
+
+ ret = av_new_packet(pkt, a);
+ if (ret < 0) {
+ av_free(pkt);
+ return ret;
+ }
+ memcpy(pkt->data,
+ track->audio.buf + a * (h * w / a - track->audio.pkt_cnt--),
+ a);
+ pkt->pts = track->audio.buf_timecode;
+ track->audio.buf_timecode = AV_NOPTS_VALUE;
+ pkt->pos = pos;
+ pkt->stream_index = st->index;
+ dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
+ }
+
+ return 0;
+}
+
+/* reconstruct full wavpack blocks from mangled matroska ones */
+static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src,
+ uint8_t **pdst, int *size)
+{
+ uint8_t *dst = NULL;
+ int dstlen = 0;
+ int srclen = *size;
+ uint32_t samples;
+ uint16_t ver;
+ int ret, offset = 0;
+
+ if (srclen < 12 || track->stream->codec->extradata_size < 2)
+ return AVERROR_INVALIDDATA;
+
+ ver = AV_RL16(track->stream->codec->extradata);
+
+ samples = AV_RL32(src);
+ src += 4;
+ srclen -= 4;
+
+ while (srclen >= 8) {
+ int multiblock;
+ uint32_t blocksize;
+ uint8_t *tmp;
+
+ uint32_t flags = AV_RL32(src);
+ uint32_t crc = AV_RL32(src + 4);
+ src += 8;
+ srclen -= 8;
+
+ multiblock = (flags & 0x1800) != 0x1800;
+ if (multiblock) {
+ if (srclen < 4) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ blocksize = AV_RL32(src);
+ src += 4;
+ srclen -= 4;
+ } else
+ blocksize = srclen;
+
+ if (blocksize > srclen) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ tmp = av_realloc(dst, dstlen + blocksize + 32);
+ if (!tmp) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ dst = tmp;
+ dstlen += blocksize + 32;
+
+ AV_WL32(dst + offset, MKTAG('w', 'v', 'p', 'k')); // tag
+ AV_WL32(dst + offset + 4, blocksize + 24); // blocksize - 8
+ AV_WL16(dst + offset + 8, ver); // version
+ AV_WL16(dst + offset + 10, 0); // track/index_no
+ AV_WL32(dst + offset + 12, 0); // total samples
+ AV_WL32(dst + offset + 16, 0); // block index
+ AV_WL32(dst + offset + 20, samples); // number of samples
+ AV_WL32(dst + offset + 24, flags); // flags
+ AV_WL32(dst + offset + 28, crc); // crc
+ memcpy(dst + offset + 32, src, blocksize); // block data
+
+ src += blocksize;
+ srclen -= blocksize;
+ offset += blocksize + 32;
+ }
+
+ *pdst = dst;
+ *size = dstlen;
+
+ return 0;
+
+fail:
+ av_freep(&dst);
+ return ret;
+}
+
+static int matroska_parse_frame(MatroskaDemuxContext *matroska,
+ MatroskaTrack *track, AVStream *st,
+ uint8_t *data, int pkt_size,
+ uint64_t timecode, uint64_t duration,
+ int64_t pos, int is_keyframe)
+{
+ MatroskaTrackEncoding *encodings = track->encodings.elem;
+ uint8_t *pkt_data = data;
+ int offset = 0, res;
+ AVPacket *pkt;
+
+ if (encodings && encodings->scope & 1) {
+ res = matroska_decode_buffer(&pkt_data, &pkt_size, track);
+ if (res < 0)
+ return res;
+ }
+
+ if (st->codec->codec_id == AV_CODEC_ID_WAVPACK) {
+ uint8_t *wv_data;
+ res = matroska_parse_wavpack(track, pkt_data, &wv_data, &pkt_size);
+ if (res < 0) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Error parsing a wavpack block.\n");
+ goto fail;
+ }
+ if (pkt_data != data)
+ av_freep(&pkt_data);
+ pkt_data = wv_data;
+ }
+
+ if (st->codec->codec_id == AV_CODEC_ID_PRORES)
+ offset = 8;
+
+ pkt = av_mallocz(sizeof(AVPacket));
+ /* XXX: prevent data copy... */
+ if (av_new_packet(pkt, pkt_size + offset) < 0) {
+ av_free(pkt);
+ av_freep(&pkt_data);
+ return AVERROR(ENOMEM);
+ }
+
+ if (st->codec->codec_id == AV_CODEC_ID_PRORES) {
+ uint8_t *buf = pkt->data;
+ bytestream_put_be32(&buf, pkt_size);
+ bytestream_put_be32(&buf, MKBETAG('i', 'c', 'p', 'f'));
+ }
+
+ memcpy(pkt->data + offset, pkt_data, pkt_size);
+
+ if (pkt_data != data)
+ av_free(pkt_data);
+
+ pkt->flags = is_keyframe;
+ pkt->stream_index = st->index;
+
+ if (track->ms_compat)
+ pkt->dts = timecode;
+ else
+ pkt->pts = timecode;
+ pkt->pos = pos;
+ if (st->codec->codec_id == AV_CODEC_ID_TEXT)
+ pkt->convergence_duration = duration;
+ else if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE)
+ pkt->duration = duration;
+
+ if (st->codec->codec_id == AV_CODEC_ID_SSA)
+ matroska_fix_ass_packet(matroska, pkt, duration);
+
+ if (matroska->prev_pkt &&
+ timecode != AV_NOPTS_VALUE &&
+ matroska->prev_pkt->pts == timecode &&
+ matroska->prev_pkt->stream_index == st->index &&
+ st->codec->codec_id == AV_CODEC_ID_SSA)
+ matroska_merge_packets(matroska->prev_pkt, pkt);
+ else {
+ dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
+ matroska->prev_pkt = pkt;
+ }
+
+ return 0;
+
+fail:
+ if (pkt_data != data)
+ av_freep(&pkt_data);
+ return res;
+}
+