ffmpeg中的各種時基 time_base


http://www.cnitblog.com/luofuchong/archive/2014/11/28/89869.html

http://www.cnblogs.com/my_life/articles/6277528.html

 

一個視頻文件的PTS不一定從0開始,因此第一個PTS就是基准值,此時的播放時間就是當前幀的PTS減去第一幀的PTS。

PTS不是具體的毫秒,而是一個數值,真實的時間由PTS*timebase計算而來。

http://bbs.csdn.net/topics/390356680

 

打印packet里面的pts。 
我測試的rmvb文件的時間基是:
E/FFMPEGSample(  504): AVStream:
E/FFMPEGSample(  504): video num=1,denum=1000
E/FFMPEGSample(  504): audio num=1,denum=1000    //時基是1/1000,一秒被分成了1000份,所以單位是毫秒

下面是最后幾幀的時間信息,cal pts就是 packet pts乘上 num/denum
E/FFMPEGSample(  504): packet pts 180546, dts=180545, rpt_cnt=0, 
E/FFMPEGSample(  504): cal pts=180.546005
E/FFMPEGSample(  504): packet pts 180712, dts=180546, rpt_cnt=0, 
E/FFMPEGSample(  504): cal pts=180.712006
E/FFMPEGSample(  504): packet pts 180713, dts=180712, rpt_cnt=0, 
E/FFMPEGSample(  504): cal pts=180.713013

從計算得到的時間,比如cal pts=180.713來看,該時間與播放畫面是吻合的(影片長度為180s, 這里計算得到的最后一幀的時間為180.713s)

 

=================================

 

ffmpeg存在多個時間基准(time_base),對應不同的階段(結構體),每個time_base具體的值不一樣,ffmpeg提供函數在各個time_base中進行切換。搞清楚各個time_base的來源,對於閱讀ffmpeg的代碼很重要。

 

一、time_base
1、AVStream(libavformat/avformat.h)

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秒。

 

2、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轉碼工具為例:

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為單位要大。

二、pts、dts
那各個結構下,pts和dts使用哪個time_base來表示呢?

1、AVPacket

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。

2、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

3、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為單位(微秒),至於為什么要轉化為微妙,可能是為了避免使用浮點數。

4、OutputStream

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不同,暫時只作記錄,不分析。


三、各個time_base之間轉換

ffmpeg提供av_rescale_q函數用於time_base之間轉換,av_rescale_q(a,b,c)作用相當於執行a*b/c,通過設置b,c的值,可以很方便的實現time_base之間轉換。
例如:
1、InputStream(AV_TIME_BASE)到AVPacket(AVStream->time_base)

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);


2、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);  


四、后記:
   AVFrame->pts和AVPacket->pts、AVPacket->dts的值,在解碼/編碼后,會經歷短暫的time_base不匹配的情況:

1、解碼后

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

2、編碼后

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為單位。

 

 

==============================================================

http://www.voidcn.com/blog/fireroll/article/p-2527916.html

 

一、函數聲明: 

int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd);


直接看代碼, 它的作用是計算 "a * b / c" 的值並分五種方式來取整. 

用在FFmpeg中,

 

則是將以 "時鍾基c" 表示的 數值a 轉換成以 "時鍾基b" 來表示。


一共有5種方式: 

AV_ROUND_ZERO     = 0, // Round toward zero. 趨近於0 AV_ROUND_INF = 1, // Round away from zero. 趨遠於0 AV_ROUND_DOWN = 2, // Round toward -infinity. 趨於更小的整數 AV_ROUND_UP = 3, // Round toward +infinity. 趨於更大的整數 AV_ROUND_NEAR_INF = 5, // Round to nearest and halfway cases away from zero. // 四舍五入,小於0.5取值趨向0,大於0.5取值趨遠於0


二、函數定義(見於libavutil/mathematics.c): 

int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) { int64_t r=0; assert(c > 0); assert(b >=0); assert((unsigned)rnd<=5 && rnd!=4); /* 將小於0的整數,轉換成大於0的整來計算 */ if (a<0 && a != INT64_MIN) return -av_rescale_rnd(-a, b, c, rnd ^ ((rnd>>1)&1)); if (rnd==AV_ROUND_NEAR_INF) r= c / 2; else if (rnd&1) r= c - 1; if (b<=INT_MAX && c<=INT_MAX) {  /* 處理小於32位整數的情況 */ if (a<=INT_MAX) return (a * b + r)/c; else return a/c*b + (a%c*b + r)/c; } else { /* 64位整數的算法 */ uint64_t a0= a&0xFFFFFFFF; uint64_t a1= a>>32; uint64_t b0= b&0xFFFFFFFF; uint64_t b1= b>>32; uint64_t t1= a0*b1 + a1*b0; uint64_t t1a= t1<<32; int i; a0 = a0*b0 + t1a; a1 = a1*b1 + (t1>>32) + (a0<t1a); a0 += r; a1 += a0<r; for (i=63; i>=0; i--) { a1+= a1 + ((a0>>i)&1); t1+=t1; if (c <= a1) { a1 -= c; t1++; } } } return t1; } 



三、實例分析 
將以"1MHz時鍾基" 表示的 "PTS/DTS值a" 轉換成以 "90kHz時鍾基" 表示。 

av_rescale_q(a=-10949117256, bq={num=1, den=1000000}, cq={num=1, den=90000)) { int64_t b= bq.num * (int64_t)cq.den; // = 1 * 90000 = 90000; int64_t c= cq.num * (int64_t)bq.den; // = 1 * 1000000 = 1000000 return av_rescale_rnd(a, b, c, 5); } av_rescale_rnd(a=10949117256, b=90000, c=1000000, rnd=5) { if (rnd==5) r = c / 2; // r =500000; if (b<=INT_MAX && c<=INT_MAX) { if (a<=INT_MAX) return (a * b + r)/c; else return a/c*b + (a%c*b + r)/c; // = 10949117256 / 1000000 * 90000 + // (10949117256 % 1000000 * 90000 + 500000) / 1000000 // = 985420553 } else { ... } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM