Tizen 2.0 Release
[framework/osp/media.git] / src / FMedia_VorbisDecoder.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 <ogg.h>
19 #include <vorbis/codec.h>
20 #include <FBaseColArrayListT.h>
21 #include <FBaseColHashMap.h>
22 #include <FBaseColHashMapT.h>
23 #include <FBaseUtilMath.h>
24 #include <FMediaTypes.h>
25 #include <FMediaAudioTypes.h>
26 #include <FBaseSysLog.h>
27 #include "FMedia_VorbisDecoder.h"
28
29 using namespace Tizen::Base;
30 using namespace Tizen::Io;
31 using namespace Tizen::Base::Collection;
32 using namespace Tizen::Base::Utility;
33
34 namespace Tizen { namespace Media
35 {
36
37 class _OggStream
38 {
39 public:
40         _OggStream(int serial)
41         {
42                 mSerial = serial;
43                 memset((void *)&mState, 0, sizeof(mState));
44                 mPacketCount = 0;
45         }
46         ~_OggStream()
47         {
48                 ogg_stream_clear(&mState);
49         }
50
51         int mSerial;
52         ogg_stream_state mState;
53         int mPacketCount;
54 };
55
56
57 _IAudioDecoder*
58 _VorbisDecoder_CreateInstance()
59 {
60         return new (std::nothrow) _VorbisDecoder();
61 }
62
63 _VorbisDecoder::_VorbisDecoder(void)
64 {
65         __isConstructed = false;
66         memset(&__vorbisBlock, 0, sizeof(vorbis_block));
67         memset(&__vorbisComment, 0, sizeof(vorbis_comment));
68         memset(&__vorbisInfo, 0, sizeof(vorbis_info));
69         memset(&__vorbisState, 0, sizeof(vorbis_dsp_state));
70         memset(&__state, 0, sizeof(__state));
71         __isHeaderDecoded = false;
72         __pStream = null;
73         __pBuf = null;
74         __offset = 0;
75         __maxOffsetLength = 0;
76         __serial = 0;
77 }
78
79 _VorbisDecoder::~_VorbisDecoder(void)
80 {
81         __isConstructed = false;
82         __isHeaderDecoded = false;
83
84         // Eliminate the memory leak for stream* previously added in hashmap
85         {
86                 IMapEnumeratorT<int,_OggStream*> * pEnum = __streamMap.GetMapEnumeratorN();
87                 if (pEnum)
88                 {
89                         _OggStream* pValue = null;
90                         while (pEnum->MoveNext() == E_SUCCESS)
91                         {
92                                 pEnum->GetValue(pValue);
93                                 if (pValue != null)
94                                 {
95                                         delete pValue;
96                                 }
97                         }
98                         delete pEnum;
99                 }
100         }
101         __streamMap.RemoveAll();
102
103         // Clear calls for the vorbis decoder library to avoid memory leak.
104         vorbis_block_clear(&__vorbisBlock);
105         vorbis_dsp_clear(&__vorbisState);
106         vorbis_comment_clear(&__vorbisComment);
107         vorbis_info_clear(&__vorbisInfo);
108
109
110         ogg_sync_clear(&__state);
111 }
112
113 result
114 _VorbisDecoder::Construct(const Tizen::Base::Collection::HashMap* pOption)
115 {
116         result r = E_SUCCESS;
117
118         SysAssertf( __isConstructed == false, " Already Constructed .");
119
120         vorbis_info_init(&__vorbisInfo);
121         vorbis_comment_init(&__vorbisComment);
122         r = __streamMap.Construct();
123         SysTryCatch(NID_MEDIA, r == E_SUCCESS, r = E_SYSTEM, E_SYSTEM,
124                           "[%s] map construct  Failed",GetErrorMessage(E_SYSTEM));
125         __isConstructed = true;
126         SetLastResult(r);
127         return r;
128
129 CATCH:
130         return r;
131 }
132
133 result
134 _VorbisDecoder::ParseOggStream(void)
135 {
136         ogg_page page;
137         int res = 0;
138         result r = E_SUCCESS;
139         char* pBuffer = null;
140
141         // " ParseOggStream offSet  & maxOffSetLength
142         SysTryCatch(NID_MEDIA, __offset != __maxOffsetLength, r = E_END_OF_FILE, E_END_OF_FILE,
143                            "[%s]  Reached End of File",GetErrorMessage(E_END_OF_FILE));         
144
145         while (ogg_sync_pageout(&__state, &page) != 1)
146         {
147                 pBuffer = ogg_sync_buffer(&__state, PAGE_SIZE);
148                 SysTryCatch(NID_MEDIA, pBuffer != null, r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
149                                    "[%s] Memory Allocation  Failed ",GetErrorMessage(E_OUT_OF_MEMORY));
150                 if ( !(__offset + PAGE_SIZE > __maxOffsetLength))
151                 {
152                         memcpy(pBuffer, __pBuf + __offset, PAGE_SIZE);
153                         __offset += PAGE_SIZE;
154                         ogg_sync_wrote(&__state, PAGE_SIZE);
155                 }
156                 else
157                 {
158                         memcpy(pBuffer, __pBuf + __offset, __maxOffsetLength - __offset );
159                         __offset = __maxOffsetLength;
160                         ogg_sync_wrote(&__state, PAGE_SIZE);
161                         res = ogg_sync_pageout(&__state, &page);
162                         SysTryCatch(NID_MEDIA, res == 1, r = E_UNSUPPORTED_FORMAT, E_UNSUPPORTED_FORMAT,
163                                            "[%s] The input data format is not supported or Reached End of File ",GetErrorMessage(E_UNSUPPORTED_FORMAT));        
164                 }
165         }
166
167
168         __serial = ogg_page_serialno(&page);
169
170         if (ogg_page_bos(&page))
171         {
172                 //  " page is the beginning of a bitstream"
173                 __pStream = new (std::nothrow) _OggStream(__serial);
174                 SysTryCatch(NID_MEDIA, (__pStream != null), r = E_SYSTEM, E_SYSTEM,
175                    "[E_SYSTEM]__streamMap.GetValue Failed",GetErrorMessage(E_SYSTEM));
176                 res = ogg_stream_init(&__pStream->mState, __serial);
177                 SysTryCatch(NID_MEDIA, (res == 0), r = E_SYSTEM, E_SYSTEM,
178                    "[%s] ogg_stream_init:", GetErrorMessage(E_SYSTEM));
179
180                 //All the hash map elements will be removed in the destructor
181                 __streamMap.Add(__serial,__pStream);
182         }
183         else
184         {
185                 _OggStream* pTemp = null;
186                 r = __streamMap.GetValue(__serial, pTemp);
187                 SysTryCatch(NID_MEDIA, (r == E_SUCCESS), r = E_SYSTEM, E_SYSTEM,
188                    "[%s] __streamMap.GetValue Failed", GetErrorMessage(r));
189                 SysTryCatch(NID_MEDIA, (pTemp != null), r = E_SYSTEM, E_SYSTEM,
190                    "[%s] __streamMap.GetValue Failed", GetErrorMessage(E_SYSTEM));
191                 __pStream = pTemp;
192         }
193
194         ogg_stream_pagein(&__pStream->mState, &page);
195         return r;
196
197 CATCH:
198         return r;
199 }
200
201 result
202 _VorbisDecoder::SetStreamHeader(void)
203 {
204         int headerCount = 0;
205         int res = 0;
206         result r = E_SUCCESS;
207         ogg_packet oggPacket;
208
209
210         r = ParseOggStream();
211         SysTryCatch(NID_MEDIA, (r == E_SUCCESS), , r,
212                                    "[%s]  ParseOggStream Failed", GetErrorMessage(r));
213
214         for (headerCount = 0; headerCount < HEADER_COUNT;)
215         {
216                 res = ogg_stream_packetout(&__pStream->mState, &oggPacket);
217                 while (res == 0)
218                 {
219                         r = ParseOggStream();
220                         SysTryCatch(NID_MEDIA, r == E_SUCCESS, , r,
221                                            "[%s] ParseOggStream Failed", GetErrorMessage(r))
222                         res = ogg_stream_packetout(&__pStream->mState, &oggPacket);
223                 }
224                 SysTryCatch(NID_MEDIA, (res > 0), r = E_SYSTEM, E_SYSTEM,
225                                    "[%s] Failed to get a packet");
226
227                 res = vorbis_synthesis_headerin(&__vorbisInfo, &__vorbisComment,
228                                                                                 &oggPacket);
229                 SysTryCatch(NID_MEDIA, (res == E_SUCCESS), r = E_SYSTEM, E_SYSTEM,
230                                    "[%s]  vorbis_synthesis_headerin Failed", GetErrorMessage(E_SYSTEM));
231                 headerCount++;
232         }
233
234         return r;
235 CATCH:
236         return r;
237 }
238
239 result
240 _VorbisDecoder::Decode(const byte* srcBuf, int srcBufSize, int& srcBufUsed,
241                                            byte* dstBuf, int dstBufSize, int& dstBufUsed)
242 {
243         result r = E_SUCCESS;
244         int res = 0;
245         ogg_packet oggPacket;
246         float** pcm = null;
247         int samples = 0;
248         short* ptr = null;
249         int dstOffset = 0;
250
251         SysTryCatch(NID_MEDIA, ( __isConstructed == true), r = E_INVALID_STATE, E_INVALID_STATE,
252                             "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
253         SysTryCatch(NID_MEDIA, ( srcBuf != null && srcBufSize >= 0), r = E_INVALID_STATE, E_INVALID_STATE,
254                             "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
255
256         __pBuf = (byte*) srcBuf;
257         __maxOffsetLength = srcBufSize;
258         __offset = 0;
259
260         if (__isHeaderDecoded != true)
261         {
262                 res = ogg_sync_init(&__state);
263                 SysTryCatch(NID_MEDIA, (res == 0), r = E_SYSTEM, E_SYSTEM,
264                                    "[%s] ogg_sync_init Failed", GetErrorMessage(E_SYSTEM));
265
266                 r = SetStreamHeader();
267                 SysTryCatch(NID_MEDIA, r == E_SUCCESS, , r,
268                                           "[%s] Failed to get a packet", GetErrorMessage(E_SYSTEM));
269
270                 res = vorbis_synthesis_init(&__vorbisState, &__vorbisInfo);
271                 SysTryCatch(NID_MEDIA, (res == E_SUCCESS), r = E_SYSTEM, E_SYSTEM,
272                                    "[%s] vorbis_synthesis_init Failed", GetErrorMessage(E_SYSTEM));
273                 res = vorbis_block_init(&__vorbisState, &__vorbisBlock);
274                 SysTryCatch(NID_MEDIA, (res == E_SUCCESS), r = E_SYSTEM, E_SYSTEM,
275                                    "[%s] vorbis_block_init Failed", GetErrorMessage(E_SYSTEM));
276                 __isHeaderDecoded = true;
277         }
278
279         //  " Decoding  Vorbis Audio "
280         while (!samples)
281         {
282                 res = ogg_stream_packetout(&__pStream->mState, &oggPacket);
283                 if (res == 0)
284                 {
285                         r = ParseOggStream();
286                         SysTryCatch(NID_MEDIA, r == E_SUCCESS, , r,
287                                            "[%s] Failed to get a packet", GetErrorMessage(E_SYSTEM));
288                         res = ogg_stream_packetout(&__pStream->mState, &oggPacket);
289                 }
290                 if (res < 0)
291                 {
292                         continue; //no block required here , this is a continue statement.
293                 }
294
295                 res = vorbis_synthesis(&__vorbisBlock, &oggPacket);             
296                 if (res == OV_ENOTAUDIO || res == OV_EBADPACKET)
297                 {
298                         continue; //no block required here , this is a continue statement.
299                 }
300                 SysTryCatch(NID_MEDIA, (res == E_SUCCESS), r = E_SYSTEM, E_SYSTEM,
301                                    "[%s] vorbis_synthesis Failed", GetErrorMessage(E_SYSTEM));
302
303                 res = vorbis_synthesis_blockin(&__vorbisState, &__vorbisBlock);
304                 SysTryCatch(NID_MEDIA, (res == E_SUCCESS), r = E_SYSTEM, E_SYSTEM,
305                                    "[%s] vorbis_synthesis_blockin Failed", GetErrorMessage(E_SYSTEM));
306
307                 samples = vorbis_synthesis_pcmout(&__vorbisState, &pcm);
308         }
309
310         ptr = (short*) (dstBuf + dstOffset);
311         SysTryCatch(NID_MEDIA, dstBufSize >= (samples * __vorbisInfo.channels * sizeof(short)),
312                            r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
313                            "[%s] Failed to get a packet", GetErrorMessage(E_SYSTEM));
314         for (int i = 0; i < samples; ++i)
315         {
316                 for (int j = 0; j < __vorbisInfo.channels; ++j)
317                 {
318                         int v = static_cast<int>(Tizen::Base::Utility::Math::Floor(0.5 + pcm[j][i] * 32767.0));
319                         if (v > 32767)
320                         {
321                                 v = 32767;
322                         }
323                         if (v < -32768)
324                         {
325                                 v = -32768;
326                         }
327                         *ptr++ = v;
328                 }
329         }
330
331         res = vorbis_synthesis_read(&__vorbisState, samples);
332         SysTryCatch(NID_MEDIA, res == E_SUCCESS, r = E_INVALID_ARG, E_INVALID_ARG,
333                            "[%s]  Decode Failed", GetErrorMessage(E_SYSTEM));
334
335         dstBufUsed = (samples * __vorbisInfo.channels * sizeof(short));
336         srcBufUsed = __offset;
337         return r;
338
339 CATCH:
340         if ( r == E_UNSUPPORTED_FORMAT && __isHeaderDecoded && srcBufSize <  PAGE_SIZE )
341         {
342                 srcBufUsed = __offset;
343                 dstBufUsed = 0;
344                 r = E_SUCCESS;
345         }
346         return r;
347 }
348
349
350 result
351 _VorbisDecoder::Probe(const byte* srcBuf, const int srcBufLength,
352                                           Tizen::Media::AudioSampleType& sampleType,
353                                           Tizen::Media::AudioChannelType& channelType, int& sampleRate)
354 {
355         result r = E_SUCCESS;
356         int res = 0;
357
358         SysTryCatch(NID_MEDIA, (__isConstructed == true), r = E_INVALID_STATE, E_INVALID_STATE,
359                            "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
360         SysTryCatch(NID_MEDIA, (srcBuf != null && srcBufLength > 0), r = E_INVALID_ARG, E_INVALID_ARG,
361                            "[%s] Invalid argument is used. The argument is not valid",GetErrorMessage(E_INVALID_ARG));
362
363         __pBuf = (byte*) srcBuf;
364         __maxOffsetLength = srcBufLength;
365         __offset = 0;
366
367         res = ogg_sync_init(&__state);
368         SysTryCatch(NID_MEDIA, (res == 0), r = E_SYSTEM, E_SYSTEM,
369                            "[%s] ogg_sync_init Failed", GetErrorMessage(E_SYSTEM));
370
371         r = SetStreamHeader();
372         SysTryCatch(NID_MEDIA, (r == E_SUCCESS), , r,
373                                    "[%s]  ParseOggStream Failed", GetErrorMessage(r));
374
375         res = vorbis_synthesis_init(&__vorbisState, &__vorbisInfo);
376         SysTryCatch(NID_MEDIA, (res == E_SUCCESS), r = E_SYSTEM, E_SYSTEM,
377                            "[%s]vorbis_synthesis_init Failed", GetErrorMessage(E_SYSTEM));
378         res = vorbis_block_init(&__vorbisState, &__vorbisBlock);
379         SysTryCatch(NID_MEDIA, (res == E_SUCCESS), r = E_SYSTEM, E_SYSTEM,
380                            "[%s]vorbis_block_init Failed", GetErrorMessage(E_SYSTEM));
381
382         channelType = (Tizen::Media::AudioChannelType)__vorbisInfo.channels;
383         sampleType = AUDIO_TYPE_PCM_S16_LE;
384         sampleRate = __vorbisInfo.rate;
385
386         // Eliminate the memory leak for stream* previously added in hashmap
387         {
388                 IMapEnumeratorT<int,_OggStream*> * pEnum = __streamMap.GetMapEnumeratorN();
389                 if (pEnum != null)
390                 {
391                         _OggStream* pValue = null;
392                         while (pEnum->MoveNext() == E_SUCCESS)
393                         {
394                                 pEnum->GetValue(pValue);
395                                 if (pValue)
396                                 {
397                                         delete pValue;
398                                 }
399                         }
400                         delete pEnum;
401                 }
402         }
403         __streamMap.RemoveAll();
404
405         // Clear calls for the vorbis decoder library to avoid memory leak.
406         vorbis_block_clear(&__vorbisBlock);
407         vorbis_dsp_clear(&__vorbisState);
408         vorbis_comment_clear(&__vorbisComment);
409         vorbis_info_clear(&__vorbisInfo);
410
411         // call to avoid memory leak
412         ogg_sync_clear(&__state);
413
414         vorbis_info_init(&__vorbisInfo);
415         vorbis_comment_init(&__vorbisComment);
416
417         return r;
418
419 CATCH:
420         return r;
421 }
422
423 result
424 _VorbisDecoder::Reset(void)
425 {
426         result r = E_SUCCESS;
427         int res = E_SUCCESS;
428
429         SysTryCatch(NID_MEDIA, (__isConstructed == true), r = E_INVALID_STATE, E_INVALID_STATE,
430                             "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
431         vorbis_info_init(&__vorbisInfo);
432         vorbis_comment_init(&__vorbisComment);
433         res = vorbis_synthesis_restart(&__vorbisState);
434         __isHeaderDecoded = false;
435         SysTryCatch(NID_MEDIA, res == E_SUCCESS, r = E_INVALID_STATE, E_INVALID_STATE,
436                             "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
437         return r;
438
439 CATCH:
440         return r;
441 }
442
443 result
444 _VorbisDecoder::GetValue(MediaPropertyType type, int& value) const
445 {
446         result r = E_SUCCESS;
447
448         SysTryCatch(NID_MEDIA, (__isConstructed == true), r = E_INVALID_STATE, E_INVALID_STATE,
449                             "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
450         SysTryCatch(NID_MEDIA, 0 < type, r = E_INVALID_ARG, E_INVALID_ARG,
451                            "[%s] Invalid argument is used. The argument is not valid",GetErrorMessage(E_INVALID_ARG));
452         SysTryCatch(NID_MEDIA, (__isHeaderDecoded == true), r = E_INVALID_STATE, E_INVALID_STATE,
453                             "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
454
455         switch (type)
456         {
457         case MEDIA_PROPERTY_AUDIO_CHANNEL_TYPE:
458                 switch (__vorbisInfo.channels)
459                 {
460                 case 0:
461                         value = AUDIO_CHANNEL_TYPE_NONE;
462                         break;
463
464                 case 1:
465                         value = AUDIO_CHANNEL_TYPE_MONO;
466                         break;
467
468                 case 2:
469                         value = AUDIO_CHANNEL_TYPE_STEREO;
470                         break;
471
472                 default:
473                         break;
474         }
475         break;
476
477         case MEDIA_PROPERTY_AUDIO_SAMPLE_TYPE:
478                 value = AUDIO_TYPE_PCM_S16_LE;
479         break;
480
481         case MEDIA_PROPERTY_AUDIO_SAMPLE_RATE:
482                 value = __vorbisInfo.rate;
483         break;
484
485         default:
486                 return E_OBJ_NOT_FOUND;
487         break;
488         }
489         return r;
490
491 CATCH:
492         return r;
493 }
494
495 result
496 _VorbisDecoder::GetValue(MediaPropertyType type, float& value) const
497 {
498         result r = E_SUCCESS;
499
500         SysTryCatch(NID_MEDIA, (__isConstructed == true), r = E_INVALID_STATE, E_INVALID_STATE,
501                             "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
502         SysTryCatch(NID_MEDIA, type < 0, r = E_INVALID_ARG, E_INVALID_ARG,
503                            "[%s] Invalid argument is used. The argument is not valid",GetErrorMessage(E_INVALID_ARG));
504         SysTryCatch(NID_MEDIA, type < MEDIA_PROPERTY_VIDEO_WIDTH || type >= MEDIA_PROPERTY_AUDIO_SAMPLE_RATE,
505                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,
506                            "[%s] The instance is not available ",GetErrorMessage(E_OBJ_NOT_FOUND));
507         SysTryCatch(NID_MEDIA, (__isHeaderDecoded == true), r = E_INVALID_STATE, E_INVALID_STATE,
508                            "[%s] The instance is in invalid state",GetErrorMessage(E_INVALID_STATE));
509
510         return E_OBJ_NOT_FOUND;
511
512 CATCH:
513         return r;
514 }
515
516 Tizen::Base::Collection::IListT<MediaPropertyType>*
517 _VorbisDecoder::GetSupportedPropertyListN(void) const
518 {
519         result r = E_SUCCESS;
520         ArrayListT<MediaPropertyType>* pPropList = new (std::nothrow) ArrayListT<MediaPropertyType>;
521
522         SysTryCatch(NID_MEDIA, pPropList != null, r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY,
523                            "[%s] Memory Allocation  Failed ",GetErrorMessage(E_OUT_OF_MEMORY));
524         pPropList->Add(MEDIA_PROPERTY_AUDIO_SAMPLE_RATE);
525         pPropList->Add(MEDIA_PROPERTY_AUDIO_CHANNEL_TYPE);
526         pPropList->Add(MEDIA_PROPERTY_AUDIO_SAMPLE_TYPE);
527         SetLastResult(r);
528         return pPropList;
529
530 CATCH:
531         return null;
532 }
533
534 bool
535 _VorbisDecoder::IsPropertySupported(MediaPropertyType type) const
536 {
537         result r = E_SUCCESS;
538
539         SysTryCatch(NID_MEDIA, (type == MEDIA_PROPERTY_AUDIO_SAMPLE_RATE
540                                                    || type == MEDIA_PROPERTY_AUDIO_CHANNEL_TYPE
541                                                    || type == MEDIA_PROPERTY_AUDIO_SAMPLE_TYPE),
542                            r = E_OBJ_NOT_FOUND, E_OBJ_NOT_FOUND,
543                            "[%s] The instance is not available ",GetErrorMessage(E_OBJ_NOT_FOUND));
544         SetLastResult(r);
545         return true;
546
547 CATCH:
548         return false;
549 }
550
551 }} // Tizen::Media