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:
parent
f0a7c5ac16
commit
fffc1e5578
@ -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.
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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',
|
||||
|
||||
132
webrtc/common_video/h264/profile_level_id.cc
Normal file
132
webrtc/common_video/h264/profile_level_id.cc
Normal 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
|
||||
62
webrtc/common_video/h264/profile_level_id.h
Normal file
62
webrtc/common_video/h264/profile_level_id.h
Normal 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_
|
||||
74
webrtc/common_video/h264/profile_level_id_unittest.cc
Normal file
74
webrtc/common_video/h264/profile_level_id_unittest.cc
Normal 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
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user