2 * Copyright (C) 2005-2008 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "threads/SystemClock.h"
24 #include "DVDPlayer.h"
26 #include "DVDInputStreams/DVDInputStream.h"
27 #include "DVDInputStreams/DVDInputStreamFile.h"
28 #include "DVDInputStreams/DVDFactoryInputStream.h"
29 #include "DVDInputStreams/DVDInputStreamNavigator.h"
30 #include "DVDInputStreams/DVDInputStreamTV.h"
32 #include "DVDDemuxers/DVDDemux.h"
33 #include "DVDDemuxers/DVDDemuxUtils.h"
34 #include "DVDDemuxers/DVDDemuxVobsub.h"
35 #include "DVDDemuxers/DVDFactoryDemuxer.h"
36 #include "DVDDemuxers/DVDDemuxFFmpeg.h"
38 #include "DVDCodecs/DVDCodecs.h"
39 #include "DVDCodecs/DVDFactoryCodec.h"
41 #include "DVDFileInfo.h"
43 #include "utils/LangCodeExpander.h"
44 #include "guilib/LocalizeStrings.h"
46 #include "utils/URIUtils.h"
47 #include "GUIInfoManager.h"
48 #include "guilib/GUIWindowManager.h"
49 #include "Application.h"
50 #include "DVDPerformanceCounter.h"
51 #include "filesystem/File.h"
52 #include "pictures/Picture.h"
53 #include "DllSwScale.h"
54 #ifdef HAS_VIDEO_PLAYBACK
55 #include "cores/VideoRenderers/RenderManager.h"
57 #ifdef HAS_PERFORMANCE_SAMPLE
58 #include "xbmc/utils/PerformanceSample.h"
60 #define MEASURE_FUNCTION
62 #include "settings/AdvancedSettings.h"
64 #include "settings/GUISettings.h"
65 #include "GUIUserMessages.h"
66 #include "settings/Settings.h"
67 #include "utils/log.h"
68 #include "utils/TimeUtils.h"
69 #include "utils/StreamDetails.h"
70 #include "utils/StreamUtils.h"
71 #include "utils/Variant.h"
72 #include "storage/MediaManager.h"
73 #include "dialogs/GUIDialogBusy.h"
74 #include "dialogs/GUIDialogKaiToast.h"
75 #include "utils/StringUtils.h"
80 void CSelectionStreams::Clear(StreamType type, StreamSource source)
82 CSingleLock lock(m_section);
83 for(int i=m_Streams.size()-1;i>=0;i--)
85 if(type && m_Streams[i].type != type)
88 if(source && m_Streams[i].source != source)
91 m_Streams.erase(m_Streams.begin() + i);
95 void CDVDPlayer::GetAudioStreamLanguage(int iStream, CStdString &strLanguage)
98 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream);
99 if(s.language.length() > 0)
100 strLanguage = s.language;
103 SelectionStream& CSelectionStreams::Get(StreamType type, int index)
105 CSingleLock lock(m_section);
107 for(int i=0;i<(int)m_Streams.size();i++)
109 if(m_Streams[i].type != type)
115 CLog::Log(LOGERROR, "%s - failed to get stream", __FUNCTION__);
119 bool CSelectionStreams::Get(StreamType type, CDemuxStream::EFlags flag, SelectionStream& out)
121 CSingleLock lock(m_section);
122 for(int i=0;i<(int)m_Streams.size();i++)
124 if(m_Streams[i].type != type)
126 if((m_Streams[i].flags & flag) != flag)
134 int CSelectionStreams::IndexOf(StreamType type, int source, int id)
136 CSingleLock lock(m_section);
138 for(int i=0;i<(int)m_Streams.size();i++)
140 if(type && m_Streams[i].type != type)
143 if(source && m_Streams[i].source != source)
147 if(m_Streams[i].id == id)
156 int CSelectionStreams::IndexOf(StreamType type, CDVDPlayer& p)
158 if (p.m_pInputStream && p.m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
161 if(type == STREAM_AUDIO)
162 id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveAudioStream();
163 else if(type == STREAM_VIDEO)
164 id = p.m_CurrentVideo.id;
165 else if(type == STREAM_SUBTITLE)
166 id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveSubtitleStream();
168 return IndexOf(type, STREAM_SOURCE_NAV, id);
171 if(type == STREAM_AUDIO)
172 return IndexOf(type, p.m_CurrentAudio.source, p.m_CurrentAudio.id);
173 else if(type == STREAM_VIDEO)
174 return IndexOf(type, p.m_CurrentVideo.source, p.m_CurrentVideo.id);
175 else if(type == STREAM_SUBTITLE)
176 return IndexOf(type, p.m_CurrentSubtitle.source, p.m_CurrentSubtitle.id);
177 else if(type == STREAM_TELETEXT)
178 return IndexOf(type, p.m_CurrentTeletext.source, p.m_CurrentTeletext.id);
183 int CSelectionStreams::Source(StreamSource source, std::string filename)
185 CSingleLock lock(m_section);
186 int index = source - 1;
187 for(int i=0;i<(int)m_Streams.size();i++)
189 SelectionStream &s = m_Streams[i];
190 if(STREAM_SOURCE_MASK(s.source) != source)
192 // if it already exists, return same
193 if(s.filename == filename)
202 void CSelectionStreams::Update(SelectionStream& s)
204 CSingleLock lock(m_section);
205 int index = IndexOf(s.type, s.source, s.id);
207 Get(s.type, index) = s;
209 m_Streams.push_back(s);
212 void CSelectionStreams::Update(CDVDInputStream* input, CDVDDemux* demuxer)
214 if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
216 CDVDInputStreamNavigator* nav = (CDVDInputStreamNavigator*)input;
217 string filename = nav->GetFileName();
218 int source = Source(STREAM_SOURCE_NAV, filename);
221 count = nav->GetAudioStreamCount();
222 for(int i=0;i<count;i++)
226 s.type = STREAM_AUDIO;
228 s.name = nav->GetAudioStreamLanguage(i);
229 s.flags = CDemuxStream::FLAG_NONE;
230 s.filename = filename;
235 count = nav->GetSubTitleStreamCount();
236 for(int i=0;i<count;i++)
240 s.type = STREAM_SUBTITLE;
242 s.name = nav->GetSubtitleStreamLanguage(i);
243 s.flags = CDemuxStream::FLAG_NONE;
244 s.filename = filename;
251 string filename = demuxer->GetFileName();
252 int count = demuxer->GetNrOfStreams();
254 if(input) /* hack to know this is sub decoder */
255 source = Source(STREAM_SOURCE_DEMUX, filename);
257 source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
260 for(int i=0;i<count;i++)
262 CDemuxStream* stream = demuxer->GetStream(i);
263 /* make sure stream is marked with right source */
264 stream->source = source;
268 s.type = stream->type;
270 s.language = stream->language;
271 s.flags = stream->flags;
272 s.filename = demuxer->GetFileName();
273 stream->GetStreamName(s.name);
275 demuxer->GetStreamCodecName(stream->iId, codec);
277 s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below.
278 if(stream->type == STREAM_AUDIO)
281 ((CDemuxStreamAudio*)stream)->GetStreamType(type);
282 if(type.length() > 0)
284 if(s.name.length() > 0)
288 s.channels = ((CDemuxStreamAudio*)stream)->iChannels;
295 CDVDPlayer::CDVDPlayer(IPlayerCallback& callback)
297 CThread("CDVDPlayer"),
298 m_CurrentAudio(STREAM_AUDIO),
299 m_CurrentVideo(STREAM_VIDEO),
300 m_CurrentSubtitle(STREAM_SUBTITLE),
301 m_CurrentTeletext(STREAM_TELETEXT),
302 m_messenger("player"),
303 m_dvdPlayerVideo(&m_clock, &m_overlayContainer, m_messenger),
304 m_dvdPlayerAudio(&m_clock, m_messenger),
305 m_dvdPlayerSubtitle(&m_overlayContainer),
306 m_dvdPlayerTeletext(),
310 m_pSubtitleDemuxer = NULL;
311 m_pInputStream = NULL;
315 m_UpdateApplication = 0;
317 m_bAbortRequest = false;
319 m_playSpeed = DVD_PLAYSPEED_NORMAL;
320 m_caching = CACHESTATE_DONE;
322 #ifdef DVDDEBUG_MESSAGE_TRACKER
323 g_dvdMessageTracker.Init();
327 CDVDPlayer::~CDVDPlayer()
331 #ifdef DVDDEBUG_MESSAGE_TRACKER
332 g_dvdMessageTracker.DeInit();
336 bool CDVDPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
340 CLog::Log(LOGNOTICE, "DVDPlayer: Opening: %s", file.GetPath().c_str());
342 // if playing a file close it first
343 // this has to be changed so we won't have to close it.
347 m_bAbortRequest = false;
348 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
351 m_UpdateApplication = 0;
353 m_PlayerOptions = options;
355 m_mimetype = file.GetMimeType();
356 m_filename = file.GetPath();
360 #if defined(HAS_VIDEO_PLAYBACK)
361 g_renderManager.PreInit();
365 if(!m_ready.WaitMSec(100))
367 CGUIDialogBusy* dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY);
369 while(!m_ready.WaitMSec(1))
370 g_windowManager.ProcessRenderLoop(false);
374 // Playback might have been stopped due to some error
375 if (m_bStop || m_bAbortRequest)
382 CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__);
387 bool CDVDPlayer::CloseFile()
389 CLog::Log(LOGNOTICE, "CDVDPlayer::CloseFile()");
391 // unpause the player
392 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
394 // set the abort request so that other threads can finish up
395 m_bAbortRequest = true;
397 // tell demuxer to abort
401 if(m_pSubtitleDemuxer)
402 m_pSubtitleDemuxer->Abort();
404 CLog::Log(LOGNOTICE, "DVDPlayer: waiting for threads to exit");
406 // wait for the main thread to finish up
407 // since this main thread cleans up all other resources and threads
408 // we are done after the StopThread call
412 m_EdlAutoSkipMarkers.Clear();
414 CLog::Log(LOGNOTICE, "DVDPlayer: finished waiting");
415 #if defined(HAS_VIDEO_PLAYBACK)
416 g_renderManager.UnInit();
421 bool CDVDPlayer::IsPlaying() const
426 void CDVDPlayer::OnStartup()
428 m_CurrentVideo.Clear();
429 m_CurrentAudio.Clear();
430 m_CurrentSubtitle.Clear();
431 m_CurrentTeletext.Clear();
435 g_dvdPerformanceCounter.EnableMainPerformance(ThreadHandle());
438 bool CDVDPlayer::OpenInputStream()
441 SAFE_DELETE(m_pInputStream);
443 CLog::Log(LOGNOTICE, "Creating InputStream");
445 // correct the filename if needed
446 CStdString filename(m_filename);
447 if (filename.Find("dvd://") == 0
448 || filename.CompareNoCase("d:\\video_ts\\video_ts.ifo") == 0
449 || filename.CompareNoCase("iso9660://video_ts/video_ts.ifo") == 0)
451 m_filename = g_mediaManager.TranslateDevicePath("");
454 m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_filename, m_mimetype);
455 if(m_pInputStream == NULL)
457 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - unable to create input stream for [%s]", m_filename.c_str());
461 m_pInputStream->SetFileItem(m_item);
463 if (!m_pInputStream->Open(m_filename.c_str(), m_mimetype))
465 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
467 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - failed to open [%s] as DVD ISO, trying Bluray", m_filename.c_str());
468 m_mimetype = "bluray/iso";
469 filename = m_filename;
470 filename = filename + "/BDMV/index.bdmv";
471 int title = (int)m_item.GetProperty("BlurayStartingTitle").asInteger();
473 filename.AppendFormat("?title=%d",title);
475 m_filename = filename;
478 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - error opening [%s]", m_filename.c_str());
482 // find any available external subtitles for non dvd files
483 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
484 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
485 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
487 // find any available external subtitles
488 std::vector<CStdString> filenames;
489 CUtil::ScanForExternalSubtitles( m_filename, filenames );
491 // find any upnp subtitles
492 CStdString key("upnp:subtitle:1");
493 for(unsigned s = 1; m_item.HasProperty(key); key.Format("upnp:subtitle:%u", ++s))
494 filenames.push_back(m_item.GetProperty(key).asString());
496 for(unsigned int i=0;i<filenames.size();i++)
498 // if vobsub subtitle:
499 if (URIUtils::GetExtension(filenames[i]) == ".idx")
501 CStdString strSubFile;
502 if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
503 AddSubtitleFile(filenames[i], strSubFile);
507 if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
509 AddSubtitleFile(filenames[i]);
512 } // end loop over all subtitle files
514 g_settings.m_currentVideoSettings.m_SubtitleCached = true;
517 SetAVDelay(g_settings.m_currentVideoSettings.m_AudioDelay);
518 SetSubTitleDelay(g_settings.m_currentVideoSettings.m_SubtitleDelay);
526 bool CDVDPlayer::OpenDemuxStream()
529 SAFE_DELETE(m_pDemuxer);
531 CLog::Log(LOGNOTICE, "Creating Demuxer");
536 while(!m_bStop && attempts-- > 0)
538 m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
539 if(!m_pDemuxer && m_pInputStream->NextStream())
541 CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__);
549 CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__);
556 CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__);
560 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
561 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
562 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
564 int64_t len = m_pInputStream->GetLength();
565 int64_t tim = m_pDemuxer->GetStreamLength();
566 if(len > 0 && tim > 0)
567 m_pInputStream->SetReadRate(len * 1000 / tim);
572 void CDVDPlayer::OpenDefaultStreams()
580 count = m_SelectionStreams.Count(STREAM_VIDEO);
584 && m_SelectionStreams.Get(STREAM_VIDEO, CDemuxStream::FLAG_DEFAULT, st))
586 if(OpenVideoStream(st.id, st.source))
589 CLog::Log(LOGWARNING, "%s - failed to open default stream (%d)", __FUNCTION__, st.id);
592 for(int i = 0;i<count && !valid;i++)
594 SelectionStream& s = m_SelectionStreams.Get(STREAM_VIDEO, i);
595 if(OpenVideoStream(s.id, s.source))
599 CloseVideoStream(true);
601 if(!m_PlayerOptions.video_only)
604 count = m_SelectionStreams.Count(STREAM_AUDIO);
606 if(g_settings.m_currentVideoSettings.m_AudioStream >= 0
607 && g_settings.m_currentVideoSettings.m_AudioStream < count)
609 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, g_settings.m_currentVideoSettings.m_AudioStream);
610 if(OpenAudioStream(s.id, s.source))
613 CLog::Log(LOGWARNING, "%s - failed to restore selected audio stream (%d)", __FUNCTION__, g_settings.m_currentVideoSettings.m_AudioStream);
617 && m_SelectionStreams.Get(STREAM_AUDIO, CDemuxStream::FLAG_DEFAULT, st))
619 if(OpenAudioStream(st.id, st.source))
622 CLog::Log(LOGWARNING, "%s - failed to open default stream (%d)", __FUNCTION__, st.id);
629 * If there is more than one audio stream and a valid one is not chosen yet, select the one
630 * with the maximum number of channels or the best codec if the same number of channels.
632 int max_channels = -1;
633 int max_codec_priority = -1;
634 CStdString max_codec;
635 int max_stream_id = -1;
636 for(int i = 0; i < count; i++)
638 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, i);
639 if(s.source == STREAM_SOURCE_DEMUX) // Only demux streams
641 int codec_priority = StreamUtils::GetCodecPriority(s.codec);
642 if(s.channels > max_channels
643 ||(s.channels == max_channels && codec_priority > max_codec_priority))
645 max_channels = s.channels;
646 max_codec_priority = codec_priority;
652 if(max_stream_id >= 0)
654 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, max_stream_id);
655 if(OpenAudioStream(s.id, s.source))
658 CLog::Log(LOGDEBUG, "%s - using automatically selected audio stream (%d) based on codec '%s' and maximum number of channels '%d'",
659 __FUNCTION__, s.id, max_codec.c_str(), max_channels);
662 CLog::Log(LOGWARNING, "%s - failed to open automatically selected audio stream (%d)", __FUNCTION__, s.id);
666 for(int i = 0; i<count && !valid; i++)
668 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, i);
669 if(OpenAudioStream(s.id, s.source))
673 CloseAudioStream(true);
676 // open subtitle stream
677 count = m_SelectionStreams.Count(STREAM_SUBTITLE);
680 // if subs are disabled, check for forced
681 if(!valid && !g_settings.m_currentVideoSettings.m_SubtitleOn
682 && m_SelectionStreams.Get(STREAM_SUBTITLE, CDemuxStream::FLAG_FORCED, st))
684 if(OpenSubtitleStream(st.id, st.source))
690 CLog::Log(LOGWARNING, "%s - failed to open default/forced stream (%d)", __FUNCTION__, st.id);
695 && g_settings.m_currentVideoSettings.m_SubtitleStream >= 0
696 && g_settings.m_currentVideoSettings.m_SubtitleStream < count)
698 SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, g_settings.m_currentVideoSettings.m_SubtitleStream);
699 if(OpenSubtitleStream(s.id, s.source))
702 CLog::Log(LOGWARNING, "%s - failed to restore selected subtitle stream (%d)", __FUNCTION__, g_settings.m_currentVideoSettings.m_SubtitleStream);
707 && m_SelectionStreams.Get(STREAM_SUBTITLE, CDemuxStream::FLAG_DEFAULT, st))
709 if(OpenSubtitleStream(st.id, st.source))
712 CLog::Log(LOGWARNING, "%s - failed to open default/forced stream (%d)", __FUNCTION__, st.id);
716 for(int i = 0;i<count && !valid; i++)
718 SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, i);
719 if(OpenSubtitleStream(s.id, s.source))
723 CloseSubtitleStream(false);
725 if(valid && (g_settings.m_currentVideoSettings.m_SubtitleOn || force) && !m_PlayerOptions.video_only)
726 m_dvdPlayerVideo.EnableSubtitle(true);
728 m_dvdPlayerVideo.EnableSubtitle(false);
730 // open teletext data stream
731 count = m_SelectionStreams.Count(STREAM_TELETEXT);
733 for(int i = 0;i<count && !valid;i++)
735 SelectionStream& s = m_SelectionStreams.Get(STREAM_TELETEXT, i);
736 if(OpenTeletextStream(s.id, s.source))
740 CloseTeletextStream(true);
743 bool CDVDPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
746 // check if we should read from subtitle demuxer
747 if(m_dvdPlayerSubtitle.AcceptsData() && m_pSubtitleDemuxer )
749 if(m_pSubtitleDemuxer)
750 packet = m_pSubtitleDemuxer->Read();
754 if(packet->iStreamId < 0)
757 stream = m_pSubtitleDemuxer->GetStream(packet->iStreamId);
760 CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
763 if(stream->source == STREAM_SOURCE_NONE)
765 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
766 m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer);
772 // read a data frame from stream.
774 packet = m_pDemuxer->Read();
778 // this groupId stuff is getting a bit messy, need to find a better way
779 // currently it is used to determine if a menu overlay is associated with a picture
780 // for dvd's we use as a group id, the current cell and the current title
781 // to be a bit more precise we alse count the number of disc's in case of a pts wrap back in the same cell / title
782 packet->iGroupId = m_pInputStream->GetCurrentGroupId();
784 if(packet->iStreamId < 0)
787 stream = m_pDemuxer->GetStream(packet->iStreamId);
790 CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
793 if(stream->source == STREAM_SOURCE_NONE)
795 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
796 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
803 bool CDVDPlayer::IsValidStream(CCurrentStream& stream)
806 return true; // we consider non selected as valid
808 int source = STREAM_SOURCE_MASK(stream.source);
809 if(source == STREAM_SOURCE_TEXT)
811 if(source == STREAM_SOURCE_DEMUX_SUB)
813 CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.id);
814 if(st == NULL || st->disabled)
816 if(st->type != stream.type)
820 if(source == STREAM_SOURCE_DEMUX)
822 CDemuxStream* st = m_pDemuxer->GetStream(stream.id);
823 if(st == NULL || st->disabled)
825 if(st->type != stream.type)
828 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
830 if(stream.type == STREAM_AUDIO && st->iPhysicalId != m_dvd.iSelectedAudioStream)
832 if(stream.type == STREAM_SUBTITLE && st->iPhysicalId != m_dvd.iSelectedSPUStream)
842 bool CDVDPlayer::IsBetterStream(CCurrentStream& current, CDemuxStream* stream)
844 // Do not reopen non-video streams if we're in video-only mode
845 if(m_PlayerOptions.video_only && current.type != STREAM_VIDEO)
848 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
852 source_type = STREAM_SOURCE_MASK(current.source);
853 if(source_type != STREAM_SOURCE_DEMUX
854 && source_type != STREAM_SOURCE_NONE)
857 source_type = STREAM_SOURCE_MASK(stream->source);
858 if(source_type != STREAM_SOURCE_DEMUX
859 || stream->type != current.type
860 || stream->iId == current.id)
863 if(current.type == STREAM_AUDIO && stream->iPhysicalId == m_dvd.iSelectedAudioStream)
865 if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream)
867 if(current.type == STREAM_VIDEO && current.id < 0)
872 if(stream->source == current.source
873 && stream->iId == current.id)
879 if(stream->type != current.type)
882 if(current.type == STREAM_SUBTITLE)
885 if(current.type == STREAM_TELETEXT)
894 void CDVDPlayer::Process()
896 if (!OpenInputStream())
898 m_bAbortRequest = true;
902 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
904 CLog::Log(LOGNOTICE, "DVDPlayer: playing a dvd with menu's");
905 m_PlayerOptions.starttime = 0;
908 if(m_PlayerOptions.state.size() > 0)
909 ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(m_PlayerOptions.state);
911 ((CDVDInputStreamNavigator*)m_pInputStream)->EnableSubtitleStream(g_settings.m_currentVideoSettings.m_SubtitleOn);
913 g_settings.m_currentVideoSettings.m_SubtitleCached = true;
916 if(!OpenDemuxStream())
918 m_bAbortRequest = true;
922 // allow renderer to switch to fullscreen if requested
923 m_dvdPlayerVideo.EnableFullscreen(m_PlayerOptions.fullscreen);
925 OpenDefaultStreams();
927 // look for any EDL files
929 m_EdlAutoSkipMarkers.Clear();
930 float fFramesPerSecond;
931 if (m_CurrentVideo.id >= 0 && m_CurrentVideo.hint.fpsrate > 0 && m_CurrentVideo.hint.fpsscale > 0)
933 fFramesPerSecond = (float)m_CurrentVideo.hint.fpsrate / (float)m_CurrentVideo.hint.fpsscale;
934 m_Edl.ReadEditDecisionLists(m_filename, fFramesPerSecond, m_CurrentVideo.hint.height);
938 * Check to see if the demuxer should start at something other than time 0. This will be the case
939 * if there was a start time specified as part of the "Start from where last stopped" (aka
940 * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
944 if(m_PlayerOptions.starttime > 0 || m_PlayerOptions.startpercent > 0)
946 if (m_PlayerOptions.startpercent > 0 && m_pDemuxer)
948 int64_t playerStartTime = (int64_t) ( ( (float) m_pDemuxer->GetStreamLength() ) * ( m_PlayerOptions.startpercent/(float)100 ) );
949 starttime = m_Edl.RestoreCutTime(playerStartTime);
953 starttime = m_Edl.RestoreCutTime((__int64)m_PlayerOptions.starttime * 1000); // s to ms
955 CLog::Log(LOGDEBUG, "%s - Start position set to last stopped position: %d", __FUNCTION__, starttime);
957 else if(m_Edl.InCut(0, &cut)
958 && (cut.action == CEdl::CUT || cut.action == CEdl::COMM_BREAK))
961 CLog::Log(LOGDEBUG, "%s - Start position set to end of first cut or commercial break: %d", __FUNCTION__, starttime);
962 if(cut.action == CEdl::COMM_BREAK)
965 * Setup auto skip markers as if the commercial break had been skipped using standard
968 m_EdlAutoSkipMarkers.commbreak_start = cut.start;
969 m_EdlAutoSkipMarkers.commbreak_end = cut.end;
970 m_EdlAutoSkipMarkers.seek_to_start = true;
975 double startpts = DVD_NOPTS_VALUE;
978 if (m_pDemuxer->SeekTime(starttime, false, &startpts))
979 CLog::Log(LOGDEBUG, "%s - starting demuxer from: %d", __FUNCTION__, starttime);
981 CLog::Log(LOGDEBUG, "%s - failed to start demuxing from: %d", __FUNCTION__, starttime);
984 if(m_pSubtitleDemuxer)
986 if(m_pSubtitleDemuxer->SeekTime(starttime, false, &startpts))
987 CLog::Log(LOGDEBUG, "%s - starting subtitle demuxer from: %d", __FUNCTION__, starttime);
989 CLog::Log(LOGDEBUG, "%s - failed to start subtitle demuxing from: %d", __FUNCTION__, starttime);
993 // make sure application know our info
994 UpdateApplication(0);
997 if(m_PlayerOptions.identify == false)
998 m_callback.OnPlayBackStarted();
1000 // we are done initializing now, set the readyevent
1003 SetCaching(CACHESTATE_FLUSH);
1005 while (!m_bAbortRequest)
1007 // handle messages send to this thread, like seek or demuxer reset requests
1013 // should we open a new input stream?
1016 if (OpenInputStream() == false)
1018 m_bAbortRequest = true;
1023 // should we open a new demuxer?
1026 if (m_pInputStream->NextStream() == false)
1029 if (m_pInputStream->IsEOF())
1032 if (OpenDemuxStream() == false)
1034 m_bAbortRequest = true;
1038 OpenDefaultStreams();
1039 UpdateApplication(0);
1043 // handle eventual seeks due to playspeed
1046 // update player state
1047 UpdatePlayState(200);
1049 // update application with our state
1050 UpdateApplication(1000);
1052 // if the queues are full, no need to read more
1053 if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0)
1054 || (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
1060 // always yield to players if they have data
1061 if((m_dvdPlayerAudio.m_messageQueue.GetDataSize() > 0 || m_CurrentAudio.id < 0)
1062 && (m_dvdPlayerVideo.m_messageQueue.GetDataSize() > 0 || m_CurrentVideo.id < 0))
1065 DemuxPacket* pPacket = NULL;
1066 CDemuxStream *pStream = NULL;
1067 ReadPacket(pPacket, pStream);
1068 if (pPacket && !pStream)
1070 /* probably a empty packet, just free it and move on */
1071 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
1077 // when paused, demuxer could be be returning empty
1078 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
1081 // if there is another stream available, let
1082 // player reopen demuxer
1083 if(m_pInputStream->NextStream())
1085 SAFE_DELETE(m_pDemuxer);
1089 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1091 CDVDInputStreamNavigator* pStream = static_cast<CDVDInputStreamNavigator*>(m_pInputStream);
1093 // stream is holding back data until demuxer has flushed
1094 if(pStream->IsHeld())
1096 pStream->SkipHold();
1100 // stills will be skipped
1101 if(m_dvd.state == DVDSTATE_STILL)
1103 if (m_dvd.iDVDStillTime > 0)
1105 if ((XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime) >= m_dvd.iDVDStillTime)
1107 m_dvd.iDVDStillTime = 0;
1108 m_dvd.iDVDStillStartTime = 0;
1109 m_dvd.state = DVDSTATE_NORMAL;
1110 pStream->SkipStill();
1116 // if playing a main title DVD/ISO rip, there is no menu structure so
1117 // dvdnav will tell us it's done by setting EOF on the stream.
1118 if (pStream->IsEOF())
1121 // always continue on dvd's
1126 // make sure we tell all players to finish it's data
1127 if(m_CurrentAudio.inited)
1128 m_dvdPlayerAudio.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
1129 if(m_CurrentVideo.inited)
1130 m_dvdPlayerVideo.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
1131 if(m_CurrentSubtitle.inited)
1132 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
1133 if(m_CurrentTeletext.inited)
1134 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
1135 m_CurrentAudio.inited = false;
1136 m_CurrentVideo.inited = false;
1137 m_CurrentSubtitle.inited = false;
1138 m_CurrentTeletext.inited = false;
1139 m_CurrentAudio.started = false;
1140 m_CurrentVideo.started = false;
1141 m_CurrentSubtitle.started = false;
1142 m_CurrentTeletext.started = false;
1144 // if we are caching, start playing it again
1145 SetCaching(CACHESTATE_DONE);
1147 // while players are still playing, keep going to allow seekbacks
1148 if(m_dvdPlayerAudio.m_messageQueue.GetDataSize() > 0
1149 || m_dvdPlayerVideo.m_messageQueue.GetDataSize() > 0)
1155 if (!m_pInputStream->IsEOF())
1156 CLog::Log(LOGINFO, "%s - eof reading from demuxer", __FUNCTION__);
1161 // it's a valid data packet, reset error counter
1164 // check so that none of our streams has become invalid
1165 if (!IsValidStream(m_CurrentAudio) && m_dvdPlayerAudio.IsStalled()) CloseAudioStream(true);
1166 if (!IsValidStream(m_CurrentVideo) && m_dvdPlayerVideo.IsStalled()) CloseVideoStream(true);
1167 if (!IsValidStream(m_CurrentSubtitle) && m_dvdPlayerSubtitle.IsStalled()) CloseSubtitleStream(true);
1168 if (!IsValidStream(m_CurrentTeletext)) CloseTeletextStream(true);
1170 // see if we can find something better to play
1171 if (IsBetterStream(m_CurrentAudio, pStream)) OpenAudioStream (pStream->iId, pStream->source);
1172 if (IsBetterStream(m_CurrentVideo, pStream)) OpenVideoStream (pStream->iId, pStream->source);
1173 if (IsBetterStream(m_CurrentSubtitle, pStream)) OpenSubtitleStream(pStream->iId, pStream->source);
1174 if (IsBetterStream(m_CurrentTeletext, pStream)) OpenTeletextStream(pStream->iId, pStream->source);
1176 // process the packet
1177 ProcessPacket(pStream, pPacket);
1179 // check if in a cut or commercial break that should be automatically skipped
1180 CheckAutoSceneSkip();
1184 void CDVDPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
1186 /* process packet if it belongs to selected stream. for dvd's don't allow automatic opening of streams*/
1187 StreamLock lock(this);
1191 if (pPacket->iStreamId == m_CurrentAudio.id && pStream->source == m_CurrentAudio.source && pStream->type == STREAM_AUDIO)
1192 ProcessAudioData(pStream, pPacket);
1193 else if (pPacket->iStreamId == m_CurrentVideo.id && pStream->source == m_CurrentVideo.source && pStream->type == STREAM_VIDEO)
1194 ProcessVideoData(pStream, pPacket);
1195 else if (pPacket->iStreamId == m_CurrentSubtitle.id && pStream->source == m_CurrentSubtitle.source && pStream->type == STREAM_SUBTITLE)
1196 ProcessSubData(pStream, pPacket);
1197 else if (pPacket->iStreamId == m_CurrentTeletext.id && pStream->source == m_CurrentTeletext.source && pStream->type == STREAM_TELETEXT)
1198 ProcessTeletextData(pStream, pPacket);
1201 pStream->SetDiscard(AVDISCARD_ALL);
1202 CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
1207 CLog::Log(LOGERROR, "%s - Exception thrown when processing demux packet", __FUNCTION__);
1212 void CDVDPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
1214 if (m_CurrentAudio.stream != (void*)pStream)
1216 /* check so that dmuxer hints or extra data hasn't changed */
1217 /* if they have, reopen stream */
1219 if (m_CurrentAudio.hint != CDVDStreamInfo(*pStream, true))
1220 OpenAudioStream( pPacket->iStreamId, pStream->source );
1222 m_CurrentAudio.stream = (void*)pStream;
1225 // check if we are too slow and need to recache
1226 CheckStartCaching(m_CurrentAudio);
1228 CheckContinuity(m_CurrentAudio, pPacket);
1229 if(pPacket->dts != DVD_NOPTS_VALUE)
1230 m_CurrentAudio.dts = pPacket->dts;
1231 else if(pPacket->pts != DVD_NOPTS_VALUE)
1232 m_CurrentAudio.dts = pPacket->pts;
1235 if (CheckPlayerInit(m_CurrentAudio, DVDPLAYER_AUDIO))
1239 * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped.
1240 * If not inside a hard cut, but the demux point has reached an EDL mute section then trigger the
1241 * AUDIO_SILENCE state. The AUDIO_SILENCE state is reverted as soon as the demux point is outside
1242 * of any EDL section while EDL mute is still active.
1245 if (CheckSceneSkip(m_CurrentAudio))
1247 else if (m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts), &cut) && cut.action == CEdl::MUTE // Inside EDL mute
1248 && !m_EdlAutoSkipMarkers.mute) // Mute not already triggered
1250 m_dvdPlayerAudio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, true));
1251 m_EdlAutoSkipMarkers.mute = true;
1253 else if (!m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts), &cut) // Outside of any EDL
1254 && m_EdlAutoSkipMarkers.mute) // But the mute hasn't been removed yet
1256 m_dvdPlayerAudio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, false));
1257 m_EdlAutoSkipMarkers.mute = false;
1260 m_dvdPlayerAudio.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1263 void CDVDPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
1265 if (m_CurrentVideo.stream != (void*)pStream)
1267 /* check so that dmuxer hints or extra data hasn't changed */
1268 /* if they have reopen stream */
1270 if (m_CurrentVideo.hint != CDVDStreamInfo(*pStream, true))
1271 OpenVideoStream(pPacket->iStreamId, pStream->source);
1273 m_CurrentVideo.stream = (void*)pStream;
1276 // check if we are too slow and need to recache
1277 CheckStartCaching(m_CurrentVideo);
1279 if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
1281 CheckContinuity(m_CurrentVideo, pPacket);
1282 if(pPacket->dts != DVD_NOPTS_VALUE)
1283 m_CurrentVideo.dts = pPacket->dts;
1284 else if(pPacket->pts != DVD_NOPTS_VALUE)
1285 m_CurrentVideo.dts = pPacket->pts;
1289 if (CheckPlayerInit(m_CurrentVideo, DVDPLAYER_VIDEO))
1292 if (CheckSceneSkip(m_CurrentVideo))
1295 m_dvdPlayerVideo.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1298 void CDVDPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
1300 if (m_CurrentSubtitle.stream != (void*)pStream)
1302 /* check so that dmuxer hints or extra data hasn't changed */
1303 /* if they have reopen stream */
1305 if (m_CurrentSubtitle.hint != CDVDStreamInfo(*pStream, true))
1306 OpenSubtitleStream(pPacket->iStreamId, pStream->source);
1308 m_CurrentSubtitle.stream = (void*)pStream;
1310 if(pPacket->dts != DVD_NOPTS_VALUE)
1311 m_CurrentSubtitle.dts = pPacket->dts;
1312 else if(pPacket->pts != DVD_NOPTS_VALUE)
1313 m_CurrentSubtitle.dts = pPacket->pts;
1316 if (CheckPlayerInit(m_CurrentSubtitle, DVDPLAYER_SUBTITLE))
1319 if (CheckSceneSkip(m_CurrentSubtitle))
1322 m_dvdPlayerSubtitle.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1324 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1325 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
1328 void CDVDPlayer::ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket)
1330 if (m_CurrentTeletext.stream != (void*)pStream)
1332 /* check so that dmuxer hints or extra data hasn't changed */
1333 /* if they have, reopen stream */
1334 if (m_CurrentTeletext.hint != CDVDStreamInfo(*pStream, true))
1335 OpenTeletextStream( pPacket->iStreamId, pStream->source );
1337 m_CurrentTeletext.stream = (void*)pStream;
1339 if(pPacket->dts != DVD_NOPTS_VALUE)
1340 m_CurrentTeletext.dts = pPacket->dts;
1341 else if(pPacket->pts != DVD_NOPTS_VALUE)
1342 m_CurrentTeletext.dts = pPacket->pts;
1345 if (CheckPlayerInit(m_CurrentTeletext, DVDPLAYER_TELETEXT))
1348 if (CheckSceneSkip(m_CurrentTeletext))
1351 m_dvdPlayerTeletext.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1354 bool CDVDPlayer::GetCachingTimes(double& level, double& delay, double& offset)
1356 if(!m_pInputStream || !m_pDemuxer)
1359 int64_t cached = m_pInputStream->GetCachedBytes();
1360 int64_t length = m_pInputStream->GetLength();
1361 int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR);
1362 unsigned rate = m_pInputStream->GetReadRate();
1363 if(cached < 0 || length <= 0 || remain < 0)
1366 double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
1367 double queued = 1000.0 * GetQueueTime() / play_sbp;
1371 offset = (double)(cached + queued) / length;
1376 if(rate == (unsigned)-1) /* buffer is full */
1382 double cache_sbp = 1.1 * (double)DVD_TIME_BASE / rate; /* underestimate by 10 % */
1383 double play_left = play_sbp * (remain + queued); /* time to play out all remaining bytes */
1384 double cache_left = cache_sbp * (remain - cached); /* time to cache the remaining bytes */
1385 double cache_need = std::max(0.0, remain - play_left / cache_sbp); /* bytes needed until play_left == cache_left */
1387 delay = cache_left - play_left;
1388 level = (cached + queued) / (cache_need + queued);
1392 void CDVDPlayer::HandlePlaySpeed()
1394 ECacheState caching = m_caching;
1396 if(IsInMenu() && caching != CACHESTATE_DONE)
1397 caching = CACHESTATE_DONE;
1399 if(caching == CACHESTATE_FULL)
1401 double level, delay, offset;
1402 if(GetCachingTimes(level, delay, offset))
1406 CGUIDialogKaiToast::QueueNotification("Cache full", "Cache filled before reaching required amount for continous playback");
1407 caching = CACHESTATE_INIT;
1410 caching = CACHESTATE_INIT;
1414 if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0)
1415 || (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
1416 caching = CACHESTATE_INIT;
1420 if(caching == CACHESTATE_INIT)
1422 // if all enabled streams have been inited we are done
1423 if((m_CurrentVideo.id < 0 || m_CurrentVideo.started)
1424 && (m_CurrentAudio.id < 0 || m_CurrentAudio.started))
1425 caching = CACHESTATE_PLAY;
1427 // handle situation that we get no data on one stream
1428 if(m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0)
1430 if ((!m_dvdPlayerAudio.AcceptsData() && !m_CurrentVideo.started)
1431 || (!m_dvdPlayerVideo.AcceptsData() && !m_CurrentAudio.started))
1433 caching = CACHESTATE_DONE;
1434 SAFE_RELEASE(m_CurrentAudio.startsync);
1435 SAFE_RELEASE(m_CurrentVideo.startsync);
1440 if(caching == CACHESTATE_PLAY)
1442 // if all enabled streams have started playing we are done
1443 if((m_CurrentVideo.id < 0 || !m_dvdPlayerVideo.IsStalled())
1444 && (m_CurrentAudio.id < 0 || !m_dvdPlayerAudio.IsStalled()))
1445 caching = CACHESTATE_DONE;
1448 if(m_caching != caching)
1449 SetCaching(caching);
1452 if(GetPlaySpeed() != DVD_PLAYSPEED_NORMAL && GetPlaySpeed() != DVD_PLAYSPEED_PAUSE)
1456 // this can't be done in menu
1457 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1460 else if (m_CurrentVideo.id >= 0
1461 && m_CurrentVideo.inited == true
1462 && m_SpeedState.lastpts != m_dvdPlayerVideo.GetCurrentPts()
1463 && m_SpeedState.lasttime != GetTime())
1465 m_SpeedState.lastpts = m_dvdPlayerVideo.GetCurrentPts();
1466 m_SpeedState.lasttime = GetTime();
1467 // check how much off clock video is when ff/rw:ing
1468 // a problem here is that seeking isn't very accurate
1469 // and since the clock will be resynced after seek
1470 // we might actually not really be playing at the wanted
1471 // speed. we'd need to have some way to not resync the clock
1472 // after a seek to remember timing. still need to handle
1473 // discontinuities somehow
1475 // when seeking, give the player a headstart to make sure
1476 // the time it takes to seek doesn't make a difference.
1478 error = m_clock.GetClock() - m_SpeedState.lastpts;
1479 error *= m_playSpeed / abs(m_playSpeed);
1481 if(error > DVD_MSEC_TO_TIME(1000))
1483 CLog::Log(LOGDEBUG, "CDVDPlayer::Process - Seeking to catch up");
1484 __int64 iTime = (__int64)DVD_TIME_TO_MSEC(m_clock.GetClock() + m_State.time_offset + 500000.0 * m_playSpeed / DVD_PLAYSPEED_NORMAL);
1485 m_messenger.Put(new CDVDMsgPlayerSeek(iTime, (GetPlaySpeed() < 0), true, false, false, true));
1491 bool CDVDPlayer::CheckStartCaching(CCurrentStream& current)
1493 if(m_caching != CACHESTATE_DONE
1494 || m_playSpeed != DVD_PLAYSPEED_NORMAL)
1500 if((current.type == STREAM_AUDIO && m_dvdPlayerAudio.IsStalled())
1501 || (current.type == STREAM_VIDEO && m_dvdPlayerVideo.IsStalled()))
1503 // don't start caching if it's only a single stream that has run dry
1504 if(m_dvdPlayerAudio.m_messageQueue.GetLevel() > 50
1505 || m_dvdPlayerVideo.m_messageQueue.GetLevel() > 50)
1508 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP)
1509 || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
1510 SetCaching(CACHESTATE_INIT);
1514 SetCaching(CACHESTATE_FULL);
1516 SetCaching(CACHESTATE_INIT);
1523 bool CDVDPlayer::CheckPlayerInit(CCurrentStream& current, unsigned int source)
1525 if(current.startpts != DVD_NOPTS_VALUE
1526 && current.dts != DVD_NOPTS_VALUE)
1528 if((current.startpts - current.dts) > DVD_SEC_TO_TIME(20))
1530 CLog::Log(LOGDEBUG, "%s - too far to decode before finishing seek", __FUNCTION__);
1531 if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
1532 m_CurrentAudio.startpts = current.dts;
1533 if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
1534 m_CurrentVideo.startpts = current.dts;
1535 if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
1536 m_CurrentSubtitle.startpts = current.dts;
1537 if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE)
1538 m_CurrentTeletext.startpts = current.dts;
1541 if(current.startpts <= current.dts)
1542 current.startpts = DVD_NOPTS_VALUE;
1545 // await start sync to be finished
1546 if(current.startpts != DVD_NOPTS_VALUE)
1548 CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
1552 // send of the sync message if any
1553 if(current.startsync)
1555 SendPlayerMessage(current.startsync, source);
1556 current.startsync = NULL;
1559 //If this is the first packet after a discontinuity, send it as a resync
1560 if (current.inited == false && current.dts != DVD_NOPTS_VALUE)
1562 current.inited = true;
1563 current.startpts = current.dts;
1565 bool setclock = false;
1566 if(m_playSpeed == DVD_PLAYSPEED_NORMAL)
1568 if( source == DVDPLAYER_AUDIO)
1569 setclock = !m_CurrentVideo.inited;
1570 else if(source == DVDPLAYER_VIDEO)
1571 setclock = !m_CurrentAudio.inited;
1575 if(source == DVDPLAYER_VIDEO)
1579 double starttime = current.startpts;
1580 if(m_CurrentAudio.inited
1581 && m_CurrentAudio.startpts != DVD_NOPTS_VALUE
1582 && m_CurrentAudio.startpts < starttime)
1583 starttime = m_CurrentAudio.startpts;
1584 if(m_CurrentVideo.inited
1585 && m_CurrentVideo.startpts != DVD_NOPTS_VALUE
1586 && m_CurrentVideo.startpts < starttime)
1587 starttime = m_CurrentVideo.startpts;
1589 starttime = current.startpts - starttime;
1592 if(starttime > DVD_SEC_TO_TIME(2))
1593 CLog::Log(LOGWARNING, "CDVDPlayer::CheckPlayerInit(%d) - Ignoring too large delay of %f", source, starttime);
1595 SendPlayerMessage(new CDVDMsgDouble(CDVDMsg::GENERAL_DELAY, starttime), source);
1598 SendPlayerMessage(new CDVDMsgGeneralResync(current.dts, setclock), source);
1603 void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
1605 if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
1608 if( pPacket->dts == DVD_NOPTS_VALUE )
1612 // these checks seem to cause more harm, than good
1613 // looping stillframes are not common in normal files
1614 // and a better fix for this behaviour would be to
1615 // correct the timestamps with some offset
1617 if (current.type == STREAM_VIDEO
1618 && m_CurrentAudio.dts != DVD_NOPTS_VALUE
1619 && m_CurrentVideo.dts != DVD_NOPTS_VALUE)
1621 /* check for looping stillframes on non dvd's, dvd's will be detected by long duration check later */
1622 if( m_pInputStream && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1624 /* special case for looping stillframes THX test discs*/
1625 /* only affect playback when not from dvd */
1626 if( (m_CurrentAudio.dts > m_CurrentVideo.dts + DVD_MSEC_TO_TIME(200))
1627 && (m_CurrentVideo.dts == pPacket->dts) )
1629 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - Detected looping stillframe");
1630 SynchronizePlayers(SYNCSOURCE_VIDEO);
1635 /* if we haven't received video for a while, but we have been */
1636 /* getting audio much more recently, make sure video wait's */
1637 /* this occurs especially on thx test disc */
1638 if( (pPacket->dts > m_CurrentVideo.dts + DVD_MSEC_TO_TIME(200))
1639 && (pPacket->dts < m_CurrentAudio.dts + DVD_MSEC_TO_TIME(50)) )
1641 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - Potential long duration frame");
1642 SynchronizePlayers(SYNCSOURCE_VIDEO);
1648 double mindts, maxdts;
1649 if(m_CurrentAudio.dts == DVD_NOPTS_VALUE)
1650 maxdts = mindts = m_CurrentVideo.dts;
1651 else if(m_CurrentVideo.dts == DVD_NOPTS_VALUE)
1652 maxdts = mindts = m_CurrentAudio.dts;
1655 maxdts = max(m_CurrentAudio.dts, m_CurrentVideo.dts);
1656 mindts = min(m_CurrentAudio.dts, m_CurrentVideo.dts);
1659 /* if we don't have max and min, we can't do anything more */
1660 if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
1663 if( pPacket->dts < mindts - DVD_MSEC_TO_TIME(100) && current.inited)
1665 /* if video player is rendering a stillframe, we need to make sure */
1666 /* audio has finished processing it's data otherwise it will be */
1667 /* displayed too early */
1669 CLog::Log(LOGWARNING, "CDVDPlayer::CheckContinuity - resync backword :%d, prev:%f, curr:%f, diff:%f"
1670 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1671 if (m_dvdPlayerVideo.IsStalled() && m_CurrentVideo.dts != DVD_NOPTS_VALUE)
1672 SynchronizePlayers(SYNCSOURCE_VIDEO);
1673 else if (m_dvdPlayerAudio.IsStalled() && m_CurrentAudio.dts != DVD_NOPTS_VALUE)
1674 SynchronizePlayers(SYNCSOURCE_AUDIO);
1676 m_CurrentAudio.inited = false;
1677 m_CurrentVideo.inited = false;
1678 m_CurrentSubtitle.inited = false;
1679 m_CurrentTeletext.inited = false;
1682 /* stream jump forward */
1683 if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000) && current.inited)
1685 CLog::Log(LOGWARNING, "CDVDPlayer::CheckContinuity - resync forward :%d, prev:%f, curr:%f, diff:%f"
1686 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1687 /* normally don't need to sync players since video player will keep playing at normal fps */
1688 /* after a discontinuity */
1689 //SynchronizePlayers(dts, pts, MSGWAIT_ALL);
1690 m_CurrentAudio.inited = false;
1691 m_CurrentVideo.inited = false;
1692 m_CurrentSubtitle.inited = false;
1693 m_CurrentTeletext.inited = false;
1696 if(current.dts != DVD_NOPTS_VALUE && pPacket->dts < current.dts && current.inited)
1698 /* warn if dts is moving backwords */
1699 CLog::Log(LOGWARNING, "CDVDPlayer::CheckContinuity - wrapback of stream:%d, prev:%f, curr:%f, diff:%f"
1700 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1705 bool CDVDPlayer::CheckSceneSkip(CCurrentStream& current)
1710 if(current.dts == DVD_NOPTS_VALUE)
1713 if(current.startpts != DVD_NOPTS_VALUE)
1717 return m_Edl.InCut(DVD_TIME_TO_MSEC(current.dts), &cut) && cut.action == CEdl::CUT;
1720 void CDVDPlayer::CheckAutoSceneSkip()
1726 * Check that there is an audio and video stream.
1728 if(m_CurrentAudio.id < 0
1729 || m_CurrentVideo.id < 0)
1733 * If there is a startpts defined for either the audio or video stream then dvdplayer is still
1734 * still decoding frames to get to the previously requested seek point.
1736 if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE
1737 || m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
1740 if(m_CurrentAudio.dts == DVD_NOPTS_VALUE
1741 || m_CurrentVideo.dts == DVD_NOPTS_VALUE)
1744 const int64_t clock = DVD_TIME_TO_MSEC(min(m_CurrentAudio.dts, m_CurrentVideo.dts));
1747 if(!m_Edl.InCut(clock, &cut))
1750 if(cut.action == CEdl::CUT
1751 && !(cut.end == m_EdlAutoSkipMarkers.cut || cut.start == m_EdlAutoSkipMarkers.cut)) // To prevent looping if same cut again
1753 CLog::Log(LOGDEBUG, "%s - Clock in EDL cut [%s - %s]: %s. Automatically skipping over.",
1754 __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(),
1755 CEdl::MillisecondsToTimeString(cut.end).c_str(), CEdl::MillisecondsToTimeString(clock).c_str());
1757 * Seeking either goes to the start or the end of the cut depending on the play direction.
1759 __int64 seek = GetPlaySpeed() >= 0 ? cut.end : cut.start;
1761 * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
1763 m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, true, false, true, false));
1765 * Seek doesn't always work reliably. Last physical seek time is recorded to prevent looping
1766 * if there was an error with seeking and it landed somewhere unexpected, perhaps back in the
1767 * cut. The cut automatic skip marker is reset every 500ms allowing another attempt at the seek.
1769 m_EdlAutoSkipMarkers.cut = GetPlaySpeed() >= 0 ? cut.end : cut.start;
1771 else if(cut.action == CEdl::COMM_BREAK
1772 && GetPlaySpeed() >= 0
1773 && cut.start > m_EdlAutoSkipMarkers.commbreak_end)
1775 CLog::Log(LOGDEBUG, "%s - Clock in commercial break [%s - %s]: %s. Automatically skipping to end of commercial break (only done once per break)",
1776 __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(), CEdl::MillisecondsToTimeString(cut.end).c_str(),
1777 CEdl::MillisecondsToTimeString(clock).c_str());
1779 * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
1781 m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false));
1783 * Each commercial break is only skipped once so poorly detected commercial breaks can be
1784 * manually re-entered. Start and end are recorded to prevent looping and to allow seeking back
1785 * to the start of the commercial break if incorrectly flagged.
1787 m_EdlAutoSkipMarkers.commbreak_start = cut.start;
1788 m_EdlAutoSkipMarkers.commbreak_end = cut.end;
1789 m_EdlAutoSkipMarkers.seek_to_start = true; // Allow backwards Seek() to go directly to the start
1793 * Reset the EDL automatic skip cut marker every 500 ms.
1795 m_EdlAutoSkipMarkers.ResetCutMarker(500); // in msec
1799 void CDVDPlayer::SynchronizeDemuxer(DWORD timeout)
1801 if(IsCurrentThread())
1803 if(!m_messenger.IsInited())
1806 CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, 0);
1807 m_messenger.Put(message->Acquire());
1808 message->Wait(&m_bStop, 0);
1812 void CDVDPlayer::SynchronizePlayers(DWORD sources)
1814 /* if we are awaiting a start sync, we can't sync here or we could deadlock */
1815 if(m_CurrentAudio.startsync
1816 || m_CurrentVideo.startsync
1817 || m_CurrentSubtitle.startsync
1818 || m_CurrentTeletext.startsync)
1820 CLog::Log(LOGDEBUG, "%s - can't sync since we are already awaiting a sync", __FUNCTION__);
1824 /* we need a big timeout as audio queue is about 8seconds for 2ch ac3 */
1825 const int timeout = 10*1000; // in milliseconds
1827 CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, sources);
1828 if (m_CurrentAudio.id >= 0)
1829 m_CurrentAudio.startsync = message->Acquire();
1831 if (m_CurrentVideo.id >= 0)
1832 m_CurrentVideo.startsync = message->Acquire();
1833 /* TODO - we have to rewrite the sync class, to not require
1834 all other players waiting for subtitle, should only
1836 if (m_CurrentSubtitle.id >= 0)
1837 m_CurrentSubtitle.startsync = message->Acquire();
1842 void CDVDPlayer::SendPlayerMessage(CDVDMsg* pMsg, unsigned int target)
1844 if(target == DVDPLAYER_AUDIO)
1845 m_dvdPlayerAudio.SendMessage(pMsg);
1846 if(target == DVDPLAYER_VIDEO)
1847 m_dvdPlayerVideo.SendMessage(pMsg);
1848 if(target == DVDPLAYER_SUBTITLE)
1849 m_dvdPlayerSubtitle.SendMessage(pMsg);
1850 if(target == DVDPLAYER_TELETEXT)
1851 m_dvdPlayerTeletext.SendMessage(pMsg);
1854 void CDVDPlayer::OnExit()
1856 g_dvdPerformanceCounter.DisableMainPerformance();
1860 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit()");
1862 // set event to inform openfile something went wrong in case openfile is still waiting for this event
1863 SetCaching(CACHESTATE_DONE);
1865 // close each stream
1866 if (!m_bAbortRequest) CLog::Log(LOGNOTICE, "DVDPlayer: eof, waiting for queues to empty");
1867 if (m_CurrentAudio.id >= 0)
1869 CLog::Log(LOGNOTICE, "DVDPlayer: closing audio stream");
1870 CloseAudioStream(!m_bAbortRequest);
1872 if (m_CurrentVideo.id >= 0)
1874 CLog::Log(LOGNOTICE, "DVDPlayer: closing video stream");
1875 CloseVideoStream(!m_bAbortRequest);
1877 if (m_CurrentSubtitle.id >= 0)
1879 CLog::Log(LOGNOTICE, "DVDPlayer: closing subtitle stream");
1880 CloseSubtitleStream(!m_bAbortRequest);
1882 if (m_CurrentTeletext.id >= 0)
1884 CLog::Log(LOGNOTICE, "DVDPlayer: closing teletext stream");
1885 CloseTeletextStream(!m_bAbortRequest);
1887 // destroy the demuxer
1890 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting demuxer");
1895 if (m_pSubtitleDemuxer)
1897 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting subtitle demuxer");
1898 delete m_pSubtitleDemuxer;
1900 m_pSubtitleDemuxer = NULL;
1902 // destroy the inputstream
1905 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting input stream");
1906 delete m_pInputStream;
1908 m_pInputStream = NULL;
1910 // clean up all selection streams
1911 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
1918 CLog::Log(LOGERROR, "%s - Exception thrown when trying to close down player, memory leak will follow", __FUNCTION__);
1919 m_pInputStream = NULL;
1924 // if we didn't stop playing, advance to the next item in xbmc's playlist
1925 if(m_PlayerOptions.identify == false)
1927 if (m_bAbortRequest)
1928 m_callback.OnPlayBackStopped();
1930 m_callback.OnPlayBackEnded();
1933 // set event to inform openfile something went wrong in case openfile is still waiting for this event
1937 void CDVDPlayer::HandleMessages()
1940 StreamLock lock(this);
1942 while (m_messenger.Get(&pMsg, 0) == MSGQ_OK)
1947 if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
1948 && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
1950 CDVDMsgPlayerSeek &msg(*((CDVDMsgPlayerSeek*)pMsg));
1952 if(!msg.GetTrickPlay())
1954 g_infoManager.SetDisplayAfterSeek(100000);
1956 SetCaching(CACHESTATE_FLUSH);
1959 double start = DVD_NOPTS_VALUE;
1961 int time = msg.GetRestore() ? (int)m_Edl.RestoreCutTime(msg.GetTime()) : msg.GetTime();
1962 CLog::Log(LOGDEBUG, "demuxer seek to: %d", time);
1963 if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start))
1965 CLog::Log(LOGDEBUG, "demuxer seek to: %d, success", time);
1966 if(m_pSubtitleDemuxer)
1968 if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward()))
1969 CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: %d, success", time);
1971 FlushBuffers(!msg.GetFlush(), start, msg.GetAccurate());
1974 CLog::Log(LOGWARNING, "error while seeking");
1976 // set flag to indicate we have finished a seeking request
1977 if(!msg.GetTrickPlay())
1979 g_infoManager.m_performingSeek = false;
1980 g_infoManager.SetDisplayAfterSeek();
1983 // dvd's will issue a HOP_CHANNEL that we need to skip
1984 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1985 m_dvd.state = DVDSTATE_SEEK;
1987 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
1988 && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
1990 g_infoManager.SetDisplayAfterSeek(100000);
1991 SetCaching(CACHESTATE_FLUSH);
1993 CDVDMsgPlayerSeekChapter &msg(*((CDVDMsgPlayerSeekChapter*)pMsg));
1994 double start = DVD_NOPTS_VALUE;
1996 // This should always be the case.
1997 if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
1999 FlushBuffers(false, start, true);
2000 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2003 g_infoManager.SetDisplayAfterSeek();
2005 else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
2007 m_CurrentAudio.stream = NULL;
2008 m_CurrentVideo.stream = NULL;
2009 m_CurrentSubtitle.stream = NULL;
2011 // we need to reset the demuxer, probably because the streams have changed
2013 m_pDemuxer->Reset();
2014 if(m_pSubtitleDemuxer)
2015 m_pSubtitleDemuxer->Reset();
2017 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
2019 CDVDMsgPlayerSetAudioStream* pMsg2 = (CDVDMsgPlayerSetAudioStream*)pMsg;
2021 SelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
2022 if(st.source != STREAM_SOURCE_NONE)
2024 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2026 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2027 if(pStream->SetActiveAudioStream(st.id))
2029 m_dvd.iSelectedAudioStream = -1;
2030 CloseAudioStream(false);
2031 m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true));
2036 CloseAudioStream(false);
2037 OpenAudioStream(st.id, st.source);
2038 m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true));
2042 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
2044 CDVDMsgPlayerSetSubtitleStream* pMsg2 = (CDVDMsgPlayerSetSubtitleStream*)pMsg;
2046 SelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
2047 if(st.source != STREAM_SOURCE_NONE)
2049 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2051 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2052 if(pStream->SetActiveSubtitleStream(st.id))
2054 m_dvd.iSelectedSPUStream = -1;
2055 CloseSubtitleStream(false);
2060 CloseSubtitleStream(false);
2061 OpenSubtitleStream(st.id, st.source);
2065 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
2067 CDVDMsgBool* pValue = (CDVDMsgBool*)pMsg;
2069 m_dvdPlayerVideo.EnableSubtitle(pValue->m_value);
2071 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2072 static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->EnableSubtitleStream(pValue->m_value);
2074 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
2076 g_infoManager.SetDisplayAfterSeek(100000);
2077 SetCaching(CACHESTATE_FLUSH);
2079 CDVDMsgPlayerSetState* pMsgPlayerSetState = (CDVDMsgPlayerSetState*)pMsg;
2081 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2083 std::string s = pMsgPlayerSetState->GetState();
2084 ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(s);
2085 m_dvd.state = DVDSTATE_NORMAL;
2086 m_dvd.iDVDStillStartTime = 0;
2087 m_dvd.iDVDStillTime = 0;
2090 g_infoManager.SetDisplayAfterSeek();
2092 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD))
2094 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
2095 static_cast<CDVDInputStreamTV*>(m_pInputStream)->Record(*(CDVDMsgBool*)pMsg);
2097 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
2099 FlushBuffers(false);
2101 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
2103 int speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
2105 // correct our current clock, as it would start going wrong otherwise
2106 if(m_State.timestamp > 0)
2109 offset = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2110 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2111 if(offset > 1000) offset = 1000;
2112 if(offset < -1000) offset = -1000;
2113 m_State.time += DVD_TIME_TO_MSEC(offset);
2114 m_State.timestamp = CDVDClock::GetAbsoluteClock();
2117 if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed)
2118 m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL);
2120 // if playspeed is different then DVD_PLAYSPEED_NORMAL or DVD_PLAYSPEED_PAUSE
2121 // audioplayer, stops outputing audio to audiorendere, but still tries to
2122 // sleep an correct amount for each packet
2123 // videoplayer just plays faster after the clock speed has been increased
2125 // 2. skip frames and adjust their pts or the clock
2126 m_playSpeed = speed;
2127 m_caching = CACHESTATE_DONE;
2128 m_clock.SetSpeed(speed);
2129 m_dvdPlayerAudio.SetSpeed(speed);
2130 m_dvdPlayerVideo.SetSpeed(speed);
2132 // TODO - we really shouldn't pause demuxer
2133 // until our buffers are somewhat filled
2135 m_pDemuxer->SetSpeed(speed);
2137 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) ||
2138 pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV) ||
2139 (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0))
2141 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2144 g_infoManager.SetDisplayAfterSeek(100000);
2147 if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT))
2148 result = input->SelectChannel(static_cast<CDVDMsgInt*>(pMsg)->m_value);
2149 else if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
2150 result = input->NextChannel();
2152 result = input->PrevChannel();
2156 FlushBuffers(false);
2157 SAFE_DELETE(m_pDemuxer);
2160 g_infoManager.SetDisplayAfterSeek();
2163 else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
2164 OnAction(((CDVDMsgType<CAction>*)pMsg)->m_value);
2165 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
2167 int player = ((CDVDMsgInt*)pMsg)->m_value;
2168 if(player == DVDPLAYER_AUDIO)
2169 m_CurrentAudio.started = true;
2170 if(player == DVDPLAYER_VIDEO)
2171 m_CurrentVideo.started = true;
2172 CLog::Log(LOGDEBUG, "CDVDPlayer::HandleMessages - player started %d", player);
2177 CLog::Log(LOGERROR, "%s - Exception thrown when handling message", __FUNCTION__);
2185 void CDVDPlayer::SetCaching(ECacheState state)
2187 if(state == CACHESTATE_FLUSH)
2189 double level, delay, offset;
2190 if(GetCachingTimes(level, delay, offset))
2191 state = CACHESTATE_FULL;
2193 state = CACHESTATE_INIT;
2196 if(m_caching == state)
2199 CLog::Log(LOGDEBUG, "CDVDPlayer::SetCaching - caching state %d", state);
2200 if(state == CACHESTATE_FULL
2201 || state == CACHESTATE_INIT)
2203 m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
2204 m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
2205 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2206 m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
2207 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2210 if(state == CACHESTATE_PLAY
2211 ||(state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY))
2213 m_clock.SetSpeed(m_playSpeed);
2214 m_dvdPlayerAudio.SetSpeed(m_playSpeed);
2215 m_dvdPlayerVideo.SetSpeed(m_playSpeed);
2220 void CDVDPlayer::SetPlaySpeed(int speed)
2222 m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed));
2223 m_dvdPlayerAudio.SetSpeed(speed);
2224 m_dvdPlayerVideo.SetSpeed(speed);
2225 SynchronizeDemuxer(100);
2228 void CDVDPlayer::Pause()
2230 if(m_playSpeed != DVD_PLAYSPEED_PAUSE && m_caching == CACHESTATE_FULL)
2232 SetCaching(CACHESTATE_DONE);
2236 // return to normal speed if it was paused before, pause otherwise
2237 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
2239 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2240 m_callback.OnPlayBackResumed();
2244 SetPlaySpeed(DVD_PLAYSPEED_PAUSE);
2245 m_callback.OnPlayBackPaused();
2249 bool CDVDPlayer::IsPaused() const
2251 return (m_playSpeed == DVD_PLAYSPEED_PAUSE) || m_caching == CACHESTATE_FULL;
2254 bool CDVDPlayer::HasVideo() const
2258 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || m_CurrentVideo.id >= 0) return true;
2263 bool CDVDPlayer::HasAudio() const
2265 return (m_CurrentAudio.id >= 0);
2268 bool CDVDPlayer::IsPassthrough() const
2270 return m_dvdPlayerAudio.IsPassthrough();
2273 bool CDVDPlayer::CanSeek()
2275 return GetTotalTime() > 0;
2278 void CDVDPlayer::Seek(bool bPlus, bool bLargeStep)
2281 // sadly this doesn't work for now, audio player must
2282 // drop packets at the same rate as we play frames
2283 if( m_playSpeed == DVD_PLAYSPEED_PAUSE && bPlus && !bLargeStep)
2285 m_dvdPlayerVideo.StepFrame();
2290 if(((bPlus && GetChapter() < GetChapterCount())
2291 || (!bPlus && GetChapter() > 1)) && bLargeStep)
2294 SeekChapter(GetChapter() + 1);
2296 SeekChapter(GetChapter() - 1);
2301 if (g_advancedSettings.m_videoUseTimeSeeking && GetTotalTime() > 2*g_advancedSettings.m_videoTimeSeekForwardBig)
2304 seek = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig;
2306 seek = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward;
2314 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig;
2316 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward;
2317 seek = (__int64)(GetTotalTimeInMsec()*(GetPercentage()+percent)/100);
2320 bool restore = true;
2324 * Alter the standard seek position based on whether any commercial breaks have been
2325 * automatically skipped.
2327 const int clock = DVD_TIME_TO_MSEC(m_clock.GetClock());
2329 * If a large backwards seek occurs within 10 seconds of the end of the last automated
2330 * commercial skip, then seek back to the start of the commercial break under the assumption
2331 * it was flagged incorrectly. 10 seconds grace period is allowed in case the watcher has to
2332 * fumble around finding the remote. Only happens once per commercial break.
2334 * Small skip does not trigger this in case the start of the commercial break was in fact fine
2335 * but it skipped too far into the program. In that case small skip backwards behaves as normal.
2337 if (!bPlus && bLargeStep
2338 && m_EdlAutoSkipMarkers.seek_to_start
2339 && clock >= m_EdlAutoSkipMarkers.commbreak_end
2340 && clock <= m_EdlAutoSkipMarkers.commbreak_end + 10*1000) // Only if within 10 seconds of the end (in msec)
2342 CLog::Log(LOGDEBUG, "%s - Seeking back to start of commercial break [%s - %s] as large backwards skip activated within 10 seconds of the automatic commercial skip (only done once per break).",
2343 __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
2344 CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
2345 seek = m_EdlAutoSkipMarkers.commbreak_start;
2347 m_EdlAutoSkipMarkers.seek_to_start = false; // So this will only happen within the 10 second grace period once.
2350 * If big skip forward within the last "reverted" commercial break, seek to the end of the
2351 * commercial break under the assumption that the break was incorrectly flagged and playback has
2352 * now reached the actual start of the commercial break. Assume that the end is flagged more
2353 * correctly than the landing point for a standard big skip (ends seem to be flagged more
2354 * accurately than the start).
2356 else if (bPlus && bLargeStep
2357 && clock >= m_EdlAutoSkipMarkers.commbreak_start
2358 && clock <= m_EdlAutoSkipMarkers.commbreak_end)
2360 CLog::Log(LOGDEBUG, "%s - Seeking to end of previously skipped commercial break [%s - %s] as big forwards skip activated within the break.",
2361 __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
2362 CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
2363 seek = m_EdlAutoSkipMarkers.commbreak_end;
2368 __int64 time = GetTime();
2369 if(g_application.CurrentFileItem().IsStack()
2370 && (seek > GetTotalTimeInMsec() || seek < 0))
2372 g_application.SeekTime((seek - time) * 0.001 + g_application.GetTime());
2373 // warning, don't access any dvdplayer variables here as
2374 // the dvdplayer object may have been destroyed
2378 m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, !bPlus, true, false, restore));
2379 SynchronizeDemuxer(100);
2380 if (seek < 0) seek = 0;
2381 m_callback.OnPlayBackSeek((int)seek, (int)(seek - time));
2384 bool CDVDPlayer::SeekScene(bool bPlus)
2386 if (!m_Edl.HasSceneMarker())
2390 * There is a 5 second grace period applied when seeking for scenes backwards. If there is no
2391 * grace period applied it is impossible to go backwards past a scene marker.
2393 __int64 clock = GetTime();
2394 if (!bPlus && clock > 5 * 1000) // 5 seconds
2397 int64_t iScenemarker;
2398 if (m_Edl.GetNextSceneMarker(bPlus, clock, &iScenemarker))
2401 * Seeking is flushed and inaccurate, just like Seek()
2403 m_messenger.Put(new CDVDMsgPlayerSeek((int)iScenemarker, !bPlus, true, false, false));
2404 SynchronizeDemuxer(100);
2410 void CDVDPlayer::GetAudioInfo(CStdString& strAudioInfo)
2412 CSingleLock lock(m_StateSection);
2413 strAudioInfo.Format("D(%s) P(%s)", m_State.demux_audio.c_str()
2414 , m_dvdPlayerAudio.GetPlayerInfo().c_str());
2417 void CDVDPlayer::GetVideoInfo(CStdString& strVideoInfo)
2419 CSingleLock lock(m_StateSection);
2420 strVideoInfo.Format("D(%s) P(%s)", m_State.demux_video.c_str()
2421 , m_dvdPlayerVideo.GetPlayerInfo().c_str());
2424 void CDVDPlayer::GetGeneralInfo(CStdString& strGeneralInfo)
2428 double dDelay = (double)m_dvdPlayerVideo.GetDelay() / DVD_TIME_BASE;
2430 double apts = m_dvdPlayerAudio.GetCurrentPts();
2431 double vpts = m_dvdPlayerVideo.GetCurrentPts();
2434 if( apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE )
2435 dDiff = (apts - vpts) / DVD_TIME_BASE;
2438 strEDL.AppendFormat(", edl:%s", m_Edl.GetInfo().c_str());
2441 CSingleLock lock(m_StateSection);
2442 if(m_State.cache_bytes >= 0)
2444 strBuf.AppendFormat(" cache:%s %2.0f%%"
2445 , StringUtils::SizeToString(m_State.cache_bytes).c_str()
2446 , m_State.cache_level * 100);
2447 if(m_playSpeed == 0 || m_caching == CACHESTATE_FULL)
2448 strBuf.AppendFormat(" %d sec", DVD_TIME_TO_SEC(m_State.cache_delay));
2451 strGeneralInfo.Format("C( ad:% 6.3f, a/v:% 6.3f%s, dcpu:%2i%% acpu:%2i%% vcpu:%2i%%%s )"
2455 , (int)(CThread::GetRelativeUsage()*100)
2456 , (int)(m_dvdPlayerAudio.GetRelativeUsage()*100)
2457 , (int)(m_dvdPlayerVideo.GetRelativeUsage()*100)
2463 void CDVDPlayer::SeekPercentage(float iPercent)
2465 __int64 iTotalTime = GetTotalTimeInMsec();
2470 SeekTime((__int64)(iTotalTime * iPercent / 100));
2473 float CDVDPlayer::GetPercentage()
2475 __int64 iTotalTime = GetTotalTimeInMsec();
2480 return GetTime() * 100 / (float)iTotalTime;
2483 float CDVDPlayer::GetCachePercentage()
2485 CSingleLock lock(m_StateSection);
2486 return min(100.0, GetPercentage() + m_State.cache_offset * 100);
2489 void CDVDPlayer::SetAVDelay(float fValue)
2491 m_dvdPlayerVideo.SetDelay( (fValue * DVD_TIME_BASE) ) ;
2494 float CDVDPlayer::GetAVDelay()
2496 return m_dvdPlayerVideo.GetDelay() / (float)DVD_TIME_BASE;
2499 void CDVDPlayer::SetSubTitleDelay(float fValue)
2501 m_dvdPlayerVideo.SetSubtitleDelay(-fValue * DVD_TIME_BASE);
2504 float CDVDPlayer::GetSubTitleDelay()
2506 return -m_dvdPlayerVideo.GetSubtitleDelay() / DVD_TIME_BASE;
2509 // priority: 1: libdvdnav, 2: external subtitles, 3: muxed subtitles
2510 int CDVDPlayer::GetSubtitleCount()
2512 StreamLock lock(this);
2513 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
2514 return m_SelectionStreams.Count(STREAM_SUBTITLE);
2517 int CDVDPlayer::GetSubtitle()
2519 return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, *this);
2522 void CDVDPlayer::GetSubtitleName(int iStream, CStdString &strStreamName)
2525 SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream);
2526 if(s.name.length() > 0)
2527 strStreamName = s.name;
2529 strStreamName = g_localizeStrings.Get(13205); // Unknown
2531 if(s.type == STREAM_NONE)
2532 strStreamName += "(Invalid)";
2535 void CDVDPlayer::GetSubtitleLanguage(int iStream, CStdString &strStreamLang)
2537 SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream);
2538 if (!g_LangCodeExpander.Lookup(strStreamLang, s.language))
2539 strStreamLang = g_localizeStrings.Get(13205); // Unknown
2542 void CDVDPlayer::SetSubtitle(int iStream)
2544 m_messenger.Put(new CDVDMsgPlayerSetSubtitleStream(iStream));
2547 bool CDVDPlayer::GetSubtitleVisible()
2549 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2551 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2552 if(pStream->IsInMenu())
2553 return g_settings.m_currentVideoSettings.m_SubtitleOn;
2555 return pStream->IsSubtitleStreamEnabled();
2558 return m_dvdPlayerVideo.IsSubtitleEnabled();
2561 void CDVDPlayer::SetSubtitleVisible(bool bVisible)
2563 g_settings.m_currentVideoSettings.m_SubtitleOn = bVisible;
2564 m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
2567 int CDVDPlayer::GetAudioStreamCount()
2569 StreamLock lock(this);
2570 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
2571 return m_SelectionStreams.Count(STREAM_AUDIO);
2574 int CDVDPlayer::GetAudioStream()
2576 return m_SelectionStreams.IndexOf(STREAM_AUDIO, *this);
2579 void CDVDPlayer::GetAudioStreamName(int iStream, CStdString& strStreamName)
2582 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream);
2583 if(s.name.length() > 0)
2584 strStreamName += s.name;
2586 strStreamName += "Unknown";
2588 if(s.type == STREAM_NONE)
2589 strStreamName += " (Invalid)";
2592 void CDVDPlayer::SetAudioStream(int iStream)
2594 m_messenger.Put(new CDVDMsgPlayerSetAudioStream(iStream));
2595 SynchronizeDemuxer(100);
2598 TextCacheStruct_t* CDVDPlayer::GetTeletextCache()
2600 if (m_CurrentTeletext.id < 0)
2603 return m_dvdPlayerTeletext.GetTeletextCache();
2606 void CDVDPlayer::LoadPage(int p, int sp, unsigned char* buffer)
2608 if (m_CurrentTeletext.id < 0)
2611 return m_dvdPlayerTeletext.LoadPage(p, sp, buffer);
2614 void CDVDPlayer::SeekTime(__int64 iTime)
2616 int seekOffset = (int)(iTime - GetTime());
2617 m_messenger.Put(new CDVDMsgPlayerSeek((int)iTime, true, true, true));
2618 SynchronizeDemuxer(100);
2619 m_callback.OnPlayBackSeek((int)iTime, seekOffset);
2622 // return the time in milliseconds
2623 __int64 CDVDPlayer::GetTime()
2625 CSingleLock lock(m_StateSection);
2627 if(m_State.timestamp > 0)
2629 offset = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2630 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2631 if(offset > 1000) offset = 1000;
2632 if(offset < -1000) offset = -1000;
2634 return llrint(m_State.time + DVD_TIME_TO_MSEC(offset));
2637 // return length in msec
2638 __int64 CDVDPlayer::GetTotalTimeInMsec()
2640 CSingleLock lock(m_StateSection);
2641 return llrint(m_State.time_total);
2644 // return length in seconds.. this should be changed to return in milleseconds throughout xbmc
2645 int CDVDPlayer::GetTotalTime()
2647 return (int)(GetTotalTimeInMsec() / 1000);
2650 void CDVDPlayer::ToFFRW(int iSpeed)
2652 // can't rewind in menu as seeking isn't possible
2654 if (iSpeed < 0 && IsInMenu()) return;
2655 SetPlaySpeed(iSpeed * DVD_PLAYSPEED_NORMAL);
2658 bool CDVDPlayer::OpenAudioStream(int iStream, int source)
2660 CLog::Log(LOGNOTICE, "Opening audio stream: %i source: %i", iStream, source);
2665 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2666 if (!pStream || pStream->disabled)
2669 if( m_CurrentAudio.id < 0 && m_CurrentVideo.id >= 0 )
2671 // up until now we wheren't playing audio, but we did play video
2672 // this will change what is used to sync the dvdclock.
2673 // since the new audio data doesn't have to have any relation
2674 // to the current video data in the packet que, we have to
2675 // wait for it to empty
2677 // this happens if a new cell has audio data, but previous didn't
2678 // and both have video data
2680 SynchronizePlayers(SYNCSOURCE_AUDIO);
2683 CDVDStreamInfo hint(*pStream, true);
2685 if(m_CurrentAudio.id < 0
2686 || m_CurrentAudio.hint != hint)
2688 if (!m_dvdPlayerAudio.OpenStream( hint ))
2690 /* mark stream as disabled, to disallaw further attempts*/
2691 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2692 pStream->disabled = true;
2693 pStream->SetDiscard(AVDISCARD_ALL);
2698 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2700 /* store information about stream */
2701 m_CurrentAudio.id = iStream;
2702 m_CurrentAudio.source = source;
2703 m_CurrentAudio.hint = hint;
2704 m_CurrentAudio.stream = (void*)pStream;
2705 m_CurrentAudio.started = false;
2707 /* we are potentially going to be waiting on this */
2708 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2710 /* audio normally won't consume full cpu, so let it have prio */
2711 m_dvdPlayerAudio.SetPriority(GetThreadPriority(*this)+1);
2716 bool CDVDPlayer::OpenVideoStream(int iStream, int source)
2718 CLog::Log(LOGNOTICE, "Opening video stream: %i source: %i", iStream, source);
2723 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2724 if(!pStream || pStream->disabled)
2726 pStream->SetDiscard(AVDISCARD_NONE);
2728 CDVDStreamInfo hint(*pStream, true);
2730 if( m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) )
2732 /* set aspect ratio as requested by navigator for dvd's */
2733 float aspect = static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->GetVideoAspectRatio();
2735 hint.aspect = aspect;
2736 hint.software = true;
2737 hint.stills = static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->IsInMenu();
2740 if(m_CurrentVideo.id < 0
2741 || m_CurrentVideo.hint != hint)
2743 if (!m_dvdPlayerVideo.OpenStream(hint))
2745 /* mark stream as disabled, to disallaw further attempts */
2746 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2747 pStream->disabled = true;
2748 pStream->SetDiscard(AVDISCARD_ALL);
2753 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2755 /* store information about stream */
2756 m_CurrentVideo.id = iStream;
2757 m_CurrentVideo.source = source;
2758 m_CurrentVideo.hint = hint;
2759 m_CurrentVideo.stream = (void*)pStream;
2760 m_CurrentVideo.started = false;
2762 /* we are potentially going to be waiting on this */
2763 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2765 #if defined(__APPLE__)
2766 // Apple thread scheduler works a little different than Linux. It
2767 // will favor OS GUI side and can cause DVDPlayerVideo to miss frame
2768 // updates when the OS gets busy. Apple's recomended method is to
2769 // elevate time critical threads to SCHED_RR and OSX does this for
2770 // the CoreAudio audio device handler thread. We do the same for
2771 // the DVDPlayerVideo thread so it can run to sleep without getting
2772 // swapped out by a busy OS.
2773 m_dvdPlayerVideo.SetPrioritySched_RR();
2775 /* use same priority for video thread as demuxing thread, as */
2776 /* otherwise demuxer will starve if video consumes the full cpu */
2777 m_dvdPlayerVideo.SetPriority(GetThreadPriority(*this));
2783 bool CDVDPlayer::OpenSubtitleStream(int iStream, int source)
2785 CLog::Log(LOGNOTICE, "Opening Subtitle stream: %i source: %i", iStream, source);
2787 CDemuxStream* pStream = NULL;
2788 std::string filename;
2789 CDVDStreamInfo hint;
2791 if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
2793 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2796 SelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
2798 if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename)
2800 CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str());
2801 auto_ptr<CDVDDemuxVobsub> demux(new CDVDDemuxVobsub());
2802 if(!demux->Open(st.filename))
2804 m_pSubtitleDemuxer = demux.release();
2807 pStream = m_pSubtitleDemuxer->GetStream(iStream);
2808 if(!pStream || pStream->disabled)
2810 pStream->SetDiscard(AVDISCARD_NONE);
2811 double pts = m_dvdPlayerVideo.GetCurrentPts();
2812 if(pts == DVD_NOPTS_VALUE)
2813 pts = m_CurrentVideo.dts;
2814 if(pts == DVD_NOPTS_VALUE)
2816 m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE));
2818 hint.Assign(*pStream, true);
2820 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
2822 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2825 filename = m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename;
2828 hint.fpsscale = m_CurrentVideo.hint.fpsscale;
2829 hint.fpsrate = m_CurrentVideo.hint.fpsrate;
2835 pStream = m_pDemuxer->GetStream(iStream);
2836 if(!pStream || pStream->disabled)
2838 pStream->SetDiscard(AVDISCARD_NONE);
2840 hint.Assign(*pStream, true);
2842 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2846 if(m_CurrentSubtitle.id < 0
2847 || m_CurrentSubtitle.hint != hint)
2849 if(m_CurrentSubtitle.id >= 0)
2851 CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream");
2852 CloseSubtitleStream(false);
2855 if(!m_dvdPlayerSubtitle.OpenStream(hint, filename))
2857 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2860 pStream->disabled = true;
2861 pStream->SetDiscard(AVDISCARD_ALL);
2867 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2869 m_CurrentSubtitle.id = iStream;
2870 m_CurrentSubtitle.source = source;
2871 m_CurrentSubtitle.hint = hint;
2872 m_CurrentSubtitle.stream = (void*)pStream;
2873 m_CurrentSubtitle.started = false;
2878 bool CDVDPlayer::OpenTeletextStream(int iStream, int source)
2883 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2884 if(!pStream || pStream->disabled)
2887 CDVDStreamInfo hint(*pStream, true);
2889 if (!m_dvdPlayerTeletext.CheckStream(hint))
2892 CLog::Log(LOGNOTICE, "Opening teletext stream: %i source: %i", iStream, source);
2894 if(m_CurrentTeletext.id < 0
2895 || m_CurrentTeletext.hint != hint)
2897 if(m_CurrentTeletext.id >= 0)
2899 CLog::Log(LOGDEBUG, " - teletext codecs hints have changed, must close previous stream");
2900 CloseTeletextStream(true);
2903 if (!m_dvdPlayerTeletext.OpenStream(hint))
2905 /* mark stream as disabled, to disallaw further attempts*/
2906 CLog::Log(LOGWARNING, "%s - Unsupported teletext stream %d. Stream disabled.", __FUNCTION__, iStream);
2907 pStream->disabled = true;
2908 pStream->SetDiscard(AVDISCARD_ALL);
2913 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2915 /* store information about stream */
2916 m_CurrentTeletext.id = iStream;
2917 m_CurrentTeletext.source = source;
2918 m_CurrentTeletext.hint = hint;
2919 m_CurrentTeletext.stream = (void*)pStream;
2920 m_CurrentTeletext.started = false;
2925 bool CDVDPlayer::CloseAudioStream(bool bWaitForBuffers)
2927 if (m_CurrentAudio.id < 0)
2930 CLog::Log(LOGNOTICE, "Closing audio stream");
2933 SetCaching(CACHESTATE_DONE);
2935 m_dvdPlayerAudio.CloseStream(bWaitForBuffers);
2937 m_CurrentAudio.Clear();
2941 bool CDVDPlayer::CloseVideoStream(bool bWaitForBuffers)
2943 if (m_CurrentVideo.id < 0)
2946 CLog::Log(LOGNOTICE, "Closing video stream");
2949 SetCaching(CACHESTATE_DONE);
2951 m_dvdPlayerVideo.CloseStream(bWaitForBuffers);
2953 m_CurrentVideo.Clear();
2957 bool CDVDPlayer::CloseSubtitleStream(bool bKeepOverlays)
2959 if (m_CurrentSubtitle.id < 0)
2962 CLog::Log(LOGNOTICE, "Closing subtitle stream");
2964 m_dvdPlayerSubtitle.CloseStream(!bKeepOverlays);
2966 m_CurrentSubtitle.Clear();
2970 bool CDVDPlayer::CloseTeletextStream(bool bWaitForBuffers)
2972 if (m_CurrentTeletext.id < 0)
2975 CLog::Log(LOGNOTICE, "Closing teletext stream");
2978 SetCaching(CACHESTATE_DONE);
2980 m_dvdPlayerTeletext.CloseStream(bWaitForBuffers);
2982 m_CurrentTeletext.Clear();
2986 void CDVDPlayer::FlushBuffers(bool queued, double pts, bool accurate)
2992 startpts = DVD_NOPTS_VALUE;
2994 m_CurrentAudio.inited = false;
2995 m_CurrentAudio.dts = DVD_NOPTS_VALUE;
2996 m_CurrentAudio.startpts = startpts;
2998 m_CurrentVideo.inited = false;
2999 m_CurrentVideo.dts = DVD_NOPTS_VALUE;
3000 m_CurrentVideo.startpts = startpts;
3002 m_CurrentSubtitle.inited = false;
3003 m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
3004 m_CurrentSubtitle.startpts = startpts;
3006 m_CurrentTeletext.inited = false;
3007 m_CurrentTeletext.dts = DVD_NOPTS_VALUE;
3008 m_CurrentTeletext.startpts = startpts;
3012 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3013 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3014 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
3015 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3016 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3017 SynchronizePlayers(SYNCSOURCE_ALL);
3021 m_dvdPlayerAudio.Flush();
3022 m_dvdPlayerVideo.Flush();
3023 m_dvdPlayerSubtitle.Flush();
3024 m_dvdPlayerTeletext.Flush();
3026 // clear subtitle and menu overlays
3027 m_overlayContainer.Clear();
3029 if(m_playSpeed == DVD_PLAYSPEED_NORMAL
3030 || m_playSpeed == DVD_PLAYSPEED_PAUSE)
3032 // make sure players are properly flushed, should put them in stalled state
3033 CDVDMsgGeneralSynchronize* msg = new CDVDMsgGeneralSynchronize(1000, 0);
3034 m_dvdPlayerAudio.m_messageQueue.Put(msg->Acquire(), 1);
3035 m_dvdPlayerVideo.m_messageQueue.Put(msg->Acquire(), 1);
3036 msg->Wait(&m_bStop, 0);
3039 // purge any pending PLAYER_STARTED messages
3040 m_messenger.Flush(CDVDMsg::PLAYER_STARTED);
3042 // we should now wait for init cache
3043 SetCaching(CACHESTATE_FLUSH);
3044 m_CurrentAudio.started = false;
3045 m_CurrentVideo.started = false;
3046 m_CurrentSubtitle.started = false;
3047 m_CurrentTeletext.started = false;
3050 if(pts != DVD_NOPTS_VALUE)
3051 m_clock.Discontinuity(pts);
3056 // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
3057 int CDVDPlayer::OnDVDNavResult(void* pData, int iMessage)
3059 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3061 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
3065 case DVDNAV_STILL_FRAME:
3067 //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
3069 dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)pData;
3070 // should wait the specified time here while we let the player running
3071 // after that call dvdnav_still_skip(m_dvdnav);
3073 if (m_dvd.state != DVDSTATE_STILL)
3075 // else notify the player we have received a still frame
3077 if(still_event->length < 0xff)
3078 m_dvd.iDVDStillTime = still_event->length * 1000;
3080 m_dvd.iDVDStillTime = 0;
3082 m_dvd.iDVDStillStartTime = XbmcThreads::SystemClockMillis();
3084 /* adjust for the output delay in the video queue */
3086 if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 )
3088 time = (DWORD)(m_dvdPlayerVideo.GetOutputDelay() / ( DVD_TIME_BASE / 1000 ));
3089 if( time < 10000 && time > 0 )
3090 m_dvd.iDVDStillTime += time;
3092 m_dvd.state = DVDSTATE_STILL;
3094 "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec",
3095 still_event->length, time / 1000);
3097 return NAVRESULT_HOLD;
3100 case DVDNAV_SPU_CLUT_CHANGE:
3102 m_dvdPlayerSubtitle.SendMessage(new CDVDMsgSubtitleClutChange((BYTE*)pData));
3105 case DVDNAV_SPU_STREAM_CHANGE:
3107 dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)pData;
3109 int iStream = event->physical_wide;
3110 bool visible = !(iStream & 0x80);
3112 m_dvdPlayerVideo.EnableSubtitle(visible);
3115 m_dvd.iSelectedSPUStream = (iStream & ~0x80);
3117 m_dvd.iSelectedSPUStream = -1;
3119 m_CurrentSubtitle.stream = NULL;
3122 case DVDNAV_AUDIO_STREAM_CHANGE:
3124 // This should be the correct way i think, however we don't have any streams right now
3125 // since the demuxer hasn't started so it doesn't change. not sure how to do this.
3126 dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)pData;
3128 // Tell system what audiostream should be opened by default
3129 if (event->logical >= 0)
3130 m_dvd.iSelectedAudioStream = event->physical;
3132 m_dvd.iSelectedAudioStream = -1;
3134 m_CurrentAudio.stream = NULL;
3137 case DVDNAV_HIGHLIGHT:
3139 //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
3140 int iButton = pStream->GetCurrentButton();
3141 CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button %d\n", iButton);
3142 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
3145 case DVDNAV_VTS_CHANGE:
3147 //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
3148 CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
3150 //Make sure we clear all the old overlays here, or else old forced items are left.
3151 m_overlayContainer.Clear();
3153 // reset the demuxer, this also imples closing the video and the audio system
3154 // this is a bit tricky cause it's the demuxer that's is making this call in the end
3155 // so we send a message to indicate the main loop that the demuxer needs a reset
3156 // this also means the libdvdnav may not return any data packets after this command
3157 m_messenger.Put(new CDVDMsgDemuxerReset());
3159 //Force an aspect ratio that is set in the dvdheaders if available
3160 m_CurrentVideo.hint.aspect = pStream->GetVideoAspectRatio();
3161 if( m_dvdPlayerAudio.m_messageQueue.IsInited() )
3162 m_dvdPlayerVideo.SendMessage(new CDVDMsgDouble(CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
3164 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
3165 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
3167 // we must hold here once more, otherwise the demuxer message
3168 // will be executed after demuxer have filled with data
3169 return NAVRESULT_HOLD;
3172 case DVDNAV_CELL_CHANGE:
3174 //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
3175 CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
3177 m_dvd.state = DVDSTATE_NORMAL;
3179 if( m_dvdPlayerVideo.m_messageQueue.IsInited() )
3180 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
3183 case DVDNAV_NAV_PACKET:
3185 //pci_t* pci = (pci_t*)pData;
3187 // this should be possible to use to make sure we get
3188 // seamless transitions over these boundaries
3189 // if we remember the old vobunits boundaries
3190 // when a packet comes out of demuxer that has
3191 // pts values outside that boundary, it belongs
3192 // to the new vobunit, wich has new timestamps
3196 case DVDNAV_HOP_CHANNEL:
3198 // This event is issued whenever a non-seamless operation has been executed.
3199 // Applications with fifos should drop the fifos content to speed up responsiveness.
3200 CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
3201 if(m_dvd.state == DVDSTATE_SEEK)
3202 m_dvd.state = DVDSTATE_NORMAL;
3204 m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
3206 return NAVRESULT_ERROR;
3211 CLog::Log(LOGDEBUG, "DVDNAV_STOP");
3212 m_dvd.state = DVDSTATE_NORMAL;
3220 return NAVRESULT_NOP;
3223 bool CDVDPlayer::OnAction(const CAction &action)
3225 #define THREAD_ACTION(action) \
3227 if (!IsCurrentThread()) { \
3228 m_messenger.Put(new CDVDMsgType<CAction>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
3233 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3235 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
3238 if( m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0 && pStream->GetTotalButtons() == 0 )
3240 switch(action.GetID())
3242 case ACTION_NEXT_ITEM:
3243 case ACTION_MOVE_RIGHT:
3244 case ACTION_MOVE_UP:
3245 case ACTION_SELECT_ITEM:
3247 THREAD_ACTION(action);
3248 /* this will force us out of the stillframe */
3249 CLog::Log(LOGDEBUG, "%s - User asked to exit stillframe", __FUNCTION__);
3250 m_dvd.iDVDStillStartTime = 0;
3251 m_dvd.iDVDStillTime = 1;
3258 switch (action.GetID())
3260 /* this code is disabled to allow switching playlist items (dvdimage "stacks") */
3262 case ACTION_PREV_ITEM: // SKIP-:
3264 THREAD_ACTION(action);
3265 CLog::Log(LOGDEBUG, " - pushed prev");
3266 pStream->OnPrevious();
3267 g_infoManager.SetDisplayAfterSeek();
3271 case ACTION_NEXT_ITEM: // SKIP+:
3273 THREAD_ACTION(action);
3274 CLog::Log(LOGDEBUG, " - pushed next");
3276 g_infoManager.SetDisplayAfterSeek();
3281 case ACTION_SHOW_VIDEOMENU: // start button
3283 THREAD_ACTION(action);
3284 CLog::Log(LOGDEBUG, " - go to menu");
3286 // send a message to everyone that we've gone to the menu
3287 CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0);
3288 g_windowManager.SendMessage(msg);
3294 if (pStream->IsInMenu())
3296 switch (action.GetID())
3298 case ACTION_NEXT_ITEM:
3299 case ACTION_PAGE_UP:
3300 THREAD_ACTION(action);
3301 CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide");
3303 g_infoManager.SetDisplayAfterSeek();
3305 case ACTION_PREV_ITEM:
3306 case ACTION_PAGE_DOWN:
3307 THREAD_ACTION(action);
3308 CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
3309 pStream->OnPrevious();
3310 g_infoManager.SetDisplayAfterSeek();
3312 case ACTION_PREVIOUS_MENU:
3313 case ACTION_NAV_BACK:
3315 THREAD_ACTION(action);
3316 CLog::Log(LOGDEBUG, " - menu back");
3320 case ACTION_MOVE_LEFT:
3322 THREAD_ACTION(action);
3323 CLog::Log(LOGDEBUG, " - move left");
3327 case ACTION_MOVE_RIGHT:
3329 THREAD_ACTION(action);
3330 CLog::Log(LOGDEBUG, " - move right");
3334 case ACTION_MOVE_UP:
3336 THREAD_ACTION(action);
3337 CLog::Log(LOGDEBUG, " - move up");
3341 case ACTION_MOVE_DOWN:
3343 THREAD_ACTION(action);
3344 CLog::Log(LOGDEBUG, " - move down");
3349 case ACTION_MOUSE_MOVE:
3350 case ACTION_MOUSE_LEFT_CLICK:
3353 GetVideoRect(rs, rd);
3354 CPoint pt(action.GetAmount(), action.GetAmount(1));
3355 if (!rd.PtInRect(pt))
3356 return false; // out of bounds
3357 THREAD_ACTION(action);
3358 // convert to video coords...
3359 pt -= CPoint(rd.x1, rd.y1);
3360 pt.x *= rs.Width() / rd.Width();
3361 pt.y *= rs.Height() / rd.Height();
3362 pt += CPoint(rs.x1, rs.y1);
3363 if (action.GetID() == ACTION_MOUSE_LEFT_CLICK)
3364 return pStream->OnMouseClick(pt);
3365 return pStream->OnMouseMove(pt);
3368 case ACTION_SELECT_ITEM:
3370 THREAD_ACTION(action);
3371 CLog::Log(LOGDEBUG, " - button select");
3372 // show button pushed overlay
3373 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_CLICKED);
3375 pStream->ActivateButton();
3389 THREAD_ACTION(action);
3390 // Offset from key codes back to button number
3391 int button = action.GetID() - REMOTE_0;
3392 CLog::Log(LOGDEBUG, " - button pressed %d", button);
3393 pStream->SelectButton(button);
3400 return true; // message is handled
3404 if (dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream))
3406 switch (action.GetID())
3408 case ACTION_NEXT_ITEM:
3409 case ACTION_PAGE_UP:
3410 m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT));
3411 g_infoManager.SetDisplayAfterSeek();
3415 case ACTION_PREV_ITEM:
3416 case ACTION_PAGE_DOWN:
3417 m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV));
3418 g_infoManager.SetDisplayAfterSeek();
3422 case ACTION_CHANNEL_SWITCH:
3424 // Offset from key codes back to button number
3425 int channel = action.GetAmount();
3426 m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT, channel));
3427 g_infoManager.SetDisplayAfterSeek();
3434 switch (action.GetID())
3436 case ACTION_NEXT_ITEM:
3437 case ACTION_PAGE_UP:
3438 if(GetChapterCount() > 0)
3440 m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()+1));
3441 g_infoManager.SetDisplayAfterSeek();
3446 case ACTION_PREV_ITEM:
3447 case ACTION_PAGE_DOWN:
3448 if(GetChapterCount() > 0)
3450 m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()-1));
3451 g_infoManager.SetDisplayAfterSeek();
3458 // return false to inform the caller we didn't handle the message
3462 bool CDVDPlayer::IsInMenu() const
3464 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3466 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
3467 if( m_dvd.state == DVDSTATE_STILL )
3470 return pStream->IsInMenu();
3475 bool CDVDPlayer::HasMenu()
3477 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3483 bool CDVDPlayer::GetCurrentSubtitle(CStdString& strSubtitle)
3485 double pts = m_clock.GetClock();
3487 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3490 m_dvdPlayerSubtitle.GetCurrentSubtitle(strSubtitle, pts - m_dvdPlayerVideo.GetSubtitleDelay());
3492 // In case we stalled, don't output any subs
3493 if (m_dvdPlayerVideo.IsStalled() || m_dvdPlayerAudio.IsStalled())
3494 strSubtitle = m_lastSub;
3496 m_lastSub = strSubtitle;
3498 return !strSubtitle.IsEmpty();
3501 CStdString CDVDPlayer::GetPlayerState()
3503 CSingleLock lock(m_StateSection);
3504 return m_State.player_state;
3507 bool CDVDPlayer::SetPlayerState(CStdString state)
3509 m_messenger.Put(new CDVDMsgPlayerSetState(state));
3513 int CDVDPlayer::GetChapterCount()
3515 CSingleLock lock(m_StateSection);
3516 return m_State.chapter_count;
3519 int CDVDPlayer::GetChapter()
3521 CSingleLock lock(m_StateSection);
3522 return m_State.chapter;
3525 void CDVDPlayer::GetChapterName(CStdString& strChapterName)
3527 CSingleLock lock(m_StateSection);
3528 strChapterName = m_State.chapter_name;
3531 int CDVDPlayer::SeekChapter(int iChapter)
3533 if (GetChapterCount() > 0)
3537 if (iChapter > GetChapterCount())
3540 // Seek to the chapter.
3541 m_messenger.Put(new CDVDMsgPlayerSeekChapter(iChapter));
3542 SynchronizeDemuxer(100);
3546 // Do a regular big jump.
3547 if (GetChapter() > 0 && iChapter > GetChapter())
3555 int CDVDPlayer::AddSubtitle(const CStdString& strSubPath)
3557 return AddSubtitleFile(strSubPath);
3560 int CDVDPlayer::GetCacheLevel() const
3562 CSingleLock lock(m_StateSection);
3563 return (int)(m_State.cache_level * 100);
3566 double CDVDPlayer::GetQueueTime()
3568 int a = m_dvdPlayerAudio.m_messageQueue.GetLevel();
3569 int v = m_dvdPlayerVideo.m_messageQueue.GetLevel();
3570 return max(a, v) * 8000.0 / 100;
3573 int CDVDPlayer::GetAudioBitrate()
3575 return m_dvdPlayerAudio.GetAudioBitrate();
3578 int CDVDPlayer::GetVideoBitrate()
3580 return m_dvdPlayerVideo.GetVideoBitrate();
3583 int CDVDPlayer::GetSourceBitrate()
3586 return (int)m_pInputStream->GetBitstreamStats().GetBitrate();
3592 int CDVDPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename, CDemuxStream::EFlags flags)
3594 std::string ext = URIUtils::GetExtension(filename);
3595 std::string vobsubfile = subfilename;
3598 if (vobsubfile.empty())
3599 vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
3602 if(!v.Open(filename, vobsubfile))
3604 m_SelectionStreams.Update(NULL, &v);
3605 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename), 0);
3606 m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags;
3611 CStdString strReplace(URIUtils::ReplaceExtension(filename,".idx"));
3612 if (XFILE::CFile::Exists(strReplace))
3616 s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename);
3617 s.type = STREAM_SUBTITLE;
3619 s.filename = filename;
3620 s.name = URIUtils::GetFileName(filename);
3622 m_SelectionStreams.Update(s);
3623 return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, s.source, s.id);
3626 void CDVDPlayer::UpdatePlayState(double timeout)
3628 CSingleLock lock(m_StateSection);
3630 if(m_State.timestamp != 0
3631 && m_State.timestamp + DVD_MSEC_TO_TIME(timeout) > CDVDClock::GetAbsoluteClock())
3634 if (m_CurrentVideo.dts != DVD_NOPTS_VALUE)
3635 m_State.dts = m_CurrentVideo.dts;
3636 else if(m_CurrentAudio.dts != DVD_NOPTS_VALUE)
3637 m_State.dts = m_CurrentAudio.dts;
3639 m_State.dts = m_clock.GetClock();
3643 m_State.chapter = m_pDemuxer->GetChapter();
3644 m_State.chapter_count = m_pDemuxer->GetChapterCount();
3645 m_pDemuxer->GetChapterName(m_State.chapter_name);
3647 m_State.time = DVD_TIME_TO_MSEC(m_clock.GetClock());
3648 m_State.time_total = m_pDemuxer->GetStreamLength();
3653 // override from input stream if needed
3655 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
3657 m_State.canrecord = static_cast<CDVDInputStreamTV*>(m_pInputStream)->CanRecord();
3658 m_State.recording = static_cast<CDVDInputStreamTV*>(m_pInputStream)->IsRecording();
3661 CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast<CDVDInputStream::IDisplayTime*>(m_pInputStream);
3664 m_State.time = pDisplayTime->GetTime();
3665 m_State.time_total = pDisplayTime->GetTotalTime();
3668 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3670 if(m_dvd.state == DVDSTATE_STILL)
3672 m_State.time = XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime;
3673 m_State.time_total = m_dvd.iDVDStillTime;
3676 if(!((CDVDInputStreamNavigator*)m_pInputStream)->GetNavigatorState(m_State.player_state))
3677 m_State.player_state = "";
3680 m_State.player_state = "";
3683 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
3685 if(((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime() > 0)
3687 m_State.time -= ((CDVDInputStreamTV*)m_pInputStream)->GetStartTime();
3688 m_State.time_total = ((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime();
3695 m_State.time = m_Edl.RemoveCutTime(llrint(m_State.time));
3696 m_State.time_total = m_Edl.RemoveCutTime(llrint(m_State.time_total));
3699 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3700 m_State.time_offset = DVD_MSEC_TO_TIME(m_State.time) - m_State.dts;
3702 m_State.time_offset = 0;
3704 if (m_CurrentAudio.id >= 0 && m_pDemuxer)
3706 CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentAudio.id);
3707 if (pStream && pStream->type == STREAM_AUDIO)
3708 ((CDemuxStreamAudio*)pStream)->GetStreamInfo(m_State.demux_audio);
3711 m_State.demux_audio = "";
3713 if (m_CurrentVideo.id >= 0 && m_pDemuxer)
3715 CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentVideo.id);
3716 if (pStream && pStream->type == STREAM_VIDEO)
3717 ((CDemuxStreamVideo*)pStream)->GetStreamInfo(m_State.demux_video);
3720 m_State.demux_video = "";
3722 double level, delay, offset;
3723 if(GetCachingTimes(level, delay, offset))
3725 m_State.cache_delay = max(0.0, delay);
3726 m_State.cache_level = max(0.0, min(1.0, level));
3727 m_State.cache_offset = offset;
3731 m_State.cache_delay = 0.0;
3732 m_State.cache_level = min(1.0, GetQueueTime() / 8000.0);
3733 m_State.cache_offset = GetQueueTime() / m_State.time_total;
3736 if(m_pInputStream && m_pInputStream->GetCachedBytes() >= 0)
3738 m_State.cache_bytes = m_pInputStream->GetCachedBytes();
3739 if(m_State.time_total)
3740 m_State.cache_bytes += m_pInputStream->GetLength() * GetQueueTime() / m_State.time_total;
3743 m_State.cache_bytes = 0;
3745 m_State.timestamp = CDVDClock::GetAbsoluteClock();
3748 void CDVDPlayer::UpdateApplication(double timeout)
3750 if(m_UpdateApplication != 0
3751 && m_UpdateApplication + DVD_MSEC_TO_TIME(timeout) > CDVDClock::GetAbsoluteClock())
3754 CDVDInputStream::IChannel* pStream = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
3757 CFileItem item(g_application.CurrentFileItem());
3758 if(pStream->UpdateItem(item))
3760 g_application.CurrentFileItem() = item;
3761 g_infoManager.SetCurrentItem(item);
3764 m_UpdateApplication = CDVDClock::GetAbsoluteClock();
3767 bool CDVDPlayer::CanRecord()
3769 CSingleLock lock(m_StateSection);
3770 return m_State.canrecord;
3773 bool CDVDPlayer::IsRecording()
3775 CSingleLock lock(m_StateSection);
3776 return m_State.recording;
3779 bool CDVDPlayer::Record(bool bOnOff)
3781 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
3783 m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff));
3789 int CDVDPlayer::GetChannels()
3791 if (m_pDemuxer && (m_CurrentAudio.id != -1))
3793 CDemuxStreamAudio* stream = static_cast<CDemuxStreamAudio*>(m_pDemuxer->GetStream(m_CurrentAudio.id));
3795 return stream->iChannels;
3800 CStdString CDVDPlayer::GetAudioCodecName()
3803 if (m_pDemuxer && (m_CurrentAudio.id != -1))
3804 m_pDemuxer->GetStreamCodecName(m_CurrentAudio.id, retVal);
3808 CStdString CDVDPlayer::GetVideoCodecName()
3811 if (m_pDemuxer && (m_CurrentVideo.id != -1))
3812 m_pDemuxer->GetStreamCodecName(m_CurrentVideo.id, retVal);
3816 int CDVDPlayer::GetPictureWidth()
3818 if (m_pDemuxer && (m_CurrentVideo.id != -1))
3820 CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
3822 return stream->iWidth;
3827 int CDVDPlayer::GetPictureHeight()
3829 if (m_pDemuxer && (m_CurrentVideo.id != -1))
3831 CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
3833 return stream->iHeight;
3838 bool CDVDPlayer::GetStreamDetails(CStreamDetails &details)
3842 bool result=CDVDFileInfo::DemuxerToStreamDetails(m_pInputStream, m_pDemuxer, details);
3843 if (result && ((CStreamDetailVideo*)details.GetStreamCount(CStreamDetail::VIDEO) > 0)) // this is more correct (dvds in particular)
3845 GetVideoAspectRatio(((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_fAspect);
3846 ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_iDuration = GetTotalTime();
3854 CStdString CDVDPlayer::GetPlayingTitle()
3856 /* Currently we support only Title Name from Teletext line 30 */
3857 TextCacheStruct_t* ttcache = m_dvdPlayerTeletext.GetTeletextCache();
3858 if (ttcache && !ttcache->line30.empty())
3859 return ttcache->line30;