[video] Retrieve DVD audio stream codec desc/channels/bitrate from ffmpeg rather...
[xbmc.git] / xbmc / cores / VideoPlayer / VideoPlayer.cpp
blob24571345f819d7e8dd0cf95c614d7648ec8c7d23
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 "LangInfo.h"
29 #include "ServiceBroker.h"
30 #include "URL.h"
31 #include "Util.h"
32 #include "VideoPlayerAudio.h"
33 #include "VideoPlayerRadioRDS.h"
34 #include "VideoPlayerVideo.h"
35 #include "application/Application.h"
36 #include "cores/DataCacheCore.h"
37 #include "cores/EdlEdit.h"
38 #include "cores/FFmpeg.h"
39 #include "cores/VideoPlayer/Process/ProcessInfo.h"
40 #include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
41 #include "guilib/GUIComponent.h"
42 #include "guilib/LocalizeStrings.h"
43 #include "guilib/StereoscopicsManager.h"
44 #include "input/actions/Action.h"
45 #include "input/actions/ActionIDs.h"
46 #include "interfaces/AnnouncementManager.h"
47 #include "messaging/ApplicationMessenger.h"
48 #include "network/NetworkFileItemClassify.h"
49 #include "settings/AdvancedSettings.h"
50 #include "settings/Settings.h"
51 #include "settings/SettingsComponent.h"
52 #include "threads/SingleLock.h"
53 #include "utils/FontUtils.h"
54 #include "utils/JobManager.h"
55 #include "utils/LangCodeExpander.h"
56 #include "utils/StreamDetails.h"
57 #include "utils/StreamUtils.h"
58 #include "utils/StringUtils.h"
59 #include "utils/URIUtils.h"
60 #include "utils/Variant.h"
61 #include "utils/log.h"
62 #include "video/Bookmark.h"
63 #include "video/VideoInfoTag.h"
64 #include "windowing/GraphicContext.h"
65 #include "windowing/WinSystem.h"
67 #include <chrono>
68 #include <iterator>
69 #include <memory>
70 #include <mutex>
71 #include <utility>
73 using namespace KODI;
74 using namespace std::chrono_literals;
76 //------------------------------------------------------------------------------
77 // selection streams
78 //------------------------------------------------------------------------------
80 #define PREDICATE_RETURN(lh, rh) \
81 do { \
82 if((lh) != (rh)) \
83 return (lh) > (rh); \
84 } while(0)
86 class PredicateSubtitleFilter
88 private:
89 std::string audiolang;
90 bool original;
91 bool nosub;
92 bool onlyforced;
93 int currentSubStream;
94 public:
95 /** \brief The class' operator() decides if the given (subtitle) SelectionStream is relevant wrt.
96 * preferred subtitle language and audio language. If the subtitle is relevant <B>false</B> false is returned.
98 * A subtitle is relevant if
99 * - it was previously selected, or
100 * - it's an external sub, or
101 * - it's a forced sub and "original stream's language" was selected and audio stream language matches, or
102 * - it's a default and a forced sub (could lead to users seeing forced subs in a foreign language!), or
103 * - its language matches the preferred subtitle's language (unequal to "original stream's language")
105 explicit PredicateSubtitleFilter(const std::string& lang, int subStream)
106 : audiolang(lang),
107 currentSubStream(subStream)
109 const std::string subtitleLang = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE);
110 original = StringUtils::EqualsNoCase(subtitleLang, "original");
111 nosub = StringUtils::EqualsNoCase(subtitleLang, "none");
112 onlyforced = StringUtils::EqualsNoCase(subtitleLang, "forced_only");
115 bool operator()(const SelectionStream& ss) const
117 if (ss.type_index == currentSubStream)
118 return false;
120 if (nosub)
121 return true;
123 if (onlyforced)
125 if ((ss.flags & StreamFlags::FLAG_FORCED) && g_LangCodeExpander.CompareISO639Codes(ss.language, audiolang))
126 return false;
127 else
128 return true;
131 if(STREAM_SOURCE_MASK(ss.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(ss.source) == STREAM_SOURCE_TEXT)
132 return false;
134 if ((ss.flags & StreamFlags::FLAG_FORCED) && g_LangCodeExpander.CompareISO639Codes(ss.language, audiolang))
135 return false;
137 if ((ss.flags & StreamFlags::FLAG_FORCED) && (ss.flags & StreamFlags::FLAG_DEFAULT))
138 return false;
140 if (ss.language == "cc" && ss.flags & StreamFlags::FLAG_HEARING_IMPAIRED)
141 return false;
143 if(!original)
145 std::string subtitle_language = g_langInfo.GetSubtitleLanguage();
146 if (g_LangCodeExpander.CompareISO639Codes(subtitle_language, ss.language))
147 return false;
149 else if (ss.flags & StreamFlags::FLAG_DEFAULT)
150 return false;
152 return true;
156 class PredicateAudioFilter
158 private:
159 int currentAudioStream;
160 bool preferStereo;
161 public:
162 explicit PredicateAudioFilter(int audioStream, bool preferStereo)
163 : currentAudioStream(audioStream)
164 , preferStereo(preferStereo)
167 bool operator()(const SelectionStream& lh, const SelectionStream& rh)
169 PREDICATE_RETURN(lh.type_index == currentAudioStream
170 , rh.type_index == currentAudioStream);
172 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
174 if (!StringUtils::EqualsNoCase(settings->GetString(CSettings::SETTING_LOCALE_AUDIOLANGUAGE), "mediadefault"))
176 if (!StringUtils::EqualsNoCase(settings->GetString(CSettings::SETTING_LOCALE_AUDIOLANGUAGE), "original"))
178 std::string audio_language = g_langInfo.GetAudioLanguage();
179 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(audio_language, lh.language)
180 , g_LangCodeExpander.CompareISO639Codes(audio_language, rh.language));
182 else
184 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_ORIGINAL,
185 rh.flags & StreamFlags::FLAG_ORIGINAL);
188 bool hearingimp = settings->GetBool(CSettings::SETTING_ACCESSIBILITY_AUDIOHEARING);
189 PREDICATE_RETURN(!hearingimp ? !(lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED
190 , !hearingimp ? !(rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED);
192 bool visualimp = settings->GetBool(CSettings::SETTING_ACCESSIBILITY_AUDIOVISUAL);
193 PREDICATE_RETURN(!visualimp ? !(lh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED) : lh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED
194 , !visualimp ? !(rh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED) : rh.flags & StreamFlags::FLAG_VISUAL_IMPAIRED);
197 if (settings->GetBool(CSettings::SETTING_VIDEOPLAYER_PREFERDEFAULTFLAG))
199 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
200 rh.flags & StreamFlags::FLAG_DEFAULT);
203 if (preferStereo)
204 PREDICATE_RETURN(lh.channels == 2,
205 rh.channels == 2);
206 else
207 PREDICATE_RETURN(lh.channels,
208 rh.channels);
210 PREDICATE_RETURN(StreamUtils::GetCodecPriority(lh.codec),
211 StreamUtils::GetCodecPriority(rh.codec));
213 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
214 rh.flags & StreamFlags::FLAG_DEFAULT);
215 return false;
219 /** \brief The class' operator() decides if the given (subtitle) SelectionStream lh is 'better than' the given (subtitle) SelectionStream rh.
220 * If lh is 'better than' rh the return value is true, false otherwise.
222 * A subtitle lh is 'better than' a subtitle rh (in evaluation order) if
223 * - lh was previously selected, or
224 * - lh is an external sub and rh not, or
225 * - 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
226 * - 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
227 * - 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
228 * - lh is a forced sub and a default sub ("original stream's language" was selected or subtitles are off)
229 * - lh is an external sub and its language matches the preferred subtitle's language (unequal to "original stream's language") and rh not, or
230 * - lh is language matches the preferred subtitle's language (unequal to "original stream's language") and rh not, or
231 * - lh is a default sub and rh not
233 class PredicateSubtitlePriority
235 private:
236 std::string audiolang;
237 bool original;
238 bool subson;
239 PredicateSubtitleFilter filter;
240 int subStream;
241 public:
242 explicit PredicateSubtitlePriority(const std::string& lang, int stream, bool ison)
243 : audiolang(lang),
244 original(StringUtils::EqualsNoCase(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE), "original")),
245 subson(ison),
246 filter(lang, stream),
247 subStream(stream)
251 bool relevant(const SelectionStream& ss) const
253 return !filter(ss);
256 bool operator()(const SelectionStream& lh, const SelectionStream& rh) const
258 PREDICATE_RETURN(relevant(lh)
259 , relevant(rh));
261 PREDICATE_RETURN(lh.type_index == subStream
262 , rh.type_index == subStream);
264 // prefer external subs
265 PREDICATE_RETURN(STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(lh.source) == STREAM_SOURCE_TEXT
266 , STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT);
268 if (!subson || original)
270 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
271 , rh.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
273 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT && g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
274 , rh.flags & StreamFlags::FLAG_DEFAULT && g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
276 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(lh.language, audiolang)
277 , g_LangCodeExpander.CompareISO639Codes(rh.language, audiolang));
279 PREDICATE_RETURN((lh.flags & (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)) == (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)
280 , (rh.flags & (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT)) == (StreamFlags::FLAG_FORCED | StreamFlags::FLAG_DEFAULT));
284 std::string subtitle_language = g_langInfo.GetSubtitleLanguage();
285 if (!original)
287 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)
288 , (STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_DEMUX_SUB || STREAM_SOURCE_MASK(rh.source) == STREAM_SOURCE_TEXT) && g_LangCodeExpander.CompareISO639Codes(subtitle_language, rh.language));
291 if (!original)
293 PREDICATE_RETURN(g_LangCodeExpander.CompareISO639Codes(subtitle_language, lh.language)
294 , g_LangCodeExpander.CompareISO639Codes(subtitle_language, rh.language));
296 bool hearingimp = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_ACCESSIBILITY_SUBHEARING);
297 PREDICATE_RETURN(!hearingimp ? !(lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : lh.flags & StreamFlags::FLAG_HEARING_IMPAIRED
298 , !hearingimp ? !(rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED) : rh.flags & StreamFlags::FLAG_HEARING_IMPAIRED);
301 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT
302 , rh.flags & StreamFlags::FLAG_DEFAULT);
304 return false;
308 class PredicateVideoFilter
310 private:
311 int currentVideoStream;
312 public:
313 explicit PredicateVideoFilter(int videoStream) : currentVideoStream(videoStream)
316 bool operator()(const SelectionStream& lh, const SelectionStream& rh)
318 PREDICATE_RETURN(lh.type_index == currentVideoStream,
319 rh.type_index == currentVideoStream);
321 PREDICATE_RETURN(lh.flags & StreamFlags::FLAG_DEFAULT,
322 rh.flags & StreamFlags::FLAG_DEFAULT);
323 return false;
327 void CSelectionStreams::Clear(StreamType type, StreamSource source)
329 auto new_end = std::remove_if(m_Streams.begin(), m_Streams.end(),
330 [type, source](const SelectionStream &stream)
332 return (type == STREAM_NONE || stream.type == type) &&
333 (source == 0 || stream.source == source);
335 m_Streams.erase(new_end, m_Streams.end());
338 SelectionStream& CSelectionStreams::Get(StreamType type, int index)
340 return const_cast<SelectionStream&>(std::as_const(*this).Get(type, index));
343 const SelectionStream& CSelectionStreams::Get(StreamType type, int index) const
345 int count = -1;
346 for (size_t i = 0; i < m_Streams.size(); ++i)
348 if (m_Streams[i].type != type)
349 continue;
350 count++;
351 if (count == index)
352 return m_Streams[i];
354 return m_invalid;
357 std::vector<SelectionStream> CSelectionStreams::Get(StreamType type)
359 std::vector<SelectionStream> streams;
360 std::copy_if(m_Streams.begin(), m_Streams.end(), std::back_inserter(streams),
361 [type](const SelectionStream &stream)
363 return stream.type == type;
365 return streams;
368 bool CSelectionStreams::Get(StreamType type, StreamFlags flag, SelectionStream& out)
370 for(size_t i=0;i<m_Streams.size();i++)
372 if(m_Streams[i].type != type)
373 continue;
374 if((m_Streams[i].flags & flag) != flag)
375 continue;
376 out = m_Streams[i];
377 return true;
379 return false;
382 int CSelectionStreams::TypeIndexOf(StreamType type, int source, int64_t demuxerId, int id) const
384 if (id < 0)
385 return -1;
387 auto it = std::find_if(m_Streams.begin(), m_Streams.end(),
388 [&](const SelectionStream& stream) {return stream.type == type
389 && stream.source == source && stream.id == id
390 && stream.demuxerId == demuxerId;});
392 if (it != m_Streams.end())
393 return it->type_index;
394 else
395 return -1;
398 int CSelectionStreams::Source(StreamSource source, const std::string& filename)
400 int index = source - 1;
401 for (size_t i=0; i<m_Streams.size(); i++)
403 SelectionStream &s = m_Streams[i];
404 if (STREAM_SOURCE_MASK(s.source) != source)
405 continue;
406 // if it already exists, return same
407 if (s.filename == filename)
408 return s.source;
409 if (index < s.source)
410 index = s.source;
412 // return next index
413 return index + 1;
416 void CSelectionStreams::Update(SelectionStream& s)
418 int index = TypeIndexOf(s.type, s.source, s.demuxerId, s.id);
419 if(index >= 0)
421 SelectionStream& o = Get(s.type, index);
422 s.type_index = o.type_index;
423 o = s;
425 else
427 s.type_index = CountType(s.type);
428 m_Streams.push_back(s);
432 void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input,
433 CDVDDemux* demuxer,
434 const std::string& filename2)
436 if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
438 std::shared_ptr<CDVDInputStreamNavigator> nav = std::static_pointer_cast<CDVDInputStreamNavigator>(input);
439 std::string filename = nav->GetFileName();
440 int source = Source(STREAM_SOURCE_NAV, filename);
442 std::vector<CDemuxStream*> demuxStreams;
443 if (demuxer)
444 demuxStreams = demuxer->GetStreams();
446 int count;
447 count = nav->GetAudioStreamCount();
448 for(int i=0;i<count;i++)
450 const auto stream =
451 std::find_if(demuxStreams.begin(), demuxStreams.end(),
452 [i](const auto& stream)
453 { return stream->type == STREAM_AUDIO && stream->dvdNavId == i; });
454 CDemuxStreamAudio* aStream =
455 (stream != demuxStreams.end()) ? static_cast<CDemuxStreamAudio*>(*stream) : nullptr;
457 SelectionStream s;
458 s.source = source;
459 s.type = STREAM_AUDIO;
460 s.id = i;
461 s.flags = StreamFlags::FLAG_NONE;
462 s.filename = filename;
464 AudioStreamInfo info = nav->GetAudioStreamInfo(i);
465 s.name = info.name;
466 s.codec = info.codecName;
467 // additional/more reliable info from ffmpeg than IFO nav data
468 if (aStream)
470 s.codecDesc = aStream->GetStreamType();
471 s.channels = aStream->iChannels;
472 s.bitrate = aStream->iBitRate;
474 else
476 s.codecDesc = info.codecDesc;
477 s.channels = info.channels;
479 s.language = g_LangCodeExpander.ConvertToISO6392B(info.language);
480 s.flags = info.flags;
481 Update(s);
484 count = nav->GetSubTitleStreamCount();
485 for(int i=0;i<count;i++)
487 SelectionStream s;
488 s.source = source;
489 s.type = STREAM_SUBTITLE;
490 s.id = i;
491 s.filename = filename;
492 s.channels = 0;
494 SubtitleStreamInfo info = nav->GetSubtitleStreamInfo(i);
495 s.name = info.name;
496 s.codec = info.codecName;
497 s.flags = info.flags;
498 s.language = g_LangCodeExpander.ConvertToISO6392B(info.language);
499 Update(s);
502 VideoStreamInfo info = nav->GetVideoStreamInfo();
503 for (int i = 1; i <= info.angles; i++)
505 SelectionStream s;
506 s.source = source;
507 s.type = STREAM_VIDEO;
508 s.id = i;
509 s.flags = StreamFlags::FLAG_NONE;
510 s.filename = filename;
511 s.channels = 0;
512 s.aspect_ratio = info.videoAspectRatio;
513 s.width = info.width;
514 s.height = info.height;
515 s.codec = info.codecName;
516 s.name = StringUtils::Format("{} {}", g_localizeStrings.Get(38032), i);
517 Update(s);
520 else if(demuxer)
522 std::string filename = demuxer->GetFileName();
523 int source;
524 if(input) /* hack to know this is sub decoder */
525 source = Source(STREAM_SOURCE_DEMUX, filename);
526 else if (!filename2.empty())
527 source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
528 else
529 source = Source(STREAM_SOURCE_VIDEOMUX, filename);
531 for (auto stream : demuxer->GetStreams())
533 /* skip streams with no type */
534 if (stream->type == STREAM_NONE)
535 continue;
536 /* make sure stream is marked with right source */
537 stream->source = source;
539 SelectionStream s;
540 s.source = source;
541 s.type = stream->type;
542 s.id = stream->uniqueId;
543 s.demuxerId = stream->demuxerId;
544 s.language = g_LangCodeExpander.ConvertToISO6392B(stream->language);
545 s.flags = stream->flags;
546 s.filename = demuxer->GetFileName();
547 s.filename2 = filename2;
548 s.name = stream->GetStreamName();
549 s.codec = demuxer->GetStreamCodecName(stream->demuxerId, stream->uniqueId);
550 s.channels = 0; // Default to 0. Overwrite if STREAM_AUDIO below.
551 if(stream->type == STREAM_VIDEO)
553 CDemuxStreamVideo* vstream = static_cast<CDemuxStreamVideo*>(stream);
554 s.width = vstream->iWidth;
555 s.height = vstream->iHeight;
556 s.aspect_ratio = vstream->fAspect;
557 s.stereo_mode = vstream->stereo_mode;
558 s.bitrate = vstream->iBitRate;
559 s.hdrType = vstream->hdr_type;
560 s.fpsRate = static_cast<uint32_t>(vstream->iFpsRate);
561 s.fpsScale = static_cast<uint32_t>(vstream->iFpsScale);
563 if(stream->type == STREAM_AUDIO)
565 s.codecDesc = static_cast<CDemuxStreamAudio*>(stream)->GetStreamType();
566 s.channels = static_cast<CDemuxStreamAudio*>(stream)->iChannels;
567 s.bitrate = static_cast<CDemuxStreamAudio*>(stream)->iBitRate;
569 Update(s);
572 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
573 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
574 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
577 void CSelectionStreams::Update(const std::shared_ptr<CDVDInputStream>& input, CDVDDemux* demuxer)
579 Update(input, demuxer, "");
582 int CSelectionStreams::CountTypeOfSource(StreamType type, StreamSource source) const
584 return std::count_if(m_Streams.begin(), m_Streams.end(),
585 [&](const SelectionStream& stream) {return (stream.type == type) && (stream.source == source);});
588 int CSelectionStreams::CountType(StreamType type) const
590 return std::count_if(m_Streams.begin(), m_Streams.end(),
591 [&](const SelectionStream& stream) { return stream.type == type; });
594 //------------------------------------------------------------------------------
595 // main class
596 //------------------------------------------------------------------------------
598 void CVideoPlayer::CreatePlayers()
600 if (m_players_created)
601 return;
603 m_VideoPlayerVideo =
604 new CVideoPlayerVideo(&m_clock, &m_overlayContainer, m_messenger, m_renderManager,
605 *m_processInfo, m_messageQueueTimeSize);
606 m_VideoPlayerAudio =
607 new CVideoPlayerAudio(&m_clock, m_messenger, *m_processInfo, m_messageQueueTimeSize);
608 m_VideoPlayerSubtitle = new CVideoPlayerSubtitle(&m_overlayContainer, *m_processInfo);
609 m_VideoPlayerTeletext = new CDVDTeletextData(*m_processInfo);
610 m_VideoPlayerRadioRDS = new CDVDRadioRDSData(*m_processInfo);
611 m_VideoPlayerAudioID3 = std::make_unique<CVideoPlayerAudioID3>(*m_processInfo);
612 m_players_created = true;
615 void CVideoPlayer::DestroyPlayers()
617 if (!m_players_created)
618 return;
620 delete m_VideoPlayerVideo;
621 delete m_VideoPlayerAudio;
622 delete m_VideoPlayerSubtitle;
623 delete m_VideoPlayerTeletext;
624 delete m_VideoPlayerRadioRDS;
625 m_VideoPlayerAudioID3.reset();
627 m_players_created = false;
630 CVideoPlayer::CVideoPlayer(IPlayerCallback& callback)
631 : IPlayer(callback),
632 CThread("VideoPlayer"),
633 m_CurrentAudio(STREAM_AUDIO, VideoPlayer_AUDIO),
634 m_CurrentVideo(STREAM_VIDEO, VideoPlayer_VIDEO),
635 m_CurrentSubtitle(STREAM_SUBTITLE, VideoPlayer_SUBTITLE),
636 m_CurrentTeletext(STREAM_TELETEXT, VideoPlayer_TELETEXT),
637 m_CurrentRadioRDS(STREAM_RADIO_RDS, VideoPlayer_RDS),
638 m_CurrentAudioID3(STREAM_AUDIO_ID3, VideoPlayer_ID3),
639 m_messenger("player"),
640 m_outboundEvents(std::make_unique<CJobQueue>(false, 1, CJob::PRIORITY_NORMAL)),
641 m_pInputStream(nullptr),
642 m_pDemuxer(nullptr),
643 m_pSubtitleDemuxer(nullptr),
644 m_pCCDemuxer(nullptr),
645 m_renderManager(m_clock, this)
647 m_players_created = false;
649 m_dvd.Clear();
650 m_State.Clear();
652 m_bAbortRequest = false;
653 m_offset_pts = 0.0;
654 m_playSpeed = DVD_PLAYSPEED_NORMAL;
655 m_streamPlayerSpeed = DVD_PLAYSPEED_NORMAL;
656 m_caching = CACHESTATE_DONE;
657 m_HasVideo = false;
658 m_HasAudio = false;
659 m_UpdateStreamDetails = false;
661 const int tenthsSeconds = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
662 CSettings::SETTING_VIDEOPLAYER_QUEUETIMESIZE);
664 m_messageQueueTimeSize = static_cast<double>(tenthsSeconds) / 10.0;
666 m_SkipCommercials = true;
668 m_processInfo.reset(CProcessInfo::CreateInstance());
669 // if we have a gui, register the cache
670 m_processInfo->SetDataCache(&CServiceBroker::GetDataCacheCore());
671 m_processInfo->SetSpeed(1.0);
672 m_processInfo->SetTempo(1.0);
673 m_processInfo->SetFrameAdvance(false);
675 CreatePlayers();
677 m_displayLost = false;
678 m_error = false;
679 m_bCloseRequest = false;
680 CServiceBroker::GetWinSystem()->Register(this);
683 CVideoPlayer::~CVideoPlayer()
685 CServiceBroker::GetWinSystem()->Unregister(this);
687 CloseFile();
688 DestroyPlayers();
690 while (m_outboundEvents->IsProcessing())
692 CThread::Sleep(10ms);
696 bool CVideoPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
698 CLog::Log(LOGINFO, "VideoPlayer::OpenFile: {}", CURL::GetRedacted(file.GetPath()));
700 if (IsRunning())
702 CDVDMsgOpenFile::FileParams params;
703 params.m_item = file;
704 params.m_options = options;
705 params.m_item.SetMimeTypeForInternetFile();
706 m_messenger.Put(std::make_shared<CDVDMsgOpenFile>(params), 1);
708 return true;
711 m_item = file;
712 m_playerOptions = options;
714 m_processInfo->SetPlayTimes(0,0,0,0);
715 m_bAbortRequest = false;
716 m_error = false;
717 m_bCloseRequest = false;
718 m_renderManager.PreInit();
720 Create();
721 m_messenger.Init();
723 m_callback.OnPlayBackStarted(m_item);
725 return true;
728 bool CVideoPlayer::CloseFile(bool reopen)
730 CLog::Log(LOGINFO, "CVideoPlayer::CloseFile()");
732 // set the abort request so that other threads can finish up
733 m_bAbortRequest = true;
734 m_bCloseRequest = true;
736 // tell demuxer to abort
737 if(m_pDemuxer)
738 m_pDemuxer->Abort();
740 if(m_pSubtitleDemuxer)
741 m_pSubtitleDemuxer->Abort();
743 if(m_pInputStream)
744 m_pInputStream->Abort();
746 m_renderManager.UnInit();
748 CLog::Log(LOGINFO, "VideoPlayer: waiting for threads to exit");
750 // wait for the main thread to finish up
751 // since this main thread cleans up all other resources and threads
752 // we are done after the StopThread call
754 CSingleExit exitlock(CServiceBroker::GetWinSystem()->GetGfxContext());
755 StopThread();
758 m_Edl.Clear();
759 CServiceBroker::GetDataCacheCore().Reset();
761 m_HasVideo = false;
762 m_HasAudio = false;
764 CLog::Log(LOGINFO, "VideoPlayer: finished waiting");
765 return true;
768 bool CVideoPlayer::IsPlaying() const
770 return !m_bStop;
773 void CVideoPlayer::OnStartup()
775 m_CurrentVideo.Clear();
776 m_CurrentAudio.Clear();
777 m_CurrentSubtitle.Clear();
778 m_CurrentTeletext.Clear();
779 m_CurrentRadioRDS.Clear();
780 m_CurrentAudioID3.Clear();
782 UTILS::FONT::ClearTemporaryFonts();
785 bool CVideoPlayer::OpenInputStream()
787 if (m_pInputStream.use_count() > 1)
788 throw std::runtime_error("m_pInputStream reference count is greater than 1");
789 m_pInputStream.reset();
791 CLog::Log(LOGINFO, "Creating InputStream");
793 m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_item, true);
794 if (m_pInputStream == nullptr)
796 CLog::Log(LOGERROR, "CVideoPlayer::OpenInputStream - unable to create input stream for [{}]",
797 CURL::GetRedacted(m_item.GetPath()));
798 return false;
801 if (!m_pInputStream->Open())
803 CLog::Log(LOGERROR, "CVideoPlayer::OpenInputStream - error opening [{}]",
804 CURL::GetRedacted(m_item.GetPath()));
805 return false;
808 // find any available external subtitles for non dvd files
809 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) &&
810 !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
812 // find any available external subtitles
813 std::vector<std::string> filenames;
815 if (!URIUtils::IsUPnP(m_item.GetPath()) &&
816 !m_item.GetProperty("no-ext-subs-scan").asBoolean(false))
817 CUtil::ScanForExternalSubtitles(m_item.GetDynPath(), filenames);
819 // load any subtitles from file item
820 std::string key("subtitle:1");
821 for (unsigned s = 1; m_item.HasProperty(key); key = StringUtils::Format("subtitle:{}", ++s))
822 filenames.push_back(m_item.GetProperty(key).asString());
824 for (unsigned int i=0;i<filenames.size();i++)
826 // if vobsub subtitle:
827 if (URIUtils::HasExtension(filenames[i], ".idx"))
829 std::string strSubFile;
830 if (CUtil::FindVobSubPair( filenames, filenames[i], strSubFile))
831 AddSubtitleFile(filenames[i], strSubFile);
833 else
835 if (!CUtil::IsVobSub(filenames, filenames[i] ))
837 AddSubtitleFile(filenames[i]);
840 } // end loop over all subtitle files
843 m_clock.Reset();
844 m_dvd.Clear();
846 return true;
849 bool CVideoPlayer::OpenDemuxStream()
851 CloseDemuxer();
853 CLog::Log(LOGINFO, "Creating Demuxer");
855 int attempts = 10;
856 while (!m_bStop && attempts-- > 0)
858 m_pDemuxer.reset(CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream));
859 if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
861 continue;
863 else if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE)
865 CLog::Log(LOGDEBUG, "{} - New stream available from input, retry open", __FUNCTION__);
866 continue;
868 break;
871 if (!m_pDemuxer)
873 CLog::Log(LOGERROR, "{} - Error creating demuxer", __FUNCTION__);
874 return false;
877 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
878 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
879 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
880 m_pDemuxer->GetPrograms(m_programs);
881 UpdateContent();
882 m_demuxerSpeed = DVD_PLAYSPEED_NORMAL;
883 m_processInfo->SetStateRealtime(false);
885 int64_t len = m_pInputStream->GetLength();
886 int64_t tim = m_pDemuxer->GetStreamLength();
887 if (len > 0 && tim > 0)
888 m_pInputStream->SetReadRate(static_cast<uint32_t>(len * 1000 / tim));
890 m_offset_pts = 0;
892 return true;
895 void CVideoPlayer::CloseDemuxer()
897 m_pDemuxer.reset();
898 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
900 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
901 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
902 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
905 void CVideoPlayer::OpenDefaultStreams(bool reset)
907 // if input stream dictate, we will open later
908 if (m_dvd.iSelectedAudioStream >= 0 ||
909 m_dvd.iSelectedSPUStream >= 0)
910 return;
912 bool valid;
914 // open video stream
915 valid = false;
917 PredicateVideoFilter vf(m_processInfo->GetVideoSettings().m_VideoStream);
918 for (const auto &stream : m_SelectionStreams.Get(STREAM_VIDEO, vf))
920 if (OpenStream(m_CurrentVideo, stream.demuxerId, stream.id, stream.source, reset))
922 valid = true;
923 break;
926 if (!valid)
928 CloseStream(m_CurrentVideo, true);
929 m_processInfo->ResetVideoCodecInfo();
932 // open audio stream
933 valid = false;
934 if (!m_playerOptions.videoOnly)
936 PredicateAudioFilter af(m_processInfo->GetVideoSettings().m_AudioStream, m_playerOptions.preferStereo);
937 for (const auto &stream : m_SelectionStreams.Get(STREAM_AUDIO, af))
939 if(OpenStream(m_CurrentAudio, stream.demuxerId, stream.id, stream.source, reset))
941 valid = true;
942 break;
947 if(!valid)
949 CloseStream(m_CurrentAudio, true);
950 m_processInfo->ResetAudioCodecInfo();
953 // enable or disable subtitles
954 bool visible = m_processInfo->GetVideoSettings().m_SubtitleOn;
956 // open subtitle stream
957 SelectionStream as = m_SelectionStreams.Get(STREAM_AUDIO, GetAudioStream());
958 PredicateSubtitlePriority psp(as.language,
959 m_processInfo->GetVideoSettings().m_SubtitleStream,
960 m_processInfo->GetVideoSettings().m_SubtitleOn);
961 valid = false;
962 // We need to close CC subtitles to avoid conflicts with external sub stream
963 if (m_CurrentSubtitle.source == STREAM_SOURCE_VIDEOMUX)
964 CloseStream(m_CurrentSubtitle, false);
966 for (const auto &stream : m_SelectionStreams.Get(STREAM_SUBTITLE, psp))
968 if (OpenStream(m_CurrentSubtitle, stream.demuxerId, stream.id, stream.source))
970 valid = true;
971 if(!psp.relevant(stream))
972 visible = false;
973 else if(stream.flags & StreamFlags::FLAG_FORCED)
974 visible = true;
975 break;
978 if(!valid)
979 CloseStream(m_CurrentSubtitle, false);
981 // only set subtitle visibility if state not stored by dvd navigator, because navigator will restore it (if visible)
982 if (!std::dynamic_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream) ||
983 m_playerOptions.state.empty())
985 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
986 if (valid && !visible)
987 SetEnableStream(m_CurrentSubtitle, false);
989 SetSubtitleVisibleInternal(visible);
992 // open teletext stream
993 valid = false;
994 for (const auto &stream : m_SelectionStreams.Get(STREAM_TELETEXT))
996 if (OpenStream(m_CurrentTeletext, stream.demuxerId, stream.id, stream.source))
998 valid = true;
999 break;
1002 if(!valid)
1003 CloseStream(m_CurrentTeletext, false);
1005 // open RDS stream
1006 valid = false;
1007 for (const auto &stream : m_SelectionStreams.Get(STREAM_RADIO_RDS))
1009 if (OpenStream(m_CurrentRadioRDS, stream.demuxerId, stream.id, stream.source))
1011 valid = true;
1012 break;
1015 if(!valid)
1016 CloseStream(m_CurrentRadioRDS, false);
1018 // open ID3 stream
1019 valid = false;
1020 for (const auto& stream : m_SelectionStreams.Get(STREAM_AUDIO_ID3))
1022 if (OpenStream(m_CurrentAudioID3, stream.demuxerId, stream.id, stream.source))
1024 valid = true;
1025 break;
1028 if (!valid)
1029 CloseStream(m_CurrentAudioID3, false);
1031 // disable demux streams
1032 if (NETWORK::IsRemote(m_item) && m_pDemuxer)
1034 for (auto &stream : m_SelectionStreams.m_Streams)
1036 if (STREAM_SOURCE_MASK(stream.source) == STREAM_SOURCE_DEMUX)
1038 if (stream.id != m_CurrentVideo.id && stream.id != m_CurrentAudio.id &&
1039 stream.id != m_CurrentSubtitle.id && stream.id != m_CurrentTeletext.id &&
1040 stream.id != m_CurrentRadioRDS.id && stream.id != m_CurrentAudioID3.id)
1042 m_pDemuxer->EnableStream(stream.demuxerId, stream.id, false);
1049 bool CVideoPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
1052 // check if we should read from subtitle demuxer
1053 if (m_pSubtitleDemuxer && m_VideoPlayerSubtitle->AcceptsData())
1055 packet = m_pSubtitleDemuxer->Read();
1057 if(packet)
1059 UpdateCorrection(packet, m_offset_pts);
1060 if(packet->iStreamId < 0)
1061 return true;
1063 stream = m_pSubtitleDemuxer->GetStream(packet->demuxerId, packet->iStreamId);
1064 if (!stream)
1066 CLog::Log(LOGERROR, "{} - Error demux packet doesn't belong to a valid stream",
1067 __FUNCTION__);
1068 return false;
1070 if (stream->source == STREAM_SOURCE_NONE)
1072 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
1073 m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer.get());
1074 UpdateContent();
1076 return true;
1080 // read a data frame from stream.
1081 if (m_pDemuxer)
1082 packet = m_pDemuxer->Read();
1084 if (packet)
1086 // stream changed, update and open defaults
1087 if (packet->iStreamId == DMX_SPECIALID_STREAMCHANGE)
1089 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1090 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1091 m_pDemuxer->GetPrograms(m_programs);
1092 UpdateContent();
1093 OpenDefaultStreams(false);
1095 // reevaluate HasVideo/Audio, we may have switched from/to a radio channel
1096 if(m_CurrentVideo.id < 0)
1097 m_HasVideo = false;
1098 if(m_CurrentAudio.id < 0)
1099 m_HasAudio = false;
1101 return true;
1104 UpdateCorrection(packet, m_offset_pts);
1106 if(packet->iStreamId < 0)
1107 return true;
1109 if(m_pDemuxer)
1111 stream = m_pDemuxer->GetStream(packet->demuxerId, packet->iStreamId);
1112 if (!stream)
1114 CLog::Log(LOGERROR, "{} - Error demux packet doesn't belong to a valid stream",
1115 __FUNCTION__);
1116 return false;
1118 if(stream->source == STREAM_SOURCE_NONE)
1120 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1121 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1122 UpdateContent();
1125 return true;
1127 return false;
1130 bool CVideoPlayer::IsValidStream(const CCurrentStream& stream)
1132 if(stream.id<0)
1133 return true; // we consider non selected as valid
1135 int source = STREAM_SOURCE_MASK(stream.source);
1136 if(source == STREAM_SOURCE_TEXT)
1137 return true;
1138 if (source == STREAM_SOURCE_DEMUX_SUB)
1140 CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.demuxerId, stream.id);
1141 if(st == NULL || st->disabled)
1142 return false;
1143 if(st->type != stream.type)
1144 return false;
1145 return true;
1147 if (source == STREAM_SOURCE_DEMUX)
1149 CDemuxStream* st = m_pDemuxer->GetStream(stream.demuxerId, stream.id);
1150 if(st == NULL || st->disabled)
1151 return false;
1152 if(st->type != stream.type)
1153 return false;
1155 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1157 if (stream.type == STREAM_AUDIO && st->dvdNavId != m_dvd.iSelectedAudioStream)
1158 return false;
1159 if(stream.type == STREAM_SUBTITLE && st->dvdNavId != m_dvd.iSelectedSPUStream)
1160 return false;
1163 return true;
1165 if (source == STREAM_SOURCE_VIDEOMUX)
1167 CDemuxStream* st = m_pCCDemuxer->GetStream(stream.id);
1168 if (st == NULL || st->disabled)
1169 return false;
1170 if (st->type != stream.type)
1171 return false;
1172 return true;
1175 return false;
1178 bool CVideoPlayer::IsBetterStream(const CCurrentStream& current, CDemuxStream* stream)
1180 // Do not reopen non-video streams if we're in video-only mode
1181 if (m_playerOptions.videoOnly && current.type != STREAM_VIDEO)
1182 return false;
1184 if(stream->disabled)
1185 return false;
1187 if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) ||
1188 m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY)))
1190 int source_type;
1192 source_type = STREAM_SOURCE_MASK(current.source);
1193 if (source_type != STREAM_SOURCE_DEMUX &&
1194 source_type != STREAM_SOURCE_NONE)
1195 return false;
1197 source_type = STREAM_SOURCE_MASK(stream->source);
1198 if(source_type != STREAM_SOURCE_DEMUX ||
1199 stream->type != current.type ||
1200 stream->uniqueId == current.id)
1201 return false;
1203 if(current.type == STREAM_AUDIO && stream->dvdNavId == m_dvd.iSelectedAudioStream)
1204 return true;
1205 if(current.type == STREAM_SUBTITLE && stream->dvdNavId == m_dvd.iSelectedSPUStream)
1206 return true;
1207 if(current.type == STREAM_VIDEO && current.id < 0)
1208 return true;
1210 else
1212 if(stream->source == current.source &&
1213 stream->uniqueId == current.id &&
1214 stream->demuxerId == current.demuxerId)
1215 return false;
1217 if(stream->type != current.type)
1218 return false;
1220 if(current.type == STREAM_SUBTITLE)
1221 return false;
1223 if(current.id < 0)
1224 return true;
1226 return false;
1229 void CVideoPlayer::CheckBetterStream(CCurrentStream& current, CDemuxStream* stream)
1231 IDVDStreamPlayer* player = GetStreamPlayer(current.player);
1232 if (!IsValidStream(current) && (player == NULL || player->IsStalled()))
1233 CloseStream(current, true);
1235 if (IsBetterStream(current, stream))
1236 OpenStream(current, stream->demuxerId, stream->uniqueId, stream->source);
1239 void CVideoPlayer::Prepare()
1241 CFFmpegLog::SetLogLevel(1);
1242 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1243 m_processInfo->SetSpeed(1.0);
1244 m_processInfo->SetTempo(1.0);
1245 m_processInfo->SetFrameAdvance(false);
1246 m_State.Clear();
1247 m_CurrentVideo.hint.Clear();
1248 m_CurrentAudio.hint.Clear();
1249 m_CurrentSubtitle.hint.Clear();
1250 m_CurrentTeletext.hint.Clear();
1251 m_CurrentRadioRDS.hint.Clear();
1252 m_CurrentAudioID3.hint.Clear();
1253 m_SpeedState.Reset(DVD_NOPTS_VALUE);
1254 m_offset_pts = 0;
1255 m_CurrentAudio.lastdts = DVD_NOPTS_VALUE;
1256 m_CurrentVideo.lastdts = DVD_NOPTS_VALUE;
1258 IPlayerCallback *cb = &m_callback;
1259 CFileItem fileItem = m_item;
1260 m_outboundEvents->Submit([=]() {
1261 cb->RequestVideoSettings(fileItem);
1264 if (!OpenInputStream())
1266 m_bAbortRequest = true;
1267 m_error = true;
1268 return;
1271 bool discStateRestored = false;
1272 if (std::shared_ptr<CDVDInputStream::IMenus> ptr = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1274 CLog::Log(LOGINFO, "VideoPlayer: playing a file with menu's");
1276 if (!m_playerOptions.state.empty())
1278 discStateRestored = ptr->SetState(m_playerOptions.state);
1280 else if(std::shared_ptr<CDVDInputStreamNavigator> nav = std::dynamic_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream))
1282 nav->EnableSubtitleStream(m_processInfo->GetVideoSettings().m_SubtitleOn);
1286 if (!OpenDemuxStream())
1288 m_bAbortRequest = true;
1289 m_error = true;
1290 return;
1292 // give players a chance to reconsider now codecs are known
1293 CreatePlayers();
1295 if (!discStateRestored)
1296 OpenDefaultStreams();
1299 * Check to see if the demuxer should start at something other than time 0. This will be the case
1300 * if there was a start time specified as part of the "Start from where last stopped" (aka
1301 * auto-resume) feature or if there is an EDL cut or commercial break that starts at time 0.
1303 std::chrono::milliseconds starttime = 0ms;
1304 if (m_playerOptions.starttime > 0 || m_playerOptions.startpercent > 0)
1306 if (m_playerOptions.startpercent > 0 && m_pDemuxer)
1308 std::chrono::milliseconds playerStartTime =
1309 std::chrono::milliseconds(static_cast<int>((static_cast<double>(
1310 m_pDemuxer->GetStreamLength() * (m_playerOptions.startpercent / 100.0)))));
1311 starttime = m_Edl.GetTimeAfterRestoringCuts(playerStartTime);
1313 else
1315 starttime =
1316 m_Edl.GetTimeAfterRestoringCuts(std::chrono::duration_cast<std::chrono::milliseconds>(
1317 std::chrono::seconds(static_cast<int>(m_playerOptions.starttime))));
1319 CLog::Log(LOGDEBUG, "{} - Start position set to last stopped position: {}", __FUNCTION__,
1320 starttime.count());
1322 else
1324 const auto hasEdit = m_Edl.InEdit(starttime);
1325 if (hasEdit)
1327 const auto& edit = hasEdit.value();
1328 // save last edit times
1329 m_Edl.SetLastEditTime(edit->start);
1330 m_Edl.SetLastEditActionType(edit->action);
1332 if (edit->action == EDL::Action::CUT)
1334 starttime = edit->end;
1335 CLog::Log(LOGDEBUG, "{} - Start position set to end of first cut: {}", __FUNCTION__,
1336 starttime.count());
1338 else if (edit->action == EDL::Action::COMM_BREAK)
1340 if (m_SkipCommercials)
1342 starttime = edit->end;
1343 CLog::Log(LOGDEBUG, "{} - Start position set to end of first commercial break: {}",
1344 __FUNCTION__, starttime.count());
1347 CVariant announcement(
1348 StringUtils::SecondsToTimeString(edit->end.count(), TIME_FORMAT_MM_SS));
1349 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnCommercial",
1350 announcement);
1355 if (starttime > 0ms)
1357 double startpts = DVD_NOPTS_VALUE;
1358 if (m_pDemuxer)
1360 if (m_pDemuxer->SeekTime(starttime.count(), true, &startpts))
1362 FlushBuffers(starttime.count() / 1000 * AV_TIME_BASE, true, true);
1363 CLog::Log(LOGDEBUG, "{} - starting demuxer from: {}", __FUNCTION__, starttime.count());
1365 else
1366 CLog::Log(LOGDEBUG, "{} - failed to start demuxing from: {}", __FUNCTION__,
1367 starttime.count());
1370 if (m_pSubtitleDemuxer)
1372 if (m_pSubtitleDemuxer->SeekTime(starttime.count(), true, &startpts))
1373 CLog::Log(LOGDEBUG, "{} - starting subtitle demuxer from: {}", __FUNCTION__,
1374 starttime.count());
1375 else
1376 CLog::Log(LOGDEBUG, "{} - failed to start subtitle demuxing from: {}", __FUNCTION__,
1377 starttime.count());
1380 m_clock.Discontinuity(DVD_MSEC_TO_TIME(starttime.count()));
1383 UpdatePlayState(0);
1385 SetCaching(CACHESTATE_FLUSH);
1388 void CVideoPlayer::Process()
1390 // Try to resolve the correct mime type. This can take some time, for example if a requested
1391 // item is located at a slow/not reachable remote source. So, do mime type detection in vp worker
1392 // thread, not directly when initalizing the player to keep GUI responsible.
1393 m_item.SetMimeTypeForInternetFile();
1395 CServiceBroker::GetWinSystem()->RegisterRenderLoop(this);
1397 Prepare();
1399 while (!m_bAbortRequest)
1401 // check display lost
1402 if (m_displayLost)
1404 CThread::Sleep(50ms);
1405 continue;
1408 // check if in an edit (cut or commercial break) that should be automatically skipped
1409 CheckAutoSceneSkip();
1411 // handle messages send to this thread, like seek or demuxer reset requests
1412 HandleMessages();
1414 if (m_bAbortRequest)
1415 break;
1417 // should we open a new input stream?
1418 if (!m_pInputStream)
1420 if (OpenInputStream() == false)
1422 m_bAbortRequest = true;
1423 break;
1427 // should we open a new demuxer?
1428 if (!m_pDemuxer)
1430 if (m_pInputStream->NextStream() == CDVDInputStream::NEXTSTREAM_NONE)
1431 break;
1433 if (m_pInputStream->IsEOF())
1434 break;
1436 if (OpenDemuxStream() == false)
1438 m_bAbortRequest = true;
1439 break;
1442 // on channel switch we don't want to close stream players at this
1443 // time. we'll get the stream change event later
1444 if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) ||
1445 !m_SelectionStreams.m_Streams.empty())
1446 OpenDefaultStreams();
1448 UpdatePlayState(0);
1451 // handle eventual seeks due to playspeed
1452 HandlePlaySpeed();
1454 // update player state
1455 UpdatePlayState(200);
1457 // make sure we run subtitle process here
1458 m_VideoPlayerSubtitle->Process(m_clock.GetClock() + m_State.time_offset - m_VideoPlayerVideo->GetSubtitleDelay(), m_State.time_offset);
1460 // tell demuxer if we want to fill buffers
1461 if (m_demuxerSpeed != DVD_PLAYSPEED_PAUSE)
1463 int audioLevel = 90;
1464 int videoLevel = 90;
1465 bool fillBuffer = false;
1466 if (m_CurrentAudio.id >= 0)
1467 audioLevel = m_VideoPlayerAudio->GetLevel();
1468 if (m_CurrentVideo.id >= 0)
1469 videoLevel = m_processInfo->GetLevelVQ();
1470 if (videoLevel < 85 && audioLevel < 85)
1472 fillBuffer = true;
1474 if (m_pDemuxer)
1475 m_pDemuxer->FillBuffer(fillBuffer);
1478 // if the queues are full, no need to read more
1479 if ((!m_VideoPlayerAudio->AcceptsData() && m_CurrentAudio.id >= 0) ||
1480 (!m_VideoPlayerVideo->AcceptsData() && m_CurrentVideo.id >= 0))
1482 if (m_playSpeed == DVD_PLAYSPEED_PAUSE &&
1483 m_demuxerSpeed != DVD_PLAYSPEED_PAUSE)
1485 if (m_pDemuxer)
1486 m_pDemuxer->SetSpeed(DVD_PLAYSPEED_PAUSE);
1487 m_demuxerSpeed = DVD_PLAYSPEED_PAUSE;
1489 CThread::Sleep(10ms);
1490 continue;
1493 // adjust demuxer speed; some rtsp servers wants to know for i.e. ff
1494 // delay pause until queue is full
1495 if (m_playSpeed != DVD_PLAYSPEED_PAUSE &&
1496 m_demuxerSpeed != m_playSpeed)
1498 if (m_pDemuxer)
1499 m_pDemuxer->SetSpeed(m_playSpeed);
1500 m_demuxerSpeed = m_playSpeed;
1503 DemuxPacket* pPacket = NULL;
1504 CDemuxStream *pStream = NULL;
1505 ReadPacket(pPacket, pStream);
1506 if (pPacket && !pStream)
1508 /* probably a empty packet, just free it and move on */
1509 CDVDDemuxUtils::FreeDemuxPacket(pPacket);
1510 continue;
1513 if (!pPacket)
1515 // when paused, demuxer could be be returning empty
1516 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
1517 continue;
1519 // check for a still frame state
1520 if (std::shared_ptr<CDVDInputStream::IMenus> pStream = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1522 // stills will be skipped
1523 if(m_dvd.state == DVDSTATE_STILL)
1525 if (m_dvd.iDVDStillTime > 0ms)
1527 const auto now = std::chrono::steady_clock::now();
1528 const auto duration = now - m_dvd.iDVDStillStartTime;
1530 if (duration >= m_dvd.iDVDStillTime)
1532 m_dvd.iDVDStillTime = 0ms;
1533 m_dvd.iDVDStillStartTime = {};
1534 m_dvd.state = DVDSTATE_NORMAL;
1535 pStream->SkipStill();
1536 continue;
1542 // if there is another stream available, reopen demuxer
1543 CDVDInputStream::ENextStream next = m_pInputStream->NextStream();
1544 if(next == CDVDInputStream::NEXTSTREAM_OPEN)
1546 CloseDemuxer();
1548 SetCaching(CACHESTATE_DONE);
1549 CLog::Log(LOGINFO, "VideoPlayer: next stream, wait for old streams to be finished");
1550 CloseStream(m_CurrentAudio, true);
1551 CloseStream(m_CurrentVideo, true);
1553 m_CurrentAudio.Clear();
1554 m_CurrentVideo.Clear();
1555 m_CurrentSubtitle.Clear();
1556 continue;
1559 // input stream asked us to just retry
1560 if(next == CDVDInputStream::NEXTSTREAM_RETRY)
1562 CThread::Sleep(100ms);
1563 continue;
1566 if (m_CurrentVideo.inited)
1568 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::VIDEO_DRAIN));
1571 m_CurrentAudio.inited = false;
1572 m_CurrentVideo.inited = false;
1573 m_CurrentSubtitle.inited = false;
1574 m_CurrentTeletext.inited = false;
1575 m_CurrentRadioRDS.inited = false;
1576 m_CurrentAudioID3.inited = false;
1578 // if we are caching, start playing it again
1579 SetCaching(CACHESTATE_DONE);
1581 // while players are still playing, keep going to allow seekbacks
1582 if (m_VideoPlayerAudio->HasData() ||
1583 m_VideoPlayerVideo->HasData())
1585 CThread::Sleep(100ms);
1586 continue;
1589 if (!m_pInputStream->IsEOF())
1590 CLog::Log(LOGINFO, "{} - eof reading from demuxer", __FUNCTION__);
1592 break;
1595 // see if we can find something better to play
1596 CheckBetterStream(m_CurrentAudio, pStream);
1597 CheckBetterStream(m_CurrentVideo, pStream);
1598 CheckBetterStream(m_CurrentSubtitle, pStream);
1599 CheckBetterStream(m_CurrentTeletext, pStream);
1600 CheckBetterStream(m_CurrentRadioRDS, pStream);
1601 CheckBetterStream(m_CurrentAudioID3, pStream);
1603 // demux video stream
1604 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SUBTITLES_PARSECAPTIONS) && CheckIsCurrent(m_CurrentVideo, pStream, pPacket))
1606 if (m_pCCDemuxer)
1608 bool first = true;
1609 while (!m_bAbortRequest)
1611 DemuxPacket *pkt = m_pCCDemuxer->Read(first ? pPacket : NULL);
1612 if (!pkt)
1613 break;
1615 first = false;
1616 if (m_pCCDemuxer->GetNrOfStreams() != m_SelectionStreams.CountTypeOfSource(STREAM_SUBTITLE, STREAM_SOURCE_VIDEOMUX))
1618 m_SelectionStreams.Clear(STREAM_SUBTITLE, STREAM_SOURCE_VIDEOMUX);
1619 m_SelectionStreams.Update(NULL, m_pCCDemuxer.get(), "");
1620 UpdateContent();
1621 OpenDefaultStreams(false);
1623 CDemuxStream *pSubStream = m_pCCDemuxer->GetStream(pkt->iStreamId);
1624 if (pSubStream && m_CurrentSubtitle.id == pkt->iStreamId && m_CurrentSubtitle.source == STREAM_SOURCE_VIDEOMUX)
1625 ProcessSubData(pSubStream, pkt);
1626 else
1627 CDVDDemuxUtils::FreeDemuxPacket(pkt);
1632 if (IsInMenuInternal())
1634 if (std::shared_ptr<CDVDInputStream::IMenus> menu = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
1636 double correction = menu->GetTimeStampCorrection();
1637 if (pPacket->dts != DVD_NOPTS_VALUE && pPacket->dts > correction)
1638 pPacket->dts -= correction;
1639 if (pPacket->pts != DVD_NOPTS_VALUE && pPacket->pts > correction)
1640 pPacket->pts -= correction;
1642 if (m_dvd.syncClock)
1644 m_clock.Discontinuity(pPacket->dts);
1645 m_dvd.syncClock = false;
1649 // process the packet
1650 ProcessPacket(pStream, pPacket);
1654 bool CVideoPlayer::CheckIsCurrent(const CCurrentStream& current,
1655 CDemuxStream* stream,
1656 DemuxPacket* pkg)
1658 if(current.id == pkg->iStreamId &&
1659 current.demuxerId == stream->demuxerId &&
1660 current.source == stream->source &&
1661 current.type == stream->type)
1662 return true;
1663 else
1664 return false;
1667 void CVideoPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
1669 // process packet if it belongs to selected stream.
1670 // for dvd's don't allow automatic opening of streams*/
1672 if (CheckIsCurrent(m_CurrentAudio, pStream, pPacket))
1673 ProcessAudioData(pStream, pPacket);
1674 else if (CheckIsCurrent(m_CurrentVideo, pStream, pPacket))
1675 ProcessVideoData(pStream, pPacket);
1676 else if (CheckIsCurrent(m_CurrentSubtitle, pStream, pPacket))
1677 ProcessSubData(pStream, pPacket);
1678 else if (CheckIsCurrent(m_CurrentTeletext, pStream, pPacket))
1679 ProcessTeletextData(pStream, pPacket);
1680 else if (CheckIsCurrent(m_CurrentRadioRDS, pStream, pPacket))
1681 ProcessRadioRDSData(pStream, pPacket);
1682 else if (CheckIsCurrent(m_CurrentAudioID3, pStream, pPacket))
1683 ProcessAudioID3Data(pStream, pPacket);
1684 else
1686 CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
1690 void CVideoPlayer::CheckStreamChanges(CCurrentStream& current, CDemuxStream* stream)
1692 if (current.stream != (void*)stream
1693 || current.changes != stream->changes)
1695 /* check so that dmuxer hints or extra data hasn't changed */
1696 /* if they have, reopen stream */
1698 if (current.hint != CDVDStreamInfo(*stream, true))
1700 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
1701 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
1702 UpdateContent();
1703 OpenDefaultStreams(false);
1706 current.stream = (void*)stream;
1707 current.changes = stream->changes;
1711 void CVideoPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
1713 CheckStreamChanges(m_CurrentAudio, pStream);
1715 bool checkcont = CheckContinuity(m_CurrentAudio, pPacket);
1716 UpdateTimestamps(m_CurrentAudio, pPacket);
1718 if (checkcont && (m_CurrentAudio.avsync == CCurrentStream::AV_SYNC_CHECK))
1719 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
1721 bool drop = false;
1722 if (CheckPlayerInit(m_CurrentAudio))
1723 drop = true;
1726 * If CheckSceneSkip() returns true then demux point is inside an EDL cut and the packets are dropped.
1728 if (CheckSceneSkip(m_CurrentAudio))
1730 drop = true;
1732 else
1734 const auto hasEdit = m_Edl.InEdit(
1735 std::chrono::milliseconds(DVD_TIME_TO_MSEC(m_CurrentAudio.dts + m_offset_pts)));
1736 if (hasEdit && hasEdit.value()->action == EDL::Action::MUTE)
1737 drop = true;
1740 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1742 if (!drop)
1743 m_CurrentAudio.packets++;
1746 void CVideoPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
1748 CheckStreamChanges(m_CurrentVideo, pStream);
1749 bool checkcont = false;
1751 if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
1753 checkcont = CheckContinuity(m_CurrentVideo, pPacket);
1754 UpdateTimestamps(m_CurrentVideo, pPacket);
1756 if (checkcont && (m_CurrentVideo.avsync == CCurrentStream::AV_SYNC_CHECK))
1757 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
1759 bool drop = false;
1760 if (CheckPlayerInit(m_CurrentVideo))
1761 drop = true;
1763 if (CheckSceneSkip(m_CurrentVideo))
1764 drop = true;
1766 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1768 if (!drop)
1769 m_CurrentVideo.packets++;
1772 void CVideoPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
1774 CheckStreamChanges(m_CurrentSubtitle, pStream);
1776 UpdateTimestamps(m_CurrentSubtitle, pPacket);
1778 bool drop = false;
1779 if (CheckPlayerInit(m_CurrentSubtitle))
1780 drop = true;
1782 if (CheckSceneSkip(m_CurrentSubtitle))
1783 drop = true;
1785 m_VideoPlayerSubtitle->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1787 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1788 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_NORMAL);
1791 void CVideoPlayer::ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket)
1793 CheckStreamChanges(m_CurrentTeletext, pStream);
1795 UpdateTimestamps(m_CurrentTeletext, pPacket);
1797 bool drop = false;
1798 if (CheckPlayerInit(m_CurrentTeletext))
1799 drop = true;
1801 if (CheckSceneSkip(m_CurrentTeletext))
1802 drop = true;
1804 m_VideoPlayerTeletext->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1807 void CVideoPlayer::ProcessRadioRDSData(CDemuxStream* pStream, DemuxPacket* pPacket)
1809 CheckStreamChanges(m_CurrentRadioRDS, pStream);
1811 UpdateTimestamps(m_CurrentRadioRDS, pPacket);
1813 bool drop = false;
1814 if (CheckPlayerInit(m_CurrentRadioRDS))
1815 drop = true;
1817 if (CheckSceneSkip(m_CurrentRadioRDS))
1818 drop = true;
1820 m_VideoPlayerRadioRDS->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1823 void CVideoPlayer::ProcessAudioID3Data(CDemuxStream* pStream, DemuxPacket* pPacket)
1825 CheckStreamChanges(m_CurrentAudioID3, pStream);
1827 UpdateTimestamps(m_CurrentAudioID3, pPacket);
1829 bool drop = false;
1830 if (CheckPlayerInit(m_CurrentAudioID3))
1831 drop = true;
1833 if (CheckSceneSkip(m_CurrentAudioID3))
1834 drop = true;
1836 m_VideoPlayerAudioID3->SendMessage(std::make_shared<CDVDMsgDemuxerPacket>(pPacket, drop));
1839 CacheInfo CVideoPlayer::GetCachingTimes()
1841 CacheInfo info{};
1843 if (!m_pInputStream || !m_pDemuxer)
1844 return info;
1846 XFILE::SCacheStatus status;
1847 if (!m_pInputStream->GetCacheStatus(&status))
1848 return info;
1850 const uint64_t& maxforward = status.maxforward;
1851 const uint64_t& cached = status.forward;
1852 const uint32_t& currate = status.currate;
1853 const uint32_t& maxrate = status.maxrate;
1854 const uint32_t& lowrate = status.lowrate;
1856 int64_t length = m_pInputStream->GetLength();
1857 int64_t remain = length - m_pInputStream->Seek(0, SEEK_CUR);
1859 if (length <= 0 || remain < 0)
1860 return info;
1862 double queueTime = GetQueueTime();
1863 double play_sbp = DVD_MSEC_TO_TIME(m_pDemuxer->GetStreamLength()) / length;
1864 double queued = 1000.0 * queueTime / play_sbp;
1866 info.level = 0.0;
1867 info.offset = (cached + queued) / length;
1868 info.time = 0.0;
1869 info.valid = true;
1871 if (currate == 0)
1872 return info;
1874 // estimated playback time of current cached bytes
1875 const double cacheTime = (static_cast<double>(cached) / currate) + (queueTime / 1000.0);
1877 // cache level as current forward bytes / max forward bytes [0.0 - 1.0]
1878 const double cacheLevel = (maxforward > 0) ? static_cast<double>(cached) / maxforward : 0.0;
1880 info.time = cacheTime;
1882 if (lowrate > 0)
1884 // buffer is full & our read rate is too low
1885 CLog::Log(LOGDEBUG, "Readrate {} was too low with {} required", lowrate, maxrate);
1886 info.level = -1.0;
1888 else
1889 info.level = cacheLevel;
1891 return info;
1894 void CVideoPlayer::HandlePlaySpeed()
1896 const bool isInMenu = IsInMenuInternal();
1897 const bool tolerateStall =
1898 isInMenu || (m_CurrentVideo.hint.flags & StreamFlags::FLAG_STILL_IMAGES);
1900 if (tolerateStall && m_caching != CACHESTATE_DONE)
1901 SetCaching(CACHESTATE_DONE);
1903 if (m_caching == CACHESTATE_FULL)
1905 CacheInfo cache = GetCachingTimes();
1906 if (cache.valid)
1908 if (cache.level < 0.0)
1910 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "SourceSlow");
1911 SetCaching(CACHESTATE_INIT);
1913 // Note: Previously used cache.level >= 1 would keep video stalled
1914 // event after cache was full
1915 // Talk link: https://github.com/xbmc/xbmc/pull/23760
1916 if (cache.time > m_messageQueueTimeSize)
1917 SetCaching(CACHESTATE_INIT);
1919 else
1921 if ((!m_VideoPlayerAudio->AcceptsData() && m_CurrentAudio.id >= 0) ||
1922 (!m_VideoPlayerVideo->AcceptsData() && m_CurrentVideo.id >= 0))
1923 SetCaching(CACHESTATE_INIT);
1926 // if audio stream stalled, wait until demux queue filled 10%
1927 if (m_pInputStream->IsRealtime() &&
1928 (m_CurrentAudio.id < 0 || m_VideoPlayerAudio->GetLevel() > 10))
1930 SetCaching(CACHESTATE_INIT);
1934 if (m_caching == CACHESTATE_INIT)
1936 // if all enabled streams have been inited we are done
1937 if ((m_CurrentVideo.id >= 0 || m_CurrentAudio.id >= 0) &&
1938 (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_STARTING) &&
1939 (m_CurrentAudio.id < 0 || m_CurrentAudio.syncState != IDVDStreamPlayer::SYNC_STARTING))
1940 SetCaching(CACHESTATE_PLAY);
1942 // handle exceptions
1943 if (m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0)
1945 if ((!m_VideoPlayerAudio->AcceptsData() || !m_VideoPlayerVideo->AcceptsData()) &&
1946 m_cachingTimer.IsTimePast())
1948 SetCaching(CACHESTATE_DONE);
1953 if (m_caching == CACHESTATE_PLAY)
1955 // if all enabled streams have started playing we are done
1956 if ((m_CurrentVideo.id < 0 || !m_VideoPlayerVideo->IsStalled()) &&
1957 (m_CurrentAudio.id < 0 || !m_VideoPlayerAudio->IsStalled()))
1958 SetCaching(CACHESTATE_DONE);
1961 if (m_caching == CACHESTATE_DONE)
1963 if (m_playSpeed == DVD_PLAYSPEED_NORMAL && !tolerateStall)
1965 // take action if audio or video stream is stalled
1966 if (((m_VideoPlayerAudio->IsStalled() && m_CurrentAudio.inited) ||
1967 (m_VideoPlayerVideo->IsStalled() && m_CurrentVideo.inited)) &&
1968 m_syncTimer.IsTimePast())
1970 if (m_pInputStream->IsRealtime())
1972 if ((m_CurrentAudio.id >= 0 && m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1973 m_VideoPlayerAudio->IsStalled()) ||
1974 (m_CurrentVideo.id >= 0 && m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1975 m_processInfo->GetLevelVQ() == 0))
1977 CLog::Log(LOGDEBUG, "Stream stalled, start buffering. Audio: {} - Video: {}",
1978 m_VideoPlayerAudio->GetLevel(), m_processInfo->GetLevelVQ());
1980 if (m_VideoPlayerAudio->AcceptsData() && m_VideoPlayerVideo->AcceptsData())
1981 SetCaching(CACHESTATE_FULL);
1982 else
1983 FlushBuffers(DVD_NOPTS_VALUE, false, true);
1986 else
1988 // start caching if audio and video have run dry
1989 if (m_VideoPlayerAudio->GetLevel() <= 50 &&
1990 m_processInfo->GetLevelVQ() <= 50)
1992 SetCaching(CACHESTATE_FULL);
1994 else if (m_CurrentAudio.id >= 0 && m_CurrentAudio.inited &&
1995 m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC &&
1996 m_VideoPlayerAudio->GetLevel() == 0)
1998 CLog::Log(LOGDEBUG,"CVideoPlayer::HandlePlaySpeed - audio stream stalled, triggering re-sync");
1999 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2000 CDVDMsgPlayerSeek::CMode mode;
2001 mode.time = (int)GetUpdatedTime();
2002 mode.backward = false;
2003 mode.accurate = true;
2004 mode.sync = true;
2005 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2009 // care for live streams
2010 else if (m_pInputStream->IsRealtime())
2012 if (m_CurrentAudio.id >= 0)
2014 double adjust = -1.0; // a unique value
2015 if (m_clock.GetSpeedAdjust() >= 0 && m_VideoPlayerAudio->GetLevel() < 5)
2016 adjust = -0.05;
2018 if (m_clock.GetSpeedAdjust() < 0 && m_VideoPlayerAudio->GetLevel() > 10)
2019 adjust = 0.0;
2021 if (adjust != -1.0)
2023 m_clock.SetSpeedAdjust(adjust);
2030 // sync streams to clock
2031 if ((m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
2032 (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC))
2034 unsigned int threshold = 20;
2035 if (m_pInputStream->IsRealtime())
2036 threshold = 40;
2038 bool video = m_CurrentVideo.id < 0 || (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
2039 (m_CurrentVideo.packets == 0 && m_CurrentAudio.packets > threshold) ||
2040 (!m_VideoPlayerAudio->AcceptsData() && m_processInfo->GetLevelVQ() < 10);
2041 bool audio = m_CurrentAudio.id < 0 || (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC) ||
2042 (m_CurrentAudio.packets == 0 && m_CurrentVideo.packets > threshold) ||
2043 (!m_VideoPlayerVideo->AcceptsData() && m_VideoPlayerAudio->GetLevel() < 10);
2045 if (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC &&
2046 (m_CurrentAudio.avsync == CCurrentStream::AV_SYNC_CONT ||
2047 m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_INSYNC))
2049 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2050 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
2051 m_VideoPlayerAudio->SendMessage(
2052 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, m_clock.GetClock()), 1);
2054 else if (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC &&
2055 (m_CurrentVideo.avsync == CCurrentStream::AV_SYNC_CONT ||
2056 m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_INSYNC))
2058 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2059 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
2060 m_VideoPlayerVideo->SendMessage(
2061 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, m_clock.GetClock()), 1);
2063 else if (video && audio)
2065 double clock = 0;
2066 if (m_CurrentAudio.syncState == IDVDStreamPlayer::SYNC_WAITSYNC)
2067 CLog::Log(LOGDEBUG, "VideoPlayer::Sync - Audio - pts: {:f}, cache: {:f}, totalcache: {:f}",
2068 m_CurrentAudio.starttime, m_CurrentAudio.cachetime, m_CurrentAudio.cachetotal);
2069 if (m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_WAITSYNC)
2070 CLog::Log(LOGDEBUG, "VideoPlayer::Sync - Video - pts: {:f}, cache: {:f}, totalcache: {:f}",
2071 m_CurrentVideo.starttime, m_CurrentVideo.cachetime, m_CurrentVideo.cachetotal);
2073 if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && m_CurrentVideo.packets > 0 &&
2074 m_playSpeed == DVD_PLAYSPEED_PAUSE)
2076 clock = m_CurrentVideo.starttime;
2078 else if (m_CurrentAudio.starttime != DVD_NOPTS_VALUE && m_CurrentAudio.packets > 0)
2080 if (m_pInputStream->IsRealtime())
2081 clock = m_CurrentAudio.starttime - m_CurrentAudio.cachetotal - DVD_MSEC_TO_TIME(400);
2082 else
2083 clock = m_CurrentAudio.starttime - m_CurrentAudio.cachetime;
2085 if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && (m_CurrentVideo.packets > 0))
2087 if (m_CurrentVideo.starttime - m_CurrentVideo.cachetotal < clock)
2089 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2091 else if (m_CurrentVideo.starttime > m_CurrentAudio.starttime &&
2092 !m_pInputStream->IsRealtime())
2094 int audioLevel = m_VideoPlayerAudio->GetLevel();
2095 //@todo hardcoded 8 seconds in message queue
2096 double maxAudioTime = clock + DVD_MSEC_TO_TIME(80 * audioLevel);
2097 if ((m_CurrentVideo.starttime - m_CurrentVideo.cachetotal) > maxAudioTime)
2098 clock = maxAudioTime;
2099 else
2100 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2104 else if (m_CurrentVideo.starttime != DVD_NOPTS_VALUE && m_CurrentVideo.packets > 0)
2106 clock = m_CurrentVideo.starttime - m_CurrentVideo.cachetotal;
2109 m_clock.Discontinuity(clock);
2110 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2111 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_NONE;
2112 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_INSYNC;
2113 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_NONE;
2114 m_VideoPlayerAudio->SendMessage(
2115 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, clock), 1);
2116 m_VideoPlayerVideo->SendMessage(
2117 std::make_shared<CDVDMsgDouble>(CDVDMsg::GENERAL_RESYNC, clock), 1);
2118 SetCaching(CACHESTATE_DONE);
2119 UpdatePlayState(0);
2121 m_syncTimer.Set(3000ms);
2123 if (!m_State.streamsReady)
2125 if (m_playerOptions.fullscreen)
2127 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SWITCHTOFULLSCREEN);
2130 IPlayerCallback *cb = &m_callback;
2131 CFileItem fileItem = m_item;
2132 m_outboundEvents->Submit([=]() {
2133 cb->OnAVStarted(fileItem);
2135 m_State.streamsReady = true;
2138 else
2140 // exceptions for which stream players won't start properly
2141 // 1. videoplayer has not detected a keyframe within length of demux buffers
2142 if (m_CurrentAudio.id >= 0 && m_CurrentVideo.id >= 0 &&
2143 !m_VideoPlayerAudio->AcceptsData() &&
2144 m_CurrentVideo.syncState == IDVDStreamPlayer::SYNC_STARTING &&
2145 m_VideoPlayerVideo->IsStalled() &&
2146 m_CurrentVideo.packets > 10)
2148 m_VideoPlayerAudio->AcceptsData();
2149 CLog::Log(LOGWARNING, "VideoPlayer::Sync - stream player video does not start, flushing buffers");
2150 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2155 // handle ff/rw
2156 if (m_playSpeed != DVD_PLAYSPEED_NORMAL && m_playSpeed != DVD_PLAYSPEED_PAUSE)
2158 if (isInMenu)
2160 // this can't be done in menu
2161 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2164 else
2166 bool check = true;
2168 // only check if we have video
2169 if (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC)
2170 check = false;
2171 // video message queue either initiated or already seen eof
2172 else if (m_CurrentVideo.inited == false && m_playSpeed >= 0)
2173 check = false;
2174 // don't check if time has not advanced since last check
2175 else if (m_SpeedState.lasttime == GetTime())
2176 check = false;
2177 // skip if frame at screen has no valid timestamp
2178 else if (m_VideoPlayerVideo->GetCurrentPts() == DVD_NOPTS_VALUE)
2179 check = false;
2180 // skip if frame on screen has not changed
2181 else if (m_SpeedState.lastpts == m_VideoPlayerVideo->GetCurrentPts() &&
2182 (m_SpeedState.lastpts > m_State.dts || m_playSpeed > 0))
2183 check = false;
2185 if (check)
2187 m_SpeedState.lastpts = m_VideoPlayerVideo->GetCurrentPts();
2188 m_SpeedState.lasttime = GetTime();
2189 m_SpeedState.lastabstime = m_clock.GetAbsoluteClock();
2191 double error;
2192 error = m_clock.GetClock() - m_SpeedState.lastpts;
2193 error *= m_playSpeed / abs(m_playSpeed);
2195 // allow a bigger error when going ff, the faster we go
2196 // the the bigger is the error we allow
2197 if (m_playSpeed > DVD_PLAYSPEED_NORMAL)
2199 double errorwin = static_cast<double>(m_playSpeed) / DVD_PLAYSPEED_NORMAL;
2200 if (errorwin > 8.0)
2201 errorwin = 8.0;
2202 error /= errorwin;
2205 if (error > DVD_MSEC_TO_TIME(1000))
2207 error = (m_clock.GetClock() - m_SpeedState.lastseekpts) / 1000;
2209 if (std::abs(error) > 1000 || (m_VideoPlayerVideo->IsRewindStalled() && std::abs(error) > 100))
2211 CLog::Log(LOGDEBUG, "CVideoPlayer::Process - Seeking to catch up, error was: {:f}",
2212 error);
2213 m_SpeedState.lastseekpts = m_clock.GetClock();
2214 int direction = (m_playSpeed > 0) ? 1 : -1;
2215 double iTime = (m_clock.GetClock() + m_State.time_offset + 1000000.0 * direction) / 1000;
2216 CDVDMsgPlayerSeek::CMode mode;
2217 mode.time = iTime;
2218 mode.backward = (m_playSpeed < 0);
2219 mode.accurate = false;
2220 mode.restore = false;
2221 mode.trickplay = true;
2222 mode.sync = false;
2223 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2230 // reset tempo
2231 if (!m_State.cantempo)
2233 float currentTempo = m_processInfo->GetNewTempo();
2234 if (currentTempo != 1.0f)
2236 SetTempo(1.0f);
2241 bool CVideoPlayer::CheckPlayerInit(CCurrentStream& current)
2243 if (current.inited)
2244 return false;
2246 if (current.startpts != DVD_NOPTS_VALUE)
2248 if(current.dts == DVD_NOPTS_VALUE)
2250 CLog::Log(LOGDEBUG, "{} - dropping packet type:{} dts:{:f} to get to start point at {:f}",
2251 __FUNCTION__, current.player, current.dts, current.startpts);
2252 return true;
2255 if ((current.startpts - current.dts) > DVD_SEC_TO_TIME(20))
2257 CLog::Log(LOGDEBUG, "{} - too far to decode before finishing seek", __FUNCTION__);
2258 if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
2259 m_CurrentAudio.startpts = current.dts;
2260 if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
2261 m_CurrentVideo.startpts = current.dts;
2262 if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
2263 m_CurrentSubtitle.startpts = current.dts;
2264 if(m_CurrentTeletext.startpts != DVD_NOPTS_VALUE)
2265 m_CurrentTeletext.startpts = current.dts;
2266 if(m_CurrentRadioRDS.startpts != DVD_NOPTS_VALUE)
2267 m_CurrentRadioRDS.startpts = current.dts;
2268 if (m_CurrentAudioID3.startpts != DVD_NOPTS_VALUE)
2269 m_CurrentAudioID3.startpts = current.dts;
2272 if(current.dts < current.startpts)
2274 CLog::Log(LOGDEBUG, "{} - dropping packet type:{} dts:{:f} to get to start point at {:f}",
2275 __FUNCTION__, current.player, current.dts, current.startpts);
2276 return true;
2280 if (current.dts != DVD_NOPTS_VALUE)
2282 current.inited = true;
2283 current.startpts = current.dts;
2285 return false;
2288 void CVideoPlayer::UpdateCorrection(DemuxPacket* pkt, double correction)
2290 pkt->m_ptsOffsetCorrection = correction;
2292 if(pkt->dts != DVD_NOPTS_VALUE)
2293 pkt->dts -= correction;
2294 if(pkt->pts != DVD_NOPTS_VALUE)
2295 pkt->pts -= correction;
2298 void CVideoPlayer::UpdateTimestamps(CCurrentStream& current, DemuxPacket* pPacket)
2300 double dts = current.dts;
2301 /* update stored values */
2302 if(pPacket->dts != DVD_NOPTS_VALUE)
2303 dts = pPacket->dts;
2304 else if(pPacket->pts != DVD_NOPTS_VALUE)
2305 dts = pPacket->pts;
2307 /* calculate some average duration */
2308 if(pPacket->duration != DVD_NOPTS_VALUE)
2309 current.dur = pPacket->duration;
2310 else if(dts != DVD_NOPTS_VALUE && current.dts != DVD_NOPTS_VALUE)
2311 current.dur = 0.1 * (current.dur * 9 + (dts - current.dts));
2313 current.dts = dts;
2315 current.dispTime = pPacket->dispTime;
2318 static void UpdateLimits(double& minimum, double& maximum, double dts)
2320 if(dts == DVD_NOPTS_VALUE)
2321 return;
2322 if(minimum == DVD_NOPTS_VALUE || minimum > dts) minimum = dts;
2323 if(maximum == DVD_NOPTS_VALUE || maximum < dts) maximum = dts;
2326 bool CVideoPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
2328 if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
2329 return false;
2331 if( pPacket->dts == DVD_NOPTS_VALUE || current.dts == DVD_NOPTS_VALUE)
2332 return false;
2334 double mindts = DVD_NOPTS_VALUE, maxdts = DVD_NOPTS_VALUE;
2335 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts);
2336 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts);
2337 UpdateLimits(mindts, maxdts, m_CurrentAudio.dts_end());
2338 UpdateLimits(mindts, maxdts, m_CurrentVideo.dts_end());
2340 /* if we don't have max and min, we can't do anything more */
2341 if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
2342 return false;
2344 double correction = 0.0;
2345 if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000))
2347 CLog::Log(LOGDEBUG,
2348 "CVideoPlayer::CheckContinuity - resync forward :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2349 current.type, current.dts, pPacket->dts, pPacket->dts - maxdts);
2350 correction = pPacket->dts - maxdts;
2353 /* if it's large scale jump, correct for it after having confirmed the jump */
2354 if(pPacket->dts + DVD_MSEC_TO_TIME(500) < current.dts_end())
2356 CLog::Log(
2357 LOGDEBUG,
2358 "CVideoPlayer::CheckContinuity - resync backward :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2359 current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
2360 correction = pPacket->dts - current.dts_end();
2362 else if(pPacket->dts < current.dts)
2364 CLog::Log(LOGDEBUG,
2365 "CVideoPlayer::CheckContinuity - wrapback :{}, prev:{:f}, curr:{:f}, diff:{:f}",
2366 current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
2369 double lastdts = pPacket->dts;
2370 if(correction != 0.0)
2372 // we want the dts values of two streams to close, or for one to be invalid (e.g. from a missing audio stream)
2373 double this_dts = pPacket->dts;
2374 double that_dts = current.type == STREAM_AUDIO ? m_CurrentVideo.lastdts : m_CurrentAudio.lastdts;
2376 if (m_CurrentAudio.id == -1 || m_CurrentVideo.id == -1 ||
2377 current.lastdts == DVD_NOPTS_VALUE ||
2378 fabs(this_dts - that_dts) < DVD_MSEC_TO_TIME(1000))
2380 m_offset_pts += correction;
2381 UpdateCorrection(pPacket, correction);
2382 lastdts = pPacket->dts;
2383 CLog::Log(LOGDEBUG, "CVideoPlayer::CheckContinuity - update correction: {:f}", correction);
2384 if (current.avsync == CCurrentStream::AV_SYNC_CHECK)
2385 current.avsync = CCurrentStream::AV_SYNC_CONT;
2387 else
2389 // not sure yet - flags the packets as unknown until we get confirmation on another audio/video packet
2390 pPacket->dts = DVD_NOPTS_VALUE;
2391 pPacket->pts = DVD_NOPTS_VALUE;
2394 else
2396 if (current.avsync == CCurrentStream::AV_SYNC_CHECK)
2397 current.avsync = CCurrentStream::AV_SYNC_CONT;
2399 current.lastdts = lastdts;
2400 return true;
2403 bool CVideoPlayer::CheckSceneSkip(const CCurrentStream& current)
2405 if (!m_Edl.HasEdits())
2406 return false;
2408 if(current.dts == DVD_NOPTS_VALUE)
2409 return false;
2411 if(current.inited == false)
2412 return false;
2414 const auto hasEdit =
2415 m_Edl.InEdit(std::chrono::milliseconds(std::lround(current.dts + m_offset_pts)));
2416 return hasEdit && hasEdit.value()->action == EDL::Action::CUT;
2419 void CVideoPlayer::CheckAutoSceneSkip()
2421 if (!m_Edl.HasEdits())
2422 return;
2424 // Check that there is an audio and video stream.
2425 if((m_CurrentAudio.id < 0 || m_CurrentAudio.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
2426 (m_CurrentVideo.id < 0 || m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC))
2427 return;
2429 // If there is a startpts defined for either the audio or video stream then VideoPlayer is still
2430 // still decoding frames to get to the previously requested seek point.
2431 if (m_CurrentAudio.inited == false ||
2432 m_CurrentVideo.inited == false)
2433 return;
2435 const std::chrono::milliseconds clock{GetTime()};
2437 const std::chrono::milliseconds correctClock = m_Edl.GetTimeAfterRestoringCuts(clock);
2438 const auto hasEdit = m_Edl.InEdit(correctClock);
2439 if (!hasEdit)
2441 // @note: Users are allowed to jump back into EDL commercial breaks
2442 // do not reset the last edit time if the last surpassed edit is a commercial break
2443 if (m_Edl.GetLastEditActionType() != EDL::Action::COMM_BREAK)
2445 m_Edl.ResetLastEditTime();
2447 return;
2450 const auto& edit = hasEdit.value();
2451 if (edit->action == EDL::Action::CUT)
2453 if ((m_playSpeed > 0 && correctClock < (edit->start + 1s)) ||
2454 (m_playSpeed < 0 && correctClock < (edit->end - 1s)))
2456 CLog::Log(LOGDEBUG, "{} - Clock in EDL cut [{} - {}]: {}. Automatically skipping over.",
2457 __FUNCTION__, StringUtils::MillisecondsToTimeString(edit->start),
2458 StringUtils::MillisecondsToTimeString(edit->end),
2459 StringUtils::MillisecondsToTimeString(clock));
2461 // Seeking either goes to the start or the end of the cut depending on the play direction.
2462 std::chrono::milliseconds seek = m_playSpeed >= 0 ? edit->end : edit->start;
2463 if (m_Edl.GetLastEditTime() != seek)
2465 CDVDMsgPlayerSeek::CMode mode;
2466 mode.time = seek.count();
2467 mode.backward = true;
2468 mode.accurate = true;
2469 mode.restore = false;
2470 mode.trickplay = false;
2471 mode.sync = true;
2472 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2474 m_Edl.SetLastEditTime(seek);
2475 m_Edl.SetLastEditActionType(edit->action);
2479 else if (edit->action == EDL::Action::COMM_BREAK)
2481 // marker for commbreak may be inaccurate. allow user to skip into break from the back
2482 if (m_playSpeed >= 0 && m_Edl.GetLastEditTime() != edit->start && clock < edit->end - 1s)
2484 CVariant announcement{StringUtils::SecondsToTimeString(
2485 std::chrono::duration_cast<std::chrono::seconds>(edit->end - edit->start).count(),
2486 TIME_FORMAT_MM_SS)};
2487 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnCommercial",
2488 announcement);
2490 m_Edl.SetLastEditTime(edit->start);
2491 m_Edl.SetLastEditActionType(edit->action);
2493 if (m_SkipCommercials)
2495 CLog::Log(LOGDEBUG,
2496 "{} - Clock in commercial break [{} - {}]: {}. Automatically skipping to end of "
2497 "commercial break",
2498 __FUNCTION__, StringUtils::MillisecondsToTimeString(edit->start),
2499 StringUtils::MillisecondsToTimeString(edit->end),
2500 StringUtils::MillisecondsToTimeString(clock));
2502 CDVDMsgPlayerSeek::CMode mode;
2503 mode.time = edit->end.count();
2504 mode.backward = true;
2505 mode.accurate = true;
2506 mode.restore = false;
2507 mode.trickplay = false;
2508 mode.sync = true;
2509 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2516 void CVideoPlayer::SynchronizeDemuxer()
2518 if(IsCurrentThread())
2519 return;
2520 if(!m_messenger.IsInited())
2521 return;
2523 auto message = std::make_shared<CDVDMsgGeneralSynchronize>(500ms, SYNCSOURCE_PLAYER);
2524 m_messenger.Put(message);
2525 message->Wait(m_bStop, 0);
2528 IDVDStreamPlayer* CVideoPlayer::GetStreamPlayer(unsigned int target)
2530 if(target == VideoPlayer_AUDIO)
2531 return m_VideoPlayerAudio;
2532 if(target == VideoPlayer_VIDEO)
2533 return m_VideoPlayerVideo;
2534 if(target == VideoPlayer_SUBTITLE)
2535 return m_VideoPlayerSubtitle;
2536 if(target == VideoPlayer_TELETEXT)
2537 return m_VideoPlayerTeletext;
2538 if(target == VideoPlayer_RDS)
2539 return m_VideoPlayerRadioRDS;
2540 if (target == VideoPlayer_ID3)
2541 return m_VideoPlayerAudioID3.get();
2542 return NULL;
2545 void CVideoPlayer::SendPlayerMessage(std::shared_ptr<CDVDMsg> pMsg, unsigned int target)
2547 IDVDStreamPlayer* player = GetStreamPlayer(target);
2548 if(player)
2549 player->SendMessage(std::move(pMsg), 0);
2552 void CVideoPlayer::OnExit()
2554 CLog::Log(LOGINFO, "CVideoPlayer::OnExit()");
2556 // set event to inform openfile something went wrong in case openfile is still waiting for this event
2557 SetCaching(CACHESTATE_DONE);
2559 // close each stream
2560 if (!m_bAbortRequest)
2561 CLog::Log(LOGINFO, "VideoPlayer: eof, waiting for queues to empty");
2563 CFileItem fileItem(m_item);
2564 UpdateFileItemStreamDetails(fileItem);
2566 CloseStream(m_CurrentAudio, !m_bAbortRequest);
2567 CloseStream(m_CurrentVideo, !m_bAbortRequest);
2568 CloseStream(m_CurrentTeletext,!m_bAbortRequest);
2569 CloseStream(m_CurrentRadioRDS, !m_bAbortRequest);
2570 CloseStream(m_CurrentAudioID3, !m_bAbortRequest);
2571 // the generalization principle was abused for subtitle player. actually it is not a stream player like
2572 // video and audio. subtitle player does not run on its own thread, hence waitForBuffers makes
2573 // no sense here. waitForBuffers is abused to clear overlay container (false clears container)
2574 // subtitles are added from video player. after video player has finished, overlays have to be cleared.
2575 CloseStream(m_CurrentSubtitle, false); // clear overlay container
2577 CServiceBroker::GetWinSystem()->UnregisterRenderLoop(this);
2579 IPlayerCallback *cb = &m_callback;
2580 CVideoSettings vs = m_processInfo->GetVideoSettings();
2581 m_outboundEvents->Submit([=]() {
2582 cb->StoreVideoSettings(fileItem, vs);
2585 CBookmark bookmark;
2586 bookmark.totalTimeInSeconds = 0;
2587 bookmark.timeInSeconds = 0;
2588 if (m_State.startTime == 0)
2590 bookmark.totalTimeInSeconds = m_State.timeMax / 1000;
2591 bookmark.timeInSeconds = m_State.time / 1000;
2593 bookmark.player = m_name;
2594 bookmark.playerState = GetPlayerState();
2595 m_outboundEvents->Submit([=]() {
2596 cb->OnPlayerCloseFile(fileItem, bookmark);
2599 // destroy objects
2600 m_renderManager.Flush(false, false);
2601 m_pDemuxer.reset();
2602 m_pSubtitleDemuxer.reset();
2603 m_subtitleDemuxerMap.clear();
2604 m_pCCDemuxer.reset();
2605 if (m_pInputStream.use_count() > 1)
2606 throw std::runtime_error("m_pInputStream reference count is greater than 1");
2607 m_pInputStream.reset();
2609 // clean up all selection streams
2610 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
2612 m_messenger.End();
2614 CFFmpegLog::ClearLogLevel();
2615 m_bStop = true;
2617 bool error = m_error;
2618 bool close = m_bCloseRequest;
2619 m_outboundEvents->Submit([=]() {
2620 if (close)
2621 cb->OnPlayBackStopped();
2622 else if (error)
2623 cb->OnPlayBackError();
2624 else
2625 cb->OnPlayBackEnded();
2629 void CVideoPlayer::HandleMessages()
2631 std::shared_ptr<CDVDMsg> pMsg = nullptr;
2633 while (m_messenger.Get(pMsg, 0ms) == MSGQ_OK)
2635 if (pMsg->IsType(CDVDMsg::PLAYER_OPENFILE) &&
2636 m_messenger.GetPacketCount(CDVDMsg::PLAYER_OPENFILE) == 0)
2638 CDVDMsgOpenFile& msg(*std::static_pointer_cast<CDVDMsgOpenFile>(pMsg));
2640 IPlayerCallback *cb = &m_callback;
2641 CFileItem fileItem(m_item);
2642 UpdateFileItemStreamDetails(fileItem);
2643 CVideoSettings vs = m_processInfo->GetVideoSettings();
2644 m_outboundEvents->Submit([=]() {
2645 cb->StoreVideoSettings(fileItem, vs);
2648 CBookmark bookmark;
2649 bookmark.totalTimeInSeconds = 0;
2650 bookmark.timeInSeconds = 0;
2651 if (m_State.startTime == 0)
2653 bookmark.totalTimeInSeconds = m_State.timeMax / 1000;
2654 bookmark.timeInSeconds = m_State.time / 1000;
2656 bookmark.player = m_name;
2657 bookmark.playerState = GetPlayerState();
2658 m_outboundEvents->Submit([=]() {
2659 cb->OnPlayerCloseFile(fileItem, bookmark);
2662 m_item = msg.GetItem();
2663 m_playerOptions = msg.GetOptions();
2665 m_processInfo->SetPlayTimes(0,0,0,0);
2667 m_outboundEvents->Submit([this]() {
2668 m_callback.OnPlayBackStarted(m_item);
2671 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2672 m_renderManager.Flush(false, false);
2673 m_pDemuxer.reset();
2674 m_pSubtitleDemuxer.reset();
2675 m_subtitleDemuxerMap.clear();
2676 m_pCCDemuxer.reset();
2677 if (m_pInputStream.use_count() > 1)
2678 throw std::runtime_error("m_pInputStream reference count is greater than 1");
2679 m_pInputStream.reset();
2681 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
2683 Prepare();
2685 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK) &&
2686 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0 &&
2687 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2689 CDVDMsgPlayerSeek& msg(*std::static_pointer_cast<CDVDMsgPlayerSeek>(pMsg));
2691 if (!m_State.canseek)
2693 m_processInfo->SetStateSeeking(false);
2694 continue;
2697 // skip seeks if player has not finished the last seek
2698 if (m_CurrentVideo.id >= 0 &&
2699 m_CurrentVideo.syncState != IDVDStreamPlayer::SYNC_INSYNC)
2701 double now = m_clock.GetAbsoluteClock();
2702 if (m_playSpeed == DVD_PLAYSPEED_NORMAL &&
2703 (now - m_State.lastSeek)/1000 < 2000 &&
2704 !msg.GetAccurate())
2706 m_processInfo->SetStateSeeking(false);
2707 continue;
2711 if (!msg.GetTrickPlay())
2713 m_processInfo->SeekFinished(0);
2714 SetCaching(CACHESTATE_FLUSH);
2717 double start = DVD_NOPTS_VALUE;
2719 double time = msg.GetTime();
2720 if (msg.GetRelative())
2721 time = (m_clock.GetClock() + m_State.time_offset) / 1000l + time;
2723 time = msg.GetRestore()
2724 ? m_Edl.GetTimeAfterRestoringCuts(std::chrono::milliseconds(std::lround(time)))
2725 .count()
2726 : time;
2728 // if input stream doesn't support ISeekTime, convert back to pts
2729 //! @todo
2730 //! After demuxer we add an offset to input pts so that displayed time and clock are
2731 //! increasing steadily. For seeking we need to determine the boundaries and offset
2732 //! of the desired segment. With the current approach calculated time may point
2733 //! to nirvana
2734 if (m_pInputStream->GetIPosTime() == nullptr)
2735 time -= m_State.time_offset/1000l;
2737 CLog::Log(LOGDEBUG, "demuxer seek to: {:f}", time);
2738 if (m_pDemuxer && m_pDemuxer->SeekTime(time, msg.GetBackward(), &start))
2740 CLog::Log(LOGDEBUG, "demuxer seek to: {:f}, success", time);
2741 if(m_pSubtitleDemuxer)
2743 if(!m_pSubtitleDemuxer->SeekTime(time, msg.GetBackward()))
2744 CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: {:f}, success", time);
2746 // dts after successful seek
2747 if (start == DVD_NOPTS_VALUE)
2748 start = DVD_MSEC_TO_TIME(time) - m_State.time_offset;
2750 m_State.dts = start;
2751 m_State.lastSeek = m_clock.GetAbsoluteClock();
2753 FlushBuffers(start, msg.GetAccurate(), msg.GetSync());
2755 else if (m_pDemuxer)
2757 CLog::Log(LOGDEBUG, "VideoPlayer: seek failed or hit end of stream");
2758 // dts after successful seek
2759 if (start == DVD_NOPTS_VALUE)
2760 start = DVD_MSEC_TO_TIME(time) - m_State.time_offset;
2762 m_State.dts = start;
2764 FlushBuffers(start, false, true);
2765 if (m_playSpeed != DVD_PLAYSPEED_PAUSE)
2767 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
2771 // set flag to indicate we have finished a seeking request
2772 if(!msg.GetTrickPlay())
2774 m_processInfo->SeekFinished(0);
2777 // dvd's will issue a HOP_CHANNEL that we need to skip
2778 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2779 m_dvd.state = DVDSTATE_SEEK;
2781 m_processInfo->SetStateSeeking(false);
2783 else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER) &&
2784 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK) == 0 &&
2785 m_messenger.GetPacketCount(CDVDMsg::PLAYER_SEEK_CHAPTER) == 0)
2787 m_processInfo->SeekFinished(0);
2788 SetCaching(CACHESTATE_FLUSH);
2790 CDVDMsgPlayerSeekChapter& msg(*std::static_pointer_cast<CDVDMsgPlayerSeekChapter>(pMsg));
2791 double start = DVD_NOPTS_VALUE;
2792 int offset = 0;
2794 // This should always be the case.
2795 if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
2797 FlushBuffers(start, true, true);
2798 int64_t beforeSeek = GetTime();
2799 offset = DVD_TIME_TO_MSEC(start) - static_cast<int>(beforeSeek);
2800 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2802 else if (m_pInputStream)
2804 CDVDInputStream::IChapter* pChapter = m_pInputStream->GetIChapter();
2805 if (pChapter && pChapter->SeekChapter(msg.GetChapter()))
2807 FlushBuffers(start, true, true);
2808 int64_t beforeSeek = GetTime();
2809 offset = DVD_TIME_TO_MSEC(start) - static_cast<int>(beforeSeek);
2810 m_callback.OnPlayBackSeekChapter(msg.GetChapter());
2813 m_processInfo->SeekFinished(offset);
2815 else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
2817 m_CurrentAudio.stream = NULL;
2818 m_CurrentVideo.stream = NULL;
2819 m_CurrentSubtitle.stream = NULL;
2821 // we need to reset the demuxer, probably because the streams have changed
2822 if(m_pDemuxer)
2823 m_pDemuxer->Reset();
2824 if(m_pSubtitleDemuxer)
2825 m_pSubtitleDemuxer->Reset();
2827 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
2829 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetAudioStream>(pMsg);
2831 SelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
2832 if(st.source != STREAM_SOURCE_NONE)
2834 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2836 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2837 if(pStream->SetActiveAudioStream(st.id))
2839 m_dvd.iSelectedAudioStream = -1;
2840 CloseStream(m_CurrentAudio, false);
2841 CDVDMsgPlayerSeek::CMode mode;
2842 mode.time = (int)GetUpdatedTime();
2843 mode.backward = true;
2844 mode.accurate = true;
2845 mode.trickplay = true;
2846 mode.sync = true;
2847 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2850 else
2852 CloseStream(m_CurrentAudio, false);
2853 OpenStream(m_CurrentAudio, st.demuxerId, st.id, st.source);
2854 AdaptForcedSubtitles();
2856 CDVDMsgPlayerSeek::CMode mode;
2857 mode.time = (int)GetUpdatedTime();
2858 mode.backward = true;
2859 mode.accurate = true;
2860 mode.trickplay = true;
2861 mode.sync = true;
2862 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2866 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_VIDEOSTREAM))
2868 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetVideoStream>(pMsg);
2870 SelectionStream& st = m_SelectionStreams.Get(STREAM_VIDEO, pMsg2->GetStreamId());
2871 if (st.source != STREAM_SOURCE_NONE)
2873 if (st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2875 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2876 if (pStream->SetAngle(st.id))
2878 m_dvd.iSelectedVideoStream = st.id;
2880 CDVDMsgPlayerSeek::CMode mode;
2881 mode.time = (int)GetUpdatedTime();
2882 mode.backward = true;
2883 mode.accurate = true;
2884 mode.trickplay = true;
2885 mode.sync = true;
2886 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2889 else
2891 CloseStream(m_CurrentVideo, false);
2892 OpenStream(m_CurrentVideo, st.demuxerId, st.id, st.source);
2893 CDVDMsgPlayerSeek::CMode mode;
2894 mode.time = (int)GetUpdatedTime();
2895 mode.backward = true;
2896 mode.accurate = true;
2897 mode.trickplay = true;
2898 mode.sync = true;
2899 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
2903 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
2905 auto pMsg2 = std::static_pointer_cast<CDVDMsgPlayerSetSubtitleStream>(pMsg);
2907 SelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
2908 if(st.source != STREAM_SOURCE_NONE)
2910 if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2912 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
2913 if(pStream->SetActiveSubtitleStream(st.id))
2915 m_dvd.iSelectedSPUStream = -1;
2916 CloseStream(m_CurrentSubtitle, false);
2919 else
2921 CloseStream(m_CurrentSubtitle, false);
2922 OpenStream(m_CurrentSubtitle, st.demuxerId, st.id, st.source);
2926 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
2928 bool isVisible = std::static_pointer_cast<CDVDMsgBool>(pMsg)->m_value;
2930 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
2931 if (!isVisible)
2932 SetEnableStream(m_CurrentSubtitle, false);
2934 SetSubtitleVisibleInternal(isVisible);
2936 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_PROGRAM))
2938 auto msg = std::static_pointer_cast<CDVDMsgInt>(pMsg);
2939 if (m_pDemuxer)
2941 m_pDemuxer->SetProgram(msg->m_value);
2942 FlushBuffers(DVD_NOPTS_VALUE, false, true);
2945 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
2947 SetCaching(CACHESTATE_FLUSH);
2949 auto pMsgPlayerSetState = std::static_pointer_cast<CDVDMsgPlayerSetState>(pMsg);
2951 if (std::shared_ptr<CDVDInputStream::IMenus> ptr = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream))
2953 if(ptr->SetState(pMsgPlayerSetState->GetState()))
2955 m_dvd.state = DVDSTATE_NORMAL;
2956 m_dvd.iDVDStillStartTime = {};
2957 m_dvd.iDVDStillTime = 0ms;
2961 m_processInfo->SeekFinished(0);
2963 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
2965 FlushBuffers(DVD_NOPTS_VALUE, true, true);
2967 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
2969 int speed = std::static_pointer_cast<CDVDMsgPlayerSetSpeed>(pMsg)->GetSpeed();
2971 // correct our current clock, as it would start going wrong otherwise
2972 if (m_State.timestamp > 0)
2974 double offset;
2975 offset = m_clock.GetAbsoluteClock() - m_State.timestamp;
2976 offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2977 offset = DVD_TIME_TO_MSEC(offset);
2978 if (offset > 1000)
2979 offset = 1000;
2980 if (offset < -1000)
2981 offset = -1000;
2982 m_State.time += offset;
2983 m_State.timestamp = m_clock.GetAbsoluteClock();
2986 if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed)
2988 m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL);
2989 m_processInfo->SeekFinished(0);
2992 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && speed != m_playSpeed)
2994 std::shared_ptr<CInputStreamPVRBase> pvrinputstream = std::static_pointer_cast<CInputStreamPVRBase>(m_pInputStream);
2995 pvrinputstream->Pause(speed == 0);
2998 // do a seek after rewind, clock is not in sync with current pts
2999 if ((speed == DVD_PLAYSPEED_NORMAL) &&
3000 (m_playSpeed != DVD_PLAYSPEED_NORMAL) &&
3001 (m_playSpeed != DVD_PLAYSPEED_PAUSE))
3003 double iTime = m_VideoPlayerVideo->GetCurrentPts();
3004 if (iTime == DVD_NOPTS_VALUE)
3005 iTime = m_clock.GetClock();
3006 iTime = (iTime + m_State.time_offset) / 1000;
3008 CDVDMsgPlayerSeek::CMode mode;
3009 mode.time = iTime;
3010 mode.backward = m_playSpeed < 0;
3011 mode.accurate = true;
3012 mode.trickplay = true;
3013 mode.sync = true;
3014 mode.restore = false;
3015 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3018 if (std::static_pointer_cast<CDVDMsgPlayerSetSpeed>(pMsg)->IsTempo())
3019 m_processInfo->SetTempo(static_cast<float>(speed) / DVD_PLAYSPEED_NORMAL);
3020 else
3021 m_processInfo->SetSpeed(static_cast<float>(speed) / DVD_PLAYSPEED_NORMAL);
3023 m_processInfo->SetFrameAdvance(false);
3025 m_playSpeed = speed;
3027 m_caching = CACHESTATE_DONE;
3028 m_clock.SetSpeed(speed);
3029 m_VideoPlayerAudio->SetSpeed(speed);
3030 m_VideoPlayerVideo->SetSpeed(speed);
3031 m_streamPlayerSpeed = speed;
3033 else if (pMsg->IsType(CDVDMsg::PLAYER_FRAME_ADVANCE))
3035 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
3037 int frames = std::static_pointer_cast<CDVDMsgInt>(pMsg)->m_value;
3038 double time = DVD_TIME_BASE / static_cast<double>(m_processInfo->GetVideoFps()) * frames;
3039 m_processInfo->SetFrameAdvance(true);
3040 m_clock.Advance(time);
3043 else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
3044 OnAction(std::static_pointer_cast<CDVDMsgType<CAction>>(pMsg)->m_value);
3045 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
3047 SStartMsg& msg = std::static_pointer_cast<CDVDMsgType<SStartMsg>>(pMsg)->m_value;
3048 if (msg.player == VideoPlayer_AUDIO)
3050 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_WAITSYNC;
3051 m_CurrentAudio.cachetime = msg.cachetime;
3052 m_CurrentAudio.cachetotal = msg.cachetotal;
3053 m_CurrentAudio.starttime = msg.timestamp;
3055 if (msg.player == VideoPlayer_VIDEO)
3057 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_WAITSYNC;
3058 m_CurrentVideo.cachetime = msg.cachetime;
3059 m_CurrentVideo.cachetotal = msg.cachetotal;
3060 m_CurrentVideo.starttime = msg.timestamp;
3062 CLog::Log(LOGDEBUG, "CVideoPlayer::HandleMessages - player started {}", msg.player);
3064 else if (pMsg->IsType(CDVDMsg::PLAYER_REPORT_STATE))
3066 SStateMsg& msg = std::static_pointer_cast<CDVDMsgType<SStateMsg>>(pMsg)->m_value;
3067 if (msg.player == VideoPlayer_AUDIO)
3069 m_CurrentAudio.syncState = msg.syncState;
3071 if (msg.player == VideoPlayer_VIDEO)
3073 m_CurrentVideo.syncState = msg.syncState;
3075 CLog::Log(LOGDEBUG, "CVideoPlayer::HandleMessages - player {} reported state: {}", msg.player,
3076 msg.syncState);
3078 else if (pMsg->IsType(CDVDMsg::SUBTITLE_ADDFILE))
3080 int id = AddSubtitleFile(std::static_pointer_cast<CDVDMsgType<std::string>>(pMsg)->m_value);
3081 if (id >= 0)
3083 SetSubtitle(id);
3084 SetSubtitleVisibleInternal(true);
3087 else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
3089 if (std::static_pointer_cast<CDVDMsgGeneralSynchronize>(pMsg)->Wait(100ms, SYNCSOURCE_PLAYER))
3090 CLog::Log(LOGDEBUG, "CVideoPlayer - CDVDMsg::GENERAL_SYNCHRONIZE");
3092 else if (pMsg->IsType(CDVDMsg::PLAYER_AVCHANGE))
3094 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
3095 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
3096 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3097 IPlayerCallback *cb = &m_callback;
3098 m_outboundEvents->Submit([=]() {
3099 cb->OnAVChange();
3102 else if (pMsg->IsType(CDVDMsg::PLAYER_ABORT))
3104 CLog::Log(LOGDEBUG, "CVideoPlayer - CDVDMsg::PLAYER_ABORT");
3105 m_bAbortRequest = true;
3107 else if (pMsg->IsType(CDVDMsg::PLAYER_SET_UPDATE_STREAM_DETAILS))
3108 m_UpdateStreamDetails = true;
3112 void CVideoPlayer::SetCaching(ECacheState state)
3114 if(state == CACHESTATE_FLUSH)
3116 CacheInfo cache = GetCachingTimes();
3117 if (cache.valid)
3118 state = CACHESTATE_FULL;
3119 else
3120 state = CACHESTATE_INIT;
3123 if(m_caching == state)
3124 return;
3126 CLog::Log(LOGDEBUG, "CVideoPlayer::SetCaching - caching state {}", state);
3127 if (state == CACHESTATE_FULL ||
3128 state == CACHESTATE_INIT)
3130 m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
3132 m_VideoPlayerAudio->SetSpeed(DVD_PLAYSPEED_PAUSE);
3133 m_VideoPlayerVideo->SetSpeed(DVD_PLAYSPEED_PAUSE);
3134 m_streamPlayerSpeed = DVD_PLAYSPEED_PAUSE;
3136 m_cachingTimer.Set(5000ms);
3139 if (state == CACHESTATE_PLAY ||
3140 (state == CACHESTATE_DONE && m_caching != CACHESTATE_PLAY))
3142 m_clock.SetSpeed(m_playSpeed);
3143 m_VideoPlayerAudio->SetSpeed(m_playSpeed);
3144 m_VideoPlayerVideo->SetSpeed(m_playSpeed);
3145 m_streamPlayerSpeed = m_playSpeed;
3147 m_caching = state;
3149 m_clock.SetSpeedAdjust(0);
3152 void CVideoPlayer::SetPlaySpeed(int speed)
3154 if (IsPlaying())
3156 CDVDMsgPlayerSetSpeed::SpeedParams params = { speed, false };
3157 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSpeed>(params));
3159 else
3161 m_playSpeed = speed;
3162 m_streamPlayerSpeed = speed;
3166 bool CVideoPlayer::CanPause() const
3168 std::unique_lock<CCriticalSection> lock(m_StateSection);
3169 return m_State.canpause;
3172 void CVideoPlayer::Pause()
3174 // toggle between pause and normal speed
3175 if (m_processInfo->GetNewSpeed() == 0)
3177 SetSpeed(1);
3179 else
3181 SetSpeed(0);
3185 bool CVideoPlayer::HasVideo() const
3187 return m_HasVideo;
3190 bool CVideoPlayer::HasAudio() const
3192 return m_HasAudio;
3195 bool CVideoPlayer::HasRDS() const
3197 return m_CurrentRadioRDS.id >= 0;
3200 bool CVideoPlayer::HasID3() const
3202 return m_CurrentAudioID3.id >= 0;
3205 bool CVideoPlayer::IsPassthrough() const
3207 return m_VideoPlayerAudio->IsPassthrough();
3210 bool CVideoPlayer::CanSeek() const
3212 std::unique_lock<CCriticalSection> lock(m_StateSection);
3213 return m_State.canseek;
3216 void CVideoPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
3218 if (!m_State.canseek)
3219 return;
3221 if (bLargeStep && bChapterOverride && GetChapter() > 0 && GetChapterCount() > 1)
3223 if (!bPlus)
3225 SeekChapter(GetPreviousChapter());
3226 return;
3228 else if (GetChapter() < GetChapterCount())
3230 SeekChapter(GetChapter() + 1);
3231 return;
3235 int64_t seekTarget;
3236 const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
3237 if (advancedSettings->m_videoUseTimeSeeking && m_processInfo->GetMaxTime() > 2000 * advancedSettings->m_videoTimeSeekForwardBig)
3239 if (bLargeStep)
3240 seekTarget = bPlus ? advancedSettings->m_videoTimeSeekForwardBig :
3241 advancedSettings->m_videoTimeSeekBackwardBig;
3242 else
3243 seekTarget = bPlus ? advancedSettings->m_videoTimeSeekForward :
3244 advancedSettings->m_videoTimeSeekBackward;
3245 seekTarget *= 1000;
3246 seekTarget += GetTime();
3248 else
3250 int percent;
3251 if (bLargeStep)
3252 percent = bPlus ? advancedSettings->m_videoPercentSeekForwardBig : advancedSettings->m_videoPercentSeekBackwardBig;
3253 else
3254 percent = bPlus ? advancedSettings->m_videoPercentSeekForward : advancedSettings->m_videoPercentSeekBackward;
3255 seekTarget = static_cast<int64_t>(m_processInfo->GetMaxTime() * (GetPercentage() + percent) / 100);
3258 bool restore = true;
3260 int64_t time = GetTime();
3261 if(g_application.CurrentFileItem().IsStack() &&
3262 (seekTarget > m_processInfo->GetMaxTime() || seekTarget < 0))
3264 g_application.SeekTime((seekTarget - time) * 0.001 + g_application.GetTime());
3265 // warning, don't access any VideoPlayer variables here as
3266 // the VideoPlayer object may have been destroyed
3267 return;
3270 CDVDMsgPlayerSeek::CMode mode;
3271 mode.time = (int)seekTarget;
3272 mode.backward = !bPlus;
3273 mode.accurate = false;
3274 mode.restore = restore;
3275 mode.trickplay = false;
3276 mode.sync = true;
3278 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3279 SynchronizeDemuxer();
3280 if (seekTarget < 0)
3281 seekTarget = 0;
3282 m_callback.OnPlayBackSeek(seekTarget, seekTarget - time);
3285 bool CVideoPlayer::SeekScene(Direction seekDirection)
3287 if (!m_Edl.HasSceneMarker())
3288 return false;
3291 * There is a 5 second grace period applied when seeking for scenes backwards. If there is no
3292 * grace period applied it is impossible to go backwards past a scene marker.
3294 auto clock = std::chrono::milliseconds(GetTime());
3295 if (seekDirection == Direction::BACKWARD && clock > 5s) // 5 seconds
3296 clock -= 5s;
3298 const std::optional<std::chrono::milliseconds> sceneMarker =
3299 m_Edl.GetNextSceneMarker(seekDirection, clock);
3300 if (sceneMarker)
3303 * Seeking is flushed and inaccurate, just like Seek()
3305 CDVDMsgPlayerSeek::CMode mode;
3306 mode.time = sceneMarker.value().count();
3307 mode.backward = seekDirection == Direction::BACKWARD;
3308 mode.accurate = false;
3309 mode.restore = false;
3310 mode.trickplay = false;
3311 mode.sync = true;
3313 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3314 SynchronizeDemuxer();
3315 return true;
3317 return false;
3320 void CVideoPlayer::GetGeneralInfo(std::string& strGeneralInfo)
3322 if (!m_bStop)
3324 double apts = m_VideoPlayerAudio->GetCurrentPts();
3325 double vpts = m_VideoPlayerVideo->GetCurrentPts();
3326 double dDiff = 0;
3328 if (apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE)
3329 dDiff = (apts - vpts) / DVD_TIME_BASE;
3331 std::string strBuf;
3332 std::unique_lock<CCriticalSection> lock(m_StateSection);
3333 if (m_State.cache_bytes >= 0)
3335 strBuf += StringUtils::Format("forward: {} / {:2.0f}% / {:6.3f}s / {:.3f}%",
3336 StringUtils::SizeToString(m_State.cache_bytes),
3337 m_State.cache_level * 100.0, m_State.cache_time,
3338 m_State.cache_offset * 100.0);
3341 strGeneralInfo = StringUtils::Format("Player: a/v:{: 6.3f}, {}", dDiff, strBuf);
3345 void CVideoPlayer::SeekPercentage(float iPercent)
3347 int64_t iTotalTime = m_processInfo->GetMaxTime();
3349 if (!iTotalTime)
3350 return;
3352 SeekTime((int64_t)(iTotalTime * iPercent / 100));
3355 float CVideoPlayer::GetPercentage()
3357 int64_t iTotalTime = m_processInfo->GetMaxTime();
3359 if (!iTotalTime)
3360 return 0.0f;
3362 return GetTime() * 100 / (float)iTotalTime;
3365 float CVideoPlayer::GetCachePercentage() const
3367 std::unique_lock<CCriticalSection> lock(m_StateSection);
3368 return (float) (m_State.cache_offset * 100); // NOTE: Percentage returned is relative
3371 void CVideoPlayer::SetAVDelay(float fValue)
3373 m_processInfo->GetVideoSettingsLocked().SetAudioDelay(fValue);
3374 m_renderManager.SetDelay(static_cast<int>(fValue * 1000.0f));
3377 float CVideoPlayer::GetAVDelay()
3379 return static_cast<float>(m_renderManager.GetDelay()) / 1000.0f;
3382 void CVideoPlayer::SetSubTitleDelay(float fValue)
3384 m_processInfo->GetVideoSettingsLocked().SetSubtitleDelay(fValue);
3385 m_VideoPlayerVideo->SetSubtitleDelay(static_cast<double>(-fValue) * DVD_TIME_BASE);
3388 float CVideoPlayer::GetSubTitleDelay()
3390 return (float) -m_VideoPlayerVideo->GetSubtitleDelay() / DVD_TIME_BASE;
3393 bool CVideoPlayer::GetSubtitleVisible() const
3395 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3397 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
3398 return pStream->IsSubtitleStreamEnabled();
3401 return m_VideoPlayerVideo->IsSubtitleEnabled();
3404 void CVideoPlayer::SetSubtitleVisible(bool bVisible)
3406 m_messenger.Put(
3407 std::make_shared<CDVDMsgBool>(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
3408 m_processInfo->GetVideoSettingsLocked().SetSubtitleVisible(bVisible);
3411 void CVideoPlayer::SetEnableStream(CCurrentStream& current, bool isEnabled)
3413 if (m_pDemuxer && STREAM_SOURCE_MASK(current.source) == STREAM_SOURCE_DEMUX)
3414 m_pDemuxer->EnableStream(current.demuxerId, current.id, isEnabled);
3417 void CVideoPlayer::SetSubtitleVisibleInternal(bool bVisible)
3419 m_VideoPlayerVideo->EnableSubtitle(bVisible);
3421 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3422 std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream)->EnableSubtitleStream(bVisible);
3424 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3427 void CVideoPlayer::SetSubtitleVerticalPosition(int value, bool save)
3429 m_processInfo->GetVideoSettingsLocked().SetSubtitleVerticalPosition(value, save);
3430 m_renderManager.SetSubtitleVerticalPosition(value, save);
3433 std::shared_ptr<TextCacheStruct_t> CVideoPlayer::GetTeletextCache()
3435 if (m_CurrentTeletext.id < 0)
3436 return nullptr;
3438 return m_VideoPlayerTeletext->GetTeletextCache();
3441 bool CVideoPlayer::HasTeletextCache() const
3443 return m_CurrentTeletext.id >= 0;
3446 void CVideoPlayer::LoadPage(int p, int sp, unsigned char* buffer)
3448 if (m_CurrentTeletext.id < 0)
3449 return;
3451 return m_VideoPlayerTeletext->LoadPage(p, sp, buffer);
3454 void CVideoPlayer::SeekTime(int64_t iTime)
3456 int64_t seekOffset = iTime - GetTime();
3458 CDVDMsgPlayerSeek::CMode mode;
3459 mode.time = static_cast<double>(iTime);
3460 mode.backward = true;
3461 mode.accurate = true;
3462 mode.trickplay = false;
3463 mode.sync = true;
3465 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3466 SynchronizeDemuxer();
3467 m_callback.OnPlayBackSeek(iTime, seekOffset);
3468 m_processInfo->SeekFinished(seekOffset);
3471 bool CVideoPlayer::SeekTimeRelative(int64_t iTime)
3473 int64_t abstime = GetTime() + iTime;
3475 // if the file has EDL cuts we can't rely on m_clock for relative seeks
3476 // EDL cuts remove time from the original file, hence we might seek to
3477 // positions too far from the current m_clock position. Seek to absolute
3478 // time instead
3479 if (m_Edl.HasCuts())
3481 SeekTime(abstime);
3482 return true;
3485 CDVDMsgPlayerSeek::CMode mode;
3486 mode.time = (int)iTime;
3487 mode.relative = true;
3488 mode.backward = (iTime < 0) ? true : false;
3489 mode.accurate = false;
3490 mode.trickplay = false;
3491 mode.sync = true;
3493 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeek>(mode));
3494 m_processInfo->SetStateSeeking(true);
3496 m_callback.OnPlayBackSeek(abstime, iTime);
3497 m_processInfo->SeekFinished(iTime);
3498 return true;
3501 // return the time in milliseconds
3502 int64_t CVideoPlayer::GetTime()
3504 std::unique_lock<CCriticalSection> lock(m_StateSection);
3505 return llrint(m_State.time);
3508 void CVideoPlayer::SetSpeed(float speed)
3510 // can't rewind in menu as seeking isn't possible
3511 // forward is fine
3512 if (speed < 0 && IsInMenu())
3513 return;
3515 if (!CanSeek() && !CanPause())
3516 return;
3518 int iSpeed = static_cast<int>(speed * DVD_PLAYSPEED_NORMAL);
3520 if (!CanSeek())
3522 if ((iSpeed != DVD_PLAYSPEED_NORMAL) && (iSpeed != DVD_PLAYSPEED_PAUSE))
3523 return;
3526 float currentSpeed = m_processInfo->GetNewSpeed();
3527 m_processInfo->SetNewSpeed(speed);
3528 if (iSpeed != currentSpeed)
3530 if (iSpeed == DVD_PLAYSPEED_NORMAL)
3531 m_callback.OnPlayBackResumed();
3532 else if (iSpeed == DVD_PLAYSPEED_PAUSE)
3533 m_callback.OnPlayBackPaused();
3535 if (iSpeed == DVD_PLAYSPEED_NORMAL)
3537 float currentTempo = m_processInfo->GetNewTempo();
3538 if (currentTempo != 1.0f)
3540 SetTempo(currentTempo);
3541 return;
3544 SetPlaySpeed(iSpeed);
3548 void CVideoPlayer::SetTempo(float tempo)
3550 tempo = floor(tempo * 100.0f + 0.5f) / 100.0f;
3551 if (m_processInfo->IsTempoAllowed(tempo))
3553 int speed = tempo * DVD_PLAYSPEED_NORMAL;
3554 CDVDMsgPlayerSetSpeed::SpeedParams params = { speed, true };
3555 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSpeed>(params));
3557 m_processInfo->SetNewTempo(tempo);
3561 void CVideoPlayer::FrameAdvance(int frames)
3563 float currentSpeed = m_processInfo->GetNewSpeed();
3564 if (currentSpeed != DVD_PLAYSPEED_PAUSE)
3565 return;
3567 m_messenger.Put(std::make_shared<CDVDMsgInt>(CDVDMsg::PLAYER_FRAME_ADVANCE, frames));
3570 bool CVideoPlayer::SupportsTempo() const
3572 return m_State.cantempo;
3575 bool CVideoPlayer::OpenStream(CCurrentStream& current, int64_t demuxerId, int iStream, int source, bool reset /*= true*/)
3577 CDemuxStream* stream = NULL;
3578 CDVDStreamInfo hint;
3580 CLog::Log(LOGINFO, "Opening stream: {} source: {}", iStream, source);
3582 if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
3584 int index = m_SelectionStreams.TypeIndexOf(current.type, source, demuxerId, iStream);
3585 if (index < 0)
3586 return false;
3587 const SelectionStream& st = m_SelectionStreams.Get(current.type, index);
3589 CLog::Log(LOGINFO, "Opening Subtitle file: {}", CURL::GetRedacted(st.filename));
3590 m_pSubtitleDemuxer.reset();
3591 const auto demux = m_subtitleDemuxerMap.find(demuxerId);
3592 if (demux == m_subtitleDemuxerMap.end())
3594 CLog::Log(LOGINFO, "No demuxer found for file {}", CURL::GetRedacted(st.filename));
3595 return false;
3598 m_pSubtitleDemuxer = demux->second;
3600 double pts = m_VideoPlayerVideo->GetCurrentPts();
3601 if(pts == DVD_NOPTS_VALUE)
3602 pts = m_CurrentVideo.dts;
3603 if(pts == DVD_NOPTS_VALUE)
3604 pts = 0;
3605 pts += m_offset_pts;
3606 if (!m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE)))
3607 CLog::Log(LOGDEBUG, "{} - failed to start subtitle demuxing from: {:f}", __FUNCTION__, pts);
3608 stream = m_pSubtitleDemuxer->GetStream(demuxerId, iStream);
3609 if(!stream || stream->disabled)
3610 return false;
3612 m_pSubtitleDemuxer->EnableStream(demuxerId, iStream, true);
3614 hint.Assign(*stream, true);
3616 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
3618 int index = m_SelectionStreams.TypeIndexOf(current.type, source, demuxerId, iStream);
3619 if(index < 0)
3620 return false;
3622 hint.Clear();
3623 hint.filename = m_SelectionStreams.Get(current.type, index).filename;
3624 hint.fpsscale = m_CurrentVideo.hint.fpsscale;
3625 hint.fpsrate = m_CurrentVideo.hint.fpsrate;
3627 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX)
3629 if(!m_pDemuxer)
3630 return false;
3632 m_pDemuxer->OpenStream(demuxerId, iStream);
3634 stream = m_pDemuxer->GetStream(demuxerId, iStream);
3635 if (!stream || stream->disabled)
3636 return false;
3638 hint.Assign(*stream, true);
3640 if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3641 hint.filename = "dvd";
3643 else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_VIDEOMUX)
3645 if(!m_pCCDemuxer)
3646 return false;
3648 stream = m_pCCDemuxer->GetStream(iStream);
3649 if(!stream || stream->disabled)
3650 return false;
3652 hint.Assign(*stream, false);
3655 bool res;
3656 switch(current.type)
3658 case STREAM_AUDIO:
3659 res = OpenAudioStream(hint, reset);
3660 break;
3661 case STREAM_VIDEO:
3662 res = OpenVideoStream(hint, reset);
3663 break;
3664 case STREAM_SUBTITLE:
3665 res = OpenSubtitleStream(hint);
3666 break;
3667 case STREAM_TELETEXT:
3668 res = OpenTeletextStream(hint);
3669 break;
3670 case STREAM_RADIO_RDS:
3671 res = OpenRadioRDSStream(hint);
3672 break;
3673 case STREAM_AUDIO_ID3:
3674 res = OpenAudioID3Stream(hint);
3675 break;
3676 default:
3677 res = false;
3678 break;
3681 if (res)
3683 int oldId = current.id;
3684 current.id = iStream;
3685 current.demuxerId = demuxerId;
3686 current.source = source;
3687 current.hint = hint;
3688 current.stream = (void*)stream;
3689 current.lastdts = DVD_NOPTS_VALUE;
3690 if (oldId >= 0 && current.avsync != CCurrentStream::AV_SYNC_FORCE)
3691 current.avsync = CCurrentStream::AV_SYNC_CHECK;
3692 if(stream)
3693 current.changes = stream->changes;
3695 else
3697 if(stream)
3699 /* mark stream as disabled, to disallow further attempts*/
3700 CLog::Log(LOGWARNING, "{} - Unsupported stream {}. Stream disabled.", __FUNCTION__,
3701 stream->uniqueId);
3702 stream->disabled = true;
3706 UpdateContentState();
3707 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
3708 CServiceBroker::GetDataCacheCore().SignalVideoInfoChange();
3709 CServiceBroker::GetDataCacheCore().SignalSubtitleInfoChange();
3711 return res;
3714 bool CVideoPlayer::OpenAudioStream(CDVDStreamInfo& hint, bool reset)
3716 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentAudio.player);
3717 if(player == nullptr)
3718 return false;
3720 if(m_CurrentAudio.id < 0 ||
3721 m_CurrentAudio.hint != hint)
3723 if (!player->OpenStream(hint))
3724 return false;
3726 player->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, m_displayLost), 1);
3728 static_cast<IDVDStreamPlayerAudio*>(player)->SetSpeed(m_streamPlayerSpeed);
3729 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_STARTING;
3730 m_CurrentAudio.packets = 0;
3732 else if (reset)
3733 player->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_RESET), 0);
3735 m_HasAudio = true;
3737 static_cast<IDVDStreamPlayerAudio*>(player)->SendMessage(
3738 std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_REQUEST_STATE), 1);
3740 return true;
3743 bool CVideoPlayer::OpenVideoStream(CDVDStreamInfo& hint, bool reset)
3745 if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
3747 /* set aspect ratio as requested by navigator for dvd's */
3748 float aspect = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream)->GetVideoAspectRatio();
3749 if (aspect != 0.0f)
3751 hint.aspect = static_cast<double>(aspect);
3752 hint.forced_aspect = true;
3754 hint.dvd = true;
3756 else if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
3758 // set framerate if not set by demuxer
3759 if (hint.fpsrate == 0 || hint.fpsscale == 0)
3761 int fpsidx = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_PVRPLAYBACK_FPS);
3762 if (fpsidx == 1)
3764 hint.fpsscale = 1000;
3765 hint.fpsrate = 50000;
3767 else if (fpsidx == 2)
3769 hint.fpsscale = 1001;
3770 hint.fpsrate = 60000;
3775 std::shared_ptr<CDVDInputStream::IMenus> pMenus = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
3776 if(pMenus && pMenus->IsInMenu())
3777 hint.stills = true;
3779 if (hint.stereo_mode.empty())
3781 CGUIComponent *gui = CServiceBroker::GetGUI();
3782 if (gui != nullptr)
3784 const CStereoscopicsManager &stereoscopicsManager = gui->GetStereoscopicsManager();
3785 hint.stereo_mode = stereoscopicsManager.DetectStereoModeByString(m_item.GetPath());
3789 if (hint.flags & AV_DISPOSITION_ATTACHED_PIC)
3790 return false;
3792 // set desired refresh rate
3793 if (m_CurrentVideo.id < 0 && m_playerOptions.fullscreen &&
3794 CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot() && hint.fpsrate != 0 &&
3795 hint.fpsscale != 0)
3797 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) != ADJUST_REFRESHRATE_OFF)
3799 const double framerate = DVD_TIME_BASE / CDVDCodecUtils::NormalizeFrameduration(
3800 (double)DVD_TIME_BASE * hint.fpsscale /
3801 (hint.fpsrate * (hint.interlaced ? 2 : 1)));
3803 RESOLUTION res = CResolutionUtils::ChooseBestResolution(static_cast<float>(framerate), hint.width, hint.height, !hint.stereo_mode.empty());
3804 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(res, false);
3805 m_renderManager.TriggerUpdateResolution(framerate, hint.width, hint.height, hint.stereo_mode);
3809 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentVideo.player);
3810 if(player == nullptr)
3811 return false;
3813 if(m_CurrentVideo.id < 0 ||
3814 m_CurrentVideo.hint != hint)
3816 if (hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_H264)
3817 m_pCCDemuxer.reset();
3819 if (!player->OpenStream(hint))
3820 return false;
3822 player->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, m_displayLost), 1);
3824 // look for any EDL files
3825 m_Edl.Clear();
3826 float fFramesPerSecond = 0.0f;
3827 if (m_CurrentVideo.hint.fpsscale > 0.0f)
3828 fFramesPerSecond = static_cast<float>(m_CurrentVideo.hint.fpsrate) / static_cast<float>(m_CurrentVideo.hint.fpsscale);
3829 m_Edl.ReadEditDecisionLists(m_item, fFramesPerSecond);
3830 CServiceBroker::GetDataCacheCore().SetEditList(m_Edl.GetEditList());
3831 CServiceBroker::GetDataCacheCore().SetCuts(m_Edl.GetCutMarkers());
3832 CServiceBroker::GetDataCacheCore().SetSceneMarkers(m_Edl.GetSceneMarkers());
3834 static_cast<IDVDStreamPlayerVideo*>(player)->SetSpeed(m_streamPlayerSpeed);
3835 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_STARTING;
3836 m_CurrentVideo.packets = 0;
3838 else if (reset)
3839 player->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_RESET), 0);
3841 m_HasVideo = true;
3843 static_cast<IDVDStreamPlayerVideo*>(player)->SendMessage(
3844 std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_REQUEST_STATE), 1);
3846 // open CC demuxer if video is mpeg2
3847 if ((hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_H264) && !m_pCCDemuxer)
3849 m_pCCDemuxer = std::make_unique<CDVDDemuxCC>(hint.codec);
3850 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_VIDEOMUX);
3853 return true;
3856 bool CVideoPlayer::OpenSubtitleStream(const CDVDStreamInfo& hint)
3858 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentSubtitle.player);
3859 if(player == nullptr)
3860 return false;
3862 if(m_CurrentSubtitle.id < 0 ||
3863 m_CurrentSubtitle.hint != hint)
3865 if (!player->OpenStream(hint))
3866 return false;
3869 return true;
3872 void CVideoPlayer::AdaptForcedSubtitles()
3874 SelectionStream ss = m_SelectionStreams.Get(STREAM_SUBTITLE, GetSubtitle());
3875 if (ss.flags & StreamFlags::FLAG_FORCED)
3877 SelectionStream as = m_SelectionStreams.Get(STREAM_AUDIO, GetAudioStream());
3878 bool isVisible = false;
3879 for (const auto &stream : m_SelectionStreams.Get(STREAM_SUBTITLE))
3881 if (stream.flags & StreamFlags::FLAG_FORCED && g_LangCodeExpander.CompareISO639Codes(stream.language, as.language))
3883 if (OpenStream(m_CurrentSubtitle, stream.demuxerId, stream.id, stream.source))
3885 isVisible = true;
3886 break;
3890 // SetEnableStream only if not visible, when visible OpenStream already implied that stream is enabled
3891 if (!isVisible)
3892 SetEnableStream(m_CurrentSubtitle, false);
3894 SetSubtitleVisibleInternal(isVisible);
3898 bool CVideoPlayer::OpenTeletextStream(CDVDStreamInfo& hint)
3900 if (!m_VideoPlayerTeletext->CheckStream(hint))
3901 return false;
3903 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentTeletext.player);
3904 if(player == nullptr)
3905 return false;
3907 if(m_CurrentTeletext.id < 0 ||
3908 m_CurrentTeletext.hint != hint)
3910 if (!player->OpenStream(hint))
3911 return false;
3914 return true;
3917 bool CVideoPlayer::OpenRadioRDSStream(CDVDStreamInfo& hint)
3919 if (!m_VideoPlayerRadioRDS->CheckStream(hint))
3920 return false;
3922 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentRadioRDS.player);
3923 if(player == nullptr)
3924 return false;
3926 if(m_CurrentRadioRDS.id < 0 ||
3927 m_CurrentRadioRDS.hint != hint)
3929 if (!player->OpenStream(hint))
3930 return false;
3933 return true;
3936 bool CVideoPlayer::OpenAudioID3Stream(CDVDStreamInfo& hint)
3938 if (!m_VideoPlayerAudioID3->CheckStream(hint))
3939 return false;
3941 IDVDStreamPlayer* player = GetStreamPlayer(m_CurrentAudioID3.player);
3942 if (player == nullptr)
3943 return false;
3945 if (m_CurrentAudioID3.id < 0 || m_CurrentAudioID3.hint != hint)
3947 if (!player->OpenStream(hint))
3948 return false;
3951 return true;
3954 bool CVideoPlayer::CloseStream(CCurrentStream& current, bool bWaitForBuffers)
3956 if (current.id < 0)
3957 return false;
3959 CLog::Log(LOGINFO, "Closing stream player {}", current.player);
3961 if(bWaitForBuffers)
3962 SetCaching(CACHESTATE_DONE);
3964 SetEnableStream(current, false);
3966 IDVDStreamPlayer* player = GetStreamPlayer(current.player);
3967 if (player)
3969 if ((current.type == STREAM_AUDIO && current.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
3970 (current.type == STREAM_VIDEO && current.syncState != IDVDStreamPlayer::SYNC_INSYNC) ||
3971 m_bAbortRequest)
3972 bWaitForBuffers = false;
3973 player->CloseStream(bWaitForBuffers);
3976 current.Clear();
3977 return true;
3980 void CVideoPlayer::FlushBuffers(double pts, bool accurate, bool sync)
3982 CLog::Log(LOGDEBUG, "CVideoPlayer::FlushBuffers - flushing buffers");
3984 double startpts;
3985 if (accurate)
3986 startpts = pts;
3987 else
3988 startpts = DVD_NOPTS_VALUE;
3990 m_SpeedState.Reset(pts);
3992 if (sync)
3994 m_CurrentAudio.inited = false;
3995 m_CurrentAudio.avsync = CCurrentStream::AV_SYNC_FORCE;
3996 m_CurrentAudio.starttime = DVD_NOPTS_VALUE;
3997 m_CurrentVideo.inited = false;
3998 m_CurrentVideo.avsync = CCurrentStream::AV_SYNC_FORCE;
3999 m_CurrentVideo.starttime = DVD_NOPTS_VALUE;
4000 m_CurrentSubtitle.inited = false;
4001 m_CurrentTeletext.inited = false;
4002 m_CurrentRadioRDS.inited = false;
4005 m_CurrentAudio.dts = DVD_NOPTS_VALUE;
4006 m_CurrentAudio.startpts = startpts;
4007 m_CurrentAudio.packets = 0;
4009 m_CurrentVideo.dts = DVD_NOPTS_VALUE;
4010 m_CurrentVideo.startpts = startpts;
4011 m_CurrentVideo.packets = 0;
4013 m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
4014 m_CurrentSubtitle.startpts = startpts;
4015 m_CurrentSubtitle.packets = 0;
4017 m_CurrentTeletext.dts = DVD_NOPTS_VALUE;
4018 m_CurrentTeletext.startpts = startpts;
4019 m_CurrentTeletext.packets = 0;
4021 m_CurrentRadioRDS.dts = DVD_NOPTS_VALUE;
4022 m_CurrentRadioRDS.startpts = startpts;
4023 m_CurrentRadioRDS.packets = 0;
4025 m_CurrentAudioID3.dts = DVD_NOPTS_VALUE;
4026 m_CurrentAudioID3.startpts = startpts;
4027 m_CurrentAudioID3.packets = 0;
4029 m_VideoPlayerAudio->Flush(sync);
4030 m_VideoPlayerVideo->Flush(sync);
4031 m_VideoPlayerSubtitle->Flush();
4032 m_VideoPlayerTeletext->Flush();
4033 m_VideoPlayerRadioRDS->Flush();
4034 m_VideoPlayerAudioID3->Flush();
4036 if (m_playSpeed == DVD_PLAYSPEED_NORMAL || m_playSpeed == DVD_PLAYSPEED_PAUSE ||
4037 (m_playSpeed >= DVD_PLAYSPEED_NORMAL * m_processInfo->MinTempoPlatform() &&
4038 m_playSpeed <= DVD_PLAYSPEED_NORMAL * m_processInfo->MaxTempoPlatform()))
4040 // make sure players are properly flushed, should put them in stalled state
4041 auto msg = std::make_shared<CDVDMsgGeneralSynchronize>(1s, SYNCSOURCE_AUDIO | SYNCSOURCE_VIDEO);
4042 m_VideoPlayerAudio->SendMessage(msg, 1);
4043 m_VideoPlayerVideo->SendMessage(msg, 1);
4044 msg->Wait(m_bStop, 0);
4046 // purge any pending PLAYER_STARTED messages
4047 m_messenger.Flush(CDVDMsg::PLAYER_STARTED);
4049 // we should now wait for init cache
4050 SetCaching(CACHESTATE_FLUSH);
4051 if (sync)
4053 m_CurrentAudio.syncState = IDVDStreamPlayer::SYNC_STARTING;
4054 m_CurrentVideo.syncState = IDVDStreamPlayer::SYNC_STARTING;
4058 if(pts != DVD_NOPTS_VALUE && sync)
4059 m_clock.Discontinuity(pts);
4060 UpdatePlayState(0);
4062 m_demuxerSpeed = DVD_PLAYSPEED_NORMAL;
4063 if (m_pDemuxer)
4064 m_pDemuxer->SetSpeed(DVD_PLAYSPEED_NORMAL);
4067 // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
4068 int CVideoPlayer::OnDiscNavResult(void* pData, int iMessage)
4070 if (!m_pInputStream)
4071 return 0;
4073 #if defined(HAVE_LIBBLURAY)
4074 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_BLURAY))
4076 switch (iMessage)
4078 case BD_EVENT_MENU_OVERLAY:
4079 m_overlayContainer.ProcessAndAddOverlayIfValid(
4080 *static_cast<std::shared_ptr<CDVDOverlay>*>(pData));
4081 break;
4082 case BD_EVENT_PLAYLIST_STOP:
4083 m_dvd.state = DVDSTATE_NORMAL;
4084 m_dvd.iDVDStillTime = 0ms;
4085 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::GENERAL_FLUSH));
4086 break;
4087 case BD_EVENT_AUDIO_STREAM:
4088 m_dvd.iSelectedAudioStream = *static_cast<int*>(pData);
4089 break;
4091 case BD_EVENT_PG_TEXTST_STREAM:
4092 m_dvd.iSelectedSPUStream = *static_cast<int*>(pData);
4093 break;
4094 case BD_EVENT_PG_TEXTST:
4096 bool enable = (*static_cast<int*>(pData) != 0);
4097 m_VideoPlayerVideo->EnableSubtitle(enable);
4099 break;
4100 case BD_EVENT_STILL_TIME:
4102 if (m_dvd.state != DVDSTATE_STILL)
4104 // else notify the player we have received a still frame
4106 m_dvd.iDVDStillTime = std::chrono::milliseconds(*static_cast<int*>(pData));
4107 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4109 if (m_dvd.iDVDStillTime > 0ms)
4110 m_dvd.iDVDStillTime *= 1000;
4112 /* adjust for the output delay in the video queue */
4113 std::chrono::milliseconds time = 0ms;
4114 if (m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0ms)
4116 time = std::chrono::milliseconds(
4117 static_cast<int>(m_VideoPlayerVideo->GetOutputDelay() / (DVD_TIME_BASE / 1000)));
4118 if (time < 10000ms && time > 0ms)
4119 m_dvd.iDVDStillTime += time;
4121 m_dvd.state = DVDSTATE_STILL;
4122 CLog::Log(LOGDEBUG, "BD_EVENT_STILL_TIME - waiting {} msec, with delay of {} msec",
4123 m_dvd.iDVDStillTime.count(), time.count());
4126 break;
4127 case BD_EVENT_STILL:
4129 bool on = static_cast<bool>(*static_cast<int*>(pData));
4130 if (on && m_dvd.state != DVDSTATE_STILL)
4132 m_dvd.state = DVDSTATE_STILL;
4133 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4134 m_dvd.iDVDStillTime = 0ms;
4135 CLog::Log(LOGDEBUG, "CDVDPlayer::OnDVDNavResult - libbluray DVDSTATE_STILL start");
4137 else if (!on && m_dvd.state == DVDSTATE_STILL)
4139 m_dvd.state = DVDSTATE_NORMAL;
4140 m_dvd.iDVDStillStartTime = {};
4141 m_dvd.iDVDStillTime = 0ms;
4142 CLog::Log(LOGDEBUG, "CDVDPlayer::OnDVDNavResult - libbluray DVDSTATE_STILL end");
4145 break;
4146 case BD_EVENT_MENU_ERROR:
4148 m_dvd.state = DVDSTATE_NORMAL;
4149 CLog::Log(LOGDEBUG, "CVideoPlayer::OnDiscNavResult - libbluray menu not supported (DVDSTATE_NORMAL)");
4150 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnBlurayMenuError");
4152 break;
4153 case BD_EVENT_ENC_ERROR:
4155 m_dvd.state = DVDSTATE_NORMAL;
4156 CLog::Log(LOGDEBUG, "CVideoPlayer::OnDiscNavResult - libbluray the disc/file is encrypted and can't be played (DVDSTATE_NORMAL)");
4157 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player,
4158 "OnBlurayEncryptedError");
4160 break;
4161 default:
4162 break;
4165 return 0;
4167 #endif
4169 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
4171 std::shared_ptr<CDVDInputStreamNavigator> pStream = std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
4173 switch (iMessage)
4175 case DVDNAV_STILL_FRAME:
4177 //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
4179 dvdnav_still_event_t *still_event = static_cast<dvdnav_still_event_t*>(pData);
4180 // should wait the specified time here while we let the player running
4181 // after that call dvdnav_still_skip(m_dvdnav);
4183 if (m_dvd.state != DVDSTATE_STILL)
4185 // else notify the player we have received a still frame
4187 if(still_event->length < 0xff)
4188 m_dvd.iDVDStillTime = std::chrono::seconds(still_event->length);
4189 else
4190 m_dvd.iDVDStillTime = 0ms;
4192 m_dvd.iDVDStillStartTime = std::chrono::steady_clock::now();
4194 /* adjust for the output delay in the video queue */
4195 std::chrono::milliseconds time = 0ms;
4196 if (m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0ms)
4198 time = std::chrono::milliseconds(
4199 static_cast<int>(m_VideoPlayerVideo->GetOutputDelay() / (DVD_TIME_BASE / 1000)));
4200 if (time < 10000ms && time > 0ms)
4201 m_dvd.iDVDStillTime += time;
4203 m_dvd.state = DVDSTATE_STILL;
4204 CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME - waiting {} sec, with delay of {} msec",
4205 still_event->length, time.count());
4207 return NAVRESULT_HOLD;
4209 break;
4210 case DVDNAV_SPU_CLUT_CHANGE:
4212 m_VideoPlayerSubtitle->SendMessage(
4213 std::make_shared<CDVDMsgSubtitleClutChange>((uint8_t*)pData));
4215 break;
4216 case DVDNAV_SPU_STREAM_CHANGE:
4218 dvdnav_spu_stream_change_event_t* event = static_cast<dvdnav_spu_stream_change_event_t*>(pData);
4220 int iStream = event->physical_wide;
4221 bool visible = !(iStream & 0x80);
4223 SetSubtitleVisibleInternal(visible);
4225 if (iStream >= 0)
4226 m_dvd.iSelectedSPUStream = (iStream & ~0x80);
4227 else
4228 m_dvd.iSelectedSPUStream = -1;
4230 m_CurrentSubtitle.stream = NULL;
4232 break;
4233 case DVDNAV_AUDIO_STREAM_CHANGE:
4235 dvdnav_audio_stream_change_event_t* event = static_cast<dvdnav_audio_stream_change_event_t*>(pData);
4236 // Tell system what audiostream should be opened by default
4237 m_dvd.iSelectedAudioStream = event->physical;
4238 m_CurrentAudio.stream = NULL;
4240 break;
4241 case DVDNAV_HIGHLIGHT:
4243 //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
4244 int iButton = pStream->GetCurrentButton();
4245 CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button {}", iButton);
4246 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_NORMAL);
4248 break;
4249 case DVDNAV_VTS_CHANGE:
4251 //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
4252 CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
4254 //Make sure we clear all the old overlays here, or else old forced items are left.
4255 m_overlayContainer.Clear();
4257 //Force an aspect ratio that is set in the dvdheaders if available
4258 m_CurrentVideo.hint.aspect = static_cast<double>(pStream->GetVideoAspectRatio());
4259 if( m_VideoPlayerVideo->IsInited() )
4260 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgDouble>(
4261 CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
4263 m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
4264 m_SelectionStreams.Update(m_pInputStream, m_pDemuxer.get());
4265 UpdateContent();
4267 return NAVRESULT_HOLD;
4269 break;
4270 case DVDNAV_CELL_CHANGE:
4272 //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
4273 CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
4275 if (m_dvd.state != DVDSTATE_STILL)
4276 m_dvd.state = DVDSTATE_NORMAL;
4278 break;
4279 case DVDNAV_NAV_PACKET:
4281 //pci_t* pci = (pci_t*)pData;
4283 // this should be possible to use to make sure we get
4284 // seamless transitions over these boundaries
4285 // if we remember the old vobunits boundaries
4286 // when a packet comes out of demuxer that has
4287 // pts values outside that boundary, it belongs
4288 // to the new vobunit, which has new timestamps
4289 UpdatePlayState(0);
4291 break;
4292 case DVDNAV_HOP_CHANNEL:
4294 // This event is issued whenever a non-seamless operation has been executed.
4295 // Applications with fifos should drop the fifos content to speed up responsiveness.
4296 CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
4297 if(m_dvd.state == DVDSTATE_SEEK)
4298 m_dvd.state = DVDSTATE_NORMAL;
4299 else
4301 bool sync = !IsInMenuInternal();
4302 FlushBuffers(DVD_NOPTS_VALUE, false, sync);
4303 m_dvd.syncClock = true;
4304 m_dvd.state = DVDSTATE_NORMAL;
4305 if (m_pDemuxer)
4306 m_pDemuxer->Flush();
4309 return NAVRESULT_ERROR;
4311 break;
4312 case DVDNAV_STOP:
4314 CLog::Log(LOGDEBUG, "DVDNAV_STOP");
4315 m_dvd.state = DVDSTATE_NORMAL;
4317 break;
4318 case DVDNAV_ERROR:
4320 CLog::Log(LOGDEBUG, "DVDNAV_ERROR");
4321 m_dvd.state = DVDSTATE_NORMAL;
4322 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player,
4323 "OnPlaybackFailed");
4325 break;
4326 default:
4328 break;
4331 return NAVRESULT_NOP;
4334 void CVideoPlayer::GetVideoResolution(unsigned int &width, unsigned int &height)
4336 RESOLUTION_INFO res = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
4337 width = res.iWidth;
4338 height = res.iHeight;
4341 bool CVideoPlayer::OnAction(const CAction &action)
4343 #define THREAD_ACTION(action) \
4344 do \
4346 if (!IsCurrentThread()) \
4348 m_messenger.Put( \
4349 std::make_shared<CDVDMsgType<CAction>>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
4350 return true; \
4352 } while (false)
4354 std::shared_ptr<CDVDInputStream::IMenus> pMenus = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4355 if (pMenus)
4357 if (m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0ms &&
4358 pMenus->GetTotalButtons() == 0)
4360 switch(action.GetID())
4362 case ACTION_NEXT_ITEM:
4363 case ACTION_MOVE_RIGHT:
4364 case ACTION_MOVE_UP:
4365 case ACTION_SELECT_ITEM:
4367 THREAD_ACTION(action);
4368 /* this will force us out of the stillframe */
4369 CLog::Log(LOGDEBUG, "{} - User asked to exit stillframe", __FUNCTION__);
4370 m_dvd.iDVDStillStartTime = {};
4371 m_dvd.iDVDStillTime = 1ms;
4373 return true;
4378 switch (action.GetID())
4380 /* this code is disabled to allow switching playlist items (dvdimage "stacks") */
4381 #if 0
4382 case ACTION_PREV_ITEM: // SKIP-:
4384 THREAD_ACTION(action);
4385 CLog::Log(LOGDEBUG, " - pushed prev");
4386 pMenus->OnPrevious();
4387 m_processInfo->SeekFinished(0);
4388 return true;
4390 break;
4391 case ACTION_NEXT_ITEM: // SKIP+:
4393 THREAD_ACTION(action);
4394 CLog::Log(LOGDEBUG, " - pushed next");
4395 pMenus->OnNext();
4396 m_processInfo->SeekFinished(0);
4397 return true;
4399 break;
4400 #endif
4401 case ACTION_SHOW_VIDEOMENU: // start button
4403 THREAD_ACTION(action);
4404 CLog::LogF(LOGDEBUG, "Trying to go to the menu");
4405 if (pMenus->OnMenu())
4407 if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
4409 SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
4410 m_callback.OnPlayBackResumed();
4413 // Let everyone know that we've gone to the menu
4414 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnMenu");
4416 return true;
4418 break;
4421 if (pMenus->IsInMenu())
4423 switch (action.GetID())
4425 case ACTION_NEXT_ITEM:
4426 THREAD_ACTION(action);
4427 CLog::Log(LOGDEBUG, " - pushed next in menu, stream will decide");
4428 if (pMenus->CanSeek() && GetChapterCount() > 0 && GetChapter() < GetChapterCount())
4429 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetChapter() + 1));
4430 else
4431 pMenus->OnNext();
4433 m_processInfo->SeekFinished(0);
4434 return true;
4435 case ACTION_PREV_ITEM:
4436 THREAD_ACTION(action);
4437 CLog::Log(LOGDEBUG, " - pushed prev in menu, stream will decide");
4438 if (pMenus->CanSeek() && GetChapterCount() > 0 && GetChapter() > 0)
4439 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetPreviousChapter()));
4440 else
4441 pMenus->OnPrevious();
4443 m_processInfo->SeekFinished(0);
4444 return true;
4445 case ACTION_PREVIOUS_MENU:
4446 case ACTION_NAV_BACK:
4448 THREAD_ACTION(action);
4449 CLog::Log(LOGDEBUG, " - menu back");
4450 pMenus->OnBack();
4452 break;
4453 case ACTION_MOVE_LEFT:
4455 THREAD_ACTION(action);
4456 CLog::Log(LOGDEBUG, " - move left");
4457 pMenus->OnLeft();
4459 break;
4460 case ACTION_MOVE_RIGHT:
4462 THREAD_ACTION(action);
4463 CLog::Log(LOGDEBUG, " - move right");
4464 pMenus->OnRight();
4466 break;
4467 case ACTION_MOVE_UP:
4469 THREAD_ACTION(action);
4470 CLog::Log(LOGDEBUG, " - move up");
4471 pMenus->OnUp();
4473 break;
4474 case ACTION_MOVE_DOWN:
4476 THREAD_ACTION(action);
4477 CLog::Log(LOGDEBUG, " - move down");
4478 pMenus->OnDown();
4480 break;
4482 case ACTION_MOUSE_MOVE:
4483 case ACTION_MOUSE_LEFT_CLICK:
4485 CRect rs, rd, rv;
4486 m_renderManager.GetVideoRect(rs, rd, rv);
4487 CPoint pt(action.GetAmount(), action.GetAmount(1));
4488 if (!rd.PtInRect(pt))
4489 return false; // out of bounds
4490 THREAD_ACTION(action);
4491 // convert to video coords...
4492 pt -= CPoint(rd.x1, rd.y1);
4493 pt.x *= rs.Width() / rd.Width();
4494 pt.y *= rs.Height() / rd.Height();
4495 pt += CPoint(rs.x1, rs.y1);
4496 if (action.GetID() == ACTION_MOUSE_LEFT_CLICK)
4498 if (pMenus->OnMouseClick(pt))
4499 return true;
4500 else
4502 CServiceBroker::GetAppMessenger()->PostMsg(
4503 TMSG_GUI_ACTION, WINDOW_INVALID, -1,
4504 static_cast<void*>(new CAction(ACTION_TRIGGER_OSD)));
4505 return false;
4508 return pMenus->OnMouseMove(pt);
4510 break;
4511 case ACTION_SELECT_ITEM:
4513 THREAD_ACTION(action);
4514 CLog::Log(LOGDEBUG, " - button select");
4515 // show button pushed overlay
4516 if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
4517 m_VideoPlayerSubtitle->UpdateOverlayInfo(std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream), LIBDVDNAV_BUTTON_CLICKED);
4519 pMenus->ActivateButton();
4521 break;
4522 case REMOTE_0:
4523 case REMOTE_1:
4524 case REMOTE_2:
4525 case REMOTE_3:
4526 case REMOTE_4:
4527 case REMOTE_5:
4528 case REMOTE_6:
4529 case REMOTE_7:
4530 case REMOTE_8:
4531 case REMOTE_9:
4533 THREAD_ACTION(action);
4534 // Offset from key codes back to button number
4535 int button = action.GetID() - REMOTE_0;
4536 CLog::Log(LOGDEBUG, " - button pressed {}", button);
4537 pMenus->SelectButton(button);
4539 break;
4540 default:
4541 return false;
4542 break;
4544 return true; // message is handled
4548 pMenus.reset();
4550 switch (action.GetID())
4552 case ACTION_NEXT_ITEM:
4553 if (GetChapter() > 0 && GetChapter() < GetChapterCount())
4555 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetChapter() + 1));
4556 m_processInfo->SeekFinished(0);
4557 return true;
4559 else if (SeekScene(Direction::FORWARD))
4560 return true;
4561 else
4562 break;
4563 case ACTION_PREV_ITEM:
4564 if (GetChapter() > 0)
4566 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(GetPreviousChapter()));
4567 m_processInfo->SeekFinished(0);
4568 return true;
4570 else if (SeekScene(Direction::BACKWARD))
4571 return true;
4572 else
4573 break;
4574 case ACTION_TOGGLE_COMMSKIP:
4575 m_SkipCommercials = !m_SkipCommercials;
4576 CServiceBroker::GetAnnouncementManager()->Announce(
4577 ANNOUNCEMENT::Player, "OnToggleSkipCommercials", CVariant{m_SkipCommercials});
4578 break;
4580 case ACTION_PLAYER_DEBUG:
4581 m_renderManager.ToggleDebug();
4582 break;
4583 case ACTION_PLAYER_DEBUG_VIDEO:
4584 m_renderManager.ToggleDebugVideo();
4585 break;
4587 case ACTION_PLAYER_PROCESS_INFO:
4588 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnProcessInfo");
4589 return true;
4592 // return false to inform the caller we didn't handle the message
4593 return false;
4596 bool CVideoPlayer::IsInMenuInternal() const
4598 std::shared_ptr<CDVDInputStream::IMenus> pStream = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4599 if (pStream)
4601 if (m_dvd.state == DVDSTATE_STILL)
4602 return true;
4603 else
4604 return pStream->IsInMenu();
4606 return false;
4610 bool CVideoPlayer::IsInMenu() const
4612 std::unique_lock<CCriticalSection> lock(m_StateSection);
4613 return m_State.isInMenu;
4616 MenuType CVideoPlayer::GetSupportedMenuType() const
4618 std::unique_lock<CCriticalSection> lock(m_StateSection);
4619 return m_State.menuType;
4622 std::string CVideoPlayer::GetPlayerState()
4624 std::unique_lock<CCriticalSection> lock(m_StateSection);
4625 return m_State.player_state;
4628 bool CVideoPlayer::SetPlayerState(const std::string& state)
4630 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetState>(state));
4631 return true;
4634 int CVideoPlayer::GetChapterCount() const
4636 std::unique_lock<CCriticalSection> lock(m_StateSection);
4637 return m_State.chapters.size();
4640 int CVideoPlayer::GetChapter() const
4642 std::unique_lock<CCriticalSection> lock(m_StateSection);
4643 return m_State.chapter;
4646 void CVideoPlayer::GetChapterName(std::string& strChapterName, int chapterIdx) const
4648 std::unique_lock<CCriticalSection> lock(m_StateSection);
4649 if (chapterIdx == -1 && m_State.chapter > 0 && m_State.chapter <= (int) m_State.chapters.size())
4650 strChapterName = m_State.chapters[m_State.chapter - 1].first;
4651 else if (chapterIdx > 0 && chapterIdx <= (int) m_State.chapters.size())
4652 strChapterName = m_State.chapters[chapterIdx - 1].first;
4655 int CVideoPlayer::SeekChapter(int iChapter)
4657 if (GetChapter() > 0)
4659 if (iChapter < 0)
4660 iChapter = 0;
4661 if (iChapter > GetChapterCount())
4662 return 0;
4664 // Seek to the chapter.
4665 m_messenger.Put(std::make_shared<CDVDMsgPlayerSeekChapter>(iChapter));
4666 SynchronizeDemuxer();
4669 return 0;
4672 int64_t CVideoPlayer::GetChapterPos(int chapterIdx) const
4674 std::unique_lock<CCriticalSection> lock(m_StateSection);
4675 if (chapterIdx > 0 && chapterIdx <= (int) m_State.chapters.size())
4676 return m_State.chapters[chapterIdx - 1].second;
4678 return -1;
4681 int CVideoPlayer::GetPreviousChapter()
4683 // 5-second grace period from chapter start to skip backwards to previous chapter
4684 // Afterwards skip to start of current chapter.
4685 const int chapter = GetChapter();
4687 if (chapter > 0 && (GetTime() < (GetChapterPos(chapter) + 5) * 1000))
4688 return chapter - 1;
4689 else
4690 return chapter;
4693 void CVideoPlayer::AddSubtitle(const std::string& strSubPath)
4695 m_messenger.Put(
4696 std::make_shared<CDVDMsgType<std::string>>(CDVDMsg::SUBTITLE_ADDFILE, strSubPath));
4699 bool CVideoPlayer::IsCaching() const
4701 std::unique_lock<CCriticalSection> lock(m_StateSection);
4702 return !m_State.isInMenu && m_State.caching;
4705 int CVideoPlayer::GetCacheLevel() const
4707 std::unique_lock<CCriticalSection> lock(m_StateSection);
4708 return (int)(m_State.cache_level * 100);
4711 double CVideoPlayer::GetQueueTime()
4713 int a = m_VideoPlayerAudio->GetLevel();
4714 int v = m_processInfo->GetLevelVQ();
4715 return std::max(a, v) * m_messageQueueTimeSize * 1000.0 / 100.0;
4718 int CVideoPlayer::AddSubtitleFile(const std::string& filename, const std::string& subfilename)
4720 std::string ext = URIUtils::GetExtension(filename);
4721 std::string vobsubfile = subfilename;
4722 if (ext == ".idx" || ext == ".sup")
4724 std::shared_ptr<CDVDDemux> pDemux;
4725 if (ext == ".idx")
4727 if (vobsubfile.empty())
4729 // find corresponding .sub (e.g. in case of manually selected .idx sub)
4730 vobsubfile = CUtil::GetVobSubSubFromIdx(filename);
4731 if (vobsubfile.empty())
4732 return -1;
4735 auto pDemuxVobsub = std::make_shared<CDVDDemuxVobsub>();
4736 if (!pDemuxVobsub->Open(filename, STREAM_SOURCE_NONE, vobsubfile))
4737 return -1;
4739 m_SelectionStreams.Update(nullptr, pDemuxVobsub.get(), vobsubfile);
4740 pDemux = pDemuxVobsub;
4742 else // .sup file
4744 CFileItem item(filename, false);
4745 std::shared_ptr<CDVDInputStream> pInput;
4746 pInput = CDVDFactoryInputStream::CreateInputStream(nullptr, item);
4747 if (!pInput || !pInput->Open())
4748 return -1;
4750 auto pDemuxFFmpeg = std::make_shared<CDVDDemuxFFmpeg>();
4751 if (!pDemuxFFmpeg->Open(pInput, false))
4752 return -1;
4754 m_SelectionStreams.Update(nullptr, pDemuxFFmpeg.get(), filename);
4755 pDemux = pDemuxFFmpeg;
4758 ExternalStreamInfo info =
4759 CUtil::GetExternalStreamDetailsFromFilename(m_item.GetDynPath(), filename);
4761 for (auto sub : pDemux->GetStreams())
4763 if (sub->type != STREAM_SUBTITLE)
4764 continue;
4766 int index = m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE,
4767 m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename),
4768 sub->demuxerId, sub->uniqueId);
4769 SelectionStream& stream = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
4771 if (stream.name.empty())
4772 stream.name = info.name;
4774 if (stream.language.empty())
4775 stream.language = info.language;
4777 if (static_cast<StreamFlags>(info.flag) != StreamFlags::FLAG_NONE)
4778 stream.flags = static_cast<StreamFlags>(info.flag);
4781 UpdateContent();
4782 // the demuxer id is unique
4783 m_subtitleDemuxerMap[pDemux->GetDemuxerId()] = pDemux;
4784 return m_SelectionStreams.TypeIndexOf(
4785 STREAM_SUBTITLE, m_SelectionStreams.Source(STREAM_SOURCE_DEMUX_SUB, filename),
4786 pDemux->GetDemuxerId(), 0);
4789 if(ext == ".sub")
4791 // if this looks like vobsub file (i.e. .idx found), add it as such
4792 std::string vobsubidx = CUtil::GetVobSubIdxFromSub(filename);
4793 if (!vobsubidx.empty())
4794 return AddSubtitleFile(vobsubidx, filename);
4797 SelectionStream s;
4798 s.source = m_SelectionStreams.Source(STREAM_SOURCE_TEXT, filename);
4799 s.type = STREAM_SUBTITLE;
4800 s.id = 0;
4801 s.filename = filename;
4802 ExternalStreamInfo info = CUtil::GetExternalStreamDetailsFromFilename(m_item.GetDynPath(), filename);
4803 s.name = info.name;
4804 s.language = info.language;
4805 if (static_cast<StreamFlags>(info.flag) != StreamFlags::FLAG_NONE)
4806 s.flags = static_cast<StreamFlags>(info.flag);
4808 m_SelectionStreams.Update(s);
4809 UpdateContent();
4810 return m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE, s.source, s.demuxerId, s.id);
4813 void CVideoPlayer::UpdatePlayState(double timeout)
4815 if (m_State.timestamp != 0 &&
4816 m_State.timestamp + DVD_MSEC_TO_TIME(timeout) > m_clock.GetAbsoluteClock())
4817 return;
4819 SPlayerState state(m_State);
4821 state.dts = DVD_NOPTS_VALUE;
4822 if (m_CurrentVideo.dts != DVD_NOPTS_VALUE)
4823 state.dts = m_CurrentVideo.dts;
4824 else if (m_CurrentAudio.dts != DVD_NOPTS_VALUE)
4825 state.dts = m_CurrentAudio.dts;
4826 else if (m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
4827 state.dts = m_CurrentVideo.startpts;
4828 else if (m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
4829 state.dts = m_CurrentAudio.startpts;
4831 state.startTime = 0;
4832 state.timeMin = 0;
4834 std::shared_ptr<CDVDInputStream::IMenus> pMenu = std::dynamic_pointer_cast<CDVDInputStream::IMenus>(m_pInputStream);
4836 if (m_pDemuxer)
4838 if (IsInMenuInternal() && pMenu && !pMenu->CanSeek())
4839 state.chapter = 0;
4840 else
4841 state.chapter = m_pDemuxer->GetChapter();
4843 state.chapters.clear();
4844 if (m_pDemuxer->GetChapterCount() > 0)
4846 for (int i = 0, ie = m_pDemuxer->GetChapterCount(); i < ie; ++i)
4848 std::string name;
4849 m_pDemuxer->GetChapterName(name, i + 1);
4850 state.chapters.emplace_back(name, m_pDemuxer->GetChapterPos(i + 1));
4853 CServiceBroker::GetDataCacheCore().SetChapters(state.chapters);
4855 state.time = m_clock.GetClock(false) * 1000 / DVD_TIME_BASE;
4856 state.timeMax = m_pDemuxer->GetStreamLength();
4859 state.canpause = false;
4860 state.canseek = false;
4861 state.cantempo = false;
4862 state.isInMenu = false;
4863 state.menuType = MenuType::NONE;
4865 if (m_pInputStream)
4867 CDVDInputStream::IChapter* pChapter = m_pInputStream->GetIChapter();
4868 if (pChapter)
4870 if (IsInMenuInternal() && pMenu && !pMenu->CanSeek())
4871 state.chapter = 0;
4872 else
4873 state.chapter = pChapter->GetChapter();
4875 state.chapters.clear();
4876 if (pChapter->GetChapterCount() > 0)
4878 for (int i = 0, ie = pChapter->GetChapterCount(); i < ie; ++i)
4880 std::string name;
4881 pChapter->GetChapterName(name, i + 1);
4882 state.chapters.emplace_back(name, pChapter->GetChapterPos(i + 1));
4885 CServiceBroker::GetDataCacheCore().SetChapters(state.chapters);
4888 CDVDInputStream::ITimes* pTimes = m_pInputStream->GetITimes();
4889 CDVDInputStream::IDisplayTime* pDisplayTime = m_pInputStream->GetIDisplayTime();
4891 CDVDInputStream::ITimes::Times times;
4892 if (pTimes && pTimes->GetTimes(times))
4894 state.startTime = times.startTime;
4895 state.time = (m_clock.GetClock(false) - times.ptsStart) * 1000 / DVD_TIME_BASE;
4896 state.timeMax = (times.ptsEnd - times.ptsStart) * 1000 / DVD_TIME_BASE;
4897 state.timeMin = (times.ptsBegin - times.ptsStart) * 1000 / DVD_TIME_BASE;
4898 state.time_offset = -times.ptsStart;
4900 else if (pDisplayTime && pDisplayTime->GetTotalTime() > 0)
4902 if (state.dts != DVD_NOPTS_VALUE)
4904 int dispTime = 0;
4905 if (m_CurrentVideo.id >= 0 && m_CurrentVideo.dispTime)
4906 dispTime = m_CurrentVideo.dispTime;
4907 else if (m_CurrentAudio.dispTime)
4908 dispTime = m_CurrentAudio.dispTime;
4910 state.time_offset = DVD_MSEC_TO_TIME(dispTime) - state.dts;
4912 state.time += state.time_offset * 1000 / DVD_TIME_BASE;
4913 state.timeMax = pDisplayTime->GetTotalTime();
4915 else
4917 state.time_offset = 0;
4920 if (pMenu)
4922 if (!pMenu->GetState(state.player_state))
4923 state.player_state = "";
4925 if (m_dvd.state == DVDSTATE_STILL)
4927 const auto now = std::chrono::steady_clock::now();
4928 const auto duration =
4929 std::chrono::duration_cast<std::chrono::milliseconds>(now - m_dvd.iDVDStillStartTime);
4930 state.time = duration.count();
4931 state.timeMax = m_dvd.iDVDStillTime.count();
4932 state.isInMenu = true;
4934 else if (IsInMenuInternal())
4936 state.time = pDisplayTime->GetTime();
4937 state.isInMenu = true;
4938 if (!pMenu->CanSeek())
4939 state.time_offset = 0;
4941 state.menuType = pMenu->GetSupportedMenuType();
4944 state.canpause = m_pInputStream->CanPause();
4946 bool realtime = m_pInputStream->IsRealtime();
4948 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK) &&
4949 !realtime)
4951 state.cantempo = true;
4953 else
4955 state.cantempo = false;
4958 m_processInfo->SetStateRealtime(realtime);
4961 if (m_Edl.HasCuts())
4963 state.time = static_cast<double>(
4964 m_Edl.GetTimeWithoutCuts(std::chrono::milliseconds(std::lround(state.time))).count());
4965 state.timeMax = state.timeMax - static_cast<double>(m_Edl.GetTotalCutTime().count());
4968 if (m_caching > CACHESTATE_DONE && m_caching < CACHESTATE_PLAY)
4969 state.caching = true;
4970 else
4971 state.caching = false;
4973 double queueTime = GetQueueTime();
4974 CacheInfo cache = GetCachingTimes();
4976 if (cache.valid)
4978 state.cache_level = std::max(0.0, std::min(1.0, cache.level));
4979 state.cache_offset = cache.offset;
4980 state.cache_time = cache.time;
4982 else
4984 state.cache_level = std::min(1.0, queueTime / (m_messageQueueTimeSize * 1000.0));
4985 state.cache_offset = queueTime / state.timeMax;
4986 state.cache_time = queueTime / 1000.0;
4989 XFILE::SCacheStatus status;
4990 if (m_pInputStream && m_pInputStream->GetCacheStatus(&status))
4992 state.cache_bytes = status.forward;
4993 if(state.timeMax)
4994 state.cache_bytes += m_pInputStream->GetLength() * (int64_t)(queueTime / state.timeMax);
4996 else
4997 state.cache_bytes = 0;
4999 state.timestamp = m_clock.GetAbsoluteClock();
5001 if (state.timeMax <= 0)
5003 state.timeMax = state.time;
5004 state.timeMin = state.time;
5006 if (state.timeMin == state.timeMax)
5008 state.canseek = false;
5009 state.cantempo = false;
5011 else
5013 state.canseek = true;
5014 state.canpause = true;
5017 m_processInfo->SetPlayTimes(state.startTime, state.time, state.timeMin, state.timeMax);
5019 std::unique_lock<CCriticalSection> lock(m_StateSection);
5020 m_State = state;
5023 int64_t CVideoPlayer::GetUpdatedTime()
5025 UpdatePlayState(0);
5026 return llrint(m_State.time);
5029 void CVideoPlayer::SetDynamicRangeCompression(long drc)
5031 m_processInfo->GetVideoSettingsLocked().SetVolumeAmplification(static_cast<float>(drc) / 100);
5032 m_VideoPlayerAudio->SetDynamicRangeCompression(drc);
5035 CVideoSettings CVideoPlayer::GetVideoSettings() const
5037 return m_processInfo->GetVideoSettings();
5040 void CVideoPlayer::SetVideoSettings(CVideoSettings& settings)
5042 m_processInfo->SetVideoSettings(settings);
5043 m_renderManager.SetVideoSettings(settings);
5044 m_renderManager.SetDelay(static_cast<int>(settings.m_AudioDelay * 1000.0f));
5045 m_renderManager.SetSubtitleVerticalPosition(settings.m_subtitleVerticalPosition,
5046 settings.m_subtitleVerticalPositionSave);
5047 m_VideoPlayerVideo->EnableSubtitle(settings.m_SubtitleOn);
5048 m_VideoPlayerVideo->SetSubtitleDelay(static_cast<int>(-settings.m_SubtitleDelay * DVD_TIME_BASE));
5051 void CVideoPlayer::FrameMove()
5053 m_renderManager.FrameMove();
5056 void CVideoPlayer::Render(bool clear, uint32_t alpha, bool gui)
5058 m_renderManager.Render(clear, 0, alpha, gui);
5061 void CVideoPlayer::FlushRenderer()
5063 m_renderManager.Flush(true, true);
5066 void CVideoPlayer::SetRenderViewMode(int mode, float zoom, float par, float shift, bool stretch)
5068 m_processInfo->GetVideoSettingsLocked().SetViewMode(mode, zoom, par, shift, stretch);
5069 m_renderManager.SetVideoSettings(m_processInfo->GetVideoSettings());
5070 m_renderManager.SetViewMode(mode);
5073 float CVideoPlayer::GetRenderAspectRatio() const
5075 return m_renderManager.GetAspectRatio();
5078 void CVideoPlayer::GetRects(CRect& source, CRect& dest, CRect& view) const
5080 m_renderManager.GetVideoRect(source, dest, view);
5083 unsigned int CVideoPlayer::GetOrientation() const
5085 return m_renderManager.GetOrientation();
5088 void CVideoPlayer::TriggerUpdateResolution()
5090 std::string stereomode;
5091 m_renderManager.TriggerUpdateResolution(0, 0, 0, stereomode);
5094 bool CVideoPlayer::IsRenderingVideo() const
5096 return m_renderManager.IsConfigured();
5099 bool CVideoPlayer::Supports(EINTERLACEMETHOD method) const
5101 if (!m_processInfo)
5102 return false;
5103 return m_processInfo->Supports(method);
5106 EINTERLACEMETHOD CVideoPlayer::GetDeinterlacingMethodDefault() const
5108 if (!m_processInfo)
5109 return EINTERLACEMETHOD::VS_INTERLACEMETHOD_NONE;
5110 return m_processInfo->GetDeinterlacingMethodDefault();
5113 bool CVideoPlayer::Supports(ESCALINGMETHOD method) const
5115 return m_renderManager.Supports(method);
5118 bool CVideoPlayer::Supports(ERENDERFEATURE feature) const
5120 return m_renderManager.Supports(feature);
5123 unsigned int CVideoPlayer::RenderCaptureAlloc()
5125 return m_renderManager.AllocRenderCapture();
5128 void CVideoPlayer::RenderCapture(unsigned int captureId, unsigned int width, unsigned int height, int flags)
5130 m_renderManager.StartRenderCapture(captureId, width, height, flags);
5133 void CVideoPlayer::RenderCaptureRelease(unsigned int captureId)
5135 m_renderManager.ReleaseRenderCapture(captureId);
5138 bool CVideoPlayer::RenderCaptureGetPixels(unsigned int captureId, unsigned int millis, uint8_t *buffer, unsigned int size)
5140 return m_renderManager.RenderCaptureGetPixels(captureId, millis, buffer, size);
5143 void CVideoPlayer::VideoParamsChange()
5145 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_AVCHANGE));
5148 void CVideoPlayer::GetDebugInfo(std::string &audio, std::string &video, std::string &general)
5150 audio = m_VideoPlayerAudio->GetPlayerInfo();
5151 video = m_VideoPlayerVideo->GetPlayerInfo();
5152 GetGeneralInfo(general);
5155 void CVideoPlayer::UpdateClockSync(bool enabled)
5157 m_processInfo->SetRenderClockSync(enabled);
5160 void CVideoPlayer::UpdateRenderInfo(CRenderInfo &info)
5162 m_processInfo->UpdateRenderInfo(info);
5165 void CVideoPlayer::UpdateRenderBuffers(int queued, int discard, int free)
5167 m_processInfo->UpdateRenderBuffers(queued, discard, free);
5170 void CVideoPlayer::UpdateGuiRender(bool gui)
5172 m_processInfo->SetGuiRender(gui);
5175 void CVideoPlayer::UpdateVideoRender(bool video)
5177 m_processInfo->SetVideoRender(video);
5180 // IDispResource interface
5181 void CVideoPlayer::OnLostDisplay()
5183 CLog::Log(LOGINFO, "VideoPlayer: OnLostDisplay received");
5184 if (m_VideoPlayerAudio->IsInited())
5185 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, true), 1);
5186 if (m_VideoPlayerVideo->IsInited())
5187 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, true), 1);
5188 m_clock.Pause(true);
5189 m_displayLost = true;
5190 FlushRenderer();
5193 void CVideoPlayer::OnResetDisplay()
5195 if (!m_displayLost)
5196 return;
5198 CLog::Log(LOGINFO, "VideoPlayer: OnResetDisplay received");
5199 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, false), 1);
5200 m_VideoPlayerVideo->SendMessage(std::make_shared<CDVDMsgBool>(CDVDMsg::GENERAL_PAUSE, false), 1);
5201 m_clock.Pause(false);
5202 m_displayLost = false;
5203 m_VideoPlayerAudio->SendMessage(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_DISPLAY_RESET), 1);
5206 void CVideoPlayer::UpdateFileItemStreamDetails(CFileItem& item)
5208 if (!m_UpdateStreamDetails)
5209 return;
5210 m_UpdateStreamDetails = false;
5212 CLog::Log(LOGDEBUG, "CVideoPlayer: updating file item stream details with available streams");
5214 VideoStreamInfo videoInfo;
5215 AudioStreamInfo audioInfo;
5216 SubtitleStreamInfo subtitleInfo;
5217 CVideoInfoTag* info = item.GetVideoInfoTag();
5218 GetVideoStreamInfo(CURRENT_STREAM, videoInfo);
5219 info->m_streamDetails.SetStreams(videoInfo, m_processInfo->GetMaxTime() / 1000, audioInfo,
5220 subtitleInfo);
5222 //grab all the audio and subtitle info and save it
5224 for (int i = 0; i < GetAudioStreamCount(); i++)
5226 GetAudioStreamInfo(i, audioInfo);
5227 info->m_streamDetails.AddStream(new CStreamDetailAudio(audioInfo));
5230 for (int i = 0; i < GetSubtitleCount(); i++)
5232 GetSubtitleStreamInfo(i, subtitleInfo);
5233 info->m_streamDetails.AddStream(new CStreamDetailSubtitle(subtitleInfo));
5237 //------------------------------------------------------------------------------
5238 // content related methods
5239 //------------------------------------------------------------------------------
5241 void CVideoPlayer::UpdateContent()
5243 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5244 m_content.m_selectionStreams = m_SelectionStreams;
5245 m_content.m_programs = m_programs;
5248 void CVideoPlayer::UpdateContentState()
5250 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5252 m_content.m_videoIndex = m_SelectionStreams.TypeIndexOf(STREAM_VIDEO, m_CurrentVideo.source,
5253 m_CurrentVideo.demuxerId, m_CurrentVideo.id);
5254 m_content.m_audioIndex = m_SelectionStreams.TypeIndexOf(STREAM_AUDIO, m_CurrentAudio.source,
5255 m_CurrentAudio.demuxerId, m_CurrentAudio.id);
5256 m_content.m_subtitleIndex = m_SelectionStreams.TypeIndexOf(STREAM_SUBTITLE, m_CurrentSubtitle.source,
5257 m_CurrentSubtitle.demuxerId, m_CurrentSubtitle.id);
5259 if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) && m_content.m_videoIndex == -1 &&
5260 m_content.m_audioIndex == -1)
5262 std::shared_ptr<CDVDInputStreamNavigator> nav =
5263 std::static_pointer_cast<CDVDInputStreamNavigator>(m_pInputStream);
5265 m_content.m_videoIndex = m_SelectionStreams.TypeIndexOf(STREAM_VIDEO, STREAM_SOURCE_NAV, -1,
5266 nav->GetActiveAngle());
5267 m_content.m_audioIndex = m_SelectionStreams.TypeIndexOf(STREAM_AUDIO, STREAM_SOURCE_NAV, -1,
5268 nav->GetActiveAudioStream());
5270 // only update the subtitle index in libdvdnav if the subtitle is provided by the dvd itself,
5271 // i.e. for external subtitles the index is always greater than the subtitlecount in dvdnav
5272 if (m_content.m_subtitleIndex < nav->GetSubTitleStreamCount())
5274 m_content.m_subtitleIndex = m_SelectionStreams.TypeIndexOf(
5275 STREAM_SUBTITLE, STREAM_SOURCE_NAV, -1, nav->GetActiveSubtitleStream());
5280 void CVideoPlayer::GetVideoStreamInfo(int streamId, VideoStreamInfo& info) const
5282 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5284 if (streamId == CURRENT_STREAM)
5285 streamId = m_content.m_videoIndex;
5287 if (streamId < 0 || streamId > GetVideoStreamCount() - 1)
5289 info.valid = false;
5290 return;
5293 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_VIDEO, streamId);
5294 if (s.language.length() > 0)
5295 info.language = s.language;
5297 if (s.name.length() > 0)
5298 info.name = s.name;
5300 m_renderManager.GetVideoRect(info.SrcRect, info.DestRect, info.VideoRect);
5302 info.valid = true;
5303 info.bitrate = s.bitrate;
5304 info.width = s.width;
5305 info.height = s.height;
5306 info.codecName = s.codec;
5307 info.videoAspectRatio = s.aspect_ratio;
5308 info.stereoMode = s.stereo_mode;
5309 info.flags = s.flags;
5310 info.hdrType = s.hdrType;
5311 info.fpsRate = s.fpsRate;
5312 info.fpsScale = s.fpsScale;
5315 int CVideoPlayer::GetVideoStreamCount() const
5317 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5318 return m_content.m_selectionStreams.CountType(STREAM_VIDEO);
5321 int CVideoPlayer::GetVideoStream() const
5323 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5324 return m_content.m_videoIndex;
5327 void CVideoPlayer::SetVideoStream(int iStream)
5329 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetVideoStream>(iStream));
5330 m_processInfo->GetVideoSettingsLocked().SetVideoStream(iStream);
5331 SynchronizeDemuxer();
5334 void CVideoPlayer::GetAudioStreamInfo(int index, AudioStreamInfo& info) const
5336 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5338 if (index == CURRENT_STREAM)
5339 index = m_content.m_audioIndex;
5341 if (index < 0 || index > GetAudioStreamCount() - 1)
5343 info.valid = false;
5344 return;
5347 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_AUDIO, index);
5348 info.language = s.language;
5349 info.name = s.name;
5351 if (s.type == STREAM_NONE)
5352 info.name += " (Invalid)";
5354 info.valid = true;
5355 info.bitrate = s.bitrate;
5356 info.channels = s.channels;
5357 info.codecName = s.codec;
5358 info.codecDesc = s.codecDesc;
5359 info.flags = s.flags;
5362 int CVideoPlayer::GetAudioStreamCount() const
5364 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5365 return m_content.m_selectionStreams.CountType(STREAM_AUDIO);
5368 int CVideoPlayer::GetAudioStream()
5370 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5371 return m_content.m_audioIndex;
5374 void CVideoPlayer::SetAudioStream(int iStream)
5376 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetAudioStream>(iStream));
5377 m_processInfo->GetVideoSettingsLocked().SetAudioStream(iStream);
5378 SynchronizeDemuxer();
5381 void CVideoPlayer::GetSubtitleStreamInfo(int index, SubtitleStreamInfo& info) const
5383 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5385 if (index == CURRENT_STREAM)
5386 index = m_content.m_subtitleIndex;
5388 if (index < 0 || index > GetSubtitleCount() - 1)
5390 info.valid = false;
5391 info.language.clear();
5392 info.flags = StreamFlags::FLAG_NONE;
5393 return;
5396 const SelectionStream& s = m_content.m_selectionStreams.Get(STREAM_SUBTITLE, index);
5397 info.name = s.name;
5399 if (s.type == STREAM_NONE)
5400 info.name += "(Invalid)";
5402 info.language = s.language;
5403 info.codecName = s.codec;
5404 info.flags = s.flags;
5405 info.isExternal = STREAM_SOURCE_MASK(s.source) == STREAM_SOURCE_DEMUX_SUB ||
5406 STREAM_SOURCE_MASK(s.source) == STREAM_SOURCE_TEXT;
5409 void CVideoPlayer::SetSubtitle(int iStream)
5411 m_messenger.Put(std::make_shared<CDVDMsgPlayerSetSubtitleStream>(iStream));
5412 m_processInfo->GetVideoSettingsLocked().SetSubtitleStream(iStream);
5415 int CVideoPlayer::GetSubtitleCount() const
5417 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5418 return m_content.m_selectionStreams.CountType(STREAM_SUBTITLE);
5421 int CVideoPlayer::GetSubtitle()
5423 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5424 return m_content.m_subtitleIndex;
5427 int CVideoPlayer::GetPrograms(std::vector<ProgramInfo>& programs)
5429 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5430 programs = m_programs;
5431 return programs.size();
5434 void CVideoPlayer::SetProgram(int progId)
5436 m_messenger.Put(std::make_shared<CDVDMsgInt>(CDVDMsg::PLAYER_SET_PROGRAM, progId));
5439 int CVideoPlayer::GetProgramsCount() const
5441 std::unique_lock<CCriticalSection> lock(m_content.m_section);
5442 return m_programs.size();
5445 void CVideoPlayer::SetUpdateStreamDetails()
5447 m_messenger.Put(std::make_shared<CDVDMsg>(CDVDMsg::PLAYER_SET_UPDATE_STREAM_DETAILS));