diff mxfenc.c @ 4448:587ce9359a9b libavformat

write timecode track
author bcoudurier
date Wed, 11 Feb 2009 07:18:00 +0000
parents efe78f986bec
children aaae79b414f3
line wrap: on
line diff
--- a/mxfenc.c	Wed Feb 11 02:32:39 2009 +0000
+++ b/mxfenc.c	Wed Feb 11 07:18:00 2009 +0000
@@ -63,7 +63,6 @@
     UID track_essence_element_key;
     int index;            ///< index in mxf_essence_container_uls table
     const UID *codec_ul;
-    int64_t duration;
     int order;            ///< interleaving order if dts are equal
     int interlaced;       ///< wether picture is interlaced
     int temporal_reordering;
@@ -124,6 +123,11 @@
     uint64_t *body_partition_offset;
     unsigned body_partitions_count;
     int last_key_index;  ///< index of last key frame
+    uint64_t duration;
+    AVStream *timecode_track;
+    int timecode_base;       ///< rounded time code base (25 or 30)
+    int timecode_start;      ///< value from mpeg-2 essence gop header
+    int timecode_drop_frame; ///< time code use drop frame method frop mpeg-2 essence gop header
 } MXFContext;
 
 static const uint8_t uuid_base[]            = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd };
@@ -194,6 +198,10 @@
     { 0x1201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x03,0x01,0x04,0x00,0x00}}, /* Start position */
     { 0x1101, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x03,0x01,0x00,0x00,0x00}}, /* SourcePackageID */
     { 0x1102, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x03,0x02,0x00,0x00,0x00}}, /* SourceTrackID */
+    // Timecode Component
+    { 0x1501, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x03,0x01,0x05,0x00,0x00}}, /* Start Time Code */
+    { 0x1502, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x04,0x01,0x01,0x02,0x06,0x00,0x00}}, /* Rounded Time Code Base */
+    { 0x1503, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x04,0x01,0x01,0x05,0x00,0x00,0x00}}, /* Drop Frame */
     // File Descriptor
     { 0x3F01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x06,0x01,0x01,0x04,0x06,0x0B,0x00,0x00}}, /* Sub Descriptors reference array */
     { 0x3006, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x06,0x01,0x01,0x03,0x05,0x00,0x00,0x00}}, /* Linked Track ID */
@@ -522,23 +530,32 @@
     mxf_write_uuid(pb, type == MaterialPackage ? Sequence: Sequence + TypeBottom, st->index);
 }
 
-static void mxf_write_common_fields(ByteIOContext *pb, AVStream *st)
+static const uint8_t smpte_12m_timecode_track_data_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x01,0x01,0x00,0x00,0x00 };
+
+static void mxf_write_common_fields(AVFormatContext *s, AVStream *st)
 {
-    const MXFCodecUL *data_def_ul = mxf_get_data_definition_ul(st->codec->codec_type);
-    MXFStreamContext *sc = st->priv_data;
+    MXFContext *mxf = s->priv_data;
+    ByteIOContext *pb = s->pb;
 
     // find data define uls
     mxf_write_local_tag(pb, 16, 0x0201);
-    put_buffer(pb, data_def_ul->uid, 16);
+    if (st == mxf->timecode_track)
+        put_buffer(pb, smpte_12m_timecode_track_data_ul, 16);
+    else {
+        const MXFCodecUL *data_def_ul = mxf_get_data_definition_ul(st->codec->codec_type);
+        put_buffer(pb, data_def_ul->uid, 16);
+    }
 
     // write duration
     mxf_write_local_tag(pb, 8, 0x0202);
-    put_be64(pb, sc->duration);
+    put_be64(pb, mxf->duration);
 }
 
 static void mxf_write_sequence(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type)
 {
+    MXFContext *mxf = s->priv_data;
     ByteIOContext *pb = s->pb;
+    enum MXFMetadataSetType component;
 
     mxf_write_metadata_key(pb, 0x010f00);
     PRINT_KEY(s, "sequence key", pb->buf_ptr - 16);
@@ -548,12 +565,45 @@
     mxf_write_uuid(pb, type == MaterialPackage ? Sequence: Sequence + TypeBottom, st->index);
 
     PRINT_KEY(s, "sequence uid", pb->buf_ptr - 16);
-    mxf_write_common_fields(pb, st);
+    mxf_write_common_fields(s, st);
 
     // write structural component
     mxf_write_local_tag(pb, 16 + 8, 0x1001);
     mxf_write_refs_count(pb, 1);
-    mxf_write_uuid(pb, type == MaterialPackage ? SourceClip: SourceClip + TypeBottom, st->index);
+    if (st == mxf->timecode_track)
+        component = TimecodeComponent;
+    else if (type == MaterialPackage)
+        component = SourceClip;
+    else
+        component = SourceClip+TypeBottom;
+    mxf_write_uuid(pb, component, st->index);
+}
+
+static void mxf_write_timecode_component(AVFormatContext *s, AVStream *st)
+{
+    MXFContext *mxf = s->priv_data;
+    ByteIOContext *pb = s->pb;
+
+    mxf_write_metadata_key(pb, 0x011400);
+    klv_encode_ber_length(pb, 75);
+
+    // UID
+    mxf_write_local_tag(pb, 16, 0x3C0A);
+    mxf_write_uuid(pb, TimecodeComponent, st->index);
+
+    mxf_write_common_fields(s, st);
+
+    // Start Time Code
+    mxf_write_local_tag(pb, 8, 0x1501);
+    put_be64(pb, mxf->timecode_start);
+
+    // Rounded Time Code Base
+    mxf_write_local_tag(pb, 2, 0x1502);
+    put_be16(pb, mxf->timecode_base);
+
+    // Drop Frame
+    mxf_write_local_tag(pb, 1, 0x1503);
+    put_byte(pb, mxf->timecode_drop_frame);
 }
 
 static void mxf_write_structural_component(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type)
@@ -570,7 +620,7 @@
     mxf_write_uuid(pb, type == MaterialPackage ? SourceClip: SourceClip + TypeBottom, st->index);
 
     PRINT_KEY(s, "structural component uid", pb->buf_ptr - 16);
-    mxf_write_common_fields(pb, st);
+    mxf_write_common_fields(s, st);
 
     // write start_position
     mxf_write_local_tag(pb, 8, 0x1201);
@@ -773,16 +823,18 @@
 {
     MXFContext *mxf = s->priv_data;
     ByteIOContext *pb = s->pb;
-    int i;
+    int i, track_count;
 
     if (type == MaterialPackage) {
+        track_count = s->nb_streams + 1; // add timecode track
         mxf_write_metadata_key(pb, 0x013600);
         PRINT_KEY(s, "Material Package key", pb->buf_ptr - 16);
-        klv_encode_ber_length(pb, 92 + 16 * s->nb_streams);
+        klv_encode_ber_length(pb, 92 + 16*track_count);
     } else {
+        track_count = s->nb_streams;
         mxf_write_metadata_key(pb, 0x013700);
         PRINT_KEY(s, "Source Package key", pb->buf_ptr - 16);
-        klv_encode_ber_length(pb, 112 + 16 * s->nb_streams); // 20 bytes length for descriptor reference
+        klv_encode_ber_length(pb, 112 + 16*track_count); // 20 bytes length for descriptor reference
     }
 
     // write uid
@@ -805,10 +857,12 @@
     put_be64(pb, mxf->timestamp);
 
     // write track refs
-    mxf_write_local_tag(pb, s->nb_streams * 16 + 8, 0x4403);
-    mxf_write_refs_count(pb, s->nb_streams);
+    mxf_write_local_tag(pb, track_count*16 + 8, 0x4403);
+    mxf_write_refs_count(pb, track_count);
     for (i = 0; i < s->nb_streams; i++)
         mxf_write_uuid(pb, type == MaterialPackage ? Track : Track + TypeBottom, i);
+    if (type == MaterialPackage)
+        mxf_write_uuid(pb, Track, s->nb_streams); // timecode track
 
     // write multiple descriptor reference
     if (type == SourcePackage) {
@@ -818,6 +872,11 @@
             mxf_write_multi_descriptor(s);
         } else
             mxf_write_uuid(pb, SubDescriptor, 0);
+    } else {
+        // write timecode track
+        mxf_write_track(s, mxf->timecode_track, type);
+        mxf_write_sequence(s, mxf->timecode_track, type);
+        mxf_write_timecode_component(s, mxf->timecode_track);
     }
 
     for (i = 0; i < s->nb_streams; i++) {
@@ -1220,9 +1279,11 @@
             if (fabs(av_q2d(st->codec->time_base) - 1/25.0) < 0.0001) {
                 samples_per_frame = PAL_samples_per_frame;
                 mxf->time_base = (AVRational){ 1, 25 };
+                mxf->timecode_base = 25;
             } else if (fabs(av_q2d(st->codec->time_base) - 1001/30000.0) < 0.0001) {
                 samples_per_frame = NTSC_samples_per_frame;
                 mxf->time_base = (AVRational){ 1001, 30000 };
+                mxf->timecode_base = 30;
             } else {
                 av_log(s, AV_LOG_ERROR, "unsupported video frame rate\n");
                 return -1;
@@ -1236,7 +1297,6 @@
             av_set_pts_info(st, 64, 1, st->codec->sample_rate);
             mxf->slice_count = 1;
         }
-        sc->duration = -1;
 
         sc->index = mxf_get_essence_container_ul_index(st->codec->codec_id);
         if (sc->index == -1) {
@@ -1265,6 +1325,15 @@
     }
 
     mxf->timestamp = mxf_parse_timestamp(s->timestamp);
+    mxf->duration = -1;
+
+    mxf->timecode_track = av_mallocz(sizeof(*mxf->timecode_track));
+    if (!mxf->timecode_track)
+        return AVERROR(ENOMEM);
+    mxf->timecode_track->priv_data = av_mallocz(sizeof(MXFStreamContext));
+    if (!mxf->timecode_track->priv_data)
+        return AVERROR(ENOMEM);
+    mxf->timecode_track->index = s->nb_streams;
 
     if (!samples_per_frame)
         samples_per_frame = PAL_samples_per_frame;
@@ -1300,7 +1369,7 @@
 {
     MXFContext *mxf = s->priv_data;
     ByteIOContext *pb = s->pb;
-    unsigned fps, frame;
+    unsigned frame;
     uint32_t time_code;
 
     frame = mxf->last_indexed_edit_unit + mxf->edit_units_count;
@@ -1321,12 +1390,8 @@
     put_be64(pb, 0);
     put_be64(pb, 0); // creation date/time stamp
 
-    // XXX drop frame
-    if (mxf->time_base.den == 30000) fps = 30;
-    else                             fps = 25;
-
     put_byte(pb, 0x81); // SMPTE 12M time code
-    time_code = ff_framenum_to_12m_time_code(frame, 0, fps);
+    time_code = ff_framenum_to_12m_time_code(frame, mxf->timecode_drop_frame, mxf->timecode_base);
     put_be32(pb, time_code);
     put_be32(pb, 0); // binary group data
     put_be64(pb, 0);
@@ -1396,8 +1461,6 @@
     klv_encode_ber_length(pb, pkt->size); // write length
     put_buffer(pb, pkt->data, pkt->size); // write value
 
-    sc->duration = FFMAX(pkt->pts + pkt->duration, sc->duration);
-
     put_flush_packet(pb);
     return 0;
 }
@@ -1431,6 +1494,8 @@
     MXFContext *mxf = s->priv_data;
     ByteIOContext *pb = s->pb;
 
+    mxf->duration = mxf->last_indexed_edit_unit + mxf->edit_units_count;
+
     mxf_write_klv_fill(s);
     mxf->footer_partition_offset = url_ftell(pb);
     mxf_write_partition(s, 0, 2, footer_partition_key, 0);
@@ -1450,6 +1515,8 @@
 
     av_freep(&mxf->index_entries);
     av_freep(&mxf->body_partition_offset);
+    av_freep(&mxf->timecode_track->priv_data);
+    av_freep(&mxf->timecode_track);
 
     mxf_free(s);
     return 0;