4578fe4ce8c4ec5eab3ee6b9ab4b5c3d56b9d08f
[WebKit-https.git] / Source / WebCore / Modules / mediasource / SourceBuffer.cpp
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 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 "SourceBuffer.h"
33
34 #if ENABLE(MEDIA_SOURCE)
35
36 #include "Event.h"
37 #include "GenericEventQueue.h"
38 #include "HTMLMediaElement.h"
39 #include "Logging.h"
40 #include "MediaSource.h"
41 #include "SourceBufferPrivate.h"
42 #include "TimeRanges.h"
43
44 namespace WebCore {
45
46 PassRef<SourceBuffer> SourceBuffer::create(PassRef<SourceBufferPrivate> sourceBufferPrivate, MediaSource* source)
47 {
48     RefPtr<SourceBuffer> sourceBuffer(adoptRef(new SourceBuffer(std::move(sourceBufferPrivate), source)));
49     sourceBuffer->suspendIfNeeded();
50     return sourceBuffer.releaseNonNull();
51 }
52
53 SourceBuffer::SourceBuffer(PassRef<SourceBufferPrivate> sourceBufferPrivate, MediaSource* source)
54     : ActiveDOMObject(source->scriptExecutionContext())
55     , m_private(std::move(sourceBufferPrivate))
56     , m_source(source)
57     , m_asyncEventQueue(*this)
58     , m_updating(false)
59     , m_timestampOffset(0)
60     , m_appendBufferTimer(this, &SourceBuffer::appendBufferTimerFired)
61 {
62     ASSERT(m_private);
63     ASSERT(m_source);
64
65     m_private->setClient(this);
66 }
67
68 SourceBuffer::~SourceBuffer()
69 {
70     ASSERT(isRemoved());
71
72     m_private->setClient(0);
73 }
74
75 PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionCode& ec) const
76 {
77     // Section 3.1 buffered attribute steps.
78     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
79     //    INVALID_STATE_ERR exception and abort these steps.
80     if (isRemoved()) {
81         ec = INVALID_STATE_ERR;
82         return 0;
83     }
84
85     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
86     return m_private->buffered();
87 }
88
89 double SourceBuffer::timestampOffset() const
90 {
91     return m_timestampOffset;
92 }
93
94 void SourceBuffer::setTimestampOffset(double offset, ExceptionCode& ec)
95 {
96     // Section 3.1 timestampOffset attribute setter steps.
97     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
98     //    INVALID_STATE_ERR exception and abort these steps.
99     if (isRemoved()) {
100         ec = INVALID_STATE_ERR;
101         return;
102     }
103
104     // 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
105     if (m_updating) {
106         ec = INVALID_STATE_ERR;
107         return;
108     }
109
110     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
111     // 4.1 Set the readyState attribute of the parent media source to "open"
112     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
113     m_source->openIfInEndedState();
114
115     // 5. If this object is waiting for the end of a media segment to be appended, then throw an INVALID_STATE_ERR
116     // and abort these steps.
117     if (!m_private->setTimestampOffset(offset)) {
118         ec = INVALID_STATE_ERR;
119         return;
120     }
121
122     // 6. Update the attribute to the new value.
123     m_timestampOffset = offset;
124 }
125
126 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionCode& ec)
127 {
128     // Section 3.2 appendBuffer()
129     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
130     // 1. If data is null then throw an INVALID_ACCESS_ERR exception and abort these steps.
131     if (!data) {
132         ec = INVALID_ACCESS_ERR;
133         return;
134     }
135
136     appendBufferInternal(static_cast<unsigned char*>(data->data()), data->byteLength(), ec);
137 }
138
139 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionCode& ec)
140 {
141     // Section 3.2 appendBuffer()
142     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
143     // 1. If data is null then throw an INVALID_ACCESS_ERR exception and abort these steps.
144     if (!data) {
145         ec = INVALID_ACCESS_ERR;
146         return;
147     }
148
149     appendBufferInternal(static_cast<unsigned char*>(data->baseAddress()), data->byteLength(), ec);
150 }
151
152 void SourceBuffer::abort(ExceptionCode& ec)
153 {
154     // Section 3.2 abort() method steps.
155     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
156     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
157     //    then throw an INVALID_STATE_ERR exception and abort these steps.
158     // 2. If the readyState attribute of the parent media source is not in the "open" state
159     //    then throw an INVALID_STATE_ERR exception and abort these steps.
160     if (isRemoved() || !m_source->isOpen()) {
161         ec = INVALID_STATE_ERR;
162         return;
163     }
164
165     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
166     abortIfUpdating();
167
168     // 4. Run the reset parser state algorithm.
169     m_private->abort();
170
171     // FIXME(229408) Add steps 5-6 update appendWindowStart & appendWindowEnd.
172 }
173
174
175 void SourceBuffer::abortIfUpdating()
176 {
177     // Section 3.2 abort() method step 3 substeps.
178     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
179
180     if (!m_updating)
181         return;
182
183     // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
184     m_appendBufferTimer.stop();
185     m_pendingAppendData.clear();
186
187     // 3.2. Set the updating attribute to false.
188     m_updating = false;
189
190     // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
191     scheduleEvent(eventNames().abortEvent);
192
193     // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
194     scheduleEvent(eventNames().updateendEvent);
195 }
196
197 void SourceBuffer::removedFromMediaSource()
198 {
199     if (isRemoved())
200         return;
201
202     m_private->removedFromMediaSource();
203     m_source = 0;
204     m_asyncEventQueue.close();
205 }
206
207 bool SourceBuffer::hasPendingActivity() const
208 {
209     return m_source;
210 }
211
212 void SourceBuffer::stop()
213 {
214     m_appendBufferTimer.stop();
215 }
216
217 bool SourceBuffer::isRemoved() const
218 {
219     return !m_source;
220 }
221
222 void SourceBuffer::scheduleEvent(const AtomicString& eventName)
223 {
224     RefPtr<Event> event = Event::create(eventName, false, false);
225     event->setTarget(this);
226
227     m_asyncEventQueue.enqueueEvent(event.release());
228 }
229
230 void SourceBuffer::appendBufferInternal(unsigned char* data, unsigned size, ExceptionCode& ec)
231 {
232     // Section 3.2 appendBuffer()
233     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
234
235     // Step 1 is enforced by the caller.
236     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an INVALID_STATE_ERR exception and abort these steps.
237     // 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
238     if (isRemoved() || m_updating) {
239         ec = INVALID_STATE_ERR;
240         return;
241     }
242
243     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
244     m_source->openIfInEndedState();
245
246     // Steps 5-6
247
248     // 7. Add data to the end of the input buffer.
249     m_pendingAppendData.append(data, size);
250
251     // 8. Set the updating attribute to true.
252     m_updating = true;
253
254     // 9. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
255     scheduleEvent(eventNames().updatestartEvent);
256
257     // 10. Asynchronously run the buffer append algorithm.
258     m_appendBufferTimer.startOneShot(0);
259 }
260
261 void SourceBuffer::appendBufferTimerFired(Timer<SourceBuffer>*)
262 {
263     ASSERT(m_updating);
264
265     // Section 3.5.4 Buffer Append Algorithm
266     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
267
268     // 1. Run the segment parser loop algorithm.
269     // Step 2 doesn't apply since we run Step 1 synchronously here.
270     size_t appendSize = m_pendingAppendData.size();
271     if (!appendSize) {
272         // Resize buffer for 0 byte appends so we always have a valid pointer.
273         // We need to convey all appends, even 0 byte ones to |m_private| so
274         // that it can clear its end of stream state if necessary.
275         m_pendingAppendData.resize(1);
276     }
277     m_private->append(m_pendingAppendData.data(), appendSize);
278
279     // 3. Set the updating attribute to false.
280     m_updating = false;
281     m_pendingAppendData.clear();
282
283     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
284     scheduleEvent(eventNames().updateEvent);
285
286     // 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
287     scheduleEvent(eventNames().updateendEvent);
288 }
289
290 void SourceBuffer::sourceBufferPrivateDidEndStream(SourceBufferPrivate*, const WTF::AtomicString& error)
291 {
292     m_source->endOfStream(error, IgnorableExceptionCode());
293 }
294
295 void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBufferPrivate*, const InitializationSegment&)
296 {
297 }
298
299 void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, PassRefPtr<MediaSample>)
300 {
301 }
302
303 bool SourceBuffer::sourceBufferPrivateHasAudio(const SourceBufferPrivate*) const
304 {
305     return false;
306 }
307
308 bool SourceBuffer::sourceBufferPrivateHasVideo(const SourceBufferPrivate*) const
309 {
310     return false;
311 }
312
313 } // namespace WebCore
314
315 #endif