|
| 1 | +/** |
| 2 | + * @file pes.cpp |
| 3 | + * PES packet parser implementation |
| 4 | + * |
| 5 | + * @license{AGPLv3 |
| 6 | + * |
| 7 | + * This program is free software: you can redistribute it and/or modify |
| 8 | + * it under the terms of the GNU Affero General Public License as |
| 9 | + * published by the Free Software Foundation, either version 3 of the |
| 10 | + * License. |
| 11 | + * |
| 12 | + * This program is distributed in the hope that it will be useful, |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | + * GNU Affero General Public License for more details.} |
| 16 | + */ |
| 17 | + |
| 18 | +#include "pes.h" |
| 19 | + |
| 20 | +#include "vdr/remux.h" |
| 21 | + |
| 22 | +extern "C" |
| 23 | +{ |
| 24 | +#include <libavutil/avutil.h> |
| 25 | +} |
| 26 | + |
| 27 | +/** |
| 28 | + * Construct a PES packet parser |
| 29 | + * |
| 30 | + * @param data Pointer to the raw PES packet data |
| 31 | + * @param size Size of the PES packet in bytes |
| 32 | + */ |
| 33 | +cPes::cPes(const uint8_t *data, int size) |
| 34 | + : m_data(data), m_size(size) |
| 35 | +{ |
| 36 | +} |
| 37 | + |
| 38 | +/** |
| 39 | + * Parse the PES packet to detect the video codec |
| 40 | + * |
| 41 | + * Analyzes the PES payload to identify the codec type (MPEG2, H.264, or HEVC). |
| 42 | + * This method looks for codec-specific start codes and stream type identifiers |
| 43 | + * in the payload. The detected codec can be retrieved using GetCodec(). |
| 44 | + * |
| 45 | + * Supported codecs: |
| 46 | + * - MPEG2 (AV_CODEC_ID_MPEG2VIDEO) |
| 47 | + * - H.264 (AV_CODEC_ID_H264) |
| 48 | + * - HEVC (AV_CODEC_ID_HEVC) |
| 49 | + * |
| 50 | + * @note This method must be called before GetCodec() |
| 51 | + */ |
| 52 | +void cPes::Parse() { |
| 53 | + int pesPayloadStart = PesPayloadOffset(m_data); |
| 54 | + |
| 55 | + if (pesPayloadStart + START_CODE_PREFIX_LEN + 1 >= (unsigned int)m_size) |
| 56 | + return; |
| 57 | + |
| 58 | + uint32_t firstThreePesPayloadBytes = ReadBytes(pesPayloadStart, START_CODE_PREFIX_LEN); |
| 59 | + const uint8_t *codecPayload = &m_data[pesPayloadStart + START_CODE_PREFIX_LEN]; |
| 60 | + |
| 61 | + // Looking for the MPEG2 start code and stream type |
| 62 | + if (firstThreePesPayloadBytes == START_CODE_PREFIX && codecPayload[0] == MPEG2_STREAM_TYPE) |
| 63 | + m_codec = AV_CODEC_ID_MPEG2VIDEO; |
| 64 | + // Looking for the H.264/HEVC start code. It can have an optional leading zero byte. |
| 65 | + else if (ReadBytes(pesPayloadStart + 1, START_CODE_PREFIX_LEN) == START_CODE_PREFIX) { |
| 66 | + codecPayload++; |
| 67 | + m_payloadHasLeadingZero = true; |
| 68 | + } else if (firstThreePesPayloadBytes != START_CODE_PREFIX) |
| 69 | + return; // no start code found |
| 70 | + |
| 71 | + if (m_size > &codecPayload[7] - m_data) { |
| 72 | + if ( codecPayload[0] == H264_STREAM_TYPE && (codecPayload[1] == 0x10 || codecPayload[1] == 0xF0 || codecPayload[7] == 0x64)) |
| 73 | + m_codec = AV_CODEC_ID_H264; |
| 74 | + else if (codecPayload[0] == HEVC_STREAM_TYPE && (codecPayload[1] == 0x10 || codecPayload[1] == 0x50 || codecPayload[7] == 0x40)) |
| 75 | + m_codec = AV_CODEC_ID_HEVC; |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +uint32_t cPes::ReadBytes(int offset, int count) |
| 80 | +{ |
| 81 | + uint32_t value = 0; |
| 82 | + |
| 83 | + for (int i = 0; i < count; i++) { |
| 84 | + value <<= 8; |
| 85 | + value |= m_data[offset + i]; |
| 86 | + } |
| 87 | + |
| 88 | + return value; |
| 89 | +} |
| 90 | + |
| 91 | +/** |
| 92 | + * Check if the PES header is valid |
| 93 | + * |
| 94 | + * Validates that the PES packet has a valid header by checking: |
| 95 | + * - The packet is long enough to contain a header |
| 96 | + * - The start code prefix (0x000001) is present |
| 97 | + * |
| 98 | + * @return true if the header is valid, false otherwise |
| 99 | + */ |
| 100 | +bool cPes::IsHeaderValid() |
| 101 | +{ |
| 102 | + return PesLongEnough(m_size) && ReadBytes(0, 3) == START_CODE_PREFIX; |
| 103 | +} |
| 104 | + |
| 105 | +/** |
| 106 | + * Check if this is a video stream |
| 107 | + * |
| 108 | + * Determines if the PES packet contains a video stream based on the stream ID. |
| 109 | + * Video streams have stream IDs in the range 0xE0-0xEF according to |
| 110 | + * H.222.0 03/2017 Table 2-22. |
| 111 | + * |
| 112 | + * @return true if this is a video stream, false otherwise |
| 113 | + */ |
| 114 | +bool cPes::IsVideoStream() |
| 115 | +{ |
| 116 | + // The low nibble is the stream number |
| 117 | + return (GetStreamId() & 0xF0) == 0xE0; |
| 118 | +} |
| 119 | + |
| 120 | +/** |
| 121 | + * Check if this is an audio stream |
| 122 | + * |
| 123 | + * Determines if the PES packet contains an audio stream based on the stream ID. |
| 124 | + * Audio streams have stream IDs in the range 0xC0-0xCF according to |
| 125 | + * H.222.0 03/2017 Table 2-22. |
| 126 | + * |
| 127 | + * @return true if this is an audio stream, false otherwise |
| 128 | + */ |
| 129 | +bool cPes::IsAudioStream() |
| 130 | +{ |
| 131 | + // The low nibble is the stream number |
| 132 | + return (GetStreamId() & 0xF0) == 0xC0; |
| 133 | +} |
| 134 | + |
| 135 | +/** |
| 136 | + * Get the Presentation Time Stamp (PTS) from the PES header |
| 137 | + * |
| 138 | + * Extracts the PTS value from the PES packet header if present. |
| 139 | + * The PTS indicates when the decoded content should be presented. |
| 140 | + * |
| 141 | + * @return The PTS value in 90 kHz units, or AV_NOPTS_VALUE if no PTS is present |
| 142 | + */ |
| 143 | +int64_t cPes::GetPts() |
| 144 | +{ |
| 145 | + if (!PesHasPts(m_data)) |
| 146 | + return AV_NOPTS_VALUE; |
| 147 | + |
| 148 | + return PesGetPts(m_data); |
| 149 | +} |
| 150 | + |
| 151 | +/** |
| 152 | + * Get a pointer to the PES payload data |
| 153 | + * |
| 154 | + * Returns a pointer to the start of the payload data, skipping the PES header. |
| 155 | + * For H.264/HEVC streams with a leading zero byte, the leading zero is also skipped. |
| 156 | + * |
| 157 | + * @return Pointer to the payload data |
| 158 | + */ |
| 159 | +const uint8_t *cPes::GetPayload() |
| 160 | +{ |
| 161 | + int headerLen = PesPayloadOffset(m_data); |
| 162 | + |
| 163 | + if (m_payloadHasLeadingZero) |
| 164 | + headerLen++; // Skip the leading zero byte for H.264/HEVC |
| 165 | + |
| 166 | + return &m_data[headerLen]; |
| 167 | +} |
| 168 | + |
| 169 | +/** |
| 170 | + * Get the size of the PES payload |
| 171 | + * |
| 172 | + * Calculates the size of the payload by subtracting the header size |
| 173 | + * (and optional leading zero for H.264/HEVC) from the total packet size. |
| 174 | + * |
| 175 | + * @return Size of the payload in bytes |
| 176 | + */ |
| 177 | +int cPes::GetPayloadSize() |
| 178 | +{ |
| 179 | + return m_size - (GetPayload() - m_data); |
| 180 | +} |
0 commit comments