diff options
Diffstat (limited to 'gst')
-rw-r--r-- | gst/deinterlace/gstdeinterlace.c | 3 | ||||
-rw-r--r-- | gst/mxf/Makefile.am | 10 | ||||
-rw-r--r-- | gst/mxf/mxf.c | 8 | ||||
-rw-r--r-- | gst/mxf/mxfaes-bwf.c | 533 | ||||
-rw-r--r-- | gst/mxf/mxfalaw.c | 160 | ||||
-rw-r--r-- | gst/mxf/mxfdemux.c | 2 | ||||
-rw-r--r-- | gst/mxf/mxfdv-dif.c | 98 | ||||
-rw-r--r-- | gst/mxf/mxfjpeg2000.c | 121 | ||||
-rw-r--r-- | gst/mxf/mxfmetadata.c | 2296 | ||||
-rw-r--r-- | gst/mxf/mxfmetadata.h | 6 | ||||
-rw-r--r-- | gst/mxf/mxfmpeg.c | 555 | ||||
-rw-r--r-- | gst/mxf/mxfmux.c | 1429 | ||||
-rw-r--r-- | gst/mxf/mxfmux.h | 103 | ||||
-rw-r--r-- | gst/mxf/mxfparse.c | 289 | ||||
-rw-r--r-- | gst/mxf/mxfparse.h | 12 | ||||
-rw-r--r-- | gst/mxf/mxftypes.h | 8 | ||||
-rw-r--r-- | gst/mxf/mxful.c | 268 | ||||
-rw-r--r-- | gst/mxf/mxful.h | 59 | ||||
-rw-r--r-- | gst/mxf/mxfup.c | 451 | ||||
-rw-r--r-- | gst/mxf/mxfvc3.c | 87 | ||||
-rw-r--r-- | gst/mxf/mxfwrite.c | 567 | ||||
-rw-r--r-- | gst/mxf/mxfwrite.h | 85 |
22 files changed, 6858 insertions, 292 deletions
diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c index 70fe1420..b2a25afe 100644 --- a/gst/deinterlace/gstdeinterlace.c +++ b/gst/deinterlace/gstdeinterlace.c @@ -353,6 +353,9 @@ gst_deinterlace_transform_ip (GstBaseTransform * trans, GstBuffer * buf) height = filter->uv_height; fill_value = 128; break; + default: + g_assert_not_reached (); + break; } for (x = 0; x < width; x++) { diff --git a/gst/mxf/Makefile.am b/gst/mxf/Makefile.am index ed824355..96534c4b 100644 --- a/gst/mxf/Makefile.am +++ b/gst/mxf/Makefile.am @@ -14,7 +14,10 @@ libgstmxf_la_SOURCES = \ mxfup.c \ mxfvc3.c \ mxfmetadata.c \ - mxfdms1.c + mxfdms1.c \ + mxfwrite.c \ + mxfmux.c \ + mxful.c libgstmxf_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) libgstmxf_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \ @@ -36,5 +39,8 @@ noinst_HEADERS = \ mxfvc3.h \ mxftypes.h \ mxfmetadata.h \ - mxfdms1.h + mxfdms1.h \ + mxfwrite.h \ + mxfmux.h \ + mxful.h diff --git a/gst/mxf/mxf.c b/gst/mxf/mxf.c index ec477721..a906f1d8 100644 --- a/gst/mxf/mxf.c +++ b/gst/mxf/mxf.c @@ -24,6 +24,7 @@ #include "mxfquark.h" #include "mxfdemux.h" +#include "mxfmux.h" #include "mxfaes-bwf.h" #include "mxfmpeg.h" #include "mxfdv-dif.h" @@ -53,6 +54,8 @@ mxf_init (void) static gboolean plugin_init (GstPlugin * plugin) { + GST_DEBUG_CATEGORY_INIT (mxf_debug, "mxf", 0, "MXF"); + mxf_init (); mxf_quark_initialize (); mxf_metadata_init_types (); @@ -67,11 +70,10 @@ plugin_init (GstPlugin * plugin) mxf_dms1_initialize (); if (!gst_element_register (plugin, "mxfdemux", GST_RANK_PRIMARY, - GST_TYPE_MXF_DEMUX)) + GST_TYPE_MXF_DEMUX) || + !gst_element_register (plugin, "mxfmux", GST_RANK_NONE, GST_TYPE_MXF_MUX)) return FALSE; - GST_DEBUG_CATEGORY_INIT (mxf_debug, "mxf", 0, "MXF"); - return TRUE; } diff --git a/gst/mxf/mxfaes-bwf.c b/gst/mxf/mxfaes-bwf.c index d40ff6a6..df9c0047 100644 --- a/gst/mxf/mxfaes-bwf.c +++ b/gst/mxf/mxfaes-bwf.c @@ -35,6 +35,7 @@ #include <gst/gst.h> #include <string.h> +#include "mxfwrite.h" #include "mxfaes-bwf.h" #include "mxfquark.h" @@ -305,6 +306,210 @@ mxf_metadata_wave_audio_essence_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_wave_audio_essence_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataWaveAudioEssenceDescriptor *self = + MXF_METADATA_WAVE_AUDIO_ESSENCE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_wave_audio_essence_descriptor_parent_class)->write_tags (m, + primer); + MXFLocalTag *t; + static const guint8 block_align_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 sequence_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00 + }; + static const guint8 avg_bps_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x03, 0x05, 0x00, 0x00, 0x00 + }; + static const guint8 channel_assignment_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x07, + 0x04, 0x02, 0x01, 0x01, 0x05, 0x00, 0x00, 0x00 + }; + static const guint8 peak_envelope_version_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x06, 0x00, 0x00, 0x00 + }; + static const guint8 peak_envelope_format_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x07, 0x00, 0x00, 0x00 + }; + static const guint8 points_per_peak_value_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x08, 0x00, 0x00, 0x00 + }; + static const guint8 peak_envelope_block_size_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x09, 0x00, 0x00, 0x00 + }; + static const guint8 peak_channels_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x0A, 0x00, 0x00, 0x00 + }; + static const guint8 peak_frames_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x0B, 0x00, 0x00, 0x00 + }; + static const guint8 peak_of_peaks_position_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x0C, 0x00, 0x00, 0x00 + }; + static const guint8 peak_envelope_timestamp_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x0D, 0x00, 0x00, 0x00 + }; + static const guint8 peak_envelope_data_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x08, + 0x04, 0x02, 0x03, 0x01, 0x0E, 0x00, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &block_align_ul, 16); + t->size = 2; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT16_BE (t->data, self->block_align); + mxf_primer_pack_add_mapping (primer, 0x3d0a, &t->key); + ret = g_list_prepend (ret, t); + + if (self->sequence_offset) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sequence_offset_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->sequence_offset); + mxf_primer_pack_add_mapping (primer, 0x3d0b, &t->key); + ret = g_list_prepend (ret, t); + } + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &avg_bps_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->avg_bps); + mxf_primer_pack_add_mapping (primer, 0x3d09, &t->key); + ret = g_list_prepend (ret, t); + + if (!mxf_ul_is_zero (&self->channel_assignment)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &channel_assignment_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->channel_assignment, 16); + mxf_primer_pack_add_mapping (primer, 0x3d32, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->peak_envelope_version) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &peak_envelope_version_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->peak_envelope_version); + mxf_primer_pack_add_mapping (primer, 0x3d29, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->peak_envelope_format) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &peak_envelope_format_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->peak_envelope_format); + mxf_primer_pack_add_mapping (primer, 0x3d2a, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->points_per_peak_value) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &points_per_peak_value_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->points_per_peak_value); + mxf_primer_pack_add_mapping (primer, 0x3d2b, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->peak_envelope_block_size) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &peak_envelope_block_size_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->peak_envelope_block_size); + mxf_primer_pack_add_mapping (primer, 0x3d2c, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->peak_channels) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &peak_channels_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->peak_channels); + mxf_primer_pack_add_mapping (primer, 0x3d2d, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->peak_frames) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &peak_frames_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->peak_frames); + mxf_primer_pack_add_mapping (primer, 0x3d2e, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->peak_of_peaks_position) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &peak_of_peaks_position_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->peak_of_peaks_position); + mxf_primer_pack_add_mapping (primer, 0x3d2f, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_timestamp_is_unknown (&self->peak_envelope_timestamp)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &peak_envelope_timestamp_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + mxf_timestamp_write (&self->peak_envelope_timestamp, t->data); + mxf_primer_pack_add_mapping (primer, 0x3d30, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->peak_envelope_data) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &peak_envelope_data_ul, 16); + t->size = self->peak_envelope_data_length; + t->data = g_memdup (self->peak_envelope_data, t->size); + mxf_primer_pack_add_mapping (primer, 0x3d31, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_wave_audio_essence_descriptor_init (MXFMetadataWaveAudioEssenceDescriptor * self) @@ -324,6 +529,8 @@ static void metadata_base_class->name_quark = MXF_QUARK (WAVE_AUDIO_ESSENCE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_wave_audio_essence_descriptor_to_structure; + metadata_base_class->write_tags = + mxf_metadata_wave_audio_essence_descriptor_write_tags; metadata_class->type = 0x0148; } @@ -705,6 +912,139 @@ mxf_metadata_aes3_audio_essence_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_aes3_audio_essence_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataAES3AudioEssenceDescriptor *self = + MXF_METADATA_AES3_AUDIO_ESSENCE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_aes3_audio_essence_descriptor_parent_class)->write_tags (m, + primer); + MXFLocalTag *t; + static const guint8 emphasis_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x05, 0x01, 0x06, 0x00, 0x00, 0x00 + }; + static const guint8 block_start_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x02, 0x03, 0x00, 0x00, 0x00 + }; + static const guint8 auxiliary_bits_mode_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x05, 0x01, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 channel_status_mode_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00 + }; + static const guint8 fixed_channel_status_data_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x05, 0x01, 0x03, 0x00, 0x00, 0x00 + }; + static const guint8 user_data_mode_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x05, 0x01, 0x04, 0x00, 0x00, 0x00 + }; + static const guint8 fixed_user_data_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x05, 0x01, 0x05, 0x00, 0x00, 0x00 + }; + + if (self->emphasis) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &emphasis_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->emphasis); + mxf_primer_pack_add_mapping (primer, 0x3d0d, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->block_start_offset) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &block_start_offset_ul, 16); + t->size = 2; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT16_BE (t->data, self->block_start_offset); + mxf_primer_pack_add_mapping (primer, 0x3d0f, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->auxiliary_bits_mode) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &auxiliary_bits_mode_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->auxiliary_bits_mode); + mxf_primer_pack_add_mapping (primer, 0x3d08, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->channel_status_mode) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &channel_status_mode_ul, 16); + t->size = 8 + self->n_channel_status_mode; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_channel_status_mode); + GST_WRITE_UINT32_BE (t->data + 4, 1); + memcpy (t->data + 8, self->channel_status_mode, t->size); + mxf_primer_pack_add_mapping (primer, 0x3d10, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->fixed_channel_status_data) { + guint i; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &fixed_channel_status_data_ul, 16); + t->size = 8 + 24 * self->n_fixed_channel_status_data; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_fixed_channel_status_data); + GST_WRITE_UINT32_BE (t->data + 4, 24); + for (i = 0; i < self->n_fixed_channel_status_data; i++) + memcpy (t->data + 8 + 24 * i, self->fixed_channel_status_data[i], 24); + mxf_primer_pack_add_mapping (primer, 0x3d11, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->user_data_mode) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &user_data_mode_ul, 16); + t->size = 8 + self->n_user_data_mode; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_user_data_mode); + GST_WRITE_UINT32_BE (t->data + 4, 1); + memcpy (t->data + 8, self->user_data_mode, t->size); + mxf_primer_pack_add_mapping (primer, 0x3d12, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->fixed_user_data) { + guint i; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &fixed_user_data_ul, 16); + t->size = 8 + 24 * self->n_fixed_user_data; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_fixed_user_data); + GST_WRITE_UINT32_BE (t->data + 4, 24); + for (i = 0; i < self->n_fixed_user_data; i++) + memcpy (t->data + 8 + 24 * i, self->fixed_user_data[i], 24); + mxf_primer_pack_add_mapping (primer, 0x3d11, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} static void mxf_metadata_aes3_audio_essence_descriptor_init @@ -728,6 +1068,8 @@ static void metadata_base_class->name_quark = MXF_QUARK (AES3_AUDIO_ESSENCE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_aes3_audio_essence_descriptor_to_structure; + metadata_base_class->write_tags = + mxf_metadata_aes3_audio_essence_descriptor_write_tags; metadata_class->type = 0x0147; } @@ -1051,6 +1393,190 @@ static const MXFEssenceElementHandler mxf_aes_bwf_essence_handler = { mxf_aes_bwf_create_caps }; +typedef struct +{ + guint64 error; + gint width, rate, channels; + MXFFraction edit_rate; +} BWFMappingData; + +static GstFlowReturn +mxf_bwf_write_func (GstBuffer * buffer, GstCaps * caps, gpointer mapping_data, + GstAdapter * adapter, GstBuffer ** outbuf, gboolean flush) +{ + BWFMappingData *md = mapping_data; + guint bytes; + guint64 speu = + gst_util_uint64_scale (md->rate, md->edit_rate.d, md->edit_rate.n); + + md->error += (md->edit_rate.d * md->rate) % (md->edit_rate.n); + if (md->error >= md->edit_rate.n) { + md->error = 0; + speu += 1; + } + + bytes = (speu * md->channels * md->width) / 8; + + if (buffer) + gst_adapter_push (adapter, buffer); + + if (gst_adapter_available (adapter) == 0) + return GST_FLOW_OK; + + if (flush) + bytes = MIN (gst_adapter_available (adapter), bytes); + + if (gst_adapter_available (adapter) >= bytes) { + *outbuf = gst_adapter_take_buffer (adapter, bytes); + } + + if (gst_adapter_available (adapter) >= bytes) + return GST_FLOW_CUSTOM_SUCCESS; + else + return GST_FLOW_OK; +} + +static const guint8 bwf_essence_container_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x06, 0x01, 0x00 +}; + +static MXFMetadataFileDescriptor * +mxf_bwf_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataWaveAudioEssenceDescriptor *ret; + GstStructure *s; + BWFMappingData *md; + gint width, rate, channels, endianness; + + s = gst_caps_get_structure (caps, 0); + if (strcmp (gst_structure_get_name (s), "audio/x-raw-int") != 0 || + !gst_structure_get_int (s, "width", &width) || + !gst_structure_get_int (s, "rate", &rate) || + !gst_structure_get_int (s, "channels", &channels) || + !gst_structure_get_int (s, "endianness", &endianness)) { + GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps); + return NULL; + } + + ret = (MXFMetadataWaveAudioEssenceDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_WAVE_AUDIO_ESSENCE_DESCRIPTOR); + + memcpy (&ret->parent.parent.essence_container, &bwf_essence_container_ul, 16); + if (endianness == G_LITTLE_ENDIAN) + memcpy (&ret->parent.sound_essence_compression, + &mxf_sound_essence_compression_uncompressed, 16); + else + memcpy (&ret->parent.sound_essence_compression, + &mxf_sound_essence_compression_aiff, 16); + + ret->block_align = (width / 8) * channels; + ret->parent.quantization_bits = width; + ret->avg_bps = ret->block_align * rate; + + if (!mxf_metadata_generic_sound_essence_descriptor_from_caps (&ret->parent, + caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + *handler = mxf_bwf_write_func; + + md = g_new0 (BWFMappingData, 1); + md->width = width; + md->rate = rate; + md->channels = channels; + *mapping_data = md; + + return (MXFMetadataFileDescriptor *) ret; +} + +static void +mxf_bwf_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf) +{ + return; +} + +static void +mxf_bwf_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package, + MXFMetadataTimelineTrack * track, MXFFraction * edit_rate) +{ + guint i; + gdouble min = G_MAXDOUBLE; + BWFMappingData *md = mapping_data; + + for (i = 0; i < package->parent.n_tracks; i++) { + MXFMetadataTimelineTrack *tmp; + + if (!MXF_IS_METADATA_TIMELINE_TRACK (package->parent.tracks[i]) || + package->parent.tracks[i] == (MXFMetadataTrack *) track) + continue; + + tmp = MXF_METADATA_TIMELINE_TRACK (package->parent.tracks[i]); + if (((gdouble) tmp->edit_rate.n) / ((gdouble) tmp->edit_rate.d) < min) { + min = ((gdouble) tmp->edit_rate.n) / ((gdouble) tmp->edit_rate.d); + memcpy (edit_rate, &tmp->edit_rate, sizeof (MXFFraction)); + } + } + + if (min == G_MAXDOUBLE) { + /* 100ms edit units */ + edit_rate->n = 10; + edit_rate->d = 1; + } + + memcpy (&md->edit_rate, edit_rate, sizeof (MXFFraction)); +} + +static guint32 +mxf_bwf_get_track_number_template (MXFMetadataFileDescriptor * a, + GstCaps * caps, gpointer mapping_data) +{ + return (0x16 << 24) | (0x01 << 8); +} + +static MXFEssenceElementWriter mxf_bwf_essence_element_writer = { + mxf_bwf_get_descriptor, + mxf_bwf_update_descriptor, + mxf_bwf_get_edit_rate, + mxf_bwf_get_track_number_template, + NULL, + {{0,}} +}; + +#define BWF_CAPS \ + "audio/x-raw-int, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ + "width = (int) 32, " \ + "depth = (int) 32, " \ + "signed = (boolean) TRUE; " \ + "audio/x-raw-int, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ + "width = (int) 24, " \ + "depth = (int) 24, " \ + "signed = (boolean) TRUE; " \ + "audio/x-raw-int, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ + "width = (int) 16, " \ + "depth = (int) 16, " \ + "signed = (boolean) TRUE; " \ + "audio/x-raw-int, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ + "width = (int) 8, " \ + "depth = (int) 8, " \ + "signed = (boolean) FALSE" + void mxf_aes_bwf_init (void) { @@ -1058,4 +1584,11 @@ mxf_aes_bwf_init (void) mxf_metadata_register (MXF_TYPE_METADATA_AES3_AUDIO_ESSENCE_DESCRIPTOR); mxf_essence_element_handler_register (&mxf_aes_bwf_essence_handler); + + mxf_bwf_essence_element_writer.pad_template = + gst_pad_template_new ("bwf_audio_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, + gst_caps_from_string (BWF_CAPS)); + memcpy (&mxf_bwf_essence_element_writer.data_definition, + mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_SOUND_ESSENCE), 16); + mxf_essence_element_writer_register (&mxf_bwf_essence_element_writer); } diff --git a/gst/mxf/mxfalaw.c b/gst/mxf/mxfalaw.c index 074f1a48..40517af0 100644 --- a/gst/mxf/mxfalaw.c +++ b/gst/mxf/mxfalaw.c @@ -29,6 +29,7 @@ #include <string.h> #include "mxfalaw.h" +#include "mxfwrite.h" GST_DEBUG_CATEGORY_EXTERN (mxf_debug); #define GST_CAT_DEFAULT mxf_debug @@ -138,8 +139,167 @@ static const MXFEssenceElementHandler mxf_alaw_essence_element_handler = { mxf_alaw_create_caps }; +typedef struct +{ + guint64 error; + gint rate, channels; + MXFFraction edit_rate; +} ALawMappingData; + +static GstFlowReturn +mxf_alaw_write_func (GstBuffer * buffer, GstCaps * caps, gpointer mapping_data, + GstAdapter * adapter, GstBuffer ** outbuf, gboolean flush) +{ + ALawMappingData *md = mapping_data; + guint bytes; + guint64 speu = + gst_util_uint64_scale (md->rate, md->edit_rate.d, md->edit_rate.n); + + md->error += (md->edit_rate.d * md->rate) % (md->edit_rate.n); + if (md->error >= md->edit_rate.n) { + md->error = 0; + speu += 1; + } + + bytes = speu * md->channels; + + if (buffer) + gst_adapter_push (adapter, buffer); + + if (gst_adapter_available (adapter) == 0) + return GST_FLOW_OK; + + if (flush) + bytes = MIN (gst_adapter_available (adapter), bytes); + + if (gst_adapter_available (adapter) >= bytes) { + *outbuf = gst_adapter_take_buffer (adapter, bytes); + } + + if (gst_adapter_available (adapter) >= bytes) + return GST_FLOW_CUSTOM_SUCCESS; + else + return GST_FLOW_OK; +} + +static const guint8 alaw_essence_container_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x03, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x0a, 0x01, 0x00 +}; + +static const MXFUL mxf_sound_essence_compression_alaw = + { {0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x03, 0x04, 0x02, 0x02, 0x02, + 0x03, 0x01, 0x01, 0x00} +}; + +static MXFMetadataFileDescriptor * +mxf_alaw_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataGenericSoundEssenceDescriptor *ret; + GstStructure *s; + ALawMappingData *md; + gint rate, channels; + + s = gst_caps_get_structure (caps, 0); + if (strcmp (gst_structure_get_name (s), "audio/x-alaw") != 0 || + !gst_structure_get_int (s, "rate", &rate) || + !gst_structure_get_int (s, "channels", &channels)) { + GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps); + return NULL; + } + + ret = (MXFMetadataGenericSoundEssenceDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_GENERIC_SOUND_ESSENCE_DESCRIPTOR); + + memcpy (&ret->parent.essence_container, &alaw_essence_container_ul, 16); + memcpy (&ret->sound_essence_compression, &mxf_sound_essence_compression_alaw, + 16); + + if (!mxf_metadata_generic_sound_essence_descriptor_from_caps (ret, caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + *handler = mxf_alaw_write_func; + + md = g_new0 (ALawMappingData, 1); + md->rate = rate; + md->channels = channels; + *mapping_data = md; + + return (MXFMetadataFileDescriptor *) ret; +} + +static void +mxf_alaw_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf) +{ + return; +} + +static void +mxf_alaw_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package, + MXFMetadataTimelineTrack * track, MXFFraction * edit_rate) +{ + guint i; + gdouble min = G_MAXDOUBLE; + ALawMappingData *md = mapping_data; + + for (i = 0; i < package->parent.n_tracks; i++) { + MXFMetadataTimelineTrack *tmp; + + if (!MXF_IS_METADATA_TIMELINE_TRACK (package->parent.tracks[i]) || + package->parent.tracks[i] == (MXFMetadataTrack *) track) + continue; + + tmp = MXF_METADATA_TIMELINE_TRACK (package->parent.tracks[i]); + if (((gdouble) tmp->edit_rate.n) / ((gdouble) tmp->edit_rate.d) < min) { + min = ((gdouble) tmp->edit_rate.n) / ((gdouble) tmp->edit_rate.d); + memcpy (edit_rate, &tmp->edit_rate, sizeof (MXFFraction)); + } + } + + if (min == G_MAXDOUBLE) { + /* 100ms edit units */ + edit_rate->n = 10; + edit_rate->d = 1; + } + + memcpy (&md->edit_rate, edit_rate, sizeof (MXFFraction)); +} + +static guint32 +mxf_alaw_get_track_number_template (MXFMetadataFileDescriptor * a, + GstCaps * caps, gpointer mapping_data) +{ + return (0x16 << 24) | (0x08 << 8); +} + +static MXFEssenceElementWriter mxf_alaw_essence_element_writer = { + mxf_alaw_get_descriptor, + mxf_alaw_update_descriptor, + mxf_alaw_get_edit_rate, + mxf_alaw_get_track_number_template, + NULL, + {{0,}} +}; + +#define ALAW_CAPS \ + "audio/x-alaw, " \ + "rate = (int) [ 8000, 192000 ], " \ + "channels = (int) [ 1, 2 ]" + void mxf_alaw_init (void) { mxf_essence_element_handler_register (&mxf_alaw_essence_element_handler); + + mxf_alaw_essence_element_writer.pad_template = + gst_pad_template_new ("alaw_audio_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, + gst_caps_from_string (ALAW_CAPS)); + memcpy (&mxf_alaw_essence_element_writer.data_definition, + mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_SOUND_ESSENCE), 16); + mxf_essence_element_writer_register (&mxf_alaw_essence_element_writer); } diff --git a/gst/mxf/mxfdemux.c b/gst/mxf/mxfdemux.c index 9e486f1e..733e920d 100644 --- a/gst/mxf/mxfdemux.c +++ b/gst/mxf/mxfdemux.c @@ -1779,7 +1779,7 @@ gst_mxf_demux_handle_generic_container_essence_element (GstMXFDemux * demux, pad->last_stop); if (ret != GST_FLOW_OK) - break; + goto out; pad->current_essence_track_position++; diff --git a/gst/mxf/mxfdv-dif.c b/gst/mxf/mxfdv-dif.c index 10e71b78..220100a3 100644 --- a/gst/mxf/mxfdv-dif.c +++ b/gst/mxf/mxfdv-dif.c @@ -24,7 +24,8 @@ /* TODO: * - playbin hangs on a lot of MXF/DV-DIF files (bug #563827) * - decodebin2 creates loops inside the linking graph (bug #563828) - * - Forwarding of timestamps in dvdemux? + * - track descriptor might be multiple descriptor, one for sound, one for video + * - there might be 2 tracks for one essence, i.e. one audio/one video track */ #ifdef HAVE_CONFIG_H @@ -32,9 +33,11 @@ #endif #include <gst/gst.h> +#include <gst/video/video.h> #include <string.h> #include "mxfdv-dif.h" +#include "mxfwrite.h" GST_DEBUG_CATEGORY_EXTERN (mxf_debug); #define GST_CAT_DEFAULT mxf_debug @@ -104,6 +107,8 @@ mxf_dv_dif_create_caps (MXFMetadataTimelineTrack * track, GstTagList ** tags, MXFEssenceElementHandleFunc * handler, gpointer * mapping_data) { GstCaps *caps = NULL; + guint i; + MXFMetadataGenericPictureEssenceDescriptor *d = NULL; g_return_val_if_fail (track != NULL, NULL); @@ -112,6 +117,15 @@ mxf_dv_dif_create_caps (MXFMetadataTimelineTrack * track, GstTagList ** tags, return NULL; } + for (i = 0; i < track->parent.n_descriptor; i++) { + if (MXF_IS_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR (track-> + parent.descriptor[i])) { + d = MXF_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR (track-> + parent.descriptor[i]); + break; + } + } + *handler = mxf_dv_dif_handle_essence_element; /* SMPTE 383M 8 */ @@ -123,6 +137,9 @@ mxf_dv_dif_create_caps (MXFMetadataTimelineTrack * track, GstTagList ** tags, gst_caps_new_simple ("video/x-dv", "systemstream", G_TYPE_BOOLEAN, TRUE, NULL); + if (d) + mxf_metadata_generic_picture_essence_descriptor_set_caps (d, caps); + if (!*tags) *tags = gst_tag_list_new (); @@ -136,8 +153,87 @@ static const MXFEssenceElementHandler mxf_dv_dif_essence_element_handler = { mxf_dv_dif_create_caps }; +static GstFlowReturn +mxf_dv_dif_write_func (GstBuffer * buffer, GstCaps * caps, + gpointer mapping_data, GstAdapter * adapter, GstBuffer ** outbuf, + gboolean flush) +{ + *outbuf = buffer; + return GST_FLOW_OK; +} + +static const guint8 dv_dif_essence_container_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x02, 0x7f, 0x01 +}; + +static MXFMetadataFileDescriptor * +mxf_dv_dif_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataCDCIPictureEssenceDescriptor *ret; + + ret = (MXFMetadataCDCIPictureEssenceDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR); + + memcpy (&ret->parent.parent.essence_container, &dv_dif_essence_container_ul, + 16); + + if (!mxf_metadata_generic_picture_essence_descriptor_from_caps (&ret->parent, + caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + *handler = mxf_dv_dif_write_func; + + return (MXFMetadataFileDescriptor *) ret; +} + +static void +mxf_dv_dif_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf) +{ + return; +} + +static void +mxf_dv_dif_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package, + MXFMetadataTimelineTrack * track, MXFFraction * edit_rate) +{ + edit_rate->n = a->sample_rate.n; + edit_rate->d = a->sample_rate.d; +} + +static guint32 +mxf_dv_dif_get_track_number_template (MXFMetadataFileDescriptor * a, + GstCaps * caps, gpointer mapping_data) +{ + return (0x18 << 24) | (0x01 << 8); +} + +static MXFEssenceElementWriter mxf_dv_dif_essence_element_writer = { + mxf_dv_dif_get_descriptor, + mxf_dv_dif_update_descriptor, + mxf_dv_dif_get_edit_rate, + mxf_dv_dif_get_track_number_template, + NULL, + {{0,}} +}; + void mxf_dv_dif_init (void) { mxf_essence_element_handler_register (&mxf_dv_dif_essence_element_handler); + + mxf_dv_dif_essence_element_writer.pad_template = + gst_pad_template_new ("dv_dif_video_sink_%u", GST_PAD_SINK, + GST_PAD_REQUEST, + gst_caps_from_string ("video/x-dv, width = " + GST_VIDEO_SIZE_RANGE ", height = " GST_VIDEO_SIZE_RANGE + ", framerate = " GST_VIDEO_FPS_RANGE ", systemstream = true")); + memcpy (&mxf_dv_dif_essence_element_writer.data_definition, + mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_PICTURE_ESSENCE), + 16); + mxf_essence_element_writer_register (&mxf_dv_dif_essence_element_writer); } diff --git a/gst/mxf/mxfjpeg2000.c b/gst/mxf/mxfjpeg2000.c index c9d7b2aa..9d949d4e 100644 --- a/gst/mxf/mxfjpeg2000.c +++ b/gst/mxf/mxfjpeg2000.c @@ -31,9 +31,11 @@ #endif #include <gst/gst.h> +#include <gst/video/video.h> #include <string.h> #include "mxfjpeg2000.h" +#include "mxfwrite.h" GST_DEBUG_CATEGORY_EXTERN (mxf_debug); #define GST_CAT_DEFAULT mxf_debug @@ -202,8 +204,127 @@ static const MXFEssenceElementHandler mxf_jpeg2000_essence_element_handler = { mxf_jpeg2000_create_caps }; +static GstFlowReturn +mxf_jpeg2000_write_func (GstBuffer * buffer, GstCaps * caps, + gpointer mapping_data, GstAdapter * adapter, GstBuffer ** outbuf, + gboolean flush) +{ + *outbuf = buffer; + return GST_FLOW_OK; +} + +static const guint8 jpeg2000_essence_container_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x0c, 0x01, 0x00 +}; + +static const guint jpeg2000_picture_essence_coding[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x04, 0x01, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00 +}; + +static MXFMetadataFileDescriptor * +mxf_jpeg2000_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataRGBAPictureEssenceDescriptor *ret; + GstStructure *s; + guint32 fourcc; + + s = gst_caps_get_structure (caps, 0); + if (strcmp (gst_structure_get_name (s), "image/x-jpc") != 0 || + !gst_structure_get_fourcc (s, "fourcc", &fourcc)) { + GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps); + return NULL; + } + + ret = (MXFMetadataRGBAPictureEssenceDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_RGBA_PICTURE_ESSENCE_DESCRIPTOR); + + memcpy (&ret->parent.parent.essence_container, &jpeg2000_essence_container_ul, + 16); + memcpy (&ret->parent.picture_essence_coding, &jpeg2000_picture_essence_coding, + 16); + + if (fourcc == GST_MAKE_FOURCC ('s', 'R', 'G', 'B')) { + ret->n_pixel_layout = 3; + ret->pixel_layout = g_new0 (guint8, 6); + ret->pixel_layout[0] = 'R'; + ret->pixel_layout[1] = 8; + ret->pixel_layout[2] = 'G'; + ret->pixel_layout[3] = 8; + ret->pixel_layout[4] = 'B'; + ret->pixel_layout[5] = 8; + } else if (fourcc == GST_MAKE_FOURCC ('s', 'Y', 'U', 'V')) { + ret->n_pixel_layout = 3; + ret->pixel_layout = g_new0 (guint8, 6); + ret->pixel_layout[0] = 'Y'; + ret->pixel_layout[1] = 8; + ret->pixel_layout[2] = 'U'; + ret->pixel_layout[3] = 8; + ret->pixel_layout[4] = 'V'; + ret->pixel_layout[5] = 8; + } else { + g_assert_not_reached (); + } + + if (!mxf_metadata_generic_picture_essence_descriptor_from_caps (&ret->parent, + caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + *handler = mxf_jpeg2000_write_func; + + return (MXFMetadataFileDescriptor *) ret; +} + +static void +mxf_jpeg2000_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf) +{ + return; +} + +static void +mxf_jpeg2000_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package, + MXFMetadataTimelineTrack * track, MXFFraction * edit_rate) +{ + edit_rate->n = a->sample_rate.n; + edit_rate->d = a->sample_rate.d; +} + +static guint32 +mxf_jpeg2000_get_track_number_template (MXFMetadataFileDescriptor * a, + GstCaps * caps, gpointer mapping_data) +{ + return (0x15 << 24) | (0x08 << 8); +} + +static MXFEssenceElementWriter mxf_jpeg2000_essence_element_writer = { + mxf_jpeg2000_get_descriptor, + mxf_jpeg2000_update_descriptor, + mxf_jpeg2000_get_edit_rate, + mxf_jpeg2000_get_track_number_template, + NULL, + {{0,}} +}; + void mxf_jpeg2000_init (void) { mxf_essence_element_handler_register (&mxf_jpeg2000_essence_element_handler); + + mxf_jpeg2000_essence_element_writer.pad_template = + gst_pad_template_new ("jpeg2000_video_sink_%u", GST_PAD_SINK, + GST_PAD_REQUEST, + gst_caps_from_string ("image/x-jpc, fields = 1, width = " + GST_VIDEO_SIZE_RANGE ", height = " GST_VIDEO_SIZE_RANGE + ", framerate = " GST_VIDEO_FPS_RANGE + ", fourcc = (GstFourcc) { sRGB, sYUV }")); + memcpy (&mxf_jpeg2000_essence_element_writer.data_definition, + mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_PICTURE_ESSENCE), + 16); + mxf_essence_element_writer_register (&mxf_jpeg2000_essence_element_writer); } diff --git a/gst/mxf/mxfmetadata.c b/gst/mxf/mxfmetadata.c index cc53ec15..07c18b0f 100644 --- a/gst/mxf/mxfmetadata.c +++ b/gst/mxf/mxfmetadata.c @@ -27,6 +27,7 @@ #include "mxfparse.h" #include "mxfmetadata.h" #include "mxfquark.h" +#include "mxfwrite.h" GST_DEBUG_CATEGORY_EXTERN (mxf_debug); #define GST_CAT_DEFAULT mxf_debug @@ -238,6 +239,115 @@ mxf_metadata_base_to_structure (MXFMetadataBase * self) return NULL; } +GstBuffer * +mxf_metadata_base_to_buffer (MXFMetadataBase * self, MXFPrimerPack * primer) +{ + MXFMetadataBaseClass *klass; + GstBuffer *ret; + GList *tags, *l; + guint size = 0, slen; + guint8 ber[9]; + MXFLocalTag *t, *last; + guint8 *data; + + g_return_val_if_fail (MXF_IS_METADATA_BASE (self), NULL); + g_return_val_if_fail (primer != NULL, NULL); + + klass = MXF_METADATA_BASE_GET_CLASS (self); + g_return_val_if_fail (klass->write_tags, NULL); + + tags = klass->write_tags (self, primer); + g_return_val_if_fail (tags != NULL, NULL); + + /* Add unknown tags */ + if (self->other_tags) { + MXFLocalTag *tmp; +#if GLIB_CHECK_VERSION (2, 16, 0) + GHashTableIter iter; + + g_hash_table_iter_init (&iter, self->other_tags); +#else + GList *l, *values; + + values = g_hash_table_get_values (self->other_tags); +#endif + +#if GLIB_CHECK_VERSION (2, 16, 0) + while (g_hash_table_iter_next (&iter, NULL, (gpointer) & t)) { +#else + for (l = values; l; l = l->next) { + t = l->data; +#endif + tmp = g_slice_dup (MXFLocalTag, t); + if (t->g_slice) { + tmp->data = g_slice_alloc (t->size); + mxf_primer_pack_add_mapping (primer, 0x0000, &t->key); + memcpy (tmp->data, t->data, t->size); + } else { + tmp->data = g_memdup (t->data, t->size); + } + tags = g_list_prepend (tags, tmp); + } + +#if !GLIB_CHECK_VERSION (2, 16, 0) + g_list_free (values); +#endif + } + + l = g_list_last (tags); + last = l->data; + tags = g_list_delete_link (tags, l); + /* Last element contains the metadata UL */ + g_return_val_if_fail (last->size == 0, NULL); + + for (l = tags; l; l = l->next) { + t = l->data; + g_assert (G_MAXUINT - t->size >= size); + size += 4 + t->size; + } + + slen = mxf_ber_encode_size (size, ber); + size += 16 + slen; + + ret = gst_buffer_new_and_alloc (size); + + memcpy (GST_BUFFER_DATA (ret), &last->key, 16); + mxf_local_tag_free (last); + last = NULL; + memcpy (GST_BUFFER_DATA (ret) + 16, ber, slen); + + data = GST_BUFFER_DATA (ret) + 16 + slen; + size -= 16 + slen; + + for (l = tags; l; l = l->next) { + guint16 local_tag; + + g_assert (size >= 4); + t = l->data; + + local_tag = + GPOINTER_TO_UINT (g_hash_table_lookup (primer->reverse_mappings, + &t->key)); + g_assert (local_tag != 0); + + GST_WRITE_UINT16_BE (data, local_tag); + GST_WRITE_UINT16_BE (data + 2, t->size); + data += 4; + size -= 4; + g_assert (size >= t->size); + + memcpy (data, t->data, t->size); + data += t->size; + size -= t->size; + + mxf_local_tag_free (t); + } + + g_list_free (tags); + + return ret; +} + G_DEFINE_ABSTRACT_TYPE (MXFMetadata, mxf_metadata, MXF_TYPE_METADATA_BASE); static gboolean @@ -281,12 +391,65 @@ error: return FALSE; } +static GList * +mxf_metadata_write_tags (MXFMetadataBase * m, MXFPrimerPack * primer) +{ + MXFMetadata *self = MXF_METADATA (m); + GList *ret = NULL; + MXFLocalTag *t; + MXFMetadataClass *klass; + static const guint8 metadata_key[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 instance_uid_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x15, 0x02, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 generation_uid_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00 + }; + + g_return_val_if_fail (MXF_IS_METADATA (self), NULL); + klass = MXF_METADATA_GET_CLASS (self); + + /* Last element contains the metadata key */ + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, metadata_key, 16); + GST_WRITE_UINT16_BE (&t->key.u[13], klass->type); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &instance_uid_ul, 16); + t->size = 16; + t->data = g_slice_alloc (16); + t->g_slice = TRUE; + memcpy (t->data, &self->parent.instance_uid, 16); + mxf_primer_pack_add_mapping (primer, 0x3c0a, &t->key); + ret = g_list_prepend (ret, t); + + if (!mxf_ul_is_zero (&self->parent.generation_uid)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &generation_uid_ul, 16); + t->size = 16; + t->data = g_slice_alloc (16); + t->g_slice = TRUE; + memcpy (t->data, &self->parent.generation_uid, 16); + mxf_primer_pack_add_mapping (primer, 0x0102, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_class_init (MXFMetadataClass * klass) { MXFMetadataBaseClass *metadata_base_class = (MXFMetadataBaseClass *) klass; metadata_base_class->handle_tag = mxf_metadata_handle_tag; + metadata_base_class->write_tags = mxf_metadata_write_tags; } static void @@ -712,6 +875,159 @@ mxf_metadata_preface_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_preface_write_tags (MXFMetadataBase * m, MXFPrimerPack * primer) +{ + MXFMetadataPreface *self = MXF_METADATA_PREFACE (m); + GList *ret = + MXF_METADATA_BASE_CLASS (mxf_metadata_preface_parent_class)->write_tags + (m, primer); + MXFLocalTag *t; + guint i; + static const guint8 last_modified_date_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x10, 0x02, 0x04, 0x00, 0x00 + }; + static const guint8 version_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x03, 0x01, 0x02, 0x01, 0x05, 0x00, 0x00, 0x00 + }; + static const guint8 object_model_version_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x03, 0x01, 0x02, 0x01, 0x04, 0x00, 0x00, 0x00 + }; + static const guint8 primary_package_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x01, 0x01, 0x04, 0x01, 0x08, 0x00, 0x00 + }; + static const guint8 identifications_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x04, 0x00, 0x00 + }; + static const guint8 content_storage_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x01, 0x00, 0x00 + }; + static const guint8 operational_pattern_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 essence_containers_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x02, 0x02, 0x10, 0x02, 0x01, 0x00, 0x00 + }; + static const guint8 dm_schemes_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x02, 0x02, 0x10, 0x02, 0x02, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &last_modified_date_ul, 16); + t->size = 8; + t->data = g_slice_alloc (8); + t->g_slice = TRUE; + mxf_timestamp_write (&self->last_modified_date, t->data); + mxf_primer_pack_add_mapping (primer, 0x3b02, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &version_ul, 16); + t->size = 2; + t->data = g_slice_alloc (2); + t->g_slice = TRUE; + GST_WRITE_UINT16_BE (t->data, self->version); + mxf_primer_pack_add_mapping (primer, 0x3b05, &t->key); + ret = g_list_prepend (ret, t); + + if (self->object_model_version) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &object_model_version_ul, 16); + t->size = 4; + t->data = g_slice_alloc (4); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->object_model_version); + mxf_primer_pack_add_mapping (primer, 0x3b07, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_ul_is_zero (&self->primary_package_uid)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &primary_package_ul, 16); + t->size = 16; + t->data = g_slice_alloc (16); + t->g_slice = TRUE; + memcpy (t->data, &self->primary_package_uid, 16); + mxf_primer_pack_add_mapping (primer, 0x3b08, &t->key); + ret = g_list_prepend (ret, t); + } + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &identifications_ul, 16); + t->size = 8 + 16 * self->n_identifications; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + mxf_primer_pack_add_mapping (primer, 0x3b06, &t->key); + GST_WRITE_UINT32_BE (t->data, self->n_identifications); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_identifications; i++) { + if (!self->identifications[i]) + continue; + + memcpy (t->data + 8 + 16 * i, + &MXF_METADATA_BASE (self->identifications[i])->instance_uid, 16); + } + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &content_storage_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + mxf_primer_pack_add_mapping (primer, 0x3b03, &t->key); + memcpy (t->data, &MXF_METADATA_BASE (self->content_storage)->instance_uid, + 16); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &operational_pattern_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + mxf_primer_pack_add_mapping (primer, 0x3b09, &t->key); + memcpy (t->data, &self->operational_pattern, 16); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &essence_containers_ul, 16); + t->size = 8 + 16 * self->n_essence_containers; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + mxf_primer_pack_add_mapping (primer, 0x3b0a, &t->key); + GST_WRITE_UINT32_BE (t->data, self->n_essence_containers); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_essence_containers; i++) { + memcpy (t->data + 8 + 16 * i, &self->essence_containers[i], 16); + } + ret = g_list_prepend (ret, t); + + if (self->dm_schemes) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &dm_schemes_ul, 16); + t->size = 8 + 16 * self->n_dm_schemes; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + mxf_primer_pack_add_mapping (primer, 0x3b0b, &t->key); + GST_WRITE_UINT32_BE (t->data, self->n_dm_schemes); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_dm_schemes; i++) { + memcpy (t->data + 8 + 16 * i, &self->dm_schemes[i], 16); + } + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_preface_init (MXFMetadataPreface * self) { @@ -729,6 +1045,7 @@ mxf_metadata_preface_class_init (MXFMetadataPrefaceClass * klass) metadata_base_class->handle_tag = mxf_metadata_preface_handle_tag; metadata_base_class->resolve = mxf_metadata_preface_resolve; metadata_base_class->to_structure = mxf_metadata_preface_to_structure; + metadata_base_class->write_tags = mxf_metadata_preface_write_tags; metadata_base_class->name_quark = MXF_QUARK (PREFACE); metadata_class->type = 0x012f; } @@ -912,6 +1229,127 @@ mxf_metadata_identification_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_identification_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataIdentification *self = MXF_METADATA_IDENTIFICATION (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_identification_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 company_name_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x02, 0x01, 0x00, 0x00 + }; + static const guint8 product_name_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x03, 0x01, 0x00, 0x00 + }; + static const guint8 product_version_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x04, 0x00, 0x00, 0x00 + }; + static const guint8 version_string_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x05, 0x01, 0x00, 0x00 + }; + static const guint8 product_uid_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x07, 0x00, 0x00, 0x00 + }; + static const guint8 modification_date_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x10, 0x02, 0x03, 0x00, 0x00 + }; + static const guint8 toolkit_version_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x0A, 0x00, 0x00, 0x00 + }; + static const guint8 platform_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x06, 0x01, 0x00, 0x00 + }; + + if (self->company_name) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &company_name_ul, 16); + t->data = mxf_utf8_to_utf16 (self->company_name, &t->size); + mxf_primer_pack_add_mapping (primer, 0x3c01, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->product_name) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &product_name_ul, 16); + t->data = mxf_utf8_to_utf16 (self->product_name, &t->size); + mxf_primer_pack_add_mapping (primer, 0x3c02, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_product_version_is_valid (&self->product_version)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &product_version_ul, 16); + t->size = 10; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + mxf_product_version_write (&self->product_version, t->data); + mxf_primer_pack_add_mapping (primer, 0x3c03, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->version_string) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &version_string_ul, 16); + t->data = mxf_utf8_to_utf16 (self->version_string, &t->size); + mxf_primer_pack_add_mapping (primer, 0x3c04, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_ul_is_zero (&self->product_uid)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &product_uid_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->product_uid, 16); + mxf_primer_pack_add_mapping (primer, 0x3c05, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_timestamp_is_unknown (&self->modification_date)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &modification_date_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + mxf_timestamp_write (&self->modification_date, t->data); + mxf_primer_pack_add_mapping (primer, 0x3c06, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_product_version_is_valid (&self->toolkit_version)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &toolkit_version_ul, 16); + t->size = 10; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + mxf_product_version_write (&self->toolkit_version, t->data); + mxf_primer_pack_add_mapping (primer, 0x3c07, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->platform) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &platform_ul, 16); + t->data = mxf_utf8_to_utf16 (self->platform, &t->size); + mxf_primer_pack_add_mapping (primer, 0x3c08, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_identification_init (MXFMetadataIdentification * self) { @@ -929,6 +1367,7 @@ mxf_metadata_identification_class_init (MXFMetadataIdentificationClass * klass) metadata_base_class->handle_tag = mxf_metadata_identification_handle_tag; metadata_base_class->name_quark = MXF_QUARK (IDENTIFICATION); metadata_base_class->to_structure = mxf_metadata_identification_to_structure; + metadata_base_class->write_tags = mxf_metadata_identification_write_tags; metadata_class->type = 0x0130; } @@ -1151,6 +1590,69 @@ mxf_metadata_content_storage_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_content_storage_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataContentStorage *self = MXF_METADATA_CONTENT_STORAGE (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_content_storage_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + guint i; + static const guint8 packages_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x05, 0x01, 0x00, 0x00 + }; + static const guint8 essence_container_data_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x05, 0x02, 0x00, 0x00 + }; + + if (self->packages) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &packages_ul, 16); + t->size = 8 + 16 * self->n_packages; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_packages); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_packages; i++) { + if (!self->packages[i]) + continue; + + memcpy (t->data + 8 + i * 16, + &MXF_METADATA_BASE (self->packages[i])->instance_uid, 16); + } + + mxf_primer_pack_add_mapping (primer, 0x1901, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->essence_container_data) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &essence_container_data_ul, 16); + t->size = 8 + 16 * self->n_essence_container_data; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_essence_container_data); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_essence_container_data; i++) { + if (!self->essence_container_data[i]) + continue; + + memcpy (t->data + 8 + i * 16, + &MXF_METADATA_BASE (self->essence_container_data[i])->instance_uid, + 16); + } + + mxf_primer_pack_add_mapping (primer, 0x1902, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_content_storage_init (MXFMetadataContentStorage * self) { @@ -1169,6 +1671,7 @@ mxf_metadata_content_storage_class_init (MXFMetadataContentStorageClass * klass) metadata_base_class->resolve = mxf_metadata_content_storage_resolve; metadata_base_class->name_quark = MXF_QUARK (CONTENT_STORAGE); metadata_base_class->to_structure = mxf_metadata_content_storage_to_structure; + metadata_base_class->write_tags = mxf_metadata_content_storage_write_tags; metadata_class->type = 0x0118; } @@ -1298,6 +1801,63 @@ mxf_metadata_essence_container_data_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_essence_container_data_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataEssenceContainerData *self = + MXF_METADATA_ESSENCE_CONTAINER_DATA (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_essence_container_data_parent_class)->write_tags (m, + primer); + MXFLocalTag *t; + static const guint8 linked_package_uid_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x06, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 body_sid_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 index_sid_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &linked_package_uid_ul, 16); + t->size = 32; + t->data = g_slice_alloc0 (32); + t->g_slice = TRUE; + if (self->linked_package) + memcpy (t->data, &self->linked_package->parent.package_uid, 32); + mxf_primer_pack_add_mapping (primer, 0x2701, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &body_sid_ul, 16); + t->size = 4; + t->data = g_slice_alloc (4); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->body_sid); + mxf_primer_pack_add_mapping (primer, 0x3f07, &t->key); + ret = g_list_prepend (ret, t); + + if (self->index_sid) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &index_sid_ul, 16); + t->size = 4; + t->data = g_slice_alloc (4); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->index_sid); + mxf_primer_pack_add_mapping (primer, 0x3f07, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_essence_container_data_init (MXFMetadataEssenceContainerData * self) @@ -1318,6 +1878,8 @@ static void metadata_base_class->name_quark = MXF_QUARK (ESSENCE_CONTAINER_DATA); metadata_base_class->to_structure = mxf_metadata_essence_container_data_to_structure; + metadata_base_class->write_tags = + mxf_metadata_essence_container_data_write_tags; metadata_class->type = 0x0123; } @@ -1518,6 +2080,95 @@ mxf_metadata_generic_package_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_generic_package_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataGenericPackage *self = MXF_METADATA_GENERIC_PACKAGE (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_generic_package_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 package_uid_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x15, 0x10, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 name_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x03, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 package_creation_date_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x10, 0x01, 0x03, 0x00, 0x00 + }; + static const guint8 package_modified_date_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x10, 0x02, 0x05, 0x00, 0x00 + }; + static const guint8 tracks_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x05, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &package_uid_ul, 16); + t->size = 32; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->package_uid, 32); + mxf_primer_pack_add_mapping (primer, 0x4401, &t->key); + ret = g_list_prepend (ret, t); + + if (self->name) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &name_ul, 16); + t->data = mxf_utf8_to_utf16 (self->name, &t->size); + mxf_primer_pack_add_mapping (primer, 0x4402, &t->key); + ret = g_list_prepend (ret, t); + } + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &package_creation_date_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + mxf_timestamp_write (&self->package_creation_date, t->data); + mxf_primer_pack_add_mapping (primer, 0x4405, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &package_modified_date_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + mxf_timestamp_write (&self->package_modified_date, t->data); + mxf_primer_pack_add_mapping (primer, 0x4404, &t->key); + ret = g_list_prepend (ret, t); + + if (self->tracks) { + guint i; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &tracks_ul, 16); + t->size = 8 + 16 * self->n_tracks; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_tracks); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_tracks; i++) { + if (!self->tracks[i]) + continue; + + memcpy (t->data + 8 + 16 * i, + &MXF_METADATA_BASE (self->tracks[i])->instance_uid, 16); + } + mxf_primer_pack_add_mapping (primer, 0x4403, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_generic_package_init (MXFMetadataGenericPackage * self) { @@ -1534,6 +2185,7 @@ mxf_metadata_generic_package_class_init (MXFMetadataGenericPackageClass * klass) metadata_base_class->handle_tag = mxf_metadata_generic_package_handle_tag; metadata_base_class->resolve = mxf_metadata_generic_package_resolve; metadata_base_class->to_structure = mxf_metadata_generic_package_to_structure; + metadata_base_class->write_tags = mxf_metadata_generic_package_write_tags; } G_DEFINE_TYPE (MXFMetadataMaterialPackage, mxf_metadata_material_package, @@ -1802,6 +2454,34 @@ mxf_metadata_source_package_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_source_package_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataSourcePackage *self = MXF_METADATA_SOURCE_PACKAGE (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_source_package_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 descriptor_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x03, 0x00, 0x00 + }; + + if (self->descriptor) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &descriptor_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &MXF_METADATA_BASE (self->descriptor)->instance_uid, 16); + mxf_primer_pack_add_mapping (primer, 0x4701, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_source_package_init (MXFMetadataSourcePackage * self) { @@ -1818,6 +2498,7 @@ mxf_metadata_source_package_class_init (MXFMetadataSourcePackageClass * klass) metadata_base_class->resolve = mxf_metadata_source_package_resolve; metadata_base_class->name_quark = MXF_QUARK (SOURCE_PACKAGE); metadata_base_class->to_structure = mxf_metadata_source_package_to_structure; + metadata_base_class->write_tags = mxf_metadata_source_package_write_tags; metadata_class->type = 0x0137; } @@ -1959,6 +2640,69 @@ mxf_metadata_track_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_track_write_tags (MXFMetadataBase * m, MXFPrimerPack * primer) +{ + MXFMetadataTrack *self = MXF_METADATA_TRACK (m); + GList *ret = + MXF_METADATA_BASE_CLASS (mxf_metadata_track_parent_class)->write_tags (m, + primer); + MXFLocalTag *t; + static const guint8 track_id_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 track_number_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x04, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 track_name_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x07, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 sequence_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x04, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &track_id_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->track_id); + mxf_primer_pack_add_mapping (primer, 0x4801, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &track_number_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->track_number); + mxf_primer_pack_add_mapping (primer, 0x4804, &t->key); + ret = g_list_prepend (ret, t); + + if (self->track_name) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &track_name_ul, 16); + t->data = mxf_utf8_to_utf16 (self->track_name, &t->size); + mxf_primer_pack_add_mapping (primer, 0x4802, &t->key); + ret = g_list_prepend (ret, t); + } + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sequence_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &MXF_METADATA_BASE (self->sequence)->instance_uid, 16); + mxf_primer_pack_add_mapping (primer, 0x4803, &t->key); + ret = g_list_prepend (ret, t); + + return ret; +} + static void mxf_metadata_track_init (MXFMetadataTrack * self) { @@ -1975,6 +2719,7 @@ mxf_metadata_track_class_init (MXFMetadataTrackClass * klass) metadata_base_class->handle_tag = mxf_metadata_track_handle_tag; metadata_base_class->resolve = mxf_metadata_track_resolve; metadata_base_class->to_structure = mxf_metadata_track_to_structure; + metadata_base_class->write_tags = mxf_metadata_track_write_tags; } /* SMPTE RP224 */ @@ -2022,6 +2767,18 @@ mxf_metadata_track_identifier_parse (const MXFUL * track_identifier) return MXF_METADATA_TRACK_UNKNOWN; } +const MXFUL * +mxf_metadata_track_identifier_get (MXFMetadataTrackType type) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (mxf_metadata_track_identifier); i++) + if (mxf_metadata_track_identifier[i].type == type) + return (const MXFUL *) &mxf_metadata_track_identifier[i].ul; + + return NULL; +} + G_DEFINE_TYPE (MXFMetadataTimelineTrack, mxf_metadata_timeline_track, MXF_TYPE_METADATA_TRACK); @@ -2078,6 +2835,46 @@ mxf_metadata_timeline_track_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_timeline_track_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataTimelineTrack *self = MXF_METADATA_TIMELINE_TRACK (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_timeline_track_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 edit_rate_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x30, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 origin_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x03, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &edit_rate_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->edit_rate.n); + GST_WRITE_UINT32_BE (t->data + 4, self->edit_rate.d); + mxf_primer_pack_add_mapping (primer, 0x4b01, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &origin_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->origin); + mxf_primer_pack_add_mapping (primer, 0x4b02, &t->key); + ret = g_list_prepend (ret, t); + + return ret; +} + static void mxf_metadata_timeline_track_init (MXFMetadataTimelineTrack * self) { @@ -2093,6 +2890,7 @@ mxf_metadata_timeline_track_class_init (MXFMetadataTimelineTrackClass * klass) metadata_base_class->handle_tag = mxf_metadata_timeline_track_handle_tag; metadata_base_class->name_quark = MXF_QUARK (TIMELINE_TRACK); metadata_base_class->to_structure = mxf_metadata_timeline_track_to_structure; + metadata_base_class->write_tags = mxf_metadata_timeline_track_write_tags; metadata_class->type = 0x013b; } @@ -2152,6 +2950,46 @@ mxf_metadata_event_track_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_event_track_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataEventTrack *self = MXF_METADATA_EVENT_TRACK (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_event_track_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 event_edit_rate_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x30, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 event_origin_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x0B, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &event_edit_rate_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->event_edit_rate.n); + GST_WRITE_UINT32_BE (t->data + 4, self->event_edit_rate.d); + mxf_primer_pack_add_mapping (primer, 0x4901, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &event_origin_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->event_origin); + mxf_primer_pack_add_mapping (primer, 0x4902, &t->key); + ret = g_list_prepend (ret, t); + + return ret; +} + static void mxf_metadata_event_track_init (MXFMetadataEventTrack * self) { @@ -2167,6 +3005,7 @@ mxf_metadata_event_track_class_init (MXFMetadataEventTrackClass * klass) metadata_base_class->handle_tag = mxf_metadata_event_track_handle_tag; metadata_base_class->name_quark = MXF_QUARK (EVENT_TRACK); metadata_base_class->to_structure = mxf_metadata_event_track_to_structure; + metadata_base_class->write_tags = mxf_metadata_event_track_write_tags; metadata_class->type = 0x0139; } @@ -2345,6 +3184,71 @@ mxf_metadata_sequence_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_sequence_write_tags (MXFMetadataBase * m, MXFPrimerPack * primer) +{ + MXFMetadataSequence *self = MXF_METADATA_SEQUENCE (m); + GList *ret = + MXF_METADATA_BASE_CLASS (mxf_metadata_sequence_parent_class)->write_tags + (m, primer); + MXFLocalTag *t; + static const guint8 data_definition_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 duration_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x02, 0x01, 0x01, 0x03, 0x00, 0x00 + }; + static const guint8 structural_components_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x09, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &data_definition_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->data_definition, 16); + mxf_primer_pack_add_mapping (primer, 0x0201, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &duration_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->duration); + mxf_primer_pack_add_mapping (primer, 0x0202, &t->key); + ret = g_list_prepend (ret, t); + + if (self->structural_components) { + guint i; + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &structural_components_ul, 16); + t->size = 8 + 16 * self->n_structural_components; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + + GST_WRITE_UINT32_BE (t->data, self->n_structural_components); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_structural_components; i++) { + if (!self->structural_components[i]) + continue; + + memcpy (t->data + 8 + i * 16, + &MXF_METADATA_BASE (self->structural_components[i])->instance_uid, + 16); + } + + mxf_primer_pack_add_mapping (primer, 0x1001, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_sequence_init (MXFMetadataSequence * self) { @@ -2363,6 +3267,7 @@ mxf_metadata_sequence_class_init (MXFMetadataSequenceClass * klass) metadata_base_class->resolve = mxf_metadata_sequence_resolve; metadata_base_class->name_quark = MXF_QUARK (SEQUENCE); metadata_base_class->to_structure = mxf_metadata_sequence_to_structure; + metadata_base_class->write_tags = mxf_metadata_sequence_write_tags; metadata_class->type = 0x010f; } @@ -2429,6 +3334,45 @@ mxf_metadata_structural_component_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_structural_component_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataStructuralComponent *self = MXF_METADATA_STRUCTURAL_COMPONENT (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_structural_component_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 data_definition_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 duration_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x02, 0x01, 0x01, 0x03, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &data_definition_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->data_definition, 16); + mxf_primer_pack_add_mapping (primer, 0x0201, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &duration_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->duration); + mxf_primer_pack_add_mapping (primer, 0x0202, &t->key); + ret = g_list_prepend (ret, t); + + return ret; +} + static void mxf_metadata_structural_component_init (MXFMetadataStructuralComponent * self) { @@ -2445,6 +3389,8 @@ static void mxf_metadata_structural_component_handle_tag; metadata_base_class->to_structure = mxf_metadata_structural_component_to_structure; + metadata_base_class->write_tags = + mxf_metadata_structural_component_write_tags; } G_DEFINE_TYPE (MXFMetadataTimecodeComponent, mxf_metadata_timecode_component, @@ -2512,6 +3458,58 @@ mxf_metadata_timecode_component_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_timecode_component_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataTimecodeComponent *self = MXF_METADATA_TIMECODE_COMPONENT (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_timecode_component_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 rounded_timecode_base_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x04, 0x01, 0x01, 0x02, 0x06, 0x00, 0x00 + }; + static const guint8 start_timecode_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x05, 0x00, 0x00 + }; + static const guint8 drop_frame_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x04, 0x01, 0x01, 0x05, 0x00, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &rounded_timecode_base_ul, 16); + t->size = 2; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT16_BE (t->data, self->rounded_timecode_base); + mxf_primer_pack_add_mapping (primer, 0x1502, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &start_timecode_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->start_timecode); + mxf_primer_pack_add_mapping (primer, 0x1501, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &drop_frame_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, (self->drop_frame) ? 1 : 0); + mxf_primer_pack_add_mapping (primer, 0x1503, &t->key); + ret = g_list_prepend (ret, t); + + return ret; +} + static void mxf_metadata_timecode_component_init (MXFMetadataTimecodeComponent * self) { @@ -2529,6 +3527,7 @@ mxf_metadata_timecode_component_class_init (MXFMetadataTimecodeComponentClass * metadata_base_class->name_quark = MXF_QUARK (TIMECODE_COMPONENT); metadata_base_class->to_structure = mxf_metadata_timecode_component_to_structure; + metadata_base_class->write_tags = mxf_metadata_timecode_component_write_tags; metadata_class->type = 0x0114; } @@ -2643,6 +3642,57 @@ mxf_metadata_source_clip_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_source_clip_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataSourceClip *self = MXF_METADATA_SOURCE_CLIP (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_source_clip_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 start_position_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x04, 0x00, 0x00 + }; + static const guint8 source_package_id_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 source_track_id_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00 + }; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &start_position_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->start_position); + mxf_primer_pack_add_mapping (primer, 0x1201, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &source_package_id_ul, 16); + t->size = 32; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->source_package_id, 32); + mxf_primer_pack_add_mapping (primer, 0x1101, &t->key); + ret = g_list_prepend (ret, t); + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &source_track_id_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->source_track_id); + mxf_primer_pack_add_mapping (primer, 0x1102, &t->key); + ret = g_list_prepend (ret, t); + + return ret; +} static void mxf_metadata_source_clip_init (MXFMetadataSourceClip * self) @@ -2660,6 +3710,7 @@ mxf_metadata_source_clip_class_init (MXFMetadataSourceClipClass * klass) metadata_base_class->resolve = mxf_metadata_source_clip_resolve; metadata_base_class->name_quark = MXF_QUARK (SOURCE_CLIP); metadata_base_class->to_structure = mxf_metadata_source_clip_to_structure; + metadata_base_class->write_tags = mxf_metadata_source_clip_write_tags; metadata_class->type = 0x0111; } @@ -2771,6 +3822,39 @@ mxf_metadata_dm_source_clip_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_dm_source_clip_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataDMSourceClip *self = MXF_METADATA_DM_SOURCE_CLIP (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_dm_source_clip_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 track_ids_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x07, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00 + }; + + if (self->track_ids) { + guint i; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &track_ids_ul, 16); + t->size = 8 + 4 * self->n_track_ids; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_track_ids); + GST_WRITE_UINT32_BE (t->data + 4, 4); + for (i = 0; i < self->n_track_ids; i++) + GST_WRITE_UINT32_BE (t->data + 8 + i * 4, self->track_ids[i]); + mxf_primer_pack_add_mapping (primer, 0x6103, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_dm_source_clip_init (MXFMetadataDMSourceClip * self) { @@ -2788,6 +3872,7 @@ mxf_metadata_dm_source_clip_class_init (MXFMetadataDMSourceClipClass * klass) metadata_base_class->handle_tag = mxf_metadata_dm_source_clip_handle_tag; metadata_base_class->name_quark = MXF_QUARK (DM_SOURCE_CLIP); metadata_base_class->to_structure = mxf_metadata_dm_source_clip_to_structure; + metadata_base_class->write_tags = mxf_metadata_dm_source_clip_write_tags; metadata_class->type = 0x0145; } @@ -2965,10 +4050,84 @@ mxf_metadata_dm_segment_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_dm_segment_write_tags (MXFMetadataBase * m, MXFPrimerPack * primer) +{ + MXFMetadataDMSegment *self = MXF_METADATA_DM_SEGMENT (m); + GList *ret = + MXF_METADATA_BASE_CLASS (mxf_metadata_dm_segment_parent_class)->write_tags + (m, primer); + MXFLocalTag *t; + static const guint8 event_start_position_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x03, 0x03, 0x03, 0x00, 0x00 + }; + static const guint8 event_comment_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x30, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 track_ids_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x07, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 dm_framework_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x0C, 0x00, 0x00 + }; + + if (self->event_start_position != -1) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &event_start_position_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->event_start_position); + mxf_primer_pack_add_mapping (primer, 0x0601, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->event_comment) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &event_comment_ul, 16); + t->data = mxf_utf8_to_utf16 (self->event_comment, &t->size); + mxf_primer_pack_add_mapping (primer, 0x0602, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->track_ids) { + guint i; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &track_ids_ul, 16); + t->size = 8 + 4 * self->n_track_ids; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_track_ids); + GST_WRITE_UINT32_BE (t->data + 4, 4); + for (i = 0; i < self->n_track_ids; i++) + GST_WRITE_UINT32_BE (t->data + 8 + i * 4, self->track_ids[i]); + mxf_primer_pack_add_mapping (primer, 0x6102, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->dm_framework) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &dm_framework_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &MXF_METADATA_BASE (self->dm_framework)->instance_uid, 16); + mxf_primer_pack_add_mapping (primer, 0x6101, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_dm_segment_init (MXFMetadataDMSegment * self) { - + self->event_start_position = -1; } static void @@ -2983,6 +4142,7 @@ mxf_metadata_dm_segment_class_init (MXFMetadataDMSegmentClass * klass) metadata_base_class->resolve = mxf_metadata_dm_segment_resolve; metadata_base_class->name_quark = MXF_QUARK (DM_SEGMENT); metadata_base_class->to_structure = mxf_metadata_dm_segment_to_structure; + metadata_base_class->write_tags = mxf_metadata_dm_segment_write_tags; metadata_class->type = 0x0141; } @@ -3129,6 +4289,43 @@ mxf_metadata_generic_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_generic_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataGenericDescriptor *self = MXF_METADATA_GENERIC_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_generic_descriptor_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 locators_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x03, 0x00, 0x00 + }; + + if (self->locators) { + guint i; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &locators_ul, 16); + t->size = 8 + 16 * self->n_locators;; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_locators); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_locators; i++) { + if (!self->locators[i]) + continue; + memcpy (t->data + 8 + 16 * i, + &MXF_METADATA_BASE (self->locators[i])->instance_uid, 16); + } + mxf_primer_pack_add_mapping (primer, 0x2f01, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_generic_descriptor_init (MXFMetadataGenericDescriptor * self) { @@ -3147,6 +4344,7 @@ mxf_metadata_generic_descriptor_class_init (MXFMetadataGenericDescriptorClass * metadata_base_class->resolve = mxf_metadata_generic_descriptor_resolve; metadata_base_class->to_structure = mxf_metadata_generic_descriptor_to_structure; + metadata_base_class->write_tags = mxf_metadata_generic_descriptor_write_tags; } G_DEFINE_TYPE (MXFMetadataFileDescriptor, mxf_metadata_file_descriptor, @@ -3247,6 +4445,91 @@ mxf_metadata_file_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_file_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataFileDescriptor *self = MXF_METADATA_FILE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_file_descriptor_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 linked_track_id_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x06, 0x01, 0x01, 0x03, 0x05, 0x00, 0x00, 0x00 + }; + static const guint8 sample_rate_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 container_duration_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 essence_container_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x01, 0x02, 0x00, 0x00 + }; + static const guint8 codec_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x01, 0x03, 0x00, 0x00 + }; + + if (self->linked_track_id) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &linked_track_id_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->linked_track_id); + mxf_primer_pack_add_mapping (primer, 0x3006, &t->key); + ret = g_list_prepend (ret, t); + } + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sample_rate_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->sample_rate.n); + GST_WRITE_UINT32_BE (t->data + 4, self->sample_rate.d); + mxf_primer_pack_add_mapping (primer, 0x3001, &t->key); + ret = g_list_prepend (ret, t); + + if (self->container_duration > 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &container_duration_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->container_duration); + mxf_primer_pack_add_mapping (primer, 0x3002, &t->key); + ret = g_list_prepend (ret, t); + } + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &essence_container_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->essence_container, 16); + mxf_primer_pack_add_mapping (primer, 0x3004, &t->key); + ret = g_list_prepend (ret, t); + + if (!mxf_ul_is_zero (&self->codec)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &codec_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->codec, 16); + mxf_primer_pack_add_mapping (primer, 0x3005, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_file_descriptor_init (MXFMetadataFileDescriptor * self) { @@ -3262,6 +4545,7 @@ mxf_metadata_file_descriptor_class_init (MXFMetadataFileDescriptorClass * klass) metadata_base_class->handle_tag = mxf_metadata_file_descriptor_handle_tag; metadata_base_class->name_quark = MXF_QUARK (FILE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_file_descriptor_to_structure; + metadata_base_class->write_tags = mxf_metadata_file_descriptor_write_tags; metadata_class->type = 0x0125; } @@ -3567,6 +4851,383 @@ mxf_metadata_generic_picture_essence_descriptor_to_structure (MXFMetadataBase * return ret; } +static GList * +mxf_metadata_generic_picture_essence_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataGenericPictureEssenceDescriptor *self = + MXF_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_generic_picture_essence_descriptor_parent_class)->write_tags + (m, primer); + MXFLocalTag *t; + static const guint8 signal_standard_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x05, 0x01, 0x13, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 frame_layout_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00 + }; + static const guint8 stored_width_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00 + }; + static const guint8 stored_height_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x02, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 stored_f2_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x03, 0x02, 0x08, 0x00, 0x00, 0x00 + }; + static const guint8 sampled_width_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x08, 0x00, 0x00, 0x00 + }; + static const guint8 sampled_height_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x07, 0x00, 0x00, 0x00 + }; + static const guint8 sampled_x_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x09, 0x00, 0x00, 0x00 + }; + static const guint8 sampled_y_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0A, 0x00, 0x00, 0x00 + }; + static const guint8 display_height_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0B, 0x00, 0x00, 0x00 + }; + static const guint8 display_width_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0C, 0x00, 0x00, 0x00 + }; + static const guint8 display_x_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0D, 0x00, 0x00, 0x00 + }; + static const guint8 display_y_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0E, 0x00, 0x00, 0x00 + }; + static const guint8 display_f2_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x03, 0x02, 0x07, 0x00, 0x00, 0x00 + }; + static const guint8 aspect_ratio_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 active_format_descriptor_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x03, 0x02, 0x09, 0x00, 0x00, 0x00 + }; + static const guint8 video_line_map_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x03, 0x02, 0x05, 0x00, 0x00, 0x00 + }; + static const guint8 alpha_transparency_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 capture_gamma_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x00 + }; + static const guint8 image_alignment_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x18, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 image_start_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x18, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 image_end_offset_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 field_dominance_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x03, 0x01, 0x06, 0x00, 0x00, 0x00 + }; + static const guint8 picture_essence_coding_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + + if (self->signal_standard != 1) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &signal_standard_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->signal_standard); + mxf_primer_pack_add_mapping (primer, 0x3215, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->frame_layout != 255) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &frame_layout_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->frame_layout); + mxf_primer_pack_add_mapping (primer, 0x320c, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->stored_width != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &stored_width_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->stored_width); + mxf_primer_pack_add_mapping (primer, 0x3203, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->stored_height != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &stored_height_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->stored_height); + mxf_primer_pack_add_mapping (primer, 0x3202, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->stored_f2_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &stored_f2_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->stored_f2_offset); + mxf_primer_pack_add_mapping (primer, 0x3216, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->sampled_width != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sampled_width_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->sampled_width); + mxf_primer_pack_add_mapping (primer, 0x3205, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->sampled_height != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sampled_height_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->sampled_height); + mxf_primer_pack_add_mapping (primer, 0x3204, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->sampled_x_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sampled_x_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->sampled_x_offset); + mxf_primer_pack_add_mapping (primer, 0x3206, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->sampled_y_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sampled_y_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->sampled_y_offset); + mxf_primer_pack_add_mapping (primer, 0x3207, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->display_height != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &display_height_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->display_height); + mxf_primer_pack_add_mapping (primer, 0x3208, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->display_width != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &display_width_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->display_width); + mxf_primer_pack_add_mapping (primer, 0x3209, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->display_x_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &display_x_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->display_x_offset); + mxf_primer_pack_add_mapping (primer, 0x320a, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->display_y_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &display_y_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->display_y_offset); + mxf_primer_pack_add_mapping (primer, 0x320b, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->display_f2_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &display_f2_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->display_f2_offset); + mxf_primer_pack_add_mapping (primer, 0x3217, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->aspect_ratio.n != 0 && self->aspect_ratio.d != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &aspect_ratio_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->aspect_ratio.n); + GST_WRITE_UINT32_BE (t->data + 4, self->aspect_ratio.d); + mxf_primer_pack_add_mapping (primer, 0x320e, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->active_format_descriptor != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &active_format_descriptor_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->active_format_descriptor); + mxf_primer_pack_add_mapping (primer, 0x3218, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->video_line_map[0] != 0 || self->video_line_map[1] != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &video_line_map_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT64_BE (t->data, self->video_line_map[0]); + GST_WRITE_UINT64_BE (t->data + 8, self->video_line_map[1]); + mxf_primer_pack_add_mapping (primer, 0x320d, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->alpha_transparency != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &alpha_transparency_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->alpha_transparency); + mxf_primer_pack_add_mapping (primer, 0x320f, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_ul_is_zero (&self->capture_gamma)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &capture_gamma_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->capture_gamma, 16); + mxf_primer_pack_add_mapping (primer, 0x3210, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->image_alignment_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &image_alignment_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->image_alignment_offset); + mxf_primer_pack_add_mapping (primer, 0x3211, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->image_start_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &image_start_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->image_start_offset); + mxf_primer_pack_add_mapping (primer, 0x3213, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->image_end_offset != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &image_end_offset_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->image_end_offset); + mxf_primer_pack_add_mapping (primer, 0x3214, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->field_dominance != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &field_dominance_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->field_dominance); + mxf_primer_pack_add_mapping (primer, 0x3212, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_ul_is_zero (&self->picture_essence_coding)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &picture_essence_coding_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->picture_essence_coding, 16); + mxf_primer_pack_add_mapping (primer, 0x3201, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_generic_picture_essence_descriptor_init (MXFMetadataGenericPictureEssenceDescriptor * self) @@ -3588,6 +5249,8 @@ static void MXF_QUARK (GENERIC_PICTURE_ESSENCE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_generic_picture_essence_descriptor_to_structure; + metadata_base_class->write_tags = + mxf_metadata_generic_picture_essence_descriptor_write_tags; metadata_class->type = 0x0127; } @@ -3641,6 +5304,66 @@ void mxf_metadata_generic_picture_essence_descriptor_set_caps par_n, par_d, NULL); } +static gint +gst_greatest_common_divisor (gint a, gint b) +{ + while (b != 0) { + int temp = a; + + a = b; + b = temp % b; + } + + return ABS (a); +} + +gboolean + mxf_metadata_generic_picture_essence_descriptor_from_caps + (MXFMetadataGenericPictureEssenceDescriptor * self, GstCaps * caps) { + gint par_n, par_d, gcd; + gint width, height; + gint fps_n, fps_d; + MXFMetadataFileDescriptor *f = (MXFMetadataFileDescriptor *) self; + GstStructure *s; + + g_return_val_if_fail (MXF_IS_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR + (self), FALSE); + g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) { + GST_ERROR ("Invalid framerate"); + return FALSE; + } + f->sample_rate.n = fps_n; + f->sample_rate.d = fps_d; + + if (!gst_structure_get_int (s, "width", &width) || + !gst_structure_get_int (s, "height", &height)) { + GST_ERROR ("Invalid width/height"); + return FALSE; + } + + self->stored_width = width; + self->stored_height = height; + + if (!gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) { + par_n = 1; + par_d = 1; + } + + self->aspect_ratio.n = par_n * width; + self->aspect_ratio.d = par_d * height; + gcd = + gst_greatest_common_divisor (self->aspect_ratio.n, self->aspect_ratio.d); + self->aspect_ratio.n /= gcd; + self->aspect_ratio.d /= gcd; + + return TRUE; +} + + G_DEFINE_TYPE (MXFMetadataGenericSoundEssenceDescriptor, mxf_metadata_generic_sound_essence_descriptor, MXF_TYPE_METADATA_FILE_DESCRIPTOR); @@ -3770,12 +5493,147 @@ mxf_metadata_generic_sound_essence_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_generic_sound_essence_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataGenericSoundEssenceDescriptor *self = + MXF_METADATA_GENERIC_SOUND_ESSENCE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_generic_sound_essence_descriptor_parent_class)->write_tags + (m, primer); + MXFLocalTag *t; + static const guint8 audio_sampling_rate_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00 + }; + static const guint8 locked_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x02, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00 + }; + static const guint8 audio_ref_level_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x02, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00 + }; + static const guint8 electro_spatial_formulation_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 channel_count_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00 + }; + static const guint8 quantization_bits_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x02, 0x03, 0x03, 0x04, 0x00, 0x00, 0x00 + }; + static const guint8 dial_norm_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 sound_essence_compression_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x02, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 + }; + + if (self->audio_sampling_rate.d && self->audio_sampling_rate.n) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &audio_sampling_rate_ul, 16); + t->size = 8; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->audio_sampling_rate.n); + GST_WRITE_UINT32_BE (t->data + 4, self->audio_sampling_rate.d); + mxf_primer_pack_add_mapping (primer, 0x3d03, &t->key); + ret = g_list_prepend (ret, t); + } + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &locked_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, (self->locked) ? 1 : 0); + mxf_primer_pack_add_mapping (primer, 0x3d02, &t->key); + ret = g_list_prepend (ret, t); + + if (self->audio_ref_level) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &audio_ref_level_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->audio_ref_level); + mxf_primer_pack_add_mapping (primer, 0x3d04, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->electro_spatial_formulation != 255) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &electro_spatial_formulation_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->electro_spatial_formulation); + mxf_primer_pack_add_mapping (primer, 0x3d05, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->channel_count) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &channel_count_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->channel_count); + mxf_primer_pack_add_mapping (primer, 0x3d07, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->quantization_bits) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &quantization_bits_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->quantization_bits); + mxf_primer_pack_add_mapping (primer, 0x3d01, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->dial_norm != 0) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &dial_norm_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->dial_norm); + mxf_primer_pack_add_mapping (primer, 0x3d0c, &t->key); + ret = g_list_prepend (ret, t); + } + + if (!mxf_ul_is_zero (&self->sound_essence_compression)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sound_essence_compression_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->sound_essence_compression, 16); + mxf_primer_pack_add_mapping (primer, 0x3d06, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_generic_sound_essence_descriptor_init (MXFMetadataGenericSoundEssenceDescriptor * self) { self->audio_sampling_rate.n = 48000; self->audio_sampling_rate.d = 1; + self->electro_spatial_formulation = 255; } static void @@ -3791,6 +5649,8 @@ static void MXF_QUARK (GENERIC_SOUND_ESSENCE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_generic_sound_essence_descriptor_to_structure; + metadata_base_class->write_tags = + mxf_metadata_generic_sound_essence_descriptor_write_tags; metadata_class->type = 0x0142; } @@ -3817,6 +5677,38 @@ void mxf_metadata_generic_sound_essence_descriptor_set_caps } } +gboolean + mxf_metadata_generic_sound_essence_descriptor_from_caps + (MXFMetadataGenericSoundEssenceDescriptor * self, GstCaps * caps) { + gint rate; + gint channels; + GstStructure *s; + + g_return_val_if_fail (MXF_IS_METADATA_GENERIC_SOUND_ESSENCE_DESCRIPTOR (self), + FALSE); + g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "rate", &rate) || rate == 0) { + GST_WARNING ("No samplerate"); + return FALSE; + } else { + self->audio_sampling_rate.n = rate; + self->audio_sampling_rate.d = 1; + } + + if (!gst_structure_get_int (s, "channels", &channels) || channels == 0) { + GST_WARNING ("No channels"); + return FALSE; + } else { + self->channel_count = channels; + } + + return TRUE; +} + + G_DEFINE_TYPE (MXFMetadataCDCIPictureEssenceDescriptor, mxf_metadata_cdci_picture_essence_descriptor, MXF_TYPE_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR); @@ -3933,7 +5825,7 @@ mxf_metadata_cdci_picture_essence_descriptor_to_structure (MXFMetadataBase * m) gst_structure_id_set (ret, MXF_QUARK (VERTICAL_SUBSAMPLING), G_TYPE_UINT, self->vertical_subsampling, NULL); - if (self->color_siting != 0) + if (self->color_siting != 255) gst_structure_id_set (ret, MXF_QUARK (COLOR_SITING), G_TYPE_UCHAR, self->color_siting, NULL); @@ -3963,11 +5855,176 @@ mxf_metadata_cdci_picture_essence_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_cdci_picture_essence_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataCDCIPictureEssenceDescriptor *self = + MXF_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_cdci_picture_essence_descriptor_parent_class)->write_tags + (m, primer); + MXFLocalTag *t; + static const guint8 component_depth_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x0A, 0x00, 0x00, 0x00 + }; + static const guint8 horizontal_subsampling_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x05, 0x00, 0x00, 0x00 + }; + static const guint8 vertical_subsampling_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x01, 0x10, 0x00, 0x00, 0x00 + }; + static const guint8 color_siting_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x06, 0x00, 0x00, 0x00 + }; + static const guint8 reversed_byte_order_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x03, 0x01, 0x02, 0x01, 0x0A, 0x00, 0x00, 0x00 + }; + static const guint8 padding_bits_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x18, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00 + }; + static const guint8 alpha_sample_depth_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00 + }; + static const guint8 black_ref_level_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x03, 0x03, 0x00, 0x00, 0x00 + }; + static const guint8 white_ref_level_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x03, 0x04, 0x00, 0x00, 0x00 + }; + static const guint8 color_range_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00 + }; + + if (self->component_depth) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &component_depth_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->component_depth); + mxf_primer_pack_add_mapping (primer, 0x3301, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->horizontal_subsampling) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &horizontal_subsampling_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->horizontal_subsampling); + mxf_primer_pack_add_mapping (primer, 0x3302, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->vertical_subsampling) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &vertical_subsampling_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->vertical_subsampling); + mxf_primer_pack_add_mapping (primer, 0x3308, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->color_siting != 0xff) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &color_siting_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->color_siting); + mxf_primer_pack_add_mapping (primer, 0x3303, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->reversed_byte_order) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &reversed_byte_order_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, (self->reversed_byte_order) ? 1 : 0); + mxf_primer_pack_add_mapping (primer, 0x330b, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->padding_bits) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &padding_bits_ul, 16); + t->size = 2; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT16_BE (t->data, self->padding_bits); + mxf_primer_pack_add_mapping (primer, 0x3307, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->alpha_sample_depth) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &alpha_sample_depth_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->alpha_sample_depth); + mxf_primer_pack_add_mapping (primer, 0x3309, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->black_ref_level) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &black_ref_level_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->black_ref_level); + mxf_primer_pack_add_mapping (primer, 0x3304, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->white_ref_level) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &white_ref_level_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->white_ref_level); + mxf_primer_pack_add_mapping (primer, 0x3305, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->color_range) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &color_range_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->color_range); + mxf_primer_pack_add_mapping (primer, 0x3306, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_cdci_picture_essence_descriptor_init (MXFMetadataCDCIPictureEssenceDescriptor * self) { - + self->color_siting = 0xff; } static void @@ -3982,6 +6039,8 @@ static void metadata_base_class->name_quark = MXF_QUARK (CDCI_PICTURE_ESSENCE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_cdci_picture_essence_descriptor_to_structure; + metadata_base_class->write_tags = + mxf_metadata_cdci_picture_essence_descriptor_write_tags; metadata_class->type = 0x0128; } @@ -4138,6 +6197,112 @@ mxf_metadata_rgba_picture_essence_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_rgba_picture_essence_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataRGBAPictureEssenceDescriptor *self = + MXF_METADATA_RGBA_PICTURE_ESSENCE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_rgba_picture_essence_descriptor_parent_class)->write_tags + (m, primer); + MXFLocalTag *t; + static const guint8 component_max_ref_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x05, 0x03, 0x0B, 0x00, 0x00, 0x00 + }; + static const guint8 component_min_ref_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x05, 0x03, 0x0C, 0x00, 0x00, 0x00 + }; + static const guint8 alpha_max_ref_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x05, 0x03, 0x0D, 0x00, 0x00, 0x00 + }; + static const guint8 alpha_min_ref_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x05, 0x03, 0x0E, 0x00, 0x00, 0x00 + }; + static const guint8 scanning_direction_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00 + }; + static const guint8 pixel_layout_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x06, 0x00, 0x00, 0x00 + }; + + if (self->component_max_ref != 255) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &component_max_ref_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->component_max_ref); + mxf_primer_pack_add_mapping (primer, 0x3406, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->component_min_ref) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &component_min_ref_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->component_min_ref); + mxf_primer_pack_add_mapping (primer, 0x3407, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->alpha_max_ref != 255) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &alpha_max_ref_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->alpha_max_ref); + mxf_primer_pack_add_mapping (primer, 0x3408, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->alpha_min_ref) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &alpha_min_ref_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->alpha_min_ref); + mxf_primer_pack_add_mapping (primer, 0x3409, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->scanning_direction) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &scanning_direction_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->scanning_direction); + mxf_primer_pack_add_mapping (primer, 0x3405, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->pixel_layout) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &pixel_layout_ul, 16); + t->size = 2 * self->n_pixel_layout + 2; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + memcpy (t->data, self->pixel_layout, self->n_pixel_layout * 2); + mxf_primer_pack_add_mapping (primer, 0x3401, &t->key); + ret = g_list_prepend (ret, t); + + } + + return ret; +} + static void mxf_metadata_rgba_picture_essence_descriptor_init (MXFMetadataRGBAPictureEssenceDescriptor * self) @@ -4161,6 +6326,8 @@ static void metadata_base_class->name_quark = MXF_QUARK (RGBA_PICTURE_ESSENCE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_rgba_picture_essence_descriptor_to_structure; + metadata_base_class->write_tags = + mxf_metadata_rgba_picture_essence_descriptor_write_tags; metadata_class->type = 0x0129; } @@ -4227,6 +6394,36 @@ mxf_metadata_generic_data_essence_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_generic_data_essence_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataGenericDataEssenceDescriptor *self = + MXF_METADATA_GENERIC_DATA_ESSENCE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_generic_data_essence_descriptor_parent_class)->write_tags + (m, primer); + MXFLocalTag *t; + static const guint8 data_essence_coding_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03, + 0x04, 0x03, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00 + }; + + if (!mxf_ul_is_zero (&self->data_essence_coding)) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &data_essence_coding_ul, 16); + t->size = 16; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + memcpy (t->data, &self->data_essence_coding, 16); + mxf_primer_pack_add_mapping (primer, 0x3e01, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_generic_data_essence_descriptor_init (MXFMetadataGenericDataEssenceDescriptor * self) @@ -4246,6 +6443,8 @@ static void metadata_base_class->name_quark = MXF_QUARK (GENERIC_DATA_ESSENCE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_generic_data_essence_descriptor_to_structure; + metadata_base_class->write_tags = + mxf_metadata_generic_data_essence_descriptor_write_tags; metadata_class->type = 0x0143; } @@ -4391,6 +6590,44 @@ mxf_metadata_multiple_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_multiple_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataMultipleDescriptor *self = MXF_METADATA_MULTIPLE_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_multiple_descriptor_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 sub_descriptors_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x0B, 0x00, 0x00 + }; + + if (self->sub_descriptors) { + guint i; + + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &sub_descriptors_ul, 16); + t->size = 8 + 16 * self->n_sub_descriptors; + t->data = g_slice_alloc0 (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->n_sub_descriptors); + GST_WRITE_UINT32_BE (t->data + 4, 16); + for (i = 0; i < self->n_sub_descriptors; i++) { + if (!self->sub_descriptors[i]) + continue; + + memcpy (t->data + 8 + 16 * i, + &MXF_METADATA_BASE (self->sub_descriptors[i])->instance_uid, 16); + } + mxf_primer_pack_add_mapping (primer, 0x3f01, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_multiple_descriptor_init (MXFMetadataMultipleDescriptor * self) { @@ -4411,6 +6648,7 @@ mxf_metadata_multiple_descriptor_class_init (MXFMetadataMultipleDescriptorClass metadata_base_class->name_quark = MXF_QUARK (MULTIPLE_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_multiple_descriptor_to_structure; + metadata_base_class->write_tags = mxf_metadata_multiple_descriptor_write_tags; metadata_class->type = 0x0144; } @@ -4480,6 +6718,31 @@ mxf_metadata_text_locator_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_text_locator_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataTextLocator *self = MXF_METADATA_TEXT_LOCATOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_text_locator_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 locator_name_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x04, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00 + }; + + if (self->locator_name) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &locator_name_ul, 16); + t->data = mxf_utf8_to_utf16 (self->locator_name, &t->size); + mxf_primer_pack_add_mapping (primer, 0x4101, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_text_locator_init (MXFMetadataTextLocator * self) { @@ -4497,6 +6760,7 @@ mxf_metadata_text_locator_class_init (MXFMetadataTextLocatorClass * klass) metadata_base_class->handle_tag = mxf_metadata_text_locator_handle_tag; metadata_base_class->name_quark = MXF_QUARK (TEXT_LOCATOR); metadata_base_class->to_structure = mxf_metadata_text_locator_to_structure; + metadata_base_class->write_tags = mxf_metadata_text_locator_write_tags; metadata_class->type = 0x0133; } @@ -4553,6 +6817,31 @@ mxf_metadata_network_locator_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_network_locator_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataNetworkLocator *self = MXF_METADATA_NETWORK_LOCATOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_network_locator_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + static const guint8 url_string_ul[] = { + 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 + }; + + if (self->url_string) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &url_string_ul, 16); + t->data = mxf_utf8_to_utf16 (self->url_string, &t->size); + mxf_primer_pack_add_mapping (primer, 0x4001, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_network_locator_init (MXFMetadataNetworkLocator * self) { @@ -4569,6 +6858,7 @@ mxf_metadata_network_locator_class_init (MXFMetadataNetworkLocatorClass * klass) metadata_base_class->handle_tag = mxf_metadata_network_locator_handle_tag; metadata_base_class->name_quark = MXF_QUARK (NETWORK_LOCATOR); metadata_base_class->to_structure = mxf_metadata_network_locator_to_structure; + metadata_base_class->write_tags = mxf_metadata_network_locator_write_tags; metadata_class->type = 0x0133; } diff --git a/gst/mxf/mxfmetadata.h b/gst/mxf/mxfmetadata.h index 635c6e02..bbdbc62f 100644 --- a/gst/mxf/mxfmetadata.h +++ b/gst/mxf/mxfmetadata.h @@ -386,6 +386,7 @@ struct _MXFMetadataBaseClass { gboolean (*handle_tag) (MXFMetadataBase *self, MXFPrimerPack *primer, guint16 tag, const guint8 *tag_data, guint tag_size); gboolean (*resolve) (MXFMetadataBase *self, GHashTable *metadata); GstStructure * (*to_structure) (MXFMetadataBase *self); + GList * (*write_tags) (MXFMetadataBase *self, MXFPrimerPack *primer); GQuark name_quark; }; @@ -751,15 +752,20 @@ struct _MXFDescriptiveMetadataFrameworkInterface { gboolean mxf_metadata_base_parse (MXFMetadataBase *self, MXFPrimerPack *primer, const guint8 *data, guint size); gboolean mxf_metadata_base_resolve (MXFMetadataBase *self, GHashTable *metadata); GstStructure * mxf_metadata_base_to_structure (MXFMetadataBase *self); +GstBuffer * mxf_metadata_base_to_buffer (MXFMetadataBase *self, MXFPrimerPack *primer); MXFMetadata *mxf_metadata_new (guint16 type, MXFPrimerPack *primer, guint64 offset, const guint8 *data, guint size); void mxf_metadata_register (GType type); void mxf_metadata_init_types (void); MXFMetadataTrackType mxf_metadata_track_identifier_parse (const MXFUL * track_identifier); +const MXFUL * mxf_metadata_track_identifier_get (MXFMetadataTrackType type); void mxf_metadata_generic_picture_essence_descriptor_set_caps (MXFMetadataGenericPictureEssenceDescriptor * self, GstCaps * caps); +gboolean mxf_metadata_generic_picture_essence_descriptor_from_caps (MXFMetadataGenericPictureEssenceDescriptor * self, GstCaps * caps); + void mxf_metadata_generic_sound_essence_descriptor_set_caps (MXFMetadataGenericSoundEssenceDescriptor * self, GstCaps * caps); +gboolean mxf_metadata_generic_sound_essence_descriptor_from_caps (MXFMetadataGenericSoundEssenceDescriptor * self, GstCaps * caps); void mxf_descriptive_metadata_register (guint8 scheme, GType *types); MXFDescriptiveMetadata * mxf_descriptive_metadata_new (guint8 scheme, guint32 type, MXFPrimerPack * primer, guint64 offset, const guint8 * data, guint size); diff --git a/gst/mxf/mxfmpeg.c b/gst/mxf/mxfmpeg.c index 996199af..868aeb80 100644 --- a/gst/mxf/mxfmpeg.c +++ b/gst/mxf/mxfmpeg.c @@ -33,10 +33,12 @@ #endif #include <gst/gst.h> +#include <gst/video/video.h> #include <string.h> #include "mxfmpeg.h" #include "mxfquark.h" +#include "mxfwrite.h" #include <gst/base/gstbytereader.h> @@ -243,10 +245,133 @@ mxf_metadata_mpeg_video_descriptor_to_structure (MXFMetadataBase * m) return ret; } +static GList * +mxf_metadata_mpeg_video_descriptor_write_tags (MXFMetadataBase * m, + MXFPrimerPack * primer) +{ + MXFMetadataMPEGVideoDescriptor *self = MXF_METADATA_MPEG_VIDEO_DESCRIPTOR (m); + GList *ret = + MXF_METADATA_BASE_CLASS + (mxf_metadata_mpeg_video_descriptor_parent_class)->write_tags (m, primer); + MXFLocalTag *t; + + if (self->single_sequence != -1) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_single_sequence_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, (self->single_sequence) ? 1 : 0); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->const_b_frames) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_constant_b_frames_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, (self->const_b_frames) ? 1 : 0); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->coded_content_type) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_coded_content_type_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->coded_content_type); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->low_delay) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_low_delay_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, (self->low_delay) ? 1 : 0); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->closed_gop) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_closed_gop_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, (self->closed_gop) ? 1 : 0); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->identical_gop) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_identical_gop_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, (self->identical_gop) ? 1 : 0); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->max_gop) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_identical_gop_ul, 16); + t->size = 2; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT16_BE (t->data, self->max_gop); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->b_picture_count) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_b_picture_count_ul, 16); + t->size = 2; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT16_BE (t->data, self->b_picture_count); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->bitrate) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_bitrate_ul, 16); + t->size = 4; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT32_BE (t->data, self->bitrate); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + if (self->profile_and_level) { + t = g_slice_new0 (MXFLocalTag); + memcpy (&t->key, &_profile_and_level_ul, 16); + t->size = 1; + t->data = g_slice_alloc (t->size); + t->g_slice = TRUE; + GST_WRITE_UINT8 (t->data, self->profile_and_level); + mxf_primer_pack_add_mapping (primer, 0, &t->key); + ret = g_list_prepend (ret, t); + } + + return ret; +} + static void mxf_metadata_mpeg_video_descriptor_init (MXFMetadataMPEGVideoDescriptor * self) { - + self->single_sequence = -1; } static void @@ -261,6 +386,8 @@ static void metadata_base_class->name_quark = MXF_QUARK (MPEG_VIDEO_DESCRIPTOR); metadata_base_class->to_structure = mxf_metadata_mpeg_video_descriptor_to_structure; + metadata_base_class->write_tags = + mxf_metadata_mpeg_video_descriptor_write_tags; metadata_class->type = 0x0151; } @@ -380,8 +507,6 @@ mxf_mpeg_is_mpeg4_keyframe (GstBuffer * buffer) } } - g_assert_not_reached (); - return FALSE; } @@ -577,7 +702,7 @@ mxf_mpeg_es_create_caps (MXFMetadataTimelineTrack * track, GstTagList ** tags, codec_name = "MPEG-1 Audio"; } else if (mxf_ul_is_equal (&s->sound_essence_compression, &sound_essence_compression_ac3)) { - caps = gst_caps_new_simple ("audio/ac3", NULL); + caps = gst_caps_new_simple ("audio/x-ac3", NULL); codec_name = "AC3 Audio"; } else if (mxf_ul_is_equal (&s->sound_essence_compression, &sound_essence_compression_mpeg1_layer1)) { @@ -736,9 +861,431 @@ static const MXFEssenceElementHandler mxf_mpeg_essence_element_handler = { mxf_mpeg_create_caps }; +typedef struct +{ + guint spf; + guint rate; +} MPEGAudioMappingData; + +static GstFlowReturn +mxf_mpeg_audio_write_func (GstBuffer * buffer, GstCaps * caps, + gpointer mapping_data, GstAdapter * adapter, GstBuffer ** outbuf, + gboolean flush) +{ + *outbuf = buffer; + return GST_FLOW_OK; +} + +static const guint8 mpeg_essence_container_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x02, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x04, 0x01, 0x01 +}; + +static MXFMetadataFileDescriptor * +mxf_mpeg_audio_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataGenericSoundEssenceDescriptor *ret; + GstStructure *s; + MPEGAudioMappingData *md = g_new0 (MPEGAudioMappingData, 1); + gint rate; + + md->spf = -1; + *mapping_data = md; + + ret = (MXFMetadataGenericSoundEssenceDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_GENERIC_SOUND_ESSENCE_DESCRIPTOR); + + s = gst_caps_get_structure (caps, 0); + if (strcmp (gst_structure_get_name (s), "audio/mpeg") == 0) { + gint mpegversion; + + if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) { + GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps); + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + if (mpegversion == 1) { + gint layer = 0; + gint mpegaudioversion = 0; + + gst_structure_get_int (s, "layer", &layer); + gst_structure_get_int (s, "mpegaudioversion", &mpegaudioversion); + + if (mpegaudioversion == 1 && layer == 1) + memcpy (&ret->sound_essence_compression, + &sound_essence_compression_mpeg1_layer1, 16); + else if (mpegaudioversion == 1 && layer == 2) + memcpy (&ret->sound_essence_compression, + &sound_essence_compression_mpeg1_layer12, 16); + else if (mpegaudioversion == 2 && layer == 1) + memcpy (&ret->sound_essence_compression, + &sound_essence_compression_mpeg2_layer1, 16); + + if (layer == 1) + md->spf = 384; + else if (layer == 2 || mpegversion == 1) + md->spf = 1152; + else + md->spf = 576; /* MPEG-2 or 2.5 */ + + /* Otherwise all 0x00, must be some kind of mpeg1 audio */ + } else if (mpegversion == 2) { + memcpy (&ret->sound_essence_compression, &sound_essence_compression_aac, + 16); + md->spf = 1024; /* FIXME: is this correct? */ + } + } else if (strcmp (gst_structure_get_name (s), "audio/x-ac3") == 0) { + memcpy (&ret->sound_essence_compression, &sound_essence_compression_ac3, + 16); + md->spf = 256; /* FIXME: is this correct? */ + } else { + g_assert_not_reached (); + } + + if (!gst_structure_get_int (s, "rate", &rate)) { + GST_ERROR ("Invalid rate"); + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + md->rate = rate; + + memcpy (&ret->parent.essence_container, &mpeg_essence_container_ul, 16); + + if (!mxf_metadata_generic_sound_essence_descriptor_from_caps (ret, caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + *handler = mxf_mpeg_audio_write_func; + + return (MXFMetadataFileDescriptor *) ret; +} + +static void +mxf_mpeg_audio_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf) +{ + return; +} + +static void +mxf_mpeg_audio_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package, + MXFMetadataTimelineTrack * track, MXFFraction * edit_rate) +{ + MPEGAudioMappingData *md = mapping_data; + + edit_rate->n = md->rate; + edit_rate->d = md->spf; +} + +static guint32 +mxf_mpeg_audio_get_track_number_template (MXFMetadataFileDescriptor * a, + GstCaps * caps, gpointer mapping_data) +{ + return (0x16 << 24) | (0x05 << 8); +} + +static MXFEssenceElementWriter mxf_mpeg_audio_essence_element_writer = { + mxf_mpeg_audio_get_descriptor, + mxf_mpeg_audio_update_descriptor, + mxf_mpeg_audio_get_edit_rate, + mxf_mpeg_audio_get_track_number_template, + NULL, + {{0,}} +}; + +#define MPEG_AUDIO_CAPS \ + "audio/mpeg, " \ + "mpegversion = (int) 1, " \ + "layer = (int) [ 1, 3 ], " \ + "rate = (int) [ 8000, 48000 ], " \ + "channels = (int) [ 1, 2 ], " \ + "parsed = (boolean) TRUE; " \ + "audio/x-ac3, " \ + "rate = (int) [ 4000, 96000 ], " \ + "channels = (int) [ 1, 6 ]; " \ + "audio/mpeg, " \ + "mpegversion = (int) 2, " \ + "rate = (int) [ 8000, 96000 ], " \ + "channels = (int) [ 1, 8 ]" + +/* See ISO/IEC 13818-2 for MPEG ES format */ +gboolean +mxf_mpeg_is_mpeg2_frame (GstBuffer * buffer) +{ + GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer); + guint32 tmp; + + while (gst_byte_reader_get_remaining (&reader) > 3) { + if (gst_byte_reader_peek_uint24_be (&reader, &tmp) && tmp == 0x000001) { + guint8 type; + + /* Found sync code */ + gst_byte_reader_skip (&reader, 3); + + if (!gst_byte_reader_get_uint8 (&reader, &type)) + break; + + /* PICTURE */ + if (type == 0x00) { + return TRUE; + } + } else { + gst_byte_reader_skip (&reader, 1); + } + } + + return FALSE; +} + +static gboolean +mxf_mpeg_is_mpeg4_frame (GstBuffer * buffer) +{ + GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer); + guint32 tmp; + + while (gst_byte_reader_get_remaining (&reader) > 3) { + if (gst_byte_reader_peek_uint24_be (&reader, &tmp) && tmp == 0x000001) { + guint8 type; + + /* Found sync code */ + gst_byte_reader_skip (&reader, 3); + + if (!gst_byte_reader_get_uint8 (&reader, &type)) + break; + + /* PICTURE */ + if (type == 0xb6) { + return TRUE; + } + } else { + gst_byte_reader_skip (&reader, 1); + } + } + + return FALSE; +} + +static GstFlowReturn +mxf_mpeg_video_write_func (GstBuffer * buffer, GstCaps * caps, + gpointer mapping_data, GstAdapter * adapter, GstBuffer ** outbuf, + gboolean flush) +{ + MXFMPEGEssenceType type = MXF_MPEG_ESSENCE_TYPE_OTHER; + + if (mapping_data) + type = *((MXFMPEGEssenceType *) mapping_data); + + if (type == MXF_MPEG_ESSENCE_TYPE_VIDEO_MPEG2) { + if (buffer && !mxf_mpeg_is_mpeg2_frame (buffer)) { + gst_adapter_push (adapter, buffer); + *outbuf = NULL; + return GST_FLOW_OK; + } else if (buffer || gst_adapter_available (adapter)) { + guint av = gst_adapter_available (adapter); + GstBuffer *ret; + + if (buffer) + ret = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buffer) + av); + else + ret = gst_buffer_new_and_alloc (av); + + if (av) { + GstBuffer *tmp = gst_adapter_take_buffer (adapter, av); + memcpy (GST_BUFFER_DATA (ret), GST_BUFFER_DATA (tmp), av); + gst_buffer_unref (tmp); + } + + if (buffer) { + memcpy (GST_BUFFER_DATA (ret) + av, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer)); + gst_buffer_unref (buffer); + } + *outbuf = ret; + return GST_FLOW_OK; + } + } else if (type == MXF_MPEG_ESSENCE_TYPE_VIDEO_MPEG4) { + if (buffer && !mxf_mpeg_is_mpeg4_frame (buffer)) { + gst_adapter_push (adapter, buffer); + *outbuf = NULL; + return GST_FLOW_OK; + } else if (buffer || gst_adapter_available (adapter)) { + guint av = gst_adapter_available (adapter); + GstBuffer *ret; + + if (buffer) + ret = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buffer) + av); + else + ret = gst_buffer_new_and_alloc (av); + + if (av) { + GstBuffer *tmp = gst_adapter_take_buffer (adapter, av); + memcpy (GST_BUFFER_DATA (ret), GST_BUFFER_DATA (tmp), av); + gst_buffer_unref (tmp); + } + + if (buffer) { + memcpy (GST_BUFFER_DATA (ret) + av, GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer)); + gst_buffer_unref (buffer); + } + *outbuf = ret; + return GST_FLOW_OK; + } + } + + *outbuf = buffer; + return GST_FLOW_OK; +} + +static const guint8 mpeg_video_picture_essence_compression_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x00, + 0x04, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00 +}; + +static MXFMetadataFileDescriptor * +mxf_mpeg_video_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataMPEGVideoDescriptor *ret; + GstStructure *s; + + ret = (MXFMetadataMPEGVideoDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_MPEG_VIDEO_DESCRIPTOR); + + s = gst_caps_get_structure (caps, 0); + + memcpy (&ret->parent.parent.parent.essence_container, + &mpeg_essence_container_ul, 16); + memcpy (&ret->parent.parent.picture_essence_coding, + &mpeg_video_picture_essence_compression_ul, 16); + if (strcmp (gst_structure_get_name (s), "video/mpeg") == 0) { + gint mpegversion; + + if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) { + GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps); + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + if (mpegversion == 1) { + MXFMPEGEssenceType type = MXF_MPEG_ESSENCE_TYPE_VIDEO_MPEG2; + + *mapping_data = g_new0 (MXFMPEGEssenceType, 1); + memcpy (*mapping_data, &type, sizeof (MXFMPEGEssenceType)); + ret->parent.parent.picture_essence_coding.u[13] = 0x10; + } else if (mpegversion == 2) { + MXFMPEGEssenceType type = MXF_MPEG_ESSENCE_TYPE_VIDEO_MPEG2; + + *mapping_data = g_new0 (MXFMPEGEssenceType, 1); + memcpy (*mapping_data, &type, sizeof (MXFMPEGEssenceType)); + ret->parent.parent.picture_essence_coding.u[13] = 0x01; + } else { + const GValue *v; + const GstBuffer *codec_data; + MXFMPEGEssenceType type = MXF_MPEG_ESSENCE_TYPE_VIDEO_MPEG4; + + *mapping_data = g_new0 (MXFMPEGEssenceType, 1); + memcpy (*mapping_data, &type, sizeof (MXFMPEGEssenceType)); + + ret->parent.parent.picture_essence_coding.u[13] = 0x20; + if ((v = gst_structure_get_value (s, "codec_data"))) { + MXFLocalTag *t = g_slice_new0 (MXFLocalTag); + codec_data = gst_value_get_buffer (v); + t->size = GST_BUFFER_SIZE (codec_data); + t->data = g_memdup (GST_BUFFER_DATA (codec_data), t->size); + memcpy (&t->key, &sony_mpeg4_extradata, 16); + mxf_local_tag_insert (t, &MXF_METADATA_BASE (ret)->other_tags); + } + } + } else if (strcmp (gst_structure_get_name (s), "video/x-h264") == 0) { + MXFMPEGEssenceType type = MXF_MPEG_ESSENCE_TYPE_VIDEO_AVC; + + *mapping_data = g_new0 (MXFMPEGEssenceType, 1); + memcpy (*mapping_data, &type, sizeof (MXFMPEGEssenceType)); + ret->parent.parent.picture_essence_coding.u[13] = 0x30; + } else { + g_assert_not_reached (); + } + + + if (!mxf_metadata_generic_picture_essence_descriptor_from_caps (&ret-> + parent.parent, caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + *handler = mxf_mpeg_video_write_func; + + return (MXFMetadataFileDescriptor *) ret; +} + +static void +mxf_mpeg_video_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf) +{ + return; +} + +static void +mxf_mpeg_video_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package, + MXFMetadataTimelineTrack * track, MXFFraction * edit_rate) +{ + (*edit_rate).n = a->sample_rate.n; + (*edit_rate).d = a->sample_rate.d; +} + +static guint32 +mxf_mpeg_video_get_track_number_template (MXFMetadataFileDescriptor * a, + GstCaps * caps, gpointer mapping_data) +{ + return (0x15 << 24) | (0x05 << 8); +} + +static MXFEssenceElementWriter mxf_mpeg_video_essence_element_writer = { + mxf_mpeg_video_get_descriptor, + mxf_mpeg_video_update_descriptor, + mxf_mpeg_video_get_edit_rate, + mxf_mpeg_video_get_track_number_template, + NULL, + {{0,}} +}; + +#define MPEG_VIDEO_CAPS \ +"video/mpeg, " \ +"mpegversion = (int) { 1, 2, 4 }, " \ +"systemstream = (boolean) FALSE, " \ +"width = " GST_VIDEO_SIZE_RANGE ", " \ +"height = " GST_VIDEO_SIZE_RANGE ", " \ +"framerate = " GST_VIDEO_FPS_RANGE "; " \ +"video/x-h264, " \ +"width = " GST_VIDEO_SIZE_RANGE ", " \ +"height = " GST_VIDEO_SIZE_RANGE ", " \ +"framerate = " GST_VIDEO_FPS_RANGE + void mxf_mpeg_init (void) { mxf_metadata_register (MXF_TYPE_METADATA_MPEG_VIDEO_DESCRIPTOR); mxf_essence_element_handler_register (&mxf_mpeg_essence_element_handler); + + mxf_mpeg_audio_essence_element_writer.pad_template = + gst_pad_template_new ("mpeg_audio_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, + gst_caps_from_string (MPEG_AUDIO_CAPS)); + memcpy (&mxf_mpeg_audio_essence_element_writer.data_definition, + mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_SOUND_ESSENCE), 16); + mxf_essence_element_writer_register (&mxf_mpeg_audio_essence_element_writer); + + mxf_mpeg_video_essence_element_writer.pad_template = + gst_pad_template_new ("mpeg_video_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, + gst_caps_from_string (MPEG_VIDEO_CAPS)); + memcpy (&mxf_mpeg_video_essence_element_writer.data_definition, + mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_PICTURE_ESSENCE), + 16); + mxf_essence_element_writer_register (&mxf_mpeg_video_essence_element_writer); + } diff --git a/gst/mxf/mxfmux.c b/gst/mxf/mxfmux.c new file mode 100644 index 00000000..0052d2e1 --- /dev/null +++ b/gst/mxf/mxfmux.c @@ -0,0 +1,1429 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <math.h> +#include <string.h> + +#include "mxfmux.h" + +#ifdef HAVE_SYS_UTSNAME_H +#include <sys/utsname.h> +#endif + +GST_DEBUG_CATEGORY_STATIC (mxfmux_debug); +#define GST_CAT_DEFAULT mxfmux_debug + +static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/mxf") + ); + +enum +{ + PROP_0 +}; + +GST_BOILERPLATE (GstMXFMux, gst_mxf_mux, GstElement, GST_TYPE_ELEMENT); + +static void gst_mxf_mux_finalize (GObject * object); +static void gst_mxf_mux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_mxf_mux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstFlowReturn gst_mxf_mux_collected (GstCollectPads * pads, + gpointer user_data); + +static gboolean gst_mxf_mux_handle_src_event (GstPad * pad, GstEvent * event); +static GstPad *gst_mxf_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name); +static void gst_mxf_mux_release_pad (GstElement * element, GstPad * pad); + +static GstStateChangeReturn +gst_mxf_mux_change_state (GstElement * element, GstStateChange transition); + +static void gst_mxf_mux_reset (GstMXFMux * mux); + +static GstFlowReturn +gst_mxf_mux_push (GstMXFMux * mux, GstBuffer * buf) +{ + guint size = GST_BUFFER_SIZE (buf); + GstFlowReturn ret; + + gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad)); + ret = gst_pad_push (mux->srcpad, buf); + mux->offset += size; + + return ret; +} + +static void +gst_mxf_mux_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + const GstPadTemplate **p; + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_templ)); + + p = mxf_essence_element_writer_get_pad_templates (); + while (p && *p) { + gst_element_class_add_pad_template (element_class, + (GstPadTemplate *) gst_object_ref (GST_OBJECT (*p))); + p++; + } + + gst_element_class_set_details_simple (element_class, "MXF muxer", + "Codec/Muxer", + "Muxes video/audio streams into a MXF stream", + "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); +} + +static void +gst_mxf_mux_class_init (GstMXFMuxClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + GST_DEBUG_CATEGORY_INIT (mxfmux_debug, "mxfmux", 0, "MXF muxer"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_mxf_mux_finalize; + gobject_class->set_property = gst_mxf_mux_set_property; + gobject_class->get_property = gst_mxf_mux_get_property; + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_mxf_mux_change_state); + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_mxf_mux_request_new_pad); + gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_mxf_mux_release_pad); +} + +static void +gst_mxf_mux_init (GstMXFMux * mux, GstMXFMuxClass * g_class) +{ + GstCaps *caps; + + mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src"); + gst_pad_set_event_function (mux->srcpad, gst_mxf_mux_handle_src_event); + caps = gst_caps_new_simple ("application/mxf", NULL); + gst_pad_set_caps (mux->srcpad, caps); + gst_caps_unref (caps); + gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad); + + mux->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (mux->collect, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_mxf_mux_collected), mux); + + gst_mxf_mux_reset (mux); +} + +static void +gst_mxf_mux_finalize (GObject * object) +{ + GstMXFMux *mux = GST_MXF_MUX (object); + + gst_mxf_mux_reset (mux); + + if (mux->metadata) { + g_hash_table_destroy (mux->metadata); + mux->metadata = NULL; + } + + gst_object_unref (mux->collect); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_mxf_mux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + //GstMXFMux *mux = GST_MXF_MUX (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mxf_mux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + //GstMXFMux *mux = GST_MXF_MUX (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mxf_mux_reset (GstMXFMux * mux) +{ + GSList *sl; + + while ((sl = mux->collect->data) != NULL) { + GstMXFMuxPad *cpad = (GstMXFMuxPad *) sl->data; + + gst_object_unref (cpad->adapter); + g_free (cpad->mapping_data); + + gst_collect_pads_remove_pad (mux->collect, cpad->collect.pad); + } + + mux->state = GST_MXF_MUX_STATE_HEADER; + mux->n_pads = 0; + + if (mux->metadata) { + g_hash_table_destroy (mux->metadata); + mux->preface = NULL; + } + mux->metadata = mxf_metadata_hash_table_new (); + + mxf_partition_pack_reset (&mux->partition); + mxf_primer_pack_reset (&mux->primer); + memset (&mux->min_edit_rate, 0, sizeof (MXFFraction)); + mux->last_gc_timestamp = 0; + mux->last_gc_position = 0; + mux->offset = 0; +} + +static gboolean +gst_mxf_mux_handle_src_event (GstPad * pad, GstEvent * event) +{ + GstEventType type; + + type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; + + switch (type) { + case GST_EVENT_SEEK: + /* disable seeking for now */ + return FALSE; + default: + break; + } + + return gst_pad_event_default (pad, event); +} + +static gboolean +gst_mxf_mux_handle_sink_event (GstPad * pad, GstEvent * event) +{ + GstMXFMux *mux = GST_MXF_MUX (gst_pad_get_parent (pad)); + gboolean ret = TRUE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG: + /* TODO: do something with the tags */ + break; + case GST_EVENT_NEWSEGMENT: + /* We don't support NEWSEGMENT events */ + ret = FALSE; + gst_event_unref (event); + break; + default: + break; + } + + /* now GstCollectPads can take care of the rest, e.g. EOS */ + if (ret) + ret = mux->collect_event (pad, event); + gst_object_unref (mux); + + return ret; +} + +static gboolean +gst_mxf_mux_setcaps (GstPad * pad, GstCaps * caps) +{ + GstMXFMux *mux = GST_MXF_MUX (gst_pad_get_parent (pad)); + GstMXFMuxPad *cpad = (GstMXFMuxPad *) gst_pad_get_element_private (pad); + gboolean ret = TRUE; + MXFUL d_instance_uid = { {0,} }; + MXFMetadataFileDescriptor *old_descriptor = cpad->descriptor; + + GST_DEBUG_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps); + + if (old_descriptor) { + memcpy (&d_instance_uid, &MXF_METADATA_BASE (old_descriptor)->instance_uid, + 16); + cpad->descriptor = NULL; + g_free (cpad->mapping_data); + cpad->mapping_data = NULL; + } + + cpad->descriptor = + cpad->writer->get_descriptor (gst_pad_get_pad_template (pad), caps, + &cpad->write_func, &cpad->mapping_data); + + if (!cpad->descriptor) { + GST_ERROR_OBJECT (mux, + "Couldn't get descriptor for pad '%s' with caps %" GST_PTR_FORMAT, + GST_PAD_NAME (pad), caps); + gst_object_unref (mux); + return FALSE; + } + + if (mxf_ul_is_zero (&d_instance_uid)) + mxf_ul_set (&d_instance_uid, mux->metadata); + + memcpy (&MXF_METADATA_BASE (cpad->descriptor)->instance_uid, &d_instance_uid, + 16); + + g_hash_table_replace (mux->metadata, + &MXF_METADATA_BASE (cpad->descriptor)->instance_uid, cpad->descriptor); + + if (old_descriptor) { + if (mux->preface && mux->preface->content_storage && + mux->preface->content_storage->packages) { + guint i, j; + + for (i = 0; i < mux->preface->content_storage->n_packages; i++) { + MXFMetadataSourcePackage *package; + + if (!MXF_IS_METADATA_SOURCE_PACKAGE (mux->preface->content_storage-> + packages[i])) + continue; + + package = + MXF_METADATA_SOURCE_PACKAGE (mux->preface->content_storage-> + packages[i]); + + if (!package->descriptor) + continue; + + if (MXF_IS_METADATA_MULTIPLE_DESCRIPTOR (package->descriptor)) { + MXFMetadataMultipleDescriptor *tmp = + MXF_METADATA_MULTIPLE_DESCRIPTOR (package->descriptor); + + for (j = 0; j < tmp->n_sub_descriptors; j++) { + if (tmp->sub_descriptors[j] == + MXF_METADATA_GENERIC_DESCRIPTOR (old_descriptor)) { + tmp->sub_descriptors[j] = + MXF_METADATA_GENERIC_DESCRIPTOR (cpad->descriptor); + memcpy (&tmp->sub_descriptors_uids[j], &d_instance_uid, 16); + } + } + } else if (package->descriptor == + MXF_METADATA_GENERIC_DESCRIPTOR (old_descriptor)) { + package->descriptor = + MXF_METADATA_GENERIC_DESCRIPTOR (cpad->descriptor); + memcpy (&package->descriptor_uid, &d_instance_uid, 16); + } + } + } + } + + gst_object_unref (mux); + + return ret; +} + +static GstPad * +gst_mxf_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * pad_name) +{ + GstMXFMux *mux = GST_MXF_MUX (element); + GstMXFMuxPad *cpad; + GstPad *pad = NULL; + guint pad_number; + gchar *name = NULL; + const MXFEssenceElementWriter *writer; + + if (mux->state != GST_MXF_MUX_STATE_HEADER) { + GST_WARNING_OBJECT (mux, "Can't request pads after writing header"); + return NULL; + } + + writer = mxf_essence_element_writer_find (templ); + if (!writer) { + GST_ERROR_OBJECT (mux, "Not our template"); + return NULL; + } + + pad_number = g_atomic_int_exchange_and_add ((gint *) & mux->n_pads, 1); + name = g_strdup_printf (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ), pad_number); + + GST_DEBUG_OBJECT (mux, "Creating pad '%s'", name); + pad = gst_pad_new_from_template (templ, name); + g_free (name); + cpad = (GstMXFMuxPad *) + gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstMXFMuxPad)); + cpad->last_timestamp = 0; + cpad->adapter = gst_adapter_new (); + cpad->writer = writer; + + /* FIXME: hacked way to override/extend the event function of + * GstCollectPads; because it sets its own event function giving the + * element no access to events. + */ + mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (pad); + gst_pad_set_event_function (pad, + GST_DEBUG_FUNCPTR (gst_mxf_mux_handle_sink_event)); + + gst_pad_set_setcaps_function (pad, gst_mxf_mux_setcaps); + gst_pad_use_fixed_caps (pad); + gst_pad_set_active (pad, TRUE); + gst_element_add_pad (element, pad); + + return pad; +} + +static void +gst_mxf_mux_release_pad (GstElement * element, GstPad * pad) +{ + /*GstMXFMux *mux = GST_MXF_MUX (GST_PAD_PARENT (pad)); + GstMXFMuxPad *cpad = (GstMXFMuxPad *) gst_pad_get_element_private (pad); + + gst_object_unref (cpad->adapter); + g_free (cpad->mapping_data); + + gst_collect_pads_remove_pad (mux->collect, pad); + gst_element_remove_pad (element, pad); */ +} + +static GstFlowReturn +gst_mxf_mux_create_metadata (GstMXFMux * mux) +{ + GstFlowReturn ret = GST_FLOW_OK; + GSList *l; + GArray *tmp; + + GST_DEBUG_OBJECT (mux, "Creating MXF metadata"); + + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + + if (!cpad || !cpad->descriptor || !GST_PAD_CAPS (cpad->collect.pad)) + return GST_FLOW_ERROR; + + if (cpad->writer->update_descriptor) + cpad->writer->update_descriptor (cpad->descriptor, + GST_PAD_CAPS (cpad->collect.pad), cpad->mapping_data, + cpad->collect.buffer); + } + + /* Preface */ + mux->preface = + (MXFMetadataPreface *) gst_mini_object_new (MXF_TYPE_METADATA_PREFACE); + mxf_ul_set (&MXF_METADATA_BASE (mux->preface)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (mux->preface)->instance_uid, mux->preface); + + mxf_timestamp_set_now (&mux->preface->last_modified_date); + mux->preface->version = 258; + mux->preface->object_model_version = 1; + + mxf_op_set_generalized (&mux->preface->operational_pattern, MXF_OP_1a, TRUE, + TRUE, FALSE); + + tmp = g_array_new (FALSE, FALSE, sizeof (MXFUL)); + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + guint i; + gboolean found = FALSE; + + if (!cpad || !cpad->descriptor || + mxf_ul_is_zero (&cpad->descriptor->essence_container)) + return GST_FLOW_ERROR; + + for (i = 0; i < tmp->len; i++) { + if (mxf_ul_is_equal (&cpad->descriptor->essence_container, + &g_array_index (tmp, MXFUL, i))) { + found = TRUE; + break; + } + } + + if (found) + continue; + + g_array_append_val (tmp, cpad->descriptor->essence_container); + } + mux->preface->n_essence_containers = tmp->len; + mux->preface->essence_containers = (MXFUL *) g_array_free (tmp, FALSE); + + /* This will later be used as UID for the material package */ + mxf_ul_set (&mux->preface->primary_package_uid, mux->metadata); + + /* Identifications */ + { + MXFMetadataIdentification *identification; + static const guint8 gst_uid[] = { + 0xe5, 0xde, 0xcd, 0x04, 0x24, 0x90, 0x69, 0x18, + 0x8a, 0xc9, 0xb5, 0xd7, 0x02, 0x58, 0x46, 0x78 + }; + guint major, minor, micro, nano; + + mux->preface->n_identifications = 1; + mux->preface->identifications = g_new0 (MXFMetadataIdentification *, 1); + identification = mux->preface->identifications[0] = + (MXFMetadataIdentification *) + gst_mini_object_new (MXF_TYPE_METADATA_IDENTIFICATION); + + mxf_ul_set (&MXF_METADATA_BASE (identification)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (identification)->instance_uid, identification); + + mxf_ul_set (&identification->this_generation_uid, NULL); + + identification->company_name = g_strdup ("GStreamer"); + identification->product_name = g_strdup ("GStreamer Multimedia Framework"); + + gst_version (&major, &minor, µ, &nano); + identification->product_version.major = major; + identification->product_version.minor = minor; + identification->product_version.patch = micro; + identification->product_version.build = nano; + identification->product_version.release = + (nano == 0) ? 1 : (nano == 1) ? 2 : 4; + + identification->version_string = + g_strdup_printf ("%u.%u.%u.%u", major, minor, micro, nano); + memcpy (&identification->product_uid, &gst_uid, 16); + + memcpy (&identification->modification_date, + &mux->preface->last_modified_date, sizeof (MXFTimestamp)); + memcpy (&identification->toolkit_version, &identification->product_version, + sizeof (MXFProductVersion)); + +#ifdef HAVE_SYS_UTSNAME_H + { + struct utsname sys_details; + + if (uname (&sys_details) == 0) { + identification->platform = g_strdup_printf ("%s %s %s", + sys_details.sysname, sys_details.release, sys_details.machine); + } + } +#endif + +#if defined(G_OS_WIN32) + if (identification->platform == NULL) + identification->platform = g_strdup ("Microsoft Windows"); +#elif defined(G_OS_BEOS) + if (identification->platform == NULL) + identification->platform = g_strdup ("BEOS"); +#elif defined(G_OS_UNIX) + if (identification->platform == NULL) + identification->platform = g_strdup ("Unix"); +#endif + } + + /* Content storage */ + { + MXFMetadataContentStorage *cstorage; + guint i; + + cstorage = mux->preface->content_storage = (MXFMetadataContentStorage *) + gst_mini_object_new (MXF_TYPE_METADATA_CONTENT_STORAGE); + mxf_ul_set (&MXF_METADATA_BASE (cstorage)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (cstorage)->instance_uid, cstorage); + + cstorage->n_packages = 2; + cstorage->packages = g_new0 (MXFMetadataGenericPackage *, 2); + + /* Source package */ + { + MXFMetadataSourcePackage *p; + + cstorage->packages[1] = (MXFMetadataGenericPackage *) + gst_mini_object_new (MXF_TYPE_METADATA_SOURCE_PACKAGE); + mxf_ul_set (&MXF_METADATA_BASE (cstorage->packages[1])->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (cstorage->packages[1])->instance_uid, + cstorage->packages[1]); + p = (MXFMetadataSourcePackage *) cstorage->packages[1]; + + mxf_umid_set (&p->parent.package_uid); + p->parent.name = g_strdup ("Source package"); + memcpy (&p->parent.package_creation_date, + &mux->preface->last_modified_date, sizeof (MXFTimestamp)); + memcpy (&p->parent.package_modified_date, + &mux->preface->last_modified_date, sizeof (MXFTimestamp)); + + p->parent.n_tracks = g_slist_length (mux->collect->data); + p->parent.tracks = g_new0 (MXFMetadataTrack *, p->parent.n_tracks); + + if (p->parent.n_tracks > 1) { + MXFMetadataMultipleDescriptor *d; + + p->descriptor = (MXFMetadataGenericDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_MULTIPLE_DESCRIPTOR); + d = (MXFMetadataMultipleDescriptor *) p->descriptor; + d->n_sub_descriptors = p->parent.n_tracks; + d->sub_descriptors = + g_new0 (MXFMetadataGenericDescriptor *, p->parent.n_tracks); + + mxf_ul_set (&MXF_METADATA_BASE (d)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (d)->instance_uid, d); + } + + /* Tracks */ + { + guint n = 0; + + /* Essence tracks */ + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + MXFMetadataTimelineTrack *track; + MXFMetadataSequence *sequence; + MXFMetadataSourceClip *clip; + + p->parent.tracks[n] = (MXFMetadataTrack *) + gst_mini_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK); + track = (MXFMetadataTimelineTrack *) p->parent.tracks[n]; + mxf_ul_set (&MXF_METADATA_BASE (track)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (track)->instance_uid, track); + + track->parent.track_id = n + 1; + track->parent.track_number = + cpad->writer->get_track_number_template (cpad->descriptor, + GST_PAD_CAPS (cpad->collect.pad), cpad->mapping_data); + + cpad->writer->get_edit_rate (cpad->descriptor, + GST_PAD_CAPS (cpad->collect.pad), cpad->mapping_data, + cpad->collect.buffer, p, track, &track->edit_rate); + + sequence = track->parent.sequence = (MXFMetadataSequence *) + gst_mini_object_new (MXF_TYPE_METADATA_SEQUENCE); + mxf_ul_set (&MXF_METADATA_BASE (sequence)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + + memcpy (&sequence->data_definition, &cpad->writer->data_definition, + 16); + + sequence->n_structural_components = 1; + sequence->structural_components = + g_new0 (MXFMetadataStructuralComponent *, 1); + + clip = (MXFMetadataSourceClip *) + gst_mini_object_new (MXF_TYPE_METADATA_SOURCE_CLIP); + sequence->structural_components[0] = + (MXFMetadataStructuralComponent *) clip; + mxf_ul_set (&MXF_METADATA_BASE (clip)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (clip)->instance_uid, clip); + + memcpy (&clip->parent.data_definition, &sequence->data_definition, + 16); + clip->start_position = 0; + + cpad->source_package = p; + cpad->source_track = track; + cpad->descriptor->linked_track_id = n + 1; + if (p->parent.n_tracks == 1) { + p->descriptor = (MXFMetadataGenericDescriptor *) cpad->descriptor; + } else { + MXF_METADATA_MULTIPLE_DESCRIPTOR (p->descriptor)-> + sub_descriptors[n] = + (MXFMetadataGenericDescriptor *) cpad->descriptor; + } + + n++; + } + } + } + + /* Material package */ + { + MXFMetadataMaterialPackage *p; + MXFFraction min_edit_rate = { 0, 0 }; + gdouble min_edit_rate_d = G_MAXDOUBLE; + + cstorage->packages[0] = (MXFMetadataGenericPackage *) + gst_mini_object_new (MXF_TYPE_METADATA_MATERIAL_PACKAGE); + memcpy (&MXF_METADATA_BASE (cstorage->packages[0])->instance_uid, + &mux->preface->primary_package_uid, 16); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (cstorage->packages[0])->instance_uid, + cstorage->packages[0]); + p = (MXFMetadataMaterialPackage *) cstorage->packages[0]; + + mxf_umid_set (&p->package_uid); + p->name = g_strdup ("Material package"); + memcpy (&p->package_creation_date, &mux->preface->last_modified_date, + sizeof (MXFTimestamp)); + memcpy (&p->package_modified_date, &mux->preface->last_modified_date, + sizeof (MXFTimestamp)); + + p->n_tracks = g_slist_length (mux->collect->data) + 1; + p->tracks = g_new0 (MXFMetadataTrack *, p->n_tracks); + + /* Tracks */ + { + guint n; + + n = 1; + /* Essence tracks */ + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + MXFMetadataSourcePackage *source_package; + MXFMetadataTimelineTrack *track, *source_track; + MXFMetadataSequence *sequence; + MXFMetadataSourceClip *clip; + + source_package = MXF_METADATA_SOURCE_PACKAGE (cstorage->packages[1]); + source_track = + MXF_METADATA_TIMELINE_TRACK (source_package->parent.tracks[n - + 1]); + + p->tracks[n] = (MXFMetadataTrack *) + gst_mini_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK); + track = (MXFMetadataTimelineTrack *) p->tracks[n]; + mxf_ul_set (&MXF_METADATA_BASE (track)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (track)->instance_uid, track); + + track->parent.track_id = n + 1; + track->parent.track_number = 0; + + cpad->writer->get_edit_rate (cpad->descriptor, + GST_PAD_CAPS (cpad->collect.pad), cpad->mapping_data, + cpad->collect.buffer, source_package, source_track, + &track->edit_rate); + + if (track->edit_rate.n != source_track->edit_rate.n || + track->edit_rate.d != source_track->edit_rate.d) { + memcpy (&source_track->edit_rate, &track->edit_rate, + sizeof (MXFFraction)); + } + + if (track->edit_rate.d <= 0 || track->edit_rate.n <= 0) { + GST_ERROR_OBJECT (mux, "Invalid edit rate"); + return GST_FLOW_ERROR; + } + + if (min_edit_rate_d > + ((gdouble) track->edit_rate.n) / ((gdouble) track->edit_rate.d)) { + min_edit_rate_d = + ((gdouble) track->edit_rate.n) / ((gdouble) track->edit_rate.d); + memcpy (&min_edit_rate, &track->edit_rate, sizeof (MXFFraction)); + } + + sequence = track->parent.sequence = (MXFMetadataSequence *) + gst_mini_object_new (MXF_TYPE_METADATA_SEQUENCE); + mxf_ul_set (&MXF_METADATA_BASE (sequence)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + + memcpy (&sequence->data_definition, &cpad->writer->data_definition, + 16); + sequence->n_structural_components = 1; + sequence->structural_components = + g_new0 (MXFMetadataStructuralComponent *, 1); + + clip = (MXFMetadataSourceClip *) + gst_mini_object_new (MXF_TYPE_METADATA_SOURCE_CLIP); + sequence->structural_components[0] = + (MXFMetadataStructuralComponent *) clip; + mxf_ul_set (&MXF_METADATA_BASE (clip)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (clip)->instance_uid, clip); + + memcpy (&clip->parent.data_definition, &sequence->data_definition, + 16); + clip->start_position = 0; + + memcpy (&clip->source_package_id, &cstorage->packages[1]->package_uid, + 32); + clip->source_track_id = n; + + n++; + } + + n = 0; + /* Timecode track */ + { + MXFMetadataTimelineTrack *track; + MXFMetadataSequence *sequence; + MXFMetadataTimecodeComponent *component; + + p->tracks[n] = (MXFMetadataTrack *) + gst_mini_object_new (MXF_TYPE_METADATA_TIMELINE_TRACK); + track = (MXFMetadataTimelineTrack *) p->tracks[n]; + mxf_ul_set (&MXF_METADATA_BASE (track)->instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (track)->instance_uid, track); + + track->parent.track_id = n + 1; + track->parent.track_number = 0; + track->parent.track_name = g_strdup ("Timecode track"); + /* FIXME: Is this correct? */ + memcpy (&track->edit_rate, &min_edit_rate, sizeof (MXFFraction)); + + sequence = track->parent.sequence = (MXFMetadataSequence *) + gst_mini_object_new (MXF_TYPE_METADATA_SEQUENCE); + mxf_ul_set (&MXF_METADATA_BASE (sequence)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (sequence)->instance_uid, sequence); + + memcpy (&sequence->data_definition, + mxf_metadata_track_identifier_get + (MXF_METADATA_TRACK_TIMECODE_12M_INACTIVE), 16); + + sequence->n_structural_components = 1; + sequence->structural_components = + g_new0 (MXFMetadataStructuralComponent *, 1); + + component = (MXFMetadataTimecodeComponent *) + gst_mini_object_new (MXF_TYPE_METADATA_TIMECODE_COMPONENT); + sequence->structural_components[0] = + (MXFMetadataStructuralComponent *) component; + mxf_ul_set (&MXF_METADATA_BASE (component)->instance_uid, + mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (component)->instance_uid, component); + + memcpy (&component->parent.data_definition, + &sequence->data_definition, 16); + + component->start_timecode = 0; + component->rounded_timecode_base = + (((gdouble) track->edit_rate.n) / ((gdouble) track->edit_rate.d) + + 0.5); + /* TODO: drop frame */ + } + + memcpy (&mux->min_edit_rate, &min_edit_rate, sizeof (MXFFraction)); + } + } + + for (i = 0; i < cstorage->packages[1]->n_tracks; i++) { + MXFMetadataTrack *track = cstorage->packages[1]->tracks[i]; + guint j; + guint32 templ; + guint8 n_type, n; + + if ((track->track_number & 0x00ff00ff) != 0) + continue; + + templ = track->track_number; + n_type = 0; + + for (j = 0; j < cstorage->packages[1]->n_tracks; j++) { + MXFMetadataTrack *tmp = cstorage->packages[1]->tracks[j]; + + if (tmp->track_number == templ) { + n_type++; + } + } + + n = 0; + for (j = 0; j < cstorage->packages[1]->n_tracks; j++) { + MXFMetadataTrack *tmp = cstorage->packages[1]->tracks[j]; + + if (tmp->track_number == templ) { + n++; + tmp->track_number |= (n_type << 16) | (n); + } + } + } + + cstorage->n_essence_container_data = 1; + cstorage->essence_container_data = + g_new0 (MXFMetadataEssenceContainerData *, 1); + cstorage->essence_container_data[0] = (MXFMetadataEssenceContainerData *) + gst_mini_object_new (MXF_TYPE_METADATA_ESSENCE_CONTAINER_DATA); + mxf_ul_set (&MXF_METADATA_BASE (cstorage->essence_container_data[0])-> + instance_uid, mux->metadata); + g_hash_table_insert (mux->metadata, + &MXF_METADATA_BASE (cstorage->essence_container_data[0])->instance_uid, + cstorage->essence_container_data[0]); + + cstorage->essence_container_data[0]->linked_package = + MXF_METADATA_SOURCE_PACKAGE (cstorage->packages[1]); + cstorage->essence_container_data[0]->index_sid = 0; + cstorage->essence_container_data[0]->body_sid = 1; + } + + return ret; +} + +static GstFlowReturn +gst_mxf_mux_create_header_partition_pack (GstMXFMux * mux) +{ + GSList *l; + guint i = 0; + + mxf_partition_pack_reset (&mux->partition); + mux->partition.type = MXF_PARTITION_PACK_HEADER; + mux->partition.closed = mux->partition.complete = FALSE; + mux->partition.major_version = 0x0001; + mux->partition.minor_version = 0x0002; + mux->partition.kag_size = 0; + mux->partition.this_partition = 0; + mux->partition.prev_partition = 0; + mux->partition.footer_partition = 0; + mux->partition.header_byte_count = 0; + mux->partition.index_byte_count = 0; + mux->partition.index_sid = 0; + mux->partition.body_offset = 0; + mux->partition.body_sid = 0; + + memcpy (&mux->partition.operational_pattern, + &mux->preface->operational_pattern, 16); + + mux->partition.n_essence_containers = g_slist_length (mux->collect->data); + mux->partition.essence_containers = + g_new0 (MXFUL, mux->partition.n_essence_containers); + + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + guint j; + gboolean found = FALSE; + + for (j = 0; j <= i; j++) { + if (mxf_ul_is_equal (&cpad->descriptor->essence_container, + &mux->partition.essence_containers[j])) { + found = TRUE; + break; + } + } + + if (found) + continue; + + memcpy (&mux->partition.essence_containers[i], + &cpad->descriptor->essence_container, 16); + i++; + } + mux->partition.n_essence_containers = i; + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_mxf_mux_write_header_metadata (GstMXFMux * mux) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *buf; + GList *buffers = NULL; +#if GLIB_CHECK_VERSION (2, 16, 0) + GHashTableIter iter; +#else + GList *values; +#endif + MXFMetadataBase *m; + GList *l; + guint64 header_byte_count = 0; + + buf = + mxf_metadata_base_to_buffer (MXF_METADATA_BASE (mux->preface), + &mux->primer); + header_byte_count += GST_BUFFER_SIZE (buf); + buffers = g_list_prepend (buffers, buf); + +#if GLIB_CHECK_VERSION (2, 16, 0) + g_hash_table_iter_init (&iter, mux->metadata); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) & m)) { +#else + values = g_hash_table_get_values (mux->metadata); + for (l = values; l; l = l->next) { + m = l->data; +#endif + buf = mxf_metadata_base_to_buffer (m, &mux->primer); + header_byte_count += GST_BUFFER_SIZE (buf); + buffers = g_list_prepend (buffers, buf); + } + +#if !GLIB_CHECK_VERSION (2, 16, 0) + g_list_free (value); +#endif + + buffers = g_list_reverse (buffers); + buf = mxf_primer_pack_to_buffer (&mux->primer); + header_byte_count += GST_BUFFER_SIZE (buf); + buffers = g_list_prepend (buffers, buf); + + mux->partition.header_byte_count = header_byte_count; + buf = mxf_partition_pack_to_buffer (&mux->partition); + if ((ret = gst_mxf_mux_push (mux, buf)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Failed pushing partition: %s", + gst_flow_get_name (ret)); + g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (buffers); + return ret; + } + + for (l = buffers; l; l = l->next) { + buf = l->data; + l->data = NULL; + if ((ret = gst_mxf_mux_push (mux, buf)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Failed pushing buffer: %s", + gst_flow_get_name (ret)); + g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (buffers); + return ret; + } + } + + g_list_free (buffers); + + return ret; +} + +static const guint8 _gc_essence_element_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x00, + 0x0d, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00 +}; + +static GstFlowReturn +gst_mxf_mux_handle_buffer (GstMXFMux * mux, GstMXFMuxPad * cpad) +{ + GstBuffer *buf = NULL; + GstBuffer *outbuf = NULL; + GstBuffer *packet; + GstFlowReturn ret = GST_FLOW_OK; + guint8 slen, ber[9]; + gboolean flush = + (cpad->collect.abidata.ABI.eos && !cpad->have_complete_edit_unit + && cpad->collect.buffer == NULL); + + if (cpad->have_complete_edit_unit) { + GST_DEBUG_OBJECT (cpad->collect.pad, + "Handling remaining buffer for track %u at position %" G_GINT64_FORMAT, + cpad->source_track->parent.track_id, cpad->pos); + buf = NULL; + } else if (!flush) { + buf = gst_collect_pads_pop (mux->collect, &cpad->collect); + } + + if (buf) { + GST_DEBUG_OBJECT (cpad->collect.pad, + "Handling buffer of size %u for track %u at position %" G_GINT64_FORMAT, + GST_BUFFER_SIZE (buf), cpad->source_track->parent.track_id, cpad->pos); + } else { + flush = TRUE; + GST_DEBUG_OBJECT (cpad->collect.pad, + "Flushing for track %u at position %" G_GINT64_FORMAT, + cpad->source_track->parent.track_id, cpad->pos); + } + + ret = cpad->write_func (buf, GST_PAD_CAPS (cpad->collect.pad), + cpad->mapping_data, cpad->adapter, &outbuf, flush); + if (ret != GST_FLOW_OK && ret != GST_FLOW_CUSTOM_SUCCESS) { + GST_ERROR_OBJECT (cpad->collect.pad, + "Failed handling buffer for track %u, reason %s", + cpad->source_track->parent.track_id, gst_flow_get_name (ret)); + return ret; + } + + if (ret == GST_FLOW_CUSTOM_SUCCESS) { + cpad->have_complete_edit_unit = TRUE; + ret = GST_FLOW_OK; + } else { + cpad->have_complete_edit_unit = FALSE; + } + + buf = outbuf; + if (buf == NULL) + return ret; + + slen = mxf_ber_encode_size (GST_BUFFER_SIZE (buf), ber); + packet = gst_buffer_new_and_alloc (16 + slen + GST_BUFFER_SIZE (buf)); + memcpy (GST_BUFFER_DATA (packet), _gc_essence_element_ul, 16); + GST_BUFFER_DATA (packet)[7] = cpad->descriptor->essence_container.u[7]; + GST_WRITE_UINT32_BE (&GST_BUFFER_DATA (packet)[12], + cpad->source_track->parent.track_number); + memcpy (&GST_BUFFER_DATA (packet)[16], ber, slen); + memcpy (&GST_BUFFER_DATA (packet)[16 + slen], GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + gst_buffer_unref (buf); + + GST_DEBUG_OBJECT (cpad->collect.pad, "Pushing buffer of size %u for track %u", + GST_BUFFER_SIZE (packet), cpad->source_track->parent.track_id); + + if ((ret = gst_mxf_mux_push (mux, packet)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (cpad->collect.pad, + "Failed pushing buffer for track %u, reason %s", + cpad->source_track->parent.track_id, gst_flow_get_name (ret)); + return ret; + } + + cpad->pos++; + cpad->last_timestamp = + gst_util_uint64_scale (GST_SECOND * cpad->pos, + cpad->source_track->edit_rate.d, cpad->source_track->edit_rate.n); + + return ret; +} + +static GstFlowReturn +gst_mxf_mux_write_body_partition (GstMXFMux * mux) +{ + GstBuffer *buf; + + mux->partition.type = MXF_PARTITION_PACK_BODY; + mux->partition.closed = mux->partition.complete = FALSE; + mux->partition.major_version = 0x0001; + mux->partition.minor_version = 0x0002; + mux->partition.kag_size = 0; + mux->partition.this_partition = mux->offset; + mux->partition.prev_partition = 0; + mux->partition.footer_partition = 0; + mux->partition.header_byte_count = 0; + mux->partition.index_byte_count = 0; + mux->partition.index_sid = 0; + mux->partition.body_offset = 0; + mux->partition.body_sid = + mux->preface->content_storage->essence_container_data[0]->body_sid; + + buf = mxf_partition_pack_to_buffer (&mux->partition); + return gst_mxf_mux_push (mux, buf); +} + +static GstFlowReturn +gst_mxf_mux_handle_eos (GstMXFMux * mux) +{ + GSList *l; + gboolean have_data = FALSE; + GstBuffer *packet; + + do { + GstMXFMuxPad *best = NULL; + + have_data = FALSE; + + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + GstClockTime next_gc_timestamp = + gst_util_uint64_scale ((mux->last_gc_position + 1) * GST_SECOND, + mux->min_edit_rate.d, mux->min_edit_rate.n); + + best = NULL; + + if (cpad->have_complete_edit_unit || + gst_adapter_available (cpad->adapter) > 0 || cpad->collect.buffer) { + have_data = TRUE; + if (cpad->last_timestamp < next_gc_timestamp) { + best = cpad; + break; + } + } else if (have_data && !l->next) { + mux->last_gc_position++; + mux->last_gc_timestamp = next_gc_timestamp; + have_data = FALSE; + best = NULL; + break; + } + } + + if (best) { + gst_mxf_mux_handle_buffer (mux, best); + have_data = TRUE; + } + } while (have_data); + + mux->last_gc_position++; + mux->last_gc_timestamp = + gst_util_uint64_scale (mux->last_gc_position * GST_SECOND, + mux->min_edit_rate.d, mux->min_edit_rate.n); + + /* Update essence track durations */ + for (l = mux->collect->data; l; l = l->next) { + GstMXFMuxPad *cpad = l->data; + guint i; + + /* Update durations */ + cpad->source_track->parent.sequence->duration = cpad->pos; + MXF_METADATA_SOURCE_CLIP (cpad->source_track->parent.sequence-> + structural_components[0])->parent.duration = cpad->pos; + for (i = 0; i < mux->preface->content_storage->packages[0]->n_tracks; i++) { + MXFMetadataTimelineTrack *track; + + if (!MXF_IS_METADATA_TIMELINE_TRACK (mux->preface->content_storage-> + packages[0]->tracks[i]) + || !MXF_IS_METADATA_SOURCE_CLIP (mux->preface->content_storage-> + packages[0]->tracks[i]->sequence->structural_components[0])) + continue; + + track = + MXF_METADATA_TIMELINE_TRACK (mux->preface->content_storage-> + packages[0]->tracks[i]); + if (MXF_METADATA_SOURCE_CLIP (track->parent.sequence-> + structural_components[0])->source_track_id == + cpad->source_track->parent.track_id) { + track->parent.sequence->structural_components[0]->duration = cpad->pos; + track->parent.sequence->duration = cpad->pos; + } + } + } + + /* Update timecode track duration */ + { + MXFMetadataTimelineTrack *track = + MXF_METADATA_TIMELINE_TRACK (mux->preface->content_storage-> + packages[0]->tracks[0]); + MXFMetadataSequence *sequence = track->parent.sequence; + MXFMetadataTimecodeComponent *component = + MXF_METADATA_TIMECODE_COMPONENT (sequence->structural_components[0]); + + sequence->duration = mux->last_gc_position; + component->parent.duration = mux->last_gc_position; + } + + { + guint64 body_partition = mux->partition.this_partition; + guint32 body_sid = mux->partition.body_sid; + guint64 footer_partition = mux->offset; + GArray *rip; + GstFlowReturn ret; + MXFRandomIndexPackEntry entry; + + mux->partition.type = MXF_PARTITION_PACK_FOOTER; + mux->partition.closed = TRUE; + mux->partition.complete = TRUE; + mux->partition.major_version = 0x0001; + mux->partition.minor_version = 0x0002; + mux->partition.kag_size = 0; + mux->partition.this_partition = mux->offset; + mux->partition.prev_partition = body_partition; + mux->partition.footer_partition = mux->offset; + mux->partition.header_byte_count = 0; + mux->partition.index_byte_count = 0; + mux->partition.index_sid = 0; + mux->partition.body_offset = 0; + mux->partition.body_sid = 0; + + gst_mxf_mux_write_header_metadata (mux); + + rip = g_array_sized_new (FALSE, FALSE, sizeof (MXFRandomIndexPackEntry), 3); + entry.offset = 0; + entry.body_sid = 0; + g_array_append_val (rip, entry); + entry.offset = body_partition; + entry.body_sid = body_sid; + g_array_append_val (rip, entry); + entry.offset = footer_partition; + entry.body_sid = 0; + g_array_append_val (rip, entry); + + packet = mxf_random_index_pack_to_buffer (rip); + if ((ret = gst_mxf_mux_push (mux, packet)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Failed pushing random index pack"); + } + g_array_free (rip, TRUE); + + /* Rewrite header partition with updated values */ + if (gst_pad_push_event (mux->srcpad, + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, + 0))) { + mux->offset = 0; + mux->partition.type = MXF_PARTITION_PACK_HEADER; + mux->partition.closed = TRUE; + mux->partition.complete = TRUE; + mux->partition.major_version = 0x0001; + mux->partition.minor_version = 0x0002; + mux->partition.kag_size = 0; + mux->partition.this_partition = 0; + mux->partition.prev_partition = footer_partition; + mux->partition.footer_partition = footer_partition; + mux->partition.header_byte_count = 0; + mux->partition.index_byte_count = 0; + mux->partition.index_sid = 0; + mux->partition.body_offset = 0; + mux->partition.body_sid = 0; + + ret = gst_mxf_mux_write_header_metadata (mux); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (mux, "Rewriting header partition failed"); + return ret; + } + } else { + GST_WARNING_OBJECT (mux, "Can't rewrite header partition"); + } + } + + return GST_FLOW_OK; +} + +static gint +_sort_mux_pads (gconstpointer a, gconstpointer b) +{ + const GstMXFMuxPad *pa = a, *pb = b; + MXFMetadataTrackType ta = + mxf_metadata_track_identifier_parse (&pa->writer->data_definition); + MXFMetadataTrackType tb = + mxf_metadata_track_identifier_parse (&pb->writer->data_definition); + + if (ta != tb) + return ta - tb; + + return pa->source_track->parent.track_number - + pa->source_track->parent.track_number; +} + +static GstFlowReturn +gst_mxf_mux_collected (GstCollectPads * pads, gpointer user_data) +{ + GstMXFMux *mux = GST_MXF_MUX (user_data); + GstMXFMuxPad *best = NULL; + GstFlowReturn ret; + GSList *sl; + gboolean eos = TRUE; + + if (mux->state == GST_MXF_MUX_STATE_ERROR) { + GST_ERROR_OBJECT (mux, "Had an error before -- returning"); + return GST_FLOW_ERROR; + } else if (mux->state == GST_MXF_MUX_STATE_EOS) { + GST_WARNING_OBJECT (mux, "EOS"); + return GST_FLOW_UNEXPECTED; + } + + if (mux->state == GST_MXF_MUX_STATE_HEADER) { + if (mux->collect->data == NULL) { + GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL), + ("No input streams configured")); + ret = GST_FLOW_ERROR; + goto error; + } + + if (gst_pad_push_event (mux->srcpad, + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, + 0))) { + if ((ret = gst_mxf_mux_create_metadata (mux)) != GST_FLOW_OK) + goto error; + + if ((ret = gst_mxf_mux_create_header_partition_pack (mux)) != GST_FLOW_OK) + goto error; + + ret = gst_mxf_mux_write_header_metadata (mux); + } else { + ret = GST_FLOW_ERROR; + } + + if (ret != GST_FLOW_OK) + goto error; + + /* Sort pads, we will always write in that order */ + mux->collect->data = g_slist_sort (mux->collect->data, _sort_mux_pads); + + /* Write body partition */ + ret = gst_mxf_mux_write_body_partition (mux); + if (ret != GST_FLOW_OK) + goto error; + mux->state = GST_MXF_MUX_STATE_DATA; + } + + g_return_val_if_fail (g_hash_table_size (mux->metadata) > 0, GST_FLOW_ERROR); + + do { + for (sl = mux->collect->data; sl; sl = sl->next) { + GstMXFMuxPad *cpad = sl->data; + GstClockTime next_gc_timestamp = + gst_util_uint64_scale ((mux->last_gc_position + 1) * GST_SECOND, + mux->min_edit_rate.d, mux->min_edit_rate.n); + + eos &= cpad->collect.abidata.ABI.eos; + + if ((!cpad->collect.abidata.ABI.eos || cpad->have_complete_edit_unit || + gst_adapter_available (cpad->adapter) > 0 || cpad->collect.buffer) + && cpad->last_timestamp < next_gc_timestamp) { + best = cpad; + break; + } else if (!eos && !sl->next) { + mux->last_gc_position++; + mux->last_gc_timestamp = next_gc_timestamp; + eos = FALSE; + best = NULL; + break; + } + } + } while (!eos && best == NULL); + + if (!eos && best) { + ret = gst_mxf_mux_handle_buffer (mux, best); + if (ret != GST_FLOW_OK) + goto error; + } else if (eos) { + GST_DEBUG_OBJECT (mux, "Handling EOS"); + + gst_mxf_mux_handle_eos (mux); + gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); + mux->state = GST_MXF_MUX_STATE_EOS; + return GST_FLOW_UNEXPECTED; + } + + return GST_FLOW_OK; + +error: + { + mux->state = GST_MXF_MUX_STATE_ERROR; + gst_pad_push_event (mux->srcpad, gst_event_new_eos ()); + return ret; + } +} + +static GstStateChangeReturn +gst_mxf_mux_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstMXFMux *mux = GST_MXF_MUX (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_collect_pads_start (mux->collect); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (mux->collect); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_mxf_mux_reset (mux); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} diff --git a/gst/mxf/mxfmux.h b/gst/mxf/mxfmux.h new file mode 100644 index 00000000..81f232ad --- /dev/null +++ b/gst/mxf/mxfmux.h @@ -0,0 +1,103 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __MXF_MUX_H__ +#define __MXF_MUX_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/base/gstcollectpads.h> + +#include "mxfwrite.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MXF_MUX \ + (gst_mxf_mux_get_type ()) +#define GST_MXF_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MXF_MUX, GstMXFMux)) +#define GST_MXF_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MXF_MUX, GstMXFMuxClass)) +#define GST_IS_MXF_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MXF_MUX)) +#define GST_IS_MXF_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MXF_MUX)) + +typedef struct +{ + GstCollectData collect; + + guint64 pos; + GstClockTime last_timestamp; + + MXFMetadataFileDescriptor *descriptor; + + GstAdapter *adapter; + gboolean have_complete_edit_unit; + + gpointer mapping_data; + const MXFEssenceElementWriter *writer; + MXFEssenceElementWriteFunc write_func; + + MXFMetadataSourcePackage *source_package; + MXFMetadataTimelineTrack *source_track; +} GstMXFMuxPad; + +typedef enum +{ + GST_MXF_MUX_STATE_HEADER, + GST_MXF_MUX_STATE_DATA, + GST_MXF_MUX_STATE_EOS, + GST_MXF_MUX_STATE_ERROR +} GstMXFMuxState; + +typedef struct _GstMXFMux { + GstElement element; + + GstPad *srcpad; + GstCollectPads *collect; + GstPadEventFunction collect_event; + + GstMXFMuxState state; + guint n_pads; + + guint64 offset; + + MXFPartitionPack partition; + MXFPrimerPack primer; + + GHashTable *metadata; + MXFMetadataPreface *preface; + + MXFFraction min_edit_rate; + guint64 last_gc_position; + GstClockTime last_gc_timestamp; + + gchar *application; +} GstMXFMux; + +typedef struct _GstMXFMuxClass { + GstElementClass parent; +} GstMXFMuxClass; + +GType gst_mxf_mux_get_type (void); + +G_END_DECLS + +#endif /* __MXF_MUX_H__ */ diff --git a/gst/mxf/mxfparse.c b/gst/mxf/mxfparse.c index d4d52312..f0c0d0e5 100644 --- a/gst/mxf/mxfparse.c +++ b/gst/mxf/mxfparse.c @@ -29,61 +29,19 @@ 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 }; - -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) +mxf_is_mxf_packet (const MXFUL * ul) { - return (memcmp (key, mxf_key, 4) == 0); + return mxf_ul_is_subclass (MXF_UL (SMPTE), ul); } /* SMPTE 377M 6.1: Check if this is a valid partition pack */ gboolean -mxf_is_partition_pack (const MXFUL * key) +mxf_is_partition_pack (const MXFUL * ul) { - if (memcmp (key, partition_pack_key, 13) == 0 && key->u[13] >= 0x02 - && key->u[13] <= 0x04 && key->u[14] < 0x05 && key->u[15] == 0x00) + if (mxf_ul_is_subclass (MXF_UL (PARTITION_PACK), ul) && + ul->u[13] >= 0x02 && ul->u[13] <= 0x04 && + ul->u[14] < 0x05 && ul->u[15] == 0x00) return TRUE; return FALSE; @@ -91,10 +49,9 @@ mxf_is_partition_pack (const MXFUL * key) /* SMPTE 377M 6.2: header partition pack has byte 14 == 0x02 */ gboolean -mxf_is_header_partition_pack (const MXFUL * key) +mxf_is_header_partition_pack (const MXFUL * ul) { - if (memcmp (key, partition_pack_key, 13) == 0 && key->u[13] == 0x02 && - key->u[14] < 0x05 && key->u[15] == 0x00) + if (mxf_is_partition_pack (ul) && ul->u[13] == 0x02) return TRUE; return FALSE; @@ -102,10 +59,9 @@ mxf_is_header_partition_pack (const MXFUL * key) /* SMPTE 377M 6.3: body partition pack has byte 14 == 0x03 */ gboolean -mxf_is_body_partition_pack (const MXFUL * key) +mxf_is_body_partition_pack (const MXFUL * ul) { - if (memcmp (key, partition_pack_key, 13) == 0 && key->u[13] == 0x03 && - key->u[14] < 0x05 && key->u[15] == 0x00) + if (mxf_is_partition_pack (ul) && ul->u[13] == 0x03) return TRUE; return FALSE; @@ -113,159 +69,92 @@ mxf_is_body_partition_pack (const MXFUL * key) /* SMPTE 377M 6.4: footer partition pack has byte 14 == 0x04 */ gboolean -mxf_is_footer_partition_pack (const MXFUL * key) +mxf_is_footer_partition_pack (const MXFUL * ul) { - if (memcmp (key, partition_pack_key, 13) == 0 && key->u[13] == 0x04 && - key->u[14] < 0x05 && key->u[15] == 0x00) + if (mxf_is_partition_pack (ul) && ul->u[13] == 0x04) return TRUE; return FALSE; } gboolean -mxf_is_fill (const MXFUL * key) +mxf_is_fill (const MXFUL * ul) { - return (memcmp (key, fill_key, 16) == 0); + return (mxf_ul_is_subclass (MXF_UL (FILL), ul)); } gboolean -mxf_is_primer_pack (const MXFUL * key) +mxf_is_primer_pack (const MXFUL * ul) { - return (memcmp (key, primer_pack_key, 16) == 0); + return (mxf_ul_is_subclass (MXF_UL (PRIMER_PACK), ul)); } gboolean -mxf_is_metadata (const MXFUL * key) +mxf_is_metadata (const MXFUL * ul) { - return (memcmp (key, metadata_key, 13) == 0 && key->u[15] == 0x00); + return (mxf_ul_is_subclass (MXF_UL (METADATA), ul)); } /* SMPTE 377M 8.7.3 */ gboolean -mxf_is_descriptive_metadata (const MXFUL * key) +mxf_is_descriptive_metadata (const MXFUL * ul) { - return (memcmp (key, mxf_key, 4) == 0 && - key->u[4] == 0x02 && - key->u[6] == 0x01 && - key->u[7] == 0x01 && - key->u[8] == 0x0d && - key->u[9] == 0x01 && key->u[10] == 0x04 && key->u[11] == 0x01); + return (mxf_ul_is_subclass (MXF_UL (DESCRIPTIVE_METADATA), ul)); } gboolean -mxf_is_random_index_pack (const MXFUL * key) +mxf_is_random_index_pack (const MXFUL * ul) { - return (memcmp (key, random_index_pack_key, 16) == 0); + return (mxf_ul_is_subclass (MXF_UL (RANDOM_INDEX_PACK), ul)); } gboolean -mxf_is_index_table_segment (const MXFUL * key) +mxf_is_index_table_segment (const MXFUL * ul) { - return (memcmp (key, index_table_segment_key, 16) == 0); + return (mxf_ul_is_subclass (MXF_UL (INDEX_TABLE_SEGMENT), ul)); } /* SMPTE 379M 6.2.1 */ gboolean -mxf_is_generic_container_system_item (const MXFUL * key) +mxf_is_generic_container_system_item (const MXFUL * ul) { - 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)); + return (mxf_ul_is_subclass (MXF_UL (GENERIC_CONTAINER_SYSTEM_ITEM), ul) && + (ul->u[12] == 0x04 || ul->u[12] == 0x14)); } /* SMPTE 379M 7.1 */ gboolean -mxf_is_generic_container_essence_element (const MXFUL * key) +mxf_is_generic_container_essence_element (const MXFUL * ul) { - 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)); + return (mxf_ul_is_subclass (MXF_UL (GENERIC_CONTAINER_ESSENCE_ELEMENT), ul) + && (ul->u[12] == 0x05 || ul->u[12] == 0x06 + || ul->u[12] == 0x07 || ul->u[12] == 0x15 + || ul->u[12] == 0x16 || ul->u[12] == 0x17 || ul->u[12] == 0x18)); } /* SMPTE 379M 8 */ gboolean -mxf_is_generic_container_essence_container_label (const MXFUL * key) +mxf_is_generic_container_essence_container_label (const MXFUL * ul) { - 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)); + return (mxf_ul_is_subclass (MXF_UL + (GENERIC_CONTAINER_ESSENCE_CONTAINER_LABEL), ul) && (ul->u[12] == 0x01 + || ul->u[12] == 0x02)); } /* Essence container label found in files generated by Avid */ -static const guint8 avid_essence_container_label[] = { - 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0xff, 0x4b, 0x46, 0x41, 0x41, 0x00, - 0x0d, 0x4d, 0x4f -}; - gboolean -mxf_is_avid_essence_container_label (const MXFUL * key) +mxf_is_avid_essence_container_label (const MXFUL * ul) { - return (memcmp (&key->u, avid_essence_container_label, 16) == 0); + return (mxf_ul_is_subclass (MXF_UL (AVID_ESSENCE_CONTAINER_ESSENCE_LABEL), + ul)); } /* Essence element key found in files generated by Avid */ -static const guint8 avid_essence_element_ul[] = { - 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, 0x0e, 0x04, 0x03, 0x01, 0x00, - 0x00, 0x00, 0x00 -}; - gboolean -mxf_is_avid_essence_container_essence_element (const MXFUL * key) +mxf_is_avid_essence_container_essence_element (const MXFUL * ul) { - return (memcmp (&key->u, avid_essence_element_ul, 12) == 0); -} - -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); -} - -guint -mxf_ul_hash (const MXFUL * key) -{ - guint32 ret = 0; - guint 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; -} - -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; + return (mxf_ul_is_subclass (MXF_UL (AVID_ESSENCE_CONTAINER_ESSENCE_ELEMENT), + ul)); } gboolean @@ -277,7 +166,9 @@ mxf_umid_is_equal (const MXFUMID * a, const MXFUMID * b) gboolean mxf_umid_is_zero (const MXFUMID * umid) { - return (memcmp (umid, &umid_zero, 32) == 0); + static const MXFUMID zero = { {0,} }; + + return (memcmp (umid, &zero, 32) == 0); } gchar * @@ -362,10 +253,13 @@ mxf_timestamp_parse (MXFTimestamp * timestamp, const guint8 * data, guint size) return TRUE; } +/* SMPTE 377M 3.3: A value of 0 for every field means unknown timestamp */ gboolean mxf_timestamp_is_unknown (const MXFTimestamp * a) { - return (memcmp (a, &mxf_timestamp_unknown, sizeof (MXFTimestamp)) == 0); + static const MXFTimestamp unknown = { 0, 0, 0, 0, 0, 0, 0 }; + + return (memcmp (a, &unknown, sizeof (MXFTimestamp)) == 0); } gint @@ -469,54 +363,11 @@ mxf_product_version_parse (MXFProductVersion * product_version, } gboolean -mxf_ul_array_parse (MXFUL ** array, guint32 * count, const guint8 * data, - guint size) +mxf_product_version_is_valid (const MXFProductVersion * version) { - guint32 element_count, element_size; - guint i; - - g_return_val_if_fail (array != NULL, FALSE); - g_return_val_if_fail (count != NULL, FALSE); - g_return_val_if_fail (data != NULL, FALSE); + static const guint8 null[sizeof (MXFProductVersion)] = { 0, }; - if (size < 8) - return FALSE; - - element_count = GST_READ_UINT32_BE (data); - data += 4; - size -= 4; - - if (element_count == 0) { - *array = NULL; - *count = 0; - return TRUE; - } - - element_size = GST_READ_UINT32_BE (data); - data += 4; - size -= 4; - - if (element_size != 16) { - *array = NULL; - *count = 0; - return FALSE; - } - - if (16 * element_count < size) { - *array = NULL; - *count = 0; - return FALSE; - } - - *array = g_new (MXFUL, element_count); - *count = element_count; - - for (i = 0; i < element_count; i++) { - memcpy (&((*array)[i]), data, 16); - data += 16; - } - - return TRUE; + return (memcmp (version, &null, sizeof (MXFProductVersion)) == 0); } /* SMPTE 377M 6.1, Table 2 */ @@ -1023,7 +874,12 @@ mxf_primer_pack_reset (MXFPrimerPack * pack) if (pack->mappings) g_hash_table_destroy (pack->mappings); + if (pack->reverse_mappings) + g_hash_table_destroy (pack->reverse_mappings); + memset (pack, 0, sizeof (MXFPrimerPack)); + + pack->next_free_tag = 0x8000; } /* structural metadata parsing */ @@ -1051,7 +907,10 @@ mxf_local_tag_parse (const guint8 * data, guint size, guint16 * tag, void mxf_local_tag_free (MXFLocalTag * tag) { - g_free (tag->data); + if (tag->g_slice) + g_slice_free1 (tag->size, tag->data); + else + g_free (tag->data); g_slice_free (MXFLocalTag, tag); } @@ -1100,6 +959,32 @@ mxf_local_tag_add_to_hash_table (const MXFPrimerPack * primer, return TRUE; } +gboolean +mxf_local_tag_insert (MXFLocalTag * tag, GHashTable ** hash_table) +{ +#ifndef GST_DISABLE_GST_DEBUG + gchar str[48]; +#endif + + g_return_val_if_fail (tag != NULL, FALSE); + g_return_val_if_fail (hash_table != NULL, FALSE); + + if (*hash_table == NULL) + *hash_table = + g_hash_table_new_full ((GHashFunc) mxf_ul_hash, + (GEqualFunc) mxf_ul_is_equal, (GDestroyNotify) NULL, + (GDestroyNotify) mxf_local_tag_free); + + g_return_val_if_fail (*hash_table != NULL, FALSE); + + GST_DEBUG ("Adding local tag 0x%04x with UL %s and size %u", tag, + mxf_ul_to_string (&tag->key, str), tag->size); + + g_hash_table_insert (*hash_table, &tag->key, tag); + + return TRUE; +} + static GSList *_mxf_essence_element_handler_registry = NULL; void diff --git a/gst/mxf/mxfparse.h b/gst/mxf/mxfparse.h index 5a5e630e..58d88d6b 100644 --- a/gst/mxf/mxfparse.h +++ b/gst/mxf/mxfparse.h @@ -25,6 +25,7 @@ #include <string.h> #include "mxftypes.h" +#include "mxful.h" #include "mxfmetadata.h" typedef GstFlowReturn (*MXFEssenceElementHandleFunc) (const MXFUL *key, GstBuffer *buffer, GstCaps *caps, MXFMetadataTimelineTrack *track, gpointer mapping_data, GstBuffer **outbuf); @@ -34,11 +35,6 @@ typedef struct { GstCaps * (*create_caps) (MXFMetadataTimelineTrack *track, GstTagList **tags, MXFEssenceElementHandleFunc *handler, gpointer *mapping_data); } MXFEssenceElementHandler; -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); -guint mxf_ul_hash (const MXFUL *ul); - gchar *mxf_umid_to_string (const MXFUMID * umid, gchar str[96]); MXFUMID *mxf_umid_from_string (const gchar *str, MXFUMID * umid); gboolean mxf_umid_is_equal (const MXFUMID *a, const MXFUMID *b); @@ -72,6 +68,7 @@ gchar * mxf_utf16_to_utf8 (const guint8 * data, guint size); gboolean mxf_product_version_parse (MXFProductVersion * product_version, const guint8 * data, guint size); +gboolean mxf_product_version_is_valid (const MXFProductVersion *version); gboolean mxf_fraction_parse (MXFFraction *fraction, const guint8 *data, guint size); gdouble mxf_fraction_to_double (const MXFFraction *fraction); @@ -81,8 +78,6 @@ gboolean mxf_timestamp_is_unknown (const MXFTimestamp *a); gint mxf_timestamp_compare (const MXFTimestamp *a, const MXFTimestamp *b); gchar *mxf_timestamp_to_string (const MXFTimestamp *t, gchar str[32]); -gboolean mxf_ul_array_parse (MXFUL **array, guint32 *count, const guint8 *data, guint size); - gboolean mxf_partition_pack_parse (const MXFUL *key, MXFPartitionPack *pack, const guint8 *data, guint size); void mxf_partition_pack_reset (MXFPartitionPack *pack); @@ -96,11 +91,12 @@ void mxf_index_table_segment_reset (MXFIndexTableSegment *segment); gboolean mxf_local_tag_parse (const guint8 * data, guint size, guint16 * tag, guint16 * tag_size, const guint8 ** tag_data); -void gst_mxf_local_tag_free (MXFLocalTag *tag); +void mxf_local_tag_free (MXFLocalTag *tag); gboolean mxf_local_tag_add_to_hash_table (const MXFPrimerPack *primer, guint16 tag, const guint8 *tag_data, guint16 tag_size, GHashTable **hash_table); +gboolean mxf_local_tag_insert (MXFLocalTag *tag, GHashTable **hash_table); void mxf_essence_element_handler_register (const MXFEssenceElementHandler *handler); const MXFEssenceElementHandler * mxf_essence_element_handler_find (const MXFMetadataTimelineTrack *track); diff --git a/gst/mxf/mxftypes.h b/gst/mxf/mxftypes.h index dcbbd28d..ffb40d9c 100644 --- a/gst/mxf/mxftypes.h +++ b/gst/mxf/mxftypes.h @@ -29,6 +29,10 @@ typedef struct { guint8 u[16]; } MXFUL; +typedef struct { + guint8 u[16]; +} MXFUUID; + /* SMPTE 377M 3.2 */ typedef struct { guint8 u[32]; @@ -65,6 +69,8 @@ typedef struct { MXFUL key; guint16 size; guint8 *data; + + gboolean g_slice; /* TRUE if data was allocated by GSlice */ } MXFLocalTag; /* SMPTE 377M 11.1 */ @@ -114,6 +120,8 @@ typedef struct { typedef struct { guint64 offset; GHashTable *mappings; + GHashTable *reverse_mappings; + guint16 next_free_tag; } MXFPrimerPack; /* SMPTE 377M 10.2.3 */ diff --git a/gst/mxf/mxful.c b/gst/mxf/mxful.c new file mode 100644 index 00000000..37624c53 --- /dev/null +++ b/gst/mxf/mxful.c @@ -0,0 +1,268 @@ +/* GStreamer + * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "mxful.h" + +const MXFUL _mxf_ul_table[] = { + /* SMPTE */ + {{0x06, 0x0e, 0x2b, 0x34, 0x00,}}, + /* FILL, SMPTE 336M */ + {{0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x03, 0x01, 0x02, 0x10, 0x01, 0x00,}}, + /* PARTITION_PACK, SMPTE 377M 6.1 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x00,}}, + /* PRIMER_PACK, SMPTE 377M 8.1 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x05, 0x01, 0x00}}, + /* METADATA, SMPTE 377M 8.6 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x00,}}, + /* DESCRIPTIVE_METADATA, SMPTE 377M 8.7.3 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x02, 0x00, 0x01, 0x01, + 0x0d, 0x01, 0x04, 0x01, 0x00,}}, + /* RANDOM_INDEX_PACK, SMPTE 377M 11.1 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x11, 0x01, 0x00}}, + /* INDEX_TABLE_SEGMENT, SMPTE 377M 10.2.2 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x10, 0x01, 0x00}}, + /* GENERIC_CONTAINER_SYSTEM_ITEM, SMPTE 379M 6.2.1 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x02, 0x00, 0x01, 0x00, + 0x0d, 0x01, 0x03, 0x01, 0x00}}, + /* GENERIC_CONTAINER_ESSENCE_ELEMENT, SMPTE 379M 7.1 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x00, + 0x0d, 0x01, 0x03, 0x01, 0x00,}}, + /* GENERIC_CONTAINER_ESSENCE_CONTAINER_LABEL, SMPTE 379M 8 */ + {{0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x00, + 0x0d, 0x01, 0x03, 0x01, 0x00,}}, + /* AVID_ESSENCE_CONTAINER_ESSENCE_ELEMENT, undocumented */ + {{0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, + 0x0e, 0x04, 0x03, 0x01, 0x00,}}, + /* AVID_ESSENCE_CONTAINER_ESSENCE_LABEL, undocumented */ + {{0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0xff, + 0x4b, 0x46, 0x41, 0x41, 0x00, 0x0d, 0x4d, 0x4f}}, +}; + +gboolean +mxf_ul_is_equal (const MXFUL * a, const MXFUL * b) +{ + guint i; + + g_return_val_if_fail (a != NULL, FALSE); + g_return_val_if_fail (b != NULL, FALSE); + + for (i = 0; i < 16; i++) { + /* registry version */ + if (i == 7) + continue; + + if (a->u[i] != b->u[i]) + return FALSE; + } + + return TRUE; +} + +gboolean +mxf_ul_is_subclass (const MXFUL * class, const MXFUL * subclass) +{ + guint i; + + g_return_val_if_fail (class != NULL, FALSE); + g_return_val_if_fail (subclass != NULL, FALSE); + + for (i = 0; i < 16; i++) { + if (i == 7) + /* registry version */ + continue; + + if (class->u[i] != 0x00 && class->u[i] != subclass->u[i]) + return FALSE; + } + + return TRUE; +} + +gboolean +mxf_ul_is_zero (const MXFUL * ul) +{ + static const guint8 zero[16] = { 0, }; + + g_return_val_if_fail (ul != NULL, FALSE); + + return (memcmp (ul, &zero, 16) == 0); +} + +gboolean +mxf_ul_is_valid (const MXFUL * ul) +{ + guint i, j; + + g_return_val_if_fail (ul != NULL, FALSE); + + for (i = 0; i < 16; i++) { + if (ul->u[i] == 0x00) { + for (j = i; j < 16; j++) { + if (ul->u[j] != 0x00) + return FALSE; + } + + return TRUE; + } + + if (ul->u[i] > 0x7f) + return FALSE; + } + + return TRUE; +} + +guint +mxf_ul_hash (const MXFUL * ul) +{ + guint32 ret = 0; + guint i; + + g_return_val_if_fail (ul != NULL, 0); + + for (i = 0; i < 4; i++) + ret ^= (ul->u[i * 4 + 0] << 24) | + (ul->u[i * 4 + 1] << 16) | + (ul->u[i * 4 + 2] << 8) | (ul->u[i * 4 + 3] << 0); + + return ret; +} + +gchar * +mxf_ul_to_string (const MXFUL * ul, gchar str[48]) +{ + gchar *ret = str; + + g_return_val_if_fail (ul != NULL, NULL); + + if (ret == NULL) + ret = g_malloc (48); + + g_snprintf (ret, 48, + "%02x.%02x.%02x.%02x." + "%02x.%02x.%02x.%02x." + "%02x.%02x.%02x.%02x." + "%02x.%02x.%02x.%02x", + ul->u[0], ul->u[1], ul->u[2], ul->u[3], + ul->u[4], ul->u[5], ul->u[6], ul->u[7], + ul->u[8], ul->u[9], ul->u[10], ul->u[11], + ul->u[12], ul->u[13], ul->u[14], ul->u[15]); + + return ret; +} + +MXFUL * +mxf_ul_from_string (const gchar * str, MXFUL * ul) +{ + MXFUL *ret = ul; + gint len; + guint i, j; + + g_return_val_if_fail (str != NULL, NULL); + + len = strlen (str); + if (len != 47) { + GST_ERROR ("Invalid UL string length %d, should be 47", len); + return NULL; + } + + if (ret == NULL) + ret = g_new0 (MXFUL, 1); + + memset (ret, 0, 16); + + for (i = 0, j = 0; i < 16; i++) { + if (!g_ascii_isxdigit (str[j]) || + !g_ascii_isxdigit (str[j + 1]) || + (str[j + 2] != '.' && str[j + 2] != '\0')) { + GST_ERROR ("Invalid UL string '%s'", str); + if (ul == NULL) + g_free (ret); + return NULL; + } + + ret->u[i] = (g_ascii_xdigit_value (str[j]) << 4) | + (g_ascii_xdigit_value (str[j + 1])); + j += 3; + } + return ret; +} + +gboolean +mxf_ul_array_parse (MXFUL ** array, guint32 * count, const guint8 * data, + guint size) +{ + guint32 element_count, element_size; + guint i; + + g_return_val_if_fail (array != NULL, FALSE); + g_return_val_if_fail (count != NULL, FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + if (size < 8) + return FALSE; + + element_count = GST_READ_UINT32_BE (data); + data += 4; + size -= 4; + + if (element_count == 0) { + *array = NULL; + *count = 0; + return TRUE; + } + + element_size = GST_READ_UINT32_BE (data); + data += 4; + size -= 4; + + if (element_size != 16) { + *array = NULL; + *count = 0; + return FALSE; + } + + if (16 * element_count < size) { + *array = NULL; + *count = 0; + return FALSE; + } + + *array = g_new (MXFUL, element_count); + *count = element_count; + + for (i = 0; i < element_count; i++) { + memcpy (&((*array)[i]), data, 16); + data += 16; + } + + return TRUE; +} diff --git a/gst/mxf/mxful.h b/gst/mxf/mxful.h new file mode 100644 index 00000000..b187229d --- /dev/null +++ b/gst/mxf/mxful.h @@ -0,0 +1,59 @@ +/* GStreamer + * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __MXF_UL_H__ +#define __MXF_UL_H__ + +#include <gst/gst.h> + +#include "mxftypes.h" + +typedef enum { + MXF_UL_SMPTE = 0, + MXF_UL_FILL, + MXF_UL_PARTITION_PACK, + MXF_UL_PRIMER_PACK, + MXF_UL_METADATA, + MXF_UL_DESCRIPTIVE_METADATA, + MXF_UL_RANDOM_INDEX_PACK, + MXF_UL_INDEX_TABLE_SEGMENT, + MXF_UL_GENERIC_CONTAINER_SYSTEM_ITEM, + MXF_UL_GENERIC_CONTAINER_ESSENCE_ELEMENT, + MXF_UL_GENERIC_CONTAINER_ESSENCE_CONTAINER_LABEL, + MXF_UL_AVID_ESSENCE_CONTAINER_ESSENCE_ELEMENT, + MXF_UL_AVID_ESSENCE_CONTAINER_ESSENCE_LABEL, + MXF_UL_MAX +} MXFULId; + +extern const MXFUL _mxf_ul_table[MXF_UL_MAX]; + +#define MXF_UL(id) (&_mxf_ul_table[MXF_UL_##id]) + +gboolean mxf_ul_is_equal (const MXFUL *a, const MXFUL *b); +gboolean mxf_ul_is_subclass (const MXFUL *class, const MXFUL *subclass); +gboolean mxf_ul_is_zero (const MXFUL *ul); +gboolean mxf_ul_is_valid (const MXFUL *ul); +guint mxf_ul_hash (const MXFUL *ul); + +gchar * mxf_ul_to_string (const MXFUL *ul, gchar str[48]); +MXFUL * mxf_ul_from_string (const gchar *str, MXFUL *ul); + +gboolean mxf_ul_array_parse (MXFUL **array, guint32 *count, const guint8 *data, guint size); + +#endif /* __MXF_UL_H__ */ diff --git a/gst/mxf/mxfup.c b/gst/mxf/mxfup.c index ccc34ffd..a86a2c19 100644 --- a/gst/mxf/mxfup.c +++ b/gst/mxf/mxfup.c @@ -24,7 +24,6 @@ /* TODO: * - Handle CDCI essence * - Handle more formats with RGBA descriptor (4:4:4 / 4:4:4:4 YUV, RGB656, ...) - * - Correctly transform for the GStreamer strides * - Handle all the dimensions and other properties in the picture * essence descriptors correctly according to S377M Annex E * - Handle interlaced correctly, i.e. weave until we support one-field-per-buffer @@ -40,12 +39,65 @@ #include <gst/video/video.h> #include "mxfup.h" +#include "mxfwrite.h" GST_DEBUG_CATEGORY_EXTERN (mxf_debug); #define GST_CAT_DEFAULT mxf_debug +static const struct +{ + const gchar *caps; + guint32 n_pixel_layout; + guint8 pixel_layout[10]; + guint32 fourcc; +} _rgba_mapping_table[] = { + { + GST_VIDEO_CAPS_RGB, 3, { + 'R', 8, 'G', 8, 'B', 8}, GST_MAKE_FOURCC ('R', 'G', 'B', ' ')}, { + GST_VIDEO_CAPS_BGR, 3, { + 'B', 8, 'G', 8, 'R', 8}, GST_MAKE_FOURCC ('B', 'G', 'R', ' ')}, { + GST_VIDEO_CAPS_YUV ("v308"), 3, { + 'Y', 8, 'U', 8, 'V', 8}, GST_MAKE_FOURCC ('v', '3', '0', '8')}, { + GST_VIDEO_CAPS_xRGB, 4, { + 'F', 8, 'R', 8, 'G', 8, 'B', 8}, GST_MAKE_FOURCC ('x', 'R', 'G', 'B')}, { + GST_VIDEO_CAPS_RGBx, 4, { + 'R', 8, 'G', 8, 'B', 8, 'F', 8}, GST_MAKE_FOURCC ('R', 'G', 'B', 'x')}, { + GST_VIDEO_CAPS_xBGR, 4, { + 'F', 8, 'B', 8, 'G', 8, 'R', 8}, GST_MAKE_FOURCC ('x', 'B', 'G', 'R')}, { + GST_VIDEO_CAPS_BGRx, 4, { + 'B', 8, 'G', 8, 'R', 8, 'F', 8}, GST_MAKE_FOURCC ('B', 'G', 'R', 'x')}, { + GST_VIDEO_CAPS_RGBA, 4, { + 'R', 8, 'G', 8, 'B', 8, 'A', 8}, GST_MAKE_FOURCC ('R', 'G', 'B', 'A')}, { + GST_VIDEO_CAPS_ARGB, 4, { + 'A', 8, 'R', 8, 'G', 8, 'B', 8}, GST_MAKE_FOURCC ('A', 'R', 'G', 'B')}, { + GST_VIDEO_CAPS_BGRA, 4, { + 'B', 8, 'G', 8, 'R', 8, 'A', 8}, GST_MAKE_FOURCC ('B', 'G', 'R', 'A')}, { + GST_VIDEO_CAPS_ABGR, 4, { + 'A', 8, 'B', 8, 'G', 8, 'R', 8}, GST_MAKE_FOURCC ('A', 'B', 'G', 'R')}, { + GST_VIDEO_CAPS_YUV ("AYUV"), 4, { + 'A', 8, 'Y', 8, 'U', 8, 'V', 8}, GST_MAKE_FOURCC ('A', 'Y', 'U', 'V')} +}; + +static const struct +{ + const gchar *caps; + guint bpp; + guint horizontal_subsampling; + guint vertical_subsampling; + gboolean reversed_byte_order; + guint32 fourcc; +} _cdci_mapping_table[] = { + { + GST_VIDEO_CAPS_YUV ("YUY2"), 2, 1, 0, TRUE, GST_MAKE_FOURCC ('Y', 'U', 'Y', + '2')}, { +GST_VIDEO_CAPS_YUV ("UYVY"), 2, 1, 0, FALSE, GST_MAKE_FOURCC ('U', 'Y', 'V', + 'Y')},}; + typedef struct { + guint32 fourcc; /* fourcc or RGB format specifier */ + gint width, height; + guint bpp; guint32 image_start_offset; guint32 image_end_offset; } MXFUPMappingData; @@ -93,7 +145,6 @@ mxf_up_handle_essence_element (const MXFUL * key, GstBuffer * buffer, } if (!data || (data->image_start_offset == 0 && data->image_end_offset == 0)) { - *outbuf = buffer; } else { if (data->image_start_offset + data->image_end_offset > GST_BUFFER_SIZE (buffer)) { @@ -104,10 +155,38 @@ mxf_up_handle_essence_element (const MXFUL * key, GstBuffer * buffer, GST_BUFFER_DATA (buffer) += data->image_start_offset; GST_BUFFER_SIZE (buffer) -= data->image_start_offset; GST_BUFFER_SIZE (buffer) -= data->image_end_offset; - *outbuf = buffer; } } + if (GST_BUFFER_SIZE (buffer) != data->bpp * data->width * data->height) { + GST_ERROR ("Invalid buffer size"); + return GST_FLOW_ERROR; + } + + if (data->bpp != 4 + || GST_ROUND_UP_4 (data->width * data->bpp) != data->width * data->bpp) { + guint y; + GstBuffer *ret; + guint8 *indata, *outdata; + + ret = + gst_buffer_new_and_alloc (GST_ROUND_UP_4 (data->width * data->bpp) * + data->height); + indata = GST_BUFFER_DATA (buffer); + outdata = GST_BUFFER_DATA (ret); + + for (y = 0; y < data->height; y++) { + memcpy (outdata, indata, data->width * data->bpp); + outdata += GST_ROUND_UP_4 (data->width * data->bpp); + indata += data->width * data->bpp; + } + + gst_buffer_unref (buffer); + *outbuf = ret; + } else { + *outbuf = buffer; + } + return GST_FLOW_OK; } @@ -117,85 +196,91 @@ mxf_up_rgba_create_caps (MXFMetadataTimelineTrack * track, MXFEssenceElementHandleFunc * handler, gpointer * mapping_data) { GstCaps *caps = NULL; + guint i; + guint32 fourcc; + guint bpp; if (!d->pixel_layout) { GST_ERROR ("No pixel layout"); return NULL; } - if (d->n_pixel_layout == 3) { - if (d->pixel_layout[0] == 'R' && d->pixel_layout[2] == 'G' - && d->pixel_layout[4] == 'B' && d->pixel_layout[1] == 8 - && d->pixel_layout[3] == 8 && d->pixel_layout[5] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_RGB); - } else if (d->pixel_layout[0] == 'B' && d->pixel_layout[2] == 'G' - && d->pixel_layout[4] == 'R' && d->pixel_layout[1] == 8 - && d->pixel_layout[3] == 8 && d->pixel_layout[5] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_BGR); - } else { - GST_ERROR ("Unsupport 3 component pixel layout"); - return NULL; - } - } else if (d->n_pixel_layout == 4) { - if (d->pixel_layout[0] == 'R' && d->pixel_layout[2] == 'G' - && d->pixel_layout[4] == 'B' && d->pixel_layout[6] == 'F' - && d->pixel_layout[1] == 8 && d->pixel_layout[3] == 8 - && d->pixel_layout[5] == 8 && d->pixel_layout[7] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_RGBx); - } else if (d->pixel_layout[0] == 'B' && d->pixel_layout[2] == 'G' - && d->pixel_layout[4] == 'R' && d->pixel_layout[6] == 'F' - && d->pixel_layout[1] == 8 && d->pixel_layout[3] == 8 - && d->pixel_layout[5] == 8 && d->pixel_layout[7] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_BGRx); - } else if (d->pixel_layout[0] == 'F' && d->pixel_layout[2] == 'R' - && d->pixel_layout[4] == 'G' && d->pixel_layout[6] == 'B' - && d->pixel_layout[1] == 8 && d->pixel_layout[3] == 8 - && d->pixel_layout[5] == 8 && d->pixel_layout[7] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_xRGB); - } else if (d->pixel_layout[0] == 'F' && d->pixel_layout[2] == 'B' - && d->pixel_layout[4] == 'G' && d->pixel_layout[6] == 'R' - && d->pixel_layout[1] == 8 && d->pixel_layout[3] == 8 - && d->pixel_layout[5] == 8 && d->pixel_layout[7] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_xBGR); - } else if (d->pixel_layout[0] == 'A' && d->pixel_layout[2] == 'R' - && d->pixel_layout[4] == 'G' && d->pixel_layout[6] == 'B' - && d->pixel_layout[1] == 8 && d->pixel_layout[3] == 8 - && d->pixel_layout[5] == 8 && d->pixel_layout[7] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_ARGB); - } else if (d->pixel_layout[0] == 'A' && d->pixel_layout[2] == 'B' - && d->pixel_layout[4] == 'G' && d->pixel_layout[6] == 'R' - && d->pixel_layout[1] == 8 && d->pixel_layout[3] == 8 - && d->pixel_layout[5] == 8 && d->pixel_layout[7] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_ABGR); - } else if (d->pixel_layout[0] == 'R' && d->pixel_layout[2] == 'G' - && d->pixel_layout[4] == 'B' && d->pixel_layout[6] == 'A' - && d->pixel_layout[1] == 8 && d->pixel_layout[3] == 8 - && d->pixel_layout[5] == 8 && d->pixel_layout[7] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_RGBA); - } else if (d->pixel_layout[0] == 'B' && d->pixel_layout[2] == 'G' - && d->pixel_layout[4] == 'R' && d->pixel_layout[6] == 'A' - && d->pixel_layout[1] == 8 && d->pixel_layout[3] == 8 - && d->pixel_layout[5] == 8 && d->pixel_layout[7] == 8) { - caps = gst_caps_from_string (GST_VIDEO_CAPS_BGRA); - } else { - GST_ERROR ("Unsupport 4 component pixel layout"); - return NULL; + for (i = 0; i < G_N_ELEMENTS (_rgba_mapping_table); i++) { + if (d->n_pixel_layout != _rgba_mapping_table[i].n_pixel_layout) + continue; + + if (memcmp (d->pixel_layout, &_rgba_mapping_table[i].pixel_layout, + _rgba_mapping_table[i].n_pixel_layout * 2) == 0) { + caps = gst_caps_from_string (_rgba_mapping_table[i].caps); + fourcc = _rgba_mapping_table[i].fourcc; + bpp = _rgba_mapping_table[i].n_pixel_layout; + break; } + } + + if (caps) { + MXFUPMappingData *data = g_new0 (MXFUPMappingData, 1); + + mxf_metadata_generic_picture_essence_descriptor_set_caps (&d->parent, caps); + + data->width = d->parent.stored_width; + data->height = d->parent.stored_height; + data->fourcc = fourcc; + data->bpp = bpp; + data->image_start_offset = + ((MXFMetadataGenericPictureEssenceDescriptor *) d)->image_start_offset; + data->image_end_offset = + ((MXFMetadataGenericPictureEssenceDescriptor *) d)->image_end_offset; + + *mapping_data = data; } else { - GST_ERROR ("Pixel layouts with %u components not supported yet", - d->n_pixel_layout); - return NULL; + GST_WARNING ("Unsupported pixel layout"); + } + + return caps; +} + +static GstCaps * +mxf_up_cdci_create_caps (MXFMetadataTimelineTrack * track, + MXFMetadataCDCIPictureEssenceDescriptor * d, GstTagList ** tags, + MXFEssenceElementHandleFunc * handler, gpointer * mapping_data) +{ + GstCaps *caps = NULL; + guint i; + guint32 fourcc; + guint bpp; + + for (i = 0; i < G_N_ELEMENTS (_cdci_mapping_table); i++) { + if (_cdci_mapping_table[i].horizontal_subsampling == + d->horizontal_subsampling + && _cdci_mapping_table[i].vertical_subsampling == + d->vertical_subsampling + && _cdci_mapping_table[i].reversed_byte_order == + d->reversed_byte_order) { + caps = gst_caps_from_string (_cdci_mapping_table[i].caps); + fourcc = _cdci_mapping_table[i].fourcc; + bpp = _cdci_mapping_table[i].bpp; + break; + } } if (caps) { MXFUPMappingData *data = g_new0 (MXFUPMappingData, 1); + mxf_metadata_generic_picture_essence_descriptor_set_caps (&d->parent, caps); + + data->width = d->parent.stored_width; + data->height = d->parent.stored_height; + data->fourcc = fourcc; + data->bpp = bpp; data->image_start_offset = ((MXFMetadataGenericPictureEssenceDescriptor *) d)->image_start_offset; data->image_end_offset = ((MXFMetadataGenericPictureEssenceDescriptor *) d)->image_end_offset; *mapping_data = data; + } else { + GST_WARNING ("Unsupported CDCI format"); } return caps; @@ -247,15 +332,12 @@ mxf_up_create_caps (MXFMetadataTimelineTrack * track, GstTagList ** tags, if (r) { caps = mxf_up_rgba_create_caps (track, r, tags, handler, mapping_data); + } else if (c) { + caps = mxf_up_cdci_create_caps (track, c, tags, handler, mapping_data); } else { - GST_ERROR ("CDCI uncompressed picture essence not supported yet"); return NULL; } - if (caps) { - mxf_metadata_generic_picture_essence_descriptor_set_caps (p, caps); - } - return caps; } @@ -264,8 +346,241 @@ static const MXFEssenceElementHandler mxf_up_essence_element_handler = { mxf_up_create_caps }; +static GstFlowReturn +mxf_up_write_func (GstBuffer * buffer, GstCaps * caps, gpointer mapping_data, + GstAdapter * adapter, GstBuffer ** outbuf, gboolean flush) +{ + MXFUPMappingData *data = mapping_data; + + if (!buffer) + return GST_FLOW_OK; + + if (GST_BUFFER_SIZE (buffer) != + GST_ROUND_UP_4 (data->bpp * data->width) * data->height) { + GST_ERROR ("Invalid buffer size"); + return GST_FLOW_ERROR; + } + + if (data->bpp != 4 + || GST_ROUND_UP_4 (data->width * data->bpp) != data->width * data->bpp) { + guint y; + GstBuffer *ret; + guint8 *indata, *outdata; + + ret = gst_buffer_new_and_alloc (data->width * data->bpp * data->height); + indata = GST_BUFFER_DATA (buffer); + outdata = GST_BUFFER_DATA (ret); + + for (y = 0; y < data->height; y++) { + memcpy (outdata, indata, data->width * data->bpp); + indata += GST_ROUND_UP_4 (data->width * data->bpp); + outdata += data->width * data->bpp; + } + + gst_buffer_unref (buffer); + + *outbuf = ret; + } else { + *outbuf = buffer; + } + + return GST_FLOW_OK; +} + +static const guint8 up_essence_container_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, + 0x0D, 0x01, 0x03, 0x01, 0x02, 0x05, 0x7F, 0x01 +}; + +static MXFMetadataFileDescriptor * +mxf_up_get_rgba_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataRGBAPictureEssenceDescriptor *ret; + guint i; + GstCaps *tmp, *intersection; + MXFUPMappingData *md = g_new0 (MXFUPMappingData, 1); + + *mapping_data = md; + + ret = (MXFMetadataRGBAPictureEssenceDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_RGBA_PICTURE_ESSENCE_DESCRIPTOR); + + for (i = 0; i < G_N_ELEMENTS (_rgba_mapping_table); i++) { + tmp = gst_caps_from_string (_rgba_mapping_table[i].caps); + intersection = gst_caps_intersect (caps, tmp); + gst_caps_unref (tmp); + + if (!gst_caps_is_empty (intersection)) { + gst_caps_unref (intersection); + ret->n_pixel_layout = _rgba_mapping_table[i].n_pixel_layout; + ret->pixel_layout = g_new0 (guint8, ret->n_pixel_layout * 2); + md->fourcc = _rgba_mapping_table[i].fourcc; + md->bpp = _rgba_mapping_table[i].n_pixel_layout; + memcpy (ret->pixel_layout, _rgba_mapping_table[i].pixel_layout, + ret->n_pixel_layout * 2); + break; + } + gst_caps_unref (intersection); + } + + if (md->fourcc == 0) { + GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps); + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + + memcpy (&ret->parent.parent.essence_container, &up_essence_container_ul, 16); + + if (!mxf_metadata_generic_picture_essence_descriptor_from_caps (&ret->parent, + caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + md->width = ret->parent.stored_width; + md->height = ret->parent.stored_height; + + *handler = mxf_up_write_func; + + return (MXFMetadataFileDescriptor *) ret; +} + +static MXFMetadataFileDescriptor * +mxf_up_get_cdci_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataCDCIPictureEssenceDescriptor *ret; + guint i; + GstCaps *tmp, *intersection; + MXFUPMappingData *md = g_new0 (MXFUPMappingData, 1); + + *mapping_data = md; + + ret = (MXFMetadataCDCIPictureEssenceDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR); + + for (i = 0; i < G_N_ELEMENTS (_cdci_mapping_table); i++) { + tmp = gst_caps_from_string (_cdci_mapping_table[i].caps); + intersection = gst_caps_intersect (caps, tmp); + gst_caps_unref (tmp); + + if (!gst_caps_is_empty (intersection)) { + gst_caps_unref (intersection); + ret->horizontal_subsampling = + _cdci_mapping_table[i].horizontal_subsampling; + ret->vertical_subsampling = _cdci_mapping_table[i].vertical_subsampling; + ret->reversed_byte_order = _cdci_mapping_table[i].reversed_byte_order; + md->fourcc = _cdci_mapping_table[i].fourcc; + md->bpp = _cdci_mapping_table[i].bpp; + break; + } + gst_caps_unref (intersection); + } + + if (md->fourcc == 0) { + GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps); + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + memcpy (&ret->parent.parent.essence_container, &up_essence_container_ul, 16); + + if (!mxf_metadata_generic_picture_essence_descriptor_from_caps (&ret->parent, + caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + md->width = ret->parent.stored_width; + md->height = ret->parent.stored_height; + + *handler = mxf_up_write_func; + + return (MXFMetadataFileDescriptor *) ret; +} + +static MXFMetadataFileDescriptor * +mxf_up_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + GstStructure *s; + + s = gst_caps_get_structure (caps, 0); + if (strcmp (gst_structure_get_name (s), "video/x-raw-rgb") == 0) { + return mxf_up_get_rgba_descriptor (tmpl, caps, handler, mapping_data); + } else if (strcmp (gst_structure_get_name (s), "video/x-raw-yuv") == 0) { + guint32 fourcc; + + if (!gst_structure_get_fourcc (s, "format", &fourcc)) + return NULL; + + if (fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V') || + fourcc == GST_MAKE_FOURCC ('v', '3', '0', '8')) + return mxf_up_get_rgba_descriptor (tmpl, caps, handler, mapping_data); + + return mxf_up_get_cdci_descriptor (tmpl, caps, handler, mapping_data); + } + + g_assert_not_reached (); +} + +static void +mxf_up_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf) +{ + return; +} + +static void +mxf_up_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package, + MXFMetadataTimelineTrack * track, MXFFraction * edit_rate) +{ + edit_rate->n = a->sample_rate.n; + edit_rate->d = a->sample_rate.d; +} + +static guint32 +mxf_up_get_track_number_template (MXFMetadataFileDescriptor * a, + GstCaps * caps, gpointer mapping_data) +{ + return (0x15 << 24) | (0x02 << 8); +} + +static MXFEssenceElementWriter mxf_up_essence_element_writer = { + mxf_up_get_descriptor, + mxf_up_update_descriptor, + mxf_up_get_edit_rate, + mxf_up_get_track_number_template, + NULL, + {{0,}} +}; + void mxf_up_init (void) { mxf_essence_element_handler_register (&mxf_up_essence_element_handler); + + mxf_up_essence_element_writer.pad_template = + gst_pad_template_new ("up_video_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, + gst_caps_from_string (GST_VIDEO_CAPS_RGB "; " + GST_VIDEO_CAPS_BGR "; " + GST_VIDEO_CAPS_RGBx "; " + GST_VIDEO_CAPS_xRGB "; " + GST_VIDEO_CAPS_BGRx "; " + GST_VIDEO_CAPS_xBGR "; " + GST_VIDEO_CAPS_ARGB "; " + GST_VIDEO_CAPS_RGBA "; " + GST_VIDEO_CAPS_ABGR "; " + GST_VIDEO_CAPS_BGRA "; " + GST_VIDEO_CAPS_YUV ("AYUV") "; " + GST_VIDEO_CAPS_YUV ("v308") "; " + GST_VIDEO_CAPS_YUV ("UYVY") "; " GST_VIDEO_CAPS_YUV ("YUY2"))); + + memcpy (&mxf_up_essence_element_writer.data_definition, + mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_PICTURE_ESSENCE), + 16); + mxf_essence_element_writer_register (&mxf_up_essence_element_writer); } diff --git a/gst/mxf/mxfvc3.c b/gst/mxf/mxfvc3.c index e0f9b804..3e94f378 100644 --- a/gst/mxf/mxfvc3.c +++ b/gst/mxf/mxfvc3.c @@ -26,9 +26,11 @@ #endif #include <gst/gst.h> +#include <gst/video/video.h> #include <string.h> #include "mxfvc3.h" +#include "mxfwrite.h" GST_DEBUG_CATEGORY_EXTERN (mxf_debug); #define GST_CAT_DEFAULT mxf_debug @@ -154,8 +156,93 @@ static const MXFEssenceElementHandler mxf_vc3_essence_element_handler = { mxf_vc3_create_caps }; +static GstFlowReturn +mxf_vc3_write_func (GstBuffer * buffer, GstCaps * caps, gpointer mapping_data, + GstAdapter * adapter, GstBuffer ** outbuf, gboolean flush) +{ + *outbuf = buffer; + return GST_FLOW_OK; +} + +/* FIXME: In which version was this added? Byte 7, assuming version 10 */ +static const guint8 vc3_essence_container_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0A, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x11, 0x01, 0x00 +}; + +static MXFMetadataFileDescriptor * +mxf_vc3_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps, + MXFEssenceElementWriteFunc * handler, gpointer * mapping_data) +{ + MXFMetadataCDCIPictureEssenceDescriptor *ret; + GstStructure *s; + + s = gst_caps_get_structure (caps, 0); + if (strcmp (gst_structure_get_name (s), "video/x-dnxhd") != 0) { + GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps); + return NULL; + } + + ret = (MXFMetadataCDCIPictureEssenceDescriptor *) + gst_mini_object_new (MXF_TYPE_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR); + + memcpy (&ret->parent.parent.essence_container, &vc3_essence_container_ul, 16); + + if (!mxf_metadata_generic_picture_essence_descriptor_from_caps (&ret->parent, + caps)) { + gst_mini_object_unref (GST_MINI_OBJECT_CAST (ret)); + return NULL; + } + + *handler = mxf_vc3_write_func; + + return (MXFMetadataFileDescriptor *) ret; +} + +static void +mxf_vc3_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf) +{ + return; +} + +static void +mxf_vc3_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps, + gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package, + MXFMetadataTimelineTrack * track, MXFFraction * edit_rate) +{ + edit_rate->n = a->sample_rate.n; + edit_rate->d = a->sample_rate.d; +} + +static guint32 +mxf_vc3_get_track_number_template (MXFMetadataFileDescriptor * a, + GstCaps * caps, gpointer mapping_data) +{ + return (0x15 << 24) | (0x05 << 8); +} + +static MXFEssenceElementWriter mxf_vc3_essence_element_writer = { + mxf_vc3_get_descriptor, + mxf_vc3_update_descriptor, + mxf_vc3_get_edit_rate, + mxf_vc3_get_track_number_template, + NULL, + {{0,}} +}; + void mxf_vc3_init (void) { mxf_essence_element_handler_register (&mxf_vc3_essence_element_handler); + + mxf_vc3_essence_element_writer.pad_template = + gst_pad_template_new ("vc3_video_sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, + gst_caps_from_string ("video/x-dnxhd, width = " GST_VIDEO_SIZE_RANGE + ", height = " GST_VIDEO_SIZE_RANGE ", framerate = " + GST_VIDEO_FPS_RANGE)); + memcpy (&mxf_vc3_essence_element_writer.data_definition, + mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_PICTURE_ESSENCE), + 16); + mxf_essence_element_writer_register (&mxf_vc3_essence_element_writer); } diff --git a/gst/mxf/mxfwrite.c b/gst/mxf/mxfwrite.c new file mode 100644 index 00000000..c2c15bba --- /dev/null +++ b/gst/mxf/mxfwrite.c @@ -0,0 +1,567 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 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 "mxfwrite.h" + +GST_DEBUG_CATEGORY_EXTERN (mxf_debug); +#define GST_CAT_DEFAULT mxf_debug + +static GList *_essence_element_writer_registry = NULL; +static GPtrArray *_essence_element_writer_pad_templates = NULL; + +void +mxf_essence_element_writer_register (const MXFEssenceElementWriter * writer) +{ + _essence_element_writer_registry = + g_list_prepend (_essence_element_writer_registry, (gpointer) writer); + + if (!_essence_element_writer_pad_templates) + _essence_element_writer_pad_templates = g_ptr_array_new (); + + if (_essence_element_writer_pad_templates->len > 0 && + g_ptr_array_index (_essence_element_writer_pad_templates, + _essence_element_writer_pad_templates->len - 1) == NULL) + g_ptr_array_remove_index (_essence_element_writer_pad_templates, + _essence_element_writer_pad_templates->len - 1); + + g_ptr_array_add (_essence_element_writer_pad_templates, + (gpointer) writer->pad_template); +} + +const GstPadTemplate ** +mxf_essence_element_writer_get_pad_templates (void) +{ + if (!_essence_element_writer_pad_templates + || _essence_element_writer_pad_templates->len == 0) + return NULL; + + if (g_ptr_array_index (_essence_element_writer_pad_templates, + _essence_element_writer_pad_templates->len - 1)) + g_ptr_array_add (_essence_element_writer_pad_templates, NULL); + + return (const GstPadTemplate **) _essence_element_writer_pad_templates->pdata; +} + +const MXFEssenceElementWriter * +mxf_essence_element_writer_find (const GstPadTemplate * templ) +{ + GList *l = _essence_element_writer_registry; + + for (; l; l = l->next) { + MXFEssenceElementWriter *writer = l->data; + + if (writer->pad_template == templ) + return writer; + } + + return NULL; +} + +void +mxf_ul_set (MXFUL * ul, GHashTable * hashtable) +{ + guint i; + +next_try: + for (i = 0; i < 4; i++) + GST_WRITE_UINT32_BE (&ul->u[i * 4], g_random_int ()); + + if (hashtable && g_hash_table_lookup_extended (hashtable, ul, NULL, NULL)) + goto next_try; +} + +void +mxf_umid_set (MXFUMID * umid) +{ + guint i; + guint32 tmp; + + /* SMPTE S330M 5.1.1: + * UMID Identifier + */ + umid->u[0] = 0x06; + umid->u[1] = 0x0a; + umid->u[2] = 0x2b; + umid->u[3] = 0x34; + umid->u[4] = 0x01; + umid->u[5] = 0x01; + umid->u[6] = 0x01; + umid->u[7] = 0x05; /* version, see RP210 */ + umid->u[8] = 0x01; + umid->u[9] = 0x01; + umid->u[10] = 0x0d; /* mixed group of components in a single container */ + + /* - UUID/UL method for material number + * - 24 bit PRG for instance number + */ + umid->u[11] = 0x20 | 0x02; + + /* Length of remaining data */ + umid->u[12] = 0x13; + + /* Instance number */ + tmp = g_random_int (); + umid->u[13] = (tmp >> 24) & 0xff; + umid->u[14] = (tmp >> 16) & 0xff; + umid->u[15] = (tmp >> 8) & 0xff; + + /* Material number: ISO UUID Version 4 */ + for (i = 16; i < 32; i += 4) + GST_WRITE_UINT32_BE (&umid->u[i], g_random_int ()); + + umid->u[16 + 6] &= 0x0f; + umid->u[16 + 6] |= 0x40; + + umid->u[16 + 8] &= 0x3f; + umid->u[16 + 8] |= 0x80; +} + +void +mxf_timestamp_set_now (MXFTimestamp * timestamp) +{ + GTimeVal tv; + time_t t; + struct tm *tm; + +#ifdef HAVE_GMTIME_R + struct tm tm_; +#endif + + g_get_current_time (&tv); + t = (time_t) tv.tv_sec; + +#ifdef HAVE_GMTIME_R + tm = gmtime_r (&t, &tm_); +#else + tm = gmtime (&t); +#endif + + timestamp->year = tm->tm_year + 1900; + timestamp->month = tm->tm_mon; + timestamp->day = tm->tm_mday; + timestamp->hour = tm->tm_hour; + timestamp->minute = tm->tm_min; + timestamp->second = tm->tm_sec; + timestamp->msecond = tv.tv_usec / 1000; +} + +static guint8 mxf_op_identification_ul[] = { + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01 +}; + +void +mxf_op_set_atom (MXFUL * ul, gboolean single_sourceclip, + gboolean single_essence_track) +{ + memcpy (&ul->u, &mxf_op_identification_ul, 12); + ul->u[12] = 0x10; + ul->u[13] = 0; + + if (!single_sourceclip) + ul->u[13] |= 0x80; + + if (!single_essence_track) + ul->u[13] |= 0x40; + + ul->u[14] = 0; + ul->u[15] = 0; +} + +void +mxf_op_set_generalized (MXFUL * ul, MXFOperationalPattern pattern, + gboolean internal_essence, gboolean streamable, gboolean single_track) +{ + g_return_if_fail (pattern >= MXF_OP_1a); + + memcpy (&ul->u, &mxf_op_identification_ul, 12); + + if (pattern == MXF_OP_1a || pattern == MXF_OP_1b || pattern == MXF_OP_1c) + ul->u[12] = 0x01; + else if (pattern == MXF_OP_2a || pattern == MXF_OP_2b || pattern == MXF_OP_2c) + ul->u[12] = 0x02; + else if (pattern == MXF_OP_3a || pattern == MXF_OP_3b || pattern == MXF_OP_3c) + ul->u[12] = 0x03; + + if (pattern == MXF_OP_1a || pattern == MXF_OP_2a || pattern == MXF_OP_3a) + ul->u[13] = 0x01; + else if (pattern == MXF_OP_1b || pattern == MXF_OP_2b || pattern == MXF_OP_3b) + ul->u[13] = 0x02; + else if (pattern == MXF_OP_1c || pattern == MXF_OP_2c || pattern == MXF_OP_3c) + ul->u[13] = 0x02; + + ul->u[14] = 0x80; + if (!internal_essence) + ul->u[14] |= 0x40; + if (!streamable) + ul->u[14] |= 0x20; + if (!single_track) + ul->u[14] |= 0x10; + + ul->u[15] = 0; +} + +static void +_mxf_mapping_ul_free (MXFUL * ul) +{ + g_slice_free (MXFUL, ul); +} + +guint16 +mxf_primer_pack_add_mapping (MXFPrimerPack * primer, guint16 local_tag, + const MXFUL * ul) +{ + MXFUL *uid; +#ifndef GST_DISABLE_GST_DEBUG + gchar str[48]; +#endif + + if (primer->mappings == NULL) { + primer->mappings = g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, (GDestroyNotify) _mxf_mapping_ul_free); + } + + if (primer->reverse_mappings == NULL) { + primer->reverse_mappings = g_hash_table_new_full ((GHashFunc) mxf_ul_hash, + (GEqualFunc) mxf_ul_is_equal, (GDestroyNotify) _mxf_mapping_ul_free, + (GDestroyNotify) NULL); + } + + if (primer->next_free_tag == 0xffff && local_tag == 0) { + GST_ERROR ("Used too many dynamic tags"); + return 0; + } + + if (local_tag == 0) { + guint16 tmp; + + tmp = GPOINTER_TO_UINT (g_hash_table_lookup (primer->reverse_mappings, ul)); + if (tmp == 0) { + local_tag = primer->next_free_tag; + primer->next_free_tag++; + } + } else { + if (g_hash_table_lookup (primer->mappings, GUINT_TO_POINTER (local_tag))) + return local_tag; + } + + g_assert (local_tag != 0); + + uid = g_slice_new (MXFUL); + memcpy (uid, ul, 16); + + GST_DEBUG ("Adding mapping = 0x%04x -> %s", local_tag, + mxf_ul_to_string (uid, str)); + g_hash_table_insert (primer->mappings, GUINT_TO_POINTER (local_tag), uid); + uid = g_slice_dup (MXFUL, uid); + g_hash_table_insert (primer->reverse_mappings, uid, + GUINT_TO_POINTER (local_tag)); + + return local_tag; +} + +guint +mxf_ber_encode_size (guint size, guint8 ber[9]) +{ + guint8 slen, i; + guint8 tmp[8]; + + memset (ber, 0, 9); + + if (size <= 127) { + ber[0] = size; + return 1; + } else if (size > G_MAXUINT) { + return 0; + } + + slen = 0; + while (size > 0) { + tmp[slen] = size & 0xff; + size >>= 8; + slen++; + } + + ber[0] = 0x80 | slen; + for (i = 0; i < slen; i++) { + ber[i + 1] = tmp[slen - i - 1]; + } + + return slen + 1; +} + +void +mxf_timestamp_write (const MXFTimestamp * timestamp, guint8 * data) +{ + GST_WRITE_UINT16_BE (data, timestamp->year); + GST_WRITE_UINT8 (data + 2, timestamp->month); + GST_WRITE_UINT8 (data + 3, timestamp->day); + GST_WRITE_UINT8 (data + 4, timestamp->hour); + GST_WRITE_UINT8 (data + 5, timestamp->minute); + GST_WRITE_UINT8 (data + 6, timestamp->second); + GST_WRITE_UINT8 (data + 7, (timestamp->msecond * 256) / 1000); +} + +guint8 * +mxf_utf8_to_utf16 (const gchar * str, guint16 * size) +{ + guint8 *ret; + GError *error = NULL; + gsize s; + + g_return_val_if_fail (size != NULL, NULL); + + if (str == NULL) { + *size = 0; + return NULL; + } + + ret = (guint8 *) + g_convert_with_fallback (str, -1, "UTF-16BE", "UTF-8", "*", NULL, &s, + &error); + + if (ret == NULL) { + GST_WARNING ("UTF-16-BE to UTF-8 conversion failed: %s", error->message); + g_error_free (error); + *size = 0; + return NULL; + } + + *size = s; + return (guint8 *) ret; +} + +void +mxf_product_version_write (const MXFProductVersion * version, guint8 * data) +{ + GST_WRITE_UINT16_BE (data, version->major); + GST_WRITE_UINT16_BE (data + 2, version->minor); + GST_WRITE_UINT16_BE (data + 4, version->patch); + GST_WRITE_UINT16_BE (data + 6, version->build); + GST_WRITE_UINT16_BE (data + 8, version->release); +} + +GstBuffer * +mxf_partition_pack_to_buffer (const MXFPartitionPack * pack) +{ + static const guint8 partition_pack_ul[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01 + }; + guint slen; + guint8 ber[9]; + GstBuffer *ret; + guint8 *data; + guint i; + guint size = + 8 + 16 * pack->n_essence_containers + 16 + 4 + 8 + 4 + 8 + 8 + 8 + 8 + 8 + + 4 + 2 + 2; + + slen = mxf_ber_encode_size (size, ber); + + ret = gst_buffer_new_and_alloc (16 + slen + size); + memcpy (GST_BUFFER_DATA (ret), &partition_pack_ul, 13); + if (pack->type == MXF_PARTITION_PACK_HEADER) + GST_BUFFER_DATA (ret)[13] = 0x02; + else if (pack->type == MXF_PARTITION_PACK_BODY) + GST_BUFFER_DATA (ret)[13] = 0x03; + else if (pack->type == MXF_PARTITION_PACK_FOOTER) + GST_BUFFER_DATA (ret)[13] = 0x04; + GST_BUFFER_DATA (ret)[14] = 0; + if (pack->complete) + GST_BUFFER_DATA (ret)[14] |= 0x02; + if (pack->closed) + GST_BUFFER_DATA (ret)[14] |= 0x01; + GST_BUFFER_DATA (ret)[14] += 1; + GST_BUFFER_DATA (ret)[15] = 0; + memcpy (GST_BUFFER_DATA (ret) + 16, &ber, slen); + + data = GST_BUFFER_DATA (ret) + 16 + slen; + + GST_WRITE_UINT16_BE (data, pack->major_version); + GST_WRITE_UINT16_BE (data + 2, pack->minor_version); + data += 4; + + GST_WRITE_UINT32_BE (data, pack->kag_size); + data += 4; + + GST_WRITE_UINT64_BE (data, pack->this_partition); + data += 8; + + GST_WRITE_UINT64_BE (data, pack->prev_partition); + data += 8; + + GST_WRITE_UINT64_BE (data, pack->footer_partition); + data += 8; + + GST_WRITE_UINT64_BE (data, pack->header_byte_count); + data += 8; + + GST_WRITE_UINT64_BE (data, pack->index_byte_count); + data += 8; + + GST_WRITE_UINT32_BE (data, pack->index_sid); + data += 4; + + GST_WRITE_UINT64_BE (data, pack->body_offset); + data += 8; + + GST_WRITE_UINT32_BE (data, pack->body_sid); + data += 4; + + memcpy (data, &pack->operational_pattern, 16); + data += 16; + + GST_WRITE_UINT32_BE (data, pack->n_essence_containers); + GST_WRITE_UINT32_BE (data + 4, 16); + data += 8; + + for (i = 0; i < pack->n_essence_containers; i++) + memcpy (data + 16 * i, &pack->essence_containers[i], 16); + + return ret; +} + +GstBuffer * +mxf_primer_pack_to_buffer (const MXFPrimerPack * pack) +{ + static const guint8 primer_pack_ul[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, + 0x01, 0x05, 0x01, 0x00 + }; + guint slen; + guint8 ber[9]; + GstBuffer *ret; + guint n; + guint8 *data; + + if (pack->mappings) + n = g_hash_table_size (pack->mappings); + else + n = 0; + + slen = mxf_ber_encode_size (8 + 18 * n, ber); + + ret = gst_buffer_new_and_alloc (16 + slen + 8 + 18 * n); + memcpy (GST_BUFFER_DATA (ret), &primer_pack_ul, 16); + memcpy (GST_BUFFER_DATA (ret) + 16, &ber, slen); + + data = GST_BUFFER_DATA (ret) + 16 + slen; + + GST_WRITE_UINT32_BE (data, n); + GST_WRITE_UINT32_BE (data + 4, 18); + data += 8; + + if (pack->mappings) { + guint16 local_tag; + MXFUL *ul; +#if GLIB_CHECK_VERSION (2, 16, 0) + GHashTableIter iter; + + g_hash_table_iter_init (&iter, pack->mappings); +#else + GList *l, *values; + + keys = g_hash_table_get_keys (pack->mappings); +#endif + +#if GLIB_CHECK_VERSION (2, 16, 0) + while (g_hash_table_iter_next (&iter, (gpointer) & local_tag, + (gpointer) & ul)) { +#else + for (l = keys l; l = l->next) { + local_tag = GPOINTER_TO_GUINT (l->data); + ul = g_hash_table_lookup (pack->mappings, GUINT_TO_POINTER (local_tag)); +#endif + GST_WRITE_UINT16_BE (data, local_tag); + memcpy (data + 2, ul, 16); + data += 18; + } + +#if !GLIB_CHECK_VERSION (2, 16, 0) + g_list_free (keys); +#endif + } + + return ret; +} + +GstBuffer * +mxf_fill_new (guint size) +{ + static const guint8 fill_ul[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x03, 0x01, 0x02, 0x10, 0x01, 0x00, 0x00, 0x00 + }; + GstBuffer *ret; + guint slen; + guint8 ber[9]; + + slen = mxf_ber_encode_size (size, ber); + + ret = gst_buffer_new_and_alloc (16 + slen + size); + memcpy (GST_BUFFER_DATA (ret), &fill_ul, 16); + memcpy (GST_BUFFER_DATA (ret) + 16, &ber, slen); + memset (GST_BUFFER_DATA (ret) + slen, 0, size); + + return ret; +} + +static const guint8 random_index_pack_ul[] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x11, 0x01, 0x00 +}; + +GstBuffer * +mxf_random_index_pack_to_buffer (const GArray * array) +{ + MXFRandomIndexPackEntry *entry; + guint i; + GstBuffer *ret; + guint8 slen, ber[9]; + guint size; + guint8 *data; + + if (array->len == 0) + return NULL; + + size = array->len * 12 + 4; + slen = mxf_ber_encode_size (size, ber); + ret = gst_buffer_new_and_alloc (16 + slen + size); + memcpy (GST_BUFFER_DATA (ret), random_index_pack_ul, 16); + memcpy (GST_BUFFER_DATA (ret) + 16, ber, slen); + + data = GST_BUFFER_DATA (ret) + 16 + slen; + + for (i = 0; i < array->len; i++) { + entry = &g_array_index (array, MXFRandomIndexPackEntry, i); + GST_WRITE_UINT32_BE (data, entry->body_sid); + GST_WRITE_UINT64_BE (data + 4, entry->offset); + data += 12; + } + GST_WRITE_UINT32_BE (data, GST_BUFFER_SIZE (ret)); + + return ret; +} diff --git a/gst/mxf/mxfwrite.h b/gst/mxf/mxfwrite.h new file mode 100644 index 00000000..4ebca925 --- /dev/null +++ b/gst/mxf/mxfwrite.h @@ -0,0 +1,85 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* Handling of the basic MXF types */ + +#ifndef __MXF_WRITE_H__ +#define __MXF_WRITE_H__ + +#include <string.h> +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +#include "mxfmetadata.h" +#include "mxftypes.h" +#include "mxfparse.h" + +typedef GstFlowReturn (*MXFEssenceElementWriteFunc) (GstBuffer *buffer, GstCaps *caps, gpointer mapping_data, GstAdapter *adapter, GstBuffer **outbuf, gboolean flush); + +typedef struct { + MXFMetadataFileDescriptor * (*get_descriptor) (GstPadTemplate *tmpl, GstCaps *caps, MXFEssenceElementWriteFunc *handler, gpointer *mapping_data); + void (*update_descriptor) (MXFMetadataFileDescriptor *d, GstCaps *caps, gpointer mapping_data, GstBuffer *buf); + void (*get_edit_rate) (MXFMetadataFileDescriptor *a, GstCaps *caps, gpointer mapping_data, GstBuffer *buf, MXFMetadataSourcePackage *package, MXFMetadataTimelineTrack *track, MXFFraction *edit_rate); + guint32 (*get_track_number_template) (MXFMetadataFileDescriptor *a, GstCaps *caps, gpointer mapping_data); + const GstPadTemplate *pad_template; + MXFUL data_definition; +} MXFEssenceElementWriter; + +typedef enum { + MXF_OP_UNKNOWN = 0, + MXF_OP_ATOM, + MXF_OP_1a, + MXF_OP_1b, + MXF_OP_1c, + MXF_OP_2a, + MXF_OP_2b, + MXF_OP_2c, + MXF_OP_3a, + MXF_OP_3b, + MXF_OP_3c, +} MXFOperationalPattern; + +void mxf_essence_element_writer_register (const MXFEssenceElementWriter *writer); +const GstPadTemplate ** mxf_essence_element_writer_get_pad_templates (void); +const MXFEssenceElementWriter *mxf_essence_element_writer_find (const GstPadTemplate *templ); + +void mxf_ul_set (MXFUL *ul, GHashTable *hashtable); +void mxf_umid_set (MXFUMID *umid); + +void mxf_timestamp_set_now (MXFTimestamp *timestamp); +void mxf_timestamp_write (const MXFTimestamp *timestamp, guint8 *data); + +void mxf_op_set_atom (MXFUL *ul, gboolean single_sourceclip, gboolean single_essence_track); +void mxf_op_set_generalized (MXFUL *ul, MXFOperationalPattern pattern, gboolean internal_essence, gboolean streamable, gboolean single_track); + +guint16 mxf_primer_pack_add_mapping (MXFPrimerPack *primer, guint16 local_tag, const MXFUL *ul); + +guint mxf_ber_encode_size (guint size, guint8 ber[9]); + +guint8 * mxf_utf8_to_utf16 (const gchar *str, guint16 *size); + +void mxf_product_version_write (const MXFProductVersion *version, guint8 *data); + +GstBuffer * mxf_partition_pack_to_buffer (const MXFPartitionPack *pack); +GstBuffer * mxf_primer_pack_to_buffer (const MXFPrimerPack *pack); +GstBuffer * mxf_fill_new (guint size); + +GstBuffer * mxf_random_index_pack_to_buffer (const GArray *array); + +#endif /* __MXF_WRITE_H__ */ |