Compare commits

..

No commits in common. "master" and "pre-1.7-merge-20260827" have entirely different histories.

11 changed files with 340 additions and 324 deletions

2
.gitignore vendored
View file

@ -10,4 +10,4 @@
!/vendor/iced_video_player/
!/vendor/iced_video_player/**
/vendor.tar
.idea/
.idea/

514
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-player"
version = "1.8.0"
version = "1.6.0"
edition = "2024"
rust-version = "1.93"

12
debian/changelog vendored
View file

@ -1,15 +1,3 @@
cosmic-player (1.8.0) noble; urgency=medium
* Epoch 1.8.0 version update
-- Jeremy Soller <jeremy@system76.com> Wed, 02 Sep 2026 08:15:23 -0600
cosmic-player (1.7.0) noble; urgency=medium
* Epoch 1.7.0 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 25 Aug 2026 09:20:51 -0600
cosmic-player (1.6.0) noble; urgency=medium
* Epoch 1.6.0 version update

View file

@ -9,28 +9,3 @@ settings = Indstillinger
open-file = Åbn fil
open-folder = Åbn mappe
file = Fil
album = Album: { $album }
audio = Lyd
no-video-or-audio-file-open = Ingen video- eller lydfil åben
off = Fra
subtitles = Undertekster
unknown-author = Ukendt Forfatter
untitled = Utituleret
open-media = Åbn medie...
open-recent-media = Åbn seneste medie
clear-recent = Ryd seneste-liste
open-media-folder = Åbn mediemappe...
open-recent-media-folder = Åbn seneste mediemappe
close-media-folder = Luk mediemappe
repeat-disabled = Gentagelse deaktiveret
repeat-track = Gentag nummer
playback = Afspilning
next-frame = Næste billede
previous-frame = Forrige billede
ab-repeat = A-B-gentagelse
ab-repeat-set-a = A-B-gentagelse (A)
ab-repeat-set-b = A-B-gentagelse (B)
ab-repeat-clear = Ryd A-B-gentagelse
xdg-name = COSMIC Medieafspiller
xdg-comment = Medieafspiller til COSMIC-skrivebordet
xdg-keywords = Lyd;Film;Film;Musik;Lyd;Video;

View file

@ -2,13 +2,13 @@ open-file = Ldi afaylu
open-folder = Ldi akaram
repeat-disabled = Allus yensa
repeat-track = Ales tazribt
quit = Ffeɣ
quit = Tuffɣa
settings = Iɣewwaṛen
appearance = Timeẓri
theme = Asentel
dark = Aɣmayan
light = Aceɛlal
match-desktop = Semṣada akked tnarit
match-desktop = Amṣada d tnarit
file = Afaylu
close-file = Mdel afaylu
album = Album: { $album }

View file

@ -1,4 +1,4 @@
settings = Configuracion
settings = Paramètres
album = Album : { $album }
audio = Àudio
no-video-or-audio-file-open = Cap de fichièr vidèo o àudio pas dobèrt
@ -9,7 +9,7 @@ untitled = Sens títol
appearance = Aparéncia
theme = Tèma
match-desktop = Assortir al burèu
dark = Escur
dark = Fosc
light = Clar
file = Fichièr
open-media = Dorbir mèdia...
@ -31,4 +31,3 @@ ab-repeat-clear = Escafar la repeticion A-B
xdg-name = Lector multimèdia COSMIC
xdg-comment = Lector multimèdia pel burèu COSMIC
xdg-keywords = Àudio;Musica;Vidèo;Film;Son;
quit = Quitar

View file

@ -1,4 +0,0 @@
settings = การตั้งค่า
dark = มืด
light = สว่าง
appearance = ลักษณะ

View file

@ -82,32 +82,7 @@ fn get_framerate(video: &Video) -> Option<f64> {
let caps = pad.current_caps()?;
let structure = caps.structure(0)?;
let framerate = structure.get::<gst::Fraction>("framerate").ok()?;
positive_framerate(framerate)
}
fn positive_framerate(framerate: gst::Fraction) -> Option<f64> {
if framerate.numer() <= 0 || framerate.denom() <= 0 {
return None;
}
let fps = framerate.numer() as f64 / framerate.denom() as f64;
fps.is_finite().then_some(fps)
}
#[cfg(test)]
mod tests {
use super::positive_framerate;
use iced_video_player::gst;
#[test]
fn variable_framerate_uses_fallback() {
assert_eq!(positive_framerate(gst::Fraction::new(0, 1)), None);
}
#[test]
fn fixed_framerate_is_preserved() {
assert_eq!(positive_framerate(gst::Fraction::new(30, 1)), Some(30.0));
}
Some(framerate.numer() as f64 / framerate.denom() as f64)
}
/// Runs application with these settings

View file

@ -4,40 +4,6 @@ use iced_video_player::{Video, gst, gst_app, gst_pbutils};
use cosmic::action;
use cosmic::app::Task;
const SUSPICIOUS_AUDIO_GAP_DURATION: gst::ClockTime = gst::ClockTime::SECOND;
fn is_suspicious_audio_gap(duration: Option<gst::ClockTime>) -> bool {
duration.is_some_and(|duration| duration >= SUSPICIOUS_AUDIO_GAP_DURATION)
}
fn suppress_suspicious_audio_gaps(audio_filter: &gst::Element) {
let Some(src_pad) = audio_filter.static_pad("src") else {
log::warn!("audio filter has no source pad; malformed audio gaps will not be suppressed");
return;
};
let _ = src_pad.add_probe(gst::PadProbeType::EVENT_DOWNSTREAM, |_, info| {
let Some(event) = info.event() else {
return gst::PadProbeReturn::Ok;
};
let gst::EventView::Gap(gap) = event.view() else {
return gst::PadProbeReturn::Ok;
};
let (timestamp, duration) = gap.get();
if !is_suspicious_audio_gap(duration) {
return gst::PadProbeReturn::Ok;
}
// Some malformed MP4 edit lists cause streamsynchronizer to emit a
// long GAP before almost every audio buffer. The next buffer's PTS is
// still continuous, so forwarding those events makes audio sinks
// repeatedly wait or resynchronise and produces choppy playback.
log::debug!("suppressing audio GAP at {timestamp} with duration {duration:?}");
drop(info.take_event());
gst::PadProbeReturn::Handled
});
}
#[derive(Debug, Default)]
pub struct VideoSettings {
pub mute: bool,
@ -67,14 +33,11 @@ pub fn new_video(
.downcast::<gst::Pipeline>()
.map_err(|_| iced_video_player::Error::Cast)
.unwrap();
let audio_filter = gst::ElementFactory::make("scaletempo")
.build()
.unwrap_or_else(|_| {
log::warn!("scaletempo element not available; speed changes will affect pitch");
gst::ElementFactory::make("identity").build().unwrap()
});
suppress_suspicious_audio_gaps(&audio_filter);
pipeline.set_property("audio-filter", &audio_filter);
if let Ok(scaletempo) = gst::ElementFactory::make("scaletempo").build() {
pipeline.set_property("audio-filter", &scaletempo);
} else {
log::warn!("scaletempo element not available; speed changes will affect pitch");
}
pipeline.connect("element-setup", false, |vals| {
let Ok(elem) = vals[1].get::<gst::Element>() else {
return None;
@ -148,25 +111,3 @@ fn disable_broken_hardware_decoders() {
}
}
}
#[cfg(test)]
mod tests {
use super::is_suspicious_audio_gap;
use iced_video_player::gst;
#[test]
fn long_audio_gap_is_suspicious() {
assert!(is_suspicious_audio_gap(Some(gst::ClockTime::SECOND)));
assert!(is_suspicious_audio_gap(Some(
gst::ClockTime::from_seconds(3)
)));
}
#[test]
fn short_or_unknown_audio_gap_is_preserved() {
assert!(!is_suspicious_audio_gap(Some(
gst::ClockTime::from_mseconds(999)
)));
assert!(!is_suspicious_audio_gap(None));
}
}