Source code

Revision control

Copy as Markdown

Other Tools

/* 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