1 #include <fstream>
2 #include <gtest/gtest.h>
3 #include "codec_def.h"
4 #include "codec_app_def.h"
5 #include "utils/BufferedData.h"
6 #include "BaseThreadDecoderTest.h"
7
readBit(uint8_t * pBufPtr,int32_t & curBit)8 static int32_t readBit (uint8_t* pBufPtr, int32_t& curBit) {
9 int nIndex = curBit / 8;
10 int nOffset = curBit % 8 + 1;
11
12 curBit++;
13 return (pBufPtr[nIndex] >> (8 - nOffset)) & 0x01;
14 }
15
readBits(uint8_t * pBufPtr,int32_t & n,int32_t & curBit)16 static int32_t readBits (uint8_t* pBufPtr, int32_t& n, int32_t& curBit) {
17 int r = 0;
18 int i;
19 for (i = 0; i < n; i++) {
20 r |= (readBit (pBufPtr, curBit) << (n - i - 1));
21 }
22 return r;
23 }
24
bsGetUe(uint8_t * pBufPtr,int32_t & curBit)25 static int32_t bsGetUe (uint8_t* pBufPtr, int32_t& curBit) {
26 int r = 0;
27 int i = 0;
28 while ((readBit (pBufPtr, curBit) == 0) && (i < 32)) {
29 i++;
30 }
31 r = readBits (pBufPtr, i, curBit);
32 r += (1 << i) - 1;
33 return r;
34 }
35
readFirstMbInSlice(uint8_t * pSliceNalPtr)36 static int32_t readFirstMbInSlice (uint8_t* pSliceNalPtr) {
37 int32_t curBit = 0;
38 int32_t firstMBInSlice = bsGetUe (pSliceNalPtr + 1, curBit);
39 return firstMBInSlice;
40 }
41
ReadFrame(uint8_t * pBuf,const int32_t & iFileSize,const int32_t & bufPos)42 static int32_t ReadFrame (uint8_t* pBuf, const int32_t& iFileSize, const int32_t& bufPos) {
43 int32_t bytes_available = iFileSize - bufPos;
44 if (bytes_available < 4) {
45 return bytes_available;
46 }
47 uint8_t* ptr = pBuf + bufPos;
48 int32_t read_bytes = 0;
49 int32_t sps_count = 0;
50 int32_t pps_count = 0;
51 int32_t non_idr_pict_count = 0;
52 int32_t idr_pict_count = 0;
53 int32_t nal_deliminator = 0;
54 while (read_bytes < bytes_available - 4) {
55 bool has4ByteStartCode = ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 1;
56 bool has3ByteStartCode = false;
57 if (!has4ByteStartCode) {
58 has3ByteStartCode = ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 1;
59 }
60 if (has4ByteStartCode || has3ByteStartCode) {
61 int32_t byteOffset = has4ByteStartCode ? 4 : 3;
62 uint8_t nal_unit_type = has4ByteStartCode ? (ptr[4] & 0x1F) : (ptr[3] & 0x1F);
63 if (nal_unit_type == 1) {
64 int32_t firstMBInSlice = readFirstMbInSlice (ptr + byteOffset);
65 if (++non_idr_pict_count >= 1 && idr_pict_count >= 1 && firstMBInSlice == 0) {
66 return read_bytes;
67 }
68 if (non_idr_pict_count >= 2 && firstMBInSlice == 0) {
69 return read_bytes;
70 }
71 } else if (nal_unit_type == 5) {
72 int32_t firstMBInSlice = readFirstMbInSlice (ptr + byteOffset);
73 if (++idr_pict_count >= 1 && non_idr_pict_count >= 1 && firstMBInSlice == 0) {
74 return read_bytes;
75 }
76 if (idr_pict_count >= 2 && firstMBInSlice == 0) {
77 return read_bytes;
78 }
79 } else if (nal_unit_type == 7) {
80 if ((++sps_count >= 1) && (non_idr_pict_count >= 1 || idr_pict_count >= 1)) {
81 return read_bytes;
82 }
83 if (sps_count == 2) return read_bytes;
84 } else if (nal_unit_type == 8) {
85 if (++pps_count >= 1 && (non_idr_pict_count >= 1 || idr_pict_count >= 1)) return read_bytes;
86 } else if (nal_unit_type == 9) {
87 if (++nal_deliminator == 2) {
88 return read_bytes;
89 }
90 }
91 if (read_bytes >= bytes_available - 4) {
92 return bytes_available;
93 }
94 read_bytes += 4;
95 ptr += 4;
96 } else {
97 ++ptr;
98 ++read_bytes;
99 }
100 }
101 return bytes_available;
102 }
103
Write2File(FILE * pFp,unsigned char * pData[3],int iStride[2],int iWidth,int iHeight)104 static void Write2File (FILE* pFp, unsigned char* pData[3], int iStride[2], int iWidth, int iHeight) {
105 int i;
106 unsigned char* pPtr = NULL;
107
108 pPtr = pData[0];
109 for (i = 0; i < iHeight; i++) {
110 fwrite (pPtr, 1, iWidth, pFp);
111 pPtr += iStride[0];
112 }
113
114 iHeight = iHeight / 2;
115 iWidth = iWidth / 2;
116 pPtr = pData[1];
117 for (i = 0; i < iHeight; i++) {
118 fwrite (pPtr, 1, iWidth, pFp);
119 pPtr += iStride[1];
120 }
121
122 pPtr = pData[2];
123 for (i = 0; i < iHeight; i++) {
124 fwrite (pPtr, 1, iWidth, pFp);
125 pPtr += iStride[1];
126 }
127 }
128
Process(SBufferInfo * pInfo,FILE * pFp)129 static void Process (SBufferInfo* pInfo, FILE* pFp) {
130 if (pFp && pInfo->pDst[0] && pInfo->pDst[1] && pInfo->pDst[2] && pInfo) {
131 int iStride[2];
132 int iWidth = pInfo->UsrData.sSystemBuffer.iWidth;
133 int iHeight = pInfo->UsrData.sSystemBuffer.iHeight;
134 iStride[0] = pInfo->UsrData.sSystemBuffer.iStride[0];
135 iStride[1] = pInfo->UsrData.sSystemBuffer.iStride[1];
136
137 Write2File (pFp, (unsigned char**)pInfo->pDst, iStride, iWidth, iHeight);
138 }
139 }
140
BaseThreadDecoderTest()141 BaseThreadDecoderTest::BaseThreadDecoderTest()
142 : decoder_ (NULL), uiTimeStamp (0), pYuvFile (NULL), bEnableYuvDumpTest (false), decodeStatus_ (OpenFile) {
143 }
144
SetUp()145 int32_t BaseThreadDecoderTest::SetUp() {
146 long rv = WelsCreateDecoder (&decoder_);
147 EXPECT_EQ (0, rv);
148 EXPECT_TRUE (decoder_ != NULL);
149 if (decoder_ == NULL) {
150 return rv;
151 }
152
153 SDecodingParam decParam;
154 memset (&decParam, 0, sizeof (SDecodingParam));
155 decParam.uiTargetDqLayer = UCHAR_MAX;
156 decParam.eEcActiveIdc = ERROR_CON_SLICE_COPY;
157 decParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
158 int iThreadCount = (rand() % 2) + 2;
159 decoder_->SetOption (DECODER_OPTION_NUM_OF_THREADS, &iThreadCount);
160
161 rv = decoder_->Initialize (&decParam);
162 EXPECT_EQ (0, rv);
163 return (int32_t)rv;
164 }
165
TearDown()166 void BaseThreadDecoderTest::TearDown() {
167 if (decoder_ != NULL) {
168 decoder_->Uninitialize();
169 WelsDestroyDecoder (decoder_);
170 }
171 }
172
173
DecodeFrame(const uint8_t * src,size_t sliceSize,Callback * cbk)174 void BaseThreadDecoderTest::DecodeFrame (const uint8_t* src, size_t sliceSize, Callback* cbk) {
175 SBufferInfo bufInfo;
176 memset (pData, 0, sizeof (pData));
177 memset (&bufInfo, 0, sizeof (SBufferInfo));
178 bufInfo.uiInBsTimeStamp = ++uiTimeStamp;
179
180 DECODING_STATE rv = decoder_->DecodeFrameNoDelay (src, (int) sliceSize, pData, &bufInfo);
181 ASSERT_TRUE (rv == dsErrorFree);
182 sBufInfo = bufInfo;
183 if (sBufInfo.iBufferStatus == 1 && cbk != NULL) {
184 if (bEnableYuvDumpTest) {
185 Process (&sBufInfo, pYuvFile);
186 }
187 const Frame frame = {
188 {
189 // y plane
190 sBufInfo.pDst[0],
191 bufInfo.UsrData.sSystemBuffer.iWidth,
192 bufInfo.UsrData.sSystemBuffer.iHeight,
193 bufInfo.UsrData.sSystemBuffer.iStride[0]
194 },
195 {
196 // u plane
197 sBufInfo.pDst[1],
198 sBufInfo.UsrData.sSystemBuffer.iWidth / 2,
199 sBufInfo.UsrData.sSystemBuffer.iHeight / 2,
200 sBufInfo.UsrData.sSystemBuffer.iStride[1]
201 },
202 {
203 // v plane
204 sBufInfo.pDst[2],
205 sBufInfo.UsrData.sSystemBuffer.iWidth / 2,
206 sBufInfo.UsrData.sSystemBuffer.iHeight / 2,
207 sBufInfo.UsrData.sSystemBuffer.iStride[1]
208 },
209 };
210 cbk->onDecodeFrame (frame);
211 }
212 }
FlushFrame(Callback * cbk)213 void BaseThreadDecoderTest::FlushFrame (Callback* cbk) {
214 SBufferInfo bufInfo;
215 memset (pData, 0, sizeof (pData));
216 memset (&bufInfo, 0, sizeof (SBufferInfo));
217
218 DECODING_STATE rv = decoder_->FlushFrame (pData, &bufInfo);
219 ASSERT_TRUE (rv == dsErrorFree);
220 sBufInfo = bufInfo;
221 if (sBufInfo.iBufferStatus == 1 && cbk != NULL) {
222 if (bEnableYuvDumpTest) {
223 Process (&sBufInfo, pYuvFile);
224 }
225 const Frame frame = {
226 {
227 // y plane
228 sBufInfo.pDst[0],
229 sBufInfo.UsrData.sSystemBuffer.iWidth,
230 sBufInfo.UsrData.sSystemBuffer.iHeight,
231 sBufInfo.UsrData.sSystemBuffer.iStride[0]
232 },
233 {
234 // u plane
235 sBufInfo.pDst[1],
236 sBufInfo.UsrData.sSystemBuffer.iWidth / 2,
237 sBufInfo.UsrData.sSystemBuffer.iHeight / 2,
238 sBufInfo.UsrData.sSystemBuffer.iStride[1]
239 },
240 {
241 // v plane
242 sBufInfo.pDst[2],
243 sBufInfo.UsrData.sSystemBuffer.iWidth / 2,
244 sBufInfo.UsrData.sSystemBuffer.iHeight / 2,
245 sBufInfo.UsrData.sSystemBuffer.iStride[1]
246 },
247 };
248 cbk->onDecodeFrame (frame);
249 }
250 }
ThreadDecodeFile(const char * fileName,Callback * cbk)251 bool BaseThreadDecoderTest::ThreadDecodeFile (const char* fileName, Callback* cbk) {
252 std::ifstream file (fileName, std::ios::in | std::ios::binary);
253 if (!file.is_open())
254 return false;
255
256 BufferedData buf;
257 char b;
258 for (;;) {
259 file.read (&b, 1);
260 if (file.gcount() != 1) { // end of file
261 break;
262 }
263 if (!buf.PushBack (b)) {
264 std::cout << "unable to allocate memory" << std::endl;
265 return false;
266 }
267 }
268
269 std::string outFileName = std::string (fileName);
270 size_t pos = outFileName.find_last_of (".");
271 if (bEnableYuvDumpTest) {
272 outFileName = outFileName.substr (0, pos) + std::string (".yuv");
273 pYuvFile = fopen (outFileName.c_str(), "wb");
274 }
275
276 uiTimeStamp = 0;
277 memset (&sBufInfo, 0, sizeof (SBufferInfo));
278 int32_t bufPos = 0;
279 int32_t bytesConsumed = 0;
280 int32_t fileSize = (int32_t)buf.Length();
281 while (bytesConsumed < fileSize) {
282 int32_t frameSize = ReadFrame (buf.data(), fileSize, bufPos);
283 if (::testing::Test::HasFatalFailure()) {
284 return false;
285 }
286 uint8_t* frame_ptr = buf.data() + bufPos;
287 DecodeFrame (frame_ptr, frameSize, cbk);
288 if (::testing::Test::HasFatalFailure()) {
289 return false;
290 }
291 bufPos += frameSize;
292 bytesConsumed += frameSize;
293 }
294
295 int32_t iEndOfStreamFlag = 1;
296 decoder_->SetOption (DECODER_OPTION_END_OF_STREAM, &iEndOfStreamFlag);
297
298 // Flush out last frames in decoder buffer
299 int32_t num_of_frames_in_buffer = 0;
300 decoder_->GetOption (DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER, &num_of_frames_in_buffer);
301 for (int32_t i = 0; i < num_of_frames_in_buffer; ++i) {
302 FlushFrame (cbk);
303 }
304 if (bEnableYuvDumpTest) {
305 fclose (pYuvFile);
306 }
307 return true;
308 }
309
Open(const char * fileName)310 bool BaseThreadDecoderTest::Open (const char* fileName) {
311 if (decodeStatus_ == OpenFile) {
312 file_.open (fileName, std::ios_base::out | std::ios_base::binary);
313 if (file_.is_open()) {
314 decodeStatus_ = Decoding;
315 return true;
316 }
317 }
318 return false;
319 }
320