ffmpeg里time_base总结

https://www.jianshu.com/p/bf323cee3b8e

 

https://www.suninf.net/2018/02/ffmpeg-timestamps.html

个人总结:

ffmpeg的时钟基,其实就是微妙。可以理解成为了避免浮点运算带来的重入误差,引入了精确的微秒作为基础的时间单位。

pts,dts等,都是以微秒为单位的。当我们把它转化成秒的时候,当然要乘以时钟基的倒数。

 

 0.1 2016.11.02 15:44* 字数 499 阅读 3001评论 1喜欢 5

本来是想写一篇time_base的详细文章,后来发现很多大神写过,那这里我就总结一下各种time_base,方便大家理解。

一、time_base

  1. AVStreamtime_base的单位是秒。每种格式的time_base的值不一样,根据采样来计算,比如mpegptsdts都是以90kHz来采样的,所以采样间隔就是1/900000秒。

  2. AVCodecContexttime_base单位同样为秒,不过精度没有AVStream->time_base高,大小为1/framerate

  3. AVPacket下的ptsdtsAVStream->time_base为单位(数值比较大),时间间隔就是AVStream->time_base

  4. AVFrame里面的pkt_ptspkt_dts是拷贝自AVPacket,同样以AVStream->time_base为单位;而pts是为输出(显示)准备的,以AVCodecContex->time_base为单位。

  5. 输入流InputStream下的ptsdtsAV_TIME_BASE为单位(微秒),至于为什么要转化为微秒,可能是为了避免使用浮点数。

  6. 输出流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);
}
  1. 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->ptsAVPacket->ptsAVPacket->dts的值,在解码/编码后,会经历短暂的time_base不匹配的情况:

  1. 解码后,decoded_frame->pts的值使用AVStream->time_base为单位,后在AVFilter里面转换成以AVCodecContext->time_base为单位。
  2. 编码后,pkt.ptspkt.dts使用AVCodecContext->time_base为单位,后通过调用"av_packet_rescale_ts"转换为AVStream->time_base为单位。

四、结束

这其实就是篇笔记,在前人的基础上对time_base的进行了大概总结, 以后会继续更新。

 

 

 

FFmpeg时间戳整理

@SUNINF · FEB 4, 2018

Catalog

使用FFmpeg库进行音视频处理时,经常需要处理视频时间、编解码的dts/pts时间戳,时间的准确处理对音视频处理至关重要。

time_base

时间基(time_base)是FFmpeg中作为时间单位的概念。不同的音视频封装格式,可以使用不同的time_base

FFmpeg内部的时间都会基于AV_TIME_BASE作为时间单位,比如AVStream中的duration表示这个流的长度为duration个AV_TIME_BASE

/**
 * Internal time base represented as integer
 */

#define AV_TIME_BASE            1000000

AVStream中的time_base的定义:

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: 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.
     *
     * Encoding: May be set by the caller before avformat_write_header() to
     * provide a hint to the muxer about the estimated duration.
     */
  int64_t duration;

	// ...
};

为了时间计算的精度,FFmpeg引入有理数的时间基形式 AV_TIME_BASE_Q

/**
 * Rational number (pair of numerator and denominator).
 */
typedef struct AVRational{
    int num; ///< Numerator
    int den; ///< Denominator
} AVRational;

/**
 * Internal time base represented as fractional value
 */

#define AV_TIME_BASE_Q          (AVRational){1, AV_TIME_BASE}

ffmpeg提供了一个把AVRatioal有理数结构转换成double的函数:

/**
 * Convert an AVRational to a `double`.
 * @param a AVRational to convert
 * @return `a` in floating-point form
 * @see av_d2q()
 */
static inline double av_q2d(AVRational a){
    return a.num / (double) a.den;
}

时间戳换算

比如计算视频的总时长:

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内部时间戳)

当需要把视频跳转到N秒的时候:

int64_t timestamp = N * AV_TIME_BASE; 
av_seek_frame(fmtctx, index_of_video, timestamp, AVSEEK_FLAG_BACKWARD);

时间基转换函数

另外,FFmpeg为我们提供了不同时间基之间的转换函数,把时间戳从一个时基调整到另外一个时基:

/**
 * Rescale a 64-bit integer by 2 rational numbers.
 *
 * The operation is mathematically equivalent to `a * bq / cq`.
 *
 * This function is equivalent to av_rescale_q_rnd() with #AV_ROUND_NEAR_INF.
 *
 * @see av_rescale(), av_rescale_rnd(), av_rescale_q_rnd()
 */
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const;

dts/pts计算

  • dts: decoding time stamp (解码时间戳)
  • pts: present time stamp (显示时间戳)

转码流程中的dts/pts转换:

输入

AVPacket packet;
av_read_frame(ifmt_ctx, &packet)

解码

AVFrame *frame = NULL;
// 转换为解码器的时间戳
av_packet_rescale_ts(&packet,
                             ifmt_ctx->streams[stream_index]->time_base,
                             ifmt_ctx->streams[stream_index]->codec->time_base);
dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 :
        avcodec_decode_audio4;
ret = dec_func(ifmt_ctx->streams[stream_index]->codec, frame,
                       &got_frame, &packet);

编码

AVPacket enc_pkt;
int (*enc_func)(AVCodecContext *, AVPacket *, const AVFrame *, int *) =
  (ifmt_ctx->streams[stream_index]->codec->codec_type ==
   AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2;
  
/* encode filtered frame */
enc_pkt.data = NULL;
enc_pkt.size = 0;
av_init_packet(&enc_pkt);
ret = enc_func(ofmt_ctx->streams[stream_index]->codec, &enc_pkt,
                filt_frame, got_frame);

输出

// 编码后的enc_pkt的时间戳以AVCodecContext->time_base为单位
// 编码器时间戳转为输出流AVStream时间戳
av_packet_rescale_ts(&enc_pkt,
                       ofmt_ctx->streams[stream_index]->codec->time_base,
                       ofmt_ctx->streams[stream_index]->time_base);
av_interleaved_write_frame(ofmt_ctx, &enc_pkt);

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值