/* * 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 "mrcp_media_agent.h" #include "media_processor.h" #include "codec_manager.h" #include "rtp_session.h" typedef struct mrcp_live_media_agent_t mrcp_live_media_agent_t; struct mrcp_live_media_agent_t { mrcp_media_agent_t *media_agent; media_processor_t *media_processor; apr_pool_t *pool; }; typedef struct mrcp_live_audio_channel_t mrcp_live_audio_channel_t; struct mrcp_live_audio_channel_t { mrcp_audio_channel_t *channel; rtp_session_t *rtp_session; media_context_t *rx_media_context; media_context_t *tx_media_context; mrcp_audio_direction_t active_direction; apr_pool_t *pool; }; typedef enum { MRCP_LIVE_EVENT_AGENT_PROCESS, MRCP_LIVE_EVENT_AGENT_STARTED, MRCP_LIVE_EVENT_AGENT_TERMINATED } mrcp_live_event_id; typedef struct mrcp_live_event_t mrcp_live_event_t; struct mrcp_live_event_t { mrcp_live_event_id event_id; }; static mrcp_status_t mrcp_live_agent_destroy(mrcp_module_t *module); static mrcp_module_state_t mrcp_live_agent_open(mrcp_module_t *module); static mrcp_module_state_t mrcp_live_agent_close(mrcp_module_t *module); static mrcp_status_t mrcp_live_agent_signal_handler(mrcp_module_t *module, apt_task_msg_t *msg); static const mrcp_module_method_set_t module_method_set = { mrcp_live_agent_destroy, mrcp_live_agent_open, mrcp_live_agent_close, mrcp_live_agent_signal_handler }; static mrcp_status_t mrcp_live_agent_channel_create(mrcp_media_agent_t *agent, mrcp_audio_channel_t *channel, apr_pool_t *pool); static const mrcp_media_agent_method_set_t media_agent_method_set = { mrcp_live_agent_channel_create, }; static mrcp_status_t mrcp_live_audio_channel_destroy(mrcp_audio_channel_t *channel); static mrcp_status_t mrcp_live_audio_channel_modify(mrcp_audio_channel_t *channel, mrcp_audio_request_t request); static mrcp_status_t mrcp_live_audio_channel_source_connect(mrcp_audio_channel_t *channel, audio_source_t *source); static mrcp_status_t mrcp_live_audio_channel_source_disconnect(mrcp_audio_channel_t *channel, audio_source_t *source); static mrcp_status_t mrcp_live_audio_channel_sink_connect(mrcp_audio_channel_t *channel, audio_sink_t *sink); static mrcp_status_t mrcp_live_audio_channel_sink_disconnect(mrcp_audio_channel_t *channel, audio_sink_t *sink); static const mrcp_audio_channel_method_set_t audio_channel_method_set = { mrcp_live_audio_channel_destroy, mrcp_live_audio_channel_modify, mrcp_live_audio_channel_source_connect, mrcp_live_audio_channel_source_disconnect, mrcp_live_audio_channel_sink_connect, mrcp_live_audio_channel_sink_disconnect, }; static APR_INLINE mrcp_live_media_agent_t* mrcp_live_media_agent_get(mrcp_module_t *module) { return ((mrcp_media_agent_t*)module)->object; } mrcp_media_agent_t* mrcp_live_media_agent_create(const char *ip, unsigned short rtp_port_min, unsigned short rtp_port_max, apr_pool_t *pool) { mrcp_live_media_agent_t *live_agent = apr_palloc(pool,sizeof(mrcp_live_media_agent_t)); live_agent->pool = pool; live_agent->media_processor = media_processor_create(pool); if(!live_agent->media_processor) { return NULL; } live_agent->media_agent = mrcp_media_agent_create( &media_agent_method_set, &module_method_set, live_agent, pool); apt_log(APT_PRIO_NOTICE,"Create Media Agent %s:[%hu,%hu]\n",ip,rtp_port_min,rtp_port_max); if(ip) { live_agent->media_agent->ip = apr_pstrdup(pool,ip); } live_agent->media_agent->rtp_port_min = rtp_port_min; live_agent->media_agent->rtp_port_max = rtp_port_max; live_agent->media_agent->rtp_port_cur = rtp_port_min; return live_agent->media_agent; } static mrcp_status_t mrcp_live_agent_destroy(mrcp_module_t *module) { mrcp_live_media_agent_t *live_agent = mrcp_live_media_agent_get(module); media_processor_destroy(live_agent->media_processor); apt_log(APT_PRIO_NOTICE,"Destroy Media Agent\n"); live_agent->pool = NULL; return MRCP_STATUS_SUCCESS; } static mrcp_module_state_t mrcp_live_agent_open(mrcp_module_t *module) { mrcp_live_media_agent_t *live_agent = mrcp_live_media_agent_get(module); media_processor_start(live_agent->media_processor); return MODULE_STATE_OPENED; } static mrcp_module_state_t mrcp_live_agent_close(mrcp_module_t *module) { mrcp_live_media_agent_t *live_agent = mrcp_live_media_agent_get(module); media_processor_terminate(live_agent->media_processor); return MODULE_STATE_CLOSED; } static APR_INLINE mrcp_live_media_agent_t* mrcp_live_agent_get(mrcp_audio_channel_t *channel) { if(channel && channel->agent) { return channel->agent->object; } return NULL; } static mrcp_status_t mrcp_live_audio_channel_open_tx(mrcp_live_audio_channel_t *live_channel) { codec_descriptor_t *codec_descriptor; mrcp_audio_channel_t *audio_channel = live_channel->channel; mrcp_live_media_agent_t *live_agent = mrcp_live_agent_get(audio_channel); codec_manager_t *codec_manager = media_processor_codec_manager_get(live_agent->media_processor); if(live_channel->active_direction & MRCP_AUDIO_SEND) { /* already opened */ return MRCP_STATUS_SUCCESS; } apt_log(APT_PRIO_INFO,"Open RTP Transmit %s:%hu -> %s:%hu\n", audio_channel->local_media->base.ip, audio_channel->local_media->base.port, audio_channel->remote_media->base.ip, audio_channel->remote_media->base.port); if(!audio_channel->local_media->codec_count) { return MRCP_STATUS_FAILURE; } codec_descriptor = audio_channel->local_media->codec_list[0]; if(audio_channel->mode != MRCP_AUDIO_CHANNEL_NONE) { if(!audio_channel->audio_sink) { const codec_manipulator_t *codec_manipulator; codec_manipulator = codec_manager_codec_get(codec_manager,codec_descriptor); audio_channel->audio_sink = rtp_session_tx_stream_create( live_channel->rtp_session, audio_channel->remote_media->base.ip, audio_channel->remote_media->base.port, codec_descriptor, codec_manipulator, audio_channel->remote_media->ptime); } } if(audio_channel->mode == MRCP_AUDIO_CHANNEL_PASSIVE) { if(!live_channel->tx_media_context) { live_channel->tx_media_context = media_connector_create( codec_descriptor->sampling_rate, live_channel->pool); media_processor_context_add( live_agent->media_processor, live_channel->tx_media_context, MEDIA_REQUEST_TYPE_ASYNC); } } if(live_channel->tx_media_context && audio_channel->audio_sink) { media_context_sink_add( live_agent->media_processor, live_channel->tx_media_context, audio_channel->audio_sink, MEDIA_REQUEST_TYPE_ASYNC); } live_channel->active_direction |= MRCP_AUDIO_SEND; return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_close_tx(mrcp_live_audio_channel_t *live_channel) { mrcp_audio_channel_t *audio_channel = live_channel->channel; if((live_channel->active_direction & MRCP_AUDIO_SEND) == 0) { /* already closed */ return MRCP_STATUS_SUCCESS; } apt_log(APT_PRIO_INFO,"Close RTP Transmit %s:%hu -> %s:%hu\n", audio_channel->local_media->base.ip, audio_channel->local_media->base.port, audio_channel->remote_media->base.ip, audio_channel->remote_media->base.port); if(live_channel->tx_media_context && audio_channel->audio_sink) { mrcp_live_media_agent_t *live_agent = mrcp_live_agent_get(audio_channel); media_context_sink_remove( live_agent->media_processor, live_channel->tx_media_context, audio_channel->audio_sink, MEDIA_REQUEST_TYPE_ASYNC); } live_channel->active_direction &= ~MRCP_AUDIO_SEND; return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_open_rx(mrcp_live_audio_channel_t *live_channel) { codec_descriptor_t *codec_descriptor; mrcp_audio_channel_t *audio_channel = live_channel->channel; mrcp_live_media_agent_t *live_agent = mrcp_live_agent_get(audio_channel); codec_manager_t *codec_manager = media_processor_codec_manager_get(live_agent->media_processor); if(live_channel->active_direction & MRCP_AUDIO_RECEIVE) { /* already opened */ return MRCP_STATUS_SUCCESS; } apt_log(APT_PRIO_INFO,"Open RTP Receive %s:%hu <- %s:%hu\n", audio_channel->local_media->base.ip, audio_channel->local_media->base.port, audio_channel->remote_media->base.ip, audio_channel->remote_media->base.port); if(!audio_channel->local_media->codec_count) { return MRCP_STATUS_FAILURE; } codec_descriptor = audio_channel->local_media->codec_list[0]; if(audio_channel->mode != MRCP_AUDIO_CHANNEL_NONE) { if(!audio_channel->audio_source) { const codec_manipulator_t *codec_manipulator; codec_manipulator = codec_manager_codec_get(codec_manager,codec_descriptor); audio_channel->audio_source = rtp_session_rx_stream_create( live_channel->rtp_session, audio_channel->remote_media->base.ip, audio_channel->remote_media->base.port, codec_descriptor, codec_manipulator); } } if(audio_channel->mode == MRCP_AUDIO_CHANNEL_PASSIVE) { if(!live_channel->rx_media_context) { live_channel->rx_media_context = media_connector_create( codec_descriptor->sampling_rate, live_channel->pool); media_processor_context_add( live_agent->media_processor, live_channel->rx_media_context, MEDIA_REQUEST_TYPE_ASYNC); } } if(live_channel->rx_media_context && audio_channel->audio_source) { media_context_source_add( live_agent->media_processor, live_channel->rx_media_context, audio_channel->audio_source, MEDIA_REQUEST_TYPE_ASYNC); } live_channel->active_direction |= MRCP_AUDIO_RECEIVE; return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_close_rx(mrcp_live_audio_channel_t *live_channel) { mrcp_audio_channel_t *audio_channel = live_channel->channel; if((live_channel->active_direction & MRCP_AUDIO_RECEIVE) == 0) { /* already closed */ return MRCP_STATUS_SUCCESS; } apt_log(APT_PRIO_INFO,"Close RTP Receive %s:%hu <- %s:%hu\n", audio_channel->local_media->base.ip, audio_channel->local_media->base.port, audio_channel->remote_media->base.ip, audio_channel->remote_media->base.port); if(live_channel->rx_media_context && audio_channel->audio_source) { mrcp_live_media_agent_t *live_agent = mrcp_live_agent_get(audio_channel); media_context_source_remove( live_agent->media_processor, live_channel->rx_media_context, audio_channel->audio_source, MEDIA_REQUEST_TYPE_ASYNC); } live_channel->active_direction &= ~MRCP_AUDIO_RECEIVE; return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_on_answer(mrcp_audio_channel_t *channel) { if(channel->local_media->base.state != channel->remote_media->base.state) { if(channel->local_media->base.state == MRCP_MEDIA_ENABLED) { channel->local_media->base.state = MRCP_MEDIA_DISABLED; } } if(channel->local_media->base.state == MRCP_MEDIA_ENABLED) { if(channel->local_media->direction & MRCP_AUDIO_SEND) { if(channel->remote_media->direction & MRCP_AUDIO_RECEIVE) { /* check/create tx context */ /* open tx */ mrcp_live_audio_channel_open_tx(channel->object); } else { /* close tx */ mrcp_live_audio_channel_close_tx(channel->object); } } else { /* close tx */ mrcp_live_audio_channel_close_tx(channel->object); } if(channel->local_media->direction & MRCP_AUDIO_RECEIVE) { if(channel->remote_media->direction & MRCP_AUDIO_SEND) { /* check/create rx context */ /* open rx */ mrcp_live_audio_channel_open_rx(channel->object); } else { /* close rx */ mrcp_live_audio_channel_close_rx(channel->object); } } else { /* close rx */ mrcp_live_audio_channel_close_rx(channel->object); } } else { /* MRCP_MEDIA_DISABLED */ /* close tx */ mrcp_live_audio_channel_close_tx(channel->object); /* close rx */ mrcp_live_audio_channel_close_rx(channel->object); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_on_offer(mrcp_audio_channel_t *channel) { if(channel->local_media->base.state != channel->remote_media->base.state) { channel->local_media->base.state = channel->remote_media->base.state; } if(channel->local_media->base.state == MRCP_MEDIA_ENABLED) { if(channel->remote_media->direction & MRCP_AUDIO_RECEIVE) { if(channel->local_media->direction & MRCP_AUDIO_SEND) { /* open (update) tx */ mrcp_live_audio_channel_open_tx(channel->object); } else { channel->local_media->direction |= MRCP_AUDIO_SEND; /* check/create tx context */ /* open tx */ mrcp_live_audio_channel_open_tx(channel->object); } } else { if(channel->local_media->direction & MRCP_AUDIO_SEND) { channel->local_media->direction &= ~MRCP_AUDIO_SEND; /* close tx */ mrcp_live_audio_channel_close_tx(channel->object); } else { /* close (update) tx */ mrcp_live_audio_channel_close_tx(channel->object); } } if(channel->remote_media->direction & MRCP_AUDIO_SEND) { if(channel->local_media->direction & MRCP_AUDIO_RECEIVE) { /* open (update) rx */ mrcp_live_audio_channel_open_rx(channel->object); } else { channel->local_media->direction |= MRCP_AUDIO_RECEIVE; /* check/create rx context */ /* open rx */ mrcp_live_audio_channel_open_rx(channel->object); } } else { if(channel->local_media->direction & MRCP_AUDIO_RECEIVE) { channel->local_media->direction &= ~MRCP_AUDIO_RECEIVE; /* close rx */ mrcp_live_audio_channel_close_rx(channel->object); } else { /* close (update) rx */ mrcp_live_audio_channel_close_rx(channel->object); } } } else { /* MRCP_MEDIA_DISABLED */ /* close (update) tx */ mrcp_live_audio_channel_close_tx(channel->object); /* close (update) rx */ mrcp_live_audio_channel_close_rx(channel->object); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_agent_channel_create( mrcp_media_agent_t *agent, mrcp_audio_channel_t *channel, apr_pool_t *pool) { mrcp_live_media_agent_t *live_agent = agent->object; mrcp_live_audio_channel_t *live_channel = apr_palloc(pool,sizeof(mrcp_live_audio_channel_t)); channel->object = live_channel; channel->agent = agent; channel->method_set = &audio_channel_method_set; apt_log(APT_PRIO_INFO,"Create Audio Channel\n"); live_channel->pool = pool; live_channel->channel = channel; live_channel->active_direction = MRCP_AUDIO_NONE; live_channel->rx_media_context = NULL; live_channel->tx_media_context = NULL; live_channel->rtp_session = NULL; if(channel->mode != MRCP_AUDIO_CHANNEL_NONE) { mrcp_media_agent_request(agent,channel->local_media,pool); live_channel->rtp_session = rtp_session_create( channel->local_media->base.ip, channel->local_media->base.port, live_channel->pool); if(!live_channel->rtp_session) { return MRCP_STATUS_FAILURE; } } if(!channel->local_media->codec_count && !channel->remote_media->codec_count) { codec_manager_t *codec_manager = media_processor_codec_manager_get(live_agent->media_processor); channel->local_media->codec_count = codec_manager_codec_list_get( codec_manager,channel->local_media->codec_list, MRCP_MAX_CODEC_COUNT, pool); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_destroy(mrcp_audio_channel_t *channel) { mrcp_live_media_agent_t *live_agent = channel->agent->object; mrcp_live_audio_channel_t *live_channel = channel->object; apt_log(APT_PRIO_INFO,"Destroy Audio Channel\n"); /* close (update) tx */ mrcp_live_audio_channel_close_tx(channel->object); /* close (update) rx */ mrcp_live_audio_channel_close_rx(channel->object); if(live_channel->tx_media_context) { media_processor_context_remove( live_agent->media_processor, live_channel->tx_media_context, MEDIA_REQUEST_TYPE_SYNC); media_context_destroy(live_channel->tx_media_context); live_channel->tx_media_context = NULL; } if(live_channel->rx_media_context) { media_processor_context_remove( live_agent->media_processor, live_channel->rx_media_context, MEDIA_REQUEST_TYPE_SYNC); media_context_destroy(live_channel->rx_media_context); live_channel->rx_media_context = NULL; } if(live_channel->rtp_session) { rtp_session_tx_stream_destroy(live_channel->rtp_session); rtp_session_rx_stream_destroy(live_channel->rtp_session); channel->audio_sink = NULL; channel->audio_source = NULL; rtp_session_destroy(live_channel->rtp_session); live_channel->rtp_session = NULL; } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_modify(mrcp_audio_channel_t *channel, mrcp_audio_request_t request) { mrcp_live_media_agent_t *live_agent = channel->agent->object; mrcp_live_audio_channel_t *live_channel = channel->object; codec_manager_t *codec_manager = media_processor_codec_manager_get(live_agent->media_processor); apt_log(APT_PRIO_INFO,"Modify Audio Channel\n"); if(channel->remote_media->base.state == MRCP_MEDIA_ENABLED) { size_t i; codec_descriptor_t *local_descriptor; codec_descriptor_t *remote_descriptor; channel->local_media->codec_count = 0; for(i=0; iremote_media->codec_count; i++) { remote_descriptor = channel->remote_media->codec_list[i]; if(!remote_descriptor) { continue; } if(codec_manager_codec_get(codec_manager,remote_descriptor)) { local_descriptor = apr_palloc(live_channel->pool,sizeof(codec_descriptor_t)); *local_descriptor = *remote_descriptor; channel->local_media->codec_list[channel->local_media->codec_count++] = local_descriptor; } } } switch(request) { case MRCP_AUDIO_REQUEST_RECEIVE_ANSWER: mrcp_live_audio_channel_on_answer(channel); break; case MRCP_AUDIO_REQUEST_RECEIVE_OFFER: mrcp_live_audio_channel_on_offer(channel); break; } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_source_connect(mrcp_audio_channel_t *channel, audio_source_t *source) { mrcp_live_media_agent_t *live_agent = channel->agent->object; mrcp_live_audio_channel_t *live_channel = channel->object; if(live_channel->tx_media_context) { media_context_source_add(live_agent->media_processor,live_channel->tx_media_context,source,MEDIA_REQUEST_TYPE_ASYNC); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_source_disconnect(mrcp_audio_channel_t *channel, audio_source_t *source) { mrcp_live_media_agent_t *live_agent = channel->agent->object; mrcp_live_audio_channel_t *live_channel = channel->object; if(live_channel->tx_media_context) { media_context_source_remove(live_agent->media_processor,live_channel->tx_media_context,source,MEDIA_REQUEST_TYPE_SYNC); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_sink_connect(mrcp_audio_channel_t *channel, audio_sink_t *sink) { mrcp_live_media_agent_t *live_agent = channel->agent->object; mrcp_live_audio_channel_t *live_channel = channel->object; if(live_channel->rx_media_context) { media_context_sink_add(live_agent->media_processor,live_channel->rx_media_context,sink,MEDIA_REQUEST_TYPE_ASYNC); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_audio_channel_sink_disconnect(mrcp_audio_channel_t *channel, audio_sink_t *sink) { mrcp_live_media_agent_t *live_agent = channel->agent->object; mrcp_live_audio_channel_t *live_channel = channel->object; if(live_channel->rx_media_context) { media_context_sink_remove(live_agent->media_processor,live_channel->rx_media_context,sink,MEDIA_REQUEST_TYPE_SYNC); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_live_agent_signal_handler(mrcp_module_t *module, apt_task_msg_t *msg) { return MRCP_STATUS_SUCCESS; }