YUV数据来源
- 摄像头直接采集的原始视频流通常为YUV格式(如YUV420),尤其是安防摄像头和网络摄像头
- 智能手机、平板电脑的摄像头通过硬件接口
- 视频会议软件(如Zoom、腾讯会议)从摄像头捕获YUV帧,进行预处理(降噪、美颜)后再编码
- 专业摄像机或采集卡输出的高清视频(如YUV422)用于后期制作,或直播推流前的中间处理
- 自动驾驶、人脸识别等AI模型常直接处理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(¶m, "preset", "slow", 0); // 编码质量与速度平衡
av_dict_set(¶m, "tune", "zerolatency", 0); // 低延迟模式[citation:2]
// 打开编码器
avcodec_open2(codec_ctx, codec, ¶m);
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);