Compare commits
4 Commits
37ce43cbde
...
e9238660d8
Author | SHA1 | Date | |
---|---|---|---|
e9238660d8 | |||
bd7cc019cf | |||
0f3c3453ac | |||
7d905adf10 |
12
src/main.rs
12
src/main.rs
@ -1,4 +1,6 @@
|
||||
#![feature(iter_array_chunks)]
|
||||
#![feature(error_generic_member_access)]
|
||||
#![feature(provide_any)]
|
||||
|
||||
extern crate ffmpeg_next as ffmpeg;
|
||||
|
||||
@ -8,7 +10,6 @@ use ffmpeg::media::Type;
|
||||
use ffmpeg::software::scaling::{context::Context, flag::Flags};
|
||||
use ffmpeg::util::frame::video::Video;
|
||||
use std::path::Path;
|
||||
use std::time::Instant;
|
||||
mod out;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
@ -17,14 +18,13 @@ fn main() -> Result<(), Error> {
|
||||
ffmpeg::init()?;
|
||||
|
||||
// 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
|
||||
.streams()
|
||||
.best(Type::Video)
|
||||
.ok_or(ffmpeg::Error::StreamNotFound)?;
|
||||
let video_stream_index = input.index();
|
||||
let fps = input.avg_frame_rate();
|
||||
|
||||
let context_decoder = ffmpeg::codec::context::Context::from_parameters(input.parameters())?;
|
||||
// Ctx's video decoder
|
||||
@ -52,7 +52,7 @@ fn main() -> Result<(), Error> {
|
||||
|
||||
// test::print_raw(&rgb_frame.data(0));
|
||||
// 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();
|
||||
}
|
||||
Ok(())
|
||||
@ -63,12 +63,10 @@ fn main() -> Result<(), Error> {
|
||||
// Is this for multiple video streams?
|
||||
if stream.index() == video_stream_index {
|
||||
decoder.send_packet(&packet)?;
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
receive_and_process_decoded_frames(&mut decoder)?;
|
||||
}
|
||||
}
|
||||
|
||||
decoder.send_eof()?;
|
||||
receive_and_process_decoded_frames(&mut decoder)?;
|
||||
}
|
||||
|
107
src/out.rs
107
src/out.rs
@ -13,62 +13,93 @@ pub enum LinesFormat {
|
||||
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
|
||||
pub fn print_square(frame: &ffmpeg::frame::Video, lines_format: LinesFormat, video_index: usize) {
|
||||
if frame.format() != Pixel::RGB24 { panic!("Must use Pixel::RGB24"); }
|
||||
if frame.width() != frame.height() { panic!("Must be 1:1 aspect ratio"); }
|
||||
/// Images must be in RGB24
|
||||
pub fn print_square(frame: &ffmpeg::frame::Video, lines_format: LinesFormat, video_index: usize) -> Result<(), ParseError> {
|
||||
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 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 pixels_wide = frame.width() as usize;
|
||||
|
||||
let mut stdout = stdout();
|
||||
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.
|
||||
for i in 0..size {
|
||||
for i in 0..pixels_wide {
|
||||
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
|
||||
let pre_slice = &buf[j..j+step];
|
||||
// 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];
|
||||
// Have to take ownership, by implicilty Cloning the data. (Copying?)
|
||||
let mut slice = Vec::from(&buf[j..j+pixels_wide*3]);
|
||||
|
||||
match lines_format {
|
||||
LinesFormat::Right => {/* Do nothing */},
|
||||
LinesFormat::RightLeft => if i & 1 == 1 { reverse(&mut slice) },
|
||||
LinesFormat::LeftRight => if i & 1 != 1 { reverse(&mut slice) },
|
||||
LinesFormat::Left => slice.reverse(),
|
||||
LinesFormat::RightLeft => if i & 1 == 1 { rev_in_place_with_groupings(&mut slice) },
|
||||
LinesFormat::LeftRight => if i & 1 != 1 { rev_in_place_with_groupings(&mut slice) },
|
||||
LinesFormat::Left => rev_in_place_with_groupings(&mut slice),
|
||||
LinesFormat::Right => {},
|
||||
}
|
||||
|
||||
|
||||
// DEBUG
|
||||
// println!("{:?} || {}-{}", slice, j, j+step);
|
||||
// NORMAL
|
||||
lock.write(&slice).unwrap();
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Reverses the order of every grouping of 3 bytes.
|
||||
fn reverse(raw: & mut [u8]) {
|
||||
// I would prefer this only went over the elements once,
|
||||
// not twice.
|
||||
|
||||
let x = raw
|
||||
.iter()
|
||||
.array_chunks::<3>()
|
||||
.rev()
|
||||
.flatten()
|
||||
.map(|f| *f)
|
||||
.collect::<Vec<u8>>();
|
||||
|
||||
x.iter().enumerate().for_each(|f| raw[f.0] = *f.1);
|
||||
fn rev_in_place_with_groupings(raw: &mut Vec<u8>) {
|
||||
for i in 0..raw.len() {
|
||||
match i % 3 {
|
||||
// Think of these as the indexes of each
|
||||
// RGB tuple. [0, 1, 2] [R, G, B].
|
||||
// 0 and 2 have to be switched, while 1 can stay.
|
||||
2 => {
|
||||
let zero = raw[i-2];
|
||||
let two = raw[i];
|
||||
raw[i-2] = two;
|
||||
raw[i] = zero;
|
||||
},
|
||||
_ => { /* 0 and 1*/ },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user