1. 程式人生 > >畢設系列之Libx264實時視訊流(YUV 420P轉H264視訊編碼篇)

畢設系列之Libx264實時視訊流(YUV 420P轉H264視訊編碼篇)

#PS:要轉載請註明出處,本人版權所有

#PS:這個只是 《 我自己 》理解,如果和你的

#原則相沖突,請諒解,勿噴

1、現在網上關於H264的文章有很多,但是我個人認為最好的就是雷霄驊博士的x264部分的文章最詳細。所以許多的細節部分,我推薦大家去雷博士的blog去看。本文只提及我們使用Libx264時候,我們要注意的問題。
2、 使用Libx264時候,我們需要關注的東西(下面用我的程式碼來說明假如我們要使用Libx264,那麼我們需要注意的幾個事情)。

//encoder
    x264_t * pX264Handle;//結構體是一個編碼器例項控制代碼,要使用這個編碼庫,我們必須有一個這種變數,沒有為啥。
//param x264_param_t * pX264Param;//這個結構體就比較重要了,他是我們設定編碼器引數的載體,我們必須具體的瞭解各種引數的意義。具體引數在下一節進行分析。 //input,output pic x264_picture_t *pPic_In;//這就是YUV輸入影象和輸出影象的載體,這裡面有一個pts引數需要注意,下面小節進行說明。 x264_picture_t *pPic_Out; //output h264 stream x264_nal_t * pNals;//這個也是比較重要的一個東西,他的作用是用來儲存編碼後,網路抽象層所儲存的資料(NAL HEADER,NAL BODY),想具體瞭解,可以去看H264編碼原理。
//user config callback //UESER_CONF_CALLBACK yX264_UserConfig; int (*yX264_UserConfig)(struct ymx264 * mvl);//私有,忽略 //pi_nal is the number of NAL units int pi_nal;//網路抽象單元個數

3、 編碼器引數分析

    //* cpuFlags  
    mvl->pX264Param->i_threads  = X264_SYNC_LOOKAHEAD_AUTO;//* 取空緩衝區繼續使用不死鎖的保證. 
    //* 視訊選項  
mvl->pX264Param->i_width = FRAME_WIDTH; //* 要編碼的影象寬度. mvl->pX264Param->i_height = FRAME_HEIGHT; //* 要編碼的影象高度 mvl->pX264Param->i_frame_total = 0; //* 編碼總幀數.不知道用0. /* Force an IDR keyframe at this interval */ mvl->pX264Param->i_keyint_max = 10; //這個引數很重要,控制i幀的頻率 mvl->pX264Param->b_repeat_headers = 1; // 重複SPS/PPS 放到關鍵幀前面//做實時流播放,此引數必須ENABLE //* 流引數 //* how many b-frame between 2 references pictures */ mvl->pX264Param->i_bframe = 5; // mvl->pX264Param->b_open_gop = 0; //* Keep some B-frames as references: 0=off, 1=strict hierarchical, 2=normal */ mvl->pX264Param->i_bframe_pyramid = 0; // mvl->pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS; //* Log引數,不需要列印編碼資訊時直接註釋掉就行 //mvl->pX264Param->i_log_level = X264_LOG_DEBUG; //* 速率控制引數 //pX264Param->rc.i_bitrate = 1024 * 10;//* 位元速率(位元率,單位Kbps) ,重要 //* muxing parameters 幀率控制,重要。 mvl->pX264Param->i_fps_den = 1; //* 幀率分母 mvl->pX264Param->i_fps_num = Y_STREAM_FPS;//* 幀率分子 mvl->pX264Param->i_timebase_den = mvl->pX264Param->i_fps_num; mvl->pX264Param->i_timebase_num = mvl->pX264Param->i_fps_den;

最後,我們需要注意一點:關於我們設定的幀率的問題,不一定是設定多少,播放的時候就是多少,只是一個參考值,編碼器會盡量的把視訊編碼為這個幀率。
4、x264_picture_t * pPic_In->i_pts += 1; 此引數非常重要。如果不進行設定,視訊流將不會正常播放。
/*
PTS:Presentation Time Stamp。PTS主要用於度量解碼後的視訊幀什麼時候被顯示出來
DTS:Decode Time Stamp。DTS主要是標識讀入記憶體中的bit流在什麼時候開始送入解碼器中進行解碼。
*/
5、關於顏色空間的問題,大家可以去百度YUV 420 ,YUV 422,YUV 444等這些原始影象的儲存問題。具體來說,他們分為兩類,一種是分組儲存(例如:YYY*UUU*VVV*),一種是交叉儲存(例如:YUYV)
6、此模組我的原始碼
ym_x264.h

/*
    FileName:ym_x264.h
    Version:1.0
    Description:
    Created On: 2017-3-19
    Modified date:
    Author:Sky
*/

#ifndef _YM_X264_H
#define _YM_X264_H

#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */

#include <stdint.h>
#include <stdio.h>

#include <ym_x264_config.h>

#include <x264.h>

#include <stdlib.h>




#define CLEAR_MEM(x) memset(&(x),0,sizeof(x))

enum yX264Cmd{

    DO_DEFAULT_PRESET = 0,
    DO_DEFAULT_USERCONF = 1,
    DO_PARAM_APPLY_PROFILE = 2,
    OPEN_ENCODER = 3,
    ENCODER_ENCODE = 4,

};
enum yX264ColorSpace{

    Y_CSP_I444 = 0,
    Y_CSP_I422 = 1,
    Y_CSP_I420 = 2,
    Y_CSP_YUYV = 3,
};

//typedef struct ymx264 yMX264;



typedef struct ymx264{

//encoder
    x264_t * pX264Handle;
//param
    x264_param_t * pX264Param;
//input,output pic
    x264_picture_t *pPic_In;
    x264_picture_t *pPic_Out;
//output h264 stream
    x264_nal_t * pNals;
//user config callback
    //UESER_CONF_CALLBACK yX264_UserConfig;
    int (*yX264_UserConfig)(struct ymx264 * mvl);
//pi_nal is the number of NAL units 
    int pi_nal;

    long cur_pts;

}yMX264; 

typedef int (*UESER_CONF_CALLBACK)(yMX264 * mvl);

int yInitMX264(yMX264 * mvl);
int yDestroyMX264(yMX264 * mvl);
int yIoctlX264(enum yX264Cmd cmd,...);
//CSC = ColorSpaceCovert,FIP = Fill In_Pic
int yDoCSC_And_FIP(yMX264 * mvl,enum yX264ColorSpace t_csp, int cache_id);

int yDo_Default_UserConf(yMX264 * mvl);


#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */ 


#endif

ym_x264.c

/*
    FileName:ym_x264.c
    Version:1.0
    Description:
    Created On: 2017-3-19
    Modified date:
    Author:Sky
*/

/*
x264_param_default():設定引數集結構體x264_param_t的預設值。
x264_picture_alloc():為影象結構體x264_picture_t分配記憶體。
x264_encoder_open():開啟編碼器。
x264_encoder_encode():編碼一幀影象。
x264_encoder_close():關閉編碼器。
x264_picture_clean():釋放x264_picture_alloc()申請的資源。

儲存資料的結構體如下所示。
x264_picture_t:儲存壓縮編碼前的畫素資料。
x264_nal_t:儲存壓縮編碼後的碼流資料。
*/
#include <ym_x264.h>

uint8_t ImgCache[ImageCacheNum][FRAME_SIZE];
int yInitMX264(yMX264 * mvl){


    mvl->pX264Param = (x264_param_t *)malloc(sizeof(x264_param_t));

//  for (int i = 0; i < ImageCacheNum; i++){

        mvl->pPic_In = (x264_picture_t *)malloc(sizeof(x264_picture_t));
        mvl->pPic_Out = (x264_picture_t *)malloc(sizeof(x264_picture_t));

        mvl->pNals = NULL;

        mvl->pi_nal = 0;

        x264_picture_init(mvl->pPic_Out);  
        x264_picture_alloc(mvl->pPic_In, FRAME_COLORSPACE, FRAME_WIDTH, FRAME_HEIGHT);

        // PTS FROM 0,AND AUTO INCRESE 1
        mvl->pPic_In->i_pts = 0;
//  }

    mvl->cur_pts = 0;




    return 0;
}
int yDestroyMX264(yMX264 * mvl){

    // 清除影象區域  
    //for (int i = 0; i < ImageCacheNum; i++)
    x264_picture_clean(mvl->pPic_In);  
    x264_encoder_close(mvl->pX264Handle);  


    free(mvl->pX264Param);
    //for (int i = 0; i < ImageCacheNum; i++){

        free(mvl->pPic_In);
        free(mvl->pPic_Out);
    //}


    return 0;
}

int yIoctlX264(enum yX264Cmd cmd,...){

    va_list arg;
    va_start(arg,cmd);
    yMX264 *mx264;
    mx264 = va_arg(arg,yMX264 *);
    va_end(arg);

    switch(cmd){

        case DO_DEFAULT_PRESET:
        {
            /* Get default params for preset/tuning */
            //x264_param_default(pParam);  //this do default set for x264,but can not config some info
            if( x264_param_default_preset( mx264->pX264Param, "veryfast", "zerolatency" ) < 0 ){

                printf("x264_param_default_preset failed!\n");
                return -1;
            }
            break;
        }
        case DO_DEFAULT_USERCONF:
        {
            //va_list arg;
            //va_start(arg,cmd);
            //mx264->yX264_UserConfig = va_arg(arg,UESER_CONF_CALLBACK);
            //mx264->yX264_UserConfig = NULL;

            //va_end(arg);

            if ( (*mx264->yX264_UserConfig)(mx264) < 0){

                printf("Do user conf callback failed.\n");
                return -1;
            }

            break;
        }
        case DO_PARAM_APPLY_PROFILE:
        {
            //x264_profile_names[0] = baseline , to set stream-quality
            if (x264_param_apply_profile(mx264->pX264Param, x264_profile_names[0]) < 0 ){

                printf("x264_param_apply_profile failed.\n");
                return -1;
            } 
            break;
        }
        case OPEN_ENCODER:
        {
            //open encoder
            if( (mx264->pX264Handle = x264_encoder_open(mx264->pX264Param)) == NULL){

                printf("x264_encoder_open failed.\n");
                return -1;              
            }
            break;
        }
        case ENCODER_ENCODE:
        {
/*
    x264_encoder_encode:
 *      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     x264_encoder_encode( x264_t *, x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_in, x264_picture_t *pic_out );
*/



            if ( x264_encoder_encode(mx264->pX264Handle, &mx264->pNals, &mx264->pi_nal, mx264->pPic_In, mx264->pPic_Out) < 0){

                printf("x264_encoder_encode failed.\n");
                return -1;                  

            }
/*
PTSPresentation Time StampPTS主要用於度量解碼後的視訊幀什麼時候被顯示出來
DTSDecode Time StampDTS主要是標識讀入記憶體中的bit流在什麼時候開始送入解碼器中進行解碼。
*/
            //Presentation Time StampPTS主要用於度量解碼後的視訊幀什麼時候被顯示出來
            //MUST DO THIS ,IT DECIDE ,主要用於度量解碼後的視訊幀什麼時候被顯示出來
            //mx264->pPic_In->i_pts += 1;
            mx264->pPic_In->i_pts += 1; 
            printf("pts = %d\n",mx264->pPic_In->i_pts);
            break; 
        }
        default :
        {
            printf("No this cmd to analyse\n");
            return -1;
            break;
        }
    }

    return 0;
}

int yDoCSC_And_FIP(yMX264 * mvl,enum yX264ColorSpace t_csp, int cache_id){

    switch(t_csp){  
        case Y_CSP_I444:
        {  
/*
            read(fd_in,pPic_In->img.plane[0],FRAME_SIZE);         //Y  
            read(fd_in,pPic_In->img.plane[1],FRAME_SIZE);         //U  
            read(fd_in,pPic_In->img.plane[2],FRAME_SIZE);         //V  
*/
            break;
        }  
        case Y_CSP_I420:
        {  
/*
            #ifndef ENABLE_YUYVTOI420 
            read(fd_in,pPic_In->img.plane[0],FRAME_SIZE);         //Y  
            read(fd_in,pPic_In->img.plane[1],FRAME_SIZE/4);     //U  
            read(fd_in,pPic_In->img.plane[2],FRAME_SIZE/4);     //V  
            #else
            //YUYV to I420
            read(fd_in,Cache,FRAME_SIZE*2);         //read one frame to cache 
            //must set to 0
            int id_u = 0,  id_v = 0 , id_y = 0;
            for (int i = 0; i < FRAME_SIZE*2 ;i+=4){ 

                pPic_In->img.plane[0][id_y] = Cache[i];//get Y
                id_y++;
                pPic_In->img.plane[0][id_y] = Cache[i+2];//get Y
                id_y++;
                if ( ((int)((i)/1280)%2) == 0 ){
                    pPic_In->img.plane[1][id_u] = Cache[i+1];//get U
                    pPic_In->img.plane[2][id_v] = Cache[i+3];//get V
                    id_u++;
                    id_v++;
                }
            }

            #endif
*/
            break;
        } 
        case Y_CSP_YUYV:{
            // firstly,Do YUYV to I420 ,then, Fill in_pic
            int id_u = 0,  id_v = 0 , id_y = 0;
            for (int i = 0; i < FRAME_SIZE ;i+=4){ 

                mvl->pPic_In->img.plane[0][id_y] = ImgCache[cache_id][i];//get Y
                id_y++;
                mvl->pPic_In->img.plane[0][id_y] = ImgCache[cache_id][i+2];//get Y
                id_y++;
                if ( ((int)((i)/1280)%2) == 0 ){

                    mvl->pPic_In->img.plane[1][id_u] = ImgCache[cache_id][i+1];//get U
                    mvl->pPic_In->img.plane[2][id_v] = ImgCache[cache_id][i+3];//get V
                    id_u++;
                    id_v++;
                }
            }
            break;
        }
        case Y_CSP_I422:
        {  
/*

            read(fd_in,pPic_In->img.plane[0],FRAME_SIZE);         //Y  
            read(fd_in,pPic_In->img.plane[1],FRAME_SIZE/2);     //U  
            read(fd_in,pPic_In->img.plane[2],FRAME_SIZE/2);     //V 
*/
            break;
        } 

        default:
        {  
            printf("Colorspace Not Support.\n");  
            return -1;
        }  
    }

}

int yDo_Default_UserConf(yMX264 * mvl){


    //* cpuFlags  
    mvl->pX264Param->i_threads  = X264_SYNC_LOOKAHEAD_AUTO;//* 取空緩衝區繼續使用不死鎖的保證. 
    //* 視訊選項  
    mvl->pX264Param->i_width   = FRAME_WIDTH; //* 要編碼的影象寬度.  
    mvl->pX264Param->i_height  = FRAME_HEIGHT; //* 要編碼的影象高度  
    mvl->pX264Param->i_frame_total = 0; //* 編碼總幀數.不知道用0.  
    /* Force an IDR keyframe at this interval */
    mvl->pX264Param->i_keyint_max = 10; 

    mvl->pX264Param->b_repeat_headers = 1;  // 重複SPS/PPS 放到關鍵幀前面
    //* 流引數  
    //* how many b-frame between 2 references pictures */
    mvl->pX264Param->i_bframe  = 5;  
    //
    mvl->pX264Param->b_open_gop  = 0;  
    //* Keep some B-frames as references: 0=off, 1=strict hierarchical, 2=normal */
    mvl->pX264Param->i_bframe_pyramid = 0;  
    //
    mvl->pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;  


    //* Log引數,不需要列印編碼資訊時直接註釋掉就行  
    //mvl->pX264Param->i_log_level  = X264_LOG_DEBUG; 

    //* 速率控制引數  
    //pX264Param->rc.i_bitrate = 1024 * 10;//* 位元速率(位元率,單位Kbps) 

    //* muxing parameters  
    mvl->pX264Param->i_fps_den  = 1; //* 幀率分母
    mvl->pX264Param->i_fps_num  = Y_STREAM_FPS;//* 幀率分子  

    mvl->pX264Param->i_timebase_den = mvl->pX264Param->i_fps_num;  
    mvl->pX264Param->i_timebase_num = mvl->pX264Param->i_fps_den;  

    return 0;
}

#PS:請尊重原創,不喜勿噴

#PS:要轉載請註明出處,本人版權所有.

有問題請留言,看到後我會第一時間回覆