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