首页 > 其他分享 >MPV进阶使用:LIBMPV

MPV进阶使用:LIBMPV

时间:2023-11-28 09:44:05浏览次数:38  
标签:mpv 进阶 event MPV LIBMPV include MainWindow log

一、介绍

mpv不仅提供了IPC的使用方式,还提供了函数库,方便将mpv嵌入其他程序。

EMBEDDING INTO OTHER PROGRAMS (LIBMPV)

mpv can be embedded into other programs as video/audio playback backend. The recommended way to do so is using libmpv. See libmpv/client.h in the mpv source code repository. This provides a C API. Bindings for other languages might be available (see wiki).

释义:mpv能被嵌入到其他程序,作为视频/音频播放后台。推荐方法是使用 libmpv。在mpv源码仓库查看libmpv/client.h。在此提供了C API。其他语言的绑定可能可用(查看 wiki)

二、使用

对于libmpv提供了qt使用案例

以下内容为验证qt案列:

qtexample.h

#ifndef QTEXAMPLE_H
#define QTEXAMPLE_H

/*
 * 注1:程序正常运行需要将动态链接库mpv-1.dll拷贝至输出目录的dubug和release
 * 注2:程序界面UI全部由代码实现
*/

#include <QMainWindow>
#include <client.h>

class QTextEdit;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    /* 自定义槽函数 */
    void on_file_open();  // 打开文件
    void on_new_window(); // 新建窗口
    void on_mpv_events(); // 这个槽函数由 wakeup()调用(通过mpv_events信号)

signals:
    void mpv_events();

private:
    QWidget *mpv_container;
    mpv_handle *mpv;
    QTextEdit *log;

    void create_mvpPlayer(); // 创建mvpPlayer
    void handle_mpv_event(mpv_event *event); // 处理mpv事件
    void append_log(const QString &text); // 附加log到plainTextEdit
};

#endif // QTEXAMPLE_H

#include <clocale>
#include <sstream>
#include <stdexcept>

#include <QtGlobal>
#include <QFileDialog>
#include <QStatusBar>
#include <QMenuBar>
#include <QMenu>
#include <QGridLayout>
#include <QApplication>
#include <QTextEdit>
#include <QJsonDocument>

#include <qthelper.hpp>
#include "qtexample.h"

/* 唤醒函数 */
static void wakeup(void *ctx)
{
    // 此回调可从任何mpv线程调用(但也可以从调用mpv API的线程递归地返回)
    // 只是需要通知要唤醒的Qt GUI线程(以便它可以使用mpv_wait_event()),并尽快返回
    MainWindow *mainwindow = (MainWindow *)ctx;
    emit mainwindow->mpv_events();
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    // 设置主窗口标题名称和最小尺寸
    setWindowTitle("Qt embedding demo");
    setMinimumSize(640, 480);

    // 添加菜单栏
    QMenu *menu = menuBar()->addMenu(tr("&File"));

    // 在菜单栏添加open file Action
    QAction *on_open = new QAction(tr("&Open"), this);
    on_open->setShortcuts(QKeySequence::Open);
    on_open->setStatusTip(tr("Open a file"));
    connect(on_open, &QAction::triggered, this, &MainWindow::on_file_open);
    menu->addAction(on_open);
    // 在菜单栏添加new window Action
    QAction *on_new = new QAction(tr("&New window"), this);
    connect(on_new, &QAction::triggered, this, &MainWindow::on_new_window);
    menu->addAction(on_new);

    // 返回主窗口的状态栏(不存在则创建一个空的状态栏)
    statusBar();

    // 在主窗口下创建mpv log窗口
    QMainWindow *log_window = new QMainWindow(this);
    log = new QTextEdit(log_window);
    log->setReadOnly(true);
    log_window->setCentralWidget(log);
    log_window->setWindowTitle("mpv log window");
    log_window->setMinimumSize(500, 50);
    log_window->show();

    // 创建mvpPlayer
    create_mvpPlayer();
}

// 处理mpv事件
void MainWindow::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
                {
                    double time = *(double *)prop->data;
                    std::stringstream ss;
                    ss << "At: " << time;
                    statusBar()->showMessage(QString::fromStdString(ss.str()));
                }
                else if (prop->format == MPV_FORMAT_NONE) // 属性数据的值格式未知 (可能意味着播放已停止)
                {
                    statusBar()->showMessage("");
                }
            }
            else if (strcmp(prop->name, "chapter-list") == 0 ||
                       strcmp(prop->name, "track-list") == 0)
            {
                // 出于演示目的,将属性作为JSON转储
                if (prop->format == MPV_FORMAT_NODE)
                {
                    QVariant v = mpv::qt::node_to_variant((mpv_node *)prop->data);
                    // 使用JSON支持,可轻松打印mpv_node内容。
                    QJsonDocument d = QJsonDocument::fromVariant(v);
                    append_log("Change property " + QString(prop->name) + ":\n");
                    append_log(d.toJson().data());
                }
            }
            break;
        }
        case MPV_EVENT_VIDEO_RECONFIG:
        {
            // 检索新的视频大小
            int64_t w, h;
            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)
            {
                // 请注意,MPV_EVENT_VIDEO_RECONFIG事件不一定表示要调整大小,如果尺寸确实发生了变化,
                // 您应该检查一下视频。如果视频不合适,mpv本身会将视频缩放,放大到container大小
                std::stringstream ss;
                ss << "Reconfig: " << w << " " << h;
                statusBar()->showMessage(QString::fromStdString(ss.str()));
            }
            break;
        }
        case MPV_EVENT_LOG_MESSAGE:
        {
            struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
            std::stringstream ss;
            ss << "[" << msg->prefix << "] " << msg->level << ": " << msg->text;
            append_log(QString::fromStdString(ss.str()));
            break;
        }
        case MPV_EVENT_SHUTDOWN:
        {
            mpv_terminate_destroy(mpv);
            mpv = NULL;
            break;
        }
        default:
            // 忽视不感兴趣的或未知的事件
            break;
    }
}

// 这个槽函数由 wakeup()调用(通过mpv_events信号)
void MainWindow::on_mpv_events()
{
    // 处理所有事件,直到事件队列为空
    while (mpv)
    {
        mpv_event *event = mpv_wait_event(mpv, 0);
        if (event->event_id == MPV_EVENT_NONE)
            break;
        handle_mpv_event(event);
    }
}

// 打开文件
void MainWindow::on_file_open()
{
    QString filename = QFileDialog::getOpenFileName(this, "Open file");
    if (mpv)
    {
        const QByteArray c_filename = filename.toUtf8();
        const char *args[] = {"loadfile", c_filename.data(), NULL};
        // 与mpv_command相同,但异步运行命令(调用mpv渲染API线程)
        mpv_command_async(mpv, 0, args);
    }
}

// 新建窗口
void MainWindow::on_new_window()
{
    // 新建个MainWindow实例
    (new MainWindow())->show();
}

// 附加log
void MainWindow::append_log(const QString &text)
{
    QTextCursor cursor = log->textCursor();
    cursor.movePosition(QTextCursor::End);
    cursor.insertText(text);
    log->setTextCursor(cursor);
}

// --stream-record   https://mpv.io/manual/master/
// 创建mvpPlayer
void MainWindow::create_mvpPlayer()
{
    // 创建mpv实例
    mpv = mpv_create();
    if (!mpv)
        throw std::runtime_error("can't create mpv instance");

    // 创建一个视频子窗口(mpv_container)来播放视频
    mpv_container = new QWidget(this);
    setCentralWidget(mpv_container);
    mpv_container->setAttribute(Qt::WA_DontCreateNativeAncestors);
    mpv_container->setAttribute(Qt::WA_NativeWindow);
    // 然后将窗口ID传递给mpv wid选项
    int64_t wid = mpv_container->winId();
    mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);

    // 启用默认绑定,因为我们很懒。 通常,播放者使用mpv作为后端将实现其自己的键绑定
    mpv_set_option_string(mpv, "input-default-bindings", "yes");

    // 启用键盘输入在X11 Window上
    mpv_set_option_string(mpv, "input-vo-keyboard", "yes");

    // 让我们通过 MPV_EVENT_PROPERTY_CHANGE 接收属性更改事件,如果这属性更改了
    mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
    mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE);
    mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE);

    // 请求级别为"info"或更高级别的日志消息。它们以 MPV_EVENT_LOG_MESSAGE 的形式接收
    mpv_request_log_messages(mpv, "info");

    // 从这里开始,将调用唤醒功能。 回调可以来自任何线程,因此我们使用 QueuedConnection 机制以线程安全的方式中继唤醒
    connect(this, &MainWindow::mpv_events, this, &MainWindow::on_mpv_events,
            Qt::QueuedConnection);
    mpv_set_wakeup_callback(mpv, wakeup, this);

    // 判断mpv实例是否成功初始化
    if (mpv_initialize(mpv) < 0)
        throw std::runtime_error("mpv failed to initialize");
}

MainWindow::~MainWindow()
{
    if (mpv)
        mpv_terminate_destroy(mpv);
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // Qt在QApplication构造函数中设置了locale,但是libmpv需要将LC_NUMERIC类别设置为“ C”,因此将其改回。
    std::setlocale(LC_NUMERIC, "C");

    MainWindow w;
    w.show();

    return a.exec();
}

输出结果:

三、注意事项

  • libmpv提供了库文件:libmpv.dll.a 和 mpv-1.dll,链接时加入lib.dll.a文件,运行时mpv-1.dll放入exe同文件夹

标签:mpv,进阶,event,MPV,LIBMPV,include,MainWindow,log
From: https://www.cnblogs.com/caojun97/p/17858731.html

相关文章

  • 【Python进阶】第6篇:Python的死锁和IP地址详解。总结md文档集合(已分享,附代码)
    本文从14大模块展示了python高级用的应用。分别有Linux命令,多任务编程、网络编程、Http协议和静态Web编程、html+css、JavaScript、jQuery、MySql数据库的各种用法、python的闭包和装饰器、mini-web框架、正则表达式等相关文章的详细讲述。全套笔记和代码自取地址:请移步这里感......
  • RSA 进阶 中
    数论二项式定理费马小定理基础练习[金盾信安杯2023]babyrsa题目:#!/usr/bin/envpythonfromlibnumimport*fromCrypto.Util.numberimportgetPrime,bytes_to_long,long_to_bytesfromsecretimportp1,q1,p2,q2,e,d,flag,Nfromrandomimportrandintdef......
  • Java开发者的Python快速进修指南:面向对象进阶
    在上一期中,我们对Python中的对象声明进行了初步介绍。这一期,我们将深入探讨对象继承、组合以及多态这三个核心概念。不过,这里不打算赘述太多理论,因为我们都知道,Python与Java在这些方面的主要区别主要体现在语法上。例如,Python支持多重继承,这意味着一个类可以同时继承多个父类的属性......
  • 2-3 函数进阶(参数、返回值、作用域)
    ​ 概要:参数的补充函数名,函数名到底是什么?返回值和print函数的作用域 1.参数的补充补充的内容包含:内存地址相关、面试题相关等,在特定情况下也可以让代码更加简洁,提升开发效率。 1.1参数内存地址相关在开始开始讲参数内存地址相关之前,我们先来学习一个技......
  • 【2024省选冲刺计划】数据结构相关-线段树进阶
    线段树进阶0x01李超线段树FZPJ4519[2021冬令营模拟]上古遗迹【题目背景】“沙……沙……沙……”独行者的脚步一次次被刻进沙漠中,干冷的风携沙尘在男子的四围穿过。“该死……这沙尘什么时候才能消停会儿……”男子止不住地咳嗽,随即停了下来,开始查看便携式投影设备上的信......
  • 【Flask笔记】4大章60页md笔记第5篇:Flask模板的进阶使用和案例(图文和代码)
    本文的主要内容:flask视图&路由、虚拟环境安装、路由各种定义、状态保持、cookie、session、模板基本使用、过滤器&自定义过滤器、模板代码复用:宏、继承/包含、模板中特有变量和函数、Flask-WTF表单、CSRF、数据库操作、ORM、Flask-SQLAlchemy、增删改查操作、案例、蓝图、单元测......
  • Java开发者的Python快速进修指南:面向对象进阶
    在上一期中,我们对Python中的对象声明进行了初步介绍。这一期,我们将深入探讨对象继承、组合以及多态这三个核心概念。不过,这里不打算赘述太多理论,因为我们都知道,Python与Java在这些方面的主要区别主要体现在语法上。例如,Python支持多重继承,这意味着一个类可以同时继承多个父类的属......
  • 【Python进阶笔记】md文档笔记第6篇:Python进程和多线程使用(图文和代码)
    本文从14大模块展示了python高级用的应用。分别有Linux命令,多任务编程、网络编程、Http协议和静态Web编程、html+css、JavaScript、jQuery、MySql数据库的各种用法、python的闭包和装饰器、mini-web框架、正则表达式等相关文章的详细讲述。全套md格式笔记和代码自取:请移步这里......
  • JAVA进阶 —— 方法引用
    原文链接:https://blog.csdn.net/hdakj22/article/details/129453599一、内容概述方法引用可以拆分为方法和引用两方面:方法引用:把已经有的方法拿过来用,当作函数接口中抽象方法的方法体。::(方法引用符)但是要注意:并不是所有的方法都可以直接引用,需要满足以下四种条件引用......
  • TechTalk | 突破 “成本洞察” ,探索趣丸科技 FinOps 的进阶之路
    《趣丸科技FinOps进阶的探索与实践》作为快速发展的互联网公司,趣丸科技在业务增长的同时,积极开展了成本优化和FinOps的落地工作,并在实践中取得不错的成效。FinOps的发展,涉及了账单波动、FinOps的边际效应、成本模型、依赖工具等多个关键问题。我们始终保持在技术探索的前沿,从......