| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "modules/mediarecorder/MediaRecorder.h" | 5 #include "modules/mediarecorder/MediaRecorder.h" |
| 6 | 6 |
| 7 #include "bindings/core/v8/Dictionary.h" | 7 #include "bindings/core/v8/Dictionary.h" |
| 8 #include "core/events/Event.h" | 8 #include "core/events/Event.h" |
| 9 #include "core/fileapi/Blob.h" | 9 #include "core/fileapi/Blob.h" |
| 10 #include "core/inspector/ConsoleMessage.h" | 10 #include "core/inspector/ConsoleMessage.h" |
| 11 #include "modules/EventTargetModules.h" | 11 #include "modules/EventTargetModules.h" |
| 12 #include "modules/mediarecorder/BlobEvent.h" | 12 #include "modules/mediarecorder/BlobEvent.h" |
| 13 #include "platform/blob/BlobData.h" | 13 #include "platform/blob/BlobData.h" |
| 14 #include "platform/network/mime/ContentType.h" | 14 #include "platform/network/mime/ContentType.h" |
| 15 #include "public/platform/Platform.h" | 15 #include "public/platform/Platform.h" |
| 16 #include "public/platform/WebMediaStream.h" | 16 #include "public/platform/WebMediaStream.h" |
| 17 #include "wtf/CurrentTime.h" |
| 17 #include "wtf/PtrUtil.h" | 18 #include "wtf/PtrUtil.h" |
| 18 #include <algorithm> | 19 #include <algorithm> |
| 19 #include <limits> | 20 #include <limits> |
| 20 | 21 |
| 21 namespace blink { | 22 namespace blink { |
| 22 | 23 |
| 23 namespace { | 24 namespace { |
| 24 | 25 |
| 25 const char* kDefaultMimeType = "video/webm"; | 26 const char* kDefaultMimeType = "video/webm"; |
| 26 | 27 |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 | 159 |
| 159 MediaRecorder::MediaRecorder(ExecutionContext* context, | 160 MediaRecorder::MediaRecorder(ExecutionContext* context, |
| 160 MediaStream* stream, | 161 MediaStream* stream, |
| 161 const MediaRecorderOptions& options, | 162 const MediaRecorderOptions& options, |
| 162 ExceptionState& exceptionState) | 163 ExceptionState& exceptionState) |
| 163 : SuspendableObject(context), | 164 : SuspendableObject(context), |
| 164 m_stream(stream), | 165 m_stream(stream), |
| 165 m_streamAmountOfTracks(stream->getTracks().size()), | 166 m_streamAmountOfTracks(stream->getTracks().size()), |
| 166 m_mimeType(options.hasMimeType() ? options.mimeType() : kDefaultMimeType), | 167 m_mimeType(options.hasMimeType() ? options.mimeType() : kDefaultMimeType), |
| 167 m_stopped(true), | 168 m_stopped(true), |
| 168 m_ignoreMutedMedia(true), | |
| 169 m_audioBitsPerSecond(0), | 169 m_audioBitsPerSecond(0), |
| 170 m_videoBitsPerSecond(0), | 170 m_videoBitsPerSecond(0), |
| 171 m_state(State::Inactive), | 171 m_state(State::Inactive), |
| 172 m_dispatchScheduledEventRunner(AsyncMethodRunner<MediaRecorder>::create( | 172 m_dispatchScheduledEventRunner(AsyncMethodRunner<MediaRecorder>::create( |
| 173 this, | 173 this, |
| 174 &MediaRecorder::dispatchScheduledEvent)) { | 174 &MediaRecorder::dispatchScheduledEvent)) { |
| 175 DCHECK(m_stream->getTracks().size()); | 175 DCHECK(m_stream->getTracks().size()); |
| 176 | 176 |
| 177 m_recorderHandler = | 177 m_recorderHandler = |
| 178 WTF::wrapUnique(Platform::current()->createMediaRecorderHandler()); | 178 WTF::wrapUnique(Platform::current()->createMediaRecorderHandler()); |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 272 scheduleDispatchEvent(Event::create(EventTypeNames::resume)); | 272 scheduleDispatchEvent(Event::create(EventTypeNames::resume)); |
| 273 } | 273 } |
| 274 | 274 |
| 275 void MediaRecorder::requestData(ExceptionState& exceptionState) { | 275 void MediaRecorder::requestData(ExceptionState& exceptionState) { |
| 276 if (m_state == State::Inactive) { | 276 if (m_state == State::Inactive) { |
| 277 exceptionState.throwDOMException( | 277 exceptionState.throwDOMException( |
| 278 InvalidStateError, | 278 InvalidStateError, |
| 279 "The MediaRecorder's state is '" + stateToString(m_state) + "'."); | 279 "The MediaRecorder's state is '" + stateToString(m_state) + "'."); |
| 280 return; | 280 return; |
| 281 } | 281 } |
| 282 writeData(nullptr /* data */, 0 /* length */, true /* lastInSlice */); | 282 writeData(nullptr /* data */, 0 /* length */, true /* lastInSlice */, |
| 283 WTF::currentTimeMS()); |
| 283 } | 284 } |
| 284 | 285 |
| 285 bool MediaRecorder::isTypeSupported(const String& type) { | 286 bool MediaRecorder::isTypeSupported(const String& type) { |
| 286 WebMediaRecorderHandler* handler = | 287 WebMediaRecorderHandler* handler = |
| 287 Platform::current()->createMediaRecorderHandler(); | 288 Platform::current()->createMediaRecorderHandler(); |
| 288 if (!handler) | 289 if (!handler) |
| 289 return false; | 290 return false; |
| 290 | 291 |
| 291 // If true is returned from this method, it only indicates that the | 292 // If true is returned from this method, it only indicates that the |
| 292 // MediaRecorder implementation is capable of recording Blob objects for the | 293 // MediaRecorder implementation is capable of recording Blob objects for the |
| (...skipping 25 matching lines...) Expand all Loading... |
| 318 if (m_stopped) | 319 if (m_stopped) |
| 319 return; | 320 return; |
| 320 | 321 |
| 321 m_stopped = true; | 322 m_stopped = true; |
| 322 m_stream.clear(); | 323 m_stream.clear(); |
| 323 m_recorderHandler.reset(); | 324 m_recorderHandler.reset(); |
| 324 } | 325 } |
| 325 | 326 |
| 326 void MediaRecorder::writeData(const char* data, | 327 void MediaRecorder::writeData(const char* data, |
| 327 size_t length, | 328 size_t length, |
| 328 bool lastInSlice) { | 329 bool lastInSlice, |
| 330 double timecode) { |
| 329 if (m_stopped && !lastInSlice) { | 331 if (m_stopped && !lastInSlice) { |
| 330 m_stopped = false; | 332 m_stopped = false; |
| 331 scheduleDispatchEvent(Event::create(EventTypeNames::start)); | 333 scheduleDispatchEvent(Event::create(EventTypeNames::start)); |
| 332 } | 334 } |
| 333 if (m_stream && m_streamAmountOfTracks != m_stream->getTracks().size()) { | 335 if (m_stream && m_streamAmountOfTracks != m_stream->getTracks().size()) { |
| 334 m_streamAmountOfTracks = m_stream->getTracks().size(); | 336 m_streamAmountOfTracks = m_stream->getTracks().size(); |
| 335 onError("Amount of tracks in MediaStream has changed."); | 337 onError("Amount of tracks in MediaStream has changed."); |
| 336 } | 338 } |
| 337 | 339 |
| 338 // TODO(mcasas): Act as |m_ignoredMutedMedia| instructs if |m_stream| track(s) | 340 // TODO(mcasas): Act as |m_ignoredMutedMedia| instructs if |m_stream| track(s) |
| 339 // is in muted() state. | 341 // is in muted() state. |
| 340 | 342 |
| 341 if (!m_blobData) { | 343 if (!m_blobData) { |
| 342 m_blobData = BlobData::create(); | 344 m_blobData = BlobData::create(); |
| 343 m_blobData->setContentType(m_mimeType); | 345 m_blobData->setContentType(m_mimeType); |
| 344 } | 346 } |
| 345 if (data) | 347 if (data) |
| 346 m_blobData->appendBytes(data, length); | 348 m_blobData->appendBytes(data, length); |
| 347 | 349 |
| 348 if (!lastInSlice) | 350 if (!lastInSlice) |
| 349 return; | 351 return; |
| 350 | 352 |
| 351 // Cache |m_blobData->length()| before release()ng it. | 353 // Cache |m_blobData->length()| before release()ng it. |
| 352 const long long blobDataLength = m_blobData->length(); | 354 const long long blobDataLength = m_blobData->length(); |
| 353 createBlobEvent(Blob::create( | 355 createBlobEvent(Blob::create(BlobDataHandle::create(std::move(m_blobData), |
| 354 BlobDataHandle::create(std::move(m_blobData), blobDataLength))); | 356 blobDataLength)), |
| 357 timecode); |
| 355 } | 358 } |
| 356 | 359 |
| 357 void MediaRecorder::onError(const WebString& message) { | 360 void MediaRecorder::onError(const WebString& message) { |
| 358 // TODO(mcasas): Beef up the Error Event and add the |message|, see | 361 // TODO(mcasas): Beef up the Error Event and add the |message|, see |
| 359 // https://github.com/w3c/mediacapture-record/issues/31 | 362 // https://github.com/w3c/mediacapture-record/issues/31 |
| 360 scheduleDispatchEvent(Event::create(EventTypeNames::error)); | 363 scheduleDispatchEvent(Event::create(EventTypeNames::error)); |
| 361 } | 364 } |
| 362 | 365 |
| 363 void MediaRecorder::createBlobEvent(Blob* blob) { | 366 void MediaRecorder::createBlobEvent(Blob* blob, double timecode) { |
| 364 // TODO(mcasas): Consider launching an Event with a TypedArray inside, see | 367 scheduleDispatchEvent( |
| 365 // https://github.com/w3c/mediacapture-record/issues/17. | 368 BlobEvent::create(EventTypeNames::dataavailable, blob, timecode)); |
| 366 scheduleDispatchEvent(BlobEvent::create(EventTypeNames::dataavailable, blob)); | |
| 367 } | 369 } |
| 368 | 370 |
| 369 void MediaRecorder::stopRecording() { | 371 void MediaRecorder::stopRecording() { |
| 370 DCHECK(m_state != State::Inactive); | 372 DCHECK(m_state != State::Inactive); |
| 371 m_state = State::Inactive; | 373 m_state = State::Inactive; |
| 372 | 374 |
| 373 m_recorderHandler->stop(); | 375 m_recorderHandler->stop(); |
| 374 | 376 |
| 375 writeData(nullptr /* data */, 0 /* length */, true /* lastInSlice */); | 377 writeData(nullptr /* data */, 0 /* length */, true /* lastInSlice */, |
| 378 WTF::currentTimeMS()); |
| 376 scheduleDispatchEvent(Event::create(EventTypeNames::stop)); | 379 scheduleDispatchEvent(Event::create(EventTypeNames::stop)); |
| 377 } | 380 } |
| 378 | 381 |
| 379 void MediaRecorder::scheduleDispatchEvent(Event* event) { | 382 void MediaRecorder::scheduleDispatchEvent(Event* event) { |
| 380 m_scheduledEvents.push_back(event); | 383 m_scheduledEvents.push_back(event); |
| 381 | 384 |
| 382 m_dispatchScheduledEventRunner->runAsync(); | 385 m_dispatchScheduledEventRunner->runAsync(); |
| 383 } | 386 } |
| 384 | 387 |
| 385 void MediaRecorder::dispatchScheduledEvent() { | 388 void MediaRecorder::dispatchScheduledEvent() { |
| 386 HeapVector<Member<Event>> events; | 389 HeapVector<Member<Event>> events; |
| 387 events.swap(m_scheduledEvents); | 390 events.swap(m_scheduledEvents); |
| 388 | 391 |
| 389 for (const auto& event : events) | 392 for (const auto& event : events) |
| 390 dispatchEvent(event); | 393 dispatchEvent(event); |
| 391 } | 394 } |
| 392 | 395 |
| 393 DEFINE_TRACE(MediaRecorder) { | 396 DEFINE_TRACE(MediaRecorder) { |
| 394 visitor->trace(m_stream); | 397 visitor->trace(m_stream); |
| 395 visitor->trace(m_dispatchScheduledEventRunner); | 398 visitor->trace(m_dispatchScheduledEventRunner); |
| 396 visitor->trace(m_scheduledEvents); | 399 visitor->trace(m_scheduledEvents); |
| 397 EventTargetWithInlineData::trace(visitor); | 400 EventTargetWithInlineData::trace(visitor); |
| 398 SuspendableObject::trace(visitor); | 401 SuspendableObject::trace(visitor); |
| 399 } | 402 } |
| 400 | 403 |
| 401 } // namespace blink | 404 } // namespace blink |
| OLD | NEW |