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

Cleanup error handling and enums

parent 62b8d5d1
Branches
No related tags found
No related merge requests found
Showing with 1589 additions and 1198 deletions
......@@ -24,6 +24,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
[[package]]
name = "atty"
version = "0.2.14"
......@@ -41,28 +47,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "backtrace"
version = "0.3.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536"
dependencies = [
"backtrace-sys",
"cfg-if",
"libc",
"rustc-demangle",
]
[[package]]
name = "backtrace-sys"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "bindgen"
version = "0.51.1"
......@@ -78,8 +62,8 @@ dependencies = [
"lazy_static",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"proc-macro2 1.0.8",
"quote 1.0.2",
"regex",
"rustc-hash",
"shlex",
......@@ -229,15 +213,6 @@ version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
[[package]]
name = "enum_primitive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180"
dependencies = [
"num-traits 0.1.43",
]
[[package]]
name = "env_logger"
version = "0.6.2"
......@@ -251,28 +226,6 @@ dependencies = [
"termcolor",
]
[[package]]
name = "failure"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "ffmpeg-dev"
version = "0.3.8"
......@@ -292,11 +245,12 @@ name = "ffmpeg_api"
version = "0.1.0"
dependencies = [
"bitflags",
"enum_primitive",
"failure",
"ffmpeg-dev",
"fraction",
"media_time",
"num-derive",
"num-traits",
"thiserror",
]
[[package]]
......@@ -388,7 +342,7 @@ dependencies = [
"jpeg-decoder",
"num-iter",
"num-rational",
"num-traits 0.2.11",
"num-traits",
"png",
"scoped_threadpool",
"tiff",
......@@ -464,9 +418,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "media-ingestion"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"failure",
"anyhow",
"ffmpeg_api",
"fraction",
"image",
......@@ -482,6 +436,7 @@ name = "media_time"
version = "0.1.0"
dependencies = [
"fraction",
"thiserror",
"time",
]
......@@ -530,7 +485,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-rational",
"num-traits 0.2.11",
"num-traits",
]
[[package]]
......@@ -541,7 +496,7 @@ checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
"autocfg",
"num-integer",
"num-traits 0.2.11",
"num-traits",
]
[[package]]
......@@ -551,7 +506,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
dependencies = [
"autocfg",
"num-traits 0.2.11",
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2"
dependencies = [
"proc-macro2 0.4.30",
"quote 0.6.13",
"syn 0.15.44",
]
[[package]]
......@@ -561,7 +527,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
dependencies = [
"autocfg",
"num-traits 0.2.11",
"num-traits",
]
[[package]]
......@@ -572,7 +538,7 @@ checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
dependencies = [
"autocfg",
"num-integer",
"num-traits 0.2.11",
"num-traits",
]
[[package]]
......@@ -584,16 +550,7 @@ dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits 0.2.11",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.11",
"num-traits",
]
[[package]]
......@@ -640,9 +597,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7959c6467d962050d639361f7703b2051c43036d03493c36f01d440fdd3138a"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
"version_check 0.9.1",
]
......@@ -652,9 +609,9 @@ version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4002d9f55991d5e019fb940a90e1a95eb80c24e77cb2462dd4dc869604d543a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
"syn-mid",
"version_check 0.9.1",
]
......@@ -665,9 +622,18 @@ version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
]
[[package]]
name = "proc-macro2"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
dependencies = [
"unicode-xid 0.1.0",
]
[[package]]
......@@ -676,7 +642,7 @@ version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
dependencies = [
"unicode-xid",
"unicode-xid 0.2.0",
]
[[package]]
......@@ -685,13 +651,22 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
dependencies = [
"proc-macro2 0.4.30",
]
[[package]]
name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
dependencies = [
"proc-macro2",
"proc-macro2 1.0.8",
]
[[package]]
......@@ -742,12 +717,6 @@ version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1"
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rustc-hash"
version = "1.1.0"
......@@ -769,9 +738,9 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
]
[[package]]
......@@ -822,9 +791,9 @@ version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
]
[[package]]
......@@ -869,43 +838,42 @@ checksum = "c6e79c80e0f4efd86ca960218d4e056249be189ff1c42824dcd9a7f51a56f0bd"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
]
[[package]]
name = "syn"
version = "1.0.15"
version = "0.15.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0294dc449adc58bb6592fff1a23d3e5e6e235afc6a0ffca2657d19e7bbffe5"
checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"proc-macro2 0.4.30",
"quote 0.6.13",
"unicode-xid 0.1.0",
]
[[package]]
name = "syn-mid"
version = "0.5.0"
name = "syn"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
checksum = "7a0294dc449adc58bb6592fff1a23d3e5e6e235afc6a0ffca2657d19e7bbffe5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.8",
"quote 1.0.2",
"unicode-xid 0.2.0",
]
[[package]]
name = "synstructure"
version = "0.12.3"
name = "syn-mid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
]
[[package]]
......@@ -938,6 +906,26 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd"
dependencies = [
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
]
[[package]]
name = "thread_local"
version = "1.0.1"
......@@ -987,9 +975,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e987cfe0537f575b5fc99909de6185f6c19c3ad8889e2275e686a873d0869ba1"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.8",
"quote 1.0.2",
"syn 1.0.15",
]
[[package]]
......@@ -1004,6 +992,12 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "unicode-xid"
version = "0.2.0"
......@@ -1033,6 +1027,7 @@ name = "webvtt"
version = "0.1.0"
dependencies = [
"media_time",
"thiserror",
]
[[package]]
......
[package]
name = "media-ingestion"
version = "0.1.0"
version = "0.1.1"
authors = ["Janne Mareike Koschinski <janne@kuschku.de>"]
description = """
......@@ -17,7 +17,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
failure = "0.1.6"
anyhow = "1.0.28"
fraction = "0.6.2"
image = "0.23.0"
serde_json = "1.0"
......
......@@ -8,9 +8,10 @@ edition = "2018"
[dependencies]
bitflags = "1.2.1"
enum_primitive = "0.1.1"
failure = "0.1.6"
ffmpeg-dev = "0.3.8"
fraction = "0.6.2"
num-traits = "0.2"
num-derive = "0.2"
thiserror = "1.0"
media_time = { path = "../media_time" }
\ No newline at end of file
use std::marker::PhantomData;
use std::path::Path;
use std::path::{Path, PathBuf};
use enum_primitive::*;
use failure::{bail, format_err, Error};
use ffmpeg_dev::sys as ffi;
use fraction::Fraction;
use num_traits::FromPrimitive;
use media_time::MediaTimeError;
use thiserror::Error;
use crate::enums::*;
use crate::err::AVError;
fn native_string(ptr: *const std::os::raw::c_char) -> Result<String, Error> {
#[derive(Error, Debug)]
pub enum AVAllocError {
#[error("Allocating a new {0} failed")]
AllocFailed(String)
}
#[derive(Error, Debug)]
pub enum StringError {
#[error("String is unexpectedly null")]
NullError,
#[error(transparent)]
Utf8Error(#[from] std::str::Utf8Error)
}
fn native_string(ptr: *const std::os::raw::c_char) -> Result<String, StringError> {
if ptr.is_null() {
Err(format_err!("String is null"))
Err(StringError::NullError)
} 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))?,
.to_str()?,
))
}
}
......@@ -24,38 +39,48 @@ pub struct AVFormatContext {
base: *mut ffi::AVFormatContext,
}
#[derive(Error, Debug)]
pub enum AVFormatContextError {
#[error(transparent)]
AllocFailed(#[from] AVAllocError),
#[error("Path {0} is invalid")]
PathInvalid(PathBuf),
#[error("Path {0} contains null byte")]
PathContainsNull(PathBuf, #[source] std::ffi::NulError),
#[error("Opening media file {0} failed")]
OpenInputFailed(PathBuf, #[source] AVError)
}
impl AVFormatContext {
pub fn new() -> Result<Self, Error> {
pub fn new() -> Result<Self, AVAllocError> {
let base = unsafe { ffi::avformat_alloc_context() };
if base.is_null() {
bail!("avformat_alloc_context() failed");
}
return if base.is_null() {
Err(AVAllocError::AllocFailed("AVFormatContext".to_string()))
} else {
Ok(AVFormatContext { base })
}
}
pub fn open_input(&mut self, path: &Path) -> Result<(), Error> {
let path = path
pub fn open_input(&mut self, path: &Path) -> Result<(), AVFormatContextError> {
let pathname = path
.to_str()
.ok_or(format_err!("Could not convert path to c string"))?;
let path = std::ffi::CString::new(path)
.map_err(|err| format_err!("Could not convert path to c string: {}", err))?;
.ok_or(AVFormatContextError::PathInvalid(path.to_path_buf()))?;
let pathname = std::ffi::CString::new(pathname)
.map_err(|err| AVFormatContextError::PathContainsNull(path.to_path_buf(), err))?;
match unsafe {
AVError::from_errno(unsafe {
ffi::avformat_open_input(
&mut self.base,
path.as_ptr(),
pathname.as_ptr(),
std::ptr::null_mut(),
std::ptr::null_mut(),
)
} {
0 => Ok(()),
_ => bail!("Could not open input"),
}
}).map_err(|err| AVFormatContextError::OpenInputFailed(path.to_path_buf(), err))
}
pub fn input_format(&self) -> Result<AVInputFormat, Error> {
pub fn input_format(&self) -> Result<AVInputFormat, AVInputFormatError> {
let base: &mut ffi::AVInputFormat = unsafe { (*self.base).iformat.as_mut() }
.ok_or(format_err!("No AVInputFormat found"))?;
.ok_or(AVInputFormatError::Invalid)?;
Ok(AVInputFormat::new(base))
}
......@@ -69,19 +94,16 @@ impl AVFormatContext {
.map(|stream| AVStream::new(stream))
}
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 read_frame(&mut self, packet: &mut AVPacket) -> Result<(), AVFrameError> {
AVError::from_errno(unsafe { ffi::av_read_frame(self.base, packet.base) })
.map_err(|err| AVFrameError::DecodingFailed(packet.stream_index(), packet.pts(), err))
}
pub fn duration(&self) -> Result<media_time::MediaTime, Error> {
pub fn duration(&self) -> Result<media_time::MediaTime, MediaTimeError> {
media_time::MediaTime::from_rational(
unsafe { (*self.base).duration },
Fraction::new(1 as u64, ffi::AV_TIME_BASE as u64),
&Fraction::new(1 as u64, ffi::AV_TIME_BASE as u64),
)
.map_err(|err| format_err!("error extracting duration: {}", err))
}
}
......@@ -95,22 +117,32 @@ pub struct AVInputFormat<'a> {
base: &'a mut ffi::AVInputFormat,
}
#[derive(Error, Debug)]
pub enum AVInputFormatError {
#[error("AVInputFormat not valid")]
Invalid,
#[error("Field {0} is invalid")]
FieldInaccessible(String, #[source] StringError),
#[error("Mime type invalid: codec {0} in container {1}")]
InvalidMime(String, String),
}
impl<'a> AVInputFormat<'a> {
fn new(base: &'a mut ffi::AVInputFormat) -> Self {
return AVInputFormat { base };
}
pub fn long_name(&self) -> Result<String, Error> {
pub fn long_name(&self) -> Result<String, AVInputFormatError> {
native_string(self.base.long_name)
.map_err(|err| format_err!("Could not access long name for input format: {}", err))
.map_err(|err| AVInputFormatError::FieldInaccessible("long_name".to_string(), err))
}
pub fn name(&self) -> Result<String, Error> {
pub fn name(&self) -> Result<String, AVInputFormatError> {
native_string(self.base.name)
.map_err(|err| format_err!("Could not access short name for input format: {}", err))
.map_err(|err| AVInputFormatError::FieldInaccessible("name".to_string(), err))
}
pub fn determine_mime(&self, stream_codec: impl AsRef<str>) -> Result<&str, Error> {
pub fn determine_mime(&self, stream_codec: impl AsRef<str>) -> Result<&str, AVInputFormatError> {
let containers = self.name()?;
let stream_codec = stream_codec.as_ref();
......@@ -123,11 +155,7 @@ impl<'a> AVInputFormat<'a> {
}
}
bail!(
"Could not determine mime type: {} video in {} container",
stream_codec,
containers
)
Err(AVInputFormatError::InvalidMime(stream_codec.to_string(), containers.to_string()))
}
}
......@@ -137,13 +165,14 @@ pub struct AVBuffer {
}
impl AVBuffer {
pub fn new(size: usize) -> Result<Self, Error> {
pub fn new(size: usize) -> Result<Self, AVAllocError> {
let base = unsafe { ffi::av_malloc(size) } as *mut u8;
if base.is_null() {
bail!("av_malloc() failed");
}
return if base.is_null() {
Err(AVAllocError::AllocFailed("AVBufferContext".to_string()))
} else {
Ok(AVBuffer { base, size })
}
}
pub fn empty() -> Self {
AVBuffer {
......@@ -165,14 +194,21 @@ pub struct AVPacket {
base: *mut ffi::AVPacket,
}
#[derive(Error, Debug)]
pub enum AVPacketError {
#[error(transparent)]
AllocFailed(#[from] AVAllocError),
}
impl AVPacket {
pub fn new() -> Result<Self, Error> {
pub fn new() -> Result<Self, AVAllocError> {
let base = unsafe { ffi::av_packet_alloc() };
if base.is_null() {
bail!("av_packet_alloc() failed");
}
return if base.is_null() {
Err(AVAllocError::AllocFailed("AVPacket".to_string()))
} else {
Ok(AVPacket { base })
}
}
fn as_ref(&self) -> &ffi::AVPacket {
unsafe { self.base.as_ref() }.unwrap_or_else(|| panic!("AVPacket base unexpectedly null"))
......@@ -202,19 +238,25 @@ pub struct AVFrame {
buffer: AVBuffer,
}
#[derive(Error, Debug)]
pub enum AVFrameError {
#[error(transparent)]
AllocFailed(#[from] AVAllocError),
#[error("Decoding a frame from packet of stream {0} at timestamp {1} failed")]
DecodingFailed(i32, i64, #[source] AVError)
}
impl AVFrame {
pub fn new() -> Result<Self, Error> {
pub fn new() -> Result<Self, AVAllocError> {
let base = unsafe { ffi::av_frame_alloc() };
if base.is_null() {
bail!("avformat_alloc_frame() failed");
return if base.is_null() {
Err(AVAllocError::AllocFailed("AVFrame".to_string()))
} else {
Ok(AVFrame { base, buffer: AVBuffer::empty(), })
}
Ok(AVFrame {
base,
buffer: AVBuffer::empty(),
})
}
pub fn init(&mut self, width: i32, height: i32, format: AVPixelFormat) -> Result<(), Error> {
pub fn init(&mut self, width: i32, height: i32, format: AVPixelFormat) -> Result<(), AVFrameError> {
self.as_mut().width = width;
self.as_mut().height = height;
self.as_mut().format = format as ffi::AVPixelFormat;
......@@ -327,12 +369,11 @@ impl<'a> AVStream<'a> {
)
}
pub fn timestamp(&self, timestamp: i64) -> Result<media_time::MediaTime, Error> {
media_time::MediaTime::from_rational(timestamp, self.time_base())
.map_err(|err| format_err!("error extracting timestamp: {}", err))
pub fn timestamp(&self, timestamp: i64) -> Result<media_time::MediaTime, MediaTimeError> {
media_time::MediaTime::from_rational(timestamp, &self.time_base())
}
pub fn duration(&self) -> Result<media_time::MediaTime, Error> {
pub fn duration(&self) -> Result<media_time::MediaTime, MediaTimeError> {
self.timestamp(self.base.duration)
}
......@@ -362,10 +403,10 @@ impl<'a> AVStream<'a> {
)
}
pub fn codec_parameters(&self) -> Result<AVCodecParameters, Error> {
pub fn codec_parameters(&self) -> Result<AVCodecParameters, AVCodecParametersError> {
Ok(AVCodecParameters::new(
unsafe { self.base.codecpar.as_mut() }
.ok_or(format_err!("No AVCodecParameters found"))?,
.ok_or(AVCodecParametersError::Invalid(self.index()))?,
self,
))
}
......@@ -376,6 +417,12 @@ pub struct AVCodecParameters<'a> {
phantom: PhantomData<&'a AVStream<'a>>,
}
#[derive(Error, Debug)]
pub enum AVCodecParametersError {
#[error("AVCodecParameters not valid for stream {0}")]
Invalid(i32),
}
impl<'a> AVCodecParameters<'a> {
fn new(base: &'a mut ffi::AVCodecParameters, _: &'a AVStream) -> Self {
return AVCodecParameters {
......@@ -396,10 +443,10 @@ impl<'a> AVCodecParameters<'a> {
self.base.bit_rate
}
pub fn find_decoder(&self) -> Result<AVCodec, Error> {
pub fn find_decoder(&self) -> Result<AVCodec, AVCodecError> {
Ok(AVCodec::new(
unsafe { ffi::avcodec_find_decoder(self.base.codec_id).as_mut() }
.ok_or(format_err!("No AVCodec found"))?,
.ok_or(AVCodecError::Invalid)?,
self,
))
}
......@@ -410,6 +457,14 @@ pub struct AVCodec<'a> {
phantom: PhantomData<&'a AVCodecParameters<'a>>,
}
#[derive(Error, Debug)]
pub enum AVCodecError {
#[error("AVCodec not valid")]
Invalid,
#[error("Field {0} is invalid")]
FieldInaccessible(String, #[source] StringError),
}
impl<'a> AVCodec<'a> {
fn new(base: &'a mut ffi::AVCodec, _: &'a AVCodecParameters) -> Self {
return AVCodec {
......@@ -418,9 +473,9 @@ impl<'a> AVCodec<'a> {
};
}
pub fn name(&self) -> Result<String, Error> {
pub fn name(&self) -> Result<String, AVCodecError> {
native_string(self.base.name)
.map_err(|err| format_err!("Could not access name for codec: {}", err))
.map_err(|err| AVCodecError::FieldInaccessible("name".to_string(), err))
}
}
......@@ -428,27 +483,36 @@ pub struct AVCodecContext {
base: *mut ffi::AVCodecContext,
}
#[derive(Error, Debug)]
pub enum AVCodecContextError {
#[error(transparent)]
AllocFailed(#[from] AVAllocError),
#[error("Field {0} is invalid")]
FieldInaccessible(String, #[source] StringError),
#[error("Error decoding packet")]
PacketError(#[source] AVError),
#[error("Error decoding frame")]
FrameError(#[source] AVError),
}
impl AVCodecContext {
pub fn new(codec: &AVCodec) -> Result<Self, Error> {
pub fn new(codec: &AVCodec) -> Result<Self, AVAllocError> {
let base = unsafe { ffi::avcodec_alloc_context3(codec.base) };
if base.is_null() {
bail!("avcodec_alloc_context3() failed");
}
Err(AVAllocError::AllocFailed("AVCodecContext".to_string()))
} else {
Ok(AVCodecContext { base })
}
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(format_err!("Error while loading packet: {}", errno)),
}
}
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(format_err!("Error while decoding frame: {}", errno)),
pub fn in_packet(&mut self, packet: &mut AVPacket) -> Result<(), AVCodecContextError> {
AVError::from_errno(unsafe { ffi::avcodec_send_packet(self.base, packet.base) })
.map_err(|err| AVCodecContextError::PacketError(err))
}
pub fn out_frame(&mut self, frame: &mut AVFrame) -> Result<(), AVCodecContextError> {
AVError::from_errno( unsafe { ffi::avcodec_receive_frame(self.base, frame.base) })
.map_err(|err| AVCodecContextError::FrameError(err))
}
fn as_ref(&self) -> &ffi::AVCodecContext {
......@@ -508,6 +572,12 @@ pub struct SwsContext {
base: *mut ffi::SwsContext,
}
#[derive(Error, Debug)]
pub enum SwsContextError {
#[error(transparent)]
AllocFailed(#[from] AVAllocError),
}
impl SwsContext {
pub fn new() -> Self {
SwsContext {
......@@ -521,7 +591,7 @@ impl SwsContext {
target: &AVFrame,
scaler: SwsScaler,
flags: SwsFlags,
) -> Result<(), Error> {
) -> Result<(), AVAllocError> {
let base = unsafe {
ffi::sws_getCachedContext(
self.base,
......@@ -537,13 +607,14 @@ impl SwsContext {
std::ptr::null(),
)
};
if base.is_null() {
bail!("sws_getCachedContext() failed");
}
Err(AVAllocError::AllocFailed("SwsContext".to_string()))
} else {
self.base = base;
Ok(())
}
}
pub fn scale(&self, source: &AVFrame, target: &mut AVFrame) -> i32 {
self.scale_slice(source, target, 0, source.height())
......
This diff is collapsed.
use num_traits::FromPrimitive;
use thiserror::Error;
use crate::err_ffi::AvFfiError;
use crate::err_av::AvInternalError;
#[derive(Error, Debug, Copy, Clone, PartialEq)]
pub enum AVError {
#[error(transparent)]
Internal(AvInternalError),
#[error(transparent)]
System(AvFfiError),
#[error("Unknown error occured: {0}")]
Unknown(i32),
}
impl AVError {
pub fn from_errno(errno: i32) -> Result<(), AVError> {
match errno {
0 => Ok(()),
errno => Err(AVError::from(-errno)),
}
}
}
impl From<i32> for AVError {
fn from(value: i32) -> Self {
if let Some(error) = AvFfiError::from_i32(value) {
return AVError::System(error);
}
if let Some(error) = AvInternalError::from_i32(value) {
return AVError::Internal(error);
}
return AVError::Unknown(value);
}
}
\ No newline at end of file
use num_derive::FromPrimitive;
use thiserror::Error;
#[derive(Error, Debug, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
#[repr(i32)]
pub enum AvInternalError {
#[error("Bitstream filter not found")]
BitstreamFilterNotFound = 0x465342F8,
#[error("Internal bug, also see AVERROR_BUG2")]
Bug = 0x21475542,
#[error("Internal bug, also see AVERROR_BUG")]
Bug2 = 0x20475542,
#[error("Buffer too small")]
BufferTooSmall = 0x53465542,
#[error("Decoder not found")]
DecoderNotFound = 0x434544F8,
#[error("Demuxer not found")]
DemuxerNotFound = 0x4D4544F8,
#[error("Encoder not found")]
EncoderNotFound = 0x434E45F8,
#[error("End of file")]
EndOfFile = 0x20464F45,
#[error("Immediate exit was requested; the called function should not be restarted")]
Exit = 0x54495845,
#[error("Generic error in an external library")]
External = 0x20545845,
#[error("Filter not found")]
FilterNotFound = 0x4C4946F8,
#[error("Input changed between calls. Reconfiguration is required.")]
InputChanged = 0x636e6701,
#[error("Invalid data found when processing input")]
InvalidData = 0x41444E49,
#[error("Muxer not found")]
MuxerNotFound = 0x58554DF8,
#[error("Option not found")]
OptionNotFound = 0x54504FF8,
#[error("Output changed between calls. Reconfiguration is required.")]
OutputChanged = 0x636e6702,
#[error("Not yet implemented in FFmpeg, patches welcome")]
NotImplemented = 0x45574150,
#[error("Protocol not found")]
ProtocolNotFound = 0x4F5250F8,
#[error("Stream not found")]
StreamNotFound = 0x525453F8,
#[error("Unknown error, typically from an external library")]
Unknown = 0x4E4B4E55,
#[error("Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it.")]
Experimental = 0x2bb2afa8,
#[error("Input and output changed between calls. Reconfiguration is required.")]
InputAndOutputChanged = 0x636e6703,
#[error("HTTP: Bad Request")]
HttpBadRequest = 0x303034F8,
#[error("HTTP: Unauthorized")]
HttpUnauthorized = 0x313034F8,
#[error("HTTP: Forbidden")]
HttpForbidden = 0x333034F8,
#[error("HTTP: Not Found")]
HttpNotFound = 0x343034F8,
#[error("HTTP: Other 4xx error")]
HttpOther4xx = 0x585834F8,
#[error("HTTP: Other 5xx error")]
HttpServerError = 0x585835F8,
}
\ No newline at end of file
use num_derive::FromPrimitive;
use thiserror::Error;
#[derive(Error, Debug, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
#[repr(i32)]
pub enum AvFfiError {
#[error("Operation not permitted")]
OperationNotPermitted = 1,
#[error("No such file or directory")]
NoSuchFileOrDirectory = 2,
#[error("No such process")]
NoSuchProcess = 3,
#[error("Interrupted system call")]
InterruptedSystemCall = 4,
#[error("I/O error")]
IoError = 5,
#[error("No such device or address")]
NoSuchDeviceOrAddress = 6,
#[error("Argument list too long")]
ArgumentListTooLong = 7,
#[error("Exec format error")]
ExecFormatError = 8,
#[error("Bad file number")]
BadFileNumber = 9,
#[error("No child processes")]
NoChildProcesses = 10,
#[error("Try again")]
TryAgain = 11,
#[error("Out of memory")]
OutOfMemory = 12,
#[error("Permission denied")]
PermissionDenied = 13,
#[error("Bad address")]
BadAddress = 14,
#[error("Block device required")]
BlockDeviceRequired = 15,
#[error("Device or resource busy")]
DeviceOrResourceBusy = 16,
#[error("File exists")]
FileExists = 17,
#[error("Cross-device link")]
CrossDeviceLink = 18,
#[error("No such device")]
NoSuchDevice = 19,
#[error("Not a directory")]
NotDirectory = 20,
#[error("Is a directory")]
Directory = 21,
#[error("Invalid argument")]
InvalidArgument = 22,
#[error("File table overflow")]
FileTableOverflow = 23,
#[error("Too many open files")]
TooManyOpenFiles = 24,
#[error("Not a typewriter")]
NotATypewriter = 25,
#[error("Text file busy")]
TextFileBusy = 26,
#[error("File too large")]
FileTooLarge = 27,
#[error("No space left on device")]
NoSpaceLeftOnDevice = 28,
#[error("Illegal seek")]
IllegalSeek = 29,
#[error("Read-only file system")]
ReadOnlyFileSystem = 30,
#[error("Too many links")]
TooManyLinks = 31,
#[error("Broken pipe")]
BrokenPipe = 32,
#[error("Math argument out of domain of func")]
MathArgumentOutOfDomain = 33,
#[error("Math result not representable")]
MathResultNotRepresentable = 34,
#[error("Resource deadlock would occur")]
ResourceDeadlock = 35,
#[error("File name too long")]
FileNameTooLong = 36,
#[error("No record locks available")]
NoRecordLocksAvailable = 37,
#[error("Invalid system call number")]
InvalidSystemCall = 38,
#[error("Directory not empty")]
DirectoryNotEmpty = 39,
#[error("Too many symbolic links encountered")]
TooManySymbolicLinks = 40,
#[error("No message of desired type")]
NoMessageOfDesiredType = 42,
#[error("Identifier removed")]
IdentifierRemoved = 43,
#[error("Channel number out of range")]
ChannelNumberOutOfRange = 44,
#[error("Level 2 not synchronized")]
Level2NotSynchronized = 45,
#[error("Level 3 halted")]
Level3Halted = 46,
#[error("Level 3 reset")]
Level3Reset = 47,
#[error("Link number out of range")]
LinkNumberOutOfRange = 48,
#[error("Protocol driver not attached")]
ProtocolDriverNotAttached = 49,
#[error("No CSI structure available")]
NoCsiStructureAvailable = 50,
#[error("Level 2 halted")]
Level2Halted = 51,
#[error("Invalid exchange")]
InvalidExchange = 52,
#[error("Invalid request descriptor")]
InvalidRequestDescriptor = 53,
#[error("Exchange full")]
ExchangeFull = 54,
#[error("No anode")]
NoAnode = 55,
#[error("Invalid request code")]
InvalidRequestCode = 56,
#[error("Invalid slot")]
InvalidSlot = 57,
#[error("Bad font file format")]
BadFontFileFormat = 59,
#[error("Device not a stream")]
DeviceNotAStream = 60,
#[error("No data available")]
NoDataAvailable = 61,
#[error("Timer expired")]
TimerExpired = 62,
#[error("Out of streams resources")]
OutOfStreamsResources = 63,
#[error("Machine is not on the network")]
MachineNotOnNetwork = 64,
#[error("Package not installed")]
PackageNotInstalled = 65,
#[error("Object is remote")]
ObjectRemote = 66,
#[error("Link has been severed")]
LinkSevered = 67,
#[error("Advertise error")]
AdvertiseError = 68,
#[error("Srmount error")]
SrmountError = 69,
#[error("Communication error on send")]
CommunicationErrorOnSend = 70,
#[error("Protocol error")]
ProtocolError = 71,
#[error("Multihop attempted")]
MultihopAttempted = 72,
#[error("RFS specific error")]
RfsSpecificError = 73,
#[error("Not a data message")]
NotADataMessage = 74,
#[error("Value too large for defined data type")]
ValueTooLarge = 75,
#[error("Name not unique on network")]
NameNotUniqueOnNetwork = 76,
#[error("File descriptor in bad state")]
FileDescriptorInBadState = 77,
#[error("Remote address changed")]
RemoteAddressChanged = 78,
#[error("Can not access a needed shared library")]
CanNotAccessSharedLibrary = 79,
#[error("Accessing a corrupted shared library")]
CorruptedSharedLibrary = 80,
#[error(".lib section in a.out corrupted")]
LibSectionCorrupted = 81,
#[error("Attempting to link in too many shared libraries")]
TooManySharedLibraries = 82,
#[error("Cannot exec a shared library directly")]
CannotExecASharedLibraryDirectly = 83,
#[error("Illegal byte sequence")]
IllegalByteSequence = 84,
#[error("Interrupted system call should be restarted")]
RestartInterruptedSystemCall = 85,
#[error("Streams pipe error")]
StreamsPipeError = 86,
#[error("Too many users")]
TooManyUsers = 87,
#[error("Socket operation on non-socket")]
SocketOperationOnNonSocket = 88,
#[error("Destination address required")]
DestinationAddressRequired = 89,
#[error("Message too long")]
MessageTooLong = 90,
#[error("Protocol wrong type for socket")]
ProtocolWrongTypeForSocket = 91,
#[error("Protocol not available")]
ProtocolNotAvailable = 92,
#[error("Protocol not supported")]
ProtocolNotSupported = 93,
#[error("Socket type not supported")]
SocketTypeNotSupported = 94,
#[error("Operation not supported on transport endpoint")]
OperationNotSupportedOnTransportEndpoint = 95,
#[error("Protocol family not supported")]
ProtocolFamilyNotSupported = 96,
#[error("Address family not supported by protocol")]
AddressFamilyNotSupportedByProtocol = 97,
#[error("Address already in use")]
AddressAlreadyInUse = 98,
#[error("Cannot assign requested address")]
CannotAssignRequestedAddress = 99,
#[error("Network is down")]
NetworkDown = 100,
#[error("Network is unreachable")]
NetworkUnreachable = 101,
#[error("Network dropped connection because of reset")]
ConnectionReset = 102,
#[error("Software caused connection abort")]
ConnectionAbort = 103,
#[error("Connection reset by peer")]
ConnectionResetByPeer = 104,
#[error("No buffer space available")]
NoBufferSpaceAvailable = 105,
#[error("Transport endpoint is already connected")]
TransportEndpointAlreadyConnected = 106,
#[error("Transport endpoint is not connected")]
TransportEndpointNotConnected = 107,
#[error("Cannot send after transport endpoint shutdown")]
CannotSendAfterTransportEndpointShutdown = 108,
#[error("Too many references: cannot splice")]
TooManyReferences = 109,
#[error("Connection timed out")]
ConnectionTimedOut = 110,
#[error("Connection refused")]
ConnectionRefused = 111,
#[error("Host is down")]
HostDown = 112,
#[error("No route to host")]
NoRouteToHost = 113,
#[error("Operation already in progress")]
OperationAlreadyInProgress = 114,
#[error("Operation now in progress")]
OperationNowInProgress = 115,
#[error("Stale file handle")]
StaleFileHandle = 116,
#[error("Structure needs cleaning")]
StructureNeedsCleaning = 117,
#[error("Not a XENIX named type file")]
NotXenixNamedTypeFile = 118,
#[error("No XENIX semaphores available")]
NoXenixSemaphoresAvailable = 119,
#[error("Is a named type file")]
NamedTypeFile = 120,
#[error("Remote I/O error")]
RemoteIoError = 121,
#[error("Quota exceeded")]
QuotaExceeded = 122,
#[error("No medium found")]
NoMediumFound = 123,
#[error("Wrong medium type")]
WrongMediumType = 124,
#[error("Operation Canceled")]
OperationCanceled = 125,
#[error("Required key not available")]
RequiredKeyNotAvailable = 126,
#[error("Key has expired")]
KeyExpired = 127,
#[error("Key has been revoked")]
KeyRevoked = 128,
#[error("Key was rejected by service")]
KeyRejectedByService = 129,
#[error("Owner died")]
OwnerDied = 130,
#[error("State not recoverable")]
StateNotRecoverable = 131,
#[error("Operation not possible due to RF-kill")]
OperationNotPossibleDueToRfKill = 132,
#[error("Memory page has hardware error")]
MemoryPageHardwareError = 133,
}
\ No newline at end of file
#[macro_use]
extern crate num_derive;
#[cfg(test)]
mod tests;
pub mod api;
pub mod enums;
pub mod err;
pub mod err_ffi;
pub mod err_av;
......@@ -9,3 +9,4 @@ edition = "2018"
[dependencies]
fraction = "0.6.2"
time = "0.2"
thiserror = "1.0"
\ No newline at end of file
use std::error;
use std::fmt;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TimeBaseError;
impl fmt::Display for TimeBaseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid time base")
}
}
impl error::Error for TimeBaseError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[cfg(test)]
mod tests;
mod errors;
mod media_time;
pub use errors::*;
pub use media_time::MediaTime;
pub use media_time::*;
use crate::errors::TimeBaseError;
use fraction::Fraction;
use thiserror::Error;
type Result<T> = std::result::Result<T, TimeBaseError>;
#[derive(Error, Debug)]
pub enum MediaTimeError {
#[error("missing numerator in timebase")]
TimebaseNumeratorMissing,
#[error("missing denominator in timebase")]
TimebaseDenominatorMissing,
#[error("invalid denominator in timebase")]
TimebaseDenominatorInvalid,
}
#[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> {
let num: u64 = *base.numer().ok_or(TimeBaseError)?;
let den: u64 = *base.denom().ok_or(TimeBaseError)?;
pub fn from_rational(timestamp: i64, base: &Fraction) -> Result<MediaTime, MediaTimeError> {
let num: u64 = *base.numer().ok_or(MediaTimeError::TimebaseNumeratorMissing)?;
let den: u64 = *base.denom().ok_or(MediaTimeError::TimebaseDenominatorMissing)?;
if den == 0 {
return Err(MediaTimeError::TimebaseDenominatorInvalid)
}
Ok(MediaTime(time::Duration::milliseconds(
(1000 * timestamp as i128 * num as i128 / den as i128) as i64,
......
......@@ -8,3 +8,4 @@ edition = "2018"
[dependencies]
media_time = { path = "../media_time" }
thiserror = "1.0"
\ No newline at end of file
use std::fs::File;
use std::io::prelude::*;
use std::io::LineWriter;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::string::String;
use media_time::MediaTime;
use thiserror::Error;
type Result<T> = std::result::Result<T, std::io::Error>;
use media_time::MediaTime;
pub struct WebVTTFile {
cues: Vec<WebVTTCue>,
......@@ -18,6 +18,12 @@ pub struct WebVTTCue {
payload: String,
}
#[derive(Error, Debug)]
pub enum WebVTTError {
#[error("Error saving file {0}")]
IoError(PathBuf, #[source] std::io::Error),
}
impl WebVTTFile {
pub fn new() -> WebVTTFile {
WebVTTFile { cues: Vec::new() }
......@@ -27,7 +33,7 @@ impl WebVTTFile {
self.cues.push(cue);
}
pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
fn save_impl(&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")?;
......@@ -37,6 +43,11 @@ impl WebVTTFile {
file.flush()?;
Ok(())
}
pub fn save(&self, path: impl AsRef<Path>) -> Result<(), WebVTTError> {
self.save_impl(&path.as_ref())
.map_err(|err| WebVTTError::IoError(path.as_ref().to_path_buf(), err))
}
}
impl WebVTTCue {
......@@ -48,7 +59,7 @@ impl WebVTTCue {
}
}
fn save(&self, writer: &mut LineWriter<File>) -> Result<()> {
fn save(&self, writer: &mut LineWriter<File>) -> Result<(), std::io::Error> {
writer.write_all(format!("{} --> {}\n", self.start, self.end).as_bytes())?;
writer.write_all(self.payload.as_bytes())?;
writer.write_all(b"\n\n")?;
......
......@@ -5,7 +5,7 @@ pub mod stream_metadata;
use std::path::Path;
use failure::{format_err, Error};
use anyhow::format_err;
use ffmpeg_api::api::*;
use ffmpeg_api::enums::*;
use image::ImageOutputFormat;
......@@ -20,12 +20,9 @@ pub fn extract(
format: ImageOutputFormat,
scaler: SwsScaler,
flags: SwsFlags,
) -> Result<(), Error> {
let mut avformat_context = AVFormatContext::new()
.map_err(|error| format_err!("Could not open video input: {}", error))?;
avformat_context
.open_input(input_file)
.map_err(|error| format_err!("Could not open video input: {}", error))?;
) -> anyhow::Result<()> {
let mut avformat_context = AVFormatContext::new()?;
avformat_context.open_input(input_file)?;
let duration = avformat_context.duration()?;
let spritesheet_path = output_folder.join("spritesheets");
......@@ -100,7 +97,7 @@ pub fn extract(
.in_packet(&mut packet)
.map_err(|error| format_err!("Could not load packet: {}", error))?;
while codec_context.out_frame(&mut frame).is_ok() {
let timestamp = media_time::MediaTime::from_rational(frame.pts(), time_base)?;
let timestamp = media_time::MediaTime::from_rational(frame.pts(), &time_base)?;
println!(
"Frame {}: {} @ {}",
......
use std::path::Path;
use failure::Error;
use ffmpeg_api::enums::{SwsFlags, SwsScaler};
use image::ImageOutputFormat;
use media_time::MediaTime;
......@@ -50,7 +49,7 @@ struct Options {
fast_scaling: bool,
}
fn main() -> Result<(), Error> {
fn main() -> anyhow::Result<()> {
let options = Options::from_args();
let mut flags = SwsFlags::empty();
......@@ -64,7 +63,7 @@ fn main() -> Result<(), Error> {
flags |= SwsFlags::BIT_EXACT_SCALING;
}
media_ingestion::extract(
if let Err(err) = media_ingestion::extract(
options.max_size,
options.num_horizontal,
options.num_vertical,
......@@ -79,7 +78,9 @@ fn main() -> Result<(), Error> {
},
options.scaler,
flags,
)?;
) {
eprintln!("Error: {}", err)
}
Ok(())
}
......@@ -2,7 +2,7 @@ use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
use failure::{bail, format_err, Error};
use anyhow::{bail, format_err, Error};
use image::{DynamicImage, ImageOutputFormat, RgbImage};
use media_time::MediaTime;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment