使用 FFmpeg 在 mp4 容器中寻找 h264 编解码器的视频帧。数据包 pts 始终为 0
Seeking to video frame of h264 codec in mp4 container with FFmpeg. Packet pts is always 0
我正在尝试使用 FFmpeg 寻找特定帧的最近关键帧,但每当我使用 av_read_frame[ 获取下一帧时=34=] 搜索后,数据包 pts 或 dts 始终为 0。这只发生在 h264/mp4 视频中,因为它对 .avi 容器中的某些编解码器工作正常。
我试过使用avformat_seek_file和av_seek_frame 但他们给了我相同的结果。
我还读到我不应该从数据包中读取时间戳,所以我尝试先用 avcodec_decode_video2[= 解码数据包34=] 并读取 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 */
也许我处理这种编解码器不正确,任何想法将不胜感激。
请注意,我只对视频帧感兴趣。
好的,我想我有所收获。
好像avformat_seek_file实际上想要的是你要查找的数据包的点而不是帧数,所以因为我使用 av_rescale_q 将数据包 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;
目前看来一切正常。我仍然会接受任何建议,因为这是我想到的第一个解决方案,可能有点天真,我不确定它是否适用于所有情况。
我正在尝试使用 FFmpeg 寻找特定帧的最近关键帧,但每当我使用 av_read_frame[ 获取下一帧时=34=] 搜索后,数据包 pts 或 dts 始终为 0。这只发生在 h264/mp4 视频中,因为它对 .avi 容器中的某些编解码器工作正常。
我试过使用avformat_seek_file和av_seek_frame 但他们给了我相同的结果。
我还读到我不应该从数据包中读取时间戳,所以我尝试先用 avcodec_decode_video2[= 解码数据包34=] 并读取 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 */
也许我处理这种编解码器不正确,任何想法将不胜感激。
请注意,我只对视频帧感兴趣。
好的,我想我有所收获。
好像avformat_seek_file实际上想要的是你要查找的数据包的点而不是帧数,所以因为我使用 av_rescale_q 将数据包 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;
目前看来一切正常。我仍然会接受任何建议,因为这是我想到的第一个解决方案,可能有点天真,我不确定它是否适用于所有情况。