二、打开视频文件

视频解码一般流程:

1. 打开视频文件
2. 查找视频流(音频流,视频流,字母流)
3. 获取解码器,创建解码器上下文(AVCodecContext)并初始化
4. 创建 AVPacket 和 AVFrame,用于接收存储编码后的媒体数据和解码后的数据
5. 读取 AVPacket,向解码器发送 AVPacket,接收解码后的数据
6. 将解码后的数据转成特定的数据格式(如: 解码后的图像一般是 yuv 数据格式,需转成 rgb)
7. 释放内存
extern "C" {        // 用C规则编译指定的代码
#include <libavformat/avformat.h>
}
#include <iostream>
using namespace std;
int main() {
    AVFormatContext *fmt_ctx = NULL;
    const char *filename = "https://stream7.iqilu.com/10339/upload_transcode/202002/09/20200209104902N3v5Vpxuvb.mp4";
    // const char *filename = "test.mp4"; // 替换成自己本地路径
    // 已不再需要
    // avformat_network_init();

    // 打开输入文件
    int ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL);
    if (ret < 0) {
        return ret;
    }
    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0) {
            // 没有找到视频流信息
        return ret;
    }
    // 打印时长(单位:秒)
    printf("时长: %.2f 秒\n", (double)fmt_ctx->duration / AV_TIME_BASE);

    // 查找视频流
    int video_stream_idx = -1;
    /*
    遍历查找
    for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_idx = i;
            break;
        }
    }*/
    video_stream_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);

    if (video_stream_idx >= 0) {
        AVStream *stream = fmt_ctx->streams[video_stream_idx];
        cout << "帧率: " << av_q2d(stream->avg_frame_rate) << endl;
        cout << "分辨率: " << stream->codecpar->width << " " <<
               stream->codecpar->height  << endl;
        cout << "编码格式: " << avcodec_get_name(stream->codecpar->codec_id)  << endl;
    }

    // 关闭文件并释放上下文
    avformat_close_input(&fmt_ctx);

    return 0;
}

函数说明

/**
 * Open an input stream and read the header. The codecs are not opened.
 * The stream must be closed with avformat_close_input().
 *
 * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
 *           May be a pointer to NULL, in which case an AVFormatContext is allocated by this
 *           function and written into ps.
 *           Note that a user-supplied AVFormatContext will be freed on failure.
 * @param url URL of the stream to open.
 * @param fmt If non-NULL, this parameter forces a specific input format.
 *            Otherwise the format is autodetected.
 * @param options  A dictionary filled with AVFormatContext and demuxer-private options.
 *                 On return this parameter will be destroyed and replaced with a dict containing
 *                 options that were not found. May be NULL.
 *
 * @return 0 on success, a negative AVERROR on failure.
 *
 * @note If you want to use custom IO, preallocate the format context and set its pb field.
 */
int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options);

ps
输出参数,指向 AVFormatContext 的指针。函数成功后,该指针将被初始化为有效的上下文。使用完毕后需通过 avformat_close_input() 释放。
url
输入媒体的路径或 URL:

本地文件:"input.mp4"
网络流:"rtsp://example.com/stream"
设备:"v4l2:/dev/video0"(Linux 摄像头)

fmt
指定输入格式(如 av_find_input_format(“mp4”))。通常设为 NULL,让 FFmpeg 自动探测格式。

const AVInputFormat *fmt = av_find_input_format("mp4");
//const AVInputFormat *fmt = av_find_input_format("rtsp");
//const AVInputFormat *fmt = av_find_input_format("rawvideo");
// 打开输入文件,强制使用指定格式
avformat_open_input(&fmt_ctx, filename, fmt, NULL);

options
传递额外选项(如网络超时、缓冲区大小)

AVDictionary *opts = NULL;
av_dict_set(&opts, "timeout", "5000000", 0); // 5 秒超时(微秒)
avformat_open_input(&fmt_ctx, url, NULL, &opts);
av_dict_free(&opts);
/**
 * Find the "best" stream in the file.
 * The best stream is determined according to various heuristics as the most
 * likely to be what the user expects.
 * If the decoder parameter is non-NULL, av_find_best_stream will find the
 * default decoder for the stream's codec; streams for which no decoder can
 * be found are ignored.
 *
 * @param ic                media file handle
 * @param type              stream type: video, audio, subtitles, etc.
 * @param wanted_stream_nb  user-requested stream number,
 *                          or -1 for automatic selection
 * @param related_stream    try to find a stream related (eg. in the same
 *                          program) to this one, or -1 if none
 * @param decoder_ret       if non-NULL, returns the decoder for the
 *                          selected stream
 * @param flags             flags; none are currently defined
 * @return  the non-negative stream number in case of success,
 *          AVERROR_STREAM_NOT_FOUND if no stream with the requested type
 *          could be found,
 *          AVERROR_DECODER_NOT_FOUND if streams were found but no decoder
 * @note  If av_find_best_stream returns successfully and decoder_ret is not
 *        NULL, then *decoder_ret is guaranteed to be set to a valid AVCodec.
 * 
 */
 int av_find_best_stream(AVFormatContext *ic,
                        enum AVMediaType type,
                        int wanted_stream_nb,
                        int related_stream,
                        AVCodec **decoder_ret,
                        int flags);

type
enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse
AVMEDIA_TYPE_NB
};
如果 decoder_ret 不为 NULL,自动查找并返回与该流匹配的解码器

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐