Skip to content
Snippets Groups Projects
Commit 75426ae2 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Improve error reporting

parent b8a3cc81
No related branches found
No related tags found
No related merge requests found
...@@ -5,7 +5,7 @@ use std::marker::PhantomData; ...@@ -5,7 +5,7 @@ use std::marker::PhantomData;
use fraction::Fraction; use fraction::Fraction;
use crate::ffmpeg_api::enums::*; use crate::ffmpeg_api::enums::*;
use crate::media_time; use crate::util::media_time;
pub struct AVFormatContext { pub struct AVFormatContext {
base: *mut ffi::AVFormatContext, base: *mut ffi::AVFormatContext,
...@@ -293,11 +293,11 @@ impl<'a> AVStream<'a> { ...@@ -293,11 +293,11 @@ impl<'a> AVStream<'a> {
) )
} }
pub fn timestamp(self: &AVStream<'a>, timestamp: i64) -> media_time::MediaTime { pub fn timestamp(self: &AVStream<'a>, timestamp: i64) -> Result<media_time::MediaTime, failure::Error> {
media_time::MediaTime::from_rational(timestamp, self.time_base()) media_time::MediaTime::from_rational(timestamp, self.time_base())
} }
pub fn duration(&self) -> media_time::MediaTime { pub fn duration(&self) -> Result<media_time::MediaTime, failure::Error> {
self.timestamp(self.base.duration) self.timestamp(self.base.duration)
} }
......
#![allow(dead_code)] #![allow(dead_code)]
pub(crate) mod ffmpeg_api; pub(crate) mod ffmpeg_api;
pub(crate) mod webvtt; pub(crate) mod thumbnail;
pub(crate) mod media_time; pub(crate) mod util;
pub(crate) mod spritesheet;
use crate::ffmpeg_api::enums::*; use crate::util::media_time::MediaTime;
use crate::ffmpeg_api::api::*;
use crate::media_time::MediaTime;
fn main() -> Result<(), std::io::Error> { fn main() -> Result<(), failure::Error> {
let mut before = std::time::SystemTime::now(); thumbnail::extract::extract(
160, 5, 5,
let input = "/home/janne/Workspace/justflix/data/video.mp4";
let output = "/home/janne/Workspace/justflix/data/spritesheets";
let mut avformat_context = AVFormatContext::new().unwrap_or_else(|error| {
panic!("Could not allocate a context to process the video: {:?}", error)
});
avformat_context.open_input(input).unwrap_or_else(|error| {
panic!("Could not open video input: {:?}", error)
});
let mut spritesheet_manager = spritesheet::SpritesheetManager::new(
160,
5, 5,
MediaTime::from_seconds(10), MediaTime::from_seconds(10),
output, "/home/janne/Workspace/justflix/data/video.mp4",
); "/home/janne/Workspace/justflix/data/spritesheets"
)?;
let mut stream: AVStream = avformat_context.find_stream(|stream| {
stream.codec_parameters().codec_type() == AVMediaType::Video
}).unwrap_or_else(|| {
panic!("Could not find video stream")
});
stream.set_discard(AVDiscard::NonKey);
let codec_parameters = stream.codec_parameters();
let local_codec = codec_parameters.find_decoder();
println!(
"Stream #{}, type: {:#?}, codec: {:#?}",
stream.index(),
codec_parameters.codec_type(),
local_codec.name()
);
let mut output_frame = AVFrame::new().unwrap_or_else(|error| {
panic!("Could not create output frame: {:?}", error)
});
if codec_parameters.codec_type() == AVMediaType::Video {
let mut codec_context = AVCodecContext::new(&local_codec).unwrap_or_else(|error| {
panic!("Could not init codec context: {:?}", error)
});
codec_context.set_parameters(&codec_parameters);
codec_context.open(&local_codec);
codec_context.set_skip_loop_filter(AVDiscard::NonKey);
codec_context.set_skip_idct(AVDiscard::NonKey);
codec_context.set_skip_frame(AVDiscard::NonKey);
let mut packet = AVPacket::new().unwrap_or_else(|error| {
panic!("Could not init temporary packet: {:?}", error)
});
let mut frame = AVFrame::new().unwrap_or_else(|error| {
panic!("Could not create input frame: {:?}", error)
});
println!("Time: {:#?}", before.elapsed().unwrap());
before = std::time::SystemTime::now();
let mut scale_context = SwsContext::new();
while avformat_context.read_frame(&mut packet).is_ok() {
if packet.stream_index() == stream.index() {
codec_context.in_packet(&mut packet).unwrap_or_else(|error| {
panic!("Could not load packet: {:?}", error)
});
while codec_context.out_frame(&mut frame).is_ok() {
println!(
"Frame {}: {} @ {}",
frame.coded_picture_number(),
stream.timestamp(frame.pts()),
frame.key_frame()
);
println!("Reading Time: {:#?}", before.elapsed().unwrap());
before = std::time::SystemTime::now();
if spritesheet_manager.fulfils_frame_interval(stream.timestamp(frame.pts())) {
if !spritesheet_manager.initialized() {
spritesheet_manager.initialize(frame.width() as u32, frame.height() as u32);
output_frame.init(
spritesheet_manager.sprite_width() as i32,
spritesheet_manager.sprite_height() as i32,
AVPixelFormat::RGB24,
).unwrap_or_else(|error| {
panic!("Could not init output frame: {:?}", error)
});
scale_context.reinit(
&frame,
&output_frame,
SwsScaler::FastBilinear,
).unwrap_or_else(|error| {
panic!("Could not reinit scale context: {:?}", error)
});
}
scale_context.scale(&frame, &mut output_frame);
println!("Processing Time: {:#?}", before.elapsed().unwrap());
before = std::time::SystemTime::now();
spritesheet_manager.add_image(
stream.timestamp(frame.pts()),
image::ImageBuffer::from_raw(
output_frame.width() as u32,
output_frame.height() as u32,
output_frame.data(0).to_vec(),
).unwrap_or_else(|| {
panic!("Could not process frame")
}),
);
println!("Writing Time: {:#?}", before.elapsed().unwrap());
before = std::time::SystemTime::now();
}
}
}
}
spritesheet_manager.end_frame(stream.duration());
spritesheet_manager.save();
}
Ok(()) Ok(())
} }
use crate::ffmpeg_api::api::*;
use crate::ffmpeg_api::enums::*;
use crate::util::media_time::*;
use crate::thumbnail::spritesheet::*;
use failure::format_err;
pub fn extract<T: AsRef<str>, U: AsRef<str>>(
max_size: u32,
num_horizontal: u32, num_vertical: u32,
frame_interval: MediaTime,
input_file: T,
output_folder: U,
) -> Result<(), failure::Error> {
let mut avformat_context = AVFormatContext::new().map_err(|error| {
format_err!("Could not allocate a context to process the video: {:?}", error)
})?;
avformat_context.open_input(input_file.as_ref()).map_err(|error| {
format_err!("Could not open video input: {:?}", error)
})?;
let mut spritesheet_manager = SpritesheetManager::new(
max_size,
num_horizontal, num_vertical,
frame_interval,
output_folder,
);
let mut stream: AVStream = avformat_context.find_stream(|stream| {
stream.codec_parameters().codec_type() == AVMediaType::Video
}).ok_or_else(|| {
format_err!("Could not find video stream")
})?;
stream.set_discard(AVDiscard::NonKey);
let codec_parameters = stream.codec_parameters();
let local_codec = codec_parameters.find_decoder();
println!(
"Stream #{}, type: {:#?}, codec: {:#?}",
stream.index(),
codec_parameters.codec_type(),
local_codec.name()
);
let mut output_frame = AVFrame::new().map_err(|error| {
format_err!("Could not create output frame: {:?}", error)
})?;
if codec_parameters.codec_type() == AVMediaType::Video {
let mut codec_context = AVCodecContext::new(&local_codec).map_err(|error| {
format_err!("Could not init codec context: {:?}", error)
})?;
codec_context.set_parameters(&codec_parameters);
codec_context.open(&local_codec);
codec_context.set_skip_loop_filter(AVDiscard::NonKey);
codec_context.set_skip_idct(AVDiscard::NonKey);
codec_context.set_skip_frame(AVDiscard::NonKey);
let mut packet = AVPacket::new().map_err(|error| {
format_err!("Could not init temporary packet: {:?}", error)
})?;
let mut frame = AVFrame::new().map_err(|error| {
format_err!("Could not create input frame: {:?}", error)
})?;
let mut scale_context = SwsContext::new();
while avformat_context.read_frame(&mut packet).is_ok() {
if packet.stream_index() == stream.index() {
codec_context.in_packet(&mut packet).map_err(|error| {
format_err!("Could not load packet: {:?}", error)
})?;
while codec_context.out_frame(&mut frame).is_ok() {
println!(
"Frame {}: {} @ {}",
frame.coded_picture_number(),
stream.timestamp(frame.pts())?,
frame.key_frame()
);
if spritesheet_manager.fulfils_frame_interval(stream.timestamp(frame.pts())?) {
if !spritesheet_manager.initialized() {
spritesheet_manager.initialize(frame.width() as u32, frame.height() as u32);
output_frame.init(
spritesheet_manager.sprite_width() as i32,
spritesheet_manager.sprite_height() as i32,
AVPixelFormat::RGB24,
).map_err(|error| {
format_err!("Could not init output frame: {:?}", error)
})?;
scale_context.reinit(
&frame,
&output_frame,
SwsScaler::FastBilinear,
).map_err(|error| {
format_err!("Could not reinit scale context: {:?}", error)
})?;
}
scale_context.scale(&frame, &mut output_frame);
spritesheet_manager.add_image(
stream.timestamp(frame.pts())?,
image::ImageBuffer::from_raw(
output_frame.width() as u32,
output_frame.height() as u32,
output_frame.data(0).to_vec(),
).ok_or_else(|| {
format_err!("Could not process frame")
})?
)?;
}
}
}
}
spritesheet_manager.end_frame(stream.duration()?);
spritesheet_manager.save()?;
}
Ok(())
}
\ No newline at end of file
pub(crate) mod extract;
pub(crate) mod spritesheet;
\ No newline at end of file
use image::{RgbImage, ImageBuffer}; use image::{RgbImage, ImageBuffer};
use crate::media_time::MediaTime; use failure::{bail, format_err};
use crate::webvtt::{WebVTTFile, WebVTTCue};
use crate::util::media_time::MediaTime;
use crate::util::webvtt::{WebVTTFile, WebVTTCue};
pub struct SpritesheetManager { pub struct SpritesheetManager {
num_horizontal: u32, num_horizontal: u32,
...@@ -88,9 +90,9 @@ impl SpritesheetManager { ...@@ -88,9 +90,9 @@ impl SpritesheetManager {
self.current_image == 0 || timestamp - self.last_timestamp > self.frame_interval self.current_image == 0 || timestamp - self.last_timestamp > self.frame_interval
} }
pub fn add_image(&mut self, timestamp: MediaTime, image: RgbImage) { pub fn add_image(&mut self, timestamp: MediaTime, image: RgbImage) -> Result<(), failure::Error> {
if image.width() != self.sprite_width || image.height() != self.sprite_height { if image.width() != self.sprite_width || image.height() != self.sprite_height {
panic!( bail!(
"Wrong image size: {}x{}, but expected {}x{}", "Wrong image size: {}x{}, but expected {}x{}",
image.width(), image.height(), image.width(), image.height(),
self.sprite_width, self.sprite_height self.sprite_width, self.sprite_height
...@@ -113,8 +115,10 @@ impl SpritesheetManager { ...@@ -113,8 +115,10 @@ impl SpritesheetManager {
self.current_image += 1; self.current_image += 1;
if self.sprite_index(self.current_image) == 0 { if self.sprite_index(self.current_image) == 0 {
self.save(); self.save()?;
} }
Ok(())
} }
pub fn end_frame(&mut self, timestamp: MediaTime) { pub fn end_frame(&mut self, timestamp: MediaTime) {
...@@ -132,19 +136,21 @@ impl SpritesheetManager { ...@@ -132,19 +136,21 @@ impl SpritesheetManager {
)); ));
} }
fn save_spritesheet(&mut self) { fn save_spritesheet(&mut self) -> Result<(), failure::Error> {
self.spritesheet.save( self.spritesheet.save(
format!("{}/spritesheet_{}.png", self.output_path, self.spritesheet_index(self.current_image)) format!("{}/spritesheet_{}.png", self.output_path, self.spritesheet_index(self.current_image))
).unwrap_or_else(|error| { ).map_err(|error| {
panic!("Could not write spritesheet: {}", error) format_err!("Could not write spritesheet: {}", error)
}); })?;
self.reinit_buffer(); self.reinit_buffer();
Ok(())
} }
pub fn save(&mut self) { pub fn save(&mut self) -> Result<(), failure::Error> {
self.save_spritesheet(); self.save_spritesheet()?;
self.metadata.save(format!("{}/spritesheet.vtt", self.output_path)).unwrap_or_else(|error| { self.metadata.save(format!("{}/spritesheet.vtt", self.output_path)).map_err(|error| {
panic!("Could not write spritesheet metadata: {}", error) format_err!("Could not write spritesheet metadata: {}", error)
}); })?;
Ok(())
} }
} }
\ No newline at end of file
use fraction::Fraction; use fraction::Fraction;
use failure::format_err;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct MediaTime(time::Duration); pub struct MediaTime(time::Duration);
impl MediaTime { impl MediaTime {
pub fn from_rational(timestamp: i64, base: Fraction) -> MediaTime { pub fn from_rational(timestamp: i64, base: Fraction) -> Result<MediaTime, failure::Error> {
let num: u64 = *base.numer().unwrap_or_else(|| { let num: u64 = *base.numer().ok_or_else(|| {
panic!("time base of unusable format") format_err!("time base of unusable format")
}); })?;
let den: u64 = *base.denom().unwrap_or_else(|| { let den: u64 = *base.denom().ok_or_else(|| {
panic!("time base of unusable format") format_err!("time base of unusable format")
}); })?;
MediaTime(time::Duration::milliseconds( Ok(MediaTime(time::Duration::milliseconds(
1000 * timestamp * num as i64 / den as i64 1000 * timestamp * num as i64 / den as i64
)) )))
} }
pub fn from_millis(timestamp: i64) -> MediaTime { pub fn from_millis(timestamp: i64) -> MediaTime {
......
pub(crate) mod webvtt;
pub(crate) mod media_time;
\ No newline at end of file
use crate::media_time::MediaTime;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::LineWriter; use std::io::LineWriter;
use crate::util::media_time::MediaTime;
pub struct WebVTTFile { pub struct WebVTTFile {
cues: Vec<WebVTTCue> cues: Vec<WebVTTCue>
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment