[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