diff options
Diffstat (limited to 'dom/media/webrtc/jsapi/RTCDTMFSender.cpp')
-rw-r--r-- | dom/media/webrtc/jsapi/RTCDTMFSender.cpp | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/dom/media/webrtc/jsapi/RTCDTMFSender.cpp b/dom/media/webrtc/jsapi/RTCDTMFSender.cpp new file mode 100644 index 0000000000..30355aca26 --- /dev/null +++ b/dom/media/webrtc/jsapi/RTCDTMFSender.cpp @@ -0,0 +1,159 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RTCDTMFSender.h" +#include "libwebrtcglue/MediaConduitInterface.h" +#include "transport/logging.h" +#include "RTCRtpTransceiver.h" +#include "nsITimer.h" +#include "mozilla/dom/RTCDTMFSenderBinding.h" +#include "mozilla/dom/RTCDTMFToneChangeEvent.h" +#include <algorithm> +#include <bitset> + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(RTCDTMFSender, DOMEventTargetHelper, + mTransceiver, mSendTimer) + +NS_IMPL_ADDREF_INHERITED(RTCDTMFSender, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(RTCDTMFSender, DOMEventTargetHelper) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCDTMFSender) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsITimerCallback) + NS_INTERFACE_MAP_ENTRY(nsINamed) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +LazyLogModule gDtmfLog("RTCDTMFSender"); + +RTCDTMFSender::RTCDTMFSender(nsPIDOMWindowInner* aWindow, + RTCRtpTransceiver* aTransceiver) + : DOMEventTargetHelper(aWindow), mTransceiver(aTransceiver) {} + +JSObject* RTCDTMFSender::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return RTCDTMFSender_Binding::Wrap(aCx, this, aGivenProto); +} + +static int GetDTMFToneCode(uint16_t c) { + const char* DTMF_TONECODES = "0123456789*#ABCD"; + + if (c == ',') { + // , is a special character indicating a 2 second delay + return -1; + } + + const char* i = strchr(DTMF_TONECODES, c); + MOZ_ASSERT(i); + return static_cast<int>(i - DTMF_TONECODES); +} + +static std::bitset<256> GetCharacterBitset(const std::string& aCharsInSet) { + std::bitset<256> result; + for (auto c : aCharsInSet) { + result[c] = true; + } + return result; +} + +static bool IsUnrecognizedChar(const char c) { + static const std::bitset<256> recognized = + GetCharacterBitset("0123456789ABCD#*,"); + return !recognized[c]; +} + +void RTCDTMFSender::SetPayloadType(int32_t aPayloadType, + int32_t aPayloadFrequency) { + MOZ_ASSERT(NS_IsMainThread()); + mPayloadType = Some(aPayloadType); + mPayloadFrequency = Some(aPayloadFrequency); +} + +void RTCDTMFSender::InsertDTMF(const nsAString& aTones, uint32_t aDuration, + uint32_t aInterToneGap, ErrorResult& aRv) { + if (!mTransceiver->CanSendDTMF()) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + std::string utf8Tones = NS_ConvertUTF16toUTF8(aTones).get(); + + std::transform(utf8Tones.begin(), utf8Tones.end(), utf8Tones.begin(), + [](const char c) { return std::toupper(c); }); + + if (std::any_of(utf8Tones.begin(), utf8Tones.end(), IsUnrecognizedChar)) { + aRv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); + return; + } + + CopyUTF8toUTF16(utf8Tones, mToneBuffer); + mDuration = std::clamp(aDuration, 40U, 6000U); + mInterToneGap = std::clamp(aInterToneGap, 30U, 6000U); + + if (mToneBuffer.Length()) { + StartPlayout(0); + } +} + +void RTCDTMFSender::StopPlayout() { + if (mSendTimer) { + mSendTimer->Cancel(); + mSendTimer = nullptr; + } +} + +void RTCDTMFSender::StartPlayout(uint32_t aDelay) { + if (!mSendTimer) { + mSendTimer = NS_NewTimer(); + mSendTimer->InitWithCallback(this, aDelay, nsITimer::TYPE_ONE_SHOT); + } +} + +nsresult RTCDTMFSender::Notify(nsITimer* timer) { + MOZ_ASSERT(NS_IsMainThread()); + StopPlayout(); + + if (!mTransceiver->IsSending()) { + return NS_OK; + } + + RTCDTMFToneChangeEventInit init; + if (!mToneBuffer.IsEmpty()) { + uint16_t toneChar = mToneBuffer.CharAt(0); + int tone = GetDTMFToneCode(toneChar); + + init.mTone.Assign(toneChar); + + mToneBuffer.Cut(0, 1); + + if (tone == -1) { + StartPlayout(2000); + } else { + // Reset delay if necessary + StartPlayout(mDuration + mInterToneGap); + mDtmfEvent.Notify(DtmfEvent(mPayloadType.ref(), mPayloadFrequency.ref(), + tone, mDuration)); + } + } + + RefPtr<RTCDTMFToneChangeEvent> event = + RTCDTMFToneChangeEvent::Constructor(this, u"tonechange"_ns, init); + DispatchTrustedEvent(event); + + return NS_OK; +} + +nsresult RTCDTMFSender::GetName(nsACString& aName) { + aName.AssignLiteral("RTCDTMFSender"); + return NS_OK; +} + +void RTCDTMFSender::GetToneBuffer(nsAString& aOutToneBuffer) { + aOutToneBuffer = mToneBuffer; +} + +} // namespace mozilla::dom + +#undef LOGTAG |