diff options
author | Sebastian Dröge <slomo@circular-chaos.org> | 2008-11-21 10:27:15 +0000 |
---|---|---|
committer | Sebastian Dröge <slomo@circular-chaos.org> | 2008-11-21 10:27:15 +0000 |
commit | e76ea1ca88b2b9fb92484a95c216ab4db714f792 (patch) | |
tree | 4a4f74b83fa3087df320727de3d058484a37428d /gst | |
parent | bba82abbf7edd7b7751af53a817622d13b5ab577 (diff) | |
download | gst-plugins-bad-e76ea1ca88b2b9fb92484a95c216ab4db714f792.tar.gz gst-plugins-bad-e76ea1ca88b2b9fb92484a95c216ab4db714f792.tar.bz2 gst-plugins-bad-e76ea1ca88b2b9fb92484a95c216ab4db714f792.zip |
Add first version of an MXF demuxer. Currently it supports
Original commit message from CVS:
* configure.ac:
* gst/mxf/Makefile.am:
* gst/mxf/mxf.c: (plugin_init):
* gst/mxf/mxfaes-bwf.c:
(mxf_metadata_wave_audio_essence_descriptor_parse),
(mxf_metadata_wave_audio_essence_descriptor_reset),
(mxf_is_aes_bwf_essence_track), (mxf_bwf_handle_essence_element),
(mxf_bwf_create_caps), (mxf_aes_bwf_create_caps):
* gst/mxf/mxfaes-bwf.h:
* gst/mxf/mxfdemux.c: (gst_mxf_pad_finalize),
(gst_mxf_pad_class_init), (gst_mxf_pad_init),
(gst_mxf_demux_flush), (gst_mxf_demux_remove_pad),
(gst_mxf_demux_reset_mxf_state), (gst_mxf_demux_reset_metadata),
(gst_mxf_demux_reset), (gst_mxf_demux_pull_range),
(gst_mxf_demux_push_src_event),
(gst_mxf_demux_handle_partition_pack),
(gst_mxf_demux_handle_primer_pack),
(gst_mxf_demux_handle_metadata_preface),
(gst_mxf_demux_handle_metadata_identification),
(gst_mxf_demux_handle_metadata_content_storage),
(gst_mxf_demux_handle_metadata_essence_container_data),
(gst_mxf_demux_handle_metadata_material_package),
(gst_mxf_demux_handle_metadata_source_package),
(gst_mxf_demux_handle_metadata_track),
(gst_mxf_demux_handle_metadata_sequence),
(gst_mxf_demux_handle_metadata_structural_component),
(gst_mxf_demux_handle_metadata_generic_descriptor),
(gst_mxf_demux_handle_metadata_file_descriptor),
(gst_mxf_demux_handle_metadata_multiple_descriptor),
(gst_mxf_demux_handle_metadata_generic_picture_essence_descriptor),
(gst_mxf_demux_handle_metadata_cdci_picture_essence_descriptor),
(gst_mxf_demux_handle_metadata_mpeg_video_descriptor),
(gst_mxf_demux_handle_metadata_generic_sound_essence_descriptor),
(gst_mxf_demux_handle_metadata_wave_audio_essence_descriptor),
(gst_mxf_demux_handle_metadata_locator),
(gst_mxf_demux_handle_header_metadata_resolve_references),
(gst_mxf_demux_handle_header_metadata_update_streams),
(gst_mxf_demux_handle_metadata),
(gst_mxf_demux_handle_generic_container_system_item),
(gst_mxf_demux_handle_generic_container_essence_element),
(gst_mxf_demux_handle_random_index_pack),
(gst_mxf_demux_handle_index_table_segment),
(gst_mxf_demux_pull_klv_packet),
(gst_mxf_demux_parse_footer_metadata),
(gst_mxf_demux_handle_klv_packet),
(gst_mxf_demux_pull_and_handle_klv_packet), (gst_mxf_demux_loop),
(gst_mxf_demux_chain), (gst_mxf_demux_src_event),
(gst_mxf_demux_src_query_type), (gst_mxf_demux_src_query),
(gst_mxf_demux_sink_activate), (gst_mxf_demux_sink_activate_push),
(gst_mxf_demux_sink_activate_pull), (gst_mxf_demux_sink_event),
(gst_mxf_demux_change_state), (gst_mxf_demux_finalize),
(gst_mxf_demux_base_init), (gst_mxf_demux_class_init),
(gst_mxf_demux_init):
* gst/mxf/mxfdemux.h:
* gst/mxf/mxfmpeg.c: (mxf_metadata_mpeg_video_descriptor_parse),
(mxf_metadata_mpeg_video_descriptor_reset),
(mxf_is_mpeg_video_essence_track),
(mxf_mpeg_video_handle_essence_element),
(mxf_mpeg_video_create_caps):
* gst/mxf/mxfmpeg.h:
* gst/mxf/mxfparse.c: (mxf_is_mxf_packet), (mxf_is_partition_pack),
(mxf_is_header_partition_pack), (mxf_is_body_partition_pack),
(mxf_is_footer_partition_pack), (mxf_is_fill),
(mxf_is_primer_pack), (mxf_is_metadata),
(mxf_is_random_index_pack), (mxf_is_index_table_segment),
(mxf_is_generic_container_system_item),
(mxf_is_generic_container_essence_element),
(mxf_is_generic_container_essence_container_label),
(mxf_ul_is_equal), (mxf_ul_is_zero), (mxf_ul_to_string),
(mxf_umid_is_equal), (mxf_umid_is_zero), (mxf_umid_to_string),
(gst_mxf_ul_hash), (gst_mxf_ul_equal), (mxf_timestamp_parse),
(mxf_timestamp_is_unknown), (mxf_timestamp_compare),
(mxf_fraction_parse), (mxf_utf16_to_utf8),
(mxf_product_version_parse), (mxf_partition_pack_parse),
(mxf_partition_pack_reset), (_mxf_mapping_ul_free),
(mxf_primer_pack_parse), (mxf_primer_pack_reset),
(mxf_local_tag_parse), (gst_mxf_local_tag_free),
(gst_metadata_add_custom_tag), (mxf_metadata_preface_parse),
(mxf_metadata_preface_reset), (mxf_metadata_identification_parse),
(mxf_metadata_identification_reset),
(mxf_metadata_content_storage_parse),
(mxf_metadata_content_storage_reset),
(mxf_metadata_essence_container_data_parse),
(mxf_metadata_essence_container_data_reset),
(mxf_metadata_generic_package_parse),
(mxf_metadata_generic_package_reset), (mxf_metadata_track_parse),
(mxf_metadata_track_reset), (mxf_metadata_track_identifier_parse),
(mxf_metadata_sequence_parse), (mxf_metadata_sequence_reset),
(mxf_metadata_structural_component_parse),
(mxf_metadata_structural_component_reset),
(mxf_metadata_generic_descriptor_parse),
(mxf_metadata_generic_descriptor_reset),
(mxf_metadata_file_descriptor_parse),
(mxf_metadata_file_descriptor_reset),
(mxf_metadata_generic_sound_essence_descriptor_parse),
(mxf_metadata_generic_sound_essence_descriptor_reset),
(mxf_metadata_generic_picture_essence_descriptor_parse),
(mxf_metadata_generic_picture_essence_descriptor_reset),
(mxf_metadata_cdci_picture_essence_descriptor_parse),
(mxf_metadata_cdci_picture_essence_descriptor_reset),
(mxf_metadata_multiple_descriptor_parse),
(mxf_metadata_multiple_descriptor_reset),
(mxf_metadata_locator_parse), (mxf_metadata_locator_reset):
* gst/mxf/mxfparse.h:
* gst/mxf/mxftypes.h:
Add first version of an MXF demuxer. Currently it supports
MPEG video and raw audio and OP 1a/b/c.
Diffstat (limited to 'gst')
-rw-r--r-- | gst/mxf/Makefile.am | 20 | ||||
-rw-r--r-- | gst/mxf/mxf.c | 46 | ||||
-rw-r--r-- | gst/mxf/mxfaes-bwf.c | 382 | ||||
-rw-r--r-- | gst/mxf/mxfaes-bwf.h | 66 | ||||
-rw-r--r-- | gst/mxf/mxfdemux.c | 2859 | ||||
-rw-r--r-- | gst/mxf/mxfdemux.h | 108 | ||||
-rw-r--r-- | gst/mxf/mxfmpeg.c | 318 | ||||
-rw-r--r-- | gst/mxf/mxfmpeg.h | 60 | ||||
-rw-r--r-- | gst/mxf/mxfparse.c | 2692 | ||||
-rw-r--r-- | gst/mxf/mxfparse.h | 129 | ||||
-rw-r--r-- | gst/mxf/mxftypes.h | 463 |
11 files changed, 7143 insertions, 0 deletions
diff --git a/gst/mxf/Makefile.am b/gst/mxf/Makefile.am new file mode 100644 index 00000000..0113d9d1 --- /dev/null +++ b/gst/mxf/Makefile.am @@ -0,0 +1,20 @@ +plugin_LTLIBRARIES = libgstmxf.la + +libgstmxf_la_SOURCES = \ + mxf.c \ + mxfdemux.c \ + mxfparse.c \ + mxfaes-bwf.c \ + mxfmpeg.c + +libgstmxf_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) +libgstmxf_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgstmxf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = \ + mxfdemux.h \ + mxfparse.h \ + mxfaes-bwf.h \ + mxfmpeg.h \ + mxftypes.h + diff --git a/gst/mxf/mxf.c b/gst/mxf/mxf.c new file mode 100644 index 00000000..655ac311 --- /dev/null +++ b/gst/mxf/mxf.c @@ -0,0 +1,46 @@ +/* GStreamer + * Copyright (C) <2008> 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gst/gst.h> + +#include "mxfdemux.h" + +GST_DEBUG_CATEGORY (mxf_debug); +#define GST_CAT_DEFAULT mxf_debug + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "mxfdemux", GST_RANK_PRIMARY, + GST_TYPE_MXF_DEMUX)) + return FALSE; + + GST_DEBUG_CATEGORY_INIT (mxf_debug, "mxf", 0, "MXF"); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "mxf", + "MXF plugin library", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/mxf/mxfaes-bwf.c b/gst/mxf/mxfaes-bwf.c new file mode 100644 index 00000000..ef97fafc --- /dev/null +++ b/gst/mxf/mxfaes-bwf.c @@ -0,0 +1,382 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* Implementation of SMPTE 382M - Mapping AES3 and Broadcast Wave + * Audio into the MXF Generic Container + */ + +/* TODO: + * - Handle the case were a track only references specific channels + * of the essence (ChannelID property) + * - Add support for more codecs + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <string.h> + +#include "mxfaes-bwf.h" + +GST_DEBUG_CATEGORY_EXTERN (mxf_debug); +#define GST_CAT_DEFAULT mxf_debug + +/* SMPTE 382M Annex 1 */ +gboolean +mxf_metadata_wave_audio_essence_descriptor_parse (const MXFUL * key, + MXFMetadataWaveAudioEssenceDescriptor * descriptor, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (descriptor, 0, sizeof (MXFMetadataWaveAudioEssenceDescriptor)); + + if (!mxf_metadata_generic_sound_essence_descriptor_parse (key, + (MXFMetadataGenericSoundEssenceDescriptor *) descriptor, primer, type, + data, size)) + goto error; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3d0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 2) + goto error; + descriptor->block_align = GST_READ_UINT16_BE (tag_data); + break; + case 0x3d0b: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->sequence_offset = GST_READ_UINT8 (tag_data); + break; + case 0x3d09: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->avg_bps = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d32: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&descriptor->channel_assignment, tag_data, 16); + break; + case 0x3d29: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->peak_envelope_version = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d2a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->peak_envelope_format = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d2b: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->points_per_peak_value = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d2c: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->peak_envelope_block_size = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d2d: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->peak_channels = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d2e: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->peak_frames = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d2f: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 8) + goto error; + descriptor->peak_of_peaks_position = GST_READ_UINT64_BE (tag_data); + break; + case 0x3d30: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!mxf_timestamp_parse (&descriptor->peak_envelope_timestamp, + tag_data, tag_size)) + goto error; + break; + case 0x3d31: + GST_WRITE_UINT16_BE (data, 0x0000); + descriptor->peak_envelope_data = g_memdup (tag_data, tag_size); + descriptor->peak_envelope_data_length = tag_size; + break; + default: + if (type != MXF_METADATA_WAVE_AUDIO_ESSENCE_DESCRIPTOR) + goto next; + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &((MXFMetadataGenericDescriptor *) descriptor)->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed wave audio essence descriptor:"); + GST_DEBUG (" block align = %u", descriptor->block_align); + GST_DEBUG (" sequence offset = %u", descriptor->sequence_offset); + GST_DEBUG (" average bps = %u", descriptor->avg_bps); + GST_DEBUG (" channel assignment = %s", + mxf_ul_to_string (&descriptor->channel_assignment, str)); + GST_DEBUG (" peak envelope version = %u", descriptor->peak_envelope_version); + GST_DEBUG (" peak envelope format = %u", descriptor->peak_envelope_format); + GST_DEBUG (" points per peak value = %u", descriptor->points_per_peak_value); + GST_DEBUG (" peak envelope block size = %u", + descriptor->peak_envelope_block_size); + GST_DEBUG (" peak channels = %u", descriptor->peak_channels); + GST_DEBUG (" peak frames = %u", descriptor->peak_frames); + GST_DEBUG (" peak of peaks position = %" G_GINT64_FORMAT, + descriptor->peak_of_peaks_position); + GST_DEBUG (" peak envelope timestamp = %d/%u/%u %u:%u:%u.%u", + descriptor->peak_envelope_timestamp.year, + descriptor->peak_envelope_timestamp.month, + descriptor->peak_envelope_timestamp.day, + descriptor->peak_envelope_timestamp.hour, + descriptor->peak_envelope_timestamp.minute, + descriptor->peak_envelope_timestamp.second, + (descriptor->peak_envelope_timestamp.quarter_msecond * 1000) / 256); + + GST_DEBUG (" peak evelope data size = %u", + descriptor->peak_envelope_data_length); + + return TRUE; + +error: + GST_ERROR ("Invalid wave audio essence descriptor"); + mxf_metadata_wave_audio_essence_descriptor_reset (descriptor); + + return FALSE; +} + +void mxf_metadata_wave_audio_essence_descriptor_reset + (MXFMetadataWaveAudioEssenceDescriptor * descriptor) +{ + g_return_if_fail (descriptor != NULL); + + mxf_metadata_generic_sound_essence_descriptor_reset ( + (MXFMetadataGenericSoundEssenceDescriptor *) descriptor); + + memset (descriptor, 0, sizeof (MXFMetadataWaveAudioEssenceDescriptor)); +} + +gboolean +mxf_is_aes_bwf_essence_track (const MXFMetadataTrack * track) +{ + guint i; + + g_return_val_if_fail (track != NULL, FALSE); + + if (track->descriptor == NULL) { + GST_ERROR ("No descriptor for this track"); + return FALSE; + } + + for (i = 0; i < track->n_descriptor; i++) { + MXFMetadataFileDescriptor *d = track->descriptor[i]; + MXFUL *key = &d->essence_container; + /* SMPTE 382M 9 */ + if (mxf_is_generic_container_essence_container_label (key) && + key->u[12] == 0x02 && + key->u[13] == 0x06 && + (key->u[14] == 0x01 || + key->u[14] == 0x02 || + key->u[14] == 0x03 || + key->u[14] == 0x04 || key->u[14] == 0x08 || key->u[14] == 0x09)) + return TRUE; + } + + + return FALSE; +} + +static GstFlowReturn +mxf_bwf_handle_essence_element (const MXFUL * key, GstBuffer * buffer, + GstCaps * caps, MXFMetadataGenericPackage * package, + MXFMetadataTrack * track, MXFMetadataStructuralComponent * component, + gpointer mapping_data, GstBuffer ** outbuf) +{ + *outbuf = buffer; + + /* SMPTE 382M Table 1: Check if this is some kind of Wave element */ + if (key->u[12] != 0x16 || (key->u[14] != 0x01 && key->u[14] != 0x02 + && key->u[14] != 0x0b)) { + GST_ERROR ("Invalid BWF essence element"); + return GST_FLOW_ERROR; + } + + /* FIXME: check if the size is a multiply of the unit size, ... */ + return GST_FLOW_OK; +} + +/* SMPTE RP224 */ +static const MXFUL mxf_sound_essence_compression_uncompressed = + { {0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x01, 0x04, 0x02, 0x02, 0x01, + 0x7F, 0x00, 0x00, 0x00} +}; +static const MXFUL mxf_sound_essence_compression_aiff = + { {0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x07, 0x04, 0x02, 0x02, 0x01, + 0x7E, 0x00, 0x00, 0x00} +}; + +static GstCaps * +mxf_bwf_create_caps (MXFMetadataGenericPackage * package, + MXFMetadataTrack * track, + MXFMetadataGenericSoundEssenceDescriptor * descriptor, GstTagList ** tags, + MXFEssenceElementHandler * handler, gpointer * mapping_data) +{ + GstCaps *ret = NULL; + MXFMetadataWaveAudioEssenceDescriptor *wa_descriptor = NULL; + gchar str[48]; + + if (((MXFMetadataGenericDescriptor *) descriptor)->type == + MXF_METADATA_WAVE_AUDIO_ESSENCE_DESCRIPTOR) + wa_descriptor = (MXFMetadataWaveAudioEssenceDescriptor *) descriptor; + + /* TODO: set caps for avg bitrate, audio codec, ...... */ + /* TODO: Handle width=!depth, needs shifting of samples */ + + /* FIXME: set a channel layout */ + + if (mxf_ul_is_zero (&descriptor->sound_essence_compression) || + mxf_ul_is_equal (&descriptor->sound_essence_compression, + &mxf_sound_essence_compression_uncompressed)) { + guint block_align; + + if (descriptor->channel_count == 0 || + descriptor->quantization_bits == 0 || + descriptor->audio_sampling_rate.n == 0 || + descriptor->audio_sampling_rate.d == 0) { + GST_ERROR ("Invalid descriptor"); + return NULL; + } + if (wa_descriptor) + block_align = wa_descriptor->block_align; + else + block_align = GST_ROUND_UP_8 (descriptor->quantization_bits) / 8; + + ret = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, + (gint) (((gdouble) descriptor->audio_sampling_rate.n) / + ((gdouble) descriptor->audio_sampling_rate.d) + 0.5), "channels", + G_TYPE_INT, descriptor->channel_count, "signed", G_TYPE_BOOLEAN, + (block_align != 1), "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, "depth", + G_TYPE_INT, (block_align / descriptor->channel_count) * 8, "width", + G_TYPE_INT, (block_align / descriptor->channel_count) * 8, NULL); + } else if (mxf_ul_is_equal (&descriptor->sound_essence_compression, + &mxf_sound_essence_compression_aiff)) { + guint block_align; + + if (descriptor->channel_count == 0 || + descriptor->quantization_bits == 0 || + descriptor->audio_sampling_rate.n == 0 || + descriptor->audio_sampling_rate.d == 0) { + GST_ERROR ("Invalid descriptor"); + return NULL; + } + + if (wa_descriptor) + block_align = wa_descriptor->block_align; + else + block_align = GST_ROUND_UP_8 (descriptor->quantization_bits) / 8; + + ret = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, + (gint) (((gdouble) descriptor->audio_sampling_rate.n) / + ((gdouble) descriptor->audio_sampling_rate.d) + 0.5), "channels", + G_TYPE_INT, descriptor->channel_count, "signed", G_TYPE_BOOLEAN, + (block_align != 1), "endianness", G_TYPE_INT, G_BIG_ENDIAN, "depth", + G_TYPE_INT, (block_align / descriptor->channel_count) * 8, "width", + G_TYPE_INT, (block_align / descriptor->channel_count) * 8, NULL); + } else { + GST_ERROR ("Unsupported sound essence compression: %s", + mxf_ul_to_string (&descriptor->sound_essence_compression, str)); + } + + *handler = mxf_bwf_handle_essence_element; + + return ret; +} + +GstCaps * +mxf_aes_bwf_create_caps (MXFMetadataGenericPackage * package, + MXFMetadataTrack * track, GstTagList ** tags, + MXFEssenceElementHandler * handler, gpointer * mapping_data) +{ + MXFMetadataGenericSoundEssenceDescriptor *s = NULL; + guint i; + + g_return_val_if_fail (package != NULL, NULL); + g_return_val_if_fail (track != NULL, NULL); + + if (track->descriptor == NULL) { + GST_ERROR ("No descriptor found for this track"); + return NULL; + } + + /* TODO: handle AES3 audio */ + + for (i = 0; i < track->n_descriptor; i++) { + if ((track->descriptor[i]->parent.type == + MXF_METADATA_WAVE_AUDIO_ESSENCE_DESCRIPTOR + || track->descriptor[i]->parent.type == + MXF_METADATA_GENERIC_SOUND_ESSENCE_DESCRIPTOR) + && (track->descriptor[i]->essence_container.u[14] == 0x01 + || track->descriptor[i]->essence_container.u[14] == 0x02 + || track->descriptor[i]->essence_container.u[14] == 0x08)) { + s = (MXFMetadataGenericSoundEssenceDescriptor *) track->descriptor[i]; + break; + } + } + + if (!s) { + GST_ERROR ("No descriptor found for this track"); + return NULL; + } else if (s) { + return mxf_bwf_create_caps (package, track, s, tags, handler, mapping_data); + } + + return NULL; +} diff --git a/gst/mxf/mxfaes-bwf.h b/gst/mxf/mxfaes-bwf.h new file mode 100644 index 00000000..6f3a5cd4 --- /dev/null +++ b/gst/mxf/mxfaes-bwf.h @@ -0,0 +1,66 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* Implementation of SMPTE 382M - Mapping AES3 and Broadcast Wave + * Audio into the MXF Generic Container + */ + +#ifndef __MXF_AES_BWF_H__ +#define __MXF_AES_BWF_H__ + +#include <gst/gst.h> + +#include "mxfparse.h" + +/* SMPTE 382M Annex 1 */ +#define MXF_METADATA_WAVE_AUDIO_ESSENCE_DESCRIPTOR 0x0148 + +/* SMPTE 382M Annex 1 */ +typedef struct { + MXFMetadataGenericSoundEssenceDescriptor parent; + + guint16 block_align; + guint8 sequence_offset; + + guint32 avg_bps; + + MXFUL channel_assignment; + + guint32 peak_envelope_version; + guint32 peak_envelope_format; + guint32 points_per_peak_value; + guint32 peak_envelope_block_size; + guint32 peak_channels; + guint32 peak_frames; + gint64 peak_of_peaks_position; + MXFTimestamp peak_envelope_timestamp; + + guint8 *peak_envelope_data; + guint16 peak_envelope_data_length; +} MXFMetadataWaveAudioEssenceDescriptor; + +gboolean mxf_metadata_wave_audio_essence_descriptor_parse (const MXFUL *key, MXFMetadataWaveAudioEssenceDescriptor *descriptor, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_wave_audio_essence_descriptor_reset (MXFMetadataWaveAudioEssenceDescriptor *descriptor); + +gboolean mxf_is_aes_bwf_essence_track (const MXFMetadataTrack *track); + +GstCaps * +mxf_aes_bwf_create_caps (MXFMetadataGenericPackage *package, MXFMetadataTrack *track, GstTagList **tags, MXFEssenceElementHandler *handler, gpointer *mapping_data); + +#endif /* __MXF_AES_BWF_H__ */ diff --git a/gst/mxf/mxfdemux.c b/gst/mxf/mxfdemux.c new file mode 100644 index 00000000..ce3c5abe --- /dev/null +++ b/gst/mxf/mxfdemux.c @@ -0,0 +1,2859 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* TODO: + * - start at correct position of the component, switch components + * - RandomIndex / IndexSegment support + * - timecode tracks + * - descriptive metadata + * - generic container system items + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mxfdemux.h" +#include "mxfparse.h" +#include "mxfaes-bwf.h" +#include "mxfmpeg.h" + +#include <string.h> + +static GstStaticPadTemplate mxf_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/mxf") + ); + +static GstStaticPadTemplate audio_src_template = +GST_STATIC_PAD_TEMPLATE ("audio_%d", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate video_src_template = +GST_STATIC_PAD_TEMPLATE ("video_%d", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate data_src_template = +GST_STATIC_PAD_TEMPLATE ("data_%d", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (mxfdemux_debug); +#define GST_CAT_DEFAULT mxfdemux_debug + +#define GST_TYPE_MXF_PAD (gst_mxf_pad_get_type()) +#define GST_MXF_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MXF_PAD,GstMXFPad)) +#define GST_MXF_PAD_CAST(pad) ((GstMXFPad *) pad) +#define GST_IS_MXF_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MXF_PAD)) + +typedef struct +{ + GstPad parent; + + guint32 track_id; + MXFMetadataTrackType track_type; + gboolean need_segment; + + guint64 essence_element_count; + MXFEssenceElementHandler handle_essence_element; + gpointer mapping_data; + + GstTagList *tags; + + guint current_material_component; + MXFMetadataGenericPackage *material_package; + MXFMetadataTrack *material_track; + MXFMetadataStructuralComponent *component; + + MXFMetadataGenericPackage *source_package; + MXFMetadataTrack *source_track; + + GstCaps *caps; +} GstMXFPad; + +typedef struct +{ + GstPadClass parent; + +} GstMXFPadClass; + +G_DEFINE_TYPE (GstMXFPad, gst_mxf_pad, GST_TYPE_PAD); + +static void +gst_mxf_pad_finalize (GObject * object) +{ + GstMXFPad *pad = GST_MXF_PAD (object); + + gst_caps_replace (&pad->caps, NULL); + + g_free (pad->mapping_data); + pad->mapping_data = NULL; + + if (pad->tags) { + gst_tag_list_free (pad->tags); + pad->tags = NULL; + } + + G_OBJECT_CLASS (gst_mxf_pad_parent_class)->finalize (object); +} + +static void +gst_mxf_pad_class_init (GstMXFPadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = gst_mxf_pad_finalize; +} + +static void +gst_mxf_pad_init (GstMXFPad * pad) +{ + +} + +static gboolean gst_mxf_demux_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_mxf_demux_src_event (GstPad * pad, GstEvent * event); +static const GstQueryType *gst_mxf_demux_src_query_type (GstPad * pad); +static gboolean gst_mxf_demux_src_query (GstPad * pad, GstQuery * query); + +GST_BOILERPLATE (GstMXFDemux, gst_mxf_demux, GstElement, GST_TYPE_ELEMENT); + +static void +gst_mxf_demux_flush (GstMXFDemux * demux, gboolean discont) +{ + GST_DEBUG_OBJECT (demux, "flushing queued data in the MXF demuxer"); + + gst_adapter_clear (demux->adapter); + + demux->flushing = FALSE; + + /* Only in push mode */ + if (!demux->random_access) { + /* We reset the offset and will get one from first push */ + demux->offset = 0; + } +} + +static void +gst_mxf_demux_remove_pad (GstMXFPad * pad, GstMXFDemux * demux) +{ + gst_element_remove_pad (GST_ELEMENT (demux), GST_PAD (pad)); +} + +static void +gst_mxf_demux_reset_mxf_state (GstMXFDemux * demux) +{ + GST_DEBUG_OBJECT (demux, "Resetting MXF state"); + mxf_partition_pack_reset (&demux->partition); + mxf_primer_pack_reset (&demux->primer); +} + +static void +gst_mxf_demux_reset_metadata (GstMXFDemux * demux) +{ + guint i; + + GST_DEBUG_OBJECT (demux, "Resetting metadata"); + + demux->update_metadata = TRUE; + demux->final_metadata = FALSE; + + demux->current_package = NULL; + + mxf_metadata_preface_reset (&demux->preface); + + if (demux->identification) { + for (i = 0; i < demux->identification->len; i++) + mxf_metadata_identification_reset (&g_array_index (demux->identification, + MXFMetadataIdentification, i)); + g_array_free (demux->identification, TRUE); + demux->identification = NULL; + } + + mxf_metadata_content_storage_reset (&demux->content_storage); + + if (demux->essence_container_data) { + for (i = 0; i < demux->essence_container_data->len; i++) + mxf_metadata_essence_container_data_reset (&g_array_index + (demux->essence_container_data, MXFMetadataEssenceContainerData, i)); + g_array_free (demux->essence_container_data, TRUE); + demux->essence_container_data = NULL; + } + + if (demux->material_package) { + for (i = 0; i < demux->material_package->len; i++) + mxf_metadata_generic_package_reset (&g_array_index + (demux->material_package, MXFMetadataGenericPackage, i)); + g_array_free (demux->material_package, TRUE); + demux->material_package = NULL; + } + + if (demux->source_package) { + for (i = 0; i < demux->source_package->len; i++) + mxf_metadata_generic_package_reset (&g_array_index (demux->source_package, + MXFMetadataGenericPackage, i)); + g_array_free (demux->source_package, TRUE); + demux->source_package = NULL; + } + + if (demux->package) { + g_ptr_array_free (demux->package, TRUE); + demux->package = NULL; + } + + if (demux->track) { + for (i = 0; i < demux->track->len; i++) + mxf_metadata_track_reset (&g_array_index (demux->track, + MXFMetadataTrack, i)); + g_array_free (demux->track, TRUE); + demux->track = NULL; + } + + if (demux->sequence) { + for (i = 0; i < demux->sequence->len; i++) + mxf_metadata_sequence_reset (&g_array_index (demux->sequence, + MXFMetadataSequence, i)); + g_array_free (demux->sequence, TRUE); + demux->sequence = NULL; + } + + if (demux->structural_component) { + for (i = 0; i < demux->structural_component->len; i++) + mxf_metadata_structural_component_reset (&g_array_index + (demux->structural_component, MXFMetadataStructuralComponent, i)); + g_array_free (demux->structural_component, TRUE); + demux->structural_component = NULL; + } + + if (demux->generic_descriptor) { + for (i = 0; i < demux->generic_descriptor->len; i++) + mxf_metadata_generic_descriptor_reset (&g_array_index + (demux->generic_descriptor, MXFMetadataGenericDescriptor, i)); + g_array_free (demux->generic_descriptor, TRUE); + demux->generic_descriptor = NULL; + } + + if (demux->file_descriptor) { + for (i = 0; i < demux->file_descriptor->len; i++) + mxf_metadata_file_descriptor_reset (&g_array_index + (demux->file_descriptor, MXFMetadataFileDescriptor, i)); + g_array_free (demux->file_descriptor, TRUE); + demux->file_descriptor = NULL; + } + + if (demux->multiple_descriptor) { + for (i = 0; i < demux->multiple_descriptor->len; i++) + mxf_metadata_multiple_descriptor_reset (&g_array_index + (demux->multiple_descriptor, MXFMetadataMultipleDescriptor, i)); + g_array_free (demux->multiple_descriptor, TRUE); + demux->multiple_descriptor = NULL; + } + + if (demux->generic_picture_essence_descriptor) { + for (i = 0; i < demux->generic_picture_essence_descriptor->len; i++) + mxf_metadata_generic_picture_essence_descriptor_reset (&g_array_index + (demux->generic_picture_essence_descriptor, + MXFMetadataGenericPictureEssenceDescriptor, i)); + g_array_free (demux->generic_picture_essence_descriptor, TRUE); + demux->generic_picture_essence_descriptor = NULL; + } + + if (demux->cdci_picture_essence_descriptor) { + for (i = 0; i < demux->cdci_picture_essence_descriptor->len; i++) + mxf_metadata_cdci_picture_essence_descriptor_reset (&g_array_index + (demux->cdci_picture_essence_descriptor, + MXFMetadataCDCIPictureEssenceDescriptor, i)); + g_array_free (demux->cdci_picture_essence_descriptor, TRUE); + demux->cdci_picture_essence_descriptor = NULL; + } + + if (demux->mpeg_video_descriptor) { + for (i = 0; i < demux->mpeg_video_descriptor->len; i++) + mxf_metadata_mpeg_video_descriptor_reset (&g_array_index + (demux->mpeg_video_descriptor, MXFMetadataMPEGVideoDescriptor, i)); + g_array_free (demux->mpeg_video_descriptor, TRUE); + demux->mpeg_video_descriptor = NULL; + } + + if (demux->generic_sound_essence_descriptor) { + for (i = 0; i < demux->generic_sound_essence_descriptor->len; i++) + mxf_metadata_generic_sound_essence_descriptor_reset (&g_array_index + (demux->generic_sound_essence_descriptor, + MXFMetadataGenericSoundEssenceDescriptor, i)); + g_array_free (demux->generic_sound_essence_descriptor, TRUE); + demux->generic_sound_essence_descriptor = NULL; + } + + if (demux->wave_audio_essence_descriptor) { + for (i = 0; i < demux->wave_audio_essence_descriptor->len; i++) + mxf_metadata_wave_audio_essence_descriptor_reset (&g_array_index + (demux->wave_audio_essence_descriptor, + MXFMetadataWaveAudioEssenceDescriptor, i)); + g_array_free (demux->wave_audio_essence_descriptor, TRUE); + demux->wave_audio_essence_descriptor = NULL; + } + + if (demux->descriptor) { + g_ptr_array_free (demux->descriptor, TRUE); + demux->descriptor = NULL; + } + + if (demux->locator) { + for (i = 0; i < demux->locator->len; i++) + mxf_metadata_locator_reset (&g_array_index (demux->locator, + MXFMetadataLocator, i)); + g_array_free (demux->locator, TRUE); + demux->locator = NULL; + } +} + +static void +gst_mxf_demux_reset (GstMXFDemux * demux) +{ + GST_DEBUG_OBJECT (demux, "cleaning up MXF demuxer"); + + demux->flushing = FALSE; + + demux->header_partition_pack_offset = 0; + demux->footer_partition_pack_offset = 0; + demux->offset = 0; + + demux->run_in = -1; + + memset (&demux->current_package_uid, 0, sizeof (MXFUMID)); + + if (demux->new_seg_event) { + gst_event_unref (demux->new_seg_event); + demux->new_seg_event = NULL; + } + + if (demux->close_seg_event) { + gst_event_unref (demux->close_seg_event); + demux->close_seg_event = NULL; + } + + gst_adapter_clear (demux->adapter); + + if (demux->src) { + g_ptr_array_foreach (demux->src, (GFunc) gst_mxf_demux_remove_pad, demux); + g_ptr_array_foreach (demux->src, (GFunc) gst_object_unref, NULL); + g_ptr_array_free (demux->src, TRUE); + demux->src = NULL; + } + + gst_mxf_demux_reset_mxf_state (demux); + gst_mxf_demux_reset_metadata (demux); +} + +static GstFlowReturn +gst_mxf_demux_pull_range (GstMXFDemux * demux, guint64 offset, + guint size, GstBuffer ** buffer) +{ + GstFlowReturn ret; + + ret = gst_pad_pull_range (demux->sinkpad, offset, size, buffer); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + GST_WARNING_OBJECT (demux, + "failed when pulling %d bytes from offset %" G_GUINT64_FORMAT ": %s", + size, offset, gst_flow_get_name (ret)); + *buffer = NULL; + return ret; + } + + if (G_UNLIKELY (*buffer && GST_BUFFER_SIZE (*buffer) != size)) { + GST_WARNING_OBJECT (demux, + "partial pull got %d when expecting %d from offset %" G_GUINT64_FORMAT, + GST_BUFFER_SIZE (*buffer), size, offset); + gst_buffer_unref (*buffer); + ret = GST_FLOW_UNEXPECTED; + *buffer = NULL; + return ret; + } + + return ret; +} + +static gboolean +gst_mxf_demux_push_src_event (GstMXFDemux * demux, GstEvent * event) +{ + gboolean ret = TRUE; + gint i; + + GST_DEBUG_OBJECT (demux, "Pushing '%s' event downstream", + GST_EVENT_TYPE_NAME (event)); + + if (!demux->src) + return ret; + + for (i = 0; i < demux->src->len; i++) { + GstPad *pad = GST_PAD (g_ptr_array_index (demux->src, i)); + + ret |= gst_pad_push_event (pad, gst_event_ref (event)); + } + + gst_event_unref (event); + + return ret; +} + +static GstFlowReturn +gst_mxf_demux_handle_partition_pack (GstMXFDemux * demux, const MXFUL * key, + GstBuffer * buffer) +{ + if (demux->partition.valid) { + mxf_partition_pack_reset (&demux->partition); + mxf_primer_pack_reset (&demux->primer); + } + + GST_DEBUG_OBJECT (demux, + "Handling partition pack of size %u at offset %" + G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_partition_pack_parse (key, &demux->partition, + GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing partition pack failed"); + return GST_FLOW_ERROR; + } + + if (demux->partition.type == MXF_PARTITION_PACK_HEADER) + demux->footer_partition_pack_offset = demux->partition.footer_partition; + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_primer_pack (GstMXFDemux * demux, const MXFUL * key, + GstBuffer * buffer) +{ + GST_DEBUG_OBJECT (demux, + "Handling primer pack of size %u at offset %" + G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (G_UNLIKELY (!demux->partition.valid)) { + GST_ERROR_OBJECT (demux, "Primer pack before partition pack"); + return GST_FLOW_ERROR; + } + + if (G_UNLIKELY (demux->primer.valid)) { + GST_ERROR_OBJECT (demux, "Primer pack already exists"); + return GST_FLOW_ERROR; + } + + if (!mxf_primer_pack_parse (key, &demux->primer, + GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing primer pack failed"); + return GST_FLOW_ERROR; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_preface (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + MXFMetadataPreface preface; + + GST_DEBUG_OBJECT (demux, + "Handling metadata preface of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (demux->final_metadata) { + GST_DEBUG_OBJECT (demux, "Metadata is already final, skipping"); + return GST_FLOW_OK; + } + + if (!mxf_metadata_preface_parse (key, &preface, &demux->primer, + GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata preface failed"); + return GST_FLOW_ERROR; + } + + if (mxf_timestamp_is_unknown (&demux->preface.last_modified_date) + || (!mxf_timestamp_is_unknown (&preface.last_modified_date) + && mxf_timestamp_compare (&demux->preface.last_modified_date, + &preface.last_modified_date) < 0)) { + GST_DEBUG_OBJECT (demux, + "Timestamp of new preface is newer than old, updating metadata"); + gst_mxf_demux_reset_metadata (demux); + memcpy (&demux->preface, &preface, sizeof (MXFMetadataPreface)); + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_identification (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + MXFMetadataIdentification identification; + + GST_DEBUG_OBJECT (demux, + "Handling metadata identification of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_metadata_identification_parse (key, &identification, + &demux->primer, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata identification failed"); + return GST_FLOW_ERROR; + } + + if (!demux->identification) + demux->identification = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataIdentification)); + + g_array_append_val (demux->identification, identification); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_content_storage (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + GST_DEBUG_OBJECT (demux, + "Handling metadata content storage of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_metadata_content_storage_parse (key, + &demux->content_storage, &demux->primer, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata content storage failed"); + return GST_FLOW_ERROR; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_essence_container_data (GstMXFDemux * + demux, const MXFUL * key, GstBuffer * buffer) +{ + MXFMetadataEssenceContainerData essence_container_data; + + GST_DEBUG_OBJECT (demux, + "Handling metadata essence container data of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_metadata_essence_container_data_parse (key, + &essence_container_data, &demux->primer, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata essence container data failed"); + return GST_FLOW_ERROR; + } + + if (!demux->essence_container_data) + demux->essence_container_data = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataEssenceContainerData)); + + g_array_append_val (demux->essence_container_data, essence_container_data); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_material_package (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + MXFMetadataGenericPackage material_package; + + GST_DEBUG_OBJECT (demux, + "Handling metadata material package of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_metadata_generic_package_parse (key, &material_package, + &demux->primer, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata material package failed"); + return GST_FLOW_ERROR; + } + + if (!demux->material_package) + demux->material_package = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataMaterialPackage)); + + g_array_append_val (demux->material_package, material_package); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_source_package (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + MXFMetadataGenericPackage source_package; + + GST_DEBUG_OBJECT (demux, + "Handling metadata source package of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_metadata_generic_package_parse (key, &source_package, + &demux->primer, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata source package failed"); + return GST_FLOW_ERROR; + } + + if (!demux->source_package) + demux->source_package = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataSourcePackage)); + + g_array_append_val (demux->source_package, source_package); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_track (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + MXFMetadataTrack track; + + GST_DEBUG_OBJECT (demux, + "Handling metadata track of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_metadata_track_parse (key, &track, &demux->primer, + GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata track timecode failed"); + return GST_FLOW_ERROR; + } + + if (!demux->track) + demux->track = g_array_new (FALSE, FALSE, sizeof (MXFMetadataTrack)); + + g_array_append_val (demux->track, track); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_sequence (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + MXFMetadataSequence sequence; + + GST_DEBUG_OBJECT (demux, + "Handling metadata sequence of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_metadata_sequence_parse (key, &sequence, + &demux->primer, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata sequence failed"); + return GST_FLOW_ERROR; + } + + if (!demux->sequence) + demux->sequence = g_array_new (FALSE, FALSE, sizeof (MXFMetadataSequence)); + + g_array_append_val (demux->sequence, sequence); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_structural_component (GstMXFDemux * demux, + const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataStructuralComponent component; + + GST_DEBUG_OBJECT (demux, + "Handling metadata structural component of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!mxf_metadata_structural_component_parse (key, &component, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata structural component failed"); + return GST_FLOW_ERROR; + } + + if (!demux->structural_component) + demux->structural_component = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataStructuralComponent)); + + g_array_append_val (demux->structural_component, component); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_generic_descriptor (GstMXFDemux * demux, + const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataGenericDescriptor descriptor; + + GST_DEBUG_OBJECT (demux, + "Handling metadata generic descriptor of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_generic_descriptor_parse (key, &descriptor, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata generic descriptor failed"); + return GST_FLOW_ERROR; + } + + if (!demux->generic_descriptor) + demux->generic_descriptor = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataGenericDescriptor)); + + g_array_append_val (demux->generic_descriptor, descriptor); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_file_descriptor (GstMXFDemux * demux, + const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataFileDescriptor descriptor; + + GST_DEBUG_OBJECT (demux, + "Handling metadata file descriptor of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_file_descriptor_parse (key, &descriptor, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata file descriptor failed"); + return GST_FLOW_ERROR; + } + + if (!demux->file_descriptor) + demux->file_descriptor = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataFileDescriptor)); + + g_array_append_val (demux->file_descriptor, descriptor); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_multiple_descriptor (GstMXFDemux * demux, + const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataMultipleDescriptor descriptor; + + GST_DEBUG_OBJECT (demux, + "Handling metadata multiple descriptor of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_multiple_descriptor_parse (key, &descriptor, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata multiple descriptor failed"); + return GST_FLOW_ERROR; + } + + if (!demux->multiple_descriptor) + demux->multiple_descriptor = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataMultipleDescriptor)); + + g_array_append_val (demux->multiple_descriptor, descriptor); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_generic_picture_essence_descriptor (GstMXFDemux * + demux, const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataGenericPictureEssenceDescriptor descriptor; + + GST_DEBUG_OBJECT (demux, + "Handling metadata generic picture essence descriptor of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_generic_picture_essence_descriptor_parse (key, &descriptor, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, + "Parsing metadata generic picture essence descriptor failed"); + return GST_FLOW_ERROR; + } + + if (!demux->generic_picture_essence_descriptor) + demux->generic_picture_essence_descriptor = + g_array_new (FALSE, FALSE, + sizeof (MXFMetadataGenericPictureEssenceDescriptor)); + + g_array_append_val (demux->generic_picture_essence_descriptor, descriptor); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_cdci_picture_essence_descriptor (GstMXFDemux * + demux, const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataCDCIPictureEssenceDescriptor descriptor; + + GST_DEBUG_OBJECT (demux, + "Handling metadata CDCI picture essence descriptor of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_cdci_picture_essence_descriptor_parse (key, &descriptor, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, + "Parsing metadata CDCI picture essence descriptor failed"); + return GST_FLOW_ERROR; + } + + if (!demux->cdci_picture_essence_descriptor) + demux->cdci_picture_essence_descriptor = + g_array_new (FALSE, FALSE, + sizeof (MXFMetadataCDCIPictureEssenceDescriptor)); + + g_array_append_val (demux->cdci_picture_essence_descriptor, descriptor); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_mpeg_video_descriptor (GstMXFDemux * demux, + const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataMPEGVideoDescriptor descriptor; + + GST_DEBUG_OBJECT (demux, + "Handling metadata MPEG video descriptor of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_mpeg_video_descriptor_parse (key, &descriptor, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata MPEG video descriptor failed"); + return GST_FLOW_ERROR; + } + + if (!demux->mpeg_video_descriptor) + demux->mpeg_video_descriptor = + g_array_new (FALSE, FALSE, sizeof (MXFMetadataMPEGVideoDescriptor)); + + g_array_append_val (demux->mpeg_video_descriptor, descriptor); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_generic_sound_essence_descriptor (GstMXFDemux * + demux, const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataGenericSoundEssenceDescriptor descriptor; + + GST_DEBUG_OBJECT (demux, + "Handling metadata generic sound essence descriptor of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_generic_sound_essence_descriptor_parse (key, &descriptor, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, + "Parsing metadata generic sound essence descriptor failed"); + return GST_FLOW_ERROR; + } + + if (!demux->generic_sound_essence_descriptor) + demux->generic_sound_essence_descriptor = + g_array_new (FALSE, FALSE, + sizeof (MXFMetadataGenericSoundEssenceDescriptor)); + + g_array_append_val (demux->generic_sound_essence_descriptor, descriptor); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_wave_audio_essence_descriptor (GstMXFDemux * + demux, const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataWaveAudioEssenceDescriptor descriptor; + + GST_DEBUG_OBJECT (demux, + "Handling metadata wave sound essence descriptor of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_wave_audio_essence_descriptor_parse (key, &descriptor, + &demux->primer, type, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, + "Parsing metadata wave sound essence descriptor failed"); + return GST_FLOW_ERROR; + } + + if (!demux->wave_audio_essence_descriptor) + demux->wave_audio_essence_descriptor = + g_array_new (FALSE, FALSE, + sizeof (MXFMetadataWaveAudioEssenceDescriptor)); + + g_array_append_val (demux->wave_audio_essence_descriptor, descriptor); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata_locator (GstMXFDemux * demux, + const MXFUL * key, guint16 type, GstBuffer * buffer) +{ + MXFMetadataLocator locator; + + GST_DEBUG_OBJECT (demux, + "Handling metadata locator of size %u" + " at offset %" G_GUINT64_FORMAT " with type 0x%04d", + GST_BUFFER_SIZE (buffer), demux->offset, type); + + if (!mxf_metadata_locator_parse (key, &locator, &demux->primer, + type, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + GST_ERROR_OBJECT (demux, "Parsing metadata locator failed"); + return GST_FLOW_ERROR; + } + + if (!demux->locator) + demux->locator = g_array_new (FALSE, FALSE, sizeof (MXFMetadataLocator)); + + g_array_append_val (demux->locator, locator); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_header_metadata_resolve_references (GstMXFDemux * demux) +{ + guint i, j, k; + GstFlowReturn ret = GST_FLOW_OK; + + GST_DEBUG_OBJECT (demux, "Resolve metadata references"); + demux->update_metadata = FALSE; + if (demux->partition.closed && demux->partition.complete) + demux->final_metadata = TRUE; + + /* Fill in demux->descriptor */ + demux->descriptor = g_ptr_array_new (); + if (demux->generic_descriptor) { + for (i = 0; i < demux->generic_descriptor->len; i++) { + g_ptr_array_add (demux->descriptor, + &g_array_index (demux->generic_descriptor, + MXFMetadataGenericDescriptor, i)); + } + } + if (demux->file_descriptor) { + for (i = 0; i < demux->file_descriptor->len; i++) { + g_ptr_array_add (demux->descriptor, + &g_array_index (demux->file_descriptor, MXFMetadataFileDescriptor, + i)); + } + } + if (demux->generic_picture_essence_descriptor) { + for (i = 0; i < demux->generic_picture_essence_descriptor->len; i++) { + g_ptr_array_add (demux->descriptor, + &g_array_index (demux->generic_picture_essence_descriptor, + MXFMetadataGenericPictureEssenceDescriptor, i)); + } + } + if (demux->cdci_picture_essence_descriptor) { + for (i = 0; i < demux->cdci_picture_essence_descriptor->len; i++) { + g_ptr_array_add (demux->descriptor, + &g_array_index (demux->cdci_picture_essence_descriptor, + MXFMetadataCDCIPictureEssenceDescriptor, i)); + } + } + if (demux->mpeg_video_descriptor) { + for (i = 0; i < demux->mpeg_video_descriptor->len; i++) { + g_ptr_array_add (demux->descriptor, + &g_array_index (demux->mpeg_video_descriptor, + MXFMetadataMPEGVideoDescriptor, i)); + } + } + if (demux->generic_sound_essence_descriptor) { + for (i = 0; i < demux->generic_sound_essence_descriptor->len; i++) { + g_ptr_array_add (demux->descriptor, + &g_array_index (demux->generic_sound_essence_descriptor, + MXFMetadataGenericSoundEssenceDescriptor, i)); + } + } + if (demux->wave_audio_essence_descriptor) { + for (i = 0; i < demux->wave_audio_essence_descriptor->len; i++) { + g_ptr_array_add (demux->descriptor, + &g_array_index (demux->wave_audio_essence_descriptor, + MXFMetadataWaveAudioEssenceDescriptor, i)); + } + } + if (demux->multiple_descriptor) { + for (i = 0; i < demux->multiple_descriptor->len; i++) { + g_ptr_array_add (demux->descriptor, + &g_array_index (demux->multiple_descriptor, + MXFMetadataMultipleDescriptor, i)); + } + } + + /* Fill in demux->package */ + demux->package = g_ptr_array_new (); + if (demux->material_package) { + for (i = 0; i < demux->material_package->len; i++) { + g_ptr_array_add (demux->package, &g_array_index (demux->material_package, + MXFMetadataGenericPackage, i)); + } + } + if (demux->source_package) { + for (i = 0; i < demux->source_package->len; i++) { + g_ptr_array_add (demux->package, &g_array_index (demux->source_package, + MXFMetadataGenericPackage, i)); + } + } + + /* Multiple descriptor */ + if (demux->multiple_descriptor && demux->descriptor) { + for (i = 0; i < demux->multiple_descriptor->len; i++) { + MXFMetadataMultipleDescriptor *d = + &g_array_index (demux->multiple_descriptor, + MXFMetadataMultipleDescriptor, i); + + d->sub_descriptors = + g_new0 (MXFMetadataGenericDescriptor *, d->n_sub_descriptors); + for (j = 0; j < d->n_sub_descriptors; j++) { + for (k = 0; k < demux->descriptor->len; k++) { + MXFMetadataGenericDescriptor *e = + g_ptr_array_index (demux->descriptor, k); + + if (mxf_ul_is_equal (&d->sub_descriptors_uids[j], &e->instance_uid)) { + d->sub_descriptors[j] = e; + break; + } + } + } + } + } + + /* See SMPTE 377M 8.4 */ + + /* Preface */ + if (demux->package) { + for (i = 0; i < demux->package->len; i++) { + MXFMetadataGenericPackage *package = + g_ptr_array_index (demux->package, i); + + if (mxf_ul_is_equal (&demux->preface.primary_package_uid, + &package->instance_uid)) { + demux->preface.primary_package = package; + break; + } + } + } + + demux->preface.identifications = + g_new0 (MXFMetadataIdentification *, demux->preface.n_identifications); + if (demux->identification) { + for (i = 0; i < demux->identification->len; i++) { + MXFMetadataIdentification *identification = + &g_array_index (demux->identification, + MXFMetadataIdentification, i); + + for (j = 0; j < demux->preface.n_identifications; j++) { + if (mxf_ul_is_equal (&demux->preface.identifications_uids[j], + &identification->instance_uid)) { + demux->preface.identifications[j] = identification; + break; + } + } + } + } + + if (!mxf_ul_is_equal (&demux->preface.content_storage_uid, + &demux->content_storage.instance_uid)) { + GST_ERROR_OBJECT (demux, + "Preface content storage UID not equal to actual content storage instance uid"); + ret = GST_FLOW_ERROR; + goto error; + } + demux->preface.content_storage = &demux->content_storage; + + /* TODO: dm_schemes */ + + /* Content storage */ + demux->content_storage.packages = + g_new0 (MXFMetadataGenericPackage *, demux->content_storage.n_packages); + if (demux->package) { + for (i = 0; i < demux->package->len; i++) { + MXFMetadataGenericPackage *package = + g_ptr_array_index (demux->package, i); + + for (j = 0; j < demux->content_storage.n_packages; j++) { + if (mxf_ul_is_equal (&demux->content_storage.packages_uids[j], + &package->instance_uid)) { + demux->content_storage.packages[j] = package; + break; + } + } + } + } + + demux->content_storage.essence_container_data = + g_new0 (MXFMetadataEssenceContainerData *, + demux->content_storage.n_essence_container_data); + if (demux->essence_container_data) { + for (i = 0; i < demux->essence_container_data->len; i++) { + MXFMetadataEssenceContainerData *data = + &g_array_index (demux->essence_container_data, + MXFMetadataEssenceContainerData, i); + + for (j = 0; j < demux->content_storage.n_essence_container_data; j++) { + if (mxf_ul_is_equal (&demux-> + content_storage.essence_container_data_uids[j], + &data->instance_uid)) { + demux->content_storage.essence_container_data[j] = data; + break; + } + } + } + } + + /* Essence container data */ + if (demux->package && demux->essence_container_data) { + for (i = 0; i < demux->package->len; i++) { + MXFMetadataGenericPackage *package = + g_ptr_array_index (demux->package, i); + + for (j = 0; j < demux->essence_container_data->len; j++) { + MXFMetadataEssenceContainerData *data = + &g_array_index (demux->essence_container_data, + MXFMetadataEssenceContainerData, j); + + if (mxf_umid_is_equal (&data->linked_package_uid, + &package->package_uid)) { + data->linked_package = package; + break; + } + } + } + } + + /* Generic package */ + if (demux->package) { + for (i = 0; i < demux->package->len; i++) { + MXFMetadataGenericPackage *package = + g_ptr_array_index (demux->package, i); + + package->tracks = g_new0 (MXFMetadataTrack *, package->n_tracks); + if (demux->track) { + for (j = 0; j < package->n_tracks; j++) { + for (k = 0; k < demux->track->len; k++) { + MXFMetadataTrack *track = + &g_array_index (demux->track, MXFMetadataTrack, k); + + if (mxf_ul_is_equal (&package->tracks_uids[j], + &track->instance_uid)) { + package->tracks[j] = track; + break; + } + } + } + } + + /* Resolve descriptors */ + if (package->n_descriptors > 0 && demux->descriptor) { + MXFMetadataGenericDescriptor *d = NULL; + + for (j = 0; j < demux->descriptor->len; j++) { + MXFMetadataGenericDescriptor *descriptor = + g_ptr_array_index (demux->descriptor, j); + + if (mxf_ul_is_equal (&package->descriptors_uid, + &descriptor->instance_uid)) { + d = descriptor; + break; + } + } + + if (d && d->type == MXF_METADATA_MULTIPLE_DESCRIPTOR) { + MXFMetadataMultipleDescriptor *e = + (MXFMetadataMultipleDescriptor *) d; + + package->n_descriptors = e->n_sub_descriptors; + package->descriptors = + g_new0 (MXFMetadataGenericDescriptor *, e->n_sub_descriptors); + + /* These are already resolved */ + for (j = 0; j < e->n_sub_descriptors; j++) + package->descriptors[j] = e->sub_descriptors[j]; + } else { + package->n_descriptors = 1; + package->descriptors = g_new0 (MXFMetadataGenericDescriptor *, 1); + package->descriptors[0] = d; + } + } + + if (package->tracks && package->descriptors) { + for (j = 0; j < package->n_tracks; j++) { + MXFMetadataTrack *track = package->tracks[j]; + guint n_descriptor = 0; + + if (!track) + continue; + + for (k = 0; k < package->n_descriptors; k++) { + MXFMetadataGenericDescriptor *d = package->descriptors[k]; + MXFMetadataFileDescriptor *e; + + if (!d || !d->is_file_descriptor) + continue; + + e = (MXFMetadataFileDescriptor *) d; + + if (e->linked_track_id == track->track_id) + n_descriptor++; + } + + track->n_descriptor = n_descriptor; + track->descriptor = + g_new0 (MXFMetadataFileDescriptor *, n_descriptor); + n_descriptor = 0; + + for (k = 0; k < package->n_descriptors; k++) { + MXFMetadataGenericDescriptor *d = package->descriptors[k]; + MXFMetadataFileDescriptor *e; + + if (!d || !d->is_file_descriptor) + continue; + + e = (MXFMetadataFileDescriptor *) d; + + if (e->linked_track_id == track->track_id) + track->descriptor[n_descriptor++] = e; + } + } + } + } + } + + /* Tracks */ + if (demux->track && demux->sequence) { + for (i = 0; i < demux->track->len; i++) { + MXFMetadataTrack *track = + &g_array_index (demux->track, MXFMetadataTrack, i); + + for (j = 0; j < demux->sequence->len; j++) { + MXFMetadataSequence *sequence = + &g_array_index (demux->sequence, MXFMetadataSequence, + i); + + if (mxf_ul_is_equal (&track->sequence_uid, &sequence->instance_uid)) { + track->sequence = sequence; + break; + } + } + } + } + + /* Sequences */ + if (demux->sequence) { + for (i = 0; i < demux->sequence->len; i++) { + MXFMetadataSequence *sequence = + &g_array_index (demux->sequence, MXFMetadataSequence, i); + + sequence->structural_components = + g_new0 (MXFMetadataStructuralComponent *, + sequence->n_structural_components); + + if (demux->structural_component) { + for (j = 0; j < sequence->n_structural_components; j++) { + for (k = 0; k < demux->structural_component->len; k++) { + MXFMetadataStructuralComponent *component = + &g_array_index (demux->structural_component, + MXFMetadataStructuralComponent, k); + + if (mxf_ul_is_equal (&sequence->structural_components_uids[j], + &component->instance_uid)) { + sequence->structural_components[j] = component; + break; + } + } + } + } + } + } + + /* Source clips */ + if (demux->structural_component && demux->source_package) { + for (i = 0; i < demux->structural_component->len; i++) { + MXFMetadataStructuralComponent *component = + &g_array_index (demux->structural_component, + MXFMetadataStructuralComponent, i); + + if (component->type != MXF_METADATA_SOURCE_CLIP) + continue; + + for (j = 0; j < demux->source_package->len; j++) { + MXFMetadataGenericPackage *package = + &g_array_index (demux->source_package, MXFMetadataGenericPackage, + j); + + if (mxf_umid_is_equal (&component->source_clip.source_package_id, + &package->package_uid)) { + component->source_clip.source_package = package; + break; + } + } + } + } + + /* Generic descriptors */ + if (demux->descriptor) { + for (i = 0; i < demux->descriptor->len; i++) { + MXFMetadataGenericDescriptor *descriptor = + g_ptr_array_index (demux->descriptor, i); + + descriptor->locators = + g_new0 (MXFMetadataLocator *, descriptor->n_locators); + + if (demux->locator) { + for (j = 0; j < descriptor->n_locators; j++) { + for (k = 0; k < demux->locator->len; k++) { + MXFMetadataLocator *locator = + &g_array_index (demux->locator, MXFMetadataLocator, + k); + + if (mxf_ul_is_equal (&descriptor->locators_uids[j], + &locator->instance_uid)) { + descriptor->locators[j] = locator; + break; + } + } + } + } + } + } + + /* Mark packages as material, top-level source and source */ + if (demux->material_package) { + for (i = 0; i < demux->material_package->len; i++) { + MXFMetadataGenericPackage *package = + &g_array_index (demux->material_package, MXFMetadataGenericPackage, + i); + + package->type = MXF_METADATA_GENERIC_PACKAGE_MATERIAL; + + for (j = 0; j < package->n_tracks; j++) { + MXFMetadataTrack *track = package->tracks[j]; + MXFMetadataSequence *sequence; + + if (!track || !track->sequence) + continue; + + sequence = track->sequence; + + for (k = 0; k < sequence->n_structural_components; k++) { + MXFMetadataStructuralComponent *component = + sequence->structural_components[k]; + + if (!component || component->type != MXF_METADATA_SOURCE_CLIP + || !component->source_clip.source_package) + continue; + + component->source_clip.source_package->type = + MXF_METADATA_GENERIC_PACKAGE_TOP_LEVEL_SOURCE; + } + } + } + } + + /* Store, for every package, the number of timestamp, metadata, essence and other tracks */ + if (demux->package) { + for (i = 0; i < demux->package->len; i++) { + MXFMetadataGenericPackage *package = + g_ptr_array_index (demux->package, i); + + if (!package->tracks || package->n_tracks == 0) + continue; + + for (j = 0; j < package->n_tracks; j++) { + MXFMetadataTrack *track = package->tracks[j]; + MXFMetadataSequence *sequence; + MXFMetadataTrackType type = MXF_METADATA_TRACK_UNKNOWN; + + if (!track || !track->sequence) + continue; + + sequence = track->sequence; + + if (mxf_ul_is_zero (&sequence->data_definition) + && sequence->structural_components) { + for (k = 0; k < sequence->n_structural_components; k++) { + MXFMetadataStructuralComponent *component = + sequence->structural_components[k]; + if (!component || mxf_ul_is_zero (&component->data_definition)) + continue; + + type = + mxf_metadata_track_identifier_parse (&component-> + data_definition); + break; + } + } else { + type = + mxf_metadata_track_identifier_parse (&sequence->data_definition); + } + + if (type == MXF_METADATA_TRACK_UNKNOWN) + continue; + else if ((type & 0xf0) == 0x10) + package->n_timecode_tracks++; + else if ((type & 0xf0) == 0x20) + package->n_metadata_tracks++; + else if ((type & 0xf0) == 0x30) + package->n_essence_tracks++; + else if ((type & 0xf0) == 0x40) + package->n_other_tracks++; + } + } + } + + return ret; + +error: + return ret; +} + +static GstFlowReturn +gst_mxf_demux_handle_header_metadata_update_streams (GstMXFDemux * demux) +{ + MXFMetadataGenericPackage *current_package = NULL; + guint i, j, k; + gboolean first_run; + + GST_DEBUG_OBJECT (demux, "Updating streams"); + +choose_package: + /* If no package was selected, select the first material package */ + if (mxf_umid_is_zero (&demux->current_package_uid) + && !demux->material_package) { + GST_ERROR_OBJECT (demux, "No material package"); + return GST_FLOW_ERROR; + } else if (mxf_umid_is_zero (&demux->current_package_uid)) { + MXFMetadataGenericPackage *p = + (MXFMetadataGenericPackage *) demux->material_package->data; + memcpy (&demux->current_package_uid, &p->package_uid, 32); + current_package = p; + if (demux->src) { + for (i = 0; i < demux->src->len; i++) { + GstMXFPad *pad = g_ptr_array_index (demux->src, i); + gst_element_remove_pad (GST_ELEMENT_CAST (demux), GST_PAD_CAST (pad)); + gst_object_unref (pad); + } + g_ptr_array_free (demux->src, TRUE); + demux->src = NULL; + } + } + + if (!current_package && demux->package) { + for (i = 0; i < demux->package->len; i++) { + MXFMetadataGenericPackage *p = g_ptr_array_index (demux->package, i); + + if (mxf_umid_is_equal (&p->package_uid, &demux->current_package_uid)) { + current_package = p; + break; + } + } + } + + if (!current_package) { + GST_WARNING_OBJECT (demux, + "Selected package not found in header metadata, choosing the first best"); + memset (&demux->current_package_uid, 0, sizeof (MXFUMID)); + goto choose_package; + } + + if (current_package->type == MXF_METADATA_GENERIC_PACKAGE_SOURCE) { + GST_WARNING_OBJECT (demux, + "Selected package is not a material package or top-level source package, choosing the first best"); + memset (&demux->current_package_uid, 0, sizeof (MXFUMID)); + goto choose_package; + } + + if (!current_package->tracks) { + GST_ERROR_OBJECT (demux, "Current package has no (resolved) tracks"); + return GST_FLOW_ERROR; + } else if (!current_package->n_essence_tracks) { + GST_ERROR_OBJECT (demux, "Current package has no essence tracks"); + return GST_FLOW_ERROR; + } + + first_run = (demux->src == NULL); + demux->current_package = current_package; + + for (i = 0; i < current_package->n_tracks; i++) { + MXFMetadataTrack *track = current_package->tracks[i]; + MXFMetadataTrackType track_type = MXF_METADATA_TRACK_UNKNOWN; + MXFMetadataSequence *sequence; + MXFMetadataStructuralComponent *component = NULL; + MXFMetadataGenericPackage *source_package = NULL; + MXFMetadataTrack *source_track = NULL; + GstMXFPad *pad = NULL; + GstCaps *caps = NULL; + + GST_DEBUG_OBJECT (demux, "Handling track %d", i); + + if (!track) { + GST_WARNING_OBJECT (demux, "Unresolved track"); + continue; + } else if (!track->sequence) { + GST_WARNING_OBJECT (demux, "Track with no sequence"); + continue; + } + + sequence = track->sequence; + + if (current_package->type == MXF_METADATA_GENERIC_PACKAGE_TOP_LEVEL_SOURCE) { + source_package = current_package; + source_track = track; + } + + track_type = + mxf_metadata_track_identifier_parse (&sequence->data_definition); + + /* TODO: handle multiple components, see SMPTE 377M page 37 */ + if (sequence->structural_components && sequence->structural_components[0]) { + component = sequence->structural_components[0]; + + if (track_type == MXF_METADATA_TRACK_UNKNOWN) + track_type = + mxf_metadata_track_identifier_parse (&component->data_definition); + + if (!source_package && component->type == MXF_METADATA_SOURCE_CLIP + && component->source_clip.source_package + && component->source_clip.source_package->type == + MXF_METADATA_GENERIC_PACKAGE_TOP_LEVEL_SOURCE + && component->source_clip.source_package->tracks) { + source_package = component->source_clip.source_package; + + for (k = 0; k < source_package->n_tracks; k++) { + MXFMetadataTrack *tmp = source_package->tracks[k]; + + if (tmp->track_id == component->source_clip.source_track_id) { + source_track = tmp; + break; + } + } + } + } + + if (track_type && (track_type & 0xf0) != 0x30) { + GST_DEBUG_OBJECT (demux, "No essence track"); + continue; + } + + if (!source_package || track_type == MXF_METADATA_TRACK_UNKNOWN + || !source_track || !component) { + GST_WARNING_OBJECT (demux, + "No source package or track type for track found"); + continue; + } + + if (!source_package->descriptors) { + GST_WARNING_OBJECT (demux, "Source package has no descriptors"); + continue; + } + + if (!source_track->descriptor) { + GST_WARNING_OBJECT (demux, "No descriptor found for track"); + continue; + } + + if (demux->src && demux->src->len > 0) { + /* Find pad from track_id */ + for (j = 0; j < demux->src->len; j++) { + GstMXFPad *tmp = g_ptr_array_index (demux->src, j); + + if (tmp->track_id == track->track_id) { + pad = tmp; + break; + } + } + } + + if (!pad && first_run) { + GstPadTemplate *templ; + gchar *pad_name; + + switch (track_type) { + case MXF_METADATA_TRACK_PICTURE_ESSENCE: + templ = + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (demux), + "video_%d"); + pad_name = g_strdup_printf ("video_%d", source_track->track_id); + break; + case MXF_METADATA_TRACK_SOUND_ESSENCE: + templ = + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (demux), + "audio_%d"); + pad_name = g_strdup_printf ("audio_%d", source_track->track_id); + break; + case MXF_METADATA_TRACK_DATA_ESSENCE: + templ = + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (demux), + "data_%d"); + pad_name = g_strdup_printf ("data_%d", source_track->track_id); + break; + default: + g_assert_not_reached (); + break; + } + + g_assert (templ != NULL); + + /* Create pad */ + pad = (GstMXFPad *) g_object_new (GST_TYPE_MXF_PAD, + "name", pad_name, "direction", GST_PAD_SRC, "template", templ, NULL); + pad->need_segment = TRUE; + g_free (pad_name); + } + + if (!pad) { + GST_WARNING_OBJECT (demux, + "Not the first pad addition run, ignoring new track"); + continue; + } + + /* Update pad */ + pad->track_id = track->track_id; + pad->track_type = track_type; + + pad->material_package = current_package; + pad->material_track = track; + pad->current_material_component = 0; + pad->component = component; + + pad->source_package = source_package; + pad->source_track = source_track; + + pad->handle_essence_element = NULL; + g_free (pad->mapping_data); + pad->mapping_data = NULL; + + switch (track_type) { + case MXF_METADATA_TRACK_PICTURE_ESSENCE: + if (mxf_is_mpeg_video_essence_track (source_track)) + caps = + mxf_mpeg_video_create_caps (source_package, source_track, + &pad->tags, &pad->handle_essence_element, &pad->mapping_data); + break; + case MXF_METADATA_TRACK_SOUND_ESSENCE: + if (mxf_is_aes_bwf_essence_track (source_track)) + caps = + mxf_aes_bwf_create_caps (source_package, source_track, &pad->tags, + &pad->handle_essence_element, &pad->mapping_data); + break; + case MXF_METADATA_TRACK_DATA_ESSENCE: + break; + default: + g_assert_not_reached (); + break; + } + + if (!caps) { + GST_WARNING_OBJECT (demux, "No caps created, ignoring stream"); + gst_object_unref (pad); + continue; + } + + GST_DEBUG_OBJECT (demux, "Created caps %" GST_PTR_FORMAT, caps); + + if (pad->caps && !gst_caps_is_equal (pad->caps, caps)) { + gst_pad_set_caps (GST_PAD_CAST (pad), caps); + gst_caps_replace (&pad->caps, gst_caps_ref (caps)); + } else if (!pad->caps) { + gst_pad_set_caps (GST_PAD_CAST (pad), caps); + pad->caps = gst_caps_ref (caps); + + gst_pad_set_event_function (GST_PAD_CAST (pad), + GST_DEBUG_FUNCPTR (gst_mxf_demux_src_event)); + + gst_pad_set_query_type_function (GST_PAD_CAST (pad), + GST_DEBUG_FUNCPTR (gst_mxf_demux_src_query_type)); + gst_pad_set_query_function (GST_PAD_CAST (pad), + GST_DEBUG_FUNCPTR (gst_mxf_demux_src_query)); + + gst_pad_use_fixed_caps (GST_PAD_CAST (pad)); + gst_pad_set_active (GST_PAD_CAST (pad), TRUE); + + gst_element_add_pad (GST_ELEMENT_CAST (demux), gst_object_ref (pad)); + + if (!demux->src) + demux->src = g_ptr_array_new (); + g_ptr_array_add (demux->src, pad); + } + gst_caps_unref (caps); + } + + gst_element_no_more_pads (GST_ELEMENT_CAST (demux)); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_metadata (GstMXFDemux * demux, const MXFUL * key, + GstBuffer * buffer) +{ + guint16 type; + GstFlowReturn ret = GST_FLOW_OK; + + type = GST_READ_UINT16_BE (key->u + 13); + + GST_DEBUG_OBJECT (demux, + "Handling metadata of size %u at offset %" + G_GUINT64_FORMAT " of type 0x%04d", GST_BUFFER_SIZE (buffer), + demux->offset, type); + + if (G_UNLIKELY (!demux->partition.valid)) { + GST_ERROR_OBJECT (demux, "Partition pack doesn't exist"); + return GST_FLOW_ERROR; + } + + if (G_UNLIKELY (!demux->primer.valid)) { + GST_ERROR_OBJECT (demux, "Primer pack doesn't exists"); + return GST_FLOW_ERROR; + } + + if (type != MXF_METADATA_PREFACE && !demux->update_metadata) { + GST_DEBUG_OBJECT (demux, + "Skipping parsing of metadata because it's older than what we have"); + return GST_FLOW_OK; + } + + /* Make writable as the parsing of descriptors sets already read local tags to 0x0000 */ + if (!gst_buffer_is_writable (buffer)) + buffer = gst_buffer_copy (buffer); + else + buffer = gst_buffer_ref (buffer); + + switch (type) { + case MXF_METADATA_PREFACE: + ret = gst_mxf_demux_handle_metadata_preface (demux, key, buffer); + break; + case MXF_METADATA_IDENTIFICATION: + ret = gst_mxf_demux_handle_metadata_identification (demux, key, buffer); + break; + case MXF_METADATA_CONTENT_STORAGE: + ret = gst_mxf_demux_handle_metadata_content_storage (demux, key, buffer); + break; + case MXF_METADATA_ESSENCE_CONTAINER_DATA: + ret = + gst_mxf_demux_handle_metadata_essence_container_data (demux, + key, buffer); + break; + case MXF_METADATA_MATERIAL_PACKAGE: + ret = gst_mxf_demux_handle_metadata_material_package (demux, key, buffer); + break; + case MXF_METADATA_SOURCE_PACKAGE: + ret = gst_mxf_demux_handle_metadata_source_package (demux, key, buffer); + break; + case MXF_METADATA_TRACK: + ret = gst_mxf_demux_handle_metadata_track (demux, key, buffer); + break; + case MXF_METADATA_SEQUENCE: + ret = gst_mxf_demux_handle_metadata_sequence (demux, key, buffer); + break; + case MXF_METADATA_TIMECODE_COMPONENT: + case MXF_METADATA_SOURCE_CLIP: + ret = + gst_mxf_demux_handle_metadata_structural_component (demux, + key, type, buffer); + break; + case MXF_METADATA_RGBA_PICTURE_ESSENCE_DESCRIPTOR: + case MXF_METADATA_GENERIC_DATA_ESSENCE_DESCRIPTOR: + ret = + gst_mxf_demux_handle_metadata_generic_descriptor (demux, + key, type, buffer); + break; + case MXF_METADATA_FILE_DESCRIPTOR: + ret = + gst_mxf_demux_handle_metadata_file_descriptor (demux, + key, type, buffer); + break; + case MXF_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR: + ret = + gst_mxf_demux_handle_metadata_generic_picture_essence_descriptor + (demux, key, type, buffer); + break; + case MXF_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR: + ret = + gst_mxf_demux_handle_metadata_cdci_picture_essence_descriptor (demux, + key, type, buffer); + break; + case MXF_METADATA_MPEG_VIDEO_DESCRIPTOR: + ret = + gst_mxf_demux_handle_metadata_mpeg_video_descriptor (demux, + key, type, buffer); + break; + case MXF_METADATA_GENERIC_SOUND_ESSENCE_DESCRIPTOR: + ret = + gst_mxf_demux_handle_metadata_generic_sound_essence_descriptor (demux, + key, type, buffer); + break; + case MXF_METADATA_MULTIPLE_DESCRIPTOR: + ret = + gst_mxf_demux_handle_metadata_multiple_descriptor (demux, + key, type, buffer); + break; + case MXF_METADATA_WAVE_AUDIO_ESSENCE_DESCRIPTOR: + ret = + gst_mxf_demux_handle_metadata_wave_audio_essence_descriptor (demux, + key, type, buffer); + break; + case MXF_METADATA_NETWORK_LOCATOR: + case MXF_METADATA_TEXT_LOCATOR: + ret = gst_mxf_demux_handle_metadata_locator (demux, key, type, buffer); + break; + default: + GST_WARNING_OBJECT (demux, + "Unknown or unhandled metadata type 0x%04x", type); + break; + } + + gst_buffer_unref (buffer); + + return ret; +} + +static GstFlowReturn +gst_mxf_demux_handle_generic_container_system_item (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + GST_DEBUG_OBJECT (demux, + "Handling generic container system item of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + /* TODO: parse this */ + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint32 track_number; + guint i; + GstMXFPad *pad = NULL; + GstBuffer *inbuf; + GstBuffer *outbuf = NULL; + + GST_DEBUG_OBJECT (demux, + "Handling generic container essence element of size %u" + " at offset %" G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + if (!demux->current_package) { + GST_ERROR_OBJECT (demux, "No package selected yet"); + return GST_FLOW_ERROR; + } + + if (!demux->src || demux->src->len == 0) { + GST_ERROR_OBJECT (demux, "No streams created yet"); + return GST_FLOW_ERROR; + } + + track_number = GST_READ_UINT32_BE (&key->u[12]); + + for (i = 0; i < demux->src->len; i++) { + GstMXFPad *p = g_ptr_array_index (demux->src, i); + + if (p->source_track->track_number == track_number || + (p->source_track->track_number == 0 && + demux->src->len == 1 && + demux->current_package->n_essence_tracks == 1)) { + pad = p; + break; + } + } + + if (!pad) { + GST_WARNING_OBJECT (demux, "No corresponding pad found"); + return GST_FLOW_OK; + } + + /* TODO: Use a better start value */ + if (pad->need_segment) { + gst_pad_push_event (GST_PAD_CAST (pad), + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0)); + pad->need_segment = FALSE; + } + + /* Create subbuffer to be able to change metadata */ + inbuf = gst_buffer_create_sub (buffer, 0, GST_BUFFER_SIZE (buffer)); + + GST_BUFFER_TIMESTAMP (inbuf) = + gst_util_uint64_scale (pad->essence_element_count + + pad->material_track->origin, + GST_SECOND * pad->material_track->edit_rate.d, + pad->material_track->edit_rate.n); + GST_BUFFER_DURATION (inbuf) = + gst_util_uint64_scale (GST_SECOND, pad->material_track->edit_rate.d, + pad->material_track->edit_rate.n); + GST_BUFFER_OFFSET (inbuf) = pad->essence_element_count; + GST_BUFFER_OFFSET_END (inbuf) = GST_BUFFER_OFFSET_NONE; + gst_buffer_set_caps (inbuf, pad->caps); + + if (pad->handle_essence_element) { + /* Takes ownership of inbuf */ + ret = + pad->handle_essence_element (key, inbuf, pad->caps, pad->source_package, + pad->source_track, pad->component, pad->mapping_data, &outbuf); + inbuf = NULL; + } else { + outbuf = inbuf; + inbuf = NULL; + ret = GST_FLOW_OK; + } + + pad->essence_element_count++; + + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (demux, "Failed to handle essence element"); + if (outbuf) { + gst_buffer_unref (outbuf); + outbuf = NULL; + } + } + + if (outbuf) { + ret = gst_pad_push (GST_PAD_CAST (pad), outbuf); + } + + return ret; +} + +static GstFlowReturn +gst_mxf_demux_handle_random_index_pack (GstMXFDemux * demux, const MXFUL * key, + GstBuffer * buffer) +{ + GST_DEBUG_OBJECT (demux, + "Handling random index pack of size %u at offset %" + G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + /* TODO: Parse this */ + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_handle_index_table_segment (GstMXFDemux * demux, + const MXFUL * key, GstBuffer * buffer) +{ + GST_DEBUG_OBJECT (demux, + "Handling index table segment of size %u at offset %" + G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + + /* TODO: Parse this */ + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_demux_pull_klv_packet (GstMXFDemux * demux, guint64 offset, MXFUL * key, + GstBuffer ** outbuf, guint64 * read) +{ + GstBuffer *buffer = NULL; + const guint8 *data; + guint64 data_offset = 0; + guint64 length; + gchar key_str[48]; + GstFlowReturn ret = GST_FLOW_OK; + + memset (key, 0, sizeof (MXFUL)); + + /* Pull 16 byte key and first byte of BER encoded length */ + if ((ret = + gst_mxf_demux_pull_range (demux, offset, 17, &buffer)) != GST_FLOW_OK) + goto beach; + + data = GST_BUFFER_DATA (buffer); + + if (!mxf_is_mxf_packet ((const MXFUL *) data)) { + GST_ERROR_OBJECT (demux, "Not an MXF packet, skipping. Key: %s", + mxf_ul_to_string ((const MXFUL *) data, key_str)); + ret = GST_FLOW_ERROR; + goto beach; + } + + memcpy (key, GST_BUFFER_DATA (buffer), 16); + + /* Decode BER encoded packet length */ + if ((data[16] & 0x80) == 0) { + length = data[16]; + data_offset = 17; + } else { + guint slen = data[16] & 0x7f; + + data_offset = 16 + 1 + slen; + + gst_buffer_unref (buffer); + + /* Must be at most 8 according to SMPTE-379M 5.3.4 */ + if (slen > 8) { + GST_ERROR_OBJECT (demux, "Invalid KLV packet length: %" G_GUINT64_FORMAT, + slen); + ret = GST_FLOW_ERROR; + goto beach; + } + + /* Now pull the length of the packet */ + if ((ret = gst_mxf_demux_pull_range (demux, offset + 17, slen, + &buffer)) != GST_FLOW_OK) + goto beach; + data = GST_BUFFER_DATA (buffer); + + length = 0; + while (slen) { + length = (length << 8) | *data; + data++; + slen--; + } + } + + gst_buffer_unref (buffer); + + /* Pull the complete KLV packet */ + if ((ret = gst_mxf_demux_pull_range (demux, offset + data_offset, length, + &buffer)) != GST_FLOW_OK) + goto beach; + + *outbuf = buffer; + buffer = NULL; + if (read) + *read = data_offset + length; + +beach: + if (buffer) + gst_buffer_unref (buffer); + + return ret; +} + +static void +gst_mxf_demux_parse_footer_metadata (GstMXFDemux * demux) +{ + MXFPartitionPack partition; + MXFPrimerPack primer; + guint64 offset, old_offset = demux->offset; + MXFUL key; + GstBuffer *buffer = NULL; + guint64 read = 0; + GstFlowReturn ret = GST_FLOW_OK; + + memcpy (&partition, &demux->partition, sizeof (MXFPartitionPack)); + memcpy (&primer, &demux->primer, sizeof (MXFPrimerPack)); + memset (&demux->partition, 0, sizeof (MXFPartitionPack)); + memset (&demux->primer, 0, sizeof (MXFPrimerPack)); + + gst_mxf_demux_reset_metadata (demux); + offset = + demux->header_partition_pack_offset + demux->footer_partition_pack_offset; + +next_try: + mxf_partition_pack_reset (&demux->partition); + mxf_primer_pack_reset (&demux->primer); + + ret = gst_mxf_demux_pull_klv_packet (demux, offset, &key, &buffer, &read); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto out; + + if (!mxf_is_partition_pack (&key)) + goto out; + + if (!mxf_partition_pack_parse (&key, &demux->partition, + GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) + goto out; + + offset += read; + gst_buffer_unref (buffer); + buffer = NULL; + + if (demux->partition.header_byte_count == 0) { + if (demux->partition.prev_partition == 0 + || demux->partition.this_partition == 0) + goto out; + + offset = + demux->header_partition_pack_offset + demux->partition.prev_partition; + goto next_try; + } + + while (TRUE) { + ret = gst_mxf_demux_pull_klv_packet (demux, offset, &key, &buffer, &read); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + offset = + demux->header_partition_pack_offset + demux->partition.prev_partition; + goto next_try; + } + + if (mxf_is_fill (&key)) { + offset += read; + gst_buffer_unref (buffer); + buffer = NULL; + } else if (mxf_is_primer_pack (&key)) { + if (!mxf_primer_pack_parse (&key, &demux->primer, + GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer))) { + offset += read; + gst_buffer_unref (buffer); + buffer = NULL; + offset = + demux->header_partition_pack_offset + + demux->partition.prev_partition; + goto next_try; + } + offset += read; + gst_buffer_unref (buffer); + buffer = NULL; + break; + } else { + gst_buffer_unref (buffer); + buffer = NULL; + offset = + demux->header_partition_pack_offset + demux->partition.prev_partition; + goto next_try; + } + } + + /* parse metadata */ + while (TRUE) { + ret = gst_mxf_demux_pull_klv_packet (demux, offset, &key, &buffer, &read); + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + offset = + demux->header_partition_pack_offset + demux->partition.prev_partition; + goto next_try; + } + + if (mxf_is_metadata (&key)) { + ret = gst_mxf_demux_handle_metadata (demux, &key, buffer); + offset += read; + gst_buffer_unref (buffer); + buffer = NULL; + + if (G_UNLIKELY (ret != GST_FLOW_OK)) { + gst_mxf_demux_reset_metadata (demux); + offset = + demux->header_partition_pack_offset + + demux->partition.prev_partition; + goto next_try; + } + } else if (mxf_is_fill (&key)) { + offset += read; + gst_buffer_unref (buffer); + buffer = NULL; + } else { + break; + } + } + + /* resolve references etc */ + + if (gst_mxf_demux_handle_header_metadata_resolve_references (demux) != + GST_FLOW_OK + || gst_mxf_demux_handle_header_metadata_update_streams (demux) != + GST_FLOW_OK) { + offset = + demux->header_partition_pack_offset + demux->partition.prev_partition; + goto next_try; + } + + demux->final_metadata = TRUE; + +out: + if (buffer) + gst_buffer_unref (buffer); + + mxf_partition_pack_reset (&demux->partition); + mxf_primer_pack_reset (&demux->primer); + memcpy (&demux->partition, &partition, sizeof (MXFPartitionPack)); + memcpy (&demux->primer, &primer, sizeof (MXFPrimerPack)); + + demux->offset = old_offset; +} + +static GstFlowReturn +gst_mxf_demux_handle_klv_packet (GstMXFDemux * demux, const MXFUL * key, + GstBuffer * buffer) +{ + gchar key_str[48]; + GstFlowReturn ret = GST_FLOW_OK; + + /* In pull mode try to get the last metadata */ + if (!demux->final_metadata && demux->random_access && demux->partition.valid + && demux->partition.type == MXF_PARTITION_PACK_HEADER + && (!demux->partition.closed || !demux->partition.complete) + && demux->footer_partition_pack_offset != 0) { + GST_DEBUG_OBJECT (demux, + "Open or incomplete header partition, trying to get final metadata from the last partitions"); + gst_mxf_demux_parse_footer_metadata (demux); + } + + /* TODO: - Pull random index pack from footer partition? + * - Pull all partitions for parsing all index segments and having a complete index + * as first thing. This also will make it possible to use the latest header + * metadata if it's not in the footer partition + */ + + if (demux->update_metadata + && !mxf_timestamp_is_unknown (&demux->preface.last_modified_date) + && !mxf_is_metadata (key) + && !mxf_is_fill (key)) { + if ((ret = + gst_mxf_demux_handle_header_metadata_resolve_references (demux)) != + GST_FLOW_OK) + goto beach; + if ((ret = + gst_mxf_demux_handle_header_metadata_update_streams (demux)) != + GST_FLOW_OK) + goto beach; + } + + if (mxf_is_partition_pack (key)) { + ret = gst_mxf_demux_handle_partition_pack (demux, key, buffer); + } else if (mxf_is_primer_pack (key)) { + ret = gst_mxf_demux_handle_primer_pack (demux, key, buffer); + } else if (mxf_is_metadata (key)) { + ret = gst_mxf_demux_handle_metadata (demux, key, buffer); + } else if (mxf_is_generic_container_system_item (key)) { + ret = + gst_mxf_demux_handle_generic_container_system_item (demux, key, buffer); + } else if (mxf_is_generic_container_essence_element (key)) { + ret = + gst_mxf_demux_handle_generic_container_essence_element (demux, key, + buffer); + } else if (mxf_is_random_index_pack (key)) { + ret = gst_mxf_demux_handle_random_index_pack (demux, key, buffer); + } else if (mxf_is_index_table_segment (key)) { + ret = gst_mxf_demux_handle_index_table_segment (demux, key, buffer); + } else if (mxf_is_fill (key)) { + GST_DEBUG_OBJECT (demux, + "Skipping filler packet of size %" G_GUINT64_FORMAT " at offset %" + G_GUINT64_FORMAT, GST_BUFFER_SIZE (buffer), demux->offset); + } else { + GST_DEBUG_OBJECT (demux, + "Skipping unknown packet of size %" G_GUINT64_FORMAT " at offset %" + G_GUINT64_FORMAT ", key: %s", GST_BUFFER_SIZE (buffer), demux->offset, + mxf_ul_to_string (key, key_str)); + } + +beach: + return ret; +} + +static GstFlowReturn +gst_mxf_demux_pull_and_handle_klv_packet (GstMXFDemux * demux) +{ + GstBuffer *buffer = NULL; + MXFUL key; + GstFlowReturn ret = GST_FLOW_OK; + guint64 read = 0; + + ret = + gst_mxf_demux_pull_klv_packet (demux, demux->offset, &key, &buffer, + &read); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto beach; + + ret = gst_mxf_demux_handle_klv_packet (demux, &key, buffer); + + demux->offset += read; + +beach: + if (buffer) + gst_buffer_unref (buffer); + + return ret; +} + +static void +gst_mxf_demux_loop (GstPad * pad) +{ + GstMXFDemux *demux = NULL; + GstFlowReturn ret = GST_FLOW_OK; + + demux = GST_MXF_DEMUX (gst_pad_get_parent (pad)); + + if (demux->run_in == -1) { + /* Skip run-in, which is at most 64K and is finished + * by a header partition pack */ + while (demux->offset < 64 * 1024) { + GstBuffer *buffer; + + if ((ret = + gst_mxf_demux_pull_range (demux, demux->offset, 16, + &buffer)) != GST_FLOW_OK) + break; + + if (mxf_is_header_partition_pack ((const MXFUL *) + GST_BUFFER_DATA (buffer))) { + GST_DEBUG_OBJECT (demux, + "Found header partition pack at offset %" G_GUINT64_FORMAT, + demux->offset); + demux->run_in = demux->offset; + demux->header_partition_pack_offset = demux->offset; + gst_buffer_unref (buffer); + break; + } + + demux->offset++; + gst_buffer_unref (buffer); + } + } + + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto pause; + + if (G_UNLIKELY (demux->run_in == -1)) { + GST_ERROR_OBJECT (demux, "No valid header partition pack found"); + ret = GST_FLOW_ERROR; + goto pause; + } + + /* Now actually do something */ + ret = gst_mxf_demux_pull_and_handle_klv_packet (demux); + + /* pause if something went wrong */ + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto pause; + + /* check EOS condition */ + if ((demux->segment.flags & GST_SEEK_FLAG_SEGMENT) && + (demux->segment.stop != -1) && + (demux->segment.last_stop >= demux->segment.stop)) { + ret = GST_FLOW_UNEXPECTED; + goto pause; + } + + gst_object_unref (demux); + + return; + +pause: + { + const gchar *reason = gst_flow_get_name (ret); + + GST_LOG_OBJECT (demux, "pausing task, reason %s", reason); + gst_pad_pause_task (pad); + + if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) { + if (ret == GST_FLOW_UNEXPECTED) { + /* perform EOS logic */ + if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { + gint64 stop; + + /* for segment playback we need to post when (in stream time) + * we stopped, this is either stop (when set) or the duration. */ + if ((stop = demux->segment.stop) == -1) + stop = demux->segment.duration; + + GST_LOG_OBJECT (demux, "Sending segment done, at end of segment"); + gst_element_post_message (GST_ELEMENT_CAST (demux), + gst_message_new_segment_done (GST_OBJECT_CAST (demux), + GST_FORMAT_TIME, stop)); + } else { + /* normal playback, send EOS to all linked pads */ + GST_LOG_OBJECT (demux, "Sending EOS, at end of stream"); + if (!gst_mxf_demux_push_src_event (demux, gst_event_new_eos ())) { + GST_WARNING_OBJECT (demux, "failed pushing EOS on streams"); + } + } + } else { + GST_ELEMENT_ERROR (demux, STREAM, FAILED, + ("Internal data stream error."), + ("stream stopped, reason %s", reason)); + gst_mxf_demux_push_src_event (demux, gst_event_new_eos ()); + } + } + gst_object_unref (demux); + return; + } +} + +static GstFlowReturn +gst_mxf_demux_chain (GstPad * pad, GstBuffer * inbuf) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstMXFDemux *demux = NULL; + MXFUL key; + const guint8 *data = NULL; + guint64 length = 0; + guint64 offset = 0; + GstBuffer *buffer = NULL; + gchar key_str[48]; + + demux = GST_MXF_DEMUX (gst_pad_get_parent (pad)); + + GST_LOG_OBJECT (demux, "received buffer of %d bytes at offset %" + G_GUINT64_FORMAT, GST_BUFFER_SIZE (inbuf), GST_BUFFER_OFFSET (inbuf)); + + if (G_UNLIKELY (GST_BUFFER_OFFSET (inbuf) == 0)) { + GST_DEBUG_OBJECT (demux, "beginning of file, expect header"); + demux->run_in = -1; + demux->offset = 0; + } + + if (G_UNLIKELY (demux->offset == 0 && GST_BUFFER_OFFSET (inbuf) != 0)) { + GST_DEBUG_OBJECT (demux, "offset was zero, synchronizing with buffer's"); + demux->offset = GST_BUFFER_OFFSET (inbuf); + } + + gst_adapter_push (demux->adapter, inbuf); + inbuf = NULL; + + while (ret == GST_FLOW_OK) { + if (G_UNLIKELY (demux->flushing)) { + GST_DEBUG_OBJECT (demux, "we are now flushing, exiting parser loop"); + ret = GST_FLOW_WRONG_STATE; + break; + } + + if (gst_adapter_available (demux->adapter) < 16) + break; + + if (demux->run_in == -1) { + /* Skip run-in, which is at most 64K and is finished + * by a header partition pack */ + + while (demux->offset < 64 * 1024 + && gst_adapter_available (demux->adapter) >= 16) { + data = gst_adapter_peek (demux->adapter, 16); + + if (mxf_is_header_partition_pack ((const MXFUL *) + data)) { + GST_DEBUG_OBJECT (demux, + "Found header partition pack at offset %" G_GUINT64_FORMAT, + demux->offset); + demux->run_in = demux->offset; + demux->header_partition_pack_offset = demux->offset; + break; + } + gst_adapter_flush (demux->adapter, 1); + } + } + + if (G_UNLIKELY (ret != GST_FLOW_OK)) + break; + + /* Need more data */ + if (demux->run_in == -1 && demux->offset < 64 * 1024) + break; + + if (G_UNLIKELY (demux->run_in == -1)) { + GST_ERROR_OBJECT (demux, "No valid header partition pack found"); + ret = GST_FLOW_ERROR; + break; + } + + if (gst_adapter_available (demux->adapter) < 17) + break; + + /* Now actually do something */ + memset (&key, 0, sizeof (MXFUL)); + + /* Pull 16 byte key and first byte of BER encoded length */ + data = gst_adapter_peek (demux->adapter, 17); + + if (!mxf_is_mxf_packet ((const MXFUL *) data)) { + GST_ERROR_OBJECT (demux, "Not an MXF packet, skipping. Key: %s", + mxf_ul_to_string ((const MXFUL *) data, key_str)); + ret = GST_FLOW_ERROR; + break; + } + + memcpy (&key, data, 16); + + /* Decode BER encoded packet length */ + if ((data[16] & 0x80) == 0) { + length = data[16]; + offset = 17; + } else { + guint slen = data[16] & 0x7f; + + offset = 16 + 1 + slen; + + /* Must be at most 8 according to SMPTE-379M 5.3.4 */ + if (slen > 8) { + GST_ERROR_OBJECT (demux, + "Invalid KLV packet length: %" G_GUINT64_FORMAT, slen); + ret = GST_FLOW_ERROR; + break; + } + + if (gst_adapter_available (demux->adapter) < 17 + slen) + break; + + data = gst_adapter_peek (demux->adapter, 17 + slen); + data += 17; + + length = 0; + while (slen) { + length = (length << 8) | *data; + data++; + slen--; + } + } + + if (gst_adapter_available (demux->adapter) < offset + length) + break; + + gst_adapter_flush (demux->adapter, offset); + buffer = gst_adapter_take_buffer (demux->adapter, length); + + ret = gst_mxf_demux_handle_klv_packet (demux, &key, buffer); + gst_buffer_unref (buffer); + } + + gst_object_unref (demux); + + return ret; +} + +static gboolean +gst_mxf_demux_src_event (GstPad * pad, GstEvent * event) +{ + GstMXFDemux *demux = GST_MXF_DEMUX (gst_pad_get_parent (pad)); + gboolean ret; + + GST_DEBUG_OBJECT (pad, "handling event %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + /* TODO: handle this */ + gst_event_unref (event); + ret = FALSE; + break; + default: + ret = gst_pad_push_event (demux->sinkpad, event); + break; + } + + gst_object_unref (demux); + + return ret; +} + +static const GstQueryType * +gst_mxf_demux_src_query_type (GstPad * pad) +{ + static const GstQueryType types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + 0 + }; + + return types; +} + +static gboolean +gst_mxf_demux_src_query (GstPad * pad, GstQuery * query) +{ + GstMXFDemux *demux = GST_MXF_DEMUX (gst_pad_get_parent (pad)); + gboolean ret = FALSE; + GstMXFPad *mxfpad = GST_MXF_PAD (pad); + + GST_DEBUG_OBJECT (pad, "handling query %s", + gst_query_type_get_name (GST_QUERY_TYPE (query))); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + gint64 pos; + + gst_query_parse_position (query, &format, NULL); + if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) + goto error; + + pos = mxfpad->essence_element_count; + if (format == GST_FORMAT_TIME) { + if (!mxfpad->material_track || mxfpad->material_track->edit_rate.n == 0 + || mxfpad->material_track->edit_rate.d == 0) + goto error; + + pos = + gst_util_uint64_scale (pos + mxfpad->material_track->origin, + GST_SECOND * mxfpad->material_track->edit_rate.d, + mxfpad->material_track->edit_rate.n); + } + + GST_DEBUG_OBJECT (pad, + "Returning position %" G_GINT64_FORMAT " in format %s", pos, + gst_format_get_name (format)); + + gst_query_set_position (query, format, pos); + ret = TRUE; + + break; + } + case GST_QUERY_DURATION:{ + gint64 duration; + GstFormat format; + + gst_query_parse_duration (query, &format, NULL); + if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) + goto error; + + if (!mxfpad->material_track || !mxfpad->material_track->sequence) + goto error; + + duration = mxfpad->material_track->sequence->duration; + if (format == GST_FORMAT_TIME) { + if (mxfpad->material_track->edit_rate.n == 0 || + mxfpad->material_track->edit_rate.d == 0) + goto error; + + duration = + gst_util_uint64_scale (duration, + GST_SECOND * mxfpad->material_track->edit_rate.d, + mxfpad->material_track->edit_rate.n); + } + + GST_DEBUG_OBJECT (pad, + "Returning duration %" G_GINT64_FORMAT " in format %s", duration, + gst_format_get_name (format)); + + gst_query_set_duration (query, format, duration); + ret = TRUE; + break; + } + default: + /* else forward upstream */ + ret = gst_pad_peer_query (demux->sinkpad, query); + break; + } + +done: + gst_object_unref (demux); + return ret; + + /* ERRORS */ +error: + { + GST_DEBUG_OBJECT (pad, "query failed"); + goto done; + } +} + +static gboolean +gst_mxf_demux_sink_activate (GstPad * sinkpad) +{ + if (gst_pad_check_pull_range (sinkpad)) { + return gst_pad_activate_pull (sinkpad, TRUE); + } else { + return gst_pad_activate_push (sinkpad, TRUE); + } +} + +static gboolean +gst_mxf_demux_sink_activate_push (GstPad * sinkpad, gboolean active) +{ + GstMXFDemux *demux; + + demux = GST_MXF_DEMUX (gst_pad_get_parent (sinkpad)); + + demux->random_access = FALSE; + + gst_object_unref (demux); + + return TRUE; +} + +static gboolean +gst_mxf_demux_sink_activate_pull (GstPad * sinkpad, gboolean active) +{ + GstMXFDemux *demux; + + demux = GST_MXF_DEMUX (gst_pad_get_parent (sinkpad)); + + if (active) { + demux->random_access = TRUE; + gst_object_unref (demux); + return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_mxf_demux_loop, + sinkpad); + } else { + demux->random_access = FALSE; + gst_object_unref (demux); + return gst_pad_stop_task (sinkpad); + } +} + +static gboolean +gst_mxf_demux_sink_event (GstPad * pad, GstEvent * event) +{ + GstMXFDemux *demux; + gboolean ret = FALSE; + + demux = GST_MXF_DEMUX (gst_pad_get_parent (pad)); + + GST_DEBUG_OBJECT (pad, "handling event %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + demux->flushing = TRUE; + ret = gst_mxf_demux_push_src_event (demux, event); + break; + case GST_EVENT_FLUSH_STOP: + gst_mxf_demux_flush (demux, TRUE); + ret = gst_mxf_demux_push_src_event (demux, event); + break; + case GST_EVENT_EOS: + if (!gst_mxf_demux_push_src_event (demux, event)) + GST_WARNING_OBJECT (pad, "failed pushing EOS on streams"); + ret = TRUE; + break; + case GST_EVENT_NEWSEGMENT: + /* TODO: handle this */ + gst_event_unref (event); + ret = FALSE; + break; + default: + ret = gst_mxf_demux_push_src_event (demux, event); + break; + } + + gst_object_unref (demux); + + return ret; +} + +static GstStateChangeReturn +gst_mxf_demux_change_state (GstElement * element, GstStateChange transition) +{ + GstMXFDemux *demux = GST_MXF_DEMUX (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_mxf_demux_reset (demux); + break; + default: + break; + } + + return ret; +} + +static void +gst_mxf_demux_finalize (GObject * object) +{ + GstMXFDemux *demux = GST_MXF_DEMUX (object); + + if (demux->adapter) { + g_object_unref (demux->adapter); + demux->adapter = NULL; + } + + if (demux->new_seg_event) { + gst_event_unref (demux->new_seg_event); + demux->new_seg_event = NULL; + } + + if (demux->close_seg_event) { + gst_event_unref (demux->close_seg_event); + demux->close_seg_event = NULL; + } + + if (demux->src) { + g_ptr_array_foreach (demux->src, (GFunc) gst_mxf_demux_remove_pad, demux); + g_ptr_array_foreach (demux->src, (GFunc) gst_object_unref, NULL); + g_ptr_array_free (demux->src, TRUE); + demux->src = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_mxf_demux_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&mxf_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&audio_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&video_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&data_src_template)); + gst_element_class_set_details_simple (element_class, "MXF Demuxer", + "Codec/Demuxer", + "Demux MXF files", "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); +} + +static void +gst_mxf_demux_class_init (GstMXFDemuxClass * klass) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (mxfdemux_debug, "mxfdemux", 0, "MXF demuxer"); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_mxf_demux_finalize); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_mxf_demux_change_state); +} + +static void +gst_mxf_demux_init (GstMXFDemux * demux, GstMXFDemuxClass * g_class) +{ + demux->sinkpad = + gst_pad_new_from_static_template (&mxf_sink_template, "sink"); + + gst_pad_set_event_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_mxf_demux_sink_event)); + gst_pad_set_chain_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_mxf_demux_chain)); + gst_pad_set_activate_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_mxf_demux_sink_activate)); + gst_pad_set_activatepull_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_mxf_demux_sink_activate_pull)); + gst_pad_set_activatepush_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_mxf_demux_sink_activate_push)); + + gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad); + + demux->adapter = gst_adapter_new (); + demux->src = g_ptr_array_new (); + gst_segment_init (&demux->segment, GST_FORMAT_TIME); + + gst_mxf_demux_reset (demux); +} diff --git a/gst/mxf/mxfdemux.h b/gst/mxf/mxfdemux.h new file mode 100644 index 00000000..b49d919c --- /dev/null +++ b/gst/mxf/mxfdemux.h @@ -0,0 +1,108 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __MXF_DEMUX_H__ +#define __MXF_DEMUX_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +#include "mxfparse.h" + +G_BEGIN_DECLS +#define GST_TYPE_MXF_DEMUX \ + (gst_mxf_demux_get_type()) +#define GST_MXF_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MXF_DEMUX,GstMXFDemux)) +#define GST_MXF_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MXF_DEMUX,GstMXFDemuxClass)) +#define GST_IS_MXF_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MXF_DEMUX)) +#define GST_IS_MXF_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MXF_DEMUX)) +typedef struct _GstMXFDemux GstMXFDemux; +typedef struct _GstMXFDemuxClass GstMXFDemuxClass; + +struct _GstMXFDemux +{ + GstElement element; + + GstPad *sinkpad; + GPtrArray *src; + + GstAdapter *adapter; + + GstSegment segment; + + GstEvent *new_seg_event; + GstEvent *close_seg_event; + + guint64 offset; + + gboolean random_access; + gboolean flushing; + + guint64 run_in; + + guint64 header_partition_pack_offset; + guint64 footer_partition_pack_offset; + + /* MXF file state */ + MXFPartitionPack partition; + MXFPrimerPack primer; + + /* Structural metadata */ + gboolean update_metadata; + gboolean final_metadata; + MXFMetadataPreface preface; + GArray *identification; + MXFMetadataContentStorage content_storage; + GArray *essence_container_data; + GArray *material_package; + GArray *source_package; + GPtrArray *package; + GArray *track; + GArray *sequence; + GArray *structural_component; + GArray *locator; + + GPtrArray *descriptor; + GArray *generic_descriptor; + GArray *file_descriptor; + GArray *generic_sound_essence_descriptor; + GArray *generic_picture_essence_descriptor; + GArray *cdci_picture_essence_descriptor; + GArray *mpeg_video_descriptor; + GArray *wave_audio_essence_descriptor; + GArray *multiple_descriptor; + + MXFUMID current_package_uid; + MXFMetadataGenericPackage *current_package; +}; + +struct _GstMXFDemuxClass +{ + GstElementClass parent_class; +}; + +GType gst_mxf_demux_get_type (void); + +G_END_DECLS + +#endif /* __MXF_DEMUX_H__ */ diff --git a/gst/mxf/mxfmpeg.c b/gst/mxf/mxfmpeg.c new file mode 100644 index 00000000..e46d5388 --- /dev/null +++ b/gst/mxf/mxfmpeg.c @@ -0,0 +1,318 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* Implementation of SMPTE 381M - Mapping MPEG streams into the MXF + * Generic Container + */ + +/* TODO: + * - Handle PES streams + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <string.h> + +#include "mxfmpeg.h" + +GST_DEBUG_CATEGORY_EXTERN (mxf_debug); +#define GST_CAT_DEFAULT mxf_debug + +/* SMPTE 381M 8.1 - ULs of local tags */ +static const guint8 _single_sequence_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x02, 0x00, 0x00 +}; + +static const guint8 _constant_b_frames_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x03, 0x00, 0x00 +}; + +static const guint8 _coded_content_type_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x04, 0x00, 0x00 +}; + +static const guint8 _low_delay_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x05, 0x00, 0x00 +}; + +static const guint8 _closed_gop_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x06, 0x00, 0x00 +}; + +static const guint8 _identical_gop_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x07, 0x00, 0x00 +}; + +static const guint8 _max_gop_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x08, 0x00, 0x00 +}; + +static const guint8 _b_picture_count_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x09, 0x00, 0x00 +}; + +static const guint8 _bitrate_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x0b, 0x00, 0x00 +}; + +static const guint8 _profile_and_level_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, 0x04, 0x01, 0x06, 0x02, 0x01, + 0x0a, 0x00, 0x00 +}; + +gboolean +mxf_metadata_mpeg_video_descriptor_parse (const MXFUL * key, + MXFMetadataMPEGVideoDescriptor * descriptor, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (descriptor, 0, sizeof (MXFMetadataMPEGVideoDescriptor)); + + if (!mxf_metadata_cdci_picture_essence_descriptor_parse (key, + (MXFMetadataCDCIPictureEssenceDescriptor *) descriptor, primer, type, + data, size)) + goto error; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + MXFUL *tag_ul = NULL; + + if (tag_size == 0 || tag == 0x0000) + goto next; + + if (!(tag_ul = + (MXFUL *) g_hash_table_lookup (primer->mappings, + GUINT_TO_POINTER (((guint) tag))))) + goto next; + + if (memcmp (tag_ul, &_single_sequence_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->single_sequence = GST_READ_UINT8 (tag_data); + } else if (memcmp (tag_ul, &_constant_b_frames_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->const_b_frames = GST_READ_UINT8 (tag_data); + } else if (memcmp (tag_ul, &_coded_content_type_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->coded_content_type = GST_READ_UINT8 (tag_data); + } else if (memcmp (tag_ul, &_low_delay_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->low_delay = GST_READ_UINT8 (tag_data); + } else if (memcmp (tag_ul, &_closed_gop_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->closed_gop = GST_READ_UINT8 (tag_data); + } else if (memcmp (tag_ul, &_identical_gop_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->identical_gop = GST_READ_UINT8 (tag_data); + } else if (memcmp (tag_ul, &_max_gop_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 2) + goto error; + descriptor->max_gop = GST_READ_UINT16_BE (tag_data); + } else if (memcmp (tag_ul, &_b_picture_count_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 2) + goto error; + descriptor->b_picture_count = GST_READ_UINT16_BE (tag_data); + } else if (memcmp (tag_ul, &_bitrate_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->bitrate = GST_READ_UINT32_BE (tag_data); + } else if (memcmp (tag_ul, &_profile_and_level_ul, 16) == 0) { + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->profile_and_level = GST_READ_UINT8 (tag_data); + } else { + if (type != MXF_METADATA_MPEG_VIDEO_DESCRIPTOR) + goto next; + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &((MXFMetadataGenericDescriptor *) descriptor)->other_tags)) + goto error; + } + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed mpeg video descriptors:"); + GST_DEBUG (" single sequence = %s", + (descriptor->single_sequence) ? "yes" : "no"); + GST_DEBUG (" constant b frames = %s", + (descriptor->single_sequence) ? "yes" : "no"); + GST_DEBUG (" coded content type = %u", descriptor->coded_content_type); + GST_DEBUG (" low delay = %s", (descriptor->low_delay) ? "yes" : "no"); + GST_DEBUG (" closed gop = %s", (descriptor->closed_gop) ? "yes" : "no"); + GST_DEBUG (" identical gop = %s", + (descriptor->identical_gop) ? "yes" : "no"); + GST_DEBUG (" max gop = %u", descriptor->max_gop); + GST_DEBUG (" b picture count = %u", descriptor->b_picture_count); + GST_DEBUG (" bitrate = %u", descriptor->bitrate); + GST_DEBUG (" profile & level = %u", descriptor->profile_and_level); + + return TRUE; + +error: + GST_ERROR ("Invalid mpeg video descriptor"); + mxf_metadata_mpeg_video_descriptor_reset (descriptor); + + return FALSE; +} + +void mxf_metadata_mpeg_video_descriptor_reset + (MXFMetadataMPEGVideoDescriptor * descriptor) +{ + g_return_if_fail (descriptor != NULL); + + mxf_metadata_cdci_picture_essence_descriptor_reset ( + (MXFMetadataCDCIPictureEssenceDescriptor *) descriptor); + + memset (descriptor, 0, sizeof (MXFMetadataMPEGVideoDescriptor)); +} + +gboolean +mxf_is_mpeg_video_essence_track (const MXFMetadataTrack * track) +{ + guint i; + + g_return_val_if_fail (track != NULL, FALSE); + + if (track->descriptor == NULL) + return FALSE; + + for (i = 0; i < track->n_descriptor; i++) { + MXFMetadataFileDescriptor *d = track->descriptor[i]; + MXFUL *key = &d->essence_container; + /* SMPTE 381M 7 */ + if (mxf_is_generic_container_essence_container_label (key) && + key->u[12] == 0x02 && + (key->u[13] == 0x04 || + key->u[13] == 0x07 || key->u[13] == 0x08 || key->u[13] == 0x09)) + return TRUE; + } + + return FALSE; +} + +static GstFlowReturn +mxf_mpeg_video_handle_essence_element (const MXFUL * key, GstBuffer * buffer, + GstCaps * caps, MXFMetadataGenericPackage * package, + MXFMetadataTrack * track, MXFMetadataStructuralComponent * component, + gpointer mapping_data, GstBuffer ** outbuf) +{ + *outbuf = buffer; + + /* SMPTE 381M 6.1 */ + if (key->u[12] != 0x15 || (key->u[14] != 0x05 && key->u[14] != 0x06 + && key->u[14] != 0x07)) { + GST_ERROR ("Invalid MPEG video essence element"); + return GST_FLOW_ERROR; + } + + return GST_FLOW_OK; +} + + +GstCaps * +mxf_mpeg_video_create_caps (MXFMetadataGenericPackage * package, + MXFMetadataTrack * track, GstTagList ** tags, + MXFEssenceElementHandler * handler, gpointer * mapping_data) +{ + MXFMetadataMPEGVideoDescriptor *d = NULL; + MXFMetadataFileDescriptor *f = NULL; + guint i; + + g_return_val_if_fail (package != NULL, NULL); + g_return_val_if_fail (track != NULL, NULL); + + if (track->descriptor == NULL) { + GST_ERROR ("No descriptor found for this track"); + return NULL; + } + + for (i = 0; i < track->n_descriptor; i++) { + if (((MXFMetadataGenericDescriptor *) track->descriptor[i])->type == + MXF_METADATA_MPEG_VIDEO_DESCRIPTOR) { + d = (MXFMetadataMPEGVideoDescriptor *) track->descriptor[i]; + f = track->descriptor[i]; + break; + } else if (((MXFMetadataGenericDescriptor *) track->descriptor[i])->type == + MXF_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR + || ((MXFMetadataGenericDescriptor *) track->descriptor[i])->type == + MXF_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR + || ((MXFMetadataGenericDescriptor *) track->descriptor[i])->type == + MXF_METADATA_FILE_DESCRIPTOR) { + f = track->descriptor[i]; + } + } + + if (!f) { + GST_ERROR ("No descriptor found for this track"); + return NULL; + } + + *handler = mxf_mpeg_video_handle_essence_element; + /* SMPTE 381M 7 */ + if (f->essence_container.u[13] == 0x04) { + /* FIXME: get mpeg version somehow */ + GST_DEBUG ("Found MPEG ES stream"); + return gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1, + "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); + } else if (f->essence_container.u[13] == 0x07) { + GST_ERROR ("MPEG PES streams not supported yet"); + return NULL; + } else if (f->essence_container.u[13] == 0x08) { + /* FIXME: get mpeg version somehow */ + GST_DEBUG ("Found MPEG PS stream"); + return gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 1, + "systemstream", G_TYPE_BOOLEAN, TRUE, NULL); + } else if (f->essence_container.u[13] == 0x09) { + GST_DEBUG ("Found MPEG TS stream"); + return gst_caps_new_simple ("video/mpegts", NULL); + } + return NULL; +} diff --git a/gst/mxf/mxfmpeg.h b/gst/mxf/mxfmpeg.h new file mode 100644 index 00000000..e251cb06 --- /dev/null +++ b/gst/mxf/mxfmpeg.h @@ -0,0 +1,60 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* Implementation of SMPTE 381M - Mapping MPEG streams into the MXF + * Generic Container + */ + +#ifndef __MXF_MPEG_H__ +#define __MXF_MPEG_H__ + +#include <gst/gst.h> + +#include "mxfparse.h" + +/* SMPTE 381M 8.1.1 */ +#define MXF_METADATA_MPEG_VIDEO_DESCRIPTOR 0x0151 + +/* SMPTE 381M 8.1 */ +typedef struct { + MXFMetadataCDCIPictureEssenceDescriptor parent; + + gboolean single_sequence; + gboolean const_b_frames; + guint8 coded_content_type; + gboolean low_delay; + + gboolean closed_gop; + gboolean identical_gop; + guint16 max_gop; + + guint16 b_picture_count; + guint32 bitrate; + guint8 profile_and_level; +} MXFMetadataMPEGVideoDescriptor; + +gboolean mxf_metadata_mpeg_video_descriptor_parse (const MXFUL *key, MXFMetadataMPEGVideoDescriptor *descriptor, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_mpeg_video_descriptor_reset (MXFMetadataMPEGVideoDescriptor *descriptor); + +gboolean mxf_is_mpeg_video_essence_track (const MXFMetadataTrack *track); + +GstCaps * +mxf_mpeg_video_create_caps (MXFMetadataGenericPackage *package, MXFMetadataTrack *track, GstTagList **tags, MXFEssenceElementHandler *handler, gpointer *mapping_data); + +#endif /* __MXF_MPEG_H__ */ diff --git a/gst/mxf/mxfparse.c b/gst/mxf/mxfparse.c new file mode 100644 index 00000000..49f5d685 --- /dev/null +++ b/gst/mxf/mxfparse.c @@ -0,0 +1,2692 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <string.h> + +#include "mxfparse.h" + +GST_DEBUG_CATEGORY_EXTERN (mxf_debug); +#define GST_CAT_DEFAULT mxf_debug + +/* SMPTE 377M 3.3: A value of 0 for every field means unknown timestamp */ +static const MXFTimestamp mxf_timestamp_unknown = { 0, 0, 0, 0, 0, 0, 0 }; + +/* FIXME: are zero UMID/UL invalid? Should be in SMPTE 298M, 330M or 336M */ +static const MXFUMID umid_zero = { {0,} }; +static const MXFUL key_zero = { {0,} }; + +/* UL common to all MXF UL */ +static const guint8 mxf_key[] = { 0x06, 0x0e, 0x2b, 0x34 }; + +/* SMPTE 377M 6.1 */ +static const guint8 partition_pack_key[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, + 0x01 +}; + +/* SMPTE 336M */ +static const guint8 fill_key[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x02, 0x10, + 0x01, 0x00, 0x00, 0x00 +}; + +/* SMPTE 377M 8.1 */ +static const guint8 primer_pack_key[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, + 0x01, 0x05, 0x01, 0x00 +}; + +/* SMPTE 377M 8.6 */ +static const guint8 metadata_key[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, 0x0d, 0x01, 0x01, 0x01, + 0x01 +}; + +static const guint8 random_index_pack_key[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, + 0x01, 0x11, 0x01, 0x00 +}; + +static const guint8 index_table_segment_key[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, + 0x01, 0x10, 0x01, 0x00 +}; + +gboolean +mxf_is_mxf_packet (const MXFUL * key) +{ + return (memcmp (key, mxf_key, 4) == 0); +} + +/* SMPTE 377M 6.1: Check if this is a valid partition pack */ +gboolean +mxf_is_partition_pack (const MXFUL * key) +{ + if (memcmp (key, partition_pack_key, 13) == 0 && key->u[13] >= 0x02 + && key->u[13] <= 0x04 && key->u[14] < 0x05 && key->u[15] == 0x00) + return TRUE; + + return FALSE; +} + +/* SMPTE 377M 6.2: header partition pack has byte 14 == 0x02 */ +gboolean +mxf_is_header_partition_pack (const MXFUL * key) +{ + if (memcmp (key, partition_pack_key, 13) == 0 && key->u[13] == 0x02 && + key->u[14] < 0x05 && key->u[15] == 0x00) + return TRUE; + + return FALSE; +} + +/* SMPTE 377M 6.3: body partition pack has byte 14 == 0x03 */ +gboolean +mxf_is_body_partition_pack (const MXFUL * key) +{ + if (memcmp (key, partition_pack_key, 13) == 0 && key->u[13] == 0x03 && + key->u[14] < 0x05 && key->u[15] == 0x00) + return TRUE; + + return FALSE; +} + +/* SMPTE 377M 6.4: footer partition pack has byte 14 == 0x04 */ +gboolean +mxf_is_footer_partition_pack (const MXFUL * key) +{ + if (memcmp (key, partition_pack_key, 13) == 0 && key->u[13] == 0x04 && + key->u[14] < 0x05 && key->u[15] == 0x00) + return TRUE; + + return FALSE; +} + +gboolean +mxf_is_fill (const MXFUL * key) +{ + return (memcmp (key, fill_key, 16) == 0); +} + +gboolean +mxf_is_primer_pack (const MXFUL * key) +{ + return (memcmp (key, primer_pack_key, 16) == 0); +} + +gboolean +mxf_is_metadata (const MXFUL * key) +{ + return (memcmp (key, metadata_key, 13) == 0 && key->u[15] == 0x00); +} + +gboolean +mxf_is_random_index_pack (const MXFUL * key) +{ + return (memcmp (key, random_index_pack_key, 16) == 0); +} + +gboolean +mxf_is_index_table_segment (const MXFUL * key) +{ + return (memcmp (key, index_table_segment_key, 16) == 0); +} + +/* SMPTE 379M 6.2.1 */ +gboolean +mxf_is_generic_container_system_item (const MXFUL * key) +{ + return (memcmp (key, mxf_key, 4) == 0 && key->u[4] == 0x02 + && key->u[6] == 0x01 && key->u[8] == 0x0d && key->u[9] == 0x01 + && key->u[10] == 0x03 && key->u[11] == 0x01 && (key->u[12] == 0x04 + || key->u[12] == 0x14)); +} + +/* SMPTE 379M 7.1 */ +gboolean +mxf_is_generic_container_essence_element (const MXFUL * key) +{ + return (memcmp (key, mxf_key, 4) == 0 && key->u[4] == 0x01 + && key->u[5] == 0x02 && key->u[6] == 0x01 && key->u[8] == 0x0d + && key->u[9] == 0x01 && key->u[10] == 0x03 && key->u[11] == 0x01 + && (key->u[12] == 0x05 || key->u[12] == 0x06 || key->u[12] == 0x07 + || key->u[12] == 0x15 || key->u[12] == 0x16 || key->u[12] == 0x17 + || key->u[12] == 0x18)); +} + +/* SMPTE 379M 8 */ +gboolean +mxf_is_generic_container_essence_container_label (const MXFUL * key) +{ + return (key->u[0] == 0x06 && + key->u[1] == 0x0e && + key->u[2] == 0x2b && + key->u[3] == 0x34 && + key->u[4] == 0x04 && + key->u[5] == 0x01 && + key->u[6] == 0x01 && + key->u[8] == 0x0d && + key->u[9] == 0x01 && + key->u[10] == 0x03 && + key->u[11] == 0x01 && (key->u[12] == 0x01 || key->u[12] == 0x02)); +} + +gboolean +mxf_ul_is_equal (const MXFUL * a, const MXFUL * b) +{ + return (memcmp (a, b, 16) == 0); +} + +gboolean +mxf_ul_is_zero (const MXFUL * key) +{ + return (memcmp (key, &key_zero, 16) == 0); +} + +gchar * +mxf_ul_to_string (const MXFUL * key, gchar str[48]) +{ + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (str != NULL, NULL); + + g_snprintf (str, 48, + "%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x", + key->u[0], key->u[1], key->u[2], key->u[3], key->u[4], key->u[5], + key->u[6], key->u[7], key->u[8], key->u[9], key->u[10], key->u[11], + key->u[12], key->u[13], key->u[14], key->u[15]); + + return str; +} + +gboolean +mxf_umid_is_equal (const MXFUMID * a, const MXFUMID * b) +{ + return (memcmp (a, b, 32) == 0); +} + +gboolean +mxf_umid_is_zero (const MXFUMID * umid) +{ + return (memcmp (umid, &umid_zero, 32) == 0); +} + +gchar * +mxf_umid_to_string (const MXFUMID * key, gchar str[96]) +{ + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (str != NULL, NULL); + + g_snprintf (str, 96, + "%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x." + "%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x", + key->u[0], key->u[1], key->u[2], key->u[3], key->u[4], key->u[5], + key->u[6], key->u[7], key->u[8], key->u[9], key->u[10], key->u[11], + key->u[12], key->u[13], key->u[14], key->u[15], + key->u[16], + key->u[17], + key->u[18], + key->u[19], + key->u[20], + key->u[21], + key->u[22], + key->u[23], + key->u[24], + key->u[25], + key->u[26], key->u[27], key->u[28], key->u[29], key->u[30], key->u[31] + ); + + return str; +} + + +static guint +gst_mxf_ul_hash (const MXFUL * key) +{ + guint32 ret = 0; + gint i; + + for (i = 0; i < 4; i++) + ret ^= + (key->u[i * 4 + 0] << 24) | (key->u[i * 4 + 1] << 16) | (key->u[i * 4 + + 2] << 8) | (key->u[i * 4 + 3] << 0); + + return ret; +} + +static gboolean +gst_mxf_ul_equal (const MXFUL * a, const MXFUL * b) +{ + return (memcmp (a, b, 16) == 0); +} + +gboolean +mxf_timestamp_parse (MXFTimestamp * timestamp, const guint8 * data, gsize size) +{ + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (timestamp != NULL, FALSE); + + memset (timestamp, 0, sizeof (MXFTimestamp)); + + if (size < 8) + return FALSE; + + timestamp->year = GST_READ_UINT16_BE (data); + timestamp->month = GST_READ_UINT8 (data + 2); + timestamp->day = GST_READ_UINT8 (data + 3); + timestamp->hour = GST_READ_UINT8 (data + 4); + timestamp->minute = GST_READ_UINT8 (data + 5); + timestamp->second = GST_READ_UINT8 (data + 6); + timestamp->quarter_msecond = GST_READ_UINT8 (data + 7); + + return TRUE; +} + +gboolean +mxf_timestamp_is_unknown (const MXFTimestamp * a) +{ + return (memcmp (a, &mxf_timestamp_unknown, sizeof (MXFTimestamp)) == 0); +} + +gint +mxf_timestamp_compare (const MXFTimestamp * a, const MXFTimestamp * b) +{ + gint diff; + + if ((diff = a->year - b->year) != 0) + return diff; + else if ((diff = a->month - b->month) != 0) + return diff; + else if ((diff = a->day - b->day) != 0) + return diff; + else if ((diff = a->hour - b->hour) != 0) + return diff; + else if ((diff = a->minute - b->minute) != 0) + return diff; + else if ((diff = a->second - b->second) != 0) + return diff; + else if ((diff = a->quarter_msecond - b->quarter_msecond) != 0) + return diff; + else + return 0; +} + +gboolean +mxf_fraction_parse (MXFFraction * fraction, const guint8 * data, guint16 size) +{ + g_return_val_if_fail (fraction != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + memset (fraction, 0, sizeof (MXFFraction)); + + if (size < 8) + return FALSE; + + fraction->n = GST_READ_UINT32_BE (data); + fraction->d = GST_READ_UINT32_BE (data + 4); + + return TRUE; +} + +gchar * +mxf_utf16_to_utf8 (const guint8 * data, guint16 size) +{ + gchar *ret; + GError *error = NULL; + + ret = + g_convert ((const gchar *) data, size, "UTF-8", "UTF-16BE", NULL, NULL, + &error); + + if (ret == NULL) { + GST_WARNING ("UTF-16-BE to UTF-8 conversion failed: %s", error->message); + g_error_free (error); + return NULL; + } + + return ret; +} + +gboolean +mxf_product_version_parse (MXFProductVersion * product_version, + const guint8 * data, gsize size) +{ + g_return_val_if_fail (product_version != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + memset (product_version, 0, sizeof (MXFProductVersion)); + + if (size < 10) + return FALSE; + + product_version->major = GST_READ_UINT16_BE (data); + product_version->minor = GST_READ_UINT16_BE (data + 2); + product_version->patch = GST_READ_UINT16_BE (data + 4); + product_version->build = GST_READ_UINT16_BE (data + 6); + product_version->release = GST_READ_UINT16_BE (data + 8); + + return TRUE; +} + +/* SMPTE 377M 6.1, Table 2 */ +gboolean +mxf_partition_pack_parse (const MXFUL * key, MXFPartitionPack * pack, + const guint8 * data, gsize size) +{ + gint i; + gchar str[48]; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (size >= 84, FALSE); + + memset (pack, 0, sizeof (MXFPartitionPack)); + + if (key->u[13] == 0x02) + pack->type = MXF_PARTITION_PACK_HEADER; + else if (key->u[13] == 0x03) + pack->type = MXF_PARTITION_PACK_BODY; + else if (key->u[13] == 0x04) + pack->type = MXF_PARTITION_PACK_FOOTER; + + pack->closed = (key->u[14] == 0x02 || key->u[14] == 0x04); + pack->complete = (key->u[14] == 0x03 || key->u[14] == 0x04); + + pack->major_version = GST_READ_UINT16_BE (data); + if (pack->major_version != 1) + goto error; + data += 2; + size -= 2; + + pack->minor_version = GST_READ_UINT16_BE (data); + data += 2; + size -= 2; + + pack->kag_size = GST_READ_UINT32_BE (data); + data += 4; + size -= 4; + + pack->this_partition = GST_READ_UINT64_BE (data); + data += 8; + size -= 8; + + pack->prev_partition = GST_READ_UINT64_BE (data); + data += 8; + size -= 8; + + pack->footer_partition = GST_READ_UINT64_BE (data); + data += 8; + size -= 8; + + pack->header_byte_count = GST_READ_UINT64_BE (data); + data += 8; + size -= 8; + + pack->index_byte_count = GST_READ_UINT64_BE (data); + data += 8; + size -= 8; + + pack->index_sid = GST_READ_UINT32_BE (data); + data += 4; + size -= 4; + + pack->body_offset = GST_READ_UINT64_BE (data); + data += 8; + size -= 8; + + pack->body_sid = GST_READ_UINT32_BE (data); + data += 4; + size -= 4; + + memcpy (&pack->operational_pattern, data, 16); + data += 16; + size -= 16; + + pack->n_essence_containers = GST_READ_UINT32_BE (data); + data += 4; + size -= 4; + + if (GST_READ_UINT32_BE (data) != 16) + goto error; + data += 4; + size -= 4; + + if (size < 16 * pack->n_essence_containers) + goto error; + + if (pack->n_essence_containers) { + pack->essence_containers = g_new (MXFUL, pack->n_essence_containers); + for (i = 0; i < pack->n_essence_containers; i++) + memcpy (&pack->essence_containers[i], data + i * 16, 16); + } + + pack->valid = TRUE; + GST_DEBUG ("Parsed partition pack: \n" + " type = %s, closed = %s, complete = %s\n" + " MXF version = %u.%u\n" + " KAG size = %u\n" + " this partition offset = %" G_GUINT64_FORMAT "\n" + " previous partition offset = %" G_GUINT64_FORMAT "\n" + " footer partition offset = %" G_GUINT64_FORMAT "\n" + " header size = %" G_GUINT64_FORMAT "\n" + " index sid = %u, size %" G_GUINT64_FORMAT "\n" + " body sid = %u, offset %" G_GUINT64_FORMAT "\n" + " operational pattern = %s\n" + " number of essence containers = %u", + (pack->type == MXF_PARTITION_PACK_HEADER) ? "header" : (pack->type == + MXF_PARTITION_PACK_BODY) ? "body" : "footer", + (pack->closed) ? "yes" : "no", (pack->complete) ? "yes" : "no", + pack->major_version, pack->minor_version, pack->kag_size, + pack->this_partition, pack->prev_partition, pack->footer_partition, + pack->header_byte_count, pack->index_sid, pack->index_byte_count, + pack->body_sid, pack->body_offset, + mxf_ul_to_string (&pack->operational_pattern, str), + pack->n_essence_containers); + + for (i = 0; i < pack->n_essence_containers; i++) { + GST_DEBUG (" essence container %d = %s", i, + mxf_ul_to_string (&pack->essence_containers[i], str)); + } + + return TRUE; + +error: + GST_ERROR ("Invalid partition pack"); + + mxf_partition_pack_reset (pack); + return FALSE; +} + +void +mxf_partition_pack_reset (MXFPartitionPack * pack) +{ + g_return_if_fail (pack != NULL); + + g_free (pack->essence_containers); + + memset (pack, 0, sizeof (MXFPartitionPack)); +} + +/* SMPTE 377M 8.2 Table 1 and 2 */ + +static void +_mxf_mapping_ul_free (MXFUL * ul) +{ + g_slice_free (MXFUL, ul); +} + +gboolean +mxf_primer_pack_parse (const MXFUL * key, MXFPrimerPack * pack, + const guint8 * data, gsize size) +{ + gint i; + guint32 n; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (size >= 8, FALSE); + + memset (pack, 0, sizeof (MXFPrimerPack)); + + pack->mappings = + g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, (GDestroyNotify) _mxf_mapping_ul_free); + + n = GST_READ_UINT32_BE (data); + data += 4; + + if (GST_READ_UINT32_BE (data) != 18) + goto error; + data += 4; + + if (size < 8 + n * 18) + goto error; + + GST_DEBUG ("Parsed primer pack:"); + for (i = 0; i < n; i++) { + guint local_tag; + gchar str[48]; + MXFUL *uid; + + local_tag = GST_READ_UINT16_BE (data); + data += 2; + + if (g_hash_table_lookup (pack->mappings, GUINT_TO_POINTER (local_tag))) + continue; + + uid = g_slice_new (MXFUL); + memcpy (uid, data, 16); + data += 16; + + g_hash_table_insert (pack->mappings, GUINT_TO_POINTER (local_tag), uid); + GST_DEBUG (" Adding primer pack association: 0x%04x -> %s", local_tag, + mxf_ul_to_string (uid, str)); + } + + pack->valid = TRUE; + + return TRUE; + +error: + GST_DEBUG ("Invalid primer pack"); + mxf_primer_pack_reset (pack); + return FALSE; +} + +void +mxf_primer_pack_reset (MXFPrimerPack * pack) +{ + g_return_if_fail (pack != NULL); + + if (pack->mappings) + g_hash_table_destroy (pack->mappings); + memset (pack, 0, sizeof (MXFPrimerPack)); +} + +/* structural metadata parsing */ + +gboolean +mxf_local_tag_parse (const guint8 * data, gsize size, guint16 * tag, + guint16 * tag_size, const guint8 ** tag_data) +{ + g_return_val_if_fail (data != NULL, FALSE); + + if (size < 4) + return FALSE; + + *tag = GST_READ_UINT16_BE (data); + *tag_size = GST_READ_UINT16_BE (data + 2); + + if (size < 4 + *tag_size) + return FALSE; + + *tag_data = data + 4; + + return TRUE; +} + +void +gst_mxf_local_tag_free (MXFLocalTag * tag) +{ + g_free (tag->data); + g_slice_free (MXFLocalTag, tag); +} + +gboolean +gst_metadata_add_custom_tag (const MXFPrimerPack * primer, + guint16 tag, const guint8 * tag_data, guint16 tag_size, + GHashTable ** hash_table) +{ + MXFLocalTag *local_tag; + MXFUL *key; + + g_return_val_if_fail (primer != NULL, FALSE); + g_return_val_if_fail (tag_data != NULL, FALSE); + g_return_val_if_fail (hash_table != NULL, FALSE); + g_return_val_if_fail (primer->mappings != NULL, FALSE); + + if (*hash_table == NULL) + *hash_table = + g_hash_table_new_full ((GHashFunc) gst_mxf_ul_hash, + (GEqualFunc) gst_mxf_ul_equal, (GDestroyNotify) NULL, + (GDestroyNotify) gst_mxf_local_tag_free); + + g_return_val_if_fail (*hash_table != NULL, FALSE); + + key = (MXFUL *) g_hash_table_lookup (primer->mappings, + GUINT_TO_POINTER (((guint) tag))); + + if (key) { + gchar str[48]; + + GST_DEBUG ("Adding local tag 0x%04x with UL %s and size %u", tag, + mxf_ul_to_string (key, str), tag_size); + + local_tag = g_slice_new (MXFLocalTag); + memcpy (&local_tag->key, key, sizeof (MXFUL)); + local_tag->size = tag_size; + local_tag->data = g_memdup (tag_data, tag_size); + + g_hash_table_insert (*hash_table, &local_tag->key, local_tag); + } else { + GST_WARNING ("Local tag with no entry in primer pack: 0x%04x", tag); + } + + return TRUE; +} + +/* All following defined in SMPTE 377M Annex A, B, C, D */ +gboolean +mxf_metadata_preface_parse (const MXFUL * key, + MXFMetadataPreface * preface, const MXFPrimerPack * primer, + const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + gint i; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (preface, 0, sizeof (MXFMetadataPreface)); + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&preface->instance_uid, tag_data, 16); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&preface->generation_uid, tag_data, 16); + break; + case 0x3b02: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!mxf_timestamp_parse (&preface->last_modified_date, tag_data, + tag_size)) + goto error; + break; + case 0x3b05: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 2) + goto error; + preface->version = GST_READ_UINT16_BE (tag_data); + break; + case 0x3b07: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + preface->object_model_version = GST_READ_UINT32_BE (tag_data); + break; + case 0x3b08: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&preface->primary_package_uid, tag_data, 16); + break; + case 0x3b06:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + + if (tag_size < 8) + goto error; + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + break; + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + if (tag_size < 8 + len * 16) + goto error; + + preface->n_identifications = len; + preface->identifications_uids = g_new (MXFUL, len); + for (i = 0; i < len; i++) + memcpy (&preface->identifications_uids[i], tag_data + 8 + i * 16, 16); + break; + } + case 0x3b03: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&preface->content_storage_uid, tag_data, 16); + break; + case 0x3b09: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&preface->operational_pattern, tag_data, 16); + break; + case 0x3b0a:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + + if (tag_size < 8) + goto error; + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + break; + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + if (tag_size < 8 + len * 16) + goto error; + + preface->n_essence_containers = len; + preface->essence_containers = g_new (MXFUL, len); + for (i = 0; i < len; i++) + memcpy (&preface->essence_containers[i], tag_data + 8 + i * 16, 16); + break; + } + case 0x3b0b:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + + if (tag_size < 8) + goto error; + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + break; + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + if (tag_size < 8 + len * 16) + goto error; + + preface->n_dm_schemes = len; + preface->dm_schemes = g_new (MXFUL, len); + for (i = 0; i < len; i++) + memcpy (&preface->dm_schemes[i], tag_data + 8 + i * 16, 16); + break; + } + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &preface->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed preface:"); + GST_DEBUG (" instance uid = %s", mxf_ul_to_string (&preface->instance_uid, + str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&preface->generation_uid, str)); + GST_DEBUG (" last modified date = %d/%u/%u %u:%u:%u.%u", + preface->last_modified_date.year, preface->last_modified_date.month, + preface->last_modified_date.day, preface->last_modified_date.hour, + preface->last_modified_date.minute, preface->last_modified_date.second, + (preface->last_modified_date.quarter_msecond * 1000) / 256); + GST_DEBUG (" version = %u.%u", (preface->version >> 8), + (preface->version & 0x0f)); + GST_DEBUG (" object model version = %u", preface->object_model_version); + GST_DEBUG (" primary package = %s", + mxf_ul_to_string (&preface->primary_package_uid, str)); + GST_DEBUG (" content storage = %s", + mxf_ul_to_string (&preface->content_storage_uid, str)); + GST_DEBUG (" operational pattern = %s", + mxf_ul_to_string (&preface->operational_pattern, str)); + GST_DEBUG (" number of identifications = %u", preface->n_identifications); + GST_DEBUG (" number of essence containers = %u", + preface->n_essence_containers); + GST_DEBUG (" number of DM schemes = %u", preface->n_dm_schemes); + + for (i = 0; i < preface->n_identifications; i++) + GST_DEBUG (" identification %d = %s", i, + mxf_ul_to_string (&preface->identifications_uids[i], str)); + + for (i = 0; i < preface->n_essence_containers; i++) + GST_DEBUG (" essence container %d = %s", i, + mxf_ul_to_string (&preface->essence_containers[i], str)); + + for (i = 0; i < preface->n_dm_schemes; i++) + GST_DEBUG (" DM schemes %d = %s", i, + mxf_ul_to_string (&preface->dm_schemes[i], str)); + + return TRUE; + +error: + GST_ERROR ("Invalid preface"); + mxf_metadata_preface_reset (preface); + + return FALSE; +} + +void +mxf_metadata_preface_reset (MXFMetadataPreface * preface) +{ + g_return_if_fail (preface != NULL); + + g_free (preface->identifications_uids); + g_free (preface->identifications); + g_free (preface->essence_containers); + g_free (preface->dm_schemes); + + if (preface->other_tags) + g_hash_table_destroy (preface->other_tags); + + memset (preface, 0, sizeof (MXFMetadataPreface)); +} + +gboolean +mxf_metadata_identification_parse (const MXFUL * key, + MXFMetadataIdentification * identification, + const MXFPrimerPack * primer, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (identification, 0, sizeof (MXFMetadataIdentification)); + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&identification->instance_uid, tag_data, 16); + break; + case 0x3c09: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&identification->generation_uid, tag_data, 16); + break; + case 0x3c01: + GST_WRITE_UINT16_BE (data, 0x0000); + identification->company_name = mxf_utf16_to_utf8 (tag_data, tag_size); + break; + case 0x3c02: + GST_WRITE_UINT16_BE (data, 0x0000); + identification->product_name = mxf_utf16_to_utf8 (tag_data, tag_size); + break; + case 0x3c03: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 10) + goto error; + if (!mxf_product_version_parse (&identification->product_version, + tag_data, tag_size)) + goto error; + break; + case 0x3c04: + GST_WRITE_UINT16_BE (data, 0x0000); + identification->version_string = mxf_utf16_to_utf8 (tag_data, tag_size); + break; + case 0x3c05: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&identification->product_uid, tag_data, 16); + break; + case 0x3c06: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 8) + goto error; + if (!mxf_timestamp_parse (&identification->modification_date, tag_data, + tag_size)) + goto error; + break; + case 0x3c07: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 10) + goto error; + if (!mxf_product_version_parse (&identification->toolkit_version, + tag_data, tag_size)) + goto error; + break; + case 0x3c08: + GST_WRITE_UINT16_BE (data, 0x0000); + identification->platform = mxf_utf16_to_utf8 (tag_data, tag_size); + break; + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &identification->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed identification:"); + GST_DEBUG (" instance uid = %s", + mxf_ul_to_string (&identification->instance_uid, str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&identification->generation_uid, str)); + GST_DEBUG (" company name = %s", + GST_STR_NULL (identification->company_name)); + GST_DEBUG (" product version = %u.%u.%u.%u.%u", + identification->product_version.major, + identification->product_version.minor, + identification->product_version.patch, + identification->product_version.build, + identification->product_version.release); + GST_DEBUG (" version string = %s", + GST_STR_NULL (identification->version_string)); + GST_DEBUG (" product uid = %s", + mxf_ul_to_string (&identification->product_uid, str)); + GST_DEBUG (" modification date = %d/%u/%u %u:%u:%u.%u", + identification->modification_date.year, + identification->modification_date.month, + identification->modification_date.day, + identification->modification_date.hour, + identification->modification_date.minute, + identification->modification_date.second, + (identification->modification_date.quarter_msecond * 1000) / 256); + GST_DEBUG (" toolkit version = %u.%u.%u.%u.%u", + identification->toolkit_version.major, + identification->toolkit_version.minor, + identification->toolkit_version.patch, + identification->toolkit_version.build, + identification->toolkit_version.release); + GST_DEBUG (" platform = %s", GST_STR_NULL (identification->platform)); + + return TRUE; + +error: + GST_ERROR ("Invalid identification"); + mxf_metadata_identification_reset (identification); + + return FALSE; +} + +void mxf_metadata_identification_reset + (MXFMetadataIdentification * identification) +{ + g_return_if_fail (identification != NULL); + + g_free (identification->company_name); + g_free (identification->product_name); + g_free (identification->version_string); + g_free (identification->platform); + + if (identification->other_tags) + g_hash_table_destroy (identification->other_tags); + + memset (identification, 0, sizeof (MXFMetadataIdentification)); +} + +gboolean +mxf_metadata_content_storage_parse (const MXFUL * key, + MXFMetadataContentStorage * content_storage, + const MXFPrimerPack * primer, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + gint i; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (content_storage, 0, sizeof (MXFMetadataContentStorage)); + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&content_storage->instance_uid, tag_data, 16); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&content_storage->generation_uid, tag_data, 16); + break; + case 0x1901:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + break; + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + if (tag_size < 8 + len * 16) + goto error; + + content_storage->packages_uids = g_new (MXFUL, len); + content_storage->n_packages = len; + for (i = 0; i < len; i++) + memcpy (&content_storage->packages_uids[i], tag_data + 8 + i * 16, + 16); + break; + } + case 0x1902:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + break; + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + if (tag_size < 8 + len * 16) + goto error; + + content_storage->essence_container_data_uids = g_new (MXFUL, len); + content_storage->n_essence_container_data = len; + for (i = 0; i < len; i++) + memcpy (&content_storage->essence_container_data_uids[i], + tag_data + 8 + i * 16, 16); + break; + } + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &content_storage->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed content storage:"); + GST_DEBUG (" instance uid = %s", + mxf_ul_to_string (&content_storage->instance_uid, str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&content_storage->generation_uid, str)); + GST_DEBUG (" number of packages = %u", content_storage->n_packages); + GST_DEBUG (" number of essence container data = %u", + content_storage->n_essence_container_data); + + for (i = 0; i < content_storage->n_packages; i++) + GST_DEBUG (" package %i = %s", i, + mxf_ul_to_string (&content_storage->packages_uids[i], str)); + for (i = 0; i < content_storage->n_packages; i++) + GST_DEBUG (" essence container data %i = %s", i, + mxf_ul_to_string (&content_storage->essence_container_data_uids[i], + str)); + + return TRUE; + +error: + GST_ERROR ("Invalid content storage"); + mxf_metadata_content_storage_reset (content_storage); + + return FALSE; +} + +void mxf_metadata_content_storage_reset + (MXFMetadataContentStorage * content_storage) +{ + g_return_if_fail (content_storage != NULL); + + g_free (content_storage->packages); + g_free (content_storage->packages_uids); + g_free (content_storage->essence_container_data); + g_free (content_storage->essence_container_data_uids); + + if (content_storage->other_tags) + g_hash_table_destroy (content_storage->other_tags); + + memset (content_storage, 0, sizeof (MXFMetadataContentStorage)); +} + +gboolean +mxf_metadata_essence_container_data_parse (const MXFUL * key, + MXFMetadataEssenceContainerData * essence_container_data, + const MXFPrimerPack * primer, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[96]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (essence_container_data, 0, sizeof (MXFMetadataEssenceContainerData)); + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&essence_container_data->instance_uid, tag_data, 16); + break; + case 0x2701: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 32) + goto error; + memcpy (&essence_container_data->linked_package_uid, tag_data, 32); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&essence_container_data->generation_uid, tag_data, 16); + break; + case 0x3f06: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + essence_container_data->index_sid = GST_READ_UINT32_BE (tag_data); + break; + case 0x3f07: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + essence_container_data->body_sid = GST_READ_UINT32_BE (tag_data); + break; + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &essence_container_data->other_tags)) + goto error; + break; + } + next: + + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed essence container data:"); + GST_DEBUG (" instance uid = %s", + mxf_ul_to_string (&essence_container_data->instance_uid, str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&essence_container_data->generation_uid, str)); + GST_DEBUG (" linked package = %s", + mxf_umid_to_string (&essence_container_data->linked_package_uid, str)); + GST_DEBUG (" index sid = %u", essence_container_data->index_sid); + GST_DEBUG (" body sid = %u", essence_container_data->body_sid); + + return TRUE; + +error: + GST_ERROR ("Invalid essence container data"); + mxf_metadata_essence_container_data_reset (essence_container_data); + + return FALSE; +} + +void mxf_metadata_essence_container_data_reset + (MXFMetadataEssenceContainerData * essence_container_data) +{ + g_return_if_fail (essence_container_data != NULL); + + if (essence_container_data->other_tags) + g_hash_table_destroy (essence_container_data->other_tags); + + memset (essence_container_data, 0, sizeof (MXFMetadataEssenceContainerData)); +} + +gboolean +mxf_metadata_generic_package_parse (const MXFUL * key, + MXFMetadataGenericPackage * generic_package, + const MXFPrimerPack * primer, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[96]; + gint i; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (generic_package, 0, sizeof (MXFMetadataGenericPackage)); + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&generic_package->instance_uid, tag_data, 16); + break; + case 0x4401: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 32) + goto error; + memcpy (&generic_package->package_uid, tag_data, 32); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&generic_package->generation_uid, tag_data, 16); + break; + case 0x4402: + generic_package->name = mxf_utf16_to_utf8 (tag_data, tag_size); + break; + case 0x4405: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!mxf_timestamp_parse (&generic_package->package_creation_date, + tag_data, tag_size)) + goto error; + break; + case 0x4404: + if (!mxf_timestamp_parse (&generic_package->package_modified_date, + tag_data, tag_size)) + goto error; + break; + case 0x4403:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + break; + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + if (tag_size < 8 + len * 16) + goto error; + + generic_package->tracks_uids = g_new (MXFUL, len); + generic_package->n_tracks = len; + for (i = 0; i < len; i++) + memcpy (&generic_package->tracks_uids[i], tag_data + 8 + i * 16, 16); + break; + } + case 0x4701: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + + generic_package->n_descriptors = 1; + memcpy (&generic_package->descriptors_uid, tag_data, 16); + break; + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &generic_package->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed package:"); + GST_DEBUG (" instance uid = %s", + mxf_ul_to_string (&generic_package->instance_uid, str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&generic_package->generation_uid, str)); + GST_DEBUG (" UMID = %s", mxf_umid_to_string (&generic_package->package_uid, + str)); + GST_DEBUG (" name = %s", GST_STR_NULL (generic_package->name)); + GST_DEBUG (" creation date = %d/%u/%u %u:%u:%u.%u", + generic_package->package_creation_date.year, + generic_package->package_creation_date.month, + generic_package->package_creation_date.day, + generic_package->package_creation_date.hour, + generic_package->package_creation_date.minute, + generic_package->package_creation_date.second, + (generic_package->package_creation_date.quarter_msecond * 1000) / 256); + GST_DEBUG (" modification date = %d/%u/%u %u:%u:%u.%u", + generic_package->package_modified_date.year, + generic_package->package_modified_date.month, + generic_package->package_modified_date.day, + generic_package->package_modified_date.hour, + generic_package->package_modified_date.minute, + generic_package->package_modified_date.second, + (generic_package->package_modified_date.quarter_msecond * 1000) / 256); + GST_DEBUG (" descriptor = %s", + mxf_ul_to_string (&generic_package->descriptors_uid, str)); + GST_DEBUG (" number of tracks = %u", generic_package->n_tracks); + + for (i = 0; i < generic_package->n_tracks; i++) + GST_DEBUG (" track %d = %s", i, + mxf_ul_to_string (&generic_package->tracks_uids[i], str)); + + return TRUE; + +error: + GST_ERROR ("Invalid package"); + mxf_metadata_generic_package_reset (generic_package); + + return FALSE; +} + +void mxf_metadata_generic_package_reset + (MXFMetadataGenericPackage * generic_package) +{ + g_return_if_fail (generic_package != NULL); + + g_free (generic_package->name); + g_free (generic_package->tracks_uids); + + g_free (generic_package->tracks); + + if (generic_package->other_tags) + g_hash_table_destroy (generic_package->other_tags); + + g_free (generic_package->descriptors); + + memset (generic_package, 0, sizeof (MXFMetadataGenericPackage)); +} + +gboolean +mxf_metadata_track_parse (const MXFUL * key, + MXFMetadataTrack * track, const MXFPrimerPack * primer, + const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (track, 0, sizeof (MXFMetadataTrack)); + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&track->instance_uid, tag_data, 16); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&track->generation_uid, tag_data, 16); + break; + case 0x4801: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + track->track_id = GST_READ_UINT32_BE (tag_data); + break; + case 0x4804: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + track->track_number = GST_READ_UINT32_BE (tag_data); + break; + case 0x4802: + GST_WRITE_UINT16_BE (data, 0x0000); + track->track_name = mxf_utf16_to_utf8 (tag_data, tag_size); + break; + case 0x4b01: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!mxf_fraction_parse (&track->edit_rate, tag_data, tag_size)) + goto error; + break; + case 0x4b02: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 8) + goto error; + track->origin = GST_READ_UINT64_BE (tag_data); + break; + case 0x4803: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&track->sequence_uid, tag_data, 16); + break; + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &track->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed track:"); + GST_DEBUG (" instance uid = %s", mxf_ul_to_string (&track->instance_uid, + str)); + GST_DEBUG (" generation uid = %s", mxf_ul_to_string (&track->generation_uid, + str)); + GST_DEBUG (" track id = %u", track->track_id); + GST_DEBUG (" track number = %u", track->track_number); + GST_DEBUG (" track name = %s", GST_STR_NULL (track->track_name)); + GST_DEBUG (" edit rate = %d/%d", track->edit_rate.n, track->edit_rate.d); + GST_DEBUG (" origin = %" G_GINT64_FORMAT, track->origin); + GST_DEBUG (" sequence uid = %s", mxf_ul_to_string (&track->sequence_uid, + str)); + + return TRUE; + +error: + GST_ERROR ("Invalid track"); + mxf_metadata_track_reset (track); + + return FALSE; +} + +void +mxf_metadata_track_reset (MXFMetadataTrack * track) +{ + g_return_if_fail (track != NULL); + + g_free (track->track_name); + + if (track->descriptor) + g_free (track->descriptor); + + if (track->other_tags) + g_hash_table_destroy (track->other_tags); + + memset (track, 0, sizeof (MXFMetadataTrack)); +} + +/* SMPTE RP224 */ +static const struct +{ + guint8 ul[16]; + MXFMetadataTrackType type; +} mxf_metadata_track_identifier[] = { + { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, + 0x01, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00}, + MXF_METADATA_TRACK_TIMECODE_12M_INACTIVE}, { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x01, + 0x02, 0x00, 0x00, 0x00}, MXF_METADATA_TRACK_TIMECODE_12M_ACTIVE}, { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x01, + 0x03, 0x00, 0x00, 0x00}, MXF_METADATA_TRACK_TIMECODE_309M}, { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x01, + 0x10, 0x00, 0x00, 0x00}, MXF_METADATA_TRACK_METADATA}, { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x02, + 0x01, 0x00, 0x00, 0x00}, MXF_METADATA_TRACK_PICTURE_ESSENCE}, { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x02, + 0x02, 0x00, 0x00, 0x00}, MXF_METADATA_TRACK_SOUND_ESSENCE}, { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x02, + 0x03, 0x00, 0x00, 0x00}, MXF_METADATA_TRACK_DATA_ESSENCE}, { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, + 0x01, 0x00, 0x00, 0x00}, MXF_METADATA_TRACK_AUXILIARY_DATA}, { { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, + 0x02, 0x00, 0x00, 0x00}, MXF_METADATA_TRACK_PARSED_TEXT} +}; + +MXFMetadataTrackType +mxf_metadata_track_identifier_parse (const MXFUL * track_identifier) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (mxf_metadata_track_identifier); i++) + if (memcmp (&mxf_metadata_track_identifier[i].ul, &track_identifier->u, + 16) == 0) + return mxf_metadata_track_identifier[i].type; + + return MXF_METADATA_TRACK_UNKNOWN; +} + +gboolean +mxf_metadata_sequence_parse (const MXFUL * key, + MXFMetadataSequence * sequence, const MXFPrimerPack * primer, + const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + gint i; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (sequence, 0, sizeof (MXFMetadataSequence)); + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&sequence->instance_uid, tag_data, 16); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&sequence->generation_uid, tag_data, 16); + break; + case 0x0201: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&sequence->data_definition, tag_data, 16); + break; + case 0x0202: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 8) + goto error; + sequence->duration = GST_READ_UINT64_BE (tag_data); + break; + case 0x1001:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + break; + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + if (tag_size < 8 + len * 16) + goto error; + + sequence->structural_components_uids = g_new (MXFUL, len); + sequence->n_structural_components = len; + for (i = 0; i < len; i++) + memcpy (&sequence->structural_components_uids[i], + tag_data + 8 + i * 16, 16); + break; + } + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &sequence->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed sequence:"); + GST_DEBUG (" instance uid = %s", mxf_ul_to_string (&sequence->instance_uid, + str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&sequence->generation_uid, str)); + GST_DEBUG (" data definition = %s", + mxf_ul_to_string (&sequence->data_definition, str)); + GST_DEBUG (" duration = %" G_GINT64_FORMAT, sequence->duration); + GST_DEBUG (" number of structural components = %u", + sequence->n_structural_components); + + for (i = 0; i < sequence->n_structural_components; i++) + GST_DEBUG (" structural component %d = %s", i, + mxf_ul_to_string (&sequence->structural_components_uids[i], str)); + + + return TRUE; + +error: + GST_ERROR ("Invalid sequence"); + mxf_metadata_sequence_reset (sequence); + + return FALSE; +} + +void +mxf_metadata_sequence_reset (MXFMetadataSequence * sequence) +{ + g_return_if_fail (sequence != NULL); + + g_free (sequence->structural_components_uids); + g_free (sequence->structural_components); + + if (sequence->other_tags) + g_hash_table_destroy (sequence->other_tags); + + memset (sequence, 0, sizeof (MXFMetadataSequence)); +} + +gboolean +mxf_metadata_structural_component_parse (const MXFUL * key, + MXFMetadataStructuralComponent * component, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[96]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (component, 0, sizeof (MXFMetadataStructuralComponent)); + + component->type = type; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&component->instance_uid, tag_data, 16); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&component->generation_uid, tag_data, 16); + break; + case 0x0201: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&component->data_definition, tag_data, 16); + break; + case 0x0202: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 8) + goto error; + component->duration = GST_READ_UINT64_BE (tag_data); + break; + /* Timecode component specifics */ + case 0x1502: + if (type != MXF_METADATA_TIMECODE_COMPONENT) + goto DFLT; + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 2) + goto error; + component->timecode_component.rounded_timecode_base = + GST_READ_UINT16_BE (tag_data); + break; + case 0x1501: + if (type != MXF_METADATA_TIMECODE_COMPONENT) + goto DFLT; + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 8) + goto error; + component->timecode_component.start_timecode = + GST_READ_UINT64_BE (tag_data); + break; + case 0x1503: + if (type != MXF_METADATA_TIMECODE_COMPONENT) + goto DFLT; + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + component->timecode_component.drop_frame = + (GST_READ_UINT8 (tag_data) != 0); + break; + /* Source clip specifics */ + case 0x1201: + if (type != MXF_METADATA_SOURCE_CLIP) + goto DFLT; + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 8) + goto error; + component->source_clip.start_position = GST_READ_UINT64_BE (tag_data); + break; + case 0x1101: + if (type != MXF_METADATA_SOURCE_CLIP) + goto DFLT; + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 32) + goto error; + memcpy (&component->source_clip.source_package_id, tag_data, 32); + break; + case 0x1102: + if (type != MXF_METADATA_SOURCE_CLIP) + goto DFLT; + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + component->source_clip.source_track_id = GST_READ_UINT32_BE (tag_data); + break; + DFLT: + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &component->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed structural component:"); + GST_DEBUG (" instance uid = %s", mxf_ul_to_string (&component->instance_uid, + str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&component->generation_uid, str)); + GST_DEBUG (" type = %s", + (component->type == + MXF_METADATA_TIMECODE_COMPONENT) ? "timecode component" : + "source clip"); + GST_DEBUG (" data definition = %s", + mxf_ul_to_string (&component->data_definition, str)); + GST_DEBUG (" duration = %" G_GINT64_FORMAT, component->duration); + + if (component->type == MXF_METADATA_TIMECODE_COMPONENT) { + GST_DEBUG (" start timecode = %" G_GINT64_FORMAT, + component->timecode_component.start_timecode); + GST_DEBUG (" rounded timecode base = %u", + component->timecode_component.rounded_timecode_base); + GST_DEBUG (" drop frame = %s", + (component->timecode_component.drop_frame) ? "yes" : "no"); + } else { + GST_DEBUG (" start position = %" G_GINT64_FORMAT, + component->source_clip.start_position); + GST_DEBUG (" source package id = %s", + mxf_umid_to_string (&component->source_clip.source_package_id, str)); + GST_DEBUG (" source track id = %u", + component->source_clip.source_track_id); + } + + return TRUE; + +error: + GST_ERROR ("Invalid structural component"); + mxf_metadata_structural_component_reset (component); + + return FALSE; +} + +void mxf_metadata_structural_component_reset + (MXFMetadataStructuralComponent * component) +{ + g_return_if_fail (component != NULL); + + if (component->other_tags) + g_hash_table_destroy (component->other_tags); + + memset (component, 0, sizeof (MXFMetadataStructuralComponent)); +} + +gboolean +mxf_metadata_generic_descriptor_parse (const MXFUL * key, + MXFMetadataGenericDescriptor * descriptor, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + gint i; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (descriptor, 0, sizeof (MXFMetadataGenericDescriptor)); + + descriptor->type = type; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&descriptor->instance_uid, tag_data, 16); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&descriptor->generation_uid, tag_data, 16); + break; + case 0x2f01:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size < 8) + goto error; + + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + goto next; + + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + + descriptor->locators_uids = g_new (MXFUL, len); + descriptor->n_locators = len; + for (i = 0; i < len; i++) + memcpy (&descriptor->locators_uids[i], tag_data + 8 + i * 16, 16); + break; + } + } + next: + + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed generic descriptor:"); + GST_DEBUG (" instance uid = %s", mxf_ul_to_string (&descriptor->instance_uid, + str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&descriptor->generation_uid, str)); + GST_DEBUG (" type = %u", descriptor->type); + GST_DEBUG (" number of locators = %u", descriptor->n_locators); + + for (i = 0; i < descriptor->n_locators; i++) + GST_DEBUG (" locator %d = %s", i, + mxf_ul_to_string (&descriptor->locators_uids[i], str)); + + return TRUE; + +error: + GST_ERROR ("Invalid generic descriptor"); + mxf_metadata_generic_descriptor_reset (descriptor); + + return FALSE; +} + +void mxf_metadata_generic_descriptor_reset + (MXFMetadataGenericDescriptor * descriptor) +{ + g_return_if_fail (descriptor != NULL); + + switch (descriptor->type) { + case MXF_METADATA_FILE_DESCRIPTOR: + break; + } + + if (descriptor->locators_uids) + g_free (descriptor->locators_uids); + + if (descriptor->locators) + g_free (descriptor->locators); + + if (descriptor->other_tags) + g_hash_table_destroy (descriptor->other_tags); + + memset (descriptor, 0, sizeof (MXFMetadataGenericDescriptor)); +} + +gboolean +mxf_metadata_file_descriptor_parse (const MXFUL * key, + MXFMetadataFileDescriptor * descriptor, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (descriptor, 0, sizeof (MXFMetadataFileDescriptor)); + + if (!mxf_metadata_generic_descriptor_parse (key, + (MXFMetadataGenericDescriptor *) descriptor, primer, type, data, + size)) + goto error; + + descriptor->parent.is_file_descriptor = TRUE; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3006: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->linked_track_id = GST_READ_UINT32_BE (tag_data); + break; + case 0x3001: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!mxf_fraction_parse (&descriptor->sample_rate, tag_data, tag_size)) + goto error; + break; + case 0x3002: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 8) + goto error; + descriptor->container_duration = GST_READ_UINT64_BE (tag_data); + break; + case 0x3004: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&descriptor->essence_container, tag_data, 16); + break; + case 0x3005: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&descriptor->codec, tag_data, 16); + break; + default: + if (type != MXF_METADATA_FILE_DESCRIPTOR) + goto next; + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &((MXFMetadataGenericDescriptor *) descriptor)->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed file descriptor:"); + GST_DEBUG (" linked track id = %u", descriptor->linked_track_id); + GST_DEBUG (" sample rate = %d/%d", descriptor->sample_rate.n, + descriptor->sample_rate.d); + GST_DEBUG (" container duration = %" G_GINT64_FORMAT, + descriptor->container_duration); + GST_DEBUG (" essence container = %s", + mxf_ul_to_string (&descriptor->essence_container, str)); + GST_DEBUG (" codec = %s", mxf_ul_to_string (&descriptor->codec, str)); + + return TRUE; + +error: + GST_ERROR ("Invalid file descriptor"); + mxf_metadata_file_descriptor_reset (descriptor); + + return FALSE; +} + +void +mxf_metadata_file_descriptor_reset (MXFMetadataFileDescriptor * descriptor) +{ + g_return_if_fail (descriptor != NULL); + + mxf_metadata_generic_descriptor_reset ((MXFMetadataGenericDescriptor *) + descriptor); + + memset (descriptor, 0, sizeof (MXFMetadataFileDescriptor)); +} + +gboolean +mxf_metadata_generic_sound_essence_descriptor_parse (const MXFUL * key, + MXFMetadataGenericSoundEssenceDescriptor * descriptor, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (descriptor, 0, sizeof (MXFMetadataGenericSoundEssenceDescriptor)); + + if (!mxf_metadata_file_descriptor_parse (key, + (MXFMetadataFileDescriptor *) descriptor, primer, type, data, size)) + goto error; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3d03: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!mxf_fraction_parse (&descriptor->audio_sampling_rate, tag_data, + tag_size)) + goto error; + break; + case 0x3d02: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->locked = (GST_READ_UINT8 (tag_data) != 0); + break; + case 0x3d04: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->audio_ref_level = GST_READ_UINT8 (tag_data); + break; + case 0x3d05: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->electro_spatial_formulation = GST_READ_UINT8 (tag_data); + break; + case 0x3d07: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->channel_count = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d01: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->quantization_bits = GST_READ_UINT32_BE (tag_data); + break; + case 0x3d0c: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->dial_norm = GST_READ_UINT8 (tag_data); + break; + case 0x3d06: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&descriptor->sound_essence_compression, tag_data, 16); + break; + default: + if (type != MXF_METADATA_GENERIC_SOUND_ESSENCE_DESCRIPTOR) + goto next; + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &((MXFMetadataGenericDescriptor *) descriptor)->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed generic sound essence descriptor:"); + GST_DEBUG (" audio sampling rate = %d/%d", descriptor->audio_sampling_rate.n, + descriptor->audio_sampling_rate.d); + GST_DEBUG (" locked = %s", (descriptor->locked) ? "yes" : "no"); + GST_DEBUG (" audio ref level = %d", descriptor->audio_ref_level); + GST_DEBUG (" electro spatial formulation = %u", + descriptor->electro_spatial_formulation); + GST_DEBUG (" channel count = %u", descriptor->channel_count); + GST_DEBUG (" quantization bits = %u", descriptor->quantization_bits); + GST_DEBUG (" dial norm = %d", descriptor->dial_norm); + GST_DEBUG (" sound essence compression = %s", + mxf_ul_to_string (&descriptor->sound_essence_compression, str)); + + return TRUE; + +error: + GST_ERROR ("Invalid generic sound essence descriptor"); + mxf_metadata_generic_sound_essence_descriptor_reset (descriptor); + + return FALSE; +} + +void mxf_metadata_generic_sound_essence_descriptor_reset + (MXFMetadataGenericSoundEssenceDescriptor * descriptor) +{ + g_return_if_fail (descriptor != NULL); + + mxf_metadata_file_descriptor_reset ((MXFMetadataFileDescriptor *) descriptor); + + memset (descriptor, 0, sizeof (MXFMetadataGenericSoundEssenceDescriptor)); +} + +gboolean +mxf_metadata_generic_picture_essence_descriptor_parse (const MXFUL * key, + MXFMetadataGenericPictureEssenceDescriptor * descriptor, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (descriptor, 0, sizeof (MXFMetadataGenericPictureEssenceDescriptor)); + + if (!mxf_metadata_file_descriptor_parse (key, + (MXFMetadataFileDescriptor *) descriptor, primer, type, data, size)) + goto error; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3215: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->signal_standard = GST_READ_UINT8 (tag_data); + break; + case 0x320c: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->frame_layout = GST_READ_UINT8 (tag_data); + break; + case 0x3203: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->stored_width = GST_READ_UINT32_BE (tag_data); + break; + case 0x3202: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->stored_height = GST_READ_UINT32_BE (tag_data); + break; + case 0x3216: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->stored_f2_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x3205: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->sampled_width = GST_READ_UINT32_BE (tag_data); + break; + case 0x3204: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->sampled_height = GST_READ_UINT32_BE (tag_data); + break; + case 0x3206: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->sampled_x_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x3207: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->sampled_y_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x3208: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->display_height = GST_READ_UINT32_BE (tag_data); + break; + case 0x3209: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->display_width = GST_READ_UINT32_BE (tag_data); + break; + case 0x320a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->display_x_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x320b: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->display_y_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x3217: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->display_f2_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x320e: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!mxf_fraction_parse (&descriptor->aspect_ratio, tag_data, tag_size)) + goto error; + break; + case 0x3218: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->active_format_descriptor = GST_READ_UINT8 (tag_data); + break; + case 0x320d: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size < 8) + goto error; + + if (GST_READ_UINT32_BE (tag_data) == 0) + goto next; + + if (GST_READ_UINT32_BE (tag_data) != 2 && + GST_READ_UINT32_BE (tag_data + 4) != 4) + goto error; + + if (tag_size != 16) + goto error; + + descriptor->video_line_map[0] = GST_READ_UINT32_BE (tag_data + 8); + descriptor->video_line_map[1] = GST_READ_UINT32_BE (tag_data + 12); + break; + case 0x320f: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->alpha_transparency = GST_READ_UINT8 (tag_data); + break; + case 0x3210: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&descriptor->capture_gamma, tag_data, 16); + break; + case 0x3211: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->image_alignment_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x3213: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->image_start_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x3214: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->image_end_offset = GST_READ_UINT32_BE (tag_data); + break; + case 0x3212: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->field_dominance = GST_READ_UINT8 (tag_data); + break; + case 0x3201: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&descriptor->picture_essence_coding, tag_data, 16); + break; + default: + if (type != MXF_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR) + goto next; + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &((MXFMetadataGenericDescriptor *) descriptor)->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed generic picture essence descriptor:"); + GST_DEBUG (" signal standard = %u", descriptor->signal_standard); + GST_DEBUG (" frame layout = %u", descriptor->frame_layout); + GST_DEBUG (" stored size = %ux%u (f2 = %d)", descriptor->stored_width, + descriptor->stored_height, descriptor->stored_f2_offset); + GST_DEBUG (" sampled size = %ux%u (offset = %d x %d)", + descriptor->sampled_width, descriptor->sampled_height, + descriptor->sampled_x_offset, descriptor->sampled_y_offset); + GST_DEBUG (" display size = %ux%u (f2 = %d, offset = %d x %d)", + descriptor->display_height, descriptor->display_width, + descriptor->display_x_offset, descriptor->display_y_offset, + descriptor->display_f2_offset); + GST_DEBUG (" aspect ratio = %d/%d", descriptor->aspect_ratio.n, + descriptor->aspect_ratio.d); + GST_DEBUG (" active format descriptor = %u", + descriptor->active_format_descriptor); + GST_DEBUG (" video line map = {%i, %i}", descriptor->video_line_map[0], + descriptor->video_line_map[1]); + GST_DEBUG (" alpha transparency = %u", descriptor->alpha_transparency); + GST_DEBUG (" capture gamma = %s", + mxf_ul_to_string (&descriptor->capture_gamma, str)); + GST_DEBUG (" image alignment offset = %u", + descriptor->image_alignment_offset); + GST_DEBUG (" image start offset = %u", descriptor->image_start_offset); + GST_DEBUG (" image end offset = %u", descriptor->image_end_offset); + GST_DEBUG (" field dominance = %u", descriptor->field_dominance); + GST_DEBUG (" picture essence coding = %s", + mxf_ul_to_string (&descriptor->picture_essence_coding, str)); + + return TRUE; + +error: + GST_ERROR ("Invalid generic picture essence descriptor"); + mxf_metadata_generic_picture_essence_descriptor_reset (descriptor); + + return FALSE; +} + +void mxf_metadata_generic_picture_essence_descriptor_reset + (MXFMetadataGenericPictureEssenceDescriptor * descriptor) +{ + g_return_if_fail (descriptor != NULL); + + mxf_metadata_file_descriptor_reset ((MXFMetadataFileDescriptor *) descriptor); + + memset (descriptor, 0, sizeof (MXFMetadataGenericPictureEssenceDescriptor)); +} + +gboolean +mxf_metadata_cdci_picture_essence_descriptor_parse (const MXFUL * key, + MXFMetadataCDCIPictureEssenceDescriptor * descriptor, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (descriptor, 0, sizeof (MXFMetadataCDCIPictureEssenceDescriptor)); + + if (!mxf_metadata_generic_picture_essence_descriptor_parse (key, + (MXFMetadataGenericPictureEssenceDescriptor *) descriptor, primer, + type, data, size)) + goto error; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3301: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->component_depth = GST_READ_UINT32_BE (tag_data); + break; + case 0x3302: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->horizontal_subsampling = GST_READ_UINT32_BE (tag_data); + break; + case 0x3308: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->vertical_subsampling = GST_READ_UINT32_BE (tag_data); + break; + case 0x3303: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->color_siting = GST_READ_UINT8 (tag_data); + break; + case 0x330b: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 1) + goto error; + descriptor->reversed_byte_order = GST_READ_UINT8 (tag_data); + break; + case 0x3307: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 2) + goto error; + descriptor->padding_bits = GST_READ_UINT16_BE (tag_data); + break; + case 0x3309: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->alpha_sample_depth = GST_READ_UINT32_BE (tag_data); + break; + case 0x3304: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->black_ref_level = GST_READ_UINT32_BE (tag_data); + break; + case 0x3305: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->white_ref_level = GST_READ_UINT32_BE (tag_data); + break; + case 0x3306: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 4) + goto error; + descriptor->color_range = GST_READ_UINT32_BE (tag_data); + break; + default: + if (type != MXF_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR) + goto next; + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &((MXFMetadataGenericDescriptor *) descriptor)->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed CDCI picture essence descriptor:"); + GST_DEBUG (" component depth = %u", descriptor->component_depth); + GST_DEBUG (" horizontal subsampling = %u", + descriptor->horizontal_subsampling); + GST_DEBUG (" vertical subsampling = %u", descriptor->vertical_subsampling); + GST_DEBUG (" color siting = %u", descriptor->color_siting); + GST_DEBUG (" reversed byte order = %s", + (descriptor->reversed_byte_order) ? "yes" : "no"); + GST_DEBUG (" padding bits = %d", descriptor->padding_bits); + GST_DEBUG (" alpha sample depth = %u", descriptor->alpha_sample_depth); + GST_DEBUG (" black ref level = %u", descriptor->black_ref_level); + GST_DEBUG (" white ref level = %u", descriptor->white_ref_level); + GST_DEBUG (" color range = %u", descriptor->color_range); + + return TRUE; + +error: + GST_ERROR ("Invalid CDCI picture essence descriptor"); + mxf_metadata_cdci_picture_essence_descriptor_reset (descriptor); + + return FALSE; +} + +void mxf_metadata_cdci_picture_essence_descriptor_reset + (MXFMetadataCDCIPictureEssenceDescriptor * descriptor) +{ + g_return_if_fail (descriptor != NULL); + + mxf_metadata_generic_picture_essence_descriptor_reset ( + (MXFMetadataGenericPictureEssenceDescriptor *) descriptor); + + memset (descriptor, 0, sizeof (MXFMetadataCDCIPictureEssenceDescriptor)); +} + +gboolean +mxf_metadata_multiple_descriptor_parse (const MXFUL * key, + MXFMetadataMultipleDescriptor * descriptor, + const MXFPrimerPack * primer, guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + gint i; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (descriptor, 0, sizeof (MXFMetadataMultipleDescriptor)); + + if (!mxf_metadata_file_descriptor_parse (key, + (MXFMetadataFileDescriptor *) descriptor, primer, type, data, size)) + goto error; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3f01:{ + guint32 len; + guint i; + + GST_WRITE_UINT16_BE (data, 0x0000); + + if (tag_size < 8) + goto error; + len = GST_READ_UINT32_BE (tag_data); + if (len == 0) + goto next; + + if (GST_READ_UINT32_BE (tag_data + 4) != 16) + goto error; + + descriptor->n_sub_descriptors = len; + descriptor->sub_descriptors_uids = g_new0 (MXFUL, len); + for (i = 0; i < len; i++) + memcpy (&descriptor->sub_descriptors_uids[i], tag_data + 8 + i * 16, + 16); + break; + } + default: + if (type != MXF_METADATA_MULTIPLE_DESCRIPTOR) + goto next; + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &((MXFMetadataGenericDescriptor *) descriptor)->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed multiple descriptor:"); + GST_DEBUG (" number of sub descriptors = %u", descriptor->n_sub_descriptors); + for (i = 0; i < descriptor->n_sub_descriptors; i++) + GST_DEBUG (" sub descriptor %d = %s", i, + mxf_ul_to_string (&descriptor->sub_descriptors_uids[i], str)); + + return TRUE; + +error: + GST_ERROR ("Invalid multiple descriptor"); + mxf_metadata_multiple_descriptor_reset (descriptor); + + return FALSE; +} + +void mxf_metadata_multiple_descriptor_reset + (MXFMetadataMultipleDescriptor * descriptor) +{ + g_return_if_fail (descriptor != NULL); + + g_free (descriptor->sub_descriptors_uids); + g_free (descriptor->sub_descriptors); + + mxf_metadata_file_descriptor_reset ((MXFMetadataFileDescriptor *) descriptor); + + memset (descriptor, 0, sizeof (MXFMetadataMultipleDescriptor)); +} + +gboolean +mxf_metadata_locator_parse (const MXFUL * key, + MXFMetadataLocator * locator, const MXFPrimerPack * primer, + guint16 type, const guint8 * data, gsize size) +{ + guint16 tag, tag_size; + const guint8 *tag_data; + gchar str[48]; + + g_return_val_if_fail (data != NULL, FALSE); + + memset (locator, 0, sizeof (MXFMetadataLocator)); + + locator->type = type; + + while (mxf_local_tag_parse (data, size, &tag, &tag_size, &tag_data)) { + if (tag_size == 0 || tag == 0x0000) + goto next; + + switch (tag) { + case 0x3c0a: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&locator->instance_uid, tag_data, 16); + break; + case 0x0102: + GST_WRITE_UINT16_BE (data, 0x0000); + if (tag_size != 16) + goto error; + memcpy (&locator->generation_uid, tag_data, 16); + break; + case 0x4101: + if (type != MXF_METADATA_TEXT_LOCATOR + && type != MXF_METADATA_NETWORK_LOCATOR) + goto DFLT; + GST_WRITE_UINT16_BE (data, 0x0000); + locator->location = mxf_utf16_to_utf8 (tag_data, tag_size); + break; + DFLT: + default: + GST_WRITE_UINT16_BE (data, 0x0000); + if (!gst_metadata_add_custom_tag (primer, tag, tag_data, tag_size, + &locator->other_tags)) + goto error; + break; + } + + next: + data += 4 + tag_size; + size -= 4 + tag_size; + } + + GST_DEBUG ("Parsed locator:"); + GST_DEBUG (" instance uid = %s", mxf_ul_to_string (&locator->instance_uid, + str)); + GST_DEBUG (" generation uid = %s", + mxf_ul_to_string (&locator->generation_uid, str)); + GST_DEBUG (" location = %s", GST_STR_NULL (locator->location)); + + return TRUE; + +error: + GST_ERROR ("Invalid locator"); + mxf_metadata_locator_reset (locator); + + return FALSE; +} + +void +mxf_metadata_locator_reset (MXFMetadataLocator * locator) +{ + g_return_if_fail (locator != NULL); + + if (locator->location) + g_free (locator->location); + + if (locator->other_tags) + g_hash_table_destroy (locator->other_tags); + + memset (locator, 0, sizeof (MXFMetadataLocator)); +} diff --git a/gst/mxf/mxfparse.h b/gst/mxf/mxfparse.h new file mode 100644 index 00000000..5b4acd3e --- /dev/null +++ b/gst/mxf/mxfparse.h @@ -0,0 +1,129 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __MXF_PARSE_H__ +#define __MXF_PARSE_H__ + +#include "mxftypes.h" + +typedef GstFlowReturn (*MXFEssenceElementHandler) (const MXFUL *key, GstBuffer *buffer, GstCaps *caps, MXFMetadataGenericPackage *package, MXFMetadataTrack *track, MXFMetadataStructuralComponent *component, gpointer mapping_data, GstBuffer **outbuf); + +gchar * mxf_ul_to_string (const MXFUL *ul, gchar str[48]); +gboolean mxf_ul_is_equal (const MXFUL *a, const MXFUL *b); +gboolean mxf_ul_is_zero (const MXFUL *ul); + +gchar *mxf_umid_to_string (const MXFUMID * umid, gchar str[96]); +gboolean mxf_umid_is_equal (const MXFUMID *a, const MXFUMID *b); +gboolean mxf_umid_is_zero (const MXFUMID *umid); + +gboolean mxf_is_mxf_packet (const MXFUL *key); + +gboolean mxf_is_partition_pack (const MXFUL *key); +gboolean mxf_is_header_partition_pack (const MXFUL *key); +gboolean mxf_is_body_partition_pack (const MXFUL *key); +gboolean mxf_is_footer_partition_pack (const MXFUL *key); + +gboolean mxf_is_primer_pack (const MXFUL *key); + +gboolean mxf_is_metadata (const MXFUL *key); + +gboolean mxf_is_random_index_pack (const MXFUL *key); +gboolean mxf_is_index_table_segment (const MXFUL *key); + +gboolean mxf_is_generic_container_system_item (const MXFUL *key); +gboolean mxf_is_generic_container_essence_element (const MXFUL *key); + +gboolean mxf_is_generic_container_essence_container_label (const MXFUL *key); + +gboolean mxf_is_fill (const MXFUL *key); + +gchar * mxf_utf16_to_utf8 (const guint8 * data, guint16 size); + +gboolean mxf_product_version_parse (MXFProductVersion * product_version, + const guint8 * data, gsize size); + +gboolean mxf_fraction_parse (MXFFraction *fraction, const guint8 *data, guint16 size); + +gboolean mxf_timestamp_parse (MXFTimestamp * timestamp, const guint8 * data, gsize size); +gboolean mxf_timestamp_is_unknown (const MXFTimestamp *a); +gint mxf_timestamp_compare (const MXFTimestamp *a, const MXFTimestamp *b); + +gboolean mxf_partition_pack_parse (const MXFUL *key, MXFPartitionPack *pack, const guint8 *data, gsize size); +void mxf_partition_pack_reset (MXFPartitionPack *pack); + +gboolean mxf_primer_pack_parse (const MXFUL *key, MXFPrimerPack *pack, const guint8 *data, gsize size); +void mxf_primer_pack_reset (MXFPrimerPack *pack); + +gboolean mxf_local_tag_parse (const guint8 * data, gsize size, guint16 * tag, + guint16 * tag_size, const guint8 ** tag_data); +void gst_mxf_local_tag_free (MXFLocalTag *tag); + +gboolean gst_metadata_add_custom_tag (const MXFPrimerPack *primer, + guint16 tag, const guint8 *tag_data, guint16 tag_size, + GHashTable **hash_table); + +gboolean mxf_metadata_preface_parse (const MXFUL *key, MXFMetadataPreface *preface, const MXFPrimerPack *primer, const guint8 *data, gsize size); +void mxf_metadata_preface_reset (MXFMetadataPreface *preface); + +gboolean mxf_metadata_identification_parse (const MXFUL *key, MXFMetadataIdentification *identification, const MXFPrimerPack *primer, const guint8 *data, gsize size); +void mxf_metadata_identification_reset (MXFMetadataIdentification *identification); + +gboolean mxf_metadata_content_storage_parse (const MXFUL *key, MXFMetadataContentStorage *content_storage, const MXFPrimerPack *primer, const guint8 *data, gsize size); +void mxf_metadata_content_storage_reset (MXFMetadataContentStorage *content_storage); + +gboolean mxf_metadata_essence_container_data_parse (const MXFUL *key, MXFMetadataEssenceContainerData *essence_container_data, const MXFPrimerPack *primer, const guint8 *data, gsize size); +void mxf_metadata_essence_container_data_reset (MXFMetadataEssenceContainerData *essence_container_data); + +gboolean mxf_metadata_generic_package_parse (const MXFUL *key, MXFMetadataGenericPackage *generic_package, const MXFPrimerPack *primer, const guint8 *data, gsize size); +void mxf_metadata_generic_package_reset (MXFMetadataGenericPackage *generic_package); + +gboolean mxf_metadata_track_parse (const MXFUL *key, MXFMetadataTrack *track, const MXFPrimerPack *primer, const guint8 *data, gsize size); +void mxf_metadata_track_reset (MXFMetadataTrack *track); + +MXFMetadataTrackType mxf_metadata_track_identifier_parse (const MXFUL *track_identifier); + +gboolean mxf_metadata_sequence_parse (const MXFUL *key, MXFMetadataSequence *sequence, const MXFPrimerPack *primer, const guint8 *data, gsize size); +void mxf_metadata_sequence_reset (MXFMetadataSequence *sequence); + +gboolean mxf_metadata_structural_component_parse (const MXFUL *key, MXFMetadataStructuralComponent *component, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_structural_component_reset (MXFMetadataStructuralComponent *component); + +gboolean mxf_metadata_generic_descriptor_parse (const MXFUL *key, MXFMetadataGenericDescriptor *descriptor, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_generic_descriptor_reset (MXFMetadataGenericDescriptor *descriptor); + +gboolean mxf_metadata_file_descriptor_parse (const MXFUL *key, MXFMetadataFileDescriptor *descriptor, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_file_descriptor_reset (MXFMetadataFileDescriptor *descriptor); + +gboolean mxf_metadata_generic_sound_essence_descriptor_parse (const MXFUL *key, MXFMetadataGenericSoundEssenceDescriptor *descriptor, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_generic_sound_essence_descriptor_reset (MXFMetadataGenericSoundEssenceDescriptor *descriptor); + +gboolean mxf_metadata_generic_picture_essence_descriptor_parse (const MXFUL *key, MXFMetadataGenericPictureEssenceDescriptor *descriptor, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_generic_picture_essence_descriptor_reset (MXFMetadataGenericPictureEssenceDescriptor *descriptor); + +gboolean mxf_metadata_cdci_picture_essence_descriptor_parse (const MXFUL *key, MXFMetadataCDCIPictureEssenceDescriptor *descriptor, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_cdci_picture_essence_descriptor_reset (MXFMetadataCDCIPictureEssenceDescriptor *descriptor); + +gboolean mxf_metadata_multiple_descriptor_parse (const MXFUL *key, MXFMetadataMultipleDescriptor *descriptor, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_multiple_descriptor_reset (MXFMetadataMultipleDescriptor *descriptor); + +gboolean mxf_metadata_locator_parse (const MXFUL *key, MXFMetadataLocator *locator, const MXFPrimerPack *primer, guint16 type, const guint8 *data, gsize size); +void mxf_metadata_locator_reset (MXFMetadataLocator *locator); + +#endif /* __MXF_PARSE_H__ */ + diff --git a/gst/mxf/mxftypes.h b/gst/mxf/mxftypes.h new file mode 100644 index 00000000..c253f2c7 --- /dev/null +++ b/gst/mxf/mxftypes.h @@ -0,0 +1,463 @@ +/* GStreamer + * Copyright (C) 2008 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __MXF_TYPES_H__ +#define __MXF_TYPES_H__ + +/* SMPTE 377M 3.2 */ +typedef struct { + guint8 u[16]; +} MXFUL; + +/* SMPTE 377M 3.2 */ +typedef struct { + guint8 u[32]; +} MXFUMID; + +/* SMPTE 377M 3.3 */ +typedef struct { + gint16 year; + guint8 month; + guint8 day; + guint8 hour; + guint8 minute; + guint8 second; + guint8 quarter_msecond; +} MXFTimestamp; + +/* SMPTE 377M 3.3 */ +typedef struct { + guint16 major; + guint16 minor; + guint16 patch; + guint16 build; + guint16 release; +} MXFProductVersion; + +/* SMPTE 377M 3.3 */ +typedef struct { + gint32 n; + gint32 d; +} MXFFraction; + +/* SMPTE 377M 8.3 */ +typedef struct { + MXFUL key; + guint16 size; + guint8 *data; +} MXFLocalTag; + +typedef enum { + MXF_PARTITION_PACK_HEADER, + MXF_PARTITION_PACK_BODY, + MXF_PARTITION_PACK_FOOTER +} MXFPartitionPackType; + +/* SMPTE 377M 6.1, Table 1 and 2 */ +typedef struct { + gboolean valid; + + MXFPartitionPackType type; + + gboolean closed; + gboolean complete; + + guint16 major_version; + guint16 minor_version; + + guint32 kag_size; + + guint64 this_partition; + guint64 prev_partition; + guint64 footer_partition; + + guint64 header_byte_count; + guint64 index_byte_count; + + guint32 index_sid; + + guint64 body_offset; + + guint32 body_sid; + + MXFUL operational_pattern; + + guint32 n_essence_containers; + MXFUL *essence_containers; +} MXFPartitionPack; + +/* SMPTE 377M 8.1 */ +typedef struct { + gboolean valid; + + GHashTable *mappings; +} MXFPrimerPack; + +/* SMPTE 377M 8.6 table 14 */ +#define MXF_METADATA_PREFACE (0x012f) +#define MXF_METADATA_IDENTIFICATION (0x0130) +#define MXF_METADATA_CONTENT_STORAGE (0x0118) +#define MXF_METADATA_ESSENCE_CONTAINER_DATA (0x0123) +#define MXF_METADATA_MATERIAL_PACKAGE (0x0136) +#define MXF_METADATA_SOURCE_PACKAGE (0x0137) +#define MXF_METADATA_TRACK (0x013b) +#define MXF_METADATA_EVENT_TRACK (0x0139) +#define MXF_METADATA_STATIC_TRACK (0x013a) +#define MXF_METADATA_SEQUENCE (0x010f) +#define MXF_METADATA_SOURCE_CLIP (0x0111) +#define MXF_METADATA_TIMECODE_COMPONENT (0x0114) +#define MXF_METADATA_DM_SEGMENT (0x0141) +#define MXF_METADATA_DM_SOURCE_CLIP (0x0145) +#define MXF_METADATA_FILE_DESCRIPTOR (0x0125) +#define MXF_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR (0x0127) +#define MXF_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR (0x0128) +#define MXF_METADATA_RGBA_PICTURE_ESSENCE_DESCRIPTOR (0x0129) +#define MXF_METADATA_GENERIC_SOUND_ESSENCE_DESCRIPTOR (0x0142) +#define MXF_METADATA_GENERIC_DATA_ESSENCE_DESCRIPTOR (0x0143) +#define MXF_METADATA_MULTIPLE_DESCRIPTOR (0x0144) +#define MXF_METADATA_NETWORK_LOCATOR (0x0132) +#define MXF_METADATA_TEXT_LOCATOR (0x0133) + +/* SMPTE 377M Annex A, B, C, D */ +typedef struct _MXFMetadataPreface MXFMetadataPreface; +typedef struct _MXFMetadataIdentification MXFMetadataIdentification; +typedef struct _MXFMetadataContentStorage MXFMetadataContentStorage; +typedef struct _MXFMetadataEssenceContainerData MXFMetadataEssenceContainerData; +typedef struct _MXFMetadataGenericPackage MXFMetadataGenericPackage; +typedef MXFMetadataGenericPackage MXFMetadataMaterialPackage; +typedef MXFMetadataGenericPackage MXFMetadataSourcePackage; +typedef struct _MXFMetadataTrack MXFMetadataTrack; +typedef struct _MXFMetadataSequence MXFMetadataSequence; +typedef struct _MXFMetadataStructuralComponent MXFMetadataStructuralComponent; +typedef struct _MXFMetadataGenericDescriptor MXFMetadataGenericDescriptor; +typedef struct _MXFMetadataFileDescriptor MXFMetadataFileDescriptor; +typedef struct _MXFMetadataGenericPictureEssenceDescriptor MXFMetadataGenericPictureEssenceDescriptor; +typedef struct _MXFMetadataCDCIPictureEssenceDescriptor MXFMetadataCDCIPictureEssenceDescriptor; +typedef struct _MXFMetadataGenericSoundEssenceDescriptor MXFMetadataGenericSoundEssenceDescriptor; +typedef struct _MXFMetadataMultipleDescriptor MXFMetadataMultipleDescriptor; +typedef struct _MXFMetadataLocator MXFMetadataLocator; + +struct _MXFMetadataPreface { + MXFUL instance_uid; + MXFUL generation_uid; + + MXFTimestamp last_modified_date; + guint16 version; + + guint32 object_model_version; + + MXFUL primary_package_uid; + MXFMetadataGenericPackage *primary_package; + + guint32 n_identifications; + MXFUL *identifications_uids; + MXFMetadataIdentification **identifications; + + MXFUL content_storage_uid; + MXFMetadataContentStorage *content_storage; + + MXFUL operational_pattern; + + guint32 n_essence_containers; + MXFUL *essence_containers; + + guint32 n_dm_schemes; + MXFUL *dm_schemes; + + GHashTable *other_tags; +}; + +struct _MXFMetadataIdentification { + MXFUL instance_uid; + MXFUL generation_uid; + + gchar *company_name; + + gchar *product_name; + MXFProductVersion product_version; + + gchar *version_string; + + MXFUL product_uid; + + MXFTimestamp modification_date; + + MXFProductVersion toolkit_version; + + gchar *platform; + + GHashTable *other_tags; +}; + +struct _MXFMetadataContentStorage { + MXFUL instance_uid; + MXFUL generation_uid; + + guint32 n_packages; + MXFUL *packages_uids; + MXFMetadataGenericPackage **packages; + + guint32 n_essence_container_data; + MXFUL *essence_container_data_uids; + MXFMetadataEssenceContainerData **essence_container_data; + + GHashTable *other_tags; +}; + +struct _MXFMetadataEssenceContainerData { + MXFUL instance_uid; + + MXFUMID linked_package_uid; + MXFMetadataGenericPackage *linked_package; + + MXFUL generation_uid; + + guint32 index_sid; + guint32 body_sid; + + GHashTable *other_tags; +}; + +typedef enum { + MXF_METADATA_GENERIC_PACKAGE_SOURCE = 0, + MXF_METADATA_GENERIC_PACKAGE_MATERIAL = 1, + MXF_METADATA_GENERIC_PACKAGE_TOP_LEVEL_SOURCE = 2 +} MXFMetadataGenericPackageType; + +struct _MXFMetadataGenericPackage { + MXFUL instance_uid; + MXFUMID package_uid; + MXFUL generation_uid; + + MXFMetadataGenericPackageType type; + + gchar *name; + MXFTimestamp package_creation_date; + MXFTimestamp package_modified_date; + + guint32 n_tracks; + MXFUL *tracks_uids; + MXFMetadataTrack **tracks; + + guint n_timecode_tracks; + guint n_metadata_tracks; + guint n_essence_tracks; + guint n_other_tracks; + + /* Only in Source packages */ + MXFUL descriptors_uid; + guint32 n_descriptors; + MXFMetadataGenericDescriptor **descriptors; + + GHashTable *other_tags; +}; + +typedef enum { + MXF_METADATA_TRACK_UNKNOWN = 0x00, + MXF_METADATA_TRACK_TIMECODE_12M_INACTIVE = 0x10, + MXF_METADATA_TRACK_TIMECODE_12M_ACTIVE = 0x11, + MXF_METADATA_TRACK_TIMECODE_309M = 0x12, + MXF_METADATA_TRACK_METADATA = 0x20, + MXF_METADATA_TRACK_PICTURE_ESSENCE = 0x30, + MXF_METADATA_TRACK_SOUND_ESSENCE = 0x31, + MXF_METADATA_TRACK_DATA_ESSENCE = 0x32, + MXF_METADATA_TRACK_AUXILIARY_DATA = 0x40, + MXF_METADATA_TRACK_PARSED_TEXT = 0x41 +} MXFMetadataTrackType; + +struct _MXFMetadataTrack { + MXFUL instance_uid; + MXFUL generation_uid; + + guint32 track_id; + guint32 track_number; + + gchar *track_name; + + MXFFraction edit_rate; + + gint64 origin; + + MXFUL sequence_uid; + MXFMetadataSequence *sequence; + + MXFMetadataFileDescriptor **descriptor; + guint n_descriptor; + + GHashTable *other_tags; +}; + +struct _MXFMetadataSequence { + MXFUL instance_uid; + MXFUL generation_uid; + + MXFUL data_definition; + + gint64 duration; + + guint32 n_structural_components; + MXFUL *structural_components_uids; + MXFMetadataStructuralComponent **structural_components; + + GHashTable *other_tags; +}; + +struct _MXFMetadataStructuralComponent { + guint16 type; + + MXFUL instance_uid; + MXFUL generation_uid; + + MXFUL data_definition; + + gint64 duration; + + union { + struct { + gint64 start_timecode; + guint16 rounded_timecode_base; + gboolean drop_frame; + } timecode_component; + + struct { + gint64 start_position; + MXFUMID source_package_id; + MXFMetadataGenericPackage *source_package; + + guint32 source_track_id; + } source_clip; + }; + + GHashTable *other_tags; +}; + +struct _MXFMetadataGenericDescriptor { + guint16 type; + + MXFUL instance_uid; + MXFUL generation_uid; + + guint32 n_locators; + MXFUL *locators_uids; + MXFMetadataLocator **locators; + + gboolean is_file_descriptor; + + GHashTable *other_tags; +}; + +struct _MXFMetadataFileDescriptor { + MXFMetadataGenericDescriptor parent; + + guint32 linked_track_id; + + MXFFraction sample_rate; + gint64 container_duration; + MXFUL essence_container; + MXFUL codec; +}; + +struct _MXFMetadataGenericPictureEssenceDescriptor { + MXFMetadataFileDescriptor parent; + + guint8 signal_standard; + guint8 frame_layout; + + guint32 stored_width; + guint32 stored_height; + gint32 stored_f2_offset; + guint32 sampled_width; + guint32 sampled_height; + gint32 sampled_x_offset; + gint32 sampled_y_offset; + guint32 display_height; + guint32 display_width; + gint32 display_x_offset; + gint32 display_y_offset; + gint32 display_f2_offset; + MXFFraction aspect_ratio; + + guint8 active_format_descriptor; + gint32 video_line_map[2]; + guint8 alpha_transparency; + MXFUL capture_gamma; + + guint32 image_alignment_offset; + guint32 image_start_offset; + guint32 image_end_offset; + + guint8 field_dominance; + + MXFUL picture_essence_coding; +}; + +struct _MXFMetadataCDCIPictureEssenceDescriptor { + MXFMetadataGenericPictureEssenceDescriptor parent; + + guint32 component_depth; + guint32 horizontal_subsampling; + guint32 vertical_subsampling; + guint8 color_siting; + gboolean reversed_byte_order; + gint16 padding_bits; + guint32 alpha_sample_depth; + guint32 black_ref_level; + guint32 white_ref_level; + guint32 color_range; +}; + +struct _MXFMetadataGenericSoundEssenceDescriptor { + MXFMetadataFileDescriptor parent; + + MXFFraction audio_sampling_rate; + + gboolean locked; + + gint8 audio_ref_level; + + guint8 electro_spatial_formulation; + + guint32 channel_count; + guint32 quantization_bits; + + gint8 dial_norm; + + MXFUL sound_essence_compression; +}; + +struct _MXFMetadataMultipleDescriptor { + MXFMetadataFileDescriptor parent; + + MXFUL *sub_descriptors_uids; + guint32 n_sub_descriptors; + MXFMetadataGenericDescriptor **sub_descriptors; +}; + +struct _MXFMetadataLocator { + guint16 type; + + MXFUL instance_uid; + MXFUL generation_uid; + + gchar *location; + + GHashTable *other_tags; +}; + +#endif /* __MXF_TYPES_H__ */ |