Skip to content
GitLab
Explorer
Connexion
Navigation principale
Rechercher ou aller à…
Projet
Vivy
Gestion
Activité
Membres
Labels
Programmation
Tickets
Tableaux des tickets
Jalons
Wiki
Wiki externe
Code
Requêtes de fusion
Dépôt
Branches
Validations
Étiquettes
Graphe du dépôt
Comparer les révisions
Compilation
Pipelines
Jobs
Planifications de pipeline
Artéfacts
Aide
Aide
Support
Documentation de GitLab
Comparer les forfaits GitLab
Forum de la communauté GitLab
Contribuer à GitLab
Donner votre avis
Raccourcis clavier
?
Extraits de code
Groupes
Projets
Afficher davantage de fils d'Ariane
Elliu
Vivy
Requêtes de fusion
!15
Video playback with mpv
Code
Examiner les modifications
Extraire la branche
Télécharger
Correctifs
Diff brut
Étendre la barre latérale
Fusionnées
Video playback with mpv
mpv
vers
master
Vue d'ensemble
29
Validations
35
Pipelines
0
Modifications
2
Fusionnées
Video playback with mpv
Kubat
requested to merge
mpv
into
master
2 août 2021
Vue d'ensemble
29
Validations
35
Pipelines
0
Modifications
2
Add video playback with mpv
Add the mpv widget
Enable the widget only for video able documents
Control the widget with a small set of buttons
Seek mpv with slider that indicates the current position in the video
Use
std::chrono
in the MpvControls to be sure to have the correct time unit...
Not mpv related changes
Don't del and add docks if they are already present, just swap the contained widget => don't reset the position of the dock at sub-document updates
What it does not for now :
Get rid of controls on MPV view when the dock goes fullscreen
Modification effectuée
6 août 2021
par
Kubat
0
0
Rapports de requête de fusion
Affichage du commit
2b0e4884
Afficher la dernière version
2 files
+
113
−
2
En ligne
Comparer les modifications
Côte à côte
En ligne
Afficher les modifications des espaces
Afficher un fichier à la fois
Fichiers
2
Vérifiée
2b0e4884
[WIP] UI: Base class for MpvContainer (not tested)
· 2b0e4884
Kubat
rédigé
2 août 2021
src/UI/DocumentViews/MpvContainer.cc
0 → 100644
+
326
−
0
Afficher le fichier @ 4a13db1a
Modifier dans l'éditeur de fichier unique
Ouvrir dans Web EDI
#include
"MpvContainer.hh"
#include
<mpv/client.h>
using
namespace
Vivy
;
using
namespace
std
::
string_literals
;
void
MpvContainer
::
mpvEventWakeUpCB
(
void
*
user
)
noexcept
{
MpvContainer
*
container
=
reinterpret_cast
<
MpvContainer
*>
(
user
);
emit
container
->
mpvEvent
();
}
MpvContainer
::
MpvContainer
(
QWidget
*
parent
)
:
QWidget
(
parent
)
,
mpv
(
mpv_create
())
{
if
(
mpv
==
nullptr
)
throw
std
::
runtime_error
(
"Failed to create the MPV context"
);
setAttribute
(
Qt
::
WA_DontCreateNativeAncestors
);
setAttribute
(
Qt
::
WA_NativeWindow
);
quint64
wid
=
winId
();
mpv_set_option
(
mpv
,
"wid"
,
MPV_FORMAT_INT64
,
&
wid
);
mpv_set_option_string
(
mpv
,
"idle"
,
"yes"
);
mpv_set_option_string
(
mpv
,
"loop-file"
,
"inf"
);
mpv_set_option_string
(
mpv
,
"no-config"
,
"yes"
);
mpv_set_option_string
(
mpv
,
"sid"
,
"no"
);
mpv_set_option_string
(
mpv
,
"input-default-bindings"
,
"no"
);
mpv_set_option_string
(
mpv
,
"input-vo-keyboard"
,
"no"
);
mpv_request_log_messages
(
mpv
,
"info"
);
mpv_observe_property
(
mpv
,
0
,
"pause"
,
MPV_FORMAT_FLAG
);
mpv_observe_property
(
mpv
,
0
,
"duration"
,
MPV_FORMAT_DOUBLE
);
mpv_observe_property
(
mpv
,
0
,
"time-pos"
,
MPV_FORMAT_DOUBLE
);
connect
(
this
,
&
MpvContainer
::
mpvEvent
,
this
,
&
MpvContainer
::
onMpvEvent
,
Qt
::
QueuedConnection
);
mpv_set_wakeup_callback
(
mpv
,
&
MpvContainer
::
mpvEventWakeUpCB
,
this
);
if
(
int
rc
=
mpv_initialize
(
mpv
);
rc
<
0
)
{
printMpvError
(
rc
);
throw
std
::
runtime_error
(
"Failed to initialize the mpv context"
);
}
}
void
MpvContainer
::
registerMpvTimeCallback
(
std
::
function
<
void
(
double
)
>
callback
)
noexcept
{
if
(
mpvTimeCallback
)
qWarning
()
<<
"Override a previous mpv callback!"
;
mpvTimeCallback
=
callback
;
}
void
MpvContainer
::
registerMpvDurationCallback
(
std
::
function
<
void
(
double
)
>
callback
)
noexcept
{
if
(
mpvDurationCallback
)
qWarning
()
<<
"Override a previous mpv callback!"
;
mpvDurationCallback
=
callback
;
}
void
MpvContainer
::
closeMpv
()
noexcept
{
if
(
mpv
)
{
qDebug
()
<<
"Closing the MPV context"
;
asyncCommand
(
AsyncCmdType
::
None
,
{
"quit"
,
nullptr
});
registerMpvTimeCallback
(
nullptr
);
registerMpvDurationCallback
(
nullptr
);
mpv_handle
*
tmp_mpv
=
mpv
;
mpv
=
nullptr
;
// Stop all other callbacks here
mpv_terminate_destroy
(
tmp_mpv
);
}
}
MpvContainer
::~
MpvContainer
()
noexcept
{
closeMpv
();
}
void
MpvContainer
::
handleMpvEvent
(
mpv_event
*
event
)
noexcept
{
// Declare here variables that can be used in the switch-case statements
qint64
w
,
h
;
double
time
;
QString
msgText
;
union
{
mpv_event_log_message
*
msg
;
mpv_event_property
*
prop
;
};
auto
checkProp
=
[](
mpv_event_property
*
prop
,
const
std
::
string
&
str
,
int
format
)
noexcept
->
bool
{
return
(
prop
->
name
==
str
)
&&
(
prop
->
format
==
format
);
};
switch
(
event
->
event_id
)
{
case
MPV_EVENT_SHUTDOWN
:
closeMpv
();
break
;
case
MPV_EVENT_VIDEO_RECONFIG
:
// TODO: Those are sync calls, prefer async calls
if
(
mpv_get_property
(
mpv
,
"dwidth"
,
MPV_FORMAT_INT64
,
&
w
)
>=
0
&&
mpv_get_property
(
mpv
,
"dheight"
,
MPV_FORMAT_INT64
,
&
h
)
>=
0
&&
(
w
>
0
&&
h
>
0
))
{
qDebug
()
<<
"Reconfigure video to"
<<
w
<<
"x"
<<
h
;
}
break
;
case
MPV_EVENT_LOG_MESSAGE
:
msg
=
reinterpret_cast
<
mpv_event_log_message
*>
(
event
->
data
);
msgText
=
msg
->
text
;
msgText
.
replace
(
'\n'
,
""
);
qDebug
().
nospace
().
noquote
()
<<
"MPV - MSG ["
<<
msg
->
prefix
<<
"] "
<<
msg
->
level
<<
": "
<<
msgText
;
break
;
case
MPV_EVENT_PROPERTY_CHANGE
:
prop
=
reinterpret_cast
<
mpv_event_property
*>
(
event
->
data
);
if
(
checkProp
(
prop
,
"time-pos"
s
,
MPV_FORMAT_DOUBLE
)
&&
mpvTimeCallback
)
{
time
=
*
reinterpret_cast
<
double
*>
(
prop
->
data
);
mpvTimeCallback
(
time
);
}
else
if
(
checkProp
(
prop
,
"duration"
s
,
MPV_FORMAT_DOUBLE
)
&&
mpvDurationCallback
)
{
time
=
*
reinterpret_cast
<
double
*>
(
prop
->
data
);
mpvDurationCallback
(
time
);
}
else
if
(
checkProp
(
prop
,
"pause"
s
,
MPV_FORMAT_FLAG
))
{
isPlaybackPaused
=
*
reinterpret_cast
<
bool
*>
(
prop
->
data
);
emit
mpvPlaybackToggled
(
!
isPlaybackPaused
);
qDebug
()
<<
"MPV -> set to"
<<
(
isPlaybackPaused
?
"pause"
:
"play"
);
}
break
;
case
MPV_EVENT_PAUSE
:
isPlaybackPaused
=
true
;
emit
mpvPlaybackToggled
(
!
isPlaybackPaused
);
qDebug
()
<<
"MPV -> set to pause"
;
break
;
case
MPV_EVENT_UNPAUSE
:
isPlaybackPaused
=
false
;
emit
mpvPlaybackToggled
(
!
isPlaybackPaused
);
qDebug
()
<<
"MPV -> set to play"
;
break
;
case
MPV_EVENT_START_FILE
:
qDebug
()
<<
"MPV: Begin of file"
;
break
;
case
MPV_EVENT_END_FILE
:
qDebug
()
<<
"MPV: Reached end of file!"
;
break
;
case
MPV_EVENT_COMMAND_REPLY
:
qDebug
()
<<
"Got return of"
<<
event
->
reply_userdata
;
handleMpvEventCommandReply
(
static_cast
<
AsyncCmdType
>
(
event
->
reply_userdata
));
break
;
// Explicitly ignored
case
MPV_EVENT_NONE
:
case
MPV_EVENT_GET_PROPERTY_REPLY
:
case
MPV_EVENT_SET_PROPERTY_REPLY
:
case
MPV_EVENT_FILE_LOADED
:
case
MPV_EVENT_TRACKS_CHANGED
:
case
MPV_EVENT_TRACK_SWITCHED
:
case
MPV_EVENT_IDLE
:
case
MPV_EVENT_TICK
:
case
MPV_EVENT_SCRIPT_INPUT_DISPATCH
:
case
MPV_EVENT_CLIENT_MESSAGE
:
case
MPV_EVENT_AUDIO_RECONFIG
:
case
MPV_EVENT_METADATA_UPDATE
:
case
MPV_EVENT_SEEK
:
case
MPV_EVENT_PLAYBACK_RESTART
:
case
MPV_EVENT_CHAPTER_CHANGE
:
case
MPV_EVENT_QUEUE_OVERFLOW
:
case
MPV_EVENT_HOOK
:
break
;
}
}
int
MpvContainer
::
getAssSid
()
const
noexcept
{
bool
conversionOk
=
false
;
const
char
*
result
=
mpv_get_property_string
(
mpv
,
"sid"
);
const
int
ret
=
QString
(
result
).
toInt
(
&
conversionOk
);
return
(
result
==
nullptr
||
!
conversionOk
)
?
-
1
:
ret
;
}
void
MpvContainer
::
handleMpvEventCommandReply
(
const
AsyncCmdType
type
)
noexcept
{
switch
(
type
)
{
case
AsyncCmdType
::
None
:
break
;
case
AsyncCmdType
::
LoadAssFile
:
case
AsyncCmdType
::
ReloadAss
:
sid
=
getAssSid
();
qDebug
()
<<
"Load ASS file with id:"
<<
sid
;
break
;
case
AsyncCmdType
::
UnloadAss
:
sid
=
getAssSid
();
qDebug
().
nospace
()
<<
"Unload Ass, rc = "
<<
sid
;
if
(
sid
!=
-
1
)
unloadAssFile
();
break
;
case
AsyncCmdType
::
LoadFile
:
sid
=
getAssSid
();
qDebug
()
<<
"MPV - CMD: File loaded by mpv, sid ="
<<
sid
;
isPlaybackPaused
=
false
;
mpvPause
();
unloadAssFile
();
break
;
case
AsyncCmdType
::
SeekTime
:
qDebug
()
<<
"MPV - CMD: Seeked playback"
;
break
;
case
AsyncCmdType
::
TogglePlayback
:
qDebug
()
<<
"MPV - CMD: Playback was toggled"
;
break
;
}
}
void
MpvContainer
::
onMpvEvent
()
noexcept
{
while
(
mpv
)
{
mpv_event
*
event
=
mpv_wait_event
(
mpv
,
0
);
if
(
event
==
nullptr
||
event
->
event_id
==
MPV_EVENT_NONE
)
break
;
handleMpvEvent
(
event
);
}
if
(
mpv
==
nullptr
)
{
qDebug
()
<<
"MPV was closed while in the event loop!"
;
}
}
void
MpvContainer
::
loadFile
(
const
QString
&
filename
)
noexcept
{
if
(
filename
.
isEmpty
())
return
;
const
QByteArray
cFileName
=
filename
.
toUtf8
();
asyncCommand
(
AsyncCmdType
::
LoadFile
,
{
"loadfile"
,
cFileName
.
data
(),
nullptr
});
}
void
MpvContainer
::
loadAssFile
(
const
QString
&
ass
)
noexcept
{
if
(
ass
.
isEmpty
())
return
;
const
QByteArray
cFileName
=
ass
.
toUtf8
();
asyncCommand
(
AsyncCmdType
::
LoadAssFile
,
{
"sub-add"
,
cFileName
.
data
(),
nullptr
});
}
void
MpvContainer
::
reloadAssFile
()
noexcept
{
asyncCommand
(
AsyncCmdType
::
ReloadAss
,
{
"sub-reload"
,
nullptr
});
}
void
MpvContainer
::
printMpvError
(
int
rc
)
const
noexcept
{
if
(
rc
==
MPV_ERROR_SUCCESS
)
return
;
qCritical
()
<<
"MPV error:"
<<
mpv_error_string
(
rc
);
}
void
MpvContainer
::
mpvPlay
()
noexcept
{
if
(
isPlaybackPaused
)
mpvTogglePlayback
();
}
void
MpvContainer
::
mpvPause
()
noexcept
{
if
(
!
isPlaybackPaused
)
mpvTogglePlayback
();
}
void
MpvContainer
::
mpvTogglePlayback
()
noexcept
{
qDebug
()
<<
"MPV: Toggling the playback"
;
asyncCommand
(
AsyncCmdType
::
TogglePlayback
,
{
"cycle"
,
"pause"
,
"up"
,
nullptr
});
}
void
MpvContainer
::
asyncCommand
(
const
AsyncCmdType
cmd
,
std
::
initializer_list
<
const
char
*>
args
)
noexcept
{
// NOTE: const_cast here, we have faith in MPV to not change the value of
// the temporary pointers here. Should be OK anyway because the `args` init
// list is a temporary and should be discarded afer the method call.
printMpvError
(
mpv_command_async
(
mpv
,
cmd
,
const_cast
<
const
char
**>
(
std
::
data
(
args
))));
}
void
MpvContainer
::
unloadAssFile
()
noexcept
{
asyncCommand
(
AsyncCmdType
::
UnloadAss
,
{
"sub-remove"
,
nullptr
});
}
void
MpvContainer
::
seekInFile
(
const
chrono
::
seconds
time
)
noexcept
{
QByteArray
seconds
=
QString
::
number
(
time
.
count
()).
toUtf8
();
asyncCommand
(
AsyncCmdType
::
SeekTime
,
{
"seek"
,
seconds
.
data
(),
"absolute"
,
nullptr
});
}
Chargement en cours