How can i call "ffprobe -i video.mp4" with rust

I am a beginner and i search for the first functions i need.
So, how can i call a normal programm with rust like "ffprobe -i video.mp4"?

In the standard library there's Command (read also the module level doc) which should be enough for most uses. But you can find higher level libraries on crates.io.

fn main() {
   std::process::Command::new("ffprobe")
        .arg("-i")
        .arg("video.mp4")
        .status()
        .expect("process failed to execute");
}
2 Likes

Thank you!
I had searched syscall, but I was not heard on "std :: process :: Command".
:slight_smile:

So, i have the next problem.
ffprobe -i video.mp4
put his output to error-out, not to std-out...

how can i catch it?

thise dosn't work:

fn main() {
    let a = std::process::Command::new("ffprobe")
        .arg("-i")
        .arg("video.mp4")
        .status()
        .expect("ffprobe konnte nicht ausgeführt werden");

    println!("================================================================================");
    println!("{}", a);
}

You’d want to use .output() instead of .status():

fn main() {
    let output = std::process::Command::new("ffprobe")
        .arg("-i")
        .arg("video.webm")
        .output()
        .expect("process failed to execute");
    println!("completed with {}", output.status);
    let s = String::from_utf8_lossy(&output.stderr);
    for line in s.lines() {
        if line.starts_with("    encoder") {
            println!("{}", &line);
        }
    }
}
2 Likes

Thank you!
it works :slight_smile:

how can I read in the output of "ffprobe -i video.mp4" with "ffprobe::Stream - Rust" and then read the fields individually Interrogate? Can someone please write me an example?
Thanks!

1 Like

Because that's not what a syscall is. Starting a program is called a "subprocess". A syscall is something completely different, it means accessing low-level functions directly provided by the kernel.

1 Like

Hello, it doesn't have to be done with a system call. Basically, I don't care how the call is made. My only problem is that I have no idea how to use "ffprobe::Stream". I just want to have the data from a film with this function in an example and then be able to read out the fields. But I'm a complete novice programmer at compiler languages. I've only written scripts using BASH and other CLI programs for the past 20 years. I'm very good at that. But I'm reading the book for Rust right now. But these things are not in there.

Try calling the ffprobe::ffprobe function from the crate instead of invoking a system command. You need to build a Path to your file and pass it to the function, then you will get a Result with an ffprobe::FfProbe where you can iterate through the streams field. Something like this might work:

use ffprobe::{ffprobe, FfProbe, Stream};
use std::path::Path;

let probe = ffprobe(Path::new("video.mp4")).unwrap();
for (i, stream) in probe.streams.into_iter().enumerate() {
    println!("Stream #{} codec_name: {}", i, stream.codec_name);
}

The documentation for Rust crates is amazing. You quickly look at how ffprobe is implemented and you can see that it is doing the exact same command invokation for you and parses the output to a struct.

I know, I was only referring to the system calls because you wrote that you searched for the term "syscall" but failed to find std::process::Command. So, I was trying to point out that this is because "syscall" is not the right term to search for, and the expression you actually want to search for is "subcommand".

Ok I understand; Thanks!

Great. Thank you!

But it doesn't work for me.
I think I have to import ffprobe first ...?
I think this should be it: GitHub - theduke/ffprobe-rs: Simple ffprobe wraper for Rust.
How do you do that?

I did it like this:

[root@freebsd ~]# cd /usr/ports/lang/rust && make clean && make && make install ; make clean
[root@freebsd ~]# cd /usr/ports/devel/git && make clean && make && make install ; make clean

$ cargo install cargo-edit

$ echo '[package]' > Cargo.toml

$ cargo add ffprobe
Updating 'GitHub - rust-lang/crates.io-index: Registry index for crates.io' index
Adding ffprobe v0.1.0 to dependencies

$ cat Cargo.toml
$ cat Cargo.toml
[package]

[dependencies]
ffprobe = "0.1.0"

and then I gave the source code a main function. In the end I got this error message.

$ vi ~/Rust/ffprobe_Film.rs

use ffprobe::{ffprobe, FfProbe, Stream};
use std::path::Path;

fn main() {
let probe = ffprobe(Path::new("video.mp4")).unwrap();
for (i, stream) in probe.streams.into_iter().enumerate() {
println!("Stream #{} codec_name: {}", i, stream.codec_name);
}
}

$ cd ~/Rust && ( rm -f ffprobe_Film ; rustc ffprobe_Film.rs; ~/Rust/ffprobe_Film)
error[E0432]: unresolved imports ffprobe, ffprobe::FfProbe, ffprobe::Stream
--> ffprobe_Film.rs:1:5
|
1 | use ffprobe::{ffprobe, FfProbe, Stream};
| ^^^^^^^ ^^^^^^^ ^^^^^^
| |
| maybe a missing crate ffprobe?

error: aborting due to previous error

For more information about this error, try rustc --explain E0432.

You'll need to use cargo build to compile your program, rather than executing rustc directly.

The easiest way to do this is to use cargo new to create the initial directory structure (including the Cargo.toml and the main source file), and then run commands like cargo add inside this directory. For more details, see the book.

2 Likes

OK

$ cargo new pro01
Created binary (application) pro01 package

$ cargo add ffprobe
Command failed due to unhandled error: Unable to find Cargo.toml

$ cd pro01/

$ cargo add ffprobe
Updating 'GitHub - rust-lang/crates.io-index: Registry index for crates.io' index
Adding ffprobe v0.1.0 to dependencies

$ vi ffprobe_Film.rs

$ cargo build
Updating crates.io index
Downloaded unicode-xid v0.2.2
Downloaded syn v1.0.72
Downloaded ffprobe v0.1.0
Downloaded 3 crates (251.3 KB) in 0.86s
Compiling proc-macro2 v1.0.26
Compiling unicode-xid v0.2.2
Compiling syn v1.0.72
Compiling serde_derive v1.0.125
Compiling ryu v1.0.5
Compiling serde v1.0.125
Compiling serde_json v1.0.64
Compiling itoa v0.4.7
Compiling quote v1.0.9
Compiling ffprobe v0.1.0
Compiling pro01 v0.1.0 (~/pro01)
Finished dev [unoptimized + debuginfo] target(s) in 13.64s

so far so good
and how can I build and start my program now?

It's right in the linked documentation: it's cargo run.

1 Like

By default, Cargo compiles the file at src/main.rs. Put your code inside that file instead of ffprobe_Film.rs. Then use the cargo run command to build and run your program.

(You can also override the default source file path if you want, but I recommend getting used the default locations first.)

OK!

$ vi src/main.rs
use ffprobe::{ffprobe, FfProbe, Stream};
use std::path::Path;

fn main() {
        let probe = ffprobe(Path::new("video.mp4")).unwrap();
        for (i, stream) in probe.streams.into_iter().enumerate() {
                println!("Stream #{} codec_name: {}", i, stream.codec_name);
        }
}



$ cargo run
   Compiling pro01 v0.1.0 (/home/manfred/pro01)
warning: unused imports: `FfProbe`, `Stream`
 --> src/main.rs:5:24
  |
5 | use ffprobe::{ffprobe, FfProbe, Stream};
  |                        ^^^^^^^  ^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: 1 warning emitted

    Finished dev [unoptimized + debuginfo] target(s) in 0.53s
     Running `target/debug/pro01`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: FfProbeError { message: "Could not deserialize ffprobe output: missing field `codec_time_base` at line 58 column 9" }', src/main.rs:9:49
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace