Skip to content

Instantly share code, notes, and snippets.

@lubosz lubosz/CMakeLists.txt Secret
Last active Aug 29, 2015

Embed
What would you like to do?
minimal code for reproduction of T399
cmake_minimum_required(VERSION 2.8)
find_package(PkgConfig)
pkg_check_modules(GST REQUIRED gstreamer-1.0>=1.4.5
gstreamer-base-1.0>=1.4.5
gstreamer-gl-1.0>=1.4.5
gstreamer-video-1.0>=1.4.5
gstreamer-app-1.0>=1.4.5)
pkg_check_modules(GTK REQUIRED gtk+-3.0>=3.16.2)
include_directories(
${GST_INCLUDE_DIRS}
${GTK_INCLUDE_DIRS}
)
link_directories (
${GST_LIBRARY_DIRS}
${GTK_LIBRARY_DIRS}
)
add_executable(bug main.c)
target_link_libraries (
bug
${GST_LIBRARIES}
${GTK_LIBRARIES}
gstreamer-1.0 gstvideo-1.0 gstbase-1.0
)
#include <stdlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <gst/gst.h>
#include <gst/app/app.h>
#include <gst/video/videooverlay.h>
#include <glib-object.h>
G_BEGIN_DECLS
#define BUG_TYPE_CLIENT \
(bug_client_get_type ())
#define BUG_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), BUG_TYPE_CLIENT, \
BugClient))
#define BUG_IS_CLIENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), BUG_TYPE_CLIENT))
typedef struct _BugClient BugClient;
typedef struct _BugClientClass BugClientClass;
G_END_DECLS GST_DEBUG_CATEGORY_STATIC (debug_category);
#define GST_CAT_DEFAULT debug_category
typedef enum
{
BUG_CLIENT_STATE_STOPPED,
BUG_CLIENT_STATE_PLAYING,
BUG_CLIENT_STATE_PAUSED,
} BugClientState;
struct _BugClient
{
GObject parent_instance;
BugClientState state;
GstElement *pipeline;
GstElement *stream_bin;
GstElement *stream_decodebin;
GstPad *stream_src_pad;
gulong stream_pad_probe_id;
GstElement *still_bin;
GstElement *still_decodebin;
GstPad *still_src_pad;
GstElement *video_bin;
GstElement *glimagesink;
GstPad *video_sink_pad;
gchar *stream_uri;
gchar *frame_uri;
};
struct _BugClientClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE (BugClient, bug_client, G_TYPE_OBJECT)
static void update_frame_image (BugClient * self)
{
gst_pad_send_event (self->video_sink_pad, gst_event_new_flush_start ());
gst_pad_send_event (self->video_sink_pad, gst_event_new_flush_stop (FALSE));
if (self->frame_uri != NULL) {
gst_element_set_state (self->still_bin, GST_STATE_READY);
g_object_set ((GObject *) self->still_decodebin,
"uri", self->frame_uri, NULL);
gst_element_set_state (self->still_bin, GST_STATE_PAUSED);
}
}
static GstPadProbeReturn
stream_pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
return GST_PAD_PROBE_OK;
}
static void
stream_pad_probe_remove (BugClient * self)
{
if (self->stream_pad_probe_id != 0) {
gst_pad_remove_probe (self->stream_src_pad, self->stream_pad_probe_id);
self->stream_pad_probe_id = 0;
}
}
void
bug_client_stop (BugClient * self)
{
GstState state;
if (self->state == BUG_CLIENT_STATE_STOPPED)
return;
GST_DEBUG ("Transition to Stop");
//gst_element_set_state (self->still_bin, GST_STATE_NULL); // freezes
gst_element_set_state (self->pipeline, GST_STATE_NULL);
gst_element_get_state (self->pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
g_assert (state == GST_STATE_NULL);
if (self->state == BUG_CLIENT_STATE_PAUSED) {
GST_DEBUG ("Unlink still_src_pad");
gst_pad_unlink (self->still_src_pad, self->video_sink_pad);
} else {
GST_DEBUG ("Unlink stream_src_pad");
gst_pad_unlink (self->stream_src_pad, self->video_sink_pad);
}
stream_pad_probe_remove (self);
self->state = BUG_CLIENT_STATE_STOPPED;
}
void
bug_client_pause (BugClient * self)
{
GstState state;
if (self->state == BUG_CLIENT_STATE_PAUSED)
return;
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (self->pipeline),
GST_DEBUG_GRAPH_SHOW_ALL,
"onpause");
gst_element_set_state (self->pipeline, GST_STATE_PAUSED);
gst_element_get_state (self->pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
g_assert (state == GST_STATE_PAUSED);
if (self->state == BUG_CLIENT_STATE_PLAYING) {
GST_DEBUG ("Transition from PLAYING to PAUSED");
GST_DEBUG ("Unlink stream_src_pad");
gst_pad_unlink (self->stream_src_pad, self->video_sink_pad);
}
GST_DEBUG ("Link still_src_pad");
gst_pad_link (self->still_src_pad, self->video_sink_pad);
update_frame_image (self);
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (self->pipeline),
GST_DEBUG_GRAPH_SHOW_ALL,
"relink");
self->state = BUG_CLIENT_STATE_PAUSED;
}
void
bug_client_play (BugClient * self)
{
if (self->state == BUG_CLIENT_STATE_PLAYING)
return;
if (self->state == BUG_CLIENT_STATE_PAUSED) {
GST_DEBUG ("Transition from PAUSED to PLAYING");
gst_pad_send_event (self->video_sink_pad, gst_event_new_flush_start ());
gst_pad_send_event (self->video_sink_pad, gst_event_new_flush_stop (FALSE));
GST_DEBUG ("Unlink still_src_pad");
gst_element_set_state (self->still_bin, GST_STATE_READY);
gst_pad_unlink (self->still_src_pad, self->video_sink_pad);
g_clear_pointer (&self->frame_uri, g_free);
}
GST_DEBUG ("Link stream_src_pad");
gst_pad_link (self->stream_src_pad, self->video_sink_pad);
gst_element_set_state (self->pipeline, GST_STATE_PLAYING);
stream_pad_probe_remove (self);
self->state = BUG_CLIENT_STATE_PLAYING;
}
static gboolean
is_video_pad (GstPad * pad)
{
GstCaps *caps;
GstStructure *structure;
const gchar *type;
caps = gst_pad_query_caps (pad, NULL);
structure = gst_caps_get_structure (caps, 0);
type = gst_structure_get_name (structure);
if (g_str_has_prefix (type, "video/"))
return TRUE;
return FALSE;
}
static void
stream_decodebin_pad_added_cb (GstElement * element,
GstPad * src_pad, BugClient * self)
{
if (is_video_pad (src_pad))
gst_ghost_pad_set_target (GST_GHOST_PAD (self->stream_src_pad), src_pad);
}
static void
still_decodebin_pad_added_cb (GstElement * element,
GstPad * src_pad, BugClient * self)
{
if (is_video_pad (src_pad))
gst_ghost_pad_set_target (GST_GHOST_PAD (self->still_src_pad), src_pad);
}
static void
create_stream_bin (BugClient * self)
{
self->stream_bin = gst_bin_new (NULL);
self->stream_decodebin = gst_element_factory_make ("uridecodebin", NULL);
g_assert (self->stream_decodebin != NULL);
gst_bin_add_many (GST_BIN (self->stream_bin), self->stream_decodebin, NULL);
/* Add a ghost pad */
self->stream_src_pad = gst_ghost_pad_new_no_target (NULL, GST_PAD_SRC);
gst_element_add_pad (self->stream_bin, self->stream_src_pad);
g_signal_connect (self->stream_decodebin, "pad-added",
G_CALLBACK (stream_decodebin_pad_added_cb), self);
}
static void
create_still_bin (BugClient * self)
{
self->still_bin = gst_bin_new (NULL);
self->still_decodebin = gst_element_factory_make ("uridecodebin", NULL);
g_assert (self->still_decodebin != NULL);
gst_bin_add_many (GST_BIN (self->still_bin), self->still_decodebin, NULL);
/* Add a ghost pad */
self->still_src_pad = gst_ghost_pad_new_no_target (NULL, GST_PAD_SRC);
gst_element_add_pad (self->still_bin, self->still_src_pad);
g_signal_connect (self->still_decodebin, "pad-added",
G_CALLBACK (still_decodebin_pad_added_cb), self);
}
static void
create_video_bin (BugClient * self)
{
GstElement *glupload;
GstPad *sink_pad;
self->video_bin = gst_bin_new (NULL);
glupload = gst_element_factory_make ("glupload", NULL);
self->glimagesink = gst_element_factory_make ("glimagesink", NULL);
g_assert (glupload != NULL);
g_assert (self->glimagesink != NULL);
gst_bin_add_many (GST_BIN (self->video_bin),
glupload, self->glimagesink, NULL);
gst_element_link_many (glupload, self->glimagesink, NULL);
/* Add a ghost pad */
sink_pad = gst_element_get_static_pad (glupload, "sink");
self->video_sink_pad = gst_ghost_pad_new (NULL, sink_pad);
gst_element_add_pad (self->video_bin, self->video_sink_pad);
gst_object_unref (sink_pad);
}
static void
bug_client_init (BugClient * self)
{}
static void
bug_client_constructed (GObject * object)
{
BugClient *self = (BugClient *) object;
G_OBJECT_CLASS (bug_client_parent_class)->constructed (object);
/* Create pipeline */
self->pipeline = gst_pipeline_new (NULL);
create_stream_bin (self);
create_still_bin (self);
create_video_bin (self);
gst_bin_add_many (GST_BIN (self->pipeline),
self->stream_bin, self->still_bin, self->video_bin, NULL);
/* We are ready */
gst_element_set_state (self->pipeline, GST_STATE_READY);
gst_element_set_locked_state (self->still_bin, TRUE);
}
static void
bug_client_finalize (GObject * object)
{
BugClient *self = (BugClient *) object;
gst_element_set_state (self->still_bin, GST_STATE_NULL);
gst_element_set_state (self->pipeline, GST_STATE_NULL);
gst_object_unref (self->pipeline);
g_free (self->stream_uri);
g_free (self->frame_uri);
G_OBJECT_CLASS (bug_client_parent_class)->finalize (object);
}
static void
bug_client_class_init (BugClientClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (debug_category, "bugclient", 0, "Bug Client");
gst_debug_set_threshold_for_name ("glcontext", 5);
//gst_debug_set_threshold_for_name("glimagesink", 4);
object_class->constructed = bug_client_constructed;
object_class->finalize = bug_client_finalize;
}
BugClient *
bug_client_new (void)
{
return g_object_new (BUG_TYPE_CLIENT, NULL);
}
void
bug_client_set_window_handle (BugClient * self, guintptr handle)
{
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (self->glimagesink),
handle);
}
void
bug_client_expose (BugClient * self)
{
gst_video_overlay_expose (GST_VIDEO_OVERLAY (self->glimagesink));
}
void
bug_client_set_uri (BugClient * self, const gchar * uri)
{
g_free (self->stream_uri);
self->stream_uri = g_strdup (uri);
if (self->state == BUG_CLIENT_STATE_STOPPED) {
GST_DEBUG ("Change stream URI to %s", self->stream_uri);
g_object_set (self->stream_decodebin, "uri", self->stream_uri, NULL);
}
}
void
bug_client_set_frame_uri (BugClient * self, const gchar * uri)
{
g_free (self->frame_uri);
self->frame_uri = g_strdup (uri);
if (self->state == BUG_CLIENT_STATE_PAUSED)
update_frame_image (self);
}
typedef struct
{
GtkApplication *app;
GtkWidget *window;
GtkWidget *drawing_area;
BugClient *client;
gint task_number;
gchar *uri;
} Example;
static void
task_play (Example * self)
{
bug_client_set_uri (self->client, self->uri);
bug_client_play (self->client);
}
static void
task_resume (Example * self)
{
bug_client_play (self->client);
}
static void
task_still_image_1 (Example * self)
{
gchar *uri =
g_strdup_printf
("http://people.collabora.com/~xclaesse/sintel/still-image1.jpg");
bug_client_pause (self->client);
bug_client_set_frame_uri (self->client, uri);
g_free (uri);
}
static void
task_stop (Example * self)
{
g_usleep (500000);
bug_client_stop (self->client);
}
static void
task_stop_quit (Example * self)
{
task_stop (self);
g_application_quit (G_APPLICATION (self->app));
}
typedef void (*TaskFunc) (Example * self);
static TaskFunc tasks[] = {
task_play,
task_still_image_1,
task_stop,
task_resume,
task_stop_quit
};
static gboolean
test_continue (gpointer user_data)
{
Example *self = user_data;
if (self->task_number >= G_N_ELEMENTS (tasks)) {
g_application_quit (G_APPLICATION (self->app));
return G_SOURCE_REMOVE;
}
tasks[self->task_number] (self);
self->task_number++;
return G_SOURCE_CONTINUE;
}
static void
drawing_area_size_allocate_cb (GtkWidget * widget,
GdkRectangle * allocation, Example * self)
{
if (self->client != NULL)
bug_client_expose (self->client);
}
static void
startup_cb (GApplication * app, Example * self)
{
self->drawing_area = gtk_drawing_area_new ();
gtk_widget_set_size_request (self->drawing_area, 640, 480);
g_signal_connect (self->drawing_area, "size-allocate",
G_CALLBACK (drawing_area_size_allocate_cb), self);
gtk_widget_show (self->drawing_area);
self->window = gtk_application_window_new (self->app);
gtk_container_add (GTK_CONTAINER (self->window), self->drawing_area);
gtk_widget_show (self->window);
gtk_widget_realize (self->drawing_area);
self->client = bug_client_new ();
bug_client_set_window_handle (self->client,
GDK_WINDOW_XID (gtk_widget_get_window (self->drawing_area)));
}
static void
activate_cb (GApplication * app, Example * self)
{
gtk_window_present (GTK_WINDOW (self->window));
}
static void
open_cb (GApplication * application,
GFile ** files, gint n_files, gchar * hint, Example * self)
{
g_assert (n_files == 1);
self->uri = g_file_get_uri (files[0]);
if (test_continue (self))
g_timeout_add_seconds (5, test_continue, self);
}
gint
main (gint argc, gchar * argv[])
{
Example self = { NULL, };
gst_init (&argc, &argv);
self.app = gtk_application_new ("org.example.BugClient",
G_APPLICATION_HANDLES_OPEN);
g_signal_connect (self.app, "startup", G_CALLBACK (startup_cb), &self);
g_signal_connect (self.app, "activate", G_CALLBACK (activate_cb), &self);
g_signal_connect (self.app, "open", G_CALLBACK (open_cb), &self);
g_application_run ((GApplication *) self.app, argc, argv);
g_clear_object (&self.app);
g_clear_object (&self.client);
g_free (self.uri);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.