Bootstrap

个人关于FFmpeg编解码中涉及到的pts总结(关于时间戳和时间基)

想要播放一个视频,需要拿到视频画面和音频数据,然后对比一下他们的时间,让他们的时间对齐,再放入显示器和扬声器中播放。在FFmpeg里,对于视频,它把画面一帧一帧增加认为是一个单元,因此对于视频来说,一个帧率为10帧每秒的视频,那么在1秒内它就是1 2 3 4 5 6 7 8 9 10这样的一个计数,可以简单地认为这里的1 2 3 4 5 6 7 8 9 10就是其中的一个时间戳(pts)
解码部分:
AVStream.time_base是AVPacket中pts和dts的时间单位。解码时通过av_read_frame读取数据到AVPacket,此时AVPacket有一个pts,该pts是以AVStream.time_base为基准的。我们需要将此pts转换成解码后的pts,可以通过av_packet_rescale_ts来把Stream的time_base转换成AVCodecContext的time_base,对于视频来讲,这里的AVCodecContext的time_base是帧率的倒数。通过解码后得到视频帧AVFrame,这里的AVFrame会有一个pts,该pts是以AVCodecContext的time_base为基准的,如果我们要拿这帧画面去显示的话,我们还要转换成显示的时间,即从AVCodecContext的time_base转换成1000000的timebase,最后得到的是我们习惯的微秒单位。
视频解码过程中的时间基转换处理:
 

AVFormatContext *ifmt_ctx;
AVStream *in_stream;
AVCodecContext *dec_ctx;
AVPacket packet;
AVFrame *frame;

// 从输入文件中读取编码帧
av_read_frame(ifmt_ctx, &packet);

// 时间基转换
int raw_video_time_base = av_inv_q(dec_ctx->framerate);
av_packet_rescale_ts(packet, in_stream->time_base, raw_video_time_base);

// 解码
avcodec_send_packet(dec_ctx, packet)
avcodec_receive_frame(dec_ctx, frame);

编码部分:
前面解码部分我们得到了一个AVFrame,并且得到了微秒为基准的pts,我们要去编码的话,就要逆过来,我们将这个pts通过调用av_rescale_q将pts转换成编码器的pts,转换成功后,就可以压缩了,压缩调用avcodec_send_frame和avcodec_receive_packet得到一个AVPacket,此时会有一个pts,该pts是以编码器为基准的,所以需要再次调用av_packet_rescale_ts将编码器的pts转换成AVStream的pts,最后才可以写入到文件或者流中。
视频编码过程中的时间基转换处理:
 

AVFormatContext *ofmt_ctx;
AVStream *out_stream;
AVCodecContext *dec_ctx;
AVCodecContext *enc_ctx;
AVPacket packet;
AVFrame *frame;

// 编码
avcodec_send_frame(enc_ctx, frame);
avcodec_receive_packet(enc_ctx, packet);

// 时间基转换
packet.stream_index = out_stream_idx;
enc_ctx->time_base = av_inv_q(dec_ctx->framerate);
av_packet_rescale_ts(&opacket, enc_ctx->time_base, out_stream->time_base);

// 将编码帧写入输出媒体文件
av_interleaved_write_frame(o_fmt_ctx, &packet);

;