[FFmpeg-devel] [PATCH 3/3] mov: support timecode extraction.

Clément Bœsch ubitux at gmail.com
Mon Jan 2 17:23:23 CET 2012


From: Clément Bœsch <clement.boesch at smartjog.com>

---
I'm really not sure about this one. All the samples I have store the timecode
in "frame number" format. But all of them have the Counter flag unset.
Unfortunately, the specs[1] stats (p160+):

    If the Counter flag is set to 1 in the timecode sample description, the
    sample data is an unsigned 32-bit integer. The timecode counter value is
    determined by dividing this unsigned 32-bit integer by the number of frames
    field in the timecode sample description.

    If the Counter flag is set to 0 in the timecode sample description, the
    sample data format is a signed 32-bit integer and is used to calculate a
    timecode record, defined as follows.

    [...]

So basically, I understand it as: cf=1 → frame number format, cf=0 → qt format.

Also, I can't find a sample with a timecode stored in quicktime format (where
counter flag should be set to 0).

The current hack just fallbacks on the frame number if the timecode in qt
format doesn't look correct, but this is not reliable.

What did I missed?

And if this is actually correct, should I assume frame number all the time? I
don't even know if some videos with timecode in QT format actually exist since
I don't have any...

[1]: http://developer.apple.com/standards/classicquicktime.html
---
 Changelog          |    2 +-
 libavformat/isom.h |    1 +
 libavformat/mov.c  |   80 ++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 77 insertions(+), 6 deletions(-)

diff --git a/Changelog b/Changelog
index 7b5d277..2dcc423 100644
--- a/Changelog
+++ b/Changelog
@@ -6,7 +6,7 @@ version next:
 - v410 Quicktime Uncompressed 4:4:4 10-bit encoder and decoder
 - SBaGen (SBG) binaural beats script demuxer
 - OpenMG Audio muxer
-- dv: add timecode to metadata
+- Timecode extraction in DV and MOV
 - thumbnail video filter
 - XML output in ffprobe
 - asplit audio filter
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 8f92cae..52ce585 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -127,6 +127,7 @@ typedef struct MOVStreamContext {
     int dts_shift;        ///< dts shift when ctts is negative
     uint32_t palette[256];
     int has_palette;
+    uint32_t tmcd_flags;  ///< tmcd track flags
 } MOVStreamContext;
 
 typedef struct MOVContext {
diff --git a/libavformat/mov.c b/libavformat/mov.c
index ee67a9f..99bf534 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -36,6 +36,7 @@
 #include "riff.h"
 #include "isom.h"
 #include "libavcodec/get_bits.h"
+#include "libavcodec/timecode.h"
 #include "id3v1.h"
 #include "mov_chan.h"
 
@@ -1367,10 +1368,10 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
             st->codec->height = sc->height;
         } else {
             if (st->codec->codec_tag == MKTAG('t','m','c','d')) {
-                int val;
+                MOVStreamContext *sc = st->priv_data;
                 avio_rb32(pb);       /* reserved */
-                val = avio_rb32(pb); /* flags */
-                if (val & 1)
+                sc->tmcd_flags = avio_rb32(pb); /* flags */
+                if (sc->tmcd_flags & 1)
                     st->codec->flags2 |= CODEC_FLAG2_DROP_FRAME_TIMECODE;
                 avio_rb32(pb); /* time scale */
                 avio_rb32(pb); /* frame duration */
@@ -2615,6 +2616,69 @@ finish:
     avio_seek(sc->pb, cur_pos, SEEK_SET);
 }
 
+static int parse_timecode_in_mov_format(AVFormatContext *s, AVStream *st,
+                                        int32_t value)
+{
+    char buf[16];
+    int hh = value>>24 & 0xff;
+    int nn = value>>23 & 0x01;
+    int mm = value>>16 & 0x7f;
+    int ss = value>> 8 & 0xff;
+    int ff = value     & 0xff;
+    int drop = st->codec->flags2 & CODEC_FLAG2_DROP_FRAME_TIMECODE;
+
+    if (mm >= 60 || ss >= 60 || ff >= 30) {
+        av_log(s, AV_LOG_WARNING,
+               "Timecode seems to be set in frame number format "
+               "but counter flag is 0.\n");
+        return -1;
+    }
+    snprintf(buf, sizeof(buf), "%s%02d:%02d:%02d%c%02d\n",
+             nn ? "-" : "", hh, mm, ss, drop ? ';' : ':', ff);
+    av_dict_set(&st->metadata, "timecode", buf, 0);
+    return 0;
+}
+
+static int parse_timecode_in_framenum_format(AVFormatContext *s, AVStream *st,
+                                             uint32_t value)
+{
+    char buf[16];
+    struct ff_timecode tc = {
+        .start = value,
+        .drop  = st->codec->flags2 & CODEC_FLAG2_DROP_FRAME_TIMECODE,
+        .rate  = (AVRational){st->codec->time_base.den,
+                              st->codec->time_base.num},
+    };
+
+    if (avpriv_check_timecode_rate(s, tc.rate, tc.drop) < 0)
+        return AVERROR(EINVAL);
+    av_dict_set(&st->metadata, "timecode",
+                avpriv_timecode_to_string(buf, &tc, 0), 0);
+    return 0;
+}
+
+static int mov_read_timecode_track(AVFormatContext *s, AVStream *st)
+{
+    MOVStreamContext *sc = st->priv_data;
+    int64_t cur_pos = avio_tell(sc->pb);
+    uint32_t value;
+
+    if (!st->nb_index_entries)
+        return -1;
+
+    avio_seek(sc->pb, st->index_entries->pos, SEEK_SET);
+    value = avio_rb32(s->pb);
+
+    av_log(s, AV_LOG_DEBUG, "timecode track: flags=%08X\n", sc->tmcd_flags);
+    if (sc->tmcd_flags & 1<<3) // check Counter flag
+        parse_timecode_in_framenum_format(s, st, value);
+    else if (parse_timecode_in_mov_format(s, st, value) < 0)
+        parse_timecode_in_framenum_format(s, st, value);
+
+    avio_seek(sc->pb, cur_pos, SEEK_SET);
+    return 0;
+}
+
 static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap)
 {
     MOVContext *mov = s->priv_data;
@@ -2640,8 +2704,14 @@ static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap)
     }
     av_dlog(mov->fc, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb));
 
-    if (pb->seekable && mov->chapter_track > 0)
-        mov_read_chapters(s);
+    if (pb->seekable) {
+        int i;
+        if (mov->chapter_track > 0)
+            mov_read_chapters(s);
+        for (i = 0; i < s->nb_streams; i++)
+            if (s->streams[i]->codec->codec_tag == MKTAG('t','m','c','d'))
+                mov_read_timecode_track(s, s->streams[i]);
+    }
 
     return 0;
 }
-- 
1.7.7.3



More information about the ffmpeg-devel mailing list