more experiments

This commit is contained in:
Robin Appelman 2021-07-02 23:01:51 +02:00
commit be1dd254e5
4 changed files with 408 additions and 462 deletions

229
Cargo.lock generated
View file

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
@ -49,6 +58,21 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7815ea54e4d821e791162e078acbebfd6d8c8939cd559c9335dceb1c8ca7282"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide 0.4.4",
"object",
"rustc-demangle",
]
[[package]]
name = "bindgen"
version = "0.58.1"
@ -90,6 +114,12 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
[[package]]
name = "cexpr"
version = "0.4.0"
@ -131,6 +161,33 @@ dependencies = [
"vec_map",
]
[[package]]
name = "color-eyre"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7"
dependencies = [
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"owo-colors",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1"
dependencies = [
"once_cell",
"owo-colors",
"tracing-core",
"tracing-error",
]
[[package]]
name = "color_quant"
version = "1.1.0"
@ -219,6 +276,16 @@ dependencies = [
"termcolor",
]
[[package]]
name = "eyre"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "ffimage"
version = "0.9.0"
@ -251,6 +318,12 @@ dependencies = [
"weezl",
]
[[package]]
name = "gimli"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
[[package]]
name = "glob"
version = "0.3.0"
@ -291,6 +364,12 @@ dependencies = [
"tiff",
]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "jpeg-decoder"
version = "0.1.22"
@ -478,18 +557,45 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "owo-colors"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
[[package]]
name = "paste"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pin-project-lite"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
[[package]]
name = "pkg-config"
version = "0.3.19"
@ -521,10 +627,11 @@ dependencies = [
name = "purpledot"
version = "0.1.0"
dependencies = [
"color-eyre",
"ffimage",
"ffimage_yuv",
"image",
"rusty_ffmpeg",
"rsmpeg",
]
[[package]]
@ -578,6 +685,24 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rsmpeg"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aca2781bf19626fa69af166efa35fca09dbf67632306e4130763d2baf62f02b7"
dependencies = [
"libc",
"paste",
"rusty_ffmpeg",
"thiserror",
]
[[package]]
name = "rustc-demangle"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49"
[[package]]
name = "rustc-hash"
version = "1.1.0"
@ -609,6 +734,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sharded-slab"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3"
dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "1.0.0"
@ -621,6 +755,17 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
@ -639,6 +784,35 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
dependencies = [
"once_cell",
]
[[package]]
name = "tiff"
version = "0.6.1"
@ -650,6 +824,59 @@ dependencies = [
"weezl",
]
[[package]]
name = "tracing"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052"
dependencies = [
"lazy_static",
]
[[package]]
name = "tracing-error"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab69019741fca4d98be3c62d2b75254528b5432233fd8a4d2739fec20278de48"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
]
[[package]]
name = "unicode-width"
version = "0.1.8"

View file

@ -4,7 +4,14 @@ version = "0.1.0"
edition = "2018"
[dependencies]
rusty_ffmpeg = "0.7"
image = "0.23"
ffimage = "0.9.0"
ffimage_yuv = "0.9.0"
ffimage = "0.9"
ffimage_yuv = "0.9"
rsmpeg = "0.6"
color-eyre = "0.5"
[profile.dev.package.backtrace]
opt-level = 3
[profile.dev.package.rsmpeg]
opt-level = 3

View file

@ -1,253 +1,117 @@
//! Port from Original code: https://github.com/leandromoreira/ffmpeg-libav-tutorial/blob/master/0_hello_world.c
use rusty_ffmpeg::ffi;
use rusty_ffmpeg::ffi::{
AVCodec, AVCodecContext, AVFormatContext, AVFrame, AVPacket, AVPixelFormat_AV_PIX_FMT_RGB24,
AVPixelFormat_AV_PIX_FMT_YUV420P,
};
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
use color_eyre::{eyre::eyre, Report, Result};
use rsmpeg::avcodec::{AVCodec, AVCodecContext, AVCodecID, AVCodecParametersRef, AVPacket};
use rsmpeg::avformat::{AVFormatContextInput, AVStreamRef};
use rsmpeg::avutil::AVFrame;
use rsmpeg::ffi;
use std::{
ffi::{CStr, CString},
fs::File,
io::Write,
ptr, slice,
};
pub struct RawFrameIter {
frame_context: OwnedAvFormatContext,
packet: OwnedAvPacket,
frame: OwnedAvFrame,
codec_context: OwnedAvCodecContext,
pub trait FormatContextInputExt {
fn into_packets(self) -> PacketIterator;
fn into_frames(self) -> Result<VideoFrames>;
fn video_stream(&self) -> Result<(i32, AVStreamRef)>;
fn video_info(&self) -> Result<VideoStreamInfo>;
}
impl RawFrameIter {
pub fn new(filepath: CString) -> Self {
println!("initializing all the containers, codecs and protocols.");
#[derive(Clone, Copy, Debug)]
pub struct VideoStreamInfo {
pub width: i32,
pub height: i32,
pub index: i32,
pub codec: AVCodecID,
}
let mut format_context = OwnedAvFormatContext::new();
impl VideoStreamInfo {
pub fn new(index: i32, codec_params: &AVCodecParametersRef) -> Self {
let width = codec_params.width;
let height = codec_params.height;
println!(
"opening the input file ({}) and loading format (container) header",
filepath.to_str().unwrap()
);
if unsafe {
ffi::avformat_open_input(
&mut (format_context.deref_mut() as *mut _) as *mut *mut _,
filepath.as_ptr(),
ptr::null_mut(),
ptr::null_mut(),
)
} != 0
{
panic!("ERROR could not open the file");
VideoStreamInfo {
width,
height,
index: index as i32,
codec: codec_params.codec_id,
}
}
}
let format_name = unsafe { CStr::from_ptr((*format_context.iformat).name) }
.to_str()
.unwrap();
pub struct PacketIterator {
context: AVFormatContextInput,
}
println!(
"format {}, duration {} us, bit_rate {}",
format_name, format_context.duration, format_context.bit_rate
);
impl Iterator for PacketIterator {
type Item = Result<AVPacket>;
println!("finding stream info from format");
fn next(&mut self) -> Option<Result<AVPacket>> {
self.context.read_packet().map_err(Report::from).transpose()
}
}
if unsafe { ffi::avformat_find_stream_info(format_context.deref_mut(), ptr::null_mut()) }
< 0
{
panic!("ERROR could not get the stream info");
}
impl FormatContextInputExt for AVFormatContextInput {
fn into_packets(self) -> PacketIterator {
PacketIterator { context: self }
}
let mut codec_ptr: *const ffi::AVCodec = ptr::null_mut();
let mut codec_parameters_ptr: *const ffi::AVCodecParameters = ptr::null_mut();
let mut video_stream_index = None;
let mut resolution = None;
fn into_frames(self) -> Result<VideoFrames> {
let (codec_context, info) = {
let (index, stream) = self.video_stream()?;
let codec_params = stream.codecpar();
let info = VideoStreamInfo::new(index, &codec_params);
let streams = unsafe {
slice::from_raw_parts(format_context.streams, format_context.nb_streams as usize)
let decoder = AVCodec::find_decoder(codec_params.codec_id)
.ok_or_else(|| eyre!("No decoder found for codec {}", info.codec))?;
let mut codec_context = AVCodecContext::new(&decoder);
codec_context.apply_codecpar(codec_params)?;
codec_context.open(None)?;
(codec_context, info)
};
for (i, stream) in streams
.iter()
.map(|stream| unsafe { stream.as_ref() }.unwrap())
.enumerate()
{
println!(
"AVStream->time_base before open coded {}/{}",
stream.time_base.num, stream.time_base.den
);
println!(
"AVStream->r_frame_rate before open coded {}/{}",
stream.r_frame_rate.num, stream.r_frame_rate.den
);
println!("AVStream->start_time {}", stream.start_time);
println!("AVStream->duration {}", stream.duration);
println!("finding the proper decoder (CODEC)");
let local_codec_params = unsafe { stream.codecpar.as_ref() }.unwrap();
let local_codec =
unsafe { ffi::avcodec_find_decoder(local_codec_params.codec_id).as_ref() }
.expect("ERROR unsupported codec!");
match local_codec_params.codec_type {
ffi::AVMediaType_AVMEDIA_TYPE_VIDEO => {
if video_stream_index.is_none() {
video_stream_index = Some(i);
codec_ptr = local_codec;
codec_parameters_ptr = local_codec_params;
}
println!(
"Video Codec: resolution {} x {}",
local_codec_params.width, local_codec_params.height
);
resolution = Some((local_codec_params.width, local_codec_params.height));
}
ffi::AVMediaType_AVMEDIA_TYPE_AUDIO => {
println!(
"Audio Codec: {} channels, sample rate {}",
local_codec_params.channels, local_codec_params.sample_rate
);
}
_ => {}
};
let codec_name = unsafe { CStr::from_ptr(local_codec.name) }
.to_str()
.unwrap();
println!(
"\tCodec {} ID {} bit_rate {}",
codec_name, local_codec.id, local_codec_params.bit_rate
);
}
let (width, height) = resolution.expect("No resolution found");
let mut codec_context = OwnedAvCodecContext::new(codec_ptr);
if unsafe {
ffi::avcodec_parameters_to_context(codec_context.deref_mut(), codec_parameters_ptr)
} < 0
{
panic!("failed to copy codec params to codec context");
}
if unsafe { ffi::avcodec_open2(codec_context.deref_mut(), codec_ptr, ptr::null_mut()) } < 0
{
panic!("failed to open codec through avcodec_open2");
}
let frame = OwnedAvFrame::new();
let packet = OwnedAvPacket::new();
let mut packets_waiting = 8;
RawFrameIter {
frame_context: format_context,
packet,
frame,
Ok(VideoFrames {
packets: self.into_packets(),
codec_context,
}
info,
})
}
fn video_stream(&self) -> Result<(i32, AVStreamRef)> {
self.streams()
.into_iter()
.enumerate()
.find(|(_index, stream)| {
stream.codecpar().codec_type == ffi::AVMediaType_AVMEDIA_TYPE_VIDEO
})
.map(|(index, stream)| (index as i32, stream))
.ok_or_else(|| eyre!("No video stream found"))
}
fn video_info(&self) -> Result<VideoStreamInfo> {
let (index, stream) = self.video_stream()?;
let codec_params = stream.codecpar();
Ok(VideoStreamInfo::new(index, &codec_params))
}
}
impl RawFrameIter {
fn next(&mut self) -> Option<&OwnedAvFrame> {
if unsafe { ffi::av_read_frame(format_context, packet) } >= 0 {
if video_stream_index == Some(packet.stream_index as usize) {
println!("AVPacket->pts {}", packet.pts);
decode_packet(packet, codec_context, frame).unwrap();
packets_waiting -= 1;
if packets_waiting <= 0 {
break;
}
}
unsafe { ffi::av_packet_unref(packet) };
Some(&self.frame)
} else {
None
}
}
pub struct VideoFrames {
packets: PacketIterator,
codec_context: AVCodecContext,
pub info: VideoStreamInfo,
}
macro_rules! owned_ptr {
($name:ident, $ptr:path) => {
struct $name {
ptr: NonNull<$ptr>,
}
impl Deref for $name {
type Target = $ptr;
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.as_ref() }
}
}
impl DerefMut for $name {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.ptr.as_mut() }
}
}
};
($name:ident, $ptr:path, $alloc:path, $free:path) => {
owned_ptr!($name, $ptr);
impl $name {
pub fn new() -> Self {
let ptr = NonNull::new(unsafe { $alloc() })
.expect("failed to allocated memory for $name");
$name { ptr }
}
}
impl Drop for $name {
fn drop(&mut self) {
unsafe {
$free(&mut (self.ptr.as_ptr()));
}
}
}
};
}
owned_ptr!(
OwnedAvFrame,
AVFrame,
ffi::av_frame_alloc,
ffi::av_frame_free
);
owned_ptr!(
OwnedAvPacket,
AVPacket,
ffi::av_packet_alloc,
ffi::av_packet_free
);
owned_ptr!(
OwnedAvFormatContext,
AVFormatContext,
ffi::avformat_alloc_context,
ffi::avformat_close_input
);
owned_ptr!(OwnedAvCodecContext, AVCodecContext);
impl OwnedAvCodecContext {
pub fn new(codec: *const ffi::AVCodec) -> Self {
let ptr = NonNull::new(unsafe { ffi::avcodec_alloc_context3(codec) })
.expect("failed to allocated memory for $name");
OwnedAvCodecContext { ptr }
}
}
impl Drop for OwnedAvCodecContext {
fn drop(&mut self) {
unsafe {
ffi::avcodec_free_context(&mut (self.ptr.as_ptr()));
}
impl Iterator for VideoFrames {
type Item = Result<AVFrame>;
fn next(&mut self) -> Option<Result<AVFrame>> {
self.packets
.next()
.filter(|res| match res {
Ok(packet) => packet.stream_index == self.info.index,
Err(_) => true,
})
.map(|res| {
res.and_then(|packet| {
self.codec_context.send_packet(Some(&packet))?;
Ok(self.codec_context.receive_frame()?)
})
})
}
}

View file

@ -1,238 +1,86 @@
//! Port from Original code: https://github.com/leandromoreira/ffmpeg-libav-tutorial/blob/master/0_hello_world.c
use color_eyre::{eyre::eyre, Result};
use framestream::FormatContextInputExt;
use rsmpeg::avformat::AVFormatContextInput;
use std::ffi::CString;
use std::fs::File;
use std::io::Write;
use std::slice;
mod framestream;
use rusty_ffmpeg::ffi;
fn main() -> Result<()> {
let path = CString::new("data/track3.mp4")?;
let input = AVFormatContextInput::open(&path)?;
let frames = input.into_frames()?;
use std::{
ffi::{CStr, CString},
fs::File,
io::Write,
ptr, slice,
};
for (i, frame) in frames.take(2).enumerate() {
let frame = frame?;
let frame_filename = format!("./data/output/frame-{}.pgm", i);
fn main() {
let filepath: CString = CString::new("./data/track3.mp4").unwrap();
println!("initializing all the containers, codecs and protocols.");
let mut format_context_ptr = unsafe { ffi::avformat_alloc_context() };
if format_context_ptr.is_null() {
panic!("ERROR could not allocate memory for Format Context");
}
println!(
"opening the input file ({}) and loading format (container) header",
filepath.to_str().unwrap()
);
if unsafe {
ffi::avformat_open_input(
&mut format_context_ptr,
filepath.as_ptr(),
ptr::null_mut(),
ptr::null_mut(),
)
} != 0
{
panic!("ERROR could not open the file");
}
let format_context = unsafe { format_context_ptr.as_mut() }.unwrap();
let format_name = unsafe { CStr::from_ptr((*format_context.iformat).name) }
.to_str()
.unwrap();
println!(
"format {}, duration {} us, bit_rate {}",
format_name, format_context.duration, format_context.bit_rate
);
println!("finding stream info from format");
if unsafe { ffi::avformat_find_stream_info(format_context, ptr::null_mut()) } < 0 {
panic!("ERROR could not get the stream info");
}
let mut codec_ptr: *const ffi::AVCodec = ptr::null_mut();
let mut codec_parameters_ptr: *const ffi::AVCodecParameters = ptr::null_mut();
let mut video_stream_index = None;
let mut resolution = None;
let streams = unsafe {
slice::from_raw_parts(format_context.streams, format_context.nb_streams as usize)
};
for (i, stream) in streams
.iter()
.map(|stream| unsafe { stream.as_ref() }.unwrap())
.enumerate()
{
println!(
"AVStream->time_base before open coded {}/{}",
stream.time_base.num, stream.time_base.den
);
println!(
"AVStream->r_frame_rate before open coded {}/{}",
stream.r_frame_rate.num, stream.r_frame_rate.den
);
println!("AVStream->start_time {}", stream.start_time);
println!("AVStream->duration {}", stream.duration);
println!("finding the proper decoder (CODEC)");
let local_codec_params = unsafe { stream.codecpar.as_ref() }.unwrap();
let local_codec =
unsafe { ffi::avcodec_find_decoder(local_codec_params.codec_id).as_ref() }
.expect("ERROR unsupported codec!");
match local_codec_params.codec_type {
ffi::AVMediaType_AVMEDIA_TYPE_VIDEO => {
if video_stream_index.is_none() {
video_stream_index = Some(i);
codec_ptr = local_codec;
codec_parameters_ptr = local_codec_params;
}
println!(
"Video Codec: resolution {} x {}",
local_codec_params.width, local_codec_params.height
);
resolution = Some((local_codec_params.width, local_codec_params.height));
}
ffi::AVMediaType_AVMEDIA_TYPE_AUDIO => {
println!(
"Audio Codec: {} channels, sample rate {}",
local_codec_params.channels, local_codec_params.sample_rate
);
}
_ => {}
};
let codec_name = unsafe { CStr::from_ptr(local_codec.name) }
.to_str()
.unwrap();
println!(
"\tCodec {} ID {} bit_rate {}",
codec_name, local_codec.id, local_codec_params.bit_rate
);
}
let (width, height) = resolution.expect("No resolution found");
let codec_context = unsafe { ffi::avcodec_alloc_context3(codec_ptr).as_mut() }
.expect("failed to allocated memory for AVCodecContext");
if unsafe { ffi::avcodec_parameters_to_context(codec_context, codec_parameters_ptr) } < 0 {
panic!("failed to copy codec params to codec context");
}
if unsafe { ffi::avcodec_open2(codec_context, codec_ptr, ptr::null_mut()) } < 0 {
panic!("failed to open codec through avcodec_open2");
}
let frame =
unsafe { ffi::av_frame_alloc().as_mut() }.expect("failed to allocated memory for AVFrame");
let packet = unsafe { ffi::av_packet_alloc().as_mut() }
.expect("failed to allocated memory for AVPacket");
let byte_size =
unsafe { ffi::avpicture_get_size(AVPixelFormat_AV_PIX_FMT_RGB24, width, height) };
let rgb_buffer = vec![0; byte_size as usize];
let rgb_frame =
unsafe { ffi::av_frame_alloc().as_mut() }.expect("failed to allocated memory for AVFrame");
unsafe {
ffi::avpicture_fill(
rgb_frame as *mut _ as *mut _,
rgb_buffer.as_ptr(),
AVPixelFormat_AV_PIX_FMT_RGB24,
width,
height,
);
}
let mut packets_waiting = 8;
while unsafe { ffi::av_read_frame(format_context, packet) } >= 0 {
if video_stream_index == Some(packet.stream_index as usize) {
println!("AVPacket->pts {}", packet.pts);
decode_packet(packet, codec_context, frame).unwrap();
packets_waiting -= 1;
if packets_waiting <= 0 {
break;
}
}
unsafe { ffi::av_packet_unref(packet) };
}
println!("releasing all the resources");
unsafe {
ffi::avformat_close_input(&mut (format_context as *mut _));
ffi::av_packet_free(&mut (packet as *mut _));
ffi::av_frame_free(&mut (frame as *mut _));
ffi::avcodec_free_context(&mut (codec_context as *mut _));
}
}
fn decode_packet(
packet: &ffi::AVPacket,
codec_context: &mut ffi::AVCodecContext,
frame: &mut ffi::AVFrame,
) -> Result<(), String> {
let mut response = unsafe { ffi::avcodec_send_packet(codec_context, packet) };
if response < 0 {
return Err(String::from("Error while sending a packet to the decoder."));
}
while response >= 0 {
response = unsafe { ffi::avcodec_receive_frame(codec_context, frame) };
if response == ffi::AVERROR(ffi::EAGAIN) || response == ffi::AVERROR_EOF {
break;
} else if response < 0 {
return Err(String::from(
"Error while receiving a frame from the decoder.",
));
} else {
println!(
"Frame {} (type={}, size={} bytes) pts {} key_frame {} [DTS {}]",
codec_context.frame_number,
unsafe { ffi::av_get_picture_type_char(frame.pict_type) },
frame.pkt_size,
frame.pts,
frame.key_frame,
frame.coded_picture_number
);
let frame_filename = format!("./data/output/frame-{}.pgm", codec_context.frame_number);
let width = frame.width as usize;
let height = frame.height as usize;
let wrap = frame.linesize[0] as usize;
let data = unsafe { slice::from_raw_parts(frame.data[0], wrap * height) };
if frame.format != AVPixelFormat_AV_PIX_FMT_YUV420P {
panic!("Input has to be yuv420p, got :{}", frame.format);
}
unsafe {
// ffi::sws_scale(codec_context, frame.data,
// wrap, 0, height, pFrameRGB->data,
// pFrameRGB->linesize);
}
dbg!(wrap, width, height);
save_gray_frame(data, wrap, width, height, frame_filename).unwrap();
}
save_gray_frame(
unsafe { slice::from_raw_parts(frame.data[0], (frame.width * frame.height) as usize) },
frame.linesize[0] as usize,
frame.width as usize,
frame.height as usize,
frame_filename,
)?;
}
Ok(())
}
use ffimage::packed::{ImageBuffer, ImageView};
use ffimage::traits::Convert;
use ffimage_yuv::yuv;
use ffimage_yuv::yuv::Yuv;
use rusty_ffmpeg::ffi::{AVPixelFormat_AV_PIX_FMT_RGB24, AVPixelFormat_AV_PIX_FMT_YUV420P};
// fn decode_packet(
// packet: &ffi::AVPacket,
// codec_context: &mut ffi::AVCodecContext,
// frame: &mut ffi::AVFrame,
// ) -> Result<(), String> {
// let mut response = unsafe { ffi::avcodec_send_packet(codec_context, packet) };
//
// if response < 0 {
// return Err(String::from("Error while sending a packet to the decoder."));
// }
//
// while response >= 0 {
// response = unsafe { ffi::avcodec_receive_frame(codec_context, frame) };
// if response == ffi::AVERROR(ffi::EAGAIN) || response == ffi::AVERROR_EOF {
// break;
// } else if response < 0 {
// return Err(String::from(
// "Error while receiving a frame from the decoder.",
// ));
// } else {
// println!(
// "Frame {} (type={}, size={} bytes) pts {} key_frame {} [DTS {}]",
// codec_context.frame_number,
// unsafe { ffi::av_get_picture_type_char(frame.pict_type) },
// frame.pkt_size,
// frame.pts,
// frame.key_frame,
// frame.coded_picture_number
// );
//
// let frame_filename = format!("./data/output/frame-{}.pgm", codec_context.frame_number);
// let width = frame.width as usize;
// let height = frame.height as usize;
// let wrap = frame.linesize[0] as usize;
// let data = unsafe { slice::from_raw_parts(frame.data[0], wrap * height) };
//
// if frame.format != AVPixelFormat_AV_PIX_FMT_YUV420P {
// panic!("Input has to be yuv420p, got :{}", frame.format);
// }
//
// unsafe {
// // ffi::sws_scale(codec_context, frame.data,
// // wrap, 0, height, pFrameRGB->data,
// // pFrameRGB->linesize);
// }
//
// dbg!(wrap, width, height);
//
// save_gray_frame(data, wrap, width, height, frame_filename).unwrap();
// }
// }
// Ok(())
// }
fn save_gray_frame(
buf: &[u8],
@ -240,7 +88,7 @@ fn save_gray_frame(
xsize: usize,
ysize: usize,
filename: String,
) -> Result<(), std::io::Error> {
) -> Result<()> {
let mut file = File::create(filename)?;
let data = format!("P5\n{} {}\n{}\n", xsize, ysize, 255);
file.write_all(data.as_bytes())?;