explicitly test ffmpeg library compatibility

Makes the problem in #11 more obvious.
This commit is contained in:
Scott Lamb 2017-10-24 07:26:18 -07:00
parent 99f10dfba6
commit b9ebb74a58
2 changed files with 91 additions and 15 deletions

8
ffmpeg/Cargo.lock generated
View File

@ -2,15 +2,15 @@
name = "moonfire-ffmpeg" name = "moonfire-ffmpeg"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "gcc" name = "cc"
version = "0.3.51" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -29,7 +29,7 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a" "checksum cc 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ef4019bdb99c0c1ddd56c12c2f507c174d729c6915eca6bd9d27c42f3d93b0f4"
"checksum libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "38f5c2b18a287cf78b4097db62e20f43cace381dc76ae5c0a3073067f78b7ddc" "checksum libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "38f5c2b18a287cf78b4097db62e20f43cace381dc76ae5c0a3073067f78b7ddc"
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"

View File

@ -33,7 +33,7 @@ extern crate libc;
use std::cell::{Ref, RefCell}; use std::cell::{Ref, RefCell};
use std::ffi::CStr; use std::ffi::CStr;
use std::fmt; use std::fmt::{self, Write};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ptr; use std::ptr;
use std::sync; use std::sync;
@ -366,14 +366,47 @@ impl fmt::Display for Error {
} }
} }
#[derive(Copy, Clone)]
struct Version(libc::c_int); struct Version(libc::c_int);
impl Version {
fn major(self) -> libc::c_int { (self.0 >> 16) & 0xFF }
fn minor(self) -> libc::c_int { (self.0 >> 8) & 0xFF }
}
impl fmt::Display for Version { impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{}.{}", (self.0 >> 16) & 0xFF, (self.0 >> 8) & 0xFF, self.0 & 0xFF) write!(f, "{}.{}.{}", (self.0 >> 16) & 0xFF, (self.0 >> 8) & 0xFF, self.0 & 0xFF)
} }
} }
struct Library {
name: &'static str,
compiled: Version,
running: Version,
}
impl Library {
fn new(name: &'static str, compiled: libc::c_int, running: libc::c_int) -> Self {
Library {
name,
compiled: Version(compiled),
running: Version(running),
}
}
fn is_compatible(&self) -> bool {
self.running.major() == self.compiled.major() &&
self.running.minor() >= self.compiled.minor()
}
}
impl fmt::Display for Library {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: running={} compiled={}", self.name, self.running, self.compiled)
}
}
pub struct Dictionary(*mut AVDictionary); pub struct Dictionary(*mut AVDictionary);
impl Dictionary { impl Dictionary {
@ -416,21 +449,38 @@ impl Drop for Dictionary {
impl Ffmpeg { impl Ffmpeg {
pub fn new() -> Ffmpeg { pub fn new() -> Ffmpeg {
START.call_once(|| unsafe { START.call_once(|| unsafe {
let libs = &[
Library::new("avutil", moonfire_ffmpeg_compiled_libavutil_version,
avutil_version()),
Library::new("avcodec", moonfire_ffmpeg_compiled_libavcodec_version,
avcodec_version()),
Library::new("avformat", moonfire_ffmpeg_compiled_libavformat_version,
avformat_version()),
];
let mut msg = String::new();
let mut compatible = true;
for l in libs {
write!(&mut msg, "\n{}", l).unwrap();
if !l.is_compatible() {
compatible = false;
msg.push_str(" <- not ABI-compatible!");
}
}
if !compatible {
panic!("Incompatible ffmpeg versions:{}", msg);
}
for l in libs {
if !l.is_compatible() {
panic!("Library {}'s running version {} isn't ABI-compatible with \
compiled version {}!", l.name, l.running, l.compiled);
}
}
moonfire_ffmpeg_init(); moonfire_ffmpeg_init();
av_register_all(); av_register_all();
if avformat_network_init() < 0 { if avformat_network_init() < 0 {
panic!("avformat_network_init failed"); panic!("avformat_network_init failed");
} }
info!("Initialized ffmpeg. Versions:\n\ info!("Initialized ffmpeg. Versions:{}", msg);
avcodec compiled={} running={}\n\
avformat compiled={} running={}\n\
avutil compiled={} running={}",
Version(moonfire_ffmpeg_compiled_libavcodec_version),
Version(avcodec_version()),
Version(moonfire_ffmpeg_compiled_libavformat_version),
Version(avformat_version()),
Version(moonfire_ffmpeg_compiled_libavutil_version),
Version(avutil_version()));
}); });
Ffmpeg{} Ffmpeg{}
} }
@ -441,6 +491,32 @@ mod tests {
use std::ffi::CString; use std::ffi::CString;
use super::Error; use super::Error;
/// Just tests that this doesn't crash with an ABI compatibility error.
#[test]
fn test_init() { super::Ffmpeg::new(); }
#[test]
fn test_is_compatible() {
// compiled major/minor/patch, running major/minor/patch, expected compatible
use ::libc::c_int;
struct Test(c_int, c_int, c_int, c_int, c_int, c_int, bool);
let tests = &[
Test(55, 1, 2, 55, 1, 2, true), // same version, compatible
Test(55, 1, 2, 55, 2, 1, true), // newer minor version, compatible
Test(55, 1, 3, 55, 1, 2, true), // older patch version, compatible (but weird)
Test(55, 2, 2, 55, 1, 2, false), // older minor version, incompatible
Test(55, 1, 2, 56, 1, 2, false), // newer major version, incompatible
Test(56, 1, 2, 55, 1, 2, false), // older major version, incompatible
];
for t in tests {
let l = super::Library::new(
"avutil", (t.0 << 16) | (t.1 << 8) | t.2, (t.3 << 16) | (t.4 << 8) | t.5);
assert!(l.is_compatible() == t.6, "{} expected={}", l, t.6);
}
}
#[test] #[test]
fn test_error() { fn test_error() {
let eof_formatted = format!("{}", Error::eof()); let eof_formatted = format!("{}", Error::eof());