diff --git a/ChangeLog b/ChangeLog index 3c955cb39a..03a653838c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2008-08-02 Sebastian Dröge + + * configure.ac: + * gst/matroska/Makefile.am: + * gst/matroska/lzo.c: (get_byte), (get_len), (copy), + (copy_backptr), (lzo1x_decode), (main): + * gst/matroska/lzo.h: + * gst/matroska/matroska-demux.c: + (gst_matroska_demux_read_track_encoding), + (gst_matroska_decompress_data), (gst_matroska_decode_data), + (gst_matroska_decode_buffer), + (gst_matroska_decode_content_encodings), + (gst_matroska_demux_read_track_encodings), + (gst_matroska_demux_add_stream), + (gst_matroska_demux_parse_blockgroup_or_simpleblock): + * gst/matroska/matroska-ids.h: + Decode the codec private data and following ContentEncoding if + necessary. + + Support bzip2, lzo and header stripped compression. For lzo use the + ffmpeg lzo implementation as liblzo is GPL licensed. + + Fix zlib decompression. + 2008-08-02 Sebastian Dröge * gst/matroska/matroska-mux.c: diff --git a/configure.ac b/configure.ac index 73fb95cd8c..ccd9770b4e 100644 --- a/configure.ac +++ b/configure.ac @@ -933,9 +933,9 @@ AG_GST_CHECK_FEATURE(WAVPACK, [wavpack plug-in], wavpack, [ AC_SUBST(WAVPACK_LIBS) ]) -dnl *** qtdemux & id3demux prefer to have zlib *** +dnl *** qtdemux & id3demux & matroska prefer to have zlib *** translit(dnm, m, l) AM_CONDITIONAL(USE_ZLIB, true) -AG_GST_CHECK_FEATURE(ZLIB, [zlib support for id3demux/qtdemux],, [ +AG_GST_CHECK_FEATURE(ZLIB, [zlib support for id3demux/qtdemux/matroska],, [ AG_GST_CHECK_LIBHEADER(ZLIB, z, uncompress,, zlib.h, [ HAVE_ZLIB="yes" @@ -944,6 +944,13 @@ AG_GST_CHECK_FEATURE(ZLIB, [zlib support for id3demux/qtdemux],, [ ]) ]) +dnl *** matroska prefers to have bz2 *** +translit(dnm, m, l) AM_CONDITIONAL(USE_BZ2, true) +AG_GST_CHECK_FEATURE(BZ2, [bz2 library for matroska ],, [ + AG_GST_CHECK_LIBHEADER(BZ2, bz2, BZ2_bzCompress, ,bzlib.h, BZ2_LIBS="-lbz2") + AC_SUBST(BZ2_LIBS) +]) + else dnl not building plugins with external dependencies, diff --git a/gst/matroska/Makefile.am b/gst/matroska/Makefile.am index 6c3f2275e8..6ce6b10d3a 100644 --- a/gst/matroska/Makefile.am +++ b/gst/matroska/Makefile.am @@ -6,7 +6,8 @@ libgstmatroska_la_SOURCES = \ matroska.c \ matroska-demux.c \ matroska-ids.c \ - matroska-mux.c + matroska-mux.c \ + lzo.c noinst_HEADERS = \ ebml-ids.h \ @@ -14,7 +15,8 @@ noinst_HEADERS = \ ebml-write.h \ matroska-demux.h \ matroska-ids.h \ - matroska-mux.h + matroska-mux.h \ + lzo.h libgstmatroska_la_CFLAGS = \ $(GST_BASE_CFLAGS) \ @@ -28,5 +30,6 @@ libgstmatroska_la_LIBADD = \ -lgstriff-@GST_MAJORMINOR@ \ -lgsttag-@GST_MAJORMINOR@ \ $(ZLIB_LIBS) \ + $(BZ2_LIBS) \ $(LIBM) libgstmatroska_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/gst/matroska/lzo.c b/gst/matroska/lzo.c new file mode 100644 index 0000000000..57982348da --- /dev/null +++ b/gst/matroska/lzo.c @@ -0,0 +1,288 @@ +/* + * LZO 1x decompression + * Copyright (c) 2006 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "_stdint.h" +#include "lzo.h" + +/*! define if we may write up to 12 bytes beyond the output buffer */ +/* #define OUTBUF_PADDED 1 */ +/*! define if we may read up to 8 bytes beyond the input buffer */ +/* #define INBUF_PADDED 1 */ +typedef struct LZOContext +{ + const uint8_t *in, *in_end; + uint8_t *out_start, *out, *out_end; + int error; +} LZOContext; + +/** + * \brief read one byte from input buffer, avoiding overrun + * \return byte read + */ +static inline int +get_byte (LZOContext * c) +{ + if (c->in < c->in_end) + return *c->in++; + c->error |= LZO_INPUT_DEPLETED; + return 1; +} + +#ifdef INBUF_PADDED +#define GETB(c) (*(c).in++) +#else +#define GETB(c) get_byte(&(c)) +#endif + +/** + * \brief decode a length value in the coding used by lzo + * \param x previous byte value + * \param mask bits used from x + * \return decoded length value + */ +static inline int +get_len (LZOContext * c, int x, int mask) +{ + int cnt = x & mask; + if (!cnt) { + while (!(x = get_byte (c))) + cnt += 255; + cnt += mask + x; + } + return cnt; +} + +/*#define UNALIGNED_LOADSTORE */ +#define BUILTIN_MEMCPY +#ifdef UNALIGNED_LOADSTORE +#define COPY2(d, s) *(uint16_t *)(d) = *(uint16_t *)(s); +#define COPY4(d, s) *(uint32_t *)(d) = *(uint32_t *)(s); +#elif defined(BUILTIN_MEMCPY) +#define COPY2(d, s) memcpy(d, s, 2); +#define COPY4(d, s) memcpy(d, s, 4); +#else +#define COPY2(d, s) (d)[0] = (s)[0]; (d)[1] = (s)[1]; +#define COPY4(d, s) (d)[0] = (s)[0]; (d)[1] = (s)[1]; (d)[2] = (s)[2]; (d)[3] = (s)[3]; +#endif + +/** + * \brief copy bytes from input to output buffer with checking + * \param cnt number of bytes to copy, must be >= 0 + */ +static inline void +copy (LZOContext * c, int cnt) +{ + register const uint8_t *src = c->in; + register uint8_t *dst = c->out; + if (cnt > c->in_end - src) { + cnt = MAX (c->in_end - src, 0); + c->error |= LZO_INPUT_DEPLETED; + } + if (cnt > c->out_end - dst) { + cnt = MAX (c->out_end - dst, 0); + c->error |= LZO_OUTPUT_FULL; + } +#if defined(INBUF_PADDED) && defined(OUTBUF_PADDED) + COPY4 (dst, src); + src += 4; + dst += 4; + cnt -= 4; + if (cnt > 0) +#endif + memcpy (dst, src, cnt); + c->in = src + cnt; + c->out = dst + cnt; +} + +/** + * \brief copy previously decoded bytes to current position + * \param back how many bytes back we start + * \param cnt number of bytes to copy, must be >= 0 + * + * cnt > back is valid, this will copy the bytes we just copied, + * thus creating a repeating pattern with a period length of back. + */ +static inline void +copy_backptr (LZOContext * c, int back, int cnt) +{ + register const uint8_t *src = &c->out[-back]; + register uint8_t *dst = c->out; + if (src < c->out_start || src > dst) { + c->error |= LZO_INVALID_BACKPTR; + return; + } + if (cnt > c->out_end - dst) { + cnt = MAX (c->out_end - dst, 0); + c->error |= LZO_OUTPUT_FULL; + } + if (back == 1) { + memset (dst, *src, cnt); + dst += cnt; + } else { +#ifdef OUTBUF_PADDED + COPY2 (dst, src); + COPY2 (dst + 2, src + 2); + src += 4; + dst += 4; + cnt -= 4; + if (cnt > 0) { + COPY2 (dst, src); + COPY2 (dst + 2, src + 2); + COPY2 (dst + 4, src + 4); + COPY2 (dst + 6, src + 6); + src += 8; + dst += 8; + cnt -= 8; + } +#endif + if (cnt > 0) { + int blocklen = back; + while (cnt > blocklen) { + memcpy (dst, src, blocklen); + dst += blocklen; + cnt -= blocklen; + blocklen <<= 1; + } + memcpy (dst, src, cnt); + } + dst += cnt; + } + c->out = dst; +} + +/** + * \brief decode LZO 1x compressed data + * \param out output buffer + * \param outlen size of output buffer, number of bytes left are returned here + * \param in input buffer + * \param inlen size of input buffer, number of bytes left are returned here + * \return 0 on success, otherwise error flags, see lzo.h + * + * make sure all buffers are appropriately padded, in must provide + * LZO_INPUT_PADDING, out must provide LZO_OUTPUT_PADDING additional bytes + */ +int +lzo1x_decode (void *out, int *outlen, const void *in, int *inlen) +{ + int state = 0; + int x; + LZOContext c; + c.in = in; + c.in_end = (const uint8_t *) in + *inlen; + c.out = c.out_start = out; + c.out_end = (uint8_t *) out + *outlen; + c.error = 0; + x = GETB (c); + if (x > 17) { + copy (&c, x - 17); + x = GETB (c); + if (x < 16) + c.error |= LZO_ERROR; + } + if (c.in > c.in_end) + c.error |= LZO_INPUT_DEPLETED; + while (!c.error) { + int cnt, back; + if (x > 15) { + if (x > 63) { + cnt = (x >> 5) - 1; + back = (GETB (c) << 3) + ((x >> 2) & 7) + 1; + } else if (x > 31) { + cnt = get_len (&c, x, 31); + x = GETB (c); + back = (GETB (c) << 6) + (x >> 2) + 1; + } else { + cnt = get_len (&c, x, 7); + back = (1 << 14) + ((x & 8) << 11); + x = GETB (c); + back += (GETB (c) << 6) + (x >> 2); + if (back == (1 << 14)) { + if (cnt != 1) + c.error |= LZO_ERROR; + break; + } + } + } else if (!state) { + cnt = get_len (&c, x, 15); + copy (&c, cnt + 3); + x = GETB (c); + if (x > 15) + continue; + cnt = 1; + back = (1 << 11) + (GETB (c) << 2) + (x >> 2) + 1; + } else { + cnt = 0; + back = (GETB (c) << 2) + (x >> 2) + 1; + } + copy_backptr (&c, back, cnt + 2); + state = cnt = x & 3; + copy (&c, cnt); + x = GETB (c); + } + *inlen = c.in_end - c.in; + if (c.in > c.in_end) + *inlen = 0; + *outlen = c.out_end - c.out; + return c.error; +} + +#ifdef TEST +#include +#include +#include "log.h" +#define MAXSZ (10*1024*1024) +int +main (int argc, char *argv[]) +{ + FILE *in = fopen (argv[1], "rb"); + uint8_t *orig = av_malloc (MAXSZ + 16); + uint8_t *comp = av_malloc (2 * MAXSZ + 16); + uint8_t *decomp = av_malloc (MAXSZ + 16); + size_t s = fread (orig, 1, MAXSZ, in); + lzo_uint clen = 0; + long tmp[LZO1X_MEM_COMPRESS]; + int inlen, outlen; + int i; + av_log_level = AV_LOG_DEBUG; + lzo1x_999_compress (orig, s, comp, &clen, tmp); + for (i = 0; i < 300; i++) { + START_TIMER inlen = clen; + outlen = MAXSZ; +#ifdef LIBLZO + if (lzo1x_decompress_safe (comp, inlen, decomp, &outlen, NULL)) +#elif defined(LIBLZO_UNSAFE) + if (lzo1x_decompress (comp, inlen, decomp, &outlen, NULL)) +#else + if (lzo1x_decode (decomp, &outlen, comp, &inlen)) +#endif + av_log (NULL, AV_LOG_ERROR, "decompression error\n"); + STOP_TIMER ("lzod") + } + if (memcmp (orig, decomp, s)) + av_log (NULL, AV_LOG_ERROR, "decompression incorrect\n"); + else + av_log (NULL, AV_LOG_ERROR, "decompression ok\n"); + return 0; +} +#endif diff --git a/gst/matroska/lzo.h b/gst/matroska/lzo.h new file mode 100644 index 0000000000..e7795f7718 --- /dev/null +++ b/gst/matroska/lzo.h @@ -0,0 +1,35 @@ +/* + * LZO 1x decompression + * copyright (c) 2006 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FFMPEG_LZO_H +#define FFMPEG_LZO_H + +#define LZO_INPUT_DEPLETED 1 +#define LZO_OUTPUT_FULL 2 +#define LZO_INVALID_BACKPTR 4 +#define LZO_ERROR 8 + +#define LZO_INPUT_PADDING 8 +#define LZO_OUTPUT_PADDING 12 + +int lzo1x_decode(void *out, int *outlen, const void *in, int *inlen); + +#endif /* FFMPEG_LZO_H */ diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index ee74aeb081..ba67759c5f 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -54,6 +54,12 @@ #include #endif +#ifdef HAVE_BZ2 +#include +#endif + +#include "lzo.h" + #include "matroska-demux.h" #include "matroska-ids.h" @@ -462,10 +468,6 @@ gst_matroska_demux_read_track_encoding (GstMatroskaDemux * demux, G_GUINT64_FORMAT, num); ret = GST_FLOW_ERROR; break; - } else if (num & 0x4) { - GST_ERROR_OBJECT (demux, "Unsupported ContentEncodingScope %" - G_GUINT64_FORMAT, num); - ret = GST_FLOW_ERROR; } GST_DEBUG_OBJECT (demux, "ContentEncodingScope: %" G_GUINT64_FORMAT, @@ -524,11 +526,6 @@ gst_matroska_demux_read_track_encoding (GstMatroskaDemux * demux, G_GUINT64_FORMAT, num); ret = GST_FLOW_ERROR; break; - } else if (num != 0) { - GST_ERROR_OBJECT (demux, "Unsupported ContentCompAlgo %" - G_GUINT64_FORMAT, num); - ret = GST_FLOW_ERROR; - break; } GST_DEBUG_OBJECT (demux, "ContentCompAlgo: %" G_GUINT64_FORMAT, num); @@ -596,6 +593,317 @@ gst_matroska_demux_read_track_encoding (GstMatroskaDemux * demux, return ret; } +static gboolean +gst_matroska_decompress_data (GstMatroskaTrackEncoding * enc, + guint8 ** data_out, guint * size_out, + GstMatroskaTrackCompressionAlgorithm algo) +{ + guint8 *new_data = NULL; + guint new_size = 0; + + guint8 *data = *data_out; + guint size = *size_out; + + gboolean ret = TRUE; + + if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_ZLIB) { +#ifdef HAVE_ZLIB + /* zlib encoded data */ + z_stream zstream; + guint orig_size; + int result; + + orig_size = size; + zstream.zalloc = (alloc_func) 0; + zstream.zfree = (free_func) 0; + zstream.opaque = (voidpf) 0; + if (inflateInit (&zstream) != Z_OK) { + GST_WARNING ("zlib initialization failed."); + ret = FALSE; + goto out; + } + zstream.next_in = (Bytef *) data; + zstream.avail_in = orig_size; + new_size = orig_size; + new_data = g_malloc (new_size); + zstream.avail_out = new_size; + zstream.next_out = (Bytef *) new_data; + + do { + result = inflate (&zstream, Z_NO_FLUSH); + if (result != Z_OK && result != Z_STREAM_END) { + GST_WARNING ("zlib decompression failed."); + g_free (new_data); + inflateEnd (&zstream); + break; + } + new_size += 4000; + new_data = g_realloc (new_data, new_size); + zstream.next_out = (Bytef *) (new_data + zstream.total_out); + zstream.avail_out += 4000; + } while (zstream.avail_in != 0 && result != Z_STREAM_END); + + if (result != Z_STREAM_END) { + ret = FALSE; + goto out; + } else { + new_size = zstream.total_out; + inflateEnd (&zstream); + } +#else + GST_WARNING ("zlib encoded tracks not supported."); + ret = FALSE; + goto out; +#endif + } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_BZLIB) { +#ifdef HAVE_BZ2 + /* bzip2 encoded data */ + bz_stream bzstream; + guint orig_size; + int result; + + bzstream.bzalloc = NULL; + bzstream.bzfree = NULL; + bzstream.opaque = NULL; + orig_size = size; + + if ((result = BZ2_bzDecompressInit (&bzstream, 0, 0)) != BZ_OK) { + GST_WARNING ("bzip2 initialization failed."); + ret = FALSE; + goto out; + } + + bzstream.next_in = (char *) data; + bzstream.avail_in = orig_size; + new_size = orig_size; + new_data = g_malloc (new_size); + bzstream.avail_out = new_size; + bzstream.next_out = (char *) new_data; + + do { + result = BZ2_bzDecompress (&bzstream); + if (result != BZ_OK && result != BZ_STREAM_END) { + GST_WARNING ("bzip2 decompression failed."); + g_free (new_data); + BZ2_bzDecompressEnd (&bzstream); + break; + } + new_size += 4000; + new_data = g_realloc (new_data, new_size); + bzstream.next_out = (char *) (new_data + bzstream.total_out_lo32); + bzstream.avail_out += 4000; + } while (bzstream.avail_in != 0 && result != BZ_STREAM_END); + + if (result != BZ_STREAM_END) { + ret = FALSE; + goto out; + } else { + new_size = bzstream.total_out_lo32; + BZ2_bzDecompressEnd (&bzstream); + } +#else + GST_WARNING ("bzip2 encoded tracks not supported."); + ret = FALSE; + goto out; +#endif + } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_LZO1X) { + /* lzo encoded data */ + int result; + int orig_size, out_size; + + orig_size = size; + out_size = size; + new_size = size; + new_data = g_malloc (new_size); + + do { + orig_size = size; + out_size = new_size; + + result = lzo1x_decode (new_data, &out_size, data, &orig_size); + + if (orig_size > 0) { + new_size += 4000; + new_data = g_realloc (new_data, new_size); + } + } while (orig_size > 0 && result == LZO_OUTPUT_FULL); + + new_size -= out_size; + + if (result != LZO_OUTPUT_FULL) { + GST_WARNING ("lzo decompression failed"); + g_free (new_data); + + ret = FALSE; + goto out; + } + + } else if (algo == GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_HEADERSTRIP) { + /* header stripped encoded data */ + if (enc->comp_settings_length > 0) { + new_data = g_malloc (size + enc->comp_settings_length); + new_size = size + enc->comp_settings_length; + + memcpy (new_data, enc->comp_settings, enc->comp_settings_length); + memcpy (new_data + enc->comp_settings_length, data, size); + } + } else { + g_assert_not_reached (); + } + +out: + + if (!ret) { + *data_out = NULL; + *size_out = 0; + } else { + *data_out = new_data; + *size_out = new_size; + } + + return ret; +} + +static gboolean +gst_matroska_decode_data (GArray * encodings, guint8 ** data_out, + guint * size_out, GstMatroskaTrackEncodingScope scope, gboolean free) +{ + guint8 *data; + guint size; + gboolean ret = TRUE; + gint i; + + g_return_val_if_fail (encodings != NULL, FALSE); + g_return_val_if_fail (data_out != NULL && *data_out != NULL, FALSE); + g_return_val_if_fail (size_out != NULL, FALSE); + + data = *data_out; + size = *size_out; + + for (i = 0; i < encodings->len; i++) { + GstMatroskaTrackEncoding *enc = + &g_array_index (encodings, GstMatroskaTrackEncoding, i); + guint8 *new_data = NULL; + guint new_size = 0; + + if ((enc->scope & scope) == 0) + continue; + + /* Encryption not supported yet */ + if (enc->type != 0) { + ret = FALSE; + break; + } + + new_data = data; + new_size = size; + + ret = + gst_matroska_decompress_data (enc, &new_data, &new_size, + enc->comp_algo); + + if (!ret) + break; + + if ((data == *data_out && free) || (data != *data_out)) + g_free (data); + + data = new_data; + size = new_size; + } + + if (!ret) { + if ((data == *data_out && free) || (data != *data_out)) + g_free (data); + + *data_out = NULL; + *size_out = 0; + } else { + *data_out = data; + *size_out = size; + } + + return ret; +} + +static GstBuffer * +gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf) +{ + guint8 *data; + guint size; + GstBuffer *new_buf; + + g_return_val_if_fail (GST_IS_BUFFER (buf), NULL); + + data = GST_BUFFER_DATA (buf); + size = GST_BUFFER_SIZE (buf); + + g_return_val_if_fail (data != NULL && size > 0, buf); + + if (gst_matroska_decode_data (context->encodings, &data, &size, + GST_MATROSKA_TRACK_ENCODING_SCOPE_FRAME, FALSE)) { + new_buf = gst_buffer_new (); + GST_BUFFER_MALLOCDATA (new_buf) = (guint8 *) data; + GST_BUFFER_DATA (new_buf) = (guint8 *) data; + GST_BUFFER_SIZE (new_buf) = size; + + gst_buffer_unref (buf); + buf = new_buf; + + return buf; + } else { + gst_buffer_unref (buf); + return NULL; + } +} + +static GstFlowReturn +gst_matroska_decode_content_encodings (GArray * encodings) +{ + gint i; + + if (encodings == NULL) + return GST_FLOW_OK; + + for (i = 0; i < encodings->len; i++) { + GstMatroskaTrackEncoding *enc = + &g_array_index (encodings, GstMatroskaTrackEncoding, i); + GstMatroskaTrackEncoding *enc2; + guint8 *data = NULL; + guint size; + + if ((enc-> + scope & GST_MATROSKA_TRACK_ENCODING_SCOPE_NEXT_CONTENT_ENCODING) == + 0) + continue; + + /* Encryption not supported yet */ + if (enc->type != 0) + return GST_FLOW_ERROR; + + if (i + 1 >= encodings->len) + return GST_FLOW_ERROR; + + enc2 = &g_array_index (encodings, GstMatroskaTrackEncoding, i + 1); + + if (enc->comp_settings_length == 0) + continue; + + data = enc->comp_settings; + size = enc->comp_settings_length; + + if (!gst_matroska_decompress_data (enc, &data, &size, enc->comp_algo)) + return GST_FLOW_ERROR; + + g_free (enc->comp_settings); + + enc->comp_settings = data; + enc->comp_settings_length = size; + } + + return GST_FLOW_OK; +} + static GstFlowReturn gst_matroska_demux_read_track_encodings (GstMatroskaDemux * demux, GstMatroskaTrackContext * context) @@ -648,9 +956,7 @@ gst_matroska_demux_read_track_encodings (GstMatroskaDemux * demux, g_array_sort (context->encodings, (GCompareFunc) gst_matroska_demux_encoding_cmp); - /* TODO: Decompress & decrypt ContentEncodings if necessary */ - - return ret; + return gst_matroska_decode_content_encodings (context->encodings); } static gboolean @@ -1355,7 +1661,16 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux) DEBUG_ELEMENT_STOP (demux, ebml, "TrackEntry", ret); - /* TODO: decompress/decrypt codec private if necessary */ + /* Decode codec private data if necessary */ + if (context->encodings && context->encodings->len > 0 && context->codec_priv + && context->codec_priv_size > 0) { + if (!gst_matroska_decode_data (context->encodings, + (guint8 **) & context->codec_priv, &context->codec_priv_size, + GST_MATROSKA_TRACK_ENCODING_SCOPE_CODEC_DATA, TRUE)) { + GST_WARNING_OBJECT (demux, "Decoding codec private data failed"); + ret = GST_FLOW_ERROR; + } + } if (context->type == 0 || context->codec_id == NULL || (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED)) { @@ -3496,100 +3811,6 @@ gst_matroska_demux_check_subtitle_buffer (GstElement * element, return GST_FLOW_OK; } -static GstBuffer * -gst_matroska_decode_buffer (GstMatroskaTrackContext * context, GstBuffer * buf) -{ - gint i; - - g_assert (context->encodings != NULL); - - for (i = 0; i < context->encodings->len; i++) { - GstMatroskaTrackEncoding *enc; - guint8 *new_data = NULL; - guint new_size = 0; - GstBuffer *new_buf; - - enc = &g_array_index (context->encodings, GstMatroskaTrackEncoding, i); - - /* Currently only compression is supported */ - if (enc->type != 0) - break; - - /* FIXME: use enc->scope ! only necessary to decode buffer if scope & 0x1 */ - - if (enc->comp_algo == 0) { -#ifdef HAVE_ZLIB - /* zlib encoded track */ - z_stream zstream; - guint orig_size; - int result; - - orig_size = GST_BUFFER_SIZE (buf); - zstream.zalloc = (alloc_func) 0; - zstream.zfree = (free_func) 0; - zstream.opaque = (voidpf) 0; - if (inflateInit (&zstream) != Z_OK) { - GST_WARNING ("zlib initialization failed."); - break; - } - zstream.next_in = (Bytef *) GST_BUFFER_DATA (buf); - zstream.avail_in = orig_size; - new_size = orig_size; - new_data = g_malloc (new_size); - zstream.avail_out = new_size; - /* FIXME: not exactly fast, right? */ - do { - new_size += 4000; - new_data = g_realloc (new_data, new_size); - zstream.next_out = (Bytef *) (new_data + zstream.total_out); - result = inflate (&zstream, Z_NO_FLUSH); - if (result != Z_OK && result != Z_STREAM_END) { - GST_WARNING ("zlib decompression failed."); - g_free (new_data); - inflateEnd (&zstream); - break; - } - zstream.avail_out += 4000; - } while (zstream.avail_out == 4000 && - zstream.avail_in != 0 && result != Z_STREAM_END); - - new_size = zstream.total_out; - inflateEnd (&zstream); -#else - GST_WARNING ("zlib encoded tracks not supported."); - break; -#endif -/* FIXME: add bzip/lzo support, what is header stripped? - * it's insane and requires deeper knowledge of the used codec - */ - } else if (enc->comp_algo == 1) { - GST_WARNING ("BZIP encoded tracks not supported."); - break; - } else if (enc->comp_algo == 2) { - GST_WARNING ("LZO encoded tracks not supported."); - break; - } else if (enc->comp_algo == 3) { - GST_WARNING ("Header-stripped tracks not supported."); - break; - } else { - g_assert_not_reached (); - } - - g_assert (new_data != NULL); - - new_buf = gst_buffer_new (); - GST_BUFFER_MALLOCDATA (new_buf) = (guint8 *) new_data; - GST_BUFFER_DATA (new_buf) = (guint8 *) new_data; - GST_BUFFER_SIZE (new_buf) = new_size; - gst_buffer_copy_metadata (new_buf, buf, GST_BUFFER_COPY_TIMESTAMPS); - - gst_buffer_unref (buf); - buf = new_buf; - } - - return buf; -} - static GstFlowReturn gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, guint64 cluster_time, gboolean is_simpleblock) @@ -3804,7 +4025,17 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, g_free (stream->codec_state); stream->codec_state = data; stream->codec_state_size = data_len; - /* TODO: decompress/decrypt if necessary */ + + /* Decode if necessary */ + if (stream->encodings && stream->encodings->len > 0 + && stream->codec_state && stream->codec_state_size > 0) { + if (!gst_matroska_decode_data (stream->encodings, + (guint8 **) & stream->codec_state, &stream->codec_state_size, + GST_MATROSKA_TRACK_ENCODING_SCOPE_CODEC_DATA, TRUE)) { + GST_WARNING_OBJECT (demux, "Decoding codec state failed"); + } + } + GST_DEBUG_OBJECT (demux, "CodecState of %u bytes", stream->codec_state_size); break; @@ -3880,6 +4111,11 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, if (stream->encodings != NULL && stream->encodings->len > 0) sub = gst_matroska_decode_buffer (stream, sub); + if (sub == NULL) { + GST_WARNING_OBJECT (demux, "Decoding buffer failed"); + goto next_lace; + } + GST_BUFFER_TIMESTAMP (sub) = lace_time; if (lace_time != GST_CLOCK_TIME_NONE) { @@ -3950,6 +4186,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, /* combine flows */ ret = gst_matroska_demux_combine_flows (demux, stream, ret); + next_lace: size -= lace_size[n]; if (lace_time != GST_CLOCK_TIME_NONE) lace_time += duration / laces; diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index c6c1852a14..8a73d37a06 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -554,6 +554,19 @@ typedef struct _Wavpack4Header { guint32 crc; /* crc for actual decoded data */ } Wavpack4Header; +typedef enum { + GST_MATROSKA_TRACK_ENCODING_SCOPE_FRAME = (1<<0), + GST_MATROSKA_TRACK_ENCODING_SCOPE_CODEC_DATA = (1<<1), + GST_MATROSKA_TRACK_ENCODING_SCOPE_NEXT_CONTENT_ENCODING = (1<<2) +} GstMatroskaTrackEncodingScope; + +typedef enum { + GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_ZLIB = 0, + GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_BZLIB = 1, + GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_LZO1X = 2, + GST_MATROSKA_TRACK_COMPRESSION_ALGORITHM_HEADERSTRIP = 3 +} GstMatroskaTrackCompressionAlgorithm; + typedef struct _GstMatroskaTrackEncoding { guint order; guint scope : 3;