Example #1
0
DanmakuDelayGetter::DanmakuDelayGetter(QStringList &names, QStringList &urls,
                                       const QString &danmakuUrl, bool download, QObject *parent) :
    QObject(parent), names(names), urls(urls), danmakuUrl(danmakuUrl), download(download)
{
    mpv = mpv_create();
    if (!mpv)
    {
        qDebug("Fails to create mpv instance.");
        deleteLater();
        return;
    }
    mpv_set_option(mpv, "no-video", MPV_FORMAT_NONE, NULL);
    mpv_set_option(mpv, "pause", MPV_FORMAT_NONE, NULL);
    mpv_set_option_string(mpv, "ao", "null");
    mpv_set_option_string(mpv, "user-agent", generateUA(urls.first()));
    QString host = QUrl(urls.first()).host();
    if (referer_table.contains(host))
        mpv_set_option_string(mpv, "referrer", referer_table[host].constData());
    mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);
    mpv_set_wakeup_callback(mpv, postEvent, this);
    if (mpv_initialize(mpv) < 0)
    {
        qDebug("Fails to initialaze mpv instance.");
        deleteLater();
        return;
    }
    delay = 0;
    start();
}
Example #2
0
MpvAudioOutput::MpvAudioOutput() : state_(AudioState::Stopped), seek_offset_(-1), volumeNeverSet_(true) {
    setlocale(LC_NUMERIC, "C");
    handle_ = mpv::qt::Handle::FromRawHandle(mpv_create());
    if (static_cast<mpv_handle *>(handle_) == nullptr)
        qDebug() << "Cannot mpv_create()";
    set_option("video", "no");
    set_option("softvol", "yes");
    set_option("ytdl", "yes");
    set_property("audio-client-name", "fubar");
    int r = mpv_initialize(handle_);
    if (r < 0) {
        qDebug() << "Failed to initialize mpv backend: " << mpv_error_string(r);
        //         raise Exception("");
    }
    thread_.reset(new std::thread([this] { event_loop(); }));
    observe_property("playback-time");
    observe_property("idle", MPV_FORMAT_FLAG);
    observe_property("pause", MPV_FORMAT_FLAG);
    observe_property("duration", MPV_FORMAT_DOUBLE);
    observe_property("metadata", MPV_FORMAT_NODE);

    r = mpv_request_log_messages(handle_, "warn");
    if (r < 0)
        qDebug() << "mpv_request_log_messages failed: " << mpv_error_string(r);
}
Example #3
0
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);
}
Example #4
0
MpvHandler::MpvHandler(int64_t wid, QObject *parent):
    QObject(parent),
    baka(static_cast<BakaEngine*>(parent))
{
    // create mpv
    mpv = mpv_create();
    if(!mpv)
        throw "Could not create mpv object";

    // set mpv options
    mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);
    mpv_set_option_string(mpv, "input-cursor", "no");   // no mouse handling
    mpv_set_option_string(mpv, "cursor-autohide", "no");// no cursor-autohide, we handle that
    mpv_set_option_string(mpv, "ytdl", "yes"); // youtube-dl support

    // get updates when these properties change
    mpv_observe_property(mpv, 0, "playback-time", MPV_FORMAT_DOUBLE);
    mpv_observe_property(mpv, 0, "volume", MPV_FORMAT_DOUBLE);
    mpv_observe_property(mpv, 0, "sid", MPV_FORMAT_INT64);
    mpv_observe_property(mpv, 0, "aid", MPV_FORMAT_INT64);
    mpv_observe_property(mpv, 0, "sub-visibility", MPV_FORMAT_FLAG);
    mpv_observe_property(mpv, 0, "mute", MPV_FORMAT_FLAG);
    mpv_observe_property(mpv, 0, "core-idle", MPV_FORMAT_FLAG);
    mpv_observe_property(mpv, 0, "paused-for-cache", MPV_FORMAT_FLAG);

    // setup callback event handling
    mpv_set_wakeup_callback(mpv, wakeup, this);
}
Example #5
0
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);
}
int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("pass a single media file as argument\n");
        return 1;
    }

    mpv_handle *ctx = mpv_create();
    if (!ctx) {
        printf("failed creating context\n");
        return 1;
    }

    // Enable default key bindings, so the user can actually interact with
    // the player (and e.g. close the window).
    check_error(mpv_set_option_string(ctx, "input-default-bindings", "yes"));

    mpv_set_option_string(ctx, "input-vo-keyboard", "yes");
    int val = 1;
    check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val));

    // Done setting up options.
    check_error(mpv_initialize(ctx));

    check_error(mpv_request_log_messages(ctx, "v"));

    check_error(mpv_stream_cb_add_ro(ctx, "myprotocol", argv[1], open_fn));

    // Play this file.
    const char *cmd[] = {"loadfile", "myprotocol://fake", NULL};
    check_error(mpv_command(ctx, cmd));

    // Let it play, and wait until the user quits.
    while (1) {
        mpv_event *event = mpv_wait_event(ctx, 10000);
        if (event->event_id == MPV_EVENT_LOG_MESSAGE) {
            struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
            printf("[%s] %s: %s", msg->prefix, msg->level, msg->text);
            continue;
        }
        printf("event: %s\n", mpv_event_name(event->event_id));
        if (event->event_id == MPV_EVENT_SHUTDOWN)
            break;
    }

    mpv_terminate_destroy(ctx);
    return 0;
}
Example #7
0
MpvObject::MpvObject(QQuickItem * parent)
    : QQuickFramebufferObject(parent), mpv{mpv_create()}, mpv_gl(nullptr)
{
    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");

    connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
            Qt::QueuedConnection);
}
Example #8
0
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    QMenu *menu = menuBar()->addMenu(tr("&File"));
    QAction *on_open = new QAction(tr("&Open"), this);
    on_open->setShortcuts(QKeySequence::Open);
    on_open->setStatusTip(tr("Open a file"));
    connect(on_open, SIGNAL(triggered()), this, SLOT(on_file_open()));
    menu->addAction(on_open);

    statusBar();

    mpv = mpv_create();
    if (!mpv)
        throw "can't create mpv instance";

    // Create a video child window. Force Qt to create a native window, and
    // pass the window ID to the mpv wid option. This doesn't work on OSX,
    // because Cocoa doesn't support this form of embedding.
    mpv_container = new QWidget(this);
    setCentralWidget(mpv_container);
    mpv_container->setAttribute(Qt::WA_NativeWindow);
    // If you have a HWND, use: int64_t wid = (intptr_t)hwnd;
    int64_t wid = mpv_container->winId();
    mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);

    // Enable default bindings, because we're lazy. Normally, a player using
    // mpv as backend would implement its own key bindings.
    mpv_set_option_string(mpv, "input-default-bindings", "yes");

    // Let us receive property change events with MPV_EVENT_PROPERTY_CHANGE if
    // this property changes.
    mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);

    // From this point on, the wakeup function will be called. The callback
    // can come from any thread, so we use the Qt QEvent mechanism to relay
    // the wakeup in a thread-safe way.
    mpv_set_wakeup_callback(mpv, wakeup, this);

    if (mpv_initialize(mpv) < 0)
        throw "mpv failed to initialize";
}
Example #9
0
KNMusicBackendMpvThread::KNMusicBackendMpvThread(QObject *parent) :
    KNMusicStandardBackendThread(parent),
    m_volumeCurve(QEasingCurve(QEasingCurve::OutQuad)),
    m_filePath(QString()),
    m_totalDuration(-1),
    m_duration(-1),
    m_startPosition(-1),
    m_endPosition(-1),
    m_mpvHandle(nullptr),
    m_state(MusicUtil::Stopped),
    m_volumeSize(100)
{
    //Initial the locale settings.
    setlocale(LC_NUMERIC, "C");
    //Create the mpv handle.
    m_mpvHandle=mpv_create();
    //Check out the mpv handle.
    if(!m_mpvHandle)
    {
        //Cannot create an instance for mpv.
        return;
    }
    //Set mpv options.
    // No mouse handling.
    mpv_set_option_string(m_mpvHandle, "input-cursor", "no");
    // No cursor-autohide.
    mpv_set_option_string(m_mpvHandle, "cursor-autohide", "no");
    mpv_set_option_string(m_mpvHandle, "no-video", NULL);

    // get updates when these properties change
    mpv_observe_property(m_mpvHandle, 0, "time-pos", MPV_FORMAT_DOUBLE);

    // setup callback event handling
    mpv_set_wakeup_callback(m_mpvHandle,
                            KNMusicBackendMpvThread::instanceWakeUp,
                            this);
    //Initialize the mpv handler.
    mpv_initialize(m_mpvHandle);
}
Example #10
0
File: simple.c Project: xnoreq/mpv
int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("pass a single media file as argument\n");
        return 1;
    }

    mpv_handle *ctx = mpv_create();
    if (!ctx) {
        printf("failed creating context\n");
        return 1;
    }

    // Enable default key bindings, so the user can actually interact with
    // the player (and e.g. close the window).
    check_error(mpv_set_option_string(ctx, "input-default-bindings", "yes"));
    mpv_set_option_string(ctx, "input-x11-keyboard", "yes");
    int val = 1;
    check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val));

    // Done setting up options.
    check_error(mpv_initialize(ctx));

    // Play this file.
    const char *cmd[] = {"loadfile", argv[1], NULL};
    check_error(mpv_command(ctx, cmd));

    // Let it play, and wait until the user quits.
    while (1) {
        mpv_event *event = mpv_wait_event(ctx, 10000);
        printf("event: %s\n", mpv_event_name(event->event_id));
        if (event->event_id == MPV_EVENT_SHUTDOWN)
            break;
    }

    mpv_terminate_destroy(ctx);
    return 0;
}
Example #11
0
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);
}
Example #13
0
static void gmpv_mpv_obj_init(GmpvMpvObj *mpv)
{
	mpv->mpv_ctx = mpv_create();
	mpv->opengl_ctx = NULL;
	mpv->playlist = NULL;
	mpv->tmp_input_file = NULL;
	mpv->log_level_list = NULL;
	mpv->autofit_ratio = -1;
	mpv->geometry = NULL;

	mpv->state.ready = FALSE;
	mpv->state.paused = TRUE;
	mpv->state.loaded = FALSE;
	mpv->state.new_file = TRUE;
	mpv->state.init_load = TRUE;

	mpv->force_opengl = FALSE;
	mpv->glarea = NULL;
	mpv->wid = -1;
	mpv->event_callback_data = NULL;
	mpv->opengl_cb_callback_data = NULL;
	mpv->event_callback = NULL;
	mpv->opengl_cb_callback = NULL;
}
bool PlayerComponent::componentInitialize()
{
  m_mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
  if (!m_mpv)
    throw FatalException(tr("Failed to load mpv."));

  mpv_request_log_messages(m_mpv, "terminal-default");
  mpv_set_option_string(m_mpv, "msg-level", "all=v");

  // No mouse events
  mpv_set_option_string(m_mpv, "input-cursor", "no");
  mpv_set_option_string(m_mpv, "cursor-autohide", "no");

  mpv_set_option_string(m_mpv, "config", "yes");
  mpv_set_option_string(m_mpv, "config-dir", Paths::dataDir().toUtf8().data());

  // We don't need this, so avoid initializing fontconfig.
  mpv_set_option_string(m_mpv, "use-text-osd", "no");

  // This forces the player not to rebase playback time to 0 with mkv. We
  // require this, because mkv transcoding lets files start at times other
  // than 0, and web-client expects that we return these times unchanged.
  mpv::qt::set_option_variant(m_mpv, "demuxer-mkv-probe-start-time", false);

  // Always use the system mixer.
  mpv_set_option_string(m_mpv, "softvol", "no");

  // Just discard audio output if no audio device could be opened. This gives
  // us better flexibility how to react to such errors (instead of just
  // aborting playback immediately).
  mpv_set_option_string(m_mpv, "audio-fallback-to-null", "yes");

  // Do not let the decoder downmix (better customization for us).
  mpv::qt::set_option_variant(m_mpv, "ad-lavc-downmix", false);

  // Make it load the hwdec interop, so hwdec can be enabled at runtime.
  mpv::qt::set_option_variant(m_mpv, "hwdec-preload", "auto");

  // User-visible application name used by some audio APIs (at least PulseAudio).
  mpv_set_option_string(m_mpv, "audio-client-name", QCoreApplication::applicationName().toUtf8().data());
  // User-visible stream title used by some audio APIs (at least PulseAudio and wasapi).
  mpv_set_option_string(m_mpv, "title", QCoreApplication::applicationName().toUtf8().data());

  // Apply some low-memory settings on RPI, which is relatively memory-constrained.
#ifdef TARGET_RPI
  // The backbuffer makes seeking back faster (without having to do a HTTP-level seek)
  mpv::qt::set_option_variant(m_mpv, "cache-backbuffer", 10 * 1024); // KB
  // The demuxer queue is used for the readahead, and also for dealing with badly
  // interlaved audio/video. Setting it too low increases sensitivity to network
  // issues, and could cause playback failure with "bad" files.
  mpv::qt::set_option_variant(m_mpv, "demuxer-max-bytes", 50 * 1024 * 1024); // bytes
#endif

  mpv_observe_property(m_mpv, 0, "pause", MPV_FORMAT_FLAG);
  mpv_observe_property(m_mpv, 0, "cache-buffering-state", MPV_FORMAT_INT64);
  mpv_observe_property(m_mpv, 0, "playback-time", MPV_FORMAT_DOUBLE);
  mpv_observe_property(m_mpv, 0, "vo-configured", MPV_FORMAT_FLAG);
  mpv_observe_property(m_mpv, 0, "duration", MPV_FORMAT_DOUBLE);
  mpv_observe_property(m_mpv, 0, "audio-device-list", MPV_FORMAT_NODE);

  connect(this, &PlayerComponent::onMpvEvents, this, &PlayerComponent::handleMpvEvents, Qt::QueuedConnection);

  mpv_set_wakeup_callback(m_mpv, wakeup_cb, this);

  if (mpv_initialize(m_mpv) < 0)
    throw FatalException(tr("Failed to initialize mpv."));

  // Setup a hook with the ID 1, which is run during the file is loaded.
  // Used to delay playback start for display framerate switching.
  // (See handler in handleMpvEvent() for details.)
  mpv::qt::command_variant(m_mpv, QStringList() << "hook-add" << "on_load" << "1" << "0");

  updateAudioDeviceList();
  setAudioConfiguration();
  updateSubtitleSettings();
  updateVideoSettings();

  connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_VIDEO), &SettingsSection::valuesUpdated,
          this, &PlayerComponent::updateVideoSettings);

  connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_SUBTITLES), &SettingsSection::valuesUpdated,
          this, &PlayerComponent::updateSubtitleSettings);

  connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_AUDIO), &SettingsSection::valuesUpdated,
          this, &PlayerComponent::setAudioConfiguration);

  return true;
}
Example #15
0
static void app_startup_handler(GApplication *app, gpointer data)
{
	gmpv_handle *ctx = data;
	const gchar *vid_area_style = ".gmpv-vid-area{background-color: black}";
	GSettingsBackend *config_backend;
	GtkCssProvider *style_provider;
	gboolean css_loaded;
	gboolean use_opengl;
	gboolean config_migrated;
	gboolean mpvinput_enable;
	gboolean csd_enable;
	gboolean dark_theme_enable;
	gchar *config_file;
	gchar *mpvinput;

	setlocale(LC_NUMERIC, "C");
	g_set_application_name(_("GNOME MPV"));
	gtk_window_set_default_icon_name(ICON_NAME);

	bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALEDIR);
	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
	textdomain(GETTEXT_PACKAGE);

	config_file = get_config_file_path();

	config_backend = g_keyfile_settings_backend_new
				(	config_file,
					CONFIG_ROOT_PATH,
					CONFIG_ROOT_GROUP );

	use_opengl = get_use_opengl();

	ctx->mpv_ctx = mpv_create();
	ctx->files = NULL;
	ctx->opengl_ctx = NULL;
	ctx->opengl_ready = FALSE;
	ctx->paused = TRUE;
	ctx->loaded = FALSE;
	ctx->new_file = TRUE;
	ctx->sub_visible = TRUE;
	ctx->init_load = TRUE;
	ctx->playlist_move_dest = -1;
	ctx->log_level_list = NULL;
	ctx->keybind_list = NULL;
	ctx->config = g_settings_new_with_backend(APP_ID, config_backend);
	ctx->app = GTK_APPLICATION(app);
	ctx->gui = MAIN_WINDOW(main_window_new(ctx->app, use_opengl));
	ctx->fs_control = NULL;
	ctx->playlist_store = PLAYLIST_WIDGET(ctx->gui->playlist)->list_store;

	config_migrated = migrate_config(ctx);
	style_provider = gtk_css_provider_new();

	css_loaded = gtk_css_provider_load_from_data
			(style_provider, vid_area_style, -1, NULL);

	if(!css_loaded)
	{
		g_warning ("Failed to apply background color css");
	}

	gtk_style_context_add_provider_for_screen
		(	gtk_window_get_screen(GTK_WINDOW(ctx->gui)),
			GTK_STYLE_PROVIDER(style_provider),
			GTK_STYLE_PROVIDER_PRIORITY_APPLICATION );

	g_object_unref(style_provider);

	csd_enable = g_settings_get_boolean
				(ctx->config, "csd-enable");

	dark_theme_enable = g_settings_get_boolean
				(ctx->config, "dark-theme-enable");

	mpvinput_enable = g_settings_get_boolean
				(ctx->config, "mpv-input-config-enable");

	mpvinput = g_settings_get_string
				(ctx->config, "mpv-input-config-file");

	if(csd_enable)
	{
		gtk_application_set_app_menu
			(ctx->app, G_MENU_MODEL(build_app_menu()));

		main_window_enable_csd(ctx->gui);
	}
	else
	{
		gtk_application_set_app_menu
			(ctx->app, NULL);

		gtk_application_set_menubar
			(ctx->app, G_MENU_MODEL(build_full_menu()));
	}

	gtk_widget_show_all(GTK_WIDGET(ctx->gui));

	if(csd_enable)
	{
		control_box_set_fullscreen_btn_visible
			(CONTROL_BOX(ctx->gui->control_box), FALSE);
	}

	control_box_set_chapter_enabled
		(CONTROL_BOX(ctx->gui->control_box), FALSE);

	if(!main_window_get_use_opengl(ctx->gui))
	{
		ctx->vid_area_wid = get_xid(ctx->gui->vid_area);
	}

	g_assert(	main_window_get_use_opengl(ctx->gui) ||
			ctx->vid_area_wid != -1 );

	main_window_load_state(ctx->gui);
	setup_accelerators(ctx);
	setup_dnd_targets(ctx);
	actionctl_map_actions(ctx);
	connect_signals(ctx);
	load_keybind(ctx, mpvinput_enable?mpvinput:NULL, FALSE);
	mpris_init(ctx);
	media_keys_init(ctx);

	g_object_set(	ctx->gui->settings,
			"gtk-application-prefer-dark-theme",
			dark_theme_enable,
			NULL );

	g_timeout_add(	SEEK_BAR_UPDATE_INTERVAL,
			(GSourceFunc)update_seek_bar,
			ctx );

	if(config_migrated)
	{
		GtkWidget *dialog
			= gtk_message_dialog_new
				(	GTK_WINDOW(ctx->gui),
					GTK_DIALOG_DESTROY_WITH_PARENT,
					GTK_MESSAGE_INFO,
					GTK_BUTTONS_OK,
					_("Your configuration file has been "
					"moved to the new location at %s."),
					config_file );

		gtk_dialog_run(GTK_DIALOG(dialog));
		gtk_widget_destroy(dialog);
	}

	g_free(config_file);
	g_free(mpvinput);
}
Example #16
0
void gmpv_mpv_obj_reset(GmpvMpvObj *mpv)
{
	const gchar *quit_cmd[] = {"quit_watch_later", NULL};
	gchar *loop_str;
	gboolean loop;
	gint64 playlist_pos;
	gint playlist_pos_rc;

	g_assert(mpv->mpv_ctx);

	loop_str = gmpv_mpv_obj_get_property_string(mpv, "loop");
	loop = (g_strcmp0(loop_str, "inf") == 0);

	mpv_free(loop_str);

	playlist_pos_rc = mpv_get_property(	mpv->mpv_ctx,
						"playlist-pos",
						MPV_FORMAT_INT64,
						&playlist_pos );

	/* Reset mpv->mpv_ctx */
	mpv->state.ready = FALSE;

	mpv_check_error(gmpv_mpv_obj_command(mpv, quit_cmd));
	gmpv_mpv_obj_quit(mpv);

	mpv->mpv_ctx = mpv_create();
	gmpv_mpv_obj_initialize(mpv);

	gmpv_mpv_obj_set_event_callback
		(	mpv,
			mpv->event_callback,
			mpv->event_callback_data );
	gmpv_mpv_obj_set_opengl_cb_callback
		(	mpv,
			mpv->opengl_cb_callback,
			mpv->opengl_cb_callback_data );

	gmpv_mpv_obj_set_property_string(mpv, "loop", loop?"inf":"no");

	if(mpv->playlist)
	{
		if(mpv->state.loaded)
		{
			gint rc;

			rc =	mpv_request_event
				(mpv->mpv_ctx, MPV_EVENT_FILE_LOADED, 0);
			mpv_check_error(rc);

			gmpv_mpv_obj_load(mpv, NULL, FALSE, TRUE);

			rc =	mpv_request_event
				(mpv->mpv_ctx, MPV_EVENT_FILE_LOADED, 1);
			mpv_check_error(rc);
		}

		if(playlist_pos_rc >= 0 && playlist_pos > 0)
		{
			gmpv_mpv_obj_set_property(	mpv,
							"playlist-pos",
							MPV_FORMAT_INT64,
							&playlist_pos );
		}

		gmpv_mpv_obj_set_property(	mpv,
						"pause",
						MPV_FORMAT_FLAG,
						&mpv->state.paused );
	}
}
Example #17
0
int main(int argc, char **argv) {

	/**
	 * Parse options
	 */

	bool verbose = false;
	bool debug = false;
	bool opt_track_number = false;
	bool opt_chapter_number = false;
	bool opt_filename = false;
	bool valid_preset = false;
	uint16_t arg_track_number = 0;
	int long_index = 0;
	int opt = 0;
	opterr = 1;
	uint8_t arg_first_chapter = 1;
	uint8_t arg_last_chapter = 99;
	char *token = NULL;
	char *token_filename = NULL;
	char tmp_filename[5] = {'\0'};
	char dvd_mpv_args[13] = {'\0'};
	char dvd_mpv_first_chapter[5] = {'\0'};
	char dvd_mpv_last_chapter[5] = {'\0'};
	mpv_handle *dvd_mpv = NULL;
	mpv_event *dvd_mpv_event = NULL;
	struct mpv_event_log_message *dvd_mpv_log_message = NULL;

	// Video Title Set
	struct dvd_vts dvd_vts[99];

	const char str_options[] = "Ac:dehl:o:p:t:Vvz";
	struct option long_options[] = {

		{ "chapters", required_argument, 0, 'c' },
		{ "track", required_argument, 0, 't' },
		{ "alang", required_argument, 0, 'l' },
		{ "aid", required_argument, 0, 'A' },

		{ "deinterlace", no_argument, 0, 'd' },
		{ "detelecine", no_argument, 0, 'e' },
		{ "preset", required_argument, 0, 'p' },

		{ "output", required_argument, 0, 'o' },

		{ "help", no_argument, 0, 'h' },
		{ "version", no_argument, 0, 'V' },

		{ "verbose", no_argument, 0, 'v' },
		{ "debug", no_argument, 0, 'z' },
		{ 0, 0, 0, 0 }

	};

	struct dvd_trip dvd_trip;

	dvd_trip.track = 1;
	dvd_trip.first_chapter = 1;
	dvd_trip.last_chapter = 99;

	memset(dvd_mpv_first_chapter, '\0', sizeof(dvd_mpv_first_chapter));
	memset(dvd_mpv_last_chapter, '\0', sizeof(dvd_mpv_last_chapter));

	memset(dvd_trip.filename, '\0', sizeof(dvd_trip.filename));
	memset(dvd_trip.container, '\0', sizeof(dvd_trip.container));
	strcpy(dvd_trip.container, "mkv");
	memset(dvd_trip.preset, '\0', sizeof(dvd_trip.preset));
	strcpy(dvd_trip.preset, "medium");
	memset(dvd_trip.vcodec, '\0', sizeof(dvd_trip.vcodec));
	memset(dvd_trip.vcodec_preset, '\0', sizeof(dvd_trip.vcodec_preset));
	memset(dvd_trip.vcodec_opts, '\0', sizeof(dvd_trip.vcodec_opts));
	memset(dvd_trip.vcodec_log_level, '\0', sizeof(dvd_trip.vcodec_log_level));
	memset(dvd_trip.color_opts, '\0', sizeof(dvd_trip.color_opts));
	memset(dvd_trip.acodec, '\0', sizeof(dvd_trip.acodec));
	memset(dvd_trip.acodec_opts, '\0', sizeof(dvd_trip.acodec_opts));
	memset(dvd_trip.audio_lang, '\0', sizeof(dvd_trip.audio_lang));
	memset(dvd_trip.audio_aid, '\0', sizeof(dvd_trip.audio_aid));
	memset(dvd_trip.vf_opts, '\0', sizeof(dvd_trip.vf_opts));
	dvd_trip.crf = 28;
	memset(dvd_trip.fps, '\0', sizeof(dvd_trip.fps));
	dvd_trip.deinterlace = false;
	dvd_trip.detelecine = false;
	dvd_trip.pass = 1;

	while((opt = getopt_long(argc, argv, str_options, long_options, &long_index )) != -1) {

		switch(opt) {

			case 'A':
				strncpy(dvd_trip.audio_aid, optarg, 3);
				break;

			case 'c':
				opt_chapter_number = true;
				token = strtok(optarg, "-"); {
					if(strlen(token) > 2) {
						fprintf(stderr, "Chapter range must be between 1 and 99\n");
						return 1;
					}
					arg_first_chapter = (uint8_t)strtoumax(token, NULL, 0);
				}

				token = strtok(NULL, "-");
				if(token != NULL) {
					if(strlen(token) > 2) {
						fprintf(stderr, "Chapter range must be between 1 and 99\n");
						return 1;
					}
					arg_last_chapter = (uint8_t)strtoumax(token, NULL, 0);
				}

				if(arg_first_chapter == 0)
					arg_first_chapter = 1;
				if(arg_last_chapter < arg_first_chapter)
					arg_last_chapter = arg_first_chapter;

				break;

			case 'd':
				dvd_trip.deinterlace = true;
				break;

			case 'e':
				dvd_trip.detelecine = true;
				break;

			case 'h':
				print_usage(DVD_INFO_PROGRAM);
				return 0;

			case 'l':
				strncpy(dvd_trip.audio_lang, optarg, 2);
				break;

			case 'p':
				if(strncmp(optarg, "low", 3) == 0) {
					strcpy(dvd_trip.preset, "low");
				} else if(strncmp(optarg, "medium", 6) == 0) {
					strcpy(dvd_trip.preset, "medium");
				} else if(strncmp(optarg, "high", 4) == 0) {
					strcpy(dvd_trip.preset, "high");
				} else if(strncmp(optarg, "insane", 6) == 0) {
					strcpy(dvd_trip.preset, "insane");
				} else {
					printf("dvd_trip [error]: valid presets - low medium high insane\n");
					return 1;
				}
				break;

			case 'o':
				opt_filename = true;
				strncpy(dvd_trip.filename, optarg, PATH_MAX - 1);
				token_filename = strtok(optarg, ".");

				// Choose preset from file extension
				while(token_filename != NULL) {

					snprintf(tmp_filename, 5, "%s", token_filename);
					token_filename = strtok(NULL, ".");

					if(token_filename == NULL && strlen(tmp_filename) == 3 && strncmp(tmp_filename, "mkv", 3) == 0) {
						strncpy(dvd_trip.container, "mkv", 4);
						valid_preset = true;
					} else if(token_filename == NULL && strlen(tmp_filename) == 3 && strncmp(tmp_filename, "mp4", 3) == 0) {
						strncpy(dvd_trip.container, "mp4", 4);
						valid_preset = true;
					} else if(token_filename == NULL && strlen(tmp_filename) == 4 && strncmp(tmp_filename, "webm", 4) == 0) {
						strncpy(dvd_trip.container, "webm", 5);
						valid_preset = true;
					}

				}

				if(!valid_preset) {
					printf("dvd_trip [error]: output filename extension must be one of: .mkv, .mp4, .webm\n");
					return 1;
				}

				break;

			case 't':
				opt_track_number = true;
				arg_track_number = (uint16_t)strtoumax(optarg, NULL, 0);
				break;

			case 'V':
				print_version("dvd_trip");
				return 0;

			case 'v':
				verbose = true;
				break;

			case 'z':
				verbose = true;
				debug = true;
				break;

			// ignore unknown arguments
			case '?':
				print_usage("dvd_trip");
				return 1;

			// let getopt_long set the variable
			case 0:
			default:
				break;

		}

	}

	const char *device_filename = DEFAULT_DVD_DEVICE;

	if (argv[optind])
		device_filename = argv[optind];

	if(access(device_filename, F_OK) != 0) {
		fprintf(stderr, "cannot access %s\n", device_filename);
		return 1;
	}

	// Check to see if device can be opened
	int dvd_fd = 0;
	dvd_fd = dvd_device_open(device_filename);
	if(dvd_fd < 0) {
		fprintf(stderr, "dvd_trip: error opening %s\n", device_filename);
		return 1;
	}
	dvd_device_close(dvd_fd);


#ifdef __linux__

	// Poll drive status if it is hardware
	if(dvd_device_is_hardware(device_filename)) {

		// Wait for the drive to become ready
		if(!dvd_drive_has_media(device_filename)) {

			fprintf(stderr, "drive status: ");
			dvd_drive_display_status(device_filename);

			return 1;

		}

	}

#endif

	dvd_reader_t *dvdread_dvd = NULL;
	dvdread_dvd = DVDOpen(device_filename);

	if(!dvdread_dvd) {
		fprintf(stderr, "* dvdread could not open %s\n", device_filename);
		return 1;
	}

	ifo_handle_t *vmg_ifo = NULL;
	vmg_ifo = ifoOpen(dvdread_dvd, 0);

	if(vmg_ifo == NULL) {
		fprintf(stderr, "* Could not open IFO zero\n");
		DVDClose(dvdread_dvd);
		return 1;
	}

	// DVD
	struct dvd_info dvd_info;
	memset(dvd_info.dvdread_id, '\0', sizeof(dvd_info.dvdread_id));
	dvd_info.video_title_sets = dvd_video_title_sets(vmg_ifo);
	dvd_info.side = 1;
	memset(dvd_info.title, '\0', sizeof(dvd_info.title));
	memset(dvd_info.provider_id, '\0', sizeof(dvd_info.provider_id));
	memset(dvd_info.vmg_id, '\0', sizeof(dvd_info.vmg_id));
	dvd_info.tracks = dvd_tracks(vmg_ifo);
	dvd_info.longest_track = 1;

	dvd_title(dvd_info.title, device_filename);
	printf("Disc title: %s\n", dvd_info.title);

	uint16_t num_ifos = 1;
	num_ifos = vmg_ifo->vts_atrt->nr_of_vtss;

	if(num_ifos < 1) {
		fprintf(stderr, "* DVD has no title IFOs?!\n");
		fprintf(stderr, "* Most likely a bug in libdvdread or a bad master or problems reading the disc\n");
		ifoClose(vmg_ifo);
		DVDClose(dvdread_dvd);
		return 1;
	}


	// Track
	struct dvd_track dvd_track;
	memset(&dvd_track, 0, sizeof(dvd_track));

	struct dvd_track dvd_tracks[DVD_MAX_TRACKS];
	memset(&dvd_tracks, 0, sizeof(dvd_track) * dvd_info.tracks);

	// Cells
	struct dvd_cell dvd_cell;
	dvd_cell.cell = 1;
	memset(dvd_cell.length, '\0', sizeof(dvd_cell.length));
	snprintf(dvd_cell.length, DVD_CELL_LENGTH + 1, "00:00:00.000");
	dvd_cell.msecs = 0;

	// Open first IFO
	uint16_t vts = 1;
	ifo_handle_t *vts_ifo = NULL;

	vts_ifo = ifoOpen(dvdread_dvd, vts);
	if(vts_ifo == NULL) {
		fprintf(stderr, "* Could not open VTS_IFO for track %u\n", 1);
		return 1;
	}
	ifoClose(vts_ifo);
	vts_ifo = NULL;

	// Create an array of all the IFOs
	ifo_handle_t *vts_ifos[DVD_MAX_VTS_IFOS];
	vts_ifos[0] = NULL;

	for(vts = 1; vts < dvd_info.video_title_sets + 1; vts++) {

		dvd_vts[vts].vts = vts;
		dvd_vts[vts].valid = false;
		dvd_vts[vts].blocks = 0;
		dvd_vts[vts].filesize = 0;
		dvd_vts[vts].vobs = 0;
		dvd_vts[vts].tracks = 0;
		dvd_vts[vts].valid_tracks = 0;
		dvd_vts[vts].invalid_tracks = 0;

		vts_ifos[vts] = ifoOpen(dvdread_dvd, vts);

		if(vts_ifos[vts] == NULL) {
			dvd_vts[vts].valid = false;
			vts_ifos[vts] = NULL;
		} else if(!ifo_is_vts(vts_ifos[vts])) {
			dvd_vts[vts].valid = false;
			ifoClose(vts_ifos[vts]);
			vts_ifos[vts] = NULL;
		} else {
			dvd_vts[vts].valid = true;
		}

	}
	
	// Exit if track number requested does not exist
	if(opt_track_number && (arg_track_number > dvd_info.tracks)) {
		fprintf(stderr, "dvd_trip: Invalid track number %d\n", arg_track_number);
		fprintf(stderr, "dvd_trip: Valid track numbers: 1 to %u\n", dvd_info.tracks);
		ifoClose(vmg_ifo);
		DVDClose(dvdread_dvd);
		return 1;
	} else if(opt_track_number) {
		dvd_trip.track = arg_track_number;
	}

	uint16_t ix = 0;
	uint16_t track = 1;
	
	uint32_t longest_msecs = 0;
	
	for(ix = 0, track = 1; ix < dvd_info.tracks; ix++, track++) {
 
		vts = dvd_vts_ifo_number(vmg_ifo, ix + 1);
		vts_ifo = vts_ifos[vts];
		dvd_track_info(&dvd_tracks[ix], track, vmg_ifo, vts_ifo);
		dvd_tracks[ix].valid = true;

		if(dvd_tracks[ix].msecs > longest_msecs) {
			dvd_info.longest_track = track;
			longest_msecs = dvd_tracks[ix].msecs;
		}

	}

	// Set the track number to rip if none is passed as an argument
	if(!opt_track_number)
		dvd_trip.track = dvd_info.longest_track;
	
	dvd_track = dvd_tracks[dvd_trip.track - 1];

	// Set the proper chapter range
	if(opt_chapter_number) {
		if(arg_first_chapter > dvd_track.chapters) {
			dvd_trip.first_chapter = dvd_track.chapters;
			fprintf(stderr, "Resetting first chapter to %u\n", dvd_trip.first_chapter);
		} else
			dvd_trip.first_chapter = arg_first_chapter;
		
		if(arg_last_chapter > dvd_track.chapters) {
			dvd_trip.last_chapter = dvd_track.chapters;
			fprintf(stderr, "Resetting last chapter to %u\n", dvd_trip.last_chapter);
		} else
			dvd_trip.last_chapter = arg_last_chapter;
	} else {
		dvd_trip.first_chapter = 1;
		dvd_trip.last_chapter = dvd_track.chapters;
	}
	
	/**
	 * File descriptors and filenames
	 */
	dvd_file_t *dvdread_vts_file = NULL;

	vts = dvd_vts_ifo_number(vmg_ifo, dvd_trip.track);
	vts_ifo = vts_ifos[vts];

	// Open the VTS VOB
	dvdread_vts_file = DVDOpenFile(dvdread_dvd, vts, DVD_READ_TITLE_VOBS);

	printf("Track: %02u, Length: %s, Chapters: %02u, Cells: %02u, Audio streams: %02u, Subpictures: %02u, Filesize: %lu, Blocks: %lu\n", dvd_track.track, dvd_track.length, dvd_track.chapters, dvd_track.cells, dvd_track.audio_tracks, dvd_track.subtitles, dvd_track.filesize, dvd_track.blocks);

	// Check for track issues
	dvd_track.valid = true;

	if(dvd_vts[vts].valid == false) {
		dvd_track.valid = false;
	}

	if(dvd_track.msecs == 0) {
		printf("	Error: track has zero length\n");
		dvd_track.valid = false;
	}

	if(dvd_track.chapters == 0) {
		printf("	Error: track has zero chapters\n");
		dvd_track.valid = false;
	}

	if(dvd_track.cells == 0) {
		printf("	Error: track has zero cells\n");
		dvd_track.valid = false;
	}

	if(dvd_track.valid == false) {

		printf("Track has been marked as invalid, quitting\n");

		DVDCloseFile(dvdread_vts_file);

		if(vts_ifo)
			ifoClose(vts_ifo);

		if(vmg_ifo)
			ifoClose(vmg_ifo);

		if(dvdread_dvd)
			DVDClose(dvdread_dvd);

		return 1;

	}

	// MPV zero-indexes tracks
	sprintf(dvd_mpv_args, "dvdread://%u", dvd_trip.track - 1);

	const char *dvd_mpv_commands[] = { "loadfile", dvd_mpv_args, NULL };


	// DVD playback using libmpv
	dvd_mpv = mpv_create();

	// Terminal output
	mpv_set_option_string(dvd_mpv, "terminal", "yes");
	if(!debug)
		mpv_set_option_string(dvd_mpv, "term-osd-bar", "yes");

	if (debug) {
		mpv_request_log_messages(dvd_mpv, "debug");
		strcpy(dvd_trip.vcodec_log_level, "full");
	} else if(verbose) {
		mpv_request_log_messages(dvd_mpv, "v");
		strcpy(dvd_trip.vcodec_log_level, "info");
	} else {
		mpv_request_log_messages(dvd_mpv, "info");
		strcpy(dvd_trip.vcodec_log_level, "info");
	}

	/** Video **/

	// Set output frames per second and color spaces based on source (NTSC or PAL)
	if(dvd_track_pal_video(vts_ifo)) {
		strcpy(dvd_trip.fps, "25");
		strcpy(dvd_trip.color_opts, "color_primaries=bt470bg,color_trc=gamma28,colorspace=bt470bg");
	} else {
		strcpy(dvd_trip.fps, "30000/1001");
		strcpy(dvd_trip.color_opts, "color_primaries=smpte170m,color_trc=smpte170m,colorspace=smpte170m");
	}

	/** Containers and Presets **/

	// Set preset defaults
	if(strncmp(dvd_trip.container, "mkv", 3) == 0) {

		if(!opt_filename)
			strcpy(dvd_trip.filename, "trip_encode.mkv");

		strcpy(dvd_trip.vcodec, "libx265");
		strcpy(dvd_trip.vcodec_preset, "medium");
		strcpy(dvd_trip.acodec, "libfdk_aac");


		if(strncmp(dvd_trip.preset, "low", 3) == 0) {
			strcpy(dvd_trip.vcodec_preset, "fast");
			dvd_trip.crf = 28;
		} else if(strncmp(dvd_trip.preset, "medium", 6) == 0) {
			strcpy(dvd_trip.vcodec_preset, "medium");
			dvd_trip.crf = 24;
		} else if(strncmp(dvd_trip.preset, "high", 4) == 0) {
			strcpy(dvd_trip.vcodec_preset, "slow");
			strcpy(dvd_trip.acodec_opts, "b=192k");
			dvd_trip.crf = 20;
		} else if(strncmp(dvd_trip.preset, "insane", 6) == 0) {
			strcpy(dvd_trip.vcodec_preset, "slower");
			strcpy(dvd_trip.acodec_opts, "b=256k");
			dvd_trip.crf = 14;
		}

		sprintf(dvd_trip.vcodec_opts, "%s,preset=%s,crf=%u,x265-params=log-level=%s", dvd_trip.color_opts, dvd_trip.vcodec_preset, dvd_trip.crf, dvd_trip.vcodec_log_level);

	}

	if(strncmp(dvd_trip.container, "mp4", 3) == 0) {

		if(!opt_filename)
			strcpy(dvd_trip.filename, "trip_encode.mp4");

		strcpy(dvd_trip.vcodec, "libx264");
		strcpy(dvd_trip.vcodec_preset, "medium");
		strcpy(dvd_trip.acodec, "libfdk_aac");
		strcpy(dvd_trip.acodec_opts, "");


		if(strncmp(dvd_trip.preset, "low", 3) == 0) {
			strcpy(dvd_trip.vcodec_preset, "fast");
			dvd_trip.crf = 28;
		} else if(strncmp(dvd_trip.preset, "medium", 6) == 0) {
			strcpy(dvd_trip.vcodec_preset, "medium");
			dvd_trip.crf = 22;
		} else if(strncmp(dvd_trip.preset, "high", 4) == 0) {
			strcpy(dvd_trip.vcodec_preset, "slow");
			strcpy(dvd_trip.acodec_opts, "b=192k");
			dvd_trip.crf = 20;
		} else if(strncmp(dvd_trip.preset, "insane", 6) == 0) {
			strcpy(dvd_trip.vcodec_preset, "slower");
			strcpy(dvd_trip.acodec_opts, "b=256k");
			dvd_trip.crf = 16;
		}

		// x264 doesn't allow passing log level (that I can see)
		sprintf(dvd_trip.vcodec_opts, "%s,preset=%s,crf=%u", dvd_trip.color_opts, dvd_trip.vcodec_preset, dvd_trip.crf);

	}

	if(strncmp(dvd_trip.container, "webm", 4) == 0) {

		if(!opt_filename)
			strcpy(dvd_trip.filename, "trip_encode.webm");

		strcpy(dvd_trip.vcodec, "libvpx-vp9");
		strcpy(dvd_trip.acodec, "libopus");
		strcpy(dvd_trip.acodec_opts, "application=audio");

		if(strncmp(dvd_trip.preset, "low", 3) == 0) {
			dvd_trip.crf = 34;
			sprintf(dvd_trip.vcodec_opts, "%s,b=0,crf=%u,keyint_min=0,g=360", dvd_trip.color_opts, dvd_trip.crf);
			strcpy(dvd_trip.acodec_opts, "application=audio,b=96000");
		}

		if(strncmp(dvd_trip.preset, "medium", 6) == 0) {
			dvd_trip.crf = 32;
			sprintf(dvd_trip.vcodec_opts, "%s,b=0,crf=%u,keyint_min=0,g=360", dvd_trip.color_opts, dvd_trip.crf);
			strcpy(dvd_trip.acodec_opts, "application=audio,b=144000");
		}

		if(strncmp(dvd_trip.preset, "high", 4) == 0) {
			dvd_trip.crf = 22;
			sprintf(dvd_trip.vcodec_opts, "%s,b=0,crf=%u,keyint_min=0,g=360", dvd_trip.color_opts, dvd_trip.crf);
			strcpy(dvd_trip.acodec_opts, "application=audio,b=192000");
		}

		if(strncmp(dvd_trip.preset, "insane", 6) == 0) {
			dvd_trip.crf = 16;
			sprintf(dvd_trip.vcodec_opts, "%s,b=0,crf=%u,keyint_min=0,g=360", dvd_trip.color_opts, dvd_trip.crf);
			strcpy(dvd_trip.acodec_opts, "application=audio,b=256000");
		}

	}

	mpv_set_option_string(dvd_mpv, "o", dvd_trip.filename);
	mpv_set_option_string(dvd_mpv, "ovc", dvd_trip.vcodec);
	mpv_set_option_string(dvd_mpv, "ovcopts", dvd_trip.vcodec_opts);
	mpv_set_option_string(dvd_mpv, "oac", dvd_trip.acodec);
	if(strlen(dvd_trip.acodec_opts) > 0)
		mpv_set_option_string(dvd_mpv, "oacopts", dvd_trip.acodec_opts);
	mpv_set_option_string(dvd_mpv, "dvd-device", device_filename);
	mpv_set_option_string(dvd_mpv, "track-auto-selection", "yes");
	mpv_set_option_string(dvd_mpv, "input-default-bindings", "yes");
	mpv_set_option_string(dvd_mpv, "input-vo-keyboard", "yes");
	mpv_set_option_string(dvd_mpv, "resume-playback", "no");

	// MPV's chapter range starts at the first one, and ends at the last one plus one
	// fex: to play chapter 1 only, mpv --start '#1' --end '#2'
	sprintf(dvd_mpv_first_chapter, "#%u", dvd_trip.first_chapter);
	sprintf(dvd_mpv_last_chapter, "#%u", dvd_trip.last_chapter + 1);
	mpv_set_option_string(dvd_mpv, "start", dvd_mpv_first_chapter);
	mpv_set_option_string(dvd_mpv, "end", dvd_mpv_last_chapter);

	if(strlen(dvd_trip.audio_aid) > 0)
		mpv_set_option_string(dvd_mpv, "aid", dvd_trip.audio_aid);
	else if(strlen(dvd_trip.audio_lang) > 0)
		mpv_set_option_string(dvd_mpv, "alang", dvd_trip.audio_lang);

	/** Video Filters **/

	if(mpv_client_api_version() <= MPV_MAKE_VERSION(1, 25)) {

		// Syntax up to 0.27.2
		mpv_set_option_string(dvd_mpv, "ofps", dvd_trip.fps);

		if(dvd_trip.detelecine && dvd_trip.deinterlace)
			sprintf(dvd_trip.vf_opts, "lavfi=yadif,lavfi=pullup,lavfi=dejudder");
		else if(dvd_trip.deinterlace)
			sprintf(dvd_trip.vf_opts, "lavfi=yadif");
		else if(dvd_trip.detelecine)
			sprintf(dvd_trip.vf_opts, "lavfi=pullup,lavfi=dejudder");

	} else {

		// Syntax starting in 0.29.1
		if(dvd_trip.detelecine && dvd_trip.deinterlace)
			sprintf(dvd_trip.vf_opts, "lavfi-yadif,lavfi-pullup,lavfi-dejudder,fps=%s", dvd_trip.fps);
		else if(dvd_trip.deinterlace)
			sprintf(dvd_trip.vf_opts, "lavfi-yadif,fps=%s", dvd_trip.fps);
		else if(dvd_trip.detelecine)
			sprintf(dvd_trip.vf_opts, "lavfi-pullup,lavfi-dejudder,fps=%s", dvd_trip.fps);
		else
			sprintf(dvd_trip.vf_opts, "fps=%s", dvd_trip.fps);

	}

	mpv_set_option_string(dvd_mpv, "vf", dvd_trip.vf_opts);

	if(dvd_trip.pass == 1) {
		fprintf(stderr, "dvd_trip [info]: dvd track %u\n", dvd_trip.track);
		fprintf(stderr, "dvd_trip [info]: chapters %u to %u\n", dvd_trip.first_chapter, dvd_trip.last_chapter);
		fprintf(stderr, "dvd_trip [info]: saving to %s\n", dvd_trip.filename);
		fprintf(stderr, "dvd_trip [info]: vcodec %s\n", dvd_trip.vcodec);
		fprintf(stderr, "dvd_trip [info]: acodec %s\n", dvd_trip.acodec);
		fprintf(stderr, "dvd_trip [info]: ovcopts %s\n", dvd_trip.vcodec_opts);
		fprintf(stderr, "dvd_trip [info]: oacopts %s\n", dvd_trip.acodec_opts);
		if(strlen(dvd_trip.vf_opts))
			fprintf(stderr, "dvd_trip [info]: vf %s\n", dvd_trip.vf_opts);
		fprintf(stderr, "dvd_trip [info]: output fps %s\n", dvd_trip.fps);
		if(dvd_trip.deinterlace)
			fprintf(stderr, "dvd_trip [info]: deinterlacing video\n");
		if(dvd_trip.detelecine)
			fprintf(stderr, "dvd_trip [info]: detelecining video\n");
	}

	mpv_initialize(dvd_mpv);
	mpv_command(dvd_mpv, dvd_mpv_commands);

	while(true) {

		dvd_mpv_event = mpv_wait_event(dvd_mpv, -1);

		if(dvd_mpv_event->event_id == MPV_EVENT_SHUTDOWN || dvd_mpv_event->event_id == MPV_EVENT_END_FILE)
			break;

		// Logging output
		if((verbose || debug) && dvd_mpv_event->event_id == MPV_EVENT_LOG_MESSAGE) {
			dvd_mpv_log_message = (struct mpv_event_log_message *)dvd_mpv_event->data;
			printf("mpv [%s]: %s", dvd_mpv_log_message->level, dvd_mpv_log_message->text);
		}

	}

	mpv_terminate_destroy(dvd_mpv);

	DVDCloseFile(dvdread_vts_file);

	if(vts_ifo)
		ifoClose(vts_ifo);
	
	if(vmg_ifo)
		ifoClose(vmg_ifo);

	if(dvdread_dvd)
		DVDClose(dvdread_dvd);
	
	return 0;

}
Example #18
0
int main(int argc, char **argv) {

	/**
	 * Parse options
	 */

	bool verbose = false;
	bool debug = false;
	bool opt_track_number = false;
	bool opt_chapter_number = false;
	bool opt_widescreen = false;
	bool opt_pan_scan = false;
	bool opt_no_video = false;
	bool opt_no_audio = false;
	uint16_t arg_track_number = 0;
	uint8_t arg_first_chapter = 1;
	uint8_t arg_last_chapter = 99;
	struct dvd_player dvd_player;
	struct dvd_playback dvd_playback;
	char dvd_mpv_args[13] = {'\0'};
	mpv_handle *dvd_mpv = NULL;
	mpv_event *dvd_mpv_event = NULL;
	struct mpv_event_log_message *dvd_mpv_log_message = NULL;
	const char *home_dir = getenv("HOME");
	const char *lang = getenv("LANG");

	// Video Title Set
	struct dvd_vts dvd_vts[99];

	// DVD player default options
	snprintf(dvd_player.config_dir, 20, "/.config/dvd_player");
	memset(dvd_player.mpv_config_dir, '\0', sizeof(dvd_player.mpv_config_dir));
	if(home_dir != NULL)
		snprintf(dvd_player.mpv_config_dir, PATH_MAX - 1, "%s%s", home_dir, dvd_player.config_dir);

	// DVD playback default options
	dvd_playback.track = 1;
	dvd_playback.first_chapter = 1;
	dvd_playback.last_chapter = 99;
	dvd_playback.fullscreen = false;
	dvd_playback.deinterlace = false;
	dvd_playback.subtitles = false;
	snprintf(dvd_playback.mpv_chapters_range, 8, "%u-%u", 1, 99);
	memset(dvd_playback.audio_lang, '\0', sizeof(dvd_playback.audio_lang));
	if(strlen(lang) >= 2)
		snprintf(dvd_playback.audio_lang, 3, "%s", strndup(lang, 2));
	memset(dvd_playback.audio_aid, '\0', sizeof(dvd_playback.audio_aid));
	memset(dvd_playback.subtitles_lang, '\0', sizeof(dvd_playback.subtitles_lang));
	if(strlen(lang) >= 2)
		snprintf(dvd_playback.subtitles_lang, 3, "%s", strndup(lang, 2));
	memset(dvd_playback.subtitles_sid, '\0', sizeof(dvd_playback.subtitles_sid));

	const char str_options[] = "Aa:c:dfhpSs:t:Vvwz";
	struct option long_options[] = {

		{ "track", required_argument, 0, 't' },
		{ "chapters", required_argument, 0, 'c' },
		{ "fullscreen", no_argument, 0, 'f' },
		{ "deinterlace", no_argument, 0, 'd' },
		{ "alang", required_argument, 0, 'a' },
		{ "slang", required_argument, 0, 's' },
		{ "aid", required_argument, 0, 'A' },
		{ "sid", required_argument, 0, 'S' },
		{ "help", no_argument, 0, 'h' },
		{ "version", no_argument, 0, 'V' },
		{ "widescreen", no_argument, 0, 'w' },
		{ "pan-scan", no_argument, 0, 'p' },
		{ "verbose", no_argument, 0, 'v' },
		{ "debug", no_argument, 0, 'z' },
		{ 0, 0, 0, 0 }

	};

	int long_index = 0;
	int opt = 0;
	opterr = 1;
	char *token = NULL;

	while((opt = getopt_long(argc, argv, str_options, long_options, &long_index )) != -1) {

		switch(opt) {

			case 'A':
				strncpy(dvd_playback.audio_aid, optarg, 3);
				break;

			case 'a':
				strncpy(dvd_playback.audio_lang, optarg, 2);
				break;

			case 'c':
				opt_chapter_number = true;
				token = strtok(optarg, "-"); {
					if(strlen(token) > 2) {
						fprintf(stderr, "Chapter range must be between 1 and 99\n");
						return 1;
					}
					arg_first_chapter = (uint8_t)strtoumax(token, NULL, 0);
				}

				token = strtok(NULL, "-");
				if(token != NULL) {
					if(strlen(token) > 2) {
						fprintf(stderr, "Chapter range must be between 1 and 99\n");
						return 1;
					}
					arg_last_chapter = (uint8_t)strtoumax(token, NULL, 0);
				}

				if(arg_first_chapter == 0)
					arg_first_chapter = 1;
				if(arg_last_chapter < arg_first_chapter)
					arg_last_chapter = arg_first_chapter;
				if(arg_first_chapter > arg_last_chapter)
					arg_first_chapter = arg_last_chapter;

				break;

			case 'd':
				dvd_playback.deinterlace = true;
				break;

			case 'f':
				dvd_playback.fullscreen = true;
				break;

			case 'h':
				print_usage(DVD_INFO_PROGRAM);
				return 0;

			case 'p':
				opt_pan_scan = true;
				break;

			case 's':
				strncpy(dvd_playback.subtitles_lang, optarg, 2);
				dvd_playback.subtitles = true;
				break;

			case 'S':
				strncpy(dvd_playback.subtitles_sid, optarg, 3);
				dvd_playback.subtitles = true;
				break;

			case 't':
				opt_track_number = true;
				arg_track_number = (uint16_t)strtoumax(optarg, NULL, 0);
				break;

			case 'V':
				print_version("dvd_player");
				return 0;

			case 'v':
				verbose = true;
				break;

			case 'w':
				opt_widescreen = true;
				break;

			case 'z':
				verbose = true;
				debug = true;
				break;

			// ignore unknown arguments
			case '?':
				print_usage("dvd_player");
				return 1;

			// let getopt_long set the variable
			case 0:
			default:
				break;

		}

	}

	if(opt_pan_scan && opt_widescreen)
		opt_pan_scan = false;
	
	const char *device_filename = DEFAULT_DVD_DEVICE;

	if (argv[optind])
		device_filename = argv[optind];

	if(access(device_filename, F_OK) != 0) {
		fprintf(stderr, "cannot access %s\n", device_filename);
		return 1;
	}

	// Check to see if device can be opened
	int dvd_fd = 0;
	dvd_fd = dvd_device_open(device_filename);
	if(dvd_fd < 0) {
		fprintf(stderr, "dvd_player: error opening %s\n", device_filename);
		return 1;
	}
	dvd_device_close(dvd_fd);


#ifdef __linux__

	// Poll drive status if it is hardware
	if(dvd_device_is_hardware(device_filename)) {

		// Wait for the drive to become ready
		if(!dvd_drive_has_media(device_filename)) {

			fprintf(stderr, "drive status: ");
			dvd_drive_display_status(device_filename);

			return 1;

		}

	}

#endif

	dvd_reader_t *dvdread_dvd = NULL;
	dvdread_dvd = DVDOpen(device_filename);

	if(!dvdread_dvd) {
		fprintf(stderr, "* dvdread could not open %s\n", device_filename);
		return 1;
	}

	ifo_handle_t *vmg_ifo = NULL;
	vmg_ifo = ifoOpen(dvdread_dvd, 0);

	if(vmg_ifo == NULL) {
		fprintf(stderr, "* Could not open IFO zero\n");
		DVDClose(dvdread_dvd);
		return 1;
	}

	// DVD
	struct dvd_info dvd_info;
	memset(dvd_info.dvdread_id, '\0', sizeof(dvd_info.dvdread_id));
	dvd_info.video_title_sets = dvd_video_title_sets(vmg_ifo);
	dvd_info.side = 1;
	memset(dvd_info.title, '\0', sizeof(dvd_info.title));
	memset(dvd_info.provider_id, '\0', sizeof(dvd_info.provider_id));
	memset(dvd_info.vmg_id, '\0', sizeof(dvd_info.vmg_id));
	dvd_info.tracks = dvd_tracks(vmg_ifo);
	dvd_info.longest_track = 1;

	dvd_title(dvd_info.title, device_filename);
	printf("Disc title: %s\n", dvd_info.title);

	uint16_t num_ifos = 1;
	num_ifos = vmg_ifo->vts_atrt->nr_of_vtss;

	if(num_ifos < 1) {
		fprintf(stderr, "* DVD has no title IFOs?!\n");
		fprintf(stderr, "* Most likely a bug in libdvdread or a bad master or problems reading the disc\n");
		ifoClose(vmg_ifo);
		DVDClose(dvdread_dvd);
		return 1;
	}


	// Track
	struct dvd_track dvd_track;
	memset(&dvd_track, 0, sizeof(dvd_track));

	struct dvd_track dvd_tracks[DVD_MAX_TRACKS];
	memset(&dvd_tracks, 0, sizeof(dvd_track) * dvd_info.tracks);

	// Open first IFO
	uint16_t vts = 1;
	ifo_handle_t *vts_ifo = NULL;

	vts_ifo = ifoOpen(dvdread_dvd, vts);
	if(vts_ifo == NULL) {
		fprintf(stderr, "* Could not open VTS_IFO for track %u\n", 1);
		return 1;
	}
	ifoClose(vts_ifo);
	vts_ifo = NULL;

	// Create an array of all the IFOs
	ifo_handle_t *vts_ifos[DVD_MAX_VTS_IFOS];
	vts_ifos[0] = NULL;

	for(vts = 1; vts < dvd_info.video_title_sets + 1; vts++) {

		dvd_vts[vts].vts = vts;
		dvd_vts[vts].valid = false;
		dvd_vts[vts].blocks = 0;
		dvd_vts[vts].filesize = 0;
		dvd_vts[vts].vobs = 0;
		dvd_vts[vts].tracks = 0;
		dvd_vts[vts].valid_tracks = 0;
		dvd_vts[vts].invalid_tracks = 0;

		vts_ifos[vts] = ifoOpen(dvdread_dvd, vts);

		if(vts_ifos[vts] == NULL) {
			dvd_vts[vts].valid = false;
			vts_ifos[vts] = NULL;
		} else if(!ifo_is_vts(vts_ifos[vts])) {
			dvd_vts[vts].valid = false;
			ifoClose(vts_ifos[vts]);
			vts_ifos[vts] = NULL;
		} else {
			dvd_vts[vts].valid = true;
		}

	}
	
	// Exit if track number requested does not exist
	if(opt_track_number && (arg_track_number > dvd_info.tracks)) {
		fprintf(stderr, "dvd_player: Invalid track number %d\n", arg_track_number);
		fprintf(stderr, "dvd_player: Valid track numbers: 1 to %u\n", dvd_info.tracks);
		ifoClose(vmg_ifo);
		DVDClose(dvdread_dvd);
		return 1;
	} else if(opt_track_number) {
		dvd_playback.track = arg_track_number;
	}

	uint16_t ix = 0;
	uint16_t track = 1;
	
	uint32_t longest_msecs = 0;
	uint16_t longest_widescreen_track = 0;
	uint32_t longest_widescreen_msecs = 0;
	uint16_t longest_pan_scan_track = 0;
	uint32_t longest_pan_scan_msecs = 0;
	
	for(ix = 0, track = 1; ix < dvd_info.tracks; ix++, track++) {
 
		vts = dvd_vts_ifo_number(vmg_ifo, ix + 1);
		vts_ifo = vts_ifos[vts];
		dvd_track_info(&dvd_tracks[ix], track, vmg_ifo, vts_ifo);

		if(dvd_tracks[ix].msecs > longest_msecs) {
			dvd_info.longest_track = track;
			longest_msecs = dvd_tracks[ix].msecs;
		}

		if(dvd_track_aspect_ratio_16x9(vts_ifo) && dvd_tracks[ix].msecs > longest_widescreen_msecs) {
			longest_widescreen_msecs = dvd_tracks[ix].msecs;
			longest_widescreen_track = track;
		}

		else if(dvd_track_aspect_ratio_4x3(vts_ifo) && dvd_tracks[ix].msecs > longest_pan_scan_msecs) {
			longest_pan_scan_msecs = dvd_tracks[ix].msecs;
			longest_pan_scan_track = track;
		}

	}

	if(opt_widescreen && longest_widescreen_track != 0)
		dvd_info.longest_track = longest_widescreen_track;
	else if(opt_pan_scan && longest_pan_scan_track != 0)
		dvd_info.longest_track = longest_pan_scan_track;

	// TODO if there is another track that is active, within ~1 seconds of longest track, and
	// the first is pan & scan, and the second is widescreen, switch to the widescreen one.
	// A more intelligent search might also see if the second one has audio tracks. Need to
	// find a reference DVD.

	// Set the track number to play if none is passed as an argument
	if(!opt_track_number)
		dvd_playback.track = dvd_info.longest_track;
	
	dvd_track = dvd_tracks[dvd_playback.track - 1];

	// Set the proper chapter range
	if(opt_chapter_number) {
		if(arg_first_chapter > dvd_track.chapters) {
			dvd_playback.first_chapter = dvd_track.chapters;
		} else
			dvd_playback.first_chapter = arg_first_chapter;
		
		if(arg_last_chapter > dvd_track.chapters) {
			dvd_playback.last_chapter = dvd_track.chapters;
		} else
			dvd_playback.last_chapter = arg_last_chapter;
	} else {
		dvd_playback.first_chapter = 1;
		dvd_playback.last_chapter = dvd_track.chapters;
	}
	
	/**
	 * File descriptors and filenames
	 */
	dvd_file_t *dvdread_vts_file = NULL;

	vts = dvd_vts_ifo_number(vmg_ifo, dvd_playback.track);
	vts_ifo = vts_ifos[vts];

	// Open the VTS VOB
	dvdread_vts_file = DVDOpenFile(dvdread_dvd, vts, DVD_READ_TITLE_VOBS);

	printf("Track: %02u, Length: %s, Chapters: %02u, Cells: %02u, Audio streams: %02u, Subpictures: %02u, Filesize: %lu, Blocks: %lu\n", dvd_track.track, dvd_track.length, dvd_track.chapters, dvd_track.cells, dvd_track.audio_tracks, dvd_track.subtitles, dvd_track.filesize, dvd_track.blocks);

	// Check for track issues
	dvd_track.valid = true;

	if(dvd_vts[vts].valid == false) {
		dvd_track.valid = false;
	}

	if(dvd_track.msecs == 0) {
		printf("	Error: track has zero length\n");
		dvd_track.valid = false;
	}

	if(dvd_track.chapters == 0) {
		printf("	Error: track has zero chapters\n");
		dvd_track.valid = false;
	}

	if(dvd_track.cells == 0) {
		printf("	Error: track has zero cells\n");
		dvd_track.valid = false;
	}

	if(dvd_track.valid == false) {

		printf("Track has been marked as invalid, quitting\n");

		DVDCloseFile(dvdread_vts_file);

		if(vts_ifo)
			ifoClose(vts_ifo);

		if(vmg_ifo)
			ifoClose(vmg_ifo);

		if(dvdread_dvd)
			DVDClose(dvdread_dvd);

		return 1;

	}

	// DVD playback using libmpv
	dvd_mpv = mpv_create();

	// Terminal output
	mpv_set_option_string(dvd_mpv, "terminal", "yes");
	mpv_set_option_string(dvd_mpv, "term-osd-bar", "yes");
	if(debug) {
		mpv_request_log_messages(dvd_mpv, "debug");
	} else if(verbose) {
		mpv_request_log_messages(dvd_mpv, "v");
	} else {
		mpv_request_log_messages(dvd_mpv, "none");
		// Skip "[ffmpeg/audio] ac3: frame sync error" which are normal when seeking on DVDs
		mpv_set_option_string(dvd_mpv, "msg-level", "ffmpeg/audio=none");
	}

	// mpv zero-indexes tracks
	snprintf(dvd_mpv_args, 13, "dvdread://%u", dvd_playback.track - 1);

	// MPV uses zero-indexing for tracks, dvd_info uses one instead
	const char *dvd_mpv_commands[] = { "loadfile", dvd_mpv_args, NULL };

	// Load user's mpv configuration in ~/.config/dvd_player/mpv.conf (and friends)
	if(strlen(dvd_player.mpv_config_dir) > 0) {
		mpv_set_option_string(dvd_mpv, "config-dir", dvd_player.mpv_config_dir);
		mpv_set_option_string(dvd_mpv, "config", "yes");
	}

	// When choosing a chapter range, mpv will add 1 to the last one requested
	snprintf(dvd_playback.mpv_chapters_range, 8, "%u-%u", dvd_playback.first_chapter, dvd_playback.last_chapter + 1);

	// Playback options and default configuration
	mpv_set_option_string(dvd_mpv, "dvd-device", device_filename);
	if(strlen(dvd_info.title) > 0)
		mpv_set_option_string(dvd_mpv, "title", dvd_info.title);
	else
		mpv_set_option_string(dvd_mpv, "title", "dvd_player");
	mpv_set_option_string(dvd_mpv, "chapter", dvd_playback.mpv_chapters_range);
	mpv_set_option_string(dvd_mpv, "input-default-bindings", "yes");
	mpv_set_option_string(dvd_mpv, "input-vo-keyboard", "yes");
	if(strlen(dvd_playback.audio_aid) > 0)
		mpv_set_option_string(dvd_mpv, "aid", dvd_playback.audio_aid);
	else if(strlen(dvd_playback.audio_lang) > 0)
		mpv_set_option_string(dvd_mpv, "alang", dvd_playback.audio_lang);
	if(dvd_playback.subtitles && strlen(dvd_playback.subtitles_sid) > 0)
		mpv_set_option_string(dvd_mpv, "sid", dvd_playback.subtitles_sid);
	else if(dvd_playback.subtitles && strlen(dvd_playback.subtitles_lang) > 0)
		mpv_set_option_string(dvd_mpv, "slang", dvd_playback.subtitles_lang);
	if(dvd_playback.fullscreen)
		mpv_set_option_string(dvd_mpv, "fullscreen", NULL);
	if(dvd_playback.deinterlace)
		mpv_set_option_string(dvd_mpv, "deinterlace", "yes");
	if(opt_no_video)
		mpv_set_option_string(dvd_mpv, "video", "no");
	if(opt_no_audio)
		mpv_set_option_string(dvd_mpv, "audio", "no");

	// start mpv
	mpv_initialize(dvd_mpv);
	mpv_command(dvd_mpv, dvd_mpv_commands);

	while(true) {

		dvd_mpv_event = mpv_wait_event(dvd_mpv, -1);

		// Goodbye :)
		if(dvd_mpv_event->event_id == MPV_EVENT_SHUTDOWN || dvd_mpv_event->event_id == MPV_EVENT_END_FILE)
			break;

		if(debug && dvd_mpv_event->event_id != MPV_EVENT_LOG_MESSAGE)
			printf("dvd_player [mpv_event_name]: %s\n", mpv_event_name(dvd_mpv_event->event_id));

		// Logging output
		if((verbose || debug) && dvd_mpv_event->event_id == MPV_EVENT_LOG_MESSAGE) {
			dvd_mpv_log_message = (struct mpv_event_log_message *)dvd_mpv_event->data;
			printf("mpv [%s]: %s", dvd_mpv_log_message->level, dvd_mpv_log_message->text);
		}

	}

	mpv_terminate_destroy(dvd_mpv);

	DVDCloseFile(dvdread_vts_file);

	if(vts_ifo)
		ifoClose(vts_ifo);
	
	if(vmg_ifo)
		ifoClose(vmg_ifo);

	if(dvdread_dvd)
		DVDClose(dvdread_dvd);
	
	return 0;

}
Example #19
0
PlayEngine::PlayEngine()
: d(new Data(this)) {
    Q_ASSERT(d->confDir.isValid());

    _Debug("Create audio/video plugins");
    d->audio = new AudioController(this);
    d->video = new VideoOutput(this);
    d->filter = new VideoFilter;

    d->chapterInfo = new ChapterInfoObject(this, this);
    d->updateMediaName();

    _Debug("Make registrations and connections");

    connect(d->video, &VideoOutput::formatChanged,
            this, &PlayEngine::updateVideoFormat);
    connect(d->video, &VideoOutput::droppedFramesChanged,
            this, &PlayEngine::droppedFramesChanged);

    d->handle = mpv_create();
    auto verbose = qgetenv("CMPLAYER_MPV_VERBOSE").toLower().trimmed();
    const QVector<QByteArray> lvs = {"no", "fatal", "error", "warn", "info",
                                     "status", "v", "debug", "trace"};
    if (lvs.indexOf(verbose) < lvs.indexOf("info"))
        verbose = "info";
    mpv_request_log_messages(d->handle, verbose.constData());

    d->observe();
    connect(this, &PlayEngine::beginChanged, this, &PlayEngine::endChanged);
    connect(this, &PlayEngine::durationChanged, this, &PlayEngine::endChanged);
    connect(this, &PlayEngine::videoStreamsChanged, this, [=] () {
        if (_Change(d->hasVideo, !d->streams[StreamVideo].tracks.isEmpty()))
            emit hasVideoChanged();
        d->videoInfo.setTracks(d->streams[StreamVideo].tracks);
    });
    connect(this, &PlayEngine::audioStreamsChanged, this, [=] ()
        { d->audioInfo.setTracks(d->streams[StreamAudio].tracks); });
    connect(this, &PlayEngine::subtitleStreamsChanged, this, [=] ()
        { d->subInfo.setTracks(d->streams[StreamSubtitle].tracks); });
    connect(this, &PlayEngine::currentVideoStreamChanged, this, [=] (int id)
        { d->videoInfo.setTrack(d->streams[StreamVideo].tracks.value(id)); });
    connect(this, &PlayEngine::currentAudioStreamChanged, this, [=] (int id)
        { d->audioInfo.setTrack(d->streams[StreamAudio].tracks.value(id)); });
    connect(this, &PlayEngine::currentSubtitleStreamChanged, this, [=] ()
        { d->subInfo.setTracks(d->streams[StreamSubtitle].tracks); });
    auto checkDeint = [=] () {
        auto act = Unavailable;
        if (d->filter->isInputInterlaced())
            act = d->filter->isOutputInterlaced() ? Deactivated : Activated;
        d->videoInfo.setDeinterlacer(act);
    };
    connect(d->filter, &VideoFilter::inputInterlacedChanged,
            this, checkDeint, Qt::QueuedConnection);
    connect(d->filter, &VideoFilter::outputInterlacedChanged,
            this, checkDeint, Qt::QueuedConnection);
    connect(d->audio, &AudioController::inputFormatChanged, this, [=] () {
        d->audioInfo.output()->setFormat(d->audio->inputFormat());
    }, Qt::QueuedConnection);
    connect(d->audio, &AudioController::outputFormatChanged, this, [=] () {
        d->audioInfo.renderer()->setFormat(d->audio->outputFormat());
    }, Qt::QueuedConnection);
    connect(d->audio, &AudioController::samplerateChanged, this, [=] (int sr) {
        d->audioInfo.renderer()->setSampleRate(sr, true);
    }, Qt::QueuedConnection);
    connect(d->audio, &AudioController::gainChanged,
            &d->audioInfo, &AudioInfoObject::setNormalizer);
    auto setOption = [this] (const char *name, const char *data) {
        const auto err = mpv_set_option_string(d->handle, name, data);
        d->fatal(err, "Couldn't set option %%=%%.", name, data);
    };
    setOption("fs", "no");
    setOption("input-cursor", "yes");
    setOption("softvol", "yes");
    setOption("softvol-max", "1000.0");
    setOption("sub-auto", "no");
    setOption("osd-level", "0");
    setOption("quiet", "yes");
    setOption("input-terminal", "no");
    setOption("ad-lavc-downmix", "no");
    setOption("title", "\"\"");
    setOption("vo", d->vo().constData());
    setOption("fixed-vo", "yes");

    auto overrides = qgetenv("CMPLAYER_MPV_OPTIONS").trimmed();
    if (!overrides.isEmpty()) {
        const auto opts = QString::fromLocal8Bit(overrides);
        const auto args = opts.split(QRegEx(uR"([\s\t]+)"_q),
                                     QString::SkipEmptyParts);
        for (int i=0; i<args.size(); ++i) {
            if (!args[i].startsWith("--"_a)) {
                _Error("Cannot parse option %%.", args[i]);
                continue;
            }
            const auto arg = args[i].midRef(2);
            const int index = arg.indexOf('='_q);
            if (index < 0) {
                if (arg.startsWith("no-"_a))
                    setOption(arg.mid(3).toLatin1(), "no");
                else
                    setOption(arg.toLatin1(), "yes");
            } else {
                const auto key = arg.left(index).toLatin1();
                const auto value = arg.mid(index+1).toLatin1();
                setOption(key, value);
            }
        }
    }
    d->fatal(mpv_initialize(d->handle), "Couldn't initialize mpv.");
    _Debug("Initialized");
    d->initialized = true;
}