基本概念
YUV格式:是一种颜色编码方式,YUV分别为三个分量:‘Y’是明亮度,也就是灰度值;‘U’和‘V’是色度
YUV格式的分类:
- planar的YUV格式:先存储planar的Y像素点,在依次存储U和V像素点
- packed的YUV格式:交叉存储YUV像素点
YUV流的采样方式:
- YUV4:4:4:表示一个Y分量对应一组UV分量。
- YUV4:2:2:表示两个Y分量共用一组UV分量。
- YUV4:2:0:表示四个Y分量共用一组UV分量。
流程
api
-
int av_frame_get_buffer(AVFrame *frame, int align);
为⾳频或视频数据分配新的buffer,使用完成后,需要将引用计数-1 -
int av_image_alloc(uint8_t *pointers[4], int linesizes[4], int w, int h, enum AVPixelFormat pix_fmt,int align):
按照指定的宽、高、像素格式来分配图像内存,第一个参数为Frame的数据 -
int av_frame_make_writable(AVFrame *frame):
-
检查AVFrame->data是否可写
-
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align):
计算1帧数据的大小,参数为像素格式、图像宽、图像⾼,字节对齐方式 -
av_image_fill_arrays: 存储⼀帧像素数据存储到AVFrame对应的data buffer
核心代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavutil/time.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
int64_t get_time()
{
return av_gettime_relative() / 1000; // 换算成毫秒
}
static int encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
FILE *outfile)
{
int ret;
/* send the frame to the encoder */
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0)
{
return -1;
}
while (ret >= 0)
{
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return 0;
} else if (ret < 0) {
return -1;
}
fwrite(pkt->data, 1, pkt->size, outfile);
}
return 0;
}
int main(int argc, char **argv)
{
char *in_yuv_file = NULL;
char *out_h264_file = NULL;
FILE *infile = NULL;
FILE *outfile = NULL;
const char *codec_name = NULL;
const AVCodec *codec = NULL;
AVCodecContext *codec_ctx= NULL;
AVFrame *frame = NULL;
AVPacket *pkt = NULL;
int ret = 0;
in_yuv_file = argv[1]; // 输入YUV文件
out_h264_file = argv[2];
codec_name = argv[3];
/* 查找指定的编码器 */
codec = avcodec_find_encoder_by_name(codec_name);
if (!codec) {
fprintf(stderr, "Codec '%s' not found\n", codec_name);
exit(1);
}
codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* 设置分辨率*/
codec_ctx->width = 1280;
codec_ctx->height = 720;
/* 设置time base */
codec_ctx->time_base = (AVRational){1, 25};
codec_ctx->framerate = (AVRational){25, 1};
/* 设置I帧间隔
* 如果frame->pict_type设置为AV_PICTURE_TYPE_I, 则忽略gop_size的设置,一直当做I帧进行编码
*/
codec_ctx->gop_size = 25; // I帧间隔
codec_ctx->max_b_frames = 2; // 如果不想包含B帧则设置为0
codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
//
if (codec->id == AV_CODEC_ID_H264) {
// 相关的参数可以参考libx264.c的 AVOption options
av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);
av_opt_set(codec_ctx->priv_data, "profile", "main", 0);
av_opt_set(codec_ctx->priv_data, "tune","zerolatency",0);
}
/*
* 设置编码器参数
*/
/* 设置bitrate */
codec_ctx->bit_rate = 3000000;
/* 将codec_ctx和codec进行绑定 */
avcodec_open2(codec_ctx, codec, NULL);
// 打开输入和输出文件
infile = fopen(in_yuv_file, "rb");
outfile = fopen(out_h264_file, "wb");
// 分配pkt和frame
pkt = av_packet_alloc();
frame = av_frame_alloc();
// 为frame分配buffer
frame->format = codec_ctx->pix_fmt;
frame->width = codec_ctx->width;
frame->height = codec_ctx->height;
ret = av_frame_get_buffer(frame, 0);
// 计算出每一帧的数据 像素格式 * 宽 * 高
// 1382400
int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,
frame->height, 1);
uint8_t *yuv_buf = (uint8_t *)malloc(frame_bytes);
// 作用
int64_t begin_time = get_time();
int64_t end_time = begin_time;
int64_t all_begin_time = get_time();
int64_t all_end_time = all_begin_time;
int64_t pts = 0;
printf("start enode\n");
for (;;) {
memset(yuv_buf, 0, frame_bytes);
size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);
ret = av_frame_make_writable(frame);
int need_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,
frame->format,
frame->width, frame->height, 1);
pts += 40;
// 设置pts 计算
frame->pts = pts; // 使用采样率作为pts的单位,具体换算成秒 pts*1/采样率
begin_time = get_time();
encode(codec_ctx, frame, pkt, outfile);
end_time = get_time();
printf("encode time:%lldms\n", end_time - begin_time);
}
/* 冲刷编码器 */
encode(codec_ctx, NULL, pkt, outfile);
all_end_time = get_time();
printf("all encode time:%lldms\n", all_end_time - all_begin_time);
// 关闭文件
fclose(infile);
fclose(outfile);
// 释放内存
if(yuv_buf) {
free(yuv_buf);
}
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&codec_ctx);
printf("main finish, please enter Enter and exit\n");
getchar();
return 0;
}