我正在尝试使用FFmpeg寻找特定帧的最近关键帧,但是在我寻找后,无论何时我使用 av_read_frame 获取下一帧,数据包pts或dts始终为0.这只发生在h264 / mp4视频中,因为它可以正常用于.avi容器中的某些编解码器。
我尝试使用 avformat_seek_file 和 av_seek_frame ,但他们给了我相同的结果。
我还读到我不应该从数据包中读取时间戳,因此我尝试首先使用 avcodec_decode_video2 解码数据包并阅读 AVFrame - > pts 信息,但此值对于h264 / mp4视频始终无效。
这是我正在尝试做的相关代码:
/*Relevant from header*/
AVCodecContext pCodecCtx;
AVFormatContext *pFormatCtx;
int videoStreamIndex;
int Class::getFrame(int desiredFrame, bool seek)
if(seek)
{
/* We seek to the selected frame */
if(avformat_seek_file(pFormatCtx, videoStreamIndex, 0, desiredFrame, desiredFrame, AVSEEK_FLAG_BACKWARD) < 0)
//if(av_seek_frame(pFormatCtx, mVideoStream, desiredFrame, AVSEEK_FLAG_BACKWARD) < 0)
{
// error management
}
avcodec_flush_buffers(pCodecCtx);
}
AVPacket packet;
int frameFinished;
/* Loop until we find the next video frame */
while(av_read_frame(pFormatCtx, &packet) >= 0 )
{
if(packet.stream_index == videoStreamIndex)
{
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
int pcktPts;
/*** management of other codecs here using av_frame_get_best_effort_timestamp() ***/
/* With this approach I have been getting correct pts info after many av_read_frame loops */
if(pCodecCtx->codec->id == AV_CODEC_ID_H264 && videoPath.toLower().endsWith(".mp4"))
{
pcktPts = av_rescale_q(packet.pts, //pFrame->pts always invalid here
pFormatCtx->streams[videoStreamIndex]->time_base,
pFormatCtx->streams[videoStreamIndex]->codec->time_base);
pcktPts = (pcktPts/pCodecCtx->ticks_per_frame);
}
if(pcktPts == desiredFrame) ....
/* more irrelevant code for reading, converting frame, etc */
也许我正在错误地处理这种编解码器,任何想法都会受到高度赞赏。
作为一个注释,我只对视频帧感兴趣。
答案 0 :(得分:3)
好的,我想我得到了一些东西。
似乎 avformat_seek_file 实际上想要你想要寻找的数据包的pts而不是帧的数量,所以因为我使用 av_rescale_q 将数据包pts转换为实际帧数我想我必须做相反的操作才能将所需的帧数转换为数据包pts。
现在在寻找之前,我会像这样转换所需的帧数:
int target = desiredFrame *
(pFormatCtx->streams[videoStreamIndex]->time_base.den /
pFormatCtx->streams[videoStreamIndex]->time_base.num) /
(pFormatCtx->streams[videoStreamIndex]->codec->time_base.den /
pFormatCtx->streams[videoStreamIndex]->codec->time_base.num )*
pCodecCtx->ticks_per_frame;
目前看起来似乎按预期工作了。我仍然会接受任何建议,因为这是我想到的第一个解决方案,可能有点幼稚,我不确定它是否适用于所有情况。