天天看点

TI MM H.264 编码 解析

目前支持的格式:

1.H.264 编码分辨率高达 1080p (1920x1080)

2.NV12(双平面)格式,8位内容支持

3.帧级编码

4.仅 I 帧编码,这是默认模式

5.I & P 帧编码

为了启用 I&P 帧编码,需要修改驱动程序代码以设置所需的配置

6.双通道编码,分辨率高达 1080p (1920x1080),分辨率为 30fps。

不支持的功能:

1.B 帧编码

2.非 NV12 像素格式

3.由于非标准设置而导致的崩溃的错误恢复

4.大于两个通道编码

encode 对象配置:

tivx_video_encoder_params_t;

名称 作用
uint32_t bitstream_format 编码输出的比特流格式。有效值为TIVX_BITSTREAM_FORMAT_H264
uint32_t features 编码特性的位标志,详细可以看tivx_enc_features_e 也用于控制H264配置文件,设置配置文件达到所启用功能的最低要求。
uint32_t rcmode RC Mode, MM_ENC_VBR MM_ENC_SVBR RC 模式设置为 VBR 或 SVBR。可变比特率 (VBR) 速率控制允许每帧分配不同的位,从而满足滑动窗口上的总体比特率要求。流式可变比特率 (SVBR) 速率控制类似于 VBR,但由于禁用位填充,无法保证恒定的输出比特率。但是,这仍然可以保证以指定比特率馈送的解码器能够成功解码。
uint32_t idr_period IDR周期是瞬时解码刷新之间的帧数 (IDR)框架。 I帧之间的帧数量 GOP数量 IDR帧保证在IDR帧之后没有帧将引用IDR帧之前的任何帧。一个典型的值大约是1-2秒的帧,例如,i_period = 30 表示将有一个 I 帧后跟 29 个 P 帧的模式,然后重复。设置 i_period = 1 将设置仅 I 帧编码。
uint32_t bitrate 码率,比特率是每秒编码的比特数。这是以bps为单位设置的,而不是kbps或mbps对于1920x1080 VBR码流,比特率在10000000左右是正常的。
uint32_t framerate 每秒编码的YUV图像帧数 ,Framerate设置流每秒编码的帧数。这主要影响RC控制将尝试为每个比特分配内存基于此和比特率的帧。典型的帧率是30和60 fps。
uint32_t crop_left uint32_t crop_right uint32_t crop_top uint32_tcrop_bottom 裁剪参数,裁剪设置设置原始图像每边裁剪的大小。注意:视频缓冲区的宽度和高度必须是16对齐的,才能与驱动程序一起工作。默认情况下,如果缓冲区宽度/高度输入非16对齐的值,驱动程序将向下舍入为16对齐。为了从非16对齐的流中获得完整的宽度/高度,应用程序必须将数据读入缓冲区从左上角开始,确保线条宽度对齐。然后必须为作物设置合适的作物编码器,沿边缘裁剪空数据。ex. 1920x1080 设置宽度/高度为1920x1088。填满输入数据后,将在缓冲区底部留下8行空数据。设置crop_bottom = 8,裁剪缓冲区底部的空8行,以避免出现绿行。注意:crop的值只能设置为2的倍数。在驱动程序中,任何奇数都将向下取整。默认设置为0。
uint32_t nslices 将每个帧划分为用于编码的切片数量。在正常情况下,该值应该设置为1。可以设置为2以利用编码器的2个硬件管道用于低延迟编码,但这种延迟优势只是显而易见的如果这是唯一被编码的流。
uint32_t base_pipe 设置在编码器中使用的基础管道。有效值为0和1。编码器有2个硬件编码管道。为获得最佳性能,多个流应该在两个管道中拆分,以最小化花费的时间等待另一个流完成帧编码对于以每帧最小延迟为目标的2片编码,应该将其设置为0以允许它同时使用管道0和管道1。
uint32_t initial_qp_i; uint32_tinitial_qp_p; uint32_t initial_qp_b; uint32_t min_qp; uint32_t max_qp; Qp设置设置量化参数(Qp)。默认情况下离开这些值为0将允许驱动程序自动计算适当的值基于流的设置。这些设置会影响编码器如何对静态或动态流进行编码。较低的QP值允许高质量的编码但更少的压缩,较高的QP值允许较大的压缩但较低的编码质量。如果QP对于流中的移动量来说太低,就会产生这种情况编码器耗尽帧数据位时产生的伪影 。QP是指定的。当流被编码时,QP值将自动调整为 mix_qp和max_qp之间的值基于流内容,正在尝试最大化给定流的质量。初始QP设置(initial_qp_i/p/b)分别设置I/ p/b帧的初始QP值。这会影响流开始时的流质量,在编码器有机会适应流之前。 min_qp/max_qp分别设置QP的最小值和最大值。取值范围是2 ~ 49。
uint32_t min_blk_size 运动搜索的最小块大小。MM_ENC_BLK_SZ_DEFAULT允许驱动程序选择它检测到的最佳块大小MM_ENC_BLK_SZ_16x16使用 16x16 块作为运动搜索的最小块。这是 H.263 的最小值 MM_ENC_BLK_SZ_8x8使用小至 8x8 块进行运动搜索。这是MPEG-4的最小值 MM_ENC_BLK_SZ_4x4使用小至 4x4 块进行运动搜索。这是 H.264 的最小值
uint32_t intra_pred_modes; 控制H264COMP_INTRA_PRED_MODES寄存器。默认值为0。详情见TRM。

参考配置:

仅 I-vs I&P 帧编码

params.bitstream_format = TIVX_BITSTREAM_FORMAT_H264;
params.features = TIVX_ENC_FEATURE_CABAC | TIVX_ENC_FEATURE_8x8;
params.rcmode = TIVX_ENC_VBR;
params.idr_period = 30;
params.i_period = 30;
params.bitrate = 10000000;
params.framerate = 30;
params.nslices = 1;
params.initial_qp_i = 0;
params.initial_qp_p = 0;
params.initial_qp_b = 0;
params.min_qp = 0;
params.max_qp = 0;
params.min_blk_size = TIVX_ENC_BLK_SZ_DEFAULT;
params.intra_pred_modes = 0;
           

注意:由于 16 对齐要求,请务必将 1080p 流的高度设置为 1088,并将 crop_bottom 设置为 8

1080p I-only

params.bitstream_format = TIVX_BITSTREAM_FORMAT_H264;
params.features = TIVX_ENC_FEATURE_CABAC | TIVX_ENC_FEATURE_8x8;
params.rcmode = TIVX_ENC_VBR;
params.idr_period = 1;
params.i_period = 1;
params.bitrate = 20000000;
params.framerate = 30;
params.nslices = 1;
params.initial_qp_i = 0;
params.initial_qp_p = 0;
params.initial_qp_b = 0;
params.min_qp = 0;
params.max_qp = 0;
params.min_blk_size = TIVX_ENC_BLK_SZ_DEFAULT;
params.intra_pred_modes = 0;
           

Recommended Configurations

Table of Contents

Overview

Overview

本节介绍各种分辨率和用例的建议设置。这些设置经过质量测试并已知有效,并且被认为适合大多数用例。对这些设置的更改可能需要其他支持。

仅 I-vs I&P 帧编码

对于需要最高质量且不受比特率限制的用例,建议使用仅 I 编码。这也允许更一致的带宽使用,因为每个编码帧的大小相似。这些主要针对ADAS和机器学习应用。

对于需要给定比特率的最高质量的用例,建议使用 I&P 帧编码,但由于吞吐量或存储限制,总体比特率有限。用例包括信息娱乐或流媒体应用程序。

1080p I&P

params.bitstream_format = TIVX_BITSTREAM_FORMAT_H264;

参数.特征 = TIVX_ENC_FEATURE_CABAC |TIVX_ENC_FEATURE_8x8;

params.rcmode = TIVX_ENC_VBR;

params.idr_period = 30;

params.i_period = 30;

参数比特率 = 10000000;

参数帧率 = 30;

params.nslices = 1;

params.initial_qp_i = 0;

params.initial_qp_p = 0;

params.initial_qp_b = 0;

params.min_qp = 0;

params.max_qp = 0;

params.min_blk_size = TIVX_ENC_BLK_SZ_DEFAULT;

params.intra_pred_modes = 0;

注意:由于 16 对齐要求,请务必将 1080p 流的高度设置为 1088,并将 crop_bottom 设置为 8

1080p 仅限 I

params.bitstream_format = TIVX_BITSTREAM_FORMAT_H264;

参数.特征 = TIVX_ENC_FEATURE_CABAC |TIVX_ENC_FEATURE_8x8;

params.rcmode = TIVX_ENC_VBR;

params.idr_period = 1;

params.i_period = 1;

参数比特率 = 20000000;

参数帧率 = 30;

params.nslices = 1;

params.initial_qp_i = 0;

params.initial_qp_p = 0;

params.initial_qp_b = 0;

params.min_qp = 0;

params.max_qp = 0;

params.min_blk_size = TIVX_ENC_BLK_SZ_DEFAULT;

params.intra_pred_modes = 0;

注意:由于 16 对齐要求,请务必将 1080p 流的高度设置为 1088,并将 crop_bottom 设置为 8

使用步骤参考:

相关结构体:

typedef struct {
  vx_node  node;
  vx_kernel kernel;

  char output_file[FILENAME_SIZE_MAX];
  vx_char output_file_path[TIVX_FILEIO_FILE_PATH_LENGTH];
  FILE *out_fp;

  tivx_video_encoder_params_t encode_params;
  vx_user_data_object configuration_obj;
  vx_user_data_object bitstream_obj;
  vx_user_data_object encoded_image;

  vx_int32 graph_parameter_index;
  vx_int32 inWidth;
  vx_int32 inHeight;

} EncodeObj;
           

1.初始化encode的相关属性:

vx_status app_init_encode(vx_context context, EncodeObj *encodeObj, char *objName)
{
    uint32_t max_bitstream_size;

    vx_status status = VX_SUCCESS;

    /* Create object for encode parameters */
    tivx_video_encoder_params_init(&(encodeObj->encode_params));
    encodeObj->configuration_obj = vxCreateUserDataObject(context,
            "tivx_video_encoder_params_t",
            sizeof(tivx_video_en
            coder_params_t),
            NULL);

    if (vxGetStatus((vx_reference)encodeObj->configuration_obj) != VX_SUCCESS)
    {
        APP_PRINTF("configuration_obj create failed\n");
        return VX_FAILURE;
    }

    /* Set bitstream format  */
    encodeObj->encode_params.bitstream_format = TIVX_BITSTREAM_FORMAT_H264;

    /* Copy object back onto main graph object ??? */
    vxCopyUserDataObject(encodeObj->configuration_obj, 0,
            sizeof(tivx_video_encoder_params_t),
            &(encodeObj->encode_params),
            VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);

    max_bitstream_size = ((uint32_t)(encodeObj->inWidth / 16)
            * (uint32_t)(encodeObj->inHeight / 16) * WORST_QP_SIZE)
                                          + ((encodeObj->inHeight >> 4) * CODED_BUFFER_INFO_SECTION_SIZE);

    encodeObj->bitstream_obj = vxCreateUserDataObject(context,
            "tivx_video_bitstream_t",
            sizeof(uint8_t) * max_bitstream_size,
            NULL);
    status = vxGetStatus((vx_reference)encodeObj->bitstream_obj);

    if(status == VX_SUCCESS)
    {
        /* Open file for output */
        char * filename = "encode_output.h264";
        snprintf(encodeObj->output_file, FILENAME_SIZE_MAX, "%s/%s", encodeObj->output_file_path, filename);

        encodeObj->out_fp = fopen(encodeObj->output_file, "wb");
        if (NULL == encodeObj->out_fp)
        {
            printf("app_create_graph: encoder: %s: Output file not opened!!!\n", encodeObj->output_file);
            printf("app_create_graph: encoder: Bitstream write to media skipped!\n");
        }
    }
    return status;
}
           

2、初始化在 相关main函数内,进行encode_init的调用。

if(status == VX_SUCCESS)
    {
        status = app_init_encode(obj->context, &obj->encodeObj, "encode_obj");
        APP_PRINTF("Encode init done!\n");
    }
           

3、创建encode的graph Node节点

在app_create_graph函数内,添加如下代码段,创建encode 的Node节点

if (status == VX_SUCCESS) //检测是否成功,然后创建encode节点
    {
        app_create_graph_encode(obj->graph, &obj->encodeObj, &obj->encodeObj.encode_image);
    }
           
vx_status app_create_graph_encode(vx_graph graph, EncodeObj *encodeObj, vx_image *output_image)
{

    vx_status status = VX_SUCCESS;

    encodeObj->node = tivxVideoEncoderNode(graph,
            encodeObj->configuration_obj, /* Encode parameters          (0) */
            output_image[0],              /* From Mosaic                (1) */
            encodeObj->bitstream_obj);    /* Bitstream to write to file (2) */
    APP_ASSERT_VALID_REF(encodeObj->node);

    vxSetNodeTarget(encodeObj->node, VX_TARGET_STRING, TIVX_TARGET_VENC1);

    vxSetReferenceName((vx_reference)encodeObj->node, "Encode_node");
    status = vxGetStatus((vx_reference)encodeObj->node);

    return (status);

}
           

待续