流媒体-H264协议-编码-x264学习-C++11多线程实现编码(四)

  1. 流媒体-H264协议-编码-x264学习-相关概念x264编译及文件解析(一)
  2. 流媒体-H264协议-编码-x264学习-主要结构体(二)
  3. 流媒体-H264协议-编码-x264学习-主要函数(三)
  4. 流媒体-H264协议-编码-x264学习-C++11多线程实现编码(四)

一、Encode类实现

/**
* @projectName   Testx264_01
* @author        wangbaojia
* @date          2020-12-16
* @brief         参考x264源码:example.c文件
*                读取yuv文件进行h264编码
*       //生成原生的yuv数据
*         ffmpeg -i in.mp4 -pix_fmt yuv420p -vcodec rawvideo -an out.yuv
*/
#ifndef X264ENCODE_H
#define X264ENCODE_H

#include <stdio.h>
#include <string>
#include <atomic>

#include <x264.h>
#include <x264_config.h>


class X264Encode
{

public:
    X264Encode();
    virtual ~X264Encode();
    virtual bool Open(std::string infile,std::string outfile);
    virtual void Close();
    virtual void Clear();
    /**
     * @brief Init  x264_param_t参数初始化及相关设置
     * @return
     */
    virtual bool Init();
    virtual bool Encode();
    virtual bool IsFileEnd();


private:

    std::string m_strInFileName = "";
    std::string m_strOutFileName = "";
    FILE* m_pInFp = nullptr;
    FILE* m_pOutFp = nullptr;
    std::atomic<bool> m_bIsFileEnd;

    int m_iWidth = 768;
    int m_iHeight = 320;

    /**
     * @brief m_pParam 结构体x264_param_t定义在x264.h中,用来初始化编码器
     */
    x264_param_t* m_pParam = nullptr;

    x264_t* m_pX264Handle = nullptr;

    /**
     * @brief m_pPicIn 描述一视频帧的特征
     */
    x264_picture_t* m_pPicIn = nullptr;
    x264_picture_t* m_pPicOut = nullptr;

//    x264_nal_t* m_pNals = nullptr;

    /* Colorspace type */
//    #define X264_CSP_MASK           0x00ff  /* */
//    #define X264_CSP_NONE           0x0000  /* Invalid mode     */
//    #define X264_CSP_I400           0x0001  /* monochrome 4:0:0 */
//    #define X264_CSP_I420           0x0002  /* yuv 4:2:0 planar */
//    #define X264_CSP_YV12           0x0003  /* yvu 4:2:0 planar */
//    #define X264_CSP_NV12           0x0004  /* yuv 4:2:0, with one y plane and one packed u+v */
//    #define X264_CSP_NV21           0x0005  /* yuv 4:2:0, with one y plane and one packed v+u */
//    #define X264_CSP_I422           0x0006  /* yuv 4:2:2 planar */
//    #define X264_CSP_YV16           0x0007  /* yvu 4:2:2 planar */
//    #define X264_CSP_NV16           0x0008  /* yuv 4:2:2, with one y plane and one packed u+v */
//    #define X264_CSP_YUYV           0x0009  /* yuyv 4:2:2 packed */
//    #define X264_CSP_UYVY           0x000a  /* uyvy 4:2:2 packed */
//    #define X264_CSP_V210           0x000b  /* 10-bit yuv 4:2:2 packed in 32 */
//    #define X264_CSP_I444           0x000c  /* yuv 4:4:4 planar */
//    #define X264_CSP_YV24           0x000d  /* yvu 4:4:4 planar */
//    #define X264_CSP_BGR            0x000e  /* packed bgr 24bits */
//    #define X264_CSP_BGRA           0x000f  /* packed bgr 32bits */
//    #define X264_CSP_RGB            0x0010  /* packed rgb 24bits */
//    #define X264_CSP_MAX            0x0011  /* end of list */
//    #define X264_CSP_VFLIP          0x1000  /* the csp is vertically flipped */
//    #define X264_CSP_HIGH_DEPTH     0x2000  /* the csp has a depth of 16 bits per pixel component */
    int m_icsp = X264_CSP_I420 ;/* 编码比特流的CSP(Colorspace type:颜色空间类型),视频图像色彩空间设置 */

};

#endif // X264ENCODE_H

#include "x264encode.h"

#include <iostream>
#include <chrono>

X264Encode::X264Encode()
{
    m_bIsFileEnd = true;
}

X264Encode::~X264Encode()
{
    std::cout<<"destory.."<<std::endl;

}

bool X264Encode::Open(std::string infile,std::string outfile)
{
    if(infile.length()== 0 || outfile.length() == 0)
    {
        std::cout<<"file name is empty.."<<std::endl;
        return false;
    }

    m_strInFileName = infile;
    m_strOutFileName = outfile;

    m_pInFp = fopen(const_cast<char*>(infile.c_str()),"rb");
    if(!m_pInFp)
    {
        std::cout<<"open input file failed.."<<std::endl;
        return false;
    }

    m_bIsFileEnd = false;//初始化文件读取位置

    m_pOutFp = fopen(const_cast<char*>(outfile.c_str()),"wb");
    if(!m_pInFp)
    {
        std::cout<<"open output file failed.."<<std::endl;
        return false;
    }

    return true;
}

/**
 * @brief X264Encode::Init x264_param_t参数初始化及相关设置
 * @return
 */
bool X264Encode::Init()
{
    /参数设置start
    //分配空间
    m_pParam = static_cast<x264_param_t*>(malloc(sizeof (x264_param_t)));
    if(!m_pParam)
    {
        std::cout<<"malloc x264_param_t failed"<<std::endl;
        return false;
    }

#ifdef PRESET

    if( x264_param_default_preset( m_pParam, "medium", nullptr ) < 0 )
    {

        return false;
    }
#else
    /**
     * fill x264_param_t with default values and do CPU detection
     *  函数内部第一行执行 memset( param, 0, sizeof( x264_param_t ) );因此需要先分配空间
     */
    x264_param_default(m_pParam);
#endif
//    int x264_param_default_preset( x264_param_t *param, const char *preset, const char *tune );将调用以下两个函数
//    static int param_apply_preset( x264_param_t *param, const char *preset );
//    static int param_apply_tune( x264_param_t *param, const char *tune );

    /* Configure non-default params */

    m_pParam->i_log_level = X264_LOG_NONE;//X264_LOG_DEBUG; // 打印debug信息
    /* 编码比特流的CSP(Colorspace type:颜色空间类型),视频图像色彩空间设置 */
//    m_pParam->i_csp = X264_CSP_I420;//可不设置,默认即为此格式

    m_pParam->i_width = m_iWidth;
    m_pParam->i_height = m_iHeight;

//    std::cout<<m_pParam->i_width<<std::endl;
//    m_pParam->i_bitdepth = 8; //像素比特位深度,新版增

    /* VFR input.  If 1, use timebase and timestamps for ratecontrol purposes.
     * If 0, use fps only. */
//    m_pParam->b_vfr_input = 0;

//    m_pParam->b_repeat_headers = 1; /* put SPS/PPS before each keyframe */

    /* if set, place start codes (4 bytes) before NAL units,
     * otherwise place size (4 bytes) before NAL units. */
//    m_pParam->b_annexb = 1;


    /**
     * @brief x264_param_apply_profile 设置画质级别
     * static const char * const x264_profile_names[] = { "baseline", "main", "high", "high10", "high422", "high444", 0 };
     */
    x264_param_apply_profile(m_pParam, x264_profile_names[0]);

    /* 描述一视频帧的特征
    typedef struct
    {
        int   i_type;       // 帧的类型,取值有X264_TYPE_KEYFRAME、X264_TYPE_P、
        // X264_TYPE_AUTO等。初始化为auto,则在编码过程自行控制。
        int   i_qpplus1;     // 此参数减1代表当前帧的量化参数值
        int   i_pic_struct;  // 帧的结构类型,表示是帧还是场,是逐行还是隔行,
        // 取值为枚举值 pic_struct_e,定义在x264.h中
        int   b_keyframe;    // 输出:是否是关键帧
        int64_t   i_pts;     // 一帧的显示时间戳
        int64_t   i_dts;     // 输出:解码时间戳。当一帧的pts非常接近0时,该dts值可能为负。

        //编码器参数设置,如果为NULL则表示继续使用前一帧的设置。某些参数
        //(例如aspect ratio) 由于收到H264本身的限制,只能每隔一个GOP才能改变。
        //这种情况下,如果想让这些改变的参数立即生效,则必须强制生成一个IDR帧。
        x264_param_t    *param;
        x264_image_t     img;         // 存放一帧图像的真实数据
        x264_image_properties_t    prop;
        x264_hrd_t    hrd_timing;     // 输出:HRD时间信息,仅当i_nal_hrd设置了才有效
        void    *opaque;              // 私有数据存放区,将输入数据拷贝到输出帧中
    } x264_picture_t ;
    */

    m_pPicIn = static_cast<x264_picture_t*>(malloc(sizeof (x264_picture_t)));
    m_pPicOut = static_cast<x264_picture_t*>(malloc(sizeof (x264_picture_t)));

    /* x264_picture_alloc:
     *  alloc data for a picture. You must call x264_picture_clean on it.
     *  returns 0 on success, or -1 on malloc failure or invalid colorspace. */
    x264_picture_alloc(m_pPicIn,m_icsp,m_pParam->i_width,m_pParam->i_height);

    /* x264_picture_init:
     *  initialize an x264_picture_t.  Needs to be done if the calling application
     *  allocates its own x264_picture_t 而不是使用 using x264_picture_alloc. */
    x264_picture_init(m_pPicOut);


    参数设置end/

    /**
     * 用于打开编码器,其中初始化了libx264编码所需要的各种变量
     * create a new encoder handler, all parameters from x264_param_t are copied
     *
        x264_validate_parameters():检查输入参数(例如输入图像的宽高是否为正数)。
        x264_predict_16x16_init():初始化Intra16x16帧内预测汇编函数。
        x264_predict_4x4_init():初始化Intra4x4帧内预测汇编函数。
        x264_pixel_init():初始化像素值计算相关的汇编函数(包括SAD、SATD、SSD等)。
        x264_dct_init():初始化DCT变换和DCT反变换相关的汇编函数。
        x264_mc_init():初始化运动补偿相关的汇编函数。
        x264_quant_init():初始化量化和反量化相关的汇编函数。
        x264_deblock_init():初始化去块效应滤波器相关的汇编函数。
        x264_lookahead_init():初始化Lookahead相关的变量。
        x264_ratecontrol_new():初始化码率控制相关的变量。
     */
    m_pX264Handle = x264_encoder_open(m_pParam);
    if(!m_pX264Handle)
    {
        std::cout<<"x264_encoder_open failed"<<std::endl;
        return false;
    }


    return true;

}

bool X264Encode::Encode()
{
    static int isPts = 0;
    size_t iLumaSize = m_pParam->i_width * m_pParam->i_height;
    size_t iChromaSize = iLumaSize / 4;

    int iNnal;
    x264_nal_t *pNals;


    //读取yuv数据填充m_pPicIn
    if(fread(m_pPicIn->img.plane[0],1,iLumaSize,m_pInFp) != iLumaSize)         //Y
    {
        return false;
    }
    if(IsFileEnd()) return false;

    if(fread(m_pPicIn->img.plane[1],1,iChromaSize,m_pInFp) != iChromaSize)     //U
    {
        return false;
    }
    if(IsFileEnd()) return false;

    if(fread(m_pPicIn->img.plane[2],1,iChromaSize,m_pInFp) != iChromaSize)     //V
    {
        return false;
    }
    if(IsFileEnd()) return false;

    m_pPicIn->i_pts = isPts;


    /* x264_encoder_encode:用于编码一帧YUV为H.264码流
     *      encode one picture.
     *      *pi_nal is the number of NAL units outputted in pp_nal.
     *      returns the number of bytes in the returned NALs.
     *      returns negative on error and zero if no NAL units returned.
     *      the payloads of all output NALs are guaranteed to be sequential in memory. */
    int iFrameSize = x264_encoder_encode(m_pX264Handle,&pNals,&iNnal,m_pPicIn,m_pPicOut);
    if(iFrameSize < 0)
    {
        std::cout<<"x264_encoder_encode failed"<<std::endl;
        return false;
    }

//    std::cout<<"Encode iNals="<<iNnal<<std::endl;
    //生成多个NAL单元,进行处理
    for(int i = 0; i < iNnal;i++)
    {
        if( !fwrite( pNals[i].p_payload, 1,pNals[i].i_payload, m_pOutFp))
        {
            std::cout<<"fwrite failed"<<std::endl;
            return false;
        }
    }

    return true;

}

bool X264Encode::IsFileEnd()
{
    if(!m_pInFp) return true;

    if(feof(m_pInFp))
    {
        std::cout<<"read to file end"<<std::endl;
        return true;
    }
    return false;

}

void X264Encode::Close()
{
    if(m_pPicIn)
    {
        free(m_pPicIn);
    }

    if(m_pPicOut)
    {
        free(m_pPicOut);
    }

    if(m_pParam)
    {
        free(m_pParam);
    }


    if(m_pInFp)
    {
        fclose(m_pInFp);
    }

    if(m_pOutFp)
    {
        fclose(m_pOutFp);
    }

}

void X264Encode::Clear()
{
    int iNnal;
    x264_nal_t *pNals;

    /* Flush delayed frames */
    std::cout<<"buffer size="<<x264_encoder_delayed_frames( m_pX264Handle)<<std::endl;

    while(x264_encoder_delayed_frames( m_pX264Handle))
    {
        //输入为nullptr
        int iFrameSize = x264_encoder_encode( m_pX264Handle, &pNals, &iNnal, nullptr, m_pPicOut );
//        std::cout<<"flush iNals="<<iNnal<<std::endl;
        if( iFrameSize < 0 )
            return;

        for(int i = 0; i < iNnal;i++)
        {
            if( !fwrite( pNals[i].p_payload, 1,pNals[i].i_payload, m_pOutFp))
            {
                std::cout<<"fwrite failed"<<std::endl;
                return;
            }
        }
    }

    if(m_pPicIn)//init分配需手动释放
    {
        x264_picture_clean(m_pPicIn);
    }

    if(m_pX264Handle)
    {
        x264_encoder_close(m_pX264Handle);
    }

}

二、完整代码下载

c++11多线程实现libx264编码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值