rtsp.c 8.3 KB

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