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:
Kongqun Yang 2015-08-25 10:31:11 -07:00 committed by KongQun Yang
parent 6cc81137a8
commit d71a37656a
2 changed files with 46 additions and 21 deletions

View File

@ -218,31 +218,43 @@ bool HasRequiredVideoFields(const MediaInfo_VideoInfo& video_info) {
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".
std::string GetPictureAspectRatio(uint32_t width, uint32_t height,
uint32_t pixel_width, uint32_t pixel_height) {
// "Reducing the quotient to minimal form" does not work well in practice as
// 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_height = pixel_height * height;
const uint32_t gcd = GreatestCommonDivisor(scaled_width, scaled_height);
DCHECK_NE(gcd, 0u) << "GCD of width*pix_width (" << scaled_width
<< ") and height*pix_height (" << scaled_height
<< ") is 0.";
const double par = static_cast<double>(scaled_width) / scaled_height;
const uint32_t par_x = scaled_width / gcd;
const uint32_t par_y = scaled_height / gcd;
return base::IntToString(par_x) + ":" + base::IntToString(par_y);
// Typical aspect ratios have par_y less than or equal to 19:
// https://en.wikipedia.org/wiki/List_of_common_resolutions
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

View File

@ -805,6 +805,17 @@ TEST_F(CommonMpdBuilderTest,
// Verify that if the picture aspect ratio of all the Representations are the
// same, @par attribute is present.
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[] =
"video_info {\n"
" codec: 'avc1'\n"
@ -844,6 +855,8 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetParAllSame) {
AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet("");
ASSERT_TRUE(video_adaptation_set);
ASSERT_TRUE(video_adaptation_set->AddRepresentation(
ConvertToMediaInfo(k480pVideoInfo)));
ASSERT_TRUE(video_adaptation_set->AddRepresentation(
ConvertToMediaInfo(k720pVideoInfo)));
ASSERT_TRUE(video_adaptation_set->AddRepresentation(