Пример #1
0
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;

	//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
	gf_mx_p(sess->mx);

	//fill TCP buffer
	e = gf_rtsp_fill_buffer(sess);
	if (e) goto exit;
	//this is upcoming, interleaved data
	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);
	//before returning an error we MUST reset the TCP buffer

	//copy the body if any
	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);
	}	
	//reset TCP buffer
	sess->CurrentPos += BodyStart + com->Content_Length;

	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, 0);
		//destroy the socket
		if (sess->connection) gf_sk_del(sess->connection);
		sess->connection = NULL;
		
		//destroy the http tunnel if any
		if (sess->HasTunnel && sess->http) {
			gf_sk_del(sess->http);
			sess->http = NULL;
		}
	}	

exit:
	gf_mx_v(sess->mx);
	return e;
}
Пример #2
0
GF_EXPORT
GF_Err gf_rtsp_get_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp)
{
	GF_Err e;
	Bool force_reset = GF_FALSE;
	u32 BodyStart, size;

	if (!sess || !rsp) return GF_BAD_PARAM;
	gf_rtsp_response_reset(rsp);


	//LOCK
	gf_mx_p(sess->mx);


	e = gf_rtsp_check_connection(sess);
	if (e)
		goto exit;

	//push data in our queue
	e = gf_rtsp_fill_buffer(sess);
	if (e)
		goto exit;

	//this is interleaved data
	if (!IsRTSPMessage(sess->TCPBuffer+sess->CurrentPos) ) {
		gf_rtsp_session_read(sess);
		e = GF_IP_NETWORK_EMPTY;
		goto exit;
	}
	e = gf_rtsp_read_reply(sess);
	if (e)
		goto exit;

	//get the reply
	gf_rtsp_get_body_info(sess, &BodyStart, &size);
	e = RTSP_ParseResponseHeader(sess, rsp, BodyStart);

	//copy the body if any
	if (!e && rsp->Content_Length) {
		rsp->body = (char *)gf_malloc(sizeof(char) * (rsp->Content_Length));
		memcpy(rsp->body, sess->TCPBuffer+sess->CurrentPos + BodyStart, rsp->Content_Length);
	}

	GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got Response:\n%s\n", sess->TCPBuffer+sess->CurrentPos));

	//reset TCP buffer
	sess->CurrentPos += BodyStart + rsp->Content_Length;

	if (e) goto exit;

	//update RTSP aggreagation info
	if (sess->NbPending) sess->NbPending -= 1;

	if (sess->RTSP_State == GF_RTSP_STATE_WAITING) sess->RTSP_State = GF_RTSP_STATE_INIT;
	//control, and everything is received
	else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL) {
		if (!sess->NbPending) sess->RTSP_State = GF_RTSP_STATE_INIT;
	}
	//this is a late reply to an aggregated control - signal nothing
	if (!strcmp(sess->RTSPLastRequest, "RESET") && sess->CSeq > rsp->CSeq) {
		e = GF_IP_NETWORK_EMPTY;
		goto exit;
	}

	//reset last request
	if (sess->RTSP_State == GF_RTSP_STATE_INIT) strcpy(sess->RTSPLastRequest, "");

	//check the CSeq is in the right range. The server should ALWAYS reply in sequence
	//to an aggreagated sequence of requests
	//if we have reseted the connection (due to an APP error) return empty
	if (rsp->CSeq && sess->CSeq > rsp->CSeq + sess->NbPending) {
		gf_mx_v(sess->mx);
		return gf_rtsp_get_response(sess, rsp);
	}

	if (sess->CSeq != rsp->CSeq + sess->NbPending) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Invalid sequence number - got %d but expected %d\n", sess->CSeq, rsp->CSeq + sess->NbPending));
		e = GF_REMOTE_SERVICE_ERROR;
		goto exit;
	}

	/*check session ID*/
	if (rsp->Session && sess->last_session_id && strcmp(sess->last_session_id, rsp->Session)) {
		e = GF_REMOTE_SERVICE_ERROR;
		goto exit;
	}

	//destroy sessionID if needed - real doesn't close the connection when destroying
	//session
	if (!strcmp(sess->RTSPLastRequest, GF_RTSP_TEARDOWN)) {
		sess->last_session_id = NULL;
	}

exit:
	if (rsp->Connection && !stricmp(rsp->Connection, "Close"))
		force_reset = GF_TRUE;
	else if (e && (e != GF_IP_NETWORK_EMPTY))
		force_reset = GF_TRUE;

	if (force_reset) {
		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->HasTunnel && sess->http) {
			gf_sk_del(sess->http);
			sess->http = NULL;
		}
	}

	gf_mx_v(sess->mx);
	return e;
}