+ /* Construct the URI used in request; this is similar to s->filename,
+ * but with authentication credentials removed and RTSP specific options
+ * stripped out. */
+ ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL,
+ host, port, "%s", path);
+
+ if (rt->control_transport == RTSP_MODE_TUNNEL) {
+ /* set up initial handshake for tunneling */
+ char httpname[1024];
+ char sessioncookie[17];
+ char headers[1024];
+
+ ff_url_join(httpname, sizeof(httpname), "http", auth, host, port, "%s", path);
+ snprintf(sessioncookie, sizeof(sessioncookie), "%08x%08x",
+ av_get_random_seed(), av_get_random_seed());
+
+ /* GET requests */
+ if (url_alloc(&rt->rtsp_hd, httpname, URL_RDONLY) < 0) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ /* generate GET headers */
+ snprintf(headers, sizeof(headers),
+ "x-sessioncookie: %s\r\n"
+ "Accept: application/x-rtsp-tunnelled\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache\r\n",
+ sessioncookie);
+ ff_http_set_headers(rt->rtsp_hd, headers);
+
+ /* complete the connection */
+ if (url_connect(rt->rtsp_hd)) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ /* POST requests */
+ if (url_alloc(&rt->rtsp_hd_out, httpname, URL_WRONLY) < 0 ) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ /* generate POST headers */
+ snprintf(headers, sizeof(headers),
+ "x-sessioncookie: %s\r\n"
+ "Content-Type: application/x-rtsp-tunnelled\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Content-Length: 32767\r\n"
+ "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n",
+ sessioncookie);
+ ff_http_set_headers(rt->rtsp_hd_out, headers);
+ ff_http_set_chunked_transfer_encoding(rt->rtsp_hd_out, 0);
+
+ /* Initialize the authentication state for the POST session. The HTTP
+ * protocol implementation doesn't properly handle multi-pass
+ * authentication for POST requests, since it would require one of
+ * the following:
+ * - implementing Expect: 100-continue, which many HTTP servers
+ * don't support anyway, even less the RTSP servers that do HTTP
+ * tunneling
+ * - sending the whole POST data until getting a 401 reply specifying
+ * what authentication method to use, then resending all that data
+ * - waiting for potential 401 replies directly after sending the
+ * POST header (waiting for some unspecified time)
+ * Therefore, we copy the full auth state, which works for both basic
+ * and digest. (For digest, we would have to synchronize the nonce
+ * count variable between the two sessions, if we'd do more requests
+ * with the original session, though.)
+ */
+ ff_http_init_auth_state(rt->rtsp_hd_out, rt->rtsp_hd);
+
+ /* complete the connection */
+ if (url_connect(rt->rtsp_hd_out)) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ } else {
+ /* open the tcp connection */
+ ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
+ if (url_open(&rt->rtsp_hd, tcpname, URL_RDWR) < 0) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ rt->rtsp_hd_out = rt->rtsp_hd;