本篇文章编码视频。输入一个原始未编码的yuv文件,格式为yuv420p,宽高848*480,将其编码为h264格式。
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavutil/opt.h>
static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
FILE *outfile)
{
int ret;
/* send the frame to the encoder */
if (frame)
printf("Send frame %3"PRId64"\n", frame->pts);
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending a frame for encoding\n");
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during encoding\n");
exit(1);
}
printf("Write packet %3"PRId64" %3"PRId64" (size=%5d)\n", pkt->pts, pkt->dts, pkt->size);
fwrite(pkt->data, 1, pkt->size, outfile);
av_packet_unref(pkt);
}
}
void encode_video(void)
{
const char *outfilename = "/Users/zhw/Desktop/result.h264";
const char *infilename = "/Users/zhw/Desktop/sintel_yuv420p_848x480.yuv";
const AVCodec *codec;
AVCodecContext *c= NULL;
int i = 0, ret, x, y;
FILE *out_file, *in_file;
AVFrame *frame;
AVPacket *pkt;
/* h264编码器 */
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
pkt = av_packet_alloc();
if (!pkt)
exit(1);
/* 码率 */
c->bit_rate = 400000;
/* 视频宽高 */
c->width = 848;
c->height = 480;
/* 帧率 */
c->time_base = (AVRational){1, 25};
c->framerate = (AVRational){25, 1};
/* 每10帧编码一个I帧 */
c->gop_size = 10;
/* 非B帧之间最多有一个B帧 */
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec->id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "slow", 0);
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
exit(1);
}
out_file = fopen(outfilename, "wb");
if (!out_file) {
fprintf(stderr, "Could not open %s\n", outfilename);
exit(1);
}
in_file = fopen(infilename, "rb");
if (!in_file) {
fprintf(stderr, "Could not open %s\n", infilename);
exit(1);
}
//存放原始音频数据
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
//创建frame的buffer.由于CPU对齐的原因,得到的frame->linesize会大于等于frame->width
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate the video frame data\n");
exit(1);
}
int width = frame->width;
int height = frame->height;
uint8_t yuv[width * height * 3 / 2];
while (!feof(in_file)) {
ret = av_frame_make_writable(frame);
if (ret < 0)
exit(1);
//清空
memset(yuv, 0, width * height * 3 / 2);
//读取Y、U、V
fread(yuv, 1, width * height * 3 / 2, in_file);
//设置Y、U、V数据到frame
/* Y */
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
frame->data[0][y * frame->linesize[0] + x] = yuv[y * width + x];
}
}
/* U and V */
for (y = 0; y < height/2; y++) {
for (x = 0; x < width/2; x++) {
frame->data[1][y * frame->linesize[1] + x] = yuv[width * height + y * width / 2 + x];
frame->data[2][y * frame->linesize[2] + x] = yuv[width * height * 5 / 4 + y * width / 2 + x];
}
}
//设置pts
frame->pts = i++;
/* 编码 */
encode(c, frame, pkt, out_file);
}
/* flush the encoder */
encode(c, NULL, pkt, out_file);
fclose(out_file);
fclose(in_file);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
}