如何在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
相关产品推荐
相关产品推荐

