summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/jsapi/RTCDTMFSender.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/jsapi/RTCDTMFSender.cpp')
-rw-r--r--dom/media/webrtc/jsapi/RTCDTMFSender.cpp159
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