blob: 7d708a62584ee9214e43acc9f3921de5d3df5c34 [file] [log] [blame]
// Copyright (C) 2019 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 "host-common/H264NaluParser.h"
#define H264_DEBUG 0
#if H264_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 H264_PRINT(color,fmt,...) fprintf(stderr, color "H264NaluParser: %s:%d " fmt "\n" RESET, __func__, __LINE__, ##__VA_ARGS__);
#else
#define H264_PRINT(fmt,...)
#endif
#define H264_INFO(fmt,...) H264_PRINT(RESET, fmt, ##__VA_ARGS__);
#define H264_WARN(fmt,...) H264_PRINT(YEL, fmt, ##__VA_ARGS__);
#define H264_ERROR(fmt,...) H264_PRINT(RED, fmt, ##__VA_ARGS__);
static const std::string kNaluTypesStrings[] = {
"0: Unspecified (non-VCL)",
"1: Coded slice of a non-IDR picture (VCL)", // P frame
"2: Coded slice data partition A (VCL)",
"3: Coded slice data partition B (VCL)",
"4: Coded slice data partition C (VCL)",
"5: Coded slice of an IDR picture (VCL)", // I frame
"6: Supplemental enhancement information (SEI) (non-VCL)",
"7: Sequence parameter set (non-VCL)", // SPS parameter
"8: Picture parameter set (non-VCL)", // PPS parameter
"9: Access unit delimiter (non-VCL)",
"10: End of sequence (non-VCL)",
"11: End of stream (non-VCL)",
"12: Filler data (non-VCL)",
"13: Sequence parameter set extension (non-VCL)",
"14: Prefix NAL unit (non-VCL)",
"15: Subset sequence parameter set (non-VCL)",
"16: Reserved (non-VCL)",
"17: Reserved (non-VCL)",
"18: Reserved (non-VCL)",
"19: Coded slice of an auxiliary coded picture without partitioning "
"(non-VCL)",
"20: Coded slice extension (non-VCL)",
"21: Coded slice extension for depth view components (non-VCL)",
"22: Reserved (non-VCL)",
"23: Reserved (non-VCL)",
"24: STAP-A Single-time aggregation packet (non-VCL)",
"25: STAP-B Single-time aggregation packet (non-VCL)",
"26: MTAP16 Multi-time aggregation packet (non-VCL)",
"27: MTAP24 Multi-time aggregation packet (non-VCL)",
"28: FU-A Fragmentation unit (non-VCL)",
"29: FU-B Fragmentation unit (non-VCL)",
"30: Unspecified (non-VCL)",
"31: Unspecified (non-VCL)",
};
namespace android {
namespace emulation {
const std::string& H264NaluParser::naluTypeToString(H264NaluType n) {
uint8_t idx = static_cast<uint8_t>(n);
H264_WARN("%s", kNaluTypesStrings[idx].c_str());
return kNaluTypesStrings[idx];
}
bool H264NaluParser::checkSpsFrame(const uint8_t* frame, size_t szBytes) {
H264NaluParser::H264NaluType currNaluType =
H264NaluParser::getFrameNaluType(frame, szBytes, NULL);
if (currNaluType != H264NaluParser::H264NaluType::SPS) {
return false;
}
H264_INFO("found sps");
return true;
}
bool H264NaluParser::checkIFrame(const uint8_t* frame, size_t szBytes) {
H264NaluParser::H264NaluType currNaluType =
H264NaluParser::getFrameNaluType(frame, szBytes, NULL);
if (currNaluType != H264NaluParser::H264NaluType::CodedSliceIDR) {
return false;
}
H264_INFO("found i frame");
return true;
}
bool H264NaluParser::checkPpsFrame(const uint8_t* frame, size_t szBytes) {
H264NaluParser::H264NaluType currNaluType =
H264NaluParser::getFrameNaluType(frame, szBytes, NULL);
if (currNaluType != H264NaluParser::H264NaluType::PPS) {
return false;
}
H264_INFO("found pps");
return true;
}
H264NaluParser::H264NaluType H264NaluParser::getFrameNaluType(const uint8_t* frame, size_t szBytes, uint8_t** data) {
if (szBytes < 4) {
H264_INFO("Not enough bytes for start code header and NALU type");
return H264NaluType::Undefined;
}
if (frame[0] != 0 || frame[1] != 0) {
H264_INFO("First two bytes of start code header are not zero");
return H264NaluType::Undefined;
}
// check for 4-byte start code header
if (frame[2] == 0 && frame[3] == 1) {
if (szBytes == 4) {
H264_INFO("Got start code header but no NALU type");
return H264NaluType::Undefined;
}
// nalu type is the lower 5 bits
uint8_t naluType = 0x1f & frame[4];
// H264_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>(H264NaluType::Undefined)) ?
static_cast<H264NaluType>(naluType) :
H264NaluType::Undefined;
}
// check for three-byte start code header
if (frame[2] == 1) {
// nalu type is the lower 5 bits
uint8_t naluType = 0x1f & frame[3];
if (data) {
*data = const_cast<uint8_t*>(&frame[3]);
}
return (naluType < static_cast<uint8_t>(H264NaluType::Undefined)) ?
static_cast<H264NaluType>(naluType) :
H264NaluType::Undefined;
}
H264_WARN("Frame did not have a start code header");
return H264NaluType::Undefined;
}
const uint8_t* H264NaluParser::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) {
H264_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