使用FFmpeg将YUV编码为H.264并封装为MP4,通过api接口实现

发布于:2025-07-02 ⋅ 阅读:(21) ⋅ 点赞:(0)

YUV数据来源

  • 摄像头直接采集的原始视频流通常为YUV格式(如YUV420),尤其是安防摄像头和网络摄像头
  • 智能手机、平板电脑的摄像头通过硬件接口
  • 视频会议软件(如Zoom、腾讯会议)从摄像头捕获YUV帧,进行预处理(降噪、美颜)后再编码
  • 专业摄像机或采集卡输出的高清视频(如YUV422)用于后期制作,或直播推流前的中间处理
  • 自动驾驶、人脸识别等AI模型常直接处理YUV数据

一、调用流程

初始化FFmpeg
配置编码器参数
创建输出上下文
设置封装格式
循环读取YUV帧
编码与封装
资源释放

二、关键步骤详解

1. 初始化组件
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>

// 注册所有编解码器和封装格式(新版本可省略)
avformat_network_init();  // 若需网络功能[citation:2][citation:5]
2. 配置编码器参数
// 查找H.264编码器
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);

// 设置编码参数
codec_ctx->width = 1280;                   // 分辨率
codec_ctx->height = 720;
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;   // 输入YUV格式
codec_ctx->time_base = (AVRational){1, 25}; // 帧率25fps
codec_ctx->bit_rate = 4000000;             // 码率4Mbps
codec_ctx->gop_size = 25;                  // 关键帧间隔[citation:2][citation:6]

// 高级参数(可选)
AVDictionary *param = NULL;
av_dict_set(&param, "preset", "slow", 0);  // 编码质量与速度平衡
av_dict_set(&param, "tune", "zerolatency", 0); // 低延迟模式[citation:2]

// 打开编码器
avcodec_open2(codec_ctx, codec, &param);
3. 创建MP4封装上下文
AVFormatContext *fmt_ctx = NULL;
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "output.mp4");

// 添加视频流
AVStream *stream = avformat_new_stream(fmt_ctx, NULL);
avcodec_parameters_from_context(stream->codecpar, codec_ctx);

// 打开输出文件
avio_open(&fmt_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);
avformat_write_header(fmt_ctx, NULL);  // 写入文件头[citation:2][citation:5]
4. 处理YUV数据并编码
AVFrame *frame = av_frame_alloc();
frame->width = codec_ctx->width;
frame->height = codec_ctx->height;
frame->format = codec_ctx->pix_fmt;
av_frame_get_buffer(frame, 32);  // 分配帧内存

// 读取YUV文件(示例:YUV420P格式)
FILE *yuv_file = fopen("input.yuv", "rb");
int y_size = frame->width * frame->height;

while (fread(frame->data[0], 1, y_size * 3/2, yuv_file) > 0) {
    frame->pts = frame_count++;  // 设置时间戳

    // 发送帧到编码器
    avcodec_send_frame(codec_ctx, frame);

    // 接收编码后的包
    AVPacket *pkt = av_packet_alloc();
    while (avcodec_receive_packet(codec_ctx, pkt) == 0) {
        av_packet_rescale_ts(pkt, codec_ctx->time_base, stream->time_base);
        av_interleaved_write_frame(fmt_ctx, pkt);  // 写入封装文件
        av_packet_unref(pkt);
    }
}
5. 收尾处理
// 冲刷编码器缓冲区
avcodec_send_frame(codec_ctx, NULL);
while (avcodec_receive_packet(codec_ctx, pkt) == 0) {
    av_interleaved_write_frame(fmt_ctx, pkt);
}

// 写入文件尾并释放资源
av_write_trailer(fmt_ctx);
avcodec_free_context(&codec_ctx);
avformat_close_input(&fmt_ctx);

网站公告

今日签到

点亮在社区的每一天
去签到