diff --git a/ChangeLog b/ChangeLog index f64a25833f..a26465748a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2004-09-01 Arwed v. Merkatz + + * gst/matroska/ebml-ids.h: + * gst/matroska/ebml-read.c: (gst_ebml_read_date): + * gst/matroska/ebml-write.c: (gst_ebml_write_date): + automatically convert unix time <-> ebml time when reading/writing a date + * gst/matroska/matroska-ids.h: + * gst/matroska/matroska-mux.c: (gst_matroska_mux_create_uid), + (gst_matroska_mux_reset), (gst_matroska_mux_audio_pad_link), + (gst_matroska_mux_track_header), (gst_matroska_mux_start), + (gst_matroska_mux_write_data): + Write track and segment UIDs, write muxing date, write + TRACKDEFAULTDURATION for TTA audio, write BLOCKDURATION if known. + 2004-08-31 Ronald S. Bultje * ext/alsa/gstalsamixer.c: (gst_alsa_mixer_build_list): diff --git a/gst/matroska/ebml-ids.h b/gst/matroska/ebml-ids.h index 329f036cd5..0dd120c34d 100644 --- a/gst/matroska/ebml-ids.h +++ b/gst/matroska/ebml-ids.h @@ -42,6 +42,9 @@ G_BEGIN_DECLS /* general EBML types */ #define GST_EBML_ID_VOID 0xEC +/* EbmlDate offset from the unix epoch in seconds, 2001/01/01 00:00:00 UTC */ +#define GST_EBML_DATE_OFFSET 978307200 + G_END_DECLS #endif /* __GST_EBML_IDS_H__ */ diff --git a/gst/matroska/ebml-read.c b/gst/matroska/ebml-read.c index 00d353dd88..db5d4a5cce 100644 --- a/gst/matroska/ebml-read.c +++ b/gst/matroska/ebml-read.c @@ -591,13 +591,18 @@ gst_ebml_read_utf8 (GstEbmlRead * ebml, guint32 * id, gchar ** str) } /* - * Read the next element as a date (nanoseconds since 1/1/2000). + * Read the next element as a date. + * Returns the seconds since the unix epoch. */ gboolean gst_ebml_read_date (GstEbmlRead * ebml, guint32 * id, gint64 * date) { - return gst_ebml_read_sint (ebml, id, date); + gint64 ebml_date; + gboolean res = gst_ebml_read_sint (ebml, id, &ebml_date); + + *date = (ebml_date / GST_SECOND) + GST_EBML_DATE_OFFSET; + return res; } /* diff --git a/gst/matroska/ebml-write.c b/gst/matroska/ebml-write.c index 30a9c4200d..1ca54ca3ad 100644 --- a/gst/matroska/ebml-write.c +++ b/gst/matroska/ebml-write.c @@ -430,10 +430,11 @@ gst_ebml_write_utf8 (GstEbmlWrite * ebml, guint32 id, const gchar * str) gst_ebml_write_ascii (ebml, id, str); } +/* date should be in seconds since the unix epoch */ void gst_ebml_write_date (GstEbmlWrite * ebml, guint32 id, gint64 date) { - gst_ebml_write_sint (ebml, id, date); + gst_ebml_write_sint (ebml, id, (date - GST_EBML_DATE_OFFSET) * GST_SECOND); } /* diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index ba55f20b53..6c4233850d 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -45,6 +45,7 @@ #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 diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index fc39cb67b1..a37e008e78 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -114,6 +114,8 @@ GST_STATIC_PAD_TEMPLATE ("subtitle_%d", GST_PAD_REQUEST, GST_STATIC_CAPS_ANY); +static GArray *used_uids; + /* gobject magic foo */ static void gst_matroska_mux_base_init (GstMatroskaMuxClass * klass); static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass); @@ -139,6 +141,9 @@ static void gst_matroska_mux_get_property (GObject * object, /* reset muxer */ static void gst_matroska_mux_reset (GstElement * element); +/* uid generation */ +static guint32 gst_matroska_mux_create_uid (); + static GstEbmlWriteClass *parent_class = NULL; /*static guint gst_matroska_mux_signals[LAST_SIGNAL] = { 0 };*/ @@ -242,6 +247,28 @@ gst_matroska_mux_init (GstMatroskaMux * mux) gst_matroska_mux_reset (GST_ELEMENT (mux)); } +static guint32 +gst_matroska_mux_create_uid () +{ + guint32 uid = 0; + GRand *rand = g_rand_new (); + + while (!uid) { + guint i; + + uid = g_rand_int (rand); + for (i = 0; i < used_uids->len; i++) { + if (g_array_index (used_uids, guint32, i) == uid) { + uid = 0; + break; + } + } + g_array_append_val (used_uids, uid); + } + g_free (rand); + return uid; +} + static void gst_matroska_mux_reset (GstElement * element) { @@ -289,6 +316,13 @@ gst_matroska_mux_reset (GstElement * element) /* reset timers */ mux->time_scale = 1000000; mux->duration = 0; + + /* reset uid array */ + if (used_uids) { + g_free (used_uids); + } + /* arbitrary size, 10 should be enough in most cases */ + used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint32), 10); } static GstPadLinkReturn @@ -468,6 +502,7 @@ gst_matroska_mux_audio_pad_link (GstPad * pad, const GstCaps * caps) audiocontext->samplerate = samplerate; audiocontext->channels = channels; audiocontext->bitdepth = 0; + context->default_duration = 0; if (!strcmp (mimetype, "audio/mpeg")) { gint mpegversion = 0; @@ -530,6 +565,9 @@ gst_matroska_mux_audio_pad_link (GstPad * pad, const GstCaps * caps) } else if (!strcmp (mimetype, "audio/x-raw-tta")) { gint width; + /* TTA frame duration */ + context->default_duration = 1.04489795918367346939 * GST_SECOND; + gst_structure_get_int (structure, "width", &width); audiocontext->bitdepth = width; context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA); @@ -610,16 +648,19 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux, gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num); gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type); + gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID, + gst_matroska_mux_create_uid ()); + if (context->default_duration) { + gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION, + context->default_duration); + } + /* type-specific stuff */ switch (context->type) { case GST_MATROSKA_TRACK_TYPE_VIDEO:{ GstMatroskaTrackVideoContext *videocontext = (GstMatroskaTrackVideoContext *) context; - /* framerate, but not in the video part */ - gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION, - context->default_duration); - master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO); gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH, videocontext->pixel_width); @@ -697,6 +738,9 @@ gst_matroska_mux_start (GstMatroskaMux * mux) gint i; guint tracknum = 1; gdouble duration = 0; + guint32 *segment_uid = (guint32 *) g_malloc (16); + GRand *rand = g_rand_new (); + GTimeVal time = { 0, 0 }; /* we start with a EBML header */ gst_ebml_write_header (ebml, "matroska", 1); @@ -723,6 +767,12 @@ gst_matroska_mux_start (GstMatroskaMux * mux) /* segment info */ mux->info_pos = ebml->pos; master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_INFO); + for (i = 0; i < 4; i++) { + segment_uid[i] = g_rand_int (rand); + } + g_free (rand); + gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID, + (guint8 *) segment_uid, 16); gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale); mux->duration_pos = ebml->pos; /* get duration */ @@ -752,8 +802,8 @@ gst_matroska_mux_start (GstMatroskaMux * mux) gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, app); } } - /* FIXME: how do I get this? Automatic? Via tags? */ - /*gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, 0); */ + g_get_current_time (&time); + gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec); gst_ebml_write_master_finish (ebml, master); /* tracks */ @@ -956,6 +1006,14 @@ gst_matroska_mux_write_data (GstMatroskaMux * mux) GST_BUFFER_DATA (hdr)[3] = 0; gst_ebml_write_buffer (ebml, hdr); gst_ebml_write_buffer (ebml, buf); + if (GST_BUFFER_DURATION_IS_VALID (buf)) { + guint64 block_duration = GST_BUFFER_DURATION (buf); + + if (block_duration != mux->sink[i].track->default_duration) { + gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, + block_duration / mux->time_scale); + } + } gst_ebml_write_master_finish (ebml, blockgroup); gst_ebml_write_master_finish (ebml, cluster); }