Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2270 lines (1995 sloc) 68.604 kb
/*
* 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<AMLSubtitle*>::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]",
&nothing, &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]",
&nothing, &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("&apos;","\'");
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() > (2000 * 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;
}
__int64 CAMLPlayer::GetTotalTime()
{
return m_duration_ms;
}
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<CAMLPlayer*>(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<CStdString> 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;i<filenames.size();i++)
{
// if vobsub subtitle:
if (URIUtils::GetExtension(filenames[i]) == ".idx")
{
CStdString strSubFile;
if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
AddSubtitleFile(filenames[i], strSubFile);
}
else
{
if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
{
AddSubtitleFile(filenames[i]);
}
}
}
}
int CAMLPlayer::AddSubtitleFile(const std::string &filename, const std::string &subfilename)
{
std::string ext = URIUtils::GetExtension(filename);
std::string vobsubfile = subfilename;
if(ext == ".idx")
{
/* TODO: we do not handle idx/sub binary subs yet.
if (vobsubfile.empty())
vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
CDVDDemuxVobsub v;
if(!v.Open(filename, vobsubfile))
return -1;
m_SelectionStreams.Update(NULL, &v);
int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename), 0);
m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags;
m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename2 = vobsubfile;
return index;
*/
return -1;
}
if(ext == ".sub")
{
// check for texual sub, if this is a idx/sub pair, ignore it.
CStdString strReplace(URIUtils::ReplaceExtension(filename,".idx"));
if (XFILE::CFile::Exists(strReplace))
return -1;
}
AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo;
info->Clear();
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<CDVDDemuxVobsub> 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);
}
Jump to Line
Something went wrong with that request. Please try again.