当前位置:   article > 正文

RK3568笔记十二:Zlmedia拉流显示测试_rkmedia拉流

rkmedia拉流

若该文为原创文章,转载请注明原文出处。

Zlmediakit功能很强大,测试一下拉流,在通过解码显示。

一、环境

1、平台:rk3568

2、开发板:ATK-RK3568正点原子板子

3、环境:buildroot

测试的代码在GitHub - airockchip/rknpu2

main_video.cc主要功能是通过Zlmedia拉取RTSP流,并解码,然后重新编码保存成视频,所以直接在例子上修改程序,增加DRM显示。

二、编译

1、修改交叉工具链

修改build-linux_RK3566_RK3568.sh,

2、增加DRM显示程序

screen_test.cc

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <sys/time.h>
  5. #include <dlfcn.h>
  6. #include <vector>
  7. #include <string>
  8. #include "rga_func.h"
  9. #include "rknn_api.h"
  10. #include "RgaUtils.h"
  11. #include "im2d.h"
  12. #include "opencv2/core/core.hpp"
  13. #include "opencv2/imgcodecs.hpp"
  14. #include "opencv2/imgproc.hpp"
  15. #include <opencv2/opencv.hpp>
  16. #include "rga.h"
  17. #include <xf86drm.h>
  18. #include <xf86drmMode.h>
  19. #include "dev.h"
  20. #include "bo.h"
  21. #include "screen_test.h"
  22. static sp_dev *mDev;
  23. #define OUTPUT_DEVICE_LCD 1
  24. static drmModeConnectorPtr lcdConnectorPtr = nullptr;
  25. static struct sp_crtc *lcdCRPtr;
  26. static drmModeEncoderPtr lcdEncoderPtr = nullptr;
  27. static uint32_t lcdCrtcId = 0;
  28. static drmModeModeInfoPtr lcdModInfoPtr;
  29. // 内部使用的函数原型声明
  30. static void get_connector(uint8_t outpuDevice);
  31. static void get_encoder(uint8_t outpuDevice);
  32. static void get_crtc(void);
  33. static int init_screens();
  34. static void get_connector(uint8_t outpuDevice)
  35. {
  36. int i, j = 0;
  37. int ret = 0;
  38. printf("mDev->num_connectors = %d\n", mDev->num_connectors);
  39. for (j = 0; j < mDev->num_connectors; j++)
  40. {
  41. // name 是分辨率信息
  42. printf("connector name:%d\n", j);
  43. printf("connector_type:%d\n", j);
  44. printf("connector_type_id:%d\n", j);
  45. printf("connector status:%d\n", j);
  46. // 对应不同的输出设备, 指定不同的connector跟encoder
  47. if (outpuDevice == OUTPUT_DEVICE_LCD)
  48. {
  49. if (mDev->connectors[j]->connector_type == DRM_MODE_CONNECTOR_DSI &&
  50. mDev->connectors[j]->connection == DRM_MODE_CONNECTED)
  51. {
  52. lcdConnectorPtr = mDev->connectors[j];
  53. }
  54. }
  55. }
  56. }
  57. static void get_encoder(uint8_t outpuDevice)
  58. {
  59. int i;
  60. for (i = 0; i < mDev->num_encoders; i++)
  61. {
  62. if (outpuDevice == OUTPUT_DEVICE_LCD)
  63. {
  64. if (mDev->encoders[i]->encoder_type == DRM_MODE_ENCODER_DSI)
  65. {
  66. lcdEncoderPtr = mDev->encoders[i];
  67. lcdCrtcId = lcdEncoderPtr->crtc_id;
  68. }
  69. }
  70. }
  71. }
  72. static void get_crtc(void)
  73. {
  74. int j;
  75. printf("lcd crtc id:%d\n", lcdCrtcId);
  76. for (j = 0; j < mDev->num_crtcs; j++)
  77. {
  78. printf("encoderPtr->crtc_id:%d\n", mDev->crtcs[j].crtc->crtc_id);
  79. printf("mode_valid:%d\n", mDev->crtcs[j].crtc->mode_valid);
  80. printf("mode_name:%s\n", mDev->crtcs[j].crtc->mode.name);
  81. if (mDev->crtcs[j].crtc->crtc_id == lcdCrtcId && mDev->crtcs[j].crtc->mode_valid)
  82. {
  83. lcdCRPtr = &mDev->crtcs[j];
  84. }
  85. }
  86. }
  87. static int init_screens()
  88. {
  89. int ret = 0;
  90. // 获取lcd connector
  91. get_connector(OUTPUT_DEVICE_LCD);
  92. if (!lcdConnectorPtr)
  93. {
  94. printf("failed to get hdmi connector or encoder.\n");
  95. return -1;
  96. }
  97. printf("lcd connector id:%d\n", lcdConnectorPtr->connector_id);
  98. // 获取lcd encoder
  99. get_encoder(OUTPUT_DEVICE_LCD);
  100. if (!lcdEncoderPtr)
  101. {
  102. printf("failed to get encoder.\n");
  103. return -2;
  104. }
  105. printf("lcd encoder id:%d\n", lcdEncoderPtr->encoder_id);
  106. // 获取一下显示分辨率之类
  107. lcdModInfoPtr = &lcdConnectorPtr->modes[0];
  108. // 把connector的encoder id赋值为encoder的id
  109. lcdConnectorPtr->encoder_id = lcdEncoderPtr->encoder_id;
  110. // 获取lcd crtc
  111. get_crtc();
  112. if (!lcdCRPtr)
  113. {
  114. printf("failed to get crtc.\n");
  115. return -3;
  116. }
  117. if (lcdCRPtr->scanout)
  118. {
  119. printf("crtc already in use\n");
  120. return -4;
  121. }
  122. printf("lcd crtc id:%d\n", lcdCRPtr->crtc->crtc_id);
  123. // allset
  124. // 获取bo, 只需要输入分辨率即可.
  125. lcdCRPtr->scanout = create_sp_bo(mDev, lcdModInfoPtr->hdisplay, lcdModInfoPtr->vdisplay, 24, 32, DRM_FORMAT_XRGB8888, 0);
  126. if (!lcdCRPtr->scanout)
  127. {
  128. printf("failed to create new scanout bo\n");
  129. return -5;
  130. }
  131. printf("fill test color\n");
  132. fill_bo(lcdCRPtr->scanout, 0xff, 0xff, 0x0, 0x0);
  133. ret = drmModeSetCrtc(mDev->fd, lcdEncoderPtr->crtc_id, lcdCRPtr->scanout->fb_id, 0, 0, &lcdConnectorPtr->connector_id, 1, lcdModInfoPtr);
  134. if (ret)
  135. {
  136. printf("failed to set crtc mode ret=%d\n", ret);
  137. return -6;
  138. }
  139. lcdCRPtr->crtc = drmModeGetCrtc(mDev->fd, lcdCRPtr->crtc->crtc_id);
  140. memcpy(&lcdCRPtr->crtc->mode, lcdModInfoPtr, sizeof(*lcdModInfoPtr));
  141. return 0;
  142. }
  143. int drm_dis_init(void)
  144. {
  145. int ret = 0;
  146. int i = 0;
  147. printf("create sp dev\n");
  148. // 创建显示设备
  149. mDev = create_sp_dev();
  150. if (!mDev)
  151. {
  152. printf("failed to exec create_sp_dev.\n");
  153. return -10;
  154. }
  155. printf("init_screen\n");
  156. // 初始化屏幕
  157. ret = init_screens();
  158. if (ret != 0)
  159. {
  160. printf("failed to exec initialize_screens.\n");
  161. return -11;
  162. }
  163. return 0;
  164. }
  165. void draw_lcd_screen_rgb_960(uint8_t *data, uint32_t dataSize)
  166. {
  167. uint32_t colIdx = 0;
  168. uint32_t rowIdx = 0;
  169. uint8_t *dataPtr = data;
  170. for (rowIdx = 0; rowIdx < 1280; rowIdx++)
  171. {
  172. uint8_t *rowPtr = (uint8_t *)lcdCRPtr->scanout->map_addr + rowIdx * lcdCRPtr->scanout->pitch;
  173. for (colIdx = 0; colIdx < 720; colIdx++)
  174. {
  175. uint8_t *pixel = rowPtr + colIdx * 4; // bgr
  176. #if 1
  177. pixel[0] = *dataPtr;
  178. dataPtr++;
  179. pixel[1] = *dataPtr;
  180. dataPtr++;
  181. pixel[2] = *dataPtr;
  182. dataPtr++;
  183. pixel[3] = 0xff;
  184. #else // bgra
  185. pixel[0] = 0xff; // B
  186. dataPtr++;
  187. pixel[1] = 0x00; //G
  188. dataPtr++;
  189. pixel[2] = 0x00; //R
  190. dataPtr++;
  191. pixel[3] = 0xFF;
  192. #endif
  193. }
  194. }
  195. }
  196. void draw_lcd_screen_rgb_dynamic(uint8_t *data, uint32_t dataSize, uint8_t screenNum, uint8_t rows, uint8_t cols)
  197. {
  198. if (rows == 0 || cols == 0 || screenNum >= rows * cols)
  199. {
  200. return; // 避免除零错误和数组越界
  201. }
  202. uint32_t startRowIdx, startColIdx;
  203. uint32_t screenWidth = LCD_SCREEN_WIDTH / cols;
  204. uint32_t screenHeight = LCD_SCREEN_HEIGHT / rows;
  205. // 计算起始行和列索引
  206. startRowIdx = (screenNum / cols) * screenHeight; // 根据屏幕编号计算起始行索引
  207. startColIdx = (screenNum % cols) * screenWidth; // 根据屏幕编号计算起始列索引
  208. uint32_t rowIdx, colIdx;
  209. uint8_t *dataPtr = data;
  210. for (rowIdx = startRowIdx; rowIdx < startRowIdx + screenHeight; rowIdx++)
  211. {
  212. uint8_t *rowPtr = (uint8_t *)lcdCRPtr->scanout->map_addr + rowIdx * lcdCRPtr->scanout->pitch;
  213. for (colIdx = startColIdx; colIdx < startColIdx + screenWidth; colIdx++)
  214. {
  215. uint8_t *pixel = rowPtr + colIdx * 4;
  216. memcpy(pixel, dataPtr, 4);
  217. dataPtr += 4;
  218. }
  219. }
  220. }
  221. void draw_lcd_screen_rgb_nine(uint8_t *data, uint32_t dataSize, uint8_t part)
  222. {
  223. uint32_t startRowIdx, startColIdx;
  224. uint32_t partWidth = LCD_SCREEN_WIDTH / 3;
  225. uint32_t partHeight = LCD_SCREEN_HEIGHT / 3;
  226. // 计算起始行和列索引
  227. startRowIdx = (part / 3) * partHeight; // 根据部分的行来计算起始行索引
  228. startColIdx = (part % 3) * partWidth; // 根据部分的列来计算起始列索引
  229. uint32_t rowIdx, colIdx;
  230. uint8_t *dataPtr = data;
  231. for (rowIdx = startRowIdx; rowIdx < startRowIdx + partHeight; rowIdx++)
  232. {
  233. uint8_t *rowPtr = (uint8_t *)lcdCRPtr->scanout->map_addr + rowIdx * lcdCRPtr->scanout->pitch;
  234. for (colIdx = startColIdx; colIdx < startColIdx + partWidth; colIdx++)
  235. {
  236. uint8_t *pixel = rowPtr + colIdx * 4;
  237. memcpy(pixel, dataPtr, 4);
  238. dataPtr += 4;
  239. }
  240. }
  241. }
  242. void draw_lcd_screen_rgb_quarter(uint8_t *data, uint32_t dataSize, uint8_t quarter)
  243. {
  244. uint32_t startRowIdx, startColIdx;
  245. switch (quarter)
  246. {
  247. case 0: // 左上角
  248. startRowIdx = 0;
  249. startColIdx = 0;
  250. break;
  251. case 1: // 右上角
  252. startRowIdx = 0;
  253. startColIdx = LCD_SCREEN_WIDTH / 2;
  254. break;
  255. case 2: // 左下角
  256. startRowIdx = LCD_SCREEN_HEIGHT / 2;
  257. startColIdx = 0;
  258. break;
  259. case 3: // 右下角
  260. startRowIdx = LCD_SCREEN_HEIGHT / 2;
  261. startColIdx = LCD_SCREEN_WIDTH / 2;
  262. break;
  263. default: // 默认为左上角
  264. startRowIdx = 0;
  265. startColIdx = 0;
  266. break;
  267. }
  268. uint32_t rowIdx, colIdx;
  269. uint8_t *dataPtr = data;
  270. for (rowIdx = startRowIdx; rowIdx < startRowIdx + LCD_SCREEN_HEIGHT / 2; rowIdx++)
  271. {
  272. uint8_t *rowPtr = (uint8_t *)lcdCRPtr->scanout->map_addr + rowIdx * lcdCRPtr->scanout->pitch;
  273. for (colIdx = startColIdx; colIdx < startColIdx + LCD_SCREEN_WIDTH / 2; colIdx++)
  274. {
  275. uint8_t *pixel = rowPtr + colIdx * 4;
  276. memcpy(pixel, dataPtr, 4);
  277. dataPtr += 4;
  278. }
  279. }
  280. }
  281. void draw_lcd_screen_rgb(uint8_t *data, uint32_t dataSize)
  282. {
  283. uint32_t colIdx = 0;
  284. uint32_t rowIdx = 0;
  285. uint8_t *dataPtr = data;
  286. for (rowIdx = 0; rowIdx < LCD_SCREEN_WIDTH; rowIdx++)
  287. {
  288. uint8_t *rowPtr = (uint8_t *)lcdCRPtr->scanout->map_addr + rowIdx * lcdCRPtr->scanout->pitch;
  289. for (colIdx = 0; colIdx < LCD_SCREEN_HEIGHT; colIdx++)
  290. {
  291. uint8_t *pixel = rowPtr + colIdx * 4;
  292. memcpy(pixel, dataPtr, 4);
  293. dataPtr += 4;
  294. #if 0
  295. uint8_t *pixel = rowPtr + colIdx * 4;
  296. pixel[0] = *dataPtr;
  297. dataPtr++;
  298. pixel[1] = *dataPtr;
  299. dataPtr++;
  300. pixel[2] = *dataPtr;
  301. dataPtr++;
  302. pixel[3] = 0xff;
  303. #endif
  304. }
  305. }
  306. }

3、格式转换显示

使用的是正点原子的5.5寸屏,在调试过程中,一直卡在显示部分,后面才发现,正点原子使用的是竖屏,横屏显示会出现问题。

修改mpp_decoder_frame_callback函数,解码后的格式是420_SP,需要转成BGRA8888格式才能显示,使用RGA转换格式

  1. memset(&src_rect, 0, sizeof(src_rect));
  2. memset(&dst_rect, 0, sizeof(dst_rect));
  3. memset(&src1, 0, sizeof(src1));
  4. memset(&dst1, 0, sizeof(dst1));
  5. printf("resize with RGA!\n");
  6. resize_buf = (unsigned char *)malloc(1280 * 720 * 3);
  7. memset(resize_buf, 0, 1280 * 720 * 3);
  8. printf("=========> width_stride: %d, height_stride: %d\n", width_stride, height_stride);
  9. src1 = wrapbuffer_virtualaddr((unsigned char *)mpp_frame_addr, width_stride, height_stride, RK_FORMAT_YCbCr_420_SP, width_stride, height_stride);
  10. dst1 = wrapbuffer_virtualaddr((unsigned char*)resize_buf, 720, 1280, RK_FORMAT_BGR_888);
  11. ret = imcheck(src1, dst1, src_rect, dst_rect);
  12. if (IM_STATUS_NOERROR != ret) {
  13. printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
  14. free(resize_buf);
  15. return ;
  16. }
  17. IM_STATUS STATUS = imresize(src1, dst1);
  18. //MySaveBmp("out.bmp", (unsigned char *)resize_buf, 1280, 720);
  19. // 显示在屏目上。
  20. printf("draw_lcd_screen_rgb_960==============>\n");
  21. draw_lcd_screen_rgb_960((uint8_t *)resize_buf, 1280 * 720 * 3);
  22. printf("draw_lcd_screen_rgb_960 ok\n");
  23. free(resize_buf);

需要注意 dst1 = wrapbuffer_virtualaddr((unsigned char*)resize_buf, 720, 1280, RK_FORMAT_BGR_888);,原本图片是1280*720,需要对调才能正常显示。

三、推流

先准备一个1280*720的视频,使用的是FFMPEG方式推流,但直接推流是无法拉流的,所以先启动一个RTSP服务器。

服务器下载地址Releases · aler9/rtsp-simple-server · GitHub

  • 启动rtsp-simple-server

下载完成后解压缩然后执行里面的rtsp-simple-server.exe

ffmpeg推流直接使用命令

ffmpeg -re -stream_loop -1 -i test.mp4 -c copy -f rtsp rtsp://192.168.0.107:8554/stream

四、程序解析

  1. // Copyright (c) 2023 by Rockchip Electronics Co., Ltd. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /*-------------------------------------------
  15. Includes
  16. -------------------------------------------*/
  17. #include <dlfcn.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include <sys/time.h>
  23. #include "im2d.h"
  24. #include "rga.h"
  25. #include "RgaUtils.h"
  26. #include "rknn_api.h"
  27. #include "postprocess.h"
  28. #include "utils/mpp_decoder.h"
  29. #include "utils/mpp_encoder.h"
  30. #include "utils/drawing.h"
  31. #include "im2d_buffer.h"
  32. #include "screen_test.h"
  33. #if defined(BUILD_VIDEO_RTSP)
  34. #include "mk_mediakit.h"
  35. #endif
  36. #define OUT_VIDEO_PATH "out.h264"
  37. #define LOG_TAG "MPP_API"
  38. static FILE *g_save_nv12; // nv12
  39. typedef struct {
  40. rknn_context rknn_ctx;
  41. rknn_input_output_num io_num;
  42. rknn_tensor_attr* input_attrs;
  43. rknn_tensor_attr* output_attrs;
  44. int model_channel;
  45. int model_width;
  46. int model_height;
  47. FILE* out_fp;
  48. MppDecoder* decoder;
  49. MppEncoder* encoder;
  50. } rknn_app_context_t;
  51. typedef struct {
  52. int width;
  53. int height;
  54. int width_stride;
  55. int height_stride;
  56. int format;
  57. char* virt_addr;
  58. int fd;
  59. } image_frame_t;
  60. /*-------------------------------------------
  61. Functions
  62. -------------------------------------------*/
  63. static void dump_tensor_attr(rknn_tensor_attr* attr)
  64. {
  65. printf(" index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, "
  66. "zp=%d, scale=%f\n",
  67. attr->index, attr->name, attr->n_dims, attr->dims[0], attr->dims[1], attr->dims[2], attr->dims[3],
  68. attr->n_elems, attr->size, get_format_string(attr->fmt), get_type_string(attr->type),
  69. get_qnt_type_string(attr->qnt_type), attr->zp, attr->scale);
  70. }
  71. double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); }
  72. static unsigned char* load_data(FILE* fp, size_t ofst, size_t sz)
  73. {
  74. unsigned char* data;
  75. int ret;
  76. data = NULL;
  77. if (NULL == fp) {
  78. return NULL;
  79. }
  80. ret = fseek(fp, ofst, SEEK_SET);
  81. if (ret != 0) {
  82. printf("blob seek failure.\n");
  83. return NULL;
  84. }
  85. data = (unsigned char*)malloc(sz);
  86. if (data == NULL) {
  87. printf("buffer malloc failure.\n");
  88. return NULL;
  89. }
  90. ret = fread(data, 1, sz, fp);
  91. return data;
  92. }
  93. static unsigned char* read_file_data(const char* filename, int* model_size)
  94. {
  95. FILE* fp;
  96. unsigned char* data;
  97. fp = fopen(filename, "rb");
  98. if (NULL == fp) {
  99. printf("Open file %s failed.\n", filename);
  100. return NULL;
  101. }
  102. fseek(fp, 0, SEEK_END);
  103. int size = ftell(fp);
  104. data = load_data(fp, 0, size);
  105. fclose(fp);
  106. *model_size = size;
  107. return data;
  108. }
  109. static int write_data_to_file(const char *path, char *data, unsigned int size) {
  110. FILE *fp;
  111. fp = fopen(path, "w");
  112. if(fp == NULL) {
  113. printf("open error: %s", path);
  114. return -1;
  115. }
  116. fwrite(data, 1, size, fp);
  117. fflush(fp);
  118. fclose(fp);
  119. return 0;
  120. }
  121. static int init_model(const char* model_path, rknn_app_context_t* app_ctx) {
  122. int ret;
  123. rknn_context ctx;
  124. /* Create the neural network */
  125. printf("Loading mode...\n");
  126. int model_data_size = 0;
  127. unsigned char* model_data = read_file_data(model_path, &model_data_size);
  128. if (model_data == NULL) {
  129. return -1;
  130. }
  131. ret = rknn_init(&ctx, model_data, model_data_size, 0, NULL);
  132. if (ret < 0) {
  133. printf("rknn_init error ret=%d\n", ret);
  134. return -1;
  135. }
  136. if (model_data) {
  137. free(model_data);
  138. }
  139. rknn_sdk_version version;
  140. ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, sizeof(rknn_sdk_version));
  141. if (ret < 0) {
  142. printf("rknn_query RKNN_QUERY_SDK_VERSION error ret=%d\n", ret);
  143. return -1;
  144. }
  145. printf("sdk version: %s driver version: %s\n", version.api_version, version.drv_version);
  146. ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &app_ctx->io_num, sizeof(rknn_input_output_num));
  147. if (ret < 0) {
  148. printf("rknn_query RKNN_QUERY_IN_OUT_NUM error ret=%d\n", ret);
  149. return -1;
  150. }
  151. printf("model input num: %d, output num: %d\n", app_ctx->io_num.n_input, app_ctx->io_num.n_output);
  152. rknn_tensor_attr* input_attrs = (rknn_tensor_attr*)malloc(app_ctx->io_num.n_input * sizeof(rknn_tensor_attr));
  153. memset(input_attrs, 0, sizeof(input_attrs));
  154. for (int i = 0; i < app_ctx->io_num.n_input; i++) {
  155. input_attrs[i].index = i;
  156. ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr));
  157. if (ret < 0) {
  158. printf("rknn_query RKNN_QUERY_INPUT_ATTR error ret=%d\n", ret);
  159. return -1;
  160. }
  161. dump_tensor_attr(&(input_attrs[i]));
  162. }
  163. rknn_tensor_attr* output_attrs = (rknn_tensor_attr*)malloc(app_ctx->io_num.n_output * sizeof(rknn_tensor_attr));
  164. memset(output_attrs, 0, sizeof(output_attrs));
  165. for (int i = 0; i < app_ctx->io_num.n_output; i++) {
  166. output_attrs[i].index = i;
  167. ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));
  168. if (ret < 0) {
  169. printf("rknn_query RKNN_QUERY_OUTPUT_ATTR error ret=%d\n", ret);
  170. return -1;
  171. }
  172. dump_tensor_attr(&(output_attrs[i]));
  173. }
  174. app_ctx->input_attrs = input_attrs;
  175. app_ctx->output_attrs = output_attrs;
  176. app_ctx->rknn_ctx = ctx;
  177. if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) {
  178. printf("model is NCHW input fmt\n");
  179. app_ctx->model_channel = input_attrs[0].dims[1];
  180. app_ctx->model_height = input_attrs[0].dims[2];
  181. app_ctx->model_width = input_attrs[0].dims[3];
  182. } else {
  183. printf("model is NHWC input fmt\n");
  184. app_ctx->model_height = input_attrs[0].dims[1];
  185. app_ctx->model_width = input_attrs[0].dims[2];
  186. app_ctx->model_channel = input_attrs[0].dims[3];
  187. }
  188. printf("model input height=%d, width=%d, channel=%d\n", app_ctx->model_height, app_ctx->model_width, app_ctx->model_channel);
  189. return 0;
  190. }
  191. static int release_model(rknn_app_context_t* app_ctx) {
  192. if (app_ctx->rknn_ctx != NULL) {
  193. rknn_destroy(app_ctx->rknn_ctx);
  194. }
  195. free(app_ctx->input_attrs);
  196. free(app_ctx->output_attrs);
  197. deinitPostProcess();
  198. return 0;
  199. }
  200. static int inference_model(rknn_app_context_t* app_ctx, image_frame_t* img, detect_result_group_t* detect_result) {
  201. int ret;
  202. rknn_context ctx = app_ctx->rknn_ctx;
  203. int model_width = app_ctx->model_width;
  204. int model_height = app_ctx->model_height;
  205. int model_channel = app_ctx->model_channel;
  206. struct timeval start_time, stop_time;
  207. const float nms_threshold = NMS_THRESH;
  208. const float box_conf_threshold = BOX_THRESH;
  209. // You may not need resize when src resulotion equals to dst resulotion
  210. void* resize_buf = nullptr;
  211. // init rga context
  212. rga_buffer_t src;
  213. rga_buffer_t dst;
  214. im_rect src_rect;
  215. im_rect dst_rect;
  216. memset(&src_rect, 0, sizeof(src_rect));
  217. memset(&dst_rect, 0, sizeof(dst_rect));
  218. memset(&src, 0, sizeof(src));
  219. memset(&dst, 0, sizeof(dst));
  220. printf("input image %dx%d stride %dx%d format=%d\n", img->width, img->height, img->width_stride, img->height_stride, img->format);
  221. float scale_w = (float)model_width / img->width;
  222. float scale_h = (float)model_height / img->height;
  223. rknn_input inputs[1];
  224. memset(inputs, 0, sizeof(inputs));
  225. inputs[0].index = 0;
  226. inputs[0].type = RKNN_TENSOR_UINT8;
  227. inputs[0].size = model_width * model_height * model_channel;
  228. inputs[0].fmt = RKNN_TENSOR_NHWC;
  229. inputs[0].pass_through = 0;
  230. printf("resize with RGA!\n");
  231. resize_buf = malloc(model_width * model_height * model_channel);
  232. memset(resize_buf, 0, model_width * model_height * model_channel);
  233. src = wrapbuffer_virtualaddr((void*)img->virt_addr, img->width, img->height, img->format, img->width_stride, img->height_stride);
  234. dst = wrapbuffer_virtualaddr((void*)resize_buf, model_width, model_height, RK_FORMAT_RGB_888);
  235. ret = imcheck(src, dst, src_rect, dst_rect);
  236. if (IM_STATUS_NOERROR != ret) {
  237. printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
  238. return -1;
  239. }
  240. IM_STATUS STATUS = imresize(src, dst);
  241. inputs[0].buf = resize_buf;
  242. gettimeofday(&start_time, NULL);
  243. rknn_inputs_set(ctx, app_ctx->io_num.n_input, inputs);
  244. rknn_output outputs[app_ctx->io_num.n_output];
  245. memset(outputs, 0, sizeof(outputs));
  246. for (int i = 0; i < app_ctx->io_num.n_output; i++) {
  247. outputs[i].want_float = 0;
  248. }
  249. ret = rknn_run(ctx, NULL);
  250. ret = rknn_outputs_get(ctx, app_ctx->io_num.n_output, outputs, NULL);
  251. gettimeofday(&stop_time, NULL);
  252. printf("once run use %f ms\n", (__get_us(stop_time) - __get_us(start_time)) / 1000);
  253. printf("post process config: box_conf_threshold = %.2f, nms_threshold = %.2f\n", box_conf_threshold, nms_threshold);
  254. std::vector<float> out_scales;
  255. std::vector<int32_t> out_zps;
  256. for (int i = 0; i < app_ctx->io_num.n_output; ++i) {
  257. out_scales.push_back(app_ctx->output_attrs[i].scale);
  258. out_zps.push_back(app_ctx->output_attrs[i].zp);
  259. }
  260. post_process((int8_t*)outputs[0].buf, (int8_t*)outputs[1].buf, (int8_t*)outputs[2].buf, model_height, model_width,
  261. box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, detect_result);
  262. ret = rknn_outputs_release(ctx, app_ctx->io_num.n_output, outputs);
  263. if (resize_buf) {
  264. free(resize_buf);
  265. }
  266. return 0;
  267. }
  268. typedef struct /**** BMP file info structure ****/
  269. {
  270. unsigned int biSize; /* Size of info header */
  271. int biWidth; /* Width of image */
  272. int biHeight; /* Height of image */
  273. unsigned short biPlanes; /* Number of color planes */
  274. unsigned short biBitCount; /* Number of bits per pixel */
  275. unsigned int biCompression; /* Type of compression to use */
  276. unsigned int biSizeImage; /* Size of image data */
  277. int biXPelsPerMeter; /* X pixels per meter */
  278. int biYPelsPerMeter; /* Y pixels per meter */
  279. unsigned int biClrUsed; /* Number of colors used */
  280. unsigned int biClrImportant; /* Number of important colors */
  281. } BITMAPINFOHEADER;
  282. typedef struct /**** BMP file header structure ****/
  283. {
  284. unsigned int bfSize; /* Size of file */
  285. unsigned short bfReserved1; /* Reserved */
  286. unsigned short bfReserved2; /* ... */
  287. unsigned int bfOffBits; /* Offset to bitmap data */
  288. } BITMAPFILEHEADER;
  289. void MySaveBmp(const char *filename,unsigned char *rgbbuf,int width,int height)
  290. {
  291. BITMAPFILEHEADER bfh;
  292. BITMAPINFOHEADER bih;
  293. /*
  294. * Magic number for file. It does not fit in the header structure due to
  295. * alignment requirements, so put it outside
  296. * 文件的魔术字,由于对齐的需要,没办法将魔术字作为BITMAPFILEHEADER的成员,所以
  297. * 这里将魔术字放在BITMAPFILEHEADER开头外面的位置。
  298. */
  299. unsigned short bfType=0x4d42; //'BM'
  300. bfh.bfReserved1 = 0;
  301. bfh.bfReserved2 = 0;
  302. bfh.bfSize = 2/* 2B魔术字 */+sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+width*height*3;
  303. bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
  304. bih.biSize = sizeof(BITMAPINFOHEADER);
  305. bih.biWidth = width;
  306. bih.biHeight = height;
  307. bih.biPlanes = 1;
  308. bih.biBitCount = 24;
  309. bih.biCompression = 0;
  310. bih.biSizeImage = 0;
  311. bih.biXPelsPerMeter = 5000;
  312. bih.biYPelsPerMeter = 5000;
  313. bih.biClrUsed = 0;
  314. bih.biClrImportant = 0;
  315. FILE *file = fopen(filename, "wb");
  316. if (!file)
  317. {
  318. printf("Could not write file\n");
  319. return;
  320. }
  321. /*Write headers*/
  322. fwrite(&bfType,sizeof(bfType),1,file);
  323. fwrite(&bfh,sizeof(bfh),1, file);
  324. fwrite(&bih,sizeof(bih),1, file);
  325. fwrite(rgbbuf,width*height*3,1,file);
  326. fclose(file);
  327. }
  328. // MPP解码回调函数
  329. void mpp_decoder_frame_callback(void* userdata, int width_stride, int height_stride, int width, int height, int format, int fd, void* data) {
  330. rknn_app_context_t* ctx = (rknn_app_context_t*)userdata;
  331. int ret = 0;
  332. static int frame_index = 0;
  333. frame_index++;
  334. void* mpp_frame = NULL;
  335. int mpp_frame_fd = 0;
  336. void* mpp_frame_addr = NULL;
  337. int mpp_frame_size;
  338. int enc_data_size;
  339. rga_buffer_t origin;
  340. rga_buffer_t src;
  341. rga_buffer_t src1;
  342. rga_buffer_t dst1;
  343. unsigned char *resize_buf = nullptr;
  344. im_rect src_rect;
  345. im_rect dst_rect;
  346. // 编码器初始化
  347. if (ctx->encoder == NULL) {
  348. MppEncoder* mpp_encoder = new MppEncoder();
  349. MppEncoderParams enc_params;
  350. memset(&enc_params, 0, sizeof(MppEncoderParams));
  351. enc_params.width = width;
  352. enc_params.height = height;
  353. enc_params.hor_stride = width_stride;
  354. enc_params.ver_stride = height_stride;
  355. enc_params.fmt = MPP_FMT_YUV420SP;
  356. //enc_params.type = MPP_VIDEO_CodingHEVC;
  357. //Note: rk3562只能支持h264格式的视频流
  358. enc_params.type = MPP_VIDEO_CodingAVC;
  359. mpp_encoder->Init(enc_params, NULL);
  360. ctx->encoder = mpp_encoder;
  361. }
  362. int enc_buf_size = ctx->encoder->GetFrameSize();
  363. char* enc_data = (char*)malloc(enc_buf_size);
  364. // 图片格式
  365. image_frame_t img;
  366. img.width = width;
  367. img.height = height;
  368. img.width_stride = width_stride;
  369. img.height_stride = height_stride;
  370. img.fd = fd;
  371. img.virt_addr = (char*)data;
  372. img.format = RK_FORMAT_YCbCr_420_SP;
  373. detect_result_group_t detect_result;
  374. memset(&detect_result, 0, sizeof(detect_result_group_t));
  375. // RKNN推理
  376. ret = inference_model(ctx, &img, &detect_result);
  377. if (ret != 0)
  378. {
  379. printf("inference model fail\n");
  380. if(enc_data)
  381. free(enc_data);
  382. return ;
  383. }
  384. mpp_frame = ctx->encoder->GetInputFrameBuffer();
  385. mpp_frame_fd = ctx->encoder->GetInputFrameBufferFd(mpp_frame);
  386. mpp_frame_addr = ctx->encoder->GetInputFrameBufferAddr(mpp_frame);
  387. mpp_frame_size = ctx->encoder->GetFrameSize();
  388. // 图片格式转换
  389. // Copy To another buffer avoid to modify mpp decoder buffer
  390. printf("wrapbuffer_fd==> width: %d, height: %d\n", width, height);
  391. origin = wrapbuffer_fd(fd, width, height, RK_FORMAT_YCbCr_420_SP, width_stride, height_stride);
  392. src = wrapbuffer_fd(mpp_frame_fd, width, height, RK_FORMAT_YCbCr_420_SP, width_stride, height_stride);
  393. imcopy(origin, src);
  394. // Draw objects
  395. for (int i = 0; i < detect_result.count; i++) {
  396. detect_result_t* det_result = &(detect_result.results[i]);
  397. printf("%s @ (%d %d %d %d) %f\n", det_result->name, det_result->box.left, det_result->box.top,
  398. det_result->box.right, det_result->box.bottom, det_result->prop);
  399. int x1 = det_result->box.left;
  400. int y1 = det_result->box.top;
  401. int x2 = det_result->box.right;
  402. int y2 = det_result->box.bottom;
  403. // 画框
  404. draw_rectangle_yuv420sp((unsigned char*)mpp_frame_addr, width_stride, height_stride, x1, y1, x2-x1+1, y2-y1+1, 0x00FF0000, 4);
  405. }
  406. // 保存原始视频流
  407. #if 0
  408. if (g_save_nv12) {
  409. fwrite((unsigned char*)mpp_frame_addr, 1, mpp_frame_size, g_save_nv12);
  410. printf("#Save len-%d to %s\n", mpp_frame_size, g_save_nv12);
  411. }
  412. #endif
  413. // 格式转换,420_SP转成BGR888
  414. memset(&src_rect, 0, sizeof(src_rect));
  415. memset(&dst_rect, 0, sizeof(dst_rect));
  416. memset(&src1, 0, sizeof(src1));
  417. memset(&dst1, 0, sizeof(dst1));
  418. printf("resize with RGA!\n");
  419. resize_buf = (unsigned char *)malloc(1280 * 720 * 3);
  420. memset(resize_buf, 0, 1280 * 720 * 3);
  421. printf("=========> width_stride: %d, height_stride: %d\n", width_stride, height_stride);
  422. src1 = wrapbuffer_virtualaddr((unsigned char *)mpp_frame_addr, width_stride, height_stride, RK_FORMAT_YCbCr_420_SP, width_stride, height_stride);
  423. dst1 = wrapbuffer_virtualaddr((unsigned char*)resize_buf, 720, 1280, RK_FORMAT_BGR_888);
  424. ret = imcheck(src1, dst1, src_rect, dst_rect);
  425. if (IM_STATUS_NOERROR != ret) {
  426. printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
  427. free(resize_buf);
  428. return ;
  429. }
  430. IM_STATUS STATUS = imresize(src1, dst1);
  431. //MySaveBmp("out.bmp", (unsigned char *)resize_buf, 1280, 720);
  432. // 显示在屏目上。
  433. printf("draw_lcd_screen_rgb_960==============>\n");
  434. draw_lcd_screen_rgb_960((uint8_t *)resize_buf, 1280 * 720 * 3);
  435. printf("draw_lcd_screen_rgb_960 ok\n");
  436. free(resize_buf);
  437. printf("waite get char to next step!\n");
  438. //getchar();
  439. printf("====>Encode\n");
  440. // Encode to file
  441. // Write header on first frame
  442. if (frame_index == 1) {
  443. enc_data_size = ctx->encoder->GetHeader(enc_data, enc_buf_size);
  444. fwrite(enc_data, 1, enc_data_size, ctx->out_fp);
  445. }
  446. printf("====>GetHeader ok\n");
  447. memset(enc_data, 0, enc_buf_size);
  448. enc_data_size = ctx->encoder->Encode(mpp_frame, enc_data, enc_buf_size);
  449. printf("====>Encode ok\n");
  450. fwrite(enc_data, 1, enc_data_size, ctx->out_fp);
  451. if (enc_data != nullptr)
  452. {
  453. free(enc_data);
  454. }
  455. printf("fwrite ok\n");
  456. }
  457. int process_video_file(rknn_app_context_t* ctx, const char* path)
  458. {
  459. int video_size;
  460. char* video_data = (char*)read_file_data(path, &video_size);
  461. char* video_data_end = video_data + video_size;
  462. printf("read video size=%d\n", video_size);
  463. const int SIZE = 8192;
  464. char* video_data_ptr = video_data;
  465. do {
  466. int pkt_eos = 0;
  467. int size = SIZE;
  468. if (video_data_ptr + size >= video_data_end) {
  469. pkt_eos = 1;
  470. size = video_data_end - video_data_ptr;
  471. }
  472. ctx->decoder->Decode((uint8_t*)video_data_ptr, size, pkt_eos);
  473. video_data_ptr += size;
  474. if (video_data_ptr >= video_data_end) {
  475. printf("reset decoder\n");
  476. break;
  477. }
  478. // LOGD("video_data_ptr=%p video_data_end=%p", video_data_ptr, video_data_end);
  479. // usleep(10*1000);
  480. } while (1);
  481. return 0;
  482. }
  483. #if defined(BUILD_VIDEO_RTSP)
  484. void API_CALL on_track_frame_out(void *user_data, mk_frame frame) {
  485. rknn_app_context_t *ctx = (rknn_app_context_t *) user_data;
  486. printf("on_track_frame_out ctx=%p\n", ctx);
  487. const char* data = mk_frame_get_data(frame);
  488. size_t size = mk_frame_get_data_size(frame);
  489. printf("decoder=%p\n", ctx->decoder);
  490. ctx->decoder->Decode((uint8_t*)data, size, 0);
  491. }
  492. void API_CALL on_mk_play_event_func(void *user_data, int err_code, const char *err_msg, mk_track tracks[],
  493. int track_count) {
  494. rknn_app_context_t *ctx = (rknn_app_context_t *) user_data;
  495. if (err_code == 0) {
  496. //success
  497. printf("play success!");
  498. int i;
  499. for (i = 0; i < track_count; ++i) {
  500. if (mk_track_is_video(tracks[i])) {
  501. log_info("got video track: %s", mk_track_codec_name(tracks[i]));
  502. //监听track数据回调
  503. mk_track_add_delegate(tracks[i], on_track_frame_out, user_data);
  504. }
  505. }
  506. } else {
  507. printf("play failed: %d %s", err_code, err_msg);
  508. }
  509. }
  510. void API_CALL on_mk_shutdown_func(void *user_data, int err_code, const char *err_msg, mk_track tracks[], int track_count) {
  511. printf("play interrupted: %d %s", err_code, err_msg);
  512. }
  513. // 下面为Zlmeidia拉流处理
  514. int process_video_rtsp(rknn_app_context_t* ctx, const char* url)
  515. {
  516. mk_config config;
  517. memset(&config, 0, sizeof(mk_config));
  518. config.log_mask = LOG_CONSOLE;
  519. mk_env_init(&config);
  520. mk_player player = mk_player_create();
  521. mk_player_set_on_result(player, on_mk_play_event_func, ctx);
  522. mk_player_set_on_shutdown(player, on_mk_shutdown_func, ctx);
  523. mk_player_play(player, url);
  524. printf("enter any key to exit\n");
  525. getchar();
  526. if (player) {
  527. mk_player_release(player);
  528. }
  529. return 0;
  530. }
  531. #endif
  532. /*-------------------------------------------
  533. Main Functions
  534. -------------------------------------------*/
  535. int main(int argc, char** argv)
  536. {
  537. int status = 0;
  538. int ret;
  539. if (argc != 4) {
  540. printf("Usage: %s <rknn_model> <video_path> <video_type 264/265> \n", argv[0]);
  541. return -1;
  542. }
  543. char* model_name = (char*)argv[1];
  544. char* video_name = argv[2];
  545. int video_type = atoi(argv[3]);
  546. rknn_app_context_t app_ctx;
  547. memset(&app_ctx, 0, sizeof(rknn_app_context_t));
  548. ret = init_model(model_name, &app_ctx);
  549. if (ret != 0) {
  550. printf("init model fail\n");
  551. return -1;
  552. }
  553. // MPP解码器
  554. if (app_ctx.decoder == NULL) {
  555. MppDecoder* decoder = new MppDecoder();
  556. decoder->Init(video_type, 30, &app_ctx);
  557. decoder->SetCallback(mpp_decoder_frame_callback);
  558. app_ctx.decoder = decoder;
  559. }
  560. // 视频保存
  561. if (app_ctx.out_fp == NULL) {
  562. FILE* fp = fopen(OUT_VIDEO_PATH, "w");
  563. if(fp == NULL) {
  564. printf("open %s error\n", OUT_VIDEO_PATH);
  565. return -1;
  566. }
  567. app_ctx.out_fp = fp;
  568. }
  569. // NV12保存
  570. g_save_nv12 = fopen("output.nv12", "w");
  571. if (!g_save_nv12)
  572. printf("#VENC TEST:: Open ./output.nv12 failed!\n");
  573. printf("app_ctx=%p decoder=%p\n", &app_ctx, app_ctx.decoder);
  574. // 判断是不是RTSP流还是文件
  575. if (strncmp(video_name, "rtsp", 4) == 0) {
  576. #if defined(BUILD_VIDEO_RTSP)
  577. // DRM初始化
  578. drm_dis_init();
  579. // 拉流处理
  580. process_video_rtsp(&app_ctx, video_name);
  581. #else
  582. printf("rtsp no support\n");
  583. #endif
  584. } else {
  585. // 文件流处理
  586. process_video_file(&app_ctx, video_name);
  587. }
  588. printf("waiting finish\n");
  589. usleep(3*1000*1000);
  590. // release
  591. fflush(app_ctx.out_fp);
  592. fclose(app_ctx.out_fp);
  593. if (app_ctx.decoder != nullptr) {
  594. delete(app_ctx.decoder);
  595. app_ctx.decoder = nullptr;
  596. }
  597. if (app_ctx.encoder != nullptr) {
  598. delete(app_ctx.encoder);
  599. app_ctx.encoder = nullptr;
  600. }
  601. release_model(&app_ctx);
  602. return 0;
  603. }

通过Zlmedia很方便的拉取RTSP流,并解码显示。使用DRM是为了多路显示。OPENCV只会显示一路,不知道怎么拼接流显示。

如有侵权,或需要完整代码,请及时联系博主。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号