WIP ffmpeg filter graphs

This commit is contained in:
lif 2019-12-09 00:01:27 -08:00
parent ffc53708d1
commit b9023a0131
1 changed files with 91 additions and 23 deletions

View File

@ -7,12 +7,13 @@ use std::path::{Path, PathBuf};
use std::pin::Pin;
use structopt::StructOpt;
use failure::Fallible;
use ferretro::retro;
use ferretro::retro::ffi::{PixelFormat, GameGeometry, SystemAvInfo, SystemInfo};
use ferretro::retro::wrapper::{LibretroWrapper, Handler};
use ffmpeg::{format, frame, media, ChannelLayout};
use ffmpeg::{format, filter, frame, media, ChannelLayout};
use ffmpeg::util::rational::Rational;
struct MyEmulator {
@ -23,9 +24,71 @@ struct MyEmulator {
video_frames: VecDeque<frame::Video>,
video_encoder: ffmpeg::encoder::Video,
audio_encoder: ffmpeg::encoder::Audio,
video_filter: filter::Graph,
audio_filter: filter::Graph,
sys_path: Option<PathBuf>,
}
fn video_filter(
video_encoder: &ffmpeg::encoder::video::Video,
av_info: &SystemAvInfo,
pix_fmt: PixelFormat,
) -> Result<filter::Graph, ffmpeg::Error> {
let mut vfilter = filter::Graph::new();
let pix_fmt = match pix_fmt {
PixelFormat::ARGB1555 => if cfg!(target_endian = "big") { "rgb555be" } else { "rgb555le" },
PixelFormat::ARGB8888 => "argb",
PixelFormat::RGB565 => if cfg!(target_endian = "big") { "rgb565be" } else { "rgb565le" },
};
let pixel_aspect = av_info.geometry.aspect_ratio / (av_info.geometry.base_width as f32 / av_info.geometry.base_height as f32);
let args = format!(
"buffer=width={}:height={}:pix_fmt={}:frame_rate={}:pixel_aspect={}",
av_info.geometry.base_width,
av_info.geometry.base_height,
pix_fmt,
av_info.timing.fps,
pixel_aspect,
);
vfilter.add(&filter::find("buffer").unwrap(), "in", &args);
//scale?
vfilter.add(&filter::find("buffersink").unwrap(), "out", "");
{
let mut out = vfilter.get("out").unwrap();
out.set_pixel_format(video_encoder.format());
}
vfilter.output("in", 0)?
.input("out", 0)?;
vfilter.validate()?;
Ok(vfilter)
}
fn audio_filter(
audio_encoder: &ffmpeg::encoder::audio::Audio,
sample_rate: f64,
) -> Result<filter::Graph, ffmpeg::Error> {
let mut afilter = filter::Graph::new();
let args = format!("sample_rate={}:sample_fmt=s16:channel_layout=stereo", sample_rate);
afilter.add(&filter::find("abuffer").unwrap(), "in", &args);
//aresample?
afilter.add(&filter::find("abuffersink").unwrap(), "out", "");
{
let mut out = afilter.get("out").unwrap();
out.set_sample_format(audio_encoder.format());
out.set_channel_layout(audio_encoder.channel_layout());
out.set_sample_rate(audio_encoder.rate());
}
afilter.output("in", 0)?
.input("out", 0)?;
afilter.validate()?;
Ok(afilter)
}
impl MyEmulator {
pub fn new(
core_path: impl AsRef<Path>,
@ -44,16 +107,15 @@ impl MyEmulator {
video_encoder.set_frame_rate(av_info.timing.fps.into());
video_encoder.set_width(av_info.geometry.base_width);
video_encoder.set_height(av_info.geometry.base_height);
video_encoder.set_format(format::Pixel::YUV420P);
video_encoder.set_aspect_ratio(av_info.geometry.aspect_ratio as f64);
audio_encoder.set_rate(44100);
audio_encoder.set_format(audio_encoder.codec().unwrap().audio().unwrap().formats().unwrap().nth(0).unwrap());
audio_encoder.set_channels(2);
audio_encoder.set_channel_layout(ChannelLayout::STEREO);
audio_encoder.set_rate(system_av_info.timing.sample_rate.round() as i32);
let video_encoder_2 = video_encoder.open().unwrap();
let audio_encoder_2 = audio_encoder.open().unwrap();
let video_filter = video_filter(&video_encoder, &av_info).unwrap();
let audio_filter = audio_filter(&audio_encoder, av_info.timing.sample_rate).unwrap();
let video_encoder = video_encoder.open().unwrap();
let audio_encoder = audio_encoder.open().unwrap();
let emu = MyEmulator {
retro,
@ -61,8 +123,10 @@ impl MyEmulator {
audio_buf: Default::default(),
video_pixel_format: format::Pixel::RGB555,
video_frames: Default::default(),
video_encoder: video_encoder_2,
audio_encoder: audio_encoder_2,
video_encoder,
audio_encoder,
video_filter,
audio_filter,
sys_path: sys_path.as_ref().map(|x| x.as_ref().to_path_buf()),
};
@ -77,7 +141,6 @@ impl MyEmulator {
self.retro.run();
let vframe = self.video_frames.pop_front().unwrap();
eprintln!("video {}x{}, {} audio samples", vframe.width(), vframe.height(), self.audio_buf.len());
let mut aframe = frame::Audio::new(
format::Sample::I16(format::sample::Type::Packed),
self.audio_buf.len(),
@ -194,25 +257,29 @@ struct Opt {
system: Option<PathBuf>,
}
fn main() {
fn main() -> Fallible<()> {
let opt: Opt = Opt::from_args();
ffmpeg::init().unwrap();
let mut octx = format::output(&opt.video).unwrap();
let vcodec = ffmpeg::encoder::find(octx.format().codec(&opt.video, media::Type::Video))
.unwrap()
.video()
.unwrap();
let acodec = ffmpeg::encoder::find(octx.format().codec(&opt.video, media::Type::Audio))
.unwrap()
.audio()
.unwrap();
let mut octx = format::output(&opt.video)?;
let mut video_encoder = octx.add_stream(vcodec).unwrap().codec().encoder().video().unwrap();
let mut audio_encoder = octx.add_stream(acodec).unwrap().codec().encoder().audio().unwrap();
let detected_vcodec = octx.format().codec(&opt.video, media::Type::Video);
let detected_acodec = octx.format().codec(&opt.video, media::Type::Audio);
let vcodec = ffmpeg::encoder::find(detected_vcodec).unwrap().video()?;
let acodec = ffmpeg::encoder::find(detected_acodec).unwrap().audio()?;
let mut video_encoder = octx.add_stream(vcodec)?.codec().encoder().video()?;
let mut audio_encoder = octx.add_stream(acodec)?.codec().encoder().audio()?;
video_encoder.set_bit_rate(64000);
video_encoder.set_format(video_encoder.codec().unwrap().video()?.formats().unwrap().nth(0).unwrap());
audio_encoder.set_bit_rate(64000);
audio_encoder.set_rate(44100);
audio_encoder.set_format(audio_encoder.codec().unwrap().audio()?.formats().unwrap().nth(0).unwrap());
audio_encoder.set_channels(2);
audio_encoder.set_channel_layout(ChannelLayout::STEREO);
let mut emu = MyEmulator::new(opt.core, &opt.system, video_encoder, audio_encoder);
emu.load_game(opt.rom);
@ -224,4 +291,5 @@ fn main() {
}
octx.write_trailer().unwrap();
Ok(())
}