[TI TDA4 J721E]Encode节点编码YUV NV12图像视频生成H.264视频流 使用和移植

         首先感谢阅读,如果您也对TDA4相关的开发感兴趣,我们这边有个学习交流微信群,可以入群和大家一起交流学习。

资历较浅,水平有限,如遇错误,请大家多指正!

保持开源精神,共同分享、进步!

博主WX : AIR_12  我会拉你入群。

链接:TDA4 相关专栏        链接:TDA4  Demo  Gitee开源库

欢迎大家加入,一起维护这个开源库,给更多的朋友提供帮助。


        原本想在0800的版本上进行Encode节点的移植,可是尝试了很久,没有成功,最后在TI的论坛上,找到了原因,0800这个版本目前不支持Encode/Decode节点的使用,需要在下一个版本才可以支持。可能在12月中旬左右才会发版本。
TDA4VM: encoder usecase in SDK8.0 - Processors forum - Processors - TI E2E support forumshttps://e2e.ti.com/support/processors-group/processors/f/processors-forum/1045086/tda4vm-encoder-usecase-in-sdk8-0?tisearch=e2e-sitesearch&keymatch=encode#

于是就在0703这个版本上进行了移植和使用。大家一定要注意版本,关于环境的搭建,大家可以查看这个博客(注意按照0703的版本进行文件下载)。

[TI TDA4 J721E]PROCESSOR-SDK-J721E RTOS Linux 开发环境搭建 基于 ubuntu 18.04_AIRKernel的博客-优快云博客


目前还是基于USB Camera获取YUV图像,然后经过encode保存成H264文件。完整的项目,我会放在下载专区,链接如下(升级了,完成了编解码一起的工程):

[TITDA4J721E]USB摄像头YUV图像获取EncodeDecode节点使用和移植disp显示-编解码文档类资源-优快云下载


工程介绍:

USB摄像头单独一个Task进行YUV图像的采集,然后在Graph运行的时候,对采集侧和处理侧都进行了上锁操作,防止图像处理过快出现问题。即USB摄像头获取图像完成,才允许Graph进行处理;Graph处理完,才允许USB摄像头采集新的数据,防止对正在处理的数据造成干扰。

PS:大家根据自己系统内的/dev/ 下的设备,设置usb camera的文件名。在app_init内USB摄像头初始化的地方进行修改。当时是为了做多个摄像头测试,所以这样写了一个函数。大家自行修改。

   if (status == VX_SUCCESS)
    {
        unsigned char i = 0;
        for (i = 0; i < CAMERA_NUM; i++)
        {
            obj->usbCameraObj[i].devName = malloc(64);                          //申请内存
            memset(obj->usbCameraObj[i].devName, 0, 64);                        //内存清空
            sprintf(obj->usbCameraObj[i].devName, "/dev/video%d",2*i);        //导入摄像的硬件名

            if (status == VX_SUCCESS)
            {
                status = app_init_usbCamera(&(obj->usbCameraObj[i])); //初始化usb摄像头
            }
            else
            {
                printf("USB Camera Running error!\n");
            }
        }
    }

一、拷贝工程到0703版本

将/vision_apps/apps/basic_demos/app_encode下的两个文件 app_encode_module.c app_encode_module.h 拷贝到app_usb_disp目录下。

需要对原有文件内的一些东西进行更改。

1、新增一个内存拷贝函数,用于将用户空间的图像数据,拷贝到tiovx的内核空间( 在app_encode_module.c内,在头文件内需要加入声明)
关于图像的相关操作,可以查看这篇博客,这里不做赘述。
[TI TDA4 J721E]TIOVX Image图像相关操作_AIRKernel的博客-优快云博客_vx框架

vx_status app_running_encodeCopy(EncodeObj *encodeObj , char *image)
{
    vx_status status = VX_SUCCESS;

    if (status == VX_SUCCESS)
    {
        vx_rectangle_t rect;
        vx_imagepatch_addressing_t image_addr;
        vx_map_id map_id;
        void *data_ptr;
        vx_uint32 img_width;
        vx_uint32 img_height;

        vxQueryImage(encodeObj->encode_image, VX_IMAGE_WIDTH, &img_width, sizeof(vx_uint32)); //查询图像宽度
        vxQueryImage(encodeObj->encode_image, VX_IMAGE_HEIGHT, &img_height, sizeof(vx_uint32)); //查询图像的高度

        rect.start_x = 0;
        rect.start_y = 0;
        rect.end_x = img_width;
        rect.end_y = img_height;
        status = vxMapImagePatch(encodeObj->encode_image, //向内核申请图像访问的指针地址,用于拷贝用户数据到vx内核空间
                                 &rect,
                                 0,
                                 &map_id,
                                 &image_addr,
                                 &data_ptr,
                                 VX_WRITE_ONLY,
                                 VX_MEMORY_TYPE_HOST,
                                 VX_NOGAP_X);

        memcpy(data_ptr, image, img_width * img_height); //拷贝数据

        vxUnmapImagePatch(encodeObj->encode_image, map_id); //释放map_id

        rect.start_x = 0;
        rect.start_y = 0;
        rect.end_x = img_width;
        rect.end_y = img_height / 2;
        status = vxMapImagePatch(encodeObj->encode_image, //向内核申请图像访问的指针地址,用于拷贝用户数据到vx内核空间
                                 &rect,
                                 1,
                                 &map_id,
                                 &image_addr,
                                 &data_ptr,
                                 VX_WRITE_ONLY,
                                 VX_MEMORY_TYPE_HOST,
                                 VX_NOGAP_X);

        memcpy(data_ptr, image + img_width * img_height, img_width * img_height / 2); //拷贝数据,这里需要注意拷贝源地址的偏移量
        vxUnmapImagePatch(encodeObj->encode_image, map_id); //释放map_id
    //    vxReleaseImage(&encodeObj->encode_image); //释放临时图像
    }
    return status;
}

2、在结构体内增加一个vx_image对象

 3、在初始化内对encode的相关属性进行设置

vx_status app_init_encode(vx_context context, EncodeObj *encodeObj)
{
    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_encoder_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;

    encodeObj->encode_params.bitstream_format = TIVX_BITSTREAM_FORMAT_H264;
    encodeObj->encode_params.features = TIVX_ENC_FEATURE_CABAC | TIVX_ENC_FEATURE_8x8;
    encodeObj->encode_params.rcmode = TIVX_ENC_SVBR;
    encodeObj->encode_params.idr_period = 30;
    encodeObj->encode_params.i_period = 30;
    encodeObj->encode_params.bitrate = 10000000;
    encodeObj->encode_params.framerate = 30;
    encodeObj->encode_params.crop_left = 0;
    encodeObj->encode_params.crop_right = 0;
    encodeObj->encode_params.crop_top = 0;
    encodeObj->encode_params.crop_bottom = 0;
    encodeObj->encode_params.nslices = 1;
    encodeObj->encode_params.base_pipe = 0;
    encodeObj->encode_params.initial_qp_i = 0;
    encodeObj->encode_params.initial_qp_p = 0;
    encodeObj->encode_params.initial_qp_b = 0;
    encodeObj->encode_params.min_qp = 0;
    encodeObj->encode_params.max_qp = 0;
    encodeObj->encode_params.min_blk_size = TIVX_ENC_BLK_SZ_DEFAULT;
    encodeObj->encode_params.intra_pred_modes = 0;

    /* 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);

    encodeObj->encode_image = vxCreateImage(context, encodeObj->inWidth, encodeObj->inHeight, VX_DF_IMAGE_NV12);

    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;
}

二、将相关函数进行调用 

1、初始化在 app_init函数内,进行encode_init的调用,设置文件输出的路径、设置encode输入文件的大小。

    if (status == VX_SUCCESS)
    {
        //必要的初始化配置相关,设置输出文件路径
        sprintf(obj->encodeObj.output_file_path, "/home/root");

        obj->encodeObj.inHeight = obj->usbCameraObj->fmt.fmt.pix.height;//USB Camera的高
        obj->encodeObj.inWidth = obj->usbCameraObj->fmt.fmt.pix.width;//USB Camera的宽

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

2、创建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);
    }

3、在app_run_graph_for_one_frame函数内,添加app_running_encodeCopy,每次执行Graph都会执行一次这个函数。


三、编译验证

直接在 vision_apps目录下

sudo make -j16 |grep error

然后执行网络拷贝脚本(IP地址是开发板地址,直接在vision_apps目录下执行此脚本即可将必要的.out文件拷贝到开发板的对应目录下了。)
网络操作,可以查看这篇博客:
[TI TDA4 J721E]开发板网络调试功能及开机自动配置网络_AIRKernel的博客-优快云博客_tda4开发板 网络

#!/bin/sh

sudo scp ./out/J7/A72/LINUX/release/*.out root@192.168.1.188:/opt/vision_apps
sudo scp ./out/J7/C66/SYSBIOS/release/*.out root@192.168.1.188:/lib/firmware/vision_apps_evm/
sudo scp ./out/J7/C71/SYSBIOS/release/*.out root@192.168.1.188:/lib/firmware/vision_apps_evm/
sudo scp ./out/J7/R5F/SYSBIOS/release/*.out root@192.168.1.188:/lib/firmware/vision_apps_evm/

exit 0

四、实际测试

利用ssh登录开发板,进入/opt/vision_apps/目录下,创建一个配置文件app_usb_disp.cfg,内容如下:

# location of conifg
tidl_config   /opt/vision_apps/test_data/psdkra/tidl_models/tidl_io_peele_300_1.bin

# location of network
tidl_network  /opt/vision_apps/test_data/psdkra/tidl_models/tidl_net_peele_300.bin

# location of input files
input_file_path   /opt/vision_apps/test_data/psdkra/tidl_demo_images

# location of output files
output_file_path ./app_tidl_od_out

# start frame Number
start_frame 500

# number of frames
num_frames  400

# input size (width height)
in_size   1024 512

# size given to DL network (width height)
# This should should not be less than 4x of input width or height
dl_size   1024 512

# size given for display (width height)
# This should should not be less than 4x of input width or height
out_size  1024 512

# vizualization threshold
viz_th    0.95

# Maximum number of Object Detection classes
num_classes 90

# delay in milli seconds (max 2000ms)
delay_in_msecs 0

# Enable or disable output image writing. 1 Enables it , 0 disables it
en_out_img_write    0

# If 1 - Enable display 0 - Disable display
display_option      1

# number of iterations to loop the inputs
num_iterations      1

# interactive input mode 1: yes, 0: no
is_interactive      1

创建好以后,执行以下指令即可:

./vx_app_usb_disp.out --cfg app_usb_disp.cfg

在/home/root目录下可以看到生成了对应的h264文件。

五、将文件通过scp拷贝到ubuntu,播放查看

开发板上执行下面的指令:

scp /home/root/encode_output.h264 ubuntu@192.168.1.119:/home/ubuntu/my640.h264

我这个跑了一天,所以文件比较大。

在ubuntu上,使用mplayer或者VLC进行播放。

mplayer my.h264 -loop 0

完整工程下载代码地址:(升级了,完成了编码和解码功能)

[TITDA4J721E]USB摄像头YUV图像获取EncodeDecode节点使用和移植disp显示-编解码文档类资源-优快云下载


下一篇将从H264出发,进行反向解码Decode操作,生成YUV NV12图像。

https://blog.youkuaiyun.com/AIRKernel/article/details/122073727


【声明】

【欢迎转载转发,请注明出处。原创比较辛苦,请尊重原创,祝大家学习愉快!】

【博主专注嵌入式开发,具有多年嵌入式软、硬件开发经验,欢迎大家学习交流!】

【如有嵌入式相关项目需求,欢迎私信】

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值