Skip to content

Instantly share code, notes, and snippets.

@pdesantis
Last active November 8, 2018 19:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pdesantis/3c6382ee43852ad8bfc51d2d37547616 to your computer and use it in GitHub Desktop.
Save pdesantis/3c6382ee43852ad8bfc51d2d37547616 to your computer and use it in GitHub Desktop.
A simple program to create a GStreamer or GStreamer Editing Services pipeline

GStreamer Editing Services Pipeline debug

Usage:

main <path-to-file> <\"gst\" or \"ges\"> <playback-rate> <start-position-in-seconds>
  • <path-to-file>: The file path to the media file to play.
  • <\"gst\" or \"ges\">: Whether to create just a GStreamer pipeline, or a GStreamer Editing Services pipeline.
  • <playback-rate>: The playback rate. 1.0 for playback at normal speed.
  • <start-position-in-seconds>: Where to seek to before playing the media file. 0 to play from the beginning.
#include <cstdlib>
#include <iostream>
#include <thread>
#include <ges/ges.h>
#include <gst/app/gstappsink.h>
bool setAndWaitForPipelineState(GstElement *pipeline, GstState targetState) {
GstStateChangeReturn ret = gst_element_set_state(pipeline, targetState);
GstState result = GST_STATE_NULL;
GstState pendingState = GST_STATE_NULL;
if (ret == GST_STATE_CHANGE_ASYNC) {
double timeout = 10.0;
double step = 0.1;
while (result != targetState && timeout > 0) {
gst_element_get_state(pipeline,
&result, &pendingState, GST_SECOND * step);
timeout -= step;
}
if (timeout <= 0) {
std::cout << "Failed to set pipeline state "
<< gst_element_state_get_name(targetState);
std::cout.flush();
return false;
}
}
return true;
}
void seek(GstElement *pipeline, gdouble const playback_rate, gint64 const start_position) {
auto flags =
static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_TRICKMODE);
while (gst_element_seek(pipeline, playback_rate, GST_FORMAT_TIME, flags,
GST_SEEK_TYPE_SET, start_position, GST_SEEK_TYPE_NONE,
GST_CLOCK_TIME_NONE) == FALSE) {
std::cout << "Seek failed, will try again...";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout.flush();
}
}
void launch_gstreamer_pipeline(std::string const file_path, gdouble const playback_rate, gint64 const start_position) {
GError *err = nullptr;
std::string description = "filesrc location=" + file_path + " ! decodebin name=dmux \
dmux. ! queue ! videoconvert ! autovideosink \
dmux. ! queue ! audioconvert ! autoaudiosink";
GstElement *pipeline = gst_parse_launch(description.c_str(), &err);
setAndWaitForPipelineState(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
seek(pipeline, playback_rate, start_position);
}
void launch_ges_pipeline(std::string const file_path, gdouble const playback_rate, gint64 const start_position) {
// Setup GES pipeline
ges_init();
GESPipeline *pipeline = ges_pipeline_new();
GstElement *pipelineElement = GST_ELEMENT(pipeline);
GESTimeline *timeline = ges_timeline_new_audio_video ();
GESLayer *layer = ges_layer_new ();
ges_timeline_add_layer(timeline, layer);
// Load file as a clip & add to timeline
std::string uri_string = "file://" + file_path;
GESUriClip *clip = ges_uri_clip_new(uri_string.c_str());
ges_layer_add_clip(layer, GES_CLIP(clip));
ges_pipeline_set_timeline(pipeline, timeline);
// Setup audio & video sinks
GstElement *audioSink = gst_element_factory_make("autoaudiosink", "audio-sink");
ges_pipeline_preview_set_audio_sink(pipeline, audioSink);
GstElement *videoSink = gst_element_factory_make("autovideosink", "video-sink");
ges_pipeline_preview_set_video_sink(pipeline, videoSink);
setAndWaitForPipelineState(pipelineElement, GST_STATE_PAUSED);
setAndWaitForPipelineState(pipelineElement, GST_STATE_PLAYING);
seek(pipelineElement, playback_rate, start_position);
}
int main(int argc, char **argv) {
if(argc < 5) {
std::cout << "Usage: " << argv[0] << " <path-to-file> <\"gst\" or \"ges\"> <playback-rate> <start-position-in-seconds>\n";
return 1;
}
std::string const file_path = argv[1];
std::string const pipeline_type = argv[2];
std::string const playback_rate_arg = argv[3];
std::string const start_position_arg = argv[4];
gdouble const playback_rate = std::stod(playback_rate_arg);
gint64 const start_position = std::stol(start_position_arg) * GST_SECOND;
gst_init(&argc, &argv);
GMainLoop *mainloop;
if (pipeline_type == "gst") {
std::thread pipelineThread(launch_gstreamer_pipeline, file_path, playback_rate, start_position);
pipelineThread.detach();
} else if (pipeline_type == "ges") {
std::thread pipelineThread(launch_ges_pipeline, file_path, playback_rate, start_position);
pipelineThread.detach();
} else {
std::cout << "Second argument must be either \"gst\" or \"ges\"";
return 1;
}
// Start a GMainLoop. GES **REQUIRES** a GMainLoop to be running in
// order to function properly!
mainloop = g_main_loop_new(nullptr, FALSE);
g_main_loop_run(mainloop);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment