Commit 00b91cbc authored by Dean Liao's avatar Dean Liao Committed by Commit Bot

media_recorder: Improve VideoTrackRecorder::CanUseAcceleratedEncoder

Right now, VideoTrackRecorder::CanUseAcceleratedEncoder() only matches
if codec is supported by hardware encoder. For video width and height,
it just checks it is at least larger than minimum encoding resolution.

This change overhauls CodecEnumerator class to keep all hardware video
encoder supported profile structure instead of just profile name. So we
can also match if a video's resolution is within hardware encoder's
capability. Also, add an optional argument, framerate, for
CanUseAcceleratedEncoder().

To write unittest for CodecEnumerator, I moved its definition from
video_track_recorder.cc to header file. Also, its constructor
accepts a list of SupportedProfile. And it has a static CodecEnumerator
getter which initializes CodecEnumerator with SupportedProfiles
provided by gpu_factories.

Also, add a four-parameter constructor for SupportedProfile.

BUG=926667
TEST=Test MediaCapabilities.encodingInfo() API on CrOS devices.
Pass content_unittest:
out/Default/content_unittests --gtest_filter=CodecEnumeratorTest.* \
--gtest_filter=VideoTrackRecorderTest.*

Change-Id: Ifa6d4fc9059214a0c666643700b299d4128e633d
Reviewed-on: https://chromium-review.googlesource.com/c/1449479Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Commit-Queue: Shuo-Peng Liao <deanliao@google.com>
Cr-Commit-Position: refs/heads/master@{#634068}
parent c9078153
......@@ -385,7 +385,8 @@ void MediaRecorderHandler::EncodingInfo(
VideoTrackRecorder::CanUseAcceleratedEncoder(
VideoStringToCodecId(codec),
configuration.video_configuration->width,
configuration.video_configuration->height);
configuration.video_configuration->height,
configuration.video_configuration->framerate);
const float pixels_per_second =
configuration.video_configuration->width *
......
......@@ -4,6 +4,7 @@
#include "content/renderer/media_recorder/video_track_recorder.h"
#include <map>
#include <utility>
#include "base/bind.h"
......@@ -83,51 +84,35 @@ static_assert(base::size(kPreferredCodecIdAndVEAProfiles) ==
// encoder implementation.
const int kMaxNumberOfFramesInEncode = 10;
// Class to encapsulate the enumeration of CodecIds/VideoCodecProfiles supported
// by the VEA underlying platform. Provides methods to query the preferred
// CodecId and to check if a given CodecId is supported.
class CodecEnumerator {
public:
CodecEnumerator();
~CodecEnumerator() = default;
// Returns the first CodecId that has an associated VEA VideoCodecProfile, or
// VP8 if none available.
CodecId GetPreferredCodecId();
// Returns the VEA VideoCodedProfile for a given CodecId, if supported, or
// VIDEO_CODEC_PROFILE_UNKNOWN otherwise.
media::VideoCodecProfile CodecIdToVEAProfile(CodecId codec);
private:
// A map of VEA-supported CodecId-and-VEA-profile pairs.
std::map<CodecId, media::VideoCodecProfile> codec_id_to_profile_;
DISALLOW_COPY_AND_ASSIGN(CodecEnumerator);
};
CodecEnumerator* GetCodecEnumerator() {
static CodecEnumerator* enumerator = new CodecEnumerator();
return enumerator;
}
CodecEnumerator::CodecEnumerator() {
// Obtains video encode accelerator's supported profiles.
media::VideoEncodeAccelerator::SupportedProfiles GetVEASupportedProfiles() {
content::RenderThreadImpl* const render_thread_impl =
content::RenderThreadImpl::current();
if (!render_thread_impl) {
DVLOG(2) << "Couldn't access the render thread";
return;
return media::VideoEncodeAccelerator::SupportedProfiles();
}
media::GpuVideoAcceleratorFactories* const gpu_factories =
render_thread_impl->GetGpuFactories();
if (!gpu_factories || !gpu_factories->IsGpuVideoAcceleratorEnabled()) {
DVLOG(2) << "Couldn't initialize GpuVideoAcceleratorFactories";
return;
return media::VideoEncodeAccelerator::SupportedProfiles();
}
return gpu_factories->GetVideoEncodeAcceleratorSupportedProfiles();
}
VideoTrackRecorder::CodecEnumerator* GetCodecEnumerator() {
static VideoTrackRecorder::CodecEnumerator* enumerator =
new VideoTrackRecorder::CodecEnumerator(GetVEASupportedProfiles());
return enumerator;
}
} // anonymous namespace
const auto vea_supported_profiles =
gpu_factories->GetVideoEncodeAcceleratorSupportedProfiles();
VideoTrackRecorder::CodecEnumerator::CodecEnumerator(
const media::VideoEncodeAccelerator::SupportedProfiles&
vea_supported_profiles) {
for (const auto& supported_profile : vea_supported_profiles) {
const media::VideoCodecProfile codec = supported_profile.profile;
#if defined(OS_ANDROID)
......@@ -140,28 +125,44 @@ CodecEnumerator::CodecEnumerator() {
for (auto& codec_id_and_profile : kPreferredCodecIdAndVEAProfiles) {
if (codec >= codec_id_and_profile.min_profile &&
codec <= codec_id_and_profile.max_profile) {
DVLOG(2) << "Accelerated codec found: " << media::GetProfileName(codec);
codec_id_to_profile_.insert(
std::make_pair(codec_id_and_profile.codec_id, codec));
DVLOG(2) << "Accelerated codec found: " << media::GetProfileName(codec)
<< ", max_resolution: "
<< supported_profile.max_resolution.ToString()
<< ", max_framerate: "
<< supported_profile.max_framerate_numerator << "/"
<< supported_profile.max_framerate_denominator;
supported_profiles_[codec_id_and_profile.codec_id].push_back(
supported_profile);
}
}
}
}
CodecId CodecEnumerator::GetPreferredCodecId() {
if (codec_id_to_profile_.empty())
VideoTrackRecorder::CodecEnumerator::~CodecEnumerator() = default;
VideoTrackRecorder::CodecId
VideoTrackRecorder::CodecEnumerator::GetPreferredCodecId() const {
if (supported_profiles_.empty())
return CodecId::VP8;
return codec_id_to_profile_.begin()->first;
return supported_profiles_.begin()->first;
}
media::VideoCodecProfile CodecEnumerator::CodecIdToVEAProfile(CodecId codec) {
const auto profile = codec_id_to_profile_.find(codec);
return profile == codec_id_to_profile_.end()
media::VideoCodecProfile
VideoTrackRecorder::CodecEnumerator::GetFirstSupportedVideoCodecProfile(
CodecId codec) const {
const auto profile = supported_profiles_.find(codec);
return profile == supported_profiles_.end()
? media::VIDEO_CODEC_PROFILE_UNKNOWN
: profile->second;
: profile->second.front().profile;
}
} // anonymous namespace
media::VideoEncodeAccelerator::SupportedProfiles
VideoTrackRecorder::CodecEnumerator::GetSupportedProfiles(CodecId codec) const {
const auto profile = supported_profiles_.find(codec);
return profile == supported_profiles_.end()
? media::VideoEncodeAccelerator::SupportedProfiles()
: profile->second;
}
VideoTrackRecorder::Counter::Counter() : count_(0u), weak_factory_(this) {}
......@@ -383,11 +384,32 @@ VideoTrackRecorder::CodecId VideoTrackRecorder::GetPreferredCodecId() {
// static
bool VideoTrackRecorder::CanUseAcceleratedEncoder(CodecId codec,
size_t width,
size_t height) {
return GetCodecEnumerator()->CodecIdToVEAProfile(codec) !=
media::VIDEO_CODEC_PROFILE_UNKNOWN &&
width >= kVEAEncoderMinResolutionWidth &&
height >= kVEAEncoderMinResolutionHeight;
size_t height,
double framerate) {
const auto profiles = GetCodecEnumerator()->GetSupportedProfiles(codec);
if (profiles.empty())
return false;
// Now we only consider the first profile.
// TODO(crbug.com/931035): Handle multiple profile cases.
const media::VideoEncodeAccelerator::SupportedProfile& profile = profiles[0];
if (profile.profile == media::VIDEO_CODEC_PROFILE_UNKNOWN)
return false;
const gfx::Size& max_resolution = profile.max_resolution;
DCHECK_GE(max_resolution.width(), 0);
const size_t max_width = static_cast<size_t>(max_resolution.width());
DCHECK_GE(max_resolution.height(), 0);
const size_t max_height = static_cast<size_t>(max_resolution.height());
const bool width_within_range =
max_width >= width && width >= kVEAEncoderMinResolutionWidth;
const bool height_within_range =
max_height >= height && height >= kVEAEncoderMinResolutionHeight;
const bool valid_framerate = framerate * profile.max_framerate_denominator <=
profile.max_framerate_numerator;
return width_within_range && height_within_range && valid_framerate;
}
VideoTrackRecorder::VideoTrackRecorder(
......@@ -473,7 +495,8 @@ void VideoTrackRecorder::InitializeEncoder(
if (allow_vea_encoder && CanUseAcceleratedEncoder(codec, input_size.width(),
input_size.height())) {
UMA_HISTOGRAM_BOOLEAN("Media.MediaRecorder.VEAUsed", true);
const auto vea_profile = GetCodecEnumerator()->CodecIdToVEAProfile(codec);
const auto vea_profile =
GetCodecEnumerator()->GetFirstSupportedVideoCodecProfile(codec);
encoder_ = VEAEncoder::Create(
on_encoded_video_callback,
media::BindToCurrentLoop(base::Bind(&VideoTrackRecorder::OnError,
......
......@@ -7,6 +7,7 @@
#include <memory>
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
......@@ -15,6 +16,7 @@
#include "content/public/common/buildflags.h"
#include "content/renderer/media_stream_video_sink.h"
#include "media/muxers/webm_muxer.h"
#include "media/video/video_encode_accelerator.h"
#include "third_party/blink/public/platform/web_media_stream_track.h"
#include "third_party/skia/include/core/SkBitmap.h"
......@@ -185,10 +187,47 @@ class CONTENT_EXPORT VideoTrackRecorder : public MediaStreamVideoSink {
DISALLOW_COPY_AND_ASSIGN(Encoder);
};
// Class to encapsulate the enumeration of CodecIds/VideoCodecProfiles
// supported by the VEA underlying platform. Provides methods to query the
// preferred CodecId and to check if a given CodecId is supported.
class CONTENT_EXPORT CodecEnumerator {
public:
CodecEnumerator(const media::VideoEncodeAccelerator::SupportedProfiles&
vea_supported_profiles);
~CodecEnumerator();
// Returns the first CodecId that has an associated VEA VideoCodecProfile,
// or VP8 if none available.
CodecId GetPreferredCodecId() const;
// Returns VEA's first supported VideoCodedProfile for a given CodecId, or
// VIDEO_CODEC_PROFILE_UNKNOWN otherwise.
media::VideoCodecProfile GetFirstSupportedVideoCodecProfile(
CodecId codec) const;
// Returns a list of supported media::VEA::SupportedProfile for a given
// CodecId, or empty vector if CodecId is unsupported.
media::VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles(
CodecId codec) const;
private:
// VEA-supported profiles grouped by CodecId.
base::flat_map<CodecId, media::VideoEncodeAccelerator::SupportedProfiles>
supported_profiles_;
DISALLOW_COPY_AND_ASSIGN(CodecEnumerator);
};
static CodecId GetPreferredCodecId();
// Returns true if the device has a hardware accelerated encoder which can
// encode video of the given |width|x|height| and |framerate| to specific
// |codec|.
// Note: default framerate value means no restriction.
static bool CanUseAcceleratedEncoder(CodecId codec,
size_t width,
size_t height);
size_t height,
double framerate = 0.0);
VideoTrackRecorder(
CodecId codec,
......
......@@ -21,6 +21,7 @@
#include "content/child/child_process.h"
#include "content/renderer/media/stream/media_stream_video_track.h"
#include "content/renderer/media/stream/mock_media_stream_video_source.h"
#include "media/base/video_codecs.h"
#include "media/base/video_frame.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -375,4 +376,90 @@ INSTANTIATE_TEST_SUITE_P(,
ValuesIn(kTrackRecorderTestSize),
::testing::Bool()));
class CodecEnumeratorTest : public ::testing::Test {
public:
using CodecEnumerator = VideoTrackRecorder::CodecEnumerator;
using CodecId = VideoTrackRecorder::CodecId;
CodecEnumeratorTest() = default;
~CodecEnumeratorTest() override = default;
media::VideoEncodeAccelerator::SupportedProfiles MakeVp8Profiles() {
media::VideoEncodeAccelerator::SupportedProfiles profiles;
profiles.emplace_back(media::VP8PROFILE_ANY, gfx::Size(1920, 1080), 30, 1);
return profiles;
}
media::VideoEncodeAccelerator::SupportedProfiles MakeVp9Profiles() {
media::VideoEncodeAccelerator::SupportedProfiles profiles;
profiles.emplace_back(media::VP9PROFILE_PROFILE1, gfx::Size(1920, 1080), 60,
1);
profiles.emplace_back(media::VP9PROFILE_PROFILE2, gfx::Size(1920, 1080), 30,
1);
return profiles;
}
media::VideoEncodeAccelerator::SupportedProfiles MakeVp8Vp9Profiles() {
media::VideoEncodeAccelerator::SupportedProfiles profiles =
MakeVp8Profiles();
media::VideoEncodeAccelerator::SupportedProfiles vp9_profiles =
MakeVp9Profiles();
profiles.insert(profiles.end(), vp9_profiles.begin(), vp9_profiles.end());
return profiles;
}
private:
DISALLOW_COPY_AND_ASSIGN(CodecEnumeratorTest);
};
TEST_F(CodecEnumeratorTest, GetPreferredCodecIdDefault) {
// Empty supported profiles.
const CodecEnumerator emulator(
(media::VideoEncodeAccelerator::SupportedProfiles()));
EXPECT_EQ(CodecId::VP8, emulator.GetPreferredCodecId());
}
TEST_F(CodecEnumeratorTest, GetPreferredCodecIdVp8) {
const CodecEnumerator emulator(MakeVp8Profiles());
EXPECT_EQ(CodecId::VP8, emulator.GetPreferredCodecId());
}
TEST_F(CodecEnumeratorTest, GetPreferredCodecIdVp9) {
const CodecEnumerator emulator(MakeVp9Profiles());
EXPECT_EQ(CodecId::VP9, emulator.GetPreferredCodecId());
}
TEST_F(CodecEnumeratorTest, GetPreferredCodecIdVp8Vp9) {
const CodecEnumerator emulator(MakeVp8Vp9Profiles());
EXPECT_EQ(CodecId::VP8, emulator.GetPreferredCodecId());
}
TEST_F(CodecEnumeratorTest, MakeSupportedProfilesVp9) {
const CodecEnumerator emulator(MakeVp9Profiles());
media::VideoEncodeAccelerator::SupportedProfiles profiles =
emulator.GetSupportedProfiles(CodecId::VP9);
EXPECT_EQ(2u, profiles.size());
EXPECT_EQ(media::VP9PROFILE_PROFILE1, profiles[0].profile);
EXPECT_EQ(media::VP9PROFILE_PROFILE2, profiles[1].profile);
}
TEST_F(CodecEnumeratorTest, MakeSupportedProfilesNoVp8) {
const CodecEnumerator emulator(MakeVp9Profiles());
media::VideoEncodeAccelerator::SupportedProfiles profiles =
emulator.GetSupportedProfiles(CodecId::VP8);
EXPECT_TRUE(profiles.empty());
}
TEST_F(CodecEnumeratorTest, GetFirstSupportedVideoCodecProfileVp9) {
const CodecEnumerator emulator(MakeVp9Profiles());
EXPECT_EQ(media::VP9PROFILE_PROFILE1,
emulator.GetFirstSupportedVideoCodecProfile(CodecId::VP9));
}
TEST_F(CodecEnumeratorTest, GetFirstSupportedVideoCodecProfileNoVp8) {
const CodecEnumerator emulator(MakeVp9Profiles());
EXPECT_EQ(media::VIDEO_CODEC_PROFILE_UNKNOWN,
emulator.GetFirstSupportedVideoCodecProfile(CodecId::VP8));
}
} // namespace content
......@@ -84,6 +84,16 @@ VideoEncodeAccelerator::SupportedProfile::SupportedProfile()
max_framerate_denominator(0) {
}
VideoEncodeAccelerator::SupportedProfile::SupportedProfile(
VideoCodecProfile profile,
const gfx::Size& max_resolution,
uint32_t max_framerate_numerator,
uint32_t max_framerate_denominator)
: profile(profile),
max_resolution(max_resolution),
max_framerate_numerator(max_framerate_numerator),
max_framerate_denominator(max_framerate_denominator) {}
VideoEncodeAccelerator::SupportedProfile::~SupportedProfile() = default;
void VideoEncodeAccelerator::Flush(FlushCallback flush_callback) {
......
......@@ -71,6 +71,10 @@ class MEDIA_EXPORT VideoEncodeAccelerator {
// Specification of an encoding profile supported by an encoder.
struct MEDIA_EXPORT SupportedProfile {
SupportedProfile();
SupportedProfile(VideoCodecProfile profile,
const gfx::Size& max_resolution,
uint32_t max_framerate_numerator = 0u,
uint32_t max_framerate_denominator = 1u);
~SupportedProfile();
VideoCodecProfile profile;
gfx::Size max_resolution;
......
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