#bevy-plugin #encoder #frame #applications #capturing #rendering #headless

bevy_capture

A Bevy plugin for capturing frames from a Bevy application

4 releases (2 breaking)

new 0.2.0 Dec 6, 2024
0.1.1 Sep 4, 2024
0.1.0 Aug 29, 2024
0.0.0 Aug 16, 2024

#462 in Game dev

39 downloads per month

MIT/Apache

45KB
639 lines

bevy_capture

crates.io docs.rs

A Bevy plugin for capturing frames from a Bevy application. It comes with some built-in encoders, e.g. for creating gifs or videos, and can be easily extended with custom encoders.

Current Limitations

  • Only headless rendering is supported, but windowed rendering should be possible as well. PRs are welcome!

Built-in Encoders

Name Description Required Features
FramesEncoder Encodes frames into individual images.
GifEncoder Encodes frames into a gif. gif
Mp4Openh264Encoder Encodes frames into an mp4 using openh264. mp4_openh264
Mp4FfmpegCliEncoder Encodes frames into an mp4 using the ffmpeg CLI (ffmpeg must be in PATH). mp4_ffmpeg_cli

Usage

For a complete example, see the simple example.

// Add plugins
app.add_plugins((
    DefaultPlugins
        .build()
        // Disable the WinitPlugin to prevent the creation of a window
        .disable::<WinitPlugin>()
        // Make sure pipelines are ready before rendering
        .set(RenderPlugin {
            synchronous_pipeline_compilation: true,
            ..default()
        }),
    // Add the ScheduleRunnerPlugin to run the app in loop mode
    ScheduleRunnerPlugin {
        run_mode: RunMode::Loop { wait: None },
    },
    // Add the CapturePlugin
    bevy_capture::CapturePlugin,
));

// Spawn a camera with the CaptureBundle
fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
  commands.spawn((
      Camera2d,
      Camera::default().target_headless(512, 512, &mut images),
      CaptureBundle::default(),
  ));
}

// Start capturing
fn update(mut capture: Query<&mut Capture>) {
  let mut capture = capture.single_mut();
  if !capture.is_capturing() {
    capture.start(
      GifEncoder::new(File::create("my_capture.gif").unwrap())
        .with_repeat(gif::Repeat::Infinite)
    );
  }
}

Implementing a Custom Encoder

struct MyCustomEncoder;

impl Encoder for MyCustomEncoder {
    fn encode(&mut self, image: &Image) -> Result<()> {
        // Called for each frame.
        todo!("Encode the image into your custom format.")
    }

    fn finish(self: Box<Self>) {
      // Called when the encoder is stopped.
      todo!("Finish encoding the frames, if necessary.")
    }
}

Alternatives

  • bevy_image_export: Less opinionated, no encoders included, only image sequences. This might be a better fit, if you end up using ffmpeg on the frames anyway.

License

Licensed under either of

at your option.

Dependencies

~39–75MB
~1.5M SLoC