housecleaning: remove stale paths in .gitignore
[vuplus_xbmc] / xbmc / cores / amlplayer / AMLPlayer.cpp
1 /*
2  *      Copyright (C) 2011-2013 Team XBMC
3  *      http://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 #include "system.h"
22
23 #include "AMLPlayer.h"
24 #include "Application.h"
25 #include "FileItem.h"
26 #include "FileURLProtocol.h"
27 #include "GUIInfoManager.h"
28 #include "video/VideoThumbLoader.h"
29 #include "Util.h"
30 #include "cores/AudioEngine/AEFactory.h"
31 #include "cores/AudioEngine/Utils/AEUtil.h"
32 #include "cores/VideoRenderers/RenderFlags.h"
33 #include "cores/VideoRenderers/RenderFormats.h"
34 #include "cores/VideoRenderers/RenderManager.h"
35 #include "dialogs/GUIDialogKaiToast.h"
36 #include "filesystem/File.h"
37 #include "filesystem/SpecialProtocol.h"
38 #include "guilib/GUIWindowManager.h"
39 #include "guilib/LocalizeStrings.h"
40 #include "settings/AdvancedSettings.h"
41 #include "settings/VideoSettings.h"
42 #include "settings/MediaSettings.h"
43 #include "settings/Settings.h"
44 #include "threads/SingleLock.h"
45 #include "utils/log.h"
46 #include "utils/TimeUtils.h"
47 #include "utils/URIUtils.h"
48 #include "utils/LangCodeExpander.h"
49 #include "utils/Variant.h"
50 #include "windowing/WindowingFactory.h"
51
52 // for external subtitles
53 #include "xbmc/cores/dvdplayer/DVDClock.h"
54 #include "xbmc/cores/dvdplayer/DVDPlayerSubtitle.h"
55 #include "xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h"
56 #include "settings/VideoSettings.h"
57
58 // amlogic libplayer
59 #include "utils/AMLUtils.h"
60 #include "DllLibamplayer.h"
61
62 static float VolumePercentToScale(float volume)
63 {
64   float audio_volume = 0.0;
65   if (volume > VOLUME_MINIMUM)
66   {
67     float dB = CAEUtil::PercentToGain(volume);
68     audio_volume = CAEUtil::GainToScale(dB);
69   }
70   if (audio_volume >= 0.99f)
71     audio_volume = 1.0f;
72
73   return audio_volume;
74 }
75
76 struct AMLChapterInfo
77 {
78   std::string name;
79   int64_t     seekto_ms;
80 };
81
82 struct AMLPlayerStreamInfo
83 {
84   void Clear()
85   {
86     id                = 0;
87     width             = 0;
88     height            = 0;
89     aspect_ratio_num  = 0;
90     aspect_ratio_den  = 0;
91     frame_rate_num    = 0;
92     frame_rate_den    = 0;
93     bit_rate          = 0;
94     duration          = 0;
95     channel           = 0;
96     sample_rate       = 0;
97     language          = "";
98     type              = STREAM_NONE;
99     source            = STREAM_SOURCE_NONE;
100     name              = "";
101     filename          = "";
102     filename2         = "";
103   }
104
105   int           id;
106   StreamType    type;
107   StreamSource  source;
108   int           width;
109   int           height;
110   int           aspect_ratio_num;
111   int           aspect_ratio_den;
112   int           frame_rate_num;
113   int           frame_rate_den;
114   int           bit_rate;
115   int           duration;
116   int           channel;
117   int           sample_rate;
118   int           format;
119   std::string   language;
120   std::string   name;
121   std::string   filename;
122   std::string   filename2;  // for vobsub subtitles, 2 files are necessary (idx/sub) 
123 };
124
125 ////////////////////////////////////////////////////////////////////////////////////////////
126 static int media_info_dump(media_info_t* minfo)
127 {
128   int i = 0;
129   CLog::Log(LOGDEBUG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
130   CLog::Log(LOGDEBUG, "======||file size:%lld",                          minfo->stream_info.file_size);
131   CLog::Log(LOGDEBUG, "======||file type:%d",                            minfo->stream_info.type);
132   CLog::Log(LOGDEBUG, "======||duration:%d",                             minfo->stream_info.duration);
133   CLog::Log(LOGDEBUG, "======||has video track?:%s",                     minfo->stream_info.has_video>0?"YES!":"NO!");
134   CLog::Log(LOGDEBUG, "======||has audio track?:%s",                     minfo->stream_info.has_audio>0?"YES!":"NO!");
135   CLog::Log(LOGDEBUG, "======||has internal subtitle?:%s",               minfo->stream_info.has_sub>0?"YES!":"NO!");
136   CLog::Log(LOGDEBUG, "======||internal subtile counts:%d",              minfo->stream_info.total_sub_num);
137   if (minfo->stream_info.has_video && minfo->stream_info.total_video_num > 0)
138   {
139     CLog::Log(LOGDEBUG, "======||video index:%d",                        minfo->stream_info.cur_video_index);
140     CLog::Log(LOGDEBUG, "======||video counts:%d",                       minfo->stream_info.total_video_num);
141     CLog::Log(LOGDEBUG, "======||video width :%d",                       minfo->video_info[0]->width);
142     CLog::Log(LOGDEBUG, "======||video height:%d",                       minfo->video_info[0]->height);
143     CLog::Log(LOGDEBUG, "======||video ratio :%d:%d",                    minfo->video_info[0]->aspect_ratio_num,minfo->video_info[0]->aspect_ratio_den);
144     CLog::Log(LOGDEBUG, "======||frame_rate  :%.2f",                     (float)minfo->video_info[0]->frame_rate_num/minfo->video_info[0]->frame_rate_den);
145     CLog::Log(LOGDEBUG, "======||video bitrate:%d",                      minfo->video_info[0]->bit_rate);
146     CLog::Log(LOGDEBUG, "======||video format:%d",                       minfo->video_info[0]->format);
147     CLog::Log(LOGDEBUG, "======||video duration:%d",                     minfo->video_info[0]->duartion);
148   }
149   if (minfo->stream_info.has_audio && minfo->stream_info.total_audio_num > 0)
150   {
151     CLog::Log(LOGDEBUG, "======||audio index:%d",                        minfo->stream_info.cur_audio_index);
152     CLog::Log(LOGDEBUG, "======||audio counts:%d",                       minfo->stream_info.total_audio_num);
153     for (i = 0; i < minfo->stream_info.total_audio_num; i++)
154     {
155       CLog::Log(LOGDEBUG, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
156       CLog::Log(LOGDEBUG, "======||audio track(%d) id:%d",               i, minfo->audio_info[i]->id);
157       CLog::Log(LOGDEBUG, "======||audio track(%d) codec type:%d",       i, minfo->audio_info[i]->aformat);
158       CLog::Log(LOGDEBUG, "======||audio track(%d) audio_channel:%d",    i, minfo->audio_info[i]->channel);
159       CLog::Log(LOGDEBUG, "======||audio track(%d) bit_rate:%d",         i, minfo->audio_info[i]->bit_rate);
160       CLog::Log(LOGDEBUG, "======||audio track(%d) audio_samplerate:%d", i, minfo->audio_info[i]->sample_rate);
161       CLog::Log(LOGDEBUG, "======||audio track(%d) duration:%d",         i, minfo->audio_info[i]->duration);
162       CLog::Log(LOGDEBUG, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
163       if (NULL != minfo->audio_info[i]->audio_tag)
164       {
165         CLog::Log(LOGDEBUG, "======||audio track title:%s",              minfo->audio_info[i]->audio_tag->title!=NULL?minfo->audio_info[i]->audio_tag->title:"unknown");
166         CLog::Log(LOGDEBUG, "======||audio track album:%s",              minfo->audio_info[i]->audio_tag->album!=NULL?minfo->audio_info[i]->audio_tag->album:"unknown");
167         CLog::Log(LOGDEBUG, "======||audio track author:%s",             minfo->audio_info[i]->audio_tag->author!=NULL?minfo->audio_info[i]->audio_tag->author:"unknown");
168         CLog::Log(LOGDEBUG, "======||audio track year:%s",               minfo->audio_info[i]->audio_tag->year!=NULL?minfo->audio_info[i]->audio_tag->year:"unknown");
169         CLog::Log(LOGDEBUG, "======||audio track comment:%s",            minfo->audio_info[i]->audio_tag->comment!=NULL?minfo->audio_info[i]->audio_tag->comment:"unknown");
170         CLog::Log(LOGDEBUG, "======||audio track genre:%s",              minfo->audio_info[i]->audio_tag->genre!=NULL?minfo->audio_info[i]->audio_tag->genre:"unknown");
171         CLog::Log(LOGDEBUG, "======||audio track copyright:%s",          minfo->audio_info[i]->audio_tag->copyright!=NULL?minfo->audio_info[i]->audio_tag->copyright:"unknown");
172         CLog::Log(LOGDEBUG, "======||audio track track:%d",              minfo->audio_info[i]->audio_tag->track);
173       }
174     }
175   }
176   if (minfo->stream_info.has_sub && minfo->stream_info.total_sub_num > 0)
177   {
178     CLog::Log(LOGDEBUG, "======||subtitle index:%d",                     minfo->stream_info.cur_sub_index);
179     CLog::Log(LOGDEBUG, "======||subtitle counts:%d",                    minfo->stream_info.total_sub_num);
180     for (i = 0; i < minfo->stream_info.total_sub_num; i++)
181     {
182       if (0 == minfo->sub_info[i]->internal_external){
183         CLog::Log(LOGDEBUG, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
184         CLog::Log(LOGDEBUG, "======||internal subtitle(%d) pid:%d",      i, minfo->sub_info[i]->id);
185         CLog::Log(LOGDEBUG, "======||internal subtitle(%d) language:%s", i, minfo->sub_info[i]->sub_language?minfo->sub_info[i]->sub_language:"unknown");
186         CLog::Log(LOGDEBUG, "======||internal subtitle(%d) width:%d",    i, minfo->sub_info[i]->width);
187         CLog::Log(LOGDEBUG, "======||internal subtitle(%d) height:%d",   i, minfo->sub_info[i]->height);
188         CLog::Log(LOGDEBUG, "======||internal subtitle(%d) resolution:%d", i, minfo->sub_info[i]->resolution);
189         CLog::Log(LOGDEBUG, "======||internal subtitle(%d) subtitle size:%lld", i, minfo->sub_info[i]->subtitle_size);
190         CLog::Log(LOGDEBUG, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
191       }
192     }
193   }
194   CLog::Log(LOGDEBUG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
195   return 0;
196 }
197
198 static const char* VideoCodecName(int vformat)
199 {
200   const char *format = "";
201
202   switch(vformat)
203   {
204     case VFORMAT_MPEG12:
205       format = "mpeg12";
206       break;
207     case VFORMAT_MPEG4:
208       format = "mpeg4";
209       break;
210     case VFORMAT_H264:
211       format = "h264";
212       break;
213     case VFORMAT_MJPEG:
214       format = "mjpeg";
215       break;
216     case VFORMAT_REAL:
217       format = "real";
218       break;
219     case VFORMAT_JPEG:
220       format = "jpeg";
221       break;
222     case VFORMAT_VC1:
223       format = "vc1";
224       break;
225     case VFORMAT_AVS:
226       format = "avs";
227       break;
228     case VFORMAT_SW:
229       format = "sw";
230       break;
231     case VFORMAT_H264MVC:
232       format = "h264mvc";
233       break;
234     default:
235       format = "unknown";
236       break;
237   }
238   return format;
239 }
240
241 static const char* AudioCodecName(int aformat)
242 {
243   const char *format = "";
244
245   switch(aformat)
246   {
247     case AFORMAT_MPEG:
248       format = "mpeg";
249       break;
250     case AFORMAT_PCM_S16LE:
251       format = "pcm";
252       break;
253     case AFORMAT_AAC:
254       format = "aac";
255       break;
256     case AFORMAT_AC3:
257       format = "ac3";
258       break;
259     case AFORMAT_ALAW:
260       format = "alaw";
261       break;
262     case AFORMAT_MULAW:
263       format = "mulaw";
264       break;
265     case AFORMAT_DTS:
266       format = "dts";
267       break;
268     case AFORMAT_PCM_S16BE:
269       format = "pcm";
270       break;
271     case AFORMAT_FLAC:
272       format = "flac";
273       break;
274     case AFORMAT_COOK:
275       format = "cook";
276       break;
277     case AFORMAT_PCM_U8:
278       format = "pcm";
279       break;
280     case AFORMAT_ADPCM:
281       format = "adpcm";
282       break;
283     case AFORMAT_AMR:
284       format = "amr";
285       break;
286     case AFORMAT_RAAC:
287       format = "raac";
288       break;
289     case AFORMAT_WMA:
290       format = "wma";
291       break;
292     case AFORMAT_WMAPRO:
293       format = "wmapro";
294       break;
295     case AFORMAT_PCM_BLURAY:
296       format = "lpcm";
297       break;
298     case AFORMAT_ALAC:
299       format = "alac";
300       break;
301     case AFORMAT_VORBIS:
302       format = "vorbis";
303       break;
304     case AFORMAT_AAC_LATM:
305       format = "aac-latm";
306       break;
307     case AFORMAT_APE:
308       format = "ape";
309       break;
310     default:
311       format = "unknown";
312       break;
313   }
314
315   return format;
316 }
317
318 ////////////////////////////////////////////////////////////////////////////////////////////
319 CAMLSubTitleThread::CAMLSubTitleThread(DllLibAmplayer *dll) :
320   CThread("AMLSubTitle"),
321   m_dll(dll),
322   m_subtitle_codec(-1)
323 {
324 }
325
326 CAMLSubTitleThread::~CAMLSubTitleThread()
327 {
328   StopThread();
329 }
330
331 void CAMLSubTitleThread::Flush()
332 {
333   CSingleLock lock(m_subtitle_csection);
334   m_subtitle_strings.clear();
335 }
336
337 void CAMLSubTitleThread::UpdateSubtitle(CStdString &subtitle, int64_t elapsed_ms)
338 {
339   CSingleLock lock(m_subtitle_csection);
340   if (!m_subtitle_strings.empty())
341   {
342     AMLSubtitle *amlsubtitle;
343     // remove any expired subtitles
344     std::deque<AMLSubtitle*>::iterator it = m_subtitle_strings.begin();
345     while (it != m_subtitle_strings.end())
346     {
347       amlsubtitle = *it;
348       if (elapsed_ms > amlsubtitle->endtime)
349         it = m_subtitle_strings.erase(it);
350       else
351         ++it;
352     }
353
354     // find the current subtitle
355     it = m_subtitle_strings.begin();
356     while (it != m_subtitle_strings.end())
357     {
358       amlsubtitle = *it;
359       if (elapsed_ms > amlsubtitle->bgntime && elapsed_ms < amlsubtitle->endtime)
360       {
361         subtitle = amlsubtitle->string;
362         break;
363       }
364       ++it;
365     }
366   }
367 }
368
369 void CAMLSubTitleThread::Process(void)
370 {
371   CLog::Log(LOGDEBUG, "CAMLSubTitleThread::Process begin");
372
373   m_subtitle_codec = m_dll->codec_open_sub_read();
374   if (m_subtitle_codec < 0)
375     CLog::Log(LOGERROR, "CAMLSubTitleThread::Process: codec_open_sub_read failed");
376
377   while (!m_bStop)
378   {
379     if (m_subtitle_codec > 0)
380     {
381       // poll sub codec, we return on timeout or when a sub gets loaded
382       // TODO: codec_poll_sub_fd has a bug in kernel driver, it trashes
383       // subs in certain conditions so we read garbage, manual poll for now.
384       //codec_poll_sub_fd(m_subtitle_codec, 1000);
385       int sub_size = m_dll->codec_get_sub_size_fd(m_subtitle_codec);
386       if (sub_size > 0)
387       {
388         // calloc sub_size + 1 so we auto terminate the string
389         char *sub_buffer = (char*)calloc(sub_size + 1, 1);
390         m_dll->codec_read_sub_data_fd(m_subtitle_codec, sub_buffer, sub_size);
391
392         // check subtitle header stamp
393         if ((sub_buffer[0] == 0x41) && (sub_buffer[1] == 0x4d) &&
394             (sub_buffer[2] == 0x4c) && (sub_buffer[3] == 0x55) &&
395             (sub_buffer[4] == 0xaa))
396         {
397           // 20 byte header, then subtitle string
398           if (sub_size >= 20)
399           {
400             // csection lock it now as we are diddling shared vars
401             CSingleLock lock(m_subtitle_csection);
402
403             AMLSubtitle *subtitle = new AMLSubtitle;
404
405             int sub_type = (sub_buffer[5]  << 16) | (sub_buffer[6] << 8)   |  sub_buffer[7];
406             // sub_pts are in ffmpeg timebase, not ms timebase, convert it.
407             int sub_pts  = (sub_buffer[12] << 24) | (sub_buffer[13] << 16) | (sub_buffer[14] << 8) | sub_buffer[15];
408
409             /* TODO: handle other subtitle codec types
410             // subtitle codecs
411             AV_CODEC_ID_DVD_SUBTITLE= 0x17000,
412             AV_CODEC_ID_DVB_SUBTITLE,
413             AV_CODEC_ID_TEXT,  ///< raw UTF-8 text
414             AV_CODEC_ID_XSUB,
415             AV_CODEC_ID_SSA,
416             AV_CODEC_ID_MOV_TEXT,
417             AV_CODEC_ID_HDMV_PGS_SUBTITLE,
418             AV_CODEC_ID_DVB_TELETEXT,
419             AV_CODEC_ID_SRT,
420             AV_CODEC_ID_MICRODVD,
421             */
422             switch(sub_type)
423             {
424               default:
425                 CLog::Log(LOGDEBUG, "CAMLSubTitleThread::Process: fixme :) "
426                   "sub_type(0x%x), size(%d), bgntime(%lld), endtime(%lld), string(%s)",
427                   sub_type, sub_size-20, subtitle->bgntime, subtitle->endtime, &sub_buffer[20]);
428                 break;
429               case AV_CODEC_ID_TEXT:
430                 subtitle->bgntime = sub_pts/ 90;
431                 subtitle->endtime = subtitle->bgntime + 4000;
432                 subtitle->string  = &sub_buffer[20];
433                 break;
434               case AV_CODEC_ID_SSA:
435                 if (strncmp((const char*)&sub_buffer[20], "Dialogue:", 9) == 0)
436                 {
437                   int  vars_found, hour1, min1, sec1, hunsec1, hour2, min2, sec2, hunsec2, nothing;
438                   char line3[sub_size];
439                   char *line = &sub_buffer[20];
440
441                   memset(line3, 0x00, sub_size);
442                   vars_found = sscanf(line, "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\n\r]",
443                     &nothing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line3);
444                   if (vars_found < 10)
445                     vars_found = sscanf(line, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\n\r]",
446                       &nothing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line3);
447
448                   if (vars_found > 9)
449                   {
450                     char *tmp, *line2 = strchr(line3, ',');
451                     // use 32 for the case that the amount of commas increase with newer SSA versions
452                     for (int comma = 4; comma < 32; comma++)
453                     {
454                       tmp = strchr(line2 + 1, ',');
455                       if (!tmp)
456                         break;
457                       if (*(++tmp) == ' ')
458                         break;
459                       // a space after a comma means we are already in a sentence
460                       line2 = tmp;
461                     }
462                     // eliminate the trailing comma
463                     if (*line2 == ',')
464                       line2++;
465                     subtitle->bgntime = 10 * (360000 * hour1 + 6000 * min1 + 100 * sec1 + hunsec1);
466                     subtitle->endtime = 10 * (360000 * hour2 + 6000 * min2 + 100 * sec2 + hunsec2);
467                     subtitle->string  = line2;
468                     // convert tags to what we understand
469                     if (subtitle->string.Replace("{\\i1}","[I]"))
470                       subtitle->string.Replace("{\\i0}","[/I]");
471                     if (subtitle->string.Replace("{\\b1}","[B]"))
472                       subtitle->string.Replace("{\\b0}","[/B]");
473                     // remove anything other tags
474                     for (std::string::const_iterator it = subtitle->string.begin(); it != subtitle->string.end(); ++it)
475                     {
476                       size_t beg = subtitle->string.find("{\\");
477                       if (beg != std::string::npos)
478                       {
479                         size_t end = subtitle->string.find("}", beg);
480                         if (end != std::string::npos)
481                           subtitle->string.erase(beg, end-beg+1);
482                       }
483                     }
484                   }
485                 }
486                 break;
487             }
488             free(sub_buffer);
489             
490             if (subtitle->string.length())
491             {
492               // quirks
493               subtitle->string.Replace("&apos;","\'");
494               m_subtitle_strings.push_back(subtitle);
495               // fixup existing endtimes so they do not exceed bgntime of previous subtitle
496               for (size_t i = 0; i < m_subtitle_strings.size() - 1; i++)
497               {
498                 if (m_subtitle_strings[i]->endtime > m_subtitle_strings[i+1]->bgntime)
499                   m_subtitle_strings[i]->endtime = m_subtitle_strings[i+1]->bgntime;
500               }
501             }
502           }
503         }
504       }
505       else
506       {
507         if (!m_bStop)
508           usleep(100 * 1000);
509       }
510     }
511     else
512     {
513       if (!m_bStop)
514         usleep(250 * 1000);
515     }
516   }
517   m_subtitle_strings.clear();
518   if (m_subtitle_codec > 0)
519     m_dll->codec_close_sub_fd(m_subtitle_codec);
520   m_subtitle_codec = -1;
521
522   CLog::Log(LOGDEBUG, "CAMLSubTitleThread::Process end");
523 }
524 ////////////////////////////////////////////////////////////////////////////////////////////
525 CAMLPlayer::CAMLPlayer(IPlayerCallback &callback)
526   : IPlayer(callback),
527   CThread                 ("AMLPlayer" ),
528   m_cpu                   (0            ),
529   m_speed                 (0            ),
530   m_paused                (false        ),
531   m_bAbortRequest         (false        ),
532   m_ready                 (true         ),
533   m_audio_index           (0            ),
534   m_audio_count           (0            ),
535   m_audio_delay           (0            ),
536   m_audio_passthrough_ac3 (false        ),
537   m_audio_passthrough_dts (false        ),
538   m_audio_mute            (false        ),
539   m_audio_volume          (0.0f         ),
540   m_video_index           (0            ),
541   m_video_count           (0            ),
542   m_video_width           (0            ),
543   m_video_height          (0            ),
544   m_video_fps_numerator   (0            ),
545   m_video_fps_denominator (0            ),
546   m_subtitle_index        (0            ),
547   m_subtitle_count        (0            ),
548   m_subtitle_show         (false        ),
549   m_subtitle_delay        (0            ),
550   m_subtitle_thread       (NULL         ),
551   m_chapter_count         (0            ),
552   m_show_mainvideo        (0            ),
553   m_view_mode             (0            ),
554   m_zoom                  (0            ),
555   m_contrast              (0            ),
556   m_brightness            (0            )
557 {
558   m_dll = new DllLibAmplayer;
559   m_dll->Load();
560   m_pid = -1;
561 #if defined(_DEBUG)
562   m_log_level = 5;
563 #else
564   m_log_level = 3;
565 #endif
566
567   // for external subtitles
568   m_dvdOverlayContainer = new CDVDOverlayContainer;
569   m_dvdPlayerSubtitle = new CDVDPlayerSubtitle(m_dvdOverlayContainer);
570
571   // Suspend AE temporarily so exclusive or hog-mode sinks
572   // don't block external player's access to audio device
573   if (!CAEFactory::Suspend())
574   {
575     CLog::Log(LOGNOTICE,"%s: Failed to suspend AudioEngine before launching external player", __FUNCTION__);
576   }
577 }
578
579 CAMLPlayer::~CAMLPlayer()
580 {
581   CloseFile();
582
583   delete m_dvdPlayerSubtitle;
584   delete m_dvdOverlayContainer;
585   delete m_dll, m_dll = NULL;
586
587   // Resume AE processing of XBMC native audio
588   if (!CAEFactory::Resume())
589   {
590     CLog::Log(LOGFATAL, "%s: Failed to restart AudioEngine after return from external player",__FUNCTION__);
591   }
592 }
593
594 bool CAMLPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options)
595 {
596   try
597   {
598     CLog::Log(LOGNOTICE, "CAMLPlayer: Opening: %s", file.GetPath().c_str());
599     // if playing a file close it first
600     // this has to be changed so we won't have to close it.
601     if (IsRunning())
602       CloseFile();
603
604     m_bAbortRequest = false;
605
606     m_item = file;
607     m_options = options;
608
609     m_pid = -1;
610     m_elapsed_ms  =  0;
611     m_duration_ms =  0;
612
613     m_audio_info  = "none";
614     m_audio_delay = 0;
615     m_audio_mute  = CAEFactory::IsMuted();
616     m_audio_volume = VolumePercentToScale(CAEFactory::GetVolume());
617     m_audio_passthrough_ac3 = CSettings::Get().GetBool("audiooutput.ac3passthrough");
618     m_audio_passthrough_dts = CSettings::Get().GetBool("audiooutput.dtspassthrough");
619
620     m_video_info  = "none";
621     m_video_width    =  0;
622     m_video_height   =  0;
623     m_video_fps_numerator = 25;
624     m_video_fps_denominator = 1;
625
626     m_subtitle_delay =  0;
627     m_subtitle_thread = NULL;
628
629     m_chapter_count  =  0;
630
631     m_show_mainvideo = -1;
632     m_dst_rect.SetRect(0, 0, 0, 0);
633     m_zoom           = -1;
634     m_contrast       = -1;
635     m_brightness     = -1;
636
637     ClearStreamInfos();
638
639     if (m_item.IsDVDFile() || m_item.IsDVD())
640       return false;
641
642     // setup to spin the busy dialog until we are playing
643     m_ready.Reset();
644
645     g_renderManager.PreInit();
646
647     // create the playing thread
648     Create();
649     if (!m_ready.WaitMSec(100))
650     {
651       CGUIDialogBusy *dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY);
652       dialog->Show();
653       while (!m_ready.WaitMSec(1))
654         g_windowManager.ProcessRenderLoop(false);
655       dialog->Close();
656     }
657
658     // Playback might have been stopped due to some error.
659     if (m_bStop || m_bAbortRequest)
660       return false;
661
662     return true;
663   }
664   catch (...)
665   {
666     CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__);
667     return false;
668   }
669 }
670
671 bool CAMLPlayer::CloseFile()
672 {
673   CLog::Log(LOGDEBUG, "CAMLPlayer::CloseFile");
674
675   // set the abort request so that other threads can finish up
676   m_bAbortRequest = true;
677
678   CLog::Log(LOGDEBUG, "CAMLPlayer: waiting for threads to exit");
679   // wait for the main thread to finish up
680   // since this main thread cleans up all other resources and threads
681   // we are done after the StopThread call
682   StopThread();
683
684   CLog::Log(LOGDEBUG, "CAMLPlayer: finished waiting");
685
686   g_renderManager.UnInit();
687
688   return true;
689 }
690
691 bool CAMLPlayer::IsPlaying() const
692 {
693   return !m_bStop;
694 }
695
696 void CAMLPlayer::Pause()
697 {
698   CLog::Log(LOGDEBUG, "CAMLPlayer::Pause");
699   CSingleLock lock(m_aml_csection);
700
701   if ((m_pid < 0) && m_bAbortRequest)
702     return;
703
704   if (m_paused)
705     m_dll->player_resume(m_pid);
706   else
707     m_dll->player_pause(m_pid);
708
709   m_paused = !m_paused;
710 }
711
712 bool CAMLPlayer::IsPaused() const
713 {
714   return m_paused;
715 }
716
717 bool CAMLPlayer::HasVideo() const
718 {
719   return m_video_count > 0;
720 }
721
722 bool CAMLPlayer::HasAudio() const
723 {
724   return m_audio_count > 0;
725 }
726
727 void CAMLPlayer::ToggleFrameDrop()
728 {
729   CLog::Log(LOGDEBUG, "CAMLPlayer::ToggleFrameDrop");
730 }
731
732 bool CAMLPlayer::CanSeek()
733 {
734   return GetTotalTime() > 0;
735 }
736
737 void CAMLPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
738 {
739   // force updated to m_elapsed_ms, m_duration_ms.
740   GetStatus();
741
742   CSingleLock lock(m_aml_csection);
743
744   // try chapter seeking first, chapter_index is ones based.
745   int chapter_index = GetChapter();
746   if (bLargeStep && bChapterOverride && chapter_index > 0)
747   {
748     if (!bPlus)
749     {
750       // seek to previous chapter
751       SeekChapter(chapter_index - 1);
752       return;
753     }
754     else if (chapter_index < GetChapterCount())
755     {
756       // seek to next chapter
757       SeekChapter(chapter_index + 1);
758       return;
759     }
760   }
761
762   int64_t seek_ms;
763   if (g_advancedSettings.m_videoUseTimeSeeking)
764   {
765     if (bLargeStep && (GetTotalTime() > (2000 * g_advancedSettings.m_videoTimeSeekForwardBig)))
766       seek_ms = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig;
767     else
768       seek_ms = bPlus ? g_advancedSettings.m_videoTimeSeekForward    : g_advancedSettings.m_videoTimeSeekBackward;
769     // convert to milliseconds
770     seek_ms *= 1000;
771     seek_ms += m_elapsed_ms;
772   }
773   else
774   {
775     float percent;
776     if (bLargeStep)
777       percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig;
778     else
779       percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward    : g_advancedSettings.m_videoPercentSeekBackward;
780     percent /= 100.0f;
781     percent += (float)m_elapsed_ms/(float)m_duration_ms;
782     // convert to milliseconds
783     seek_ms = m_duration_ms * percent;
784   }
785
786   // handle stacked videos, dvdplayer does it so we do it too.
787   if (g_application.CurrentFileItem().IsStack() &&
788     (seek_ms > m_duration_ms || seek_ms < 0))
789   {
790     CLog::Log(LOGDEBUG, "CAMLPlayer::Seek: In mystery code, what did I do");
791     g_application.SeekTime((seek_ms - m_elapsed_ms) * 0.001 + g_application.GetTime());
792     // warning, don't access any object variables here as
793     // the object may have been destroyed
794     return;
795   }
796
797   if (seek_ms <= 1000)
798     seek_ms = 1000;
799
800   if (seek_ms > m_duration_ms)
801     seek_ms = m_duration_ms;
802
803   // do seek here
804   g_infoManager.SetDisplayAfterSeek(100000);
805   SeekTime(seek_ms);
806   m_callback.OnPlayBackSeek((int)seek_ms, (int)(seek_ms - m_elapsed_ms));
807   g_infoManager.SetDisplayAfterSeek();
808 }
809
810 bool CAMLPlayer::SeekScene(bool bPlus)
811 {
812   CLog::Log(LOGDEBUG, "CAMLPlayer::SeekScene");
813   return false;
814 }
815
816 void CAMLPlayer::SeekPercentage(float fPercent)
817 {
818   CSingleLock lock(m_aml_csection);
819
820   // force updated to m_elapsed_ms, m_duration_ms.
821   GetStatus();
822
823   if (m_duration_ms)
824   {
825     int64_t seek_ms = fPercent * m_duration_ms / 100.0;
826     if (seek_ms <= 1000)
827       seek_ms = 1000;
828
829     // do seek here
830     g_infoManager.SetDisplayAfterSeek(100000);
831     SeekTime(seek_ms);
832     m_callback.OnPlayBackSeek((int)seek_ms, (int)(seek_ms - m_elapsed_ms));
833     g_infoManager.SetDisplayAfterSeek();
834   }
835 }
836
837 float CAMLPlayer::GetPercentage()
838 {
839   GetStatus();
840   if (m_duration_ms)
841     return 100.0f * (float)m_elapsed_ms/(float)m_duration_ms;
842   else
843     return 0.0f;
844 }
845
846 void CAMLPlayer::SetMute(bool bOnOff)
847 {
848   m_audio_mute = bOnOff;
849
850 #if defined(HAS_AMLPLAYER_AUDIO_SETVOLUME)
851   CSingleLock lock(m_aml_csection);
852   if (m_dll->check_pid_valid(m_pid))
853   {
854     if (m_audio_mute)
855       m_dll->audio_set_volume(m_pid, 0.0);
856     else
857       m_dll->audio_set_volume(m_pid, m_audio_volume);
858   }
859 #endif
860 }
861
862 void CAMLPlayer::SetVolume(float volume)
863 {
864   // volume is a float percent from 0.0 to 1.0
865   m_audio_volume = VolumePercentToScale(volume);
866
867 #if defined(HAS_AMLPLAYER_AUDIO_SETVOLUME)
868   CSingleLock lock(m_aml_csection);
869   if (!m_audio_mute && m_dll->check_pid_valid(m_pid))
870     m_dll->audio_set_volume(m_pid, m_audio_volume);
871 #endif
872 }
873
874 void CAMLPlayer::GetAudioInfo(CStdString &strAudioInfo)
875 {
876   CSingleLock lock(m_aml_csection);
877   if (m_audio_streams.empty() || m_audio_index > (int)(m_audio_streams.size() - 1))
878     return;
879
880   strAudioInfo.Format("Audio stream (%s) [Kb/s:%.2f]",
881     AudioCodecName(m_audio_streams[m_audio_index]->format),
882     (double)m_audio_streams[m_audio_index]->bit_rate / 1024.0);
883 }
884
885 void CAMLPlayer::GetVideoInfo(CStdString &strVideoInfo)
886 {
887   CSingleLock lock(m_aml_csection);
888   if (m_video_streams.empty() || m_video_index > (int)(m_video_streams.size() - 1))
889     return;
890
891   strVideoInfo.Format("Video stream (%s) [fr:%.3f Mb/s:%.2f]",
892     VideoCodecName(m_video_streams[m_video_index]->format),
893     GetActualFPS(),
894     (double)m_video_streams[m_video_index]->bit_rate / (1024.0*1024.0));
895 }
896
897 int CAMLPlayer::GetAudioStreamCount()
898 {
899   //CLog::Log(LOGDEBUG, "CAMLPlayer::GetAudioStreamCount");
900   return m_audio_count;
901 }
902
903 int CAMLPlayer::GetAudioStream()
904 {
905   //CLog::Log(LOGDEBUG, "CAMLPlayer::GetAudioStream");
906   return m_audio_index;
907 }
908
909 void CAMLPlayer::SetAudioStream(int SetAudioStream)
910 {
911   //CLog::Log(LOGDEBUG, "CAMLPlayer::SetAudioStream");
912   CSingleLock lock(m_aml_csection);
913
914   if (SetAudioStream > (int)m_audio_streams.size() || SetAudioStream < 0)
915     return;
916
917   m_audio_index = SetAudioStream;
918   SetAudioPassThrough(m_audio_streams[m_audio_index]->format);
919
920   if (m_dll->check_pid_valid(m_pid))
921   {
922     m_dll->player_aid(m_pid, m_audio_streams[m_audio_index]->id);
923   }
924 }
925
926 void CAMLPlayer::SetAVDelay(float fValue)
927 {
928   CLog::Log(LOGDEBUG, "CAMLPlayer::SetAVDelay (%f)", fValue);
929   m_audio_delay = fValue * 1000.0;
930
931 #if defined(HAS_AMLPLAYER_AUDIO_SETDELAY)
932   if (m_audio_streams.size() && m_dll->check_pid_valid(m_pid))
933   {
934     CSingleLock lock(m_aml_csection);
935     m_dll->audio_set_delay(m_pid, m_audio_delay);
936   }
937 #endif
938 }
939
940 float CAMLPlayer::GetAVDelay()
941 {
942   return (float)m_audio_delay / 1000.0;
943 }
944
945 void CAMLPlayer::SetSubTitleDelay(float fValue = 0.0f)
946 {
947   if (GetSubtitleCount())
948   {
949     CSingleLock lock(m_aml_csection);
950     m_subtitle_delay = fValue * 1000.0;
951   }
952 }
953
954 float CAMLPlayer::GetSubTitleDelay()
955 {
956   return (float)m_subtitle_delay / 1000.0;
957 }
958
959 int CAMLPlayer::GetSubtitleCount()
960 {
961   return m_subtitle_count;
962 }
963
964 int CAMLPlayer::GetSubtitle()
965 {
966   if (m_subtitle_show)
967     return m_subtitle_index;
968   else
969     return -1;
970 }
971
972 void CAMLPlayer::GetSubtitleStreamInfo(int index, SPlayerSubtitleStreamInfo &info)
973 {
974   CSingleLock lock(m_aml_csection);
975
976   if (index > (int)m_subtitle_streams.size() -1 || index < 0)
977     return;
978
979   info.language = m_subtitle_streams[index]->language;
980   info.name = m_subtitle_streams[m_subtitle_index]->name;
981
982   if (m_log_level > 5)
983     CLog::Log(LOGDEBUG, "CAMLPlayer::GetSubtitleName, iStream(%d)", index);
984 }
985  
986 void CAMLPlayer::SetSubtitle(int iStream)
987 {
988   CSingleLock lock(m_aml_csection);
989
990   if (iStream > (int)m_subtitle_streams.size() || iStream < 0)
991     return;
992
993   m_subtitle_index = iStream;
994
995   // smells like a bug, if no showing subs and we get called
996   // to set the subtitle, we are expected to update internal state
997   // but not show the subtitle.
998   if (!m_subtitle_show)
999     return;
1000
1001   if (m_dll->check_pid_valid(m_pid) && m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE)
1002   {
1003     m_dll->player_sid(m_pid, m_subtitle_streams[m_subtitle_index]->id);
1004     aml_set_sysfs_int("/sys/class/subtitle/curr", m_subtitle_index);
1005     if (m_subtitle_thread)
1006       m_subtitle_thread->Flush();
1007   }
1008   else
1009   {
1010     m_dvdPlayerSubtitle->CloseStream(true);
1011     OpenSubtitleStream(m_subtitle_index);
1012   }
1013 }
1014
1015 bool CAMLPlayer::GetSubtitleVisible()
1016 {
1017   return m_subtitle_show;
1018 }
1019
1020 void CAMLPlayer::SetSubtitleVisible(bool bVisible)
1021 {
1022   m_subtitle_show = (bVisible && m_subtitle_count);
1023   CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn = bVisible;
1024
1025   if (m_subtitle_show  && m_subtitle_count)
1026   {
1027     if (CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream < m_subtitle_count)
1028       m_subtitle_index = CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream;
1029     // on startup, if asked to show subs and SetSubtitle has not
1030     // been called, we are expected to switch/show the 1st subtitle
1031     if (m_subtitle_index < 0)
1032       m_subtitle_index = 0;
1033     if (m_dll->check_pid_valid(m_pid) && m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE)
1034     {
1035       m_dll->player_sid(m_pid, m_subtitle_streams[m_subtitle_index]->id);
1036       aml_set_sysfs_int("/sys/class/subtitle/curr", m_subtitle_index);
1037       if (m_subtitle_thread)
1038         m_subtitle_thread->Flush();
1039     }
1040     else
1041       OpenSubtitleStream(m_subtitle_index);
1042   }
1043 }
1044
1045 int CAMLPlayer::AddSubtitle(const CStdString& strSubPath)
1046 {
1047   CSingleLock lock(m_aml_csection);
1048
1049   return AddSubtitleFile(strSubPath);
1050 }
1051
1052 void CAMLPlayer::GetVideoRect(CRect& SrcRect, CRect& DestRect)
1053 {
1054   g_renderManager.GetVideoRect(SrcRect, DestRect);
1055 }
1056
1057 void CAMLPlayer::GetVideoAspectRatio(float &fAR)
1058 {
1059   fAR = g_renderManager.GetAspectRatio();
1060 }
1061
1062 int CAMLPlayer::GetChapterCount()
1063 {
1064   return m_chapter_count;
1065 }
1066
1067 int CAMLPlayer::GetChapter()
1068 {
1069   // returns a one based value or zero if no chapters
1070   GetStatus();
1071
1072   int chapter_index = -1;
1073   for (int i = 0; i < m_chapter_count; i++)
1074   {
1075     if (m_elapsed_ms >= m_chapters[i]->seekto_ms)
1076       chapter_index = i;
1077   }
1078   return chapter_index + 1;
1079 }
1080
1081 void CAMLPlayer::GetChapterName(CStdString& strChapterName)
1082 {
1083   if (m_chapter_count)
1084     strChapterName = m_chapters[GetChapter() - 1]->name;
1085 }
1086
1087 int CAMLPlayer::SeekChapter(int chapter_index)
1088 {
1089   CSingleLock lock(m_aml_csection);
1090
1091   // chapter_index is a one based value.
1092   if (m_chapter_count > 1)
1093   {
1094     if (chapter_index < 1)
1095       chapter_index = 1;
1096     if (chapter_index > m_chapter_count)
1097       return 0;
1098
1099     // time units are seconds,
1100     // so we add 1000ms to get into the chapter.
1101     int64_t seek_ms = m_chapters[chapter_index - 1]->seekto_ms + 1000;
1102
1103     //  seek to 1 second and play is immediate.
1104     if (seek_ms <= 0)
1105       seek_ms = 1000;
1106
1107     // seek to chapter here
1108     g_infoManager.SetDisplayAfterSeek(100000);
1109     SeekTime(seek_ms);
1110     m_callback.OnPlayBackSeekChapter(chapter_index);
1111     g_infoManager.SetDisplayAfterSeek();
1112   }
1113   else
1114   {
1115     // we do not have a chapter list so do a regular big jump.
1116     if (chapter_index > 0)
1117       Seek(true,  true);
1118     else
1119       Seek(false, true);
1120   }
1121   return 0;
1122 }
1123
1124 float CAMLPlayer::GetActualFPS()
1125 {
1126   float video_fps = m_video_fps_numerator / m_video_fps_denominator;
1127   CLog::Log(LOGDEBUG, "CAMLPlayer::GetActualFPS:m_video_fps(%f)", video_fps);
1128   return video_fps;
1129 }
1130
1131 void CAMLPlayer::SeekTime(int64_t seek_ms)
1132 {
1133   CSingleLock lock(m_aml_csection);
1134
1135   // we cannot seek if paused
1136   if (m_paused)
1137     return;
1138
1139   if (seek_ms <= 0)
1140     seek_ms = 100;
1141
1142   // seek here
1143   if (m_dll->check_pid_valid(m_pid))
1144   {
1145     if (!CheckPlaying())
1146       return;
1147     // player_timesearch is seconds (float).
1148     m_dll->player_timesearch(m_pid, (float)seek_ms/1000.0);
1149     WaitForSearchOK(5000);
1150     WaitForPlaying(5000);
1151   }
1152 }
1153
1154 int64_t CAMLPlayer::GetTime()
1155 {
1156   return m_elapsed_ms;
1157 }
1158
1159 int64_t CAMLPlayer::GetTotalTime()
1160 {
1161   return m_duration_ms;
1162 }
1163
1164 void CAMLPlayer::GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info)
1165 {
1166   CSingleLock lock(m_aml_csection);
1167   if (index < 0 || m_audio_streams.empty() || index > (int)(m_audio_streams.size() - 1))
1168     return;
1169
1170   info.bitrate = m_audio_streams[index]->bit_rate;
1171
1172   info.language = m_audio_streams[index]->language;
1173
1174   info.channels = m_audio_streams[index]->channel;
1175
1176   info.audioCodecName = AudioCodecName(m_audio_streams[index]->format);
1177
1178   if (info.audioCodecName.size())
1179     info.name = info.audioCodecName + " ";
1180
1181   switch(info.channels)
1182   {
1183   case 1:
1184     info.name += "Mono";
1185     break;
1186   case 2: 
1187     info.name += "Stereo";
1188     break;
1189   case 6: 
1190     info.name += "5.1";
1191     break;
1192   case 7:
1193     info.name += "6.1";
1194     break;
1195   case 8:
1196     info.name += "7.1";
1197     break;
1198   default:
1199     char temp[32];
1200     sprintf(temp, "%d-chs", info.channels);
1201     info.name += temp;
1202   }
1203 }
1204
1205 void CAMLPlayer::GetVideoStreamInfo(SPlayerVideoStreamInfo &info)
1206 {
1207   CSingleLock lock(m_aml_csection);
1208   if (m_video_streams.empty() || m_video_index > (int)(m_video_streams.size() - 1))
1209     return;
1210
1211   info.bitrate = m_video_streams[m_video_index]->bit_rate;
1212   info.videoCodecName = VideoCodecName(m_video_streams[m_video_index]->format);
1213   info.videoAspectRatio = g_renderManager.GetAspectRatio();
1214   g_renderManager.GetVideoRect(info.SrcRect, info.DestRect);
1215 }
1216
1217 int CAMLPlayer::GetSourceBitrate()
1218 {
1219   CLog::Log(LOGDEBUG, "CAMLPlayer::GetSourceBitrate");
1220   return 0;
1221 }
1222
1223 int CAMLPlayer::GetBitsPerSample()
1224 {
1225   CLog::Log(LOGDEBUG, "CAMLPlayer::GetBitsPerSample");
1226   return 0;
1227 }
1228
1229 int CAMLPlayer::GetSampleRate()
1230 {
1231   CSingleLock lock(m_aml_csection);
1232   if (m_audio_streams.empty() || m_audio_index > (int)(m_audio_streams.size() - 1))
1233     return 0;
1234   
1235   return m_audio_streams[m_audio_index]->sample_rate;
1236 }
1237
1238 int CAMLPlayer::GetPictureWidth()
1239 {
1240   //CLog::Log(LOGDEBUG, "CAMLPlayer::GetPictureWidth(%d)", m_video_width);
1241   return m_video_width;
1242 }
1243
1244 int CAMLPlayer::GetPictureHeight()
1245 {
1246   //CLog::Log(LOGDEBUG, "CAMLPlayer::GetPictureHeight(%)", m_video_height);
1247   return m_video_height;
1248 }
1249
1250 bool CAMLPlayer::GetStreamDetails(CStreamDetails &details)
1251 {
1252   //CLog::Log(LOGDEBUG, "CAMLPlayer::GetStreamDetails");
1253   return false;
1254 }
1255
1256 void CAMLPlayer::ToFFRW(int iSpeed)
1257 {
1258   CLog::Log(LOGDEBUG, "CAMLPlayer::ToFFRW: iSpeed(%d), m_speed(%d)", iSpeed, m_speed);
1259   CSingleLock lock(m_aml_csection);
1260
1261   if (!m_dll->check_pid_valid(m_pid) && m_bAbortRequest)
1262     return;
1263
1264   if (m_speed != iSpeed)
1265   {
1266     // recover power of two value
1267     int ipower = 0;
1268     int ispeed = abs(iSpeed);
1269     while (ispeed >>= 1) ipower++;
1270
1271     switch(ipower)
1272     {
1273       // regular playback
1274       case  0:
1275         m_dll->player_forward(m_pid, 0);
1276         break;
1277       default:
1278         // N x fast forward/rewind (I-frames)
1279         // speed playback 1,2,4,8
1280         if (iSpeed > 0)
1281           m_dll->player_forward(m_pid,   iSpeed);
1282         else
1283           m_dll->player_backward(m_pid, -iSpeed);
1284         break;
1285     }
1286
1287     m_speed = iSpeed;
1288   }
1289 }
1290
1291 bool CAMLPlayer::GetCurrentSubtitle(CStdString& strSubtitle)
1292 {
1293   strSubtitle = "";
1294
1295   if (m_subtitle_count)
1296   {
1297     // force updated to m_elapsed_ms.
1298     GetStatus();
1299     if (m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE && m_subtitle_thread)
1300     {
1301       m_subtitle_thread->UpdateSubtitle(strSubtitle, m_elapsed_ms - m_subtitle_delay);
1302     }
1303     else
1304     {
1305       double pts = DVD_MSEC_TO_TIME(m_elapsed_ms) - DVD_MSEC_TO_TIME(m_subtitle_delay);
1306       m_dvdOverlayContainer->CleanUp(pts);
1307       m_dvdPlayerSubtitle->GetCurrentSubtitle(strSubtitle, pts);
1308     }
1309   }
1310
1311   return !strSubtitle.IsEmpty();
1312 }
1313
1314 void CAMLPlayer::OnStartup()
1315 {
1316   //m_CurrentVideo.Clear();
1317   //m_CurrentAudio.Clear();
1318   //m_CurrentSubtitle.Clear();
1319
1320   //CThread::SetName("AMLPlayer");
1321 }
1322
1323 void CAMLPlayer::OnExit()
1324 {
1325   //CLog::Log(LOGNOTICE, "CAMLPlayer::OnExit()");
1326
1327   m_bStop = true;
1328   // if we didn't stop playing, advance to the next item in xbmc's playlist
1329   if (m_options.identify == false)
1330   {
1331     if (m_bAbortRequest)
1332       m_callback.OnPlayBackStopped();
1333     else
1334       m_callback.OnPlayBackEnded();
1335   }
1336   // set event to inform openfile something went wrong
1337   // in case openfile is still waiting for this event
1338   m_ready.Set();
1339 }
1340
1341 void CAMLPlayer::Process()
1342 {
1343   CLog::Log(LOGNOTICE, "CAMLPlayer::Process");
1344   try
1345   {
1346     static AML_URLProtocol vfs_protocol = {
1347       "vfs",
1348       CFileURLProtocol::Open,
1349       CFileURLProtocol::Read,
1350       CFileURLProtocol::Write,
1351       CFileURLProtocol::Seek,
1352       CFileURLProtocol::SeekEx,
1353       CFileURLProtocol::Close,
1354     };
1355
1356     CStdString url = m_item.GetPath();
1357     if (url.Left(strlen("smb://")).Equals("smb://"))
1358     {
1359       // the name string needs to persist
1360       static const char *smb_name = "smb";
1361       vfs_protocol.name = smb_name;
1362     }
1363     else if (url.Left(strlen("afp://")).Equals("afp://"))
1364     {
1365       // the name string needs to persist
1366       static const char *afp_name = "afp";
1367       vfs_protocol.name = afp_name;
1368     }
1369     else if (url.Left(strlen("nfs://")).Equals("nfs://"))
1370     {
1371       // the name string needs to persist
1372       static const char *nfs_name = "nfs";
1373       vfs_protocol.name = nfs_name;
1374     }
1375     else if (url.Left(strlen("rar://")).Equals("rar://"))
1376     {
1377       // the name string needs to persist
1378       static const char *rar_name = "rar";
1379       vfs_protocol.name = rar_name;
1380     }
1381     else if (url.Left(strlen("ftp://")).Equals("ftp://"))
1382     {
1383       // the name string needs to persist
1384       static const char *http_name = "xb-ftp";
1385       vfs_protocol.name = http_name;
1386       url = "xb-" + url;
1387     }
1388     else if (url.Left(strlen("ftps://")).Equals("ftps://"))
1389     {
1390       // the name string needs to persist
1391       static const char *http_name = "xb-ftps";
1392       vfs_protocol.name = http_name;
1393       url = "xb-" + url;
1394     }
1395     else if (url.Left(strlen("http://")).Equals("http://"))
1396     {
1397       // the name string needs to persist
1398       static const char *http_name = "xb-http";
1399       vfs_protocol.name = http_name;
1400       url = "xb-" + url;
1401     }
1402     else if (url.Left(strlen("https://")).Equals("https://"))
1403     {
1404       // the name string needs to persist
1405       static const char *http_name = "xb-https";
1406       vfs_protocol.name = http_name;
1407       url = "xb-" + url;
1408     }
1409     else if (url.Left(strlen("hdhomerun://")).Equals("hdhomerun://"))
1410     {
1411       // the name string needs to persist
1412       static const char *http_name = "xb-hdhomerun";
1413       vfs_protocol.name = http_name;
1414       url = "xb-" + url;
1415     }
1416     else if (url.Left(strlen("sftp://")).Equals("sftp://"))
1417     {
1418       // the name string needs to persist
1419       static const char *http_name = "xb-sftp";
1420       vfs_protocol.name = http_name;
1421       url = "xb-" + url;
1422     }
1423     else if (url.Left(strlen("udp://")).Equals("udp://"))
1424     {
1425       std::string udp_params;
1426       // bump up the default udp params for ffmpeg.
1427       // ffmpeg will strip out 'dummy=10', we only add it
1428       // to make the logic below with prpending '&' work right.
1429       // to watch for udp errors, 'cat /proc/net/udp'
1430       if (url.find("?") == std::string::npos)
1431         udp_params.append("?dummy=10");
1432       if (url.find("pkt_size=") == std::string::npos)
1433         udp_params.append("&pkt_size=5264");
1434       if (url.find("buffer_size=") == std::string::npos)
1435         udp_params.append("&buffer_size=5390336");
1436       // newer ffmpeg uses fifo_size instead of buf_size
1437       if (url.find("buf_size=") == std::string::npos)
1438         udp_params.append("&buf_size=5390336");
1439
1440       if (udp_params.size() > 0)
1441         url.append(udp_params);
1442     }
1443     CLog::Log(LOGDEBUG, "CAMLPlayer::Process: URL=%s", url.c_str());
1444
1445     if (m_dll->player_init() != PLAYER_SUCCESS)
1446     {
1447       CLog::Log(LOGDEBUG, "player init failed");
1448       throw "CAMLPlayer::Process:player init failed";
1449     }
1450     CLog::Log(LOGDEBUG, "player init......");
1451     usleep(50 * 1000);
1452
1453     // must be after player_init
1454     m_dll->av_register_protocol2(&vfs_protocol, sizeof(vfs_protocol));
1455
1456     static play_control_t play_control;
1457     memset(&play_control, 0, sizeof(play_control_t));
1458     // if we do not register a callback,
1459     // then the libamplayer will free run checking status.
1460     m_dll->player_register_update_callback(&play_control.callback_fn, &UpdatePlayerInfo, 1000);
1461     // amlplayer owns file_name and will release on exit
1462     play_control.file_name = (char*)strdup(url.c_str());
1463     //play_control->nosound   = 1; // if disable audio...,must call this api
1464     play_control.video_index = -1; //MUST
1465     play_control.audio_index = -1; //MUST
1466     play_control.sub_index   = -1; //MUST
1467     play_control.hassub      =  1;
1468     if (m_options.starttime > 0)   // player start position in seconds as is starttime
1469       play_control.t_pos = m_options.starttime;
1470     else
1471       play_control.t_pos     = -1;
1472     play_control.need_start  =  1; // if 0,you can omit player_start_play API.
1473                                    // just play video/audio immediately.
1474                                    // if 1,then need call "player_start_play" API;
1475     play_control.displast_frame = 0; // 0:black out when player exit    1:keep last frame when player exit
1476
1477     // tweak player playback buffers for udp
1478     if (url.Left(strlen("udp://")).Equals("udp://"))
1479     {
1480       play_control.auto_buffing_enable = 1;
1481       play_control.buffing_min        = 0.01; // default = 0.01
1482       play_control.buffing_middle     = 0.02; // default = 0.02
1483       play_control.buffing_max        = 0.20; // default = 0.80
1484       play_control.byteiobufsize      = 1024 * 128; // maps to av_open_input_file buffer size (1024 * 32)
1485       //play_control.loopbufsize        =;
1486       //play_control.enable_rw_on_pause =;
1487     }
1488
1489     m_aml_state.clear();
1490     m_aml_state.push_back(0);
1491     m_pid = m_dll->player_start(&play_control, 0);
1492     if (m_pid < 0)
1493     {
1494       if (m_log_level > 5)
1495         CLog::Log(LOGDEBUG, "player start failed! error = %d", m_pid);
1496       throw "CAMLPlayer::Process:player start failed";
1497     }
1498
1499     // wait for media to open with 30 second timeout.
1500     if (WaitForFormatValid(30000))
1501     {
1502       // start the playback.
1503       int res = m_dll->player_start_play(m_pid);
1504       if (res != PLAYER_SUCCESS)
1505         throw "CAMLPlayer::Process:player_start_play() failed";
1506     }
1507     else
1508     {
1509       throw "CAMLPlayer::Process:WaitForFormatValid timeout";
1510     }
1511
1512     // hide the mainvideo layer so we can get stream info
1513     // and setup/transition to gui video playback
1514     // without having video playback blended into it.
1515     if (m_item.IsVideo())
1516       ShowMainVideo(false);
1517
1518     // wait for playback to start with 20 second timeout
1519     if (WaitForPlaying(20000))
1520     {
1521       m_speed = 1;
1522       m_callback.OnPlayBackSpeedChanged(m_speed);
1523
1524       // get our initial status.
1525       GetStatus();
1526
1527       // the default staturation is too high, drop it
1528       SetVideoSaturation(110);
1529
1530       // drop CGUIDialogBusy dialog and release the hold in OpenFile.
1531       m_ready.Set();
1532
1533       // we are playing but hidden and all stream fields are valid.
1534       // check for video in media content
1535       if (GetVideoStreamCount() > 0)
1536       {
1537         SetAVDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_AudioDelay);
1538
1539         // turn on/off subs
1540         SetSubtitleVisible(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn);
1541         SetSubTitleDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleDelay);
1542
1543         // setup renderer for bypass. This tell renderer to get out of the way as
1544         // hw decoder will be doing the actual video rendering in a video plane
1545         // that is under the GUI layer.
1546         int width  = GetPictureWidth();
1547         int height = GetPictureHeight();
1548         double fFrameRate = GetActualFPS();
1549         unsigned int flags = 0;
1550
1551         flags |= CONF_FLAGS_FULLSCREEN;
1552         CStdString formatstr = "BYPASS";
1553         CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f. format: %s",
1554           __FUNCTION__, width, height, fFrameRate, formatstr.c_str());
1555         g_renderManager.IsConfigured();
1556         if (!g_renderManager.Configure(width, height, width, height, fFrameRate, flags, RENDER_FMT_BYPASS, 0, 0))
1557         {
1558           CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__);
1559         }
1560         if (!g_renderManager.IsStarted())
1561         {
1562           CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__);
1563         }
1564
1565         g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack);
1566
1567         m_subtitle_thread = new CAMLSubTitleThread(m_dll);
1568         m_subtitle_thread->Create();
1569       }
1570
1571       if (m_options.identify == false)
1572         m_callback.OnPlayBackStarted();
1573
1574       bool stopPlaying = false;
1575       while (!m_bAbortRequest && !stopPlaying)
1576       {
1577         player_status pstatus = (player_status)GetPlayerSerializedState();
1578         switch(pstatus)
1579         {
1580           case PLAYER_INITING:
1581           case PLAYER_TYPE_REDY:
1582           case PLAYER_INITOK:
1583             if (m_log_level > 5)
1584               CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus));
1585             // player is parsing file, decoder not running
1586             break;
1587
1588           default:
1589           case PLAYER_RUNNING:
1590             GetStatus();
1591             // playback status, decoder is running
1592             break;
1593
1594           case PLAYER_START:
1595           case PLAYER_BUFFERING:
1596           case PLAYER_PAUSE:
1597           case PLAYER_SEARCHING:
1598           case PLAYER_SEARCHOK:
1599           case PLAYER_FF_END:
1600           case PLAYER_FB_END:
1601           case PLAYER_PLAY_NEXT:
1602           case PLAYER_BUFFER_OK:
1603             if (m_log_level > 5)
1604               CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus));
1605             break;
1606
1607           case PLAYER_FOUND_SUB:
1608             // found a NEW subtitle in stream.
1609             // TODO: reload m_subtitle_streams
1610             if (m_log_level > 5)
1611               CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus));
1612             break;
1613
1614           case PLAYER_PLAYEND:
1615             GetStatus();
1616             if (m_log_level > 5)
1617               CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus));
1618             break;
1619
1620           case PLAYER_ERROR:
1621             if (m_log_level > 5)
1622             {
1623               printf("CAMLPlayer::Process PLAYER_ERROR\n");
1624               printf("CAMLPlayer::Process: %s\n", m_dll->player_status2str(pstatus));
1625             }
1626             m_bAbortRequest = true;
1627             break;
1628
1629           case PLAYER_STOPED:
1630           case PLAYER_EXIT:
1631             if (m_log_level > 5)
1632             {
1633               CLog::Log(LOGDEBUG, "CAMLPlayer::Process PLAYER_STOPPED");
1634               CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus));
1635             }
1636             stopPlaying = true;
1637             break;
1638         }
1639         if (!stopPlaying)
1640           usleep(250 * 1000);
1641       }
1642     }
1643   }
1644   catch(char* error)
1645   {
1646     CLog::Log(LOGERROR, "%s", error);
1647   }
1648   catch(...)
1649   {
1650     CLog::Log(LOGERROR, "CAMLPlayer::Process Exception thrown");
1651   }
1652
1653   if (m_log_level > 5)
1654     CLog::Log(LOGDEBUG, "CAMLPlayer::Process stopped");
1655   if (m_dll->check_pid_valid(m_pid))
1656   {
1657     delete m_subtitle_thread;
1658     m_subtitle_thread = NULL;
1659     m_dll->player_stop(m_pid);
1660     m_dll->player_exit(m_pid);
1661     m_pid = -1;
1662   }
1663
1664   // we are done, hide the mainvideo layer.
1665   ShowMainVideo(false);
1666   m_ready.Set();
1667
1668   ClearStreamInfos();
1669
1670   // reset ac3/dts passthough
1671   SetAudioPassThrough(AFORMAT_UNKNOWN);
1672
1673   if (m_log_level > 5)
1674     CLog::Log(LOGDEBUG, "CAMLPlayer::Process exit");
1675 }
1676
1677 void CAMLPlayer::GetRenderFeatures(std::vector<int> &renderFeatures)
1678 {
1679   renderFeatures.push_back(RENDERFEATURE_ZOOM);
1680   renderFeatures.push_back(RENDERFEATURE_CONTRAST);
1681   renderFeatures.push_back(RENDERFEATURE_BRIGHTNESS);
1682   renderFeatures.push_back(RENDERFEATURE_STRETCH);
1683 }
1684
1685 void CAMLPlayer::GetDeinterlaceMethods(std::vector<int> &deinterlaceMethods)
1686 {
1687   deinterlaceMethods.push_back(VS_INTERLACEMETHOD_DEINTERLACE);
1688 }
1689
1690 void CAMLPlayer::GetDeinterlaceModes(std::vector<int> &deinterlaceModes)
1691 {
1692   deinterlaceModes.push_back(VS_DEINTERLACEMODE_AUTO);
1693 }
1694
1695 void CAMLPlayer::GetScalingMethods(std::vector<int> &scalingMethods)
1696 {
1697 }
1698
1699 void CAMLPlayer::GetAudioCapabilities(std::vector<int> &audioCaps)
1700 {
1701   audioCaps.push_back(IPC_AUD_SELECT_STREAM);
1702   audioCaps.push_back(IPC_AUD_SELECT_OUTPUT);
1703 #if defined(HAS_AMLPLAYER_AUDIO_SETDELAY)
1704   audioCaps.push_back(IPC_AUD_OFFSET);
1705 #endif
1706 }
1707
1708 void CAMLPlayer::GetSubtitleCapabilities(std::vector<int> &subCaps)
1709 {
1710   subCaps.push_back(IPC_SUBS_EXTERNAL);
1711   subCaps.push_back(IPC_SUBS_SELECT);
1712   subCaps.push_back(IPC_SUBS_OFFSET);
1713 }
1714
1715
1716 ////////////////////////////////////////////////////////////////////////////////////////////
1717 ////////////////////////////////////////////////////////////////////////////////////////////
1718 int CAMLPlayer::GetVideoStreamCount()
1719 {
1720   //CLog::Log(LOGDEBUG, "CAMLPlayer::GetVideoStreamCount(%d)", m_video_count);
1721   return m_video_count;
1722 }
1723
1724 void CAMLPlayer::ShowMainVideo(bool show)
1725 {
1726   if (m_show_mainvideo == show)
1727     return;
1728
1729   aml_set_sysfs_int("/sys/class/video/disable_video", show ? 0:1);
1730
1731   m_show_mainvideo = show;
1732 }
1733
1734 void CAMLPlayer::SetVideoZoom(float zoom)
1735 {
1736   // input zoom range is 0.5 to 2.0 with a default of 1.0.
1737   // output zoom range is 2 to 300 with default of 100.
1738   // we limit that to a range of 50 to 200 with default of 100.
1739   aml_set_sysfs_int("/sys/class/video/zoom", (int)(100 * zoom));
1740 }
1741
1742 void CAMLPlayer::SetVideoContrast(int contrast)
1743 {
1744   // input contrast range is 0 to 100 with default of 50.
1745   // output contrast range is -255 to 255 with default of 0.
1746   contrast = (255 * (contrast - 50)) / 50;
1747   aml_set_sysfs_int("/sys/class/video/contrast", contrast);
1748 }
1749 void CAMLPlayer::SetVideoBrightness(int brightness)
1750 {
1751   // input brightness range is 0 to 100 with default of 50.
1752   // output brightness range is -127 to 127 with default of 0.
1753   brightness = (127 * (brightness - 50)) / 50;
1754   aml_set_sysfs_int("/sys/class/video/brightness", brightness);
1755 }
1756 void CAMLPlayer::SetVideoSaturation(int saturation)
1757 {
1758   // output saturation range is -127 to 127 with default of 127.
1759   aml_set_sysfs_int("/sys/class/video/saturation", saturation);
1760 }
1761
1762 void CAMLPlayer::SetAudioPassThrough(int format)
1763 {
1764   aml_set_audio_passthrough(
1765     (m_audio_passthrough_ac3 && format == AFORMAT_AC3) ||
1766     (m_audio_passthrough_dts && format == AFORMAT_DTS));
1767 }
1768
1769 int CAMLPlayer::GetPlayerSerializedState(void)
1770 {
1771   CSingleLock lock(m_aml_state_csection);
1772
1773   int playerstate;
1774   int dequeue_size = m_aml_state.size();
1775
1776   if (dequeue_size > 0)
1777   {
1778     // serialized state is the front element.
1779     playerstate = m_aml_state.front();
1780     // pop the front element if there are
1781     // more present.
1782     if (dequeue_size > 1)
1783       m_aml_state.pop_front();
1784   }
1785   else
1786   {
1787     // if queue is empty (only at startup),
1788     // pull the player state directly. this should
1789     // really never happen but we need to cover it.
1790     playerstate = m_dll->player_get_state(m_pid);
1791     m_aml_state.push_back(playerstate);
1792   }
1793
1794   return playerstate;
1795 }
1796
1797
1798 int CAMLPlayer::UpdatePlayerInfo(int pid, player_info_t *info)
1799 {
1800   // we get called when status changes or after update time expires.
1801   // static callback from libamplayer, since it does not pass an opaque,
1802   // we have to retreve our player class reference the hard way.
1803   boost::shared_ptr<CAMLPlayer> amlplayer = boost::dynamic_pointer_cast<CAMLPlayer>(g_application.m_pPlayer->GetInternal());
1804   if (amlplayer)
1805   {
1806     CSingleLock lock(amlplayer->m_aml_state_csection);
1807     if (amlplayer->m_aml_state.back() != info->status)
1808     {
1809       //CLog::Log(LOGDEBUG, "update_player_info: %s, old state %s", player_status2str(info->status), player_status2str(info->last_sta));
1810       amlplayer->m_aml_state.push_back(info->status);
1811     }
1812   }
1813   return 0;
1814 }
1815
1816 bool CAMLPlayer::CheckPlaying()
1817 {
1818   return ((player_status)GetPlayerSerializedState() == PLAYER_RUNNING);
1819 }
1820
1821 bool CAMLPlayer::WaitForStopped(int timeout_ms)
1822 {
1823   while (!m_bAbortRequest && (timeout_ms > 0))
1824   {
1825     player_status pstatus = (player_status)GetPlayerSerializedState();
1826     if (m_log_level > 5)
1827       CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForStopped: %s", m_dll->player_status2str(pstatus));
1828     switch(pstatus)
1829     {
1830       default:
1831         usleep(20 * 1000);
1832         timeout_ms -= 20;
1833         break;
1834       case PLAYER_PLAYEND:
1835       case PLAYER_STOPED:
1836       case PLAYER_ERROR:
1837       case PLAYER_EXIT:
1838         m_bAbortRequest = true;
1839         return true;
1840         break;
1841     }
1842   }
1843
1844   return false;
1845 }
1846
1847 bool CAMLPlayer::WaitForSearchOK(int timeout_ms)
1848 {
1849   while (!m_bAbortRequest && (timeout_ms > 0))
1850   {
1851     player_status pstatus = (player_status)GetPlayerSerializedState();
1852     if (m_log_level > 5)
1853       CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForSearchOK: %s", m_dll->player_status2str(pstatus));
1854     switch(pstatus)
1855     {
1856       default:
1857         usleep(20 * 1000);
1858         timeout_ms -= 20;
1859         break;
1860       case PLAYER_STOPED:
1861         return false;
1862       case PLAYER_ERROR:
1863       case PLAYER_EXIT:
1864         m_bAbortRequest = true;
1865         return false;
1866         break;
1867       case PLAYER_SEARCHOK:
1868         return true;
1869         break;
1870     }
1871   }
1872
1873   return false;
1874 }
1875
1876 bool CAMLPlayer::WaitForPlaying(int timeout_ms)
1877 {
1878   while (!m_bAbortRequest && (timeout_ms > 0))
1879   {
1880     // anoying that we have to hammer setting audio volume via mute
1881     // but we have to catch it before any audio comes out.
1882     // we cannot do this for m1 (playback will bork) so trap it out.
1883     if (aml_get_cputype() != 1)
1884       SetMute(m_audio_mute);
1885
1886     player_status pstatus = (player_status)GetPlayerSerializedState();
1887     if (m_log_level > 5)
1888       CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForPlaying: %s", m_dll->player_status2str(pstatus));
1889     switch(pstatus)
1890     {
1891       default:
1892         usleep(20 * 1000);
1893         timeout_ms -= 20;
1894         break;
1895       case PLAYER_ERROR:
1896       case PLAYER_EXIT:
1897         m_bAbortRequest = true;
1898         return false;
1899         break;
1900       case PLAYER_RUNNING:
1901         // restore mute/volume settings
1902         SetMute(m_audio_mute);
1903         return true;
1904         break;
1905     }
1906   }
1907
1908   return false;
1909 }
1910
1911 bool CAMLPlayer::WaitForFormatValid(int timeout_ms)
1912 {
1913   while (timeout_ms > 0)
1914   {
1915     player_status pstatus = (player_status)GetPlayerSerializedState();
1916     if (m_log_level > 5)
1917       CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForFormatValid: %s", m_dll->player_status2str(pstatus));
1918     switch(pstatus)
1919     {
1920       default:
1921         usleep(20 * 1000);
1922         timeout_ms -= 20;
1923         break;
1924       case PLAYER_ERROR:
1925       case PLAYER_EXIT:
1926         m_bAbortRequest = true;
1927         return false;
1928         break;
1929       case PLAYER_INITOK:
1930
1931         ClearStreamInfos();
1932
1933         media_info_t media_info = {{0}};
1934 #if defined(TARGET_ANDROID)
1935         // media_info_t might be different so check its size.
1936         // player_get_media_info will memset to zero the passed
1937         // structure so alloc more space and preset to a know value
1938         // so we can compare the size we use to the size the lib uses. 
1939         int msize = sizeof(media_info_t) + 10240;
1940         media_info_t *test_media_info = (media_info_t*)calloc(msize, 1);
1941         memset(test_media_info, 0xEF, msize);
1942
1943         int res = m_dll->player_get_media_info(m_pid, test_media_info);
1944
1945         uint8_t *t1 = (uint8_t*)test_media_info;
1946         for (size_t i = msize-1; i >= 0; i--)
1947         {
1948           if (t1[i] != 0xEF)
1949           {
1950             if (sizeof(media_info_t) != i+1)
1951             {
1952               CLog::Log(LOGERROR, "CAMLPlayer::media_info_t(%d) size changed to %d",
1953                 sizeof(media_info_t), i+1);
1954               // size is different, we cannot trust it
1955               free(test_media_info);
1956               return false;
1957             }
1958             break;
1959           }
1960         }
1961         media_info = *test_media_info;
1962         free(test_media_info);
1963 #else
1964         int res = m_dll->player_get_media_info(m_pid, &media_info);
1965 #endif
1966         if (res != PLAYER_SUCCESS)
1967           return false;
1968
1969         if (m_log_level > 5)
1970           media_info_dump(&media_info);
1971
1972         // video info
1973         if (media_info.stream_info.has_video && media_info.stream_info.total_video_num > 0)
1974         {
1975           for (int i = 0; i < media_info.stream_info.total_video_num &&
1976               media_info.stream_info.total_video_num < MAX_VIDEO_STREAMS; i++)
1977           {
1978             AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
1979             info->Clear();
1980
1981             info->id              = media_info.video_info[i]->id;
1982             info->type            = STREAM_VIDEO;
1983             info->width           = media_info.video_info[i]->width;
1984             info->height          = media_info.video_info[i]->height;
1985             info->aspect_ratio_num= media_info.video_info[i]->aspect_ratio_num;
1986             info->aspect_ratio_den= media_info.video_info[i]->aspect_ratio_den;
1987             info->frame_rate_num  = media_info.video_info[i]->frame_rate_num;
1988             info->frame_rate_den  = media_info.video_info[i]->frame_rate_den;
1989             info->bit_rate        = media_info.video_info[i]->bit_rate;
1990             info->duration        = media_info.video_info[i]->duartion;
1991             info->format          = media_info.video_info[i]->format;
1992
1993             m_video_streams.push_back(info);
1994           }
1995
1996           m_video_index = media_info.stream_info.cur_video_index;
1997           m_video_count = media_info.stream_info.total_video_num;
1998           if (m_video_index != 0)
1999             m_video_index = 0;
2000           m_video_width = media_info.video_info[m_video_index]->width;
2001           m_video_height= media_info.video_info[m_video_index]->height;
2002           m_video_fps_numerator = media_info.video_info[m_video_index]->frame_rate_num;
2003           m_video_fps_denominator = media_info.video_info[m_video_index]->frame_rate_den;
2004
2005           // bail if we do not get a valid width/height
2006           if (m_video_width == 0 || m_video_height == 0)
2007             return false;
2008         }
2009
2010         // audio info
2011         if (media_info.stream_info.has_audio && media_info.stream_info.total_audio_num > 0)
2012         {
2013           for (int i = 0; i < media_info.stream_info.total_audio_num &&
2014               media_info.stream_info.total_audio_num < MAX_AUDIO_STREAMS; i++)
2015           {
2016             AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
2017             info->Clear();
2018
2019             info->id              = media_info.audio_info[i]->id;
2020             info->type            = STREAM_AUDIO;
2021             info->channel         = media_info.audio_info[i]->channel;
2022             info->sample_rate     = media_info.audio_info[i]->sample_rate;
2023             info->bit_rate        = media_info.audio_info[i]->bit_rate;
2024             info->duration        = media_info.audio_info[i]->duration;
2025             info->format          = media_info.audio_info[i]->aformat;
2026 #if defined(HAS_AMLPLAYER_AUDIO_LANG)
2027             if (media_info.audio_info[i]->audio_language[0] != 0)
2028               info->language = std::string(media_info.audio_info[i]->audio_language, 3);
2029 #endif
2030
2031             m_audio_streams.push_back(info);
2032           }
2033
2034           m_audio_index = media_info.stream_info.cur_audio_index;
2035           if (m_audio_index != 0)
2036             m_audio_index = 0;
2037           m_audio_count = media_info.stream_info.total_audio_num;
2038           // setup ac3/dts passthough if required
2039           SetAudioPassThrough(m_audio_streams[m_audio_index]->format);
2040         }
2041
2042         // subtitle info
2043         if (media_info.stream_info.has_sub && media_info.stream_info.total_sub_num > 0)
2044         {
2045           for (int i = 0; i < media_info.stream_info.total_sub_num && i < MAX_SUB_STREAMS; i++)
2046           {
2047             AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
2048             info->Clear();
2049
2050             info->id   = media_info.sub_info[i]->id;
2051             info->type = STREAM_SUBTITLE;
2052             if (media_info.sub_info[i]->sub_language && media_info.sub_info[i]->sub_language[0] != 0)
2053               info->language = std::string(media_info.sub_info[i]->sub_language, 3);
2054             m_subtitle_streams.push_back(info);
2055           }
2056           m_subtitle_index = media_info.stream_info.cur_sub_index;
2057         }
2058         // find any external subs
2059         FindSubtitleFiles();
2060         // setup count and index
2061         m_subtitle_count = m_subtitle_streams.size();
2062         if (m_subtitle_count && m_subtitle_index != 0)
2063           m_subtitle_index = 0;
2064
2065 #if defined(HAS_AMLPLAYER_CHAPTERS)
2066         // chapter info
2067         if (media_info.stream_info.total_chapter_num > 0)
2068         {
2069           m_chapter_count = media_info.stream_info.total_chapter_num;
2070           for (int i = 0; i < m_chapter_count && m_chapter_count < MAX_CHAPTERS; i++)
2071           {
2072             if (media_info.chapter_info[i] != NULL)
2073             {
2074               AMLChapterInfo *info = new AMLChapterInfo;
2075
2076               info->name = media_info.chapter_info[i]->name;
2077               info->seekto_ms = media_info.chapter_info[i]->seekto_ms;
2078               m_chapters.push_back(info);
2079             }
2080           }
2081         }
2082 #endif
2083
2084         return true;
2085         break;
2086     }
2087   }
2088
2089   return false;
2090 }
2091
2092 void CAMLPlayer::ClearStreamInfos()
2093 {
2094   CSingleLock lock(m_aml_csection);
2095
2096   if (!m_audio_streams.empty())
2097   {
2098     for (unsigned int i = 0; i < m_audio_streams.size(); i++)
2099       delete m_audio_streams[i];
2100     m_audio_streams.clear();
2101   }
2102   m_audio_count = 0;
2103   m_audio_index = -1;
2104
2105   if (!m_video_streams.empty())
2106   {
2107     for (unsigned int i = 0; i < m_video_streams.size(); i++)
2108       delete m_video_streams[i];
2109     m_video_streams.clear();
2110   }
2111   m_video_count = 0;
2112   m_video_index = -1;
2113
2114   if (!m_subtitle_streams.empty())
2115   {
2116     for (unsigned int i = 0; i < m_subtitle_streams.size(); i++)
2117       delete m_subtitle_streams[i];
2118     m_subtitle_streams.clear();
2119   }
2120   m_subtitle_count = 0;
2121   m_subtitle_index = -1;
2122
2123   if (!m_chapters.empty())
2124   {
2125     for (unsigned int i = 0; i < m_chapters.size(); i++)
2126       delete m_chapters[i];
2127     m_chapters.clear();
2128   }
2129   m_chapter_count = 0;
2130 }
2131
2132 bool CAMLPlayer::GetStatus()
2133 {
2134   CSingleLock lock(m_aml_csection);
2135
2136   if (!m_dll->check_pid_valid(m_pid))
2137     return false;
2138
2139   player_info_t player_info;
2140   int res = m_dll->player_get_play_info(m_pid, &player_info);
2141   if (res != PLAYER_SUCCESS)
2142     return false;
2143
2144   m_elapsed_ms  = player_info.current_ms;
2145   m_duration_ms = 1000 * player_info.full_time;
2146   //CLog::Log(LOGDEBUG, "CAMLPlayer::GetStatus: audio_bufferlevel(%f), video_bufferlevel(%f), bufed_time(%d), bufed_pos(%lld)",
2147   //  player_info.audio_bufferlevel, player_info.video_bufferlevel, player_info.bufed_time, player_info.bufed_pos);
2148
2149   return true;
2150 }
2151
2152 void CAMLPlayer::FindSubtitleFiles()
2153 {
2154   // find any available external subtitles
2155   std::vector<CStdString> filenames;
2156   CUtil::ScanForExternalSubtitles(m_item.GetPath(), filenames);
2157
2158   // find any upnp subtitles
2159   CStdString key("upnp:subtitle:1");
2160   for(unsigned s = 1; m_item.HasProperty(key); key.Format("upnp:subtitle:%u", ++s))
2161     filenames.push_back(m_item.GetProperty(key).asString());
2162
2163   for(unsigned int i=0;i<filenames.size();i++)
2164   {
2165     // if vobsub subtitle:              
2166     if (URIUtils::HasExtension(filenames[i], ".idx"))
2167     {
2168       CStdString strSubFile;
2169       if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
2170         AddSubtitleFile(filenames[i], strSubFile);
2171     }
2172     else 
2173     {
2174       if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
2175       {
2176         AddSubtitleFile(filenames[i]);
2177       }
2178     }   
2179   }
2180 }
2181
2182 int CAMLPlayer::AddSubtitleFile(const std::string &filename, const std::string &subfilename)
2183 {
2184   std::string ext = URIUtils::GetExtension(filename);
2185   std::string vobsubfile = subfilename;
2186
2187   if(ext == ".idx")
2188   {
2189     /* TODO: we do not handle idx/sub binary subs yet.
2190     if (vobsubfile.empty())
2191       vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
2192
2193     CDVDDemuxVobsub v;
2194     if(!v.Open(filename, vobsubfile))
2195       return -1;
2196     m_SelectionStreams.Update(NULL, &v);
2197     int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename), 0);
2198     m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags;
2199     m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename2 = vobsubfile;
2200     return index;
2201     */
2202     return -1;
2203   }
2204   if(ext == ".sub")
2205   {
2206     // check for texual sub, if this is a idx/sub pair, ignore it.
2207     CStdString strReplace(URIUtils::ReplaceExtension(filename,".idx"));
2208     if (XFILE::CFile::Exists(strReplace))
2209       return -1;
2210   }
2211
2212   AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
2213   info->Clear();
2214
2215   info->id       = 0;
2216   info->type     = STREAM_SUBTITLE;
2217   info->source   = STREAM_SOURCE_TEXT;
2218   info->filename = filename;
2219   info->name     = URIUtils::GetFileName(filename);
2220   info->frame_rate_num = m_video_fps_numerator;
2221   info->frame_rate_den = m_video_fps_denominator;
2222   m_subtitle_streams.push_back(info);
2223
2224   return m_subtitle_streams.size();
2225 }
2226
2227 bool CAMLPlayer::OpenSubtitleStream(int index)
2228 {
2229   CLog::Log(LOGNOTICE, "Opening external subtitle stream: %i", index);
2230
2231   CDemuxStream* pStream = NULL;
2232   std::string filename;
2233   CDVDStreamInfo hint;
2234
2235   if (m_subtitle_streams[index]->source == STREAM_SOURCE_DEMUX_SUB)
2236   {
2237     /*
2238     int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2239     if(index < 0)
2240       return false;
2241     SelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
2242
2243     if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename)
2244     {
2245       CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str());
2246       auto_ptr<CDVDDemuxVobsub> demux(new CDVDDemuxVobsub());
2247       if(!demux->Open(st.filename, st.filename2))
2248         return false;
2249       m_pSubtitleDemuxer = demux.release();
2250     }
2251
2252     pStream = m_pSubtitleDemuxer->GetStream(iStream);
2253     if(!pStream || pStream->disabled)
2254       return false;
2255     pStream->SetDiscard(AVDISCARD_NONE);
2256     double pts = m_dvdPlayerVideo.GetCurrentPts();
2257     if(pts == DVD_NOPTS_VALUE)
2258       pts = m_CurrentVideo.dts;
2259     if(pts == DVD_NOPTS_VALUE)
2260       pts = 0;
2261     pts += m_offset_pts;
2262     m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE));
2263
2264     hint.Assign(*pStream, true);
2265     */
2266     return false;
2267   }
2268   else if (m_subtitle_streams[index]->source == STREAM_SOURCE_TEXT)
2269   {
2270     filename = m_subtitle_streams[index]->filename;
2271
2272     hint.Clear();
2273     hint.fpsscale = m_subtitle_streams[index]->frame_rate_den;
2274     hint.fpsrate  = m_subtitle_streams[index]->frame_rate_num;
2275   }
2276
2277   m_dvdPlayerSubtitle->CloseStream(true);
2278   if (!m_dvdPlayerSubtitle->OpenStream(hint, filename))
2279   {
2280     CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, index);
2281     if(pStream)
2282     {
2283       pStream->disabled = true;
2284       pStream->SetDiscard(AVDISCARD_ALL);
2285     }
2286     return false;
2287   }
2288
2289   return true;
2290 }
2291
2292 void CAMLPlayer::SetVideoRect(const CRect &SrcRect, const CRect &DestRect)
2293 {
2294   // this routine gets called every video frame
2295   // and is in the context of the renderer thread so
2296   // do not do anything stupid here.
2297
2298   // video zoom adjustment.
2299   float zoom = CMediaSettings::Get().GetCurrentVideoSettings().m_CustomZoomAmount;
2300   if ((int)(zoom * 1000) != (int)(m_zoom * 1000))
2301   {
2302     m_zoom = zoom;
2303   }
2304   // video contrast adjustment.
2305   int contrast = CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast;
2306   if (contrast != m_contrast)
2307   {
2308     SetVideoContrast(contrast);
2309     m_contrast = contrast;
2310   }
2311   // video brightness adjustment.
2312   int brightness = CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness;
2313   if (brightness != m_brightness)
2314   {
2315     SetVideoBrightness(brightness);
2316     m_brightness = brightness;
2317   }
2318
2319   // check if destination rect or video view mode has changed
2320   if ((m_dst_rect != DestRect) || (m_view_mode != CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode))
2321   {
2322     m_dst_rect  = DestRect;
2323     m_view_mode = CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode;
2324   }
2325   else
2326   {
2327     // mainvideo 'should' be showing already if we get here, make sure.
2328     ShowMainVideo(true);
2329     return;
2330   }
2331
2332   CRect gui, display, dst_rect;
2333   gui = g_graphicsContext.GetViewWindow();
2334   // when display is at 1080p, we have freescale enabled
2335   // and that scales all layers into 1080p display including video,
2336   // so we have to setup video axis for 720p instead of 1080p... Boooo.
2337   display = g_graphicsContext.GetViewWindow();
2338   //RESOLUTION res = g_graphicsContext.GetVideoResolution();
2339   //display.SetRect(0, 0, CDisplaySettings::Get().GetResolutionInfo(res).iScreenWidth, CDisplaySettings::Get().GetResolutionInfo(res).iScreenHeight);
2340   dst_rect = m_dst_rect;
2341   if (gui != display)
2342   {
2343     float xscale = display.Width()  / gui.Width();
2344     float yscale = display.Height() / gui.Height();
2345     dst_rect.x1 *= xscale;
2346     dst_rect.x2 *= xscale;
2347     dst_rect.y1 *= yscale;
2348     dst_rect.y2 *= yscale;
2349   }
2350
2351   ShowMainVideo(false);
2352
2353   // goofy 0/1 based difference in aml axis coordinates.
2354   // fix them.
2355   dst_rect.x2--;
2356   dst_rect.y2--;
2357
2358   char video_axis[256] = {0};
2359   sprintf(video_axis, "%d %d %d %d", (int)dst_rect.x1, (int)dst_rect.y1, (int)dst_rect.x2, (int)dst_rect.y2);
2360   aml_set_sysfs_str("/sys/class/video/axis", video_axis);
2361   // make sure we are in 'full stretch' so we can stretch
2362   aml_set_sysfs_int("/sys/class/video/screen_mode", 1);
2363 /*
2364   CStdString rectangle;
2365   rectangle.Format("%i,%i,%i,%i",
2366     (int)dst_rect.x1, (int)dst_rect.y1,
2367     (int)dst_rect.Width(), (int)dst_rect.Height());
2368   CLog::Log(LOGDEBUG, "CAMLPlayer::SetVideoRect:dst_rect(%s)", rectangle.c_str());
2369 */
2370   // we only get called once gui has changed to something
2371   // that would show video playback, so show it.
2372   ShowMainVideo(true);
2373 }
2374
2375 void CAMLPlayer::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect)
2376 {
2377   CAMLPlayer *player = (CAMLPlayer*)ctx;
2378   player->SetVideoRect(SrcRect, DestRect);
2379 }
2380