mpv是啥?
mpv之前是mediaplayer,windwos之前好像都有预装这个,都来经过发展,现在是一个对跨平台开发很友好的播放器工具。
Qt是啥?
制作桌面应用程序的开发语言及工具,基于C++,跨平台。
基础开发步骤
1.动态壁纸需要一个播放视频的软件,那么可以借鉴mpv的官方demo
https://github.com/mpv-player/mpv-examples/tree/master/libmpv
可以使用里面的qt文件夹里面的demo或者qt_opengl里面的demo
(我是使用的opengl那个demo)
demo编译就会发现,可以播放视频了,那么就以这个demo作为我们的播放模块的代码
2.动态壁纸需要放到图标层以下(windows)
程序需要msvc编译,不能用mingw编译,怀疑是mingw用不了部分win32的部分api
需要在qt pro文件中增加api所需要的库文件
LIBS+=-luser32 //所需要的win32 api库
win32: LIBS += $$PWD/3rd/libmpv.dll.a //mpv的库文件INCLUDEPATH += $$PWD/3rd/mpv //mpv头文件地址增加
3.关键代码:设置窗口放到图标下面(windows)
#ifdef Q_OS_WINDOWS
#include <windows.h>WId viewId;
HHOOK hook;
HWND workerW;LRESULT CALLBACK HookShoot(_In_ int nCode, _In_ WPARAM wParam,LPARAM lParam){if(wParam == WM_MOUSEMOVE || wParam == WM_NCMOUSEMOVE){MOUSEHOOKSTRUCT * mshook = (MOUSEHOOKSTRUCT *)lParam;PostMessage((HWND)viewId,WM_MOUSEMOVE,0,MAKELPARAM(mshook->pt.x,mshook->pt.y));};return CallNextHookEx(NULL, nCode, wParam, lParam);
}HWND _WORKERW = nullptr;inline BOOL CALLBACK EnumWindowsProc(_In_ HWND tophandle, _In_ LPARAM topparamhandle)
{HWND defview = FindWindowEx(tophandle, 0, L"SHELLDLL_DefView", nullptr);if (defview != nullptr){_WORKERW = FindWindowEx(0, tophandle, L"WorkerW", 0);}return true;
}HWND GetWorkerDesktop(){int result;HWND windowHandle = FindWindow(L"Progman", nullptr);//使用 0x3e8 命令分割出两个 WorkerWSendMessageTimeout(windowHandle, 0x052c, 0 ,0, SMTO_NORMAL, 0x3e8,(PDWORD_PTR)&result);//遍历窗体获得窗口句柄EnumWindows(EnumWindowsProc,(LPARAM)nullptr);// 显示WorkerWShowWindow(_WORKERW,SW_HIDE);return windowHandle;
}void Wallpaper::registerDesktop()
{
//windwos设置workerW = GetWorkerDesktop();if(this){viewId = this->winId();// 返回workerW窗口句柄//设置窗口样式GetWindowLongA((HWND)viewId, GWL_STYLE);//设置壁纸窗体的父窗体SetParent((HWND)viewId,workerW);SetWindowPos((HWND)viewId,HWND_TOP,0,0,0,0,WS_EX_LEFT|WS_EX_LTRREADING|WS_EX_RIGHTSCROLLBAR|WS_EX_NOACTIVATE);// 设置全局鼠标事件钩子(会很卡)
// hook = SetWindowsHookEx(WH_MOUSE_LL,HookShoot,GetModuleHandle(NULL),0);QDesktopWidget *desktop = QApplication::desktop();this->move(QPoint(0,0));int height = desktop->height();int width = desktop->width();this->resize(QSize(width,height));this->showFullScreen();}
4关键代码 linux,设置桌面属性
#include <QtX11Extras/QX11Info>
#include <xcb/xcb.h>
#include <xcb/xcb_ewmh.h>
#include <QtPlatformHeaders/QXcbWindowFunctions>
void Wallpaper::registerDesktop()
{this->winId();QWindow *window = this->windowHandle();window->setOpacity(0.99);if (QApplication::platformName() == "wayland") {qDebug() << "wayland set desktop"; //wayland环境下没有做} else {//x11环境下直接设置这个即可QXcbWindowFunctions::setWmWindowType(window, QXcbWindowFunctions::Desktop);}//随意激活下窗口,可能不需要QTimer::singleShot(1, this, [=] {show();lower();});
}
5 mpv播放器使用关键代码 (可以换用其他播放器)
头文件:
#ifndef PLAYERWINDOW_H
#define PLAYERWINDOW_H#include <QtWidgets/QOpenGLWidget>
#include "mpv/client.h"
#include "mpv/render_gl.h"
#include "mpv/qthelper.hpp"
#include <QSize>class MpvWidget Q_DECL_FINAL: public QOpenGLWidget
{Q_OBJECT
public:MpvWidget(QWidget *parent = 0, Qt::WindowFlags f = nullptr);~MpvWidget();void command(const QVariant ¶ms);void setProperty(const QString &name, const QVariant &value);QVariant getProperty(const QString &name) const;QSize sizeHint() const Q_DECL_OVERRIDE { return QSize(480, 270);}Q_SIGNALS:void durationChanged(int value);void positionChanged(int value);
protected:void initializeGL() Q_DECL_OVERRIDE;void paintGL() Q_DECL_OVERRIDE;
private Q_SLOTS:void on_mpv_events();void maybeUpdate();
private:void handle_mpv_event(mpv_event *event);static void on_update(void *ctx);mpv_handle *mpv;mpv_render_context *mpv_gl;bool m_bScrrenShot{false};};#endif // PLAYERWINDOW_H
cpp:
#include "mpvwidget.h"
#include <stdexcept>
#include <QtGui/QOpenGLContext>
#include <QtCore/QMetaObject>
#include "application.h"#include <QDateTime>
#include <QStandardPaths>static void wakeup(void *ctx)
{QMetaObject::invokeMethod((MpvWidget *)ctx, "on_mpv_events", Qt::QueuedConnection);
}static void *get_proc_address(void *ctx, const char *name)
{Q_UNUSED(ctx);QOpenGLContext *glctx = QOpenGLContext::currentContext();if (!glctx)return nullptr;return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name)));
}MpvWidget::MpvWidget(QWidget *parent, Qt::WindowFlags f): QOpenGLWidget(parent, f)
{setAttribute(Qt::WA_TranslucentBackground);mpv = mpv_create();if (!mpv)throw std::runtime_error("could not create mpv context");// mpv_set_option_string(mpv, "terminal", "yes");mpv_set_option_string(mpv, "msg-level", "all=v");if (mpv_initialize(mpv) < 0)throw std::runtime_error("could not initialize mpv context");// Request hw decoding, just for testing.
// mpv::qt::set_option_variant(mpv, "hwdec", "auto");mpv::qt::set_option_variant(mpv, "hwdec", "gpu");mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);mpv_set_wakeup_callback(mpv, wakeup, this);QList<QVariant> list;list << "no-correct-pts";
// mpv::qt::command_variant(mpv, list);mpv::qt::set_property_variant(mpv, "correct-pts", "no");mpv::qt::set_property_variant(mpv, "fps", "0");}MpvWidget::~MpvWidget()
{makeCurrent();if (mpv_gl)mpv_render_context_free(mpv_gl);mpv_terminate_destroy(mpv);
}void MpvWidget::command(const QVariant ¶ms)
{mpv::qt::command_variant(mpv, params);
}void MpvWidget::setProperty(const QString &name, const QVariant &value)
{mpv::qt::set_property_variant(mpv, name, value);
}QVariant MpvWidget::getProperty(const QString &name) const
{return mpv::qt::get_property_variant(mpv, name);
}void MpvWidget::initializeGL()
{mpv_opengl_init_params gl_init_params{get_proc_address, nullptr, nullptr};mpv_render_param params[] {{MPV_RENDER_PARAM_API_TYPE, const_cast<char *>(MPV_RENDER_API_TYPE_OPENGL)},{MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params},{MPV_RENDER_PARAM_INVALID, nullptr}};if (mpv_render_context_create(&mpv_gl, mpv, params) < 0)throw std::runtime_error("failed to initialize mpv GL context");mpv_render_context_set_update_callback(mpv_gl, MpvWidget::on_update, reinterpret_cast<void *>(this));
}void MpvWidget::paintGL()
{int iwidth = width();double dwidth = iwidth * devicePixelRatioF();int iheight = height();double dheight = iheight * devicePixelRatioF();int deviceiwidth = dwidth;int deviceiheight = dheight;mpv_opengl_fbo mpfbo{static_cast<int>(defaultFramebufferObject()), deviceiwidth, deviceiheight, 0};int flip_y{1};mpv_render_param params[] = {{MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo},{MPV_RENDER_PARAM_FLIP_Y, &flip_y},{MPV_RENDER_PARAM_INVALID, nullptr}};// See render_gl.h on what OpenGL environment mpv expects, and// other API details.mpv_render_context_render(mpv_gl, params);if (m_bScrrenShot) {m_bScrrenShot = false;QPixmap pix = QPixmap::fromImage(grabFramebuffer());QDateTime::currentMSecsSinceEpoch();QString path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/" + QString::number(QDateTime::currentMSecsSinceEpoch()) + ".png";pix.save(path);}
}void MpvWidget::on_mpv_events()
{// Process all events, until the event queue is empty.while (mpv) {mpv_event *event = mpv_wait_event(mpv, 0);if (event->event_id == MPV_EVENT_NONE) {break;}handle_mpv_event(event);}
}void MpvWidget::handle_mpv_event(mpv_event *event)
{switch (event->event_id) {case MPV_EVENT_PROPERTY_CHANGE: {mpv_event_property *prop = (mpv_event_property *)event->data;if (strcmp(prop->name, "time-pos") == 0) {if (prop->format == MPV_FORMAT_DOUBLE) {double time = *(double *)prop->data;Q_EMIT positionChanged(time);}} else if (strcmp(prop->name, "duration") == 0) {if (prop->format == MPV_FORMAT_DOUBLE) {double time = *(double *)prop->data;Q_EMIT durationChanged(time);}}break;}default: ;// Ignore uninteresting or unknown events.}
}// Make Qt invoke mpv_render_context_render() to draw a new/updated video frame.
void MpvWidget::maybeUpdate()
{// If the Qt window is not visible, Qt's update() will just skip rendering.// This confuses mpv's render API, and may lead to small occasional// freezes due to video rendering timing out.// Handle this by manually redrawing.// Note: Qt doesn't seem to provide a way to query whether update() will// be skipped, and the following code still fails when e.g. switching// to a different workspace with a reparenting window manager.if (window()->isMinimized()) {makeCurrent();paintGL();context()->swapBuffers(context()->surface());doneCurrent();} else {update();}
}void MpvWidget::on_update(void *ctx)
{QMetaObject::invokeMethod((MpvWidget *)ctx, "maybeUpdate");
}
只要将激活窗口包含播放器,对应linux和windows设置就可以使用。
在windows下几乎完美,图标可点击,linux下只能显示视频(ubuntu18.04可以,其他似乎都不行)。
demo git地址(windows/linux)
windwos:
windwos源码
或者
https://download.csdn.net/download/qq_43081702/86741121
linux:
linux源码
windows /linux下demo可执行程序地址
linux可执行程序下载
windows可执行程序下载