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/>.
22 #include "DVDPlayer.h"
24 #include "DVDInputStreams/DVDInputStream.h"
25 #include "DVDInputStreams/DVDInputStreamFile.h"
26 #include "DVDInputStreams/DVDFactoryInputStream.h"
27 #include "DVDInputStreams/DVDInputStreamNavigator.h"
28 #include "DVDInputStreams/DVDInputStreamTV.h"
29 #include "DVDInputStreams/DVDInputStreamPVRManager.h"
31 #include "DVDDemuxers/DVDDemux.h"
32 #include "DVDDemuxers/DVDDemuxUtils.h"
33 #include "DVDDemuxers/DVDDemuxVobsub.h"
34 #include "DVDDemuxers/DVDFactoryDemuxer.h"
35 #include "DVDDemuxers/DVDDemuxFFmpeg.h"
37 #include "DVDCodecs/DVDCodecs.h"
38 #include "DVDCodecs/DVDFactoryCodec.h"
40 #include "DVDFileInfo.h"
42 #include "utils/LangCodeExpander.h"
43 #include "guilib/Key.h"
44 #include "guilib/LocalizeStrings.h"
46 #include "utils/URIUtils.h"
47 #include "GUIInfoManager.h"
48 #include "guilib/GUIWindowManager.h"
49 #include "guilib/StereoscopicsManager.h"
50 #include "Application.h"
51 #include "ApplicationMessenger.h"
52 #include "filesystem/File.h"
53 #include "pictures/Picture.h"
54 #include "DllSwScale.h"
55 #ifdef HAS_VIDEO_PLAYBACK
56 #include "cores/VideoRenderers/RenderManager.h"
58 #ifdef HAS_PERFORMANCE_SAMPLE
59 #include "xbmc/utils/PerformanceSample.h"
61 #define MEASURE_FUNCTION
63 #include "settings/AdvancedSettings.h"
65 #include "GUIUserMessages.h"
66 #include "settings/Settings.h"
67 #include "settings/MediaSettings.h"
68 #include "utils/log.h"
69 #include "utils/TimeUtils.h"
70 #include "utils/StreamDetails.h"
71 #include "pvr/PVRManager.h"
72 #include "pvr/channels/PVRChannel.h"
73 #include "pvr/windows/GUIWindowPVR.h"
74 #include "pvr/addons/PVRClients.h"
75 #include "filesystem/PVRFile.h"
76 #include "video/dialogs/GUIDialogFullScreenInfo.h"
77 #include "utils/StreamUtils.h"
78 #include "utils/Variant.h"
79 #include "storage/MediaManager.h"
80 #include "dialogs/GUIDialogBusy.h"
81 #include "dialogs/GUIDialogKaiToast.h"
82 #include "xbmc/playlists/PlayListM3U.h"
83 #include "utils/StringUtils.h"
87 #include "utils/LangCodeExpander.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 class PredicateSubtitleFilter
142 std::string audiolang;
146 /** \brief The class' operator() decides if the given (subtitle) SelectionStream is relevant wrt.
147 * preferred subtitle language and audio language. If the subtitle is relevant <B>false</B> false is returned.
149 * A subtitle is relevant if
150 * - it was previously selected, or
151 * - it's an external sub and "prefer external subs was selected", or
152 * - it's a forced sub and "original stream's language" was selected, or
153 * - it's a forced sub and its language matches the audio's language, or
154 * - it's a default sub, or
155 * - its language matches the preferred subtitle's language (unequal to "original stream's language")
157 PredicateSubtitleFilter(std::string& lang)
159 original(StringUtils::EqualsNoCase(CSettings::Get().GetString("locale.subtitlelanguage"), "original")),
160 preferexternal(CSettings::Get().GetBool("subtitles.preferexternal"))
164 bool operator()(const SelectionStream& ss) const
166 if (ss.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream)
171 if(STREAM_SOURCE_MASK(ss.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(ss.source) == STREAM_SOURCE_TEXT)
175 if ((ss.flags & CDemuxStream::FLAG_FORCED) && (original || g_LangCodeExpander.CompareLangCodes(ss.language, audiolang)))
178 if ((ss.flags & CDemuxStream::FLAG_DEFAULT))
183 std::string subtitle_language = g_langInfo.GetSubtitleLanguage();
184 if (g_LangCodeExpander.CompareLangCodes(subtitle_language, ss.language))
192 static bool PredicateAudioPriority(const SelectionStream& lh, const SelectionStream& rh)
194 PREDICATE_RETURN(lh.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream
195 , rh.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream);
197 if(!StringUtils::EqualsNoCase(CSettings::Get().GetString("locale.audiolanguage"), "original"))
199 CStdString audio_language = g_langInfo.GetAudioLanguage();
200 PREDICATE_RETURN(g_LangCodeExpander.CompareLangCodes(audio_language, lh.language)
201 , g_LangCodeExpander.CompareLangCodes(audio_language, rh.language));
204 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
205 , rh.flags & CDemuxStream::FLAG_DEFAULT);
207 PREDICATE_RETURN(lh.channels
210 PREDICATE_RETURN(StreamUtils::GetCodecPriority(lh.codec)
211 , StreamUtils::GetCodecPriority(rh.codec));
215 /** \brief The class' operator() decides if the given (subtitle) SelectionStream lh is 'better than' the given (subtitle) SelectionStream rh.
216 * If lh is 'better than' rh the return value is true, false otherwise.
218 * A subtitle lh is 'better than' a subtitle rh (in evaluation order) if
219 * - lh was previously selected, or
220 * - lh is an external sub and "prefer external subs was selected" and rh not, or
221 * - lh is a forced sub and ("original stream's language" was selected or subtitles are off) and rh not, or
222 * - lh is an external sub and its language matches the preferred subtitle's language (unequal to "original stream's language") and rh not, or
223 * - lh is an external sub and rh not, or
224 * - lh is language matches the preferred subtitle's language (unequal to "original stream's language") and rh not, or
225 * - lh is a default sub and rh not
227 class PredicateSubtitlePriority
230 std::string audiolang;
234 PredicateSubtitleFilter filter;
236 PredicateSubtitlePriority(std::string& lang)
238 original(StringUtils::EqualsNoCase(CSettings::Get().GetString("locale.subtitlelanguage"), "original")),
239 preferextsubs(CSettings::Get().GetBool("subtitles.preferexternal")),
240 subson(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn),
245 bool relevant(const SelectionStream& ss) const
250 bool operator()(const SelectionStream& lh, const SelectionStream& rh) const
252 PREDICATE_RETURN(relevant(lh)
255 PREDICATE_RETURN(lh.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream
256 , rh.type_index == CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream);
260 PREDICATE_RETURN(STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_DEMUX_SUB
261 , STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB);
263 PREDICATE_RETURN(STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_TEXT
264 , STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT);
267 if(!subson || original)
269 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_FORCED && g_LangCodeExpander.CompareLangCodes(lh.language, audiolang)
270 , rh.flags & CDemuxStream::FLAG_FORCED && g_LangCodeExpander.CompareLangCodes(rh.language, audiolang));
272 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_FORCED
273 , rh.flags & CDemuxStream::FLAG_FORCED);
276 CStdString subtitle_language = g_langInfo.GetSubtitleLanguage();
279 PREDICATE_RETURN((STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_TEXT) && g_LangCodeExpander.CompareLangCodes(subtitle_language, lh.language)
280 , (STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT) && g_LangCodeExpander.CompareLangCodes(subtitle_language, rh.language));
283 PREDICATE_RETURN(STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_DEMUX_SUB
284 , STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB);
286 PREDICATE_RETURN(STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_TEXT
287 , STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT);
291 PREDICATE_RETURN(g_LangCodeExpander.CompareLangCodes(subtitle_language, lh.language)
292 , g_LangCodeExpander.CompareLangCodes(subtitle_language, rh.language));
295 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
296 , rh.flags & CDemuxStream::FLAG_DEFAULT);
302 static bool PredicateVideoPriority(const SelectionStream& lh, const SelectionStream& rh)
304 PREDICATE_RETURN(lh.flags & CDemuxStream::FLAG_DEFAULT
305 , rh.flags & CDemuxStream::FLAG_DEFAULT);
309 bool CSelectionStreams::Get(StreamType type, CDemuxStream::EFlags flag, SelectionStream& out)
311 CSingleLock lock(m_section);
312 for(int i=0;i<(int)m_Streams.size();i++)
314 if(m_Streams[i].type != type)
316 if((m_Streams[i].flags & flag) != flag)
324 int CSelectionStreams::IndexOf(StreamType type, int source, int id) const
326 CSingleLock lock(m_section);
328 for(int i=0;i<(int)m_Streams.size();i++)
330 if(type && m_Streams[i].type != type)
333 if(source && m_Streams[i].source != source)
337 if(m_Streams[i].id == id)
346 int CSelectionStreams::IndexOf(StreamType type, CDVDPlayer& p) const
348 if (p.m_pInputStream && p.m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
351 if(type == STREAM_AUDIO)
352 id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveAudioStream();
353 else if(type == STREAM_VIDEO)
354 id = p.m_CurrentVideo.id;
355 else if(type == STREAM_SUBTITLE)
356 id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveSubtitleStream();
358 return IndexOf(type, STREAM_SOURCE_NAV, id);
361 if(type == STREAM_AUDIO)
362 return IndexOf(type, p.m_CurrentAudio.source, p.m_CurrentAudio.id);
363 else if(type == STREAM_VIDEO)
364 return IndexOf(type, p.m_CurrentVideo.source, p.m_CurrentVideo.id);
365 else if(type == STREAM_SUBTITLE)
366 return IndexOf(type, p.m_CurrentSubtitle.source, p.m_CurrentSubtitle.id);
367 else if(type == STREAM_TELETEXT)
368 return IndexOf(type, p.m_CurrentTeletext.source, p.m_CurrentTeletext.id);
373 int CSelectionStreams::Source(StreamSource source, std::string filename)
375 CSingleLock lock(m_section);
376 int index = source - 1;
377 for(int i=0;i<(int)m_Streams.size();i++)
379 SelectionStream &s = m_Streams[i];
380 if(STREAM_SOURCE_MASK(s.source) != source)
382 // if it already exists, return same
383 if(s.filename == filename)
392 void CSelectionStreams::Update(SelectionStream& s)
394 CSingleLock lock(m_section);
395 int index = IndexOf(s.type, s.source, s.id);
398 SelectionStream& o = Get(s.type, index);
399 s.type_index = o.type_index;
404 s.type_index = Count(s.type);
405 m_Streams.push_back(s);
409 void CSelectionStreams::Update(CDVDInputStream* input, CDVDDemux* demuxer)
411 if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
413 CDVDInputStreamNavigator* nav = (CDVDInputStreamNavigator*)input;
414 string filename = nav->GetFileName();
415 int source = Source(STREAM_SOURCE_NAV, filename);
418 count = nav->GetAudioStreamCount();
419 for(int i=0;i<count;i++)
423 s.type = STREAM_AUDIO;
425 s.flags = CDemuxStream::FLAG_NONE;
426 s.filename = filename;
428 DVDNavStreamInfo info;
429 nav->GetAudioStreamInfo(i, info);
431 s.language = g_LangCodeExpander.ConvertToISO6392T(info.language);
432 s.channels = info.channels;
436 count = nav->GetSubTitleStreamCount();
437 for(int i=0;i<count;i++)
441 s.type = STREAM_SUBTITLE;
443 s.flags = CDemuxStream::FLAG_NONE;
444 s.filename = filename;
447 DVDNavStreamInfo info;
448 nav->GetSubtitleStreamInfo(i, info);
450 s.language = g_LangCodeExpander.ConvertToISO6392T(info.language);
456 string filename = demuxer->GetFileName();
457 int count = demuxer->GetNrOfStreams();
459 if(input) /* hack to know this is sub decoder */
460 source = Source(STREAM_SOURCE_DEMUX, filename);
462 source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
465 for(int i=0;i<count;i++)
467 CDemuxStream* stream = demuxer->GetStream(i);
468 /* skip streams with no type */
469 if (stream->type == STREAM_NONE)
471 /* make sure stream is marked with right source */
472 stream->source = source;
476 s.type = stream->type;
478 s.language = g_LangCodeExpander.ConvertToISO6392T(stream->language);
479 s.flags = stream->flags;
480 s.filename = demuxer->GetFileName();
481 stream->GetStreamName(s.name);
483 demuxer->GetStreamCodecName(stream->iId, codec);
485 s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below.
486 if(stream->type == STREAM_AUDIO)
489 ((CDemuxStreamAudio*)stream)->GetStreamType(type);
490 if(type.length() > 0)
492 if(s.name.length() > 0)
496 s.channels = ((CDemuxStreamAudio*)stream)->iChannels;
503 CDVDPlayer::CDVDPlayer(IPlayerCallback& callback)
505 CThread("DVDPlayer"),
506 m_CurrentAudio(STREAM_AUDIO, DVDPLAYER_AUDIO),
507 m_CurrentVideo(STREAM_VIDEO, DVDPLAYER_VIDEO),
508 m_CurrentSubtitle(STREAM_SUBTITLE, DVDPLAYER_SUBTITLE),
509 m_CurrentTeletext(STREAM_TELETEXT, DVDPLAYER_TELETEXT),
510 m_messenger("player"),
511 m_dvdPlayerVideo(&m_clock, &m_overlayContainer, m_messenger),
512 m_dvdPlayerAudio(&m_clock, m_messenger),
513 m_dvdPlayerSubtitle(&m_overlayContainer),
514 m_dvdPlayerTeletext(),
516 m_DemuxerPausePending(false)
519 m_pSubtitleDemuxer = NULL;
520 m_pInputStream = NULL;
524 m_EdlAutoSkipMarkers.Clear();
525 m_UpdateApplication = 0;
527 m_bAbortRequest = false;
530 m_playSpeed = DVD_PLAYSPEED_NORMAL;
531 m_caching = CACHESTATE_DONE;
535 memset(&m_SpeedState, 0, sizeof(m_SpeedState));
537 #ifdef DVDDEBUG_MESSAGE_TRACKER
538 g_dvdMessageTracker.Init();
542 CDVDPlayer::~CDVDPlayer()
546 #ifdef DVDDEBUG_MESSAGE_TRACKER
547 g_dvdMessageTracker.DeInit();
551 bool CDVDPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
555 CLog::Log(LOGNOTICE, "DVDPlayer: Opening: %s", CURL::GetRedacted(file.GetPath()).c_str());
557 // if playing a file close it first
558 // this has to be changed so we won't have to close it.
562 m_bAbortRequest = false;
563 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
566 m_UpdateApplication = 0;
569 m_PlayerOptions = options;
571 m_mimetype = file.GetMimeType();
572 m_filename = file.GetPath();
576 #if defined(HAS_VIDEO_PLAYBACK)
577 g_renderManager.PreInit();
582 // wait for the ready event
583 CGUIDialogBusy::WaitOnEvent(m_ready, g_advancedSettings.m_videoBusyDialogDelay_ms, false);
585 // Playback might have been stopped due to some error
586 if (m_bStop || m_bAbortRequest)
593 CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__);
598 bool CDVDPlayer::CloseFile(bool reopen)
600 CLog::Log(LOGNOTICE, "CDVDPlayer::CloseFile()");
602 // set the abort request so that other threads can finish up
603 m_bAbortRequest = true;
605 // tell demuxer to abort
609 if(m_pSubtitleDemuxer)
610 m_pSubtitleDemuxer->Abort();
613 m_pInputStream->Abort();
615 CLog::Log(LOGNOTICE, "DVDPlayer: waiting for threads to exit");
617 // wait for the main thread to finish up
618 // since this main thread cleans up all other resources and threads
619 // we are done after the StopThread call
623 m_EdlAutoSkipMarkers.Clear();
628 CLog::Log(LOGNOTICE, "DVDPlayer: finished waiting");
629 #if defined(HAS_VIDEO_PLAYBACK)
630 g_renderManager.UnInit();
635 bool CDVDPlayer::IsPlaying() const
640 void CDVDPlayer::OnStartup()
642 m_CurrentVideo.Clear();
643 m_CurrentAudio.Clear();
644 m_CurrentSubtitle.Clear();
645 m_CurrentTeletext.Clear();
649 CUtil::ClearTempFonts();
652 bool CDVDPlayer::OpenInputStream()
655 SAFE_DELETE(m_pInputStream);
657 CLog::Log(LOGNOTICE, "Creating InputStream");
659 // correct the filename if needed
660 CStdString filename(m_filename);
661 if (StringUtils::StartsWith(filename, "dvd://")
662 || StringUtils::EqualsNoCase(filename, "iso9660://video_ts/video_ts.ifo"))
664 m_filename = g_mediaManager.TranslateDevicePath("");
667 m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_filename, m_mimetype);
668 if(m_pInputStream == NULL)
670 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - unable to create input stream for [%s]", m_filename.c_str());
674 m_pInputStream->SetFileItem(m_item);
676 if (!m_pInputStream->Open(m_filename.c_str(), m_mimetype))
678 CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - error opening [%s]", m_filename.c_str());
682 // find any available external subtitles for non dvd files
683 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
684 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)
685 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
686 && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
688 // find any available external subtitles
689 std::vector<CStdString> filenames;
690 CUtil::ScanForExternalSubtitles( m_filename, filenames );
692 // find any upnp subtitles
693 CStdString key("upnp:subtitle:1");
694 for(unsigned s = 1; m_item.HasProperty(key); key = StringUtils::Format("upnp:subtitle:%u", ++s))
695 filenames.push_back(m_item.GetProperty(key).asString());
697 for(unsigned int i=0;i<filenames.size();i++)
699 // if vobsub subtitle:
700 if (URIUtils::HasExtension(filenames[i], ".idx"))
702 CStdString strSubFile;
703 if ( CUtil::FindVobSubPair( filenames, filenames[i], strSubFile ) )
704 AddSubtitleFile(filenames[i], strSubFile);
708 if ( !CUtil::IsVobSub(filenames, filenames[i] ) )
710 AddSubtitleFile(filenames[i]);
713 } // end loop over all subtitle files
715 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleCached = true;
718 SetAVDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_AudioDelay);
719 SetSubTitleDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleDelay);
723 m_ChannelEntryTimeOut.SetInfinite();
728 bool CDVDPlayer::OpenDemuxStream()
731 SAFE_DELETE(m_pDemuxer);
733 CLog::Log(LOGNOTICE, "Creating Demuxer");
738 while(!m_bStop && attempts-- > 0)
740 m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
741 if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
745 else if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE)
747 CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__);
755 CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__);
762 CLog::Log(LOGERROR, "%s - Exception thrown when opening demuxer", __FUNCTION__);
766 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
767 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
768 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
770 int64_t len = m_pInputStream->GetLength();
771 int64_t tim = m_pDemuxer->GetStreamLength();
772 if(len > 0 && tim > 0)
773 m_pInputStream->SetReadRate(g_advancedSettings.m_readBufferFactor * len * 1000 / tim);
778 void CDVDPlayer::OpenDefaultStreams(bool reset)
780 // if input stream dictate, we will open later
781 if(m_dvd.iSelectedAudioStream >= 0
782 || m_dvd.iSelectedSPUStream >= 0)
785 SelectionStreams streams;
789 streams = m_SelectionStreams.Get(STREAM_VIDEO, PredicateVideoPriority);
791 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
793 if(OpenVideoStream(it->id, it->source, reset))
797 CloseVideoStream(true);
800 if(m_PlayerOptions.video_only)
803 streams = m_SelectionStreams.Get(STREAM_AUDIO, PredicateAudioPriority);
806 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
808 if(OpenAudioStream(it->id, it->source, reset))
812 CloseAudioStream(true);
814 // enable or disable subtitles
815 bool visible = CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn;
817 // open subtitle stream
818 SelectionStream as = m_SelectionStreams.Get(STREAM_AUDIO, GetAudioStream());
819 PredicateSubtitlePriority psp(as.language);
820 streams = m_SelectionStreams.Get(STREAM_SUBTITLE, psp);
822 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
824 if(OpenSubtitleStream(it->id, it->source))
827 if(!psp.relevant(*it))
829 else if(it->flags & CDemuxStream::FLAG_FORCED)
834 CloseSubtitleStream(true);
836 SetSubtitleVisibleInternal(visible);
838 // open teletext stream
839 streams = m_SelectionStreams.Get(STREAM_TELETEXT);
841 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
843 if(OpenTeletextStream(it->id, it->source))
847 CloseTeletextStream(true);
850 bool CDVDPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
853 // check if we should read from subtitle demuxer
854 if( m_pSubtitleDemuxer && m_dvdPlayerSubtitle.AcceptsData() )
856 packet = m_pSubtitleDemuxer->Read();
860 UpdateCorrection(packet, m_offset_pts);
861 if(packet->iStreamId < 0)
864 stream = m_pSubtitleDemuxer->GetStream(packet->iStreamId);
867 CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
870 if(stream->source == STREAM_SOURCE_NONE)
872 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
873 m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer);
879 // read a data frame from stream.
881 packet = m_pDemuxer->Read();
885 // stream changed, update and open defaults
886 if(packet->iStreamId == DMX_SPECIALID_STREAMCHANGE)
888 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
889 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
890 OpenDefaultStreams(false);
892 // reevaluate HasVideo/Audio, we may have switched from/to a radio channel
893 if(m_CurrentVideo.id < 0)
895 if(m_CurrentAudio.id < 0)
901 UpdateCorrection(packet, m_offset_pts);
903 if(packet->iStreamId < 0)
908 stream = m_pDemuxer->GetStream(packet->iStreamId);
911 CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
914 if(stream->source == STREAM_SOURCE_NONE)
916 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
917 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
925 bool CDVDPlayer::IsValidStream(CCurrentStream& stream)
928 return true; // we consider non selected as valid
930 int source = STREAM_SOURCE_MASK(stream.source);
931 if(source == STREAM_SOURCE_TEXT)
933 if(source == STREAM_SOURCE_DEMUX_SUB)
935 CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.id);
936 if(st == NULL || st->disabled)
938 if(st->type != stream.type)
942 if(source == STREAM_SOURCE_DEMUX)
944 CDemuxStream* st = m_pDemuxer->GetStream(stream.id);
945 if(st == NULL || st->disabled)
947 if(st->type != stream.type)
950 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
952 if(stream.type == STREAM_AUDIO && st->iPhysicalId != m_dvd.iSelectedAudioStream)
954 if(stream.type == STREAM_SUBTITLE && st->iPhysicalId != m_dvd.iSelectedSPUStream)
964 bool CDVDPlayer::IsBetterStream(CCurrentStream& current, CDemuxStream* stream)
966 // Do not reopen non-video streams if we're in video-only mode
967 if(m_PlayerOptions.video_only && current.type != STREAM_VIDEO)
973 if (m_pInputStream && ( m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
974 || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) )
978 source_type = STREAM_SOURCE_MASK(current.source);
979 if(source_type != STREAM_SOURCE_DEMUX
980 && source_type != STREAM_SOURCE_NONE)
983 source_type = STREAM_SOURCE_MASK(stream->source);
984 if(source_type != STREAM_SOURCE_DEMUX
985 || stream->type != current.type
986 || stream->iId == current.id)
989 if(current.type == STREAM_AUDIO && stream->iPhysicalId == m_dvd.iSelectedAudioStream)
991 if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream)
993 if(current.type == STREAM_VIDEO && current.id < 0)
998 if(stream->source == current.source
999 && stream->iId == current.id)
1002 if(stream->type != current.type)
1005 if(current.type == STREAM_SUBTITLE)
1014 void CDVDPlayer::Process()
1016 if (!OpenInputStream())
1018 m_bAbortRequest = true;
1022 if (CDVDInputStream::IMenus* ptr = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
1024 CLog::Log(LOGNOTICE, "DVDPlayer: playing a file with menu's");
1025 if(dynamic_cast<CDVDInputStreamNavigator*>(m_pInputStream))
1026 m_PlayerOptions.starttime = 0;
1028 if(m_PlayerOptions.state.size() > 0)
1029 ptr->SetState(m_PlayerOptions.state);
1030 else if(CDVDInputStreamNavigator* nav = dynamic_cast<CDVDInputStreamNavigator*>(m_pInputStream))
1031 nav->EnableSubtitleStream(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn);
1033 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleCached = true;
1036 if(!OpenDemuxStream())
1038 m_bAbortRequest = true;
1042 // allow renderer to switch to fullscreen if requested
1043 m_dvdPlayerVideo.EnableFullscreen(m_PlayerOptions.fullscreen);
1045 OpenDefaultStreams();
1047 // look for any EDL files
1049 m_EdlAutoSkipMarkers.Clear();
1050 if (m_CurrentVideo.id >= 0 && m_CurrentVideo.hint.fpsrate > 0 && m_CurrentVideo.hint.fpsscale > 0)
1052 float fFramesPerSecond = (float)m_CurrentVideo.hint.fpsrate / (float)m_CurrentVideo.hint.fpsscale;
1053 m_Edl.ReadEditDecisionLists(m_filename, fFramesPerSecond, m_CurrentVideo.hint.height);
1057 * Check to see if the demuxer should start at something other than time 0. This will be the case
1058 * if there was a start time specified as part of the "Start from where last stopped" (aka
1059 * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
1063 if(m_PlayerOptions.starttime > 0 || m_PlayerOptions.startpercent > 0)
1065 if (m_PlayerOptions.startpercent > 0 && m_pDemuxer)
1067 int64_t playerStartTime = (int64_t) ( ( (float) m_pDemuxer->GetStreamLength() ) * ( m_PlayerOptions.startpercent/(float)100 ) );
1068 starttime = m_Edl.RestoreCutTime(playerStartTime);
1072 starttime = m_Edl.RestoreCutTime((int64_t)m_PlayerOptions.starttime * 1000); // s to ms
1074 CLog::Log(LOGDEBUG, "%s - Start position set to last stopped position: %d", __FUNCTION__, starttime);
1076 else if(m_Edl.InCut(0, &cut)
1077 && (cut.action == CEdl::CUT || cut.action == CEdl::COMM_BREAK))
1079 starttime = cut.end;
1080 CLog::Log(LOGDEBUG, "%s - Start position set to end of first cut or commercial break: %d", __FUNCTION__, starttime);
1081 if(cut.action == CEdl::COMM_BREAK)
1084 * Setup auto skip markers as if the commercial break had been skipped using standard
1087 m_EdlAutoSkipMarkers.commbreak_start = cut.start;
1088 m_EdlAutoSkipMarkers.commbreak_end = cut.end;
1089 m_EdlAutoSkipMarkers.seek_to_start = true;
1094 double startpts = DVD_NOPTS_VALUE;
1097 if (m_pDemuxer->SeekTime(starttime, false, &startpts))
1098 CLog::Log(LOGDEBUG, "%s - starting demuxer from: %d", __FUNCTION__, starttime);
1100 CLog::Log(LOGDEBUG, "%s - failed to start demuxing from: %d", __FUNCTION__, starttime);
1103 if(m_pSubtitleDemuxer)
1105 if(m_pSubtitleDemuxer->SeekTime(starttime, false, &startpts))
1106 CLog::Log(LOGDEBUG, "%s - starting subtitle demuxer from: %d", __FUNCTION__, starttime);
1108 CLog::Log(LOGDEBUG, "%s - failed to start subtitle demuxing from: %d", __FUNCTION__, starttime);
1112 // make sure all selected stream have data on startup
1113 if (CachePVRStream())
1114 SetCaching(CACHESTATE_PVR);
1116 // make sure application know our info
1117 UpdateApplication(0);
1120 if(m_PlayerOptions.identify == false)
1121 m_callback.OnPlayBackStarted();
1123 // we are done initializing now, set the readyevent
1126 if (!CachePVRStream())
1127 SetCaching(CACHESTATE_FLUSH);
1129 while (!m_bAbortRequest)
1131 // handle messages send to this thread, like seek or demuxer reset requests
1137 // should we open a new input stream?
1140 if (OpenInputStream() == false)
1142 m_bAbortRequest = true;
1147 // should we open a new demuxer?
1150 if (m_pInputStream->NextStream() == CDVDInputStream::NEXTSTREAM_NONE)
1153 if (m_pInputStream->IsEOF())
1156 if (OpenDemuxStream() == false)
1158 m_bAbortRequest = true;
1162 OpenDefaultStreams();
1164 // never allow first frames after open to be skipped
1165 if( m_dvdPlayerVideo.IsInited() )
1166 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
1168 if (CachePVRStream())
1169 SetCaching(CACHESTATE_PVR);
1171 UpdateApplication(0);
1175 // handle eventual seeks due to playspeed
1178 // update player state
1179 UpdatePlayState(200);
1181 // update application with our state
1182 UpdateApplication(1000);
1184 // make sure we run subtitle process here
1185 m_dvdPlayerSubtitle.Process(m_clock.GetClock() + m_State.time_offset - m_dvdPlayerVideo.GetSubtitleDelay(), m_State.time_offset);
1187 if (CheckDelayedChannelEntry())
1190 // if the queues are full, no need to read more
1191 if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0) ||
1192 (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
1194 if(m_pDemuxer && m_DemuxerPausePending)
1196 m_DemuxerPausePending = false;
1197 m_pDemuxer->SetSpeed(DVD_PLAYSPEED_PAUSE);
1204 // always yield to players if they have data levels > 50 percent
1205 if((m_dvdPlayerAudio.GetLevel() > 50 || m_CurrentAudio.id < 0)
1206 && (m_dvdPlayerVideo.GetLevel() > 50 || m_CurrentVideo.id < 0))
1209 DemuxPacket* pPacket = NULL;
1210 CDemuxStream *pStream = NULL;
1211 ReadPacket(pPacket, pStream);
1212 if (pPacket && !pStream)
1214 /* probably a empty packet, just free it and move on */
1215 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
1221 // when paused, demuxer could be be returning empty
1222 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
1225 // check for a still frame state
1226 if (CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
1228 // stills will be skipped
1229 if(m_dvd.state == DVDSTATE_STILL)
1231 if (m_dvd.iDVDStillTime > 0)
1233 if ((XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime) >= m_dvd.iDVDStillTime)
1235 m_dvd.iDVDStillTime = 0;
1236 m_dvd.iDVDStillStartTime = 0;
1237 m_dvd.state = DVDSTATE_NORMAL;
1238 pStream->SkipStill();
1245 // if there is another stream available, reopen demuxer
1246 CDVDInputStream::ENextStream next = m_pInputStream->NextStream();
1247 if(next == CDVDInputStream::NEXTSTREAM_OPEN)
1249 SAFE_DELETE(m_pDemuxer);
1250 m_CurrentAudio.stream = NULL;
1251 m_CurrentVideo.stream = NULL;
1252 m_CurrentSubtitle.stream = NULL;
1256 // input stream asked us to just retry
1257 if(next == CDVDInputStream::NEXTSTREAM_RETRY)
1263 // make sure we tell all players to finish it's data
1264 if(m_CurrentAudio.inited)
1265 m_dvdPlayerAudio.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
1266 if(m_CurrentVideo.inited)
1267 m_dvdPlayerVideo.SendMessage (new CDVDMsg(CDVDMsg::GENERAL_EOF));
1268 if(m_CurrentSubtitle.inited)
1269 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
1270 if(m_CurrentTeletext.inited)
1271 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_EOF));
1272 m_CurrentAudio.inited = false;
1273 m_CurrentVideo.inited = false;
1274 m_CurrentSubtitle.inited = false;
1275 m_CurrentTeletext.inited = false;
1277 // if we are caching, start playing it again
1278 SetCaching(CACHESTATE_DONE);
1280 // while players are still playing, keep going to allow seekbacks
1281 if(m_dvdPlayerAudio.HasData()
1282 || m_dvdPlayerVideo.HasData())
1288 if (!m_pInputStream->IsEOF())
1289 CLog::Log(LOGINFO, "%s - eof reading from demuxer", __FUNCTION__);
1291 m_CurrentAudio.started = false;
1292 m_CurrentVideo.started = false;
1293 m_CurrentSubtitle.started = false;
1294 m_CurrentTeletext.started = false;
1299 // it's a valid data packet, reset error counter
1302 // check so that none of our streams has become invalid
1303 if (!IsValidStream(m_CurrentAudio) && m_dvdPlayerAudio.IsStalled()) CloseAudioStream(true);
1304 if (!IsValidStream(m_CurrentVideo) && m_dvdPlayerVideo.IsStalled()) CloseVideoStream(true);
1305 if (!IsValidStream(m_CurrentSubtitle) && m_dvdPlayerSubtitle.IsStalled()) CloseSubtitleStream(true);
1306 if (!IsValidStream(m_CurrentTeletext)) CloseTeletextStream(true);
1308 // see if we can find something better to play
1309 if (IsBetterStream(m_CurrentAudio, pStream)) OpenAudioStream (pStream->iId, pStream->source);
1310 if (IsBetterStream(m_CurrentVideo, pStream)) OpenVideoStream (pStream->iId, pStream->source);
1311 if (IsBetterStream(m_CurrentSubtitle, pStream)) OpenSubtitleStream(pStream->iId, pStream->source);
1312 if (IsBetterStream(m_CurrentTeletext, pStream)) OpenTeletextStream(pStream->iId, pStream->source);
1314 // process the packet
1315 ProcessPacket(pStream, pPacket);
1317 // check if in a cut or commercial break that should be automatically skipped
1318 CheckAutoSceneSkip();
1322 bool CDVDPlayer::CheckDelayedChannelEntry(void)
1324 bool bReturn(false);
1326 if (m_ChannelEntryTimeOut.IsTimePast())
1328 CFileItem currentFile(g_application.CurrentFileItem());
1329 CPVRChannel *currentChannel = currentFile.GetPVRChannelInfoTag();
1330 SwitchChannel(*currentChannel);
1333 m_ChannelEntryTimeOut.SetInfinite();
1339 void CDVDPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
1341 /* process packet if it belongs to selected stream. for dvd's don't allow automatic opening of streams*/
1342 StreamLock lock(this);
1346 if (pPacket->iStreamId == m_CurrentAudio.id && pStream->source == m_CurrentAudio.source && pStream->type == STREAM_AUDIO)
1347 ProcessAudioData(pStream, pPacket);
1348 else if (pPacket->iStreamId == m_CurrentVideo.id && pStream->source == m_CurrentVideo.source && pStream->type == STREAM_VIDEO)
1349 ProcessVideoData(pStream, pPacket);
1350 else if (pPacket->iStreamId == m_CurrentSubtitle.id && pStream->source == m_CurrentSubtitle.source && pStream->type == STREAM_SUBTITLE)
1351 ProcessSubData(pStream, pPacket);
1352 else if (pPacket->iStreamId == m_CurrentTeletext.id && pStream->source == m_CurrentTeletext.source && pStream->type == STREAM_TELETEXT)
1353 ProcessTeletextData(pStream, pPacket);
1356 pStream->SetDiscard(AVDISCARD_ALL);
1357 CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
1362 CLog::Log(LOGERROR, "%s - Exception thrown when processing demux packet", __FUNCTION__);
1367 void CDVDPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
1369 if (m_CurrentAudio.stream != (void*)pStream
1370 || m_CurrentAudio.changes != pStream->changes)
1372 /* check so that dmuxer hints or extra data hasn't changed */
1373 /* if they have, reopen stream */
1375 if (m_CurrentAudio.hint != CDVDStreamInfo(*pStream, true))
1376 OpenAudioStream( pPacket->iStreamId, pStream->source );
1378 m_CurrentAudio.stream = (void*)pStream;
1379 m_CurrentAudio.changes = pStream->changes;
1382 // check if we are too slow and need to recache
1383 CheckStartCaching(m_CurrentAudio);
1385 CheckContinuity(m_CurrentAudio, pPacket);
1386 UpdateTimestamps(m_CurrentAudio, pPacket);
1389 if (CheckPlayerInit(m_CurrentAudio, DVDPLAYER_AUDIO))
1393 * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped.
1394 * If not inside a hard cut, but the demux point has reached an EDL mute section then trigger the
1395 * AUDIO_SILENCE state. The AUDIO_SILENCE state is reverted as soon as the demux point is outside
1396 * of any EDL section while EDL mute is still active.
1399 if (CheckSceneSkip(m_CurrentAudio))
1401 else if (m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) && cut.action == CEdl::MUTE // Inside EDL mute
1402 && !m_EdlAutoSkipMarkers.mute) // Mute not already triggered
1404 m_dvdPlayerAudio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, true));
1405 m_EdlAutoSkipMarkers.mute = true;
1407 else if (!m_Edl.InCut(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &cut) // Outside of any EDL
1408 && m_EdlAutoSkipMarkers.mute) // But the mute hasn't been removed yet
1410 m_dvdPlayerAudio.SendMessage(new CDVDMsgBool(CDVDMsg::AUDIO_SILENCE, false));
1411 m_EdlAutoSkipMarkers.mute = false;
1414 m_dvdPlayerAudio.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1417 void CDVDPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
1419 if (m_CurrentVideo.stream != (void*)pStream
1420 || m_CurrentVideo.changes != pStream->changes)
1422 /* check so that dmuxer hints or extra data hasn't changed */
1423 /* if they have reopen stream */
1425 if (m_CurrentVideo.hint != CDVDStreamInfo(*pStream, true))
1426 OpenVideoStream(pPacket->iStreamId, pStream->source);
1428 m_CurrentVideo.stream = (void*)pStream;
1429 m_CurrentVideo.changes = pStream->changes;
1432 // check if we are too slow and need to recache
1433 CheckStartCaching(m_CurrentVideo);
1435 if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
1437 CheckContinuity(m_CurrentVideo, pPacket);
1438 UpdateTimestamps(m_CurrentVideo, pPacket);
1442 if (CheckPlayerInit(m_CurrentVideo, DVDPLAYER_VIDEO))
1445 if (CheckSceneSkip(m_CurrentVideo))
1448 m_dvdPlayerVideo.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1451 void CDVDPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
1453 if (m_CurrentSubtitle.stream != (void*)pStream
1454 || m_CurrentSubtitle.changes != pStream->changes)
1456 /* check so that dmuxer hints or extra data hasn't changed */
1457 /* if they have reopen stream */
1459 if (m_CurrentSubtitle.hint != CDVDStreamInfo(*pStream, true))
1460 OpenSubtitleStream(pPacket->iStreamId, pStream->source);
1462 m_CurrentSubtitle.stream = (void*)pStream;
1463 m_CurrentSubtitle.changes = pStream->changes;
1466 UpdateTimestamps(m_CurrentSubtitle, pPacket);
1469 if (CheckPlayerInit(m_CurrentSubtitle, DVDPLAYER_SUBTITLE))
1472 if (CheckSceneSkip(m_CurrentSubtitle))
1475 m_dvdPlayerSubtitle.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1477 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1478 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
1481 void CDVDPlayer::ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket)
1483 if (m_CurrentTeletext.stream != (void*)pStream
1484 || m_CurrentTeletext.changes != pStream->changes)
1486 /* check so that dmuxer hints or extra data hasn't changed */
1487 /* if they have, reopen stream */
1488 if (m_CurrentTeletext.hint != CDVDStreamInfo(*pStream, true))
1489 OpenTeletextStream( pPacket->iStreamId, pStream->source );
1491 m_CurrentTeletext.stream = (void*)pStream;
1492 m_CurrentTeletext.changes = pStream->changes;
1494 UpdateTimestamps(m_CurrentTeletext, pPacket);
1497 if (CheckPlayerInit(m_CurrentTeletext, DVDPLAYER_TELETEXT))
1500 if (CheckSceneSkip(m_CurrentTeletext))
1503 m_dvdPlayerTeletext.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1506 bool CDVDPlayer::GetCachingTimes(double& level, double& delay, double& offset)
1508 if(!m_pInputStream || !m_pDemuxer)
1511 XFILE::SCacheStatus status;
1512 if (!m_pInputStream->GetCacheStatus(&status))
1515 int64_t cached = status.forward;
1516 unsigned currate = status.currate;
1517 unsigned maxrate = status.maxrate;
1518 bool full = status.full;
1520 int64_t length = m_pInputStream->GetLength();
1521 int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR);
1523 if(cached < 0 || length <= 0 || remain < 0)
1526 double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
1527 double queued = 1000.0 * GetQueueTime() / play_sbp;
1531 offset = (double)(cached + queued) / length;
1536 double cache_sbp = 1.1 * (double)DVD_TIME_BASE / currate; /* underestimate by 10 % */
1537 double play_left = play_sbp * (remain + queued); /* time to play out all remaining bytes */
1538 double cache_left = cache_sbp * (remain - cached); /* time to cache the remaining bytes */
1539 double cache_need = std::max(0.0, remain - play_left / cache_sbp); /* bytes needed until play_left == cache_left */
1541 delay = cache_left - play_left;
1543 if (full && (currate < maxrate) )
1544 level = -1.0; /* buffer is full & our read rate is too low */
1546 level = (cached + queued) / (cache_need + queued);
1551 void CDVDPlayer::HandlePlaySpeed()
1553 ECacheState caching = m_caching;
1555 if(IsInMenu() && caching != CACHESTATE_DONE)
1556 caching = CACHESTATE_DONE;
1558 if(caching == CACHESTATE_FULL)
1560 double level, delay, offset;
1561 if(GetCachingTimes(level, delay, offset))
1565 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(21454), g_localizeStrings.Get(21455));
1566 caching = CACHESTATE_INIT;
1569 caching = CACHESTATE_INIT;
1573 if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0)
1574 || (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
1575 caching = CACHESTATE_INIT;
1579 if(caching == CACHESTATE_INIT)
1581 // if all enabled streams have been inited we are done
1582 if((m_CurrentVideo.id < 0 || m_CurrentVideo.started)
1583 && (m_CurrentAudio.id < 0 || m_CurrentAudio.started))
1584 caching = CACHESTATE_PLAY;
1586 // handle situation that we get no data on one stream
1587 if(m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0)
1589 if ((!m_dvdPlayerAudio.AcceptsData() && !m_CurrentVideo.started)
1590 || (!m_dvdPlayerVideo.AcceptsData() && !m_CurrentAudio.started))
1592 caching = CACHESTATE_DONE;
1597 if (caching == CACHESTATE_PVR)
1599 bool bGotAudio(m_pDemuxer->GetNrOfAudioStreams() > 0);
1600 bool bGotVideo(m_pDemuxer->GetNrOfVideoStreams() > 0);
1601 bool bAudioLevelOk(m_dvdPlayerAudio.GetLevel() > g_advancedSettings.m_iPVRMinAudioCacheLevel);
1602 bool bVideoLevelOk(m_dvdPlayerVideo.GetLevel() > g_advancedSettings.m_iPVRMinVideoCacheLevel);
1603 bool bAudioFull(!m_dvdPlayerAudio.AcceptsData());
1604 bool bVideoFull(!m_dvdPlayerVideo.AcceptsData());
1606 if (/* if all streams got at least g_advancedSettings.m_iPVRMinCacheLevel in their buffers, we're done */
1607 ((bGotVideo || bGotAudio) && (!bGotAudio || bAudioLevelOk) && (!bGotVideo || bVideoLevelOk)) ||
1608 /* or if one of the buffers is full */
1609 (bAudioFull || bVideoFull))
1611 CLog::Log(LOGDEBUG, "set caching from pvr to done. audio (%d) = %d. video (%d) = %d",
1612 bGotAudio, m_dvdPlayerAudio.GetLevel(),
1613 bGotVideo, m_dvdPlayerVideo.GetLevel());
1615 CFileItem currentItem(g_application.CurrentFileItem());
1616 if (currentItem.HasPVRChannelInfoTag())
1617 g_PVRManager.LoadCurrentChannelSettings();
1619 caching = CACHESTATE_DONE;
1623 /* ensure that automatically started players are stopped while caching */
1624 if (m_CurrentAudio.started)
1625 m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
1626 if (m_CurrentVideo.started)
1627 m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
1631 if(caching == CACHESTATE_PLAY)
1633 // if all enabled streams have started playing we are done
1634 if((m_CurrentVideo.id < 0 || !m_dvdPlayerVideo.IsStalled())
1635 && (m_CurrentAudio.id < 0 || !m_dvdPlayerAudio.IsStalled()))
1636 caching = CACHESTATE_DONE;
1639 if(m_caching != caching)
1640 SetCaching(caching);
1643 if(GetPlaySpeed() != DVD_PLAYSPEED_NORMAL && GetPlaySpeed() != DVD_PLAYSPEED_PAUSE)
1647 // this can't be done in menu
1648 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1651 else if (m_CurrentVideo.id >= 0
1652 && m_CurrentVideo.inited == true
1653 && m_SpeedState.lastpts != m_dvdPlayerVideo.GetCurrentPts()
1654 && m_SpeedState.lasttime != GetTime())
1656 m_SpeedState.lastpts = m_dvdPlayerVideo.GetCurrentPts();
1657 m_SpeedState.lasttime = GetTime();
1658 // check how much off clock video is when ff/rw:ing
1659 // a problem here is that seeking isn't very accurate
1660 // and since the clock will be resynced after seek
1661 // we might actually not really be playing at the wanted
1662 // speed. we'd need to have some way to not resync the clock
1663 // after a seek to remember timing. still need to handle
1664 // discontinuities somehow
1666 // when seeking, give the player a headstart to make sure
1667 // the time it takes to seek doesn't make a difference.
1669 error = m_clock.GetClock() - m_SpeedState.lastpts;
1670 error *= m_playSpeed / abs(m_playSpeed);
1672 if(error > DVD_MSEC_TO_TIME(1000))
1674 CLog::Log(LOGDEBUG, "CDVDPlayer::Process - Seeking to catch up");
1675 int64_t iTime = (int64_t)DVD_TIME_TO_MSEC(m_clock.GetClock() + m_State.time_offset + 500000.0 * m_playSpeed / DVD_PLAYSPEED_NORMAL);
1676 m_messenger.Put(new CDVDMsgPlayerSeek(iTime, (GetPlaySpeed() < 0), true, false, false, true));
1682 bool CDVDPlayer::CheckStartCaching(CCurrentStream& current)
1684 if(m_caching != CACHESTATE_DONE
1685 || m_playSpeed != DVD_PLAYSPEED_NORMAL)
1691 if((current.type == STREAM_AUDIO && m_dvdPlayerAudio.IsStalled())
1692 || (current.type == STREAM_VIDEO && m_dvdPlayerVideo.IsStalled()))
1694 if (CachePVRStream())
1696 if ((current.type == STREAM_AUDIO && current.started && m_dvdPlayerAudio.GetLevel() == 0) ||
1697 (current.type == STREAM_VIDEO && current.started && m_dvdPlayerVideo.GetLevel() == 0))
1699 CLog::Log(LOGDEBUG, "%s stream stalled. start buffering", current.type == STREAM_AUDIO ? "audio" : "video");
1700 SetCaching(CACHESTATE_PVR);
1705 // don't start caching if it's only a single stream that has run dry
1706 if(m_dvdPlayerAudio.GetLevel() > 50
1707 || m_dvdPlayerVideo.GetLevel() > 50)
1711 SetCaching(CACHESTATE_FULL);
1713 SetCaching(CACHESTATE_INIT);
1719 bool CDVDPlayer::CheckPlayerInit(CCurrentStream& current, unsigned int source)
1724 if(current.startpts != DVD_NOPTS_VALUE)
1726 if(current.dts == DVD_NOPTS_VALUE)
1728 CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
1732 if((current.startpts - current.dts) > DVD_SEC_TO_TIME(20))
1734 CLog::Log(LOGDEBUG, "%s - too far to decode before finishing seek", __FUNCTION__);
1735 if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
1736 m_CurrentAudio.startpts = current.dts;
1737 if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
1738 m_CurrentVideo.startpts = current.dts;
1739 if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
1740 m_CurrentSubtitle.startpts = current.dts;
1741 if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE)
1742 m_CurrentTeletext.startpts = current.dts;
1745 if(current.dts < current.startpts)
1747 CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source, current.dts, current.startpts);
1752 //If this is the first packet after a discontinuity, send it as a resync
1753 if (current.dts != DVD_NOPTS_VALUE)
1755 current.inited = true;
1756 current.startpts = current.dts;
1758 bool setclock = false;
1759 if(m_playSpeed == DVD_PLAYSPEED_NORMAL)
1761 if( source == DVDPLAYER_AUDIO)
1762 setclock = !m_CurrentVideo.inited;
1763 else if(source == DVDPLAYER_VIDEO)
1764 setclock = !m_CurrentAudio.inited;
1768 if(source == DVDPLAYER_VIDEO)
1772 double starttime = current.startpts;
1773 if(m_CurrentAudio.inited
1774 && m_CurrentAudio.startpts != DVD_NOPTS_VALUE
1775 && m_CurrentAudio.startpts < starttime)
1776 starttime = m_CurrentAudio.startpts;
1777 if(m_CurrentVideo.inited
1778 && m_CurrentVideo.startpts != DVD_NOPTS_VALUE
1779 && m_CurrentVideo.startpts < starttime)
1780 starttime = m_CurrentVideo.startpts;
1782 starttime = current.startpts - starttime;
1783 if(starttime > 0 && setclock)
1785 if(starttime > DVD_SEC_TO_TIME(2))
1786 CLog::Log(LOGWARNING, "CDVDPlayer::CheckPlayerInit(%d) - Ignoring too large delay of %f", source, starttime);
1788 SendPlayerMessage(new CDVDMsgDouble(CDVDMsg::GENERAL_DELAY, starttime), source);
1791 SendPlayerMessage(new CDVDMsgGeneralResync(current.dts, setclock), source);
1796 void CDVDPlayer::UpdateCorrection(DemuxPacket* pkt, double correction)
1798 if(pkt->dts != DVD_NOPTS_VALUE) pkt->dts -= correction;
1799 if(pkt->pts != DVD_NOPTS_VALUE) pkt->pts -= correction;
1802 void CDVDPlayer::UpdateTimestamps(CCurrentStream& current, DemuxPacket* pPacket)
1804 double dts = current.dts;
1805 /* update stored values */
1806 if(pPacket->dts != DVD_NOPTS_VALUE)
1808 else if(pPacket->pts != DVD_NOPTS_VALUE)
1811 /* calculate some average duration */
1812 if(pPacket->duration != DVD_NOPTS_VALUE)
1813 current.dur = pPacket->duration;
1814 else if(dts != DVD_NOPTS_VALUE && current.dts != DVD_NOPTS_VALUE)
1815 current.dur = 0.1 * (current.dur * 9 + (dts - current.dts));
1819 /* send a playback state structure periodically */
1820 if(current.dts_state == DVD_NOPTS_VALUE
1821 || abs(current.dts - current.dts_state) > DVD_MSEC_TO_TIME(200))
1823 current.dts_state = current.dts;
1826 // make sure we send no outdated state to a/v players
1828 SendPlayerMessage(new CDVDMsgType<SPlayerState>(CDVDMsg::PLAYER_DISPLAYTIME, m_StateInput), current.player);
1832 CSingleLock lock(m_StateSection);
1833 m_State = m_StateInput;
1838 static void UpdateLimits(double& minimum, double& maximum, double dts)
1840 if(dts == DVD_NOPTS_VALUE)
1842 if(minimum == DVD_NOPTS_VALUE || minimum > dts) minimum = dts;
1843 if(maximum == DVD_NOPTS_VALUE || maximum < dts) maximum = dts;
1846 void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
1848 if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
1851 if( pPacket->dts == DVD_NOPTS_VALUE || current.dts == DVD_NOPTS_VALUE)
1854 double mindts = DVD_NOPTS_VALUE, maxdts = DVD_NOPTS_VALUE;
1855 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts);
1856 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts);
1857 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts_end());
1858 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts_end());
1860 /* if we don't have max and min, we can't do anything more */
1861 if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
1864 double correction = 0.0;
1865 if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000))
1867 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - resync forward :%d, prev:%f, curr:%f, diff:%f"
1868 , current.type, current.dts, pPacket->dts, pPacket->dts - maxdts);
1869 correction = pPacket->dts - maxdts;
1872 /* if it's large scale jump, correct for it */
1873 if(pPacket->dts + DVD_MSEC_TO_TIME(100) < current.dts_end())
1875 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - resync backward :%d, prev:%f, curr:%f, diff:%f"
1876 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1877 correction = pPacket->dts - current.dts_end();
1879 else if(pPacket->dts < current.dts)
1881 CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - wrapback :%d, prev:%f, curr:%f, diff:%f"
1882 , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1885 if(correction != 0.0)
1887 /* disable detection on next packet on other stream to avoid ping pong-ing */
1888 if(m_CurrentAudio.player != current.player) m_CurrentAudio.dts = DVD_NOPTS_VALUE;
1889 if(m_CurrentVideo.player != current.player) m_CurrentVideo.dts = DVD_NOPTS_VALUE;
1891 m_offset_pts += correction;
1892 UpdateCorrection(pPacket, correction);
1896 bool CDVDPlayer::CheckSceneSkip(CCurrentStream& current)
1901 if(current.dts == DVD_NOPTS_VALUE)
1904 if(current.inited == false)
1908 return m_Edl.InCut(DVD_TIME_TO_MSEC(current.dts + m_offset_pts), &cut) && cut.action == CEdl::CUT;
1911 void CDVDPlayer::CheckAutoSceneSkip()
1917 * Check that there is an audio and video stream.
1919 if(m_CurrentAudio.id < 0
1920 || m_CurrentVideo.id < 0)
1924 * If there is a startpts defined for either the audio or video stream then dvdplayer is still
1925 * still decoding frames to get to the previously requested seek point.
1927 if(m_CurrentAudio.inited == false
1928 || m_CurrentVideo.inited == false)
1931 if(m_CurrentAudio.dts == DVD_NOPTS_VALUE
1932 || m_CurrentVideo.dts == DVD_NOPTS_VALUE)
1935 const int64_t clock = DVD_TIME_TO_MSEC(min(m_CurrentAudio.dts, m_CurrentVideo.dts) + m_offset_pts);
1938 if(!m_Edl.InCut(clock, &cut))
1941 if(cut.action == CEdl::CUT
1942 && !(cut.end == m_EdlAutoSkipMarkers.cut || cut.start == m_EdlAutoSkipMarkers.cut)) // To prevent looping if same cut again
1944 CLog::Log(LOGDEBUG, "%s - Clock in EDL cut [%s - %s]: %s. Automatically skipping over.",
1945 __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(),
1946 CEdl::MillisecondsToTimeString(cut.end).c_str(), CEdl::MillisecondsToTimeString(clock).c_str());
1948 * Seeking either goes to the start or the end of the cut depending on the play direction.
1950 int64_t seek = GetPlaySpeed() >= 0 ? cut.end : cut.start;
1952 * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
1954 m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, true, false, true, false, true));
1956 * Seek doesn't always work reliably. Last physical seek time is recorded to prevent looping
1957 * if there was an error with seeking and it landed somewhere unexpected, perhaps back in the
1958 * cut. The cut automatic skip marker is reset every 500ms allowing another attempt at the seek.
1960 m_EdlAutoSkipMarkers.cut = GetPlaySpeed() >= 0 ? cut.end : cut.start;
1962 else if(cut.action == CEdl::COMM_BREAK
1963 && GetPlaySpeed() >= 0
1964 && cut.start > m_EdlAutoSkipMarkers.commbreak_end)
1966 CLog::Log(LOGDEBUG, "%s - Clock in commercial break [%s - %s]: %s. Automatically skipping to end of commercial break (only done once per break)",
1967 __FUNCTION__, CEdl::MillisecondsToTimeString(cut.start).c_str(), CEdl::MillisecondsToTimeString(cut.end).c_str(),
1968 CEdl::MillisecondsToTimeString(clock).c_str());
1970 * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
1972 m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false, true));
1974 * Each commercial break is only skipped once so poorly detected commercial breaks can be
1975 * manually re-entered. Start and end are recorded to prevent looping and to allow seeking back
1976 * to the start of the commercial break if incorrectly flagged.
1978 m_EdlAutoSkipMarkers.commbreak_start = cut.start;
1979 m_EdlAutoSkipMarkers.commbreak_end = cut.end;
1980 m_EdlAutoSkipMarkers.seek_to_start = true; // Allow backwards Seek() to go directly to the start
1985 void CDVDPlayer::SynchronizeDemuxer(unsigned int timeout)
1987 if(IsCurrentThread())
1989 if(!m_messenger.IsInited())
1992 CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, 0);
1993 m_messenger.Put(message->Acquire());
1994 message->Wait(&m_bStop, 0);
1998 void CDVDPlayer::SynchronizePlayers(unsigned int sources)
2000 /* we need a big timeout as audio queue is about 8seconds for 2ch ac3 */
2001 const int timeout = 10*1000; // in milliseconds
2003 CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, sources);
2004 if (m_CurrentAudio.id >= 0)
2005 m_dvdPlayerAudio.SendMessage(message->Acquire());
2007 if (m_CurrentVideo.id >= 0)
2008 m_dvdPlayerVideo.SendMessage(message->Acquire());
2009 /* TODO - we have to rewrite the sync class, to not require
2010 all other players waiting for subtitle, should only
2012 if (m_CurrentSubtitle.id >= 0)
2013 m_dvdPlayerSubtitle.SendMessage(message->Acquire());
2018 void CDVDPlayer::SendPlayerMessage(CDVDMsg* pMsg, unsigned int target)
2020 if(target == DVDPLAYER_AUDIO)
2021 m_dvdPlayerAudio.SendMessage(pMsg);
2022 if(target == DVDPLAYER_VIDEO)
2023 m_dvdPlayerVideo.SendMessage(pMsg);
2024 if(target == DVDPLAYER_SUBTITLE)
2025 m_dvdPlayerSubtitle.SendMessage(pMsg);
2026 if(target == DVDPLAYER_TELETEXT)
2027 m_dvdPlayerTeletext.SendMessage(pMsg);
2030 void CDVDPlayer::OnExit()
2034 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit()");
2036 // set event to inform openfile something went wrong in case openfile is still waiting for this event
2037 SetCaching(CACHESTATE_DONE);
2039 // close each stream
2040 if (!m_bAbortRequest) CLog::Log(LOGNOTICE, "DVDPlayer: eof, waiting for queues to empty");
2041 if (m_CurrentAudio.id >= 0)
2043 CLog::Log(LOGNOTICE, "DVDPlayer: closing audio stream");
2044 CloseAudioStream(!m_bAbortRequest);
2046 if (m_CurrentVideo.id >= 0)
2048 CLog::Log(LOGNOTICE, "DVDPlayer: closing video stream");
2049 CloseVideoStream(!m_bAbortRequest);
2051 if (m_CurrentSubtitle.id >= 0)
2053 CLog::Log(LOGNOTICE, "DVDPlayer: closing subtitle stream");
2054 CloseSubtitleStream(!m_bAbortRequest);
2056 if (m_CurrentTeletext.id >= 0)
2058 CLog::Log(LOGNOTICE, "DVDPlayer: closing teletext stream");
2059 CloseTeletextStream(!m_bAbortRequest);
2061 // destroy the demuxer
2064 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting demuxer");
2069 if (m_pSubtitleDemuxer)
2071 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting subtitle demuxer");
2072 delete m_pSubtitleDemuxer;
2074 m_pSubtitleDemuxer = NULL;
2076 // destroy the inputstream
2079 CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting input stream");
2080 delete m_pInputStream;
2082 m_pInputStream = NULL;
2084 // clean up all selection streams
2085 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
2092 CLog::Log(LOGERROR, "%s - Exception thrown when trying to close down player, memory leak will follow", __FUNCTION__);
2093 m_pInputStream = NULL;
2098 // if we didn't stop playing, advance to the next item in xbmc's playlist
2099 if(m_PlayerOptions.identify == false)
2101 if (m_bAbortRequest)
2102 m_callback.OnPlayBackStopped();
2104 m_callback.OnPlayBackEnded();
2107 // set event to inform openfile something went wrong in case openfile is still waiting for this event
2111 void CDVDPlayer::HandleMessages()
2114 StreamLock lock(this);
2116 while (m_messenger.Get(&pMsg, 0) == MSGQ_OK)
2121 if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
2122 && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2124 CDVDMsgPlayerSeek &msg(*((CDVDMsgPlayerSeek*)pMsg));
2126 if (!m_State.canseek)
2132 if(!msg.GetTrickPlay())
2134 g_infoManager.SetDisplayAfterSeek(100000);
2136 SetCaching(CACHESTATE_FLUSH);
2139 double start = DVD_NOPTS_VALUE;
2141 int time = msg.GetRestore() ? (int)m_Edl.RestoreCutTime(msg.GetTime()) : msg.GetTime();
2143 // if input streams doesn't support seektime we must convert back to clock
2144 if(dynamic_cast<CDVDInputStream::ISeekTime*>(m_pInputStream) == NULL)
2145 time -= DVD_TIME_TO_MSEC(m_State.time_offset - m_offset_pts);
2147 CLog::Log(LOGDEBUG, "demuxer seek to: %d", time);
2148 if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start))
2150 CLog::Log(LOGDEBUG, "demuxer seek to: %d, success", time);
2151 if(m_pSubtitleDemuxer)
2153 if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward()))
2154 CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: %d, success", time);
2156 // dts after successful seek
2157 if (m_StateInput.time_src == ETIMESOURCE_CLOCK && start == DVD_NOPTS_VALUE)
2158 m_StateInput.dts = DVD_MSEC_TO_TIME(time);
2160 m_StateInput.dts = start;
2162 FlushBuffers(!msg.GetFlush(), start, msg.GetAccurate());
2165 CLog::Log(LOGWARNING, "error while seeking");
2167 // set flag to indicate we have finished a seeking request
2168 if(!msg.GetTrickPlay())
2169 g_infoManager.SetDisplayAfterSeek();
2171 // dvd's will issue a HOP_CHANNEL that we need to skip
2172 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2173 m_dvd.state = DVDSTATE_SEEK;
2175 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0
2176 && m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2178 g_infoManager.SetDisplayAfterSeek(100000);
2179 SetCaching(CACHESTATE_FLUSH);
2181 CDVDMsgPlayerSeekChapter &msg(*((CDVDMsgPlayerSeekChapter*)pMsg));
2182 double start = DVD_NOPTS_VALUE;
2184 // This should always be the case.
2185 if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
2187 FlushBuffers(false, start, true);
2188 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2191 g_infoManager.SetDisplayAfterSeek();
2193 else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
2195 m_CurrentAudio.stream = NULL;
2196 m_CurrentVideo.stream = NULL;
2197 m_CurrentSubtitle.stream = NULL;
2199 // we need to reset the demuxer, probably because the streams have changed
2201 m_pDemuxer->Reset();
2202 if(m_pSubtitleDemuxer)
2203 m_pSubtitleDemuxer->Reset();
2205 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
2207 CDVDMsgPlayerSetAudioStream* pMsg2 = (CDVDMsgPlayerSetAudioStream*)pMsg;
2209 SelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
2210 if(st.source != STREAM_SOURCE_NONE)
2212 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2214 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2215 if(pStream->SetActiveAudioStream(st.id))
2217 m_dvd.iSelectedAudioStream = -1;
2218 CloseAudioStream(false);
2219 m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true, true, true));
2224 CloseAudioStream(false);
2225 OpenAudioStream(st.id, st.source);
2226 AdaptForcedSubtitles();
2227 m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true, true, true));
2231 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
2233 CDVDMsgPlayerSetSubtitleStream* pMsg2 = (CDVDMsgPlayerSetSubtitleStream*)pMsg;
2235 SelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
2236 if(st.source != STREAM_SOURCE_NONE)
2238 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2240 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2241 if(pStream->SetActiveSubtitleStream(st.id))
2243 m_dvd.iSelectedSPUStream = -1;
2244 CloseSubtitleStream(false);
2249 CloseSubtitleStream(false);
2250 OpenSubtitleStream(st.id, st.source);
2254 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
2256 CDVDMsgBool* pValue = (CDVDMsgBool*)pMsg;
2257 SetSubtitleVisibleInternal(pValue->m_value);
2259 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
2261 g_infoManager.SetDisplayAfterSeek(100000);
2262 SetCaching(CACHESTATE_FLUSH);
2264 CDVDMsgPlayerSetState* pMsgPlayerSetState = (CDVDMsgPlayerSetState*)pMsg;
2266 if (CDVDInputStream::IMenus* ptr = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
2268 if(ptr->SetState(pMsgPlayerSetState->GetState()))
2270 m_dvd.state = DVDSTATE_NORMAL;
2271 m_dvd.iDVDStillStartTime = 0;
2272 m_dvd.iDVDStillTime = 0;
2276 g_infoManager.SetDisplayAfterSeek();
2278 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD))
2280 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2282 input->Record(*(CDVDMsgBool*)pMsg);
2284 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
2286 FlushBuffers(false);
2288 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
2290 int speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
2292 // correct our current clock, as it would start going wrong otherwise
2293 if(m_State.timestamp > 0)
2296 offset = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2297 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2298 if(offset > 1000) offset = 1000;
2299 if(offset < -1000) offset = -1000;
2300 m_State.time += DVD_TIME_TO_MSEC(offset);
2301 m_State.timestamp = CDVDClock::GetAbsoluteClock();
2304 if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed)
2305 m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL);
2307 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && speed != m_playSpeed)
2309 CDVDInputStreamPVRManager* pvrinputstream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream);
2310 pvrinputstream->Pause( speed == 0 );
2313 // if playspeed is different then DVD_PLAYSPEED_NORMAL or DVD_PLAYSPEED_PAUSE
2314 // audioplayer, stops outputing audio to audiorendere, but still tries to
2315 // sleep an correct amount for each packet
2316 // videoplayer just plays faster after the clock speed has been increased
2318 // 2. skip frames and adjust their pts or the clock
2319 m_playSpeed = speed;
2320 m_caching = CACHESTATE_DONE;
2321 m_clock.SetSpeed(speed);
2322 m_dvdPlayerAudio.SetSpeed(speed);
2323 m_dvdPlayerVideo.SetSpeed(speed);
2325 // We can't pause demuxer until our buffers are full. Doing so will result in continued
2326 // calls to Read() which may then block indefinitely (CDVDInputStreamRTMP for example).
2329 m_DemuxerPausePending = (speed == DVD_PLAYSPEED_PAUSE);
2330 if (!m_DemuxerPausePending)
2331 m_pDemuxer->SetSpeed(speed);
2334 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) == 0)
2336 FlushBuffers(false);
2337 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2338 if(input && input->SelectChannelByNumber(static_cast<CDVDMsgInt*>(pMsg)->m_value))
2340 SAFE_DELETE(m_pDemuxer);
2343 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2344 CApplicationMessenger::Get().MediaStop(false);
2347 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0)
2349 FlushBuffers(false);
2350 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2351 if(input && input->SelectChannel(static_cast<CDVDMsgType <CPVRChannel> *>(pMsg)->m_value))
2353 SAFE_DELETE(m_pDemuxer);
2356 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2357 CApplicationMessenger::Get().MediaStop(false);
2360 else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV))
2362 CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
2365 bool bSwitchSuccessful(false);
2366 bool bShowPreview(CSettings::Get().GetInt("pvrplayback.channelentrytimeout") > 0);
2370 g_infoManager.SetDisplayAfterSeek(100000);
2371 FlushBuffers(false);
2374 if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
2375 bSwitchSuccessful = input->NextChannel(bShowPreview);
2377 bSwitchSuccessful = input->PrevChannel(bShowPreview);
2379 if(bSwitchSuccessful)
2383 UpdateApplication(0);
2384 m_ChannelEntryTimeOut.Set(CSettings::Get().GetInt("pvrplayback.channelentrytimeout"));
2388 m_ChannelEntryTimeOut.SetInfinite();
2389 SAFE_DELETE(m_pDemuxer);
2391 g_infoManager.SetDisplayAfterSeek();
2396 CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__);
2397 CApplicationMessenger::Get().MediaStop(false);
2401 else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
2402 OnAction(((CDVDMsgType<CAction>*)pMsg)->m_value);
2403 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
2405 int player = ((CDVDMsgInt*)pMsg)->m_value;
2406 if(player == DVDPLAYER_AUDIO)
2407 m_CurrentAudio.started = true;
2408 if(player == DVDPLAYER_VIDEO)
2409 m_CurrentVideo.started = true;
2410 CLog::Log(LOGDEBUG, "CDVDPlayer::HandleMessages - player started %d", player);
2412 else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME))
2414 CDVDPlayer::SPlayerState& state = ((CDVDMsgType<CDVDPlayer::SPlayerState>*)pMsg)->m_value;
2416 CSingleLock lock(m_StateSection);
2417 /* prioritize data from video player, but only accept data *
2418 * after it has been started to avoid race conditions after seeks */
2419 if(m_CurrentVideo.started)
2421 if(state.player == DVDPLAYER_VIDEO)
2424 else if(m_CurrentAudio.started)
2426 if(state.player == DVDPLAYER_AUDIO)
2433 CLog::Log(LOGERROR, "%s - Exception thrown when handling message", __FUNCTION__);
2441 void CDVDPlayer::SetCaching(ECacheState state)
2443 if(state == CACHESTATE_FLUSH)
2445 double level, delay, offset;
2446 if(GetCachingTimes(level, delay, offset))
2447 state = CACHESTATE_FULL;
2449 state = CACHESTATE_INIT;
2452 if(m_caching == state)
2455 CLog::Log(LOGDEBUG, "CDVDPlayer::SetCaching - caching state %d", state);
2456 if(state == CACHESTATE_FULL
2457 || state == CACHESTATE_INIT
2458 || state == CACHESTATE_PVR)
2460 m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
2461 m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
2462 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2463 m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
2464 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2466 if (state == CACHESTATE_PVR)
2467 m_pInputStream->ResetScanTimeout((unsigned int) CSettings::Get().GetInt("pvrplayback.scantime") * 1000);
2470 if(state == CACHESTATE_PLAY
2471 ||(state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY))
2473 m_clock.SetSpeed(m_playSpeed);
2474 m_dvdPlayerAudio.SetSpeed(m_playSpeed);
2475 m_dvdPlayerVideo.SetSpeed(m_playSpeed);
2476 m_pInputStream->ResetScanTimeout(0);
2481 void CDVDPlayer::SetPlaySpeed(int speed)
2483 m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed));
2484 m_dvdPlayerAudio.SetSpeed(speed);
2485 m_dvdPlayerVideo.SetSpeed(speed);
2486 SynchronizeDemuxer(100);
2489 bool CDVDPlayer::CanPause()
2491 CSingleLock lock(m_StateSection);
2492 return m_State.canpause;
2495 void CDVDPlayer::Pause()
2497 CSingleLock lock(m_StateSection);
2498 if (!m_State.canpause)
2502 if(m_playSpeed != DVD_PLAYSPEED_PAUSE && (m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR))
2504 SetCaching(CACHESTATE_DONE);
2508 // return to normal speed if it was paused before, pause otherwise
2509 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
2511 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2512 m_callback.OnPlayBackResumed();
2516 SetPlaySpeed(DVD_PLAYSPEED_PAUSE);
2517 m_callback.OnPlayBackPaused();
2521 bool CDVDPlayer::IsPaused() const
2523 return m_playSpeed == DVD_PLAYSPEED_PAUSE || m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR;
2526 bool CDVDPlayer::HasVideo() const
2531 bool CDVDPlayer::HasAudio() const
2536 bool CDVDPlayer::IsPassthrough() const
2538 return m_dvdPlayerAudio.IsPassthrough();
2541 bool CDVDPlayer::CanSeek()
2543 CSingleLock lock(m_StateSection);
2544 return m_State.canseek;
2547 void CDVDPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
2550 // sadly this doesn't work for now, audio player must
2551 // drop packets at the same rate as we play frames
2552 if( m_playSpeed == DVD_PLAYSPEED_PAUSE && bPlus && !bLargeStep)
2554 m_dvdPlayerVideo.StepFrame();
2558 if (!m_State.canseek)
2561 if (bLargeStep && bChapterOverride && GetChapter() > 0)
2565 SeekChapter(GetChapter() - 1);
2568 else if (GetChapter() < GetChapterCount())
2570 SeekChapter(GetChapter() + 1);
2576 if (g_advancedSettings.m_videoUseTimeSeeking && GetTotalTime() > 2000*g_advancedSettings.m_videoTimeSeekForwardBig)
2579 seek = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig;
2581 seek = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward;
2589 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig;
2591 percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward;
2592 seek = (int64_t)(GetTotalTimeInMsec()*(GetPercentage()+percent)/100);
2595 bool restore = true;
2599 * Alter the standard seek position based on whether any commercial breaks have been
2600 * automatically skipped.
2602 const int clock = DVD_TIME_TO_MSEC(m_clock.GetClock());
2604 * If a large backwards seek occurs within 10 seconds of the end of the last automated
2605 * commercial skip, then seek back to the start of the commercial break under the assumption
2606 * it was flagged incorrectly. 10 seconds grace period is allowed in case the watcher has to
2607 * fumble around finding the remote. Only happens once per commercial break.
2609 * Small skip does not trigger this in case the start of the commercial break was in fact fine
2610 * but it skipped too far into the program. In that case small skip backwards behaves as normal.
2612 if (!bPlus && bLargeStep
2613 && m_EdlAutoSkipMarkers.seek_to_start
2614 && clock >= m_EdlAutoSkipMarkers.commbreak_end
2615 && clock <= m_EdlAutoSkipMarkers.commbreak_end + 10*1000) // Only if within 10 seconds of the end (in msec)
2617 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).",
2618 __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
2619 CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
2620 seek = m_EdlAutoSkipMarkers.commbreak_start;
2622 m_EdlAutoSkipMarkers.seek_to_start = false; // So this will only happen within the 10 second grace period once.
2625 * If big skip forward within the last "reverted" commercial break, seek to the end of the
2626 * commercial break under the assumption that the break was incorrectly flagged and playback has
2627 * now reached the actual start of the commercial break. Assume that the end is flagged more
2628 * correctly than the landing point for a standard big skip (ends seem to be flagged more
2629 * accurately than the start).
2631 else if (bPlus && bLargeStep
2632 && clock >= m_EdlAutoSkipMarkers.commbreak_start
2633 && clock <= m_EdlAutoSkipMarkers.commbreak_end)
2635 CLog::Log(LOGDEBUG, "%s - Seeking to end of previously skipped commercial break [%s - %s] as big forwards skip activated within the break.",
2636 __FUNCTION__, CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_start).c_str(),
2637 CEdl::MillisecondsToTimeString(m_EdlAutoSkipMarkers.commbreak_end).c_str());
2638 seek = m_EdlAutoSkipMarkers.commbreak_end;
2643 int64_t time = GetTime();
2644 if(g_application.CurrentFileItem().IsStack()
2645 && (seek > GetTotalTimeInMsec() || seek < 0))
2647 g_application.SeekTime((seek - time) * 0.001 + g_application.GetTime());
2648 // warning, don't access any dvdplayer variables here as
2649 // the dvdplayer object may have been destroyed
2653 m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, !bPlus, true, false, restore));
2654 SynchronizeDemuxer(100);
2655 if (seek < 0) seek = 0;
2656 m_callback.OnPlayBackSeek((int)seek, (int)(seek - time));
2659 bool CDVDPlayer::SeekScene(bool bPlus)
2661 if (!m_Edl.HasSceneMarker())
2665 * There is a 5 second grace period applied when seeking for scenes backwards. If there is no
2666 * grace period applied it is impossible to go backwards past a scene marker.
2668 int64_t clock = GetTime();
2669 if (!bPlus && clock > 5 * 1000) // 5 seconds
2672 int64_t iScenemarker;
2673 if (m_Edl.GetNextSceneMarker(bPlus, clock, &iScenemarker))
2676 * Seeking is flushed and inaccurate, just like Seek()
2678 m_messenger.Put(new CDVDMsgPlayerSeek((int)iScenemarker, !bPlus, true, false, false));
2679 SynchronizeDemuxer(100);
2685 void CDVDPlayer::GetAudioInfo(CStdString& strAudioInfo)
2687 { CSingleLock lock(m_StateSection);
2688 strAudioInfo = StringUtils::Format("D(%s)", m_StateInput.demux_audio.c_str());
2690 strAudioInfo += StringUtils::Format("\nP(%s)", m_dvdPlayerAudio.GetPlayerInfo().c_str());
2693 void CDVDPlayer::GetVideoInfo(CStdString& strVideoInfo)
2695 { CSingleLock lock(m_StateSection);
2696 strVideoInfo = StringUtils::Format("D(%s)", m_StateInput.demux_video.c_str());
2698 strVideoInfo += StringUtils::Format("\nP(%s)", m_dvdPlayerVideo.GetPlayerInfo().c_str());
2701 void CDVDPlayer::GetGeneralInfo(CStdString& strGeneralInfo)
2705 double dDelay = m_dvdPlayerVideo.GetDelay() / DVD_TIME_BASE - g_renderManager.GetDisplayLatency();
2707 double apts = m_dvdPlayerAudio.GetCurrentPts();
2708 double vpts = m_dvdPlayerVideo.GetCurrentPts();
2711 if( apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE )
2712 dDiff = (apts - vpts) / DVD_TIME_BASE;
2715 strEDL += StringUtils::Format(", edl:%s", m_Edl.GetInfo().c_str());
2718 CSingleLock lock(m_StateSection);
2719 if(m_StateInput.cache_bytes >= 0)
2721 strBuf += StringUtils::Format(" cache:%s %2.0f%%"
2722 , StringUtils::SizeToString(m_State.cache_bytes).c_str()
2723 , m_State.cache_level * 100);
2724 if(m_playSpeed == 0 || m_caching == CACHESTATE_FULL)
2725 strBuf += StringUtils::Format(" %d sec", DVD_TIME_TO_SEC(m_State.cache_delay));
2728 strGeneralInfo = StringUtils::Format("C( ad:% 6.3f, a/v:% 6.3f%s, dcpu:%2i%% acpu:%2i%% vcpu:%2i%%%s )"
2732 , (int)(CThread::GetRelativeUsage()*100)
2733 , (int)(m_dvdPlayerAudio.GetRelativeUsage()*100)
2734 , (int)(m_dvdPlayerVideo.GetRelativeUsage()*100)
2740 void CDVDPlayer::SeekPercentage(float iPercent)
2742 int64_t iTotalTime = GetTotalTimeInMsec();
2747 SeekTime((int64_t)(iTotalTime * iPercent / 100));
2750 float CDVDPlayer::GetPercentage()
2752 int64_t iTotalTime = GetTotalTimeInMsec();
2757 return GetTime() * 100 / (float)iTotalTime;
2760 float CDVDPlayer::GetCachePercentage()
2762 CSingleLock lock(m_StateSection);
2763 return m_StateInput.cache_offset * 100; // NOTE: Percentage returned is relative
2766 void CDVDPlayer::SetAVDelay(float fValue)
2768 m_dvdPlayerVideo.SetDelay( (fValue * DVD_TIME_BASE) ) ;
2771 float CDVDPlayer::GetAVDelay()
2773 return m_dvdPlayerVideo.GetDelay() / (float)DVD_TIME_BASE;
2776 void CDVDPlayer::SetSubTitleDelay(float fValue)
2778 m_dvdPlayerVideo.SetSubtitleDelay(-fValue * DVD_TIME_BASE);
2781 float CDVDPlayer::GetSubTitleDelay()
2783 return -m_dvdPlayerVideo.GetSubtitleDelay() / DVD_TIME_BASE;
2786 // priority: 1: libdvdnav, 2: external subtitles, 3: muxed subtitles
2787 int CDVDPlayer::GetSubtitleCount()
2789 return m_SelectionStreams.Count(STREAM_SUBTITLE);
2792 int CDVDPlayer::GetSubtitle()
2794 return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, *this);
2797 void CDVDPlayer::GetSubtitleStreamInfo(int index, SPlayerSubtitleStreamInfo &info)
2799 if (index < 0 || index > (int) GetSubtitleCount() - 1)
2802 SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
2803 if(s.name.length() > 0)
2806 if(s.type == STREAM_NONE)
2807 info.name += "(Invalid)";
2809 info.language = s.language;
2812 void CDVDPlayer::SetSubtitle(int iStream)
2814 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream = iStream;
2815 m_messenger.Put(new CDVDMsgPlayerSetSubtitleStream(iStream));
2818 bool CDVDPlayer::GetSubtitleVisible()
2820 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2822 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2823 if(pStream->IsInMenu())
2824 return CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn;
2826 return pStream->IsSubtitleStreamEnabled();
2829 return m_dvdPlayerVideo.IsSubtitleEnabled();
2832 void CDVDPlayer::SetSubtitleVisible(bool bVisible)
2834 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn = bVisible;
2835 m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
2838 void CDVDPlayer::SetSubtitleVisibleInternal(bool bVisible)
2840 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn = bVisible;
2841 m_dvdPlayerVideo.EnableSubtitle(bVisible);
2843 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2844 static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->EnableSubtitleStream(bVisible);
2847 int CDVDPlayer::GetAudioStreamCount()
2849 return m_SelectionStreams.Count(STREAM_AUDIO);
2852 int CDVDPlayer::GetAudioStream()
2854 return m_SelectionStreams.IndexOf(STREAM_AUDIO, *this);
2857 void CDVDPlayer::SetAudioStream(int iStream)
2859 CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream = iStream;
2860 m_messenger.Put(new CDVDMsgPlayerSetAudioStream(iStream));
2861 SynchronizeDemuxer(100);
2864 TextCacheStruct_t* CDVDPlayer::GetTeletextCache()
2866 if (m_CurrentTeletext.id < 0)
2869 return m_dvdPlayerTeletext.GetTeletextCache();
2872 void CDVDPlayer::LoadPage(int p, int sp, unsigned char* buffer)
2874 if (m_CurrentTeletext.id < 0)
2877 return m_dvdPlayerTeletext.LoadPage(p, sp, buffer);
2880 void CDVDPlayer::SeekTime(int64_t iTime)
2882 int seekOffset = (int)(iTime - GetTime());
2883 m_messenger.Put(new CDVDMsgPlayerSeek((int)iTime, true, true, true));
2884 SynchronizeDemuxer(100);
2885 m_callback.OnPlayBackSeek((int)iTime, seekOffset);
2888 // return the time in milliseconds
2889 int64_t CDVDPlayer::GetTime()
2891 CSingleLock lock(m_StateSection);
2893 const double limit = DVD_MSEC_TO_TIME(200);
2894 if(m_State.timestamp > 0)
2896 offset = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2897 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2898 if(offset > limit) offset = limit;
2899 if(offset < -limit) offset = -limit;
2901 return llrint(m_State.time + DVD_TIME_TO_MSEC(offset));
2904 // return length in msec
2905 int64_t CDVDPlayer::GetTotalTimeInMsec()
2907 CSingleLock lock(m_StateSection);
2908 return llrint(m_State.time_total);
2911 // return length in seconds.. this should be changed to return in milleseconds throughout xbmc
2912 int64_t CDVDPlayer::GetTotalTime()
2914 return GetTotalTimeInMsec();
2917 void CDVDPlayer::ToFFRW(int iSpeed)
2919 // can't rewind in menu as seeking isn't possible
2921 if (iSpeed < 0 && IsInMenu()) return;
2922 SetPlaySpeed(iSpeed * DVD_PLAYSPEED_NORMAL);
2925 bool CDVDPlayer::OpenAudioStream(int iStream, int source, bool reset)
2927 CLog::Log(LOGNOTICE, "Opening audio stream: %i source: %i", iStream, source);
2932 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2933 if (!pStream || pStream->disabled)
2936 if( m_CurrentAudio.id < 0 && m_CurrentVideo.id >= 0 )
2938 // up until now we wheren't playing audio, but we did play video
2939 // this will change what is used to sync the dvdclock.
2940 // since the new audio data doesn't have to have any relation
2941 // to the current video data in the packet que, we have to
2942 // wait for it to empty
2944 // this happens if a new cell has audio data, but previous didn't
2945 // and both have video data
2947 SynchronizePlayers(SYNCSOURCE_AUDIO);
2950 CDVDStreamInfo hint(*pStream, true);
2952 if(m_CurrentAudio.id < 0
2953 || m_CurrentAudio.hint != hint)
2955 if (!m_dvdPlayerAudio.OpenStream( hint ))
2957 /* mark stream as disabled, to disallaw further attempts*/
2958 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2959 pStream->disabled = true;
2960 pStream->SetDiscard(AVDISCARD_ALL);
2965 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
2967 /* store information about stream */
2968 m_CurrentAudio.id = iStream;
2969 m_CurrentAudio.source = source;
2970 m_CurrentAudio.hint = hint;
2971 m_CurrentAudio.stream = (void*)pStream;
2972 m_CurrentAudio.started = false;
2975 /* we are potentially going to be waiting on this */
2976 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
2978 /* audio normally won't consume full cpu, so let it have prio */
2979 m_dvdPlayerAudio.SetPriority(GetPriority()+1);
2980 CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream = GetAudioStream();
2984 bool CDVDPlayer::OpenVideoStream(int iStream, int source, bool reset)
2986 CLog::Log(LOGNOTICE, "Opening video stream: %i source: %i", iStream, source);
2991 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2992 if(!pStream || pStream->disabled)
2994 pStream->SetDiscard(AVDISCARD_NONE);
2996 CDVDStreamInfo hint(*pStream, true);
2998 if( m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) )
3000 /* set aspect ratio as requested by navigator for dvd's */
3001 float aspect = static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->GetVideoAspectRatio();
3004 hint.aspect = aspect;
3005 hint.forced_aspect = true;
3007 hint.software = true;
3010 boost::shared_ptr<CPVRClient> client;
3011 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) &&
3012 pStream->type == STREAM_VIDEO &&
3013 g_PVRClients->GetPlayingClient(client) && client->HandlesDemuxing())
3015 // set the fps in hints
3016 const CDemuxStreamVideo *stream = static_cast<const CDemuxStreamVideo*>(pStream);
3017 hint.fpsrate = stream->iFpsRate;
3018 hint.fpsscale = stream->iFpsScale;
3021 CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3022 if(pMenus && pMenus->IsInMenu())
3025 if (hint.stereo_mode.empty())
3026 hint.stereo_mode = CStereoscopicsManager::Get().DetectStereoModeByString(m_filename);
3028 if(m_CurrentVideo.id < 0
3029 || m_CurrentVideo.hint != hint)
3031 if (!m_dvdPlayerVideo.OpenStream(hint))
3033 /* mark stream as disabled, to disallaw further attempts */
3034 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
3035 pStream->disabled = true;
3036 pStream->SetDiscard(AVDISCARD_ALL);
3041 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3043 /* store information about stream */
3044 m_CurrentVideo.id = iStream;
3045 m_CurrentVideo.source = source;
3046 m_CurrentVideo.hint = hint;
3047 m_CurrentVideo.stream = (void*)pStream;
3048 m_CurrentVideo.started = false;
3051 /* we are potentially going to be waiting on this */
3052 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
3054 #if defined(TARGET_DARWIN)
3055 // Apple thread scheduler works a little different than Linux. It
3056 // will favor OS GUI side and can cause DVDPlayerVideo to miss frame
3057 // updates when the OS gets busy. Apple's recomended method is to
3058 // elevate time critical threads to SCHED_RR and OSX does this for
3059 // the CoreAudio audio device handler thread. We do the same for
3060 // the DVDPlayerVideo thread so it can run to sleep without getting
3061 // swapped out by a busy OS.
3062 m_dvdPlayerVideo.SetPriority(GetSchedRRPriority());
3064 /* use same priority for video thread as demuxing thread, as */
3065 /* otherwise demuxer will starve if video consumes the full cpu */
3066 m_dvdPlayerVideo.SetPriority(GetPriority());
3072 bool CDVDPlayer::OpenSubtitleStream(int iStream, int source)
3074 CLog::Log(LOGNOTICE, "Opening Subtitle stream: %i source: %i", iStream, source);
3076 CDemuxStream* pStream = NULL;
3077 std::string filename;
3078 CDVDStreamInfo hint;
3080 if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
3082 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
3085 SelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
3087 if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename)
3089 CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str());
3090 auto_ptr<CDVDDemuxVobsub> demux(new CDVDDemuxVobsub());
3091 if(!demux->Open(st.filename, st.filename2))
3093 m_pSubtitleDemuxer = demux.release();
3096 pStream = m_pSubtitleDemuxer->GetStream(iStream);
3097 if(!pStream || pStream->disabled)
3099 pStream->SetDiscard(AVDISCARD_NONE);
3100 double pts = m_dvdPlayerVideo.GetCurrentPts();
3101 if(pts == DVD_NOPTS_VALUE)
3102 pts = m_CurrentVideo.dts;
3103 if(pts == DVD_NOPTS_VALUE)
3105 pts += m_offset_pts;
3106 m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE));
3108 hint.Assign(*pStream, true);
3110 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
3112 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
3115 filename = m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename;
3118 hint.fpsscale = m_CurrentVideo.hint.fpsscale;
3119 hint.fpsrate = m_CurrentVideo.hint.fpsrate;
3125 pStream = m_pDemuxer->GetStream(iStream);
3126 if(!pStream || pStream->disabled)
3128 pStream->SetDiscard(AVDISCARD_NONE);
3130 hint.Assign(*pStream, true);
3132 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3136 if(m_CurrentSubtitle.id < 0
3137 || m_CurrentSubtitle.hint != hint)
3139 if(m_CurrentSubtitle.id >= 0)
3141 CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream");
3142 CloseSubtitleStream(false);
3145 if(!m_dvdPlayerSubtitle.OpenStream(hint, filename))
3147 CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
3150 pStream->disabled = true;
3151 pStream->SetDiscard(AVDISCARD_ALL);
3157 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3159 m_CurrentSubtitle.id = iStream;
3160 m_CurrentSubtitle.source = source;
3161 m_CurrentSubtitle.hint = hint;
3162 m_CurrentSubtitle.stream = (void*)pStream;
3163 m_CurrentSubtitle.started = false;
3165 CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream = GetSubtitle();
3169 bool CDVDPlayer::AdaptForcedSubtitles()
3172 SelectionStream ss = m_SelectionStreams.Get(STREAM_SUBTITLE, GetSubtitle());
3173 if (ss.flags & CDemuxStream::FLAG_FORCED || !GetSubtitleVisible())
3175 SelectionStream as = m_SelectionStreams.Get(STREAM_AUDIO, GetAudioStream());
3176 SelectionStreams streams = m_SelectionStreams.Get(STREAM_SUBTITLE);
3178 for(SelectionStreams::iterator it = streams.begin(); it != streams.end() && !valid; ++it)
3180 if (it->flags & CDemuxStream::FLAG_FORCED && g_LangCodeExpander.CompareLangCodes(it->language, as.language))
3182 if(OpenSubtitleStream(it->id, it->source))
3185 SetSubtitleVisibleInternal(true);
3191 CloseSubtitleStream(true);
3192 SetSubtitleVisibleInternal(false);
3198 bool CDVDPlayer::OpenTeletextStream(int iStream, int source)
3203 CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
3204 if(!pStream || pStream->disabled)
3207 CDVDStreamInfo hint(*pStream, true);
3209 if (!m_dvdPlayerTeletext.CheckStream(hint))
3212 CLog::Log(LOGNOTICE, "Opening teletext stream: %i source: %i", iStream, source);
3214 if(m_CurrentTeletext.id < 0
3215 || m_CurrentTeletext.hint != hint)
3217 if(m_CurrentTeletext.id >= 0)
3219 CLog::Log(LOGDEBUG, " - teletext codecs hints have changed, must close previous stream");
3220 CloseTeletextStream(true);
3223 if (!m_dvdPlayerTeletext.OpenStream(hint))
3225 /* mark stream as disabled, to disallaw further attempts*/
3226 CLog::Log(LOGWARNING, "%s - Unsupported teletext stream %d. Stream disabled.", __FUNCTION__, iStream);
3227 pStream->disabled = true;
3228 pStream->SetDiscard(AVDISCARD_ALL);
3233 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3235 /* store information about stream */
3236 m_CurrentTeletext.id = iStream;
3237 m_CurrentTeletext.source = source;
3238 m_CurrentTeletext.hint = hint;
3239 m_CurrentTeletext.stream = (void*)pStream;
3240 m_CurrentTeletext.started = false;
3245 bool CDVDPlayer::CloseAudioStream(bool bWaitForBuffers)
3247 if (m_CurrentAudio.id < 0)
3250 CLog::Log(LOGNOTICE, "Closing audio stream");
3253 SetCaching(CACHESTATE_DONE);
3255 m_dvdPlayerAudio.CloseStream(bWaitForBuffers);
3257 m_CurrentAudio.Clear();
3261 bool CDVDPlayer::CloseVideoStream(bool bWaitForBuffers)
3263 if (m_CurrentVideo.id < 0)
3266 CLog::Log(LOGNOTICE, "Closing video stream");
3269 SetCaching(CACHESTATE_DONE);
3271 m_dvdPlayerVideo.CloseStream(bWaitForBuffers);
3273 m_CurrentVideo.Clear();
3277 bool CDVDPlayer::CloseSubtitleStream(bool bKeepOverlays)
3279 if (m_CurrentSubtitle.id < 0)
3282 CLog::Log(LOGNOTICE, "Closing subtitle stream");
3284 m_dvdPlayerSubtitle.CloseStream(!bKeepOverlays);
3286 m_CurrentSubtitle.Clear();
3290 bool CDVDPlayer::CloseTeletextStream(bool bWaitForBuffers)
3292 if (m_CurrentTeletext.id < 0)
3295 CLog::Log(LOGNOTICE, "Closing teletext stream");
3298 SetCaching(CACHESTATE_DONE);
3300 m_dvdPlayerTeletext.CloseStream(bWaitForBuffers);
3302 m_CurrentTeletext.Clear();
3306 void CDVDPlayer::FlushBuffers(bool queued, double pts, bool accurate)
3312 startpts = DVD_NOPTS_VALUE;
3314 /* call with demuxer pts */
3315 if(startpts != DVD_NOPTS_VALUE)
3316 startpts -= m_offset_pts;
3318 m_CurrentAudio.inited = false;
3319 m_CurrentAudio.dts = DVD_NOPTS_VALUE;
3320 m_CurrentAudio.startpts = startpts;
3322 m_CurrentVideo.inited = false;
3323 m_CurrentVideo.dts = DVD_NOPTS_VALUE;
3324 m_CurrentVideo.startpts = startpts;
3326 m_CurrentSubtitle.inited = false;
3327 m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
3328 m_CurrentSubtitle.startpts = startpts;
3330 m_CurrentTeletext.inited = false;
3331 m_CurrentTeletext.dts = DVD_NOPTS_VALUE;
3332 m_CurrentTeletext.startpts = startpts;
3336 m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3337 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3338 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
3339 m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3340 m_dvdPlayerTeletext.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
3341 SynchronizePlayers(SYNCSOURCE_ALL);
3345 m_dvdPlayerAudio.Flush();
3346 m_dvdPlayerVideo.Flush();
3347 m_dvdPlayerSubtitle.Flush();
3348 m_dvdPlayerTeletext.Flush();
3350 // clear subtitle and menu overlays
3351 m_overlayContainer.Clear();
3353 if(m_playSpeed == DVD_PLAYSPEED_NORMAL
3354 || m_playSpeed == DVD_PLAYSPEED_PAUSE)
3356 // make sure players are properly flushed, should put them in stalled state
3357 CDVDMsgGeneralSynchronize* msg = new CDVDMsgGeneralSynchronize(1000, 0);
3358 m_dvdPlayerAudio.SendMessage(msg->Acquire(), 1);
3359 m_dvdPlayerVideo.SendMessage(msg->Acquire(), 1);
3360 msg->Wait(&m_bStop, 0);
3363 // purge any pending PLAYER_STARTED messages
3364 m_messenger.Flush(CDVDMsg::PLAYER_STARTED);
3366 // we should now wait for init cache
3367 SetCaching(CACHESTATE_FLUSH);
3368 m_CurrentAudio.started = false;
3369 m_CurrentVideo.started = false;
3370 m_CurrentSubtitle.started = false;
3371 m_CurrentTeletext.started = false;
3374 if(pts != DVD_NOPTS_VALUE)
3375 m_clock.Discontinuity(pts);
3378 // update state, buffers are flushed and it may take some time until
3379 // we get an update from players
3380 CSingleLock lock(m_StateSection);
3381 m_State = m_StateInput;
3385 // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
3386 int CDVDPlayer::OnDVDNavResult(void* pData, int iMessage)
3388 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
3391 m_overlayContainer.Add((CDVDOverlay*)pData);
3392 else if(iMessage == 1)
3393 m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
3394 else if(iMessage == 2)
3395 m_dvd.iSelectedAudioStream = *(int*)pData;
3396 else if(iMessage == 3)
3397 m_dvd.iSelectedSPUStream = *(int*)pData;
3398 else if(iMessage == 4)
3399 m_dvdPlayerVideo.EnableSubtitle(*(int*)pData ? true: false);
3400 else if(iMessage == 5)
3402 if (m_dvd.state != DVDSTATE_STILL)
3404 // else notify the player we have received a still frame
3406 m_dvd.iDVDStillTime = *(int*)pData;
3407 m_dvd.iDVDStillStartTime = XbmcThreads::SystemClockMillis();
3409 /* adjust for the output delay in the video queue */
3410 unsigned int time = 0;
3411 if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 )
3413 time = (unsigned int)(m_dvdPlayerVideo.GetOutputDelay() / ( DVD_TIME_BASE / 1000 ));
3414 if( time < 10000 && time > 0 )
3415 m_dvd.iDVDStillTime += time;
3417 m_dvd.state = DVDSTATE_STILL;
3419 "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec",
3420 m_dvd.iDVDStillTime, time / 1000);
3423 else if (iMessage == 6)
3425 m_dvd.state = DVDSTATE_NORMAL;
3426 CLog::Log(LOGDEBUG, "CDVDPlayer::OnDVDNavResult - libbluray read error (DVDSTATE_NORMAL)");
3427 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25008), g_localizeStrings.Get(25009));
3433 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3435 CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
3439 case DVDNAV_STILL_FRAME:
3441 //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
3443 dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)pData;
3444 // should wait the specified time here while we let the player running
3445 // after that call dvdnav_still_skip(m_dvdnav);
3447 if (m_dvd.state != DVDSTATE_STILL)
3449 // else notify the player we have received a still frame
3451 if(still_event->length < 0xff)
3452 m_dvd.iDVDStillTime = still_event->length * 1000;
3454 m_dvd.iDVDStillTime = 0;
3456 m_dvd.iDVDStillStartTime = XbmcThreads::SystemClockMillis();
3458 /* adjust for the output delay in the video queue */
3459 unsigned int time = 0;
3460 if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 )
3462 time = (unsigned int)(m_dvdPlayerVideo.GetOutputDelay() / ( DVD_TIME_BASE / 1000 ));
3463 if( time < 10000 && time > 0 )
3464 m_dvd.iDVDStillTime += time;
3466 m_dvd.state = DVDSTATE_STILL;
3468 "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec",
3469 still_event->length, time / 1000);
3471 return NAVRESULT_HOLD;
3474 case DVDNAV_SPU_CLUT_CHANGE:
3476 m_dvdPlayerSubtitle.SendMessage(new CDVDMsgSubtitleClutChange((uint8_t*)pData));
3479 case DVDNAV_SPU_STREAM_CHANGE:
3481 dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)pData;
3483 int iStream = event->physical_wide;
3484 bool visible = !(iStream & 0x80);
3486 SetSubtitleVisibleInternal(visible);
3489 m_dvd.iSelectedSPUStream = (iStream & ~0x80);
3491 m_dvd.iSelectedSPUStream = -1;
3493 m_CurrentSubtitle.stream = NULL;
3496 case DVDNAV_AUDIO_STREAM_CHANGE:
3498 // This should be the correct way i think, however we don't have any streams right now
3499 // since the demuxer hasn't started so it doesn't change. not sure how to do this.
3500 dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)pData;
3502 // Tell system what audiostream should be opened by default
3503 if (event->logical >= 0)
3504 m_dvd.iSelectedAudioStream = event->physical;
3506 m_dvd.iSelectedAudioStream = -1;
3508 m_CurrentAudio.stream = NULL;
3511 case DVDNAV_HIGHLIGHT:
3513 //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
3514 int iButton = pStream->GetCurrentButton();
3515 CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button %d\n", iButton);
3516 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
3519 case DVDNAV_VTS_CHANGE:
3521 //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
3522 CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
3524 //Make sure we clear all the old overlays here, or else old forced items are left.
3525 m_overlayContainer.Clear();
3527 //Force an aspect ratio that is set in the dvdheaders if available
3528 m_CurrentVideo.hint.aspect = pStream->GetVideoAspectRatio();
3529 if( m_dvdPlayerVideo.IsInited() )
3530 m_dvdPlayerVideo.SendMessage(new CDVDMsgDouble(CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
3532 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
3533 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
3535 return NAVRESULT_HOLD;
3538 case DVDNAV_CELL_CHANGE:
3540 //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
3541 CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
3543 m_dvd.state = DVDSTATE_NORMAL;
3545 if( m_dvdPlayerVideo.IsInited() )
3546 m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
3549 case DVDNAV_NAV_PACKET:
3551 //pci_t* pci = (pci_t*)pData;
3553 // this should be possible to use to make sure we get
3554 // seamless transitions over these boundaries
3555 // if we remember the old vobunits boundaries
3556 // when a packet comes out of demuxer that has
3557 // pts values outside that boundary, it belongs
3558 // to the new vobunit, wich has new timestamps
3562 case DVDNAV_HOP_CHANNEL:
3564 // This event is issued whenever a non-seamless operation has been executed.
3565 // Applications with fifos should drop the fifos content to speed up responsiveness.
3566 CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
3567 if(m_dvd.state == DVDSTATE_SEEK)
3568 m_dvd.state = DVDSTATE_NORMAL;
3570 m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
3572 return NAVRESULT_ERROR;
3577 CLog::Log(LOGDEBUG, "DVDNAV_STOP");
3578 m_dvd.state = DVDSTATE_NORMAL;
3586 return NAVRESULT_NOP;
3589 bool CDVDPlayer::ShowPVRChannelInfo(void)
3591 bool bReturn(false);
3593 if (CSettings::Get().GetBool("pvrmenu.infoswitch"))
3595 int iTimeout = CSettings::Get().GetBool("pvrmenu.infotimeout") ? CSettings::Get().GetInt("pvrmenu.infotime") : 0;
3596 g_PVRManager.ShowPlayerInfo(iTimeout);
3604 bool CDVDPlayer::OnAction(const CAction &action)
3606 #define THREAD_ACTION(action) \
3608 if (!IsCurrentThread()) { \
3609 m_messenger.Put(new CDVDMsgType<CAction>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
3614 CDVDInputStream::IMenus* pMenus = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3617 if( m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0 && pMenus->GetTotalButtons() == 0 )
3619 switch(action.GetID())
3621 case ACTION_NEXT_ITEM:
3622 case ACTION_MOVE_RIGHT:
3623 case ACTION_MOVE_UP:
3624 case ACTION_SELECT_ITEM:
3626 THREAD_ACTION(action);
3627 /* this will force us out of the stillframe */
3628 CLog::Log(LOGDEBUG, "%s - User asked to exit stillframe", __FUNCTION__);
3629 m_dvd.iDVDStillStartTime = 0;
3630 m_dvd.iDVDStillTime = 1;
3637 switch (action.GetID())
3639 /* this code is disabled to allow switching playlist items (dvdimage "stacks") */
3641 case ACTION_PREV_ITEM: // SKIP-:
3643 THREAD_ACTION(action);
3644 CLog::Log(LOGDEBUG, " - pushed prev");
3645 pMenus->OnPrevious();
3646 g_infoManager.SetDisplayAfterSeek();
3650 case ACTION_NEXT_ITEM: // SKIP+:
3652 THREAD_ACTION(action);
3653 CLog::Log(LOGDEBUG, " - pushed next");
3655 g_infoManager.SetDisplayAfterSeek();
3660 case ACTION_SHOW_VIDEOMENU: // start button
3662 THREAD_ACTION(action);
3663 CLog::Log(LOGDEBUG, " - go to menu");
3665 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
3667 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
3668 m_callback.OnPlayBackResumed();
3670 // send a message to everyone that we've gone to the menu
3671 CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0);
3672 g_windowManager.SendThreadMessage(msg);
3678 if (pMenus->IsInMenu())
3680 switch (action.GetID())
3682 case ACTION_NEXT_ITEM:
3683 THREAD_ACTION(action);
3684 CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide");
3686 g_infoManager.SetDisplayAfterSeek();
3688 case ACTION_PREV_ITEM:
3689 THREAD_ACTION(action);
3690 CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
3691 pMenus->OnPrevious();
3692 g_infoManager.SetDisplayAfterSeek();
3694 case ACTION_PREVIOUS_MENU:
3695 case ACTION_NAV_BACK:
3697 THREAD_ACTION(action);
3698 CLog::Log(LOGDEBUG, " - menu back");
3702 case ACTION_MOVE_LEFT:
3704 THREAD_ACTION(action);
3705 CLog::Log(LOGDEBUG, " - move left");
3709 case ACTION_MOVE_RIGHT:
3711 THREAD_ACTION(action);
3712 CLog::Log(LOGDEBUG, " - move right");
3716 case ACTION_MOVE_UP:
3718 THREAD_ACTION(action);
3719 CLog::Log(LOGDEBUG, " - move up");
3723 case ACTION_MOVE_DOWN:
3725 THREAD_ACTION(action);
3726 CLog::Log(LOGDEBUG, " - move down");
3731 case ACTION_MOUSE_MOVE:
3732 case ACTION_MOUSE_LEFT_CLICK:
3735 m_dvdPlayerVideo.GetVideoRect(rs, rd);
3736 CPoint pt(action.GetAmount(), action.GetAmount(1));
3737 if (!rd.PtInRect(pt))
3738 return false; // out of bounds
3739 THREAD_ACTION(action);
3740 // convert to video coords...
3741 pt -= CPoint(rd.x1, rd.y1);
3742 pt.x *= rs.Width() / rd.Width();
3743 pt.y *= rs.Height() / rd.Height();
3744 pt += CPoint(rs.x1, rs.y1);
3745 if (action.GetID() == ACTION_MOUSE_LEFT_CLICK)
3746 return pMenus->OnMouseClick(pt);
3747 return pMenus->OnMouseMove(pt);
3750 case ACTION_SELECT_ITEM:
3752 THREAD_ACTION(action);
3753 CLog::Log(LOGDEBUG, " - button select");
3754 // show button pushed overlay
3755 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3756 m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_CLICKED);
3758 pMenus->ActivateButton();
3772 THREAD_ACTION(action);
3773 // Offset from key codes back to button number
3774 int button = action.GetID() - REMOTE_0;
3775 CLog::Log(LOGDEBUG, " - button pressed %d", button);
3776 pMenus->SelectButton(button);
3783 return true; // message is handled
3787 if (dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream))
3789 switch (action.GetID())
3791 case ACTION_MOVE_UP:
3792 case ACTION_NEXT_ITEM:
3793 case ACTION_CHANNEL_UP:
3794 m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT));
3795 g_infoManager.SetDisplayAfterSeek();
3796 ShowPVRChannelInfo();
3800 case ACTION_MOVE_DOWN:
3801 case ACTION_PREV_ITEM:
3802 case ACTION_CHANNEL_DOWN:
3803 m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV));
3804 g_infoManager.SetDisplayAfterSeek();
3805 ShowPVRChannelInfo();
3809 case ACTION_CHANNEL_SWITCH:
3811 // Offset from key codes back to button number
3812 int channel = action.GetAmount();
3813 m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER, channel));
3814 g_infoManager.SetDisplayAfterSeek();
3815 ShowPVRChannelInfo();
3822 switch (action.GetID())
3824 case ACTION_NEXT_ITEM:
3825 if (GetChapter() > 0 && GetChapter() < GetChapterCount())
3827 m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter() + 1));
3828 g_infoManager.SetDisplayAfterSeek();
3833 case ACTION_PREV_ITEM:
3834 if (GetChapter() > 0)
3836 m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter() - 1));
3837 g_infoManager.SetDisplayAfterSeek();
3844 // return false to inform the caller we didn't handle the message
3848 bool CDVDPlayer::IsInMenu() const
3850 CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3853 if( m_dvd.state == DVDSTATE_STILL )
3856 return pStream->IsInMenu();
3861 bool CDVDPlayer::HasMenu()
3863 CDVDInputStream::IMenus* pStream = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream);
3865 return pStream->HasMenu();
3870 CStdString CDVDPlayer::GetPlayerState()
3872 CSingleLock lock(m_StateSection);
3873 return m_State.player_state;
3876 bool CDVDPlayer::SetPlayerState(CStdString state)
3878 m_messenger.Put(new CDVDMsgPlayerSetState(state));
3882 int CDVDPlayer::GetChapterCount()
3884 CSingleLock lock(m_StateSection);
3885 return m_State.chapter_count;
3888 int CDVDPlayer::GetChapter()
3890 CSingleLock lock(m_StateSection);
3891 return m_State.chapter;
3894 void CDVDPlayer::GetChapterName(CStdString& strChapterName)
3896 CSingleLock lock(m_StateSection);
3897 strChapterName = m_State.chapter_name;
3900 int CDVDPlayer::SeekChapter(int iChapter)
3902 if (GetChapter() > 0)
3906 if (iChapter > GetChapterCount())
3909 // Seek to the chapter.
3910 m_messenger.Put(new CDVDMsgPlayerSeekChapter(iChapter));
3911 SynchronizeDemuxer(100);
3917 int CDVDPlayer::AddSubtitle(const CStdString& strSubPath)
3919 return AddSubtitleFile(strSubPath);
3922 int CDVDPlayer::GetCacheLevel() const
3924 CSingleLock lock(m_StateSection);
3925 return (int)(m_StateInput.cache_level * 100);
3928 double CDVDPlayer::GetQueueTime()
3930 int a = m_dvdPlayerAudio.GetLevel();
3931 int v = m_dvdPlayerVideo.GetLevel();
3932 return max(a, v) * 8000.0 / 100;
3935 void CDVDPlayer::GetVideoStreamInfo(SPlayerVideoStreamInfo &info)
3937 info.bitrate = m_dvdPlayerVideo.GetVideoBitrate();
3940 if (m_pDemuxer && (m_CurrentVideo.id != -1))
3942 m_pDemuxer->GetStreamCodecName(m_CurrentVideo.id, retVal);
3943 CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
3946 info.width = stream->iWidth;
3947 info.height = stream->iHeight;
3950 info.videoCodecName = retVal;
3951 info.videoAspectRatio = m_dvdPlayerVideo.GetAspectRatio();
3952 m_dvdPlayerVideo.GetVideoRect(info.SrcRect, info.DestRect);
3953 info.stereoMode = m_dvdPlayerVideo.GetStereoMode();
3954 if (info.stereoMode == "mono")
3955 info.stereoMode = "";
3958 int CDVDPlayer::GetSourceBitrate()
3961 return (int)m_pInputStream->GetBitstreamStats().GetBitrate();
3966 void CDVDPlayer::GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info)
3968 if (index < 0 || index > GetAudioStreamCount() - 1 )
3971 if (index == GetAudioStream())
3972 info.bitrate = m_dvdPlayerAudio.GetAudioBitrate();
3973 else if (m_pDemuxer)
3975 CDemuxStreamAudio* stream = m_pDemuxer->GetStreamFromAudioId(index);
3977 info.bitrate = stream->iBitRate;
3980 SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, index);
3981 if(s.language.length() > 0)
3982 info.language = s.language;
3984 if(s.name.length() > 0)
3987 if(s.type == STREAM_NONE)
3988 info.name += " (Invalid)";
3992 CDemuxStreamAudio* stream = static_cast<CDemuxStreamAudio*>(m_pDemuxer->GetStreamFromAudioId(index));
3995 info.channels = stream->iChannels;
3996 CStdString codecName;
3997 m_pDemuxer->GetStreamCodecName(stream->iId, codecName);
3998 info.audioCodecName = codecName;
4003 int CDVDPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename, CDemuxStream::EFlags flags)
4005 std::string ext = URIUtils::GetExtension(filename);
4006 std::string vobsubfile = subfilename;
4009 if (vobsubfile.empty())
4010 vobsubfile = URIUtils::ReplaceExtension(filename, ".sub");
4013 if(!v.Open(filename, vobsubfile))
4015 m_SelectionStreams.Update(NULL, &v);
4016 int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename), 0);
4017 m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags;
4018 m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename2 = vobsubfile;
4019 ExternalStreamInfo info;
4020 CUtil::GetExternalStreamDetailsFromFilename(m_filename, vobsubfile, info);
4021 m_SelectionStreams.Get(STREAM_SUBTITLE, index).name = info.name;
4022 if (m_SelectionStreams.Get(STREAM_SUBTITLE, index).language.empty())
4023 m_SelectionStreams.Get(STREAM_SUBTITLE, index).language = info.language;
4025 if (static_cast<CDemuxStream::EFlags>(info.flag) == CDemuxStream::FLAG_NONE)
4026 m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = flags;
4028 m_SelectionStreams.Get(STREAM_SUBTITLE, index).flags = static_cast<CDemuxStream::EFlags>(info.flag);
4034 CStdString strReplace(URIUtils::ReplaceExtension(filename,".idx"));
4035 if (XFILE::CFile::Exists(strReplace))
4039 s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename);
4040 s.type = STREAM_SUBTITLE;
4042 s.filename = filename;
4043 ExternalStreamInfo info;
4044 CUtil::GetExternalStreamDetailsFromFilename(m_filename, filename, info);
4046 s.language = info.language;
4047 if (static_cast<CDemuxStream::EFlags>(info.flag) == CDemuxStream::FLAG_NONE)
4050 s.flags = static_cast<CDemuxStream::EFlags>(info.flag);
4052 m_SelectionStreams.Update(s);
4053 return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, s.source, s.id);
4056 void CDVDPlayer::UpdatePlayState(double timeout)
4058 if(m_StateInput.timestamp != 0
4059 && m_StateInput.timestamp + DVD_MSEC_TO_TIME(timeout) > CDVDClock::GetAbsoluteClock())
4062 SPlayerState state(m_StateInput);
4064 if (m_CurrentVideo.dts != DVD_NOPTS_VALUE)
4065 state.dts = m_CurrentVideo.dts;
4066 else if(m_CurrentAudio.dts != DVD_NOPTS_VALUE)
4067 state.dts = m_CurrentAudio.dts;
4068 else if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
4069 state.dts = m_CurrentVideo.startpts;
4070 else if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
4071 state.dts = m_CurrentAudio.startpts;
4076 state.chapter = m_pDemuxer->GetChapter();
4077 state.chapter_count = m_pDemuxer->GetChapterCount();
4078 m_pDemuxer->GetChapterName(state.chapter_name);
4080 if(state.dts == DVD_NOPTS_VALUE)
4083 state.time = DVD_TIME_TO_MSEC(state.dts + m_offset_pts);
4084 state.time_total = m_pDemuxer->GetStreamLength();
4085 state.time_src = ETIMESOURCE_CLOCK;
4088 state.canpause = true;
4089 state.canseek = true;
4093 // override from input stream if needed
4094 CDVDInputStream::IChannel* pChannel = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
4097 state.canrecord = pChannel->CanRecord();
4098 state.recording = pChannel->IsRecording();
4101 CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast<CDVDInputStream::IDisplayTime*>(m_pInputStream);
4102 if (pDisplayTime && pDisplayTime->GetTotalTime() > 0)
4104 state.time = pDisplayTime->GetTime();
4105 state.time_total = pDisplayTime->GetTotalTime();
4106 state.time_src = ETIMESOURCE_INPUT;
4109 if (CDVDInputStream::IMenus* ptr = dynamic_cast<CDVDInputStream::IMenus*>(m_pInputStream))
4111 if(!ptr->GetState(state.player_state))
4112 state.player_state = "";
4114 if(m_dvd.state == DVDSTATE_STILL)
4116 state.time = XbmcThreads::SystemClockMillis() - m_dvd.iDVDStillStartTime;
4117 state.time_total = m_dvd.iDVDStillTime;
4118 state.time_src = ETIMESOURCE_MENU;
4122 if (CDVDInputStream::ISeekable* ptr = dynamic_cast<CDVDInputStream::ISeekable*>(m_pInputStream))
4124 state.canpause = ptr->CanPause();
4125 state.canseek = ptr->CanSeek();
4131 state.time = m_Edl.RemoveCutTime(llrint(state.time));
4132 state.time_total = m_Edl.RemoveCutTime(llrint(state.time_total));
4135 if(state.time_total <= 0)
4136 state.canseek = false;
4138 if (state.time_src == ETIMESOURCE_CLOCK)
4139 state.time_offset = m_offset_pts;
4140 else if (state.dts != DVD_NOPTS_VALUE)
4141 state.time_offset = DVD_MSEC_TO_TIME(state.time) - state.dts;
4143 if (m_CurrentAudio.id >= 0 && m_pDemuxer)
4145 CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentAudio.id);
4146 if (pStream && pStream->type == STREAM_AUDIO)
4147 ((CDemuxStreamAudio*)pStream)->GetStreamInfo(state.demux_audio);
4150 state.demux_audio = "";
4152 if (m_CurrentVideo.id >= 0 && m_pDemuxer)
4154 CDemuxStream* pStream = m_pDemuxer->GetStream(m_CurrentVideo.id);
4155 if (pStream && pStream->type == STREAM_VIDEO)
4156 ((CDemuxStreamVideo*)pStream)->GetStreamInfo(state.demux_video);
4159 state.demux_video = "";
4161 double level, delay, offset;
4162 if(GetCachingTimes(level, delay, offset))
4164 state.cache_delay = max(0.0, delay);
4165 state.cache_level = max(0.0, min(1.0, level));
4166 state.cache_offset = offset;
4170 state.cache_delay = 0.0;
4171 state.cache_level = min(1.0, GetQueueTime() / 8000.0);
4172 state.cache_offset = GetQueueTime() / state.time_total;
4175 XFILE::SCacheStatus status;
4176 if(m_pInputStream && m_pInputStream->GetCacheStatus(&status))
4178 state.cache_bytes = status.forward;
4179 if(state.time_total)
4180 state.cache_bytes += m_pInputStream->GetLength() * GetQueueTime() / state.time_total;
4183 state.cache_bytes = 0;
4185 state.timestamp = CDVDClock::GetAbsoluteClock();
4187 CSingleLock lock(m_StateSection);
4188 m_StateInput = state;
4191 void CDVDPlayer::UpdateApplication(double timeout)
4193 if(m_UpdateApplication != 0
4194 && m_UpdateApplication + DVD_MSEC_TO_TIME(timeout) > CDVDClock::GetAbsoluteClock())
4197 CDVDInputStream::IChannel* pStream = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
4200 CFileItem item(g_application.CurrentFileItem());
4201 if(pStream->UpdateItem(item))
4203 g_application.CurrentFileItem() = item;
4204 CApplicationMessenger::Get().SetCurrentItem(item);
4207 m_UpdateApplication = CDVDClock::GetAbsoluteClock();
4210 bool CDVDPlayer::CanRecord()
4212 CSingleLock lock(m_StateSection);
4213 return m_State.canrecord;
4216 bool CDVDPlayer::IsRecording()
4218 CSingleLock lock(m_StateSection);
4219 return m_State.recording;
4222 bool CDVDPlayer::Record(bool bOnOff)
4224 if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV) ||
4225 m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) )
4227 m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff));
4233 bool CDVDPlayer::GetStreamDetails(CStreamDetails &details)
4237 std::vector<SelectionStream> subs = m_SelectionStreams.Get(STREAM_SUBTITLE);
4238 std::vector<CStreamDetailSubtitle> extSubDetails;
4239 for (unsigned int i = 0; i < subs.size(); i++)
4241 if (subs[i].filename == m_filename)
4244 CStreamDetailSubtitle p;
4245 p.m_strLanguage = subs[i].language;
4246 extSubDetails.push_back(p);
4249 bool result = CDVDFileInfo::DemuxerToStreamDetails(m_pInputStream, m_pDemuxer, extSubDetails, details);
4250 if (result && details.GetStreamCount(CStreamDetail::VIDEO) > 0) // this is more correct (dvds in particular)
4253 * We can only obtain the aspect & duration from dvdplayer when the Process() thread is running
4254 * and UpdatePlayState() has been called at least once. In this case dvdplayer duration/AR will
4255 * return 0 and we'll have to fallback to the (less accurate) info from the demuxer.
4257 float aspect = m_dvdPlayerVideo.GetAspectRatio();
4259 ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_fAspect = aspect;
4261 int64_t duration = GetTotalTime() / 1000;
4263 ((CStreamDetailVideo*)details.GetNthStream(CStreamDetail::VIDEO,0))->m_iDuration = duration;
4271 CStdString CDVDPlayer::GetPlayingTitle()
4273 /* Currently we support only Title Name from Teletext line 30 */
4274 TextCacheStruct_t* ttcache = m_dvdPlayerTeletext.GetTeletextCache();
4275 if (ttcache && !ttcache->line30.empty())
4276 return ttcache->line30;
4281 bool CDVDPlayer::SwitchChannel(const CPVRChannel &channel)
4283 if (!g_PVRManager.CheckParentalLock(channel))
4287 if (!g_PVRManager.PerformChannelSwitch(channel, true))
4290 UpdateApplication(0);
4293 /* make sure the pvr window is updated */
4294 CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
4296 pWindow->SetInvalid();
4298 /* select the new channel */
4299 m_messenger.Put(new CDVDMsgType<CPVRChannel>(CDVDMsg::PLAYER_CHANNEL_SELECT, channel));
4304 bool CDVDPlayer::CachePVRStream(void) const
4306 return m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) &&
4307 !g_PVRManager.IsPlayingRecording() &&
4308 g_advancedSettings.m_bPVRCacheInDvdPlayer;