/work/openh264/codec/decoder/plus/src/welsDecoderExt.cpp
Line | Count | Source |
1 | | /*! |
2 | | * \copy |
3 | | * Copyright (c) 2009-2013, Cisco Systems |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions |
8 | | * are met: |
9 | | * |
10 | | * * Redistributions of source code must retain the above copyright |
11 | | * notice, this list of conditions and the following disclaimer. |
12 | | * |
13 | | * * Redistributions in binary form must reproduce the above copyright |
14 | | * notice, this list of conditions and the following disclaimer in |
15 | | * the documentation and/or other materials provided with the |
16 | | * distribution. |
17 | | * |
18 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
21 | | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
22 | | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
23 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
24 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
25 | | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
26 | | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
28 | | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | | * POSSIBILITY OF SUCH DAMAGE. |
30 | | * |
31 | | * |
32 | | * welsDecoderExt.cpp |
33 | | * |
34 | | * Abstract |
35 | | * Cisco OpenH264 decoder extension utilization |
36 | | * |
37 | | * History |
38 | | * 3/12/2009 Created |
39 | | * |
40 | | * |
41 | | ************************************************************************/ |
42 | | //#include <assert.h> |
43 | | #include "welsDecoderExt.h" |
44 | | #include "welsCodecTrace.h" |
45 | | #include "codec_def.h" |
46 | | #include "typedefs.h" |
47 | | #include "memory_align.h" |
48 | | #include "utils.h" |
49 | | #include "version.h" |
50 | | |
51 | | //#include "macros.h" |
52 | | #include "decoder.h" |
53 | | #include "decoder_core.h" |
54 | | #include "manage_dec_ref.h" |
55 | | #include "error_concealment.h" |
56 | | |
57 | | #include "measure_time.h" |
58 | | extern "C" { |
59 | | #include "decoder_core.h" |
60 | | #include "manage_dec_ref.h" |
61 | | } |
62 | | #include "error_code.h" |
63 | | #include "crt_util_safe_x.h" // Safe CRT routines like util for cross platforms |
64 | | #include <time.h> |
65 | | #if defined(_WIN32) /*&& defined(_DEBUG)*/ |
66 | | |
67 | | #include <windows.h> |
68 | | #include <stdio.h> |
69 | | #include <stdarg.h> |
70 | | #include <sys/types.h> |
71 | | #include <malloc.h> |
72 | | #else |
73 | | #include <sys/time.h> |
74 | | #endif |
75 | | |
76 | | namespace WelsDec { |
77 | | |
78 | | ////////////////////////////////////////////////////////////////////// |
79 | | // Construction/Destruction |
80 | | ////////////////////////////////////////////////////////////////////// |
81 | | |
82 | | /*************************************************************************** |
83 | | * Description: |
84 | | * class CWelsDecoder constructor function, do initialization and |
85 | | * alloc memory required |
86 | | * |
87 | | * Input parameters: none |
88 | | * |
89 | | * return: none |
90 | | ***************************************************************************/ |
91 | 0 | DECLARE_PROCTHREAD (pThrProcInit, p) { |
92 | 0 | SWelsDecThreadInfo* sThreadInfo = (SWelsDecThreadInfo*)p; |
93 | | #if defined(WIN32) |
94 | | _alloca (WELS_DEC_MAX_THREAD_STACK_SIZE * (sThreadInfo->uiThrNum + 1)); |
95 | | #endif |
96 | 0 | return sThreadInfo->pThrProcMain (p); |
97 | 0 | } |
98 | | |
99 | 0 | static DECODING_STATE ConstructAccessUnit (CWelsDecoder* pWelsDecoder, PWelsDecoderThreadCTX pThrCtx) { |
100 | 0 | int iRet = dsErrorFree; |
101 | | //WelsMutexLock (&pWelsDecoder->m_csDecoder); |
102 | 0 | if (pThrCtx->pCtx->pLastThreadCtx != NULL) { |
103 | 0 | PWelsDecoderThreadCTX pLastThreadCtx = (PWelsDecoderThreadCTX) (pThrCtx->pCtx->pLastThreadCtx); |
104 | 0 | WAIT_EVENT (&pLastThreadCtx->sSliceDecodeStart, WELS_DEC_THREAD_WAIT_INFINITE); |
105 | 0 | RESET_EVENT (&pLastThreadCtx->sSliceDecodeStart); |
106 | 0 | } |
107 | 0 | pThrCtx->pDec = NULL; |
108 | 0 | if (GetThreadCount (pThrCtx->pCtx) > 1) { |
109 | 0 | RESET_EVENT (&pThrCtx->sSliceDecodeFinish); |
110 | 0 | } |
111 | 0 | iRet |= pWelsDecoder->DecodeFrame2WithCtx (pThrCtx->pCtx, NULL, 0, pThrCtx->ppDst, &pThrCtx->sDstInfo); |
112 | | |
113 | | //WelsMutexUnlock (&pWelsDecoder->m_csDecoder); |
114 | 0 | return (DECODING_STATE)iRet; |
115 | 0 | } |
116 | | |
117 | 0 | DECLARE_PROCTHREAD (pThrProcFrame, p) { |
118 | 0 | SWelsDecoderThreadCTX* pThrCtx = (SWelsDecoderThreadCTX*)p; |
119 | 0 | while (1) { |
120 | 0 | RELEASE_SEMAPHORE (pThrCtx->sThreadInfo.sIsBusy); |
121 | 0 | RELEASE_SEMAPHORE (&pThrCtx->sThreadInfo.sIsIdle); |
122 | 0 | WAIT_SEMAPHORE (&pThrCtx->sThreadInfo.sIsActivated, WELS_DEC_THREAD_WAIT_INFINITE); |
123 | 0 | if (pThrCtx->sThreadInfo.uiCommand == WELS_DEC_THREAD_COMMAND_RUN) { |
124 | 0 | CWelsDecoder* pWelsDecoder = (CWelsDecoder*)pThrCtx->threadCtxOwner; |
125 | 0 | ConstructAccessUnit (pWelsDecoder, pThrCtx); |
126 | 0 | } else if (pThrCtx->sThreadInfo.uiCommand == WELS_DEC_THREAD_COMMAND_ABORT) { |
127 | 0 | break; |
128 | 0 | } |
129 | 0 | } |
130 | 0 | return 0; |
131 | 0 | } |
132 | | |
133 | | CWelsDecoder::CWelsDecoder (void) |
134 | 33 | : m_pWelsTrace (NULL), |
135 | 33 | m_uiDecodeTimeStamp (0), |
136 | 33 | m_bIsBaseline (false), |
137 | 33 | m_iCpuCount (1), |
138 | 33 | m_iThreadCount (0), |
139 | 33 | m_iCtxCount (1), |
140 | 33 | m_pPicBuff (NULL), |
141 | 33 | m_bParamSetsLostFlag (false), |
142 | 33 | m_bFreezeOutput (false), |
143 | 33 | m_DecCtxActiveCount (0), |
144 | 33 | m_pDecThrCtx (NULL), |
145 | 33 | m_pLastDecThrCtx (NULL), |
146 | 33 | m_iLastBufferedIdx (0), |
147 | 33 | m_iStreamSeqNum (0) { |
148 | | #ifdef OUTPUT_BIT_STREAM |
149 | | char chFileName[1024] = { 0 }; //for .264 |
150 | | int iBufUsed = 0; |
151 | | int iBufLeft = 1023; |
152 | | int iCurUsed; |
153 | | |
154 | | char chFileNameSize[1024] = { 0 }; //for .len |
155 | | int iBufUsedSize = 0; |
156 | | int iBufLeftSize = 1023; |
157 | | int iCurUsedSize; |
158 | | #endif//OUTPUT_BIT_STREAM |
159 | | |
160 | | |
161 | 33 | m_pWelsTrace = new welsCodecTrace(); |
162 | 33 | if (m_pWelsTrace != NULL) { |
163 | 33 | m_pWelsTrace->SetCodecInstance (this); |
164 | 33 | m_pWelsTrace->SetTraceLevel (WELS_LOG_ERROR); |
165 | | |
166 | 33 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::CWelsDecoder() entry"); |
167 | 33 | } |
168 | | |
169 | 33 | ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, true); |
170 | | |
171 | 33 | m_iCpuCount = GetCPUCount(); |
172 | 33 | if (m_iCpuCount > WELS_DEC_MAX_NUM_CPU) { |
173 | 33 | m_iCpuCount = WELS_DEC_MAX_NUM_CPU; |
174 | 33 | } |
175 | | |
176 | 33 | m_pDecThrCtx = new SWelsDecoderThreadCTX[m_iCtxCount]; |
177 | 33 | memset (m_pDecThrCtx, 0, sizeof (SWelsDecoderThreadCTX)*m_iCtxCount); |
178 | 561 | for (int32_t i = 0; i < WELS_DEC_MAX_NUM_CPU; ++i) { |
179 | 528 | m_pDecThrCtxActive[i] = NULL; |
180 | 528 | } |
181 | | #ifdef OUTPUT_BIT_STREAM |
182 | | SWelsTime sCurTime; |
183 | | |
184 | | WelsGetTimeOfDay (&sCurTime); |
185 | | |
186 | | iCurUsed = WelsSnprintf (chFileName, iBufLeft, "bs_0x%p_", (void*)this); |
187 | | iCurUsedSize = WelsSnprintf (chFileNameSize, iBufLeftSize, "size_0x%p_", (void*)this); |
188 | | |
189 | | iBufUsed += iCurUsed; |
190 | | iBufLeft -= iCurUsed; |
191 | | if (iBufLeft > 0) { |
192 | | iCurUsed = WelsStrftime (&chFileName[iBufUsed], iBufLeft, "%y%m%d%H%M%S", &sCurTime); |
193 | | iBufUsed += iCurUsed; |
194 | | iBufLeft -= iCurUsed; |
195 | | } |
196 | | |
197 | | iBufUsedSize += iCurUsedSize; |
198 | | iBufLeftSize -= iCurUsedSize; |
199 | | if (iBufLeftSize > 0) { |
200 | | iCurUsedSize = WelsStrftime (&chFileNameSize[iBufUsedSize], iBufLeftSize, "%y%m%d%H%M%S", &sCurTime); |
201 | | iBufUsedSize += iCurUsedSize; |
202 | | iBufLeftSize -= iCurUsedSize; |
203 | | } |
204 | | |
205 | | if (iBufLeft > 0) { |
206 | | iCurUsed = WelsSnprintf (&chFileName[iBufUsed], iBufLeft, ".%03.3u.264", WelsGetMillisecond (&sCurTime)); |
207 | | iBufUsed += iCurUsed; |
208 | | iBufLeft -= iCurUsed; |
209 | | } |
210 | | |
211 | | if (iBufLeftSize > 0) { |
212 | | iCurUsedSize = WelsSnprintf (&chFileNameSize[iBufUsedSize], iBufLeftSize, ".%03.3u.len", |
213 | | WelsGetMillisecond (&sCurTime)); |
214 | | iBufUsedSize += iCurUsedSize; |
215 | | iBufLeftSize -= iCurUsedSize; |
216 | | } |
217 | | |
218 | | |
219 | | m_pFBS = WelsFopen (chFileName, "wb"); |
220 | | m_pFBSSize = WelsFopen (chFileNameSize, "wb"); |
221 | | #endif//OUTPUT_BIT_STREAM |
222 | 33 | } |
223 | | |
224 | | /*************************************************************************** |
225 | | * Description: |
226 | | * class CWelsDecoder destructor function, destroy allocced memory |
227 | | * |
228 | | * Input parameters: none |
229 | | * |
230 | | * return: none |
231 | | ***************************************************************************/ |
232 | 33 | CWelsDecoder::~CWelsDecoder() { |
233 | 33 | if (m_pWelsTrace != NULL) { |
234 | 33 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::~CWelsDecoder()"); |
235 | 33 | } |
236 | 33 | CloseDecoderThreads(); |
237 | 33 | UninitDecoder(); |
238 | | |
239 | | #ifdef OUTPUT_BIT_STREAM |
240 | | if (m_pFBS) { |
241 | | WelsFclose (m_pFBS); |
242 | | m_pFBS = NULL; |
243 | | } |
244 | | if (m_pFBSSize) { |
245 | | WelsFclose (m_pFBSSize); |
246 | | m_pFBSSize = NULL; |
247 | | } |
248 | | #endif//OUTPUT_BIT_STREAM |
249 | | |
250 | 33 | if (m_pWelsTrace != NULL) { |
251 | 33 | delete m_pWelsTrace; |
252 | 33 | m_pWelsTrace = NULL; |
253 | 33 | } |
254 | 33 | if (m_pDecThrCtx != NULL) { |
255 | 33 | delete[] m_pDecThrCtx; |
256 | 33 | m_pDecThrCtx = NULL; |
257 | 33 | } |
258 | 33 | } |
259 | | |
260 | 33 | long CWelsDecoder::Initialize (const SDecodingParam* pParam) { |
261 | 33 | int iRet = ERR_NONE; |
262 | 33 | if (m_pWelsTrace == NULL) { |
263 | 0 | return cmMallocMemeError; |
264 | 0 | } |
265 | | |
266 | 33 | if (pParam == NULL) { |
267 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "CWelsDecoder::Initialize(), invalid input argument."); |
268 | 0 | return cmInitParaError; |
269 | 0 | } |
270 | | |
271 | | // H.264 decoder initialization,including memory allocation,then open it ready to decode |
272 | 33 | iRet = InitDecoder (pParam); |
273 | 33 | if (iRet) |
274 | 0 | return iRet; |
275 | | |
276 | 33 | return cmResultSuccess; |
277 | 33 | } |
278 | | |
279 | 33 | long CWelsDecoder::Uninitialize() { |
280 | 33 | UninitDecoder(); |
281 | | |
282 | 33 | return ERR_NONE; |
283 | 33 | } |
284 | | |
285 | 99 | void CWelsDecoder::UninitDecoder (void) { |
286 | 198 | for (int32_t i = 0; i < m_iCtxCount; ++i) { |
287 | 99 | if (m_pDecThrCtx[i].pCtx != NULL) { |
288 | 33 | if (i > 0) { |
289 | 0 | WelsResetRefPicWithoutUnRef (m_pDecThrCtx[i].pCtx); |
290 | 0 | } |
291 | 33 | UninitDecoderCtx (m_pDecThrCtx[i].pCtx); |
292 | 33 | } |
293 | 99 | } |
294 | 99 | } |
295 | | |
296 | 33 | void CWelsDecoder::OpenDecoderThreads() { |
297 | 33 | if (m_iThreadCount >= 1) { |
298 | 0 | m_uiDecodeTimeStamp = 0; |
299 | 0 | CREATE_SEMAPHORE (&m_sIsBusy, m_iThreadCount, m_iThreadCount, NULL); |
300 | 0 | WelsMutexInit (&m_csDecoder); |
301 | 0 | CREATE_EVENT (&m_sBufferingEvent, 1, 0, NULL); |
302 | 0 | SET_EVENT (&m_sBufferingEvent); |
303 | 0 | CREATE_EVENT (&m_sReleaseBufferEvent, 1, 0, NULL); |
304 | 0 | SET_EVENT (&m_sReleaseBufferEvent); |
305 | 0 | for (int32_t i = 0; i < m_iThreadCount; ++i) { |
306 | 0 | m_pDecThrCtx[i].sThreadInfo.uiThrMaxNum = m_iThreadCount; |
307 | 0 | m_pDecThrCtx[i].sThreadInfo.uiThrNum = i; |
308 | 0 | m_pDecThrCtx[i].sThreadInfo.uiThrStackSize = WELS_DEC_MAX_THREAD_STACK_SIZE; |
309 | 0 | m_pDecThrCtx[i].sThreadInfo.pThrProcMain = pThrProcFrame; |
310 | 0 | m_pDecThrCtx[i].sThreadInfo.sIsBusy = &m_sIsBusy; |
311 | 0 | m_pDecThrCtx[i].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_RUN; |
312 | 0 | m_pDecThrCtx[i].threadCtxOwner = this; |
313 | 0 | m_pDecThrCtx[i].kpSrc = NULL; |
314 | 0 | m_pDecThrCtx[i].kiSrcLen = 0; |
315 | 0 | m_pDecThrCtx[i].ppDst = NULL; |
316 | 0 | m_pDecThrCtx[i].pDec = NULL; |
317 | 0 | CREATE_EVENT (&m_pDecThrCtx[i].sImageReady, 1, 0, NULL); |
318 | 0 | CREATE_EVENT (&m_pDecThrCtx[i].sSliceDecodeStart, 1, 0, NULL); |
319 | 0 | CREATE_EVENT (&m_pDecThrCtx[i].sSliceDecodeFinish, 1, 0, NULL); |
320 | 0 | CREATE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle, 0, 1, NULL); |
321 | 0 | CREATE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated, 0, 1, NULL); |
322 | 0 | CREATE_THREAD (&m_pDecThrCtx[i].sThreadInfo.sThrHandle, pThrProcInit, (void*) (& (m_pDecThrCtx[i]))); |
323 | 0 | } |
324 | 0 | } |
325 | 33 | } |
326 | 33 | void CWelsDecoder::CloseDecoderThreads() { |
327 | 33 | if (m_iThreadCount >= 1) { |
328 | 0 | SET_EVENT (&m_sReleaseBufferEvent); |
329 | 0 | for (int32_t i = 0; i < m_iThreadCount; i++) { //waiting the completion begun slices |
330 | 0 | WAIT_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); |
331 | 0 | m_pDecThrCtx[i].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_ABORT; |
332 | 0 | RELEASE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated); |
333 | 0 | WAIT_THREAD (&m_pDecThrCtx[i].sThreadInfo.sThrHandle); |
334 | 0 | CLOSE_EVENT (&m_pDecThrCtx[i].sImageReady); |
335 | 0 | CLOSE_EVENT (&m_pDecThrCtx[i].sSliceDecodeStart); |
336 | 0 | CLOSE_EVENT (&m_pDecThrCtx[i].sSliceDecodeFinish); |
337 | 0 | CLOSE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle); |
338 | 0 | CLOSE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated); |
339 | 0 | } |
340 | 0 | WelsMutexDestroy (&m_csDecoder); |
341 | 0 | CLOSE_EVENT (&m_sBufferingEvent); |
342 | 0 | CLOSE_EVENT (&m_sReleaseBufferEvent); |
343 | 0 | CLOSE_SEMAPHORE (&m_sIsBusy); |
344 | 0 | } |
345 | 33 | } |
346 | | |
347 | 66 | void CWelsDecoder::UninitDecoderCtx (PWelsDecoderContext& pCtx) { |
348 | 66 | if (pCtx != NULL) { |
349 | | |
350 | 33 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::UninitDecoderCtx(), openh264 codec version = %s.", |
351 | 33 | VERSION_NUMBER); |
352 | | |
353 | 33 | WelsEndDecoder (pCtx); |
354 | | |
355 | 33 | if (pCtx->pMemAlign != NULL) { |
356 | 33 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, |
357 | 33 | "CWelsDecoder::UninitDecoder(), verify memory usage (%d bytes) after free..", |
358 | 33 | pCtx->pMemAlign->WelsGetMemoryUsage()); |
359 | 33 | delete pCtx->pMemAlign; |
360 | 33 | pCtx->pMemAlign = NULL; |
361 | 33 | } |
362 | | |
363 | 33 | if (NULL != pCtx) { |
364 | 33 | WelsFree (pCtx, "m_pDecContext"); |
365 | | |
366 | 33 | pCtx = NULL; |
367 | 33 | } |
368 | 33 | if (m_iCtxCount <= 1) m_pDecThrCtx[0].pCtx = NULL; |
369 | 33 | } |
370 | 66 | } |
371 | | |
372 | | // the return value of this function is not suitable, it need report failure info to upper layer. |
373 | 33 | int32_t CWelsDecoder::InitDecoder (const SDecodingParam* pParam) { |
374 | | |
375 | 33 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, |
376 | 33 | "CWelsDecoder::init_decoder(), openh264 codec version = %s, ParseOnly = %d", |
377 | 33 | VERSION_NUMBER, (int32_t)pParam->bParseOnly); |
378 | 33 | if (m_iThreadCount >= 1 && pParam->bParseOnly) { |
379 | 0 | m_iThreadCount = 0; |
380 | 0 | } |
381 | 33 | OpenDecoderThreads(); |
382 | | //reset decoder context |
383 | 33 | memset (&m_sDecoderStatistics, 0, sizeof (SDecoderStatistics)); |
384 | 33 | memset (&m_sLastDecPicInfo, 0, sizeof (SWelsLastDecPicInfo)); |
385 | 33 | memset (&m_sVlcTable, 0, sizeof (SVlcTable)); |
386 | 33 | UninitDecoder(); |
387 | 33 | WelsDecoderLastDecPicInfoDefaults (m_sLastDecPicInfo); |
388 | 66 | for (int32_t i = 0; i < m_iCtxCount; ++i) { |
389 | 33 | InitDecoderCtx (m_pDecThrCtx[i].pCtx, pParam); |
390 | 33 | if (m_iThreadCount >= 1) { |
391 | 0 | m_pDecThrCtx[i].pCtx->pThreadCtx = &m_pDecThrCtx[i]; |
392 | 0 | } |
393 | 33 | } |
394 | 33 | m_bParamSetsLostFlag = false; |
395 | 33 | m_bFreezeOutput = false; |
396 | 33 | return cmResultSuccess; |
397 | 33 | } |
398 | | |
399 | | // the return value of this function is not suitable, it need report failure info to upper layer. |
400 | 33 | int32_t CWelsDecoder::InitDecoderCtx (PWelsDecoderContext& pCtx, const SDecodingParam* pParam) { |
401 | | |
402 | 33 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, |
403 | 33 | "CWelsDecoder::init_decoder(), openh264 codec version = %s, ParseOnly = %d", |
404 | 33 | VERSION_NUMBER, (int32_t)pParam->bParseOnly); |
405 | | |
406 | | //reset decoder context |
407 | 33 | UninitDecoderCtx (pCtx); |
408 | 33 | pCtx = (PWelsDecoderContext)WelsMallocz (sizeof (SWelsDecoderContext), "m_pDecContext"); |
409 | 33 | if (NULL == pCtx) |
410 | 0 | return cmMallocMemeError; |
411 | 33 | int32_t iCacheLineSize = 16; // on chip cache line size in byte |
412 | 33 | pCtx->pMemAlign = new CMemoryAlign (iCacheLineSize); |
413 | 33 | WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, (NULL == pCtx->pMemAlign), UninitDecoderCtx (pCtx)) |
414 | 33 | if (m_iCtxCount <= 1) m_pDecThrCtx[0].pCtx = pCtx; |
415 | | //fill in default value into context |
416 | 33 | pCtx->pLastDecPicInfo = &m_sLastDecPicInfo; |
417 | 33 | pCtx->pDecoderStatistics = &m_sDecoderStatistics; |
418 | 33 | pCtx->pVlcTable = &m_sVlcTable; |
419 | 33 | pCtx->pPictInfoList = m_sPictInfoList; |
420 | 33 | pCtx->pPictReoderingStatus = &m_sReoderingStatus; |
421 | 33 | pCtx->pCsDecoder = &m_csDecoder; |
422 | 33 | pCtx->pStreamSeqNum = &m_iStreamSeqNum; |
423 | 33 | WelsDecoderDefaults (pCtx, &m_pWelsTrace->m_sLogCtx); |
424 | 33 | WelsDecoderSpsPpsDefaults (pCtx->sSpsPpsCtx); |
425 | | //check param and update decoder context |
426 | 33 | pCtx->pParam = (SDecodingParam*)pCtx->pMemAlign->WelsMallocz (sizeof (SDecodingParam), |
427 | 33 | "SDecodingParam"); |
428 | 33 | WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, (NULL == pCtx->pParam), UninitDecoderCtx (pCtx)); |
429 | 33 | int32_t iRet = DecoderConfigParam (pCtx, pParam); |
430 | 33 | WELS_VERIFY_RETURN_IFNEQ (iRet, cmResultSuccess); |
431 | | |
432 | | //init decoder |
433 | 33 | WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, WelsInitDecoder (pCtx, &m_pWelsTrace->m_sLogCtx), |
434 | 33 | UninitDecoderCtx (pCtx)) |
435 | 33 | pCtx->pPicBuff = NULL; |
436 | 33 | return cmResultSuccess; |
437 | 33 | } |
438 | | |
439 | 0 | int32_t CWelsDecoder::ResetDecoder (PWelsDecoderContext& pCtx) { |
440 | | // TBC: need to be modified when context and trace point are null |
441 | 0 | if (m_iThreadCount >= 1) { |
442 | 0 | ThreadResetDecoder (pCtx); |
443 | 0 | } else { |
444 | 0 | if (pCtx != NULL && m_pWelsTrace != NULL) { |
445 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "ResetDecoder(), context error code is %d", |
446 | 0 | pCtx->iErrorCode); |
447 | 0 | SDecodingParam sPrevParam; |
448 | 0 | memcpy (&sPrevParam, pCtx->pParam, sizeof (SDecodingParam)); |
449 | |
|
450 | 0 | WELS_VERIFY_RETURN_PROC_IF (cmInitParaError, InitDecoderCtx (pCtx, &sPrevParam), |
451 | 0 | UninitDecoderCtx (pCtx)); |
452 | 0 | } else if (m_pWelsTrace != NULL) { |
453 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "ResetDecoder() failed as decoder context null"); |
454 | 0 | } |
455 | 0 | ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, false); |
456 | 0 | } |
457 | 0 | return ERR_INFO_UNINIT; |
458 | 0 | } |
459 | | |
460 | 0 | int32_t CWelsDecoder::ThreadResetDecoder (PWelsDecoderContext& pCtx) { |
461 | | // TBC: need to be modified when context and trace point are null |
462 | 0 | SDecodingParam sPrevParam; |
463 | 0 | if (pCtx != NULL && m_pWelsTrace != NULL) { |
464 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "ResetDecoder(), context error code is %d", pCtx->iErrorCode); |
465 | 0 | memcpy (&sPrevParam, pCtx->pParam, sizeof (SDecodingParam)); |
466 | 0 | ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, true); |
467 | 0 | CloseDecoderThreads(); |
468 | 0 | UninitDecoder(); |
469 | 0 | InitDecoder (&sPrevParam); |
470 | 0 | } else if (m_pWelsTrace != NULL) { |
471 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "ResetDecoder() failed as decoder context null"); |
472 | 0 | } |
473 | 0 | return ERR_INFO_UNINIT; |
474 | 0 | } |
475 | | |
476 | | /* |
477 | | * Set Option |
478 | | */ |
479 | 0 | long CWelsDecoder::SetOption (DECODER_OPTION eOptID, void* pOption) { |
480 | 0 | int iVal = 0; |
481 | 0 | if (eOptID == DECODER_OPTION_NUM_OF_THREADS) { |
482 | 0 | if (pOption != NULL) { |
483 | 0 | int32_t threadCount = * ((int32_t*)pOption); |
484 | 0 | if (threadCount < 0) threadCount = 0; |
485 | 0 | if (threadCount > m_iCpuCount) { |
486 | 0 | threadCount = m_iCpuCount; |
487 | 0 | } |
488 | 0 | if (threadCount > 3) { |
489 | 0 | threadCount = 3; |
490 | 0 | } |
491 | 0 | if (threadCount != m_iThreadCount) { |
492 | 0 | m_iThreadCount = threadCount; |
493 | 0 | if (m_pDecThrCtx != NULL) { |
494 | 0 | delete [] m_pDecThrCtx; |
495 | 0 | m_iCtxCount = m_iThreadCount == 0 ? 1 : m_iThreadCount; |
496 | 0 | m_pDecThrCtx = new SWelsDecoderThreadCTX[m_iCtxCount]; |
497 | 0 | memset (m_pDecThrCtx, 0, sizeof (SWelsDecoderThreadCTX)*m_iCtxCount); |
498 | 0 | } |
499 | 0 | } |
500 | 0 | } |
501 | 0 | return cmResultSuccess; |
502 | 0 | } |
503 | 0 | for (int32_t i = 0; i < m_iCtxCount; ++i) { |
504 | 0 | PWelsDecoderContext pDecContext = m_pDecThrCtx[i].pCtx; |
505 | 0 | if (pDecContext == NULL && eOptID != DECODER_OPTION_TRACE_LEVEL && |
506 | 0 | eOptID != DECODER_OPTION_TRACE_CALLBACK && eOptID != DECODER_OPTION_TRACE_CALLBACK_CONTEXT) |
507 | 0 | return dsInitialOptExpected; |
508 | 0 | if (eOptID == DECODER_OPTION_END_OF_STREAM) { // Indicate bit-stream of the final frame to be decoded |
509 | 0 | if (pOption == NULL) |
510 | 0 | return cmInitParaError; |
511 | | |
512 | 0 | iVal = * ((int*)pOption); // boolean value for whether enabled End Of Stream flag |
513 | |
|
514 | 0 | if (pDecContext == NULL) return dsInitialOptExpected; |
515 | | |
516 | 0 | pDecContext->bEndOfStreamFlag = iVal ? true : false; |
517 | 0 | if (iVal && m_iThreadCount >= 1) |
518 | 0 | SET_EVENT (&m_sReleaseBufferEvent); |
519 | |
|
520 | 0 | return cmResultSuccess; |
521 | 0 | } else if (eOptID == DECODER_OPTION_ERROR_CON_IDC) { // Indicate error concealment status |
522 | 0 | if (pOption == NULL) |
523 | 0 | return cmInitParaError; |
524 | | |
525 | 0 | if (pDecContext == NULL) return dsInitialOptExpected; |
526 | | |
527 | 0 | iVal = * ((int*)pOption); // int value for error concealment idc |
528 | 0 | iVal = WELS_CLIP3 (iVal, (int32_t)ERROR_CON_DISABLE, (int32_t)ERROR_CON_SLICE_MV_COPY_CROSS_IDR_FREEZE_RES_CHANGE); |
529 | 0 | if ((pDecContext->pParam->bParseOnly) && (iVal != (int32_t)ERROR_CON_DISABLE)) { |
530 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, |
531 | 0 | "CWelsDecoder::SetOption for ERROR_CON_IDC = %d not allowd for parse only!.", iVal); |
532 | 0 | return cmInitParaError; |
533 | 0 | } |
534 | | |
535 | 0 | pDecContext->pParam->eEcActiveIdc = (ERROR_CON_IDC)iVal; |
536 | 0 | InitErrorCon (pDecContext); |
537 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, |
538 | 0 | "CWelsDecoder::SetOption for ERROR_CON_IDC = %d.", iVal); |
539 | |
|
540 | 0 | return cmResultSuccess; |
541 | 0 | } else if (eOptID == DECODER_OPTION_TRACE_LEVEL) { |
542 | 0 | if (m_pWelsTrace) { |
543 | 0 | uint32_t level = * ((uint32_t*)pOption); |
544 | 0 | m_pWelsTrace->SetTraceLevel (level); |
545 | 0 | } |
546 | 0 | return cmResultSuccess; |
547 | 0 | } else if (eOptID == DECODER_OPTION_TRACE_CALLBACK) { |
548 | 0 | if (m_pWelsTrace) { |
549 | 0 | WelsTraceCallback callback = * ((WelsTraceCallback*)pOption); |
550 | 0 | m_pWelsTrace->SetTraceCallback (callback); |
551 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, |
552 | 0 | "CWelsDecoder::SetOption():DECODER_OPTION_TRACE_CALLBACK callback = %p.", |
553 | 0 | callback); |
554 | 0 | } |
555 | 0 | return cmResultSuccess; |
556 | 0 | } else if (eOptID == DECODER_OPTION_TRACE_CALLBACK_CONTEXT) { |
557 | 0 | if (m_pWelsTrace) { |
558 | 0 | void* ctx = * ((void**)pOption); |
559 | 0 | m_pWelsTrace->SetTraceCallbackContext (ctx); |
560 | 0 | } |
561 | 0 | return cmResultSuccess; |
562 | 0 | } else if (eOptID == DECODER_OPTION_GET_STATISTICS) { |
563 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING, |
564 | 0 | "CWelsDecoder::SetOption():DECODER_OPTION_GET_STATISTICS: this option is get-only!"); |
565 | 0 | return cmInitParaError; |
566 | 0 | } else if (eOptID == DECODER_OPTION_STATISTICS_LOG_INTERVAL) { |
567 | 0 | if (pOption) { |
568 | 0 | if (pDecContext == NULL) return dsInitialOptExpected; |
569 | 0 | pDecContext->pDecoderStatistics->iStatisticsLogInterval = (* ((unsigned int*)pOption)); |
570 | 0 | return cmResultSuccess; |
571 | 0 | } |
572 | 0 | } else if (eOptID == DECODER_OPTION_GET_SAR_INFO) { |
573 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING, |
574 | 0 | "CWelsDecoder::SetOption():DECODER_OPTION_GET_SAR_INFO: this option is get-only!"); |
575 | 0 | return cmInitParaError; |
576 | 0 | } |
577 | 0 | } |
578 | 0 | return cmInitParaError; |
579 | 0 | } |
580 | | |
581 | | /* |
582 | | * Get Option |
583 | | */ |
584 | 0 | long CWelsDecoder::GetOption (DECODER_OPTION eOptID, void* pOption) { |
585 | 0 | int iVal = 0; |
586 | 0 | if (DECODER_OPTION_NUM_OF_THREADS == eOptID) { |
587 | 0 | * ((int*)pOption) = m_iThreadCount; |
588 | 0 | return cmResultSuccess; |
589 | 0 | } |
590 | 0 | PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx; |
591 | 0 | if (pDecContext == NULL) |
592 | 0 | return cmInitExpected; |
593 | | |
594 | 0 | if (pOption == NULL) |
595 | 0 | return cmInitParaError; |
596 | | |
597 | 0 | if (DECODER_OPTION_END_OF_STREAM == eOptID) { |
598 | 0 | iVal = pDecContext->bEndOfStreamFlag; |
599 | 0 | * ((int*)pOption) = iVal; |
600 | 0 | return cmResultSuccess; |
601 | 0 | } |
602 | 0 | #ifdef LONG_TERM_REF |
603 | 0 | else if (DECODER_OPTION_IDR_PIC_ID == eOptID) { |
604 | 0 | iVal = pDecContext->uiCurIdrPicId; |
605 | 0 | * ((int*)pOption) = iVal; |
606 | 0 | return cmResultSuccess; |
607 | 0 | } else if (DECODER_OPTION_FRAME_NUM == eOptID) { |
608 | 0 | iVal = pDecContext->iFrameNum; |
609 | 0 | * ((int*)pOption) = iVal; |
610 | 0 | return cmResultSuccess; |
611 | 0 | } else if (DECODER_OPTION_LTR_MARKING_FLAG == eOptID) { |
612 | 0 | iVal = pDecContext->bCurAuContainLtrMarkSeFlag; |
613 | 0 | * ((int*)pOption) = iVal; |
614 | 0 | return cmResultSuccess; |
615 | 0 | } else if (DECODER_OPTION_LTR_MARKED_FRAME_NUM == eOptID) { |
616 | 0 | iVal = pDecContext->iFrameNumOfAuMarkedLtr; |
617 | 0 | * ((int*)pOption) = iVal; |
618 | 0 | return cmResultSuccess; |
619 | 0 | } |
620 | 0 | #endif |
621 | 0 | else if (DECODER_OPTION_VCL_NAL == eOptID) { //feedback whether or not have VCL NAL in current AU |
622 | 0 | iVal = pDecContext->iFeedbackVclNalInAu; |
623 | 0 | * ((int*)pOption) = iVal; |
624 | 0 | return cmResultSuccess; |
625 | 0 | } else if (DECODER_OPTION_TEMPORAL_ID == eOptID) { //if have VCL NAL in current AU, then feedback the temporal ID |
626 | 0 | iVal = pDecContext->iFeedbackTidInAu; |
627 | 0 | * ((int*)pOption) = iVal; |
628 | 0 | return cmResultSuccess; |
629 | 0 | } else if (DECODER_OPTION_IS_REF_PIC == eOptID) { |
630 | 0 | iVal = pDecContext->iFeedbackNalRefIdc; |
631 | 0 | if (iVal > 0) |
632 | 0 | iVal = 1; |
633 | 0 | * ((int*)pOption) = iVal; |
634 | 0 | return cmResultSuccess; |
635 | 0 | } else if (DECODER_OPTION_ERROR_CON_IDC == eOptID) { |
636 | 0 | iVal = (int)pDecContext->pParam->eEcActiveIdc; |
637 | 0 | * ((int*)pOption) = iVal; |
638 | 0 | return cmResultSuccess; |
639 | 0 | } else if (DECODER_OPTION_GET_STATISTICS == eOptID) { // get decoder statistics info for real time debugging |
640 | 0 | SDecoderStatistics* pDecoderStatistics = (static_cast<SDecoderStatistics*> (pOption)); |
641 | |
|
642 | 0 | memcpy (pDecoderStatistics, pDecContext->pDecoderStatistics, sizeof (SDecoderStatistics)); |
643 | |
|
644 | 0 | if (pDecContext->pDecoderStatistics->uiDecodedFrameCount != 0) { //not original status |
645 | 0 | pDecoderStatistics->fAverageFrameSpeedInMs = (float) (pDecContext->dDecTime) / |
646 | 0 | (pDecContext->pDecoderStatistics->uiDecodedFrameCount); |
647 | 0 | pDecoderStatistics->fActualAverageFrameSpeedInMs = (float) (pDecContext->dDecTime) / |
648 | 0 | (pDecContext->pDecoderStatistics->uiDecodedFrameCount + pDecContext->pDecoderStatistics->uiFreezingIDRNum + |
649 | 0 | pDecContext->pDecoderStatistics->uiFreezingNonIDRNum); |
650 | 0 | } |
651 | 0 | return cmResultSuccess; |
652 | 0 | } else if (eOptID == DECODER_OPTION_STATISTICS_LOG_INTERVAL) { |
653 | 0 | if (pOption) { |
654 | 0 | iVal = pDecContext->pDecoderStatistics->iStatisticsLogInterval; |
655 | 0 | * ((unsigned int*)pOption) = iVal; |
656 | 0 | return cmResultSuccess; |
657 | 0 | } |
658 | 0 | } else if (DECODER_OPTION_GET_SAR_INFO == eOptID) { //get decoder SAR info in VUI |
659 | 0 | PVuiSarInfo pVuiSarInfo = (static_cast<PVuiSarInfo> (pOption)); |
660 | 0 | memset (pVuiSarInfo, 0, sizeof (SVuiSarInfo)); |
661 | 0 | if (!pDecContext->pSps) { |
662 | 0 | return cmInitExpected; |
663 | 0 | } else { |
664 | 0 | pVuiSarInfo->uiSarWidth = pDecContext->pSps->sVui.uiSarWidth; |
665 | 0 | pVuiSarInfo->uiSarHeight = pDecContext->pSps->sVui.uiSarHeight; |
666 | 0 | pVuiSarInfo->bOverscanAppropriateFlag = pDecContext->pSps->sVui.bOverscanAppropriateFlag; |
667 | 0 | return cmResultSuccess; |
668 | 0 | } |
669 | 0 | } else if (DECODER_OPTION_PROFILE == eOptID) { |
670 | 0 | if (!pDecContext->pSps) { |
671 | 0 | return cmInitExpected; |
672 | 0 | } |
673 | 0 | iVal = (int)pDecContext->pSps->uiProfileIdc; |
674 | 0 | * ((int*)pOption) = iVal; |
675 | 0 | return cmResultSuccess; |
676 | 0 | } else if (DECODER_OPTION_LEVEL == eOptID) { |
677 | 0 | if (!pDecContext->pSps) { |
678 | 0 | return cmInitExpected; |
679 | 0 | } |
680 | 0 | iVal = (int)pDecContext->pSps->uiLevelIdc; |
681 | 0 | * ((int*)pOption) = iVal; |
682 | 0 | return cmResultSuccess; |
683 | 0 | } else if (DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER == eOptID) { |
684 | 0 | for (int32_t activeThread = 0; activeThread < m_DecCtxActiveCount; ++activeThread) { |
685 | 0 | WAIT_SEMAPHORE (&m_pDecThrCtxActive[activeThread]->sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); |
686 | 0 | RELEASE_SEMAPHORE (&m_pDecThrCtxActive[activeThread]->sThreadInfo.sIsIdle); |
687 | 0 | } |
688 | 0 | * ((int*)pOption) = m_sReoderingStatus.iNumOfPicts; |
689 | 0 | return cmResultSuccess; |
690 | 0 | } |
691 | | |
692 | 0 | return cmInitParaError; |
693 | 0 | } |
694 | | |
695 | | DECODING_STATE CWelsDecoder::DecodeFrameNoDelay (const unsigned char* kpSrc, |
696 | | const int kiSrcLen, |
697 | | unsigned char** ppDst, |
698 | 1 | SBufferInfo* pDstInfo) { |
699 | 1 | int iRet = dsErrorFree; |
700 | 1 | if (m_iThreadCount >= 1) { |
701 | 0 | SET_EVENT (&m_sReleaseBufferEvent); |
702 | 0 | iRet = ThreadDecodeFrameInternal (kpSrc, kiSrcLen, ppDst, pDstInfo); |
703 | 0 | if (m_sReoderingStatus.iNumOfPicts) { |
704 | 0 | WAIT_EVENT (&m_sBufferingEvent, WELS_DEC_THREAD_WAIT_INFINITE); |
705 | 0 | RESET_EVENT (&m_sBufferingEvent); |
706 | 0 | RESET_EVENT (&m_sReleaseBufferEvent); |
707 | 0 | if (!m_sReoderingStatus.bHasBSlice) { |
708 | 0 | if (m_sReoderingStatus.iNumOfPicts > 1) { |
709 | 0 | ReleaseBufferedReadyPictureNoReorder (NULL, ppDst, pDstInfo); |
710 | 0 | } |
711 | 0 | } |
712 | 0 | else { |
713 | 0 | ReleaseBufferedReadyPictureReorder (NULL, ppDst, pDstInfo); |
714 | 0 | } |
715 | 0 | } |
716 | 0 | return (DECODING_STATE)iRet; |
717 | 0 | } |
718 | | //SBufferInfo sTmpBufferInfo; |
719 | | //unsigned char* ppTmpDst[3] = {NULL, NULL, NULL}; |
720 | 1 | iRet = (int)DecodeFrame2 (kpSrc, kiSrcLen, ppDst, pDstInfo); |
721 | | //memcpy (&sTmpBufferInfo, pDstInfo, sizeof (SBufferInfo)); |
722 | | //ppTmpDst[0] = ppDst[0]; |
723 | | //ppTmpDst[1] = ppDst[1]; |
724 | | //ppTmpDst[2] = ppDst[2]; |
725 | 1 | iRet |= DecodeFrame2 (NULL, 0, ppDst, pDstInfo); |
726 | | //if ((pDstInfo->iBufferStatus == 0) && (sTmpBufferInfo.iBufferStatus == 1)) { |
727 | | //memcpy (pDstInfo, &sTmpBufferInfo, sizeof (SBufferInfo)); |
728 | | //ppDst[0] = ppTmpDst[0]; |
729 | | //ppDst[1] = ppTmpDst[1]; |
730 | | //ppDst[2] = ppTmpDst[2]; |
731 | | //} |
732 | 1 | return (DECODING_STATE)iRet; |
733 | 1 | } |
734 | | |
735 | | DECODING_STATE CWelsDecoder::DecodeFrame2WithCtx (PWelsDecoderContext pDecContext, const unsigned char* kpSrc, |
736 | | const int kiSrcLen, |
737 | | unsigned char** ppDst, |
738 | 2 | SBufferInfo* pDstInfo) { |
739 | 2 | if (pDecContext == NULL || pDecContext->pParam == NULL) { |
740 | 0 | if (m_pWelsTrace != NULL) { |
741 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "Call DecodeFrame2 without Initialize.\n"); |
742 | 0 | } |
743 | 0 | return dsInitialOptExpected; |
744 | 0 | } |
745 | | |
746 | 2 | if (pDecContext->pParam->bParseOnly) { |
747 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "bParseOnly should be false for this API calling! \n"); |
748 | 0 | pDecContext->iErrorCode |= dsInvalidArgument; |
749 | 0 | return dsInvalidArgument; |
750 | 0 | } |
751 | 2 | if (CheckBsBuffer (pDecContext, kiSrcLen)) { |
752 | 0 | if (ResetDecoder(pDecContext)) { |
753 | 0 | if (pDstInfo) pDstInfo->iBufferStatus = 0; |
754 | 0 | return dsOutOfMemory; |
755 | 0 | } |
756 | 0 | return dsErrorFree; |
757 | 0 | } |
758 | 2 | if (kiSrcLen > 0 && kpSrc != NULL) { |
759 | | #ifdef OUTPUT_BIT_STREAM |
760 | | if (m_pFBS) { |
761 | | WelsFwrite (kpSrc, sizeof (unsigned char), kiSrcLen, m_pFBS); |
762 | | WelsFflush (m_pFBS); |
763 | | } |
764 | | if (m_pFBSSize) { |
765 | | WelsFwrite (&kiSrcLen, sizeof (int), 1, m_pFBSSize); |
766 | | WelsFflush (m_pFBSSize); |
767 | | } |
768 | | #endif//OUTPUT_BIT_STREAM |
769 | 1 | pDecContext->bEndOfStreamFlag = false; |
770 | 1 | if (GetThreadCount (pDecContext) <= 0) { |
771 | 1 | pDecContext->uiDecodingTimeStamp = ++m_uiDecodeTimeStamp; |
772 | 1 | } |
773 | 1 | } else { |
774 | | //For application MODE, the error detection should be added for safe. |
775 | | //But for CONSOLE MODE, when decoding LAST AU, kiSrcLen==0 && kpSrc==NULL. |
776 | 1 | pDecContext->bEndOfStreamFlag = true; |
777 | 1 | pDecContext->bInstantDecFlag = true; |
778 | 1 | } |
779 | | |
780 | 2 | int64_t iStart, iEnd; |
781 | 2 | iStart = WelsTime(); |
782 | | |
783 | 2 | if (GetThreadCount (pDecContext) <= 1) { |
784 | 2 | ppDst[0] = ppDst[1] = ppDst[2] = NULL; |
785 | 2 | } |
786 | 2 | pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding. |
787 | 2 | pDecContext->iFeedbackVclNalInAu = FEEDBACK_UNKNOWN_NAL; //initialize |
788 | 2 | unsigned long long uiInBsTimeStamp = pDstInfo->uiInBsTimeStamp; |
789 | 2 | if (GetThreadCount (pDecContext) <= 1) { |
790 | 2 | memset (pDstInfo, 0, sizeof (SBufferInfo)); |
791 | 2 | } |
792 | 2 | pDstInfo->uiInBsTimeStamp = uiInBsTimeStamp; |
793 | 2 | #ifdef LONG_TERM_REF |
794 | 2 | pDecContext->bReferenceLostAtT0Flag = false; //initialize for LTR |
795 | 2 | pDecContext->bCurAuContainLtrMarkSeFlag = false; |
796 | 2 | pDecContext->iFrameNumOfAuMarkedLtr = 0; |
797 | 2 | pDecContext->iFrameNum = -1; //initialize |
798 | 2 | #endif |
799 | | |
800 | 2 | if (GetThreadCount (pDecContext) >= 1) { |
801 | 0 | WAIT_EVENT (&m_sReleaseBufferEvent, WELS_DEC_THREAD_WAIT_INFINITE); |
802 | 0 | } |
803 | | |
804 | 2 | pDecContext->iFeedbackTidInAu = -1; //initialize |
805 | 2 | pDecContext->iFeedbackNalRefIdc = -1; //initialize |
806 | 2 | if (pDstInfo) { |
807 | 2 | pDstInfo->uiOutYuvTimeStamp = 0; |
808 | 2 | pDecContext->uiTimeStamp = pDstInfo->uiInBsTimeStamp; |
809 | 2 | } else { |
810 | 0 | pDecContext->uiTimeStamp = 0; |
811 | 0 | } |
812 | 2 | WelsDecodeBs (pDecContext, kpSrc, kiSrcLen, ppDst, |
813 | 2 | pDstInfo, NULL); //iErrorCode has been modified in this function |
814 | 2 | pDecContext->bInstantDecFlag = false; //reset no-delay flag |
815 | 2 | if (pDecContext->iErrorCode) { |
816 | 1 | EWelsNalUnitType eNalType = |
817 | 1 | NAL_UNIT_UNSPEC_0; //for NBR, IDR frames are expected to decode as followed if error decoding an IDR currently |
818 | | |
819 | 1 | eNalType = pDecContext->sCurNalHead.eNalUnitType; |
820 | 1 | if (pDecContext->iErrorCode & dsOutOfMemory) { |
821 | 0 | if (ResetDecoder (pDecContext)) { |
822 | 0 | if (pDstInfo) pDstInfo->iBufferStatus = 0; |
823 | 0 | return dsOutOfMemory; |
824 | 0 | } |
825 | 0 | return dsErrorFree; |
826 | 0 | } |
827 | 1 | if (pDecContext->iErrorCode & dsRefListNullPtrs) { |
828 | 0 | if (ResetDecoder (pDecContext)) { |
829 | 0 | if (pDstInfo) pDstInfo->iBufferStatus = 0; |
830 | 0 | return dsRefListNullPtrs; |
831 | 0 | } |
832 | 0 | return dsErrorFree; |
833 | 0 | } |
834 | | //for AVC bitstream (excluding AVC with temporal scalability, including TP), as long as error occur, SHOULD notify upper layer key frame loss. |
835 | 1 | if ((IS_PARAM_SETS_NALS (eNalType) || NAL_UNIT_CODED_SLICE_IDR == eNalType) || |
836 | 1 | (VIDEO_BITSTREAM_AVC == pDecContext->eVideoType)) { |
837 | 1 | if (pDecContext->pParam->eEcActiveIdc == ERROR_CON_DISABLE) { |
838 | 1 | #ifdef LONG_TERM_REF |
839 | 1 | pDecContext->bParamSetsLostFlag = true; |
840 | | #else |
841 | | pDecContext->bReferenceLostAtT0Flag = true; |
842 | | #endif |
843 | 1 | } |
844 | 1 | } |
845 | | |
846 | 1 | if (pDecContext->bPrintFrameErrorTraceFlag) { |
847 | 1 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "decode failed, failure type:%d \n", |
848 | 1 | pDecContext->iErrorCode); |
849 | 1 | pDecContext->bPrintFrameErrorTraceFlag = false; |
850 | 1 | } else { |
851 | 0 | pDecContext->iIgnoredErrorInfoPacketCount++; |
852 | 0 | if (pDecContext->iIgnoredErrorInfoPacketCount == INT_MAX) { |
853 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING, "continuous error reached INT_MAX! Restart as 0."); |
854 | 0 | pDecContext->iIgnoredErrorInfoPacketCount = 0; |
855 | 0 | } |
856 | 0 | } |
857 | 1 | if ((pDecContext->pParam->eEcActiveIdc != ERROR_CON_DISABLE) && (pDstInfo->iBufferStatus == 1)) { |
858 | | //TODO after dec status updated |
859 | 0 | pDecContext->iErrorCode |= dsDataErrorConcealed; |
860 | |
|
861 | 0 | pDecContext->pDecoderStatistics->uiDecodedFrameCount++; |
862 | 0 | if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t |
863 | 0 | ResetDecStatNums (pDecContext->pDecoderStatistics); |
864 | 0 | pDecContext->pDecoderStatistics->uiDecodedFrameCount++; |
865 | 0 | } |
866 | 0 | int32_t iMbConcealedNum = pDecContext->iMbEcedNum + pDecContext->iMbEcedPropNum; |
867 | 0 | pDecContext->pDecoderStatistics->uiAvgEcRatio = pDecContext->iMbNum == 0 ? |
868 | 0 | (pDecContext->pDecoderStatistics->uiAvgEcRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) : (( |
869 | 0 | pDecContext->pDecoderStatistics->uiAvgEcRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) + (( |
870 | 0 | iMbConcealedNum * 100) / pDecContext->iMbNum)); |
871 | 0 | pDecContext->pDecoderStatistics->uiAvgEcPropRatio = pDecContext->iMbNum == 0 ? |
872 | 0 | (pDecContext->pDecoderStatistics->uiAvgEcPropRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) : (( |
873 | 0 | pDecContext->pDecoderStatistics->uiAvgEcPropRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) + (( |
874 | 0 | pDecContext->iMbEcedPropNum * 100) / pDecContext->iMbNum)); |
875 | 0 | pDecContext->pDecoderStatistics->uiEcFrameNum += (iMbConcealedNum == 0 ? 0 : 1); |
876 | 0 | pDecContext->pDecoderStatistics->uiAvgEcRatio = pDecContext->pDecoderStatistics->uiEcFrameNum == 0 ? 0 : |
877 | 0 | pDecContext->pDecoderStatistics->uiAvgEcRatio / pDecContext->pDecoderStatistics->uiEcFrameNum; |
878 | 0 | pDecContext->pDecoderStatistics->uiAvgEcPropRatio = pDecContext->pDecoderStatistics->uiEcFrameNum == 0 ? 0 : |
879 | 0 | pDecContext->pDecoderStatistics->uiAvgEcPropRatio / pDecContext->pDecoderStatistics->uiEcFrameNum; |
880 | 0 | } |
881 | 1 | iEnd = WelsTime(); |
882 | 1 | pDecContext->dDecTime += (iEnd - iStart) / 1e3; |
883 | | |
884 | 1 | OutputStatisticsLog (*pDecContext->pDecoderStatistics); |
885 | 1 | if (GetThreadCount (pDecContext) >= 1) { |
886 | 0 | BufferingReadyPicture (pDecContext, ppDst, pDstInfo); |
887 | 0 | SET_EVENT (&m_sBufferingEvent); |
888 | 1 | } else { |
889 | 1 | ReorderPicturesInDisplay (pDecContext, ppDst, pDstInfo); |
890 | 1 | } |
891 | | |
892 | 1 | return (DECODING_STATE)pDecContext->iErrorCode; |
893 | 1 | } |
894 | | // else Error free, the current codec works well |
895 | | |
896 | 1 | if (pDstInfo->iBufferStatus == 1) { |
897 | |
|
898 | 0 | pDecContext->pDecoderStatistics->uiDecodedFrameCount++; |
899 | 0 | if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t |
900 | 0 | ResetDecStatNums (pDecContext->pDecoderStatistics); |
901 | 0 | pDecContext->pDecoderStatistics->uiDecodedFrameCount++; |
902 | 0 | } |
903 | |
|
904 | 0 | OutputStatisticsLog (*pDecContext->pDecoderStatistics); |
905 | 0 | } |
906 | 1 | iEnd = WelsTime(); |
907 | 1 | pDecContext->dDecTime += (iEnd - iStart) / 1e3; |
908 | | |
909 | 1 | if (GetThreadCount (pDecContext) >= 1) { |
910 | 0 | BufferingReadyPicture (pDecContext, ppDst, pDstInfo); |
911 | 0 | SET_EVENT (&m_sBufferingEvent); |
912 | 1 | } else { |
913 | 1 | ReorderPicturesInDisplay (pDecContext, ppDst, pDstInfo); |
914 | 1 | } |
915 | 1 | return dsErrorFree; |
916 | 2 | } |
917 | | |
918 | | DECODING_STATE CWelsDecoder::DecodeFrame2 (const unsigned char* kpSrc, |
919 | | const int kiSrcLen, |
920 | | unsigned char** ppDst, |
921 | 2 | SBufferInfo* pDstInfo) { |
922 | 2 | PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx; |
923 | 2 | return DecodeFrame2WithCtx (pDecContext, kpSrc, kiSrcLen, ppDst, pDstInfo); |
924 | 2 | } |
925 | | |
926 | | DECODING_STATE CWelsDecoder::FlushFrame (unsigned char** ppDst, |
927 | 0 | SBufferInfo* pDstInfo) { |
928 | 0 | bool bEndOfStreamFlag = true; |
929 | 0 | if (m_iThreadCount <= 1) { |
930 | 0 | for (int32_t j = 0; j < m_iCtxCount; ++j) { |
931 | 0 | if (!m_pDecThrCtx[j].pCtx->bEndOfStreamFlag) { |
932 | 0 | bEndOfStreamFlag = false; |
933 | 0 | } |
934 | 0 | } |
935 | 0 | } |
936 | 0 | if (bEndOfStreamFlag && m_sReoderingStatus.iNumOfPicts > 0) { |
937 | 0 | if (!m_sReoderingStatus.bHasBSlice) { |
938 | 0 | ReleaseBufferedReadyPictureNoReorder (NULL, ppDst, pDstInfo); |
939 | 0 | } |
940 | 0 | else { |
941 | 0 | ReleaseBufferedReadyPictureReorder (NULL, ppDst, pDstInfo, true); |
942 | 0 | } |
943 | 0 | } |
944 | 0 | return dsErrorFree; |
945 | 0 | } |
946 | | |
947 | 1 | void CWelsDecoder::OutputStatisticsLog (SDecoderStatistics& sDecoderStatistics) { |
948 | 1 | if ((sDecoderStatistics.uiDecodedFrameCount > 0) && (sDecoderStatistics.iStatisticsLogInterval > 0) |
949 | 0 | && ((sDecoderStatistics.uiDecodedFrameCount % sDecoderStatistics.iStatisticsLogInterval) == 0)) { |
950 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, |
951 | 0 | "DecoderStatistics: uiWidth=%d, uiHeight=%d, fAverageFrameSpeedInMs=%.1f, fActualAverageFrameSpeedInMs=%.1f, \ |
952 | 0 | uiDecodedFrameCount=%d, uiResolutionChangeTimes=%d, uiIDRCorrectNum=%d, \ |
953 | 0 | uiAvgEcRatio=%d, uiAvgEcPropRatio=%d, uiEcIDRNum=%d, uiEcFrameNum=%d, \ |
954 | 0 | uiIDRLostNum=%d, uiFreezingIDRNum=%d, uiFreezingNonIDRNum=%d, iAvgLumaQp=%d, \ |
955 | 0 | iSpsReportErrorNum=%d, iSubSpsReportErrorNum=%d, iPpsReportErrorNum=%d, iSpsNoExistNalNum=%d, iSubSpsNoExistNalNum=%d, iPpsNoExistNalNum=%d, \ |
956 | 0 | uiProfile=%d, uiLevel=%d, \ |
957 | 0 | iCurrentActiveSpsId=%d, iCurrentActivePpsId=%d,", |
958 | 0 | sDecoderStatistics.uiWidth, |
959 | 0 | sDecoderStatistics.uiHeight, |
960 | 0 | sDecoderStatistics.fAverageFrameSpeedInMs, |
961 | 0 | sDecoderStatistics.fActualAverageFrameSpeedInMs, |
962 | |
|
963 | 0 | sDecoderStatistics.uiDecodedFrameCount, |
964 | 0 | sDecoderStatistics.uiResolutionChangeTimes, |
965 | 0 | sDecoderStatistics.uiIDRCorrectNum, |
966 | |
|
967 | 0 | sDecoderStatistics.uiAvgEcRatio, |
968 | 0 | sDecoderStatistics.uiAvgEcPropRatio, |
969 | 0 | sDecoderStatistics.uiEcIDRNum, |
970 | 0 | sDecoderStatistics.uiEcFrameNum, |
971 | |
|
972 | 0 | sDecoderStatistics.uiIDRLostNum, |
973 | 0 | sDecoderStatistics.uiFreezingIDRNum, |
974 | 0 | sDecoderStatistics.uiFreezingNonIDRNum, |
975 | 0 | sDecoderStatistics.iAvgLumaQp, |
976 | |
|
977 | 0 | sDecoderStatistics.iSpsReportErrorNum, |
978 | 0 | sDecoderStatistics.iSubSpsReportErrorNum, |
979 | 0 | sDecoderStatistics.iPpsReportErrorNum, |
980 | 0 | sDecoderStatistics.iSpsNoExistNalNum, |
981 | 0 | sDecoderStatistics.iSubSpsNoExistNalNum, |
982 | 0 | sDecoderStatistics.iPpsNoExistNalNum, |
983 | |
|
984 | 0 | sDecoderStatistics.uiProfile, |
985 | 0 | sDecoderStatistics.uiLevel, |
986 | |
|
987 | 0 | sDecoderStatistics.iCurrentActiveSpsId, |
988 | 0 | sDecoderStatistics.iCurrentActivePpsId); |
989 | 0 | } |
990 | 1 | } |
991 | | |
992 | | void CWelsDecoder::BufferingReadyPicture (PWelsDecoderContext pCtx, unsigned char** ppDst, |
993 | 0 | SBufferInfo* pDstInfo) { |
994 | 0 | if (pDstInfo->iBufferStatus == 0) { |
995 | 0 | return; |
996 | 0 | } |
997 | 0 | m_bIsBaseline = pCtx->pSps->uiProfileIdc == 66 || pCtx->pSps->uiProfileIdc == 83; |
998 | 0 | if (!m_bIsBaseline) { |
999 | 0 | if (pCtx->pSliceHeader->eSliceType == B_SLICE) { |
1000 | 0 | m_sReoderingStatus.bHasBSlice = true; |
1001 | 0 | } |
1002 | 0 | } |
1003 | 0 | for (int32_t i = 0; i < 16; ++i) { |
1004 | 0 | if (m_sPictInfoList[i].iPOC == IMinInt32) { |
1005 | 0 | memcpy (&m_sPictInfoList[i].sBufferInfo, pDstInfo, sizeof (SBufferInfo)); |
1006 | 0 | m_sPictInfoList[i].iPOC = pCtx->pSliceHeader->iPicOrderCntLsb; |
1007 | 0 | m_sPictInfoList[i].iSeqNum = pCtx->iSeqNum; |
1008 | 0 | m_sPictInfoList[i].uiDecodingTimeStamp = pCtx->uiDecodingTimeStamp; |
1009 | 0 | if (pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb != NULL) { |
1010 | 0 | m_sPictInfoList[i].iPicBuffIdx = pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb->iPicBuffIdx; |
1011 | 0 | if (GetThreadCount (pCtx) <= 1) ++pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb->iRefCount; |
1012 | 0 | } |
1013 | 0 | m_iLastBufferedIdx = i; |
1014 | 0 | pDstInfo->iBufferStatus = 0; |
1015 | 0 | ++m_sReoderingStatus.iNumOfPicts; |
1016 | 0 | if (i > m_sReoderingStatus.iLargestBufferedPicIndex) { |
1017 | 0 | m_sReoderingStatus.iLargestBufferedPicIndex = i; |
1018 | 0 | } |
1019 | 0 | break; |
1020 | 0 | } |
1021 | 0 | } |
1022 | 0 | } |
1023 | | |
1024 | | void CWelsDecoder::ReleaseBufferedReadyPictureReorder (PWelsDecoderContext pCtx, unsigned char** ppDst, |
1025 | 0 | SBufferInfo* pDstInfo, bool isFlush) { |
1026 | 0 | PPicBuff pPicBuff = pCtx ? pCtx->pPicBuff : m_pPicBuff; |
1027 | 0 | if (pCtx == NULL && m_iThreadCount <= 1) { |
1028 | 0 | pCtx = m_pDecThrCtx[0].pCtx; |
1029 | 0 | } |
1030 | 0 | if (m_sReoderingStatus.iNumOfPicts > 0) { |
1031 | 0 | m_sReoderingStatus.iMinPOC = IMinInt32; |
1032 | 0 | int32_t firstValidIdx = -1; |
1033 | 0 | for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) { |
1034 | 0 | if (m_sReoderingStatus.iMinPOC == IMinInt32 && m_sPictInfoList[i].iPOC > IMinInt32) { |
1035 | 0 | m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC; |
1036 | 0 | m_sReoderingStatus.iMinSeqNum = m_sPictInfoList[i].iSeqNum; |
1037 | 0 | m_sReoderingStatus.iPictInfoIndex = i; |
1038 | 0 | firstValidIdx = i; |
1039 | 0 | break; |
1040 | 0 | } |
1041 | 0 | } |
1042 | 0 | for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) { |
1043 | 0 | if (i == firstValidIdx) continue; |
1044 | 0 | if (m_sPictInfoList[i].iPOC > IMinInt32 |
1045 | 0 | && ((m_sPictInfoList[i].iSeqNum == m_sReoderingStatus.iMinSeqNum) ? (m_sPictInfoList[i].iPOC < m_sReoderingStatus.iMinPOC) : (m_sPictInfoList[i].iSeqNum - m_sReoderingStatus.iMinSeqNum < 0))) { |
1046 | 0 | m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC; |
1047 | 0 | m_sReoderingStatus.iMinSeqNum = m_sPictInfoList[i].iSeqNum; |
1048 | 0 | m_sReoderingStatus.iPictInfoIndex = i; |
1049 | 0 | } |
1050 | 0 | } |
1051 | 0 | } |
1052 | 0 | if (m_sReoderingStatus.iMinPOC > IMinInt32) { |
1053 | 0 | bool isReady = true; |
1054 | 0 | if (!isFlush) { |
1055 | 0 | int32_t iLastPOC = pCtx != NULL ? pCtx->pSliceHeader->iPicOrderCntLsb : m_sPictInfoList[m_iLastBufferedIdx].iPOC; |
1056 | 0 | int32_t iLastSeqNum = pCtx != NULL ? pCtx->iSeqNum : m_sPictInfoList[m_iLastBufferedIdx].iSeqNum; |
1057 | 0 | isReady = (m_sReoderingStatus.iLastWrittenPOC > IMinInt32 |
1058 | 0 | && m_sReoderingStatus.iMinPOC - m_sReoderingStatus.iLastWrittenPOC <= 1) |
1059 | 0 | || m_sReoderingStatus.iMinPOC < iLastPOC |
1060 | 0 | || m_sReoderingStatus.iMinSeqNum - iLastSeqNum < 0; |
1061 | 0 | } |
1062 | 0 | if (isReady) { |
1063 | 0 | m_sReoderingStatus.iLastWrittenPOC = m_sReoderingStatus.iMinPOC; |
1064 | 0 | m_sReoderingStatus.iLastWrittenSeqNum = m_sReoderingStatus.iMinSeqNum; |
1065 | | #if defined (_DEBUG) |
1066 | | #ifdef _MOTION_VECTOR_DUMP_ |
1067 | | fprintf (stderr, "Output POC: #%d uiDecodingTimeStamp=%d\n", m_sReoderingStatus.iLastWrittenPOC, |
1068 | | m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].uiDecodingTimeStamp); |
1069 | | #endif |
1070 | | #endif |
1071 | 0 | memcpy (pDstInfo, &m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].sBufferInfo, sizeof (SBufferInfo)); |
1072 | 0 | ppDst[0] = pDstInfo->pDst[0]; |
1073 | 0 | ppDst[1] = pDstInfo->pDst[1]; |
1074 | 0 | ppDst[2] = pDstInfo->pDst[2]; |
1075 | 0 | m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32; |
1076 | 0 | int32_t iPicBuffIdx = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx; |
1077 | 0 | if (pPicBuff != NULL) { |
1078 | 0 | if (iPicBuffIdx >= 0 && iPicBuffIdx < pPicBuff->iCapacity) |
1079 | 0 | { |
1080 | 0 | PPicture pPic = pPicBuff->ppPic[iPicBuffIdx]; |
1081 | 0 | --pPic->iRefCount; |
1082 | 0 | if (pPic->iRefCount <= 0 && pPic->pSetUnRef) |
1083 | 0 | pPic->pSetUnRef(pPic); |
1084 | 0 | } |
1085 | 0 | } |
1086 | 0 | m_sReoderingStatus.iMinPOC = IMinInt32; |
1087 | 0 | --m_sReoderingStatus.iNumOfPicts; |
1088 | 0 | } |
1089 | 0 | } |
1090 | 0 | } |
1091 | | |
1092 | | //if there is no b-frame, no ordering based on values of POCs is necessary. |
1093 | | //The function is added to force to avoid picture reordering because some h.264 streams do not follow H.264 POC specifications. |
1094 | | void CWelsDecoder::ReleaseBufferedReadyPictureNoReorder(PWelsDecoderContext pCtx, unsigned char** ppDst, SBufferInfo* pDstInfo) |
1095 | 0 | { |
1096 | 0 | int32_t firstValidIdx = -1; |
1097 | 0 | uint32_t uiDecodingTimeStamp = 0; |
1098 | 0 | for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) { |
1099 | 0 | if (m_sPictInfoList[i].iPOC != IMinInt32) { |
1100 | 0 | uiDecodingTimeStamp = m_sPictInfoList[i].uiDecodingTimeStamp; |
1101 | 0 | m_sReoderingStatus.iPictInfoIndex = i; |
1102 | 0 | firstValidIdx = i; |
1103 | 0 | break; |
1104 | 0 | } |
1105 | 0 | } |
1106 | 0 | for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) { |
1107 | 0 | if (i == firstValidIdx) continue; |
1108 | 0 | if (m_sPictInfoList[i].iPOC != IMinInt32 && m_sPictInfoList[i].uiDecodingTimeStamp < uiDecodingTimeStamp) { |
1109 | 0 | uiDecodingTimeStamp = m_sPictInfoList[i].uiDecodingTimeStamp; |
1110 | 0 | m_sReoderingStatus.iPictInfoIndex = i; |
1111 | 0 | } |
1112 | 0 | } |
1113 | 0 | if (uiDecodingTimeStamp > 0) { |
1114 | | #if defined (_DEBUG) |
1115 | | #ifdef _MOTION_VECTOR_DUMP_ |
1116 | | fprintf(stderr, "Output POC: #%d uiDecodingTimeStamp=%d\n", m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC, |
1117 | | uiDecodingTimeStamp); |
1118 | | #endif |
1119 | | #endif |
1120 | 0 | m_sReoderingStatus.iLastWrittenPOC = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC; |
1121 | 0 | m_sReoderingStatus.iLastWrittenSeqNum = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iSeqNum; |
1122 | 0 | memcpy(pDstInfo, &m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].sBufferInfo, sizeof(SBufferInfo)); |
1123 | 0 | ppDst[0] = pDstInfo->pDst[0]; |
1124 | 0 | ppDst[1] = pDstInfo->pDst[1]; |
1125 | 0 | ppDst[2] = pDstInfo->pDst[2]; |
1126 | 0 | m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32; |
1127 | 0 | if (pCtx || m_pPicBuff) { |
1128 | 0 | PPicBuff pPicBuff = pCtx ? pCtx->pPicBuff : m_pPicBuff; |
1129 | 0 | PPicture pPic = pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]; |
1130 | 0 | --pPic->iRefCount; |
1131 | 0 | if (pPic->iRefCount <= 0 && pPic->pSetUnRef) |
1132 | 0 | pPic->pSetUnRef(pPic); |
1133 | 0 | } |
1134 | 0 | --m_sReoderingStatus.iNumOfPicts; |
1135 | 0 | } |
1136 | 0 | return; |
1137 | 0 | } |
1138 | | |
1139 | | DECODING_STATE CWelsDecoder::ReorderPicturesInDisplay(PWelsDecoderContext pDecContext, unsigned char** ppDst, |
1140 | 2 | SBufferInfo* pDstInfo) { |
1141 | 2 | DECODING_STATE iRet = dsErrorFree; |
1142 | 2 | if (pDecContext->pSps != NULL) { |
1143 | 0 | m_bIsBaseline = pDecContext->pSps->uiProfileIdc == 66 || pDecContext->pSps->uiProfileIdc == 83; |
1144 | 0 | if (!m_bIsBaseline) { |
1145 | 0 | if (pDstInfo->iBufferStatus == 1) { |
1146 | 0 | if (pDecContext->pSliceHeader->eSliceType == B_SLICE && |
1147 | 0 | ((pDecContext->iSeqNum == m_sReoderingStatus.iLastWrittenSeqNum) ? |
1148 | 0 | (pDecContext->pSliceHeader->iPicOrderCntLsb <= m_sReoderingStatus.iLastWrittenPOC + 2) : |
1149 | 0 | (pDecContext->iSeqNum - m_sReoderingStatus.iLastWrittenSeqNum == 1 && pDecContext->pSliceHeader->iPicOrderCntLsb == 0))) { |
1150 | 0 | m_sReoderingStatus.iLastWrittenPOC = pDecContext->pSliceHeader->iPicOrderCntLsb; |
1151 | 0 | m_sReoderingStatus.iLastWrittenSeqNum = pDecContext->iSeqNum; |
1152 | | //issue #3478, use b-slice type to determine correct picture order as the first priority as POC order is not as reliable as based on b-slice |
1153 | 0 | ppDst[0] = pDstInfo->pDst[0]; |
1154 | 0 | ppDst[1] = pDstInfo->pDst[1]; |
1155 | 0 | ppDst[2] = pDstInfo->pDst[2]; |
1156 | | #if defined (_DEBUG) |
1157 | | #ifdef _MOTION_VECTOR_DUMP_ |
1158 | | fprintf (stderr, "Output POC: #%d uiDecodingTimeStamp=%d\n", pDecContext->pSliceHeader->iPicOrderCntLsb, |
1159 | | pDecContext->uiDecodingTimeStamp); |
1160 | | #endif |
1161 | | #endif |
1162 | 0 | return iRet; |
1163 | 0 | } |
1164 | 0 | BufferingReadyPicture(pDecContext, ppDst, pDstInfo); |
1165 | 0 | if (!m_sReoderingStatus.bHasBSlice && m_sReoderingStatus.iNumOfPicts > 1) { |
1166 | 0 | ReleaseBufferedReadyPictureNoReorder (pDecContext, ppDst, pDstInfo); |
1167 | 0 | } |
1168 | 0 | else { |
1169 | 0 | ReleaseBufferedReadyPictureReorder (pDecContext, ppDst, pDstInfo); |
1170 | 0 | } |
1171 | 0 | } |
1172 | 0 | } |
1173 | 0 | } |
1174 | 2 | return iRet; |
1175 | 2 | } |
1176 | | |
1177 | 0 | DECODING_STATE CWelsDecoder::DecodeParser (const unsigned char* kpSrc, const int kiSrcLen, SParserBsInfo* pDstInfo) { |
1178 | 0 | PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx; |
1179 | |
|
1180 | 0 | if (pDecContext == NULL || pDecContext->pParam == NULL) { |
1181 | 0 | if (m_pWelsTrace != NULL) { |
1182 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "Call DecodeParser without Initialize.\n"); |
1183 | 0 | } |
1184 | 0 | return dsInitialOptExpected; |
1185 | 0 | } |
1186 | | |
1187 | 0 | if (!pDecContext->pParam->bParseOnly) { |
1188 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "bParseOnly should be true for this API calling! \n"); |
1189 | 0 | pDecContext->iErrorCode |= dsInvalidArgument; |
1190 | 0 | return dsInvalidArgument; |
1191 | 0 | } |
1192 | 0 | int64_t iEnd, iStart = WelsTime(); |
1193 | 0 | if (CheckBsBuffer (pDecContext, kiSrcLen)) { |
1194 | 0 | if (ResetDecoder (pDecContext)) |
1195 | 0 | return dsOutOfMemory; |
1196 | | |
1197 | 0 | return dsErrorFree; |
1198 | 0 | } |
1199 | 0 | if (kiSrcLen > 0 && kpSrc != NULL) { |
1200 | | #ifdef OUTPUT_BITSTREAM |
1201 | | if (m_pFBS) { |
1202 | | WelsFwrite (kpSrc, sizeof (unsigned char), kiSrcLen, m_pFBS); |
1203 | | WelsFflush (m_pFBS); |
1204 | | } |
1205 | | #endif//OUTPUT_BIT_STREAM |
1206 | 0 | pDecContext->bEndOfStreamFlag = false; |
1207 | 0 | } else { |
1208 | | //For application MODE, the error detection should be added for safe. |
1209 | | //But for CONSOLE MODE, when decoding LAST AU, kiSrcLen==0 && kpSrc==NULL. |
1210 | 0 | pDecContext->bEndOfStreamFlag = true; |
1211 | 0 | pDecContext->bInstantDecFlag = true; |
1212 | 0 | } |
1213 | |
|
1214 | 0 | pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding. |
1215 | 0 | pDecContext->pParam->eEcActiveIdc = ERROR_CON_DISABLE; //add protection to disable EC here. |
1216 | 0 | pDecContext->iFeedbackNalRefIdc = -1; //initialize |
1217 | 0 | if (!pDecContext->bFramePending) { //frame complete |
1218 | 0 | pDecContext->pParserBsInfo->iNalNum = 0; |
1219 | 0 | memset (pDecContext->pParserBsInfo->pNalLenInByte, 0, MAX_NAL_UNITS_IN_LAYER); |
1220 | 0 | } |
1221 | 0 | pDstInfo->iNalNum = 0; |
1222 | 0 | pDstInfo->iSpsWidthInPixel = pDstInfo->iSpsHeightInPixel = 0; |
1223 | 0 | if (pDstInfo) { |
1224 | 0 | pDecContext->uiTimeStamp = pDstInfo->uiInBsTimeStamp; |
1225 | 0 | pDstInfo->uiOutBsTimeStamp = 0; |
1226 | 0 | } else { |
1227 | 0 | pDecContext->uiTimeStamp = 0; |
1228 | 0 | } |
1229 | 0 | WelsDecodeBs (pDecContext, kpSrc, kiSrcLen, NULL, NULL, pDstInfo); |
1230 | 0 | if (pDecContext->iErrorCode & dsOutOfMemory) { |
1231 | 0 | if (ResetDecoder (pDecContext)) |
1232 | 0 | return dsOutOfMemory; |
1233 | 0 | return dsErrorFree; |
1234 | 0 | } |
1235 | | |
1236 | 0 | if (!pDecContext->bFramePending && pDecContext->pParserBsInfo->iNalNum) { |
1237 | 0 | memcpy (pDstInfo, pDecContext->pParserBsInfo, sizeof (SParserBsInfo)); |
1238 | |
|
1239 | 0 | if (pDecContext->iErrorCode == ERR_NONE) { //update statistics: decoding frame count |
1240 | 0 | pDecContext->pDecoderStatistics->uiDecodedFrameCount++; |
1241 | 0 | if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t |
1242 | 0 | ResetDecStatNums (pDecContext->pDecoderStatistics); |
1243 | 0 | pDecContext->pDecoderStatistics->uiDecodedFrameCount++; |
1244 | 0 | } |
1245 | 0 | } |
1246 | 0 | } |
1247 | |
|
1248 | 0 | pDecContext->bInstantDecFlag = false; //reset no-delay flag |
1249 | |
|
1250 | 0 | if (pDecContext->iErrorCode && pDecContext->bPrintFrameErrorTraceFlag) { |
1251 | 0 | WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "decode failed, failure type:%d \n", pDecContext->iErrorCode); |
1252 | 0 | pDecContext->bPrintFrameErrorTraceFlag = false; |
1253 | 0 | } |
1254 | 0 | iEnd = WelsTime(); |
1255 | 0 | pDecContext->dDecTime += (iEnd - iStart) / 1e3; |
1256 | 0 | return (DECODING_STATE)pDecContext->iErrorCode; |
1257 | 0 | } |
1258 | | |
1259 | | DECODING_STATE CWelsDecoder::DecodeFrame (const unsigned char* kpSrc, |
1260 | | const int kiSrcLen, |
1261 | | unsigned char** ppDst, |
1262 | | int* pStride, |
1263 | | int& iWidth, |
1264 | 0 | int& iHeight) { |
1265 | 0 | DECODING_STATE eDecState = dsErrorFree; |
1266 | 0 | SBufferInfo DstInfo; |
1267 | |
|
1268 | 0 | memset (&DstInfo, 0, sizeof (SBufferInfo)); |
1269 | 0 | DstInfo.UsrData.sSystemBuffer.iStride[0] = pStride[0]; |
1270 | 0 | DstInfo.UsrData.sSystemBuffer.iStride[1] = pStride[1]; |
1271 | 0 | DstInfo.UsrData.sSystemBuffer.iWidth = iWidth; |
1272 | 0 | DstInfo.UsrData.sSystemBuffer.iHeight = iHeight; |
1273 | |
|
1274 | 0 | eDecState = DecodeFrame2 (kpSrc, kiSrcLen, ppDst, &DstInfo); |
1275 | 0 | if (eDecState == dsErrorFree) { |
1276 | 0 | pStride[0] = DstInfo.UsrData.sSystemBuffer.iStride[0]; |
1277 | 0 | pStride[1] = DstInfo.UsrData.sSystemBuffer.iStride[1]; |
1278 | 0 | iWidth = DstInfo.UsrData.sSystemBuffer.iWidth; |
1279 | 0 | iHeight = DstInfo.UsrData.sSystemBuffer.iHeight; |
1280 | 0 | } |
1281 | |
|
1282 | 0 | return eDecState; |
1283 | 0 | } |
1284 | | |
1285 | | DECODING_STATE CWelsDecoder::DecodeFrameEx (const unsigned char* kpSrc, |
1286 | | const int kiSrcLen, |
1287 | | unsigned char* pDst, |
1288 | | int iDstStride, |
1289 | | int& iDstLen, |
1290 | | int& iWidth, |
1291 | | int& iHeight, |
1292 | 0 | int& iColorFormat) { |
1293 | 0 | DECODING_STATE state = dsErrorFree; |
1294 | |
|
1295 | 0 | return state; |
1296 | 0 | } |
1297 | | |
1298 | 0 | DECODING_STATE CWelsDecoder::ParseAccessUnit (SWelsDecoderThreadCTX& sThreadCtx) { |
1299 | 0 | sThreadCtx.pCtx->bHasNewSps = false; |
1300 | 0 | sThreadCtx.pCtx->bParamSetsLostFlag = m_bParamSetsLostFlag; |
1301 | 0 | sThreadCtx.pCtx->bFreezeOutput = m_bFreezeOutput; |
1302 | 0 | sThreadCtx.pCtx->uiDecodingTimeStamp = ++m_uiDecodeTimeStamp; |
1303 | 0 | bool bPicBuffChanged = false; |
1304 | 0 | if (m_pLastDecThrCtx != NULL && sThreadCtx.pCtx->sSpsPpsCtx.iSeqId < m_pLastDecThrCtx->pCtx->sSpsPpsCtx.iSeqId) { |
1305 | 0 | CopySpsPps (m_pLastDecThrCtx->pCtx, sThreadCtx.pCtx); |
1306 | 0 | sThreadCtx.pCtx->iPicQueueNumber = m_pLastDecThrCtx->pCtx->iPicQueueNumber; |
1307 | 0 | if (sThreadCtx.pCtx->pPicBuff != m_pPicBuff) { |
1308 | 0 | bPicBuffChanged = true; |
1309 | 0 | sThreadCtx.pCtx->pPicBuff = m_pPicBuff; |
1310 | 0 | sThreadCtx.pCtx->bHaveGotMemory = m_pPicBuff != NULL; |
1311 | 0 | sThreadCtx.pCtx->iImgWidthInPixel = m_pLastDecThrCtx->pCtx->iImgWidthInPixel; |
1312 | 0 | sThreadCtx.pCtx->iImgHeightInPixel = m_pLastDecThrCtx->pCtx->iImgHeightInPixel; |
1313 | 0 | } |
1314 | 0 | } |
1315 | | |
1316 | | //if threadCount > 1, then each thread must contain exact one complete frame. |
1317 | 0 | if (GetThreadCount (sThreadCtx.pCtx) > 1) { |
1318 | 0 | sThreadCtx.pCtx->pAccessUnitList->uiAvailUnitsNum = 0; |
1319 | 0 | sThreadCtx.pCtx->pAccessUnitList->uiActualUnitsNum = 0; |
1320 | 0 | } |
1321 | |
|
1322 | 0 | int32_t iRet = DecodeFrame2WithCtx (sThreadCtx.pCtx, sThreadCtx.kpSrc, sThreadCtx.kiSrcLen, sThreadCtx.ppDst, |
1323 | 0 | &sThreadCtx.sDstInfo); |
1324 | |
|
1325 | 0 | int32_t iErr = InitConstructAccessUnit (sThreadCtx.pCtx, &sThreadCtx.sDstInfo); |
1326 | 0 | if (ERR_NONE != iErr) { |
1327 | 0 | return (DECODING_STATE) (iRet | iErr); |
1328 | 0 | } |
1329 | 0 | if (sThreadCtx.pCtx->bNewSeqBegin) { |
1330 | 0 | m_pPicBuff = sThreadCtx.pCtx->pPicBuff; |
1331 | 0 | } else if (bPicBuffChanged) { |
1332 | 0 | InitialDqLayersContext (sThreadCtx.pCtx, sThreadCtx.pCtx->pSps->iMbWidth << 4, sThreadCtx.pCtx->pSps->iMbHeight << 4); |
1333 | 0 | } |
1334 | 0 | if (!sThreadCtx.pCtx->bNewSeqBegin && m_pLastDecThrCtx != NULL) { |
1335 | 0 | sThreadCtx.pCtx->sFrameCrop = m_pLastDecThrCtx->pCtx->pSps->sFrameCrop; |
1336 | 0 | } |
1337 | 0 | m_bParamSetsLostFlag = sThreadCtx.pCtx->bNewSeqBegin ? false : sThreadCtx.pCtx->bParamSetsLostFlag; |
1338 | 0 | m_bFreezeOutput = sThreadCtx.pCtx->bNewSeqBegin ? false : sThreadCtx.pCtx->bFreezeOutput; |
1339 | 0 | return (DECODING_STATE)iErr; |
1340 | 0 | } |
1341 | | /* |
1342 | | * Run decoding picture in separate thread. |
1343 | | */ |
1344 | | |
1345 | | int CWelsDecoder::ThreadDecodeFrameInternal (const unsigned char* kpSrc, const int kiSrcLen, unsigned char** ppDst, |
1346 | 0 | SBufferInfo* pDstInfo) { |
1347 | 0 | int state = dsErrorFree; |
1348 | 0 | int32_t i, j; |
1349 | 0 | int32_t signal = 0; |
1350 | | |
1351 | | //serial using of threads |
1352 | 0 | if (m_DecCtxActiveCount < m_iThreadCount) { |
1353 | 0 | signal = m_DecCtxActiveCount; |
1354 | 0 | } else { |
1355 | 0 | signal = m_pDecThrCtxActive[0]->sThreadInfo.uiThrNum; |
1356 | 0 | } |
1357 | |
|
1358 | 0 | WAIT_SEMAPHORE (&m_pDecThrCtx[signal].sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); |
1359 | |
|
1360 | 0 | for (i = 0; i < m_DecCtxActiveCount; ++i) { |
1361 | 0 | if (m_pDecThrCtxActive[i] == &m_pDecThrCtx[signal]) { |
1362 | 0 | m_pDecThrCtxActive[i] = NULL; |
1363 | 0 | for (j = i; j < m_DecCtxActiveCount - 1; j++) { |
1364 | 0 | m_pDecThrCtxActive[j] = m_pDecThrCtxActive[j + 1]; |
1365 | 0 | m_pDecThrCtxActive[j + 1] = NULL; |
1366 | 0 | } |
1367 | 0 | --m_DecCtxActiveCount; |
1368 | 0 | break; |
1369 | 0 | } |
1370 | 0 | } |
1371 | |
|
1372 | 0 | m_pDecThrCtxActive[m_DecCtxActiveCount++] = &m_pDecThrCtx[signal]; |
1373 | 0 | if (m_pLastDecThrCtx != NULL) { |
1374 | 0 | m_pDecThrCtx[signal].pCtx->pLastThreadCtx = m_pLastDecThrCtx; |
1375 | 0 | } |
1376 | 0 | m_pDecThrCtx[signal].kpSrc = const_cast<uint8_t*> (kpSrc); |
1377 | 0 | m_pDecThrCtx[signal].kiSrcLen = kiSrcLen; |
1378 | 0 | m_pDecThrCtx[signal].ppDst = ppDst; |
1379 | 0 | memcpy (&m_pDecThrCtx[signal].sDstInfo, pDstInfo, sizeof (SBufferInfo)); |
1380 | |
|
1381 | 0 | ParseAccessUnit (m_pDecThrCtx[signal]); |
1382 | 0 | if (m_iThreadCount > 1) { |
1383 | 0 | m_pLastDecThrCtx = &m_pDecThrCtx[signal]; |
1384 | 0 | } |
1385 | 0 | m_pDecThrCtx[signal].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_RUN; |
1386 | 0 | RELEASE_SEMAPHORE (&m_pDecThrCtx[signal].sThreadInfo.sIsActivated); |
1387 | | |
1388 | | // wait early picture |
1389 | 0 | if (m_DecCtxActiveCount >= m_iThreadCount) { |
1390 | 0 | WAIT_SEMAPHORE (&m_pDecThrCtxActive[0]->sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE); |
1391 | 0 | RELEASE_SEMAPHORE (&m_pDecThrCtxActive[0]->sThreadInfo.sIsIdle); |
1392 | 0 | } |
1393 | 0 | return state; |
1394 | 0 | } |
1395 | | |
1396 | | } // namespace WelsDec |
1397 | | |
1398 | | |
1399 | | using namespace WelsDec; |
1400 | | /* |
1401 | | * WelsGetDecoderCapability |
1402 | | * @return: DecCapability information |
1403 | | */ |
1404 | 0 | int WelsGetDecoderCapability (SDecoderCapability* pDecCapability) { |
1405 | 0 | memset (pDecCapability, 0, sizeof (SDecoderCapability)); |
1406 | 0 | pDecCapability->iProfileIdc = 66; //Baseline |
1407 | 0 | pDecCapability->iProfileIop = 0xE0; //11100000b |
1408 | 0 | pDecCapability->iLevelIdc = 32; //level_idc = 3.2 |
1409 | 0 | pDecCapability->iMaxMbps = 216000; //from level_idc = 3.2 |
1410 | 0 | pDecCapability->iMaxFs = 5120; //from level_idc = 3.2 |
1411 | 0 | pDecCapability->iMaxCpb = 20000; //from level_idc = 3.2 |
1412 | 0 | pDecCapability->iMaxDpb = 20480; //from level_idc = 3.2 |
1413 | 0 | pDecCapability->iMaxBr = 20000; //from level_idc = 3.2 |
1414 | 0 | pDecCapability->bRedPicCap = 0; //not support redundant pic |
1415 | |
|
1416 | 0 | return ERR_NONE; |
1417 | 0 | } |
1418 | | /* WINAPI is indeed in prefix due to sync to application layer callings!! */ |
1419 | | |
1420 | | /* |
1421 | | * WelsCreateDecoder |
1422 | | * @return: success in return 0, otherwise failed. |
1423 | | */ |
1424 | 33 | long WelsCreateDecoder (ISVCDecoder** ppDecoder) { |
1425 | | |
1426 | 33 | if (NULL == ppDecoder) { |
1427 | 0 | return ERR_INVALID_PARAMETERS; |
1428 | 0 | } |
1429 | | |
1430 | 33 | *ppDecoder = new CWelsDecoder(); |
1431 | | |
1432 | 33 | if (NULL == *ppDecoder) { |
1433 | 0 | return ERR_MALLOC_FAILED; |
1434 | 0 | } |
1435 | | |
1436 | 33 | return ERR_NONE; |
1437 | 33 | } |
1438 | | |
1439 | | /* |
1440 | | * WelsDestroyDecoder |
1441 | | */ |
1442 | 33 | void WelsDestroyDecoder (ISVCDecoder* pDecoder) { |
1443 | 33 | if (NULL != pDecoder) { |
1444 | 33 | delete (CWelsDecoder*)pDecoder; |
1445 | 33 | } |
1446 | 33 | } |