diff --git a/src/ffmpeg_api/api.rs b/src/ffmpeg_api/api.rs index c44435fa10d0ee02e307bca2eefe158487b1a3d6..1f9607264f5fcc0086ddcb741365338694b7b991 100644 --- a/src/ffmpeg_api/api.rs +++ b/src/ffmpeg_api/api.rs @@ -1,20 +1,32 @@ use std::marker::PhantomData; +use std::path::Path; use enum_primitive::*; -use failure::bail; +use failure::{bail, format_err, Error}; use ffmpeg_dev::sys as ffi; use fraction::Fraction; use crate::ffmpeg_api::enums::*; use crate::util::media_time; -use std::path::Path; + +fn native_string(ptr: *const std::os::raw::c_char) -> Result<String, Error> { + if ptr.is_null() { + Err(format_err!("String is null")) + } else { + Ok(String::from( + unsafe { std::ffi::CStr::from_ptr(ptr) } + .to_str() + .map_err(|err| format_err!("String is not valid utf-8: {}", err))?, + )) + } +} pub struct AVFormatContext { base: *mut ffi::AVFormatContext, } -impl<'a> AVFormatContext { - pub fn new() -> Result<Self, failure::Error> { +impl AVFormatContext { + pub fn new() -> Result<Self, Error> { let base = unsafe { ffi::avformat_alloc_context() }; if base.is_null() { bail!("avformat_alloc_context() failed"); @@ -22,12 +34,12 @@ impl<'a> AVFormatContext { Ok(AVFormatContext { base }) } - pub fn open_input(&mut self, path: &Path) -> Result<(), failure::Error> { + pub fn open_input(&mut self, path: &Path) -> Result<(), Error> { let path = path .to_str() - .ok_or(failure::format_err!("Could not convert path to c string"))?; + .ok_or(format_err!("Could not convert path to c string"))?; let path = std::ffi::CString::new(path) - .map_err(|err| failure::format_err!("Could not convert path to c string: {}", err))?; + .map_err(|err| format_err!("Could not convert path to c string: {}", err))?; match unsafe { ffi::avformat_open_input( @@ -42,9 +54,9 @@ impl<'a> AVFormatContext { } } - pub fn input_format(&self) -> Result<AVInputFormat, failure::Error> { + pub fn input_format(&self) -> Result<AVInputFormat, Error> { let base: &mut ffi::AVInputFormat = unsafe { (*self.base).iformat.as_mut() } - .ok_or(failure::format_err!("No AVInputFormat found"))?; + .ok_or(format_err!("No AVInputFormat found"))?; Ok(AVInputFormat::new(base)) } @@ -72,14 +84,14 @@ impl<'a> AVFormatContext { .find(predicate) } - pub fn read_frame(&mut self, packet: &mut AVPacket) -> Result<(), failure::Error> { + pub fn read_frame(&mut self, packet: &mut AVPacket) -> Result<(), Error> { match unsafe { ffi::av_read_frame(self.base, packet.base) } { 0 => Ok(()), errno => bail!("Error while decoding frame: {}", errno), } } - pub fn duration(&self) -> Result<media_time::MediaTime, failure::Error> { + pub fn duration(&self) -> Result<media_time::MediaTime, Error> { media_time::MediaTime::from_rational( unsafe { (*self.base).duration }, Fraction::new(1 as u64, ffi::AV_TIME_BASE as u64), @@ -102,45 +114,17 @@ impl<'a> AVInputFormat<'a> { return AVInputFormat { base }; } - pub fn long_name(&self) -> Result<String, failure::Error> { - let raw: *const ::std::os::raw::c_char = self.base.long_name; - - if raw.is_null() { - bail!("No long name found for input format") - } else { - Ok(String::from( - unsafe { std::ffi::CStr::from_ptr(raw) } - .to_str() - .map_err(|err| { - failure::format_err!( - "Could not convert long name for input format to string: {}", - err - ) - })?, - )) - } + pub fn long_name(&self) -> Result<String, Error> { + native_string(self.base.long_name) + .map_err(|err| format_err!("Could not access long name for input format: {}", err)) } - pub fn name(&self) -> Result<String, failure::Error> { - let raw: *const ::std::os::raw::c_char = self.base.name; - - if raw.is_null() { - bail!("No name found for input format") - } else { - Ok(String::from( - unsafe { std::ffi::CStr::from_ptr(raw) } - .to_str() - .map_err(|err| { - failure::format_err!( - "Could not convert name for input format to string: {}", - err - ) - })?, - )) - } + pub fn name(&self) -> Result<String, Error> { + native_string(self.base.name) + .map_err(|err| format_err!("Could not access short name for input format: {}", err)) } - pub fn determine_mime<T: AsRef<str>>(&self, stream_codec: T) -> Result<&str, failure::Error> { + pub fn determine_mime(&self, stream_codec: impl AsRef<str>) -> Result<&str, Error> { let containers = self.name()?; let stream_codec = stream_codec.as_ref(); @@ -166,7 +150,7 @@ pub struct AVBuffer { } impl AVBuffer { - pub fn new(size: usize) -> Result<Self, failure::Error> { + pub fn new(size: usize) -> Result<Self, Error> { let base = unsafe { ffi::av_malloc(size) } as *mut u8; if base.is_null() { bail!("av_malloc() failed"); @@ -195,7 +179,7 @@ pub struct AVPacket { } impl AVPacket { - pub fn new() -> Result<Self, failure::Error> { + pub fn new() -> Result<Self, Error> { let base = unsafe { ffi::av_packet_alloc() }; if base.is_null() { bail!("av_packet_alloc() failed"); @@ -232,7 +216,7 @@ pub struct AVFrame { } impl AVFrame { - pub fn new() -> Result<Self, failure::Error> { + pub fn new() -> Result<Self, Error> { let base = unsafe { ffi::av_frame_alloc() }; if base.is_null() { bail!("avformat_alloc_frame() failed"); @@ -243,12 +227,7 @@ impl AVFrame { }) } - pub fn init( - &mut self, - width: i32, - height: i32, - format: AVPixelFormat, - ) -> Result<(), failure::Error> { + pub fn init(&mut self, width: i32, height: i32, format: AVPixelFormat) -> Result<(), Error> { self.as_mut().width = width; self.as_mut().height = height; self.as_mut().format = format as ffi::AVPixelFormat; @@ -350,25 +329,22 @@ impl<'a> AVStream<'a> { return AVStream { base }; } - pub fn index(self: &AVStream<'a>) -> i32 { + pub fn index(&self) -> i32 { self.base.index } - pub fn time_base(self: &AVStream<'a>) -> Fraction { + pub fn time_base(&self) -> Fraction { Fraction::new( self.base.time_base.num as u32, self.base.time_base.den as u32, ) } - pub fn timestamp( - self: &AVStream<'a>, - timestamp: i64, - ) -> Result<media_time::MediaTime, failure::Error> { + pub fn timestamp(&self, timestamp: i64) -> Result<media_time::MediaTime, Error> { media_time::MediaTime::from_rational(timestamp, self.time_base()) } - pub fn duration(&self) -> Result<media_time::MediaTime, failure::Error> { + pub fn duration(&self) -> Result<media_time::MediaTime, Error> { self.timestamp(self.base.duration) } @@ -398,10 +374,10 @@ impl<'a> AVStream<'a> { ) } - pub fn codec_parameters(&self) -> Result<AVCodecParameters, failure::Error> { + pub fn codec_parameters(&self) -> Result<AVCodecParameters, Error> { Ok(AVCodecParameters::new( unsafe { self.base.codecpar.as_mut() } - .ok_or(failure::format_err!("No AVCodecParameters found"))?, + .ok_or(format_err!("No AVCodecParameters found"))?, self, )) } @@ -432,12 +408,12 @@ impl<'a> AVCodecParameters<'a> { self.base.bit_rate } - pub fn find_decoder(&self) -> AVCodec { - AVCodec::new( + pub fn find_decoder(&self) -> Result<AVCodec, Error> { + Ok(AVCodec::new( unsafe { ffi::avcodec_find_decoder(self.base.codec_id).as_mut() } - .expect("AVCodec was unexpectedly null"), + .ok_or(format_err!("No AVCodec found"))?, self, - ) + )) } } @@ -454,12 +430,9 @@ impl<'a> AVCodec<'a> { }; } - pub fn name(self: &AVCodec<'a>) -> std::string::String { - String::from( - unsafe { std::ffi::CStr::from_ptr(self.base.name) } - .to_str() - .unwrap(), - ) + pub fn name(&self) -> Result<String, Error> { + native_string(self.base.name) + .map_err(|err| format_err!("Could not access name for codec: {}", err)) } } @@ -468,7 +441,7 @@ pub struct AVCodecContext { } impl AVCodecContext { - pub fn new(codec: &AVCodec) -> Result<Self, failure::Error> { + pub fn new(codec: &AVCodec) -> Result<Self, Error> { let base = unsafe { ffi::avcodec_alloc_context3(codec.base) }; if base.is_null() { bail!("avcodec_alloc_context3() failed"); @@ -476,23 +449,17 @@ impl AVCodecContext { Ok(AVCodecContext { base }) } - pub fn in_packet(&mut self, packet: &mut AVPacket) -> Result<(), failure::Error> { + pub fn in_packet(&mut self, packet: &mut AVPacket) -> Result<(), Error> { match unsafe { ffi::avcodec_send_packet(self.base, packet.base) } { 0 => Ok(()), - errno => Err(failure::format_err!( - "Error while loading paclet: {}", - errno - )), + errno => Err(format_err!("Error while loading packet: {}", errno)), } } - pub fn out_frame(&mut self, frame: &mut AVFrame) -> Result<(), failure::Error> { + pub fn out_frame(&mut self, frame: &mut AVFrame) -> Result<(), Error> { match unsafe { ffi::avcodec_receive_frame(self.base, frame.base) } { 0 => Ok(()), - errno => Err(failure::format_err!( - "Error while decoding frame: {}", - errno - )), + errno => Err(format_err!("Error while decoding frame: {}", errno)), } } @@ -565,7 +532,7 @@ impl SwsContext { source: &AVFrame, target: &AVFrame, scaler: SwsScaler, - ) -> Result<(), failure::Error> { + ) -> Result<(), Error> { let base = unsafe { ffi::sws_getCachedContext( self.base, diff --git a/src/ingest/extract.rs b/src/ingest/extract.rs index 91ccd15dbb38acee1d8dbed283851be6e027f6d6..28b5be0e53b049d4aa0247694ea5ffa2b131e779 100644 --- a/src/ingest/extract.rs +++ b/src/ingest/extract.rs @@ -1,6 +1,7 @@ -use failure::format_err; use std::path::Path; +use failure::{format_err, Error}; + use crate::ffmpeg_api::api::*; use crate::ffmpeg_api::enums::*; use crate::ingest::spritesheet::*; @@ -14,7 +15,7 @@ pub fn extract( frame_interval: MediaTime, input_file: &Path, output_folder: &Path, -) -> Result<(), failure::Error> { +) -> Result<(), Error> { let mut avformat_context = AVFormatContext::new() .map_err(|error| format_err!("Could not open video input: {}", error))?; avformat_context @@ -48,19 +49,19 @@ pub fn extract( let time_base = stream.time_base(); let codec_parameters = stream.codec_parameters()?; - let local_codec = codec_parameters.find_decoder(); + let local_codec = codec_parameters.find_decoder()?; println!( "Stream #{}, type: {:#?}, codec: {:#?}", index, codec_parameters.codec_type(), - local_codec.name() + local_codec.name()? ); let mut metadata = StreamMetadata::new( avformat_context .input_format()? - .determine_mime(local_codec.name())?, + .determine_mime(local_codec.name()?)?, duration, codec_parameters.bit_rate() / 1000, ); diff --git a/src/ingest/spritesheet.rs b/src/ingest/spritesheet.rs index 42b61d5d42edf208af6ab2bcb601ca13aab46909..9a6daa9c5d955acf38146768c98bd1fce6e9ba8f 100644 --- a/src/ingest/spritesheet.rs +++ b/src/ingest/spritesheet.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use failure::{bail, format_err}; +use failure::{bail, format_err, Error}; use image::{ImageBuffer, RgbImage}; use crate::util::media_time::MediaTime; @@ -23,13 +23,13 @@ pub struct SpritesheetManager { } impl SpritesheetManager { - pub fn new<T: AsRef<str>, U: Into<PathBuf>>( + pub fn new( max_side: u32, num_horizontal: u32, num_vertical: u32, frame_interval: MediaTime, - output_path: U, - name: T, + output_path: impl Into<PathBuf>, + name: impl AsRef<str>, ) -> SpritesheetManager { SpritesheetManager { num_horizontal, @@ -101,11 +101,7 @@ impl SpritesheetManager { self.current_image == 0 || timestamp - self.last_timestamp > self.frame_interval } - pub fn add_image( - &mut self, - timestamp: MediaTime, - image: RgbImage, - ) -> Result<(), failure::Error> { + pub fn add_image(&mut self, timestamp: MediaTime, image: RgbImage) -> Result<(), Error> { if image.width() != self.sprite_width || image.height() != self.sprite_height { bail!( "Wrong image size: {}x{}, but expected {}x{}", @@ -150,7 +146,7 @@ impl SpritesheetManager { )); } - fn save_spritesheet(&mut self) -> Result<(), failure::Error> { + fn save_spritesheet(&mut self) -> Result<(), Error> { self.spritesheet .save(self.output_path.join(format!( "{}_{}.jpg", @@ -162,7 +158,7 @@ impl SpritesheetManager { Ok(()) } - pub fn save(&mut self) -> Result<(), failure::Error> { + pub fn save(&mut self) -> Result<(), Error> { self.save_spritesheet()?; self.metadata .save(self.output_path.join(format!("{}.vtt", self.name))) diff --git a/src/main.rs b/src/main.rs index 32c15efbd4aa5572b60af5bf75e20f7ccbf827d1..8db4ba3831ac8ccead385ac03b1a7fb0d09aabd4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ pub(crate) mod util; use std::path::Path; +use failure::Error; use structopt::StructOpt; use crate::util::media_time::MediaTime; @@ -25,7 +26,7 @@ struct Options { max_size: u32, } -fn main() -> Result<(), failure::Error> { +fn main() -> Result<(), Error> { let options = Options::from_args(); ingest::extract::extract( diff --git a/src/util/media_time.rs b/src/util/media_time.rs index ef804856d595d9c497951ad41eb8743be4ce3877..00c5b11844700d0bb5487b4107f302c5a0bd375b 100644 --- a/src/util/media_time.rs +++ b/src/util/media_time.rs @@ -1,11 +1,11 @@ -use failure::format_err; +use failure::{format_err, Error}; use fraction::Fraction; #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct MediaTime(time::Duration); impl MediaTime { - pub fn from_rational(timestamp: i64, base: Fraction) -> Result<MediaTime, failure::Error> { + pub fn from_rational(timestamp: i64, base: Fraction) -> Result<MediaTime, Error> { let num: u64 = *base .numer() .ok_or_else(|| format_err!("time base of unusable format"))?; diff --git a/src/util/stream_metadata.rs b/src/util/stream_metadata.rs index 3d71b1af433424c66dcf0b4c1c73dd08f7be48d9..912034727fe86d9a2921530434be246d6367866a 100644 --- a/src/util/stream_metadata.rs +++ b/src/util/stream_metadata.rs @@ -17,11 +17,7 @@ pub struct StreamMetadata { } impl StreamMetadata { - pub fn new<T: AsRef<str>>( - content_type: T, - duration: MediaTime, - bitrate: i64, - ) -> StreamMetadata { + pub fn new(content_type: impl AsRef<str>, duration: MediaTime, bitrate: i64) -> StreamMetadata { StreamMetadata { content_type: String::from(content_type.as_ref()), duration: duration.seconds(), @@ -38,7 +34,7 @@ impl StreamMetadata { self.aspect_ratio = (width as f64 / height as f64) as f32; } - pub fn save<T: AsRef<Path>>(&self, path: T) -> Result<(), std::io::Error> { + pub fn save(&self, path: impl AsRef<Path>) -> Result<(), std::io::Error> { serde_json::to_writer(BufWriter::new(File::create(path)?), self)?; Ok(()) } diff --git a/src/util/webvtt.rs b/src/util/webvtt.rs index 782fac52f5e09791aa124d1fc52e46b79801edeb..7a6788e1abe9e70a1ebdc667c07bcd71d32da575 100644 --- a/src/util/webvtt.rs +++ b/src/util/webvtt.rs @@ -25,7 +25,7 @@ impl WebVTTFile { self.cues.push(cue); } - pub fn save<T: AsRef<Path>>(&self, path: T) -> Result<(), std::io::Error> { + pub fn save(&self, path: impl AsRef<Path>) -> Result<(), std::io::Error> { let file = File::create(path)?; let mut file = LineWriter::new(file); file.write_all(b"WEBVTT\n\n")?;