因此,我想在特定时间从视频抓取帧,使用 libav 用作缩略图。

我正在使用的是以下代码。它可以编译并正常工作(就根本不涉及检索图片而言),但是我很难将其获取到来检索正确的图片

我根本无法理解libav明显使用每个视频多个时基的背后的逻辑,但逻辑清晰。具体找出哪些函数期望/返回哪种时基。

不幸的是,文档基本上没有任何帮助。如此抢救?

#define ABORT(x) do {fprintf(stderr, x); exit(1);} while(0)

av_register_all();

AVFormatContext *format_context = ...;
AVCodec *codec = ...;
AVStream *stream = ...;
AVCodecContext *codec_context = ...;
int stream_index = ...;

// open codec_context, etc.

AVRational stream_time_base = stream->time_base;
AVRational codec_time_base = codec_context->time_base;

printf("stream_time_base: %d / %d = %.5f\n", stream_time_base.num, stream_time_base.den, av_q2d(stream_time_base));
printf("codec_time_base: %d / %d = %.5f\n\n", codec_time_base.num, codec_time_base.den, av_q2d(codec_time_base));

AVFrame *frame = avcodec_alloc_frame();

printf("duration: %lld @ %d/sec (%.2f sec)\n", format_context->duration, AV_TIME_BASE, (double)format_context->duration / AV_TIME_BASE);
printf("duration: %lld @ %d/sec (stream time base)\n\n", format_context->duration / AV_TIME_BASE * stream_time_base.den, stream_time_base.den);
printf("duration: %lld @ %d/sec (codec time base)\n", format_context->duration / AV_TIME_BASE * codec_time_base.den, codec_time_base.den);

double request_time = 10.0; // 10 seconds. Video's total duration is ~20sec
int64_t request_timestamp = request_time / av_q2d(stream_time_base);
printf("requested: %.2f (sec)\t-> %2lld (pts)\n", request_time, request_timestamp);

av_seek_frame(format_context, stream_index, request_timestamp, 0);

AVPacket packet;
int frame_finished;
do {
    if (av_read_frame(format_context, &packet) < 0) {
        break;
    } else if (packet.stream_index != stream_index) {
        av_free_packet(&packet);
        continue;
    }
    avcodec_decode_video2(codec_context, frame, &frame_finished, &packet);
} while (!frame_finished);

// do something with frame

int64_t received_timestamp = frame->pkt_pts;
double received_time = received_timestamp * av_q2d(stream_time_base);
printf("received:  %.2f (sec)\t-> %2lld (pts)\n\n", received_time, received_timestamp);

使用测试电影文件运行它,我得到以下输出:
    stream_time_base: 1 / 30000 = 0.00003
    codec_time_base: 50 / 2997 = 0.01668

    duration: 20062041 @ 1000000/sec (20.06 sec)
    duration: 600000 @ 30000/sec (stream time base)
    duration: 59940 @ 2997/sec (codec time base)

    requested: 10.00 (sec)  -> 300000 (pts)
    received:  0.07 (sec)   -> 2002 (pts)

时间不匹配。这里发生了什么?我究竟做错了什么?

在寻找线索时,我从libav-users邮件列表中偶然发现了这个this statement ...



…由于我在官方文档中找不到任何此类提及,这令我更加困惑。

无论如何,我更换了……
double received_time = received_timestamp * av_q2d(stream_time_base);

…和…
double received_time = received_timestamp * av_q2d(codec_time_base);

…输出更改为…
...

requested: 10.00 (sec)  -> 300000 (pts)
received:  33.40 (sec)  -> 2002 (pts)

仍然没有匹配项。 怎么了?

最佳答案

基本上是这样的:

  • 流时基是您真正感兴趣的。它是数据包时间戳的含义,也是输出帧上的pkt_pts(因为它只是从相应的数据包中复制的)。
  • 编解码器时基(如果有设置的话)只是可能写在编解码器级别 header 中的帧速率的倒数。在没有容器计时信息的情况下(例如,当您阅读原始视频时),此功能很有用,但在其他情况下,可以安全地忽略它。
  • AVFrame.pkt_pts是已解码到此帧中的数据包的时间戳。如前所述,它只是数据包的直接副本,因此位于流时基中。这是您要使用的字段(如果容器具有时间戳记)。
  • AVFrame.pts从未设置为解码时有用的任何东西,请忽略它(将来可能会替换pkt_pts,以使整个困惑情况变得不那么困惑,但就目前而言,就像这样,主要是出于历史原因)。
  • 格式上下文的持续时间以AV_TIME_BASE(即微秒)为单位。它不能处于任何流时基中,因为您可以有三个bazillion流,每个流都有其自己的时基。
  • 在查找后获得不同时间戳会遇到的问题就是查找不准确。在大多数情况下,您只能寻找最接近的关键帧,因此通常要间隔几秒钟。解码和丢弃不需要的帧必须手动完成。
  • 关于video - 我在使用libav(ffmpeg)进行帧查找/读取时使用时间戳/时基有什么问题?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18837006/

    10-11 00:39