feat(windows): mpv plays audio on Windows, with libmpv shipped in the installer
libmpv on Windows (DR-237): - The builder image carries zhongfly's LGPL libmpv-2.dll, pinned by asset name and sha256, plus an MSVC mpv.lib generated from the DLL's own mpv_* exports (the archive ships only a MinGW .dll.a). LGPL, not the GPL builds: no x264/x265, mpv -Dgpl=false; FFmpeg is version3, so LGPL-3.0. THIRD_PARTY_NOTICES.md records it. - build-windows-cross.sh stages both files into src-tauri/windows-libs/; build.rs links mpv.lib from there and tauri.windows.conf.json bundles the DLL beside jellytau.exe from there, with the licence texts under licenses/. One directory, so the DLL shipped is the one linked. - Workflows move to builder image 2026.09.1. Windows audio: - MpvBackend replaces WebviewAudioBackend on Windows (ao=wasapi), so volume, EQ, normalization and gapless work there as on Linux. Local files are passed to mpv as native paths, not file:// URLs. Fixes found on the way: - player_play_item decided "does the backend render video" with cfg!(not(linux)), true on Windows, while get_player_status sent Windows video to the <video> element. With mpv as the backend that would decode every film's soundtrack twice. All three callers now ask video_renders_natively() (UT-273). - confine_queued_path rebuilt paths with PathBuf::push, so on Windows a queued `downloads/x` was stored as `downloads\x`, no longer the spelling the app built. It now keeps the caller's separator (UT-205, which only ever ran on Linux, failed under Windows). Verified: jellytau.exe imports libmpv-2.dll; the full unit suite cross-compiled for Windows passes under wine against the shipped DLL (943/943, including the mpv injection and TLS tests); the NSIS installer contains the DLL and licence texts. Not yet run on real Windows hardware.
This commit is contained in:
@@ -9,7 +9,6 @@ use crate::utils::conversions::{seconds_to_ticks, volume_to_percent};
|
||||
use crate::utils::lock::MutexSafe;
|
||||
use libmpv::Mpv;
|
||||
use log::{debug, error, info, warn};
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
@@ -54,8 +53,21 @@ struct InternalState {
|
||||
volume: f32,
|
||||
}
|
||||
|
||||
/// Detect which audio system is available on the system
|
||||
/// The audio output mpv should use on Windows: WASAPI, the only one it ships
|
||||
/// there. Nothing to probe — and spawning `pactl` from a GUI app on Windows
|
||||
/// would at best fail and at worst flash a console window.
|
||||
///
|
||||
/// TRACES: UR-004 | DR-237
|
||||
#[cfg(target_os = "windows")]
|
||||
fn detect_audio_system() -> String {
|
||||
"wasapi".to_string()
|
||||
}
|
||||
|
||||
/// Detect which audio system is available on the system
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn detect_audio_system() -> String {
|
||||
use std::process::Command;
|
||||
|
||||
info!("[MpvBackend] Detecting audio system...");
|
||||
|
||||
// Try PulseAudio/PipeWire first (most common on modern Linux)
|
||||
@@ -95,6 +107,13 @@ fn detect_audio_system() -> String {
|
||||
fn get_stream_url(media: &MediaItem) -> String {
|
||||
match &media.source {
|
||||
MediaSource::Remote { stream_url, .. } => stream_url.clone(),
|
||||
// A Windows path is not a URL (`file://C:\...` is malformed); mpv
|
||||
// takes the native path as it is. Safe to pass verbatim because it goes
|
||||
// to mpv as one argv element (DR-298), not through a command string.
|
||||
// TRACES: UR-004, UR-071 | DR-237
|
||||
MediaSource::Local { file_path, .. } if cfg!(target_os = "windows") => {
|
||||
file_path.to_string_lossy().into_owned()
|
||||
}
|
||||
MediaSource::Local { file_path, .. } => {
|
||||
format!("file://{}", file_path.to_string_lossy())
|
||||
}
|
||||
@@ -121,6 +140,8 @@ static MPV_HANDLE: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
|
||||
/// can fail, and the app falls back to a no-op backend rather than dying).
|
||||
///
|
||||
/// TRACES: UR-080 | DR-231
|
||||
// Only the Linux video surface reads it until Windows gets one (DR-237).
|
||||
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
pub fn registered_handle() -> *mut libmpv_sys::mpv_handle {
|
||||
MPV_HANDLE
|
||||
.get()
|
||||
|
||||
Reference in New Issue
Block a user