Commit 5825fead authored by Guido Urdaneta's avatar Guido Urdaneta Committed by Commit Bot

Add support for resizeMode in [MediaStreamTrack|InputDeviceInfo].getCapabilities()

Intent to Ship:
https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/V2srjdzRCXE

Bug: 854980
Change-Id: Ie49e3d2f9b8afe1abc552172d78dd86088b6ce41
Reviewed-on: https://chromium-review.googlesource.com/c/1310714
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarHenrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#610108}
parent 83ec360f
...@@ -16,65 +16,30 @@ ...@@ -16,65 +16,30 @@
<script> <script>
"use strict"; "use strict";
//NOTE ALEX: for completion, a test for ondevicechange event is missing. //NOTE ALEX: for completion, a test for ondevicechange event is missing.
promise_test(function() { promise_test(async () => {
assert_true(undefined !== navigator.mediaDevices.enumerateDevices, "navigator.mediaDevices.enumerateDevices exists"); assert_true(undefined !== navigator.mediaDevices.enumerateDevices, "navigator.mediaDevices.enumerateDevices exists");
return navigator.mediaDevices.enumerateDevices().then(function(list) { const device_list = await navigator.mediaDevices.enumerateDevices();
for (let mediainfo of list) { for (const mediainfo of device_list) {
assert_true(undefined !== mediainfo.deviceId, "mediaInfo's deviceId should exist."); assert_true(undefined !== mediainfo.deviceId, "mediaInfo's deviceId should exist.");
assert_true(undefined !== mediainfo.kind, "mediaInfo's kind should exist."); assert_true(undefined !== mediainfo.kind, "mediaInfo's kind should exist.");
assert_true(undefined !== mediainfo.label, "mediaInfo's label should exist."); assert_in_array(mediainfo.kind, ["videoinput", "audioinput", "audiooutput"]);
assert_true(undefined !== mediainfo.groupId, "mediaInfo's groupId should exist."); assert_true(undefined !== mediainfo.label, "mediaInfo's label should exist.");
// TODO the values of some of those fields should be empty string by default if no permission has been requested. assert_true(undefined !== mediainfo.groupId, "mediaInfo's groupId should exist.");
if ( mediainfo.kind == "audioinput" || mediainfo.kind == "videoinput") {
assert_true(mediainfo instanceof InputDeviceInfo);
var capabilities = mediainfo.getCapabilities();
assert_equals(typeof capabilities, "object", "capabilities must be an object.");
assert_equals(typeof capabilities.deviceId, "string", "deviceId must be a string.");
assert_equals(typeof capabilities.groupId, "string", "groupId must be a string.");
if (mediainfo.kind == "audioinput") {
assert_equals(typeof capabilities.echoCancellation, "object", "echoCancellation must be an object.");
assert_equals(typeof capabilities.autoGainControl, "object", "autoGainControl must be an object.");
assert_equals(typeof capabilities.noiseSuppression, "object", "noiseSuppression must be an object.");
}
if (mediainfo.kind == "videoinput") {
assert_equals(typeof capabilities.facingMode, "object", "facingMode must be an object.");
verifyVideoRangeProperties(capabilities);
}
} else if ( mediainfo.kind == "audiooutput" ) {
assert_true(mediainfo instanceof MediaDeviceInfo);
} else {
assert_unreached("mediainfo.kind should be one of 'audioinput', 'videoinput', or 'audiooutput'.")
}
}
});
}, "mediaDevices.enumerateDevices() is present and working on navigator");
function verifyVideoRangeProperties(capabilities) {
if (capabilities.hasOwnProperty('width')) {
assert_equals(Object.keys(capabilities.width).length, 2);
assert_true(capabilities.width.hasOwnProperty('min'));
assert_true(capabilities.width.hasOwnProperty('max'));
assert_less_than_equal(capabilities.width.min, capabilities.width.max);
}
if (capabilities.hasOwnProperty('height')) {
assert_equals(Object.keys(capabilities.height).length, 2);
assert_true(capabilities.height.hasOwnProperty('min'));
assert_true(capabilities.height.hasOwnProperty('max'));
assert_less_than_equal(capabilities.height.min, capabilities.height.max);
} }
if (capabilities.hasOwnProperty('aspectRatio')) { }, "mediaDevices.enumerateDevices() is present and working");
assert_equals(Object.keys(capabilities.aspectRatio).length, 2);
assert_true(capabilities.aspectRatio.hasOwnProperty('min')); promise_test(async () => {
assert_true(capabilities.aspectRatio.hasOwnProperty('max')); const device_list = await navigator.mediaDevices.enumerateDevices();
assert_less_than_equal(capabilities.aspectRatio.min, capabilities.aspectRatio.max); for (const mediainfo of device_list) {
} if (mediainfo.kind == "audioinput" || mediainfo.kind == "videoinput") {
if (capabilities.hasOwnProperty('frameRate')) { assert_true(mediainfo instanceof InputDeviceInfo);
assert_equals(Object.keys(capabilities.frameRate).length, 2); } else if ( mediainfo.kind == "audiooutput" ) {
assert_true(capabilities.frameRate.hasOwnProperty('min')); assert_true(mediainfo instanceof MediaDeviceInfo);
assert_true(capabilities.frameRate.hasOwnProperty('max')); } else {
assert_less_than_equal(capabilities.frameRate.min, capabilities.frameRate.max); assert_unreached("mediainfo.kind should be one of 'audioinput', 'videoinput', or 'audiooutput'.")
}
} }
} }, "InputDeviceInfo is supported");
</script> </script>
</body> </body>
</html> </html>
This is a testharness.js-based test.
PASS Setup audio MediaStreamTrack getCapabilities() test for volume
PASS Setup audio MediaStreamTrack getCapabilities() test for sampleRate
PASS Setup audio MediaStreamTrack getCapabilities() test for sampleSize
PASS Setup audio MediaStreamTrack getCapabilities() test for echoCancellation
PASS Setup audio MediaStreamTrack getCapabilities() test for autoGainControl
PASS Setup audio MediaStreamTrack getCapabilities() test for noiseSuppression
PASS Setup audio MediaStreamTrack getCapabilities() test for latency
PASS Setup audio MediaStreamTrack getCapabilities() test for channelCount
PASS Setup audio MediaStreamTrack getCapabilities() test for deviceId
PASS Setup audio MediaStreamTrack getCapabilities() test for groupId
PASS Setup video MediaStreamTrack getCapabilities() test for width
PASS Setup video MediaStreamTrack getCapabilities() test for height
PASS Setup video MediaStreamTrack getCapabilities() test for aspectRatio
PASS Setup video MediaStreamTrack getCapabilities() test for frameRate
PASS Setup video MediaStreamTrack getCapabilities() test for facingMode
PASS Setup video MediaStreamTrack getCapabilities() test for resizeMode
PASS Setup video MediaStreamTrack getCapabilities() test for deviceId
PASS Setup video MediaStreamTrack getCapabilities() test for groupId
PASS Setup audio InputDeviceInfo getCapabilities() test for volume
PASS Setup audio InputDeviceInfo getCapabilities() test for sampleRate
PASS Setup audio InputDeviceInfo getCapabilities() test for sampleSize
PASS Setup audio InputDeviceInfo getCapabilities() test for echoCancellation
PASS Setup audio InputDeviceInfo getCapabilities() test for autoGainControl
PASS Setup audio InputDeviceInfo getCapabilities() test for noiseSuppression
PASS Setup audio InputDeviceInfo getCapabilities() test for latency
PASS Setup audio InputDeviceInfo getCapabilities() test for channelCount
PASS Setup audio InputDeviceInfo getCapabilities() test for deviceId
PASS Setup audio InputDeviceInfo getCapabilities() test for groupId
PASS Setup video InputDeviceInfo getCapabilities() test for width
PASS Setup video InputDeviceInfo getCapabilities() test for height
PASS Setup video InputDeviceInfo getCapabilities() test for aspectRatio
PASS Setup video InputDeviceInfo getCapabilities() test for frameRate
PASS Setup video InputDeviceInfo getCapabilities() test for facingMode
PASS Setup video InputDeviceInfo getCapabilities() test for resizeMode
PASS Setup video InputDeviceInfo getCapabilities() test for deviceId
PASS Setup video InputDeviceInfo getCapabilities() test for groupId
FAIL Audio track getCapabilities() volume property present. assert_true: expected true got false
FAIL Audio track getCapabilities() volume properly supported. assert_equals: expected "object" but got "undefined"
FAIL Audio track getCapabilities() sampleRate property present. assert_true: expected true got false
FAIL Audio track getCapabilities() sampleRate properly supported. assert_equals: expected "object" but got "undefined"
FAIL Audio track getCapabilities() sampleSize property present. assert_true: expected true got false
FAIL Audio track getCapabilities() sampleSize properly supported. assert_equals: expected "object" but got "undefined"
PASS Audio track getCapabilities() echoCancellation property present.
PASS Audio track getCapabilities() echoCancellation properly supported.
PASS Audio track getCapabilities() autoGainControl property present.
PASS Audio track getCapabilities() autoGainControl properly supported.
PASS Audio track getCapabilities() noiseSuppression property present.
PASS Audio track getCapabilities() noiseSuppression properly supported.
FAIL Audio track getCapabilities() latency property present. assert_true: expected true got false
FAIL Audio track getCapabilities() latency properly supported. assert_equals: expected "object" but got "undefined"
FAIL Audio track getCapabilities() channelCount property present. assert_true: expected true got false
FAIL Audio track getCapabilities() channelCount properly supported. assert_equals: expected "object" but got "undefined"
PASS Audio track getCapabilities() deviceId property present.
PASS Audio track getCapabilities() deviceId properly supported.
PASS Audio track getCapabilities() groupId property present.
PASS Audio track getCapabilities() groupId properly supported.
PASS Video track getCapabilities() width property present.
PASS Video track getCapabilities() width properly supported.
PASS Video track getCapabilities() height property present.
PASS Video track getCapabilities() height properly supported.
PASS Video track getCapabilities() aspectRatio property present.
PASS Video track getCapabilities() aspectRatio properly supported.
PASS Video track getCapabilities() frameRate property present.
PASS Video track getCapabilities() frameRate properly supported.
PASS Video track getCapabilities() facingMode property present.
PASS Video track getCapabilities() facingMode properly supported.
PASS Video track getCapabilities() resizeMode property present.
PASS Video track getCapabilities() resizeMode properly supported.
PASS Video track getCapabilities() resizeMode properly supported. Value: none
PASS Video track getCapabilities() resizeMode properly supported. Value: crop-and-scale
PASS Video track getCapabilities() deviceId property present.
PASS Video track getCapabilities() deviceId properly supported.
PASS Video track getCapabilities() groupId property present.
PASS Video track getCapabilities() groupId properly supported.
FAIL Audio device getCapabilities() volume property present. assert_true: expected true got false
FAIL Audio device getCapabilities() volume properly supported. assert_equals: expected "object" but got "undefined"
FAIL Audio device getCapabilities() sampleRate property present. assert_true: expected true got false
FAIL Audio device getCapabilities() sampleRate properly supported. assert_equals: expected "object" but got "undefined"
FAIL Audio device getCapabilities() sampleSize property present. assert_true: expected true got false
FAIL Audio device getCapabilities() sampleSize properly supported. assert_equals: expected "object" but got "undefined"
PASS Audio device getCapabilities() echoCancellation property present.
PASS Audio device getCapabilities() echoCancellation properly supported.
PASS Audio device getCapabilities() autoGainControl property present.
PASS Audio device getCapabilities() autoGainControl properly supported.
PASS Audio device getCapabilities() noiseSuppression property present.
PASS Audio device getCapabilities() noiseSuppression properly supported.
FAIL Audio device getCapabilities() latency property present. assert_true: expected true got false
FAIL Audio device getCapabilities() latency properly supported. assert_equals: expected "object" but got "undefined"
FAIL Audio device getCapabilities() channelCount property present. assert_true: expected true got false
FAIL Audio device getCapabilities() channelCount properly supported. assert_equals: expected "object" but got "undefined"
PASS Audio device getCapabilities() deviceId property present.
PASS Audio device getCapabilities() deviceId properly supported.
PASS Audio device getCapabilities() groupId property present.
PASS Audio device getCapabilities() groupId properly supported.
PASS Video device getCapabilities() width property present.
PASS Video device getCapabilities() width properly supported.
PASS Video device getCapabilities() height property present.
PASS Video device getCapabilities() height properly supported.
PASS Video device getCapabilities() aspectRatio property present.
PASS Video device getCapabilities() aspectRatio properly supported.
PASS Video device getCapabilities() frameRate property present.
PASS Video device getCapabilities() frameRate properly supported.
PASS Video device getCapabilities() facingMode property present.
PASS Video device getCapabilities() facingMode properly supported.
PASS Video device getCapabilities() resizeMode property present.
PASS Video device getCapabilities() resizeMode properly supported.
PASS Video device getCapabilities() resizeMode properly supported. Value: none
PASS Video device getCapabilities() resizeMode properly supported. Value: crop-and-scale
PASS Video device getCapabilities() deviceId property present.
PASS Video device getCapabilities() deviceId properly supported.
PASS Video device getCapabilities() groupId property present.
PASS Video device getCapabilities() groupId properly supported.
Harness: the test ran to completion.
<!doctype html> <!doctype html>
<title>MediaStreamTrack GetCapabilities</title> <title>MediaStreamTrack and InputDeviceInfo GetCapabilities</title>
<p class="instructions">This test checks for the presence of audio and video properties in
<code>MediaStreamTrack.getCapabilities()</code> method.</p>
<script src=/resources/testharness.js></script> <script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script> <script src=/resources/testharnessreport.js></script>
<script> <script>
promise_test(() => {
return navigator.mediaDevices.getUserMedia({audio: true, video: true}) const audioProperties = [
.then(stream => { {name: "volume", type: "number"},
var audioCapabilities = stream.getAudioTracks()[0].getCapabilities(); {name: "sampleRate", type: "number"},
var videoCapabilities = stream.getVideoTracks()[0].getCapabilities(); {name: "sampleSize", type: "number"},
assert_true(undefined !== audioCapabilities.deviceId, "MediaTrackCapabilities's deviceId should exist for an audio track."); {name: "echoCancellation", type: "boolean"},
assert_true(undefined !== audioCapabilities.groupId, "MediaTrackCapabilities's groupId should exist for an audio track."); {name: "autoGainControl", type: "boolean"},
assert_true(undefined !== audioCapabilities.echoCancellation, "MediaTrackCapabilities's echoCancellation should exist for an audio track."); {name: "noiseSuppression", type: "boolean"},
assert_true(undefined !== audioCapabilities.autoGainControl, "MediaTrackCapabilities's autoGainControl should exist for an audio track."); {name: "latency", type: "number"},
assert_true(undefined !== audioCapabilities.noiseSuppression, "MediaTrackCapabilities's noiseSuppression should exist for an audio track."); {name: "channelCount", type: "number"},
assert_true(undefined !== videoCapabilities.deviceId, "MediaTrackCapabilities's deviceId should exist for a video track."); {name: "deviceId", type: "string"},
assert_true(undefined !== videoCapabilities.groupId, "MediaTrackCapabilities's groupId should exist for a video track."); {name: "groupId", type: "string"}
}); ];
const videoProperties = [
{name: "width", type: "number"},
{name: "height", type: "number"},
{name: "aspectRatio", type: "number"},
{name: "frameRate", type: "number"},
{name: "facingMode", type: "enum-any", validValues: ["user", "environment", "left", "right"]},
{name: "resizeMode", type: "enum-all", validValues: ["none", "crop-and-scale"]},
{name: "deviceId", type: "string"},
{name: "groupId", type: "string"},
];
function verifyBooleanCapability(capability) {
assert_less_than_equal(capability.length, 2);
capability.forEach(c => assert_equals(typeof c, "boolean"));
}
function verifyNumberCapability(capability) {
assert_equals(typeof capability, "object");
assert_equals(Object.keys(capability).length, 2);
assert_true(capability.hasOwnProperty('min'));
assert_true(capability.hasOwnProperty('max'));
assert_less_than_equal(capability.min, capability.max);
}
// Verify that any value provided by an enum capability is in the set of valid
// values.
function verifyEnumAnyCapability(capability, enumMembers) {
capability.forEach(c => {
assert_equals(typeof c, "string");
assert_in_array(c, enumMembers);
}); });
}
// Verify that all required values are supported by a capability.
function verifyEnumAllCapability(capability, enumMembers, testNamePrefix) {
enumMembers.forEach(member => {
test(() => {
assert_in_array(member, capability);
}, testNamePrefix + " Value: " + member);
});
}
function testCapabilities(capabilities, property, testNamePrefix) {
let testName = testNamePrefix + " " + property.name;
test(() => {
assert_true(capabilities.hasOwnProperty(property.name));
}, testName + " property present.");
const capability = capabilities[property.name];
testName += " properly supported.";
if (property.type == "string") {
test(() => {
assert_equals(typeof capability, "string");
}, testName);
}
if (property.type == "boolean") {
test(() => {
verifyBooleanCapability(capability);
}, testName);
}
if (property.type == "number") {
test(() => {
verifyNumberCapability(capability);
}, testName);
}
if (property.type.startsWith("enum")) {
test(() => {
verifyEnumAnyCapability(capability, property.validValues);
}, testName);
if (property.type == "enum-all") {
verifyEnumAllCapability(capability, property.validValues, testName);
}
}
}
{
audioProperties.forEach(property => {
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
t.add_cleanup(() => stream.getAudioTracks()[0].stop());
const audioCapabilities = stream.getAudioTracks()[0].getCapabilities();
testCapabilities(audioCapabilities, property, "Audio track getCapabilities()");
}, "Setup audio MediaStreamTrack getCapabilities() test for " + property.name);
});
videoProperties.forEach(property => {
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({video: true});
t.add_cleanup(() => stream.getVideoTracks()[0].stop());
const audioCapabilities = stream.getVideoTracks()[0].getCapabilities();
testCapabilities(audioCapabilities, property, "Video track getCapabilities()");
}, "Setup video MediaStreamTrack getCapabilities() test for " + property.name);
});
}
{
audioProperties.forEach(property => {
promise_test(async t => {
const devices = await navigator.mediaDevices.enumerateDevices();
for (const device of devices) {
// Test only one device.
if (device.kind == "audioinput") {
assert_inherits(device, "getCapabilities");
const capabilities = device.getCapabilities();
testCapabilities(capabilities, property, "Audio device getCapabilities()");
break;
}
}
}, "Setup audio InputDeviceInfo getCapabilities() test for " + property.name);
});
videoProperties.forEach(property => {
promise_test(async t => {
const devices = await navigator.mediaDevices.enumerateDevices();
for (const device of devices) {
// Test only one device.
if (device.kind == "videoinput") {
assert_inherits(device, "getCapabilities");
const capabilities = device.getCapabilities();
testCapabilities(capabilities, property, "Video device getCapabilities()");
break;
}
}
}, "Setup video InputDeviceInfo getCapabilities() test for " + property.name);
});
}
</script> </script>
...@@ -22,6 +22,9 @@ promise_test(function() { ...@@ -22,6 +22,9 @@ promise_test(function() {
assert_true(capabilities.hasOwnProperty('groupId')); assert_true(capabilities.hasOwnProperty('groupId'));
assert_true(capabilities.hasOwnProperty('facingMode')); assert_true(capabilities.hasOwnProperty('facingMode'));
verifyVideoRangeProperties(capabilities); verifyVideoRangeProperties(capabilities);
assert_true(capabilities.hasOwnProperty('resizeMode'));
assert_in_array('none', capabilities.resizeMode);
assert_in_array('crop-and-scale', capabilities.resizeMode);
}); });
}, 'getCapabilities() support for getUserMedia() video track.'); }, 'getCapabilities() support for getUserMedia() video track.');
...@@ -42,6 +45,9 @@ test(function() { ...@@ -42,6 +45,9 @@ test(function() {
assert_true(capabilities.hasOwnProperty('facingMode')); assert_true(capabilities.hasOwnProperty('facingMode'));
assert_equals(Object.keys(capabilities.facingMode).length, 0); assert_equals(Object.keys(capabilities.facingMode).length, 0);
verifyVideoRangeProperties(capabilities); verifyVideoRangeProperties(capabilities);
assert_true(capabilities.hasOwnProperty('resizeMode'));
assert_in_array('none', capabilities.resizeMode);
assert_in_array('crop-and-scale', capabilities.resizeMode);
}, 'getCapabilities() support for video track associated with a canvas element.'); }, 'getCapabilities() support for video track associated with a canvas element.');
test(function() { test(function() {
...@@ -60,6 +66,9 @@ test(function() { ...@@ -60,6 +66,9 @@ test(function() {
assert_true(videoCapabilities.hasOwnProperty('facingMode')); assert_true(videoCapabilities.hasOwnProperty('facingMode'));
assert_equals(Object.keys(videoCapabilities.facingMode).length, 0); assert_equals(Object.keys(videoCapabilities.facingMode).length, 0);
verifyVideoRangeProperties(videoCapabilities); verifyVideoRangeProperties(videoCapabilities);
assert_true(videoCapabilities.hasOwnProperty('resizeMode'));
assert_in_array('none', videoCapabilities.resizeMode);
assert_in_array('crop-and-scale', videoCapabilities.resizeMode);
}; };
}, 'getCapabilities() support for audio and video tracks associated with a video element.'); }, 'getCapabilities() support for audio and video tracks associated with a video element.');
...@@ -81,6 +90,9 @@ promise_test(function() { ...@@ -81,6 +90,9 @@ promise_test(function() {
assert_true(videoCapabilities.hasOwnProperty('deviceId')); assert_true(videoCapabilities.hasOwnProperty('deviceId'));
assert_true(videoCapabilities.hasOwnProperty('facingMode')); assert_true(videoCapabilities.hasOwnProperty('facingMode'));
assert_equals(Object.keys(videoCapabilities.facingMode).length, 0); assert_equals(Object.keys(videoCapabilities.facingMode).length, 0);
assert_true(videoCapabilities.hasOwnProperty('resizeMode'));
assert_in_array('none', videoCapabilities.resizeMode);
assert_in_array('crop-and-scale', videoCapabilities.resizeMode);
}); });
}, 'getCapabilities() support for audio and video tracks associated with a RTCPeerConnection.'); }, 'getCapabilities() support for audio and video tracks associated with a RTCPeerConnection.');
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include "third_party/blink/renderer/modules/mediastream/input_device_info.h" #include "third_party/blink/renderer/modules/mediastream/input_device_info.h"
#include <algorithm> #include <algorithm>
#include "third_party/blink/public/platform/web_media_stream_track.h"
#include "third_party/blink/renderer/modules/mediastream/media_track_capabilities.h" #include "third_party/blink/renderer/modules/mediastream/media_track_capabilities.h"
namespace blink { namespace blink {
...@@ -131,6 +133,8 @@ MediaTrackCapabilities* InputDeviceInfo::getCapabilities() const { ...@@ -131,6 +133,8 @@ MediaTrackCapabilities* InputDeviceInfo::getCapabilities() const {
break; break;
} }
capabilities->setFacingMode(facing_mode); capabilities->setFacingMode(facing_mode);
capabilities->setResizeMode({WebMediaStreamTrack::kResizeModeNone,
WebMediaStreamTrack::kResizeModeRescale});
} }
return capabilities; return capabilities;
} }
......
...@@ -393,6 +393,8 @@ MediaTrackCapabilities* MediaStreamTrack::getCapabilities() const { ...@@ -393,6 +393,8 @@ MediaTrackCapabilities* MediaStreamTrack::getCapabilities() const {
break; break;
} }
capabilities->setFacingMode(facing_mode); capabilities->setFacingMode(facing_mode);
capabilities->setResizeMode({WebMediaStreamTrack::kResizeModeNone,
WebMediaStreamTrack::kResizeModeRescale});
} }
return capabilities; return capabilities;
} }
......
...@@ -8,6 +8,7 @@ dictionary MediaTrackCapabilities { ...@@ -8,6 +8,7 @@ dictionary MediaTrackCapabilities {
DoubleRange aspectRatio; DoubleRange aspectRatio;
DoubleRange frameRate; DoubleRange frameRate;
sequence<DOMString> facingMode; sequence<DOMString> facingMode;
sequence<DOMString> resizeMode;
sequence<boolean> echoCancellation; sequence<boolean> echoCancellation;
// See http://crbug.com/846270. // See http://crbug.com/846270.
[OriginTrialEnabled=ExperimentalHardwareEchoCancellation] sequence<DOMString> echoCancellationType; [OriginTrialEnabled=ExperimentalHardwareEchoCancellation] sequence<DOMString> echoCancellationType;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment