Add functionality for parsing H264 profile-level-id

The new code is only exercised in tests so far. The H264 profile-level-id
parsing is not complete, but it should be enough for our purposes for
now.

BUG=webrtc:6400,webrtc:6337

Review-Url: https://codereview.webrtc.org/2459633002
Cr-Commit-Position: refs/heads/master@{#14850}
This commit is contained in:
magjed 2016-10-31 05:55:57 -07:00 committed by Commit bot
parent f0a7c5ac16
commit fffc1e5578
7 changed files with 273 additions and 4 deletions

View File

@ -498,8 +498,6 @@ enum VideoCodecComplexity {
kComplexityMax = 3
};
enum VideoCodecProfile { kProfileBase = 0x00, kProfileMain = 0x01 };
enum VP8ResilienceMode {
kResilienceOff, // The stream produced by the encoder requires a
// recovery frame (typically a key frame) to be
@ -543,7 +541,6 @@ struct VideoCodecVP9 {
// H264 specific.
struct VideoCodecH264 {
VideoCodecProfile profile;
bool frameDroppingOn;
int keyFrameInterval;
// These are NULL/0 if not externally negotiated.

View File

@ -24,6 +24,8 @@ rtc_static_library("common_video") {
"h264/h264_common.h",
"h264/pps_parser.cc",
"h264/pps_parser.h",
"h264/profile_level_id.cc",
"h264/profile_level_id.h",
"h264/sps_parser.cc",
"h264/sps_parser.h",
"h264/sps_vui_rewriter.cc",
@ -97,6 +99,7 @@ if (rtc_include_tests) {
"bitrate_adjuster_unittest.cc",
"h264/h264_bitstream_parser_unittest.cc",
"h264/pps_parser_unittest.cc",
"h264/profile_level_id_unittest.cc",
"h264/sps_parser_unittest.cc",
"h264/sps_vui_rewriter_unittest.cc",
"i420_buffer_pool_unittest.cc",

View File

@ -60,6 +60,8 @@
'h264/h264_common.h',
'h264/pps_parser.cc',
'h264/pps_parser.h',
'h264/profile_level_id.cc',
'h264/profile_level_id.h',
'h264/sps_parser.cc',
'h264/sps_parser.h',
'h264/h264_bitstream_parser.cc',

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/common_video/h264/profile_level_id.h"
#include <cstdlib>
#include <cstring>
namespace {
// For level_idc=11 and profile_idc=0x42, 0x4D, or 0x58, the constraint set3
// flag specifies if level 1b or level 1.1 is used.
const uint8_t kConstraintSet3Flag = 0x10;
// Convert a string of 8 characters into a byte where the positions containing
// character c will have their bit set. For example, c = 'x', str = "x1xx0000"
// will return 0b10110000. constexpr is used so that the pattern table in
// kProfilePatterns is statically initialized.
constexpr uint8_t ByteMaskString(char c, const char (&str)[9]) {
return (str[0] == c) << 7
| (str[1] == c) << 6
| (str[2] == c) << 5
| (str[3] == c) << 4
| (str[4] == c) << 3
| (str[5] == c) << 2
| (str[6] == c) << 1
| (str[7] == c) << 0;
}
// Class for matching bit patterns such as "x1xx0000" where 'x' is allowed to be
// either 0 or 1.
class BitPattern {
public:
constexpr BitPattern(const char (&str)[9])
: mask_(~ByteMaskString('x', str)),
masked_value_(ByteMaskString('1', str)) {}
bool IsMatch(uint8_t value) const { return masked_value_ == (value & mask_); }
private:
const uint8_t mask_;
const uint8_t masked_value_;
};
// Table for converting between profile_idc/profile_iop to H264::Profile.
struct ProfilePattern {
const uint8_t profile_idc;
const BitPattern profile_iop;
const webrtc::H264::Profile profile;
};
// This is from https://tools.ietf.org/html/rfc6184#section-8.1.
constexpr ProfilePattern kProfilePatterns[] = {
{0x42, BitPattern("x1xx0000"), webrtc::H264::kProfileConstrainedBaseline},
{0x4D, BitPattern("1xxx0000"), webrtc::H264::kProfileConstrainedBaseline},
{0x58, BitPattern("11xx0000"), webrtc::H264::kProfileConstrainedBaseline},
{0x42, BitPattern("x0xx0000"), webrtc::H264::kProfileBaseline},
{0x58, BitPattern("10xx0000"), webrtc::H264::kProfileBaseline},
{0x4D, BitPattern("0x0x0000"), webrtc::H264::kProfileMain},
{0x64, BitPattern("00000000"), webrtc::H264::kProfileHigh},
{0x64, BitPattern("00001100"), webrtc::H264::kProfileConstrainedHigh}};
} // anonymous namespace
namespace webrtc {
namespace H264 {
rtc::Optional<ProfileLevelId> ParseProfileLevelId(const char* str) {
// The string should consist of 3 bytes in hexadecimal format.
if (strlen(str) != 6u)
return rtc::Optional<ProfileLevelId>();
const uint32_t profile_level_id_numeric = strtol(str, nullptr, 16);
if (profile_level_id_numeric == 0)
return rtc::Optional<ProfileLevelId>();
// Separate into three bytes.
const uint8_t level_idc =
static_cast<uint8_t>(profile_level_id_numeric & 0xFF);
const uint8_t profile_iop =
static_cast<uint8_t>((profile_level_id_numeric >> 8) & 0xFF);
const uint8_t profile_idc =
static_cast<uint8_t>((profile_level_id_numeric >> 16) & 0xFF);
// Parse level based on level_idc and constraint set 3 flag.
Level level;
switch (level_idc) {
case kLevel1_1:
level = (profile_iop & kConstraintSet3Flag) != 0 ? kLevel1_b : kLevel1_1;
break;
case kLevel1:
case kLevel1_2:
case kLevel1_3:
case kLevel2:
case kLevel2_1:
case kLevel2_2:
case kLevel3:
case kLevel3_1:
case kLevel3_2:
case kLevel4:
case kLevel4_1:
case kLevel4_2:
case kLevel5:
case kLevel5_1:
case kLevel5_2:
level = static_cast<Level>(level_idc);
break;
default:
// Unrecognized level_idc.
return rtc::Optional<ProfileLevelId>();
}
// Parse profile_idc/profile_iop into a Profile enum.
for (const ProfilePattern& pattern : kProfilePatterns) {
if (profile_idc == pattern.profile_idc &&
pattern.profile_iop.IsMatch(profile_iop)) {
return rtc::Optional<ProfileLevelId>({pattern.profile, level});
}
}
// Unrecognized profile_idc/profile_iop combination.
return rtc::Optional<ProfileLevelId>();
}
} // namespace H264
} // namespace webrtc

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_COMMON_VIDEO_H264_PROFILE_LEVEL_ID_H_
#define WEBRTC_COMMON_VIDEO_H264_PROFILE_LEVEL_ID_H_
#include "webrtc/base/optional.h"
namespace webrtc {
namespace H264 {
enum Profile {
kProfileConstrainedBaseline,
kProfileBaseline,
kProfileMain,
kProfileConstrainedHigh,
kProfileHigh,
};
// All values are equal to ten times the level number, except level 1b which is
// special.
enum Level {
kLevel1_b = 0,
kLevel1 = 10,
kLevel1_1 = 11,
kLevel1_2 = 12,
kLevel1_3 = 13,
kLevel2 = 20,
kLevel2_1 = 21,
kLevel2_2 = 22,
kLevel3 = 30,
kLevel3_1 = 31,
kLevel3_2 = 32,
kLevel4 = 40,
kLevel4_1 = 41,
kLevel4_2 = 42,
kLevel5 = 50,
kLevel5_1 = 51,
kLevel5_2 = 52
};
struct ProfileLevelId {
Profile profile;
Level level;
};
// Parse profile level id that is represented as a string of 3 hex bytes.
// Nothing will be returned if the string is not a recognized H264
// profile level id.
rtc::Optional<ProfileLevelId> ParseProfileLevelId(const char* str);
} // namespace H264
} // namespace webrtc
#endif // WEBRTC_COMMON_VIDEO_H264_PROFILE_LEVEL_ID_H_

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/common_video/h264/profile_level_id.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace H264 {
TEST(H264ProfileLevelIdParsing, TestInvalid) {
// Malformed strings.
EXPECT_FALSE(ParseProfileLevelId(""));
EXPECT_FALSE(ParseProfileLevelId(" 42e01f"));
EXPECT_FALSE(ParseProfileLevelId("4242e01f"));
EXPECT_FALSE(ParseProfileLevelId("e01f"));
EXPECT_FALSE(ParseProfileLevelId("gggggg"));
// Invalid level.
EXPECT_FALSE(ParseProfileLevelId("42e000"));
EXPECT_FALSE(ParseProfileLevelId("42e00f"));
EXPECT_FALSE(ParseProfileLevelId("42e0ff"));
// Invalid profile.
EXPECT_FALSE(ParseProfileLevelId("42e11f"));
EXPECT_FALSE(ParseProfileLevelId("58601f"));
EXPECT_FALSE(ParseProfileLevelId("64e01f"));
}
TEST(H264ProfileLevelIdParsing, TestLevel) {
EXPECT_EQ(kLevel3_1, ParseProfileLevelId("42e01f")->level);
EXPECT_EQ(kLevel1_1, ParseProfileLevelId("42e00b")->level);
EXPECT_EQ(kLevel1_b, ParseProfileLevelId("42f00b")->level);
EXPECT_EQ(kLevel4_2, ParseProfileLevelId("42C02A")->level);
EXPECT_EQ(kLevel5_2, ParseProfileLevelId("640c34")->level);
}
TEST(H264ProfileLevelIdParsing, TestConstrainedBaseline) {
EXPECT_EQ(kProfileConstrainedBaseline,
ParseProfileLevelId("42e01f")->profile);
EXPECT_EQ(kProfileConstrainedBaseline,
ParseProfileLevelId("42C02A")->profile);
EXPECT_EQ(kProfileConstrainedBaseline,
ParseProfileLevelId("4de01f")->profile);
EXPECT_EQ(kProfileConstrainedBaseline,
ParseProfileLevelId("58f01f")->profile);
}
TEST(H264ProfileLevelIdParsing, TestBaseline) {
EXPECT_EQ(kProfileBaseline, ParseProfileLevelId("42a01f")->profile);
EXPECT_EQ(kProfileBaseline, ParseProfileLevelId("58A01F")->profile);
}
TEST(H264ProfileLevelIdParsing, TestMain) {
EXPECT_EQ(kProfileMain, ParseProfileLevelId("4D401f")->profile);
}
TEST(H264ProfileLevelIdParsing, TestHigh) {
EXPECT_EQ(kProfileHigh, ParseProfileLevelId("64001f")->profile);
}
TEST(H264ProfileLevelIdParsing, TestConstrainedHigh) {
EXPECT_EQ(kProfileConstrainedHigh, ParseProfileLevelId("640c1f")->profile);
}
} // namespace H264
} // namespace webrtc

View File

@ -62,7 +62,6 @@ VideoCodecH264 VideoEncoder::GetDefaultH264Settings() {
VideoCodecH264 h264_settings;
memset(&h264_settings, 0, sizeof(h264_settings));
h264_settings.profile = kProfileBase;
h264_settings.frameDroppingOn = true;
h264_settings.keyFrameInterval = 3000;
h264_settings.spsData = nullptr;