85b1986f58bfd55e68a00af3d3604d8de1628689
[platform/framework/native/media.git] / src / FMedia_H264Decoder.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 #include <FBaseColArrayListT.h>
19 #include <FBaseColHashMap.h>
20 #include <FBaseInteger.h>
21 #include <FMediaTypes.h>
22 #include <FBaseSysLog.h>
23 #include "FMedia_Ffmpeg.h"
24 #include "FMedia_IVideoDecoder.h"
25 #include "FMedia_H264Decoder.h"
26
27 using namespace Tizen::Base;
28 using namespace Tizen::Io;
29 using namespace Tizen::Base::Collection;
30
31 namespace Tizen { namespace Media
32 {
33
34 static const int _INPUT_BUFFER_PADDING_SIZE = 8;
35 static const int _ERROR_STRING_LENGTH = 256;
36
37 _IVideoDecoder*
38 _H264Decoder_CreateInstance(void)
39 {
40         return new (std::nothrow) _H264Decoder();
41 }
42
43 _H264Decoder::_H264Decoder(void)
44 {
45         __pCodecCtx = null;
46         __pCodec = null;
47         __decodeCalled = false;
48         __pParser = null;
49         __inPacketMode = false;
50 }
51
52 _H264Decoder::~_H264Decoder(void)
53 {
54         if (__pCodecCtx != null)
55         {
56                 avcodec_flush_buffers(__pCodecCtx);
57                 avcodec_close(__pCodecCtx);
58                 av_free(__pCodecCtx);
59                 av_parser_close(__pParser);
60                 __pCodecCtx = null;
61                 __pCodec = null;
62         }
63 }
64
65 result
66 _H264Decoder::Construct(const Tizen::Base::Collection::HashMap* pOption)
67 {
68         result r = E_SUCCESS;
69         int res = 0;
70         IMapEnumerator* pMapEnum = null;
71         Integer* pKey = null;
72         Integer* pValue = null;
73         int key = -1;
74         int value = -1;
75
76         SysAssertf((__pCodecCtx == null && __pCodec == null), " Already Constructed .");\r
77
78         if (pOption)
79         {
80                 // The initialization values are given in the Hashmap
81                 pMapEnum = pOption->GetMapEnumeratorN();
82                 if (pMapEnum != null)
83                 {
84                         while (pMapEnum->MoveNext() == E_SUCCESS)
85                         {
86                                 pKey = dynamic_cast<Integer*>(pMapEnum->GetKey());
87                                 pValue = dynamic_cast<Integer*>(pMapEnum->GetValue());
88
89                                 if (pKey && pValue)
90                                 {
91                                         key = pKey->ToInt();
92                                         value = pValue->ToInt();
93                                         switch (key)
94                                         {
95                                         case MEDIA_PROPERTY_VIDEO_H264_USE_ANNEX_B:
96                                                 SysTryCatch(NID_MEDIA, value == 0 || value == 1,
97                                                                    r = E_OUT_OF_RANGE, E_OUT_OF_RANGE,
98                                                                    "[%s] Invalid argument is used. The value is out of range", GetErrorMessage(E_OUT_OF_RANGE));\r
99                                                 break;
100
101                                         default:
102                                                 break;
103                                         }
104                                 }
105
106                         }
107                         delete pMapEnum;
108                         pMapEnum = null;
109                 }
110         }
111
112         avcodec_register_all();
113
114         __pCodec = avcodec_find_decoder(CODEC_ID_H264);
115         SysTryCatch(NID_MEDIA, __pCodec != null, r = E_SYSTEM, E_SYSTEM, "[%s]  AVCODEC Find Decoder Failed for CODEC_ID_H264", GetErrorMessage(E_SYSTEM));
116
117         __pCodecCtx = avcodec_alloc_context3(__pCodec);
118         SysTryCatch(NID_MEDIA, __pCodecCtx != null, r = E_SYSTEM, E_SYSTEM,  "[%s] AVCODEC Context Allcoation Failed", GetErrorMessage(E_SYSTEM));\r
119
120         // AVCodecContext parameters
121         __pCodecCtx->debug_mv = 0; //set by user
122         __pCodecCtx->debug = 0; //set by user
123         __pCodecCtx->workaround_bugs = 1; //set by user
124         __pCodecCtx->lowres = 0; //set by user
125         __pCodecCtx->skip_frame = AVDISCARD_DEFAULT; //set by user
126         __pCodecCtx->skip_idct = AVDISCARD_DEFAULT; //set by user
127         __pCodecCtx->skip_loop_filter = AVDISCARD_DEFAULT; //set by user
128         __pCodecCtx->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK ;
129         __pCodecCtx->idct_algo = FF_IDCT_H264;
130         __pCodecCtx->strict_std_compliance = FF_COMPLIANCE_NORMAL;
131
132         if (__pCodec->capabilities & CODEC_CAP_TRUNCATED)
133         {
134                 __pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
135         }
136
137         res = avcodec_open2(__pCodecCtx, __pCodec, null);
138         SysTryCatch(NID_MEDIA, res >= 0, r = E_SYSTEM, E_SYSTEM,
139                            "[%s] AVCODEC Codec Open Failed for CODEC_ID_H264, \
140                            result of avcodec_open() = %d ",
141                            GetErrorMessage(E_SYSTEM), res);\r
142
143         __pParser = av_parser_init(CODEC_ID_H264);
144         SysTryCatch(NID_MEDIA, __pParser != null, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] Construct Failed");
145
146         return r;
147
148 CATCH:
149         if (pMapEnum != null)
150         {
151                 delete pMapEnum;
152         }
153         if (__pCodecCtx != null)
154         {
155                 avcodec_close(__pCodecCtx);
156                 __pCodecCtx = null;
157                 __pCodec = null;
158         }
159         return r;
160 }
161
162 result
163 _H264Decoder::Decode(const byte* pSrcBuf, int& srcBufLength,
164                                          byte*& dstBuf, int& dstBufLength, bool& gotFrame)
165 {
166         result r = E_SUCCESS;
167         int res = 0;
168         AVFrame* pVideoFrame = null;
169         AVPacket inputPacket;
170         int gotPicture = 0;
171         int remainingBytes = 0;
172         int srcOffset = 0;
173         int i = 0;
174         int offset = 0;
175         int dataRead = 0;
176         uint8_t* pOutPtr = null;
177         int outDataSize = 0;
178         int64_t pts = 0;
179         int64_t dts = 0;
180         int64_t pos = 0;
181         int parsingOffSet = 0;
182
183         // TODO: add srcBufUsed, dstBufUsed param
184
185         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "The instance is in invalid state");\r
186         SysTryReturnResult(NID_MEDIA, (pSrcBuf != null && dstBuf != null && srcBufLength > 0 && dstBufLength > 0),
187                                            E_INVALID_ARG, "Invalid argument is used. The argument is not valid");
188         av_init_packet(&inputPacket);
189
190         pVideoFrame = avcodec_alloc_frame();
191         SysTryCatch(NID_MEDIA, (pVideoFrame != null), r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
192                            "[E_SYSTEM] alloc frame failed");
193
194         remainingBytes = srcBufLength;
195
196         while (remainingBytes && __inPacketMode == false)
197         {
198                 // Parsing the stream to extract the packets
199                 dataRead = av_parser_parse2(__pParser, __pCodecCtx, &pOutPtr,
200                                                                         &outDataSize, (uint8_t*) (pSrcBuf + parsingOffSet),
201                                                                         remainingBytes, pts, dts, pos);
202
203                 // Parsing offset needs to be updated to avoid infinite loop
204                 remainingBytes -= dataRead;
205                 parsingOffSet += dataRead;
206
207                 if ( outDataSize == 0 || pOutPtr == null)
208                 {
209                         continue; // it should not be in a block .
210                 }
211
212                 inputPacket.size = outDataSize;
213                 inputPacket.data = (uint8_t*) pOutPtr;
214
215                 // Decoding the video packet
216                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
217                 SysTryCatch(NID_MEDIA, res >= 0, r = E_INVALID_ARG, E_INVALID_ARG,
218                                    "[%s] The input data format is not supported \
219                                    result of avcodec_decode_video2 = %d",
220                                    GetErrorMessage(E_UNSUPPORTED_FORMAT), res);\r
221
222                 if (gotPicture) // Parsing of H264 packets done and got picture
223                 {
224                         // "Got Picture."
225                         if (res < srcBufLength)
226                         {
227                                 srcBufLength = srcBufLength - remainingBytes ;  //Input bytes used
228                         }
229                         goto GOTDECODEDFRAME;
230                 }
231
232         }
233
234         // The parser failed to read a frame from bit stream so changing decode mode to packet mode avoiding parsing
235         if ( __inPacketMode == false && (outDataSize == 0 || pOutPtr == null) && remainingBytes == 0)
236         {
237                         __inPacketMode = true;
238         }
239
240         if (__inPacketMode)
241         {
242                 inputPacket.size = srcBufLength;
243                 inputPacket.data = (uint8_t*) pSrcBuf;
244
245                 SysLog(NID_MEDIA, "Before Decode. inputPacket.size :: %d  ", srcBufLength);
246                 // Decoding the video packet
247                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
248                 SysTryCatch(NID_MEDIA, res >= 0, r = E_INVALID_ARG, E_INVALID_ARG,
249                                    "[%s] The input data format is not supported, \
250                                    result of avcodec_decode_video2() = %d", 
251                                    GetErrorMessage(E_UNSUPPORTED_FORMAT), res);\r
252                 if (gotPicture)
253                 {
254                         //  "Got Picture. "
255                         if (res <= srcBufLength)
256                         {
257                                 srcBufLength = res;  //Input bytes used
258                         }
259                         goto GOTDECODEDFRAME;
260                 }
261         }
262
263         if (!gotPicture)
264         {
265                 SysLog(NID_MEDIA, "Trying Again. Giving Empty Buffer");
266                 inputPacket.data = null;
267                 inputPacket.size = 0;
268                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
269                 SysTryCatch(NID_MEDIA, res >= 0, r = E_INVALID_ARG, E_INVALID_ARG,
270                                    "[%s] The input data format is not supported \
271                                    result of avcodec_decode_video2() = %d",
272                                    GetErrorMessage(E_UNSUPPORTED_FORMAT), res);\r
273         }
274
275 GOTDECODEDFRAME:
276
277         if (gotPicture)
278         {
279                 __decodeCalled = true;
280                 // "Got Picture. "
281                 if ( __pCodecCtx->pix_fmt == PIX_FMT_YUV420P ) // handling only  yuv 420 case
282                 {
283                         SysTryCatch(NID_MEDIA, dstBufLength >= (__pCodecCtx->height*__pCodecCtx->width*3)/2,
284                                            r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
285                                            "[%s] Decode Failed Insufficient destination buffer", GetErrorMessage(E_OUT_OF_MEMORY));\r
286                         for (i = 0; i < __pCodecCtx->height; i++)
287                         {
288                                 srcOffset = i * pVideoFrame->linesize[0];
289                                 offset = i * __pCodecCtx->width;
290                                 memcpy(dstBuf + offset, pVideoFrame->data[0] + srcOffset, __pCodecCtx->width);
291                         }
292                         for (i = 0; i < __pCodecCtx->height / 2; i++)
293                         {
294                                 srcOffset = i * pVideoFrame->linesize[1];
295                                 offset = (__pCodecCtx->height * __pCodecCtx->width) + (i * __pCodecCtx->width) / 2;
296                                 memcpy(dstBuf + offset, pVideoFrame->data[1] + srcOffset, __pCodecCtx->width / 2);
297                         }
298                         for (i = 0; i < __pCodecCtx->height / 2; i++)
299                         {
300                                 srcOffset = i * pVideoFrame->linesize[2];
301                                 offset = ((__pCodecCtx->height*__pCodecCtx->width*5)/4)+(i*__pCodecCtx->width)/ 2;
302                                 memcpy(dstBuf + offset, pVideoFrame->data[2] + srcOffset, __pCodecCtx->width / 2);
303                         }
304                         gotFrame = true;
305                         dstBufLength = (__pCodecCtx->height * __pCodecCtx->width * 3) / 2;
306                 }
307         }
308         else
309         {
310                 // "No Frame."
311                 gotFrame = false;
312         }
313
314
315         if (pVideoFrame != null)
316         {
317                 av_free(pVideoFrame);
318         }
319         return r;
320
321 CATCH:
322         if (pVideoFrame != null)
323         {
324                 av_free(pVideoFrame);
325         }
326         return r;
327 }
328
329 result
330 _H264Decoder::Probe(const byte* pSrcBuf, const int srcBufLength,
331                                         int& width, int& height, MediaPixelFormat& pixelFormat)
332 {
333         result r = E_SUCCESS;
334         int res = 0;
335         AVFrame* pVideoFrame = null;
336         AVPacket inputPacket;
337         uint8_t* pInputData = null;
338         int gotPicture = 0;
339         int remainingBytes = 0;
340         int dataRead = 0;
341         uint8_t *pOutPtr = null;
342         int outDataSize = 0;
343         int64_t pts = 0;
344         int64_t dts = 0;
345         int64_t pos = 0;
346         int parsingOffSet = 0;
347
348         // TODO: merge with Decode ?
349
350
351         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "The instance is in invalid state");\r
352         SysTryReturnResult(NID_MEDIA, (pSrcBuf != null && srcBufLength > 0), E_INVALID_ARG,
353                                           "pSrcBuf:0x%x %d", pSrcBuf, srcBufLength);
354
355         av_init_packet(&inputPacket);
356         inputPacket.size = srcBufLength + FF_INPUT_BUFFER_PADDING_SIZE;
357         pInputData = new (std::nothrow) byte[inputPacket.size + FF_INPUT_BUFFER_PADDING_SIZE];
358         pVideoFrame = avcodec_alloc_frame();
359         SysTryCatch(NID_MEDIA, (pInputData != null && pVideoFrame != null),
360                            r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
361                            "[E_SYSTEM] Input buffer allocation  Failed");
362         memcpy(pInputData, pSrcBuf, srcBufLength);
363         memset(pInputData + srcBufLength, 0, _INPUT_BUFFER_PADDING_SIZE);
364         inputPacket.data = (uint8_t*) pInputData;
365
366         // Decoding the video packet
367         remainingBytes = inputPacket.size;
368
369         do
370         {
371                 // Parsing the stream to extract the packets
372                 dataRead = av_parser_parse2(__pParser, __pCodecCtx, &pOutPtr, &outDataSize, (uint8_t*) (pSrcBuf + parsingOffSet),
373                                                                         remainingBytes, pts, dts, pos);
374
375                 // Parsing offset needs to be updated to avoid infinite loop
376                 remainingBytes -= dataRead;
377                 parsingOffSet += dataRead;
378
379                 if ( outDataSize == 0 || pOutPtr == null)
380                 {
381                         continue; // no block is required for this if statement.
382                 }
383
384                 inputPacket.size = outDataSize;
385                 inputPacket.data = (uint8_t*) pOutPtr;
386
387                 // Decoding the video packet
388                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
389                 SysTryCatch(NID_MEDIA, res >= 0, r = E_UNSUPPORTED_FORMAT, E_UNSUPPORTED_FORMAT,
390                                    "[%s] Invalid argument is used. The argument is not valid, \
391                                    result of avcodec_decode_video2 = %d",
392                                    GetErrorMessage(E_INVALID_ARG), res);\r
393
394                 if (gotPicture)
395                 {
396                         // "Got Picture. "
397                         if (__pCodecCtx->pix_fmt == PIX_FMT_YUV420P) // handling only  yuv 420 case
398                         {
399                                 width = __pCodecCtx->width;
400                                 height = __pCodecCtx->height;
401                                 pixelFormat = MEDIA_PIXEL_FORMAT_YUV420P;
402                         }
403                         break;
404                 }
405
406                 // All bytes of the frame are not consumed
407                 if (dataRead > res)
408                 {
409                         remainingBytes += dataRead - res;
410                         parsingOffSet -= dataRead - res;
411                 }
412         }
413         while (remainingBytes);
414
415         if (!gotPicture)
416         {
417                 inputPacket.data = null;
418                 inputPacket.size = 0;
419                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
420
421                 if (gotPicture && res>= 0)
422                 {
423                         // "Got Picture. "
424                         if (__pCodecCtx->pix_fmt == PIX_FMT_YUV420P) // handling only  yuv 420 case
425                         {
426                                 width = __pCodecCtx->width;
427                                 height = __pCodecCtx->height;
428                                 pixelFormat = MEDIA_PIXEL_FORMAT_YUV420P;
429                         }
430                 }
431                 else
432                 {
433                         r = E_UNSUPPORTED_FORMAT;
434                         // "No Frame. "
435                 }
436         }
437
438         delete[] pInputData;
439         av_free(pVideoFrame);
440
441         return r;
442
443 CATCH:
444         if (pInputData != null)
445         {
446                 delete[] pInputData;
447         }
448         if (pVideoFrame != null)
449         {
450                 av_free(pVideoFrame);
451         }
452         return r;
453 }
454
455 result
456 _H264Decoder::Reset(void)
457 {
458         result r = E_SUCCESS;
459
460         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "Not constructed");
461         avcodec_flush_buffers(__pCodecCtx);
462         __decodeCalled = false;
463         __inPacketMode = false;
464         return r;
465 }
466
467 result
468 _H264Decoder::GetValue(MediaPropertyType type, int& value) const
469 {
470         result r = E_SUCCESS;
471
472         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "Not constructed");
473         SysTryCatch(NID_MEDIA, __decodeCalled == true, r = E_INVALID_STATE, E_INVALID_STATE,
474                             "[%s] The instance is in invalid state", GetErrorMessage(E_INVALID_STATE));\r
475         SysTryCatch(NID_MEDIA, type>0, r = E_INVALID_ARG, E_INVALID_ARG,
476                            "[%s] Invalid argument is used. The argument is not valid", GetErrorMessage(E_INVALID_ARG));\r
477         SysTryCatch(NID_MEDIA, type >= MEDIA_PROPERTY_VIDEO_WIDTH && type <= MEDIA_PROPERTY_AUDIO_SAMPLE_RATE,
478                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,  "[%s] The instance is not available ", GetErrorMessage(E_OBJ_NOT_FOUND));\r
479
480         switch (type)
481         {
482         case MEDIA_PROPERTY_VIDEO_WIDTH:
483                 value = __pCodecCtx->width;
484         break;
485
486         case MEDIA_PROPERTY_VIDEO_HEIGHT:
487                 value = __pCodecCtx->height;
488         break;
489
490         case MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT:
491                 value = MEDIA_PIXEL_FORMAT_YUV420P;
492         break;
493
494         default:
495                 return E_OBJ_NOT_FOUND;
496         break;
497         }
498         return r;
499
500 CATCH:
501         return r;
502 }
503
504 result
505 _H264Decoder::GetValue(MediaPropertyType type, float& value) const
506 {
507         result r = E_SUCCESS;
508
509         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "Not constructed");
510         SysTryCatch(NID_MEDIA, __decodeCalled == true, r = E_INVALID_STATE, E_INVALID_STATE,
511                             "[%s] The instance is in invalid state", GetErrorMessage(E_INVALID_STATE));\r
512         SysTryCatch(NID_MEDIA, type>0, r = E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] GetValue Failed");
513         SysTryCatch(NID_MEDIA, type >= MEDIA_PROPERTY_VIDEO_WIDTH && type <= MEDIA_PROPERTY_AUDIO_SAMPLE_RATE,
514                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,
515                            "[%s] The instance is not available ", GetErrorMessage(E_OBJ_NOT_FOUND));\r
516
517         return E_OBJ_NOT_FOUND;
518
519 CATCH:
520         return r;
521 }
522
523 Tizen::Base::Collection::IListT<MediaPropertyType>*
524 _H264Decoder::GetSupportedPropertyListN(void) const
525 {
526         result r = E_SUCCESS;
527         ArrayListT<MediaPropertyType>* pPropList = new (std::nothrow) ArrayListT<MediaPropertyType>;
528
529         SysTryCatch(NID_MEDIA, pPropList != null, r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
530                            "[%s] Memory Allocation  Failed ", GetErrorMessage(E_OUT_OF_MEMORY));\r
531         pPropList->Add(MEDIA_PROPERTY_VIDEO_WIDTH);
532         pPropList->Add(MEDIA_PROPERTY_VIDEO_HEIGHT);
533         pPropList->Add(MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT);
534         SetLastResult(r);
535         return pPropList;
536
537 CATCH:
538         return null;
539 }
540
541 bool
542 _H264Decoder::IsPropertySupported(MediaPropertyType type) const
543 {
544         result r = E_SUCCESS;
545
546         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "Not constructed");
547         SysTryCatch(NID_MEDIA, (type == MEDIA_PROPERTY_VIDEO_WIDTH || type == MEDIA_PROPERTY_VIDEO_HEIGHT
548                                                    || type == MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT ),
549                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,
550                             "[%s] The instance is not available ", GetErrorMessage(E_OBJ_NOT_FOUND));\r
551         SetLastResult(r);
552         return true;
553
554 CATCH:
555         return false;
556 }
557
558 }} // Tizen::Media