av_q2d()将时间从AVRational形式转换为double形式。AVRational是分数类型,double是双精度浮点数类型,转换的结果单位是秒。转换前后的值基于同一时间基,仅仅是数值的表现形式不同而已。
qv_q2d()实现如下:
/** * 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; }qv_q2d()使用方法如下:
时刻值:timestamp(单位秒) = pts × av_q2d(stream->time_base); 时长值:duration(单位秒) = stream->duration × av_q2d(stream->time_base); 3.5 时间基转换函数av_rescale_q()用于不同时间基的转换,用于将时间值从一种时间基转换为另一种时间基。
/** * 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;av_packet_rescale_ts()用于将AVPacket中各种时间值从一种时间基转换为另一种时间基。
/** * Convert valid timing fields (timestamps / durations) in a packet from one * timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be * ignored. * * @param pkt packet on which the conversion will be performed * @param tb_src source timebase, in which the timing fields in pkt are * expressed * @param tb_dst destination timebase, to which the timing fields will be * converted */ void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst); 3.6 转封装过程中的时间基转换容器中的时间基(AVStream.time_base,3.2节中的tbn)定义如下:
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; ...... }AVStream.time_base是AVPacket中pts和dts的时间单位,输入流与输出流中time_base按如下方式确定:
对于输入流:打开输入文件后,调用avformat_find_stream_info()可获取到每个流中的time_base
对于输出流:打开输出文件后,调用avformat_write_header()可根据输出文件封装格式确定每个流的time_base并写入输出文件中
不同封装格式具有不同的时间基,在转封装(将一种封装格式转换为另一种封装格式)过程中,时间基转换相关代码如下:
av_read_frame(ifmt_ctx, &pkt); pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);下面的代码具有和上面代码相同的效果:
// 从输入文件中读取packet av_read_frame(ifmt_ctx, &pkt); // 将packet中的各时间值从输入流封装格式时间基转换到输出流封装格式时间基 av_packet_rescale_ts(&pkt, in_stream->time_base, out_stream->time_base);这里流里的时间基in_stream->time_base和out_stream->time_base,是容器中的时间基,就是3.2节中的tbn。
例如,flv封装格式的time_base为{1,1000},ts封装格式的time_base为{1,90000}
我们编写程序将flv封装格式转换为ts封装格式,抓取原文件(flv)的前四帧显示时间戳:
再抓取转换的文件(ts)的前四帧显示时间戳:
think@opensuse> ffprobe -show_frames -select_streams v tnmil3.ts | grep pkt_pts ffprobe version 4.1 Copyright (c) 2007-2018 the FFmpeg developers Input #0, mpegts, from 'tnmil3.ts': Duration: 00:00:03.58, start: 0.017000, bitrate: 619 kb/s Program 1 Metadata: service_name : Service01 service_provider: FFmpeg Stream #0:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(progressive), 784x480, 25 fps, 25 tbr, 90k tbn, 50 tbc Stream #0:1[0x101]: Audio: aac (LC) ([15][0][0][0] / 0x000F), 44100 Hz, stereo, fltp, 127 kb/s pkt_pts=7200 pkt_pts_time=0.080000 pkt_pts=10800 pkt_pts_time=0.120000 pkt_pts=14400 pkt_pts_time=0.160000 pkt_pts=18000 pkt_pts_time=0.200000