// 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(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(&frame[4]); } return (naluType < static_cast(H264NaluType::Undefined)) ? static_cast(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(&frame[3]); } return (naluType < static_cast(H264NaluType::Undefined)) ? static_cast(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