Cap the number of SourceBuffers that may be added to a MediaSource.
[WebKit-https.git] / Source / WebCore / Modules / mediasource / MediaSource.cpp
1 /*
2  * Copyright (C) 2012 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "MediaSource.h"
33
34 #if ENABLE(MEDIA_SOURCE)
35
36 #include "ContentType.h"
37 #include "Event.h"
38 #include "TimeRanges.h"
39 #include <wtf/Uint8Array.h>
40
41 namespace WebCore {
42
43 PassRefPtr<MediaSource> MediaSource::create(ScriptExecutionContext* context)
44 {
45     return adoptRef(new MediaSource(context));
46 }
47
48 MediaSource::MediaSource(ScriptExecutionContext* context)
49     : ContextDestructionObserver(context)
50     , m_readyState(closedKeyword())
51     , m_player(0)
52 {
53     m_sourceBuffers = SourceBufferList::create(scriptExecutionContext());
54     m_activeSourceBuffers = SourceBufferList::create(scriptExecutionContext());
55 }
56
57 SourceBufferList* MediaSource::sourceBuffers()
58 {
59     return m_sourceBuffers.get();
60 }
61
62 SourceBufferList* MediaSource::activeSourceBuffers()
63 {
64     // FIXME(91649): support track selection
65     return m_activeSourceBuffers.get();
66 }
67
68 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionCode& ec)
69 {
70     // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#dom-addsourcebuffer
71     // 1. If type is null or an empty then throw an INVALID_ACCESS_ERR exception and
72     // abort these steps.
73     if (type.isNull() || type.isEmpty()) {
74         ec = INVALID_ACCESS_ERR;
75         return 0;
76     }
77
78     // 2. If type contains a MIME type that is not supported ..., then throw a
79     // NOT_SUPPORTED_ERR exception and abort these steps.
80     ContentType contentType(type);
81     Vector<String> codecs = contentType.codecs();
82
83     if (!codecs.size()) {
84         ec = NOT_SUPPORTED_ERR;
85         return 0;
86     }
87     
88     // 4. If the readyState attribute is not in the "open" state then throw an
89     // INVALID_STATE_ERR exception and abort these steps.
90     if (!m_player || m_readyState != openKeyword()) {
91         ec = INVALID_STATE_ERR;
92         return 0;
93     }
94
95     // 5. Create a new SourceBuffer object and associated resources.
96     String id = m_sourceBuffers->generateUniqueId();
97     if (id == emptyString()) {
98         ec = QUOTA_EXCEEDED_ERR;
99         return 0;
100     }
101
102     RefPtr<SourceBuffer> buffer = SourceBuffer::create(id, this);
103
104     switch (m_player->sourceAddId(buffer->id(), contentType.type(), codecs)) {
105     case MediaPlayer::Ok:
106         // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
107         m_sourceBuffers->add(buffer);
108         m_activeSourceBuffers->add(buffer);
109         // 7. Return the new object to the caller.
110         return buffer.get();
111     case MediaPlayer::NotSupported:
112         // 2 (cont). If type contains a MIME type ... that is not supported with the types 
113         // specified for the other SourceBuffer objects in sourceBuffers, then throw
114         // a NOT_SUPPORTED_ERR exception and abort these steps.
115         ec = NOT_SUPPORTED_ERR;
116         return 0;
117     case MediaPlayer::ReachedIdLimit:
118         // 3 (cont). If the user agent can't handle any more SourceBuffer objects then throw 
119         // a QUOTA_EXCEEDED_ERR exception and abort these steps.
120         ec = QUOTA_EXCEEDED_ERR;
121         return 0;
122     }
123
124     ASSERT_NOT_REACHED();
125     return 0;
126 }
127
128 void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionCode& ec)
129 {
130     // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#dom-removesourcebuffer
131     // 1. If sourceBuffer is null then throw an INVALID_ACCESS_ERR exception and
132     // abort these steps.
133     if (!buffer) {
134         ec = INVALID_ACCESS_ERR;
135         return;
136     }
137
138     // 2. If sourceBuffers is empty then throw an INVALID_STATE_ERR exception and
139     // abort these steps.
140     if (!m_player || !m_sourceBuffers->length()) {
141         ec = INVALID_STATE_ERR;
142         return;
143     }
144
145     // 3. If sourceBuffer specifies an object that is not in sourceBuffers then
146     // throw a NOT_FOUND_ERR exception and abort these steps.
147     // 6. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event
148     // on that object.
149     if (!m_sourceBuffers->remove(buffer)) {
150         ec = NOT_FOUND_ERR;
151         return;
152     }
153
154     // 7. Destroy all resources for sourceBuffer.
155     m_activeSourceBuffers->remove(buffer);
156     m_player->sourceRemoveId(buffer->id());
157
158     // 4. Remove track information from audioTracks, videoTracks, and textTracks for all tracks 
159     // associated with sourceBuffer and fire a simple event named change on the modified lists.
160     // FIXME(91649): support track selection
161
162     // 5. If sourceBuffer is in activeSourceBuffers, then remove it from that list and fire a
163     // removesourcebuffer event on that object.
164     // FIXME(91649): support track selection
165 }
166
167 const String& MediaSource::readyState() const
168 {
169     return m_readyState;
170 }
171
172 void MediaSource::setReadyState(const String& state)
173 {
174     ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword());
175     if (m_readyState == state)
176         return;
177
178     String oldState = m_readyState;
179     m_readyState = state;
180
181     if (m_readyState == closedKeyword()) {
182         m_sourceBuffers->clear();
183         m_activeSourceBuffers->clear();
184         m_player = 0;
185         dispatchEvent(Event::create(eventNames().webkitsourcecloseEvent, false, false));
186         return;
187     }
188     
189     if (oldState == openKeyword() && m_readyState == endedKeyword()) {
190         dispatchEvent(Event::create(eventNames().webkitsourceendedEvent, false, false));
191         return;
192     }
193
194     if (m_readyState == openKeyword()) {
195         dispatchEvent(Event::create(eventNames().webkitsourceopenEvent, false, false));
196         return;
197     }
198 }
199
200 void MediaSource::endOfStream(const String& error, ExceptionCode& ec)
201 {
202     // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#dom-endofstream
203     // 1. If the readyState attribute is not in the "open" state then throw an
204     // INVALID_STATE_ERR exception and abort these steps.
205     if (!m_player || m_readyState != openKeyword()) {
206         ec = INVALID_STATE_ERR;
207         return;
208     }
209
210     MediaPlayer::EndOfStreamStatus eosStatus = MediaPlayer::EosNoError;
211
212     if (error.isNull() || error.isEmpty())
213         eosStatus = MediaPlayer::EosNoError;
214     else if (error == "network")
215         eosStatus = MediaPlayer::EosNetworkError;
216     else if (error == "decode")
217         eosStatus = MediaPlayer::EosDecodeError;
218     else {
219         ec = INVALID_ACCESS_ERR;
220         return;
221     }
222
223     // 2. Change the readyState attribute value to "ended".
224     setReadyState(endedKeyword());
225     m_player->sourceEndOfStream(eosStatus);
226 }
227
228 PassRefPtr<TimeRanges> MediaSource::buffered(const String& id, ExceptionCode& ec) const
229 {
230     if (!m_player || m_readyState == closedKeyword()) {
231         ec = INVALID_STATE_ERR;
232         return 0;
233     }
234
235     return m_player->sourceBuffered(id);
236 }
237
238 void MediaSource::append(const String& id, PassRefPtr<Uint8Array> data, ExceptionCode& ec)
239 {
240     if (!data.get()) {
241         ec = INVALID_ACCESS_ERR;
242         return;
243     }
244
245     if (!m_player || m_readyState != openKeyword()) {
246         ec = INVALID_STATE_ERR;
247         return;
248     }
249
250     if (!data->length())
251         return;
252
253     if (!m_player->sourceAppend(id, data->data(), data->length())) {
254         ec = SYNTAX_ERR;
255         return;
256     }
257 }
258
259 void MediaSource::abort(const String& id, ExceptionCode& ec)
260 {
261     if (!m_player || m_readyState != openKeyword()) {
262         ec = INVALID_STATE_ERR;
263         return;
264     }
265
266     if (!m_player->sourceAbort(id))
267         ASSERT_NOT_REACHED();
268 }
269
270 const AtomicString& MediaSource::interfaceName() const
271 {
272     return eventNames().interfaceForMediaSource;
273 }
274
275 ScriptExecutionContext* MediaSource::scriptExecutionContext() const
276 {
277     return ContextDestructionObserver::scriptExecutionContext();
278 }
279
280 EventTargetData* MediaSource::eventTargetData()
281 {
282     return &m_eventTargetData;
283 }
284
285 EventTargetData* MediaSource::ensureEventTargetData()
286 {
287     return &m_eventTargetData;
288 }
289
290 } // namespace WebCore
291
292 #endif