X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fdv.c;h=d99dee37824be6ea7cad387cdbf7f7b2323b3573;hb=b5a69e79c579e6e15e2019ffd34ef0e09aeab586;hp=f69be8775561f579049b65824af5ddd7bd282aaa;hpb=1e19927f12a2a65954aabf38b584025beff15cc3;p=ffmpeg diff --git a/libavformat/dv.c b/libavformat/dv.c index f69be877556..d99dee37824 100644 --- a/libavformat/dv.c +++ b/libavformat/dv.c @@ -30,6 +30,7 @@ */ #include #include "avformat.h" +#include "internal.h" #include "libavcodec/dvdata.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" @@ -86,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; } @@ -214,7 +218,7 @@ static int dv_extract_audio_info(DVDemuxContext* c, uint8_t* frame) c->ast[i] = avformat_new_stream(c->fctx, NULL); if (!c->ast[i]) break; - av_set_pts_info(c->ast[i], 64, 1, 30000); + avpriv_set_pts_info(c->ast[i], 64, 1, 30000); c->ast[i]->codec->codec_type = AVMEDIA_TYPE_AUDIO; c->ast[i]->codec->codec_id = CODEC_ID_PCM_S16LE; @@ -244,13 +248,11 @@ static int dv_extract_video_info(DVDemuxContext *c, uint8_t* frame) if (c->sys) { avctx = c->vst->codec; - av_set_pts_info(c->vst, 64, c->sys->time_base.num, + 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 */ @@ -266,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 */ @@ -403,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) { @@ -443,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; }