Title: [237642] trunk
Revision
237642
Author
commit-qu...@webkit.org
Date
2018-10-31 10:19:27 -0700 (Wed, 31 Oct 2018)

Log Message

MediaRecorder should fire dataavailable event when all tracks are ended and stop() is called
https://bugs.webkit.org/show_bug.cgi?id=190778

Patch by YUHAN WU <yuhan...@apple.com> on 2018-10-31
Reviewed by Youenn Fablet.

LayoutTests/imported/w3c:

* web-platform-tests/mediacapture-record/BlobEvent-constructor-expected.txt:
* web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution-expected.txt: Added.
* web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution.html: Added.
* web-platform-tests/mediacapture-record/MediaRecorder-stop-expected.txt:
* web-platform-tests/mediacapture-record/MediaRecorder-stop.html:
* web-platform-tests/mediacapture-record/support/MediaRecorder-iframe.html: Added.

Source/WebCore:

Implement _javascript_ dispatch event dataavailable and _javascript_ exposed method stop().
Implement a mock string as the output buffer of MediaRecorder.
Remove the declaration of timecode in BlobEvent since it has not been implemented in MediaRecorder and MediaRecorderPrivate.

Tests: http/wpt/mediarecorder/MediaRecorder-dataavailable.html
       http/wpt/mediarecorder/MediaRecorder-mock-dataavailable.html
       imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution.html
       imported/w3c/web-platform-tests/mediacapture-record/support/MediaRecorder-iframe.html

* CMakeLists.txt:
* Modules/mediarecorder/BlobEvent.cpp: Added.
(WebCore::BlobEvent::create):
(WebCore::BlobEvent::BlobEvent):
(WebCore::BlobEvent::eventInterface const):
* Modules/mediarecorder/BlobEvent.h:
* Modules/mediarecorder/BlobEvent.idl:
* Modules/mediarecorder/MediaRecorder.cpp:
(WebCore::MediaRecorder::MediaRecorder):
(WebCore::MediaRecorder::~MediaRecorder):
(WebCore::MediaRecorder::stop):
(WebCore::MediaRecorder::startRecording):
(WebCore::MediaRecorder::stopRecording):
(WebCore::MediaRecorder::stopRecordingInternal):
(WebCore::MediaRecorder::didAddOrRemoveTrack):
(WebCore::MediaRecorder::trackEnded):
(WebCore::MediaRecorder::sampleBufferUpdated):
(WebCore::MediaRecorder::audioSamplesAvailable):
(WebCore::MediaRecorder::scheduleDeferredTask):
* Modules/mediarecorder/MediaRecorder.h:
* Modules/mediarecorder/MediaRecorder.idl:
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* dom/EventNames.h:
* dom/EventNames.in:
* platform/mediarecorder/MediaRecorderPrivate.h: Added.
* platform/mediarecorder/MediaRecorderPrivateMock.cpp: Added.
(WebCore::MediaRecorderPrivateMock::sampleBufferUpdated):
(WebCore::MediaRecorderPrivateMock::audioSamplesAvailable):
(WebCore::MediaRecorderPrivateMock::generateMockString):
(WebCore::MediaRecorderPrivateMock::fetchData):
* platform/mediarecorder/MediaRecorderPrivateMock.h: Added.

LayoutTests:

These tests are used to check if MediaRecorder can generate both video and audio buffers through mock source.

* http/wpt/mediarecorder/MediaRecorder-dataavailable-expected.txt: Added.
* http/wpt/mediarecorder/MediaRecorder-dataavailable.html: Added.
* http/wpt/mediarecorder/MediaRecorder-mock-dataavailable-expected.txt: Added.
* http/wpt/mediarecorder/MediaRecorder-mock-dataavailable.html: Added.
* platform/win/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (237641 => 237642)


--- trunk/LayoutTests/ChangeLog	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/LayoutTests/ChangeLog	2018-10-31 17:19:27 UTC (rev 237642)
@@ -1,3 +1,18 @@
+2018-10-31  YUHAN WU  <yuhan...@apple.com>
+
+        MediaRecorder should fire dataavailable event when all tracks are ended and stop() is called
+        https://bugs.webkit.org/show_bug.cgi?id=190778
+
+        Reviewed by Youenn Fablet.
+
+        These tests are used to check if MediaRecorder can generate both video and audio buffers through mock source.
+
+        * http/wpt/mediarecorder/MediaRecorder-dataavailable-expected.txt: Added.
+        * http/wpt/mediarecorder/MediaRecorder-dataavailable.html: Added.
+        * http/wpt/mediarecorder/MediaRecorder-mock-dataavailable-expected.txt: Added.
+        * http/wpt/mediarecorder/MediaRecorder-mock-dataavailable.html: Added.
+        * platform/win/TestExpectations:
+
 2018-10-31  Chris Dumez  <cdu...@apple.com>
 
         [PSON] When process-swapping for a POST request the HTTP body gets dropped

Added: trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-dataavailable-expected.txt (0 => 237642)


--- trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-dataavailable-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-dataavailable-expected.txt	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,5 @@
+
+PASS MediaRecorder will fire a dataavailable event with a blob data for a video-only stream when stop() is called 
+PASS MediaRecorder will fire a dataavailable event with a blob data for a audio-only stream when stop() is called 
+PASS MediaRecorder will fire a dataavailable event with a blob data for a video-audio stream when stop() is called 
+

Added: trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-dataavailable.html (0 => 237642)


--- trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-dataavailable.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-dataavailable.html	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,101 @@
+<!doctype html>
+<html>
+<head>
+    <title>MediaRecorder Dataavailable</title>
+    <link rel="help" href=""
+    <script src=""
+    <script src=""
+</head>
+<body>
+<canvas id="canvas" width="200" height="200">
+</canvas>
+<script>
+    var context;
+
+    function createVideoStream() {
+        const canvas = document.getElementById("canvas");
+        context = canvas.getContext('2d');
+        return canvas.captureStream();
+    }
+
+    function drawSomethingOnCanvas() {
+        context.fillStyle = "red";
+        context.fillRect(0, 0, 10, 10);
+    }
+
+    function dataAvailableAssertions(blobEvent) {
+        assert_true(blobEvent instanceof BlobEvent, 'the type of event should be BlobEvent');
+        assert_equals(blobEvent.type, 'dataavailable', 'the event type should be dataavailable');
+        assert_true(blobEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
+        assert_true(blobEvent.data instanceof Blob, 'the type of data should be Blob');
+        assert_true(blobEvent.data.size > 0, 'the blob should contain some buffers');
+    }
+
+    async_test(t => {
+        const video = createVideoStream();
+        const recorder = new MediaRecorder(video);
+
+        recorder._ondataavailable_ = t.step_func(blobEvent => {
+            dataAvailableAssertions(blobEvent);
+            t.done();
+        });
+        recorder.start();
+        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
+        drawSomethingOnCanvas();
+        setTimeout(() => {
+            recorder.stop();
+        }, 500)
+    }, 'MediaRecorder will fire a dataavailable event with a blob data for a video-only stream when stop() is called');
+
+    async_test(t => {
+        const ac = new AudioContext();
+        const osc = ac.createOscillator();
+        const dest = ac.createMediaStreamDestination();
+        const audio = dest.stream;
+        osc.connect(dest);
+        const recorder = new MediaRecorder(audio);
+
+        recorder._ondataavailable_ = t.step_func(blobEvent => {
+            dataAvailableAssertions(blobEvent);
+            t.done();
+        });
+        recorder.start();
+        osc.start();
+        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
+        setTimeout(() => {
+            recorder.stop();
+            osc.stop();
+        }, 500);
+    }, 'MediaRecorder will fire a dataavailable event with a blob data for a audio-only stream when stop() is called');
+
+    async_test(t => {
+        const ac = new AudioContext();
+        const osc = ac.createOscillator();
+        const dest = ac.createMediaStreamDestination();
+        const audio = dest.stream;
+        osc.connect(dest);
+
+        const video = createVideoStream();
+        assert_equals(video.getAudioTracks().length, 0, "video mediastream starts with no audio track");
+        assert_equals(audio.getAudioTracks().length, 1, "audio mediastream starts with one audio track");
+        video.addTrack(audio.getAudioTracks()[0]);
+        assert_equals(video.getAudioTracks().length, 1, "video mediastream starts with one audio track");
+        const recorder = new MediaRecorder(video);
+
+        recorder._ondataavailable_ = t.step_func(blobEvent => {
+            dataAvailableAssertions(blobEvent);
+            t.done();
+        });
+        recorder.start();
+        osc.start();
+        drawSomethingOnCanvas();
+        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
+        setTimeout(() => {
+            recorder.stop();
+            osc.stop();
+        }, 500);
+    }, 'MediaRecorder will fire a dataavailable event with a blob data for a video-audio stream when stop() is called');
+
+</script>
+</body>
+</html>
\ No newline at end of file

Added: trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-mock-dataavailable-expected.txt (0 => 237642)


--- trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-mock-dataavailable-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-mock-dataavailable-expected.txt	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,5 @@
+
+PASS MediaRecorder will fire a dataavailable event which only contains video buffers for a video-only stream when stop() is called 
+PASS MediaRecorder will fire a dataavailable event which only contains audio buffers for a audio-only stream when stop() is called 
+PASS MediaRecorder will fire a dataavailable event which only contains both video and audio buffers when stop() is called 
+

Added: trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-mock-dataavailable.html (0 => 237642)


--- trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-mock-dataavailable.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/mediarecorder/MediaRecorder-mock-dataavailable.html	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,106 @@
+<!doctype html>
+<html>
+<head>
+    <title>MediaRecorder Dataavailable</title>
+    <link rel="help" href=""
+    <script src=""
+    <script src=""
+</head>
+<body>
+<canvas id="canvas" width="200" height="200">
+</canvas>
+<script>
+    var context;
+    const reader = new FileReader();
+
+    function createVideoStream() {
+        const canvas = document.getElementById("canvas");
+        context = canvas.getContext('2d');
+        return canvas.captureStream();
+    }
+
+    function drawSomethingOnCanvas() {
+        context.fillStyle = "red";
+        context.fillRect(0, 0, 10, 10);
+    }
+
+    async_test(t => {
+        const video = createVideoStream();
+        const recorder = new MediaRecorder(video);
+
+        recorder._ondataavailable_ = t.step_func(blobEvent => {
+            reader.readAsText(blobEvent.data);
+            reader._onloadend_ = t.step_func(() => {
+                assert_not_equals(reader.result.indexOf('Video'), -1, 'MediaRecorder successfully captured video buffers');
+                assert_equals(reader.result.indexOf('Audio'), -1, 'MediaRecorder does not capture audio buffers for a video-only stream');
+                t.done();
+            });
+        });
+        recorder.start();
+        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
+        drawSomethingOnCanvas();
+        setTimeout(() => {
+            recorder.stop();
+        }, 500);
+    }, 'MediaRecorder will fire a dataavailable event which only contains video buffers for a video-only stream when stop() is called');
+
+    async_test(t => {
+        const ac = new AudioContext();
+        const osc = ac.createOscillator();
+        const dest = ac.createMediaStreamDestination();
+        const audio = dest.stream;
+        osc.connect(dest);
+        const recorder = new MediaRecorder(audio);
+
+        recorder._ondataavailable_ = t.step_func(blobEvent => {
+            reader.readAsText(blobEvent.data);
+            reader._onloadend_ = t.step_func(() => {
+                assert_not_equals(reader.result.indexOf('Audio'), -1, 'MediaRecorder successfully captured video buffers');
+                assert_equals(reader.result.indexOf('Video'), -1, 'MediaRecorder does not capture audio buffers for a video-only stream');
+                t.done();
+            });
+        });
+        recorder.start();
+        osc.start();
+        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
+        setTimeout(() => {
+            recorder.stop();
+            osc.stop();
+        }, 500);
+    }, 'MediaRecorder will fire a dataavailable event which only contains audio buffers for a audio-only stream when stop() is called');
+
+    async_test(t => {
+        const ac = new AudioContext();
+        const osc = ac.createOscillator();
+        const dest = ac.createMediaStreamDestination();
+        const audio = dest.stream;
+        osc.connect(dest);
+
+        const video = createVideoStream();
+        assert_equals(video.getAudioTracks().length, 0, "video mediastream starts with no audio track");
+        assert_equals(audio.getAudioTracks().length, 1, "audio mediastream starts with one audio track");
+        video.addTrack(audio.getAudioTracks()[0]);
+        assert_equals(video.getAudioTracks().length, 1, "video mediastream starts with one audio track");
+        const recorder = new MediaRecorder(video);
+
+        recorder._ondataavailable_ = t.step_func(blobEvent => {
+            reader.readAsText(blobEvent.data);
+            reader._onloadend_ = t.step_func(() => {
+                assert_not_equals(reader.result.indexOf('Audio'), -1, 'MediaRecorder successfully captured video buffers');
+                assert_not_equals(reader.result.indexOf('Video'), -1, 'MediaRecorder does not capture audio buffers for a video-only stream');
+                t.done();
+            });
+        });
+        recorder.start();
+        osc.start();
+        drawSomethingOnCanvas();
+        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
+        setTimeout(() => {
+            recorder.stop();
+            osc.stop();
+        }, 500);
+    }, 'MediaRecorder will fire a dataavailable event which only contains both video and audio buffers when stop() is called');
+
+</script>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (237641 => 237642)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2018-10-31 17:19:27 UTC (rev 237642)
@@ -1,3 +1,17 @@
+2018-10-31  YUHAN WU  <yuhan...@apple.com>
+
+        MediaRecorder should fire dataavailable event when all tracks are ended and stop() is called
+        https://bugs.webkit.org/show_bug.cgi?id=190778
+
+        Reviewed by Youenn Fablet.
+
+        * web-platform-tests/mediacapture-record/BlobEvent-constructor-expected.txt:
+        * web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution-expected.txt: Added.
+        * web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution.html: Added.
+        * web-platform-tests/mediacapture-record/MediaRecorder-stop-expected.txt:
+        * web-platform-tests/mediacapture-record/MediaRecorder-stop.html:
+        * web-platform-tests/mediacapture-record/support/MediaRecorder-iframe.html: Added.
+
 2018-10-30  Chris Dumez  <cdu...@apple.com>
 
         Resync XHR Web Platform Tests from upstream

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/BlobEvent-constructor-expected.txt (237641 => 237642)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/BlobEvent-constructor-expected.txt	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/BlobEvent-constructor-expected.txt	2018-10-31 17:19:27 UTC (rev 237642)
@@ -1,5 +1,5 @@
 
 PASS The BlobEventInit dictionary is required 
 PASS The BlobEventInit dictionary's data member is required. 
-FAIL The BlobEvent instance's data attribute is set. assert_equals: expected (object) object "[object Blob]" but got (undefined) undefined
+PASS The BlobEvent instance's data attribute is set. 
 

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution-expected.txt (0 => 237642)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution-expected.txt	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,4 @@
+
+PASS MediaRecorder will not fire the stop event when stop() is called and the script execution context is going away 
+PASS MediaRecorder will not fire the stop event when all tracks are ended and the script execution context is going away 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution.html (0 => 237642)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution.html	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset="utf-8">
+<html>
+<title>MediaRecorder destroy script execution context</title>
+<script src=""
+<script src=""
+<body>
+<iframe src="" id="subFrame-stop" name="subFrameStop"></iframe>
+<iframe src="" id="subFrame-allTrackEnded" name="subFrameAllTrackEnded"></iframe>
+<script>
+    var iframeForCallingStop = document.getElementById('subFrame-stop');
+    var iframeForAllTrackEnded = document.getElementById('subFrame-allTrackEnded');
+
+    var testForCallingStop = async_test('MediaRecorder will not fire the stop event when stop() is called and the script execution context is going away');
+    var testForAllTrackEnded = async_test('MediaRecorder will not fire the stop event when all tracks are ended and the script execution context is going away');
+
+    iframeForCallingStop._onload_ = function(e) {
+        subFrameStop.window.prepareForTest();
+        const recorder = subFrameStop.window.recorder;
+        recorder._ondataavailable_ = testForCallingStop.step_func(blobEvent => {
+            iframeForCallingStop.remove();
+            testForCallingStop.step_timeout(testForCallingStop.step_func_done(), 0);
+        });
+        recorder._onstop_ = testForCallingStop.unreached_func('Unexpected stop event');
+        recorder.start();
+        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
+        subFrameStop.window.drawSomethingOnCanvas();
+        recorder.stop();
+    };
+
+    iframeForAllTrackEnded._onload_ = function(e) {
+        subFrameAllTrackEnded.window.prepareForTest();
+        const recorder = subFrameAllTrackEnded.window.recorder;
+        recorder._ondataavailable_ = testForAllTrackEnded.step_func(blobEvent => {
+            iframeForAllTrackEnded.remove();
+            testForAllTrackEnded.step_timeout(testForAllTrackEnded.step_func_done(), 0);
+        });
+        recorder._onstop_ = testForAllTrackEnded.unreached_func('Unexpected stop event');
+        recorder.start();
+        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
+        subFrameAllTrackEnded.window.drawSomethingOnCanvas();
+        subFrameAllTrackEnded.window.video.getVideoTracks()[0].stop();
+    };
+
+</script>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-stop-expected.txt (237641 => 237642)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-stop-expected.txt	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-stop-expected.txt	2018-10-31 17:19:27 UTC (rev 237642)
@@ -1,3 +1,4 @@
 
 PASS MediaRecorder will stop recording and fire a stop event when all tracks are ended 
+PASS MediaRecorder will stop recording and fire a stop event when stop() is called 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-stop.html (237641 => 237642)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-stop.html	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-stop.html	2018-10-31 17:19:27 UTC (rev 237642)
@@ -29,7 +29,22 @@
         recorder.start();
         assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
         video.getVideoTracks()[0].stop();
-    }, "MediaRecorder will stop recording and fire a stop event when all tracks are ended", { timeout: 10000 });
+    }, "MediaRecorder will stop recording and fire a stop event when all tracks are ended");
+
+    async_test(t => {
+        let video = createVideoStream();
+        let recorder = new MediaRecorder(video);
+        recorder._onstop_ = t.step_func(errorEvent => {
+            assert_equals(errorEvent.type, 'stop', 'the error type should be stop');
+            assert_true(errorEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
+            assert_equals(recorder.state, "inactive", "MediaRecorder has been stopped when stop() is called");
+            t.done();
+        });
+        recorder.start();
+        assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
+        recorder.stop();
+        assert_equals(recorder.state, "recording", "State should remain the same until stop event is fired");
+    }, "MediaRecorder will stop recording and fire a stop event when stop() is called");
 </script>
 </body>
 </html>
\ No newline at end of file

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/support/MediaRecorder-iframe.html (0 => 237642)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/support/MediaRecorder-iframe.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/mediacapture-record/support/MediaRecorder-iframe.html	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Start a MediaRecorder</title>
+<html>
+<body>
+<canvas id="canvas" width="200px" height="200px"></canvas>
+<script>
+    var context;
+    var recorder;
+    var video;
+
+    function createVideoStream() {
+        const canvas = document.getElementById("canvas");
+        context = canvas.getContext('2d');
+        return canvas.captureStream();
+    }
+
+    function drawSomethingOnCanvas() {
+        context.fillStyle = "red";
+        context.fillRect(0, 0, 10, 10);
+    }
+
+    function prepareForTest() {
+        video = createVideoStream();
+        recorder = new MediaRecorder(video);
+    }
+</script>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/LayoutTests/platform/win/TestExpectations (237641 => 237642)


--- trunk/LayoutTests/platform/win/TestExpectations	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/LayoutTests/platform/win/TestExpectations	2018-10-31 17:19:27 UTC (rev 237642)
@@ -4222,9 +4222,9 @@
 webkit.org/b/190032 legacy-animation-engine/animations/stacking-context-unchanged-while-running.html [ Pass ImageOnlyFailure ]
 
 # MediaRecorder is not enabled on Windows
-imported/w3c/web-platform-tests/mediacapture-record/BlobEvent-constructor.html [ Skip ]
-imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-constructor.html [ Skip ]
+imported/w3c/web-platform-tests/mediacapture-record [ Skip ]
 fast/mediacapturefromelement/CanvasCaptureMediaStream-imagebitmaprenderingcontext.html [ Skip ]
+http/wpt/mediarecorder [ Skip ]
 
 webkit.org/b/190472 fast/forms/fieldset/fieldset-elements-htmlcollection.html [ Failure ]
 

Modified: trunk/Source/WebCore/CMakeLists.txt (237641 => 237642)


--- trunk/Source/WebCore/CMakeLists.txt	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/CMakeLists.txt	2018-10-31 17:19:27 UTC (rev 237642)
@@ -113,6 +113,7 @@
     "${WEBCORE_DIR}/platform/graphics/iso"
     "${WEBCORE_DIR}/platform/graphics/opentype"
     "${WEBCORE_DIR}/platform/graphics/transforms"
+    "${WEBCORE_DIR}/platform/mediarecorder"
     "${WEBCORE_DIR}/platform/mediastream"
     "${WEBCORE_DIR}/platform/mediastream/libwebrtc"
     "${WEBCORE_DIR}/platform/mock"

Modified: trunk/Source/WebCore/ChangeLog (237641 => 237642)


--- trunk/Source/WebCore/ChangeLog	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/ChangeLog	2018-10-31 17:19:27 UTC (rev 237642)
@@ -1,3 +1,52 @@
+2018-10-31  YUHAN WU  <yuhan...@apple.com>
+
+        MediaRecorder should fire dataavailable event when all tracks are ended and stop() is called
+        https://bugs.webkit.org/show_bug.cgi?id=190778
+
+        Reviewed by Youenn Fablet.
+
+        Implement _javascript_ dispatch event dataavailable and _javascript_ exposed method stop().
+        Implement a mock string as the output buffer of MediaRecorder.
+        Remove the declaration of timecode in BlobEvent since it has not been implemented in MediaRecorder and MediaRecorderPrivate. 
+
+        Tests: http/wpt/mediarecorder/MediaRecorder-dataavailable.html
+               http/wpt/mediarecorder/MediaRecorder-mock-dataavailable.html
+               imported/w3c/web-platform-tests/mediacapture-record/MediaRecorder-destroy-script-execution.html
+               imported/w3c/web-platform-tests/mediacapture-record/support/MediaRecorder-iframe.html
+
+        * CMakeLists.txt:
+        * Modules/mediarecorder/BlobEvent.cpp: Added.
+        (WebCore::BlobEvent::create):
+        (WebCore::BlobEvent::BlobEvent):
+        (WebCore::BlobEvent::eventInterface const):
+        * Modules/mediarecorder/BlobEvent.h:
+        * Modules/mediarecorder/BlobEvent.idl:
+        * Modules/mediarecorder/MediaRecorder.cpp:
+        (WebCore::MediaRecorder::MediaRecorder):
+        (WebCore::MediaRecorder::~MediaRecorder):
+        (WebCore::MediaRecorder::stop):
+        (WebCore::MediaRecorder::startRecording):
+        (WebCore::MediaRecorder::stopRecording):
+        (WebCore::MediaRecorder::stopRecordingInternal):
+        (WebCore::MediaRecorder::didAddOrRemoveTrack):
+        (WebCore::MediaRecorder::trackEnded):
+        (WebCore::MediaRecorder::sampleBufferUpdated):
+        (WebCore::MediaRecorder::audioSamplesAvailable):
+        (WebCore::MediaRecorder::scheduleDeferredTask):
+        * Modules/mediarecorder/MediaRecorder.h:
+        * Modules/mediarecorder/MediaRecorder.idl:
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/EventNames.h:
+        * dom/EventNames.in:
+        * platform/mediarecorder/MediaRecorderPrivate.h: Added.
+        * platform/mediarecorder/MediaRecorderPrivateMock.cpp: Added.
+        (WebCore::MediaRecorderPrivateMock::sampleBufferUpdated):
+        (WebCore::MediaRecorderPrivateMock::audioSamplesAvailable):
+        (WebCore::MediaRecorderPrivateMock::generateMockString):
+        (WebCore::MediaRecorderPrivateMock::fetchData):
+        * platform/mediarecorder/MediaRecorderPrivateMock.h: Added.
+
 2018-10-31  Claudio Saavedra  <csaave...@igalia.com>
 
         [GTK][WPE] Remaining topPrivatelyControlledDomain() fixes

Copied: trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.cpp (from rev 237641, trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.h) (0 => 237642)


--- trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.cpp	                        (rev 0)
+++ trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.cpp	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "config.h"
+#include "BlobEvent.h"
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "Blob.h"
+
+namespace WebCore {
+
+Ref<BlobEvent> BlobEvent::create(const AtomicString& type, Init&& init, IsTrusted isTrusted)
+{
+    return adoptRef(*new BlobEvent(type, WTFMove(init), isTrusted));
+}
+
+Ref<BlobEvent> BlobEvent::create(const AtomicString& type, CanBubble canBubble, IsCancelable isCancelable, Ref<Blob>&& data)
+{
+    return adoptRef(*new BlobEvent(type, canBubble, isCancelable, WTFMove(data)));
+}
+
+BlobEvent::BlobEvent(const AtomicString& type, Init&& init, IsTrusted isTrusted)
+    : Event(type, init, isTrusted)
+    , m_blob(init.data.releaseNonNull())
+{
+}
+
+BlobEvent::BlobEvent(const AtomicString& type, CanBubble canBubble, IsCancelable isCancelable, Ref<Blob>&& data)
+    : Event(type, canBubble, isCancelable)
+    , m_blob(WTFMove(data))
+{
+}
+    
+EventInterface BlobEvent::eventInterface() const
+{
+    return BlobEventInterfaceType;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)

Modified: trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.h (237641 => 237642)


--- trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.h	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.h	2018-10-31 17:19:27 UTC (rev 237642)
@@ -39,16 +39,19 @@
         double timecode;
     };
     
-    static Ref<BlobEvent> create(const AtomicString& type, Init&& init, IsTrusted isTrusted = IsTrusted::No)
-    {
-        return adoptRef(*new BlobEvent(type, WTFMove(init), isTrusted));
-    }
+    static Ref<BlobEvent> create(const AtomicString&, Init&&, IsTrusted = IsTrusted::No);
+    static Ref<BlobEvent> create(const AtomicString&, CanBubble, IsCancelable, Ref<Blob>&&);
 
+    Blob& data() const { return m_blob.get(); }
+    
 private:
-    BlobEvent(const AtomicString& type, Init&& init, IsTrusted isTrusted)
-        : Event(type, WTFMove(init), isTrusted)
-    {
-    }
+    BlobEvent(const AtomicString&, Init&&, IsTrusted);
+    BlobEvent(const AtomicString&, CanBubble, IsCancelable, Ref<Blob>&&);
+    
+    // Event
+    EventInterface eventInterface() const final;
+    
+    Ref<Blob> m_blob;
 };
     
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.idl (237641 => 237642)


--- trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.idl	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.idl	2018-10-31 17:19:27 UTC (rev 237642)
@@ -31,7 +31,7 @@
     Exposed=Window
 ]  interface BlobEvent : Event {
     // FIXME: Implement these:
-    // [SameObject] readonly attribute Blob data;
+    [SameObject] readonly attribute Blob data;
     // readonly attribute DOMHighResTimeStamp timecode;
 };
 

Modified: trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp (237641 => 237642)


--- trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp	2018-10-31 17:19:27 UTC (rev 237642)
@@ -28,9 +28,12 @@
 
 #if ENABLE(MEDIA_STREAM)
 
+#include "Blob.h"
+#include "BlobEvent.h"
 #include "Document.h"
 #include "EventNames.h"
 #include "MediaRecorderErrorEvent.h"
+#include "MediaRecorderPrivateMock.h"
 
 namespace WebCore {
 
@@ -45,11 +48,10 @@
     : ActiveDOMObject(&document)
     , m_options(WTFMove(option))
     , m_stream(WTFMove(stream))
+    , m_private(makeUniqueRef<MediaRecorderPrivateMock>()) // FIXME: we will need to decide which MediaRecorderPrivate instance to create based on the mock enabled feature flag
 {
-    m_tracks = WTF::map(m_stream->getTracks(), [this] (const auto& track) -> Ref<MediaStreamTrackPrivate> {
-        auto& privateTrack = track->privateTrack();
-        privateTrack.addObserver(*this);
-        return privateTrack;
+    m_tracks = WTF::map(m_stream->getTracks(), [] (auto&& track) -> Ref<MediaStreamTrackPrivate> {
+        return track->privateTrack();
     });
     m_stream->addObserver(this);
 }
@@ -57,13 +59,13 @@
 MediaRecorder::~MediaRecorder()
 {
     m_stream->removeObserver(this);
-    for (auto& track : m_tracks)
-        track->removeObserver(*this);
+    stopRecordingInternal();
 }
 
 void MediaRecorder::stop()
 {
     m_isActive = false;
+    stopRecordingInternal();
 }
 
 const char* MediaRecorder::activeDOMObjectName() const
@@ -76,16 +78,49 @@
     return false; // FIXME: We should do better here as this prevents entering PageCache.
 }
 
-ExceptionOr<void> MediaRecorder::start(std::optional<int> timeslice)
+ExceptionOr<void> MediaRecorder::startRecording(std::optional<int> timeslice)
 {
     UNUSED_PARAM(timeslice);
     if (state() != RecordingState::Inactive)
         return Exception { InvalidStateError, "The MediaRecorder's state must be inactive in order to start recording"_s };
     
+    for (auto& track : m_tracks)
+        track->addObserver(*this);
+
     m_state = RecordingState::Recording;
     return { };
 }
 
+ExceptionOr<void> MediaRecorder::stopRecording()
+{
+    if (state() == RecordingState::Inactive)
+        return Exception { InvalidStateError, "The MediaRecorder's state cannot be inactive"_s };
+
+    scheduleDeferredTask([this] {
+        if (!m_isActive || state() == RecordingState::Inactive)
+            return;
+
+        stopRecordingInternal();
+        ASSERT(m_state == RecordingState::Inactive);
+        dispatchEvent(BlobEvent::create(eventNames().dataavailableEvent, Event::CanBubble::No, Event::IsCancelable::No, m_private->fetchData()));
+        if (!m_isActive)
+            return;
+        dispatchEvent(Event::create(eventNames().stopEvent, Event::CanBubble::No, Event::IsCancelable::No));
+    });
+    return { };
+}
+
+void MediaRecorder::stopRecordingInternal()
+{
+    if (state() != RecordingState::Recording)
+        return;
+
+    for (auto& track : m_tracks)
+        track->removeObserver(*this);
+
+    m_state = RecordingState::Inactive;
+}
+
 void MediaRecorder::didAddOrRemoveTrack()
 {
     scheduleDeferredTask([this] {
@@ -103,15 +138,20 @@
     });
     if (position != notFound)
         return;
-    scheduleDeferredTask([this] {
-        if (!m_isActive || state() == RecordingState::Inactive)
-            return;
-        // FIXME: Add a dataavailable event
-        auto event = Event::create(eventNames().stopEvent, Event::CanBubble::No, Event::IsCancelable::No);
-        setNewRecordingState(RecordingState::Inactive, WTFMove(event));
-    });
+
+    stopRecording();
 }
 
+void MediaRecorder::sampleBufferUpdated(MediaStreamTrackPrivate& track, MediaSample& mediaSample)
+{
+    m_private->sampleBufferUpdated(track, mediaSample);
+}
+
+void MediaRecorder::audioSamplesAvailable(MediaStreamTrackPrivate& track, const MediaTime& mediaTime, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t sampleCount)
+{
+    m_private->audioSamplesAvailable(track, mediaTime, audioData, description, sampleCount);
+}
+
 void MediaRecorder::setNewRecordingState(RecordingState newState, Ref<Event>&& event)
 {
     m_state = newState;
@@ -124,10 +164,7 @@
     auto* scriptExecutionContext = this->scriptExecutionContext();
     if (!scriptExecutionContext)
         return;
-    scriptExecutionContext->postTask([weakThis = makeWeakPtr(*this), function = WTFMove(function)] (auto&) {
-        if (!weakThis)
-            return;
-
+    scriptExecutionContext->postTask([protectedThis = makeRef(*this), function = WTFMove(function)] (auto&) {
         function();
     });
 }

Modified: trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.h (237641 => 237642)


--- trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.h	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.h	2018-10-31 17:19:27 UTC (rev 237642)
@@ -25,14 +25,17 @@
 #pragma once
 
 #if ENABLE(MEDIA_STREAM)
+
 #include "ActiveDOMObject.h"
 #include "EventTarget.h"
 #include "MediaStream.h"
 #include "MediaStreamTrackPrivate.h"
+#include <wtf/UniqueRef.h>
 
 namespace WebCore {
 
 class Document;
+class MediaRecorderPrivate;
 
 class MediaRecorder final
     : public ActiveDOMObject
@@ -60,7 +63,8 @@
     using RefCounted::ref;
     using RefCounted::deref;
     
-    ExceptionOr<void> start(std::optional<int>);
+    ExceptionOr<void> startRecording(std::optional<int>);
+    ExceptionOr<void> stopRecording();
     
 private:
     MediaRecorder(Document&, Ref<MediaStream>&&, Options&& = { });
@@ -76,6 +80,8 @@
     const char* activeDOMObjectName() const final;
     bool canSuspendForDocumentSuspension() const final;
     
+    void stopRecordingInternal();
+    
     // MediaStream::Observer
     void didAddOrRemoveTrack() final;
     
@@ -84,6 +90,8 @@
     void trackMutedChanged(MediaStreamTrackPrivate&) final { };
     void trackSettingsChanged(MediaStreamTrackPrivate&) final { };
     void trackEnabledChanged(MediaStreamTrackPrivate&) final { };
+    void sampleBufferUpdated(MediaStreamTrackPrivate&, MediaSample&) final;
+    void audioSamplesAvailable(MediaStreamTrackPrivate&, const MediaTime&, const PlatformAudioData&, const AudioStreamDescription&, size_t) final;
     
     void scheduleDeferredTask(Function<void()>&&);
     void setNewRecordingState(RecordingState, Ref<Event>&&);
@@ -90,6 +98,7 @@
     
     Options m_options;
     Ref<MediaStream> m_stream;
+    UniqueRef<MediaRecorderPrivate> m_private;
     RecordingState m_state { RecordingState::Inactive };
     Vector<Ref<MediaStreamTrackPrivate>> m_tracks;
     

Modified: trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.idl (237641 => 237642)


--- trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.idl	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/Modules/mediarecorder/MediaRecorder.idl	2018-10-31 17:19:27 UTC (rev 237642)
@@ -38,7 +38,7 @@
     // readonly attribute DOMString mimeType;
     // attribute EventHandler onstart;
     attribute EventHandler onstop;
-    // attribute EventHandler ondataavailable;
+    attribute EventHandler ondataavailable;
     // attribute EventHandler onpause;
     // attribute EventHandler onresume;
     attribute EventHandler onerror;
@@ -45,8 +45,8 @@
     // readonly attribute unsigned long videoBitsPerSecond;
     // readonly attribute unsigned long audioBitsPerSecond;
 
-    [MayThrowException] void start(optional long timeslice);
-    // void stop();
+    [MayThrowException, ImplementedAs=startRecording] void start(optional long timeslice);
+    [ImplementedAs=stopRecording] void stop();
     // void pause();
     // void resume();
     // void requestData();

Modified: trunk/Source/WebCore/Sources.txt (237641 => 237642)


--- trunk/Source/WebCore/Sources.txt	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/Sources.txt	2018-10-31 17:19:27 UTC (rev 237642)
@@ -140,6 +140,7 @@
 
 Modules/mediacontrols/MediaControlsHost.cpp
 
+Modules/mediarecorder/BlobEvent.cpp
 Modules/mediarecorder/MediaRecorder.cpp
 Modules/mediarecorder/MediaRecorderErrorEvent.cpp
 
@@ -1767,6 +1768,8 @@
 
 platform/mediacapabilities/MediaEngineConfigurationFactory.cpp
 
+platform/mediarecorder/MediaRecorderPrivateMock.cpp
+
 platform/mediastream/CaptureDeviceManager.cpp
 platform/mediastream/MediaConstraints.cpp
 platform/mediastream/MediaEndpointConfiguration.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (237641 => 237642)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2018-10-31 17:19:27 UTC (rev 237642)
@@ -1360,6 +1360,7 @@
 		4BDA40012151B6F500FD6604 /* CSSRegisteredCustomProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BDA3FFB2151B6F400FD6604 /* CSSRegisteredCustomProperty.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		4D3B00AB215D69A70076B983 /* MediaRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B00A9215D69A70076B983 /* MediaRecorder.h */; };
 		4D3B00AF215D6A690076B983 /* BlobEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B00AD215D6A690076B983 /* BlobEvent.h */; };
+		4D3B5016217E58B700665DB1 /* MediaRecorderPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */; };
 		4DB7130D216ECB4D0096A4DD /* MediaRecorderErrorEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DB7130C216EC2BD0096A4DD /* MediaRecorderErrorEvent.h */; };
 		4E1959220A39DABA00220FE5 /* MediaFeatureNames.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E1959200A39DABA00220FE5 /* MediaFeatureNames.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		4E19592A0A39DACC00220FE5 /* MediaQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E1959240A39DACC00220FE5 /* MediaQuery.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -7920,6 +7921,10 @@
 		4D3B00A8215D69A60076B983 /* MediaRecorder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaRecorder.cpp; sourceTree = "<group>"; };
 		4D3B00A9215D69A70076B983 /* MediaRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaRecorder.h; sourceTree = "<group>"; };
 		4D3B00AD215D6A690076B983 /* BlobEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlobEvent.h; sourceTree = "<group>"; };
+		4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaRecorderPrivate.h; sourceTree = "<group>"; };
+		4D7EB3F4217C6AE600D64888 /* BlobEvent.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BlobEvent.cpp; sourceTree = "<group>"; };
+		4D9F6B642182532B0092A9C5 /* MediaRecorderPrivateMock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaRecorderPrivateMock.h; sourceTree = "<group>"; };
+		4D9F6B652182532B0092A9C5 /* MediaRecorderPrivateMock.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MediaRecorderPrivateMock.cpp; sourceTree = "<group>"; };
 		4DB7130A216EC2BC0096A4DD /* MediaRecorderErrorEvent.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MediaRecorderErrorEvent.idl; sourceTree = "<group>"; };
 		4DB7130C216EC2BD0096A4DD /* MediaRecorderErrorEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaRecorderErrorEvent.h; sourceTree = "<group>"; };
 		4DB7130E216EEBAE0096A4DD /* MediaRecorderErrorEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaRecorderErrorEvent.cpp; sourceTree = "<group>"; };
@@ -18037,6 +18042,7 @@
 		4D3B00A0215D5C0F0076B983 /* mediarecorder */ = {
 			isa = PBXGroup;
 			children = (
+				4D7EB3F4217C6AE600D64888 /* BlobEvent.cpp */,
 				4D3B00AD215D6A690076B983 /* BlobEvent.h */,
 				4D3B00A6215D645B0076B983 /* BlobEvent.idl */,
 				4D3B00A8215D69A60076B983 /* MediaRecorder.cpp */,
@@ -18049,6 +18055,16 @@
 			path = mediarecorder;
 			sourceTree = "<group>";
 		};
+		4D3B5012217E58A300665DB1 /* mediarecorder */ = {
+			isa = PBXGroup;
+			children = (
+				4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */,
+				4D9F6B652182532B0092A9C5 /* MediaRecorderPrivateMock.cpp */,
+				4D9F6B642182532B0092A9C5 /* MediaRecorderPrivateMock.h */,
+			);
+			path = mediarecorder;
+			sourceTree = "<group>";
+		};
 		510310421BA8C64C003329C0 /* client */ = {
 			isa = PBXGroup;
 			children = (
@@ -24831,6 +24847,7 @@
 				A59E3C1B11580F340072928E /* ios */,
 				6582A14809999D6C00BEEB6D /* mac */,
 				9AC6F02021148F1E00CBDA06 /* mediacapabilities */,
+				4D3B5012217E58A300665DB1 /* mediarecorder */,
 				C96F5EBF1B5872260091EA9D /* mediasession */,
 				07221B9217CF0AD400848E51 /* mediastream */,
 				59C77F101054591C00506104 /* mock */,
@@ -29945,6 +29962,7 @@
 				4471710E205AF945000A116E /* MediaQueryParserContext.h in Headers */,
 				4D3B00AB215D69A70076B983 /* MediaRecorder.h in Headers */,
 				4DB7130D216ECB4D0096A4DD /* MediaRecorderErrorEvent.h in Headers */,
+				4D3B5016217E58B700665DB1 /* MediaRecorderPrivate.h in Headers */,
 				C90843D01B18E47D00B68564 /* MediaRemoteControls.h in Headers */,
 				CD8ACA8F1D23971900ECC59E /* MediaRemoteSoftLink.h in Headers */,
 				CEEFCD7A19DB31F7003876D7 /* MediaResourceLoader.h in Headers */,

Modified: trunk/Source/WebCore/dom/EventNames.h (237641 => 237642)


--- trunk/Source/WebCore/dom/EventNames.h	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/dom/EventNames.h	2018-10-31 17:19:27 UTC (rev 237642)
@@ -101,6 +101,7 @@
     macro(copy) \
     macro(cuechange) \
     macro(cut) \
+    macro(dataavailable) \
     macro(datachannel) \
     macro(dblclick) \
     macro(devicechange) \

Modified: trunk/Source/WebCore/dom/EventNames.in (237641 => 237642)


--- trunk/Source/WebCore/dom/EventNames.in	2018-10-31 16:39:06 UTC (rev 237641)
+++ trunk/Source/WebCore/dom/EventNames.in	2018-10-31 17:19:27 UTC (rev 237642)
@@ -47,6 +47,7 @@
 ApplePayShippingMethodSelectedEvent conditional=APPLE_PAY
 ApplePayValidateMerchantEvent conditional=APPLE_PAY
 AudioProcessingEvent conditional=WEB_AUDIO
+BlobEvent conditional=MEDIA_STREAM
 OfflineAudioCompletionEvent conditional=WEB_AUDIO
 MediaRecorderErrorEvent conditional=MEDIA_STREAM
 MediaStreamTrackEvent conditional=MEDIA_STREAM

Copied: trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.h (from rev 237641, trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.h) (0 => 237642)


--- trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.h	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_STREAM)
+
+namespace WTF {
+class MediaTime;
+}
+
+namespace WebCore {
+
+class AudioStreamDescription;
+class Blob;
+class PlatformAudioData;
+class MediaSample;
+class MediaStreamTrackPrivate;
+
+class MediaRecorderPrivate {
+public:
+    virtual void sampleBufferUpdated(MediaStreamTrackPrivate&, MediaSample&) = 0;
+    virtual void audioSamplesAvailable(MediaStreamTrackPrivate&, const WTF::MediaTime&, const PlatformAudioData&, const AudioStreamDescription&, size_t) = 0;
+    
+    virtual Ref<Blob> fetchData() = 0;
+    virtual ~MediaRecorderPrivate() = default;
+};
+    
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)

Copied: trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.cpp (from rev 237641, trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.h) (0 => 237642)


--- trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.cpp	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "config.h"
+#include "MediaRecorderPrivateMock.h"
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "Blob.h"
+#include "MediaStreamTrackPrivate.h"
+
+namespace WebCore {
+
+void MediaRecorderPrivateMock::sampleBufferUpdated(MediaStreamTrackPrivate& track, MediaSample&)
+{
+    generateMockString(track);
+}
+
+void MediaRecorderPrivateMock::audioSamplesAvailable(MediaStreamTrackPrivate& track, const WTF::MediaTime&, const PlatformAudioData&, const AudioStreamDescription&, size_t)
+{
+    generateMockString(track);
+}
+
+void MediaRecorderPrivateMock::generateMockString(MediaStreamTrackPrivate& track)
+{
+    auto locker = holdLock(m_bufferLock);
+    if (track.type() == RealtimeMediaSource::Type::Audio)
+        m_buffer.append("Audio Track ID: ");
+    else
+        m_buffer.append("Video Track ID: ");
+    m_buffer.append(track.id());
+    m_buffer.append(" Counter: ");
+    m_buffer.appendNumber(++m_counter);
+    m_buffer.append("\r\n---------\r\n");
+}
+
+Ref<Blob> MediaRecorderPrivateMock::fetchData()
+{
+    auto locker = holdLock(m_bufferLock);
+    Vector<uint8_t> value(m_buffer.length());
+    memcpy(value.data(), m_buffer.characters8(), m_buffer.length());
+    m_buffer.clear();
+    return Blob::create(WTFMove(value), "text/plain");
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)

Copied: trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.h (from rev 237641, trunk/Source/WebCore/Modules/mediarecorder/BlobEvent.h) (0 => 237642)


--- trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateMock.h	2018-10-31 17:19:27 UTC (rev 237642)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "MediaRecorderPrivate.h"
+#include <wtf/Lock.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+class Blob;
+class MediaStreamTrackPrivate;
+
+class MediaRecorderPrivateMock final : public MediaRecorderPrivate {
+private:
+    void sampleBufferUpdated(MediaStreamTrackPrivate&, MediaSample&) final;
+    void audioSamplesAvailable(MediaStreamTrackPrivate&, const WTF::MediaTime&, const PlatformAudioData&, const AudioStreamDescription&, size_t) final;
+    Ref<Blob> fetchData() final;
+    
+    void generateMockString(MediaStreamTrackPrivate&);
+
+    mutable Lock m_bufferLock;
+    StringBuilder m_buffer;
+    unsigned m_counter { 0 };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to