0

[media] Add aspect ratio to VideoDecoderConfig

This CL introduces VideoAspectRatio, which contains either a display
aspect ratio or a pixel aspect ratio. VideoDecoderConfig gains a new
aspect_ratio() method replacing GetPixelAspectRatio(), and all callers
of GetNaturalSize() are converted to use it.

Bug: 1213667
Change-Id: I0d1226e7dfedf62e45ef8e11aa1485bdad76d59d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2923231
Commit-Queue: Dan Sanders <sandersd@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Chrome Cunningham <chcunningham@chromium.org>
Cr-Commit-Position: refs/heads/master@{#889466}
This commit is contained in:
Dan Sanders
2021-06-04 21:43:39 +00:00
committed by Chromium LUCI CQ
parent edc3e86f1d
commit 068c5a4fbf
28 changed files with 311 additions and 303 deletions

@ -313,6 +313,8 @@ source_set("base") {
"use_after_free_checker.h",
"user_input_monitor.cc",
"user_input_monitor.h",
"video_aspect_ratio.cc",
"video_aspect_ratio.h",
"video_bitrate_allocation.cc",
"video_bitrate_allocation.h",
"video_codecs.cc",
@ -612,6 +614,7 @@ source_set("unit_tests") {
"unaligned_shared_memory_unittest.cc",
"user_input_monitor_unittest.cc",
"vector_math_unittest.cc",
"video_aspect_ratio_unittest.cc",
"video_bitrate_allocation_unittest.cc",
"video_codecs_unittest.cc",
"video_color_space_unittest.cc",

@ -0,0 +1,81 @@
// Copyright 2021 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 "media/base/video_aspect_ratio.h"
#include <cmath>
#include <limits>
namespace media {
VideoAspectRatio::VideoAspectRatio(Type type, int width, int height) {
type_ = type;
aspect_ratio_ = height ? double{width} / height : 0.0;
}
// static
VideoAspectRatio VideoAspectRatio::PAR(int width, int height) {
return VideoAspectRatio(Type::kPixel, width, height);
}
// static
VideoAspectRatio VideoAspectRatio::DAR(int width, int height) {
return VideoAspectRatio(Type::kDisplay, width, height);
}
VideoAspectRatio::VideoAspectRatio(const gfx::Rect& visible_rect,
const gfx::Size& natural_size) {
// The size of a pixel is:
// (natural_width / visible_width) by (natural_height / visible_height).
// Both are multiplied by (visible_width * visible_height) to avoid division.
double w = double{visible_rect.height()} * natural_size.width();
double h = double{visible_rect.width()} * natural_size.height();
type_ = Type::kPixel;
aspect_ratio_ = h != 0.0 ? w / h : 0.0;
}
bool VideoAspectRatio::IsValid() const {
return std::isfinite(aspect_ratio_) && aspect_ratio_ > 0.0;
}
gfx::Size VideoAspectRatio::GetNaturalSize(
const gfx::Rect& visible_rect) const {
if (!IsValid() || visible_rect.IsEmpty())
return visible_rect.size();
double w = visible_rect.width();
double h = visible_rect.height();
switch (type_) {
case Type::kDisplay:
if (aspect_ratio_ >= w / h) {
// Display aspect is wider, grow width.
w = h * aspect_ratio_;
} else {
// Display aspect is narrower, grow height.
h = w / aspect_ratio_;
}
break;
case Type::kPixel:
if (aspect_ratio_ >= 1.0) {
// Wide pixels, grow width.
w = w * aspect_ratio_;
} else {
// Narrow pixels, grow height.
h = h / aspect_ratio_;
}
break;
}
w = std::round(w);
h = std::round(h);
if (w < 1.0 || w > std::numeric_limits<int>::max() || h < 1.0 ||
h > std::numeric_limits<int>::max()) {
return visible_rect.size();
}
return gfx::Size(w, h);
}
} // namespace media

@ -0,0 +1,61 @@
// Copyright 2021 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_VIDEO_ASPECT_RATIO_H_
#define MEDIA_BASE_VIDEO_ASPECT_RATIO_H_
#include <stdint.h>
#include "media/base/media_export.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace media {
class MEDIA_EXPORT VideoAspectRatio {
public:
// Create a pixel aspect ratio (PAR). |width| and |height| describe the
// shape of a pixel. For example, an anamorphic video has regtangular pixels
// with a 2:1 PAR, that is they are twice as wide as they are tall.
//
// Note that this is also called a sample aspect ratio (SAR), but SAR can also
// mean storage aspect ratio (which is the coded size).
static VideoAspectRatio PAR(int width, int height);
// Create a display aspect ratio (DAR). |width| and |height| describe the
// shape of the rendered picture. For example a 1920x1080 video with square
// pixels has a 1920:1080 = 16:9 DAR.
static VideoAspectRatio DAR(int width, int height);
// A default-constructed VideoAspectRatio is !IsValid().
VideoAspectRatio() = default;
// Create a VideoAspectRatio from a known |natural_size|.
// TODO(crbug.com/1214061): Remove.
VideoAspectRatio(const gfx::Rect& visible_rect,
const gfx::Size& natural_size);
// An aspect ratio is invalid if it was default constructed, had nonpositive
// components, or exceeds implementation limits.
bool IsValid() const;
// Computes the expected display size for a given visible size.
// Returns visible_rect.size() if !IsValid().
gfx::Size GetNaturalSize(const gfx::Rect& visible_rect) const;
private:
enum class Type {
kDisplay,
kPixel,
};
VideoAspectRatio(Type type, int width, int height);
Type type_ = Type::kDisplay;
double aspect_ratio_ = 0.0;
};
} // namespace media
#endif // MEDIA_BASE_VIDEO_ASPECT_RATIO_H_

@ -0,0 +1,73 @@
// Copyright 2021 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 "media/base/video_aspect_ratio.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace media {
namespace {
constexpr gfx::Rect kRect_4_3(0, 0, 4, 3);
constexpr gfx::Rect kRect_16_9(0, 0, 16, 9);
} // namespace
TEST(VideoAspectRatioTest, DefaultConstruction) {
VideoAspectRatio aspect_ratio;
EXPECT_FALSE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(16, 9));
}
TEST(VideoAspectRatioTest, FromNaturalSize) {
VideoAspectRatio aspect_ratio;
aspect_ratio = VideoAspectRatio(kRect_16_9, gfx::Size(16, 9));
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(16, 9));
aspect_ratio = VideoAspectRatio(kRect_4_3, gfx::Size(16, 9));
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_4_3), gfx::Size(5, 3));
aspect_ratio = VideoAspectRatio(kRect_16_9, gfx::Size(4, 3));
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(16, 12));
}
TEST(VideoAspectRatioTest, Pixel) {
VideoAspectRatio aspect_ratio;
aspect_ratio = VideoAspectRatio::PAR(1, 1);
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(16, 9));
aspect_ratio = VideoAspectRatio::PAR(1, 2);
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(16, 18));
aspect_ratio = VideoAspectRatio::PAR(2, 1);
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(32, 9));
}
TEST(VideoAspectRatioTest, Display) {
VideoAspectRatio aspect_ratio;
aspect_ratio = VideoAspectRatio::DAR(1, 1);
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(16, 16));
aspect_ratio = VideoAspectRatio::DAR(1, 2);
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(16, 32));
aspect_ratio = VideoAspectRatio::DAR(2, 1);
EXPECT_TRUE(aspect_ratio.IsValid());
EXPECT_EQ(aspect_ratio.GetNaturalSize(kRect_16_9), gfx::Size(18, 9));
}
} // namespace media

@ -62,6 +62,7 @@ void VideoDecoderConfig::Initialize(VideoCodec codec,
coded_size_ = coded_size;
visible_rect_ = visible_rect;
natural_size_ = natural_size;
aspect_ratio_ = VideoAspectRatio(visible_rect, natural_size);
extra_data_ = extra_data;
encryption_scheme_ = encryption_scheme;
color_space_info_ = color_space;
@ -131,10 +132,6 @@ std::string VideoDecoderConfig::GetHumanReadableCodecName() const {
return GetCodecName(codec());
}
double VideoDecoderConfig::GetPixelAspectRatio() const {
return ::media::GetPixelAspectRatio(visible_rect_, natural_size_);
}
void VideoDecoderConfig::SetExtraData(const std::vector<uint8_t>& extra_data) {
extra_data_ = extra_data;
}

@ -13,6 +13,7 @@
#include "base/macros.h"
#include "media/base/encryption_scheme.h"
#include "media/base/media_export.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_codecs.h"
#include "media/base/video_color_space.h"
#include "media/base/video_transformation.h"
@ -99,33 +100,20 @@ class MEDIA_EXPORT VideoDecoderConfig {
// aperture. Usually, but not always, origin-aligned (top-left).
const gfx::Rect& visible_rect() const { return visible_rect_; }
// DEPRECATED: Use aspect_ratio().GetNaturalSize().
// TODO(crbug.com/1214061): Remove.
// Final visible width and height of a video frame with aspect ratio taken
// into account. Image data in the visible_rect() should be scaled to this
// size for display.
const gfx::Size& natural_size() const { return natural_size_; }
// The shape of encoded pixels. Given visible_rect() and a pixel aspect ratio,
// it is possible to compute natural_size() (see video_util.h).
//
// SUBTLE: "pixel aspect ratio" != "display aspect ratio". *Pixel* aspect
// ratio describes the shape of a *pixel* as the ratio of its width to its
// height (ex: anamorphic video may have rectangular pixels). *Display* aspect
// ratio is natural_width / natural_height.
//
// CONTRACT: Dynamic changes to *pixel* aspect ratio are not supported unless
// done with explicit signal (new init-segment in MSE). Streams may still
// change their frame sizes dynamically, including their *display* aspect
// ratio. But, at this time (2019) changes to pixel aspect ratio are not
// surfaced by all platform decoders (ex: MediaCodec), so non-support is
// chosen for cross platform consistency. Hence, natural size should always be
// computed by scaling visbilte_size by the *pixel* aspect ratio from the
// container metadata. See GetNaturalSize() in video_util.h.
//
// TODO(crbug.com/837337): This should be explicitly set (replacing
// |natural_size|). Alternatively, this could be replaced by
// GetNaturalSize(visible_rect), with pixel aspect ratio being an internal
// detail of the config.
double GetPixelAspectRatio() const;
// The aspect ratio parsed from the container. Decoders may override using
// in-band metadata only if !aspect_ratio().IsValid().
const VideoAspectRatio& aspect_ratio() const { return aspect_ratio_; }
void set_aspect_ratio(const VideoAspectRatio& aspect_ratio) {
aspect_ratio_ = aspect_ratio;
}
// Optional video decoder initialization data, such as H.264 AVCC.
//
@ -185,10 +173,11 @@ class MEDIA_EXPORT VideoDecoderConfig {
// Deprecated. TODO(wolenetz): Remove. See https://crbug.com/665539.
gfx::Size coded_size_;
gfx::Rect visible_rect_;
gfx::Size natural_size_;
VideoAspectRatio aspect_ratio_;
std::vector<uint8_t> extra_data_;
EncryptionScheme encryption_scheme_ = EncryptionScheme::kUnencrypted;

@ -5,7 +5,6 @@
#include "media/base/limits.h"
#include "media/base/media_util.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
@ -30,67 +29,6 @@ TEST(VideoDecoderConfigTest, AlphaModeSetCorrectly) {
EXPECT_EQ(config.alpha_mode(), VideoDecoderConfig::AlphaMode::kHasAlpha);
}
TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorZero) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1);
VideoDecoderConfig config(
kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
natural_size, EmptyExtraData(), EncryptionScheme::kUnencrypted);
EXPECT_FALSE(config.IsValidConfig());
}
TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorZero) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0);
VideoDecoderConfig config(
kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
natural_size, EmptyExtraData(), EncryptionScheme::kUnencrypted);
EXPECT_FALSE(config.IsValidConfig());
}
TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorNegative) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1);
VideoDecoderConfig config(
kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
natural_size, EmptyExtraData(), EncryptionScheme::kUnencrypted);
EXPECT_FALSE(config.IsValidConfig());
}
TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorNegative) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1);
VideoDecoderConfig config(
kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
natural_size, EmptyExtraData(), EncryptionScheme::kUnencrypted);
EXPECT_FALSE(config.IsValidConfig());
}
TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorTooLarge) {
int width = kVisibleRect.size().width();
int num = ceil(static_cast<double>(limits::kMaxDimension + 1) / width);
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1);
VideoDecoderConfig config(
kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
natural_size, EmptyExtraData(), EncryptionScheme::kUnencrypted);
EXPECT_FALSE(config.IsValidConfig());
}
TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorVeryLarge) {
// This test makes sure that highly skewed pixel ratios arent counted as valid
// configurations.
int den = 2 * kVisibleRect.size().width() + 1;
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, den);
EXPECT_EQ(320, natural_size.width());
EXPECT_EQ(240 * 641, natural_size.height());
VideoDecoderConfig config(
kCodecVP8, VP8PROFILE_ANY, VideoDecoderConfig::AlphaMode::kIsOpaque,
VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect,
natural_size, EmptyExtraData(), EncryptionScheme::kUnencrypted);
EXPECT_FALSE(config.IsValidConfig());
}
TEST(VideoDecoderConfigTest, SetProfile) {
VideoDecoderConfig config(
kCodecVP9, VP9PROFILE_PROFILE0, VideoDecoderConfig::AlphaMode::kIsOpaque,

@ -258,45 +258,6 @@ scoped_refptr<VideoFrame> ReadbackTextureBackedFrameToMemorySyncOOP(
} // namespace
double GetPixelAspectRatio(const gfx::Rect& visible_rect,
const gfx::Size& natural_size) {
double visible_width = visible_rect.width();
double visible_height = visible_rect.height();
double natural_width = natural_size.width();
double natural_height = natural_size.height();
return (visible_height * natural_width) / (visible_width * natural_height);
}
gfx::Size GetNaturalSize(const gfx::Rect& visible_rect,
double pixel_aspect_ratio) {
// TODO(sandersd): Also handle conversion back to integers overflowing.
if (!std::isfinite(pixel_aspect_ratio) || pixel_aspect_ratio <= 0.0)
return gfx::Size();
// The HTML spec requires that we always grow a dimension to match aspect
// ratio, rather than modify just the width:
// github.com/whatwg/html/commit/2e94aa64fcf9adbd2f70d8c2aecd192c8678e298
if (pixel_aspect_ratio >= 1.0) {
return gfx::Size(std::round(visible_rect.width() * pixel_aspect_ratio),
visible_rect.height());
}
return gfx::Size(visible_rect.width(),
std::round(visible_rect.height() / pixel_aspect_ratio));
}
gfx::Size GetNaturalSize(const gfx::Size& visible_size,
int aspect_ratio_numerator,
int aspect_ratio_denominator) {
if (aspect_ratio_denominator <= 0 || aspect_ratio_numerator <= 0)
return gfx::Size();
double pixel_aspect_ratio =
aspect_ratio_numerator / static_cast<double>(aspect_ratio_denominator);
return GetNaturalSize(gfx::Rect(visible_size), pixel_aspect_ratio);
}
void FillYUV(VideoFrame* frame, uint8_t y, uint8_t u, uint8_t v) {
// Fill the Y plane.
uint8_t* y_plane = frame->data(VideoFrame::kYPlane);

@ -30,37 +30,6 @@ namespace media {
class VideoFramePool;
class VideoFrame;
// Computes the pixel aspect ratio of a given |visible_rect| from its
// |natural_size|.
//
// See https://en.wikipedia.org/wiki/Pixel_aspect_ratio for a detailed
// definition.
//
// Returns NaN or Infinity if |visible_rect| or |natural_size| are empty.
//
// Note: Something has probably gone wrong if you need to call this function;
// pixel aspect ratios should be the source of truth.
//
// TODO(crbug.com/837337): Decide how to encode 'not provided' for pixel aspect
// ratios, and return that if one of the inputs is empty.
MEDIA_EXPORT double GetPixelAspectRatio(const gfx::Rect& visible_rect,
const gfx::Size& natural_size);
// Increases (at most) one of the dimensions of |visible_rect| to produce
// a |natural_size| with the given pixel aspect ratio.
//
// Returns gfx::Size() if |pixel_aspect_ratio| is not finite and positive.
MEDIA_EXPORT gfx::Size GetNaturalSize(const gfx::Rect& visible_rect,
double pixel_aspect_ratio);
// Overload that takes the pixel aspect ratio as an integer fraction (and
// |visible_size| instead of |visible_rect|).
//
// Returns gfx::Size() if numerator or denominator are not positive.
MEDIA_EXPORT gfx::Size GetNaturalSize(const gfx::Size& visible_size,
int aspect_ratio_numerator,
int aspect_ratio_denominator);
// Fills |frame| containing YUV data to the given color values.
MEDIA_EXPORT void FillYUV(VideoFrame* frame, uint8_t y, uint8_t u, uint8_t v);

@ -189,84 +189,6 @@ class VideoUtilTest : public testing::Test {
scoped_refptr<VideoFrame> destination_frame_;
};
TEST_F(VideoUtilTest, GetPixelAspectRatio) {
gfx::Rect visible_rect(320, 240);
// Test empty or invalid combinations.
EXPECT_TRUE(std::isnan(GetPixelAspectRatio(gfx::Rect(), gfx::Size())));
EXPECT_TRUE(std::isnan(GetPixelAspectRatio(gfx::Rect(1, 1), gfx::Size())));
EXPECT_TRUE(std::isnan(GetPixelAspectRatio(gfx::Rect(), gfx::Size(1, 1))));
EXPECT_TRUE(
std::isinf(GetPixelAspectRatio(gfx::Rect(1, 1), gfx::Size(1, 0))));
EXPECT_EQ(0.0, GetPixelAspectRatio(gfx::Rect(1, 1), gfx::Size(0, 1)));
EXPECT_EQ(0.0, GetPixelAspectRatio(gfx::Rect(1, 0), gfx::Size(1, 1)));
EXPECT_TRUE(
std::isinf(GetPixelAspectRatio(gfx::Rect(0, 1), gfx::Size(1, 1))));
// Some normal ratios.
EXPECT_DOUBLE_EQ(1.0, GetPixelAspectRatio(visible_rect, gfx::Size(320, 240)));
EXPECT_DOUBLE_EQ(2.0, GetPixelAspectRatio(visible_rect, gfx::Size(640, 240)));
EXPECT_DOUBLE_EQ(0.5, GetPixelAspectRatio(visible_rect, gfx::Size(320, 480)));
}
TEST_F(VideoUtilTest, GetNaturalSize_Double) {
gfx::Rect visible_rect(320, 240);
// Test 0 sizes.
EXPECT_EQ(gfx::Size(0, 0), GetNaturalSize(gfx::Rect(0, 0), 1.0));
EXPECT_EQ(gfx::Size(0, 1), GetNaturalSize(gfx::Rect(0, 1), 1.0));
EXPECT_EQ(gfx::Size(1, 0), GetNaturalSize(gfx::Rect(1, 0), 1.0));
// Test abnormal ratios.
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_rect, NAN));
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_rect, 0.0));
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_rect, INFINITY));
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_rect, -INFINITY));
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_rect, -1.0));
// Test normal sizes and ratios.
EXPECT_EQ(gfx::Size(320, 240), GetNaturalSize(visible_rect, 1.0 / 1.0));
EXPECT_EQ(gfx::Size(640, 240), GetNaturalSize(visible_rect, 2.0 / 1.0));
EXPECT_EQ(gfx::Size(320, 480), GetNaturalSize(visible_rect, 1.0 / 2.0));
EXPECT_EQ(gfx::Size(427, 240), GetNaturalSize(visible_rect, 4.0 / 3.0));
EXPECT_EQ(gfx::Size(320, 320), GetNaturalSize(visible_rect, 3.0 / 4.0));
EXPECT_EQ(gfx::Size(569, 240), GetNaturalSize(visible_rect, 16.0 / 9.0));
EXPECT_EQ(gfx::Size(320, 427), GetNaturalSize(visible_rect, 9.0 / 16.0));
// Test some random ratios.
EXPECT_EQ(gfx::Size(495, 240), GetNaturalSize(visible_rect, 17.0 / 11.0));
EXPECT_EQ(gfx::Size(320, 371), GetNaturalSize(visible_rect, 11.0 / 17.0));
}
TEST_F(VideoUtilTest, GetNaturalSize_Fraction) {
gfx::Size visible_size(320, 240);
// Test 0 sizes.
EXPECT_EQ(gfx::Size(0, 0), GetNaturalSize(gfx::Size(0, 0), 1, 1));
EXPECT_EQ(gfx::Size(0, 1), GetNaturalSize(gfx::Size(0, 1), 1, 1));
EXPECT_EQ(gfx::Size(1, 0), GetNaturalSize(gfx::Size(1, 0), 1, 1));
// Test abnormal ratios.
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_size, 0, 0));
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_size, 0, 1));
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_size, 1, 0));
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_size, 1, -1));
EXPECT_EQ(gfx::Size(), GetNaturalSize(visible_size, -1, 1));
// Test normal sizes and ratios.
EXPECT_EQ(gfx::Size(320, 240), GetNaturalSize(visible_size, 1, 1));
EXPECT_EQ(gfx::Size(640, 240), GetNaturalSize(visible_size, 2, 1));
EXPECT_EQ(gfx::Size(320, 480), GetNaturalSize(visible_size, 1, 2));
EXPECT_EQ(gfx::Size(427, 240), GetNaturalSize(visible_size, 4, 3));
EXPECT_EQ(gfx::Size(320, 320), GetNaturalSize(visible_size, 3, 4));
EXPECT_EQ(gfx::Size(569, 240), GetNaturalSize(visible_size, 16, 9));
EXPECT_EQ(gfx::Size(320, 427), GetNaturalSize(visible_size, 9, 16));
// Test some random ratios.
EXPECT_EQ(gfx::Size(495, 240), GetNaturalSize(visible_size, 17, 11));
EXPECT_EQ(gfx::Size(320, 371), GetNaturalSize(visible_size, 11, 17));
}
namespace {
uint8_t src6x4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,

@ -525,7 +525,7 @@ void CdmAdapter::InitializeVideoDecoder(const VideoDecoderConfig& config,
return;
}
pixel_aspect_ratio_ = config.GetPixelAspectRatio();
aspect_ratio_ = config.aspect_ratio();
is_video_encrypted_ = config.is_encrypted();
if (status == cdm::kDeferredInitialization) {
@ -605,7 +605,7 @@ void CdmAdapter::DecryptAndDecodeVideo(scoped_refptr<DecoderBuffer> encrypted,
gfx::Rect visible_rect(video_frame->Size().width, video_frame->Size().height);
scoped_refptr<VideoFrame> decoded_frame = video_frame->TransformToVideoFrame(
GetNaturalSize(visible_rect, pixel_aspect_ratio_));
aspect_ratio_.GetNaturalSize(visible_rect));
if (!decoded_frame) {
DLOG(ERROR) << __func__ << ": TransformToVideoFrame failed.";
std::move(video_decode_cb).Run(Decryptor::kError, nullptr);
@ -640,7 +640,7 @@ void CdmAdapter::DeinitializeDecoder(StreamType stream_type) {
audio_channel_layout_ = CHANNEL_LAYOUT_NONE;
break;
case Decryptor::kVideo:
pixel_aspect_ratio_ = 0.0;
aspect_ratio_ = VideoAspectRatio();
break;
}
}

@ -28,6 +28,7 @@
#include "media/base/content_decryption_module.h"
#include "media/base/decryptor.h"
#include "media/base/media_export.h"
#include "media/base/video_aspect_ratio.h"
#include "media/cdm/api/content_decryption_module.h"
#include "ui/gfx/geometry/size.h"
@ -241,7 +242,7 @@ class MEDIA_EXPORT CdmAdapter final : public ContentDecryptionModule,
ChannelLayout audio_channel_layout_ = CHANNEL_LAYOUT_NONE;
// Keep track of aspect ratio from the latest configuration.
double pixel_aspect_ratio_ = 0.0;
VideoAspectRatio aspect_ratio_;
// Whether the current video config is encrypted.
bool is_video_encrypted_ = false;

@ -14,6 +14,7 @@
#include "media/base/decoder_buffer.h"
#include "media/base/encryption_scheme.h"
#include "media/base/media_util.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_util.h"
#include "media/formats/mp4/box_definitions.h"
@ -491,17 +492,26 @@ bool AVStreamToVideoDecoderConfig(const AVStream* stream,
gfx::Rect visible_rect(codec_context->width, codec_context->height);
gfx::Size coded_size = visible_rect.size();
AVRational aspect_ratio = {1, 1};
if (stream->sample_aspect_ratio.num)
aspect_ratio = stream->sample_aspect_ratio;
else if (codec_context->sample_aspect_ratio.num)
aspect_ratio = codec_context->sample_aspect_ratio;
// In some cases a container may have a DAR but no PAR, but FFmpeg translates
// everything to PAR. It is possible to get the render width and height, but I
// didn't find a way to determine whether that should be preferred to the PAR.
VideoAspectRatio aspect_ratio;
if (stream->sample_aspect_ratio.num) {
aspect_ratio = VideoAspectRatio::PAR(stream->sample_aspect_ratio.num,
stream->sample_aspect_ratio.den);
} else if (codec_context->sample_aspect_ratio.num) {
aspect_ratio =
VideoAspectRatio::PAR(codec_context->sample_aspect_ratio.num,
codec_context->sample_aspect_ratio.den);
}
// Used to guess color space and to create the config. The first use should
// probably change to coded size, and the second should be removed as part of
// crbug.com/1214061.
gfx::Size natural_size = aspect_ratio.GetNaturalSize(visible_rect);
VideoCodec codec = CodecIDToVideoCodec(codec_context->codec_id);
gfx::Size natural_size =
GetNaturalSize(visible_rect.size(), aspect_ratio.num, aspect_ratio.den);
// Without the ffmpeg decoder configured, libavformat is unable to get the
// profile, format, or coded size. So choose sensible defaults and let
// decoders fail later if the configuration is actually unsupported.
@ -649,6 +659,8 @@ bool AVStreamToVideoDecoderConfig(const AVStream* stream,
color_space, VideoTransformation(video_rotation),
coded_size, visible_rect, natural_size, extra_data,
GetEncryptionScheme(stream));
// Set the aspect ratio explicitly since our version hasn't been rounded.
config->set_aspect_ratio(aspect_ratio);
if (stream->nb_side_data) {
for (int i = 0; i < stream->nb_side_data; ++i) {

@ -19,6 +19,7 @@
#include "media/base/decoder_buffer.h"
#include "media/base/limits.h"
#include "media/base/media_log.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_util.h"
extern "C" {
@ -441,7 +442,7 @@ scoped_refptr<VideoFrame> Dav1dVideoDecoder::BindImageToVideoFrame(
auto frame = VideoFrame::WrapExternalYuvData(
pixel_format, visible_size, gfx::Rect(visible_size),
GetNaturalSize(gfx::Rect(visible_size), config_.GetPixelAspectRatio()),
config_.aspect_ratio().GetNaturalSize(gfx::Rect(visible_size)),
pic->stride[0], uv_plane_stride, uv_plane_stride,
static_cast<uint8_t*>(pic->data[0]), u_plane, v_plane,
base::TimeDelta::FromMicroseconds(pic->m.timestamp));

@ -20,6 +20,7 @@
#include "media/base/limits.h"
#include "media/base/media_log.h"
#include "media/base/timestamp_constants.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/ffmpeg/ffmpeg_common.h"
@ -139,15 +140,13 @@ int FFmpegVideoDecoder::GetVideoBuffer(struct AVCodecContext* codec_context,
if (ret < 0)
return ret;
gfx::Size natural_size;
if (codec_context->sample_aspect_ratio.num > 0) {
natural_size = GetNaturalSize(size,
codec_context->sample_aspect_ratio.num,
codec_context->sample_aspect_ratio.den);
} else {
natural_size =
GetNaturalSize(gfx::Rect(size), config_.GetPixelAspectRatio());
VideoAspectRatio aspect_ratio = config_.aspect_ratio();
if (!aspect_ratio.IsValid() && codec_context->sample_aspect_ratio.num > 0) {
aspect_ratio =
VideoAspectRatio::PAR(codec_context->sample_aspect_ratio.num,
codec_context->sample_aspect_ratio.den);
}
gfx::Size natural_size = aspect_ratio.GetNaturalSize(gfx::Rect(size));
// FFmpeg has specific requirements on the allocation size of the frame. The
// following logic replicates FFmpeg's allocation strategy to ensure buffers

@ -30,6 +30,7 @@
#include "media/base/bind_to_current_loop.h"
#include "media/base/cdm_context.h"
#include "media/base/media_switches.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_decoder.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
@ -268,9 +269,8 @@ class FuchsiaVideoDecoder : public VideoDecoder,
OutputCB output_cb_;
WaitingCB waiting_cb_;
// Aspect ratio specified in container, or 1.0 if it's not specified. This
// value is used only if the aspect ratio is not specified in the bitstream.
float container_pixel_aspect_ratio_ = 1.0;
// Aspect ratio specified in container.
VideoAspectRatio container_aspect_ratio_;
std::unique_ptr<SysmemBufferStream> sysmem_buffer_stream_;
@ -362,7 +362,7 @@ void FuchsiaVideoDecoder::Initialize(const VideoDecoderConfig& config,
output_cb_ = output_cb;
waiting_cb_ = waiting_cb;
container_pixel_aspect_ratio_ = config.GetPixelAspectRatio();
container_aspect_ratio_ = config.aspect_ratio();
// Keep decoder and decryptor if the configuration hasn't changed.
if (decoder_ && current_config_.is_encrypted() == config.codec() &&
@ -646,13 +646,11 @@ void FuchsiaVideoDecoder::OnStreamProcessorOutputPacket(
auto display_rect = gfx::Rect(output_format_.primary_display_width_pixels,
output_format_.primary_display_height_pixels);
float pixel_aspect_ratio;
if (output_format_.has_pixel_aspect_ratio) {
pixel_aspect_ratio =
static_cast<float>(output_format_.pixel_aspect_ratio_width) /
static_cast<float>(output_format_.pixel_aspect_ratio_height);
} else {
pixel_aspect_ratio = container_pixel_aspect_ratio_;
VideoAspectRatio aspect_ratio = container_aspect_ratio_;
if (!aspect_ratio.IsValid() && output_format_.has_pixel_aspect_ratio) {
aspect_ratio =
VideoAspectRatio::PAR(output_format_.pixel_aspect_ratio_width,
output_format_.pixel_aspect_ratio_height);
}
auto timestamp = output_packet.timestamp();
@ -670,7 +668,7 @@ void FuchsiaVideoDecoder::OnStreamProcessorOutputPacket(
auto frame = output_mailboxes_[buffer_index]->CreateFrame(
pixel_format, coded_size, display_rect,
GetNaturalSize(display_rect, pixel_aspect_ratio), timestamp,
aspect_ratio.GetNaturalSize(display_rect), timestamp,
base::BindOnce(&FuchsiaVideoDecoder::ReleaseOutputPacket,
base::Unretained(this), std::move(output_packet)));

@ -303,7 +303,7 @@ void Gav1VideoDecoder::Initialize(const VideoDecoderConfig& config,
output_cb_ = output_cb;
state_ = DecoderState::kDecoding;
color_space_ = config.color_space_info();
pixel_aspect_ratio_ = config.GetPixelAspectRatio();
aspect_ratio_ = config.aspect_ratio();
std::move(bound_init_cb).Run(OkStatus());
}
@ -442,9 +442,9 @@ scoped_refptr<VideoFrame> Gav1VideoDecoder::CreateVideoFrame(
// The buffer for the new frame will be zero initialized. Reused frames
// will not be zero initialized.
// The zero initialization is necessary for FFmpeg but not for libgav1.
return frame_pool_.CreateFrame(
format, coded_size, visible_rect,
GetNaturalSize(visible_rect, pixel_aspect_ratio_), kNoTimestamp);
return frame_pool_.CreateFrame(format, coded_size, visible_rect,
aspect_ratio_.GetNaturalSize(visible_rect),
kNoTimestamp);
}
void Gav1VideoDecoder::CloseDecoder() {

@ -15,6 +15,7 @@
#include "base/sequence_checker.h"
#include "media/base/media_export.h"
#include "media/base/supported_video_decoder_config.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame_pool.h"
#include "media/filters/offloading_video_decoder.h"
@ -73,7 +74,7 @@ class MEDIA_EXPORT Gav1VideoDecoder : public OffloadableVideoDecoder {
// Info configured in Initialize(). These are used in outputting frames.
VideoColorSpace color_space_;
double pixel_aspect_ratio_;
VideoAspectRatio aspect_ratio_;
DecoderState state_ = DecoderState::kUninitialized;

@ -24,7 +24,7 @@
#include "media/base/decoder_buffer.h"
#include "media/base/limits.h"
#include "media/base/media_switches.h"
#include "media/base/video_util.h"
#include "media/base/video_aspect_ratio.h"
#include "media/filters/frame_buffer_pool.h"
#include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
#include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
@ -547,7 +547,7 @@ bool VpxVideoDecoder::CopyVpxImageToVideoFrame(
// vpx_video_decoder inconsistent with decoders where changes to
// pixel aspect ratio are not surfaced (e.g. Android MediaCodec).
const gfx::Size natural_size =
GetNaturalSize(gfx::Rect(visible_size), config_.GetPixelAspectRatio());
config_.aspect_ratio().GetNaturalSize(gfx::Rect(visible_size));
if (memory_pool_) {
DCHECK_EQ(kCodecVP9, config_.codec());

@ -504,16 +504,16 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) {
// If PASP is available, use the coded size and PASP to calculate the
// natural size. Otherwise, use the size in track header for natural size.
gfx::Size natural_size(visible_rect.size());
VideoAspectRatio aspect_ratio;
if (entry.pixel_aspect.h_spacing != 1 ||
entry.pixel_aspect.v_spacing != 1) {
natural_size =
GetNaturalSize(visible_rect.size(), entry.pixel_aspect.h_spacing,
entry.pixel_aspect.v_spacing);
aspect_ratio = VideoAspectRatio::PAR(entry.pixel_aspect.h_spacing,
entry.pixel_aspect.v_spacing);
} else if (track->header.width && track->header.height) {
natural_size =
gfx::Size(track->header.width, track->header.height);
aspect_ratio =
VideoAspectRatio::DAR(track->header.width, track->header.height);
}
gfx::Size natural_size = aspect_ratio.GetNaturalSize(visible_rect);
uint32_t video_track_id = track->header.track_id;
if (video_track_ids_.find(video_track_id) != video_track_ids_.end()) {
@ -537,6 +537,7 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) {
// No decoder-specific buffer needed for AVC;
// SPS/PPS are embedded in the video stream
EmptyExtraData(), scheme);
video_config.set_aspect_ratio(aspect_ratio);
video_config.set_level(entry.video_codec_level);
if (entry.video_color_space.IsSpecified())

@ -27,10 +27,10 @@
#include "media/base/scoped_async_trace.h"
#include "media/base/status.h"
#include "media/base/supported_video_decoder_config.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_codecs.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/gpu/android/android_video_surface_chooser.h"
#include "media/gpu/android/codec_allocator.h"
#include "media/media_buildflags.h"
@ -980,7 +980,7 @@ bool MediaCodecVideoDecoder::DequeueOutput() {
}
video_frame_factory_->CreateVideoFrame(
std::move(output_buffer), presentation_time,
GetNaturalSize(visible_rect, decoder_config_.GetPixelAspectRatio()),
decoder_config_.aspect_ratio().GetNaturalSize(visible_rect),
CreatePromotionHintCB(),
base::BindOnce(&MediaCodecVideoDecoder::ForwardVideoFrame,
weak_factory_.GetWeakPtr(), reset_generation_,

@ -15,7 +15,6 @@
#include "base/thread_annotations.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/video_util.h"
namespace media {
@ -219,10 +218,7 @@ class PictureBufferManagerImpl : public PictureBufferManager {
DLOG(WARNING) << "visible_rect " << visible_rect.ToString()
<< " exceeds coded_size "
<< picture_buffer_data.texture_size.ToString();
double pixel_aspect_ratio =
GetPixelAspectRatio(visible_rect, natural_size);
visible_rect.Intersect(gfx::Rect(picture_buffer_data.texture_size));
natural_size = GetNaturalSize(visible_rect, pixel_aspect_ratio);
}
// Record the output.

@ -23,6 +23,7 @@
#include "media/base/bitstream_buffer.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_log.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_codecs.h"
#include "media/base/video_types.h"
#include "media/base/video_util.h"
@ -635,7 +636,7 @@ void VdaVideoDecoder::PictureReadyOnParentThread(Picture picture) {
// Create a VideoFrame for the picture.
scoped_refptr<VideoFrame> frame = picture_buffer_manager_->CreateVideoFrame(
picture, timestamp, visible_rect,
GetNaturalSize(visible_rect, config_.GetPixelAspectRatio()));
config_.aspect_ratio().GetNaturalSize(visible_rect));
if (!frame) {
EnterErrorState();
return;

@ -173,7 +173,7 @@ void V4L2VideoDecoder::Initialize(const VideoDecoderConfig& config,
DCHECK(!output_queue_);
profile_ = config.profile();
pixel_aspect_ratio_ = config.GetPixelAspectRatio();
aspect_ratio_ = config.aspect_ratio();
if (profile_ == VIDEO_CODEC_PROFILE_UNKNOWN) {
VLOGF(1) << "Unknown profile.";
@ -376,7 +376,7 @@ bool V4L2VideoDecoder::SetupOutputFormat(const gfx::Size& size,
if (pool) {
absl::optional<GpuBufferLayout> layout = pool->Initialize(
fourcc, adjusted_size, visible_rect,
GetNaturalSize(visible_rect, pixel_aspect_ratio_), num_output_frames_,
aspect_ratio_.GetNaturalSize(visible_rect), num_output_frames_,
/*use_protected=*/false);
if (!layout) {
VLOGF(1) << "Failed to setup format to VFPool";
@ -683,7 +683,7 @@ void V4L2VideoDecoder::OutputFrame(scoped_refptr<VideoFrame> frame,
if (frame->visible_rect() != visible_rect ||
frame->timestamp() != timestamp) {
gfx::Size natural_size = GetNaturalSize(visible_rect, pixel_aspect_ratio_);
gfx::Size natural_size = aspect_ratio_.GetNaturalSize(visible_rect);
scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
frame, frame->format(), visible_rect, natural_size);
wrapped_frame->set_timestamp(timestamp);

@ -23,6 +23,7 @@
#include "base/time/time.h"
#include "media/base/cdm_context.h"
#include "media/base/supported_video_decoder_config.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_types.h"
#include "media/gpu/chromeos/gpu_buffer_layout.h"
#include "media/gpu/chromeos/video_decoder_pipeline.h"
@ -170,8 +171,9 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
// The default value is only used at the first time of
// DmabufVideoFramePool::Initialize() during Initialize().
size_t num_output_frames_ = 1;
// Ratio of natural_size to visible_rect of the output frame.
double pixel_aspect_ratio_ = 0.0;
// Aspect ratio from config to use for output frames.
VideoAspectRatio aspect_ratio_;
// Callbacks passed from Initialize().
OutputCB output_cb_;

@ -287,7 +287,7 @@ void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config,
DCHECK(client_);
frame_pool_ = client_->GetVideoFramePool();
pixel_aspect_ratio_ = config.GetPixelAspectRatio();
aspect_ratio_ = config.aspect_ratio();
output_cb_ = std::move(output_cb);
waiting_cb_ = std::move(waiting_cb);
@ -588,7 +588,7 @@ void VaapiVideoDecoder::SurfaceReady(scoped_refptr<VASurface> va_surface,
if (video_frame->visible_rect() != visible_rect ||
video_frame->timestamp() != timestamp) {
gfx::Size natural_size = GetNaturalSize(visible_rect, pixel_aspect_ratio_);
gfx::Size natural_size = aspect_ratio_.GetNaturalSize(visible_rect);
scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
video_frame, video_frame->format(), visible_rect, natural_size);
wrapped_frame->set_timestamp(timestamp);
@ -774,7 +774,7 @@ void VaapiVideoDecoder::ApplyResolutionChangeWithScreenSizes(
}
}
const gfx::Size natural_size =
GetNaturalSize(output_visible_rect, pixel_aspect_ratio_);
aspect_ratio_.GetNaturalSize(output_visible_rect);
if (!frame_pool_->Initialize(
*format_fourcc, output_pic_size, output_visible_rect, natural_size,
decoder_->GetRequiredNumOfPictures(), !!cdm_context_ref_)) {

@ -25,6 +25,7 @@
#include "media/base/cdm_context.h"
#include "media/base/status.h"
#include "media/base/supported_video_decoder_config.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_codecs.h"
#include "media/base/video_frame_layout.h"
#include "media/gpu/chromeos/video_decoder_pipeline.h"
@ -172,8 +173,8 @@ class VaapiVideoDecoder : public DecoderInterface,
VideoColorSpace color_space_;
absl::optional<gfx::HDRMetadata> hdr_metadata_;
// Ratio of natural size to |visible_rect_| of the output frames.
double pixel_aspect_ratio_ = 0.0;
// Aspect ratio from the config.
VideoAspectRatio aspect_ratio_;
// Video frame pool used to allocate and recycle video frames.
DmabufVideoFramePool* frame_pool_ = nullptr;

@ -23,6 +23,7 @@
#include "media/base/decoder_buffer.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_codecs.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
@ -825,8 +826,8 @@ bool D3D11VideoDecoder::OutputResult(const CodecPicture* picture,
visible_rect = config_.visible_rect();
// TODO(https://crbug.com/843150): Use aspect ratio from decoder (SPS) if
// stream metadata doesn't overrride it.
double pixel_aspect_ratio = config_.GetPixelAspectRatio();
// the config's aspect ratio isn't valid.
gfx::Size natural_size = config_.aspect_ratio().GetNaturalSize(visible_rect);
base::TimeDelta timestamp = picture_buffer->timestamp_;
@ -843,7 +844,7 @@ bool D3D11VideoDecoder::OutputResult(const CodecPicture* picture,
scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTextures(
texture_selector_->PixelFormat(), mailbox_holders,
VideoFrame::ReleaseMailboxCB(), picture_buffer->size(), visible_rect,
GetNaturalSize(visible_rect, pixel_aspect_ratio), timestamp);
natural_size, timestamp);
if (!frame) {
// This can happen if, somehow, we get an unsupported combination of