aboutsummaryrefslogtreecommitdiffstats
path: root/libavformat/dv.c
diff options
context:
space:
mode:
authorMatthieu Bouron <matthieu.bouron@gmail.com>2011-12-22 22:57:02 +0100
committerMichael Niedermayer <michaelni@gmx.at>2011-12-26 05:46:29 +0100
commit357cb53306a7bc7fcb0d3fa870cabd1d63abbe75 (patch)
treefe0fc01031730a699c4fe8c65fa7be43f458aba2 /libavformat/dv.c
parenta45880dcdbb1e6fe2ef4d2e32bafa1f400f68704 (diff)
downloadffmpeg-357cb53306a7bc7fcb0d3fa870cabd1d63abbe75.tar.gz
dv: add timecode to metadata
Reviewed-by: Reimar Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavformat/dv.c')
-rw-r--r--libavformat/dv.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/libavformat/dv.c b/libavformat/dv.c
index e571f39799..69c0de95c3 100644
--- a/libavformat/dv.c
+++ b/libavformat/dv.c
@@ -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;
}