1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2024 The Khronos Group Inc.
6 * Copyright (c) 2024 Igalia S.L
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 */
21 #include "deMemory.h"
22
23 #include "vktVideoTestUtils.hpp"
24 #include "vktDemuxer.hpp"
25
26 #include <algorithm>
27 #include <limits>
28
29 namespace vkt
30 {
31 namespace video
32 {
33
create(Params && params)34 std::shared_ptr<Demuxer> Demuxer::create(Params &¶ms)
35 {
36 switch (params.codecOperation)
37 {
38 case vk::VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
39 case vk::VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
40 {
41 return std::make_shared<H26XAnnexBDemuxer>(std::move(params));
42 }
43 break;
44 case vk::VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
45 {
46 if (params.framing == ElementaryStreamFraming::AV1_ANNEXB)
47 return std::make_shared<AV1AnnexBDemuxer>(std::move(params));
48 else if (params.framing == ElementaryStreamFraming::IVF)
49 return std::make_shared<DuckIVFDemuxer>(std::move(params));
50 else
51 TCU_THROW(InternalError, "unknown elementary stream framing");
52 }
53 break;
54 default:
55 TCU_THROW(InternalError, "Unknown codec operation");
56 }
57 }
58
Demuxer(Params && params)59 Demuxer::Demuxer(Params &¶ms) : m_params(std::move(params))
60 {
61 }
62
H26XAnnexBDemuxer(Params && params)63 H26XAnnexBDemuxer::H26XAnnexBDemuxer(Params &¶ms) : Demuxer(std::move(params))
64 {
65 readToNextStartCode(nullptr);
66 }
67
nextPacket()68 std::vector<uint8_t> H26XAnnexBDemuxer::nextPacket()
69 {
70 std::vector<uint8_t> packet;
71 readToNextStartCode(&packet);
72 return packet;
73 }
74
75 // Very inefficient but simple algorithm, which is fine for the CTS
76 // since it can expect never to have to deal with inputs larger than a
77 // couple of megabytes. A ~20x time boost would be mapping the
78 // bitstreams into memory and using the std::boyer_moore algorithm to
79 // find the start codes, at the cost of extra complexity handling
80 // corner cases in file mapping and low-memory environments.
readToNextStartCode(std::vector<uint8_t> * payload)81 void H26XAnnexBDemuxer::readToNextStartCode(std::vector<uint8_t> *payload)
82 {
83 int zeroRunCount = 0;
84 auto &reader = m_params.data;
85 uint8_t byte;
86
87 if (reader->isEof() || reader->isError())
88 return;
89
90 if (payload)
91 {
92 // TODO: Fix the sample parser's interface
93 payload->push_back(0x0);
94 payload->push_back(0x0);
95 payload->push_back(0x0);
96 payload->push_back(0x1);
97 }
98
99 while (true)
100 {
101 byte = reader->readByteChecked("failure looking for H26X start code");
102 if (reader->isEof() || reader->isError())
103 return;
104
105 if (byte == 0x01 && zeroRunCount >= 3)
106 {
107 return;
108 }
109 else if (byte == 0x00)
110 {
111 zeroRunCount++;
112 }
113 else
114 {
115 while (zeroRunCount > 0)
116 {
117 zeroRunCount--;
118 payload->push_back(0x0);
119 }
120 payload->push_back(byte);
121 }
122 }
123 }
124
DuckIVFDemuxer(Params && params)125 DuckIVFDemuxer::DuckIVFDemuxer(Params &¶ms) : Demuxer(std::move(params)), m_frameNumber(0)
126 {
127 readHeader();
128 }
129
130 DE_PACKED(DuckIVFFrameHeader {
131 uint32_t sizeOfFrame; // bytes 0-3 size of frame in bytes (not including the 12-byte header)
132 uint64_t presentationTimestamp; // bytes 4-11 64-bit presentation timestamp
133 } DuckIVFFrameHeader);
134
nextPacket()135 std::vector<uint8_t> DuckIVFDemuxer::nextPacket()
136 {
137 auto &reader = m_params.data;
138
139 DuckIVFFrameHeader frameHdr;
140 reader->readChecked((uint8_t *)&frameHdr, sizeof(DuckIVFFrameHeader), "error reading Duck IVF frame header");
141
142 std::vector<uint8_t> packet(frameHdr.sizeOfFrame);
143 reader->readChecked(packet.data(), frameHdr.sizeOfFrame, "error reading Duck IVF frame");
144
145 m_frameNumber++;
146
147 DE_ASSERT(packet.size() > 0);
148
149 return packet;
150 }
151
readHeader()152 void DuckIVFDemuxer::readHeader()
153 {
154 auto &reader = m_params.data;
155
156 DE_ASSERT(!reader->isError() && !reader->isEof());
157
158 reader->readChecked((uint8_t *)&m_hdr, sizeof(DuckIVFDemuxer::Header), "invalid Duck IVF header");
159
160 static uint8_t kDuckIVFSignature[4] = {'D', 'K', 'I', 'F'};
161 if (deMemCmp(&m_hdr.signature, kDuckIVFSignature, sizeof(kDuckIVFSignature)) != 0)
162 TCU_THROW(InternalError, "invalid Duck IVF signature");
163
164 m_numFrames = m_hdr.framesInFile;
165 }
166
AV1AnnexBDemuxer(Params && params)167 AV1AnnexBDemuxer::AV1AnnexBDemuxer(Params &¶ms) : Demuxer(std::move(params))
168 {
169 }
170
nextPacket()171 std::vector<uint8_t> AV1AnnexBDemuxer::nextPacket()
172 {
173 std::vector<uint8_t> packet;
174 auto &reader = m_params.data;
175
176 DE_ASSERT(!reader->isError());
177 if (reader->isEof())
178 return packet;
179
180 if (m_remainingBytesInTemporalUnit == 0)
181 {
182 uint32_t tuUlebSize = 0;
183 uint32_t tuSize = getUleb128(&tuUlebSize);
184 m_remainingBytesInTemporalUnit = tuSize;
185 }
186
187 uint32_t frameUlebSize = 0;
188 uint32_t frameSize = getUleb128(&frameUlebSize);
189
190 packet.resize(frameSize);
191 reader->read(packet);
192
193 DE_ASSERT((frameSize + frameUlebSize) <= m_remainingBytesInTemporalUnit);
194 m_remainingBytesInTemporalUnit -= (frameSize + frameUlebSize);
195
196 m_frameNumber++;
197
198 return packet;
199 }
200
getUleb128(uint32_t * numBytes)201 uint32_t AV1AnnexBDemuxer::getUleb128(uint32_t *numBytes)
202 {
203 auto &in = m_params.data;
204 uint64_t val = 0;
205 uint32_t i = 0;
206 uint32_t more = 0;
207 uint32_t bytesRead = 0;
208
209 do
210 {
211 const int v = in->readByteChecked("error reading uleb128 value");
212 more = v & 0x80;
213 val |= ((uint64_t)(v & 0x7F)) << i;
214 bytesRead += 1;
215 i += 7;
216 } while (more && i < 56);
217
218 if (val > std::numeric_limits<uint32_t>::max() || more)
219 return 0;
220
221 if (numBytes)
222 *numBytes = bytesRead;
223
224 return (uint32_t)val;
225 }
226
227 } // namespace video
228 } // namespace vkt
229