tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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