19.FFmpeg学习笔记 - 编码视频

本篇文章编码视频。输入一个原始未编码的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);
    
}

猜你喜欢

转载自blog.csdn.net/whoyouare888/article/details/94722339