diff --git a/Cargo.lock b/Cargo.lock index 1b6a2bd..4ac9d36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -1379,7 +1379,7 @@ dependencies = [ [[package]] name = "egui-video" version = "0.8.0" -source = "git+https://github.com/v0l/egui-video.git?rev=e01405c9771e523a04ba36ffc5a08876ac0b274a#e01405c9771e523a04ba36ffc5a08876ac0b274a" +source = "git+https://github.com/v0l/egui-video.git?rev=bb3f6b83ba3a0619b1b9de0d4da88acb7fafd257#bb3f6b83ba3a0619b1b9de0d4da88acb7fafd257" dependencies = [ "anyhow", "atomic", @@ -1389,7 +1389,6 @@ dependencies = [ "ffmpeg-rs-raw", "log", "nom", - "url", ] [[package]] @@ -1605,11 +1604,12 @@ dependencies = [ [[package]] name = "ffmpeg-rs-raw" version = "0.1.0" -source = "git+https://git.v0l.io/Kieran/ffmpeg-rs-raw.git?rev=bc39a2ad99ad6489fa1525b9ad4fbca5dbf9cd07#bc39a2ad99ad6489fa1525b9ad4fbca5dbf9cd07" +source = "git+https://git.v0l.io/Kieran/ffmpeg-rs-raw.git?rev=8b6166f1db18ffb322a5a634d6b6deaddb79ecbf#8b6166f1db18ffb322a5a634d6b6deaddb79ecbf" dependencies = [ "anyhow", "ffmpeg-sys-the-third", "libc", + "log", "slimbox", ] diff --git a/Cargo.toml b/Cargo.toml index e8977e5..4686438 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ resvg = { version = "0.44.0", default-features = false } serde = { version = "1.0.214", features = ["derive"] } serde_with = { version = "3.11.0", features = ["hex"] } -egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "e01405c9771e523a04ba36ffc5a08876ac0b274a" } +egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "bb3f6b83ba3a0619b1b9de0d4da88acb7fafd257" } #egui-video = { path = "../egui-video" } [target.'cfg(not(target_os = "android"))'.dependencies] diff --git a/src/route/mod.rs b/src/route/mod.rs index 37bda12..4b43c00 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -1,6 +1,6 @@ use crate::app::NativeLayerOps; use crate::link::NostrLink; -use crate::login::{Login, LoginKind}; +use crate::login::Login; use crate::note_util::OwnedNote; use crate::route::home::HomePage; use crate::route::login::LoginPage; diff --git a/src/services/ffmpeg_loader.rs b/src/services/ffmpeg_loader.rs index 12d5d63..19f803d 100644 --- a/src/services/ffmpeg_loader.rs +++ b/src/services/ffmpeg_loader.rs @@ -1,6 +1,6 @@ use anyhow::Error; use egui::ColorImage; -use egui_video::ffmpeg_rs_raw::{Decoder, Demuxer, Scaler}; +use egui_video::ffmpeg_rs_raw::{get_frame_from_hw, Decoder, Demuxer, Scaler}; use egui_video::ffmpeg_sys_the_third::{av_frame_free, av_packet_free, AVPixelFormat}; use egui_video::media_player::video_frame_to_image; use std::path::PathBuf; @@ -36,25 +36,23 @@ impl FfmpegLoader { anyhow::bail!("Not a video/image"); }; let mut decode = Decoder::new(); - let rgb = AVPixelFormat::AV_PIX_FMT_RGB24; - let mut scaler = Scaler::new(rgb); + let rgb = AVPixelFormat::AV_PIX_FMT_RGBA; + let mut scaler = Scaler::new(); decode.setup_decoder(bv, None)?; let mut n_pkt = 0; loop { let (mut pkt, stream) = demuxer.get_packet()?; - if pkt.is_null() { - break; - } if (*stream).index as usize == bv.index { let frames = decode.decode_pkt(pkt, stream)?; if let Some((frame, _)) = frames.first() { - let mut frame = *frame; + let mut frame = get_frame_from_hw(*frame)?; let frame_rgb = scaler.process_frame( frame, (*frame).width as u16, (*frame).height as u16, + rgb, )?; av_frame_free(&mut frame); diff --git a/src/services/image_cache.rs b/src/services/image_cache.rs index c6fcf4b..cd728e0 100644 --- a/src/services/image_cache.rs +++ b/src/services/image_cache.rs @@ -1,9 +1,7 @@ use crate::services::ffmpeg_loader::FfmpegLoader; use crate::theme::NEUTRAL_800; use anyhow::Error; -use eframe::epaint::Color32; -use egui::load::SizedTexture; -use egui::{ColorImage, Context, Image, ImageData, SizeHint, TextureHandle, TextureOptions}; +use egui::{ColorImage, Context, Image, ImageData, TextureHandle, TextureOptions}; use itertools::Itertools; use log::{error, info}; use lru::LruCache; @@ -13,7 +11,6 @@ use sha2::{Digest, Sha256}; use std::collections::HashSet; use std::fs; use std::num::NonZeroUsize; -use std::ops::Deref; use std::path::PathBuf; use std::sync::{Arc, Mutex}; @@ -62,7 +59,7 @@ impl ImageCache { if url.ends_with(".svg") { Self::load_svg(bytes) } else { - let mut loader = FfmpegLoader::new(); + let loader = FfmpegLoader::new(); loader.load_image_bytes(url, bytes) } } @@ -138,7 +135,7 @@ impl ImageCache { } async fn load_image(ctx: &Context, path: PathBuf, key: &str) -> Option { - let mut loader = FfmpegLoader::new(); + let loader = FfmpegLoader::new(); match loader.load_image(path) { Ok(i) => Some(ctx.load_texture(key, ImageData::from(i), TextureOptions::default())), Err(e) => { @@ -153,7 +150,7 @@ impl ImageCache { use resvg::usvg::{Options, Tree}; let opt = Options::default(); - let mut rtree = Tree::from_data(svg, &opt) + let rtree = Tree::from_data(svg, &opt) .map_err(|err| err.to_string()) .map_err(|e| Error::msg(e))?; diff --git a/src/widgets/avatar.rs b/src/widgets/avatar.rs index 2a18bf5..2c6dfb9 100644 --- a/src/widgets/avatar.rs +++ b/src/widgets/avatar.rs @@ -1,47 +1,42 @@ use crate::route::RouteServices; -use crate::services::image_cache::ImageCache; use crate::services::ndb_wrapper::SubWrapper; -use egui::{vec2, Color32, Image, Pos2, Response, Rounding, Sense, Ui, Vec2, Widget}; +use egui::{vec2, Color32, Pos2, Response, Rounding, Sense, Ui, Vec2, Widget}; use nostrdb::NdbProfile; pub struct Avatar<'a> { - image: Option>, + image: Option<&'a str>, sub: Option, size: Option, + services: &'a RouteServices<'a>, } impl<'a> Avatar<'a> { - pub fn new(img: Image<'a>) -> Self { - Self { - image: Some(img), - sub: None, - size: None, - } - } - - pub fn new_optional(img: Option>) -> Self { + pub fn new_optional(img: Option<&'a str>, services: &'a RouteServices<'a>) -> Self { Self { image: img, sub: None, size: None, + services, } } - pub fn from_profile(p: &'a Option>, svc: &'a ImageCache) -> Self { - let img = p.map_or(None, |f| f.picture().map(|f| svc.load(f))); + pub fn from_profile(p: &'a Option>, services: &'a RouteServices<'a>) -> Self { + let img = p.map(|f| f.picture()).unwrap_or(None); Self { image: img, sub: None, size: None, + services, } } - pub fn pubkey(pk: &[u8; 32], svc: &'a RouteServices<'a>) -> Self { - let (p, sub) = svc.ndb.fetch_profile(svc.tx, pk); + pub fn pubkey(pk: &[u8; 32], services: &'a RouteServices<'a>) -> Self { + let (p, sub) = services.ndb.fetch_profile(services.tx, pk); Self { - image: p.and_then(|p| p.picture().map(|p| svc.img_cache.load(p))), + image: p.map(|f| f.picture()).unwrap_or(None), sub, size: None, + services, } } @@ -65,10 +60,14 @@ impl<'a> Widget for Avatar<'a> { fn ui(self, ui: &mut Ui) -> Response { let size_v = self.size.unwrap_or(40.); let size = Vec2::new(size_v, size_v); - if !ui.is_rect_visible(ui.cursor()) { + if !ui.is_visible() { return Self::placeholder(ui, size_v); } - match self.image { + match self + .image + .as_ref() + .map(|i| self.services.img_cache.load(*i)) + { Some(img) => img .fit_to_exact_size(size) .rounding(Rounding::same(size_v)) diff --git a/src/widgets/chat.rs b/src/widgets/chat.rs index 31a5830..615b90e 100644 --- a/src/widgets/chat.rs +++ b/src/widgets/chat.rs @@ -4,7 +4,7 @@ use crate::route::RouteServices; use crate::services::ndb_wrapper::{NDBWrapper, SubWrapper}; use crate::widgets::chat_message::ChatMessage; use crate::widgets::NostrWidget; -use egui::{Frame, Margin, Response, ScrollArea, Ui, Widget}; +use egui::{Frame, Margin, Response, ScrollArea, Ui}; use itertools::Itertools; use nostrdb::{Filter, Note, NoteKey, Transaction}; @@ -68,10 +68,11 @@ impl NostrWidget for Chat { ui.vertical(|ui| { ui.spacing_mut().item_spacing.y = 8.0; for ev in events - .iter() + .into_iter() .sorted_by(|a, b| a.created_at().cmp(&b.created_at())) { - ChatMessage::new(&stream, ev, services).ui(ui); + let c = ChatMessage::new(&stream, &ev, services); + ui.add(c); } }) }) diff --git a/src/widgets/chat_message.rs b/src/widgets/chat_message.rs index 7b93679..b13e402 100644 --- a/src/widgets/chat_message.rs +++ b/src/widgets/chat_message.rs @@ -57,7 +57,7 @@ impl<'a> Widget for ChatMessage<'a> { format.color = Color32::WHITE; job.append(self.ev.content(), 5.0, format.clone()); - ui.add(Avatar::from_profile(&profile, self.services.img_cache).size(24.)); + ui.add(Avatar::from_profile(&profile, self.services).size(24.)); ui.add(Label::new(job).wrap_mode(TextWrapMode::Wrap)); }) .response diff --git a/src/widgets/header.rs b/src/widgets/header.rs index 520932c..bac240d 100644 --- a/src/widgets/header.rs +++ b/src/widgets/header.rs @@ -1,10 +1,9 @@ -use crate::login::LoginKind; use crate::route::{RouteServices, Routes}; use crate::widgets::avatar::Avatar; use crate::widgets::{Button, NostrWidget}; use eframe::emath::Align; use eframe::epaint::Vec2; -use egui::{CursorIcon, Frame, Image, Layout, Margin, Response, Sense, Ui, Widget}; +use egui::{CursorIcon, Frame, Layout, Margin, Response, Sense, Ui, Widget}; pub struct Header; diff --git a/src/widgets/profile.rs b/src/widgets/profile.rs index 78179c5..3954617 100644 --- a/src/widgets/profile.rs +++ b/src/widgets/profile.rs @@ -12,6 +12,7 @@ pub struct Profile<'a> { profile: Option>, sub: Option, img_cache: &'a ImageCache, + services: &'a RouteServices<'a>, } impl<'a> Profile<'a> { @@ -24,6 +25,7 @@ impl<'a> Profile<'a> { profile: p, img_cache: services.img_cache, sub, + services, } } @@ -37,7 +39,7 @@ impl<'a> Widget for Profile<'a> { ui.horizontal(|ui| { ui.spacing_mut().item_spacing.x = 8.; - ui.add(Avatar::from_profile(&self.profile, self.img_cache).size(self.size)); + ui.add(Avatar::from_profile(&self.profile, self.services).size(self.size)); ui.add(Username::new(&self.profile, FONT_SIZE)) }) .response diff --git a/src/widgets/stream_list.rs b/src/widgets/stream_list.rs index b406902..acc6318 100644 --- a/src/widgets/stream_list.rs +++ b/src/widgets/stream_list.rs @@ -16,7 +16,7 @@ impl<'a> StreamList<'a> { pub fn new( id: egui::Id, streams: &'a NoteStore<'a>, - services: &'a RouteServices, + services: &'a RouteServices<'a>, heading: Option>, ) -> Self { Self { diff --git a/src/widgets/stream_player.rs b/src/widgets/stream_player.rs index dd376e5..de35503 100644 --- a/src/widgets/stream_player.rs +++ b/src/widgets/stream_player.rs @@ -1,5 +1,5 @@ use crate::widgets::PlaceholderRect; -use egui::{Context, Response, Ui, Vec2, Widget}; +use egui::{Context, Response, Ui, Widget}; use egui_video::{Player, PlayerControls}; pub struct StreamPlayer { diff --git a/src/widgets/stream_tile.rs b/src/widgets/stream_tile.rs index 439f037..3296d93 100644 --- a/src/widgets/stream_tile.rs +++ b/src/widgets/stream_tile.rs @@ -32,10 +32,15 @@ impl Widget for StreamEvent<'_> { let w = ui.available_width(); let h = (w / 16.0) * 9.0; - let cover = self.event.image().map(|p| self.services.img_cache.load(p)); let (response, painter) = ui.allocate_painter(Vec2::new(w, h), Sense::click()); + let cover = if ui.is_visible() { + self.event.image().map(|p| self.services.img_cache.load(p)) + } else { + None + }; + if let Some(cover) = cover.map(|c| { c.rounding(Rounding::same(12.)) .load_for_size(painter.ctx(), Vec2::new(w, h)) @@ -111,7 +116,7 @@ impl Widget for StreamEvent<'_> { }); } ui.horizontal(|ui| { - ui.add(Avatar::from_profile(&host_profile, self.services.img_cache).size(40.)); + ui.add(Avatar::from_profile(&host_profile, self.services).size(40.)); let title = RichText::new(self.event.title().unwrap_or("Untitled")) .size(16.) .color(Color32::WHITE); diff --git a/src/widgets/stream_title.rs b/src/widgets/stream_title.rs index 150f6da..b48876f 100644 --- a/src/widgets/stream_title.rs +++ b/src/widgets/stream_title.rs @@ -2,7 +2,7 @@ use crate::note_util::NoteUtil; use crate::route::RouteServices; use crate::stream_info::StreamInfo; use crate::widgets::{NostrWidget, Profile}; -use egui::{Color32, Frame, Label, Margin, Response, RichText, TextWrapMode, Ui, Widget}; +use egui::{Color32, Frame, Label, Margin, Response, RichText, TextWrapMode, Ui}; use nostrdb::Note; pub struct StreamTitle<'a> { @@ -26,7 +26,7 @@ impl<'a> NostrWidget for StreamTitle<'a> { .color(Color32::WHITE); ui.add(Label::new(title.strong()).wrap_mode(TextWrapMode::Truncate)); - Profile::new(self.event.host(), services).size(32.).ui(ui); + ui.add(Profile::new(self.event.host(), services).size(32.)); if let Some(summary) = self .event diff --git a/src/widgets/write_chat.rs b/src/widgets/write_chat.rs index 2baf406..1fca080 100644 --- a/src/widgets/write_chat.rs +++ b/src/widgets/write_chat.rs @@ -1,9 +1,9 @@ use crate::link::NostrLink; -use crate::route::{RouteAction, RouteServices}; +use crate::route::RouteServices; use crate::theme::{MARGIN_DEFAULT, NEUTRAL_900, ROUNDING_DEFAULT}; use crate::widgets::{NativeTextInput, NostrWidget}; use eframe::emath::Align; -use egui::{Frame, Image, Layout, Margin, Response, Rounding, Sense, Stroke, TextEdit, Ui, Widget}; +use egui::{Frame, Layout, Response, Sense, Ui, Widget}; use log::info; pub struct WriteChat {