test_rtsp.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /*************************************************************************
  2. * Copyright (C) [2019] by Cambricon, Inc. All rights reserved
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  13. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  15. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. * THE SOFTWARE.
  19. *************************************************************************/
  20. #include "opencv2/highgui/highgui.hpp"
  21. #include "opencv2/imgproc/imgproc.hpp"
  22. #if (CV_MAJOR_VERSION >= 3)
  23. #include "opencv2/imgcodecs/imgcodecs.hpp"
  24. #endif
  25. #ifdef __cplusplus
  26. extern "C" {
  27. #endif
  28. #include <libavcodec/avcodec.h>
  29. #include <libavformat/avformat.h>
  30. #include <libavutil/avutil.h>
  31. #ifdef __cplusplus
  32. }
  33. #endif
  34. #include <arpa/inet.h>
  35. #include <ifaddrs.h>
  36. #include <net/if.h>
  37. #include <netdb.h>
  38. #include <stdio.h>
  39. #include <string.h>
  40. #include <sys/ioctl.h>
  41. #include <unistd.h>
  42. #include <future>
  43. #include <iostream>
  44. #include <memory>
  45. #include <string>
  46. #include <vector>
  47. #include "device/mlu_context.h"
  48. #include "easyinfer/mlu_memory_op.h"
  49. #include "gtest/gtest.h"
  50. #include "cnstream_frame_va.hpp"
  51. #include "rtsp_sink.hpp"
  52. #include "test_base.hpp"
  53. namespace cnstream {
  54. static constexpr const char *gname = "rtsp";
  55. static constexpr int g_dev_id = 0;
  56. static constexpr int g_width = 1280;
  57. static constexpr int g_height = 720;
  58. static int g_channel_id = 0;
  59. static int g_frame_id = 0;
  60. std::string GetIp() {
  61. void *tmpAddrPtr = NULL;
  62. struct ifaddrs *ifAddrStruct = NULL;
  63. getifaddrs(&ifAddrStruct);
  64. std::string valid_ip;
  65. while (ifAddrStruct != NULL) {
  66. if (ifAddrStruct->ifa_addr->sa_family == AF_INET) {
  67. tmpAddrPtr = &(reinterpret_cast<struct sockaddr_in *>(ifAddrStruct->ifa_addr))->sin_addr;
  68. char addressBuffer[INET_ADDRSTRLEN];
  69. inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
  70. // LOGI(RTSP_UNITTEST) << "Check out ip4: " << ifAddrStruct->ifa_name << ":" << addressBuffer;
  71. std::string str = addressBuffer;
  72. if (str.substr(0, 1) != "0" && str.substr(0, 3) != "127" && str.substr(0, 3) != "172") {
  73. valid_ip = str;
  74. break;
  75. }
  76. }
  77. valid_ip = "get invalid ip ...";
  78. ifAddrStruct = ifAddrStruct->ifa_next;
  79. }
  80. LOGI(RTSP_UNITTEST) << "valid_ip: " << valid_ip;
  81. return valid_ip;
  82. }
  83. bool PullRtspStreamOpencv(int port = 9445) {
  84. std::string url = "rtsp://" + GetIp() + ":" + std::to_string(port) + "/live";
  85. cv::VideoCapture capture(url);
  86. if (!capture.isOpened()) {
  87. return false;
  88. }
  89. cv::Mat frame;
  90. int i = 3;
  91. while (i--) {
  92. if (!capture.read(frame)) {
  93. return false;
  94. }
  95. cv::waitKey(30);
  96. }
  97. return true;
  98. }
  99. bool PullRtspStreamFFmpeg(int port = 9445) {
  100. if (port == -1) return true;
  101. AVFormatContext *format_ctx = avformat_alloc_context();
  102. std::string url = "rtsp://" + GetIp() + ":" + std::to_string(port) + "/live";
  103. LOGI(RTSP_UNITTEST) << "Pull rtsp stream, url: " << url;
  104. int ret = -1;
  105. AVDictionary *opts = nullptr;
  106. av_dict_set(&opts, "stimeout", "10000", 0);
  107. ret = avformat_open_input(&format_ctx, url.c_str(), nullptr, &opts);
  108. if (ret != 0) {
  109. fprintf(stderr, "fail to open url: %s, return value: %d\n", url.c_str(), ret);
  110. return -1;
  111. }
  112. ret = avformat_find_stream_info(format_ctx, nullptr);
  113. if (ret < 0) {
  114. fprintf(stderr, "fail to get stream information: %d\n", ret);
  115. return -1;
  116. }
  117. int video_stream_index = -1;
  118. fprintf(stdout, "Number of elements in AVFormatContext.streams: %d\n", format_ctx->nb_streams);
  119. for (uint32_t i = 0; i < format_ctx->nb_streams; ++i) {
  120. const AVStream *vstream = format_ctx->streams[i];
  121. CNS_IGNORE_DEPRECATED_PUSH
  122. #if LIBAVFORMAT_VERSION_INT < FFMPEG_VERSION_3_1
  123. fprintf(stdout, "type of the encoded data: %d\n", vstream->codecpar->codec_id);
  124. if (vstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
  125. video_stream_index = i;
  126. fprintf(stdout, "dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",
  127. vstream->codecpar->width, vstream->codecpar->height, vstream->codecpar->format);
  128. #else
  129. fprintf(stdout, "type of the encoded data: %d\n", vstream->codec->codec_id);
  130. if (vstream->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
  131. video_stream_index = i;
  132. fprintf(stdout, "dimensions of the video frame in pixels: width: %d, height: %d\n", vstream->codec->width,
  133. vstream->codec->height);
  134. #endif
  135. CNS_IGNORE_DEPRECATED_POP
  136. }
  137. }
  138. if (video_stream_index == -1) {
  139. fprintf(stderr, "no video stream\n");
  140. return -1;
  141. }
  142. int cnt = 0;
  143. AVPacket pkt;
  144. while (1) {
  145. if (++cnt > 5) break;
  146. ret = av_read_frame(format_ctx, &pkt);
  147. if (ret < 0) {
  148. fprintf(stderr, "error or end of file: %d\n", ret);
  149. continue;
  150. }
  151. if (pkt.stream_index == video_stream_index) {
  152. fprintf(stdout, "video stream, packet size: %d\n", pkt.size);
  153. }
  154. av_packet_unref(&pkt);
  155. }
  156. avformat_close_input(&format_ctx);
  157. avformat_free_context(format_ctx);
  158. return true;
  159. }
  160. std::shared_ptr<CNFrameInfo> GenTestData(CNPixelFormat pix_fmt, int width, int height, int frame_rate,
  161. void **frame_data_ptr) {
  162. size_t nbytes = width * height * sizeof(uint8_t) * 3;
  163. size_t boundary = 1 << 16;
  164. nbytes = (nbytes + boundary - 1) & ~(boundary - 1); // align to 64kb
  165. // fake data
  166. void *planes[CN_MAX_PLANES] = {nullptr, nullptr, nullptr};
  167. edk::MluMemoryOp mem_op;
  168. *frame_data_ptr = mem_op.AllocMlu(nbytes);
  169. cnrtMemset(*frame_data_ptr, 0, nbytes);
  170. void *frame_data = *frame_data_ptr;
  171. planes[0] = frame_data; // 0 plane
  172. planes[1] = reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(frame_data) + width * height); // 1 plane
  173. planes[2] = reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(frame_data) + 2 * width * height); // 2 plane
  174. auto data = cnstream::CNFrameInfo::Create(std::to_string(g_channel_id));
  175. std::shared_ptr<CNDataFrame> frame(new (std::nothrow) CNDataFrame());
  176. switch (pix_fmt) {
  177. case CNPixelFormat::BGR24:
  178. frame->fmt = CNDataFormat::CN_PIXEL_FORMAT_BGR24;
  179. break;
  180. case CNPixelFormat::NV12:
  181. frame->fmt = CNDataFormat::CN_PIXEL_FORMAT_YUV420_NV12;
  182. break;
  183. case CNPixelFormat::NV21:
  184. frame->fmt = CNDataFormat::CN_PIXEL_FORMAT_YUV420_NV21;
  185. break;
  186. default:
  187. frame->fmt = CNDataFormat::CN_PIXEL_FORMAT_YUV420_NV21;
  188. break;
  189. }
  190. data->SetStreamIndex(g_channel_id);
  191. frame->frame_id = g_frame_id++;
  192. data->timestamp = frame->frame_id * 90000 / frame_rate;
  193. frame->width = width;
  194. frame->height = height;
  195. void *ptr_mlu[3] = {planes[0], planes[1], planes[2]};
  196. frame->stride[0] = frame->stride[1] = frame->stride[2] = width;
  197. frame->ctx.dev_id = g_dev_id;
  198. frame->ctx.ddr_channel = g_channel_id;
  199. frame->ctx.dev_type = DevContext::DevType::MLU;
  200. frame->dst_device_id = g_dev_id;
  201. frame->CopyToSyncMem(ptr_mlu, true);
  202. data->collection.Add(kCNDataFrameTag, frame);
  203. return data;
  204. }
  205. void Process(std::shared_ptr<Module> ptr, CNPixelFormat pix_fmt, int width, int height, int port, int frame_rate,
  206. int line) {
  207. if (g_channel_id > 3) g_channel_id = 0;
  208. g_frame_id = 0;
  209. void *frame_data = nullptr;
  210. edk::MluMemoryOp mem_op;
  211. auto data = GenTestData(pix_fmt, width, height, frame_rate, &frame_data);
  212. int ret = ptr->Process(data);
  213. mem_op.FreeMlu(frame_data);
  214. auto fut = std::async(std::launch::async, PullRtspStreamFFmpeg, port);
  215. EXPECT_EQ(ret, 0) << line;
  216. for (int i = 0; i < 30; ++i) {
  217. data = GenTestData(pix_fmt, width, height, frame_rate, &frame_data);
  218. ret = ptr->Process(data);
  219. std::this_thread::sleep_for(std::chrono::milliseconds(1000 / frame_rate));
  220. mem_op.FreeMlu(frame_data);
  221. EXPECT_EQ(ret, 0) << line;
  222. }
  223. // create eos frame for clearing stream idx
  224. cnstream::CNFrameInfo::Create(std::to_string(g_channel_id), true);
  225. g_channel_id++;
  226. fut.get();
  227. }
  228. void TestAllCase(ModuleParamSet params, int frame_rate, bool tiler, int line) {
  229. std::shared_ptr<Module> ptr = std::make_shared<RtspSink>(gname);
  230. EXPECT_TRUE(ptr->Open(params));
  231. int port = stoi(params["port"]);
  232. std::vector<CNPixelFormat> pixel_formats = {CNPixelFormat::NV21, CNPixelFormat::NV12};
  233. if (params.find("input_frame") == params.end() || params["input_frame"] == "cpu") {
  234. pixel_formats.push_back(CNPixelFormat::BGR24);
  235. }
  236. for (auto &format : pixel_formats) {
  237. Process(ptr, format, g_width, g_height, port, frame_rate, line);
  238. port = tiler ? -1 : port + 1;
  239. }
  240. ASSERT_NO_THROW(ptr->Close());
  241. }
  242. TEST(RTSP, RTSP) {
  243. ModuleParamSet params;
  244. int frame_rate = 25;
  245. params.clear();
  246. params["port"] = "9554";
  247. params["input_frame"] = "cpu";
  248. params["encoder_type"] = "mlu";
  249. params["device_id"] = "0";
  250. params["resample"] = "false";
  251. params["frame_rate"] = std::to_string(frame_rate);
  252. TestAllCase(params, frame_rate, false, __LINE__);
  253. params.clear();
  254. params["port"] = "9554";
  255. params["input_frame"] = "cpu";
  256. params["encoder_type"] = "mlu";
  257. params["device_id"] = "0";
  258. params["resample"] = "true";
  259. params["frame_rate"] = std::to_string(frame_rate);
  260. TestAllCase(params, frame_rate, false, __LINE__);
  261. params.clear();
  262. params["port"] = "9554";
  263. params["input_frame"] = "cpu";
  264. params["encoder_type"] = "mlu";
  265. params["device_id"] = "0";
  266. params["view_rows"] = "2";
  267. params["view_cols"] = "3";
  268. params["frame_rate"] = std::to_string(frame_rate);
  269. TestAllCase(params, frame_rate, true, __LINE__);
  270. params.clear();
  271. params["port"] = "9554";
  272. params["dst_width"] = "1920";
  273. params["dst_height"] = "1080";
  274. params["input_frame"] = "cpu";
  275. params["encoder_type"] = "cpu";
  276. params["device_id"] = "-1";
  277. params["resample"] = "false";
  278. params["frame_rate"] = std::to_string(frame_rate);
  279. TestAllCase(params, frame_rate, false, __LINE__);
  280. params.clear();
  281. params["port"] = "9554";
  282. params["input_frame"] = "cpu";
  283. params["encoder_type"] = "cpu";
  284. params["device_id"] = "-1";
  285. params["resample"] = "true";
  286. params["frame_rate"] = std::to_string(frame_rate);
  287. TestAllCase(params, frame_rate, false, __LINE__);
  288. params.clear();
  289. params["port"] = "9554";
  290. params["dst_width"] = "720";
  291. params["dst_height"] = "480";
  292. params["view_rows"] = "2";
  293. params["view_cols"] = "3";
  294. params["input_frame"] = "cpu";
  295. params["encoder_type"] = "cpu";
  296. params["device_id"] = "-1";
  297. params["frame_rate"] = std::to_string(frame_rate);
  298. TestAllCase(params, frame_rate, true, __LINE__);
  299. params.clear();
  300. params["port"] = "9554";
  301. params["dst_width"] = "0";
  302. params["dst_height"] = "0";
  303. params["input_frame"] = "mlu";
  304. params["encoder_type"] = "cpu";
  305. params["device_id"] = "0";
  306. params["frame_rate"] = std::to_string(frame_rate);
  307. TestAllCase(params, frame_rate, false, __LINE__);
  308. }
  309. } // namespace cnstream