diff --git a/Cargo.lock b/Cargo.lock index a3608ec..2685073 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1300,6 +1300,7 @@ dependencies = [ [[package]] name = "moonfire-ffmpeg" version = "0.0.1" +source = "git+https://github.com/scottlamb/moonfire-ffmpeg#c517aa782867c8b882524a2d21361753ca845f06" dependencies = [ "cc", "libc", diff --git a/Cargo.toml b/Cargo.toml index 3898be2..435d7d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ nightly = ["db/nightly", "parking_lot/nightly", "smallvec/union"] bundled = ["rusqlite/bundled"] [workspace] -members = ["base", "db", "ffmpeg"] +members = ["base", "db"] [dependencies] base = { package = "moonfire-base", path = "base" } @@ -28,7 +28,7 @@ cursive = "0.14.0" db = { package = "moonfire-db", path = "db" } docopt = "1.0" failure = "0.1.1" -ffmpeg = { package = "moonfire-ffmpeg", path = "ffmpeg" } +ffmpeg = { package = "moonfire-ffmpeg", git = "https://github.com/scottlamb/moonfire-ffmpeg" } futures = "0.3" fnv = "1.0" h264-reader = { git = "https://github.com/dholroyd/h264-reader" } diff --git a/ffmpeg/Cargo.toml b/ffmpeg/Cargo.toml deleted file mode 100644 index 16df0e6..0000000 --- a/ffmpeg/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "moonfire-ffmpeg" -version = "0.0.1" -authors = ["Scott Lamb "] -readme = "../README.md" -edition = "2018" -#links = "ffmpeg" - -[lib] -path = "lib.rs" - -[dependencies] -libc = "0.2" -log = { version = "0.4", features = ["release_max_level_info"] } -parking_lot = { version = "0.9", features = [] } - -[build-dependencies] -cc = "1.0" -pkg-config = "0.3" diff --git a/ffmpeg/build.rs b/ffmpeg/build.rs deleted file mode 100644 index 345b821..0000000 --- a/ffmpeg/build.rs +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of Moonfire NVR, a security camera network video recorder. -// Copyright (C) 2017 The Moonfire NVR Authors -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations including -// the two. -// -// You must obey the GNU General Public License in all respects for all -// of the code used other than OpenSSL. If you modify file(s) with this -// exception, you may extend this exception to your version of the -// file(s), but you are not obligated to do so. If you do not wish to do -// so, delete this exception statement from your version. If you delete -// this exception statement from all source files in the program, then -// also delete it here. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -fn main() { - let libraries = [ - pkg_config::Config::new().atleast_version("54.1").probe("libavutil").unwrap(), - pkg_config::Config::new().atleast_version("56.0").probe("libavcodec").unwrap(), - pkg_config::Config::new().atleast_version("57.5").probe("libavformat").unwrap(), - ]; - let mut wrapper = cc::Build::new(); - - for lib in &libraries { - // Pass include paths on to gcc. It'd be nice if pkg-config allowed fetching CFLAGS and - // passing that on; see . But - // the include paths are likely all that's included/significant for compilation. - for p in &lib.include_paths { - wrapper.include(p); - } - } - - wrapper.file("wrapper.c").compile("libwrapper.a"); -} diff --git a/ffmpeg/lib.rs b/ffmpeg/lib.rs deleted file mode 100644 index 76cf8fd..0000000 --- a/ffmpeg/lib.rs +++ /dev/null @@ -1,504 +0,0 @@ -// This file is part of Moonfire NVR, a security camera network video recorder. -// Copyright (C) 2017 The Moonfire NVR Authors -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations including -// the two. -// -// You must obey the GNU General Public License in all respects for all -// of the code used other than OpenSSL. If you modify file(s) with this -// exception, you may extend this exception to your version of the -// file(s), but you are not obligated to do so. If you do not wish to do -// so, delete this exception statement from your version. If you delete -// this exception statement from all source files in the program, then -// also delete it here. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use log::info; -use parking_lot::Once; -use std::cell::{Ref, RefCell}; -use std::ffi::CStr; -use std::fmt::{self, Write}; -use std::ptr; - -static START: Once = Once::new(); - -//#[link(name = "avcodec")] -extern "C" { - fn avcodec_version() -> libc::c_int; - fn av_init_packet(p: *mut AVPacket); - fn av_packet_unref(p: *mut AVPacket); - - fn moonfire_ffmpeg_codecpar_codec_id(ctx: *const AVCodecParameters) -> libc::c_int; - fn moonfire_ffmpeg_codecpar_codec_type(ctx: *const AVCodecParameters) -> libc::c_int; - fn moonfire_ffmpeg_codecpar_extradata(ctx: *const AVCodecParameters) -> DataLen; - fn moonfire_ffmpeg_codecpar_height(ctx: *const AVCodecParameters) -> libc::c_int; - fn moonfire_ffmpeg_codecpar_width(ctx: *const AVCodecParameters) -> libc::c_int; -} - -//#[link(name = "avformat")] -extern "C" { - fn avformat_version() -> libc::c_int; - - fn avformat_open_input(ctx: *mut *mut AVFormatContext, url: *const libc::c_char, - fmt: *const AVInputFormat, options: *mut *mut AVDictionary) - -> libc::c_int; - fn avformat_close_input(ctx: *mut *mut AVFormatContext); - fn avformat_find_stream_info(ctx: *mut AVFormatContext, options: *mut *mut AVDictionary) - -> libc::c_int; - fn av_read_frame(ctx: *mut AVFormatContext, p: *mut AVPacket) -> libc::c_int; - fn av_register_all(); - fn avformat_network_init() -> libc::c_int; - - fn moonfire_ffmpeg_fctx_streams(ctx: *const AVFormatContext) -> StreamsLen; - - fn moonfire_ffmpeg_stream_codecpar(stream: *const AVStream) -> *const AVCodecParameters; - fn moonfire_ffmpeg_stream_time_base(stream: *const AVStream) -> AVRational; -} - -//#[link(name = "avutil")] -extern "C" { - fn avutil_version() -> libc::c_int; - fn av_strerror(e: libc::c_int, b: *mut libc::c_char, s: libc::size_t) -> libc::c_int; - fn av_dict_count(d: *const AVDictionary) -> libc::c_int; - fn av_dict_get(d: *const AVDictionary, key: *const libc::c_char, prev: *mut AVDictionaryEntry, - flags: libc::c_int) -> *mut AVDictionaryEntry; - fn av_dict_set(d: *mut *mut AVDictionary, key: *const libc::c_char, value: *const libc::c_char, - flags: libc::c_int) -> libc::c_int; - fn av_dict_free(d: *mut *mut AVDictionary); -} - -//#[link(name = "wrapper")] -extern "C" { - static moonfire_ffmpeg_compiled_libavcodec_version: libc::c_int; - static moonfire_ffmpeg_compiled_libavformat_version: libc::c_int; - static moonfire_ffmpeg_compiled_libavutil_version: libc::c_int; - static moonfire_ffmpeg_av_dict_ignore_suffix: libc::c_int; - static moonfire_ffmpeg_av_nopts_value: i64; - - static moonfire_ffmpeg_av_codec_id_h264: libc::c_int; - static moonfire_ffmpeg_avmedia_type_video: libc::c_int; - - static moonfire_ffmpeg_averror_eof: libc::c_int; - - fn moonfire_ffmpeg_init(); - - fn moonfire_ffmpeg_packet_alloc() -> *mut AVPacket; - fn moonfire_ffmpeg_packet_free(p: *mut AVPacket); - fn moonfire_ffmpeg_packet_is_key(p: *const AVPacket) -> bool; - fn moonfire_ffmpeg_packet_pts(p: *const AVPacket) -> i64; - fn moonfire_ffmpeg_packet_dts(p: *const AVPacket) -> i64; - fn moonfire_ffmpeg_packet_duration(p: *const AVPacket) -> libc::c_int; - fn moonfire_ffmpeg_packet_set_pts(p: *mut AVPacket, pts: i64); - fn moonfire_ffmpeg_packet_set_dts(p: *mut AVPacket, dts: i64); - fn moonfire_ffmpeg_packet_set_duration(p: *mut AVPacket, dur: libc::c_int); - fn moonfire_ffmpeg_packet_data(p: *const AVPacket) -> DataLen; - fn moonfire_ffmpeg_packet_stream_index(p: *const AVPacket) -> libc::c_uint; -} - -pub struct Ffmpeg {} - -// No accessors here; seems reasonable to assume ABI stability of this simple struct. -#[repr(C)] -struct AVDictionaryEntry { - key: *mut libc::c_char, - value: *mut libc::c_char, -} - -// Likewise, seems reasonable to assume this struct has a stable ABI. -#[repr(C)] -pub struct AVRational { - pub num: libc::c_int, - pub den: libc::c_int, -} - -// No ABI stability assumption here; use heap allocation/deallocation and accessors only. -enum AVCodecParameters {} -enum AVDictionary {} -enum AVFormatContext {} -enum AVInputFormat {} -enum AVPacket {} -enum AVStream {} - -pub struct InputFormatContext { - ctx: *mut AVFormatContext, - pkt: RefCell<*mut AVPacket>, -} - -impl InputFormatContext { - pub fn open(source: &CStr, dict: &mut Dictionary) -> Result { - let mut ctx = ptr::null_mut(); - Error::wrap(unsafe { - avformat_open_input(&mut ctx, source.as_ptr(), ptr::null(), &mut dict.0) - })?; - let pkt = unsafe { moonfire_ffmpeg_packet_alloc() }; - if pkt.is_null() { - panic!("malloc failed"); - } - unsafe { av_init_packet(pkt) }; - Ok(InputFormatContext{ - ctx, - pkt: RefCell::new(pkt), - }) - } - - pub fn find_stream_info(&mut self) -> Result<(), Error> { - Error::wrap(unsafe { avformat_find_stream_info(self.ctx, ptr::null_mut()) }) - } - - // XXX: non-mut because of lexical lifetime woes in the caller. This is also why we need a - // RefCell. - pub fn read_frame<'i>(&'i self) -> Result, Error> { - let pkt = self.pkt.borrow(); - Error::wrap(unsafe { av_read_frame(self.ctx, *pkt) })?; - Ok(Packet(pkt)) - } - - pub fn streams<'i>(&'i self) -> Streams<'i> { - Streams(unsafe { - let s = moonfire_ffmpeg_fctx_streams(self.ctx); - std::slice::from_raw_parts(s.streams, s.len as usize) - }) - } -} - -unsafe impl Send for InputFormatContext {} - -impl Drop for InputFormatContext { - fn drop(&mut self) { - unsafe { - moonfire_ffmpeg_packet_free(*self.pkt.borrow()); - avformat_close_input(&mut self.ctx); - } - } -} - -// matches moonfire_ffmpeg_data_len -#[repr(C)] -struct DataLen { - data: *const u8, - len: libc::size_t, -} - -// matches moonfire_ffmpeg_streams_len -#[repr(C)] -struct StreamsLen { - streams: *const *const AVStream, - len: libc::size_t, -} - -pub struct Packet<'i>(Ref<'i, *mut AVPacket>); - -impl<'i> Packet<'i> { - pub fn is_key(&self) -> bool { unsafe { moonfire_ffmpeg_packet_is_key(*self.0) } } - pub fn pts(&self) -> Option { - match unsafe { moonfire_ffmpeg_packet_pts(*self.0) } { - v if v == unsafe { moonfire_ffmpeg_av_nopts_value } => None, - v => Some(v), - } - } - pub fn set_pts(&mut self, pts: Option) { - let real_pts = match pts { - None => unsafe { moonfire_ffmpeg_av_nopts_value }, - Some(v) => v, - }; - unsafe { moonfire_ffmpeg_packet_set_pts(*self.0, real_pts); } - } - pub fn dts(&self) -> i64 { unsafe { moonfire_ffmpeg_packet_dts(*self.0) } } - pub fn set_dts(&mut self, dts: i64) { - unsafe { moonfire_ffmpeg_packet_set_dts(*self.0, dts); } - } - pub fn duration(&self) -> i32 { unsafe { moonfire_ffmpeg_packet_duration(*self.0) } } - pub fn set_duration(&mut self, dur: i32) { - unsafe { moonfire_ffmpeg_packet_set_duration(*self.0, dur) } - } - pub fn stream_index(&self) -> usize { - unsafe { moonfire_ffmpeg_packet_stream_index(*self.0) as usize } - } - pub fn data(&self) -> Option<&[u8]> { - unsafe { - let d = moonfire_ffmpeg_packet_data(*self.0); - if d.data.is_null() { - None - } else { - Some(::std::slice::from_raw_parts(d.data, d.len)) - } - } - } -} - -impl<'i> Drop for Packet<'i> { - fn drop(&mut self) { - unsafe { - av_packet_unref(*self.0); - } - } -} - -pub struct Streams<'owner>(&'owner [*const AVStream]); - -impl<'owner> Streams<'owner> { - pub fn get(&self, i: usize) -> Stream<'owner> { Stream(unsafe { self.0[i].as_ref() }.unwrap()) } - pub fn len(&self) -> usize { self.0.len() } -} - -pub struct Stream<'o>(&'o AVStream); - -impl<'o> Stream<'o> { - pub fn codecpar<'s>(&'s self) -> CodecParameters<'s> { - CodecParameters(unsafe { moonfire_ffmpeg_stream_codecpar(self.0).as_ref() }.unwrap()) - } - - pub fn time_base(&self) -> AVRational { - unsafe { moonfire_ffmpeg_stream_time_base(self.0) } - } -} - -pub struct CodecParameters<'s>(&'s AVCodecParameters); - -impl<'s> CodecParameters<'s> { - pub fn extradata(&self) -> &[u8] { - unsafe { - let d = moonfire_ffmpeg_codecpar_extradata(self.0); - ::std::slice::from_raw_parts(d.data, d.len) - } - } - pub fn width(&self) -> libc::c_int { unsafe { moonfire_ffmpeg_codecpar_width(self.0) } } - pub fn height(&self) -> libc::c_int { unsafe { moonfire_ffmpeg_codecpar_height(self.0) } } - pub fn codec_id(&self) -> CodecId { - CodecId(unsafe { moonfire_ffmpeg_codecpar_codec_id(self.0) }) - } - pub fn codec_type(&self) -> MediaType { - MediaType(unsafe { moonfire_ffmpeg_codecpar_codec_type(self.0) }) - } -} - -#[derive(Copy, Clone, Debug)] -pub struct CodecId(libc::c_int); - -impl CodecId { - pub fn is_h264(self) -> bool { self.0 == unsafe { moonfire_ffmpeg_av_codec_id_h264 } } -} - -#[derive(Copy, Clone, Debug)] -pub struct MediaType(libc::c_int); - -impl MediaType { - pub fn is_video(self) -> bool { self.0 == unsafe { moonfire_ffmpeg_avmedia_type_video } } -} - -#[derive(Copy, Clone)] -pub struct Error(libc::c_int); - -impl Error { - pub fn eof() -> Self { Error(unsafe { moonfire_ffmpeg_averror_eof }) } - - fn wrap(raw: libc::c_int) -> Result<(), Error> { - match raw { - 0 => Ok(()), - r => Err(Error(r)), - } - } - - pub fn is_eof(self) -> bool { return self.0 == unsafe { moonfire_ffmpeg_averror_eof } } -} - -impl std::error::Error for Error { - fn description(&self) -> &str { - // TODO: pull out some common cases. - "ffmpeg error" - } - - fn cause(&self) -> Option<&dyn std::error::Error> { None } -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Error({} /* {} */)", self.0, self) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - const ARRAYLEN: usize = 64; - let mut buf = [0; ARRAYLEN]; - let s = unsafe { - // Note av_strerror uses strlcpy, so it guarantees a trailing NUL byte. - av_strerror(self.0, buf.as_mut_ptr(), ARRAYLEN); - CStr::from_ptr(buf.as_ptr()) - }; - f.write_str(&s.to_string_lossy()) - } -} - -#[derive(Copy, Clone)] -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 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - 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); - -impl Dictionary { - pub fn new() -> Dictionary { Dictionary(ptr::null_mut()) } - pub fn size(&self) -> usize { (unsafe { av_dict_count(self.0) }) as usize } - pub fn empty(&self) -> bool { self.size() == 0 } - pub fn set(&mut self, key: &CStr, value: &CStr) -> Result<(), Error> { - Error::wrap(unsafe { av_dict_set(&mut self.0, key.as_ptr(), value.as_ptr(), 0) }) - } -} - -impl fmt::Display for Dictionary { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut ent = ptr::null_mut(); - let mut first = true; - loop { - unsafe { - let c = 0; - ent = av_dict_get(self.0, &c, ent, moonfire_ffmpeg_av_dict_ignore_suffix); - if ent.is_null() { - break; - } - if first { - first = false; - } else { - write!(f, ", ")?; - } - write!(f, "{}={}", CStr::from_ptr((*ent).key).to_string_lossy(), - CStr::from_ptr((*ent).value).to_string_lossy())?; - } - } - Ok(()) - } -} - -impl Drop for Dictionary { - fn drop(&mut self) { unsafe { av_dict_free(&mut self.0) } } -} - -impl Ffmpeg { - pub fn new() -> Ffmpeg { - 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); - } - moonfire_ffmpeg_init(); - av_register_all(); - if avformat_network_init() < 0 { - panic!("avformat_network_init failed"); - } - info!("Initialized ffmpeg. Versions:{}", msg); - }); - Ffmpeg{} - } -} - -#[cfg(test)] -mod tests { - use std::ffi::CString; - 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] - fn test_error() { - let eof_formatted = format!("{}", Error::eof()); - assert!(eof_formatted.contains("End of file"), "eof formatted is: {}", eof_formatted); - let eof_debug = format!("{:?}", Error::eof()); - assert!(eof_debug.contains("End of file"), "eof debug is: {}", eof_debug); - - // Errors should be round trippable to a CString. (This will fail if they contain NUL - // bytes.) - CString::new(eof_formatted).unwrap(); - } -} diff --git a/ffmpeg/wrapper.c b/ffmpeg/wrapper.c deleted file mode 100644 index 87e961d..0000000 --- a/ffmpeg/wrapper.c +++ /dev/null @@ -1,151 +0,0 @@ -// This file is part of Moonfire NVR, a security camera network video recorder. -// Copyright (C) 2017 The Moonfire NVR Authors -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations including -// the two. -// -// You must obey the GNU General Public License in all respects for all -// of the code used other than OpenSSL. If you modify file(s) with this -// exception, you may extend this exception to your version of the -// file(s), but you are not obligated to do so. If you do not wish to do -// so, delete this exception statement from your version. If you delete -// this exception statement from all source files in the program, then -// also delete it here. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -/* vim: set sw=4 et: */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const int moonfire_ffmpeg_compiled_libavcodec_version = LIBAVCODEC_VERSION_INT; -const int moonfire_ffmpeg_compiled_libavformat_version = LIBAVFORMAT_VERSION_INT; -const int moonfire_ffmpeg_compiled_libavutil_version = LIBAVUTIL_VERSION_INT; - -const int moonfire_ffmpeg_av_dict_ignore_suffix = AV_DICT_IGNORE_SUFFIX; - -const int64_t moonfire_ffmpeg_av_nopts_value = AV_NOPTS_VALUE; - -const int moonfire_ffmpeg_avmedia_type_video = AVMEDIA_TYPE_VIDEO; - -const int moonfire_ffmpeg_av_codec_id_h264 = AV_CODEC_ID_H264; - -const int moonfire_ffmpeg_averror_eof = AVERROR_EOF; - -// Prior to libavcodec 58.9.100, multithreaded callers were expected to supply -// a lock callback. That release deprecated this API. It also introduced a -// FF_API_LOCKMGR #define to track its removal: -// -// * older builds (in which the lock callback is needed) don't define it. -// * middle builds (in which the callback is deprecated) define it as 1. -// value of 1. -// * future builds (in which the callback removed) will define -// it as 0. -// -// so (counterintuitively) use the lock manager when FF_API_LOCKMGR is -// undefined. - -#ifndef FF_API_LOCKMGR -static int lock_callback(void **mutex, enum AVLockOp op) { - switch (op) { - case AV_LOCK_CREATE: - *mutex = malloc(sizeof(pthread_mutex_t)); - if (*mutex == NULL) - return -1; - if (pthread_mutex_init(*mutex, NULL) != 0) - return -1; - break; - case AV_LOCK_DESTROY: - if (pthread_mutex_destroy(*mutex) != 0) - return -1; - free(*mutex); - *mutex = NULL; - break; - case AV_LOCK_OBTAIN: - if (pthread_mutex_lock(*mutex) != 0) - return -1; - break; - case AV_LOCK_RELEASE: - if (pthread_mutex_unlock(*mutex) != 0) - return -1; - break; - default: - return -1; - } - return 0; -} -#endif - -void moonfire_ffmpeg_init(void) { -#ifndef FF_API_LOCKMGR - if (av_lockmgr_register(&lock_callback) < 0) { - abort(); - } -#endif -} - -struct moonfire_ffmpeg_streams { - AVStream** streams; - size_t len; -}; - -struct moonfire_ffmpeg_data { - uint8_t *data; - size_t len; -}; - -struct moonfire_ffmpeg_streams moonfire_ffmpeg_fctx_streams(AVFormatContext *ctx) { - struct moonfire_ffmpeg_streams s = {ctx->streams, ctx->nb_streams}; - return s; -} - -AVPacket *moonfire_ffmpeg_packet_alloc(void) { return malloc(sizeof(AVPacket)); } -void moonfire_ffmpeg_packet_free(AVPacket *pkt) { free(pkt); } -bool moonfire_ffmpeg_packet_is_key(AVPacket *pkt) { return (pkt->flags & AV_PKT_FLAG_KEY) != 0; } -int64_t moonfire_ffmpeg_packet_pts(AVPacket *pkt) { return pkt->pts; } -void moonfire_ffmpeg_packet_set_dts(AVPacket *pkt, int64_t dts) { pkt->dts = dts; } -void moonfire_ffmpeg_packet_set_pts(AVPacket *pkt, int64_t pts) { pkt->pts = pts; } -void moonfire_ffmpeg_packet_set_duration(AVPacket *pkt, int dur) { pkt->duration = dur; } -int64_t moonfire_ffmpeg_packet_dts(AVPacket *pkt) { return pkt->dts; } -int moonfire_ffmpeg_packet_duration(AVPacket *pkt) { return pkt->duration; } -int moonfire_ffmpeg_packet_stream_index(AVPacket *pkt) { return pkt->stream_index; } -struct moonfire_ffmpeg_data moonfire_ffmpeg_packet_data(AVPacket *pkt) { - struct moonfire_ffmpeg_data d = {pkt->data, pkt->size}; - return d; -} - -AVCodecParameters *moonfire_ffmpeg_stream_codecpar(AVStream *stream) { return stream->codecpar; } -AVRational moonfire_ffmpeg_stream_time_base(AVStream *stream) { return stream->time_base; } - -int moonfire_ffmpeg_codecpar_codec_id(AVCodecParameters *codecpar) { return codecpar->codec_id; } -int moonfire_ffmpeg_codecpar_codec_type(AVCodecParameters *codecpar) { - return codecpar->codec_type; -} -struct moonfire_ffmpeg_data moonfire_ffmpeg_codecpar_extradata(AVCodecParameters *codecpar) { - struct moonfire_ffmpeg_data d = {codecpar->extradata, codecpar->extradata_size}; - return d; -} -int moonfire_ffmpeg_codecpar_height(AVCodecParameters *codecpar) { return codecpar->height; } -int moonfire_ffmpeg_codecpar_width(AVCodecParameters *codecpar) { return codecpar->width; } diff --git a/src/stream.rs b/src/stream.rs index be301d7..730172a 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -34,6 +34,7 @@ use failure::{Error, bail}; use ffmpeg; use lazy_static::lazy_static; use log::{debug, info, warn}; +use std::convert::TryFrom; use std::ffi::CString; use std::result::Result; @@ -61,29 +62,25 @@ pub trait Opener : Sync { pub trait Stream { fn get_extra_data(&self) -> Result; - fn get_next<'p>(&'p mut self) -> Result, ffmpeg::Error>; + fn get_next<'p>(&'p mut self) -> Result, ffmpeg::Error>; } pub struct Ffmpeg {} impl Ffmpeg { fn new() -> Ffmpeg { - START.call_once(|| { - ffmpeg::Ffmpeg::new(); - //ffmpeg::init().unwrap(); - //ffmpeg::format::network::init(); - }); + START.call_once(|| { ffmpeg::Ffmpeg::new(); }); Ffmpeg{} } } impl Opener for Ffmpeg { fn open(&self, src: Source) -> Result { - use ffmpeg::InputFormatContext; + use ffmpeg::avformat::InputFormatContext; let (mut input, discard_first) = match src { #[cfg(test)] Source::File(filename) => { - let mut open_options = ffmpeg::Dictionary::new(); + let mut open_options = ffmpeg::avutil::Dictionary::new(); // Work around https://github.com/scottlamb/moonfire-nvr/issues/10 open_options.set(cstr!("advanced_editlist"), cstr!("false")).unwrap(); @@ -97,7 +94,7 @@ impl Opener for Ffmpeg { (i, false) } Source::Rtsp{url, redacted_url} => { - let mut open_options = ffmpeg::Dictionary::new(); + let mut open_options = ffmpeg::avutil::Dictionary::new(); open_options.set(cstr!("rtsp_transport"), cstr!("tcp")).unwrap(); open_options.set(cstr!("user-agent"), cstr!("moonfire-nvr")).unwrap(); // 10-second socket timeout, in microseconds. @@ -150,7 +147,7 @@ impl Opener for Ffmpeg { } pub struct FfmpegStream { - input: ffmpeg::InputFormatContext, + input: ffmpeg::avformat::InputFormatContext, video_i: usize, } @@ -166,10 +163,12 @@ impl Stream for FfmpegStream { if !codec_id.is_h264() { bail!("stream's video codec {:?} is not h264", codec_id); } - h264::ExtraData::parse(codec.extradata(), codec.width() as u16, codec.height() as u16) + let dims = codec.dims(); + h264::ExtraData::parse(codec.extradata(), u16::try_from(dims.width)?, + u16::try_from(dims.height)?) } - fn get_next<'i>(&'i mut self) -> Result, ffmpeg::Error> { + fn get_next<'i>(&'i mut self) -> Result, ffmpeg::Error> { loop { let p = self.input.read_frame()?; if p.stream_index() == self.video_i { diff --git a/src/streamer.rs b/src/streamer.rs index 29e8be9..ad89ed1 100644 --- a/src/streamer.rs +++ b/src/streamer.rs @@ -236,7 +236,7 @@ mod tests { } impl<'a> Stream for ProxyingStream<'a> { - fn get_next(&mut self) -> Result { + fn get_next(&mut self) -> Result { if self.pkts_left == 0 { return Err(ffmpeg::Error::eof()); }