Reviewed-by: Reimar
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
| ... | ... |
@@ -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 |
|