2 * Copyright (C) 2005-2012 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "threads/SystemClock.h"
23 #include "DVDPlayer.h"
25 #include "DVDInputStreams/DVDInputStream.h"
26 #include "DVDInputStreams/DVDInputStreamFile.h"
27 #include "DVDInputStreams/DVDFactoryInputStream.h"
28 #include "DVDInputStreams/DVDInputStreamNavigator.h"
29 #include "DVDInputStreams/DVDInputStreamTV.h"
30 #include "DVDInputStreams/DVDInputStreamPVRManager.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 "ApplicationMessenger.h"
51 #include "DVDPerformanceCounter.h"
52 #include "filesystem/File.h"
53 #include "pictures/Picture.h"
54 #include "DllSwScale.h"
55 #ifdef HAS_VIDEO_PLAYBACK
56 #include "cores/VideoRenderers/RenderManager.h"
58 #ifdef HAS_PERFORMANCE_SAMPLE
59 #include "xbmc/utils/PerformanceSample.h"
61 #define MEASURE_FUNCTION
63 #include "settings/AdvancedSettings.h"
65 #include "settings/GUISettings.h"
66 #include "GUIUserMessages.h"
67 #include "settings/Settings.h"
68 #include "utils/log.h"
69 #include "utils/TimeUtils.h"
70 #include "utils/StreamDetails.h"
71 #include "pvr/PVRManager.h"
72 #include "pvr/channels/PVRChannel.h"
73 #include "pvr/windows/GUIWindowPVR.h"
74 #include "pvr/addons/PVRClients.h"
75 #include "filesystem/PVRFile.h"
76 #include "video/dialogs/GUIDialogFullScreenInfo.h"
77 #include "utils/StreamUtils.h"
78 #include "utils/Variant.h"
79 #include "storage/MediaManager.h"
80 #include "dialogs/GUIDialogBusy.h"
81 #include "dialogs/GUIDialogKaiToast.h"
82 #include "xbmc/playlists/PlayListM3U.h"
83 #include "utils/StringUtils.h"
86 #include "ApplicationMessenger.h"
91 void CSelectionStreams::Clear(StreamType type, StreamSource source)
93 CSingleLock lock(m_section);
94 for(int i=m_Streams.size()-1;i>=0;i--)
96 if(type && m_Streams[i].type != type)
99 if(source && m_Streams[i].source != source)
102 m_Streams.erase(m_Streams.begin() + i);
106 void CDVDPlayer::GetAudioStreamLanguage(int iStream, CStdString &strLanguage)
109 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream);
110 if(s.language.length() > 0)
111 strLanguage = s.language;
114 SelectionStream& CSelectionStreams::Get(StreamType type, int index)
116 CSingleLock lock(m_section);
118 for(int i=0;i<(int)m_Streams.size();i++)
120 if(m_Streams[i].type != type)
126 CLog::Log(LOGERROR, "%s - failed to get stream", __FUNCTION__);
130 std::vector<SelectionStream> CSelectionStreams::Get(StreamType type)
132 std::vector<SelectionStream> streams;
133 int count = Count(type);
134 for(int index = 0; index < count; ++index){
135 streams.push_back(Get(type, index));
140 #define PREDICATE_RETURN(lh, rh) \
143 return (lh) > (rh); \
146 static bool PredicateAudioPriority(const SelectionStream& lh, const SelectionStream& rh)
148 PREDICATE_RETURN(lh.type_index == g_settings.m_currentVideoSettings.m_AudioStream
149 , rh.type_index == g_settings.m_currentVideoSettings.m_AudioStream);
151 if(!g_guiSettings.GetString("locale.audiolanguage").Equals("original"))
153 CStdString audio_language = g_langInfo.GetAudioLanguage();
154 PREDICATE_RETURN(audio_language.Equals(lh.language.c_str())
155 , audio_language.Equals(rh.language.c_str()));
158 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
159 , rh.flags & CDemuxStream::FLAG_DEFAULT);
161 PREDICATE_RETURN(lh.channels
164 PREDICATE_RETURN(StreamUtils::GetCodecPriority(lh.codec)
165 , StreamUtils::GetCodecPriority(rh.codec));
169 static bool PredicateSubtitlePriority(const SelectionStream& lh, const SelectionStream& rh)
171 if(!g_settings.m_currentVideoSettings.m_SubtitleOn)
173 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_FORCED
174 , rh.flags & CDemuxStream::FLAG_FORCED);
177 PREDICATE_RETURN(lh.type_index == g_settings.m_currentVideoSettings.m_SubtitleStream
178 , rh.type_index == g_settings.m_currentVideoSettings.m_SubtitleStream);
180 CStdString subtitle_language = g_langInfo.GetSubtitleLanguage();
181 if(!g_guiSettings.GetString("locale.subtitlelanguage").Equals("original"))
183 PREDICATE_RETURN((lh.source == STREAM_SOURCE_DEMUX_SUB || lh.source == STREAM_SOURCE_TEXT) && subtitle_language.Equals(lh.language.c_str())
184 , (rh.source == STREAM_SOURCE_DEMUX_SUB || rh.source == STREAM_SOURCE_TEXT) && subtitle_language.Equals(rh.language.c_str()));
187 PREDICATE_RETURN(lh.source == STREAM_SOURCE_DEMUX_SUB
188 , rh.source == STREAM_SOURCE_DEMUX_SUB);
190 PREDICATE_RETURN(lh.source == STREAM_SOURCE_TEXT
191 , rh.source == STREAM_SOURCE_TEXT);
193 if(!g_guiSettings.GetString("locale.subtitlelanguage").Equals("original"))
195 PREDICATE_RETURN(subtitle_language.Equals(lh.language.c_str())
196 , subtitle_language.Equals(rh.language.c_str()));
199 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
200 , rh.flags & CDemuxStream::FLAG_DEFAULT);
205 static bool PredicateVideoPriority(const SelectionStream& lh, const SelectionStream& rh)
207 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
208 , rh.flags & CDemuxStream::FLAG_DEFAULT);
212 bool CSelectionStreams::Get(StreamType type, CDemuxStream::EFlags flag, SelectionStream& out)
214 CSingleLock lock(m_section);
215 for(int i=0;i<(int)m_Streams.size();i++)
217 if(m_Streams[i].type != type)
219 if((m_Streams[i].flags & flag) != flag)
227 int CSelectionStreams::IndexOf(StreamType type, int source, int id) const
229 CSingleLock lock(m_section);
231 for(int i=0;i<(int)m_Streams.size();i++)
233 if(type && m_Streams[i].type != type)
236 if(source && m_Streams[i].source != source)
240 if(m_Streams[i].id == id)
249 int CSelectionStreams::IndexOf(StreamType type, CDVDPlayer& p) const
251 if (p.m_pInputStream && p.m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
254 if(type == STREAM_AUDIO)
255 id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveAudioStream();
256 else if(type == STREAM_VIDEO)
257 id = p.m_CurrentVideo.id;
258 else if(type == STREAM_SUBTITLE)
259 id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveSubtitleStream();
261 return IndexOf(type, STREAM_SOURCE_NAV, id);
264 if(type == STREAM_AUDIO)
265 return IndexOf(type, p.m_CurrentAudio.source, p.m_CurrentAudio.id);
266 else if(type == STREAM_VIDEO)
267 return IndexOf(type, p.m_CurrentVideo.source, p.m_CurrentVideo.id);
268 else if(type == STREAM_SUBTITLE)
269 return IndexOf(type, p.m_CurrentSubtitle.source, p.m_CurrentSubtitle.id);
270 else if(type == STREAM_TELETEXT)
271 return IndexOf(type, p.m_CurrentTeletext.source, p.m_CurrentTeletext.id);
276 int CSelectionStreams::Source(StreamSource source, std::string filename)
278 CSingleLock lock(m_section);
279 int index = source - 1;
280 for(int i=0;i<(int)m_Streams.size();i++)
282 SelectionStream &s = m_Streams[i];
283 if(STREAM_SOURCE_MASK(s.source) != source)
285 // if it already exists, return same
286 if(s.filename == filename)
295 void CSelectionStreams::Update(SelectionStream& s)
297 CSingleLock lock(m_section);
298 int index = IndexOf(s.type, s.source, s.id);
301 SelectionStream& o = Get(s.type, index);
302 s.type_index = o.type_index;
307 s.type_index = Count(s.type);
308 m_Streams.push_back(s);
312 void CSelectionStreams::Update(CDVDInputStream* input, CDVDDemux* demuxer)
314 if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
316 CDVDInputStreamNavigator* nav = (CDVDInputStreamNavigator*)input;
317 string filename = nav->GetFileName();
318 int source = Source(STREAM_SOURCE_NAV, filename);
321 count = nav->GetAudioStreamCount();
322 for(int i=0;i<count;i++)
326 s.type = STREAM_AUDIO;
328 s.name = nav->GetAudioStreamLanguage(i);
329 s.flags = CDemuxStream::FLAG_NONE;
330 s.filename = filename;
335 count = nav->GetSubTitleStreamCount();
336 for(int i=0;i<count;i++)
340 s.type = STREAM_SUBTITLE;
342 s.name = nav->GetSubtitleStreamLanguage(i);
343 s.flags = CDemuxStream::FLAG_NONE;
344 s.filename = filename;
351 string filename = demuxer->GetFileName();
352 int count = demuxer->GetNrOfStreams();
354 if(input) /* hack to know this is sub decoder */
355 source = Source(STREAM_SOURCE_DEMUX, filename);
357 source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
360 for(int i=0;i<count;i++)
362 CDemuxStream* stream = demuxer->GetStream(i);
363 /* make sure stream is marked with right source */
364 stream->source = source;
368 s.type = stream->type;
370 s.language = stream->language;
371 s.flags = stream->flags;
372 s.filename = demuxer->GetFileName();
373 stream->GetStreamName(s.name);
375 demuxer->GetStreamCodecName(stream->iId, codec);
377 s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below.
378 if(stream->type == STREAM_AUDIO)
381 ((CDemuxStreamAudio*)stream)->GetStreamType(type);
382 if(type.length() > 0)
384 if(s.name.length() > 0)
388 s.channels = ((CDemuxStreamAudio*)stream)->iChannels;
395 CDVDPlayer::CDVDPlayer(IPlayerCallback& callback)
397 CThread("CDVDPlayer"),
398 m_CurrentAudio(STREAM_AUDIO, DVDPLAYER_AUDIO),
399 m_CurrentVideo(STREAM_VIDEO, DVDPLAYER_VIDEO),
400 m_CurrentSubtitle(STREAM_SUBTITLE, DVDPLAYER_SUBTITLE),
401 m_CurrentTeletext(STREAM_TELETEXT, DVDPLAYER_TELETEXT),
402 m_messenger("player"),
403 m_dvdPlayerVideo(&m_clock, &m_overlayContainer, m_messenger),
404 m_dvdPlayerAudio(&m_clock, m_messenger),
405 m_dvdPlayerSubtitle(&m_overlayContainer),
406 m_dvdPlayerTeletext(),
410 m_pSubtitleDemuxer = NULL;
411 m_pInputStream = NULL;
415 m_EdlAutoSkipMarkers.Clear();
416 m_UpdateApplication = 0;
418 m_bAbortRequest = false;
421 m_playSpeed = DVD_PLAYSPEED_NORMAL;
422 m_caching = CACHESTATE_DONE;
424 memset(&m_SpeedState, 0, sizeof(m_SpeedState));
426 #ifdef DVDDEBUG_MESSAGE_TRACKER
427 g_dvdMessageTracker.Init();
431 CDVDPlayer::~CDVDPlayer()
435 #ifdef DVDDEBUG_MESSAGE_TRACKER
436 g_dvdMessageTracker.DeInit();
440 bool CDVDPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
444 CLog::Log(LOGNOTICE, "DVDPlayer: Opening: %s", file.GetPath().c_str());
446 // if playing a file close it first
447 // this has to be changed so we won't have to close it.
451 m_bAbortRequest = false;
452 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
455 m_UpdateApplication = 0;
458 m_PlayerOptions = options;
460 m_mimetype = file.GetMimeType();
461 m_filename = file.GetPath();
465 #if defined(HAS_VIDEO_PLAYBACK)
466 g_renderManager.PreInit();
470 if(!m_ready.WaitMSec(100))
472 CGUIDialogBusy* dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY);
476 while(!m_ready.WaitMSec(1))
477 g_windowManager.ProcessRenderLoop(false);
482 // Playback might have been stopped due to some error
483 if (m_bStop || m_bAbortRequest)
490 CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__);
495 bool CDVDPlayer::CloseFile()
497 CLog::Log(LOGNOTICE, "CDVDPlayer::CloseFile()");
499 // unpause the player
500 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
502 // set the abort request so that other threads can finish up
503 m_bAbortRequest = true;
505 // tell demuxer to abort
509 if(m_pSubtitleDemuxer)
510 m_pSubtitleDemuxer->Abort();
512 CLog::Log(LOGNOTICE, "DVDPlayer: waiting for threads to exit");
514 // wait for the main thread to finish up
515 // since this main thread cleans up all other resources and threads
516 // we are done after the StopThread call
520 m_EdlAutoSkipMarkers.Clear();
522 CLog::Log(LOGNOTICE, "DVDPlayer: finished waiting");
523 #if defined(HAS_VIDEO_PLAYBACK)
524 g_renderManager.UnInit();
529 bool CDVDPlayer::IsPlaying() const
534 void CDVDPlayer::OnStartup()
536 m_CurrentVideo.Clear();
537 m_CurrentAudio.Clear();
538 m_CurrentSubtitle.Clear();
539 m_CurrentTeletext.Clear();
543 g_dvdPerformanceCounter.EnableMainPerformance(this);
544 CUtil::ClearTempFonts();
547 bool CDVDPlayer::OpenInputStream()
550 SAFE_DELETE(m_pInputStream);
552 CLog::Log(LOGNOTICE, "Creating InputStream");
554 // correct the filename if needed
555 CStdString filename(m_filename);
556 if (filename.Find("dvd://") == 0
557 || filename.CompareNoCase("iso9660://video_ts/video_ts.ifo") == 0)
559 m_filename = g_mediaManager.TranslateDevicePath("");
562 // before creating the input stream, if this is an HLS playlist then get the
563 // most appropriate bitrate based on our network settings
564 if (filename.Left(7) == "http://" && filename.Right(5) == ".m3u8")
566 // get the available bandwidth (as per user settings)
567 int maxrate = g_guiSettings.GetInt("network.bandwidth");
571 // determine the most appropriate stream
572 m_filename = PLAYLIST::CPlayListM3U::GetBestBandwidthStream(m_filename, (size_t)maxrate);
574 m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_filename, m_mimetype);
575 if(m_pInputStream == NULL)
577 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - unable to create input stream for [%s]", m_filename.c_str());
581 m_pInputStream->SetFileItem(m_item);
583 if (!m_pInputStream->Open(m_filename.c_str(), m_mimetype))
585 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - error opening [%s]", m_filename.c_str());
589 // find any available external subtitles for non dvd files
590 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
591 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)
592 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
593 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
595 // find any available external subtitles
596 std::vector<CStdString> filenames;
597 CUtil::ScanForExternalSubtitles( m_filename, filenames );
599 // find any upnp subtitles
600 CStdString key("upnp:subtitle:1");
601 for(unsigned s = 1; m_item.HasProperty(key); key.Format("upnp:subtitle:%u", ++s))
602 filenames.push_back(m_item.GetProperty(key).asString());
604 for(unsigned int i=0;i<filenames.size();i++)
606 // if vobsub subtitle:
607 if (URIUtils::GetExtension(filenames[i]) == ".idx")
609 CStdString strSubFile;
610 if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
611 AddSubtitleFile(filenames[i], strSubFile);
615 if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
617 AddSubtitleFile(filenames[i]);
620 } // end loop over all subtitle files
622 g_settings.m_currentVideoSettings.m_SubtitleCached = true;
625 SetAVDelay(g_settings.m_currentVideoSettings.m_AudioDelay);
626 SetSubTitleDelay(g_settings.m_currentVideoSettings.m_SubtitleDelay);
630 m_iChannelEntryTimeOut = 0;
635 bool CDVDPlayer::OpenDemuxStream()
638 SAFE_DELETE(m_pDemuxer);
640 CLog::Log(LOGNOTICE, "Creating Demuxer");
645 while(!m_bStop && attempts-- > 0)
647 m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
648 if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
652 else if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE)
654 CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__);
662 CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__);
669 CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__);
673 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
674 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
675 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
677 int64_t len = m_pInputStream->GetLength();
678 int64_t tim = m_pDemuxer->GetStreamLength();
679 if(len > 0 && tim > 0)
680 m_pInputStream->SetReadRate(len * 1000 / tim);
685 void CDVDPlayer::OpenDefaultStreams(bool reset)
687 // bypass for DVDs. The DVD Navigator has already dictated which streams to open.
688 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
691 SelectionStreams streams;
695 streams = m_SelectionStreams.Get(STREAM_VIDEO, PredicateVideoPriority);
697 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
699 if(OpenVideoStream(it->id, it->source, reset))
703 CloseVideoStream(true);
706 if(m_PlayerOptions.video_only)
709 streams = m_SelectionStreams.Get(STREAM_AUDIO, PredicateAudioPriority);
712 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
714 if(OpenAudioStream(it->id, it->source, reset))
718 CloseAudioStream(true);
721 m_dvdPlayerVideo.EnableSubtitle(g_settings.m_currentVideoSettings.m_SubtitleOn);
723 // open subtitle stream
724 streams = m_SelectionStreams.Get(STREAM_SUBTITLE, PredicateSubtitlePriority);
726 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
728 if(OpenSubtitleStream(it->id, it->source))
731 if(it->flags & CDemuxStream::FLAG_FORCED)
732 m_dvdPlayerVideo.EnableSubtitle(true);
736 CloseSubtitleStream(true);
738 // open teletext stream
739 streams = m_SelectionStreams.Get(STREAM_TELETEXT);
741 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
743 if(OpenTeletextStream(it->id, it->source))
747 CloseTeletextStream(true);
750 bool CDVDPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
753 // check if we should read from subtitle demuxer
754 if(m_dvdPlayerSubtitle.AcceptsData() && m_pSubtitleDemuxer )
756 if(m_pSubtitleDemuxer)
757 packet = m_pSubtitleDemuxer->Read();
761 UpdateCorrection(packet, m_offset_pts);
762 if(packet->iStreamId < 0)
765 stream = m_pSubtitleDemuxer->GetStream(packet->iStreamId);
768 CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
771 if(stream->source == STREAM_SOURCE_NONE)
773 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
774 m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer);
780 // read a data frame from stream.
782 packet = m_pDemuxer->Read();
786 // stream changed, update and open defaults
787 if(packet->iStreamId == DMX_SPECIALID_STREAMCHANGE)
789 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
790 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
791 OpenDefaultStreams(false);
795 UpdateCorrection(packet, m_offset_pts);
796 // this groupId stuff is getting a bit messy, need to find a better way
797 // currently it is used to determine if a menu overlay is associated with a picture
798 // for dvd's we use as a group id, the current cell and the current title
799 // 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
800 packet->iGroupId = m_pInputStream->GetCurrentGroupId();
802 if(packet->iStreamId < 0)
807 stream = m_pDemuxer->GetStream(packet->iStreamId);
810 CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
813 if(stream->source == STREAM_SOURCE_NONE)
815 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
816 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
824 bool CDVDPlayer::IsValidStream(CCurrentStream& stream)
827 return true; // we consider non selected as valid
829 int source = STREAM_SOURCE_MASK(stream.source);
830 if(source == STREAM_SOURCE_TEXT)
832 if(source == STREAM_SOURCE_DEMUX_SUB)
834 CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.id);
835 if(st == NULL || st->disabled)
837 if(st->type != stream.type)
841 if(source == STREAM_SOURCE_DEMUX)
843 CDemuxStream* st = m_pDemuxer->GetStream(stream.id);
844 if(st == NULL || st->disabled)
846 if(st->type != stream.type)
849 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
851 if(stream.type == STREAM_AUDIO && st->iPhysicalId != m_dvd.iSelectedAudioStream)
853 if(stream.type == STREAM_SUBTITLE && st->iPhysicalId != m_dvd.iSelectedSPUStream)
863 bool CDVDPlayer::IsBetterStream(CCurrentStream& current, CDemuxStream* stream)
865 // Do not reopen non-video streams if we're in video-only mode
866 if(m_PlayerOptions.video_only && current.type != STREAM_VIDEO)
869 if (m_pInputStream && ( m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
870 || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) )
874 source_type = STREAM_SOURCE_MASK(current.source);
875 if(source_type != STREAM_SOURCE_DEMUX
876 && source_type != STREAM_SOURCE_NONE)
879 source_type = STREAM_SOURCE_MASK(stream->source);
880 if(source_type != STREAM_SOURCE_DEMUX
881 || stream->type != current.type
882 || stream->iId == current.id)
885 if(current.type == STREAM_AUDIO && stream->iPhysicalId == m_dvd.iSelectedAudioStream)
887 if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream)
889 if(current.type == STREAM_VIDEO && current.id < 0)
894 if(stream->source == current.source
895 && stream->iId == current.id)
901 if(stream->type != current.type)
904 if(current.type == STREAM_SUBTITLE)
913 void CDVDPlayer::Process()
915 if (!OpenInputStream())
917 m_bAbortRequest = true;
921 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
923 CLog::Log(LOGNOTICE, "DVDPlayer: playing a dvd with menu's");
924 m_PlayerOptions.starttime = 0;
927 if(m_PlayerOptions.state.size() > 0)
928 ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(m_PlayerOptions.state);
930 ((CDVDInputStreamNavigator*)m_pInputStream)->EnableSubtitleStream(g_settings.m_currentVideoSettings.m_SubtitleOn);
932 g_settings.m_currentVideoSettings.m_SubtitleCached = true;
935 if(!OpenDemuxStream())
937 m_bAbortRequest = true;
941 // allow renderer to switch to fullscreen if requested
942 m_dvdPlayerVideo.EnableFullscreen(m_PlayerOptions.fullscreen);
944 OpenDefaultStreams();
946 // look for any EDL files
948 m_EdlAutoSkipMarkers.Clear();
949 float fFramesPerSecond;
950 if (m_CurrentVideo.id >= 0 && m_CurrentVideo.hint.fpsrate > 0 && m_CurrentVideo.hint.fpsscale > 0)
952 fFramesPerSecond = (float)m_CurrentVideo.hint.fpsrate / (float)m_CurrentVideo.hint.fpsscale;
953 m_Edl.ReadEditDecisionLists(m_filename, fFramesPerSecond, m_CurrentVideo.hint.height);
957 * Check to see if the demuxer should start at something other than time 0. This will be the case
958 * if there was a start time specified as part of the "Start from where last stopped" (aka
959 * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
963 if(m_PlayerOptions.starttime > 0 || m_PlayerOptions.startpercent > 0)
965 if (m_PlayerOptions.startpercent > 0 && m_pDemuxer)
967 int64_t playerStartTime = (int64_t) ( ( (float) m_pDemuxer->GetStreamLength() ) * ( m_PlayerOptions.startpercent/(float)100 ) );
968 starttime = m_Edl.RestoreCutTime(playerStartTime);
972 starttime = m_Edl.RestoreCutTime((int64_t)m_PlayerOptions.starttime * 1000); // s to ms
974 CLog::Log(LOGDEBUG, "%s - Start position set to last stopped position: %d", __FUNCTION__, starttime);
976 else if(m_Edl.InCut(0, &cut)
977 && (cut.action == CEdl::CUT || cut.action == CEdl::COMM_BREAK))
980 CLog::Log(LOGDEBUG, "%s - Start position set to end of first cut or commercial break: %d", __FUNCTION__, starttime);
981 if(cut.action == CEdl::COMM_BREAK)
984 * Setup auto skip markers as if the commercial break had been skipped using standard
987 m_EdlAutoSkipMarkers.commbreak_start = cut.start;
988 m_EdlAutoSkipMarkers.commbreak_end = cut.end;
989 m_EdlAutoSkipMarkers.seek_to_start = true;
994 double startpts = DVD_NOPTS_VALUE;
997 if (m_pDemuxer->SeekTime(starttime, false, &startpts))
998 CLog::Log(LOGDEBUG, "%s - starting demuxer from: %d", __FUNCTION__, starttime);
1000 CLog::Log(LOGDEBUG, "%s - failed to start demuxing from: %d", __FUNCTION__, starttime);
1003 if(m_pSubtitleDemuxer)
1005 if(m_pSubtitleDemuxer->SeekTime(starttime, false, &startpts))
1006 CLog::Log(LOGDEBUG, "%s - starting subtitle demuxer from: %d", __FUNCTION__, starttime);
1008 CLog::Log(LOGDEBUG, "%s - failed to start subtitle demuxing from: %d", __FUNCTION__, starttime);
1012 // make sure all selected stream have data on startup
1013 if (CachePVRStream())
1014 SetCaching(CACHESTATE_PVR);
1016 // make sure application know our info
1017 UpdateApplication(0);
1020 if(m_PlayerOptions.identify == false)
1021 m_callback.OnPlayBackStarted();
1023 // we are done initializing now, set the readyevent
1026 if (!CachePVRStream())
1027 SetCaching(CACHESTATE_FLUSH);
1029 while (!m_bAbortRequest)
1031 // handle messages send to this thread, like seek or demuxer reset requests
1037 // should we open a new input stream?
1040 if (OpenInputStream() == false)
1042 m_bAbortRequest = true;
1047 // should we open a new demuxer?
1050 if (m_pInputStream->NextStream() == CDVDInputStream::NEXTSTREAM_NONE)
1053 if (m_pInputStream->IsEOF())
1056 if (OpenDemuxStream() == false)
1058 m_bAbortRequest = true;
1062 OpenDefaultStreams();
1064 if (CachePVRStream())
1065 SetCaching(CACHESTATE_PVR);
1067 UpdateApplication(0);
1071 // handle eventual seeks due to playspeed
1074 // update player state
1075 UpdatePlayState(200);
1077 // update application with our state
1078 UpdateApplication(1000);
1080 if (CheckDelayedChannelEntry())
1083 // if the queues are full, no need to read more
1084 if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0) ||
1085 (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
1091 // always yield to players if they have data levels > 50 percent
1092 if((m_dvdPlayerAudio.GetLevel() > 50 || m_CurrentAudio.id < 0)
1093 && (m_dvdPlayerVideo.GetLevel() > 50 || m_CurrentVideo.id < 0))
1096 DemuxPacket* pPacket = NULL;
1097 CDemuxStream *pStream = NULL;
1098 ReadPacket(pPacket, pStream);
1099 if (pPacket && !pStream)
1101 /* probably a empty packet, just free it and move on */
1102 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
1108 // when paused, demuxer could be be returning empty
1109 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
1112 // check for a still frame state
1113 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1115 CDVDInputStreamNavigator* pStream = static_cast<CDVDInputStreamNavigator*>(m_pInputStream);
1117 // stills will be skipped
1118 if(m_dvd.state == DVDSTATE_STILL)
1120 if (m_dvd.iDVDStillTime > 0)
1122 if ((XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime) >= m_dvd.iDVDStillTime)
1124 m_dvd.iDVDStillTime = 0;
1125 m_dvd.iDVDStillStartTime = 0;
1126 m_dvd.state = DVDSTATE_NORMAL;
1127 pStream->SkipStill();
1134 // if there is another stream available, reopen demuxer
1135 CDVDInputStream::ENextStream next = m_pInputStream->NextStream();
1136 if(next == CDVDInputStream::NEXTSTREAM_OPEN)
1138 SAFE_DELETE(m_pDemuxer);
1139 m_CurrentAudio.stream = NULL;
1140 m_CurrentVideo.stream = NULL;
1141 m_CurrentSubtitle.stream = NULL;
1145 // input stream asked us to just retry
1146 if(next == CDVDInputStream::NEXTSTREAM_RETRY)
1151 else if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
1153 CDVDInputStreamPVRManager* pStream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream);
1154 if (pStream->IsEOF())
1161 // make sure we tell all players to finish it's data
1162 if(m_CurrentAudio.inited)
1163 m_dvdPlayerAudio.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
1164 if(m_CurrentVideo.inited)
1165 m_dvdPlayerVideo.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
1166 if(m_CurrentSubtitle.inited)
1167 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
1168 if(m_CurrentTeletext.inited)
1169 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
1170 m_CurrentAudio.inited = false;
1171 m_CurrentVideo.inited = false;
1172 m_CurrentSubtitle.inited = false;
1173 m_CurrentTeletext.inited = false;
1174 m_CurrentAudio.started = false;
1175 m_CurrentVideo.started = false;
1176 m_CurrentSubtitle.started = false;
1177 m_CurrentTeletext.started = false;
1179 // if we are caching, start playing it again
1180 SetCaching(CACHESTATE_DONE);
1182 // while players are still playing, keep going to allow seekbacks
1183 if(m_dvdPlayerAudio.HasData()
1184 || m_dvdPlayerVideo.HasData())
1190 if (!m_pInputStream->IsEOF())
1191 CLog::Log(LOGINFO, "%s - eof reading from demuxer", __FUNCTION__);
1196 // it's a valid data packet, reset error counter
1199 // check so that none of our streams has become invalid
1200 if (!IsValidStream(m_CurrentAudio) && m_dvdPlayerAudio.IsStalled()) CloseAudioStream(true);
1201 if (!IsValidStream(m_CurrentVideo) && m_dvdPlayerVideo.IsStalled()) CloseVideoStream(true);
1202 if (!IsValidStream(m_CurrentSubtitle) && m_dvdPlayerSubtitle.IsStalled()) CloseSubtitleStream(true);
1203 if (!IsValidStream(m_CurrentTeletext)) CloseTeletextStream(true);
1205 // see if we can find something better to play
1206 if (IsBetterStream(m_CurrentAudio, pStream)) OpenAudioStream (pStream->iId, pStream->source);
1207 if (IsBetterStream(m_CurrentVideo, pStream)) OpenVideoStream (pStream->iId, pStream->source);
1208 if (IsBetterStream(m_CurrentSubtitle, pStream)) OpenSubtitleStream(pStream->iId, pStream->source);
1209 if (IsBetterStream(m_CurrentTeletext, pStream)) OpenTeletextStream(pStream->iId, pStream->source);
1211 // process the packet
1212 ProcessPacket(pStream, pPacket);
1214 // check if in a cut or commercial break that should be automatically skipped
1215 CheckAutoSceneSkip();
1219 bool CDVDPlayer::CheckDelayedChannelEntry(void)
1221 bool bReturn(false);
1223 if (m_iChannelEntryTimeOut > 0 && XbmcThreads::SystemClockMillis() >= m_iChannelEntryTimeOut)
1225 CFileItem currentFile(g_application.CurrentFileItem());
1226 CPVRChannel *currentChannel = currentFile.GetPVRChannelInfoTag();
1227 SwitchChannel(*currentChannel);
1230 m_iChannelEntryTimeOut = 0;
1236 void CDVDPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
1238 /* process packet if it belongs to selected stream. for dvd's don't allow automatic opening of streams*/
1239 StreamLock lock(this);
1243 if (pPacket->iStreamId == m_CurrentAudio.id && pStream->source == m_CurrentAudio.source && pStream->type == STREAM_AUDIO)
1244 ProcessAudioData(pStream, pPacket);
1245 else if (pPacket->iStreamId == m_CurrentVideo.id && pStream->source == m_CurrentVideo.source && pStream->type == STREAM_VIDEO)
1246 ProcessVideoData(pStream, pPacket);
1247 else if (pPacket->iStreamId == m_CurrentSubtitle.id && pStream->source == m_CurrentSubtitle.source && pStream->type == STREAM_SUBTITLE)
1248 ProcessSubData(pStream, pPacket);
1249 else if (pPacket->iStreamId == m_CurrentTeletext.id && pStream->source == m_CurrentTeletext.source && pStream->type == STREAM_TELETEXT)
1250 ProcessTeletextData(pStream, pPacket);
1253 pStream->SetDiscard(AVDISCARD_ALL);
1254 CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
1259 CLog::Log(LOGERROR, "%s - Exception thrown when processing demux packet", __FUNCTION__);
1264 void CDVDPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
1266 if (m_CurrentAudio.stream != (void*)pStream
1267 || m_CurrentAudio.changes != pStream->changes)
1269 /* check so that dmuxer hints or extra data hasn't changed */
1270 /* if they have, reopen stream */
1272 if (m_CurrentAudio.hint != CDVDStreamInfo(*pStream, true))
1273 OpenAudioStream( pPacket->iStreamId, pStream->source );
1275 m_CurrentAudio.stream = (void*)pStream;
1278 // check if we are too slow and need to recache
1279 CheckStartCaching(m_CurrentAudio);
1281 CheckContinuity(m_CurrentAudio, pPacket);
1282 UpdateTimestamps(m_CurrentAudio, pPacket);
1285 if (CheckPlayerInit(m_CurrentAudio, DVDPLAYER_AUDIO))
1289 * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped.
1290 * If not inside a hard cut, but the demux point has reached an EDL mute section then trigger the
1291 * AUDIO_SILENCE state. The AUDIO_SILENCE state is reverted as soon as the demux point is outside
1292 * of any EDL section while EDL mute is still active.
1295 if (CheckSceneSkip(m_CurrentAudio))
1297 else if (m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) && cut.action == CEdl::MUTE // Inside EDL mute
1298 && !m_EdlAutoSkipMarkers.mute) // Mute not already triggered
1300 m_dvdPlayerAudio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, true));
1301 m_EdlAutoSkipMarkers.mute = true;
1303 else if (!m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) // Outside of any EDL
1304 && m_EdlAutoSkipMarkers.mute) // But the mute hasn't been removed yet
1306 m_dvdPlayerAudio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, false));
1307 m_EdlAutoSkipMarkers.mute = false;
1310 m_dvdPlayerAudio.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1313 void CDVDPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
1315 if (m_CurrentVideo.stream != (void*)pStream
1316 || m_CurrentVideo.changes != pStream->changes)
1318 /* check so that dmuxer hints or extra data hasn't changed */
1319 /* if they have reopen stream */
1321 if (m_CurrentVideo.hint != CDVDStreamInfo(*pStream, true))
1322 OpenVideoStream(pPacket->iStreamId, pStream->source);
1324 m_CurrentVideo.stream = (void*)pStream;
1327 // check if we are too slow and need to recache
1328 CheckStartCaching(m_CurrentVideo);
1330 if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
1332 CheckContinuity(m_CurrentVideo, pPacket);
1333 UpdateTimestamps(m_CurrentVideo, pPacket);
1337 if (CheckPlayerInit(m_CurrentVideo, DVDPLAYER_VIDEO))
1340 if (CheckSceneSkip(m_CurrentVideo))
1343 m_dvdPlayerVideo.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1346 void CDVDPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
1348 if (m_CurrentSubtitle.stream != (void*)pStream
1349 || m_CurrentSubtitle.changes != pStream->changes)
1351 /* check so that dmuxer hints or extra data hasn't changed */
1352 /* if they have reopen stream */
1354 if (m_CurrentSubtitle.hint != CDVDStreamInfo(*pStream, true))
1355 OpenSubtitleStream(pPacket->iStreamId, pStream->source);
1357 m_CurrentSubtitle.stream = (void*)pStream;
1360 UpdateTimestamps(m_CurrentSubtitle, pPacket);
1363 if (CheckPlayerInit(m_CurrentSubtitle, DVDPLAYER_SUBTITLE))
1366 if (CheckSceneSkip(m_CurrentSubtitle))
1369 m_dvdPlayerSubtitle.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1371 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1372 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
1375 void CDVDPlayer::ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket)
1377 if (m_CurrentTeletext.stream != (void*)pStream
1378 || m_CurrentTeletext.changes != pStream->changes)
1380 /* check so that dmuxer hints or extra data hasn't changed */
1381 /* if they have, reopen stream */
1382 if (m_CurrentTeletext.hint != CDVDStreamInfo(*pStream, true))
1383 OpenTeletextStream( pPacket->iStreamId, pStream->source );
1385 m_CurrentTeletext.stream = (void*)pStream;
1387 UpdateTimestamps(m_CurrentTeletext, pPacket);
1390 if (CheckPlayerInit(m_CurrentTeletext, DVDPLAYER_TELETEXT))
1393 if (CheckSceneSkip(m_CurrentTeletext))
1396 m_dvdPlayerTeletext.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1399 bool CDVDPlayer::GetCachingTimes(double& level, double& delay, double& offset)
1401 if(!m_pInputStream || !m_pDemuxer)
1404 XFILE::SCacheStatus status;
1405 if (!m_pInputStream->GetCacheStatus(&status))
1408 int64_t cached = status.forward;
1409 unsigned currate = status.currate;
1410 unsigned maxrate = status.maxrate;
1411 bool full = status.full;
1413 int64_t length = m_pInputStream->GetLength();
1414 int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR);
1416 if(cached < 0 || length <= 0 || remain < 0)
1419 double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
1420 double queued = 1000.0 * GetQueueTime() / play_sbp;
1424 offset = (double)(cached + queued) / length;
1429 double cache_sbp = 1.1 * (double)DVD_TIME_BASE / currate; /* underestimate by 10 % */
1430 double play_left = play_sbp * (remain + queued); /* time to play out all remaining bytes */
1431 double cache_left = cache_sbp * (remain - cached); /* time to cache the remaining bytes */
1432 double cache_need = std::max(0.0, remain - play_left / cache_sbp); /* bytes needed until play_left == cache_left */
1434 delay = cache_left - play_left;
1436 if (full && (currate < maxrate) )
1437 level = -1.0; /* buffer is full & our read rate is too low */
1439 level = (cached + queued) / (cache_need + queued);
1444 void CDVDPlayer::HandlePlaySpeed()
1446 ECacheState caching = m_caching;
1448 if(IsInMenu() && caching != CACHESTATE_DONE)
1449 caching = CACHESTATE_DONE;
1451 if(caching == CACHESTATE_FULL)
1453 double level, delay, offset;
1454 if(GetCachingTimes(level, delay, offset))
1458 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(21454), g_localizeStrings.Get(21455));
1459 caching = CACHESTATE_INIT;
1462 caching = CACHESTATE_INIT;
1466 if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0)
1467 || (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
1468 caching = CACHESTATE_INIT;
1472 if(caching == CACHESTATE_INIT)
1474 // if all enabled streams have been inited we are done
1475 if((m_CurrentVideo.id < 0 || m_CurrentVideo.started)
1476 && (m_CurrentAudio.id < 0 || m_CurrentAudio.started))
1477 caching = CACHESTATE_PLAY;
1479 // handle situation that we get no data on one stream
1480 if(m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0)
1482 if ((!m_dvdPlayerAudio.AcceptsData() && !m_CurrentVideo.started)
1483 || (!m_dvdPlayerVideo.AcceptsData() && !m_CurrentAudio.started))
1485 caching = CACHESTATE_DONE;
1490 if (caching == CACHESTATE_PVR)
1492 bool bGotAudio(m_pDemuxer->GetNrOfAudioStreams() > 0);
1493 bool bGotVideo(m_pDemuxer->GetNrOfVideoStreams() > 0);
1494 bool bAudioLevelOk(m_dvdPlayerAudio.GetLevel() > g_advancedSettings.m_iPVRMinAudioCacheLevel);
1495 bool bVideoLevelOk(m_dvdPlayerVideo.GetLevel() > g_advancedSettings.m_iPVRMinVideoCacheLevel);
1496 bool bAudioFull(!m_dvdPlayerAudio.AcceptsData());
1497 bool bVideoFull(!m_dvdPlayerVideo.AcceptsData());
1499 if (/* if all streams got at least g_advancedSettings.m_iPVRMinCacheLevel in their buffers, we're done */
1500 ((bGotVideo || bGotAudio) && (!bGotAudio || bAudioLevelOk) && (!bGotVideo || bVideoLevelOk)) ||
1501 /* or if one of the buffers is full */
1502 (bAudioFull || bVideoFull))
1504 CLog::Log(LOGDEBUG, "set caching from pvr to done. audio (%d) = %d. video (%d) = %d",
1505 bGotAudio, m_dvdPlayerAudio.GetLevel(),
1506 bGotVideo, m_dvdPlayerVideo.GetLevel());
1508 CFileItem currentItem(g_application.CurrentFileItem());
1509 if (currentItem.HasPVRChannelInfoTag())
1510 g_PVRManager.LoadCurrentChannelSettings();
1512 caching = CACHESTATE_DONE;
1516 /* ensure that automatically started players are stopped while caching */
1517 if (m_CurrentAudio.started)
1518 m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
1519 if (m_CurrentVideo.started)
1520 m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
1524 if(caching == CACHESTATE_PLAY)
1526 // if all enabled streams have started playing we are done
1527 if((m_CurrentVideo.id < 0 || !m_dvdPlayerVideo.IsStalled())
1528 && (m_CurrentAudio.id < 0 || !m_dvdPlayerAudio.IsStalled()))
1529 caching = CACHESTATE_DONE;
1532 if(m_caching != caching)
1533 SetCaching(caching);
1536 if(GetPlaySpeed() != DVD_PLAYSPEED_NORMAL && GetPlaySpeed() != DVD_PLAYSPEED_PAUSE)
1540 // this can't be done in menu
1541 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1544 else if (m_CurrentVideo.id >= 0
1545 && m_CurrentVideo.inited == true
1546 && m_SpeedState.lastpts != m_dvdPlayerVideo.GetCurrentPts()
1547 && m_SpeedState.lasttime != GetTime())
1549 m_SpeedState.lastpts = m_dvdPlayerVideo.GetCurrentPts();
1550 m_SpeedState.lasttime = GetTime();
1551 // check how much off clock video is when ff/rw:ing
1552 // a problem here is that seeking isn't very accurate
1553 // and since the clock will be resynced after seek
1554 // we might actually not really be playing at the wanted
1555 // speed. we'd need to have some way to not resync the clock
1556 // after a seek to remember timing. still need to handle
1557 // discontinuities somehow
1559 // when seeking, give the player a headstart to make sure
1560 // the time it takes to seek doesn't make a difference.
1562 error = m_clock.GetClock() - m_SpeedState.lastpts;
1563 error *= m_playSpeed / abs(m_playSpeed);
1565 if(error > DVD_MSEC_TO_TIME(1000))
1567 CLog::Log(LOGDEBUG, "CDVDPlayer::Process - Seeking to catch up");
1568 int64_t iTime = (int64_t)DVD_TIME_TO_MSEC(m_clock.GetClock() + m_State.time_offset + 500000.0 * m_playSpeed / DVD_PLAYSPEED_NORMAL);
1569 m_messenger.Put(new CDVDMsgPlayerSeek(iTime, (GetPlaySpeed() < 0), true, false, false, true));
1575 bool CDVDPlayer::CheckStartCaching(CCurrentStream& current)
1577 if(m_caching != CACHESTATE_DONE
1578 || m_playSpeed != DVD_PLAYSPEED_NORMAL)
1584 if((current.type == STREAM_AUDIO && m_dvdPlayerAudio.IsStalled())
1585 || (current.type == STREAM_VIDEO && m_dvdPlayerVideo.IsStalled()))
1587 if (CachePVRStream())
1589 if ((current.type == STREAM_AUDIO && current.started && m_dvdPlayerAudio.GetLevel() == 0) ||
1590 (current.type == STREAM_VIDEO && current.started && m_dvdPlayerVideo.GetLevel() == 0))
1592 CLog::Log(LOGDEBUG, "%s stream stalled. start buffering", current.type == STREAM_AUDIO ? "audio" : "video");
1593 SetCaching(CACHESTATE_PVR);
1598 // don't start caching if it's only a single stream that has run dry
1599 if(m_dvdPlayerAudio.GetLevel() > 50
1600 || m_dvdPlayerVideo.GetLevel() > 50)
1604 SetCaching(CACHESTATE_FULL);
1606 SetCaching(CACHESTATE_INIT);
1612 bool CDVDPlayer::CheckPlayerInit(CCurrentStream& current, unsigned int source)
1617 if(current.startpts != DVD_NOPTS_VALUE)
1619 if(current.dts == DVD_NOPTS_VALUE)
1621 CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
1625 if((current.startpts - current.dts) > DVD_SEC_TO_TIME(20))
1627 CLog::Log(LOGDEBUG, "%s - too far to decode before finishing seek", __FUNCTION__);
1628 if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
1629 m_CurrentAudio.startpts = current.dts;
1630 if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
1631 m_CurrentVideo.startpts = current.dts;
1632 if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
1633 m_CurrentSubtitle.startpts = current.dts;
1634 if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE)
1635 m_CurrentTeletext.startpts = current.dts;
1638 if(current.dts < current.startpts)
1640 CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
1645 //If this is the first packet after a discontinuity, send it as a resync
1646 if (current.dts != DVD_NOPTS_VALUE)
1648 current.inited = true;
1649 current.startpts = current.dts;
1651 bool setclock = false;
1652 if(m_playSpeed == DVD_PLAYSPEED_NORMAL)
1654 if( source == DVDPLAYER_AUDIO)
1655 setclock = !m_CurrentVideo.inited;
1656 else if(source == DVDPLAYER_VIDEO)
1657 setclock = !m_CurrentAudio.inited;
1661 if(source == DVDPLAYER_VIDEO)
1665 double starttime = current.startpts;
1666 if(m_CurrentAudio.inited
1667 && m_CurrentAudio.startpts != DVD_NOPTS_VALUE
1668 && m_CurrentAudio.startpts < starttime)
1669 starttime = m_CurrentAudio.startpts;
1670 if(m_CurrentVideo.inited
1671 && m_CurrentVideo.startpts != DVD_NOPTS_VALUE
1672 && m_CurrentVideo.startpts < starttime)
1673 starttime = m_CurrentVideo.startpts;
1675 starttime = current.startpts - starttime;
1676 if(starttime > 0 && setclock)
1678 if(starttime > DVD_SEC_TO_TIME(2))
1679 CLog::Log(LOGWARNING, "CDVDPlayer::CheckPlayerInit(%d) - Ignoring too large delay of %f", source, starttime);
1681 SendPlayerMessage(new CDVDMsgDouble(CDVDMsg::GENERAL_DELAY, starttime), source);
1684 SendPlayerMessage(new CDVDMsgGeneralResync(current.dts, setclock), source);
1689 void CDVDPlayer::UpdateCorrection(DemuxPacket* pkt, double correction)
1691 if(pkt->dts != DVD_NOPTS_VALUE) pkt->dts -= correction;
1692 if(pkt->pts != DVD_NOPTS_VALUE) pkt->pts -= correction;
1695 void CDVDPlayer::UpdateTimestamps(CCurrentStream& current, DemuxPacket* pPacket)
1697 double dts = current.dts;
1698 /* update stored values */
1699 if(pPacket->dts != DVD_NOPTS_VALUE)
1701 else if(pPacket->pts != DVD_NOPTS_VALUE)
1704 /* calculate some average duration */
1705 if(pPacket->duration != DVD_NOPTS_VALUE)
1706 current.dur = pPacket->duration;
1707 else if(dts != DVD_NOPTS_VALUE && current.dts != DVD_NOPTS_VALUE)
1708 current.dur = 0.1 * (current.dur * 9 + (dts - current.dts));
1713 static void UpdateLimits(double& minimum, double& maximum, double dts)
1715 if(dts == DVD_NOPTS_VALUE)
1717 if(minimum == DVD_NOPTS_VALUE || minimum > dts) minimum = dts;
1718 if(maximum == DVD_NOPTS_VALUE || maximum < dts) maximum = dts;
1721 void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
1723 if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
1726 if( pPacket->dts == DVD_NOPTS_VALUE || current.dts == DVD_NOPTS_VALUE)
1729 double mindts = DVD_NOPTS_VALUE, maxdts = DVD_NOPTS_VALUE;
1730 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts);
1731 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts);
1732 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts_end());
1733 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts_end());
1735 /* if we don't have max and min, we can't do anything more */
1736 if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
1739 double correction = 0.0;
1740 if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000))
1742 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - resync forward :%d, prev:%f, curr:%f, diff:%f"
1743 , current.type, current.dts, pPacket->dts, pPacket->dts - maxdts);
1744 correction = pPacket->dts - maxdts;
1747 /* if it's large scale jump, correct for it */
1748 if(pPacket->dts + DVD_MSEC_TO_TIME(100) < current.dts_end())
1750 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - resync backward :%d, prev:%f, curr:%f, diff:%f"
1751 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1752 correction = pPacket->dts - current.dts_end();
1754 else if(pPacket->dts < current.dts)
1756 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - wrapback :%d, prev:%f, curr:%f, diff:%f"
1757 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1760 if(correction != 0.0)
1762 /* disable detection on next packet on other stream to avoid ping pong-ing */
1763 if(m_CurrentAudio.player != current.player) m_CurrentAudio.dts = DVD_NOPTS_VALUE;
1764 if(m_CurrentVideo.player != current.player) m_CurrentVideo.dts = DVD_NOPTS_VALUE;
1766 m_offset_pts += correction;
1767 UpdateCorrection(pPacket, correction);
1771 bool CDVDPlayer::CheckSceneSkip(CCurrentStream& current)
1776 if(current.dts == DVD_NOPTS_VALUE)
1779 if(current.inited == false)
1783 return m_Edl.InCut(DVD_TIME_TO_MSEC(current.dts + m_offset_pts), &cut) && cut.action == CEdl::CUT;
1786 void CDVDPlayer::CheckAutoSceneSkip()
1792 * Check that there is an audio and video stream.
1794 if(m_CurrentAudio.id < 0
1795 || m_CurrentVideo.id < 0)
1799 * If there is a startpts defined for either the audio or video stream then dvdplayer is still
1800 * still decoding frames to get to the previously requested seek point.
1802 if(m_CurrentAudio.inited == false
1803 || m_CurrentVideo.inited == false)
1806 if(m_CurrentAudio.dts == DVD_NOPTS_VALUE
1807 || m_CurrentVideo.dts == DVD_NOPTS_VALUE)
1810 const int64_t clock = DVD_TIME_TO_MSEC(min(m_CurrentAudio.dts, m_CurrentVideo.dts) + m_offset_pts);
1813 if(!m_Edl.InCut(clock, &cut))
1816 if(cut.action == CEdl::CUT
1817 && !(cut.end == m_EdlAutoSkipMarkers.cut || cut.start == m_EdlAutoSkipMarkers.cut)) // To prevent looping if same cut again
1819 CLog::Log(LOGDEBUG, "%s - Clock in EDL cut [%s - %s]: %s. Automatically skipping over.",
1820 __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(),
1821 CEdl::MillisecondsToTimeString(cut.end).c_str(), CEdl::MillisecondsToTimeString(clock).c_str());
1823 * Seeking either goes to the start or the end of the cut depending on the play direction.
1825 int64_t seek = GetPlaySpeed() >= 0 ? cut.end : cut.start;
1827 * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
1829 m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, true, false, true, false, true));
1831 * Seek doesn't always work reliably. Last physical seek time is recorded to prevent looping
1832 * if there was an error with seeking and it landed somewhere unexpected, perhaps back in the
1833 * cut. The cut automatic skip marker is reset every 500ms allowing another attempt at the seek.
1835 m_EdlAutoSkipMarkers.cut = GetPlaySpeed() >= 0 ? cut.end : cut.start;
1837 else if(cut.action == CEdl::COMM_BREAK
1838 && GetPlaySpeed() >= 0
1839 && cut.start > m_EdlAutoSkipMarkers.commbreak_end)
1841 CLog::Log(LOGDEBUG, "%s - Clock in commercial break [%s - %s]: %s. Automatically skipping to end of commercial break (only done once per break)",
1842 __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(), CEdl::MillisecondsToTimeString(cut.end).c_str(),
1843 CEdl::MillisecondsToTimeString(clock).c_str());
1845 * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
1847 m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false, true));
1849 * Each commercial break is only skipped once so poorly detected commercial breaks can be
1850 * manually re-entered. Start and end are recorded to prevent looping and to allow seeking back
1851 * to the start of the commercial break if incorrectly flagged.
1853 m_EdlAutoSkipMarkers.commbreak_start = cut.start;
1854 m_EdlAutoSkipMarkers.commbreak_end = cut.end;
1855 m_EdlAutoSkipMarkers.seek_to_start = true; // Allow backwards Seek() to go directly to the start
1860 void CDVDPlayer::SynchronizeDemuxer(unsigned int timeout)
1862 if(IsCurrentThread())
1864 if(!m_messenger.IsInited())
1867 CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, 0);
1868 m_messenger.Put(message->Acquire());
1869 message->Wait(&m_bStop, 0);
1873 void CDVDPlayer::SynchronizePlayers(unsigned int sources)
1875 /* we need a big timeout as audio queue is about 8seconds for 2ch ac3 */
1876 const int timeout = 10*1000; // in milliseconds
1878 CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, sources);
1879 if (m_CurrentAudio.id >= 0)
1880 m_dvdPlayerAudio.SendMessage(message->Acquire());
1882 if (m_CurrentVideo.id >= 0)
1883 m_dvdPlayerVideo.SendMessage(message->Acquire());
1884 /* TODO - we have to rewrite the sync class, to not require
1885 all other players waiting for subtitle, should only
1887 if (m_CurrentSubtitle.id >= 0)
1888 m_dvdPlayerSubtitle.SendMessage(message->Acquire());
1893 void CDVDPlayer::SendPlayerMessage(CDVDMsg* pMsg, unsigned int target)
1895 if(target == DVDPLAYER_AUDIO)
1896 m_dvdPlayerAudio.SendMessage(pMsg);
1897 if(target == DVDPLAYER_VIDEO)
1898 m_dvdPlayerVideo.SendMessage(pMsg);
1899 if(target == DVDPLAYER_SUBTITLE)
1900 m_dvdPlayerSubtitle.SendMessage(pMsg);
1901 if(target == DVDPLAYER_TELETEXT)
1902 m_dvdPlayerTeletext.SendMessage(pMsg);
1905 void CDVDPlayer::OnExit()
1907 g_dvdPerformanceCounter.DisableMainPerformance();
1911 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit()");
1913 // set event to inform openfile something went wrong in case openfile is still waiting for this event
1914 SetCaching(CACHESTATE_DONE);
1916 // close each stream
1917 if (!m_bAbortRequest) CLog::Log(LOGNOTICE, "DVDPlayer: eof, waiting for queues to empty");
1918 if (m_CurrentAudio.id >= 0)
1920 CLog::Log(LOGNOTICE, "DVDPlayer: closing audio stream");
1921 CloseAudioStream(!m_bAbortRequest);
1923 if (m_CurrentVideo.id >= 0)
1925 CLog::Log(LOGNOTICE, "DVDPlayer: closing video stream");
1926 CloseVideoStream(!m_bAbortRequest);
1928 if (m_CurrentSubtitle.id >= 0)
1930 CLog::Log(LOGNOTICE, "DVDPlayer: closing subtitle stream");
1931 CloseSubtitleStream(!m_bAbortRequest);
1933 if (m_CurrentTeletext.id >= 0)
1935 CLog::Log(LOGNOTICE, "DVDPlayer: closing teletext stream");
1936 CloseTeletextStream(!m_bAbortRequest);
1938 // destroy the demuxer
1941 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting demuxer");
1946 if (m_pSubtitleDemuxer)
1948 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting subtitle demuxer");
1949 delete m_pSubtitleDemuxer;
1951 m_pSubtitleDemuxer = NULL;
1953 // destroy the inputstream
1956 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting input stream");
1957 delete m_pInputStream;
1959 m_pInputStream = NULL;
1961 // clean up all selection streams
1962 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
1969 CLog::Log(LOGERROR, "%s - Exception thrown when trying to close down player, memory leak will follow", __FUNCTION__);
1970 m_pInputStream = NULL;
1975 // if we didn't stop playing, advance to the next item in xbmc's playlist
1976 if(m_PlayerOptions.identify == false)
1978 if (m_bAbortRequest)
1979 m_callback.OnPlayBackStopped();
1981 m_callback.OnPlayBackEnded();
1984 // set event to inform openfile something went wrong in case openfile is still waiting for this event
1988 void CDVDPlayer::HandleMessages()
1991 StreamLock lock(this);
1993 while (m_messenger.Get(&pMsg, 0) == MSGQ_OK)
1998 if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
1999 && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2001 CDVDMsgPlayerSeek &msg(*((CDVDMsgPlayerSeek*)pMsg));
2003 if (!m_State.canseek)
2009 if(!msg.GetTrickPlay())
2011 g_infoManager.SetDisplayAfterSeek(100000);
2013 SetCaching(CACHESTATE_FLUSH);
2016 double start = DVD_NOPTS_VALUE;
2018 int time = msg.GetRestore() ? (int)m_Edl.RestoreCutTime(msg.GetTime()) : msg.GetTime();
2020 // if input streams doesn't support seektime we must convert back to clock
2021 if(dynamic_cast<CDVDInputStream::ISeekTime*>(m_pInputStream) == NULL)
2022 time -= m_State.time_offset;
2024 CLog::Log(LOGDEBUG, "demuxer seek to: %d", time);
2025 if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start))
2027 CLog::Log(LOGDEBUG, "demuxer seek to: %d, success", time);
2028 if(m_pSubtitleDemuxer)
2030 if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward()))
2031 CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: %d, success", time);
2033 FlushBuffers(!msg.GetFlush(), start, msg.GetAccurate());
2036 CLog::Log(LOGWARNING, "error while seeking");
2038 // set flag to indicate we have finished a seeking request
2039 if(!msg.GetTrickPlay())
2040 g_infoManager.SetDisplayAfterSeek();
2042 // dvd's will issue a HOP_CHANNEL that we need to skip
2043 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2044 m_dvd.state = DVDSTATE_SEEK;
2046 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
2047 && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2049 g_infoManager.SetDisplayAfterSeek(100000);
2050 SetCaching(CACHESTATE_FLUSH);
2052 CDVDMsgPlayerSeekChapter &msg(*((CDVDMsgPlayerSeekChapter*)pMsg));
2053 double start = DVD_NOPTS_VALUE;
2055 // This should always be the case.
2056 if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
2058 FlushBuffers(false, start, true);
2059 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2062 g_infoManager.SetDisplayAfterSeek();
2064 else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
2066 m_CurrentAudio.stream = NULL;
2067 m_CurrentVideo.stream = NULL;
2068 m_CurrentSubtitle.stream = NULL;
2070 // we need to reset the demuxer, probably because the streams have changed
2072 m_pDemuxer->Reset();
2073 if(m_pSubtitleDemuxer)
2074 m_pSubtitleDemuxer->Reset();
2076 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
2078 CDVDMsgPlayerSetAudioStream* pMsg2 = (CDVDMsgPlayerSetAudioStream*)pMsg;
2080 SelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
2081 if(st.source != STREAM_SOURCE_NONE)
2083 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2085 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2086 if(pStream->SetActiveAudioStream(st.id))
2088 m_dvd.iSelectedAudioStream = -1;
2089 CloseAudioStream(false);
2090 m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true, true, true));
2095 CloseAudioStream(false);
2096 OpenAudioStream(st.id, st.source);
2097 m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true, true, true));
2101 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
2103 CDVDMsgPlayerSetSubtitleStream* pMsg2 = (CDVDMsgPlayerSetSubtitleStream*)pMsg;
2105 SelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
2106 if(st.source != STREAM_SOURCE_NONE)
2108 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2110 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2111 if(pStream->SetActiveSubtitleStream(st.id))
2113 m_dvd.iSelectedSPUStream = -1;
2114 CloseSubtitleStream(false);
2119 CloseSubtitleStream(false);
2120 OpenSubtitleStream(st.id, st.source);
2124 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
2126 CDVDMsgBool* pValue = (CDVDMsgBool*)pMsg;
2128 m_dvdPlayerVideo.EnableSubtitle(pValue->m_value);
2130 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2131 static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->EnableSubtitleStream(pValue->m_value);
2133 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
2135 g_infoManager.SetDisplayAfterSeek(100000);
2136 SetCaching(CACHESTATE_FLUSH);
2138 CDVDMsgPlayerSetState* pMsgPlayerSetState = (CDVDMsgPlayerSetState*)pMsg;
2140 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2142 std::string s = pMsgPlayerSetState->GetState();
2143 ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(s);
2144 m_dvd.state = DVDSTATE_NORMAL;
2145 m_dvd.iDVDStillStartTime = 0;
2146 m_dvd.iDVDStillTime = 0;
2149 g_infoManager.SetDisplayAfterSeek();
2151 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD))
2153 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2155 input->Record(*(CDVDMsgBool*)pMsg);
2157 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
2159 FlushBuffers(false);
2161 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
2163 int speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
2165 // correct our current clock, as it would start going wrong otherwise
2166 if(m_State.timestamp > 0)
2169 offset = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2170 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2171 if(offset > 1000) offset = 1000;
2172 if(offset < -1000) offset = -1000;
2173 m_State.time += DVD_TIME_TO_MSEC(offset);
2174 m_State.timestamp = CDVDClock::GetAbsoluteClock();
2177 if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed)
2178 m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL);
2180 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && speed != m_playSpeed)
2182 CDVDInputStreamPVRManager* pvrinputstream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream);
2183 pvrinputstream->Pause( speed == 0 );
2186 // if playspeed is different then DVD_PLAYSPEED_NORMAL or DVD_PLAYSPEED_PAUSE
2187 // audioplayer, stops outputing audio to audiorendere, but still tries to
2188 // sleep an correct amount for each packet
2189 // videoplayer just plays faster after the clock speed has been increased
2191 // 2. skip frames and adjust their pts or the clock
2192 m_playSpeed = speed;
2193 m_caching = CACHESTATE_DONE;
2194 m_clock.SetSpeed(speed);
2195 m_dvdPlayerAudio.SetSpeed(speed);
2196 m_dvdPlayerVideo.SetSpeed(speed);
2198 // TODO - we really shouldn't pause demuxer
2199 // until our buffers are somewhat filled
2201 m_pDemuxer->SetSpeed(speed);
2203 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) == 0)
2205 FlushBuffers(false);
2206 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2207 if(input && input->SelectChannelByNumber(static_cast<CDVDMsgInt*>(pMsg)->m_value))
2209 SAFE_DELETE(m_pDemuxer);
2212 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2213 CApplicationMessenger::Get().MediaStop(false);
2216 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0)
2218 FlushBuffers(false);
2219 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2220 if(input && input->SelectChannel(static_cast<CDVDMsgType <CPVRChannel> *>(pMsg)->m_value))
2222 SAFE_DELETE(m_pDemuxer);
2225 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2226 CApplicationMessenger::Get().MediaStop(false);
2229 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV))
2231 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2234 bool bSwitchSuccessful(false);
2235 bool bShowPreview(g_guiSettings.GetInt("pvrplayback.channelentrytimeout") > 0);
2239 g_infoManager.SetDisplayAfterSeek(100000);
2240 FlushBuffers(false);
2243 if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
2244 bSwitchSuccessful = input->NextChannel(bShowPreview);
2246 bSwitchSuccessful = input->PrevChannel(bShowPreview);
2248 if(bSwitchSuccessful)
2252 UpdateApplication(0);
2253 m_iChannelEntryTimeOut = XbmcThreads::SystemClockMillis() + g_guiSettings.GetInt("pvrplayback.channelentrytimeout");
2257 m_iChannelEntryTimeOut = 0;
2258 SAFE_DELETE(m_pDemuxer);
2260 g_infoManager.SetDisplayAfterSeek();
2265 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2266 CApplicationMessenger::Get().MediaStop(false);
2270 else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
2271 OnAction(((CDVDMsgType<CAction>*)pMsg)->m_value);
2272 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
2274 int player = ((CDVDMsgInt*)pMsg)->m_value;
2275 if(player == DVDPLAYER_AUDIO)
2276 m_CurrentAudio.started = true;
2277 if(player == DVDPLAYER_VIDEO)
2278 m_CurrentVideo.started = true;
2279 CLog::Log(LOGDEBUG, "CDVDPlayer::HandleMessages - player started %d", player);
2284 CLog::Log(LOGERROR, "%s - Exception thrown when handling message", __FUNCTION__);
2292 void CDVDPlayer::SetCaching(ECacheState state)
2294 if(state == CACHESTATE_FLUSH)
2296 double level, delay, offset;
2297 if(GetCachingTimes(level, delay, offset))
2298 state = CACHESTATE_FULL;
2300 state = CACHESTATE_INIT;
2303 if(m_caching == state)
2306 CLog::Log(LOGDEBUG, "CDVDPlayer::SetCaching - caching state %d", state);
2307 if(state == CACHESTATE_FULL
2308 || state == CACHESTATE_INIT
2309 || state == CACHESTATE_PVR)
2311 m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
2312 m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
2313 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2314 m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
2315 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2317 if (state == CACHESTATE_PVR)
2318 m_pInputStream->ResetScanTimeout((unsigned int) g_guiSettings.GetInt("pvrplayback.scantime") * 1000);
2321 if(state == CACHESTATE_PLAY
2322 ||(state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY))
2324 m_clock.SetSpeed(m_playSpeed);
2325 m_dvdPlayerAudio.SetSpeed(m_playSpeed);
2326 m_dvdPlayerVideo.SetSpeed(m_playSpeed);
2327 m_pInputStream->ResetScanTimeout(0);
2332 void CDVDPlayer::SetPlaySpeed(int speed)
2334 m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed));
2335 m_dvdPlayerAudio.SetSpeed(speed);
2336 m_dvdPlayerVideo.SetSpeed(speed);
2337 SynchronizeDemuxer(100);
2340 bool CDVDPlayer::CanPause()
2342 CSingleLock lock(m_StateSection);
2343 return m_State.canpause;
2346 void CDVDPlayer::Pause()
2348 CSingleLock lock(m_StateSection);
2349 if (!m_State.canpause)
2353 if(m_playSpeed != DVD_PLAYSPEED_PAUSE && (m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR))
2355 SetCaching(CACHESTATE_DONE);
2359 // return to normal speed if it was paused before, pause otherwise
2360 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
2362 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2363 m_callback.OnPlayBackResumed();
2367 SetPlaySpeed(DVD_PLAYSPEED_PAUSE);
2368 m_callback.OnPlayBackPaused();
2372 bool CDVDPlayer::IsPaused() const
2374 return m_playSpeed == DVD_PLAYSPEED_PAUSE || m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR;
2377 bool CDVDPlayer::HasVideo() const
2379 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)) return true;
2381 return m_SelectionStreams.Count(STREAM_VIDEO) > 0 ? true : false;
2384 bool CDVDPlayer::HasAudio() const
2386 return m_SelectionStreams.Count(STREAM_AUDIO) > 0 ? true : false;
2389 bool CDVDPlayer::IsPassthrough() const
2391 return m_dvdPlayerAudio.IsPassthrough();
2394 bool CDVDPlayer::CanSeek()
2396 CSingleLock lock(m_StateSection);
2397 return m_State.canseek;
2400 void CDVDPlayer::Seek(bool bPlus, bool bLargeStep)
2403 // sadly this doesn't work for now, audio player must
2404 // drop packets at the same rate as we play frames
2405 if( m_playSpeed == DVD_PLAYSPEED_PAUSE && bPlus && !bLargeStep)
2407 m_dvdPlayerVideo.StepFrame();
2411 if (!m_State.canseek)
2414 if(((bPlus && GetChapter() < GetChapterCount())
2415 || (!bPlus && GetChapter() > 1)) && bLargeStep)
2418 SeekChapter(GetChapter() + 1);
2420 SeekChapter(GetChapter() - 1);
2425 if (g_advancedSettings.m_videoUseTimeSeeking && GetTotalTime() > 2000*g_advancedSettings.m_videoTimeSeekForwardBig)
2428 seek = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig;
2430 seek = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward;
2438 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig;
2440 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward;
2441 seek = (int64_t)(GetTotalTimeInMsec()*(GetPercentage()+percent)/100);
2444 bool restore = true;
2448 * Alter the standard seek position based on whether any commercial breaks have been
2449 * automatically skipped.
2451 const int clock = DVD_TIME_TO_MSEC(m_clock.GetClock());
2453 * If a large backwards seek occurs within 10 seconds of the end of the last automated
2454 * commercial skip, then seek back to the start of the commercial break under the assumption
2455 * it was flagged incorrectly. 10 seconds grace period is allowed in case the watcher has to
2456 * fumble around finding the remote. Only happens once per commercial break.
2458 * Small skip does not trigger this in case the start of the commercial break was in fact fine
2459 * but it skipped too far into the program. In that case small skip backwards behaves as normal.
2461 if (!bPlus && bLargeStep
2462 && m_EdlAutoSkipMarkers.seek_to_start
2463 && clock >= m_EdlAutoSkipMarkers.commbreak_end
2464 && clock <= m_EdlAutoSkipMarkers.commbreak_end + 10*1000) // Only if within 10 seconds of the end (in msec)
2466 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).",
2467 __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
2468 CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
2469 seek = m_EdlAutoSkipMarkers.commbreak_start;
2471 m_EdlAutoSkipMarkers.seek_to_start = false; // So this will only happen within the 10 second grace period once.
2474 * If big skip forward within the last "reverted" commercial break, seek to the end of the
2475 * commercial break under the assumption that the break was incorrectly flagged and playback has
2476 * now reached the actual start of the commercial break. Assume that the end is flagged more
2477 * correctly than the landing point for a standard big skip (ends seem to be flagged more
2478 * accurately than the start).
2480 else if (bPlus && bLargeStep
2481 && clock >= m_EdlAutoSkipMarkers.commbreak_start
2482 && clock <= m_EdlAutoSkipMarkers.commbreak_end)
2484 CLog::Log(LOGDEBUG, "%s - Seeking to end of previously skipped commercial break [%s - %s] as big forwards skip activated within the break.",
2485 __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
2486 CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
2487 seek = m_EdlAutoSkipMarkers.commbreak_end;
2492 int64_t time = GetTime();
2493 if(g_application.CurrentFileItem().IsStack()
2494 && (seek > GetTotalTimeInMsec() || seek < 0))
2496 g_application.SeekTime((seek - time) * 0.001 + g_application.GetTime());
2497 // warning, don't access any dvdplayer variables here as
2498 // the dvdplayer object may have been destroyed
2502 m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, !bPlus, true, false, restore));
2503 SynchronizeDemuxer(100);
2504 if (seek < 0) seek = 0;
2505 m_callback.OnPlayBackSeek((int)seek, (int)(seek - time));
2508 bool CDVDPlayer::SeekScene(bool bPlus)
2510 if (!m_Edl.HasSceneMarker())
2514 * There is a 5 second grace period applied when seeking for scenes backwards. If there is no
2515 * grace period applied it is impossible to go backwards past a scene marker.
2517 int64_t clock = GetTime();
2518 if (!bPlus && clock > 5 * 1000) // 5 seconds
2521 int64_t iScenemarker;
2522 if (m_Edl.GetNextSceneMarker(bPlus, clock, &iScenemarker))
2525 * Seeking is flushed and inaccurate, just like Seek()
2527 m_messenger.Put(new CDVDMsgPlayerSeek((int)iScenemarker, !bPlus, true, false, false));
2528 SynchronizeDemuxer(100);
2534 void CDVDPlayer::GetAudioInfo(CStdString& strAudioInfo)
2536 { CSingleLock lock(m_StateSection);
2537 strAudioInfo.Format("D(%s)", m_State.demux_audio.c_str());
2539 strAudioInfo.AppendFormat(" P(%s)", m_dvdPlayerAudio.GetPlayerInfo().c_str());
2542 void CDVDPlayer::GetVideoInfo(CStdString& strVideoInfo)
2544 { CSingleLock lock(m_StateSection);
2545 strVideoInfo.Format("D(%s)", m_State.demux_video.c_str());
2547 strVideoInfo.AppendFormat(" P(%s)", m_dvdPlayerVideo.GetPlayerInfo().c_str());
2550 void CDVDPlayer::GetGeneralInfo(CStdString& strGeneralInfo)
2554 double dDelay = m_dvdPlayerVideo.GetDelay() / DVD_TIME_BASE - g_renderManager.GetDisplayLatency();
2556 double apts = m_dvdPlayerAudio.GetCurrentPts();
2557 double vpts = m_dvdPlayerVideo.GetCurrentPts();
2560 if( apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE )
2561 dDiff = (apts - vpts) / DVD_TIME_BASE;
2564 strEDL.AppendFormat(", edl:%s", m_Edl.GetInfo().c_str());
2567 CSingleLock lock(m_StateSection);
2568 if(m_State.cache_bytes >= 0)
2570 strBuf.AppendFormat(" cache:%s %2.0f%%"
2571 , StringUtils::SizeToString(m_State.cache_bytes).c_str()
2572 , m_State.cache_level * 100);
2573 if(m_playSpeed == 0 || m_caching == CACHESTATE_FULL)
2574 strBuf.AppendFormat(" %d sec", DVD_TIME_TO_SEC(m_State.cache_delay));
2577 strGeneralInfo.Format("C( ad:% 6.3f, a/v:% 6.3f%s, dcpu:%2i%% acpu:%2i%% vcpu:%2i%%%s )"
2581 , (int)(CThread::GetRelativeUsage()*100)
2582 , (int)(m_dvdPlayerAudio.GetRelativeUsage()*100)
2583 , (int)(m_dvdPlayerVideo.GetRelativeUsage()*100)
2589 void CDVDPlayer::SeekPercentage(float iPercent)
2591 int64_t iTotalTime = GetTotalTimeInMsec();
2596 SeekTime((int64_t)(iTotalTime * iPercent / 100));
2599 float CDVDPlayer::GetPercentage()
2601 int64_t iTotalTime = GetTotalTimeInMsec();
2606 return GetTime() * 100 / (float)iTotalTime;
2609 float CDVDPlayer::GetCachePercentage()
2611 CSingleLock lock(m_StateSection);
2612 return m_State.cache_offset * 100; // NOTE: Percentage returned is relative
2615 void CDVDPlayer::SetAVDelay(float fValue)
2617 m_dvdPlayerVideo.SetDelay( (fValue * DVD_TIME_BASE) ) ;
2620 float CDVDPlayer::GetAVDelay()
2622 return m_dvdPlayerVideo.GetDelay() / (float)DVD_TIME_BASE;
2625 void CDVDPlayer::SetSubTitleDelay(float fValue)
2627 m_dvdPlayerVideo.SetSubtitleDelay(-fValue * DVD_TIME_BASE);
2630 float CDVDPlayer::GetSubTitleDelay()
2632 return -m_dvdPlayerVideo.GetSubtitleDelay() / DVD_TIME_BASE;
2635 // priority: 1: libdvdnav, 2: external subtitles, 3: muxed subtitles
2636 int CDVDPlayer::GetSubtitleCount()
2638 StreamLock lock(this);
2639 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
2640 return m_SelectionStreams.Count(STREAM_SUBTITLE);
2643 int CDVDPlayer::GetSubtitle()
2645 return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, *this);
2648 void CDVDPlayer::GetSubtitleName(int iStream, CStdString &strStreamName)
2651 SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream);
2652 if(s.name.length() > 0)
2653 strStreamName = s.name;
2655 strStreamName = g_localizeStrings.Get(13205); // Unknown
2657 if(s.type == STREAM_NONE)
2658 strStreamName += "(Invalid)";
2661 void CDVDPlayer::GetSubtitleLanguage(int iStream, CStdString &strStreamLang)
2663 SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream);
2664 if (!g_LangCodeExpander.Lookup(strStreamLang, s.language))
2665 strStreamLang = g_localizeStrings.Get(13205); // Unknown
2668 void CDVDPlayer::SetSubtitle(int iStream)
2670 m_messenger.Put(new CDVDMsgPlayerSetSubtitleStream(iStream));
2673 bool CDVDPlayer::GetSubtitleVisible()
2675 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2677 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2678 if(pStream->IsInMenu())
2679 return g_settings.m_currentVideoSettings.m_SubtitleOn;
2681 return pStream->IsSubtitleStreamEnabled();
2684 return m_dvdPlayerVideo.IsSubtitleEnabled();
2687 void CDVDPlayer::SetSubtitleVisible(bool bVisible)
2689 g_settings.m_currentVideoSettings.m_SubtitleOn = bVisible;
2690 m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
2693 int CDVDPlayer::GetAudioStreamCount()
2695 StreamLock lock(this);
2696 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
2697 return m_SelectionStreams.Count(STREAM_AUDIO);
2700 int CDVDPlayer::GetAudioStream()
2702 return m_SelectionStreams.IndexOf(STREAM_AUDIO, *this);
2705 void CDVDPlayer::GetAudioStreamName(int iStream, CStdString& strStreamName)
2708 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream);
2709 if(s.name.length() > 0)
2710 strStreamName += s.name;
2712 strStreamName += "Unknown";
2714 if(s.type == STREAM_NONE)
2715 strStreamName += " (Invalid)";
2718 void CDVDPlayer::SetAudioStream(int iStream)
2720 m_messenger.Put(new CDVDMsgPlayerSetAudioStream(iStream));
2721 SynchronizeDemuxer(100);
2724 TextCacheStruct_t* CDVDPlayer::GetTeletextCache()
2726 if (m_CurrentTeletext.id < 0)
2729 return m_dvdPlayerTeletext.GetTeletextCache();
2732 void CDVDPlayer::LoadPage(int p, int sp, unsigned char* buffer)
2734 if (m_CurrentTeletext.id < 0)
2737 return m_dvdPlayerTeletext.LoadPage(p, sp, buffer);
2740 void CDVDPlayer::SeekTime(int64_t iTime)
2742 int seekOffset = (int)(iTime - GetTime());
2743 m_messenger.Put(new CDVDMsgPlayerSeek((int)iTime, true, true, true));
2744 SynchronizeDemuxer(100);
2745 m_callback.OnPlayBackSeek((int)iTime, seekOffset);
2748 // return the time in milliseconds
2749 int64_t CDVDPlayer::GetTime()
2751 CSingleLock lock(m_StateSection);
2753 if(m_State.timestamp > 0)
2755 offset = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2756 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2757 if(offset > 1000) offset = 1000;
2758 if(offset < -1000) offset = -1000;
2760 return llrint(m_State.time + DVD_TIME_TO_MSEC(offset));
2763 // return length in msec
2764 int64_t CDVDPlayer::GetTotalTimeInMsec()
2766 CSingleLock lock(m_StateSection);
2767 return llrint(m_State.time_total);
2770 // return length in seconds.. this should be changed to return in milleseconds throughout xbmc
2771 int64_t CDVDPlayer::GetTotalTime()
2773 return GetTotalTimeInMsec();
2776 void CDVDPlayer::ToFFRW(int iSpeed)
2778 // can't rewind in menu as seeking isn't possible
2780 if (iSpeed < 0 && IsInMenu()) return;
2781 SetPlaySpeed(iSpeed * DVD_PLAYSPEED_NORMAL);
2784 bool CDVDPlayer::OpenAudioStream(int iStream, int source, bool reset)
2786 CLog::Log(LOGNOTICE, "Opening audio stream: %i source: %i", iStream, source);
2791 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2792 if (!pStream || pStream->disabled)
2795 if( m_CurrentAudio.id < 0 && m_CurrentVideo.id >= 0 )
2797 // up until now we wheren't playing audio, but we did play video
2798 // this will change what is used to sync the dvdclock.
2799 // since the new audio data doesn't have to have any relation
2800 // to the current video data in the packet que, we have to
2801 // wait for it to empty
2803 // this happens if a new cell has audio data, but previous didn't
2804 // and both have video data
2806 SynchronizePlayers(SYNCSOURCE_AUDIO);
2809 CDVDStreamInfo hint(*pStream, true);
2811 if(m_CurrentAudio.id < 0
2812 || m_CurrentAudio.hint != hint)
2814 if (!m_dvdPlayerAudio.OpenStream( hint ))
2816 /* mark stream as disabled, to disallaw further attempts*/
2817 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2818 pStream->disabled = true;
2819 pStream->SetDiscard(AVDISCARD_ALL);
2824 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2826 /* store information about stream */
2827 m_CurrentAudio.id = iStream;
2828 m_CurrentAudio.source = source;
2829 m_CurrentAudio.hint = hint;
2830 m_CurrentAudio.stream = (void*)pStream;
2831 m_CurrentAudio.started = false;
2833 /* we are potentially going to be waiting on this */
2834 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2836 /* audio normally won't consume full cpu, so let it have prio */
2837 m_dvdPlayerAudio.SetPriority(GetPriority()+1);
2842 bool CDVDPlayer::OpenVideoStream(int iStream, int source, bool reset)
2844 CLog::Log(LOGNOTICE, "Opening video stream: %i source: %i", iStream, source);
2849 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2850 if(!pStream || pStream->disabled)
2852 pStream->SetDiscard(AVDISCARD_NONE);
2854 CDVDStreamInfo hint(*pStream, true);
2856 if( m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) )
2858 /* set aspect ratio as requested by navigator for dvd's */
2859 float aspect = static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->GetVideoAspectRatio();
2862 hint.aspect = aspect;
2863 hint.forced_aspect = true;
2865 hint.software = true;
2868 boost::shared_ptr<CPVRClient> client;
2869 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) &&
2870 pStream->type == STREAM_VIDEO &&
2871 g_PVRClients->GetPlayingClient(client) && client->HandlesDemuxing())
2873 // set the fps in hints
2874 const CDemuxStreamVideo *stream = static_cast<const CDemuxStreamVideo*>(pStream);
2875 hint.fpsrate = stream->iFpsRate;
2876 hint.fpsscale = stream->iFpsScale;
2879 CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
2880 if(pMenus && pMenus->IsInMenu())
2883 if(m_CurrentVideo.id < 0
2884 || m_CurrentVideo.hint != hint)
2886 if (!m_dvdPlayerVideo.OpenStream(hint))
2888 /* mark stream as disabled, to disallaw further attempts */
2889 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2890 pStream->disabled = true;
2891 pStream->SetDiscard(AVDISCARD_ALL);
2896 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2898 /* store information about stream */
2899 m_CurrentVideo.id = iStream;
2900 m_CurrentVideo.source = source;
2901 m_CurrentVideo.hint = hint;
2902 m_CurrentVideo.stream = (void*)pStream;
2903 m_CurrentVideo.started = false;
2905 /* we are potentially going to be waiting on this */
2906 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2908 #if defined(TARGET_DARWIN)
2909 // Apple thread scheduler works a little different than Linux. It
2910 // will favor OS GUI side and can cause DVDPlayerVideo to miss frame
2911 // updates when the OS gets busy. Apple's recomended method is to
2912 // elevate time critical threads to SCHED_RR and OSX does this for
2913 // the CoreAudio audio device handler thread. We do the same for
2914 // the DVDPlayerVideo thread so it can run to sleep without getting
2915 // swapped out by a busy OS.
2916 m_dvdPlayerVideo.SetPriority(GetSchedRRPriority());
2918 /* use same priority for video thread as demuxing thread, as */
2919 /* otherwise demuxer will starve if video consumes the full cpu */
2920 m_dvdPlayerVideo.SetPriority(GetPriority());
2926 bool CDVDPlayer::OpenSubtitleStream(int iStream, int source)
2928 CLog::Log(LOGNOTICE, "Opening Subtitle stream: %i source: %i", iStream, source);
2930 CDemuxStream* pStream = NULL;
2931 std::string filename;
2932 CDVDStreamInfo hint;
2934 if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
2936 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2939 SelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
2941 if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename)
2943 CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str());
2944 auto_ptr<CDVDDemuxVobsub> demux(new CDVDDemuxVobsub());
2945 if(!demux->Open(st.filename, st.filename2))
2947 m_pSubtitleDemuxer = demux.release();
2950 pStream = m_pSubtitleDemuxer->GetStream(iStream);
2951 if(!pStream || pStream->disabled)
2953 pStream->SetDiscard(AVDISCARD_NONE);
2954 double pts = m_dvdPlayerVideo.GetCurrentPts();
2955 if(pts == DVD_NOPTS_VALUE)
2956 pts = m_CurrentVideo.dts;
2957 if(pts == DVD_NOPTS_VALUE)
2959 pts += m_offset_pts;
2960 m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE));
2962 hint.Assign(*pStream, true);
2964 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
2966 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2969 filename = m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename;
2972 hint.fpsscale = m_CurrentVideo.hint.fpsscale;
2973 hint.fpsrate = m_CurrentVideo.hint.fpsrate;
2979 pStream = m_pDemuxer->GetStream(iStream);
2980 if(!pStream || pStream->disabled)
2982 pStream->SetDiscard(AVDISCARD_NONE);
2984 hint.Assign(*pStream, true);
2986 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2990 if(m_CurrentSubtitle.id < 0
2991 || m_CurrentSubtitle.hint != hint)
2993 if(m_CurrentSubtitle.id >= 0)
2995 CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream");
2996 CloseSubtitleStream(false);
2999 if(!m_dvdPlayerSubtitle.OpenStream(hint, filename))
3001 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
3004 pStream->disabled = true;
3005 pStream->SetDiscard(AVDISCARD_ALL);
3011 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3013 m_CurrentSubtitle.id = iStream;
3014 m_CurrentSubtitle.source = source;
3015 m_CurrentSubtitle.hint = hint;
3016 m_CurrentSubtitle.stream = (void*)pStream;
3017 m_CurrentSubtitle.started = false;
3022 bool CDVDPlayer::OpenTeletextStream(int iStream, int source)
3027 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
3028 if(!pStream || pStream->disabled)
3031 CDVDStreamInfo hint(*pStream, true);
3033 if (!m_dvdPlayerTeletext.CheckStream(hint))
3036 CLog::Log(LOGNOTICE, "Opening teletext stream: %i source: %i", iStream, source);
3038 if(m_CurrentTeletext.id < 0
3039 || m_CurrentTeletext.hint != hint)
3041 if(m_CurrentTeletext.id >= 0)
3043 CLog::Log(LOGDEBUG, " - teletext codecs hints have changed, must close previous stream");
3044 CloseTeletextStream(true);
3047 if (!m_dvdPlayerTeletext.OpenStream(hint))
3049 /* mark stream as disabled, to disallaw further attempts*/
3050 CLog::Log(LOGWARNING, "%s - Unsupported teletext stream %d. Stream disabled.", __FUNCTION__, iStream);
3051 pStream->disabled = true;
3052 pStream->SetDiscard(AVDISCARD_ALL);
3057 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3059 /* store information about stream */
3060 m_CurrentTeletext.id = iStream;
3061 m_CurrentTeletext.source = source;
3062 m_CurrentTeletext.hint = hint;
3063 m_CurrentTeletext.stream = (void*)pStream;
3064 m_CurrentTeletext.started = false;
3069 bool CDVDPlayer::CloseAudioStream(bool bWaitForBuffers)
3071 if (m_CurrentAudio.id < 0)
3074 CLog::Log(LOGNOTICE, "Closing audio stream");
3077 SetCaching(CACHESTATE_DONE);
3079 m_dvdPlayerAudio.CloseStream(bWaitForBuffers);
3081 m_CurrentAudio.Clear();
3085 bool CDVDPlayer::CloseVideoStream(bool bWaitForBuffers)
3087 if (m_CurrentVideo.id < 0)
3090 CLog::Log(LOGNOTICE, "Closing video stream");
3093 SetCaching(CACHESTATE_DONE);
3095 m_dvdPlayerVideo.CloseStream(bWaitForBuffers);
3097 m_CurrentVideo.Clear();
3101 bool CDVDPlayer::CloseSubtitleStream(bool bKeepOverlays)
3103 if (m_CurrentSubtitle.id < 0)
3106 CLog::Log(LOGNOTICE, "Closing subtitle stream");
3108 m_dvdPlayerSubtitle.CloseStream(!bKeepOverlays);
3110 m_CurrentSubtitle.Clear();
3114 bool CDVDPlayer::CloseTeletextStream(bool bWaitForBuffers)
3116 if (m_CurrentTeletext.id < 0)
3119 CLog::Log(LOGNOTICE, "Closing teletext stream");
3122 SetCaching(CACHESTATE_DONE);
3124 m_dvdPlayerTeletext.CloseStream(bWaitForBuffers);
3126 m_CurrentTeletext.Clear();
3130 void CDVDPlayer::FlushBuffers(bool queued, double pts, bool accurate)
3136 startpts = DVD_NOPTS_VALUE;
3138 /* call with demuxer pts */
3139 if(startpts != DVD_NOPTS_VALUE)
3140 startpts -= m_offset_pts;
3142 m_CurrentAudio.inited = false;
3143 m_CurrentAudio.dts = DVD_NOPTS_VALUE;
3144 m_CurrentAudio.startpts = startpts;
3146 m_CurrentVideo.inited = false;
3147 m_CurrentVideo.dts = DVD_NOPTS_VALUE;
3148 m_CurrentVideo.startpts = startpts;
3150 m_CurrentSubtitle.inited = false;
3151 m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
3152 m_CurrentSubtitle.startpts = startpts;
3154 m_CurrentTeletext.inited = false;
3155 m_CurrentTeletext.dts = DVD_NOPTS_VALUE;
3156 m_CurrentTeletext.startpts = startpts;
3160 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3161 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3162 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
3163 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3164 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3165 SynchronizePlayers(SYNCSOURCE_ALL);
3169 m_dvdPlayerAudio.Flush();
3170 m_dvdPlayerVideo.Flush();
3171 m_dvdPlayerSubtitle.Flush();
3172 m_dvdPlayerTeletext.Flush();
3174 // clear subtitle and menu overlays
3175 m_overlayContainer.Clear();
3177 if(m_playSpeed == DVD_PLAYSPEED_NORMAL
3178 || m_playSpeed == DVD_PLAYSPEED_PAUSE)
3180 // make sure players are properly flushed, should put them in stalled state
3181 CDVDMsgGeneralSynchronize* msg = new CDVDMsgGeneralSynchronize(1000, 0);
3182 m_dvdPlayerAudio.SendMessage(msg->Acquire(), 1);
3183 m_dvdPlayerVideo.SendMessage(msg->Acquire(), 1);
3184 msg->Wait(&m_bStop, 0);
3187 // purge any pending PLAYER_STARTED messages
3188 m_messenger.Flush(CDVDMsg::PLAYER_STARTED);
3190 // we should now wait for init cache
3191 SetCaching(CACHESTATE_FLUSH);
3192 m_CurrentAudio.started = false;
3193 m_CurrentVideo.started = false;
3194 m_CurrentSubtitle.started = false;
3195 m_CurrentTeletext.started = false;
3198 if(pts != DVD_NOPTS_VALUE)
3199 m_clock.Discontinuity(pts);
3204 // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
3205 int CDVDPlayer::OnDVDNavResult(void* pData, int iMessage)
3207 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
3210 m_overlayContainer.Add((CDVDOverlay*)pData);
3211 else if(iMessage == 1)
3212 m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
3213 else if(iMessage == 2)
3214 m_dvd.iSelectedAudioStream = *(int*)pData;
3215 else if(iMessage == 3)
3216 m_dvd.iSelectedSPUStream = *(int*)pData;
3217 else if(iMessage == 4)
3218 m_dvdPlayerVideo.EnableSubtitle(*(int*)pData ? true: false);
3223 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3225 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
3229 case DVDNAV_STILL_FRAME:
3231 //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
3233 dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)pData;
3234 // should wait the specified time here while we let the player running
3235 // after that call dvdnav_still_skip(m_dvdnav);
3237 if (m_dvd.state != DVDSTATE_STILL)
3239 // else notify the player we have received a still frame
3241 if(still_event->length < 0xff)
3242 m_dvd.iDVDStillTime = still_event->length * 1000;
3244 m_dvd.iDVDStillTime = 0;
3246 m_dvd.iDVDStillStartTime = XbmcThreads::SystemClockMillis();
3248 /* adjust for the output delay in the video queue */
3250 if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 )
3252 time = (DWORD)(m_dvdPlayerVideo.GetOutputDelay() / ( DVD_TIME_BASE / 1000 ));
3253 if( time < 10000 && time > 0 )
3254 m_dvd.iDVDStillTime += time;
3256 m_dvd.state = DVDSTATE_STILL;
3258 "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec",
3259 still_event->length, time / 1000);
3261 return NAVRESULT_HOLD;
3264 case DVDNAV_SPU_CLUT_CHANGE:
3266 m_dvdPlayerSubtitle.SendMessage(new CDVDMsgSubtitleClutChange((BYTE*)pData));
3269 case DVDNAV_SPU_STREAM_CHANGE:
3271 dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)pData;
3273 int iStream = event->physical_wide;
3274 bool visible = !(iStream & 0x80);
3276 m_dvdPlayerVideo.EnableSubtitle(visible);
3279 m_dvd.iSelectedSPUStream = (iStream & ~0x80);
3281 m_dvd.iSelectedSPUStream = -1;
3283 m_CurrentSubtitle.stream = NULL;
3286 case DVDNAV_AUDIO_STREAM_CHANGE:
3288 // This should be the correct way i think, however we don't have any streams right now
3289 // since the demuxer hasn't started so it doesn't change. not sure how to do this.
3290 dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)pData;
3292 // Tell system what audiostream should be opened by default
3293 if (event->logical >= 0)
3294 m_dvd.iSelectedAudioStream = event->physical;
3296 m_dvd.iSelectedAudioStream = -1;
3298 m_CurrentAudio.stream = NULL;
3301 case DVDNAV_HIGHLIGHT:
3303 //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
3304 int iButton = pStream->GetCurrentButton();
3305 CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button %d\n", iButton);
3306 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
3309 case DVDNAV_VTS_CHANGE:
3311 //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
3312 CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
3314 //Make sure we clear all the old overlays here, or else old forced items are left.
3315 m_overlayContainer.Clear();
3317 //Force an aspect ratio that is set in the dvdheaders if available
3318 m_CurrentVideo.hint.aspect = pStream->GetVideoAspectRatio();
3319 if( m_dvdPlayerVideo.IsInited() )
3320 m_dvdPlayerVideo.SendMessage(new CDVDMsgDouble(CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
3322 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
3323 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
3325 return NAVRESULT_HOLD;
3328 case DVDNAV_CELL_CHANGE:
3330 //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
3331 CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
3333 m_dvd.state = DVDSTATE_NORMAL;
3335 if( m_dvdPlayerVideo.IsInited() )
3336 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
3339 case DVDNAV_NAV_PACKET:
3341 //pci_t* pci = (pci_t*)pData;
3343 // this should be possible to use to make sure we get
3344 // seamless transitions over these boundaries
3345 // if we remember the old vobunits boundaries
3346 // when a packet comes out of demuxer that has
3347 // pts values outside that boundary, it belongs
3348 // to the new vobunit, wich has new timestamps
3352 case DVDNAV_HOP_CHANNEL:
3354 // This event is issued whenever a non-seamless operation has been executed.
3355 // Applications with fifos should drop the fifos content to speed up responsiveness.
3356 CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
3357 if(m_dvd.state == DVDSTATE_SEEK)
3358 m_dvd.state = DVDSTATE_NORMAL;
3360 m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
3362 return NAVRESULT_ERROR;
3367 CLog::Log(LOGDEBUG, "DVDNAV_STOP");
3368 m_dvd.state = DVDSTATE_NORMAL;
3376 return NAVRESULT_NOP;
3379 bool CDVDPlayer::ShowPVRChannelInfo(void)
3381 bool bReturn(false);
3383 if (g_guiSettings.GetBool("pvrmenu.infoswitch"))
3385 int iTimeout = g_guiSettings.GetBool("pvrmenu.infotimeout") ? g_guiSettings.GetInt("pvrmenu.infotime") : 0;
3386 g_PVRManager.ShowPlayerInfo(iTimeout);
3394 bool CDVDPlayer::OnAction(const CAction &action)
3396 #define THREAD_ACTION(action) \
3398 if (!IsCurrentThread()) { \
3399 m_messenger.Put(new CDVDMsgType<CAction>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
3404 CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3407 if( m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0 && pMenus->GetTotalButtons() == 0 )
3409 switch(action.GetID())
3411 case ACTION_NEXT_ITEM:
3412 case ACTION_MOVE_RIGHT:
3413 case ACTION_MOVE_UP:
3414 case ACTION_SELECT_ITEM:
3416 THREAD_ACTION(action);
3417 /* this will force us out of the stillframe */
3418 CLog::Log(LOGDEBUG, "%s - User asked to exit stillframe", __FUNCTION__);
3419 m_dvd.iDVDStillStartTime = 0;
3420 m_dvd.iDVDStillTime = 1;
3427 switch (action.GetID())
3429 /* this code is disabled to allow switching playlist items (dvdimage "stacks") */
3431 case ACTION_PREV_ITEM: // SKIP-:
3433 THREAD_ACTION(action);
3434 CLog::Log(LOGDEBUG, " - pushed prev");
3435 pMenus->OnPrevious();
3436 g_infoManager.SetDisplayAfterSeek();
3440 case ACTION_NEXT_ITEM: // SKIP+:
3442 THREAD_ACTION(action);
3443 CLog::Log(LOGDEBUG, " - pushed next");
3445 g_infoManager.SetDisplayAfterSeek();
3450 case ACTION_SHOW_VIDEOMENU: // start button
3452 THREAD_ACTION(action);
3453 CLog::Log(LOGDEBUG, " - go to menu");
3455 // send a message to everyone that we've gone to the menu
3456 CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0);
3457 g_windowManager.SendMessage(msg);
3463 if (pMenus->IsInMenu())
3465 switch (action.GetID())
3467 case ACTION_NEXT_ITEM:
3468 THREAD_ACTION(action);
3469 CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide");
3471 g_infoManager.SetDisplayAfterSeek();
3473 case ACTION_PREV_ITEM:
3474 THREAD_ACTION(action);
3475 CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
3476 pMenus->OnPrevious();
3477 g_infoManager.SetDisplayAfterSeek();
3479 case ACTION_PREVIOUS_MENU:
3480 case ACTION_NAV_BACK:
3482 THREAD_ACTION(action);
3483 CLog::Log(LOGDEBUG, " - menu back");
3487 case ACTION_MOVE_LEFT:
3489 THREAD_ACTION(action);
3490 CLog::Log(LOGDEBUG, " - move left");
3494 case ACTION_MOVE_RIGHT:
3496 THREAD_ACTION(action);
3497 CLog::Log(LOGDEBUG, " - move right");
3501 case ACTION_MOVE_UP:
3503 THREAD_ACTION(action);
3504 CLog::Log(LOGDEBUG, " - move up");
3508 case ACTION_MOVE_DOWN:
3510 THREAD_ACTION(action);
3511 CLog::Log(LOGDEBUG, " - move down");
3516 case ACTION_MOUSE_MOVE:
3517 case ACTION_MOUSE_LEFT_CLICK:
3520 GetVideoRect(rs, rd);
3521 CPoint pt(action.GetAmount(), action.GetAmount(1));
3522 if (!rd.PtInRect(pt))
3523 return false; // out of bounds
3524 THREAD_ACTION(action);
3525 // convert to video coords...
3526 pt -= CPoint(rd.x1, rd.y1);
3527 pt.x *= rs.Width() / rd.Width();
3528 pt.y *= rs.Height() / rd.Height();
3529 pt += CPoint(rs.x1, rs.y1);
3530 if (action.GetID() == ACTION_MOUSE_LEFT_CLICK)
3531 return pMenus->OnMouseClick(pt);
3532 return pMenus->OnMouseMove(pt);
3535 case ACTION_SELECT_ITEM:
3537 THREAD_ACTION(action);
3538 CLog::Log(LOGDEBUG, " - button select");
3539 // show button pushed overlay
3540 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3541 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_CLICKED);
3543 pMenus->ActivateButton();
3557 THREAD_ACTION(action);
3558 // Offset from key codes back to button number
3559 int button = action.GetID() - REMOTE_0;
3560 CLog::Log(LOGDEBUG, " - button pressed %d", button);
3561 pMenus->SelectButton(button);
3568 return true; // message is handled
3572 if (dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream))
3574 switch (action.GetID())
3576 case ACTION_MOVE_UP:
3577 case ACTION_NEXT_ITEM:
3578 case ACTION_CHANNEL_UP:
3579 m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT));
3580 g_infoManager.SetDisplayAfterSeek();
3581 ShowPVRChannelInfo();
3585 case ACTION_MOVE_DOWN:
3586 case ACTION_PREV_ITEM:
3587 case ACTION_CHANNEL_DOWN:
3588 m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV));
3589 g_infoManager.SetDisplayAfterSeek();
3590 ShowPVRChannelInfo();
3594 case ACTION_CHANNEL_SWITCH:
3596 // Offset from key codes back to button number
3597 int channel = action.GetAmount();
3598 m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER, channel));
3599 g_infoManager.SetDisplayAfterSeek();
3600 ShowPVRChannelInfo();
3607 switch (action.GetID())
3609 case ACTION_NEXT_ITEM:
3610 if(GetChapterCount() > 0)
3612 m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()+1));
3613 g_infoManager.SetDisplayAfterSeek();
3618 case ACTION_PREV_ITEM:
3619 if(GetChapterCount() > 0)
3621 m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()-1));
3622 g_infoManager.SetDisplayAfterSeek();
3629 // return false to inform the caller we didn't handle the message
3633 bool CDVDPlayer::IsInMenu() const
3635 CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3638 if( m_dvd.state == DVDSTATE_STILL )
3641 return pStream->IsInMenu();
3646 bool CDVDPlayer::HasMenu()
3648 CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3655 bool CDVDPlayer::GetCurrentSubtitle(CStdString& strSubtitle)
3657 double pts = m_clock.GetClock();
3659 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3662 m_dvdPlayerSubtitle.GetCurrentSubtitle(strSubtitle, pts - m_dvdPlayerVideo.GetSubtitleDelay());
3664 // In case we stalled, don't output any subs
3665 if ((m_dvdPlayerVideo.IsStalled() && HasVideo()) || (m_dvdPlayerAudio.IsStalled() && HasAudio()))
3666 strSubtitle = m_lastSub;
3668 m_lastSub = strSubtitle;
3670 return !strSubtitle.IsEmpty();
3673 CStdString CDVDPlayer::GetPlayerState()
3675 CSingleLock lock(m_StateSection);
3676 return m_State.player_state;
3679 bool CDVDPlayer::SetPlayerState(CStdString state)
3681 m_messenger.Put(new CDVDMsgPlayerSetState(state));
3685 int CDVDPlayer::GetChapterCount()
3687 CSingleLock lock(m_StateSection);
3688 return m_State.chapter_count;
3691 int CDVDPlayer::GetChapter()
3693 CSingleLock lock(m_StateSection);
3694 return m_State.chapter;
3697 void CDVDPlayer::GetChapterName(CStdString& strChapterName)
3699 CSingleLock lock(m_StateSection);
3700 strChapterName = m_State.chapter_name;
3703 int CDVDPlayer::SeekChapter(int iChapter)
3705 if (GetChapterCount() > 0)
3709 if (iChapter > GetChapterCount())
3712 // Seek to the chapter.
3713 m_messenger.Put(new CDVDMsgPlayerSeekChapter(iChapter));
3714 SynchronizeDemuxer(100);
3718 // Do a regular big jump.
3719 if (GetChapter() > 0 && iChapter > GetChapter())
3727 int CDVDPlayer::AddSubtitle(const CStdString& strSubPath)
3729 return AddSubtitleFile(strSubPath);
3732 int CDVDPlayer::GetCacheLevel() const
3734 CSingleLock lock(m_StateSection);
3735 return (int)(m_State.cache_level * 100);
3738 double CDVDPlayer::GetQueueTime()
3740 int a = m_dvdPlayerAudio.GetLevel();
3741 int v = m_dvdPlayerVideo.GetLevel();
3742 return max(a, v) * 8000.0 / 100;
3745 int CDVDPlayer::GetAudioBitrate()
3747 return m_dvdPlayerAudio.GetAudioBitrate();
3750 int CDVDPlayer::GetVideoBitrate()
3752 return m_dvdPlayerVideo.GetVideoBitrate();
3755 int CDVDPlayer::GetSourceBitrate()
3758 return (int)m_pInputStream->GetBitstreamStats().GetBitrate();
3764 int CDVDPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename, CDemuxStream::EFlags flags)
3766 std::string ext = URIUtils::GetExtension(filename);
3767 std::string vobsubfile = subfilename;
3770 if (vobsubfile.empty())
3771 vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
3774 if(!v.Open(filename, vobsubfile))
3776 m_SelectionStreams.Update(NULL, &v);
3777 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename), 0);
3778 m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags;
3779 m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename2 = vobsubfile;
3784 CStdString strReplace(URIUtils::ReplaceExtension(filename,".idx"));
3785 if (XFILE::CFile::Exists(strReplace))
3789 s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename);
3790 s.type = STREAM_SUBTITLE;
3792 s.filename = filename;
3793 s.name = URIUtils::GetFileName(filename);
3795 m_SelectionStreams.Update(s);
3796 return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, s.source, s.id);
3799 void CDVDPlayer::UpdatePlayState(double timeout)
3801 if(m_State.timestamp != 0
3802 && m_State.timestamp + DVD_MSEC_TO_TIME(timeout) > CDVDClock::GetAbsoluteClock())
3805 SPlayerState state(m_State);
3807 if (m_CurrentVideo.dts != DVD_NOPTS_VALUE)
3808 state.dts = m_CurrentVideo.dts;
3809 else if(m_CurrentAudio.dts != DVD_NOPTS_VALUE)
3810 state.dts = m_CurrentAudio.dts;
3812 state.dts = m_clock.GetClock();
3816 state.chapter = m_pDemuxer->GetChapter();
3817 state.chapter_count = m_pDemuxer->GetChapterCount();
3818 m_pDemuxer->GetChapterName(state.chapter_name);
3820 state.time = DVD_TIME_TO_MSEC(m_clock.GetClock() + m_offset_pts);
3821 state.time_total = m_pDemuxer->GetStreamLength();
3822 state.time_src = ETIMESOURCE_CLOCK;
3827 // override from input stream if needed
3828 CDVDInputStream::IChannel* pChannel = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
3831 state.canrecord = pChannel->CanRecord();
3832 state.recording = pChannel->IsRecording();
3835 CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast<CDVDInputStream::IDisplayTime*>(m_pInputStream);
3836 if (pDisplayTime && pDisplayTime->GetTotalTime() > 0)
3838 state.time = pDisplayTime->GetTime();
3839 state.time_total = pDisplayTime->GetTotalTime();
3840 state.time_src = ETIMESOURCE_INPUT;
3843 if (dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
3845 if(m_dvd.state == DVDSTATE_STILL)
3847 state.time = XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime;
3848 state.time_total = m_dvd.iDVDStillTime;
3849 state.time_src = ETIMESOURCE_MENU;
3853 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
3855 CDVDInputStreamPVRManager* pvrinputstream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream);
3856 state.canpause = pvrinputstream->CanPause();
3857 state.canseek = pvrinputstream->CanSeek();
3861 state.canseek = state.time_total > 0 ? true : false;
3862 state.canpause = true;
3868 state.time = m_Edl.RemoveCutTime(llrint(state.time));
3869 state.time_total = m_Edl.RemoveCutTime(llrint(state.time_total));
3872 state.player_state = "";
3873 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3875 if(!((CDVDInputStreamNavigator*)m_pInputStream)->GetNavigatorState(state.player_state))
3876 state.player_state = "";
3879 if (state.time_src == ETIMESOURCE_CLOCK)
3880 state.time_offset = 0;
3882 state.time_offset = DVD_MSEC_TO_TIME(state.time) - state.dts;
3884 if (m_CurrentAudio.id >= 0 && m_pDemuxer)
3886 CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentAudio.id);
3887 if (pStream && pStream->type == STREAM_AUDIO)
3888 ((CDemuxStreamAudio*)pStream)->GetStreamInfo(state.demux_audio);
3891 state.demux_audio = "";
3893 if (m_CurrentVideo.id >= 0 && m_pDemuxer)
3895 CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentVideo.id);
3896 if (pStream && pStream->type == STREAM_VIDEO)
3897 ((CDemuxStreamVideo*)pStream)->GetStreamInfo(state.demux_video);
3900 state.demux_video = "";
3902 double level, delay, offset;
3903 if(GetCachingTimes(level, delay, offset))
3905 state.cache_delay = max(0.0, delay);
3906 state.cache_level = max(0.0, min(1.0, level));
3907 state.cache_offset = offset;
3911 state.cache_delay = 0.0;
3912 state.cache_level = min(1.0, GetQueueTime() / 8000.0);
3913 state.cache_offset = GetQueueTime() / state.time_total;
3916 XFILE::SCacheStatus status;
3917 if(m_pInputStream && m_pInputStream->GetCacheStatus(&status))
3919 state.cache_bytes = status.forward;
3920 if(state.time_total)
3921 state.cache_bytes += m_pInputStream->GetLength() * GetQueueTime() / state.time_total;
3924 state.cache_bytes = 0;
3926 state.timestamp = CDVDClock::GetAbsoluteClock();
3928 CSingleLock lock(m_StateSection);
3932 void CDVDPlayer::UpdateApplication(double timeout)
3934 if(m_UpdateApplication != 0
3935 && m_UpdateApplication + DVD_MSEC_TO_TIME(timeout) > CDVDClock::GetAbsoluteClock())
3938 CDVDInputStream::IChannel* pStream = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
3941 CFileItem item(g_application.CurrentFileItem());
3942 if(pStream->UpdateItem(item))
3944 g_application.CurrentFileItem() = item;
3945 CApplicationMessenger::Get().SetCurrentItem(item);
3948 m_UpdateApplication = CDVDClock::GetAbsoluteClock();
3951 bool CDVDPlayer::CanRecord()
3953 CSingleLock lock(m_StateSection);
3954 return m_State.canrecord;
3957 bool CDVDPlayer::IsRecording()
3959 CSingleLock lock(m_StateSection);
3960 return m_State.recording;
3963 bool CDVDPlayer::Record(bool bOnOff)
3965 if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV) ||
3966 m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) )
3968 m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff));
3974 int CDVDPlayer::GetChannels()
3976 if (m_pDemuxer && (m_CurrentAudio.id != -1))
3978 CDemuxStreamAudio* stream = static_cast<CDemuxStreamAudio*>(m_pDemuxer->GetStream(m_CurrentAudio.id));
3980 return stream->iChannels;
3985 CStdString CDVDPlayer::GetAudioCodecName()
3988 if (m_pDemuxer && (m_CurrentAudio.id != -1))
3989 m_pDemuxer->GetStreamCodecName(m_CurrentAudio.id, retVal);
3993 CStdString CDVDPlayer::GetVideoCodecName()
3996 if (m_pDemuxer && (m_CurrentVideo.id != -1))
3997 m_pDemuxer->GetStreamCodecName(m_CurrentVideo.id, retVal);
4001 int CDVDPlayer::GetPictureWidth()
4003 if (m_pDemuxer && (m_CurrentVideo.id != -1))
4005 CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
4007 return stream->iWidth;
4012 int CDVDPlayer::GetPictureHeight()
4014 if (m_pDemuxer && (m_CurrentVideo.id != -1))
4016 CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
4018 return stream->iHeight;
4023 bool CDVDPlayer::GetStreamDetails(CStreamDetails &details)
4027 bool result=CDVDFileInfo::DemuxerToStreamDetails(m_pInputStream, m_pDemuxer, details);
4028 if (result && details.GetStreamCount(CStreamDetail::VIDEO) > 0) // this is more correct (dvds in particular)
4030 GetVideoAspectRatio(((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_fAspect);
4031 ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_iDuration = GetTotalTime() / 1000;
4039 CStdString CDVDPlayer::GetPlayingTitle()
4041 /* Currently we support only Title Name from Teletext line 30 */
4042 TextCacheStruct_t* ttcache = m_dvdPlayerTeletext.GetTeletextCache();
4043 if (ttcache && !ttcache->line30.empty())
4044 return ttcache->line30;
4049 bool CDVDPlayer::SwitchChannel(const CPVRChannel &channel)
4051 if (!g_PVRManager.CheckParentalLock(channel))
4055 if (!g_PVRManager.PerformChannelSwitch(channel, true))
4058 UpdateApplication(0);
4061 /* make sure the pvr window is updated */
4062 CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
4064 pWindow->SetInvalid();
4066 /* select the new channel */
4067 m_messenger.Put(new CDVDMsgType<CPVRChannel>(CDVDMsg::PLAYER_CHANNEL_SELECT, channel));
4072 bool CDVDPlayer::CachePVRStream(void) const
4074 return m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) &&
4075 !g_PVRManager.IsPlayingRecording() &&
4076 g_advancedSettings.m_bPVRCacheInDvdPlayer;