ffmpeg中dts和pts的转换
FFMPEG的AVRational time_base:
typedef struct AVRational{
int num; ///< numerator
int den; ///< denominator
} AVRational;
AVRational这个结构标识一个分数,num为分数,den为分母。
原文链接:
https://blog.csdn.net/zhuweigangzwg/article/details/64919706
参考:
http://blog.chinaunix.net/uid-20554957-id-5836134.html
https://blog.csdn.net/topsluo/article/details/77981732
time_base:
它是用来度量时间的。
如果把1秒分为25等份,你可以理解就是一把尺,那么每一格表示的就是1/25秒。此时的time_base={1,25}
如果你是把1秒分成90000份,每一个刻度就是1/90000秒,此时的time_base={1,90000}。
所谓时间基表示的就是每个刻度是多少秒
pts的值就是占多少个时间刻度(占多少个格子)。它的单位不是秒,而是时间刻度。只有pts加上time_base两者同时在一起,才能表达出时间是多少。
好比我只告诉你,某物体的长度占某一把尺上的20个刻度。但是我不告诉你,这把尺总共是多少厘米的,你就没办法计算每个刻度是多少厘米,你也就无法知道物体的长度。
pts=20个刻度
time_base={1,10} 每一个刻度是1/10厘米
所以物体的长度=ptstime_base=201/10 厘米
duration和pts单位一样,duration表示当前帧的持续时间占多少格。或者理解是两帧的间隔时间是占多少格。一定要理解单位。
pts:格子数
av_q2d(st->time_base): 秒/格
计算视频长度:
time(秒) = st->duration * av_q2d(st->time_base)
av_q2d(time_base)=每个刻度是多长时间
此时你应该不难理解 pts*av_q2d(time_base)
才是帧的显示时间戳。
案例一:
我们从rtsp拉流后转rtmp推流,要进行dts和pts的转换,这种转换很简单,因为输入的dts没有被破坏,只需要简单转换一下就可以了。
frame.pts = av_rescale_q_rnd(
frame.pts, m_Video_Input_time_base, outStream->time_base,
static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
frame.dts = av_rescale_q_rnd(
frame.dts, m_Video_Input_time_base, outStream->time_base,
static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
frame.duration = av_rescale_q(frame.duration, m_Video_Input_time_base,
outStream->time_base);
其中m_Video_Input_time_base为inputstream 的AVstream中的timebase,frame.dts为发送出去rtmp包的timebase。
案例二:
假如我们将输入rtsp流解码后压缩一下尺寸,再推送到rtmp上去。
因为我们编码的时候设置的参数:
pAVCodecCtx->time_base.num = 1;
pAVCodecCtx->time_base.den = 25;
//这里保存一下编码后输出的dts
int64 temp_dts = frame.dts;
if (m_firstImg) {
//第一帧必须为关键帧
if (!(frame.flags & AV_PKT_FLAG_KEY))
return false;
m_firstImg = false;
m_curTamp = 0;
frame.pts = m_curTamp;
frame.dts = m_curTamp;
//这里很重要了,输出的流1s有den个格子,除以25就是目前一帧所持续时间(两帧间隔)
frame.duration = outStream->time_base.den / 25;
m_lastDuration = frame.duration;
} else {
//如果编码出来的帧dts有误,比如duration大于真确的duration,或者dts比上一张的小,那么我们就自己
//手动进行赋值
if ((abs(frame.dts - m_lastDts) > (outStream->time_base.den / 25)) ||
(frame.dts <= m_lastDts)) {
m_curTamp += m_lastDuration;
frame.pts = m_curTamp;
frame.dts = m_curTamp;
frame.duration = m_lastDuration;
} else {
//将编码出来帧的duration进行不同timebase的转换
m_lastDuration =
av_rescale_q((frame.dts - m_lastDts), {1,25}, outStream->time_base);
//dts就是将duration逐渐累加,比如第10帧就是10*duration
m_curTamp += m_lastDuration;
frame.pts = m_curTamp;
frame.dts = m_curTamp;
frame.duration = m_lastDuration;
}
}
m_lastDts = temp_dts;
m_duration += m_lastDuration;
案例三:
这是雷神写的一段:
入口
PTS/DTS问题
没有封装格式的裸流(例如H.264裸流)是不包含PTS、DTS这些参数的。在发送这种数据的时候,需要自己计算并写入AVPacket的PTS,DTS,duration等参数。这里还没有深入研究,简单写了一点代码,如下所示。
//FIX:No PTS (Example: Raw H.264)
//Simple Write PTS
if(pkt.pts==AV_NOPTS_VALUE){
//Write PTS
AVRational time_base1 = ifmt_ctx->streams[videoindex]->time_base;
//Duration between 2 frames (us)
//比如1s有25帧,那么就是AV_TIME_BASE/25,av_q2d来计算输入帧率
int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
//Parameters,pts就是持续了多少格,
//frame_index*calc_duration,这个是基于AV_TIME_BASE来确定的,输出的时候我们还要转一次,时间基准
//转到输入流的基准上去,做完这一步还要接着将timebase转到输出流的timebase上去
pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
//因为没有b帧,所以dts=pts
pkt.dts=pkt.pts;
//
pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
}
/* copy packet */
//转换PTS/DTS(Convert PTS/DTS)
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
//ffmpeg代码
/**
* 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;
}
ypedef struct AVStream {
int index; /**< stream index in AVFormatContext */
AVCodecContext *codec;
/**
* 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;
/**
* Real base framerate of the stream.
* This is the lowest framerate with which all timestamps can be
* represented accurately (it is the least common multiple of all
* framerates in the stream). Note, this value is just a guess!
* For example, if the time base is 1/90000 and all frames have either
* approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1.
*/
AVRational r_frame_rate;
}
#define AV_TIME_BASE 1000000
上一篇: 红枣玫瑰枸杞,你了解过吗