dv: add timecode to metadata
authorMatthieu Bouron <matthieu.bouron@gmail.com>
Thu, 22 Dec 2011 21:57:02 +0000 (22:57 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Mon, 26 Dec 2011 04:46:29 +0000 (05:46 +0100)
Reviewed-by: Reimar
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Changelog
libavformat/dv.c

index b4e7b58..1ececce 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -7,6 +7,7 @@ version next:
 - SBaGen (SBG) binaural beats script demuxer
 - OpenMG Audio muxer
 - SMJPEG demuxer
+- dv: add timecode to metadata
 
 
 version 0.9:
index e571f39..69c0de9 100644 (file)
@@ -87,6 +87,9 @@ static const uint8_t* dv_extract_pack(uint8_t* frame, enum dv_pack_type t)
     case dv_video_control:
         offs = (80*5 + 48 + 5);
         break;
+    case dv_timecode:
+        offs = (80*1 + 3 + 3);
+        break;
     default:
         return NULL;
     }
@@ -267,6 +270,45 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t* frame)
     return size;
 }
 
+static int bcd2int(uint8_t bcd)
+{
+   int low  = bcd & 0xf;
+   int high = bcd >> 4;
+   if (low > 9 || high > 9)
+       return -1;
+   return low + 10*high;
+}
+
+static int dv_extract_timecode(DVDemuxContext* c, uint8_t* frame, char tc[32])
+{
+    int hh, mm, ss, ff, drop_frame;
+    const uint8_t *tc_pack;
+
+    tc_pack = dv_extract_pack(frame, dv_timecode);
+    if (!tc_pack)
+        return 0;
+
+    ff = bcd2int(tc_pack[1] & 0x3f);
+    ss = bcd2int(tc_pack[2] & 0x7f);
+    mm = bcd2int(tc_pack[3] & 0x7f);
+    hh = bcd2int(tc_pack[4] & 0x3f);
+    drop_frame = tc_pack[1] >> 6 & 0x1;
+
+    if (ff < 0 || ss < 0 || mm < 0 || hh < 0)
+        return -1;
+
+    // For PAL systems, drop frame bit is replaced by an arbitrary
+    // bit so its value should not be considered. Drop frame timecode
+    // is only relevant for NTSC systems.
+    if(c->sys->ltc_divisor == 25 || c->sys->ltc_divisor == 50) {
+        drop_frame = 0;
+    }
+
+    snprintf(tc, 32, "%02d:%02d:%02d%c%02d",
+             hh, mm, ss, drop_frame ? ';' : ':', ff);
+    return 1;
+}
+
 /*
  * The following 3 functions constitute our interface to the world
  */
@@ -404,6 +446,38 @@ typedef struct RawDVContext {
     uint8_t         buf[DV_MAX_FRAME_SIZE];
 } RawDVContext;
 
+static int dv_read_timecode(AVFormatContext *s) {
+    int ret;
+    char timecode[32];
+    int64_t pos = avio_tell(s->pb);
+
+    // Read 3 DIF blocks: Header block and 2 Subcode blocks.
+    int partial_frame_size = 3 * 80;
+    uint8_t *partial_frame = av_mallocz(sizeof(*partial_frame) *
+                                        partial_frame_size);
+
+    RawDVContext *c = s->priv_data;
+    ret = avio_read(s->pb, partial_frame, partial_frame_size);
+    if (ret < 0)
+        goto finish;
+
+    if (ret < partial_frame_size) {
+        ret = -1;
+        goto finish;
+    }
+
+    ret = dv_extract_timecode(c->dv_demux, partial_frame, timecode);
+    if (ret)
+        av_dict_set(&s->metadata, "timecode", timecode, 0);
+    else if (ret < 0)
+        av_log(s, AV_LOG_ERROR, "Detected timecode is invalid");
+
+finish:
+    av_free(partial_frame);
+    avio_seek(s->pb, pos, SEEK_SET);
+    return ret;
+}
+
 static int dv_read_header(AVFormatContext *s,
                           AVFormatParameters *ap)
 {
@@ -444,6 +518,9 @@ static int dv_read_header(AVFormatContext *s,
     s->bit_rate = av_rescale_q(c->dv_demux->sys->frame_size, (AVRational){8,1},
                                c->dv_demux->sys->time_base);
 
+    if (s->pb->seekable)
+        dv_read_timecode(s);
+
     return 0;
 }