Merge pull request #25762 from CastagnaIT/gui_menu_tracks
[xbmc.git] / xbmc / cores / VideoPlayer / VideoPlayer.cpp
blob5a20ca0fffc677a5ff1dbb925451b8610975bf21
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 #include "network/NetworkFileItemClassify.h"
21 #if defined(HAVE_LIBBLURAY)
22 #include "DVDInputStreams/DVDInputStreamBluray.h"
23 #endif
24 #include "DVDInputStreams/DVDInputStreamNavigator.h"
25 #include "DVDInputStreams/InputStreamPVRBase.h"
26 #include "DVDMessage.h"
27 #include "FileItem.h"
28 #include "GUIUserMessages.h"
29 #include "LangInfo.h"
30 #include "ServiceBroker.h"
31 #include "URL.h"
32 #include "Util.h"
33 #include "VideoPlayerAudio.h"
34 #include "VideoPlayerRadioRDS.h"
35 #include "VideoPlayerVideo.h"
36 #include "application/Application.h"
37 #include "cores/DataCacheCore.h"
38 #include "cores/EdlEdit.h"
39 #include "cores/FFmpeg.h"
40 #include "cores/VideoPlayer/Process/ProcessInfo.h"
41 #include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
42 #include "dialogs/GUIDialogKaiToast.h"
43 #include "guilib/GUIComponent.h"
44 #include "guilib/GUIWindowManager.h"
45 #include "guilib/LocalizeStrings.h"
46 #include "guilib/StereoscopicsManager.h"
47 #include "input/actions/Action.h"
48 #include "input/actions/ActionIDs.h"
49 #include "messaging/ApplicationMessenger.h"
50 #include "network/NetworkFileItemClassify.h"
51 #include "settings/AdvancedSettings.h"
52 #include "settings/Settings.h"
53 #include "settings/SettingsComponent.h"
54 #include "threads/SingleLock.h"
55 #include "utils/FontUtils.h"
56 #include "utils/JobManager.h"
57 #include "utils/LangCodeExpander.h"
58 #include "utils/StreamDetails.h"
59 #include "utils/StreamUtils.h"
60 #include "utils/StringUtils.h"
61 #include "utils/URIUtils.h"
62 #include "utils/Variant.h"
63 #include "utils/log.h"
64 #include "video/Bookmark.h"
65 #include "video/VideoInfoTag.h"
66 #include "windowing/WinSystem.h"
68 #include <chrono>
69 #include <iterator>
70 #include <memory>
71 #include <mutex>
72 #include <utility>
74 using namespace KODI;
75 using namespace std::chrono_literals;
77 //------------------------------------------------------------------------------
78 // selection streams
79 //------------------------------------------------------------------------------
81 #define PREDICATE_RETURN(lh, rh) \
82 do { \
83 if((lh) != (rh)) \
84 return (lh) > (rh); \
85 } while(0)
87 class PredicateSubtitleFilter
89 private:
90 std::string audiolang;
91 bool original;
92 bool nosub;
93 bool onlyforced;
94 int currentSubStream;
95 public:
96 /** \brief The class' operator() decides if the given (subtitle) SelectionStream is relevant wrt.
97 * preferred subtitle language and audio language. If the subtitle is relevant <B>false</B> false is returned.
99 * A subtitle is relevant if
100 * - it was previously selected, or
101 * - it's an external sub, or
102 * - it's a forced sub and "original stream's language" was selected and audio stream language matches, or
103 * - it's a default and a forced sub (could lead to users seeing forced subs in a foreign language!), or
104 * - its language matches the preferred subtitle's language (unequal to "original stream's language")
106 explicit PredicateSubtitleFilter(const std::string& lang, int subStream)
107 : audiolang(lang),
108 currentSubStream(subStream)
110 const std::string subtitleLang = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE);
111 original = StringUtils::EqualsNoCase(subtitleLang, "original");
112 nosub = StringUtils::EqualsNoCase(subtitleLang, "none");
113 onlyforced = StringUtils::EqualsNoCase(subtitleLang, "forced_only");
116 bool operator()(const SelectionStream& ss) const
118 if (ss.type_index == currentSubStream)
119 return false;
121 if (nosub)
122 return true;
124 if (onlyforced)
126 if ((ss.flags & StreamFlags::FLAG_FORCED) && g_LangCodeExpander.CompareISO639Codes(ss.language, audiolang))
127 return false;
128 else
129 return true;
132 if(STREAM_SOURCE_MASK(ss.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(ss.source) == STREAM_SOURCE_TEXT)
133 return false;
135 if ((ss.flags & StreamFlags::FLAG_FORCED) && g_LangCodeExpander.CompareISO639Codes(ss.language, audiolang))
136 return false;
138 if ((ss.flags & StreamFlags::FLAG_FORCED) && (ss.flags & StreamFlags::FLAG_DEFAULT))
139 return false;
141 if (ss.language == "cc" && ss.flags & StreamFlags::FLAG_HEARING_IMPAIRED)
142 return false;
144 if(!original)
146 std::string subtitle_language = g_langInfo.GetSubtitleLanguage();
147 if (g_LangCodeExpander.CompareISO639Codes(subtitle_language, ss.language))
148 return false;
150 else if (ss.flags & StreamFlags::FLAG_DEFAULT)
151 return false;
153 return true;
157 class PredicateAudioFilter
159 private:
160 int currentAudioStream;
161 bool preferStereo;
162 public:
163 explicit PredicateAudioFilter(int audioStream, bool preferStereo)
164 : currentAudioStream(audioStream)
165 , preferStereo(preferStereo)
168 bool operator()(const SelectionStream& lh, const SelectionStream& rh)
170 PREDICATE_RETURN(lh.type_index == currentAudioStream
171 , rh.type_index == currentAudioStream);
173 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
175 if (!StringUtils::EqualsNoCase(settings->GetString(CSettings::SETTING_LOCALE_AUDIOLANGUAGE), "mediadefault"))
177 if (!StringUtils::EqualsNoCase(settings->GetString(CSettings::SETTING_LOCALE_AUDIOLANGUAGE), "original"))
179 std::string audio_language = g_langInfo.GetAudioLanguage();
180 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(audio_language, lh.language)
181 , g_LangCodeExpander.CompareISO639Codes(audio_language, rh.language));
183 else
185 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_ORIGINAL,
186 rh.flags & StreamFlags::FLAG_ORIGINAL);
189 bool hearingimp = settings->GetBool(CSettings::SETTING_ACCESSIBILITY_AUDIOHEARING);
190 PREDICATE_RETURN(!hearingimp ? !(lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED
191 , !hearingimp ? !(rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED);
193 bool visualimp = settings->GetBool(CSettings::SETTING_ACCESSIBILITY_AUDIOVISUAL);
194 PREDICATE_RETURN(!visualimp ? !(lh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED) : lh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED
195 , !visualimp ? !(rh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED) : rh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED);
198 if (settings->GetBool(CSettings::SETTING_VIDEOPLAYER_PREFERDEFAULTFLAG))
200 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
201 rh.flags & StreamFlags::FLAG_DEFAULT);
204 if (preferStereo)
205 PREDICATE_RETURN(lh.channels == 2,
206 rh.channels == 2);
207 else
208 PREDICATE_RETURN(lh.channels,
209 rh.channels);
211 PREDICATE_RETURN(StreamUtils::GetCodecPriority(lh.codec),
212 StreamUtils::GetCodecPriority(rh.codec));
214 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
215 rh.flags & StreamFlags::FLAG_DEFAULT);
216 return false;
220 /** \brief The class' operator() decides if the given (subtitle) SelectionStream lh is 'better than' the given (subtitle) SelectionStream rh.
221 * If lh is 'better than' rh the return value is true, false otherwise.
223 * A subtitle lh is 'better than' a subtitle rh (in evaluation order) if
224 * - lh was previously selected, or
225 * - lh is an external sub and rh not, or
226 * - 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
227 * - 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
228 * - 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
229 * - lh is a forced sub and a default sub ("original stream's language" was selected or subtitles are off)
230 * - lh is an external sub and its language matches the preferred subtitle's language (unequal to "original stream's language") and rh not, or
231 * - lh is language matches the preferred subtitle's language (unequal to "original stream's language") and rh not, or
232 * - lh is a default sub and rh not
234 class PredicateSubtitlePriority
236 private:
237 std::string audiolang;
238 bool original;
239 bool subson;
240 PredicateSubtitleFilter filter;
241 int subStream;
242 public:
243 explicit PredicateSubtitlePriority(const std::string& lang, int stream, bool ison)
244 : audiolang(lang),
245 original(StringUtils::EqualsNoCase(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE), "original")),
246 subson(ison),
247 filter(lang, stream),
248 subStream(stream)
252 bool relevant(const SelectionStream& ss) const
254 return !filter(ss);
257 bool operator()(const SelectionStream& lh, const SelectionStream& rh) const
259 PREDICATE_RETURN(relevant(lh)
260 , relevant(rh));
262 PREDICATE_RETURN(lh.type_index == subStream
263 , rh.type_index == subStream);
265 // prefer external subs
266 PREDICATE_RETURN(STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_TEXT
267 , STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT);
269 if (!subson || original)
271 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
272 , rh.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
274 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT && g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
275 , rh.flags & StreamFlags::FLAG_DEFAULT && g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
277 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
278 , g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
280 PREDICATE_RETURN((lh.flags & (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)) == (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)
281 , (rh.flags & (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)) == (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT));
285 std::string subtitle_language = g_langInfo.GetSubtitleLanguage();
286 if (!original)
288 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)
289 , (STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT) && g_LangCodeExpander.CompareISO639Codes(subtitle_language, rh.language));
292 if (!original)
294 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(subtitle_language, lh.language)
295 , g_LangCodeExpander.CompareISO639Codes(subtitle_language, rh.language));
297 bool hearingimp = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_ACCESSIBILITY_SUBHEARING);
298 PREDICATE_RETURN(!hearingimp ? !(lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED
299 , !hearingimp ? !(rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED);
302 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT
303 , rh.flags & StreamFlags::FLAG_DEFAULT);
305 return false;
309 class PredicateVideoFilter
311 private:
312 int currentVideoStream;
313 public:
314 explicit PredicateVideoFilter(int videoStream) : currentVideoStream(videoStream)
317 bool operator()(const SelectionStream& lh, const SelectionStream& rh)
319 PREDICATE_RETURN(lh.type_index == currentVideoStream,
320 rh.type_index == currentVideoStream);
322 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
323 rh.flags & StreamFlags::FLAG_DEFAULT);
324 return false;
328 void CSelectionStreams::Clear(StreamType type, StreamSource source)
330 auto new_end = std::remove_if(m_Streams.begin(), m_Streams.end(),
331 [type, source](const SelectionStream &stream)
333 return (type == STREAM_NONE || stream.type == type) &&
334 (source == 0 || stream.source == source);
336 m_Streams.erase(new_end, m_Streams.end());
339 SelectionStream& CSelectionStreams::Get(StreamType type, int index)
341 return const_cast<SelectionStream&>(std::as_const(*this).Get(type, index));
344 const SelectionStream& CSelectionStreams::Get(StreamType type, int index) const
346 int count = -1;
347 for (size_t i = 0; i < m_Streams.size(); ++i)
349 if (m_Streams[i].type != type)
350 continue;
351 count++;
352 if (count == index)
353 return m_Streams[i];
355 return m_invalid;
358 std::vector<SelectionStream> CSelectionStreams::Get(StreamType type)
360 std::vector<SelectionStream> streams;
361 std::copy_if(m_Streams.begin(), m_Streams.end(), std::back_inserter(streams),
362 [type](const SelectionStream &stream)
364 return stream.type == type;
366 return streams;
369 bool CSelectionStreams::Get(StreamType type, StreamFlags flag, SelectionStream& out)
371 for(size_t i=0;i<m_Streams.size();i++)
373 if(m_Streams[i].type != type)
374 continue;
375 if((m_Streams[i].flags & flag) != flag)
376 continue;
377 out = m_Streams[i];
378 return true;
380 return false;
383 int CSelectionStreams::TypeIndexOf(StreamType type, int source, int64_t demuxerId, int id) const
385 if (id < 0)
386 return -1;
388 auto it = std::find_if(m_Streams.begin(), m_Streams.end(),
389 [&](const SelectionStream& stream) {return stream.type == type
390 && stream.source == source && stream.id == id
391 && stream.demuxerId == demuxerId;});
393 if (it != m_Streams.end())
394 return it->type_index;
395 else
396 return -1;
399 int CSelectionStreams::Source(StreamSource source, const std::string& filename)
401 int index = source - 1;
402 for (size_t i=0; i<m_Streams.size(); i++)
404 SelectionStream &s = m_Streams[i];
405 if (STREAM_SOURCE_MASK(s.source) != source)
406 continue;
407 // if it already exists, return same
408 if (s.filename == filename)
409 return s.source;
410 if (index < s.source)
411 index = s.source;
413 // return next index
414 return index + 1;
417 void CSelectionStreams::Update(SelectionStream& s)
419 int index = TypeIndexOf(s.type, s.source, s.demuxerId, s.id);
420 if(index >= 0)
422 SelectionStream& o = Get(s.type, index);
423 s.type_index = o.type_index;
424 o = s;
426 else
428 s.type_index = CountType(s.type);
429 m_Streams.push_back(s);
433 void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input,
434 CDVDDemux* demuxer,
435 const std::string& filename2)
437 if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
439 std::shared_ptr<CDVDInputStreamNavigator> nav = std::static_pointer_cast<CDVDInputStreamNavigator>(input);
440 std::string filename = nav->GetFileName();
441 int source = Source(STREAM_SOURCE_NAV, filename);
443 int count;
444 count = nav->GetAudioStreamCount();
445 for(int i=0;i<count;i++)
447 SelectionStream s;
448 s.source = source;
449 s.type = STREAM_AUDIO;
450 s.id = i;
451 s.flags = StreamFlags::FLAG_NONE;
452 s.filename = filename;
454 AudioStreamInfo info = nav->GetAudioStreamInfo(i);
455 s.name = info.name;
456 s.codec = info.codecName;
457 s.codecDesc = info.codecDesc;
458 s.language = g_LangCodeExpander.ConvertToISO6392B(info.language);
459 s.channels = info.channels;
460 s.flags = info.flags;
461 Update(s);
464 count = nav->GetSubTitleStreamCount();
465 for(int i=0;i<count;i++)
467 SelectionStream s;
468 s.source = source;
469 s.type = STREAM_SUBTITLE;
470 s.id = i;
471 s.filename = filename;
472 s.channels = 0;
474 SubtitleStreamInfo info = nav->GetSubtitleStreamInfo(i);
475 s.name = info.name;
476 s.codec = info.codecName;
477 s.flags = info.flags;
478 s.language = g_LangCodeExpander.ConvertToISO6392B(info.language);
479 Update(s);
482 VideoStreamInfo info = nav->GetVideoStreamInfo();
483 for (int i = 1; i <= info.angles; i++)
485 SelectionStream s;
486 s.source = source;
487 s.type = STREAM_VIDEO;
488 s.id = i;
489 s.flags = StreamFlags::FLAG_NONE;
490 s.filename = filename;
491 s.channels = 0;
492 s.aspect_ratio = info.videoAspectRatio;
493 s.width = info.width;
494 s.height = info.height;
495 s.codec = info.codecName;
496 s.name = StringUtils::Format("{} {}", g_localizeStrings.Get(38032), i);
497 Update(s);
500 else if(demuxer)
502 std::string filename = demuxer->GetFileName();
503 int source;
504 if(input) /* hack to know this is sub decoder */
505 source = Source(STREAM_SOURCE_DEMUX, filename);
506 else if (!filename2.empty())
507 source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
508 else
509 source = Source(STREAM_SOURCE_VIDEOMUX, filename);
511 for (auto stream : demuxer->GetStreams())
513 /* skip streams with no type */
514 if (stream->type == STREAM_NONE)
515 continue;
516 /* make sure stream is marked with right source */
517 stream->source = source;
519 SelectionStream s;
520 s.source = source;
521 s.type = stream->type;
522 s.id = stream->uniqueId;
523 s.demuxerId = stream->demuxerId;
524 s.language = g_LangCodeExpander.ConvertToISO6392B(stream->language);
525 s.flags = stream->flags;
526 s.filename = demuxer->GetFileName();
527 s.filename2 = filename2;
528 s.name = stream->GetStreamName();
529 s.codec = demuxer->GetStreamCodecName(stream->demuxerId, stream->uniqueId);
530 s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below.
531 if(stream->type == STREAM_VIDEO)
533 CDemuxStreamVideo* vstream = static_cast<CDemuxStreamVideo*>(stream);
534 s.width = vstream->iWidth;
535 s.height = vstream->iHeight;
536 s.aspect_ratio = vstream->fAspect;
537 s.stereo_mode = vstream->stereo_mode;
538 s.bitrate = vstream->iBitRate;
539 s.hdrType = vstream->hdr_type;
540 s.fpsRate = static_cast<uint32_t>(vstream->iFpsRate);
541 s.fpsScale = static_cast<uint32_t>(vstream->iFpsScale);
543 if(stream->type == STREAM_AUDIO)
545 s.codecDesc = static_cast<CDemuxStreamAudio*>(stream)->GetStreamType();
546 s.channels = static_cast<CDemuxStreamAudio*>(stream)->iChannels;
547 s.bitrate = static_cast<CDemuxStreamAudio*>(stream)->iBitRate;
549 Update(s);
552 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
553 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
554 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
557 void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input, CDVDDemux* demuxer)
559 Update(input, demuxer, "");
562 int CSelectionStreams::CountTypeOfSource(StreamType type, StreamSource source) const
564 return std::count_if(m_Streams.begin(), m_Streams.end(),
565 [&](const SelectionStream& stream) {return (stream.type == type) && (stream.source == source);});
568 int CSelectionStreams::CountType(StreamType type) const
570 return std::count_if(m_Streams.begin(), m_Streams.end(),
571 [&](const SelectionStream& stream) { return stream.type == type; });
574 //------------------------------------------------------------------------------
575 // main class
576 //------------------------------------------------------------------------------
578 void CVideoPlayer::CreatePlayers()
580 if (m_players_created)
581 return;
583 m_VideoPlayerVideo =
584 new CVideoPlayerVideo(&m_clock, &m_overlayContainer, m_messenger, m_renderManager,
585 *m_processInfo, m_messageQueueTimeSize);
586 m_VideoPlayerAudio =
587 new CVideoPlayerAudio(&m_clock, m_messenger, *m_processInfo, m_messageQueueTimeSize);
588 m_VideoPlayerSubtitle = new CVideoPlayerSubtitle(&m_overlayContainer, *m_processInfo);
589 m_VideoPlayerTeletext = new CDVDTeletextData(*m_processInfo);
590 m_VideoPlayerRadioRDS = new CDVDRadioRDSData(*m_processInfo);
591 m_VideoPlayerAudioID3 = std::make_unique<CVideoPlayerAudioID3>(*m_processInfo);
592 m_players_created = true;
595 void CVideoPlayer::DestroyPlayers()
597 if (!m_players_created)
598 return;
600 delete m_VideoPlayerVideo;
601 delete m_VideoPlayerAudio;
602 delete m_VideoPlayerSubtitle;
603 delete m_VideoPlayerTeletext;
604 delete m_VideoPlayerRadioRDS;
605 m_VideoPlayerAudioID3.reset();
607 m_players_created = false;
610 CVideoPlayer::CVideoPlayer(IPlayerCallback& callback)
611 : IPlayer(callback),
612 CThread("VideoPlayer"),
613 m_CurrentAudio(STREAM_AUDIO, VideoPlayer_AUDIO),
614 m_CurrentVideo(STREAM_VIDEO, VideoPlayer_VIDEO),
615 m_CurrentSubtitle(STREAM_SUBTITLE, VideoPlayer_SUBTITLE),
616 m_CurrentTeletext(STREAM_TELETEXT, VideoPlayer_TELETEXT),
617 m_CurrentRadioRDS(STREAM_RADIO_RDS, VideoPlayer_RDS),
618 m_CurrentAudioID3(STREAM_AUDIO_ID3, VideoPlayer_ID3),
619 m_messenger("player"),
620 m_outboundEvents(std::make_unique<CJobQueue>(false, 1, CJob::PRIORITY_NORMAL)),
621 m_pInputStream(nullptr),
622 m_pDemuxer(nullptr),
623 m_pSubtitleDemuxer(nullptr),
624 m_pCCDemuxer(nullptr),
625 m_renderManager(m_clock, this)
627 m_players_created = false;
629 m_dvd.Clear();
630 m_State.Clear();
632 m_bAbortRequest = false;
633 m_offset_pts = 0.0;
634 m_playSpeed = DVD_PLAYSPEED_NORMAL;
635 m_streamPlayerSpeed = DVD_PLAYSPEED_NORMAL;
636 m_caching = CACHESTATE_DONE;
637 m_HasVideo = false;
638 m_HasAudio = false;
639 m_UpdateStreamDetails = false;
641 const int tenthsSeconds = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
642 CSettings::SETTING_VIDEOPLAYER_QUEUETIMESIZE);
644 m_messageQueueTimeSize = static_cast<double>(tenthsSeconds) / 10.0;
646 m_SkipCommercials = true;
648 m_processInfo.reset(CProcessInfo::CreateInstance());
649 // if we have a gui, register the cache
650 m_processInfo->SetDataCache(&CServiceBroker::GetDataCacheCore());
651 m_processInfo->SetSpeed(1.0);
652 m_processInfo->SetTempo(1.0);
653 m_processInfo->SetFrameAdvance(false);
655 CreatePlayers();
657 m_displayLost = false;
658 m_error = false;
659 m_bCloseRequest = false;
660 CServiceBroker::GetWinSystem()->Register(this);
663 CVideoPlayer::~CVideoPlayer()
665 CServiceBroker::GetWinSystem()->Unregister(this);
667 CloseFile();
668 DestroyPlayers();
670 while (m_outboundEvents->IsProcessing())
672 CThread::Sleep(10ms);
676 bool CVideoPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
678 CLog::Log(LOGINFO, "VideoPlayer::OpenFile: {}", CURL::GetRedacted(file.GetPath()));
680 if (IsRunning())
682 CDVDMsgOpenFile::FileParams params;
683 params.m_item = file;
684 params.m_options = options;
685 params.m_item.SetMimeTypeForInternetFile();
686 m_messenger.Put(std::make_shared<CDVDMsgOpenFile>(params), 1);
688 return true;
691 m_item = file;
692 m_playerOptions = options;
694 m_processInfo->SetPlayTimes(0,0,0,0);
695 m_bAbortRequest = false;
696 m_error = false;
697 m_bCloseRequest = false;
698 m_renderManager.PreInit();
700 Create();
701 m_messenger.Init();
703 m_callback.OnPlayBackStarted(m_item);
705 return true;
708 bool CVideoPlayer::CloseFile(bool reopen)
710 CLog::Log(LOGINFO, "CVideoPlayer::CloseFile()");
712 // set the abort request so that other threads can finish up
713 m_bAbortRequest = true;
714 m_bCloseRequest = true;
716 // tell demuxer to abort
717 if(m_pDemuxer)
718 m_pDemuxer->Abort();
720 if(m_pSubtitleDemuxer)
721 m_pSubtitleDemuxer->Abort();
723 if(m_pInputStream)
724 m_pInputStream->Abort();
726 m_renderManager.UnInit();
728 CLog::Log(LOGINFO, "VideoPlayer: waiting for threads to exit");
730 // wait for the main thread to finish up
731 // since this main thread cleans up all other resources and threads
732 // we are done after the StopThread call
734 CSingleExit exitlock(CServiceBroker::GetWinSystem()->GetGfxContext());
735 StopThread();
738 m_Edl.Clear();
739 CServiceBroker::GetDataCacheCore().Reset();
741 m_HasVideo = false;
742 m_HasAudio = false;
744 CLog::Log(LOGINFO, "VideoPlayer: finished waiting");
745 return true;
748 bool CVideoPlayer::IsPlaying() const
750 return !m_bStop;
753 void CVideoPlayer::OnStartup()
755 m_CurrentVideo.Clear();
756 m_CurrentAudio.Clear();
757 m_CurrentSubtitle.Clear();
758 m_CurrentTeletext.Clear();
759 m_CurrentRadioRDS.Clear();
760 m_CurrentAudioID3.Clear();
762 UTILS::FONT::ClearTemporaryFonts();
765 bool CVideoPlayer::OpenInputStream()
767 if (m_pInputStream.use_count() > 1)
768 throw std::runtime_error("m_pInputStream reference count is greater than 1");
769 m_pInputStream.reset();
771 CLog::Log(LOGINFO, "Creating InputStream");
773 m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_item, true);
774 if (m_pInputStream == nullptr)
776 CLog::Log(LOGERROR, "CVideoPlayer::OpenInputStream - unable to create input stream for [{}]",
777 CURL::GetRedacted(m_item.GetPath()));
778 return false;
781 if (!m_pInputStream->Open())
783 CLog::Log(LOGERROR, "CVideoPlayer::OpenInputStream - error opening [{}]",
784 CURL::GetRedacted(m_item.GetPath()));
785 return false;
788 // find any available external subtitles for non dvd files
789 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) &&
790 !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
792 // find any available external subtitles
793 std::vector<std::string> filenames;
795 if (!URIUtils::IsUPnP(m_item.GetPath()) &&
796 !m_item.GetProperty("no-ext-subs-scan").asBoolean(false))
797 CUtil::ScanForExternalSubtitles(m_item.GetDynPath(), filenames);
799 // load any subtitles from file item
800 std::string key("subtitle:1");
801 for (unsigned s = 1; m_item.HasProperty(key); key = StringUtils::Format("subtitle:{}", ++s))
802 filenames.push_back(m_item.GetProperty(key).asString());
804 for (unsigned int i=0;i<filenames.size();i++)
806 // if vobsub subtitle:
807 if (URIUtils::HasExtension(filenames[i], ".idx"))
809 std::string strSubFile;
810 if (CUtil::FindVobSubPair( filenames, filenames[i], strSubFile))
811 AddSubtitleFile(filenames[i], strSubFile);
813 else
815 if (!CUtil::IsVobSub(filenames, filenames[i] ))
817 AddSubtitleFile(filenames[i]);
820 } // end loop over all subtitle files
823 m_clock.Reset();
824 m_dvd.Clear();
826 return true;
829 bool CVideoPlayer::OpenDemuxStream()
831 CloseDemuxer();
833 CLog::Log(LOGINFO, "Creating Demuxer");
835 int attempts = 10;
836 while (!m_bStop && attempts-- > 0)
838 m_pDemuxer.reset(CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream));
839 if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
841 continue;
843 else if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE)
845 CLog::Log(LOGDEBUG, "{} - New stream available from input, retry open", __FUNCTION__);
846 continue;
848 break;
851 if (!m_pDemuxer)
853 CLog::Log(LOGERROR, "{} - Error creating demuxer", __FUNCTION__);
854 return false;
857 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
858 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
859 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
860 m_pDemuxer->GetPrograms(m_programs);
861 UpdateContent();
862 m_demuxerSpeed = DVD_PLAYSPEED_NORMAL;
863 m_processInfo->SetStateRealtime(false);
865 int64_t len = m_pInputStream->GetLength();
866 int64_t tim = m_pDemuxer->GetStreamLength();
867 if (len > 0 && tim > 0)
868 m_pInputStream->SetReadRate(static_cast<uint32_t>(len * 1000 / tim));
870 m_offset_pts = 0;
872 return true;
875 void CVideoPlayer::CloseDemuxer()
877 m_pDemuxer.reset();
878 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
880 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
881 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
882 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
885 void CVideoPlayer::OpenDefaultStreams(bool reset)
887 // if input stream dictate, we will open later
888 if (m_dvd.iSelectedAudioStream >= 0 ||
889 m_dvd.iSelectedSPUStream >= 0)
890 return;
892 bool valid;
894 // open video stream
895 valid = false;
897 PredicateVideoFilter vf(m_processInfo->GetVideoSettings().m_VideoStream);
898 for (const auto &stream : m_SelectionStreams.Get(STREAM_VIDEO, vf))
900 if (OpenStream(m_CurrentVideo, stream.demuxerId, stream.id, stream.source, reset))
902 valid = true;
903 break;
906 if (!valid)
908 CloseStream(m_CurrentVideo, true);
909 m_processInfo->ResetVideoCodecInfo();
912 // open audio stream
913 valid = false;
914 if (!m_playerOptions.videoOnly)
916 PredicateAudioFilter af(m_processInfo->GetVideoSettings().m_AudioStream, m_playerOptions.preferStereo);
917 for (const auto &stream : m_SelectionStreams.Get(STREAM_AUDIO, af))
919 if(OpenStream(m_CurrentAudio, stream.demuxerId, stream.id, stream.source, reset))
921 valid = true;
922 break;
927 if(!valid)
929 CloseStream(m_CurrentAudio, true);
930 m_processInfo->ResetAudioCodecInfo();
933 // enable or disable subtitles
934 bool visible = m_processInfo->GetVideoSettings().m_SubtitleOn;
936 // open subtitle stream
937 SelectionStream as = m_SelectionStreams.Get(STREAM_AUDIO, GetAudioStream());
938 PredicateSubtitlePriority psp(as.language,
939 m_processInfo->GetVideoSettings().m_SubtitleStream,
940 m_processInfo->GetVideoSettings().m_SubtitleOn);
941 valid = false;
942 // We need to close CC subtitles to avoid conflicts with external sub stream
943 if (m_CurrentSubtitle.source == STREAM_SOURCE_VIDEOMUX)
944 CloseStream(m_CurrentSubtitle, false);
946 for (const auto &stream : m_SelectionStreams.Get(STREAM_SUBTITLE, psp))
948 if (OpenStream(m_CurrentSubtitle, stream.demuxerId, stream.id, stream.source))
950 valid = true;
951 if(!psp.relevant(stream))
952 visible = false;
953 else if(stream.flags & StreamFlags::FLAG_FORCED)
954 visible = true;
955 break;
958 if(!valid)
959 CloseStream(m_CurrentSubtitle, false);
961 // only set subtitle visibility if state not stored by dvd navigator, because navigator will restore it (if visible)
962 if (!std::dynamic_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream) ||
963 m_playerOptions.state.empty())
965 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
966 if (valid && !visible)
967 SetEnableStream(m_CurrentSubtitle, false);
969 SetSubtitleVisibleInternal(visible);
972 // open teletext stream
973 valid = false;
974 for (const auto &stream : m_SelectionStreams.Get(STREAM_TELETEXT))
976 if (OpenStream(m_CurrentTeletext, stream.demuxerId, stream.id, stream.source))
978 valid = true;
979 break;
982 if(!valid)
983 CloseStream(m_CurrentTeletext, false);
985 // open RDS stream
986 valid = false;
987 for (const auto &stream : m_SelectionStreams.Get(STREAM_RADIO_RDS))
989 if (OpenStream(m_CurrentRadioRDS, stream.demuxerId, stream.id, stream.source))
991 valid = true;
992 break;
995 if(!valid)
996 CloseStream(m_CurrentRadioRDS, false);
998 // open ID3 stream
999 valid = false;
1000 for (const auto& stream : m_SelectionStreams.Get(STREAM_AUDIO_ID3))
1002 if (OpenStream(m_CurrentAudioID3, stream.demuxerId, stream.id, stream.source))
1004 valid = true;
1005 break;
1008 if (!valid)
1009 CloseStream(m_CurrentAudioID3, false);
1011 // disable demux streams
1012 if (NETWORK::IsRemote(m_item) && m_pDemuxer)
1014 for (auto &stream : m_SelectionStreams.m_Streams)
1016 if (STREAM_SOURCE_MASK(stream.source) == STREAM_SOURCE_DEMUX)
1018 if (stream.id != m_CurrentVideo.id && stream.id != m_CurrentAudio.id &&
1019 stream.id != m_CurrentSubtitle.id && stream.id != m_CurrentTeletext.id &&
1020 stream.id != m_CurrentRadioRDS.id && stream.id != m_CurrentAudioID3.id)
1022 m_pDemuxer->EnableStream(stream.demuxerId, stream.id, false);
1029 bool CVideoPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
1032 // check if we should read from subtitle demuxer
1033 if (m_pSubtitleDemuxer && m_VideoPlayerSubtitle->AcceptsData())
1035 packet = m_pSubtitleDemuxer->Read();
1037 if(packet)
1039 UpdateCorrection(packet, m_offset_pts);
1040 if(packet->iStreamId < 0)
1041 return true;
1043 stream = m_pSubtitleDemuxer->GetStream(packet->demuxerId, packet->iStreamId);
1044 if (!stream)
1046 CLog::Log(LOGERROR, "{} - Error demux packet doesn't belong to a valid stream",
1047 __FUNCTION__);
1048 return false;
1050 if (stream->source == STREAM_SOURCE_NONE)
1052 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
1053 m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer.get());
1054 UpdateContent();
1056 return true;
1060 // read a data frame from stream.
1061 if (m_pDemuxer)
1062 packet = m_pDemuxer->Read();
1064 if (packet)
1066 // stream changed, update and open defaults
1067 if (packet->iStreamId == DMX_SPECIALID_STREAMCHANGE)
1069 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1070 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1071 m_pDemuxer->GetPrograms(m_programs);
1072 UpdateContent();
1073 OpenDefaultStreams(false);
1075 // reevaluate HasVideo/Audio, we may have switched from/to a radio channel
1076 if(m_CurrentVideo.id < 0)
1077 m_HasVideo = false;
1078 if(m_CurrentAudio.id < 0)
1079 m_HasAudio = false;
1081 return true;
1084 UpdateCorrection(packet, m_offset_pts);
1086 if(packet->iStreamId < 0)
1087 return true;
1089 if(m_pDemuxer)
1091 stream = m_pDemuxer->GetStream(packet->demuxerId, packet->iStreamId);
1092 if (!stream)
1094 CLog::Log(LOGERROR, "{} - Error demux packet doesn't belong to a valid stream",
1095 __FUNCTION__);
1096 return false;
1098 if(stream->source == STREAM_SOURCE_NONE)
1100 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1101 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1102 UpdateContent();
1105 return true;
1107 return false;
1110 bool CVideoPlayer::IsValidStream(const CCurrentStream& stream)
1112 if(stream.id<0)
1113 return true; // we consider non selected as valid
1115 int source = STREAM_SOURCE_MASK(stream.source);
1116 if(source == STREAM_SOURCE_TEXT)
1117 return true;
1118 if (source == STREAM_SOURCE_DEMUX_SUB)
1120 CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.demuxerId, stream.id);
1121 if(st == NULL || st->disabled)
1122 return false;
1123 if(st->type != stream.type)
1124 return false;
1125 return true;
1127 if (source == STREAM_SOURCE_DEMUX)
1129 CDemuxStream* st = m_pDemuxer->GetStream(stream.demuxerId, stream.id);
1130 if(st == NULL || st->disabled)
1131 return false;
1132 if(st->type != stream.type)
1133 return false;
1135 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1137 if (stream.type == STREAM_AUDIO && st->dvdNavId != m_dvd.iSelectedAudioStream)
1138 return false;
1139 if(stream.type == STREAM_SUBTITLE && st->dvdNavId != m_dvd.iSelectedSPUStream)
1140 return false;
1143 return true;
1145 if (source == STREAM_SOURCE_VIDEOMUX)
1147 CDemuxStream* st = m_pCCDemuxer->GetStream(stream.id);
1148 if (st == NULL || st->disabled)
1149 return false;
1150 if (st->type != stream.type)
1151 return false;
1152 return true;
1155 return false;
1158 bool CVideoPlayer::IsBetterStream(const CCurrentStream& current, CDemuxStream* stream)
1160 // Do not reopen non-video streams if we're in video-only mode
1161 if (m_playerOptions.videoOnly && current.type != STREAM_VIDEO)
1162 return false;
1164 if(stream->disabled)
1165 return false;
1167 if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) ||
1168 m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY)))
1170 int source_type;
1172 source_type = STREAM_SOURCE_MASK(current.source);
1173 if (source_type != STREAM_SOURCE_DEMUX &&
1174 source_type != STREAM_SOURCE_NONE)
1175 return false;
1177 source_type = STREAM_SOURCE_MASK(stream->source);
1178 if(source_type != STREAM_SOURCE_DEMUX ||
1179 stream->type != current.type ||
1180 stream->uniqueId == current.id)
1181 return false;
1183 if(current.type == STREAM_AUDIO && stream->dvdNavId == m_dvd.iSelectedAudioStream)
1184 return true;
1185 if(current.type == STREAM_SUBTITLE && stream->dvdNavId == m_dvd.iSelectedSPUStream)
1186 return true;
1187 if(current.type == STREAM_VIDEO && current.id < 0)
1188 return true;
1190 else
1192 if(stream->source == current.source &&
1193 stream->uniqueId == current.id &&
1194 stream->demuxerId == current.demuxerId)
1195 return false;
1197 if(stream->type != current.type)
1198 return false;
1200 if(current.type == STREAM_SUBTITLE)
1201 return false;
1203 if(current.id < 0)
1204 return true;
1206 return false;
1209 void CVideoPlayer::CheckBetterStream(CCurrentStream& current, CDemuxStream* stream)
1211 IDVDStreamPlayer* player = GetStreamPlayer(current.player);
1212 if (!IsValidStream(current) && (player == NULL || player->IsStalled()))
1213 CloseStream(current, true);
1215 if (IsBetterStream(current, stream))
1216 OpenStream(current, stream->demuxerId, stream->uniqueId, stream->source);
1219 void CVideoPlayer::Prepare()
1221 CFFmpegLog::SetLogLevel(1);
1222 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1223 m_processInfo->SetSpeed(1.0);
1224 m_processInfo->SetTempo(1.0);
1225 m_processInfo->SetFrameAdvance(false);
1226 m_State.Clear();
1227 m_CurrentVideo.hint.Clear();
1228 m_CurrentAudio.hint.Clear();
1229 m_CurrentSubtitle.hint.Clear();
1230 m_CurrentTeletext.hint.Clear();
1231 m_CurrentRadioRDS.hint.Clear();
1232 m_CurrentAudioID3.hint.Clear();
1233 m_SpeedState.Reset(DVD_NOPTS_VALUE);
1234 m_offset_pts = 0;
1235 m_CurrentAudio.lastdts = DVD_NOPTS_VALUE;
1236 m_CurrentVideo.lastdts = DVD_NOPTS_VALUE;
1238 IPlayerCallback *cb = &m_callback;
1239 CFileItem fileItem = m_item;
1240 m_outboundEvents->Submit([=]() {
1241 cb->RequestVideoSettings(fileItem);
1244 if (!OpenInputStream())
1246 m_bAbortRequest = true;
1247 m_error = true;
1248 return;
1251 bool discStateRestored = false;
1252 if (std::shared_ptr<CDVDInputStream::IMenus> ptr = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1254 CLog::Log(LOGINFO, "VideoPlayer: playing a file with menu's");
1256 if (!m_playerOptions.state.empty())
1258 discStateRestored = ptr->SetState(m_playerOptions.state);
1260 else if(std::shared_ptr<CDVDInputStreamNavigator> nav = std::dynamic_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream))
1262 nav->EnableSubtitleStream(m_processInfo->GetVideoSettings().m_SubtitleOn);
1266 if (!OpenDemuxStream())
1268 m_bAbortRequest = true;
1269 m_error = true;
1270 return;
1272 // give players a chance to reconsider now codecs are known
1273 CreatePlayers();
1275 if (!discStateRestored)
1276 OpenDefaultStreams();
1279 * Check to see if the demuxer should start at something other than time 0. This will be the case
1280 * if there was a start time specified as part of the "Start from where last stopped" (aka
1281 * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
1283 std::chrono::milliseconds starttime = 0ms;
1284 if (m_playerOptions.starttime > 0 || m_playerOptions.startpercent > 0)
1286 if (m_playerOptions.startpercent > 0 && m_pDemuxer)
1288 std::chrono::milliseconds playerStartTime =
1289 std::chrono::milliseconds(static_cast<int>((static_cast<double>(
1290 m_pDemuxer->GetStreamLength() * (m_playerOptions.startpercent / 100.0)))));
1291 starttime = m_Edl.GetTimeAfterRestoringCuts(playerStartTime);
1293 else
1295 starttime =
1296 m_Edl.GetTimeAfterRestoringCuts(std::chrono::duration_cast<std::chrono::milliseconds>(
1297 std::chrono::seconds(static_cast<int>(m_playerOptions.starttime))));
1299 CLog::Log(LOGDEBUG, "{} - Start position set to last stopped position: {}", __FUNCTION__,
1300 starttime.count());
1302 else
1304 const auto hasEdit = m_Edl.InEdit(starttime);
1305 if (hasEdit)
1307 const auto& edit = hasEdit.value();
1308 // save last edit times
1309 m_Edl.SetLastEditTime(edit->start);
1310 m_Edl.SetLastEditActionType(edit->action);
1312 if (edit->action == EDL::Action::CUT)
1314 starttime = edit->end;
1315 CLog::Log(LOGDEBUG, "{} - Start position set to end of first cut: {}", __FUNCTION__,
1316 starttime.count());
1318 else if (edit->action == EDL::Action::COMM_BREAK)
1320 if (m_SkipCommercials)
1322 starttime = edit->end;
1323 CLog::Log(LOGDEBUG, "{} - Start position set to end of first commercial break: {}",
1324 __FUNCTION__, starttime.count());
1327 const std::shared_ptr<CAdvancedSettings> advancedSettings =
1328 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
1329 if (advancedSettings && advancedSettings->m_EdlDisplayCommbreakNotifications)
1331 const std::string timeString =
1332 StringUtils::SecondsToTimeString(edit->end.count(), TIME_FORMAT_MM_SS);
1333 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25011), timeString);
1339 if (starttime > 0ms)
1341 double startpts = DVD_NOPTS_VALUE;
1342 if (m_pDemuxer)
1344 if (m_pDemuxer->SeekTime(starttime.count(), true, &startpts))
1346 FlushBuffers(starttime.count() / 1000 * AV_TIME_BASE, true, true);
1347 CLog::Log(LOGDEBUG, "{} - starting demuxer from: {}", __FUNCTION__, starttime.count());
1349 else
1350 CLog::Log(LOGDEBUG, "{} - failed to start demuxing from: {}", __FUNCTION__,
1351 starttime.count());
1354 if (m_pSubtitleDemuxer)
1356 if (m_pSubtitleDemuxer->SeekTime(starttime.count(), true, &startpts))
1357 CLog::Log(LOGDEBUG, "{} - starting subtitle demuxer from: {}", __FUNCTION__,
1358 starttime.count());
1359 else
1360 CLog::Log(LOGDEBUG, "{} - failed to start subtitle demuxing from: {}", __FUNCTION__,
1361 starttime.count());
1364 m_clock.Discontinuity(DVD_MSEC_TO_TIME(starttime.count()));
1367 UpdatePlayState(0);
1369 SetCaching(CACHESTATE_FLUSH);
1372 void CVideoPlayer::Process()
1374 // Try to resolve the correct mime type. This can take some time, for example if a requested
1375 // item is located at a slow/not reachable remote source. So, do mime type detection in vp worker
1376 // thread, not directly when initalizing the player to keep GUI responsible.
1377 m_item.SetMimeTypeForInternetFile();
1379 CServiceBroker::GetWinSystem()->RegisterRenderLoop(this);
1381 Prepare();
1383 while (!m_bAbortRequest)
1385 // check display lost
1386 if (m_displayLost)
1388 CThread::Sleep(50ms);
1389 continue;
1392 // check if in an edit (cut or commercial break) that should be automatically skipped
1393 CheckAutoSceneSkip();
1395 // handle messages send to this thread, like seek or demuxer reset requests
1396 HandleMessages();
1398 if (m_bAbortRequest)
1399 break;
1401 // should we open a new input stream?
1402 if (!m_pInputStream)
1404 if (OpenInputStream() == false)
1406 m_bAbortRequest = true;
1407 break;
1411 // should we open a new demuxer?
1412 if (!m_pDemuxer)
1414 if (m_pInputStream->NextStream() == CDVDInputStream::NEXTSTREAM_NONE)
1415 break;
1417 if (m_pInputStream->IsEOF())
1418 break;
1420 if (OpenDemuxStream() == false)
1422 m_bAbortRequest = true;
1423 break;
1426 // on channel switch we don't want to close stream players at this
1427 // time. we'll get the stream change event later
1428 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) ||
1429 !m_SelectionStreams.m_Streams.empty())
1430 OpenDefaultStreams();
1432 UpdatePlayState(0);
1435 // handle eventual seeks due to playspeed
1436 HandlePlaySpeed();
1438 // update player state
1439 UpdatePlayState(200);
1441 // make sure we run subtitle process here
1442 m_VideoPlayerSubtitle->Process(m_clock.GetClock() + m_State.time_offset - m_VideoPlayerVideo->GetSubtitleDelay(), m_State.time_offset);
1444 // tell demuxer if we want to fill buffers
1445 if (m_demuxerSpeed != DVD_PLAYSPEED_PAUSE)
1447 int audioLevel = 90;
1448 int videoLevel = 90;
1449 bool fillBuffer = false;
1450 if (m_CurrentAudio.id >= 0)
1451 audioLevel = m_VideoPlayerAudio->GetLevel();
1452 if (m_CurrentVideo.id >= 0)
1453 videoLevel = m_processInfo->GetLevelVQ();
1454 if (videoLevel < 85 && audioLevel < 85)
1456 fillBuffer = true;
1458 if (m_pDemuxer)
1459 m_pDemuxer->FillBuffer(fillBuffer);
1462 // if the queues are full, no need to read more
1463 if ((!m_VideoPlayerAudio->AcceptsData() && m_CurrentAudio.id >= 0) ||
1464 (!m_VideoPlayerVideo->AcceptsData() && m_CurrentVideo.id >= 0))
1466 if (m_playSpeed == DVD_PLAYSPEED_PAUSE &&
1467 m_demuxerSpeed != DVD_PLAYSPEED_PAUSE)
1469 if (m_pDemuxer)
1470 m_pDemuxer->SetSpeed(DVD_PLAYSPEED_PAUSE);
1471 m_demuxerSpeed = DVD_PLAYSPEED_PAUSE;
1473 CThread::Sleep(10ms);
1474 continue;
1477 // adjust demuxer speed; some rtsp servers wants to know for i.e. ff
1478 // delay pause until queue is full
1479 if (m_playSpeed != DVD_PLAYSPEED_PAUSE &&
1480 m_demuxerSpeed != m_playSpeed)
1482 if (m_pDemuxer)
1483 m_pDemuxer->SetSpeed(m_playSpeed);
1484 m_demuxerSpeed = m_playSpeed;
1487 DemuxPacket* pPacket = NULL;
1488 CDemuxStream *pStream = NULL;
1489 ReadPacket(pPacket, pStream);
1490 if (pPacket && !pStream)
1492 /* probably a empty packet, just free it and move on */
1493 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
1494 continue;
1497 if (!pPacket)
1499 // when paused, demuxer could be be returning empty
1500 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
1501 continue;
1503 // check for a still frame state
1504 if (std::shared_ptr<CDVDInputStream::IMenus> pStream = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1506 // stills will be skipped
1507 if(m_dvd.state == DVDSTATE_STILL)
1509 if (m_dvd.iDVDStillTime > 0ms)
1511 const auto now = std::chrono::steady_clock::now();
1512 const auto duration = now - m_dvd.iDVDStillStartTime;
1514 if (duration >= m_dvd.iDVDStillTime)
1516 m_dvd.iDVDStillTime = 0ms;
1517 m_dvd.iDVDStillStartTime = {};
1518 m_dvd.state = DVDSTATE_NORMAL;
1519 pStream->SkipStill();
1520 continue;
1526 // if there is another stream available, reopen demuxer
1527 CDVDInputStream::ENextStream next = m_pInputStream->NextStream();
1528 if(next == CDVDInputStream::NEXTSTREAM_OPEN)
1530 CloseDemuxer();
1532 SetCaching(CACHESTATE_DONE);
1533 CLog::Log(LOGINFO, "VideoPlayer: next stream, wait for old streams to be finished");
1534 CloseStream(m_CurrentAudio, true);
1535 CloseStream(m_CurrentVideo, true);
1537 m_CurrentAudio.Clear();
1538 m_CurrentVideo.Clear();
1539 m_CurrentSubtitle.Clear();
1540 continue;
1543 // input stream asked us to just retry
1544 if(next == CDVDInputStream::NEXTSTREAM_RETRY)
1546 CThread::Sleep(100ms);
1547 continue;
1550 if (m_CurrentVideo.inited)
1552 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::VIDEO_DRAIN));
1555 m_CurrentAudio.inited = false;
1556 m_CurrentVideo.inited = false;
1557 m_CurrentSubtitle.inited = false;
1558 m_CurrentTeletext.inited = false;
1559 m_CurrentRadioRDS.inited = false;
1560 m_CurrentAudioID3.inited = false;
1562 // if we are caching, start playing it again
1563 SetCaching(CACHESTATE_DONE);
1565 // while players are still playing, keep going to allow seekbacks
1566 if (m_VideoPlayerAudio->HasData() ||
1567 m_VideoPlayerVideo->HasData())
1569 CThread::Sleep(100ms);
1570 continue;
1573 if (!m_pInputStream->IsEOF())
1574 CLog::Log(LOGINFO, "{} - eof reading from demuxer", __FUNCTION__);
1576 break;
1579 // see if we can find something better to play
1580 CheckBetterStream(m_CurrentAudio, pStream);
1581 CheckBetterStream(m_CurrentVideo, pStream);
1582 CheckBetterStream(m_CurrentSubtitle, pStream);
1583 CheckBetterStream(m_CurrentTeletext, pStream);
1584 CheckBetterStream(m_CurrentRadioRDS, pStream);
1585 CheckBetterStream(m_CurrentAudioID3, pStream);
1587 // demux video stream
1588 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SUBTITLES_PARSECAPTIONS) && CheckIsCurrent(m_CurrentVideo, pStream, pPacket))
1590 if (m_pCCDemuxer)
1592 bool first = true;
1593 while (!m_bAbortRequest)
1595 DemuxPacket *pkt = m_pCCDemuxer->Read(first ? pPacket : NULL);
1596 if (!pkt)
1597 break;
1599 first = false;
1600 if (m_pCCDemuxer->GetNrOfStreams() != m_SelectionStreams.CountTypeOfSource(STREAM_SUBTITLE, STREAM_SOURCE_VIDEOMUX))
1602 m_SelectionStreams.Clear(STREAM_SUBTITLE, STREAM_SOURCE_VIDEOMUX);
1603 m_SelectionStreams.Update(NULL, m_pCCDemuxer.get(), "");
1604 UpdateContent();
1605 OpenDefaultStreams(false);
1607 CDemuxStream *pSubStream = m_pCCDemuxer->GetStream(pkt->iStreamId);
1608 if (pSubStream && m_CurrentSubtitle.id == pkt->iStreamId && m_CurrentSubtitle.source == STREAM_SOURCE_VIDEOMUX)
1609 ProcessSubData(pSubStream, pkt);
1610 else
1611 CDVDDemuxUtils::FreeDemuxPacket(pkt);
1616 if (IsInMenuInternal())
1618 if (std::shared_ptr<CDVDInputStream::IMenus> menu = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1620 double correction = menu->GetTimeStampCorrection();
1621 if (pPacket->dts != DVD_NOPTS_VALUE && pPacket->dts > correction)
1622 pPacket->dts -= correction;
1623 if (pPacket->pts != DVD_NOPTS_VALUE && pPacket->pts > correction)
1624 pPacket->pts -= correction;
1626 if (m_dvd.syncClock)
1628 m_clock.Discontinuity(pPacket->dts);
1629 m_dvd.syncClock = false;
1633 // process the packet
1634 ProcessPacket(pStream, pPacket);
1638 bool CVideoPlayer::CheckIsCurrent(const CCurrentStream& current,
1639 CDemuxStream* stream,
1640 DemuxPacket* pkg)
1642 if(current.id == pkg->iStreamId &&
1643 current.demuxerId == stream->demuxerId &&
1644 current.source == stream->source &&
1645 current.type == stream->type)
1646 return true;
1647 else
1648 return false;
1651 void CVideoPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
1653 // process packet if it belongs to selected stream.
1654 // for dvd's don't allow automatic opening of streams*/
1656 if (CheckIsCurrent(m_CurrentAudio, pStream, pPacket))
1657 ProcessAudioData(pStream, pPacket);
1658 else if (CheckIsCurrent(m_CurrentVideo, pStream, pPacket))
1659 ProcessVideoData(pStream, pPacket);
1660 else if (CheckIsCurrent(m_CurrentSubtitle, pStream, pPacket))
1661 ProcessSubData(pStream, pPacket);
1662 else if (CheckIsCurrent(m_CurrentTeletext, pStream, pPacket))
1663 ProcessTeletextData(pStream, pPacket);
1664 else if (CheckIsCurrent(m_CurrentRadioRDS, pStream, pPacket))
1665 ProcessRadioRDSData(pStream, pPacket);
1666 else if (CheckIsCurrent(m_CurrentAudioID3, pStream, pPacket))
1667 ProcessAudioID3Data(pStream, pPacket);
1668 else
1670 CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
1674 void CVideoPlayer::CheckStreamChanges(CCurrentStream& current, CDemuxStream* stream)
1676 if (current.stream != (void*)stream
1677 || current.changes != stream->changes)
1679 /* check so that dmuxer hints or extra data hasn't changed */
1680 /* if they have, reopen stream */
1682 if (current.hint != CDVDStreamInfo(*stream, true))
1684 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1685 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1686 UpdateContent();
1687 OpenDefaultStreams(false);
1690 current.stream = (void*)stream;
1691 current.changes = stream->changes;
1695 void CVideoPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
1697 CheckStreamChanges(m_CurrentAudio, pStream);
1699 bool checkcont = CheckContinuity(m_CurrentAudio, pPacket);
1700 UpdateTimestamps(m_CurrentAudio, pPacket);
1702 if (checkcont && (m_CurrentAudio.avsync == CCurrentStream::AV_SYNC_CHECK))
1703 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
1705 bool drop = false;
1706 if (CheckPlayerInit(m_CurrentAudio))
1707 drop = true;
1710 * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped.
1712 if (CheckSceneSkip(m_CurrentAudio))
1714 drop = true;
1716 else
1718 const auto hasEdit = m_Edl.InEdit(
1719 std::chrono::milliseconds(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts)));
1720 if (hasEdit && hasEdit.value()->action == EDL::Action::MUTE)
1721 drop = true;
1724 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1726 if (!drop)
1727 m_CurrentAudio.packets++;
1730 void CVideoPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
1732 CheckStreamChanges(m_CurrentVideo, pStream);
1733 bool checkcont = false;
1735 if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
1737 checkcont = CheckContinuity(m_CurrentVideo, pPacket);
1738 UpdateTimestamps(m_CurrentVideo, pPacket);
1740 if (checkcont && (m_CurrentVideo.avsync == CCurrentStream::AV_SYNC_CHECK))
1741 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
1743 bool drop = false;
1744 if (CheckPlayerInit(m_CurrentVideo))
1745 drop = true;
1747 if (CheckSceneSkip(m_CurrentVideo))
1748 drop = true;
1750 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1752 if (!drop)
1753 m_CurrentVideo.packets++;
1756 void CVideoPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
1758 CheckStreamChanges(m_CurrentSubtitle, pStream);
1760 UpdateTimestamps(m_CurrentSubtitle, pPacket);
1762 bool drop = false;
1763 if (CheckPlayerInit(m_CurrentSubtitle))
1764 drop = true;
1766 if (CheckSceneSkip(m_CurrentSubtitle))
1767 drop = true;
1769 m_VideoPlayerSubtitle->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1771 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1772 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_NORMAL);
1775 void CVideoPlayer::ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket)
1777 CheckStreamChanges(m_CurrentTeletext, pStream);
1779 UpdateTimestamps(m_CurrentTeletext, pPacket);
1781 bool drop = false;
1782 if (CheckPlayerInit(m_CurrentTeletext))
1783 drop = true;
1785 if (CheckSceneSkip(m_CurrentTeletext))
1786 drop = true;
1788 m_VideoPlayerTeletext->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1791 void CVideoPlayer::ProcessRadioRDSData(CDemuxStream* pStream, DemuxPacket* pPacket)
1793 CheckStreamChanges(m_CurrentRadioRDS, pStream);
1795 UpdateTimestamps(m_CurrentRadioRDS, pPacket);
1797 bool drop = false;
1798 if (CheckPlayerInit(m_CurrentRadioRDS))
1799 drop = true;
1801 if (CheckSceneSkip(m_CurrentRadioRDS))
1802 drop = true;
1804 m_VideoPlayerRadioRDS->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1807 void CVideoPlayer::ProcessAudioID3Data(CDemuxStream* pStream, DemuxPacket* pPacket)
1809 CheckStreamChanges(m_CurrentAudioID3, pStream);
1811 UpdateTimestamps(m_CurrentAudioID3, pPacket);
1813 bool drop = false;
1814 if (CheckPlayerInit(m_CurrentAudioID3))
1815 drop = true;
1817 if (CheckSceneSkip(m_CurrentAudioID3))
1818 drop = true;
1820 m_VideoPlayerAudioID3->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1823 CacheInfo CVideoPlayer::GetCachingTimes()
1825 CacheInfo info{};
1827 if (!m_pInputStream || !m_pDemuxer)
1828 return info;
1830 XFILE::SCacheStatus status;
1831 if (!m_pInputStream->GetCacheStatus(&status))
1832 return info;
1834 const uint64_t& maxforward = status.maxforward;
1835 const uint64_t& cached = status.forward;
1836 const uint32_t& currate = status.currate;
1837 const uint32_t& maxrate = status.maxrate;
1838 const uint32_t& lowrate = status.lowrate;
1840 int64_t length = m_pInputStream->GetLength();
1841 int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR);
1843 if (length <= 0 || remain < 0)
1844 return info;
1846 double queueTime = GetQueueTime();
1847 double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
1848 double queued = 1000.0 * queueTime / play_sbp;
1850 info.level = 0.0;
1851 info.offset = (cached + queued) / length;
1852 info.time = 0.0;
1853 info.valid = true;
1855 if (currate == 0)
1856 return info;
1858 // estimated playback time of current cached bytes
1859 const double cacheTime = (static_cast<double>(cached) / currate) + (queueTime / 1000.0);
1861 // cache level as current forward bytes / max forward bytes [0.0 - 1.0]
1862 const double cacheLevel = (maxforward > 0) ? static_cast<double>(cached) / maxforward : 0.0;
1864 info.time = cacheTime;
1866 if (lowrate > 0)
1868 // buffer is full & our read rate is too low
1869 CLog::Log(LOGDEBUG, "Readrate {} was too low with {} required", lowrate, maxrate);
1870 info.level = -1.0;
1872 else
1873 info.level = cacheLevel;
1875 return info;
1878 void CVideoPlayer::HandlePlaySpeed()
1880 const bool isInMenu = IsInMenuInternal();
1881 const bool tolerateStall =
1882 isInMenu || (m_CurrentVideo.hint.flags & StreamFlags::FLAG_STILL_IMAGES);
1884 if (tolerateStall && m_caching != CACHESTATE_DONE)
1885 SetCaching(CACHESTATE_DONE);
1887 if (m_caching == CACHESTATE_FULL)
1889 CacheInfo cache = GetCachingTimes();
1890 if (cache.valid)
1892 if (cache.level < 0.0)
1894 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(21454), g_localizeStrings.Get(21455));
1895 SetCaching(CACHESTATE_INIT);
1897 // Note: Previously used cache.level >= 1 would keep video stalled
1898 // event after cache was full
1899 // Talk link: https://github.com/xbmc/xbmc/pull/23760
1900 if (cache.time > m_messageQueueTimeSize)
1901 SetCaching(CACHESTATE_INIT);
1903 else
1905 if ((!m_VideoPlayerAudio->AcceptsData() && m_CurrentAudio.id >= 0) ||
1906 (!m_VideoPlayerVideo->AcceptsData() && m_CurrentVideo.id >= 0))
1907 SetCaching(CACHESTATE_INIT);
1910 // if audio stream stalled, wait until demux queue filled 10%
1911 if (m_pInputStream->IsRealtime() &&
1912 (m_CurrentAudio.id < 0 || m_VideoPlayerAudio->GetLevel() > 10))
1914 SetCaching(CACHESTATE_INIT);
1918 if (m_caching == CACHESTATE_INIT)
1920 // if all enabled streams have been inited we are done
1921 if ((m_CurrentVideo.id >= 0 || m_CurrentAudio.id >= 0) &&
1922 (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_STARTING) &&
1923 (m_CurrentAudio.id < 0 || m_CurrentAudio.syncState != IDVDStreamPlayer::SYNC_STARTING))
1924 SetCaching(CACHESTATE_PLAY);
1926 // handle exceptions
1927 if (m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0)
1929 if ((!m_VideoPlayerAudio->AcceptsData() || !m_VideoPlayerVideo->AcceptsData()) &&
1930 m_cachingTimer.IsTimePast())
1932 SetCaching(CACHESTATE_DONE);
1937 if (m_caching == CACHESTATE_PLAY)
1939 // if all enabled streams have started playing we are done
1940 if ((m_CurrentVideo.id < 0 || !m_VideoPlayerVideo->IsStalled()) &&
1941 (m_CurrentAudio.id < 0 || !m_VideoPlayerAudio->IsStalled()))
1942 SetCaching(CACHESTATE_DONE);
1945 if (m_caching == CACHESTATE_DONE)
1947 if (m_playSpeed == DVD_PLAYSPEED_NORMAL && !tolerateStall)
1949 // take action if audio or video stream is stalled
1950 if (((m_VideoPlayerAudio->IsStalled() && m_CurrentAudio.inited) ||
1951 (m_VideoPlayerVideo->IsStalled() && m_CurrentVideo.inited)) &&
1952 m_syncTimer.IsTimePast())
1954 if (m_pInputStream->IsRealtime())
1956 if ((m_CurrentAudio.id >= 0 && m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1957 m_VideoPlayerAudio->IsStalled()) ||
1958 (m_CurrentVideo.id >= 0 && m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1959 m_processInfo->GetLevelVQ() == 0))
1961 CLog::Log(LOGDEBUG, "Stream stalled, start buffering. Audio: {} - Video: {}",
1962 m_VideoPlayerAudio->GetLevel(), m_processInfo->GetLevelVQ());
1964 if (m_VideoPlayerAudio->AcceptsData() && m_VideoPlayerVideo->AcceptsData())
1965 SetCaching(CACHESTATE_FULL);
1966 else
1967 FlushBuffers(DVD_NOPTS_VALUE, false, true);
1970 else
1972 // start caching if audio and video have run dry
1973 if (m_VideoPlayerAudio->GetLevel() <= 50 &&
1974 m_processInfo->GetLevelVQ() <= 50)
1976 SetCaching(CACHESTATE_FULL);
1978 else if (m_CurrentAudio.id >= 0 && m_CurrentAudio.inited &&
1979 m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1980 m_VideoPlayerAudio->GetLevel() == 0)
1982 CLog::Log(LOGDEBUG,"CVideoPlayer::HandlePlaySpeed - audio stream stalled, triggering re-sync");
1983 FlushBuffers(DVD_NOPTS_VALUE, true, true);
1984 CDVDMsgPlayerSeek::CMode mode;
1985 mode.time = (int)GetUpdatedTime();
1986 mode.backward = false;
1987 mode.accurate = true;
1988 mode.sync = true;
1989 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
1993 // care for live streams
1994 else if (m_pInputStream->IsRealtime())
1996 if (m_CurrentAudio.id >= 0)
1998 double adjust = -1.0; // a unique value
1999 if (m_clock.GetSpeedAdjust() >= 0 && m_VideoPlayerAudio->GetLevel() < 5)
2000 adjust = -0.05;
2002 if (m_clock.GetSpeedAdjust() < 0 && m_VideoPlayerAudio->GetLevel() > 10)
2003 adjust = 0.0;
2005 if (adjust != -1.0)
2007 m_clock.SetSpeedAdjust(adjust);
2014 // sync streams to clock
2015 if ((m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
2016 (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC))
2018 unsigned int threshold = 20;
2019 if (m_pInputStream->IsRealtime())
2020 threshold = 40;
2022 bool video = m_CurrentVideo.id < 0 || (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
2023 (m_CurrentVideo.packets == 0 && m_CurrentAudio.packets > threshold) ||
2024 (!m_VideoPlayerAudio->AcceptsData() && m_processInfo->GetLevelVQ() < 10);
2025 bool audio = m_CurrentAudio.id < 0 || (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
2026 (m_CurrentAudio.packets == 0 && m_CurrentVideo.packets > threshold) ||
2027 (!m_VideoPlayerVideo->AcceptsData() && m_VideoPlayerAudio->GetLevel() < 10);
2029 if (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC &&
2030 (m_CurrentAudio.avsync == CCurrentStream::AV_SYNC_CONT ||
2031 m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_INSYNC))
2033 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2034 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
2035 m_VideoPlayerAudio->SendMessage(
2036 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, m_clock.GetClock()), 1);
2038 else if (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC &&
2039 (m_CurrentVideo.avsync == CCurrentStream::AV_SYNC_CONT ||
2040 m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC))
2042 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2043 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
2044 m_VideoPlayerVideo->SendMessage(
2045 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, m_clock.GetClock()), 1);
2047 else if (video && audio)
2049 double clock = 0;
2050 if (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC)
2051 CLog::Log(LOGDEBUG, "VideoPlayer::Sync - Audio - pts: {:f}, cache: {:f}, totalcache: {:f}",
2052 m_CurrentAudio.starttime, m_CurrentAudio.cachetime, m_CurrentAudio.cachetotal);
2053 if (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC)
2054 CLog::Log(LOGDEBUG, "VideoPlayer::Sync - Video - pts: {:f}, cache: {:f}, totalcache: {:f}",
2055 m_CurrentVideo.starttime, m_CurrentVideo.cachetime, m_CurrentVideo.cachetotal);
2057 if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && m_CurrentVideo.packets > 0 &&
2058 m_playSpeed == DVD_PLAYSPEED_PAUSE)
2060 clock = m_CurrentVideo.starttime;
2062 else if (m_CurrentAudio.starttime != DVD_NOPTS_VALUE && m_CurrentAudio.packets > 0)
2064 if (m_pInputStream->IsRealtime())
2065 clock = m_CurrentAudio.starttime - m_CurrentAudio.cachetotal - DVD_MSEC_TO_TIME(400);
2066 else
2067 clock = m_CurrentAudio.starttime - m_CurrentAudio.cachetime;
2069 if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && (m_CurrentVideo.packets > 0))
2071 if (m_CurrentVideo.starttime - m_CurrentVideo.cachetotal < clock)
2073 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2075 else if (m_CurrentVideo.starttime > m_CurrentAudio.starttime &&
2076 !m_pInputStream->IsRealtime())
2078 int audioLevel = m_VideoPlayerAudio->GetLevel();
2079 //@todo hardcoded 8 seconds in message queue
2080 double maxAudioTime = clock + DVD_MSEC_TO_TIME(80 * audioLevel);
2081 if ((m_CurrentVideo.starttime - m_CurrentVideo.cachetotal) > maxAudioTime)
2082 clock = maxAudioTime;
2083 else
2084 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2088 else if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && m_CurrentVideo.packets > 0)
2090 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2093 m_clock.Discontinuity(clock);
2094 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2095 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
2096 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2097 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
2098 m_VideoPlayerAudio->SendMessage(
2099 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, clock), 1);
2100 m_VideoPlayerVideo->SendMessage(
2101 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, clock), 1);
2102 SetCaching(CACHESTATE_DONE);
2103 UpdatePlayState(0);
2105 m_syncTimer.Set(3000ms);
2107 if (!m_State.streamsReady)
2109 if (m_playerOptions.fullscreen)
2111 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SWITCHTOFULLSCREEN);
2114 IPlayerCallback *cb = &m_callback;
2115 CFileItem fileItem = m_item;
2116 m_outboundEvents->Submit([=]() {
2117 cb->OnAVStarted(fileItem);
2119 m_State.streamsReady = true;
2122 else
2124 // exceptions for which stream players won't start properly
2125 // 1. videoplayer has not detected a keyframe within length of demux buffers
2126 if (m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0 &&
2127 !m_VideoPlayerAudio->AcceptsData() &&
2128 m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_STARTING &&
2129 m_VideoPlayerVideo->IsStalled() &&
2130 m_CurrentVideo.packets > 10)
2132 m_VideoPlayerAudio->AcceptsData();
2133 CLog::Log(LOGWARNING, "VideoPlayer::Sync - stream player video does not start, flushing buffers");
2134 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2139 // handle ff/rw
2140 if (m_playSpeed != DVD_PLAYSPEED_NORMAL && m_playSpeed != DVD_PLAYSPEED_PAUSE)
2142 if (isInMenu)
2144 // this can't be done in menu
2145 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2148 else
2150 bool check = true;
2152 // only check if we have video
2153 if (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC)
2154 check = false;
2155 // video message queue either initiated or already seen eof
2156 else if (m_CurrentVideo.inited == false && m_playSpeed >= 0)
2157 check = false;
2158 // don't check if time has not advanced since last check
2159 else if (m_SpeedState.lasttime == GetTime())
2160 check = false;
2161 // skip if frame at screen has no valid timestamp
2162 else if (m_VideoPlayerVideo->GetCurrentPts() == DVD_NOPTS_VALUE)
2163 check = false;
2164 // skip if frame on screen has not changed
2165 else if (m_SpeedState.lastpts == m_VideoPlayerVideo->GetCurrentPts() &&
2166 (m_SpeedState.lastpts > m_State.dts || m_playSpeed > 0))
2167 check = false;
2169 if (check)
2171 m_SpeedState.lastpts = m_VideoPlayerVideo->GetCurrentPts();
2172 m_SpeedState.lasttime = GetTime();
2173 m_SpeedState.lastabstime = m_clock.GetAbsoluteClock();
2175 double error;
2176 error = m_clock.GetClock() - m_SpeedState.lastpts;
2177 error *= m_playSpeed / abs(m_playSpeed);
2179 // allow a bigger error when going ff, the faster we go
2180 // the the bigger is the error we allow
2181 if (m_playSpeed > DVD_PLAYSPEED_NORMAL)
2183 double errorwin = static_cast<double>(m_playSpeed) / DVD_PLAYSPEED_NORMAL;
2184 if (errorwin > 8.0)
2185 errorwin = 8.0;
2186 error /= errorwin;
2189 if (error > DVD_MSEC_TO_TIME(1000))
2191 error = (m_clock.GetClock() - m_SpeedState.lastseekpts) / 1000;
2193 if (std::abs(error) > 1000 || (m_VideoPlayerVideo->IsRewindStalled() && std::abs(error) > 100))
2195 CLog::Log(LOGDEBUG, "CVideoPlayer::Process - Seeking to catch up, error was: {:f}",
2196 error);
2197 m_SpeedState.lastseekpts = m_clock.GetClock();
2198 int direction = (m_playSpeed > 0) ? 1 : -1;
2199 double iTime = (m_clock.GetClock() + m_State.time_offset + 1000000.0 * direction) / 1000;
2200 CDVDMsgPlayerSeek::CMode mode;
2201 mode.time = iTime;
2202 mode.backward = (m_playSpeed < 0);
2203 mode.accurate = false;
2204 mode.restore = false;
2205 mode.trickplay = true;
2206 mode.sync = false;
2207 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2214 // reset tempo
2215 if (!m_State.cantempo)
2217 float currentTempo = m_processInfo->GetNewTempo();
2218 if (currentTempo != 1.0f)
2220 SetTempo(1.0f);
2225 bool CVideoPlayer::CheckPlayerInit(CCurrentStream& current)
2227 if (current.inited)
2228 return false;
2230 if (current.startpts != DVD_NOPTS_VALUE)
2232 if(current.dts == DVD_NOPTS_VALUE)
2234 CLog::Log(LOGDEBUG, "{} - dropping packet type:{} dts:{:f} to get to start point at {:f}",
2235 __FUNCTION__, current.player, current.dts, current.startpts);
2236 return true;
2239 if ((current.startpts - current.dts) > DVD_SEC_TO_TIME(20))
2241 CLog::Log(LOGDEBUG, "{} - too far to decode before finishing seek", __FUNCTION__);
2242 if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
2243 m_CurrentAudio.startpts = current.dts;
2244 if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
2245 m_CurrentVideo.startpts = current.dts;
2246 if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
2247 m_CurrentSubtitle.startpts = current.dts;
2248 if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE)
2249 m_CurrentTeletext.startpts = current.dts;
2250 if(m_CurrentRadioRDS.startpts != DVD_NOPTS_VALUE)
2251 m_CurrentRadioRDS.startpts = current.dts;
2252 if (m_CurrentAudioID3.startpts != DVD_NOPTS_VALUE)
2253 m_CurrentAudioID3.startpts = current.dts;
2256 if(current.dts < current.startpts)
2258 CLog::Log(LOGDEBUG, "{} - dropping packet type:{} dts:{:f} to get to start point at {:f}",
2259 __FUNCTION__, current.player, current.dts, current.startpts);
2260 return true;
2264 if (current.dts != DVD_NOPTS_VALUE)
2266 current.inited = true;
2267 current.startpts = current.dts;
2269 return false;
2272 void CVideoPlayer::UpdateCorrection(DemuxPacket* pkt, double correction)
2274 pkt->m_ptsOffsetCorrection = correction;
2276 if(pkt->dts != DVD_NOPTS_VALUE)
2277 pkt->dts -= correction;
2278 if(pkt->pts != DVD_NOPTS_VALUE)
2279 pkt->pts -= correction;
2282 void CVideoPlayer::UpdateTimestamps(CCurrentStream& current, DemuxPacket* pPacket)
2284 double dts = current.dts;
2285 /* update stored values */
2286 if(pPacket->dts != DVD_NOPTS_VALUE)
2287 dts = pPacket->dts;
2288 else if(pPacket->pts != DVD_NOPTS_VALUE)
2289 dts = pPacket->pts;
2291 /* calculate some average duration */
2292 if(pPacket->duration != DVD_NOPTS_VALUE)
2293 current.dur = pPacket->duration;
2294 else if(dts != DVD_NOPTS_VALUE && current.dts != DVD_NOPTS_VALUE)
2295 current.dur = 0.1 * (current.dur * 9 + (dts - current.dts));
2297 current.dts = dts;
2299 current.dispTime = pPacket->dispTime;
2302 static void UpdateLimits(double& minimum, double& maximum, double dts)
2304 if(dts == DVD_NOPTS_VALUE)
2305 return;
2306 if(minimum == DVD_NOPTS_VALUE || minimum > dts) minimum = dts;
2307 if(maximum == DVD_NOPTS_VALUE || maximum < dts) maximum = dts;
2310 bool CVideoPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
2312 if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
2313 return false;
2315 if( pPacket->dts == DVD_NOPTS_VALUE || current.dts == DVD_NOPTS_VALUE)
2316 return false;
2318 double mindts = DVD_NOPTS_VALUE, maxdts = DVD_NOPTS_VALUE;
2319 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts);
2320 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts);
2321 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts_end());
2322 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts_end());
2324 /* if we don't have max and min, we can't do anything more */
2325 if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
2326 return false;
2328 double correction = 0.0;
2329 if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000))
2331 CLog::Log(LOGDEBUG,
2332 "CVideoPlayer::CheckContinuity - resync forward :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2333 current.type, current.dts, pPacket->dts, pPacket->dts - maxdts);
2334 correction = pPacket->dts - maxdts;
2337 /* if it's large scale jump, correct for it after having confirmed the jump */
2338 if(pPacket->dts + DVD_MSEC_TO_TIME(500) < current.dts_end())
2340 CLog::Log(
2341 LOGDEBUG,
2342 "CVideoPlayer::CheckContinuity - resync backward :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2343 current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
2344 correction = pPacket->dts - current.dts_end();
2346 else if(pPacket->dts < current.dts)
2348 CLog::Log(LOGDEBUG,
2349 "CVideoPlayer::CheckContinuity - wrapback :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2350 current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
2353 double lastdts = pPacket->dts;
2354 if(correction != 0.0)
2356 // we want the dts values of two streams to close, or for one to be invalid (e.g. from a missing audio stream)
2357 double this_dts = pPacket->dts;
2358 double that_dts = current.type == STREAM_AUDIO ? m_CurrentVideo.lastdts : m_CurrentAudio.lastdts;
2360 if (m_CurrentAudio.id == -1 || m_CurrentVideo.id == -1 ||
2361 current.lastdts == DVD_NOPTS_VALUE ||
2362 fabs(this_dts - that_dts) < DVD_MSEC_TO_TIME(1000))
2364 m_offset_pts += correction;
2365 UpdateCorrection(pPacket, correction);
2366 lastdts = pPacket->dts;
2367 CLog::Log(LOGDEBUG, "CVideoPlayer::CheckContinuity - update correction: {:f}", correction);
2368 if (current.avsync == CCurrentStream::AV_SYNC_CHECK)
2369 current.avsync = CCurrentStream::AV_SYNC_CONT;
2371 else
2373 // not sure yet - flags the packets as unknown until we get confirmation on another audio/video packet
2374 pPacket->dts = DVD_NOPTS_VALUE;
2375 pPacket->pts = DVD_NOPTS_VALUE;
2378 else
2380 if (current.avsync == CCurrentStream::AV_SYNC_CHECK)
2381 current.avsync = CCurrentStream::AV_SYNC_CONT;
2383 current.lastdts = lastdts;
2384 return true;
2387 bool CVideoPlayer::CheckSceneSkip(const CCurrentStream& current)
2389 if (!m_Edl.HasEdits())
2390 return false;
2392 if(current.dts == DVD_NOPTS_VALUE)
2393 return false;
2395 if(current.inited == false)
2396 return false;
2398 const auto hasEdit =
2399 m_Edl.InEdit(std::chrono::milliseconds(std::lround(current.dts + m_offset_pts)));
2400 return hasEdit && hasEdit.value()->action == EDL::Action::CUT;
2403 void CVideoPlayer::CheckAutoSceneSkip()
2405 if (!m_Edl.HasEdits())
2406 return;
2408 // Check that there is an audio and video stream.
2409 if((m_CurrentAudio.id < 0 || m_CurrentAudio.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
2410 (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC))
2411 return;
2413 // If there is a startpts defined for either the audio or video stream then VideoPlayer is still
2414 // still decoding frames to get to the previously requested seek point.
2415 if (m_CurrentAudio.inited == false ||
2416 m_CurrentVideo.inited == false)
2417 return;
2419 const std::chrono::milliseconds clock{GetTime()};
2421 const std::chrono::milliseconds correctClock = m_Edl.GetTimeAfterRestoringCuts(clock);
2422 const auto hasEdit = m_Edl.InEdit(correctClock);
2423 if (!hasEdit)
2425 // @note: Users are allowed to jump back into EDL commercial breaks
2426 // do not reset the last edit time if the last surpassed edit is a commercial break
2427 if (m_Edl.GetLastEditActionType() != EDL::Action::COMM_BREAK)
2429 m_Edl.ResetLastEditTime();
2431 return;
2434 const auto& edit = hasEdit.value();
2435 if (edit->action == EDL::Action::CUT)
2437 if ((m_playSpeed > 0 && correctClock < (edit->start + 1s)) ||
2438 (m_playSpeed < 0 && correctClock < (edit->end - 1s)))
2440 CLog::Log(LOGDEBUG, "{} - Clock in EDL cut [{} - {}]: {}. Automatically skipping over.",
2441 __FUNCTION__, CEdl::MillisecondsToTimeString(edit->start),
2442 CEdl::MillisecondsToTimeString(edit->end), CEdl::MillisecondsToTimeString(clock));
2444 // Seeking either goes to the start or the end of the cut depending on the play direction.
2445 std::chrono::milliseconds seek = m_playSpeed >= 0 ? edit->end : edit->start;
2446 if (m_Edl.GetLastEditTime() != seek)
2448 CDVDMsgPlayerSeek::CMode mode;
2449 mode.time = seek.count();
2450 mode.backward = true;
2451 mode.accurate = true;
2452 mode.restore = false;
2453 mode.trickplay = false;
2454 mode.sync = true;
2455 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2457 m_Edl.SetLastEditTime(seek);
2458 m_Edl.SetLastEditActionType(edit->action);
2462 else if (edit->action == EDL::Action::COMM_BREAK)
2464 // marker for commbreak may be inaccurate. allow user to skip into break from the back
2465 if (m_playSpeed >= 0 && m_Edl.GetLastEditTime() != edit->start && clock < edit->end - 1s)
2467 const std::shared_ptr<CAdvancedSettings> advancedSettings =
2468 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
2469 if (advancedSettings && advancedSettings->m_EdlDisplayCommbreakNotifications)
2471 const std::string timeString = StringUtils::SecondsToTimeString(
2472 std::chrono::duration_cast<std::chrono::seconds>(edit->end - edit->start).count(),
2473 TIME_FORMAT_MM_SS);
2474 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25011), timeString);
2477 m_Edl.SetLastEditTime(edit->start);
2478 m_Edl.SetLastEditActionType(edit->action);
2480 if (m_SkipCommercials)
2482 CLog::Log(LOGDEBUG,
2483 "{} - Clock in commercial break [{} - {}]: {}. Automatically skipping to end of "
2484 "commercial break",
2485 __FUNCTION__, CEdl::MillisecondsToTimeString(edit->start),
2486 CEdl::MillisecondsToTimeString(edit->end), CEdl::MillisecondsToTimeString(clock));
2488 CDVDMsgPlayerSeek::CMode mode;
2489 mode.time = edit->end.count();
2490 mode.backward = true;
2491 mode.accurate = true;
2492 mode.restore = false;
2493 mode.trickplay = false;
2494 mode.sync = true;
2495 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2502 void CVideoPlayer::SynchronizeDemuxer()
2504 if(IsCurrentThread())
2505 return;
2506 if(!m_messenger.IsInited())
2507 return;
2509 auto message = std::make_shared<CDVDMsgGeneralSynchronize>(500ms, SYNCSOURCE_PLAYER);
2510 m_messenger.Put(message);
2511 message->Wait(m_bStop, 0);
2514 IDVDStreamPlayer* CVideoPlayer::GetStreamPlayer(unsigned int target)
2516 if(target == VideoPlayer_AUDIO)
2517 return m_VideoPlayerAudio;
2518 if(target == VideoPlayer_VIDEO)
2519 return m_VideoPlayerVideo;
2520 if(target == VideoPlayer_SUBTITLE)
2521 return m_VideoPlayerSubtitle;
2522 if(target == VideoPlayer_TELETEXT)
2523 return m_VideoPlayerTeletext;
2524 if(target == VideoPlayer_RDS)
2525 return m_VideoPlayerRadioRDS;
2526 if (target == VideoPlayer_ID3)
2527 return m_VideoPlayerAudioID3.get();
2528 return NULL;
2531 void CVideoPlayer::SendPlayerMessage(std::shared_ptr<CDVDMsg> pMsg, unsigned int target)
2533 IDVDStreamPlayer* player = GetStreamPlayer(target);
2534 if(player)
2535 player->SendMessage(std::move(pMsg), 0);
2538 void CVideoPlayer::OnExit()
2540 CLog::Log(LOGINFO, "CVideoPlayer::OnExit()");
2542 // set event to inform openfile something went wrong in case openfile is still waiting for this event
2543 SetCaching(CACHESTATE_DONE);
2545 // close each stream
2546 if (!m_bAbortRequest)
2547 CLog::Log(LOGINFO, "VideoPlayer: eof, waiting for queues to empty");
2549 CFileItem fileItem(m_item);
2550 UpdateFileItemStreamDetails(fileItem);
2552 CloseStream(m_CurrentAudio, !m_bAbortRequest);
2553 CloseStream(m_CurrentVideo, !m_bAbortRequest);
2554 CloseStream(m_CurrentTeletext,!m_bAbortRequest);
2555 CloseStream(m_CurrentRadioRDS, !m_bAbortRequest);
2556 CloseStream(m_CurrentAudioID3, !m_bAbortRequest);
2557 // the generalization principle was abused for subtitle player. actually it is not a stream player like
2558 // video and audio. subtitle player does not run on its own thread, hence waitForBuffers makes
2559 // no sense here. waitForBuffers is abused to clear overlay container (false clears container)
2560 // subtitles are added from video player. after video player has finished, overlays have to be cleared.
2561 CloseStream(m_CurrentSubtitle, false); // clear overlay container
2563 CServiceBroker::GetWinSystem()->UnregisterRenderLoop(this);
2565 IPlayerCallback *cb = &m_callback;
2566 CVideoSettings vs = m_processInfo->GetVideoSettings();
2567 m_outboundEvents->Submit([=]() {
2568 cb->StoreVideoSettings(fileItem, vs);
2571 CBookmark bookmark;
2572 bookmark.totalTimeInSeconds = 0;
2573 bookmark.timeInSeconds = 0;
2574 if (m_State.startTime == 0)
2576 bookmark.totalTimeInSeconds = m_State.timeMax / 1000;
2577 bookmark.timeInSeconds = m_State.time / 1000;
2579 bookmark.player = m_name;
2580 bookmark.playerState = GetPlayerState();
2581 m_outboundEvents->Submit([=]() {
2582 cb->OnPlayerCloseFile(fileItem, bookmark);
2585 // destroy objects
2586 m_renderManager.Flush(false, false);
2587 m_pDemuxer.reset();
2588 m_pSubtitleDemuxer.reset();
2589 m_subtitleDemuxerMap.clear();
2590 m_pCCDemuxer.reset();
2591 if (m_pInputStream.use_count() > 1)
2592 throw std::runtime_error("m_pInputStream reference count is greater than 1");
2593 m_pInputStream.reset();
2595 // clean up all selection streams
2596 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
2598 m_messenger.End();
2600 CFFmpegLog::ClearLogLevel();
2601 m_bStop = true;
2603 bool error = m_error;
2604 bool close = m_bCloseRequest;
2605 m_outboundEvents->Submit([=]() {
2606 if (close)
2607 cb->OnPlayBackStopped();
2608 else if (error)
2609 cb->OnPlayBackError();
2610 else
2611 cb->OnPlayBackEnded();
2615 void CVideoPlayer::HandleMessages()
2617 std::shared_ptr<CDVDMsg> pMsg = nullptr;
2619 while (m_messenger.Get(pMsg, 0ms) == MSGQ_OK)
2621 if (pMsg->IsType(CDVDMsg::PLAYER_OPENFILE) &&
2622 m_messenger.GetPacketCount(CDVDMsg::PLAYER_OPENFILE) == 0)
2624 CDVDMsgOpenFile& msg(*std::static_pointer_cast<CDVDMsgOpenFile>(pMsg));
2626 IPlayerCallback *cb = &m_callback;
2627 CFileItem fileItem(m_item);
2628 UpdateFileItemStreamDetails(fileItem);
2629 CVideoSettings vs = m_processInfo->GetVideoSettings();
2630 m_outboundEvents->Submit([=]() {
2631 cb->StoreVideoSettings(fileItem, vs);
2634 CBookmark bookmark;
2635 bookmark.totalTimeInSeconds = 0;
2636 bookmark.timeInSeconds = 0;
2637 if (m_State.startTime == 0)
2639 bookmark.totalTimeInSeconds = m_State.timeMax / 1000;
2640 bookmark.timeInSeconds = m_State.time / 1000;
2642 bookmark.player = m_name;
2643 bookmark.playerState = GetPlayerState();
2644 m_outboundEvents->Submit([=]() {
2645 cb->OnPlayerCloseFile(fileItem, bookmark);
2648 m_item = msg.GetItem();
2649 m_playerOptions = msg.GetOptions();
2651 m_processInfo->SetPlayTimes(0,0,0,0);
2653 m_outboundEvents->Submit([this]() {
2654 m_callback.OnPlayBackStarted(m_item);
2657 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2658 m_renderManager.Flush(false, false);
2659 m_pDemuxer.reset();
2660 m_pSubtitleDemuxer.reset();
2661 m_subtitleDemuxerMap.clear();
2662 m_pCCDemuxer.reset();
2663 if (m_pInputStream.use_count() > 1)
2664 throw std::runtime_error("m_pInputStream reference count is greater than 1");
2665 m_pInputStream.reset();
2667 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
2669 Prepare();
2671 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) &&
2672 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0 &&
2673 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2675 CDVDMsgPlayerSeek& msg(*std::static_pointer_cast<CDVDMsgPlayerSeek>(pMsg));
2677 if (!m_State.canseek)
2679 m_processInfo->SetStateSeeking(false);
2680 continue;
2683 // skip seeks if player has not finished the last seek
2684 if (m_CurrentVideo.id >= 0 &&
2685 m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC)
2687 double now = m_clock.GetAbsoluteClock();
2688 if (m_playSpeed == DVD_PLAYSPEED_NORMAL &&
2689 (now - m_State.lastSeek)/1000 < 2000 &&
2690 !msg.GetAccurate())
2692 m_processInfo->SetStateSeeking(false);
2693 continue;
2697 if (!msg.GetTrickPlay())
2699 m_processInfo->SeekFinished(0);
2700 SetCaching(CACHESTATE_FLUSH);
2703 double start = DVD_NOPTS_VALUE;
2705 double time = msg.GetTime();
2706 if (msg.GetRelative())
2707 time = (m_clock.GetClock() + m_State.time_offset) / 1000l + time;
2709 time = msg.GetRestore()
2710 ? m_Edl.GetTimeAfterRestoringCuts(std::chrono::milliseconds(std::lround(time)))
2711 .count()
2712 : time;
2714 // if input stream doesn't support ISeekTime, convert back to pts
2715 //! @todo
2716 //! After demuxer we add an offset to input pts so that displayed time and clock are
2717 //! increasing steadily. For seeking we need to determine the boundaries and offset
2718 //! of the desired segment. With the current approach calculated time may point
2719 //! to nirvana
2720 if (m_pInputStream->GetIPosTime() == nullptr)
2721 time -= m_State.time_offset/1000l;
2723 CLog::Log(LOGDEBUG, "demuxer seek to: {:f}", time);
2724 if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start))
2726 CLog::Log(LOGDEBUG, "demuxer seek to: {:f}, success", time);
2727 if(m_pSubtitleDemuxer)
2729 if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward()))
2730 CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: {:f}, success", time);
2732 // dts after successful seek
2733 if (start == DVD_NOPTS_VALUE)
2734 start = DVD_MSEC_TO_TIME(time) - m_State.time_offset;
2736 m_State.dts = start;
2737 m_State.lastSeek = m_clock.GetAbsoluteClock();
2739 FlushBuffers(start, msg.GetAccurate(), msg.GetSync());
2741 else if (m_pDemuxer)
2743 CLog::Log(LOGDEBUG, "VideoPlayer: seek failed or hit end of stream");
2744 // dts after successful seek
2745 if (start == DVD_NOPTS_VALUE)
2746 start = DVD_MSEC_TO_TIME(time) - m_State.time_offset;
2748 m_State.dts = start;
2750 FlushBuffers(start, false, true);
2751 if (m_playSpeed != DVD_PLAYSPEED_PAUSE)
2753 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2757 // set flag to indicate we have finished a seeking request
2758 if(!msg.GetTrickPlay())
2760 m_processInfo->SeekFinished(0);
2763 // dvd's will issue a HOP_CHANNEL that we need to skip
2764 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2765 m_dvd.state = DVDSTATE_SEEK;
2767 m_processInfo->SetStateSeeking(false);
2769 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) &&
2770 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0 &&
2771 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2773 m_processInfo->SeekFinished(0);
2774 SetCaching(CACHESTATE_FLUSH);
2776 CDVDMsgPlayerSeekChapter& msg(*std::static_pointer_cast<CDVDMsgPlayerSeekChapter>(pMsg));
2777 double start = DVD_NOPTS_VALUE;
2778 int offset = 0;
2780 // This should always be the case.
2781 if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
2783 FlushBuffers(start, true, true);
2784 int64_t beforeSeek = GetTime();
2785 offset = DVD_TIME_TO_MSEC(start) - static_cast<int>(beforeSeek);
2786 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2788 else if (m_pInputStream)
2790 CDVDInputStream::IChapter* pChapter = m_pInputStream->GetIChapter();
2791 if (pChapter && pChapter->SeekChapter(msg.GetChapter()))
2793 FlushBuffers(start, true, true);
2794 int64_t beforeSeek = GetTime();
2795 offset = DVD_TIME_TO_MSEC(start) - static_cast<int>(beforeSeek);
2796 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2799 m_processInfo->SeekFinished(offset);
2801 else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
2803 m_CurrentAudio.stream = NULL;
2804 m_CurrentVideo.stream = NULL;
2805 m_CurrentSubtitle.stream = NULL;
2807 // we need to reset the demuxer, probably because the streams have changed
2808 if(m_pDemuxer)
2809 m_pDemuxer->Reset();
2810 if(m_pSubtitleDemuxer)
2811 m_pSubtitleDemuxer->Reset();
2813 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
2815 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetAudioStream>(pMsg);
2817 SelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
2818 if(st.source != STREAM_SOURCE_NONE)
2820 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2822 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2823 if(pStream->SetActiveAudioStream(st.id))
2825 m_dvd.iSelectedAudioStream = -1;
2826 CloseStream(m_CurrentAudio, false);
2827 CDVDMsgPlayerSeek::CMode mode;
2828 mode.time = (int)GetUpdatedTime();
2829 mode.backward = true;
2830 mode.accurate = true;
2831 mode.trickplay = true;
2832 mode.sync = true;
2833 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2836 else
2838 CloseStream(m_CurrentAudio, false);
2839 OpenStream(m_CurrentAudio, st.demuxerId, st.id, st.source);
2840 AdaptForcedSubtitles();
2842 CDVDMsgPlayerSeek::CMode mode;
2843 mode.time = (int)GetUpdatedTime();
2844 mode.backward = true;
2845 mode.accurate = true;
2846 mode.trickplay = true;
2847 mode.sync = true;
2848 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2852 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_VIDEOSTREAM))
2854 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetVideoStream>(pMsg);
2856 SelectionStream& st = m_SelectionStreams.Get(STREAM_VIDEO, pMsg2->GetStreamId());
2857 if (st.source != STREAM_SOURCE_NONE)
2859 if (st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2861 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2862 if (pStream->SetAngle(st.id))
2864 m_dvd.iSelectedVideoStream = st.id;
2866 CDVDMsgPlayerSeek::CMode mode;
2867 mode.time = (int)GetUpdatedTime();
2868 mode.backward = true;
2869 mode.accurate = true;
2870 mode.trickplay = true;
2871 mode.sync = true;
2872 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2875 else
2877 CloseStream(m_CurrentVideo, false);
2878 OpenStream(m_CurrentVideo, st.demuxerId, st.id, st.source);
2879 CDVDMsgPlayerSeek::CMode mode;
2880 mode.time = (int)GetUpdatedTime();
2881 mode.backward = true;
2882 mode.accurate = true;
2883 mode.trickplay = true;
2884 mode.sync = true;
2885 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2889 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
2891 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetSubtitleStream>(pMsg);
2893 SelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
2894 if(st.source != STREAM_SOURCE_NONE)
2896 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2898 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2899 if(pStream->SetActiveSubtitleStream(st.id))
2901 m_dvd.iSelectedSPUStream = -1;
2902 CloseStream(m_CurrentSubtitle, false);
2905 else
2907 CloseStream(m_CurrentSubtitle, false);
2908 OpenStream(m_CurrentSubtitle, st.demuxerId, st.id, st.source);
2912 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
2914 bool isVisible = std::static_pointer_cast<CDVDMsgBool>(pMsg)->m_value;
2916 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
2917 if (!isVisible)
2918 SetEnableStream(m_CurrentSubtitle, false);
2920 SetSubtitleVisibleInternal(isVisible);
2922 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_PROGRAM))
2924 auto msg = std::static_pointer_cast<CDVDMsgInt>(pMsg);
2925 if (m_pDemuxer)
2927 m_pDemuxer->SetProgram(msg->m_value);
2928 FlushBuffers(DVD_NOPTS_VALUE, false, true);
2931 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
2933 SetCaching(CACHESTATE_FLUSH);
2935 auto pMsgPlayerSetState = std::static_pointer_cast<CDVDMsgPlayerSetState>(pMsg);
2937 if (std::shared_ptr<CDVDInputStream::IMenus> ptr = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
2939 if(ptr->SetState(pMsgPlayerSetState->GetState()))
2941 m_dvd.state = DVDSTATE_NORMAL;
2942 m_dvd.iDVDStillStartTime = {};
2943 m_dvd.iDVDStillTime = 0ms;
2947 m_processInfo->SeekFinished(0);
2949 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
2951 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2953 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
2955 int speed = std::static_pointer_cast<CDVDMsgPlayerSetSpeed>(pMsg)->GetSpeed();
2957 // correct our current clock, as it would start going wrong otherwise
2958 if (m_State.timestamp > 0)
2960 double offset;
2961 offset = m_clock.GetAbsoluteClock() - m_State.timestamp;
2962 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2963 offset = DVD_TIME_TO_MSEC(offset);
2964 if (offset > 1000)
2965 offset = 1000;
2966 if (offset < -1000)
2967 offset = -1000;
2968 m_State.time += offset;
2969 m_State.timestamp = m_clock.GetAbsoluteClock();
2972 if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed)
2974 m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL);
2975 m_processInfo->SeekFinished(0);
2978 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && speed != m_playSpeed)
2980 std::shared_ptr<CInputStreamPVRBase> pvrinputstream = std::static_pointer_cast<CInputStreamPVRBase>(m_pInputStream);
2981 pvrinputstream->Pause(speed == 0);
2984 // do a seek after rewind, clock is not in sync with current pts
2985 if ((speed == DVD_PLAYSPEED_NORMAL) &&
2986 (m_playSpeed != DVD_PLAYSPEED_NORMAL) &&
2987 (m_playSpeed != DVD_PLAYSPEED_PAUSE))
2989 double iTime = m_VideoPlayerVideo->GetCurrentPts();
2990 if (iTime == DVD_NOPTS_VALUE)
2991 iTime = m_clock.GetClock();
2992 iTime = (iTime + m_State.time_offset) / 1000;
2994 CDVDMsgPlayerSeek::CMode mode;
2995 mode.time = iTime;
2996 mode.backward = m_playSpeed < 0;
2997 mode.accurate = true;
2998 mode.trickplay = true;
2999 mode.sync = true;
3000 mode.restore = false;
3001 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3004 if (std::static_pointer_cast<CDVDMsgPlayerSetSpeed>(pMsg)->IsTempo())
3005 m_processInfo->SetTempo(static_cast<float>(speed) / DVD_PLAYSPEED_NORMAL);
3006 else
3007 m_processInfo->SetSpeed(static_cast<float>(speed) / DVD_PLAYSPEED_NORMAL);
3009 m_processInfo->SetFrameAdvance(false);
3011 m_playSpeed = speed;
3013 m_caching = CACHESTATE_DONE;
3014 m_clock.SetSpeed(speed);
3015 m_VideoPlayerAudio->SetSpeed(speed);
3016 m_VideoPlayerVideo->SetSpeed(speed);
3017 m_streamPlayerSpeed = speed;
3019 else if (pMsg->IsType(CDVDMsg::PLAYER_FRAME_ADVANCE))
3021 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
3023 int frames = std::static_pointer_cast<CDVDMsgInt>(pMsg)->m_value;
3024 double time = DVD_TIME_BASE / static_cast<double>(m_processInfo->GetVideoFps()) * frames;
3025 m_processInfo->SetFrameAdvance(true);
3026 m_clock.Advance(time);
3029 else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
3030 OnAction(std::static_pointer_cast<CDVDMsgType<CAction>>(pMsg)->m_value);
3031 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
3033 SStartMsg& msg = std::static_pointer_cast<CDVDMsgType<SStartMsg>>(pMsg)->m_value;
3034 if (msg.player == VideoPlayer_AUDIO)
3036 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_WAITSYNC;
3037 m_CurrentAudio.cachetime = msg.cachetime;
3038 m_CurrentAudio.cachetotal = msg.cachetotal;
3039 m_CurrentAudio.starttime = msg.timestamp;
3041 if (msg.player == VideoPlayer_VIDEO)
3043 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_WAITSYNC;
3044 m_CurrentVideo.cachetime = msg.cachetime;
3045 m_CurrentVideo.cachetotal = msg.cachetotal;
3046 m_CurrentVideo.starttime = msg.timestamp;
3048 CLog::Log(LOGDEBUG, "CVideoPlayer::HandleMessages - player started {}", msg.player);
3050 else if (pMsg->IsType(CDVDMsg::PLAYER_REPORT_STATE))
3052 SStateMsg& msg = std::static_pointer_cast<CDVDMsgType<SStateMsg>>(pMsg)->m_value;
3053 if (msg.player == VideoPlayer_AUDIO)
3055 m_CurrentAudio.syncState = msg.syncState;
3057 if (msg.player == VideoPlayer_VIDEO)
3059 m_CurrentVideo.syncState = msg.syncState;
3061 CLog::Log(LOGDEBUG, "CVideoPlayer::HandleMessages - player {} reported state: {}", msg.player,
3062 msg.syncState);
3064 else if (pMsg->IsType(CDVDMsg::SUBTITLE_ADDFILE))
3066 int id = AddSubtitleFile(std::static_pointer_cast<CDVDMsgType<std::string>>(pMsg)->m_value);
3067 if (id >= 0)
3069 SetSubtitle(id);
3070 SetSubtitleVisibleInternal(true);
3073 else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
3075 if (std::static_pointer_cast<CDVDMsgGeneralSynchronize>(pMsg)->Wait(100ms, SYNCSOURCE_PLAYER))
3076 CLog::Log(LOGDEBUG, "CVideoPlayer - CDVDMsg::GENERAL_SYNCHRONIZE");
3078 else if (pMsg->IsType(CDVDMsg::PLAYER_AVCHANGE))
3080 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
3081 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
3082 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3083 IPlayerCallback *cb = &m_callback;
3084 m_outboundEvents->Submit([=]() {
3085 cb->OnAVChange();
3088 else if (pMsg->IsType(CDVDMsg::PLAYER_ABORT))
3090 CLog::Log(LOGDEBUG, "CVideoPlayer - CDVDMsg::PLAYER_ABORT");
3091 m_bAbortRequest = true;
3093 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_UPDATE_STREAM_DETAILS))
3094 m_UpdateStreamDetails = true;
3098 void CVideoPlayer::SetCaching(ECacheState state)
3100 if(state == CACHESTATE_FLUSH)
3102 CacheInfo cache = GetCachingTimes();
3103 if (cache.valid)
3104 state = CACHESTATE_FULL;
3105 else
3106 state = CACHESTATE_INIT;
3109 if(m_caching == state)
3110 return;
3112 CLog::Log(LOGDEBUG, "CVideoPlayer::SetCaching - caching state {}", state);
3113 if (state == CACHESTATE_FULL ||
3114 state == CACHESTATE_INIT)
3116 m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
3118 m_VideoPlayerAudio->SetSpeed(DVD_PLAYSPEED_PAUSE);
3119 m_VideoPlayerVideo->SetSpeed(DVD_PLAYSPEED_PAUSE);
3120 m_streamPlayerSpeed = DVD_PLAYSPEED_PAUSE;
3122 m_cachingTimer.Set(5000ms);
3125 if (state == CACHESTATE_PLAY ||
3126 (state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY))
3128 m_clock.SetSpeed(m_playSpeed);
3129 m_VideoPlayerAudio->SetSpeed(m_playSpeed);
3130 m_VideoPlayerVideo->SetSpeed(m_playSpeed);
3131 m_streamPlayerSpeed = m_playSpeed;
3133 m_caching = state;
3135 m_clock.SetSpeedAdjust(0);
3138 void CVideoPlayer::SetPlaySpeed(int speed)
3140 if (IsPlaying())
3142 CDVDMsgPlayerSetSpeed::SpeedParams params = { speed, false };
3143 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSpeed>(params));
3145 else
3147 m_playSpeed = speed;
3148 m_streamPlayerSpeed = speed;
3152 bool CVideoPlayer::CanPause() const
3154 std::unique_lock<CCriticalSection> lock(m_StateSection);
3155 return m_State.canpause;
3158 void CVideoPlayer::Pause()
3160 // toggle between pause and normal speed
3161 if (m_processInfo->GetNewSpeed() == 0)
3163 SetSpeed(1);
3165 else
3167 SetSpeed(0);
3171 bool CVideoPlayer::HasVideo() const
3173 return m_HasVideo;
3176 bool CVideoPlayer::HasAudio() const
3178 return m_HasAudio;
3181 bool CVideoPlayer::HasRDS() const
3183 return m_CurrentRadioRDS.id >= 0;
3186 bool CVideoPlayer::HasID3() const
3188 return m_CurrentAudioID3.id >= 0;
3191 bool CVideoPlayer::IsPassthrough() const
3193 return m_VideoPlayerAudio->IsPassthrough();
3196 bool CVideoPlayer::CanSeek() const
3198 std::unique_lock<CCriticalSection> lock(m_StateSection);
3199 return m_State.canseek;
3202 void CVideoPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
3204 if (!m_State.canseek)
3205 return;
3207 if (bLargeStep && bChapterOverride && GetChapter() > 0 && GetChapterCount() > 1)
3209 if (!bPlus)
3211 SeekChapter(GetPreviousChapter());
3212 return;
3214 else if (GetChapter() < GetChapterCount())
3216 SeekChapter(GetChapter() + 1);
3217 return;
3221 int64_t seekTarget;
3222 const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
3223 if (advancedSettings->m_videoUseTimeSeeking && m_processInfo->GetMaxTime() > 2000 * advancedSettings->m_videoTimeSeekForwardBig)
3225 if (bLargeStep)
3226 seekTarget = bPlus ? advancedSettings->m_videoTimeSeekForwardBig :
3227 advancedSettings->m_videoTimeSeekBackwardBig;
3228 else
3229 seekTarget = bPlus ? advancedSettings->m_videoTimeSeekForward :
3230 advancedSettings->m_videoTimeSeekBackward;
3231 seekTarget *= 1000;
3232 seekTarget += GetTime();
3234 else
3236 int percent;
3237 if (bLargeStep)
3238 percent = bPlus ? advancedSettings->m_videoPercentSeekForwardBig : advancedSettings->m_videoPercentSeekBackwardBig;
3239 else
3240 percent = bPlus ? advancedSettings->m_videoPercentSeekForward : advancedSettings->m_videoPercentSeekBackward;
3241 seekTarget = static_cast<int64_t>(m_processInfo->GetMaxTime() * (GetPercentage() + percent) / 100);
3244 bool restore = true;
3246 int64_t time = GetTime();
3247 if(g_application.CurrentFileItem().IsStack() &&
3248 (seekTarget > m_processInfo->GetMaxTime() || seekTarget < 0))
3250 g_application.SeekTime((seekTarget - time) * 0.001 + g_application.GetTime());
3251 // warning, don't access any VideoPlayer variables here as
3252 // the VideoPlayer object may have been destroyed
3253 return;
3256 CDVDMsgPlayerSeek::CMode mode;
3257 mode.time = (int)seekTarget;
3258 mode.backward = !bPlus;
3259 mode.accurate = false;
3260 mode.restore = restore;
3261 mode.trickplay = false;
3262 mode.sync = true;
3264 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3265 SynchronizeDemuxer();
3266 if (seekTarget < 0)
3267 seekTarget = 0;
3268 m_callback.OnPlayBackSeek(seekTarget, seekTarget - time);
3271 bool CVideoPlayer::SeekScene(Direction seekDirection)
3273 if (!m_Edl.HasSceneMarker())
3274 return false;
3277 * There is a 5 second grace period applied when seeking for scenes backwards. If there is no
3278 * grace period applied it is impossible to go backwards past a scene marker.
3280 auto clock = std::chrono::milliseconds(GetTime());
3281 if (seekDirection == Direction::BACKWARD && clock > 5s) // 5 seconds
3282 clock -= 5s;
3284 const std::optional<std::chrono::milliseconds> sceneMarker =
3285 m_Edl.GetNextSceneMarker(seekDirection, clock);
3286 if (sceneMarker)
3289 * Seeking is flushed and inaccurate, just like Seek()
3291 CDVDMsgPlayerSeek::CMode mode;
3292 mode.time = sceneMarker.value().count();
3293 mode.backward = seekDirection == Direction::BACKWARD;
3294 mode.accurate = false;
3295 mode.restore = false;
3296 mode.trickplay = false;
3297 mode.sync = true;
3299 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3300 SynchronizeDemuxer();
3301 return true;
3303 return false;
3306 void CVideoPlayer::GetGeneralInfo(std::string& strGeneralInfo)
3308 if (!m_bStop)
3310 double apts = m_VideoPlayerAudio->GetCurrentPts();
3311 double vpts = m_VideoPlayerVideo->GetCurrentPts();
3312 double dDiff = 0;
3314 if (apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE)
3315 dDiff = (apts - vpts) / DVD_TIME_BASE;
3317 std::string strBuf;
3318 std::unique_lock<CCriticalSection> lock(m_StateSection);
3319 if (m_State.cache_bytes >= 0)
3321 strBuf += StringUtils::Format("forward: {} / {:2.0f}% / {:6.3f}s / {:.3f}%",
3322 StringUtils::SizeToString(m_State.cache_bytes),
3323 m_State.cache_level * 100.0, m_State.cache_time,
3324 m_State.cache_offset * 100.0);
3327 strGeneralInfo = StringUtils::Format("Player: a/v:{: 6.3f}, {}", dDiff, strBuf);
3331 void CVideoPlayer::SeekPercentage(float iPercent)
3333 int64_t iTotalTime = m_processInfo->GetMaxTime();
3335 if (!iTotalTime)
3336 return;
3338 SeekTime((int64_t)(iTotalTime * iPercent / 100));
3341 float CVideoPlayer::GetPercentage()
3343 int64_t iTotalTime = m_processInfo->GetMaxTime();
3345 if (!iTotalTime)
3346 return 0.0f;
3348 return GetTime() * 100 / (float)iTotalTime;
3351 float CVideoPlayer::GetCachePercentage() const
3353 std::unique_lock<CCriticalSection> lock(m_StateSection);
3354 return (float) (m_State.cache_offset * 100); // NOTE: Percentage returned is relative
3357 void CVideoPlayer::SetAVDelay(float fValue)
3359 m_processInfo->GetVideoSettingsLocked().SetAudioDelay(fValue);
3360 m_renderManager.SetDelay(static_cast<int>(fValue * 1000.0f));
3363 float CVideoPlayer::GetAVDelay()
3365 return static_cast<float>(m_renderManager.GetDelay()) / 1000.0f;
3368 void CVideoPlayer::SetSubTitleDelay(float fValue)
3370 m_processInfo->GetVideoSettingsLocked().SetSubtitleDelay(fValue);
3371 m_VideoPlayerVideo->SetSubtitleDelay(static_cast<double>(-fValue) * DVD_TIME_BASE);
3374 float CVideoPlayer::GetSubTitleDelay()
3376 return (float) -m_VideoPlayerVideo->GetSubtitleDelay() / DVD_TIME_BASE;
3379 bool CVideoPlayer::GetSubtitleVisible() const
3381 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3383 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
3384 return pStream->IsSubtitleStreamEnabled();
3387 return m_VideoPlayerVideo->IsSubtitleEnabled();
3390 void CVideoPlayer::SetSubtitleVisible(bool bVisible)
3392 m_messenger.Put(
3393 std::make_shared<CDVDMsgBool>(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
3394 m_processInfo->GetVideoSettingsLocked().SetSubtitleVisible(bVisible);
3397 void CVideoPlayer::SetEnableStream(CCurrentStream& current, bool isEnabled)
3399 if (m_pDemuxer && STREAM_SOURCE_MASK(current.source) == STREAM_SOURCE_DEMUX)
3400 m_pDemuxer->EnableStream(current.demuxerId, current.id, isEnabled);
3403 void CVideoPlayer::SetSubtitleVisibleInternal(bool bVisible)
3405 m_VideoPlayerVideo->EnableSubtitle(bVisible);
3407 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3408 std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream)->EnableSubtitleStream(bVisible);
3410 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3413 void CVideoPlayer::SetSubtitleVerticalPosition(int value, bool save)
3415 m_processInfo->GetVideoSettingsLocked().SetSubtitleVerticalPosition(value, save);
3416 m_renderManager.SetSubtitleVerticalPosition(value, save);
3419 std::shared_ptr<TextCacheStruct_t> CVideoPlayer::GetTeletextCache()
3421 if (m_CurrentTeletext.id < 0)
3422 return nullptr;
3424 return m_VideoPlayerTeletext->GetTeletextCache();
3427 bool CVideoPlayer::HasTeletextCache() const
3429 return m_CurrentTeletext.id >= 0;
3432 void CVideoPlayer::LoadPage(int p, int sp, unsigned char* buffer)
3434 if (m_CurrentTeletext.id < 0)
3435 return;
3437 return m_VideoPlayerTeletext->LoadPage(p, sp, buffer);
3440 void CVideoPlayer::SeekTime(int64_t iTime)
3442 int64_t seekOffset = iTime - GetTime();
3444 CDVDMsgPlayerSeek::CMode mode;
3445 mode.time = static_cast<double>(iTime);
3446 mode.backward = true;
3447 mode.accurate = true;
3448 mode.trickplay = false;
3449 mode.sync = true;
3451 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3452 SynchronizeDemuxer();
3453 m_callback.OnPlayBackSeek(iTime, seekOffset);
3454 m_processInfo->SeekFinished(seekOffset);
3457 bool CVideoPlayer::SeekTimeRelative(int64_t iTime)
3459 int64_t abstime = GetTime() + iTime;
3461 // if the file has EDL cuts we can't rely on m_clock for relative seeks
3462 // EDL cuts remove time from the original file, hence we might seek to
3463 // positions too far from the current m_clock position. Seek to absolute
3464 // time instead
3465 if (m_Edl.HasCuts())
3467 SeekTime(abstime);
3468 return true;
3471 CDVDMsgPlayerSeek::CMode mode;
3472 mode.time = (int)iTime;
3473 mode.relative = true;
3474 mode.backward = (iTime < 0) ? true : false;
3475 mode.accurate = false;
3476 mode.trickplay = false;
3477 mode.sync = true;
3479 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3480 m_processInfo->SetStateSeeking(true);
3482 m_callback.OnPlayBackSeek(abstime, iTime);
3483 m_processInfo->SeekFinished(iTime);
3484 return true;
3487 // return the time in milliseconds
3488 int64_t CVideoPlayer::GetTime()
3490 std::unique_lock<CCriticalSection> lock(m_StateSection);
3491 return llrint(m_State.time);
3494 void CVideoPlayer::SetSpeed(float speed)
3496 // can't rewind in menu as seeking isn't possible
3497 // forward is fine
3498 if (speed < 0 && IsInMenu())
3499 return;
3501 if (!CanSeek() && !CanPause())
3502 return;
3504 int iSpeed = static_cast<int>(speed * DVD_PLAYSPEED_NORMAL);
3506 if (!CanSeek())
3508 if ((iSpeed != DVD_PLAYSPEED_NORMAL) && (iSpeed != DVD_PLAYSPEED_PAUSE))
3509 return;
3512 float currentSpeed = m_processInfo->GetNewSpeed();
3513 m_processInfo->SetNewSpeed(speed);
3514 if (iSpeed != currentSpeed)
3516 if (iSpeed == DVD_PLAYSPEED_NORMAL)
3517 m_callback.OnPlayBackResumed();
3518 else if (iSpeed == DVD_PLAYSPEED_PAUSE)
3519 m_callback.OnPlayBackPaused();
3521 if (iSpeed == DVD_PLAYSPEED_NORMAL)
3523 float currentTempo = m_processInfo->GetNewTempo();
3524 if (currentTempo != 1.0f)
3526 SetTempo(currentTempo);
3527 return;
3530 SetPlaySpeed(iSpeed);
3534 void CVideoPlayer::SetTempo(float tempo)
3536 tempo = floor(tempo * 100.0f + 0.5f) / 100.0f;
3537 if (m_processInfo->IsTempoAllowed(tempo))
3539 int speed = tempo * DVD_PLAYSPEED_NORMAL;
3540 CDVDMsgPlayerSetSpeed::SpeedParams params = { speed, true };
3541 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSpeed>(params));
3543 m_processInfo->SetNewTempo(tempo);
3547 void CVideoPlayer::FrameAdvance(int frames)
3549 float currentSpeed = m_processInfo->GetNewSpeed();
3550 if (currentSpeed != DVD_PLAYSPEED_PAUSE)
3551 return;
3553 m_messenger.Put(std::make_shared<CDVDMsgInt>(CDVDMsg::PLAYER_FRAME_ADVANCE, frames));
3556 bool CVideoPlayer::SupportsTempo() const
3558 return m_State.cantempo;
3561 bool CVideoPlayer::OpenStream(CCurrentStream& current, int64_t demuxerId, int iStream, int source, bool reset /*= true*/)
3563 CDemuxStream* stream = NULL;
3564 CDVDStreamInfo hint;
3566 CLog::Log(LOGINFO, "Opening stream: {} source: {}", iStream, source);
3568 if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
3570 int index = m_SelectionStreams.TypeIndexOf(current.type, source, demuxerId, iStream);
3571 if (index < 0)
3572 return false;
3573 const SelectionStream& st = m_SelectionStreams.Get(current.type, index);
3575 CLog::Log(LOGINFO, "Opening Subtitle file: {}", CURL::GetRedacted(st.filename));
3576 m_pSubtitleDemuxer.reset();
3577 const auto demux = m_subtitleDemuxerMap.find(demuxerId);
3578 if (demux == m_subtitleDemuxerMap.end())
3580 CLog::Log(LOGINFO, "No demuxer found for file {}", CURL::GetRedacted(st.filename));
3581 return false;
3584 m_pSubtitleDemuxer = demux->second;
3586 double pts = m_VideoPlayerVideo->GetCurrentPts();
3587 if(pts == DVD_NOPTS_VALUE)
3588 pts = m_CurrentVideo.dts;
3589 if(pts == DVD_NOPTS_VALUE)
3590 pts = 0;
3591 pts += m_offset_pts;
3592 if (!m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE)))
3593 CLog::Log(LOGDEBUG, "{} - failed to start subtitle demuxing from: {:f}", __FUNCTION__, pts);
3594 stream = m_pSubtitleDemuxer->GetStream(demuxerId, iStream);
3595 if(!stream || stream->disabled)
3596 return false;
3598 m_pSubtitleDemuxer->EnableStream(demuxerId, iStream, true);
3600 hint.Assign(*stream, true);
3602 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
3604 int index = m_SelectionStreams.TypeIndexOf(current.type, source, demuxerId, iStream);
3605 if(index < 0)
3606 return false;
3608 hint.Clear();
3609 hint.filename = m_SelectionStreams.Get(current.type, index).filename;
3610 hint.fpsscale = m_CurrentVideo.hint.fpsscale;
3611 hint.fpsrate = m_CurrentVideo.hint.fpsrate;
3613 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX)
3615 if(!m_pDemuxer)
3616 return false;
3618 m_pDemuxer->OpenStream(demuxerId, iStream);
3620 stream = m_pDemuxer->GetStream(demuxerId, iStream);
3621 if (!stream || stream->disabled)
3622 return false;
3624 hint.Assign(*stream, true);
3626 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3627 hint.filename = "dvd";
3629 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_VIDEOMUX)
3631 if(!m_pCCDemuxer)
3632 return false;
3634 stream = m_pCCDemuxer->GetStream(iStream);
3635 if(!stream || stream->disabled)
3636 return false;
3638 hint.Assign(*stream, false);
3641 bool res;
3642 switch(current.type)
3644 case STREAM_AUDIO:
3645 res = OpenAudioStream(hint, reset);
3646 break;
3647 case STREAM_VIDEO:
3648 res = OpenVideoStream(hint, reset);
3649 break;
3650 case STREAM_SUBTITLE:
3651 res = OpenSubtitleStream(hint);
3652 break;
3653 case STREAM_TELETEXT:
3654 res = OpenTeletextStream(hint);
3655 break;
3656 case STREAM_RADIO_RDS:
3657 res = OpenRadioRDSStream(hint);
3658 break;
3659 case STREAM_AUDIO_ID3:
3660 res = OpenAudioID3Stream(hint);
3661 break;
3662 default:
3663 res = false;
3664 break;
3667 if (res)
3669 int oldId = current.id;
3670 current.id = iStream;
3671 current.demuxerId = demuxerId;
3672 current.source = source;
3673 current.hint = hint;
3674 current.stream = (void*)stream;
3675 current.lastdts = DVD_NOPTS_VALUE;
3676 if (oldId >= 0 && current.avsync != CCurrentStream::AV_SYNC_FORCE)
3677 current.avsync = CCurrentStream::AV_SYNC_CHECK;
3678 if(stream)
3679 current.changes = stream->changes;
3681 else
3683 if(stream)
3685 /* mark stream as disabled, to disallow further attempts*/
3686 CLog::Log(LOGWARNING, "{} - Unsupported stream {}. Stream disabled.", __FUNCTION__,
3687 stream->uniqueId);
3688 stream->disabled = true;
3692 UpdateContentState();
3693 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
3694 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
3695 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3697 return res;
3700 bool CVideoPlayer::OpenAudioStream(CDVDStreamInfo& hint, bool reset)
3702 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentAudio.player);
3703 if(player == nullptr)
3704 return false;
3706 if(m_CurrentAudio.id < 0 ||
3707 m_CurrentAudio.hint != hint)
3709 if (!player->OpenStream(hint))
3710 return false;
3712 player->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, m_displayLost), 1);
3714 static_cast<IDVDStreamPlayerAudio*>(player)->SetSpeed(m_streamPlayerSpeed);
3715 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_STARTING;
3716 m_CurrentAudio.packets = 0;
3718 else if (reset)
3719 player->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_RESET), 0);
3721 m_HasAudio = true;
3723 static_cast<IDVDStreamPlayerAudio*>(player)->SendMessage(
3724 std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_REQUEST_STATE), 1);
3726 return true;
3729 bool CVideoPlayer::OpenVideoStream(CDVDStreamInfo& hint, bool reset)
3731 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3733 /* set aspect ratio as requested by navigator for dvd's */
3734 float aspect = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream)->GetVideoAspectRatio();
3735 if (aspect != 0.0f)
3737 hint.aspect = static_cast<double>(aspect);
3738 hint.forced_aspect = true;
3740 hint.dvd = true;
3742 else if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
3744 // set framerate if not set by demuxer
3745 if (hint.fpsrate == 0 || hint.fpsscale == 0)
3747 int fpsidx = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_PVRPLAYBACK_FPS);
3748 if (fpsidx == 1)
3750 hint.fpsscale = 1000;
3751 hint.fpsrate = 50000;
3753 else if (fpsidx == 2)
3755 hint.fpsscale = 1001;
3756 hint.fpsrate = 60000;
3761 std::shared_ptr<CDVDInputStream::IMenus> pMenus = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
3762 if(pMenus && pMenus->IsInMenu())
3763 hint.stills = true;
3765 if (hint.stereo_mode.empty())
3767 CGUIComponent *gui = CServiceBroker::GetGUI();
3768 if (gui != nullptr)
3770 const CStereoscopicsManager &stereoscopicsManager = gui->GetStereoscopicsManager();
3771 hint.stereo_mode = stereoscopicsManager.DetectStereoModeByString(m_item.GetPath());
3775 if (hint.flags & AV_DISPOSITION_ATTACHED_PIC)
3776 return false;
3778 // set desired refresh rate
3779 if (m_CurrentVideo.id < 0 && m_playerOptions.fullscreen &&
3780 CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot() && hint.fpsrate != 0 &&
3781 hint.fpsscale != 0)
3783 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) != ADJUST_REFRESHRATE_OFF)
3785 const double framerate = DVD_TIME_BASE / CDVDCodecUtils::NormalizeFrameduration(
3786 (double)DVD_TIME_BASE * hint.fpsscale /
3787 (hint.fpsrate * (hint.interlaced ? 2 : 1)));
3789 RESOLUTION res = CResolutionUtils::ChooseBestResolution(static_cast<float>(framerate), hint.width, hint.height, !hint.stereo_mode.empty());
3790 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(res, false);
3791 m_renderManager.TriggerUpdateResolution(framerate, hint.width, hint.height, hint.stereo_mode);
3795 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentVideo.player);
3796 if(player == nullptr)
3797 return false;
3799 if(m_CurrentVideo.id < 0 ||
3800 m_CurrentVideo.hint != hint)
3802 if (hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_H264)
3803 m_pCCDemuxer.reset();
3805 if (!player->OpenStream(hint))
3806 return false;
3808 player->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, m_displayLost), 1);
3810 // look for any EDL files
3811 m_Edl.Clear();
3812 float fFramesPerSecond = 0.0f;
3813 if (m_CurrentVideo.hint.fpsscale > 0.0f)
3814 fFramesPerSecond = static_cast<float>(m_CurrentVideo.hint.fpsrate) / static_cast<float>(m_CurrentVideo.hint.fpsscale);
3815 m_Edl.ReadEditDecisionLists(m_item, fFramesPerSecond);
3816 CServiceBroker::GetDataCacheCore().SetEditList(m_Edl.GetEditList());
3817 CServiceBroker::GetDataCacheCore().SetCuts(m_Edl.GetCutMarkers());
3818 CServiceBroker::GetDataCacheCore().SetSceneMarkers(m_Edl.GetSceneMarkers());
3820 static_cast<IDVDStreamPlayerVideo*>(player)->SetSpeed(m_streamPlayerSpeed);
3821 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_STARTING;
3822 m_CurrentVideo.packets = 0;
3824 else if (reset)
3825 player->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_RESET), 0);
3827 m_HasVideo = true;
3829 static_cast<IDVDStreamPlayerVideo*>(player)->SendMessage(
3830 std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_REQUEST_STATE), 1);
3832 // open CC demuxer if video is mpeg2
3833 if ((hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_H264) && !m_pCCDemuxer)
3835 m_pCCDemuxer = std::make_unique<CDVDDemuxCC>(hint.codec);
3836 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_VIDEOMUX);
3839 return true;
3842 bool CVideoPlayer::OpenSubtitleStream(const CDVDStreamInfo& hint)
3844 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentSubtitle.player);
3845 if(player == nullptr)
3846 return false;
3848 if(m_CurrentSubtitle.id < 0 ||
3849 m_CurrentSubtitle.hint != hint)
3851 if (!player->OpenStream(hint))
3852 return false;
3855 return true;
3858 void CVideoPlayer::AdaptForcedSubtitles()
3860 SelectionStream ss = m_SelectionStreams.Get(STREAM_SUBTITLE, GetSubtitle());
3861 if (ss.flags & StreamFlags::FLAG_FORCED)
3863 SelectionStream as = m_SelectionStreams.Get(STREAM_AUDIO, GetAudioStream());
3864 bool isVisible = false;
3865 for (const auto &stream : m_SelectionStreams.Get(STREAM_SUBTITLE))
3867 if (stream.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(stream.language, as.language))
3869 if (OpenStream(m_CurrentSubtitle, stream.demuxerId, stream.id, stream.source))
3871 isVisible = true;
3872 break;
3876 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
3877 if (!isVisible)
3878 SetEnableStream(m_CurrentSubtitle, false);
3880 SetSubtitleVisibleInternal(isVisible);
3884 bool CVideoPlayer::OpenTeletextStream(CDVDStreamInfo& hint)
3886 if (!m_VideoPlayerTeletext->CheckStream(hint))
3887 return false;
3889 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentTeletext.player);
3890 if(player == nullptr)
3891 return false;
3893 if(m_CurrentTeletext.id < 0 ||
3894 m_CurrentTeletext.hint != hint)
3896 if (!player->OpenStream(hint))
3897 return false;
3900 return true;
3903 bool CVideoPlayer::OpenRadioRDSStream(CDVDStreamInfo& hint)
3905 if (!m_VideoPlayerRadioRDS->CheckStream(hint))
3906 return false;
3908 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentRadioRDS.player);
3909 if(player == nullptr)
3910 return false;
3912 if(m_CurrentRadioRDS.id < 0 ||
3913 m_CurrentRadioRDS.hint != hint)
3915 if (!player->OpenStream(hint))
3916 return false;
3919 return true;
3922 bool CVideoPlayer::OpenAudioID3Stream(CDVDStreamInfo& hint)
3924 if (!m_VideoPlayerAudioID3->CheckStream(hint))
3925 return false;
3927 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentAudioID3.player);
3928 if (player == nullptr)
3929 return false;
3931 if (m_CurrentAudioID3.id < 0 || m_CurrentAudioID3.hint != hint)
3933 if (!player->OpenStream(hint))
3934 return false;
3937 return true;
3940 bool CVideoPlayer::CloseStream(CCurrentStream& current, bool bWaitForBuffers)
3942 if (current.id < 0)
3943 return false;
3945 CLog::Log(LOGINFO, "Closing stream player {}", current.player);
3947 if(bWaitForBuffers)
3948 SetCaching(CACHESTATE_DONE);
3950 SetEnableStream(current, false);
3952 IDVDStreamPlayer* player = GetStreamPlayer(current.player);
3953 if (player)
3955 if ((current.type == STREAM_AUDIO && current.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
3956 (current.type == STREAM_VIDEO && current.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
3957 m_bAbortRequest)
3958 bWaitForBuffers = false;
3959 player->CloseStream(bWaitForBuffers);
3962 current.Clear();
3963 return true;
3966 void CVideoPlayer::FlushBuffers(double pts, bool accurate, bool sync)
3968 CLog::Log(LOGDEBUG, "CVideoPlayer::FlushBuffers - flushing buffers");
3970 double startpts;
3971 if (accurate)
3972 startpts = pts;
3973 else
3974 startpts = DVD_NOPTS_VALUE;
3976 m_SpeedState.Reset(pts);
3978 if (sync)
3980 m_CurrentAudio.inited = false;
3981 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_FORCE;
3982 m_CurrentAudio.starttime = DVD_NOPTS_VALUE;
3983 m_CurrentVideo.inited = false;
3984 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_FORCE;
3985 m_CurrentVideo.starttime = DVD_NOPTS_VALUE;
3986 m_CurrentSubtitle.inited = false;
3987 m_CurrentTeletext.inited = false;
3988 m_CurrentRadioRDS.inited = false;
3991 m_CurrentAudio.dts = DVD_NOPTS_VALUE;
3992 m_CurrentAudio.startpts = startpts;
3993 m_CurrentAudio.packets = 0;
3995 m_CurrentVideo.dts = DVD_NOPTS_VALUE;
3996 m_CurrentVideo.startpts = startpts;
3997 m_CurrentVideo.packets = 0;
3999 m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
4000 m_CurrentSubtitle.startpts = startpts;
4001 m_CurrentSubtitle.packets = 0;
4003 m_CurrentTeletext.dts = DVD_NOPTS_VALUE;
4004 m_CurrentTeletext.startpts = startpts;
4005 m_CurrentTeletext.packets = 0;
4007 m_CurrentRadioRDS.dts = DVD_NOPTS_VALUE;
4008 m_CurrentRadioRDS.startpts = startpts;
4009 m_CurrentRadioRDS.packets = 0;
4011 m_CurrentAudioID3.dts = DVD_NOPTS_VALUE;
4012 m_CurrentAudioID3.startpts = startpts;
4013 m_CurrentAudioID3.packets = 0;
4015 m_VideoPlayerAudio->Flush(sync);
4016 m_VideoPlayerVideo->Flush(sync);
4017 m_VideoPlayerSubtitle->Flush();
4018 m_VideoPlayerTeletext->Flush();
4019 m_VideoPlayerRadioRDS->Flush();
4020 m_VideoPlayerAudioID3->Flush();
4022 if (m_playSpeed == DVD_PLAYSPEED_NORMAL || m_playSpeed == DVD_PLAYSPEED_PAUSE ||
4023 (m_playSpeed >= DVD_PLAYSPEED_NORMAL * m_processInfo->MinTempoPlatform() &&
4024 m_playSpeed <= DVD_PLAYSPEED_NORMAL * m_processInfo->MaxTempoPlatform()))
4026 // make sure players are properly flushed, should put them in stalled state
4027 auto msg = std::make_shared<CDVDMsgGeneralSynchronize>(1s, SYNCSOURCE_AUDIO | SYNCSOURCE_VIDEO);
4028 m_VideoPlayerAudio->SendMessage(msg, 1);
4029 m_VideoPlayerVideo->SendMessage(msg, 1);
4030 msg->Wait(m_bStop, 0);
4032 // purge any pending PLAYER_STARTED messages
4033 m_messenger.Flush(CDVDMsg::PLAYER_STARTED);
4035 // we should now wait for init cache
4036 SetCaching(CACHESTATE_FLUSH);
4037 if (sync)
4039 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_STARTING;
4040 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_STARTING;
4044 if(pts != DVD_NOPTS_VALUE && sync)
4045 m_clock.Discontinuity(pts);
4046 UpdatePlayState(0);
4048 m_demuxerSpeed = DVD_PLAYSPEED_NORMAL;
4049 if (m_pDemuxer)
4050 m_pDemuxer->SetSpeed(DVD_PLAYSPEED_NORMAL);
4053 // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
4054 int CVideoPlayer::OnDiscNavResult(void* pData, int iMessage)
4056 if (!m_pInputStream)
4057 return 0;
4059 #if defined(HAVE_LIBBLURAY)
4060 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
4062 switch (iMessage)
4064 case BD_EVENT_MENU_OVERLAY:
4065 m_overlayContainer.ProcessAndAddOverlayIfValid(
4066 *static_cast<std::shared_ptr<CDVDOverlay>*>(pData));
4067 break;
4068 case BD_EVENT_PLAYLIST_STOP:
4069 m_dvd.state = DVDSTATE_NORMAL;
4070 m_dvd.iDVDStillTime = 0ms;
4071 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_FLUSH));
4072 break;
4073 case BD_EVENT_AUDIO_STREAM:
4074 m_dvd.iSelectedAudioStream = *static_cast<int*>(pData);
4075 break;
4077 case BD_EVENT_PG_TEXTST_STREAM:
4078 m_dvd.iSelectedSPUStream = *static_cast<int*>(pData);
4079 break;
4080 case BD_EVENT_PG_TEXTST:
4082 bool enable = (*static_cast<int*>(pData) != 0);
4083 m_VideoPlayerVideo->EnableSubtitle(enable);
4085 break;
4086 case BD_EVENT_STILL_TIME:
4088 if (m_dvd.state != DVDSTATE_STILL)
4090 // else notify the player we have received a still frame
4092 m_dvd.iDVDStillTime = std::chrono::milliseconds(*static_cast<int*>(pData));
4093 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4095 if (m_dvd.iDVDStillTime > 0ms)
4096 m_dvd.iDVDStillTime *= 1000;
4098 /* adjust for the output delay in the video queue */
4099 std::chrono::milliseconds time = 0ms;
4100 if (m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0ms)
4102 time = std::chrono::milliseconds(
4103 static_cast<int>(m_VideoPlayerVideo->GetOutputDelay() / (DVD_TIME_BASE / 1000)));
4104 if (time < 10000ms && time > 0ms)
4105 m_dvd.iDVDStillTime += time;
4107 m_dvd.state = DVDSTATE_STILL;
4108 CLog::Log(LOGDEBUG, "BD_EVENT_STILL_TIME - waiting {} msec, with delay of {} msec",
4109 m_dvd.iDVDStillTime.count(), time.count());
4112 break;
4113 case BD_EVENT_STILL:
4115 bool on = static_cast<bool>(*static_cast<int*>(pData));
4116 if (on && m_dvd.state != DVDSTATE_STILL)
4118 m_dvd.state = DVDSTATE_STILL;
4119 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4120 m_dvd.iDVDStillTime = 0ms;
4121 CLog::Log(LOGDEBUG, "CDVDPlayer::OnDVDNavResult - libbluray DVDSTATE_STILL start");
4123 else if (!on && m_dvd.state == DVDSTATE_STILL)
4125 m_dvd.state = DVDSTATE_NORMAL;
4126 m_dvd.iDVDStillStartTime = {};
4127 m_dvd.iDVDStillTime = 0ms;
4128 CLog::Log(LOGDEBUG, "CDVDPlayer::OnDVDNavResult - libbluray DVDSTATE_STILL end");
4131 break;
4132 case BD_EVENT_MENU_ERROR:
4134 m_dvd.state = DVDSTATE_NORMAL;
4135 CLog::Log(LOGDEBUG, "CVideoPlayer::OnDiscNavResult - libbluray menu not supported (DVDSTATE_NORMAL)");
4136 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25008), g_localizeStrings.Get(25009));
4138 break;
4139 case BD_EVENT_ENC_ERROR:
4141 m_dvd.state = DVDSTATE_NORMAL;
4142 CLog::Log(LOGDEBUG, "CVideoPlayer::OnDiscNavResult - libbluray the disc/file is encrypted and can't be played (DVDSTATE_NORMAL)");
4143 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(16026), g_localizeStrings.Get(29805));
4145 break;
4146 default:
4147 break;
4150 return 0;
4152 #endif
4154 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
4156 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
4158 switch (iMessage)
4160 case DVDNAV_STILL_FRAME:
4162 //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
4164 dvdnav_still_event_t *still_event = static_cast<dvdnav_still_event_t*>(pData);
4165 // should wait the specified time here while we let the player running
4166 // after that call dvdnav_still_skip(m_dvdnav);
4168 if (m_dvd.state != DVDSTATE_STILL)
4170 // else notify the player we have received a still frame
4172 if(still_event->length < 0xff)
4173 m_dvd.iDVDStillTime = std::chrono::seconds(still_event->length);
4174 else
4175 m_dvd.iDVDStillTime = 0ms;
4177 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4179 /* adjust for the output delay in the video queue */
4180 std::chrono::milliseconds time = 0ms;
4181 if (m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0ms)
4183 time = std::chrono::milliseconds(
4184 static_cast<int>(m_VideoPlayerVideo->GetOutputDelay() / (DVD_TIME_BASE / 1000)));
4185 if (time < 10000ms && time > 0ms)
4186 m_dvd.iDVDStillTime += time;
4188 m_dvd.state = DVDSTATE_STILL;
4189 CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME - waiting {} sec, with delay of {} msec",
4190 still_event->length, time.count());
4192 return NAVRESULT_HOLD;
4194 break;
4195 case DVDNAV_SPU_CLUT_CHANGE:
4197 m_VideoPlayerSubtitle->SendMessage(
4198 std::make_shared<CDVDMsgSubtitleClutChange>((uint8_t*)pData));
4200 break;
4201 case DVDNAV_SPU_STREAM_CHANGE:
4203 dvdnav_spu_stream_change_event_t* event = static_cast<dvdnav_spu_stream_change_event_t*>(pData);
4205 int iStream = event->physical_wide;
4206 bool visible = !(iStream & 0x80);
4208 SetSubtitleVisibleInternal(visible);
4210 if (iStream >= 0)
4211 m_dvd.iSelectedSPUStream = (iStream & ~0x80);
4212 else
4213 m_dvd.iSelectedSPUStream = -1;
4215 m_CurrentSubtitle.stream = NULL;
4217 break;
4218 case DVDNAV_AUDIO_STREAM_CHANGE:
4220 dvdnav_audio_stream_change_event_t* event = static_cast<dvdnav_audio_stream_change_event_t*>(pData);
4221 // Tell system what audiostream should be opened by default
4222 m_dvd.iSelectedAudioStream = event->physical;
4223 m_CurrentAudio.stream = NULL;
4225 break;
4226 case DVDNAV_HIGHLIGHT:
4228 //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
4229 int iButton = pStream->GetCurrentButton();
4230 CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button {}", iButton);
4231 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_NORMAL);
4233 break;
4234 case DVDNAV_VTS_CHANGE:
4236 //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
4237 CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
4239 //Make sure we clear all the old overlays here, or else old forced items are left.
4240 m_overlayContainer.Clear();
4242 //Force an aspect ratio that is set in the dvdheaders if available
4243 m_CurrentVideo.hint.aspect = static_cast<double>(pStream->GetVideoAspectRatio());
4244 if( m_VideoPlayerVideo->IsInited() )
4245 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgDouble>(
4246 CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
4248 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
4249 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
4250 UpdateContent();
4252 return NAVRESULT_HOLD;
4254 break;
4255 case DVDNAV_CELL_CHANGE:
4257 //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
4258 CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
4260 if (m_dvd.state != DVDSTATE_STILL)
4261 m_dvd.state = DVDSTATE_NORMAL;
4263 break;
4264 case DVDNAV_NAV_PACKET:
4266 //pci_t* pci = (pci_t*)pData;
4268 // this should be possible to use to make sure we get
4269 // seamless transitions over these boundaries
4270 // if we remember the old vobunits boundaries
4271 // when a packet comes out of demuxer that has
4272 // pts values outside that boundary, it belongs
4273 // to the new vobunit, which has new timestamps
4274 UpdatePlayState(0);
4276 break;
4277 case DVDNAV_HOP_CHANNEL:
4279 // This event is issued whenever a non-seamless operation has been executed.
4280 // Applications with fifos should drop the fifos content to speed up responsiveness.
4281 CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
4282 if(m_dvd.state == DVDSTATE_SEEK)
4283 m_dvd.state = DVDSTATE_NORMAL;
4284 else
4286 bool sync = !IsInMenuInternal();
4287 FlushBuffers(DVD_NOPTS_VALUE, false, sync);
4288 m_dvd.syncClock = true;
4289 m_dvd.state = DVDSTATE_NORMAL;
4290 if (m_pDemuxer)
4291 m_pDemuxer->Flush();
4294 return NAVRESULT_ERROR;
4296 break;
4297 case DVDNAV_STOP:
4299 CLog::Log(LOGDEBUG, "DVDNAV_STOP");
4300 m_dvd.state = DVDSTATE_NORMAL;
4302 break;
4303 case DVDNAV_ERROR:
4305 CLog::Log(LOGDEBUG, "DVDNAV_ERROR");
4306 m_dvd.state = DVDSTATE_NORMAL;
4307 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(16026),
4308 g_localizeStrings.Get(16029));
4310 break;
4311 default:
4313 break;
4316 return NAVRESULT_NOP;
4319 void CVideoPlayer::GetVideoResolution(unsigned int &width, unsigned int &height)
4321 RESOLUTION_INFO res = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
4322 width = res.iWidth;
4323 height = res.iHeight;
4326 bool CVideoPlayer::OnAction(const CAction &action)
4328 #define THREAD_ACTION(action) \
4329 do \
4331 if (!IsCurrentThread()) \
4333 m_messenger.Put( \
4334 std::make_shared<CDVDMsgType<CAction>>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
4335 return true; \
4337 } while (false)
4339 std::shared_ptr<CDVDInputStream::IMenus> pMenus = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4340 if (pMenus)
4342 if (m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0ms &&
4343 pMenus->GetTotalButtons() == 0)
4345 switch(action.GetID())
4347 case ACTION_NEXT_ITEM:
4348 case ACTION_MOVE_RIGHT:
4349 case ACTION_MOVE_UP:
4350 case ACTION_SELECT_ITEM:
4352 THREAD_ACTION(action);
4353 /* this will force us out of the stillframe */
4354 CLog::Log(LOGDEBUG, "{} - User asked to exit stillframe", __FUNCTION__);
4355 m_dvd.iDVDStillStartTime = {};
4356 m_dvd.iDVDStillTime = 1ms;
4358 return true;
4363 switch (action.GetID())
4365 /* this code is disabled to allow switching playlist items (dvdimage "stacks") */
4366 #if 0
4367 case ACTION_PREV_ITEM: // SKIP-:
4369 THREAD_ACTION(action);
4370 CLog::Log(LOGDEBUG, " - pushed prev");
4371 pMenus->OnPrevious();
4372 m_processInfo->SeekFinished(0);
4373 return true;
4375 break;
4376 case ACTION_NEXT_ITEM: // SKIP+:
4378 THREAD_ACTION(action);
4379 CLog::Log(LOGDEBUG, " - pushed next");
4380 pMenus->OnNext();
4381 m_processInfo->SeekFinished(0);
4382 return true;
4384 break;
4385 #endif
4386 case ACTION_SHOW_VIDEOMENU: // start button
4388 THREAD_ACTION(action);
4389 CLog::LogF(LOGDEBUG, "Trying to go to the menu");
4390 if (pMenus->OnMenu())
4392 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
4394 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
4395 m_callback.OnPlayBackResumed();
4398 // send a message to everyone that we've gone to the menu
4399 CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0);
4400 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
4402 return true;
4404 break;
4407 if (pMenus->IsInMenu())
4409 switch (action.GetID())
4411 case ACTION_NEXT_ITEM:
4412 THREAD_ACTION(action);
4413 CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide");
4414 if (pMenus->CanSeek() && GetChapterCount() > 0 && GetChapter() < GetChapterCount())
4415 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetChapter() + 1));
4416 else
4417 pMenus->OnNext();
4419 m_processInfo->SeekFinished(0);
4420 return true;
4421 case ACTION_PREV_ITEM:
4422 THREAD_ACTION(action);
4423 CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
4424 if (pMenus->CanSeek() && GetChapterCount() > 0 && GetChapter() > 0)
4425 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetPreviousChapter()));
4426 else
4427 pMenus->OnPrevious();
4429 m_processInfo->SeekFinished(0);
4430 return true;
4431 case ACTION_PREVIOUS_MENU:
4432 case ACTION_NAV_BACK:
4434 THREAD_ACTION(action);
4435 CLog::Log(LOGDEBUG, " - menu back");
4436 pMenus->OnBack();
4438 break;
4439 case ACTION_MOVE_LEFT:
4441 THREAD_ACTION(action);
4442 CLog::Log(LOGDEBUG, " - move left");
4443 pMenus->OnLeft();
4445 break;
4446 case ACTION_MOVE_RIGHT:
4448 THREAD_ACTION(action);
4449 CLog::Log(LOGDEBUG, " - move right");
4450 pMenus->OnRight();
4452 break;
4453 case ACTION_MOVE_UP:
4455 THREAD_ACTION(action);
4456 CLog::Log(LOGDEBUG, " - move up");
4457 pMenus->OnUp();
4459 break;
4460 case ACTION_MOVE_DOWN:
4462 THREAD_ACTION(action);
4463 CLog::Log(LOGDEBUG, " - move down");
4464 pMenus->OnDown();
4466 break;
4468 case ACTION_MOUSE_MOVE:
4469 case ACTION_MOUSE_LEFT_CLICK:
4471 CRect rs, rd, rv;
4472 m_renderManager.GetVideoRect(rs, rd, rv);
4473 CPoint pt(action.GetAmount(), action.GetAmount(1));
4474 if (!rd.PtInRect(pt))
4475 return false; // out of bounds
4476 THREAD_ACTION(action);
4477 // convert to video coords...
4478 pt -= CPoint(rd.x1, rd.y1);
4479 pt.x *= rs.Width() / rd.Width();
4480 pt.y *= rs.Height() / rd.Height();
4481 pt += CPoint(rs.x1, rs.y1);
4482 if (action.GetID() == ACTION_MOUSE_LEFT_CLICK)
4484 if (pMenus->OnMouseClick(pt))
4485 return true;
4486 else
4488 CServiceBroker::GetAppMessenger()->PostMsg(
4489 TMSG_GUI_ACTION, WINDOW_INVALID, -1,
4490 static_cast<void*>(new CAction(ACTION_TRIGGER_OSD)));
4491 return false;
4494 return pMenus->OnMouseMove(pt);
4496 break;
4497 case ACTION_SELECT_ITEM:
4499 THREAD_ACTION(action);
4500 CLog::Log(LOGDEBUG, " - button select");
4501 // show button pushed overlay
4502 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
4503 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_CLICKED);
4505 pMenus->ActivateButton();
4507 break;
4508 case REMOTE_0:
4509 case REMOTE_1:
4510 case REMOTE_2:
4511 case REMOTE_3:
4512 case REMOTE_4:
4513 case REMOTE_5:
4514 case REMOTE_6:
4515 case REMOTE_7:
4516 case REMOTE_8:
4517 case REMOTE_9:
4519 THREAD_ACTION(action);
4520 // Offset from key codes back to button number
4521 int button = action.GetID() - REMOTE_0;
4522 CLog::Log(LOGDEBUG, " - button pressed {}", button);
4523 pMenus->SelectButton(button);
4525 break;
4526 default:
4527 return false;
4528 break;
4530 return true; // message is handled
4534 pMenus.reset();
4536 switch (action.GetID())
4538 case ACTION_NEXT_ITEM:
4539 if (GetChapter() > 0 && GetChapter() < GetChapterCount())
4541 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetChapter() + 1));
4542 m_processInfo->SeekFinished(0);
4543 return true;
4545 else if (SeekScene(Direction::FORWARD))
4546 return true;
4547 else
4548 break;
4549 case ACTION_PREV_ITEM:
4550 if (GetChapter() > 0)
4552 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetPreviousChapter()));
4553 m_processInfo->SeekFinished(0);
4554 return true;
4556 else if (SeekScene(Direction::BACKWARD))
4557 return true;
4558 else
4559 break;
4560 case ACTION_TOGGLE_COMMSKIP:
4561 m_SkipCommercials = !m_SkipCommercials;
4562 CGUIDialogKaiToast::QueueNotification(g_localizeStrings.Get(25011),
4563 g_localizeStrings.Get(m_SkipCommercials ? 25013 : 25012));
4564 break;
4565 case ACTION_PLAYER_DEBUG:
4566 m_renderManager.ToggleDebug();
4567 break;
4568 case ACTION_PLAYER_DEBUG_VIDEO:
4569 m_renderManager.ToggleDebugVideo();
4570 break;
4572 case ACTION_PLAYER_PROCESS_INFO:
4573 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_DIALOG_PLAYER_PROCESS_INFO)
4575 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_DIALOG_PLAYER_PROCESS_INFO);
4576 return true;
4578 break;
4581 // return false to inform the caller we didn't handle the message
4582 return false;
4585 bool CVideoPlayer::IsInMenuInternal() const
4587 std::shared_ptr<CDVDInputStream::IMenus> pStream = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4588 if (pStream)
4590 if (m_dvd.state == DVDSTATE_STILL)
4591 return true;
4592 else
4593 return pStream->IsInMenu();
4595 return false;
4599 bool CVideoPlayer::IsInMenu() const
4601 std::unique_lock<CCriticalSection> lock(m_StateSection);
4602 return m_State.isInMenu;
4605 MenuType CVideoPlayer::GetSupportedMenuType() const
4607 std::unique_lock<CCriticalSection> lock(m_StateSection);
4608 return m_State.menuType;
4611 std::string CVideoPlayer::GetPlayerState()
4613 std::unique_lock<CCriticalSection> lock(m_StateSection);
4614 return m_State.player_state;
4617 bool CVideoPlayer::SetPlayerState(const std::string& state)
4619 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetState>(state));
4620 return true;
4623 int CVideoPlayer::GetChapterCount() const
4625 std::unique_lock<CCriticalSection> lock(m_StateSection);
4626 return m_State.chapters.size();
4629 int CVideoPlayer::GetChapter() const
4631 std::unique_lock<CCriticalSection> lock(m_StateSection);
4632 return m_State.chapter;
4635 void CVideoPlayer::GetChapterName(std::string& strChapterName, int chapterIdx) const
4637 std::unique_lock<CCriticalSection> lock(m_StateSection);
4638 if (chapterIdx == -1 && m_State.chapter > 0 && m_State.chapter <= (int) m_State.chapters.size())
4639 strChapterName = m_State.chapters[m_State.chapter - 1].first;
4640 else if (chapterIdx > 0 && chapterIdx <= (int) m_State.chapters.size())
4641 strChapterName = m_State.chapters[chapterIdx - 1].first;
4644 int CVideoPlayer::SeekChapter(int iChapter)
4646 if (GetChapter() > 0)
4648 if (iChapter < 0)
4649 iChapter = 0;
4650 if (iChapter > GetChapterCount())
4651 return 0;
4653 // Seek to the chapter.
4654 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(iChapter));
4655 SynchronizeDemuxer();
4658 return 0;
4661 int64_t CVideoPlayer::GetChapterPos(int chapterIdx) const
4663 std::unique_lock<CCriticalSection> lock(m_StateSection);
4664 if (chapterIdx > 0 && chapterIdx <= (int) m_State.chapters.size())
4665 return m_State.chapters[chapterIdx - 1].second;
4667 return -1;
4670 int CVideoPlayer::GetPreviousChapter()
4672 // 5-second grace period from chapter start to skip backwards to previous chapter
4673 // Afterwards skip to start of current chapter.
4674 const int chapter = GetChapter();
4676 if (chapter > 0 && (GetTime() < (GetChapterPos(chapter) + 5) * 1000))
4677 return chapter - 1;
4678 else
4679 return chapter;
4682 void CVideoPlayer::AddSubtitle(const std::string& strSubPath)
4684 m_messenger.Put(
4685 std::make_shared<CDVDMsgType<std::string>>(CDVDMsg::SUBTITLE_ADDFILE, strSubPath));
4688 bool CVideoPlayer::IsCaching() const
4690 std::unique_lock<CCriticalSection> lock(m_StateSection);
4691 return !m_State.isInMenu && m_State.caching;
4694 int CVideoPlayer::GetCacheLevel() const
4696 std::unique_lock<CCriticalSection> lock(m_StateSection);
4697 return (int)(m_State.cache_level * 100);
4700 double CVideoPlayer::GetQueueTime()
4702 int a = m_VideoPlayerAudio->GetLevel();
4703 int v = m_processInfo->GetLevelVQ();
4704 return std::max(a, v) * m_messageQueueTimeSize * 1000.0 / 100.0;
4707 int CVideoPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename)
4709 std::string ext = URIUtils::GetExtension(filename);
4710 std::string vobsubfile = subfilename;
4711 if (ext == ".idx" || ext == ".sup")
4713 std::shared_ptr<CDVDDemux> pDemux;
4714 if (ext == ".idx")
4716 if (vobsubfile.empty())
4718 // find corresponding .sub (e.g. in case of manually selected .idx sub)
4719 vobsubfile = CUtil::GetVobSubSubFromIdx(filename);
4720 if (vobsubfile.empty())
4721 return -1;
4724 auto pDemuxVobsub = std::make_shared<CDVDDemuxVobsub>();
4725 if (!pDemuxVobsub->Open(filename, STREAM_SOURCE_NONE, vobsubfile))
4726 return -1;
4728 m_SelectionStreams.Update(nullptr, pDemuxVobsub.get(), vobsubfile);
4729 pDemux = pDemuxVobsub;
4731 else // .sup file
4733 CFileItem item(filename, false);
4734 std::shared_ptr<CDVDInputStream> pInput;
4735 pInput = CDVDFactoryInputStream::CreateInputStream(nullptr, item);
4736 if (!pInput || !pInput->Open())
4737 return -1;
4739 auto pDemuxFFmpeg = std::make_shared<CDVDDemuxFFmpeg>();
4740 if (!pDemuxFFmpeg->Open(pInput, false))
4741 return -1;
4743 m_SelectionStreams.Update(nullptr, pDemuxFFmpeg.get(), filename);
4744 pDemux = pDemuxFFmpeg;
4747 ExternalStreamInfo info =
4748 CUtil::GetExternalStreamDetailsFromFilename(m_item.GetDynPath(), filename);
4750 for (auto sub : pDemux->GetStreams())
4752 if (sub->type != STREAM_SUBTITLE)
4753 continue;
4755 int index = m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE,
4756 m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename),
4757 sub->demuxerId, sub->uniqueId);
4758 SelectionStream& stream = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
4760 if (stream.name.empty())
4761 stream.name = info.name;
4763 if (stream.language.empty())
4764 stream.language = info.language;
4766 if (static_cast<StreamFlags>(info.flag) != StreamFlags::FLAG_NONE)
4767 stream.flags = static_cast<StreamFlags>(info.flag);
4770 UpdateContent();
4771 // the demuxer id is unique
4772 m_subtitleDemuxerMap[pDemux->GetDemuxerId()] = pDemux;
4773 return m_SelectionStreams.TypeIndexOf(
4774 STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename),
4775 pDemux->GetDemuxerId(), 0);
4778 if(ext == ".sub")
4780 // if this looks like vobsub file (i.e. .idx found), add it as such
4781 std::string vobsubidx = CUtil::GetVobSubIdxFromSub(filename);
4782 if (!vobsubidx.empty())
4783 return AddSubtitleFile(vobsubidx, filename);
4786 SelectionStream s;
4787 s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename);
4788 s.type = STREAM_SUBTITLE;
4789 s.id = 0;
4790 s.filename = filename;
4791 ExternalStreamInfo info = CUtil::GetExternalStreamDetailsFromFilename(m_item.GetDynPath(), filename);
4792 s.name = info.name;
4793 s.language = info.language;
4794 if (static_cast<StreamFlags>(info.flag) != StreamFlags::FLAG_NONE)
4795 s.flags = static_cast<StreamFlags>(info.flag);
4797 m_SelectionStreams.Update(s);
4798 UpdateContent();
4799 return m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE, s.source, s.demuxerId, s.id);
4802 void CVideoPlayer::UpdatePlayState(double timeout)
4804 if (m_State.timestamp != 0 &&
4805 m_State.timestamp + DVD_MSEC_TO_TIME(timeout) > m_clock.GetAbsoluteClock())
4806 return;
4808 SPlayerState state(m_State);
4810 state.dts = DVD_NOPTS_VALUE;
4811 if (m_CurrentVideo.dts != DVD_NOPTS_VALUE)
4812 state.dts = m_CurrentVideo.dts;
4813 else if (m_CurrentAudio.dts != DVD_NOPTS_VALUE)
4814 state.dts = m_CurrentAudio.dts;
4815 else if (m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
4816 state.dts = m_CurrentVideo.startpts;
4817 else if (m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
4818 state.dts = m_CurrentAudio.startpts;
4820 state.startTime = 0;
4821 state.timeMin = 0;
4823 std::shared_ptr<CDVDInputStream::IMenus> pMenu = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4825 if (m_pDemuxer)
4827 if (IsInMenuInternal() && pMenu && !pMenu->CanSeek())
4828 state.chapter = 0;
4829 else
4830 state.chapter = m_pDemuxer->GetChapter();
4832 state.chapters.clear();
4833 if (m_pDemuxer->GetChapterCount() > 0)
4835 for (int i = 0, ie = m_pDemuxer->GetChapterCount(); i < ie; ++i)
4837 std::string name;
4838 m_pDemuxer->GetChapterName(name, i + 1);
4839 state.chapters.emplace_back(name, m_pDemuxer->GetChapterPos(i + 1));
4842 CServiceBroker::GetDataCacheCore().SetChapters(state.chapters);
4844 state.time = m_clock.GetClock(false) * 1000 / DVD_TIME_BASE;
4845 state.timeMax = m_pDemuxer->GetStreamLength();
4848 state.canpause = false;
4849 state.canseek = false;
4850 state.cantempo = false;
4851 state.isInMenu = false;
4852 state.menuType = MenuType::NONE;
4854 if (m_pInputStream)
4856 CDVDInputStream::IChapter* pChapter = m_pInputStream->GetIChapter();
4857 if (pChapter)
4859 if (IsInMenuInternal() && pMenu && !pMenu->CanSeek())
4860 state.chapter = 0;
4861 else
4862 state.chapter = pChapter->GetChapter();
4864 state.chapters.clear();
4865 if (pChapter->GetChapterCount() > 0)
4867 for (int i = 0, ie = pChapter->GetChapterCount(); i < ie; ++i)
4869 std::string name;
4870 pChapter->GetChapterName(name, i + 1);
4871 state.chapters.emplace_back(name, pChapter->GetChapterPos(i + 1));
4874 CServiceBroker::GetDataCacheCore().SetChapters(state.chapters);
4877 CDVDInputStream::ITimes* pTimes = m_pInputStream->GetITimes();
4878 CDVDInputStream::IDisplayTime* pDisplayTime = m_pInputStream->GetIDisplayTime();
4880 CDVDInputStream::ITimes::Times times;
4881 if (pTimes && pTimes->GetTimes(times))
4883 state.startTime = times.startTime;
4884 state.time = (m_clock.GetClock(false) - times.ptsStart) * 1000 / DVD_TIME_BASE;
4885 state.timeMax = (times.ptsEnd - times.ptsStart) * 1000 / DVD_TIME_BASE;
4886 state.timeMin = (times.ptsBegin - times.ptsStart) * 1000 / DVD_TIME_BASE;
4887 state.time_offset = -times.ptsStart;
4889 else if (pDisplayTime && pDisplayTime->GetTotalTime() > 0)
4891 if (state.dts != DVD_NOPTS_VALUE)
4893 int dispTime = 0;
4894 if (m_CurrentVideo.id >= 0 && m_CurrentVideo.dispTime)
4895 dispTime = m_CurrentVideo.dispTime;
4896 else if (m_CurrentAudio.dispTime)
4897 dispTime = m_CurrentAudio.dispTime;
4899 state.time_offset = DVD_MSEC_TO_TIME(dispTime) - state.dts;
4901 state.time += state.time_offset * 1000 / DVD_TIME_BASE;
4902 state.timeMax = pDisplayTime->GetTotalTime();
4904 else
4906 state.time_offset = 0;
4909 if (pMenu)
4911 if (!pMenu->GetState(state.player_state))
4912 state.player_state = "";
4914 if (m_dvd.state == DVDSTATE_STILL)
4916 const auto now = std::chrono::steady_clock::now();
4917 const auto duration =
4918 std::chrono::duration_cast<std::chrono::milliseconds>(now - m_dvd.iDVDStillStartTime);
4919 state.time = duration.count();
4920 state.timeMax = m_dvd.iDVDStillTime.count();
4921 state.isInMenu = true;
4923 else if (IsInMenuInternal())
4925 state.time = pDisplayTime->GetTime();
4926 state.isInMenu = true;
4927 if (!pMenu->CanSeek())
4928 state.time_offset = 0;
4930 state.menuType = pMenu->GetSupportedMenuType();
4933 state.canpause = m_pInputStream->CanPause();
4935 bool realtime = m_pInputStream->IsRealtime();
4937 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK) &&
4938 !realtime)
4940 state.cantempo = true;
4942 else
4944 state.cantempo = false;
4947 m_processInfo->SetStateRealtime(realtime);
4950 if (m_Edl.HasCuts())
4952 state.time = static_cast<double>(
4953 m_Edl.GetTimeWithoutCuts(std::chrono::milliseconds(std::lround(state.time))).count());
4954 state.timeMax = state.timeMax - static_cast<double>(m_Edl.GetTotalCutTime().count());
4957 if (m_caching > CACHESTATE_DONE && m_caching < CACHESTATE_PLAY)
4958 state.caching = true;
4959 else
4960 state.caching = false;
4962 double queueTime = GetQueueTime();
4963 CacheInfo cache = GetCachingTimes();
4965 if (cache.valid)
4967 state.cache_level = std::max(0.0, std::min(1.0, cache.level));
4968 state.cache_offset = cache.offset;
4969 state.cache_time = cache.time;
4971 else
4973 state.cache_level = std::min(1.0, queueTime / (m_messageQueueTimeSize * 1000.0));
4974 state.cache_offset = queueTime / state.timeMax;
4975 state.cache_time = queueTime / 1000.0;
4978 XFILE::SCacheStatus status;
4979 if (m_pInputStream && m_pInputStream->GetCacheStatus(&status))
4981 state.cache_bytes = status.forward;
4982 if(state.timeMax)
4983 state.cache_bytes += m_pInputStream->GetLength() * (int64_t)(queueTime / state.timeMax);
4985 else
4986 state.cache_bytes = 0;
4988 state.timestamp = m_clock.GetAbsoluteClock();
4990 if (state.timeMax <= 0)
4992 state.timeMax = state.time;
4993 state.timeMin = state.time;
4995 if (state.timeMin == state.timeMax)
4997 state.canseek = false;
4998 state.cantempo = false;
5000 else
5002 state.canseek = true;
5003 state.canpause = true;
5006 m_processInfo->SetPlayTimes(state.startTime, state.time, state.timeMin, state.timeMax);
5008 std::unique_lock<CCriticalSection> lock(m_StateSection);
5009 m_State = state;
5012 int64_t CVideoPlayer::GetUpdatedTime()
5014 UpdatePlayState(0);
5015 return llrint(m_State.time);
5018 void CVideoPlayer::SetDynamicRangeCompression(long drc)
5020 m_processInfo->GetVideoSettingsLocked().SetVolumeAmplification(static_cast<float>(drc) / 100);
5021 m_VideoPlayerAudio->SetDynamicRangeCompression(drc);
5024 CVideoSettings CVideoPlayer::GetVideoSettings() const
5026 return m_processInfo->GetVideoSettings();
5029 void CVideoPlayer::SetVideoSettings(CVideoSettings& settings)
5031 m_processInfo->SetVideoSettings(settings);
5032 m_renderManager.SetVideoSettings(settings);
5033 m_renderManager.SetDelay(static_cast<int>(settings.m_AudioDelay * 1000.0f));
5034 m_renderManager.SetSubtitleVerticalPosition(settings.m_subtitleVerticalPosition,
5035 settings.m_subtitleVerticalPositionSave);
5036 m_VideoPlayerVideo->EnableSubtitle(settings.m_SubtitleOn);
5037 m_VideoPlayerVideo->SetSubtitleDelay(static_cast<int>(-settings.m_SubtitleDelay * DVD_TIME_BASE));
5040 void CVideoPlayer::FrameMove()
5042 m_renderManager.FrameMove();
5045 void CVideoPlayer::Render(bool clear, uint32_t alpha, bool gui)
5047 m_renderManager.Render(clear, 0, alpha, gui);
5050 void CVideoPlayer::FlushRenderer()
5052 m_renderManager.Flush(true, true);
5055 void CVideoPlayer::SetRenderViewMode(int mode, float zoom, float par, float shift, bool stretch)
5057 m_processInfo->GetVideoSettingsLocked().SetViewMode(mode, zoom, par, shift, stretch);
5058 m_renderManager.SetVideoSettings(m_processInfo->GetVideoSettings());
5059 m_renderManager.SetViewMode(mode);
5062 float CVideoPlayer::GetRenderAspectRatio() const
5064 return m_renderManager.GetAspectRatio();
5067 void CVideoPlayer::GetRects(CRect& source, CRect& dest, CRect& view) const
5069 m_renderManager.GetVideoRect(source, dest, view);
5072 unsigned int CVideoPlayer::GetOrientation() const
5074 return m_renderManager.GetOrientation();
5077 void CVideoPlayer::TriggerUpdateResolution()
5079 std::string stereomode;
5080 m_renderManager.TriggerUpdateResolution(0, 0, 0, stereomode);
5083 bool CVideoPlayer::IsRenderingVideo() const
5085 return m_renderManager.IsConfigured();
5088 bool CVideoPlayer::Supports(EINTERLACEMETHOD method) const
5090 if (!m_processInfo)
5091 return false;
5092 return m_processInfo->Supports(method);
5095 EINTERLACEMETHOD CVideoPlayer::GetDeinterlacingMethodDefault() const
5097 if (!m_processInfo)
5098 return EINTERLACEMETHOD::VS_INTERLACEMETHOD_NONE;
5099 return m_processInfo->GetDeinterlacingMethodDefault();
5102 bool CVideoPlayer::Supports(ESCALINGMETHOD method) const
5104 return m_renderManager.Supports(method);
5107 bool CVideoPlayer::Supports(ERENDERFEATURE feature) const
5109 return m_renderManager.Supports(feature);
5112 unsigned int CVideoPlayer::RenderCaptureAlloc()
5114 return m_renderManager.AllocRenderCapture();
5117 void CVideoPlayer::RenderCapture(unsigned int captureId, unsigned int width, unsigned int height, int flags)
5119 m_renderManager.StartRenderCapture(captureId, width, height, flags);
5122 void CVideoPlayer::RenderCaptureRelease(unsigned int captureId)
5124 m_renderManager.ReleaseRenderCapture(captureId);
5127 bool CVideoPlayer::RenderCaptureGetPixels(unsigned int captureId, unsigned int millis, uint8_t *buffer, unsigned int size)
5129 return m_renderManager.RenderCaptureGetPixels(captureId, millis, buffer, size);
5132 void CVideoPlayer::VideoParamsChange()
5134 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_AVCHANGE));
5137 void CVideoPlayer::GetDebugInfo(std::string &audio, std::string &video, std::string &general)
5139 audio = m_VideoPlayerAudio->GetPlayerInfo();
5140 video = m_VideoPlayerVideo->GetPlayerInfo();
5141 GetGeneralInfo(general);
5144 void CVideoPlayer::UpdateClockSync(bool enabled)
5146 m_processInfo->SetRenderClockSync(enabled);
5149 void CVideoPlayer::UpdateRenderInfo(CRenderInfo &info)
5151 m_processInfo->UpdateRenderInfo(info);
5154 void CVideoPlayer::UpdateRenderBuffers(int queued, int discard, int free)
5156 m_processInfo->UpdateRenderBuffers(queued, discard, free);
5159 void CVideoPlayer::UpdateGuiRender(bool gui)
5161 m_processInfo->SetGuiRender(gui);
5164 void CVideoPlayer::UpdateVideoRender(bool video)
5166 m_processInfo->SetVideoRender(video);
5169 // IDispResource interface
5170 void CVideoPlayer::OnLostDisplay()
5172 CLog::Log(LOGINFO, "VideoPlayer: OnLostDisplay received");
5173 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, true), 1);
5174 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, true), 1);
5175 m_clock.Pause(true);
5176 m_displayLost = true;
5177 FlushRenderer();
5180 void CVideoPlayer::OnResetDisplay()
5182 if (!m_displayLost)
5183 return;
5185 CLog::Log(LOGINFO, "VideoPlayer: OnResetDisplay received");
5186 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, false), 1);
5187 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, false), 1);
5188 m_clock.Pause(false);
5189 m_displayLost = false;
5190 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_DISPLAY_RESET), 1);
5193 void CVideoPlayer::UpdateFileItemStreamDetails(CFileItem& item)
5195 if (!m_UpdateStreamDetails)
5196 return;
5197 m_UpdateStreamDetails = false;
5199 CLog::Log(LOGDEBUG, "CVideoPlayer: updating file item stream details with available streams");
5201 VideoStreamInfo videoInfo;
5202 AudioStreamInfo audioInfo;
5203 SubtitleStreamInfo subtitleInfo;
5204 CVideoInfoTag* info = item.GetVideoInfoTag();
5205 GetVideoStreamInfo(CURRENT_STREAM, videoInfo);
5206 info->m_streamDetails.SetStreams(videoInfo, m_processInfo->GetMaxTime() / 1000, audioInfo,
5207 subtitleInfo);
5209 //grab all the audio and subtitle info and save it
5211 for (int i = 0; i < GetAudioStreamCount(); i++)
5213 GetAudioStreamInfo(i, audioInfo);
5214 info->m_streamDetails.AddStream(new CStreamDetailAudio(audioInfo));
5217 for (int i = 0; i < GetSubtitleCount(); i++)
5219 GetSubtitleStreamInfo(i, subtitleInfo);
5220 info->m_streamDetails.AddStream(new CStreamDetailSubtitle(subtitleInfo));
5224 //------------------------------------------------------------------------------
5225 // content related methods
5226 //------------------------------------------------------------------------------
5228 void CVideoPlayer::UpdateContent()
5230 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5231 m_content.m_selectionStreams = m_SelectionStreams;
5232 m_content.m_programs = m_programs;
5235 void CVideoPlayer::UpdateContentState()
5237 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5239 m_content.m_videoIndex = m_SelectionStreams.TypeIndexOf(STREAM_VIDEO, m_CurrentVideo.source,
5240 m_CurrentVideo.demuxerId, m_CurrentVideo.id);
5241 m_content.m_audioIndex = m_SelectionStreams.TypeIndexOf(STREAM_AUDIO, m_CurrentAudio.source,
5242 m_CurrentAudio.demuxerId, m_CurrentAudio.id);
5243 m_content.m_subtitleIndex = m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE, m_CurrentSubtitle.source,
5244 m_CurrentSubtitle.demuxerId, m_CurrentSubtitle.id);
5246 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) && m_content.m_videoIndex == -1 &&
5247 m_content.m_audioIndex == -1)
5249 std::shared_ptr<CDVDInputStreamNavigator> nav =
5250 std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
5252 m_content.m_videoIndex = m_SelectionStreams.TypeIndexOf(STREAM_VIDEO, STREAM_SOURCE_NAV, -1,
5253 nav->GetActiveAngle());
5254 m_content.m_audioIndex = m_SelectionStreams.TypeIndexOf(STREAM_AUDIO, STREAM_SOURCE_NAV, -1,
5255 nav->GetActiveAudioStream());
5257 // only update the subtitle index in libdvdnav if the subtitle is provided by the dvd itself,
5258 // i.e. for external subtitles the index is always greater than the subtitlecount in dvdnav
5259 if (m_content.m_subtitleIndex < nav->GetSubTitleStreamCount())
5261 m_content.m_subtitleIndex = m_SelectionStreams.TypeIndexOf(
5262 STREAM_SUBTITLE, STREAM_SOURCE_NAV, -1, nav->GetActiveSubtitleStream());
5267 void CVideoPlayer::GetVideoStreamInfo(int streamId, VideoStreamInfo& info) const
5269 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5271 if (streamId == CURRENT_STREAM)
5272 streamId = m_content.m_videoIndex;
5274 if (streamId < 0 || streamId > GetVideoStreamCount() - 1)
5276 info.valid = false;
5277 return;
5280 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_VIDEO, streamId);
5281 if (s.language.length() > 0)
5282 info.language = s.language;
5284 if (s.name.length() > 0)
5285 info.name = s.name;
5287 m_renderManager.GetVideoRect(info.SrcRect, info.DestRect, info.VideoRect);
5289 info.valid = true;
5290 info.bitrate = s.bitrate;
5291 info.width = s.width;
5292 info.height = s.height;
5293 info.codecName = s.codec;
5294 info.videoAspectRatio = s.aspect_ratio;
5295 info.stereoMode = s.stereo_mode;
5296 info.flags = s.flags;
5297 info.hdrType = s.hdrType;
5298 info.fpsRate = s.fpsRate;
5299 info.fpsScale = s.fpsScale;
5302 int CVideoPlayer::GetVideoStreamCount() const
5304 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5305 return m_content.m_selectionStreams.CountType(STREAM_VIDEO);
5308 int CVideoPlayer::GetVideoStream() const
5310 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5311 return m_content.m_videoIndex;
5314 void CVideoPlayer::SetVideoStream(int iStream)
5316 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetVideoStream>(iStream));
5317 m_processInfo->GetVideoSettingsLocked().SetVideoStream(iStream);
5318 SynchronizeDemuxer();
5321 void CVideoPlayer::GetAudioStreamInfo(int index, AudioStreamInfo& info) const
5323 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5325 if (index == CURRENT_STREAM)
5326 index = m_content.m_audioIndex;
5328 if (index < 0 || index > GetAudioStreamCount() - 1)
5330 info.valid = false;
5331 return;
5334 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_AUDIO, index);
5335 info.language = s.language;
5336 info.name = s.name;
5338 if (s.type == STREAM_NONE)
5339 info.name += " (Invalid)";
5341 info.valid = true;
5342 info.bitrate = s.bitrate;
5343 info.channels = s.channels;
5344 info.codecName = s.codec;
5345 info.codecDesc = s.codecDesc;
5346 info.flags = s.flags;
5349 int CVideoPlayer::GetAudioStreamCount() const
5351 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5352 return m_content.m_selectionStreams.CountType(STREAM_AUDIO);
5355 int CVideoPlayer::GetAudioStream()
5357 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5358 return m_content.m_audioIndex;
5361 void CVideoPlayer::SetAudioStream(int iStream)
5363 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetAudioStream>(iStream));
5364 m_processInfo->GetVideoSettingsLocked().SetAudioStream(iStream);
5365 SynchronizeDemuxer();
5368 void CVideoPlayer::GetSubtitleStreamInfo(int index, SubtitleStreamInfo& info) const
5370 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5372 if (index == CURRENT_STREAM)
5373 index = m_content.m_subtitleIndex;
5375 if (index < 0 || index > GetSubtitleCount() - 1)
5377 info.valid = false;
5378 info.language.clear();
5379 info.flags = StreamFlags::FLAG_NONE;
5380 return;
5383 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_SUBTITLE, index);
5384 info.name = s.name;
5386 if (s.type == STREAM_NONE)
5387 info.name += "(Invalid)";
5389 info.language = s.language;
5390 info.codecName = s.codec;
5391 info.flags = s.flags;
5392 info.isExternal = STREAM_SOURCE_MASK(s.source) == STREAM_SOURCE_DEMUX_SUB ||
5393 STREAM_SOURCE_MASK(s.source) == STREAM_SOURCE_TEXT;
5396 void CVideoPlayer::SetSubtitle(int iStream)
5398 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSubtitleStream>(iStream));
5399 m_processInfo->GetVideoSettingsLocked().SetSubtitleStream(iStream);
5402 int CVideoPlayer::GetSubtitleCount() const
5404 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5405 return m_content.m_selectionStreams.CountType(STREAM_SUBTITLE);
5408 int CVideoPlayer::GetSubtitle()
5410 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5411 return m_content.m_subtitleIndex;
5414 int CVideoPlayer::GetPrograms(std::vector<ProgramInfo>& programs)
5416 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5417 programs = m_programs;
5418 return programs.size();
5421 void CVideoPlayer::SetProgram(int progId)
5423 m_messenger.Put(std::make_shared<CDVDMsgInt>(CDVDMsg::PLAYER_SET_PROGRAM, progId));
5426 int CVideoPlayer::GetProgramsCount() const
5428 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5429 return m_programs.size();
5432 void CVideoPlayer::SetUpdateStreamDetails()
5434 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_SET_UPDATE_STREAM_DETAILS));