使用ffmpeg3.x进行YUV420P->H.264的简单转码

来源:互联网 发布:tcp20端口和21端口 编辑:程序博客网 时间:2024/06/03 13:57

本文代码是基于雷霄骅的《最简单的基于FFMPEG的视频编码器(YUV编码为H.264)》一文修改得来的。

由于雷霄骅原文中采用的API在新版本ffmpeg中,关健的编码API发生了变化,我在此基础上,参考了ffmpeg3.x的sample和header修改后得到。

主要的变化是在编码API上,原来是通过avcodec_encode_video2()来完成编码的,现在编码API变为avcodec_send_frame()/avcodec_receive_packet()组合。

avcodec_send_frame()顾名词义就是发送一帧去编码。avcodec_receive_packet()是吐出来的编码包,写到文件中就是H.264数据了。

另外我也是初学者,对个别编码参数的含义还没有完全搞懂,还有就是在将所有帧编码写入文件后,最后为什么要flush一下,也还没有搞明白,相信以后我会知道答案的^_^

以下代码在vs 2013上跑通,ffmpeg来自:ffmpeg-20170525-b946bd8-win32-dev

extern "C"{#include "libavutil/opt.h"#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libavutil/imgutils.h"};//encode one framestatic int encode(AVCodecContext *pCodecCtx, AVFrame *pFrame, AVPacket *pPkt, FILE *out_file) {    int got_packet = 0;    int ret = avcodec_send_frame(pCodecCtx, pFrame);    if (ret < 0) {        //failed to send frame for encoding        return -1;    }    while (!ret) {        ret = avcodec_receive_packet(pCodecCtx, pPkt);        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {            return 0;        }else if (ret < 0) {            //error during encoding            return -1;        }        printf("Write frame %d, size=%d\n", pPkt->pts, pPkt->size);        fwrite(pPkt->data, 1, pPkt->size, out_file);        av_packet_unref(pPkt);    }    return 0;}int _tmain(int argc, _TCHAR* argv[]){    AVFormatContext *pFormatCtx;    AVOutputFormat *pOutputFmt;    AVStream *pStream;    AVCodecContext *pCodecCtx;    AVCodec *pCodec;    AVPacket *pkt;    AVFrame *pFrame;    FILE *in_file = NULL, *out_file = NULL;    in_file = fopen("ds_480x272.yuv", "rb");    if (in_file == NULL){        printf("cannot open in file\n");        goto OUT;    }    int in_w = 480, in_h = 272;    int nFrameNum = 100;    out_file = fopen("out.h264", "wb");    if (out_file == NULL) {        printf("cannot create out file\n");        goto OUT;    }    uint8_t* pFrameBuf = NULL;    int frame_buf_size = 0;    int y_size = 0;    int nEncodedFrameCount = 0;    uint8_t endcode[] = { 0, 0, 1, 0xb7 };    av_register_all();    pFormatCtx = avformat_alloc_context();    pOutputFmt = av_guess_format(NULL, "test.h264", NULL);    pFormatCtx->oformat = pOutputFmt;    //除了以下方法,另外还可以使用avcodec_find_encoder_by_name()来获取AVCodec    pCodec = avcodec_find_encoder(pOutputFmt->video_codec);    if (!pCodec) {        //cannot find encoder        return -1;    }    pCodecCtx = avcodec_alloc_context3(pCodec);    if (!pCodecCtx) {        //failed get AVCodecContext        return -1;    }    pkt = av_packet_alloc();    if (!pkt){        return -1;    }    //pCodecCtx = pStream->codec;    pCodecCtx->codec_id = pOutputFmt->video_codec;    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;    pCodecCtx->width = in_w;    pCodecCtx->height = in_h;    pCodecCtx->time_base.num = 1;    pCodecCtx->time_base.den = 25;    //pCodecCtx->time_base = (AVRational){ 1, 25 };    pCodecCtx->bit_rate = 400000;   //???    pCodecCtx->gop_size = 250;      //???    //pCodecCtx->framerate = (AVRational){ 25, 1 };    //H264    //pCodecCtx->me_range = 16;     //???    //pCodecCtx->max_qdiff = 4;     //???    //pCodecCtx->qcompress = 0.6;       //???    pCodecCtx->qmin = 10;           //???    pCodecCtx->qmax = 51;           //???    //Optional Param    pCodecCtx->max_b_frames = 3;    //Set Option    AVDictionary *param = NULL;    //H.264    if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {        av_dict_set(&param, "preset", "slow", 0);        av_dict_set(&param, "tune", "zerolatency", 0);        //av_dict_set(&param, "profile", "main", 0);    }    //H.265    if (pCodecCtx->codec_id == AV_CODEC_ID_H265) {        av_dict_set(&param, "preset", "ultrafast", 0);        av_dict_set(&param, "tune", "zero-latency", 0);    }    if (avcodec_open2(pCodecCtx, pCodec, &param) < 0) {        //failed to open codec        return -1;    }    pFrame = av_frame_alloc();    if (!pFrame) {        fprintf(stderr, "Could not allocate the video frame data\n");        goto OUT;    }    pFrame->format = pCodecCtx->pix_fmt;    pFrame->width = pCodecCtx->width;    pFrame->height = pCodecCtx->height;    int ret = av_frame_get_buffer(pFrame, 32);    if (ret < 0) {        fprintf(stderr, "Could not allocate the video frame data\n");        goto OUT;    }    frame_buf_size = av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);    pFrameBuf = (uint8_t*)av_malloc(frame_buf_size);    av_image_fill_arrays(pFrame->data, pFrame->linesize,         pFrameBuf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);    y_size = pCodecCtx->width * pCodecCtx->height;    for (int i = 0; i < nFrameNum; i++) {        //Read raw YUV data        if (fread(pFrameBuf, 1, y_size * 3 / 2, in_file) <= 0) {            //failed to read raw YUV data            return -1;        }        else if (feof(in_file)) {            break;        }        ret = av_frame_make_writable(pFrame);        if (ret < 0) {            goto OUT;        }        pFrame->data[0] = pFrameBuf;                //Y        pFrame->data[1] = pFrameBuf + y_size;       //U        pFrame->data[2] = pFrameBuf + y_size*5/4;   //V        //PTS        pFrame->pts = i;        //encode        encode(pCodecCtx, pFrame, pkt, out_file);    }    //flush the encoder    encode(pCodecCtx, NULL, pkt, out_file);    //add sequence end code to have a real MPEG file    fwrite(endcode, 1, sizeof(endcode), out_file);    avcodec_free_context(&pCodecCtx);    avformat_free_context(pFormatCtx);    av_frame_free(&pFrame);    av_packet_free(&pkt);    av_free(pFrameBuf);OUT:    if (out_file)        fclose(out_file);    if (in_file)        fclose(in_file);    system("pause");    return 0;}