Compare commits

...

4 Commits

Author SHA1 Message Date
e9238660d8 Satisfying the compiler
Some checks reported warnings
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2023-09-05 08:31:48 -06:00
bd7cc019cf Changed to static test image 2023-09-05 08:30:05 -06:00
0f3c3453ac Finished explantion comments 2023-09-05 08:29:48 -06:00
7d905adf10 code cleanup and comments 2023-09-05 08:21:17 -06:00
2 changed files with 74 additions and 45 deletions

View File

@ -1,4 +1,6 @@
#![feature(iter_array_chunks)] #![feature(iter_array_chunks)]
#![feature(error_generic_member_access)]
#![feature(provide_any)]
extern crate ffmpeg_next as ffmpeg; extern crate ffmpeg_next as ffmpeg;
@ -8,7 +10,6 @@ use ffmpeg::media::Type;
use ffmpeg::software::scaling::{context::Context, flag::Flags}; use ffmpeg::software::scaling::{context::Context, flag::Flags};
use ffmpeg::util::frame::video::Video; use ffmpeg::util::frame::video::Video;
use std::path::Path; use std::path::Path;
use std::time::Instant;
mod out; mod out;
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
@ -17,14 +18,13 @@ fn main() -> Result<(), Error> {
ffmpeg::init()?; ffmpeg::init()?;
// read the input file // read the input file
if let Ok(mut ictx) = input(&Path::new("output.mkv")) { if let Ok(mut ictx) = input(&Path::new("rgb.png")) {
let input = ictx let input = ictx
.streams() .streams()
.best(Type::Video) .best(Type::Video)
.ok_or(ffmpeg::Error::StreamNotFound)?; .ok_or(ffmpeg::Error::StreamNotFound)?;
let video_stream_index = input.index(); let video_stream_index = input.index();
let fps = input.avg_frame_rate();
let context_decoder = ffmpeg::codec::context::Context::from_parameters(input.parameters())?; let context_decoder = ffmpeg::codec::context::Context::from_parameters(input.parameters())?;
// Ctx's video decoder // Ctx's video decoder
@ -52,7 +52,7 @@ fn main() -> Result<(), Error> {
// test::print_raw(&rgb_frame.data(0)); // test::print_raw(&rgb_frame.data(0));
// test::print_square(&rgb_frame.data(0), rgb_frame.width() as usize); // test::print_square(&rgb_frame.data(0), rgb_frame.width() as usize);
out::print_square(&rgb_frame, out::LinesFormat::RightLeft, video_stream_index); out::print_square(&rgb_frame, out::LinesFormat::RightLeft, video_stream_index).unwrap();
// test::small_matrix(); // test::small_matrix();
} }
Ok(()) Ok(())
@ -63,12 +63,10 @@ fn main() -> Result<(), Error> {
// Is this for multiple video streams? // Is this for multiple video streams?
if stream.index() == video_stream_index { if stream.index() == video_stream_index {
decoder.send_packet(&packet)?; decoder.send_packet(&packet)?;
let now = Instant::now();
receive_and_process_decoded_frames(&mut decoder)?; receive_and_process_decoded_frames(&mut decoder)?;
} }
} }
decoder.send_eof()?; decoder.send_eof()?;
receive_and_process_decoded_frames(&mut decoder)?; receive_and_process_decoded_frames(&mut decoder)?;
} }

View File

@ -13,62 +13,93 @@ pub enum LinesFormat {
Left, Left,
} }
#[derive(Debug)]
pub enum ParseError {
NotRGB24,
NotSquareAspectRatio,
VideoIndexOutOfBounds,
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
ParseError::NotRGB24 => "Not in RGB24 format!",
ParseError::NotSquareAspectRatio => "Not in a square (1:1) aspect ratio!",
ParseError::VideoIndexOutOfBounds => "Selected video index is not valid (Out of Bounds)!",
};
write!(f, "{s}")
}
}
impl std::error::Error for ParseError {}
/// Must use square images /// Must use square images
pub fn print_square(frame: &ffmpeg::frame::Video, lines_format: LinesFormat, video_index: usize) { /// Images must be in RGB24
if frame.format() != Pixel::RGB24 { panic!("Must use Pixel::RGB24"); } pub fn print_square(frame: &ffmpeg::frame::Video, lines_format: LinesFormat, video_index: usize) -> Result<(), ParseError> {
if frame.width() != frame.height() { panic!("Must be 1:1 aspect ratio"); } if frame.format() != Pixel::RGB24 { return Err(ParseError::NotRGB24); }
if frame.width() != frame.height() { return Err(ParseError::NotSquareAspectRatio); }
// 8 is the value hard-coded in AVFrame
if !(0..=8).contains(&video_index) { return Err(ParseError::VideoIndexOutOfBounds); }
// This will panic if it's out of bounds. let pixels_wide = frame.width() as usize;
let z = video_index;
let size = frame.width() as usize;
// How many bytes per pixel RGBA
let linesize = unsafe { std::ptr::addr_of!((*frame.as_ptr()).linesize) };
let step = (unsafe { *linesize })[z] as usize;
let mut stdout = stdout(); let mut stdout = stdout();
let mut lock = stdout.lock(); let mut lock = stdout.lock();
let buf = frame.data(z); // EXPLANATION: (12x12 image)
// The full buffer will be 1152 bytes.
//
// 1152/12=96 Buffer length / Pixels high = 96 Bytes per line.
// 96/12=8 Bytes per line / Pixels per line = 8 Bytes per pixel.
//
// But RGB24 should only use 3 bytes per pixel. The other 5 we just discard.
// They might be alpha, or grayscale overlay, I'm not really sure.
//
// 8-5=3 Bytes per pixel - Discarded bytes = Used bytes
// 3*12=36 Used bytes * Pixels per line = Slice length
//
// This is because all the extra data (the 5 extra bytes) are at the end
// of each line. So we just take the first (Pixel width)*3 bytes.
let buf = frame.data(video_index);
let step = buf.len() / frame.height() as usize;
// (Assuming square) Step thru buffer. // (Assuming square) Step thru buffer.
for i in 0..size { for i in 0..pixels_wide {
let j = i*step; let j = i*step;
// Slices non-inclusively, ie: Even though j(n)+step = j(n+1) the ranges don't overlap. It take from..until // Have to take ownership, by implicilty Cloning the data. (Copying?)
let pre_slice = &buf[j..j+step]; let mut slice = Vec::from(&buf[j..j+pixels_wide*3]);
// Only take the size*3 of bytes, ignoring alpha values which are stored after.
// let slice = &pre_slice[0..size*3];
let mut slice = Vec::from(&pre_slice[0..size*3]);
// let lol = &buf[j..j+step][0..size*3];
match lines_format { match lines_format {
LinesFormat::Right => {/* Do nothing */}, LinesFormat::RightLeft => if i & 1 == 1 { rev_in_place_with_groupings(&mut slice) },
LinesFormat::RightLeft => if i & 1 == 1 { reverse(&mut slice) }, LinesFormat::LeftRight => if i & 1 != 1 { rev_in_place_with_groupings(&mut slice) },
LinesFormat::LeftRight => if i & 1 != 1 { reverse(&mut slice) }, LinesFormat::Left => rev_in_place_with_groupings(&mut slice),
LinesFormat::Left => slice.reverse(), LinesFormat::Right => {},
} }
// DEBUG // DEBUG
// println!("{:?} || {}-{}", slice, j, j+step); // println!("{:?} || {}-{}", slice, j, j+step);
// NORMAL // NORMAL
lock.write(&slice).unwrap(); lock.write(&slice).unwrap();
} }
stdout.flush().unwrap(); stdout.flush().unwrap();
Ok(())
} }
fn rev_in_place_with_groupings(raw: &mut Vec<u8>) {
/// Reverses the order of every grouping of 3 bytes. for i in 0..raw.len() {
fn reverse(raw: & mut [u8]) { match i % 3 {
// I would prefer this only went over the elements once, // Think of these as the indexes of each
// not twice. // RGB tuple. [0, 1, 2] [R, G, B].
// 0 and 2 have to be switched, while 1 can stay.
let x = raw 2 => {
.iter() let zero = raw[i-2];
.array_chunks::<3>() let two = raw[i];
.rev() raw[i-2] = two;
.flatten() raw[i] = zero;
.map(|f| *f) },
.collect::<Vec<u8>>(); _ => { /* 0 and 1*/ },
}
x.iter().enumerate().for_each(|f| raw[f.0] = *f.1); }
} }