use mcai_worker_sdk::prelude::info;
use stainless_ffmpeg::{
check_result, format_context::FormatContext, frame::Frame, order::output::OutputStream,
order::ParameterValue, packet::Packet, prelude::*, tools, tools::rational::Rational,
video_encoder::VideoEncoder,
};
use std::{collections::HashMap, ffi::CString};
unsafe fn write_header(format_context: &FormatContext) -> Result<(), String> {
let path = CString::new(format_context.filename.as_str()).unwrap();
check_result!(avio_open(
&mut (*format_context.format_context).pb as *mut _,
path.as_ptr(),
AVIO_FLAG_WRITE
));
check_result!(avformat_write_header(
format_context.format_context,
std::ptr::null_mut()
));
Ok(())
}
unsafe fn get_black_frame(pixel_format: &str, width: i32, height: i32) -> Result {
let av_frame = av_frame_alloc();
let pix_fmt = av_get_pix_fmt(CString::new(pixel_format).unwrap().into_raw());
(*av_frame).width = width;
(*av_frame).height = height;
(*av_frame).format = pix_fmt as i32;
let ret_code = av_image_alloc(
(*av_frame).data.as_mut_ptr(),
(*av_frame).linesize.as_mut_ptr(),
(*av_frame).width,
(*av_frame).height,
pix_fmt,
1,
);
check_result!(ret_code);
Ok(Frame {
name: Some("black_frame".to_string()),
frame: av_frame,
index: 0,
})
}
unsafe fn write_frame(
format_context: &FormatContext,
video_encoder: &mut VideoEncoder,
frame: &Frame,
interleaved: bool,
) -> Result<(), String> {
let av_packet = av_packet_alloc();
av_init_packet(av_packet);
(*av_packet).data = std::ptr::null_mut();
(*av_packet).size = 0;
(*av_packet).pts = video_encoder.pts;
let packet = Packet {
name: None,
packet: av_packet,
};
if video_encoder.encode(frame, &packet)? {
(*av_packet).stream_index = video_encoder.stream_index as i32;
if interleaved {
let ret_code = av_interleaved_write_frame(format_context.format_context, av_packet);
check_result!(ret_code);
} else {
let ret_code = av_write_frame(format_context.format_context, av_packet);
check_result!(ret_code);
}
}
Ok(())
}
unsafe fn flush_encoder(
format_context: &FormatContext,
video_encoder: &VideoEncoder,
interleaved: bool,
) -> Result<(), String> {
let av_packet = av_packet_alloc();
av_init_packet(av_packet);
(*av_packet).data = std::ptr::null_mut();
(*av_packet).size = 0;
let packet = Packet {
name: None,
packet: av_packet,
};
let ret = avcodec_send_frame(video_encoder.codec_context, std::ptr::null_mut());
if ret != 0 && ret != AVERROR_EOF {
check_result!(ret);
}
check_result!(avcodec_receive_packet(
video_encoder.codec_context,
packet.packet as *mut _
));
(*av_packet).stream_index = video_encoder.stream_index as i32;
if interleaved {
let ret_code = av_interleaved_write_frame(format_context.format_context, av_packet);
check_result!(ret_code);
} else {
let ret_code = av_write_frame(format_context.format_context, av_packet);
check_result!(ret_code);
}
Ok(())
}
unsafe fn close_file(format_context: &FormatContext) -> Result<(), String> {
check_result!(av_write_trailer(format_context.format_context));
Ok(())
}
pub fn create_xdcam_sample_file(file_path: &str, nb_frames: i32) -> Result<(), String> {
let xdcam_profile = [
("gop_size", ParameterValue::Int64(12)),
("max_b_frames", ParameterValue::Int64(2)),
(
"frame_rate",
ParameterValue::Rational(Rational { num: 25, den: 1 }),
),
("width", ParameterValue::Int64(1920)),
("height", ParameterValue::Int64(1080)),
(
"pixel_format",
ParameterValue::String("yuv422p".to_string()),
),
("bitrate", ParameterValue::Int64(50_000_000)),
];
let mut codec_parameters = HashMap::::new();
for (key, value) in &xdcam_profile {
codec_parameters.insert(key.to_string(), value.clone());
}
let output_stream = OutputStream {
label: Some("video_stream".to_string()),
codec: "mpeg2video".to_string(),
parameters: codec_parameters,
};
info!("{:?}", output_stream);
let mut video_encoder = VideoEncoder::new("video".to_string(), 0, &output_stream).unwrap();
let mut format_context = FormatContext::new(file_path)?;
let output_parameters = HashMap::::new();
format_context.open_output(&output_parameters)?;
format_context.add_video_stream(&video_encoder)?;
unsafe {
let black_frame = get_black_frame("yuv422p", 1920, 1080)?;
write_header(&format_context)?;
for _i in 0..nb_frames {
write_frame(&format_context, &mut video_encoder, &black_frame, false)?;
}
let mut flush_result = Ok(());
while flush_result.is_ok() {
flush_result = flush_encoder(&format_context, &video_encoder, false);
}
close_file(&format_context)?;
}
Ok(())
}