2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
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/Key.h"
45 #include "guilib/LocalizeStrings.h"
47 #include "utils/URIUtils.h"
48 #include "GUIInfoManager.h"
49 #include "guilib/GUIWindowManager.h"
50 #include "Application.h"
51 #include "ApplicationMessenger.h"
52 #include "DVDPerformanceCounter.h"
53 #include "filesystem/File.h"
54 #include "pictures/Picture.h"
55 #include "DllSwScale.h"
56 #ifdef HAS_VIDEO_PLAYBACK
57 #include "cores/VideoRenderers/RenderManager.h"
59 #ifdef HAS_PERFORMANCE_SAMPLE
60 #include "xbmc/utils/PerformanceSample.h"
62 #define MEASURE_FUNCTION
64 #include "settings/AdvancedSettings.h"
66 #include "GUIUserMessages.h"
67 #include "settings/Settings.h"
68 #include "settings/MediaSettings.h"
69 #include "utils/log.h"
70 #include "utils/TimeUtils.h"
71 #include "utils/StreamDetails.h"
72 #include "pvr/PVRManager.h"
73 #include "pvr/channels/PVRChannel.h"
74 #include "pvr/windows/GUIWindowPVR.h"
75 #include "pvr/addons/PVRClients.h"
76 #include "filesystem/PVRFile.h"
77 #include "video/dialogs/GUIDialogFullScreenInfo.h"
78 #include "utils/StreamUtils.h"
79 #include "utils/Variant.h"
80 #include "storage/MediaManager.h"
81 #include "dialogs/GUIDialogBusy.h"
82 #include "dialogs/GUIDialogKaiToast.h"
83 #include "xbmc/playlists/PlayListM3U.h"
84 #include "utils/StringUtils.h"
92 void CSelectionStreams::Clear(StreamType type, StreamSource source)
94 CSingleLock lock(m_section);
95 for(int i=m_Streams.size()-1;i>=0;i--)
97 if(type && m_Streams[i].type != type)
100 if(source && m_Streams[i].source != source)
103 m_Streams.erase(m_Streams.begin() + i);
107 SelectionStream& CSelectionStreams::Get(StreamType type, int index)
109 CSingleLock lock(m_section);
111 for(int i=0;i<(int)m_Streams.size();i++)
113 if(m_Streams[i].type != type)
119 CLog::Log(LOGERROR, "%s - failed to get stream", __FUNCTION__);
123 std::vector<SelectionStream> CSelectionStreams::Get(StreamType type)
125 std::vector<SelectionStream> streams;
126 int count = Count(type);
127 for(int index = 0; index < count; ++index){
128 streams.push_back(Get(type, index));
133 #define PREDICATE_RETURN(lh, rh) \
136 return (lh) > (rh); \
139 static bool PredicateAudioPriority(const SelectionStream& lh, const SelectionStream& rh)
141 PREDICATE_RETURN(lh.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream
142 , rh.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream);
144 if(!StringUtils::EqualsNoCase(CSettings::Get().GetString("locale.audiolanguage"), "original"))
146 CStdString audio_language = g_langInfo.GetAudioLanguage();
147 PREDICATE_RETURN(audio_language.Equals(lh.language.c_str())
148 , audio_language.Equals(rh.language.c_str()));
151 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
152 , rh.flags & CDemuxStream::FLAG_DEFAULT);
154 PREDICATE_RETURN(lh.channels
157 PREDICATE_RETURN(StreamUtils::GetCodecPriority(lh.codec)
158 , StreamUtils::GetCodecPriority(rh.codec));
162 static bool PredicateSubtitlePriority(const SelectionStream& lh, const SelectionStream& rh)
164 if(!CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn)
166 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_FORCED
167 , rh.flags & CDemuxStream::FLAG_FORCED);
170 PREDICATE_RETURN(lh.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream
171 , rh.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream);
173 CStdString subtitle_language = g_langInfo.GetSubtitleLanguage();
174 if(!StringUtils::EqualsNoCase(CSettings::Get().GetString("locale.subtitlelanguage"), "original"))
176 PREDICATE_RETURN((lh.source == STREAM_SOURCE_DEMUX_SUB || lh.source == STREAM_SOURCE_TEXT) && subtitle_language.Equals(lh.language.c_str())
177 , (rh.source == STREAM_SOURCE_DEMUX_SUB || rh.source == STREAM_SOURCE_TEXT) && subtitle_language.Equals(rh.language.c_str()));
180 PREDICATE_RETURN(lh.source == STREAM_SOURCE_DEMUX_SUB
181 , rh.source == STREAM_SOURCE_DEMUX_SUB);
183 PREDICATE_RETURN(lh.source == STREAM_SOURCE_TEXT
184 , rh.source == STREAM_SOURCE_TEXT);
186 if(!StringUtils::EqualsNoCase(CSettings::Get().GetString("locale.subtitlelanguage"), "original"))
188 PREDICATE_RETURN(subtitle_language.Equals(lh.language.c_str())
189 , subtitle_language.Equals(rh.language.c_str()));
192 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
193 , rh.flags & CDemuxStream::FLAG_DEFAULT);
198 static bool PredicateVideoPriority(const SelectionStream& lh, const SelectionStream& rh)
200 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
201 , rh.flags & CDemuxStream::FLAG_DEFAULT);
205 bool CSelectionStreams::Get(StreamType type, CDemuxStream::EFlags flag, SelectionStream& out)
207 CSingleLock lock(m_section);
208 for(int i=0;i<(int)m_Streams.size();i++)
210 if(m_Streams[i].type != type)
212 if((m_Streams[i].flags & flag) != flag)
220 int CSelectionStreams::IndexOf(StreamType type, int source, int id) const
222 CSingleLock lock(m_section);
224 for(int i=0;i<(int)m_Streams.size();i++)
226 if(type && m_Streams[i].type != type)
229 if(source && m_Streams[i].source != source)
233 if(m_Streams[i].id == id)
242 int CSelectionStreams::IndexOf(StreamType type, CDVDPlayer& p) const
244 if (p.m_pInputStream && p.m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
247 if(type == STREAM_AUDIO)
248 id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveAudioStream();
249 else if(type == STREAM_VIDEO)
250 id = p.m_CurrentVideo.id;
251 else if(type == STREAM_SUBTITLE)
252 id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveSubtitleStream();
254 return IndexOf(type, STREAM_SOURCE_NAV, id);
257 if(type == STREAM_AUDIO)
258 return IndexOf(type, p.m_CurrentAudio.source, p.m_CurrentAudio.id);
259 else if(type == STREAM_VIDEO)
260 return IndexOf(type, p.m_CurrentVideo.source, p.m_CurrentVideo.id);
261 else if(type == STREAM_SUBTITLE)
262 return IndexOf(type, p.m_CurrentSubtitle.source, p.m_CurrentSubtitle.id);
263 else if(type == STREAM_TELETEXT)
264 return IndexOf(type, p.m_CurrentTeletext.source, p.m_CurrentTeletext.id);
269 int CSelectionStreams::Source(StreamSource source, std::string filename)
271 CSingleLock lock(m_section);
272 int index = source - 1;
273 for(int i=0;i<(int)m_Streams.size();i++)
275 SelectionStream &s = m_Streams[i];
276 if(STREAM_SOURCE_MASK(s.source) != source)
278 // if it already exists, return same
279 if(s.filename == filename)
288 void CSelectionStreams::Update(SelectionStream& s)
290 CSingleLock lock(m_section);
291 int index = IndexOf(s.type, s.source, s.id);
294 SelectionStream& o = Get(s.type, index);
295 s.type_index = o.type_index;
300 s.type_index = Count(s.type);
301 m_Streams.push_back(s);
305 void CSelectionStreams::Update(CDVDInputStream* input, CDVDDemux* demuxer)
307 if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
309 CDVDInputStreamNavigator* nav = (CDVDInputStreamNavigator*)input;
310 string filename = nav->GetFileName();
311 int source = Source(STREAM_SOURCE_NAV, filename);
314 count = nav->GetAudioStreamCount();
315 for(int i=0;i<count;i++)
319 s.type = STREAM_AUDIO;
321 s.flags = CDemuxStream::FLAG_NONE;
322 s.filename = filename;
324 DVDNavStreamInfo info;
325 nav->GetAudioStreamInfo(i, info);
327 s.language = info.language;
328 s.channels = info.channels;
332 count = nav->GetSubTitleStreamCount();
333 for(int i=0;i<count;i++)
337 s.type = STREAM_SUBTITLE;
339 s.flags = CDemuxStream::FLAG_NONE;
340 s.filename = filename;
343 DVDNavStreamInfo info;
344 nav->GetSubtitleStreamInfo(i, info);
346 s.language = info.language;
352 string filename = demuxer->GetFileName();
353 int count = demuxer->GetNrOfStreams();
355 if(input) /* hack to know this is sub decoder */
356 source = Source(STREAM_SOURCE_DEMUX, filename);
358 source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
361 for(int i=0;i<count;i++)
363 CDemuxStream* stream = demuxer->GetStream(i);
364 /* make sure stream is marked with right source */
365 stream->source = source;
369 s.type = stream->type;
371 s.language = stream->language;
373 if (s.language.length() == 2)
376 g_LangCodeExpander.ConvertToThreeCharCode(lang, stream->language);
380 s.flags = stream->flags;
381 s.filename = demuxer->GetFileName();
382 stream->GetStreamName(s.name);
384 demuxer->GetStreamCodecName(stream->iId, codec);
386 s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below.
387 if(stream->type == STREAM_AUDIO)
390 ((CDemuxStreamAudio*)stream)->GetStreamType(type);
391 if(type.length() > 0)
393 if(s.name.length() > 0)
397 s.channels = ((CDemuxStreamAudio*)stream)->iChannels;
404 CDVDPlayer::CDVDPlayer(IPlayerCallback& callback)
406 CThread("DVDPlayer"),
407 m_CurrentAudio(STREAM_AUDIO, DVDPLAYER_AUDIO),
408 m_CurrentVideo(STREAM_VIDEO, DVDPLAYER_VIDEO),
409 m_CurrentSubtitle(STREAM_SUBTITLE, DVDPLAYER_SUBTITLE),
410 m_CurrentTeletext(STREAM_TELETEXT, DVDPLAYER_TELETEXT),
411 m_messenger("player"),
412 m_dvdPlayerVideo(&m_clock, &m_overlayContainer, m_messenger),
413 m_dvdPlayerAudio(&m_clock, m_messenger),
414 m_dvdPlayerSubtitle(&m_overlayContainer),
415 m_dvdPlayerTeletext(),
419 m_pSubtitleDemuxer = NULL;
420 m_pInputStream = NULL;
424 m_EdlAutoSkipMarkers.Clear();
425 m_UpdateApplication = 0;
427 m_bAbortRequest = false;
430 m_playSpeed = DVD_PLAYSPEED_NORMAL;
431 m_caching = CACHESTATE_DONE;
435 memset(&m_SpeedState, 0, sizeof(m_SpeedState));
437 #ifdef DVDDEBUG_MESSAGE_TRACKER
438 g_dvdMessageTracker.Init();
442 CDVDPlayer::~CDVDPlayer()
446 #ifdef DVDDEBUG_MESSAGE_TRACKER
447 g_dvdMessageTracker.DeInit();
451 bool CDVDPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
455 CLog::Log(LOGNOTICE, "DVDPlayer: Opening: %s", file.GetPath().c_str());
457 // if playing a file close it first
458 // this has to be changed so we won't have to close it.
462 m_bAbortRequest = false;
463 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
466 m_UpdateApplication = 0;
469 m_PlayerOptions = options;
471 m_mimetype = file.GetMimeType();
472 m_filename = file.GetPath();
476 #if defined(HAS_VIDEO_PLAYBACK)
477 g_renderManager.PreInit();
481 if(!m_ready.WaitMSec(100))
483 CGUIDialogBusy* dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY);
487 while(!m_ready.WaitMSec(1))
488 g_windowManager.ProcessRenderLoop(false);
493 // Playback might have been stopped due to some error
494 if (m_bStop || m_bAbortRequest)
501 CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__);
506 bool CDVDPlayer::CloseFile()
508 CLog::Log(LOGNOTICE, "CDVDPlayer::CloseFile()");
510 // unpause the player
511 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
513 // set the abort request so that other threads can finish up
514 m_bAbortRequest = true;
516 // tell demuxer to abort
520 if(m_pSubtitleDemuxer)
521 m_pSubtitleDemuxer->Abort();
524 m_pInputStream->Abort();
526 CLog::Log(LOGNOTICE, "DVDPlayer: waiting for threads to exit");
528 // wait for the main thread to finish up
529 // since this main thread cleans up all other resources and threads
530 // we are done after the StopThread call
534 m_EdlAutoSkipMarkers.Clear();
539 CLog::Log(LOGNOTICE, "DVDPlayer: finished waiting");
540 #if defined(HAS_VIDEO_PLAYBACK)
541 g_renderManager.UnInit();
546 bool CDVDPlayer::IsPlaying() const
551 void CDVDPlayer::OnStartup()
553 m_CurrentVideo.Clear();
554 m_CurrentAudio.Clear();
555 m_CurrentSubtitle.Clear();
556 m_CurrentTeletext.Clear();
560 g_dvdPerformanceCounter.EnableMainPerformance(this);
561 CUtil::ClearTempFonts();
564 bool CDVDPlayer::OpenInputStream()
567 SAFE_DELETE(m_pInputStream);
569 CLog::Log(LOGNOTICE, "Creating InputStream");
571 // correct the filename if needed
572 CStdString filename(m_filename);
573 if (filename.Find("dvd://") == 0
574 || filename.CompareNoCase("iso9660://video_ts/video_ts.ifo") == 0)
576 m_filename = g_mediaManager.TranslateDevicePath("");
579 // before creating the input stream, if this is an HLS playlist then get the
580 // most appropriate bitrate based on our network settings
581 // ensure to strip off the url options by using a temp CURL object
582 if (filename.Left(7) == "http://" && CURL(filename).GetFileName().Right(5) == ".m3u8")
584 // get the available bandwidth (as per user settings)
585 int maxrate = CSettings::Get().GetInt("network.bandwidth");
589 // determine the most appropriate stream
590 m_filename = PLAYLIST::CPlayListM3U::GetBestBandwidthStream(m_filename, (size_t)maxrate);
592 m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_filename, m_mimetype);
593 if(m_pInputStream == NULL)
595 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - unable to create input stream for [%s]", m_filename.c_str());
599 m_pInputStream->SetFileItem(m_item);
601 if (!m_pInputStream->Open(m_filename.c_str(), m_mimetype))
603 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - error opening [%s]", m_filename.c_str());
607 // find any available external subtitles for non dvd files
608 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
609 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)
610 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
611 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
613 // find any available external subtitles
614 std::vector<CStdString> filenames;
615 CUtil::ScanForExternalSubtitles( m_filename, filenames );
617 // find any upnp subtitles
618 CStdString key("upnp:subtitle:1");
619 for(unsigned s = 1; m_item.HasProperty(key); key.Format("upnp:subtitle:%u", ++s))
620 filenames.push_back(m_item.GetProperty(key).asString());
622 for(unsigned int i=0;i<filenames.size();i++)
624 // if vobsub subtitle:
625 if (URIUtils::HasExtension(filenames[i], ".idx"))
627 CStdString strSubFile;
628 if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
629 AddSubtitleFile(filenames[i], strSubFile);
633 if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
635 AddSubtitleFile(filenames[i]);
638 } // end loop over all subtitle files
640 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleCached = true;
643 SetAVDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_AudioDelay);
644 SetSubTitleDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleDelay);
648 m_iChannelEntryTimeOut = 0;
653 bool CDVDPlayer::OpenDemuxStream()
656 SAFE_DELETE(m_pDemuxer);
658 CLog::Log(LOGNOTICE, "Creating Demuxer");
663 while(!m_bStop && attempts-- > 0)
665 m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
666 if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
670 else if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE)
672 CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__);
680 CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__);
687 CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__);
691 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
692 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
693 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
695 int64_t len = m_pInputStream->GetLength();
696 int64_t tim = m_pDemuxer->GetStreamLength();
697 if(len > 0 && tim > 0)
698 m_pInputStream->SetReadRate(len * 1000 / tim);
703 void CDVDPlayer::OpenDefaultStreams(bool reset)
705 // if input stream dictate, we will open later
706 if(m_dvd.iSelectedAudioStream >= 0
707 || m_dvd.iSelectedSPUStream >= 0)
710 SelectionStreams streams;
714 streams = m_SelectionStreams.Get(STREAM_VIDEO, PredicateVideoPriority);
716 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
718 if(OpenVideoStream(it->id, it->source, reset))
722 CloseVideoStream(true);
725 if(m_PlayerOptions.video_only)
728 streams = m_SelectionStreams.Get(STREAM_AUDIO, PredicateAudioPriority);
731 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
733 if(OpenAudioStream(it->id, it->source, reset))
737 CloseAudioStream(true);
740 m_dvdPlayerVideo.EnableSubtitle(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn);
742 // open subtitle stream
743 streams = m_SelectionStreams.Get(STREAM_SUBTITLE, PredicateSubtitlePriority);
745 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
747 if(OpenSubtitleStream(it->id, it->source))
750 if(it->flags & CDemuxStream::FLAG_FORCED)
751 m_dvdPlayerVideo.EnableSubtitle(true);
755 CloseSubtitleStream(true);
757 // open teletext stream
758 streams = m_SelectionStreams.Get(STREAM_TELETEXT);
760 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
762 if(OpenTeletextStream(it->id, it->source))
766 CloseTeletextStream(true);
769 bool CDVDPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
772 // check if we should read from subtitle demuxer
773 if( m_pSubtitleDemuxer && m_dvdPlayerSubtitle.AcceptsData() )
775 packet = m_pSubtitleDemuxer->Read();
779 UpdateCorrection(packet, m_offset_pts);
780 if(packet->iStreamId < 0)
783 stream = m_pSubtitleDemuxer->GetStream(packet->iStreamId);
786 CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
789 if(stream->source == STREAM_SOURCE_NONE)
791 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
792 m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer);
798 // read a data frame from stream.
800 packet = m_pDemuxer->Read();
804 // stream changed, update and open defaults
805 if(packet->iStreamId == DMX_SPECIALID_STREAMCHANGE)
807 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
808 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
809 OpenDefaultStreams(false);
811 // reevaluate HasVideo/Audio, we may have switched from/to a radio channel
812 if(m_CurrentVideo.id < 0)
814 if(m_CurrentAudio.id < 0)
820 UpdateCorrection(packet, m_offset_pts);
822 if(packet->iStreamId < 0)
827 stream = m_pDemuxer->GetStream(packet->iStreamId);
830 CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
833 if(stream->source == STREAM_SOURCE_NONE)
835 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
836 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
844 bool CDVDPlayer::IsValidStream(CCurrentStream& stream)
847 return true; // we consider non selected as valid
849 int source = STREAM_SOURCE_MASK(stream.source);
850 if(source == STREAM_SOURCE_TEXT)
852 if(source == STREAM_SOURCE_DEMUX_SUB)
854 CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.id);
855 if(st == NULL || st->disabled)
857 if(st->type != stream.type)
861 if(source == STREAM_SOURCE_DEMUX)
863 CDemuxStream* st = m_pDemuxer->GetStream(stream.id);
864 if(st == NULL || st->disabled)
866 if(st->type != stream.type)
869 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
871 if(stream.type == STREAM_AUDIO && st->iPhysicalId != m_dvd.iSelectedAudioStream)
873 if(stream.type == STREAM_SUBTITLE && st->iPhysicalId != m_dvd.iSelectedSPUStream)
883 bool CDVDPlayer::IsBetterStream(CCurrentStream& current, CDemuxStream* stream)
885 // Do not reopen non-video streams if we're in video-only mode
886 if(m_PlayerOptions.video_only && current.type != STREAM_VIDEO)
892 if (m_pInputStream && ( m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
893 || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) )
897 source_type = STREAM_SOURCE_MASK(current.source);
898 if(source_type != STREAM_SOURCE_DEMUX
899 && source_type != STREAM_SOURCE_NONE)
902 source_type = STREAM_SOURCE_MASK(stream->source);
903 if(source_type != STREAM_SOURCE_DEMUX
904 || stream->type != current.type
905 || stream->iId == current.id)
908 if(current.type == STREAM_AUDIO && stream->iPhysicalId == m_dvd.iSelectedAudioStream)
910 if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream)
912 if(current.type == STREAM_VIDEO && current.id < 0)
917 if(stream->source == current.source
918 && stream->iId == current.id)
921 if(stream->type != current.type)
924 if(current.type == STREAM_SUBTITLE)
933 void CDVDPlayer::Process()
935 if (!OpenInputStream())
937 m_bAbortRequest = true;
941 if (CDVDInputStream::IMenus* ptr = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
943 CLog::Log(LOGNOTICE, "DVDPlayer: playing a file with menu's");
944 m_PlayerOptions.starttime = 0;
946 if(m_PlayerOptions.state.size() > 0)
947 ptr->SetState(m_PlayerOptions.state);
948 else if(CDVDInputStreamNavigator* nav = dynamic_cast<CDVDInputStreamNavigator*>(m_pInputStream))
949 nav->EnableSubtitleStream(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn);
951 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleCached = true;
954 if(!OpenDemuxStream())
956 m_bAbortRequest = true;
960 // allow renderer to switch to fullscreen if requested
961 m_dvdPlayerVideo.EnableFullscreen(m_PlayerOptions.fullscreen);
963 OpenDefaultStreams();
965 // look for any EDL files
967 m_EdlAutoSkipMarkers.Clear();
968 float fFramesPerSecond;
969 if (m_CurrentVideo.id >= 0 && m_CurrentVideo.hint.fpsrate > 0 && m_CurrentVideo.hint.fpsscale > 0)
971 fFramesPerSecond = (float)m_CurrentVideo.hint.fpsrate / (float)m_CurrentVideo.hint.fpsscale;
972 m_Edl.ReadEditDecisionLists(m_filename, fFramesPerSecond, m_CurrentVideo.hint.height);
976 * Check to see if the demuxer should start at something other than time 0. This will be the case
977 * if there was a start time specified as part of the "Start from where last stopped" (aka
978 * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
982 if(m_PlayerOptions.starttime > 0 || m_PlayerOptions.startpercent > 0)
984 if (m_PlayerOptions.startpercent > 0 && m_pDemuxer)
986 int64_t playerStartTime = (int64_t) ( ( (float) m_pDemuxer->GetStreamLength() ) * ( m_PlayerOptions.startpercent/(float)100 ) );
987 starttime = m_Edl.RestoreCutTime(playerStartTime);
991 starttime = m_Edl.RestoreCutTime((int64_t)m_PlayerOptions.starttime * 1000); // s to ms
993 CLog::Log(LOGDEBUG, "%s - Start position set to last stopped position: %d", __FUNCTION__, starttime);
995 else if(m_Edl.InCut(0, &cut)
996 && (cut.action == CEdl::CUT || cut.action == CEdl::COMM_BREAK))
999 CLog::Log(LOGDEBUG, "%s - Start position set to end of first cut or commercial break: %d", __FUNCTION__, starttime);
1000 if(cut.action == CEdl::COMM_BREAK)
1003 * Setup auto skip markers as if the commercial break had been skipped using standard
1006 m_EdlAutoSkipMarkers.commbreak_start = cut.start;
1007 m_EdlAutoSkipMarkers.commbreak_end = cut.end;
1008 m_EdlAutoSkipMarkers.seek_to_start = true;
1013 double startpts = DVD_NOPTS_VALUE;
1016 if (m_pDemuxer->SeekTime(starttime, false, &startpts))
1017 CLog::Log(LOGDEBUG, "%s - starting demuxer from: %d", __FUNCTION__, starttime);
1019 CLog::Log(LOGDEBUG, "%s - failed to start demuxing from: %d", __FUNCTION__, starttime);
1022 if(m_pSubtitleDemuxer)
1024 if(m_pSubtitleDemuxer->SeekTime(starttime, false, &startpts))
1025 CLog::Log(LOGDEBUG, "%s - starting subtitle demuxer from: %d", __FUNCTION__, starttime);
1027 CLog::Log(LOGDEBUG, "%s - failed to start subtitle demuxing from: %d", __FUNCTION__, starttime);
1031 // make sure all selected stream have data on startup
1032 if (CachePVRStream())
1033 SetCaching(CACHESTATE_PVR);
1035 // make sure application know our info
1036 UpdateApplication(0);
1039 if(m_PlayerOptions.identify == false)
1040 m_callback.OnPlayBackStarted();
1042 // we are done initializing now, set the readyevent
1045 if (!CachePVRStream())
1046 SetCaching(CACHESTATE_FLUSH);
1048 while (!m_bAbortRequest)
1050 // handle messages send to this thread, like seek or demuxer reset requests
1056 // should we open a new input stream?
1059 if (OpenInputStream() == false)
1061 m_bAbortRequest = true;
1066 // should we open a new demuxer?
1069 if (m_pInputStream->NextStream() == CDVDInputStream::NEXTSTREAM_NONE)
1072 if (m_pInputStream->IsEOF())
1075 if (OpenDemuxStream() == false)
1077 m_bAbortRequest = true;
1081 OpenDefaultStreams();
1083 // never allow first frames after open to be skipped
1084 if( m_dvdPlayerVideo.IsInited() )
1085 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
1087 if (CachePVRStream())
1088 SetCaching(CACHESTATE_PVR);
1090 UpdateApplication(0);
1094 // handle eventual seeks due to playspeed
1097 // update player state
1098 UpdatePlayState(200);
1100 // update application with our state
1101 UpdateApplication(1000);
1103 if (CheckDelayedChannelEntry())
1106 // if the queues are full, no need to read more
1107 if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0) ||
1108 (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
1114 // always yield to players if they have data levels > 50 percent
1115 if((m_dvdPlayerAudio.GetLevel() > 50 || m_CurrentAudio.id < 0)
1116 && (m_dvdPlayerVideo.GetLevel() > 50 || m_CurrentVideo.id < 0))
1119 DemuxPacket* pPacket = NULL;
1120 CDemuxStream *pStream = NULL;
1121 ReadPacket(pPacket, pStream);
1122 if (pPacket && !pStream)
1124 /* probably a empty packet, just free it and move on */
1125 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
1131 // when paused, demuxer could be be returning empty
1132 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
1135 // check for a still frame state
1136 if (CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
1138 // stills will be skipped
1139 if(m_dvd.state == DVDSTATE_STILL)
1141 if (m_dvd.iDVDStillTime > 0)
1143 if ((XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime) >= m_dvd.iDVDStillTime)
1145 m_dvd.iDVDStillTime = 0;
1146 m_dvd.iDVDStillStartTime = 0;
1147 m_dvd.state = DVDSTATE_NORMAL;
1148 pStream->SkipStill();
1155 // if there is another stream available, reopen demuxer
1156 CDVDInputStream::ENextStream next = m_pInputStream->NextStream();
1157 if(next == CDVDInputStream::NEXTSTREAM_OPEN)
1159 SAFE_DELETE(m_pDemuxer);
1160 m_CurrentAudio.stream = NULL;
1161 m_CurrentVideo.stream = NULL;
1162 m_CurrentSubtitle.stream = NULL;
1166 // input stream asked us to just retry
1167 if(next == CDVDInputStream::NEXTSTREAM_RETRY)
1173 // make sure we tell all players to finish it's data
1174 if(m_CurrentAudio.inited)
1175 m_dvdPlayerAudio.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
1176 if(m_CurrentVideo.inited)
1177 m_dvdPlayerVideo.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
1178 if(m_CurrentSubtitle.inited)
1179 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
1180 if(m_CurrentTeletext.inited)
1181 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
1182 m_CurrentAudio.inited = false;
1183 m_CurrentVideo.inited = false;
1184 m_CurrentSubtitle.inited = false;
1185 m_CurrentTeletext.inited = false;
1187 // if we are caching, start playing it again
1188 SetCaching(CACHESTATE_DONE);
1190 // while players are still playing, keep going to allow seekbacks
1191 if(m_dvdPlayerAudio.HasData()
1192 || m_dvdPlayerVideo.HasData())
1198 if (!m_pInputStream->IsEOF())
1199 CLog::Log(LOGINFO, "%s - eof reading from demuxer", __FUNCTION__);
1201 m_CurrentAudio.started = false;
1202 m_CurrentVideo.started = false;
1203 m_CurrentSubtitle.started = false;
1204 m_CurrentTeletext.started = false;
1209 // it's a valid data packet, reset error counter
1212 // check so that none of our streams has become invalid
1213 if (!IsValidStream(m_CurrentAudio) && m_dvdPlayerAudio.IsStalled()) CloseAudioStream(true);
1214 if (!IsValidStream(m_CurrentVideo) && m_dvdPlayerVideo.IsStalled()) CloseVideoStream(true);
1215 if (!IsValidStream(m_CurrentSubtitle) && m_dvdPlayerSubtitle.IsStalled()) CloseSubtitleStream(true);
1216 if (!IsValidStream(m_CurrentTeletext)) CloseTeletextStream(true);
1218 // see if we can find something better to play
1219 if (IsBetterStream(m_CurrentAudio, pStream)) OpenAudioStream (pStream->iId, pStream->source);
1220 if (IsBetterStream(m_CurrentVideo, pStream)) OpenVideoStream (pStream->iId, pStream->source);
1221 if (IsBetterStream(m_CurrentSubtitle, pStream)) OpenSubtitleStream(pStream->iId, pStream->source);
1222 if (IsBetterStream(m_CurrentTeletext, pStream)) OpenTeletextStream(pStream->iId, pStream->source);
1224 // process the packet
1225 ProcessPacket(pStream, pPacket);
1227 // check if in a cut or commercial break that should be automatically skipped
1228 CheckAutoSceneSkip();
1232 bool CDVDPlayer::CheckDelayedChannelEntry(void)
1234 bool bReturn(false);
1236 if (m_iChannelEntryTimeOut > 0 && XbmcThreads::SystemClockMillis() >= m_iChannelEntryTimeOut)
1238 CFileItem currentFile(g_application.CurrentFileItem());
1239 CPVRChannel *currentChannel = currentFile.GetPVRChannelInfoTag();
1240 SwitchChannel(*currentChannel);
1243 m_iChannelEntryTimeOut = 0;
1249 void CDVDPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
1251 /* process packet if it belongs to selected stream. for dvd's don't allow automatic opening of streams*/
1252 StreamLock lock(this);
1256 if (pPacket->iStreamId == m_CurrentAudio.id && pStream->source == m_CurrentAudio.source && pStream->type == STREAM_AUDIO)
1257 ProcessAudioData(pStream, pPacket);
1258 else if (pPacket->iStreamId == m_CurrentVideo.id && pStream->source == m_CurrentVideo.source && pStream->type == STREAM_VIDEO)
1259 ProcessVideoData(pStream, pPacket);
1260 else if (pPacket->iStreamId == m_CurrentSubtitle.id && pStream->source == m_CurrentSubtitle.source && pStream->type == STREAM_SUBTITLE)
1261 ProcessSubData(pStream, pPacket);
1262 else if (pPacket->iStreamId == m_CurrentTeletext.id && pStream->source == m_CurrentTeletext.source && pStream->type == STREAM_TELETEXT)
1263 ProcessTeletextData(pStream, pPacket);
1266 pStream->SetDiscard(AVDISCARD_ALL);
1267 CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
1272 CLog::Log(LOGERROR, "%s - Exception thrown when processing demux packet", __FUNCTION__);
1277 void CDVDPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
1279 if (m_CurrentAudio.stream != (void*)pStream
1280 || m_CurrentAudio.changes != pStream->changes)
1282 /* check so that dmuxer hints or extra data hasn't changed */
1283 /* if they have, reopen stream */
1285 if (m_CurrentAudio.hint != CDVDStreamInfo(*pStream, true))
1286 OpenAudioStream( pPacket->iStreamId, pStream->source );
1288 m_CurrentAudio.stream = (void*)pStream;
1291 // check if we are too slow and need to recache
1292 CheckStartCaching(m_CurrentAudio);
1294 CheckContinuity(m_CurrentAudio, pPacket);
1295 UpdateTimestamps(m_CurrentAudio, pPacket);
1298 if (CheckPlayerInit(m_CurrentAudio, DVDPLAYER_AUDIO))
1302 * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped.
1303 * If not inside a hard cut, but the demux point has reached an EDL mute section then trigger the
1304 * AUDIO_SILENCE state. The AUDIO_SILENCE state is reverted as soon as the demux point is outside
1305 * of any EDL section while EDL mute is still active.
1308 if (CheckSceneSkip(m_CurrentAudio))
1310 else if (m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) && cut.action == CEdl::MUTE // Inside EDL mute
1311 && !m_EdlAutoSkipMarkers.mute) // Mute not already triggered
1313 m_dvdPlayerAudio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, true));
1314 m_EdlAutoSkipMarkers.mute = true;
1316 else if (!m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) // Outside of any EDL
1317 && m_EdlAutoSkipMarkers.mute) // But the mute hasn't been removed yet
1319 m_dvdPlayerAudio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, false));
1320 m_EdlAutoSkipMarkers.mute = false;
1323 m_dvdPlayerAudio.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1326 void CDVDPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
1328 if (m_CurrentVideo.stream != (void*)pStream
1329 || m_CurrentVideo.changes != pStream->changes)
1331 /* check so that dmuxer hints or extra data hasn't changed */
1332 /* if they have reopen stream */
1334 if (m_CurrentVideo.hint != CDVDStreamInfo(*pStream, true))
1335 OpenVideoStream(pPacket->iStreamId, pStream->source);
1337 m_CurrentVideo.stream = (void*)pStream;
1340 // check if we are too slow and need to recache
1341 CheckStartCaching(m_CurrentVideo);
1343 if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
1345 CheckContinuity(m_CurrentVideo, pPacket);
1346 UpdateTimestamps(m_CurrentVideo, pPacket);
1350 if (CheckPlayerInit(m_CurrentVideo, DVDPLAYER_VIDEO))
1353 if (CheckSceneSkip(m_CurrentVideo))
1356 m_dvdPlayerVideo.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1359 void CDVDPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
1361 if (m_CurrentSubtitle.stream != (void*)pStream
1362 || m_CurrentSubtitle.changes != pStream->changes)
1364 /* check so that dmuxer hints or extra data hasn't changed */
1365 /* if they have reopen stream */
1367 if (m_CurrentSubtitle.hint != CDVDStreamInfo(*pStream, true))
1368 OpenSubtitleStream(pPacket->iStreamId, pStream->source);
1370 m_CurrentSubtitle.stream = (void*)pStream;
1373 UpdateTimestamps(m_CurrentSubtitle, pPacket);
1376 if (CheckPlayerInit(m_CurrentSubtitle, DVDPLAYER_SUBTITLE))
1379 if (CheckSceneSkip(m_CurrentSubtitle))
1382 m_dvdPlayerSubtitle.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1384 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1385 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
1388 void CDVDPlayer::ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket)
1390 if (m_CurrentTeletext.stream != (void*)pStream
1391 || m_CurrentTeletext.changes != pStream->changes)
1393 /* check so that dmuxer hints or extra data hasn't changed */
1394 /* if they have, reopen stream */
1395 if (m_CurrentTeletext.hint != CDVDStreamInfo(*pStream, true))
1396 OpenTeletextStream( pPacket->iStreamId, pStream->source );
1398 m_CurrentTeletext.stream = (void*)pStream;
1400 UpdateTimestamps(m_CurrentTeletext, pPacket);
1403 if (CheckPlayerInit(m_CurrentTeletext, DVDPLAYER_TELETEXT))
1406 if (CheckSceneSkip(m_CurrentTeletext))
1409 m_dvdPlayerTeletext.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1412 bool CDVDPlayer::GetCachingTimes(double& level, double& delay, double& offset)
1414 if(!m_pInputStream || !m_pDemuxer)
1417 XFILE::SCacheStatus status;
1418 if (!m_pInputStream->GetCacheStatus(&status))
1421 int64_t cached = status.forward;
1422 unsigned currate = status.currate;
1423 unsigned maxrate = status.maxrate;
1424 bool full = status.full;
1426 int64_t length = m_pInputStream->GetLength();
1427 int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR);
1429 if(cached < 0 || length <= 0 || remain < 0)
1432 double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
1433 double queued = 1000.0 * GetQueueTime() / play_sbp;
1437 offset = (double)(cached + queued) / length;
1442 double cache_sbp = 1.1 * (double)DVD_TIME_BASE / currate; /* underestimate by 10 % */
1443 double play_left = play_sbp * (remain + queued); /* time to play out all remaining bytes */
1444 double cache_left = cache_sbp * (remain - cached); /* time to cache the remaining bytes */
1445 double cache_need = std::max(0.0, remain - play_left / cache_sbp); /* bytes needed until play_left == cache_left */
1447 delay = cache_left - play_left;
1449 if (full && (currate < maxrate) )
1450 level = -1.0; /* buffer is full & our read rate is too low */
1452 level = (cached + queued) / (cache_need + queued);
1457 void CDVDPlayer::HandlePlaySpeed()
1459 ECacheState caching = m_caching;
1461 if(IsInMenu() && caching != CACHESTATE_DONE)
1462 caching = CACHESTATE_DONE;
1464 if(caching == CACHESTATE_FULL)
1466 double level, delay, offset;
1467 if(GetCachingTimes(level, delay, offset))
1471 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(21454), g_localizeStrings.Get(21455));
1472 caching = CACHESTATE_INIT;
1475 caching = CACHESTATE_INIT;
1479 if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0)
1480 || (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
1481 caching = CACHESTATE_INIT;
1485 if(caching == CACHESTATE_INIT)
1487 // if all enabled streams have been inited we are done
1488 if((m_CurrentVideo.id < 0 || m_CurrentVideo.started)
1489 && (m_CurrentAudio.id < 0 || m_CurrentAudio.started))
1490 caching = CACHESTATE_PLAY;
1492 // handle situation that we get no data on one stream
1493 if(m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0)
1495 if ((!m_dvdPlayerAudio.AcceptsData() && !m_CurrentVideo.started)
1496 || (!m_dvdPlayerVideo.AcceptsData() && !m_CurrentAudio.started))
1498 caching = CACHESTATE_DONE;
1503 if (caching == CACHESTATE_PVR)
1505 bool bGotAudio(m_pDemuxer->GetNrOfAudioStreams() > 0);
1506 bool bGotVideo(m_pDemuxer->GetNrOfVideoStreams() > 0);
1507 bool bAudioLevelOk(m_dvdPlayerAudio.GetLevel() > g_advancedSettings.m_iPVRMinAudioCacheLevel);
1508 bool bVideoLevelOk(m_dvdPlayerVideo.GetLevel() > g_advancedSettings.m_iPVRMinVideoCacheLevel);
1509 bool bAudioFull(!m_dvdPlayerAudio.AcceptsData());
1510 bool bVideoFull(!m_dvdPlayerVideo.AcceptsData());
1512 if (/* if all streams got at least g_advancedSettings.m_iPVRMinCacheLevel in their buffers, we're done */
1513 ((bGotVideo || bGotAudio) && (!bGotAudio || bAudioLevelOk) && (!bGotVideo || bVideoLevelOk)) ||
1514 /* or if one of the buffers is full */
1515 (bAudioFull || bVideoFull))
1517 CLog::Log(LOGDEBUG, "set caching from pvr to done. audio (%d) = %d. video (%d) = %d",
1518 bGotAudio, m_dvdPlayerAudio.GetLevel(),
1519 bGotVideo, m_dvdPlayerVideo.GetLevel());
1521 CFileItem currentItem(g_application.CurrentFileItem());
1522 if (currentItem.HasPVRChannelInfoTag())
1523 g_PVRManager.LoadCurrentChannelSettings();
1525 caching = CACHESTATE_DONE;
1529 /* ensure that automatically started players are stopped while caching */
1530 if (m_CurrentAudio.started)
1531 m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
1532 if (m_CurrentVideo.started)
1533 m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
1537 if(caching == CACHESTATE_PLAY)
1539 // if all enabled streams have started playing we are done
1540 if((m_CurrentVideo.id < 0 || !m_dvdPlayerVideo.IsStalled())
1541 && (m_CurrentAudio.id < 0 || !m_dvdPlayerAudio.IsStalled()))
1542 caching = CACHESTATE_DONE;
1545 if(m_caching != caching)
1546 SetCaching(caching);
1549 if(GetPlaySpeed() != DVD_PLAYSPEED_NORMAL && GetPlaySpeed() != DVD_PLAYSPEED_PAUSE)
1553 // this can't be done in menu
1554 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1557 else if (m_CurrentVideo.id >= 0
1558 && m_CurrentVideo.inited == true
1559 && m_SpeedState.lastpts != m_dvdPlayerVideo.GetCurrentPts()
1560 && m_SpeedState.lasttime != GetTime())
1562 m_SpeedState.lastpts = m_dvdPlayerVideo.GetCurrentPts();
1563 m_SpeedState.lasttime = GetTime();
1564 // check how much off clock video is when ff/rw:ing
1565 // a problem here is that seeking isn't very accurate
1566 // and since the clock will be resynced after seek
1567 // we might actually not really be playing at the wanted
1568 // speed. we'd need to have some way to not resync the clock
1569 // after a seek to remember timing. still need to handle
1570 // discontinuities somehow
1572 // when seeking, give the player a headstart to make sure
1573 // the time it takes to seek doesn't make a difference.
1575 error = m_clock.GetClock() - m_SpeedState.lastpts;
1576 error *= m_playSpeed / abs(m_playSpeed);
1578 if(error > DVD_MSEC_TO_TIME(1000))
1580 CLog::Log(LOGDEBUG, "CDVDPlayer::Process - Seeking to catch up");
1581 int64_t iTime = (int64_t)DVD_TIME_TO_MSEC(m_clock.GetClock() + m_State.time_offset + 500000.0 * m_playSpeed / DVD_PLAYSPEED_NORMAL);
1582 m_messenger.Put(new CDVDMsgPlayerSeek(iTime, (GetPlaySpeed() < 0), true, false, false, true));
1588 bool CDVDPlayer::CheckStartCaching(CCurrentStream& current)
1590 if(m_caching != CACHESTATE_DONE
1591 || m_playSpeed != DVD_PLAYSPEED_NORMAL)
1597 if((current.type == STREAM_AUDIO && m_dvdPlayerAudio.IsStalled())
1598 || (current.type == STREAM_VIDEO && m_dvdPlayerVideo.IsStalled()))
1600 if (CachePVRStream())
1602 if ((current.type == STREAM_AUDIO && current.started && m_dvdPlayerAudio.GetLevel() == 0) ||
1603 (current.type == STREAM_VIDEO && current.started && m_dvdPlayerVideo.GetLevel() == 0))
1605 CLog::Log(LOGDEBUG, "%s stream stalled. start buffering", current.type == STREAM_AUDIO ? "audio" : "video");
1606 SetCaching(CACHESTATE_PVR);
1611 // don't start caching if it's only a single stream that has run dry
1612 if(m_dvdPlayerAudio.GetLevel() > 50
1613 || m_dvdPlayerVideo.GetLevel() > 50)
1617 SetCaching(CACHESTATE_FULL);
1619 SetCaching(CACHESTATE_INIT);
1625 bool CDVDPlayer::CheckPlayerInit(CCurrentStream& current, unsigned int source)
1630 if(current.startpts != DVD_NOPTS_VALUE)
1632 if(current.dts == DVD_NOPTS_VALUE)
1634 CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
1638 if((current.startpts - current.dts) > DVD_SEC_TO_TIME(20))
1640 CLog::Log(LOGDEBUG, "%s - too far to decode before finishing seek", __FUNCTION__);
1641 if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
1642 m_CurrentAudio.startpts = current.dts;
1643 if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
1644 m_CurrentVideo.startpts = current.dts;
1645 if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
1646 m_CurrentSubtitle.startpts = current.dts;
1647 if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE)
1648 m_CurrentTeletext.startpts = current.dts;
1651 if(current.dts < current.startpts)
1653 CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
1658 //If this is the first packet after a discontinuity, send it as a resync
1659 if (current.dts != DVD_NOPTS_VALUE)
1661 current.inited = true;
1662 current.startpts = current.dts;
1664 bool setclock = false;
1665 if(m_playSpeed == DVD_PLAYSPEED_NORMAL)
1667 if( source == DVDPLAYER_AUDIO)
1668 setclock = !m_CurrentVideo.inited;
1669 else if(source == DVDPLAYER_VIDEO)
1670 setclock = !m_CurrentAudio.inited;
1674 if(source == DVDPLAYER_VIDEO)
1678 double starttime = current.startpts;
1679 if(m_CurrentAudio.inited
1680 && m_CurrentAudio.startpts != DVD_NOPTS_VALUE
1681 && m_CurrentAudio.startpts < starttime)
1682 starttime = m_CurrentAudio.startpts;
1683 if(m_CurrentVideo.inited
1684 && m_CurrentVideo.startpts != DVD_NOPTS_VALUE
1685 && m_CurrentVideo.startpts < starttime)
1686 starttime = m_CurrentVideo.startpts;
1688 starttime = current.startpts - starttime;
1689 if(starttime > 0 && setclock)
1691 if(starttime > DVD_SEC_TO_TIME(2))
1692 CLog::Log(LOGWARNING, "CDVDPlayer::CheckPlayerInit(%d) - Ignoring too large delay of %f", source, starttime);
1694 SendPlayerMessage(new CDVDMsgDouble(CDVDMsg::GENERAL_DELAY, starttime), source);
1697 SendPlayerMessage(new CDVDMsgGeneralResync(current.dts, setclock), source);
1702 void CDVDPlayer::UpdateCorrection(DemuxPacket* pkt, double correction)
1704 if(pkt->dts != DVD_NOPTS_VALUE) pkt->dts -= correction;
1705 if(pkt->pts != DVD_NOPTS_VALUE) pkt->pts -= correction;
1708 void CDVDPlayer::UpdateTimestamps(CCurrentStream& current, DemuxPacket* pPacket)
1710 double dts = current.dts;
1711 /* update stored values */
1712 if(pPacket->dts != DVD_NOPTS_VALUE)
1714 else if(pPacket->pts != DVD_NOPTS_VALUE)
1717 /* calculate some average duration */
1718 if(pPacket->duration != DVD_NOPTS_VALUE)
1719 current.dur = pPacket->duration;
1720 else if(dts != DVD_NOPTS_VALUE && current.dts != DVD_NOPTS_VALUE)
1721 current.dur = 0.1 * (current.dur * 9 + (dts - current.dts));
1725 /* send a playback state structure periodically */
1726 if(current.dts_state == DVD_NOPTS_VALUE
1727 || abs(current.dts - current.dts_state) > DVD_MSEC_TO_TIME(200))
1729 current.dts_state = current.dts;
1732 // make sure we send no outdated state to a/v players
1734 SendPlayerMessage(new CDVDMsgType<SPlayerState>(CDVDMsg::PLAYER_DISPLAYTIME, m_StateInput), current.player);
1738 CSingleLock lock(m_StateSection);
1739 m_State = m_StateInput;
1744 static void UpdateLimits(double& minimum, double& maximum, double dts)
1746 if(dts == DVD_NOPTS_VALUE)
1748 if(minimum == DVD_NOPTS_VALUE || minimum > dts) minimum = dts;
1749 if(maximum == DVD_NOPTS_VALUE || maximum < dts) maximum = dts;
1752 void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
1754 if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
1757 if( pPacket->dts == DVD_NOPTS_VALUE || current.dts == DVD_NOPTS_VALUE)
1760 double mindts = DVD_NOPTS_VALUE, maxdts = DVD_NOPTS_VALUE;
1761 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts);
1762 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts);
1763 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts_end());
1764 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts_end());
1766 /* if we don't have max and min, we can't do anything more */
1767 if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
1770 double correction = 0.0;
1771 if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000))
1773 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - resync forward :%d, prev:%f, curr:%f, diff:%f"
1774 , current.type, current.dts, pPacket->dts, pPacket->dts - maxdts);
1775 correction = pPacket->dts - maxdts;
1778 /* if it's large scale jump, correct for it */
1779 if(pPacket->dts + DVD_MSEC_TO_TIME(100) < current.dts_end())
1781 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - resync backward :%d, prev:%f, curr:%f, diff:%f"
1782 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1783 correction = pPacket->dts - current.dts_end();
1785 else if(pPacket->dts < current.dts)
1787 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - wrapback :%d, prev:%f, curr:%f, diff:%f"
1788 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1791 if(correction != 0.0)
1793 /* disable detection on next packet on other stream to avoid ping pong-ing */
1794 if(m_CurrentAudio.player != current.player) m_CurrentAudio.dts = DVD_NOPTS_VALUE;
1795 if(m_CurrentVideo.player != current.player) m_CurrentVideo.dts = DVD_NOPTS_VALUE;
1797 m_offset_pts += correction;
1798 UpdateCorrection(pPacket, correction);
1802 bool CDVDPlayer::CheckSceneSkip(CCurrentStream& current)
1807 if(current.dts == DVD_NOPTS_VALUE)
1810 if(current.inited == false)
1814 return m_Edl.InCut(DVD_TIME_TO_MSEC(current.dts + m_offset_pts), &cut) && cut.action == CEdl::CUT;
1817 void CDVDPlayer::CheckAutoSceneSkip()
1823 * Check that there is an audio and video stream.
1825 if(m_CurrentAudio.id < 0
1826 || m_CurrentVideo.id < 0)
1830 * If there is a startpts defined for either the audio or video stream then dvdplayer is still
1831 * still decoding frames to get to the previously requested seek point.
1833 if(m_CurrentAudio.inited == false
1834 || m_CurrentVideo.inited == false)
1837 if(m_CurrentAudio.dts == DVD_NOPTS_VALUE
1838 || m_CurrentVideo.dts == DVD_NOPTS_VALUE)
1841 const int64_t clock = DVD_TIME_TO_MSEC(min(m_CurrentAudio.dts, m_CurrentVideo.dts) + m_offset_pts);
1844 if(!m_Edl.InCut(clock, &cut))
1847 if(cut.action == CEdl::CUT
1848 && !(cut.end == m_EdlAutoSkipMarkers.cut || cut.start == m_EdlAutoSkipMarkers.cut)) // To prevent looping if same cut again
1850 CLog::Log(LOGDEBUG, "%s - Clock in EDL cut [%s - %s]: %s. Automatically skipping over.",
1851 __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(),
1852 CEdl::MillisecondsToTimeString(cut.end).c_str(), CEdl::MillisecondsToTimeString(clock).c_str());
1854 * Seeking either goes to the start or the end of the cut depending on the play direction.
1856 int64_t seek = GetPlaySpeed() >= 0 ? cut.end : cut.start;
1858 * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
1860 m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, true, false, true, false, true));
1862 * Seek doesn't always work reliably. Last physical seek time is recorded to prevent looping
1863 * if there was an error with seeking and it landed somewhere unexpected, perhaps back in the
1864 * cut. The cut automatic skip marker is reset every 500ms allowing another attempt at the seek.
1866 m_EdlAutoSkipMarkers.cut = GetPlaySpeed() >= 0 ? cut.end : cut.start;
1868 else if(cut.action == CEdl::COMM_BREAK
1869 && GetPlaySpeed() >= 0
1870 && cut.start > m_EdlAutoSkipMarkers.commbreak_end)
1872 CLog::Log(LOGDEBUG, "%s - Clock in commercial break [%s - %s]: %s. Automatically skipping to end of commercial break (only done once per break)",
1873 __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(), CEdl::MillisecondsToTimeString(cut.end).c_str(),
1874 CEdl::MillisecondsToTimeString(clock).c_str());
1876 * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
1878 m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false, true));
1880 * Each commercial break is only skipped once so poorly detected commercial breaks can be
1881 * manually re-entered. Start and end are recorded to prevent looping and to allow seeking back
1882 * to the start of the commercial break if incorrectly flagged.
1884 m_EdlAutoSkipMarkers.commbreak_start = cut.start;
1885 m_EdlAutoSkipMarkers.commbreak_end = cut.end;
1886 m_EdlAutoSkipMarkers.seek_to_start = true; // Allow backwards Seek() to go directly to the start
1891 void CDVDPlayer::SynchronizeDemuxer(unsigned int timeout)
1893 if(IsCurrentThread())
1895 if(!m_messenger.IsInited())
1898 CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, 0);
1899 m_messenger.Put(message->Acquire());
1900 message->Wait(&m_bStop, 0);
1904 void CDVDPlayer::SynchronizePlayers(unsigned int sources)
1906 /* we need a big timeout as audio queue is about 8seconds for 2ch ac3 */
1907 const int timeout = 10*1000; // in milliseconds
1909 CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, sources);
1910 if (m_CurrentAudio.id >= 0)
1911 m_dvdPlayerAudio.SendMessage(message->Acquire());
1913 if (m_CurrentVideo.id >= 0)
1914 m_dvdPlayerVideo.SendMessage(message->Acquire());
1915 /* TODO - we have to rewrite the sync class, to not require
1916 all other players waiting for subtitle, should only
1918 if (m_CurrentSubtitle.id >= 0)
1919 m_dvdPlayerSubtitle.SendMessage(message->Acquire());
1924 void CDVDPlayer::SendPlayerMessage(CDVDMsg* pMsg, unsigned int target)
1926 if(target == DVDPLAYER_AUDIO)
1927 m_dvdPlayerAudio.SendMessage(pMsg);
1928 if(target == DVDPLAYER_VIDEO)
1929 m_dvdPlayerVideo.SendMessage(pMsg);
1930 if(target == DVDPLAYER_SUBTITLE)
1931 m_dvdPlayerSubtitle.SendMessage(pMsg);
1932 if(target == DVDPLAYER_TELETEXT)
1933 m_dvdPlayerTeletext.SendMessage(pMsg);
1936 void CDVDPlayer::OnExit()
1938 g_dvdPerformanceCounter.DisableMainPerformance();
1942 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit()");
1944 // set event to inform openfile something went wrong in case openfile is still waiting for this event
1945 SetCaching(CACHESTATE_DONE);
1947 // close each stream
1948 if (!m_bAbortRequest) CLog::Log(LOGNOTICE, "DVDPlayer: eof, waiting for queues to empty");
1949 if (m_CurrentAudio.id >= 0)
1951 CLog::Log(LOGNOTICE, "DVDPlayer: closing audio stream");
1952 CloseAudioStream(!m_bAbortRequest);
1954 if (m_CurrentVideo.id >= 0)
1956 CLog::Log(LOGNOTICE, "DVDPlayer: closing video stream");
1957 CloseVideoStream(!m_bAbortRequest);
1959 if (m_CurrentSubtitle.id >= 0)
1961 CLog::Log(LOGNOTICE, "DVDPlayer: closing subtitle stream");
1962 CloseSubtitleStream(!m_bAbortRequest);
1964 if (m_CurrentTeletext.id >= 0)
1966 CLog::Log(LOGNOTICE, "DVDPlayer: closing teletext stream");
1967 CloseTeletextStream(!m_bAbortRequest);
1969 // destroy the demuxer
1972 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting demuxer");
1977 if (m_pSubtitleDemuxer)
1979 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting subtitle demuxer");
1980 delete m_pSubtitleDemuxer;
1982 m_pSubtitleDemuxer = NULL;
1984 // destroy the inputstream
1987 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting input stream");
1988 delete m_pInputStream;
1990 m_pInputStream = NULL;
1992 // clean up all selection streams
1993 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
2000 CLog::Log(LOGERROR, "%s - Exception thrown when trying to close down player, memory leak will follow", __FUNCTION__);
2001 m_pInputStream = NULL;
2006 // if we didn't stop playing, advance to the next item in xbmc's playlist
2007 if(m_PlayerOptions.identify == false)
2009 if (m_bAbortRequest)
2010 m_callback.OnPlayBackStopped();
2012 m_callback.OnPlayBackEnded();
2015 // set event to inform openfile something went wrong in case openfile is still waiting for this event
2019 void CDVDPlayer::HandleMessages()
2022 StreamLock lock(this);
2024 while (m_messenger.Get(&pMsg, 0) == MSGQ_OK)
2029 if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
2030 && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2032 CDVDMsgPlayerSeek &msg(*((CDVDMsgPlayerSeek*)pMsg));
2034 if (!m_State.canseek)
2040 if(!msg.GetTrickPlay())
2042 g_infoManager.SetDisplayAfterSeek(100000);
2044 SetCaching(CACHESTATE_FLUSH);
2047 double start = DVD_NOPTS_VALUE;
2049 int time = msg.GetRestore() ? (int)m_Edl.RestoreCutTime(msg.GetTime()) : msg.GetTime();
2051 // if input streams doesn't support seektime we must convert back to clock
2052 if(dynamic_cast<CDVDInputStream::ISeekTime*>(m_pInputStream) == NULL)
2053 time -= DVD_TIME_TO_MSEC(m_State.time_offset - m_offset_pts);
2055 CLog::Log(LOGDEBUG, "demuxer seek to: %d", time);
2056 if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start))
2058 CLog::Log(LOGDEBUG, "demuxer seek to: %d, success", time);
2059 if(m_pSubtitleDemuxer)
2061 if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward()))
2062 CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: %d, success", time);
2064 // dts after successful seek
2065 if (m_StateInput.time_src == ETIMESOURCE_CLOCK && start == DVD_NOPTS_VALUE)
2066 m_StateInput.dts = DVD_MSEC_TO_TIME(time);
2068 m_StateInput.dts = start;
2070 FlushBuffers(!msg.GetFlush(), start, msg.GetAccurate());
2073 CLog::Log(LOGWARNING, "error while seeking");
2075 // set flag to indicate we have finished a seeking request
2076 if(!msg.GetTrickPlay())
2077 g_infoManager.SetDisplayAfterSeek();
2079 // dvd's will issue a HOP_CHANNEL that we need to skip
2080 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2081 m_dvd.state = DVDSTATE_SEEK;
2083 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
2084 && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2086 g_infoManager.SetDisplayAfterSeek(100000);
2087 SetCaching(CACHESTATE_FLUSH);
2089 CDVDMsgPlayerSeekChapter &msg(*((CDVDMsgPlayerSeekChapter*)pMsg));
2090 double start = DVD_NOPTS_VALUE;
2092 // This should always be the case.
2093 if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
2095 FlushBuffers(false, start, true);
2096 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2099 g_infoManager.SetDisplayAfterSeek();
2101 else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
2103 m_CurrentAudio.stream = NULL;
2104 m_CurrentVideo.stream = NULL;
2105 m_CurrentSubtitle.stream = NULL;
2107 // we need to reset the demuxer, probably because the streams have changed
2109 m_pDemuxer->Reset();
2110 if(m_pSubtitleDemuxer)
2111 m_pSubtitleDemuxer->Reset();
2113 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
2115 CDVDMsgPlayerSetAudioStream* pMsg2 = (CDVDMsgPlayerSetAudioStream*)pMsg;
2117 SelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
2118 if(st.source != STREAM_SOURCE_NONE)
2120 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2122 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2123 if(pStream->SetActiveAudioStream(st.id))
2125 m_dvd.iSelectedAudioStream = -1;
2126 CloseAudioStream(false);
2127 m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true, true, true));
2132 CloseAudioStream(false);
2133 OpenAudioStream(st.id, st.source);
2134 m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true, true, true));
2138 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
2140 CDVDMsgPlayerSetSubtitleStream* pMsg2 = (CDVDMsgPlayerSetSubtitleStream*)pMsg;
2142 SelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
2143 if(st.source != STREAM_SOURCE_NONE)
2145 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2147 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2148 if(pStream->SetActiveSubtitleStream(st.id))
2150 m_dvd.iSelectedSPUStream = -1;
2151 CloseSubtitleStream(false);
2156 CloseSubtitleStream(false);
2157 OpenSubtitleStream(st.id, st.source);
2161 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
2163 CDVDMsgBool* pValue = (CDVDMsgBool*)pMsg;
2165 m_dvdPlayerVideo.EnableSubtitle(pValue->m_value);
2167 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2168 static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->EnableSubtitleStream(pValue->m_value);
2170 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
2172 g_infoManager.SetDisplayAfterSeek(100000);
2173 SetCaching(CACHESTATE_FLUSH);
2175 CDVDMsgPlayerSetState* pMsgPlayerSetState = (CDVDMsgPlayerSetState*)pMsg;
2177 if (CDVDInputStream::IMenus* ptr = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
2179 if(ptr->SetState(pMsgPlayerSetState->GetState()))
2181 m_dvd.state = DVDSTATE_NORMAL;
2182 m_dvd.iDVDStillStartTime = 0;
2183 m_dvd.iDVDStillTime = 0;
2187 g_infoManager.SetDisplayAfterSeek();
2189 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD))
2191 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2193 input->Record(*(CDVDMsgBool*)pMsg);
2195 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
2197 FlushBuffers(false);
2199 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
2201 int speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
2203 // correct our current clock, as it would start going wrong otherwise
2204 if(m_State.timestamp > 0)
2207 offset = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2208 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2209 if(offset > 1000) offset = 1000;
2210 if(offset < -1000) offset = -1000;
2211 m_State.time += DVD_TIME_TO_MSEC(offset);
2212 m_State.timestamp = CDVDClock::GetAbsoluteClock();
2215 if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed)
2216 m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL);
2218 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && speed != m_playSpeed)
2220 CDVDInputStreamPVRManager* pvrinputstream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream);
2221 pvrinputstream->Pause( speed == 0 );
2224 // if playspeed is different then DVD_PLAYSPEED_NORMAL or DVD_PLAYSPEED_PAUSE
2225 // audioplayer, stops outputing audio to audiorendere, but still tries to
2226 // sleep an correct amount for each packet
2227 // videoplayer just plays faster after the clock speed has been increased
2229 // 2. skip frames and adjust their pts or the clock
2230 m_playSpeed = speed;
2231 m_caching = CACHESTATE_DONE;
2232 m_clock.SetSpeed(speed);
2233 m_dvdPlayerAudio.SetSpeed(speed);
2234 m_dvdPlayerVideo.SetSpeed(speed);
2236 // TODO - we really shouldn't pause demuxer
2237 // until our buffers are somewhat filled
2239 m_pDemuxer->SetSpeed(speed);
2241 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) == 0)
2243 FlushBuffers(false);
2244 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2245 if(input && input->SelectChannelByNumber(static_cast<CDVDMsgInt*>(pMsg)->m_value))
2247 SAFE_DELETE(m_pDemuxer);
2250 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2251 CApplicationMessenger::Get().MediaStop(false);
2254 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0)
2256 FlushBuffers(false);
2257 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2258 if(input && input->SelectChannel(static_cast<CDVDMsgType <CPVRChannel> *>(pMsg)->m_value))
2260 SAFE_DELETE(m_pDemuxer);
2263 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2264 CApplicationMessenger::Get().MediaStop(false);
2267 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV))
2269 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2272 bool bSwitchSuccessful(false);
2273 bool bShowPreview(CSettings::Get().GetInt("pvrplayback.channelentrytimeout") > 0);
2277 g_infoManager.SetDisplayAfterSeek(100000);
2278 FlushBuffers(false);
2281 if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
2282 bSwitchSuccessful = input->NextChannel(bShowPreview);
2284 bSwitchSuccessful = input->PrevChannel(bShowPreview);
2286 if(bSwitchSuccessful)
2290 UpdateApplication(0);
2291 m_iChannelEntryTimeOut = XbmcThreads::SystemClockMillis() + CSettings::Get().GetInt("pvrplayback.channelentrytimeout");
2295 m_iChannelEntryTimeOut = 0;
2296 SAFE_DELETE(m_pDemuxer);
2298 g_infoManager.SetDisplayAfterSeek();
2303 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2304 CApplicationMessenger::Get().MediaStop(false);
2308 else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
2309 OnAction(((CDVDMsgType<CAction>*)pMsg)->m_value);
2310 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
2312 int player = ((CDVDMsgInt*)pMsg)->m_value;
2313 if(player == DVDPLAYER_AUDIO)
2314 m_CurrentAudio.started = true;
2315 if(player == DVDPLAYER_VIDEO)
2316 m_CurrentVideo.started = true;
2317 CLog::Log(LOGDEBUG, "CDVDPlayer::HandleMessages - player started %d", player);
2319 else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME))
2321 CDVDPlayer::SPlayerState& state = ((CDVDMsgType<CDVDPlayer::SPlayerState>*)pMsg)->m_value;
2323 CSingleLock lock(m_StateSection);
2324 /* prioritize data from video player, but only accept data *
2325 * after it has been started to avoid race conditions after seeks */
2326 if(m_CurrentVideo.started)
2328 if(state.player == DVDPLAYER_VIDEO)
2331 else if(m_CurrentAudio.started)
2333 if(state.player == DVDPLAYER_AUDIO)
2340 CLog::Log(LOGERROR, "%s - Exception thrown when handling message", __FUNCTION__);
2348 void CDVDPlayer::SetCaching(ECacheState state)
2350 if(state == CACHESTATE_FLUSH)
2352 double level, delay, offset;
2353 if(GetCachingTimes(level, delay, offset))
2354 state = CACHESTATE_FULL;
2356 state = CACHESTATE_INIT;
2359 if(m_caching == state)
2362 CLog::Log(LOGDEBUG, "CDVDPlayer::SetCaching - caching state %d", state);
2363 if(state == CACHESTATE_FULL
2364 || state == CACHESTATE_INIT
2365 || state == CACHESTATE_PVR)
2367 m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
2368 m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
2369 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2370 m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
2371 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2373 if (state == CACHESTATE_PVR)
2374 m_pInputStream->ResetScanTimeout((unsigned int) CSettings::Get().GetInt("pvrplayback.scantime") * 1000);
2377 if(state == CACHESTATE_PLAY
2378 ||(state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY))
2380 m_clock.SetSpeed(m_playSpeed);
2381 m_dvdPlayerAudio.SetSpeed(m_playSpeed);
2382 m_dvdPlayerVideo.SetSpeed(m_playSpeed);
2383 m_pInputStream->ResetScanTimeout(0);
2388 void CDVDPlayer::SetPlaySpeed(int speed)
2390 m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed));
2391 m_dvdPlayerAudio.SetSpeed(speed);
2392 m_dvdPlayerVideo.SetSpeed(speed);
2393 SynchronizeDemuxer(100);
2396 bool CDVDPlayer::CanPause()
2398 CSingleLock lock(m_StateSection);
2399 return m_State.canpause;
2402 void CDVDPlayer::Pause()
2404 CSingleLock lock(m_StateSection);
2405 if (!m_State.canpause)
2409 if(m_playSpeed != DVD_PLAYSPEED_PAUSE && (m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR))
2411 SetCaching(CACHESTATE_DONE);
2415 // return to normal speed if it was paused before, pause otherwise
2416 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
2418 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2419 m_callback.OnPlayBackResumed();
2423 SetPlaySpeed(DVD_PLAYSPEED_PAUSE);
2424 m_callback.OnPlayBackPaused();
2428 bool CDVDPlayer::IsPaused() const
2430 return m_playSpeed == DVD_PLAYSPEED_PAUSE || m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR;
2433 bool CDVDPlayer::HasVideo() const
2438 bool CDVDPlayer::HasAudio() const
2443 bool CDVDPlayer::IsPassthrough() const
2445 return m_dvdPlayerAudio.IsPassthrough();
2448 bool CDVDPlayer::CanSeek()
2450 CSingleLock lock(m_StateSection);
2451 return m_State.canseek;
2454 void CDVDPlayer::Seek(bool bPlus, bool bLargeStep)
2457 // sadly this doesn't work for now, audio player must
2458 // drop packets at the same rate as we play frames
2459 if( m_playSpeed == DVD_PLAYSPEED_PAUSE && bPlus && !bLargeStep)
2461 m_dvdPlayerVideo.StepFrame();
2465 if (!m_State.canseek)
2468 if(((bPlus && GetChapter() < GetChapterCount())
2469 || (!bPlus && GetChapter() > 1)) && bLargeStep)
2472 SeekChapter(GetChapter() + 1);
2474 SeekChapter(GetChapter() - 1);
2479 if (g_advancedSettings.m_videoUseTimeSeeking && GetTotalTime() > 2000*g_advancedSettings.m_videoTimeSeekForwardBig)
2482 seek = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig;
2484 seek = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward;
2492 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig;
2494 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward;
2495 seek = (int64_t)(GetTotalTimeInMsec()*(GetPercentage()+percent)/100);
2498 bool restore = true;
2502 * Alter the standard seek position based on whether any commercial breaks have been
2503 * automatically skipped.
2505 const int clock = DVD_TIME_TO_MSEC(m_clock.GetClock());
2507 * If a large backwards seek occurs within 10 seconds of the end of the last automated
2508 * commercial skip, then seek back to the start of the commercial break under the assumption
2509 * it was flagged incorrectly. 10 seconds grace period is allowed in case the watcher has to
2510 * fumble around finding the remote. Only happens once per commercial break.
2512 * Small skip does not trigger this in case the start of the commercial break was in fact fine
2513 * but it skipped too far into the program. In that case small skip backwards behaves as normal.
2515 if (!bPlus && bLargeStep
2516 && m_EdlAutoSkipMarkers.seek_to_start
2517 && clock >= m_EdlAutoSkipMarkers.commbreak_end
2518 && clock <= m_EdlAutoSkipMarkers.commbreak_end + 10*1000) // Only if within 10 seconds of the end (in msec)
2520 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).",
2521 __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
2522 CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
2523 seek = m_EdlAutoSkipMarkers.commbreak_start;
2525 m_EdlAutoSkipMarkers.seek_to_start = false; // So this will only happen within the 10 second grace period once.
2528 * If big skip forward within the last "reverted" commercial break, seek to the end of the
2529 * commercial break under the assumption that the break was incorrectly flagged and playback has
2530 * now reached the actual start of the commercial break. Assume that the end is flagged more
2531 * correctly than the landing point for a standard big skip (ends seem to be flagged more
2532 * accurately than the start).
2534 else if (bPlus && bLargeStep
2535 && clock >= m_EdlAutoSkipMarkers.commbreak_start
2536 && clock <= m_EdlAutoSkipMarkers.commbreak_end)
2538 CLog::Log(LOGDEBUG, "%s - Seeking to end of previously skipped commercial break [%s - %s] as big forwards skip activated within the break.",
2539 __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
2540 CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
2541 seek = m_EdlAutoSkipMarkers.commbreak_end;
2546 int64_t time = GetTime();
2547 if(g_application.CurrentFileItem().IsStack()
2548 && (seek > GetTotalTimeInMsec() || seek < 0))
2550 g_application.SeekTime((seek - time) * 0.001 + g_application.GetTime());
2551 // warning, don't access any dvdplayer variables here as
2552 // the dvdplayer object may have been destroyed
2556 m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, !bPlus, true, false, restore));
2557 SynchronizeDemuxer(100);
2558 if (seek < 0) seek = 0;
2559 m_callback.OnPlayBackSeek((int)seek, (int)(seek - time));
2562 bool CDVDPlayer::SeekScene(bool bPlus)
2564 if (!m_Edl.HasSceneMarker())
2568 * There is a 5 second grace period applied when seeking for scenes backwards. If there is no
2569 * grace period applied it is impossible to go backwards past a scene marker.
2571 int64_t clock = GetTime();
2572 if (!bPlus && clock > 5 * 1000) // 5 seconds
2575 int64_t iScenemarker;
2576 if (m_Edl.GetNextSceneMarker(bPlus, clock, &iScenemarker))
2579 * Seeking is flushed and inaccurate, just like Seek()
2581 m_messenger.Put(new CDVDMsgPlayerSeek((int)iScenemarker, !bPlus, true, false, false));
2582 SynchronizeDemuxer(100);
2588 void CDVDPlayer::GetAudioInfo(CStdString& strAudioInfo)
2590 { CSingleLock lock(m_StateSection);
2591 strAudioInfo.Format("D(%s)", m_StateInput.demux_audio.c_str());
2593 strAudioInfo.AppendFormat("\nP(%s)", m_dvdPlayerAudio.GetPlayerInfo().c_str());
2596 void CDVDPlayer::GetVideoInfo(CStdString& strVideoInfo)
2598 { CSingleLock lock(m_StateSection);
2599 strVideoInfo.Format("D(%s)", m_StateInput.demux_video.c_str());
2601 strVideoInfo.AppendFormat("\nP(%s)", m_dvdPlayerVideo.GetPlayerInfo().c_str());
2604 void CDVDPlayer::GetGeneralInfo(CStdString& strGeneralInfo)
2608 double dDelay = m_dvdPlayerVideo.GetDelay() / DVD_TIME_BASE - g_renderManager.GetDisplayLatency();
2610 double apts = m_dvdPlayerAudio.GetCurrentPts();
2611 double vpts = m_dvdPlayerVideo.GetCurrentPts();
2614 if( apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE )
2615 dDiff = (apts - vpts) / DVD_TIME_BASE;
2618 strEDL.AppendFormat(", edl:%s", m_Edl.GetInfo().c_str());
2621 CSingleLock lock(m_StateSection);
2622 if(m_StateInput.cache_bytes >= 0)
2624 strBuf.AppendFormat(" cache:%s %2.0f%%"
2625 , StringUtils::SizeToString(m_State.cache_bytes).c_str()
2626 , m_State.cache_level * 100);
2627 if(m_playSpeed == 0 || m_caching == CACHESTATE_FULL)
2628 strBuf.AppendFormat(" %d sec", DVD_TIME_TO_SEC(m_State.cache_delay));
2631 strGeneralInfo.Format("C( ad:% 6.3f, a/v:% 6.3f%s, dcpu:%2i%% acpu:%2i%% vcpu:%2i%%%s )"
2635 , (int)(CThread::GetRelativeUsage()*100)
2636 , (int)(m_dvdPlayerAudio.GetRelativeUsage()*100)
2637 , (int)(m_dvdPlayerVideo.GetRelativeUsage()*100)
2643 void CDVDPlayer::SeekPercentage(float iPercent)
2645 int64_t iTotalTime = GetTotalTimeInMsec();
2650 SeekTime((int64_t)(iTotalTime * iPercent / 100));
2653 float CDVDPlayer::GetPercentage()
2655 int64_t iTotalTime = GetTotalTimeInMsec();
2660 return GetTime() * 100 / (float)iTotalTime;
2663 float CDVDPlayer::GetCachePercentage()
2665 CSingleLock lock(m_StateSection);
2666 return m_StateInput.cache_offset * 100; // NOTE: Percentage returned is relative
2669 void CDVDPlayer::SetAVDelay(float fValue)
2671 m_dvdPlayerVideo.SetDelay( (fValue * DVD_TIME_BASE) ) ;
2674 float CDVDPlayer::GetAVDelay()
2676 return m_dvdPlayerVideo.GetDelay() / (float)DVD_TIME_BASE;
2679 void CDVDPlayer::SetSubTitleDelay(float fValue)
2681 m_dvdPlayerVideo.SetSubtitleDelay(-fValue * DVD_TIME_BASE);
2684 float CDVDPlayer::GetSubTitleDelay()
2686 return -m_dvdPlayerVideo.GetSubtitleDelay() / DVD_TIME_BASE;
2689 // priority: 1: libdvdnav, 2: external subtitles, 3: muxed subtitles
2690 int CDVDPlayer::GetSubtitleCount()
2692 StreamLock lock(this);
2693 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
2694 return m_SelectionStreams.Count(STREAM_SUBTITLE);
2697 int CDVDPlayer::GetSubtitle()
2699 return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, *this);
2702 void CDVDPlayer::GetSubtitleStreamInfo(int index, SPlayerSubtitleStreamInfo &info)
2704 if (index < 0 || index > (int) GetSubtitleCount() - 1)
2707 SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
2708 if(s.name.length() > 0)
2711 if(s.type == STREAM_NONE)
2712 info.name += "(Invalid)";
2714 info.language = s.language;
2717 void CDVDPlayer::SetSubtitle(int iStream)
2719 m_messenger.Put(new CDVDMsgPlayerSetSubtitleStream(iStream));
2722 bool CDVDPlayer::GetSubtitleVisible()
2724 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2726 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2727 if(pStream->IsInMenu())
2728 return CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn;
2730 return pStream->IsSubtitleStreamEnabled();
2733 return m_dvdPlayerVideo.IsSubtitleEnabled();
2736 void CDVDPlayer::SetSubtitleVisible(bool bVisible)
2738 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn = bVisible;
2739 m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
2742 int CDVDPlayer::GetAudioStreamCount()
2744 StreamLock lock(this);
2745 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
2746 return m_SelectionStreams.Count(STREAM_AUDIO);
2749 int CDVDPlayer::GetAudioStream()
2751 return m_SelectionStreams.IndexOf(STREAM_AUDIO, *this);
2754 void CDVDPlayer::SetAudioStream(int iStream)
2756 m_messenger.Put(new CDVDMsgPlayerSetAudioStream(iStream));
2757 SynchronizeDemuxer(100);
2760 TextCacheStruct_t* CDVDPlayer::GetTeletextCache()
2762 if (m_CurrentTeletext.id < 0)
2765 return m_dvdPlayerTeletext.GetTeletextCache();
2768 void CDVDPlayer::LoadPage(int p, int sp, unsigned char* buffer)
2770 if (m_CurrentTeletext.id < 0)
2773 return m_dvdPlayerTeletext.LoadPage(p, sp, buffer);
2776 void CDVDPlayer::SeekTime(int64_t iTime)
2778 int seekOffset = (int)(iTime - GetTime());
2779 m_messenger.Put(new CDVDMsgPlayerSeek((int)iTime, true, true, true));
2780 SynchronizeDemuxer(100);
2781 m_callback.OnPlayBackSeek((int)iTime, seekOffset);
2784 // return the time in milliseconds
2785 int64_t CDVDPlayer::GetTime()
2787 CSingleLock lock(m_StateSection);
2789 const double limit = DVD_MSEC_TO_TIME(200);
2790 if(m_State.timestamp > 0)
2792 offset = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2793 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2794 if(offset > limit) offset = limit;
2795 if(offset < -limit) offset = -limit;
2797 return llrint(m_State.time + DVD_TIME_TO_MSEC(offset));
2800 // return length in msec
2801 int64_t CDVDPlayer::GetTotalTimeInMsec()
2803 CSingleLock lock(m_StateSection);
2804 return llrint(m_State.time_total);
2807 // return length in seconds.. this should be changed to return in milleseconds throughout xbmc
2808 int64_t CDVDPlayer::GetTotalTime()
2810 return GetTotalTimeInMsec();
2813 void CDVDPlayer::ToFFRW(int iSpeed)
2815 // can't rewind in menu as seeking isn't possible
2817 if (iSpeed < 0 && IsInMenu()) return;
2818 SetPlaySpeed(iSpeed * DVD_PLAYSPEED_NORMAL);
2821 bool CDVDPlayer::OpenAudioStream(int iStream, int source, bool reset)
2823 CLog::Log(LOGNOTICE, "Opening audio stream: %i source: %i", iStream, source);
2828 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2829 if (!pStream || pStream->disabled)
2832 if( m_CurrentAudio.id < 0 && m_CurrentVideo.id >= 0 )
2834 // up until now we wheren't playing audio, but we did play video
2835 // this will change what is used to sync the dvdclock.
2836 // since the new audio data doesn't have to have any relation
2837 // to the current video data in the packet que, we have to
2838 // wait for it to empty
2840 // this happens if a new cell has audio data, but previous didn't
2841 // and both have video data
2843 SynchronizePlayers(SYNCSOURCE_AUDIO);
2846 CDVDStreamInfo hint(*pStream, true);
2848 if(m_CurrentAudio.id < 0
2849 || m_CurrentAudio.hint != hint)
2851 if (!m_dvdPlayerAudio.OpenStream( hint ))
2853 /* mark stream as disabled, to disallaw further attempts*/
2854 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2855 pStream->disabled = true;
2856 pStream->SetDiscard(AVDISCARD_ALL);
2861 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2863 /* store information about stream */
2864 m_CurrentAudio.id = iStream;
2865 m_CurrentAudio.source = source;
2866 m_CurrentAudio.hint = hint;
2867 m_CurrentAudio.stream = (void*)pStream;
2868 m_CurrentAudio.started = false;
2871 /* we are potentially going to be waiting on this */
2872 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2874 /* audio normally won't consume full cpu, so let it have prio */
2875 m_dvdPlayerAudio.SetPriority(GetPriority()+1);
2880 bool CDVDPlayer::OpenVideoStream(int iStream, int source, bool reset)
2882 CLog::Log(LOGNOTICE, "Opening video stream: %i source: %i", iStream, source);
2887 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2888 if(!pStream || pStream->disabled)
2890 pStream->SetDiscard(AVDISCARD_NONE);
2892 CDVDStreamInfo hint(*pStream, true);
2894 if( m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) )
2896 /* set aspect ratio as requested by navigator for dvd's */
2897 float aspect = static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->GetVideoAspectRatio();
2900 hint.aspect = aspect;
2901 hint.forced_aspect = true;
2903 hint.software = true;
2906 boost::shared_ptr<CPVRClient> client;
2907 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) &&
2908 pStream->type == STREAM_VIDEO &&
2909 g_PVRClients->GetPlayingClient(client) && client->HandlesDemuxing())
2911 // set the fps in hints
2912 const CDemuxStreamVideo *stream = static_cast<const CDemuxStreamVideo*>(pStream);
2913 hint.fpsrate = stream->iFpsRate;
2914 hint.fpsscale = stream->iFpsScale;
2917 CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
2918 if(pMenus && pMenus->IsInMenu())
2921 if(m_CurrentVideo.id < 0
2922 || m_CurrentVideo.hint != hint)
2924 if (!m_dvdPlayerVideo.OpenStream(hint))
2926 /* mark stream as disabled, to disallaw further attempts */
2927 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2928 pStream->disabled = true;
2929 pStream->SetDiscard(AVDISCARD_ALL);
2934 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2936 /* store information about stream */
2937 m_CurrentVideo.id = iStream;
2938 m_CurrentVideo.source = source;
2939 m_CurrentVideo.hint = hint;
2940 m_CurrentVideo.stream = (void*)pStream;
2941 m_CurrentVideo.started = false;
2944 /* we are potentially going to be waiting on this */
2945 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2947 #if defined(TARGET_DARWIN)
2948 // Apple thread scheduler works a little different than Linux. It
2949 // will favor OS GUI side and can cause DVDPlayerVideo to miss frame
2950 // updates when the OS gets busy. Apple's recomended method is to
2951 // elevate time critical threads to SCHED_RR and OSX does this for
2952 // the CoreAudio audio device handler thread. We do the same for
2953 // the DVDPlayerVideo thread so it can run to sleep without getting
2954 // swapped out by a busy OS.
2955 m_dvdPlayerVideo.SetPriority(GetSchedRRPriority());
2957 /* use same priority for video thread as demuxing thread, as */
2958 /* otherwise demuxer will starve if video consumes the full cpu */
2959 m_dvdPlayerVideo.SetPriority(GetPriority());
2965 bool CDVDPlayer::OpenSubtitleStream(int iStream, int source)
2967 CLog::Log(LOGNOTICE, "Opening Subtitle stream: %i source: %i", iStream, source);
2969 CDemuxStream* pStream = NULL;
2970 std::string filename;
2971 CDVDStreamInfo hint;
2973 if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
2975 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2978 SelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
2980 if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename)
2982 CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str());
2983 auto_ptr<CDVDDemuxVobsub> demux(new CDVDDemuxVobsub());
2984 if(!demux->Open(st.filename, st.filename2))
2986 m_pSubtitleDemuxer = demux.release();
2989 pStream = m_pSubtitleDemuxer->GetStream(iStream);
2990 if(!pStream || pStream->disabled)
2992 pStream->SetDiscard(AVDISCARD_NONE);
2993 double pts = m_dvdPlayerVideo.GetCurrentPts();
2994 if(pts == DVD_NOPTS_VALUE)
2995 pts = m_CurrentVideo.dts;
2996 if(pts == DVD_NOPTS_VALUE)
2998 pts += m_offset_pts;
2999 m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE));
3001 hint.Assign(*pStream, true);
3003 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
3005 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
3008 filename = m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename;
3011 hint.fpsscale = m_CurrentVideo.hint.fpsscale;
3012 hint.fpsrate = m_CurrentVideo.hint.fpsrate;
3018 pStream = m_pDemuxer->GetStream(iStream);
3019 if(!pStream || pStream->disabled)
3021 pStream->SetDiscard(AVDISCARD_NONE);
3023 hint.Assign(*pStream, true);
3025 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3029 if(m_CurrentSubtitle.id < 0
3030 || m_CurrentSubtitle.hint != hint)
3032 if(m_CurrentSubtitle.id >= 0)
3034 CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream");
3035 CloseSubtitleStream(false);
3038 if(!m_dvdPlayerSubtitle.OpenStream(hint, filename))
3040 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
3043 pStream->disabled = true;
3044 pStream->SetDiscard(AVDISCARD_ALL);
3050 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3052 m_CurrentSubtitle.id = iStream;
3053 m_CurrentSubtitle.source = source;
3054 m_CurrentSubtitle.hint = hint;
3055 m_CurrentSubtitle.stream = (void*)pStream;
3056 m_CurrentSubtitle.started = false;
3058 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
3062 bool CDVDPlayer::OpenTeletextStream(int iStream, int source)
3067 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
3068 if(!pStream || pStream->disabled)
3071 CDVDStreamInfo hint(*pStream, true);
3073 if (!m_dvdPlayerTeletext.CheckStream(hint))
3076 CLog::Log(LOGNOTICE, "Opening teletext stream: %i source: %i", iStream, source);
3078 if(m_CurrentTeletext.id < 0
3079 || m_CurrentTeletext.hint != hint)
3081 if(m_CurrentTeletext.id >= 0)
3083 CLog::Log(LOGDEBUG, " - teletext codecs hints have changed, must close previous stream");
3084 CloseTeletextStream(true);
3087 if (!m_dvdPlayerTeletext.OpenStream(hint))
3089 /* mark stream as disabled, to disallaw further attempts*/
3090 CLog::Log(LOGWARNING, "%s - Unsupported teletext stream %d. Stream disabled.", __FUNCTION__, iStream);
3091 pStream->disabled = true;
3092 pStream->SetDiscard(AVDISCARD_ALL);
3097 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3099 /* store information about stream */
3100 m_CurrentTeletext.id = iStream;
3101 m_CurrentTeletext.source = source;
3102 m_CurrentTeletext.hint = hint;
3103 m_CurrentTeletext.stream = (void*)pStream;
3104 m_CurrentTeletext.started = false;
3109 bool CDVDPlayer::CloseAudioStream(bool bWaitForBuffers)
3111 if (m_CurrentAudio.id < 0)
3114 CLog::Log(LOGNOTICE, "Closing audio stream");
3117 SetCaching(CACHESTATE_DONE);
3119 m_dvdPlayerAudio.CloseStream(bWaitForBuffers);
3121 m_CurrentAudio.Clear();
3125 bool CDVDPlayer::CloseVideoStream(bool bWaitForBuffers)
3127 if (m_CurrentVideo.id < 0)
3130 CLog::Log(LOGNOTICE, "Closing video stream");
3133 SetCaching(CACHESTATE_DONE);
3135 m_dvdPlayerVideo.CloseStream(bWaitForBuffers);
3137 m_CurrentVideo.Clear();
3141 bool CDVDPlayer::CloseSubtitleStream(bool bKeepOverlays)
3143 if (m_CurrentSubtitle.id < 0)
3146 CLog::Log(LOGNOTICE, "Closing subtitle stream");
3148 m_dvdPlayerSubtitle.CloseStream(!bKeepOverlays);
3150 m_CurrentSubtitle.Clear();
3154 bool CDVDPlayer::CloseTeletextStream(bool bWaitForBuffers)
3156 if (m_CurrentTeletext.id < 0)
3159 CLog::Log(LOGNOTICE, "Closing teletext stream");
3162 SetCaching(CACHESTATE_DONE);
3164 m_dvdPlayerTeletext.CloseStream(bWaitForBuffers);
3166 m_CurrentTeletext.Clear();
3170 void CDVDPlayer::FlushBuffers(bool queued, double pts, bool accurate)
3176 startpts = DVD_NOPTS_VALUE;
3178 /* call with demuxer pts */
3179 if(startpts != DVD_NOPTS_VALUE)
3180 startpts -= m_offset_pts;
3182 m_CurrentAudio.inited = false;
3183 m_CurrentAudio.dts = DVD_NOPTS_VALUE;
3184 m_CurrentAudio.startpts = startpts;
3186 m_CurrentVideo.inited = false;
3187 m_CurrentVideo.dts = DVD_NOPTS_VALUE;
3188 m_CurrentVideo.startpts = startpts;
3190 m_CurrentSubtitle.inited = false;
3191 m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
3192 m_CurrentSubtitle.startpts = startpts;
3194 m_CurrentTeletext.inited = false;
3195 m_CurrentTeletext.dts = DVD_NOPTS_VALUE;
3196 m_CurrentTeletext.startpts = startpts;
3200 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3201 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3202 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
3203 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3204 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3205 SynchronizePlayers(SYNCSOURCE_ALL);
3209 m_dvdPlayerAudio.Flush();
3210 m_dvdPlayerVideo.Flush();
3211 m_dvdPlayerSubtitle.Flush();
3212 m_dvdPlayerTeletext.Flush();
3214 // clear subtitle and menu overlays
3215 m_overlayContainer.Clear();
3217 if(m_playSpeed == DVD_PLAYSPEED_NORMAL
3218 || m_playSpeed == DVD_PLAYSPEED_PAUSE)
3220 // make sure players are properly flushed, should put them in stalled state
3221 CDVDMsgGeneralSynchronize* msg = new CDVDMsgGeneralSynchronize(1000, 0);
3222 m_dvdPlayerAudio.SendMessage(msg->Acquire(), 1);
3223 m_dvdPlayerVideo.SendMessage(msg->Acquire(), 1);
3224 msg->Wait(&m_bStop, 0);
3227 // purge any pending PLAYER_STARTED messages
3228 m_messenger.Flush(CDVDMsg::PLAYER_STARTED);
3230 // we should now wait for init cache
3231 SetCaching(CACHESTATE_FLUSH);
3232 m_CurrentAudio.started = false;
3233 m_CurrentVideo.started = false;
3234 m_CurrentSubtitle.started = false;
3235 m_CurrentTeletext.started = false;
3238 if(pts != DVD_NOPTS_VALUE)
3239 m_clock.Discontinuity(pts);
3242 // update state, buffers are flushed and it may take some time until
3243 // we get an update from players
3244 CSingleLock lock(m_StateSection);
3245 m_State = m_StateInput;
3249 // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
3250 int CDVDPlayer::OnDVDNavResult(void* pData, int iMessage)
3252 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
3255 m_overlayContainer.Add((CDVDOverlay*)pData);
3256 else if(iMessage == 1)
3257 m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
3258 else if(iMessage == 2)
3259 m_dvd.iSelectedAudioStream = *(int*)pData;
3260 else if(iMessage == 3)
3261 m_dvd.iSelectedSPUStream = *(int*)pData;
3262 else if(iMessage == 4)
3263 m_dvdPlayerVideo.EnableSubtitle(*(int*)pData ? true: false);
3264 else if(iMessage == 5)
3266 if (m_dvd.state != DVDSTATE_STILL)
3268 // else notify the player we have received a still frame
3270 m_dvd.iDVDStillTime = *(int*)pData;
3271 m_dvd.iDVDStillStartTime = XbmcThreads::SystemClockMillis();
3273 /* adjust for the output delay in the video queue */
3274 unsigned int time = 0;
3275 if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 )
3277 time = (unsigned int)(m_dvdPlayerVideo.GetOutputDelay() / ( DVD_TIME_BASE / 1000 ));
3278 if( time < 10000 && time > 0 )
3279 m_dvd.iDVDStillTime += time;
3281 m_dvd.state = DVDSTATE_STILL;
3283 "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec",
3284 m_dvd.iDVDStillTime, time / 1000);
3291 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3293 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
3297 case DVDNAV_STILL_FRAME:
3299 //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
3301 dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)pData;
3302 // should wait the specified time here while we let the player running
3303 // after that call dvdnav_still_skip(m_dvdnav);
3305 if (m_dvd.state != DVDSTATE_STILL)
3307 // else notify the player we have received a still frame
3309 if(still_event->length < 0xff)
3310 m_dvd.iDVDStillTime = still_event->length * 1000;
3312 m_dvd.iDVDStillTime = 0;
3314 m_dvd.iDVDStillStartTime = XbmcThreads::SystemClockMillis();
3316 /* adjust for the output delay in the video queue */
3317 unsigned int time = 0;
3318 if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 )
3320 time = (unsigned int)(m_dvdPlayerVideo.GetOutputDelay() / ( DVD_TIME_BASE / 1000 ));
3321 if( time < 10000 && time > 0 )
3322 m_dvd.iDVDStillTime += time;
3324 m_dvd.state = DVDSTATE_STILL;
3326 "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec",
3327 still_event->length, time / 1000);
3329 return NAVRESULT_HOLD;
3332 case DVDNAV_SPU_CLUT_CHANGE:
3334 m_dvdPlayerSubtitle.SendMessage(new CDVDMsgSubtitleClutChange((uint8_t*)pData));
3337 case DVDNAV_SPU_STREAM_CHANGE:
3339 dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)pData;
3341 int iStream = event->physical_wide;
3342 bool visible = !(iStream & 0x80);
3344 m_dvdPlayerVideo.EnableSubtitle(visible);
3347 m_dvd.iSelectedSPUStream = (iStream & ~0x80);
3349 m_dvd.iSelectedSPUStream = -1;
3351 m_CurrentSubtitle.stream = NULL;
3354 case DVDNAV_AUDIO_STREAM_CHANGE:
3356 // This should be the correct way i think, however we don't have any streams right now
3357 // since the demuxer hasn't started so it doesn't change. not sure how to do this.
3358 dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)pData;
3360 // Tell system what audiostream should be opened by default
3361 if (event->logical >= 0)
3362 m_dvd.iSelectedAudioStream = event->physical;
3364 m_dvd.iSelectedAudioStream = -1;
3366 m_CurrentAudio.stream = NULL;
3369 case DVDNAV_HIGHLIGHT:
3371 //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
3372 int iButton = pStream->GetCurrentButton();
3373 CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button %d\n", iButton);
3374 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
3377 case DVDNAV_VTS_CHANGE:
3379 //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
3380 CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
3382 //Make sure we clear all the old overlays here, or else old forced items are left.
3383 m_overlayContainer.Clear();
3385 //Force an aspect ratio that is set in the dvdheaders if available
3386 m_CurrentVideo.hint.aspect = pStream->GetVideoAspectRatio();
3387 if( m_dvdPlayerVideo.IsInited() )
3388 m_dvdPlayerVideo.SendMessage(new CDVDMsgDouble(CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
3390 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
3391 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
3393 return NAVRESULT_HOLD;
3396 case DVDNAV_CELL_CHANGE:
3398 //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
3399 CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
3401 m_dvd.state = DVDSTATE_NORMAL;
3403 if( m_dvdPlayerVideo.IsInited() )
3404 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
3407 case DVDNAV_NAV_PACKET:
3409 //pci_t* pci = (pci_t*)pData;
3411 // this should be possible to use to make sure we get
3412 // seamless transitions over these boundaries
3413 // if we remember the old vobunits boundaries
3414 // when a packet comes out of demuxer that has
3415 // pts values outside that boundary, it belongs
3416 // to the new vobunit, wich has new timestamps
3420 case DVDNAV_HOP_CHANNEL:
3422 // This event is issued whenever a non-seamless operation has been executed.
3423 // Applications with fifos should drop the fifos content to speed up responsiveness.
3424 CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
3425 if(m_dvd.state == DVDSTATE_SEEK)
3426 m_dvd.state = DVDSTATE_NORMAL;
3428 m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
3430 return NAVRESULT_ERROR;
3435 CLog::Log(LOGDEBUG, "DVDNAV_STOP");
3436 m_dvd.state = DVDSTATE_NORMAL;
3444 return NAVRESULT_NOP;
3447 bool CDVDPlayer::ShowPVRChannelInfo(void)
3449 bool bReturn(false);
3451 if (CSettings::Get().GetBool("pvrmenu.infoswitch"))
3453 int iTimeout = CSettings::Get().GetBool("pvrmenu.infotimeout") ? CSettings::Get().GetInt("pvrmenu.infotime") : 0;
3454 g_PVRManager.ShowPlayerInfo(iTimeout);
3462 bool CDVDPlayer::OnAction(const CAction &action)
3464 #define THREAD_ACTION(action) \
3466 if (!IsCurrentThread()) { \
3467 m_messenger.Put(new CDVDMsgType<CAction>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
3472 CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3475 if( m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0 && pMenus->GetTotalButtons() == 0 )
3477 switch(action.GetID())
3479 case ACTION_NEXT_ITEM:
3480 case ACTION_MOVE_RIGHT:
3481 case ACTION_MOVE_UP:
3482 case ACTION_SELECT_ITEM:
3484 THREAD_ACTION(action);
3485 /* this will force us out of the stillframe */
3486 CLog::Log(LOGDEBUG, "%s - User asked to exit stillframe", __FUNCTION__);
3487 m_dvd.iDVDStillStartTime = 0;
3488 m_dvd.iDVDStillTime = 1;
3495 switch (action.GetID())
3497 /* this code is disabled to allow switching playlist items (dvdimage "stacks") */
3499 case ACTION_PREV_ITEM: // SKIP-:
3501 THREAD_ACTION(action);
3502 CLog::Log(LOGDEBUG, " - pushed prev");
3503 pMenus->OnPrevious();
3504 g_infoManager.SetDisplayAfterSeek();
3508 case ACTION_NEXT_ITEM: // SKIP+:
3510 THREAD_ACTION(action);
3511 CLog::Log(LOGDEBUG, " - pushed next");
3513 g_infoManager.SetDisplayAfterSeek();
3518 case ACTION_SHOW_VIDEOMENU: // start button
3520 THREAD_ACTION(action);
3521 CLog::Log(LOGDEBUG, " - go to menu");
3523 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
3525 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
3526 m_callback.OnPlayBackResumed();
3528 // send a message to everyone that we've gone to the menu
3529 CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0);
3530 g_windowManager.SendThreadMessage(msg);
3536 if (pMenus->IsInMenu())
3538 switch (action.GetID())
3540 case ACTION_NEXT_ITEM:
3541 THREAD_ACTION(action);
3542 CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide");
3544 g_infoManager.SetDisplayAfterSeek();
3546 case ACTION_PREV_ITEM:
3547 THREAD_ACTION(action);
3548 CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
3549 pMenus->OnPrevious();
3550 g_infoManager.SetDisplayAfterSeek();
3552 case ACTION_PREVIOUS_MENU:
3553 case ACTION_NAV_BACK:
3555 THREAD_ACTION(action);
3556 CLog::Log(LOGDEBUG, " - menu back");
3560 case ACTION_MOVE_LEFT:
3562 THREAD_ACTION(action);
3563 CLog::Log(LOGDEBUG, " - move left");
3567 case ACTION_MOVE_RIGHT:
3569 THREAD_ACTION(action);
3570 CLog::Log(LOGDEBUG, " - move right");
3574 case ACTION_MOVE_UP:
3576 THREAD_ACTION(action);
3577 CLog::Log(LOGDEBUG, " - move up");
3581 case ACTION_MOVE_DOWN:
3583 THREAD_ACTION(action);
3584 CLog::Log(LOGDEBUG, " - move down");
3589 case ACTION_MOUSE_MOVE:
3590 case ACTION_MOUSE_LEFT_CLICK:
3593 m_dvdPlayerVideo.GetVideoRect(rs, rd);
3594 CPoint pt(action.GetAmount(), action.GetAmount(1));
3595 if (!rd.PtInRect(pt))
3596 return false; // out of bounds
3597 THREAD_ACTION(action);
3598 // convert to video coords...
3599 pt -= CPoint(rd.x1, rd.y1);
3600 pt.x *= rs.Width() / rd.Width();
3601 pt.y *= rs.Height() / rd.Height();
3602 pt += CPoint(rs.x1, rs.y1);
3603 if (action.GetID() == ACTION_MOUSE_LEFT_CLICK)
3604 return pMenus->OnMouseClick(pt);
3605 return pMenus->OnMouseMove(pt);
3608 case ACTION_SELECT_ITEM:
3610 THREAD_ACTION(action);
3611 CLog::Log(LOGDEBUG, " - button select");
3612 // show button pushed overlay
3613 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3614 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_CLICKED);
3616 pMenus->ActivateButton();
3630 THREAD_ACTION(action);
3631 // Offset from key codes back to button number
3632 int button = action.GetID() - REMOTE_0;
3633 CLog::Log(LOGDEBUG, " - button pressed %d", button);
3634 pMenus->SelectButton(button);
3641 return true; // message is handled
3645 if (dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream))
3647 switch (action.GetID())
3649 case ACTION_MOVE_UP:
3650 case ACTION_NEXT_ITEM:
3651 case ACTION_CHANNEL_UP:
3652 m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT));
3653 g_infoManager.SetDisplayAfterSeek();
3654 ShowPVRChannelInfo();
3658 case ACTION_MOVE_DOWN:
3659 case ACTION_PREV_ITEM:
3660 case ACTION_CHANNEL_DOWN:
3661 m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV));
3662 g_infoManager.SetDisplayAfterSeek();
3663 ShowPVRChannelInfo();
3667 case ACTION_CHANNEL_SWITCH:
3669 // Offset from key codes back to button number
3670 int channel = action.GetAmount();
3671 m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER, channel));
3672 g_infoManager.SetDisplayAfterSeek();
3673 ShowPVRChannelInfo();
3680 switch (action.GetID())
3682 case ACTION_NEXT_ITEM:
3683 if(GetChapterCount() > 0)
3685 m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()+1));
3686 g_infoManager.SetDisplayAfterSeek();
3691 case ACTION_PREV_ITEM:
3692 if(GetChapterCount() > 0)
3694 m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()-1));
3695 g_infoManager.SetDisplayAfterSeek();
3702 // return false to inform the caller we didn't handle the message
3706 bool CDVDPlayer::IsInMenu() const
3708 CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3711 if( m_dvd.state == DVDSTATE_STILL )
3714 return pStream->IsInMenu();
3719 bool CDVDPlayer::HasMenu()
3721 CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3728 bool CDVDPlayer::GetCurrentSubtitle(CStdString& strSubtitle)
3730 double pts = m_clock.GetClock();
3732 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3735 m_dvdPlayerSubtitle.GetCurrentSubtitle(strSubtitle, pts - m_dvdPlayerVideo.GetSubtitleDelay());
3737 // In case we stalled, don't output any subs
3738 if ((m_dvdPlayerVideo.IsStalled() && HasVideo()) || (m_dvdPlayerAudio.IsStalled() && HasAudio()))
3739 strSubtitle = m_lastSub;
3741 m_lastSub = strSubtitle;
3743 return !strSubtitle.IsEmpty();
3746 CStdString CDVDPlayer::GetPlayerState()
3748 CSingleLock lock(m_StateSection);
3749 return m_State.player_state;
3752 bool CDVDPlayer::SetPlayerState(CStdString state)
3754 m_messenger.Put(new CDVDMsgPlayerSetState(state));
3758 int CDVDPlayer::GetChapterCount()
3760 CSingleLock lock(m_StateSection);
3761 return m_State.chapter_count;
3764 int CDVDPlayer::GetChapter()
3766 CSingleLock lock(m_StateSection);
3767 return m_State.chapter;
3770 void CDVDPlayer::GetChapterName(CStdString& strChapterName)
3772 CSingleLock lock(m_StateSection);
3773 strChapterName = m_State.chapter_name;
3776 int CDVDPlayer::SeekChapter(int iChapter)
3778 if (GetChapterCount() > 0)
3782 if (iChapter > GetChapterCount())
3785 // Seek to the chapter.
3786 m_messenger.Put(new CDVDMsgPlayerSeekChapter(iChapter));
3787 SynchronizeDemuxer(100);
3791 // Do a regular big jump.
3792 if (GetChapter() > 0 && iChapter > GetChapter())
3800 int CDVDPlayer::AddSubtitle(const CStdString& strSubPath)
3802 return AddSubtitleFile(strSubPath);
3805 int CDVDPlayer::GetCacheLevel() const
3807 CSingleLock lock(m_StateSection);
3808 return (int)(m_StateInput.cache_level * 100);
3811 double CDVDPlayer::GetQueueTime()
3813 int a = m_dvdPlayerAudio.GetLevel();
3814 int v = m_dvdPlayerVideo.GetLevel();
3815 return max(a, v) * 8000.0 / 100;
3818 void CDVDPlayer::GetVideoStreamInfo(SPlayerVideoStreamInfo &info)
3820 info.bitrate = m_dvdPlayerVideo.GetVideoBitrate();
3823 if (m_pDemuxer && (m_CurrentVideo.id != -1))
3824 m_pDemuxer->GetStreamCodecName(m_CurrentVideo.id, retVal);
3825 info.videoCodecName = retVal;
3826 info.videoAspectRatio = m_dvdPlayerVideo.GetAspectRatio();
3827 m_dvdPlayerVideo.GetVideoRect(info.SrcRect, info.DestRect);
3830 int CDVDPlayer::GetSourceBitrate()
3833 return (int)m_pInputStream->GetBitstreamStats().GetBitrate();
3838 void CDVDPlayer::GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info)
3840 if (index < 0 || index > GetAudioStreamCount() - 1 )
3843 if (index == GetAudioStream())
3844 info.bitrate = m_dvdPlayerAudio.GetAudioBitrate();
3846 info.bitrate = m_pDemuxer->GetStreamFromAudioId(index)->iBitRate;
3848 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, index);
3849 if(s.language.length() > 0)
3850 info.language = s.language;
3852 if(s.name.length() > 0)
3855 if(s.type == STREAM_NONE)
3856 info.name += " (Invalid)";
3860 CDemuxStreamAudio* stream = static_cast<CDemuxStreamAudio*>(m_pDemuxer->GetStreamFromAudioId(index));
3863 info.channels = stream->iChannels;
3864 CStdString codecName;
3865 m_pDemuxer->GetStreamCodecName(stream->iId, codecName);
3866 info.audioCodecName = codecName;
3871 int CDVDPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename, CDemuxStream::EFlags flags)
3873 std::string ext = URIUtils::GetExtension(filename);
3874 std::string vobsubfile = subfilename;
3877 if (vobsubfile.empty())
3878 vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
3881 if(!v.Open(filename, vobsubfile))
3883 m_SelectionStreams.Update(NULL, &v);
3884 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename), 0);
3885 m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags;
3886 m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename2 = vobsubfile;
3891 CStdString strReplace(URIUtils::ReplaceExtension(filename,".idx"));
3892 if (XFILE::CFile::Exists(strReplace))
3896 s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename);
3897 s.type = STREAM_SUBTITLE;
3899 s.filename = filename;
3900 s.name = URIUtils::GetFileName(filename);
3902 m_SelectionStreams.Update(s);
3903 return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, s.source, s.id);
3906 void CDVDPlayer::UpdatePlayState(double timeout)
3908 if(m_StateInput.timestamp != 0
3909 && m_StateInput.timestamp + DVD_MSEC_TO_TIME(timeout) > CDVDClock::GetAbsoluteClock())
3912 SPlayerState state(m_StateInput);
3914 if (m_CurrentVideo.dts != DVD_NOPTS_VALUE)
3915 state.dts = m_CurrentVideo.dts;
3916 else if(m_CurrentAudio.dts != DVD_NOPTS_VALUE)
3917 state.dts = m_CurrentAudio.dts;
3921 state.chapter = m_pDemuxer->GetChapter();
3922 state.chapter_count = m_pDemuxer->GetChapterCount();
3923 m_pDemuxer->GetChapterName(state.chapter_name);
3925 if(state.dts == DVD_NOPTS_VALUE)
3928 state.time = DVD_TIME_TO_MSEC(state.dts + m_offset_pts);
3929 state.time_total = m_pDemuxer->GetStreamLength();
3930 state.time_src = ETIMESOURCE_CLOCK;
3933 state.canpause = true;
3934 state.canseek = true;
3938 // override from input stream if needed
3939 CDVDInputStream::IChannel* pChannel = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
3942 state.canrecord = pChannel->CanRecord();
3943 state.recording = pChannel->IsRecording();
3946 CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast<CDVDInputStream::IDisplayTime*>(m_pInputStream);
3947 if (pDisplayTime && pDisplayTime->GetTotalTime() > 0)
3949 state.time = pDisplayTime->GetTime();
3950 state.time_total = pDisplayTime->GetTotalTime();
3951 state.time_src = ETIMESOURCE_INPUT;
3954 if (CDVDInputStream::IMenus* ptr = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
3956 if(!ptr->GetState(state.player_state))
3957 state.player_state = "";
3959 if(m_dvd.state == DVDSTATE_STILL)
3961 state.time = XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime;
3962 state.time_total = m_dvd.iDVDStillTime;
3963 state.time_src = ETIMESOURCE_MENU;
3967 if (CDVDInputStream::ISeekable* ptr = dynamic_cast<CDVDInputStream::ISeekable*>(m_pInputStream))
3969 state.canpause = ptr->CanPause();
3970 state.canseek = ptr->CanSeek();
3976 state.time = m_Edl.RemoveCutTime(llrint(state.time));
3977 state.time_total = m_Edl.RemoveCutTime(llrint(state.time_total));
3980 if(state.time_total <= 0)
3981 state.canseek = false;
3983 if (state.time_src == ETIMESOURCE_CLOCK)
3984 state.time_offset = m_offset_pts;
3985 else if (state.dts != DVD_NOPTS_VALUE)
3986 state.time_offset = DVD_MSEC_TO_TIME(state.time) - state.dts;
3988 if (m_CurrentAudio.id >= 0 && m_pDemuxer)
3990 CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentAudio.id);
3991 if (pStream && pStream->type == STREAM_AUDIO)
3992 ((CDemuxStreamAudio*)pStream)->GetStreamInfo(state.demux_audio);
3995 state.demux_audio = "";
3997 if (m_CurrentVideo.id >= 0 && m_pDemuxer)
3999 CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentVideo.id);
4000 if (pStream && pStream->type == STREAM_VIDEO)
4001 ((CDemuxStreamVideo*)pStream)->GetStreamInfo(state.demux_video);
4004 state.demux_video = "";
4006 double level, delay, offset;
4007 if(GetCachingTimes(level, delay, offset))
4009 state.cache_delay = max(0.0, delay);
4010 state.cache_level = max(0.0, min(1.0, level));
4011 state.cache_offset = offset;
4015 state.cache_delay = 0.0;
4016 state.cache_level = min(1.0, GetQueueTime() / 8000.0);
4017 state.cache_offset = GetQueueTime() / state.time_total;
4020 XFILE::SCacheStatus status;
4021 if(m_pInputStream && m_pInputStream->GetCacheStatus(&status))
4023 state.cache_bytes = status.forward;
4024 if(state.time_total)
4025 state.cache_bytes += m_pInputStream->GetLength() * GetQueueTime() / state.time_total;
4028 state.cache_bytes = 0;
4030 state.timestamp = CDVDClock::GetAbsoluteClock();
4032 CSingleLock lock(m_StateSection);
4033 m_StateInput = state;
4036 void CDVDPlayer::UpdateApplication(double timeout)
4038 if(m_UpdateApplication != 0
4039 && m_UpdateApplication + DVD_MSEC_TO_TIME(timeout) > CDVDClock::GetAbsoluteClock())
4042 CDVDInputStream::IChannel* pStream = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
4045 CFileItem item(g_application.CurrentFileItem());
4046 if(pStream->UpdateItem(item))
4048 g_application.CurrentFileItem() = item;
4049 CApplicationMessenger::Get().SetCurrentItem(item);
4052 m_UpdateApplication = CDVDClock::GetAbsoluteClock();
4055 bool CDVDPlayer::CanRecord()
4057 CSingleLock lock(m_StateSection);
4058 return m_State.canrecord;
4061 bool CDVDPlayer::IsRecording()
4063 CSingleLock lock(m_StateSection);
4064 return m_State.recording;
4067 bool CDVDPlayer::Record(bool bOnOff)
4069 if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV) ||
4070 m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) )
4072 m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff));
4078 int CDVDPlayer::GetPictureWidth()
4080 if (m_pDemuxer && (m_CurrentVideo.id != -1))
4082 CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
4084 return stream->iWidth;
4089 int CDVDPlayer::GetPictureHeight()
4091 if (m_pDemuxer && (m_CurrentVideo.id != -1))
4093 CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
4095 return stream->iHeight;
4100 bool CDVDPlayer::GetStreamDetails(CStreamDetails &details)
4104 bool result = CDVDFileInfo::DemuxerToStreamDetails(m_pInputStream, m_pDemuxer, details);
4105 if (result && details.GetStreamCount(CStreamDetail::VIDEO) > 0) // this is more correct (dvds in particular)
4108 * We can only obtain the aspect & duration from dvdplayer when the Process() thread is running
4109 * and UpdatePlayState() has been called at least once. In this case dvdplayer duration/AR will
4110 * return 0 and we'll have to fallback to the (less accurate) info from the demuxer.
4112 float aspect = m_dvdPlayerVideo.GetAspectRatio();
4114 ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_fAspect = aspect;
4116 int64_t duration = GetTotalTime() / 1000;
4118 ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_iDuration = duration;
4126 CStdString CDVDPlayer::GetPlayingTitle()
4128 /* Currently we support only Title Name from Teletext line 30 */
4129 TextCacheStruct_t* ttcache = m_dvdPlayerTeletext.GetTeletextCache();
4130 if (ttcache && !ttcache->line30.empty())
4131 return ttcache->line30;
4136 bool CDVDPlayer::SwitchChannel(const CPVRChannel &channel)
4138 if (!g_PVRManager.CheckParentalLock(channel))
4142 if (!g_PVRManager.PerformChannelSwitch(channel, true))
4145 UpdateApplication(0);
4148 /* make sure the pvr window is updated */
4149 CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
4151 pWindow->SetInvalid();
4153 /* select the new channel */
4154 m_messenger.Put(new CDVDMsgType<CPVRChannel>(CDVDMsg::PLAYER_CHANNEL_SELECT, channel));
4159 bool CDVDPlayer::CachePVRStream(void) const
4161 return m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) &&
4162 !g_PVRManager.IsPlayingRecording() &&
4163 g_advancedSettings.m_bPVRCacheInDvdPlayer;