gstreamer/gst/mxf/mxfmux.c
Sebastian Dröge cc1feff99f mxfmux: Instead of releasing request pads on stop(), clear them only
Request pads are requested by applications and as such should only be released
by them again. Instead of releasing them when stopping the muxer, just clear
their state so that they can be used again when starting the muxer again.

https://bugzilla.gnome.org/show_bug.cgi?id=763862
2016-03-25 12:54:00 +02:00

1730 lines
55 KiB
C

/* GStreamer
* Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-mxfmux
*
* mxfmux muxes different streams into an MXF file.
*
* <refsect2>
* <title>Example launch line</title>
* |[
* gst-launch-1.0 -v filesrc location=/path/to/audio ! decodebin ! queue ! mxfmux name=m ! filesink location=file.mxf filesrc location=/path/to/video ! decodebin ! queue ! m.
* ]| This pipeline muxes an audio and video file into a single MXF file.
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include <string.h>
#include "mxfmux.h"
#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif
GST_DEBUG_CATEGORY_STATIC (mxfmux_debug);
#define GST_CAT_DEFAULT mxfmux_debug
#define GST_TYPE_MXF_MUX_PAD (gst_mxf_mux_pad_get_type())
#define GST_MXF_MUX_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MXF_MUX_PAD, GstMXFMuxPad))
#define GST_MXF_MUX_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MXF_MUX_PAD, GstMXFMuxPadClass))
#define GST_MXF_MUX_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_MXF_MUX_PAD, GstMXFMuxPadClass))
#define GST_IS_MXF_MUX_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MXF_MUX_PAD))
#define GST_IS_MXF_MUX_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MXF_MUX_PAD))
typedef struct
{
GstAggregatorPad parent;
guint64 pos;
GstClockTime last_timestamp;
MXFMetadataFileDescriptor *descriptor;
GstAdapter *adapter;
gboolean have_complete_edit_unit;
gpointer mapping_data;
const MXFEssenceElementWriter *writer;
MXFEssenceElementWriteFunc write_func;
MXFMetadataSourcePackage *source_package;
MXFMetadataTimelineTrack *source_track;
} GstMXFMuxPad;
typedef struct
{
GstAggregatorPadClass parent_class;
} GstMXFMuxPadClass;
GType gst_mxf_mux_pad_get_type (void);
G_DEFINE_TYPE (GstMXFMuxPad, gst_mxf_mux_pad, GST_TYPE_AGGREGATOR_PAD);
static void
gst_mxf_mux_pad_finalize (GObject * object)
{
GstMXFMuxPad *pad = GST_MXF_MUX_PAD (object);
g_object_unref (pad->adapter);
g_free (pad->mapping_data);
G_OBJECT_CLASS (gst_mxf_mux_pad_parent_class)->finalize (object);
}
static void
gst_mxf_mux_pad_class_init (GstMXFMuxPadClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
object_class->finalize = gst_mxf_mux_pad_finalize;
}
static void
gst_mxf_mux_pad_init (GstMXFMuxPad * pad)
{
}
static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/mxf")
);
enum
{
PROP_0
};
#define gst_mxf_mux_parent_class parent_class
G_DEFINE_TYPE (GstMXFMux, gst_mxf_mux, GST_TYPE_AGGREGATOR);
static void gst_mxf_mux_finalize (GObject * object);
static GstFlowReturn gst_mxf_mux_aggregate (GstAggregator * aggregator,
gboolean timeout);
static gboolean gst_mxf_mux_stop (GstAggregator * aggregator);
static gboolean gst_mxf_mux_src_event (GstAggregator * aggregator,
GstEvent * event);
static gboolean gst_mxf_mux_sink_event (GstAggregator * aggregator,
GstAggregatorPad * aggpad, GstEvent * event);
static GstAggregatorPad *gst_mxf_mux_create_new_pad (GstAggregator * aggregator,
GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
static void gst_mxf_mux_reset (GstMXFMux * mux);
static GstFlowReturn
gst_mxf_mux_push (GstMXFMux * mux, GstBuffer * buf)
{
guint size = gst_buffer_get_size (buf);
GstFlowReturn ret;
ret = gst_aggregator_finish_buffer (GST_AGGREGATOR (mux), buf);
mux->offset += size;
return ret;
}
static void
gst_mxf_mux_class_init (GstMXFMuxClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstAggregatorClass *gstaggregator_class;
const GstPadTemplate **p;
GST_DEBUG_CATEGORY_INIT (mxfmux_debug, "mxfmux", 0, "MXF muxer");
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstaggregator_class = (GstAggregatorClass *) klass;
gobject_class->finalize = gst_mxf_mux_finalize;
gstaggregator_class->create_new_pad =
GST_DEBUG_FUNCPTR (gst_mxf_mux_create_new_pad);
gstaggregator_class->src_event = GST_DEBUG_FUNCPTR (gst_mxf_mux_src_event);
gstaggregator_class->sink_event = GST_DEBUG_FUNCPTR (gst_mxf_mux_sink_event);
gstaggregator_class->stop = GST_DEBUG_FUNCPTR (gst_mxf_mux_stop);
gstaggregator_class->aggregate = GST_DEBUG_FUNCPTR (gst_mxf_mux_aggregate);
gstaggregator_class->sinkpads_type = GST_TYPE_MXF_MUX_PAD;
gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
p = mxf_essence_element_writer_get_pad_templates ();
while (p && *p) {
gst_element_class_add_pad_template (gstelement_class,
(GstPadTemplate *) gst_object_ref (GST_OBJECT (*p)));
p++;
}
gst_element_class_set_static_metadata (gstelement_class, "MXF muxer",
"Codec/Muxer",
"Muxes video/audio streams into a MXF stream",
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
}
static void
gst_mxf_mux_init (GstMXFMux * mux)
{
mux->index_table = g_array_new (FALSE, FALSE, sizeof (MXFIndexTableSegment));
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;
g_list_free (mux->metadata_list);
mux->metadata_list = NULL;
}
if (mux->index_table) {
g_array_free (mux->index_table, TRUE);
mux->index_table = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_mxf_mux_reset (GstMXFMux * mux)
{
GList *l;
GST_OBJECT_LOCK (mux);
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
GstMXFMuxPad *pad = l->data;
gst_adapter_clear (pad->adapter);
g_free (pad->mapping_data);
pad->mapping_data = NULL;
pad->pos = 0;
pad->last_timestamp = 0;
pad->descriptor = NULL;
pad->have_complete_edit_unit = FALSE;
pad->source_package = NULL;
pad->source_track = NULL;
}
GST_OBJECT_UNLOCK (mux);
mux->state = GST_MXF_MUX_STATE_HEADER;
if (mux->metadata) {
g_hash_table_destroy (mux->metadata);
mux->preface = NULL;
g_list_free (mux->metadata_list);
mux->metadata_list = 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;
g_array_set_size (mux->index_table, 0);
}
static gboolean
gst_mxf_mux_src_event (GstAggregator * aggregator, GstEvent * event)
{
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
/* disable seeking for now */
gst_event_unref (event);
return FALSE;
default:
return GST_AGGREGATOR_CLASS (parent_class)->src_event (aggregator, event);
break;
}
g_assert_not_reached ();
}
static gboolean
gst_mxf_mux_set_caps (GstMXFMux * mux, GstMXFMuxPad * pad, GstCaps * caps)
{
gboolean ret = TRUE;
MXFUUID d_instance_uid = { {0,} };
MXFMetadataFileDescriptor *old_descriptor = pad->descriptor;
GList *l;
GST_DEBUG_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps);
if (old_descriptor) {
memcpy (&d_instance_uid, &MXF_METADATA_BASE (old_descriptor)->instance_uid,
16);
pad->descriptor = NULL;
g_free (pad->mapping_data);
pad->mapping_data = NULL;
}
pad->descriptor =
pad->writer->get_descriptor (GST_PAD_PAD_TEMPLATE (pad), caps,
&pad->write_func, &pad->mapping_data);
if (!pad->descriptor) {
GST_ERROR_OBJECT (mux,
"Couldn't get descriptor for pad '%s' with caps %" GST_PTR_FORMAT,
GST_PAD_NAME (pad), caps);
return FALSE;
}
if (mxf_uuid_is_zero (&d_instance_uid))
mxf_uuid_init (&d_instance_uid, mux->metadata);
memcpy (&MXF_METADATA_BASE (pad->descriptor)->instance_uid, &d_instance_uid,
16);
if (old_descriptor) {
for (l = mux->metadata_list; l; l = l->next) {
MXFMetadataBase *tmp = l->data;
if (mxf_uuid_is_equal (&d_instance_uid, &tmp->instance_uid)) {
l->data = pad->descriptor;
break;
}
}
} else {
mux->metadata_list = g_list_prepend (mux->metadata_list, pad->descriptor);
}
g_hash_table_replace (mux->metadata,
&MXF_METADATA_BASE (pad->descriptor)->instance_uid, pad->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 (pad->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 (pad->descriptor);
memcpy (&package->descriptor_uid, &d_instance_uid, 16);
}
}
}
}
return ret;
}
static gboolean
gst_mxf_mux_sink_event (GstAggregator * aggregator,
GstAggregatorPad * aggpad, GstEvent * event)
{
GstMXFMux *mux = GST_MXF_MUX (aggregator);
gboolean ret = TRUE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_TAG:
/* TODO: do something with the tags */
break;
case GST_EVENT_CAPS:{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
ret = gst_mxf_mux_set_caps (mux, GST_MXF_MUX_PAD (aggpad), caps);
break;
}
default:
break;
}
/* now GstAggregator can take care of the rest, e.g. EOS */
if (ret)
ret =
GST_AGGREGATOR_CLASS (parent_class)->sink_event (aggregator, aggpad,
event);
return ret;
}
static char *
gst_mxf_mux_create_pad_name (GstPadTemplate * templ, guint id)
{
GString *string;
string = g_string_new (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ));
g_string_truncate (string, string->len - 2);
g_string_append_printf (string, "%u", id);
return g_string_free (string, FALSE);
}
static GstAggregatorPad *
gst_mxf_mux_create_new_pad (GstAggregator * aggregator,
GstPadTemplate * templ, const gchar * pad_name, const GstCaps * caps)
{
GstMXFMux *mux = GST_MXF_MUX (aggregator);
GstMXFMuxPad *pad;
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_add ((gint *) & mux->n_pads, 1);
name = gst_mxf_mux_create_pad_name (templ, pad_number);
GST_DEBUG_OBJECT (mux, "Creating pad '%s'", name);
pad =
g_object_new (GST_TYPE_MXF_MUX_PAD, "name", name, "direction",
GST_PAD_SINK, "template", templ, NULL);
g_free (name);
pad->last_timestamp = 0;
pad->adapter = gst_adapter_new ();
pad->writer = writer;
gst_pad_use_fixed_caps (GST_PAD_CAST (pad));
return GST_AGGREGATOR_PAD (pad);
}
static GstFlowReturn
gst_mxf_mux_create_metadata (GstMXFMux * mux)
{
GstFlowReturn ret = GST_FLOW_OK;
GList *l;
GArray *tmp;
GST_DEBUG_OBJECT (mux, "Creating MXF metadata");
GST_OBJECT_LOCK (mux);
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
GstMXFMuxPad *pad = l->data;
GstCaps *caps;
GstBuffer *buffer;
if (!pad || !pad->descriptor) {
GST_OBJECT_UNLOCK (mux);
return GST_FLOW_ERROR;
}
caps = gst_pad_get_current_caps (GST_PAD_CAST (pad));
if (!caps) {
GST_OBJECT_UNLOCK (mux);
return GST_FLOW_ERROR;
}
buffer = gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
if (pad->writer->update_descriptor)
pad->writer->update_descriptor (pad->descriptor,
caps, pad->mapping_data, buffer);
if (buffer)
gst_buffer_unref (buffer);
gst_caps_unref (caps);
}
/* Preface */
mux->preface =
(MXFMetadataPreface *) g_object_new (MXF_TYPE_METADATA_PREFACE, NULL);
mxf_uuid_init (&MXF_METADATA_BASE (mux->preface)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (mux->preface)->instance_uid, mux->preface);
mux->metadata_list = g_list_prepend (mux->metadata_list, 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 = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
GstMXFMuxPad *pad = l->data;
guint i;
gboolean found = FALSE;
if (!pad || !pad->descriptor ||
mxf_ul_is_zero (&pad->descriptor->essence_container)) {
GST_OBJECT_UNLOCK (mux);
return GST_FLOW_ERROR;
}
for (i = 0; i < tmp->len; i++) {
if (mxf_ul_is_equal (&pad->descriptor->essence_container,
&g_array_index (tmp, MXFUL, i))) {
found = TRUE;
break;
}
}
if (found)
continue;
g_array_append_val (tmp, pad->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_uuid_init (&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 *)
g_object_new (MXF_TYPE_METADATA_IDENTIFICATION, NULL);
mxf_uuid_init (&MXF_METADATA_BASE (identification)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (identification)->instance_uid, identification);
mux->metadata_list = g_list_prepend (mux->metadata_list, identification);
mxf_uuid_init (&identification->this_generation_uid, NULL);
identification->company_name = g_strdup ("GStreamer");
identification->product_name = g_strdup ("GStreamer Multimedia Framework");
gst_version (&major, &minor, &micro, &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 *)
g_object_new (MXF_TYPE_METADATA_CONTENT_STORAGE, NULL);
mxf_uuid_init (&MXF_METADATA_BASE (cstorage)->instance_uid, mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (cstorage)->instance_uid, cstorage);
mux->metadata_list = g_list_prepend (mux->metadata_list, cstorage);
cstorage->n_packages = 2;
cstorage->packages = g_new0 (MXFMetadataGenericPackage *, 2);
/* Source package */
{
MXFMetadataSourcePackage *p;
cstorage->packages[1] = (MXFMetadataGenericPackage *)
g_object_new (MXF_TYPE_METADATA_SOURCE_PACKAGE, NULL);
mxf_uuid_init (&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]);
mux->metadata_list =
g_list_prepend (mux->metadata_list, cstorage->packages[1]);
p = (MXFMetadataSourcePackage *) cstorage->packages[1];
mxf_umid_init (&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 = GST_ELEMENT_CAST (mux)->numsinkpads + 1;
p->parent.tracks = g_new0 (MXFMetadataTrack *, p->parent.n_tracks);
if (p->parent.n_tracks > 2) {
MXFMetadataMultipleDescriptor *d;
p->descriptor = (MXFMetadataGenericDescriptor *)
g_object_new (MXF_TYPE_METADATA_MULTIPLE_DESCRIPTOR, NULL);
d = (MXFMetadataMultipleDescriptor *) p->descriptor;
d->n_sub_descriptors = p->parent.n_tracks - 1;
d->sub_descriptors =
g_new0 (MXFMetadataGenericDescriptor *, p->parent.n_tracks - 1);
mxf_uuid_init (&MXF_METADATA_BASE (d)->instance_uid, mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (d)->instance_uid, d);
mux->metadata_list = g_list_prepend (mux->metadata_list, d);
}
/* Tracks */
{
guint n;
n = 1;
/* Essence tracks */
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
GstMXFMuxPad *pad = l->data;
MXFMetadataTimelineTrack *track;
MXFMetadataSequence *sequence;
MXFMetadataSourceClip *clip;
GstCaps *caps;
GstBuffer *buffer;
p->parent.tracks[n] = (MXFMetadataTrack *)
g_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK, NULL);
track = (MXFMetadataTimelineTrack *) p->parent.tracks[n];
mxf_uuid_init (&MXF_METADATA_BASE (track)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (track)->instance_uid, track);
mux->metadata_list = g_list_prepend (mux->metadata_list, track);
caps = gst_pad_get_current_caps (GST_PAD_CAST (pad));
buffer = gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
track->parent.track_id = n + 1;
track->parent.track_number =
pad->writer->get_track_number_template (pad->descriptor,
caps, pad->mapping_data);
/* FIXME: All tracks in a source package must have the same edit
* rate! This means that if we have different edit rates, we need to
* make them different source packages and essence containers with
* a different BodySID */
pad->writer->get_edit_rate (pad->descriptor,
caps, pad->mapping_data, buffer, p, track, &track->edit_rate);
if (buffer)
gst_buffer_unref (buffer);
gst_caps_unref (caps);
sequence = track->parent.sequence = (MXFMetadataSequence *)
g_object_new (MXF_TYPE_METADATA_SEQUENCE, NULL);
mxf_uuid_init (&MXF_METADATA_BASE (sequence)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (sequence)->instance_uid, sequence);
mux->metadata_list = g_list_prepend (mux->metadata_list, sequence);
memcpy (&sequence->data_definition, &pad->writer->data_definition,
16);
sequence->n_structural_components = 1;
sequence->structural_components =
g_new0 (MXFMetadataStructuralComponent *, 1);
clip = (MXFMetadataSourceClip *)
g_object_new (MXF_TYPE_METADATA_SOURCE_CLIP, NULL);
sequence->structural_components[0] =
(MXFMetadataStructuralComponent *) clip;
mxf_uuid_init (&MXF_METADATA_BASE (clip)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (clip)->instance_uid, clip);
mux->metadata_list = g_list_prepend (mux->metadata_list, clip);
memcpy (&clip->parent.data_definition, &sequence->data_definition,
16);
clip->start_position = 0;
pad->source_package = p;
pad->source_track = track;
pad->descriptor->linked_track_id = n + 1;
if (p->parent.n_tracks == 2) {
p->descriptor = (MXFMetadataGenericDescriptor *) pad->descriptor;
} else {
MXF_METADATA_MULTIPLE_DESCRIPTOR (p->
descriptor)->sub_descriptors[n - 1] =
(MXFMetadataGenericDescriptor *) pad->descriptor;
}
n++;
}
}
}
/* Material package */
{
MXFMetadataMaterialPackage *p;
MXFFraction min_edit_rate = { 0, 0 };
gdouble min_edit_rate_d = G_MAXDOUBLE;
cstorage->packages[0] = (MXFMetadataGenericPackage *)
g_object_new (MXF_TYPE_METADATA_MATERIAL_PACKAGE, NULL);
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]);
mux->metadata_list =
g_list_prepend (mux->metadata_list, cstorage->packages[0]);
p = (MXFMetadataMaterialPackage *) cstorage->packages[0];
mxf_umid_init (&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 = GST_ELEMENT_CAST (mux)->numsinkpads + 1;
p->tracks = g_new0 (MXFMetadataTrack *, p->n_tracks);
/* Tracks */
{
guint n;
n = 1;
/* Essence tracks */
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
GstMXFMuxPad *pad = l->data;
GstCaps *caps;
GstBuffer *buffer;
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]);
p->tracks[n] = (MXFMetadataTrack *)
g_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK, NULL);
track = (MXFMetadataTimelineTrack *) p->tracks[n];
mxf_uuid_init (&MXF_METADATA_BASE (track)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (track)->instance_uid, track);
mux->metadata_list = g_list_prepend (mux->metadata_list, track);
track->parent.track_id = n + 1;
track->parent.track_number = 0;
caps = gst_pad_get_current_caps (GST_PAD_CAST (pad));
buffer = gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
pad->writer->get_edit_rate (pad->descriptor,
caps, pad->mapping_data,
buffer, source_package, source_track, &track->edit_rate);
if (buffer)
gst_buffer_unref (buffer);
gst_caps_unref (caps);
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 (track->edit_rate.d <= 0 || track->edit_rate.n <= 0) {
GST_ERROR_OBJECT (mux, "Invalid edit rate");
GST_OBJECT_UNLOCK (mux);
return GST_FLOW_ERROR;
}
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 *)
g_object_new (MXF_TYPE_METADATA_SEQUENCE, NULL);
mxf_uuid_init (&MXF_METADATA_BASE (sequence)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (sequence)->instance_uid, sequence);
mux->metadata_list = g_list_prepend (mux->metadata_list, sequence);
memcpy (&sequence->data_definition, &pad->writer->data_definition,
16);
sequence->n_structural_components = 1;
sequence->structural_components =
g_new0 (MXFMetadataStructuralComponent *, 1);
clip = (MXFMetadataSourceClip *)
g_object_new (MXF_TYPE_METADATA_SOURCE_CLIP, NULL);
sequence->structural_components[0] =
(MXFMetadataStructuralComponent *) clip;
mxf_uuid_init (&MXF_METADATA_BASE (clip)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (clip)->instance_uid, clip);
mux->metadata_list = g_list_prepend (mux->metadata_list, 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 + 1;
n++;
}
n = 0;
/* Timecode track */
{
MXFMetadataTimelineTrack *track;
MXFMetadataSequence *sequence;
MXFMetadataTimecodeComponent *component;
p->tracks[n] = (MXFMetadataTrack *)
g_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK, NULL);
track = (MXFMetadataTimelineTrack *) p->tracks[n];
mxf_uuid_init (&MXF_METADATA_BASE (track)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (track)->instance_uid, track);
mux->metadata_list = g_list_prepend (mux->metadata_list, 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 *)
g_object_new (MXF_TYPE_METADATA_SEQUENCE, NULL);
mxf_uuid_init (&MXF_METADATA_BASE (sequence)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (sequence)->instance_uid, sequence);
mux->metadata_list = g_list_prepend (mux->metadata_list, 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_new0 (MXFMetadataStructuralComponent *, 1);
component = (MXFMetadataTimecodeComponent *)
g_object_new (MXF_TYPE_METADATA_TIMECODE_COMPONENT, NULL);
sequence->structural_components[0] =
(MXFMetadataStructuralComponent *) component;
mxf_uuid_init (&MXF_METADATA_BASE (component)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (component)->instance_uid, component);
mux->metadata_list = g_list_prepend (mux->metadata_list, component);
memcpy (&component->parent.data_definition,
&sequence->data_definition, 16);
component->start_timecode = 0;
if (track->edit_rate.d == 0)
component->rounded_timecode_base = 1;
else
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));
}
}
/* Timecode track */
{
MXFMetadataSourcePackage *p;
MXFMetadataTimelineTrack *track;
MXFMetadataSequence *sequence;
MXFMetadataTimecodeComponent *component;
guint n = 0;
p = (MXFMetadataSourcePackage *) cstorage->packages[1];
p->parent.tracks[n] = (MXFMetadataTrack *)
g_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK, NULL);
track = (MXFMetadataTimelineTrack *) p->parent.tracks[n];
mxf_uuid_init (&MXF_METADATA_BASE (track)->instance_uid, mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (track)->instance_uid, track);
mux->metadata_list = g_list_prepend (mux->metadata_list, 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, &mux->min_edit_rate, sizeof (MXFFraction));
sequence = track->parent.sequence = (MXFMetadataSequence *)
g_object_new (MXF_TYPE_METADATA_SEQUENCE, NULL);
mxf_uuid_init (&MXF_METADATA_BASE (sequence)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (sequence)->instance_uid, sequence);
mux->metadata_list = g_list_prepend (mux->metadata_list, 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_new0 (MXFMetadataStructuralComponent *, 1);
component = (MXFMetadataTimecodeComponent *)
g_object_new (MXF_TYPE_METADATA_TIMECODE_COMPONENT, NULL);
sequence->structural_components[0] =
(MXFMetadataStructuralComponent *) component;
mxf_uuid_init (&MXF_METADATA_BASE (component)->instance_uid,
mux->metadata);
g_hash_table_insert (mux->metadata,
&MXF_METADATA_BASE (component)->instance_uid, component);
mux->metadata_list = g_list_prepend (mux->metadata_list, component);
memcpy (&component->parent.data_definition,
&sequence->data_definition, 16);
component->start_timecode = 0;
if (track->edit_rate.d == 0)
component->rounded_timecode_base = 1;
else
component->rounded_timecode_base =
(((gdouble) track->edit_rate.n) /
((gdouble) track->edit_rate.d) + 0.5);
/* TODO: drop frame */
}
for (i = 1; 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 = 1; 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 = 1; 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_new0 (MXFMetadataEssenceContainerData *, 1);
cstorage->essence_container_data[0] = (MXFMetadataEssenceContainerData *)
g_object_new (MXF_TYPE_METADATA_ESSENCE_CONTAINER_DATA, NULL);
mxf_uuid_init (&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]);
mux->metadata_list =
g_list_prepend (mux->metadata_list,
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 = 2;
cstorage->essence_container_data[0]->body_sid = 1;
}
/* Sort descriptors at the correct places */
{
GList *l;
GList *descriptors = NULL;
for (l = mux->metadata_list; l; l = l->next) {
MXFMetadataBase *m = l->data;
if (MXF_IS_METADATA_GENERIC_DESCRIPTOR (m)
&& !MXF_IS_METADATA_MULTIPLE_DESCRIPTOR (m)) {
descriptors = l;
l->prev->next = NULL;
l->prev = NULL;
break;
}
}
g_assert (descriptors != NULL);
for (l = mux->metadata_list; l; l = l->next) {
MXFMetadataBase *m = l->data;
GList *s;
if (MXF_IS_METADATA_MULTIPLE_DESCRIPTOR (m) ||
MXF_IS_METADATA_SOURCE_PACKAGE (m)) {
s = l->prev;
l->prev = g_list_last (descriptors);
s->next = descriptors;
descriptors->prev = s;
l->prev->next = l;
break;
}
}
}
GST_OBJECT_UNLOCK (mux);
mux->metadata_list = g_list_reverse (mux->metadata_list);
return ret;
}
static GstFlowReturn
gst_mxf_mux_init_partition_pack (GstMXFMux * mux)
{
GList *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 = 1;
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);
GST_OBJECT_LOCK (mux);
mux->partition.n_essence_containers = GST_ELEMENT_CAST (mux)->numsinkpads;
mux->partition.essence_containers =
g_new0 (MXFUL, mux->partition.n_essence_containers);
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
GstMXFMuxPad *pad = l->data;
guint j;
gboolean found = FALSE;
for (j = 0; j <= i; j++) {
if (mxf_ul_is_equal (&pad->descriptor->essence_container,
&mux->partition.essence_containers[j])) {
found = TRUE;
break;
}
}
if (found)
continue;
memcpy (&mux->partition.essence_containers[i],
&pad->descriptor->essence_container, 16);
i++;
}
mux->partition.n_essence_containers = i;
GST_OBJECT_UNLOCK (mux);
return GST_FLOW_OK;
}
static GstFlowReturn
gst_mxf_mux_write_header_metadata (GstMXFMux * mux)
{
GstFlowReturn ret = GST_FLOW_OK;
GstBuffer *buf;
GList *buffers = NULL;
GList *l;
MXFMetadataBase *m;
guint64 header_byte_count = 0;
for (l = mux->metadata_list; l; l = l->next) {
m = l->data;
buf = mxf_metadata_base_to_buffer (m, &mux->primer);
header_byte_count += gst_buffer_get_size (buf);
buffers = g_list_prepend (buffers, buf);
}
buffers = g_list_reverse (buffers);
buf = mxf_primer_pack_to_buffer (&mux->primer);
header_byte_count += gst_buffer_get_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 (l, (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, 0x01,
0x0d, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00
};
static GstFlowReturn
gst_mxf_mux_handle_buffer (GstMXFMux * mux, GstMXFMuxPad * pad)
{
GstBuffer *buf = gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
GstBuffer *outbuf = NULL;
GstMapInfo map;
gsize buf_size;
GstFlowReturn ret = GST_FLOW_OK;
guint8 slen, ber[9];
gboolean flush = gst_aggregator_pad_is_eos (GST_AGGREGATOR_PAD (pad))
&& !pad->have_complete_edit_unit && buf == NULL;
gboolean is_keyframe = buf ?
!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) : TRUE;
if (pad->have_complete_edit_unit) {
GST_DEBUG_OBJECT (pad,
"Handling remaining buffer for track %u at position %" G_GINT64_FORMAT,
pad->source_track->parent.track_id, pad->pos);
if (buf)
gst_buffer_unref (buf);
buf = NULL;
} else if (!flush) {
if (buf)
gst_buffer_unref (buf);
buf = gst_aggregator_pad_steal_buffer (GST_AGGREGATOR_PAD (pad));
}
if (buf) {
GST_DEBUG_OBJECT (pad,
"Handling buffer of size %" G_GSIZE_FORMAT " for track %u at position %"
G_GINT64_FORMAT, gst_buffer_get_size (buf),
pad->source_track->parent.track_id, pad->pos);
} else {
flush = TRUE;
GST_DEBUG_OBJECT (pad,
"Flushing for track %u at position %" G_GINT64_FORMAT,
pad->source_track->parent.track_id, pad->pos);
}
ret = pad->write_func (buf, pad->mapping_data, pad->adapter, &outbuf, flush);
if (ret != GST_FLOW_OK && ret != GST_FLOW_CUSTOM_SUCCESS) {
GST_ERROR_OBJECT (pad,
"Failed handling buffer for track %u, reason %s",
pad->source_track->parent.track_id, gst_flow_get_name (ret));
return ret;
}
if (ret == GST_FLOW_CUSTOM_SUCCESS) {
pad->have_complete_edit_unit = TRUE;
ret = GST_FLOW_OK;
} else {
pad->have_complete_edit_unit = FALSE;
}
buf = outbuf;
if (buf == NULL)
return ret;
/* We currently only index the first essence stream */
if (pad == (GstMXFMuxPad *) GST_ELEMENT_CAST (mux)->sinkpads->data) {
MXFIndexTableSegment *segment;
const gint max_segment_size = G_MAXUINT16 / 11;
if (mux->index_table->len == 0 ||
g_array_index (mux->index_table, MXFIndexTableSegment,
mux->index_table->len - 1).index_duration >= max_segment_size) {
MXFIndexTableSegment s;
memset (&segment, 0, sizeof (segment));
mxf_uuid_init (&s.instance_id, mux->metadata);
memcpy (&s.index_edit_rate, &pad->source_track->edit_rate,
sizeof (s.index_edit_rate));
s.index_start_position = pad->pos;
s.index_duration = 0;
s.edit_unit_byte_count = 0;
s.index_sid =
mux->preface->content_storage->essence_container_data[0]->index_sid;
s.body_sid =
mux->preface->content_storage->essence_container_data[0]->body_sid;
s.slice_count = 0;
s.pos_table_count = 0;
s.n_delta_entries = 0;
s.delta_entries = NULL;
s.n_index_entries = 0;
s.index_entries = g_new0 (MXFIndexEntry, max_segment_size);
g_array_append_val (mux->index_table, s);
}
segment =
&g_array_index (mux->index_table, MXFIndexTableSegment,
mux->index_table->len - 1);
segment->index_entries[segment->n_index_entries].temporal_offset = 0;
segment->index_entries[segment->n_index_entries].key_frame_offset = 0;
segment->index_entries[segment->n_index_entries].flags = is_keyframe ? 0x80 : 0x20; /* FIXME: Need to distinguish all the cases */
segment->index_entries[segment->n_index_entries].stream_offset =
mux->partition.body_offset;
segment->n_index_entries++;
segment->index_duration++;
}
buf_size = gst_buffer_get_size (buf);
slen = mxf_ber_encode_size (buf_size, ber);
outbuf = gst_buffer_new_and_alloc (16 + slen);
gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
memcpy (map.data, _gc_essence_element_ul, 16);
GST_WRITE_UINT32_BE (map.data + 12, pad->source_track->parent.track_number);
memcpy (map.data + 16, ber, slen);
gst_buffer_unmap (outbuf, &map);
outbuf = gst_buffer_append (outbuf, buf);
GST_DEBUG_OBJECT (pad,
"Pushing buffer of size %" G_GSIZE_FORMAT " for track %u",
gst_buffer_get_size (outbuf), pad->source_track->parent.track_id);
mux->partition.body_offset += gst_buffer_get_size (outbuf);
if ((ret = gst_mxf_mux_push (mux, outbuf)) != GST_FLOW_OK) {
GST_ERROR_OBJECT (pad,
"Failed pushing buffer for track %u, reason %s",
pad->source_track->parent.track_id, gst_flow_get_name (ret));
return ret;
}
pad->pos++;
pad->last_timestamp =
gst_util_uint64_scale (GST_SECOND * pad->pos,
pad->source_track->edit_rate.d, pad->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 = TRUE;
mux->partition.complete = TRUE;
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)
{
GList *l;
gboolean have_data = FALSE;
GstBuffer *packet;
do {
GstMXFMuxPad *best = NULL;
have_data = FALSE;
GST_OBJECT_LOCK (mux);
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
GstMXFMuxPad *pad = l->data;
GstBuffer *buffer =
gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
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 (pad->have_complete_edit_unit ||
gst_adapter_available (pad->adapter) > 0 || buffer) {
have_data = TRUE;
if (pad->last_timestamp < next_gc_timestamp) {
best = gst_object_ref (pad);
if (buffer)
gst_buffer_unref (buffer);
break;
}
}
if (buffer)
gst_buffer_unref (buffer);
if (have_data && !l->next) {
mux->last_gc_position++;
mux->last_gc_timestamp = next_gc_timestamp;
break;
}
}
GST_OBJECT_UNLOCK (mux);
if (best) {
gst_mxf_mux_handle_buffer (mux, best);
gst_object_unref (best);
have_data = TRUE;
}
} while (have_data);
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);
/* Update essence track durations */
GST_OBJECT_LOCK (mux);
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
GstMXFMuxPad *pad = l->data;
guint i;
/* Update durations */
pad->source_track->parent.sequence->duration = pad->pos;
MXF_METADATA_SOURCE_CLIP (pad->source_track->parent.
sequence->structural_components[0])->parent.duration = pad->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 ==
pad->source_track->parent.track_id) {
track->parent.sequence->structural_components[0]->duration = pad->pos;
track->parent.sequence->duration = pad->pos;
}
}
}
GST_OBJECT_UNLOCK (mux);
/* 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;
}
{
MXFMetadataTimelineTrack *track =
MXF_METADATA_TIMELINE_TRACK (mux->preface->
content_storage->packages[1]->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;
GstSegment segment;
MXFRandomIndexPackEntry entry;
GList *index_entries = NULL, *l;
guint index_byte_count = 0;
guint i;
GstBuffer *buf;
for (i = 0; i < mux->index_table->len; i++) {
MXFIndexTableSegment *segment =
&g_array_index (mux->index_table, MXFIndexTableSegment, i);
GstBuffer *segment_buffer = mxf_index_table_segment_to_buffer (segment);
index_byte_count += gst_buffer_get_size (segment_buffer);
index_entries = g_list_prepend (index_entries, segment_buffer);
}
mux->partition.type = MXF_PARTITION_PACK_FOOTER;
mux->partition.closed = TRUE;
mux->partition.complete = TRUE;
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 = index_byte_count;
mux->partition.index_sid =
mux->preface->content_storage->essence_container_data[0]->index_sid;
mux->partition.body_offset = 0;
mux->partition.body_sid = 0;
gst_mxf_mux_write_header_metadata (mux);
index_entries = g_list_reverse (index_entries);
for (l = index_entries; l; l = l->next) {
if ((ret = gst_mxf_mux_push (mux, l->data)) != GST_FLOW_OK) {
GST_ERROR_OBJECT (mux, "Failed pushing index table segment");
}
}
g_list_free (index_entries);
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 */
gst_segment_init (&segment, GST_FORMAT_BYTES);
if (gst_pad_push_event (GST_AGGREGATOR_SRC_PAD (mux),
gst_event_new_segment (&segment))) {
mux->offset = 0;
mux->partition.type = MXF_PARTITION_PACK_HEADER;
mux->partition.closed = TRUE;
mux->partition.complete = TRUE;
mux->partition.this_partition = 0;
mux->partition.prev_partition = 0;
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;
}
g_assert (mux->offset == body_partition);
mux->partition.type = MXF_PARTITION_PACK_BODY;
mux->partition.closed = TRUE;
mux->partition.complete = TRUE;
mux->partition.this_partition = mux->offset;
mux->partition.prev_partition = 0;
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 =
mux->preface->content_storage->essence_container_data[0]->body_sid;
buf = mxf_partition_pack_to_buffer (&mux->partition);
ret = gst_mxf_mux_push (mux, buf);
if (ret != GST_FLOW_OK) {
GST_ERROR_OBJECT (mux, "Rewriting body 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 gboolean
gst_mxf_mux_stop (GstAggregator * aggregator)
{
GstMXFMux *mux = GST_MXF_MUX (aggregator);
gst_mxf_mux_reset (mux);
return TRUE;
}
static GstFlowReturn
gst_mxf_mux_aggregate (GstAggregator * aggregator, gboolean timeout)
{
GstMXFMux *mux = GST_MXF_MUX (aggregator);
GstMXFMuxPad *best = NULL;
GstFlowReturn ret;
GList *l;
gboolean eos = TRUE;
if (timeout) {
GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
("Live mixing and got a timeout. This is not supported yet"));
ret = GST_FLOW_ERROR;
goto error;
}
if (mux->state == GST_MXF_MUX_STATE_ERROR) {
GST_ERROR_OBJECT (mux, "Had an error before -- returning");
return GST_FLOW_ERROR;
} else if (mux->state == GST_MXF_MUX_STATE_EOS) {
GST_WARNING_OBJECT (mux, "EOS");
return GST_FLOW_EOS;
}
if (mux->state == GST_MXF_MUX_STATE_HEADER) {
GstCaps *caps;
if (GST_ELEMENT_CAST (mux)->sinkpads == NULL) {
GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
("No input streams configured"));
ret = GST_FLOW_ERROR;
goto error;
}
caps = gst_caps_new_empty_simple ("application/mxf");
gst_aggregator_set_src_caps (GST_AGGREGATOR (mux), caps);
gst_caps_unref (caps);
if ((ret = gst_mxf_mux_create_metadata (mux)) != GST_FLOW_OK)
goto error;
if ((ret = gst_mxf_mux_init_partition_pack (mux)) != GST_FLOW_OK)
goto error;
if ((ret = gst_mxf_mux_write_header_metadata (mux)) != GST_FLOW_OK)
goto error;
/* Sort pads, we will always write in that order */
GST_OBJECT_LOCK (mux);
GST_ELEMENT_CAST (mux)->sinkpads =
g_list_sort (GST_ELEMENT_CAST (mux)->sinkpads, _sort_mux_pads);
GST_OBJECT_UNLOCK (mux);
/* Write body partition */
ret = gst_mxf_mux_write_body_partition (mux);
if (ret != GST_FLOW_OK)
goto error;
mux->state = GST_MXF_MUX_STATE_DATA;
}
g_return_val_if_fail (g_hash_table_size (mux->metadata) > 0, GST_FLOW_ERROR);
do {
GST_OBJECT_LOCK (mux);
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
gboolean pad_eos;
GstMXFMuxPad *pad = l->data;
GstBuffer *buffer;
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);
pad_eos = gst_aggregator_pad_is_eos (GST_AGGREGATOR_PAD (pad));
if (!pad_eos)
eos = FALSE;
buffer = gst_aggregator_pad_get_buffer (GST_AGGREGATOR_PAD (pad));
if ((!pad_eos || pad->have_complete_edit_unit ||
gst_adapter_available (pad->adapter) > 0 || buffer)
&& pad->last_timestamp < next_gc_timestamp) {
if (buffer)
gst_buffer_unref (buffer);
best = gst_object_ref (pad);
break;
} else if (!eos && !l->next) {
mux->last_gc_position++;
mux->last_gc_timestamp = next_gc_timestamp;
eos = FALSE;
if (buffer)
gst_buffer_unref (buffer);
best = NULL;
break;
}
if (buffer)
gst_buffer_unref (buffer);
}
GST_OBJECT_UNLOCK (mux);
} while (!eos && best == NULL);
if (!eos && best) {
ret = gst_mxf_mux_handle_buffer (mux, best);
gst_object_unref (best);
if (ret != GST_FLOW_OK)
goto error;
} else if (eos) {
GST_DEBUG_OBJECT (mux, "Handling EOS");
if (best)
gst_object_unref (best);
gst_mxf_mux_handle_eos (mux);
mux->state = GST_MXF_MUX_STATE_EOS;
return GST_FLOW_EOS;
} else {
g_assert_not_reached ();
}
return GST_FLOW_OK;
error:
{
mux->state = GST_MXF_MUX_STATE_ERROR;
return ret;
}
}