mirror of https://github.com/moparisthebest/curl
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
|
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
|
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
|
\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
|
Interleaved RTP poses some challeneges for the client application. Since the
|
||||||
stream data is sharing the RTSP control connection, it is critical to service
|
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)
|
in 7.20.0)
|
||||||
.IP CURLOPT_INTERLEAVEDATA
|
.IP CURLOPT_INTERLEAVEDATA
|
||||||
This is the stream that will be passed to \fICURLOPT_INTERLEAVEFUNCTION\fP when
|
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
|
interleaved RTP data is received. (Added in 7.20.0)
|
||||||
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)
|
|
||||||
.SH ERROR OPTIONS
|
.SH ERROR OPTIONS
|
||||||
.IP CURLOPT_ERRORBUFFER
|
.IP CURLOPT_ERRORBUFFER
|
||||||
Pass a char * to a buffer that the libcurl may store human readable error
|
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_sent;
|
||||||
long CSeq_recv;
|
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);
|
httpStatus = Curl_http_done(conn, status, premature);
|
||||||
|
|
||||||
/* Check the sequence numbers */
|
/* Check the sequence numbers */
|
||||||
|
@ -139,7 +143,7 @@ CURLcode Curl_rtsp_done(struct connectdata *conn,
|
||||||
}
|
}
|
||||||
else if (data->set.rtspreq == RTSPREQ_RECEIVE &&
|
else if (data->set.rtspreq == RTSPREQ_RECEIVE &&
|
||||||
(conn->proto.rtspc.rtp_channel == -1)) {
|
(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 */
|
/* TODO CPC: Server -> Client logic here */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,7 +380,7 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done)
|
||||||
result =
|
result =
|
||||||
Curl_add_bufferf(req_buffer,
|
Curl_add_bufferf(req_buffer,
|
||||||
"%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
|
"%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,
|
(p_request ? p_request : ""), p_stream_uri,
|
||||||
rtsp->CSeq_sent);
|
rtsp->CSeq_sent);
|
||||||
if(result)
|
if(result)
|
||||||
|
@ -387,7 +391,7 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done)
|
||||||
* to make comparison easier
|
* to make comparison easier
|
||||||
*/
|
*/
|
||||||
if(p_session_id) {
|
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)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -512,8 +516,7 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done)
|
||||||
CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
ssize_t *nread,
|
ssize_t *nread,
|
||||||
bool *readmore,
|
bool *readmore) {
|
||||||
bool *done) {
|
|
||||||
struct SingleRequest *k = &data->req;
|
struct SingleRequest *k = &data->req;
|
||||||
struct rtsp_conn *rtspc = &(conn->proto.rtspc);
|
struct rtsp_conn *rtspc = &(conn->proto.rtspc);
|
||||||
|
|
||||||
|
@ -538,10 +541,6 @@ CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||||
rtp_dataleft = *nread;
|
rtp_dataleft = *nread;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rtp_dataleft == 0 || rtp[0] != '$') {
|
|
||||||
return CURLE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
while((rtp_dataleft > 0) &&
|
while((rtp_dataleft > 0) &&
|
||||||
(rtp[0] == '$')) {
|
(rtp[0] == '$')) {
|
||||||
if(rtp_dataleft > 4) {
|
if(rtp_dataleft > 4) {
|
||||||
|
@ -564,22 +563,18 @@ CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||||
else {
|
else {
|
||||||
/* We have the full RTP interleaved packet
|
/* We have the full RTP interleaved packet
|
||||||
* Write out the header but strip the leading '$' */
|
* Write out the header but strip the leading '$' */
|
||||||
infof(data, "CPCDEBUG: RTP write channel %d rtp_length %d\n",
|
DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
|
||||||
rtspc->rtp_channel, rtp_length);
|
rtspc->rtp_channel, rtp_length));
|
||||||
result = rtp_client_write(conn, &rtp[1], rtp_length + 3);
|
result = rtp_client_write(conn, &rtp[1], rtp_length + 3);
|
||||||
if(result) {
|
if(result) {
|
||||||
failf(data, "Got an error writing an RTP packet");
|
failf(data, "Got an error writing an RTP packet");
|
||||||
*done = TRUE;
|
*readmore = FALSE;
|
||||||
*readmore = FALSE;
|
Curl_safefree(rtspc->rtp_buf);
|
||||||
return result;
|
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 */
|
/* Move forward in the buffer */
|
||||||
rtp_dataleft -= rtp_length + 4;
|
rtp_dataleft -= rtp_length + 4;
|
||||||
rtp += 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(data->set.rtspreq == RTSPREQ_RECEIVE) {
|
||||||
/* If we are in a passive receive, give control back
|
/* If we are in a passive receive, give control back
|
||||||
* to the app as often as we can.
|
* to the app as often as we can.
|
||||||
*
|
|
||||||
* Otherwise, keep chugging along until we get RTSP data
|
|
||||||
*/
|
*/
|
||||||
k->keepon &= ~KEEP_RECV;
|
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] == '$') {
|
||||||
if(rtp_dataleft != 0 && rtp[0] == '$') {
|
DEBUGF(infof(data, "RTP Rewinding %zu %s\n", rtp_dataleft,
|
||||||
infof(data, "RTP Rewinding %zu %s %s\n", rtp_dataleft,
|
*readmore ? "(READMORE)" : ""));
|
||||||
*done ? "DONE " : "",
|
|
||||||
*readmore ? "READMORE" : "");
|
|
||||||
|
|
||||||
/* Store the incomplete RTP packet for a "rewind" */
|
/* Store the incomplete RTP packet for a "rewind" */
|
||||||
scratch = malloc(rtp_dataleft);
|
scratch = malloc(rtp_dataleft);
|
||||||
if(!scratch)
|
if(!scratch)
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
memcpy(scratch, rtp, rtp_dataleft);
|
memcpy(scratch, rtp, rtp_dataleft);
|
||||||
Curl_safefree(rtspc->rtp_buf);
|
Curl_safefree(rtspc->rtp_buf);
|
||||||
rtspc->rtp_buf = scratch;
|
rtspc->rtp_buf = scratch;
|
||||||
rtspc->rtp_bufsize = rtp_dataleft;
|
rtspc->rtp_bufsize = rtp_dataleft;
|
||||||
return CURLE_OK;
|
|
||||||
}
|
/* As far as the transfer is concerned, this data is consumed */
|
||||||
|
*nread = 0;
|
||||||
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* RTP followed by RTSP */
|
/* Fix up k->str to point just after the last RTP packet */
|
||||||
if(rtp_dataleft == 0) {
|
k->str += *nread - rtp_dataleft;
|
||||||
/* Need more */
|
|
||||||
*readmore = TRUE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* 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
|
/* either all of the data has been read or...
|
||||||
* it is somewhere in the merged data from k->str. */
|
* rtp now points at the next byte to parse
|
||||||
|
*/
|
||||||
|
if(rtp_dataleft > 0)
|
||||||
DEBUGASSERT(k->str[0] == rtp[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 */
|
/* 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_buf = NULL;
|
||||||
rtspc->rtp_bufsize = 0;
|
rtspc->rtp_bufsize = 0;
|
||||||
|
|
||||||
/* TODO CPC: Could implement parsing logic for Server->Client requests
|
|
||||||
here */
|
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,7 +685,7 @@ CURLcode Curl_rtsp_parseheader(struct connectdata *conn,
|
||||||
failf(data, "Unable to read the CSeq header: [%s]", header);
|
failf(data, "Unable to read the CSeq header: [%s]", header);
|
||||||
return CURLE_RTSP_CSEQ_ERROR;
|
return CURLE_RTSP_CSEQ_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(checkprefix("Session:", header)) {
|
else if(checkprefix("Session:", header)) {
|
||||||
char *start;
|
char *start;
|
||||||
|
|
||||||
|
|
10
lib/rtsp.h
10
lib/rtsp.h
|
@ -27,11 +27,17 @@
|
||||||
|
|
||||||
extern const struct Curl_handler Curl_handler_rtsp;
|
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,
|
CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
ssize_t *nread,
|
ssize_t *nread,
|
||||||
bool *readmore,
|
bool *readmore);
|
||||||
bool *done);
|
|
||||||
|
|
||||||
|
|
||||||
/* protocol-specific functions set up to be called by the main engine */
|
/* 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;
|
CURLcode result = CURLE_OK;
|
||||||
ssize_t nread; /* number of bytes read */
|
ssize_t nread; /* number of bytes read */
|
||||||
|
size_t excess = 0; /* excess bytes read */
|
||||||
bool is_empty_data = FALSE;
|
bool is_empty_data = FALSE;
|
||||||
|
#ifndef CURL_DISABLE_RTSP
|
||||||
|
bool readmore = FALSE; /* used by RTP to signal for more data */
|
||||||
|
#endif
|
||||||
|
|
||||||
*done = FALSE;
|
*done = FALSE;
|
||||||
|
|
||||||
|
@ -437,15 +441,13 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||||
k->str = k->buf;
|
k->str = k->buf;
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_RTSP
|
#ifndef CURL_DISABLE_RTSP
|
||||||
|
/* Check for RTP at the beginning of the data */
|
||||||
if(conn->protocol & PROT_RTSP) {
|
if(conn->protocol & PROT_RTSP) {
|
||||||
bool readmore = FALSE;
|
result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore);
|
||||||
result = Curl_rtsp_rtp_readwrite(data, conn, &nread, &readmore, done);
|
|
||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
if(readmore)
|
if(readmore)
|
||||||
break;
|
break;
|
||||||
if(*done)
|
|
||||||
return CURLE_OK;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -458,6 +460,18 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||||
result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
|
result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
|
||||||
if(result)
|
if(result)
|
||||||
return 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)
|
if(stop_reading)
|
||||||
/* We've stopped dealing with input, get out of the do-while loop */
|
/* We've stopped dealing with input, get out of the do-while loop */
|
||||||
break;
|
break;
|
||||||
|
@ -594,13 +608,19 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||||
}
|
}
|
||||||
#endif /* CURL_DISABLE_HTTP */
|
#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) &&
|
if((-1 != k->maxdownload) &&
|
||||||
(k->bytecount + nread >= 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)) {
|
if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
|
||||||
/* The 'excess' amount below can't be more than BUFSIZE which
|
/* The 'excess' amount below can't be more than BUFSIZE which
|
||||||
always will fit in a size_t */
|
always will fit in a size_t */
|
||||||
size_t excess = (size_t)(k->bytecount + nread - k->maxdownload);
|
|
||||||
if(excess > 0 && !k->ignorebody) {
|
if(excess > 0 && !k->ignorebody) {
|
||||||
infof(data,
|
infof(data,
|
||||||
"Rewinding stream by : %d"
|
"Rewinding stream by : %d"
|
||||||
|
@ -612,6 +632,15 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||||
read_rewind(conn, excess);
|
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);
|
nread = (ssize_t) (k->maxdownload - k->bytecount);
|
||||||
if(nread < 0 ) /* this should be unusual */
|
if(nread < 0 ) /* this should be unusual */
|
||||||
|
@ -630,9 +659,17 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||||
if(k->badheader && !k->ignorebody) {
|
if(k->badheader && !k->ignorebody) {
|
||||||
/* we parsed a piece of data wrongly assuming it was a header
|
/* we parsed a piece of data wrongly assuming it was a header
|
||||||
and now we output it as body instead */
|
and now we output it as body instead */
|
||||||
result = Curl_client_write(conn, CLIENTWRITE_BODY,
|
|
||||||
data->state.headerbuff,
|
/* Don't let excess data pollute body writes */
|
||||||
k->hbuflen);
|
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)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -694,6 +731,25 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||||
|
|
||||||
} /* if(! header and data to read ) */
|
} /* 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(is_empty_data) {
|
||||||
/* if we received nothing, the server closed the connection and we
|
/* if we received nothing, the server closed the connection and we
|
||||||
are done */
|
are done */
|
||||||
|
|
Loading…
Reference in New Issue