2018-11-05 10:10:14 +00:00
|
|
|
// This example demonstrates GStreamer's playbin element.
|
|
|
|
// This element takes an arbitrary URI as parameter, and if there is a source
|
|
|
|
// element within gstreamer, that supports this uri, the playbin will try
|
|
|
|
// to automatically create a pipeline that properly plays this media source.
|
|
|
|
// For this, the playbin internally relies on more bin elements, like the
|
|
|
|
// autovideosink and the decodebin.
|
|
|
|
// Essentially, this element is a single-element pipeline able to play
|
|
|
|
// any format from any uri-addressable source that gstreamer supports.
|
|
|
|
// Much of the playbin's behavior can be controlled by so-called flags, as well
|
|
|
|
// as the playbin's properties and signals.
|
|
|
|
|
2017-07-31 15:24:30 +00:00
|
|
|
extern crate gstreamer as gst;
|
2017-08-17 14:58:15 +00:00
|
|
|
use gst::prelude::*;
|
2017-07-31 15:24:30 +00:00
|
|
|
|
|
|
|
extern crate glib;
|
|
|
|
|
|
|
|
use std::env;
|
|
|
|
|
2017-11-12 18:07:02 +00:00
|
|
|
#[path = "../examples-common.rs"]
|
|
|
|
mod examples_common;
|
|
|
|
|
|
|
|
fn example_main() {
|
2017-07-31 15:24:30 +00:00
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
let args: Vec<_> = env::args().collect();
|
|
|
|
let uri: &str = if args.len() == 2 {
|
|
|
|
args[1].as_ref()
|
|
|
|
} else {
|
2017-11-10 15:53:32 +00:00
|
|
|
println!("Usage: playbin uri");
|
2017-11-27 11:01:03 +00:00
|
|
|
std::process::exit(-1)
|
2017-07-31 15:24:30 +00:00
|
|
|
};
|
|
|
|
|
2018-11-05 10:10:14 +00:00
|
|
|
// Create a new playbin element, and tell it what uri to play back.
|
2017-07-31 15:24:30 +00:00
|
|
|
let playbin = gst::ElementFactory::make("playbin", None).unwrap();
|
2017-08-17 14:58:15 +00:00
|
|
|
playbin
|
|
|
|
.set_property("uri", &glib::Value::from(uri))
|
|
|
|
.unwrap();
|
2017-07-31 15:24:30 +00:00
|
|
|
|
|
|
|
// For flags handling
|
2018-11-05 10:10:14 +00:00
|
|
|
// With flags, one can configure playbin's behavior such as whether it
|
|
|
|
// should play back contained video streams, or if it should render subtitles.
|
2017-07-31 15:24:30 +00:00
|
|
|
// let flags = playbin.get_property("flags").unwrap();
|
|
|
|
// let flags_class = FlagsClass::new(flags.type_()).unwrap();
|
|
|
|
// let flags = flags_class.builder_with_value(flags).unwrap()
|
|
|
|
// .unset_by_nick("text")
|
|
|
|
// .unset_by_nick("video")
|
|
|
|
// .build()
|
|
|
|
// .unwrap();
|
|
|
|
// playbin.set_property("flags", &flags).unwrap();
|
|
|
|
|
2018-11-05 10:10:14 +00:00
|
|
|
// The playbin also provides any kind of metadata that it found in the played stream.
|
|
|
|
// For this, the playbin provides signals notifying about changes in the metadata.
|
|
|
|
// Doing this with a signal makes sense for multiple reasons.
|
|
|
|
// - The metadata is only found after the pipeline has been started
|
|
|
|
// - Live streams (such as internet radios) update this metadata during the stream
|
|
|
|
// Note that this signal will be emitted from the streaming threads usually,
|
|
|
|
// not the application's threads!
|
2017-08-11 12:31:59 +00:00
|
|
|
playbin
|
|
|
|
.connect("audio-tags-changed", false, |values| {
|
2018-11-05 10:10:14 +00:00
|
|
|
// The metadata of any of the contained audio streams changed
|
|
|
|
// In the case of a live-stream from an internet radio, this could for example
|
|
|
|
// mark the beginning of a new track, or a new DJ.
|
2017-08-11 12:31:59 +00:00
|
|
|
let playbin = values[0].get::<glib::Object>().unwrap();
|
2018-11-05 10:10:14 +00:00
|
|
|
// This gets the index of the stream that changed. This is neccessary, since
|
|
|
|
// there could e.g. be multiple audio streams (english, spanish, ...).
|
2017-08-11 12:31:59 +00:00
|
|
|
let idx = values[1].get::<i32>().unwrap();
|
|
|
|
|
|
|
|
println!("audio tags of audio stream {} changed:", idx);
|
|
|
|
|
2018-11-05 10:10:14 +00:00
|
|
|
// HELP: is this correct?
|
|
|
|
// We were only notified about the change of metadata. If we want to do
|
|
|
|
// something with it, we first need to actually query the metadata from the playbin.
|
|
|
|
// We do this by facilliating the get-audio-tags action-signal on playbin.
|
|
|
|
// Sending an action-signal to an element essentially is a function call on the element.
|
|
|
|
// It is done that way, because elements do not have their own function API, they are
|
|
|
|
// relying on GStreamer and GLib's API. The only way an element can communicate with an
|
|
|
|
// application is via properties, signals or action signals (or custom messages, events, queries).
|
|
|
|
// So what the following code does, is essentially asking playbin to tell us its already
|
|
|
|
// internally stored tag list for this stream index.
|
2017-08-11 12:31:59 +00:00
|
|
|
let tags = playbin
|
|
|
|
.emit("get-audio-tags", &[&idx.to_value()])
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
2017-08-17 14:58:15 +00:00
|
|
|
let tags = tags.get::<gst::TagList>().unwrap();
|
2017-08-11 12:31:59 +00:00
|
|
|
|
2017-08-17 14:58:15 +00:00
|
|
|
if let Some(artist) = tags.get::<gst::tags::Artist>() {
|
2017-08-11 12:31:59 +00:00
|
|
|
println!(" Artist: {}", artist.get().unwrap());
|
|
|
|
}
|
|
|
|
|
2017-08-17 14:58:15 +00:00
|
|
|
if let Some(title) = tags.get::<gst::tags::Title>() {
|
2017-08-11 12:31:59 +00:00
|
|
|
println!(" Title: {}", title.get().unwrap());
|
|
|
|
}
|
|
|
|
|
2017-08-17 14:58:15 +00:00
|
|
|
if let Some(album) = tags.get::<gst::tags::Album>() {
|
2017-08-11 12:31:59 +00:00
|
|
|
println!(" Album: {}", album.get().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
2018-10-08 12:02:23 +00:00
|
|
|
})
|
|
|
|
.unwrap();
|
2017-08-11 12:31:59 +00:00
|
|
|
|
2018-11-05 10:10:14 +00:00
|
|
|
// The playbin element itself is a playbin, so it can be used as one, despite being
|
|
|
|
// created from an element factory.
|
2017-07-31 15:24:30 +00:00
|
|
|
let bus = playbin.get_bus().unwrap();
|
|
|
|
|
2019-01-08 16:13:37 +00:00
|
|
|
playbin
|
|
|
|
.set_state(gst::State::Playing)
|
|
|
|
.expect("Unable to set the pipeline to the `Playing` state");
|
2017-07-31 15:24:30 +00:00
|
|
|
|
2018-12-27 22:06:03 +00:00
|
|
|
for msg in bus.iter_timed(gst::CLOCK_TIME_NONE) {
|
2017-08-17 14:58:15 +00:00
|
|
|
use gst::MessageView;
|
|
|
|
|
2017-07-31 15:24:30 +00:00
|
|
|
match msg.view() {
|
|
|
|
MessageView::Eos(..) => break,
|
|
|
|
MessageView::Error(err) => {
|
|
|
|
println!(
|
2017-11-16 11:58:56 +00:00
|
|
|
"Error from {:?}: {} ({:?})",
|
2018-01-29 12:25:12 +00:00
|
|
|
err.get_src().map(|s| s.get_path_string()),
|
2017-07-31 15:24:30 +00:00
|
|
|
err.get_error(),
|
|
|
|
err.get_debug()
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-08 16:13:37 +00:00
|
|
|
playbin
|
|
|
|
.set_state(gst::State::Null)
|
|
|
|
.expect("Unable to set the pipeline to the `Null` state");
|
2017-07-31 15:24:30 +00:00
|
|
|
}
|
2017-11-12 18:07:02 +00:00
|
|
|
|
|
|
|
fn main() {
|
|
|
|
// tutorials_common::run is only required to set up the application environent on macOS
|
|
|
|
// (but not necessary in normal Cocoa applications where this is set up autmatically)
|
|
|
|
examples_common::run(example_main);
|
|
|
|
}
|