Add libmpv backend

This commit adds the basic functionality including:
- local/remote video playback
- subtitles support
- Kaffeine playlist integration

What is missing:
- sound controls

Signed-off-by: Mavridis Philippe <mavridisf@gmail.com>
feat/libmpv-backend
Mavridis Philippe 1 year ago
parent 06028cccb1
commit 54545a0913
No known key found for this signature in database
GPG Key ID: 93F66F98F906147D

@ -63,6 +63,7 @@ option( WITH_XTEST "Enable support for XTest"
option( WITH_XINERAMA "Enable support for xinerama" ${WITH_ALL_OPTIONS} )
option( WITH_XCB "Enable support for xcb" ${WITH_ALL_OPTIONS} )
option( WITH_GSTREAMER "Enable support for gstreamer backend" ${WITH_ALL_OPTIONS} )
option( WITH_LIBMPV "Enable support for libmpv backend" ${WITH_ALL_OPTIONS} )
option( WITH_OGGVORBIS "Enable support for oggvorbis" ${WITH_ALL_OPTIONS} )
option( WITH_LAME "Enable support for lame" ${WITH_ALL_OPTIONS} )
option( WITH_DVB "Enable support for dvb" ${WITH_ALL_OPTIONS} )

@ -135,6 +135,16 @@ if( WITH_GSTREAMER )
message( STATUS "gstreamer plugins version: ${GSTREAMER_PLUGIN_VERSION}" )
endif( WITH_GSTREAMER )
##### check for libmpv
if( WITH_LIBMPV )
pkg_search_module( MPV mpv )
if( NOT MPV_FOUND )
tde_message_fatal( "libmpv support has been requested but mpv headers were not found on your system." )
endif()
message( STATUS "libmpv version: ${MPV_VERSION}" )
endif( WITH_LIBMPV )
##### check for lame

@ -4,3 +4,7 @@ add_subdirectory( xine-part )
if( WITH_GSTREAMER )
add_subdirectory( gstreamer-part )
endif()
if( WITH_LIBMPV )
add_subdirectory( libmpv-part )
endif()

@ -0,0 +1,50 @@
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${TDE_INCLUDE_DIR}
${TQT_INCLUDE_DIRS}
${GSTREAMER_INCLUDE_DIRS}
${GSTREAMER_PLUGIN_INCLUDE_DIRS}
${GSTREAMER_VIDEO_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/kaffeine/src/player-parts/kaffeine-part
${CMAKE_SOURCE_DIR}/kaffeine/src
)
link_directories(
${TQT_LIBRARY_DIRS}
${TDE_LIB_DIR}
#${MPV_LIBRARY_DIRS}
)
##### libmpvpart (kpart)
tde_add_kpart( libmpvpart AUTOMOC
SOURCES
libmpv_part.cpp libmpv_event.cpp
LINK
tdecore-shared
tdeui-shared
tdeio-shared
tdeparts-shared
kaffeinepart-shared
${MPV_LIBRARIES}
DESTINATION ${PLUGIN_INSTALL_DIR}
)
##### other data
tde_create_translated_desktop(
SOURCE libmpv_part.desktop
DESTINATION ${SERVICES_INSTALL_DIR}
PO_DIR kaffeine-desktops
)
install(
FILES libmpv_part.rc
DESTINATION ${DATA_INSTALL_DIR}/libmpvpart
)

@ -0,0 +1,91 @@
/*
* Kaffeine libmpv part
* Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com>
*
* Based on Kaffeine dummy part
* Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net>
*
* 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
*/
// TQt
#include <tqapplication.h>
// TDE
#include <tdelocale.h>
#include <kdebug.h>
// Part
#include "libmpv_event.h"
#include "libmpv_part.h"
MpvEventThread::MpvEventThread(MpvPart *part) {
m_part = part;
initPropertyObservers();
}
void MpvEventThread::initPropertyObservers() {
mpv_observe_property(m_part->m_mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
mpv_observe_property(m_part->m_mpv, 0, "duration", MPV_FORMAT_DOUBLE);
mpv_observe_property(m_part->m_mpv, 0, "media-title", MPV_FORMAT_STRING);
// "The advantage over using this instead of calculating it out of other
// properties is that it properly falls back to estimating the playback
// position from the byte position, if the file duration is not known."
mpv_observe_property(m_part->m_mpv, 0, "percent-pos", MPV_FORMAT_DOUBLE);
//mpv_observe_property(m_mpv, 0, "track-list", MPV_FORMAT_NODE);
//mpv_observe_property(m_mpv, 0, "chapter-list", MPV_FORMAT_NODE);
}
void MpvEventThread::run() {
while (m_part->m_mpv) {
mpv_event *event = mpv_wait_event(m_part->m_mpv, 0);
if (event->event_id != MPV_EVENT_NONE) {
processEvent(event);
}
}
}
void MpvEventThread::processEvent(mpv_event *event) {
switch (event->event_id) {
case MPV_EVENT_PROPERTY_CHANGE: {
mpv_event_property *prop = (mpv_event_property *)event->data;
MpvPropertyChangeEvent *pe = new MpvPropertyChangeEvent(
prop->name, prop->format, prop->data);
TQApplication::postEvent(m_part, pe);
break;
}
case MPV_EVENT_LOG_MESSAGE: {
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
kdDebug() << "[mpv " << msg->prefix << "] " << msg->level << ": "
<< msg->text << endl;
break;
}
case MPV_EVENT_END_FILE: {
struct mpv_event_end_file *end = (struct mpv_event_end_file *)event->data;
TQString error;
if (end->reason == MPV_END_FILE_REASON_ERROR) {
error = TQString(mpv_error_string(end->error));
}
MpvEOFEvent *eofe = new MpvEOFEvent(end->reason, error);
TQApplication::postEvent(m_part, eofe);
}
default: break; // ignore other events
}
}

@ -0,0 +1,81 @@
/*
* Kaffeine libmpv part
* Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com>
*
* Based on Kaffeine dummy part
* Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net>
*
* 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
*/
// TQt
#include <tqthread.h>
#include <tqevent.h>
#include <tqdatetime.h>
// libmpv
#include <mpv/client.h>
#define MPVPART_EVENT_PROPERTY_CHANGE 65890
#define MPVPART_EVENT_EOF 65891
class MpvPart;
class MpvPropertyChangeEvent : public TQCustomEvent {
public:
MpvPropertyChangeEvent(TQCString property, mpv_format format, void *data)
: TQCustomEvent(MPVPART_EVENT_PROPERTY_CHANGE),
_property(property), _format(format), _data(data)
{}
TQCString property() { return _property; }
mpv_format format() { return _format; }
void *data() { return _data; }
double toDouble() { return *(double *)_data; }
TQTime toTime() { return TQTime().addMSecs(toDouble() * 1000); }
private:
TQCString _property;
mpv_format _format;
void *_data;
};
class MpvEOFEvent : public TQCustomEvent {
public:
MpvEOFEvent(mpv_end_file_reason reason, TQString error = TQString::null)
: TQCustomEvent(MPVPART_EVENT_EOF), _reason(reason), _error(error)
{}
mpv_end_file_reason reason() { return _reason; }
TQString error() { return _error; }
private:
mpv_end_file_reason _reason;
TQString _error;
};
class MpvEventThread : public TQThread {
public:
MpvEventThread(MpvPart *part);
virtual void run();
private:
void initPropertyObservers();
void processEvent(mpv_event *event);
private:
MpvPart *m_part;
};

@ -0,0 +1,401 @@
/*
* Kaffeine libmpv part
* Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com>
*
* Based on Kaffeine dummy part
* Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net>
*
* 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
*/
// TQt
#include <tqtooltip.h>
#include <tqslider.h>
#include <tqpushbutton.h>
#include <tqfile.h>
// TDE
#include <tdeparts/genericfactory.h>
#include <tdeglobalsettings.h>
#include <tdeio/netaccess.h>
#include <tdeaction.h>
#include <kmimetype.h>
#include <tdemessagebox.h>
#include <kxmlguifactory.h>
#include <tdetoolbar.h>
#include <kdebug.h>
// Kaffeine
#include "playlistimport.h"
// Part
#include "libmpv_part.h"
#include "libmpv_event.h"
typedef KParts::GenericFactory<MpvPart> MpvPartFactory;
K_EXPORT_COMPONENT_FACTORY (libmpvpart, MpvPartFactory);
MpvPart::MpvPart(TQWidget* parentWidget, const char* widgetName, TQObject* parent, const char* name, const TQStringList& /*args*/)
: KaffeinePart(parent, name ? name : "MpvPart"),
m_current(0),
m_seeking(false)
{
// Create an instance of this class
setInstance(MpvPartFactory::instance());
// Create container widget
m_player = new TQWidget(parentWidget);
m_player->setBackgroundColor(TQt::black);
m_player->setFocusPolicy(TQ_ClickFocus);
setWidget(m_player);
// Initialize and embed mpv
if (!initMpv()) {
kdError() << "libmpvpart: initialization of mpv failed!" << endl;
emit canceled(i18n("MPV initialization failed!"));
return;
}
// Initialize GUI
setXMLFile("libmpv_part.rc");
initActions();
emit setStatusBarText(i18n("Ready"));
}
MpvPart::~MpvPart()
{
if (m_mpv) {
mpv_terminate_destroy(m_mpv);
m_mpv = nullptr;
}
if (m_eventThread) {
// the event thread doesn't do I/O, so we just kill it
m_eventThread->terminate();
}
}
TDEAboutData *MpvPart::createAboutData() {
TDEAboutData* aboutData = new TDEAboutData(
"libmpvpart", I18N_NOOP("MpvPart"),
"0.1", "A Kaffeine player part based on libmpv",
TDEAboutData::License_GPL,
"(c) 2023 Trinity Desktop Project", 0);
aboutData->addAuthor("Mavridis Philippe", I18N_NOOP("Developer"), "mavridisf@gmail.com");
aboutData->addAuthor("Jürgen Kofler", I18N_NOOP("Original Kaffeine developer"), "kaffeine@gmx.net");
return aboutData;
}
bool MpvPart::initMpv() {
// Create mpv instance
m_mpv = mpv_create();
if (!m_mpv) {
return false;
}
// Embed mpv into container widget
int64_t wid = static_cast<int64_t>(m_player->winId());
mpv_set_option(m_mpv, "wid", MPV_FORMAT_INT64, &wid);
// Get log messages with minimal level "info"
mpv_request_log_messages(m_mpv, "info");
// Start mpv event thread
m_eventThread = new MpvEventThread(this);
m_eventThread->start(TQThread::LowPriority);
return (mpv_initialize(m_mpv) >= 0);
}
void MpvPart::initActions()
{
new TDEAction(i18n("Play"), "media-playback-start", 0, this, SLOT(slotPlay()), actionCollection(), "player_play");
new TDEAction(i18n("Pause"), "media-playback-pause", Key_Space, this, SLOT(slotTogglePause()), actionCollection(), "player_pause");
new TDEAction(i18n("Stop"), "media-playback-stop", Key_Backspace, this, SLOT(slotStop()), actionCollection(), "player_stop");
new TDEAction(i18n("&Previous"), "media-skip-backward", Key_PageUp, this, SLOT(slotPrevious()), actionCollection(), "player_previous");
new TDEAction(i18n("&Next"), "media-skip-forward", Key_PageDown, this, SLOT(slotNext()), actionCollection(), "player_next");
// Important: we have a max of 1000 instead of 100 for better precision; multiply/divide your percentages by 10
m_position = new TQSlider(0, 1000, 10, 0, TQt::Horizontal, 0);
TQToolTip::add(m_position, i18n("Position"));
m_position->setTracking(false);
m_position->setFocusPolicy(TQ_NoFocus);
m_position->setMinimumWidth(100);
connect(m_position, SIGNAL(sliderPressed()), this, SLOT(slotStartSeeking()));
connect(m_position, SIGNAL(sliderMoved(int)), this, SLOT(slotSetSeekingPos(int)));
connect(m_position, SIGNAL(sliderReleased()), this, SLOT(slotStopSeeking()));
KWidgetAction *posAction = new KWidgetAction(m_position, i18n("Position"), 0, 0, 0, actionCollection(), "player_position");
posAction->setAutoSized(true);
m_playtime = new TQPushButton(0);
TQFontMetrics met(TDEGlobalSettings::generalFont());
m_playtime->setFixedWidth(met.width("-88:88:88") + 6);
m_playtime->setSizePolicy(TQSizePolicy (TQSizePolicy::Fixed, TQSizePolicy::Fixed));
m_playtime->setFocusPolicy(TQ_NoFocus);
new KWidgetAction(m_playtime, i18n("Playtime"), 0, 0, 0, actionCollection(), "player_playtime");
resetTime();
}
// Custom events dispatched from mpv event thread are handled here
void MpvPart::customEvent(TQCustomEvent *event) {
if (event->type() == MPVPART_EVENT_PROPERTY_CHANGE) {
MpvPropertyChangeEvent *pe = (MpvPropertyChangeEvent *)event;
if (pe->property() == "time-pos" && pe->format() == MPV_FORMAT_DOUBLE) {
m_time = pe->toTime();
m_playtime->setText(m_time.toString());
}
else if (pe->property() == "percent-pos" && pe->format() == MPV_FORMAT_DOUBLE) {
if (!m_seeking) {
m_percent = pe->toDouble();
m_position->setValue(m_percent * 10);
}
}
else if (pe->property() == "duration" && pe->format() == MPV_FORMAT_DOUBLE) {
MRL mrl = m_playlist[m_current];
TQTime length = TQTime().addMSecs(pe->toDouble() * 1000);
if (!length.isNull()) {
mrl.setLength(length);
emit signalNewMeta(m_mrl);
}
}
}
else if (event->type() == MPVPART_EVENT_EOF) {
resetTime();
MpvEOFEvent *eofe = (MpvEOFEvent *)event;
if (eofe->reason() == MPV_END_FILE_REASON_ERROR) {
KMessageBox::detailedError(nullptr, i18n("Cannot play file."), eofe->error());
}
}
}
bool MpvPart::openURL(const MRL& mrl) {
/* Note: we do not use the internal playlist feature of mpv.
* Instead we rely on the playlist feature of Kaffeine.
*/
if (!m_mpv) return false;
m_mrl = mrl;
m_playlist.clear();
m_current = 0;
/* Playlist handling code taken from gstreamer_part.cpp */
bool playlist = false;
TQString ext = m_mrl.kurl().fileName();
ext = ext.remove( 0 , ext.findRev('.')+1 ).lower();
if ( m_mrl.mime().isNull() ) {
KMimeType::Ptr mime = KMimeType::findByURL( m_mrl.kurl().path() );
m_mrl.setMime( mime->name() );
}
if ( (m_mrl.mime() == "text/plain") || (m_mrl.mime() == "text/xml") || (m_mrl.mime() == "application/x-kaffeine")
|| (m_mrl.mime() == "audio/x-scpls") || (m_mrl.mime() == "audio/x-mpegurl") || (m_mrl.mime() == "audio/mpegurl")
|| (ext == "asx") || (ext == "asf") || (ext == "wvx") || (ext == "wax") ) /* windows meta files */
{
kdDebug() << "libmpvpart: Check for kaffeine/noatun/m3u/pls/asx playlist\n";
TQString localFile;
if ( TDEIO::NetAccess::download(m_mrl.kurl(), localFile, widget()) ) {
TQFile file( localFile );
file.open( IO_ReadOnly );
TQTextStream stream( &file );
TQString firstLine = stream.readLine();
TQString secondLine = stream.readLine();
file.close();
if ( secondLine.contains("kaffeine", false) ) {
kdDebug() << "libmpvpart: Try loading kaffeine playlist\n";
playlist = PlaylistImport::kaffeine( localFile, m_playlist );
}
if ( secondLine.contains("noatun", false) ) {
kdDebug() << "libmpvpart: Try loading noatun playlist\n";
playlist = PlaylistImport::noatun( localFile, m_playlist);
}
if ( firstLine.contains("asx", false) ) {
kdDebug() << "libmpvpart: Try loading asx playlist\n";
playlist = PlaylistImport::asx( localFile, m_playlist );
}
if ( firstLine.contains("[playlist]", false) ) {
kdDebug() << "libmpvpart: Try loading pls playlist\n";
playlist = PlaylistImport::pls( localFile, m_playlist );
}
if (ext == "m3u" || ext == "m3u8") { //indentify by extension
kdDebug() << "libmpvpart: Try loading m3u playlist\n";
playlist = PlaylistImport::m3u( localFile, m_playlist );
}
}
else
kdError() << "libmpvpart: " << TDEIO::NetAccess::lastErrorString() << endl;
}
// check for ram playlist
if ( (ext == "ra") || (ext == "rm") || (ext == "ram") || (ext == "lsc") || (ext == "pl") ) {
kdDebug() << "libmpvpart: Try loading ram playlist\n";
playlist = PlaylistImport::ram( m_mrl, m_playlist, widget() );
}
if (!playlist)
{
kdDebug() << "libmpvpart: Got single track" << endl;
m_playlist.append( m_mrl );
}
slotPlay();
return true;
}
bool MpvPart::closeURL() {
if (!m_mpv) return false;
const char *args[] = {"playlist-remove", "current", nullptr};
mpv_command_async(m_mpv, 0, args);
return true;
}
void MpvPart::slotPlay() {
if (!m_mpv) return;
if (isPaused()) {
int value = 0;
mpv_set_property(m_mpv, "pause", MPV_FORMAT_FLAG, &value);
return;
}
if (m_playlist.count() > 0) {
emit setStatusBarText( i18n("Opening...") );
MRL curMRL = m_playlist[m_current];
const char *args[] = {"loadfile", curMRL.url().local8Bit(), nullptr};
mpv_command_async(m_mpv, 0, args);
// Add subtitles
if ((!curMRL.subtitleFiles().isEmpty()) && (curMRL.currentSubtitle() > -1)) {
kdDebug() << "adding subs" << endl;
const char *args[] = {"sub-add", curMRL.subtitleFiles()[curMRL.currentSubtitle()].local8Bit(), nullptr};
mpv_command_async(m_mpv, 0, args);
}
}
else {
emit signalRequestCurrentTrack();
}
}
void MpvPart::slotPause(bool pause) {
int value = pause ? 1 : 0;
mpv_set_property(m_mpv, "pause", MPV_FORMAT_FLAG, &value);
}
void MpvPart::slotTogglePause() {
if (!m_mpv) return;
slotPause(!isPaused());
}
bool MpvPart::isPlaying()
{
if (!m_mpv) return false;
int result;
if (mpv_get_property(m_mpv, "core-idle", MPV_FORMAT_FLAG, &result) < 0) {
return false;
}
return !(bool)result;
}
bool MpvPart::isPaused() {
if (!m_mpv) return false;
int result;
if (mpv_get_property(m_mpv, "pause", MPV_FORMAT_FLAG, &result) < 0) {
return false;
}
return (bool)result;
}
void MpvPart::resetTime() {
m_playtime->setText("0:00:00");
m_position->setValue(0);
}
void MpvPart::slotStartSeeking() {
m_seeking = true;
slotPause(true);
}
void MpvPart::slotStopSeeking() {
slotSetPosition(m_percent);
slotPause(false);
m_seeking = false;
}
void MpvPart::slotSetSeekingPos(int pos) {
m_percent = (uint)pos / 10;
}
uint MpvPart::volume() const {
return 0;
}
uint MpvPart::position() const {
return m_percent;
}
void MpvPart::slotSetVolume(uint) {
}
void MpvPart::slotSetPosition(uint position) {
if (!m_mpv) return;
int64_t value = (int64_t)position;
mpv_set_property(m_mpv, "percent-pos", MPV_FORMAT_INT64, &value);
}
void MpvPart::slotPrevious() {
if ( m_current > 0 ) {
m_current--;
slotPlay();
}
else {
emit signalRequestPreviousTrack();
}
}
void MpvPart::slotNext() {
if ((m_playlist.count() > 0) && (m_current < m_playlist.count()-1)) {
m_current++;
slotPlay();
}
else {
emit signalRequestNextTrack();
}
}
void MpvPart::slotStop() {
if (isPlaying()) {
const char *args[] = {"stop", nullptr};
mpv_command_async(m_mpv, 0, args);
}
}
void MpvPart::slotMute() {
}
#include "libmpv_part.moc"

@ -0,0 +1,10 @@
[Desktop Entry]
Encoding=UTF-8
Icon=mpv
Name=Kaffeine-MPV
Description=A Kaffeine engine based on libmpv
MimeType=application/x-ogg;audio/basic;audio/vnd.rn-realaudio;audio/x-aiff;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-ogg;audio/x-pn-realaudio;audio/x-pn-realaudio-plugin;audio/x-scpls;audio/x-wav;audio/x-flac;video/x-matroska;audio/x-matroska;video/mpeg;video/msvideo;video/quicktime;video/vnd.rn-realvideo;video/x-avi;video/x-fli;video/x-flic;video/x-ms-asf;video/x-ms-wmv;video/x-msvideo;application/x-mplayer2;application/smil;application/x-kaffeine;audio/x-musepack;
X-TDE-ServiceTypes=KParts/ReadOnlyPart,KaffeinePart
Type=Service
X-TDE-Library=libmpvpart
X-TDE-InitialPreference=10

@ -0,0 +1,116 @@
/*
* Kaffeine libmpv part
* Copyright (C) 2023 Mavridis Philippe <mavridisf@gmail.com>
*
* Based on Kaffeine dummy part
* Copyright (C) 2004-2005 Jürgen Kofler <kaffeine@gmx.net>
*
* 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
*/
#ifndef __LIBMPVPART_H__
#define __LIBMPVPART_H__
// TQt
#include <tqwidget.h>
// TDE
#include <tdeparts/factory.h>
// Kaffeine
#include "kaffeinepart.h"
// libmpv
#include <mpv/client.h>
class TQSlider;
class TQPushButton;
class MpvEventThread;
/**
* MpvPart -- A KaffeinePart which uses libmpv as backend
* @author Mavridis Philippe <mavridisf@gmail.com>
*
*/
class MpvPart : public KaffeinePart
{
TQ_OBJECT
friend class MpvEventThread;
public:
MpvPart(TQWidget*, const char*, TQObject*, const char*, const TQStringList&);
virtual ~MpvPart();
/*
* Reimplement from KaffeinePart
*/
uint volume() const; /* percent */
uint position() const; /* percent */
bool isPlaying();
bool isPaused();
bool closeURL();
static TDEAboutData* createAboutData();
public slots:
/*
* Reimplement from KaffeinePart
*/
bool openURL(const MRL& mrl);
void slotPlay();
void slotPause(bool pause);
void slotTogglePause();
void slotSetVolume(uint); /* percent */
void slotSetPosition(uint); /* percent */
void slotStop();
void slotMute();
void slotPrevious();
void slotNext();
signals:
void mpvEvents();
protected:
mpv_handle *m_mpv;
private slots:
void slotStartSeeking();
void slotStopSeeking();
void slotSetSeekingPos(int pos);
private:
bool initMpv();
void initActions();
void resetTime();
void customEvent(TQCustomEvent *event);
private:
TQWidget *m_player;
MpvEventThread *m_eventThread;
TQSlider *m_position;
TQPushButton *m_playtime;
MRL m_mrl;
TQValueList<MRL> m_playlist;
uint m_current;
TQTime m_time;
uint m_percent;
bool m_seeking;
};
#endif /* __LIBMPVPART_H__ */

@ -0,0 +1,26 @@
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui name="libmpv_part" version="1">
<MenuBar>
<Menu name="player"><text>&amp;Player</text>
<Action name="player_play"/>
<Action name="player_pause"/>
<Action name="player_stop"/>
<Separator/>
<Action name="player_previous"/>
<Action name="player_next"/>
</Menu>
</MenuBar>
<ToolBar name="controls" position="Bottom"><text>Controls Toolbar</text>
<Action name="player_previous"/>
<Action name="player_play"/>
<Action name="player_pause"/>
<Action name="player_stop"/>
<Action name="player_next"/>
</ToolBar>
<ToolBar name="position" fullWidth="true" position="Bottom"><text>Position Toolbar</text>
<Action name="player_position"/>
<Separator/>
<Action name="player_playtime"/>
</ToolBar>
<StatusBar/>
</kpartgui>
Loading…
Cancel
Save