Merge pull request #2041 from popcornmix/display_aspect_ratio
[vuplus_xbmc] / xbmc / cores / omxplayer / OMXVideo.cpp
1 /*
2  *      Copyright (C) 2010-2012 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #if (defined HAVE_CONFIG_H) && (!defined WIN32)
22   #include "config.h"
23 #elif defined(_WIN32)
24 #include "system.h"
25 #endif
26
27 #include "OMXVideo.h"
28
29 #include "utils/log.h"
30 #include "linux/XMemUtils.h"
31 #include "DVDDemuxers/DVDDemuxUtils.h"
32 #include "settings/AdvancedSettings.h"
33 #include "xbmc/guilib/GraphicContext.h"
34 #include "settings/Settings.h"
35 #include "utils/BitstreamConverter.h"
36
37
38 #include <sys/time.h>
39 #include <inttypes.h>
40
41 #ifdef CLASSNAME
42 #undef CLASSNAME
43 #endif
44 #define CLASSNAME "COMXVideo"
45
46 #if 0
47 // TODO: These are Nvidia Tegra2 dependent, need to dynamiclly find the
48 // right codec matched to video format.
49 #define OMX_H264BASE_DECODER    "OMX.Nvidia.h264.decode"
50 // OMX.Nvidia.h264ext.decode segfaults, not sure why.
51 //#define OMX_H264MAIN_DECODER  "OMX.Nvidia.h264ext.decode"
52 #define OMX_H264MAIN_DECODER    "OMX.Nvidia.h264.decode"
53 #define OMX_H264HIGH_DECODER    "OMX.Nvidia.h264ext.decode"
54 #define OMX_MPEG4_DECODER       "OMX.Nvidia.mp4.decode"
55 #define OMX_MPEG4EXT_DECODER    "OMX.Nvidia.mp4ext.decode"
56 #define OMX_MPEG2V_DECODER      "OMX.Nvidia.mpeg2v.decode"
57 #define OMX_VC1_DECODER         "OMX.Nvidia.vc1.decode"
58 #endif
59
60 #define OMX_VIDEO_DECODER       "OMX.broadcom.video_decode"
61 #define OMX_H264BASE_DECODER    OMX_VIDEO_DECODER
62 #define OMX_H264MAIN_DECODER    OMX_VIDEO_DECODER
63 #define OMX_H264HIGH_DECODER    OMX_VIDEO_DECODER
64 #define OMX_MPEG4_DECODER       OMX_VIDEO_DECODER
65 #define OMX_MSMPEG4V1_DECODER   OMX_VIDEO_DECODER
66 #define OMX_MSMPEG4V2_DECODER   OMX_VIDEO_DECODER
67 #define OMX_MSMPEG4V3_DECODER   OMX_VIDEO_DECODER
68 #define OMX_MPEG4EXT_DECODER    OMX_VIDEO_DECODER
69 #define OMX_MPEG2V_DECODER      OMX_VIDEO_DECODER
70 #define OMX_VC1_DECODER         OMX_VIDEO_DECODER
71 #define OMX_WMV3_DECODER        OMX_VIDEO_DECODER
72 #define OMX_VP8_DECODER         OMX_VIDEO_DECODER
73
74 #define MAX_TEXT_LENGTH 1024
75
76 COMXVideo::COMXVideo()
77 {
78   m_is_open           = false;
79   m_Pause             = false;
80   m_extradata         = NULL;
81   m_extrasize         = 0;
82   m_video_convert     = false;
83   m_video_codec_name  = "";
84   m_deinterlace       = false;
85   m_hdmi_clock_sync   = false;
86   m_first_frame       = true;
87 }
88
89 COMXVideo::~COMXVideo()
90 {
91   Close();
92 }
93
94 bool COMXVideo::SendDecoderConfig()
95 {
96   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
97
98   /* send decoder config */
99   if(m_extrasize > 0 && m_extradata != NULL)
100   {
101     OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer();
102
103     if(omx_buffer == NULL)
104     {
105       CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err);
106       return false;
107     }
108
109     omx_buffer->nOffset = 0;
110     omx_buffer->nFilledLen = m_extrasize;
111     if(omx_buffer->nFilledLen > omx_buffer->nAllocLen)
112     {
113       CLog::Log(LOGERROR, "%s::%s - omx_buffer->nFilledLen > omx_buffer->nAllocLen", CLASSNAME, __func__);
114       return false;
115     }
116
117     memset((unsigned char *)omx_buffer->pBuffer, 0x0, omx_buffer->nAllocLen);
118     memcpy((unsigned char *)omx_buffer->pBuffer, m_extradata, omx_buffer->nFilledLen);
119     omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME;
120   
121     omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
122     if (omx_err != OMX_ErrorNone)
123     {
124       CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err);
125       return false;
126     }
127   }
128   return true;
129 }
130
131 bool COMXVideo::NaluFormatStartCodes(enum CodecID codec, uint8_t *in_extradata, int in_extrasize)
132 {
133   switch(codec)
134   {
135     case CODEC_ID_H264:
136       if (in_extrasize < 7 || in_extradata == NULL)
137         return true;
138       // valid avcC atom data always starts with the value 1 (version), otherwise annexb
139       else if ( *in_extradata != 1 )
140         return true;
141     default: break;
142   }
143   return false;    
144 }
145
146 bool COMXVideo::Open(CDVDStreamInfo &hints, OMXClock *clock, bool deinterlace, bool hdmi_clock_sync)
147 {
148   Close();
149
150   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
151   std::string decoder_name;
152
153   m_video_codec_name      = "";
154   m_codingType            = OMX_VIDEO_CodingUnused;
155
156   m_decoded_width  = hints.width;
157   m_decoded_height = hints.height;
158
159   m_hdmi_clock_sync = hdmi_clock_sync;
160
161   if(!m_decoded_width || !m_decoded_height)
162     return false;
163
164   if(hints.extrasize > 0 && hints.extradata != NULL)
165   {
166     m_extrasize = hints.extrasize;
167     m_extradata = (uint8_t *)malloc(m_extrasize);
168     memcpy(m_extradata, hints.extradata, hints.extrasize);
169   }
170
171   switch (hints.codec)
172   {
173     case CODEC_ID_H264:
174     {
175       switch(hints.profile)
176       {
177         case FF_PROFILE_H264_BASELINE:
178           // (role name) video_decoder.avc
179           // H.264 Baseline profile
180           decoder_name = OMX_H264BASE_DECODER;
181           m_codingType = OMX_VIDEO_CodingAVC;
182           m_video_codec_name = "omx-h264";
183           break;
184         case FF_PROFILE_H264_MAIN:
185           // (role name) video_decoder.avc
186           // H.264 Main profile
187           decoder_name = OMX_H264MAIN_DECODER;
188           m_codingType = OMX_VIDEO_CodingAVC;
189           m_video_codec_name = "omx-h264";
190           break;
191         case FF_PROFILE_H264_HIGH:
192           // (role name) video_decoder.avc
193           // H.264 Main profile
194           decoder_name = OMX_H264HIGH_DECODER;
195           m_codingType = OMX_VIDEO_CodingAVC;
196           m_video_codec_name = "omx-h264";
197           break;
198         case FF_PROFILE_UNKNOWN:
199           decoder_name = OMX_H264HIGH_DECODER;
200           m_codingType = OMX_VIDEO_CodingAVC;
201           m_video_codec_name = "omx-h264";
202           break;
203         default:
204           decoder_name = OMX_H264HIGH_DECODER;
205           m_codingType = OMX_VIDEO_CodingAVC;
206           m_video_codec_name = "omx-h264";
207           break;
208       }
209
210       /* check interlaced */
211       if(m_extrasize > 9 && m_extradata[0] == 1)
212       {
213         CBitstreamConverter converter;
214         converter.Open(hints.codec, (uint8_t *)hints.extradata, hints.extrasize, true);
215
216         int32_t  max_ref_frames = 0;
217         uint8_t  *spc = m_extradata + 6;
218         uint32_t sps_size = BS_RB16(spc);
219         bool     interlaced = true;
220         if (sps_size)
221           converter.parseh264_sps(spc+3, sps_size-1, &interlaced, &max_ref_frames);
222         if(!interlaced && deinterlace)
223           deinterlace = false;
224
225         converter.Close();
226       }
227     }
228     break;
229     case CODEC_ID_MPEG4:
230       // (role name) video_decoder.mpeg4
231       // MPEG-4, DivX 4/5 and Xvid compatible
232       decoder_name = OMX_MPEG4_DECODER;
233       m_codingType = OMX_VIDEO_CodingMPEG4;
234       m_video_codec_name = "omx-mpeg4";
235       break;
236     case CODEC_ID_MPEG1VIDEO:
237     case CODEC_ID_MPEG2VIDEO:
238       // (role name) video_decoder.mpeg2
239       // MPEG-2
240       decoder_name = OMX_MPEG2V_DECODER;
241       m_codingType = OMX_VIDEO_CodingMPEG2;
242       m_video_codec_name = "omx-mpeg2";
243       break;
244     case CODEC_ID_H263:
245       // (role name) video_decoder.mpeg4
246       // MPEG-4, DivX 4/5 and Xvid compatible
247       decoder_name = OMX_MPEG4_DECODER;
248       m_codingType = OMX_VIDEO_CodingMPEG4;
249       m_video_codec_name = "omx-h263";
250       break;
251     case CODEC_ID_VP8:
252       // (role name) video_decoder.vp8
253       // VP8
254       decoder_name = OMX_VP8_DECODER;
255       m_codingType = OMX_VIDEO_CodingVP8;
256       m_video_codec_name = "omx-vp8";
257     break;
258     case CODEC_ID_VC1:
259     case CODEC_ID_WMV3:
260       // (role name) video_decoder.vc1
261       // VC-1, WMV9
262       decoder_name = OMX_VC1_DECODER;
263       m_codingType = OMX_VIDEO_CodingWMV;
264       m_video_codec_name = "omx-vc1";
265       break;
266     default:
267       return false;
268     break;
269   }
270
271   /* enable deintelace on SD and 1080i */
272   if(m_decoded_width <= 720 && m_decoded_height <=576 && deinterlace)
273     m_deinterlace = deinterlace;
274   else if(m_decoded_width >= 1920 && m_decoded_height >= 540 && deinterlace)
275     m_deinterlace = deinterlace;
276
277   if(m_deinterlace)
278     CLog::Log(LOGDEBUG, "COMXVideo::Open : enable deinterlace\n");
279
280   std::string componentName = "";
281
282   componentName = decoder_name;
283   if(!m_omx_decoder.Initialize((const std::string)componentName, OMX_IndexParamVideoInit))
284     return false;
285
286   componentName = "OMX.broadcom.video_render";
287   if(!m_omx_render.Initialize((const std::string)componentName, OMX_IndexParamVideoInit))
288     return false;
289
290   componentName = "OMX.broadcom.video_scheduler";
291   if(!m_omx_sched.Initialize((const std::string)componentName, OMX_IndexParamVideoInit))
292     return false;
293
294   if(m_deinterlace)
295   {
296     componentName = "OMX.broadcom.image_fx";
297     if(!m_omx_image_fx.Initialize((const std::string)componentName, OMX_IndexParamImageInit))
298       return false;
299   }
300
301   OMX_VIDEO_PARAM_PORTFORMATTYPE formatType;
302   /*
303   OMX_INIT_STRUCTURE(formatType);
304   formatType.nPortIndex = m_omx_decoder.GetInputPort();
305   OMX_U32 nIndex = 1;
306   bool bFound = false;
307
308   omx_err = OMX_ErrorNone;
309   do
310   {
311     formatType.nIndex = nIndex;
312     omx_err = m_omx_decoder.GetParameter(OMX_IndexParamVideoPortFormat, &formatType);
313     if(formatType.eCompressionFormat == m_codingType)
314     {
315       bFound = true;
316       break;
317     }
318     nIndex++;
319   }
320   while(omx_err == OMX_ErrorNone);
321
322   if(!bFound)
323   {
324     CLog::Log(LOGINFO, "COMXVideo::Open coding : %s not supported\n", m_video_codec_name.c_str());
325     return false;
326   }
327   */
328
329   if(clock == NULL)
330     return false;
331
332   m_av_clock = clock;
333   m_omx_clock = m_av_clock->GetOMXClock();
334
335   if(m_omx_clock->GetComponent() == NULL)
336   {
337     m_av_clock = NULL;
338     m_omx_clock = NULL;
339     return false;
340   }
341
342   if(m_deinterlace)
343   {
344     m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_image_fx, m_omx_image_fx.GetInputPort());
345     m_omx_tunnel_image_fx.Initialize(&m_omx_image_fx, m_omx_image_fx.GetOutputPort(), &m_omx_sched, m_omx_sched.GetInputPort());
346   }
347   else
348   {
349     m_omx_tunnel_decoder.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_sched, m_omx_sched.GetInputPort());
350   }
351   m_omx_tunnel_sched.Initialize(&m_omx_sched, m_omx_sched.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort());
352
353   m_omx_tunnel_clock.Initialize(m_omx_clock, m_omx_clock->GetInputPort()  + 1, &m_omx_sched, m_omx_sched.GetOutputPort()  + 1);
354
355   omx_err = m_omx_tunnel_clock.Establish(false);
356   if(omx_err != OMX_ErrorNone)
357   {
358     CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_clock.Establish\n");
359     return false;
360   }
361
362   omx_err = m_omx_decoder.SetStateForComponent(OMX_StateIdle);
363   if (omx_err != OMX_ErrorNone)
364   {
365     CLog::Log(LOGERROR, "COMXVideo::Open m_omx_decoder.SetStateForComponent\n");
366     return false;
367   }
368
369   OMX_INIT_STRUCTURE(formatType);
370   formatType.nPortIndex = m_omx_decoder.GetInputPort();
371   formatType.eCompressionFormat = m_codingType;
372
373   if (hints.fpsscale > 0 && hints.fpsrate > 0)
374   {
375     formatType.xFramerate = (long long)(1<<16)*hints.fpsrate / hints.fpsscale;
376   }
377   else
378   {
379     formatType.xFramerate = 25 * (1<<16);
380   }
381
382   omx_err = m_omx_decoder.SetParameter(OMX_IndexParamVideoPortFormat, &formatType);
383   if(omx_err != OMX_ErrorNone)
384     return false;
385   
386   OMX_PARAM_PORTDEFINITIONTYPE portParam;
387   OMX_INIT_STRUCTURE(portParam);
388   portParam.nPortIndex = m_omx_decoder.GetInputPort();
389
390   omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &portParam);
391   if(omx_err != OMX_ErrorNone)
392   {
393     CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err);
394     return false;
395   }
396
397   portParam.nPortIndex = m_omx_decoder.GetInputPort();
398   portParam.nBufferCountActual = VIDEO_BUFFERS;
399
400   portParam.format.video.nFrameWidth  = m_decoded_width;
401   portParam.format.video.nFrameHeight = m_decoded_height;
402
403   omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &portParam);
404   if(omx_err != OMX_ErrorNone)
405   {
406     CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamPortDefinition omx_err(0x%08x)\n", omx_err);
407     return false;
408   }
409
410   OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE concanParam;
411   OMX_INIT_STRUCTURE(concanParam);
412   if(g_advancedSettings.m_omxDecodeStartWithValidFrame)
413     concanParam.bStartWithValidFrame = OMX_TRUE;
414   else
415     concanParam.bStartWithValidFrame = OMX_FALSE;
416
417   omx_err = m_omx_decoder.SetParameter(OMX_IndexParamBrcmVideoDecodeErrorConcealment, &concanParam);
418   if(omx_err != OMX_ErrorNone)
419   {
420     CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamBrcmVideoDecodeErrorConcealment omx_err(0x%08x)\n", omx_err);
421     return false;
422   }
423
424   if (m_deinterlace)
425   {
426     // the deinterlace component requires 3 additional video buffers in addition to the DPB (this is normally 2).
427     OMX_PARAM_U32TYPE extra_buffers;
428     OMX_INIT_STRUCTURE(extra_buffers);
429     extra_buffers.nU32 = 3;
430
431     omx_err = m_omx_decoder.SetParameter(OMX_IndexParamBrcmExtraBuffers, &extra_buffers);
432     if(omx_err != OMX_ErrorNone)
433     {
434       CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexParamBrcmExtraBuffers omx_err(0x%08x)\n", omx_err);
435       return false;
436     }
437   }
438
439   // broadcom omx entension:
440   // When enabled, the timestamp fifo mode will change the way incoming timestamps are associated with output images.
441   // In this mode the incoming timestamps get used without re-ordering on output images.
442   if(hints.ptsinvalid)
443   {
444     OMX_CONFIG_BOOLEANTYPE timeStampMode;
445     OMX_INIT_STRUCTURE(timeStampMode);
446     timeStampMode.bEnabled = OMX_TRUE;
447
448     omx_err = m_omx_decoder.SetParameter((OMX_INDEXTYPE)OMX_IndexParamBrcmVideoTimestampFifo, &timeStampMode);
449     if (omx_err != OMX_ErrorNone)
450     {
451       CLog::Log(LOGERROR, "COMXVideo::Open OMX_IndexParamBrcmVideoTimestampFifo error (0%08x)\n", omx_err);
452       return false;
453     }
454   }
455
456   if(NaluFormatStartCodes(hints.codec, m_extradata, m_extrasize))
457   {
458     OMX_NALSTREAMFORMATTYPE nalStreamFormat;
459     OMX_INIT_STRUCTURE(nalStreamFormat);
460     nalStreamFormat.nPortIndex = m_omx_decoder.GetInputPort();
461     nalStreamFormat.eNaluFormat = OMX_NaluFormatStartCodes;
462
463     omx_err = m_omx_decoder.SetParameter((OMX_INDEXTYPE)OMX_IndexParamNalStreamFormatSelect, &nalStreamFormat);
464     if (omx_err != OMX_ErrorNone)
465     {
466       CLog::Log(LOGERROR, "COMXVideo::Open OMX_IndexParamNalStreamFormatSelect error (0%08x)\n", omx_err);
467       return false;
468     }
469   }
470
471   if(m_hdmi_clock_sync)
472   {
473     OMX_CONFIG_LATENCYTARGETTYPE latencyTarget;
474     OMX_INIT_STRUCTURE(latencyTarget);
475     latencyTarget.nPortIndex = m_omx_render.GetInputPort();
476     latencyTarget.bEnabled = OMX_TRUE;
477     latencyTarget.nFilter = 2;
478     latencyTarget.nTarget = 4000;
479     latencyTarget.nShift = 3;
480     latencyTarget.nSpeedFactor = -135;
481     latencyTarget.nInterFactor = 500;
482     latencyTarget.nAdjCap = 20;
483
484     omx_err = m_omx_render.SetConfig(OMX_IndexConfigLatencyTarget, &latencyTarget);
485     if (omx_err != OMX_ErrorNone)
486     {
487       CLog::Log(LOGERROR, "COMXVideo::Open OMX_IndexConfigLatencyTarget error (0%08x)\n", omx_err);
488       return false;
489     }
490   }
491
492   // Alloc buffers for the omx intput port.
493   omx_err = m_omx_decoder.AllocInputBuffers();
494   if (omx_err != OMX_ErrorNone)
495   {
496     CLog::Log(LOGERROR, "COMXVideo::Open AllocOMXInputBuffers error (0%08x)\n", omx_err);
497     return false;
498   }
499
500   omx_err = m_omx_tunnel_decoder.Establish(false);
501   if(omx_err != OMX_ErrorNone)
502   {
503     CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_decoder.Establish\n");
504     return false;
505   }
506
507   omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting);
508   if (omx_err != OMX_ErrorNone)
509   {
510     CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_decoder.SetStateForComponent\n");
511     return false;
512   }
513
514   if(m_deinterlace)
515   {
516     OMX_CONFIG_IMAGEFILTERPARAMSTYPE image_filter;
517     OMX_INIT_STRUCTURE(image_filter);
518
519     image_filter.nPortIndex = m_omx_image_fx.GetOutputPort();
520     image_filter.nNumParams = 1;
521     image_filter.nParams[0] = 3;
522     image_filter.eImageFilter = OMX_ImageFilterDeInterlaceAdvanced;
523
524     omx_err = m_omx_image_fx.SetConfig(OMX_IndexConfigCommonImageFilterParameters, &image_filter);
525     if(omx_err != OMX_ErrorNone)
526     {
527       CLog::Log(LOGERROR, "COMXVideo::Open error OMX_IndexConfigCommonImageFilterParameters omx_err(0x%08x)\n", omx_err);
528       return false;
529     }
530
531     omx_err = m_omx_tunnel_image_fx.Establish(false);
532     if(omx_err != OMX_ErrorNone)
533     {
534       CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_image_fx.Establish\n");
535       return false;
536     }
537
538     omx_err = m_omx_image_fx.SetStateForComponent(OMX_StateExecuting);
539     if (omx_err != OMX_ErrorNone)
540     {
541       CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_image_fx.SetStateForComponent\n");
542       return false;
543     }
544
545     m_omx_image_fx.DisablePort(m_omx_image_fx.GetInputPort(), false);
546     m_omx_image_fx.DisablePort(m_omx_image_fx.GetOutputPort(), false);
547   }
548
549   omx_err = m_omx_tunnel_sched.Establish(false);
550   if(omx_err != OMX_ErrorNone)
551   {
552     CLog::Log(LOGERROR, "COMXVideo::Open m_omx_tunnel_sched.Establish\n");
553     return false;
554   }
555
556   omx_err = m_omx_sched.SetStateForComponent(OMX_StateExecuting);
557   if (omx_err != OMX_ErrorNone)
558   {
559     CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_sched.SetStateForComponent\n");
560     return false;
561   }
562
563   omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
564   if (omx_err != OMX_ErrorNone)
565   {
566     CLog::Log(LOGERROR, "COMXVideo::Open error m_omx_render.SetStateForComponent\n");
567     return false;
568   }
569
570   if(!SendDecoderConfig())
571     return false;
572
573   m_is_open           = true;
574   m_drop_state        = false;
575
576   OMX_CONFIG_DISPLAYREGIONTYPE configDisplay;
577   OMX_INIT_STRUCTURE(configDisplay);
578   configDisplay.nPortIndex = m_omx_render.GetInputPort();
579
580   configDisplay.set = OMX_DISPLAY_SET_TRANSFORM;
581
582   switch(hints.orientation)
583   {
584     case 90:
585       configDisplay.transform = OMX_DISPLAY_ROT90;
586       break;
587     case 180:
588       configDisplay.transform = OMX_DISPLAY_ROT180;
589       break;
590     case 270:
591       configDisplay.transform = OMX_DISPLAY_ROT270;
592       break;
593     default:
594       configDisplay.transform = OMX_DISPLAY_ROT0;
595       break;
596   }
597
598   omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
599   if(omx_err != OMX_ErrorNone)
600   {
601     CLog::Log(LOGWARNING, "COMXVideo::Open could not set orientation : %d\n", hints.orientation);
602   }
603
604   /*
605   configDisplay.set     = OMX_DISPLAY_SET_LAYER;
606   configDisplay.layer   = 2;
607
608   omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
609   if(omx_err != OMX_ErrorNone)
610     return false;
611
612   configDisplay.set     = OMX_DISPLAY_SET_DEST_RECT;
613   configDisplay.dest_rect.x_offset  = 100;
614   configDisplay.dest_rect.y_offset  = 100;
615   configDisplay.dest_rect.width     = 640;
616   configDisplay.dest_rect.height    = 480;
617     
618   omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
619   if(omx_err != OMX_ErrorNone)
620     return false;
621
622   configDisplay.set     = OMX_DISPLAY_SET_TRANSFORM;
623   configDisplay.transform = OMX_DISPLAY_ROT180;
624     
625   omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
626   if(omx_err != OMX_ErrorNone)
627     return false;
628
629   configDisplay.set     = OMX_DISPLAY_SET_FULLSCREEN;
630   configDisplay.fullscreen = OMX_FALSE;
631     
632   omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
633   if(omx_err != OMX_ErrorNone)
634     return false;
635
636   configDisplay.set     = OMX_DISPLAY_SET_MODE;
637   configDisplay.mode    = OMX_DISPLAY_MODE_FILL; //OMX_DISPLAY_MODE_LETTERBOX;
638     
639   omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
640   if(omx_err != OMX_ErrorNone)
641     return false;
642
643   configDisplay.set     = OMX_DISPLAY_SET_LAYER;
644   configDisplay.layer   = 1;
645
646   omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
647   if(omx_err != OMX_ErrorNone)
648     return false;
649
650   configDisplay.set     = OMX_DISPLAY_SET_ALPHA;
651   configDisplay.alpha   = OMX_FALSE;
652     
653   omx_err = m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
654   if(omx_err != OMX_ErrorNone)
655     return false;
656
657   */
658
659   if(m_omx_decoder.BadState())
660     return false;
661
662   CLog::Log(LOGDEBUG,
663     "%s::%s - decoder_component(0x%p), input_port(0x%x), output_port(0x%x) deinterlace %d hdmiclocksync %d\n",
664     CLASSNAME, __func__, m_omx_decoder.GetComponent(), m_omx_decoder.GetInputPort(), m_omx_decoder.GetOutputPort(),
665     m_deinterlace, m_hdmi_clock_sync);
666
667   m_first_frame   = true;
668   // start from assuming all recent frames had valid pts
669   m_history_valid_pts = ~0;
670
671   return true;
672 }
673
674 void COMXVideo::Close()
675 {
676   m_omx_tunnel_decoder.Flush();
677   if(m_deinterlace)
678     m_omx_tunnel_image_fx.Flush();
679   m_omx_tunnel_clock.Flush();
680   m_omx_tunnel_sched.Flush();
681
682   m_omx_tunnel_clock.Deestablish();
683   m_omx_tunnel_decoder.Deestablish();
684   if(m_deinterlace)
685     m_omx_tunnel_image_fx.Deestablish();
686   m_omx_tunnel_sched.Deestablish();
687
688   m_omx_decoder.FlushInput();
689
690   m_omx_sched.Deinitialize(true);
691   m_omx_decoder.Deinitialize(true);
692   if(m_deinterlace)
693     m_omx_image_fx.Deinitialize(true);
694   m_omx_render.Deinitialize(true);
695
696   m_is_open       = false;
697
698   if(m_extradata)
699     free(m_extradata);
700   m_extradata = NULL;
701   m_extrasize = 0;
702
703   m_video_convert     = false;
704   m_video_codec_name  = "";
705   m_deinterlace       = false;
706   m_first_frame       = true;
707   m_av_clock          = NULL;
708 }
709
710 void COMXVideo::SetDropState(bool bDrop)
711 {
712   m_drop_state = bDrop;
713 }
714
715 unsigned int COMXVideo::GetFreeSpace()
716 {
717   return m_omx_decoder.GetInputBufferSpace();
718 }
719
720 unsigned int COMXVideo::GetSize()
721 {
722   return m_omx_decoder.GetInputBufferSize();
723 }
724
725 static unsigned count_bits(int32_t value)
726 {
727   unsigned bits = 0;
728   for(;value;++bits)
729     value &= value - 1;
730   return bits;
731 }
732
733 int COMXVideo::Decode(uint8_t *pData, int iSize, double dts, double pts)
734 {
735   OMX_ERRORTYPE omx_err;
736
737   if( m_drop_state )
738     return true;
739
740   unsigned int demuxer_bytes = (unsigned int)iSize;
741   uint8_t *demuxer_content = pData;
742
743   if (demuxer_content && demuxer_bytes > 0)
744   {
745     while(demuxer_bytes)
746     {
747       // 500ms timeout
748       OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(500);
749       if(omx_buffer == NULL)
750       {
751         CLog::Log(LOGERROR, "OMXVideo::Decode timeout\n");
752         return false;
753       }
754
755       /*
756       CLog::Log(DEBUG, "COMXVideo::Video VDec : pts %lld omx_buffer 0x%08x buffer 0x%08x number %d\n", 
757           pts, omx_buffer, omx_buffer->pBuffer, (int)omx_buffer->pAppPrivate);
758       if(pts == DVD_NOPTS_VALUE)
759       {
760         CLog::Log(LOGDEBUG, "VDec : pts %f omx_buffer 0x%08x buffer 0x%08x number %d\n", 
761           (float)pts / AV_TIME_BASE, (int)omx_buffer, (int)omx_buffer->pBuffer, (int)omx_buffer->pAppPrivate);
762       }
763       */
764
765       omx_buffer->nFlags = 0;
766       omx_buffer->nOffset = 0;
767       // some packed bitstream AVI files set almost all pts values to DVD_NOPTS_VALUE, but have a scattering of real pts values.
768       // the valid pts values match the dts values.
769       // if a stream has had more than 4 valid pts values in the last 16, the use UNKNOWN, otherwise use dts
770       m_history_valid_pts = (m_history_valid_pts << 1) | (pts != DVD_NOPTS_VALUE);
771       if(pts == DVD_NOPTS_VALUE && count_bits(m_history_valid_pts & 0xffff) < 4)
772         pts = dts;
773
774       if(m_av_clock->VideoStart())
775       {
776         // only send dts on first frame to get nearly correct starttime
777         if(pts == DVD_NOPTS_VALUE)
778           pts = dts;
779         if(pts == DVD_NOPTS_VALUE)
780           omx_buffer->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN;
781         omx_buffer->nFlags = OMX_BUFFERFLAG_STARTTIME;
782         CLog::Log(LOGDEBUG, "OMXVideo::Decode VDec : setStartTime %f\n", (pts == DVD_NOPTS_VALUE ? 0.0 : pts) / DVD_TIME_BASE);
783         m_av_clock->VideoStart(false);
784       }
785       else
786       {
787         if(pts == DVD_NOPTS_VALUE)
788           omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;
789       }
790
791       omx_buffer->nTimeStamp = ToOMXTime((uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts);
792       omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes;
793       memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen);
794
795       demuxer_bytes -= omx_buffer->nFilledLen;
796       demuxer_content += omx_buffer->nFilledLen;
797
798       if(demuxer_bytes == 0)
799         omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
800
801       int nRetry = 0;
802       while(true)
803       {
804         omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
805         if (omx_err == OMX_ErrorNone)
806         {
807           break;
808         }
809         else
810         {
811           CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err);
812           nRetry++;
813         }
814         if(nRetry == 5)
815         {
816           CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() finaly failed\n", CLASSNAME, __func__);
817           return false;
818         }
819       }
820
821       if(m_first_frame && m_deinterlace)
822       {
823         OMX_PARAM_PORTDEFINITIONTYPE port_image;
824         OMX_INIT_STRUCTURE(port_image);
825         port_image.nPortIndex = m_omx_decoder.GetOutputPort();
826
827         omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_image);
828         if(omx_err != OMX_ErrorNone)
829           CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 1 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err);
830
831         /* we assume when the sizes equal we have the first decoded frame */
832         if(port_image.format.video.nFrameWidth == m_decoded_width && port_image.format.video.nFrameHeight == m_decoded_height)
833         {
834           m_first_frame = false;
835
836           omx_err = m_omx_decoder.WaitForEvent(OMX_EventPortSettingsChanged);
837           if(omx_err == OMX_ErrorStreamCorrupt)
838           {
839             CLog::Log(LOGERROR, "%s::%s - image not unsupported\n", CLASSNAME, __func__);
840             return false;
841           }
842
843           m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), false);
844           m_omx_sched.DisablePort(m_omx_sched.GetInputPort(), false);
845
846           if(m_deinterlace)
847           {
848             m_omx_image_fx.DisablePort(m_omx_image_fx.GetOutputPort(), false);
849             m_omx_image_fx.DisablePort(m_omx_image_fx.GetInputPort(), false);
850
851             port_image.nPortIndex = m_omx_image_fx.GetInputPort();
852             omx_err = m_omx_image_fx.SetParameter(OMX_IndexParamPortDefinition, &port_image);
853             if(omx_err != OMX_ErrorNone)
854               CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 2 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err);
855
856             port_image.nPortIndex = m_omx_image_fx.GetOutputPort();
857             omx_err = m_omx_image_fx.SetParameter(OMX_IndexParamPortDefinition, &port_image);
858             if(omx_err != OMX_ErrorNone)
859               CLog::Log(LOGERROR, "%s::%s - error OMX_IndexParamPortDefinition 3 omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err);
860           }
861
862           m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), false);
863
864           if(m_deinterlace)
865           {
866             m_omx_image_fx.EnablePort(m_omx_image_fx.GetOutputPort(), false);
867             m_omx_image_fx.EnablePort(m_omx_image_fx.GetInputPort(), false);
868           }
869
870           m_omx_sched.EnablePort(m_omx_sched.GetInputPort(), false);
871         }
872       }
873     }
874
875     return true;
876
877   }
878   
879   return false;
880 }
881
882 void COMXVideo::Reset(void)
883 {
884   if(!m_is_open)
885     return;
886
887   m_omx_decoder.FlushInput();
888   m_omx_tunnel_decoder.Flush();
889
890   /*
891   OMX_ERRORTYPE omx_err;
892   OMX_CONFIG_BOOLEANTYPE configBool;
893   OMX_INIT_STRUCTURE(configBool);
894   configBool.bEnabled = OMX_TRUE;
895
896   omx_err = m_omx_decoder.SetConfig(OMX_IndexConfigRefreshCodec, &configBool);
897   if (omx_err != OMX_ErrorNone)
898     CLog::Log(LOGERROR, "%s::%s - error reopen codec omx_err(0x%08x)\n", CLASSNAME, __func__, omx_err);
899
900   SendDecoderConfig();
901
902   m_first_frame   = true;
903   */
904 }
905
906 ///////////////////////////////////////////////////////////////////////////////////////////
907 bool COMXVideo::Pause()
908 {
909   if(m_omx_render.GetComponent() == NULL)
910     return false;
911
912   if(m_Pause) return true;
913   m_Pause = true;
914
915   m_omx_sched.SetStateForComponent(OMX_StatePause);
916   m_omx_render.SetStateForComponent(OMX_StatePause);
917
918   return true;
919 }
920
921 ///////////////////////////////////////////////////////////////////////////////////////////
922 bool COMXVideo::Resume()
923 {
924   if(m_omx_render.GetComponent() == NULL)
925     return false;
926
927   if(!m_Pause) return true;
928   m_Pause = false;
929
930   m_omx_sched.SetStateForComponent(OMX_StateExecuting);
931   m_omx_render.SetStateForComponent(OMX_StateExecuting);
932
933   return true;
934 }
935
936 ///////////////////////////////////////////////////////////////////////////////////////////
937 void COMXVideo::SetVideoRect(const CRect& SrcRect, const CRect& DestRect)
938 {
939   if(!m_is_open)
940     return;
941
942   OMX_CONFIG_DISPLAYREGIONTYPE configDisplay;
943   float sx1 = SrcRect.x1, sy1 = SrcRect.y1, sx2 = SrcRect.x2, sy2 = SrcRect.y2;
944   float dx1 = DestRect.x1, dy1 = DestRect.y1, dx2 = DestRect.x2, dy2 = DestRect.y2;
945   float sw = SrcRect.Width() / DestRect.Width();
946   float sh = SrcRect.Height() / DestRect.Height();
947
948   // doesn't like negative coordinates on dest_rect. So adjust by increasing src_rect
949   if (dx1 < 0.0f) {
950     sx1 -= dx1 * sw;
951     dx1 -= dx1;
952   }
953   if (dy1 < 0.0f) {
954     sy1 -= dy1 * sh;
955     dy1 -= dy1;
956   }
957
958   OMX_INIT_STRUCTURE(configDisplay);
959   configDisplay.nPortIndex = m_omx_render.GetInputPort();
960   configDisplay.fullscreen = OMX_FALSE;
961   configDisplay.noaspect   = OMX_TRUE;
962
963   configDisplay.set                 = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_DEST_RECT|OMX_DISPLAY_SET_SRC_RECT|OMX_DISPLAY_SET_FULLSCREEN|OMX_DISPLAY_SET_NOASPECT);
964   configDisplay.dest_rect.x_offset  = (int)(dx1+0.5f);
965   configDisplay.dest_rect.y_offset  = (int)(dy1+0.5f);
966   configDisplay.dest_rect.width     = (int)(dx2-dx1+0.5f);
967   configDisplay.dest_rect.height    = (int)(dy2-dy1+0.5f);
968
969   configDisplay.src_rect.x_offset   = (int)(sx1+0.5f);
970   configDisplay.src_rect.y_offset   = (int)(sy1+0.5f);
971   configDisplay.src_rect.width      = (int)(sx2-sx1+0.5f);
972   configDisplay.src_rect.height     = (int)(sy2-sy1+0.5f);
973
974   m_omx_render.SetConfig(OMX_IndexConfigDisplayRegion, &configDisplay);
975
976   CLog::Log(LOGDEBUG, "dest_rect.x_offset %d dest_rect.y_offset %d dest_rect.width %d dest_rect.height %d\n",
977       configDisplay.dest_rect.x_offset, configDisplay.dest_rect.y_offset, 
978       configDisplay.dest_rect.width, configDisplay.dest_rect.height);
979 }
980
981 int COMXVideo::GetInputBufferSize()
982 {
983   return m_omx_decoder.GetInputBufferSize();
984 }
985
986 void COMXVideo::WaitCompletion()
987 {
988   if(!m_is_open)
989     return;
990
991   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
992   OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer();
993   
994   if(omx_buffer == NULL)
995   {
996     CLog::Log(LOGERROR, "%s::%s - buffer error 0x%08x", CLASSNAME, __func__, omx_err);
997     return;
998   }
999   
1000   omx_buffer->nOffset     = 0;
1001   omx_buffer->nFilledLen  = 0;
1002   omx_buffer->nTimeStamp  = ToOMXTime(0LL);
1003
1004   omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS | OMX_BUFFERFLAG_TIME_UNKNOWN;
1005   
1006   omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
1007   if (omx_err != OMX_ErrorNone)
1008   {
1009     CLog::Log(LOGERROR, "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, omx_err);
1010     return;
1011   }
1012
1013   unsigned int nTimeOut = 30000;
1014
1015   while(nTimeOut)
1016   {
1017     if(m_omx_render.IsEOS())
1018     {
1019       CLog::Log(LOGDEBUG, "%s::%s - got eos\n", CLASSNAME, __func__);
1020       break;
1021     }
1022
1023     if(nTimeOut == 0)
1024     {
1025       CLog::Log(LOGERROR, "%s::%s - wait for eos timed out\n", CLASSNAME, __func__);
1026       break;
1027     }
1028     Sleep(50);
1029     nTimeOut -= 50;
1030   }
1031
1032   m_omx_render.ResetEos();
1033
1034   return;
1035 }