/* * OpenMRCP - Open Source Media Resource Control Protocol Stack * Copyright (C) 2007, Cepstral LLC * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * Author(s): * Arsen Chaloyan * * Contributor(s): * */ #include #include #include "mrcp_client_signaling_agent.h" #include "mrcp_client_proto_agent.h" #include "apt_task.h" #include "mrcp_parser.h" #include "rtsp_engine.h" typedef struct mrcp_client_rtsp_agent_t mrcp_client_rtsp_agent_t; struct mrcp_client_rtsp_agent_t { mrcp_signaling_agent_t *signaling_agent; mrcp_proto_agent_t proto_agent; unsigned char create_count; unsigned char open_count; rtsp_engine_event_handler_t handler; rtsp_engine_t *engine; apt_task_msg_pool_t *msg_pool; apr_pool_t *pool; }; typedef struct mrcp_client_rtsp_session_t mrcp_client_rtsp_session_t; struct mrcp_client_rtsp_session_t { mrcp_signaling_channel_t *channel; mrcp_connection_t *proto_connection; rtsp_session_t *session; su_home_t *home; }; typedef enum { MRCP_RTSP_EVENT_AGENT_STARTED, MRCP_RTSP_EVENT_AGENT_TERMINATED, MRCP_RTSP_EVENT_SESSION_CREATED, MRCP_RTSP_EVENT_SESSION_TERMINATED, MRCP_RTSP_EVENT_REQUEST_RECEIVED, MRCP_RTSP_EVENT_RESPONSE_RECEIVED } mrcp_rtsp_event_id; typedef struct mrcp_rtsp_event_t mrcp_rtsp_event_t; struct mrcp_rtsp_event_t { mrcp_rtsp_event_id event_id; mrcp_client_rtsp_agent_t *rtsp_agent; rtsp_session_t *session; rtsp_message_t *request; rtsp_message_t *response; }; static mrcp_status_t mrcp_rtsp_agent_destroy(mrcp_module_t *module); static mrcp_module_state_t mrcp_rtsp_agent_open(mrcp_module_t *module); static mrcp_module_state_t mrcp_rtsp_agent_close(mrcp_module_t *module); static mrcp_status_t mrcp_rtsp_agent_signal_handler(mrcp_module_t *module, apt_task_msg_t *msg); static const mrcp_module_method_set_t module_method_set = { mrcp_rtsp_agent_destroy, mrcp_rtsp_agent_open, mrcp_rtsp_agent_close, mrcp_rtsp_agent_signal_handler }; static mrcp_status_t mrcp_rtsp_resource_discover(mrcp_signaling_agent_t *agent); static mrcp_signaling_channel_t* mrcp_rtsp_session_create(mrcp_signaling_agent_t *agent, apr_pool_t *pool); static const mrcp_signaling_agent_method_set_t signaling_agent_method_set = { mrcp_rtsp_resource_discover, mrcp_rtsp_session_create, }; static mrcp_status_t mrcp_rtsp_session_offer(mrcp_signaling_channel_t *channel); static mrcp_status_t mrcp_rtsp_session_terminate(mrcp_signaling_channel_t *channel); static mrcp_status_t mrcp_rtsp_session_destroy(mrcp_signaling_channel_t *channel); static const mrcp_signaling_channel_method_set_t signaling_channel_method_set = { mrcp_rtsp_session_offer, NULL, /*answer*/ mrcp_rtsp_session_terminate, mrcp_rtsp_session_destroy }; static mrcp_connection_t* mrcp_rtsp_proto_connect(mrcp_proto_agent_t *proto_agent, mrcp_signaling_channel_t *channel, const char *remote_ip, unsigned short remote_port); static const mrcp_proto_agent_method_set_t proto_agent_method_set = { mrcp_rtsp_proto_connect, }; static mrcp_status_t mrcp_rtsp_proto_disconnect(mrcp_connection_t *proto_connection); static mrcp_status_t mrcp_rtsp_proto_message_send(mrcp_connection_t *proto_connection, mrcp_message_t *mrcp_message); static const mrcp_connection_method_set_t connection_method_set = { mrcp_rtsp_proto_disconnect, mrcp_rtsp_proto_message_send }; size_t rtsp_request_generate_by_mrcp_descriptor(rtsp_message_t *message, mrcp_descriptor_t *local_descriptor, mrcp_descriptor_t *remote_descriptor); mrcp_status_t mrcp_descriptor_generate_by_rtsp_response(mrcp_descriptor_t *descriptor, rtsp_message_t *response, rtsp_message_t *request, apr_pool_t *pool, su_home_t *home); static APR_INLINE mrcp_client_rtsp_agent_t* mrcp_rtsp_agent_get(mrcp_module_t *module) { return ((mrcp_signaling_agent_t*)module)->object; } static mrcp_signaling_channel_t* mrcp_rtsp_session_create(mrcp_signaling_agent_t *agent, apr_pool_t *pool) { mrcp_client_rtsp_agent_t *rtsp_agent = mrcp_client_signaling_agent_object_get(agent); mrcp_client_rtsp_session_t *rtsp_session = apr_palloc(pool,sizeof(mrcp_client_rtsp_session_t)); rtsp_session->home = su_home_new(sizeof(*rtsp_session->home)); rtsp_session->proto_connection = NULL; rtsp_session->channel = mrcp_client_signaling_channel_create(agent,&signaling_channel_method_set,rtsp_session,pool); rtsp_session->session = rtsp_engine_session_create(rtsp_agent->engine,NULL); rtsp_engine_session_object_set(rtsp_session->session,rtsp_session); return rtsp_session->channel; } static mrcp_status_t mrcp_rtsp_resource_discover(mrcp_signaling_agent_t *agent) { /* send rtsp describe */ return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_rtsp_session_offer(mrcp_signaling_channel_t *channel) { mrcp_client_rtsp_session_t *rtsp_session = channel->object; rtsp_message_t *request; request = rtsp_request_create(channel->pool); if(rtsp_request_generate_by_mrcp_descriptor(request,&channel->local_descriptor,&channel->remote_descriptor) > 0) { apt_log(APT_PRIO_INFO,"Local SDP\n[%s]\n", request->body); } return rtsp_engine_message_send(rtsp_session->session,request); } static mrcp_status_t mrcp_rtsp_session_terminate(mrcp_signaling_channel_t *channel) { mrcp_client_rtsp_session_t *rtsp_session = channel->object; rtsp_message_t *request; mrcp_media_control_t *control_media = NULL; size_t i; mrcp_descriptor_t *descriptor = &channel->local_descriptor; for(i=0; imedia_count; i++) { if(descriptor->media[i] && descriptor->media[i]->type == MRCP_MEDIA_TYPE_CONTROL && descriptor->media[i]->state == MRCP_MEDIA_ENABLED) { control_media = (mrcp_media_control_t *)descriptor->media[i]; request = rtsp_request_create(channel->pool); request->start_line.common.request_line.method_id = RTSP_METHOD_TEARDOWN; request->start_line.common.request_line.resource_name = control_media->resource_name; rtsp_engine_message_send(rtsp_session->session,request); } } if(!control_media) { rtsp_engine_session_terminate(rtsp_session->session); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_rtsp_session_destroy(mrcp_signaling_channel_t *channel) { mrcp_client_rtsp_session_t *rtsp_session = channel->object; if(!rtsp_session) { return MRCP_STATUS_FAILURE; } if(rtsp_session->home) { su_home_unref(rtsp_session->home); rtsp_session->home = NULL; } channel->object = NULL; rtsp_engine_session_destroy(rtsp_session->session); rtsp_session->session = NULL; return MRCP_STATUS_SUCCESS; } static mrcp_connection_t* mrcp_rtsp_proto_connect(mrcp_proto_agent_t *proto_agent, mrcp_signaling_channel_t *channel, const char *remote_ip, unsigned short remote_port) { mrcp_client_rtsp_session_t *rtsp_session = channel->object; if(!rtsp_session) { return NULL; } if(!rtsp_session->proto_connection) { rtsp_session->proto_connection = apr_palloc(rtsp_session->channel->pool,sizeof(mrcp_connection_t)); rtsp_session->proto_connection->agent = proto_agent; rtsp_session->proto_connection->method_set = &connection_method_set; rtsp_session->proto_connection->event_set = proto_agent->connection_event_set; rtsp_session->proto_connection->object = rtsp_session; } return rtsp_session->proto_connection; } static mrcp_status_t mrcp_rtsp_proto_disconnect(mrcp_connection_t *proto_connection) { /* nothing to do, no actual connection exists */ return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_rtsp_proto_message_send(mrcp_connection_t *proto_connection, mrcp_message_t *mrcp_message) { char buffer[MRCP_MESSAGE_MAX_SIZE]; apt_text_stream_t text_stream; size_t size = sizeof(buffer)-1; mrcp_client_rtsp_session_t *rtsp_session = proto_connection->object; mrcp_client_rtsp_agent_t *rtsp_agent = proto_connection->agent->object; rtsp_message_t *rtsp_message = NULL; if(!rtsp_agent || !rtsp_session || !rtsp_session->session) { return MRCP_STATUS_FAILURE; } if(!mrcp_message || mrcp_message->start_line.message_type != MRCP_MESSAGE_TYPE_REQUEST) { return MRCP_STATUS_FAILURE; } text_stream.buffer = buffer; text_stream.size = size; text_stream.pos = text_stream.buffer; mrcp_message->start_line.version = MRCP_VERSION_1; if(mrcp_message_generate(proto_connection->agent->resource_container,mrcp_message,&text_stream) != MRCP_STATUS_SUCCESS) { apt_log(APT_PRIO_WARNING,"Failed to Generate MRCPv1 Message\n"); return MRCP_STATUS_FAILURE; } *text_stream.pos = '\0'; apt_log(APT_PRIO_DEBUG,"Send MRCPv1 Message size=%lu\n%s\n",text_stream.size,text_stream.buffer); /* send rtsp announce */ rtsp_message = rtsp_request_create(rtsp_session->channel->pool); rtsp_message->start_line.common.request_line.method_id = RTSP_METHOD_ANNOUNCE; rtsp_message->start_line.common.request_line.resource_name = mrcp_message->channel_id.resource_name; rtsp_message->body = apr_pstrdup(rtsp_message->pool,text_stream.buffer); rtsp_message->header.content_type = RTSP_CONTENT_TYPE_MRCP; rtsp_header_property_add(&rtsp_message->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE); rtsp_message->header.content_length = text_stream.size; rtsp_header_property_add(&rtsp_message->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH); return rtsp_engine_message_send(rtsp_session->session,rtsp_message); } static mrcp_status_t mrcp_rtsp_process_session_terminate(mrcp_client_rtsp_agent_t *rtsp_agent, rtsp_session_t *session) { mrcp_client_rtsp_session_t *rtsp_session = rtsp_engine_session_object_get(session); if(rtsp_session) { rtsp_session->channel->event_set->on_terminate(rtsp_session->channel); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_rtsp_process_answer(mrcp_client_rtsp_agent_t *rtsp_agent, rtsp_session_t *session, rtsp_message_t *response, rtsp_message_t *request) { mrcp_client_rtsp_session_t *rtsp_session = rtsp_engine_session_object_get(session); mrcp_signaling_channel_t *channel = rtsp_session->channel; mrcp_descriptor_generate_by_rtsp_response( &channel->remote_descriptor, response, request, channel->pool, rtsp_session->home); channel->event_set->on_answer(channel); if(request->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { if(!mrcp_enabled_control_media_get(&rtsp_session->channel->remote_descriptor)) { rtsp_engine_session_terminate(rtsp_session->session); } } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_rtsp_process_message(mrcp_client_rtsp_agent_t *rtsp_agent, rtsp_session_t *session, rtsp_message_t *message, const char *resource_name) { if(message->header.content_type == RTSP_CONTENT_TYPE_MRCP) { mrcp_client_rtsp_session_t *rtsp_session = rtsp_engine_session_object_get(session); apt_text_stream_t text_stream; mrcp_message_t *mrcp_message; text_stream.buffer = message->body; text_stream.size = strlen(message->body); text_stream.pos = text_stream.buffer; apt_log(APT_PRIO_DEBUG,"Receive MRCPv1 Message size=%lu\n%s\n",text_stream.size,text_stream.buffer); mrcp_message = mrcp_message_create(rtsp_session->channel->pool); mrcp_message->channel_id.session_id.hex_str = message->header.session_id; mrcp_message->channel_id.session_id.length = strlen(message->header.session_id); mrcp_message->channel_id.resource_name = resource_name; if(mrcp_message_parse(rtsp_agent->proto_agent.resource_container,mrcp_message,&text_stream) != MRCP_STATUS_SUCCESS) { apt_log(APT_PRIO_WARNING,"Failed to Parse MRCPv1 Message\n"); return MRCP_STATUS_FAILURE; } rtsp_session->proto_connection->event_set->on_receive(rtsp_session->proto_connection,mrcp_message); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_rtsp_process_request(mrcp_client_rtsp_agent_t *rtsp_agent, rtsp_session_t *session, rtsp_message_t *message) { rtsp_message_t *response = NULL; if(message->start_line.message_type != RTSP_MESSAGE_TYPE_REQUEST) { return MRCP_STATUS_FAILURE; } apt_log(APT_PRIO_DEBUG,"Process RTSP Request [%s]\n",message->start_line.common.request_line.method_name); switch(message->start_line.common.request_line.method_id) { case RTSP_METHOD_TEARDOWN: //mrcp_rtsp_process_event(rtsp_agent,session,message); break; case RTSP_METHOD_ANNOUNCE: mrcp_rtsp_process_message(rtsp_agent,session,message,message->start_line.common.request_line.resource_name); break; default: { /* not supported message */ response = rtsp_response_create(message, RTSP_STATUS_CODE_METHOD_NOT_ALLOWED,RTSP_REASON_PHRASE_METHOD_NOT_ALLOWED,message->pool); } } if(!response) { response = rtsp_response_create(message,RTSP_STATUS_CODE_OK,RTSP_REASON_PHRASE_OK,message->pool); } rtsp_engine_message_send(session,response); return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_rtsp_process_response(mrcp_client_rtsp_agent_t *rtsp_agent, rtsp_session_t *session, rtsp_message_t *response, rtsp_message_t *request) { if(response->start_line.message_type != RTSP_MESSAGE_TYPE_RESPONSE) { return MRCP_STATUS_FAILURE; } apt_log(APT_PRIO_DEBUG,"Process RTSP Response\n"); if(rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) != MRCP_STATUS_SUCCESS || response->header.content_type == RTSP_CONTENT_TYPE_SDP) { return mrcp_rtsp_process_answer(rtsp_agent,session,response,request); } return mrcp_rtsp_process_message(rtsp_agent,session,response,request->start_line.common.request_line.resource_name); } static mrcp_status_t mrcp_rtsp_agent_signal_handler(mrcp_module_t *module, apt_task_msg_t *msg) { mrcp_client_rtsp_agent_t *rtsp_agent = mrcp_rtsp_agent_get(module); mrcp_rtsp_event_t *rtsp_event = (mrcp_rtsp_event_t*)msg->data; if(!rtsp_event) { return MRCP_STATUS_FAILURE; } switch(rtsp_event->event_id) { case MRCP_RTSP_EVENT_AGENT_STARTED: break; case MRCP_RTSP_EVENT_AGENT_TERMINATED: if(module->event_set->on_close) { module->event_set->on_close(module); } break; case MRCP_RTSP_EVENT_SESSION_CREATED: break; case MRCP_RTSP_EVENT_SESSION_TERMINATED: mrcp_rtsp_process_session_terminate(rtsp_agent,rtsp_event->session); break; case MRCP_RTSP_EVENT_REQUEST_RECEIVED: mrcp_rtsp_process_request(rtsp_agent,rtsp_event->session,rtsp_event->request); break; case MRCP_RTSP_EVENT_RESPONSE_RECEIVED: mrcp_rtsp_process_response(rtsp_agent,rtsp_event->session,rtsp_event->response,rtsp_event->request); break; } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_rtsp_task_msg_signal(mrcp_client_rtsp_agent_t *rtsp_agent, mrcp_rtsp_event_id event_id, rtsp_session_t *session, rtsp_message_t *request, rtsp_message_t *response) { apt_task_msg_t *task_msg; if(!rtsp_agent || !session) { return MRCP_STATUS_FAILURE; } task_msg = apt_task_msg_acquire(rtsp_agent->msg_pool); if(task_msg) { mrcp_rtsp_event_t *rtsp_event = (mrcp_rtsp_event_t*)task_msg->data; rtsp_event->event_id = event_id; rtsp_event->rtsp_agent = rtsp_agent; rtsp_event->session = session; rtsp_event->request = request; rtsp_event->response = response; task_msg->msg_pool = rtsp_agent->msg_pool; rtsp_agent->signaling_agent->module.signal( &rtsp_agent->signaling_agent->module, task_msg); } return MRCP_STATUS_SUCCESS; } static apt_bool_t mrcp_rtsp_on_session_create(rtsp_engine_event_handler_t *handler, rtsp_session_t *session) { return mrcp_rtsp_task_msg_signal(handler->object,MRCP_RTSP_EVENT_SESSION_CREATED,session,NULL,NULL); } static apt_bool_t mrcp_rtsp_on_session_terminate(rtsp_engine_event_handler_t *handler, rtsp_session_t *session) { return mrcp_rtsp_task_msg_signal(handler->object,MRCP_RTSP_EVENT_SESSION_TERMINATED,session,NULL,NULL); } static apt_bool_t mrcp_rtsp_on_request_receive(rtsp_engine_event_handler_t *handler, rtsp_session_t *session, rtsp_message_t *request) { return mrcp_rtsp_task_msg_signal(handler->object,MRCP_RTSP_EVENT_REQUEST_RECEIVED,session,request,NULL); } static apt_bool_t mrcp_rtsp_on_response_receive(rtsp_engine_event_handler_t *handler, rtsp_session_t *session, rtsp_message_t *response, rtsp_message_t *request) { return mrcp_rtsp_task_msg_signal(handler->object,MRCP_RTSP_EVENT_RESPONSE_RECEIVED,session,request,response); } static mrcp_module_state_t mrcp_rtsp_agent_open(mrcp_module_t *module) { mrcp_client_rtsp_agent_t *rtsp_agent = mrcp_rtsp_agent_get(module); if(rtsp_agent->open_count++ != 0) { return MODULE_STATE_OPENED; } apt_log(APT_PRIO_INFO,"Open RTSP Agent\n"); rtsp_agent->handler.object = rtsp_agent; rtsp_agent->handler.on_session_create = mrcp_rtsp_on_session_create; rtsp_agent->handler.on_session_terminate = mrcp_rtsp_on_session_terminate; rtsp_agent->handler.on_request_receive = mrcp_rtsp_on_request_receive; rtsp_agent->handler.on_response_receive = mrcp_rtsp_on_response_receive; rtsp_agent->engine = rtsp_client_engine_start(&rtsp_agent->handler, rtsp_agent->signaling_agent->server_ip, rtsp_agent->signaling_agent->server_sip_port, rtsp_agent->signaling_agent->resource_location); return MODULE_STATE_OPEN_INPROGRESS; } static mrcp_module_state_t mrcp_rtsp_agent_close(mrcp_module_t *module) { mrcp_client_rtsp_agent_t *rtsp_agent = mrcp_rtsp_agent_get(module); if(--rtsp_agent->open_count > 0) { return MODULE_STATE_CLOSED; } apt_log(APT_PRIO_INFO,"Close RTSP Agent\n"); rtsp_engine_shutdown(rtsp_agent->engine); return MODULE_STATE_CLOSED; } static void mrcp_rtsp_agent_config(mrcp_client_rtsp_agent_t *rtsp_agent, const char *client_ip, unsigned short client_port, const char *server_ip, unsigned short server_port, const char *resource_location) { if(client_ip) { rtsp_agent->signaling_agent->client_ip = apr_pstrdup(rtsp_agent->pool,client_ip); } if(server_ip) { rtsp_agent->signaling_agent->server_ip = apr_pstrdup(rtsp_agent->pool,server_ip); } rtsp_agent->signaling_agent->client_sip_port = client_port; rtsp_agent->signaling_agent->server_sip_port = server_port; if(resource_location) { rtsp_agent->signaling_agent->resource_location = apr_pstrdup(rtsp_agent->pool,resource_location); } } static mrcp_status_t mrcp_rtsp_agent_destroy(mrcp_module_t *module) { mrcp_client_rtsp_agent_t *rtsp_agent = mrcp_rtsp_agent_get(module); if(--rtsp_agent->create_count > 0) { return MRCP_STATUS_SUCCESS; } apt_log(APT_PRIO_NOTICE,"Destroy RTSP Agent\n"); apt_task_msg_pool_destroy(rtsp_agent->msg_pool); rtsp_agent->pool = NULL; return MRCP_STATUS_SUCCESS; } mrcp_signaling_agent_t* mrcp_client_rtsp_agent_create(const char *client_ip, unsigned short client_port, const char *server_ip, unsigned short server_port, const char *resource_location, apr_pool_t *pool) { mrcp_client_rtsp_agent_t *rtsp_agent = apr_palloc(pool,sizeof(mrcp_client_rtsp_agent_t)); mrcp_signaling_agent_t *agent = mrcp_client_signaling_agent_create( &signaling_agent_method_set, &module_method_set, rtsp_agent, pool); apt_log(APT_PRIO_NOTICE,"Create RTSP Client Agent %s:%hu -> %s:%hu\n",client_ip,client_port,server_ip,server_port); rtsp_agent->pool = pool; rtsp_agent->signaling_agent = agent; rtsp_agent->engine = NULL; mrcp_rtsp_agent_config(rtsp_agent,client_ip,client_port,server_ip,server_port,resource_location); rtsp_agent->create_count = 1; rtsp_agent->open_count = 0; rtsp_agent->msg_pool = apt_task_msg_pool_create_waitable_static(sizeof(mrcp_rtsp_event_t),pool); return agent; } mrcp_proto_agent_t* mrcp_client_rtsp_proto_agent_create(mrcp_signaling_agent_t *signaling_agent) { mrcp_client_rtsp_agent_t *rtsp_agent; if(!signaling_agent || !signaling_agent->object) { return NULL; } rtsp_agent = signaling_agent->object; rtsp_agent->proto_agent.object = rtsp_agent; rtsp_agent->proto_agent.agent_method_set = &proto_agent_method_set; mrcp_module_init(&rtsp_agent->proto_agent.module,&module_method_set); rtsp_agent->create_count++; return &rtsp_agent->proto_agent; }