test_rtsp_server.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /*************************************************************************
  2. * Copyright (C) [2020] 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. #ifdef __GNUC__
  21. #pragma GCC diagnostic push
  22. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  23. #endif
  24. #ifdef __cplusplus
  25. extern "C" {
  26. #endif
  27. #include <libavcodec/avcodec.h>
  28. #include <libavformat/avformat.h>
  29. #include <libavutil/avutil.h>
  30. #ifdef __cplusplus
  31. }
  32. #endif
  33. #include <mutex>
  34. #include <string>
  35. #include <thread>
  36. #include "cnstream_logging.hpp"
  37. #include "gtest/gtest.h"
  38. #include "rtsp_server.hpp"
  39. #include "test_base.hpp"
  40. #include "video/circular_buffer.hpp"
  41. #include "video/frame_rate_controller.hpp"
  42. namespace cnstream {
  43. static constexpr const char *test_file = "../../modules/unitest/source/data/img.mp4";
  44. bool TestRtspServer(const std::string &file) {
  45. bool ret = true;
  46. AVFormatContext *fmt_ctx = nullptr;
  47. AVBitStreamFilterContext *bf_ctx = nullptr;
  48. AVPacket packet;
  49. int video_index = -1;
  50. bool first_frame = true;
  51. bool find_pts = true; // set it to true by default!
  52. AVStream *vstream = nullptr;
  53. AVCodecID codec_id = AV_CODEC_ID_H264;
  54. int width = 0, height = 0;
  55. double frame_rate;
  56. FrameRateController *frc = nullptr;
  57. std::mutex mtx;
  58. video::CircularBuffer *buffer = new video::CircularBuffer();
  59. RtspServer::Param param;
  60. RtspServer *rtsp_server = nullptr;
  61. auto get_packet = [&](uint8_t *data, int size, double *timestamp, int *buffer_percent) -> int {
  62. int ret = -1;
  63. AVPacket packet;
  64. std::unique_lock<std::mutex> lk(mtx);
  65. if (buffer->Size() <= sizeof(packet)) return 0;
  66. buffer->Read(reinterpret_cast<uint8_t *>(&packet), sizeof(packet), true);
  67. if (size < 0) { // skip packet
  68. buffer->Read(nullptr, sizeof(packet) + packet.size);
  69. return packet.size;
  70. } else if (!data) { // get packet size, timestamp
  71. ret = packet.size;
  72. } else { // read out packet data
  73. if (size < packet.size) return -1;
  74. buffer->Read(reinterpret_cast<uint8_t *>(&packet), sizeof(packet));
  75. ret = buffer->Read(data, packet.size);
  76. if (ret > 0) {
  77. if (timestamp) *timestamp = packet.pts / 1000.0;
  78. if (buffer_percent) *buffer_percent = buffer->Size() * 100 / buffer->Capacity();
  79. }
  80. }
  81. return ret;
  82. };
  83. av_register_all();
  84. avformat_network_init();
  85. // format context
  86. fmt_ctx = avformat_alloc_context();
  87. // open input
  88. int ret_code = avformat_open_input(&fmt_ctx, file.c_str(), nullptr, nullptr);
  89. if (0 != ret_code) {
  90. LOGE(RTSP_SERVER_UNITTEST) << "Couldn't open input stream.";
  91. ret = false;
  92. goto end;
  93. }
  94. // find video stream information
  95. ret_code = avformat_find_stream_info(fmt_ctx, nullptr);
  96. if (ret_code < 0) {
  97. LOGE(RTSP_SERVER_UNITTEST) << "Couldn't find stream information.";
  98. ret = false;
  99. goto end;
  100. }
  101. video_index = -1;
  102. for (uint32_t loop_i = 0; loop_i < fmt_ctx->nb_streams; loop_i++) {
  103. vstream = fmt_ctx->streams[loop_i];
  104. AVMediaType media_type;
  105. media_type = vstream->codec->codec_type;
  106. if (media_type == AVMEDIA_TYPE_VIDEO) {
  107. video_index = loop_i;
  108. break;
  109. }
  110. }
  111. if (video_index == -1) {
  112. LOGE(RTSP_SERVER_UNITTEST) << "Didn't find a video stream.";
  113. ret = false;
  114. goto end;
  115. }
  116. codec_id = vstream->codec->codec_id;
  117. width = vstream->codec->width;
  118. height = vstream->codec->height;
  119. frame_rate = av_q2d(vstream->r_frame_rate);
  120. // bitstream filter
  121. bf_ctx = nullptr;
  122. if (strstr(fmt_ctx->iformat->name, "mp4") || strstr(fmt_ctx->iformat->name, "flv") ||
  123. strstr(fmt_ctx->iformat->name, "matroska")) {
  124. if (AV_CODEC_ID_H264 == codec_id) {
  125. bf_ctx = av_bitstream_filter_init("h264_mp4toannexb");
  126. } else if (AV_CODEC_ID_HEVC == codec_id) {
  127. bf_ctx = av_bitstream_filter_init("hevc_mp4toannexb");
  128. } else {
  129. }
  130. }
  131. param.port = 8554;
  132. param.authentication = false;
  133. param.width = width;
  134. param.height = height;
  135. param.bit_rate = vstream->codec->bit_rate;
  136. switch (codec_id) {
  137. case AV_CODEC_ID_H264:
  138. param.codec_type = RtspServer::CodecType::H264;
  139. break;
  140. case AV_CODEC_ID_HEVC:
  141. param.codec_type = RtspServer::CodecType::H265;
  142. break;
  143. case AV_CODEC_ID_MPEG4:
  144. param.codec_type = RtspServer::CodecType::MPEG4;
  145. break;
  146. default:
  147. LOGE(RTSP_SERVER_UNITTEST) << "Unsupported codec id: " << codec_id;
  148. goto end;
  149. }
  150. param.get_packet = get_packet;
  151. rtsp_server = new RtspServer(param);
  152. if (!rtsp_server) {
  153. ret = false;
  154. goto end;
  155. }
  156. if (!rtsp_server->Start()) {
  157. ret = false;
  158. goto end;
  159. }
  160. av_init_packet(&packet);
  161. packet.data = nullptr;
  162. packet.size = 0;
  163. LOGI(RTSP_SERVER_UNITTEST) << "Stream frame rate is " << frame_rate;
  164. frc = new FrameRateController(frame_rate);
  165. frc->Start();
  166. while (true) {
  167. if (av_read_frame(fmt_ctx, &packet) < 0) {
  168. LOGI(RTSP_SERVER_UNITTEST) << "Reach file end.";
  169. rtsp_server->OnEvent(RtspServer::Event::EVENT_EOS);
  170. break;
  171. }
  172. if (packet.stream_index != video_index) {
  173. av_packet_unref(&packet);
  174. continue;
  175. }
  176. AVStream *vstream = fmt_ctx->streams[video_index];
  177. if (first_frame) {
  178. if (packet.flags & AV_PKT_FLAG_KEY) {
  179. first_frame = false;
  180. } else {
  181. av_packet_unref(&packet);
  182. continue;
  183. }
  184. }
  185. if (bf_ctx) {
  186. av_bitstream_filter_filter(bf_ctx, vstream->codec, nullptr, &packet.data, &packet.size, packet.data, packet.size,
  187. 0);
  188. }
  189. // find pts information
  190. if (AV_NOPTS_VALUE == packet.pts && find_pts) {
  191. find_pts = false;
  192. LOGW(RTSP_SERVER_UNITTEST) << "Didn't find pts informations, use ordered numbers instead. "
  193. << "stream url: " << file.c_str();
  194. } else if (AV_NOPTS_VALUE != packet.pts) {
  195. find_pts = true;
  196. packet.pts = av_rescale_q(packet.pts, vstream->time_base, {1, 1000});
  197. packet.dts = av_rescale_q(packet.dts, vstream->time_base, {1, 1000});
  198. }
  199. while (true) {
  200. std::unique_lock<std::mutex> lk(mtx);
  201. size_t free_size = buffer->Capacity() - buffer->Size();
  202. if (free_size <= packet.size + sizeof(AVPacket)) {
  203. lk.unlock();
  204. std::this_thread::sleep_for(std::chrono::milliseconds(50));
  205. continue;
  206. }
  207. buffer->Write(reinterpret_cast<uint8_t *>(&packet), sizeof(packet));
  208. buffer->Write(packet.data, packet.size);
  209. // LOGI(RTSP_SERVER_UNITTEST) << "Send packet, size=" << packet.size << ", pts=" << packet.pts << ", dts=" <<
  210. // packet.dts;
  211. break;
  212. }
  213. rtsp_server->OnEvent(RtspServer::Event::EVENT_DATA);
  214. if (bf_ctx) {
  215. av_freep(&packet.data);
  216. }
  217. av_packet_unref(&packet);
  218. frc->Control();
  219. }
  220. end:
  221. if (frc) {
  222. delete frc;
  223. frc = nullptr;
  224. }
  225. if (rtsp_server) {
  226. rtsp_server->Stop();
  227. delete rtsp_server;
  228. rtsp_server = nullptr;
  229. }
  230. av_packet_unref(&packet);
  231. if (fmt_ctx) {
  232. avformat_close_input(&fmt_ctx);
  233. avformat_free_context(fmt_ctx);
  234. fmt_ctx = nullptr;
  235. }
  236. if (bf_ctx) {
  237. av_bitstream_filter_close(bf_ctx);
  238. bf_ctx = nullptr;
  239. }
  240. if (buffer) delete buffer;
  241. return ret;
  242. }
  243. TEST(RtspServer, Streamming) { EXPECT_TRUE(TestRtspServer(GetExePath() + test_file)); }
  244. } // namespace cnstream