void PlayerComponent::queueMedia(const QString& url, const QVariantMap& options, const QVariantMap &metadata, const QString& audioStream, const QString& subtitleStream) { m_mediaFrameRate = metadata["frameRate"].toFloat(); // returns 0 on failure updateVideoSettings(); QVariantList command; command << "loadfile" << url; command << "append-play"; // if nothing is playing, play it now, otherwise just enqueue it QVariantMap extraArgs; quint64 startMilliseconds = options["startMilliseconds"].toLongLong(); if (startMilliseconds != 0) extraArgs.insert("start", "+" + QString::number(startMilliseconds / 1000.0)); // detect subtitles if (!subtitleStream.isEmpty()) { // If the stream title starts with a #, then it's an index if (subtitleStream.startsWith("#")) extraArgs.insert("ff-sid", subtitleStream.mid(1)); else extraArgs.insert("sub-file", subtitleStream); } else { // no subtitles, tell mpv to ignore them. extraArgs.insert("sid", "no"); } if (metadata["type"] == "music") extraArgs.insert("vid", "no"); // and then the audio stream if (!audioStream.isEmpty()) extraArgs.insert("ff-aid", audioStream); extraArgs.insert("pause", options["autoplay"].toBool() ? "no" : "yes"); QString userAgent = metadata["headers"].toMap()["User-Agent"].toString(); if (userAgent.size()) extraArgs.insert("user-agent", userAgent); command << extraArgs; QLOG_DEBUG() << command; mpv::qt::command_variant(m_mpv, command); }
bool PlayerComponent::switchDisplayFrameRate() { QLOG_DEBUG() << "Video framerate:" << m_mediaFrameRate << "fps"; if (!SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "refreshrate.auto_switch").toBool()) { QLOG_DEBUG() << "Not switching refresh-rate (disabled by settings)."; return false; } bool fs = SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fullscreen").toBool(); #if KONVERGO_OPENELEC fs = true; #endif if (!fs) { QLOG_DEBUG() << "Not switching refresh-rate (not in fullscreen mode)."; return false; } if (m_mediaFrameRate < 1) { QLOG_DEBUG() << "Not switching refresh-rate (no known video framerate)."; return false; } // Make sure a timer started by the previous file ending isn't accidentally // still in-flight. It could switch the display back after we've switched. m_restoreDisplayTimer.stop(); DisplayComponent* display = &DisplayComponent::Get(); if (!display->switchToBestVideoMode(m_mediaFrameRate)) { QLOG_DEBUG() << "Switching refresh-rate failed or unnecessary."; return false; } // Make sure settings dependent on the display refresh rate are updated properly. updateVideoSettings(); return true; }
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; }
void PlayerComponent::onRefreshRateChange() { // Make sure settings dependent on the display refresh rate are updated properly. updateVideoSettings(); }