MpvWidget::MpvWidget(QWidget *parent, Qt::WindowFlags f) : QOpenGLWidget(parent, f) { mpv = mpv::qt::Handle::FromRawHandle(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"); // Make use of the MPV_SUB_API_OPENGL_CB API. mpv::qt::set_option_variant(mpv, "vo", "opengl-cb"); // Request hw decoding, just for testing. mpv::qt::set_option_variant(mpv, "hwdec", "auto"); mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); if (!mpv_gl) throw std::runtime_error("OpenGL not compiled in"); mpv_opengl_cb_set_update_callback(mpv_gl, MpvWidget::on_update, (void *)this); connect(this, SIGNAL(frameSwapped()), SLOT(swapped())); 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); }
VideoPlayer::~VideoPlayer() { makeCurrent(); if(mpv_gl) mpv_opengl_cb_set_update_callback(mpv_gl, nullptr, nullptr); mpv_opengl_cb_uninit_gl(mpv_gl); }
VideoPlayer::VideoPlayer(QWidget *parent, Qt::WindowFlags f) : QOpenGLWidget(parent, f), //PosUpdateTimer(this) PosUpdate(mpv) { mpv = mpv::qt::Handle::FromRawHandle(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"); mpv::qt::set_option_variant(mpv, "vo", "opengl-cb"); mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); if(!mpv_gl) throw std::runtime_error("OpenGL not compiled in"); mpv_opengl_cb_set_update_callback(mpv_gl, VideoPlayer::on_update, (void*)this); connect(this, SIGNAL(frameSwapped()), SLOT(swapped())); //mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE); mpv_set_wakeup_callback(mpv, wakeup, this); pause(); TimePosition = 0; connect(&PosUpdate, SIGNAL(timeChanged(int)), this, SLOT(timeChange(int))); //connect(&PosUpdateTimer, SIGNAL(timeout()), this, SLOT(updateTime())); //PosUpdateTimer.start(40); }
MpvWidget::~MpvWidget() { makeCurrent(); if (mpv_gl) mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL); // Until this call is done, we need to make sure the player remains // alive. This is done implicitly with the mpv::qt::Handle instance // in this class. mpv_opengl_cb_uninit_gl(mpv_gl); }
void gmpv_mpv_obj_set_opengl_cb_callback( GmpvMpvObj *mpv, mpv_opengl_cb_update_fn func, void *data ) { mpv->opengl_cb_callback = func; mpv->opengl_cb_callback_data = data; if(mpv->opengl_ctx) { mpv_opengl_cb_set_update_callback(mpv->opengl_ctx, func, data); } }
void PlayerQuickItem::initMpv(PlayerComponent* player) { m_mpv = player->getMpvHandle(); m_mpvGL = (mpv_opengl_cb_context *)mpv_get_sub_api(m_mpv, MPV_SUB_API_OPENGL_CB); if (!m_mpvGL) throw FatalException(tr("OpenGL not enabled in libmpv.")); mpv_opengl_cb_set_update_callback(m_mpvGL, on_update, (void *)this); connect(player, &PlayerComponent::windowVisible, this, &QQuickItem::setVisible); window()->update(); }
static void finalise(GObject* obj) { GtPlayerBackendMpvOpenGL* self = GT_PLAYER_BACKEND_MPV_OPENGL(obj); GtPlayerBackendMpvOpenGLPrivate* priv = gt_player_backend_mpv_opengl_get_instance_private(self); MESSAGE("Finalise"); if (priv->mpv_event_cb_id > 0) g_source_remove(priv->mpv_event_cb_id); mpv_set_wakeup_callback(priv->mpv, NULL, NULL); mpv_opengl_cb_set_update_callback(priv->mpv_opengl, NULL, NULL); mpv_opengl_cb_uninit_gl(priv->mpv_opengl); mpv_terminate_destroy(priv->mpv); g_clear_object(&priv->widget); g_free(priv->uri); G_OBJECT_CLASS(gt_player_backend_mpv_opengl_parent_class)->finalize(obj); }
static void gt_player_mpv_init(GtPlayerMpv* self) { GtPlayerMpvPrivate* priv = gt_player_mpv_get_instance_private(self); priv->mpv = mpv_create(); priv->opengl_area = gtk_gl_area_new(); priv->opengl_ready = FALSE; priv->chat_view = GTK_WIDGET(gt_chat_new()); check_mpv_error(mpv_set_option_string(priv->mpv, "vo", "opengl-cb")); check_mpv_error(mpv_initialize(priv->mpv)); priv->mpv_opengl = mpv_get_sub_api(priv->mpv, MPV_SUB_API_OPENGL_CB); mpv_opengl_cb_set_update_callback(priv->mpv_opengl, (mpv_opengl_cb_update_fn) opengl_cb, self); g_signal_connect(priv->opengl_area, "render", G_CALLBACK(render_cb), self); gtk_container_add(GTK_CONTAINER(self), priv->opengl_area); gtk_widget_show_all(GTK_WIDGET(self)); }
static void gt_player_backend_mpv_opengl_init(GtPlayerBackendMpvOpenGL* self) { GtPlayerBackendMpvOpenGLPrivate* priv = gt_player_backend_mpv_opengl_get_instance_private(self); MESSAGE("Init"); setlocale(LC_NUMERIC, "C"); priv->widget = gtk_gl_area_new(); priv->mpv = mpv_create(); g_object_set(priv->widget, "expand", TRUE, NULL); gtk_widget_add_events(priv->widget, GDK_BUTTON_PRESS_MASK); check_mpv_error(mpv_set_option_string(priv->mpv, "audio-client-name", "GNOME Twitch")); check_mpv_error(mpv_set_option_string(priv->mpv, "title", "")); check_mpv_error(mpv_set_option_string(priv->mpv, "vo", "opengl-cb")); check_mpv_error(mpv_set_option_string(priv->mpv, "softvol", "yes")); check_mpv_error(mpv_set_option_string(priv->mpv, "softvol-max", "100")); check_mpv_error(mpv_observe_property(priv->mpv, 0, "volume", MPV_FORMAT_DOUBLE)); check_mpv_error(mpv_observe_property(priv->mpv, 0, "cache-buffering-state", MPV_FORMAT_INT64)); check_mpv_error(mpv_initialize(priv->mpv)); mpv_set_wakeup_callback(priv->mpv, mpv_wakeup_cb, self); priv->mpv_opengl = mpv_get_sub_api(priv->mpv, MPV_SUB_API_OPENGL_CB); mpv_opengl_cb_set_update_callback(priv->mpv_opengl, (mpv_opengl_cb_update_fn) opengl_cb, self); g_signal_connect(priv->widget, "destroy", G_CALLBACK(widget_destroy_cb), self); g_signal_connect(priv->widget, "realize", G_CALLBACK(realise_oneshot_cb), self); g_signal_connect(priv->widget, "render", G_CALLBACK(render_cb), self); }
static void *render_thread_run(void *data) { struct render_thread *render_thread = data; EGLDisplay egl_display = NULL; EGLSurface egl_surface = NULL; EGLContext egl_context = NULL; // Gather egl params and bind to mpv opengl_cb { pthread_mutex_lock(&render_thread->lock); egl_display = render_thread->egl_display; egl_surface = render_thread->egl_surface; egl_context = render_thread->egl_context; eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); eglSwapInterval(egl_display, 0); int res = mpv_opengl_cb_init_gl(render_thread->opengl_cb_context, NULL, mpv_get_proc_address, NULL); if (res < 0) { log_mpv_error("mpv_opengl_cb_init_gl failed", res); } mpv_opengl_cb_set_update_callback(render_thread->opengl_cb_context, update_callback, render_thread); // TODO: after the first uninit the video track is reset to 0 (none) // Setting this to 1 restarts the playback though and messes up the time the video // was playing at. uint64_t on = 1; mpv_set_property(render_thread->mpv, "vid", MPV_FORMAT_INT64, &on); render_thread->running = 1; pthread_cond_broadcast(&render_thread->ready); pthread_mutex_unlock(&render_thread->lock); } int64_t last_time = mpv_get_time_us(render_thread->mpv); int frames = 0; while (1) { // Wait for available frames { pthread_mutex_lock(&render_thread->frame_wait_lock); while (!render_thread->frame_available) { pthread_cond_wait(&render_thread->frame_wait, &render_thread->frame_wait_lock); } render_thread->frame_available = 0; pthread_mutex_unlock(&render_thread->frame_wait_lock); } // Check if still running int run, width, height; { pthread_mutex_lock(&render_thread->lock); run = render_thread->running; width = render_thread->width; height = render_thread->height; pthread_mutex_unlock(&render_thread->lock); } if (!run) { break; } // int64_t draw_start = mpv_get_time_us(render_thread->mpv); mpv_opengl_cb_draw(render_thread->opengl_cb_context, 0, width, -height); // LOGI("Render time %ld", (mpv_get_time_us(render_thread->mpv) - draw_start)); if (!eglSwapBuffers(egl_display, egl_surface)) { LOGE("eglSwapBuffers failed %d", eglGetError()); } // mpv_opengl_cb_report_flip(render_thread->opengl_cb_context, 0); frames++; int64_t now = mpv_get_time_us(render_thread->mpv); if (now - last_time >= 1000 * 1000) { // last_time += 1000 * 1000; last_time = now; // Don't play catch-up LOGI("Render fps %d", frames); frames = 0; } } // Release egl params and unbind from mpv opengl_cb { pthread_mutex_lock(&render_thread->lock); int res = mpv_opengl_cb_uninit_gl(render_thread->opengl_cb_context); if (res < 0) { log_mpv_error("mpv_opengl_cb_uninit_gl failed", res); } eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); pthread_mutex_unlock(&render_thread->lock); } return NULL; }
PlayerQuickItem::~PlayerQuickItem() { if (m_mpvGL) mpv_opengl_cb_set_update_callback(m_mpvGL, NULL, NULL); }