[video] Fix large bookmark thumbnails
[xbmc.git] / xbmc / cores / VideoPlayer / VideoPlayer.cpp
blob3b28e338b7aa7a672cd54fe02e381683fe97b6e7
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "VideoPlayer.h"
11 #include "DVDCodecs/DVDCodecUtils.h"
12 #include "DVDDemuxers/DVDDemux.h"
13 #include "DVDDemuxers/DVDDemuxCC.h"
14 #include "DVDDemuxers/DVDDemuxFFmpeg.h"
15 #include "DVDDemuxers/DVDDemuxUtils.h"
16 #include "DVDDemuxers/DVDDemuxVobsub.h"
17 #include "DVDDemuxers/DVDFactoryDemuxer.h"
18 #include "DVDInputStreams/DVDFactoryInputStream.h"
19 #include "DVDInputStreams/DVDInputStream.h"
20 #if defined(HAVE_LIBBLURAY)
21 #include "DVDInputStreams/DVDInputStreamBluray.h"
22 #endif
23 #include "DVDInputStreams/DVDInputStreamNavigator.h"
24 #include "DVDInputStreams/InputStreamPVRBase.h"
25 #include "DVDMessage.h"
26 #include "FileItem.h"
27 #include "GUIUserMessages.h"
28 #include "LangInfo.h"
29 #include "ServiceBroker.h"
30 #include "URL.h"
31 #include "Util.h"
32 #include "VideoPlayerAudio.h"
33 #include "VideoPlayerRadioRDS.h"
34 #include "VideoPlayerVideo.h"
35 #include "application/Application.h"
36 #include "cores/DataCacheCore.h"
37 #include "cores/EdlEdit.h"
38 #include "cores/FFmpeg.h"
39 #include "cores/VideoPlayer/Process/ProcessInfo.h"
40 #include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
41 #include "dialogs/GUIDialogKaiToast.h"
42 #include "guilib/GUIComponent.h"
43 #include "guilib/GUIWindowManager.h"
44 #include "guilib/LocalizeStrings.h"
45 #include "guilib/StereoscopicsManager.h"
46 #include "input/actions/Action.h"
47 #include "input/actions/ActionIDs.h"
48 #include "messaging/ApplicationMessenger.h"
49 #include "settings/AdvancedSettings.h"
50 #include "settings/Settings.h"
51 #include "settings/SettingsComponent.h"
52 #include "threads/SingleLock.h"
53 #include "utils/FontUtils.h"
54 #include "utils/JobManager.h"
55 #include "utils/LangCodeExpander.h"
56 #include "utils/StreamDetails.h"
57 #include "utils/StreamUtils.h"
58 #include "utils/StringUtils.h"
59 #include "utils/URIUtils.h"
60 #include "utils/Variant.h"
61 #include "utils/log.h"
62 #include "video/Bookmark.h"
63 #include "video/VideoInfoTag.h"
64 #include "windowing/WinSystem.h"
66 #include <iterator>
67 #include <memory>
68 #include <mutex>
69 #include <utility>
71 using namespace std::chrono_literals;
73 //------------------------------------------------------------------------------
74 // selection streams
75 //------------------------------------------------------------------------------
77 #define PREDICATE_RETURN(lh, rh) \
78 do { \
79 if((lh) != (rh)) \
80 return (lh) > (rh); \
81 } while(0)
83 class PredicateSubtitleFilter
85 private:
86 std::string audiolang;
87 bool original;
88 bool nosub;
89 bool onlyforced;
90 int currentSubStream;
91 public:
92 /** \brief The class' operator() decides if the given (subtitle) SelectionStream is relevant wrt.
93 * preferred subtitle language and audio language. If the subtitle is relevant <B>false</B> false is returned.
95 * A subtitle is relevant if
96 * - it was previously selected, or
97 * - it's an external sub, or
98 * - it's a forced sub and "original stream's language" was selected and audio stream language matches, or
99 * - it's a default and a forced sub (could lead to users seeing forced subs in a foreign language!), or
100 * - its language matches the preferred subtitle's language (unequal to "original stream's language")
102 explicit PredicateSubtitleFilter(const std::string& lang, int subStream)
103 : audiolang(lang),
104 currentSubStream(subStream)
106 const std::string subtitleLang = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE);
107 original = StringUtils::EqualsNoCase(subtitleLang, "original");
108 nosub = StringUtils::EqualsNoCase(subtitleLang, "none");
109 onlyforced = StringUtils::EqualsNoCase(subtitleLang, "forced_only");
112 bool operator()(const SelectionStream& ss) const
114 if (ss.type_index == currentSubStream)
115 return false;
117 if (nosub)
118 return true;
120 if (onlyforced)
122 if ((ss.flags & StreamFlags::FLAG_FORCED) && g_LangCodeExpander.CompareISO639Codes(ss.language, audiolang))
123 return false;
124 else
125 return true;
128 if(STREAM_SOURCE_MASK(ss.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(ss.source) == STREAM_SOURCE_TEXT)
129 return false;
131 if ((ss.flags & StreamFlags::FLAG_FORCED) && g_LangCodeExpander.CompareISO639Codes(ss.language, audiolang))
132 return false;
134 if ((ss.flags & StreamFlags::FLAG_FORCED) && (ss.flags & StreamFlags::FLAG_DEFAULT))
135 return false;
137 if (ss.language == "cc" && ss.flags & StreamFlags::FLAG_HEARING_IMPAIRED)
138 return false;
140 if(!original)
142 std::string subtitle_language = g_langInfo.GetSubtitleLanguage();
143 if (g_LangCodeExpander.CompareISO639Codes(subtitle_language, ss.language))
144 return false;
146 else if (ss.flags & StreamFlags::FLAG_DEFAULT)
147 return false;
149 return true;
153 class PredicateAudioFilter
155 private:
156 int currentAudioStream;
157 bool preferStereo;
158 public:
159 explicit PredicateAudioFilter(int audioStream, bool preferStereo)
160 : currentAudioStream(audioStream)
161 , preferStereo(preferStereo)
164 bool operator()(const SelectionStream& lh, const SelectionStream& rh)
166 PREDICATE_RETURN(lh.type_index == currentAudioStream
167 , rh.type_index == currentAudioStream);
169 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
171 if (!StringUtils::EqualsNoCase(settings->GetString(CSettings::SETTING_LOCALE_AUDIOLANGUAGE), "mediadefault"))
173 if (!StringUtils::EqualsNoCase(settings->GetString(CSettings::SETTING_LOCALE_AUDIOLANGUAGE), "original"))
175 std::string audio_language = g_langInfo.GetAudioLanguage();
176 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(audio_language, lh.language)
177 , g_LangCodeExpander.CompareISO639Codes(audio_language, rh.language));
179 else
181 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_ORIGINAL,
182 rh.flags & StreamFlags::FLAG_ORIGINAL);
185 bool hearingimp = settings->GetBool(CSettings::SETTING_ACCESSIBILITY_AUDIOHEARING);
186 PREDICATE_RETURN(!hearingimp ? !(lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED
187 , !hearingimp ? !(rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED);
189 bool visualimp = settings->GetBool(CSettings::SETTING_ACCESSIBILITY_AUDIOVISUAL);
190 PREDICATE_RETURN(!visualimp ? !(lh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED) : lh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED
191 , !visualimp ? !(rh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED) : rh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED);
194 if (settings->GetBool(CSettings::SETTING_VIDEOPLAYER_PREFERDEFAULTFLAG))
196 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
197 rh.flags & StreamFlags::FLAG_DEFAULT);
200 if (preferStereo)
201 PREDICATE_RETURN(lh.channels == 2,
202 rh.channels == 2);
203 else
204 PREDICATE_RETURN(lh.channels,
205 rh.channels);
207 PREDICATE_RETURN(StreamUtils::GetCodecPriority(lh.codec),
208 StreamUtils::GetCodecPriority(rh.codec));
210 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
211 rh.flags & StreamFlags::FLAG_DEFAULT);
212 return false;
216 /** \brief The class' operator() decides if the given (subtitle) SelectionStream lh is 'better than' the given (subtitle) SelectionStream rh.
217 * If lh is 'better than' rh the return value is true, false otherwise.
219 * A subtitle lh is 'better than' a subtitle rh (in evaluation order) if
220 * - lh was previously selected, or
221 * - lh is an external sub and rh not, or
222 * - lh is a forced sub and ("original stream's language" was selected or subtitles are off) and audio stream language matches sub language and rh not, or
223 * - lh is a default sub and ("original stream's language" was selected or subtitles are off) and audio stream language matches sub language and rh not, or
224 * - lh is a sub where audio stream language matches sub language and (original stream's language" was selected or subtitles are off) and rh not, or
225 * - lh is a forced sub and a default sub ("original stream's language" was selected or subtitles are off)
226 * - lh is an external sub and its language matches the preferred subtitle's language (unequal to "original stream's language") and rh not, or
227 * - lh is language matches the preferred subtitle's language (unequal to "original stream's language") and rh not, or
228 * - lh is a default sub and rh not
230 class PredicateSubtitlePriority
232 private:
233 std::string audiolang;
234 bool original;
235 bool subson;
236 PredicateSubtitleFilter filter;
237 int subStream;
238 public:
239 explicit PredicateSubtitlePriority(const std::string& lang, int stream, bool ison)
240 : audiolang(lang),
241 original(StringUtils::EqualsNoCase(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE), "original")),
242 subson(ison),
243 filter(lang, stream),
244 subStream(stream)
248 bool relevant(const SelectionStream& ss) const
250 return !filter(ss);
253 bool operator()(const SelectionStream& lh, const SelectionStream& rh) const
255 PREDICATE_RETURN(relevant(lh)
256 , relevant(rh));
258 PREDICATE_RETURN(lh.type_index == subStream
259 , rh.type_index == subStream);
261 // prefer external subs
262 PREDICATE_RETURN(STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_TEXT
263 , STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT);
265 if (!subson || original)
267 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
268 , rh.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
270 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT && g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
271 , rh.flags & StreamFlags::FLAG_DEFAULT && g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
273 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
274 , g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
276 PREDICATE_RETURN((lh.flags & (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)) == (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)
277 , (rh.flags & (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)) == (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT));
281 std::string subtitle_language = g_langInfo.GetSubtitleLanguage();
282 if (!original)
284 PREDICATE_RETURN((STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_TEXT) && g_LangCodeExpander.CompareISO639Codes(subtitle_language, lh.language)
285 , (STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT) && g_LangCodeExpander.CompareISO639Codes(subtitle_language, rh.language));
288 if (!original)
290 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(subtitle_language, lh.language)
291 , g_LangCodeExpander.CompareISO639Codes(subtitle_language, rh.language));
293 bool hearingimp = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_ACCESSIBILITY_SUBHEARING);
294 PREDICATE_RETURN(!hearingimp ? !(lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED
295 , !hearingimp ? !(rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED);
298 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT
299 , rh.flags & StreamFlags::FLAG_DEFAULT);
301 return false;
305 class PredicateVideoFilter
307 private:
308 int currentVideoStream;
309 public:
310 explicit PredicateVideoFilter(int videoStream) : currentVideoStream(videoStream)
313 bool operator()(const SelectionStream& lh, const SelectionStream& rh)
315 PREDICATE_RETURN(lh.type_index == currentVideoStream,
316 rh.type_index == currentVideoStream);
318 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
319 rh.flags & StreamFlags::FLAG_DEFAULT);
320 return false;
324 void CSelectionStreams::Clear(StreamType type, StreamSource source)
326 auto new_end = std::remove_if(m_Streams.begin(), m_Streams.end(),
327 [type, source](const SelectionStream &stream)
329 return (type == STREAM_NONE || stream.type == type) &&
330 (source == 0 || stream.source == source);
332 m_Streams.erase(new_end, m_Streams.end());
335 SelectionStream& CSelectionStreams::Get(StreamType type, int index)
337 return const_cast<SelectionStream&>(std::as_const(*this).Get(type, index));
340 const SelectionStream& CSelectionStreams::Get(StreamType type, int index) const
342 int count = -1;
343 for (size_t i = 0; i < m_Streams.size(); ++i)
345 if (m_Streams[i].type != type)
346 continue;
347 count++;
348 if (count == index)
349 return m_Streams[i];
351 return m_invalid;
354 std::vector<SelectionStream> CSelectionStreams::Get(StreamType type)
356 std::vector<SelectionStream> streams;
357 std::copy_if(m_Streams.begin(), m_Streams.end(), std::back_inserter(streams),
358 [type](const SelectionStream &stream)
360 return stream.type == type;
362 return streams;
365 bool CSelectionStreams::Get(StreamType type, StreamFlags flag, SelectionStream& out)
367 for(size_t i=0;i<m_Streams.size();i++)
369 if(m_Streams[i].type != type)
370 continue;
371 if((m_Streams[i].flags & flag) != flag)
372 continue;
373 out = m_Streams[i];
374 return true;
376 return false;
379 int CSelectionStreams::TypeIndexOf(StreamType type, int source, int64_t demuxerId, int id) const
381 if (id < 0)
382 return -1;
384 auto it = std::find_if(m_Streams.begin(), m_Streams.end(),
385 [&](const SelectionStream& stream) {return stream.type == type
386 && stream.source == source && stream.id == id
387 && stream.demuxerId == demuxerId;});
389 if (it != m_Streams.end())
390 return it->type_index;
391 else
392 return -1;
395 int CSelectionStreams::Source(StreamSource source, const std::string& filename)
397 int index = source - 1;
398 for (size_t i=0; i<m_Streams.size(); i++)
400 SelectionStream &s = m_Streams[i];
401 if (STREAM_SOURCE_MASK(s.source) != source)
402 continue;
403 // if it already exists, return same
404 if (s.filename == filename)
405 return s.source;
406 if (index < s.source)
407 index = s.source;
409 // return next index
410 return index + 1;
413 void CSelectionStreams::Update(SelectionStream& s)
415 int index = TypeIndexOf(s.type, s.source, s.demuxerId, s.id);
416 if(index >= 0)
418 SelectionStream& o = Get(s.type, index);
419 s.type_index = o.type_index;
420 o = s;
422 else
424 s.type_index = CountType(s.type);
425 m_Streams.push_back(s);
429 void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input,
430 CDVDDemux* demuxer,
431 const std::string& filename2)
433 if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
435 std::shared_ptr<CDVDInputStreamNavigator> nav = std::static_pointer_cast<CDVDInputStreamNavigator>(input);
436 std::string filename = nav->GetFileName();
437 int source = Source(STREAM_SOURCE_NAV, filename);
439 int count;
440 count = nav->GetAudioStreamCount();
441 for(int i=0;i<count;i++)
443 SelectionStream s;
444 s.source = source;
445 s.type = STREAM_AUDIO;
446 s.id = i;
447 s.flags = StreamFlags::FLAG_NONE;
448 s.filename = filename;
450 AudioStreamInfo info = nav->GetAudioStreamInfo(i);
451 s.name = info.name;
452 s.codec = info.codecName;
453 s.language = g_LangCodeExpander.ConvertToISO6392B(info.language);
454 s.channels = info.channels;
455 s.flags = info.flags;
456 Update(s);
459 count = nav->GetSubTitleStreamCount();
460 for(int i=0;i<count;i++)
462 SelectionStream s;
463 s.source = source;
464 s.type = STREAM_SUBTITLE;
465 s.id = i;
466 s.filename = filename;
467 s.channels = 0;
469 SubtitleStreamInfo info = nav->GetSubtitleStreamInfo(i);
470 s.name = info.name;
471 s.flags = info.flags;
472 s.language = g_LangCodeExpander.ConvertToISO6392B(info.language);
473 Update(s);
476 VideoStreamInfo info = nav->GetVideoStreamInfo();
477 for (int i = 1; i <= info.angles; i++)
479 SelectionStream s;
480 s.source = source;
481 s.type = STREAM_VIDEO;
482 s.id = i;
483 s.flags = StreamFlags::FLAG_NONE;
484 s.filename = filename;
485 s.channels = 0;
486 s.aspect_ratio = info.videoAspectRatio;
487 s.width = info.width;
488 s.height = info.height;
489 s.codec = info.codecName;
490 s.name = StringUtils::Format("{} {}", g_localizeStrings.Get(38032), i);
491 Update(s);
494 else if(demuxer)
496 std::string filename = demuxer->GetFileName();
497 int source;
498 if(input) /* hack to know this is sub decoder */
499 source = Source(STREAM_SOURCE_DEMUX, filename);
500 else if (!filename2.empty())
501 source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
502 else
503 source = Source(STREAM_SOURCE_VIDEOMUX, filename);
505 for (auto stream : demuxer->GetStreams())
507 /* skip streams with no type */
508 if (stream->type == STREAM_NONE)
509 continue;
510 /* make sure stream is marked with right source */
511 stream->source = source;
513 SelectionStream s;
514 s.source = source;
515 s.type = stream->type;
516 s.id = stream->uniqueId;
517 s.demuxerId = stream->demuxerId;
518 s.language = g_LangCodeExpander.ConvertToISO6392B(stream->language);
519 s.flags = stream->flags;
520 s.filename = demuxer->GetFileName();
521 s.filename2 = filename2;
522 s.name = stream->GetStreamName();
523 s.codec = demuxer->GetStreamCodecName(stream->demuxerId, stream->uniqueId);
524 s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below.
525 if(stream->type == STREAM_VIDEO)
527 CDemuxStreamVideo* vstream = static_cast<CDemuxStreamVideo*>(stream);
528 s.width = vstream->iWidth;
529 s.height = vstream->iHeight;
530 s.aspect_ratio = vstream->fAspect;
531 s.stereo_mode = vstream->stereo_mode;
532 s.bitrate = vstream->iBitRate;
533 s.hdrType = vstream->hdr_type;
535 if(stream->type == STREAM_AUDIO)
537 std::string type;
538 type = static_cast<CDemuxStreamAudio*>(stream)->GetStreamType();
539 if(type.length() > 0)
541 if(s.name.length() > 0)
542 s.name += " - ";
543 s.name += type;
545 s.channels = static_cast<CDemuxStreamAudio*>(stream)->iChannels;
546 s.bitrate = static_cast<CDemuxStreamAudio*>(stream)->iBitRate;
548 Update(s);
551 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
552 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
553 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
556 void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input, CDVDDemux* demuxer)
558 Update(input, demuxer, "");
561 int CSelectionStreams::CountTypeOfSource(StreamType type, StreamSource source) const
563 return std::count_if(m_Streams.begin(), m_Streams.end(),
564 [&](const SelectionStream& stream) {return (stream.type == type) && (stream.source == source);});
567 int CSelectionStreams::CountType(StreamType type) const
569 return std::count_if(m_Streams.begin(), m_Streams.end(),
570 [&](const SelectionStream& stream) { return stream.type == type; });
573 //------------------------------------------------------------------------------
574 // main class
575 //------------------------------------------------------------------------------
577 void CVideoPlayer::CreatePlayers()
579 if (m_players_created)
580 return;
582 m_VideoPlayerVideo = new CVideoPlayerVideo(&m_clock, &m_overlayContainer, m_messenger, m_renderManager, *m_processInfo);
583 m_VideoPlayerAudio = new CVideoPlayerAudio(&m_clock, m_messenger, *m_processInfo);
584 m_VideoPlayerSubtitle = new CVideoPlayerSubtitle(&m_overlayContainer, *m_processInfo);
585 m_VideoPlayerTeletext = new CDVDTeletextData(*m_processInfo);
586 m_VideoPlayerRadioRDS = new CDVDRadioRDSData(*m_processInfo);
587 m_VideoPlayerAudioID3 = std::make_unique<CVideoPlayerAudioID3>(*m_processInfo);
588 m_players_created = true;
591 void CVideoPlayer::DestroyPlayers()
593 if (!m_players_created)
594 return;
596 delete m_VideoPlayerVideo;
597 delete m_VideoPlayerAudio;
598 delete m_VideoPlayerSubtitle;
599 delete m_VideoPlayerTeletext;
600 delete m_VideoPlayerRadioRDS;
601 m_VideoPlayerAudioID3.reset();
603 m_players_created = false;
606 CVideoPlayer::CVideoPlayer(IPlayerCallback& callback)
607 : IPlayer(callback),
608 CThread("VideoPlayer"),
609 m_CurrentAudio(STREAM_AUDIO, VideoPlayer_AUDIO),
610 m_CurrentVideo(STREAM_VIDEO, VideoPlayer_VIDEO),
611 m_CurrentSubtitle(STREAM_SUBTITLE, VideoPlayer_SUBTITLE),
612 m_CurrentTeletext(STREAM_TELETEXT, VideoPlayer_TELETEXT),
613 m_CurrentRadioRDS(STREAM_RADIO_RDS, VideoPlayer_RDS),
614 m_CurrentAudioID3(STREAM_AUDIO_ID3, VideoPlayer_ID3),
615 m_messenger("player"),
616 m_outboundEvents(std::make_unique<CJobQueue>(false, 1, CJob::PRIORITY_NORMAL)),
617 m_pInputStream(nullptr),
618 m_pDemuxer(nullptr),
619 m_pSubtitleDemuxer(nullptr),
620 m_pCCDemuxer(nullptr),
621 m_renderManager(m_clock, this)
623 m_players_created = false;
625 m_dvd.Clear();
626 m_State.Clear();
628 m_bAbortRequest = false;
629 m_offset_pts = 0.0;
630 m_playSpeed = DVD_PLAYSPEED_NORMAL;
631 m_streamPlayerSpeed = DVD_PLAYSPEED_NORMAL;
632 m_caching = CACHESTATE_DONE;
633 m_HasVideo = false;
634 m_HasAudio = false;
635 m_UpdateStreamDetails = false;
637 m_SkipCommercials = true;
639 m_processInfo.reset(CProcessInfo::CreateInstance());
640 // if we have a gui, register the cache
641 m_processInfo->SetDataCache(&CServiceBroker::GetDataCacheCore());
642 m_processInfo->SetSpeed(1.0);
643 m_processInfo->SetTempo(1.0);
644 m_processInfo->SetFrameAdvance(false);
646 CreatePlayers();
648 m_displayLost = false;
649 m_error = false;
650 m_bCloseRequest = false;
651 CServiceBroker::GetWinSystem()->Register(this);
654 CVideoPlayer::~CVideoPlayer()
656 CServiceBroker::GetWinSystem()->Unregister(this);
658 CloseFile();
659 DestroyPlayers();
661 while (m_outboundEvents->IsProcessing())
663 CThread::Sleep(10ms);
667 bool CVideoPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
669 CLog::Log(LOGINFO, "VideoPlayer::OpenFile: {}", CURL::GetRedacted(file.GetPath()));
671 if (IsRunning())
673 CDVDMsgOpenFile::FileParams params;
674 params.m_item = file;
675 params.m_options = options;
676 params.m_item.SetMimeTypeForInternetFile();
677 m_messenger.Put(std::make_shared<CDVDMsgOpenFile>(params), 1);
679 return true;
682 m_item = file;
683 m_playerOptions = options;
685 m_processInfo->SetPlayTimes(0,0,0,0);
686 m_bAbortRequest = false;
687 m_error = false;
688 m_bCloseRequest = false;
689 m_renderManager.PreInit();
691 Create();
692 m_messenger.Init();
694 m_callback.OnPlayBackStarted(m_item);
696 return true;
699 bool CVideoPlayer::CloseFile(bool reopen)
701 CLog::Log(LOGINFO, "CVideoPlayer::CloseFile()");
703 // set the abort request so that other threads can finish up
704 m_bAbortRequest = true;
705 m_bCloseRequest = true;
707 // tell demuxer to abort
708 if(m_pDemuxer)
709 m_pDemuxer->Abort();
711 if(m_pSubtitleDemuxer)
712 m_pSubtitleDemuxer->Abort();
714 if(m_pInputStream)
715 m_pInputStream->Abort();
717 m_renderManager.UnInit();
719 CLog::Log(LOGINFO, "VideoPlayer: waiting for threads to exit");
721 // wait for the main thread to finish up
722 // since this main thread cleans up all other resources and threads
723 // we are done after the StopThread call
725 CSingleExit exitlock(CServiceBroker::GetWinSystem()->GetGfxContext());
726 StopThread();
729 m_Edl.Clear();
730 CServiceBroker::GetDataCacheCore().Reset();
732 m_HasVideo = false;
733 m_HasAudio = false;
735 CLog::Log(LOGINFO, "VideoPlayer: finished waiting");
736 return true;
739 bool CVideoPlayer::IsPlaying() const
741 return !m_bStop;
744 void CVideoPlayer::OnStartup()
746 m_CurrentVideo.Clear();
747 m_CurrentAudio.Clear();
748 m_CurrentSubtitle.Clear();
749 m_CurrentTeletext.Clear();
750 m_CurrentRadioRDS.Clear();
751 m_CurrentAudioID3.Clear();
753 UTILS::FONT::ClearTemporaryFonts();
756 bool CVideoPlayer::OpenInputStream()
758 if (m_pInputStream.use_count() > 1)
759 throw std::runtime_error("m_pInputStream reference count is greater than 1");
760 m_pInputStream.reset();
762 CLog::Log(LOGINFO, "Creating InputStream");
764 m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_item, true);
765 if (m_pInputStream == nullptr)
767 CLog::Log(LOGERROR, "CVideoPlayer::OpenInputStream - unable to create input stream for [{}]",
768 CURL::GetRedacted(m_item.GetPath()));
769 return false;
772 if (!m_pInputStream->Open())
774 CLog::Log(LOGERROR, "CVideoPlayer::OpenInputStream - error opening [{}]",
775 CURL::GetRedacted(m_item.GetPath()));
776 return false;
779 // find any available external subtitles for non dvd files
780 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) &&
781 !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
783 // find any available external subtitles
784 std::vector<std::string> filenames;
785 CUtil::ScanForExternalSubtitles(m_item.GetDynPath(), filenames);
787 // load any subtitles from file item
788 std::string key("subtitle:1");
789 for (unsigned s = 1; m_item.HasProperty(key); key = StringUtils::Format("subtitle:{}", ++s))
790 filenames.push_back(m_item.GetProperty(key).asString());
792 for (unsigned int i=0;i<filenames.size();i++)
794 // if vobsub subtitle:
795 if (URIUtils::HasExtension(filenames[i], ".idx"))
797 std::string strSubFile;
798 if (CUtil::FindVobSubPair( filenames, filenames[i], strSubFile))
799 AddSubtitleFile(filenames[i], strSubFile);
801 else
803 if (!CUtil::IsVobSub(filenames, filenames[i] ))
805 AddSubtitleFile(filenames[i]);
808 } // end loop over all subtitle files
811 m_clock.Reset();
812 m_dvd.Clear();
814 return true;
817 bool CVideoPlayer::OpenDemuxStream()
819 CloseDemuxer();
821 CLog::Log(LOGINFO, "Creating Demuxer");
823 int attempts = 10;
824 while (!m_bStop && attempts-- > 0)
826 m_pDemuxer.reset(CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream));
827 if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
829 continue;
831 else if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE)
833 CLog::Log(LOGDEBUG, "{} - New stream available from input, retry open", __FUNCTION__);
834 continue;
836 break;
839 if (!m_pDemuxer)
841 CLog::Log(LOGERROR, "{} - Error creating demuxer", __FUNCTION__);
842 return false;
845 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
846 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
847 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
848 m_pDemuxer->GetPrograms(m_programs);
849 UpdateContent();
850 m_demuxerSpeed = DVD_PLAYSPEED_NORMAL;
851 m_processInfo->SetStateRealtime(false);
853 int64_t len = m_pInputStream->GetLength();
854 int64_t tim = m_pDemuxer->GetStreamLength();
855 if (len > 0 && tim > 0)
856 m_pInputStream->SetReadRate(static_cast<uint32_t>(len * 1000 / tim));
858 m_offset_pts = 0;
860 return true;
863 void CVideoPlayer::CloseDemuxer()
865 m_pDemuxer.reset();
866 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
868 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
869 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
870 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
873 void CVideoPlayer::OpenDefaultStreams(bool reset)
875 // if input stream dictate, we will open later
876 if (m_dvd.iSelectedAudioStream >= 0 ||
877 m_dvd.iSelectedSPUStream >= 0)
878 return;
880 bool valid;
882 // open video stream
883 valid = false;
885 PredicateVideoFilter vf(m_processInfo->GetVideoSettings().m_VideoStream);
886 for (const auto &stream : m_SelectionStreams.Get(STREAM_VIDEO, vf))
888 if (OpenStream(m_CurrentVideo, stream.demuxerId, stream.id, stream.source, reset))
890 valid = true;
891 break;
894 if (!valid)
896 CloseStream(m_CurrentVideo, true);
897 m_processInfo->ResetVideoCodecInfo();
900 // open audio stream
901 valid = false;
902 if (!m_playerOptions.videoOnly)
904 PredicateAudioFilter af(m_processInfo->GetVideoSettings().m_AudioStream, m_playerOptions.preferStereo);
905 for (const auto &stream : m_SelectionStreams.Get(STREAM_AUDIO, af))
907 if(OpenStream(m_CurrentAudio, stream.demuxerId, stream.id, stream.source, reset))
909 valid = true;
910 break;
915 if(!valid)
917 CloseStream(m_CurrentAudio, true);
918 m_processInfo->ResetAudioCodecInfo();
921 // enable or disable subtitles
922 bool visible = m_processInfo->GetVideoSettings().m_SubtitleOn;
924 // open subtitle stream
925 SelectionStream as = m_SelectionStreams.Get(STREAM_AUDIO, GetAudioStream());
926 PredicateSubtitlePriority psp(as.language,
927 m_processInfo->GetVideoSettings().m_SubtitleStream,
928 m_processInfo->GetVideoSettings().m_SubtitleOn);
929 valid = false;
930 // We need to close CC subtitles to avoid conflicts with external sub stream
931 if (m_CurrentSubtitle.source == STREAM_SOURCE_VIDEOMUX)
932 CloseStream(m_CurrentSubtitle, false);
934 for (const auto &stream : m_SelectionStreams.Get(STREAM_SUBTITLE, psp))
936 if (OpenStream(m_CurrentSubtitle, stream.demuxerId, stream.id, stream.source))
938 valid = true;
939 if(!psp.relevant(stream))
940 visible = false;
941 else if(stream.flags & StreamFlags::FLAG_FORCED)
942 visible = true;
943 break;
946 if(!valid)
947 CloseStream(m_CurrentSubtitle, false);
949 // only set subtitle visibility if state not stored by dvd navigator, because navigator will restore it (if visible)
950 if (!std::dynamic_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream) ||
951 m_playerOptions.state.empty())
953 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
954 if (valid && !visible)
955 SetEnableStream(m_CurrentSubtitle, false);
957 SetSubtitleVisibleInternal(visible);
960 // open teletext stream
961 valid = false;
962 for (const auto &stream : m_SelectionStreams.Get(STREAM_TELETEXT))
964 if (OpenStream(m_CurrentTeletext, stream.demuxerId, stream.id, stream.source))
966 valid = true;
967 break;
970 if(!valid)
971 CloseStream(m_CurrentTeletext, false);
973 // open RDS stream
974 valid = false;
975 for (const auto &stream : m_SelectionStreams.Get(STREAM_RADIO_RDS))
977 if (OpenStream(m_CurrentRadioRDS, stream.demuxerId, stream.id, stream.source))
979 valid = true;
980 break;
983 if(!valid)
984 CloseStream(m_CurrentRadioRDS, false);
986 // open ID3 stream
987 valid = false;
988 for (const auto& stream : m_SelectionStreams.Get(STREAM_AUDIO_ID3))
990 if (OpenStream(m_CurrentAudioID3, stream.demuxerId, stream.id, stream.source))
992 valid = true;
993 break;
996 if (!valid)
997 CloseStream(m_CurrentAudioID3, false);
999 // disable demux streams
1000 if (m_item.IsRemote() && m_pDemuxer)
1002 for (auto &stream : m_SelectionStreams.m_Streams)
1004 if (STREAM_SOURCE_MASK(stream.source) == STREAM_SOURCE_DEMUX)
1006 if (stream.id != m_CurrentVideo.id && stream.id != m_CurrentAudio.id &&
1007 stream.id != m_CurrentSubtitle.id && stream.id != m_CurrentTeletext.id &&
1008 stream.id != m_CurrentRadioRDS.id && stream.id != m_CurrentAudioID3.id)
1010 m_pDemuxer->EnableStream(stream.demuxerId, stream.id, false);
1017 bool CVideoPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
1020 // check if we should read from subtitle demuxer
1021 if (m_pSubtitleDemuxer && m_VideoPlayerSubtitle->AcceptsData())
1023 packet = m_pSubtitleDemuxer->Read();
1025 if(packet)
1027 UpdateCorrection(packet, m_offset_pts);
1028 if(packet->iStreamId < 0)
1029 return true;
1031 stream = m_pSubtitleDemuxer->GetStream(packet->demuxerId, packet->iStreamId);
1032 if (!stream)
1034 CLog::Log(LOGERROR, "{} - Error demux packet doesn't belong to a valid stream",
1035 __FUNCTION__);
1036 return false;
1038 if (stream->source == STREAM_SOURCE_NONE)
1040 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
1041 m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer.get());
1042 UpdateContent();
1044 return true;
1048 // read a data frame from stream.
1049 if (m_pDemuxer)
1050 packet = m_pDemuxer->Read();
1052 if (packet)
1054 // stream changed, update and open defaults
1055 if (packet->iStreamId == DMX_SPECIALID_STREAMCHANGE)
1057 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1058 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1059 m_pDemuxer->GetPrograms(m_programs);
1060 UpdateContent();
1061 OpenDefaultStreams(false);
1063 // reevaluate HasVideo/Audio, we may have switched from/to a radio channel
1064 if(m_CurrentVideo.id < 0)
1065 m_HasVideo = false;
1066 if(m_CurrentAudio.id < 0)
1067 m_HasAudio = false;
1069 return true;
1072 UpdateCorrection(packet, m_offset_pts);
1074 if(packet->iStreamId < 0)
1075 return true;
1077 if(m_pDemuxer)
1079 stream = m_pDemuxer->GetStream(packet->demuxerId, packet->iStreamId);
1080 if (!stream)
1082 CLog::Log(LOGERROR, "{} - Error demux packet doesn't belong to a valid stream",
1083 __FUNCTION__);
1084 return false;
1086 if(stream->source == STREAM_SOURCE_NONE)
1088 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1089 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1090 UpdateContent();
1093 return true;
1095 return false;
1098 bool CVideoPlayer::IsValidStream(const CCurrentStream& stream)
1100 if(stream.id<0)
1101 return true; // we consider non selected as valid
1103 int source = STREAM_SOURCE_MASK(stream.source);
1104 if(source == STREAM_SOURCE_TEXT)
1105 return true;
1106 if (source == STREAM_SOURCE_DEMUX_SUB)
1108 CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.demuxerId, stream.id);
1109 if(st == NULL || st->disabled)
1110 return false;
1111 if(st->type != stream.type)
1112 return false;
1113 return true;
1115 if (source == STREAM_SOURCE_DEMUX)
1117 CDemuxStream* st = m_pDemuxer->GetStream(stream.demuxerId, stream.id);
1118 if(st == NULL || st->disabled)
1119 return false;
1120 if(st->type != stream.type)
1121 return false;
1123 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1125 if (stream.type == STREAM_AUDIO && st->dvdNavId != m_dvd.iSelectedAudioStream)
1126 return false;
1127 if(stream.type == STREAM_SUBTITLE && st->dvdNavId != m_dvd.iSelectedSPUStream)
1128 return false;
1131 return true;
1133 if (source == STREAM_SOURCE_VIDEOMUX)
1135 CDemuxStream* st = m_pCCDemuxer->GetStream(stream.id);
1136 if (st == NULL || st->disabled)
1137 return false;
1138 if (st->type != stream.type)
1139 return false;
1140 return true;
1143 return false;
1146 bool CVideoPlayer::IsBetterStream(const CCurrentStream& current, CDemuxStream* stream)
1148 // Do not reopen non-video streams if we're in video-only mode
1149 if (m_playerOptions.videoOnly && current.type != STREAM_VIDEO)
1150 return false;
1152 if(stream->disabled)
1153 return false;
1155 if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) ||
1156 m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY)))
1158 int source_type;
1160 source_type = STREAM_SOURCE_MASK(current.source);
1161 if (source_type != STREAM_SOURCE_DEMUX &&
1162 source_type != STREAM_SOURCE_NONE)
1163 return false;
1165 source_type = STREAM_SOURCE_MASK(stream->source);
1166 if(source_type != STREAM_SOURCE_DEMUX ||
1167 stream->type != current.type ||
1168 stream->uniqueId == current.id)
1169 return false;
1171 if(current.type == STREAM_AUDIO && stream->dvdNavId == m_dvd.iSelectedAudioStream)
1172 return true;
1173 if(current.type == STREAM_SUBTITLE && stream->dvdNavId == m_dvd.iSelectedSPUStream)
1174 return true;
1175 if(current.type == STREAM_VIDEO && current.id < 0)
1176 return true;
1178 else
1180 if(stream->source == current.source &&
1181 stream->uniqueId == current.id &&
1182 stream->demuxerId == current.demuxerId)
1183 return false;
1185 if(stream->type != current.type)
1186 return false;
1188 if(current.type == STREAM_SUBTITLE)
1189 return false;
1191 if(current.id < 0)
1192 return true;
1194 return false;
1197 void CVideoPlayer::CheckBetterStream(CCurrentStream& current, CDemuxStream* stream)
1199 IDVDStreamPlayer* player = GetStreamPlayer(current.player);
1200 if (!IsValidStream(current) && (player == NULL || player->IsStalled()))
1201 CloseStream(current, true);
1203 if (IsBetterStream(current, stream))
1204 OpenStream(current, stream->demuxerId, stream->uniqueId, stream->source);
1207 void CVideoPlayer::Prepare()
1209 CFFmpegLog::SetLogLevel(1);
1210 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1211 m_processInfo->SetSpeed(1.0);
1212 m_processInfo->SetTempo(1.0);
1213 m_processInfo->SetFrameAdvance(false);
1214 m_State.Clear();
1215 m_CurrentVideo.hint.Clear();
1216 m_CurrentAudio.hint.Clear();
1217 m_CurrentSubtitle.hint.Clear();
1218 m_CurrentTeletext.hint.Clear();
1219 m_CurrentRadioRDS.hint.Clear();
1220 m_CurrentAudioID3.hint.Clear();
1221 m_SpeedState.Reset(DVD_NOPTS_VALUE);
1222 m_offset_pts = 0;
1223 m_CurrentAudio.lastdts = DVD_NOPTS_VALUE;
1224 m_CurrentVideo.lastdts = DVD_NOPTS_VALUE;
1226 IPlayerCallback *cb = &m_callback;
1227 CFileItem fileItem = m_item;
1228 m_outboundEvents->Submit([=]() {
1229 cb->RequestVideoSettings(fileItem);
1232 if (!OpenInputStream())
1234 m_bAbortRequest = true;
1235 m_error = true;
1236 return;
1239 bool discStateRestored = false;
1240 if (std::shared_ptr<CDVDInputStream::IMenus> ptr = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1242 CLog::Log(LOGINFO, "VideoPlayer: playing a file with menu's");
1244 if (!m_playerOptions.state.empty())
1246 discStateRestored = ptr->SetState(m_playerOptions.state);
1248 else if(std::shared_ptr<CDVDInputStreamNavigator> nav = std::dynamic_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream))
1250 nav->EnableSubtitleStream(m_processInfo->GetVideoSettings().m_SubtitleOn);
1254 if (!OpenDemuxStream())
1256 m_bAbortRequest = true;
1257 m_error = true;
1258 return;
1260 // give players a chance to reconsider now codecs are known
1261 CreatePlayers();
1263 if (!discStateRestored)
1264 OpenDefaultStreams();
1267 * Check to see if the demuxer should start at something other than time 0. This will be the case
1268 * if there was a start time specified as part of the "Start from where last stopped" (aka
1269 * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
1271 EDL::Edit edit;
1272 int starttime = 0;
1273 if (m_playerOptions.starttime > 0 || m_playerOptions.startpercent > 0)
1275 if (m_playerOptions.startpercent > 0 && m_pDemuxer)
1277 int playerStartTime = static_cast<int>((static_cast<double>(
1278 m_pDemuxer->GetStreamLength() * (m_playerOptions.startpercent / 100.0))));
1279 starttime = m_Edl.GetTimeAfterRestoringCuts(playerStartTime);
1281 else
1283 starttime = m_Edl.GetTimeAfterRestoringCuts(
1284 static_cast<int>(m_playerOptions.starttime * 1000)); // s to ms
1286 CLog::Log(LOGDEBUG, "{} - Start position set to last stopped position: {}", __FUNCTION__,
1287 starttime);
1289 else if (m_Edl.InEdit(starttime, &edit))
1291 // save last edit times
1292 m_Edl.SetLastEditTime(edit.start);
1293 m_Edl.SetLastEditActionType(edit.action);
1295 if (edit.action == EDL::Action::CUT)
1297 starttime = edit.end;
1298 CLog::Log(LOGDEBUG, "{} - Start position set to end of first cut: {}", __FUNCTION__,
1299 starttime);
1301 else if (edit.action == EDL::Action::COMM_BREAK)
1303 if (m_SkipCommercials)
1305 starttime = edit.end;
1306 CLog::Log(LOGDEBUG, "{} - Start position set to end of first commercial break: {}",
1307 __FUNCTION__, starttime);
1310 const std::shared_ptr<CAdvancedSettings> advancedSettings =
1311 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
1312 if (advancedSettings && advancedSettings->m_EdlDisplayCommbreakNotifications)
1314 const std::string timeString =
1315 StringUtils::SecondsToTimeString(edit.end / 1000, TIME_FORMAT_MM_SS);
1316 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25011), timeString);
1321 if (starttime > 0)
1323 double startpts = DVD_NOPTS_VALUE;
1324 if (m_pDemuxer)
1326 if (m_pDemuxer->SeekTime(starttime, true, &startpts))
1328 FlushBuffers(starttime / 1000 * AV_TIME_BASE, true, true);
1329 CLog::Log(LOGDEBUG, "{} - starting demuxer from: {}", __FUNCTION__, starttime);
1331 else
1332 CLog::Log(LOGDEBUG, "{} - failed to start demuxing from: {}", __FUNCTION__, starttime);
1335 if (m_pSubtitleDemuxer)
1337 if(m_pSubtitleDemuxer->SeekTime(starttime, true, &startpts))
1338 CLog::Log(LOGDEBUG, "{} - starting subtitle demuxer from: {}", __FUNCTION__, starttime);
1339 else
1340 CLog::Log(LOGDEBUG, "{} - failed to start subtitle demuxing from: {}", __FUNCTION__,
1341 starttime);
1344 m_clock.Discontinuity(DVD_MSEC_TO_TIME(starttime));
1347 UpdatePlayState(0);
1349 SetCaching(CACHESTATE_FLUSH);
1352 void CVideoPlayer::Process()
1354 // Try to resolve the correct mime type. This can take some time, for example if a requested
1355 // item is located at a slow/not reachable remote source. So, do mime type detection in vp worker
1356 // thread, not directly when initalizing the player to keep GUI responsible.
1357 m_item.SetMimeTypeForInternetFile();
1359 CServiceBroker::GetWinSystem()->RegisterRenderLoop(this);
1361 Prepare();
1363 while (!m_bAbortRequest)
1365 // check display lost
1366 if (m_displayLost)
1368 CThread::Sleep(50ms);
1369 continue;
1372 // check if in an edit (cut or commercial break) that should be automatically skipped
1373 CheckAutoSceneSkip();
1375 // handle messages send to this thread, like seek or demuxer reset requests
1376 HandleMessages();
1378 if (m_bAbortRequest)
1379 break;
1381 // should we open a new input stream?
1382 if (!m_pInputStream)
1384 if (OpenInputStream() == false)
1386 m_bAbortRequest = true;
1387 break;
1391 // should we open a new demuxer?
1392 if (!m_pDemuxer)
1394 if (m_pInputStream->NextStream() == CDVDInputStream::NEXTSTREAM_NONE)
1395 break;
1397 if (m_pInputStream->IsEOF())
1398 break;
1400 if (OpenDemuxStream() == false)
1402 m_bAbortRequest = true;
1403 break;
1406 // on channel switch we don't want to close stream players at this
1407 // time. we'll get the stream change event later
1408 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) ||
1409 !m_SelectionStreams.m_Streams.empty())
1410 OpenDefaultStreams();
1412 UpdatePlayState(0);
1415 // handle eventual seeks due to playspeed
1416 HandlePlaySpeed();
1418 // update player state
1419 UpdatePlayState(200);
1421 // make sure we run subtitle process here
1422 m_VideoPlayerSubtitle->Process(m_clock.GetClock() + m_State.time_offset - m_VideoPlayerVideo->GetSubtitleDelay(), m_State.time_offset);
1424 // tell demuxer if we want to fill buffers
1425 if (m_demuxerSpeed != DVD_PLAYSPEED_PAUSE)
1427 int audioLevel = 90;
1428 int videoLevel = 90;
1429 bool fillBuffer = false;
1430 if (m_CurrentAudio.id >= 0)
1431 audioLevel = m_VideoPlayerAudio->GetLevel();
1432 if (m_CurrentVideo.id >= 0)
1433 videoLevel = m_processInfo->GetLevelVQ();
1434 if (videoLevel < 85 && audioLevel < 85)
1436 fillBuffer = true;
1438 if (m_pDemuxer)
1439 m_pDemuxer->FillBuffer(fillBuffer);
1442 // if the queues are full, no need to read more
1443 if ((!m_VideoPlayerAudio->AcceptsData() && m_CurrentAudio.id >= 0) ||
1444 (!m_VideoPlayerVideo->AcceptsData() && m_CurrentVideo.id >= 0))
1446 if (m_playSpeed == DVD_PLAYSPEED_PAUSE &&
1447 m_demuxerSpeed != DVD_PLAYSPEED_PAUSE)
1449 if (m_pDemuxer)
1450 m_pDemuxer->SetSpeed(DVD_PLAYSPEED_PAUSE);
1451 m_demuxerSpeed = DVD_PLAYSPEED_PAUSE;
1453 CThread::Sleep(10ms);
1454 continue;
1457 // adjust demuxer speed; some rtsp servers wants to know for i.e. ff
1458 // delay pause until queue is full
1459 if (m_playSpeed != DVD_PLAYSPEED_PAUSE &&
1460 m_demuxerSpeed != m_playSpeed)
1462 if (m_pDemuxer)
1463 m_pDemuxer->SetSpeed(m_playSpeed);
1464 m_demuxerSpeed = m_playSpeed;
1467 DemuxPacket* pPacket = NULL;
1468 CDemuxStream *pStream = NULL;
1469 ReadPacket(pPacket, pStream);
1470 if (pPacket && !pStream)
1472 /* probably a empty packet, just free it and move on */
1473 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
1474 continue;
1477 if (!pPacket)
1479 // when paused, demuxer could be be returning empty
1480 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
1481 continue;
1483 // check for a still frame state
1484 if (std::shared_ptr<CDVDInputStream::IMenus> pStream = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1486 // stills will be skipped
1487 if(m_dvd.state == DVDSTATE_STILL)
1489 if (m_dvd.iDVDStillTime > 0ms)
1491 const auto now = std::chrono::steady_clock::now();
1492 const auto duration = now - m_dvd.iDVDStillStartTime;
1494 if (duration >= m_dvd.iDVDStillTime)
1496 m_dvd.iDVDStillTime = 0ms;
1497 m_dvd.iDVDStillStartTime = {};
1498 m_dvd.state = DVDSTATE_NORMAL;
1499 pStream->SkipStill();
1500 continue;
1506 // if there is another stream available, reopen demuxer
1507 CDVDInputStream::ENextStream next = m_pInputStream->NextStream();
1508 if(next == CDVDInputStream::NEXTSTREAM_OPEN)
1510 CloseDemuxer();
1512 SetCaching(CACHESTATE_DONE);
1513 CLog::Log(LOGINFO, "VideoPlayer: next stream, wait for old streams to be finished");
1514 CloseStream(m_CurrentAudio, true);
1515 CloseStream(m_CurrentVideo, true);
1517 m_CurrentAudio.Clear();
1518 m_CurrentVideo.Clear();
1519 m_CurrentSubtitle.Clear();
1520 continue;
1523 // input stream asked us to just retry
1524 if(next == CDVDInputStream::NEXTSTREAM_RETRY)
1526 CThread::Sleep(100ms);
1527 continue;
1530 if (m_CurrentVideo.inited)
1532 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::VIDEO_DRAIN));
1535 m_CurrentAudio.inited = false;
1536 m_CurrentVideo.inited = false;
1537 m_CurrentSubtitle.inited = false;
1538 m_CurrentTeletext.inited = false;
1539 m_CurrentRadioRDS.inited = false;
1540 m_CurrentAudioID3.inited = false;
1542 // if we are caching, start playing it again
1543 SetCaching(CACHESTATE_DONE);
1545 // while players are still playing, keep going to allow seekbacks
1546 if (m_VideoPlayerAudio->HasData() ||
1547 m_VideoPlayerVideo->HasData())
1549 CThread::Sleep(100ms);
1550 continue;
1553 if (!m_pInputStream->IsEOF())
1554 CLog::Log(LOGINFO, "{} - eof reading from demuxer", __FUNCTION__);
1556 break;
1559 // see if we can find something better to play
1560 CheckBetterStream(m_CurrentAudio, pStream);
1561 CheckBetterStream(m_CurrentVideo, pStream);
1562 CheckBetterStream(m_CurrentSubtitle, pStream);
1563 CheckBetterStream(m_CurrentTeletext, pStream);
1564 CheckBetterStream(m_CurrentRadioRDS, pStream);
1565 CheckBetterStream(m_CurrentAudioID3, pStream);
1567 // demux video stream
1568 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SUBTITLES_PARSECAPTIONS) && CheckIsCurrent(m_CurrentVideo, pStream, pPacket))
1570 if (m_pCCDemuxer)
1572 bool first = true;
1573 while (!m_bAbortRequest)
1575 DemuxPacket *pkt = m_pCCDemuxer->Read(first ? pPacket : NULL);
1576 if (!pkt)
1577 break;
1579 first = false;
1580 if (m_pCCDemuxer->GetNrOfStreams() != m_SelectionStreams.CountTypeOfSource(STREAM_SUBTITLE, STREAM_SOURCE_VIDEOMUX))
1582 m_SelectionStreams.Clear(STREAM_SUBTITLE, STREAM_SOURCE_VIDEOMUX);
1583 m_SelectionStreams.Update(NULL, m_pCCDemuxer.get(), "");
1584 UpdateContent();
1585 OpenDefaultStreams(false);
1587 CDemuxStream *pSubStream = m_pCCDemuxer->GetStream(pkt->iStreamId);
1588 if (pSubStream && m_CurrentSubtitle.id == pkt->iStreamId && m_CurrentSubtitle.source == STREAM_SOURCE_VIDEOMUX)
1589 ProcessSubData(pSubStream, pkt);
1590 else
1591 CDVDDemuxUtils::FreeDemuxPacket(pkt);
1596 if (IsInMenuInternal())
1598 if (std::shared_ptr<CDVDInputStream::IMenus> menu = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1600 double correction = menu->GetTimeStampCorrection();
1601 if (pPacket->dts != DVD_NOPTS_VALUE && pPacket->dts > correction)
1602 pPacket->dts -= correction;
1603 if (pPacket->pts != DVD_NOPTS_VALUE && pPacket->pts > correction)
1604 pPacket->pts -= correction;
1606 if (m_dvd.syncClock)
1608 m_clock.Discontinuity(pPacket->dts);
1609 m_dvd.syncClock = false;
1613 // process the packet
1614 ProcessPacket(pStream, pPacket);
1618 bool CVideoPlayer::CheckIsCurrent(const CCurrentStream& current,
1619 CDemuxStream* stream,
1620 DemuxPacket* pkg)
1622 if(current.id == pkg->iStreamId &&
1623 current.demuxerId == stream->demuxerId &&
1624 current.source == stream->source &&
1625 current.type == stream->type)
1626 return true;
1627 else
1628 return false;
1631 void CVideoPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
1633 // process packet if it belongs to selected stream.
1634 // for dvd's don't allow automatic opening of streams*/
1636 if (CheckIsCurrent(m_CurrentAudio, pStream, pPacket))
1637 ProcessAudioData(pStream, pPacket);
1638 else if (CheckIsCurrent(m_CurrentVideo, pStream, pPacket))
1639 ProcessVideoData(pStream, pPacket);
1640 else if (CheckIsCurrent(m_CurrentSubtitle, pStream, pPacket))
1641 ProcessSubData(pStream, pPacket);
1642 else if (CheckIsCurrent(m_CurrentTeletext, pStream, pPacket))
1643 ProcessTeletextData(pStream, pPacket);
1644 else if (CheckIsCurrent(m_CurrentRadioRDS, pStream, pPacket))
1645 ProcessRadioRDSData(pStream, pPacket);
1646 else if (CheckIsCurrent(m_CurrentAudioID3, pStream, pPacket))
1647 ProcessAudioID3Data(pStream, pPacket);
1648 else
1650 CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
1654 void CVideoPlayer::CheckStreamChanges(CCurrentStream& current, CDemuxStream* stream)
1656 if (current.stream != (void*)stream
1657 || current.changes != stream->changes)
1659 /* check so that dmuxer hints or extra data hasn't changed */
1660 /* if they have, reopen stream */
1662 if (current.hint != CDVDStreamInfo(*stream, true))
1664 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1665 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1666 UpdateContent();
1667 OpenDefaultStreams(false);
1670 current.stream = (void*)stream;
1671 current.changes = stream->changes;
1675 void CVideoPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
1677 CheckStreamChanges(m_CurrentAudio, pStream);
1679 bool checkcont = CheckContinuity(m_CurrentAudio, pPacket);
1680 UpdateTimestamps(m_CurrentAudio, pPacket);
1682 if (checkcont && (m_CurrentAudio.avsync == CCurrentStream::AV_SYNC_CHECK))
1683 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
1685 bool drop = false;
1686 if (CheckPlayerInit(m_CurrentAudio))
1687 drop = true;
1690 * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped.
1692 EDL::Edit edit;
1693 if (CheckSceneSkip(m_CurrentAudio))
1694 drop = true;
1695 else if (m_Edl.InEdit(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts), &edit) &&
1696 edit.action == EDL::Action::MUTE)
1698 drop = true;
1701 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1703 if (!drop)
1704 m_CurrentAudio.packets++;
1707 void CVideoPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
1709 CheckStreamChanges(m_CurrentVideo, pStream);
1710 bool checkcont = false;
1712 if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
1714 checkcont = CheckContinuity(m_CurrentVideo, pPacket);
1715 UpdateTimestamps(m_CurrentVideo, pPacket);
1717 if (checkcont && (m_CurrentVideo.avsync == CCurrentStream::AV_SYNC_CHECK))
1718 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
1720 bool drop = false;
1721 if (CheckPlayerInit(m_CurrentVideo))
1722 drop = true;
1724 if (CheckSceneSkip(m_CurrentVideo))
1725 drop = true;
1727 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1729 if (!drop)
1730 m_CurrentVideo.packets++;
1733 void CVideoPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
1735 CheckStreamChanges(m_CurrentSubtitle, pStream);
1737 UpdateTimestamps(m_CurrentSubtitle, pPacket);
1739 bool drop = false;
1740 if (CheckPlayerInit(m_CurrentSubtitle))
1741 drop = true;
1743 if (CheckSceneSkip(m_CurrentSubtitle))
1744 drop = true;
1746 m_VideoPlayerSubtitle->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1748 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1749 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_NORMAL);
1752 void CVideoPlayer::ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket)
1754 CheckStreamChanges(m_CurrentTeletext, pStream);
1756 UpdateTimestamps(m_CurrentTeletext, pPacket);
1758 bool drop = false;
1759 if (CheckPlayerInit(m_CurrentTeletext))
1760 drop = true;
1762 if (CheckSceneSkip(m_CurrentTeletext))
1763 drop = true;
1765 m_VideoPlayerTeletext->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1768 void CVideoPlayer::ProcessRadioRDSData(CDemuxStream* pStream, DemuxPacket* pPacket)
1770 CheckStreamChanges(m_CurrentRadioRDS, pStream);
1772 UpdateTimestamps(m_CurrentRadioRDS, pPacket);
1774 bool drop = false;
1775 if (CheckPlayerInit(m_CurrentRadioRDS))
1776 drop = true;
1778 if (CheckSceneSkip(m_CurrentRadioRDS))
1779 drop = true;
1781 m_VideoPlayerRadioRDS->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1784 void CVideoPlayer::ProcessAudioID3Data(CDemuxStream* pStream, DemuxPacket* pPacket)
1786 CheckStreamChanges(m_CurrentAudioID3, pStream);
1788 UpdateTimestamps(m_CurrentAudioID3, pPacket);
1790 bool drop = false;
1791 if (CheckPlayerInit(m_CurrentAudioID3))
1792 drop = true;
1794 if (CheckSceneSkip(m_CurrentAudioID3))
1795 drop = true;
1797 m_VideoPlayerAudioID3->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1800 CacheInfo CVideoPlayer::GetCachingTimes()
1802 CacheInfo info{};
1804 if (!m_pInputStream || !m_pDemuxer)
1805 return info;
1807 XFILE::SCacheStatus status;
1808 if (!m_pInputStream->GetCacheStatus(&status))
1809 return info;
1811 const uint64_t& maxforward = status.maxforward;
1812 const uint64_t& cached = status.forward;
1813 const uint32_t& currate = status.currate;
1814 const uint32_t& maxrate = status.maxrate;
1815 const uint32_t& lowrate = status.lowrate;
1817 int64_t length = m_pInputStream->GetLength();
1818 int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR);
1820 if (length <= 0 || remain < 0)
1821 return info;
1823 double queueTime = GetQueueTime();
1824 double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
1825 double queued = 1000.0 * queueTime / play_sbp;
1827 info.level = 0.0;
1828 info.offset = (cached + queued) / length;
1829 info.time = 0.0;
1830 info.valid = true;
1832 if (currate == 0)
1833 return info;
1835 // estimated playback time of current cached bytes
1836 const double cacheTime = (static_cast<double>(cached) / currate) + (queueTime / 1000.0);
1838 // cache level as current forward bytes / max forward bytes [0.0 - 1.0]
1839 const double cacheLevel = (maxforward > 0) ? static_cast<double>(cached) / maxforward : 0.0;
1841 info.time = cacheTime;
1843 if (lowrate > 0)
1845 // buffer is full & our read rate is too low
1846 CLog::Log(LOGDEBUG, "Readrate {} was too low with {} required", lowrate, maxrate);
1847 info.level = -1.0;
1849 else
1850 info.level = cacheLevel;
1852 return info;
1855 void CVideoPlayer::HandlePlaySpeed()
1857 const bool isInMenu = IsInMenuInternal();
1858 const bool tolerateStall =
1859 isInMenu || (m_CurrentVideo.hint.flags & StreamFlags::FLAG_STILL_IMAGES);
1861 if (tolerateStall && m_caching != CACHESTATE_DONE)
1862 SetCaching(CACHESTATE_DONE);
1864 if (m_caching == CACHESTATE_FULL)
1866 CacheInfo cache = GetCachingTimes();
1867 if (cache.valid)
1869 if (cache.level < 0.0)
1871 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(21454), g_localizeStrings.Get(21455));
1872 SetCaching(CACHESTATE_INIT);
1874 // Note: Previously used cache.level >= 1 would keep video stalled
1875 // event after cache was full
1876 // Talk link: https://github.com/xbmc/xbmc/pull/23760
1877 if (cache.time > 8.0)
1878 SetCaching(CACHESTATE_INIT);
1880 else
1882 if ((!m_VideoPlayerAudio->AcceptsData() && m_CurrentAudio.id >= 0) ||
1883 (!m_VideoPlayerVideo->AcceptsData() && m_CurrentVideo.id >= 0))
1884 SetCaching(CACHESTATE_INIT);
1887 // if audio stream stalled, wait until demux queue filled 10%
1888 if (m_pInputStream->IsRealtime() &&
1889 (m_CurrentAudio.id < 0 || m_VideoPlayerAudio->GetLevel() > 10))
1891 SetCaching(CACHESTATE_INIT);
1895 if (m_caching == CACHESTATE_INIT)
1897 // if all enabled streams have been inited we are done
1898 if ((m_CurrentVideo.id >= 0 || m_CurrentAudio.id >= 0) &&
1899 (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_STARTING) &&
1900 (m_CurrentAudio.id < 0 || m_CurrentAudio.syncState != IDVDStreamPlayer::SYNC_STARTING))
1901 SetCaching(CACHESTATE_PLAY);
1903 // handle exceptions
1904 if (m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0)
1906 if ((!m_VideoPlayerAudio->AcceptsData() || !m_VideoPlayerVideo->AcceptsData()) &&
1907 m_cachingTimer.IsTimePast())
1909 SetCaching(CACHESTATE_DONE);
1914 if (m_caching == CACHESTATE_PLAY)
1916 // if all enabled streams have started playing we are done
1917 if ((m_CurrentVideo.id < 0 || !m_VideoPlayerVideo->IsStalled()) &&
1918 (m_CurrentAudio.id < 0 || !m_VideoPlayerAudio->IsStalled()))
1919 SetCaching(CACHESTATE_DONE);
1922 if (m_caching == CACHESTATE_DONE)
1924 if (m_playSpeed == DVD_PLAYSPEED_NORMAL && !tolerateStall)
1926 // take action if audio or video stream is stalled
1927 if (((m_VideoPlayerAudio->IsStalled() && m_CurrentAudio.inited) ||
1928 (m_VideoPlayerVideo->IsStalled() && m_CurrentVideo.inited)) &&
1929 m_syncTimer.IsTimePast())
1931 if (m_pInputStream->IsRealtime())
1933 if ((m_CurrentAudio.id >= 0 && m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1934 m_VideoPlayerAudio->IsStalled()) ||
1935 (m_CurrentVideo.id >= 0 && m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1936 m_processInfo->GetLevelVQ() == 0))
1938 CLog::Log(LOGDEBUG, "Stream stalled, start buffering. Audio: {} - Video: {}",
1939 m_VideoPlayerAudio->GetLevel(), m_processInfo->GetLevelVQ());
1941 if (m_VideoPlayerAudio->AcceptsData() && m_VideoPlayerVideo->AcceptsData())
1942 SetCaching(CACHESTATE_FULL);
1943 else
1944 FlushBuffers(DVD_NOPTS_VALUE, false, true);
1947 else
1949 // start caching if audio and video have run dry
1950 if (m_VideoPlayerAudio->GetLevel() <= 50 &&
1951 m_processInfo->GetLevelVQ() <= 50)
1953 SetCaching(CACHESTATE_FULL);
1955 else if (m_CurrentAudio.id >= 0 && m_CurrentAudio.inited &&
1956 m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1957 m_VideoPlayerAudio->GetLevel() == 0)
1959 CLog::Log(LOGDEBUG,"CVideoPlayer::HandlePlaySpeed - audio stream stalled, triggering re-sync");
1960 FlushBuffers(DVD_NOPTS_VALUE, true, true);
1961 CDVDMsgPlayerSeek::CMode mode;
1962 mode.time = (int)GetUpdatedTime();
1963 mode.backward = false;
1964 mode.accurate = true;
1965 mode.sync = true;
1966 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
1970 // care for live streams
1971 else if (m_pInputStream->IsRealtime())
1973 if (m_CurrentAudio.id >= 0)
1975 double adjust = -1.0; // a unique value
1976 if (m_clock.GetSpeedAdjust() >= 0 && m_VideoPlayerAudio->GetLevel() < 5)
1977 adjust = -0.05;
1979 if (m_clock.GetSpeedAdjust() < 0 && m_VideoPlayerAudio->GetLevel() > 10)
1980 adjust = 0.0;
1982 if (adjust != -1.0)
1984 m_clock.SetSpeedAdjust(adjust);
1991 // sync streams to clock
1992 if ((m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
1993 (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC))
1995 unsigned int threshold = 20;
1996 if (m_pInputStream->IsRealtime())
1997 threshold = 40;
1999 bool video = m_CurrentVideo.id < 0 || (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
2000 (m_CurrentVideo.packets == 0 && m_CurrentAudio.packets > threshold) ||
2001 (!m_VideoPlayerAudio->AcceptsData() && m_processInfo->GetLevelVQ() < 10);
2002 bool audio = m_CurrentAudio.id < 0 || (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
2003 (m_CurrentAudio.packets == 0 && m_CurrentVideo.packets > threshold) ||
2004 (!m_VideoPlayerVideo->AcceptsData() && m_VideoPlayerAudio->GetLevel() < 10);
2006 if (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC &&
2007 (m_CurrentAudio.avsync == CCurrentStream::AV_SYNC_CONT ||
2008 m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_INSYNC))
2010 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2011 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
2012 m_VideoPlayerAudio->SendMessage(
2013 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, m_clock.GetClock()), 1);
2015 else if (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC &&
2016 (m_CurrentVideo.avsync == CCurrentStream::AV_SYNC_CONT ||
2017 m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC))
2019 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2020 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
2021 m_VideoPlayerVideo->SendMessage(
2022 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, m_clock.GetClock()), 1);
2024 else if (video && audio)
2026 double clock = 0;
2027 if (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC)
2028 CLog::Log(LOGDEBUG, "VideoPlayer::Sync - Audio - pts: {:f}, cache: {:f}, totalcache: {:f}",
2029 m_CurrentAudio.starttime, m_CurrentAudio.cachetime, m_CurrentAudio.cachetotal);
2030 if (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC)
2031 CLog::Log(LOGDEBUG, "VideoPlayer::Sync - Video - pts: {:f}, cache: {:f}, totalcache: {:f}",
2032 m_CurrentVideo.starttime, m_CurrentVideo.cachetime, m_CurrentVideo.cachetotal);
2034 if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && m_CurrentVideo.packets > 0 &&
2035 m_playSpeed == DVD_PLAYSPEED_PAUSE)
2037 clock = m_CurrentVideo.starttime;
2039 else if (m_CurrentAudio.starttime != DVD_NOPTS_VALUE && m_CurrentAudio.packets > 0)
2041 if (m_pInputStream->IsRealtime())
2042 clock = m_CurrentAudio.starttime - m_CurrentAudio.cachetotal - DVD_MSEC_TO_TIME(400);
2043 else
2044 clock = m_CurrentAudio.starttime - m_CurrentAudio.cachetime;
2046 if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && (m_CurrentVideo.packets > 0))
2048 if (m_CurrentVideo.starttime - m_CurrentVideo.cachetotal < clock)
2050 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2052 else if (m_CurrentVideo.starttime > m_CurrentAudio.starttime &&
2053 !m_pInputStream->IsRealtime())
2055 int audioLevel = m_VideoPlayerAudio->GetLevel();
2056 //@todo hardcoded 8 seconds in message queue
2057 double maxAudioTime = clock + DVD_MSEC_TO_TIME(80 * audioLevel);
2058 if ((m_CurrentVideo.starttime - m_CurrentVideo.cachetotal) > maxAudioTime)
2059 clock = maxAudioTime;
2060 else
2061 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2065 else if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && m_CurrentVideo.packets > 0)
2067 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2070 m_clock.Discontinuity(clock);
2071 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2072 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
2073 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2074 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
2075 m_VideoPlayerAudio->SendMessage(
2076 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, clock), 1);
2077 m_VideoPlayerVideo->SendMessage(
2078 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, clock), 1);
2079 SetCaching(CACHESTATE_DONE);
2080 UpdatePlayState(0);
2082 m_syncTimer.Set(3000ms);
2084 if (!m_State.streamsReady)
2086 if (m_playerOptions.fullscreen)
2088 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SWITCHTOFULLSCREEN);
2091 IPlayerCallback *cb = &m_callback;
2092 CFileItem fileItem = m_item;
2093 m_outboundEvents->Submit([=]() {
2094 cb->OnAVStarted(fileItem);
2096 m_State.streamsReady = true;
2099 else
2101 // exceptions for which stream players won't start properly
2102 // 1. videoplayer has not detected a keyframe within length of demux buffers
2103 if (m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0 &&
2104 !m_VideoPlayerAudio->AcceptsData() &&
2105 m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_STARTING &&
2106 m_VideoPlayerVideo->IsStalled() &&
2107 m_CurrentVideo.packets > 10)
2109 m_VideoPlayerAudio->AcceptsData();
2110 CLog::Log(LOGWARNING, "VideoPlayer::Sync - stream player video does not start, flushing buffers");
2111 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2116 // handle ff/rw
2117 if (m_playSpeed != DVD_PLAYSPEED_NORMAL && m_playSpeed != DVD_PLAYSPEED_PAUSE)
2119 if (isInMenu)
2121 // this can't be done in menu
2122 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2125 else
2127 bool check = true;
2129 // only check if we have video
2130 if (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC)
2131 check = false;
2132 // video message queue either initiated or already seen eof
2133 else if (m_CurrentVideo.inited == false && m_playSpeed >= 0)
2134 check = false;
2135 // don't check if time has not advanced since last check
2136 else if (m_SpeedState.lasttime == GetTime())
2137 check = false;
2138 // skip if frame at screen has no valid timestamp
2139 else if (m_VideoPlayerVideo->GetCurrentPts() == DVD_NOPTS_VALUE)
2140 check = false;
2141 // skip if frame on screen has not changed
2142 else if (m_SpeedState.lastpts == m_VideoPlayerVideo->GetCurrentPts() &&
2143 (m_SpeedState.lastpts > m_State.dts || m_playSpeed > 0))
2144 check = false;
2146 if (check)
2148 m_SpeedState.lastpts = m_VideoPlayerVideo->GetCurrentPts();
2149 m_SpeedState.lasttime = GetTime();
2150 m_SpeedState.lastabstime = m_clock.GetAbsoluteClock();
2152 double error;
2153 error = m_clock.GetClock() - m_SpeedState.lastpts;
2154 error *= m_playSpeed / abs(m_playSpeed);
2156 // allow a bigger error when going ff, the faster we go
2157 // the the bigger is the error we allow
2158 if (m_playSpeed > DVD_PLAYSPEED_NORMAL)
2160 double errorwin = static_cast<double>(m_playSpeed) / DVD_PLAYSPEED_NORMAL;
2161 if (errorwin > 8.0)
2162 errorwin = 8.0;
2163 error /= errorwin;
2166 if (error > DVD_MSEC_TO_TIME(1000))
2168 error = (m_clock.GetClock() - m_SpeedState.lastseekpts) / 1000;
2170 if (std::abs(error) > 1000 || (m_VideoPlayerVideo->IsRewindStalled() && std::abs(error) > 100))
2172 CLog::Log(LOGDEBUG, "CVideoPlayer::Process - Seeking to catch up, error was: {:f}",
2173 error);
2174 m_SpeedState.lastseekpts = m_clock.GetClock();
2175 int direction = (m_playSpeed > 0) ? 1 : -1;
2176 double iTime = (m_clock.GetClock() + m_State.time_offset + 1000000.0 * direction) / 1000;
2177 CDVDMsgPlayerSeek::CMode mode;
2178 mode.time = iTime;
2179 mode.backward = (m_playSpeed < 0);
2180 mode.accurate = false;
2181 mode.restore = false;
2182 mode.trickplay = true;
2183 mode.sync = false;
2184 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2191 // reset tempo
2192 if (!m_State.cantempo)
2194 float currentTempo = m_processInfo->GetNewTempo();
2195 if (currentTempo != 1.0f)
2197 SetTempo(1.0f);
2202 bool CVideoPlayer::CheckPlayerInit(CCurrentStream& current)
2204 if (current.inited)
2205 return false;
2207 if (current.startpts != DVD_NOPTS_VALUE)
2209 if(current.dts == DVD_NOPTS_VALUE)
2211 CLog::Log(LOGDEBUG, "{} - dropping packet type:{} dts:{:f} to get to start point at {:f}",
2212 __FUNCTION__, current.player, current.dts, current.startpts);
2213 return true;
2216 if ((current.startpts - current.dts) > DVD_SEC_TO_TIME(20))
2218 CLog::Log(LOGDEBUG, "{} - too far to decode before finishing seek", __FUNCTION__);
2219 if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
2220 m_CurrentAudio.startpts = current.dts;
2221 if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
2222 m_CurrentVideo.startpts = current.dts;
2223 if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
2224 m_CurrentSubtitle.startpts = current.dts;
2225 if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE)
2226 m_CurrentTeletext.startpts = current.dts;
2227 if(m_CurrentRadioRDS.startpts != DVD_NOPTS_VALUE)
2228 m_CurrentRadioRDS.startpts = current.dts;
2229 if (m_CurrentAudioID3.startpts != DVD_NOPTS_VALUE)
2230 m_CurrentAudioID3.startpts = current.dts;
2233 if(current.dts < current.startpts)
2235 CLog::Log(LOGDEBUG, "{} - dropping packet type:{} dts:{:f} to get to start point at {:f}",
2236 __FUNCTION__, current.player, current.dts, current.startpts);
2237 return true;
2241 if (current.dts != DVD_NOPTS_VALUE)
2243 current.inited = true;
2244 current.startpts = current.dts;
2246 return false;
2249 void CVideoPlayer::UpdateCorrection(DemuxPacket* pkt, double correction)
2251 pkt->m_ptsOffsetCorrection = correction;
2253 if(pkt->dts != DVD_NOPTS_VALUE)
2254 pkt->dts -= correction;
2255 if(pkt->pts != DVD_NOPTS_VALUE)
2256 pkt->pts -= correction;
2259 void CVideoPlayer::UpdateTimestamps(CCurrentStream& current, DemuxPacket* pPacket)
2261 double dts = current.dts;
2262 /* update stored values */
2263 if(pPacket->dts != DVD_NOPTS_VALUE)
2264 dts = pPacket->dts;
2265 else if(pPacket->pts != DVD_NOPTS_VALUE)
2266 dts = pPacket->pts;
2268 /* calculate some average duration */
2269 if(pPacket->duration != DVD_NOPTS_VALUE)
2270 current.dur = pPacket->duration;
2271 else if(dts != DVD_NOPTS_VALUE && current.dts != DVD_NOPTS_VALUE)
2272 current.dur = 0.1 * (current.dur * 9 + (dts - current.dts));
2274 current.dts = dts;
2276 current.dispTime = pPacket->dispTime;
2279 static void UpdateLimits(double& minimum, double& maximum, double dts)
2281 if(dts == DVD_NOPTS_VALUE)
2282 return;
2283 if(minimum == DVD_NOPTS_VALUE || minimum > dts) minimum = dts;
2284 if(maximum == DVD_NOPTS_VALUE || maximum < dts) maximum = dts;
2287 bool CVideoPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
2289 if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
2290 return false;
2292 if( pPacket->dts == DVD_NOPTS_VALUE || current.dts == DVD_NOPTS_VALUE)
2293 return false;
2295 double mindts = DVD_NOPTS_VALUE, maxdts = DVD_NOPTS_VALUE;
2296 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts);
2297 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts);
2298 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts_end());
2299 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts_end());
2301 /* if we don't have max and min, we can't do anything more */
2302 if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
2303 return false;
2305 double correction = 0.0;
2306 if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000))
2308 CLog::Log(LOGDEBUG,
2309 "CVideoPlayer::CheckContinuity - resync forward :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2310 current.type, current.dts, pPacket->dts, pPacket->dts - maxdts);
2311 correction = pPacket->dts - maxdts;
2314 /* if it's large scale jump, correct for it after having confirmed the jump */
2315 if(pPacket->dts + DVD_MSEC_TO_TIME(500) < current.dts_end())
2317 CLog::Log(
2318 LOGDEBUG,
2319 "CVideoPlayer::CheckContinuity - resync backward :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2320 current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
2321 correction = pPacket->dts - current.dts_end();
2323 else if(pPacket->dts < current.dts)
2325 CLog::Log(LOGDEBUG,
2326 "CVideoPlayer::CheckContinuity - wrapback :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2327 current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
2330 double lastdts = pPacket->dts;
2331 if(correction != 0.0)
2333 // we want the dts values of two streams to close, or for one to be invalid (e.g. from a missing audio stream)
2334 double this_dts = pPacket->dts;
2335 double that_dts = current.type == STREAM_AUDIO ? m_CurrentVideo.lastdts : m_CurrentAudio.lastdts;
2337 if (m_CurrentAudio.id == -1 || m_CurrentVideo.id == -1 ||
2338 current.lastdts == DVD_NOPTS_VALUE ||
2339 fabs(this_dts - that_dts) < DVD_MSEC_TO_TIME(1000))
2341 m_offset_pts += correction;
2342 UpdateCorrection(pPacket, correction);
2343 lastdts = pPacket->dts;
2344 CLog::Log(LOGDEBUG, "CVideoPlayer::CheckContinuity - update correction: {:f}", correction);
2345 if (current.avsync == CCurrentStream::AV_SYNC_CHECK)
2346 current.avsync = CCurrentStream::AV_SYNC_CONT;
2348 else
2350 // not sure yet - flags the packets as unknown until we get confirmation on another audio/video packet
2351 pPacket->dts = DVD_NOPTS_VALUE;
2352 pPacket->pts = DVD_NOPTS_VALUE;
2355 else
2357 if (current.avsync == CCurrentStream::AV_SYNC_CHECK)
2358 current.avsync = CCurrentStream::AV_SYNC_CONT;
2360 current.lastdts = lastdts;
2361 return true;
2364 bool CVideoPlayer::CheckSceneSkip(const CCurrentStream& current)
2366 if (!m_Edl.HasEdits())
2367 return false;
2369 if(current.dts == DVD_NOPTS_VALUE)
2370 return false;
2372 if(current.inited == false)
2373 return false;
2375 EDL::Edit edit;
2376 return m_Edl.InEdit(DVD_TIME_TO_MSEC(current.dts + m_offset_pts), &edit) &&
2377 edit.action == EDL::Action::CUT;
2380 void CVideoPlayer::CheckAutoSceneSkip()
2382 if (!m_Edl.HasEdits())
2383 return;
2385 // Check that there is an audio and video stream.
2386 if((m_CurrentAudio.id < 0 || m_CurrentAudio.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
2387 (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC))
2388 return;
2390 // If there is a startpts defined for either the audio or video stream then VideoPlayer is still
2391 // still decoding frames to get to the previously requested seek point.
2392 if (m_CurrentAudio.inited == false ||
2393 m_CurrentVideo.inited == false)
2394 return;
2396 const int64_t clock = GetTime();
2398 const double correctClock = m_Edl.GetTimeAfterRestoringCuts(clock);
2399 EDL::Edit edit;
2400 if (!m_Edl.InEdit(correctClock, &edit))
2402 // @note: Users are allowed to jump back into EDL commercial breaks
2403 // do not reset the last edit time if the last surpassed edit is a commercial break
2404 if (m_Edl.GetLastEditActionType() != EDL::Action::COMM_BREAK)
2406 m_Edl.ResetLastEditTime();
2408 return;
2411 if (edit.action == EDL::Action::CUT)
2413 if ((m_playSpeed > 0 && correctClock < (edit.start + 1000)) ||
2414 (m_playSpeed < 0 && correctClock < (edit.end - 1000)))
2416 CLog::Log(LOGDEBUG, "{} - Clock in EDL cut [{} - {}]: {}. Automatically skipping over.",
2417 __FUNCTION__, CEdl::MillisecondsToTimeString(edit.start),
2418 CEdl::MillisecondsToTimeString(edit.end), CEdl::MillisecondsToTimeString(clock));
2420 // Seeking either goes to the start or the end of the cut depending on the play direction.
2421 int seek = m_playSpeed >= 0 ? edit.end : edit.start;
2422 if (m_Edl.GetLastEditTime() != seek)
2424 CDVDMsgPlayerSeek::CMode mode;
2425 mode.time = seek;
2426 mode.backward = true;
2427 mode.accurate = true;
2428 mode.restore = false;
2429 mode.trickplay = false;
2430 mode.sync = true;
2431 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2433 m_Edl.SetLastEditTime(seek);
2434 m_Edl.SetLastEditActionType(edit.action);
2438 else if (edit.action == EDL::Action::COMM_BREAK)
2440 // marker for commbreak may be inaccurate. allow user to skip into break from the back
2441 if (m_playSpeed >= 0 && m_Edl.GetLastEditTime() != edit.start && clock < edit.end - 1000)
2443 const std::shared_ptr<CAdvancedSettings> advancedSettings =
2444 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
2445 if (advancedSettings && advancedSettings->m_EdlDisplayCommbreakNotifications)
2447 const std::string timeString =
2448 StringUtils::SecondsToTimeString((edit.end - edit.start) / 1000, TIME_FORMAT_MM_SS);
2449 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25011), timeString);
2452 m_Edl.SetLastEditTime(edit.start);
2453 m_Edl.SetLastEditActionType(edit.action);
2455 if (m_SkipCommercials)
2457 CLog::Log(LOGDEBUG,
2458 "{} - Clock in commercial break [{} - {}]: {}. Automatically skipping to end of "
2459 "commercial break",
2460 __FUNCTION__, CEdl::MillisecondsToTimeString(edit.start),
2461 CEdl::MillisecondsToTimeString(edit.end), CEdl::MillisecondsToTimeString(clock));
2463 CDVDMsgPlayerSeek::CMode mode;
2464 mode.time = edit.end;
2465 mode.backward = true;
2466 mode.accurate = true;
2467 mode.restore = false;
2468 mode.trickplay = false;
2469 mode.sync = true;
2470 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2477 void CVideoPlayer::SynchronizeDemuxer()
2479 if(IsCurrentThread())
2480 return;
2481 if(!m_messenger.IsInited())
2482 return;
2484 auto message = std::make_shared<CDVDMsgGeneralSynchronize>(500ms, SYNCSOURCE_PLAYER);
2485 m_messenger.Put(message);
2486 message->Wait(m_bStop, 0);
2489 IDVDStreamPlayer* CVideoPlayer::GetStreamPlayer(unsigned int target)
2491 if(target == VideoPlayer_AUDIO)
2492 return m_VideoPlayerAudio;
2493 if(target == VideoPlayer_VIDEO)
2494 return m_VideoPlayerVideo;
2495 if(target == VideoPlayer_SUBTITLE)
2496 return m_VideoPlayerSubtitle;
2497 if(target == VideoPlayer_TELETEXT)
2498 return m_VideoPlayerTeletext;
2499 if(target == VideoPlayer_RDS)
2500 return m_VideoPlayerRadioRDS;
2501 if (target == VideoPlayer_ID3)
2502 return m_VideoPlayerAudioID3.get();
2503 return NULL;
2506 void CVideoPlayer::SendPlayerMessage(std::shared_ptr<CDVDMsg> pMsg, unsigned int target)
2508 IDVDStreamPlayer* player = GetStreamPlayer(target);
2509 if(player)
2510 player->SendMessage(std::move(pMsg), 0);
2513 void CVideoPlayer::OnExit()
2515 CLog::Log(LOGINFO, "CVideoPlayer::OnExit()");
2517 // set event to inform openfile something went wrong in case openfile is still waiting for this event
2518 SetCaching(CACHESTATE_DONE);
2520 // close each stream
2521 if (!m_bAbortRequest)
2522 CLog::Log(LOGINFO, "VideoPlayer: eof, waiting for queues to empty");
2524 CFileItem fileItem(m_item);
2525 UpdateFileItemStreamDetails(fileItem);
2527 CloseStream(m_CurrentAudio, !m_bAbortRequest);
2528 CloseStream(m_CurrentVideo, !m_bAbortRequest);
2529 CloseStream(m_CurrentTeletext,!m_bAbortRequest);
2530 CloseStream(m_CurrentRadioRDS, !m_bAbortRequest);
2531 CloseStream(m_CurrentAudioID3, !m_bAbortRequest);
2532 // the generalization principle was abused for subtitle player. actually it is not a stream player like
2533 // video and audio. subtitle player does not run on its own thread, hence waitForBuffers makes
2534 // no sense here. waitForBuffers is abused to clear overlay container (false clears container)
2535 // subtitles are added from video player. after video player has finished, overlays have to be cleared.
2536 CloseStream(m_CurrentSubtitle, false); // clear overlay container
2538 CServiceBroker::GetWinSystem()->UnregisterRenderLoop(this);
2540 IPlayerCallback *cb = &m_callback;
2541 CVideoSettings vs = m_processInfo->GetVideoSettings();
2542 m_outboundEvents->Submit([=]() {
2543 cb->StoreVideoSettings(fileItem, vs);
2546 CBookmark bookmark;
2547 bookmark.totalTimeInSeconds = 0;
2548 bookmark.timeInSeconds = 0;
2549 if (m_State.startTime == 0)
2551 bookmark.totalTimeInSeconds = m_State.timeMax / 1000;
2552 bookmark.timeInSeconds = m_State.time / 1000;
2554 bookmark.player = m_name;
2555 bookmark.playerState = GetPlayerState();
2556 m_outboundEvents->Submit([=]() {
2557 cb->OnPlayerCloseFile(fileItem, bookmark);
2560 // destroy objects
2561 m_renderManager.Flush(false, false);
2562 m_pDemuxer.reset();
2563 m_pSubtitleDemuxer.reset();
2564 m_subtitleDemuxerMap.clear();
2565 m_pCCDemuxer.reset();
2566 if (m_pInputStream.use_count() > 1)
2567 throw std::runtime_error("m_pInputStream reference count is greater than 1");
2568 m_pInputStream.reset();
2570 // clean up all selection streams
2571 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
2573 m_messenger.End();
2575 CFFmpegLog::ClearLogLevel();
2576 m_bStop = true;
2578 bool error = m_error;
2579 bool close = m_bCloseRequest;
2580 m_outboundEvents->Submit([=]() {
2581 if (close)
2582 cb->OnPlayBackStopped();
2583 else if (error)
2584 cb->OnPlayBackError();
2585 else
2586 cb->OnPlayBackEnded();
2590 void CVideoPlayer::HandleMessages()
2592 std::shared_ptr<CDVDMsg> pMsg = nullptr;
2594 while (m_messenger.Get(pMsg, 0ms) == MSGQ_OK)
2596 if (pMsg->IsType(CDVDMsg::PLAYER_OPENFILE) &&
2597 m_messenger.GetPacketCount(CDVDMsg::PLAYER_OPENFILE) == 0)
2599 CDVDMsgOpenFile& msg(*std::static_pointer_cast<CDVDMsgOpenFile>(pMsg));
2601 IPlayerCallback *cb = &m_callback;
2602 CFileItem fileItem(m_item);
2603 UpdateFileItemStreamDetails(fileItem);
2604 CVideoSettings vs = m_processInfo->GetVideoSettings();
2605 m_outboundEvents->Submit([=]() {
2606 cb->StoreVideoSettings(fileItem, vs);
2609 CBookmark bookmark;
2610 bookmark.totalTimeInSeconds = 0;
2611 bookmark.timeInSeconds = 0;
2612 if (m_State.startTime == 0)
2614 bookmark.totalTimeInSeconds = m_State.timeMax / 1000;
2615 bookmark.timeInSeconds = m_State.time / 1000;
2617 bookmark.player = m_name;
2618 bookmark.playerState = GetPlayerState();
2619 m_outboundEvents->Submit([=]() {
2620 cb->OnPlayerCloseFile(fileItem, bookmark);
2623 m_item = msg.GetItem();
2624 m_playerOptions = msg.GetOptions();
2626 m_processInfo->SetPlayTimes(0,0,0,0);
2628 m_outboundEvents->Submit([this]() {
2629 m_callback.OnPlayBackStarted(m_item);
2632 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2633 m_renderManager.Flush(false, false);
2634 m_pDemuxer.reset();
2635 m_pSubtitleDemuxer.reset();
2636 m_subtitleDemuxerMap.clear();
2637 m_pCCDemuxer.reset();
2638 if (m_pInputStream.use_count() > 1)
2639 throw std::runtime_error("m_pInputStream reference count is greater than 1");
2640 m_pInputStream.reset();
2642 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
2644 Prepare();
2646 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) &&
2647 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0 &&
2648 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2650 CDVDMsgPlayerSeek& msg(*std::static_pointer_cast<CDVDMsgPlayerSeek>(pMsg));
2652 if (!m_State.canseek)
2654 m_processInfo->SetStateSeeking(false);
2655 continue;
2658 // skip seeks if player has not finished the last seek
2659 if (m_CurrentVideo.id >= 0 &&
2660 m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC)
2662 double now = m_clock.GetAbsoluteClock();
2663 if (m_playSpeed == DVD_PLAYSPEED_NORMAL &&
2664 (now - m_State.lastSeek)/1000 < 2000 &&
2665 !msg.GetAccurate())
2667 m_processInfo->SetStateSeeking(false);
2668 continue;
2672 if (!msg.GetTrickPlay())
2674 m_processInfo->SeekFinished(0);
2675 SetCaching(CACHESTATE_FLUSH);
2678 double start = DVD_NOPTS_VALUE;
2680 double time = msg.GetTime();
2681 if (msg.GetRelative())
2682 time = (m_clock.GetClock() + m_State.time_offset) / 1000l + time;
2684 time = msg.GetRestore() ? m_Edl.GetTimeAfterRestoringCuts(time) : time;
2686 // if input stream doesn't support ISeekTime, convert back to pts
2687 //! @todo
2688 //! After demuxer we add an offset to input pts so that displayed time and clock are
2689 //! increasing steadily. For seeking we need to determine the boundaries and offset
2690 //! of the desired segment. With the current approach calculated time may point
2691 //! to nirvana
2692 if (m_pInputStream->GetIPosTime() == nullptr)
2693 time -= m_State.time_offset/1000l;
2695 CLog::Log(LOGDEBUG, "demuxer seek to: {:f}", time);
2696 if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start))
2698 CLog::Log(LOGDEBUG, "demuxer seek to: {:f}, success", time);
2699 if(m_pSubtitleDemuxer)
2701 if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward()))
2702 CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: {:f}, success", time);
2704 // dts after successful seek
2705 if (start == DVD_NOPTS_VALUE)
2706 start = DVD_MSEC_TO_TIME(time) - m_State.time_offset;
2708 m_State.dts = start;
2709 m_State.lastSeek = m_clock.GetAbsoluteClock();
2711 FlushBuffers(start, msg.GetAccurate(), msg.GetSync());
2713 else if (m_pDemuxer)
2715 CLog::Log(LOGDEBUG, "VideoPlayer: seek failed or hit end of stream");
2716 // dts after successful seek
2717 if (start == DVD_NOPTS_VALUE)
2718 start = DVD_MSEC_TO_TIME(time) - m_State.time_offset;
2720 m_State.dts = start;
2722 FlushBuffers(start, false, true);
2723 if (m_playSpeed != DVD_PLAYSPEED_PAUSE)
2725 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2729 // set flag to indicate we have finished a seeking request
2730 if(!msg.GetTrickPlay())
2732 m_processInfo->SeekFinished(0);
2735 // dvd's will issue a HOP_CHANNEL that we need to skip
2736 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2737 m_dvd.state = DVDSTATE_SEEK;
2739 m_processInfo->SetStateSeeking(false);
2741 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) &&
2742 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0 &&
2743 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2745 m_processInfo->SeekFinished(0);
2746 SetCaching(CACHESTATE_FLUSH);
2748 CDVDMsgPlayerSeekChapter& msg(*std::static_pointer_cast<CDVDMsgPlayerSeekChapter>(pMsg));
2749 double start = DVD_NOPTS_VALUE;
2750 int offset = 0;
2752 // This should always be the case.
2753 if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
2755 FlushBuffers(start, true, true);
2756 int64_t beforeSeek = GetTime();
2757 offset = DVD_TIME_TO_MSEC(start) - static_cast<int>(beforeSeek);
2758 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2760 else if (m_pInputStream)
2762 CDVDInputStream::IChapter* pChapter = m_pInputStream->GetIChapter();
2763 if (pChapter && pChapter->SeekChapter(msg.GetChapter()))
2765 FlushBuffers(start, true, true);
2766 int64_t beforeSeek = GetTime();
2767 offset = DVD_TIME_TO_MSEC(start) - static_cast<int>(beforeSeek);
2768 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2771 m_processInfo->SeekFinished(offset);
2773 else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
2775 m_CurrentAudio.stream = NULL;
2776 m_CurrentVideo.stream = NULL;
2777 m_CurrentSubtitle.stream = NULL;
2779 // we need to reset the demuxer, probably because the streams have changed
2780 if(m_pDemuxer)
2781 m_pDemuxer->Reset();
2782 if(m_pSubtitleDemuxer)
2783 m_pSubtitleDemuxer->Reset();
2785 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
2787 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetAudioStream>(pMsg);
2789 SelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
2790 if(st.source != STREAM_SOURCE_NONE)
2792 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2794 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2795 if(pStream->SetActiveAudioStream(st.id))
2797 m_dvd.iSelectedAudioStream = -1;
2798 CloseStream(m_CurrentAudio, false);
2799 CDVDMsgPlayerSeek::CMode mode;
2800 mode.time = (int)GetUpdatedTime();
2801 mode.backward = true;
2802 mode.accurate = true;
2803 mode.trickplay = true;
2804 mode.sync = true;
2805 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2808 else
2810 CloseStream(m_CurrentAudio, false);
2811 OpenStream(m_CurrentAudio, st.demuxerId, st.id, st.source);
2812 AdaptForcedSubtitles();
2814 CDVDMsgPlayerSeek::CMode mode;
2815 mode.time = (int)GetUpdatedTime();
2816 mode.backward = true;
2817 mode.accurate = true;
2818 mode.trickplay = true;
2819 mode.sync = true;
2820 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2824 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_VIDEOSTREAM))
2826 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetVideoStream>(pMsg);
2828 SelectionStream& st = m_SelectionStreams.Get(STREAM_VIDEO, pMsg2->GetStreamId());
2829 if (st.source != STREAM_SOURCE_NONE)
2831 if (st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2833 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2834 if (pStream->SetAngle(st.id))
2836 m_dvd.iSelectedVideoStream = st.id;
2838 CDVDMsgPlayerSeek::CMode mode;
2839 mode.time = (int)GetUpdatedTime();
2840 mode.backward = true;
2841 mode.accurate = true;
2842 mode.trickplay = true;
2843 mode.sync = true;
2844 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2847 else
2849 CloseStream(m_CurrentVideo, false);
2850 OpenStream(m_CurrentVideo, st.demuxerId, st.id, st.source);
2851 CDVDMsgPlayerSeek::CMode mode;
2852 mode.time = (int)GetUpdatedTime();
2853 mode.backward = true;
2854 mode.accurate = true;
2855 mode.trickplay = true;
2856 mode.sync = true;
2857 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2861 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
2863 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetSubtitleStream>(pMsg);
2865 SelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
2866 if(st.source != STREAM_SOURCE_NONE)
2868 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2870 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2871 if(pStream->SetActiveSubtitleStream(st.id))
2873 m_dvd.iSelectedSPUStream = -1;
2874 CloseStream(m_CurrentSubtitle, false);
2877 else
2879 CloseStream(m_CurrentSubtitle, false);
2880 OpenStream(m_CurrentSubtitle, st.demuxerId, st.id, st.source);
2884 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
2886 bool isVisible = std::static_pointer_cast<CDVDMsgBool>(pMsg)->m_value;
2888 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
2889 if (!isVisible)
2890 SetEnableStream(m_CurrentSubtitle, false);
2892 SetSubtitleVisibleInternal(isVisible);
2894 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_PROGRAM))
2896 auto msg = std::static_pointer_cast<CDVDMsgInt>(pMsg);
2897 if (m_pDemuxer)
2899 m_pDemuxer->SetProgram(msg->m_value);
2900 FlushBuffers(DVD_NOPTS_VALUE, false, true);
2903 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
2905 SetCaching(CACHESTATE_FLUSH);
2907 auto pMsgPlayerSetState = std::static_pointer_cast<CDVDMsgPlayerSetState>(pMsg);
2909 if (std::shared_ptr<CDVDInputStream::IMenus> ptr = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
2911 if(ptr->SetState(pMsgPlayerSetState->GetState()))
2913 m_dvd.state = DVDSTATE_NORMAL;
2914 m_dvd.iDVDStillStartTime = {};
2915 m_dvd.iDVDStillTime = 0ms;
2919 m_processInfo->SeekFinished(0);
2921 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
2923 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2925 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
2927 int speed = std::static_pointer_cast<CDVDMsgPlayerSetSpeed>(pMsg)->GetSpeed();
2929 // correct our current clock, as it would start going wrong otherwise
2930 if (m_State.timestamp > 0)
2932 double offset;
2933 offset = m_clock.GetAbsoluteClock() - m_State.timestamp;
2934 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2935 offset = DVD_TIME_TO_MSEC(offset);
2936 if (offset > 1000)
2937 offset = 1000;
2938 if (offset < -1000)
2939 offset = -1000;
2940 m_State.time += offset;
2941 m_State.timestamp = m_clock.GetAbsoluteClock();
2944 if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed)
2946 m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL);
2947 m_processInfo->SeekFinished(0);
2950 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && speed != m_playSpeed)
2952 std::shared_ptr<CInputStreamPVRBase> pvrinputstream = std::static_pointer_cast<CInputStreamPVRBase>(m_pInputStream);
2953 pvrinputstream->Pause(speed == 0);
2956 // do a seek after rewind, clock is not in sync with current pts
2957 if ((speed == DVD_PLAYSPEED_NORMAL) &&
2958 (m_playSpeed != DVD_PLAYSPEED_NORMAL) &&
2959 (m_playSpeed != DVD_PLAYSPEED_PAUSE))
2961 double iTime = m_VideoPlayerVideo->GetCurrentPts();
2962 if (iTime == DVD_NOPTS_VALUE)
2963 iTime = m_clock.GetClock();
2964 iTime = (iTime + m_State.time_offset) / 1000;
2966 CDVDMsgPlayerSeek::CMode mode;
2967 mode.time = iTime;
2968 mode.backward = m_playSpeed < 0;
2969 mode.accurate = true;
2970 mode.trickplay = true;
2971 mode.sync = true;
2972 mode.restore = false;
2973 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2976 if (std::static_pointer_cast<CDVDMsgPlayerSetSpeed>(pMsg)->IsTempo())
2977 m_processInfo->SetTempo(static_cast<float>(speed) / DVD_PLAYSPEED_NORMAL);
2978 else
2979 m_processInfo->SetSpeed(static_cast<float>(speed) / DVD_PLAYSPEED_NORMAL);
2981 m_processInfo->SetFrameAdvance(false);
2983 m_playSpeed = speed;
2985 m_caching = CACHESTATE_DONE;
2986 m_clock.SetSpeed(speed);
2987 m_VideoPlayerAudio->SetSpeed(speed);
2988 m_VideoPlayerVideo->SetSpeed(speed);
2989 m_streamPlayerSpeed = speed;
2991 else if (pMsg->IsType(CDVDMsg::PLAYER_FRAME_ADVANCE))
2993 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
2995 int frames = std::static_pointer_cast<CDVDMsgInt>(pMsg)->m_value;
2996 double time = DVD_TIME_BASE / static_cast<double>(m_processInfo->GetVideoFps()) * frames;
2997 m_processInfo->SetFrameAdvance(true);
2998 m_clock.Advance(time);
3001 else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
3002 OnAction(std::static_pointer_cast<CDVDMsgType<CAction>>(pMsg)->m_value);
3003 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
3005 SStartMsg& msg = std::static_pointer_cast<CDVDMsgType<SStartMsg>>(pMsg)->m_value;
3006 if (msg.player == VideoPlayer_AUDIO)
3008 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_WAITSYNC;
3009 m_CurrentAudio.cachetime = msg.cachetime;
3010 m_CurrentAudio.cachetotal = msg.cachetotal;
3011 m_CurrentAudio.starttime = msg.timestamp;
3013 if (msg.player == VideoPlayer_VIDEO)
3015 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_WAITSYNC;
3016 m_CurrentVideo.cachetime = msg.cachetime;
3017 m_CurrentVideo.cachetotal = msg.cachetotal;
3018 m_CurrentVideo.starttime = msg.timestamp;
3020 CLog::Log(LOGDEBUG, "CVideoPlayer::HandleMessages - player started {}", msg.player);
3022 else if (pMsg->IsType(CDVDMsg::PLAYER_REPORT_STATE))
3024 SStateMsg& msg = std::static_pointer_cast<CDVDMsgType<SStateMsg>>(pMsg)->m_value;
3025 if (msg.player == VideoPlayer_AUDIO)
3027 m_CurrentAudio.syncState = msg.syncState;
3029 if (msg.player == VideoPlayer_VIDEO)
3031 m_CurrentVideo.syncState = msg.syncState;
3033 CLog::Log(LOGDEBUG, "CVideoPlayer::HandleMessages - player {} reported state: {}", msg.player,
3034 msg.syncState);
3036 else if (pMsg->IsType(CDVDMsg::SUBTITLE_ADDFILE))
3038 int id = AddSubtitleFile(std::static_pointer_cast<CDVDMsgType<std::string>>(pMsg)->m_value);
3039 if (id >= 0)
3041 SetSubtitle(id);
3042 SetSubtitleVisibleInternal(true);
3045 else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
3047 if (std::static_pointer_cast<CDVDMsgGeneralSynchronize>(pMsg)->Wait(100ms, SYNCSOURCE_PLAYER))
3048 CLog::Log(LOGDEBUG, "CVideoPlayer - CDVDMsg::GENERAL_SYNCHRONIZE");
3050 else if (pMsg->IsType(CDVDMsg::PLAYER_AVCHANGE))
3052 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
3053 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
3054 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3055 IPlayerCallback *cb = &m_callback;
3056 m_outboundEvents->Submit([=]() {
3057 cb->OnAVChange();
3060 else if (pMsg->IsType(CDVDMsg::PLAYER_ABORT))
3062 CLog::Log(LOGDEBUG, "CVideoPlayer - CDVDMsg::PLAYER_ABORT");
3063 m_bAbortRequest = true;
3065 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_UPDATE_STREAM_DETAILS))
3066 m_UpdateStreamDetails = true;
3070 void CVideoPlayer::SetCaching(ECacheState state)
3072 if(state == CACHESTATE_FLUSH)
3074 CacheInfo cache = GetCachingTimes();
3075 if (cache.valid)
3076 state = CACHESTATE_FULL;
3077 else
3078 state = CACHESTATE_INIT;
3081 if(m_caching == state)
3082 return;
3084 CLog::Log(LOGDEBUG, "CVideoPlayer::SetCaching - caching state {}", state);
3085 if (state == CACHESTATE_FULL ||
3086 state == CACHESTATE_INIT)
3088 m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
3090 m_VideoPlayerAudio->SetSpeed(DVD_PLAYSPEED_PAUSE);
3091 m_VideoPlayerVideo->SetSpeed(DVD_PLAYSPEED_PAUSE);
3092 m_streamPlayerSpeed = DVD_PLAYSPEED_PAUSE;
3094 m_cachingTimer.Set(5000ms);
3097 if (state == CACHESTATE_PLAY ||
3098 (state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY))
3100 m_clock.SetSpeed(m_playSpeed);
3101 m_VideoPlayerAudio->SetSpeed(m_playSpeed);
3102 m_VideoPlayerVideo->SetSpeed(m_playSpeed);
3103 m_streamPlayerSpeed = m_playSpeed;
3105 m_caching = state;
3107 m_clock.SetSpeedAdjust(0);
3110 void CVideoPlayer::SetPlaySpeed(int speed)
3112 if (IsPlaying())
3114 CDVDMsgPlayerSetSpeed::SpeedParams params = { speed, false };
3115 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSpeed>(params));
3117 else
3119 m_playSpeed = speed;
3120 m_streamPlayerSpeed = speed;
3124 bool CVideoPlayer::CanPause() const
3126 std::unique_lock<CCriticalSection> lock(m_StateSection);
3127 return m_State.canpause;
3130 void CVideoPlayer::Pause()
3132 // toggle between pause and normal speed
3133 if (m_processInfo->GetNewSpeed() == 0)
3135 SetSpeed(1);
3137 else
3139 SetSpeed(0);
3143 bool CVideoPlayer::HasVideo() const
3145 return m_HasVideo;
3148 bool CVideoPlayer::HasAudio() const
3150 return m_HasAudio;
3153 bool CVideoPlayer::HasRDS() const
3155 return m_CurrentRadioRDS.id >= 0;
3158 bool CVideoPlayer::HasID3() const
3160 return m_CurrentAudioID3.id >= 0;
3163 bool CVideoPlayer::IsPassthrough() const
3165 return m_VideoPlayerAudio->IsPassthrough();
3168 bool CVideoPlayer::CanSeek() const
3170 std::unique_lock<CCriticalSection> lock(m_StateSection);
3171 return m_State.canseek;
3174 void CVideoPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
3176 if (!m_State.canseek)
3177 return;
3179 if (bLargeStep && bChapterOverride && GetChapter() > 0 && GetChapterCount() > 1)
3181 if (!bPlus)
3183 SeekChapter(GetPreviousChapter());
3184 return;
3186 else if (GetChapter() < GetChapterCount())
3188 SeekChapter(GetChapter() + 1);
3189 return;
3193 int64_t seekTarget;
3194 const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
3195 if (advancedSettings->m_videoUseTimeSeeking && m_processInfo->GetMaxTime() > 2000 * advancedSettings->m_videoTimeSeekForwardBig)
3197 if (bLargeStep)
3198 seekTarget = bPlus ? advancedSettings->m_videoTimeSeekForwardBig :
3199 advancedSettings->m_videoTimeSeekBackwardBig;
3200 else
3201 seekTarget = bPlus ? advancedSettings->m_videoTimeSeekForward :
3202 advancedSettings->m_videoTimeSeekBackward;
3203 seekTarget *= 1000;
3204 seekTarget += GetTime();
3206 else
3208 int percent;
3209 if (bLargeStep)
3210 percent = bPlus ? advancedSettings->m_videoPercentSeekForwardBig : advancedSettings->m_videoPercentSeekBackwardBig;
3211 else
3212 percent = bPlus ? advancedSettings->m_videoPercentSeekForward : advancedSettings->m_videoPercentSeekBackward;
3213 seekTarget = static_cast<int64_t>(m_processInfo->GetMaxTime() * (GetPercentage() + percent) / 100);
3216 bool restore = true;
3218 int64_t time = GetTime();
3219 if(g_application.CurrentFileItem().IsStack() &&
3220 (seekTarget > m_processInfo->GetMaxTime() || seekTarget < 0))
3222 g_application.SeekTime((seekTarget - time) * 0.001 + g_application.GetTime());
3223 // warning, don't access any VideoPlayer variables here as
3224 // the VideoPlayer object may have been destroyed
3225 return;
3228 CDVDMsgPlayerSeek::CMode mode;
3229 mode.time = (int)seekTarget;
3230 mode.backward = !bPlus;
3231 mode.accurate = false;
3232 mode.restore = restore;
3233 mode.trickplay = false;
3234 mode.sync = true;
3236 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3237 SynchronizeDemuxer();
3238 if (seekTarget < 0)
3239 seekTarget = 0;
3240 m_callback.OnPlayBackSeek(seekTarget, seekTarget - time);
3243 bool CVideoPlayer::SeekScene(bool bPlus)
3245 if (!m_Edl.HasSceneMarker())
3246 return false;
3249 * There is a 5 second grace period applied when seeking for scenes backwards. If there is no
3250 * grace period applied it is impossible to go backwards past a scene marker.
3252 int64_t clock = GetTime();
3253 if (!bPlus && clock > 5 * 1000) // 5 seconds
3254 clock -= 5 * 1000;
3256 int iScenemarker;
3257 if (m_Edl.GetNextSceneMarker(bPlus, clock, &iScenemarker))
3260 * Seeking is flushed and inaccurate, just like Seek()
3262 CDVDMsgPlayerSeek::CMode mode;
3263 mode.time = iScenemarker;
3264 mode.backward = !bPlus;
3265 mode.accurate = false;
3266 mode.restore = false;
3267 mode.trickplay = false;
3268 mode.sync = true;
3270 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3271 SynchronizeDemuxer();
3272 return true;
3274 return false;
3277 void CVideoPlayer::GetGeneralInfo(std::string& strGeneralInfo)
3279 if (!m_bStop)
3281 double apts = m_VideoPlayerAudio->GetCurrentPts();
3282 double vpts = m_VideoPlayerVideo->GetCurrentPts();
3283 double dDiff = 0;
3285 if (apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE)
3286 dDiff = (apts - vpts) / DVD_TIME_BASE;
3288 std::string strBuf;
3289 std::unique_lock<CCriticalSection> lock(m_StateSection);
3290 if (m_State.cache_bytes >= 0)
3292 strBuf += StringUtils::Format("forward: {} / {:2.0f}% / {:6.3f}s / {:.3f}%",
3293 StringUtils::SizeToString(m_State.cache_bytes),
3294 m_State.cache_level * 100.0, m_State.cache_time,
3295 m_State.cache_offset * 100.0);
3298 strGeneralInfo = StringUtils::Format("Player: a/v:{: 6.3f}, {}", dDiff, strBuf);
3302 void CVideoPlayer::SeekPercentage(float iPercent)
3304 int64_t iTotalTime = m_processInfo->GetMaxTime();
3306 if (!iTotalTime)
3307 return;
3309 SeekTime((int64_t)(iTotalTime * iPercent / 100));
3312 float CVideoPlayer::GetPercentage()
3314 int64_t iTotalTime = m_processInfo->GetMaxTime();
3316 if (!iTotalTime)
3317 return 0.0f;
3319 return GetTime() * 100 / (float)iTotalTime;
3322 float CVideoPlayer::GetCachePercentage() const
3324 std::unique_lock<CCriticalSection> lock(m_StateSection);
3325 return (float) (m_State.cache_offset * 100); // NOTE: Percentage returned is relative
3328 void CVideoPlayer::SetAVDelay(float fValue)
3330 m_processInfo->GetVideoSettingsLocked().SetAudioDelay(fValue);
3331 m_renderManager.SetDelay(static_cast<int>(fValue * 1000.0f));
3334 float CVideoPlayer::GetAVDelay()
3336 return static_cast<float>(m_renderManager.GetDelay()) / 1000.0f;
3339 void CVideoPlayer::SetSubTitleDelay(float fValue)
3341 m_processInfo->GetVideoSettingsLocked().SetSubtitleDelay(fValue);
3342 m_VideoPlayerVideo->SetSubtitleDelay(static_cast<double>(-fValue) * DVD_TIME_BASE);
3345 float CVideoPlayer::GetSubTitleDelay()
3347 return (float) -m_VideoPlayerVideo->GetSubtitleDelay() / DVD_TIME_BASE;
3350 bool CVideoPlayer::GetSubtitleVisible() const
3352 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3354 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
3355 return pStream->IsSubtitleStreamEnabled();
3358 return m_VideoPlayerVideo->IsSubtitleEnabled();
3361 void CVideoPlayer::SetSubtitleVisible(bool bVisible)
3363 m_messenger.Put(
3364 std::make_shared<CDVDMsgBool>(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
3365 m_processInfo->GetVideoSettingsLocked().SetSubtitleVisible(bVisible);
3368 void CVideoPlayer::SetEnableStream(CCurrentStream& current, bool isEnabled)
3370 if (m_pDemuxer && STREAM_SOURCE_MASK(current.source) == STREAM_SOURCE_DEMUX)
3371 m_pDemuxer->EnableStream(current.demuxerId, current.id, isEnabled);
3374 void CVideoPlayer::SetSubtitleVisibleInternal(bool bVisible)
3376 m_VideoPlayerVideo->EnableSubtitle(bVisible);
3378 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3379 std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream)->EnableSubtitleStream(bVisible);
3381 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3384 void CVideoPlayer::SetSubtitleVerticalPosition(int value, bool save)
3386 m_processInfo->GetVideoSettingsLocked().SetSubtitleVerticalPosition(value, save);
3387 m_renderManager.SetSubtitleVerticalPosition(value, save);
3390 std::shared_ptr<TextCacheStruct_t> CVideoPlayer::GetTeletextCache()
3392 if (m_CurrentTeletext.id < 0)
3393 return nullptr;
3395 return m_VideoPlayerTeletext->GetTeletextCache();
3398 bool CVideoPlayer::HasTeletextCache() const
3400 return m_CurrentTeletext.id >= 0;
3403 void CVideoPlayer::LoadPage(int p, int sp, unsigned char* buffer)
3405 if (m_CurrentTeletext.id < 0)
3406 return;
3408 return m_VideoPlayerTeletext->LoadPage(p, sp, buffer);
3411 void CVideoPlayer::SeekTime(int64_t iTime)
3413 int64_t seekOffset = iTime - GetTime();
3415 CDVDMsgPlayerSeek::CMode mode;
3416 mode.time = static_cast<double>(iTime);
3417 mode.backward = true;
3418 mode.accurate = true;
3419 mode.trickplay = false;
3420 mode.sync = true;
3422 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3423 SynchronizeDemuxer();
3424 m_callback.OnPlayBackSeek(iTime, seekOffset);
3425 m_processInfo->SeekFinished(seekOffset);
3428 bool CVideoPlayer::SeekTimeRelative(int64_t iTime)
3430 int64_t abstime = GetTime() + iTime;
3432 // if the file has EDL cuts we can't rely on m_clock for relative seeks
3433 // EDL cuts remove time from the original file, hence we might seek to
3434 // positions too far from the current m_clock position. Seek to absolute
3435 // time instead
3436 if (m_Edl.HasCuts())
3438 SeekTime(abstime);
3439 return true;
3442 CDVDMsgPlayerSeek::CMode mode;
3443 mode.time = (int)iTime;
3444 mode.relative = true;
3445 mode.backward = (iTime < 0) ? true : false;
3446 mode.accurate = false;
3447 mode.trickplay = false;
3448 mode.sync = true;
3450 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3451 m_processInfo->SetStateSeeking(true);
3453 m_callback.OnPlayBackSeek(abstime, iTime);
3454 m_processInfo->SeekFinished(iTime);
3455 return true;
3458 // return the time in milliseconds
3459 int64_t CVideoPlayer::GetTime()
3461 std::unique_lock<CCriticalSection> lock(m_StateSection);
3462 return llrint(m_State.time);
3465 void CVideoPlayer::SetSpeed(float speed)
3467 // can't rewind in menu as seeking isn't possible
3468 // forward is fine
3469 if (speed < 0 && IsInMenu())
3470 return;
3472 if (!CanSeek() && !CanPause())
3473 return;
3475 int iSpeed = static_cast<int>(speed * DVD_PLAYSPEED_NORMAL);
3477 if (!CanSeek())
3479 if ((iSpeed != DVD_PLAYSPEED_NORMAL) && (iSpeed != DVD_PLAYSPEED_PAUSE))
3480 return;
3483 float currentSpeed = m_processInfo->GetNewSpeed();
3484 m_processInfo->SetNewSpeed(speed);
3485 if (iSpeed != currentSpeed)
3487 if (iSpeed == DVD_PLAYSPEED_NORMAL)
3488 m_callback.OnPlayBackResumed();
3489 else if (iSpeed == DVD_PLAYSPEED_PAUSE)
3490 m_callback.OnPlayBackPaused();
3492 if (iSpeed == DVD_PLAYSPEED_NORMAL)
3494 float currentTempo = m_processInfo->GetNewTempo();
3495 if (currentTempo != 1.0f)
3497 SetTempo(currentTempo);
3498 return;
3501 SetPlaySpeed(iSpeed);
3505 void CVideoPlayer::SetTempo(float tempo)
3507 tempo = floor(tempo * 100.0f + 0.5f) / 100.0f;
3508 if (m_processInfo->IsTempoAllowed(tempo))
3510 int speed = tempo * DVD_PLAYSPEED_NORMAL;
3511 CDVDMsgPlayerSetSpeed::SpeedParams params = { speed, true };
3512 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSpeed>(params));
3514 m_processInfo->SetNewTempo(tempo);
3518 void CVideoPlayer::FrameAdvance(int frames)
3520 float currentSpeed = m_processInfo->GetNewSpeed();
3521 if (currentSpeed != DVD_PLAYSPEED_PAUSE)
3522 return;
3524 m_messenger.Put(std::make_shared<CDVDMsgInt>(CDVDMsg::PLAYER_FRAME_ADVANCE, frames));
3527 bool CVideoPlayer::SupportsTempo() const
3529 return m_State.cantempo;
3532 bool CVideoPlayer::OpenStream(CCurrentStream& current, int64_t demuxerId, int iStream, int source, bool reset /*= true*/)
3534 CDemuxStream* stream = NULL;
3535 CDVDStreamInfo hint;
3537 CLog::Log(LOGINFO, "Opening stream: {} source: {}", iStream, source);
3539 if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
3541 int index = m_SelectionStreams.TypeIndexOf(current.type, source, demuxerId, iStream);
3542 if (index < 0)
3543 return false;
3544 const SelectionStream& st = m_SelectionStreams.Get(current.type, index);
3546 CLog::Log(LOGINFO, "Opening Subtitle file: {}", CURL::GetRedacted(st.filename));
3547 m_pSubtitleDemuxer.reset();
3548 const auto demux = m_subtitleDemuxerMap.find(demuxerId);
3549 if (demux == m_subtitleDemuxerMap.end())
3551 CLog::Log(LOGINFO, "No demuxer found for file {}", CURL::GetRedacted(st.filename));
3552 return false;
3555 m_pSubtitleDemuxer = demux->second;
3557 double pts = m_VideoPlayerVideo->GetCurrentPts();
3558 if(pts == DVD_NOPTS_VALUE)
3559 pts = m_CurrentVideo.dts;
3560 if(pts == DVD_NOPTS_VALUE)
3561 pts = 0;
3562 pts += m_offset_pts;
3563 if (!m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE)))
3564 CLog::Log(LOGDEBUG, "{} - failed to start subtitle demuxing from: {:f}", __FUNCTION__, pts);
3565 stream = m_pSubtitleDemuxer->GetStream(demuxerId, iStream);
3566 if(!stream || stream->disabled)
3567 return false;
3569 m_pSubtitleDemuxer->EnableStream(demuxerId, iStream, true);
3571 hint.Assign(*stream, true);
3573 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
3575 int index = m_SelectionStreams.TypeIndexOf(current.type, source, demuxerId, iStream);
3576 if(index < 0)
3577 return false;
3579 hint.Clear();
3580 hint.filename = m_SelectionStreams.Get(current.type, index).filename;
3581 hint.fpsscale = m_CurrentVideo.hint.fpsscale;
3582 hint.fpsrate = m_CurrentVideo.hint.fpsrate;
3584 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX)
3586 if(!m_pDemuxer)
3587 return false;
3589 m_pDemuxer->OpenStream(demuxerId, iStream);
3591 stream = m_pDemuxer->GetStream(demuxerId, iStream);
3592 if (!stream || stream->disabled)
3593 return false;
3595 hint.Assign(*stream, true);
3597 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3598 hint.filename = "dvd";
3600 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_VIDEOMUX)
3602 if(!m_pCCDemuxer)
3603 return false;
3605 stream = m_pCCDemuxer->GetStream(iStream);
3606 if(!stream || stream->disabled)
3607 return false;
3609 hint.Assign(*stream, false);
3612 bool res;
3613 switch(current.type)
3615 case STREAM_AUDIO:
3616 res = OpenAudioStream(hint, reset);
3617 break;
3618 case STREAM_VIDEO:
3619 res = OpenVideoStream(hint, reset);
3620 break;
3621 case STREAM_SUBTITLE:
3622 res = OpenSubtitleStream(hint);
3623 break;
3624 case STREAM_TELETEXT:
3625 res = OpenTeletextStream(hint);
3626 break;
3627 case STREAM_RADIO_RDS:
3628 res = OpenRadioRDSStream(hint);
3629 break;
3630 case STREAM_AUDIO_ID3:
3631 res = OpenAudioID3Stream(hint);
3632 break;
3633 default:
3634 res = false;
3635 break;
3638 if (res)
3640 int oldId = current.id;
3641 current.id = iStream;
3642 current.demuxerId = demuxerId;
3643 current.source = source;
3644 current.hint = hint;
3645 current.stream = (void*)stream;
3646 current.lastdts = DVD_NOPTS_VALUE;
3647 if (oldId >= 0 && current.avsync != CCurrentStream::AV_SYNC_FORCE)
3648 current.avsync = CCurrentStream::AV_SYNC_CHECK;
3649 if(stream)
3650 current.changes = stream->changes;
3652 else
3654 if(stream)
3656 /* mark stream as disabled, to disallow further attempts*/
3657 CLog::Log(LOGWARNING, "{} - Unsupported stream {}. Stream disabled.", __FUNCTION__,
3658 stream->uniqueId);
3659 stream->disabled = true;
3663 UpdateContentState();
3664 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
3665 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
3666 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3668 return res;
3671 bool CVideoPlayer::OpenAudioStream(CDVDStreamInfo& hint, bool reset)
3673 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentAudio.player);
3674 if(player == nullptr)
3675 return false;
3677 if(m_CurrentAudio.id < 0 ||
3678 m_CurrentAudio.hint != hint)
3680 if (!player->OpenStream(hint))
3681 return false;
3683 player->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, m_displayLost), 1);
3685 static_cast<IDVDStreamPlayerAudio*>(player)->SetSpeed(m_streamPlayerSpeed);
3686 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_STARTING;
3687 m_CurrentAudio.packets = 0;
3689 else if (reset)
3690 player->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_RESET), 0);
3692 m_HasAudio = true;
3694 static_cast<IDVDStreamPlayerAudio*>(player)->SendMessage(
3695 std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_REQUEST_STATE), 1);
3697 return true;
3700 bool CVideoPlayer::OpenVideoStream(CDVDStreamInfo& hint, bool reset)
3702 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3704 /* set aspect ratio as requested by navigator for dvd's */
3705 float aspect = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream)->GetVideoAspectRatio();
3706 if (aspect != 0.0f)
3708 hint.aspect = static_cast<double>(aspect);
3709 hint.forced_aspect = true;
3711 hint.dvd = true;
3713 else if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
3715 // set framerate if not set by demuxer
3716 if (hint.fpsrate == 0 || hint.fpsscale == 0)
3718 int fpsidx = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_PVRPLAYBACK_FPS);
3719 if (fpsidx == 1)
3721 hint.fpsscale = 1000;
3722 hint.fpsrate = 50000;
3724 else if (fpsidx == 2)
3726 hint.fpsscale = 1001;
3727 hint.fpsrate = 60000;
3732 std::shared_ptr<CDVDInputStream::IMenus> pMenus = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
3733 if(pMenus && pMenus->IsInMenu())
3734 hint.stills = true;
3736 if (hint.stereo_mode.empty())
3738 CGUIComponent *gui = CServiceBroker::GetGUI();
3739 if (gui != nullptr)
3741 const CStereoscopicsManager &stereoscopicsManager = gui->GetStereoscopicsManager();
3742 hint.stereo_mode = stereoscopicsManager.DetectStereoModeByString(m_item.GetPath());
3746 if (hint.flags & AV_DISPOSITION_ATTACHED_PIC)
3747 return false;
3749 // set desired refresh rate
3750 if (m_CurrentVideo.id < 0 && m_playerOptions.fullscreen &&
3751 CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot() && hint.fpsrate != 0 &&
3752 hint.fpsscale != 0)
3754 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) != ADJUST_REFRESHRATE_OFF)
3756 const double framerate = DVD_TIME_BASE / CDVDCodecUtils::NormalizeFrameduration(
3757 (double)DVD_TIME_BASE * hint.fpsscale /
3758 (hint.fpsrate * (hint.interlaced ? 2 : 1)));
3760 RESOLUTION res = CResolutionUtils::ChooseBestResolution(static_cast<float>(framerate), hint.width, hint.height, !hint.stereo_mode.empty());
3761 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(res, false);
3762 m_renderManager.TriggerUpdateResolution(framerate, hint.width, hint.height, hint.stereo_mode);
3766 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentVideo.player);
3767 if(player == nullptr)
3768 return false;
3770 if(m_CurrentVideo.id < 0 ||
3771 m_CurrentVideo.hint != hint)
3773 if (hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_H264)
3774 m_pCCDemuxer.reset();
3776 if (!player->OpenStream(hint))
3777 return false;
3779 player->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, m_displayLost), 1);
3781 // look for any EDL files
3782 m_Edl.Clear();
3783 float fFramesPerSecond = 0.0f;
3784 if (m_CurrentVideo.hint.fpsscale > 0.0f)
3785 fFramesPerSecond = static_cast<float>(m_CurrentVideo.hint.fpsrate) / static_cast<float>(m_CurrentVideo.hint.fpsscale);
3786 m_Edl.ReadEditDecisionLists(m_item, fFramesPerSecond);
3787 CServiceBroker::GetDataCacheCore().SetEditList(m_Edl.GetEditList());
3788 CServiceBroker::GetDataCacheCore().SetCuts(m_Edl.GetCutMarkers());
3789 CServiceBroker::GetDataCacheCore().SetSceneMarkers(m_Edl.GetSceneMarkers());
3791 static_cast<IDVDStreamPlayerVideo*>(player)->SetSpeed(m_streamPlayerSpeed);
3792 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_STARTING;
3793 m_CurrentVideo.packets = 0;
3795 else if (reset)
3796 player->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_RESET), 0);
3798 m_HasVideo = true;
3800 static_cast<IDVDStreamPlayerVideo*>(player)->SendMessage(
3801 std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_REQUEST_STATE), 1);
3803 // open CC demuxer if video is mpeg2
3804 if ((hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_H264) && !m_pCCDemuxer)
3806 m_pCCDemuxer = std::make_unique<CDVDDemuxCC>(hint.codec);
3807 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_VIDEOMUX);
3810 return true;
3813 bool CVideoPlayer::OpenSubtitleStream(const CDVDStreamInfo& hint)
3815 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentSubtitle.player);
3816 if(player == nullptr)
3817 return false;
3819 if(m_CurrentSubtitle.id < 0 ||
3820 m_CurrentSubtitle.hint != hint)
3822 if (!player->OpenStream(hint))
3823 return false;
3826 return true;
3829 void CVideoPlayer::AdaptForcedSubtitles()
3831 SelectionStream ss = m_SelectionStreams.Get(STREAM_SUBTITLE, GetSubtitle());
3832 if (ss.flags & StreamFlags::FLAG_FORCED)
3834 SelectionStream as = m_SelectionStreams.Get(STREAM_AUDIO, GetAudioStream());
3835 bool isVisible = false;
3836 for (const auto &stream : m_SelectionStreams.Get(STREAM_SUBTITLE))
3838 if (stream.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(stream.language, as.language))
3840 if (OpenStream(m_CurrentSubtitle, stream.demuxerId, stream.id, stream.source))
3842 isVisible = true;
3843 break;
3847 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
3848 if (!isVisible)
3849 SetEnableStream(m_CurrentSubtitle, false);
3851 SetSubtitleVisibleInternal(isVisible);
3855 bool CVideoPlayer::OpenTeletextStream(CDVDStreamInfo& hint)
3857 if (!m_VideoPlayerTeletext->CheckStream(hint))
3858 return false;
3860 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentTeletext.player);
3861 if(player == nullptr)
3862 return false;
3864 if(m_CurrentTeletext.id < 0 ||
3865 m_CurrentTeletext.hint != hint)
3867 if (!player->OpenStream(hint))
3868 return false;
3871 return true;
3874 bool CVideoPlayer::OpenRadioRDSStream(CDVDStreamInfo& hint)
3876 if (!m_VideoPlayerRadioRDS->CheckStream(hint))
3877 return false;
3879 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentRadioRDS.player);
3880 if(player == nullptr)
3881 return false;
3883 if(m_CurrentRadioRDS.id < 0 ||
3884 m_CurrentRadioRDS.hint != hint)
3886 if (!player->OpenStream(hint))
3887 return false;
3890 return true;
3893 bool CVideoPlayer::OpenAudioID3Stream(CDVDStreamInfo& hint)
3895 if (!m_VideoPlayerAudioID3->CheckStream(hint))
3896 return false;
3898 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentAudioID3.player);
3899 if (player == nullptr)
3900 return false;
3902 if (m_CurrentAudioID3.id < 0 || m_CurrentAudioID3.hint != hint)
3904 if (!player->OpenStream(hint))
3905 return false;
3908 return true;
3911 bool CVideoPlayer::CloseStream(CCurrentStream& current, bool bWaitForBuffers)
3913 if (current.id < 0)
3914 return false;
3916 CLog::Log(LOGINFO, "Closing stream player {}", current.player);
3918 if(bWaitForBuffers)
3919 SetCaching(CACHESTATE_DONE);
3921 SetEnableStream(current, false);
3923 IDVDStreamPlayer* player = GetStreamPlayer(current.player);
3924 if (player)
3926 if ((current.type == STREAM_AUDIO && current.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
3927 (current.type == STREAM_VIDEO && current.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
3928 m_bAbortRequest)
3929 bWaitForBuffers = false;
3930 player->CloseStream(bWaitForBuffers);
3933 current.Clear();
3934 return true;
3937 void CVideoPlayer::FlushBuffers(double pts, bool accurate, bool sync)
3939 CLog::Log(LOGDEBUG, "CVideoPlayer::FlushBuffers - flushing buffers");
3941 double startpts;
3942 if (accurate)
3943 startpts = pts;
3944 else
3945 startpts = DVD_NOPTS_VALUE;
3947 m_SpeedState.Reset(pts);
3949 if (sync)
3951 m_CurrentAudio.inited = false;
3952 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_FORCE;
3953 m_CurrentAudio.starttime = DVD_NOPTS_VALUE;
3954 m_CurrentVideo.inited = false;
3955 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_FORCE;
3956 m_CurrentVideo.starttime = DVD_NOPTS_VALUE;
3957 m_CurrentSubtitle.inited = false;
3958 m_CurrentTeletext.inited = false;
3959 m_CurrentRadioRDS.inited = false;
3962 m_CurrentAudio.dts = DVD_NOPTS_VALUE;
3963 m_CurrentAudio.startpts = startpts;
3964 m_CurrentAudio.packets = 0;
3966 m_CurrentVideo.dts = DVD_NOPTS_VALUE;
3967 m_CurrentVideo.startpts = startpts;
3968 m_CurrentVideo.packets = 0;
3970 m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
3971 m_CurrentSubtitle.startpts = startpts;
3972 m_CurrentSubtitle.packets = 0;
3974 m_CurrentTeletext.dts = DVD_NOPTS_VALUE;
3975 m_CurrentTeletext.startpts = startpts;
3976 m_CurrentTeletext.packets = 0;
3978 m_CurrentRadioRDS.dts = DVD_NOPTS_VALUE;
3979 m_CurrentRadioRDS.startpts = startpts;
3980 m_CurrentRadioRDS.packets = 0;
3982 m_CurrentAudioID3.dts = DVD_NOPTS_VALUE;
3983 m_CurrentAudioID3.startpts = startpts;
3984 m_CurrentAudioID3.packets = 0;
3986 m_VideoPlayerAudio->Flush(sync);
3987 m_VideoPlayerVideo->Flush(sync);
3988 m_VideoPlayerSubtitle->Flush();
3989 m_VideoPlayerTeletext->Flush();
3990 m_VideoPlayerRadioRDS->Flush();
3991 m_VideoPlayerAudioID3->Flush();
3993 if (m_playSpeed == DVD_PLAYSPEED_NORMAL || m_playSpeed == DVD_PLAYSPEED_PAUSE ||
3994 (m_playSpeed >= DVD_PLAYSPEED_NORMAL * m_processInfo->MinTempoPlatform() &&
3995 m_playSpeed <= DVD_PLAYSPEED_NORMAL * m_processInfo->MaxTempoPlatform()))
3997 // make sure players are properly flushed, should put them in stalled state
3998 auto msg = std::make_shared<CDVDMsgGeneralSynchronize>(1s, SYNCSOURCE_AUDIO | SYNCSOURCE_VIDEO);
3999 m_VideoPlayerAudio->SendMessage(msg, 1);
4000 m_VideoPlayerVideo->SendMessage(msg, 1);
4001 msg->Wait(m_bStop, 0);
4003 // purge any pending PLAYER_STARTED messages
4004 m_messenger.Flush(CDVDMsg::PLAYER_STARTED);
4006 // we should now wait for init cache
4007 SetCaching(CACHESTATE_FLUSH);
4008 if (sync)
4010 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_STARTING;
4011 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_STARTING;
4015 if(pts != DVD_NOPTS_VALUE && sync)
4016 m_clock.Discontinuity(pts);
4017 UpdatePlayState(0);
4019 m_demuxerSpeed = DVD_PLAYSPEED_NORMAL;
4020 if (m_pDemuxer)
4021 m_pDemuxer->SetSpeed(DVD_PLAYSPEED_NORMAL);
4024 // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
4025 int CVideoPlayer::OnDiscNavResult(void* pData, int iMessage)
4027 if (!m_pInputStream)
4028 return 0;
4030 #if defined(HAVE_LIBBLURAY)
4031 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
4033 switch (iMessage)
4035 case BD_EVENT_MENU_OVERLAY:
4036 m_overlayContainer.ProcessAndAddOverlayIfValid(
4037 *static_cast<std::shared_ptr<CDVDOverlay>*>(pData));
4038 break;
4039 case BD_EVENT_PLAYLIST_STOP:
4040 m_dvd.state = DVDSTATE_NORMAL;
4041 m_dvd.iDVDStillTime = 0ms;
4042 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_FLUSH));
4043 break;
4044 case BD_EVENT_AUDIO_STREAM:
4045 m_dvd.iSelectedAudioStream = *static_cast<int*>(pData);
4046 break;
4048 case BD_EVENT_PG_TEXTST_STREAM:
4049 m_dvd.iSelectedSPUStream = *static_cast<int*>(pData);
4050 break;
4051 case BD_EVENT_PG_TEXTST:
4053 bool enable = (*static_cast<int*>(pData) != 0);
4054 m_VideoPlayerVideo->EnableSubtitle(enable);
4056 break;
4057 case BD_EVENT_STILL_TIME:
4059 if (m_dvd.state != DVDSTATE_STILL)
4061 // else notify the player we have received a still frame
4063 m_dvd.iDVDStillTime = std::chrono::milliseconds(*static_cast<int*>(pData));
4064 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4066 if (m_dvd.iDVDStillTime > 0ms)
4067 m_dvd.iDVDStillTime *= 1000;
4069 /* adjust for the output delay in the video queue */
4070 std::chrono::milliseconds time = 0ms;
4071 if (m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0ms)
4073 time = std::chrono::milliseconds(
4074 static_cast<int>(m_VideoPlayerVideo->GetOutputDelay() / (DVD_TIME_BASE / 1000)));
4075 if (time < 10000ms && time > 0ms)
4076 m_dvd.iDVDStillTime += time;
4078 m_dvd.state = DVDSTATE_STILL;
4079 CLog::Log(LOGDEBUG, "BD_EVENT_STILL_TIME - waiting {} msec, with delay of {} msec",
4080 m_dvd.iDVDStillTime.count(), time.count());
4083 break;
4084 case BD_EVENT_STILL:
4086 bool on = static_cast<bool>(*static_cast<int*>(pData));
4087 if (on && m_dvd.state != DVDSTATE_STILL)
4089 m_dvd.state = DVDSTATE_STILL;
4090 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4091 m_dvd.iDVDStillTime = 0ms;
4092 CLog::Log(LOGDEBUG, "CDVDPlayer::OnDVDNavResult - libbluray DVDSTATE_STILL start");
4094 else if (!on && m_dvd.state == DVDSTATE_STILL)
4096 m_dvd.state = DVDSTATE_NORMAL;
4097 m_dvd.iDVDStillStartTime = {};
4098 m_dvd.iDVDStillTime = 0ms;
4099 CLog::Log(LOGDEBUG, "CDVDPlayer::OnDVDNavResult - libbluray DVDSTATE_STILL end");
4102 break;
4103 case BD_EVENT_MENU_ERROR:
4105 m_dvd.state = DVDSTATE_NORMAL;
4106 CLog::Log(LOGDEBUG, "CVideoPlayer::OnDiscNavResult - libbluray menu not supported (DVDSTATE_NORMAL)");
4107 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25008), g_localizeStrings.Get(25009));
4109 break;
4110 case BD_EVENT_ENC_ERROR:
4112 m_dvd.state = DVDSTATE_NORMAL;
4113 CLog::Log(LOGDEBUG, "CVideoPlayer::OnDiscNavResult - libbluray the disc/file is encrypted and can't be played (DVDSTATE_NORMAL)");
4114 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(16026), g_localizeStrings.Get(29805));
4116 break;
4117 default:
4118 break;
4121 return 0;
4123 #endif
4125 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
4127 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
4129 switch (iMessage)
4131 case DVDNAV_STILL_FRAME:
4133 //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
4135 dvdnav_still_event_t *still_event = static_cast<dvdnav_still_event_t*>(pData);
4136 // should wait the specified time here while we let the player running
4137 // after that call dvdnav_still_skip(m_dvdnav);
4139 if (m_dvd.state != DVDSTATE_STILL)
4141 // else notify the player we have received a still frame
4143 if(still_event->length < 0xff)
4144 m_dvd.iDVDStillTime = std::chrono::seconds(still_event->length);
4145 else
4146 m_dvd.iDVDStillTime = 0ms;
4148 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4150 /* adjust for the output delay in the video queue */
4151 std::chrono::milliseconds time = 0ms;
4152 if (m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0ms)
4154 time = std::chrono::milliseconds(
4155 static_cast<int>(m_VideoPlayerVideo->GetOutputDelay() / (DVD_TIME_BASE / 1000)));
4156 if (time < 10000ms && time > 0ms)
4157 m_dvd.iDVDStillTime += time;
4159 m_dvd.state = DVDSTATE_STILL;
4160 CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME - waiting {} sec, with delay of {} msec",
4161 still_event->length, time.count());
4163 return NAVRESULT_HOLD;
4165 break;
4166 case DVDNAV_SPU_CLUT_CHANGE:
4168 m_VideoPlayerSubtitle->SendMessage(
4169 std::make_shared<CDVDMsgSubtitleClutChange>((uint8_t*)pData));
4171 break;
4172 case DVDNAV_SPU_STREAM_CHANGE:
4174 dvdnav_spu_stream_change_event_t* event = static_cast<dvdnav_spu_stream_change_event_t*>(pData);
4176 int iStream = event->physical_wide;
4177 bool visible = !(iStream & 0x80);
4179 SetSubtitleVisibleInternal(visible);
4181 if (iStream >= 0)
4182 m_dvd.iSelectedSPUStream = (iStream & ~0x80);
4183 else
4184 m_dvd.iSelectedSPUStream = -1;
4186 m_CurrentSubtitle.stream = NULL;
4188 break;
4189 case DVDNAV_AUDIO_STREAM_CHANGE:
4191 dvdnav_audio_stream_change_event_t* event = static_cast<dvdnav_audio_stream_change_event_t*>(pData);
4192 // Tell system what audiostream should be opened by default
4193 m_dvd.iSelectedAudioStream = event->physical;
4194 m_CurrentAudio.stream = NULL;
4196 break;
4197 case DVDNAV_HIGHLIGHT:
4199 //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
4200 int iButton = pStream->GetCurrentButton();
4201 CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button {}", iButton);
4202 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_NORMAL);
4204 break;
4205 case DVDNAV_VTS_CHANGE:
4207 //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
4208 CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
4210 //Make sure we clear all the old overlays here, or else old forced items are left.
4211 m_overlayContainer.Clear();
4213 //Force an aspect ratio that is set in the dvdheaders if available
4214 m_CurrentVideo.hint.aspect = static_cast<double>(pStream->GetVideoAspectRatio());
4215 if( m_VideoPlayerVideo->IsInited() )
4216 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgDouble>(
4217 CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
4219 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
4220 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
4221 UpdateContent();
4223 return NAVRESULT_HOLD;
4225 break;
4226 case DVDNAV_CELL_CHANGE:
4228 //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
4229 CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
4231 if (m_dvd.state != DVDSTATE_STILL)
4232 m_dvd.state = DVDSTATE_NORMAL;
4234 break;
4235 case DVDNAV_NAV_PACKET:
4237 //pci_t* pci = (pci_t*)pData;
4239 // this should be possible to use to make sure we get
4240 // seamless transitions over these boundaries
4241 // if we remember the old vobunits boundaries
4242 // when a packet comes out of demuxer that has
4243 // pts values outside that boundary, it belongs
4244 // to the new vobunit, which has new timestamps
4245 UpdatePlayState(0);
4247 break;
4248 case DVDNAV_HOP_CHANNEL:
4250 // This event is issued whenever a non-seamless operation has been executed.
4251 // Applications with fifos should drop the fifos content to speed up responsiveness.
4252 CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
4253 if(m_dvd.state == DVDSTATE_SEEK)
4254 m_dvd.state = DVDSTATE_NORMAL;
4255 else
4257 bool sync = !IsInMenuInternal();
4258 FlushBuffers(DVD_NOPTS_VALUE, false, sync);
4259 m_dvd.syncClock = true;
4260 m_dvd.state = DVDSTATE_NORMAL;
4261 if (m_pDemuxer)
4262 m_pDemuxer->Flush();
4265 return NAVRESULT_ERROR;
4267 break;
4268 case DVDNAV_STOP:
4270 CLog::Log(LOGDEBUG, "DVDNAV_STOP");
4271 m_dvd.state = DVDSTATE_NORMAL;
4273 break;
4274 case DVDNAV_ERROR:
4276 CLog::Log(LOGDEBUG, "DVDNAV_ERROR");
4277 m_dvd.state = DVDSTATE_NORMAL;
4278 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(16026),
4279 g_localizeStrings.Get(16029));
4281 break;
4282 default:
4284 break;
4287 return NAVRESULT_NOP;
4290 void CVideoPlayer::GetVideoResolution(unsigned int &width, unsigned int &height)
4292 RESOLUTION_INFO res = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
4293 width = res.iWidth;
4294 height = res.iHeight;
4297 bool CVideoPlayer::OnAction(const CAction &action)
4299 #define THREAD_ACTION(action) \
4300 do \
4302 if (!IsCurrentThread()) \
4304 m_messenger.Put( \
4305 std::make_shared<CDVDMsgType<CAction>>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
4306 return true; \
4308 } while (false)
4310 std::shared_ptr<CDVDInputStream::IMenus> pMenus = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4311 if (pMenus)
4313 if (m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0ms &&
4314 pMenus->GetTotalButtons() == 0)
4316 switch(action.GetID())
4318 case ACTION_NEXT_ITEM:
4319 case ACTION_MOVE_RIGHT:
4320 case ACTION_MOVE_UP:
4321 case ACTION_SELECT_ITEM:
4323 THREAD_ACTION(action);
4324 /* this will force us out of the stillframe */
4325 CLog::Log(LOGDEBUG, "{} - User asked to exit stillframe", __FUNCTION__);
4326 m_dvd.iDVDStillStartTime = {};
4327 m_dvd.iDVDStillTime = 1ms;
4329 return true;
4334 switch (action.GetID())
4336 /* this code is disabled to allow switching playlist items (dvdimage "stacks") */
4337 #if 0
4338 case ACTION_PREV_ITEM: // SKIP-:
4340 THREAD_ACTION(action);
4341 CLog::Log(LOGDEBUG, " - pushed prev");
4342 pMenus->OnPrevious();
4343 m_processInfo->SeekFinished(0);
4344 return true;
4346 break;
4347 case ACTION_NEXT_ITEM: // SKIP+:
4349 THREAD_ACTION(action);
4350 CLog::Log(LOGDEBUG, " - pushed next");
4351 pMenus->OnNext();
4352 m_processInfo->SeekFinished(0);
4353 return true;
4355 break;
4356 #endif
4357 case ACTION_SHOW_VIDEOMENU: // start button
4359 THREAD_ACTION(action);
4360 CLog::LogF(LOGDEBUG, "Trying to go to the menu");
4361 if (pMenus->OnMenu())
4363 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
4365 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
4366 m_callback.OnPlayBackResumed();
4369 // send a message to everyone that we've gone to the menu
4370 CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0);
4371 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
4373 return true;
4375 break;
4378 if (pMenus->IsInMenu())
4380 switch (action.GetID())
4382 case ACTION_NEXT_ITEM:
4383 THREAD_ACTION(action);
4384 CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide");
4385 if (pMenus->CanSeek() && GetChapterCount() > 0 && GetChapter() < GetChapterCount())
4386 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetChapter() + 1));
4387 else
4388 pMenus->OnNext();
4390 m_processInfo->SeekFinished(0);
4391 return true;
4392 case ACTION_PREV_ITEM:
4393 THREAD_ACTION(action);
4394 CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
4395 if (pMenus->CanSeek() && GetChapterCount() > 0 && GetChapter() > 0)
4396 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetPreviousChapter()));
4397 else
4398 pMenus->OnPrevious();
4400 m_processInfo->SeekFinished(0);
4401 return true;
4402 case ACTION_PREVIOUS_MENU:
4403 case ACTION_NAV_BACK:
4405 THREAD_ACTION(action);
4406 CLog::Log(LOGDEBUG, " - menu back");
4407 pMenus->OnBack();
4409 break;
4410 case ACTION_MOVE_LEFT:
4412 THREAD_ACTION(action);
4413 CLog::Log(LOGDEBUG, " - move left");
4414 pMenus->OnLeft();
4416 break;
4417 case ACTION_MOVE_RIGHT:
4419 THREAD_ACTION(action);
4420 CLog::Log(LOGDEBUG, " - move right");
4421 pMenus->OnRight();
4423 break;
4424 case ACTION_MOVE_UP:
4426 THREAD_ACTION(action);
4427 CLog::Log(LOGDEBUG, " - move up");
4428 pMenus->OnUp();
4430 break;
4431 case ACTION_MOVE_DOWN:
4433 THREAD_ACTION(action);
4434 CLog::Log(LOGDEBUG, " - move down");
4435 pMenus->OnDown();
4437 break;
4439 case ACTION_MOUSE_MOVE:
4440 case ACTION_MOUSE_LEFT_CLICK:
4442 CRect rs, rd, rv;
4443 m_renderManager.GetVideoRect(rs, rd, rv);
4444 CPoint pt(action.GetAmount(), action.GetAmount(1));
4445 if (!rd.PtInRect(pt))
4446 return false; // out of bounds
4447 THREAD_ACTION(action);
4448 // convert to video coords...
4449 pt -= CPoint(rd.x1, rd.y1);
4450 pt.x *= rs.Width() / rd.Width();
4451 pt.y *= rs.Height() / rd.Height();
4452 pt += CPoint(rs.x1, rs.y1);
4453 if (action.GetID() == ACTION_MOUSE_LEFT_CLICK)
4455 if (pMenus->OnMouseClick(pt))
4456 return true;
4457 else
4459 CServiceBroker::GetAppMessenger()->PostMsg(
4460 TMSG_GUI_ACTION, WINDOW_INVALID, -1,
4461 static_cast<void*>(new CAction(ACTION_TRIGGER_OSD)));
4462 return false;
4465 return pMenus->OnMouseMove(pt);
4467 break;
4468 case ACTION_SELECT_ITEM:
4470 THREAD_ACTION(action);
4471 CLog::Log(LOGDEBUG, " - button select");
4472 // show button pushed overlay
4473 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
4474 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_CLICKED);
4476 pMenus->ActivateButton();
4478 break;
4479 case REMOTE_0:
4480 case REMOTE_1:
4481 case REMOTE_2:
4482 case REMOTE_3:
4483 case REMOTE_4:
4484 case REMOTE_5:
4485 case REMOTE_6:
4486 case REMOTE_7:
4487 case REMOTE_8:
4488 case REMOTE_9:
4490 THREAD_ACTION(action);
4491 // Offset from key codes back to button number
4492 int button = action.GetID() - REMOTE_0;
4493 CLog::Log(LOGDEBUG, " - button pressed {}", button);
4494 pMenus->SelectButton(button);
4496 break;
4497 default:
4498 return false;
4499 break;
4501 return true; // message is handled
4505 pMenus.reset();
4507 switch (action.GetID())
4509 case ACTION_NEXT_ITEM:
4510 if (GetChapter() > 0 && GetChapter() < GetChapterCount())
4512 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetChapter() + 1));
4513 m_processInfo->SeekFinished(0);
4514 return true;
4516 else if (SeekScene(true))
4517 return true;
4518 else
4519 break;
4520 case ACTION_PREV_ITEM:
4521 if (GetChapter() > 0)
4523 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetPreviousChapter()));
4524 m_processInfo->SeekFinished(0);
4525 return true;
4527 else if (SeekScene(false))
4528 return true;
4529 else
4530 break;
4531 case ACTION_TOGGLE_COMMSKIP:
4532 m_SkipCommercials = !m_SkipCommercials;
4533 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25011),
4534 g_localizeStrings.Get(m_SkipCommercials ? 25013 : 25012));
4535 break;
4536 case ACTION_PLAYER_DEBUG:
4537 m_renderManager.ToggleDebug();
4538 break;
4539 case ACTION_PLAYER_DEBUG_VIDEO:
4540 m_renderManager.ToggleDebugVideo();
4541 break;
4543 case ACTION_PLAYER_PROCESS_INFO:
4544 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_DIALOG_PLAYER_PROCESS_INFO)
4546 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_DIALOG_PLAYER_PROCESS_INFO);
4547 return true;
4549 break;
4552 // return false to inform the caller we didn't handle the message
4553 return false;
4556 bool CVideoPlayer::IsInMenuInternal() const
4558 std::shared_ptr<CDVDInputStream::IMenus> pStream = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4559 if (pStream)
4561 if (m_dvd.state == DVDSTATE_STILL)
4562 return true;
4563 else
4564 return pStream->IsInMenu();
4566 return false;
4570 bool CVideoPlayer::IsInMenu() const
4572 std::unique_lock<CCriticalSection> lock(m_StateSection);
4573 return m_State.isInMenu;
4576 MenuType CVideoPlayer::GetSupportedMenuType() const
4578 std::unique_lock<CCriticalSection> lock(m_StateSection);
4579 return m_State.menuType;
4582 std::string CVideoPlayer::GetPlayerState()
4584 std::unique_lock<CCriticalSection> lock(m_StateSection);
4585 return m_State.player_state;
4588 bool CVideoPlayer::SetPlayerState(const std::string& state)
4590 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetState>(state));
4591 return true;
4594 int CVideoPlayer::GetChapterCount() const
4596 std::unique_lock<CCriticalSection> lock(m_StateSection);
4597 return m_State.chapters.size();
4600 int CVideoPlayer::GetChapter() const
4602 std::unique_lock<CCriticalSection> lock(m_StateSection);
4603 return m_State.chapter;
4606 void CVideoPlayer::GetChapterName(std::string& strChapterName, int chapterIdx) const
4608 std::unique_lock<CCriticalSection> lock(m_StateSection);
4609 if (chapterIdx == -1 && m_State.chapter > 0 && m_State.chapter <= (int) m_State.chapters.size())
4610 strChapterName = m_State.chapters[m_State.chapter - 1].first;
4611 else if (chapterIdx > 0 && chapterIdx <= (int) m_State.chapters.size())
4612 strChapterName = m_State.chapters[chapterIdx - 1].first;
4615 int CVideoPlayer::SeekChapter(int iChapter)
4617 if (GetChapter() > 0)
4619 if (iChapter < 0)
4620 iChapter = 0;
4621 if (iChapter > GetChapterCount())
4622 return 0;
4624 // Seek to the chapter.
4625 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(iChapter));
4626 SynchronizeDemuxer();
4629 return 0;
4632 int64_t CVideoPlayer::GetChapterPos(int chapterIdx) const
4634 std::unique_lock<CCriticalSection> lock(m_StateSection);
4635 if (chapterIdx > 0 && chapterIdx <= (int) m_State.chapters.size())
4636 return m_State.chapters[chapterIdx - 1].second;
4638 return -1;
4641 int CVideoPlayer::GetPreviousChapter()
4643 // 5-second grace period from chapter start to skip backwards to previous chapter
4644 // Afterwards skip to start of current chapter.
4645 const int chapter = GetChapter();
4647 if (chapter > 0 && (GetTime() < (GetChapterPos(chapter) + 5) * 1000))
4648 return chapter - 1;
4649 else
4650 return chapter;
4653 void CVideoPlayer::AddSubtitle(const std::string& strSubPath)
4655 m_messenger.Put(
4656 std::make_shared<CDVDMsgType<std::string>>(CDVDMsg::SUBTITLE_ADDFILE, strSubPath));
4659 bool CVideoPlayer::IsCaching() const
4661 std::unique_lock<CCriticalSection> lock(m_StateSection);
4662 return !m_State.isInMenu && m_State.caching;
4665 int CVideoPlayer::GetCacheLevel() const
4667 std::unique_lock<CCriticalSection> lock(m_StateSection);
4668 return (int)(m_State.cache_level * 100);
4671 double CVideoPlayer::GetQueueTime()
4673 int a = m_VideoPlayerAudio->GetLevel();
4674 int v = m_processInfo->GetLevelVQ();
4675 return std::max(a, v) * 8000.0 / 100;
4678 int CVideoPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename)
4680 std::string ext = URIUtils::GetExtension(filename);
4681 std::string vobsubfile = subfilename;
4682 if (ext == ".idx" || ext == ".sup")
4684 std::shared_ptr<CDVDDemux> pDemux;
4685 if (ext == ".idx")
4687 if (vobsubfile.empty())
4689 // find corresponding .sub (e.g. in case of manually selected .idx sub)
4690 vobsubfile = CUtil::GetVobSubSubFromIdx(filename);
4691 if (vobsubfile.empty())
4692 return -1;
4695 auto pDemuxVobsub = std::make_shared<CDVDDemuxVobsub>();
4696 if (!pDemuxVobsub->Open(filename, STREAM_SOURCE_NONE, vobsubfile))
4697 return -1;
4699 m_SelectionStreams.Update(nullptr, pDemuxVobsub.get(), vobsubfile);
4700 pDemux = pDemuxVobsub;
4702 else // .sup file
4704 CFileItem item(filename, false);
4705 std::shared_ptr<CDVDInputStream> pInput;
4706 pInput = CDVDFactoryInputStream::CreateInputStream(nullptr, item);
4707 if (!pInput || !pInput->Open())
4708 return -1;
4710 auto pDemuxFFmpeg = std::make_shared<CDVDDemuxFFmpeg>();
4711 if (!pDemuxFFmpeg->Open(pInput, false))
4712 return -1;
4714 m_SelectionStreams.Update(nullptr, pDemuxFFmpeg.get(), filename);
4715 pDemux = pDemuxFFmpeg;
4718 ExternalStreamInfo info =
4719 CUtil::GetExternalStreamDetailsFromFilename(m_item.GetDynPath(), filename);
4721 for (auto sub : pDemux->GetStreams())
4723 if (sub->type != STREAM_SUBTITLE)
4724 continue;
4726 int index = m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE,
4727 m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename),
4728 sub->demuxerId, sub->uniqueId);
4729 SelectionStream& stream = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
4731 if (stream.name.empty())
4732 stream.name = info.name;
4734 if (stream.language.empty())
4735 stream.language = info.language;
4737 if (static_cast<StreamFlags>(info.flag) != StreamFlags::FLAG_NONE)
4738 stream.flags = static_cast<StreamFlags>(info.flag);
4741 UpdateContent();
4742 // the demuxer id is unique
4743 m_subtitleDemuxerMap[pDemux->GetDemuxerId()] = pDemux;
4744 return m_SelectionStreams.TypeIndexOf(
4745 STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename),
4746 pDemux->GetDemuxerId(), 0);
4749 if(ext == ".sub")
4751 // if this looks like vobsub file (i.e. .idx found), add it as such
4752 std::string vobsubidx = CUtil::GetVobSubIdxFromSub(filename);
4753 if (!vobsubidx.empty())
4754 return AddSubtitleFile(vobsubidx, filename);
4757 SelectionStream s;
4758 s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename);
4759 s.type = STREAM_SUBTITLE;
4760 s.id = 0;
4761 s.filename = filename;
4762 ExternalStreamInfo info = CUtil::GetExternalStreamDetailsFromFilename(m_item.GetDynPath(), filename);
4763 s.name = info.name;
4764 s.language = info.language;
4765 if (static_cast<StreamFlags>(info.flag) != StreamFlags::FLAG_NONE)
4766 s.flags = static_cast<StreamFlags>(info.flag);
4768 m_SelectionStreams.Update(s);
4769 UpdateContent();
4770 return m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE, s.source, s.demuxerId, s.id);
4773 void CVideoPlayer::UpdatePlayState(double timeout)
4775 if (m_State.timestamp != 0 &&
4776 m_State.timestamp + DVD_MSEC_TO_TIME(timeout) > m_clock.GetAbsoluteClock())
4777 return;
4779 SPlayerState state(m_State);
4781 state.dts = DVD_NOPTS_VALUE;
4782 if (m_CurrentVideo.dts != DVD_NOPTS_VALUE)
4783 state.dts = m_CurrentVideo.dts;
4784 else if (m_CurrentAudio.dts != DVD_NOPTS_VALUE)
4785 state.dts = m_CurrentAudio.dts;
4786 else if (m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
4787 state.dts = m_CurrentVideo.startpts;
4788 else if (m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
4789 state.dts = m_CurrentAudio.startpts;
4791 state.startTime = 0;
4792 state.timeMin = 0;
4794 std::shared_ptr<CDVDInputStream::IMenus> pMenu = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4796 if (m_pDemuxer)
4798 if (IsInMenuInternal() && pMenu && !pMenu->CanSeek())
4799 state.chapter = 0;
4800 else
4801 state.chapter = m_pDemuxer->GetChapter();
4803 state.chapters.clear();
4804 if (m_pDemuxer->GetChapterCount() > 0)
4806 for (int i = 0, ie = m_pDemuxer->GetChapterCount(); i < ie; ++i)
4808 std::string name;
4809 m_pDemuxer->GetChapterName(name, i + 1);
4810 state.chapters.emplace_back(name, m_pDemuxer->GetChapterPos(i + 1));
4813 CServiceBroker::GetDataCacheCore().SetChapters(state.chapters);
4815 state.time = m_clock.GetClock(false) * 1000 / DVD_TIME_BASE;
4816 state.timeMax = m_pDemuxer->GetStreamLength();
4819 state.canpause = false;
4820 state.canseek = false;
4821 state.cantempo = false;
4822 state.isInMenu = false;
4823 state.menuType = MenuType::NONE;
4825 if (m_pInputStream)
4827 CDVDInputStream::IChapter* pChapter = m_pInputStream->GetIChapter();
4828 if (pChapter)
4830 if (IsInMenuInternal() && pMenu && !pMenu->CanSeek())
4831 state.chapter = 0;
4832 else
4833 state.chapter = pChapter->GetChapter();
4835 state.chapters.clear();
4836 if (pChapter->GetChapterCount() > 0)
4838 for (int i = 0, ie = pChapter->GetChapterCount(); i < ie; ++i)
4840 std::string name;
4841 pChapter->GetChapterName(name, i + 1);
4842 state.chapters.emplace_back(name, pChapter->GetChapterPos(i + 1));
4845 CServiceBroker::GetDataCacheCore().SetChapters(state.chapters);
4848 CDVDInputStream::ITimes* pTimes = m_pInputStream->GetITimes();
4849 CDVDInputStream::IDisplayTime* pDisplayTime = m_pInputStream->GetIDisplayTime();
4851 CDVDInputStream::ITimes::Times times;
4852 if (pTimes && pTimes->GetTimes(times))
4854 state.startTime = times.startTime;
4855 state.time = (m_clock.GetClock(false) - times.ptsStart) * 1000 / DVD_TIME_BASE;
4856 state.timeMax = (times.ptsEnd - times.ptsStart) * 1000 / DVD_TIME_BASE;
4857 state.timeMin = (times.ptsBegin - times.ptsStart) * 1000 / DVD_TIME_BASE;
4858 state.time_offset = -times.ptsStart;
4860 else if (pDisplayTime && pDisplayTime->GetTotalTime() > 0)
4862 if (state.dts != DVD_NOPTS_VALUE)
4864 int dispTime = 0;
4865 if (m_CurrentVideo.id >= 0 && m_CurrentVideo.dispTime)
4866 dispTime = m_CurrentVideo.dispTime;
4867 else if (m_CurrentAudio.dispTime)
4868 dispTime = m_CurrentAudio.dispTime;
4870 state.time_offset = DVD_MSEC_TO_TIME(dispTime) - state.dts;
4872 state.time += state.time_offset * 1000 / DVD_TIME_BASE;
4873 state.timeMax = pDisplayTime->GetTotalTime();
4875 else
4877 state.time_offset = 0;
4880 if (pMenu)
4882 if (!pMenu->GetState(state.player_state))
4883 state.player_state = "";
4885 if (m_dvd.state == DVDSTATE_STILL)
4887 const auto now = std::chrono::steady_clock::now();
4888 const auto duration =
4889 std::chrono::duration_cast<std::chrono::milliseconds>(now - m_dvd.iDVDStillStartTime);
4890 state.time = duration.count();
4891 state.timeMax = m_dvd.iDVDStillTime.count();
4892 state.isInMenu = true;
4894 else if (IsInMenuInternal())
4896 state.time = pDisplayTime->GetTime();
4897 state.isInMenu = true;
4898 if (!pMenu->CanSeek())
4899 state.time_offset = 0;
4901 state.menuType = pMenu->GetSupportedMenuType();
4904 state.canpause = m_pInputStream->CanPause();
4906 bool realtime = m_pInputStream->IsRealtime();
4908 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK) &&
4909 !realtime)
4911 state.cantempo = true;
4913 else
4915 state.cantempo = false;
4918 m_processInfo->SetStateRealtime(realtime);
4921 if (m_Edl.HasCuts())
4923 state.time = static_cast<double>(m_Edl.GetTimeWithoutCuts(state.time));
4924 state.timeMax = state.timeMax - static_cast<double>(m_Edl.GetTotalCutTime());
4927 if (m_caching > CACHESTATE_DONE && m_caching < CACHESTATE_PLAY)
4928 state.caching = true;
4929 else
4930 state.caching = false;
4932 double queueTime = GetQueueTime();
4933 CacheInfo cache = GetCachingTimes();
4935 if (cache.valid)
4937 state.cache_level = std::max(0.0, std::min(1.0, cache.level));
4938 state.cache_offset = cache.offset;
4939 state.cache_time = cache.time;
4941 else
4943 state.cache_level = std::min(1.0, queueTime / 8000.0);
4944 state.cache_offset = queueTime / state.timeMax;
4945 state.cache_time = queueTime / 1000.0;
4948 XFILE::SCacheStatus status;
4949 if (m_pInputStream && m_pInputStream->GetCacheStatus(&status))
4951 state.cache_bytes = status.forward;
4952 if(state.timeMax)
4953 state.cache_bytes += m_pInputStream->GetLength() * (int64_t)(queueTime / state.timeMax);
4955 else
4956 state.cache_bytes = 0;
4958 state.timestamp = m_clock.GetAbsoluteClock();
4960 if (state.timeMax <= 0)
4962 state.timeMax = state.time;
4963 state.timeMin = state.time;
4965 if (state.timeMin == state.timeMax)
4967 state.canseek = false;
4968 state.cantempo = false;
4970 else
4972 state.canseek = true;
4973 state.canpause = true;
4976 m_processInfo->SetPlayTimes(state.startTime, state.time, state.timeMin, state.timeMax);
4978 std::unique_lock<CCriticalSection> lock(m_StateSection);
4979 m_State = state;
4982 int64_t CVideoPlayer::GetUpdatedTime()
4984 UpdatePlayState(0);
4985 return llrint(m_State.time);
4988 void CVideoPlayer::SetDynamicRangeCompression(long drc)
4990 m_processInfo->GetVideoSettingsLocked().SetVolumeAmplification(static_cast<float>(drc) / 100);
4991 m_VideoPlayerAudio->SetDynamicRangeCompression(drc);
4994 CVideoSettings CVideoPlayer::GetVideoSettings() const
4996 return m_processInfo->GetVideoSettings();
4999 void CVideoPlayer::SetVideoSettings(CVideoSettings& settings)
5001 m_processInfo->SetVideoSettings(settings);
5002 m_renderManager.SetVideoSettings(settings);
5003 m_renderManager.SetDelay(static_cast<int>(settings.m_AudioDelay * 1000.0f));
5004 m_renderManager.SetSubtitleVerticalPosition(settings.m_subtitleVerticalPosition,
5005 settings.m_subtitleVerticalPositionSave);
5006 m_VideoPlayerVideo->EnableSubtitle(settings.m_SubtitleOn);
5007 m_VideoPlayerVideo->SetSubtitleDelay(static_cast<int>(-settings.m_SubtitleDelay * DVD_TIME_BASE));
5010 void CVideoPlayer::FrameMove()
5012 m_renderManager.FrameMove();
5015 void CVideoPlayer::Render(bool clear, uint32_t alpha, bool gui)
5017 m_renderManager.Render(clear, 0, alpha, gui);
5020 void CVideoPlayer::FlushRenderer()
5022 m_renderManager.Flush(true, true);
5025 void CVideoPlayer::SetRenderViewMode(int mode, float zoom, float par, float shift, bool stretch)
5027 m_processInfo->GetVideoSettingsLocked().SetViewMode(mode, zoom, par, shift, stretch);
5028 m_renderManager.SetVideoSettings(m_processInfo->GetVideoSettings());
5029 m_renderManager.SetViewMode(mode);
5032 float CVideoPlayer::GetRenderAspectRatio() const
5034 return m_renderManager.GetAspectRatio();
5037 void CVideoPlayer::GetRects(CRect& source, CRect& dest, CRect& view) const
5039 m_renderManager.GetVideoRect(source, dest, view);
5042 void CVideoPlayer::TriggerUpdateResolution()
5044 std::string stereomode;
5045 m_renderManager.TriggerUpdateResolution(0, 0, 0, stereomode);
5048 bool CVideoPlayer::IsRenderingVideo() const
5050 return m_renderManager.IsConfigured();
5053 bool CVideoPlayer::Supports(EINTERLACEMETHOD method) const
5055 if (!m_processInfo)
5056 return false;
5057 return m_processInfo->Supports(method);
5060 EINTERLACEMETHOD CVideoPlayer::GetDeinterlacingMethodDefault() const
5062 if (!m_processInfo)
5063 return EINTERLACEMETHOD::VS_INTERLACEMETHOD_NONE;
5064 return m_processInfo->GetDeinterlacingMethodDefault();
5067 bool CVideoPlayer::Supports(ESCALINGMETHOD method) const
5069 return m_renderManager.Supports(method);
5072 bool CVideoPlayer::Supports(ERENDERFEATURE feature) const
5074 return m_renderManager.Supports(feature);
5077 unsigned int CVideoPlayer::RenderCaptureAlloc()
5079 return m_renderManager.AllocRenderCapture();
5082 void CVideoPlayer::RenderCapture(unsigned int captureId, unsigned int width, unsigned int height, int flags)
5084 m_renderManager.StartRenderCapture(captureId, width, height, flags);
5087 void CVideoPlayer::RenderCaptureRelease(unsigned int captureId)
5089 m_renderManager.ReleaseRenderCapture(captureId);
5092 bool CVideoPlayer::RenderCaptureGetPixels(unsigned int captureId, unsigned int millis, uint8_t *buffer, unsigned int size)
5094 return m_renderManager.RenderCaptureGetPixels(captureId, millis, buffer, size);
5097 void CVideoPlayer::VideoParamsChange()
5099 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_AVCHANGE));
5102 void CVideoPlayer::GetDebugInfo(std::string &audio, std::string &video, std::string &general)
5104 audio = m_VideoPlayerAudio->GetPlayerInfo();
5105 video = m_VideoPlayerVideo->GetPlayerInfo();
5106 GetGeneralInfo(general);
5109 void CVideoPlayer::UpdateClockSync(bool enabled)
5111 m_processInfo->SetRenderClockSync(enabled);
5114 void CVideoPlayer::UpdateRenderInfo(CRenderInfo &info)
5116 m_processInfo->UpdateRenderInfo(info);
5119 void CVideoPlayer::UpdateRenderBuffers(int queued, int discard, int free)
5121 m_processInfo->UpdateRenderBuffers(queued, discard, free);
5124 void CVideoPlayer::UpdateGuiRender(bool gui)
5126 m_processInfo->SetGuiRender(gui);
5129 void CVideoPlayer::UpdateVideoRender(bool video)
5131 m_processInfo->SetVideoRender(video);
5134 // IDispResource interface
5135 void CVideoPlayer::OnLostDisplay()
5137 CLog::Log(LOGINFO, "VideoPlayer: OnLostDisplay received");
5138 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, true), 1);
5139 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, true), 1);
5140 m_clock.Pause(true);
5141 m_displayLost = true;
5142 FlushRenderer();
5145 void CVideoPlayer::OnResetDisplay()
5147 if (!m_displayLost)
5148 return;
5150 CLog::Log(LOGINFO, "VideoPlayer: OnResetDisplay received");
5151 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, false), 1);
5152 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, false), 1);
5153 m_clock.Pause(false);
5154 m_displayLost = false;
5155 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_DISPLAY_RESET), 1);
5158 void CVideoPlayer::UpdateFileItemStreamDetails(CFileItem& item)
5160 if (!m_UpdateStreamDetails)
5161 return;
5162 m_UpdateStreamDetails = false;
5164 CLog::Log(LOGDEBUG, "CVideoPlayer: updating file item stream details with available streams");
5166 VideoStreamInfo videoInfo;
5167 AudioStreamInfo audioInfo;
5168 SubtitleStreamInfo subtitleInfo;
5169 CVideoInfoTag* info = item.GetVideoInfoTag();
5170 GetVideoStreamInfo(CURRENT_STREAM, videoInfo);
5171 info->m_streamDetails.SetStreams(videoInfo, m_processInfo->GetMaxTime() / 1000, audioInfo,
5172 subtitleInfo);
5174 //grab all the audio and subtitle info and save it
5176 for (int i = 0; i < GetAudioStreamCount(); i++)
5178 GetAudioStreamInfo(i, audioInfo);
5179 info->m_streamDetails.AddStream(new CStreamDetailAudio(audioInfo));
5182 for (int i = 0; i < GetSubtitleCount(); i++)
5184 GetSubtitleStreamInfo(i, subtitleInfo);
5185 info->m_streamDetails.AddStream(new CStreamDetailSubtitle(subtitleInfo));
5189 //------------------------------------------------------------------------------
5190 // content related methods
5191 //------------------------------------------------------------------------------
5193 void CVideoPlayer::UpdateContent()
5195 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5196 m_content.m_selectionStreams = m_SelectionStreams;
5197 m_content.m_programs = m_programs;
5200 void CVideoPlayer::UpdateContentState()
5202 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5204 m_content.m_videoIndex = m_SelectionStreams.TypeIndexOf(STREAM_VIDEO, m_CurrentVideo.source,
5205 m_CurrentVideo.demuxerId, m_CurrentVideo.id);
5206 m_content.m_audioIndex = m_SelectionStreams.TypeIndexOf(STREAM_AUDIO, m_CurrentAudio.source,
5207 m_CurrentAudio.demuxerId, m_CurrentAudio.id);
5208 m_content.m_subtitleIndex = m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE, m_CurrentSubtitle.source,
5209 m_CurrentSubtitle.demuxerId, m_CurrentSubtitle.id);
5211 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) && m_content.m_videoIndex == -1 &&
5212 m_content.m_audioIndex == -1)
5214 std::shared_ptr<CDVDInputStreamNavigator> nav =
5215 std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
5217 m_content.m_videoIndex = m_SelectionStreams.TypeIndexOf(STREAM_VIDEO, STREAM_SOURCE_NAV, -1,
5218 nav->GetActiveAngle());
5219 m_content.m_audioIndex = m_SelectionStreams.TypeIndexOf(STREAM_AUDIO, STREAM_SOURCE_NAV, -1,
5220 nav->GetActiveAudioStream());
5222 // only update the subtitle index in libdvdnav if the subtitle is provided by the dvd itself,
5223 // i.e. for external subtitles the index is always greater than the subtitlecount in dvdnav
5224 if (m_content.m_subtitleIndex < nav->GetSubTitleStreamCount())
5226 m_content.m_subtitleIndex = m_SelectionStreams.TypeIndexOf(
5227 STREAM_SUBTITLE, STREAM_SOURCE_NAV, -1, nav->GetActiveSubtitleStream());
5232 void CVideoPlayer::GetVideoStreamInfo(int streamId, VideoStreamInfo& info) const
5234 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5236 if (streamId == CURRENT_STREAM)
5237 streamId = m_content.m_videoIndex;
5239 if (streamId < 0 || streamId > GetVideoStreamCount() - 1)
5241 info.valid = false;
5242 return;
5245 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_VIDEO, streamId);
5246 if (s.language.length() > 0)
5247 info.language = s.language;
5249 if (s.name.length() > 0)
5250 info.name = s.name;
5252 m_renderManager.GetVideoRect(info.SrcRect, info.DestRect, info.VideoRect);
5254 info.valid = true;
5255 info.bitrate = s.bitrate;
5256 info.width = s.width;
5257 info.height = s.height;
5258 info.codecName = s.codec;
5259 info.videoAspectRatio = s.aspect_ratio;
5260 info.stereoMode = s.stereo_mode;
5261 info.flags = s.flags;
5262 info.hdrType = s.hdrType;
5265 int CVideoPlayer::GetVideoStreamCount() const
5267 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5268 return m_content.m_selectionStreams.CountType(STREAM_VIDEO);
5271 int CVideoPlayer::GetVideoStream() const
5273 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5274 return m_content.m_videoIndex;
5277 void CVideoPlayer::SetVideoStream(int iStream)
5279 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetVideoStream>(iStream));
5280 m_processInfo->GetVideoSettingsLocked().SetVideoStream(iStream);
5281 SynchronizeDemuxer();
5284 void CVideoPlayer::GetAudioStreamInfo(int index, AudioStreamInfo& info) const
5286 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5288 if (index == CURRENT_STREAM)
5289 index = m_content.m_audioIndex;
5291 if (index < 0 || index > GetAudioStreamCount() - 1)
5293 info.valid = false;
5294 return;
5297 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_AUDIO, index);
5298 info.language = s.language;
5299 info.name = s.name;
5301 if (s.type == STREAM_NONE)
5302 info.name += " (Invalid)";
5304 info.valid = true;
5305 info.bitrate = s.bitrate;
5306 info.channels = s.channels;
5307 info.codecName = s.codec;
5308 info.flags = s.flags;
5311 int CVideoPlayer::GetAudioStreamCount() const
5313 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5314 return m_content.m_selectionStreams.CountType(STREAM_AUDIO);
5317 int CVideoPlayer::GetAudioStream()
5319 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5320 return m_content.m_audioIndex;
5323 void CVideoPlayer::SetAudioStream(int iStream)
5325 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetAudioStream>(iStream));
5326 m_processInfo->GetVideoSettingsLocked().SetAudioStream(iStream);
5327 SynchronizeDemuxer();
5330 void CVideoPlayer::GetSubtitleStreamInfo(int index, SubtitleStreamInfo& info) const
5332 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5334 if (index == CURRENT_STREAM)
5335 index = m_content.m_subtitleIndex;
5337 if (index < 0 || index > GetSubtitleCount() - 1)
5339 info.valid = false;
5340 info.language.clear();
5341 info.flags = StreamFlags::FLAG_NONE;
5342 return;
5345 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_SUBTITLE, index);
5346 info.name = s.name;
5348 if (s.type == STREAM_NONE)
5349 info.name += "(Invalid)";
5351 info.language = s.language;
5352 info.flags = s.flags;
5355 void CVideoPlayer::SetSubtitle(int iStream)
5357 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSubtitleStream>(iStream));
5358 m_processInfo->GetVideoSettingsLocked().SetSubtitleStream(iStream);
5361 int CVideoPlayer::GetSubtitleCount() const
5363 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5364 return m_content.m_selectionStreams.CountType(STREAM_SUBTITLE);
5367 int CVideoPlayer::GetSubtitle()
5369 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5370 return m_content.m_subtitleIndex;
5373 int CVideoPlayer::GetPrograms(std::vector<ProgramInfo>& programs)
5375 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5376 programs = m_programs;
5377 return programs.size();
5380 void CVideoPlayer::SetProgram(int progId)
5382 m_messenger.Put(std::make_shared<CDVDMsgInt>(CDVDMsg::PLAYER_SET_PROGRAM, progId));
5385 int CVideoPlayer::GetProgramsCount() const
5387 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5388 return m_programs.size();
5391 void CVideoPlayer::SetUpdateStreamDetails()
5393 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_SET_UPDATE_STREAM_DETAILS));