Commit 5d8952a7 authored by Xu Guangxin's avatar Xu Guangxin Committed by Thomas Daede
Browse files

muxer: support container format via ffmpeg.

parent 09f5595b
......@@ -39,6 +39,7 @@ dav1d-sys = { version = "0.2", optional = true }
aom-sys = { version = "0.1.2", optional = true }
scan_fmt = { version = "0.2", optional = true }
ivf = { path = "ivf/", optional = true }
avformat-sys = { path = "avformat-sys/", optional = true }
rayon = "1.0"
bincode = "1.1"
arrayvec = "0.4.10"
......
[package]
name = "avformat-sys"
version = "0.1.0"
authors = ["Luca Barbato <lu_zero@gentoo.org>"]
license = "MIT"
description = "FFI bindings to ffmpeg"
edition = "2018"
build = "build.rs"
[package.metadata.pkg-config]
libavformat = "58.18.102"
libavcodec = "58.0.0"
libavutil = "56.0.0"
[features]
build_sources = []
[build-dependencies]
bindgen = "0.49"
metadeps = "1.1.2"
[dependencies]
MIT License
Copyright (c) 2018 Luca Barbato
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
use std::path::PathBuf;
use std::io::Write;
use std::env;
use std::fs::File;
fn format_write(builder: bindgen::Builder) -> String {
builder
.generate()
.unwrap()
.to_string()
}
fn main() {
let libs = metadeps::probe().unwrap();
let headers = libs.get("libavformat").unwrap().include_paths.clone();
let mut builder = bindgen::builder().header("data/av.h")
.default_enum_style(bindgen::EnumVariation::ModuleConsts);
for header in headers {
builder = builder.clang_arg("-I").clang_arg(header.to_str().unwrap());
}
// Manually fix the comment so rustdoc won't try to pick them
let s = format_write(builder);
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let mut file = File::create(out_path.join("av.rs")).unwrap();
let _ = file.write(s.as_bytes());
}
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#[cfg_attr(feature = "cargo-clippy", allow(const_static_lifetime))]
#[cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
pub mod av{
include!(concat!(env!("OUT_DIR"), "/av.rs"));
}
pub use av::*;
#[cfg(test)]
mod tests {
use super::av::*;
use std::ffi::CStr;
use std::mem;
#[test]
fn config() {
println!("{}", unsafe {
CStr::from_ptr(avformat_configuration()).to_string_lossy()
});
}
}
// Copyright (c) 2017-2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
#[cfg(feature = "avformat-sys")]
use avformat_sys::*;
#[allow(unused_imports)]
use super::Muxer;
#[allow(unused_imports)]
use std::ffi::CString;
#[allow(unused_imports)]
use std::io;
#[allow(unused_imports)]
use std::mem;
#[allow(unused_imports)]
use std::ptr;
#[cfg(feature = "avformat-sys")]
pub struct AvformatMuxer {
context: *mut AVFormatContext,
stream_time_base: AVRational , //time base get from container
time_base: AVRational , //set by muxer caller
duration: i64,
}
#[cfg(feature = "avformat-sys")]
impl AvformatMuxer {
pub fn open(path: &str) -> Box<dyn Muxer> {
unsafe {
let mut context = ptr::null_mut();
let p = CString::new(path).expect("convert to cstring failed");
match avformat_alloc_output_context2(
&mut context,
ptr::null_mut(),
ptr::null(),
p.as_ptr() as *const i8
) {
0 => {
let mut io = ptr::null_mut();
let ret =
avio_open(&mut io, p.as_ptr() as *const i8, AVIO_FLAG_WRITE as i32);
if ret < 0 {
panic!("open ouput file {} failed", path);
}
(*context).pb = io;
let default_time_base = AVRational {num: 1, den: 1};
Box::new(AvformatMuxer { context,
stream_time_base : default_time_base, time_base: default_time_base,
duration: 0,
})
}
e => panic!("open ouput failed, error = {}", e)
}
}
}
}
#[cfg(feature = "avformat-sys")]
impl Muxer for AvformatMuxer {
fn write_header(
&mut self, width: usize, height: usize, framerate_num: usize,
framerate_den: usize
) {
unsafe {
let stream = avformat_new_stream(self.context, ptr::null_mut());
if stream.is_null() {
panic!("new stream failed");
}
let param = (*stream).codecpar;
(*param).codec_type = AVMediaType::AVMEDIA_TYPE_VIDEO;
(*param).codec_id = AVCodecID::AV_CODEC_ID_AV1;
(*param).width = width as i32;
(*param).height = height as i32;
let ret = avformat_write_header(self.context, ptr::null_mut());
if ret < 0 {
panic!("write header failed error = {}", ret);
}
self.stream_time_base = (*stream).time_base;
//set timebase and duration base on fps.
self.time_base.den = framerate_num as i32;
self.time_base.num = framerate_den as i32;
self.duration = av_rescale_q(1, self.time_base, self.stream_time_base);
}
}
fn write_frame(&mut self, pts: u64, data: &[u8]) {
unsafe {
let mut pkt: AVPacket = mem::zeroed();
av_init_packet(&mut pkt);
pkt.data = data.as_ptr() as *mut _;
pkt.size = data.len() as i32;
let pts = av_rescale_q(pts as i64, self.time_base, self.stream_time_base);
pkt.pts = pts;
//no b frame, pts equals dts
pkt.dts = pts;
pkt.duration = self.duration;
let ret = av_write_frame(self.context, &mut pkt);
if ret < 0 {
panic!("write frame failed error = {}", ret);
}
}
}
fn flush(&mut self) -> io::Result<()> {
unsafe {
loop {
let ret = av_write_frame(self.context, ptr::null_mut());
if ret <0 {
panic!("write frame failed error = {}", ret)
}
if ret == 1 {
break;
}
}
}
Ok(())
}
}
#[cfg(feature = "avformat-sys")]
impl Drop for AvformatMuxer {
fn drop(&mut self) {
unsafe {
av_write_trailer(self.context);
avio_close((*self.context).pb);
avformat_free_context(self.context);
}
}
}
......@@ -10,10 +10,19 @@
mod ivf;
use self::ivf::IvfMuxer;
mod mp4;
use mp4::Mp4Muxer;
mod y4m;
pub use self::y4m::write_y4m_frame;
#[cfg(feature = "avformat-sys")]
mod avformat;
use std::io;
use std::ffi::OsStr;
use std::path::Path;
pub trait Muxer {
fn write_header(
......@@ -27,5 +36,25 @@ pub trait Muxer {
}
pub fn create_muxer(path: &str) -> Box<dyn Muxer> {
IvfMuxer::open(path)
if path == "-" {
return IvfMuxer::open(path);
}
let ext = Path::new(path)
.extension()
.and_then(OsStr::to_str)
.map(str::to_lowercase)
.expect("no extension");
match &ext[..] {
"mp4" => {
Mp4Muxer::open(path)
}
"ivf" => {
IvfMuxer::open(path)
}
_e => {
panic!("{} is not a supported extension, please change to .ivf", ext);
}
}
}
// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
use super::Muxer;
#[cfg(feature = "avformat-sys")]
use super::avformat::AvformatMuxer;
pub struct Mp4Muxer {
}
#[allow(unused_variables)]
impl Mp4Muxer {
pub fn open(path: &str) -> Box<dyn Muxer> {
#[cfg(feature = "avformat-sys")] {
AvformatMuxer::open(path)
}
#[cfg(not(feature = "avformat-sys"))] {
panic!("need avformat-sys for .mp4, please build with --features=\"avformat-sys\", or you can use .ivf extension");
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment