From e50039897b9550ff01901069cb17e923760c8600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 11 Mar 2009 19:32:16 +0100 Subject: mxf: Add MXF muxer This muxer currently only supports OP1a and is probably not yet 100% complying to the standards. --- gst/mxf/mxfmux.c | 1327 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1327 insertions(+) create mode 100644 gst/mxf/mxfmux.c (limited to 'gst/mxf/mxfmux.c') diff --git a/gst/mxf/mxfmux.c b/gst/mxf/mxfmux.c new file mode 100644 index 00000000..0cec37ee --- /dev/null +++ b/gst/mxf/mxfmux.c @@ -0,0 +1,1327 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "mxfmux.h" + +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif + +GST_DEBUG_CATEGORY_STATIC (mxfmux_debug); +#define GST_CAT_DEFAULT mxfmux_debug + +static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/mxf") + ); + +enum +{ + PROP_0 +}; + +GST_BOILERPLATE (GstMXFMux, gst_mxf_mux, GstElement, GST_TYPE_ELEMENT); + +static void gst_mxf_mux_finalize (GObject * object); +static void gst_mxf_mux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_mxf_mux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstFlowReturn gst_mxf_mux_collected (GstCollectPads * pads, + gpointer user_data); + +static gboolean gst_mxf_mux_handle_src_event (GstPad * pad, GstEvent * event); +static GstPad *gst_mxf_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name); +static void gst_mxf_mux_release_pad (GstElement * element, GstPad * pad); + +static GstStateChangeReturn +gst_mxf_mux_change_state (GstElement * element, GstStateChange transition); + +static void gst_mxf_mux_reset (GstMXFMux * mux); + +static GstFlowReturn +gst_mxf_mux_push (GstMXFMux * mux, GstBuffer * buf) +{ + guint size = GST_BUFFER_SIZE (buf); + GstFlowReturn ret; + + gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad)); + ret = gst_pad_push (mux->srcpad, buf); + mux->offset += size; + + return ret; +} + +static void +gst_mxf_mux_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + const GstPadTemplate **p; + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_templ)); + + p = mxf_essence_element_writer_get_pad_templates (); + while (p && *p) { + gst_element_class_add_pad_template (element_class, + (GstPadTemplate *) gst_object_ref (GST_OBJECT (*p))); + p++; + } + + gst_element_class_set_details_simple (element_class, "MXF muxer", + "Codec/Muxer", + "Muxes video/audio streams into a MXF stream", + "Sebastian Dröge "); +} + +static void +gst_mxf_mux_class_init (GstMXFMuxClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + GST_DEBUG_CATEGORY_INIT (mxfmux_debug, "mxfmux", 0, "MXF muxer"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_mxf_mux_finalize; + gobject_class->set_property = gst_mxf_mux_set_property; + gobject_class->get_property = gst_mxf_mux_get_property; + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_mxf_mux_change_state); + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_mxf_mux_request_new_pad); + gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_mxf_mux_release_pad); +} + +static void +gst_mxf_mux_init (GstMXFMux * mux, GstMXFMuxClass * g_class) +{ + GstCaps *caps; + + mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); + gst_pad_set_event_function (mux->srcpad, gst_mxf_mux_handle_src_event); + caps = gst_caps_new_simple ("application/mxf", NULL); + gst_pad_set_caps (mux->srcpad, caps); + gst_caps_unref (caps); + gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad); + + mux->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (mux->collect, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_mxf_mux_collected), mux); + + gst_mxf_mux_reset (mux); +} + +static void +gst_mxf_mux_finalize (GObject * object) +{ + GstMXFMux *mux = GST_MXF_MUX (object); + + gst_mxf_mux_reset (mux); + + if (mux->metadata) { + g_hash_table_destroy (mux->metadata); + mux->metadata = NULL; + } + + gst_object_unref (mux->collect); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_mxf_mux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + //GstMXFMux *mux = GST_MXF_MUX (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mxf_mux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + //GstMXFMux *mux = GST_MXF_MUX (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mxf_mux_reset (GstMXFMux * mux) +{ + GSList *sl; + + while ((sl = mux->collect->data) != NULL) { + GstMXFMuxPad *cpad = (GstMXFMuxPad *) sl->data; + + gst_object_unref (cpad->adapter); + g_free (cpad->mapping_data); + + gst_collect_pads_remove_pad (mux->collect, cpad->collect.pad); + } + + mux->state = GST_MXF_MUX_STATE_HEADER; + mux->n_pads = 0; + + if (mux->metadata) { + g_hash_table_destroy (mux->metadata); + mux->preface = NULL; + } + mux->metadata = mxf_metadata_hash_table_new (); + + mxf_partition_pack_reset (&mux->partition); + mxf_primer_pack_reset (&mux->primer); + memset (&mux->min_edit_rate, 0, sizeof (MXFFraction)); + mux->last_gc_timestamp = 0; + mux->last_gc_position = 0; + mux->offset = 0; +} + +static gboolean +gst_mxf_mux_handle_src_event (GstPad * pad, GstEvent * event) +{ + GstEventType type; + + type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; + + switch (type) { + case GST_EVENT_SEEK: + /* disable seeking for now */ + return FALSE; + default: + break; + } + + return gst_pad_event_default (pad, event); +} + +static gboolean +gst_mxf_mux_handle_sink_event (GstPad * pad, GstEvent * event) +{ + GstMXFMux *mux = GST_MXF_MUX (gst_pad_get_parent (pad)); + gboolean ret = TRUE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG: + /* TODO: do something with the tags */ + break; + case GST_EVENT_NEWSEGMENT: + /* We don't support NEWSEGMENT events */ + ret = FALSE; + gst_event_unref (event); + break; + default: + break; + } + + /* now GstCollectPads can take care of the rest, e.g. EOS */ + if (ret) + ret = mux->collect_event (pad, event); + gst_object_unref (mux); + + return ret; +} + +static gboolean +gst_mxf_mux_setcaps (GstPad * pad, GstCaps * caps) +{ + GstMXFMux *mux = GST_MXF_MUX (gst_pad_get_parent (pad)); + GstMXFMuxPad *cpad = (GstMXFMuxPad *) gst_pad_get_element_private (pad); + gboolean ret = TRUE; + MXFUL d_instance_uid = { {0,} }; + MXFMetadataFileDescriptor *old_descriptor = cpad->descriptor; + + if (old_descriptor) { + memcpy (&d_instance_uid, &MXF_METADATA_BASE (old_descriptor)->instance_uid, + 16); + cpad->descriptor = NULL; + g_free (cpad->mapping_data); + cpad->mapping_data = NULL; + } + + cpad->descriptor = + cpad->writer->get_descriptor (gst_pad_get_pad_template (pad), caps, + &cpad->write_func, &cpad->mapping_data); + + if (!cpad->descriptor) { + GST_ERROR_OBJECT (mux, + "Couldn't get descriptor for pad '%s' with caps %" GST_PTR_FORMAT, + GST_PAD_NAME (pad), caps); + gst_object_unref (mux); + return FALSE; + } + + if (mxf_ul_is_zero (&d_instance_uid)) + mxf_ul_set (&d_instance_uid, mux->metadata); + + memcpy (&MXF_METADATA_BASE (cpad->descriptor)->instance_uid, &d_instance_uid, + 16); + + g_hash_table_replace (mux->metadata, + &MXF_METADATA_BASE (cpad->descriptor)->instance_uid, cpad->descriptor); + + if (old_descriptor) { + if (mux->preface && mux->preface->content_storage && + mux->preface->content_storage->packages) { + guint i, j; + + for (i = 0; i < mux->preface->content_storage->n_packages; i++) { + MXFMetadataSourcePackage *package; + + if (!MXF_IS_METADATA_SOURCE_PACKAGE (mux->preface-> + content_storage->packages[i])) + continue; + + package = + MXF_METADATA_SOURCE_PACKAGE (mux->preface-> + content_storage->packages[i]); + + if (!package->descriptor) + continue; + + if (MXF_IS_METADATA_MULTIPLE_DESCRIPTOR (package->descriptor)) { + MXFMetadataMultipleDescriptor *tmp = + MXF_METADATA_MULTIPLE_DESCRIPTOR (package->descriptor); + + for (j = 0; j < tmp->n_sub_descriptors; j++) { + if (tmp->sub_descriptors[j] == + MXF_METADATA_GENERIC_DESCRIPTOR (old_descriptor)) { + tmp->sub_descriptors[j] = + MXF_METADATA_GENERIC_DESCRIPTOR (cpad->descriptor); + memcpy (&tmp->sub_descriptors_uids[j], &d_instance_uid, 16); + } + } + } else if (package->descriptor == + MXF_METADATA_GENERIC_DESCRIPTOR (old_descriptor)) { + package->descriptor = + MXF_METADATA_GENERIC_DESCRIPTOR (cpad->descriptor); + memcpy (&package->descriptor_uid, &d_instance_uid, 16); + } + } + } + } + + gst_object_unref (mux); + + return ret; +} + +static GstPad * +gst_mxf_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * pad_name) +{ + GstMXFMux *mux = GST_MXF_MUX (element); + GstMXFMuxPad *cpad; + GstPad *pad = NULL; + guint pad_number; + gchar *name = NULL; + const MXFEssenceElementWriter *writer; + + if (mux->state != GST_MXF_MUX_STATE_HEADER) { + GST_WARNING_OBJECT (mux, "Can't request pads after writing header"); + return NULL; + } + + writer = mxf_essence_element_writer_find (templ); + if (!writer) { + GST_ERROR_OBJECT (mux, "Not our template"); + return NULL; + } + + pad_number = g_atomic_int_exchange_and_add ((gint *) & mux->n_pads, 1); + name = g_strdup_printf (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ), pad_number); + + pad = gst_pad_new_from_template (templ, name); + g_free (name); + cpad = (GstMXFMuxPad *) + gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstMXFMuxPad)); + cpad->last_timestamp = 0; + cpad->adapter = gst_adapter_new (); + cpad->writer = writer; + + /* FIXME: hacked way to override/extend the event function of + * GstCollectPads; because it sets its own event function giving the + * element no access to events. + */ + mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (pad); + gst_pad_set_event_function (pad, + GST_DEBUG_FUNCPTR (gst_mxf_mux_handle_sink_event)); + + gst_pad_set_setcaps_function (pad, gst_mxf_mux_setcaps); + gst_pad_use_fixed_caps (pad); + gst_pad_set_active (pad, TRUE); + gst_element_add_pad (element, pad); + + return pad; +} + +static void +gst_mxf_mux_release_pad (GstElement * element, GstPad * pad) +{ + GstMXFMux *mux = GST_MXF_MUX (GST_PAD_PARENT (pad)); + GstMXFMuxPad *cpad = (GstMXFMuxPad *) gst_pad_get_element_private (pad); + + gst_object_unref (cpad->adapter); + g_free (cpad->mapping_data); + + gst_collect_pads_remove_pad (mux->collect, pad); + gst_element_remove_pad (element, pad); +} + +static GstFlowReturn +gst_mxf_mux_create_metadata (GstMXFMux * mux) +{ + GstFlowReturn ret = GST_FLOW_OK; + GSList *l; + GArray *tmp; + + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + + if (cpad->writer->update_descriptor) + cpad->writer->update_descriptor (cpad->descriptor, + GST_PAD_CAPS (cpad->collect.pad), cpad->mapping_data, + cpad->collect.buffer); + } + + /* Preface */ + mux->preface = + (MXFMetadataPreface *) gst_mini_object_new (MXF_TYPE_METADATA_PREFACE); + mxf_ul_set (&MXF_METADATA_BASE (mux->preface)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (mux->preface)->instance_uid, mux->preface); + + mxf_timestamp_set_now (&mux->preface->last_modified_date); + mux->preface->version = 258; + mux->preface->object_model_version = 1; + + mxf_op_set_generalized (&mux->preface->operational_pattern, MXF_OP_1a, TRUE, + TRUE, FALSE); + + tmp = g_array_new (FALSE, FALSE, sizeof (MXFUL)); + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + + if (!cpad || !cpad->descriptor || + mxf_ul_is_zero (&cpad->descriptor->essence_container)) + return GST_FLOW_ERROR; + + g_array_append_val (tmp, cpad->descriptor->essence_container); + } + mux->preface->n_essence_containers = tmp->len; + mux->preface->essence_containers = (MXFUL *) g_array_free (tmp, FALSE); + + /* This will later be used as UID for the material package */ + mxf_ul_set (&mux->preface->primary_package_uid, mux->metadata); + + /* Identifications */ + { + MXFMetadataIdentification *identification; + static const guint8 gst_uid[] = { + 0xe5, 0xde, 0xcd, 0x04, 0x24, 0x90, 0x69, 0x18, + 0x8a, 0xc9, 0xb5, 0xd7, 0x02, 0x58, 0x46, 0x78 + }; + guint major, minor, micro, nano; + + mux->preface->n_identifications = 1; + mux->preface->identifications = g_new0 (MXFMetadataIdentification *, 1); + identification = mux->preface->identifications[0] = + (MXFMetadataIdentification *) + gst_mini_object_new (MXF_TYPE_METADATA_IDENTIFICATION); + + mxf_ul_set (&MXF_METADATA_BASE (identification)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (identification)->instance_uid, identification); + + mxf_ul_set (&identification->this_generation_uid, NULL); + + identification->company_name = g_strdup ("GStreamer"); + identification->product_name = g_strdup ("GStreamer Multimedia Framework"); + + gst_version (&major, &minor, µ, &nano); + identification->product_version.major = major; + identification->product_version.minor = minor; + identification->product_version.patch = micro; + identification->product_version.build = nano; + identification->product_version.release = + (nano == 0) ? 1 : (nano == 1) ? 2 : 4; + + identification->version_string = + g_strdup_printf ("%u.%u.%u.%u", major, minor, micro, nano); + memcpy (&identification->product_uid, &gst_uid, 16); + + memcpy (&identification->modification_date, + &mux->preface->last_modified_date, sizeof (MXFTimestamp)); + memcpy (&identification->toolkit_version, &identification->product_version, + sizeof (MXFProductVersion)); + +#ifdef HAVE_SYS_UTSNAME_H + { + struct utsname sys_details; + + if (uname (&sys_details) == 0) { + identification->platform = g_strdup_printf ("%s %s %s", + sys_details.sysname, sys_details.release, sys_details.machine); + } + } +#endif + +#if defined(G_OS_WIN32) + if (identification->platform == NULL) + identification->platform = g_strdup ("Microsoft Windows"); +#elif defined(G_OS_BEOS) + if (identification->platform == NULL) + identification->platform = g_strdup ("BEOS"); +#elif defined(G_OS_UNIX) + if (identification->platform == NULL) + identification->platform = g_strdup ("Unix"); +#endif + } + + /* Content storage */ + { + MXFMetadataContentStorage *cstorage; + guint i; + + cstorage = mux->preface->content_storage = (MXFMetadataContentStorage *) + gst_mini_object_new (MXF_TYPE_METADATA_CONTENT_STORAGE); + mxf_ul_set (&MXF_METADATA_BASE (cstorage)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (cstorage)->instance_uid, cstorage); + + cstorage->n_packages = 2; + cstorage->packages = g_new (MXFMetadataGenericPackage *, 2); + + /* Source package */ + { + MXFMetadataSourcePackage *p; + + cstorage->packages[1] = (MXFMetadataGenericPackage *) + gst_mini_object_new (MXF_TYPE_METADATA_SOURCE_PACKAGE); + mxf_ul_set (&MXF_METADATA_BASE (cstorage->packages[1])->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (cstorage->packages[1])->instance_uid, + cstorage->packages[1]); + p = (MXFMetadataSourcePackage *) cstorage->packages[1]; + + mxf_umid_set (&p->parent.package_uid); + p->parent.name = g_strdup ("Source package"); + memcpy (&p->parent.package_creation_date, + &mux->preface->last_modified_date, sizeof (MXFTimestamp)); + memcpy (&p->parent.package_modified_date, + &mux->preface->last_modified_date, sizeof (MXFTimestamp)); + + p->parent.n_tracks = g_slist_length (mux->collect->data); + p->parent.tracks = g_new (MXFMetadataTrack *, p->parent.n_tracks); + + if (p->parent.n_tracks > 1) { + MXFMetadataMultipleDescriptor *d; + + p->descriptor = (MXFMetadataGenericDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_MULTIPLE_DESCRIPTOR); + d = (MXFMetadataMultipleDescriptor *) p->descriptor; + d->n_sub_descriptors = p->parent.n_tracks; + d->sub_descriptors = + g_new (MXFMetadataGenericDescriptor *, p->parent.n_tracks); + + mxf_ul_set (&MXF_METADATA_BASE (d)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (d)->instance_uid, d); + } + + /* Tracks */ + { + guint n = 0; + + /* Essence tracks */ + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + MXFMetadataTimelineTrack *track; + MXFMetadataSequence *sequence; + MXFMetadataSourceClip *clip; + + p->parent.tracks[n] = (MXFMetadataTrack *) + gst_mini_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK); + track = (MXFMetadataTimelineTrack *) p->parent.tracks[n]; + mxf_ul_set (&MXF_METADATA_BASE (track)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (track)->instance_uid, track); + + track->parent.track_id = n + 1; + track->parent.track_number = + cpad->writer->get_track_number_template (cpad->descriptor, + GST_PAD_CAPS (cpad->collect.pad), cpad->mapping_data); + + cpad->writer->get_edit_rate (cpad->descriptor, + GST_PAD_CAPS (cpad->collect.pad), cpad->mapping_data, + cpad->collect.buffer, p, track, &track->edit_rate); + + sequence = track->parent.sequence = (MXFMetadataSequence *) + gst_mini_object_new (MXF_TYPE_METADATA_SEQUENCE); + mxf_ul_set (&MXF_METADATA_BASE (sequence)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + + memcpy (&sequence->data_definition, &cpad->writer->data_definition, + 16); + + sequence->n_structural_components = 1; + sequence->structural_components = + g_new (MXFMetadataStructuralComponent *, 1); + + clip = (MXFMetadataSourceClip *) + gst_mini_object_new (MXF_TYPE_METADATA_SOURCE_CLIP); + sequence->structural_components[0] = + (MXFMetadataStructuralComponent *) clip; + mxf_ul_set (&MXF_METADATA_BASE (clip)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (clip)->instance_uid, clip); + + memcpy (&clip->parent.data_definition, &sequence->data_definition, + 16); + clip->start_position = 0; + + cpad->source_package = p; + cpad->source_track = track; + cpad->descriptor->linked_track_id = n + 1; + if (p->parent.n_tracks == 1) { + p->descriptor = (MXFMetadataGenericDescriptor *) cpad->descriptor; + } else { + MXF_METADATA_MULTIPLE_DESCRIPTOR (p-> + descriptor)->sub_descriptors[n] = + (MXFMetadataGenericDescriptor *) cpad->descriptor; + } + + n++; + } + } + } + + /* Material package */ + { + MXFMetadataMaterialPackage *p; + MXFFraction min_edit_rate = { 0, 0 }; + gdouble min_edit_rate_d = G_MAXDOUBLE; + + cstorage->packages[0] = (MXFMetadataGenericPackage *) + gst_mini_object_new (MXF_TYPE_METADATA_MATERIAL_PACKAGE); + memcpy (&MXF_METADATA_BASE (cstorage->packages[0])->instance_uid, + &mux->preface->primary_package_uid, 16); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (cstorage->packages[0])->instance_uid, + cstorage->packages[0]); + p = (MXFMetadataMaterialPackage *) cstorage->packages[0]; + + mxf_umid_set (&p->package_uid); + p->name = g_strdup ("Material package"); + memcpy (&p->package_creation_date, &mux->preface->last_modified_date, + sizeof (MXFTimestamp)); + memcpy (&p->package_modified_date, &mux->preface->last_modified_date, + sizeof (MXFTimestamp)); + + p->n_tracks = g_slist_length (mux->collect->data) + 1; + p->tracks = g_new (MXFMetadataTrack *, p->n_tracks); + + /* Tracks */ + { + guint n; + + n = 1; + /* Essence tracks */ + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + MXFMetadataSourcePackage *source_package; + MXFMetadataTimelineTrack *track, *source_track; + MXFMetadataSequence *sequence; + MXFMetadataSourceClip *clip; + + source_package = MXF_METADATA_SOURCE_PACKAGE (cstorage->packages[1]); + source_track = + MXF_METADATA_TIMELINE_TRACK (source_package->parent.tracks[n - + 1]); + + p->tracks[n] = (MXFMetadataTrack *) + gst_mini_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK); + track = (MXFMetadataTimelineTrack *) p->tracks[n]; + mxf_ul_set (&MXF_METADATA_BASE (track)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (track)->instance_uid, track); + + track->parent.track_id = n + 1; + track->parent.track_number = 0; + + cpad->writer->get_edit_rate (cpad->descriptor, + GST_PAD_CAPS (cpad->collect.pad), cpad->mapping_data, + cpad->collect.buffer, source_package, source_track, + &track->edit_rate); + + if (track->edit_rate.n != source_track->edit_rate.n || + track->edit_rate.d != source_track->edit_rate.d) { + memcpy (&source_track->edit_rate, &track->edit_rate, + sizeof (MXFFraction)); + } + + if (min_edit_rate_d > + ((gdouble) track->edit_rate.n) / ((gdouble) track->edit_rate.d)) { + min_edit_rate_d = + ((gdouble) track->edit_rate.n) / ((gdouble) track->edit_rate.d); + memcpy (&min_edit_rate, &track->edit_rate, sizeof (MXFFraction)); + } + + sequence = track->parent.sequence = (MXFMetadataSequence *) + gst_mini_object_new (MXF_TYPE_METADATA_SEQUENCE); + mxf_ul_set (&MXF_METADATA_BASE (sequence)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + + memcpy (&sequence->data_definition, &cpad->writer->data_definition, + 16); + sequence->n_structural_components = 1; + sequence->structural_components = + g_new (MXFMetadataStructuralComponent *, 1); + + clip = (MXFMetadataSourceClip *) + gst_mini_object_new (MXF_TYPE_METADATA_SOURCE_CLIP); + sequence->structural_components[0] = + (MXFMetadataStructuralComponent *) clip; + mxf_ul_set (&MXF_METADATA_BASE (clip)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (clip)->instance_uid, clip); + + memcpy (&clip->parent.data_definition, &sequence->data_definition, + 16); + clip->start_position = 0; + + memcpy (&clip->source_package_id, &cstorage->packages[1]->package_uid, + 32); + clip->source_track_id = n; + + n++; + } + + n = 0; + /* Timecode track */ + { + MXFMetadataTimelineTrack *track; + MXFMetadataSequence *sequence; + MXFMetadataTimecodeComponent *component; + + p->tracks[n] = (MXFMetadataTrack *) + gst_mini_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK); + track = (MXFMetadataTimelineTrack *) p->tracks[n]; + mxf_ul_set (&MXF_METADATA_BASE (track)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (track)->instance_uid, track); + + track->parent.track_id = n + 1; + track->parent.track_number = 0; + track->parent.track_name = g_strdup ("Timecode track"); + /* FIXME: Is this correct? */ + memcpy (&track->edit_rate, &min_edit_rate, sizeof (MXFFraction)); + + sequence = track->parent.sequence = (MXFMetadataSequence *) + gst_mini_object_new (MXF_TYPE_METADATA_SEQUENCE); + mxf_ul_set (&MXF_METADATA_BASE (sequence)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + + memcpy (&sequence->data_definition, + mxf_metadata_track_identifier_get + (MXF_METADATA_TRACK_TIMECODE_12M_INACTIVE), 16); + + sequence->n_structural_components = 1; + sequence->structural_components = + g_new (MXFMetadataStructuralComponent *, 1); + + component = (MXFMetadataTimecodeComponent *) + gst_mini_object_new (MXF_TYPE_METADATA_TIMECODE_COMPONENT); + sequence->structural_components[0] = + (MXFMetadataStructuralComponent *) component; + mxf_ul_set (&MXF_METADATA_BASE (component)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (component)->instance_uid, component); + + memcpy (&component->parent.data_definition, + &sequence->data_definition, 16); + + component->start_timecode = 0; + component->rounded_timecode_base = + (((gdouble) track->edit_rate.n) / ((gdouble) track->edit_rate.d) + + 0.5); + /* TODO: drop frame */ + } + + memcpy (&mux->min_edit_rate, &min_edit_rate, sizeof (MXFFraction)); + } + } + + for (i = 0; i < cstorage->packages[1]->n_tracks; i++) { + MXFMetadataTrack *track = cstorage->packages[1]->tracks[i]; + guint j; + guint32 templ; + guint8 n_type, n; + + if ((track->track_number & 0x00ff00ff) != 0) + continue; + + templ = track->track_number; + n_type = 0; + + for (j = 0; j < cstorage->packages[1]->n_tracks; j++) { + MXFMetadataTrack *tmp = cstorage->packages[1]->tracks[j]; + + if (tmp->track_number == templ) { + n_type++; + } + } + + n = 0; + for (j = 0; j < cstorage->packages[1]->n_tracks; j++) { + MXFMetadataTrack *tmp = cstorage->packages[1]->tracks[j]; + + if (tmp->track_number == templ) { + n++; + tmp->track_number |= (n_type << 16) | (n); + } + } + } + + cstorage->n_essence_container_data = 1; + cstorage->essence_container_data = + g_new (MXFMetadataEssenceContainerData *, 1); + cstorage->essence_container_data[0] = (MXFMetadataEssenceContainerData *) + gst_mini_object_new (MXF_TYPE_METADATA_ESSENCE_CONTAINER_DATA); + mxf_ul_set (&MXF_METADATA_BASE (cstorage-> + essence_container_data[0])->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (cstorage->essence_container_data[0])->instance_uid, + cstorage->essence_container_data[0]); + + cstorage->essence_container_data[0]->linked_package = + MXF_METADATA_SOURCE_PACKAGE (cstorage->packages[1]); + cstorage->essence_container_data[0]->index_sid = 0; + cstorage->essence_container_data[0]->body_sid = 1; + } + + return ret; +} + +static GstFlowReturn +gst_mxf_mux_create_header_partition_pack (GstMXFMux * mux) +{ + GSList *l; + guint i = 0; + + mxf_partition_pack_reset (&mux->partition); + mux->partition.type = MXF_PARTITION_PACK_HEADER; + mux->partition.closed = mux->partition.complete = FALSE; + mux->partition.major_version = 0x0001; + mux->partition.minor_version = 0x0002; + mux->partition.kag_size = 0; + mux->partition.this_partition = 0; + mux->partition.prev_partition = 0; + mux->partition.footer_partition = 0; + mux->partition.header_byte_count = 0; + mux->partition.index_byte_count = 0; + mux->partition.index_sid = 0; + mux->partition.body_offset = 0; + mux->partition.body_sid = 0; + + memcpy (&mux->partition.operational_pattern, + &mux->preface->operational_pattern, 16); + + mux->partition.n_essence_containers = g_slist_length (mux->collect->data); + mux->partition.essence_containers = + g_new0 (MXFUL, mux->partition.n_essence_containers); + + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + guint j; + + for (j = 0; j < i; j++) { + if (mxf_ul_is_equal (&cpad->descriptor->essence_container, + &mux->partition.essence_containers[j])) + continue; + } + + memcpy (&mux->partition.essence_containers[i], + &cpad->descriptor->essence_container, 16); + i++; + } + mux->partition.n_essence_containers = i; + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_mux_write_header_metadata (GstMXFMux * mux) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *buf; + GList *buffers = NULL; +#if GLIB_CHECK_VERSION (2, 16, 0) + GHashTableIter iter; +#else + GList *values; +#endif + MXFMetadataBase *m; + GList *l; + guint64 header_byte_count = 0; + + buf = + mxf_metadata_base_to_buffer (MXF_METADATA_BASE (mux->preface), + &mux->primer); + header_byte_count += GST_BUFFER_SIZE (buf); + buffers = g_list_prepend (buffers, buf); + +#if GLIB_CHECK_VERSION (2, 16, 0) + g_hash_table_iter_init (&iter, mux->metadata); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) & m)) { +#else + values = g_hash_table_get_values (mux->metadata); + for (l = values; l; l = l->next) { + m = l->data; +#endif + buf = mxf_metadata_base_to_buffer (m, &mux->primer); + header_byte_count += GST_BUFFER_SIZE (buf); + buffers = g_list_prepend (buffers, buf); + } + +#if !GLIB_CHECK_VERSION (2, 16, 0) + g_list_free (value); +#endif + + buffers = g_list_reverse (buffers); + buf = mxf_primer_pack_to_buffer (&mux->primer); + header_byte_count += GST_BUFFER_SIZE (buf); + buffers = g_list_prepend (buffers, buf); + + mux->partition.header_byte_count = header_byte_count; + buf = mxf_partition_pack_to_buffer (&mux->partition); + if ((ret = gst_mxf_mux_push (mux, buf)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Failed pushing partition: %s", + gst_flow_get_name (ret)); + g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (buffers); + return ret; + } + + for (l = buffers; l; l = l->next) { + buf = l->data; + l->data = NULL; + if ((ret = gst_mxf_mux_push (mux, buf)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Failed pushing buffer: %s", + gst_flow_get_name (ret)); + g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (buffers); + return ret; + } + } + + g_list_free (buffers); + + return ret; +} + +static const guint8 _gc_essence_element_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x00, + 0x0d, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00 +}; + +static GstFlowReturn +gst_mxf_mux_handle_buffer (GstMXFMux * mux, GstMXFMuxPad * cpad) +{ + GstBuffer *buf = gst_collect_pads_pop (mux->collect, &cpad->collect); + GstBuffer *outbuf = NULL; + GstBuffer *packet; + GstFlowReturn ret = GST_FLOW_OK; + guint8 slen, ber[9]; + + if ((ret = + cpad->write_func (buf, GST_PAD_CAPS (cpad->collect.pad), + cpad->mapping_data, cpad->adapter, &outbuf, + FALSE) != GST_FLOW_OK)) { + GST_ERROR_OBJECT (mux, "Failed handling buffer for track %u", + cpad->source_track->parent.track_id); + return ret; + } + + buf = outbuf; + if (buf == NULL) + return ret; + + slen = mxf_ber_encode_size (GST_BUFFER_SIZE (buf), ber); + packet = gst_buffer_new_and_alloc (16 + slen + GST_BUFFER_SIZE (buf)); + memcpy (GST_BUFFER_DATA (packet), _gc_essence_element_ul, 16); + GST_BUFFER_DATA (packet)[7] = cpad->descriptor->essence_container.u[7]; + GST_WRITE_UINT32_BE (&GST_BUFFER_DATA (packet)[12], + cpad->source_track->parent.track_number); + memcpy (&GST_BUFFER_DATA (packet)[16], ber, slen); + memcpy (&GST_BUFFER_DATA (packet)[16 + slen], GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + gst_buffer_unref (buf); + + if ((ret = gst_mxf_mux_push (mux, packet)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Failed pushing buffer for track %u", + cpad->source_track->parent.track_id); + return ret; + } + + cpad->pos++; + cpad->last_timestamp = + gst_util_uint64_scale (GST_SECOND * cpad->pos, + cpad->source_track->edit_rate.d, cpad->source_track->edit_rate.n); + + return ret; +} + +static GstFlowReturn +gst_mxf_mux_write_body_partition (GstMXFMux * mux) +{ + GstBuffer *buf; + + mux->partition.type = MXF_PARTITION_PACK_BODY; + mux->partition.closed = mux->partition.complete = FALSE; + mux->partition.major_version = 0x0001; + mux->partition.minor_version = 0x0002; + mux->partition.kag_size = 0; + mux->partition.this_partition = mux->offset; + mux->partition.prev_partition = 0; + mux->partition.footer_partition = 0; + mux->partition.header_byte_count = 0; + mux->partition.index_byte_count = 0; + mux->partition.index_sid = 0; + mux->partition.body_offset = 0; + mux->partition.body_sid = + mux->preface->content_storage->essence_container_data[0]->body_sid; + + buf = mxf_partition_pack_to_buffer (&mux->partition); + return gst_mxf_mux_push (mux, buf); +} + +static GstFlowReturn +gst_mxf_mux_handle_eos (GstMXFMux * mux) +{ + GSList *l; + GstBuffer *packet, *buf; + + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + GstFlowReturn ret; + guint i; + + buf = NULL; + cpad->write_func (NULL, GST_PAD_CAPS (cpad->collect.pad), + cpad->mapping_data, cpad->adapter, &buf, TRUE); + + if (buf) { + guint8 slen, ber[9]; + + slen = mxf_ber_encode_size (GST_BUFFER_SIZE (buf), ber); + packet = gst_buffer_new_and_alloc (16 + slen + GST_BUFFER_SIZE (buf)); + memcpy (GST_BUFFER_DATA (packet), _gc_essence_element_ul, 16); + GST_BUFFER_DATA (packet)[7] = cpad->descriptor->essence_container.u[7]; + GST_WRITE_UINT32_BE (&GST_BUFFER_DATA (packet)[12], + cpad->source_track->parent.track_number); + memcpy (&GST_BUFFER_DATA (packet)[16], ber, slen); + memcpy (&GST_BUFFER_DATA (packet)[16 + slen], GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + gst_buffer_unref (buf); + + ret = gst_mxf_mux_push (mux, packet); + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (mux, "Failed handling EOS for track %u", + cpad->source_track->parent.track_id); + } + cpad->pos++; + } + + /* Update durations */ + cpad->source_track->parent.sequence->duration = cpad->pos; + MXF_METADATA_SOURCE_CLIP (cpad->source_track->parent. + sequence->structural_components[0])->parent.duration = cpad->pos; + for (i = 0; i < mux->preface->content_storage->packages[0]->n_tracks; i++) { + MXFMetadataTimelineTrack *track; + + if (!MXF_IS_METADATA_TIMELINE_TRACK (mux->preface-> + content_storage->packages[0]->tracks[i]) + || !MXF_IS_METADATA_SOURCE_CLIP (mux->preface-> + content_storage->packages[0]->tracks[i]->sequence-> + structural_components[0])) + continue; + + track = + MXF_METADATA_TIMELINE_TRACK (mux->preface-> + content_storage->packages[0]->tracks[i]); + if (MXF_METADATA_SOURCE_CLIP (track->parent. + sequence->structural_components[0])->source_track_id == + cpad->source_track->parent.track_id) { + track->parent.sequence->structural_components[0]->duration = cpad->pos; + track->parent.sequence->duration = cpad->pos; + } + } + } + + /* Update timecode track duration */ + { + MXFMetadataTimelineTrack *track = + MXF_METADATA_TIMELINE_TRACK (mux->preface-> + content_storage->packages[0]->tracks[0]); + MXFMetadataSequence *sequence = track->parent.sequence; + MXFMetadataTimecodeComponent *component = + MXF_METADATA_TIMECODE_COMPONENT (sequence->structural_components[0]); + + sequence->duration = mux->last_gc_position; + component->parent.duration = mux->last_gc_position; + } + + { + guint64 body_partition = mux->partition.this_partition; + guint32 body_sid = mux->partition.body_sid; + guint64 footer_partition = mux->offset; + GArray *rip; + GstFlowReturn ret; + MXFRandomIndexPackEntry entry; + + mux->partition.type = MXF_PARTITION_PACK_FOOTER; + mux->partition.closed = TRUE; + mux->partition.complete = TRUE; + mux->partition.major_version = 0x0001; + mux->partition.minor_version = 0x0002; + mux->partition.kag_size = 0; + mux->partition.this_partition = mux->offset; + mux->partition.prev_partition = body_partition; + mux->partition.footer_partition = mux->offset; + mux->partition.header_byte_count = 0; + mux->partition.index_byte_count = 0; + mux->partition.index_sid = 0; + mux->partition.body_offset = 0; + mux->partition.body_sid = 0; + + gst_mxf_mux_write_header_metadata (mux); + + rip = g_array_sized_new (FALSE, FALSE, sizeof (MXFRandomIndexPackEntry), 3); + entry.offset = 0; + entry.body_sid = 0; + g_array_append_val (rip, entry); + entry.offset = body_partition; + entry.body_sid = body_sid; + g_array_append_val (rip, entry); + entry.offset = footer_partition; + entry.body_sid = 0; + g_array_append_val (rip, entry); + + packet = mxf_random_index_pack_to_buffer (rip); + if ((ret = gst_mxf_mux_push (mux, packet)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Failed pushing random index pack"); + } + g_array_free (rip, TRUE); + + /* Rewrite header partition with updated values */ + if (gst_pad_push_event (mux->srcpad, + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, + 0))) { + mux->offset = 0; + mux->partition.type = MXF_PARTITION_PACK_HEADER; + mux->partition.closed = TRUE; + mux->partition.complete = TRUE; + mux->partition.major_version = 0x0001; + mux->partition.minor_version = 0x0002; + mux->partition.kag_size = 0; + mux->partition.this_partition = 0; + mux->partition.prev_partition = footer_partition; + mux->partition.footer_partition = footer_partition; + mux->partition.header_byte_count = 0; + mux->partition.index_byte_count = 0; + mux->partition.index_sid = 0; + mux->partition.body_offset = 0; + mux->partition.body_sid = 0; + + ret = gst_mxf_mux_write_header_metadata (mux); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Rewriting header partition failed"); + return ret; + } + } else { + GST_WARNING_OBJECT (mux, "Can't rewrite header partition"); + } + } + + return GST_FLOW_OK; +} + +static gint +_sort_mux_pads (gconstpointer a, gconstpointer b) +{ + const GstMXFMuxPad *pa = a, *pb = b; + MXFMetadataTrackType ta = + mxf_metadata_track_identifier_parse (&pa->writer->data_definition); + MXFMetadataTrackType tb = + mxf_metadata_track_identifier_parse (&pb->writer->data_definition); + + if (ta != tb) + return ta - tb; + + return pa->source_track->parent.track_number - + pa->source_track->parent.track_number; +} + +static GstFlowReturn +gst_mxf_mux_collected (GstCollectPads * pads, gpointer user_data) +{ + GstMXFMux *mux = GST_MXF_MUX (user_data); + GstMXFMuxPad *best = NULL; + GstFlowReturn ret; + GSList *sl; + gboolean eos = TRUE; + + if (mux->state == GST_MXF_MUX_STATE_HEADER) { + if (mux->collect->data == NULL) { + GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL), + ("No input streams configured")); + return GST_FLOW_ERROR; + } + + if (gst_pad_push_event (mux->srcpad, + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, + 0))) { + if ((ret = gst_mxf_mux_create_metadata (mux)) != GST_FLOW_OK) + return ret; + + if ((ret = gst_mxf_mux_create_header_partition_pack (mux)) != GST_FLOW_OK) + return ret; + + ret = gst_mxf_mux_write_header_metadata (mux); + } else { + ret = GST_FLOW_ERROR; + } + + if (ret != GST_FLOW_OK) + return ret; + + /* Sort pads, we will always write in that order */ + mux->collect->data = g_slist_sort (mux->collect->data, _sort_mux_pads); + + /* Write body partition */ + ret = gst_mxf_mux_write_body_partition (mux); + if (ret != GST_FLOW_OK) + return ret; + mux->state = GST_MXF_MUX_STATE_DATA; + } + + for (sl = mux->collect->data; sl; sl = sl->next) { + GstMXFMuxPad *cpad = sl->data; + GstClockTime next_gc_timestamp = + gst_util_uint64_scale ((mux->last_gc_position + 1) * GST_SECOND, + mux->min_edit_rate.d, mux->min_edit_rate.n); + + if (cpad->collect.abidata.ABI.eos) + continue; + + if (cpad->last_timestamp < next_gc_timestamp) { + eos = FALSE; + best = cpad; + break; + } else if (eos || !sl->next) { + mux->last_gc_position++; + mux->last_gc_timestamp = next_gc_timestamp; + sl = mux->collect->data; + } + eos = FALSE; + } + + if (!eos && best) { + return gst_mxf_mux_handle_buffer (mux, best); + } else if (eos) { + mux->last_gc_position++; + mux->last_gc_timestamp = + gst_util_uint64_scale (mux->last_gc_position * GST_SECOND, + mux->min_edit_rate.d, mux->min_edit_rate.n); + gst_mxf_mux_handle_eos (mux); + gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); + return GST_FLOW_UNEXPECTED; + } else { + return GST_FLOW_OK; + } +} + +static GstStateChangeReturn +gst_mxf_mux_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstMXFMux *mux = GST_MXF_MUX (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_collect_pads_start (mux->collect); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (mux->collect); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_mxf_mux_reset (mux); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} -- cgit v1.2.1