|
#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; |
|
} |