mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-09 05:38:58 +00:00
mxfmux can't negotiate caps with upstream/downstream and always outputs specific caps based on the input streams. This will always happen before it produces the first buffers. By having the default aggregator negotiation enabled the same caps would be pushed twice in the beginning, and again every time a reconfigure event is received. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2372>
1838 lines
59 KiB
C
1838 lines
59 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
|
|
* @title: mxfmux
|
|
*
|
|
* mxfmux muxes different streams into an MXF file.
|
|
*
|
|
* ## Example launch line
|
|
* |[
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "gstmxfelements.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)
|
|
{
|
|
pad->adapter = gst_adapter_new ();
|
|
}
|
|
|
|
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);
|
|
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (mxfmux, "mxfmux", GST_RANK_PRIMARY,
|
|
GST_TYPE_MXF_MUX, mxf_element_init (plugin));
|
|
|
|
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->negotiate = NULL;
|
|
|
|
gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
|
|
&src_templ, GST_TYPE_MXF_MUX_PAD);
|
|
|
|
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>");
|
|
|
|
gst_type_mark_as_plugin_api (GST_TYPE_MXF_MUX_PAD, 0);
|
|
}
|
|
|
|
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) {
|
|
gsize n;
|
|
for (n = 0; n < mux->index_table->len; ++n)
|
|
g_free (g_array_index (mux->index_table, MXFIndexTableSegment,
|
|
n).index_entries);
|
|
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;
|
|
gsize n;
|
|
|
|
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;
|
|
|
|
if (mux->index_table)
|
|
for (n = 0; n < mux->index_table->len; ++n)
|
|
g_free (g_array_index (mux->index_table, MXFIndexTableSegment,
|
|
n).index_entries);
|
|
g_array_set_size (mux->index_table, 0);
|
|
mux->current_index_pos = 0;
|
|
mux->last_keyframe_pos = 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->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_peek_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, µ, &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_peek_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_peek_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_peek_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;
|
|
GstClockTime pts = buf ? GST_BUFFER_PTS (buf) : GST_CLOCK_TIME_NONE;
|
|
GstClockTime dts = buf ? GST_BUFFER_DTS (buf) : GST_CLOCK_TIME_NONE;
|
|
|
|
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_pop_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->current_index_pos).index_duration >= max_segment_size) {
|
|
|
|
if (mux->index_table->len > 0)
|
|
mux->current_index_pos++;
|
|
|
|
if (mux->index_table->len <= mux->current_index_pos) {
|
|
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));
|
|
if (mux->index_table->len > 0)
|
|
s.index_start_position =
|
|
g_array_index (mux->index_table, MXFIndexTableSegment,
|
|
mux->index_table->len - 1).index_start_position;
|
|
else
|
|
s.index_start_position = 0;
|
|
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->current_index_pos);
|
|
|
|
if (dts != GST_CLOCK_TIME_NONE && pts != GST_CLOCK_TIME_NONE) {
|
|
guint64 pts_pos;
|
|
guint64 pts_index_pos, pts_segment_pos;
|
|
gint64 index_pos_diff;
|
|
MXFIndexTableSegment *pts_segment;
|
|
|
|
pts =
|
|
gst_segment_to_running_time (&pad->parent.segment, GST_FORMAT_TIME,
|
|
pts);
|
|
pts_pos =
|
|
gst_util_uint64_scale_round (pts, pad->source_track->edit_rate.n,
|
|
pad->source_track->edit_rate.d * GST_SECOND);
|
|
|
|
index_pos_diff = pts_pos - pad->pos;
|
|
pts_index_pos = mux->current_index_pos;
|
|
pts_segment_pos = segment->n_index_entries;
|
|
if (index_pos_diff >= 0) {
|
|
while (pts_segment_pos + index_pos_diff >= max_segment_size) {
|
|
index_pos_diff -= max_segment_size - pts_segment_pos;
|
|
pts_segment_pos = 0;
|
|
pts_index_pos++;
|
|
|
|
if (pts_index_pos >= mux->index_table->len) {
|
|
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));
|
|
if (mux->index_table->len > 0)
|
|
s.index_start_position =
|
|
g_array_index (mux->index_table, MXFIndexTableSegment,
|
|
mux->index_table->len - 1).index_start_position;
|
|
else
|
|
s.index_start_position = 0;
|
|
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);
|
|
}
|
|
}
|
|
} else {
|
|
while (pts_segment_pos + index_pos_diff <= 0) {
|
|
if (pts_index_pos == 0) {
|
|
pts_index_pos = G_MAXUINT64;
|
|
break;
|
|
}
|
|
index_pos_diff += pts_segment_pos;
|
|
pts_segment_pos = max_segment_size;
|
|
pts_index_pos--;
|
|
}
|
|
}
|
|
if (pts_index_pos != G_MAXUINT64) {
|
|
g_assert (index_pos_diff < 127 && index_pos_diff >= -127);
|
|
pts_segment =
|
|
&g_array_index (mux->index_table, MXFIndexTableSegment,
|
|
pts_index_pos);
|
|
pts_segment->index_entries[pts_segment_pos +
|
|
index_pos_diff].temporal_offset = -index_pos_diff;
|
|
}
|
|
}
|
|
|
|
/* Leave temporal offset initialized at 0, above code will set it as necessary */
|
|
;
|
|
if (is_keyframe)
|
|
mux->last_keyframe_pos = pad->pos;
|
|
segment->index_entries[segment->n_index_entries].key_frame_offset =
|
|
MIN (pad->pos - mux->last_keyframe_pos, 127);
|
|
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_peek_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_peek_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;
|
|
}
|
|
}
|