实用指南:【代码】使用FFmpeg将YUV编码为H.264并封装为MP4,通过api接口实现。

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);
posted @ 2025-08-03 15:18  yfceshi  阅读(36)  评论(0)    收藏  举报