Annotation of embedaddon/curl/lib/rtsp.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2019, 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.haxx.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:
! 23: #include "curl_setup.h"
! 24:
! 25: #ifndef CURL_DISABLE_RTSP
! 26:
! 27: #include "urldata.h"
! 28: #include <curl/curl.h>
! 29: #include "transfer.h"
! 30: #include "sendf.h"
! 31: #include "multiif.h"
! 32: #include "http.h"
! 33: #include "url.h"
! 34: #include "progress.h"
! 35: #include "rtsp.h"
! 36: #include "strcase.h"
! 37: #include "select.h"
! 38: #include "connect.h"
! 39: #include "strdup.h"
! 40: /* The last 3 #include files should be in this order */
! 41: #include "curl_printf.h"
! 42: #include "curl_memory.h"
! 43: #include "memdebug.h"
! 44:
! 45: #define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1])))
! 46:
! 47: #define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \
! 48: ((int)((unsigned char)((p)[3]))))
! 49:
! 50: /* protocol-specific functions set up to be called by the main engine */
! 51: static CURLcode rtsp_do(struct connectdata *conn, bool *done);
! 52: static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature);
! 53: static CURLcode rtsp_connect(struct connectdata *conn, bool *done);
! 54: static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead);
! 55: static int rtsp_getsock_do(struct connectdata *conn, curl_socket_t *socks);
! 56:
! 57: /*
! 58: * Parse and write out any available RTP data.
! 59: *
! 60: * nread: amount of data left after k->str. will be modified if RTP
! 61: * data is parsed and k->str is moved up
! 62: * readmore: whether or not the RTP parser needs more data right away
! 63: */
! 64: static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
! 65: struct connectdata *conn,
! 66: ssize_t *nread,
! 67: bool *readmore);
! 68:
! 69: static CURLcode rtsp_setup_connection(struct connectdata *conn);
! 70: static unsigned int rtsp_conncheck(struct connectdata *check,
! 71: unsigned int checks_to_perform);
! 72:
! 73: /* this returns the socket to wait for in the DO and DOING state for the multi
! 74: interface and then we're always _sending_ a request and thus we wait for
! 75: the single socket to become writable only */
! 76: static int rtsp_getsock_do(struct connectdata *conn,
! 77: curl_socket_t *socks)
! 78: {
! 79: /* write mode */
! 80: socks[0] = conn->sock[FIRSTSOCKET];
! 81: return GETSOCK_WRITESOCK(0);
! 82: }
! 83:
! 84: static
! 85: CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len);
! 86:
! 87:
! 88: /*
! 89: * RTSP handler interface.
! 90: */
! 91: const struct Curl_handler Curl_handler_rtsp = {
! 92: "RTSP", /* scheme */
! 93: rtsp_setup_connection, /* setup_connection */
! 94: rtsp_do, /* do_it */
! 95: rtsp_done, /* done */
! 96: ZERO_NULL, /* do_more */
! 97: rtsp_connect, /* connect_it */
! 98: ZERO_NULL, /* connecting */
! 99: ZERO_NULL, /* doing */
! 100: ZERO_NULL, /* proto_getsock */
! 101: rtsp_getsock_do, /* doing_getsock */
! 102: ZERO_NULL, /* domore_getsock */
! 103: ZERO_NULL, /* perform_getsock */
! 104: rtsp_disconnect, /* disconnect */
! 105: rtsp_rtp_readwrite, /* readwrite */
! 106: rtsp_conncheck, /* connection_check */
! 107: PORT_RTSP, /* defport */
! 108: CURLPROTO_RTSP, /* protocol */
! 109: PROTOPT_NONE /* flags */
! 110: };
! 111:
! 112:
! 113: static CURLcode rtsp_setup_connection(struct connectdata *conn)
! 114: {
! 115: struct RTSP *rtsp;
! 116:
! 117: conn->data->req.protop = rtsp = calloc(1, sizeof(struct RTSP));
! 118: if(!rtsp)
! 119: return CURLE_OUT_OF_MEMORY;
! 120:
! 121: return CURLE_OK;
! 122: }
! 123:
! 124:
! 125: /*
! 126: * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
! 127: * want to block the application forever while receiving a stream. Therefore,
! 128: * we cannot assume that an RTSP socket is dead just because it is readable.
! 129: *
! 130: * Instead, if it is readable, run Curl_connalive() to peek at the socket
! 131: * and distinguish between closed and data.
! 132: */
! 133: static bool rtsp_connisdead(struct connectdata *check)
! 134: {
! 135: int sval;
! 136: bool ret_val = TRUE;
! 137:
! 138: sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
! 139: if(sval == 0) {
! 140: /* timeout */
! 141: ret_val = FALSE;
! 142: }
! 143: else if(sval & CURL_CSELECT_ERR) {
! 144: /* socket is in an error state */
! 145: ret_val = TRUE;
! 146: }
! 147: else if(sval & CURL_CSELECT_IN) {
! 148: /* readable with no error. could still be closed */
! 149: ret_val = !Curl_connalive(check);
! 150: }
! 151:
! 152: return ret_val;
! 153: }
! 154:
! 155: /*
! 156: * Function to check on various aspects of a connection.
! 157: */
! 158: static unsigned int rtsp_conncheck(struct connectdata *check,
! 159: unsigned int checks_to_perform)
! 160: {
! 161: unsigned int ret_val = CONNRESULT_NONE;
! 162:
! 163: if(checks_to_perform & CONNCHECK_ISDEAD) {
! 164: if(rtsp_connisdead(check))
! 165: ret_val |= CONNRESULT_DEAD;
! 166: }
! 167:
! 168: return ret_val;
! 169: }
! 170:
! 171:
! 172: static CURLcode rtsp_connect(struct connectdata *conn, bool *done)
! 173: {
! 174: CURLcode httpStatus;
! 175: struct Curl_easy *data = conn->data;
! 176:
! 177: httpStatus = Curl_http_connect(conn, done);
! 178:
! 179: /* Initialize the CSeq if not already done */
! 180: if(data->state.rtsp_next_client_CSeq == 0)
! 181: data->state.rtsp_next_client_CSeq = 1;
! 182: if(data->state.rtsp_next_server_CSeq == 0)
! 183: data->state.rtsp_next_server_CSeq = 1;
! 184:
! 185: conn->proto.rtspc.rtp_channel = -1;
! 186:
! 187: return httpStatus;
! 188: }
! 189:
! 190: static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead)
! 191: {
! 192: (void) dead;
! 193: Curl_safefree(conn->proto.rtspc.rtp_buf);
! 194: return CURLE_OK;
! 195: }
! 196:
! 197:
! 198: static CURLcode rtsp_done(struct connectdata *conn,
! 199: CURLcode status, bool premature)
! 200: {
! 201: struct Curl_easy *data = conn->data;
! 202: struct RTSP *rtsp = data->req.protop;
! 203: CURLcode httpStatus;
! 204:
! 205: /* Bypass HTTP empty-reply checks on receive */
! 206: if(data->set.rtspreq == RTSPREQ_RECEIVE)
! 207: premature = TRUE;
! 208:
! 209: httpStatus = Curl_http_done(conn, status, premature);
! 210:
! 211: if(rtsp) {
! 212: /* Check the sequence numbers */
! 213: long CSeq_sent = rtsp->CSeq_sent;
! 214: long CSeq_recv = rtsp->CSeq_recv;
! 215: if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) {
! 216: failf(data,
! 217: "The CSeq of this request %ld did not match the response %ld",
! 218: CSeq_sent, CSeq_recv);
! 219: return CURLE_RTSP_CSEQ_ERROR;
! 220: }
! 221: if(data->set.rtspreq == RTSPREQ_RECEIVE &&
! 222: (conn->proto.rtspc.rtp_channel == -1)) {
! 223: infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
! 224: }
! 225: }
! 226:
! 227: return httpStatus;
! 228: }
! 229:
! 230: static CURLcode rtsp_do(struct connectdata *conn, bool *done)
! 231: {
! 232: struct Curl_easy *data = conn->data;
! 233: CURLcode result = CURLE_OK;
! 234: Curl_RtspReq rtspreq = data->set.rtspreq;
! 235: struct RTSP *rtsp = data->req.protop;
! 236: Curl_send_buffer *req_buffer;
! 237: curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
! 238: curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
! 239:
! 240: const char *p_request = NULL;
! 241: const char *p_session_id = NULL;
! 242: const char *p_accept = NULL;
! 243: const char *p_accept_encoding = NULL;
! 244: const char *p_range = NULL;
! 245: const char *p_referrer = NULL;
! 246: const char *p_stream_uri = NULL;
! 247: const char *p_transport = NULL;
! 248: const char *p_uagent = NULL;
! 249: const char *p_proxyuserpwd = NULL;
! 250: const char *p_userpwd = NULL;
! 251:
! 252: *done = TRUE;
! 253:
! 254: rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
! 255: rtsp->CSeq_recv = 0;
! 256:
! 257: /* Setup the 'p_request' pointer to the proper p_request string
! 258: * Since all RTSP requests are included here, there is no need to
! 259: * support custom requests like HTTP.
! 260: **/
! 261: data->set.opt_no_body = TRUE; /* most requests don't contain a body */
! 262: switch(rtspreq) {
! 263: default:
! 264: failf(data, "Got invalid RTSP request");
! 265: return CURLE_BAD_FUNCTION_ARGUMENT;
! 266: case RTSPREQ_OPTIONS:
! 267: p_request = "OPTIONS";
! 268: break;
! 269: case RTSPREQ_DESCRIBE:
! 270: p_request = "DESCRIBE";
! 271: data->set.opt_no_body = FALSE;
! 272: break;
! 273: case RTSPREQ_ANNOUNCE:
! 274: p_request = "ANNOUNCE";
! 275: break;
! 276: case RTSPREQ_SETUP:
! 277: p_request = "SETUP";
! 278: break;
! 279: case RTSPREQ_PLAY:
! 280: p_request = "PLAY";
! 281: break;
! 282: case RTSPREQ_PAUSE:
! 283: p_request = "PAUSE";
! 284: break;
! 285: case RTSPREQ_TEARDOWN:
! 286: p_request = "TEARDOWN";
! 287: break;
! 288: case RTSPREQ_GET_PARAMETER:
! 289: /* GET_PARAMETER's no_body status is determined later */
! 290: p_request = "GET_PARAMETER";
! 291: data->set.opt_no_body = FALSE;
! 292: break;
! 293: case RTSPREQ_SET_PARAMETER:
! 294: p_request = "SET_PARAMETER";
! 295: break;
! 296: case RTSPREQ_RECORD:
! 297: p_request = "RECORD";
! 298: break;
! 299: case RTSPREQ_RECEIVE:
! 300: p_request = "";
! 301: /* Treat interleaved RTP as body*/
! 302: data->set.opt_no_body = FALSE;
! 303: break;
! 304: case RTSPREQ_LAST:
! 305: failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
! 306: return CURLE_BAD_FUNCTION_ARGUMENT;
! 307: }
! 308:
! 309: if(rtspreq == RTSPREQ_RECEIVE) {
! 310: Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
! 311:
! 312: return result;
! 313: }
! 314:
! 315: p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
! 316: if(!p_session_id &&
! 317: (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
! 318: failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
! 319: p_request);
! 320: return CURLE_BAD_FUNCTION_ARGUMENT;
! 321: }
! 322:
! 323: /* Stream URI. Default to server '*' if not specified */
! 324: if(data->set.str[STRING_RTSP_STREAM_URI]) {
! 325: p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
! 326: }
! 327: else {
! 328: p_stream_uri = "*";
! 329: }
! 330:
! 331: /* Transport Header for SETUP requests */
! 332: p_transport = Curl_checkheaders(conn, "Transport");
! 333: if(rtspreq == RTSPREQ_SETUP && !p_transport) {
! 334: /* New Transport: setting? */
! 335: if(data->set.str[STRING_RTSP_TRANSPORT]) {
! 336: Curl_safefree(conn->allocptr.rtsp_transport);
! 337:
! 338: conn->allocptr.rtsp_transport =
! 339: aprintf("Transport: %s\r\n",
! 340: data->set.str[STRING_RTSP_TRANSPORT]);
! 341: if(!conn->allocptr.rtsp_transport)
! 342: return CURLE_OUT_OF_MEMORY;
! 343: }
! 344: else {
! 345: failf(data,
! 346: "Refusing to issue an RTSP SETUP without a Transport: header.");
! 347: return CURLE_BAD_FUNCTION_ARGUMENT;
! 348: }
! 349:
! 350: p_transport = conn->allocptr.rtsp_transport;
! 351: }
! 352:
! 353: /* Accept Headers for DESCRIBE requests */
! 354: if(rtspreq == RTSPREQ_DESCRIBE) {
! 355: /* Accept Header */
! 356: p_accept = Curl_checkheaders(conn, "Accept")?
! 357: NULL:"Accept: application/sdp\r\n";
! 358:
! 359: /* Accept-Encoding header */
! 360: if(!Curl_checkheaders(conn, "Accept-Encoding") &&
! 361: data->set.str[STRING_ENCODING]) {
! 362: Curl_safefree(conn->allocptr.accept_encoding);
! 363: conn->allocptr.accept_encoding =
! 364: aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
! 365:
! 366: if(!conn->allocptr.accept_encoding)
! 367: return CURLE_OUT_OF_MEMORY;
! 368:
! 369: p_accept_encoding = conn->allocptr.accept_encoding;
! 370: }
! 371: }
! 372:
! 373: /* The User-Agent string might have been allocated in url.c already, because
! 374: it might have been used in the proxy connect, but if we have got a header
! 375: with the user-agent string specified, we erase the previously made string
! 376: here. */
! 377: if(Curl_checkheaders(conn, "User-Agent") && conn->allocptr.uagent) {
! 378: Curl_safefree(conn->allocptr.uagent);
! 379: conn->allocptr.uagent = NULL;
! 380: }
! 381: else if(!Curl_checkheaders(conn, "User-Agent") &&
! 382: data->set.str[STRING_USERAGENT]) {
! 383: p_uagent = conn->allocptr.uagent;
! 384: }
! 385:
! 386: /* setup the authentication headers */
! 387: result = Curl_http_output_auth(conn, p_request, p_stream_uri, FALSE);
! 388: if(result)
! 389: return result;
! 390:
! 391: p_proxyuserpwd = conn->allocptr.proxyuserpwd;
! 392: p_userpwd = conn->allocptr.userpwd;
! 393:
! 394: /* Referrer */
! 395: Curl_safefree(conn->allocptr.ref);
! 396: if(data->change.referer && !Curl_checkheaders(conn, "Referer"))
! 397: conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
! 398: else
! 399: conn->allocptr.ref = NULL;
! 400:
! 401: p_referrer = conn->allocptr.ref;
! 402:
! 403: /*
! 404: * Range Header
! 405: * Only applies to PLAY, PAUSE, RECORD
! 406: *
! 407: * Go ahead and use the Range stuff supplied for HTTP
! 408: */
! 409: if(data->state.use_range &&
! 410: (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {
! 411:
! 412: /* Check to see if there is a range set in the custom headers */
! 413: if(!Curl_checkheaders(conn, "Range") && data->state.range) {
! 414: Curl_safefree(conn->allocptr.rangeline);
! 415: conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
! 416: p_range = conn->allocptr.rangeline;
! 417: }
! 418: }
! 419:
! 420: /*
! 421: * Sanity check the custom headers
! 422: */
! 423: if(Curl_checkheaders(conn, "CSeq")) {
! 424: failf(data, "CSeq cannot be set as a custom header.");
! 425: return CURLE_RTSP_CSEQ_ERROR;
! 426: }
! 427: if(Curl_checkheaders(conn, "Session")) {
! 428: failf(data, "Session ID cannot be set as a custom header.");
! 429: return CURLE_BAD_FUNCTION_ARGUMENT;
! 430: }
! 431:
! 432: /* Initialize a dynamic send buffer */
! 433: req_buffer = Curl_add_buffer_init();
! 434:
! 435: if(!req_buffer)
! 436: return CURLE_OUT_OF_MEMORY;
! 437:
! 438: result =
! 439: Curl_add_bufferf(&req_buffer,
! 440: "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
! 441: "CSeq: %ld\r\n", /* CSeq */
! 442: p_request, p_stream_uri, rtsp->CSeq_sent);
! 443: if(result)
! 444: return result;
! 445:
! 446: /*
! 447: * Rather than do a normal alloc line, keep the session_id unformatted
! 448: * to make comparison easier
! 449: */
! 450: if(p_session_id) {
! 451: result = Curl_add_bufferf(&req_buffer, "Session: %s\r\n", p_session_id);
! 452: if(result)
! 453: return result;
! 454: }
! 455:
! 456: /*
! 457: * Shared HTTP-like options
! 458: */
! 459: result = Curl_add_bufferf(&req_buffer,
! 460: "%s" /* transport */
! 461: "%s" /* accept */
! 462: "%s" /* accept-encoding */
! 463: "%s" /* range */
! 464: "%s" /* referrer */
! 465: "%s" /* user-agent */
! 466: "%s" /* proxyuserpwd */
! 467: "%s" /* userpwd */
! 468: ,
! 469: p_transport ? p_transport : "",
! 470: p_accept ? p_accept : "",
! 471: p_accept_encoding ? p_accept_encoding : "",
! 472: p_range ? p_range : "",
! 473: p_referrer ? p_referrer : "",
! 474: p_uagent ? p_uagent : "",
! 475: p_proxyuserpwd ? p_proxyuserpwd : "",
! 476: p_userpwd ? p_userpwd : "");
! 477:
! 478: /*
! 479: * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
! 480: * with basic and digest, it will be freed anyway by the next request
! 481: */
! 482: Curl_safefree(conn->allocptr.userpwd);
! 483: conn->allocptr.userpwd = NULL;
! 484:
! 485: if(result)
! 486: return result;
! 487:
! 488: if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
! 489: result = Curl_add_timecondition(conn, req_buffer);
! 490: if(result)
! 491: return result;
! 492: }
! 493:
! 494: result = Curl_add_custom_headers(conn, FALSE, req_buffer);
! 495: if(result)
! 496: return result;
! 497:
! 498: if(rtspreq == RTSPREQ_ANNOUNCE ||
! 499: rtspreq == RTSPREQ_SET_PARAMETER ||
! 500: rtspreq == RTSPREQ_GET_PARAMETER) {
! 501:
! 502: if(data->set.upload) {
! 503: putsize = data->state.infilesize;
! 504: data->set.httpreq = HTTPREQ_PUT;
! 505:
! 506: }
! 507: else {
! 508: postsize = (data->state.infilesize != -1)?
! 509: data->state.infilesize:
! 510: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
! 511: data->set.httpreq = HTTPREQ_POST;
! 512: }
! 513:
! 514: if(putsize > 0 || postsize > 0) {
! 515: /* As stated in the http comments, it is probably not wise to
! 516: * actually set a custom Content-Length in the headers */
! 517: if(!Curl_checkheaders(conn, "Content-Length")) {
! 518: result =
! 519: Curl_add_bufferf(&req_buffer,
! 520: "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
! 521: (data->set.upload ? putsize : postsize));
! 522: if(result)
! 523: return result;
! 524: }
! 525:
! 526: if(rtspreq == RTSPREQ_SET_PARAMETER ||
! 527: rtspreq == RTSPREQ_GET_PARAMETER) {
! 528: if(!Curl_checkheaders(conn, "Content-Type")) {
! 529: result = Curl_add_bufferf(&req_buffer,
! 530: "Content-Type: text/parameters\r\n");
! 531: if(result)
! 532: return result;
! 533: }
! 534: }
! 535:
! 536: if(rtspreq == RTSPREQ_ANNOUNCE) {
! 537: if(!Curl_checkheaders(conn, "Content-Type")) {
! 538: result = Curl_add_bufferf(&req_buffer,
! 539: "Content-Type: application/sdp\r\n");
! 540: if(result)
! 541: return result;
! 542: }
! 543: }
! 544:
! 545: data->state.expect100header = FALSE; /* RTSP posts are simple/small */
! 546: }
! 547: else if(rtspreq == RTSPREQ_GET_PARAMETER) {
! 548: /* Check for an empty GET_PARAMETER (heartbeat) request */
! 549: data->set.httpreq = HTTPREQ_HEAD;
! 550: data->set.opt_no_body = TRUE;
! 551: }
! 552: }
! 553:
! 554: /* RTSP never allows chunked transfer */
! 555: data->req.forbidchunk = TRUE;
! 556: /* Finish the request buffer */
! 557: result = Curl_add_buffer(&req_buffer, "\r\n", 2);
! 558: if(result)
! 559: return result;
! 560:
! 561: if(postsize > 0) {
! 562: result = Curl_add_buffer(&req_buffer, data->set.postfields,
! 563: (size_t)postsize);
! 564: if(result)
! 565: return result;
! 566: }
! 567:
! 568: /* issue the request */
! 569: result = Curl_add_buffer_send(&req_buffer, conn,
! 570: &data->info.request_size, 0, FIRSTSOCKET);
! 571: if(result) {
! 572: failf(data, "Failed sending RTSP request");
! 573: return result;
! 574: }
! 575:
! 576: Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1);
! 577:
! 578: /* Increment the CSeq on success */
! 579: data->state.rtsp_next_client_CSeq++;
! 580:
! 581: if(data->req.writebytecount) {
! 582: /* if a request-body has been sent off, we make sure this progress is
! 583: noted properly */
! 584: Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
! 585: if(Curl_pgrsUpdate(conn))
! 586: result = CURLE_ABORTED_BY_CALLBACK;
! 587: }
! 588:
! 589: return result;
! 590: }
! 591:
! 592:
! 593: static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
! 594: struct connectdata *conn,
! 595: ssize_t *nread,
! 596: bool *readmore) {
! 597: struct SingleRequest *k = &data->req;
! 598: struct rtsp_conn *rtspc = &(conn->proto.rtspc);
! 599:
! 600: char *rtp; /* moving pointer to rtp data */
! 601: ssize_t rtp_dataleft; /* how much data left to parse in this round */
! 602: char *scratch;
! 603: CURLcode result;
! 604:
! 605: if(rtspc->rtp_buf) {
! 606: /* There was some leftover data the last time. Merge buffers */
! 607: char *newptr = Curl_saferealloc(rtspc->rtp_buf,
! 608: rtspc->rtp_bufsize + *nread);
! 609: if(!newptr) {
! 610: rtspc->rtp_buf = NULL;
! 611: rtspc->rtp_bufsize = 0;
! 612: return CURLE_OUT_OF_MEMORY;
! 613: }
! 614: rtspc->rtp_buf = newptr;
! 615: memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread);
! 616: rtspc->rtp_bufsize += *nread;
! 617: rtp = rtspc->rtp_buf;
! 618: rtp_dataleft = rtspc->rtp_bufsize;
! 619: }
! 620: else {
! 621: /* Just parse the request buffer directly */
! 622: rtp = k->str;
! 623: rtp_dataleft = *nread;
! 624: }
! 625:
! 626: while((rtp_dataleft > 0) &&
! 627: (rtp[0] == '$')) {
! 628: if(rtp_dataleft > 4) {
! 629: int rtp_length;
! 630:
! 631: /* Parse the header */
! 632: /* The channel identifier immediately follows and is 1 byte */
! 633: rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp);
! 634:
! 635: /* The length is two bytes */
! 636: rtp_length = RTP_PKT_LENGTH(rtp);
! 637:
! 638: if(rtp_dataleft < rtp_length + 4) {
! 639: /* Need more - incomplete payload*/
! 640: *readmore = TRUE;
! 641: break;
! 642: }
! 643: /* We have the full RTP interleaved packet
! 644: * Write out the header including the leading '$' */
! 645: DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
! 646: rtspc->rtp_channel, rtp_length));
! 647: result = rtp_client_write(conn, &rtp[0], rtp_length + 4);
! 648: if(result) {
! 649: failf(data, "Got an error writing an RTP packet");
! 650: *readmore = FALSE;
! 651: Curl_safefree(rtspc->rtp_buf);
! 652: rtspc->rtp_buf = NULL;
! 653: rtspc->rtp_bufsize = 0;
! 654: return result;
! 655: }
! 656:
! 657: /* Move forward in the buffer */
! 658: rtp_dataleft -= rtp_length + 4;
! 659: rtp += rtp_length + 4;
! 660:
! 661: if(data->set.rtspreq == RTSPREQ_RECEIVE) {
! 662: /* If we are in a passive receive, give control back
! 663: * to the app as often as we can.
! 664: */
! 665: k->keepon &= ~KEEP_RECV;
! 666: }
! 667: }
! 668: else {
! 669: /* Need more - incomplete header */
! 670: *readmore = TRUE;
! 671: break;
! 672: }
! 673: }
! 674:
! 675: if(rtp_dataleft != 0 && rtp[0] == '$') {
! 676: DEBUGF(infof(data, "RTP Rewinding %zd %s\n", rtp_dataleft,
! 677: *readmore ? "(READMORE)" : ""));
! 678:
! 679: /* Store the incomplete RTP packet for a "rewind" */
! 680: scratch = malloc(rtp_dataleft);
! 681: if(!scratch) {
! 682: Curl_safefree(rtspc->rtp_buf);
! 683: rtspc->rtp_buf = NULL;
! 684: rtspc->rtp_bufsize = 0;
! 685: return CURLE_OUT_OF_MEMORY;
! 686: }
! 687: memcpy(scratch, rtp, rtp_dataleft);
! 688: Curl_safefree(rtspc->rtp_buf);
! 689: rtspc->rtp_buf = scratch;
! 690: rtspc->rtp_bufsize = rtp_dataleft;
! 691:
! 692: /* As far as the transfer is concerned, this data is consumed */
! 693: *nread = 0;
! 694: return CURLE_OK;
! 695: }
! 696: /* Fix up k->str to point just after the last RTP packet */
! 697: k->str += *nread - rtp_dataleft;
! 698:
! 699: /* either all of the data has been read or...
! 700: * rtp now points at the next byte to parse
! 701: */
! 702: if(rtp_dataleft > 0)
! 703: DEBUGASSERT(k->str[0] == rtp[0]);
! 704:
! 705: DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */
! 706:
! 707: *nread = rtp_dataleft;
! 708:
! 709: /* If we get here, we have finished with the leftover/merge buffer */
! 710: Curl_safefree(rtspc->rtp_buf);
! 711: rtspc->rtp_buf = NULL;
! 712: rtspc->rtp_bufsize = 0;
! 713:
! 714: return CURLE_OK;
! 715: }
! 716:
! 717: static
! 718: CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
! 719: {
! 720: struct Curl_easy *data = conn->data;
! 721: size_t wrote;
! 722: curl_write_callback writeit;
! 723: void *user_ptr;
! 724:
! 725: if(len == 0) {
! 726: failf(data, "Cannot write a 0 size RTP packet.");
! 727: return CURLE_WRITE_ERROR;
! 728: }
! 729:
! 730: /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that
! 731: function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP
! 732: data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA
! 733: pointer to write out the RTP data. */
! 734: if(data->set.fwrite_rtp) {
! 735: writeit = data->set.fwrite_rtp;
! 736: user_ptr = data->set.rtp_out;
! 737: }
! 738: else {
! 739: writeit = data->set.fwrite_func;
! 740: user_ptr = data->set.out;
! 741: }
! 742:
! 743: Curl_set_in_callback(data, true);
! 744: wrote = writeit(ptr, 1, len, user_ptr);
! 745: Curl_set_in_callback(data, false);
! 746:
! 747: if(CURL_WRITEFUNC_PAUSE == wrote) {
! 748: failf(data, "Cannot pause RTP");
! 749: return CURLE_WRITE_ERROR;
! 750: }
! 751:
! 752: if(wrote != len) {
! 753: failf(data, "Failed writing RTP data");
! 754: return CURLE_WRITE_ERROR;
! 755: }
! 756:
! 757: return CURLE_OK;
! 758: }
! 759:
! 760: CURLcode Curl_rtsp_parseheader(struct connectdata *conn,
! 761: char *header)
! 762: {
! 763: struct Curl_easy *data = conn->data;
! 764: long CSeq = 0;
! 765:
! 766: if(checkprefix("CSeq:", header)) {
! 767: /* Store the received CSeq. Match is verified in rtsp_done */
! 768: int nc = sscanf(&header[4], ": %ld", &CSeq);
! 769: if(nc == 1) {
! 770: struct RTSP *rtsp = data->req.protop;
! 771: rtsp->CSeq_recv = CSeq; /* mark the request */
! 772: data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
! 773: }
! 774: else {
! 775: failf(data, "Unable to read the CSeq header: [%s]", header);
! 776: return CURLE_RTSP_CSEQ_ERROR;
! 777: }
! 778: }
! 779: else if(checkprefix("Session:", header)) {
! 780: char *start;
! 781:
! 782: /* Find the first non-space letter */
! 783: start = header + 8;
! 784: while(*start && ISSPACE(*start))
! 785: start++;
! 786:
! 787: if(!*start) {
! 788: failf(data, "Got a blank Session ID");
! 789: }
! 790: else if(data->set.str[STRING_RTSP_SESSION_ID]) {
! 791: /* If the Session ID is set, then compare */
! 792: if(strncmp(start, data->set.str[STRING_RTSP_SESSION_ID],
! 793: strlen(data->set.str[STRING_RTSP_SESSION_ID])) != 0) {
! 794: failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
! 795: start, data->set.str[STRING_RTSP_SESSION_ID]);
! 796: return CURLE_RTSP_SESSION_ERROR;
! 797: }
! 798: }
! 799: else {
! 800: /* If the Session ID is not set, and we find it in a response, then set
! 801: * it.
! 802: *
! 803: * Allow any non whitespace content, up to the field separator or end of
! 804: * line. RFC 2326 isn't 100% clear on the session ID and for example
! 805: * gstreamer does url-encoded session ID's not covered by the standard.
! 806: */
! 807: char *end = start;
! 808: while(*end && *end != ';' && !ISSPACE(*end))
! 809: end++;
! 810:
! 811: /* Copy the id substring into a new buffer */
! 812: data->set.str[STRING_RTSP_SESSION_ID] = malloc(end - start + 1);
! 813: if(data->set.str[STRING_RTSP_SESSION_ID] == NULL)
! 814: return CURLE_OUT_OF_MEMORY;
! 815: memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, end - start);
! 816: (data->set.str[STRING_RTSP_SESSION_ID])[end - start] = '\0';
! 817: }
! 818: }
! 819: return CURLE_OK;
! 820: }
! 821:
! 822: #endif /* CURL_DISABLE_RTSP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>