gstreamer-rs/tutorials/src/bin/basic-tutorial-4.rs

165 lines
5.4 KiB
Rust

use std::{io, io::Write};
use gst::prelude::*;
#[path = "../tutorials-common.rs"]
mod tutorials_common;
struct CustomData {
/// Our one and only element
playbin: gst::Element,
/// Are we in the PLAYING state?
playing: bool,
/// Should we terminate execution?
terminate: bool,
/// Is seeking enabled for this media?
seek_enabled: bool,
/// Have we performed the seek already?
seek_done: bool,
/// How long does this media last, in nanoseconds
duration: Option<gst::ClockTime>,
}
fn tutorial_main() {
// Initialize GStreamer
gst::init().unwrap();
let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
// Creat the playbin element
let playbin = gst::ElementFactory::make("playbin")
.name("playbin")
// Set the URI to play
.property("uri", uri)
.build()
.expect("Failed to create playbin element");
// Start playing
playbin
.set_state(gst::State::Playing)
.expect("Unable to set the playbin to the `Playing` state");
// Listen to the bus
let bus = playbin.bus().unwrap();
let mut custom_data = CustomData {
playbin,
playing: false,
terminate: false,
seek_enabled: false,
seek_done: false,
duration: gst::ClockTime::NONE,
};
while !custom_data.terminate {
let msg = bus.timed_pop(100 * gst::ClockTime::MSECOND);
match msg {
Some(msg) => {
handle_message(&mut custom_data, &msg);
}
None => {
if custom_data.playing {
let position = custom_data
.playbin
.query_position::<gst::ClockTime>()
.expect("Could not query current position.");
// If we didn't know it yet, query the stream duration
if custom_data.duration == gst::ClockTime::NONE {
custom_data.duration = custom_data.playbin.query_duration();
}
// Print current position and total duration
print!(
"\rPosition {} / {}",
position,
custom_data.duration.display()
);
io::stdout().flush().unwrap();
if custom_data.seek_enabled
&& !custom_data.seek_done
&& position > 10 * gst::ClockTime::SECOND
{
println!("\nReached 10s, performing seek...");
custom_data
.playbin
.seek_simple(
gst::SeekFlags::FLUSH | gst::SeekFlags::KEY_UNIT,
30 * gst::ClockTime::SECOND,
)
.expect("Failed to seek.");
custom_data.seek_done = true;
}
}
}
}
}
// Shutdown pipeline
custom_data
.playbin
.set_state(gst::State::Null)
.expect("Unable to set the playbin to the `Null` state");
}
fn handle_message(custom_data: &mut CustomData, msg: &gst::Message) {
use gst::MessageView;
match msg.view() {
MessageView::Error(err) => {
println!(
"Error received from element {:?}: {} ({:?})",
err.src().map(|s| s.path_string()),
err.error(),
err.debug()
);
custom_data.terminate = true;
}
MessageView::Eos(..) => {
println!("End-Of-Stream reached.");
custom_data.terminate = true;
}
MessageView::DurationChanged(_) => {
// The duration has changed, mark the current one as invalid
custom_data.duration = gst::ClockTime::NONE;
}
MessageView::StateChanged(state_changed) => {
if state_changed
.src()
.map(|s| s == &custom_data.playbin)
.unwrap_or(false)
{
let new_state = state_changed.current();
let old_state = state_changed.old();
println!("Pipeline state changed from {old_state:?} to {new_state:?}");
custom_data.playing = new_state == gst::State::Playing;
if custom_data.playing {
let mut seeking = gst::query::Seeking::new(gst::Format::Time);
if custom_data.playbin.query(&mut seeking) {
let (seekable, start, end) = seeking.result();
custom_data.seek_enabled = seekable;
if seekable {
println!("Seeking is ENABLED from {start} to {end}")
} else {
println!("Seeking is DISABLED for this stream.")
}
} else {
eprintln!("Seeking query failed.")
}
}
}
}
_ => (),
}
}
fn main() {
// tutorials_common::run is only required to set up the application environment on macOS
// (but not necessary in normal Cocoa applications where this is set up automatically)
tutorials_common::run(tutorial_main);
}