diff options
Diffstat (limited to 'gst/matroska/matroska-mux.c')
-rw-r--r-- | gst/matroska/matroska-mux.c | 159 |
1 files changed, 135 insertions, 24 deletions
diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index dd3fbd32..d41ba0ad 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -20,6 +20,10 @@ * 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 @@ -55,6 +59,10 @@ static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", "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, @@ -92,6 +100,7 @@ static GstStaticPadTemplate videosink_templ = /* FIXME: * * audio/x-raw-float: endianness needs defining. + * * require codec data, etc as needed */ static GstStaticPadTemplate audiosink_templ = GST_STATIC_PAD_TEMPLATE ("audio_%d", @@ -184,11 +193,6 @@ static void 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)); @@ -198,7 +202,10 @@ gst_matroska_mux_base_init (gpointer g_class) 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"); @@ -208,12 +215,13 @@ static void 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; @@ -246,11 +254,7 @@ gst_matroska_mux_class_init (GstMatroskaMuxClass * klass) 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); @@ -301,8 +305,10 @@ static guint32 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; @@ -357,6 +363,7 @@ static void gst_matroska_mux_reset (GstElement * element) { GstMatroskaMux *mux = GST_MATROSKA_MUX (element); + GSList *walk; /* reset EBML write */ @@ -368,6 +375,7 @@ gst_matroska_mux_reset (GstElement * element) /* clean up existing streams */ while ((walk = mux->collect->data) != NULL) { GstMatroskaPad *collect_pad; + GstPad *thepad; collect_pad = (GstMatroskaPad *) walk->data; @@ -396,7 +404,7 @@ gst_matroska_mux_reset (GstElement * element) mux->index = NULL; /* reset timers */ - mux->time_scale = 1000000; + mux->time_scale = GST_MSECOND; mux->duration = 0; /* reset uid array */ @@ -463,9 +471,13 @@ static gboolean 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)); @@ -514,12 +526,19 @@ static gboolean 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)); @@ -578,6 +597,11 @@ skip_details: 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); @@ -594,7 +618,9 @@ skip_details: || !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); @@ -666,6 +692,7 @@ skip_details: if (codec_data != NULL) { guint8 *priv_data = NULL; + guint priv_data_size = 0; GstBuffer *codec_data_buf = g_value_peek_pointer (codec_data); @@ -735,8 +762,11 @@ xiph3_streamheader_to_codecdata (const GValue * streamheader, GstMatroskaTrackContext * context, GstBuffer ** p_buf0) { GstBuffer *buf[3]; + GArray *bufarr; + guint8 *priv_data; + guint i, offset, priv_data_size; if (streamheader == NULL) @@ -835,6 +865,7 @@ vorbis_streamheader_to_codecdata (const GValue * streamheader, } else { if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) { GstMatroskaTrackAudioContext *audiocontext; + guint8 *hdr; hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4; @@ -865,7 +896,9 @@ theora_streamheader_to_codecdata (const GValue * streamheader, 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; @@ -920,11 +953,17 @@ static gboolean 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)); @@ -949,6 +988,11 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) 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; @@ -957,6 +1001,10 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) 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: @@ -980,10 +1028,12 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) 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; @@ -992,6 +1042,7 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) return TRUE; } else if (!strcmp (mimetype, "audio/x-raw-int")) { gint endianness, width, depth; + gboolean signedness; if (!gst_structure_get_int (structure, "width", &width) || @@ -1078,7 +1129,8 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps) 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. */ @@ -1101,30 +1153,36 @@ gst_matroska_mux_request_new_pad (GstElement * element, 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; @@ -1138,6 +1196,8 @@ gst_matroska_mux_request_new_pad (GstElement * element, 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; @@ -1174,12 +1234,14 @@ static void 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) { @@ -1219,8 +1281,11 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux, 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); @@ -1298,6 +1363,7 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux, /* 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); @@ -1314,7 +1380,8 @@ static void 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, @@ -1322,11 +1389,17 @@ gst_matroska_mux_start (GstMatroskaMux * mux) 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 }; @@ -1354,7 +1427,7 @@ gst_matroska_mux_start (GstMatroskaMux * mux) /* 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); } @@ -1368,8 +1441,11 @@ gst_matroska_mux_start (GstMatroskaMux * mux) 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; @@ -1406,6 +1482,7 @@ gst_matroska_mux_start (GstMatroskaMux * mux) collected = g_slist_next (collected)) { GstMatroskaPad *collect_pad; + GstPad *thepad; collect_pad = (GstMatroskaPad *) collected->data; @@ -1429,6 +1506,7 @@ static void gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag, gpointer data) { + /* TODO: more sensible tag mappings */ struct { gchar *matroska_tagname; @@ -1446,11 +1524,14 @@ gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag, 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) { @@ -1486,9 +1567,13 @@ static void gst_matroska_mux_finish (GstMatroskaMux * mux) { GstEbmlWrite *ebml = mux->ebml_write; + guint64 pos; + guint64 duration = 0; + GSList *collected; + GstTagList *tags; /* finish last cluster */ @@ -1499,6 +1584,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) /* cues */ if (mux->index != NULL) { guint n; + guint64 master, pointentry_master, trackpos_master; mux->cues_pos = ebml->pos; @@ -1513,7 +1599,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) 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); @@ -1527,6 +1613,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) if (mux->meta_index != NULL) { guint n; + guint64 master, seekentry_master; mux->meta_pos = ebml->pos; @@ -1554,6 +1641,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) 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); @@ -1618,6 +1706,7 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) 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; @@ -1650,6 +1739,13 @@ gst_matroska_mux_finish (GstMatroskaMux * mux) 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 */ @@ -1671,6 +1767,7 @@ static GstMatroskaPad * gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped) { GSList *collected; + GstMatroskaPad *best = NULL; *popped = FALSE; @@ -1743,12 +1840,19 @@ static GstFlowReturn 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 */ @@ -1765,6 +1869,8 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad) /* 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"); @@ -1871,7 +1977,7 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad) /* 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 */ @@ -1926,8 +2032,11 @@ static GstFlowReturn 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"); @@ -1965,6 +2074,7 @@ gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data) * 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)) @@ -2001,6 +2111,7 @@ static GstStateChangeReturn gst_matroska_mux_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret; + GstMatroskaMux *mux = GST_MATROSKA_MUX (element); switch (transition) { @@ -2051,7 +2162,7 @@ gst_matroska_mux_set_property (GObject * object, 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); |