Skip to content

Commit

Permalink
Merge af5eeaa into f1698f0
Browse files Browse the repository at this point in the history
  • Loading branch information
Romanelaf committed Apr 6, 2020
2 parents f1698f0 + af5eeaa commit 7361463
Show file tree
Hide file tree
Showing 12 changed files with 919 additions and 296 deletions.
32 changes: 32 additions & 0 deletions examples/deep_probe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use env_logger::{Builder, Env};
use log::LevelFilter;
use stainless_ffmpeg::probe::deep::CheckParameterValue;
use stainless_ffmpeg::probe::*;
use std::collections::HashMap;
use std::env;

fn main() {
Builder::from_env(Env::default().default_filter_or("debug")).init();

if let Some(path) = env::args().last() {
let mut probe = DeepProbe::new(&path);
let duration_params = CheckParameterValue {
min: Some(2000),
max: Some(10000),
num: None,
den: None,
};
let mut params = HashMap::new();
params.insert("duration".to_string(), duration_params);
let check = DeepProbeCheck {
silence_detect: params,
};
probe.process(LevelFilter::Off, check).unwrap();
let result = serde_json::to_string(&probe).unwrap();
println!("{}", result);

if let Some(result) = probe.result {
println!("Deep probe result : \n{}", result);
}
}
}
1 change: 1 addition & 0 deletions src/audio_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl AudioDecoder {
Ok(Frame {
frame,
name: Some(self.identifier.clone()),
index: self.stream_index as usize,
})
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/filter_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,22 +280,24 @@ impl FilterGraph {
));
}

for output_filter in self.audio_outputs.iter() {
for (index, output_filter) in self.audio_outputs.iter().enumerate() {
let output_frame = av_frame_alloc();
check_result!(av_buffersink_get_frame(output_filter.context, output_frame));
output_audio_frames.push(Frame {
name: Some(output_filter.get_label()),
frame: output_frame,
index,
});
}

for output_filter in self.video_outputs.iter() {
for (index, output_filter) in self.video_outputs.iter().enumerate() {
let output_frame = av_frame_alloc();
let result = av_buffersink_get_frame(output_filter.context, output_frame);
if result >= 0 {
output_video_frames.push(Frame {
name: Some(output_filter.get_label()),
frame: output_frame,
index,
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/format_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ impl FormatContext {
}

pub fn next_packet(&mut self) -> Result<Packet, String> {
if self.frames.is_empty() {
if !self.frames.is_empty() {
if self.frame_index >= self.frames.len() as usize {
return Err("End of data stream".to_string());
}
Expand Down
1 change: 1 addition & 0 deletions src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::ptr::null_mut;
pub struct Frame {
pub name: Option<String>,
pub frame: *mut AVFrame,
pub index: usize,
}

impl Frame {
Expand Down
27 changes: 16 additions & 11 deletions src/order/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub mod output;
pub mod output_kind;
mod output_result;
pub mod parameters;
mod stream;
pub mod stream;

use crate::frame::Frame;
use crate::order::decoder_format::DecoderFormat;
Expand Down Expand Up @@ -43,7 +43,7 @@ pub struct Order {
#[serde(skip)]
output_formats: Vec<EncoderFormat>,
#[serde(skip)]
filter_graph: FilterGraph,
pub filter_graph: FilterGraph,
}

impl Order {
Expand Down Expand Up @@ -100,17 +100,22 @@ impl Order {

for output_frame in output_audio_frames {
for output in &self.outputs {
if let Some(OutputKind::AudioMetadata) = output.kind {
let mut entry = HashMap::new();
entry.insert("pts".to_owned(), format!("{}", output_frame.get_pts()));
if output.stream == output_frame.name {
if let Some(OutputKind::AudioMetadata) = output.kind {
let mut entry = HashMap::new();
entry.insert("pts".to_owned(), output_frame.get_pts().to_string());
if let Input::Streams { streams, .. } = &self.inputs[output_frame.index] {
entry.insert("stream_id".to_owned(), streams[0].index.to_string());
}

for key in &output.keys {
if let Some(value) = output_frame.get_metadata(&key) {
entry.insert(key.clone(), value);
for key in &output.keys {
if let Some(value) = output_frame.get_metadata(&key) {
entry.insert(key.clone(), value);
}
}
}

results.push(OutputResult::Entry(entry));
results.push(OutputResult::Entry(entry));
}
}
}

Expand All @@ -131,7 +136,7 @@ impl Order {
for output in &self.outputs {
if let Some(OutputKind::VideoMetadata) = output.kind {
let mut entry = HashMap::new();
entry.insert("pts".to_owned(), format!("{}", output_frame.get_pts()));
entry.insert("pts".to_owned(), output_frame.get_pts().to_string());

for key in &output.keys {
if let Some(value) = output_frame.get_metadata(&key) {
Expand Down
196 changes: 196 additions & 0 deletions src/probe/deep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
use crate::format_context::FormatContext;
use crate::probe::silence_detect::detect_silence;
use log::LevelFilter;
use stainless_ffmpeg_sys::*;
use std::{cmp, collections::HashMap, fmt};

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct DeepProbe {
#[serde(skip_serializing)]
filename: String,
pub result: Option<DeepProbeResult>,
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct DeepProbeResult {
#[serde(default)]
streams: Vec<StreamProbeResult>,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct SilenceResult {
pub start: i64,
pub end: i64,
}

fn is_false(x: &bool) -> bool {
!x
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct StreamProbeResult {
stream_index: usize,
count_packets: usize,
min_packet_size: i32,
max_packet_size: i32,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub detected_silence: Vec<SilenceResult>,
#[serde(skip_serializing_if = "is_false")]
pub silent_stream: bool,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct CheckParameterValue {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub num: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub den: Option<u64>,
}

#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct DeepProbeCheck {
pub silence_detect: HashMap<String, CheckParameterValue>,
}

impl fmt::Display for DeepProbeResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (index, stream) in self.streams.iter().enumerate() {
writeln!(f, "\n{:30} : {:?}", "Stream Index", index)?;
writeln!(f, "{:30} : {:?}", "Number of packets", stream.count_packets)?;
writeln!(
f,
"{:30} : {:?}",
"Minimum packet size", stream.min_packet_size
)?;
writeln!(
f,
"{:30} : {:?}",
"Maximum packet size", stream.max_packet_size
)?;
writeln!(
f,
"{:30} : {:?}",
"Silence detection", stream.detected_silence
)?;
}
Ok(())
}
}

impl StreamProbeResult {
pub fn new() -> Self {
StreamProbeResult {
stream_index: 0,
count_packets: 0,
min_packet_size: std::i32::MAX,
max_packet_size: std::i32::MIN,
detected_silence: vec![],
silent_stream: false,
}
}
}

impl DeepProbe {
pub fn new(filename: &str) -> Self {
DeepProbe {
filename: filename.to_owned(),
result: None,
}
}

pub fn process(&mut self, log_level: LevelFilter, check: DeepProbeCheck) -> Result<(), String> {
let av_log_level = match log_level {
LevelFilter::Error => AV_LOG_ERROR,
LevelFilter::Warn => AV_LOG_WARNING,
LevelFilter::Info => AV_LOG_INFO,
LevelFilter::Debug => AV_LOG_DEBUG,
LevelFilter::Trace => AV_LOG_TRACE,
LevelFilter::Off => AV_LOG_QUIET,
};

unsafe {
av_log_set_level(av_log_level);
}

let mut context = FormatContext::new(&self.filename).unwrap();
if context.open_input().is_err() {
self.result = None;
context.close_input();
return Ok(());
}

let mut streams = vec![];
streams.resize(context.get_nb_streams() as usize, StreamProbeResult::new());
loop {
match context.next_packet() {
Ok(packet) => unsafe {
let stream_index = (*packet.packet).stream_index as usize;
let packet_size = (*packet.packet).size;

streams[stream_index].stream_index = stream_index;
streams[stream_index].count_packets += 1;
streams[stream_index].min_packet_size =
cmp::min(packet_size, streams[stream_index].min_packet_size);
streams[stream_index].max_packet_size =
cmp::max(packet_size, streams[stream_index].max_packet_size);
},
Err(_) => {
break;
}
}
}

if !check.silence_detect.is_empty() {
let mut audio_indexes = vec![];
for stream_index in 0..context.get_nb_streams() {
if context.get_stream_type(stream_index as isize) == AVMediaType::AVMEDIA_TYPE_AUDIO {
audio_indexes.push(stream_index);
}
}
detect_silence(
&self.filename,
&mut streams,
audio_indexes,
check.silence_detect,
);
}

self.result = Some(DeepProbeResult { streams });

context.close_input();
Ok(())
}
}

#[test]
fn deep_probe_mxf_sample() {
use serde_json;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;

let mut probe = DeepProbe::new("tests/PAL_1080i_MPEG_XDCAM-HD_colorbar.mxf");
let mut params = HashMap::new();
let duration = CheckParameterValue {
min: Some(2000),
max: None,
num: None,
den: None,
};
params.insert("duration".to_string(), duration);
let check_list = DeepProbeCheck {
silence_detect: params,
};
probe.process(LevelFilter::Error, check_list).unwrap();

let mut file = File::open("tests/deep_probe.json").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();

let reference: DeepProbe = serde_json::from_str(&contents).unwrap();
assert_eq!(probe, reference);
}

0 comments on commit 7361463

Please sign in to comment.