diff --git a/examples/ffmpeg_recorder.rs b/examples/ffmpeg_recorder.rs index d7df52d..f95936e 100644 --- a/examples/ffmpeg_recorder.rs +++ b/examples/ffmpeg_recorder.rs @@ -13,7 +13,7 @@ use ferretro::retro; use ferretro::retro::ffi::{PixelFormat, GameGeometry, SystemAvInfo, SystemInfo}; use ferretro::retro::wrapper::{LibretroWrapper, Handler}; -use ffmpeg::{ChannelLayout, Packet, filter, format, frame, media}; +use ffmpeg::{ChannelLayout, Packet, codec, filter, format, frame, media}; use ffmpeg::util::rational::Rational; struct MyEmulator { @@ -77,7 +77,7 @@ fn video_filter( } fn audio_filter( - audio_encoder: &ffmpeg::encoder::audio::Audio, + audio_encoder: &ffmpeg::codec::encoder::Audio, sample_rate: f64, ) -> Result { let mut afilter = filter::Graph::new(); @@ -99,6 +99,21 @@ fn audio_filter( .input("out", 0)? .parse("anull")?; afilter.validate()?; + // human-readable filter graph + eprintln!("{}", afilter.dump()); + + if let Some(codec) = audio_encoder.codec() { + if !codec + .capabilities() + .contains(ffmpeg::codec::capabilities::Capabilities::VARIABLE_FRAME_SIZE) + { + afilter + .get("out") + .unwrap() + .sink() + .set_frame_size(audio_encoder.frame_size()); + } + } Ok(afilter) } @@ -129,7 +144,7 @@ impl MyEmulator { let mut video_output = octx.add_stream(vcodec).unwrap(); let mut video_encoder = video_output.codec().encoder().video().unwrap(); - video_encoder.set_bit_rate(64000); + video_encoder.set_bit_rate(2560000); video_encoder.set_format(video_encoder.codec().unwrap().video().unwrap().formats().unwrap().nth(0).unwrap()); video_encoder.set_time_base(Rational::new(1, fps_int)); @@ -176,10 +191,12 @@ static bool ffmpeg_init_config(struct ff_config_param *params, 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); - - let audio_filter = audio_filter(&audio_encoder, av_info.timing.sample_rate).unwrap(); + audio_encoder.set_time_base(Rational::new(1, fps_int)); + audio_output.set_time_base(Rational::new(1, fps_int)); let mut audio_encoder = audio_encoder.open_as(acodec).unwrap(); + let audio_filter = audio_filter(&audio_encoder, av_info.timing.sample_rate).unwrap(); + audio_encoder.set_rate(av_info.timing.sample_rate.round() as i32); audio_output.set_parameters(&audio_encoder); @@ -217,7 +234,7 @@ static bool ffmpeg_init_config(struct ff_config_param *params, self.video_filter.get("in").unwrap().source().add(&vframe).unwrap(); let mut filtered_vframe = frame::Video::empty(); - while self.video_filter.get("out").unwrap().sink().frame(&mut filtered_vframe).is_ok() { + while let Ok(..) = self.video_filter.get("out").unwrap().sink().frame(&mut filtered_vframe) { if self.video_filter.get("in").unwrap().source().failed_requests() > 0 { println!("🎥 failed to put filter input frame"); } @@ -229,24 +246,27 @@ static bool ffmpeg_init_config(struct ff_config_param *params, if encoded_packet.size() > 0 { encoded_packet.set_stream(0); // use stream index... //encoded_packet.rescale_ts(Rational(1, 1), Rational(1, 60)); - encoded_packet.write(&mut self.octx).unwrap(); // AAA + encoded_packet.write_interleaved(&mut self.octx).unwrap(); // AAA } //encoded_packet.write_interleaved(&mut self.octx).unwrap(); // AAA } } + eprintln!("Audio buffer length {}", self.audio_buf.len()); let mut aframe = frame::Audio::new( format::Sample::I16(format::sample::Type::Packed), self.audio_buf.len(), ChannelLayout::STEREO ); + aframe.set_channels(2); aframe.set_rate(32040); + aframe.set_pts(Some(frame)); let aplane: &mut [(i16, i16)] = aframe.plane_mut(0); aplane.copy_from_slice(self.audio_buf.as_ref()); + //eprintln!("src: {:?}, dest: {:?}", self.audio_buf, aplane); self.audio_buf.clear(); - let mut out = ffmpeg::Packet::empty(); self.audio_filter.get("in").unwrap().source().add(&aframe).unwrap(); let mut filtered_aframe = frame::Audio::empty(); @@ -255,13 +275,14 @@ static bool ffmpeg_init_config(struct ff_config_param *params, println!("🎥 failed to put filter input frame"); } + filtered_aframe.set_pts(Some(frame)); self.audio_encoder.send_frame(&aframe).unwrap(); let mut encoded_packet = ffmpeg::Packet::empty(); while let Ok(..) = self.audio_encoder.receive_packet(&mut encoded_packet) { if encoded_packet.size() > 0 { encoded_packet.set_stream(1); //encoded_packet.rescale_ts(Rational(1, 1), Rational(1, 60)); - encoded_packet.write(&mut self.octx).unwrap(); // AAA + encoded_packet.write_interleaved(&mut self.octx).unwrap(); // AAA } } } @@ -283,6 +304,10 @@ static bool ffmpeg_init_config(struct ff_config_param *params, .load_game(Some(path), data, None) .unwrap(); } + + pub fn end(&mut self) { + self.octx.write_trailer().unwrap(); + } } impl retro::wrapper::Handler for MyEmulator { @@ -352,11 +377,9 @@ impl retro::wrapper::Handler for MyEmulator { } self.video_encoder.set_frame_rate(system_av_info.timing.fps.into()); - /* if system_av_info.timing.sample_rate.round() as i32 > 0 { self.audio_encoder.set_rate(system_av_info.timing.sample_rate.round() as i32); } - */ self.av_info.timing = system_av_info.timing; self.set_geometry(system_av_info.geometry); true @@ -431,6 +454,7 @@ fn main() -> Fallible<()> { let mut packet = Packet::empty(); eprintln!("flushed: {:?}", emu.video_encoder.flush(&mut packet).unwrap()); + emu.end(); //octx.write_trailer().unwrap(); Ok(()) }