[Libav-user] A newbie question on "av_seek_frame" usage
Some Nerd
withertorcher at gmail.com
Wed Dec 13 06:18:18 EET 2023
Hi libav people,
I've been getting to know libav lately for a specific project regarding
studying frame-by-frame animation, with my goal for libav being to produce
JPG images from a video using multiple CPU threads.
Right now, I'm studying various example code and scrubbing through the
documentation to make experimental minimal code that does what I need.
I managed to output a specific range of frames as ".PGM" files, but now I
want to utilize "av_seek_frame
<https://ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#gaa23f7619d8d4ea0857065d9979c75ac8>"
to start closer to the first frame in my target range (as far as I know
av_seek_frame results in the decoder being placed at the nearest "keyframe"
before the target "frame" - with the frame being dependent upon the
keyframe).
However, "av_seek_frame" does not behave as I expect it to, and I can find
no examples of its usage in context online. I imagine I'm using it
incorrectly.
"av_seek_frame" always returns 0 (a success code) and the decoder's current
frame remains at whatever it was before calling "av_seek_frame". It's as if
av_seek_frame is doing nothing, which makes me think my usage is incorrect.
I attached my current code experimentation as "seek_test.cpp"
and the video I'm testing with is located here:
https://drive.google.com/file/d/1xSiGioMSEE2iEt0eVzniZE2HHUP-g16l/view
(I also tried other videos for testing, but they had the same issue.)
I've been at it for a few hours and I'd love if someone experienced could
tell me what I'm doing wrong to have av_seek_frame not behave as expected
:) I have much to learn.
- Ash
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://ffmpeg.org/pipermail/libav-user/attachments/20231212/679ef7cf/attachment.htm>
-------------- next part --------------
char *file_to_try = "E:/starfish.mp4";
int output_frame_min = 550;
int output_frame_max = 555;
// NOTE: Modified version of example:
// https://github.com/namndev/FFmpegTutorial/blob/master/0_hello_world.c
AVFormatContext *format_context = avformat_alloc_context();
avformat_open_input(&format_context, file_to_try, 0, 0);
avformat_find_stream_info(format_context, 0);
for(int stream_index = 0; stream_index < format_context->nb_streams; ++stream_index) {
AVCodecParameters *local_codec_parameters = format_context->streams[stream_index]->codecpar;
if(local_codec_parameters->codec_type == AVMEDIA_TYPE_VIDEO) {
const AVCodec *codec = avcodec_find_decoder(local_codec_parameters->codec_id);
AVCodecContext *codec_context = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_context, local_codec_parameters);
avcodec_open2(codec_context, codec, 0);
AVPacket *packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
// NOTE: trying either of these seek methods; neither seem to do anything despite returning 0
#if 1
int seek_result = av_seek_frame(format_context, stream_index, output_frame_min, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
assert(seek_result >= 0);
#else
// https://stackoverflow.com/a/5265251
int seek_result = avformat_seek_file(format_context,stream_index,0,output_frame_min,output_frame_min,AVSEEK_FLAG_FRAME);
assert(seek_result >= 0);
#endif
bool reached_max_frame = false;
while(!reached_max_frame && av_read_frame(format_context, packet) >= 0) {
if(packet->stream_index == stream_index) {
int response = avcodec_send_packet(codec_context, packet);
if(response >= 0) {
while(true) {
int response = avcodec_receive_frame(codec_context, frame);
if(!reached_max_frame && response >= 0) {
// NOTE: I think it may already be from 0,
// and the way av_read_frame works may just skip the 0th frame and give the next one, making it start from 1?
int frame_number_from_0 = codec_context->frame_number - 1;
if(frame_number_from_0 == output_frame_max) {
reached_max_frame = true;
}
if(frame_number_from_0 >= output_frame_min) {
unsigned char *buffer = frame->data[0];
char filename_data[512];
snprintf(filename_data, sizeof(filename_data), "%d.pgm", frame_number_from_0);
char *filename = &filename_data[0];
// NOTE: Write to .PGM image
FILE *f;
int i;
f = fopen(filename,"w");
fprintf(f, "P5\n%d %d\n%d\n", frame->width, frame->height, 255);
for (i = 0; i < frame->height; i++)
fwrite(buffer + i * frame->linesize[0], 1, frame->width, f);
fclose(f);
}
} else {
if (response != AVERROR(EAGAIN) && response != AVERROR_EOF && !reached_max_frame) {
assert(false, "TODO: error logging");
}
break;
}
}
}
}
}
av_packet_free(&packet);
av_frame_free(&frame);
avcodec_free_context(&codec_context);
}
}
avformat_close_input(&format_context);
avformat_free_context(format_context);
More information about the Libav-user
mailing list