示例#1
0
void RP_ResetSession(RTSPSession *sess, GF_Err e)
{
	GF_RTSPCommand *com;
	u32 first = 1;

	//destroy command list
	while (gf_list_count(sess->rtsp_commands)) {
		com = (GF_RTSPCommand *)gf_list_get(sess->rtsp_commands, 0);
		gf_list_rem(sess->rtsp_commands, 0);
		//this destroys stacks if any
//		RP_SendFailure(sess, com, first ? e : GF_OK);
		gf_rtsp_command_del(com);
		first = 0;
	}
	/*reset session state*/	
	gf_rtsp_session_reset(sess->session, 1);
	sess->flags &= ~RTSP_WAIT_REPLY;
}
示例#2
0
void RP_ProcessCommands(RTSPSession *sess)
{
	GF_RTSPCommand *com;
	GF_Err e;
	u32 time;

	com = RP_GetCommand(sess);

	/*if asked or command to send, flushout TCP - TODO: check what's going on with ANNOUNCE*/
	if ((com && !(sess->flags & RTSP_WAIT_REPLY) ) || (sess->flags & RTSP_TCP_FLUSH) ) {
		while (1) {
			e = gf_rtsp_session_read(sess->session);
			if (e) break;
		}
		sess->flags &= ~RTSP_TCP_FLUSH;
	}

	/*handle response or announce*/
	if ( (com && (sess->flags & RTSP_WAIT_REPLY) ) /*|| (!com && sess->owner->handle_announce)*/) {
		e = gf_rtsp_get_response(sess->session, sess->rtsp_rsp);
		if (e!= GF_IP_NETWORK_EMPTY) {
			e = RP_ProcessResponse(sess, com, e);
			/*this is a service connect error -> plugin may be discarded */
			if (e!=GF_OK) {
				RP_RemoveCommand(sess);
				gf_rtsp_command_del(com);
				gf_term_on_connect(sess->owner->service, NULL, e);
				return;
			}

			RP_RemoveCommand(sess);
			gf_rtsp_command_del(com);
			sess->flags &= ~RTSP_WAIT_REPLY;
			sess->command_time = 0;
		} else {
			/*evaluate timeout*/
			time = gf_sys_clock() - sess->command_time;
			/*don't waste time waiting for teardown ACK, half a sec is enough. If server is not replying
			in time it is likely to never reply (happens with RTP over RTSP) -> kill session 
			and create new one*/
			if (!strcmp(com->method, GF_RTSP_TEARDOWN) && (time>=500) ) time = sess->owner->time_out;
			//signal what's going on
			if (time >= sess->owner->time_out) {
				if (!strcmp(com->method, GF_RTSP_TEARDOWN)) gf_rtsp_session_reset(sess->session, 1);

				RP_ProcessResponse(sess, com, GF_IP_NETWORK_FAILURE);
				RP_RemoveCommand(sess);
				gf_rtsp_command_del(com);
				sess->flags &= ~RTSP_WAIT_REPLY;
				sess->command_time = 0;
				gf_rtsp_reset_aggregation(sess->session);
			}
		}
		return;
	}

	if (!com) return;

	/*send command - check RTSP session state first*/
	switch (gf_rtsp_get_session_state(sess->session)) {
	case GF_RTSP_STATE_WAITING:
	case GF_RTSP_STATE_WAIT_FOR_CONTROL:
		return;
	case GF_RTSP_STATE_INVALIDATED:
		RP_SendFailure(sess, com, GF_IP_NETWORK_FAILURE);
		RP_RemoveCommand(sess);
		gf_rtsp_command_del(com);
		sess->flags &= ~RTSP_WAIT_REPLY;
		sess->command_time = 0;
		return;
	}
	/*process*/
	com->User_Agent = (char*)gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(sess->owner->service), "Downloader", "UserAgent");
	if (!com->User_Agent) com->User_Agent = "GPAC " GPAC_VERSION " RTSP Client";
	com->Accept_Language = RTSP_LANGUAGE;
	/*if no session assigned and a session ID is valid, use it*/
	if (sess->session_id && !com->Session)
		com->Session = sess->session_id;

	e = GF_OK;
	/*preprocess describe before sending (always the ESD url thing)*/
	if (!strcmp(com->method, GF_RTSP_DESCRIBE)) {
		com->Session = NULL;
		if (!RP_PreprocessDescribe(sess, com)) {
			e = GF_BAD_PARAM;
			goto exit;
		}
	}
	/*preprocess play/stop/pause before sending (aggregation)*/
	if (!strcmp(com->method, GF_RTSP_PLAY) 
		|| !strcmp(com->method, GF_RTSP_PAUSE)
		|| !strcmp(com->method, GF_RTSP_TEARDOWN)) {
		//command is skipped
		if (!RP_PreprocessUserCom(sess, com)) {
			e = GF_BAD_PARAM;
			goto exit;
		}
	}
	e = gf_rtsp_send_command(sess->session, com);
	if (e) {
		RP_SendFailure(sess, com, e);
		RP_ProcessResponse(sess, com, e);
	} else {
		sess->command_time = gf_sys_clock();
		sess->flags |= RTSP_WAIT_REPLY;
	}

exit:
	/*reset static strings*/
	com->User_Agent = NULL;
	com->Accept_Language = NULL;
	com->Session = NULL;
	/*remove command*/
	if (e) {
		RP_RemoveCommand(sess);
		gf_rtsp_command_del(com);
		sess->flags &= ~RTSP_WAIT_REPLY;
		sess->command_time = 0;
	}
}
示例#3
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;
}
示例#4
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;
}