Merge pull request #2948 from ace20022/blu_lang_fix
[vuplus_xbmc] / xbmc / linux / OMXClock.cpp
1 /*
2  *      Copyright (C) 2005-2013 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 TARGET_WINDOWS)
22   #include "config.h"
23 #elif defined(TARGET_WINDOWS)
24 #include "system.h"
25 #endif
26
27 #if defined(HAVE_OMXLIB)
28
29 #include "video/VideoReferenceClock.h"
30 #include "settings/Settings.h"
31
32 #include "OMXClock.h"
33 #include "utils/MathUtils.h"
34
35 #define OMX_PRE_ROLL 200
36 #define TP(speed) ((speed) < 0 || (speed) > 4*DVD_PLAYSPEED_NORMAL)
37
38 OMXClock::OMXClock()
39 {
40   m_pause       = false;
41
42   m_fps = 25.0f;
43   m_omx_speed = DVD_PLAYSPEED_NORMAL;
44   m_WaitMask = 0;
45   m_eState = OMX_TIME_ClockStateStopped;
46   m_eClock = OMX_TIME_RefClockNone;
47   m_clock        = NULL;
48
49   pthread_mutex_init(&m_lock, NULL);
50 }
51
52 OMXClock::~OMXClock()
53 {
54   OMXDeinitialize();
55   pthread_mutex_destroy(&m_lock);
56 }
57
58 void OMXClock::Lock()
59 {
60   pthread_mutex_lock(&m_lock);
61 }
62
63 void OMXClock::UnLock()
64 {
65   pthread_mutex_unlock(&m_lock);
66 }
67
68 void OMXClock::OMXSetClockPorts(OMX_TIME_CONFIG_CLOCKSTATETYPE *clock, bool has_video, bool has_audio)
69 {
70   if(m_omx_clock.GetComponent() == NULL)
71     return;
72
73   if(!clock)
74     return;
75
76   clock->nWaitMask = 0;
77
78   if(has_audio)
79   {
80     clock->nWaitMask |= OMX_CLOCKPORT0;
81   }
82
83   if(has_video)
84   {
85     clock->nWaitMask |= OMX_CLOCKPORT1;
86   }
87 }
88
89 bool OMXClock::OMXSetReferenceClock(bool has_audio, bool lock /* = true */)
90 {
91   if(lock)
92     Lock();
93
94   bool ret = true;
95   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
96   OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock;
97   OMX_INIT_STRUCTURE(refClock);
98
99   if(has_audio)
100     refClock.eClock = OMX_TIME_RefClockAudio;
101   else
102     refClock.eClock = OMX_TIME_RefClockVideo;
103
104   if (refClock.eClock != m_eClock)
105   {
106     CLog::Log(LOGNOTICE, "OMXClock using %s as reference", refClock.eClock == OMX_TIME_RefClockVideo ? "video" : "audio");
107
108     omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeActiveRefClock, &refClock);
109     if(omx_err != OMX_ErrorNone)
110     {
111       CLog::Log(LOGERROR, "OMXClock::OMXSetReferenceClock error setting OMX_IndexConfigTimeActiveRefClock");
112       ret = false;
113     }
114     m_eClock = refClock.eClock;
115   }
116   if(lock)
117     UnLock();
118
119   return ret;
120 }
121
122 bool OMXClock::OMXInitialize(CDVDClock *clock)
123 {
124   std::string componentName = "";
125
126   m_pause       = false;
127
128   m_clock = clock;
129
130   componentName = "OMX.broadcom.clock";
131   if(!m_omx_clock.Initialize((const std::string)componentName, OMX_IndexParamOtherInit))
132     return false;
133
134   m_omx_clock.DisableAllPorts();
135
136   return true;
137 }
138
139 void OMXClock::OMXDeinitialize()
140 {
141   if(m_omx_clock.GetComponent() == NULL)
142     return;
143
144   m_omx_clock.Deinitialize();
145
146   m_omx_speed = DVD_PLAYSPEED_NORMAL;
147 }
148
149 bool OMXClock::OMXStateExecute(bool lock /* = true */)
150 {
151   if(m_omx_clock.GetComponent() == NULL)
152     return false;
153
154   if(lock)
155     Lock();
156
157   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
158
159   if(m_omx_clock.GetState() != OMX_StateExecuting)
160   {
161
162     OMXStateIdle(false);
163
164     omx_err = m_omx_clock.SetStateForComponent(OMX_StateExecuting);
165     if (omx_err != OMX_ErrorNone)
166     {
167       CLog::Log(LOGERROR, "OMXClock::StateExecute m_omx_clock.SetStateForComponent\n");
168       if(lock)
169         UnLock();
170       return false;
171     }
172   }
173
174   if(lock)
175     UnLock();
176
177   return true;
178 }
179
180 void OMXClock::OMXStateIdle(bool lock /* = true */)
181 {
182   if(m_omx_clock.GetComponent() == NULL)
183     return;
184
185   if(lock)
186     Lock();
187
188   if(m_omx_clock.GetState() == OMX_StateExecuting)
189     m_omx_clock.SetStateForComponent(OMX_StatePause);
190
191   if(m_omx_clock.GetState() != OMX_StateIdle)
192     m_omx_clock.SetStateForComponent(OMX_StateIdle);
193
194   if(lock)
195     UnLock();
196 }
197
198 COMXCoreComponent *OMXClock::GetOMXClock()
199 {
200   return &m_omx_clock;
201 }
202
203 bool  OMXClock::OMXStop(bool lock /* = true */)
204 {
205   if(m_omx_clock.GetComponent() == NULL)
206     return false;
207
208   if(lock)
209     Lock();
210
211   CLog::Log(LOGDEBUG, "OMXClock::OMXStop\n");
212
213   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
214   OMX_TIME_CONFIG_CLOCKSTATETYPE clock;
215   OMX_INIT_STRUCTURE(clock);
216
217   clock.eState      = OMX_TIME_ClockStateStopped;
218   clock.nOffset     = ToOMXTime(-1000LL * OMX_PRE_ROLL);
219
220   omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock);
221   if(omx_err != OMX_ErrorNone)
222   {
223     CLog::Log(LOGERROR, "OMXClock::Stop error setting OMX_IndexConfigTimeClockState\n");
224     if(lock)
225       UnLock();
226     return false;
227   }
228   m_eState = clock.eState;
229
230   if(lock)
231     UnLock();
232
233   return true;
234 }
235
236 bool OMXClock::OMXStep(int steps /* = 1 */, bool lock /* = true */)
237 {
238   if(m_omx_clock.GetComponent() == NULL)
239     return false;
240
241   if(lock)
242     Lock();
243
244   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
245   OMX_PARAM_U32TYPE param;
246   OMX_INIT_STRUCTURE(param);
247
248   param.nPortIndex = OMX_ALL;
249   param.nU32 = steps;
250
251   omx_err = m_omx_clock.SetConfig(OMX_IndexConfigSingleStep, &param);
252   if(omx_err != OMX_ErrorNone)
253   {
254     CLog::Log(LOGERROR, "OMXClock::Error setting OMX_IndexConfigSingleStep\n");
255     if(lock)
256       UnLock();
257     return false;
258   }
259
260   if(lock)
261     UnLock();
262
263   CLog::Log(LOGDEBUG, "OMXClock::Step (%d)", steps);
264   return true;
265 }
266
267 bool OMXClock::OMXReset(bool has_video, bool has_audio, bool lock /* = true */)
268 {
269   if(m_omx_clock.GetComponent() == NULL)
270     return false;
271
272   if(lock)
273     Lock();
274
275   if(!OMXSetReferenceClock(has_audio, false))
276   {
277     if(lock)
278       UnLock();
279     return false;
280   }
281
282   if (m_eState == OMX_TIME_ClockStateStopped)
283   {
284     OMX_TIME_CONFIG_CLOCKSTATETYPE clock;
285     OMX_INIT_STRUCTURE(clock);
286
287     clock.eState    = OMX_TIME_ClockStateWaitingForStartTime;
288     clock.nOffset   = ToOMXTime(-1000LL * OMX_PRE_ROLL);
289
290     OMXSetClockPorts(&clock, has_video, has_audio);
291
292     if(clock.nWaitMask)
293     {
294       OMX_ERRORTYPE omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeClockState, &clock);
295       if(omx_err != OMX_ErrorNone)
296       {
297         CLog::Log(LOGERROR, "OMXClock::OMXReset error setting OMX_IndexConfigTimeClockState\n");
298         if(lock)
299           UnLock();
300         return false;
301       }
302       CLog::Log(LOGDEBUG, "OMXClock::OMXReset audio / video : %d / %d wait mask %d->%d state : %d->%d\n",
303           has_audio, has_video, m_WaitMask, clock.nWaitMask, m_eState, clock.eState);
304       if (m_eState != OMX_TIME_ClockStateStopped)
305         m_WaitMask = clock.nWaitMask;
306       m_eState = clock.eState;
307     }
308   }
309
310   if(lock)
311     UnLock();
312
313   return true;
314 }
315
316 double OMXClock::OMXMediaTime(bool lock /* = true */)
317 {
318   if(m_omx_clock.GetComponent() == NULL)
319     return 0;
320
321   if(lock)
322     Lock();
323
324   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
325   double pts = 0;
326
327   OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
328   OMX_INIT_STRUCTURE(timeStamp);
329   timeStamp.nPortIndex = m_omx_clock.GetInputPort();
330
331   omx_err = m_omx_clock.GetConfig(OMX_IndexConfigTimeCurrentMediaTime, &timeStamp);
332   if(omx_err != OMX_ErrorNone)
333   {
334     CLog::Log(LOGERROR, "OMXClock::MediaTime error getting OMX_IndexConfigTimeCurrentMediaTime\n");
335     if(lock)
336       UnLock();
337     return 0;
338   }
339
340   pts = FromOMXTime(timeStamp.nTimestamp);
341
342   if(lock)
343     UnLock();
344   
345   return pts;
346 }
347
348 double OMXClock::OMXClockAdjustment(bool lock /* = true */)
349 {
350   if(m_omx_clock.GetComponent() == NULL)
351     return 0;
352
353   if(lock)
354     Lock();
355
356   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
357   double pts = 0;
358
359   OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
360   OMX_INIT_STRUCTURE(timeStamp);
361   timeStamp.nPortIndex = m_omx_clock.GetInputPort();
362
363   omx_err = m_omx_clock.GetConfig(OMX_IndexConfigClockAdjustment, &timeStamp);
364   if(omx_err != OMX_ErrorNone)
365   {
366     CLog::Log(LOGERROR, "OMXClock::MediaTime error getting OMX_IndexConfigClockAdjustment\n");
367     if(lock)
368       UnLock();
369     return 0;
370   }
371
372   pts = (double)FromOMXTime(timeStamp.nTimestamp);
373   //CLog::Log(LOGINFO, "OMXClock::ClockAdjustment %.0f %.0f\n", (double)FromOMXTime(timeStamp.nTimestamp), pts);
374   if(lock)
375     UnLock();
376
377   return pts;
378 }
379
380
381 // Set the media time, so calls to get media time use the updated value,
382 // useful after a seek so mediatime is updated immediately (rather than waiting for first decoded packet)
383 bool OMXClock::OMXMediaTime(double pts, bool lock /* = true*/)
384 {
385   if(m_omx_clock.GetComponent() == NULL)
386     return false;
387
388   if(lock)
389     Lock();
390
391   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
392   OMX_INDEXTYPE index;
393   OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp;
394   OMX_INIT_STRUCTURE(timeStamp);
395   timeStamp.nPortIndex = m_omx_clock.GetInputPort();
396
397   if(m_eClock == OMX_TIME_RefClockAudio)
398     index = OMX_IndexConfigTimeCurrentAudioReference;
399   else
400     index = OMX_IndexConfigTimeCurrentVideoReference;
401
402   timeStamp.nTimestamp = ToOMXTime(pts);
403
404   omx_err = m_omx_clock.SetConfig(index, &timeStamp);
405   if(omx_err != OMX_ErrorNone)
406   {
407     CLog::Log(LOGERROR, "OMXClock::OMXMediaTime error setting %s", index == OMX_IndexConfigTimeCurrentAudioReference ?
408        "OMX_IndexConfigTimeCurrentAudioReference":"OMX_IndexConfigTimeCurrentVideoReference");
409     if(lock)
410       UnLock();
411     return false;
412   }
413
414   CLog::Log(LOGDEBUG, "OMXClock::OMXMediaTime set config %s = %.2f", index == OMX_IndexConfigTimeCurrentAudioReference ?
415        "OMX_IndexConfigTimeCurrentAudioReference":"OMX_IndexConfigTimeCurrentVideoReference", pts);
416
417   if(lock)
418     UnLock();
419
420   return true;
421 }
422
423 bool OMXClock::OMXPause(bool lock /* = true */)
424 {
425   if(m_omx_clock.GetComponent() == NULL)
426     return false;
427
428   if(!m_pause)
429   {
430     if(lock)
431       Lock();
432
433     if (OMXSetSpeed(0, false, true))
434       m_pause = true;
435
436     if(lock)
437       UnLock();
438   }
439   return m_pause == true;
440 }
441
442 bool OMXClock::OMXResume(bool lock /* = true */)
443 {
444   if(m_omx_clock.GetComponent() == NULL)
445     return false;
446
447   if(m_pause)
448   {
449     if(lock)
450       Lock();
451
452     if (OMXSetSpeed(m_omx_speed, false, true))
453       m_pause = false;
454
455     if(lock)
456       UnLock();
457   }
458   return m_pause == false;
459 }
460
461 bool OMXClock::OMXSetSpeed(int speed, bool lock /* = true */, bool pause_resume /* = false */)
462 {
463   if(m_omx_clock.GetComponent() == NULL)
464     return false;
465
466   if(lock)
467     Lock();
468
469   CLog::Log(LOGDEBUG, "OMXClock::OMXSetSpeed(%.2f) pause_resume:%d", (float)speed / (float)DVD_PLAYSPEED_NORMAL, pause_resume);
470
471   if (pause_resume)
472   {
473     OMX_ERRORTYPE omx_err = OMX_ErrorNone;
474     OMX_TIME_CONFIG_SCALETYPE scaleType;
475     OMX_INIT_STRUCTURE(scaleType);
476
477     if (TP(speed))
478       scaleType.xScale = 0; // for trickplay we just pause, and single step
479     else
480       scaleType.xScale = (speed << 16) / DVD_PLAYSPEED_NORMAL;
481     omx_err = m_omx_clock.SetConfig(OMX_IndexConfigTimeScale, &scaleType);
482     if(omx_err != OMX_ErrorNone)
483     {
484       CLog::Log(LOGERROR, "OMXClock::OMXSetSpeed error setting OMX_IndexConfigTimeClockState\n");
485       if(lock)
486         UnLock();
487       return false;
488     }
489   }
490   if (!pause_resume)
491     m_omx_speed = speed;
492
493   if(lock)
494     UnLock();
495
496   return true;
497 }
498
499 bool OMXClock::HDMIClockSync(bool lock /* = true */)
500 {
501   if(m_omx_clock.GetComponent() == NULL)
502     return false;
503
504   if(lock)
505     Lock();
506
507   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
508   OMX_CONFIG_LATENCYTARGETTYPE latencyTarget;
509   OMX_INIT_STRUCTURE(latencyTarget);
510
511   latencyTarget.nPortIndex = OMX_ALL;
512   latencyTarget.bEnabled = OMX_TRUE;
513   latencyTarget.nFilter = 10;
514   latencyTarget.nTarget = 0;
515   latencyTarget.nShift = 3;
516   latencyTarget.nSpeedFactor = -200;
517   latencyTarget.nInterFactor = 100;
518   latencyTarget.nAdjCap = 100;
519
520   omx_err = m_omx_clock.SetConfig(OMX_IndexConfigLatencyTarget, &latencyTarget);
521   if(omx_err != OMX_ErrorNone)
522   {
523     CLog::Log(LOGERROR, "OMXClock::Speed error setting OMX_IndexConfigLatencyTarget\n");
524     if(lock)
525       UnLock();
526     return false;
527   }
528
529   if(lock)
530     UnLock();
531
532   return true;
533 }
534
535 int64_t OMXClock::CurrentHostCounter(void)
536 {
537   struct timespec now;
538   clock_gettime(CLOCK_MONOTONIC, &now);
539   return( ((int64_t)now.tv_sec * 1000000000L) + now.tv_nsec );
540 }
541
542 int64_t OMXClock::CurrentHostFrequency(void)
543 {
544   return( (int64_t)1000000000L );
545 }
546
547 int OMXClock::GetRefreshRate(double* interval)
548 {
549   if(!interval)
550     return false;
551
552   *interval = m_fps;
553   return true;
554 }
555
556 double OMXClock::NormalizeFrameduration(double frameduration)
557 {
558   //if the duration is within 20 microseconds of a common duration, use that
559   const double durations[] = {DVD_TIME_BASE * 1.001 / 24.0, DVD_TIME_BASE / 24.0, DVD_TIME_BASE / 25.0,
560                               DVD_TIME_BASE * 1.001 / 30.0, DVD_TIME_BASE / 30.0, DVD_TIME_BASE / 50.0,
561                               DVD_TIME_BASE * 1.001 / 60.0, DVD_TIME_BASE / 60.0};
562
563   double lowestdiff = DVD_TIME_BASE;
564   int    selected   = -1;
565   for (size_t i = 0; i < sizeof(durations) / sizeof(durations[0]); i++)
566   {
567     double diff = fabs(frameduration - durations[i]);
568     if (diff < DVD_MSEC_TO_TIME(0.02) && diff < lowestdiff)
569     {
570       selected = i;
571       lowestdiff = diff;
572     }
573   }
574
575   if (selected != -1)
576     return durations[selected];
577   else
578     return frameduration;
579 }
580
581 #endif