]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/dv.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavformat / dv.c
index e571f397993ee01bda24ddb7f28a3e8c1feafcd3..d99dee37824be6ea7cad387cdbf7f7b2323b3573 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;
     }
@@ -248,10 +251,8 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t* frame)
         avpriv_set_pts_info(c->vst, 64, c->sys->time_base.num,
                         c->sys->time_base.den);
         avctx->time_base= c->sys->time_base;
-        if (!avctx->width){
-            avctx->width = c->sys->width;
-            avctx->height = c->sys->height;
-        }
+        if (!avctx->width)
+            avcodec_set_dimensions(avctx, c->sys->width, c->sys->height);
         avctx->pix_fmt = c->sys->pix_fmt;
 
         /* finding out SAR is a little bit messy */
@@ -267,6 +268,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 +444,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 +516,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;
 }