mirror of
https://github.com/curl/curl.git
synced 2024-11-21 01:16:58 +08:00
Chris Conroy's RTSP followup fixes
This commit is contained in:
parent
3cb76e5ebb
commit
2f3bce1193
@ -411,7 +411,7 @@ interleaved header as well as the included data for each call. The first byte
|
||||
is always an ASCII dollar sign. The dollar sign is followed by a one byte
|
||||
channel identifier and then a 2 byte integer length in network byte order. See
|
||||
\fIRFC 2326 Section 10.12\fP for more information on how RTP interleaving
|
||||
behaves.
|
||||
behaves. If unset or set to NULL, curl will use the default write function.
|
||||
|
||||
Interleaved RTP poses some challeneges for the client application. Since the
|
||||
stream data is sharing the RTSP control connection, it is critical to service
|
||||
@ -424,11 +424,7 @@ process any pending RTP data before marking the request as finished. (Added
|
||||
in 7.20.0)
|
||||
.IP CURLOPT_INTERLEAVEDATA
|
||||
This is the stream that will be passed to \fICURLOPT_INTERLEAVEFUNCTION\fP when
|
||||
interleaved RTP data is received. Since the application is required to provide
|
||||
a custom function for RTP data, there is no requirement that the stream
|
||||
pointer be a valid FILE pointer. An application may wish to pass a cookie
|
||||
containing information about many streams to assist in demultiplexing the
|
||||
different RTP channels. (Added in 7.20.0)
|
||||
interleaved RTP data is received. (Added in 7.20.0)
|
||||
.SH ERROR OPTIONS
|
||||
.IP CURLOPT_ERRORBUFFER
|
||||
Pass a char * to a buffer that the libcurl may store human readable error
|
||||
|
96
lib/rtsp.c
96
lib/rtsp.c
@ -127,6 +127,10 @@ CURLcode Curl_rtsp_done(struct connectdata *conn,
|
||||
long CSeq_sent;
|
||||
long CSeq_recv;
|
||||
|
||||
/* Bypass HTTP empty-reply checks on receive */
|
||||
if(data->set.rtspreq == RTSPREQ_RECEIVE)
|
||||
premature = TRUE;
|
||||
|
||||
httpStatus = Curl_http_done(conn, status, premature);
|
||||
|
||||
/* Check the sequence numbers */
|
||||
@ -139,7 +143,7 @@ CURLcode Curl_rtsp_done(struct connectdata *conn,
|
||||
}
|
||||
else if (data->set.rtspreq == RTSPREQ_RECEIVE &&
|
||||
(conn->proto.rtspc.rtp_channel == -1)) {
|
||||
infof(data, "Got a non RTP Receive with a CSeq of %ld\n", CSeq_recv);
|
||||
infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
|
||||
/* TODO CPC: Server -> Client logic here */
|
||||
}
|
||||
|
||||
@ -376,7 +380,7 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done)
|
||||
result =
|
||||
Curl_add_bufferf(req_buffer,
|
||||
"%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
|
||||
"CSeq: %d \r\n", /* CSeq */
|
||||
"CSeq: %d\r\n", /* CSeq */
|
||||
(p_request ? p_request : ""), p_stream_uri,
|
||||
rtsp->CSeq_sent);
|
||||
if(result)
|
||||
@ -387,7 +391,7 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done)
|
||||
* to make comparison easier
|
||||
*/
|
||||
if(p_session_id) {
|
||||
result = Curl_add_bufferf(req_buffer, "Session: %s \r\n", p_session_id);
|
||||
result = Curl_add_bufferf(req_buffer, "Session: %s\r\n", p_session_id);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
@ -512,8 +516,7 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done)
|
||||
CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||
struct connectdata *conn,
|
||||
ssize_t *nread,
|
||||
bool *readmore,
|
||||
bool *done) {
|
||||
bool *readmore) {
|
||||
struct SingleRequest *k = &data->req;
|
||||
struct rtsp_conn *rtspc = &(conn->proto.rtspc);
|
||||
|
||||
@ -538,10 +541,6 @@ CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||
rtp_dataleft = *nread;
|
||||
}
|
||||
|
||||
if(rtp_dataleft == 0 || rtp[0] != '$') {
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
while((rtp_dataleft > 0) &&
|
||||
(rtp[0] == '$')) {
|
||||
if(rtp_dataleft > 4) {
|
||||
@ -564,22 +563,18 @@ CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||
else {
|
||||
/* We have the full RTP interleaved packet
|
||||
* Write out the header but strip the leading '$' */
|
||||
infof(data, "CPCDEBUG: RTP write channel %d rtp_length %d\n",
|
||||
rtspc->rtp_channel, rtp_length);
|
||||
DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
|
||||
rtspc->rtp_channel, rtp_length));
|
||||
result = rtp_client_write(conn, &rtp[1], rtp_length + 3);
|
||||
if(result) {
|
||||
failf(data, "Got an error writing an RTP packet");
|
||||
*done = TRUE;
|
||||
*readmore = FALSE;
|
||||
return result;
|
||||
failf(data, "Got an error writing an RTP packet");
|
||||
*readmore = FALSE;
|
||||
Curl_safefree(rtspc->rtp_buf);
|
||||
rtspc->rtp_buf = NULL;
|
||||
rtspc->rtp_bufsize = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Update progress */
|
||||
k->bytecount += rtp_length + 4;
|
||||
Curl_pgrsSetDownloadCounter(data, k->bytecount);
|
||||
if(k->bytecountp)
|
||||
*k->bytecountp = k->bytecount;
|
||||
|
||||
/* Move forward in the buffer */
|
||||
rtp_dataleft -= rtp_length + 4;
|
||||
rtp += rtp_length + 4;
|
||||
@ -587,11 +582,8 @@ CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||
if(data->set.rtspreq == RTSPREQ_RECEIVE) {
|
||||
/* If we are in a passive receive, give control back
|
||||
* to the app as often as we can.
|
||||
*
|
||||
* Otherwise, keep chugging along until we get RTSP data
|
||||
*/
|
||||
k->keepon &= ~KEEP_RECV;
|
||||
*done = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -602,41 +594,36 @@ CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||
}
|
||||
}
|
||||
|
||||
if(*done || *readmore) {
|
||||
if(rtp_dataleft != 0 && rtp[0] == '$') {
|
||||
infof(data, "RTP Rewinding %zu %s %s\n", rtp_dataleft,
|
||||
*done ? "DONE " : "",
|
||||
*readmore ? "READMORE" : "");
|
||||
if(rtp_dataleft != 0 && rtp[0] == '$') {
|
||||
DEBUGF(infof(data, "RTP Rewinding %zu %s\n", rtp_dataleft,
|
||||
*readmore ? "(READMORE)" : ""));
|
||||
|
||||
/* Store the incomplete RTP packet for a "rewind" */
|
||||
scratch = malloc(rtp_dataleft);
|
||||
if(!scratch)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
memcpy(scratch, rtp, rtp_dataleft);
|
||||
Curl_safefree(rtspc->rtp_buf);
|
||||
rtspc->rtp_buf = scratch;
|
||||
rtspc->rtp_bufsize = rtp_dataleft;
|
||||
return CURLE_OK;
|
||||
}
|
||||
/* Store the incomplete RTP packet for a "rewind" */
|
||||
scratch = malloc(rtp_dataleft);
|
||||
if(!scratch)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
memcpy(scratch, rtp, rtp_dataleft);
|
||||
Curl_safefree(rtspc->rtp_buf);
|
||||
rtspc->rtp_buf = scratch;
|
||||
rtspc->rtp_bufsize = rtp_dataleft;
|
||||
|
||||
/* As far as the transfer is concerned, this data is consumed */
|
||||
*nread = 0;
|
||||
return CURLE_OK;
|
||||
}
|
||||
else {
|
||||
/* RTP followed by RTSP */
|
||||
if(rtp_dataleft == 0) {
|
||||
/* Need more */
|
||||
*readmore = TRUE;
|
||||
}
|
||||
else {
|
||||
/* Fix up k->str to point just after the last RTP packet */
|
||||
k->str += *nread - rtp_dataleft;
|
||||
/* Fix up k->str to point just after the last RTP packet */
|
||||
k->str += *nread - rtp_dataleft;
|
||||
|
||||
/* rtp may point into the leftover buffer, but at this point
|
||||
* it is somewhere in the merged data from k->str. */
|
||||
/* either all of the data has been read or...
|
||||
* rtp now points at the next byte to parse
|
||||
*/
|
||||
if(rtp_dataleft > 0)
|
||||
DEBUGASSERT(k->str[0] == rtp[0]);
|
||||
|
||||
DEBUGASSERT(rtp_dataleft < *nread); /* sanity check */
|
||||
DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */
|
||||
|
||||
*nread = rtp_dataleft;
|
||||
}
|
||||
*nread = rtp_dataleft;
|
||||
}
|
||||
|
||||
/* If we get here, we have finished with the leftover/merge buffer */
|
||||
@ -644,9 +631,6 @@ CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||
rtspc->rtp_buf = NULL;
|
||||
rtspc->rtp_bufsize = 0;
|
||||
|
||||
/* TODO CPC: Could implement parsing logic for Server->Client requests
|
||||
here */
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@ -701,7 +685,7 @@ CURLcode Curl_rtsp_parseheader(struct connectdata *conn,
|
||||
failf(data, "Unable to read the CSeq header: [%s]", header);
|
||||
return CURLE_RTSP_CSEQ_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(checkprefix("Session:", header)) {
|
||||
char *start;
|
||||
|
||||
|
10
lib/rtsp.h
10
lib/rtsp.h
@ -27,11 +27,17 @@
|
||||
|
||||
extern const struct Curl_handler Curl_handler_rtsp;
|
||||
|
||||
/*
|
||||
* Parse and write out any available RTP data.
|
||||
*
|
||||
* nread: amount of data left after k->str. will be modified if RTP
|
||||
* data is parsed and k->str is moved up
|
||||
* readmore: whether or not the RTP parser needs more data right away
|
||||
*/
|
||||
CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||
struct connectdata *conn,
|
||||
ssize_t *nread,
|
||||
bool *readmore,
|
||||
bool *done);
|
||||
bool *readmore);
|
||||
|
||||
|
||||
/* protocol-specific functions set up to be called by the main engine */
|
||||
|
@ -368,7 +368,11 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
ssize_t nread; /* number of bytes read */
|
||||
size_t excess = 0; /* excess bytes read */
|
||||
bool is_empty_data = FALSE;
|
||||
#ifndef CURL_DISABLE_RTSP
|
||||
bool readmore = FALSE; /* used by RTP to signal for more data */
|
||||
#endif
|
||||
|
||||
*done = FALSE;
|
||||
|
||||
@ -437,15 +441,13 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||
k->str = k->buf;
|
||||
|
||||
#ifndef CURL_DISABLE_RTSP
|
||||
/* Check for RTP at the beginning of the data */
|
||||
if(conn->protocol & PROT_RTSP) {
|
||||
bool readmore = FALSE;
|
||||
result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore, done);
|
||||
result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
|
||||
if(result)
|
||||
return result;
|
||||
if(readmore)
|
||||
break;
|
||||
if(*done)
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -458,6 +460,18 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||
result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
#ifndef CURL_DISABLE_RTSP
|
||||
/* Check for RTP after the headers if there is no Content */
|
||||
if(k->maxdownload <= 0 && nread > 0 && (conn->protocol & PROT_RTSP)) {
|
||||
result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
|
||||
if(result)
|
||||
return result;
|
||||
if(readmore)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(stop_reading)
|
||||
/* We've stopped dealing with input, get out of the do-while loop */
|
||||
break;
|
||||
@ -594,13 +608,19 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||
}
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
|
||||
/* Account for body content stored in the header buffer */
|
||||
if(k->badheader && !k->ignorebody) {
|
||||
DEBUGF(infof(data, "Increasing bytecount by %" FORMAT_OFF_T" from hbuflen\n", k->hbuflen));
|
||||
k->bytecount += k->hbuflen;
|
||||
}
|
||||
|
||||
if((-1 != k->maxdownload) &&
|
||||
(k->bytecount + nread >= k->maxdownload)) {
|
||||
|
||||
excess = (size_t)(k->bytecount + nread - k->maxdownload);
|
||||
if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
|
||||
/* The 'excess' amount below can't be more than BUFSIZE which
|
||||
always will fit in a size_t */
|
||||
size_t excess = (size_t)(k->bytecount + nread - k->maxdownload);
|
||||
if(excess > 0 && !k->ignorebody) {
|
||||
infof(data,
|
||||
"Rewinding stream by : %d"
|
||||
@ -612,6 +632,15 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||
read_rewind(conn, excess);
|
||||
}
|
||||
}
|
||||
else {
|
||||
infof(data,
|
||||
"Excess found in a non pipelined read:"
|
||||
" excess=%zu"
|
||||
", size=%" FORMAT_OFF_T
|
||||
", maxdownload=%" FORMAT_OFF_T
|
||||
", bytecount=%" FORMAT_OFF_T "\n",
|
||||
excess, k->size, k->maxdownload, k->bytecount);
|
||||
}
|
||||
|
||||
nread = (ssize_t) (k->maxdownload - k->bytecount);
|
||||
if(nread < 0 ) /* this should be unusual */
|
||||
@ -630,9 +659,17 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||
if(k->badheader && !k->ignorebody) {
|
||||
/* we parsed a piece of data wrongly assuming it was a header
|
||||
and now we output it as body instead */
|
||||
result = Curl_client_write(conn, CLIENTWRITE_BODY,
|
||||
data->state.headerbuff,
|
||||
k->hbuflen);
|
||||
|
||||
/* Don't let excess data pollute body writes */
|
||||
if(k->maxdownload == -1 || (curl_off_t)k->hbuflen <= k->maxdownload)
|
||||
result = Curl_client_write(conn, CLIENTWRITE_BODY,
|
||||
data->state.headerbuff,
|
||||
k->hbuflen);
|
||||
else
|
||||
result = Curl_client_write(conn, CLIENTWRITE_BODY,
|
||||
data->state.headerbuff,
|
||||
k->maxdownload);
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
@ -694,6 +731,25 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||
|
||||
} /* if(! header and data to read ) */
|
||||
|
||||
#ifndef CURL_DISABLE_RTSP
|
||||
if(excess > 0 && !conn->bits.stream_was_rewound &&
|
||||
(conn->protocol & PROT_RTSP)) {
|
||||
/* Check for RTP after the content if there is unrewound excess */
|
||||
|
||||
/* Parse the excess data */
|
||||
k->str += nread;
|
||||
nread = excess;
|
||||
|
||||
result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(readmore)
|
||||
k->keepon |= KEEP_RECV; /* we're not done reading */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(is_empty_data) {
|
||||
/* if we received nothing, the server closed the connection and we
|
||||
are done */
|
||||
|
Loading…
Reference in New Issue
Block a user