首先感谢阅读,如果您也对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
【声明】
【欢迎转载转发,请注明出处。原创比较辛苦,请尊重原创,祝大家学习愉快!】
【博主专注嵌入式开发,具有多年嵌入式软、硬件开发经验,欢迎大家学习交流!】
【如有嵌入式相关项目需求,欢迎私信】