一. AV_TIME_BASE
經常在FFmpeg的代碼中看到一個奇怪的單位 AV_TIME_BASE ,比如 AVFormatContext 結構體中就有這樣一個字段: duration ,它在FFmpeg中的解釋如下:
/** * Duration of the stream, in AV_TIME_BASE fractional * seconds. Only set this value if you know none of the individual stream * durations and also do not set any of them. This is deduced from the * AVStream values if not set. * * Demuxing only, set by libavformat. */ int64_t duration;
以一段時長為60s的視頻為例,用FFmpeg將其讀入到內存,並打印出它的 duration 后發現:
AVFormatContext *ic = NULL; int re = avformat_open_input(&ic, path, 0, 0); if (re != 0) { LOGE("avformat_open_input failed: %s", av_err2str(re)); return; } LOGI("avformat_open_input %s success", path); re = avformat_find_stream_info(ic, 0); if (re != 0) { LOGE("avformat_find_stream_info failed: %s", av_err2str(re)); } LOGI("duration is: %lld, nb_streams = %d, input filename is: %s, bitrate = %lld", ic->duration, ic->nb_streams, ic->filename, ic->bit_rate);
duration 此時為60000000,而它的注釋也說得很清楚,是以 AV_TIME_BASE 為單位,找到 AV_TIME_BASE :
/** * Internal time base represented as integer */ #define AV_TIME_BASE 1000000 /** * Internal time base represented as fractional value */ #define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}
發現該值為1000000,60000000/1000000剛好為60,而1s = 1000000μs 由此可見,FFmpeg內部的時間單位其實是微秒(μs),而 AV_TIME_BASE_Q 其實是一種分數的表示形式,其中的1表示分子, AV_TIME_BASE 也就是1000000,表示的是分母,所以它其實就是1微秒,也就是 1/1000000 秒。
事實上,除了 AVFormatContext 中的 duration ,FFmpeg中的pts,dts也是基於timebase來換算的,時間基(time_base)是FFmpeg中作為時間單位的概念,比如上面的 AV_TIME_BASE_Q ,它就相當於是 1/1000000 秒,也就是把1s分成1000000份,可以理解成是一把尺,那么每一格就是 1/1000000 秒,此時的time_base就是{1, 1000000}。所謂的時間基就是指每個刻度是多少秒。那么它的作用是什么呢?其實就是為了更精確的度量時間。
二. pts/dts的時間戳究竟代表什么
之前解釋過PTS和DTS的基本概念:
DTS(Decoding Time Stamp):即解碼時間戳,這個時間戳的意義在於告訴播放器該在什么時候解碼這一幀的數據。
PTS(Presentation Time Stamp):即顯示時間戳,這個時間戳用來告訴播放器該在什么時候顯示這一幀的數據。
光看字面意思,它也是一種時間戳,那它這種時間戳到底是什么樣子的?是傳統意義上 yyyy-MM-dd HH:mm:ss ?首先可以肯定的是絕不是 yyyy-MM-dd HH:mm:ss,因為這種時間戳的控制精度不夠,上面甚至都用μs來表示了,那這個PTS和DTS的值到底是個什么東西?
pts和dts的值指的是占多少個時間刻度(占多少個格子)。它的單位不是秒,而是時間刻度。只有pts與time_base兩者結合在一起,才能表達出具體的時間是多少。好比我只告訴你,某個物體的長度占某一把尺上的20個刻度。但是我不告訴你,每個刻度是多少厘米,你仍然無法知道物體的長度。pts 就是這樣的東西,pts(占了多少個時間刻度) , time_base(每個時間刻度是多少秒) ,而幀的顯示時間戳 = pts(占了多少個時間刻度) * time_base(每個時間刻度是多少秒)。
三. 一些時間基轉換的場景
【計算視頻總時長】
AVFormatContext *ifmt_ctx = NULL; avformat_open_input(&ifmt_ctx, filename, NULL, NULL); double totle_seconds = ifmt_ctx->duration * av_q2d(AV_TIME_BASE_Q);
【根據PTS求出一幀在視頻中對應的秒數位置】
double sec = enc_pkt.pts * av_q2d(ofmt_ctx->streams[stream_index]->time_base);
【ffmpeg內部的時間戳與標准的時間轉換方法】
timestamp(ffmpeg內部時間戳) = AV_TIME_BASE * time(秒)
time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg內部時間戳)
【當需要把視頻Seek到N秒的時候】
// 指定流索引 int pos = 20 * r2d(ic->streams[videoStream]->time_base); av_seek_frame(ic,videoStream, pos, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME ); // 未指定指定流索引 int64_t timestamp = N * AV_TIME_BASE; av_seek_frame(fmtctx, -1, timestamp, AVSEEK_FLAG_BACKWARD);
參考鏈接: