Browse code

dv: add timecode to metadata

Reviewed-by: Reimar
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Matthieu Bouron authored on 2011/12/23 06:57:02
Showing 2 changed files
... ...
@@ -7,6 +7,7 @@ version next:
7 7
 - SBaGen (SBG) binaural beats script demuxer
8 8
 - OpenMG Audio muxer
9 9
 - SMJPEG demuxer
10
+- dv: add timecode to metadata
10 11
 
11 12
 
12 13
 version 0.9:
... ...
@@ -87,6 +87,9 @@ static const uint8_t* dv_extract_pack(uint8_t* frame, enum dv_pack_type t)
87 87
     case dv_video_control:
88 88
         offs = (80*5 + 48 + 5);
89 89
         break;
90
+    case dv_timecode:
91
+        offs = (80*1 + 3 + 3);
92
+        break;
90 93
     default:
91 94
         return NULL;
92 95
     }
... ...
@@ -267,6 +270,45 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t* frame)
267 267
     return size;
268 268
 }
269 269
 
270
+static int bcd2int(uint8_t bcd)
271
+{
272
+   int low  = bcd & 0xf;
273
+   int high = bcd >> 4;
274
+   if (low > 9 || high > 9)
275
+       return -1;
276
+   return low + 10*high;
277
+}
278
+
279
+static int dv_extract_timecode(DVDemuxContext* c, uint8_t* frame, char tc[32])
280
+{
281
+    int hh, mm, ss, ff, drop_frame;
282
+    const uint8_t *tc_pack;
283
+
284
+    tc_pack = dv_extract_pack(frame, dv_timecode);
285
+    if (!tc_pack)
286
+        return 0;
287
+
288
+    ff = bcd2int(tc_pack[1] & 0x3f);
289
+    ss = bcd2int(tc_pack[2] & 0x7f);
290
+    mm = bcd2int(tc_pack[3] & 0x7f);
291
+    hh = bcd2int(tc_pack[4] & 0x3f);
292
+    drop_frame = tc_pack[1] >> 6 & 0x1;
293
+
294
+    if (ff < 0 || ss < 0 || mm < 0 || hh < 0)
295
+        return -1;
296
+
297
+    // For PAL systems, drop frame bit is replaced by an arbitrary
298
+    // bit so its value should not be considered. Drop frame timecode
299
+    // is only relevant for NTSC systems.
300
+    if(c->sys->ltc_divisor == 25 || c->sys->ltc_divisor == 50) {
301
+        drop_frame = 0;
302
+    }
303
+
304
+    snprintf(tc, 32, "%02d:%02d:%02d%c%02d",
305
+             hh, mm, ss, drop_frame ? ';' : ':', ff);
306
+    return 1;
307
+}
308
+
270 309
 /*
271 310
  * The following 3 functions constitute our interface to the world
272 311
  */
... ...
@@ -404,6 +446,38 @@ typedef struct RawDVContext {
404 404
     uint8_t         buf[DV_MAX_FRAME_SIZE];
405 405
 } RawDVContext;
406 406
 
407
+static int dv_read_timecode(AVFormatContext *s) {
408
+    int ret;
409
+    char timecode[32];
410
+    int64_t pos = avio_tell(s->pb);
411
+
412
+    // Read 3 DIF blocks: Header block and 2 Subcode blocks.
413
+    int partial_frame_size = 3 * 80;
414
+    uint8_t *partial_frame = av_mallocz(sizeof(*partial_frame) *
415
+                                        partial_frame_size);
416
+
417
+    RawDVContext *c = s->priv_data;
418
+    ret = avio_read(s->pb, partial_frame, partial_frame_size);
419
+    if (ret < 0)
420
+        goto finish;
421
+
422
+    if (ret < partial_frame_size) {
423
+        ret = -1;
424
+        goto finish;
425
+    }
426
+
427
+    ret = dv_extract_timecode(c->dv_demux, partial_frame, timecode);
428
+    if (ret)
429
+        av_dict_set(&s->metadata, "timecode", timecode, 0);
430
+    else if (ret < 0)
431
+        av_log(s, AV_LOG_ERROR, "Detected timecode is invalid");
432
+
433
+finish:
434
+    av_free(partial_frame);
435
+    avio_seek(s->pb, pos, SEEK_SET);
436
+    return ret;
437
+}
438
+
407 439
 static int dv_read_header(AVFormatContext *s,
408 440
                           AVFormatParameters *ap)
409 441
 {
... ...
@@ -444,6 +518,9 @@ static int dv_read_header(AVFormatContext *s,
444 444
     s->bit_rate = av_rescale_q(c->dv_demux->sys->frame_size, (AVRational){8,1},
445 445
                                c->dv_demux->sys->time_base);
446 446
 
447
+    if (s->pb->seekable)
448
+        dv_read_timecode(s);
449
+
447 450
     return 0;
448 451
 }
449 452