blob: 726bc1b9e70d9becc6caa8f6a482edef516bd461 [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2000-2026
* All rights reserved
*
* This file is part of GPAC / IETF RTP/RTSP/SDP sub-project
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <gpac/internal/ietf_dev.h>
#ifndef GPAC_DISABLE_STREAMING
#include <gpac/token.h>
#include <gpac/base_coding.h>
GF_EXPORT
GF_RTSPCommand *gf_rtsp_command_new()
{
GF_RTSPCommand *tmp;
GF_SAFEALLOC(tmp, GF_RTSPCommand);
if (!tmp) return NULL;
tmp->Xtensions = gf_list_new();
tmp->Transports = gf_list_new();
return tmp;
}
#define COM_FREE_CLEAN(hdr) if (com->hdr) gf_free(com->hdr); \
com->hdr = NULL;
GF_EXPORT
void gf_rtsp_command_reset(GF_RTSPCommand *com)
{
if (!com) return;
//free all headers
COM_FREE_CLEAN(Accept);
COM_FREE_CLEAN(Accept_Encoding);
COM_FREE_CLEAN(Accept_Language);
COM_FREE_CLEAN(Authorization);
COM_FREE_CLEAN(Cache_Control);
COM_FREE_CLEAN(Conference);
COM_FREE_CLEAN(Connection);
COM_FREE_CLEAN(From);
COM_FREE_CLEAN(Proxy_Authorization);
COM_FREE_CLEAN(Proxy_Require);
COM_FREE_CLEAN(Referer);
COM_FREE_CLEAN(Session);
COM_FREE_CLEAN(User_Agent);
COM_FREE_CLEAN(body);
COM_FREE_CLEAN(service_name);
COM_FREE_CLEAN(ControlString);
COM_FREE_CLEAN(method);
//this is for server only, set to OK by default
com->StatusCode = NC_RTSP_OK;
com->user_data = NULL;
com->Bandwidth = com->Blocksize = com->Content_Length = com->CSeq = 0;
com->Scale = com->Speed = 0.0;
if (com->Range) gf_free(com->Range);
com->Range = NULL;
while (gf_list_count(com->Transports)) {
GF_RTSPTransport *trans = (GF_RTSPTransport *) gf_list_get(com->Transports, 0);
gf_list_rem(com->Transports, 0);
gf_rtsp_transport_del(trans);
}
while (gf_list_count(com->Xtensions)) {
GF_X_Attribute *att = (GF_X_Attribute*)gf_list_get(com->Xtensions, 0);
gf_list_rem(com->Xtensions, 0);
gf_free(att->Name);
gf_free(att->Value);
gf_free(att);
}
}
GF_EXPORT
void gf_rtsp_command_del(GF_RTSPCommand *com)
{
if (!com) return;
gf_rtsp_command_reset(com);
gf_list_del(com->Xtensions);
gf_list_del(com->Transports);
gf_free(com);
}
GF_Err RTSP_WriteCommand(GF_RTSPSession *sess, GF_RTSPCommand *com, unsigned char *req_buffer,
unsigned char **out_buffer, u32 *out_size)
{
u32 i, cur_pos, size, count;
char *buffer;
*out_buffer = NULL;
size = RTSP_WRITE_STEPALLOC;
buffer = (char *) gf_malloc(size);
cur_pos = 0;
//request
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, req_buffer);
//then all headers
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept", com->Accept);
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Encoding", com->Accept_Encoding);
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Language", com->Accept_Language);
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Authorization", com->Authorization);
if (com->Bandwidth) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Bandwidth: ");
RTSP_WRITE_INT(buffer, size, cur_pos, com->Bandwidth, 0);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
}
if (com->Blocksize) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Blocksize: ");
RTSP_WRITE_INT(buffer, size, cur_pos, com->Blocksize, 0);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
}
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Cache-Control", com->Cache_Control);
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Conference", com->Conference);
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Connection", com->Connection);
//if we have a body write the content length
if (com->body) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Content-Length: ");
RTSP_WRITE_INT(buffer, size, cur_pos, (u32) strlen(com->body), 0);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
}
//write the CSeq - use the SESSION CSeq
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "CSeq: ");
RTSP_WRITE_INT(buffer, size, cur_pos, sess->CSeq, 0);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
RTSP_WRITE_HEADER(buffer, size, cur_pos, "From", com->From);
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Authorization", com->Proxy_Authorization);
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Require", com->Proxy_Require);
//Range, only NPT
if (com->Range && !com->Range->UseSMPTE) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Range: npt=");
RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Range->start);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
if (com->Range->end > com->Range->start) {
RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Range->end);
}
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
}
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Referer", com->Referer);
if (com->Scale != 0.0) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: ");
RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Scale);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
}
RTSP_WRITE_HEADER(buffer, size, cur_pos, "Session", com->Session);
if (com->Speed != 0.0) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Speed: ");
RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Speed);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
}
//transport info
count = gf_list_count(com->Transports);
if (count) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Transport: ");
for (i=0; i<count; i++) {
GF_RTSPTransport *trans;
//line separator for headers
if (i) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n ,");
trans = (GF_RTSPTransport *) gf_list_get(com->Transports, i);
//then write the structure
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->Profile);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";unicast" : ";multicast"));
if (trans->destination) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";destination=");
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->destination);
}
if (trans->source) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";source=");
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->source);
}
if (trans->IsRecord) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";mode=RECORD");
if (trans->Append) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";append");
}
if (trans->IsInterleaved) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";interleaved=");
RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtpID, 0);
if (trans->rtcpID != trans->rtpID) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtcpID, 0);
}
}
if (trans->port_first) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";server_port=" : ";port="));
RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_first, 0);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_last, 0);
}
if (/*trans->IsUnicast && */trans->client_port_first) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";client_port=");
RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_first, 0);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_last, 0);
}
//multicast specific
if (!trans->IsUnicast) {
if (trans->MulticastLayers) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";layers=");
RTSP_WRITE_INT(buffer, size, cur_pos, trans->MulticastLayers, 0);
}
if (trans->TTL) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ttl=");
RTSP_WRITE_INT(buffer, size, cur_pos, trans->TTL, 0);
}
}
if (trans->SSRC) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ssrc=");
RTSP_WRITE_HEX(buffer, size, cur_pos, trans->SSRC, 0);
}
}
//done with transport
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
}
RTSP_WRITE_HEADER(buffer, size, cur_pos, "User-Agent", com->User_Agent);
//eXtensions
count = gf_list_count(com->Xtensions);
for (i=0; i<count; i++) {
GF_X_Attribute *att = (GF_X_Attribute *) gf_list_get(com->Xtensions, i);
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "x-");
RTSP_WRITE_HEADER(buffer, size, cur_pos, att->Name, att->Value);
}
//the end of header
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
//then body
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, com->body);
//the end of message ? to check, should not be needed...
// RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
*out_buffer = (unsigned char *)buffer;
*out_size = (u32) strlen(buffer);
return GF_OK;
}
//format a DESCRIBE, SETUP, PLAY or PAUSE on a session
//YOUR COMMAND MUST BE FORMATTED ACCORDINGLY
//sCtrl contains a control string if needed, formatting the REQUEST as server_url/service_name/sCtrl
GF_EXPORT
GF_Err gf_rtsp_send_command(GF_RTSPSession *sess, GF_RTSPCommand *com)
{
GF_Err e;
char *sCtrl;
const char *rad;
u32 size;
char buffer[1024], *result, *body;
if (!com || !com->method) return GF_BAD_PARAM;
sCtrl = com->ControlString;
//NB: OPTIONS is not sent this way
if (strcmp(com->method, GF_RTSP_DESCRIBE)
&& strcmp(com->method, GF_RTSP_ANNOUNCE)
&& strcmp(com->method, GF_RTSP_GET_PARAMETER)
&& strcmp(com->method, GF_RTSP_SET_PARAMETER)
&& strcmp(com->method, GF_RTSP_SETUP)
&& strcmp(com->method, GF_RTSP_PLAY)
&& strcmp(com->method, GF_RTSP_PAUSE)
&& strcmp(com->method, GF_RTSP_RECORD)
&& strcmp(com->method, GF_RTSP_REDIRECT)
&& strcmp(com->method, GF_RTSP_TEARDOWN)
&& strcmp(com->method, GF_RTSP_OPTIONS)
) return GF_BAD_PARAM;
//check the state machine
if (strcmp(com->method, GF_RTSP_PLAY)
&& strcmp(com->method, GF_RTSP_PAUSE)
&& strcmp(com->method, GF_RTSP_RECORD)
&& sess->RTSP_State != GF_RTSP_STATE_INIT)
return GF_SERVICE_ERROR;
//aggregation is ONLY for the same request - unclear in RFC2326 ...
//it is often mentioned "queued requests" at the server, like 3 PLAYS
//and a PAUSE ....
/*
else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL
&& strcmp(com->method, sess->RTSPLastRequest))
&& strcmp(com->method, GF_RTSP_OPTIONS))
return GF_BAD_PARAM;
*/
//OPTIONS must have a parameter string
if (!strcmp(com->method, GF_RTSP_OPTIONS) && !sCtrl) return GF_BAD_PARAM;
//update sequence number
if (!com->is_resend) {
sess->CSeq += 1;
sess->NbPending += 1;
com->is_resend = GF_FALSE;
}
if (!strcmp(com->method, GF_RTSP_OPTIONS)) {
snprintf(buffer, 1024, "OPTIONS %s %s\r\n", sCtrl, GF_RTSP_VERSION);
} else {
rad = (sess->ConnectionType == GF_SOCK_TYPE_TCP) ? "rtsp" : "rtspu";
if (sCtrl) {
char szServURL[1024];
sprintf(szServURL, "%s://%s:%d/%s", rad, sess->Server, sess->Port, sess->Service);
char *ctrl_url = gf_url_concatenate(szServURL, sCtrl);
snprintf(buffer, 1024, "%s %s %s\r\n", com->method, ctrl_url, GF_RTSP_VERSION);
gf_free(ctrl_url);
} else {
snprintf(buffer, 1024, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, GF_RTSP_VERSION);
}
}
//Body on ANNOUNCE, GET_PARAMETER, SET_PARAMETER ONLY
body = NULL;
if (strcmp(com->method, GF_RTSP_ANNOUNCE)
&& strcmp(com->method, GF_RTSP_GET_PARAMETER)
&& strcmp(com->method, GF_RTSP_SET_PARAMETER)
) {
//this is an error, but don't say anything
if (com->body) {
body = com->body;
com->body = NULL;
}
}
result = NULL;
e = RTSP_WriteCommand(sess, com, (unsigned char *)buffer, (unsigned char **) &result, &size);
//restore body if needed
if (body) com->body = body;
if (e) goto exit;
GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Sending Command:\n%s\n", result));
//send buffer
e = gf_rtsp_send_data(sess, result, size);
if (e)
goto exit;
//update our state
if (!strcmp(com->method, GF_RTSP_RECORD)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
else if (!strcmp(com->method, GF_RTSP_PLAY)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
else if (!strcmp(com->method, GF_RTSP_PAUSE)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
else sess->RTSP_State = GF_RTSP_STATE_WAITING;
//teardown invalidates the session most of the time, so we force the user to wait for the reply
//as the reply may indicate a connection-closed
gf_strcpy(sess->RTSPLastRequest, com->method);
exit:
if (result) gf_free(result);
return e;
}
void gf_rtsp_set_command_value(GF_RTSPCommand *com, char *Header, char *Value)
{
char LineBuffer[400];
s32 LinePos;
GF_RTSPTransport *trans;
GF_X_Attribute *x_Att;
if (!stricmp(Header, "Accept")) com->Accept = gf_strdup(Value);
else if (!stricmp(Header, "Accept-Encoding")) com->Accept_Encoding = gf_strdup(Value);
else if (!stricmp(Header, "Accept-Language")) com->Accept_Language = gf_strdup(Value);
else if (!stricmp(Header, "Authorization")) com->Authorization = gf_strdup(Value);
else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%u", &com->Bandwidth);
else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%u", &com->Blocksize);
else if (!stricmp(Header, "Cache-Control")) com->Cache_Control = gf_strdup(Value);
else if (!stricmp(Header, "Conference")) com->Conference = gf_strdup(Value);
else if (!stricmp(Header, "Connection")) com->Connection = gf_strdup(Value);
else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%u", &com->Content_Length);
else if (!stricmp(Header, "CSeq")) sscanf(Value, "%u", &com->CSeq);
else if (!stricmp(Header, "From")) com->From = gf_strdup(Value);
else if (!stricmp(Header, "Proxy_Authorization")) com->Proxy_Authorization = gf_strdup(Value);
else if (!stricmp(Header, "Proxy_Require")) com->Proxy_Require = gf_strdup(Value);
else if (!stricmp(Header, "Range")) com->Range = gf_rtsp_range_parse(Value);
else if (!stricmp(Header, "Referer")) com->Referer = gf_strdup(Value);
else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &com->Scale);
else if (!stricmp(Header, "Session"))
com->Session = gf_strdup(Value);
else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &com->Speed);
else if (!stricmp(Header, "User_Agent")) com->User_Agent = gf_strdup(Value);
//Transports
else if (!stricmp(Header, "Transport")) {
LinePos = 0;
while (1) {
LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400);
if (LinePos <= 0) return;
trans = gf_rtsp_transport_parse(Value);
if (trans) gf_list_add(com->Transports, trans);
}
}
//eXtensions attributes
else if (!strnicmp(Header, "x-", 2)) {
x_Att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute));
x_Att->Name = gf_strdup(Header+2);
x_Att->Value = NULL;
if (Value && strlen(Value)) x_Att->Value = gf_strdup(Value);
gf_list_add(com->Xtensions, x_Att);
}
//the rest is ignored
}
GF_Err RTSP_ParseCommandHeader(GF_RTSPSession *sess, GF_RTSPCommand *com, u32 BodyStart)
{
char LineBuffer[1024];
char ValBuf[1024];
char *buffer;
s32 Pos, ret;
u32 Size;
Size = sess->CurrentSize - sess->CurrentPos;
buffer = sess->tcp_buffer + sess->CurrentPos;
//by default the command is wrong ;)
com->StatusCode = NC_RTSP_Bad_Request;
//parse first line
ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024);
if (ret < 0) return GF_REMOTE_SERVICE_ERROR;
//method
Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 1024);
if (Pos <= 0) return GF_OK;
com->method = gf_strdup((const char *) ValBuf);
//URL
Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
if (Pos <= 0) return GF_OK;
com->service_name = gf_strdup(ValBuf);
//RTSP version
Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
if (Pos <= 0) return GF_OK;
if (strcmp(ValBuf, GF_RTSP_VERSION)) {
com->StatusCode = NC_RTSP_RTSP_Version_Not_Supported;
return GF_OK;
}
com->StatusCode = NC_RTSP_OK;
return gf_rtsp_parse_header(buffer + ret, Size - ret, BodyStart, com, NULL);
}
char *RTSP_DEFINED_METHODS[] =
{
GF_RTSP_DESCRIBE,
GF_RTSP_SETUP,
GF_RTSP_PLAY,
GF_RTSP_PAUSE,
GF_RTSP_RECORD,
GF_RTSP_TEARDOWN,
GF_RTSP_GET_PARAMETER,
GF_RTSP_SET_PARAMETER,
GF_RTSP_OPTIONS,
GF_RTSP_ANNOUNCE,
GF_RTSP_REDIRECT,
NULL
};
GF_EXPORT
GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com)
{
GF_Err e;
u32 BodyStart, size, com_buf_size;
if (!sess || !com) return GF_BAD_PARAM;
//reset the command
gf_rtsp_command_reset(com);
//if no connection, we have sent a "Connection: Close"
if (!sess->connection) return GF_IP_CONNECTION_CLOSED;
//lock
//fill TCP buffer
e = gf_rtsp_fill_buffer(sess);
if (e) goto exit;
//this is upcoming, interleaved data
if (sess->TCPChannels && sess->interleaved) {
u32 i=0;
Bool sync = GF_FALSE;
while (RTSP_DEFINED_METHODS[i]) {
if (!strncmp(sess->tcp_buffer+sess->CurrentPos, RTSP_DEFINED_METHODS[i], strlen(RTSP_DEFINED_METHODS[i]) ) ) {
sync = GF_TRUE;
break;
}
}
if (!sync) {
e = GF_IP_NETWORK_EMPTY;
goto exit;
}
}
e = gf_rtsp_read_reply(sess);
if (e) goto exit;
gf_rtsp_get_body_info(sess, &BodyStart, &size, GF_FALSE);
com_buf_size=0;
if (sess->tunnel_mode==RTSP_HTTP_SERVER) {
//no body start, this means no '=' at the end of the base64 string, assume we got the entire message
if (!BodyStart) BodyStart = sess->CurrentSize - sess->CurrentPos;
com_buf_size = BodyStart;
u32 dsize = gf_base64_decode(sess->tcp_buffer+sess->CurrentPos, BodyStart, sess->tcp_buffer+sess->CurrentPos, BodyStart);
sess->tcp_buffer[sess->CurrentPos + dsize] = 0;
gf_rtsp_get_body_info(sess, &BodyStart, &size, GF_TRUE);
}
GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got Command:\n%s\n", sess->tcp_buffer+sess->CurrentPos));
e = RTSP_ParseCommandHeader(sess, com, BodyStart);
//before returning an error we MUST reset the TCP buffer
//copy the body if any
if (!e && com->Content_Length) {
u32 rsp_size = sess->CurrentSize - sess->CurrentPos;
if (rsp_size < com->Content_Length) {
GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Invalid content length %u - Response was: \n%s\n", com->Content_Length, sess->tcp_buffer+sess->CurrentPos));
e = GF_NON_COMPLIANT_BITSTREAM;
goto exit;
}
com->body = (char *) gf_malloc(sizeof(char) * (com->Content_Length));
memcpy(com->body, sess->tcp_buffer+sess->CurrentPos + BodyStart, com->Content_Length);
}
//reset TCP buffer
if (!com_buf_size) com_buf_size = BodyStart + com->Content_Length;
sess->CurrentPos += com_buf_size;
if (!sess->CSeq && !sess->HTTP_Cookie //first request , cookie not set
&& (sess->tunnel_mode!=RTSP_HTTP_DISABLE) //not disabled
&& (!strncmp(sess->tcp_buffer, "GET ", 4) || !strncmp(sess->tcp_buffer, "POST ", 5))
) {
char *sess_cookie = strstr(sess->tcp_buffer, "x-sessioncookie");
if (sess_cookie) sess_cookie = strchr(sess_cookie, ':');
if (sess_cookie) {
while (sess_cookie[0] && strchr(" :", sess_cookie[0]))
sess_cookie++;
char *sep = strchr(sess_cookie, '\r');
if (sep) {
sep[0] = 0;
sess->HTTP_Cookie = gf_strdup(sess_cookie);
sep[0] = '\r';
if (!strncmp(sess->tcp_buffer, "GET ", 4)) {
char szCom[501];
snprintf(szCom, 500, "HTTP/1.0 200 OK\r\nServer: %s\r\nCache-Control: no-store\r\nPragma: no-cache\r\nContent-Type: application/x-rtsp-tunnelled\r\n\r\n", sess->Server);
szCom[500] = 0;
GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPTunnel] Send reply %s\n", szCom));
e = gf_rtsp_send_data(sess, szCom, (u32) strlen(szCom));
if (e && (e!= GF_IP_NETWORK_EMPTY)) return e;
return GF_IP_NETWORK_EMPTY;
}
//post
return GF_RTSP_TUNNEL_POST;
}
}
}
if (!com->CSeq)
com->StatusCode = NC_RTSP_Bad_Request;
if (e || (com->StatusCode != NC_RTSP_OK)) goto exit;
//NB: there is no "session state" in our lib when acting at the server side, as it depends
//on the server implementation. We cannot block responses / announcement to be sent
//dynamically, nor reset the session ourselves as we don't know the details of the session
//(eg TEARDOWN may keep resources up or not, ...)
//we also have the same pb for CSeq, as nothing forbids a server to buffer commands (and it
//happens during aggregation of PLAY/PAUSE with overlapping ranges)
//however store the last CSeq in case for client checking
if (!sess->CSeq) {
sess->CSeq = com->CSeq;
}
//check we're in the right range
else {
if (sess->CSeq >= com->CSeq)
com->StatusCode = NC_RTSP_Header_Field_Not_Valid;
else
sess->CSeq = com->CSeq;
}
//
//if a connection closed is signal, check this is the good session
// and reset it (the client is no longer connected)
if (sess->last_session_id && com->Session && !strcmp(com->Session, sess->last_session_id)
&& com->Connection && !stricmp(com->Connection, "Close")) {
gf_rtsp_session_reset(sess, GF_FALSE);
//destroy the socket
if (sess->connection) gf_sk_del(sess->connection);
sess->connection = NULL;
//destroy the http tunnel if any
if (sess->http) {
gf_sk_del(sess->http);
sess->http = NULL;
}
}
exit:
return e;
}
#endif /*GPAC_DISABLE_STREAMING*/