ffmpeg3.x 编码实现

首先向雷神致敬!本文是在雷神的基础上进行修改,基于ffmpeg3.X版本,进行修改,本地测试通过。

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern "C"
{
    #include <libavcodec\avcodec.h>  
    #include <libavformat\avformat.h>  
    #include <libswscale\swscale.h>  
    #include <libavutil\pixfmt.h>  
    #include <libavutil\imgutils.h>  
    #include <libavutil/opt.h>
}


extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
};

//encode one frame
static 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 *pPkt;
    AVFrame *pFrame;

    /*读入文件,并创建输出文件*/
    FILE *in_file  = NULL;
    FILE *out_file = NULL;
    in_file = fopen("1920X1080.yuv", "rb");
    if (in_file == NULL) {
        printf("cannot open input file\n");
        return -1;
    }

    out_file = fopen("out.h264", "wb");
    if (out_file == NULL) {
        printf("cannot create out file\n");
        return -1;
    }

    int nWidth = 1920;
    int nHeight = 1080;
    int nFrameNum = 100;
    unsigned char* pFrameBuf = NULL;//存储buffer
    int nFrameBufSize = 0;//buffer大小
    int nReadSize = 0;
    int nEncodedFrameCount = 0;
    unsigned char endcode[] = { 0, 0, 1, 0xb7 };

    av_register_all();//初始化

    pFormatCtx = avformat_alloc_context();//申请AVFormatContext
    pOutputFmt = av_guess_format(NULL, "out.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);//申请AVCodecContext,并初始化。
    if (!pCodecCtx) {
        //failed get AVCodecContext
        return -1;
    }

    pPkt = av_packet_alloc();//申请Packet资源

    if (!pPkt) {
        return -1;
    }
    //编码参数设定
    pCodecCtx->codec_id = pOutputFmt->video_codec;   //编码类型对应的ID。
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;      //编码类型,音频、视频等等。
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;         //像素格式。
    pCodecCtx->width = nWidth;                       //宽
    pCodecCtx->height = nHeight;                     //高
    pCodecCtx->time_base.num = 1;                    //时间戳,分子
    pCodecCtx->time_base.den = 25;                   //时间戳,分母
    pCodecCtx->bit_rate = 400000;                    //比特率
    pCodecCtx->gop_size = 250;                       //连续的画面组
    pCodecCtx->qmin = 10;                            //最小量化参数
    pCodecCtx->qmax = 51;                            //最大量化参数
    pCodecCtx->max_b_frames = 3;                     //最大B帧

    //Set Option
    AVDictionary *param = NULL;
    //H.264
    if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
        av_dict_set(¶m, "preset", "slow", 0);
        av_dict_set(¶m, "tune", "zerolatency", 0);
        //av_dict_set(¶m, "profile", "main", 0);
    }
    //H.265
    if (pCodecCtx->codec_id == AV_CODEC_ID_H265) {
        av_dict_set(¶m, "preset", "ultrafast", 0);
        av_dict_set(¶m, "tune", "zero-latency", 0);
    }
    // 用已有的AVCodec 初始化AVCodecContext
    if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) {
        //failed to open codec
        return -1;
    }

    pFrame = av_frame_alloc();//申请AVFrame
    if (!pFrame) {
        fprintf(stderr, "Could not allocate the video frame data\n");
        return -1;
    }
    pFrame->format = pCodecCtx->pix_fmt;
    pFrame->width = pCodecCtx->width;
    pFrame->height = pCodecCtx->height;

    int ret = av_frame_get_buffer(pFrame, 32);//申请buffer大小。
    if (ret < 0) {
        fprintf(stderr, "Could not allocate the video frame data\n");
        return -1;
    }
    /*计算buffer大小*/
    nFrameBufSize = av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);

    pFrameBuf = (unsigned char *)av_malloc(nFrameBufSize);
    av_image_fill_arrays(pFrame->data, pFrame->linesize, pFrameBuf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);

    nReadSize = pCodecCtx->width * pCodecCtx->height;

    for (int i = 0; i < nFrameNum; i++) {
        //读取YUV数据,读取大小为nReadSize
        if (fread(pFrameBuf, 1, nReadSize * 3 / 2, in_file) <= 0) {
            //读取数据失败
            return -1;
        }
        else if (feof(in_file)) {
            break;
        }

        ret = av_frame_make_writable(pFrame);//判断帧数据是否可写。
        if (ret < 0) {
            return -1;
        }

        pFrame->data[0] = pFrameBuf;                    //Y
        pFrame->data[1] = pFrameBuf + nReadSize;           //U
        pFrame->data[2] = pFrameBuf + nReadSize * 5 / 4;   //V
        pFrame->pts = i;                                //PTS
        
        //encode
        encode(pCodecCtx, pFrame, pPkt, out_file);
    }

    //flush the encoder
    encode(pCodecCtx, NULL, pPkt, out_file);

    //写数据
    fwrite(endcode, 1, sizeof(endcode), out_file);

    avcodec_free_context(&pCodecCtx);
    avformat_free_context(pFormatCtx);
    av_frame_free(&pFrame);
    av_packet_free(&pPkt);
    av_free(pFrameBuf);

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值