Tizen 2.0 Release
[framework/osp/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         __pCodecCtx = avcodec_alloc_context();
115         SysTryCatch(NID_MEDIA, __pCodecCtx != null, r = E_SYSTEM, E_SYSTEM,  "[%s] AVCODEC Context Allcoation Failed", GetErrorMessage(E_SYSTEM));\r
116         __pCodec = avcodec_find_decoder(CODEC_ID_H264);
117         SysTryCatch(NID_MEDIA, __pCodec != null, r = E_SYSTEM, E_SYSTEM, "[%s]  AVCODEC Find Decoder Failed for CODEC_ID_H264", GetErrorMessage(E_SYSTEM));\r
118
119         // AVCodecContext parameters
120         __pCodecCtx->debug_mv = 0; //set by user
121         __pCodecCtx->debug = 0; //set by user
122         __pCodecCtx->workaround_bugs = 1; //set by user
123         __pCodecCtx->lowres = 0; //set by user
124         __pCodecCtx->skip_frame = AVDISCARD_DEFAULT; //set by user
125         __pCodecCtx->skip_idct = AVDISCARD_DEFAULT; //set by user
126         __pCodecCtx->skip_loop_filter = AVDISCARD_DEFAULT; //set by user
127         __pCodecCtx->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK ;
128         __pCodecCtx->idct_algo = FF_IDCT_H264;
129         __pCodecCtx->strict_std_compliance = FF_COMPLIANCE_NORMAL;
130
131         if (__pCodec->capabilities & CODEC_CAP_TRUNCATED)
132         {
133                 __pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
134         }
135
136         res = avcodec_open(__pCodecCtx, __pCodec);
137         SysTryCatch(NID_MEDIA, res >= 0, r = E_SYSTEM, E_SYSTEM,
138                            "[%s] AVCODEC Codec Open Failed for CODEC_ID_H264, \
139                            result of avcodec_open() = %d ",
140                            GetErrorMessage(E_SYSTEM), res);\r
141
142         __pParser = av_parser_init(CODEC_ID_H264);
143         SysTryCatch(NID_MEDIA, __pParser != null, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] Construct Failed");
144
145         return r;
146
147 CATCH:
148         if (pMapEnum != null)
149         {
150                 delete pMapEnum;
151         }
152         if (__pCodecCtx != null)
153         {
154                 avcodec_close(__pCodecCtx);
155                 __pCodecCtx = null;
156                 __pCodec = null;
157         }
158         return r;
159 }
160
161 result
162 _H264Decoder::Decode(const byte* pSrcBuf, int& srcBufLength,
163                                          byte*& dstBuf, int& dstBufLength, bool& gotFrame)
164 {
165         result r = E_SUCCESS;
166         int res = 0;
167         AVFrame* pVideoFrame = null;
168         AVPacket inputPacket;
169         int gotPicture = 0;
170         int remainingBytes = 0;
171         int srcOffset = 0;
172         int i = 0;
173         int offset = 0;
174         int dataRead = 0;
175         uint8_t* pOutPtr = null;
176         int outDataSize = 0;
177         int64_t pts = 0;
178         int64_t dts = 0;
179         int64_t pos = 0;
180         int parsingOffSet = 0;
181
182         // TODO: add srcBufUsed, dstBufUsed param
183
184         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "The instance is in invalid state");\r
185         SysTryReturnResult(NID_MEDIA, (pSrcBuf != null && dstBuf != null && srcBufLength > 0 && dstBufLength > 0),
186                                            E_INVALID_ARG, "Invalid argument is used. The argument is not valid");
187         av_init_packet(&inputPacket);
188
189         pVideoFrame = avcodec_alloc_frame();
190         SysTryCatch(NID_MEDIA, (pVideoFrame != null), r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
191                            "[E_SYSTEM] alloc frame failed");
192
193         remainingBytes = srcBufLength;
194
195         while (remainingBytes && __inPacketMode == false)
196         {
197                 // Parsing the stream to extract the packets
198                 dataRead = av_parser_parse2(__pParser, __pCodecCtx, &pOutPtr,
199                                                                         &outDataSize, (uint8_t*) (pSrcBuf + parsingOffSet),
200                                                                         remainingBytes, pts, dts, pos);
201
202                 // Parsing offset needs to be updated to avoid infinite loop
203                 remainingBytes -= dataRead;
204                 parsingOffSet += dataRead;
205
206                 if ( outDataSize == 0 || pOutPtr == null)
207                 {
208                         continue; // it should not be in a block .
209                 }
210
211                 inputPacket.size = outDataSize;
212                 inputPacket.data = (uint8_t*) pOutPtr;
213
214                 // Decoding the video packet
215                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
216                 SysTryCatch(NID_MEDIA, res >= 0, r = E_INVALID_ARG, E_INVALID_ARG,
217                                    "[%s] The input data format is not supported \
218                                    result of avcodec_decode_video2 = %d",
219                                    GetErrorMessage(E_UNSUPPORTED_FORMAT), res);\r
220
221                 if (gotPicture) // Parsing of H264 packets done and got picture
222                 {
223                         // "Got Picture."
224                         if (res < srcBufLength)
225                         {
226                                 srcBufLength = srcBufLength - remainingBytes ;  //Input bytes used
227                         }
228                         goto GOTDECODEDFRAME;
229                 }
230
231         }
232
233         // The parser failed to read a frame from bit stream so changing decode mode to packet mode avoiding parsing
234         if ( __inPacketMode == false && (outDataSize == 0 || pOutPtr == null) && remainingBytes == 0)
235         {
236                         __inPacketMode = true;
237         }
238
239         if (__inPacketMode)
240         {
241                 inputPacket.size = srcBufLength;
242                 inputPacket.data = (uint8_t*) pSrcBuf;
243
244                 SysLog(NID_MEDIA, "Before Decode. inputPacket.size :: %d  ", srcBufLength);
245                 // Decoding the video packet
246                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
247                 SysTryCatch(NID_MEDIA, res >= 0, r = E_INVALID_ARG, E_INVALID_ARG,
248                                    "[%s] The input data format is not supported, \
249                                    result of avcodec_decode_video2() = %d", 
250                                    GetErrorMessage(E_UNSUPPORTED_FORMAT), res);\r
251                 if (gotPicture)
252                 {
253                         //  "Got Picture. "
254                         if (res <= srcBufLength)
255                         {
256                                 srcBufLength = res;  //Input bytes used
257                         }
258                         goto GOTDECODEDFRAME;
259                 }
260         }
261
262         if (!gotPicture)
263         {
264                 SysLog(NID_MEDIA, "Trying Again. Giving Empty Buffer");
265                 inputPacket.data = null;
266                 inputPacket.size = 0;
267                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
268                 SysTryCatch(NID_MEDIA, res >= 0, r = E_INVALID_ARG, E_INVALID_ARG,
269                                    "[%s] The input data format is not supported \
270                                    result of avcodec_decode_video2() = %d",
271                                    GetErrorMessage(E_UNSUPPORTED_FORMAT), res);\r
272         }
273
274 GOTDECODEDFRAME:
275
276         if (gotPicture)
277         {
278                 __decodeCalled = true;
279                 // "Got Picture. "
280                 if ( __pCodecCtx->pix_fmt == PIX_FMT_YUV420P ) // handling only  yuv 420 case
281                 {
282                         SysTryCatch(NID_MEDIA, dstBufLength >= (__pCodecCtx->height*__pCodecCtx->width*3)/2,
283                                            r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
284                                            "[%s] Decode Failed Insufficient destination buffer", GetErrorMessage(E_OUT_OF_MEMORY));\r
285                         for (i = 0; i < __pCodecCtx->height; i++)
286                         {
287                                 srcOffset = i * pVideoFrame->linesize[0];
288                                 offset = i * __pCodecCtx->width;
289                                 memcpy(dstBuf + offset, pVideoFrame->data[0] + srcOffset, __pCodecCtx->width);
290                         }
291                         for (i = 0; i < __pCodecCtx->height / 2; i++)
292                         {
293                                 srcOffset = i * pVideoFrame->linesize[1];
294                                 offset = (__pCodecCtx->height * __pCodecCtx->width) + (i * __pCodecCtx->width) / 2;
295                                 memcpy(dstBuf + offset, pVideoFrame->data[1] + srcOffset, __pCodecCtx->width / 2);
296                         }
297                         for (i = 0; i < __pCodecCtx->height / 2; i++)
298                         {
299                                 srcOffset = i * pVideoFrame->linesize[2];
300                                 offset = ((__pCodecCtx->height*__pCodecCtx->width*5)/4)+(i*__pCodecCtx->width)/ 2;
301                                 memcpy(dstBuf + offset, pVideoFrame->data[2] + srcOffset, __pCodecCtx->width / 2);
302                         }
303                         gotFrame = true;
304                         dstBufLength = (__pCodecCtx->height * __pCodecCtx->width * 3) / 2;
305                 }
306         }
307         else
308         {
309                 // "No Frame."
310                 gotFrame = false;
311         }
312
313
314         if (pVideoFrame != null)
315         {
316                 av_free(pVideoFrame);
317         }
318         return r;
319
320 CATCH:
321         if (pVideoFrame != null)
322         {
323                 av_free(pVideoFrame);
324         }
325         return r;
326 }
327
328 result
329 _H264Decoder::Probe(const byte* pSrcBuf, const int srcBufLength,
330                                         int& width, int& height, MediaPixelFormat& pixelFormat)
331 {
332         result r = E_SUCCESS;
333         int res = 0;
334         AVFrame* pVideoFrame = null;
335         AVPacket inputPacket;
336         uint8_t* pInputData = null;
337         int gotPicture = 0;
338         int remainingBytes = 0;
339         int dataRead = 0;
340         uint8_t *pOutPtr = null;
341         int outDataSize = 0;
342         int64_t pts = 0;
343         int64_t dts = 0;
344         int64_t pos = 0;
345         int parsingOffSet = 0;
346
347         // TODO: merge with Decode ?
348
349
350         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "The instance is in invalid state");\r
351         SysTryReturnResult(NID_MEDIA, (pSrcBuf != null && srcBufLength > 0), E_INVALID_ARG,
352                                           "pSrcBuf:0x%x %d", pSrcBuf, srcBufLength);
353
354         av_init_packet(&inputPacket);
355         inputPacket.size = srcBufLength + FF_INPUT_BUFFER_PADDING_SIZE;
356         pInputData = new (std::nothrow) byte[inputPacket.size + FF_INPUT_BUFFER_PADDING_SIZE];
357         pVideoFrame = avcodec_alloc_frame();
358         SysTryCatch(NID_MEDIA, (pInputData != null && pVideoFrame != null),
359                            r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
360                            "[E_SYSTEM] Input buffer allocation  Failed");
361         memcpy(pInputData, pSrcBuf, srcBufLength);
362         memset(pInputData + srcBufLength, 0, _INPUT_BUFFER_PADDING_SIZE);
363         inputPacket.data = (uint8_t*) pInputData;
364
365         // Decoding the video packet
366         remainingBytes = inputPacket.size;
367
368         do
369         {
370                 // Parsing the stream to extract the packets
371                 dataRead = av_parser_parse2(__pParser, __pCodecCtx, &pOutPtr, &outDataSize, (uint8_t*) (pSrcBuf + parsingOffSet),
372                                                                         remainingBytes, pts, dts, pos);
373
374                 // Parsing offset needs to be updated to avoid infinite loop
375                 remainingBytes -= dataRead;
376                 parsingOffSet += dataRead;
377
378                 if ( outDataSize == 0 || pOutPtr == null)
379                 {
380                         continue; // no block is required for this if statement.
381                 }
382
383                 inputPacket.size = outDataSize;
384                 inputPacket.data = (uint8_t*) pOutPtr;
385
386                 // Decoding the video packet
387                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
388                 SysTryCatch(NID_MEDIA, res >= 0, r = E_UNSUPPORTED_FORMAT, E_UNSUPPORTED_FORMAT,
389                                    "[%s] Invalid argument is used. The argument is not valid, \
390                                    result of avcodec_decode_video2 = %d",
391                                    GetErrorMessage(E_INVALID_ARG), res);\r
392
393                 if (gotPicture)
394                 {
395                         // "Got Picture. "
396                         if (__pCodecCtx->pix_fmt == PIX_FMT_YUV420P) // handling only  yuv 420 case
397                         {
398                                 width = __pCodecCtx->width;
399                                 height = __pCodecCtx->height;
400                                 pixelFormat = MEDIA_PIXEL_FORMAT_YUV420P;
401                         }
402                         break;
403                 }
404
405                 // All bytes of the frame are not consumed
406                 if (dataRead > res)
407                 {
408                         remainingBytes += dataRead - res;
409                         parsingOffSet -= dataRead - res;
410                 }
411         }
412         while (remainingBytes);
413
414         if (!gotPicture)
415         {
416                 inputPacket.data = null;
417                 inputPacket.size = 0;
418                 res = avcodec_decode_video2(__pCodecCtx, pVideoFrame, &gotPicture, &inputPacket);
419
420                 if (gotPicture && res>= 0)
421                 {
422                         // "Got Picture. "
423                         if (__pCodecCtx->pix_fmt == PIX_FMT_YUV420P) // handling only  yuv 420 case
424                         {
425                                 width = __pCodecCtx->width;
426                                 height = __pCodecCtx->height;
427                                 pixelFormat = MEDIA_PIXEL_FORMAT_YUV420P;
428                         }
429                 }
430                 else
431                 {
432                         r = E_UNSUPPORTED_FORMAT;
433                         // "No Frame. "
434                 }
435         }
436
437         delete[] pInputData;
438         av_free(pVideoFrame);
439
440         return r;
441
442 CATCH:
443         if (pInputData != null)
444         {
445                 delete[] pInputData;
446         }
447         if (pVideoFrame != null)
448         {
449                 av_free(pVideoFrame);
450         }
451         return r;
452 }
453
454 result
455 _H264Decoder::Reset(void)
456 {
457         result r = E_SUCCESS;
458
459         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "Not constructed");
460         avcodec_flush_buffers(__pCodecCtx);
461         __decodeCalled = false;
462         __inPacketMode = false;
463         return r;
464 }
465
466 result
467 _H264Decoder::GetValue(MediaPropertyType type, int& value) const
468 {
469         result r = E_SUCCESS;
470
471         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "Not constructed");
472         SysTryCatch(NID_MEDIA, __decodeCalled == true, r = E_INVALID_STATE, E_INVALID_STATE,
473                             "[%s] The instance is in invalid state", GetErrorMessage(E_INVALID_STATE));\r
474         SysTryCatch(NID_MEDIA, type>0, r = E_INVALID_ARG, E_INVALID_ARG,
475                            "[%s] Invalid argument is used. The argument is not valid", GetErrorMessage(E_INVALID_ARG));\r
476         SysTryCatch(NID_MEDIA, type >= MEDIA_PROPERTY_VIDEO_WIDTH && type <= MEDIA_PROPERTY_AUDIO_SAMPLE_RATE,
477                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,  "[%s] The instance is not available ", GetErrorMessage(E_OBJ_NOT_FOUND));\r
478
479         switch (type)
480         {
481         case MEDIA_PROPERTY_VIDEO_WIDTH:
482                 value = __pCodecCtx->width;
483         break;
484
485         case MEDIA_PROPERTY_VIDEO_HEIGHT:
486                 value = __pCodecCtx->height;
487         break;
488
489         case MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT:
490                 value = MEDIA_PIXEL_FORMAT_YUV420P;
491         break;
492
493         default:
494                 return E_OBJ_NOT_FOUND;
495         break;
496         }
497         return r;
498
499 CATCH:
500         return r;
501 }
502
503 result
504 _H264Decoder::GetValue(MediaPropertyType type, float& value) const
505 {
506         result r = E_SUCCESS;
507
508         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "Not constructed");
509         SysTryCatch(NID_MEDIA, __decodeCalled == true, r = E_INVALID_STATE, E_INVALID_STATE,
510                             "[%s] The instance is in invalid state", GetErrorMessage(E_INVALID_STATE));\r
511         SysTryCatch(NID_MEDIA, type>0, r = E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] GetValue Failed");
512         SysTryCatch(NID_MEDIA, type >= MEDIA_PROPERTY_VIDEO_WIDTH && type <= MEDIA_PROPERTY_AUDIO_SAMPLE_RATE,
513                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,
514                            "[%s] The instance is not available ", GetErrorMessage(E_OBJ_NOT_FOUND));\r
515
516         return E_OBJ_NOT_FOUND;
517
518 CATCH:
519         return r;
520 }
521
522 Tizen::Base::Collection::IListT<MediaPropertyType>*
523 _H264Decoder::GetSupportedPropertyListN(void) const
524 {
525         result r = E_SUCCESS;
526         ArrayListT<MediaPropertyType>* pPropList = new (std::nothrow) ArrayListT<MediaPropertyType>;
527
528         SysTryCatch(NID_MEDIA, pPropList != null, r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
529                            "[%s] Memory Allocation  Failed ", GetErrorMessage(E_OUT_OF_MEMORY));\r
530         pPropList->Add(MEDIA_PROPERTY_VIDEO_WIDTH);
531         pPropList->Add(MEDIA_PROPERTY_VIDEO_HEIGHT);
532         pPropList->Add(MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT);
533         SetLastResult(r);
534         return pPropList;
535
536 CATCH:
537         return null;
538 }
539
540 bool
541 _H264Decoder::IsPropertySupported(MediaPropertyType type) const
542 {
543         result r = E_SUCCESS;
544
545         SysTryReturnResult(NID_MEDIA, __pCodecCtx, E_INVALID_STATE, "Not constructed");
546         SysTryCatch(NID_MEDIA, (type == MEDIA_PROPERTY_VIDEO_WIDTH || type == MEDIA_PROPERTY_VIDEO_HEIGHT
547                                                    || type == MEDIA_PROPERTY_VIDEO_PIXEL_FORMAT ),
548                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,
549                             "[%s] The instance is not available ", GetErrorMessage(E_OBJ_NOT_FOUND));\r
550         SetLastResult(r);
551         return true;
552
553 CATCH:
554         return false;
555 }
556
557 }} // Tizen::Media