ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

libx264 编码参数调整--流媒体

2021-08-04 17:02:31  阅读:323  来源: 互联网

标签:__ 流媒体 -- libx264 param x264 size frame out


在标题加了个后缀-流媒体。所以主要是借libx264来研究一下其中关于在流媒体场景中(低延时)偏向的设置, 关于编码器 码率,图像效果等等这里面有好多参数,指不定有相互影响的参数,理解的不多,这里借鉴各处博客以及x264官方wiki和libx264源码,官方网站https://www.videolan.org/developers/x264.html (vlc 一家组织,去vlc官网即可,还可以找到wiki文档))记录下试水libx264-流媒体低延时编码的参数设置
环境:
        5代corei7 cpu ,ubuntu 系统。
        libx264  配置 --disable-asm 关闭,这个可能会降低效率,实际使用需要开启,这里只是研究测试。
libx264 ,configure + make 之后默认生成x264可执行文件,不过我们这里并不打算使用x264工具来研究,其源码目录下有一个 example.c 文件,可以作为api 调用的参考。需要我们 执行
#make example
生成example 可执行文件,本片参考这个example 源码来写自己的demo, 
1.0 设置参数。 2.0 输入一帧yuv420数据。 3.0 编码取出数据

Q1: 怎么设置编码器让其不生成B帧?

param.i_bframe=0 //  /* how many b-frame between 2 references pictures */ 源码注释

Q2:怎么设置GOP值

param.i_keyint_max=xx /* Force an IDR keyframe at this interval */

Q3:在每一个I帧前面加上sps pps信息

param.b_repeat_headers=1 /* put SPS/PPS before each keyframe */

Q4:关键点,怎么让编码器不缓存,期望它是送入一帧数据,编码完就出一帧数据?

x264_param_default_preset( &param, "ultrafast", "zerolatency" )
第二个参数,zerolatency。 设置为这个之后,就是进一帧出一帧的顺序了。在编码存储本地文件的时候可能不需要这么做,但是事实性要求高的肯定要这么设置了,不然始终有delay。
第一个参数,实际上设置的是包含一系列参数的设置组合,根据你选择的速度来帮忙设置一些列参数,比如上面的param.i_bframe在选择 ultrafast的时候就会被设置为0,在源码 base.c ::param_apply_preset( x264_param_t *param, const char *preset ) 函数中可以看到详细信息,可选的值:{"ultrafast","superfast","veryfast","faster","fast","medium","slow","slower","veryslow","placebo"}
看名字也就知道意思了。

其他的一些参数:

   param.i_bitdepth = 8;
    param.i_csp = X264_CSP_I420;//颜色空间 color space?
    param.i_width  = width;
    param.i_height = height;
    param.b_vfr_input = 0; //设置为1将控制时间戳?
    param.b_annexb = 1;//每一个帧携带启始码00 00 00 01,如果设置为0,则不添加00 00 00 01,而是存储这一帧的数据大小值。(占4个字节)

对比结果

不设置"zerolatency",即使第一个参数设置的是 "ultrafast",也是有缓存,延时输出:

比如上面,一直到输入12帧之后才输出。
开启 "zerolatency" 之后:

这才是我们流媒体实时性要求的。
可以看到我这样的硬件环境,ultrafast 设置编码一帧的速度在 4ms以内,(这里原yuv数据分辨率为640x480,yuv420sp,libx264没有开启asm 汇编级别的平台优化). 并且基本上编码 I帧的耗时比P帧高,上面的 nal->i_type ==7为I帧。(原本以为P帧信息冗余更少,要帧参考,可能耗时会更大,看来不是。)

放上调试demo :

//canok 20210804

#include <stdint.h>
#include <stdio.h>
#include <sys/time.h>
#include <x264.h>

#define FAIL_IF_ERROR( cond, ... )\
do\
{\
    if( cond )\
    {\
        fprintf( stderr, __VA_ARGS__ );\
        goto fail;\
    }\
} while( 0 )


int64_t getNowUs(){
    struct timeval tv;
    gettimeofday(&tv, 0);
    return (int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_usec;
}

int main( int argc, char **argv )
{//https://www.cnblogs.com/wainiwann/p/5647521.html
    int width, height;
    x264_param_t param;
    x264_picture_t pic;
    x264_picture_t pic_out;
    x264_t *h;
    int i_frame = 0;
    int out_frame=0;
    int i_frame_size;
    x264_nal_t *nal;
    int i_nal;

    //FAIL_IF_ERROR( !(argc > 1), "Example usage: example 352x288 <input.yuv >output.h264\n" );
    if(argc !=3){
        printf("usage: my_encoder WxH inputfile.yuv\n");
        return -1;
    }

    FILE *fp_in=fopen(argv[2],"r");
    if(fp_in==NULL){
        printf("[%s%d] fopen err:%s\n",__FUNCTION__,__LINE__,argv[2]);
        return -1;
    }

    FILE *fp_out=fopen("out.h264","w+");
    if(fp_out==NULL){
        printf("[%s%d] fopen err:out.h264 \n",__FUNCTION__,__LINE__);
        return -1;
    }

    FAIL_IF_ERROR( 2 != sscanf( argv[1], "%dx%d", &width, &height ), "resolution not specified or incorrect\n" );



    /* Get default params for preset/tuning */
    #if 0
    if( x264_param_default_preset( &param, "medium", NULL ) < 0 )
        goto fail;
    #else
    //设置到最快
    //具体参数可以看base.c::param_apply_preset
    //zerolatency不延迟这个设置之后,就进一帧出一帧了,不延迟
    //char speed[][16]={"ultrafast","superfast","veryfast","faster","fast","medium","slow","slower","veryslow","placebo"};
    if( x264_param_default_preset( &param, "ultrafast", "zerolatency" ) < 0 )
    //if( x264_param_default_preset( &param, "ultrafast", NULL ) < 0 )
        goto fail;
    #endif
    /* Configure non-default params */
    param.i_bitdepth = 8;
    param.i_csp = X264_CSP_I420;//颜色空间
    param.i_width  = width;
    param.i_height = height;
    param.b_vfr_input = 0; //设置为1将控制时间戳?
    param.b_repeat_headers = 1; //在每一个I帧前面加上sps pps
    param.b_annexb = 1;//每一个帧携带启始码00 00 00 01,如果设置为0,则不添加00 00 00 01,而是存储这一帧的数据大小值。(占4个字节)

    param.i_bframe =0; // 不使用B帧/* how many b-frame between 2 references pictures */

    //param.i_keyint_min =
    param.i_keyint_max = 5; //gop

//https://blog.csdn.net/bingqingsuimeng/article/details/79197901
    param.rc.b_mb_tree = 0;//实时编码是强烈建议为0

    /* Apply profile restrictions. */
    if( x264_param_apply_profile( &param, "high" ) < 0 )
        goto fail;

    if( x264_picture_alloc( &pic, param.i_csp, param.i_width, param.i_height ) < 0 )
        goto fail;
#undef fail
#define fail fail2

    h = x264_encoder_open( &param );
    if( !h )
        goto fail;
#undef fail
#define fail fail3

    int luma_size = width * height;
    int chroma_size = luma_size / 4;
    /* Encode frames */
    for( ;; i_frame++ )
    {
        /* Read input frame */
        if( fread( pic.img.plane[0], 1, luma_size, fp_in ) != (unsigned)luma_size )
            break;
        if( fread( pic.img.plane[1], 1, chroma_size, fp_in ) != (unsigned)chroma_size )
            break;
        if( fread( pic.img.plane[2], 1, chroma_size, fp_in ) != (unsigned)chroma_size )
            break;

        pic.i_pts = i_frame;
        int64_t t1=getNowUs();
        
        printf("[%s%d]in:%d %ld\n",__FUNCTION__,__LINE__,i_frame, t1);
        i_frame_size = x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out );
        if( i_frame_size < 0 )
            goto fail;
        else if( i_frame_size )
        {
            int64_t t2=getNowUs();
            printf("[%s%d]out:%d %ld\n",__FUNCTION__,__LINE__,out_frame, t2);
            //SLICE_TYPE_P SLICE_TYPE_B SLICE_TYPE_I 
            //nal->i_type nal_unit_type_e
            /*enum sei_payload_type_e
            {
                SEI_BUFFERING_PERIOD       = 0,
                SEI_PIC_TIMING             = 1,
                SEI_PAN_SCAN_RECT          = 2,
                SEI_FILLER                 = 3,
                SEI_USER_DATA_REGISTERED   = 4,
                SEI_USER_DATA_UNREGISTERED = 5,
                SEI_RECOVERY_POINT         = 6,
                SEI_DEC_REF_PIC_MARKING    = 7,
                SEI_FRAME_PACKING          = 45,
                SEI_ALTERNATIVE_TRANSFER   = 147,
            };*/
            printf("[%s%d]in:%d---%dout,taketimes:%ld,nal->i_type:%d\n",__FUNCTION__,__LINE__,i_frame,out_frame,t2-t1,nal->i_type);
            out_frame++;
            if( !fwrite( nal->p_payload, i_frame_size, 1, fp_out ) )
                goto fail;
        }
    }
    /* Flush delayed frames */
    //设置 "zerolatency" 之后就不会有delayed_frames了。
    while( x264_encoder_delayed_frames( h ) )
    {
        i_frame_size = x264_encoder_encode( h, &nal, &i_nal, NULL, &pic_out );
        if( i_frame_size < 0 )
            goto fail;
        else if( i_frame_size )
        {
            int64_t t2=getNowUs();
            printf("[%s%d]out-delayframe:%d %ld\n",__FUNCTION__,__LINE__,out_frame, t2);
            out_frame++;
            if( !fwrite( nal->p_payload, i_frame_size, 1, fp_out ) )
                goto fail;
        }
    }

    x264_encoder_close( h );
    x264_picture_clean( &pic );
    return 0;

#undef fail
fail3:
    x264_encoder_close( h );
fail2:
    x264_picture_clean( &pic );
fail:
    fclose(fp_in);
    fclose(fp_out);
    return -1;
}

 makefile: 参照make example 时的输出copy一个就行

all:
	gcc -Wno-maybe-uninitialized -Wshadow -O3 -ffast-math -m64  -Wall -I. -I../x264-master/my_install/include -std=gnu99 -D_GNU_SOURCE -mpreferred-stack-boundary=6 -fomit-frame-pointer -fno-tree-vectorize -fvisibility=hidden  -c my_encoder.c -o my_encoder.o
	gcc -o my_encoder  my_encoder.o ../x264-master/my_install/lib/libx264.a -m64  -lm -lpthread -ldl

#my_encoder.c  demo文件
#../x264-master/my_install/lib/libx264.a lib264库

标签:__,流媒体,--,libx264,param,x264,size,frame,out
来源: https://blog.csdn.net/u012459903/article/details/119385709

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有