ffmpeg中av_seek_frame() 的问题

asgaobiao 2010-11-24 08:50:55
ffmpeg中解码具体的某一帧,用到av_seek_frame,但是不清楚具体步骤是什么样的。
希望能给个具体的例子,看看到底要用到哪些函数
以下是ffmpeg中的seek的例子,在哪些地方加解码函数才能获得想要的具体某一帧的图像?

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include "libavformat/avformat.h"

#undef exit

int main(int argc, char **argv)
{
const char *filename;
AVFormatContext *ic;
int i, ret, stream_id;
int64_t timestamp;
AVFormatParameters params, *ap= ¶ms;
memset(ap, 0, sizeof(params));
ap->channels=1;
ap->sample_rate= 22050;

/* initialize libavcodec, and register all codecs and formats */
av_register_all();

if (argc != 2) {
printf("usage: %s input_file\n"
"\n", argv[0]);
exit(1);
}

filename = argv[1];

/* allocate the media context */
ic = avformat_alloc_context();
if (!ic) {
fprintf(stderr, "Memory error\n");
exit(1);
}

ret = av_open_input_file(&ic, filename, NULL, 0, ap);
if (ret < 0) {
fprintf(stderr, "cannot open %s\n", filename);
exit(1);
}

ret = av_find_stream_info(ic);
if (ret < 0) {
fprintf(stderr, "%s: could not find codec parameters\n", filename);
exit(1);
}

for(i=0; ; i++){
AVPacket pkt;
AVStream *st;

memset(&pkt, 0, sizeof(pkt));
if(ret>=0){
ret= av_read_frame(ic, &pkt);
printf("ret:%2d", ret);
if(ret>=0){
st= ic->streams[pkt.stream_index];
printf(" st:%2d dts:%f pts:%f pos:%" PRId64 " size:%d flags:%d", pkt.stream_index, pkt.dts*av_q2d(st->time_base), pkt.pts*av_q2d(st->time_base), pkt.pos, pkt.size, pkt.flags);
}
printf("\n");
}

if(i>25) break;

stream_id= (i>>1)%(ic->nb_streams+1) - 1;
timestamp= (i*19362894167LL) % (4*AV_TIME_BASE) - AV_TIME_BASE;
if(stream_id>=0){
st= ic->streams[stream_id];
timestamp= av_rescale_q(timestamp, AV_TIME_BASE_Q, st->time_base);
}
ret = av_seek_frame(ic, stream_id, timestamp, (i&1)*AVSEEK_FLAG_BACKWARD);
printf("ret:%2d st:%2d ts:%f flags:%d\n", ret, stream_id, timestamp*(stream_id<0 ? 1.0/AV_TIME_BASE : av_q2d(st->time_base)), i&1);
}

return 0;
}


谢谢
...全文
4524 14 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
luofl_ 2012-12-20
  • 打赏
  • 举报
回复
表示没看懂怎么回事。我在VS2012下面编译tutorial08.c出错,修改了语法错误后,运行的时候也有问题,不知道怎么解决了。 中断位置是这里。

		if(w > screen->w)
		{
			w = screen->w;
			h = ((int)rint(w / aspect_ratio)) & -3;
		}
		x = (screen->w - w) / 2;
		y = (screen->h - h) / 2;
		rect.x = x;
		rect.y = y;
		rect.w = w;
		rect.h = h;
		SDL_DisplayYUVOverlay(vp->bmp, &rect); // 此处出现访问异常
	}
}

void video_refresh_timer(void *userdata)
{

	VideoState *is = (VideoState *)userdata;
asgaobiao 2010-11-26
  • 打赏
  • 举报
回复
结贴结贴:
av_seek_frame就能实现,弄了几天,最后发现是自己的timestamp没有乘以1000000.晕倒!
用法可以参照我给出来的例子,在read_frame之前加上seek部分的内容就ok啦。


特此感谢 chilli211的耐心指导,膜拜之!
SoftSoftSoft2008 2010-11-25
  • 打赏
  • 举报
回复
可能输入 av_seek_frame 的参数没有给对吧。
asgaobiao 2010-11-25
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 chilli211 的回复:]
在while(av_read_frame(pFormatCtx, &packet)>=0)之前添加代码:
av_seek_frame(pFormatCtx, -1, lStart*1000, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_ANY);
avcodec_flush_buffers(pFormatCtx->streams[video_stream]->co……
[/Quote]
thx。
bug没有了,不过还是每次从第一帧开始啊,为啥?
asgaobiao 2010-11-25
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 chilli211 的回复:]
在while(av_read_frame(pFormatCtx, &packet)>=0)之前添加代码:
av_seek_frame(pFormatCtx, -1, lStart*1000, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_ANY);
avcodec_flush_buffers(pFormatCtx->streams[video_stream]->co……
[/Quote]

产生bug了,说header 被损坏了,然后就跳出。
asgaobiao 2010-11-24
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 oexpress 的回复:]
清参考ffmpeg源代码,调试跟踪里面的流程,如果文件有index或者码流信息,回去找最近的indeX SEEK到I祯,如果没有会按照byte Seek,具体参考下面的函数:

int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
{
int ret;
AVStr……
[/Quote]
谢谢。
这个我知道,这是ffmpeg的源代码,这个函数的作用我懂,我想知道找到I frame以后的解码和输出。
CyberLogix 2010-11-24
  • 打赏
  • 举报
回复
清参考ffmpeg源代码,调试跟踪里面的流程,如果文件有index或者码流信息,回去找最近的indeX SEEK到I祯,如果没有会按照byte Seek,具体参考下面的函数:

int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
{
int ret;
AVStream *st;

ff_read_frame_flush(s);

if(flags & AVSEEK_FLAG_BYTE)
return av_seek_frame_byte(s, stream_index, timestamp, flags);

if(stream_index < 0){
stream_index= av_find_default_stream_index(s);
if(stream_index < 0)
return -1;

st= s->streams[stream_index];
/* timestamp for default must be expressed in AV_TIME_BASE units */
timestamp = av_rescale(timestamp, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num);
}

/* first, we try the format specific seek */
if (s->iformat->read_seek)
ret = s->iformat->read_seek(s, stream_index, timestamp, flags);
else
ret = -1;
if (ret >= 0) {
return 0;
}

if(s->iformat->read_timestamp)
return av_seek_frame_binary(s, stream_index, timestamp, flags);
else
return av_seek_frame_generic(s, stream_index, timestamp, flags);
}
chilli211 2010-11-24
  • 打赏
  • 举报
回复
在while(av_read_frame(pFormatCtx, &packet)>=0)之前添加代码:
av_seek_frame(pFormatCtx, -1, lStart*1000, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_ANY);
avcodec_flush_buffers(pFormatCtx->streams[video_stream]->codec);
avcodec_flush_buffers(pFormatCtx->streams[audio_stream]->codec);
其中,lStart自己设定,单位毫秒。
asgaobiao 2010-11-24
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 chilli211 的回复:]
1)执行av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags),定位到timestamp前的最近关键帧;
2)使用av_read_frame读取数据包;
3)根据包类型分别执行音视频解码。
// 定位
av_seek_frame(pFormatCtx, -1, lStart*1000……
[/Quote]
谢谢诶
在楼上的帖子应该加到什么位置,才能读到任意的timestamp?
我总是只能读到第一帧。
asgaobiao 2010-11-24
  • 打赏
  • 举报
回复

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame);

int main (int argc, const char * argv[])
{
AVFormatContext *pFormatCtx;
int i, videoStream;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame;
AVFrame *pFrameRGB;
AVPacket packet;
int frameFinished;
int numBytes;
uint8_t *buffer;
//PacketQueue *videoq;
// Register all formats and codecs
av_register_all();
// memset(videoq, 0, sizeof(PacketQueue));
// Open video file
argv[1]="test.avi";

if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
return -1; // Couldn't open file

// Retrieve stream information
if(av_find_stream_info(pFormatCtx)<0)
return -1; // Couldn't find stream information

// Dump information about file onto standard error
dump_format(pFormatCtx, 0, argv[1], false);

// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
{
videoStream=i;
break;
}
if(videoStream==-1)
return -1; // Didn't find a video stream

// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;

// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
return -1; // Codec not found

// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
return -1; // Could not open codec

// Hack to correct wrong frame rates that seem to be generated by some codecs
if(pCodecCtx->time_base.num>1000 && pCodecCtx->time_base.den==1)
pCodecCtx->time_base.den=1000;

// Allocate video frame
pFrame=avcodec_alloc_frame();

// Allocate an AVFrame structure
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
return -1;

// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height);

buffer=(uint8_t*)malloc(numBytes);

// Assign appropriate parts of buffer to image planes in pFrameRGB
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);

// Read frames and save first five frames to disk
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0)
{
// Is this a packet from the video stream?
if(packet.stream_index==videoStream)
{
// Decode video frame
avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
packet.data, packet.size);

// Did we get a video frame?
if(frameFinished)
{
static struct SwsContext *img_convert_ctx;

#if 0
// Older removed code
// Convert the image from its native format to RGB swscale
img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,
(AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width,
pCodecCtx->height);

// function template, for reference
int sws_scale(struct SwsContext *context, uint8_t* src[], int srcStride[], int srcSliceY,
int srcSliceH, uint8_t* dst[], int dstStride[]);
#endif
// Convert the image into YUV format that SDL uses
if(img_convert_ctx == NULL) {
int w = pCodecCtx->width;
int h = pCodecCtx->height;

img_convert_ctx = sws_getContext(w, h,
pCodecCtx->pix_fmt,
w, h, PIX_FMT_RGB24, SWS_BICUBIC,
NULL, NULL, NULL);
if(img_convert_ctx == NULL) {
fprintf(stderr, "Cannot initialize the conversion context!\n");
exit(1);
}
}
int ret = sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
#if 0 // this use to be true, as of 1/2009, but apparently it is no longer true in 3/2009
if(ret) {
fprintf(stderr, "SWS_Scale failed [%d]!\n", ret);
exit(-1);
}
#endif
// Save the frame to disk
if(i++<=20)
// rockframe_out(pCodecCtx,packet,videoq->frame_out);
if(videoq->nb_packets<=5)
{
packet_queue_put(pCodecCtx,videoq, videoq->frame_out);
}
while(videoq->nb_packets>5||pause)
{
Sleep(100);
}

}
}

// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
}

// Free the RGB image
free(buffer);
av_free(pFrameRGB);

// Free the YUV frame
av_free(pFrame);

// Close the codec
avcodec_close(pCodecCtx);

// Close the video file
av_close_input_file(pFormatCtx);

return 0;
}

static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
FILE *pFile;
char szFilename[32];
int y;

// Open file
sprintf(szFilename, "%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;

// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);

// Write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

// Close file
fclose(pFile);
}


或者是这个代码中,怎么加入av_seek_frame(),才能实现寻找关键帧,并且保存呢?
asgaobiao 2010-11-24
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 oexpress 的回复:]
找到I frame以后,继续readpkt,avcode_decode_video(),和以前的流程一样
[/Quote]
谢谢,
流程我大概是懂的,但是不知道为什么一直读出来的都是第一帧开始的。请指点。
chilli211 2010-11-24
  • 打赏
  • 举报
回复
1)执行av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags),定位到timestamp前的最近关键帧;
2)使用av_read_frame读取数据包;
3)根据包类型分别执行音视频解码。
// 定位
av_seek_frame(pFormatCtx, -1, lStart*1000, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_ANY);
avcodec_flush_buffers(pFormatCtx->streams[video_stream]->codec);
avcodec_flush_buffers(pFormatCtx->streams[audio_stream]->codec);

while(av_read_frame(pFormatCtx, &packet)>=0)
{
// 视频解码
if(packet.stream_index==video_stream)
{
// 从关键帧处开始解码
len_video= avcodec_decode_video(decodec_video, picture, &get_picture,packet.data, packet.size);
if (get_picture)
{
// 根据packet.dts判断是否是想要的视频帧
……
}

}
// 音频解码
else if (packet.stream_index==audio_stream)
{
len_audio= avcodec_decode_audio3 (decodec_audio,p_decompressed_audio_buf,
&decompressed_audio_buf_size, &packet );
if ( len_audio != -1 )
{
……
}
}
av_free_packet(&packet);
}



CyberLogix 2010-11-24
  • 打赏
  • 举报
回复
找到I frame以后,继续readpkt,avcode_decode_video(),和以前的流程一样
asgaobiao 2010-11-24
  • 打赏
  • 举报
回复
我发的那个程序作为例子,在什么地方加入解码avcode_decode_video(),来获得frame

2,553

社区成员

发帖
与我相关
我的任务
社区描述
专题开发/技术/项目 多媒体/流媒体开发
社区管理员
  • 多媒体/流媒体开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧