1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GOOGLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "modules/mediastream/RTCDTMFSender.h"
28
29#include "bindings/core/v8/ExceptionMessages.h"
30#include "bindings/core/v8/ExceptionState.h"
31#include "core/dom/ExceptionCode.h"
32#include "core/dom/ExecutionContext.h"
33#include "modules/mediastream/MediaStreamTrack.h"
34#include "modules/mediastream/RTCDTMFToneChangeEvent.h"
35#include "public/platform/WebMediaStreamTrack.h"
36#include "public/platform/WebRTCDTMFSenderHandler.h"
37#include "public/platform/WebRTCPeerConnectionHandler.h"
38
39namespace blink {
40
41static const long minToneDurationMs = 70;
42static const long defaultToneDurationMs = 100;
43static const long maxToneDurationMs = 6000;
44static const long minInterToneGapMs = 50;
45static const long defaultInterToneGapMs = 50;
46
47RTCDTMFSender* RTCDTMFSender::create(ExecutionContext* context, WebRTCPeerConnectionHandler* peerConnectionHandler, MediaStreamTrack* track, ExceptionState& exceptionState)
48{
49    OwnPtr<WebRTCDTMFSenderHandler> handler = adoptPtr(peerConnectionHandler->createDTMFSender(track->component()));
50    if (!handler) {
51        exceptionState.throwDOMException(NotSupportedError, "The MediaStreamTrack provided is not an element of a MediaStream that's currently in the local streams set.");
52        return nullptr;
53    }
54
55    RTCDTMFSender* dtmfSender = adoptRefCountedGarbageCollectedWillBeNoop(new RTCDTMFSender(context, track, handler.release()));
56    dtmfSender->suspendIfNeeded();
57    return dtmfSender;
58}
59
60RTCDTMFSender::RTCDTMFSender(ExecutionContext* context, MediaStreamTrack* track, PassOwnPtr<WebRTCDTMFSenderHandler> handler)
61    : ActiveDOMObject(context)
62    , m_track(track)
63    , m_duration(defaultToneDurationMs)
64    , m_interToneGap(defaultInterToneGapMs)
65    , m_handler(handler)
66    , m_stopped(false)
67    , m_scheduledEventTimer(this, &RTCDTMFSender::scheduledEventTimerFired)
68{
69    m_handler->setClient(this);
70}
71
72RTCDTMFSender::~RTCDTMFSender()
73{
74}
75
76bool RTCDTMFSender::canInsertDTMF() const
77{
78    return m_handler->canInsertDTMF();
79}
80
81MediaStreamTrack* RTCDTMFSender::track() const
82{
83    return m_track.get();
84}
85
86String RTCDTMFSender::toneBuffer() const
87{
88    return m_handler->currentToneBuffer();
89}
90
91void RTCDTMFSender::insertDTMF(const String& tones, ExceptionState& exceptionState)
92{
93    insertDTMF(tones, defaultToneDurationMs, defaultInterToneGapMs, exceptionState);
94}
95
96void RTCDTMFSender::insertDTMF(const String& tones, long duration, ExceptionState& exceptionState)
97{
98    insertDTMF(tones, duration, defaultInterToneGapMs, exceptionState);
99}
100
101void RTCDTMFSender::insertDTMF(const String& tones, long duration, long interToneGap, ExceptionState& exceptionState)
102{
103    if (!canInsertDTMF()) {
104        exceptionState.throwDOMException(NotSupportedError, "The 'canInsertDTMF' attribute is false: this sender cannot send DTMF.");
105        return;
106    }
107
108    if (duration > maxToneDurationMs || duration < minToneDurationMs) {
109        exceptionState.throwDOMException(SyntaxError, ExceptionMessages::indexOutsideRange("duration", duration, minToneDurationMs, ExceptionMessages::ExclusiveBound, maxToneDurationMs, ExceptionMessages::ExclusiveBound));
110        return;
111    }
112
113    if (interToneGap < minInterToneGapMs) {
114        exceptionState.throwDOMException(SyntaxError, ExceptionMessages::indexExceedsMinimumBound("intertone gap", interToneGap, minInterToneGapMs));
115        return;
116    }
117
118    m_duration = duration;
119    m_interToneGap = interToneGap;
120
121    if (!m_handler->insertDTMF(tones, m_duration, m_interToneGap))
122        exceptionState.throwDOMException(SyntaxError, "Could not send provided tones, '" + tones + "'.");
123}
124
125void RTCDTMFSender::didPlayTone(const WebString& tone)
126{
127    scheduleDispatchEvent(RTCDTMFToneChangeEvent::create(tone));
128}
129
130const AtomicString& RTCDTMFSender::interfaceName() const
131{
132    return EventTargetNames::RTCDTMFSender;
133}
134
135ExecutionContext* RTCDTMFSender::executionContext() const
136{
137    return ActiveDOMObject::executionContext();
138}
139
140void RTCDTMFSender::stop()
141{
142    m_stopped = true;
143    m_handler->setClient(0);
144}
145
146void RTCDTMFSender::scheduleDispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
147{
148    m_scheduledEvents.append(event);
149
150    if (!m_scheduledEventTimer.isActive())
151        m_scheduledEventTimer.startOneShot(0, FROM_HERE);
152}
153
154void RTCDTMFSender::scheduledEventTimerFired(Timer<RTCDTMFSender>*)
155{
156    if (m_stopped)
157        return;
158
159    WillBeHeapVector<RefPtrWillBeMember<Event> > events;
160    events.swap(m_scheduledEvents);
161
162    WillBeHeapVector<RefPtrWillBeMember<Event> >::iterator it = events.begin();
163    for (; it != events.end(); ++it)
164        dispatchEvent((*it).release());
165}
166
167void RTCDTMFSender::trace(Visitor* visitor)
168{
169    visitor->trace(m_track);
170    visitor->trace(m_scheduledEvents);
171    EventTargetWithInlineData::trace(visitor);
172}
173
174} // namespace blink
175