diff options
author | quietvoid <39477805+quietvoid@users.noreply.github.com> | 2024-01-27 07:16:31 -0500 |
---|---|---|
committer | quietvoid <39477805+quietvoid@users.noreply.github.com> | 2024-01-28 06:27:26 -0500 |
commit | a8f286cb6e6fb7c5d74983a13c095f8faac489d1 (patch) | |
tree | 64c1dca6624a88377f9f5464fd904de300548dd0 | |
parent | e2c54e7fa945276c7d36a7081bbb92b3f3447328 (diff) |
Utils: Add HEVC SEI parser
-rw-r--r-- | xbmc/utils/BitstreamReader.cpp | 4 | ||||
-rw-r--r-- | xbmc/utils/BitstreamReader.h | 3 | ||||
-rw-r--r-- | xbmc/utils/CMakeLists.txt | 2 | ||||
-rw-r--r-- | xbmc/utils/HevcSei.cpp | 122 | ||||
-rw-r--r-- | xbmc/utils/HevcSei.h | 53 |
5 files changed, 184 insertions, 0 deletions
diff --git a/xbmc/utils/BitstreamReader.cpp b/xbmc/utils/BitstreamReader.cpp index 8900a40a15..1ec8a11057 100644 --- a/xbmc/utils/BitstreamReader.cpp +++ b/xbmc/utils/BitstreamReader.cpp @@ -21,6 +21,8 @@ uint32_t CBitstreamReader::ReadBits(int nbits) buffer += offbits / 8; offbits %= 8; + m_posBits += nbits; + return ret; } @@ -30,6 +32,8 @@ void CBitstreamReader::SkipBits(int nbits) buffer += offbits / 8; offbits %= 8; + m_posBits += nbits; + if (buffer > (start + length)) oflow = 1; } diff --git a/xbmc/utils/BitstreamReader.h b/xbmc/utils/BitstreamReader.h index 5d818d3767..8ff803b8b4 100644 --- a/xbmc/utils/BitstreamReader.h +++ b/xbmc/utils/BitstreamReader.h @@ -17,10 +17,13 @@ public: uint32_t ReadBits(int nbits); void SkipBits(int nbits); uint32_t GetBits(int nbits); + unsigned int Position() { return m_posBits; } + unsigned int AvailableBits() { return length * 8 - m_posBits; } private: const uint8_t *buffer, *start; int offbits = 0, length, oflow = 0; + int m_posBits{0}; }; const uint8_t* find_start_code(const uint8_t *p, const uint8_t *end, uint32_t *state); diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt index 68c5c666af..87429d34c3 100644 --- a/xbmc/utils/CMakeLists.txt +++ b/xbmc/utils/CMakeLists.txt @@ -29,6 +29,7 @@ set(SOURCES ActorProtocol.cpp FontUtils.cpp GpuInfo.cpp GroupUtils.cpp + HevcSei.cpp HTMLUtil.cpp HttpHeader.cpp HttpParser.cpp @@ -115,6 +116,7 @@ set(HEADERS ActorProtocol.h GpuInfo.h GroupUtils.h HDRCapabilities.h + HevcSei.h HTMLUtil.h HttpHeader.h HttpParser.h diff --git a/xbmc/utils/HevcSei.cpp b/xbmc/utils/HevcSei.cpp new file mode 100644 index 0000000000..03cfc14916 --- /dev/null +++ b/xbmc/utils/HevcSei.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "HevcSei.h" + +void HevcAddStartCodeEmulationPrevention3Byte(std::vector<uint8_t>& buf) +{ + size_t i = 0; + + while (i < buf.size()) + { + if (i > 2 && buf[i - 2] == 0 && buf[i - 1] == 0 && buf[i] <= 3) + buf.insert(buf.begin() + i, 3); + + i += 1; + } +} + +void HevcClearStartCodeEmulationPrevention3Byte(const uint8_t* buf, + const size_t len, + std::vector<uint8_t>& out) +{ + size_t i = 0; + + if (len > 2) + { + out.reserve(len); + + out.emplace_back(buf[0]); + out.emplace_back(buf[1]); + + for (i = 2; i < len; i++) + { + if (!(buf[i - 2] == 0 && buf[i - 1] == 0 && buf[i] == 3)) + out.emplace_back(buf[i]); + } + } + else + { + out.assign(buf, buf + len); + } +} + +int CHevcSei::ParseSeiMessage(CBitstreamReader& br, std::vector<CHevcSei>& messages) +{ + CHevcSei sei; + uint8_t lastPayloadTypeByte{0}; + uint8_t lastPayloadSizeByte{0}; + + sei.m_msgOffset = br.Position() / 8; + + lastPayloadTypeByte = br.ReadBits(8); + while (lastPayloadTypeByte == 0xFF) + { + lastPayloadTypeByte = br.ReadBits(8); + sei.m_payloadType += 255; + } + + sei.m_payloadType += lastPayloadTypeByte; + + lastPayloadSizeByte = br.ReadBits(8); + while (lastPayloadSizeByte == 0xFF) + { + lastPayloadSizeByte = br.ReadBits(8); + sei.m_payloadSize += 255; + } + + sei.m_payloadSize += lastPayloadSizeByte; + sei.m_payloadOffset = br.Position() / 8; + + // Invalid size + if (sei.m_payloadSize > br.AvailableBits()) + return 1; + + br.SkipBits(sei.m_payloadSize * 8); + messages.emplace_back(sei); + + return 0; +} + +std::vector<CHevcSei> CHevcSei::ParseSeiRbspInternal(const uint8_t* buf, const size_t len) +{ + std::vector<CHevcSei> messages; + + if (len > 4) + { + CBitstreamReader br(buf, len); + + // forbidden_zero_bit, nal_type, nuh_layer_id, temporal_id + // nal_type == SEI_PREFIX should already be verified by caller + br.SkipBits(16); + + while (true) + { + if (ParseSeiMessage(br, messages)) + break; + + if (br.AvailableBits() <= 8) + break; + } + } + + return messages; +} + +std::vector<CHevcSei> CHevcSei::ParseSeiRbsp(const uint8_t* buf, const size_t len) +{ + return ParseSeiRbspInternal(buf, len); +} + +std::vector<CHevcSei> CHevcSei::ParseSeiRbspUnclearedEmulation(const uint8_t* inData, + const size_t inDataLen, + std::vector<uint8_t>& buf) +{ + HevcClearStartCodeEmulationPrevention3Byte(inData, inDataLen, buf); + return ParseSeiRbsp(buf.data(), buf.size()); +} diff --git a/xbmc/utils/HevcSei.h b/xbmc/utils/HevcSei.h new file mode 100644 index 0000000000..08943afd54 --- /dev/null +++ b/xbmc/utils/HevcSei.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "BitstreamReader.h" + +#include <optional> +#include <vector> + +/*! + * \brief Parses HEVC SEI messages for supplemental video information. + * + * The CHevcSei class is used to interpret and handle Supplemental Enhancement + * Information (SEI) messages found in High Efficiency Video Coding (HEVC) + * bitstreams. It is particularly useful for extracting HDR10+ metadata and + * other types of supplemental data from HEVC encoded video streams. + * + * \note This class deals with SEI messages in HEVC streams and does not + * process the video content itself. + */ +class CHevcSei +{ +public: + CHevcSei() = default; + ~CHevcSei() = default; + + uint8_t m_payloadType{0}; + size_t m_payloadSize{0}; + + // In relation to the input SEI rbsp payload + size_t m_msgOffset{0}; + size_t m_payloadOffset{0}; + + // Parses SEI payload assumed to not have emulation prevention 3 bytes + static std::vector<CHevcSei> ParseSeiRbsp(const uint8_t* buf, const size_t len); + + // Clears emulation prevention 3 bytes and fills in the passed buf + static std::vector<CHevcSei> ParseSeiRbspUnclearedEmulation(const uint8_t* inData, + const size_t inDataLen, + std::vector<uint8_t>& buf); + +private: + // Parses single SEI message from the reader and pushes it to the list + static int ParseSeiMessage(CBitstreamReader& br, std::vector<CHevcSei>& messages); + + static std::vector<CHevcSei> ParseSeiRbspInternal(const uint8_t* buf, const size_t len); +}; |