RTCDTMFSender.cpp (4601B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "RTCDTMFSender.h" 6 7 #include <algorithm> 8 #include <bitset> 9 10 #include "RTCRtpTransceiver.h" 11 #include "libwebrtcglue/MediaConduitInterface.h" 12 #include "mozilla/dom/RTCDTMFSenderBinding.h" 13 #include "mozilla/dom/RTCDTMFToneChangeEvent.h" 14 #include "nsITimer.h" 15 #include "transport/logging.h" 16 17 namespace mozilla::dom { 18 19 NS_IMPL_CYCLE_COLLECTION_INHERITED(RTCDTMFSender, DOMEventTargetHelper, 20 mTransceiver, mSendTimer) 21 22 NS_IMPL_ADDREF_INHERITED(RTCDTMFSender, DOMEventTargetHelper) 23 NS_IMPL_RELEASE_INHERITED(RTCDTMFSender, DOMEventTargetHelper) 24 25 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCDTMFSender) 26 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 27 NS_INTERFACE_MAP_ENTRY(nsITimerCallback) 28 NS_INTERFACE_MAP_ENTRY(nsINamed) 29 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 30 31 LazyLogModule gDtmfLog("RTCDTMFSender"); 32 33 RTCDTMFSender::RTCDTMFSender(nsPIDOMWindowInner* aWindow, 34 RTCRtpTransceiver* aTransceiver) 35 : DOMEventTargetHelper(aWindow), mTransceiver(aTransceiver) {} 36 37 JSObject* RTCDTMFSender::WrapObject(JSContext* aCx, 38 JS::Handle<JSObject*> aGivenProto) { 39 return RTCDTMFSender_Binding::Wrap(aCx, this, aGivenProto); 40 } 41 42 static int GetDTMFToneCode(uint16_t c) { 43 const char* DTMF_TONECODES = "0123456789*#ABCD"; 44 45 if (c == ',') { 46 // , is a special character indicating a 2 second delay 47 return -1; 48 } 49 50 const char* i = strchr(DTMF_TONECODES, c); 51 MOZ_ASSERT(i); 52 return static_cast<int>(i - DTMF_TONECODES); 53 } 54 55 static std::bitset<256> GetCharacterBitset(const std::string& aCharsInSet) { 56 std::bitset<256> result; 57 for (auto c : aCharsInSet) { 58 result[c] = true; 59 } 60 return result; 61 } 62 63 static bool IsUnrecognizedChar(const char c) { 64 static const std::bitset<256> recognized = 65 GetCharacterBitset("0123456789ABCD#*,"); 66 return !recognized[c]; 67 } 68 69 void RTCDTMFSender::SetPayloadType(int32_t aPayloadType, 70 int32_t aPayloadFrequency) { 71 MOZ_ASSERT(NS_IsMainThread()); 72 mPayloadType = Some(aPayloadType); 73 mPayloadFrequency = Some(aPayloadFrequency); 74 } 75 76 bool RTCDTMFSender::CanInsertDTMF() const { 77 return mTransceiver->CanSendDTMF(); 78 } 79 80 void RTCDTMFSender::InsertDTMF(const nsAString& aTones, uint32_t aDuration, 81 uint32_t aInterToneGap, ErrorResult& aRv) { 82 if (!mTransceiver->CanSendDTMF()) { 83 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 84 return; 85 } 86 87 std::string utf8Tones = NS_ConvertUTF16toUTF8(aTones).get(); 88 89 std::transform(utf8Tones.begin(), utf8Tones.end(), utf8Tones.begin(), 90 [](const char c) { return std::toupper(c); }); 91 92 if (std::any_of(utf8Tones.begin(), utf8Tones.end(), IsUnrecognizedChar)) { 93 aRv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); 94 return; 95 } 96 97 CopyUTF8toUTF16(utf8Tones, mToneBuffer); 98 mDuration = std::clamp(aDuration, 40U, 6000U); 99 mInterToneGap = std::clamp(aInterToneGap, 30U, 6000U); 100 101 if (mToneBuffer.Length()) { 102 StartPlayout(0); 103 } 104 } 105 106 void RTCDTMFSender::StopPlayout() { 107 if (mSendTimer) { 108 mSendTimer->Cancel(); 109 mSendTimer = nullptr; 110 } 111 } 112 113 void RTCDTMFSender::StartPlayout(uint32_t aDelay) { 114 if (!mSendTimer) { 115 mSendTimer = NS_NewTimer(); 116 mSendTimer->InitWithCallback(this, aDelay, nsITimer::TYPE_ONE_SHOT); 117 } 118 } 119 120 nsresult RTCDTMFSender::Notify(nsITimer* timer) { 121 MOZ_ASSERT(NS_IsMainThread()); 122 StopPlayout(); 123 124 if (!mTransceiver->IsSending()) { 125 return NS_OK; 126 } 127 128 RTCDTMFToneChangeEventInit init; 129 if (!mToneBuffer.IsEmpty()) { 130 uint16_t toneChar = mToneBuffer.CharAt(0); 131 int tone = GetDTMFToneCode(toneChar); 132 133 init.mTone.Assign(toneChar); 134 135 mToneBuffer.Cut(0, 1); 136 137 if (tone == -1) { 138 StartPlayout(2000); 139 } else { 140 // Reset delay if necessary 141 StartPlayout(mDuration + mInterToneGap); 142 mDtmfEvent.Notify(DtmfEvent(mPayloadType.ref(), mPayloadFrequency.ref(), 143 tone, mDuration)); 144 } 145 } 146 147 RefPtr<RTCDTMFToneChangeEvent> event = 148 RTCDTMFToneChangeEvent::Constructor(this, u"tonechange"_ns, init); 149 DispatchTrustedEvent(event); 150 151 return NS_OK; 152 } 153 154 nsresult RTCDTMFSender::GetName(nsACString& aName) { 155 aName.AssignLiteral("RTCDTMFSender"); 156 return NS_OK; 157 } 158 159 void RTCDTMFSender::GetToneBuffer(nsAString& aOutToneBuffer) { 160 aOutToneBuffer = mToneBuffer; 161 } 162 163 } // namespace mozilla::dom 164 165 #undef LOGTAG