2 * Copyright (C) 2011-2013 Team XBMC
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)
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.
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/>.
23 #include "AMLPlayer.h"
24 #include "Application.h"
26 #include "FileURLProtocol.h"
27 #include "GUIInfoManager.h"
28 #include "video/VideoThumbLoader.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"
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"
59 #include "utils/AMLUtils.h"
60 #include "DllLibamplayer.h"
62 static float VolumePercentToScale(float volume)
64 float audio_volume = 0.0;
65 if (volume > VOLUME_MINIMUM)
67 float dB = CAEUtil::PercentToGain(volume);
68 audio_volume = CAEUtil::GainToScale(dB);
70 if (audio_volume >= 0.99f)
82 struct AMLPlayerStreamInfo
99 source = STREAM_SOURCE_NONE;
110 int aspect_ratio_num;
111 int aspect_ratio_den;
119 std::string language;
121 std::string filename;
122 std::string filename2; // for vobsub subtitles, 2 files are necessary (idx/sub)
125 ////////////////////////////////////////////////////////////////////////////////////////////
126 static int media_info_dump(media_info_t* minfo)
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)
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);
149 if (minfo->stream_info.has_audio && minfo->stream_info.total_audio_num > 0)
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++)
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)
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);
176 if (minfo->stream_info.has_sub && minfo->stream_info.total_sub_num > 0)
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++)
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, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
194 CLog::Log(LOGDEBUG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
198 static const char* VideoCodecName(int vformat)
200 const char *format = "";
231 case VFORMAT_H264MVC:
241 static const char* AudioCodecName(int aformat)
243 const char *format = "";
250 case AFORMAT_PCM_S16LE:
268 case AFORMAT_PCM_S16BE:
295 case AFORMAT_PCM_BLURAY:
304 case AFORMAT_AAC_LATM:
318 ////////////////////////////////////////////////////////////////////////////////////////////
319 CAMLSubTitleThread::CAMLSubTitleThread(DllLibAmplayer *dll) :
320 CThread("AMLSubTitle"),
326 CAMLSubTitleThread::~CAMLSubTitleThread()
331 void CAMLSubTitleThread::Flush()
333 CSingleLock lock(m_subtitle_csection);
334 m_subtitle_strings.clear();
337 void CAMLSubTitleThread::UpdateSubtitle(CStdString &subtitle, int64_t elapsed_ms)
339 CSingleLock lock(m_subtitle_csection);
340 if (!m_subtitle_strings.empty())
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())
348 if (elapsed_ms > amlsubtitle->endtime)
349 it = m_subtitle_strings.erase(it);
354 // find the current subtitle
355 it = m_subtitle_strings.begin();
356 while (it != m_subtitle_strings.end())
359 if (elapsed_ms > amlsubtitle->bgntime && elapsed_ms < amlsubtitle->endtime)
361 subtitle = amlsubtitle->string;
369 void CAMLSubTitleThread::Process(void)
371 CLog::Log(LOGDEBUG, "CAMLSubTitleThread::Process begin");
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");
379 if (m_subtitle_codec > 0)
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);
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);
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))
397 // 20 byte header, then subtitle string
400 // csection lock it now as we are diddling shared vars
401 CSingleLock lock(m_subtitle_csection);
403 AMLSubtitle *subtitle = new AMLSubtitle;
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];
409 /* TODO: handle other subtitle codec types
411 AV_CODEC_ID_DVD_SUBTITLE= 0x17000,
412 AV_CODEC_ID_DVB_SUBTITLE,
413 AV_CODEC_ID_TEXT, ///< raw UTF-8 text
416 AV_CODEC_ID_MOV_TEXT,
417 AV_CODEC_ID_HDMV_PGS_SUBTITLE,
418 AV_CODEC_ID_DVB_TELETEXT,
420 AV_CODEC_ID_MICRODVD,
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]);
429 case AV_CODEC_ID_TEXT:
430 subtitle->bgntime = sub_pts/ 90;
431 subtitle->endtime = subtitle->bgntime + 4000;
432 subtitle->string = &sub_buffer[20];
434 case AV_CODEC_ID_SSA:
435 if (strncmp((const char*)&sub_buffer[20], "Dialogue:", 9) == 0)
437 int vars_found, hour1, min1, sec1, hunsec1, hour2, min2, sec2, hunsec2, nothing;
438 char line3[sub_size];
439 char *line = &sub_buffer[20];
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 ¬hing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line3);
445 vars_found = sscanf(line, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\n\r]",
446 ¬hing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line3);
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++)
454 tmp = strchr(line2 + 1, ',');
459 // a space after a comma means we are already in a sentence
462 // eliminate the trailing comma
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)
476 size_t beg = subtitle->string.find("{\\");
477 if (beg != std::string::npos)
479 size_t end = subtitle->string.find("}", beg);
480 if (end != std::string::npos)
481 subtitle->string.erase(beg, end-beg+1);
490 if (subtitle->string.length())
493 subtitle->string.Replace("'","\'");
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++)
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;
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;
522 CLog::Log(LOGDEBUG, "CAMLSubTitleThread::Process end");
524 ////////////////////////////////////////////////////////////////////////////////////////////
525 CAMLPlayer::CAMLPlayer(IPlayerCallback &callback)
527 CThread ("AMLPlayer" ),
531 m_bAbortRequest (false ),
536 m_audio_passthrough_ac3 (false ),
537 m_audio_passthrough_dts (false ),
538 m_audio_mute (false ),
539 m_audio_volume (0.0f ),
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 ),
558 m_dll = new DllLibAmplayer;
567 // for external subtitles
568 m_dvdOverlayContainer = new CDVDOverlayContainer;
569 m_dvdPlayerSubtitle = new CDVDPlayerSubtitle(m_dvdOverlayContainer);
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())
575 CLog::Log(LOGNOTICE,"%s: Failed to suspend AudioEngine before launching external player", __FUNCTION__);
579 CAMLPlayer::~CAMLPlayer()
583 delete m_dvdPlayerSubtitle;
584 delete m_dvdOverlayContainer;
585 delete m_dll, m_dll = NULL;
587 // Resume AE processing of XBMC native audio
588 if (!CAEFactory::Resume())
590 CLog::Log(LOGFATAL, "%s: Failed to restart AudioEngine after return from external player",__FUNCTION__);
594 bool CAMLPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options)
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.
604 m_bAbortRequest = false;
613 m_audio_info = "none";
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");
620 m_video_info = "none";
623 m_video_fps_numerator = 25;
624 m_video_fps_denominator = 1;
626 m_subtitle_delay = 0;
627 m_subtitle_thread = NULL;
631 m_show_mainvideo = -1;
632 m_dst_rect.SetRect(0, 0, 0, 0);
639 if (m_item.IsDVDFile() || m_item.IsDVD())
642 // setup to spin the busy dialog until we are playing
645 g_renderManager.PreInit();
647 // create the playing thread
649 if (!m_ready.WaitMSec(100))
651 CGUIDialogBusy *dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY);
653 while (!m_ready.WaitMSec(1))
654 g_windowManager.ProcessRenderLoop(false);
658 // Playback might have been stopped due to some error.
659 if (m_bStop || m_bAbortRequest)
666 CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__);
671 bool CAMLPlayer::CloseFile()
673 CLog::Log(LOGDEBUG, "CAMLPlayer::CloseFile");
675 // set the abort request so that other threads can finish up
676 m_bAbortRequest = true;
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
684 CLog::Log(LOGDEBUG, "CAMLPlayer: finished waiting");
686 g_renderManager.UnInit();
691 bool CAMLPlayer::IsPlaying() const
696 void CAMLPlayer::Pause()
698 CLog::Log(LOGDEBUG, "CAMLPlayer::Pause");
699 CSingleLock lock(m_aml_csection);
701 if ((m_pid < 0) && m_bAbortRequest)
705 m_dll->player_resume(m_pid);
707 m_dll->player_pause(m_pid);
709 m_paused = !m_paused;
712 bool CAMLPlayer::IsPaused() const
717 bool CAMLPlayer::HasVideo() const
719 return m_video_count > 0;
722 bool CAMLPlayer::HasAudio() const
724 return m_audio_count > 0;
727 void CAMLPlayer::ToggleFrameDrop()
729 CLog::Log(LOGDEBUG, "CAMLPlayer::ToggleFrameDrop");
732 bool CAMLPlayer::CanSeek()
734 return GetTotalTime() > 0;
737 void CAMLPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
739 // force updated to m_elapsed_ms, m_duration_ms.
742 CSingleLock lock(m_aml_csection);
744 // try chapter seeking first, chapter_index is ones based.
745 int chapter_index = GetChapter();
746 if (bLargeStep && bChapterOverride && chapter_index > 0)
750 // seek to previous chapter
751 SeekChapter(chapter_index - 1);
754 else if (chapter_index < GetChapterCount())
756 // seek to next chapter
757 SeekChapter(chapter_index + 1);
763 if (g_advancedSettings.m_videoUseTimeSeeking)
765 if (bLargeStep && (GetTotalTime() > (2000 * g_advancedSettings.m_videoTimeSeekForwardBig)))
766 seek_ms = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig;
768 seek_ms = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward;
769 // convert to milliseconds
771 seek_ms += m_elapsed_ms;
777 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig;
779 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward;
781 percent += (float)m_elapsed_ms/(float)m_duration_ms;
782 // convert to milliseconds
783 seek_ms = m_duration_ms * percent;
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))
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
800 if (seek_ms > m_duration_ms)
801 seek_ms = m_duration_ms;
804 g_infoManager.SetDisplayAfterSeek(100000);
806 m_callback.OnPlayBackSeek((int)seek_ms, (int)(seek_ms - m_elapsed_ms));
807 g_infoManager.SetDisplayAfterSeek();
810 bool CAMLPlayer::SeekScene(bool bPlus)
812 CLog::Log(LOGDEBUG, "CAMLPlayer::SeekScene");
816 void CAMLPlayer::SeekPercentage(float fPercent)
818 CSingleLock lock(m_aml_csection);
820 // force updated to m_elapsed_ms, m_duration_ms.
825 int64_t seek_ms = fPercent * m_duration_ms / 100.0;
830 g_infoManager.SetDisplayAfterSeek(100000);
832 m_callback.OnPlayBackSeek((int)seek_ms, (int)(seek_ms - m_elapsed_ms));
833 g_infoManager.SetDisplayAfterSeek();
837 float CAMLPlayer::GetPercentage()
841 return 100.0f * (float)m_elapsed_ms/(float)m_duration_ms;
846 void CAMLPlayer::SetMute(bool bOnOff)
848 m_audio_mute = bOnOff;
850 #if defined(HAS_AMLPLAYER_AUDIO_SETVOLUME)
851 CSingleLock lock(m_aml_csection);
852 if (m_dll->check_pid_valid(m_pid))
855 m_dll->audio_set_volume(m_pid, 0.0);
857 m_dll->audio_set_volume(m_pid, m_audio_volume);
862 void CAMLPlayer::SetVolume(float volume)
864 // volume is a float percent from 0.0 to 1.0
865 m_audio_volume = VolumePercentToScale(volume);
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);
874 void CAMLPlayer::GetAudioInfo(CStdString &strAudioInfo)
876 CSingleLock lock(m_aml_csection);
877 if (m_audio_streams.empty() || m_audio_index > (int)(m_audio_streams.size() - 1))
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);
885 void CAMLPlayer::GetVideoInfo(CStdString &strVideoInfo)
887 CSingleLock lock(m_aml_csection);
888 if (m_video_streams.empty() || m_video_index > (int)(m_video_streams.size() - 1))
891 strVideoInfo.Format("Video stream (%s) [fr:%.3f Mb/s:%.2f]",
892 VideoCodecName(m_video_streams[m_video_index]->format),
894 (double)m_video_streams[m_video_index]->bit_rate / (1024.0*1024.0));
897 int CAMLPlayer::GetAudioStreamCount()
899 //CLog::Log(LOGDEBUG, "CAMLPlayer::GetAudioStreamCount");
900 return m_audio_count;
903 int CAMLPlayer::GetAudioStream()
905 //CLog::Log(LOGDEBUG, "CAMLPlayer::GetAudioStream");
906 return m_audio_index;
909 void CAMLPlayer::SetAudioStream(int SetAudioStream)
911 //CLog::Log(LOGDEBUG, "CAMLPlayer::SetAudioStream");
912 CSingleLock lock(m_aml_csection);
914 if (SetAudioStream > (int)m_audio_streams.size() || SetAudioStream < 0)
917 m_audio_index = SetAudioStream;
918 SetAudioPassThrough(m_audio_streams[m_audio_index]->format);
920 if (m_dll->check_pid_valid(m_pid))
922 m_dll->player_aid(m_pid, m_audio_streams[m_audio_index]->id);
926 void CAMLPlayer::SetAVDelay(float fValue)
928 CLog::Log(LOGDEBUG, "CAMLPlayer::SetAVDelay (%f)", fValue);
929 m_audio_delay = fValue * 1000.0;
931 #if defined(HAS_AMLPLAYER_AUDIO_SETDELAY)
932 if (m_audio_streams.size() && m_dll->check_pid_valid(m_pid))
934 CSingleLock lock(m_aml_csection);
935 m_dll->audio_set_delay(m_pid, m_audio_delay);
940 float CAMLPlayer::GetAVDelay()
942 return (float)m_audio_delay / 1000.0;
945 void CAMLPlayer::SetSubTitleDelay(float fValue = 0.0f)
947 if (GetSubtitleCount())
949 CSingleLock lock(m_aml_csection);
950 m_subtitle_delay = fValue * 1000.0;
954 float CAMLPlayer::GetSubTitleDelay()
956 return (float)m_subtitle_delay / 1000.0;
959 int CAMLPlayer::GetSubtitleCount()
961 return m_subtitle_count;
964 int CAMLPlayer::GetSubtitle()
967 return m_subtitle_index;
972 void CAMLPlayer::GetSubtitleStreamInfo(int index, SPlayerSubtitleStreamInfo &info)
974 CSingleLock lock(m_aml_csection);
976 if (index > (int)m_subtitle_streams.size() -1 || index < 0)
979 info.language = m_subtitle_streams[index]->language;
980 info.name = m_subtitle_streams[m_subtitle_index]->name;
983 CLog::Log(LOGDEBUG, "CAMLPlayer::GetSubtitleName, iStream(%d)", index);
986 void CAMLPlayer::SetSubtitle(int iStream)
988 CSingleLock lock(m_aml_csection);
990 if (iStream > (int)m_subtitle_streams.size() || iStream < 0)
993 m_subtitle_index = iStream;
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)
1001 if (m_dll->check_pid_valid(m_pid) && m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE)
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();
1010 m_dvdPlayerSubtitle->CloseStream(true);
1011 OpenSubtitleStream(m_subtitle_index);
1015 bool CAMLPlayer::GetSubtitleVisible()
1017 return m_subtitle_show;
1020 void CAMLPlayer::SetSubtitleVisible(bool bVisible)
1022 m_subtitle_show = (bVisible && m_subtitle_count);
1023 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn = bVisible;
1025 if (m_subtitle_show && m_subtitle_count)
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)
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();
1041 OpenSubtitleStream(m_subtitle_index);
1045 int CAMLPlayer::AddSubtitle(const CStdString& strSubPath)
1047 CSingleLock lock(m_aml_csection);
1049 return AddSubtitleFile(strSubPath);
1052 void CAMLPlayer::GetVideoRect(CRect& SrcRect, CRect& DestRect)
1054 g_renderManager.GetVideoRect(SrcRect, DestRect);
1057 void CAMLPlayer::GetVideoAspectRatio(float &fAR)
1059 fAR = g_renderManager.GetAspectRatio();
1062 int CAMLPlayer::GetChapterCount()
1064 return m_chapter_count;
1067 int CAMLPlayer::GetChapter()
1069 // returns a one based value or zero if no chapters
1072 int chapter_index = -1;
1073 for (int i = 0; i < m_chapter_count; i++)
1075 if (m_elapsed_ms >= m_chapters[i]->seekto_ms)
1078 return chapter_index + 1;
1081 void CAMLPlayer::GetChapterName(CStdString& strChapterName)
1083 if (m_chapter_count)
1084 strChapterName = m_chapters[GetChapter() - 1]->name;
1087 int CAMLPlayer::SeekChapter(int chapter_index)
1089 CSingleLock lock(m_aml_csection);
1091 // chapter_index is a one based value.
1092 if (m_chapter_count > 1)
1094 if (chapter_index < 1)
1096 if (chapter_index > m_chapter_count)
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;
1103 // seek to 1 second and play is immediate.
1107 // seek to chapter here
1108 g_infoManager.SetDisplayAfterSeek(100000);
1110 m_callback.OnPlayBackSeekChapter(chapter_index);
1111 g_infoManager.SetDisplayAfterSeek();
1115 // we do not have a chapter list so do a regular big jump.
1116 if (chapter_index > 0)
1124 float CAMLPlayer::GetActualFPS()
1126 float video_fps = m_video_fps_numerator / m_video_fps_denominator;
1127 CLog::Log(LOGDEBUG, "CAMLPlayer::GetActualFPS:m_video_fps(%f)", video_fps);
1131 void CAMLPlayer::SeekTime(int64_t seek_ms)
1133 CSingleLock lock(m_aml_csection);
1135 // we cannot seek if paused
1143 if (m_dll->check_pid_valid(m_pid))
1145 if (!CheckPlaying())
1147 // player_timesearch is seconds (float).
1148 m_dll->player_timesearch(m_pid, (float)seek_ms/1000.0);
1149 WaitForSearchOK(5000);
1150 WaitForPlaying(5000);
1154 int64_t CAMLPlayer::GetTime()
1156 return m_elapsed_ms;
1159 int64_t CAMLPlayer::GetTotalTime()
1161 return m_duration_ms;
1164 void CAMLPlayer::GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info)
1166 CSingleLock lock(m_aml_csection);
1167 if (index < 0 || m_audio_streams.empty() || index > (int)(m_audio_streams.size() - 1))
1170 info.bitrate = m_audio_streams[index]->bit_rate;
1172 info.language = m_audio_streams[index]->language;
1174 info.channels = m_audio_streams[index]->channel;
1176 info.audioCodecName = AudioCodecName(m_audio_streams[index]->format);
1178 if (info.audioCodecName.size())
1179 info.name = info.audioCodecName + " ";
1181 switch(info.channels)
1184 info.name += "Mono";
1187 info.name += "Stereo";
1200 sprintf(temp, "%d-chs", info.channels);
1205 void CAMLPlayer::GetVideoStreamInfo(SPlayerVideoStreamInfo &info)
1207 CSingleLock lock(m_aml_csection);
1208 if (m_video_streams.empty() || m_video_index > (int)(m_video_streams.size() - 1))
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);
1217 int CAMLPlayer::GetSourceBitrate()
1219 CLog::Log(LOGDEBUG, "CAMLPlayer::GetSourceBitrate");
1223 int CAMLPlayer::GetBitsPerSample()
1225 CLog::Log(LOGDEBUG, "CAMLPlayer::GetBitsPerSample");
1229 int CAMLPlayer::GetSampleRate()
1231 CSingleLock lock(m_aml_csection);
1232 if (m_audio_streams.empty() || m_audio_index > (int)(m_audio_streams.size() - 1))
1235 return m_audio_streams[m_audio_index]->sample_rate;
1238 int CAMLPlayer::GetPictureWidth()
1240 //CLog::Log(LOGDEBUG, "CAMLPlayer::GetPictureWidth(%d)", m_video_width);
1241 return m_video_width;
1244 int CAMLPlayer::GetPictureHeight()
1246 //CLog::Log(LOGDEBUG, "CAMLPlayer::GetPictureHeight(%)", m_video_height);
1247 return m_video_height;
1250 bool CAMLPlayer::GetStreamDetails(CStreamDetails &details)
1252 //CLog::Log(LOGDEBUG, "CAMLPlayer::GetStreamDetails");
1256 void CAMLPlayer::ToFFRW(int iSpeed)
1258 CLog::Log(LOGDEBUG, "CAMLPlayer::ToFFRW: iSpeed(%d), m_speed(%d)", iSpeed, m_speed);
1259 CSingleLock lock(m_aml_csection);
1261 if (!m_dll->check_pid_valid(m_pid) && m_bAbortRequest)
1264 if (m_speed != iSpeed)
1266 // recover power of two value
1268 int ispeed = abs(iSpeed);
1269 while (ispeed >>= 1) ipower++;
1275 m_dll->player_forward(m_pid, 0);
1278 // N x fast forward/rewind (I-frames)
1279 // speed playback 1,2,4,8
1281 m_dll->player_forward(m_pid, iSpeed);
1283 m_dll->player_backward(m_pid, -iSpeed);
1291 bool CAMLPlayer::GetCurrentSubtitle(CStdString& strSubtitle)
1295 if (m_subtitle_count)
1297 // force updated to m_elapsed_ms.
1299 if (m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE && m_subtitle_thread)
1301 m_subtitle_thread->UpdateSubtitle(strSubtitle, m_elapsed_ms - m_subtitle_delay);
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);
1311 return !strSubtitle.IsEmpty();
1314 void CAMLPlayer::OnStartup()
1316 //m_CurrentVideo.Clear();
1317 //m_CurrentAudio.Clear();
1318 //m_CurrentSubtitle.Clear();
1320 //CThread::SetName("AMLPlayer");
1323 void CAMLPlayer::OnExit()
1325 //CLog::Log(LOGNOTICE, "CAMLPlayer::OnExit()");
1328 // if we didn't stop playing, advance to the next item in xbmc's playlist
1329 if (m_options.identify == false)
1331 if (m_bAbortRequest)
1332 m_callback.OnPlayBackStopped();
1334 m_callback.OnPlayBackEnded();
1336 // set event to inform openfile something went wrong
1337 // in case openfile is still waiting for this event
1341 void CAMLPlayer::Process()
1343 CLog::Log(LOGNOTICE, "CAMLPlayer::Process");
1346 static AML_URLProtocol vfs_protocol = {
1348 CFileURLProtocol::Open,
1349 CFileURLProtocol::Read,
1350 CFileURLProtocol::Write,
1351 CFileURLProtocol::Seek,
1352 CFileURLProtocol::SeekEx,
1353 CFileURLProtocol::Close,
1356 CStdString url = m_item.GetPath();
1357 if (url.Left(strlen("smb://")).Equals("smb://"))
1359 // the name string needs to persist
1360 static const char *smb_name = "smb";
1361 vfs_protocol.name = smb_name;
1363 else if (url.Left(strlen("afp://")).Equals("afp://"))
1365 // the name string needs to persist
1366 static const char *afp_name = "afp";
1367 vfs_protocol.name = afp_name;
1369 else if (url.Left(strlen("nfs://")).Equals("nfs://"))
1371 // the name string needs to persist
1372 static const char *nfs_name = "nfs";
1373 vfs_protocol.name = nfs_name;
1375 else if (url.Left(strlen("rar://")).Equals("rar://"))
1377 // the name string needs to persist
1378 static const char *rar_name = "rar";
1379 vfs_protocol.name = rar_name;
1381 else if (url.Left(strlen("ftp://")).Equals("ftp://"))
1383 // the name string needs to persist
1384 static const char *http_name = "xb-ftp";
1385 vfs_protocol.name = http_name;
1388 else if (url.Left(strlen("ftps://")).Equals("ftps://"))
1390 // the name string needs to persist
1391 static const char *http_name = "xb-ftps";
1392 vfs_protocol.name = http_name;
1395 else if (url.Left(strlen("http://")).Equals("http://"))
1397 // the name string needs to persist
1398 static const char *http_name = "xb-http";
1399 vfs_protocol.name = http_name;
1402 else if (url.Left(strlen("https://")).Equals("https://"))
1404 // the name string needs to persist
1405 static const char *http_name = "xb-https";
1406 vfs_protocol.name = http_name;
1409 else if (url.Left(strlen("hdhomerun://")).Equals("hdhomerun://"))
1411 // the name string needs to persist
1412 static const char *http_name = "xb-hdhomerun";
1413 vfs_protocol.name = http_name;
1416 else if (url.Left(strlen("sftp://")).Equals("sftp://"))
1418 // the name string needs to persist
1419 static const char *http_name = "xb-sftp";
1420 vfs_protocol.name = http_name;
1423 else if (url.Left(strlen("udp://")).Equals("udp://"))
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");
1440 if (udp_params.size() > 0)
1441 url.append(udp_params);
1443 CLog::Log(LOGDEBUG, "CAMLPlayer::Process: URL=%s", url.c_str());
1445 if (m_dll->player_init() != PLAYER_SUCCESS)
1447 CLog::Log(LOGDEBUG, "player init failed");
1448 throw "CAMLPlayer::Process:player init failed";
1450 CLog::Log(LOGDEBUG, "player init......");
1453 // must be after player_init
1454 m_dll->av_register_protocol2(&vfs_protocol, sizeof(vfs_protocol));
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;
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
1477 // tweak player playback buffers for udp
1478 if (url.Left(strlen("udp://")).Equals("udp://"))
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 =;
1489 m_aml_state.clear();
1490 m_aml_state.push_back(0);
1491 m_pid = m_dll->player_start(&play_control, 0);
1494 if (m_log_level > 5)
1495 CLog::Log(LOGDEBUG, "player start failed! error = %d", m_pid);
1496 throw "CAMLPlayer::Process:player start failed";
1499 // wait for media to open with 30 second timeout.
1500 if (WaitForFormatValid(30000))
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";
1509 throw "CAMLPlayer::Process:WaitForFormatValid timeout";
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);
1518 // wait for playback to start with 20 second timeout
1519 if (WaitForPlaying(20000))
1522 m_callback.OnPlayBackSpeedChanged(m_speed);
1524 // get our initial status.
1527 // the default staturation is too high, drop it
1528 SetVideoSaturation(110);
1530 // drop CGUIDialogBusy dialog and release the hold in OpenFile.
1533 // we are playing but hidden and all stream fields are valid.
1534 // check for video in media content
1535 if (GetVideoStreamCount() > 0)
1537 SetAVDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_AudioDelay);
1540 SetSubtitleVisible(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn);
1541 SetSubTitleDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleDelay);
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;
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))
1558 CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__);
1560 if (!g_renderManager.IsStarted())
1562 CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__);
1565 g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack);
1567 m_subtitle_thread = new CAMLSubTitleThread(m_dll);
1568 m_subtitle_thread->Create();
1571 if (m_options.identify == false)
1572 m_callback.OnPlayBackStarted();
1574 bool stopPlaying = false;
1575 while (!m_bAbortRequest && !stopPlaying)
1577 player_status pstatus = (player_status)GetPlayerSerializedState();
1580 case PLAYER_INITING:
1581 case PLAYER_TYPE_REDY:
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
1589 case PLAYER_RUNNING:
1591 // playback status, decoder is running
1595 case PLAYER_BUFFERING:
1597 case PLAYER_SEARCHING:
1598 case PLAYER_SEARCHOK:
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));
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));
1614 case PLAYER_PLAYEND:
1616 if (m_log_level > 5)
1617 CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus));
1621 if (m_log_level > 5)
1623 printf("CAMLPlayer::Process PLAYER_ERROR\n");
1624 printf("CAMLPlayer::Process: %s\n", m_dll->player_status2str(pstatus));
1626 m_bAbortRequest = true;
1631 if (m_log_level > 5)
1633 CLog::Log(LOGDEBUG, "CAMLPlayer::Process PLAYER_STOPPED");
1634 CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus));
1646 CLog::Log(LOGERROR, "%s", error);
1650 CLog::Log(LOGERROR, "CAMLPlayer::Process Exception thrown");
1653 if (m_log_level > 5)
1654 CLog::Log(LOGDEBUG, "CAMLPlayer::Process stopped");
1655 if (m_dll->check_pid_valid(m_pid))
1657 delete m_subtitle_thread;
1658 m_subtitle_thread = NULL;
1659 m_dll->player_stop(m_pid);
1660 m_dll->player_exit(m_pid);
1664 // we are done, hide the mainvideo layer.
1665 ShowMainVideo(false);
1670 // reset ac3/dts passthough
1671 SetAudioPassThrough(AFORMAT_UNKNOWN);
1673 if (m_log_level > 5)
1674 CLog::Log(LOGDEBUG, "CAMLPlayer::Process exit");
1677 void CAMLPlayer::GetRenderFeatures(std::vector<int> &renderFeatures)
1679 renderFeatures.push_back(RENDERFEATURE_ZOOM);
1680 renderFeatures.push_back(RENDERFEATURE_CONTRAST);
1681 renderFeatures.push_back(RENDERFEATURE_BRIGHTNESS);
1682 renderFeatures.push_back(RENDERFEATURE_STRETCH);
1685 void CAMLPlayer::GetDeinterlaceMethods(std::vector<int> &deinterlaceMethods)
1687 deinterlaceMethods.push_back(VS_INTERLACEMETHOD_DEINTERLACE);
1690 void CAMLPlayer::GetDeinterlaceModes(std::vector<int> &deinterlaceModes)
1692 deinterlaceModes.push_back(VS_DEINTERLACEMODE_AUTO);
1695 void CAMLPlayer::GetScalingMethods(std::vector<int> &scalingMethods)
1699 void CAMLPlayer::GetAudioCapabilities(std::vector<int> &audioCaps)
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);
1708 void CAMLPlayer::GetSubtitleCapabilities(std::vector<int> &subCaps)
1710 subCaps.push_back(IPC_SUBS_EXTERNAL);
1711 subCaps.push_back(IPC_SUBS_SELECT);
1712 subCaps.push_back(IPC_SUBS_OFFSET);
1716 ////////////////////////////////////////////////////////////////////////////////////////////
1717 ////////////////////////////////////////////////////////////////////////////////////////////
1718 int CAMLPlayer::GetVideoStreamCount()
1720 //CLog::Log(LOGDEBUG, "CAMLPlayer::GetVideoStreamCount(%d)", m_video_count);
1721 return m_video_count;
1724 void CAMLPlayer::ShowMainVideo(bool show)
1726 if (m_show_mainvideo == show)
1729 aml_set_sysfs_int("/sys/class/video/disable_video", show ? 0:1);
1731 m_show_mainvideo = show;
1734 void CAMLPlayer::SetVideoZoom(float zoom)
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));
1742 void CAMLPlayer::SetVideoContrast(int contrast)
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);
1749 void CAMLPlayer::SetVideoBrightness(int brightness)
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);
1756 void CAMLPlayer::SetVideoSaturation(int saturation)
1758 // output saturation range is -127 to 127 with default of 127.
1759 aml_set_sysfs_int("/sys/class/video/saturation", saturation);
1762 void CAMLPlayer::SetAudioPassThrough(int format)
1764 aml_set_audio_passthrough(
1765 (m_audio_passthrough_ac3 && format == AFORMAT_AC3) ||
1766 (m_audio_passthrough_dts && format == AFORMAT_DTS));
1769 int CAMLPlayer::GetPlayerSerializedState(void)
1771 CSingleLock lock(m_aml_state_csection);
1774 int dequeue_size = m_aml_state.size();
1776 if (dequeue_size > 0)
1778 // serialized state is the front element.
1779 playerstate = m_aml_state.front();
1780 // pop the front element if there are
1782 if (dequeue_size > 1)
1783 m_aml_state.pop_front();
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);
1798 int CAMLPlayer::UpdatePlayerInfo(int pid, player_info_t *info)
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());
1806 CSingleLock lock(amlplayer->m_aml_state_csection);
1807 if (amlplayer->m_aml_state.back() != info->status)
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);
1816 bool CAMLPlayer::CheckPlaying()
1818 return ((player_status)GetPlayerSerializedState() == PLAYER_RUNNING);
1821 bool CAMLPlayer::WaitForStopped(int timeout_ms)
1823 while (!m_bAbortRequest && (timeout_ms > 0))
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));
1834 case PLAYER_PLAYEND:
1838 m_bAbortRequest = true;
1847 bool CAMLPlayer::WaitForSearchOK(int timeout_ms)
1849 while (!m_bAbortRequest && (timeout_ms > 0))
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));
1864 m_bAbortRequest = true;
1867 case PLAYER_SEARCHOK:
1876 bool CAMLPlayer::WaitForPlaying(int timeout_ms)
1878 while (!m_bAbortRequest && (timeout_ms > 0))
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);
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));
1897 m_bAbortRequest = true;
1900 case PLAYER_RUNNING:
1901 // restore mute/volume settings
1902 SetMute(m_audio_mute);
1911 bool CAMLPlayer::WaitForFormatValid(int timeout_ms)
1913 while (timeout_ms > 0)
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));
1926 m_bAbortRequest = true;
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);
1943 int res = m_dll->player_get_media_info(m_pid, test_media_info);
1945 uint8_t *t1 = (uint8_t*)test_media_info;
1946 for (size_t i = msize-1; i >= 0; i--)
1950 if (sizeof(media_info_t) != i+1)
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);
1961 media_info = *test_media_info;
1962 free(test_media_info);
1964 int res = m_dll->player_get_media_info(m_pid, &media_info);
1966 if (res != PLAYER_SUCCESS)
1969 if (m_log_level > 5)
1970 media_info_dump(&media_info);
1973 if (media_info.stream_info.has_video && media_info.stream_info.total_video_num > 0)
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++)
1978 AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
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;
1993 m_video_streams.push_back(info);
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)
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;
2005 // bail if we do not get a valid width/height
2006 if (m_video_width == 0 || m_video_height == 0)
2011 if (media_info.stream_info.has_audio && media_info.stream_info.total_audio_num > 0)
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++)
2016 AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
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);
2031 m_audio_streams.push_back(info);
2034 m_audio_index = media_info.stream_info.cur_audio_index;
2035 if (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);
2043 if (media_info.stream_info.has_sub && media_info.stream_info.total_sub_num > 0)
2045 for (int i = 0; i < media_info.stream_info.total_sub_num && i < MAX_SUB_STREAMS; i++)
2047 AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
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);
2056 m_subtitle_index = media_info.stream_info.cur_sub_index;
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;
2065 #if defined(HAS_AMLPLAYER_CHAPTERS)
2067 if (media_info.stream_info.total_chapter_num > 0)
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++)
2072 if (media_info.chapter_info[i] != NULL)
2074 AMLChapterInfo *info = new AMLChapterInfo;
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);
2092 void CAMLPlayer::ClearStreamInfos()
2094 CSingleLock lock(m_aml_csection);
2096 if (!m_audio_streams.empty())
2098 for (unsigned int i = 0; i < m_audio_streams.size(); i++)
2099 delete m_audio_streams[i];
2100 m_audio_streams.clear();
2105 if (!m_video_streams.empty())
2107 for (unsigned int i = 0; i < m_video_streams.size(); i++)
2108 delete m_video_streams[i];
2109 m_video_streams.clear();
2114 if (!m_subtitle_streams.empty())
2116 for (unsigned int i = 0; i < m_subtitle_streams.size(); i++)
2117 delete m_subtitle_streams[i];
2118 m_subtitle_streams.clear();
2120 m_subtitle_count = 0;
2121 m_subtitle_index = -1;
2123 if (!m_chapters.empty())
2125 for (unsigned int i = 0; i < m_chapters.size(); i++)
2126 delete m_chapters[i];
2129 m_chapter_count = 0;
2132 bool CAMLPlayer::GetStatus()
2134 CSingleLock lock(m_aml_csection);
2136 if (!m_dll->check_pid_valid(m_pid))
2139 player_info_t player_info;
2140 int res = m_dll->player_get_play_info(m_pid, &player_info);
2141 if (res != PLAYER_SUCCESS)
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);
2152 void CAMLPlayer::FindSubtitleFiles()
2154 // find any available external subtitles
2155 std::vector<CStdString> filenames;
2156 CUtil::ScanForExternalSubtitles(m_item.GetPath(), filenames);
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());
2163 for(unsigned int i=0;i<filenames.size();i++)
2165 // if vobsub subtitle:
2166 if (URIUtils::HasExtension(filenames[i], ".idx"))
2168 CStdString strSubFile;
2169 if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
2170 AddSubtitleFile(filenames[i], strSubFile);
2174 if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
2176 AddSubtitleFile(filenames[i]);
2182 int CAMLPlayer::AddSubtitleFile(const std::string &filename, const std::string &subfilename)
2184 std::string ext = URIUtils::GetExtension(filename);
2185 std::string vobsubfile = subfilename;
2189 /* TODO: we do not handle idx/sub binary subs yet.
2190 if (vobsubfile.empty())
2191 vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
2194 if(!v.Open(filename, vobsubfile))
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;
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))
2212 AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
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);
2224 return m_subtitle_streams.size();
2227 bool CAMLPlayer::OpenSubtitleStream(int index)
2229 CLog::Log(LOGNOTICE, "Opening external subtitle stream: %i", index);
2231 CDemuxStream* pStream = NULL;
2232 std::string filename;
2233 CDVDStreamInfo hint;
2235 if (m_subtitle_streams[index]->source == STREAM_SOURCE_DEMUX_SUB)
2238 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2241 SelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
2243 if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename)
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))
2249 m_pSubtitleDemuxer = demux.release();
2252 pStream = m_pSubtitleDemuxer->GetStream(iStream);
2253 if(!pStream || pStream->disabled)
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)
2261 pts += m_offset_pts;
2262 m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE));
2264 hint.Assign(*pStream, true);
2268 else if (m_subtitle_streams[index]->source == STREAM_SOURCE_TEXT)
2270 filename = m_subtitle_streams[index]->filename;
2273 hint.fpsscale = m_subtitle_streams[index]->frame_rate_den;
2274 hint.fpsrate = m_subtitle_streams[index]->frame_rate_num;
2277 m_dvdPlayerSubtitle->CloseStream(true);
2278 if (!m_dvdPlayerSubtitle->OpenStream(hint, filename))
2280 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, index);
2283 pStream->disabled = true;
2284 pStream->SetDiscard(AVDISCARD_ALL);
2292 void CAMLPlayer::SetVideoRect(const CRect &SrcRect, const CRect &DestRect)
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.
2298 // video zoom adjustment.
2299 float zoom = CMediaSettings::Get().GetCurrentVideoSettings().m_CustomZoomAmount;
2300 if ((int)(zoom * 1000) != (int)(m_zoom * 1000))
2304 // video contrast adjustment.
2305 int contrast = CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast;
2306 if (contrast != m_contrast)
2308 SetVideoContrast(contrast);
2309 m_contrast = contrast;
2311 // video brightness adjustment.
2312 int brightness = CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness;
2313 if (brightness != m_brightness)
2315 SetVideoBrightness(brightness);
2316 m_brightness = brightness;
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))
2322 m_dst_rect = DestRect;
2323 m_view_mode = CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode;
2327 // mainvideo 'should' be showing already if we get here, make sure.
2328 ShowMainVideo(true);
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;
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;
2351 ShowMainVideo(false);
2353 // goofy 0/1 based difference in aml axis coordinates.
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);
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());
2370 // we only get called once gui has changed to something
2371 // that would show video playback, so show it.
2372 ShowMainVideo(true);
2375 void CAMLPlayer::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect)
2377 CAMLPlayer *player = (CAMLPlayer*)ctx;
2378 player->SetVideoRect(SrcRect, DestRect);