aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorquietvoid <39477805+quietvoid@users.noreply.github.com>2024-01-27 07:16:31 -0500
committerquietvoid <39477805+quietvoid@users.noreply.github.com>2024-01-28 06:27:26 -0500
commita8f286cb6e6fb7c5d74983a13c095f8faac489d1 (patch)
tree64c1dca6624a88377f9f5464fd904de300548dd0
parente2c54e7fa945276c7d36a7081bbb92b3f3447328 (diff)
Utils: Add HEVC SEI parser
-rw-r--r--xbmc/utils/BitstreamReader.cpp4
-rw-r--r--xbmc/utils/BitstreamReader.h3
-rw-r--r--xbmc/utils/CMakeLists.txt2
-rw-r--r--xbmc/utils/HevcSei.cpp122
-rw-r--r--xbmc/utils/HevcSei.h53
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);
+};