1eca1631a7
CoglGst is a GStreamer integration library that facilitates video playback using the Cogl API. It works by retrieving each video frame from the GStreamer pipeline and attaching it to a Cogl pipeline in the form of a Cogl texture along with possible color model conversion shaders. The pipeline is then retrieved by the user during each draw. An example use of the CoglGst API is included in the examples directory. Reviewed-by: Neil Roberts <neil@linux.intel.com> (cherry picked from commit dfb94cf4b0b6b42d6465df362a0c0af780596890)
249 lines
6.6 KiB
C
249 lines
6.6 KiB
C
#include <cogl/cogl.h>
|
|
#include <cogl-gst/cogl-gst.h>
|
|
|
|
typedef struct _Data
|
|
{
|
|
CoglFramebuffer *fb;
|
|
CoglPipeline *pln;
|
|
CoglGstVideoSink *sink;
|
|
CoglBool draw_ready;
|
|
CoglBool frame_ready;
|
|
GMainLoop *main_loop;
|
|
}Data;
|
|
|
|
static CoglBool
|
|
_bus_watch (GstBus *bus,
|
|
GstMessage *msg,
|
|
void *user_data)
|
|
{
|
|
Data *data = (Data*) user_data;
|
|
switch (GST_MESSAGE_TYPE (msg))
|
|
{
|
|
case GST_MESSAGE_EOS:
|
|
{
|
|
g_main_loop_quit (data->main_loop);
|
|
break;
|
|
}
|
|
case GST_MESSAGE_ERROR:
|
|
{
|
|
char *debug;
|
|
GError *error = NULL;
|
|
|
|
gst_message_parse_error (msg, &error, &debug);
|
|
g_free (debug);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_error ("Playback error: %s\n", error->message);
|
|
g_error_free (error);
|
|
}
|
|
g_main_loop_quit (data->main_loop);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_draw (Data *data)
|
|
{
|
|
/*
|
|
The cogl pipeline needs to be retrieved from the sink before every draw.
|
|
This is due to the cogl-gst sink creating a new cogl pipeline for each frame
|
|
by copying the previous one and attaching the new frame to it.
|
|
*/
|
|
CoglPipeline* current = cogl_gst_video_sink_get_pipeline (data->sink);
|
|
|
|
cogl_framebuffer_clear4f (data->fb,
|
|
COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_DEPTH, 0,
|
|
0, 0, 1);
|
|
data->pln = current;
|
|
|
|
cogl_framebuffer_push_matrix (data->fb);
|
|
cogl_framebuffer_translate (data->fb, 640 / 2, 480 / 2, 0);
|
|
cogl_framebuffer_draw_textured_rectangle (data->fb, data->pln, -320, -240,
|
|
320, 240, 0, 0, 1, 1);
|
|
cogl_framebuffer_pop_matrix (data->fb);
|
|
|
|
cogl_onscreen_swap_buffers (COGL_ONSCREEN (data->fb));
|
|
}
|
|
|
|
static void
|
|
_check_draw (Data *data)
|
|
{
|
|
/* The frame is only drawn once we know that a new buffer is ready
|
|
* from GStreamer and that Cogl is ready to accept some new
|
|
* rendering */
|
|
if (data->draw_ready && data->frame_ready)
|
|
{
|
|
_draw (data);
|
|
data->draw_ready = FALSE;
|
|
data->frame_ready = FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_frame_callback (CoglOnscreen *onscreen,
|
|
CoglFrameEvent event,
|
|
CoglFrameInfo *info,
|
|
void *user_data)
|
|
{
|
|
Data *data = user_data;
|
|
|
|
if (event == COGL_FRAME_EVENT_SYNC)
|
|
{
|
|
data->draw_ready = TRUE;
|
|
_check_draw (data);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_new_frame_cb (CoglGstVideoSink *sink,
|
|
Data *data)
|
|
{
|
|
data->frame_ready = TRUE;
|
|
_check_draw (data);
|
|
}
|
|
|
|
/*
|
|
A callback like this should be attached to the cogl-pipeline-ready
|
|
signal. This way requesting the cogl pipeline before its creation
|
|
by the sink is avoided. At this point, user textures and snippets can
|
|
be added to the cogl pipeline.
|
|
*/
|
|
|
|
static void
|
|
_set_up_pipeline (gpointer instance,
|
|
gpointer user_data)
|
|
{
|
|
Data* data = (Data*) user_data;
|
|
|
|
/*
|
|
The cogl-gst sink, depending on the video format, can use up to 3 texture
|
|
layers to render a frame. To avoid overwriting frame data, the first
|
|
free layer in the cogl pipeline needs to be queried before adding any
|
|
additional textures.
|
|
*/
|
|
|
|
int free_layer = cogl_gst_video_sink_get_free_layer (data->sink);
|
|
data->pln = cogl_gst_video_sink_get_pipeline (data->sink);
|
|
|
|
while (free_layer > 0)
|
|
{
|
|
free_layer--;
|
|
cogl_pipeline_set_layer_filters (data->pln, free_layer,
|
|
COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR,
|
|
COGL_PIPELINE_FILTER_LINEAR);
|
|
}
|
|
|
|
cogl_onscreen_add_frame_callback (COGL_ONSCREEN (data->fb), _frame_callback,
|
|
data, NULL);
|
|
|
|
/*
|
|
The cogl-gst-new-frame signal is emitted when the cogl-gst sink has
|
|
retrieved a new frame and attached it to the cogl pipeline. This can be
|
|
used to make sure cogl doesn't do any unnecessary drawing i.e. keeps to the
|
|
frame-rate of the video.
|
|
*/
|
|
|
|
g_signal_connect (data->sink, "new-frame", G_CALLBACK (_new_frame_cb), data);
|
|
}
|
|
|
|
int
|
|
main (int argc,
|
|
char **argv)
|
|
{
|
|
Data data;
|
|
CoglContext *ctx;
|
|
CoglOnscreen *onscreen;
|
|
CoglMatrix view;
|
|
float fovy, aspect, z_near, z_2d, z_far;
|
|
GstElement *pipeline;
|
|
GstElement *bin;
|
|
GSource *cogl_source;
|
|
GstBus *bus;
|
|
char *uri;
|
|
|
|
/* Set the necessary cogl elements */
|
|
|
|
ctx = cogl_context_new (NULL, NULL);
|
|
onscreen = cogl_onscreen_new (ctx, 640, 480);
|
|
data.fb = COGL_FRAMEBUFFER (onscreen);
|
|
cogl_onscreen_show (onscreen);
|
|
|
|
cogl_framebuffer_set_viewport (data.fb, 0, 0, 640, 480);
|
|
fovy = 60;
|
|
aspect = 640 / 480;
|
|
z_near = 0.1;
|
|
z_2d = 1000;
|
|
z_far = 2000;
|
|
|
|
cogl_framebuffer_perspective (data.fb, fovy, aspect, z_near, z_far);
|
|
cogl_matrix_init_identity (&view);
|
|
cogl_matrix_view_2d_in_perspective (&view, fovy, aspect, z_near, z_2d,
|
|
640, 480);
|
|
cogl_framebuffer_set_modelview_matrix (data.fb, &view);
|
|
|
|
/* Intialize GStreamer */
|
|
|
|
gst_init (&argc, &argv);
|
|
|
|
/*
|
|
Create the cogl-gst video sink by calling the cogl_gst_video_sink_new
|
|
function and passing it a CoglContext (this is used to create the
|
|
CoglPipeline and the texures for each frame). Alternatively you can use
|
|
gst_element_factory_make ("coglsink", "some_name") and then set the
|
|
context with cogl_gst_video_sink_set_context.
|
|
*/
|
|
|
|
data.sink = cogl_gst_video_sink_new (ctx);
|
|
|
|
pipeline = gst_pipeline_new ("gst-player");
|
|
bin = gst_element_factory_make ("playbin", "bin");
|
|
|
|
if (argc < 2)
|
|
uri = "http://docs.gstreamer.com/media/sintel_trailer-480p.webm";
|
|
else
|
|
uri = argv[1];
|
|
|
|
g_object_set (G_OBJECT (bin), "video-sink", GST_ELEMENT (data.sink), NULL);
|
|
|
|
|
|
gst_bin_add (GST_BIN (pipeline), bin);
|
|
|
|
g_object_set (G_OBJECT (bin), "uri", uri, NULL);
|
|
|
|
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
|
gst_bus_add_watch (bus, _bus_watch, &data);
|
|
|
|
data.main_loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
cogl_source = cogl_glib_source_new (ctx, G_PRIORITY_DEFAULT);
|
|
g_source_attach (cogl_source, NULL);
|
|
|
|
/*
|
|
The cogl-pipeline-ready signal tells you when the cogl pipeline is
|
|
initialized i.e. when cogl-gst has figured out the video format and
|
|
is prepared to retrieve and attach the first frame of the video.
|
|
*/
|
|
|
|
g_signal_connect (data.sink, "pipeline-ready",
|
|
G_CALLBACK (_set_up_pipeline), &data);
|
|
|
|
data.draw_ready = TRUE;
|
|
data.frame_ready = FALSE;
|
|
|
|
g_main_loop_run (data.main_loop);
|
|
|
|
g_source_destroy (cogl_source);
|
|
g_source_unref (cogl_source);
|
|
|
|
g_main_loop_unref (data.main_loop);
|
|
|
|
return 0;
|
|
}
|