[Cogl] [PATCH 15/15] cogl-gst: expose aspect ratio apis

Lionel Landwerlin llandwerlin at gmail.com
Sat Jun 22 01:58:59 PDT 2013


From: Robert Bragg <robert at linux.intel.com>

This adds several utility apis that aim to make it as easy as possible
for an application to determine what size a video should be drawn at.

The important detail here is that these apis take into account the
pixel-aspect-ratio in addition to the video's own aspect ratio.

This patch updates the cogl-basic-video-player example to use the
cogl_gst_video_sink_fit_size() api to perform letterboxing.

Reviewed-by: Neil Roberts <neil at linux.intel.com>

(cherry picked from commit d26f17c97ff6b9f6d6211e0527d5965a85305a56)
---
 cogl-gst/cogl-gst-video-sink.c               |  57 +++++++++++
 cogl-gst/cogl-gst-video-sink.h               |  92 ++++++++++++++++++
 doc/reference/cogl-gst/cogl-gst-sections.txt |   4 +
 examples/cogl-basic-video-player.c           | 139 +++++++++++++++++++++------
 4 files changed, 262 insertions(+), 30 deletions(-)

diff --git a/cogl-gst/cogl-gst-video-sink.c b/cogl-gst/cogl-gst-video-sink.c
index 42d44b7..b4dada5 100644
--- a/cogl-gst/cogl-gst-video-sink.c
+++ b/cogl-gst/cogl-gst-video-sink.c
@@ -1268,3 +1268,60 @@ cogl_gst_video_sink_new (CoglContext *ctx)
 
   return sink;
 }
+
+float
+cogl_gst_video_sink_get_aspect (CoglGstVideoSink *vt)
+{
+  GstVideoInfo *info = &vt->priv->info;
+  return ((float)info->width * (float)info->par_n) /
+    ((float)info->height * (float)info->par_d);
+}
+
+float
+cogl_gst_video_sink_get_width_for_height (CoglGstVideoSink *vt,
+                                          float height)
+{
+  float aspect = cogl_gst_video_sink_get_aspect (vt);
+  return height * aspect;
+}
+
+float
+cogl_gst_video_sink_get_height_for_width (CoglGstVideoSink *vt,
+                                          float width)
+{
+  float aspect = cogl_gst_video_sink_get_aspect (vt);
+  return width / aspect;
+}
+
+void
+cogl_gst_video_sink_fit_size (CoglGstVideoSink *vt,
+                              const CoglGstRectangle *available,
+                              CoglGstRectangle *output)
+{
+  if (available->height == 0.0f)
+    {
+      output->x = available->x;
+      output->y = available->y;
+      output->width = output->height = 0;
+    }
+  else
+    {
+      float available_aspect = available->width / available->height;
+      float video_aspect = cogl_gst_video_sink_get_aspect (vt);
+
+      if (video_aspect > available_aspect)
+        {
+          output->width = available->width;
+          output->height = available->width / video_aspect;
+          output->x = available->x;
+          output->y = available->y + (available->height - output->height) / 2;
+        }
+      else
+        {
+          output->width = available->height * video_aspect;
+          output->height = available->height;
+          output->x = available->x + (available->width - output->width) / 2;
+          output->y = available->y;
+        }
+    }
+}
diff --git a/cogl-gst/cogl-gst-video-sink.h b/cogl-gst/cogl-gst-video-sink.h
index a9694a6..45ed835 100644
--- a/cogl-gst/cogl-gst-video-sink.h
+++ b/cogl-gst/cogl-gst-video-sink.h
@@ -330,6 +330,98 @@ void
 cogl_gst_video_sink_setup_pipeline (CoglGstVideoSink *sink,
                                     CoglPipeline *pipeline);
 
+/**
+ * cogl_gst_video_sink_get_aspect:
+ * @sink: A #CoglGstVideoSink
+ *
+ * Returns a width-for-height aspect ratio that lets you calculate a
+ * suitable width for displaying your video based on a given height by
+ * multiplying your chosen height by the returned aspect ratio.
+ *
+ * This aspect ratio is calculated based on the underlying size of the
+ * video buffers and the current pixel-aspect-ratio.
+ *
+ * Return value: a width-for-height aspect ratio
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+float
+cogl_gst_video_sink_get_aspect (CoglGstVideoSink *sink);
+
+/**
+ * cogl_gst_video_sink_get_width_for_height:
+ * @sink: A #CoglGstVideoSink
+ * @height: A specific output @height
+ *
+ * Calculates a suitable output width for a specific output @height
+ * that will maintain the video's aspect ratio.
+ *
+ * Return value: An output width for the given output @height.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+float
+cogl_gst_video_sink_get_width_for_height (CoglGstVideoSink *sink,
+                                          float height);
+
+/**
+ * cogl_gst_video_sink_get_height_for_width:
+ * @sink: A #CoglGstVideoSink
+ * @width: A specific output @width
+ *
+ * Calculates a suitable output height for a specific output @width
+ * that will maintain the video's aspect ratio.
+ *
+ * Return value: An output height for the given output @width.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+float
+cogl_gst_video_sink_get_height_for_width (CoglGstVideoSink *sink,
+                                          float width);
+
+/**
+ * CoglGstRectangle:
+ * @x: The X coordinate of the top left of the rectangle
+ * @y: The Y coordinate of the top left of the rectangle
+ * @width: The width of the rectangle
+ * @height: The height of the rectangle
+ *
+ * Describes a rectangle that can be used for video output.
+ */
+typedef struct _CoglGstRectangle
+{
+  float x;
+  float y;
+  float width;
+  float height;
+} CoglGstRectangle;
+
+/**
+ * cogl_gst_video_sink_fit_size:
+ * @sink: A #CoglGstVideoSink
+ * @available: The space available for video output
+ * @output: The return location for the calculated output position
+ *
+ * Calculates a suitable @output rectangle that can fit inside the
+ * @available space while maintaining the aspect ratio of the current
+ * video.
+ *
+ * Applications would typically use this api for "letterboxing" by
+ * using this api to position a video inside a fixed screen space and
+ * filling the remaining space with black borders.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_gst_video_sink_fit_size (CoglGstVideoSink *sink,
+                              const CoglGstRectangle *available,
+                              CoglGstRectangle *output);
+
 G_END_DECLS
 
 #endif
diff --git a/doc/reference/cogl-gst/cogl-gst-sections.txt b/doc/reference/cogl-gst/cogl-gst-sections.txt
index ea0e761..35dd368 100644
--- a/doc/reference/cogl-gst/cogl-gst-sections.txt
+++ b/doc/reference/cogl-gst/cogl-gst-sections.txt
@@ -13,6 +13,10 @@ cogl_gst_video_sink_setup_pipeline
 cogl_gst_video_sink_get_free_layer
 cogl_gst_video_sink_set_first_layer
 cogl_gst_video_sink_set_default_sample
+cogl_gst_video_sink_get_aspect
+cogl_gst_video_sink_get_width_for_height
+cogl_gst_video_sink_get_height_for_width
+cogl_gst_video_sink_fit_size
 
 <SUBSECTION Standard>
 COGL_GST_IS_VIDEO_SINK
diff --git a/examples/cogl-basic-video-player.c b/examples/cogl-basic-video-player.c
index ac6bb4b..ba1e6c6 100644
--- a/examples/cogl-basic-video-player.c
+++ b/examples/cogl-basic-video-player.c
@@ -1,17 +1,23 @@
+#include <stdbool.h>
+
 #include <cogl/cogl.h>
 #include <cogl-gst/cogl-gst.h>
 
 typedef struct _Data
 {
   CoglFramebuffer *fb;
-  CoglPipeline *pln;
+  CoglPipeline *border_pipeline;
+  CoglPipeline *video_pipeline;
   CoglGstVideoSink *sink;
-  CoglBool draw_ready;
-  CoglBool frame_ready;
+  int onscreen_width;
+  int onscreen_height;
+  CoglGstRectangle video_output;
+  bool draw_ready;
+  bool frame_ready;
   GMainLoop *main_loop;
 }Data;
 
-static CoglBool
+static gboolean
 _bus_watch (GstBus *bus,
             GstMessage *msg,
             void *user_data)
@@ -57,16 +63,55 @@ _draw (Data *data)
   */
   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;
+  data->video_pipeline = 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);
+  if (data->video_output.x)
+    {
+      int x = data->video_output.x;
+
+      /* Letterboxed with vertical borders */
+      cogl_framebuffer_draw_rectangle (data->fb,
+                                       data->border_pipeline,
+                                       0, 0, x, data->onscreen_height);
+      cogl_framebuffer_draw_rectangle (data->fb,
+                                       data->border_pipeline,
+                                       data->onscreen_width - x,
+                                       0,
+                                       data->onscreen_width,
+                                       data->onscreen_height);
+      cogl_framebuffer_draw_rectangle (data->fb, data->video_pipeline,
+                                       x, 0,
+                                       x + data->video_output.width,
+                                       data->onscreen_height);
+    }
+  else if (data->video_output.y)
+    {
+      int y = data->video_output.y;
+
+      /* Letterboxed with horizontal borders */
+      cogl_framebuffer_draw_rectangle (data->fb,
+                                       data->border_pipeline,
+                                       0, 0, data->onscreen_width, y);
+      cogl_framebuffer_draw_rectangle (data->fb,
+                                       data->border_pipeline,
+                                       0,
+                                       data->onscreen_height - y,
+                                       data->onscreen_width,
+                                       data->onscreen_height);
+      cogl_framebuffer_draw_rectangle (data->fb, data->video_pipeline,
+                                       0, y,
+                                       data->onscreen_width,
+                                       y + data->video_output.height);
+
+    }
+  else
+    {
+      cogl_framebuffer_draw_rectangle (data->fb,
+                                       data->video_pipeline,
+                                       0, 0,
+                                       data->onscreen_width,
+                                       data->onscreen_height);
+    }
 
   cogl_onscreen_swap_buffers (COGL_ONSCREEN (data->fb));
 }
@@ -108,6 +153,32 @@ _new_frame_cb (CoglGstVideoSink *sink,
   _check_draw (data);
 }
 
+static void
+_resize_callback (CoglOnscreen *onscreen,
+                  int width,
+                  int height,
+                  void *user_data)
+{
+  Data *data = user_data;
+  CoglGstRectangle available;
+
+  data->onscreen_width = width;
+  data->onscreen_height = height;
+
+  cogl_framebuffer_orthographic (data->fb, 0, 0, width, height, -1, 100);
+
+  if (!data->video_pipeline)
+    return;
+
+  available.x = 0;
+  available.y = 0;
+  available.width = width;
+  available.height = height;
+  cogl_gst_video_sink_fit_size (data->sink,
+                                &available,
+                                &data->video_output);
+}
+
 /*
   A callback like this should be attached to the cogl-pipeline-ready
   signal. This way requesting the cogl pipeline before its creation
@@ -129,16 +200,26 @@ _set_up_pipeline (gpointer instance,
   */
 
   int free_layer = cogl_gst_video_sink_get_free_layer (data->sink);
-  data->pln = cogl_gst_video_sink_get_pipeline (data->sink);
+  data->video_pipeline = 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_set_layer_filters (data->video_pipeline, free_layer,
                                        COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR,
                                        COGL_PIPELINE_FILTER_LINEAR);
     }
 
+  /* disable blending... */
+  cogl_pipeline_set_blend (data->video_pipeline,
+                           "RGBA = ADD (SRC_COLOR, 0)", NULL);
+
+  /* Now that we know the video size we can perform letterboxing */
+  _resize_callback (COGL_ONSCREEN (data->fb),
+                    data->onscreen_width,
+                    data->onscreen_height,
+                    data);
+
   cogl_onscreen_add_frame_callback (COGL_ONSCREEN (data->fb), _frame_callback,
                                     data, NULL);
 
@@ -159,33 +240,31 @@ main (int argc,
   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;
 
+  memset (&data, 0, sizeof (Data));
+
   /* 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_set_resizable (onscreen, TRUE);
+  cogl_onscreen_add_resize_callback (onscreen, _resize_callback, &data, NULL);
   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);
+  data.fb = COGL_FRAMEBUFFER (onscreen);
+  cogl_framebuffer_orthographic (data.fb, 0, 0, 640, 480, -1, 100);
+
+  data.border_pipeline = cogl_pipeline_new (ctx);
+  cogl_pipeline_set_color4f (data.border_pipeline, 0, 0, 0, 1);
+  /* disable blending */
+  cogl_pipeline_set_blend (data.border_pipeline,
+                           "RGBA = ADD (SRC_COLOR, 0)", NULL);
 
   /* Intialize GStreamer */
 
-- 
1.8.3.1



More information about the Cogl mailing list