/*
* This file is part of Project DiaStar Server.
*
* More information about this project can be found at:
* http://www.projectdiastar.org.
*
* Copyright (C) 2009 Dialogic Corp.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
* Alternatively see .
* Or see the LICENSE file included within the source tree.
*
*/
/*!
* \file ipmdevice.cxx
* \brief Dialogic IPM device
* \author Antony Martin
* \author John Tarlton
* \version 2-APR-2009
*/
/*------------------------------ Dependencies --------------------------------*/
#include "logger.h"
#include "string-util.h"
#include "dialogicchannelmanager.h"
#include "mmdevice.h"
#include "gciptdevice.h"
#include "ipmdevice.h"
/*----------------------------------------------------------------------------*/
/*
* ctor
*/
IpmDevice::IpmDevice( const std::string& name,
DialogicChannelManager& channelMgr )
: DialogicDevice(name, channelMgr)
{
ipt_ = 0; /* standalone mode */
busy_ = false;
/* Use the most preferred audio coder as the default.
*/
const Coders::audio_coders_t& audioCoders = channelMgr_.getAudioCoders();
Coders::audio_coders_t::const_iterator i = audioCoders.begin();
if ( i != audioCoders.end() )
{
localAudioCoder_ = *i;
}
else /* PCMU */
{
localAudioCoder_.coderType = CODER_TYPE_G711ULAW64K;
localAudioCoder_.coderPayloadType = 0;
localAudioCoder_.coderFramesize = CODER_FRAMESIZE_20;
localAudioCoder_.framesPerPkt = 1;
localAudioCoder_.vadEnable = CODER_VAD_ENABLE;
localAudioCoder_.redPayloadType = 0;
}
/* Use the most preferred video coder as the default.
*/
const Coders::video_coders_t& videoCoders = channelMgr_.getVideoCoders();
Coders::video_coders_t::const_iterator j = videoCoders.begin();
if ( j != videoCoders.end() )
{
localVideoCoder_ = *j;
}
else /* H.263 */
{
localVideoCoder_.coderType = CODER_TYPE_H263;
localVideoCoder_.coderPayloadType = 34;
localVideoCoder_.profile = VIDEO_PROFILE_0_H263;
localVideoCoder_.level = VIDEO_LEVEL_10_H263;
localVideoCoder_.bitRate = 90000;
localVideoCoder_.samplingRate = VIDEO_SAMPLING_RATE_DEFAULT;
/* QCIF=2 */
localVideoCoder_.active_fmt.imageWidth = VIDEO_IMAGE_WIDTH_176;
localVideoCoder_.active_fmt.imageHeight = VIDEO_IMAGE_HEIGHT_144;
localVideoCoder_.active_fmt.framesPerSec = VIDEO_FRAMESPERSEC_15;
localVideoCoder_.available_fmts.push_back(localVideoCoder_.active_fmt);
}
remoteAudioCoder_ = localAudioCoder_;
remoteVideoCoder_ = localVideoCoder_;
/* Number of steps in the initialisation sequence, decremented by event
* handlers. When zero, state is changed to IDLE.
*
* IPMEV_OPEN,
* DMEV_GET_RX_PORT_INFO,
* DMEV_GET_TX_PORT_INFO
*/
initStepsRemaining_ = 3;
}
/*
* dtor
*/
IpmDevice::~IpmDevice()
{
}
/*
* Process a command that was queueued because an async operation was in progress
* when it was initially requested.
*/
void IpmDevice::processPendingCommand()
{
while ( !busy_ && !pending_.empty() )
{
LOGDEBUG("IpmDevice::processPendingCommand()");
switch( pending_.front().cmd )
{
case IpmCommand::START:
startMedia();
break;
case IpmCommand::STOP:
stop(pending_.front().operation);
break;
case IpmCommand::LISTEN:
listen(pending_.front().other);
break;
case IpmCommand::UNLISTEN:
unListen();
break;
case IpmCommand::CONNECT:
connect(pending_.front().other);
break;
case IpmCommand::DISCONNECT:
disconnect();
break;
default:
break;
}
pending_.pop();
}
}
/*
* Open the device.
*/
bool IpmDevice::open()
{
LOGDEBUG("IpmDevice::open() device: " << getDeviceName());
devHandle_ = ipm_Open(getDeviceName().c_str(), NULL, EV_ASYNC);
if ( devHandle_ < 0 )
{
LOGERROR("ipm_Open() failed");
return false;
}
state_ = IPM_OPENING;
return true;
}
/*
* Close the device
*/
bool IpmDevice::close()
{
LOGDEBUG("IpmDevice::close() device: " << getDeviceName());
if ( devHandle_ > 0 )
{
if ( ipm_Close(devHandle_, NULL) < 0 )
{
LOGERROR("ipm_Close() failed");
return false;
}
devHandle_ = 0;
}
return true;
}
/*
* Connect media.
*/
bool IpmDevice::listen( DialogicDevice* other )
{
if ( busy_ )
{
pending_.push(IpmCommand(IpmCommand::LISTEN, other));
return true;
}
if ( other_ != other )
{
LOGDEBUG("IpmDevice::listen() device: " << getDeviceName() <<
" other: " << other->getDeviceName());
SC_TSINFO tsinfo;
tsinfo.sc_numts = 1;
tsinfo.sc_tsarrayp = other->getXmitTimeslotPtr();
if ( ipm_Listen(devHandle_, &tsinfo, EV_ASYNC) == -1 )
{
LOGERROR("ipm_Listen() failed on device: " << getDeviceName() <<
" " << ATDV_ERRMSGP(devHandle_));
return false;
}
other_ = other;
busy_ = true;
}
return true;
}
/*
* Disconnect media.
*/
bool IpmDevice::unListen()
{
if ( busy_ )
{
pending_.push(IpmCommand(IpmCommand::UNLISTEN));
return true;
}
if ( other_ )
{
LOGDEBUG("IpmDevice::unListen() device: " << getDeviceName());
if ( ipm_UnListen(devHandle_, EV_ASYNC) == -1)
{
LOGERROR("ipm_UnListen() failed on device: " << getDeviceName() <<
" " << ATDV_ERRMSGP(devHandle_));
return false;
}
other_ = 0;
busy_ = true;
}
return true;
}
/*
* Connect media.
*/
bool IpmDevice::connect( DialogicDevice* other )
{
if ( busy_ )
{
pending_.push(IpmCommand(IpmCommand::CONNECT, other));
return true;
}
if ( !other_ )
{
LOGDEBUG("IpmDevice::connect() device: " << getDeviceName() <<
" to: " << other->getDeviceName());
DM_PORT_CONNECT_INFO_LIST portConnectInfoList;
memset(&portConnectInfoList, 0, sizeof(portConnectInfoList));
INIT_DM_PORT_CONNECT_INFO_LIST(&portConnectInfoList);
int count = 0;
INIT_DM_PORT_CONNECT_INFO(&portConnectInfoList.port_connect_info[count]);
portConnectInfoList.port_connect_info[count].unFlags = DMFL_TRANSCODE_ON;
portConnectInfoList.port_connect_info[count].port_info_tx = getAudioTxPortInfo();
portConnectInfoList.port_connect_info[count].port_info_rx = other->getAudioRxPortInfo();
count++;
INIT_DM_PORT_CONNECT_INFO(&portConnectInfoList.port_connect_info[count]);
portConnectInfoList.port_connect_info[count].unFlags = DMFL_TRANSCODE_ON;
portConnectInfoList.port_connect_info[count].port_info_tx = getVideoTxPortInfo();
portConnectInfoList.port_connect_info[count].port_info_rx = other->getVideoRxPortInfo();
count++;
portConnectInfoList.unCount = count;
if ( dev_PortConnect(devHandle_, &portConnectInfoList, NULL) != DEV_SUCCESS )
{
LOGERROR("IpmDevice::connect() dev_PortConnect() failed on device: " << getDeviceName() <<
" " << ATDV_ERRMSGP(devHandle_));
return false;
}
other_ = other;
busy_ = true;
}
else
{
if ( other_ != other )
{
LOGERROR("IpmDevice::connect() " << getDeviceName() <<
" unable to connect to device: " << other->getDeviceName() <<
" already connected to device: " << other_->getDeviceName());
}
}
return true;
}
/*
* Disconnect media.
*/
bool IpmDevice::disconnect()
{
if ( busy_ )
{
pending_.push(IpmCommand(IpmCommand::DISCONNECT));
return true;
}
if ( other_ )
{
LOGDEBUG("IpmDevice::disconnect() device: " << getDeviceName() <<
" from: " << other_->getDeviceName());
DM_PORT_CONNECT_INFO_LIST portConnectInfoList;
memset(&portConnectInfoList, 0, sizeof(portConnectInfoList));
INIT_DM_PORT_CONNECT_INFO_LIST(&portConnectInfoList);
int count = 0;
INIT_DM_PORT_CONNECT_INFO(&portConnectInfoList.port_connect_info[count]);
portConnectInfoList.port_connect_info[count].port_info_tx = getAudioTxPortInfo();
portConnectInfoList.port_connect_info[count].port_info_rx = other_->getAudioRxPortInfo();
count++;
INIT_DM_PORT_CONNECT_INFO(&portConnectInfoList.port_connect_info[count]);
portConnectInfoList.port_connect_info[count].port_info_tx = getVideoTxPortInfo();
portConnectInfoList.port_connect_info[count].port_info_rx = other_->getVideoRxPortInfo();
count++;
portConnectInfoList.unCount = count;
if ( dev_PortDisconnect(devHandle_, &portConnectInfoList, NULL) != DEV_SUCCESS )
{
LOGERROR("IpmDevice::disconnect() dev_PortDisconnect() failed on device: " << getDeviceName() <<
" " << ATDV_ERRMSGP(devHandle_));
return false;
}
other_ = 0;
busy_ = true;
}
return true;
}
/*
* Search localMediaInfo_ for the requested information.
*/
bool IpmDevice::getLocalMediaInfo( eIPM_MEDIA_TYPE type,
IpInfo& rtp_media ) const
{
switch ( type )
{
case MEDIATYPE_AUDIO_LOCAL_RTP_INFO:
rtp_media = localRtpAudio_;
break;
case MEDIATYPE_AUDIO_LOCAL_RTCP_INFO:
rtp_media = localRtcpAudio_;
break;
case MEDIATYPE_VIDEO_LOCAL_RTP_INFO:
rtp_media = localRtpVideo_;
break;
case MEDIATYPE_VIDEO_LOCAL_RTCP_INFO:
rtp_media = localRtcpVideo_;
break;
default:
return false;
}
return true;
}
/*
* Save the remote params.
*/
void IpmDevice::setRemoteMediaInfo( eIPM_MEDIA_TYPE type,
const IpInfo& rtp_info )
{
if ( type == MEDIATYPE_AUDIO_REMOTE_RTP_INFO )
{
remoteRtpAudio_ = rtp_info;
remoteRtcpAudio_ = rtp_info;
remoteRtcpAudio_.setPort(remoteRtpAudio_.getPort() + 1);
LOGDEBUG("IpmDevice::setRemoteMediaInfo() device: " << getDeviceName() <<
" a=" << remoteRtpAudio_);
}
if ( type == MEDIATYPE_VIDEO_REMOTE_RTP_INFO )
{
remoteRtpVideo_ = rtp_info;
remoteRtcpVideo_ = rtp_info;
remoteRtcpVideo_.setPort(remoteRtpVideo_.getPort() + 1);
LOGDEBUG("IpmDevice::setRemoteMediaInfo() device: " << getDeviceName() <<
" v=" << remoteRtpVideo_);
}
}
/*
* Stop the IPM streaming and/or digit reception
*/
bool IpmDevice::stop( eIPM_STOP_OPERATION operation )
{
if ( state_ == IPM_IDLE )
{
return true; /* already stopped */
}
if ( busy_ )
{
pending_.push(IpmCommand(IpmCommand::STOP, operation));
return true;
}
if ( ipm_Stop(devHandle_, operation, EV_ASYNC) == -1 )
{
LOGERROR("ipm_Stop() on device: " << getDeviceName() << " failed: " <<
ATDV_LASTERR(devHandle_));
return false;
}
LOGDEBUG("IpmDevice::stopMedia() stopping RTP streaming on device: " <<
getDeviceName());
state_ = IPM_STOPPING;
busy_ = true;
return true;
}
/*
* Start RTP streaming to peer using the current settings.
*/
bool IpmDevice::startMedia()
{
return startMedia(remoteRtpAudio_,
remoteRtcpAudio_,
remoteRtpVideo_,
remoteRtcpVideo_);
}
/*
* Start RTP streaming.
*/
bool IpmDevice::startMedia( const IpInfo& remote_rtp_audio,
const IpInfo& remote_rtcp_audio,
const IpInfo& remote_rtp_video,
const IpInfo& remote_rtcp_video )
{
if ( busy_ )
{
pending_.push(IpmCommand(IpmCommand::START));
return true;
}
if ( state_ == IPM_STREAMING )
{
/* already started, ignore */
LOGDEBUG("IpmDevice::startMedia() already streaming on device: " <<
getDeviceName());
return true;
}
if ( !remote_rtp_audio.isValid() && !remote_rtp_video.isValid() )
{
LOGERROR("IpmDevice::startMedia() no remote rtp media for device: " <<
getDeviceName());
return false;
}
setDtmfMode(DTMF_RFC2833);
IPM_MEDIA_INFO mediaInfo;
memset(&mediaInfo, 0, sizeof(IPM_MEDIA_INFO));
int mediaCnt = 0; /* limit MAX_MEDIA_INFO */
/* local audio
*/
mediaInfo.MediaData[mediaCnt].eMediaType = MEDIATYPE_AUDIO_LOCAL_CODER_INFO;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.eCoderType = localAudioCoder_.coderType;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.eFrameSize = localAudioCoder_.coderFramesize;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.unFramesPerPkt = localAudioCoder_.framesPerPkt;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.eVadEnable = localAudioCoder_.vadEnable;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.unCoderPayloadType = localAudioCoder_.coderPayloadType;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.unRedPayloadType = localAudioCoder_.redPayloadType;
mediaCnt++;
/* remote audio
*/
mediaInfo.MediaData[mediaCnt].eMediaType = MEDIATYPE_AUDIO_REMOTE_CODER_INFO;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.eCoderType = remoteAudioCoder_.coderType;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.eFrameSize = remoteAudioCoder_.coderFramesize;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.unFramesPerPkt = remoteAudioCoder_.framesPerPkt;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.eVadEnable = remoteAudioCoder_.vadEnable;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.unCoderPayloadType = remoteAudioCoder_.coderPayloadType;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.unRedPayloadType = remoteAudioCoder_.redPayloadType;
mediaCnt++;
/* remote audio ports and IP addresses
*/
mediaInfo.MediaData[mediaCnt].eMediaType = MEDIATYPE_AUDIO_REMOTE_RTP_INFO;
copy_string_to_c_array(remote_rtp_audio.getAddress(),
mediaInfo.MediaData[mediaCnt].mediaInfo.PortInfo.cIPAddress,
IP_ADDR_SIZE);
mediaInfo.MediaData[mediaCnt].mediaInfo.PortInfo.unPortId = remote_rtp_audio.getPort();
mediaCnt++;
mediaInfo.MediaData[mediaCnt].eMediaType = MEDIATYPE_AUDIO_REMOTE_RTCP_INFO;
copy_string_to_c_array(remote_rtcp_audio.getAddress(),
mediaInfo.MediaData[mediaCnt].mediaInfo.PortInfo.cIPAddress,
IP_ADDR_SIZE);
mediaInfo.MediaData[mediaCnt].mediaInfo.PortInfo.unPortId = remote_rtcp_audio.getPort();
mediaCnt++;
if ( remote_rtp_video.isValid() )
{
/* local video
*/
mediaInfo.MediaData[mediaCnt].eMediaType = MEDIATYPE_VIDEO_LOCAL_CODER_INFO;
INIT_IPM_VIDEO_CODER_INFO(&mediaInfo.MediaData[mediaCnt].mediaInfo.VideoCoderInfo);
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.eCoderType = localVideoCoder_.coderType;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.unCoderPayloadType = localVideoCoder_.coderPayloadType;
IPM_VIDEO_CODER_INFO_EX localVideoCoderInfoEx;
INIT_IPM_VIDEO_CODER_INFO_EX(&localVideoCoderInfoEx);
localVideoCoderInfoEx.eProfile = localVideoCoder_.profile;
localVideoCoderInfoEx.eLevel = localVideoCoder_.level;
localVideoCoderInfoEx.eImageWidth = localVideoCoder_.active_fmt.imageWidth;
localVideoCoderInfoEx.eImageHeight = localVideoCoder_.active_fmt.imageHeight;
localVideoCoderInfoEx.eFramesPerSec = localVideoCoder_.active_fmt.framesPerSec;
localVideoCoderInfoEx.unBitRate = localVideoCoder_.bitRate;
localVideoCoderInfoEx.eSamplingRate = localVideoCoder_.samplingRate;
localVideoCoderInfoEx.unVisualConfigSize = 0;
localVideoCoderInfoEx.szVisualConfiguration = NULL;
mediaInfo.MediaData[mediaCnt].mediaInfo.VideoCoderInfo.pExtraCoderInfo = &localVideoCoderInfoEx;
mediaCnt++;
/* remote video
*/
mediaInfo.MediaData[mediaCnt].eMediaType = MEDIATYPE_VIDEO_REMOTE_CODER_INFO;
INIT_IPM_VIDEO_CODER_INFO(&mediaInfo.MediaData[mediaCnt].mediaInfo.VideoCoderInfo);
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.eCoderType = remoteVideoCoder_.coderType;
mediaInfo.MediaData[mediaCnt].mediaInfo.CoderInfo.unCoderPayloadType = remoteVideoCoder_.coderPayloadType;
IPM_VIDEO_CODER_INFO_EX remoteVideoCoderInfoEx;
INIT_IPM_VIDEO_CODER_INFO_EX(&remoteVideoCoderInfoEx);
remoteVideoCoderInfoEx.eProfile = remoteVideoCoder_.profile;
remoteVideoCoderInfoEx.eLevel = remoteVideoCoder_.level;
remoteVideoCoderInfoEx.eImageWidth = remoteVideoCoder_.active_fmt.imageWidth;
remoteVideoCoderInfoEx.eImageHeight = remoteVideoCoder_.active_fmt.imageHeight;
remoteVideoCoderInfoEx.eFramesPerSec = remoteVideoCoder_.active_fmt.framesPerSec;
remoteVideoCoderInfoEx.unBitRate = remoteVideoCoder_.bitRate;
remoteVideoCoderInfoEx.eSamplingRate = remoteVideoCoder_.samplingRate;
remoteVideoCoderInfoEx.unVisualConfigSize = 0;
remoteVideoCoderInfoEx.szVisualConfiguration = NULL;
mediaInfo.MediaData[mediaCnt].mediaInfo.VideoCoderInfo.pExtraCoderInfo = &remoteVideoCoderInfoEx;
mediaCnt++;
/* remote video ports and IP addresses
*/
mediaInfo.MediaData[mediaCnt].eMediaType = MEDIATYPE_VIDEO_REMOTE_RTP_INFO;
copy_string_to_c_array(remote_rtp_video.getAddress(),
mediaInfo.MediaData[mediaCnt].mediaInfo.PortInfo.cIPAddress,
IP_ADDR_SIZE);
mediaInfo.MediaData[mediaCnt].mediaInfo.PortInfo.unPortId = remote_rtp_video.getPort();
mediaCnt++;
mediaInfo.MediaData[mediaCnt].eMediaType = MEDIATYPE_VIDEO_REMOTE_RTCP_INFO;
copy_string_to_c_array(remote_rtcp_video.getAddress(),
mediaInfo.MediaData[mediaCnt].mediaInfo.PortInfo.cIPAddress,
IP_ADDR_SIZE);
mediaInfo.MediaData[mediaCnt].mediaInfo.PortInfo.unPortId = remote_rtcp_video.getPort();
mediaCnt++;
}
/* done
*/
mediaInfo.unCount = mediaCnt;
LOGDEBUG("IpmDevice::startMedia() starting audio on device: " <<
getDeviceName() <<
" remote_rtp: " << remote_rtp_audio <<
" remote_rtcp: " << remote_rtcp_audio);
if ( remote_rtp_video.isValid() )
{
LOGDEBUG("IpmDevice::startMedia() starting video on device: " <<
getDeviceName() <<
" remote_rtp: " << remote_rtp_video <<
" remote_rtcp: " << remote_rtcp_video);
}
if ( ipm_StartMedia(devHandle_, &mediaInfo, DATA_IP_TDM_BIDIRECTIONAL, EV_ASYNC) == -1 )
{
LOGERROR("ipm_StartMedia failed for device: " << getDeviceName() <<
" with error: " << ATDV_ERRMSGP(devHandle_));
return false;
}
state_ = IPM_STARTING;
busy_ = true;
return true;
}
/*
* Forward to event specific handler.
*/
bool IpmDevice::processEvent( METAEVENT& metaevent )
{
switch ( metaevent.evttype )
{
case IPMEV_OPEN:
onOpen();
break;
case IPMEV_EVENT_ENABLED:
onEventEnabled();
break;
case IPMEV_GET_LOCAL_MEDIA_INFO:
onLocalMediaInfo((IPM_MEDIA_INFO*)sr_getevtdatap());
break;
case IPMEV_STARTMEDIA:
onStartMedia();
break;
case IPMEV_STOPPED:
onStopped();
break;
case IPMEV_LISTEN:
onListen();
break;
case IPMEV_UNLISTEN:
onUnListen();
break;
case IPMEV_ERROR:
onError();
break;
case IPMEV_TELEPHONY_EVENT:
onTelephonyEvent((IPM_TELEPHONY_INFO*)sr_getevtdatap());
break;
case DMEV_GET_TX_PORT_INFO:
onGetTxPortInfo((DM_PORT_INFO_LIST*)sr_getevtdatap());
break;
case DMEV_GET_RX_PORT_INFO:
onGetRxPortInfo((DM_PORT_INFO_LIST*)sr_getevtdatap());
break;
case DMEV_GET_TX_PORT_INFO_FAIL:
onPortGetFail();
break;
case DMEV_GET_RX_PORT_INFO_FAIL:
onPortGetFail();
break;
case DMEV_PORT_CONNECT:
onPortConnect();
break;
case DMEV_PORT_CONNECT_FAIL:
onPortConnectFail();
break;
case DMEV_PORT_DISCONNECT:
onPortDisconnect();
break;
case DMEV_PORT_DISCONNECT_FAIL:
LOGERROR("IpmDevice::onPortDisconnectFail() device: " << getDeviceName());
break;
default:
LOGWARN("IpmDevice::processEvent() unhandled event: 0x" <<
std::hex << metaevent.evttype << " for device: " << getDeviceName());
return false;
}
return true;
}
/*
* Handler for IPMEV_OPEN events.
*/
void IpmDevice::onOpen()
{
LOGINFO("IpmDevice::onOpen() device: " << getDeviceName());
other_ = 0;
/* Get and save the transmit timeslot on CTBus
*/
SC_TSINFO tsinfo;
tsinfo.sc_numts = 1;
tsinfo.sc_tsarrayp = getXmitTimeslotPtr();
if ( ipm_GetXmitSlot(devHandle_, &tsinfo, EV_SYNC) == -1 )
{
LOGERROR("ipm_GetXmitSlot() failed" );
}
/* Enable dtmf events.
*/
eIPM_EVENT event = EVT_RFC2833;
if ( ipm_EnableEvents(devHandle_, &event, 1, EV_ASYNC) < 0 )
{
LOGERROR("ipm_EnableEvent() failed on device:" << devHandle_);
}
/* Request port info
*/
if ( dev_GetTransmitPortInfo(devHandle_, this) == -1 )
{
LOGERROR("dev_GetTransmitPortInfo() failed");
}
if ( dev_GetReceivePortInfo(devHandle_, this) == -1 )
{
LOGERROR("dev_GetReceivePortInfo() failed");
}
/* Request the local media information.
*/
IPM_MEDIA_INFO media_info;
memset(&media_info, 0, sizeof(IPM_MEDIA_INFO));
media_info.unCount = 2;
media_info.MediaData[0].eMediaType = MEDIATYPE_AUDIO_LOCAL_RTP_INFO;
media_info.MediaData[1].eMediaType = MEDIATYPE_VIDEO_LOCAL_RTP_INFO;
if ( ipm_GetLocalMediaInfo(devHandle_, &media_info, EV_ASYNC) == -1 )
{
LOGERROR("ipm_GetLocalMediaInfo() failed for device: " <<
getDeviceName() << " with error: " << ATDV_LASTERR(devHandle_));
}
/* update init state
*/
state_ = IPM_INITIALIZATION_START;
initStepsRemaining_--;
if ( initStepsRemaining_ == 0 )
{
state_ = IPM_IDLE;
}
}
/*
* Handler for DMEV_GET_TX_PORT_INFO events.
*/
void IpmDevice::onGetTxPortInfo( DM_PORT_INFO_LIST* portInfoList )
{
LOGINFO("IpmDevice::onGetTxPortInfo() device: " << getDeviceName());
txPortInfoList_ = *portInfoList;
LOGDEBUG(std::endl << txPortInfoList_);
for ( unsigned int i = 0; i < txPortInfoList_.unCount; i++ )
{
switch ( txPortInfoList_.port_info[i].port_media_type )
{
case DM_PORT_MEDIA_TYPE_AUDIO:
audioPortTxInfo_ = txPortInfoList_.port_info[i];
break;
case DM_PORT_MEDIA_TYPE_VIDEO:
videoPortTxInfo_ = txPortInfoList_.port_info[i];
break;
default:
break;
}
}
initStepsRemaining_--;
if ( initStepsRemaining_ == 0 )
{
state_ = IPM_IDLE;
}
}
/*
* Handler for DMEV_GET_RX_PORT_INFO events.
*/
void IpmDevice::onGetRxPortInfo( DM_PORT_INFO_LIST* portInfoList )
{
LOGINFO("IpmDevice::onGetRxPortInfo() device: " << getDeviceName());
rxPortInfoList_ = *portInfoList;
LOGDEBUG(std::endl << rxPortInfoList_);
for ( unsigned int i = 0; i < rxPortInfoList_.unCount; i++ )
{
switch ( rxPortInfoList_.port_info[i].port_media_type )
{
case DM_PORT_MEDIA_TYPE_AUDIO:
audioPortRxInfo_ = rxPortInfoList_.port_info[i];
break;
case DM_PORT_MEDIA_TYPE_VIDEO:
videoPortRxInfo_ = rxPortInfoList_.port_info[i];
break;
default:
break;
}
}
initStepsRemaining_--;
if ( initStepsRemaining_ == 0 )
{
state_ = IPM_IDLE;
}
}
/*
* Handler for DMEV_GET_TX_PORT_INFO_FAIL and DMEV_GET_RX_PORT_INFO_FAIL
* events.
*/
void IpmDevice::onPortGetFail()
{
LOGINFO("IpmDevice::onPortGetFail() device: " << getDeviceName());
state_ = IPM_INVALID;
}
/*
* Handler for IPMEV_ERROR events.
*/
void IpmDevice::onError()
{
LOGERROR("IpmDevice::onError() device: " << getDeviceName() <<
" error: " << ATDV_ERRMSGP(devHandle_));
state_ = IPM_INVALID;
}
/*
* Handler for IPMEV_TELEPHONY_EVENT events.
*/
void IpmDevice::onTelephonyEvent( IPM_TELEPHONY_INFO* info )
{
LOGINFO("IpmDevice::processEvent() IPMEV_TELEPHONY_EVENT device: " << getDeviceName() );
const std::string dtmf = "0123456789*#";
switch ( info->eTelInfoType )
{
case TEL_INFOTYPE_EVENT:
{
int event_id = info->TelephonyInfo.TelEvtInfo.eTelephonyEventID;
LOGDEBUG("TelephonyEventID: 0x" << std::hex << event_id);
if ( event_id >= 0 && event_id <= 11 )
{
channelMgr_.onDtmf(ipt_, dtmf[event_id], 100); /* fake duration */
}
break;
}
default:
break;
}
}
/*
* Handler for IPMEV_GET_LOCAL_MEDIA_INFO events.
*/
void IpmDevice::onLocalMediaInfo( IPM_MEDIA_INFO* mediaInfo )
{
LOGINFO("IpmDevice::onLocalMediaInfo() device: " << getDeviceName());
LOGDEBUG(std::endl << *mediaInfo);
for( unsigned int i = 0; i < mediaInfo->unCount; i++ )
{
switch ( mediaInfo->MediaData[i].eMediaType )
{
case MEDIATYPE_AUDIO_LOCAL_RTP_INFO:
localRtpAudio_.setAddress(mediaInfo->MediaData[i].mediaInfo.PortInfo.cIPAddress);
localRtpAudio_.setPort(mediaInfo->MediaData[i].mediaInfo.PortInfo.unPortId);
break;
case MEDIATYPE_AUDIO_LOCAL_RTCP_INFO:
localRtcpAudio_.setAddress(mediaInfo->MediaData[i].mediaInfo.PortInfo.cIPAddress);
localRtcpAudio_.setPort(mediaInfo->MediaData[i].mediaInfo.PortInfo.unPortId);
break;
case MEDIATYPE_VIDEO_LOCAL_RTP_INFO:
localRtpVideo_.setAddress(mediaInfo->MediaData[i].mediaInfo.PortInfo.cIPAddress);
localRtpVideo_.setPort(mediaInfo->MediaData[i].mediaInfo.PortInfo.unPortId);
break;
case MEDIATYPE_VIDEO_LOCAL_RTCP_INFO:
localRtcpVideo_.setAddress(mediaInfo->MediaData[i].mediaInfo.PortInfo.cIPAddress);
localRtcpVideo_.setPort(mediaInfo->MediaData[i].mediaInfo.PortInfo.unPortId);
break;
default:
break;
}
}
}
/*
* Handler for IPMEV_EVENT_ENABLED events
*/
void IpmDevice::onEventEnabled()
{
LOGINFO("IpmDevice::onEventEnabled() device: " << getDeviceName());
initStepsRemaining_--;
if ( initStepsRemaining_ == 0 )
{
state_ = IPM_IDLE;
}
}
/*
* Handler for IPMEV_STARTMEDIA events.
*/
void IpmDevice::onStartMedia()
{
LOGINFO("IpmDevice::onStartMedia() device: " << getDeviceName());
state_ = IPM_STREAMING;
busy_ = false;
processPendingCommand();
}
/*
* Handler for IPMEV_STOPPED events.
*/
void IpmDevice::onStopped()
{
LOGINFO("IpmDevice::onStopped() device: " << getDeviceName());
state_ = IPM_IDLE;
busy_ = false;
processPendingCommand();
}
/*
* Handler for IPMEV_LISTEN events.
*/
void IpmDevice::onListen()
{
LOGINFO("IpmDevice::onListen() device: " << getDeviceName());
busy_ = false;
processPendingCommand();
}
/*
* Handler for IPMEV__UNLISTEN events.
*/
void IpmDevice::onUnListen()
{
LOGINFO("IpmDevice::onUnListen() device: " << getDeviceName());
busy_ = false;
processPendingCommand();
}
/*
* Handler for DMEV_PORT_CONNECT events.
*/
void IpmDevice::onPortConnect()
{
LOGINFO("IpmDevice::onPortConnect() device: " << getDeviceName());
busy_ = false;
processPendingCommand();
}
/*
* Handler for DMEV_PORT_CONNECT_FAIL events.
*/
void IpmDevice::onPortConnectFail()
{
LOGERROR("IpmDevice::onPortConnectFail() device: " << getDeviceName());
busy_ = false;
processPendingCommand();
}
/*
* Handler for DMEV_PORT_DISCONNECT events.
*/
void IpmDevice::onPortDisconnect()
{
LOGINFO("IpmDevice::onPortDisconnect() device: " << getDeviceName());
busy_ = false;
processPendingCommand();
}
/*
* Set the dtmf signalling mode.
*/
void IpmDevice::setDtmfMode( DtmfMode dtmf_mode )
{
IPM_PARM_INFO parmInfo;
switch ( dtmf_mode )
{
case DTMF_RFC2833:
{
eIPM_DTMFXFERMODE value = DTMFXFERMODE_RFC2833;
parmInfo.eParm = PARMCH_DTMFXFERMODE;
parmInfo.pvParmValue = &value;
LOGINFO("IpmDevice::setDtmfMode() using RFC2833");
if ( ipm_SetParm(devHandle_, &parmInfo, EV_SYNC) < 0 )
{
LOGERROR("Error in ipm_SetParm()\n");
}
int rfc2833PayloadType = 101;
parmInfo.eParm = PARMCH_RFC2833EVT_TX_PLT;
parmInfo.pvParmValue = &rfc2833PayloadType;
if ( ipm_SetParm(devHandle_, &parmInfo, EV_SYNC) < 0 )
{
LOGERROR("Error in ipm_SetParm()\n");
}
parmInfo.eParm = PARMCH_RFC2833EVT_RX_PLT;
parmInfo.pvParmValue = &rfc2833PayloadType;
if ( ipm_SetParm(devHandle_, &parmInfo, EV_SYNC) < 0 )
{
LOGERROR("Error in ipm_SetParm()\n");
}
}
break;
case DTMF_INBAND:
{
eIPM_DTMFXFERMODE value = DTMFXFERMODE_INBAND;
parmInfo.eParm = PARMCH_DTMFXFERMODE;
parmInfo.pvParmValue = &value;
LOGINFO("IpmDevice::setDtmfMode() using inband audio");
if ( ipm_SetParm(devHandle_, &parmInfo, EV_SYNC) < 0 )
{
LOGERROR("Error in ipm_SetParm()\n");
}
}
break;
}
}
/*
* Convert a minimum-picture-interval to frames-per-second.
* RFC4629: fps = 30 / (1.001 * the specified value)
*/
int IpmDevice::mpi2fps( int mpi )
{
int fps;
switch ( mpi )
{
case 1:
fps = VIDEO_FRAMESPERSEC_2997;
break;
case 2:
fps = VIDEO_FRAMESPERSEC_15;
break;
case 3:
fps = VIDEO_FRAMESPERSEC_10;
break;
case 5:
fps = VIDEO_FRAMESPERSEC_6;
break;
default:
fps = VIDEO_FRAMESPERSEC_DEFAULT;
break;
}
return fps;
}
/*
* Convert frames-per-second to a minimum-picture-interval.
* RFC4629: fps = 30 / (1.001 * the specified value)
*/
int IpmDevice::fps2mpi( eVIDEO_FRAMESPERSEC fps )
{
int mpi;
switch ( fps )
{
case VIDEO_FRAMESPERSEC_6:
mpi = 5;
break;
case VIDEO_FRAMESPERSEC_10:
mpi = 3;
break;
case VIDEO_FRAMESPERSEC_2997:
case VIDEO_FRAMESPERSEC_30:
mpi = 1;
break;
default: /* 15 fps */
mpi = 2;
break;
}
return mpi;
}
/*
* offer sdpS contain all available codecs.
*/
void IpmDevice::buildOfferSdp( SdpSessionDescription& sdp ) const
{
/* get here so it can be used for the origin address */
IpInfo local_audio_rtp;
getLocalMediaInfo(MEDIATYPE_AUDIO_LOCAL_RTP_INFO, local_audio_rtp);
sdp.version()->setVersion("0");
sdp.origin()->setUserName("DiaStarServer");
sdp.origin()->setNetworkType("IN");
sdp.origin()->setAddressType("IP4");
sdp.origin()->setAddress(local_audio_rtp.getAddress().c_str());
/* use a common c= for all media */
sdp.connection()->setNetworkType("IN");
sdp.connection()->setAddressType("IP4");
sdp.connection()->setAddress(local_audio_rtp.getAddress().c_str());
time_t t;
time(&t);
std::stringstream sessionIdandVersion;
sessionIdandVersion << t;
sdp.origin()->setSessionId(sessionIdandVersion.str().c_str());
sdp.origin()->setVersion(sessionIdandVersion.str().c_str());
sdp.sessionName()->setName("DiaStarServer");
SdpTimeDescription* timeDescription = sdp.timeDescriptionList()->addItem();
timeDescription->time()->setStart(0);
timeDescription->time()->setStop(0);
/* media
*/
SdpMediaDescriptionList* mdList = sdp.mediaDescriptionList();
mdList->clear();
/* m=audio
*/
const Coders::audio_coders_t& audioCoders = channelMgr_.getAudioCoders();
if ( audioCoders.size() > 0 )
{
SdpMediaDescription* audioMD = mdList->addItem();
SdpMedia* audioMedia = audioMD->media();
audioMedia->setMedia("audio");
audioMedia->setPort(local_audio_rtp.getPort());
audioMedia->setTransport("RTP/AVP");
audioMedia->setNumPorts(1);
SdpAttributeList* audioAttrList = audioMD->attributeList();
SdpAttribute* audioAttribute;
Coders::audio_coders_t::const_iterator i;
for ( i = audioCoders.begin(); i != audioCoders.end(); ++i )
{
const AudioCoderInfo& audio_coder_info = *i;
if ( audio_coder_info.coderType == CODER_TYPE_G711ULAW64K )
{
audioMedia->addFormat("0");
audioAttribute = audioAttrList->addItem();
audioAttribute->setProperty("rtpmap");
audioAttribute->setPropertyValue("0 PCMU/8000");
}
else if ( audio_coder_info.coderType == CODER_TYPE_G711ALAW64K )
{
audioMedia->addFormat("8");
audioAttribute = audioAttrList->addItem();
audioAttribute->setProperty("rtpmap");
audioAttribute->setPropertyValue("8 PCMA/8000");
}
else
{
/* more */
}
}
audioMedia->addFormat("101");
audioAttribute = audioAttrList->addItem();
audioAttribute->setProperty("rtpmap");
audioAttribute->setPropertyValue("101 telephone-event/8000");
audioAttribute = audioAttrList->addItem();
audioAttribute->setProperty("sendrecv");
audioAttribute->setPropertyValue("");
}
/* m=video
*/
const Coders::video_coders_t& videoCoders = channelMgr_.getVideoCoders();
if ( videoCoders.size() > 0 )
{
IpInfo local_video_rtp;
if ( getLocalMediaInfo(MEDIATYPE_VIDEO_LOCAL_RTP_INFO, local_video_rtp) )
{
SdpMediaDescription* videoMD = mdList->addItem();
SdpMedia* videoMedia = videoMD->media();
videoMedia->setMedia("video");
videoMedia->setPort(local_video_rtp.getPort());
videoMedia->setTransport("RTP/AVP");
videoMedia->setNumPorts(1);
SdpAttributeList* videoAttrList = videoMD->attributeList();
SdpAttribute* videoAttribute = videoAttrList->addItem();
Coders::video_coders_t::const_iterator j;
for ( j = videoCoders.begin(); j != videoCoders.end(); ++j )
{
const VideoCoderInfo& video_coder_info = *j;
if ( (video_coder_info.coderType == CODER_TYPE_H263 ) ||
(video_coder_info.coderType == CODER_TYPE_H263_1998) )
{
std::stringstream payload;
payload.str("");
payload << video_coder_info.coderPayloadType;
videoMedia->addFormat(payload.str().c_str());
std::stringstream rtpmap;
rtpmap.str("");
rtpmap << video_coder_info.coderPayloadType << " ";
rtpmap << video_coder_info.encoding << '/' << (*j).bitRate;
videoAttribute->setProperty("rtpmap");
videoAttribute->setPropertyValue(rtpmap.str().c_str());
videoAttribute = videoAttrList->addItem();
videoAttribute->setProperty("fmtp");
std::stringstream fmtp;
fmtp.str("");
fmtp << video_coder_info.coderPayloadType << " ";
std::vector::const_iterator k = video_coder_info.available_fmts.begin();
while ( k != video_coder_info.available_fmts.end() )
{
if ( ((*k).imageWidth == VIDEO_IMAGE_WIDTH_352) &&
((*k).imageHeight == VIDEO_IMAGE_HEIGHT_288) )
{
fmtp << "CIF=";
}
else if ( ((*k).imageWidth == VIDEO_IMAGE_WIDTH_176) &&
((*k).imageHeight == VIDEO_IMAGE_HEIGHT_144) )
{
fmtp << "QCIF=";
}
else
{
;
}
fmtp << fps2mpi((*k).framesPerSec);
if ( ++k != video_coder_info.available_fmts.end() )
{
fmtp << ';';
}
}
videoAttribute->setPropertyValue(fmtp.str().c_str());
}
videoAttribute = videoAttrList->addItem();
videoAttribute->setProperty("sendrecv");
videoAttribute->setPropertyValue("");
std::stringstream bandwidth;
bandwidth << video_coder_info.bitRate / 1000;
videoMD->bandwidth()->setModifier("AS");
videoMD->bandwidth()->setBandwidthValue(bandwidth.str().c_str());
}
}
}
}
/*
* Answer sdpS return all 'm' (media) headers that were 'offered'.
* Unused/unsupported ones have their port set to 0.
*/
void IpmDevice::buildAnswerSdp( SdpSessionDescription& sdp ) const
{
/* get here so it can be used for the origin address */
IpInfo local_audio_rtp;
getLocalMediaInfo(MEDIATYPE_AUDIO_LOCAL_RTP_INFO, local_audio_rtp);
sdp.version()->setVersion("0");
sdp.origin()->setUserName("DiaStarServer");
sdp.origin()->setNetworkType("IN");
sdp.origin()->setAddressType("IP4");
sdp.origin()->setAddress(local_audio_rtp.getAddress().c_str());
/* use a common c= for all media */
sdp.connection()->setNetworkType("IN");
sdp.connection()->setAddressType("IP4");
sdp.connection()->setAddress(local_audio_rtp.getAddress().c_str());
time_t t;
time(&t);
std::stringstream sessionIdandVersion;
sessionIdandVersion << t;
sdp.origin()->setSessionId(sessionIdandVersion.str().c_str());
sdp.origin()->setVersion(sessionIdandVersion.str().c_str());
sdp.sessionName()->setName("DiaStarServer");
SdpTimeDescription* timeDescription = sdp.timeDescriptionList()->addItem();
timeDescription->time()->setStart(0);
timeDescription->time()->setStop(0);
/*
* media
*/
SdpMediaDescriptionList* mdList = sdp.mediaDescriptionList();
mdList->clear();
/* m=audio
*/
RemoteMedia::audio_t::const_iterator i;
for ( i = offered_media_.audio.begin(); i != offered_media_.audio.end(); ++i )
{
/* chosen codec
*/
if ( remoteAudioCoder_ == (*i).coder )
{
SdpMediaDescription* audioMD = mdList->addItem();
SdpMedia* audioMedia = audioMD->media();
audioMedia->setMedia("audio");
audioMedia->setPort(local_audio_rtp.getPort());
audioMedia->setTransport("RTP/AVP");
audioMedia->setNumPorts(1);
SdpAttributeList* audioAttrList = audioMD->attributeList();
SdpAttribute* audioAttribute = audioAttrList->addItem();
audioAttribute->setProperty("rtpmap");
if ( remoteAudioCoder_.coderType == CODER_TYPE_G711ULAW64K )
{
audioMedia->addFormat("0");
audioAttribute->setPropertyValue("0 PCMU/8000");
}
else if ( remoteAudioCoder_.coderType == CODER_TYPE_G711ALAW64K )
{
audioMedia->addFormat("8");
audioAttribute->setPropertyValue("8 PCMA/8000");
}
else
{
;
}
audioMedia->addFormat("101");
audioAttribute = audioAttrList->addItem();
audioAttribute->setProperty("rtpmap");
audioAttribute->setPropertyValue("101 telephone-event/8000");
audioAttribute = audioAttrList->addItem();
audioAttribute->setProperty((*i).direction.c_str());
audioAttribute->setPropertyValue("");
break;
}
}
/* rejected media, send m= with port set to zero.
*/
if ( i == offered_media_.audio.end() )
{
for ( i = offered_media_.audio.begin(); i != offered_media_.audio.end(); ++i )
{
SdpMediaDescription* audioMD = mdList->addItem();
SdpMedia* audioMedia = audioMD->media();
audioMedia->setMedia("audio");
audioMedia->setPort(0); /* important! */
audioMedia->setTransport("RTP/AVP");
audioMedia->setNumPorts(1);
// TODO add formats
}
}
/* m=video
*/
IpInfo local_video_rtp;
getLocalMediaInfo(MEDIATYPE_VIDEO_LOCAL_RTP_INFO, local_video_rtp);
RemoteMedia::video_t::const_iterator j;
for ( j = offered_media_.video.begin(); j != offered_media_.video.end(); ++j )
{
/* chosen codec
*/
if ( remoteVideoCoder_ == (*j).coder )
{
SdpMediaDescription* videoMD = mdList->addItem();
SdpMedia* videoMedia = videoMD->media();
videoMedia->setMedia("video");
videoMedia->setPort(local_video_rtp.getPort());
videoMedia->setTransport("RTP/AVP");
videoMedia->setNumPorts(1);
if ( (remoteVideoCoder_.coderType == CODER_TYPE_H263 ) ||
(remoteVideoCoder_.coderType == CODER_TYPE_H263_1998) )
{
std::stringstream payload;
payload << remoteVideoCoder_.coderPayloadType;
videoMedia->addFormat(payload.str().c_str());
std::stringstream rtpmap;
rtpmap << remoteVideoCoder_.coderPayloadType << " ";
rtpmap << remoteVideoCoder_.encoding << '/' << remoteVideoCoder_.bitRate;
SdpAttributeList* videoAttrList = videoMD->attributeList();
SdpAttribute* videoAttribute = videoAttrList->addItem();
videoAttribute->setProperty("rtpmap");
videoAttribute->setPropertyValue(rtpmap.str().c_str());
videoAttribute = videoAttrList->addItem();
videoAttribute->setProperty("fmtp");
std::stringstream fmtp;
if ( (remoteVideoCoder_.active_fmt.imageWidth == VIDEO_IMAGE_WIDTH_352) &&
(remoteVideoCoder_.active_fmt.imageHeight == VIDEO_IMAGE_HEIGHT_288) )
{
fmtp << remoteVideoCoder_.coderPayloadType << " CIF=";
}
else if ( (remoteVideoCoder_.active_fmt.imageWidth == VIDEO_IMAGE_WIDTH_176) &&
(remoteVideoCoder_.active_fmt.imageHeight == VIDEO_IMAGE_HEIGHT_144) )
{
fmtp << remoteVideoCoder_.coderPayloadType << " QCIF=";
}
else
{
;
}
fmtp << fps2mpi(remoteVideoCoder_.active_fmt.framesPerSec);
videoAttribute->setPropertyValue(fmtp.str().c_str());
videoAttribute = videoAttrList->addItem();
videoAttribute->setProperty((*j).direction.c_str());
videoAttribute->setPropertyValue("");
std::stringstream bandwidth;
bandwidth << remoteVideoCoder_.bitRate / 1000;
videoMD->bandwidth()->setModifier("AS");
videoMD->bandwidth()->setBandwidthValue(bandwidth.str().c_str());
}
/* more coders */
break;
}
}
/* rejected media, send m= with port set to zero.
*/
if ( j == offered_media_.video.end() )
{
for ( j = offered_media_.video.begin(); j != offered_media_.video.end(); ++j )
{
SdpMediaDescription* videoMD = mdList->addItem();
SdpMedia* videoMedia = videoMD->media();
videoMedia->setMedia("video");
videoMedia->setPort(0); /* important! */
videoMedia->setTransport("RTP/AVP");
videoMedia->setNumPorts(1);
// TODO add formats
}
}
}
/*
* Extract the media details from the sdp and choose a coder from those offered.
*/
void IpmDevice::onSdp( SdpSessionDescription& sdp )
{
parseSdp(sdp); /* parses the sdp into offered_media_ */
setRemoteMediaInfo(MEDIATYPE_AUDIO_REMOTE_RTP_INFO, IpInfo()); /* clear */
const Coders::audio_coders_t& audioCoders = channelMgr_.getAudioCoders();
Coders::audio_coders_t::const_iterator i;
for ( i = audioCoders.begin(); i != audioCoders.end(); ++i )
{
RemoteMedia::audio_t::const_iterator j;
for ( j = offered_media_.audio.begin(); j != offered_media_.audio.end(); ++j )
{
if ( *i == (*j).coder )
{
LOGINFO("IpmDevice::onSdp() chose: \"" << (*i).encoding << "\" for audio");
remoteAudioCoder_ = (*j).coder;
setRemoteMediaInfo(MEDIATYPE_AUDIO_REMOTE_RTP_INFO, (*j).rtp);
break;
}
}
if ( j != offered_media_.audio.end() )
{
break;
}
}
setRemoteMediaInfo(MEDIATYPE_VIDEO_REMOTE_RTP_INFO, IpInfo()); /* clear */
const Coders::video_coders_t& videoCoders = channelMgr_.getVideoCoders();
Coders::video_coders_t::const_iterator k;
for ( k = videoCoders.begin(); k != videoCoders.end(); ++k )
{
RemoteMedia::video_t::const_iterator l;
for ( l = offered_media_.video.begin(); l != offered_media_.video.end(); ++l )
{
if ( *k == (*l).coder )
{
LOGINFO("IpmDevice::onSdp() chose: \"" << (*k).encoding << "\" for video");
remoteVideoCoder_ = (*l).coder;
/* use the first fmt offered, there is always at least one */
remoteVideoCoder_.active_fmt = (*l).coder.available_fmts[0];
setRemoteMediaInfo(MEDIATYPE_VIDEO_REMOTE_RTP_INFO, (*l).rtp);
break;
}
}
if ( l != offered_media_.video.end() )
{
break;
}
}
}
/*
* Extract media details from an sdp.
*/
void IpmDevice::parseSdp( SdpSessionDescription& sdp )
{
offered_media_.audio.clear();
offered_media_.video.clear();
std::string addr(sdp.connection()->getAddress());
std::string direction = "sendrecv";
SdpAttributeList* attributeList = sdp.attributeList();
int nAttribute = attributeList->numItem();
for ( int n = 0; n < nAttribute; n++ )
{
SdpAttribute* attribute = attributeList->getItem(n);
if ( !strcmp(attribute->getProperty(), "sendonly") )
{
direction = "sendonly";
}
if ( !strcmp(attribute->getProperty(), "recvonly") )
{
direction = "recvonly";
}
if ( !strcmp(attribute->getProperty(), "sendrecv") )
{
direction = "sendrecv";
}
if ( !strcmp(attribute->getProperty(), "inactive") )
{
direction = "inactive";
}
}
// TODO add direction check inside each media description
int num_media = sdp.mediaDescriptionList()->numItem();
for ( int i = 0; i < num_media; i++ )
{
SdpMediaDescription* md = sdp.mediaDescriptionList()->getItem(i);
/* m=audio
*/
SdpMedia* media = md->media();
if ( !strcmp(media->getMedia(), "audio") &&
!strcmp(media->getTransport(), "RTP/AVP") &&
(media->getPort() != 0) )
{
IpInfo rtp(addr, media->getPort());
int num_format = media->getNumFormat();
for ( int j = 0; j < num_format; j++ )
{
RemoteMedia::Audio audio_media;
audio_media.rtp = rtp;
audio_media.coder.coderPayloadType = atoi(media->getFormat(j));
audio_media.coder.coderType = CODER_TYPE_NONSTANDARD;
/* set defaults for fixed formats, these may be overridden by
* an fmtp attribute.
*/
if ( audio_media.coder.coderPayloadType == 0 ) /* PCMU */
{
audio_media.coder.encoding = "PCMU";
audio_media.coder.coderType = CODER_TYPE_G711ULAW64K;
audio_media.coder.coderFramesize = CODER_FRAMESIZE_20;
audio_media.coder.framesPerPkt = 1;
audio_media.coder.vadEnable = CODER_VAD_ENABLE;
audio_media.coder.redPayloadType = 0;
}
else if ( audio_media.coder.coderPayloadType == 8 ) /* PCMA */
{
audio_media.coder.encoding = "PCMA";
audio_media.coder.coderType = CODER_TYPE_G711ALAW64K;
audio_media.coder.coderFramesize = CODER_FRAMESIZE_20;
audio_media.coder.framesPerPkt = 1;
audio_media.coder.vadEnable = CODER_VAD_ENABLE;
audio_media.coder.redPayloadType = 0;
}
else
{
/* more payload types */
}
audio_media.direction = direction;
audio_media.rfc2833 = false;
offered_media_.audio.push_back(audio_media);
}
SdpAttributeList* attributeList = md->attributeList();
int nAttribute = attributeList->numItem();
for ( int k = 0; k < nAttribute; k++ )
{
SdpAttribute* attribute = attributeList->getItem(k);
if ( strcmp(attribute->getProperty(), "rtpmap") == 0 )
{
StringTokeniser rtpmap(attribute->getPropertyValue());
int payload = atoi(rtpmap.token(' ').c_str());
std::string encoding = rtpmap.token('/');
/* clockrate not used */
RemoteMedia::audio_t::iterator i;
for ( i = offered_media_.audio.begin(); i != offered_media_.audio.end(); ++i )
{
if ( (*i).coder.coderPayloadType == payload )
{
(*i).coder.encoding = encoding;
if ( strcasecmp((*i).coder.encoding.c_str(), "PCMU") == 0 )
{
(*i).coder.coderType = CODER_TYPE_G711ULAW64K;
}
else if ( strcasecmp((*i).coder.encoding.c_str(), "PCMA") == 0 )
{
(*i).coder.coderType = CODER_TYPE_G711ALAW64K;
}
else if ( strcasecmp((*i).coder.encoding.c_str(), "telephone-event") == 0 )
{
(*i).coder.coderType = CODER_TYPE_NONSTANDARD;
(*i).rfc2833 = true;
}
else
{
}
break;
}
}
}
else if ( strcmp(attribute->getProperty(), "fmtp") == 0 )
{
StringTokeniser fmtp(attribute->getPropertyValue());
int format = atoi(fmtp.token(' ').c_str());
RemoteMedia::audio_t::iterator i;
for ( i = offered_media_.audio.begin(); i != offered_media_.audio.end(); ++i )
{
if ( (*i).coder.coderPayloadType == format )
{
if ( ((*i).coder.coderType == CODER_TYPE_NONSTANDARD) && ((*i).rfc2833) )
{
LOGDEBUG("IpmDevice::parseSdp() rfc2833 " << fmtp.token());
/* TODO use this */
}
else
{
/* more payload types */
}
break;
}
}
}
else
{
/* more */
}
}
}
/* m=video
*/
if ( !strcmp(media->getMedia(), "video") &&
!strcmp(media->getTransport(), "RTP/AVP") &&
(media->getPort() != 0) )
{
IpInfo rtp(addr, media->getPort());
int num_format = media->getNumFormat();
for ( int j = 0; j < num_format; j++ )
{
RemoteMedia::Video video_media;
video_media.rtp = rtp;
video_media.coder.coderPayloadType = atoi(media->getFormat(j));
/* set defaults for fixed formats, these may be overridden by
* an fmtp attribute.
*/
if ( video_media.coder.coderPayloadType == 34 ) /* H.263 */
{
video_media.coder.encoding = "H263";
video_media.coder.coderType = CODER_TYPE_H263;
video_media.coder.profile = VIDEO_PROFILE_0_H263;
video_media.coder.level = VIDEO_LEVEL_10_H263;
video_media.coder.samplingRate = VIDEO_SAMPLING_RATE_DEFAULT;
video_media.coder.bitRate = 90000;
/* set default fmt, this will be removed if an
* fmtp attribute is found.
*/
VideoCoderInfo::Fmt fmt;
fmt.imageWidth = VIDEO_IMAGE_WIDTH_176;
fmt.imageHeight = VIDEO_IMAGE_HEIGHT_144;
fmt.framesPerSec = VIDEO_FRAMESPERSEC_15;
video_media.coder.available_fmts.push_back(fmt);
}
else
{
/* more known payload types */
}
video_media.direction = direction;
offered_media_.video.push_back(video_media);
}
SdpAttributeList* attributeList = md->attributeList();
int nAttribute = attributeList->numItem();
for ( int k = 0; k < nAttribute; k++ )
{
SdpAttribute* attribute = attributeList->getItem(k);
if ( strcmp(attribute->getProperty(), "rtpmap") == 0 )
{
StringTokeniser rtpmap(attribute->getPropertyValue());
int payload = atoi(rtpmap.token(' ').c_str());
std::string encoding = rtpmap.token('/');
/* clockrate not used */
RemoteMedia::video_t::iterator i;
for ( i = offered_media_.video.begin(); i != offered_media_.video.end(); ++i )
{
if ( (*i).coder.coderPayloadType == payload )
{
(*i).coder.encoding = encoding;
if ( strcasecmp((*i).coder.encoding.c_str(), "H263") == 0 )
{
(*i).coder.coderType = CODER_TYPE_H263;
}
else if ( strcasecmp((*i).coder.encoding.c_str(), "H263-1998") == 0 )
{
(*i).coder.coderType = CODER_TYPE_H263_1998;
(*i).coder.profile = VIDEO_PROFILE_0_H263;
(*i).coder.level = VIDEO_LEVEL_10_H263;
(*i).coder.samplingRate = VIDEO_SAMPLING_RATE_DEFAULT;
(*i).coder.bitRate = 90000;
/* set default fmt, this will be removed if an
* fmtp attribute is found.
*/
VideoCoderInfo::Fmt fmt;
fmt.imageWidth = VIDEO_IMAGE_WIDTH_176;
fmt.imageHeight = VIDEO_IMAGE_HEIGHT_144;
fmt.framesPerSec = VIDEO_FRAMESPERSEC_15;
(*i).coder.available_fmts.push_back(fmt);
}
else
{
/* more */
}
break;
}
}
}
else if ( strcmp(attribute->getProperty(), "fmtp") == 0 )
{
StringTokeniser fmtp(attribute->getPropertyValue());
int format = atoi(fmtp.token(' ').c_str());
RemoteMedia::video_t::iterator i;
for ( i = offered_media_.video.begin(); i != offered_media_.video.end(); ++i )
{
if ( (*i).coder.coderPayloadType == format )
{
if ( ((*i).coder.coderType == CODER_TYPE_H263) ||
((*i).coder.coderType == CODER_TYPE_H263_1998) )
{
/* e.g. fmtp:34 CIF=2;QCIF=1 */
VideoCoderInfo::Fmt fmt;
bool done = false;
while ( !done )
{
std::string size = fmtp.token('=');
trim(size);
if ( size.empty() )
{
break;
}
std::string mpi = fmtp.token(';');
trim(mpi);
if ( mpi.empty() )
{
mpi = fmtp.token();
done = true;
}
if ( size == "CIF" )
{
fmt.imageWidth = VIDEO_IMAGE_WIDTH_352;
fmt.imageHeight = VIDEO_IMAGE_HEIGHT_288;
}
else if ( size == "QCIF" )
{
fmt.imageWidth = VIDEO_IMAGE_WIDTH_176;
fmt.imageHeight = VIDEO_IMAGE_HEIGHT_144;
}
else
{
continue; /* ignore unsupported params */
}
int fps = mpi2fps(atoi(mpi.c_str()));
if ( fps > VIDEO_FRAMESPERSEC_30 )
{
fmt.framesPerSec = VIDEO_FRAMESPERSEC_30;
}
else
{
fmt.framesPerSec = (eVIDEO_FRAMESPERSEC)fps;
}
(*i).coder.available_fmts.push_back(fmt);
}
/* remove the default fmt */
if ( (*i).coder.available_fmts.size() > 1 )
{
(*i).coder.available_fmts.erase((*i).coder.available_fmts.begin());
}
}
else
{
/* more payload types */
}
break;
}
}
}
else
{
/* more attributes */
}
}
}
}
}
/* vim:ts=4:set nu:
* EOF
*/