1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | ***************************************************************************/ |
22 | #include "test.h" |
23 | |
24 | #ifdef HAVE_NETINET_IN_H |
25 | # include <netinet/in.h> |
26 | #endif |
27 | #ifdef HAVE_NETDB_H |
28 | # include <netdb.h> |
29 | #endif |
30 | #ifdef HAVE_ARPA_INET_H |
31 | # include <arpa/inet.h> |
32 | #endif |
33 | #ifdef HAVE_SYS_STAT_H |
34 | # include <sys/stat.h> |
35 | #endif |
36 | #ifdef HAVE_FCNTL_H |
37 | # include <fcntl.h> |
38 | #endif |
39 | |
40 | #include "warnless.h" |
41 | #include "memdebug.h" |
42 | |
43 | #define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1]))) |
44 | |
45 | #define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ |
46 | ((int)((unsigned char)((p)[3])))) |
47 | |
48 | #define RTP_DATA_SIZE 12 |
49 | static const char *RTP_DATA = "$_1234\n\0asdf" ; |
50 | |
51 | static int rtp_packet_count = 0; |
52 | |
53 | static size_t rtp_write(void *ptr, size_t size, size_t nmemb, void *stream) |
54 | { |
55 | char *data = (char *)ptr; |
56 | int channel = RTP_PKT_CHANNEL(data); |
57 | int message_size; |
58 | int coded_size = RTP_PKT_LENGTH(data); |
59 | size_t failure = (size && nmemb) ? 0 : 1; |
60 | int i; |
61 | (void)stream; |
62 | |
63 | message_size = curlx_uztosi(size * nmemb) - 4; |
64 | |
65 | printf("RTP: message size %d, channel %d\n" , message_size, channel); |
66 | if(message_size != coded_size) { |
67 | printf("RTP embedded size (%d) does not match the write size (%d).\n" , |
68 | coded_size, message_size); |
69 | return failure; |
70 | } |
71 | |
72 | data += 4; |
73 | for(i = 0; i < message_size; i += RTP_DATA_SIZE) { |
74 | if(message_size - i > RTP_DATA_SIZE) { |
75 | if(memcmp(RTP_DATA, data + i, RTP_DATA_SIZE) != 0) { |
76 | printf("RTP PAYLOAD CORRUPTED [%s]\n" , data + i); |
77 | return failure; |
78 | } |
79 | } |
80 | else { |
81 | if(memcmp(RTP_DATA, data + i, message_size - i) != 0) { |
82 | printf("RTP PAYLOAD END CORRUPTED (%d), [%s]\n" , |
83 | message_size - i, data + i); |
84 | return failure; |
85 | } |
86 | } |
87 | } |
88 | |
89 | rtp_packet_count++; |
90 | fprintf(stderr, "packet count is %d\n" , rtp_packet_count); |
91 | |
92 | return size * nmemb; |
93 | } |
94 | |
95 | /* build request url */ |
96 | static char *suburl(const char *base, int i) |
97 | { |
98 | return curl_maprintf("%s%.4d" , base, i); |
99 | } |
100 | |
101 | int test(char *URL) |
102 | { |
103 | int res; |
104 | CURL *curl; |
105 | char *stream_uri = NULL; |
106 | int request = 1; |
107 | |
108 | FILE *protofile = fopen(libtest_arg2, "wb" ); |
109 | if(!protofile) { |
110 | fprintf(stderr, "Couldn't open the protocol dump file\n" ); |
111 | return TEST_ERR_MAJOR_BAD; |
112 | } |
113 | |
114 | if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { |
115 | fprintf(stderr, "curl_global_init() failed\n" ); |
116 | fclose(protofile); |
117 | return TEST_ERR_MAJOR_BAD; |
118 | } |
119 | |
120 | curl = curl_easy_init(); |
121 | if(!curl) { |
122 | fprintf(stderr, "curl_easy_init() failed\n" ); |
123 | fclose(protofile); |
124 | curl_global_cleanup(); |
125 | return TEST_ERR_MAJOR_BAD; |
126 | } |
127 | test_setopt(curl, CURLOPT_URL, URL); |
128 | |
129 | stream_uri = suburl(URL, request++); |
130 | if(!stream_uri) { |
131 | res = TEST_ERR_MAJOR_BAD; |
132 | goto test_cleanup; |
133 | } |
134 | test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); |
135 | free(stream_uri); |
136 | stream_uri = NULL; |
137 | |
138 | test_setopt(curl, CURLOPT_INTERLEAVEFUNCTION, rtp_write); |
139 | test_setopt(curl, CURLOPT_TIMEOUT, 3L); |
140 | test_setopt(curl, CURLOPT_VERBOSE, 1L); |
141 | test_setopt(curl, CURLOPT_WRITEDATA, protofile); |
142 | |
143 | test_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RTP/AVP/TCP;interleaved=0-1" ); |
144 | test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); |
145 | |
146 | res = curl_easy_perform(curl); |
147 | if(res) |
148 | goto test_cleanup; |
149 | |
150 | /* This PLAY starts the interleave */ |
151 | stream_uri = suburl(URL, request++); |
152 | if(!stream_uri) { |
153 | res = TEST_ERR_MAJOR_BAD; |
154 | goto test_cleanup; |
155 | } |
156 | test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); |
157 | free(stream_uri); |
158 | stream_uri = NULL; |
159 | test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); |
160 | |
161 | res = curl_easy_perform(curl); |
162 | if(res) |
163 | goto test_cleanup; |
164 | |
165 | /* The DESCRIBE request will try to consume data after the Content */ |
166 | stream_uri = suburl(URL, request++); |
167 | if(!stream_uri) { |
168 | res = TEST_ERR_MAJOR_BAD; |
169 | goto test_cleanup; |
170 | } |
171 | test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); |
172 | free(stream_uri); |
173 | stream_uri = NULL; |
174 | test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); |
175 | |
176 | res = curl_easy_perform(curl); |
177 | if(res) |
178 | goto test_cleanup; |
179 | |
180 | stream_uri = suburl(URL, request++); |
181 | if(!stream_uri) { |
182 | res = TEST_ERR_MAJOR_BAD; |
183 | goto test_cleanup; |
184 | } |
185 | test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); |
186 | free(stream_uri); |
187 | stream_uri = NULL; |
188 | test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); |
189 | |
190 | res = curl_easy_perform(curl); |
191 | if(res) |
192 | goto test_cleanup; |
193 | |
194 | fprintf(stderr, "PLAY COMPLETE\n" ); |
195 | |
196 | /* Use Receive to get the rest of the data */ |
197 | while(!res && rtp_packet_count < 13) { |
198 | fprintf(stderr, "LOOPY LOOP!\n" ); |
199 | test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE); |
200 | res = curl_easy_perform(curl); |
201 | } |
202 | |
203 | test_cleanup: |
204 | free(stream_uri); |
205 | |
206 | if(protofile) |
207 | fclose(protofile); |
208 | |
209 | curl_easy_cleanup(curl); |
210 | curl_global_cleanup(); |
211 | |
212 | return res; |
213 | } |
214 | |