From 2d9b3a544d0a5d5f7b0564fd1a9ad216bcb6aaab Mon Sep 17 00:00:00 2001 From: davilla Date: Thu, 19 Jul 2012 22:28:13 -0400 Subject: [PATCH] [droid] add new internal player for amlogic based SoCs --- .gitignore | 1 + Makefile.in | 4 + configure.in | 11 +- system/playercorefactory.xml | 6 +- xbmc/cores/amlplayer/AMLPlayer.cpp | 2269 ++++++++++++++++++++ xbmc/cores/amlplayer/AMLPlayer.h | 256 +++ xbmc/cores/amlplayer/DllLibamplayer.h | 163 ++ xbmc/cores/amlplayer/FileURLProtocol.cpp | 144 ++ xbmc/cores/amlplayer/FileURLProtocol.h | 81 + xbmc/cores/amlplayer/Makefile.in | 15 + .../cores/amlplayer/amlplayer_advancedsettings.xml | 6 + xbmc/cores/playercorefactory/PlayerCoreConfig.h | 6 + xbmc/cores/playercorefactory/PlayerCoreFactory.cpp | 7 + xbmc/cores/playercorefactory/PlayerCoreFactory.h | 14 +- xbmc/input/linux/LinuxInputDevices.cpp | 17 + xbmc/video/windows/GUIWindowFullScreen.cpp | 6 +- 16 files changed, 2997 insertions(+), 9 deletions(-) mode change 100755 => 100644 Makefile.in create mode 100644 xbmc/cores/amlplayer/AMLPlayer.cpp create mode 100644 xbmc/cores/amlplayer/AMLPlayer.h create mode 100644 xbmc/cores/amlplayer/DllLibamplayer.h create mode 100644 xbmc/cores/amlplayer/FileURLProtocol.cpp create mode 100644 xbmc/cores/amlplayer/FileURLProtocol.h create mode 100644 xbmc/cores/amlplayer/Makefile.in create mode 100644 xbmc/cores/amlplayer/amlplayer_advancedsettings.xml diff --git a/.gitignore b/.gitignore index 96085c5..c80d0e2 100644 --- a/.gitignore +++ b/.gitignore @@ -536,6 +536,7 @@ lib/cmyth/Makefile /xbmc/cores/VideoRenderers/Makefile /xbmc/cores/dvdplayer/Makefile +/xbmc/cores/amlplayer/Makefile # /lib/ffmpeg/ /lib/ffmpeg/config.h diff --git a/Makefile.in b/Makefile.in old mode 100755 new mode 100644 index 0057729..10d5be9 --- a/Makefile.in +++ b/Makefile.in @@ -139,6 +139,10 @@ DIRECTORY_ARCHIVES += xbmc/windowing/android/windowing_android.a DIRECTORY_ARCHIVES += xbmc/android/activity/activity.a endif +ifeq (@USE_AMLPLAYER@,1) +DIRECTORY_ARCHIVES += xbmc/cores/amlplayer/amlplayer.a +endif + PAPCODECS_DIRS= \ lib/xbadpcm \ lib/nosefart \ diff --git a/configure.in b/configure.in index 17ba34b..5e8b488 100644 --- a/configure.in +++ b/configure.in @@ -475,7 +475,7 @@ AC_ARG_ENABLE([libcap], AC_ARG_ENABLE([player], [AS_HELP_STRING([--enable-player], - [enable additional players from a list of comma separated names, (default is none)])], + [enable additional players from a list of comma separated names, (default is none, choices are amlplayer)])], [add_players=$enableval], [add_players=no]) @@ -1648,6 +1648,14 @@ AC_CHECK_HEADERS([yajl/yajl_version.h], [], [ AC_DEFINE(YAJL_MAJOR, 1, [yajl version 1]) ], []) +# additional internal players +case $add_players in + *amlplayer*) + AC_CHECK_HEADER([amlplayer/amports/amstream.h],, AC_MSG_ERROR($missing_headers)) + XB_ADD_PLAYER([AMLPLAYER], [amlplayer]) + ;; +esac + # platform specific bin utilities if test "$host_vendor" != "apple" ; then AC_CHECK_PROG(HAVE_GAWK,gawk,"yes","no",) @@ -2114,6 +2122,7 @@ OUTPUT_FILES="Makefile \ xbmc/cores/dvdplayer/DVDSubtitles/Makefile \ xbmc/cores/AudioEngine/Makefile \ xbmc/cores/paplayer/Makefile \ + xbmc/cores/amlplayer/Makefile \ lib/timidity/Makefile \ lib/xbadpcm/Makefile \ lib/asap/Makefile \ diff --git a/system/playercorefactory.xml b/system/playercorefactory.xml index 66784de..6e475ef 100644 --- a/system/playercorefactory.xml +++ b/system/playercorefactory.xml @@ -24,9 +24,9 @@ - - - + + + diff --git a/xbmc/cores/amlplayer/AMLPlayer.cpp b/xbmc/cores/amlplayer/AMLPlayer.cpp new file mode 100644 index 0000000..40dadfc --- /dev/null +++ b/xbmc/cores/amlplayer/AMLPlayer.cpp @@ -0,0 +1,2269 @@ +/* + * Copyright (C) 2011-2012 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#include "AMLPlayer.h" +#include "Application.h" +#include "FileItem.h" +#include "FileURLProtocol.h" +#include "GUIInfoManager.h" +#include "ThumbLoader.h" +#include "Util.h" +#include "cores/VideoRenderers/RenderFlags.h" +#include "cores/VideoRenderers/RenderFormats.h" +#include "cores/VideoRenderers/RenderManager.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "settings/VideoSettings.h" +#include "threads/SingleLock.h" +#include "utils/log.h" +#include "utils/TimeUtils.h" +#include "utils/URIUtils.h" +#include "utils/LangCodeExpander.h" +#include "utils/Variant.h" +#include "windowing/WindowingFactory.h" +#include "windowing/egl/WinEGLPlatform.h" + +// for external subtitles +#include "xbmc/cores/dvdplayer/DVDClock.h" +#include "xbmc/cores/dvdplayer/DVDPlayerSubtitle.h" +#include "xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h" + +// amlogic libplayer +#include "DllLibamplayer.h" + +struct AMLChapterInfo +{ + std::string name; + int64_t seekto_ms; +}; + +struct AMLPlayerStreamInfo +{ + void Clear() + { + id = 0; + width = 0; + height = 0; + aspect_ratio_num = 0; + aspect_ratio_den = 0; + frame_rate_num = 0; + frame_rate_den = 0; + bit_rate = 0; + duration = 0; + channel = 0; + sample_rate = 0; + language = ""; + type = STREAM_NONE; + source = STREAM_SOURCE_NONE; + name = ""; + filename = ""; + filename2 = ""; + } + + int id; + StreamType type; + StreamSource source; + int width; + int height; + int aspect_ratio_num; + int aspect_ratio_den; + int frame_rate_num; + int frame_rate_den; + int bit_rate; + int duration; + int channel; + int sample_rate; + int format; + std::string language; + std::string name; + std::string filename; + std::string filename2; // for vobsub subtitles, 2 files are necessary (idx/sub) +}; + + +static int set_sysfs_str(const char *path, const char *val) +{ + int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd >= 0) + { + write(fd, val, strlen(val)); + close(fd); + return 0; + } + return -1; +} + +static int set_sysfs_int(const char *path, const int val) +{ + char bcmd[16]; + int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd >= 0) + { + sprintf(bcmd, "%d", val); + write(fd, bcmd, strlen(bcmd)); + close(fd); + return 0; + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +static int media_info_dump(media_info_t* minfo) +{ + int i = 0; + CLog::Log(LOGDEBUG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + CLog::Log(LOGDEBUG, "======||file size:%lld", minfo->stream_info.file_size); + CLog::Log(LOGDEBUG, "======||file type:%d", minfo->stream_info.type); + CLog::Log(LOGDEBUG, "======||duration:%d", minfo->stream_info.duration); + CLog::Log(LOGDEBUG, "======||has video track?:%s", minfo->stream_info.has_video>0?"YES!":"NO!"); + CLog::Log(LOGDEBUG, "======||has audio track?:%s", minfo->stream_info.has_audio>0?"YES!":"NO!"); + CLog::Log(LOGDEBUG, "======||has internal subtitle?:%s", minfo->stream_info.has_sub>0?"YES!":"NO!"); + CLog::Log(LOGDEBUG, "======||internal subtile counts:%d", minfo->stream_info.total_sub_num); + if (minfo->stream_info.has_video && minfo->stream_info.total_video_num > 0) + { + CLog::Log(LOGDEBUG, "======||video index:%d", minfo->stream_info.cur_video_index); + CLog::Log(LOGDEBUG, "======||video counts:%d", minfo->stream_info.total_video_num); + CLog::Log(LOGDEBUG, "======||video width :%d", minfo->video_info[0]->width); + CLog::Log(LOGDEBUG, "======||video height:%d", minfo->video_info[0]->height); + CLog::Log(LOGDEBUG, "======||video ratio :%d:%d", minfo->video_info[0]->aspect_ratio_num,minfo->video_info[0]->aspect_ratio_den); + CLog::Log(LOGDEBUG, "======||frame_rate :%.2f", (float)minfo->video_info[0]->frame_rate_num/minfo->video_info[0]->frame_rate_den); + CLog::Log(LOGDEBUG, "======||video bitrate:%d", minfo->video_info[0]->bit_rate); + CLog::Log(LOGDEBUG, "======||video format:%d", minfo->video_info[0]->format); + CLog::Log(LOGDEBUG, "======||video duration:%d", minfo->video_info[0]->duartion); + } + if (minfo->stream_info.has_audio && minfo->stream_info.total_audio_num > 0) + { + CLog::Log(LOGDEBUG, "======||audio index:%d", minfo->stream_info.cur_audio_index); + CLog::Log(LOGDEBUG, "======||audio counts:%d", minfo->stream_info.total_audio_num); + for (i = 0; i < minfo->stream_info.total_audio_num; i++) + { + CLog::Log(LOGDEBUG, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); + CLog::Log(LOGDEBUG, "======||audio track(%d) id:%d", i, minfo->audio_info[i]->id); + CLog::Log(LOGDEBUG, "======||audio track(%d) codec type:%d", i, minfo->audio_info[i]->aformat); + CLog::Log(LOGDEBUG, "======||audio track(%d) audio_channel:%d", i, minfo->audio_info[i]->channel); + CLog::Log(LOGDEBUG, "======||audio track(%d) bit_rate:%d", i, minfo->audio_info[i]->bit_rate); + CLog::Log(LOGDEBUG, "======||audio track(%d) audio_samplerate:%d", i, minfo->audio_info[i]->sample_rate); + CLog::Log(LOGDEBUG, "======||audio track(%d) duration:%d", i, minfo->audio_info[i]->duration); + CLog::Log(LOGDEBUG, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); + if (NULL != minfo->audio_info[i]->audio_tag) + { + CLog::Log(LOGDEBUG, "======||audio track title:%s", minfo->audio_info[i]->audio_tag->title!=NULL?minfo->audio_info[i]->audio_tag->title:"unknown"); + CLog::Log(LOGDEBUG, "======||audio track album:%s", minfo->audio_info[i]->audio_tag->album!=NULL?minfo->audio_info[i]->audio_tag->album:"unknown"); + CLog::Log(LOGDEBUG, "======||audio track author:%s", minfo->audio_info[i]->audio_tag->author!=NULL?minfo->audio_info[i]->audio_tag->author:"unknown"); + CLog::Log(LOGDEBUG, "======||audio track year:%s", minfo->audio_info[i]->audio_tag->year!=NULL?minfo->audio_info[i]->audio_tag->year:"unknown"); + CLog::Log(LOGDEBUG, "======||audio track comment:%s", minfo->audio_info[i]->audio_tag->comment!=NULL?minfo->audio_info[i]->audio_tag->comment:"unknown"); + CLog::Log(LOGDEBUG, "======||audio track genre:%s", minfo->audio_info[i]->audio_tag->genre!=NULL?minfo->audio_info[i]->audio_tag->genre:"unknown"); + CLog::Log(LOGDEBUG, "======||audio track copyright:%s", minfo->audio_info[i]->audio_tag->copyright!=NULL?minfo->audio_info[i]->audio_tag->copyright:"unknown"); + CLog::Log(LOGDEBUG, "======||audio track track:%d", minfo->audio_info[i]->audio_tag->track); + } + } + } + if (minfo->stream_info.has_sub && minfo->stream_info.total_sub_num > 0) + { + CLog::Log(LOGDEBUG, "======||subtitle index:%d", minfo->stream_info.cur_sub_index); + CLog::Log(LOGDEBUG, "======||subtitle counts:%d", minfo->stream_info.total_sub_num); + for (i = 0; i < minfo->stream_info.total_sub_num; i++) + { + if (0 == minfo->sub_info[i]->internal_external){ + CLog::Log(LOGDEBUG, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); + CLog::Log(LOGDEBUG, "======||internal subtitle(%d) pid:%d", i, minfo->sub_info[i]->id); + CLog::Log(LOGDEBUG, "======||internal subtitle(%d) language:%s", i, minfo->sub_info[i]->sub_language?minfo->sub_info[i]->sub_language:"unknown"); + CLog::Log(LOGDEBUG, "======||internal subtitle(%d) width:%d", i, minfo->sub_info[i]->width); + CLog::Log(LOGDEBUG, "======||internal subtitle(%d) height:%d", i, minfo->sub_info[i]->height); + CLog::Log(LOGDEBUG, "======||internal subtitle(%d) resolution:%d", i, minfo->sub_info[i]->resolution); + CLog::Log(LOGDEBUG, "======||internal subtitle(%d) subtitle size:%lld", i, minfo->sub_info[i]->subtitle_size); + CLog::Log(LOGDEBUG, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); + } + } + } + CLog::Log(LOGDEBUG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + return 0; +} + +static const char* VideoCodecName(int vformat) +{ + const char *format = ""; + + switch(vformat) + { + case VFORMAT_MPEG12: + format = "mpeg12"; + break; + case VFORMAT_MPEG4: + format = "mpeg4"; + break; + case VFORMAT_H264: + format = "h264"; + break; + case VFORMAT_MJPEG: + format = "mjpeg"; + break; + case VFORMAT_REAL: + format = "real"; + break; + case VFORMAT_JPEG: + format = "jpeg"; + break; + case VFORMAT_VC1: + format = "vc1"; + break; + case VFORMAT_AVS: + format = "avs"; + break; + case VFORMAT_SW: + format = "sw"; + break; + case VFORMAT_H264MVC: + format = "h264mvc"; + break; + default: + format = "unknown"; + break; + } + return format; +} + +static const char* AudioCodecName(int aformat) +{ + const char *format = ""; + + switch(aformat) + { + case AFORMAT_MPEG: + format = "mpeg"; + break; + case AFORMAT_PCM_S16LE: + format = "pcm"; + break; + case AFORMAT_AAC: + format = "aac"; + break; + case AFORMAT_AC3: + format = "ac3"; + break; + case AFORMAT_ALAW: + format = "alaw"; + break; + case AFORMAT_MULAW: + format = "mulaw"; + break; + case AFORMAT_DTS: + format = "dts"; + break; + case AFORMAT_PCM_S16BE: + format = "pcm"; + break; + case AFORMAT_FLAC: + format = "flac"; + break; + case AFORMAT_COOK: + format = "cook"; + break; + case AFORMAT_PCM_U8: + format = "pcm"; + break; + case AFORMAT_ADPCM: + format = "adpcm"; + break; + case AFORMAT_AMR: + format = "amr"; + break; + case AFORMAT_RAAC: + format = "raac"; + break; + case AFORMAT_WMA: + format = "wma"; + break; + case AFORMAT_WMAPRO: + format = "wmapro"; + break; + case AFORMAT_PCM_BLURAY: + format = "lpcm"; + break; + case AFORMAT_ALAC: + format = "alac"; + break; + case AFORMAT_VORBIS: + format = "vorbis"; + break; + default: + format = "unknown"; + break; + } + + return format; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +CAMLSubTitleThread::CAMLSubTitleThread(DllLibAmplayer *dll) : + CThread("CAMLSubTitleThread"), + m_dll(dll), + m_subtitle_codec(-1) +{ +} + +CAMLSubTitleThread::~CAMLSubTitleThread() +{ + StopThread(); +} + +void CAMLSubTitleThread::UpdateSubtitle(CStdString &subtitle, int64_t elapsed_ms) +{ + CSingleLock lock(m_subtitle_csection); + if (m_subtitle_strings.size()) + { + AMLSubtitle *amlsubtitle; + // remove any expired subtitles + std::deque::iterator it = m_subtitle_strings.begin(); + while (it != m_subtitle_strings.end()) + { + amlsubtitle = *it; + if (elapsed_ms > amlsubtitle->endtime) + it = m_subtitle_strings.erase(it); + else + it++; + } + + // find the current subtitle + it = m_subtitle_strings.begin(); + while (it != m_subtitle_strings.end()) + { + amlsubtitle = *it; + if (elapsed_ms > amlsubtitle->bgntime && elapsed_ms < amlsubtitle->endtime) + { + subtitle = amlsubtitle->string; + break; + } + it++; + } + } +} + +void CAMLSubTitleThread::Process(void) +{ + CLog::Log(LOGDEBUG, "CAMLSubTitleThread::Process begin"); + + m_subtitle_codec = m_dll->codec_open_sub_read(); + if (m_subtitle_codec < 0) + CLog::Log(LOGERROR, "CAMLSubTitleThread::Process: codec_open_sub_read failed"); + + while (!m_bStop) + { + if (m_subtitle_codec > 0) + { + // poll sub codec, we return on timeout or when a sub gets loaded + // TODO: codec_poll_sub_fd has a bug in kernel driver, it trashes + // subs in certain conditions so we read garbage, manual poll for now. + //codec_poll_sub_fd(m_subtitle_codec, 1000); + int sub_size = m_dll->codec_get_sub_size_fd(m_subtitle_codec); + if (sub_size > 0) + { + int sub_type = 0, sub_pts = 0; + // calloc sub_size + 1 so we auto terminate the string + char *sub_buffer = (char*)calloc(sub_size + 1, 1); + m_dll->codec_read_sub_data_fd(m_subtitle_codec, sub_buffer, sub_size); + + // check subtitle header stamp + if ((sub_buffer[0] == 0x41) && (sub_buffer[1] == 0x4d) && + (sub_buffer[2] == 0x4c) && (sub_buffer[3] == 0x55) && + (sub_buffer[4] == 0xaa)) + { + // 20 byte header, then subtitle string + if (sub_size >= 20) + { + // csection lock it now as we are diddling shared vars + CSingleLock lock(m_subtitle_csection); + + AMLSubtitle *subtitle = new AMLSubtitle; + + sub_type = (sub_buffer[5] << 16) | (sub_buffer[6] << 8) | sub_buffer[7]; + // sub_pts are in ffmpeg timebase, not ms timebase, convert it. + sub_pts = (sub_buffer[12] << 24) | (sub_buffer[13] << 16) | (sub_buffer[14] << 8) | sub_buffer[15]; + + /* TODO: handle other subtitle codec types + // subtitle codecs + CODEC_ID_DVD_SUBTITLE= 0x17000, + CODEC_ID_DVB_SUBTITLE, + CODEC_ID_TEXT, ///< raw UTF-8 text + CODEC_ID_XSUB, + CODEC_ID_SSA, + CODEC_ID_MOV_TEXT, + CODEC_ID_HDMV_PGS_SUBTITLE, + CODEC_ID_DVB_TELETEXT, + CODEC_ID_SRT, + CODEC_ID_MICRODVD, + */ + switch(sub_type) + { + default: + CLog::Log(LOGDEBUG, "CAMLSubTitleThread::Process: fixme :) " + "sub_type(0x%x), size(%d), bgntime(%lld), endtime(%lld), string(%s)", + sub_type, sub_size-20, subtitle->bgntime, subtitle->endtime, &sub_buffer[20]); + break; + case CODEC_ID_TEXT: + subtitle->bgntime = sub_pts/ 90; + subtitle->endtime = subtitle->bgntime + 4000; + subtitle->string = &sub_buffer[20]; + break; + case CODEC_ID_SSA: + if (strncmp((const char*)&sub_buffer[20], "Dialogue:", 9) == 0) + { + int vars_found, hour1, min1, sec1, hunsec1, hour2, min2, sec2, hunsec2, nothing; + char line3[sub_size]; + char *line = &sub_buffer[20]; + + memset(line3, 0x00, sub_size); + vars_found = sscanf(line, "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\n\r]", + ¬hing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line3); + if (vars_found < 10) + vars_found = sscanf(line, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,%[^\n\r]", + ¬hing, &hour1, &min1, &sec1, &hunsec1, &hour2, &min2, &sec2, &hunsec2, line3); + + if (vars_found > 9) + { + char *tmp, *line2 = strchr(line3, ','); + // use 32 for the case that the amount of commas increase with newer SSA versions + for (int comma = 4; comma < 32; comma++) + { + tmp = strchr(line2 + 1, ','); + if (!tmp) + break; + if (*(++tmp) == ' ') + break; + // a space after a comma means we are already in a sentence + line2 = tmp; + } + // eliminate the trailing comma + if (*line2 == ',') + line2++; + subtitle->bgntime = 10 * (360000 * hour1 + 6000 * min1 + 100 * sec1 + hunsec1); + subtitle->endtime = 10 * (360000 * hour2 + 6000 * min2 + 100 * sec2 + hunsec2); + subtitle->string = line2; + // convert tags to what we understand + if (subtitle->string.Replace("{\\i1}","[I]")) + subtitle->string.Replace("{\\i0}","[/I]"); + if (subtitle->string.Replace("{\\b1}","[B]")) + subtitle->string.Replace("{\\b0}","[/B]"); + // remove anything other tags + for (std::string::const_iterator it = subtitle->string.begin(); it != subtitle->string.end(); ++it) + { + size_t beg = subtitle->string.find("{\\"); + if (beg != std::string::npos) + { + size_t end = subtitle->string.find("}", beg); + if (end != std::string::npos) + subtitle->string.erase(beg, end-beg+1); + } + } + } + } + break; + } + free(sub_buffer); + + if (subtitle->string.length()) + { + // quirks + subtitle->string.Replace("'","\'"); + m_subtitle_strings.push_back(subtitle); + // fixup existing endtimes so they do not exceed bgntime of previous subtitle + for (size_t i = 0; i < m_subtitle_strings.size() - 1; i++) + { + if (m_subtitle_strings[i]->endtime > m_subtitle_strings[i+1]->bgntime) + m_subtitle_strings[i]->endtime = m_subtitle_strings[i+1]->bgntime; + } + } + } + } + } + else + { + usleep(100 * 1000); + } + } + else + { + usleep(250 * 1000); + } + } + m_subtitle_strings.clear(); + if (m_subtitle_codec > 0) + m_dll->codec_close_sub_fd(m_subtitle_codec); + m_subtitle_codec = -1; + + CLog::Log(LOGDEBUG, "CAMLSubTitleThread::Process end"); +} +//////////////////////////////////////////////////////////////////////////////////////////// +CAMLPlayer::CAMLPlayer(IPlayerCallback &callback) + : IPlayer(callback), + CThread("CAMLPlayer"), + m_ready(true) +{ + m_dll = new DllLibAmplayer; + m_dll->Load(); + m_pid = -1; + m_speed = 0; + m_paused = false; +#if defined(_DEBUG) + m_log_level = 5; +#else + m_log_level = 3; +#endif + m_StopPlaying = false; + + // for external subtitles + m_dvdOverlayContainer = new CDVDOverlayContainer; + m_dvdPlayerSubtitle = new CDVDPlayerSubtitle(m_dvdOverlayContainer); +} + +CAMLPlayer::~CAMLPlayer() +{ + CloseFile(); + + delete m_dvdPlayerSubtitle; + delete m_dvdOverlayContainer; + delete m_dll, m_dll = NULL; +} + +bool CAMLPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options) +{ + try + { + CLog::Log(LOGNOTICE, "CAMLPlayer: Opening: %s", file.GetPath().c_str()); + // if playing a file close it first + // this has to be changed so we won't have to close it. + if (IsRunning()) + CloseFile(); + + m_item = file; + m_options = options; + + m_elapsed_ms = 0; + m_duration_ms = 0; + + m_audio_info = "none"; + m_audio_delay = g_settings.m_currentVideoSettings.m_AudioDelay; + m_audio_passthrough_ac3 = g_guiSettings.GetBool("audiooutput.ac3passthrough"); + m_audio_passthrough_dts = g_guiSettings.GetBool("audiooutput.dtspassthrough"); + + m_video_info = "none"; + m_video_width = 0; + m_video_height = 0; + m_video_fps_numerator = 25; + m_video_fps_denominator = 1; + + m_subtitle_delay = 0; + m_subtitle_thread = NULL; + + m_chapter_index = 0; + m_chapter_count = 0; + + m_show_mainvideo = -1; + m_dst_rect.SetRect(0, 0, 0, 0); + m_zoom = -1; + m_contrast = -1; + m_brightness = -1; + + ClearStreamInfos(); + + m_StopPlaying = false; + // setup to spin the busy dialog until we are playing + m_ready.Reset(); + + g_renderManager.PreInit(); + + // create the playing thread + Create(); + if (!m_ready.WaitMSec(100)) + { + CGUIDialogBusy *dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY); + dialog->Show(); + while (!m_StopPlaying && !m_ready.WaitMSec(1)) + g_windowManager.ProcessRenderLoop(false); + dialog->Close(); + } + + // Playback might have been stopped due to some error. + if (m_bStop || m_StopPlaying) + return false; + + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__); + return false; + } +} + +bool CAMLPlayer::CloseFile() +{ + CLog::Log(LOGDEBUG, "CAMLPlayer::CloseFile"); + + // set the abort request so that other threads can finish up + m_StopPlaying = true; + + CLog::Log(LOGDEBUG, "CAMLPlayer: waiting for threads to exit"); + // wait for the main thread to finish up + // since this main thread cleans up all other resources and threads + // we are done after the StopThread call + StopThread(); + + CLog::Log(LOGDEBUG, "CAMLPlayer: finished waiting"); + + g_renderManager.UnInit(); + + return true; +} + +bool CAMLPlayer::IsPlaying() const +{ + return !m_bStop; +} + +void CAMLPlayer::Pause() +{ + CLog::Log(LOGDEBUG, "CAMLPlayer::Pause"); + CSingleLock lock(m_aml_csection); + + if ((m_pid < 0) && m_StopPlaying) + return; + + if (m_paused) + m_dll->player_resume(m_pid); + else + m_dll->player_pause(m_pid); + + m_paused = !m_paused; +} + +bool CAMLPlayer::IsPaused() const +{ + return m_paused; +} + +bool CAMLPlayer::HasVideo() const +{ + return m_video_count > 0; +} + +bool CAMLPlayer::HasAudio() const +{ + return m_audio_count > 0; +} + +void CAMLPlayer::ToggleFrameDrop() +{ + CLog::Log(LOGDEBUG, "CAMLPlayer::ToggleFrameDrop"); +} + +bool CAMLPlayer::CanSeek() +{ + return GetTotalTime() > 0; +} + +void CAMLPlayer::Seek(bool bPlus, bool bLargeStep) +{ + // force updated to m_elapsed_ms, m_duration_ms. + GetStatus(); + + CSingleLock lock(m_aml_csection); + + // try chapter seeking first, chapter_index is ones based. + int chapter_index = GetChapter(); + if (bLargeStep) + { + // seek to next chapter + if (bPlus && (chapter_index < GetChapterCount())) + { + SeekChapter(chapter_index + 1); + return; + } + // seek to previous chapter + if (!bPlus && chapter_index) + { + SeekChapter(chapter_index - 1); + return; + } + } + + int64_t seek_ms; + if (g_advancedSettings.m_videoUseTimeSeeking) + { + if (bLargeStep && (GetTotalTime() > (2 * g_advancedSettings.m_videoTimeSeekForwardBig))) + seek_ms = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig; + else + seek_ms = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward; + // convert to milliseconds + seek_ms *= 1000; + seek_ms += m_elapsed_ms; + } + else + { + float percent; + if (bLargeStep) + percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig; + else + percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward; + percent /= 100.0f; + percent += (float)m_elapsed_ms/(float)m_duration_ms; + // convert to milliseconds + seek_ms = m_duration_ms * percent; + } + + // handle stacked videos, dvdplayer does it so we do it too. + if (g_application.CurrentFileItem().IsStack() && + (seek_ms > m_duration_ms || seek_ms < 0)) + { + CLog::Log(LOGDEBUG, "CAMLPlayer::Seek: In mystery code, what did I do"); + g_application.SeekTime((seek_ms - m_elapsed_ms) * 0.001 + g_application.GetTime()); + // warning, don't access any object variables here as + // the object may have been destroyed + return; + } + + if (seek_ms <= 1000) + seek_ms = 1000; + + if (seek_ms > m_duration_ms) + seek_ms = m_duration_ms; + + // do seek here + g_infoManager.SetDisplayAfterSeek(100000); + SeekTime(seek_ms); + m_callback.OnPlayBackSeek((int)seek_ms, (int)(seek_ms - m_elapsed_ms)); + g_infoManager.SetDisplayAfterSeek(); +} + +bool CAMLPlayer::SeekScene(bool bPlus) +{ + CLog::Log(LOGDEBUG, "CAMLPlayer::SeekScene"); + return false; +} + +void CAMLPlayer::SeekPercentage(float fPercent) +{ + CSingleLock lock(m_aml_csection); + + // force updated to m_elapsed_ms, m_duration_ms. + GetStatus(); + + if (m_duration_ms) + { + int64_t seek_ms = fPercent * m_duration_ms / 100.0; + if (seek_ms <= 1000) + seek_ms = 1000; + + // do seek here + g_infoManager.SetDisplayAfterSeek(100000); + SeekTime(seek_ms); + m_callback.OnPlayBackSeek((int)seek_ms, (int)(seek_ms - m_elapsed_ms)); + g_infoManager.SetDisplayAfterSeek(); + } +} + +float CAMLPlayer::GetPercentage() +{ + GetStatus(); + if (m_duration_ms) + return 100.0f * (float)m_elapsed_ms/(float)m_duration_ms; + else + return 0.0f; +} + +void CAMLPlayer::SetVolume(float volume) +{ + CLog::Log(LOGDEBUG, "CAMLPlayer::SetVolume(%f)", volume); +#if !defined(TARGET_ANDROID) + CSingleLock lock(m_aml_csection); + // volume is a float percent from 0.0 to 1.0 + if (m_dll->check_pid_valid(m_pid)) + m_dll->audio_set_volume(m_pid, volume); +#endif +} + +void CAMLPlayer::GetAudioInfo(CStdString &strAudioInfo) +{ + CSingleLock lock(m_aml_csection); + if (m_audio_streams.size() == 0 || m_audio_index > (int)(m_audio_streams.size() - 1)) + return; + + strAudioInfo.Format("Audio stream (%s) [kB/s:%.2f]", + AudioCodecName(m_audio_streams[m_audio_index]->format), + (double)m_audio_streams[m_audio_index]->bit_rate / 1024.0); +} + +void CAMLPlayer::GetVideoInfo(CStdString &strVideoInfo) +{ + CSingleLock lock(m_aml_csection); + if (m_video_streams.size() == 0 || m_video_index > (int)(m_video_streams.size() - 1)) + return; + + strVideoInfo.Format("Video stream (%s) [fr:%.3f Mb/s:%.2f]", + VideoCodecName(m_video_streams[m_video_index]->format), + GetActualFPS(), + (double)m_video_streams[m_video_index]->bit_rate / (1024.0*1024.0)); +} + +int CAMLPlayer::GetAudioStreamCount() +{ + //CLog::Log(LOGDEBUG, "CAMLPlayer::GetAudioStreamCount"); + return m_audio_count; +} + +int CAMLPlayer::GetAudioStream() +{ + //CLog::Log(LOGDEBUG, "CAMLPlayer::GetAudioStream"); + return m_audio_index; +} + +void CAMLPlayer::GetAudioStreamName(int iStream, CStdString &strStreamName) +{ + //CLog::Log(LOGDEBUG, "CAMLPlayer::GetAudioStreamName"); + CSingleLock lock(m_aml_csection); + + strStreamName.Format("Undefined"); + + if (iStream > (int)m_audio_streams.size() || iStream < 0) + return; + + if ( m_audio_streams[iStream]->language.size()) + { + CStdString name; + g_LangCodeExpander.Lookup( name, m_audio_streams[iStream]->language); + strStreamName = name; + } + +} + +void CAMLPlayer::SetAudioStream(int SetAudioStream) +{ + //CLog::Log(LOGDEBUG, "CAMLPlayer::SetAudioStream"); + CSingleLock lock(m_aml_csection); + + if (SetAudioStream > (int)m_audio_streams.size() || SetAudioStream < 0) + return; + + m_audio_index = SetAudioStream; + SetAudioPassThrough(m_audio_streams[m_audio_index]->format); + + if (m_dll->check_pid_valid(m_pid)) + { + m_dll->player_aid(m_pid, m_audio_streams[m_audio_index]->id); + } +} + +void CAMLPlayer::SetSubTitleDelay(float fValue = 0.0f) +{ + if (GetSubtitleCount()) + { + CSingleLock lock(m_aml_csection); + m_subtitle_delay = fValue * 1000.0; + } +} + +float CAMLPlayer::GetSubTitleDelay() +{ + return (float)m_subtitle_delay / 1000.0; +} + +int CAMLPlayer::GetSubtitleCount() +{ + return m_subtitle_count; +} + +int CAMLPlayer::GetSubtitle() +{ + if (m_subtitle_show) + return m_subtitle_index; + else + return -1; +} + +void CAMLPlayer::GetSubtitleName(int iStream, CStdString &strStreamName) +{ + CSingleLock lock(m_aml_csection); + + strStreamName = ""; + + if (iStream > (int)m_subtitle_streams.size() || iStream < 0) + return; + + if (m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE) + { + if ( m_subtitle_streams[iStream]->language.size()) + { + CStdString name; + g_LangCodeExpander.Lookup(name, m_subtitle_streams[iStream]->language); + strStreamName = name; + } + else + strStreamName = g_localizeStrings.Get(13205); // Unknown + } + else + { + if(m_subtitle_streams[m_subtitle_index]->name.length() > 0) + strStreamName = m_subtitle_streams[m_subtitle_index]->name; + else + strStreamName = g_localizeStrings.Get(13205); // Unknown + } + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::GetSubtitleName, iStream(%d)", iStream); +} + +void CAMLPlayer::SetSubtitle(int iStream) +{ + CSingleLock lock(m_aml_csection); + + if (iStream > (int)m_subtitle_streams.size() || iStream < 0) + return; + + m_subtitle_index = iStream; + + // smells like a bug, if no showing subs and we get called + // to set the subtitle, we are expected to update internal state + // but not show the subtitle. + if (!m_subtitle_show) + return; + + if (m_dll->check_pid_valid(m_pid) && m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE) + m_dll->player_sid(m_pid, m_subtitle_streams[m_subtitle_index]->id); + else + { + m_dvdPlayerSubtitle->CloseStream(true); + OpenSubtitleStream(m_subtitle_index); + } +} + +bool CAMLPlayer::GetSubtitleVisible() +{ + return m_subtitle_show; +} + +void CAMLPlayer::SetSubtitleVisible(bool bVisible) +{ + m_subtitle_show = (bVisible && m_subtitle_count); + g_settings.m_currentVideoSettings.m_SubtitleOn = bVisible; + + if (m_subtitle_show && m_subtitle_count) + { + // on startup, if asked to show subs and SetSubtitle has not + // been called, we are expected to switch/show the 1st subtitle + if (m_subtitle_index < 0) + m_subtitle_index = 0; + if (m_dll->check_pid_valid(m_pid) && m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE) + m_dll->player_sid(m_pid, m_subtitle_streams[m_subtitle_index]->id); + else + OpenSubtitleStream(m_subtitle_index); + } +} + +int CAMLPlayer::AddSubtitle(const CStdString& strSubPath) +{ + CSingleLock lock(m_aml_csection); + + return AddSubtitleFile(strSubPath); +} + +void CAMLPlayer::Update(bool bPauseDrawing) +{ + g_renderManager.Update(bPauseDrawing); +} + +void CAMLPlayer::GetVideoRect(CRect& SrcRect, CRect& DestRect) +{ + g_renderManager.GetVideoRect(SrcRect, DestRect); +} + +void CAMLPlayer::GetVideoAspectRatio(float &fAR) +{ + fAR = g_renderManager.GetAspectRatio(); +} + +int CAMLPlayer::GetChapterCount() +{ + return m_chapter_count; +} + +int CAMLPlayer::GetChapter() +{ + GetStatus(); + + for (int i = 0; i < m_chapter_count - 1; i++) + { + if (m_elapsed_ms >= m_chapters[i]->seekto_ms && m_elapsed_ms < m_chapters[i + 1]->seekto_ms) + return i + 1; + } + return 0; +} + +void CAMLPlayer::GetChapterName(CStdString& strChapterName) +{ + if (m_chapter_count) + strChapterName = m_chapters[GetChapter() - 1]->name; +} + +int CAMLPlayer::SeekChapter(int chapter_index) +{ + CSingleLock lock(m_aml_csection); + + // chapter_index is a one based value. + if (m_chapter_count > 1) + { + if (chapter_index < 1) + chapter_index = 1; + if (chapter_index > m_chapter_count) + return 0; + + // time units are seconds, + // so we add 1000ms to get into the chapter. + int64_t seek_ms = m_chapters[chapter_index - 1]->seekto_ms + 1000; + + // seek to 1 second and play is immediate. + if (seek_ms <= 0) + seek_ms = 1000; + + // seek to chapter here + g_infoManager.SetDisplayAfterSeek(100000); + SeekTime(seek_ms); + m_callback.OnPlayBackSeekChapter(chapter_index); + g_infoManager.SetDisplayAfterSeek(); + } + else + { + // we do not have a chapter list so do a regular big jump. + if (chapter_index > 0) + Seek(true, true); + else + Seek(false, true); + } + return 0; +} + +float CAMLPlayer::GetActualFPS() +{ + float video_fps = m_video_fps_numerator / m_video_fps_denominator; + CLog::Log(LOGDEBUG, "CAMLPlayer::GetActualFPS:m_video_fps(%f)", video_fps); + return video_fps; +} + +void CAMLPlayer::SeekTime(__int64 seek_ms) +{ + CSingleLock lock(m_aml_csection); + + // we cannot seek if paused + if (m_paused) + return; + + if (seek_ms <= 0) + seek_ms = 100; + + // seek here + if (m_dll->check_pid_valid(m_pid)) + { + if (!CheckPlaying()) + return; + // player_timesearch is seconds (float). + m_dll->player_timesearch(m_pid, (float)seek_ms/1000.0); + WaitForSearchOK(5000); + WaitForPlaying(5000); + } +} + +__int64 CAMLPlayer::GetTime() +{ + return m_elapsed_ms; +} + +int CAMLPlayer::GetTotalTime() +{ + return m_duration_ms / 1000; +} + +int CAMLPlayer::GetAudioBitrate() +{ + CSingleLock lock(m_aml_csection); + if (m_audio_streams.size() == 0 || m_audio_index > (int)(m_audio_streams.size() - 1)) + return 0; + + return m_audio_streams[m_audio_index]->bit_rate; +} + +int CAMLPlayer::GetVideoBitrate() +{ + CSingleLock lock(m_aml_csection); + if (m_video_streams.size() == 0 || m_video_index > (int)(m_video_streams.size() - 1)) + return 0; + + return m_video_streams[m_video_index]->bit_rate; +} + +int CAMLPlayer::GetSourceBitrate() +{ + CLog::Log(LOGDEBUG, "CAMLPlayer::GetSourceBitrate"); + return 0; +} + +int CAMLPlayer::GetChannels() +{ + CSingleLock lock(m_aml_csection); + if (m_audio_streams.size() == 0 || m_audio_index > (int)(m_audio_streams.size() - 1)) + return 0; + + return m_audio_streams[m_audio_index]->channel; +} + +int CAMLPlayer::GetBitsPerSample() +{ + CLog::Log(LOGDEBUG, "CAMLPlayer::GetBitsPerSample"); + return 0; +} + +int CAMLPlayer::GetSampleRate() +{ + CSingleLock lock(m_aml_csection); + if (m_audio_streams.size() == 0 || m_audio_index > (int)(m_audio_streams.size() - 1)) + return 0; + + return m_audio_streams[m_audio_index]->sample_rate; +} + +CStdString CAMLPlayer::GetAudioCodecName() +{ + CStdString strAudioCodec = ""; + if (m_audio_streams.size() == 0 || m_audio_index > (int)(m_audio_streams.size() - 1)) + return strAudioCodec; + + strAudioCodec = AudioCodecName(m_audio_streams[m_audio_index]->format); + + return strAudioCodec; +} + +CStdString CAMLPlayer::GetVideoCodecName() +{ + CStdString strVideoCodec = ""; + if (m_video_streams.size() == 0 || m_video_index > (int)(m_video_streams.size() - 1)) + return strVideoCodec; + + strVideoCodec = VideoCodecName(m_video_streams[m_video_index]->format); + + return strVideoCodec; +} + +int CAMLPlayer::GetPictureWidth() +{ + //CLog::Log(LOGDEBUG, "CAMLPlayer::GetPictureWidth(%d)", m_video_width); + return m_video_width; +} + +int CAMLPlayer::GetPictureHeight() +{ + //CLog::Log(LOGDEBUG, "CAMLPlayer::GetPictureHeight(%)", m_video_height); + return m_video_height; +} + +bool CAMLPlayer::GetStreamDetails(CStreamDetails &details) +{ + //CLog::Log(LOGDEBUG, "CAMLPlayer::GetStreamDetails"); + return false; +} + +void CAMLPlayer::ToFFRW(int iSpeed) +{ + CLog::Log(LOGDEBUG, "CAMLPlayer::ToFFRW: iSpeed(%d), m_speed(%d)", iSpeed, m_speed); + CSingleLock lock(m_aml_csection); + + if (!m_dll->check_pid_valid(m_pid) && m_StopPlaying) + return; + + if (m_speed != iSpeed) + { + // recover power of two value + int ipower = 0; + int ispeed = abs(iSpeed); + while (ispeed >>= 1) ipower++; + + switch(ipower) + { + // regular playback + case 0: + m_dll->player_forward(m_pid, 0); + break; + default: + // N x fast forward/rewind (I-frames) + // speed playback 1,2,4,8 + if (iSpeed > 0) + m_dll->player_forward(m_pid, iSpeed); + else + m_dll->player_backward(m_pid, -iSpeed); + break; + } + + m_speed = iSpeed; + } +} + +bool CAMLPlayer::GetCurrentSubtitle(CStdString& strSubtitle) +{ + strSubtitle = ""; + + if (m_subtitle_count) + { + // force updated to m_elapsed_ms. + GetStatus(); + if (m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE && m_subtitle_thread) + { + m_subtitle_thread->UpdateSubtitle(strSubtitle, m_elapsed_ms - m_subtitle_delay); + } + else + { + double pts = DVD_MSEC_TO_TIME(m_elapsed_ms) - DVD_MSEC_TO_TIME(m_subtitle_delay); + m_dvdOverlayContainer->CleanUp(pts); + m_dvdPlayerSubtitle->GetCurrentSubtitle(strSubtitle, pts); + } + } + + return !strSubtitle.IsEmpty(); +} + +void CAMLPlayer::OnStartup() +{ + //m_CurrentVideo.Clear(); + //m_CurrentAudio.Clear(); + //m_CurrentSubtitle.Clear(); + + //CThread::SetName("CAMLPlayer"); +} + +void CAMLPlayer::OnExit() +{ + //CLog::Log(LOGNOTICE, "CAMLPlayer::OnExit()"); + usleep(500 * 1000); + + m_bStop = true; + // if we didn't stop playing, advance to the next item in xbmc's playlist + if (m_options.identify == false) + { + if (m_StopPlaying) + m_callback.OnPlayBackStopped(); + else + m_callback.OnPlayBackEnded(); + } + // set event to inform openfile something went wrong + // in case openfile is still waiting for this event + m_ready.Set(); +} + +void CAMLPlayer::Process() +{ + CLog::Log(LOGNOTICE, "CAMLPlayer::Process"); + try + { + CJobManager::GetInstance().Pause(kJobTypeMediaFlags); + + if (CJobManager::GetInstance().IsProcessing(kJobTypeMediaFlags) > 0) + { + if (!WaitForPausedThumbJobs(20000)) + { + CJobManager::GetInstance().UnPause(kJobTypeMediaFlags); + throw "CAMLPlayer::Process:thumbgen jobs still running !!!"; + } + } + + static AML_URLProtocol vfs_protocol = { + "vfs", + CFileURLProtocol::Open, + CFileURLProtocol::Read, + CFileURLProtocol::Write, + CFileURLProtocol::Seek, + CFileURLProtocol::SeekEx, + CFileURLProtocol::Close, + }; + + CStdString url = m_item.GetPath(); + if (url.Left(strlen("smb://")).Equals("smb://")) + { + // the name string needs to persist + static const char *smb_name = "smb"; + vfs_protocol.name = smb_name; + } + else if (url.Left(strlen("afp://")).Equals("afp://")) + { + // the name string needs to persist + static const char *afp_name = "afp"; + vfs_protocol.name = afp_name; + } + else if (url.Left(strlen("nfs://")).Equals("nfs://")) + { + // the name string needs to persist + static const char *nfs_name = "nfs"; + vfs_protocol.name = nfs_name; + } + else if (url.Left(strlen("rar://")).Equals("rar://")) + { + // the name string needs to persist + static const char *rar_name = "rar"; + vfs_protocol.name = rar_name; + } + else if (url.Left(strlen("ftp://")).Equals("ftp://")) + { + // the name string needs to persist + static const char *http_name = "xb-ftp"; + vfs_protocol.name = http_name; + url = "xb-" + url; + } + else if (url.Left(strlen("ftps://")).Equals("ftps://")) + { + // the name string needs to persist + static const char *http_name = "xb-ftps"; + vfs_protocol.name = http_name; + url = "xb-" + url; + } + else if (url.Left(strlen("http://")).Equals("http://")) + { + // the name string needs to persist + static const char *http_name = "xb-http"; + vfs_protocol.name = http_name; + url = "xb-" + url; + } + else if (url.Left(strlen("https://")).Equals("https://")) + { + // the name string needs to persist + static const char *http_name = "xb-https"; + vfs_protocol.name = http_name; + url = "xb-" + url; + } + CLog::Log(LOGDEBUG, "CAMLPlayer::Process: URL=%s", url.c_str()); + + if (m_dll->player_init() != PLAYER_SUCCESS) + { + CLog::Log(LOGDEBUG, "player init failed"); + throw "CAMLPlayer::Process:player init failed"; + } + CLog::Log(LOGDEBUG, "player init......"); + usleep(250 * 1000); + + // must be after player_init + m_dll->av_register_protocol2(&vfs_protocol, sizeof(vfs_protocol)); + + static play_control_t play_control; + memset(&play_control, 0, sizeof(play_control_t)); + // if we do not register a callback, + // then the libamplayer will free run checking status. + m_dll->player_register_update_callback(&play_control.callback_fn, &UpdatePlayerInfo, 1000); + // amlplayer owns file_name and will release on exit + play_control.file_name = (char*)strdup(url.c_str()); + //play_control.nosound = 1; // if disable audio...,must call this api + play_control.video_index = -1; //MUST + play_control.audio_index = -1; //MUST + play_control.sub_index = -1; //MUST + play_control.hassub = 1; + if (m_options.starttime > 0) // player start position in seconds as is starttime + play_control.t_pos = m_options.starttime; + else + play_control.t_pos = -1; + play_control.need_start = 1; // if 0,you can omit player_start_play API. + // just play video/audio immediately. + // if 1,then need call "player_start_play" API; + //play_control.auto_buffing_enable = 1; + //play_control.buffing_min = 0.2; + //play_control.buffing_middle = 0.5; + //play_control.buffing_max = 0.8; + //play_control.byteiobufsize =; // maps to av_open_input_file buffer size + //play_control.loopbufsize =; + //play_control.enable_rw_on_pause =; + m_aml_state.clear(); + m_aml_state.push_back(0); + m_pid = m_dll->player_start(&play_control, 0); + if (m_pid < 0) + { + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "player start failed! error = %d", m_pid); + throw "CAMLPlayer::Process:player start failed"; + } + + // wait for media to open with 30 second timeout. + if (WaitForFormatValid(30000)) + { + // start the playback. + int res = m_dll->player_start_play(m_pid); + if (res != PLAYER_SUCCESS) + throw "CAMLPlayer::Process:player_start_play() failed"; + } + else + { + throw "CAMLPlayer::Process:WaitForFormatValid timeout"; + } + + // hide the mainvideo layer so we can get stream info + // and setup/transition to gui video playback + // without having video playback blended into it. + if (m_item.IsVideo()) + ShowMainVideo(false); + + // wait for playback to start with 20 second timeout + if (WaitForPlaying(20000)) + { + m_speed = 1; + m_callback.OnPlayBackSpeedChanged(m_speed); + + // get our initial status. + GetStatus(); + + // restore system volume setting. + SetVolume(g_settings.m_fVolumeLevel); + + // drop CGUIDialogBusy dialog and release the hold in OpenFile. + m_ready.Set(); + + // we are playing but hidden and all stream fields are valid. + // check for video in media content + if (GetVideoStreamCount() > 0) + { + // turn on/off subs + SetSubtitleVisible(g_settings.m_currentVideoSettings.m_SubtitleOn); + SetSubTitleDelay(g_settings.m_currentVideoSettings.m_SubtitleDelay); + + // setup renderer for bypass. This tell renderer to get out of the way as + // hw decoder will be doing the actual video rendering in a video plane + // that is under the GUI layer. + int width = GetPictureWidth(); + int height = GetPictureHeight(); + double fFrameRate = GetActualFPS(); + unsigned int flags = 0; + + flags |= CONF_FLAGS_FULLSCREEN; + CStdString formatstr = "BYPASS"; + CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f. format: %s", + __FUNCTION__, width, height, fFrameRate, formatstr.c_str()); + g_renderManager.IsConfigured(); + if (!g_renderManager.Configure(width, height, width, height, fFrameRate, flags, RENDER_FMT_BYPASS, 0)) + { + CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__); + } + if (!g_renderManager.IsStarted()) + { + CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__); + } + + g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack); + + m_subtitle_thread = new CAMLSubTitleThread(m_dll); + m_subtitle_thread->Create(); + } + + if (m_options.identify == false) + m_callback.OnPlayBackStarted(); + + while (!m_StopPlaying) + { + player_status pstatus = (player_status)GetPlayerSerializedState(); + switch(pstatus) + { + case PLAYER_INITING: + case PLAYER_TYPE_REDY: + case PLAYER_INITOK: + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus)); + // player is parsing file, decoder not running + break; + + default: + case PLAYER_RUNNING: + GetStatus(); + // playback status, decoder is running + break; + + case PLAYER_START: + case PLAYER_BUFFERING: + case PLAYER_PAUSE: + case PLAYER_SEARCHING: + case PLAYER_SEARCHOK: + case PLAYER_FF_END: + case PLAYER_FB_END: + case PLAYER_PLAY_NEXT: + case PLAYER_BUFFER_OK: + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus)); + break; + + case PLAYER_FOUND_SUB: + // found a NEW subtitle in stream. + // TODO: reload m_subtitle_streams + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus)); + break; + + case PLAYER_PLAYEND: + GetStatus(); + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus)); + break; + + case PLAYER_ERROR: + case PLAYER_STOPED: + case PLAYER_EXIT: + if (m_log_level > 5) + { + CLog::Log(LOGDEBUG, "CAMLPlayer::Process PLAYER_STOPED"); + CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus)); + } + m_StopPlaying = true; + break; + } + usleep(250 * 1000); + } + } + } + catch(char* error) + { + CLog::Log(LOGERROR, "%s", error); + } + catch(...) + { + CLog::Log(LOGERROR, "CAMLPlayer::Process Exception thrown"); + } + + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::Process stopped"); + if (m_dll->check_pid_valid(m_pid)) + { + delete m_subtitle_thread; + m_subtitle_thread = NULL; + m_dll->player_stop(m_pid); + m_dll->player_exit(m_pid); + m_pid = -1; + } + + // we are done, hide the mainvideo layer. + ShowMainVideo(false); + m_ready.Set(); + + ClearStreamInfos(); + + // reset ac3/dts passthough + SetAudioPassThrough(AFORMAT_UNKNOWN); + // let thumbgen jobs resume. + CJobManager::GetInstance().UnPause(kJobTypeMediaFlags); + + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::Process exit"); +} +/* +void CAMLPlayer::GetRenderFeatures(Features* renderFeatures) +{ + renderFeatures->push_back(RENDERFEATURE_ZOOM); + renderFeatures->push_back(RENDERFEATURE_CONTRAST); + renderFeatures->push_back(RENDERFEATURE_BRIGHTNESS); + renderFeatures->push_back(RENDERFEATURE_STRETCH); + return; +} + +void CAMLPlayer::GetDeinterlaceMethods(Features* deinterlaceMethods) +{ + deinterlaceMethods->push_back(VS_INTERLACEMETHOD_DEINTERLACE); + return; +} + +void CAMLPlayer::GetDeinterlaceModes(Features* deinterlaceModes) +{ + deinterlaceModes->push_back(VS_DEINTERLACEMODE_AUTO); + return; +} + +void CAMLPlayer::GetScalingMethods(Features* scalingMethods) +{ + return; +} + +void CAMLPlayer::GetAudioCapabilities(Features* audioCaps) +{ + audioCaps->push_back(IPC_AUD_SELECT_STREAM); + return; +} + +void CAMLPlayer::GetSubtitleCapabilities(Features* subCaps) +{ + subCaps->push_back(IPC_SUBS_EXTERNAL); + subCaps->push_back(IPC_SUBS_OFFSET); + subCaps->push_back(IPC_SUBS_SELECT); + return; +} +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +int CAMLPlayer::GetVideoStreamCount() +{ + //CLog::Log(LOGDEBUG, "CAMLPlayer::GetVideoStreamCount(%d)", m_video_count); + return m_video_count; +} + +void CAMLPlayer::ShowMainVideo(bool show) +{ + if (m_show_mainvideo == show) + return; + + set_sysfs_int("/sys/class/video/disable_video", show ? 0:1); + + m_show_mainvideo = show; +} + +void CAMLPlayer::SetVideoZoom(float zoom) +{ + // input zoom range is 0.5 to 2.0 with a default of 1.0. + // output zoom range is 2 to 300 with default of 100. + // we limit that to a range of 50 to 200 with default of 100. + set_sysfs_int("/sys/class/video/zoom", (int)(100 * zoom)); +} + +void CAMLPlayer::SetVideoContrast(int contrast) +{ + // input contrast range is 0 to 100 with default of 50. + // output contrast range is -255 to 255 with default of 0. + contrast = (255 * (contrast - 50)) / 50; + set_sysfs_int("/sys/class/video/contrast", contrast); +} +void CAMLPlayer::SetVideoBrightness(int brightness) +{ + // input brightness range is 0 to 100 with default of 50. + // output brightness range is -127 to 127 with default of 0. + brightness = (127 * (brightness - 50)) / 50; + set_sysfs_int("/sys/class/video/brightness", brightness); +} + +void CAMLPlayer::SetAudioPassThrough(int format) +{ + if (m_audio_passthrough_ac3 && format == AFORMAT_AC3) + set_sysfs_int("/sys/class/audiodsp/digital_raw", 1); + else if (m_audio_passthrough_dts && format == AFORMAT_DTS) + set_sysfs_int("/sys/class/audiodsp/digital_raw", 1); + else + set_sysfs_int("/sys/class/audiodsp/digital_raw", 0); +} + +bool CAMLPlayer::WaitForPausedThumbJobs(int timeout_ms) +{ + // use m_bStop and Sleep so we can get canceled. + while (!m_bStop && (timeout_ms > 0)) + { + if (CJobManager::GetInstance().IsProcessing(kJobTypeMediaFlags) > 0) + { + Sleep(100); + timeout_ms -= 100; + } + else + return true; + } + + return false; +} + +int CAMLPlayer::GetPlayerSerializedState(void) +{ + CSingleLock lock(m_aml_state_csection); + + int playerstate; + int dequeue_size = m_aml_state.size(); + + if (dequeue_size > 0) + { + // serialized state is the front element. + playerstate = m_aml_state.front(); + // pop the front element if there are + // more present. + if (dequeue_size > 1) + m_aml_state.pop_front(); + } + else + { + // if queue is empty (only at startup), + // pull the player state directly. this should + // really never happen but we need to cover it. + playerstate = m_dll->player_get_state(m_pid); + m_aml_state.push_back(playerstate); + } + + return playerstate; +} + + +int CAMLPlayer::UpdatePlayerInfo(int pid, player_info_t *info) +{ + // we get called when status changes or after update time expires. + // static callback from libamplayer, since it does not pass an opaque, + // we have to retreve our player class reference the hard way. + CAMLPlayer *amlplayer = dynamic_cast(g_application.m_pPlayer); + if (amlplayer) + { + CSingleLock lock(amlplayer->m_aml_state_csection); + if (amlplayer->m_aml_state.back() != info->status) + { + //CLog::Log(LOGDEBUG, "update_player_info: %s, old state %s", player_status2str(info->status), player_status2str(info->last_sta)); + amlplayer->m_aml_state.push_back(info->status); + } + } + return 0; +} + +bool CAMLPlayer::CheckPlaying() +{ + return ((player_status)GetPlayerSerializedState() == PLAYER_RUNNING); +} + +bool CAMLPlayer::WaitForStopped(int timeout_ms) +{ + while (!m_StopPlaying && (timeout_ms > 0)) + { + player_status pstatus = (player_status)GetPlayerSerializedState(); + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForStopped: %s", m_dll->player_status2str(pstatus)); + switch(pstatus) + { + default: + usleep(100 * 1000); + timeout_ms -= 100; + break; + case PLAYER_PLAYEND: + case PLAYER_STOPED: + case PLAYER_ERROR: + case PLAYER_EXIT: + m_StopPlaying = true; + return true; + break; + } + } + + return false; +} + +bool CAMLPlayer::WaitForSearchOK(int timeout_ms) +{ + while (!m_StopPlaying && (timeout_ms > 0)) + { + player_status pstatus = (player_status)GetPlayerSerializedState(); + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForSearchOK: %s", m_dll->player_status2str(pstatus)); + switch(pstatus) + { + default: + usleep(100 * 1000); + timeout_ms -= 100; + break; + case PLAYER_STOPED: + return false; + case PLAYER_ERROR: + case PLAYER_EXIT: + m_StopPlaying = true; + return false; + break; + case PLAYER_SEARCHOK: + return true; + break; + } + } + + return false; +} + +bool CAMLPlayer::WaitForPlaying(int timeout_ms) +{ + while (!m_StopPlaying && (timeout_ms > 0)) + { + player_status pstatus = (player_status)GetPlayerSerializedState(); + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForPlaying: %s", m_dll->player_status2str(pstatus)); + switch(pstatus) + { + default: + usleep(100 * 1000); + timeout_ms -= 100; + break; + case PLAYER_ERROR: + case PLAYER_EXIT: + m_StopPlaying = true; + return false; + break; + case PLAYER_RUNNING: + return true; + break; + } + } + + return false; +} + +bool CAMLPlayer::WaitForFormatValid(int timeout_ms) +{ + while (timeout_ms > 0) + { + player_status pstatus = (player_status)GetPlayerSerializedState(); + if (m_log_level > 5) + CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForFormatValid: %s", m_dll->player_status2str(pstatus)); + switch(pstatus) + { + default: + usleep(100 * 1000); + timeout_ms -= 100; + break; + case PLAYER_ERROR: + case PLAYER_EXIT: + m_StopPlaying = true; + return false; + break; + case PLAYER_INITOK: + + ClearStreamInfos(); + + media_info_t media_info; + int res = m_dll->player_get_media_info(m_pid, &media_info); + if (res != PLAYER_SUCCESS) + return false; + + if (m_log_level > 5) + { + media_info_dump(&media_info); + + // m_video_index, m_audio_index, m_subtitle_index might be -1 eventhough + // total_video_xxx is > 0, not sure why, they should be set to zero or + // some other sensible value. + CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForFormatValid: " + "m_video_index(%d), m_audio_index(%d), m_subtitle_index(%d), m_chapter_count(%d)", + media_info.stream_info.cur_video_index, + media_info.stream_info.cur_audio_index, +#if !defined(TARGET_ANDROID) + media_info.stream_info.cur_sub_index, + media_info.stream_info.total_chapter_num); +#else + media_info.stream_info.cur_sub_index, + 0); +#endif + } + + // video info + if (media_info.stream_info.has_video && media_info.stream_info.total_video_num > 0) + { + for (int i = 0; i < media_info.stream_info.total_video_num; i++) + { + AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo; + info->Clear(); + + info->id = media_info.video_info[i]->id; + info->type = STREAM_VIDEO; + info->width = media_info.video_info[i]->width; + info->height = media_info.video_info[i]->height; + info->frame_rate_num = media_info.video_info[i]->frame_rate_num; + info->frame_rate_den = media_info.video_info[i]->frame_rate_den; + info->bit_rate = media_info.video_info[i]->bit_rate; + info->duration = media_info.video_info[i]->duartion; + info->format = media_info.video_info[i]->format; + + m_video_streams.push_back(info); + } + + m_video_index = media_info.stream_info.cur_video_index; + m_video_count = media_info.stream_info.total_video_num; + if (m_video_index != 0) + m_video_index = 0; + m_video_width = media_info.video_info[m_video_index]->width; + m_video_height= media_info.video_info[m_video_index]->height; + m_video_fps_numerator = media_info.video_info[m_video_index]->frame_rate_num; + m_video_fps_denominator = media_info.video_info[m_video_index]->frame_rate_den; + + // bail if we do not get a valid width/height + if (m_video_width == 0 || m_video_height == 0) + return false; + } + + // audio info + if (media_info.stream_info.has_audio && media_info.stream_info.total_audio_num > 0) + { + for (int i = 0; i < media_info.stream_info.total_audio_num; i++) + { + AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo; + info->Clear(); + + info->id = media_info.audio_info[i]->id; + info->type = STREAM_AUDIO; + info->channel = media_info.audio_info[i]->channel; + info->sample_rate = media_info.audio_info[i]->sample_rate; + info->bit_rate = media_info.audio_info[i]->bit_rate; + info->duration = media_info.audio_info[i]->duration; + info->format = media_info.audio_info[i]->aformat; +#if !defined(TARGET_ANDROID) + if (media_info.audio_info[i]->audio_language[0] != 0) + info->language = std::string(media_info.audio_info[i]->audio_language, 3); +#endif + m_audio_streams.push_back(info); + } + + m_audio_index = media_info.stream_info.cur_audio_index; + if (m_audio_index != 0) + m_audio_index = 0; + m_audio_count = media_info.stream_info.total_audio_num; + // setup ac3/dts passthough if required + SetAudioPassThrough(m_audio_streams[m_audio_index]->format); + } + + // subtitle info + if (media_info.stream_info.has_sub && media_info.stream_info.total_sub_num > 0) + { + for (int i = 0; i < media_info.stream_info.total_sub_num; i++) + { + AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo; + info->Clear(); + + info->id = media_info.sub_info[i]->id; + info->type = STREAM_SUBTITLE; + if (media_info.sub_info[i]->sub_language && media_info.sub_info[i]->sub_language[0] != 0) + info->language = std::string(media_info.sub_info[i]->sub_language, 3); + m_subtitle_streams.push_back(info); + } + m_subtitle_index = media_info.stream_info.cur_sub_index; + } + // find any external subs + FindSubtitleFiles(); + // setup count and index + m_subtitle_count = m_subtitle_streams.size(); + if (m_subtitle_count && m_subtitle_index != 0) + m_subtitle_index = 0; + +#if !defined(TARGET_ANDROID) + // chapter info + if (media_info.stream_info.total_chapter_num > 0) + { + m_chapter_count = media_info.stream_info.total_chapter_num; + for (int i = 0; i < m_chapter_count; i++) + { + if (media_info.chapter_info[i] != NULL) + { + AMLChapterInfo *info = new AMLChapterInfo; + + info->name = media_info.chapter_info[i]->name; + info->seekto_ms = media_info.chapter_info[i]->seekto_ms; + m_chapters.push_back(info); + } + } + } +#endif + return true; + break; + } + } + + return false; +} + +void CAMLPlayer::ClearStreamInfos() +{ + CSingleLock lock(m_aml_csection); + + if (!m_audio_streams.empty()) + { + for (unsigned int i = 0; i < m_audio_streams.size(); i++) + delete m_audio_streams[i]; + m_audio_streams.clear(); + } + m_audio_count = 0; + m_audio_index = -1; + + if (!m_video_streams.empty()) + { + for (unsigned int i = 0; i < m_video_streams.size(); i++) + delete m_video_streams[i]; + m_video_streams.clear(); + } + m_video_count = 0; + m_video_index = -1; + + if (!m_subtitle_streams.empty()) + { + for (unsigned int i = 0; i < m_subtitle_streams.size(); i++) + delete m_subtitle_streams[i]; + m_subtitle_streams.clear(); + } + m_subtitle_count = 0; + m_subtitle_index = -1; + + if (!m_chapters.empty()) + { + for (unsigned int i = 0; i < m_chapters.size(); i++) + delete m_chapters[i]; + m_chapters.clear(); + } + m_chapter_count = 0; +} + +bool CAMLPlayer::GetStatus() +{ + CSingleLock lock(m_aml_csection); + + if (!m_dll->check_pid_valid(m_pid)) + return false; + + player_info_t player_info; + int res = m_dll->player_get_play_info(m_pid, &player_info); + if (res != PLAYER_SUCCESS) + return false; + + m_elapsed_ms = player_info.current_ms; + m_duration_ms = 1000 * player_info.full_time; + //CLog::Log(LOGDEBUG, "CAMLPlayer::GetStatus: audio_bufferlevel(%f), video_bufferlevel(%f), bufed_time(%d), bufed_pos(%lld)", + // player_info.audio_bufferlevel, player_info.video_bufferlevel, player_info.bufed_time, player_info.bufed_pos); + + return true; +} + +void CAMLPlayer::FindSubtitleFiles() +{ + // find any available external subtitles + std::vector filenames; + CUtil::ScanForExternalSubtitles(m_item.GetPath(), filenames); + + // find any upnp subtitles + CStdString key("upnp:subtitle:1"); + for(unsigned s = 1; m_item.HasProperty(key); key.Format("upnp:subtitle:%u", ++s)) + filenames.push_back(m_item.GetProperty(key).asString()); + + for(unsigned int i=0;iClear(); + + info->id = 0; + info->type = STREAM_SUBTITLE; + info->source = STREAM_SOURCE_TEXT; + info->filename = filename; + info->name = URIUtils::GetFileName(filename); + info->frame_rate_num = m_video_fps_numerator; + info->frame_rate_den = m_video_fps_denominator; + m_subtitle_streams.push_back(info); + + return m_subtitle_streams.size(); +} + +bool CAMLPlayer::OpenSubtitleStream(int index) +{ + CLog::Log(LOGNOTICE, "Opening external subtitle stream: %i", index); + + CDemuxStream* pStream = NULL; + std::string filename; + CDVDStreamInfo hint; + + if (m_subtitle_streams[index]->source == STREAM_SOURCE_DEMUX_SUB) + { + /* + int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream); + if(index < 0) + return false; + SelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index); + + if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename) + { + CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str()); + auto_ptr demux(new CDVDDemuxVobsub()); + if(!demux->Open(st.filename, st.filename2)) + return false; + m_pSubtitleDemuxer = demux.release(); + } + + pStream = m_pSubtitleDemuxer->GetStream(iStream); + if(!pStream || pStream->disabled) + return false; + pStream->SetDiscard(AVDISCARD_NONE); + double pts = m_dvdPlayerVideo.GetCurrentPts(); + if(pts == DVD_NOPTS_VALUE) + pts = m_CurrentVideo.dts; + if(pts == DVD_NOPTS_VALUE) + pts = 0; + pts += m_offset_pts; + m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE)); + + hint.Assign(*pStream, true); + */ + return false; + } + else if (m_subtitle_streams[index]->source == STREAM_SOURCE_TEXT) + { + filename = m_subtitle_streams[index]->filename; + + hint.Clear(); + hint.fpsscale = m_subtitle_streams[index]->frame_rate_den; + hint.fpsrate = m_subtitle_streams[index]->frame_rate_num; + } + + m_dvdPlayerSubtitle->CloseStream(true); + if (!m_dvdPlayerSubtitle->OpenStream(hint, filename)) + { + CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, index); + if(pStream) + { + pStream->disabled = true; + pStream->SetDiscard(AVDISCARD_ALL); + } + return false; + } + + return true; +} + +void CAMLPlayer::SetVideoRect(const CRect &SrcRect, const CRect &DestRect) +{ + // this routine gets called every video frame + // and is in the context of the renderer thread so + // do not do anything stupid here. + + // video zoom adjustment. + float zoom = g_settings.m_currentVideoSettings.m_CustomZoomAmount; + if ((int)(zoom * 1000) != (int)(m_zoom * 1000)) + { + SetVideoZoom(zoom); + m_zoom = zoom; + } + // video contrast adjustment. + int contrast = g_settings.m_currentVideoSettings.m_Contrast; + if (contrast != m_contrast) + { + SetVideoContrast(contrast); + m_contrast = contrast; + } + // video brightness adjustment. + int brightness = g_settings.m_currentVideoSettings.m_Brightness; + if (brightness != m_brightness) + { + SetVideoBrightness(brightness); + m_brightness = brightness; + } + + // check if destination rect or video view mode has changed + if ((m_dst_rect != DestRect) || (m_view_mode != g_settings.m_currentVideoSettings.m_ViewMode)) + { + m_dst_rect = DestRect; + m_view_mode = g_settings.m_currentVideoSettings.m_ViewMode; + } + else + { + // mainvideo 'should' be showing already if we get here, make sure. + ShowMainVideo(true); + return; + } + + CRect gui, display, dst_rect; + RESOLUTION res = g_graphicsContext.GetVideoResolution(); + gui.SetRect(0, 0, g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight); + // when display is at 1080p, we have freescale enabled + // and that scales all layers into 1080p display including video, + // so we have to setup video axis for 720p instead of 1080p... Boooo. + display.SetRect(0, 0, g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight); + //display.SetRect(0, 0, g_settings.m_ResInfo[res].iScreenWidth, g_settings.m_ResInfo[res].iScreenHeight); + dst_rect = m_dst_rect; + if (gui != display) + { + float xscale = display.Width() / gui.Width(); + float yscale = display.Height() / gui.Height(); + dst_rect.x1 *= xscale; + dst_rect.x2 *= xscale; + dst_rect.y1 *= yscale; + dst_rect.y2 *= yscale; + } + // destination rectangle cannot be outside display bounds + if (!display.PtInRect(CPoint(dst_rect.x1, dst_rect.y1))) + return; + if (!display.PtInRect(CPoint(dst_rect.x2, dst_rect.y2))) + return; + + ShowMainVideo(false); + + char video_axis[256] = {0}; + sprintf(video_axis, "%d %d %d %d", (int)dst_rect.x1, (int)dst_rect.y1, (int)dst_rect.x2, (int)dst_rect.y2); + set_sysfs_str("/sys/class/video/axis", video_axis); + + CStdString rectangle; + rectangle.Format("%i,%i,%i,%i", + (int)dst_rect.x1, (int)dst_rect.y1, + (int)dst_rect.Width(), (int)dst_rect.Height()); + CLog::Log(LOGDEBUG, "CAMLPlayer::SetVideoRect:dst_rect(%s)", rectangle.c_str()); + + // we only get called once gui has changed to something + // that would show video playback, so show it. + ShowMainVideo(true); +} + +void CAMLPlayer::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect) +{ + CAMLPlayer *player = (CAMLPlayer*)ctx; + player->SetVideoRect(SrcRect, DestRect); +} diff --git a/xbmc/cores/amlplayer/AMLPlayer.h b/xbmc/cores/amlplayer/AMLPlayer.h new file mode 100644 index 0000000..9ffaeac --- /dev/null +++ b/xbmc/cores/amlplayer/AMLPlayer.h @@ -0,0 +1,256 @@ +#pragma once +/* + * Copyright (C) 2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "FileItem.h" +#include "cores/IPlayer.h" +#include "dialogs/GUIDialogBusy.h" +#include "threads/Thread.h" + +typedef struct AMLChapterInfo AMLChapterInfo; +typedef struct AMLPlayerStreamInfo AMLPlayerStreamInfo; +typedef struct player_info player_info_t; + +struct AMLSubtitle +{ + int64_t bgntime; + int64_t endtime; + CStdString string; +}; + +class DllLibAmplayer; + +class CAMLSubTitleThread : public CThread +{ +public: + CAMLSubTitleThread(DllLibAmplayer* dll); + virtual ~CAMLSubTitleThread(); + + void UpdateSubtitle(CStdString &subtitle, int64_t elapsed_ms); +protected: + virtual void Process(void); + + DllLibAmplayer *m_dll; + int m_subtitle_codec; + std::deque m_subtitle_strings; + CCriticalSection m_subtitle_csection; +}; + +class CDVDPlayerSubtitle; +class CDVDOverlayContainer; + +class CAMLPlayer : public IPlayer, public CThread +{ +public: + + CAMLPlayer(IPlayerCallback &callback); + virtual ~CAMLPlayer(); + + virtual void RegisterAudioCallback(IAudioCallback* pCallback) {} + virtual void UnRegisterAudioCallback() {} + virtual bool OpenFile(const CFileItem &file, const CPlayerOptions &options); + virtual bool QueueNextFile(const CFileItem &file) {return false;} + virtual void OnNothingToQueueNotify() {} + virtual bool CloseFile(); + virtual bool IsPlaying() const; + virtual void Pause(); + virtual bool IsPaused() const; + virtual bool HasVideo() const; + virtual bool HasAudio() const; + virtual void ToggleFrameDrop(); + virtual bool CanSeek(); + virtual void Seek(bool bPlus = true, bool bLargeStep = false); + virtual bool SeekScene(bool bPlus = true); + virtual void SeekPercentage(float fPercent = 0.0f); + virtual float GetPercentage(); + virtual void SetVolume(float volume); + virtual void SetDynamicRangeCompression(long drc) {} + virtual void GetAudioInfo(CStdString &strAudioInfo); + virtual void GetVideoInfo(CStdString &strVideoInfo); + virtual void GetGeneralInfo(CStdString &strVideoInfo) {}; + virtual void Update(bool bPauseDrawing); + virtual void GetVideoRect(CRect& SrcRect, CRect& DestRect); + virtual void GetVideoAspectRatio(float &fAR); + virtual bool CanRecord() {return false;}; + virtual bool IsRecording() {return false;}; + virtual bool Record(bool bOnOff) {return false;}; + + virtual void SetAVDelay(float fValue = 0.0f) {return;} + virtual float GetAVDelay() {return 0.0f;}; + + virtual void SetSubTitleDelay(float fValue); + virtual float GetSubTitleDelay(); + virtual int GetSubtitleCount(); + virtual int GetSubtitle(); + virtual void GetSubtitleName(int iStream, CStdString &strStreamName); + virtual void SetSubtitle(int iStream); + virtual bool GetSubtitleVisible(); + virtual void SetSubtitleVisible(bool bVisible); + virtual bool GetSubtitleExtension(CStdString &strSubtitleExtension) { return false; } + virtual int AddSubtitle(const CStdString& strSubPath); + + virtual int GetAudioStreamCount(); + virtual int GetAudioStream(); + virtual void GetAudioStreamName(int iStream, CStdString &strStreamName); + virtual void SetAudioStream(int iStream); + virtual void GetAudioStreamLanguage(int iStream, CStdString &strLanguage) {}; + + virtual TextCacheStruct_t* GetTeletextCache() {return NULL;}; + virtual void LoadPage(int p, int sp, unsigned char* buffer) {}; + + virtual int GetChapterCount(); + virtual int GetChapter(); + virtual void GetChapterName(CStdString& strChapterName); + virtual int SeekChapter(int iChapter); + + virtual float GetActualFPS(); + virtual void SeekTime(__int64 iTime = 0); + virtual __int64 GetTime(); + virtual int GetTotalTime(); + virtual int GetAudioBitrate(); + virtual int GetVideoBitrate(); + virtual int GetSourceBitrate(); + virtual int GetChannels(); + virtual int GetBitsPerSample(); + virtual int GetSampleRate(); + virtual CStdString GetAudioCodecName(); + virtual CStdString GetVideoCodecName(); + virtual int GetPictureWidth(); + virtual int GetPictureHeight(); + virtual bool GetStreamDetails(CStreamDetails &details); + virtual void ToFFRW(int iSpeed = 0); + // Skip to next track/item inside the current media (if supported). + virtual bool SkipNext() {return false;} + + //Returns true if not playback (paused or stopped beeing filled) + virtual bool IsCaching() const {return false;}; + //Cache filled in Percent + virtual int GetCacheLevel() const {return -1;}; + + virtual bool IsInMenu() const {return false;}; + virtual bool HasMenu() {return false;}; + + virtual void DoAudioWork() {}; + virtual bool OnAction(const CAction &action) {return false;}; + + virtual bool GetCurrentSubtitle(CStdString& strSubtitle); + //returns a state that is needed for resuming from a specific time + virtual CStdString GetPlayerState() {return "";}; + virtual bool SetPlayerState(CStdString state) {return false;}; + + virtual CStdString GetPlayingTitle() {return "";}; +/* + virtual void GetRenderFeatures(Features* renderFeatures); + virtual void GetDeinterlaceMethods(Features* deinterlaceMethods); + virtual void GetDeinterlaceModes(Features* deinterlaceModes); + virtual void GetScalingMethods(Features* scalingMethods); + virtual void GetAudioCapabilities(Features* audioCaps); + virtual void GetSubtitleCapabilities(Features* subCaps); +*/ + +protected: + virtual void OnStartup(); + virtual void OnExit(); + virtual void Process(); + +private: + int GetVideoStreamCount(); + void ShowMainVideo(bool show); + void SetVideoZoom(float zoom); + void SetVideoContrast(int contrast); + void SetVideoBrightness(int brightness); + void SetAudioPassThrough(int format); + bool WaitForPausedThumbJobs(int timeout_ms); + + int GetPlayerSerializedState(void); + static int UpdatePlayerInfo(int pid, player_info_t *info); + bool CheckPlaying(); + bool WaitForStopped(int timeout_ms); + bool WaitForSearchOK(int timeout_ms); + bool WaitForPlaying(int timeout_ms); + bool WaitForOpenMedia(int timeout_ms); + bool WaitForFormatValid(int timeout_ms); + void ClearStreamInfos(); + bool GetStatus(); + + void FindSubtitleFiles(); + int AddSubtitleFile(const std::string& filename, const std::string& subfilename = ""); + bool OpenSubtitleStream(int index); + void SetVideoRect(const CRect &SrcRect, const CRect &DestRect); + static void RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect); + + DllLibAmplayer *m_dll; + int m_cpu; + int m_speed; + bool m_paused; + bool m_StopPlaying; + CEvent m_ready; + CFileItem m_item; + CPlayerOptions m_options; + int m_log_level; + + int64_t m_elapsed_ms; + int64_t m_duration_ms; + + int m_audio_index; + int m_audio_count; + CStdString m_audio_info; + int m_audio_delay; + bool m_audio_passthrough_ac3; + bool m_audio_passthrough_dts; + + int m_video_index; + int m_video_count; + CStdString m_video_info; + int m_video_width; + int m_video_height; + float m_video_fps_numerator; + float m_video_fps_denominator; + + int m_subtitle_index; + int m_subtitle_count; + bool m_subtitle_show; + int m_subtitle_delay; + CAMLSubTitleThread *m_subtitle_thread; + CDVDPlayerSubtitle *m_dvdPlayerSubtitle; + CDVDOverlayContainer *m_dvdOverlayContainer; + + int m_chapter_index; + int m_chapter_count; + + int m_show_mainvideo; + CRect m_dst_rect; + int m_view_mode; + float m_zoom; + int m_contrast; + int m_brightness; + + CCriticalSection m_aml_csection; + CCriticalSection m_aml_state_csection; + std::deque m_aml_state; + int m_pid; + + std::vector m_video_streams; + std::vector m_audio_streams; + std::vector m_subtitle_streams; + std::vector m_chapters; + +}; diff --git a/xbmc/cores/amlplayer/DllLibamplayer.h b/xbmc/cores/amlplayer/DllLibamplayer.h new file mode 100644 index 0000000..ea78a7b --- /dev/null +++ b/xbmc/cores/amlplayer/DllLibamplayer.h @@ -0,0 +1,163 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#include "DynamicDll.h" + +extern "C" +{ +#include +#include +} + +struct AML_URLProtocol; + +class DllLibAmplayerInterface +{ +public: + virtual ~DllLibAmplayerInterface() {}; + + virtual int player_init(void)=0; + virtual int player_start(play_control_t *p, unsigned long priv)=0; + virtual int player_stop(int pid)=0; + virtual int player_stop_async(int pid)=0; + virtual int player_exit(int pid)=0; + virtual int player_pause(int pid)=0; + virtual int player_resume(int pid)=0; + virtual int player_timesearch(int pid, float s_time)=0; + virtual int player_forward(int pid, int speed)=0; + virtual int player_backward(int pid, int speed)=0; + virtual int player_aid(int pid, int audio_id)=0; + virtual int player_sid(int pid, int sub_id)=0; + virtual int player_progress_exit(void)=0; + virtual int player_list_allpid(pid_info_t *pid)=0; + virtual int check_pid_valid(int pid)=0; + virtual int player_get_play_info(int pid, player_info_t *info)=0; + virtual int player_get_media_info(int pid, media_info_t *minfo)=0; + virtual int player_video_overlay_en(unsigned enable)=0; + virtual int player_start_play(int pid)=0; + virtual player_status player_get_state(int pid)=0; + virtual unsigned int player_get_extern_priv(int pid)=0; + virtual int player_enable_autobuffer(int pid, int enable)=0; + virtual int player_set_autobuffer_level(int pid, float min, float middle, float max)=0; + virtual int player_register_update_callback(callback_t *cb,update_state_fun_t up_fn,int interval_s)=0; + virtual char* player_status2str(player_status status)=0; + + virtual int audio_set_volume(int pid,float val)=0; + + virtual int codec_open_sub_read(void)=0; + virtual int codec_close_sub_fd(int sub_fd)=0; + virtual int codec_get_sub_size_fd(int sub_fd)=0; + virtual int codec_read_sub_data_fd(int sub_fd, char* buf, unsigned int length)=0; + +#if defined(TARGET_ANDROID) + // on android, libamplayer.so has ffmpeg built in and + // we need to use av_register_protocol2 from there. + virtual int av_register_protocol2(AML_URLProtocol *protocol, int size)=0; +#else + virtual int av_register_protocol2(AML_URLProtocol *protocol, int size) + { return ::av_register_protocol2(protocol, size);}; +#endif +}; + +class DllLibAmplayer : public DllDynamic, DllLibAmplayerInterface +{ + DECLARE_DLL_WRAPPER(DllLibAmplayer, "libamplayer.so") + + DEFINE_METHOD0(int, player_init) + DEFINE_METHOD2(int, player_start, (play_control_t *p1, unsigned long p2)) + DEFINE_METHOD1(int, player_stop, (int p1)) + DEFINE_METHOD1(int, player_stop_async, (int p1)) + DEFINE_METHOD1(int, player_exit, (int p1)) + DEFINE_METHOD1(int, player_pause, (int p1)) + DEFINE_METHOD1(int, player_resume, (int p1)) + DEFINE_METHOD2(int, player_timesearch, (int p1, float p2)) + DEFINE_METHOD2(int, player_forward, (int p1, int p2)) + DEFINE_METHOD2(int, player_backward, (int p1, int p2)) + DEFINE_METHOD2(int, player_aid, (int p1, int p2)) + DEFINE_METHOD2(int, player_sid, (int p1, int p2)) + DEFINE_METHOD0(int, player_progress_exit) + DEFINE_METHOD1(int, player_list_allpid, (pid_info_t *p1)) + DEFINE_METHOD1(int, check_pid_valid, (int p1)) + DEFINE_METHOD2(int, player_get_play_info, (int p1, player_info_t *p2)) + DEFINE_METHOD2(int, player_get_media_info, (int p1, media_info_t *p2)) + DEFINE_METHOD1(int, player_video_overlay_en, (unsigned int p1)) + DEFINE_METHOD1(int, player_start_play, (int p1)) + DEFINE_METHOD1(player_status, player_get_state, (int p1)) + DEFINE_METHOD1(unsigned int, player_get_extern_priv,(int p1)) + DEFINE_METHOD2(int, player_enable_autobuffer, (int p1, int p2)) + DEFINE_METHOD4(int, player_set_autobuffer_level, (int p1, float p2, float p3, float p4)) + DEFINE_METHOD3(int, player_register_update_callback, (callback_t *p1, update_state_fun_t p2, int p3)) + DEFINE_METHOD1(char*, player_status2str, (player_status p1)) + + DEFINE_METHOD2(int, audio_set_volume, (int p1, float p2)) + + DEFINE_METHOD0(int, codec_open_sub_read) + DEFINE_METHOD1(int, codec_close_sub_fd, (int p1)) + DEFINE_METHOD1(int, codec_get_sub_size_fd, (int p1)) + DEFINE_METHOD3(int, codec_read_sub_data_fd,(int p1, char *p2, unsigned int p3)) + +#if defined(TARGET_ANDROID) + DEFINE_METHOD2(int, av_register_protocol2, (AML_URLProtocol *p1, int p2)) +#endif + + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(player_init) + RESOLVE_METHOD(player_start) + RESOLVE_METHOD(player_stop) + RESOLVE_METHOD(player_stop_async) + RESOLVE_METHOD(player_exit) + RESOLVE_METHOD(player_pause) + RESOLVE_METHOD(player_resume) + RESOLVE_METHOD(player_timesearch) + RESOLVE_METHOD(player_forward) + RESOLVE_METHOD(player_backward) + RESOLVE_METHOD(player_aid) + RESOLVE_METHOD(player_sid) + RESOLVE_METHOD(player_progress_exit) + RESOLVE_METHOD(player_list_allpid) + RESOLVE_METHOD(check_pid_valid) + RESOLVE_METHOD(player_get_play_info) + RESOLVE_METHOD(player_get_media_info) + RESOLVE_METHOD(player_video_overlay_en) + RESOLVE_METHOD(player_start_play) + RESOLVE_METHOD(player_get_state) + RESOLVE_METHOD(player_get_extern_priv) + RESOLVE_METHOD(player_enable_autobuffer) + RESOLVE_METHOD(player_set_autobuffer_level) + RESOLVE_METHOD(player_register_update_callback) + RESOLVE_METHOD(player_status2str) + + RESOLVE_METHOD(audio_set_volume) + + RESOLVE_METHOD(codec_open_sub_read) + RESOLVE_METHOD(codec_close_sub_fd) + RESOLVE_METHOD(codec_get_sub_size_fd) + RESOLVE_METHOD(codec_read_sub_data_fd) + +#if defined(TARGET_ANDROID) + RESOLVE_METHOD(av_register_protocol2) +#endif + + END_METHOD_RESOLVE() +}; diff --git a/xbmc/cores/amlplayer/FileURLProtocol.cpp b/xbmc/cores/amlplayer/FileURLProtocol.cpp new file mode 100644 index 0000000..a670621 --- /dev/null +++ b/xbmc/cores/amlplayer/FileURLProtocol.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2011-2012 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "FileURLProtocol.h" +#include "filesystem/File.h" +#include "utils/log.h" +#include "FileItem.h" + +#define URL_RDONLY 1 /**< read-only */ +#define URL_WRONLY 2 /**< write-only */ +#define URL_RDWR (URL_RDONLY|URL_WRONLY) /**< read-write */ + +#define AVSEEK_SIZE 0x10000 +#define AVSEEK_FORCE 0x20000 + +//======================================================================== +int CFileURLProtocol::Open(AML_URLContext *h, const char *filename, int flags) +{ + if (flags != URL_RDONLY) + { + CLog::Log(LOGDEBUG, "CFileURLProtocol::Open: Only read-only is supported"); + return -EINVAL; + } + + CStdString url = filename; + if (url.Left(strlen("xb-http://")).Equals("xb-http://")) + { + url = url.Right(url.size() - strlen("xb-")); + } + else if (url.Left(strlen("xb-https://")).Equals("xb-https://")) + { + url = url.Right(url.size() - strlen("xb-")); + } + else if (url.Left(strlen("xb-ftp://")).Equals("xb-ftp://")) + { + url = url.Right(url.size() - strlen("xb-")); + } + else if (url.Left(strlen("xb-ftps://")).Equals("xb-ftps://")) + { + url = url.Right(url.size() - strlen("xb-")); + } + + CLog::Log(LOGDEBUG, "CFileURLProtocol::Open filename2(%s)", url.c_str()); + // open the file, always in read mode, calc bitrate + unsigned int cflags = READ_BITRATE; + XFILE::CFile *cfile = new XFILE::CFile(); + + if (CFileItem(url, true).IsInternetStream()) + cflags |= READ_CACHED; + + // open file in binary mode + if (!cfile->Open(url, cflags)) + { + delete cfile; + return -EIO; + } + + h->priv_data = (void *)cfile; + + return 0; +} + +//======================================================================== +int CFileURLProtocol::Read(AML_URLContext *h, unsigned char *buf, int size) +{ + XFILE::CFile *cfile = (XFILE::CFile*)h->priv_data; + + int readsize = cfile->Read(buf, size); + //CLog::Log(LOGDEBUG, "CFileURLProtocol::Read size(%d), readsize(%d)", size, readsize); + + return readsize; +} + +//======================================================================== +int CFileURLProtocol::Write(AML_URLContext *h, unsigned char *buf, int size) +{ + //CLog::Log(LOGDEBUG, "CFileURLProtocol::Write size(%d)", size); + return 0; +} + +//======================================================================== +int64_t CFileURLProtocol::Seek(AML_URLContext *h, int64_t pos, int whence) +{ + //CLog::Log(LOGDEBUG, "CFileURLProtocol::Seek1 pos(%lld), whence(%d)", pos, whence); + XFILE::CFile *cfile = (XFILE::CFile*)h->priv_data; + whence &= ~AVSEEK_FORCE; + + // seek to the end of file + if (pos == -1 || whence == AVSEEK_SIZE) + pos = cfile->GetLength(); + else + pos = cfile->Seek(pos, whence); + + //CLog::Log(LOGDEBUG, "CFileURLProtocol::Seek2 pos(%lld), whence(%d)", pos, whence); + + return pos; +} + +//======================================================================== +int64_t CFileURLProtocol::SeekEx(AML_URLContext *h, int64_t pos, int whence) +{ + //CLog::Log(LOGDEBUG, "CFileURLProtocol::SeekEx1 pos(%lld), whence(%d)", pos, whence); + XFILE::CFile *cfile = (XFILE::CFile*)h->priv_data; + whence &= ~AVSEEK_FORCE; + + // seek to the end of file + if (pos == -1 || whence == AVSEEK_SIZE) + pos = cfile->GetLength(); + else + pos = cfile->Seek(pos, whence); + + //CLog::Log(LOGDEBUG, "CFileURLProtocol::SeekEx2 pos(%lld), whence(%d)", pos, whence); + + return pos; +} + +//======================================================================== +int CFileURLProtocol::Close(AML_URLContext *h) +{ + CLog::Log(LOGDEBUG, "CFileURLProtocol::Close"); + XFILE::CFile *cfile = (XFILE::CFile*)h->priv_data; + cfile->Close(); + delete cfile; + + return 0; +} diff --git a/xbmc/cores/amlplayer/FileURLProtocol.h b/xbmc/cores/amlplayer/FileURLProtocol.h new file mode 100644 index 0000000..c39f4c3 --- /dev/null +++ b/xbmc/cores/amlplayer/FileURLProtocol.h @@ -0,0 +1,81 @@ +#pragma once +/* + * Copyright (C) 2011-2012 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include +#include + +// this is an icky hack, we need to use amffmpeg structures but we cannot +// include them or risk colliding with our internal ffmpeg defs. The only +// this we use is URLProtocol to setup callbacks into uur vfs, so we fake +// a renamed URLContext/URLProtocol. The only thing we need to worry about +// is keeping the alignment to what is in amffmpeg, so we can forward +// declare all pointers to stucts as we never ref them and the compiler is happy. + +struct AVClass; +struct url_lpbuf; + +typedef struct AML_URLContext { + const AVClass *av_class; + struct AML_URLProtocol *prot; + struct url_lpbuf *lpbuf; + int flags; + int is_streamed; + int max_packet_size; + void *priv_data; + char *filename; + char *headers; + int is_connected; + int is_slowmedia; + int fastdetectedinfo; + int support_time_seek; + char *location; +} AML_URLContext; + +typedef struct AML_URLProtocol { + const char *name; + int (*url_open)(AML_URLContext *h, const char *url, int flags); + int (*url_read)(AML_URLContext *h, unsigned char *buf, int size); + int (*url_write)(AML_URLContext *h, unsigned char *buf, int size); + int64_t (*url_seek)(AML_URLContext *h, int64_t pos, int whence); + int64_t (*url_exseek)(AML_URLContext *h, int64_t pos, int whence); + int (*url_close)(AML_URLContext *h); + struct AML_URLContext *next; + int (*url_read_pause)(AML_URLContext *h, int pause); + int64_t (*url_read_seek)(AML_URLContext *h, int stream_index, int64_t timestamp, int flags); + int (*url_get_file_handle)(AML_URLContext *h); + int priv_data_size; + const AVClass *priv_data_class; + int flags; + int (*url_check)(AML_URLContext *h, int mask); +} AML_URLProtocol; + +class CFileURLProtocol +{ + +public: + static int Open (AML_URLContext *h, const char *filename, int flags); + static int Read (AML_URLContext *h, unsigned char *buf, int size); + static int Write(AML_URLContext *h, unsigned char *buf, int size); + static int64_t Seek(AML_URLContext *h, int64_t pos, int whence); + static int64_t SeekEx(AML_URLContext *h, int64_t pos, int whence); + static int Close(AML_URLContext *h); +}; diff --git a/xbmc/cores/amlplayer/Makefile.in b/xbmc/cores/amlplayer/Makefile.in new file mode 100644 index 0000000..430f2dd --- /dev/null +++ b/xbmc/cores/amlplayer/Makefile.in @@ -0,0 +1,15 @@ +.PHONY: compile + +INCLUDES += -I$(prefix)/include/amlplayer + +SRCS = AMLPlayer.cpp +SRCS+= FileURLProtocol.cpp + +LIB = amlplayer.a + +@abs_top_srcdir@/system/advancedsettings.xml: $(LIB) + cp -f amlplayer_advancedsettings.xml $@ + +include @abs_top_srcdir@/Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) + diff --git a/xbmc/cores/amlplayer/amlplayer_advancedsettings.xml b/xbmc/cores/amlplayer/amlplayer_advancedsettings.xml new file mode 100644 index 0000000..7199cd4 --- /dev/null +++ b/xbmc/cores/amlplayer/amlplayer_advancedsettings.xml @@ -0,0 +1,6 @@ + + + diff --git a/xbmc/cores/playercorefactory/PlayerCoreConfig.h b/xbmc/cores/playercorefactory/PlayerCoreConfig.h index cd78a82..486c09f 100644 --- a/xbmc/cores/playercorefactory/PlayerCoreConfig.h +++ b/xbmc/cores/playercorefactory/PlayerCoreConfig.h @@ -25,6 +25,9 @@ #include "PlayerCoreFactory.h" #include "cores/dvdplayer/DVDPlayer.h" #include "cores/paplayer/PAPlayer.h" +#if defined(HAS_AMLPLAYER) +#include "cores/amlplayer/AMLPlayer.h" +#endif #include "cores/ExternalPlayer/ExternalPlayer.h" #include "utils/log.h" @@ -74,6 +77,9 @@ public: case EPC_DVDPLAYER: pPlayer = new CDVDPlayer(callback); break; case EPC_PAPLAYER: pPlayer = new PAPlayer(callback); break; case EPC_EXTPLAYER: pPlayer = new CExternalPlayer(callback); break; +#if defined(HAS_AMLPLAYER) + case EPC_AMLPLAYER: pPlayer = new CAMLPlayer(callback); break; +#endif default: return NULL; } diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp index 3aaf546..9a4506b 100644 --- a/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp +++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp @@ -280,6 +280,13 @@ bool CPlayerCoreFactory::LoadConfiguration(TiXmlElement* pConfig, bool clear) paplayer->m_bPlaysAudio = true; s_vecCoreConfigs.push_back(paplayer); +#if defined(HAS_AMLPLAYER) + CPlayerCoreConfig* amlplayer = new CPlayerCoreConfig("AMLPlayer", EPC_AMLPLAYER, NULL); + amlplayer->m_bPlaysAudio = true; + amlplayer->m_bPlaysVideo = true; + s_vecCoreConfigs.push_back(amlplayer); +#endif + for(std::vector::iterator it = s_vecCoreSelectionRules.begin(); it != s_vecCoreSelectionRules.end(); it++) delete *it; s_vecCoreSelectionRules.clear(); diff --git a/xbmc/cores/playercorefactory/PlayerCoreFactory.h b/xbmc/cores/playercorefactory/PlayerCoreFactory.h index 04ac1dc..2762629 100644 --- a/xbmc/cores/playercorefactory/PlayerCoreFactory.h +++ b/xbmc/cores/playercorefactory/PlayerCoreFactory.h @@ -37,15 +37,21 @@ enum EPLAYERCORES EPC_DVDPLAYER, EPC_MPLAYER, EPC_PAPLAYER, +#if defined(HAS_AMLPLAYER) + EPC_AMLPLAYER, +#endif EPC_EXTPLAYER }; typedef unsigned int PLAYERCOREID; typedef std::vector VECPLAYERCORES; -const PLAYERCOREID PCID_NONE = 0; -const PLAYERCOREID PCID_DVDPLAYER = 1; -const PLAYERCOREID PCID_MPLAYER = 2; -const PLAYERCOREID PCID_PAPLAYER = 3; +const PLAYERCOREID PCID_NONE = EPC_NONE; +const PLAYERCOREID PCID_DVDPLAYER = EPC_DVDPLAYER; +const PLAYERCOREID PCID_MPLAYER = EPC_MPLAYER; +const PLAYERCOREID PCID_PAPLAYER = EPC_PAPLAYER; +#if defined(HAS_AMLPLAYER) +const PLAYERCOREID PCID_AMLPLAYER = EPC_AMLPLAYER; +#endif class CPlayerCoreFactory { diff --git a/xbmc/input/linux/LinuxInputDevices.cpp b/xbmc/input/linux/LinuxInputDevices.cpp index a5057e2..790c64f 100644 --- a/xbmc/input/linux/LinuxInputDevices.cpp +++ b/xbmc/input/linux/LinuxInputDevices.cpp @@ -763,6 +763,23 @@ void CLinuxInputDevice::SetupKeyboardAutoRepeat(int fd) { bool enable = true; +#if defined(HAS_AMLPLAYER) + // ignore the native aml driver named 'key_input', + // it is the dedicated power key handler (am_key_input) + if (strncmp(m_deviceName, "key_input", strlen("key_input")) == 0) + return; + // ignore the native aml driver named 'aml_keypad', + // it is the dedicated IR remote handler (amremote) + else if (strncmp(m_deviceName, "aml_keypad", strlen("aml_keypad")) == 0) + return; + + // turn off any keyboard autorepeat, there is a kernel bug + // where if the cpu is max'ed then key up is missed and + // we get a flood of EV_REP that never stop until next + // key down/up. Very nasty when seeking during video playback. + enable = false; +#endif + if (enable) { int kbdrep[2] = { 400, 80 }; diff --git a/xbmc/video/windows/GUIWindowFullScreen.cpp b/xbmc/video/windows/GUIWindowFullScreen.cpp index 6f9989e..1f0fc6c 100644 --- a/xbmc/video/windows/GUIWindowFullScreen.cpp +++ b/xbmc/video/windows/GUIWindowFullScreen.cpp @@ -975,7 +975,11 @@ void CGUIWindowFullScreen::Render() void CGUIWindowFullScreen::RenderTTFSubtitles() { - if ((g_application.GetCurrentPlayer() == EPC_MPLAYER || g_application.GetCurrentPlayer() == EPC_DVDPLAYER) && + if ((g_application.GetCurrentPlayer() == EPC_MPLAYER || +#if defined(HAS_AMLPLAYER) + g_application.GetCurrentPlayer() == EPC_AMLPLAYER || +#endif + g_application.GetCurrentPlayer() == EPC_DVDPLAYER) && CUtil::IsUsingTTFSubtitles() && (g_application.m_pPlayer->GetSubtitleVisible())) { CSingleLock lock (m_fontLock); -- 2.7.4