ffmpeg time_basehtml
ffmpeg存在多個時間基準(time_base),對應不一樣的階段(結構體),每一個time_base具體的值不同,ffmpeg提供函數在各個time_base中進行切換。搞清楚各個time_base的來源,對於閱讀ffmpeg的代碼很重要。
1、time_base
一、AVStream(libavformat/avformat.h)app
typedef struct AVStream { /** * This is the fundamental unit of time (in seconds) in terms * of which frame timestamps are represented. * * decoding: set by libavformat * encoding: May be set by the caller before avformat_write_header() to * provide a hint to the muxer about the desired timebase. In * avformat_write_header(), the muxer will overwrite this field * with the timebase that will actually be used for the timestamps * written into the file (which may or may not be related to the * user-provided one, depending on the format). */ AVRational time_base; /** * Decoding: pts of the first frame of the stream in presentation order, in stream time base. * Only set this if you are absolutely 100% sure that the value you set * it to really is the pts of the first frame. * This may be undefined (AV_NOPTS_VALUE). * @note The ASF header does NOT contain a correct start_time the ASF * demuxer must NOT set this. */ int64_t start_time; /** * Decoding: duration of the stream, in stream time base. * If a source file does not specify a duration, but does specify * a bitrate, this value will be estimated from bitrate and file size. */ int64_t duration; 從上面的信息能夠看到,AVStream->time_base單位爲秒。 那AVStream->time_base具體的值是多少呢?下面以mpegts_demuxer爲例: static int mpegts_set_stream_info(AVStream *st, PESContext *pes, uint32_t stream_type, uint32_t prog_reg_desc) { avpriv_set_pts_info(st, 33, 1, 90000); void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den) { AVRational new_tb; if (av_reduce(&new_tb.num, &new_tb.den, pts_num, pts_den, INT_MAX)) { if (new_tb.num != pts_num) av_log(NULL, AV_LOG_DEBUG, "st:%d removing common factor %d from timebase\n", s->index, pts_num / new_tb.num); } else av_log(NULL, AV_LOG_WARNING, "st:%d has too large timebase, reducing\n", s->index); if (new_tb.num <= 0 || new_tb.den <= 0) { av_log(NULL, AV_LOG_ERROR, "Ignoring attempt to set invalid timebase %d/%d for st:%d\n", new_tb.num, new_tb.den, s->index); return; } s->time_base = new_tb; av_codec_set_pkt_timebase(s->codec, new_tb); s->pts_wrap_bits = pts_wrap_bits; } 經過avpriv_set_pts_info(st, 33, 1, 90000)函數,設置AVStream->time_base爲1/90000。爲何是90000?由於mpeg的pts、dts都是以90kHz來採樣的,因此採樣間隔爲1/90000秒。 二、AVCodecContext typedef struct AVCodecContext { /** * This is the fundamental unit of time (in seconds) in terms * of which frame timestamps are represented. For fixed-fps content, * timebase should be 1/framerate and timestamp increments should be * identically 1. * - encoding: MUST be set by user. * - decoding: Set by libavcodec. */ AVRational time_base;
從上面的信息能夠看到,AVCodecContext->time_base單位一樣爲秒,不過精度沒有AVStream->time_base高,大小爲1/framerate。
下面以ffmpeg轉碼工具爲例:less
static int transcode_init(void) { if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { if (ost->filter && !ost->frame_rate.num) ost->frame_rate = av_buffersink_get_frame_rate(ost->filter->filter); if (ist && !ost->frame_rate.num) ost->frame_rate = ist->framerate; if (ist && !ost->frame_rate.num) ost->frame_rate = ist->st->r_frame_rate; if (ist && !ost->frame_rate.num) { ost->frame_rate = (AVRational){25, 1}; av_log(NULL, AV_LOG_WARNING, "No information " "about the input framerate is available. Falling " "back to a default value of 25fps for output stream #%d:%d. Use the -r option " "if you want a different framerate.\n", ost->file_index, ost->index); } // ost->frame_rate = ist->st->avg_frame_rate.num ? ist->st->avg_frame_rate : (AVRational){25, 1}; if (ost->enc && ost->enc->supported_framerates && !ost->force_fps) { int idx = av_find_nearest_q_idx(ost->frame_rate, ost->enc->supported_framerates); ost->frame_rate = ost->enc->supported_framerates[idx]; } if (enc_ctx->codec_id == AV_CODEC_ID_MPEG4) { av_reduce(&ost->frame_rate.num, &ost->frame_rate.den, ost->frame_rate.num, ost->frame_rate.den, 65535); } } switch (enc_ctx->codec_type) { case AVMEDIA_TYPE_VIDEO: enc_ctx->time_base = av_inv_q(ost->frame_rate); if (ost->filter && !(enc_ctx->time_base.num && enc_ctx->time_base.den)) enc_ctx->time_base = ost->filter->filter->inputs[0]->time_base; if ( av_q2d(enc_ctx->time_base) < 0.001 && video_sync_method != VSYNC_PASSTHROUGH && (video_sync_method == VSYNC_CFR || video_sync_method == VSYNC_VSCFR || (video_sync_method == VSYNC_AUTO && !(oc->oformat->flags & AVFMT_VARIABLE_FPS)))){ av_log(oc, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n" "Please consider specifying a lower framerate, a different muxer or -vsync 2\n"); }
首先獲取ost->frame_rate,而後計算enc_ctx->time_base = 1/ost->frame_rate。
總結:
AVStream->time_base比AVCodecContext->time_base精度要高(數值要小),好比AVStream->time_base爲1/90000,而AVCodecContext->time_base爲1/30(假設frame_rate爲30);一樣的pts和dts,以AVStream->time_base爲單位,數值要比以AVCodecContext->time_base爲單位要大。
2、pts、dts
那各個結構下,pts和dts使用哪一個time_base來表示呢?
一、AVPacketide
typedef struct AVPacket { /** * Presentation timestamp in AVStream->time_base units; the time at which * the decompressed packet will be presented to the user. * Can be AV_NOPTS_VALUE if it is not stored in the file. * pts MUST be larger or equal to dts as presentation cannot happen before * decompression, unless one wants to view hex dumps. Some formats misuse * the terms dts and pts/cts to mean something different. Such timestamps * must be converted to true pts/dts before they are stored in AVPacket. */ int64_t pts; /** * Decompression timestamp in AVStream->time_base units; the time at which * the packet is decompressed. * Can be AV_NOPTS_VALUE if it is not stored in the file. */ int64_t dts;
從上面能夠看到,AVPacket下的pts和dts以AVStream->time_base爲單位(數值比較大)。這也很容易理解,根據mpeg的協議,壓縮後或解壓前的數據,pts和dts是90kHz時鐘的採樣值,時間間隔就是AVStream->time_base。
二、AVFrame函數
typedef struct AVFrame { /** * Presentation timestamp in time_base units (time when frame should be shown to user). */ int64_t pts; /** * PTS copied from the AVPacket that was decoded to produce this frame. */ int64_t pkt_pts; /** * DTS copied from the AVPacket that triggered returning this frame. (if frame threading isn't used) * This is also the Presentation time of this AVFrame calculated from * only AVPacket.dts values without pts values. */ int64_t pkt_dts;
注意:
AVFrame裏面的pkt_pts和pkt_dts是拷貝自AVPacket,一樣以AVStream->time_base爲單位;而pts是爲輸出(顯示)準備的,以AVCodecContex->time_base爲單位)。//FIXME
三、InputStream工具
typedef struct InputStream { int file_index; AVStream *st; AVCodecContext *dec_ctx; int64_t start; /* time when read started */ /* predicted dts of the next packet read for this stream or (when there are * several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */ int64_t next_dts; int64_t dts; ///< dts of the last packet read for this stream (in AV_TIME_BASE units) int64_t next_pts; ///< synthetic pts for the next decode frame (in AV_TIME_BASE units) int64_t pts; ///< current pts of the decoded frame (in AV_TIME_BASE units)
從上面能夠看到,InputStream下的pts和dts以AV_TIME_BASE爲單位(微秒),至於爲何要轉化爲微妙,多是爲了不使用浮點數。
四、OutputStreamui
typedef struct OutputStream { int file_index; /* file index */ int index; /* stream index in the output file */ int source_index; /* InputStream index */ AVStream *st; /* stream in the output file */ int encoding_needed; /* true if encoding needed for this stream */ int frame_number; /* input pts and corresponding output pts for A/V sync */ struct InputStream *sync_ist; /* input stream to sync against */ int64_t sync_opts; /* output frame counter, could be changed to some true timestamp */ // FIXME look at frame_number /* pts of the first frame encoded for this stream, used for limiting * recording time */ int64_t first_pts; /* dts of the last packet sent to the muxer */ int64_t last_mux_dts; AVBitStreamFilterContext *bitstream_filters; AVCodecContext *enc_ctx; AVCodec *enc; int64_t max_frames; AVFrame *filtered_frame;
OutputStream涉及音視頻同步,結構和InputStream不一樣,暫時只做記錄,不分析。
3、各個time_base之間轉換
ffmpeg提供av_rescale_q函數用於time_base之間轉換,av_rescale_q(a,b,c)做用至關於執行a*b/c,經過設置b,c的值,能夠很方便的實現time_base之間轉換。
例如:
一、InputStream(AV_TIME_BASE)到AVPacket(AVStream->time_base)this
static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output) { pkt->dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base);
二、AVPacket(AVStream->time_base)到InputStream(AV_TIME_BASE)編碼
static int process_input_packet(InputStream *ist, const AVPacket *pkt) { if (pkt->dts != AV_NOPTS_VALUE) { ist->next_dts = ist->dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q);
4、後記:
AVFrame->pts和AVPacket->pts、AVPacket->dts的值,在解碼/編碼後,會經歷短暫的time_base不匹配的狀況:
一、解碼後url
static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output) { decoded_frame = ist->decoded_frame; pkt->dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base); update_benchmark(NULL); ret = avcodec_decode_video2(ist->dec_ctx, decoded_frame, got_output, pkt); best_effort_timestamp= av_frame_get_best_effort_timestamp(decoded_frame); if(best_effort_timestamp != AV_NOPTS_VALUE) ist->next_pts = ist->pts = av_rescale_q(decoded_frame->pts = best_effort_timestamp, ist->st->time_base, AV_TIME_BASE_Q);
解碼後,decoded_frame->pts的值使用AVStream->time_base爲單位,後在AVFilter裏面轉換成以AVCodecContext->time_base爲單位。 //FIXME
二、編碼後
static void do_video_out(AVFormatContext *s, OutputStream *ost, AVFrame *in_picture) { ret = avcodec_encode_video2(enc, &pkt, in_picture, &got_packet); if (got_packet) { if (debug_ts) { av_log(NULL, AV_LOG_INFO, "encoder -> type:video " "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n", av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &enc->time_base), av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &enc->time_base)); } if (pkt.pts == AV_NOPTS_VALUE && !(enc->codec->capabilities & CODEC_CAP_DELAY)) pkt.pts = ost->sync_opts; av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base); if (debug_ts) { av_log(NULL, AV_LOG_INFO, "encoder -> type:video " "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n", av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &ost->st->time_base), av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &ost->st->time_base)); } frame_size = pkt.size; write_frame(s, &pkt, ost); /* if two pass, output log */ if (ost->logfile && enc->stats_out) { fprintf(ost->logfile, "%s", enc->stats_out); } }
編碼後,pkt.pts、pkt.dts使用AVCodecContext->time_base爲單位,後經過調用"av_packet_rescale_ts"轉換爲AVStream->time_base爲單位。