FFmpeg - 基本 API大全(视频编解码相关的)

发布于:2025-08-12 ⋅ 阅读:(19) ⋅ 点赞:(0)

FFmpeg 是一个强大的多媒体处理库,下面我将介绍其基本 API 并结合网络流/本地文件解码示例说明每个 API 的功能和用法。

一、核心 API 分类

1. 格式处理 API (libavformat)

2. 编解码 API (libavcodec)

3. 实用工具 API (libavutil)

4. 图像缩放/像素格式转换 API (libswscale)

二、基本 API 详解及示例

1. 初始化相关 API

c

// 注册所有编解码器和格式 (4.0+已废弃,但许多示例仍保留)
av_register_all();

// 初始化网络库 (用于网络流)
avformat_network_init();

2. 打开输入流 API

c

AVFormatContext *pFormatCtx = NULL;

// 本地文件打开
const char *url = "input.mp4";
if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {
    printf("无法打开输入文件\n");
    return -1;
}

// 网络流打开 (如RTMP)
const char *rtmp_url = "rtmp://live.example.com/app/stream";
AVDictionary *options = NULL;
av_dict_set(&options, "rtsp_transport", "tcp", 0); // 设置RTSP over TCP
av_dict_set(&options, "stimeout", "5000000", 0);  // 设置超时5秒

if (avformat_open_input(&pFormatCtx, rtmp_url, NULL, &options) != 0) {
    printf("无法打开网络流\n");
    return -1;
}

3. 获取流信息 API

c

if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
    printf("无法获取流信息\n");
    return -1;
}

// 打印流信息
av_dump_format(pFormatCtx, 0, url, 0);

4. 查找视频流 API

c

int videoStream = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
    if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
        videoStream = i;
        break;
    }
}
if (videoStream == -1) {
    printf("未找到视频流\n");
    return -1;
}

5. 编解码器设置 API

c

// 获取编解码器参数
AVCodecParameters *pCodecParams = pFormatCtx->streams[videoStream]->codecpar;

// 查找解码器
const AVCodec *pCodec = avcodec_find_decoder(pCodecParams->codec_id);
if (!pCodec) {
    printf("不支持的解码器\n");
    return -1;
}

// 创建解码器上下文
AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);
if (!pCodecCtx) {
    printf("无法分配编解码器上下文\n");
    return -1;
}

// 复制参数到上下文
if (avcodec_parameters_to_context(pCodecCtx, pCodecParams) < 0) {
    printf("无法复制编解码器参数\n");
    return -1;
}

// 打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
    printf("无法打开解码器\n");
    return -1;
}

6. 帧和包处理 API

c

// 分配帧
AVFrame *pFrame = av_frame_alloc();
if (!pFrame) {
    printf("无法分配帧\n");
    return -1;
}

// 初始化包
AVPacket packet;
av_init_packet(&packet);
packet.data = NULL;
packet.size = 0;

7. 解码循环 API

c

while (av_read_frame(pFormatCtx, &packet) >= 0) {
    if (packet.stream_index == videoStream) {
        // 发送数据包到解码器
        int ret = avcodec_send_packet(pCodecCtx, &packet);
        if (ret < 0) {
            printf("发送解码包出错\n");
            continue;
        }

        // 接收解码后的帧
        while (ret >= 0) {
            ret = avcodec_receive_frame(pCodecCtx, pFrame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                break;
            } else if (ret < 0) {
                printf("解码出错\n");
                return -1;
            }

            // 成功解码一帧,可以处理帧数据
            printf("解码帧: width=%d, height=%d, format=%d, pts=%lld\n",
                  pFrame->width, pFrame->height, pFrame->format, pFrame->pts);
            
            // 这里可以添加帧处理代码...
        }
    }
    av_packet_unref(&packet); // 释放包
}

8. 刷新解码器 API

c

// 发送空包刷新解码器
avcodec_send_packet(pCodecCtx, NULL);

// 接收剩余的帧
while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
    printf("刷新解码器获取的帧: pts=%lld\n", pFrame->pts);
    // 处理帧...
}

9. 资源释放 API

c

av_frame_free(&pFrame);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFormatCtx);
avformat_network_deinit(); // 如果之前调用了avformat_network_init()

三、完整网络流解码示例

c

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>

int main() {
    AVFormatContext *pFormatCtx = NULL;
    AVCodecContext *pCodecCtx = NULL;
    const AVCodec *pCodec = NULL;
    AVFrame *pFrame = NULL;
    AVPacket packet;
    int videoStream = -1;
    
    // 初始化网络库
    avformat_network_init();
    
    // 打开网络流 (这里使用RTMP示例)
    const char *url = "rtmp://live.example.com/app/stream";
    AVDictionary *options = NULL;
    av_dict_set(&options, "rtsp_transport", "tcp", 0);
    av_dict_set(&options, "stimeout", "5000000", 0);
    
    if (avformat_open_input(&pFormatCtx, url, NULL, &options) != 0) {
        printf("无法打开网络流\n");
        return -1;
    }
    
    // 获取流信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("无法获取流信息\n");
        return -1;
    }
    
    // 查找视频流
    for (int i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
            break;
        }
    }
    
    if (videoStream == -1) {
        printf("未找到视频流\n");
        return -1;
    }
    
    // 设置解码器
    AVCodecParameters *pCodecParams = pFormatCtx->streams[videoStream]->codecpar;
    pCodec = avcodec_find_decoder(pCodecParams->codec_id);
    if (!pCodec) {
        printf("不支持的解码器\n");
        return -1;
    }
    
    pCodecCtx = avcodec_alloc_context3(pCodec);
    avcodec_parameters_to_context(pCodecCtx, pCodecParams);
    
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("无法打开解码器\n");
        return -1;
    }
    
    // 准备帧和包
    pFrame = av_frame_alloc();
    av_init_packet(&packet);
    
    // 解码循环
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        if (packet.stream_index == videoStream) {
            if (avcodec_send_packet(pCodecCtx, &packet) < 0) {
                printf("发送解码包出错\n");
                continue;
            }
            
            while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
                printf("解码帧: width=%d, height=%d, format=%d, pts=%lld\n",
                      pFrame->width, pFrame->height, pFrame->format, pFrame->pts);
                // 实际应用中这里可以处理帧数据
            }
        }
        av_packet_unref(&packet);
    }
    
    // 刷新解码器
    avcodec_send_packet(pCodecCtx, NULL);
    while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
        printf("刷新解码器获取的帧\n");
    }
    
    // 清理
    av_frame_free(&pFrame);
    avcodec_free_context(&pCodecCtx);
    avformat_close_input(&pFormatCtx);
    avformat_network_deinit();
    
    return 0;
}

四、关键 API 功能总结

API 功能描述 常用场景
av_register_all() 注册所有编解码器和格式 旧版本初始化 (4.0+已废弃)
avformat_network_init() 初始化网络库 处理网络流前调用
avformat_open_input() 打开媒体文件或流 本地文件/网络流输入
avformat_find_stream_info() 获取流信息 打开输入后获取详细信息
avcodec_find_decoder() 查找解码器 根据codec_id查找解码器
avcodec_alloc_context3() 创建编解码器上下文 解码器/编码器设置
avcodec_parameters_to_context() 复制参数到上下文 设置解码器参数
avcodec_open2() 打开编解码器 初始化编解码器
av_read_frame() 读取数据包 解码循环中获取数据
avcodec_send_packet() 发送数据包到解码器 现代解码流程
avcodec_receive_frame() 接收解码后的帧 现代解码流程
av_packet_unref() 释放数据包 处理完包后释放资源
av_frame_free() 释放帧 处理完帧后释放资源

五、不同场景下的注意事项

  1. 本地文件解码:

    • 不需要调用avformat_network_init()

    • 通常不需要设置超时等网络参数

  2. 网络流解码:

    • 必须调用avformat_network_init()

    • 建议设置合理的超时参数

    • 对于RTSP流,建议强制使用TCP传输

  3. 实时流处理:

    • 可能需要设置realtime标志

    • 可能需要禁用缓冲av_dict_set(&options, "fflags", "nobuffer", 0)

  4. 错误处理:

    • 所有API调用都应检查返回值

    • 网络流需要更健壮的错误恢复机制

这个指南涵盖了FFmpeg解码的基本API和使用方法,实际应用中可能需要根据具体需求进行调整和扩展。