You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2127 lines
68 KiB
2127 lines
68 KiB
/*****************************************************************************
|
|
* Copyright (C) 2019 VLC authors and VideoLAN
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* ( at your option ) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "dialogs/extensions/extensions_manager.hpp"
|
|
#include "player_controller.hpp"
|
|
#include "player_controller_p.hpp"
|
|
#include "util/shared_input_item.hpp"
|
|
|
|
#include <vlc_actions.h> /* ACTION_ID */
|
|
#include <vlc_url.h> /* vlc_uri_decode */
|
|
#include <vlc_strings.h> /* vlc_strfplayer */
|
|
#include <vlc_aout.h> /* audio_output_t */
|
|
#include <vlc_es.h>
|
|
#include <vlc_cxx_helpers.hpp>
|
|
#include <vlc_vout.h>
|
|
#include <vlc_preparser.h>
|
|
|
|
#include <QFile>
|
|
#include <QDir>
|
|
#include <QSignalMapper>
|
|
#include <QThreadPool>
|
|
|
|
#include <cassert>
|
|
|
|
#define POSITION_MIN_UPDATE_INTERVAL VLC_TICK_FROM_MS(15)
|
|
|
|
//PlayerController private implementation
|
|
|
|
using SharedEsId = vlc_shared_data_ptr_type(vlc_es_id_t,
|
|
vlc_es_id_Hold,
|
|
vlc_es_id_Release);
|
|
|
|
using SharedTitleList = vlc_shared_data_ptr_type(vlc_player_title_list,
|
|
vlc_player_title_list_Hold,
|
|
vlc_player_title_list_Release);
|
|
|
|
PlayerControllerPrivate::~PlayerControllerPrivate()
|
|
{
|
|
assert(m_smpteTimerRequestCount == 0);
|
|
|
|
vlc_player_locker locker{m_player}; //this also locks the player
|
|
vlc_player_vout_RemoveListener( m_player, m_player_vout_listener );
|
|
vlc_player_aout_RemoveListener( m_player, m_player_aout_listener );
|
|
vlc_player_RemoveListener( m_player, m_player_listener );
|
|
vlc_player_RemoveTimer( m_player, m_player_timer );
|
|
if (m_preparser != nullptr)
|
|
vlc_preparser_Delete(m_preparser);
|
|
}
|
|
|
|
bool PlayerControllerPrivate::isCurrentItemSynced()
|
|
{
|
|
/* The media can change before the UI gets updated and thus a player
|
|
* request can be submitted on the wrong media or worse, no media at
|
|
* all. Here d->m_currentItem is read and modified under the player
|
|
* lock. */
|
|
return m_currentItem.get() == vlc_player_GetCurrentMedia( m_player );
|
|
}
|
|
|
|
|
|
void PlayerControllerPrivate::UpdateName(input_item_t* media)
|
|
{
|
|
Q_Q(PlayerController);
|
|
/* Update text, name and nowplaying */
|
|
QString name;
|
|
if (! media)
|
|
return;
|
|
|
|
/* Try to get the nowplaying */
|
|
char *format = var_InheritString( p_intf, "input-title-format" );
|
|
char *formatted = NULL;
|
|
if (format != NULL)
|
|
{
|
|
vlc_player_Lock( m_player );
|
|
formatted = vlc_strfplayer( m_player, media, format );
|
|
vlc_player_Unlock( m_player );
|
|
free( format );
|
|
if( formatted != NULL )
|
|
{
|
|
name = qfu(formatted);
|
|
free( formatted );
|
|
}
|
|
}
|
|
|
|
/* If we have Nothing */
|
|
if( name.simplified().isEmpty() )
|
|
{
|
|
char *uri = input_item_GetURI( media );
|
|
char *file = uri ? strrchr( uri, '/' ) : NULL;
|
|
if( file != NULL )
|
|
{
|
|
vlc_uri_decode( ++file );
|
|
name = qfu(file);
|
|
}
|
|
else
|
|
name = qfu(uri);
|
|
free( uri );
|
|
}
|
|
|
|
name = name.trimmed();
|
|
|
|
if( m_name != name )
|
|
{
|
|
emit q->nameChanged( name );
|
|
m_name = name;
|
|
}
|
|
}
|
|
|
|
|
|
void PlayerControllerPrivate::UpdateArt(input_item_t *p_item)
|
|
{
|
|
Q_Q(PlayerController);
|
|
if (! p_item)
|
|
return;
|
|
|
|
QString url = PlayerController::decodeArtURL( p_item );
|
|
|
|
/* the art hasn't changed, no need to update */
|
|
if(m_artUrl == url)
|
|
return;
|
|
|
|
/* Update Art meta */
|
|
m_artUrl = url;
|
|
emit q->artChanged( m_artUrl );
|
|
}
|
|
|
|
void PlayerControllerPrivate::UpdateStats( const input_stats_t& stats )
|
|
{
|
|
Q_Q(PlayerController);
|
|
emit q->statisticsUpdated( stats );
|
|
}
|
|
|
|
void PlayerControllerPrivate::UpdateProgram(enum vlc_player_list_action action, const struct vlc_player_program* prgm)
|
|
{
|
|
Q_Q(PlayerController);
|
|
m_programList.updatePrograms( action, prgm );
|
|
|
|
bool hasPrograms = (m_programList.getCount() > 1);
|
|
|
|
if (m_hasPrograms != hasPrograms)
|
|
{
|
|
m_hasPrograms = hasPrograms;
|
|
|
|
emit q->hasProgramsChanged(hasPrograms);
|
|
}
|
|
|
|
emit q->isEncryptedChanged( prgm->scrambled );
|
|
}
|
|
|
|
void PlayerControllerPrivate::UpdateTrackSelection(vlc_es_id_t *trackid, bool selected)
|
|
{
|
|
if (trackid == NULL)
|
|
return;
|
|
es_format_category_e cat = vlc_es_id_GetCat(trackid);
|
|
TrackListModel* tracklist;
|
|
switch (cat) {
|
|
case VIDEO_ES: tracklist = &m_videoTracks; break;
|
|
case AUDIO_ES: tracklist = &m_audioTracks; break;
|
|
case SPU_ES: tracklist = &m_subtitleTracks; break;
|
|
default: return;
|
|
}
|
|
tracklist->updateTrackSelection(trackid, selected);
|
|
}
|
|
|
|
void PlayerControllerPrivate::UpdateMeta( input_item_t *p_item )
|
|
{
|
|
Q_Q(PlayerController);
|
|
|
|
{
|
|
vlc_mutex_locker locker(&p_item->lock);
|
|
|
|
if (p_item->p_meta)
|
|
{
|
|
m_title = vlc_meta_Get(p_item->p_meta, vlc_meta_Title);
|
|
m_artist = vlc_meta_Get(p_item->p_meta, vlc_meta_Artist);
|
|
m_album = vlc_meta_Get(p_item->p_meta, vlc_meta_Album);
|
|
m_artwork = vlc_meta_Get(p_item->p_meta, vlc_meta_ArtworkURL);
|
|
}
|
|
}
|
|
|
|
emit q->currentMetaChanged( p_item );
|
|
}
|
|
|
|
void PlayerControllerPrivate::UpdateInfo( input_item_t *p_item )
|
|
{
|
|
Q_Q(PlayerController);
|
|
emit q->infoChanged( p_item );
|
|
}
|
|
|
|
void PlayerControllerPrivate::UpdateVouts(vout_thread_t **vouts, size_t i_vouts, enum vlc_player_vout_action action)
|
|
{
|
|
Q_Q(PlayerController);
|
|
bool hadVideo = m_hasVideo;
|
|
|
|
if (action == VLC_PLAYER_VOUT_STARTED)
|
|
++m_vout_ref;
|
|
else if (action == VLC_PLAYER_VOUT_STOPPED)
|
|
--m_vout_ref;
|
|
assert(m_vout_ref >= 0);
|
|
m_hasVideo = (m_vout_ref > 0);
|
|
|
|
vout_thread_t* main_vout = nullptr;
|
|
if (m_hasVideo)
|
|
main_vout = vouts[0];
|
|
|
|
m_zoom.resetObject( main_vout );
|
|
m_aspectRatio.resetObject( main_vout );
|
|
m_crop.resetObject( main_vout );
|
|
m_fit.resetObject( main_vout );
|
|
m_deinterlace.resetObject( main_vout );
|
|
m_deinterlaceMode.resetObject( main_vout );
|
|
m_autoscale.resetObject( main_vout );
|
|
|
|
emit q->voutListChanged(vouts, i_vouts);
|
|
if( hadVideo != m_hasVideo )
|
|
emit q->hasVideoOutputChanged(m_hasVideo);
|
|
}
|
|
|
|
void PlayerControllerPrivate::UpdateSpuOrder(vlc_es_id_t *es_id, enum vlc_vout_order order)
|
|
{
|
|
switch (order)
|
|
{
|
|
case VLC_VOUT_ORDER_NONE:
|
|
if (es_id == m_secondarySpuEsId.get())
|
|
m_secondarySpuEsId.reset(NULL, false);
|
|
break;
|
|
case VLC_VOUT_ORDER_SECONDARY:
|
|
m_secondarySpuEsId.reset(es_id, true);
|
|
if (m_secondarySubtitleDelay != 0)
|
|
{
|
|
vlc_player_locker lock{ m_player };
|
|
vlc_player_SetEsIdDelay(m_player, es_id,
|
|
m_secondarySubtitleDelay,
|
|
VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int PlayerControllerPrivate::interpolateTime(vlc_tick_t system_now)
|
|
{
|
|
vlc_tick_t new_time;
|
|
if (vlc_player_timer_point_Interpolate(&m_player_time, system_now,
|
|
&new_time, &m_position) == VLC_SUCCESS)
|
|
{
|
|
m_time = new_time != VLC_TICK_INVALID ? new_time - VLC_TICK_0 : 0;
|
|
return VLC_SUCCESS;
|
|
}
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
//player callbacks
|
|
|
|
static void on_player_current_media_changed(vlc_player_t *, input_item_t *new_media, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_current_media_changed");
|
|
|
|
if (!new_media)
|
|
{
|
|
that->callAsync([that] () {
|
|
{
|
|
vlc_player_locker lock{ that->m_player };
|
|
that->m_currentItem.reset(nullptr);
|
|
}
|
|
emit that->q_func()->inputChanged(false);
|
|
});
|
|
return;
|
|
}
|
|
|
|
SharedInputItem newMediaPtr = SharedInputItem( new_media );
|
|
that->callAsync([that,newMediaPtr] () {
|
|
PlayerController* q = that->q_func();
|
|
that->UpdateName( newMediaPtr.get() );
|
|
that->UpdateArt( newMediaPtr.get() );
|
|
that->UpdateMeta( newMediaPtr.get() );
|
|
that->m_url = vlc::wrap_cptr( input_item_GetURI( newMediaPtr.get() ) ).get();
|
|
|
|
{
|
|
vlc_player_locker lock{ that->m_player };
|
|
that->m_currentItem = std::move(newMediaPtr);
|
|
}
|
|
|
|
that->m_canRestorePlayback = false;
|
|
emit q->playbackRestoreQueried();
|
|
|
|
emit q->inputChanged(true);
|
|
});
|
|
}
|
|
|
|
static void on_player_state_changed(vlc_player_t *, enum vlc_player_state state, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_state_changed");
|
|
|
|
that->callAsync([that,state] () {
|
|
PlayerController* q = that->q_func();
|
|
that->m_playing_status = static_cast<PlayerController::PlayingState>(state);
|
|
switch ( state ) {
|
|
case VLC_PLAYER_STATE_STARTED:
|
|
msg_Dbg( that->p_intf, "on_player_state_changed VLC_PLAYER_STATE_STARTED");
|
|
break;
|
|
case VLC_PLAYER_STATE_PLAYING:
|
|
{
|
|
msg_Dbg( that->p_intf, "on_player_state_changed VLC_PLAYER_STATE_PLAYING");
|
|
SharedAOut aout = q->getAout();
|
|
that->m_audioStereoMode.resetObject( aout.get() );
|
|
that->m_audioMixMode.resetObject( aout.get() );
|
|
that->m_audioVisualization.resetObject( aout.get() );
|
|
break;
|
|
}
|
|
case VLC_PLAYER_STATE_PAUSED:
|
|
msg_Dbg( that->p_intf, "on_player_state_changed VLC_PLAYER_STATE_PAUSED");
|
|
break;
|
|
case VLC_PLAYER_STATE_STOPPING:
|
|
msg_Dbg( that->p_intf, "on_player_state_changed VLC_PLAYER_STATE_STOPPING");
|
|
break;
|
|
case VLC_PLAYER_STATE_STOPPED:
|
|
{
|
|
msg_Dbg( that->p_intf, "on_player_state_changed VLC_PLAYER_STATE_STOPPED");
|
|
|
|
that->m_audioStereoMode.resetObject((audio_output_t*)nullptr);
|
|
that->m_audioMixMode.resetObject((audio_output_t*)nullptr);
|
|
that->m_audioVisualization.resetObject((audio_output_t*)nullptr);
|
|
|
|
/* reset the state on stop */
|
|
that->m_position = 0;
|
|
that->m_time = 0;
|
|
that->m_length = 0;
|
|
emit q->positionUpdated( -1.0, 0 ,0 );
|
|
that->m_rate = 1.0f;
|
|
emit q->rateChanged( 1.0f );
|
|
that->m_name = "";
|
|
emit q->nameChanged( "" );
|
|
that->m_hasChapters = false;
|
|
emit q->hasChaptersChanged( false );
|
|
that->m_hasTitles = false;
|
|
emit q->hasTitlesChanged( false );
|
|
that->m_hasMenu= false;
|
|
emit q->hasMenuChanged( false );
|
|
that->m_isMenu= false;
|
|
emit q->isMenuChanged( false );
|
|
that->m_isInteractive= false;
|
|
emit q->isInteractiveChanged( false );
|
|
that->m_canRestorePlayback = false;
|
|
emit q->playbackRestoreQueried();
|
|
|
|
that->m_teletextAvailable = false;
|
|
emit q->teletextAvailableChanged( false );
|
|
that->m_ABLoopState = PlayerController::ABLOOP_STATE_NONE;
|
|
that->m_ABLoopA = VLC_TICK_INVALID;
|
|
that->m_ABLoopB = VLC_TICK_INVALID;
|
|
emit q->ABLoopStateChanged(PlayerController::ABLOOP_STATE_NONE);
|
|
emit q->ABLoopAChanged(VLC_TICK_INVALID);
|
|
emit q->ABLoopBChanged(VLC_TICK_INVALID);
|
|
that->m_hasVideo = false;
|
|
emit q->hasVideoOutputChanged( false );
|
|
|
|
emit q->voutListChanged( NULL, 0 );
|
|
|
|
/* Reset all InfoPanels but stats */
|
|
that->m_artUrl = "";
|
|
emit q->artChanged( NULL );
|
|
emit q->artChanged( "" );
|
|
emit q->infoChanged( NULL );
|
|
emit q->currentMetaChanged( (input_item_t *)NULL );
|
|
|
|
that->m_hasPrograms =false;
|
|
emit q->hasProgramsChanged( false );
|
|
|
|
that->m_encrypted =false;
|
|
emit q->isEncryptedChanged( false );
|
|
that->m_recording =false;
|
|
emit q->recordingChanged( false );
|
|
|
|
that->m_buffering = 0.f;
|
|
emit q->bufferingChanged( 0.0 );
|
|
|
|
break;
|
|
}
|
|
}
|
|
emit q->playingStateChanged( that->m_playing_status );
|
|
});
|
|
}
|
|
|
|
void on_player_error_changed(vlc_player_t *, enum vlc_player_error , void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_error_changed");
|
|
}
|
|
|
|
static void on_player_buffering(vlc_player_t *, float new_buffering, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_buffering");
|
|
that->callAsync([that,new_buffering](){
|
|
that->m_buffering = new_buffering;
|
|
emit that->q_func()->bufferingChanged( new_buffering );
|
|
});
|
|
}
|
|
|
|
static void on_player_capabilities_changed(vlc_player_t *, int old_caps, int new_caps, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_capabilities_changed");
|
|
that->callAsync([that, old_caps, new_caps]() {
|
|
PlayerController* q = that->q_func();
|
|
that->m_capabilities = new_caps;
|
|
|
|
bool oldSeekable = old_caps & VLC_PLAYER_CAP_SEEK;
|
|
bool newSeekable = new_caps & VLC_PLAYER_CAP_SEEK;
|
|
if (newSeekable != oldSeekable)
|
|
emit q->seekableChanged( newSeekable );
|
|
|
|
bool oldRewindable = old_caps & VLC_PLAYER_CAP_REWIND;
|
|
bool newRewindable = new_caps & VLC_PLAYER_CAP_REWIND;
|
|
if (newRewindable != oldRewindable)
|
|
emit q->rewindableChanged( newRewindable );
|
|
|
|
bool oldPauseable = old_caps & VLC_PLAYER_CAP_PAUSE;
|
|
bool newPauseable = new_caps & VLC_PLAYER_CAP_PAUSE;
|
|
if (newPauseable != oldPauseable)
|
|
emit q->pausableChanged( newPauseable );
|
|
|
|
bool oldChangeRate = old_caps & VLC_PLAYER_CAP_CHANGE_RATE;
|
|
bool newChangeRate = new_caps & VLC_PLAYER_CAP_CHANGE_RATE;
|
|
if (newChangeRate != oldChangeRate)
|
|
emit q->rateChangableChanged( newChangeRate );
|
|
});
|
|
|
|
//FIXME other events?
|
|
}
|
|
|
|
static void on_player_track_list_changed(vlc_player_t *, enum vlc_player_list_action action, const struct vlc_player_track *track, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
struct vlc_player_track* dup = vlc_player_track_Dup(track);
|
|
if (!dup)
|
|
return;
|
|
std::shared_ptr<struct vlc_player_track> trackPtr(dup, vlc_player_track_Delete);
|
|
|
|
msg_Dbg( that->p_intf, "on_player_track_list_changed");
|
|
that->callAsync([that,action,trackPtr] () {
|
|
switch (trackPtr.get()->fmt.i_cat) {
|
|
case VIDEO_ES:
|
|
msg_Dbg( that->p_intf, "on_player_track_list_changed (video)");
|
|
that->m_videoTracks.updateTracks( action, trackPtr.get() );
|
|
break;
|
|
case AUDIO_ES:
|
|
msg_Dbg( that->p_intf, "on_player_track_list_changed (audio)");
|
|
that->m_audioTracks.updateTracks( action, trackPtr.get() );
|
|
break;
|
|
case SPU_ES:
|
|
msg_Dbg( that->p_intf, "on_player_track_list_changed (spu)");
|
|
that->m_subtitleTracks.updateTracks( action, trackPtr.get() );
|
|
break;
|
|
default:
|
|
//we don't handle other kind of tracks
|
|
msg_Dbg( that->p_intf, "on_player_track_list_changed (other)");
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
static void on_player_track_selection_changed(vlc_player_t *, vlc_es_id_t * unselected, vlc_es_id_t *selected, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_track_selection_changed");
|
|
|
|
SharedEsId unselectedPtr = SharedEsId(unselected);
|
|
SharedEsId selectedPtr = SharedEsId(selected);
|
|
|
|
that->callAsync([that,unselectedPtr,selectedPtr] () {
|
|
if (unselectedPtr)
|
|
that->UpdateTrackSelection( unselectedPtr.get(), false );
|
|
if (selectedPtr)
|
|
that->UpdateTrackSelection( selectedPtr.get(), true );
|
|
});
|
|
}
|
|
|
|
|
|
static void on_player_program_list_changed(vlc_player_t *, enum vlc_player_list_action action, const struct vlc_player_program *prgm, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_program_list_changed");
|
|
struct vlc_player_program* dup = vlc_player_program_Dup(prgm);
|
|
if (!dup)
|
|
return;
|
|
std::shared_ptr<struct vlc_player_program> prgmPtr(dup, vlc_player_program_Delete);
|
|
|
|
that->callAsync([that,action,prgmPtr] (){
|
|
that->UpdateProgram(action, prgmPtr.get());
|
|
});
|
|
}
|
|
|
|
static void on_player_program_selection_changed(vlc_player_t *, int unselected, int selected, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_program_selection_changed");
|
|
|
|
that->callAsync([that,unselected,selected] (){
|
|
that->m_programList.updateProgramSelection(unselected, false);
|
|
that->m_programList.updateProgramSelection(selected, true);
|
|
});
|
|
}
|
|
|
|
static void on_player_titles_changed(vlc_player_t *, struct vlc_player_title_list *titles, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_title_array_changed");
|
|
|
|
SharedTitleList sharedTitleList = SharedTitleList(titles);
|
|
|
|
that->callAsync([that,sharedTitleList] (){
|
|
struct vlc_player_title_list *titles = sharedTitleList.get();
|
|
that->m_chapterList.resetTitle(nullptr);
|
|
that->m_titleList.resetTitles(titles);
|
|
|
|
bool hasMenu = false;
|
|
bool hasTitles = false;
|
|
if (titles)
|
|
{
|
|
size_t nbTitles = vlc_player_title_list_GetCount(titles);
|
|
for( size_t i = 0; i < nbTitles; i++)
|
|
{
|
|
const vlc_player_title* title = vlc_player_title_list_GetAt(titles, i);
|
|
if( (title ->flags & INPUT_TITLE_MENU) != 0 )
|
|
{
|
|
hasMenu = true;
|
|
break;
|
|
}
|
|
}
|
|
hasTitles = nbTitles != 0;
|
|
}
|
|
if (hasTitles != that->m_hasTitles)
|
|
{
|
|
that->m_hasTitles = hasTitles;
|
|
emit that->q_func()->hasTitlesChanged(hasTitles);
|
|
}
|
|
if (!hasTitles && that->m_hasChapters)
|
|
{
|
|
that->m_hasChapters = false;
|
|
emit that->q_func()->hasChaptersChanged(false);
|
|
}
|
|
if (hasMenu != that->m_hasMenu)
|
|
{
|
|
that->m_hasMenu = hasMenu;
|
|
emit that->q_func()->hasMenuChanged(hasMenu);
|
|
}
|
|
});
|
|
}
|
|
|
|
static void on_player_title_selection_changed(vlc_player_t *,
|
|
const struct vlc_player_title *new_title, size_t new_idx, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_title_selection_changed");
|
|
|
|
bool hasChapter = (new_title != nullptr && new_title->chapter_count != 0);
|
|
that->callAsync([that,new_title,new_idx,hasChapter] (){
|
|
PlayerController* q = that->q_func();
|
|
that->m_chapterList.resetTitle(new_title);
|
|
that->m_titleList.setCurrent(new_idx);
|
|
that->m_hasChapters = hasChapter;
|
|
that->m_isInteractive = new_title && (new_title->flags & VLC_PLAYER_TITLE_INTERACTIVE);
|
|
that->m_isMenu = new_title && (new_title->flags & VLC_PLAYER_TITLE_MENU);
|
|
emit q->isMenuChanged( that->m_isMenu );
|
|
emit q->isInteractiveChanged( that->m_isInteractive );
|
|
emit q->hasChaptersChanged( hasChapter );
|
|
});
|
|
}
|
|
|
|
static void on_player_chapter_selection_changed(vlc_player_t *,
|
|
const struct vlc_player_title *, size_t,
|
|
const struct vlc_player_chapter *, size_t chapter_idx,
|
|
void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_chapter_selection_changed");
|
|
that->callAsync([that,chapter_idx] (){
|
|
that->m_chapterList.setCurrent(chapter_idx);
|
|
});
|
|
}
|
|
|
|
|
|
static void on_player_teletext_menu_changed(vlc_player_t *, bool has_teletext_menu, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_teletext_menu_changed, %s", has_teletext_menu ? "available" : "unavailable" );
|
|
that->callAsync([that,has_teletext_menu] () {
|
|
that->m_teletextAvailable = has_teletext_menu;
|
|
emit that->q_func()->teletextAvailableChanged(has_teletext_menu);
|
|
});
|
|
}
|
|
|
|
static void on_player_teletext_enabled_changed(vlc_player_t *, bool enabled, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_teletext_enabled_changed %s", enabled ? "enabled" : "disabled");
|
|
that->callAsync([that,enabled] () {
|
|
that->m_teletextEnabled = enabled;
|
|
emit that->q_func()->teletextEnabledChanged(enabled);
|
|
});
|
|
}
|
|
|
|
static void on_player_teletext_page_changed(vlc_player_t *, unsigned new_page, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_teletext_page_changed %u", new_page);
|
|
that->callAsync([that,new_page] () {
|
|
that->m_teletextPage = new_page;
|
|
emit that->q_func()->teletextPageChanged(new_page);
|
|
});
|
|
}
|
|
|
|
static void on_player_teletext_transparency_changed(vlc_player_t *, bool enabled, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_teletext_transparency_changed %s", enabled ? "enabled" : "disabled");
|
|
that->callAsync([that,enabled] () {
|
|
that->m_teletextTransparent = enabled;
|
|
emit that->q_func()->teletextTransparencyChanged(enabled);
|
|
});
|
|
}
|
|
|
|
static void on_player_category_delay_changed(vlc_player_t *,
|
|
enum es_format_category_e cat, vlc_tick_t new_delay,
|
|
void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_category_delay_changed: %d", cat );
|
|
that->callAsync([that,cat,new_delay] (){
|
|
switch (cat)
|
|
{
|
|
case AUDIO_ES:
|
|
that->m_audioDelay = new_delay;
|
|
emit that->q_func()->audioDelayChanged( new_delay );
|
|
break;
|
|
case SPU_ES:
|
|
that->m_subtitleDelay = new_delay;
|
|
emit that->q_func()->subtitleDelayChanged( new_delay );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
static void on_player_track_delay_changed(vlc_player_t *,
|
|
vlc_es_id_t *es_id, vlc_tick_t new_delay,
|
|
void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
SharedEsId sharedEsId = SharedEsId(es_id);
|
|
|
|
that->callAsync([that,sharedEsId,new_delay] (){
|
|
if (that->m_secondarySpuEsId == sharedEsId)
|
|
{
|
|
that->m_secondarySubtitleDelay = new_delay;
|
|
emit that->q_func()->secondarySubtitleDelayChanged( new_delay );
|
|
}
|
|
});
|
|
}
|
|
|
|
static void on_player_associated_subs_fps_changed(vlc_player_t *, float subs_fps, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_associated_subs_fps_changed");
|
|
that->callAsync([that,subs_fps] (){
|
|
that->m_subtitleFPS = subs_fps;
|
|
emit that->q_func()->subtitleFPSChanged( subs_fps );
|
|
});
|
|
}
|
|
|
|
void on_player_renderer_changed(vlc_player_t *, vlc_renderer_item_t *new_item, void *data)
|
|
{
|
|
VLC_UNUSED(new_item);
|
|
VLC_UNUSED(data);
|
|
}
|
|
|
|
|
|
static void on_player_record_changed(vlc_player_t *, bool recording, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_record_changed");
|
|
that->callAsync([that,recording] (){
|
|
that->m_recording = recording;
|
|
emit that->q_func()->recordingChanged( recording );
|
|
});
|
|
}
|
|
|
|
static void on_player_signal_changed(vlc_player_t *, float quality, float strength, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
VLC_UNUSED(quality);
|
|
VLC_UNUSED(strength);
|
|
msg_Dbg( that->p_intf, "on_player_signal_changed");
|
|
}
|
|
|
|
static void on_player_stats_changed(vlc_player_t *, const struct input_stats_t *stats, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
struct input_stats_t stats_tmp = *stats;
|
|
that->callAsync([that,stats_tmp] () {
|
|
that->m_stats = stats_tmp;
|
|
emit that->q_func()->statisticsUpdated( that->m_stats );
|
|
});
|
|
}
|
|
|
|
static void on_player_atobloop_changed(vlc_player_t *, enum vlc_player_abloop state, vlc_tick_t time, double, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_atobloop_changed");
|
|
that->callAsync([that,state,time] (){
|
|
PlayerController* q = that->q_func();
|
|
switch (state) {
|
|
case VLC_PLAYER_ABLOOP_NONE:
|
|
that->m_ABLoopA = VLC_TICK_INVALID;
|
|
that->m_ABLoopB = VLC_TICK_INVALID;
|
|
emit q->ABLoopAChanged(that->m_ABLoopA);
|
|
emit q->ABLoopBChanged(that->m_ABLoopB);
|
|
break;
|
|
case VLC_PLAYER_ABLOOP_A:
|
|
that->m_ABLoopA = time;
|
|
emit q->ABLoopAChanged(that->m_ABLoopA);
|
|
break;
|
|
case VLC_PLAYER_ABLOOP_B:
|
|
that->m_ABLoopB = time;
|
|
emit q->ABLoopBChanged(that->m_ABLoopB);
|
|
break;
|
|
}
|
|
that->m_ABLoopState = static_cast<PlayerController::ABLoopState>(state);
|
|
emit q->ABLoopStateChanged(that->m_ABLoopState);
|
|
});
|
|
}
|
|
|
|
static void on_player_media_meta_changed(vlc_player_t *, input_item_t *media, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_item_meta_changed");
|
|
|
|
SharedInputItem mediaPtr(media);
|
|
//call on object thread
|
|
that->callAsync([that,mediaPtr] () {
|
|
that->UpdateName(mediaPtr.get());
|
|
that->UpdateArt(mediaPtr.get());
|
|
that->UpdateMeta(mediaPtr.get());
|
|
});
|
|
}
|
|
|
|
static void on_player_media_epg_changed(vlc_player_t *, input_item_t *, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_item_epg_changed");
|
|
that->callAsync([that] () {
|
|
emit that->q_func()->epgChanged();
|
|
});
|
|
}
|
|
|
|
static void on_player_subitems_changed(vlc_player_t *, input_item_t *, input_item_node_t *, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_subitems_changed");
|
|
}
|
|
|
|
|
|
static void on_player_vout_changed(vlc_player_t *player, enum vlc_player_vout_action action,
|
|
vout_thread_t *, enum vlc_vout_order order, vlc_es_id_t *es_id, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_vout_list_changed");
|
|
|
|
switch (vlc_es_id_GetCat(es_id))
|
|
{
|
|
case VIDEO_ES:
|
|
{
|
|
//player is locked within callbacks*
|
|
size_t i_vout = 0;
|
|
vout_thread_t **vouts = vlc_player_vout_HoldAll(player, &i_vout);
|
|
|
|
std::shared_ptr<vout_thread_t*> voutsPtr( vouts, [i_vout]( vout_thread_t**vouts ) {
|
|
for (size_t i = 0; i < i_vout; i++)
|
|
vout_Release( vouts[i] );
|
|
free(vouts);
|
|
});
|
|
|
|
//call on object thread
|
|
that->callAsync([that,voutsPtr,i_vout,action] () {
|
|
that->UpdateVouts(voutsPtr.get(), i_vout, action);
|
|
});
|
|
break;
|
|
}
|
|
case SPU_ES:
|
|
{
|
|
SharedEsId sharedEsId = SharedEsId(es_id);
|
|
that->callAsync([that,sharedEsId,order] () {
|
|
that->UpdateSpuOrder(sharedEsId.get(), order);
|
|
});
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//player vout callbacks
|
|
|
|
static void on_player_vout_fullscreen_changed(vout_thread_t* vout, bool is_fullscreen, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_vout_fullscreen_changed %s", is_fullscreen ? "fullscreen" : "windowed");
|
|
|
|
SharedVOutThread vOutThread(vout);
|
|
that->callAsync([that,vOutThread,is_fullscreen] () {
|
|
PlayerController* q = that->q_func();
|
|
const PlayerController::VOutThreadList voutList = q->getVouts();
|
|
if (vOutThread == nullptr //property sets for all vout
|
|
|| (voutList.size() == 1 && vOutThread.get() == voutList[0].get()) ) //on the only vout
|
|
{
|
|
that->m_fullscreen = is_fullscreen;
|
|
emit q->fullscreenChanged(is_fullscreen);
|
|
}
|
|
});
|
|
}
|
|
|
|
static void on_player_vout_wallpaper_mode_changed(vout_thread_t* vout, bool enabled, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_vout_wallpaper_mode_changed %s", enabled ? "enabled" : "disabled");
|
|
|
|
SharedVOutThread vOutThread(vout);
|
|
that->callAsync([that,vOutThread, enabled] () {
|
|
PlayerController* q = that->q_func();
|
|
const PlayerController::VOutThreadList voutList = q->getVouts();
|
|
if (vOutThread == nullptr //property sets for all vout
|
|
|| (voutList.size() == 1 && vOutThread.get() == voutList[0].get()) ) //on the only vout
|
|
{
|
|
that->m_wallpaperMode = enabled;
|
|
emit q->wallpaperModeChanged(enabled);
|
|
}
|
|
});
|
|
}
|
|
|
|
//player aout callbacks
|
|
|
|
static void on_player_aout_volume_changed(audio_output_t *, float volume, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_aout_volume_changed");
|
|
that->callAsync([that,volume](){
|
|
that->m_volume = volume;
|
|
emit that->q_func()->volumeChanged( volume );
|
|
});
|
|
}
|
|
|
|
static void on_player_aout_mute_changed(audio_output_t *, bool muted, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_aout_mute_changed");
|
|
that->callAsync([that,muted](){
|
|
that->m_muted = muted;
|
|
emit that->q_func()->soundMuteChanged(muted);
|
|
});
|
|
}
|
|
|
|
static void on_player_corks_changed(vlc_player_t *, unsigned, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_player_corks_changed");
|
|
}
|
|
|
|
static void on_player_playback_restore_queried(vlc_player_t *, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
msg_Dbg( that->p_intf, "on_playback_restore_queried");
|
|
that->callAsync([that](){
|
|
that->m_canRestorePlayback = true;
|
|
emit that->q_func()->playbackRestoreQueried();
|
|
});
|
|
}
|
|
|
|
static void on_player_timer_update(const struct vlc_player_timer_point *point,
|
|
void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
if (that->seeking)
|
|
return;
|
|
|
|
that->callAsync([that,point_copy = *point](){
|
|
PlayerController* q = that->q_func();
|
|
|
|
that->m_player_time = point_copy;
|
|
bool lengthOrRateChanged = false;
|
|
|
|
if (that->m_length != that->m_player_time.length)
|
|
{
|
|
that->m_length = that->m_player_time.length;
|
|
emit q->lengthChanged(that->m_length);
|
|
|
|
lengthOrRateChanged = true;
|
|
}
|
|
if (that->m_rate != that->m_player_time.rate)
|
|
{
|
|
that->m_rate = that->m_player_time.rate;
|
|
emit q->rateChanged(that->m_rate);
|
|
|
|
lengthOrRateChanged = true;
|
|
}
|
|
|
|
vlc_tick_t system_now = vlc_tick_now();
|
|
if (that->interpolateTime(system_now) == VLC_SUCCESS)
|
|
{
|
|
if (lengthOrRateChanged || !that->m_position_timer.isActive())
|
|
{
|
|
q->updatePosition();
|
|
|
|
if (that->m_player_time.system_date != VLC_TICK_MAX)
|
|
{
|
|
// Setup the position update interval, depending on media
|
|
// length and rate. XXX: VLC_TICK_FROM_MS(1) is an educated
|
|
// guess, it should be also calculated according to the slider
|
|
// size.
|
|
|
|
vlc_tick_t interval =
|
|
that->m_length / that->m_player_time.rate / VLC_TICK_FROM_MS(1);
|
|
if (interval < POSITION_MIN_UPDATE_INTERVAL)
|
|
interval = POSITION_MIN_UPDATE_INTERVAL;
|
|
|
|
that->m_position_timer.start(MS_FROM_VLC_TICK(interval));
|
|
}
|
|
}
|
|
q->updateTime(system_now, lengthOrRateChanged);
|
|
}
|
|
|
|
});
|
|
}
|
|
|
|
static void on_player_timer_paused(vlc_tick_t system_date, void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
that->callAsync([that,system_date](){
|
|
PlayerController* q = that->q_func();
|
|
|
|
if (system_date != VLC_TICK_INVALID
|
|
&& that->interpolateTime(system_date) == VLC_SUCCESS)
|
|
{
|
|
// The discontinuity event got a valid system date, update the time
|
|
// properties.
|
|
q->updatePosition();
|
|
q->updateTime(system_date, false);
|
|
}
|
|
|
|
// And stop the timers.
|
|
that->m_position_timer.stop();
|
|
that->m_time_timer.stop();
|
|
});
|
|
}
|
|
|
|
static void on_player_timer_seek(const struct vlc_player_timer_point *point,
|
|
void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate*>(data);
|
|
if (point != NULL)
|
|
{
|
|
that->seeking = true;
|
|
that->callAsync([that,point_copy = *point](){
|
|
PlayerController* q = that->q_func();
|
|
|
|
that->m_player_time = point_copy;
|
|
if (that->m_player_time.position > 0)
|
|
that->m_position = that->m_player_time.position;
|
|
if (that->m_player_time.ts != VLC_TICK_INVALID)
|
|
that->m_time = that->m_player_time.ts - VLC_TICK_0;
|
|
|
|
q->updatePosition();
|
|
q->updateTime(VLC_TICK_INVALID, false);
|
|
|
|
that->m_position_timer.stop();
|
|
that->m_time_timer.stop();
|
|
});
|
|
}
|
|
else
|
|
that->seeking = false;
|
|
}
|
|
static void on_player_timer_smpte_update(const struct vlc_player_timer_smpte_timecode *tc,
|
|
void *data)
|
|
{
|
|
PlayerControllerPrivate* that = static_cast<PlayerControllerPrivate *>(data);
|
|
|
|
that->callAsync([that, tc_copy = *tc](){
|
|
that->m_highResolutionTime = QString::asprintf("%02u:%02u:%02u%c%02u",
|
|
tc_copy.hours,
|
|
tc_copy.minutes,
|
|
tc_copy.seconds,
|
|
tc_copy.drop_frame ? '.' : ':',
|
|
tc_copy.frames);
|
|
emit that->q_func()->highResolutionTimeChanged(that->m_highResolutionTime);
|
|
});
|
|
}
|
|
|
|
|
|
static void on_preparse_ended_callback(input_item_t *p_item,
|
|
int, void *userdata)
|
|
{
|
|
PlayerControllerPrivate *me = reinterpret_cast<PlayerControllerPrivate *>(userdata);
|
|
me->onArtFetchEnded(p_item, input_item_IsArtFetched(p_item));
|
|
}
|
|
|
|
|
|
} //extern "C"
|
|
|
|
static const struct vlc_player_cbs player_cbs = {
|
|
on_player_current_media_changed,
|
|
on_player_state_changed,
|
|
on_player_error_changed,
|
|
on_player_buffering,
|
|
nullptr, // on_player_rate_changed: handled by on_player_timer_update
|
|
on_player_capabilities_changed,
|
|
nullptr, // on_player_position_changed: handled by on_player_timer_update
|
|
nullptr, // on_player_length_changed: handled by on_player_timer_update
|
|
on_player_track_list_changed,
|
|
on_player_track_selection_changed,
|
|
on_player_track_delay_changed,
|
|
on_player_program_list_changed,
|
|
on_player_program_selection_changed,
|
|
on_player_titles_changed,
|
|
on_player_title_selection_changed,
|
|
on_player_chapter_selection_changed,
|
|
on_player_teletext_menu_changed,
|
|
on_player_teletext_enabled_changed,
|
|
on_player_teletext_page_changed,
|
|
on_player_teletext_transparency_changed,
|
|
on_player_category_delay_changed,
|
|
on_player_associated_subs_fps_changed,
|
|
on_player_renderer_changed,
|
|
on_player_record_changed,
|
|
on_player_signal_changed,
|
|
on_player_stats_changed,
|
|
on_player_atobloop_changed,
|
|
on_player_media_meta_changed,
|
|
on_player_media_epg_changed,
|
|
on_player_subitems_changed,
|
|
nullptr, // on_media_attachments_added: not used
|
|
on_player_vout_changed,
|
|
on_player_corks_changed,
|
|
on_player_playback_restore_queried,
|
|
nullptr, // on_stopping_current_media: not used
|
|
};
|
|
|
|
static const vlc_player_vout_cbs player_vout_cbs = []{
|
|
struct vlc_player_vout_cbs cbs{};
|
|
cbs.on_fullscreen_changed = on_player_vout_fullscreen_changed;
|
|
cbs.on_wallpaper_mode_changed = on_player_vout_wallpaper_mode_changed;
|
|
return cbs;
|
|
}();
|
|
|
|
static const vlc_player_aout_cbs player_aout_cbs = []{
|
|
struct vlc_player_aout_cbs cbs{};
|
|
cbs.on_volume_changed = on_player_aout_volume_changed;
|
|
cbs.on_mute_changed = on_player_aout_mute_changed;
|
|
return cbs;
|
|
}();
|
|
|
|
static const vlc_player_timer_cbs player_timer_cbs = []{
|
|
struct vlc_player_timer_cbs cbs {};
|
|
cbs.on_update = on_player_timer_update;
|
|
cbs.on_paused = on_player_timer_paused;
|
|
cbs.on_seek = on_player_timer_seek;
|
|
return cbs;
|
|
}();
|
|
|
|
static const struct vlc_player_timer_smpte_cbs player_timer_smpte_cbs = {
|
|
on_player_timer_smpte_update
|
|
};
|
|
|
|
// art fetcher callbacks
|
|
|
|
static const input_item_parser_cbs_t art_fetcher_cbs = []{
|
|
input_item_parser_cbs_t cbs{};
|
|
cbs.on_ended = on_preparse_ended_callback;
|
|
return cbs;
|
|
}();
|
|
|
|
|
|
PlayerControllerPrivate::PlayerControllerPrivate(PlayerController *playercontroller, qt_intf_t *p_intf)
|
|
: q_ptr(playercontroller)
|
|
, p_intf(p_intf)
|
|
, m_player(p_intf->p_player)
|
|
, m_videoTracks(m_player)
|
|
, m_audioTracks(m_player)
|
|
, m_subtitleTracks(m_player)
|
|
, m_titleList(m_player)
|
|
, m_chapterList(m_player)
|
|
, m_programList(m_player)
|
|
, m_zoom((vout_thread_t*)nullptr, "zoom")
|
|
, m_aspectRatio((vout_thread_t*)nullptr, "aspect-ratio")
|
|
, m_crop((vout_thread_t*)nullptr, "crop")
|
|
, m_fit((vout_thread_t*)nullptr, "fit")
|
|
, m_deinterlace((vout_thread_t*)nullptr, "deinterlace")
|
|
, m_deinterlaceMode((vout_thread_t*)nullptr, "deinterlace-mode")
|
|
, m_autoscale((vout_thread_t*)nullptr, "autoscale")
|
|
, m_audioStereoMode((audio_output_t*)nullptr, "stereo-mode")
|
|
, m_audioMixMode((audio_output_t*)nullptr, "mix-mode")
|
|
, m_audioDeviceList(m_player)
|
|
, m_audioVisualization((audio_output_t*)nullptr, "visual")
|
|
, m_rendererManager(p_intf, m_player)
|
|
{
|
|
{
|
|
vlc_player_locker locker{m_player};
|
|
m_player_listener = vlc_player_AddListener( m_player, &player_cbs, this );
|
|
m_player_aout_listener = vlc_player_aout_AddListener( m_player, &player_aout_cbs, this );
|
|
m_player_vout_listener = vlc_player_vout_AddListener( m_player, &player_vout_cbs, this );
|
|
m_player_timer = vlc_player_AddTimer( m_player, VLC_TICK_FROM_MS(500), &player_timer_cbs, this );
|
|
}
|
|
|
|
QObject::connect( &m_autoscale, &QVLCBool::valueChanged, q_ptr, &PlayerController::autoscaleChanged );
|
|
QObject::connect( &m_audioVisualization, &VLCVarChoiceModel::hasCurrentChanged, q_ptr, &PlayerController::hasAudioVisualizationChanged );
|
|
|
|
m_time_timer.setSingleShot( true );
|
|
m_time_timer.setTimerType( Qt::PreciseTimer );
|
|
|
|
// Initialise fullscreen to match the player state
|
|
m_fullscreen = vlc_player_vout_IsFullscreen( m_player );
|
|
|
|
m_volume = vlc_player_aout_GetVolume( m_player );
|
|
}
|
|
|
|
void PlayerControllerPrivate::addSMPTETimer()
|
|
{
|
|
assert( !m_player_timer_smpte );
|
|
|
|
vlc_player_locker lock{ m_player };
|
|
m_player_timer_smpte = vlc_player_AddSmpteTimer( m_player, &player_timer_smpte_cbs, this );
|
|
assert( m_player_timer_smpte );
|
|
}
|
|
|
|
void PlayerControllerPrivate::removeSMPTETimer()
|
|
{
|
|
assert( m_player_timer_smpte );
|
|
|
|
vlc_player_locker lock{ m_player };
|
|
vlc_player_RemoveTimer( m_player, m_player_timer_smpte );
|
|
m_player_timer_smpte = nullptr;
|
|
}
|
|
|
|
PlayerController::PlayerController( qt_intf_t *_p_intf )
|
|
: QObject(NULL)
|
|
, d_ptr( new PlayerControllerPrivate(this, _p_intf) )
|
|
{
|
|
connect( &d_ptr->m_position_timer, &QTimer::timeout, this, &PlayerController::updatePositionFromTimer );
|
|
connect( &d_ptr->m_time_timer, &QTimer::timeout, this, &PlayerController::updateTimeFromTimer );
|
|
}
|
|
|
|
PlayerController::~PlayerController()
|
|
{
|
|
}
|
|
|
|
// PLAYBACK
|
|
|
|
vlc_player_t * PlayerController::getPlayer() const
|
|
{
|
|
Q_D(const PlayerController);
|
|
|
|
return d->m_player;
|
|
}
|
|
|
|
input_item_t *PlayerController::getInput() const
|
|
{
|
|
Q_D(const PlayerController);
|
|
vlc_player_locker locker{ d->m_player };
|
|
return vlc_player_GetCurrentMedia( d->m_player );
|
|
}
|
|
|
|
bool PlayerController::isStarted() const
|
|
{
|
|
Q_D(const PlayerController);
|
|
vlc_player_locker locker{ d->m_player };
|
|
return vlc_player_IsStarted( d->m_player );
|
|
}
|
|
|
|
bool PlayerController::hasInput() const
|
|
{
|
|
return getInput();
|
|
}
|
|
|
|
void PlayerController::reverse()
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "reverse");
|
|
vlc_player_locker lock{ d->m_player };
|
|
if ( vlc_player_CanChangeRate( d->m_player ) )
|
|
{
|
|
float f_rate_ = vlc_player_GetRate( d->m_player );
|
|
vlc_player_ChangeRate( d->m_player, -f_rate_ );
|
|
}
|
|
}
|
|
|
|
void PlayerController::setRate( float new_rate )
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "setRate %f", new_rate);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if ( vlc_player_CanChangeRate( d->m_player ) )
|
|
vlc_player_ChangeRate( d->m_player, new_rate );
|
|
}
|
|
|
|
void PlayerController::slower()
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "slower");
|
|
vlc_player_locker lock{ d->m_player };
|
|
if ( vlc_player_CanChangeRate( d->m_player ) )
|
|
vlc_player_DecrementRate( d->m_player );
|
|
}
|
|
|
|
void PlayerController::faster()
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "faster");
|
|
vlc_player_locker lock{ d->m_player };
|
|
if ( vlc_player_CanChangeRate( d->m_player ) )
|
|
vlc_player_IncrementRate( d->m_player );
|
|
}
|
|
|
|
void PlayerController::littlefaster()
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "littlefaster");
|
|
var_SetInteger( vlc_object_instance(d->p_intf), "key-action", ACTIONID_RATE_FASTER_FINE );
|
|
}
|
|
|
|
void PlayerController::littleslower()
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "littleslower");
|
|
var_SetInteger( vlc_object_instance(d->p_intf), "key-action", ACTIONID_RATE_SLOWER_FINE );
|
|
}
|
|
|
|
void PlayerController::normalRate()
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "normalRate");
|
|
vlc_player_locker lock{ d->m_player };
|
|
if ( vlc_player_CanChangeRate( d->m_player ) )
|
|
vlc_player_ChangeRate( d->m_player, 1.0f );
|
|
}
|
|
|
|
|
|
void PlayerController::setTime(VLCTick new_time)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_SetTime( d->m_player, new_time );
|
|
}
|
|
|
|
void PlayerController::setPosition(double position)
|
|
{
|
|
Q_D(PlayerController);
|
|
if (qFuzzyCompare(d->m_position, position))
|
|
return;
|
|
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_SetPosition( d->m_player, position );
|
|
}
|
|
|
|
void PlayerController::jumpFwd()
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "jumpFwd");
|
|
int i_interval = var_InheritInteger( d->p_intf, "short-jump-size" );
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_JumpTime( d->m_player, vlc_tick_from_sec( i_interval ) );
|
|
}
|
|
|
|
void PlayerController::jumpBwd()
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "jumpBwd");
|
|
int i_interval = var_InheritInteger( d->p_intf, "short-jump-size" );
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_JumpTime( d->m_player, vlc_tick_from_sec( -i_interval ) );
|
|
}
|
|
|
|
void PlayerController::jumpToTime(VLCTick i_time)
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg( d->p_intf, "jumpToTime");
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_JumpTime( d->m_player, i_time );
|
|
}
|
|
|
|
void PlayerController::jumpToPos( double new_pos )
|
|
{
|
|
Q_D(PlayerController);
|
|
{
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
vlc_player_SetPosition( d->m_player, new_pos );
|
|
}
|
|
emit seekRequested( new_pos );
|
|
}
|
|
|
|
void PlayerController::frameNext()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_NextVideoFrame( d->m_player );
|
|
}
|
|
|
|
//TRACKS
|
|
|
|
void PlayerController::setAudioDelay(VLCTick delay)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_SetAudioDelay( d->m_player, delay, VLC_PLAYER_WHENCE_ABSOLUTE );
|
|
}
|
|
|
|
/*Q_INVOKABLE*/void PlayerController::addAudioDelay(VLCTick delay)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_SetAudioDelay( d->m_player, delay, VLC_PLAYER_WHENCE_RELATIVE );
|
|
emit audioDelayChanged(delay);
|
|
}
|
|
|
|
void PlayerController::setSubtitleDelay(VLCTick delay)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if(!d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_SetSubtitleDelay( d->m_player, delay, VLC_PLAYER_WHENCE_ABSOLUTE );
|
|
}
|
|
|
|
/*Q_INVOKABLE*/void PlayerController::addSubtitleDelay(VLCTick delay)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_SetSubtitleDelay( d->m_player, delay, VLC_PLAYER_WHENCE_RELATIVE);
|
|
emit subtitleDelayChanged(delay);
|
|
}
|
|
|
|
void PlayerController::setSecondarySubtitleDelay(VLCTick delay)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if (d->m_secondarySpuEsId.get() != NULL)
|
|
vlc_player_SetEsIdDelay(d->m_player, d->m_secondarySpuEsId.get(),
|
|
delay, VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
}
|
|
|
|
/*Q_INVOKABLE*/void PlayerController::addSecondarySubtitleDelay(VLCTick delay)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if (d->m_secondarySpuEsId.get() != NULL) {
|
|
vlc_player_SetEsIdDelay(d->m_player, d->m_secondarySpuEsId.get(),
|
|
delay, VLC_PLAYER_WHENCE_RELATIVE);
|
|
emit secondarySubtitleDelayChanged(delay);
|
|
}
|
|
}
|
|
|
|
int PlayerController::getAudioDelayMS() const
|
|
{
|
|
return MS_FROM_VLC_TICK( getAudioDelay() );
|
|
}
|
|
|
|
void PlayerController::setAudioDelayMS(int ms)
|
|
{
|
|
setAudioDelay( VLC_TICK_FROM_MS(ms) );
|
|
}
|
|
|
|
int PlayerController::getSubtitleDelayMS() const
|
|
{
|
|
return MS_FROM_VLC_TICK( getSubtitleDelay() );
|
|
}
|
|
|
|
void PlayerController::setSubtitleDelayMS(int ms)
|
|
{
|
|
setSubtitleDelay( VLC_TICK_FROM_MS(ms) );
|
|
}
|
|
|
|
int PlayerController::getSecondarySubtitleDelayMS() const
|
|
{
|
|
return MS_FROM_VLC_TICK( getSecondarySubtitleDelay() );
|
|
}
|
|
|
|
void PlayerController::setSecondarySubtitleDelayMS(int ms)
|
|
{
|
|
setSecondarySubtitleDelay( VLC_TICK_FROM_MS(ms) );
|
|
}
|
|
|
|
void PlayerController::setSubtitleFPS(float fps)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
vlc_player_SetAssociatedSubsFPS( d->m_player, fps );
|
|
}
|
|
|
|
//TITLE/CHAPTER/MENU
|
|
|
|
void PlayerController::sectionPrev()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
{
|
|
if (vlc_player_GetSelectedChapter( d->m_player ) != NULL)
|
|
vlc_player_SelectPrevChapter( d->m_player );
|
|
else
|
|
vlc_player_SelectPrevTitle( d->m_player );
|
|
}
|
|
}
|
|
|
|
void PlayerController::sectionNext()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
{
|
|
if (vlc_player_GetSelectedChapter( d->m_player ) != NULL)
|
|
vlc_player_SelectNextChapter( d->m_player );
|
|
else
|
|
vlc_player_SelectNextTitle( d->m_player );
|
|
}
|
|
}
|
|
|
|
void PlayerController::sectionMenu()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
vlc_player_Navigate( d->m_player, VLC_PLAYER_NAV_MENU );
|
|
}
|
|
|
|
void PlayerController::navigateUp()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
vlc_player_Navigate( d->m_player, VLC_PLAYER_NAV_UP);
|
|
}
|
|
|
|
void PlayerController::navigateDown()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
vlc_player_Navigate( d->m_player, VLC_PLAYER_NAV_DOWN);
|
|
}
|
|
|
|
void PlayerController::navigateLeft()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
vlc_player_Navigate( d->m_player, VLC_PLAYER_NAV_LEFT);
|
|
}
|
|
|
|
void PlayerController::navigateRight()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
vlc_player_Navigate( d->m_player, VLC_PLAYER_NAV_RIGHT);
|
|
}
|
|
|
|
void PlayerController::navigateActivate()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
vlc_player_Navigate( d->m_player, VLC_PLAYER_NAV_ACTIVATE);
|
|
}
|
|
|
|
void PlayerController::chapterNext()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if (vlc_player_IsStarted(d->m_player ))
|
|
vlc_player_SelectNextChapter( d->m_player );
|
|
}
|
|
|
|
void PlayerController::chapterPrev()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if (vlc_player_IsStarted(d->m_player ))
|
|
vlc_player_SelectPrevChapter( d->m_player );
|
|
}
|
|
|
|
void PlayerController::titleNext()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if (vlc_player_IsStarted(d->m_player ))
|
|
vlc_player_SelectNextTitle( d->m_player );
|
|
}
|
|
|
|
void PlayerController::titlePrev()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if (vlc_player_IsStarted(d->m_player ))
|
|
vlc_player_SelectPrevTitle( d->m_player );
|
|
}
|
|
|
|
//PROGRAMS
|
|
|
|
void PlayerController::changeProgram( int program )
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if( vlc_player_IsStarted( d->m_player ) )
|
|
vlc_player_SelectProgram( d->m_player, program );
|
|
}
|
|
|
|
//TELETEXT
|
|
|
|
|
|
void PlayerController::enableTeletext( bool enable )
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if (vlc_player_IsStarted(d->m_player ))
|
|
vlc_player_SetTeletextEnabled( d->m_player, enable );
|
|
}
|
|
|
|
void PlayerController::setTeletextPage(int page)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if (vlc_player_IsTeletextEnabled( d->m_player ))
|
|
vlc_player_SelectTeletextPage( d->m_player, page );
|
|
}
|
|
|
|
void PlayerController::setTeletextTransparency( bool transparent )
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !d->isCurrentItemSynced() )
|
|
return;
|
|
if (vlc_player_IsTeletextEnabled( d->m_player ))
|
|
vlc_player_SetTeletextTransparency( d->m_player, transparent );
|
|
}
|
|
|
|
//VOUT PROPERTIES
|
|
|
|
PlayerController::VOutThreadList PlayerController::getVouts() const
|
|
{
|
|
Q_D(const PlayerController);
|
|
vout_thread_t **pp_vout;
|
|
VOutThreadList VoutList;
|
|
size_t i_vout;
|
|
{
|
|
vlc_player_locker lock{ d->m_player };
|
|
if( !vlc_player_IsStarted( d->m_player ) )
|
|
return VOutThreadList{};
|
|
i_vout = 0;
|
|
pp_vout = vlc_player_vout_HoldAll( d->m_player, &i_vout );
|
|
if ( i_vout <= 0 )
|
|
return VOutThreadList{};
|
|
}
|
|
VoutList.reserve( i_vout );
|
|
for( size_t i = 0; i < i_vout; i++ )
|
|
{
|
|
assert( pp_vout[i] );
|
|
//pass ownership
|
|
VoutList.append(SharedVOutThread(pp_vout[i], false));
|
|
}
|
|
free( pp_vout );
|
|
|
|
return VoutList;
|
|
}
|
|
|
|
SharedVOutThread PlayerController::getVout()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vout_thread_t* vout = vlc_player_vout_Hold( d->m_player );
|
|
if( vout == NULL )
|
|
return SharedVOutThread{};
|
|
return SharedVOutThread{vout, false};
|
|
}
|
|
|
|
void PlayerController::setFullscreen( bool new_val )
|
|
{
|
|
Q_D(PlayerController);
|
|
msg_Dbg(d->p_intf, "setFullscreen %s", new_val? "fullscreen" : "windowed");
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_vout_SetFullscreen( d->m_player, new_val );
|
|
}
|
|
|
|
void PlayerController::toggleFullscreen()
|
|
{
|
|
Q_D(PlayerController);
|
|
setFullscreen( ! d->m_fullscreen );
|
|
}
|
|
|
|
void PlayerController::setWallpaperMode( bool new_val )
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_vout_SetWallpaperModeEnabled( d->m_player, new_val );
|
|
}
|
|
|
|
bool PlayerController::getAutoscale( ) const
|
|
{
|
|
Q_D(const PlayerController);
|
|
return d->m_autoscale.getValue();
|
|
}
|
|
|
|
void PlayerController::setAutoscale( bool new_val )
|
|
{
|
|
Q_D(PlayerController);
|
|
d->m_autoscale.setValue( new_val );
|
|
}
|
|
|
|
//AOUT PROPERTIES
|
|
|
|
SharedAOut PlayerController::getAout()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
return SharedAOut( vlc_player_aout_Hold( d->m_player ), false );
|
|
}
|
|
|
|
void PlayerController::setVolume(float volume)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_aout_SetVolume( d->m_player, volume );
|
|
}
|
|
|
|
void PlayerController::setVolumeUp(int steps)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_aout_IncrementVolume( d->m_player, steps, NULL );
|
|
}
|
|
|
|
void PlayerController::setVolumeDown(int steps)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_aout_DecrementVolume( d->m_player, steps, NULL );
|
|
}
|
|
|
|
void PlayerController::setMuted(bool muted)
|
|
{
|
|
Q_D(PlayerController);
|
|
if( d->m_muted == muted )
|
|
return;
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_aout_Mute( d->m_player, muted );
|
|
}
|
|
|
|
void PlayerController::toggleMuted()
|
|
{
|
|
Q_D(PlayerController);
|
|
setMuted( !d->m_muted );
|
|
}
|
|
|
|
bool PlayerController::hasAudioVisualization() const
|
|
{
|
|
Q_D(const PlayerController);
|
|
return d->m_audioVisualization.hasCurrent();
|
|
}
|
|
|
|
void PlayerController::updatePosition()
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
// Update position properties
|
|
emit positionChanged(d->m_position);
|
|
emit positionUpdated(d->m_position, d->m_time,
|
|
SEC_FROM_VLC_TICK(d->m_length));
|
|
}
|
|
|
|
void PlayerController::updatePositionFromTimer()
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
vlc_tick_t system_now = vlc_tick_now();
|
|
if (d->interpolateTime(system_now) == VLC_SUCCESS)
|
|
updatePosition();
|
|
}
|
|
|
|
void PlayerController::updateTime(vlc_tick_t system_now, bool forceUpdate)
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
// Update time properties
|
|
emit timeChanged(d->m_time);
|
|
if (d->m_time != VLC_TICK_INVALID && d->m_length != VLC_TICK_INVALID)
|
|
d->m_remainingTime = d->m_length - d->m_time;
|
|
else
|
|
d->m_remainingTime = VLC_TICK_INVALID;
|
|
emit remainingTimeChanged(d->m_remainingTime);
|
|
|
|
if (system_now != VLC_TICK_INVALID
|
|
&& d->m_player_time.system_date != VLC_TICK_MAX
|
|
&& (forceUpdate || !d->m_time_timer.isActive()))
|
|
{
|
|
// Tell the timer to wait until the next second is reached.
|
|
vlc_tick_t next_update_date =
|
|
vlc_player_timer_point_GetNextIntervalDate(&d->m_player_time, system_now,
|
|
d->m_time, VLC_TICK_FROM_SEC(1));
|
|
|
|
vlc_tick_t next_update_interval = next_update_date - system_now;
|
|
|
|
if (next_update_interval > 0)
|
|
{
|
|
// The timer can be triggered a little before. In that case, it's
|
|
// likely that we didn't reach the next next second. It's better to
|
|
// add a very small delay in order to be triggered after the next
|
|
// seconds.
|
|
static const unsigned imprecision_delay_ms = 30;
|
|
|
|
d->m_time_timer.start(MS_FROM_VLC_TICK(next_update_interval)
|
|
+ imprecision_delay_ms);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlayerController::openVLsub()
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
const auto extensionManager = ExtensionsManager::getInstance( d->p_intf );
|
|
if ( !extensionManager->isLoaded() )
|
|
extensionManager->loadExtensions();
|
|
|
|
extensionManager->openVLsub();
|
|
}
|
|
|
|
void PlayerController::updateTimeFromTimer()
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
vlc_tick_t system_now = vlc_tick_now();
|
|
if (d->interpolateTime(system_now) == VLC_SUCCESS)
|
|
updateTime(system_now, false);
|
|
}
|
|
|
|
void PlayerController::restorePlaybackPos()
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
if (!d->isCurrentItemSynced())
|
|
return;
|
|
vlc_player_RestorePlaybackPos( d->m_player );
|
|
}
|
|
|
|
void PlayerController::acknowledgeRestoreCallback()
|
|
{
|
|
Q_D(PlayerController);
|
|
if (d->m_canRestorePlayback) {
|
|
d->m_canRestorePlayback = false;
|
|
emit playbackRestoreQueried();
|
|
}
|
|
}
|
|
|
|
//MISC
|
|
|
|
void PlayerController::setABloopState(ABLoopState state)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_SetAtoBLoop( d->m_player, static_cast<vlc_player_abloop>(state));
|
|
}
|
|
|
|
void PlayerController::toggleABloopState()
|
|
{
|
|
Q_D(PlayerController);
|
|
switch (d->m_ABLoopState) {
|
|
case ABLOOP_STATE_NONE:
|
|
setABloopState(ABLOOP_STATE_A);
|
|
break;
|
|
case ABLOOP_STATE_A:
|
|
setABloopState(ABLOOP_STATE_B);
|
|
break;
|
|
case ABLOOP_STATE_B:
|
|
setABloopState(ABLOOP_STATE_NONE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void PlayerController::toggleRecord()
|
|
{
|
|
Q_D(PlayerController);
|
|
setRecording(!d->m_recording);
|
|
}
|
|
|
|
void PlayerController::toggleVisualization()
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
if ( d->m_audioVisualization.rowCount() < 1 )
|
|
return;
|
|
|
|
if ( !d->m_audioVisualization.hasCurrent()
|
|
|| d->m_audioVisualization.currentRow() == 0 /*0th row is "Disable"*/)
|
|
{
|
|
const int r = rand() % d->m_audioVisualization.rowCount();
|
|
d->m_audioVisualization.setData( d->m_audioVisualization.index( r )
|
|
, QVariant::fromValue<bool>( true )
|
|
, Qt::CheckStateRole );
|
|
}
|
|
else
|
|
{
|
|
d->m_audioVisualization.setData( d->m_audioVisualization.index(0)
|
|
, QVariant::fromValue<bool>( true )
|
|
, Qt::CheckStateRole);
|
|
}
|
|
}
|
|
|
|
void PlayerController::requestAddSMPTETimer()
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
if ( ++d->m_smpteTimerRequestCount == 1 )
|
|
{
|
|
d->addSMPTETimer();
|
|
}
|
|
}
|
|
|
|
void PlayerController::requestRemoveSMPTETimer()
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
assert( d->m_smpteTimerRequestCount > 0 );
|
|
if ( --d->m_smpteTimerRequestCount == 0 )
|
|
{
|
|
d->removeSMPTETimer();
|
|
}
|
|
}
|
|
|
|
void PlayerController::setRecording( bool recording )
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
vlc_player_SetRecordingEnabled( d->m_player, recording, NULL );
|
|
}
|
|
|
|
void PlayerController::snapshot()
|
|
{
|
|
SharedVOutThread vout = getVout();
|
|
if (vout)
|
|
{
|
|
/* Passing a lambda directly would require Qt 5.15:
|
|
* <https://doc.qt.io/qt-5/qthreadpool.html#start-1>
|
|
*/
|
|
struct SnapshotTask : public QRunnable
|
|
{
|
|
SharedVOutThread vout;
|
|
SnapshotTask(SharedVOutThread vout) : vout(std::move(vout)) {}
|
|
void run() override
|
|
{
|
|
var_TriggerCallback(vout.get(), "video-snapshot");
|
|
}
|
|
};
|
|
|
|
QThreadPool::globalInstance()->start(new SnapshotTask(std::move(vout)));
|
|
}
|
|
}
|
|
|
|
|
|
//OTHER
|
|
|
|
|
|
/* Playlist Control functions */
|
|
|
|
void PlayerController::requestArtUpdate( input_item_t *p_item )
|
|
{
|
|
Q_D(PlayerController);
|
|
|
|
if (d->m_preparser == nullptr)
|
|
{
|
|
vlc_tick_t default_timeout =
|
|
VLC_TICK_FROM_MS(var_InheritInteger(d->p_intf, "preparse-timeout"));
|
|
if (default_timeout < 0)
|
|
default_timeout = 0;
|
|
|
|
const struct vlc_preparser_cfg cfg = [default_timeout]{
|
|
struct vlc_preparser_cfg cfg{};
|
|
cfg.types = VLC_PREPARSER_TYPE_FETCHMETA_ALL;
|
|
cfg.max_parser_threads = 1;
|
|
cfg.timeout = default_timeout;
|
|
return cfg;
|
|
}();
|
|
d->m_preparser = vlc_preparser_New(VLC_OBJECT(d->p_intf), &cfg);
|
|
if (unlikely(d->m_preparser == nullptr))
|
|
return;
|
|
}
|
|
|
|
int fetch_options = var_InheritBool( d->p_intf, "metadata-network-access" ) ?
|
|
VLC_PREPARSER_TYPE_FETCHMETA_ALL :
|
|
VLC_PREPARSER_TYPE_FETCHMETA_LOCAL;
|
|
|
|
vlc_preparser_Push( d->m_preparser, p_item, fetch_options,
|
|
&art_fetcher_cbs, d );
|
|
}
|
|
|
|
void PlayerControllerPrivate::onArtFetchEnded(input_item_t *p_item, bool)
|
|
{
|
|
Q_Q(PlayerController);
|
|
|
|
vlc_player_locker lock{ m_player };
|
|
bool b_current_item = (p_item == vlc_player_GetCurrentMedia( m_player ));
|
|
/* No input will signal the cover art to update,
|
|
* let's do it ourself */
|
|
if ( b_current_item )
|
|
UpdateArt( p_item );
|
|
else
|
|
emit q->artChanged( p_item );
|
|
}
|
|
|
|
const QString PlayerController::decodeArtURL( input_item_t *p_item )
|
|
{
|
|
assert( p_item );
|
|
|
|
char *psz_art = input_item_GetArtURL( p_item );
|
|
if( psz_art )
|
|
{
|
|
char *psz = vlc_uri2path( psz_art );
|
|
free( psz_art );
|
|
psz_art = psz;
|
|
}
|
|
|
|
#if 0
|
|
/* Taglib seems to define a attachment://, It won't work yet */
|
|
url = url.replace( "attachment://", "" );
|
|
#endif
|
|
|
|
QString path = qfu( psz_art ? psz_art : "" );
|
|
free( psz_art );
|
|
return path;
|
|
}
|
|
|
|
void PlayerController::setArt( input_item_t *p_item, QString fileUrl )
|
|
{
|
|
Q_D(PlayerController);
|
|
if( hasInput() )
|
|
{
|
|
char *psz_cachedir = config_GetUserDir( VLC_CACHE_DIR );
|
|
QString old_url = decodeArtURL( p_item );
|
|
old_url = QDir( old_url ).canonicalPath();
|
|
|
|
if( psz_cachedir != nullptr && old_url.startsWith( QString::fromUtf8( psz_cachedir ) ) )
|
|
QFile( old_url ).remove(); /* Purge cached artwork */
|
|
|
|
free( psz_cachedir );
|
|
|
|
input_item_SetArtURL( p_item , fileUrl.toUtf8().constData() );
|
|
d->UpdateArt( p_item );
|
|
}
|
|
}
|
|
|
|
bool PlayerController::associateSubtitleFile(const QString &uri)
|
|
{
|
|
return AddAssociatedMedia(SPU_ES, uri, true, true, true) == VLC_SUCCESS;
|
|
}
|
|
|
|
int PlayerController::AddAssociatedMedia(es_format_category_e cat, const QString &uri, bool select, bool notify, bool check_ext)
|
|
{
|
|
Q_D(PlayerController);
|
|
vlc_player_locker lock{ d->m_player };
|
|
return vlc_player_AddAssociatedMedia( d->m_player, cat, qtu(uri), select, notify, check_ext );
|
|
}
|
|
|
|
#define QABSTRACTLIST_GETTER( type, fun, var ) \
|
|
type* PlayerController::fun() \
|
|
{ \
|
|
Q_D(PlayerController); \
|
|
return &d->var; \
|
|
}
|
|
|
|
|
|
QABSTRACTLIST_GETTER( TrackListModel, getVideoTracks, m_videoTracks)
|
|
QABSTRACTLIST_GETTER( TrackListModel, getAudioTracks, m_audioTracks)
|
|
QABSTRACTLIST_GETTER( TrackListModel, getSubtitleTracks, m_subtitleTracks)
|
|
QABSTRACTLIST_GETTER( TitleListModel, getTitles, m_titleList)
|
|
QABSTRACTLIST_GETTER( ChapterListModel,getChapters, m_chapterList)
|
|
QABSTRACTLIST_GETTER( AudioDeviceModel, getAudioDevices, m_audioDeviceList)
|
|
QABSTRACTLIST_GETTER( ProgramListModel, getPrograms, m_programList)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getZoom, m_zoom)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getAspectRatio, m_aspectRatio)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getCrop, m_crop)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getFit, m_fit)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getDeinterlace, m_deinterlace)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getDeinterlaceMode, m_deinterlaceMode)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getAudioStereoMode, m_audioStereoMode)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getAudioMixMode, m_audioMixMode)
|
|
QABSTRACTLIST_GETTER( VLCVarChoiceModel, getAudioVisualizations, m_audioVisualization)
|
|
QABSTRACTLIST_GETTER( RendererManager, getRendererManager, m_rendererManager)
|
|
|
|
#undef QABSTRACTLIST_GETTER
|
|
|
|
#define PRIMITIVETYPE_GETTER( type, fun, var ) \
|
|
type PlayerController::fun() const \
|
|
{ \
|
|
Q_D(const PlayerController); \
|
|
return d->var; \
|
|
}
|
|
|
|
|
|
PRIMITIVETYPE_GETTER(PlayerController::PlayingState, getPlayingState, m_playing_status)
|
|
PRIMITIVETYPE_GETTER(QString, getName, m_name)
|
|
PRIMITIVETYPE_GETTER(VLCTick, getTime, m_time)
|
|
PRIMITIVETYPE_GETTER(VLCTick, getRemainingTime, m_remainingTime)
|
|
PRIMITIVETYPE_GETTER(double, getPosition, m_position)
|
|
PRIMITIVETYPE_GETTER(VLCTick, getLength, m_length)
|
|
PRIMITIVETYPE_GETTER(VLCTick, getAudioDelay, m_audioDelay)
|
|
PRIMITIVETYPE_GETTER(VLCTick, getSubtitleDelay, m_subtitleDelay)
|
|
PRIMITIVETYPE_GETTER(VLCTick, getSecondarySubtitleDelay, m_secondarySubtitleDelay)
|
|
PRIMITIVETYPE_GETTER(bool, isSeekable, m_capabilities & VLC_PLAYER_CAP_SEEK)
|
|
PRIMITIVETYPE_GETTER(bool, isRewindable, m_capabilities & VLC_PLAYER_CAP_REWIND)
|
|
PRIMITIVETYPE_GETTER(bool, isPausable, m_capabilities & VLC_PLAYER_CAP_PAUSE)
|
|
PRIMITIVETYPE_GETTER(bool, isRateChangable, m_capabilities & VLC_PLAYER_CAP_CHANGE_RATE)
|
|
PRIMITIVETYPE_GETTER(bool, canRestorePlayback, m_canRestorePlayback);
|
|
PRIMITIVETYPE_GETTER(float, getSubtitleFPS, m_subtitleFPS)
|
|
PRIMITIVETYPE_GETTER(bool, hasVideoOutput, m_hasVideo)
|
|
PRIMITIVETYPE_GETTER(float, getBuffering, m_buffering)
|
|
PRIMITIVETYPE_GETTER(float, getVolume, m_volume)
|
|
PRIMITIVETYPE_GETTER(bool, isMuted, m_muted)
|
|
PRIMITIVETYPE_GETTER(bool, isFullscreen, m_fullscreen)
|
|
PRIMITIVETYPE_GETTER(bool, getWallpaperMode, m_wallpaperMode)
|
|
PRIMITIVETYPE_GETTER(float, getRate, m_rate)
|
|
PRIMITIVETYPE_GETTER(bool, hasTitles, m_hasTitles)
|
|
PRIMITIVETYPE_GETTER(bool, hasChapters, m_hasChapters)
|
|
PRIMITIVETYPE_GETTER(bool, hasMenu, m_hasMenu)
|
|
PRIMITIVETYPE_GETTER(bool, isMenu, m_isMenu)
|
|
PRIMITIVETYPE_GETTER(bool, isInteractive, m_isInteractive)
|
|
PRIMITIVETYPE_GETTER(bool, hasPrograms, m_hasPrograms)
|
|
PRIMITIVETYPE_GETTER(bool, isEncrypted, m_encrypted)
|
|
PRIMITIVETYPE_GETTER(bool, isRecording, m_recording)
|
|
PRIMITIVETYPE_GETTER(PlayerController::ABLoopState, getABloopState, m_ABLoopState)
|
|
PRIMITIVETYPE_GETTER(VLCTick, getABLoopA, m_ABLoopA)
|
|
PRIMITIVETYPE_GETTER(VLCTick, getABLoopB, m_ABLoopB)
|
|
PRIMITIVETYPE_GETTER(bool, isTeletextEnabled, m_teletextEnabled)
|
|
PRIMITIVETYPE_GETTER(bool, isTeletextAvailable, m_teletextAvailable)
|
|
PRIMITIVETYPE_GETTER(int, getTeletextPage, m_teletextPage)
|
|
PRIMITIVETYPE_GETTER(bool, getTeletextTransparency, m_teletextTransparent)
|
|
PRIMITIVETYPE_GETTER(QString, getTitle, m_title)
|
|
PRIMITIVETYPE_GETTER(QString, getArtist, m_artist)
|
|
PRIMITIVETYPE_GETTER(QString, getAlbum, m_album)
|
|
PRIMITIVETYPE_GETTER(QUrl, getArtwork, m_artwork)
|
|
PRIMITIVETYPE_GETTER(QUrl, getUrl, m_url)
|
|
|
|
// High resolution time fed by SMPTE timer
|
|
PRIMITIVETYPE_GETTER(QString, highResolutionTime, m_highResolutionTime)
|
|
|
|
#undef PRIMITIVETYPE_GETTER
|
|
|