use super::*;
use crate::core::context::frame_source::{FrameSource, FrameSourceParams};
use crate::core::writer::WriterError;
use crossbeam_channel::Sender;
use super::fg_bind::{init_filter_graph, probe_writer_filter_shape};
use super::opt_util::{choose_encoder, ofilter_bind_ost, process_metadata};
pub(crate) fn build_writer_context(
params: FrameSourceParams,
queue_capacity: usize,
filter_desc: Option<&str>,
output: Output,
) -> Result<(FfmpegContext, Sender<Vec<u8>>)> {
crate::core::initialize_ffmpeg();
let scheduler_status = Arc::new(std::sync::atomic::AtomicUsize::new(
crate::core::scheduler::ffmpeg_scheduler::STATUS_INIT,
));
let interrupt_state = Arc::new(crate::core::context::InterruptState::new(
scheduler_status.clone(),
));
let mut outputs = vec![output];
let mut muxs = open_output_files(&mut outputs, false, &interrupt_state)?;
if !muxs[0].stream_map_specs.is_empty() || !muxs[0].stream_maps.is_empty() {
return Err(WriterError::StreamMapsUnsupported.into());
}
let desc = filter_desc.unwrap_or("null");
let shape = probe_writer_filter_shape(desc)?;
if shape.components > 1 {
return Err(WriterError::DisconnectedFilterGraph {
components: shape.components,
}
.into());
}
if shape.input_pads != 1
|| shape.video_input_pads != 1
|| shape.output_pads != 1
|| shape.video_output_pads != 1
{
return Err(WriterError::FilterShape {
input_pads: shape.input_pads,
video_input_pads: shape.video_input_pads,
output_pads: shape.output_pads,
video_output_pads: shape.video_output_pads,
}
.into());
}
if !shape.output_reachable {
return Err(WriterError::UnreachableFilterOutput.into());
}
let mut filter_graph = init_filter_graph(0, desc, None, None, None)?;
let fg_sender = ifilter_bind_frame_source(&mut filter_graph, ¶ms);
{
let mux = &mut muxs[0];
if !mux.video_disable {
match choose_encoder(mux, AVMEDIA_TYPE_VIDEO)? {
Some((codec_id, enc)) => {
ofilter_bind_ost(0, mux, &mut filter_graph, 0, codec_id, enc, None, true)?;
}
None => {
error!(target: LOG_TARGET,
"VideoWriter output requested streamcopy (video codec \
'copy'), but pushed frames must be encoded"
);
return Err(OpenOutputError::InvalidArgument.into());
}
}
}
unsafe {
crate::core::context::attachment::create_attachment_streams(mux)?;
process_metadata(mux, &Vec::new())?;
}
}
if !muxs[0].has_src() || !filter_graph.outputs[0].has_dst() {
warn!(target: LOG_TARGET, "Writer output consumes no video stream");
return Err(OpenOutputError::NotContainStream.into());
}
check_frame_filter_pipeline(&muxs, &[])?;
let (ingress_sender, ingress_receiver) = crossbeam_channel::bounded(queue_capacity);
let frame_source = FrameSource {
ingress: ingress_receiver,
fg_sender,
params,
};
Ok((
FfmpegContext {
independent_readrate: false,
demuxs: Vec::new(),
filter_graphs: vec![filter_graph],
muxs,
frame_sources: vec![frame_source],
scheduler_status,
interrupt_state,
},
ingress_sender,
))
}
fn ifilter_bind_frame_source(
filter_graph: &mut FilterGraph,
params: &FrameSourceParams,
) -> Sender<crate::core::context::FrameBox> {
let input_filter = &mut filter_graph.inputs[0];
input_filter.opts.framerate = AVRational {
num: params.fps_num,
den: params.fps_den,
};
unsafe {
let fallback = input_filter.opts.fallback.as_mut_ptr();
(*fallback).format = params.pix_fmt as i32;
(*fallback).width = params.width;
(*fallback).height = params.height;
(*fallback).time_base = AVRational {
num: params.fps_den,
den: params.fps_num,
};
}
input_filter.bound = true;
let (sender, _finished_flag_list) = filter_graph.get_src_sender();
sender
}