Commit 033eec0f authored by Ted Meyer's avatar Ted Meyer Committed by Commit Bot

Adds FallbackVideoDecoder for wrapping two decoders

Allows a preferred and fallback decoder to be sent cross-thread and
initialized without requiring extra thread hops back to report the
status of an intermediate failure.

Bug: 832917
Change-Id: Ib9e014a746e38588ce285153230511b33db8e47b
Reviewed-on: https://chromium-review.googlesource.com/1023115
Commit-Queue: Frank Liberato <liberato@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#555948}
parent 3e51c4be
......@@ -141,6 +141,8 @@ source_set("base") {
"encryption_scheme.h",
"fake_audio_worker.cc",
"fake_audio_worker.h",
"fallback_video_decoder.cc",
"fallback_video_decoder.h",
"feedback_signal_accumulator.h",
"hdr_metadata.cc",
"hdr_metadata.h",
......@@ -471,6 +473,7 @@ source_set("unit_tests") {
"djb2_unittest.cc",
"fake_audio_worker_unittest.cc",
"fake_demuxer_stream_unittest.cc",
"fallback_video_decoder_unittest.cc",
"feedback_signal_accumulator_unittest.cc",
"gmock_callback_support_unittest.cc",
"key_systems_unittest.cc",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <utility>
#include "base/bind.h"
#include "media/base/decoder_buffer.h"
#include "media/base/fallback_video_decoder.h"
#include "media/base/video_decoder_config.h"
namespace media {
FallbackVideoDecoder::FallbackVideoDecoder(
std::unique_ptr<VideoDecoder> preferred,
std::unique_ptr<VideoDecoder> fallback)
: preferred_decoder_(std::move(preferred)),
fallback_decoder_(std::move(fallback)),
weak_factory_(this) {}
void FallbackVideoDecoder::Initialize(
const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb,
const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb) {
// If we've already fallen back, just reinitialize the selected decoder.
if (selected_decoder_ && did_fallback_) {
selected_decoder_->Initialize(config, low_delay, cdm_context, init_cb,
output_cb, waiting_for_decryption_key_cb);
return;
}
InitCB fallback_initialize_cb = base::BindRepeating(
&FallbackVideoDecoder::FallbackInitialize, weak_factory_.GetWeakPtr(),
config, low_delay, cdm_context, init_cb, output_cb,
waiting_for_decryption_key_cb);
preferred_decoder_->Initialize(config, low_delay, cdm_context,
std::move(fallback_initialize_cb), output_cb,
waiting_for_decryption_key_cb);
}
void FallbackVideoDecoder::FallbackInitialize(
const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb,
const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb,
bool success) {
// The preferred decoder was successfully initialized.
if (success) {
selected_decoder_ = preferred_decoder_.get();
init_cb.Run(true);
return;
}
did_fallback_ = true;
preferred_decoder_.reset();
selected_decoder_ = fallback_decoder_.get();
fallback_decoder_->Initialize(config, low_delay, cdm_context, init_cb,
output_cb, waiting_for_decryption_key_cb);
}
void FallbackVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
const DecodeCB& decode_cb) {
DCHECK(selected_decoder_);
selected_decoder_->Decode(std::move(buffer), decode_cb);
}
void FallbackVideoDecoder::Reset(const base::RepeatingClosure& reset_cb) {
DCHECK(selected_decoder_);
selected_decoder_->Reset(reset_cb);
}
bool FallbackVideoDecoder::NeedsBitstreamConversion() const {
DCHECK(selected_decoder_);
return selected_decoder_->NeedsBitstreamConversion();
}
bool FallbackVideoDecoder::CanReadWithoutStalling() const {
DCHECK(selected_decoder_);
return selected_decoder_->CanReadWithoutStalling();
}
int FallbackVideoDecoder::GetMaxDecodeRequests() const {
DCHECK(selected_decoder_);
return selected_decoder_->GetMaxDecodeRequests();
}
std::string FallbackVideoDecoder::GetDisplayName() const {
// MojoVideoDecoder always identifies itself as such, and never asks for the
// name of the underlying decoder.
NOTREACHED();
return "FallbackVideoDecoder";
}
FallbackVideoDecoder::~FallbackVideoDecoder() = default;
} // namespace media
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_BASE_FALLBACK_VIDEO_DECODER_H_
#define MEDIA_BASE_FALLBACK_VIDEO_DECODER_H_
#include <memory>
#include <string>
#include "base/memory/weak_ptr.h"
#include "media/base/video_decoder.h"
namespace media {
// A Wrapper VideoDecoder which supports a fallback and a preferred decoder.
class MEDIA_EXPORT FallbackVideoDecoder : public VideoDecoder {
public:
FallbackVideoDecoder(std::unique_ptr<VideoDecoder> preferred,
std::unique_ptr<VideoDecoder> fallback);
// media::VideoDecoder implementation.
std::string GetDisplayName() const override;
void Initialize(
const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb,
const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb) override;
void Decode(scoped_refptr<DecoderBuffer> buffer,
const DecodeCB& decode_cb) override;
void Reset(const base::RepeatingClosure& reset_cb) override;
bool NeedsBitstreamConversion() const override;
bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override;
protected:
~FallbackVideoDecoder() override;
private:
void FallbackInitialize(
const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb,
const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb,
bool success);
std::unique_ptr<media::VideoDecoder> preferred_decoder_;
std::unique_ptr<media::VideoDecoder> fallback_decoder_;
media::VideoDecoder* selected_decoder_ = nullptr;
bool did_fallback_ = false;
base::WeakPtrFactory<FallbackVideoDecoder> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FallbackVideoDecoder);
};
} // namespace media
#endif // MEDIA_BASE_FALLBACK_VIDEO_DECODER_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <tuple>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/run_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/fallback_video_decoder.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/mock_filters.h"
#include "media/base/test_helpers.h"
#include "media/base/video_decoder.h"
#include "media/base/video_decoder_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest-param-test.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::StrictMock;
using ::testing::_;
namespace media {
class FallbackVideoDecoderUnittest : public ::testing::TestWithParam<bool> {
public:
FallbackVideoDecoderUnittest()
: backup_decoder_(nullptr),
preferred_decoder_(nullptr),
fallback_decoder_(nullptr) {}
~FallbackVideoDecoderUnittest() override { Destroy(); }
std::unique_ptr<VideoDecoder> MakeMockDecoderWithExpectations(
bool is_fallback,
bool preferred_should_succeed) {
std::string n = is_fallback ? "Fallback" : "Preferred";
StrictMock<MockVideoDecoder>* result = new StrictMock<MockVideoDecoder>(n);
if (is_fallback && !preferred_should_succeed) {
EXPECT_CALL(*result, Initialize(_, _, _, _, _, _))
.WillOnce(RunCallback<3>(true));
}
if (!is_fallback) {
preferred_decoder_ = result;
EXPECT_CALL(*result, Initialize(_, _, _, _, _, _))
.WillOnce(RunCallback<3>(preferred_should_succeed));
} else {
backup_decoder_ = result;
}
return std::unique_ptr<VideoDecoder>(result);
}
void Initialize(bool preferred_should_succeed) {
fallback_decoder_ = new FallbackVideoDecoder(
MakeMockDecoderWithExpectations(false, preferred_should_succeed),
MakeMockDecoderWithExpectations(true, preferred_should_succeed));
fallback_decoder_->Initialize(
video_decoder_config_, false, nullptr,
base::BindRepeating([](bool success) { EXPECT_TRUE(success); }),
base::DoNothing(), base::DoNothing());
}
protected:
void Destroy() { std::default_delete<VideoDecoder>()(fallback_decoder_); }
bool PreferredShouldSucceed() { return GetParam(); }
StrictMock<MockVideoDecoder>* backup_decoder_;
StrictMock<MockVideoDecoder>* preferred_decoder_;
VideoDecoder* fallback_decoder_;
VideoDecoderConfig video_decoder_config_;
private:
DISALLOW_COPY_AND_ASSIGN(FallbackVideoDecoderUnittest);
};
INSTANTIATE_TEST_CASE_P(DoesPreferredInitFail,
FallbackVideoDecoderUnittest,
testing::ValuesIn({true, false}));
#define EXPECT_ON_CORRECT_DECODER(method) \
if (PreferredShouldSucceed()) \
EXPECT_CALL(*preferred_decoder_, method); \
else \
EXPECT_CALL(*backup_decoder_, method) // Intentionally leave off semicolon.
// Do not test the name lookup; it is NOTREACHED.
TEST_P(FallbackVideoDecoderUnittest, MethodsRedirectedAsExpected) {
Initialize(PreferredShouldSucceed());
EXPECT_ON_CORRECT_DECODER(Decode(_, _));
fallback_decoder_->Decode(nullptr, base::DoNothing());
EXPECT_ON_CORRECT_DECODER(Reset(_));
fallback_decoder_->Reset(base::DoNothing());
EXPECT_ON_CORRECT_DECODER(NeedsBitstreamConversion());
fallback_decoder_->NeedsBitstreamConversion();
EXPECT_ON_CORRECT_DECODER(CanReadWithoutStalling());
fallback_decoder_->CanReadWithoutStalling();
EXPECT_ON_CORRECT_DECODER(GetMaxDecodeRequests());
fallback_decoder_->GetMaxDecodeRequests();
}
// │ first initialization │ second initialization │
// preferred │ preferred │ backup │ preferred │ backup │
// will succeed │ init called │ init called │ init called │ init called │
//───────────────┼─────────────┼─────────────┼─────────────┼─────────────┤
// false │ ✓ │ ✓ │ x │ ✓ │
// true │ ✓ │ x │ ✓ │ ✓ │
TEST_P(FallbackVideoDecoderUnittest, ReinitializeWithPreferredFailing) {
Initialize(PreferredShouldSucceed());
// If we succeedd the first time, it should still be alive.
if (PreferredShouldSucceed()) {
EXPECT_CALL(*preferred_decoder_, Initialize(_, _, _, _, _, _))
.WillOnce(RunCallback<3>(false)); // fail initialization
}
EXPECT_CALL(*backup_decoder_, Initialize(_, _, _, _, _, _))
.WillOnce(RunCallback<3>(true));
fallback_decoder_->Initialize(
video_decoder_config_, false, nullptr,
base::BindRepeating([](bool success) { EXPECT_TRUE(success); }),
base::DoNothing(), base::DoNothing());
}
// │ first initialization │ second initialization │
// preferred │ preferred │ backup │ preferred │ backup │
// will succeed │ init called │ init called │ init called │ init called │
//───────────────┼─────────────┼─────────────┼─────────────┼─────────────┤
// false │ ✓ │ ✓ │ x │ ✓ │
// true │ ✓ │ x │ ✓ │ x │
TEST_P(FallbackVideoDecoderUnittest, ReinitializeWithPreferredSuccessful) {
Initialize(PreferredShouldSucceed());
// If we succeedd the first time, it should still be alive.
if (PreferredShouldSucceed()) {
EXPECT_CALL(*preferred_decoder_, Initialize(_, _, _, _, _, _))
.WillOnce(RunCallback<3>(true)); // pass initialization
} else {
// Otherwise, preferred was deleted, and we only backup still exists.
EXPECT_CALL(*backup_decoder_, Initialize(_, _, _, _, _, _))
.WillOnce(RunCallback<3>(true));
}
fallback_decoder_->Initialize(
video_decoder_config_, false, nullptr,
base::BindRepeating([](bool success) { EXPECT_TRUE(success); }),
base::DoNothing(), base::DoNothing());
}
} // namespace media
......@@ -207,6 +207,7 @@ class MockVideoDecoder : public VideoDecoder {
MOCK_METHOD1(Reset, void(const base::Closure&));
MOCK_CONST_METHOD0(GetMaxDecodeRequests, int());
MOCK_CONST_METHOD0(CanReadWithoutStalling, bool());
MOCK_CONST_METHOD0(NeedsBitstreamConversion, bool());
private:
std::string decoder_name_;
......
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