You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
8.0 KiB
243 lines
8.0 KiB
// 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__);
|
|
|
|
namespace android {
|
|
namespace emulation {
|
|
|
|
const std::string H264NaluParser::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)",
|
|
};
|
|
|
|
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
|