/* * 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('='); if ( size.empty() ) { break; } std::string mpi = fmtp.token(';'); 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 */