From 43f8c922532f5c283fe5fa1778b904abcafb3078 Mon Sep 17 00:00:00 2001 From: Chris Cochrun Date: Mon, 3 Apr 2023 14:57:23 -0500 Subject: [PATCH] add image_model file and video_thumbnail Neither of these are used yet, but it's time to start added the code --- src/rust/image_model.rs | 26 ++++++++ src/rust/video_thumbnail.rs | 128 ++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/rust/image_model.rs create mode 100644 src/rust/video_thumbnail.rs diff --git a/src/rust/image_model.rs b/src/rust/image_model.rs new file mode 100644 index 0000000..35564ba --- /dev/null +++ b/src/rust/image_model.rs @@ -0,0 +1,26 @@ +#[cxx_qt::bridge] +mod image_model { + unsafe extern "C++" { + include!(< QAbstractListModel >); + include!("cxx-qt-lib/qhash.h"); + type QHash_i32_QByteArray = cxx_qt_lib::QHash; + include!("cxx-qt-lib/qmap.h"); + type QMap_QString_QVariant = cxx_qt_lib::QMap; + include!("cxx-qt-lib/qvariant.h"); + type QVariant = cxx_qt_lib::QVariant; + include!("cxx-qt-lib/qstring.h"); + type QString = cxx_qt_lib::QString; + include!("cxx-qt-lib/qmodelindex.h"); + type QModelIndex = cxx_qt_lib::QModelIndex; + include!("cxx-qt-lib/qvector.h"); + type QVector_i32 = cxx_qt_lib::QVector; + include!("cxx-qt-lib/qstringlist.h"); + type QStringList = cxx_qt_lib::QStringList; + include!("cxx-qt-lib/qlist.h"); + type QList_QString = cxx_qt_lib::QList; + // #[cxx_name = "Slidey"] + // type CxxSlidey = super::qobject::Slidey; + // include!("cxx-qt-lib/qvector.h"); + // type QVector_Slidey = cxx_qt_lib::QVector; + } +} diff --git a/src/rust/video_thumbnail.rs b/src/rust/video_thumbnail.rs new file mode 100644 index 0000000..b2e0a2e --- /dev/null +++ b/src/rust/video_thumbnail.rs @@ -0,0 +1,128 @@ +use ffmpeg_next::ffi::AV_TIME_BASE; +use ffmpeg_next::software::scaling::flag::BICUBIC; +use ffmpeg_next::{decoder, format, frame, media, software::scaling, Error as FFMpegError}; +use image::{ImageBuffer, Rgba}; +use std::error::Error; +use std::path::{Path, PathBuf}; +use std::time::Instant; + +pub struct ImageIter { + input: format::context::Input, + stream_index: usize, + decoder: decoder::Video, + context: Option, +} + +impl ImageIter { + pub fn new>(path: I) -> Result> { + let input = format::input(&path)?; + + if let Some(stream) = input.streams().best(media::Type::Video) { + let stream_index = stream.index(); + + if let Ok(video) = stream.codec().decoder().video() { + println!("Video width:{}", video.width()); + println!("Video format:{:?}", video.format()); + println!("Video height:{}", video.height()); + + let mut context = None; + + if video.format() != format::Pixel::RGBA { + println!("Need to convert to RGBA"); + + context = Some(scaling::Context::get( + video.format(), + video.width(), + video.height(), + format::Pixel::RGBA, + video.width(), + video.height(), + BICUBIC, + )?); + } + + return Ok(Self { + input, + stream_index, + decoder: video, + context, + }); + } + } + + return Err(Box::new(FFMpegError::Unknown)); + } + + pub fn seek(&mut self, time_s: f64) -> Result<(), Box> { + let timestamp = (time_s * AV_TIME_BASE as f64) as i64; + + self.input.seek(timestamp, timestamp..)?; + + Ok(()) + } +} +pub fn get_ms(now: Instant) -> f32 { + let duration = now.elapsed(); + let nanos = duration.subsec_nanos() as f32; + (1000000000f32 * duration.as_secs() as f32 + nanos) / (1000000f32) +} + +pub impl Iterator for ImageIter { + type Item = Result, Vec>, Box>; + + fn next(&mut self) -> Option { + let imagetime = Instant::now(); + let mut output = frame::Video::empty(); + + while let Some((stream, packet)) = self.input.packets().next() { + if stream.index() != self.stream_index { + continue; + } + + let decodetime = Instant::now(); + + // if let Err(err) = self.decoder.decode(&packet, &mut output) { + // return Some(Err(Box::new(err))); + // } + + if let Err(err) = self.decoder.send_packet(&packet) { + return Some(Err(Box::new(err))); + } + + if let Err(err) = self.decoder.receive_frame(&mut output) { + return Some(Err(Box::new(err))); + } + + println!("Image decode time: {}", get_ms(decodetime)); + + if output.format() == format::Pixel::None { + println!("Skipping null format frame"); + continue; + } + + if let Some(ref mut context) = self.context { + let convert_time = Instant::now(); + let mut new_output = frame::Video::empty(); + + if let Err(err) = context.run(&output, &mut new_output) { + return Some(Err(Box::new(err))); + } + + println!("Image convert time: {}", get_ms(convert_time)); + + output = new_output; + } + + let data = output.data(0).to_vec(); + + if let Some(image) = + ImageBuffer::, _>::from_raw(output.width(), output.height(), data) + { + println!("Image read time: {}", get_ms(imagetime)); + return Some(Ok(image)); + } + } + + None + } +}