vaapisink: allow scaling to ignore aspect ratio.
[platform/upstream/gstreamer-vaapi.git] / gst / vaapi / gstvaapisink.c
index 4c8d46d..092169a 100644 (file)
@@ -83,7 +83,9 @@ GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapisink);
 
 /* Default template */
 static const char gst_vaapisink_sink_caps_str[] =
-#if !GST_CHECK_VERSION(1,0,0)
+#if GST_CHECK_VERSION(1,0,0)
+    GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) "; "
+#else
     "video/x-raw-yuv, "
     "width  = (int) [ 1, MAX ], "
     "height = (int) [ 1, MAX ]; "
@@ -153,8 +155,10 @@ enum {
     PROP_DISPLAY_TYPE,
     PROP_FULLSCREEN,
     PROP_SYNCHRONOUS,
+    PROP_USE_GLX,
     PROP_USE_REFLECTION,
     PROP_ROTATION,
+    PROP_FORCE_ASPECT_RATIO,
 };
 
 #define DEFAULT_DISPLAY_TYPE            GST_VAAPI_DISPLAY_TYPE_ANY
@@ -253,8 +257,10 @@ static void
 gst_vaapisink_destroy(GstVaapiSink *sink)
 {
     gst_buffer_replace(&sink->video_buffer, NULL);
-    g_clear_object(&sink->texture);
-    g_clear_object(&sink->display);
+#if USE_GLX
+    gst_vaapi_texture_replace(&sink->texture, NULL);
+#endif
+    gst_vaapi_display_replace(&sink->display, NULL);
     g_clear_object(&sink->uploader);
 
     gst_caps_replace(&sink->caps, NULL);
@@ -329,12 +335,13 @@ gst_vaapisink_ensure_display(GstVaapiSink *sink)
 {
     GstVaapiDisplayType display_type;
     GstVaapiRenderMode render_mode;
+    const gboolean had_display = sink->display != NULL;
 
     if (!gst_vaapi_ensure_display(sink, sink->display_type, &sink->display))
         return FALSE;
 
     display_type = gst_vaapi_display_get_display_type(sink->display);
-    if (display_type != sink->display_type) {
+    if (display_type != sink->display_type || (!had_display && sink->display)) {
         GST_INFO("created %s %p", get_display_type_name(display_type),
             sink->display);
         sink->display_type = display_type;
@@ -375,6 +382,19 @@ gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height)
     if (!sink->caps)
         return TRUE;
 
+    if (!sink->keep_aspect) {
+        display_rect->width = width;
+        display_rect->height = height;
+        display_rect->x = 0;
+        display_rect->y = 0;
+
+        GST_DEBUG("force-aspect-ratio is false; distorting while scaling video");
+        GST_DEBUG("render rect (%d,%d):%ux%u",
+                  display_rect->x, display_rect->y,
+                  display_rect->width, display_rect->height);
+        return TRUE;
+    }
+
     GST_DEBUG("ensure render rect within %ux%u bounds", width, height);
 
     gst_vaapi_display_get_pixel_aspect_ratio(
@@ -540,7 +560,7 @@ gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id)
         gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)) == xid)
         return TRUE;
 
-    g_clear_object(&sink->window);
+    gst_vaapi_window_replace(&sink->window, NULL);
 
     switch (sink->display_type) {
 #if USE_GLX
@@ -667,13 +687,7 @@ gst_vaapisink_start(GstBaseSink *base_sink)
 {
     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
 
-    if (!gst_vaapisink_ensure_display(sink))
-        return FALSE;
-
-    sink->uploader = gst_vaapi_uploader_new(sink->display);
-    if (!sink->uploader)
-        return FALSE;
-    return TRUE;
+    return gst_vaapisink_ensure_uploader(sink);
 }
 
 static gboolean
@@ -685,8 +699,8 @@ gst_vaapisink_stop(GstBaseSink *base_sink)
 #if GST_CHECK_VERSION(1,0,0)
     g_clear_object(&sink->video_buffer_pool);
 #endif
-    g_clear_object(&sink->window);
-    g_clear_object(&sink->display);
+    gst_vaapi_window_replace(&sink->window, NULL);
+    gst_vaapi_display_replace(&sink->display, NULL);
     g_clear_object(&sink->uploader);
 
     return TRUE;
@@ -714,7 +728,17 @@ gst_vaapisink_get_caps_impl(GstBaseSink *base_sink)
 static inline GstCaps *
 gst_vaapisink_get_caps(GstBaseSink *base_sink, GstCaps *filter)
 {
-    return gst_vaapisink_get_caps_impl(base_sink);
+    GstCaps *caps, *out_caps;
+
+    caps = gst_vaapisink_get_caps_impl(base_sink);
+    if (caps && filter) {
+        out_caps = gst_caps_intersect_full(caps, filter,
+            GST_CAPS_INTERSECT_FIRST);
+        gst_caps_unref(caps);
+    }
+    else
+        out_caps = caps;
+    return out_caps;
 }
 #else
 #define gst_vaapisink_get_caps gst_vaapisink_get_caps_impl
@@ -821,20 +845,37 @@ render_background(GstVaapiSink *sink)
 }
 
 static void
-render_frame(GstVaapiSink *sink)
+render_frame(GstVaapiSink *sink, GstVaapiSurface *surface,
+    const GstVaapiRectangle *surface_rect)
 {
     const guint x1 = sink->display_rect.x;
     const guint x2 = sink->display_rect.x + sink->display_rect.width;
     const guint y1 = sink->display_rect.y;
     const guint y2 = sink->display_rect.y + sink->display_rect.height;
+    gfloat tx1, tx2, ty1, ty2;
+    guint width, height;
+
+    if (surface_rect) {
+        gst_vaapi_surface_get_size(surface, &width, &height);
+        tx1 = (gfloat)surface_rect->x / width;
+        ty1 = (gfloat)surface_rect->y / height;
+        tx2 = (gfloat)(surface_rect->x + surface_rect->width) / width;
+        ty2 = (gfloat)(surface_rect->y + surface_rect->height) / height;
+    }
+    else {
+        tx1 = 0.0f;
+        ty1 = 0.0f;
+        tx2 = 1.0f;
+        ty2 = 1.0f;
+    }
 
     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
     glBegin(GL_QUADS);
     {
-        glTexCoord2f(0.0f, 0.0f); glVertex2i(x1, y1);
-        glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y2);
-        glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y2);
-        glTexCoord2f(1.0f, 0.0f); glVertex2i(x2, y1);
+        glTexCoord2f(tx1, ty1); glVertex2i(x1, y1);
+        glTexCoord2f(tx1, ty2); glVertex2i(x1, y2);
+        glTexCoord2f(tx2, ty2); glVertex2i(x2, y2);
+        glTexCoord2f(tx2, ty1); glVertex2i(x2, y1);
     }
     glEnd();
 }
@@ -862,10 +903,47 @@ render_reflection(GstVaapiSink *sink)
 }
 
 static gboolean
+gst_vaapisink_ensure_texture(GstVaapiSink *sink, GstVaapiSurface *surface)
+{
+    GstVideoRectangle tex_rect, dis_rect, out_rect;
+    guint width, height;
+
+    if (sink->texture)
+        return TRUE;
+
+    gst_vaapi_surface_get_size(surface, &width, &height);
+    tex_rect.x = 0;
+    tex_rect.y = 0;
+    tex_rect.w = width;
+    tex_rect.h = height;
+
+    gst_vaapi_display_get_size(sink->display, &width, &height);
+    dis_rect.x = 0;
+    dis_rect.y = 0;
+    dis_rect.w = width;
+    dis_rect.h = height;
+
+    gst_video_sink_center_rect(tex_rect, dis_rect, &out_rect, TRUE);
+
+    /* XXX: use surface size for now since some VA drivers have issues
+       with downscaling to the provided texture size. i.e. we should be
+       using the resulting out_rect size, which preserves the aspect
+       ratio of the surface */
+    width = tex_rect.w;
+    height = tex_rect.h;
+    GST_INFO("texture size %ux%u", width, height);
+
+    sink->texture = gst_vaapi_texture_new(sink->display,
+        GL_TEXTURE_2D, GL_BGRA, width, height);
+    return sink->texture != NULL;
+}
+
+static gboolean
 gst_vaapisink_show_frame_glx(
-    GstVaapiSink    *sink,
-    GstVaapiSurface *surface,
-    guint            flags
+    GstVaapiSink               *sink,
+    GstVaapiSurface            *surface,
+    const GstVaapiRectangle    *surface_rect,
+    guint                       flags
 )
 {
     GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(sink->window);
@@ -873,17 +951,8 @@ gst_vaapisink_show_frame_glx(
     GLuint texture;
 
     gst_vaapi_window_glx_make_current(window);
-    if (!sink->texture) {
-        sink->texture = gst_vaapi_texture_new(
-            sink->display,
-            GL_TEXTURE_2D,
-            GL_BGRA,
-            sink->video_width,
-            sink->video_height
-        );
-        if (!sink->texture)
-            goto error_create_texture;
-    }
+    if (!gst_vaapisink_ensure_texture(sink, surface))
+        goto error_create_texture;
     if (!gst_vaapi_texture_put_surface(sink->texture, surface, flags))
         goto error_transfer_surface;
 
@@ -903,7 +972,7 @@ gst_vaapisink_show_frame_glx(
             glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
             glTranslatef(50.0f, 0.0f, 0.0f);
         }
-        render_frame(sink);
+        render_frame(sink, surface, surface_rect);
         if (sink->use_reflection) {
             glPushMatrix();
             glTranslatef(0.0, (GLfloat)sink->display_rect.height + 5.0f, 0.0f);
@@ -933,13 +1002,14 @@ error_transfer_surface:
 
 static inline gboolean
 gst_vaapisink_put_surface(
-    GstVaapiSink    *sink,
-    GstVaapiSurface *surface,
-    guint            flags
+    GstVaapiSink               *sink,
+    GstVaapiSurface            *surface,
+    const GstVaapiRectangle    *surface_rect,
+    guint                       flags
 )
 {
     if (!gst_vaapi_window_put_surface(sink->window, surface,
-                NULL, &sink->display_rect, flags)) {
+                surface_rect, &sink->display_rect, flags)) {
         GST_DEBUG("could not render VA surface");
         return FALSE;
     }
@@ -955,12 +1025,26 @@ gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
     GstBuffer *buffer;
     guint flags;
     gboolean success;
+    GstVaapiRectangle *surface_rect = NULL;
+#if GST_CHECK_VERSION(1,0,0)
+    GstVaapiRectangle tmp_rect;
+#endif
 
     meta = gst_buffer_get_vaapi_video_meta(src_buffer);
 #if GST_CHECK_VERSION(1,0,0)
     if (!meta)
         return GST_FLOW_EOS;
     buffer = gst_buffer_ref(src_buffer);
+
+    GstVideoCropMeta * const crop_meta =
+        gst_buffer_get_video_crop_meta(buffer);
+    if (crop_meta) {
+        surface_rect = &tmp_rect;
+        surface_rect->x = crop_meta->x;
+        surface_rect->y = crop_meta->y;
+        surface_rect->width = crop_meta->width;
+        surface_rect->height = crop_meta->height;
+    }
 #else
     if (meta)
         buffer = gst_buffer_ref(src_buffer);
@@ -982,10 +1066,9 @@ gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
     }
 #endif
 
-    if (sink->display != gst_vaapi_video_meta_get_display(meta)) {
-        g_clear_object(&sink->display);
-        sink->display = g_object_ref(gst_vaapi_video_meta_get_display(meta));
-    }
+    if (sink->display != gst_vaapi_video_meta_get_display(meta))
+        gst_vaapi_display_replace(&sink->display,
+            gst_vaapi_video_meta_get_display(meta));
 
     if (!sink->window)
         goto error;
@@ -999,30 +1082,43 @@ gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
     GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
               GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
 
+    if (!surface_rect)
+        surface_rect = (GstVaapiRectangle *)
+            gst_vaapi_video_meta_get_render_rect(meta);
+
+    if (surface_rect)
+        GST_DEBUG("render rect (%d,%d), size %ux%u",
+                  surface_rect->x, surface_rect->y,
+                  surface_rect->width, surface_rect->height);
+
     flags = gst_vaapi_video_meta_get_render_flags(meta);
 
     if (!gst_vaapi_apply_composition(surface, src_buffer))
         GST_WARNING("could not update subtitles");
 
     switch (sink->display_type) {
-#if USE_GLX
-    case GST_VAAPI_DISPLAY_TYPE_GLX:
-        success = gst_vaapisink_show_frame_glx(sink, surface, flags);
-        break;
-#endif
 #if USE_DRM
     case GST_VAAPI_DISPLAY_TYPE_DRM:
         success = TRUE;
         break;
 #endif
+#if USE_GLX
+    case GST_VAAPI_DISPLAY_TYPE_GLX:
+        if (!sink->use_glx)
+            goto put_surface_x11;
+        success = gst_vaapisink_show_frame_glx(sink, surface, surface_rect,
+            flags);
+        break;
+#endif
 #if USE_X11
     case GST_VAAPI_DISPLAY_TYPE_X11:
-        success = gst_vaapisink_put_surface(sink, surface, flags);
+    put_surface_x11:
+        success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
         break;
 #endif
 #if USE_WAYLAND
     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
-        success = gst_vaapisink_put_surface(sink, surface, flags);
+        success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
         break;
 #endif
     default:
@@ -1068,6 +1164,8 @@ gst_vaapisink_propose_allocation(GstBaseSink *base_sink, GstQuery *query)
     gst_query_add_allocation_meta(query,
         GST_VIDEO_META_API_TYPE, NULL);
     gst_query_add_allocation_meta(query,
+        GST_VIDEO_CROP_META_API_TYPE, NULL);
+    gst_query_add_allocation_meta(query,
         GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
     return TRUE;
 
@@ -1162,12 +1260,18 @@ gst_vaapisink_set_property(
     case PROP_SYNCHRONOUS:
         sink->synchronous = g_value_get_boolean(value);
         break;
+    case PROP_USE_GLX:
+        sink->use_glx = g_value_get_boolean(value);
+        break;
     case PROP_USE_REFLECTION:
         sink->use_reflection = g_value_get_boolean(value);
         break;
     case PROP_ROTATION:
         sink->rotation_req = g_value_get_enum(value);
         break;
+    case PROP_FORCE_ASPECT_RATIO:
+        sink->keep_aspect = g_value_get_boolean(value);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -1194,12 +1298,18 @@ gst_vaapisink_get_property(
     case PROP_SYNCHRONOUS:
         g_value_set_boolean(value, sink->synchronous);
         break;
+    case PROP_USE_GLX:
+        g_value_set_boolean(value, sink->use_glx);
+        break;
     case PROP_USE_REFLECTION:
         g_value_set_boolean(value, sink->use_reflection);
         break;
     case PROP_ROTATION:
         g_value_set_enum(value, sink->rotation);
         break;
+    case PROP_FORCE_ASPECT_RATIO:
+        g_value_set_boolean(value, sink->keep_aspect);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -1256,6 +1366,15 @@ gst_vaapisink_class_init(GstVaapiSinkClass *klass)
 #if USE_GLX
     g_object_class_install_property
         (object_class,
+         PROP_USE_GLX,
+         g_param_spec_boolean("use-glx",
+                              "OpenGL rendering",
+                              "Enables OpenGL rendering",
+                              FALSE,
+                              G_PARAM_READWRITE));
+
+    g_object_class_install_property
+        (object_class,
          PROP_USE_REFLECTION,
          g_param_spec_boolean("use-reflection",
                               "Reflection effect",
@@ -1302,6 +1421,21 @@ gst_vaapisink_class_init(GstVaapiSinkClass *klass)
                            GST_VAAPI_TYPE_ROTATION,
                            DEFAULT_ROTATION,
                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+    /**
+     * GstVaapiSink:force-aspect-ratio:
+     *
+     * When enabled, scaling respects video aspect ratio; when disabled, the
+     * video is distorted to fit the window.
+     */
+    g_object_class_install_property
+        (object_class,
+         PROP_FORCE_ASPECT_RATIO,
+         g_param_spec_boolean("force-aspect-ratio",
+                              "Force aspect ratio",
+                              "When enabled, scaling will respect original aspect ratio",
+                              TRUE,
+                              G_PARAM_READWRITE));
 }
 
 static void
@@ -1327,4 +1461,5 @@ gst_vaapisink_init(GstVaapiSink *sink)
     sink->use_reflection = FALSE;
     sink->use_overlay    = FALSE;
     sink->use_rotation   = FALSE;
+    sink->keep_aspect    = TRUE;
 }