michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "CSFLog.h" michael@0: #include "timecard.h" michael@0: michael@0: #include "CC_Common.h" michael@0: michael@0: #include "CC_SIPCCCall.h" michael@0: #include "CC_SIPCCCallInfo.h" michael@0: #include "VcmSIPCCBinding.h" michael@0: #include "CSFVideoTermination.h" michael@0: #include "CSFAudioTermination.h" michael@0: #include "CSFAudioControl.h" michael@0: michael@0: extern "C" michael@0: { michael@0: #include "ccapi_call.h" michael@0: #include "ccapi_call_listener.h" michael@0: #include "config_api.h" michael@0: } michael@0: michael@0: using namespace std; michael@0: using namespace CSF; michael@0: michael@0: static const char* logTag = "CC_SIPCCCall"; michael@0: michael@0: CSF_IMPLEMENT_WRAP(CC_SIPCCCall, cc_call_handle_t); michael@0: michael@0: CC_SIPCCCall::CC_SIPCCCall (cc_call_handle_t aCallHandle) : michael@0: callHandle(aCallHandle), michael@0: pMediaData(new CC_SIPCCCallMediaData(nullptr, false, false, -1)), michael@0: m_lock("CC_SIPCCCall") michael@0: { michael@0: CSFLogInfo( logTag, "Creating CC_SIPCCCall %u", callHandle ); michael@0: michael@0: AudioControl * audioControl = VcmSIPCCBinding::getAudioControl(); michael@0: michael@0: if(audioControl) michael@0: { michael@0: pMediaData->volume = audioControl->getDefaultVolume(); michael@0: } michael@0: } michael@0: michael@0: michael@0: /* michael@0: CCAPI_CALL_EV_CAPABILITY -- From phone team: "...CCAPI_CALL_EV_CAPABILITY is generated for the capability changes but we decided to michael@0: suppress it if it's due to state changes. We found it redundant as a state change implicitly implies a michael@0: capability change. This event will still be generated if the capability changes without a state change. michael@0: CCAPI_CALL_EV_CALLINFO -- From phone team: "...CCAPI_CALL_EV_CALLINFO is generated for any caller id related changes including michael@0: called/calling/redirecting name/number etc..." michael@0: CCAPI_CALL_EV_PLACED_CALLINFO -- From phone team: "CCAPI_CALL_EV_PLACED_CALLINFO was a trigger to update the placed call history and michael@0: gives the actual number dialed out. I think this event can be deprecated." michael@0: */ michael@0: michael@0: /* michael@0: CallState michael@0: michael@0: REORDER: You get this if you misdial a number. michael@0: */ michael@0: michael@0: // This function sets the remote window parameters for the call. Note that it currently only tolerates a single michael@0: // video stream on the call and would need to be updated to handle multiple remote video streams for conferencing. michael@0: void CC_SIPCCCall::setRemoteWindow (VideoWindowHandle window) michael@0: { michael@0: VideoTermination * pVideo = VcmSIPCCBinding::getVideoTermination(); michael@0: pMediaData->remoteWindow = window; michael@0: michael@0: if (!pVideo) michael@0: { michael@0: CSFLogWarn( logTag, "setRemoteWindow: no video provider found"); michael@0: return; michael@0: } michael@0: michael@0: for (StreamMapType::iterator entry = pMediaData->streamMap.begin(); entry != pMediaData->streamMap.end(); entry++) michael@0: { michael@0: if (entry->second.isVideo) michael@0: { michael@0: // first video stream found michael@0: int streamId = entry->first; michael@0: pVideo->setRemoteWindow(streamId, pMediaData->remoteWindow); michael@0: michael@0: return; michael@0: } michael@0: } michael@0: CSFLogInfo( logTag, "setRemoteWindow:no video stream found in call %u", callHandle ); michael@0: } michael@0: michael@0: int CC_SIPCCCall::setExternalRenderer(VideoFormat vFormat, ExternalRendererHandle renderer) michael@0: { michael@0: VideoTermination * pVideo = VcmSIPCCBinding::getVideoTermination(); michael@0: pMediaData->extRenderer = renderer; michael@0: pMediaData->videoFormat = vFormat; michael@0: michael@0: if (!pVideo) michael@0: { michael@0: CSFLogWarn( logTag, "setExternalRenderer: no video provider found"); michael@0: return -1; michael@0: } michael@0: michael@0: for (StreamMapType::iterator entry = pMediaData->streamMap.begin(); entry != pMediaData->streamMap.end(); entry++) michael@0: { michael@0: if (entry->second.isVideo) michael@0: { michael@0: // first video stream found michael@0: int streamId = entry->first; michael@0: return pVideo->setExternalRenderer(streamId, pMediaData->videoFormat, pMediaData->extRenderer); michael@0: } michael@0: } michael@0: CSFLogInfo( logTag, "setExternalRenderer:no video stream found in call %u", callHandle ); michael@0: return -1; michael@0: } michael@0: michael@0: void CC_SIPCCCall::sendIFrame() michael@0: { michael@0: VideoTermination * pVideo = VcmSIPCCBinding::getVideoTermination(); michael@0: michael@0: if (pVideo) michael@0: { michael@0: pVideo->sendIFrame(callHandle); michael@0: } michael@0: } michael@0: michael@0: CC_CallInfoPtr CC_SIPCCCall::getCallInfo () michael@0: { michael@0: cc_callinfo_ref_t callInfo = CCAPI_Call_getCallInfo(callHandle); michael@0: CC_SIPCCCallInfoPtr callInfoPtr = CC_SIPCCCallInfo::wrap(callInfo); michael@0: callInfoPtr->setMediaData( pMediaData); michael@0: return callInfoPtr.get(); michael@0: } michael@0: michael@0: michael@0: // michael@0: // Operations - The following function are actions that can be taken the execute an operation on the Call, ie calls michael@0: // down to pSIPCC to originate a call, end a call etc. michael@0: // michael@0: michael@0: bool CC_SIPCCCall::originateCall (cc_sdp_direction_t video_pref, const string & digits) michael@0: { michael@0: return (CCAPI_Call_originateCall(callHandle, video_pref, digits.c_str()) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::answerCall (cc_sdp_direction_t video_pref) michael@0: { michael@0: return (CCAPI_Call_answerCall(callHandle, video_pref) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::hold (cc_hold_reason_t reason) michael@0: { michael@0: return (CCAPI_Call_hold(callHandle, reason) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::resume (cc_sdp_direction_t video_pref) michael@0: { michael@0: return (CCAPI_Call_resume(callHandle, video_pref) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::endCall() michael@0: { michael@0: return (CCAPI_Call_endCall(callHandle) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::sendDigit (cc_digit_t digit) michael@0: { michael@0: AudioTermination * pAudio = VcmSIPCCBinding::getAudioTermination(); michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: michael@0: // Convert public digit (as enum or char) to RFC2833 form. michael@0: int digitId = -1; michael@0: switch(digit) michael@0: { michael@0: case '0': michael@0: digitId = 0; michael@0: break; michael@0: case '1': michael@0: digitId = 1; michael@0: break; michael@0: case '2': michael@0: digitId = 2; michael@0: break; michael@0: case '3': michael@0: digitId = 3; michael@0: break; michael@0: case '4': michael@0: digitId = 4; michael@0: break; michael@0: case '5': michael@0: digitId = 5; michael@0: break; michael@0: case '6': michael@0: digitId = 6; michael@0: break; michael@0: case '7': michael@0: digitId = 7; michael@0: break; michael@0: case '8': michael@0: digitId = 8; michael@0: break; michael@0: case '9': michael@0: digitId = 9; michael@0: break; michael@0: case '*': michael@0: digitId = 10; michael@0: break; michael@0: case '#': michael@0: digitId = 11; michael@0: break; michael@0: case 'A': michael@0: digitId = 12; michael@0: break; michael@0: case 'B': michael@0: digitId = 13; michael@0: break; michael@0: case 'C': michael@0: digitId = 14; michael@0: break; michael@0: case 'D': michael@0: digitId = 15; michael@0: break; michael@0: case '+': michael@0: digitId = 16; michael@0: break; michael@0: } michael@0: michael@0: for (StreamMapType::iterator entry = pMediaData->streamMap.begin(); entry != pMediaData->streamMap.end(); entry++) michael@0: { michael@0: if (entry->second.isVideo == false) michael@0: { michael@0: // first is the streamId michael@0: if (pAudio->sendDtmf(entry->first, digitId) != 0) michael@0: { michael@0: // We have sent a digit, done. michael@0: break; michael@0: } michael@0: else michael@0: { michael@0: CSFLogWarn( logTag, "sendDigit:sendDtmf returned fail"); michael@0: } michael@0: } michael@0: } michael@0: return (CCAPI_Call_sendDigit(callHandle, digit) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::backspace() michael@0: { michael@0: return (CCAPI_Call_backspace(callHandle) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::redial (cc_sdp_direction_t video_pref) michael@0: { michael@0: return (CCAPI_Call_redial(callHandle, video_pref) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::initiateCallForwardAll() michael@0: { michael@0: return (CCAPI_Call_initiateCallForwardAll(callHandle) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::endConsultativeCall() michael@0: { michael@0: return (CCAPI_Call_endConsultativeCall(callHandle) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::conferenceStart (cc_sdp_direction_t video_pref) michael@0: { michael@0: return (CCAPI_Call_conferenceStart(callHandle, video_pref) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::conferenceComplete (CC_CallPtr otherLeg, cc_sdp_direction_t video_pref) michael@0: { michael@0: return (CCAPI_Call_conferenceComplete(callHandle, ((CC_SIPCCCall*)otherLeg.get())->callHandle, video_pref) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::transferStart (cc_sdp_direction_t video_pref) michael@0: { michael@0: return (CCAPI_Call_transferStart(callHandle, video_pref) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::transferComplete (CC_CallPtr otherLeg, michael@0: cc_sdp_direction_t video_pref) michael@0: { michael@0: return (CCAPI_Call_transferComplete(callHandle, ((CC_SIPCCCall*)otherLeg.get())->callHandle, video_pref) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::cancelTransferOrConferenceFeature() michael@0: { michael@0: return (CCAPI_Call_cancelTransferOrConferenceFeature(callHandle) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::directTransfer (CC_CallPtr target) michael@0: { michael@0: return (CCAPI_Call_directTransfer(callHandle, ((CC_SIPCCCall*)target.get())->callHandle) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::joinAcrossLine (CC_CallPtr target) michael@0: { michael@0: return (CCAPI_Call_joinAcrossLine(callHandle, ((CC_SIPCCCall*)target.get())->callHandle) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::blfCallPickup (cc_sdp_direction_t video_pref, const string & speed) michael@0: { michael@0: return (CCAPI_Call_blfCallPickup(callHandle, video_pref, speed.c_str()) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::select() michael@0: { michael@0: return (CCAPI_Call_select(callHandle) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::updateVideoMediaCap (cc_sdp_direction_t video_pref) michael@0: { michael@0: return (CCAPI_Call_updateVideoMediaCap(callHandle, video_pref) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::sendInfo (const string & infopackage, const string & infotype, const string & infobody) michael@0: { michael@0: return (CCAPI_Call_sendInfo(callHandle, infopackage.c_str(), infotype.c_str(), infobody.c_str()) == CC_SUCCESS); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::muteAudio(void) michael@0: { michael@0: return setAudioMute(true); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::unmuteAudio() michael@0: { michael@0: return setAudioMute(false); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::muteVideo() michael@0: { michael@0: return setVideoMute(true); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::unmuteVideo() michael@0: { michael@0: return setVideoMute(false); michael@0: } michael@0: michael@0: bool CC_SIPCCCall::setAudioMute(bool mute) michael@0: { michael@0: bool returnCode = false; michael@0: AudioTermination * pAudio = VcmSIPCCBinding::getAudioTermination(); michael@0: pMediaData->audioMuteState = mute; michael@0: // we need to set the mute status of all audio streams in the map michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: for (StreamMapType::iterator entry = pMediaData->streamMap.begin(); entry != pMediaData->streamMap.end(); entry++) michael@0: { michael@0: if (entry->second.isVideo == false) michael@0: { michael@0: // first is the streamId michael@0: if (pAudio->mute(entry->first, mute)) michael@0: { michael@0: // We have muted at least one stream michael@0: returnCode = true; michael@0: } michael@0: else michael@0: { michael@0: CSFLogWarn( logTag, "setAudioMute:audio mute returned fail"); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (CCAPI_Call_setAudioMute(callHandle, mute) != CC_SUCCESS) michael@0: { michael@0: returnCode = false; michael@0: } michael@0: michael@0: return returnCode; michael@0: } michael@0: michael@0: bool CC_SIPCCCall::setVideoMute(bool mute) michael@0: { michael@0: bool returnCode = false; michael@0: VideoTermination * pVideo = VcmSIPCCBinding::getVideoTermination(); michael@0: pMediaData->videoMuteState = mute; michael@0: // we need to set the mute status of all audio streams in the map michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: for (StreamMapType::iterator entry = pMediaData->streamMap.begin(); entry != pMediaData->streamMap.end(); entry++) michael@0: { michael@0: if (entry->second.isVideo == true) michael@0: { michael@0: // first is the streamId michael@0: if (pVideo->mute(entry->first, mute)) michael@0: { michael@0: // We have muted at least one stream michael@0: returnCode = true; michael@0: } michael@0: else michael@0: { michael@0: CSFLogWarn( logTag, "setVideoMute:video mute returned fail"); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (CCAPI_Call_setVideoMute(callHandle, mute) != CC_SUCCESS) michael@0: { michael@0: returnCode = false; michael@0: } michael@0: michael@0: return returnCode; michael@0: } michael@0: michael@0: void CC_SIPCCCall::addStream(int streamId, bool isVideo) michael@0: { michael@0: michael@0: CSFLogInfo( logTag, "addStream: %d video=%s callhandle=%u", michael@0: streamId, isVideo ? "TRUE" : "FALSE", callHandle); michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: pMediaData->streamMap[streamId].isVideo = isVideo; michael@0: } michael@0: // The new stream needs to be given any properties that the call has for it. michael@0: // At the moment the only candidate is the muted state michael@0: if (isVideo) michael@0: { michael@0: #ifndef NO_WEBRTC_VIDEO michael@0: VideoTermination * pVideo = VcmSIPCCBinding::getVideoTermination(); michael@0: michael@0: // if there is a window for this call apply it to the stream michael@0: if ( pMediaData->remoteWindow != nullptr) michael@0: { michael@0: pVideo->setRemoteWindow(streamId, pMediaData->remoteWindow); michael@0: } michael@0: else michael@0: { michael@0: CSFLogInfo( logTag, "addStream: remoteWindow is NULL"); michael@0: } michael@0: michael@0: if(pMediaData->extRenderer != nullptr) michael@0: { michael@0: pVideo->setExternalRenderer(streamId, pMediaData->videoFormat, pMediaData->extRenderer); michael@0: } michael@0: else michael@0: { michael@0: CSFLogInfo( logTag, "addStream: externalRenderer is NULL"); michael@0: michael@0: } michael@0: michael@0: michael@0: for (StreamMapType::iterator entry = pMediaData->streamMap.begin(); entry != pMediaData->streamMap.end(); entry++) michael@0: { michael@0: if (entry->second.isVideo == false) michael@0: { michael@0: // first is the streamId michael@0: pVideo->setAudioStreamId(entry->first); michael@0: } michael@0: } michael@0: if (!pVideo->mute(streamId, pMediaData->videoMuteState)) michael@0: { michael@0: CSFLogError( logTag, "setting video mute state failed for new stream: %d", streamId); michael@0: } else michael@0: { michael@0: CSFLogError( logTag, "setting video mute state SUCCEEDED for new stream: %d", streamId); michael@0: michael@0: } michael@0: #endif michael@0: } michael@0: else michael@0: { michael@0: AudioTermination * pAudio = VcmSIPCCBinding::getAudioTermination(); michael@0: if (!pAudio->mute(streamId, pMediaData->audioMuteState)) michael@0: { michael@0: CSFLogError( logTag, "setting audio mute state failed for new stream: %d", streamId); michael@0: } michael@0: if (!pAudio->setVolume(streamId, pMediaData->volume)) michael@0: { michael@0: CSFLogError( logTag, "setting volume state failed for new stream: %d", streamId); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void CC_SIPCCCall::removeStream(int streamId) michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: michael@0: if ( pMediaData->streamMap.erase(streamId) != 1) michael@0: { michael@0: CSFLogError( logTag, "removeStream stream that was never in the streamMap: %d", streamId); michael@0: } michael@0: } michael@0: michael@0: bool CC_SIPCCCall::setVolume(int volume) michael@0: { michael@0: bool returnCode = false; michael@0: michael@0: AudioTermination * pAudio = VcmSIPCCBinding::getAudioTermination(); michael@0: { michael@0: mozilla::MutexAutoLock lock(m_lock); michael@0: for (StreamMapType::iterator entry = pMediaData->streamMap.begin(); entry != pMediaData->streamMap.end(); entry++) michael@0: { michael@0: if (entry->second.isVideo == false) michael@0: { michael@0: // first is the streamId michael@0: int streamId = entry->first; michael@0: if (pAudio->setVolume(streamId, volume)) michael@0: { michael@0: //We have changed the volume on at least one stream, michael@0: //so persist the new volume as the call volume, michael@0: //and report success for the volume change, even if it michael@0: //fails on other streams michael@0: pMediaData->volume = volume; michael@0: returnCode = true; michael@0: } michael@0: else michael@0: { michael@0: CSFLogWarn( logTag, "setVolume:set volume on stream %d returned fail", michael@0: streamId); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return returnCode; michael@0: } michael@0: michael@0: CC_SIPCCCallMediaDataPtr CC_SIPCCCall::getMediaData() michael@0: { michael@0: return pMediaData; michael@0: } michael@0: michael@0: void CC_SIPCCCall::originateP2PCall (cc_sdp_direction_t video_pref, const std::string & digits, const std::string & ip) michael@0: { michael@0: CCAPI_Config_set_server_address(ip.c_str()); michael@0: CCAPI_Call_originateCall(callHandle, video_pref, digits.c_str()); michael@0: } michael@0: michael@0: /* michael@0: * This method works asynchronously, is an onCallEvent with the resulting SDP michael@0: */ michael@0: void CC_SIPCCCall::createOffer (cc_media_constraints_t *constraints, michael@0: Timecard *tc) { michael@0: CCAPI_CreateOffer(callHandle, constraints, tc); michael@0: } michael@0: /* michael@0: * This method works asynchronously, there is onCallEvent with the resulting SDP michael@0: */ michael@0: void CC_SIPCCCall::createAnswer (cc_media_constraints_t *constraints, michael@0: Timecard *tc) { michael@0: CCAPI_CreateAnswer(callHandle, constraints, tc); michael@0: michael@0: } michael@0: michael@0: void CC_SIPCCCall::setLocalDescription(cc_jsep_action_t action, michael@0: const std::string & sdp, michael@0: Timecard *tc) { michael@0: CCAPI_SetLocalDescription(callHandle, action, sdp.c_str(), tc); michael@0: } michael@0: michael@0: void CC_SIPCCCall::setRemoteDescription(cc_jsep_action_t action, michael@0: const std::string & sdp, michael@0: Timecard *tc) { michael@0: CCAPI_SetRemoteDescription(callHandle, action, sdp.c_str(), tc); michael@0: } michael@0: michael@0: void CC_SIPCCCall::setPeerConnection(const std::string& handle) michael@0: { michael@0: CSFLogDebug(logTag, "setPeerConnection"); michael@0: michael@0: peerconnection = handle; // Cache this here. we need it to make the CC_SIPCCCallInfo michael@0: CCAPI_SetPeerConnection(callHandle, handle.c_str()); michael@0: } michael@0: michael@0: const std::string& CC_SIPCCCall::getPeerConnection() const { michael@0: return peerconnection; michael@0: } michael@0: michael@0: void CC_SIPCCCall::addStream(cc_media_stream_id_t stream_id, michael@0: cc_media_track_id_t track_id, michael@0: cc_media_type_t media_type, michael@0: cc_media_constraints_t *constraints) { michael@0: CCAPI_AddStream(callHandle, stream_id, track_id, media_type, constraints); michael@0: } michael@0: michael@0: void CC_SIPCCCall::removeStream(cc_media_stream_id_t stream_id, cc_media_track_id_t track_id, cc_media_type_t media_type) { michael@0: CCAPI_RemoveStream(callHandle, stream_id, track_id, media_type); michael@0: } michael@0: michael@0: void CC_SIPCCCall::addICECandidate(const std::string & candidate, michael@0: const std::string & mid, michael@0: unsigned short level, michael@0: Timecard *tc) { michael@0: CCAPI_AddICECandidate(callHandle, candidate.c_str(), mid.c_str(), michael@0: (cc_level_t) level, tc); michael@0: }