2008-06-10 Sebastian Dröge <slomo@circular-chaos.org>
+ * gst/matroska/ebml-write.c: (gst_ebml_write_float):
+ Use GDOUBLE_TO_BE() instead of (probably slower) custom code.
+
+ * gst/matroska/matroska-demux.c: (gst_matroska_demux_base_init),
+ (gst_matroska_demux_class_init), (gst_matroska_demux_init),
+ (gst_matroska_track_free), (gst_matroska_demux_encoding_cmp),
+ (gst_matroska_demux_read_track_encodings),
+ (gst_matroska_demux_add_stream),
+ (gst_matroska_demux_handle_src_query),
+ (gst_matroska_demux_init_stream),
+ (gst_matroska_demux_parse_index_cuetrack),
+ (gst_matroska_demux_parse_index_pointentry),
+ (gst_matroska_demux_parse_info),
+ (gst_matroska_demux_parse_metadata_id_simple_tag),
+ (gst_matroska_demux_parse_metadata),
+ (gst_matroska_demux_add_wvpk_header), (gst_matroska_decode_buffer),
+ (gst_matroska_demux_parse_blockgroup_or_simpleblock),
+ (gst_matroska_demux_parse_cluster),
+ (gst_matroska_demux_parse_contents_seekentry),
+ (gst_matroska_demux_loop_stream_parse_id),
+ (gst_matroska_demux_loop), (gst_matroska_demux_video_caps),
+ (gst_matroska_demux_audio_caps),
+ (gst_matroska_demux_subtitle_caps):
+ * gst/matroska/matroska-demux.h:
+ * gst/matroska/matroska-ids.c:
+ (gst_matroska_track_init_subtitle_context):
+ * gst/matroska/matroska-ids.h:
+ * gst/matroska/matroska-mux.c: (gst_matroska_mux_base_init),
+ (gst_matroska_mux_class_init), (gst_matroska_mux_init),
+ (gst_matroska_mux_create_uid), (gst_matroska_mux_reset),
+ (gst_matroska_mux_video_pad_setcaps),
+ (gst_matroska_mux_audio_pad_setcaps),
+ (gst_matroska_mux_subtitle_pad_setcaps),
+ (gst_matroska_mux_request_new_pad),
+ (gst_matroska_mux_track_header), (gst_matroska_mux_start),
+ (gst_matroska_mux_write_simple_tag), (gst_matroska_mux_finish),
+ (gst_matroska_mux_write_data), (gst_matroska_mux_collected),
+ (gst_matroska_mux_set_property):
+ Add many FIXMEs/TODOs all over the matroska muxer and demuxer
+ elements, do some checks for valid values in the demuxer, handle
+ tracktimecodescale in the demuxer, set correct default values for all
+ settings in the demuxer, review and add all missing matroska
+ IDs and some more raw YUV formats, and some trivial cleanup.
+
+2008-06-10 Sebastian Dröge <slomo@circular-chaos.org>
+
* ext/pulse/pulsemixer.c: (gst_pulsemixer_base_init),
(gst_pulsemixer_class_init):
* ext/pulse/pulsesink.c: (gst_pulsesink_base_init),
#endif
#include <string.h>
+#include <gst/floatcast/floatcast.h>
#include "ebml-write.h"
#include "ebml-ids.h"
void
gst_ebml_write_float (GstEbmlWrite * ebml, guint32 id, gdouble num)
{
-#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
- gint n;
-#endif
GstBuffer *buf = gst_ebml_write_element_new (ebml, sizeof (num));
gst_ebml_write_element_id (buf, id);
gst_ebml_write_element_size (buf, 8);
-#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
- for (n = 0; n < 8; n++) {
- GST_BUFFER_DATA (buf)[GST_BUFFER_SIZE (buf)] = ((guint8 *) & num)[7 - n];
- GST_BUFFER_SIZE (buf) += 1;
- }
-#else
+ num = GDOUBLE_TO_BE (num);
gst_ebml_write_element_data (buf, (guint8 *) & num, 8);
-#endif
gst_ebml_write_element_push (ebml, buf);
}
* Boston, MA 02111-1307, USA.
*/
+/* TODO: "Unkown track header" & "Unknown entry": implement if useful
+ * TODO: dynamic number of tracks without upper bound
+ * FIXME: uint64 -> int64 overflows!
+ * FIXME: ignore 0xBF aka. CRC32 elements without warning
+ * TODO: check CRC32 if present
+ * FIXME: go out of loops, don't add Track or whatever if something goes wrong
+ * or required elements are not there
+ * TODO: there can be a segment after the first segment. Handle like
+ * chained oggs. Fixes #334082
+ * TODO: handle gaps better, especially gaps at the start of a track.
+ * Needs sending of filler segments, closing of segments and
+ * other magic... Fixes #429322
+ * TODO: Test samples: http://www.matroska.org/samples/matrix/index.html
+ * http://samples.mplayerhq.hu/Matroska/
+ * TODO: check if demuxing is done correct for all codecs according to spec
+ * TODO: seeking with incomplete or without CUE
+ */
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include <string.h>
-/* For AVI compatibility mode... Who did that? */
-/* and for fourcc stuff */
+/* For AVI compatibility mode
+ and for fourcc stuff */
#include <gst/riff/riff-read.h>
#include <gst/riff/riff-ids.h>
#include <gst/riff/riff-media.h>
GST_STATIC_CAPS ("video/x-matroska")
);
+/* TODO: fill in caps! */
+
static GstStaticPadTemplate audio_src_templ =
GST_STATIC_PAD_TEMPLATE ("audio_%02d",
GST_PAD_SRC,
"application/x-subtitle-unknown")
);
-static void gst_matroska_demux_base_init (GstMatroskaDemuxClass * klass);
-static void gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass);
-static void gst_matroska_demux_init (GstMatroskaDemux * demux);
-
/* element functions */
static void gst_matroska_demux_loop (GstPad * pad);
static gboolean gst_matroska_demux_sink_activate_pull (GstPad * sinkpad,
gboolean active);
static gboolean gst_matroska_demux_sink_activate (GstPad * sinkpad);
+
static gboolean gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux,
GstEvent * event);
static gboolean gst_matroska_demux_handle_src_event (GstPad * pad,
static GstCaps *gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext
* audiocontext,
const gchar * codec_id, gpointer data, guint size, gchar ** codec_name);
-static GstCaps *gst_matroska_demux_complex_caps (GstMatroskaTrackComplexContext
- * complexcontext, const gchar * codec_id, gpointer data, guint size);
static GstCaps
* gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
subtitlecontext, const gchar * codec_id, gpointer data, guint size);
/* stream methods */
static void gst_matroska_demux_reset (GstElement * element);
-static GstEbmlReadClass *parent_class; /* NULL; */
-
-static GType
-gst_matroska_demux_get_type (void)
-{
- static GType gst_matroska_demux_type; /* 0 */
-
- if (!gst_matroska_demux_type) {
- static const GTypeInfo gst_matroska_demux_info = {
- sizeof (GstMatroskaDemuxClass),
- (GBaseInitFunc) gst_matroska_demux_base_init,
- NULL,
- (GClassInitFunc) gst_matroska_demux_class_init,
- NULL,
- NULL,
- sizeof (GstMatroskaDemux),
- 0,
- (GInstanceInitFunc) gst_matroska_demux_init,
- };
-
- gst_matroska_demux_type =
- g_type_register_static (GST_TYPE_EBML_READ,
- "GstMatroskaDemux", &gst_matroska_demux_info, 0);
- }
-
- return gst_matroska_demux_type;
-}
+GST_BOILERPLATE (GstMatroskaDemux, gst_matroska_demux, GstEbmlRead,
+ GST_TYPE_EBML_READ);
static void
-gst_matroska_demux_base_init (GstMatroskaDemuxClass * klass)
+gst_matroska_demux_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
- static const GstElementDetails gst_matroska_demux_details =
- GST_ELEMENT_DETAILS ("Matroska demuxer",
- "Codec/Demuxer",
- "Demuxes a Matroska Stream into video/audio/subtitles",
- "Ronald Bultje <rbultje@ronald.bitfreak.net>");
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&video_src_templ));
gst_static_pad_template_get (&subtitle_src_templ));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_templ));
- gst_element_class_set_details (element_class, &gst_matroska_demux_details);
- GST_DEBUG_CATEGORY_INIT (matroskademux_debug, "matroskademux", 0,
- "Matroska demuxer");
+ gst_element_class_set_details_simple (element_class, "Matroska demuxer",
+ "Codec/Demuxer",
+ "Demuxes a Matroska Stream into video/audio/subtitles",
+ "Ronald Bultje <rbultje@ronald.bitfreak.net>");
}
static void
gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
{
- GstElementClass *gstelement_class;
-
- gstelement_class = (GstElementClass *) klass;
+ GstElementClass *gstelement_class = (GstElementClass *) klass;;
- parent_class = g_type_class_peek_parent (klass);
+ GST_DEBUG_CATEGORY_INIT (matroskademux_debug, "matroskademux", 0,
+ "Matroska demuxer");
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state);
}
static void
-gst_matroska_demux_init (GstMatroskaDemux * demux)
+gst_matroska_demux_init (GstMatroskaDemux * demux,
+ GstMatroskaDemuxClass * klass)
{
- GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
gint i;
- demux->sinkpad =
- gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
- "sink"), "sink");
+ demux->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
gst_pad_set_activate_function (demux->sinkpad,
GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate));
gst_pad_set_activatepull_function (demux->sinkpad,
g_array_free (track->encodings, TRUE);
}
+ if (track->pending_tags)
+ gst_tag_list_free (track->pending_tags);
+
g_free (track);
}
gst_matroska_demux_reset (GstElement * element)
{
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);
+
guint i;
/* reset input */
gst_matroska_demux_encoding_cmp (gconstpointer a, gconstpointer b)
{
const GstMatroskaTrackEncoding *enc_a;
+
const GstMatroskaTrackEncoding *enc_b;
enc_a = (const GstMatroskaTrackEncoding *) a;
enc_b = (const GstMatroskaTrackEncoding *) b;
+ /* FIXME: give warning if diff == 0, should be unique! */
+
return (gint) enc_b->order - (gint) enc_a->order;
}
GstMatroskaDemux * demux, GstMatroskaTrackContext * context)
{
GstFlowReturn ret;
+
guint32 id;
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
case GST_MATROSKA_ID_CONTENTENCODING:{
GstMatroskaTrackEncoding enc = { 0, };
+ /* Set default values */
+ enc.scope = 1;
+
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
break;
}
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+ /* FIXME: must be unique, check this! */
enc.order = num;
break;
}
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
- if (num > 7)
+ if (num > 7 && num == 0)
GST_WARNING ("Unknown scope value in contents encoding.");
else
enc.scope = num;
break;
}
+ /* FIXME: Might be compressed or encrypted, depending on ContentEncodingScope & 0x4
+ * and the previous ContentEncodingOrder */
while (ret == GST_FLOW_OK) {
if ((ret =
&num)) != GST_FLOW_OK) {
break;
}
+ /* FIXME: maybe don't add tracks at all for which we don't
+ * support the compression algorithm */
if (num > 3)
- GST_WARNING ("Unknown scope value in encoding compalgo.");
+ GST_WARNING ("Unknown value in encoding compalgo.");
else
enc.comp_algo = num;
break;
}
case GST_MATROSKA_ID_CONTENTCOMPSETTINGS:{
guint8 *data;
+
guint64 size;
case GST_MATROSKA_ID_CONTENTENCRYPTION:
GST_WARNING ("Encrypted tracks not yet supported");
- /* pass-through */
+ /* FIXME: Might be compressed, depending on ContentEncodingScope & 0x4
+ * and the previous ContentEncodingOrder */
+ /* FIXME: don't add encrypted tracks at all */
+ ret = gst_ebml_read_skip (ebml);
+ break;
default:
GST_WARNING
("Unknown track encoding header entry 0x%x - ignoring", id);
gst_matroska_demux_add_stream (GstMatroskaDemux * demux)
{
GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
+
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstMatroskaTrackContext *context;
+
GstPadTemplate *templ = NULL;
+
GstCaps *caps = NULL;
+
gchar *padname = NULL;
+
GstFlowReturn ret;
+
guint32 id;
+
GstTagList *list = NULL;
+
gchar *codec = NULL;
if (demux->num_streams >= GST_MATROSKA_DEMUX_MAX_STREAMS) {
context->default_duration = 0;
context->pos = 0;
context->set_discont = TRUE;
+ context->timecodescale = 1.0;
+ context->flags =
+ GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT |
+ GST_MATROSKA_TRACK_LACING;
context->last_flow = GST_FLOW_OK;
demux->num_streams++;
}
switch (id) {
+ /* FIXME: check unique */
/* track number (unique stream ID) */
case GST_MATROSKA_ID_TRACKNUMBER:{
guint64 num;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+ if (num == 0) {
+ GST_WARNING ("Invalid track number (0) - skipping");
+ ret = GST_FLOW_ERROR;
+ break;
+ }
+
context->num = num;
break;
}
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+ if (num == 0) {
+ GST_WARNING ("Invalid track UID (0) - skipping");
+ ret = GST_FLOW_ERROR;
+ break;
+ }
+
context->uid = num;
break;
}
GST_WARNING
("More than one tracktype defined in a trackentry - skipping");
break;
+ } else if (track_type < 1 || track_type > 254) {
+ GST_WARNING ("Invalid track type (%u) - skipping",
+ (guint) track_type);
+ break;
}
/* ok, so we're actually going to reallocate this thing */
case GST_MATROSKA_TRACK_TYPE_AUDIO:
gst_matroska_track_init_audio_context (&context);
break;
- case GST_MATROSKA_TRACK_TYPE_COMPLEX:
- gst_matroska_track_init_complex_context (&context);
- break;
case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
gst_matroska_track_init_subtitle_context (&context);
break;
+ case GST_MATROSKA_TRACK_TYPE_COMPLEX:
case GST_MATROSKA_TRACK_TYPE_LOGO:
+ case GST_MATROSKA_TRACK_TYPE_BUTTONS:
case GST_MATROSKA_TRACK_TYPE_CONTROL:
default:
GST_WARNING ("Unknown or unsupported track type %"
}
switch (id) {
- /* fixme, this should be one-up, but I get it here (?) */
+ /* Should be one level up but some broken muxers write it here. */
case GST_MATROSKA_ID_TRACKDEFAULTDURATION:{
guint64 num;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num == 0) {
+ GST_WARNING ("Invalid track default duration (0) - ignoring");
+ break;
+ }
+
context->default_duration = num;
break;
}
/* video framerate */
+ /* NOTE: This one is here only for backward compatibility.
+ * Use _TRACKDEFAULDURATION one level up. */
case GST_MATROSKA_ID_VIDEOFRAMERATE:{
gdouble num;
if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
- if (num != 0.0) {
- context->default_duration =
- gst_gdouble_to_guint64 ((gdouble) GST_SECOND / num);
- videocontext->default_fps = num;
+
+ if (num <= 0.0) {
+ GST_WARNING ("Invalid video framerate (%lf fps) - ignoring",
+ num);
+ break;
}
+
+ if (context->default_duration == 0)
+ context->default_duration =
+ gst_gdouble_to_guint64 ((gdouble) GST_SECOND * (1.0 / num));
+ videocontext->default_fps = num;
break;
}
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num == 0) {
+ GST_WARNING ("Invalid display width (0) - ignoring");
+ break;
+ }
+
videocontext->display_width = num;
GST_DEBUG ("display_width %" G_GUINT64_FORMAT, num);
break;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num == 0) {
+ GST_WARNING ("Invalid display height (0) - ignoring");
+ break;
+ }
+
videocontext->display_height = num;
GST_DEBUG ("display_height %" G_GUINT64_FORMAT, num);
break;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num == 0) {
+ GST_WARNING ("Invalid pixel width (0) - ignoring");
+ break;
+ }
+
videocontext->pixel_width = num;
GST_DEBUG ("pixel_width %" G_GUINT64_FORMAT, num);
break;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num == 0) {
+ GST_WARNING ("Invalid pixel height (0) - ignoring");
+ break;
+ }
+
videocontext->pixel_height = num;
GST_DEBUG ("pixel_height %" G_GUINT64_FORMAT, num);
break;
}
/* aspect ratio behaviour */
- case GST_MATROSKA_ID_VIDEOASPECTRATIO:{
+ case GST_MATROSKA_ID_VIDEOASPECTRATIOTYPE:{
guint64 num;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num > G_MAXUINT32) {
+ GST_WARNING ("Invalid video colourspace (%" G_GUINT64_FORMAT
+ ") - ignoring", num);
+ break;
+ }
videocontext->fourcc = num;
break;
}
+ case GST_MATROSKA_ID_VIDEODISPLAYUNIT:
+ case GST_MATROSKA_ID_VIDEOPIXELCROPBOTTOM:
+ case GST_MATROSKA_ID_VIDEOPIXELCROPTOP:
+ case GST_MATROSKA_ID_VIDEOPIXELCROPLEFT:
+ case GST_MATROSKA_ID_VIDEOPIXELCROPRIGHT:
+ case GST_MATROSKA_ID_VIDEOGAMMAVALUE:
default:
GST_WARNING ("Unknown video track header entry 0x%x - ignoring",
id);
if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num <= 0.0) {
+ GST_WARNING ("Invalid audio sample rate (%lf) - ignoring)",
+ num);
+ break;
+ }
+
+ if (context->default_duration == 0)
+ context->default_duration =
+ gst_gdouble_to_guint64 ((gdouble) GST_SECOND * (1.0 / num));
+
audiocontext->samplerate = num;
break;
}
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num == 0) {
+ GST_WARNING ("Invalid audio bit depth (0) - ignoring)");
+ break;
+ }
+
audiocontext->bitdepth = num;
break;
}
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num == 0) {
+ GST_WARNING
+ ("Invalid number of audio channels (0) - ignoring)");
+ break;
+ }
+
audiocontext->channels = num;
break;
}
+ case GST_MATROSKA_ID_AUDIOCHANNELPOSITIONS:
+ case GST_MATROSKA_ID_AUDIOOUTPUTSAMPLINGFREQ:
default:
GST_WARNING ("Unknown audio track header entry 0x%x - ignoring",
id);
/* codec private data */
case GST_MATROSKA_ID_CODECPRIVATE:{
guint8 *data;
+
guint64 size;
if ((ret =
&size)) != GST_FLOW_OK) {
break;
}
+ /* TODO: might be compressed or encrypted */
context->codec_priv = data;
context->codec_priv_size = size;
GST_LOG_OBJECT (demux, "%u bytes of codec private data", (guint) size);
break;
}
+ /* whether the track must be used during playback */
+ case GST_MATROSKA_ID_TRACKFLAGFORCED:{
+ guint64 num;
+
+ if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
+ break;
+
+ if (num)
+ context->flags |= GST_MATROSKA_TRACK_FORCED;
+ else
+ context->flags &= ~GST_MATROSKA_TRACK_FORCED;
+ break;
+ }
+
/* lacing (like MPEG, where blocks don't end/start on frame
* boundaries) */
case GST_MATROSKA_ID_TRACKFLAGLACING:{
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num == 0) {
+ GST_WARNING ("Invalid track default duration (0) - ignoring");
+ break;
+ }
+
context->default_duration = num;
break;
}
break;
}
+ case GST_MATROSKA_ID_TRACKTIMECODESCALE:{
+ gdouble num;
+
+ if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK)
+ break;
+
+ if (num <= 0.0) {
+ GST_WARNING ("Invalid track time code scale (%lf) - ignoring", num);
+ break;
+ }
+
+ context->timecodescale = num;
+ break;
+ }
+
default:
GST_WARNING ("Unknown track header entry 0x%x - ignoring", id);
/* pass-through */
- /* we ignore these because they're nothing useful (i.e. crap). */
- case GST_MATROSKA_ID_CODECINFOURL:
- case GST_MATROSKA_ID_CODECDOWNLOADURL:
+ /* we ignore these because they're nothing useful (i.e. crap)
+ * or simply not implemented yet. */
case GST_MATROSKA_ID_TRACKMINCACHE:
case GST_MATROSKA_ID_TRACKMAXCACHE:
+ case GST_MATROSKA_ID_MAXBLOCKADDITIONID:
+ case GST_MATROSKA_ID_TRACKATTACHMENTLINK:
+ case GST_MATROSKA_ID_TRACKOVERLAY:
+ case GST_MATROSKA_ID_TRACKTRANSLATE:
+ case GST_MATROSKA_ID_TRACKOFFSET:
+ case GST_MATROSKA_ID_CODECSETTINGS:
+ case GST_MATROSKA_ID_CODECINFOURL:
+ case GST_MATROSKA_ID_CODECDOWNLOADURL:
+ case GST_MATROSKA_ID_CODECDECODEALL:
case GST_EBML_ID_VOID:
ret = gst_ebml_read_skip (ebml);
break;
break;
}
- case GST_MATROSKA_TRACK_TYPE_COMPLEX:{
- GstMatroskaTrackComplexContext *complexcontext =
- (GstMatroskaTrackComplexContext *) context;
- padname = g_strdup_printf ("video_%02d", demux->num_v_streams++);
- templ = gst_element_class_get_pad_template (klass, "video_%02d");
- caps = gst_matroska_demux_complex_caps (complexcontext,
- context->codec_id, context->codec_priv, context->codec_priv_size);
- break;
- }
-
case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
GstMatroskaTrackSubtitleContext *subtitlecontext =
(GstMatroskaTrackSubtitleContext *) context;
break;
}
+ case GST_MATROSKA_TRACK_TYPE_COMPLEX:
case GST_MATROSKA_TRACK_TYPE_LOGO:
+ case GST_MATROSKA_TRACK_TYPE_BUTTONS:
case GST_MATROSKA_TRACK_TYPE_CONTROL:
default:
/* we should already have quit by now */
gst_matroska_demux_handle_src_query (GstPad * pad, GstQuery * query)
{
GstMatroskaDemux *demux;
+
gboolean res = FALSE;
demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad));
+ /* FIXME: do queries on the Tracks, not on the Segment.
+ * Convert between time and frames if we know the duration
+ * of one frame for the track */
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:
{
gint64 segment_stop, gboolean keyunit)
{
guint entry;
+
guint n = 0;
if (!demux->num_indexes)
gst_matroska_demux_send_event (GstMatroskaDemux * demux, GstEvent * event)
{
gboolean ret = TRUE;
+
gint i;
g_return_val_if_fail (event != NULL, FALSE);
gst_matroska_demux_element_send_event (GstElement * element, GstEvent * event)
{
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);
+
gboolean res;
g_return_val_if_fail (event != NULL, FALSE);
GstEvent * event)
{
GstMatroskaIndex *entry;
+
GstSeekFlags flags;
+
GstSeekType cur_type, stop_type;
+
GstFormat format;
+
GstEvent *newsegment_event;
+
gboolean flush, keyunit;
+
gdouble rate;
+
gint64 cur, stop;
+
gint64 segment_start, segment_stop;
+
gint i;
gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
gst_matroska_demux_handle_src_event (GstPad * pad, GstEvent * event)
{
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad));
+
gboolean res = TRUE;
switch (GST_EVENT_TYPE (event)) {
gst_matroska_demux_init_stream (GstMatroskaDemux * demux)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
guint32 id;
+
gchar *doctype;
+
guint version;
+
GstFlowReturn ret;
if ((ret = gst_ebml_read_header (ebml, &doctype, &version)) != GST_FLOW_OK)
return GST_FLOW_ERROR;
}
- /* find segment, must be the next element */
- while (1) {
+ /* find segment, must be the next element but search as long as
+ * we find it anyway */
+ while (TRUE) {
guint last_level;
if ((ret = gst_ebml_peek_id (ebml, &last_level, &id)) != GST_FLOW_OK) {
gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret = GST_FLOW_OK;
+
guint32 id;
while (ret == GST_FLOW_OK) {
gboolean prevent_eos, GstMatroskaIndex * idx, guint64 length)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
guint32 id;
+
GstFlowReturn ret;
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
goto error;
+ if (num == 0) {
+ idx->track = -1;
+ GST_WARNING ("Invalid cue track number (0)");
+ goto error;
+ break;
+ }
idx->track = num;
break;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
goto error;
+ /* FIXME: may overflow, our seeks, etc are int64 based */
+
idx->pos = num;
break;
}
GST_WARNING ("Unknown entry 0x%x in CuesTrackPositions", id);
/* fall-through */
+ case GST_MATROSKA_ID_CUEBLOCKNUMBER:
+ case GST_MATROSKA_ID_CUECODECSTATE:
+ case GST_MATROSKA_ID_CUEREFERENCE:
case GST_EBML_ID_VOID:
if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
goto error;
gboolean prevent_eos, guint64 length)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstMatroskaIndex idx;
+
guint32 id;
+
GstFlowReturn ret;
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
}
/* position in the file + track to which it belongs */
- case GST_MATROSKA_ID_CUETRACKPOSITION:
+ case GST_MATROSKA_ID_CUETRACKPOSITIONS:
{
ret = gst_matroska_demux_parse_index_cuetrack (demux, prevent_eos, &idx,
length);
gst_matroska_demux_parse_index (GstMatroskaDemux * demux, gboolean prevent_eos)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
guint32 id;
+
guint64 length = 0;
+
GstFlowReturn ret = GST_FLOW_OK;
if (prevent_eos) {
gst_matroska_demux_parse_info (GstMatroskaDemux * demux)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret = GST_FLOW_OK;
+
guint32 id;
while (ret == GST_FLOW_OK) {
case GST_MATROSKA_ID_DURATION:{
gdouble num;
+
GstClockTime dur;
if ((ret = gst_ebml_read_float (ebml, &id, &num)) != GST_FLOW_OK) {
break;
}
+
+ if (num <= 0.0) {
+ GST_WARNING ("Invalid duration (%lf) - skipping", num);
+ break;
+ }
+
dur = gst_gdouble_to_guint64 (num *
gst_guint64_to_gdouble (demux->time_scale));
if (GST_CLOCK_TIME_IS_VALID (dur) && dur <= G_MAXINT64)
break;
}
- case GST_MATROSKA_ID_SEGMENTUID:{
+ case GST_MATROSKA_ID_SEGMENTUID:
+ case GST_MATROSKA_ID_SEGMENTFILENAME:
+ case GST_MATROSKA_ID_PREVUID:
+ case GST_MATROSKA_ID_PREVFILENAME:
+ case GST_MATROSKA_ID_NEXTUID:
+ case GST_MATROSKA_ID_NEXTFILENAME:
+ case GST_MATROSKA_ID_TITLE:
+ case GST_MATROSKA_ID_SEGMENTFAMILY:
+ case GST_MATROSKA_ID_CHAPTERTRANSLATE:{
/* TODO not yet implemented. */
ret = gst_ebml_read_skip (ebml);
break;
gst_matroska_demux_parse_metadata_id_simple_tag (GstMatroskaDemux * demux,
gboolean prevent_eos, guint64 length, GstTagList ** p_taglist)
{
+ /* FIXME: check if there are more useful mappings */
struct
{
gchar *matroska_tagname;
GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}
};
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret;
+
guint32 id;
+
gchar *value = NULL;
+
gchar *tag = NULL;
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
GST_WARNING ("Unknown entry 0x%x in metadata collection", id);
/* fall-through */
+ case GST_MATROSKA_ID_TAGLANGUAGE:
+ case GST_MATROSKA_ID_TAGDEFAULT:
+ case GST_MATROSKA_ID_TAGBINARY:
case GST_EBML_ID_VOID:
ret = gst_ebml_read_skip (ebml);
break;
for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
+
const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
if (strcmp (tagname_mkv, tag) == 0) {
g_value_init (&src, G_TYPE_STRING);
g_value_set_string (&src, value);
g_value_init (&dest, dest_type);
- g_value_transform (&src, &dest);
+ if (g_value_transform (&src, &dest))
+ gst_tag_list_add_values (*p_taglist, GST_TAG_MERGE_APPEND,
+ tagname_gst, &dest, NULL);
g_value_unset (&src);
- gst_tag_list_add_values (*p_taglist, GST_TAG_MERGE_APPEND,
- tagname_gst, &dest, NULL);
g_value_unset (&dest);
break;
}
gboolean prevent_eos, guint64 length, GstTagList ** p_taglist)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
guint32 id;
+
GstFlowReturn ret;
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
gboolean prevent_eos)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstTagList *taglist = gst_tag_list_new ();
+
GstFlowReturn ret = GST_FLOW_OK;
+
guint64 length = 0;
+
guint32 id;
+ /* TODO: review length/eos logic */
if (prevent_eos) {
length = gst_ebml_read_get_length (ebml);
}
/* fall-through */
case GST_EBML_ID_VOID:
+ /* FIXME: Use to limit the tags to specific pads */
+ case GST_MATROSKA_ID_TARGETS:
ret = gst_ebml_read_skip (ebml);
break;
}
gst_matroska_ebmlnum_uint (guint8 * data, guint size, guint64 * num)
{
gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
+
guint64 total;
if (size <= 0) {
gst_matroska_ebmlnum_sint (guint8 * data, guint size, gint64 * num)
{
guint64 unum;
+
gint res;
/* read as unsigned number first */
GstMatroskaTrackContext * stream, guint8 * data, guint len)
{
GstFlowReturn ret, cret;
+
GstBuffer *header_buf = NULL;
ret = gst_pad_alloc_buffer_and_set_caps (stream->pad,
GstMatroskaTrackContext * stream)
{
GstFlowReturn ret;
+
guint8 *pdata;
+
guint off, len;
GST_LOG_OBJECT (demux, "priv data size = %u", stream->codec_priv_size);
GstMatroskaTrackContext * stream)
{
GstFlowReturn ret;
+
guint8 *p = (guint8 *) stream->codec_priv;
+
gint i, offset, length, num_packets;
/* start of the stream and vorbis audio or theora video, need to
start = strstr (stream->codec_priv, "palette:");
if (start) {
gint i;
+
guint32 clut[16];
+
guint32 col;
+
guint8 r, g, b, y, u, v;
start += 8;
GstMatroskaTrackContext * stream, gint block_length, GstBuffer ** buf)
{
GstBuffer *newbuf;
+
guint8 *data;
+
guint newlen;
+
GstFlowReturn ret, cret;
/* we need to reconstruct the header of the wavpack block */
Wavpack4Header wvh;
+ /* FIXME: broken for > 2 channels and hybrid files
+ http://www.matroska.org/technical/specs/codecid/wavpack.html */
+
wvh.ck_id[0] = 'w';
wvh.ck_id[1] = 'v';
wvh.ck_id[2] = 'p';
GstMatroskaTrackContext * stream, GstBuffer * buf)
{
GstMatroskaTrackSubtitleContext *sub_stream;
+
const gchar *encoding, *data;
+
GError *err = NULL;
+
GstBuffer *newbuf;
+
gchar *utf8;
+
guint size;
sub_stream = (GstMatroskaTrackSubtitleContext *) stream;
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);
if (enc->type != 0)
break;
- /* FIXME: use enc->scope ? */
+ /* 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);
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);
GST_WARNING ("GZIP 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;
guint64 cluster_time, gboolean is_simpleblock)
{
GstMatroskaTrackContext *stream = NULL;
+
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret = GST_FLOW_OK;
+
gboolean readblock = FALSE;
+
guint32 id;
+
guint64 block_duration = 0;
+
GstBuffer *buf = NULL;
- gint stream_num = 0, n, laces = 0;
+
+ gint stream_num = -1, n, laces = 0;
+
guint size = 0;
+
gint *lace_size = NULL;
+
gint64 time = 0;
- gint64 lace_time = 0;
+
gint flags = 0;
+
gint64 referenceblock = 0;
while (ret == GST_FLOW_OK) {
case GST_MATROSKA_ID_BLOCK:
{
guint64 num;
+
guint8 *data;
if ((ret = gst_ebml_read_buffer (ebml, &id, &buf)) != GST_FLOW_OK)
gst_buffer_unref (buf);
buf = NULL;
GST_WARNING ("Invalid stream %d or size %u", stream_num, size);
+ ret = GST_FLOW_ERROR;
break;
}
stream = demux->src[stream_num];
/* time (relative to cluster time) */
- time = ((gint16) GST_READ_UINT16_BE (data)) * demux->time_scale;
+ time = ((gint16) GST_READ_UINT16_BE (data));
data += 2;
size -= 2;
flags = GST_READ_UINT8 (data);
total = lace_size[0] = num;
for (n = 1; ret == GST_FLOW_OK && n < laces - 1; n++) {
gint64 snum;
+
gint r;
if ((r = gst_matroska_ebmlnum_sint (data, size, &snum)) < 0) {
GST_WARNING ("Unknown entry 0x%x in blockgroup data", id);
/* fall-through */
+ case GST_MATROSKA_ID_BLOCKVIRTUAL:
+ case GST_MATROSKA_ID_BLOCKADDITIONS:
+ case GST_MATROSKA_ID_REFERENCEPRIORITY:
+ case GST_MATROSKA_ID_REFERENCEVIRTUAL:
+ case GST_MATROSKA_ID_CODECSTATE:
+ case GST_MATROSKA_ID_SLICES:
case GST_EBML_ID_VOID:
ret = gst_ebml_read_skip (ebml);
break;
}
}
- if (cluster_time != GST_CLOCK_TIME_NONE) {
- if (time < 0 && (-time) > cluster_time)
- lace_time = cluster_time;
- else
- lace_time = cluster_time + time;
- } else {
- lace_time = GST_CLOCK_TIME_NONE;
- }
-
- stream = demux->src[stream_num];
-
- if (referenceblock && readblock && stream->set_discont) {
+ if (referenceblock && readblock && demux->src[stream_num]->set_discont) {
/* When doing seeks or such, we need to restart on key frames or
decoders might choke. */
readblock = FALSE;
if (ret == GST_FLOW_OK && readblock) {
guint64 duration = 0;
+ gint64 lace_time = 0;
+
+ stream = demux->src[stream_num];
+
+ if (cluster_time != GST_CLOCK_TIME_NONE) {
+ /* FIXME: What to do with negative timestamps? Give timestamp 0 or -1?
+ * Drop unless the lace contains timestamp 0? */
+ if (time < 0 && (-time) > cluster_time) {
+ lace_time = 0;
+ } else {
+ if (stream->timecodescale == 1.0)
+ lace_time = (cluster_time + time) * demux->time_scale;
+ else
+ lace_time =
+ gst_util_guint64_to_gdouble ((cluster_time + time) *
+ demux->time_scale) * stream->timecodescale;
+ }
+ } else {
+ lace_time = GST_CLOCK_TIME_NONE;
+ }
+
if (block_duration) {
- duration = block_duration * demux->time_scale;
+ if (stream->timecodescale == 1.0)
+ duration = block_duration * demux->time_scale;
+ else
+ duration =
+ gst_util_gdouble_to_guint64 (gst_util_guint64_to_gdouble
+ (block_duration * demux->time_scale) * stream->timecodescale);
} else if (stream->default_duration) {
duration = stream->default_duration;
}
-
+ /* else duration is diff between timecode of this and next block */
for (n = 0; n < laces; n++) {
GstBuffer *sub;
&sub);
}
- /* FIXME: do all laces have the same lenght? */
+ /* FIXME: do all laces have the same length? the lenght of a lace should
+ * in theory be default_duration as one lace should contain on frame */
if (duration) {
GST_BUFFER_DURATION (sub) = duration / laces;
stream->pos += GST_BUFFER_DURATION (sub);
gst_matroska_demux_parse_cluster (GstMatroskaDemux * demux)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret = GST_FLOW_OK;
+
guint64 cluster_time = GST_CLOCK_TIME_NONE;
+
guint32 id;
while (ret == GST_FLOW_OK) {
guint64 num;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) == GST_FLOW_OK) {
- cluster_time = num * demux->time_scale;
+ cluster_time = num;
}
break;
}
GST_WARNING ("Unknown entry 0x%x in cluster data", id);
/* fall-through */
+ case GST_MATROSKA_ID_POSITION:
+ case GST_MATROSKA_ID_PREVSIZE:
+ case GST_MATROSKA_ID_ENCRYPTEDBLOCK:
+ case GST_MATROSKA_ID_SILENTTRACKS:
case GST_EBML_ID_VOID:
ret = gst_ebml_read_skip (ebml);
break;
gboolean * p_run_loop)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret;
+
guint64 seek_pos = (guint64) - 1;
+
guint32 seek_id = 0;
+
guint32 id;
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
return GST_FLOW_OK;
}
+ /* FIXME: Do this for the other elements except CLUSTERS too.
+ * We can't know that they will come before the CLUSTERS */
switch (seek_id) {
case GST_MATROSKA_ID_CUES:
case GST_MATROSKA_ID_TAGS:
{
guint level_up = demux->level_up;
+
guint64 before_pos, length;
+
GstEbmlLevel *level;
/* remember */
if ((ret =
gst_matroska_demux_parse_index (demux, TRUE)) != GST_FLOW_OK)
return ret;
+ /* FIXME: why is this here? */
if (gst_ebml_read_get_length (ebml) == ebml->offset)
*p_run_loop = FALSE;
else
gst_matroska_demux_parse_metadata (demux,
TRUE)) != GST_FLOW_OK)
return ret;
+ /* FIXME: why is this here? */
if (gst_ebml_read_get_length (ebml) == ebml->offset)
*p_run_loop = FALSE;
else
break;
}
- /* used to be here in 0.8 version, but makes mewmew sample not work */
+ /* FIXME:
+ * used to be here in 0.8 version, but makes mewmew sample not work */
/* if (*p_run_loop == FALSE) break; */
finish:
gboolean * p_run_loop)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret = GST_FLOW_OK;
+
guint32 id;
while (ret == GST_FLOW_OK) {
guint32 id, gboolean * p_run_loop)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret;
switch (id) {
+ /* FIXME: not mandatory... will things break?
+ * Can only happen exactly once, ignore second
+ * occurences! */
/* stream info */
- case GST_MATROSKA_ID_INFO:
+ case GST_MATROSKA_ID_SEGMENTINFO:
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
return ret;
if ((ret = gst_matroska_demux_parse_info (demux)) != GST_FLOW_OK)
return ret;
break;
+ /* FIXME: might happen more than once, second
+ * occurences must be exactly the same so drop them */
/* track info headers */
case GST_MATROSKA_ID_TRACKS:
{
break;
}
+ /* FIXME: can only happen once or never but
+ * for the sake of sanity ignore second occurences */
/* stream index */
case GST_MATROSKA_ID_CUES:
{
break;
}
+ /* FIXME: can be there more than once, why do we have
+ * ->metadata_parsed? */
/* metadata */
case GST_MATROSKA_ID_TAGS:
{
- if (!demux->index_parsed) {
+ if (!demux->metadata_parsed) {
if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
return ret;
if ((ret =
break;
}
+ /* FIXME: must not be there but can happen more than once with
+ * different content */
/* file index (if seekable, seek to Cues/Tags to parse it) */
case GST_MATROSKA_ID_SEEKHEAD:
{
break;
}
+ /* FIXME: must not be there */
case GST_MATROSKA_ID_CLUSTER:
{
if (demux->state != GST_MATROSKA_DEMUX_STATE_DATA) {
+ /* FIXME: Skip first and try to read TRACKS and other things
+ * first, then go back here. */
demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
/* FIXME: different streams might have different lengths! */
/* send initial discont */
break;
}
+ /* TODO: Implement parsing of attachments and push them
+ * through an attachment pad, one for each attachment */
+ /* FIXME: must not be there but can only be once */
+ case GST_MATROSKA_ID_ATTACHMENTS:{
+ GST_INFO ("Attachments elements are not supported yet");
+ if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
+ return ret;
+ break;
+ }
+
+ /* TODO: Implement parsing of chapters */
+ /* FIXME: Must not be there but can only be once */
+ case GST_MATROSKA_ID_CHAPTERS:{
+ GST_INFO ("Chapters elements are not supported yet");
+ if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
+ return ret;
+ break;
+ }
+
default:
GST_WARNING ("Unknown matroska file header ID 0x%x at %"
G_GUINT64_FORMAT, id, GST_EBML_READ (demux)->offset);
gst_matroska_demux_loop_stream (GstMatroskaDemux * demux)
{
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret = GST_FLOW_OK;
+
gboolean run_loop = TRUE;
+
guint32 id;
/* we've found our segment, start reading the different contents in here */
static void
gst_matroska_demux_loop (GstPad * pad)
{
- GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (gst_pad_get_parent (pad));
+ GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (GST_PAD_PARENT (pad));
+
GstEbmlRead *ebml = GST_EBML_READ (demux);
+
GstFlowReturn ret;
/* first, if we're to start, let's actually get starting */
goto pause;
}
- /* all is fine */
- gst_object_unref (demux);
return;
/* ERRORS */
gst_matroska_demux_send_event (demux, gst_event_new_eos ());
}
}
- gst_object_unref (demux);
return;
}
}
gchar ** codec_name)
{
GstMatroskaTrackContext *context = (GstMatroskaTrackContext *) videocontext;
+
GstCaps *caps = NULL;
g_assert (videocontext != NULL);
context->send_xiph_headers = FALSE;
context->send_flac_headers = FALSE;
+ /* TODO: check if we have all codec types from matroska-ids.h
+ * check if we have to do more special things with codec_private
+ *
+ * Add support for
+ * GST_MATROSKA_CODEC_ID_VIDEO_QUICKTIME
+ * GST_MATROSKA_CODEC_ID_VIDEO_SNOW
+ */
+
if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC)) {
gst_riff_strf_vids *vids = NULL;
} else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED)) {
guint32 fourcc = 0;
- /* how nice, this is undocumented... */
switch (videocontext->fourcc) {
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
*codec_name = g_strdup ("Raw planar YUV 4:2:0");
*codec_name = g_strdup ("Raw packed YUV 4:2:2");
fourcc = videocontext->fourcc;
break;
+ case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
+ *codec_name = g_strdup ("Raw packed YUV 4:2:0");
+ fourcc = videocontext->fourcc;
+ break;
+ case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
+ *codec_name = g_strdup ("Raw packed YUV 4:2:2");
+ fourcc = videocontext->fourcc;
+ break;
+ case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
+ *codec_name = g_strdup ("Raw packed YUV 4:4:4 with alpha channel");
+ fourcc = videocontext->fourcc;
+ break;
default:
GST_DEBUG ("Unknown fourcc %" GST_FOURCC_FORMAT,
if (caps != NULL) {
int i;
+
GstStructure *structure;
for (i = 0; i < gst_caps_get_size (caps); i++) {
structure = gst_caps_get_structure (caps, i);
+ /* FIXME: use the real unit here! */
GST_DEBUG ("video size %dx%d, target display size %dx%d (any unit)",
videocontext->pixel_width,
videocontext->pixel_height,
/* pixel width and height are the w and h of the video in pixels */
if (videocontext->pixel_width > 0 && videocontext->pixel_height > 0) {
gint w = videocontext->pixel_width;
+
gint h = videocontext->pixel_height;
gst_structure_set (structure,
gchar ** codec_name)
{
GstMatroskaTrackContext *context = (GstMatroskaTrackContext *) audiocontext;
+
GstCaps *caps = NULL;
g_assert (audiocontext != NULL);
context->send_xiph_headers = FALSE;
context->send_flac_headers = FALSE;
+ /* TODO: check if we have all codec types from matroska-ids.h
+ * check if we have to do more special things with codec_private
+ * check if we need bitdepth in different places too
+ * implement channel position magic
+ * Add support for:
+ * GST_MATROSKA_CODEC_ID_AUDIO_AC3_BSID9
+ * GST_MATROSKA_CODEC_ID_AUDIO_AC3_BSID10
+ * GST_MATROSKA_CODEC_ID_AUDIO_QUICKTIME_QDMC
+ * GST_MATROSKA_CODEC_ID_AUDIO_QUICKTIME_QDM2
+ */
+
if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1) ||
!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2) ||
!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3)) {
audiocontext->bitdepth);
} else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT)) {
caps = gst_caps_new_simple ("audio/x-raw-float",
- "endianness", G_TYPE_INT, G_BYTE_ORDER,
+ "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
"width", G_TYPE_INT, audiocontext->bitdepth, NULL);
*codec_name = g_strdup_printf ("Raw %d-bit floating-point audio",
audiocontext->bitdepth);
caps = gst_riff_create_audio_caps (auds->format, NULL, auds, NULL,
NULL, codec_name);
}
- } else if (g_str_has_prefix (codec_id, "A_AAC")) {
+ } else if (g_str_has_prefix (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_AAC)) {
GstBuffer *priv = NULL;
+
gint mpegversion = -1;
+
gint rate_idx, profile;
+
guint8 *data = NULL;
/* unspecified AAC profile with opaque private codec data */
- if (strcmp (codec_id, "A_AAC") == 0) {
+ if (strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_AAC) == 0) {
if (context->codec_priv_size >= 2) {
guint obj_type, freq_index, explicit_freq_bytes = 0;
- codec_id = GST_MATROSKA_CODEC_ID_AUDIO_MPEG4;
+ codec_id = GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4;
freq_index = (GST_READ_UINT16_BE (context->codec_priv) & 0x780) >> 7;
obj_type = (GST_READ_UINT16_BE (context->codec_priv) & 0xF800) >> 11;
if (freq_index == 15)
} else {
GST_WARNING ("Opaque A_AAC codec ID, but no codec private data");
/* just try this and see what happens ... */
- codec_id = GST_MATROSKA_CODEC_ID_AUDIO_MPEG4;
+ codec_id = GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4;
}
}
GST_BUFFER_SIZE (priv) = 2;
}
- if (!strncmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_MPEG2,
- strlen (GST_MATROSKA_CODEC_ID_AUDIO_MPEG2))) {
+ if (!strncmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2,
+ strlen (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2))) {
mpegversion = 2;
- } else if (!strncmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_MPEG4,
- strlen (GST_MATROSKA_CODEC_ID_AUDIO_MPEG4))) {
+ } else if (!strncmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4,
+ strlen (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4))) {
mpegversion = 4;
if (g_strrstr (codec_id, "SBR")) {
}
static GstCaps *
-gst_matroska_demux_complex_caps (GstMatroskaTrackComplexContext *
- complexcontext, const gchar * codec_id, gpointer data, guint size)
-{
- GstCaps *caps = NULL;
-
- GST_DEBUG ("Unknown complex stream: codec_id='%s'", codec_id);
-
- return caps;
-}
-
-static GstCaps *
gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
subtitlecontext, const gchar * codec_id, gpointer data, guint size)
{
GstCaps *caps = NULL;
/* for backwards compatibility */
- if (!g_ascii_strcasecmp (codec_id, "S_TEXT/ASCII"))
+ if (!g_ascii_strcasecmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_ASCII))
codec_id = GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8;
else if (!g_ascii_strcasecmp (codec_id, "S_SSA"))
codec_id = GST_MATROSKA_CODEC_ID_SUBTITLE_SSA;
else if (!g_ascii_strcasecmp (codec_id, "S_USF"))
codec_id = GST_MATROSKA_CODEC_ID_SUBTITLE_USF;
+ /* TODO: Add GST_MATROSKA_CODEC_ID_SUBTITLE_BMP support
+ * Check if we have to do something with codec_private */
if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8)) {
caps = gst_caps_new_simple ("text/plain", NULL);
subtitlecontext->check_utf8 = TRUE;
GstStateChange transition)
{
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (element);
+
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
/* handle upwards state changes here */
#define GST_IS_MATROSKA_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MATROSKA_DEMUX))
-#define GST_MATROSKA_DEMUX_MAX_STREAMS 64
+/* The spec says that more than 127 stream is discouraged so
+ * take this as a limit for now */
+#define GST_MATROSKA_DEMUX_MAX_STREAMS 127
typedef enum {
GST_MATROSKA_DEMUX_STATE_START,
subtitle_context->invalid_utf8 = FALSE;
return TRUE;
}
-
-gboolean
-gst_matroska_track_init_complex_context (GstMatroskaTrackContext ** p_context)
-{
- GstMatroskaTrackComplexContext *complex_context;
-
- g_assert (p_context != NULL && *p_context != NULL);
-
- /* already set up? (track info might come before track type) */
- if ((*p_context)->type == GST_MATROSKA_TRACK_TYPE_COMPLEX)
- return TRUE;
-
- /* it better not have been set up as some other track type ... */
- if ((*p_context)->type != 0) {
- g_return_val_if_reached (FALSE);
- }
-
- complex_context = g_renew (GstMatroskaTrackComplexContext, *p_context, 1);
- *p_context = (GstMatroskaTrackContext *) complex_context;
- (*p_context)->type = GST_MATROSKA_TRACK_TYPE_COMPLEX;
- return TRUE;
-}
* Matroska element IDs. max. 32-bit.
*/
-/* toplevel segment */
-#define GST_MATROSKA_ID_SEGMENT 0x18538067
-
-/* matroska top-level master IDs */
-#define GST_MATROSKA_ID_INFO 0x1549A966
-#define GST_MATROSKA_ID_TRACKS 0x1654AE6B
-#define GST_MATROSKA_ID_CUES 0x1C53BB6B
-#define GST_MATROSKA_ID_TAGS 0x1254C367
-#define GST_MATROSKA_ID_SEEKHEAD 0x114D9B74
-#define GST_MATROSKA_ID_CLUSTER 0x1F43B675
-
-/* IDs in the info master */
-#define GST_MATROSKA_ID_TIMECODESCALE 0x2AD7B1
-#define GST_MATROSKA_ID_DURATION 0x4489
-#define GST_MATROSKA_ID_WRITINGAPP 0x5741
-#define GST_MATROSKA_ID_MUXINGAPP 0x4D80
-#define GST_MATROSKA_ID_DATEUTC 0x4461
-#define GST_MATROSKA_ID_SEGMENTUID 0x73A4
-
-/* ID in the tracks master */
-#define GST_MATROSKA_ID_TRACKENTRY 0xAE
-
-/* IDs in the trackentry master */
-#define GST_MATROSKA_ID_TRACKNUMBER 0xD7
-#define GST_MATROSKA_ID_TRACKUID 0x73C5
-#define GST_MATROSKA_ID_TRACKTYPE 0x83
-#define GST_MATROSKA_ID_TRACKAUDIO 0xE1
-#define GST_MATROSKA_ID_TRACKVIDEO 0xE0
-#define GST_MATROSKA_ID_CODECID 0x86
-#define GST_MATROSKA_ID_CODECPRIVATE 0x63A2
-#define GST_MATROSKA_ID_CODECNAME 0x258688
-#define GST_MATROSKA_ID_CODECINFOURL 0x3B4040
-#define GST_MATROSKA_ID_CODECDOWNLOADURL 0x26B240
-#define GST_MATROSKA_ID_TRACKNAME 0x536E
-#define GST_MATROSKA_ID_TRACKLANGUAGE 0x22B59C
-#define GST_MATROSKA_ID_TRACKFLAGENABLED 0xB9
-#define GST_MATROSKA_ID_TRACKFLAGDEFAULT 0x88
-#define GST_MATROSKA_ID_TRACKFLAGLACING 0x9C
-#define GST_MATROSKA_ID_TRACKMINCACHE 0x6DE7
-#define GST_MATROSKA_ID_TRACKMAXCACHE 0x6DF8
-#define GST_MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383
-#define GST_MATROSKA_ID_CONTENTENCODINGS 0x6D80
-
-/* IDs in the trackvideo master */
-#define GST_MATROSKA_ID_VIDEOFRAMERATE 0x2383E3
-#define GST_MATROSKA_ID_VIDEODISPLAYWIDTH 0x54B0
-#define GST_MATROSKA_ID_VIDEODISPLAYHEIGHT 0x54BA
-#define GST_MATROSKA_ID_VIDEOPIXELWIDTH 0xB0
-#define GST_MATROSKA_ID_VIDEOPIXELHEIGHT 0xBA
-#define GST_MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A
-#define GST_MATROSKA_ID_VIDEOSTEREOMODE 0x53B9
-#define GST_MATROSKA_ID_VIDEOASPECTRATIO 0x54B3
-#define GST_MATROSKA_ID_VIDEOCOLOURSPACE 0x2EB524
-
-/* IDs in the trackaudio master */
-#define GST_MATROSKA_ID_AUDIOSAMPLINGFREQ 0xB5
-#define GST_MATROSKA_ID_AUDIOBITDEPTH 0x6264
-#define GST_MATROSKA_ID_AUDIOCHANNELS 0x9F
-
-/* ID in the cues master */
-#define GST_MATROSKA_ID_POINTENTRY 0xBB
+/* toplevel Segment */
+#define GST_MATROSKA_ID_SEGMENT 0x18538067
+
+/* matroska top-level master IDs, childs of Segment */
+#define GST_MATROSKA_ID_SEGMENTINFO 0x1549A966
+#define GST_MATROSKA_ID_TRACKS 0x1654AE6B
+#define GST_MATROSKA_ID_CUES 0x1C53BB6B
+#define GST_MATROSKA_ID_TAGS 0x1254C367
+#define GST_MATROSKA_ID_SEEKHEAD 0x114D9B74
+#define GST_MATROSKA_ID_CLUSTER 0x1F43B675
+#define GST_MATROSKA_ID_ATTACHMENTS 0x1941A469
+#define GST_MATROSKA_ID_CHAPTERS 0x1043A770
+
+/* IDs in the SegmentInfo master */
+#define GST_MATROSKA_ID_TIMECODESCALE 0x2AD7B1
+#define GST_MATROSKA_ID_DURATION 0x4489
+#define GST_MATROSKA_ID_WRITINGAPP 0x5741
+#define GST_MATROSKA_ID_MUXINGAPP 0x4D80
+#define GST_MATROSKA_ID_DATEUTC 0x4461
+#define GST_MATROSKA_ID_SEGMENTUID 0x73A4
+#define GST_MATROSKA_ID_SEGMENTFILENAME 0x7384
+#define GST_MATROSKA_ID_PREVUID 0x3CB923
+#define GST_MATROSKA_ID_PREVFILENAME 0x3C83AB
+#define GST_MATROSKA_ID_NEXTUID 0x3EB923
+#define GST_MATROSKA_ID_NEXTFILENAME 0x3E83BB
+#define GST_MATROSKA_ID_TITLE 0x7BA9
+#define GST_MATROSKA_ID_SEGMENTFAMILY 0x4444
+#define GST_MATROSKA_ID_CHAPTERTRANSLATE 0x6924
+
+/* IDs in the ChapterTranslate master */
+#define GST_MATROSKA_ID_CHAPTERTRANSLATEEDITIONUID 0x69FC
+#define GST_MATROSKA_ID_CHAPTERTRANSLATECODEC 0x69BF
+#define GST_MATROSKA_ID_CHAPTERTRANSLATEID 0x69A5
+
+/* ID in the Tracks master */
+#define GST_MATROSKA_ID_TRACKENTRY 0xAE
+
+/* IDs in the TrackEntry master */
+#define GST_MATROSKA_ID_TRACKNUMBER 0xD7
+#define GST_MATROSKA_ID_TRACKUID 0x73C5
+#define GST_MATROSKA_ID_TRACKTYPE 0x83
+#define GST_MATROSKA_ID_TRACKAUDIO 0xE1
+#define GST_MATROSKA_ID_TRACKVIDEO 0xE0
+#define GST_MATROSKA_ID_CONTENTENCODINGS 0x6D80
+#define GST_MATROSKA_ID_CODECID 0x86
+#define GST_MATROSKA_ID_CODECPRIVATE 0x63A2
+#define GST_MATROSKA_ID_CODECNAME 0x258688
+#define GST_MATROSKA_ID_TRACKNAME 0x536E
+#define GST_MATROSKA_ID_TRACKLANGUAGE 0x22B59C
+#define GST_MATROSKA_ID_TRACKFLAGENABLED 0xB9
+#define GST_MATROSKA_ID_TRACKFLAGDEFAULT 0x88
+#define GST_MATROSKA_ID_TRACKFLAGFORCED 0x55AA
+#define GST_MATROSKA_ID_TRACKFLAGLACING 0x9C
+#define GST_MATROSKA_ID_TRACKMINCACHE 0x6DE7
+#define GST_MATROSKA_ID_TRACKMAXCACHE 0x6DF8
+#define GST_MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383
+#define GST_MATROSKA_ID_TRACKTIMECODESCALE 0x23314F
+#define GST_MATROSKA_ID_MAXBLOCKADDITIONID 0x55EE
+#define GST_MATROSKA_ID_TRACKATTACHMENTLINK 0x7446
+#define GST_MATROSKA_ID_TRACKOVERLAY 0x6FAB
+#define GST_MATROSKA_ID_TRACKTRANSLATE 0x6624
+/* semi-draft */
+#define GST_MATROSKA_ID_TRACKOFFSET 0x537F
+/* semi-draft */
+#define GST_MATROSKA_ID_CODECSETTINGS 0x3A9697
+/* semi-draft */
+#define GST_MATROSKA_ID_CODECINFOURL 0x3B4040
+/* semi-draft */
+#define GST_MATROSKA_ID_CODECDOWNLOADURL 0x26B240
+/* semi-draft */
+#define GST_MATROSKA_ID_CODECDECODEALL 0xAA
+
+/* IDs in the TrackTranslate master */
+#define GST_MATROSKA_ID_TRACKTRANSLATEEDITIONUID 0x66FC
+#define GST_MATROSKA_ID_TRACKTRANSLATECODEC 0x66BF
+#define GST_MATROSKA_ID_TRACKTRANSLATETRACKID 0x66A5
+
+
+/* IDs in the TrackVideo master */
+/* NOTE: This one is here only for backward compatibility.
+ * Use _TRACKDEFAULDURATION */
+#define GST_MATROSKA_ID_VIDEOFRAMERATE 0x2383E3
+#define GST_MATROSKA_ID_VIDEODISPLAYWIDTH 0x54B0
+#define GST_MATROSKA_ID_VIDEODISPLAYHEIGHT 0x54BA
+#define GST_MATROSKA_ID_VIDEODISPLAYUNIT 0x54B2
+#define GST_MATROSKA_ID_VIDEOPIXELWIDTH 0xB0
+#define GST_MATROSKA_ID_VIDEOPIXELHEIGHT 0xBA
+#define GST_MATROSKA_ID_VIDEOPIXELCROPBOTTOM 0x54AA
+#define GST_MATROSKA_ID_VIDEOPIXELCROPTOP 0x54BB
+#define GST_MATROSKA_ID_VIDEOPIXELCROPLEFT 0x54CC
+#define GST_MATROSKA_ID_VIDEOPIXELCROPRIGHT 0x54DD
+#define GST_MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A
+/* semi-draft */
+#define GST_MATROSKA_ID_VIDEOSTEREOMODE 0x53B8
+#define GST_MATROSKA_ID_VIDEOASPECTRATIOTYPE 0x54B3
+#define GST_MATROSKA_ID_VIDEOCOLOURSPACE 0x2EB524
+/* semi-draft */
+#define GST_MATROSKA_ID_VIDEOGAMMAVALUE 0x2FB523
+
+/* IDs in the TrackAudio master */
+#define GST_MATROSKA_ID_AUDIOSAMPLINGFREQ 0xB5
+#define GST_MATROSKA_ID_AUDIOBITDEPTH 0x6264
+#define GST_MATROSKA_ID_AUDIOCHANNELS 0x9F
+/* semi-draft */
+#define GST_MATROSKA_ID_AUDIOCHANNELPOSITIONS 0x7D7B
+#define GST_MATROSKA_ID_AUDIOOUTPUTSAMPLINGFREQ 0x78B5
+
+/* IDs in the TrackContentEncoding master */
+#define GST_MATROSKA_ID_CONTENTENCODING 0x6240
+
+/* IDs in the ContentEncoding master */
+#define GST_MATROSKA_ID_CONTENTENCODINGORDER 0x5031
+#define GST_MATROSKA_ID_CONTENTENCODINGSCOPE 0x5032
+#define GST_MATROSKA_ID_CONTENTENCODINGTYPE 0x5033
+#define GST_MATROSKA_ID_CONTENTCOMPRESSION 0x5034
+#define GST_MATROSKA_ID_CONTENTENCRYPTION 0x5035
+
+/* IDs in the ContentCompression master */
+#define GST_MATROSKA_ID_CONTENTCOMPALGO 0x4254
+#define GST_MATROSKA_ID_CONTENTCOMPSETTINGS 0x4255
+
+/* IDs in the ContentEncryption master */
+#define GST_MATROSKA_ID_CONTENTENCALGO 0x47E1
+#define GST_MATROSKA_ID_CONTENTENCKEYID 0x47E2
+#define GST_MATROSKA_ID_CONTENTSIGNATURE 0x47E3
+#define GST_MATROSKA_ID_CONTENTSIGKEYID 0x47E4
+#define GST_MATROSKA_ID_CONTENTSIGALGO 0x47E5
+#define GST_MATROSKA_ID_CONTENTSIGHASHALGO 0x47E6
+
+/* ID in the CUEs master */
+#define GST_MATROSKA_ID_POINTENTRY 0xBB
/* IDs in the pointentry master */
-#define GST_MATROSKA_ID_CUETIME 0xB3
-#define GST_MATROSKA_ID_CUETRACKPOSITION 0xB7
-
-/* IDs in the cuetrackposition master */
-#define GST_MATROSKA_ID_CUETRACK 0xF7
-#define GST_MATROSKA_ID_CUECLUSTERPOSITION 0xF1
-
-/* IDs in the tags master */
-#define GST_MATROSKA_ID_TAG 0x7373
-
-/* in the tag master */
-#define GST_MATROSKA_ID_SIMPLETAG 0x67C8
-
-/* in the simpletag master */
-#define GST_MATROSKA_ID_TAGNAME 0x45A3
-#define GST_MATROSKA_ID_TAGSTRING 0x4487
-
-/* IDs in the seekhead master */
-#define GST_MATROSKA_ID_SEEKENTRY 0x4DBB
-
-/* IDs in the seekpoint master */
-#define GST_MATROSKA_ID_SEEKID 0x53AB
-#define GST_MATROSKA_ID_SEEKPOSITION 0x53AC
-
-/* IDs in the cluster master */
-#define GST_MATROSKA_ID_CLUSTERTIMECODE 0xE7
-#define GST_MATROSKA_ID_BLOCKGROUP 0xA0
-#define GST_MATROSKA_ID_SIMPLEBLOCK 0xA3
-#define GST_MATROSKA_ID_REFERENCEBLOCK 0xFB
-
-/* IDs in the blockgroup master */
-#define GST_MATROSKA_ID_BLOCK 0xA1
-#define GST_MATROSKA_ID_BLOCKDURATION 0x9B
-
-/* IDs in the contentencodings master */
-#define GST_MATROSKA_ID_CONTENTENCODING 0x6240
-
-/* IDS IN THE CONTENTENCODING MASTER */
-#define GST_MATROSKA_ID_CONTENTENCODINGORDER 0X5031
-#define GST_MATROSKA_ID_CONTENTENCODINGSCOPE 0X5032
-#define GST_MATROSKA_ID_CONTENTENCODINGTYPE 0X5033
-#define GST_MATROSKA_ID_CONTENTCOMPRESSION 0X5034
-#define GST_MATROSKA_ID_CONTENTENCRYPTION 0X5035
-
-/* IDS IN THE CONTENTCOMPRESSION MASTER */
-#define GST_MATROSKA_ID_CONTENTCOMPALGO 0X4254
-#define GST_MATROSKA_ID_CONTENTCOMPSETTINGS 0X4255
-
+#define GST_MATROSKA_ID_CUETIME 0xB3
+#define GST_MATROSKA_ID_CUETRACKPOSITIONS 0xB7
+
+/* IDs in the CueTrackPositions master */
+#define GST_MATROSKA_ID_CUETRACK 0xF7
+#define GST_MATROSKA_ID_CUECLUSTERPOSITION 0xF1
+#define GST_MATROSKA_ID_CUEBLOCKNUMBER 0x5378
+/* semi-draft */
+#define GST_MATROSKA_ID_CUECODECSTATE 0xEA
+/* semi-draft */
+#define GST_MATROSKA_ID_CUEREFERENCE 0xDB
+
+/* IDs in the CueReference master */
+/* semi-draft */
+#define GST_MATROSKA_ID_CUEREFTIME 0x96
+/* semi-draft */
+#define GST_MATROSKA_ID_CUEREFCLUSTER 0x97
+/* semi-draft */
+#define GST_MATROSKA_ID_CUEREFNUMBER 0x535F
+/* semi-draft */
+#define GST_MATROSKA_ID_CUEREFCODECSTATE 0xEB
+
+/* IDs in the Tags master */
+#define GST_MATROSKA_ID_TAG 0x7373
+
+/* in the Tag master */
+#define GST_MATROSKA_ID_SIMPLETAG 0x67C8
+#define GST_MATROSKA_ID_TARGETS 0x63C0
+
+/* in the SimpleTag master */
+#define GST_MATROSKA_ID_TAGNAME 0x45A3
+#define GST_MATROSKA_ID_TAGSTRING 0x4487
+#define GST_MATROSKA_ID_TAGLANGUAGE 0x447A
+#define GST_MATROSKA_ID_TAGDEFAULT 0x4484
+#define GST_MATROSKA_ID_TAGBINARY 0x4485
+
+/* in the Targets master */
+#define GST_MATROSKA_ID_TARGETTYPEVALUE 0x68CA
+#define GST_MATROSKA_ID_TARGETTYPE 0x63CA
+#define GST_MATROSKA_ID_TARGETTRACKUID 0x63C5
+#define GST_MATROSKA_ID_TARGETEDITIONUID 0x63C5
+#define GST_MATROSKA_ID_TARGETCHAPTERUID 0x63C4
+#define GST_MATROSKA_ID_TARGETATTACHMENTUID 0x63C6
+
+/* IDs in the SeekHead master */
+#define GST_MATROSKA_ID_SEEKENTRY 0x4DBB
+
+/* IDs in the SeekEntry master */
+#define GST_MATROSKA_ID_SEEKID 0x53AB
+#define GST_MATROSKA_ID_SEEKPOSITION 0x53AC
+
+/* IDs in the Cluster master */
+#define GST_MATROSKA_ID_CLUSTERTIMECODE 0xE7
+#define GST_MATROSKA_ID_BLOCKGROUP 0xA0
+#define GST_MATROSKA_ID_SIMPLEBLOCK 0xA3
+#define GST_MATROSKA_ID_REFERENCEBLOCK 0xFB
+#define GST_MATROSKA_ID_POSITION 0xA7
+#define GST_MATROSKA_ID_PREVSIZE 0xAB
+/* semi-draft */
+#define GST_MATROSKA_ID_ENCRYPTEDBLOCK 0xAF
+#define GST_MATROSKA_ID_SILENTTRACKS 0x5854
+
+/* IDs in the SilentTracks master */
+#define GST_MATROSKA_ID_SILENTTRACKNUMBER 0x58D7
+
+/* IDs in the BlockGroup master */
+#define GST_MATROSKA_ID_BLOCK 0xA1
+#define GST_MATROSKA_ID_BLOCKDURATION 0x9B
+/* semi-draft */
+#define GST_MATROSKA_ID_BLOCKVIRTUAL 0xA2
+#define GST_MATROSKA_ID_REFERENCEBLOCK 0xFB
+#define GST_MATROSKA_ID_BLOCKADDITIONS 0x75A1
+#define GST_MATROSKA_ID_REFERENCEPRIORITY 0xFA
+/* semi-draft */
+#define GST_MATROSKA_ID_REFERENCEVIRTUAL 0xFD
+/* semi-draft */
+#define GST_MATROSKA_ID_CODECSTATE 0xA4
+#define GST_MATROSKA_ID_SLICES 0x8E
+
+/* IDs in the BlockAdditions master */
+#define GST_MATROSKA_ID_BLOCKMORE 0xA6
+
+/* IDs in the BlockMore master */
+#define GST_MATROSKA_ID_BLOCKADDID 0xEE
+#define GST_MATROSKA_ID_BLOCKADDITIONAL 0xA5
+
+/* IDs in the Slices master */
+#define GST_MATROSKA_ID_TIMESLICE 0xE8
+
+/* IDs in the TimeSlice master */
+#define GST_MATROSKA_ID_LACENUMBER 0xCC
+/* semi-draft */
+#define GST_MATROSKA_ID_FRAMENUMBER 0xCD
+/* semi-draft */
+#define GST_MATROSKA_ID_BLOCKADDITIONID 0xCB
+/* semi-draft */
+#define GST_MATROSKA_ID_TIMESLICEDELAY 0xCE
+#define GST_MATROSKA_ID_TIMESLICEDURATION 0xCF
+
+/* IDs in the Attachments master */
+#define GST_MATROSKA_ID_ATTACHEDFILE 0x61A7
+
+/* IDs in the AttachedFile master */
+#define GST_MATROSKA_ID_FILEDESCRIPTION 0x467E
+#define GST_MATROSKA_ID_FILENAME 0x466E
+#define GST_MATROSKA_ID_FILEMIMETYPE 0x4660
+#define GST_MATROSKA_ID_FILEDATA 0x465C
+#define GST_MATROSKA_ID_FILEUID 0x46AE
+/* semi-draft */
+#define GST_MATROSKA_ID_FILEREFERRAL 0x4675
+
+/* IDs in the Chapters master */
+#define GST_MATROSKA_ID_EDITIONENTRY 0x45B9
+
+/* IDs in the EditionEntry master */
+#define GST_MATROSKA_ID_EDITIONUID 0x45BC
+#define GST_MATROSKA_ID_EDITIONFLAGHIDDEN 0x45BD
+#define GST_MATROSKA_ID_EDITIONFLAGDEFAULT 0x45DB
+#define GST_MATROSKA_ID_EDITIONFLAGORDERED 0x45DD
+#define GST_MATROSKA_ID_CHAPTERATOM 0xB6
+
+/* IDs in the ChapterAtom master */
+#define GST_MATROSKA_ID_CHAPTERUID 0x73C4
+#define GST_MATROSKA_ID_CHAPTERTIMESTART 0x91
+#define GST_MATROSKA_ID_CHAPTERTIMESTOP 0x92
+#define GST_MATROSKA_ID_CHAPTERFLAGHIDDEN 0x98
+#define GST_MATROSKA_ID_CHAPTERFLAGENABLED 0x4598
+#define GST_MATROSKA_ID_CHAPTERSEGMENTUID 0x6E67
+#define GST_MATROSKA_ID_CHAPTERSEGMENTEDITIONUID 0x6EBC
+#define GST_MATROSKA_ID_CHAPTERPHYSICALEQUIV 0x63C3
+#define GST_MATROSKA_ID_CHAPTERTRACK 0x8F
+#define GST_MATROSKA_ID_CHAPTERDISPLAY 0x80
+#define GST_MATROSKA_ID_CHAPPROCESS 0x6944
+
+/* IDs in the ChapProcess master */
+#define GST_MATROSKA_ID_CHAPPROCESSCODECID 0x6955
+#define GST_MATROSKA_ID_CHAPPROCESSPRIVATE 0x450D
+#define GST_MATROSKA_ID_CHAPPROCESSCOMMAND 0x6911
+
+/* IDs in the ChapProcessCommand master */
+#define GST_MATROSKA_ID_CHAPPROCESSTIME 0x6922
+#define GST_MATROSKA_ID_CHAPPROCESSDATA 0x6933
+
+/* IDs in the ChapterDisplay master */
+#define GST_MATROSKA_ID_CHAPSTRING 0x85
+#define GST_MATROSKA_ID_CHAPLANGUAGE 0x437C
+#define GST_MATROSKA_ID_CHAPCOUNTRY 0x437E
+
+/* IDs in the ChapterTrack master */
+#define GST_MATROSKA_ID_CHAPTERTRACKNUMBER 0x89
/*
* Matroska Codec IDs. Strings.
#define GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3 "V_MPEG4/MS/V3"
#define GST_MATROSKA_CODEC_ID_VIDEO_MPEG1 "V_MPEG1"
#define GST_MATROSKA_CODEC_ID_VIDEO_MPEG2 "V_MPEG2"
+/* FIXME: not (yet) in the spec! */
#define GST_MATROSKA_CODEC_ID_VIDEO_MJPEG "V_MJPEG"
#define GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1 "V_REAL/RV10"
#define GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2 "V_REAL/RV20"
#define GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3 "V_REAL/RV30"
#define GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4 "V_REAL/RV40"
#define GST_MATROSKA_CODEC_ID_VIDEO_THEORA "V_THEORA"
+#define GST_MATROSKA_CODEC_ID_VIDEO_QUICKTIME "V_QUICKTIME"
+#define GST_MATROSKA_CODEC_ID_VIDEO_SNOW "V_SNOW"
#define GST_MATROSKA_CODEC_ID_VIDEO_DIRAC "V_DIRAC"
-/* TODO: Quicktime */
-
-#define GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1 "A_MPEG/L1"
-#define GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2 "A_MPEG/L2"
-#define GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3 "A_MPEG/L3"
-#define GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE "A_PCM/INT/BIG"
-#define GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE "A_PCM/INT/LIT"
-#define GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT "A_PCM/FLOAT/IEEE"
-#define GST_MATROSKA_CODEC_ID_AUDIO_AC3 "A_AC3"
-#define GST_MATROSKA_CODEC_ID_AUDIO_DTS "A_DTS"
-#define GST_MATROSKA_CODEC_ID_AUDIO_VORBIS "A_VORBIS"
-#define GST_MATROSKA_CODEC_ID_AUDIO_FLAC "A_FLAC"
-#define GST_MATROSKA_CODEC_ID_AUDIO_ACM "A_MS/ACM"
-#define GST_MATROSKA_CODEC_ID_AUDIO_MPEG2 "A_AAC/MPEG2/"
-#define GST_MATROSKA_CODEC_ID_AUDIO_MPEG4 "A_AAC/MPEG4/"
-#define GST_MATROSKA_CODEC_ID_AUDIO_TTA "A_TTA1"
-#define GST_MATROSKA_CODEC_ID_AUDIO_WAVPACK4 "A_WAVPACK4"
-#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4 "A_REAL/28_8"
-#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8 "A_REAL/28_8"
-#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK "A_REAL/COOK"
-#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_SIPR "A_REAL/SIPR"
-#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_RALF "A_REAL/RALF"
-#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_ATRC "A_REAL/ATRC"
-
-/* TODO: AC3-9/10 (?), Musepack, Quicktime */
+#define GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1 "A_MPEG/L1"
+#define GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2 "A_MPEG/L2"
+#define GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3 "A_MPEG/L3"
+#define GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE "A_PCM/INT/BIG"
+#define GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE "A_PCM/INT/LIT"
+#define GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT "A_PCM/FLOAT/IEEE"
+#define GST_MATROSKA_CODEC_ID_AUDIO_AC3 "A_AC3"
+#define GST_MATROSKA_CODEC_ID_AUDIO_AC3_BSID9 "A_AC3/BSID9"
+#define GST_MATROSKA_CODEC_ID_AUDIO_AC3_BSID10 "A_AC3/BSID10"
+#define GST_MATROSKA_CODEC_ID_AUDIO_DTS "A_DTS"
+#define GST_MATROSKA_CODEC_ID_AUDIO_VORBIS "A_VORBIS"
+#define GST_MATROSKA_CODEC_ID_AUDIO_FLAC "A_FLAC"
+#define GST_MATROSKA_CODEC_ID_AUDIO_ACM "A_MS/ACM"
+#define GST_MATROSKA_CODEC_ID_AUDIO_TTA "A_TTA1"
+#define GST_MATROSKA_CODEC_ID_AUDIO_WAVPACK4 "A_WAVPACK4"
+#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4 "A_REAL/14_4"
+#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8 "A_REAL/28_8"
+#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK "A_REAL/COOK"
+#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_SIPR "A_REAL/SIPR"
+#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_RALF "A_REAL/RALF"
+#define GST_MATROSKA_CODEC_ID_AUDIO_REAL_ATRC "A_REAL/ATRC"
+#define GST_MATROSKA_CODEC_ID_AUDIO_AAC "A_AAC"
+#define GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "A_AAC/MPEG2/"
+#define GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "A_AAC/MPEG4/"
+#define GST_MATROSKA_CODEC_ID_AUDIO_QUICKTIME_QDMC "A_QUICKTIME/QDMC"
+#define GST_MATROSKA_CODEC_ID_AUDIO_QUICKTIME_QDM2 "A_QUICKTIME/QDM2"
+/* Undefined for now:
+#define GST_MATROSKA_CODEC_ID_AUDIO_MPC "A_MPC"
+*/
+
+#define GST_MATROSKA_CODEC_ID_SUBTITLE_ASCII "S_TEXT/ASCII"
#define GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8 "S_TEXT/UTF8"
#define GST_MATROSKA_CODEC_ID_SUBTITLE_SSA "S_TEXT/SSA"
#define GST_MATROSKA_CODEC_ID_SUBTITLE_ASS "S_TEXT/ASS"
#define GST_MATROSKA_CODEC_ID_SUBTITLE_USF "S_TEXT/USF"
#define GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB "S_VOBSUB"
+#define GST_MATROSKA_CODEC_ID_SUBTITLE_BMP "S_IMAGE/BMP"
/*
- * Matrodka tags. Strings.
+ * Matroska tags. Strings.
*/
+/* TODO: check for other tags */
+
#define GST_MATROSKA_TAG_ID_TITLE "TITLE"
#define GST_MATROSKA_TAG_ID_AUTHOR "AUTHOR"
#define GST_MATROSKA_TAG_ID_ALBUM "ALBUM"
#define GST_MATROSKA_TAG_ID_COPYRIGHT "COPYRIGHT"
/*
+ * TODO: add this tag & mappings
+ * "URL" -> GST_TAG_LOCATION
+ * "BPS" -> GST_TAG_BITRATE
+ * "BPM" -> GST_TAG_BEATS_PER_MINUTE
+ * "REPLAYGAIN_GAIN" -> GST_TAG_*_GAIN see http://replaygain.hydrogenaudio.org/rg_data_format.html
+ * "REPLAYGAIN_PEAK" -> GST_TAG_*_PEAK see http://replaygain.hydrogenaudio.org/peak_data_format.html
+ * "TERMS_OF_USE" -> GST_TAG_LICENSE
+ * "DATE_RECORDED" -> GST_TAG_DATE
+ * "COMPOSER" -> GST_TAG_COMPOSER
+ * "LEAD_PERFORMER" -> GST_TAG_PERFORMER
+ * "GENRE" -> GST_TAG_GENRE
+ *
+ * "TOTAL_PARTS" -> GST_TAG_TRACK_COUNT depending on target
+ * "PART_NUMBER" -> GST_TAG_TRACK_NUMBER depending on target
+ *
+ * "EMAIL" ->
+ * "ADDRESS" ->
+ * "FAX" -> GST_TAG_CONTACT
+ * "PHONE" ->
+ *
+ * TODO: maybe add custom gstreamer tags for other standard matroska tags
+ */
+
+/*
* Enumerations for various types (mapping from binary
* value to what it actually means).
*/
GST_MATROSKA_TRACK_TYPE_COMPLEX = 0x3,
GST_MATROSKA_TRACK_TYPE_LOGO = 0x10,
GST_MATROSKA_TRACK_TYPE_SUBTITLE = 0x11,
+ GST_MATROSKA_TRACK_TYPE_BUTTONS = 0x12,
GST_MATROSKA_TRACK_TYPE_CONTROL = 0x20,
} GstMatroskaTrackType;
GST_MATROSKA_TRACK_ENABLED = (1<<0),
GST_MATROSKA_TRACK_DEFAULT = (1<<1),
GST_MATROSKA_TRACK_LACING = (1<<2),
+ GST_MATROSKA_TRACK_FORCED = (1<<3),
GST_MATROSKA_TRACK_SHIFT = (1<<16)
} GstMatroskaTrackFlags;
GST_MATROSKA_VIDEOTRACK_INTERLACED = (GST_MATROSKA_TRACK_SHIFT<<0)
} GstMatroskaVideoTrackFlags;
+/* TODO: check if all fields are used */
typedef struct _GstMatroskaTrackContext {
GstPad *pad;
GstCaps *caps;
GstMatroskaTrackFlags flags;
guint64 default_duration;
guint64 pos;
+ gdouble timecodescale;
gboolean set_discont; /* TRUE = set DISCONT flag on next buffer */
guint samplerate, channels, bitdepth;
} GstMatroskaTrackAudioContext;
-typedef struct _GstMatroskaTrackComplexContext {
- GstMatroskaTrackContext parent;
-
- /* nothing special goes here, apparently */
-} GstMatroskaTrackComplexContext;
-
typedef struct _GstMatroskaTrackSubtitleContext {
GstMatroskaTrackContext parent;
gboolean gst_matroska_track_init_video_context (GstMatroskaTrackContext ** p_context);
gboolean gst_matroska_track_init_audio_context (GstMatroskaTrackContext ** p_context);
gboolean gst_matroska_track_init_subtitle_context (GstMatroskaTrackContext ** p_context);
-gboolean gst_matroska_track_init_complex_context (GstMatroskaTrackContext ** p_context);
#endif /* __GST_MATROSKA_IDS_H__ */
* Boston, MA 02111-1307, USA.
*/
+/* TODO: - check everywhere that we don't write invalid values
+ * - make sure timestamps are correctly scaled everywhere
+ */
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
"width = (int) [ 16, 4096 ], " \
"height = (int) [ 16, 4096 ] "
+/* FIXME:
+ * * require codec data, etc as needed
+ */
+
static GstStaticPadTemplate videosink_templ =
GST_STATIC_PAD_TEMPLATE ("video_%d",
GST_PAD_SINK,
/* FIXME:
* * audio/x-raw-float: endianness needs defining.
+ * * require codec data, etc as needed
*/
static GstStaticPadTemplate audiosink_templ =
GST_STATIC_PAD_TEMPLATE ("audio_%d",
gst_matroska_mux_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- static const GstElementDetails gst_matroska_mux_details =
- GST_ELEMENT_DETAILS ("Matroska muxer",
- "Codec/Muxer",
- "Muxes video/audio/subtitle streams into a matroska stream",
- "Ronald Bultje <rbultje@ronald.bitfreak.net>");
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&videosink_templ));
gst_static_pad_template_get (&subtitlesink_templ));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_templ));
- gst_element_class_set_details (element_class, &gst_matroska_mux_details);
+ gst_element_class_set_details_simple (element_class, "Matroska muxer",
+ "Codec/Muxer",
+ "Muxes video/audio/subtitle streams into a matroska stream",
+ "Ronald Bultje <rbultje@ronald.bitfreak.net>");
GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
"Matroska muxer");
gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
{
GObjectClass *gobject_class;
+
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
- gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_matroska_mux_finalize);
+ gobject_class->finalize = gst_matroska_mux_finalize;
gobject_class->get_property = gst_matroska_mux_get_property;
gobject_class->set_property = gst_matroska_mux_set_property;
static void
gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
{
- GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
-
- mux->srcpad =
- gst_pad_new_from_template (gst_element_class_get_pad_template
- (gstelement_class, "src"), "src");
+ mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
gst_matroska_mux_create_uid (void)
{
guint32 uid = 0;
+
GRand *rand = g_rand_new ();
+ /* FIXME: array needs locking or moved into instance structure */
while (!uid) {
guint i;
gst_matroska_mux_reset (GstElement * element)
{
GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
+
GSList *walk;
/* reset EBML write */
/* clean up existing streams */
while ((walk = mux->collect->data) != NULL) {
GstMatroskaPad *collect_pad;
+
GstPad *thepad;
collect_pad = (GstMatroskaPad *) walk->data;
mux->index = NULL;
/* reset timers */
- mux->time_scale = 1000000;
+ mux->time_scale = GST_MSECOND;
mux->duration = 0;
/* reset uid array */
gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
{
GstMatroskaTrackContext *context;
+
GstMatroskaPad *collect_pad;
+
GstMatroskaMux *mux;
+
GstTagList *list;
+
gboolean ret;
mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
{
GstMatroskaTrackContext *context = NULL;
+
GstMatroskaTrackVideoContext *videocontext;
+
GstMatroskaMux *mux;
+
GstMatroskaPad *collect_pad;
+
GstStructure *structure;
+
const gchar *mimetype;
+
gint width, height, pixel_width, pixel_height;
+
gint fps_d, fps_n;
mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
videocontext->eye_mode = GST_MATROSKA_EYE_MODE_MONO;
videocontext->fourcc = 0;
+ /* TODO: - check if we handle all codecs by the spec, i.e. codec private
+ * data and other settings
+ * - add new formats
+ */
+
/* find type */
if (!strcmp (mimetype, "video/x-raw-yuv")) {
context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
|| !strcmp (mimetype, "video/x-dv")
|| !strcmp (mimetype, "video/x-h263")) {
BITMAPINFOHEADER *bih;
+
const GValue *codec_data;
+
gint size = sizeof (BITMAPINFOHEADER);
bih = g_new0 (BITMAPINFOHEADER, 1);
if (codec_data != NULL) {
guint8 *priv_data = NULL;
+
guint priv_data_size = 0;
GstBuffer *codec_data_buf = g_value_peek_pointer (codec_data);
GstMatroskaTrackContext * context, GstBuffer ** p_buf0)
{
GstBuffer *buf[3];
+
GArray *bufarr;
+
guint8 *priv_data;
+
guint i, offset, priv_data_size;
if (streamheader == NULL)
} else {
if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
GstMatroskaTrackAudioContext *audiocontext;
+
guint8 *hdr;
hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
GST_WARNING ("First header not a theora identification header, ignoring");
} else {
GstMatroskaTrackVideoContext *videocontext;
+
guint fps_num, fps_denom, par_num, par_denom;
+
guint8 *hdr;
hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
{
GstMatroskaTrackContext *context = NULL;
+
GstMatroskaTrackAudioContext *audiocontext;
+
GstMatroskaMux *mux;
+
GstMatroskaPad *collect_pad;
+
const gchar *mimetype;
+
gint samplerate = 0, channels = 0;
+
GstStructure *structure;
mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
audiocontext->bitdepth = 0;
context->default_duration = 0;
+ /* TODO: - check if we handle all codecs by the spec, i.e. codec private
+ * data and other settings
+ * - add new formats
+ */
+
if (!strcmp (mimetype, "audio/mpeg")) {
gint mpegversion = 0;
case 1:{
gint layer;
+ /* FIXME: number of samples per frame also depends on the mpegversion
+ * which we don't pass as a caps field
+ */
+
gst_structure_get_int (structure, "layer", &layer);
switch (layer) {
case 1:
break;
}
case 2:
- context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG2 "MAIN");
+ context->codec_id =
+ g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "MAIN");
break;
case 4:
- context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG4 "MAIN");
+ context->codec_id =
+ g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "MAIN");
break;
default:
return FALSE;
return TRUE;
} else if (!strcmp (mimetype, "audio/x-raw-int")) {
gint endianness, width, depth;
+
gboolean signedness;
if (!gst_structure_get_int (structure, "width", &width) ||
static gboolean
gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
{
- /* Consider this as boilerplate code for now. There is
+ /* FIXME:
+ * Consider this as boilerplate code for now. There is
* no single subtitle creation element in GStreamer,
* neither do I know how subtitling works at all. */
GstPadTemplate * templ, const gchar * pad_name)
{
GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
+
GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
+
GstMatroskaPad *collect_pad;
+
GstPad *newpad = NULL;
+
gchar *name = NULL;
+
GstPadSetCapsFunction setcapsfunc = NULL;
+
GstMatroskaTrackContext *context = NULL;
if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
- setcapsfunc = gst_matroska_mux_audio_pad_setcaps;
+ setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
context = (GstMatroskaTrackContext *)
g_new0 (GstMatroskaTrackAudioContext, 1);
context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
context->name = g_strdup ("Audio");
} else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
name = g_strdup_printf ("video_%d", mux->num_v_streams++);
- setcapsfunc = gst_matroska_mux_video_pad_setcaps;
+ setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
context = (GstMatroskaTrackContext *)
g_new0 (GstMatroskaTrackVideoContext, 1);
context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
context->name = g_strdup ("Video");
} else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
- setcapsfunc = gst_matroska_mux_subtitle_pad_setcaps;
+ setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
context = (GstMatroskaTrackContext *)
g_new0 (GstMatroskaTrackSubtitleContext, 1);
context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
g_free (name);
collect_pad = (GstMatroskaPad *)
gst_collect_pads_add_pad (mux->collect, newpad, sizeof (GstMatroskaPad));
+
+ /* TODO: check default values for the context */
context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
collect_pad->track = context;
collect_pad->buffer = NULL;
gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
{
GstMatroskaMux *mux;
+
GSList *walk;
mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
GstCollectData *cdata = (GstCollectData *) walk->data;
+
GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
if (cdata->pad == pad) {
GstMatroskaTrackContext * context)
{
GstEbmlWrite *ebml = mux->ebml_write;
+
guint64 master;
+ /* TODO: check if everything necessary is written and check default values */
+
/* track type goes before the type-specific stuff */
gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
/* FIXME: until we have a nice way of getting the codecname
* out of the caps, I'm not going to enable this. Too much
* (useless, double, boring) work... */
+ /* TODO: Use value from tags if any */
/*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
context->codec_name); */
gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
gst_matroska_mux_start (GstMatroskaMux * mux)
{
GstEbmlWrite *ebml = mux->ebml_write;
- guint32 seekhead_id[] = { GST_MATROSKA_ID_INFO,
+
+ guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
GST_MATROSKA_ID_TRACKS,
GST_MATROSKA_ID_CUES,
GST_MATROSKA_ID_SEEKHEAD,
0
};
guint64 master, child;
+
GSList *collected;
+
int i;
+
guint tracknum = 1;
+
GstClockTime duration = 0;
+
guint32 *segment_uid = (guint32 *) g_malloc (16);
+
GRand *rand = g_rand_new ();
GTimeVal time = { 0, 0 };
/* segment info */
mux->info_pos = ebml->pos;
- master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_INFO);
+ master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
for (i = 0; i < 4; i++) {
segment_uid[i] = g_rand_int (rand);
}
for (collected = mux->collect->data; collected;
collected = g_slist_next (collected)) {
GstMatroskaPad *collect_pad;
+
GstFormat format = GST_FORMAT_TIME;
+
GstPad *thepad;
+
gint64 trackduration;
collect_pad = (GstMatroskaPad *) collected->data;
collected = g_slist_next (collected)) {
GstMatroskaPad *collect_pad;
+
GstPad *thepad;
collect_pad = (GstMatroskaPad *) collected->data;
gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
gpointer data)
{
+ /* TODO: more sensible tag mappings */
struct
{
gchar *matroska_tagname;
GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}
};
GstEbmlWrite *ebml = (GstEbmlWrite *) data;
+
guint i;
+
guint64 simpletag_master;
for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
+
const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
if (strcmp (tagname_gst, tag) == 0) {
gst_matroska_mux_finish (GstMatroskaMux * mux)
{
GstEbmlWrite *ebml = mux->ebml_write;
+
guint64 pos;
+
guint64 duration = 0;
+
GSList *collected;
+
GstTagList *tags;
/* finish last cluster */
/* cues */
if (mux->index != NULL) {
guint n;
+
guint64 master, pointentry_master, trackpos_master;
mux->cues_pos = ebml->pos;
gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
idx->time / mux->time_scale);
trackpos_master = gst_ebml_write_master_start (ebml,
- GST_MATROSKA_ID_CUETRACKPOSITION);
+ GST_MATROSKA_ID_CUETRACKPOSITIONS);
gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
idx->pos - mux->segment_master);
if (mux->meta_index != NULL) {
guint n;
+
guint64 master, seekentry_master;
mux->meta_pos = ebml->pos;
if (tags != NULL) {
guint64 master_tags, master_tag;
+ /* TODO: maybe limit via the TARGETS id by looking at the source pad */
mux->tags_pos = ebml->pos;
master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
for (collected = mux->collect->data; collected;
collected = g_slist_next (collected)) {
GstMatroskaPad *collect_pad;
+
GstClockTime min_duration; /* observed minimum duration */
collect_pad = (GstMatroskaPad *) collected->data;
gst_guint64_to_gdouble (duration) /
gst_guint64_to_gdouble (mux->time_scale));
gst_ebml_write_seek (ebml, pos);
+ } else {
+ /* void'ify */
+ guint64 my_pos = ebml->pos;
+
+ gst_ebml_write_seek (ebml, mux->duration_pos);
+ gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
+ gst_ebml_write_seek (ebml, my_pos);
}
/* finish segment - this also writes element length */
gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
{
GSList *collected;
+
GstMatroskaPad *best = NULL;
*popped = FALSE;
gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
{
GstEbmlWrite *ebml = mux->ebml_write;
+
GstBuffer *buf, *hdr;
+
guint64 cluster, blockgroup;
+
gboolean write_duration;
+
gint16 relative_timestamp;
+
gint64 relative_timestamp64;
+
guint64 block_duration;
+
gboolean is_video_keyframe = FALSE;
/* write data */
/* hm, invalid timestamp (due to --to be fixed--- element upstream);
* this would wreak havoc with time stored in matroska file */
+ /* TODO: maybe calculate a timestamp by using the previous timestamp
+ * and default duration */
if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
GST_WARNING_OBJECT (collect_pad->collect.pad,
"Invalid buffer timestamp; dropping buffer");
/* write the block, for matroska v2 use SimpleBlock if possible
* one slice (*breath*).
- * FIXME: lacing, etc. */
+ * FIXME: Need to do correct lacing! */
relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
if (relative_timestamp64 >= 0) {
/* round the timestamp */
gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
{
GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
+
GstMatroskaPad *best;
+
gboolean popped;
+
GstFlowReturn ret;
GST_DEBUG_OBJECT (mux, "Collected pads");
* the actual duration later when we send an updated header on eos */
if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
+
GstClockTime end_ts = start_ts;
if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret;
+
GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
switch (transition) {
break;
}
g_free (mux->writing_app);
- mux->writing_app = g_strdup (g_value_get_string (value));
+ mux->writing_app = g_value_dup_string (value);
break;
case ARG_MATROSKA_VERSION:
mux->matroska_version = g_value_get_int (value);