【发布时间】:2021-11-05 14:14:09
【问题描述】:
我有一个实时生成视频帧的过程。我正在将生成的视频帧流混合到一个视频文件中(mp4 容器上的 x264 编解码器)。
我正在使用 ffmpeg-libav,并且基于 muxing.c 示例。该示例的问题在于,这不是真实世界的场景,因为在给定的流持续时间的 while 循环上生成帧,从不丢失帧。
在我的程序中,帧应该以 FPS 生成,但是,取决于硬件容量,它生成的帧可能低于 FPS。当我初始化视频流上下文时,我声明帧速率为 FPS:
AVRational r = { 1, FPS };
ost->st->time_base = r;
这指定视频将具有 FPS 帧速率,但如果生成的帧较少,则播放速度会更快,因为它仍会重现视频,就像每秒具有所有声明的帧一样。
在谷歌上搜索了很多关于该主题的内容后,我了解到解决此问题的关键是操纵 pts 和 dts,但我仍然没有找到可行的解决方案。
在 muxing.c 示例中编写视频帧时有两个关键函数,我在我的程序中使用的例程:
AVFrame* get_video_frame(int timestamp, OutputStream *ost, const QImage &image)
{
/* when we pass a frame to the encoder, it may keep a reference to it
* internally; make sure we do not overwrite it here */
if (av_frame_make_writable(ost->frame) < 0)
exit(1);
av_image_fill_arrays(ost->tmp_frame->data, ost->tmp_frame->linesize, image.bits(), AV_PIX_FMT_RGBA, ost->frame->width, ost->frame->height, 8);
libyuv::ABGRToI420(ost->tmp_frame->data[0], ost->tmp_frame->linesize[0], ost->frame->data[0], ost->frame->linesize[0], ost->frame->data[1], ost->frame->linesize[1], ost->frame->data[2], ost->frame->linesize[2], ost->tmp_frame->width, -ost->tmp_frame->height);
#if 1 // this is my attempt to rescale pts, but crashes with pts<dts
ost->frame->pts = av_rescale_q(timestamp, AVRational{1, 1000}, ost->st->time_base);
#else
ost->frame->pts = ost->next_pts++;
#endif
return ost->frame;
}
在原始代码中,pts 只是每个帧的递增整数。我正在尝试做的是在录制开始后以毫秒为单位传递一个时间戳,以便我可以重新调整 pts。当我重新调整 pts 时,程序崩溃并抱怨 pts 低于 dts。
从我一直在阅读的内容来看,pts/dts 操作应该在数据包级别完成,所以我也尝试在 write_frame 例程上进行操作,但没有成功。
int write_frame(AVFormatContext *fmt_ctx, AVCodecContext *c, AVStream *st, AVFrame *frame)
{
int ret;
// send the frame to the encoder
ret = avcodec_send_frame(c, frame);
if (ret<0)
{
fprintf(stderr, "Error sending a frame to the encoder\n");
exit(1);
}
while (ret >= 0)
{
AVPacket pkt = { 0 };
ret = avcodec_receive_packet(c, &pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
break;
}
else if (ret<0)
{
//fprintf(stderr, "Error encoding a frame: %s\n", av_err2str(ret));
exit(1);
}
/* rescale output packet timestamp values from codec to stream timebase */
av_packet_rescale_ts(&pkt, c->time_base, st->time_base);
pkt.stream_index = st->index;
/* Write the compressed frame to the media file. */
//log_packet(fmt_ctx, &pkt);
ret = av_interleaved_write_frame(fmt_ctx, &pkt);
av_packet_unref(&pkt);
if (ret < 0)
{
//fprintf(stderr, "Error while writing output packet: %s\n", av_err2str(ret));
exit(1);
}
}
return ret == AVERROR_EOF ? 1 : 0;
}
我应该如何操作 dts 和 pts 以便我可以在某个帧上实现视频,而该视频没有流初始化中指定的所有帧?我应该在哪里进行这种操作?在 get_video_frame 上?在 write_frame 上?两者都有?
我是否朝着正确的方向前进?我错过了什么?
【问题讨论】:
标签: video ffmpeg libav libavcodec libavformat