Fix cosmic-player file launch and video playback
Some checks failed
Validate .desktop files / validate (push) Has been cancelled

This commit is contained in:
Lionel DARNIS 2026-07-09 11:51:07 +02:00
parent 4a1bfc975c
commit e7781986f5
5 changed files with 67 additions and 22 deletions

View file

@ -1,5 +1,5 @@
use vergen::EmitBuilder;
use std::{env, fs, path::PathBuf};
use vergen::EmitBuilder;
use xdgen::{App, Context, FluentString};
fn main() {

View file

@ -5,7 +5,7 @@ Exec=cosmic-player %U
Terminal=false
Type=Application
StartupNotify=true
MimeType=application/mxf;application/ram;application/sdp;application/vnd.apple.mpegurl;application/vnd.ms-asf;application/vnd.ms-wpl;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;application/x-extension-m4a;application/x-extension-mp4;application/x-flash-video;application/x-matroska;application/x-netshow-channel;application/x-quicktimeplayer;application/x-shorten;image/vnd.rn-realpix;image/x-pict;misc/ultravox;text/x-google-video-pointer;video/3gp;video/3gpp;video/3gpp2;video/dv;video/divx;video/fli;video/flv;video/mp2t;video/mp4;video/mp4v-es;video/mpeg;video/mpeg-system;video/msvideo;video/ogg;video/quicktime;video/vivo;video/vnd.divx;video/vnd.mpegurl;video/vnd.rn-realvideo;video/vnd.vivo;video/webm;video/x-anim;video/x-avi;video/x-flc;video/x-fli;video/x-flic;video/x-flv;video/x-m4v;video/x-matroska;video/x-mjpeg;video/x-mpeg;video/x-mpeg2;video/x-ms-asf;video/x-ms-asf-plugin;video/x-ms-asx;video/x-msvideo;video/x-ms-wm;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvx;video/x-nsv;video/x-ogm+ogg;video/x-theora;video/x-theora+ogg;video/x-totem-stream;audio/x-pn-realaudio;application/smil;application/smil+xml;application/x-quicktime-media-link;application/x-smil;text/google-video-pointer;x-content/video-dvd;x-scheme-handler/pnm;x-scheme-handler/mms;x-scheme-handler/net;x-scheme-handler/rtp;x-scheme-handler/rtmp;x-scheme-handler/rtsp;x-scheme-handler/mmsh;x-scheme-handler/uvox;x-scheme-handler/icy;x-scheme-handler/icyx;
MimeType=application/mxf;application/ram;application/sdp;application/vnd.apple.mpegurl;application/vnd.ms-asf;application/vnd.ms-wpl;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;application/x-extension-m4a;application/x-extension-mp4;application/x-flash-video;application/x-matroska;application/x-netshow-channel;application/x-quicktimeplayer;application/x-shorten;image/vnd.rn-realpix;image/x-pict;misc/ultravox;text/x-google-video-pointer;video/3gp;video/3gpp;video/3gpp2;video/dv;video/divx;video/fli;video/flv;video/matroska;video/mp2t;video/mp4;video/mp4v-es;video/mpeg;video/mpeg-system;video/msvideo;video/ogg;video/quicktime;video/vivo;video/vnd.divx;video/vnd.mpegurl;video/vnd.rn-realvideo;video/vnd.vivo;video/webm;video/x-anim;video/x-avi;video/x-flc;video/x-fli;video/x-flic;video/x-flv;video/x-m4v;video/x-matroska;video/x-mjpeg;video/x-mpeg;video/x-mpeg2;video/x-ms-asf;video/x-ms-asf-plugin;video/x-ms-asx;video/x-msvideo;video/x-ms-wm;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvx;video/x-nsv;video/x-ogm+ogg;video/x-theora;video/x-theora+ogg;video/x-totem-stream;audio/x-pn-realaudio;application/smil;application/smil+xml;application/x-quicktime-media-link;application/x-smil;text/google-video-pointer;x-content/video-dvd;x-scheme-handler/pnm;x-scheme-handler/mms;x-scheme-handler/net;x-scheme-handler/rtp;x-scheme-handler/rtmp;x-scheme-handler/rtsp;x-scheme-handler/mmsh;x-scheme-handler/uvox;x-scheme-handler/icy;x-scheme-handler/icyx;
Icon=com.system76.CosmicPlayer
Categories=COSMIC;AudioVideo;Player;Video;
Keywords=Audio;Film;Movie;Music;Sound;Video;

View file

@ -8,10 +8,10 @@ use cosmic::iced::event::{self, Event};
use cosmic::iced::keyboard::key::Physical;
use cosmic::iced::keyboard::{Event as KeyEvent, Key, Modifiers};
use cosmic::iced::mouse::{Event as MouseEvent, ScrollDelta};
use cosmic::iced::window::set_mode;
use cosmic::iced::{
Alignment, Background, Border, Color, ContentFit, Length, Limits, Subscription, exit, window,
};
use cosmic::iced::window::set_mode;
use cosmic::widget::menu::action::MenuAction;
use cosmic::widget::{self, Slider, about::About, nav_bar, segmented_button};
use cosmic::{Application, ApplicationExt, Element, action, cosmic_theme, executor, font, theme};
@ -107,16 +107,6 @@ fn main() -> Result<(), Box<dyn Error>> {
}
}
#[cfg(all(unix, not(target_os = "redox")))]
match fork::daemon(true, true) {
Ok(fork::Fork::Child) => (),
Ok(fork::Fork::Parent(_child_pid)) => process::exit(0),
Err(err) => {
eprintln!("failed to daemonize: {:?}", err);
process::exit(1);
}
}
localize::localize();
let config = match cosmic_config::Config::new(App::APP_ID, CONFIG_VERSION) {
@ -169,6 +159,18 @@ fn main() -> Result<(), Box<dyn Error>> {
Ok(())
}
fn stream_tags(pipeline: &gst::Pipeline, signal: &str, stream: i32) -> Option<gst::TagList> {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
pipeline.emit_by_name::<Option<gst::TagList>>(signal, &[&stream])
})) {
Ok(tags) => tags,
Err(_) => {
log::warn!("failed to read {signal} for stream {stream}");
None
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Action {
About,
@ -417,14 +419,15 @@ impl App {
let n_video = pipeline.property::<i32>("n-video");
for i in 0..n_video {
let tags: gst::TagList = pipeline.emit_by_name("get-video-tags", &[&i]);
if let Some(tags) = stream_tags(&pipeline, "get-video-tags", i) {
log::info!("video stream {i}: {tags:#?}");
}
}
let n_audio = pipeline.property::<i32>("n-audio");
self.audio_codes = Vec::with_capacity(n_audio as usize);
for i in 0..n_audio {
let tags: gst::TagList = pipeline.emit_by_name("get-audio-tags", &[&i]);
let tags = stream_tags(&pipeline, "get-audio-tags", i).unwrap_or_default();
log::info!("audio stream {i}: {tags:#?}");
self.audio_codes
.push(if let Some(title) = tags.get::<gst::tags::Title>() {
@ -446,7 +449,7 @@ impl App {
name: fl!("off"),
});
for i in 0..n_text {
let tags: gst::TagList = pipeline.emit_by_name("get-text-tags", &[&i]);
let tags = stream_tags(&pipeline, "get-text-tags", i).unwrap_or_default();
log::info!("text stream {i}: {tags:#?}");
let name = if let Some(title) = tags.get::<gst::tags::Title>() {
title.get().to_string()

View file

@ -16,11 +16,17 @@ pub fn new_video(
//TODO: this code came from iced_video_player::Video::new and has been modified to stop the pipeline on error
//TODO: remove unwraps and enable playback of files with only audio.
gst::init().unwrap();
disable_broken_hardware_decoders();
let pipeline = format!(
"playbin uri=\"{}\"{} video-sink=\"videoscale ! videoconvert ! videoflip method=automatic ! appsink name=iced_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1\"",
"playbin uri=\"{}\"{}{} video-sink=\"videoscale ! videoconvert ! videoflip method=automatic ! appsink name=iced_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1\"",
url.as_str(),
if settings.mute { " mute=true" } else { "" }
if settings.mute { " mute=true" } else { "" },
if settings.mute {
" audio-sink=fakesink"
} else {
""
}
);
let pipeline = gst::parse::launch(pipeline.as_ref())
.unwrap()
@ -84,3 +90,24 @@ pub fn new_video(
}
}
}
fn disable_broken_hardware_decoders() {
let registry = gst::Registry::get();
for decoder in [
"nvav1dec",
"nvh264dec",
"nvh265dec",
"nvjpegdec",
"nvmpeg2videodec",
"nvmpeg4videodec",
"nvmpegvideodec",
"nvvp8dec",
"nvvp9dec",
] {
if let Some(feature) = registry.lookup_feature(decoder) {
let rank = feature.rank();
feature.set_rank(gst::Rank::NONE);
log::debug!("disabled GStreamer decoder {decoder} with rank {rank:?}");
}
}
}

View file

@ -277,9 +277,21 @@ impl Video {
// wait for up to 5 seconds until the decoder gets the source capabilities
pipeline.state(gst::ClockTime::from_seconds(5)).0?;
let mut initial_sample = None;
let caps = pad.current_caps().or_else(|| {
let sample = video_sink
.try_pull_preroll(gst::ClockTime::from_seconds(2))
.or_else(|| video_sink.try_pull_sample(gst::ClockTime::from_seconds(2)))?;
let caps = sample.caps().map(|caps| caps.to_owned());
if sample.buffer().is_some() {
initial_sample = Some(sample);
}
caps
});
// extract resolution and framerate
// TODO(jazzfool): maybe we want to extract some other information too?
let (width, height, framerate, has_video) = if let Some(caps) = pad.current_caps() {
let (width, height, framerate, has_video) = if let Some(caps) = caps {
let s = caps.structure(0).ok_or(Error::Caps)?;
let width = s.get::<i32>("width").map_err(|_| Error::Caps)?;
let height = s.get::<i32>("height").map_err(|_| Error::Caps)?;
@ -313,8 +325,11 @@ impl Video {
let sync_av = pipeline.has_property("av-offset");
// NV12 = 12bpp
let frame = Arc::new(Mutex::new(Frame::new()));
let upload_frame = Arc::new(AtomicBool::new(false));
let has_initial_sample = initial_sample.is_some();
let frame = Arc::new(Mutex::new(
initial_sample.map(Frame).unwrap_or_else(Frame::new),
));
let upload_frame = Arc::new(AtomicBool::new(has_initial_sample));
let alive = Arc::new(AtomicBool::new(true));
let last_frame_time = Arc::new(Mutex::new(Instant::now()));