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