nathanbabcock / ffmpeg-sidecar

Wrap a standalone FFmpeg binary in an intuitive Iterator interface. 🏍
MIT License
289 stars 21 forks source link

Adding metadata to videos #9

Closed jungerm2 closed 1 year ago

jungerm2 commented 1 year ago

First off, thanks for the fantastic work on this crate, it's very useful and easy to use.

I'm currently using this crate to stitch together images to create a video using the image2 muxer, like so:

let pbar = ProgressBar::new(num_frames);

let cmd = format!(
    "-framerate {fps} -f image2 -i {pattern} -y -vcodec libx264 -crf 22 -pix_fmt yuv420p {outfile}"
);

let mut ffmpeg_runner = FfmpegCommand::new().args(cmd.split(' ')).spawn().unwrap();
ffmpeg_runner
    .iter()
    .unwrap()
    .filter_progress()
    .for_each(|progress| pbar.set_position(progress.frame as u64));
pbar.finish_and_clear();

However, I'd like to add metadata to the resulting video. According to the ffmpeg docs I can do so by adding an argument like "-metadata title='some cool title'" right before the output file. If I do this in the above example the code silently fails. I'm not entirely sure what's going on but my guess is that splitting the command on spaces causes issues.

Any suggestions for this? Is there a way to better report any errors that ffmpeg might be silently throwing? In fact, I've noticed that if the command starts with ffmpeg it also fails silently.

nathanbabcock commented 1 year ago

I haven't taken a close look at this yet, but the first thing I'd try is do it without filter_progress() -- instead, use for_each() immediately and use a match statement to sort out different types of messages. The iterator already includes every ffmpeg log message, including any errors that might be happning, so you can try printing those out. They are of type FfmpegEvent::Log I think.

I can give a better example of what I mean later this weekend.

nathanbabcock commented 1 year ago

@jungerm2 Turns out it's something even simpler. When you call .split(' '), it splits your title 'some cool title' into three different arguments! After displaying the FFmpeg log messages, you would see an error message, something like "cool" is not a valid argument.

To fix this, you can add the arguments this way instead:

.args(["-metadata", "title=some cool title"])

Keep in mind you can call arg or args multiple times, each time they just append additional arguments. Also you should omit the single quotes from the title entirely, otherwise they will be included in the metadata and show up as the video's title. Lastly, I would recommend printing the FFmpeg log messages while developing/debugging your program, in order to catch errors like this in the future.

Here's a working example: https://github.com/nathanbabcock/ffmpeg-sidecar/blob/main/examples/metadata.rs. If you clone the master branch, you can run this with the command cargo run --example metadata.

Hope this helps!

jungerm2 commented 1 year ago

Thanks for the detailed response and example code. It clarifies a lot. I had tried using args as I suspected split was creating multiple arguments, but I must've miss used it...

One thing I should say for anyone reading this after the fact is that the metadata args need to come right before the output arg, i.e: .args(cmd.split(' ')).args(metadata_args).output(outfile).

Anyways, it seems to work fine now, so I'm going to close this issue. Thanks!