rtsp.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * Copyright (c) 2011 - 2019, 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. res = curl_easy_setopt((A), (B), (C)); \
  60. if(res != CURLE_OK) \
  61. fprintf(stderr, "curl_easy_setopt(%s, %s, %s) failed: %d\n", \
  62. #A, #B, #C, res);
  63. #define my_curl_easy_perform(A) \
  64. res = curl_easy_perform(A); \
  65. if(res != CURLE_OK) \
  66. fprintf(stderr, "curl_easy_perform(%s) failed: %d\n", #A, res);
  67. /* send RTSP OPTIONS request */
  68. static void rtsp_options(CURL *curl, const char *uri)
  69. {
  70. CURLcode res = CURLE_OK;
  71. printf("\nRTSP: OPTIONS %s\n", uri);
  72. my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
  73. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
  74. my_curl_easy_perform(curl);
  75. }
  76. /* send RTSP DESCRIBE request and write sdp response to a file */
  77. static void rtsp_describe(CURL *curl, const char *uri,
  78. const char *sdp_filename)
  79. {
  80. CURLcode res = CURLE_OK;
  81. FILE *sdp_fp = fopen(sdp_filename, "wb");
  82. printf("\nRTSP: DESCRIBE %s\n", uri);
  83. if(sdp_fp == NULL) {
  84. fprintf(stderr, "Could not open '%s' for writing\n", sdp_filename);
  85. sdp_fp = stdout;
  86. }
  87. else {
  88. printf("Writing SDP to '%s'\n", sdp_filename);
  89. }
  90. my_curl_easy_setopt(curl, CURLOPT_WRITEDATA, sdp_fp);
  91. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
  92. my_curl_easy_perform(curl);
  93. my_curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
  94. if(sdp_fp != stdout) {
  95. fclose(sdp_fp);
  96. }
  97. }
  98. /* send RTSP SETUP request */
  99. static void rtsp_setup(CURL *curl, const char *uri, const char *transport)
  100. {
  101. CURLcode res = CURLE_OK;
  102. printf("\nRTSP: SETUP %s\n", uri);
  103. printf(" TRANSPORT %s\n", transport);
  104. my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
  105. my_curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, transport);
  106. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
  107. my_curl_easy_perform(curl);
  108. }
  109. /* send RTSP PLAY request */
  110. static void rtsp_play(CURL *curl, const char *uri, const char *range)
  111. {
  112. CURLcode res = CURLE_OK;
  113. printf("\nRTSP: PLAY %s\n", uri);
  114. my_curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
  115. my_curl_easy_setopt(curl, CURLOPT_RANGE, range);
  116. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
  117. my_curl_easy_perform(curl);
  118. /* switch off using range again */
  119. my_curl_easy_setopt(curl, CURLOPT_RANGE, NULL);
  120. }
  121. /* send RTSP TEARDOWN request */
  122. static void rtsp_teardown(CURL *curl, const char *uri)
  123. {
  124. CURLcode res = CURLE_OK;
  125. printf("\nRTSP: TEARDOWN %s\n", uri);
  126. my_curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
  127. my_curl_easy_perform(curl);
  128. }
  129. /* convert url into an sdp filename */
  130. static void get_sdp_filename(const char *url, char *sdp_filename,
  131. size_t namelen)
  132. {
  133. const char *s = strrchr(url, '/');
  134. strcpy(sdp_filename, "video.sdp");
  135. if(s != NULL) {
  136. s++;
  137. if(s[0] != '\0') {
  138. snprintf(sdp_filename, namelen, "%s.sdp", s);
  139. }
  140. }
  141. }
  142. /* scan sdp file for media control attribute */
  143. static void get_media_control_attribute(const char *sdp_filename,
  144. char *control)
  145. {
  146. int max_len = 256;
  147. char *s = malloc(max_len);
  148. FILE *sdp_fp = fopen(sdp_filename, "rb");
  149. control[0] = '\0';
  150. if(sdp_fp != NULL) {
  151. while(fgets(s, max_len - 2, sdp_fp) != NULL) {
  152. sscanf(s, " a = control: %s", control);
  153. }
  154. fclose(sdp_fp);
  155. }
  156. free(s);
  157. }
  158. /* main app */
  159. int main(int argc, char * const argv[])
  160. {
  161. #if 1
  162. const char *transport = "RTP/AVP;unicast;client_port=1234-1235"; /* UDP */
  163. #else
  164. /* TCP */
  165. const char *transport = "RTP/AVP/TCP;unicast;client_port=1234-1235";
  166. #endif
  167. const char *range = "0.000-";
  168. int rc = EXIT_SUCCESS;
  169. char *base_name = NULL;
  170. printf("\nRTSP request %s\n", VERSION_STR);
  171. printf(" Project web site: "
  172. "https://github.com/BackupGGCode/rtsprequest\n");
  173. printf(" Requires curl V7.20 or greater\n\n");
  174. /* check command line */
  175. if((argc != 2) && (argc != 3)) {
  176. base_name = strrchr(argv[0], '/');
  177. if(base_name == NULL) {
  178. base_name = strrchr(argv[0], '\\');
  179. }
  180. if(base_name == NULL) {
  181. base_name = argv[0];
  182. }
  183. else {
  184. base_name++;
  185. }
  186. printf("Usage: %s url [transport]\n", base_name);
  187. printf(" url of video server\n");
  188. printf(" transport (optional) specifier for media stream"
  189. " protocol\n");
  190. printf(" default transport: %s\n", transport);
  191. printf("Example: %s rtsp://192.168.0.2/media/video1\n\n", base_name);
  192. rc = EXIT_FAILURE;
  193. }
  194. else {
  195. const char *url = argv[1];
  196. char *uri = malloc(strlen(url) + 32);
  197. char *sdp_filename = malloc(strlen(url) + 32);
  198. char *control = malloc(strlen(url) + 32);
  199. CURLcode res;
  200. get_sdp_filename(url, sdp_filename, strlen(url) + 32);
  201. if(argc == 3) {
  202. transport = argv[2];
  203. }
  204. /* initialize curl */
  205. res = curl_global_init(CURL_GLOBAL_ALL);
  206. if(res == CURLE_OK) {
  207. curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
  208. CURL *curl;
  209. fprintf(stderr, " curl V%s loaded\n", data->version);
  210. /* initialize this curl session */
  211. curl = curl_easy_init();
  212. if(curl != NULL) {
  213. my_curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
  214. my_curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
  215. my_curl_easy_setopt(curl, CURLOPT_HEADERDATA, stdout);
  216. my_curl_easy_setopt(curl, CURLOPT_URL, url);
  217. /* request server options */
  218. snprintf(uri, strlen(url) + 32, "%s", url);
  219. rtsp_options(curl, uri);
  220. /* request session description and write response to sdp file */
  221. rtsp_describe(curl, uri, sdp_filename);
  222. /* get media control attribute from sdp file */
  223. get_media_control_attribute(sdp_filename, control);
  224. /* setup media stream */
  225. snprintf(uri, strlen(url) + 32, "%s/%s", url, control);
  226. rtsp_setup(curl, uri, transport);
  227. /* start playing media stream */
  228. snprintf(uri, strlen(url) + 32, "%s/", url);
  229. rtsp_play(curl, uri, range);
  230. printf("Playing video, press any key to stop ...");
  231. _getch();
  232. printf("\n");
  233. /* teardown session */
  234. rtsp_teardown(curl, uri);
  235. /* cleanup */
  236. curl_easy_cleanup(curl);
  237. curl = NULL;
  238. }
  239. else {
  240. fprintf(stderr, "curl_easy_init() failed\n");
  241. }
  242. curl_global_cleanup();
  243. }
  244. else {
  245. fprintf(stderr, "curl_global_init(%s) failed: %d\n",
  246. "CURL_GLOBAL_ALL", res);
  247. }
  248. free(control);
  249. free(sdp_filename);
  250. free(uri);
  251. }
  252. return rc;
  253. }