| Differences between
and this patch
- a/Source/WebCore/ChangeLog +41 lines
Lines 1-3 a/Source/WebCore/ChangeLog_sec1
1
2018-11-27  YUHAN WU  <yuhan_wu@apple.com>
2
3
        Implement non-timeslice mode encoding for MediaRecorder
4
        https://bugs.webkit.org/show_bug.cgi?id=192069
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        Implement the encoding for non-timeslice mode of MediaRecorder.
9
        It only supports to record MP4 file through H264 and AAC encoding, we will need to support more MIME types and encoding methods.
10
11
        Tests: http/wpt/mediarecorder/MediaRecorder-real-audio-only-dataavailable.html
12
               http/wpt/mediarecorder/MediaRecorder-real-audio-video-dataavailable.html
13
               http/wpt/mediarecorder/MediaRecorder-real-video-only-dataavailable.html
14
15
        * Modules/mediarecorder/MediaRecorder.cpp:
16
        (WebCore::MediaRecorder::getPrivate):
17
        (WebCore::MediaRecorder::MediaRecorder):
18
        (WebCore::MediaRecorder::stopRecording):
19
        * Modules/mediarecorder/MediaRecorder.h:
20
        * Sources.txt:
21
        * SourcesCocoa.txt:
22
        * WebCore.xcodeproj/project.pbxproj:
23
        * platform/mediarecorder/MediaRecorderPrivate.h:
24
        (WebCore::MediaRecorderPrivate::stopRecording):
25
        * platform/mediarecorder/MediaRecorderPrivateReal.cpp: Added.
26
        (WebCore::MediaRecorderPrivateReal::MediaRecorderPrivateReal):
27
        (WebCore::MediaRecorderPrivateReal::sampleBufferUpdated):
28
        (WebCore::MediaRecorderPrivateReal::audioSamplesAvailable):
29
        (WebCore::MediaRecorderPrivateReal::stopRecording):
30
        (WebCore::MediaRecorderPrivateReal::fetchData):
31
        * platform/mediarecorder/MediaRecorderPrivateReal.h: Added.
32
        * platform/mediarecorder/MediaRecorderPrivateWriter.h: Added.
33
        * platform/mediarecorder/MediaRecorderPrivateWriter.mm: Added.
34
        (WebCore::MediaRecorderPrivateWriter::setupWriter):
35
        (WebCore::MediaRecorderPrivateWriter::setVideoInput):
36
        (WebCore::MediaRecorderPrivateWriter::setAudioInput):
37
        (WebCore::MediaRecorderPrivateWriter::appendVideoSampleBuffer):
38
        (WebCore::MediaRecorderPrivateWriter::appendAudioSampleBuffer):
39
        (WebCore::MediaRecorderPrivateWriter::stopRecording):
40
        (WebCore::MediaRecorderPrivateWriter::fetchData):
41
1
2018-11-01  Chris Dumez  <cdumez@apple.com>
42
2018-11-01  Chris Dumez  <cdumez@apple.com>
2
43
3
        Location object sans browsing context
44
        Location object sans browsing context
- a/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp -1 / +13 lines
Lines 30-39 a/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp_sec1
30
30
31
#include "Blob.h"
31
#include "Blob.h"
32
#include "BlobEvent.h"
32
#include "BlobEvent.h"
33
#include "DeprecatedGlobalSettings.h"
33
#include "Document.h"
34
#include "Document.h"
34
#include "EventNames.h"
35
#include "EventNames.h"
35
#include "MediaRecorderErrorEvent.h"
36
#include "MediaRecorderErrorEvent.h"
36
#include "MediaRecorderPrivateMock.h"
37
#include "MediaRecorderPrivateMock.h"
38
#include "MediaRecorderPrivateReal.h"
37
39
38
namespace WebCore {
40
namespace WebCore {
39
41
Lines 44-54 Ref<MediaRecorder> MediaRecorder::create(Document& document, Ref<MediaStream>&& a/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp_sec2
44
    return recorder;
46
    return recorder;
45
}
47
}
46
48
49
UniqueRef<MediaRecorderPrivate> MediaRecorder::getPrivate(Ref<MediaStream>&& stream)
50
{
51
    if (DeprecatedGlobalSettings::mockCaptureDevicesEnabled())
52
        return makeUniqueRef<MediaRecorderPrivateMock>();
53
    
54
    return makeUniqueRef<MediaRecorderPrivateReal>(WTFMove(stream));
55
}
56
47
MediaRecorder::MediaRecorder(Document& document, Ref<MediaStream>&& stream, Options&& option)
57
MediaRecorder::MediaRecorder(Document& document, Ref<MediaStream>&& stream, Options&& option)
48
    : ActiveDOMObject(&document)
58
    : ActiveDOMObject(&document)
49
    , m_options(WTFMove(option))
59
    , m_options(WTFMove(option))
50
    , m_stream(WTFMove(stream))
60
    , m_stream(WTFMove(stream))
51
    , m_private(makeUniqueRef<MediaRecorderPrivateMock>()) // FIXME: we will need to decide which MediaRecorderPrivate instance to create based on the mock enabled feature flag
61
    , m_private(getPrivate(m_stream.copyRef()))
52
{
62
{
53
    m_tracks = WTF::map(m_stream->getTracks(), [] (auto&& track) -> Ref<MediaStreamTrackPrivate> {
63
    m_tracks = WTF::map(m_stream->getTracks(), [] (auto&& track) -> Ref<MediaStreamTrackPrivate> {
54
        return track->privateTrack();
64
        return track->privateTrack();
Lines 95-100 ExceptionOr<void> MediaRecorder::stopRecording() a/Source/WebCore/Modules/mediarecorder/MediaRecorder.cpp_sec3
95
{
105
{
96
    if (state() == RecordingState::Inactive)
106
    if (state() == RecordingState::Inactive)
97
        return Exception { InvalidStateError, "The MediaRecorder's state cannot be inactive"_s };
107
        return Exception { InvalidStateError, "The MediaRecorder's state cannot be inactive"_s };
108
    
109
    m_private->stopRecording();
98
110
99
    scheduleDeferredTask([this] {
111
    scheduleDeferredTask([this] {
100
        if (!m_isActive || state() == RecordingState::Inactive)
112
        if (!m_isActive || state() == RecordingState::Inactive)
- a/Source/WebCore/Modules/mediarecorder/MediaRecorder.h +1 lines
Lines 68-73 public: a/Source/WebCore/Modules/mediarecorder/MediaRecorder.h_sec1
68
    
68
    
69
private:
69
private:
70
    MediaRecorder(Document&, Ref<MediaStream>&&, Options&& = { });
70
    MediaRecorder(Document&, Ref<MediaStream>&&, Options&& = { });
71
    UniqueRef<MediaRecorderPrivate> getPrivate(Ref<MediaStream>&&);
71
    
72
    
72
    // EventTarget
73
    // EventTarget
73
    void refEventTarget() final { ref(); }
74
    void refEventTarget() final { ref(); }
- a/Source/WebCore/Sources.txt +1 lines
Lines 1768-1773 platform/graphics/transforms/TranslateTransformOperation.cpp a/Source/WebCore/Sources.txt_sec1
1768
1768
1769
platform/mediacapabilities/MediaEngineConfigurationFactory.cpp
1769
platform/mediacapabilities/MediaEngineConfigurationFactory.cpp
1770
1770
1771
platform/mediarecorder/MediaRecorderPrivateReal.cpp
1771
platform/mediarecorder/MediaRecorderPrivateMock.cpp
1772
platform/mediarecorder/MediaRecorderPrivateMock.cpp
1772
1773
1773
platform/mediastream/CaptureDeviceManager.cpp
1774
platform/mediastream/CaptureDeviceManager.cpp
- a/Source/WebCore/SourcesCocoa.txt +2 lines
Lines 473-478 platform/mac/WebNSAttributedStringExtras.mm a/Source/WebCore/SourcesCocoa.txt_sec1
473
platform/mac/WebPlaybackControlsManager.mm
473
platform/mac/WebPlaybackControlsManager.mm
474
platform/mac/WidgetMac.mm
474
platform/mac/WidgetMac.mm
475
475
476
platform/mediarecorder/MediaRecorderPrivateWriter.mm
477
476
platform/mediasession/mac/MediaSessionInterruptionProviderMac.mm
478
platform/mediasession/mac/MediaSessionInterruptionProviderMac.mm
477
479
478
platform/mediastream/ios/AVAudioSessionCaptureDevice.mm
480
platform/mediastream/ios/AVAudioSessionCaptureDevice.mm
- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +12 lines
Lines 1361-1366 a/Source/WebCore/WebCore.xcodeproj/project.pbxproj_sec1
1361
		4D3B00AB215D69A70076B983 /* MediaRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B00A9215D69A70076B983 /* MediaRecorder.h */; };
1361
		4D3B00AB215D69A70076B983 /* MediaRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B00A9215D69A70076B983 /* MediaRecorder.h */; };
1362
		4D3B00AF215D6A690076B983 /* BlobEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B00AD215D6A690076B983 /* BlobEvent.h */; };
1362
		4D3B00AF215D6A690076B983 /* BlobEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B00AD215D6A690076B983 /* BlobEvent.h */; };
1363
		4D3B5016217E58B700665DB1 /* MediaRecorderPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */; };
1363
		4D3B5016217E58B700665DB1 /* MediaRecorderPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */; };
1364
		4D73F946218BC5FA003A3ED6 /* MediaRecorderPrivateReal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D73F944218BC5FA003A3ED6 /* MediaRecorderPrivateReal.h */; };
1365
		4D73F94E218C4A87003A3ED6 /* MediaRecorderPrivateWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D73F94C218C4A87003A3ED6 /* MediaRecorderPrivateWriter.h */; };
1364
		4DB7130D216ECB4D0096A4DD /* MediaRecorderErrorEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DB7130C216EC2BD0096A4DD /* MediaRecorderErrorEvent.h */; };
1366
		4DB7130D216ECB4D0096A4DD /* MediaRecorderErrorEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DB7130C216EC2BD0096A4DD /* MediaRecorderErrorEvent.h */; };
1365
		4E1959220A39DABA00220FE5 /* MediaFeatureNames.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E1959200A39DABA00220FE5 /* MediaFeatureNames.h */; settings = {ATTRIBUTES = (Private, ); }; };
1367
		4E1959220A39DABA00220FE5 /* MediaFeatureNames.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E1959200A39DABA00220FE5 /* MediaFeatureNames.h */; settings = {ATTRIBUTES = (Private, ); }; };
1366
		4E19592A0A39DACC00220FE5 /* MediaQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E1959240A39DACC00220FE5 /* MediaQuery.h */; settings = {ATTRIBUTES = (Private, ); }; };
1368
		4E19592A0A39DACC00220FE5 /* MediaQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E1959240A39DACC00220FE5 /* MediaQuery.h */; settings = {ATTRIBUTES = (Private, ); }; };
Lines 7922-7927 a/Source/WebCore/WebCore.xcodeproj/project.pbxproj_sec2
7922
		4D3B00A9215D69A70076B983 /* MediaRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaRecorder.h; sourceTree = "<group>"; };
7924
		4D3B00A9215D69A70076B983 /* MediaRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaRecorder.h; sourceTree = "<group>"; };
7923
		4D3B00AD215D6A690076B983 /* BlobEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlobEvent.h; sourceTree = "<group>"; };
7925
		4D3B00AD215D6A690076B983 /* BlobEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlobEvent.h; sourceTree = "<group>"; };
7924
		4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaRecorderPrivate.h; sourceTree = "<group>"; };
7926
		4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaRecorderPrivate.h; sourceTree = "<group>"; };
7927
		4D73F944218BC5FA003A3ED6 /* MediaRecorderPrivateReal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaRecorderPrivateReal.h; sourceTree = "<group>"; };
7928
		4D73F945218BC5FA003A3ED6 /* MediaRecorderPrivateReal.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MediaRecorderPrivateReal.cpp; sourceTree = "<group>"; };
7929
		4D73F94C218C4A87003A3ED6 /* MediaRecorderPrivateWriter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaRecorderPrivateWriter.h; sourceTree = "<group>"; };
7930
		4D73F94D218C4A87003A3ED6 /* MediaRecorderPrivateWriter.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = MediaRecorderPrivateWriter.mm; sourceTree = "<group>"; wrapsLines = 0; };
7925
		4D7EB3F4217C6AE600D64888 /* BlobEvent.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BlobEvent.cpp; sourceTree = "<group>"; };
7931
		4D7EB3F4217C6AE600D64888 /* BlobEvent.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BlobEvent.cpp; sourceTree = "<group>"; };
7926
		4D9F6B642182532B0092A9C5 /* MediaRecorderPrivateMock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaRecorderPrivateMock.h; sourceTree = "<group>"; };
7932
		4D9F6B642182532B0092A9C5 /* MediaRecorderPrivateMock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaRecorderPrivateMock.h; sourceTree = "<group>"; };
7927
		4D9F6B652182532B0092A9C5 /* MediaRecorderPrivateMock.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MediaRecorderPrivateMock.cpp; sourceTree = "<group>"; };
7933
		4D9F6B652182532B0092A9C5 /* MediaRecorderPrivateMock.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MediaRecorderPrivateMock.cpp; sourceTree = "<group>"; };
Lines 18061-18066 a/Source/WebCore/WebCore.xcodeproj/project.pbxproj_sec3
18061
				4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */,
18067
				4D3B5014217E58B700665DB1 /* MediaRecorderPrivate.h */,
18062
				4D9F6B652182532B0092A9C5 /* MediaRecorderPrivateMock.cpp */,
18068
				4D9F6B652182532B0092A9C5 /* MediaRecorderPrivateMock.cpp */,
18063
				4D9F6B642182532B0092A9C5 /* MediaRecorderPrivateMock.h */,
18069
				4D9F6B642182532B0092A9C5 /* MediaRecorderPrivateMock.h */,
18070
				4D73F945218BC5FA003A3ED6 /* MediaRecorderPrivateReal.cpp */,
18071
				4D73F944218BC5FA003A3ED6 /* MediaRecorderPrivateReal.h */,
18072
				4D73F94C218C4A87003A3ED6 /* MediaRecorderPrivateWriter.h */,
18073
				4D73F94D218C4A87003A3ED6 /* MediaRecorderPrivateWriter.mm */,
18064
			);
18074
			);
18065
			path = mediarecorder;
18075
			path = mediarecorder;
18066
			sourceTree = "<group>";
18076
			sourceTree = "<group>";
Lines 29963-29968 a/Source/WebCore/WebCore.xcodeproj/project.pbxproj_sec4
29963
				4D3B00AB215D69A70076B983 /* MediaRecorder.h in Headers */,
29973
				4D3B00AB215D69A70076B983 /* MediaRecorder.h in Headers */,
29964
				4DB7130D216ECB4D0096A4DD /* MediaRecorderErrorEvent.h in Headers */,
29974
				4DB7130D216ECB4D0096A4DD /* MediaRecorderErrorEvent.h in Headers */,
29965
				4D3B5016217E58B700665DB1 /* MediaRecorderPrivate.h in Headers */,
29975
				4D3B5016217E58B700665DB1 /* MediaRecorderPrivate.h in Headers */,
29976
				4D73F946218BC5FA003A3ED6 /* MediaRecorderPrivateReal.h in Headers */,
29977
				4D73F94E218C4A87003A3ED6 /* MediaRecorderPrivateWriter.h in Headers */,
29966
				C90843D01B18E47D00B68564 /* MediaRemoteControls.h in Headers */,
29978
				C90843D01B18E47D00B68564 /* MediaRemoteControls.h in Headers */,
29967
				CD8ACA8F1D23971900ECC59E /* MediaRemoteSoftLink.h in Headers */,
29979
				CD8ACA8F1D23971900ECC59E /* MediaRemoteSoftLink.h in Headers */,
29968
				CEEFCD7A19DB31F7003876D7 /* MediaResourceLoader.h in Headers */,
29980
				CEEFCD7A19DB31F7003876D7 /* MediaResourceLoader.h in Headers */,
- a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.h +1 lines
Lines 45-50 public: a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.h_sec1
45
    
45
    
46
    virtual Ref<Blob> fetchData() = 0;
46
    virtual Ref<Blob> fetchData() = 0;
47
    virtual ~MediaRecorderPrivate() = default;
47
    virtual ~MediaRecorderPrivate() = default;
48
    virtual void stopRecording() { }
48
};
49
};
49
    
50
    
50
} // namespace WebCore
51
} // namespace WebCore
- a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateReal.cpp +88 lines
Line 0 a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateReal.cpp_sec1
1
/*
2
 * Copyright (C) 2018 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
 */
24
25
26
#include "config.h"
27
#include "MediaRecorderPrivateReal.h"
28
29
#if ENABLE(MEDIA_STREAM)
30
31
#include "AudioStreamDescription.h"
32
#include "Blob.h"
33
#include "MediaSample.h"
34
#include "MediaStream.h"
35
#include "SharedBuffer.h"
36
#include "WebAudioBufferList.h"
37
38
namespace WebCore {
39
40
MediaRecorderPrivateReal::MediaRecorderPrivateReal(Ref<MediaStream>&& stream)
41
    : m_writer(makeUniqueRef<MediaRecorderPrivateWriter>())
42
    , m_stream(WTFMove(stream))
43
{
44
    m_writer->setupWriter();
45
    auto videoTracks = m_stream->getVideoTracks();
46
    if (videoTracks.size()) {
47
        auto settings = videoTracks[0]->getSettings();
48
        if (settings.width && settings.height) {
49
            m_writer->setVideoInput(*settings.width, *settings.height);
50
            m_recordedVideoTrackID = videoTracks[0]->id(); // FIXME: we will need to implement support for multiple video tracks, currently we only choose the first track as the recorded track
51
        }
52
    }
53
    auto audioTracks = m_stream->getAudioTracks();
54
    if (audioTracks.size()) {
55
        m_writer->setAudioInput();
56
        m_recordedAudioTrackID = audioTracks[0]->id(); // ditto
57
    }
58
}
59
60
void MediaRecorderPrivateReal::sampleBufferUpdated(MediaStreamTrackPrivate& track, MediaSample& sampleBuffer)
61
{
62
    if (track.id() != m_recordedVideoTrackID)
63
        return;
64
    m_writer->appendVideoSampleBuffer(sampleBuffer.platformSample().sample.cmSampleBuffer);
65
}
66
67
void MediaRecorderPrivateReal::audioSamplesAvailable(MediaStreamTrackPrivate& track, const WTF::MediaTime& mediaTime, const PlatformAudioData& data, const AudioStreamDescription& description, size_t sampleCount)
68
{
69
    if (track.id() != m_recordedAudioTrackID)
70
        return;
71
    ASSERT(is<WebAudioBufferList>(data));
72
    ASSERT(description.platformDescription().type == PlatformDescription::CAAudioStreamBasicType);
73
    m_writer->appendAudioSampleBuffer(data, description, mediaTime, sampleCount);
74
}
75
76
void MediaRecorderPrivateReal::stopRecording()
77
{
78
    m_writer->stopRecording();
79
}
80
81
Ref<Blob> MediaRecorderPrivateReal::fetchData()
82
{
83
    return m_writer->fetchData();
84
}
85
86
} // namespace WebCore
87
88
#endif // ENABLE(MEDIA_STREAM)
- a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateReal.h +58 lines
Line 0 a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateReal.h_sec1
1
/*
2
 * Copyright (C) 2018 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
 */
24
25
#pragma once
26
27
#if ENABLE(MEDIA_STREAM)
28
29
#include "MediaRecorderPrivate.h"
30
#include "MediaRecorderPrivateWriter.h"
31
#include <wtf/Lock.h>
32
#include <wtf/UniqueRef.h>
33
34
namespace WebCore {
35
36
class Blob;
37
class MediaStream;
38
39
class MediaRecorderPrivateReal final : public MediaRecorderPrivate {
40
public:
41
    MediaRecorderPrivateReal(Ref<MediaStream>&&);
42
private:
43
    void sampleBufferUpdated(MediaStreamTrackPrivate&, MediaSample&) final;
44
    void audioSamplesAvailable(MediaStreamTrackPrivate&, const WTF::MediaTime&, const PlatformAudioData&, const AudioStreamDescription&, size_t) final;
45
    Ref<Blob> fetchData() final;
46
    void stopRecording();
47
    
48
    String m_recordedVideoTrackID;
49
    String m_recordedAudioTrackID;
50
51
    mutable Lock m_bufferLock;
52
    UniqueRef<MediaRecorderPrivateWriter> m_writer;
53
    Ref<MediaStream> m_stream;
54
};
55
56
} // namespace WebCore
57
58
#endif // ENABLE(MEDIA_STREAM)
- a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateWriter.h +82 lines
Line 0 a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateWriter.h_sec1
1
/*
2
 * Copyright (C) 2018 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
 */
24
25
#pragma once
26
27
#if ENABLE(MEDIA_STREAM)
28
29
#include <wtf/Deque.h>
30
#include <wtf/Lock.h>
31
#include <wtf/RetainPtr.h>
32
#include <wtf/ThreadSafeRefCounted.h>
33
#include <wtf/WeakPtr.h>
34
#include <wtf/threads/BinarySemaphore.h>
35
36
typedef struct opaqueCMSampleBuffer *CMSampleBufferRef;
37
38
OBJC_CLASS AVAssetWriter;
39
OBJC_CLASS AVAssetWriterInput;
40
41
namespace WTF {
42
class MediaTime;
43
}
44
45
namespace WebCore {
46
47
class Blob;
48
class PlatformAudioData;
49
class AudioStreamDescription;
50
51
class MediaRecorderPrivateWriter : public ThreadSafeRefCounted<MediaRecorderPrivateWriter>, public CanMakeWeakPtr<MediaRecorderPrivateWriter> {
52
public:
53
    bool setupWriter();
54
    bool setVideoInput(int width, int height);
55
    bool setAudioInput();
56
    void appendVideoSampleBuffer(CMSampleBufferRef);
57
    void appendAudioSampleBuffer(const PlatformAudioData&, const AudioStreamDescription&, const WTF::MediaTime&, size_t);
58
    void stopRecording();
59
    Ref<Blob> fetchData();
60
private:    
61
    RetainPtr<AVAssetWriter> m_writer;
62
    RetainPtr<AVAssetWriterInput> m_videoInput;
63
    RetainPtr<AVAssetWriterInput> m_audioInput;
64
65
    String m_path;
66
    Lock m_videoLock;
67
    Lock m_audioLock;
68
    BinarySemaphore m_FinishWritingSemaphore;
69
    BinarySemaphore m_FinishWritingAudioSemaphore;
70
    BinarySemaphore m_FinishWritingVideoSemaphore;
71
    bool canWrite { false };
72
    bool isStopped { false };
73
    bool firstAudioSample { true };
74
    dispatch_queue_t m_audioPullQueue;
75
    dispatch_queue_t m_videoPullQueue;
76
    Deque<RetainPtr<CMSampleBufferRef>> m_videoBufferPool;
77
    Deque<RetainPtr<CMSampleBufferRef>> m_audioBufferPool;
78
};
79
80
} // namespace WebCore
81
82
#endif // ENABLE(MEDIA_STREAM)
- a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateWriter.mm +269 lines
Line 0 a/Source/WebCore/platform/mediarecorder/MediaRecorderPrivateWriter.mm_sec1
1
/*
2
 * Copyright (C) 2015 Apple 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23
 * THE POSSIBILITY OF SUCH DAMAGE.
24
 */
25
26
#include "config.h"
27
#include "MediaRecorderPrivateWriter.h"
28
29
#if ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)
30
31
#include "AudioStreamDescription.h"
32
#include "Blob.h"
33
#include "FileSystem.h"
34
#include "SharedBuffer.h"
35
#include "WebAudioBufferList.h"
36
#include <AVFoundation/AVAssetWriter.h>
37
#include <AVFoundation/AVAssetWriterInput.h>
38
#include <pal/cf/CoreMediaSoftLink.h>
39
40
typedef AVAssetWriter AVAssetWriterType;
41
typedef AVAssetWriterInput AVAssetWriterInputType;
42
43
SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
44
45
SOFT_LINK_CLASS(AVFoundation, AVAssetWriter)
46
SOFT_LINK_CLASS(AVFoundation, AVAssetWriterInput)
47
48
SOFT_LINK_CONSTANT(AVFoundation, AVFileTypeMPEG4, NSString *)
49
SOFT_LINK_CONSTANT(AVFoundation, AVVideoCodecKey, NSString *)
50
SOFT_LINK_CONSTANT(AVFoundation, AVVideoCodecTypeH264, NSString *)
51
SOFT_LINK_CONSTANT(AVFoundation, AVVideoWidthKey, NSString *)
52
SOFT_LINK_CONSTANT(AVFoundation, AVVideoHeightKey, NSString *)
53
SOFT_LINK_CONSTANT(AVFoundation, AVMediaTypeVideo, NSString *)
54
SOFT_LINK_CONSTANT(AVFoundation, AVMediaTypeAudio, NSString *)
55
SOFT_LINK_CONSTANT(AVFoundation, AVEncoderBitRatePerChannelKey, NSString *)
56
SOFT_LINK_CONSTANT(AVFoundation, AVFormatIDKey, NSString *)
57
SOFT_LINK_CONSTANT(AVFoundation, AVNumberOfChannelsKey, NSString *)
58
SOFT_LINK_CONSTANT(AVFoundation, AVSampleRateKey, NSString *)
59
60
#define AVFileTypeMPEG4 getAVFileTypeMPEG4()
61
#define AVMediaTypeAudio getAVMediaTypeAudio()
62
#define AVMediaTypeVideo getAVMediaTypeVideo()
63
#define AVVideoCodecKey getAVVideoCodecKey()
64
#define AVVideoCodecTypeH264 getAVVideoCodecTypeH264()
65
#define AVVideoWidthKey getAVVideoWidthKey()
66
#define AVVideoHeightKey getAVVideoHeightKey()
67
#define AVEncoderBitRatePerChannelKey getAVEncoderBitRatePerChannelKey()
68
#define AVFormatIDKey getAVFormatIDKey()
69
#define AVNumberOfChannelsKey getAVNumberOfChannelsKey()
70
#define AVSampleRateKey getAVSampleRateKey()
71
72
using namespace WebCore;
73
74
namespace WebCore {
75
76
using namespace PAL;
77
78
bool MediaRecorderPrivateWriter::setupWriter()
79
{
80
    if (m_writer)
81
        return false;
82
    
83
    NSString *directory = FileSystem::createTemporaryDirectory(@"videos");
84
    NSString *path = [directory stringByAppendingString:@"/test.mp4"];
85
    NSURL *outputURL = [NSURL fileURLWithPath:path];
86
    m_path = [path UTF8String];
87
    NSError *error = nil;
88
    m_writer = adoptNS([allocAVAssetWriterInstance() initWithURL:outputURL fileType:AVFileTypeMPEG4 error:&error]);
89
    if (error)
90
        return false;
91
    return true;
92
}
93
94
bool MediaRecorderPrivateWriter::setVideoInput(int width, int height)
95
{
96
    if (m_videoInput)
97
        return false;
98
    NSDictionary *videoSettings = @{ AVVideoCodecKey: AVVideoCodecTypeH264, AVVideoWidthKey: [NSNumber numberWithInt:width], AVVideoHeightKey: [NSNumber numberWithInt:height] };
99
    m_videoInput = adoptNS([allocAVAssetWriterInputInstance() initWithMediaType:AVMediaTypeVideo outputSettings:videoSettings sourceFormatHint:NULL]);
100
    [m_videoInput setExpectsMediaDataInRealTime:true];
101
    
102
    if (![m_writer canAddInput:m_videoInput.get()])
103
        return false;
104
    [m_writer addInput:m_videoInput.get()];
105
    m_videoPullQueue = dispatch_queue_create("WebCoreVideoRecordingPullBufferQueue", DISPATCH_QUEUE_SERIAL);
106
    return true;
107
}
108
109
bool MediaRecorderPrivateWriter::setAudioInput()
110
{
111
    if (m_audioInput)
112
        return false;
113
    NSDictionary *audioSettings = @{ AVEncoderBitRatePerChannelKey : @(28000), AVFormatIDKey : @(kAudioFormatMPEG4AAC), AVNumberOfChannelsKey : @(1), AVSampleRateKey : @(22050) };
114
115
    m_audioInput = adoptNS([allocAVAssetWriterInputInstance() initWithMediaType:AVMediaTypeAudio outputSettings:audioSettings sourceFormatHint:NULL]);
116
    [m_audioInput setExpectsMediaDataInRealTime:true];
117
    
118
    if (![m_writer canAddInput:m_audioInput.get()])
119
        return false;
120
    [m_writer addInput:m_audioInput.get()];
121
    m_audioPullQueue = dispatch_queue_create("WebCoreAudioRecordingPushBufferQueue", DISPATCH_QUEUE_SERIAL);
122
    return true;
123
}
124
125
void MediaRecorderPrivateWriter::appendVideoSampleBuffer(CMSampleBufferRef sampleBuffer)
126
{
127
    if (!canWrite) {
128
        bool success = [m_writer startWriting];
129
        if (!success)
130
            isStopped = true;
131
        CMTime startTime = CMClockGetTime(CMClockGetHostTimeClock());
132
        [m_writer startSessionAtSourceTime:startTime];
133
        canWrite = true;
134
        [m_videoInput requestMediaDataWhenReadyOnQueue:m_videoPullQueue usingBlock:[this, weakThis = makeWeakPtr(*this)] {
135
            do {
136
                if (!weakThis)
137
                    return;
138
                if (![m_videoInput isReadyForMoreMediaData])
139
                    break;
140
                auto locker = holdLock(m_videoLock);
141
                if (m_videoBufferPool.isEmpty())
142
                    break;
143
                auto buf = m_videoBufferPool.takeFirst();
144
                locker.unlockEarly();
145
                if (![m_videoInput appendSampleBuffer:buf.get()])
146
                    break;
147
            } while (true);
148
            if (isStopped && m_videoBufferPool.isEmpty()) {
149
                [m_videoInput markAsFinished];
150
                m_FinishWritingVideoSemaphore.signal();
151
            }
152
        }];
153
        return;
154
    }
155
    CMTime startTime = CMClockGetTime(CMClockGetHostTimeClock());
156
    CMItemCount count;
157
    CMSampleBufferGetSampleTimingInfoArray(sampleBuffer, 0, nil, &count);
158
    CMSampleTimingInfo* pInfo = new CMSampleTimingInfo[count];
159
    CMSampleBufferGetSampleTimingInfoArray(sampleBuffer, count, pInfo, &count);
160
    for (CMItemCount i = 0; i < count; i++) {
161
        pInfo[i].decodeTimeStamp = kCMTimeInvalid;
162
        pInfo[i].presentationTimeStamp = startTime;
163
    }
164
    CMSampleBufferRef sout;
165
    CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, sampleBuffer, count, pInfo, &sout);
166
    free(pInfo);
167
    auto locker = holdLock(m_videoLock);
168
    if (isStopped)
169
        return;
170
    m_videoBufferPool.append(retainPtr(sout));
171
}
172
173
void MediaRecorderPrivateWriter::appendAudioSampleBuffer(const PlatformAudioData& data, const AudioStreamDescription& description, const WTF::MediaTime&, size_t sampleCount)
174
{
175
    if ((!canWrite && m_videoInput) || isStopped)
176
        return;
177
    CMSampleBufferRef sampleBuffer;
178
    CMFormatDescriptionRef format;
179
    OSStatus error;
180
    auto& basicDescription = *WTF::get<const AudioStreamBasicDescription*>(description.platformDescription().description);
181
    error = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &basicDescription, 0, NULL, 0, NULL, NULL, &format);
182
    if (firstAudioSample) {
183
        if (!m_videoInput) {
184
            // audio-only recording
185
            bool success = [m_writer startWriting];
186
            if (!success)
187
                isStopped = true;
188
            CMTime startTime = CMClockGetTime(CMClockGetHostTimeClock());
189
            [m_writer startSessionAtSourceTime:startTime];
190
            canWrite = true;
191
        }
192
        firstAudioSample = false;
193
        [m_audioInput requestMediaDataWhenReadyOnQueue:m_audioPullQueue usingBlock:[this, weakThis = makeWeakPtr(*this)] {
194
            do {
195
                if (!weakThis)
196
                    return;
197
                if (![m_audioInput isReadyForMoreMediaData])
198
                    break;
199
                auto locker = holdLock(m_audioLock);
200
                if (m_audioBufferPool.isEmpty())
201
                    break;
202
                auto buf = m_audioBufferPool.takeFirst();
203
                locker.unlockEarly();
204
                [m_audioInput appendSampleBuffer:buf.get()];
205
            } while (true);
206
            if (isStopped && m_audioBufferPool.isEmpty()) {
207
                [m_audioInput markAsFinished];
208
                m_FinishWritingAudioSemaphore.signal();
209
            }
210
        }];
211
    }
212
    CMTime startTime = CMClockGetTime(CMClockGetHostTimeClock());
213
214
    error = CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault, NULL, false, NULL, NULL, format, sampleCount, startTime, NULL, &sampleBuffer);
215
    if (error)
216
        return;
217
    error = CMSampleBufferSetDataBufferFromAudioBufferList(sampleBuffer, kCFAllocatorDefault, kCFAllocatorDefault, 0, downcast<WebAudioBufferList>(data).list());
218
    if (error)
219
        return;
220
221
    auto locker = holdLock(m_audioLock);
222
    m_audioBufferPool.append(retainPtr(sampleBuffer));
223
}
224
225
void MediaRecorderPrivateWriter::stopRecording()
226
{
227
    isStopped = true;
228
    ASSERT([m_writer status] == AVAssetWriterStatusWriting);
229
    if (m_videoInput)
230
        m_FinishWritingVideoSemaphore.wait();
231
232
    if (m_audioInput)
233
        m_FinishWritingAudioSemaphore.wait();
234
235
    [m_writer finishWritingWithCompletionHandler:^{
236
        isStopped = false;
237
        canWrite = false;
238
        firstAudioSample = true;
239
        if (m_videoInput) {
240
            m_videoInput.clear();
241
            m_videoInput = NULL;
242
            dispatch_release(m_videoPullQueue);
243
        }
244
        if (m_audioInput) {
245
            m_audioInput.clear();
246
            m_audioInput = NULL;
247
            dispatch_release(m_audioPullQueue);
248
        }
249
        m_writer.clear();
250
        m_writer = NULL;
251
        m_FinishWritingSemaphore.signal();
252
    }];
253
    m_FinishWritingSemaphore.wait();
254
}
255
256
Ref<Blob> MediaRecorderPrivateWriter::fetchData()
257
{
258
    if (m_path.isEmpty())
259
        return Blob::create();
260
    
261
    RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_path);
262
    if (!data)
263
        return Blob::create();
264
    return Blob::create(*data, "video/mp4"); // FIXME: we will need to support more MIME types
265
}
266
267
} // namespace WebCore
268
269
#endif // ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)
- a/LayoutTests/ChangeLog +16 lines
Lines 1-3 a/LayoutTests/ChangeLog_sec1
1
2018-11-27  YUHAN WU  <yuhan_wu@apple.com>
2
3
        Implement non-timeslice mode encoding for MediaRecorder
4
        https://bugs.webkit.org/show_bug.cgi?id=192069
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        Create new tests for encoding of MediaRecorder. Check if the produced video and audio are correct.
9
10
        * http/wpt/mediarecorder/MediaRecorder-real-audio-only-dataavailable-expected.txt: Added.
11
        * http/wpt/mediarecorder/MediaRecorder-real-audio-only-dataavailable.html: Added.
12
        * http/wpt/mediarecorder/MediaRecorder-real-audio-video-dataavailable-expected.txt: Added.
13
        * http/wpt/mediarecorder/MediaRecorder-real-audio-video-dataavailable.html: Added.
14
        * http/wpt/mediarecorder/MediaRecorder-real-video-only-dataavailable-expected.txt: Added.
15
        * http/wpt/mediarecorder/MediaRecorder-real-video-only-dataavailable.html: Added.
16
1
2018-11-01  Chris Dumez  <cdumez@apple.com>
17
2018-11-01  Chris Dumez  <cdumez@apple.com>
2
18
3
        Location object sans browsing context
19
        Location object sans browsing context
- a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-audio-only-dataavailable-expected.txt +3 lines
Line 0 a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-audio-only-dataavailable-expected.txt_sec1
1
2
PASS MediaRecorder can successfully record the audio for a audio-only stream 
3
- a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-audio-only-dataavailable.html +49 lines
Line 0 a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-audio-only-dataavailable.html_sec1
1
<!doctype html>
2
<html>
3
<head>
4
    <title>MediaRecorder Dataavailable</title>
5
    <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
6
    <script src="/resources/testharness.js"></script>
7
    <script src="/resources/testharnessreport.js"></script>
8
</head>
9
<body>
10
<audio id="player">
11
</audio>
12
<script>
13
    if (window.internals)
14
        internals.setMockMediaCaptureDevicesEnabled(false);
15
16
    async_test(t => {
17
        const ac = new AudioContext();
18
        const osc = ac.createOscillator();
19
        const dest = ac.createMediaStreamDestination();
20
        const audio = dest.stream;
21
        osc.connect(dest);
22
        const recorder = new MediaRecorder(audio);
23
        recorder.ondataavailable = t.step_func(blobEvent => {
24
            assert_true(blobEvent instanceof BlobEvent, 'the type of event should be BlobEvent');
25
            assert_equals(blobEvent.type, 'dataavailable', 'the event type should be dataavailable');
26
            assert_true(blobEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
27
            assert_true(blobEvent.data instanceof Blob, 'the type of data should be Blob');
28
            assert_true(blobEvent.data.size > 0, 'the blob should contain some buffers');
29
            const player = document.getElementById("player");
30
            player.src = window.URL.createObjectURL(blobEvent.data);
31
            player.oncanplay = () => {
32
                assert_greater_than(player.duration, 0, 'the duration should be greater than 0');
33
                t.done();
34
            };
35
            player.load();
36
        });
37
38
        recorder.start();
39
        osc.start();
40
        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
41
        setTimeout(() => {
42
            recorder.stop();
43
            osc.stop();
44
        }, 5000);
45
    }, 'MediaRecorder can successfully record the audio for a audio-only stream');
46
47
</script>
48
</body>
49
</html>
- a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-audio-video-dataavailable-expected.txt +4 lines
Line 0 a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-audio-video-dataavailable-expected.txt_sec1
1
2
3
PASS MediaRecorder can successfully record the video for a audio-video stream 
4
- a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-audio-video-dataavailable.html +100 lines
Line 0 a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-audio-video-dataavailable.html_sec1
1
<!doctype html>
2
<html>
3
<head>
4
    <title>MediaRecorder Dataavailable</title>
5
    <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
6
    <script src="/resources/testharness.js"></script>
7
    <script src="/resources/testharnessreport.js"></script>
8
    <script src="../common/canvas-tests.js"></script>
9
    <link rel="stylesheet" href="../common/canvas-tests.css">
10
</head>
11
<body>
12
<div>
13
    <video id="player">
14
    </video>
15
</div>
16
<div>
17
    <canvas id="canvas" width="200" height="200">
18
    </canvas>
19
    <canvas id="frame" width="200" height="200">
20
    </canvas>
21
</div>
22
<script>
23
    var context;
24
    var drawIterationsPerTest = 100;
25
    var drawTimes = 0;
26
27
    function createVideoStream() {
28
        const canvas = document.getElementById("canvas");
29
        context = canvas.getContext('2d');
30
        return canvas.captureStream();
31
    }
32
33
    function doDraw() {
34
        if (context) {
35
            context.fillStyle = "#ff0000";
36
            context.fillRect(0, 0, 100, 200);
37
            context.fillStyle = "#00ff00";
38
            context.fillRect(100, 0, 100, 200);
39
            if (drawTimes < drawIterationsPerTest) {
40
                drawTimes++;
41
                window.requestAnimationFrame(doDraw);
42
            }
43
        }
44
    }
45
46
    if (window.internals)
47
        internals.setMockMediaCaptureDevicesEnabled(false);
48
49
    async_test(t => {
50
        const ac = new AudioContext();
51
        const osc = ac.createOscillator();
52
        const dest = ac.createMediaStreamDestination();
53
        const audio = dest.stream;
54
        osc.connect(dest);
55
56
        const video = createVideoStream();
57
        assert_equals(video.getAudioTracks().length, 0, "video mediastream starts with no audio track");
58
        assert_equals(audio.getAudioTracks().length, 1, "audio mediastream starts with one audio track");
59
        video.addTrack(audio.getAudioTracks()[0]);
60
        assert_equals(video.getAudioTracks().length, 1, "video mediastream starts with one audio track");
61
        const recorder = new MediaRecorder(video);
62
63
        recorder.ondataavailable = t.step_func(blobEvent => {
64
            assert_true(blobEvent instanceof BlobEvent, 'the type of event should be BlobEvent');
65
            assert_equals(blobEvent.type, 'dataavailable', 'the event type should be dataavailable');
66
            assert_true(blobEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
67
            assert_true(blobEvent.data instanceof Blob, 'the type of data should be Blob');
68
            assert_true(blobEvent.data.size > 0, 'the blob should contain some buffers');
69
            const player = document.getElementById("player");
70
            player.src = window.URL.createObjectURL(blobEvent.data);
71
            player.oncanplay = () => {
72
                assert_greater_than(player.duration, 0, 'the duration should be greater than 0');
73
                player.play();
74
            };
75
            player.onended = () => {
76
                const resFrame = document.getElementById("frame");
77
                const resContext = resFrame.getContext('2d');
78
                resContext.drawImage(player, 0, 0);
79
                _assertPixelApprox(resFrame, 0, 0, 255, 0, 0, 255, "0, 0", "255, 0, 0, 255", 5);
80
                _assertPixelApprox(resFrame, 50, 50, 255, 0, 0, 255, "50, 50", "255, 0, 0, 255", 5);
81
                _assertPixelApprox(resFrame, 199, 0, 0, 255, 0, 255, "199, 0", "0, 255, 0, 255", 5);
82
                _assertPixelApprox(resFrame, 199, 199, 0, 255, 0, 255, "199, 199", "0, 255, 0, 255", 5);
83
                t.done();
84
            };
85
            player.load();
86
        });
87
88
        doDraw();
89
        recorder.start();
90
        osc.start();
91
        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
92
        setTimeout(() => {
93
            recorder.stop();
94
            osc.stop();
95
        }, 2000);
96
    }, 'MediaRecorder can successfully record the video for a audio-video stream');
97
98
</script>
99
</body>
100
</html>
- a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-video-only-dataavailable-expected.txt +4 lines
Line 0 a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-video-only-dataavailable-expected.txt_sec1
1
2
3
PASS MediaRecorder can successfully record the video for a video-only stream 
4
- a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-video-only-dataavailable.html +86 lines
Line 0 a/LayoutTests/http/wpt/mediarecorder/MediaRecorder-real-video-only-dataavailable.html_sec1
1
<!doctype html>
2
<html>
3
<head>
4
    <title>MediaRecorder Dataavailable</title>
5
    <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
6
    <script src="/resources/testharness.js"></script>
7
    <script src="/resources/testharnessreport.js"></script>
8
    <script src="../common/canvas-tests.js"></script>
9
    <link rel="stylesheet" href="../common/canvas-tests.css">
10
</head>
11
<body>
12
<div>
13
    <video id="player">
14
    </video>
15
</div>
16
<div>
17
    <canvas id="canvas" width="200" height="200">
18
    </canvas>
19
    <canvas id="frame" width="200" height="200">
20
    </canvas>
21
</div>
22
<script>
23
    var context;
24
    var drawIterationsPerTest = 100;
25
    var drawTimes = 0;
26
27
    function createVideoStream() {
28
        const canvas = document.getElementById("canvas");
29
        context = canvas.getContext('2d');
30
        return canvas.captureStream();
31
    }
32
33
    function doDraw() {
34
        if (context) {
35
            context.fillStyle = "#ff0000";
36
            context.fillRect(0, 0, 100, 200);
37
            context.fillStyle = "#00ff00";
38
            context.fillRect(100, 0, 100, 200);
39
            if (drawTimes < drawIterationsPerTest) {
40
                drawTimes++;
41
                window.requestAnimationFrame(doDraw);
42
            }
43
        }
44
    }
45
46
    if (window.internals)
47
        internals.setMockMediaCaptureDevicesEnabled(false);
48
49
    async_test(t => {
50
        const video = createVideoStream();
51
        const recorder = new MediaRecorder(video);
52
        recorder.ondataavailable = t.step_func(blobEvent => {
53
            assert_true(blobEvent instanceof BlobEvent, 'the type of event should be BlobEvent');
54
            assert_equals(blobEvent.type, 'dataavailable', 'the event type should be dataavailable');
55
            assert_true(blobEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
56
            assert_true(blobEvent.data instanceof Blob, 'the type of data should be Blob');
57
            assert_true(blobEvent.data.size > 0, 'the blob should contain some buffers');
58
            const player = document.getElementById("player");
59
            player.src = window.URL.createObjectURL(blobEvent.data);
60
            player.oncanplay = () => {
61
                assert_greater_than(player.duration, 0, 'the duration should be greater than 0');
62
                player.play();
63
            };
64
            player.onended = () => {
65
                const resFrame = document.getElementById("frame");
66
                const resContext = resFrame.getContext('2d');
67
                resContext.drawImage(player, 0, 0);
68
                _assertPixelApprox(resFrame, 0, 0, 255, 0, 0, 255, "0, 0", "255, 0, 0, 255", 5);
69
                _assertPixelApprox(resFrame, 50, 50, 255, 0, 0, 255, "50, 50", "255, 0, 0, 255", 5);
70
                _assertPixelApprox(resFrame, 199, 0, 0, 255, 0, 255, "199, 0", "0, 255, 0, 255", 5);
71
                _assertPixelApprox(resFrame, 199, 199, 0, 255, 0, 255, "199, 199", "0, 255, 0, 255", 5);
72
                t.done();
73
            };
74
            player.load();
75
        });
76
        doDraw();
77
        recorder.start();
78
        assert_equals(recorder.state, 'recording', 'MediaRecorder has been started successfully');
79
        setTimeout(() => {
80
            recorder.stop();
81
        }, 2000);
82
    }, 'MediaRecorder can successfully record the video for a video-only stream');
83
84
</script>
85
</body>
86
</html>

Return to Bug 192069