rtsp.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * Copyright (c) 2011 - 2021, Jim Hollinger
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * * Neither the name of Jim Hollinger nor the names of its contributors
  14. * may be used to endorse or promote products derived from this
  15. * software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  20. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  21. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  22. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  23. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. */
  30. /* <DESC>
  31. * A basic RTSP transfer
  32. * </DESC>
  33. */
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #if defined (WIN32)
  38. # include <conio.h> /* _getch() */
  39. #else
  40. # include <termios.h>
  41. # include <unistd.h>
  42. static int _getch(void)
  43. {
  44. struct termios oldt, newt;
  45. int ch;
  46. tcgetattr(STDIN_FILENO, &oldt);
  47. newt = oldt;
  48. newt.c_lflag &= ~( ICANON | ECHO);
  49. tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  50. ch = getchar();
  51. tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  52. return ch;
  53. }
  54. #endif
  55. #include <curl/curl.h>
  56. #define VERSION_STR "V1.0"
  57. /* error handling macros */
  58. #define my_curl_easy_setopt(A, B, C) \
  59. do { \
  60. res = curl_easy_setopt((A), (B), (C)); \
  61. if(res != CURLE_OK) \
  62. fprintf(stderr, "curl_easy_setopt(%s, %s, %s) failed: %d\n", \
  63. #A, #B, #C, res); \
  64. } while(0)
  65. #define my_curl_easy_perform(A) \
  66. do { \
  67. res = curl_easy_perform(A); \
  68. if(res != CURLE_OK) \
  69. fprintf(stderr, "curl_easy_perform(%s) failed: %d\n", #A, res); \
  70. } while(0)
  71. /* send RTSP OPTIONS request */
  72. static void rtsp_options(CURL *curl, const char *uri)
  73. {
  74. CURLcode res = CURLE_OK;
  75. printf("\nRTSP: OPTIONS %s\n", uri);
  76. my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
  77. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
  78. my_curl_easy_perform(curl);
  79. }
  80. /* send RTSP DESCRIBE request and write sdp response to a file */
  81. static void rtsp_describe(CURL *curl, const char *uri,
  82. const char *sdp_filename)
  83. {
  84. CURLcode res = CURLE_OK;
  85. FILE *sdp_fp = fopen(sdp_filename, "wb");
  86. printf("\nRTSP: DESCRIBE %s\n", uri);
  87. if(!sdp_fp) {
  88. fprintf(stderr, "Could not open '%s' for writing\n", sdp_filename);
  89. sdp_fp = stdout;
  90. }
  91. else {
  92. printf("Writing SDP to '%s'\n", sdp_filename);
  93. }
  94. my_curl_easy_setopt(curl, CURLOPT_WRITEDATA, sdp_fp);
  95. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
  96. my_curl_easy_perform(curl);
  97. my_curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
  98. if(sdp_fp != stdout) {
  99. fclose(sdp_fp);
  100. }
  101. }
  102. /* send RTSP SETUP request */
  103. static void rtsp_setup(CURL *curl, const char *uri, const char *transport)
  104. {
  105. CURLcode res = CURLE_OK;
  106. printf("\nRTSP: SETUP %s\n", uri);
  107. printf(" TRANSPORT %s\n", transport);
  108. my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
  109. my_curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, transport);
  110. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
  111. my_curl_easy_perform(curl);
  112. }
  113. /* send RTSP PLAY request */
  114. static void rtsp_play(CURL *curl, const char *uri, const char *range)
  115. {
  116. CURLcode res = CURLE_OK;
  117. printf("\nRTSP: PLAY %s\n", uri);
  118. my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
  119. my_curl_easy_setopt(curl, CURLOPT_RANGE, range);
  120. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
  121. my_curl_easy_perform(curl);
  122. /* switch off using range again */
  123. my_curl_easy_setopt(curl, CURLOPT_RANGE, NULL);
  124. }
  125. /* send RTSP TEARDOWN request */
  126. static void rtsp_teardown(CURL *curl, const char *uri)
  127. {
  128. CURLcode res = CURLE_OK;
  129. printf("\nRTSP: TEARDOWN %s\n", uri);
  130. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
  131. my_curl_easy_perform(curl);
  132. }
  133. /* convert url into an sdp filename */
  134. static void get_sdp_filename(const char *url, char *sdp_filename,
  135. size_t namelen)
  136. {
  137. const char *s = strrchr(url, '/');
  138. strcpy(sdp_filename, "video.sdp");
  139. if(s != NULL) {
  140. s++;
  141. if(s[0] != '\0') {
  142. snprintf(sdp_filename, namelen, "%s.sdp", s);
  143. }
  144. }
  145. }
  146. /* scan sdp file for media control attribute */
  147. static void get_media_control_attribute(const char *sdp_filename,
  148. char *control)
  149. {
  150. int max_len = 256;
  151. char *s = malloc(max_len);
  152. FILE *sdp_fp = fopen(sdp_filename, "rb");
  153. control[0] = '\0';
  154. if(sdp_fp != NULL) {
  155. while(fgets(s, max_len - 2, sdp_fp) != NULL) {
  156. sscanf(s, " a = control: %32s", control);
  157. }
  158. fclose(sdp_fp);
  159. }
  160. free(s);
  161. }
  162. /* main app */
  163. int main(int argc, char * const argv[])
  164. {
  165. #if 1
  166. const char *transport = "RTP/AVP;unicast;client_port=1234-1235"; /* UDP */
  167. #else
  168. /* TCP */
  169. const char *transport = "RTP/AVP/TCP;unicast;client_port=1234-1235";
  170. #endif
  171. const char *range = "0.000-";
  172. int rc = EXIT_SUCCESS;
  173. char *base_name = NULL;
  174. printf("\nRTSP request %s\n", VERSION_STR);
  175. printf(" Project website: "
  176. "https://github.com/BackupGGCode/rtsprequest\n");
  177. printf(" Requires curl V7.20 or greater\n\n");
  178. /* check command line */
  179. if((argc != 2) && (argc != 3)) {
  180. base_name = strrchr(argv[0], '/');
  181. if(!base_name) {
  182. base_name = strrchr(argv[0], '\\');
  183. }
  184. if(!base_name) {
  185. base_name = argv[0];
  186. }
  187. else {
  188. base_name++;
  189. }
  190. printf("Usage: %s url [transport]\n", base_name);
  191. printf(" url of video server\n");
  192. printf(" transport (optional) specifier for media stream"
  193. " protocol\n");
  194. printf(" default transport: %s\n", transport);
  195. printf("Example: %s rtsp://192.168.0.2/media/video1\n\n", base_name);
  196. rc = EXIT_FAILURE;
  197. }
  198. else {
  199. const char *url = argv[1];
  200. char *uri = malloc(strlen(url) + 32);
  201. char *sdp_filename = malloc(strlen(url) + 32);
  202. char *control = malloc(strlen(url) + 32);
  203. CURLcode res;
  204. get_sdp_filename(url, sdp_filename, strlen(url) + 32);
  205. if(argc == 3) {
  206. transport = argv[2];
  207. }
  208. /* initialize curl */
  209. res = curl_global_init(CURL_GLOBAL_ALL);
  210. if(res == CURLE_OK) {
  211. curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
  212. CURL *curl;
  213. fprintf(stderr, " curl V%s loaded\n", data->version);
  214. /* initialize this curl session */
  215. curl = curl_easy_init();
  216. if(curl != NULL) {
  217. my_curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
  218. my_curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
  219. my_curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout);
  220. my_curl_easy_setopt(curl, CURLOPT_URL, url);
  221. /* request server options */
  222. snprintf(uri, strlen(url) + 32, "%s", url);
  223. rtsp_options(curl, uri);
  224. /* request session description and write response to sdp file */
  225. rtsp_describe(curl, uri, sdp_filename);
  226. /* get media control attribute from sdp file */
  227. get_media_control_attribute(sdp_filename, control);
  228. /* setup media stream */
  229. snprintf(uri, strlen(url) + 32, "%s/%s", url, control);
  230. rtsp_setup(curl, uri, transport);
  231. /* start playing media stream */
  232. snprintf(uri, strlen(url) + 32, "%s/", url);
  233. rtsp_play(curl, uri, range);
  234. printf("Playing video, press any key to stop ...");
  235. _getch();
  236. printf("\n");
  237. /* teardown session */
  238. rtsp_teardown(curl, uri);
  239. /* cleanup */
  240. curl_easy_cleanup(curl);
  241. curl = NULL;
  242. }
  243. else {
  244. fprintf(stderr, "curl_easy_init() failed\n");
  245. }
  246. curl_global_cleanup();
  247. }
  248. else {
  249. fprintf(stderr, "curl_global_init(%s) failed: %d\n",
  250. "CURL_GLOBAL_ALL", res);
  251. }
  252. free(control);
  253. free(sdp_filename);
  254. free(uri);
  255. }
  256. return rc;
  257. }