Find @par with smallest error with par_y in [1, 19]
Quotient reduction to minimal form does not work well in practice due to rounding of the inputs. Bug: 23432995 Change-Id: If333ab48accb7f6a3fac6ba4eb48ca7b9cee9826
This commit is contained in:
parent
6cc81137a8
commit
d71a37656a
|
@ -218,31 +218,43 @@ bool HasRequiredVideoFields(const MediaInfo_VideoInfo& video_info) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Euclidean algorithm.
|
|
||||||
// gcd(a,0) = a
|
|
||||||
// gcd(a,b) = gcd(b, a % b)
|
|
||||||
uint32_t GreatestCommonDivisor(uint32_t a, uint32_t b) {
|
|
||||||
while (b != 0) {
|
|
||||||
const uint32_t new_b = a % b;
|
|
||||||
a = b;
|
|
||||||
b = new_b;
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the picture aspect ratio string e.g. "16:9", "4:3".
|
// Returns the picture aspect ratio string e.g. "16:9", "4:3".
|
||||||
std::string GetPictureAspectRatio(uint32_t width, uint32_t height,
|
// "Reducing the quotient to minimal form" does not work well in practice as
|
||||||
uint32_t pixel_width, uint32_t pixel_height) {
|
// there may be some rounding performed in the input, e.g. the resolution of
|
||||||
|
// 480p is 854:480 for 16:9 aspect ratio, can only be reduced to 427:240.
|
||||||
|
// The algorithm finds out the pair of integers, num and den, where num / den is
|
||||||
|
// the closest ratio to scaled_width / scaled_height, by looping den through
|
||||||
|
// common values.
|
||||||
|
std::string GetPictureAspectRatio(uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t pixel_width,
|
||||||
|
uint32_t pixel_height) {
|
||||||
const uint32_t scaled_width = pixel_width * width;
|
const uint32_t scaled_width = pixel_width * width;
|
||||||
const uint32_t scaled_height = pixel_height * height;
|
const uint32_t scaled_height = pixel_height * height;
|
||||||
const uint32_t gcd = GreatestCommonDivisor(scaled_width, scaled_height);
|
const double par = static_cast<double>(scaled_width) / scaled_height;
|
||||||
DCHECK_NE(gcd, 0u) << "GCD of width*pix_width (" << scaled_width
|
|
||||||
<< ") and height*pix_height (" << scaled_height
|
|
||||||
<< ") is 0.";
|
|
||||||
|
|
||||||
const uint32_t par_x = scaled_width / gcd;
|
// Typical aspect ratios have par_y less than or equal to 19:
|
||||||
const uint32_t par_y = scaled_height / gcd;
|
// https://en.wikipedia.org/wiki/List_of_common_resolutions
|
||||||
return base::IntToString(par_x) + ":" + base::IntToString(par_y);
|
const uint32_t kLargestPossibleParY = 19;
|
||||||
|
|
||||||
|
uint32_t par_num;
|
||||||
|
uint32_t par_den;
|
||||||
|
double min_error = 1.0;
|
||||||
|
for (uint32_t den = 1; den <= kLargestPossibleParY; ++den) {
|
||||||
|
uint32_t num = par * den + 0.5;
|
||||||
|
double error = fabs(par - static_cast<double>(num) / den);
|
||||||
|
if (error < min_error) {
|
||||||
|
min_error = error;
|
||||||
|
par_num = num;
|
||||||
|
par_den = den;
|
||||||
|
if (error == 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VLOG(2) << "width*pix_width : height*pixel_height (" << scaled_width << ":"
|
||||||
|
<< scaled_height << ") reduced to " << par_num << ":" << par_den
|
||||||
|
<< " with error " << min_error << ".";
|
||||||
|
|
||||||
|
return base::IntToString(par_num) + ":" + base::IntToString(par_den);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds an entry to picture_aspect_ratio if the size of picture_aspect_ratio is
|
// Adds an entry to picture_aspect_ratio if the size of picture_aspect_ratio is
|
||||||
|
|
|
@ -805,6 +805,17 @@ TEST_F(CommonMpdBuilderTest,
|
||||||
// Verify that if the picture aspect ratio of all the Representations are the
|
// Verify that if the picture aspect ratio of all the Representations are the
|
||||||
// same, @par attribute is present.
|
// same, @par attribute is present.
|
||||||
TEST_F(CommonMpdBuilderTest, AdaptationSetParAllSame) {
|
TEST_F(CommonMpdBuilderTest, AdaptationSetParAllSame) {
|
||||||
|
const char k480pVideoInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: 'avc1'\n"
|
||||||
|
" width: 854\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 3000\n"
|
||||||
|
" frame_duration: 100\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
const char k720pVideoInfo[] =
|
const char k720pVideoInfo[] =
|
||||||
"video_info {\n"
|
"video_info {\n"
|
||||||
" codec: 'avc1'\n"
|
" codec: 'avc1'\n"
|
||||||
|
@ -844,6 +855,8 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetParAllSame) {
|
||||||
|
|
||||||
AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet("");
|
AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet("");
|
||||||
ASSERT_TRUE(video_adaptation_set);
|
ASSERT_TRUE(video_adaptation_set);
|
||||||
|
ASSERT_TRUE(video_adaptation_set->AddRepresentation(
|
||||||
|
ConvertToMediaInfo(k480pVideoInfo)));
|
||||||
ASSERT_TRUE(video_adaptation_set->AddRepresentation(
|
ASSERT_TRUE(video_adaptation_set->AddRepresentation(
|
||||||
ConvertToMediaInfo(k720pVideoInfo)));
|
ConvertToMediaInfo(k720pVideoInfo)));
|
||||||
ASSERT_TRUE(video_adaptation_set->AddRepresentation(
|
ASSERT_TRUE(video_adaptation_set->AddRepresentation(
|
||||||
|
|
Loading…
Reference in New Issue