FFmpeg h264编码

通过FFmpeg进行h264的编码流程:
1.通过名字(h264编码器名字是libx264)查找编码器:avcodec_find_encoder_by_name()
2.设置编码器的参数(比如码率、分辨率、帧率、时间基等)。
3.打开编码器avcodec_open2()
4.对帧数据进行编码avcodec_send_frame()/avcodec_receive_packet(),编码完后写入文件。(在实际使用中帧数据来源于解码后的元数据或来源于摄像头,本文示例的帧数据时人工添加了模拟数据。)


/*h264编码*/
#include 
#include 
#include 

#include 

#include 
#include 

// 对每一帧进行编码
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" (size=%5d)\n", pkt->pts, pkt->size);
        fwrite(pkt->data, 1, pkt->size, outfile);
        av_packet_unref(pkt);
    }
}

int main(int argc, char **argv)
{
    const char *filename, *codec_name;  // 输出文件路径和编码器名字,由运行程序时传入参数(要编码的内容是从摄像头获取的)
    const AVCodec *codec;  // 编码器
    AVCodecContext *c= NULL;  // 编码上下文环境
    // got_output用于标记一帧是否压缩成功
    int i, ret, x, y, got_output;
    FILE *f;
    AVFrame *frame; // 原始帧(未压缩的数据)
    AVPacket pkt;
    uint8_t endcode[] = { 0, 0, 1, 0xb7 };

    if (argc <= 2) {
        fprintf(stderr, "Usage: %s  \n", argv[0]);
        exit(0);
    }
    filename = argv[1];
    codec_name = argv[2];// (h264编码器名字是libx264)

    // avcodec_register_all();

    // 通过名字查找编码器
    codec = avcodec_find_encoder_by_name(codec_name);
    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);
    }

    // 设置码率
    c->bit_rate = 400000;
    // 设置视频宽高
    c->width = 352;
    c->height = 288;
    // 设置时间基、帧率(时间基根据帧率而变化)
    c->time_base = (AVRational){1, 25};
    c->framerate = (AVRational){25, 1};

    // 设置多少帧产生一个关键帧,也就是一组帧是多少帧
    c->gop_size = 10;
    // 设置b帧
    c->max_b_frames = 1;
    // 要编码的原始数据的YUV格式
    c->pix_fmt = AV_PIX_FMT_YUV420P;

    // 如果编码器id是h264,
    if (codec->id == AV_CODEC_ID_H264)
        // preset表示采用一个预先设定好的参数集,级别是slow,slow表示压缩速度是慢的,慢的可以保证视频质量,用快的会降低视频质量
        av_opt_set(c->priv_data, "preset", "slow", 0);

    // 打开编码器
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    // 
    f = fopen(filename, "wb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }

    // 初始化帧并设置帧的YUV格式和分辨率
    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;

    ret = av_frame_get_buffer(frame, 32);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate the video frame data\n");
        exit(1);
    }

    // 这里是人工添加数据模拟生成1秒钟(25帧)的视频(真实应用中是从摄像头获取的原始数据,摄像头拿到数据后会传给编码器,然后编码器进行编码形成一帧帧数据。)
    for (i = 0; i < 25; i++) {
        av_init_packet(&pkt);
        pkt.data = NULL;    // packet data will be allocated by the encoder
        pkt.size = 0;

        // 强制输出写入文件
        fflush(stdout);

        /* make sure the frame data is writable */
        ret = av_frame_make_writable(frame);
        if (ret < 0)
            exit(1);

        // 下面2个循环是人工往frame里面添的数据
        /* Y */
        for (y = 0; y < c->height; y++) {
            for (x = 0; x < c->width; x++) {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
            }
        }

        /* Cb and Cr */
        for (y = 0; y < c->height/2; y++) {
            for (x = 0; x < c->width/2; x++) {
                frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
            }
        }

        frame->pts = i;

        // 进行编码压缩
        encode(c,frame,&pkt,f);
    }

  
    /* flush the encoder */
    encode(c, NULL, &pkt, f);

    /* add sequence end code to have a real MPEG file */
    fwrite(endcode, 1, sizeof(endcode), f);
    fclose(f);

    avcodec_free_context(&c);
    av_frame_free(&frame);

    return 0;
}

你可能感兴趣的:(FFmpeg h264编码)