blob: ad563d9eac2131227d3e0dae294b3844f48c5fee [file] [log] [blame]
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// A parser for H.264 bitstream. It will determine will kind of Netowrk
// Abstraction Layer Unit (NALU) we have received from the guest.
#include "android/emulation/HevcNaluParser.h"
#define HEVC_DEBUG 0
#if HEVC_DEBUG
#define RED "\x1B[31m"
#define GRN "\x1B[32m"
#define YEL "\x1B[33m"
#define BLU "\x1B[34m"
#define MAG "\x1B[35m"
#define CYN "\x1B[36m"
#define WHT "\x1B[37m"
#define RESET "\x1B[0m"
#define HEVC_PRINT(color, fmt, ...) \
fprintf(stderr, color "HevcNaluParser: %s:%d " fmt "\n" RESET, __func__, \
__LINE__, ##__VA_ARGS__);
#else
#define HEVC_PRINT(fmt, ...)
#endif
#define HEVC_INFO(fmt, ...) HEVC_PRINT(RESET, fmt, ##__VA_ARGS__);
#define HEVC_WARN(fmt, ...) HEVC_PRINT(YEL, fmt, ##__VA_ARGS__);
#define HEVC_ERROR(fmt, ...) HEVC_PRINT(RED, fmt, ##__VA_ARGS__);
static const std::string kNaluTypesStrings[] = {
"0: TRAIL_N",
"1: TRAIL_R", // P frame
"2: TSA_N",
"3: TSA_R",
"4: STSA_N",
"5: STSA_R",
"6: RADL_N",
"7: RADL_R",
"8: RASL_N",
"9: RASL_R",
"10: RSV_VCL_N10",
"11: RSV_VCL_N11",
"12: RSV_VCL_N12",
"13: RSV_VCL_N13",
"14: RSV_VCL_N14",
"15: RSV_VCL_N15",
"16: BLA_W_LP",
"17: BLA_W_RADL",
"18: BLA_N_LP",
"19: IDR_W_RADL",
"20: IDR_N_LP",
"21: CRA_NUT",
"22: RSV_IRAP_VCL22",
"23: RSV_IRAP_VCL23",
"24: RSV_IRAP_VCL24",
"25: RSV_IRAP_VCL25",
"26: RSV_IRAP_VCL26",
"27: RSV_IRAP_VCL27",
"28: RSV_IRAP_VCL28",
"29: RSV_IRAP_VCL29",
"30: RSV_IRAP_VCL30",
"31: RSV_IRAP_VCL31",
"32: VPS(NON-VCL)",
"33: SPS(NON-VCL)",
"34: PPS(NON-VCL)",
"35: AUD_NUT(NON-VCL)",
"36: EOS_NUT(NON-VCL)",
"37: EOB_NUT(NON-VCL)",
"38: FD_NUT(NON-VCL)",
"39: SEI_PREFIX(NON-VCL)",
"40: SEI_SUFFIX(NON-VCL)",
"41: RSV_NVCL41",
"42: RSV_NVCL42",
"43: RSV_NVCL43",
"44: RSV_NVCL44",
"45: RSV_NVCL45",
"46: RSV_NVCL46",
"47: RSV_NVCL47",
"48: RSV_NVCL48",
"49: RSV_NVCL49",
"50: RSV_NVCL50",
"51: RSV_NVCL51",
"52: RSV_NVCL52",
"53: RSV_NVCL53",
"54: RSV_NVCL54",
"55: RSV_NVCL55",
"56: RSV_NVCL56",
"57: RSV_NVCL57",
"58: RSV_NVCL58",
"59: RSV_NVCL59",
"60: RSV_NVCL60",
"61: RSV_NVCL61",
"62: RSV_NVCL62",
"63: RSV_NVCL63",
"64: Undefied",
};
namespace android {
namespace emulation {
const std::string& HevcNaluParser::naluTypeToString(HevcNaluType n) {
uint8_t idx = static_cast<uint8_t>(n);
HEVC_WARN("%s", kNaluTypesStrings[idx].c_str());
return kNaluTypesStrings[idx];
}
bool HevcNaluParser::checkSpsFrame(const uint8_t* frame, size_t szBytes) {
HevcNaluParser::HevcNaluType currNaluType =
HevcNaluParser::getFrameNaluType(frame, szBytes, NULL);
if (currNaluType != HevcNaluParser::HevcNaluType::SPS) {
return false;
}
HEVC_INFO("found sps");
return true;
}
bool HevcNaluParser::checkIFrame(const uint8_t* frame, size_t szBytes) {
HevcNaluParser::HevcNaluType currNaluType =
HevcNaluParser::getFrameNaluType(frame, szBytes, NULL);
// TODO: is this true?
if (currNaluType != HevcNaluParser::HevcNaluType::IDR_W_RADL) {
return false;
}
HEVC_INFO("found i frame");
return true;
}
bool HevcNaluParser::checkPpsFrame(const uint8_t* frame, size_t szBytes) {
HevcNaluParser::HevcNaluType currNaluType =
HevcNaluParser::getFrameNaluType(frame, szBytes, NULL);
if (currNaluType != HevcNaluParser::HevcNaluType::PPS) {
return false;
}
HEVC_INFO("found pps");
return true;
}
bool HevcNaluParser::checkVpsFrame(const uint8_t* frame, size_t szBytes) {
HevcNaluParser::HevcNaluType currNaluType =
HevcNaluParser::getFrameNaluType(frame, szBytes, NULL);
if (currNaluType != HevcNaluParser::HevcNaluType::VPS) {
return false;
}
HEVC_INFO("found vps");
return true;
}
HevcNaluParser::HevcNaluType HevcNaluParser::getFrameNaluType(
const uint8_t* frame,
size_t szBytes,
uint8_t** data) {
if (szBytes < 4) {
HEVC_INFO("Not enough bytes for start code header and NALU type");
return HevcNaluType::Undefined;
}
if (frame[0] != 0 || frame[1] != 0) {
HEVC_INFO("First two bytes of start code header are not zero");
return HevcNaluType::Undefined;
}
// check for 4-byte start code header
if (frame[2] == 0 && frame[3] == 1) {
if (szBytes == 4) {
HEVC_INFO("Got start code header but no NALU type");
return HevcNaluType::Undefined;
}
const bool forbiddenBitIsInvalid = 0x80 & frame[4];
if (forbiddenBitIsInvalid) {
HEVC_INFO("Got start code header but no NALU type");
return HevcNaluType::Undefined;
}
// nalu type is the lower 6 bits after shiftting to right 1 bit
uint8_t naluType = 0x3f & (frame[4] >> 1);
// HEVC_INFO("frame[4]=0x%2x nalutype=0x%2x", frame[4],
// naluType);
if (data) {
*data = const_cast<uint8_t*>(&frame[4]);
}
return (naluType < static_cast<uint8_t>(HevcNaluType::Undefined))
? static_cast<HevcNaluType>(naluType)
: HevcNaluType::Undefined;
}
// check for three-byte start code header
if (frame[2] == 1) {
// nalu type is the lower 5 bits
uint8_t naluType = 0x3f & (frame[3] >> 1);
if (data) {
*data = const_cast<uint8_t*>(&frame[3]);
}
return (naluType < static_cast<uint8_t>(HevcNaluType::Undefined))
? static_cast<HevcNaluType>(naluType)
: HevcNaluType::Undefined;
}
HEVC_WARN("Frame did not have a start code header");
return HevcNaluType::Undefined;
}
const uint8_t* HevcNaluParser::getNextStartCodeHeader(const uint8_t* frame,
size_t szBytes) {
// // Start code can either be 0x000001 or 0x00000001. Beware of doing
// this comparison
// // by casting, as the frame may be in network-byte order (big endian).
// const uint8_t* res = nullptr;
// size_t idx = 0;
// const int kHeaderMinSize = 3;
// int64_t remaining = szBytes;
//
// while (remaining >= kHeaderMinSize) {
// if (frame[idx] != 0) {
// ++idx;
// --remaining;
// continue;
// }
//
// if (frame[idx + 1] != 0) {
// idx += 2;
// remaining -= 2;
// continue;
// }
//
// if (frame[idx + 2] == 1) {
// res = &frame[idx];
// break;
// }
//
// // check for four byte header if enough bytes left
// if (frame[idx + 2] == 0) {
// if (remaining >= 4 && frame[idx + 3] == 1) {
// res = &frame[idx];
// break;
// } else {
// idx += 4;
// remaining -= 4;
// continue;
// }
// } else {
// idx += 3;
// remaining -= 3;
// continue;
// }
// }
//
// return res;
// This implementation uses bit shifting
constexpr uint32_t startHeaderMask = 0x00000001;
uint32_t window = 0;
if (szBytes < 3) {
HEVC_INFO("Not enough bytes for a start code header");
return nullptr;
}
// we can't do a cast to 32-bit int because it is in network-byte format
// (big endian).
window |= (uint32_t)frame[0] << 16;
window |= (uint32_t)frame[1] << 8;
window |= frame[2];
if (szBytes == 3) {
if (!(window ^ startHeaderMask)) {
return &frame[0];
} else {
return nullptr;
}
}
size_t remaining = szBytes - 3;
while (remaining > 0) {
window = (window << 8) | (uint32_t)frame[szBytes - remaining];
if (!(window ^ startHeaderMask)) {
// four-byte header match
return &frame[szBytes - remaining - 3];
} else if (!((window & 0x00ffffff) ^ startHeaderMask)) {
// three-byte header match
return &frame[szBytes - remaining - 2];
}
--remaining;
}
return nullptr;
}
} // namespace emulation
} // namespace android