Commit b7dab9dc authored by Armando Miraglia's avatar Armando Miraglia Committed by Commit Bot

[MediaRecorder] Fix corrupted video problem with pause/resume spamming.


This CL fixes an issue caused by the following sequence of events:
1. MediaRecorderHandler::Pause(): recording_ = false
2. MediaRecorderHandler::WriteData(): if (!recording_) return

The problem is that the WriteData() invocation was decided to
happen while the recorder was still resumed. In that state, wholes
would be produced which are not tolerated by vpx_encoder, which
interprets those as corrupted.

The fix reverts the semantic of the code to when the class was not under
oilpan and would require manual memory management and restores the
"invalidation" semantic from when the class was using a weak ptr.
In fact, the weak reference would be invalidated hence making the
callbacks no-ops. To keep the same semantic, this CL introduces a
boolean and avoids overloading the meaning of 'recording_'

This should also not require calling Pause() in Stop(), as all callbacks
will not do anything after Stop() is called.

Fixed: 1060358
Change-Id: I88b0aeb10fd13034412f3689648cebd1df40d6bf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2106072
Commit-Queue: Armando Miraglia <armax@chromium.org>
Reviewed-by: default avatarMarkus Handell <handellm@google.com>
Cr-Commit-Position: refs/heads/master@{#752838}
parent 04e5dae2
...@@ -335,8 +335,7 @@ void MediaRecorderHandler::Stop() { ...@@ -335,8 +335,7 @@ void MediaRecorderHandler::Stop() {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
// Don't check |recording_| since we can go directly from pause() to stop(). // Don't check |recording_| since we can go directly from pause() to stop().
if (recording_) invalidated_ = true;
Pause();
recording_ = false; recording_ = false;
timeslice_ = base::TimeDelta::FromMilliseconds(0); timeslice_ = base::TimeDelta::FromMilliseconds(0);
...@@ -496,6 +495,10 @@ void MediaRecorderHandler::OnEncodedVideo( ...@@ -496,6 +495,10 @@ void MediaRecorderHandler::OnEncodedVideo(
base::TimeTicks timestamp, base::TimeTicks timestamp,
bool is_key_frame) { bool is_key_frame) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
if (invalidated_)
return;
auto params_with_codec = params; auto params_with_codec = params;
params_with_codec.codec = MediaVideoCodecFromCodecId(video_codec_id_); params_with_codec.codec = MediaVideoCodecFromCodecId(video_codec_id_);
HandleEncodedVideo(params_with_codec, std::move(encoded_data), HandleEncodedVideo(params_with_codec, std::move(encoded_data),
...@@ -524,9 +527,6 @@ void MediaRecorderHandler::HandleEncodedVideo( ...@@ -524,9 +527,6 @@ void MediaRecorderHandler::HandleEncodedVideo(
bool is_key_frame) { bool is_key_frame) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
if (video_recorders_.IsEmpty())
return;
if (UpdateTracksAndCheckIfChanged()) { if (UpdateTracksAndCheckIfChanged()) {
recorder_->OnError("Amount of tracks in MediaStream has changed."); recorder_->OnError("Amount of tracks in MediaStream has changed.");
return; return;
...@@ -557,7 +557,7 @@ void MediaRecorderHandler::OnEncodedAudio(const media::AudioParameters& params, ...@@ -557,7 +557,7 @@ void MediaRecorderHandler::OnEncodedAudio(const media::AudioParameters& params,
base::TimeTicks timestamp) { base::TimeTicks timestamp) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
if (audio_recorders_.IsEmpty()) if (invalidated_)
return; return;
if (UpdateTracksAndCheckIfChanged()) { if (UpdateTracksAndCheckIfChanged()) {
...@@ -576,7 +576,7 @@ void MediaRecorderHandler::OnEncodedAudio(const media::AudioParameters& params, ...@@ -576,7 +576,7 @@ void MediaRecorderHandler::OnEncodedAudio(const media::AudioParameters& params,
void MediaRecorderHandler::WriteData(base::StringPiece data) { void MediaRecorderHandler::WriteData(base::StringPiece data) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
if (!recording_) if (invalidated_)
return; return;
const base::TimeTicks now = base::TimeTicks::Now(); const base::TimeTicks now = base::TimeTicks::Now();
......
...@@ -144,6 +144,7 @@ class MODULES_EXPORT MediaRecorderHandler final ...@@ -144,6 +144,7 @@ class MODULES_EXPORT MediaRecorderHandler final
// The last seen video codec of the last received encoded video frame. // The last seen video codec of the last received encoded video frame.
base::Optional<media::VideoCodec> last_seen_codec_; base::Optional<media::VideoCodec> last_seen_codec_;
bool invalidated_ = false;
bool recording_; bool recording_;
// The MediaStream being recorded. // The MediaStream being recorded.
Member<MediaStreamDescriptor> media_stream_; Member<MediaStreamDescriptor> media_stream_;
......
...@@ -137,6 +137,16 @@ class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams> { ...@@ -137,6 +137,16 @@ class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams> {
media_recorder_handler_->OnVideoFrameForTesting(std::move(frame), media_recorder_handler_->OnVideoFrameForTesting(std::move(frame),
base::TimeTicks::Now()); base::TimeTicks::Now());
} }
void OnEncodedVideoForTesting(const media::WebmMuxer::VideoParameters& params,
std::string encoded_data,
std::string encoded_alpha,
base::TimeTicks timestamp,
bool is_key_frame) {
media_recorder_handler_->OnEncodedVideo(params, encoded_data, encoded_alpha,
timestamp, is_key_frame);
}
void OnAudioBusForTesting(const media::AudioBus& audio_bus) { void OnAudioBusForTesting(const media::AudioBus& audio_bus) {
media_recorder_handler_->OnAudioBusForTesting(audio_bus, media_recorder_handler_->OnAudioBusForTesting(audio_bus,
base::TimeTicks::Now()); base::TimeTicks::Now());
...@@ -505,6 +515,37 @@ TEST_P(MediaRecorderHandlerTest, ActualMimeType) { ...@@ -505,6 +515,37 @@ TEST_P(MediaRecorderHandlerTest, ActualMimeType) {
media_recorder_handler_ = nullptr; media_recorder_handler_ = nullptr;
} }
TEST_P(MediaRecorderHandlerTest, PauseRecorderForVideo) {
// Video-only test: Audio would be very similar.
if (GetParam().has_audio)
return;
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));
EXPECT_TRUE(media_recorder_handler_->Start(0));
Mock::VerifyAndClearExpectations(recorder);
media_recorder_handler_->Pause();
EXPECT_CALL(*recorder, WriteData).Times(AtLeast(1));
media::WebmMuxer::VideoParameters params(gfx::Size(), 1, media::kCodecVP9,
gfx::ColorSpace());
OnEncodedVideoForTesting(params, "vp9 frame", "", base::TimeTicks::Now(),
true);
// Expect a last call on destruction.
EXPECT_CALL(*recorder, WriteData(_, _, true, _)).Times(1);
media_recorder_handler_ = nullptr;
}
struct MediaRecorderPassthroughTestParams { struct MediaRecorderPassthroughTestParams {
const char* mime_type; const char* mime_type;
media::VideoCodec codec; media::VideoCodec codec;
...@@ -621,8 +662,8 @@ TEST_F(MediaRecorderHandlerPassthroughTest, ErrorsOutOnCodecSwitch) { ...@@ -621,8 +662,8 @@ TEST_F(MediaRecorderHandlerPassthroughTest, ErrorsOutOnCodecSwitch) {
// transfer doesn't crash the media recorder. // transfer doesn't crash the media recorder.
OnVideoFrameForTesting(FakeEncodedVideoFrame::Builder() OnVideoFrameForTesting(FakeEncodedVideoFrame::Builder()
.WithKeyFrame(true) .WithKeyFrame(true)
.WithCodec(media::kCodecVP9) .WithCodec(media::kCodecVP8)
.WithData(std::string("vp9 frame")) .WithData(std::string("vp8 frame"))
.BuildRefPtr()); .BuildRefPtr());
platform_->RunUntilIdle(); platform_->RunUntilIdle();
} }
......
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