|
| 1 | +/* gstreamermm - a C++ wrapper for gstreamer |
| 2 | + * |
| 3 | + * Basic Tutorial 4: Time management |
| 4 | + * |
| 5 | + * This tutorial shows how to use GStreamer time-related facilities. In particular: |
| 6 | + * - How to query the pipeline for information like stream position or duration. |
| 7 | + * - How to seek (jump) to a different position (time) inside the stream. |
| 8 | + */ |
| 9 | + |
| 10 | +#include <gstreamermm.h> |
| 11 | +#include <glibmm/main.h> |
| 12 | +#include <iostream> |
| 13 | +#include <iomanip> |
| 14 | +#include <cstdlib> |
| 15 | + |
| 16 | +using Glib::RefPtr; |
| 17 | + |
| 18 | +// Expose objects to be used in callbacks |
| 19 | +RefPtr<Glib::MainLoop> mainloop; |
| 20 | +RefPtr<Gst::Element> playbin; |
| 21 | +static bool playing {false}; |
| 22 | +static bool seekable {false}; |
| 23 | +static bool seek_done {false}; |
| 24 | +static gint64 duration {(gint64)Gst::CLOCK_TIME_NONE}; |
| 25 | + |
| 26 | +static std::ostringstream format_gst_time(gint64 gst_time) |
| 27 | +{ |
| 28 | + std::ostringstream oss_gst_time (std::ostringstream::out); |
| 29 | + |
| 30 | + oss_gst_time << std::right << std::setfill('0') << |
| 31 | + std::setw(3) << Gst::get_hours(gst_time) << ":" << |
| 32 | + std::setw(2) << Gst::get_minutes(gst_time) << ":" << |
| 33 | + std::setw(2) << Gst::get_seconds(gst_time) << "." << |
| 34 | + std::setw(9) << std::left << Gst::get_fractional_seconds(gst_time); |
| 35 | + return oss_gst_time; |
| 36 | +} |
| 37 | + |
| 38 | +// This function is used to receive asynchronous messages in the main loop. |
| 39 | +bool on_bus_message(const RefPtr<Gst::Bus>&, |
| 40 | + const RefPtr<Gst::Message>& message) |
| 41 | +{ |
| 42 | + switch (message->get_message_type()) { |
| 43 | + case Gst::MESSAGE_EOS: |
| 44 | + std::cout << std::endl << "End of stream" << std::endl; |
| 45 | + mainloop->quit(); |
| 46 | + return false; |
| 47 | + case Gst::MESSAGE_ERROR: |
| 48 | + { |
| 49 | + RefPtr<Gst::MessageError> msgError {RefPtr<Gst::MessageError>::cast_static(message)}; |
| 50 | + if (msgError) |
| 51 | + { |
| 52 | + Glib::Error err {msgError->parse_error()}; |
| 53 | + std::string debug_info {msgError->parse_debug()}; |
| 54 | + std::cerr << "Error received from element " << message->get_source()->get_name() << ": " << |
| 55 | + err.what() << std::endl; |
| 56 | + if (!debug_info.empty()) |
| 57 | + std::cout << "Debugging information: " << debug_info << std::endl; |
| 58 | + } |
| 59 | + else |
| 60 | + { |
| 61 | + std::cerr << "Error." << std::endl; |
| 62 | + } |
| 63 | + mainloop->quit(); |
| 64 | + return false; |
| 65 | + } |
| 66 | + case Gst::MESSAGE_DURATION_CHANGED: |
| 67 | + /* The duration has changed, mark the current one as invalid */ |
| 68 | + duration = Gst::CLOCK_TIME_NONE; |
| 69 | + break; |
| 70 | + case Gst::MESSAGE_STATE_CHANGED: |
| 71 | + { |
| 72 | + // We are only interested in state-changed messages from the playbin |
| 73 | + if (RefPtr<Gst::Element>::cast_dynamic(message->get_source()) == playbin) |
| 74 | + { |
| 75 | + auto state_get_name = [] (Gst::State state) -> std::string { |
| 76 | + return gst_element_state_get_name(static_cast<GstState>(state)); |
| 77 | + }; |
| 78 | + RefPtr<Gst::MessageStateChanged> msgSC {RefPtr<Gst::MessageStateChanged>::cast_static(message)}; |
| 79 | + Gst::State old_state {msgSC->parse_old_state()}; |
| 80 | + Gst::State new_state {msgSC->parse_new_state()}; |
| 81 | + std::cout << "Pipeline state changed: " << |
| 82 | + state_get_name(old_state) << " -> " << |
| 83 | + state_get_name(new_state) << std::endl; |
| 84 | + /* Remember whether we are in the PLAYING state or not */ |
| 85 | + playing = (new_state == Gst::STATE_PLAYING); |
| 86 | + if (playing) |
| 87 | + { |
| 88 | + /* We just moved to PLAYING. Check if seeking is possible */ |
| 89 | + Gst::Format format {Gst::FORMAT_TIME}; |
| 90 | + RefPtr<Gst::Query> query {Gst::Query::create_seeking(format)}; |
| 91 | + if (playbin->query(query)) |
| 92 | + { |
| 93 | + gint64 segment_start {0}, segment_end {0}; |
| 94 | + RefPtr<Gst::QuerySeeking> seek_query = RefPtr<Gst::QuerySeeking>::cast_static(query); |
| 95 | + seek_query->parse(format, seekable, segment_start, segment_end); |
| 96 | + if (seekable) |
| 97 | + std::cout << "Seeking is ENABLED from " << format_gst_time(segment_start).str() << |
| 98 | + " to " << format_gst_time(segment_end).str() << std::endl; |
| 99 | + else |
| 100 | + std::cout << "Seeking is DISABLED for this stream." << std::endl; |
| 101 | + } |
| 102 | + } |
| 103 | + } |
| 104 | + break; |
| 105 | + } |
| 106 | + default: |
| 107 | + //std::cout << "Unhandled message type: " << message->get_message_type() << std::endl; |
| 108 | + break; |
| 109 | + } |
| 110 | + |
| 111 | + return true; |
| 112 | +} |
| 113 | + |
| 114 | +bool on_timeout() |
| 115 | +{ |
| 116 | + // only if playing |
| 117 | + if (playing) |
| 118 | + { |
| 119 | + /* Query the current position of the stream */ |
| 120 | + gint64 position {0}; |
| 121 | + if (!playbin->query_position(Gst::FORMAT_TIME, position)) |
| 122 | + std::cerr << "Could not query current position." << std::endl; |
| 123 | + |
| 124 | + /* If we didn't know it yet, query the stream duration */ |
| 125 | + if (duration == (gint64)Gst::CLOCK_TIME_NONE) |
| 126 | + { |
| 127 | + if (!playbin->query_duration(Gst::FORMAT_TIME, duration)) |
| 128 | + std::cerr << "Could not query current duration." << std::endl; |
| 129 | + } |
| 130 | + |
| 131 | + /* Print current position and total duration */ |
| 132 | + std::cout << format_gst_time(position).str() << "/" << |
| 133 | + format_gst_time(duration).str() << "\r" << std::flush; |
| 134 | + |
| 135 | + // If seeking is enabled, we have not done it yet, and the time is right, seek |
| 136 | + if (seekable && !seek_done && position > 10 * (gint64)Gst::SECOND) |
| 137 | + { |
| 138 | + std::cout << "Reached 10s, performing seek..." << std::endl; |
| 139 | + playbin->seek(Gst::FORMAT_TIME, Gst::SEEK_FLAG_FLUSH | Gst::SEEK_FLAG_KEY_UNIT, 30 * Gst::SECOND); |
| 140 | + seek_done = true; |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + return true; |
| 145 | +} |
| 146 | + |
| 147 | +int main(int argc, char** argv) |
| 148 | +{ |
| 149 | + // Initialize gstreamermm: |
| 150 | + Gst::init(argc, argv); |
| 151 | + |
| 152 | + // default uri |
| 153 | + Glib::ustring uri {"https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm"}; |
| 154 | + |
| 155 | + // Take the commandline argument and ensure that it is a uri: |
| 156 | + if (argc < 2) |
| 157 | + { |
| 158 | + std::cout << "Usage: " << argv[0] << " <uri>" << std::endl; |
| 159 | + std::cout << "missing uri argument, use default uri instead." << std::endl; |
| 160 | + } |
| 161 | + else if (Gst::URIHandler::uri_is_valid(argv[1])) |
| 162 | + { |
| 163 | + uri = argv[1]; |
| 164 | + } |
| 165 | + |
| 166 | + playbin = Gst::ElementFactory::create_element("playbin"); |
| 167 | + |
| 168 | + if (!playbin) |
| 169 | + { |
| 170 | + std::cerr << "The playbin element could not be created." << std::endl; |
| 171 | + return EXIT_FAILURE; |
| 172 | + } |
| 173 | + |
| 174 | + // Set the URI to play |
| 175 | + playbin->set_property("uri", uri); |
| 176 | + |
| 177 | + // Create the main loop. |
| 178 | + mainloop = Glib::MainLoop::create(); |
| 179 | + |
| 180 | + // Get the bus and watch the messages |
| 181 | + RefPtr<Gst::Bus> bus {playbin->get_bus()}; |
| 182 | + bus->add_watch(sigc::ptr_fun(&on_bus_message)); |
| 183 | + |
| 184 | + // start play back and listen to events |
| 185 | + if (playbin->set_state(Gst::STATE_PLAYING) == Gst::STATE_CHANGE_FAILURE) |
| 186 | + { |
| 187 | + std::cerr << "Unable to set the pipeline to the playing state." << std::endl; |
| 188 | + return EXIT_FAILURE; |
| 189 | + } |
| 190 | + |
| 191 | + // timeout of 100 milliseconds |
| 192 | + Glib::signal_timeout().connect(sigc::ptr_fun(&on_timeout), 100); |
| 193 | + |
| 194 | + // Now set the playbin to the PLAYING state and start the main loop: |
| 195 | + std::cout << "Running." << std::endl; |
| 196 | + mainloop->run(); |
| 197 | + |
| 198 | + // Clean up nicely: |
| 199 | + std::cout << "Returned. Stopping pipeline." << std::endl; |
| 200 | + playbin->set_state(Gst::STATE_NULL); |
| 201 | + |
| 202 | + return EXIT_SUCCESS; |
| 203 | +} |
0 commit comments