This source file includes following definitions.
- gf_rtsp_command_new
- gf_rtsp_command_reset
- gf_rtsp_command_del
- RTSP_WriteCommand
- gf_rtsp_send_command
- gf_rtsp_set_command_value
- RTSP_ParseCommandHeader
- gf_rtsp_get_command
#include <gpac/internal/ietf_dev.h>
#ifndef GPAC_DISABLE_STREAMING
#include <gpac/token.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)
{
GF_RTSPTransport *trans;
GF_X_Attribute *att;
if (!com) return;
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);
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)) {
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)) {
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, temp[50];
GF_RTSPTransport *trans;
GF_X_Attribute *att;
*out_buffer = NULL;
size = RTSP_WRITE_STEPALLOC;
buffer = (char *) gf_malloc(size);
cur_pos = 0;
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, req_buffer);
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 (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");
}
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);
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");
}
count = gf_list_count(com->Transports);
if (count) {
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Transport: ");
for (i=0; i<count; i++) {
if (i) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n ,");
trans = (GF_RTSPTransport *) gf_list_get(com->Transports, i);
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->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);
}
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_INT(buffer, size, cur_pos, trans->SSRC, 0);
}
}
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
}
RTSP_WRITE_HEADER(buffer, size, cur_pos, "User-Agent", com->User_Agent);
count = gf_list_count(com->Xtensions);
for (i=0; i<count; i++) {
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);
}
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, com->body);
*out_buffer = (unsigned char *)buffer;
*out_size = (u32) strlen(buffer);
return GF_OK;
}
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;
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_REDIRECTE)
&& strcmp(com->method, GF_RTSP_TEARDOWN)
&& strcmp(com->method, GF_RTSP_OPTIONS)
) return GF_BAD_PARAM;
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;
if (!strcmp(com->method, GF_RTSP_OPTIONS) && !sCtrl) return GF_BAD_PARAM;
sess->CSeq += 1;
sess->NbPending += 1;
if (!strcmp(com->method, GF_RTSP_OPTIONS)) {
sprintf(buffer, "OPTIONS %s %s\r\n", sCtrl, GF_RTSP_VERSION);
} else {
rad = (sess->ConnectionType == GF_SOCK_TYPE_TCP) ? "rtsp" : "rtspu";
if (sCtrl) {
if (strstr(sCtrl, sess->Server) && strstr(sCtrl, sess->Service)) {
sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, GF_RTSP_VERSION);
}
else if (strstr(sCtrl, sess->Service)) {
sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sCtrl, GF_RTSP_VERSION);
}
else if (!strnicmp(sCtrl, "rtsp", 4)) {
sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, GF_RTSP_VERSION);
}
else {
sprintf(buffer, "%s %s://%s/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Service, sCtrl, GF_RTSP_VERSION);
}
} else {
sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, GF_RTSP_VERSION);
}
}
body = NULL;
if (strcmp(com->method, GF_RTSP_ANNOUNCE)
&& strcmp(com->method, GF_RTSP_GET_PARAMETER)
&& strcmp(com->method, GF_RTSP_SET_PARAMETER)
) {
if (com->body) {
body = com->body;
com->body = NULL;
}
}
result = NULL;
e = RTSP_WriteCommand(sess, com, (unsigned char *)buffer, (unsigned char **) &result, &size);
if (body) com->body = body;
if (e) goto exit;
GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Sending Command:\n%s\n", result));
e = gf_rtsp_send_data(sess, result, size);
if (e) goto exit;
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;
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);
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);
}
}
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);
}
}
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->TCPBuffer + sess->CurrentPos;
com->StatusCode = NC_RTSP_Bad_Request;
ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024);
if (ret < 0) return GF_REMOTE_SERVICE_ERROR;
Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 1024);
if (Pos <= 0) return GF_OK;
com->method = gf_strdup((const char *) ValBuf);
Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
if (Pos <= 0) return GF_OK;
com->service_name = gf_strdup(ValBuf);
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);
}
GF_EXPORT
GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com)
{
GF_Err e;
u32 BodyStart, size;
if (!sess || !com) return GF_BAD_PARAM;
gf_rtsp_command_reset(com);
if (!sess->connection) return GF_IP_CONNECTION_CLOSED;
gf_mx_p(sess->mx);
e = gf_rtsp_fill_buffer(sess);
if (e) goto exit;
if (strncmp(sess->TCPBuffer+sess->CurrentPos, "RTSP", 4)) {
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);
e = RTSP_ParseCommandHeader(sess, com, BodyStart);
if (!e && com->Content_Length) {
com->body = (char *) gf_malloc(sizeof(char) * (com->Content_Length));
memcpy(com->body, sess->TCPBuffer+sess->CurrentPos + BodyStart, com->Content_Length);
}
sess->CurrentPos += BodyStart + com->Content_Length;
if (!com->CSeq) com->StatusCode = NC_RTSP_Bad_Request;
if (e || (com->StatusCode != NC_RTSP_OK)) goto exit;
if (!sess->CSeq) {
sess->CSeq = com->CSeq;
}
else {
if (sess->CSeq >= com->CSeq)
com->StatusCode = NC_RTSP_Header_Field_Not_Valid;
else
sess->CSeq = com->CSeq;
}
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);
if (sess->connection) gf_sk_del(sess->connection);
sess->connection = NULL;
if (sess->HasTunnel && sess->http) {
gf_sk_del(sess->http);
sess->http = NULL;
}
}
exit:
gf_mx_v(sess->mx);
return e;
}
#endif