文档章节

ffmpeg time_base详解

j
 jane_linux
发布于 2017/03/28 15:03
字数 1862
阅读 16
收藏 0
点赞 0
评论 0

ffmpeg time_base

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为单位。

读后总结:

 1、AVStream结构体中的time_base以秒为单位,时间刻度(1,90000)位单位,mpeg的pts、dts采样率是90HZ,

2、AVCodecContext中的time_base以秒为单位,时间刻度(1,framerate(帧率))

3、PTS DTS 以何种单位表示:

    a、AVPacket里的PTS DTS以AVStream->time_base为单位,(1,90000)

     b、AVFrame里的pkt_pts, pkt_dts以AVStream->time_base为单位,PTS以AVCodecContex->time_base为单位(1,25(帧率))

 

本文转载自:http://blog.csdn.net/peckjerry/article/details/48344389

共有 人打赏支持
j
粉丝 0
博文 10
码字总数 970
作品 0
基于ffmpeg截取视频帧画面

看到有人发了嵌入式Linux通过帧缓存截图 - Embedded Linux Framebuffer Screenshot,我在前些时间也做了一些类似的工作,不过我截的是视频文件的帧画面。文章我在blog内发表了,现看到有人做...

mickelfeng
2013/06/19
0
0
解密FFmpeg播放状态控制内幕

上一篇文章(http://my.oschina.net/u/2336532/blog/400790)我们解决了在FFmpeg下如何处理H264和AAC的扩展数据,根据解出的NALU长度恢复了H264的起始码和AAC的ADTS头,这样一般来说播放是没有...

东辉在线
2015/04/21
0
0
How to Build ffmpeg with NDK r9

This is a updated post for a previous post, where we built ffmpeg 0.8 with Android NDK r5 and r6. This post will give instructions of how to build ffmpeg 2.0.1 with Android NDK ......

wape-yang
2014/06/14
0
0
Build ffmpeg with NDK r9(转载)

How to Build ffmpeg with NDK r9 by ROMAN10 on AUGUST 18, 2013 · 52 COMMENTS This is a updated post for a previous post, where we built ffmpeg 0.8 with Android NDK r5 and r6. Th......

老牛吃嫩草
2014/03/13
0
0
解密FFmpeg播放track mode控制

上一篇文章(http://my.oschina.net/u/2336532/blog/400790)我们解决了在FFmpeg下如何处理H264和AAC的扩展数据,根据解出的NALU长度恢复了H264的起始码和AAC的ADTS头,这样一般来说播放是没有...

yang_danny
2015/04/19
0
0
如何将ffmpeg在windows编译和使用

FFmpeg在Windows系统下的编译过程,分四步: 如下: 1. 配置编译环境 2. 下载FFMPEG的代码 3. 编译,获取FFMPEG库(头文件,lib,和DLL) 4. 在VC下配置,测试 1. 配置编译环境 1)安装MSy...

李荣刚
2015/03/25
0
0
windows 下 hls 的点播和直播

Apple的动态码率自适应技术。主要用于PC和Apple终端的音视频服务。包括一个m3u(8)的索引文件,TS媒体分片文件和key加密串文件(可选)。 常用的流媒体协议主要有 HTTP 渐进下载和基于 RTSP/R...

mlianghua
2015/06/05
0
0
android端采用FFmpeg进行音频混合与拼接剪切

接触FFmpeg有一段时间了,它是音视频开发的开源库,几乎其他所有播放器、直播平台都基于FFmpeg进行二次开发。本篇文章来总结下采用FFmpeg进行音频处理:音频混合、音频剪切、音频拼接与音频转...

mp624183768
05/13
0
0
php 安装ffmpeg-php

下载ffmepg源码包和ffmpeg-php包,如下: #wget http://www.ffmpeg.org/releases/ffmpeg-export-snapshot.tar.bz2 #wget http://downloads.sourceforge.net/project/ffmpeg-php/ffmpeg-php/0......

Thebreezecomes
06/28
0
0
使用ffmpeg推流到Wowza

Step by step instructions for using FFmpeg as a live video/audio encoder with Wowza Media Server. Updated for FFMpeg build r25705 (22 November 2010) Note: Wowza Media Systems pr......

Johnny_feng
2013/12/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

实现异步有哪些方法

有哪些方法可以实现异步呢? 方式一:java 线程池 示例: @Test public final void test_ThreadPool() throws InterruptedException { ScheduledThreadPoolExecutor scheduledThre......

黄威
今天
0
0
linux服务器修改mtu值优化cpu

一、jumbo frames 相关 1、什么是jumbo frames Jumbo frames 是指比标准Ethernet Frames长的frame,即比1518/1522 bit大的frames,Jumbo frame的大小是每个设备厂商规定的,不属于IEEE标准;...

六库科技
今天
0
0
牛客网刷题

1. 二维数组中的查找(难度:易) 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入...

大不了敲一辈子代码
今天
0
0
linux系统的任务计划、服务管理

linux任务计划cron 在linux下,有时候要在我们不在的时候执行一项命令,或启动一个脚本,可以使用任务计划cron功能。 任务计划要用crontab命令完成 选项: -u 指定某个用户,不加-u表示当前用...

黄昏残影
昨天
0
0
设计模式:单例模式

单例模式的定义是确保某个类在任何情况下都只有一个实例,并且需要提供一个全局的访问点供调用者访问该实例的一种模式。 实现以上模式基于以下必须遵守的两点: 1.构造方法私有化 2.提供一个...

人觉非常君
昨天
0
0
《Linux Perf Master》Edition 0.4 发布

在线阅读:https://riboseyim.gitbook.io/perf 在线阅读:https://www.gitbook.com/book/riboseyim/linux-perf-master/details 百度网盘【pdf、mobi、ePub】:https://pan.baidu.com/s/1C20T......

RiboseYim
昨天
1
0
conda 换源

https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/conda config --add channels https://mir......

阿豪boy
昨天
1
0
Confluence 6 安装补丁类文件

Atlassian 支持或者 Atlassian 缺陷修复小组可能针对有一些关键问题会提供补丁来解决这些问题,但是这些问题还没有放到下一个更新版本中。这些问题将会使用 Class 类文件同时在官方 Jira bug...

honeymose
昨天
0
0
非常实用的IDEA插件之总结

1、Alibaba Java Coding Guidelines 经过247天的持续研发,阿里巴巴于10月14日在杭州云栖大会上,正式发布众所期待的《阿里巴巴Java开发规约》扫描插件!该插件由阿里巴巴P3C项目组研发。P3C...

Gibbons
昨天
1
0
Tomcat介绍,安装jdk,安装tomcat,配置Tomcat监听80端口

Tomcat介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 java程序写的网站用tomcat+jdk来运行...

TaoXu
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部