Enable H264 simulcast
https://bugs.webkit.org/show_bug.cgi?id=190167
Reviewed by Eric Carlson.
Source/ThirdParty/libwebrtc:
Rename .m files to .mm to enable C++ compilation of included header files.
Rename RTCH264VideoEncoder to RTCSingleH264Encoder.
Implement a new RTCH264VideoEncoder that spawns as many RTCSingleH264Encoder as needed for simulcast.
Update ObjC API to allow passing simulcast parameters to/from RTCH264VideoEncoder.
* Configurations/libwebrtc.iOS.exp:
* Configurations/libwebrtc.iOSsim.exp:
* Configurations/libwebrtc.mac.exp:
* Source/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCDefaultVideoDecoderFactory.mm: Renamed from Source/ThirdParty/libwebrtc/Source/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCDefaultVideoDecoderFactory.m.
* Source/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCDefaultVideoEncoderFactory.mm: Renamed from Source/ThirdParty/libwebrtc/Source/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCDefaultVideoEncoderFactory.m.
* Source/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCVideoCodec+Private.h:
* Source/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCVideoCodecH264.mm:
(-[RTCCodecSpecificInfoH264 nativeCodecSpecificInfo]):
* Source/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCVideoEncoderSettings.mm:
(-[RTCVideoEncoderSettings initWithNativeVideoCodec:]):
* Source/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCWrappedNativeVideoEncoder.mm:
(-[RTCWrappedNativeVideoEncoder setBitrate:framerate:]):
(-[RTCWrappedNativeVideoEncoder setRateAllocation:framerate:]):
* Source/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoEncoderH264.mm:
(-[RTCSingleVideoEncoderH264 initWithCodecInfo:simulcastIndex:]):
(-[RTCSingleVideoEncoderH264 startEncodeWithSettings:numberOfCores:]):
(-[RTCSingleVideoEncoderH264 encode:codecSpecificInfo:frameTypes:]):
(-[RTCSingleVideoEncoderH264 resetCompressionSessionWithPixelFormat:]):
(-[RTCSingleVideoEncoderH264 scalingSettings]):
(-[RTCSingleVideoEncoderH264 setRateAllocation:framerate:]):
(-[RTCVideoEncoderH264 initWithCodecInfo:]):
(-[RTCVideoEncoderH264 setCallback:]):
(-[RTCVideoEncoderH264 startEncodeWithSettings:numberOfCores:]):
(-[RTCVideoEncoderH264 releaseEncoder]):
(-[RTCVideoEncoderH264 encode:codecSpecificInfo:frameTypes:]):
(-[RTCVideoEncoderH264 setRateAllocation:framerate:]):
(-[RTCVideoEncoderH264 implementationName]):
(-[RTCVideoEncoderH264 scalingSettings]):
(-[RTCVideoEncoderH264 setBitrate:framerate:]):
* Source/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCVideoCodec.h:
* Source/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCVideoCodecH264.h:
* Source/webrtc/sdk/objc/Framework/Native/src/objc_video_encoder_factory.mm:
* libwebrtc.xcodeproj/project.pbxproj:
Source/WebCore:
Activate H264 simulcast trial field.
Make track.getSettings() expose width and height for incoming tracks.
Test: webrtc/simulcast-h264.html
* Configurations/WebCore.xcconfig:
* Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp:
(WebCore::LibWebRTCMediaEndpoint::LibWebRTCMediaEndpoint):
* platform/mediastream/RealtimeIncomingVideoSource.cpp:
(WebCore::RealtimeIncomingVideoSource::RealtimeIncomingVideoSource):
LayoutTests:
* TestExpectations:
* webrtc/simulcast-h264-expected.txt: Added.
* webrtc/simulcast-h264.html: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@236792 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/webrtc/simulcast-h264.html b/LayoutTests/webrtc/simulcast-h264.html
new file mode 100644
index 0000000..16f2f84
--- /dev/null
+++ b/LayoutTests/webrtc/simulcast-h264.html
@@ -0,0 +1,126 @@
+<!doctype html>
+<html>
+<head>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div>
+ <video id="low" playsinline autoplay width="320"></video>
+ <video id="mid" playsinline autoplay width="320"></video>
+ <video id="high" playsinline autoplay width="320"></video>
+</div>
+<script>
+if (window.testRunner)
+ testRunner.setWebRTCUnifiedPlanEnabled(false);
+var state;
+var finished = false;
+// This test is largely inspired from Chrome/Firefox/Simulcast playground tests.
+promise_test(async (test) => {
+ if (window.testRunner && testRunner.timeout) {
+ setTimeout(() => {
+ if (!finished)
+ throw "test stucked in state: " + state;
+ }, testRunner.timeout * 0.9);
+ }
+
+ state = "start";
+
+ const pc1 = new RTCPeerConnection();
+ const pc2 = new RTCPeerConnection();
+ pc1.onicecandidate = (e) => pc2.addIceCandidate(e.candidate);
+ pc2.onicecandidate = (e) => pc1.addIceCandidate(e.candidate);
+
+ let counter = 0;
+ pc2.ontrack = (e) => {
+ if (counter == 0)
+ low.srcObject = new MediaStream([e.track]);
+ else if (counter == 1)
+ mid.srcObject = new MediaStream([e.track]);
+ else
+ high.srcObject = new MediaStream([e.track]);
+ ++counter;
+ }
+
+ const localStream = await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 } });
+ pc1.addTrack(localStream.getVideoTracks()[0], localStream);
+ const offer = await pc1.createOffer();
+
+ state = "got offer";
+
+ match = offer.sdp.match(/a=ssrc:(\d+) cname:(.*)\r\n/);
+ msid = offer.sdp.match(/a=ssrc:(\d+) msid:(.*)\r\n/);
+ var lines = offer.sdp.trim().split('\r\n');
+ var removed = lines.splice(lines.length - 4, 4);
+ var videoSSRC1 = parseInt(match[1]);
+ rtxSSRC1 = offer.sdp.split('\r\n').filter((line) => { return line.startsWith('a=ssrc-group:FID ')})[0].split(' ')[2];
+ var videoSSRC2 = videoSSRC1 + 1;
+ var rtxSSRC2 = videoSSRC1 + 2;
+ var videoSSRC3 = videoSSRC1 + 3;
+ var rtxSSRC3 = videoSSRC1 + 4;
+ lines.push(removed[0]);
+ lines.push(removed[1]);
+ lines.push('a=ssrc:' + videoSSRC2 + ' cname:' + match[2]);
+ lines.push('a=ssrc:' + videoSSRC2 + ' msid:' + msid[2]);
+ lines.push('a=ssrc:' + rtxSSRC2 + ' cname:' + match[2]);
+ lines.push('a=ssrc:' + rtxSSRC2 + ' msid:' + msid[2]);
+
+ lines.push('a=ssrc:' + videoSSRC3 + ' cname:' + match[2]);
+ lines.push('a=ssrc:' + videoSSRC3 + ' msid:' + msid[2]);
+ lines.push('a=ssrc:' + rtxSSRC3 + ' cname:' + match[2]);
+ lines.push('a=ssrc:' + rtxSSRC3 + ' msid:' + msid[2]);
+
+ lines.push('a=ssrc-group:FID ' + videoSSRC2 + ' ' + rtxSSRC2);
+ lines.push('a=ssrc-group:FID ' + videoSSRC3 + ' ' + rtxSSRC3);
+ lines.push('a=ssrc-group:SIM ' + videoSSRC1 + ' ' + videoSSRC2 + ' ' + videoSSRC3);
+ offer.sdp = lines.join('\r\n') + '\r\n';
+
+ var offer2 = {
+ type: 'offer',
+ sdp: offer.sdp,
+ };
+ offer2.sdp = offer2.sdp.replace('a=ssrc-group:SIM ' + videoSSRC1 + ' ' + videoSSRC2 + ' ' + videoSSRC3 + '\r\n', '');
+
+ offer2.sdp = offer2.sdp.replace('a=ssrc:' + videoSSRC1 + ' msid:' + msid[2], 'a=ssrc:' + videoSSRC1 + ' msid:low low');
+ offer2.sdp = offer2.sdp.replace('a=ssrc:' + rtxSSRC1 + ' msid:' + msid[2], 'a=ssrc:' + rtxSSRC1 + ' msid:low low');
+
+ offer2.sdp = offer2.sdp.replace('a=ssrc:' + videoSSRC2 + ' msid:' + msid[2], 'a=ssrc:' + videoSSRC2 + ' msid:mid mid');
+ offer2.sdp = offer2.sdp.replace('a=ssrc:' + rtxSSRC2 + ' msid:' + msid[2], 'a=ssrc:' + rtxSSRC2 + ' msid:mid mid');
+
+ offer2.sdp = offer2.sdp.replace('a=ssrc:' + videoSSRC3 + ' msid:' + msid[2], 'a=ssrc:' + videoSSRC3 + ' msid:hi hi');
+ offer2.sdp = offer2.sdp.replace('a=ssrc:' + rtxSSRC3 + ' msid:' + msid[2], 'a=ssrc:' + rtxSSRC3 + ' msid:hi hi');
+ await Promise.all([
+ pc1.setLocalDescription(offer),
+ pc2.setRemoteDescription(offer2),
+ ]);
+
+ state = "set description";
+
+ const answer = await pc2.createAnswer();
+
+ state = "got answer";
+
+ await Promise.all([
+ pc2.setLocalDescription(answer),
+ pc1.setRemoteDescription(answer),
+ ]);
+
+ state = "set description 2";
+
+ await low.play();
+ state = "video low plays";
+
+ assert_equals(low.srcObject.getVideoTracks()[0].getSettings().height, 240);
+ assert_equals(low.srcObject.getVideoTracks()[0].getSettings().width, 320);
+
+ await mid.play();
+ state = "video mid plays";
+
+ assert_equals(mid.srcObject.getVideoTracks()[0].getSettings().height, 480);
+ assert_equals(mid.srcObject.getVideoTracks()[0].getSettings().width, 640);
+
+ finished = true;
+}, "Testing simulcast");
+</script>
+</body>
+</html>