You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在libav中为MJPEG流强制设置60FPS输入帧率

解决Libav解析MJPEG流时强制设置60FPS的问题

问题分析

MJPEG流多数情况下没有内嵌帧率或时间戳信息,Libav会默认用25FPS填充r_frame_rate等参数,这就是实际60FPS流被识别为25FPS的原因。你直接修改AVStream参数无效,是因为这些参数在avformat_find_stream_info执行后就已确定,后续修改不会影响Libav读取流的逻辑。

可行解决方案

方案1:打开流前强制指定帧率

在调用avformat_open_input前,通过输入选项强制Libav以60FPS解析流,这是最直接的处理方式:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

int main(int argc, char *argv[]) {
    AVFormatContext *fmt_ctx = NULL;
    AVCodec *codec = NULL;
    AVPacket pkt;
    AVDictionary *options = NULL;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <mjpeg-stream-url>\n", argv[0]);
        exit(1);
    }

    av_register_all();

    // 强制设置解析帧率为60FPS
    av_dict_set(&options, "framerate", "60", 0);

    if (avformat_open_input(&fmt_ctx, argv[1], NULL, &options) < 0) {
        fprintf(stderr, "Could not open stream '%s'\n", argv[1]);
        exit(1);
    }
    av_dict_free(&options); // 释放选项字典

    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        fprintf(stderr, "Could not find stream information\n");
        exit(1);
    }

    int video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);

    if (video_stream_index < 0) {
        fprintf(stderr, "Could not find video stream\n");
        exit(1);
    }

    AVStream *video_stream = fmt_ctx->streams[video_stream_index];
    // 可验证参数是否生效
    printf("r_frame_rate: %d/%d\n", video_stream->r_frame_rate.num, video_stream->r_frame_rate.den);
    printf("time_base: %d/%d\n", video_stream->time_base.num, video_stream->time_base.den);

    av_init_packet(&pkt);

    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == video_stream_index) {
            // Sending it to an websocket ....
        }
        av_packet_unref(&pkt);
    }

    avformat_close_input(&fmt_ctx);
    avformat_free_context(fmt_ctx);

    return 0;
}

方案2:手动为数据包生成时间戳

如果方案1不生效(比如部分特殊MJPEG流格式不支持该选项),可以忽略Libav生成的默认时间戳,自行按60FPS节奏给每个数据包设置pts和dts:

// ... 其余代码与方案1一致 ...

AVStream *video_stream = fmt_ctx->streams[video_stream_index];
// 手动设置时间基准为1/60
video_stream->time_base = (AVRational){1, 60};
int64_t frame_counter = 0;

av_init_packet(&pkt);

while (av_read_frame(fmt_ctx, &pkt) >= 0) {
    if (pkt.stream_index == video_stream_index) {
        // 按60FPS手动生成时间戳
        pkt.pts = frame_counter;
        pkt.dts = frame_counter;
        pkt.duration = 1; // 每帧持续1个时间基准单位,即1/60秒
        frame_counter++;

        // Sending it to an websocket ....
    }
    av_packet_unref(&pkt);
}

// ... 后续代码 ...

注意点

  • 你原代码存在一处错误:stream->time_base中的stream未定义,应改为video_stream->time_base。
  • 如果WebSocket接收端不需要依赖时间戳渲染,直接转发数据包即可;若需按正确帧率播放,必须保证时间戳的准确性。

内容的提问来源于stack exchange,提问作者Christoph

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:21:48