Commit f2c35e4d authored by Simon Jackson's avatar Simon Jackson Committed by Chromium LUCI CQ

Add support for encoding CBR audio files with `MediaRecorder`.

The `MediaRecorder` spec was recently updated to allow either constant
bitrate (CBR) or variable bitrate (VBR) encoding to be specified with
`MediaRecorderOptions`, previously VBR was assumed when using anything
other than uncompressed PCM.

This change adds support for the updated specification and for enabling
the hard CBR mode of the Opus encoder when CBR mode is configured for
the `MediaRecorder`.

Intent-to-ship: https://groups.google.com/a/chromium.org/g/blink-dev/c/ewJTgBMTxGg/m/w2qGgVCeAQAJ

Bug: 980559
Change-Id: I2e4d6305e951dedfbbcd2447b69fc4f81209a3e3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1731907Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Reviewed-by: default avatarFelicia Lim <flim@chromium.org>
Reviewed-by: default avatarMarkus Handell <handellm@google.com>
Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Commit-Queue: Chrome Cunningham <chcunningham@chromium.org>
Cr-Commit-Position: refs/heads/master@{#842887}
parent df646211
......@@ -975,6 +975,7 @@ Siddharth Bagai <b.siddharth@samsung.com>
Siddharth Shankar <funkysidd@gmail.com>
Simeon Kuran <simeon.kuran@gmail.com>
Simon Arlott <simon.arlott@gmail.com>
Simon Jackson <simon.jackson@sonocent.com>
Simon La Macchia <smacchia@amazon.com>
Siva Kumar Gunturi <siva.gunturi@samsung.com>
Sohan Jyoti Ghosh <sohan.jyoti@huawei.com>
......
......@@ -65,9 +65,11 @@ namespace blink {
AudioTrackOpusEncoder::AudioTrackOpusEncoder(
OnEncodedAudioCB on_encoded_audio_cb,
int32_t bits_per_second)
int32_t bits_per_second,
bool vbr_enabled)
: AudioTrackEncoder(std::move(on_encoded_audio_cb)),
bits_per_second_(bits_per_second),
vbr_enabled_(vbr_enabled),
opus_encoder_(nullptr) {}
AudioTrackOpusEncoder::~AudioTrackOpusEncoder() {
......@@ -144,6 +146,12 @@ void AudioTrackOpusEncoder::OnSetFormat(
DLOG(ERROR) << "Failed to set Opus bitrate: " << bitrate;
return;
}
const opus_int32 vbr_enabled = static_cast<opus_int32>(vbr_enabled_);
if (opus_encoder_ctl(opus_encoder_, OPUS_SET_VBR(vbr_enabled)) != OPUS_OK) {
DLOG(ERROR) << "Failed to set Opus VBR mode: " << vbr_enabled;
return;
}
}
void AudioTrackOpusEncoder::EncodeAudio(
......
......@@ -24,7 +24,8 @@ class AudioTrackOpusEncoder : public AudioTrackEncoder,
public media::AudioConverter::InputCallback {
public:
AudioTrackOpusEncoder(OnEncodedAudioCB on_encoded_audio_cb,
int32_t bits_per_second);
int32_t bits_per_second,
bool vbr_enabled = true);
void OnSetFormat(const media::AudioParameters& params) override;
void EncodeAudio(std::unique_ptr<media::AudioBus> input_bus,
......@@ -44,6 +45,11 @@ class AudioTrackOpusEncoder : public AudioTrackEncoder,
// Target bitrate for Opus. If 0, Opus provide automatic bitrate is used.
const int32_t bits_per_second_;
// Opus operates in VBR or constrained VBR modes even when a fixed bitrate
// is specified, unless 'hard' CBR is explicitly enabled by disabling VBR
// mode with this flag.
const bool vbr_enabled_;
// Output parameters after audio conversion. This differs from the input
// parameters only in sample_rate() and frames_per_buffer(): output should be
// 48ksamples/s and 2880, respectively.
......
......@@ -48,12 +48,14 @@ AudioTrackRecorder::AudioTrackRecorder(
MediaStreamComponent* track,
OnEncodedAudioCB on_encoded_audio_cb,
base::OnceClosure on_track_source_ended_cb,
int32_t bits_per_second)
int32_t bits_per_second,
BitrateMode bitrate_mode)
: TrackRecorder(std::move(on_track_source_ended_cb)),
track_(track),
encoder_(CreateAudioEncoder(codec,
std::move(on_encoded_audio_cb),
bits_per_second)),
bits_per_second,
bitrate_mode)),
encoder_thread_(Thread::CreateThread(
ThreadCreationParams(ThreadType::kAudioEncoderThread))),
encoder_task_runner_(encoder_thread_->GetTaskRunner()) {
......@@ -75,7 +77,8 @@ AudioTrackRecorder::~AudioTrackRecorder() {
scoped_refptr<AudioTrackEncoder> AudioTrackRecorder::CreateAudioEncoder(
CodecId codec,
OnEncodedAudioCB on_encoded_audio_cb,
int32_t bits_per_second) {
int32_t bits_per_second,
BitrateMode bitrate_mode) {
if (codec == CodecId::PCM) {
return base::MakeRefCounted<AudioTrackPcmEncoder>(
media::BindToCurrentLoop(std::move(on_encoded_audio_cb)));
......@@ -83,8 +86,8 @@ scoped_refptr<AudioTrackEncoder> AudioTrackRecorder::CreateAudioEncoder(
// All other paths will use the AudioTrackOpusEncoder.
return base::MakeRefCounted<AudioTrackOpusEncoder>(
media::BindToCurrentLoop(std::move(on_encoded_audio_cb)),
bits_per_second);
media::BindToCurrentLoop(std::move(on_encoded_audio_cb)), bits_per_second,
bitrate_mode == BitrateMode::VARIABLE);
}
void AudioTrackRecorder::OnSetFormat(const media::AudioParameters& params) {
......
......@@ -41,6 +41,8 @@ class MODULES_EXPORT AudioTrackRecorder
LAST
};
enum class BitrateMode { CONSTANT, VARIABLE };
using OnEncodedAudioCB =
base::RepeatingCallback<void(const media::AudioParameters& params,
std::string encoded_data,
......@@ -52,7 +54,8 @@ class MODULES_EXPORT AudioTrackRecorder
MediaStreamComponent* track,
OnEncodedAudioCB on_encoded_audio_cb,
base::OnceClosure on_track_source_ended_cb,
int32_t bits_per_second);
int32_t bits_per_second,
BitrateMode bitrate_mode);
~AudioTrackRecorder() override;
// Implement MediaStreamAudioSink.
......@@ -69,7 +72,8 @@ class MODULES_EXPORT AudioTrackRecorder
static scoped_refptr<AudioTrackEncoder> CreateAudioEncoder(
CodecId codec,
OnEncodedAudioCB on_encoded_audio_cb,
int32_t bits_per_second);
int32_t bits_per_second,
BitrateMode bitrate_mode);
void ConnectToTrack();
void DisconnectFromTrack();
......
......@@ -53,6 +53,7 @@ struct ATRTestParams {
const media::ChannelLayout channel_layout;
const int sample_rate;
const AudioTrackRecorder::CodecId codec;
const AudioTrackRecorder::BitrateMode bitrateMode;
};
const ATRTestParams kATRTestParams[] = {
......@@ -60,27 +61,45 @@ const ATRTestParams kATRTestParams[] = {
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY, /* input format */
media::CHANNEL_LAYOUT_STEREO, /* channel layout */
kDefaultSampleRate, /* sample rate */
AudioTrackRecorder::CodecId::OPUS}, /* codec for encoding */
AudioTrackRecorder::CodecId::OPUS, /* codec for encoding */
AudioTrackRecorder::BitrateMode::VARIABLE}, /* constant/variable rate */
// Change to mono:
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY, media::CHANNEL_LAYOUT_MONO,
kDefaultSampleRate, AudioTrackRecorder::CodecId::OPUS},
kDefaultSampleRate, AudioTrackRecorder::CodecId::OPUS,
AudioTrackRecorder::BitrateMode::VARIABLE},
// Different sampling rate as well:
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY, media::CHANNEL_LAYOUT_MONO,
24000, AudioTrackRecorder::CodecId::OPUS},
24000, AudioTrackRecorder::CodecId::OPUS,
AudioTrackRecorder::BitrateMode::VARIABLE},
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO, 8000, AudioTrackRecorder::CodecId::OPUS},
media::CHANNEL_LAYOUT_STEREO, 8000, AudioTrackRecorder::CodecId::OPUS,
AudioTrackRecorder::BitrateMode::VARIABLE},
// Using a non-default Opus sampling rate (48, 24, 16, 12, or 8 kHz).
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY, media::CHANNEL_LAYOUT_MONO,
22050, AudioTrackRecorder::CodecId::OPUS},
22050, AudioTrackRecorder::CodecId::OPUS,
AudioTrackRecorder::BitrateMode::VARIABLE},
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO, 44100, AudioTrackRecorder::CodecId::OPUS},
media::CHANNEL_LAYOUT_STEREO, 44100, AudioTrackRecorder::CodecId::OPUS,
AudioTrackRecorder::BitrateMode::VARIABLE},
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO, 96000, AudioTrackRecorder::CodecId::OPUS},
media::CHANNEL_LAYOUT_STEREO, 96000, AudioTrackRecorder::CodecId::OPUS,
AudioTrackRecorder::BitrateMode::VARIABLE},
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY, media::CHANNEL_LAYOUT_MONO,
kDefaultSampleRate, AudioTrackRecorder::CodecId::PCM},
kDefaultSampleRate, AudioTrackRecorder::CodecId::PCM,
AudioTrackRecorder::BitrateMode::VARIABLE},
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO, kDefaultSampleRate,
AudioTrackRecorder::CodecId::PCM,
AudioTrackRecorder::BitrateMode::VARIABLE},
// Use Opus in constatnt bitrate mode:
{media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO, kDefaultSampleRate,
AudioTrackRecorder::CodecId::PCM},
AudioTrackRecorder::CodecId::OPUS,
AudioTrackRecorder::BitrateMode::CONSTANT},
};
class AudioTrackRecorderTest : public testing::TestWithParam<ATRTestParams> {
......@@ -112,7 +131,7 @@ class AudioTrackRecorderTest : public testing::TestWithParam<ATRTestParams> {
WTF::BindRepeating(&AudioTrackRecorderTest::OnEncodedAudio,
WTF::Unretained(this)),
ConvertToBaseOnceCallback(CrossThreadBindOnce([] {})),
0 /* bits_per_second */);
0 /* bits_per_second */, GetParam().bitrateMode);
}
~AudioTrackRecorderTest() {
......@@ -260,9 +279,17 @@ TEST_P(AudioTrackRecorderTest, OnDataOpus) {
// Give ATR initial audio parameters.
audio_track_recorder_->OnSetFormat(first_params_);
std::vector<std::size_t> encodedPacketSizes;
// TODO(ajose): consider adding WillOnce(SaveArg...) and inspecting, as done
// in VTR unittests. http://crbug.com/548856
EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _)).Times(1);
EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
.Times(1)
.WillOnce([&encodedPacketSizes](const media::AudioParameters&,
std::string encoded_data,
base::TimeTicks) {
encodedPacketSizes.push_back(encoded_data.size());
});
audio_track_recorder_->OnData(*GetFirstSourceAudioBus(),
base::TimeTicks::Now());
for (int i = 0; i < kRatioInputToOutputFrames - 1; ++i) {
......@@ -273,9 +300,14 @@ TEST_P(AudioTrackRecorderTest, OnDataOpus) {
EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
.Times(1)
// Only reset the decoder once we've heard back:
.WillOnce(
.WillOnce(testing::DoAll(
RunOnceClosure(WTF::Bind(&AudioTrackRecorderTest::ResetDecoder,
WTF::Unretained(this), second_params_)));
WTF::Unretained(this), second_params_)),
[&encodedPacketSizes](const media::AudioParameters&,
std::string encoded_data, base::TimeTicks) {
encodedPacketSizes.push_back(encoded_data.size());
}));
audio_track_recorder_->OnData(*GetFirstSourceAudioBus(),
base::TimeTicks::Now());
for (int i = 0; i < kRatioInputToOutputFrames - 1; ++i) {
......@@ -296,7 +328,13 @@ TEST_P(AudioTrackRecorderTest, OnDataOpus) {
// Send audio with different params.
EXPECT_CALL(*this, DoOnEncodedAudio(_, _, _))
.Times(1)
.WillOnce(RunOnceClosure(std::move(quit_closure)));
.WillOnce(testing::DoAll(
RunOnceClosure(std::move(quit_closure)),
[&encodedPacketSizes](const media::AudioParameters&,
std::string encoded_data, base::TimeTicks) {
encodedPacketSizes.push_back(encoded_data.size());
}));
audio_track_recorder_->OnData(*GetSecondSourceAudioBus(),
base::TimeTicks::Now());
for (int i = 0; i < kRatioInputToOutputFrames - 1; ++i) {
......@@ -304,6 +342,14 @@ TEST_P(AudioTrackRecorderTest, OnDataOpus) {
base::TimeTicks::Now());
}
// Check that in CBR mode, all the packets are the same size, to confirm it
// actually made a CBR recording.
if (GetParam().bitrateMode == AudioTrackRecorder::BitrateMode::CONSTANT) {
if (!encodedPacketSizes.empty()) {
EXPECT_THAT(encodedPacketSizes, testing::Each(encodedPacketSizes[0]));
}
}
run_loop.Run();
testing::Mock::VerifyAndClearExpectations(this);
}
......
......@@ -23,6 +23,7 @@
#include "third_party/blink/renderer/platform/network/mime/content_type.h"
#include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
......@@ -49,6 +50,29 @@ String StateToString(MediaRecorder::State state) {
return String();
}
String BitrateModeToString(AudioTrackRecorder::BitrateMode bitrateMode) {
switch (bitrateMode) {
case AudioTrackRecorder::BitrateMode::CONSTANT:
return "constant";
case AudioTrackRecorder::BitrateMode::VARIABLE:
return "variable";
}
NOTREACHED();
return String();
}
AudioTrackRecorder::BitrateMode GetBitrateModeFromOptions(
const MediaRecorderOptions* const options) {
if (options->hasAudioBitrateMode()) {
if (!CodeUnitCompareIgnoringASCIICase(options->audioBitrateMode(),
"constant"))
return AudioTrackRecorder::BitrateMode::CONSTANT;
}
return AudioTrackRecorder::BitrateMode::VARIABLE;
}
// Allocates the requested bit rates from |bitrateOptions| into the respective
// |{audio,video}BitsPerSecond| (where a value of zero indicates Platform to use
// whatever it sees fit). If |options.bitsPerSecond()| is specified, it
......@@ -190,7 +214,7 @@ MediaRecorder::MediaRecorder(ExecutionContext* context,
if (!recorder_handler_->Initialize(
this, stream->Descriptor(), content_type.GetType(),
content_type.Parameter("codecs"), audio_bits_per_second_,
video_bits_per_second_)) {
video_bits_per_second_, GetBitrateModeFromOptions(options))) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"Failed to initialize native MediaRecorder the type provided (" +
......@@ -206,6 +230,10 @@ String MediaRecorder::state() const {
return StateToString(state_);
}
String MediaRecorder::audioBitrateMode() const {
return BitrateModeToString(recorder_handler_->AudioBitrateMode());
}
void MediaRecorder::start(ExceptionState& exception_state) {
start(std::numeric_limits<int>::max() /* timeSlice */, exception_state);
}
......
......@@ -49,6 +49,7 @@ class MODULES_EXPORT MediaRecorder
String state() const;
uint32_t videoBitsPerSecond() const { return video_bits_per_second_; }
uint32_t audioBitsPerSecond() const { return audio_bits_per_second_; }
String audioBitrateMode() const;
DEFINE_ATTRIBUTE_EVENT_LISTENER(start, kStart)
DEFINE_ATTRIBUTE_EVENT_LISTENER(stop, kStop)
......
......@@ -23,6 +23,7 @@ enum RecordingState { "inactive", "recording", "paused" };
attribute EventHandler onerror;
[HighEntropy=Direct, MeasureAs=MediaRecorder_VideoBitsPerSecond] readonly attribute unsigned long videoBitsPerSecond;
[HighEntropy=Direct, MeasureAs=MediaRecorder_AudioBitsPerSecond] readonly attribute unsigned long audioBitsPerSecond;
readonly attribute BitrateMode audioBitrateMode;
[RaisesException, Measure] void start(optional long timeslice);
[RaisesException] void stop();
......
......@@ -201,12 +201,14 @@ bool MediaRecorderHandler::CanSupportMimeType(const String& type,
return true;
}
bool MediaRecorderHandler::Initialize(MediaRecorder* recorder,
MediaStreamDescriptor* media_stream,
const String& type,
const String& codecs,
int32_t audio_bits_per_second,
int32_t video_bits_per_second) {
bool MediaRecorderHandler::Initialize(
MediaRecorder* recorder,
MediaStreamDescriptor* media_stream,
const String& type,
const String& codecs,
int32_t audio_bits_per_second,
int32_t video_bits_per_second,
AudioTrackRecorder::BitrateMode audio_bitrate_mode) {
DCHECK(IsMainThread());
// Save histogram data so we can see how much MediaStream Recorder is used.
// The histogram counts the number of calls to the JS API.
......@@ -244,9 +246,14 @@ bool MediaRecorderHandler::Initialize(MediaRecorder* recorder,
audio_bits_per_second_ = audio_bits_per_second;
video_bits_per_second_ = video_bits_per_second;
audio_bitrate_mode_ = audio_bitrate_mode;
return true;
}
AudioTrackRecorder::BitrateMode MediaRecorderHandler::AudioBitrateMode() {
return audio_bitrate_mode_;
}
bool MediaRecorderHandler::Start(int timeslice) {
DCHECK(IsMainThread());
DCHECK(!recording_);
......@@ -345,7 +352,8 @@ bool MediaRecorderHandler::Start(int timeslice) {
WrapWeakPersistent(this)));
audio_recorders_.emplace_back(std::make_unique<AudioTrackRecorder>(
audio_codec_id_, audio_tracks_[0], std::move(on_encoded_audio_cb),
std::move(on_track_source_changed_cb), audio_bits_per_second_));
std::move(on_track_source_changed_cb), audio_bits_per_second_,
audio_bitrate_mode_));
}
recording_ = true;
......
......@@ -63,7 +63,11 @@ class MODULES_EXPORT MediaRecorderHandler final
const String& type,
const String& codecs,
int32_t audio_bits_per_second,
int32_t video_bits_per_second);
int32_t video_bits_per_second,
AudioTrackRecorder::BitrateMode audio_bitrate_mode);
AudioTrackRecorder::BitrateMode AudioBitrateMode();
bool Start(int timeslice);
void Stop();
void Pause();
......@@ -137,6 +141,9 @@ class MODULES_EXPORT MediaRecorderHandler final
// Audio Codec, OPUS is used by default.
AudioTrackRecorder::CodecId audio_codec_id_;
// Audio bitrate mode (constant, variable, etc.), VBR is used by default.
AudioTrackRecorder::BitrateMode audio_bitrate_mode_;
// |recorder_| has no notion of time, thus may configure us via
// start(timeslice) to notify it after a certain |timeslice_| has passed. We
// use a moving |slice_origin_timestamp_| to track those time chunks.
......
......@@ -267,6 +267,28 @@ TEST_P(MediaRecorderHandlerTest, CanSupportMimeType) {
mime_type_audio, example_unsupported_codecs_2));
}
// Checks that it uses the specified bitrate mode.
TEST_P(MediaRecorderHandlerTest, SupportsBitrateMode) {
AddTracks();
V8TestingScope scope;
auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
const String mime_type(GetParam().mime_type);
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
EXPECT_EQ(media_recorder_handler_->AudioBitrateMode(),
AudioTrackRecorder::BitrateMode::VARIABLE);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::CONSTANT));
EXPECT_EQ(media_recorder_handler_->AudioBitrateMode(),
AudioTrackRecorder::BitrateMode::CONSTANT);
}
// Checks that the initialization-destruction sequence works fine.
TEST_P(MediaRecorderHandlerTest, InitializeStartStop) {
AddTracks();
......@@ -275,7 +297,8 @@ TEST_P(MediaRecorderHandlerTest, InitializeStartStop) {
const String mime_type(GetParam().mime_type);
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
EXPECT_FALSE(recording());
EXPECT_FALSE(hasVideoRecorders());
EXPECT_FALSE(hasAudioRecorders());
......@@ -310,7 +333,8 @@ TEST_P(MediaRecorderHandlerTest, EncodeVideoFrames) {
const String mime_type(GetParam().mime_type);
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
EXPECT_TRUE(media_recorder_handler_->Start(0));
InSequence s;
......@@ -400,7 +424,8 @@ TEST_P(MediaRecorderHandlerTest, OpusEncodeAudioFrames) {
const String mime_type(GetParam().mime_type);
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
EXPECT_TRUE(media_recorder_handler_->Start(0));
InSequence s;
......@@ -466,7 +491,8 @@ TEST_P(MediaRecorderHandlerTest, WebmMuxerErrorWhileEncoding) {
const String mime_type(GetParam().mime_type);
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
EXPECT_TRUE(media_recorder_handler_->Start(0));
InSequence s;
......@@ -513,7 +539,8 @@ TEST_P(MediaRecorderHandlerTest, ActualMimeType) {
const String mime_type(GetParam().mime_type);
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
StringBuilder actual_mime_type;
actual_mime_type.Append(GetParam().mime_type);
......@@ -547,7 +574,8 @@ TEST_P(MediaRecorderHandlerTest, PauseRecorderForVideo) {
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
EXPECT_TRUE(media_recorder_handler_->Start(0));
Mock::VerifyAndClearExpectations(recorder);
......@@ -578,7 +606,8 @@ TEST_P(MediaRecorderHandlerTest, StartStopStartRecorderForVideo) {
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
EXPECT_TRUE(media_recorder_handler_->Start(0));
media_recorder_handler_->Stop();
......@@ -633,7 +662,8 @@ TEST_P(MediaRecorderHandlerH264ProfileTest, ActualMimeType) {
const String mime_type(GetParam().mime_type);
const String codecs(GetParam().codecs);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), mime_type, codecs, 0, 0));
recorder, registry_.test_stream(), mime_type, codecs, 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
String actual_mime_type =
String(GetParam().mime_type) + ";codecs=" + GetParam().codecs;
......@@ -703,8 +733,9 @@ TEST_P(MediaRecorderHandlerPassthroughTest, PassesThrough) {
V8TestingScope scope;
auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
media_recorder_handler_->Initialize(recorder, registry_.test_stream(), "", "",
0, 0);
media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), "", "", 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE);
media_recorder_handler_->Start(0);
const size_t kFrameSize = 42;
......@@ -736,7 +767,8 @@ TEST_F(MediaRecorderHandlerPassthroughTest, ErrorsOutOnCodecSwitch) {
V8TestingScope scope;
auto* recorder = MakeGarbageCollected<MockMediaRecorder>(scope);
EXPECT_TRUE(media_recorder_handler_->Initialize(
recorder, registry_.test_stream(), "", "", 0, 0));
recorder, registry_.test_stream(), "", "", 0, 0,
AudioTrackRecorder::BitrateMode::VARIABLE));
EXPECT_TRUE(media_recorder_handler_->Start(0));
// NOTE, Asan: the prototype of WriteData which has a const char* as data
......
......@@ -4,6 +4,8 @@
// https://w3c.github.io/mediacapture-record/#mediarecorderoptions-section
enum BitrateMode { "constant", "variable" };
dictionary MediaRecorderOptions {
DOMString mimeType = ""; // Encoding mimeType.
......@@ -11,4 +13,5 @@ dictionary MediaRecorderOptions {
unsigned long audioBitsPerSecond;
unsigned long videoBitsPerSecond;
unsigned long bitsPerSecond;
BitrateMode audioBitrateMode = "variable";
};
This is a testharness.js-based test.
Found 70 tests; 58 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 70 tests; 60 PASS, 10 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS idl_test setup
PASS idl_test validation
PASS MediaRecorder interface: existence and properties of interface object
......@@ -19,7 +19,7 @@ PASS MediaRecorder interface: attribute onresume
PASS MediaRecorder interface: attribute onerror
PASS MediaRecorder interface: attribute videoBitsPerSecond
PASS MediaRecorder interface: attribute audioBitsPerSecond
FAIL MediaRecorder interface: attribute audioBitrateMode assert_true: The prototype object must have a property "audioBitrateMode" expected true got false
PASS MediaRecorder interface: attribute audioBitrateMode
PASS MediaRecorder interface: operation start(optional unsigned long)
PASS MediaRecorder interface: operation stop()
PASS MediaRecorder interface: operation pause()
......@@ -39,7 +39,7 @@ PASS MediaRecorder interface: [object MediaRecorder] must inherit property "onre
PASS MediaRecorder interface: [object MediaRecorder] must inherit property "onerror" with the proper type
PASS MediaRecorder interface: [object MediaRecorder] must inherit property "videoBitsPerSecond" with the proper type
PASS MediaRecorder interface: [object MediaRecorder] must inherit property "audioBitsPerSecond" with the proper type
FAIL MediaRecorder interface: [object MediaRecorder] must inherit property "audioBitrateMode" with the proper type assert_inherits: property "audioBitrateMode" not found in prototype chain
PASS MediaRecorder interface: [object MediaRecorder] must inherit property "audioBitrateMode" with the proper type
PASS MediaRecorder interface: [object MediaRecorder] must inherit property "start(optional unsigned long)" with the proper type
PASS MediaRecorder interface: calling start(optional unsigned long) on [object MediaRecorder] with too few arguments must throw TypeError
PASS MediaRecorder interface: [object MediaRecorder] must inherit property "stop()" with the proper type
......
......@@ -4306,6 +4306,7 @@ interface MediaQueryListEvent : Event
interface MediaRecorder : EventTarget
static method isTypeSupported
attribute @@toStringTag
getter audioBitrateMode
getter audioBitsPerSecond
getter mimeType
getter ondataavailable
......
......@@ -5101,6 +5101,7 @@ interface MediaQueryListEvent : Event
interface MediaRecorder : EventTarget
static method isTypeSupported
attribute @@toStringTag
getter audioBitrateMode
getter audioBitsPerSecond
getter mimeType
getter ondataavailable
......
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