<commit_before>/*
 *  gstvaapisink.c - VA-API video sink
 *
 *  gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

/**
 * SECTION:gstvaapisink
 * @short_description: A VA-API based videosink
 *
 * vaapisink renders video frames to a drawable (X #Window) on a local
 * display using the Video Acceleration (VA) API. The element will
 * create its own internal window and render into it.
 */

#include "config.h"
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/vaapi/gstvaapivideobuffer.h>
#include <gst/vaapi/gstvaapivideosink.h>
#include <gst/vaapi/gstvaapidisplay_x11.h>
#include <gst/vaapi/gstvaapiwindow_x11.h>
#if USE_VAAPISINK_GLX
#include <gst/vaapi/gstvaapidisplay_glx.h>
#include <gst/vaapi/gstvaapiwindow_glx.h>
#endif
#include "gstvaapisink.h"

/* Supported interfaces */
#include <gst/interfaces/xoverlay.h>

#define GST_PLUGIN_NAME "vaapisink"
#define GST_PLUGIN_DESC "A VA-API based videosink"

GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapisink);
#define GST_CAT_DEFAULT gst_debug_vaapisink

/* ElementFactory information */
static const GstElementDetails gst_vaapisink_details =
    GST_ELEMENT_DETAILS(
        "VA-API sink",
        "Sink/Video",
        GST_PLUGIN_DESC,
        "<NAME> <<EMAIL>>");

/* Default template */
static GstStaticPadTemplate gst_vaapisink_sink_factory =
    GST_STATIC_PAD_TEMPLATE(
        "sink",
        GST_PAD_SINK,
        GST_PAD_ALWAYS,
        GST_STATIC_CAPS(GST_VAAPI_SURFACE_CAPS));

static void gst_vaapisink_iface_init(GType type);

GST_BOILERPLATE_FULL(
    GstVaapiSink,
    gst_vaapisink,
    GstVideoSink,
    GST_TYPE_VIDEO_SINK,
    gst_vaapisink_iface_init);

enum {
    PROP_0,

    PROP_USE_GLX,
    PROP_DISPLAY,
    PROP_FULLSCREEN,
    PROP_SYNCHRONOUS,
    PROP_USE_REFLECTION
};

/* GstImplementsInterface interface */

static gboolean
gst_vaapisink_implements_interface_supported(
    GstImplementsInterface *iface,
    GType                   type
)
{
    return (type == GST_VAAPI_TYPE_VIDEO_SINK ||
            type == GST_TYPE_X_OVERLAY);
}

static void
gst_vaapisink_implements_iface_init(GstImplementsInterfaceClass *iface)
{
    iface->supported = gst_vaapisink_implements_interface_supported;
}

/* GstVaapiVideoSink interface */

static GstVaapiDisplay *
gst_vaapisink_get_display(GstVaapiSink *sink);

static GstVaapiDisplay *
gst_vaapi_video_sink_do_get_display(GstVaapiVideoSink *sink)
{
    return gst_vaapisink_get_display(GST_VAAPISINK(sink));
}

static void
gst_vaapi_video_sink_iface_init(GstVaapiVideoSinkInterface *iface)
{
    iface->get_display = gst_vaapi_video_sink_do_get_display;
}

/* GstXOverlay interface */

static gboolean
gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, XID xid);

static GstFlowReturn
gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);

static void
gst_vaapisink_xoverlay_set_xid(GstXOverlay *overlay, XID xid)
{
    GstVaapiSink * const sink = GST_VAAPISINK(overlay);

    gst_vaapisink_ensure_window_xid(sink, xid);
}

static void
gst_vaapisink_xoverlay_expose(GstXOverlay *overlay)
{
    GstBaseSink * const base_sink = GST_BASE_SINK(overlay);
    GstBuffer *buffer;

    buffer = gst_base_sink_get_last_buffer(base_sink);
    if (buffer) {
        gst_vaapisink_show_frame(base_sink, buffer);
        gst_buffer_unref(buffer);
    }
}

static void
gst_vaapisink_xoverlay_iface_init(GstXOverlayClass *iface)
{
    iface->set_xwindow_id = gst_vaapisink_xoverlay_set_xid;
    iface->expose         = gst_vaapisink_xoverlay_expose;
}

static void
gst_vaapisink_iface_init(GType type)
{
    const GType g_define_type_id = type;

    G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
                          gst_vaapisink_implements_iface_init);
    G_IMPLEMENT_INTERFACE(GST_VAAPI_TYPE_VIDEO_SINK,
                          gst_vaapi_video_sink_iface_init);
    G_IMPLEMENT_INTERFACE(GST_TYPE_X_OVERLAY,
                          gst_vaapisink_xoverlay_iface_init);
}

static void
gst_vaapisink_destroy(GstVaapiSink *sink)
{
    if (sink->display) {
        g_object_unref(sink->display);
        sink->display = NULL;
    }

    if (sink->display_name) {
        g_free(sink->display_name);
        sink->display_name = NULL;
    }
}

static inline gboolean
gst_vaapisink_ensure_display(GstVaapiSink *sink)
{
    if (!sink->display) {
#if USE_VAAPISINK_GLX
        if (sink->use_glx)
            sink->display = gst_vaapi_display_glx_new(sink->display_name);
        else
#endif
            sink->display = gst_vaapi_display_x11_new(sink->display_name);
        if (!sink->display || !gst_vaapi_display_get_display(sink->display))
            return FALSE;
        g_object_set(sink, "synchronous", sink->synchronous, NULL);
    }
    return sink->display != NULL;
}

static gboolean
gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height)
{
    GstVaapiRectangle * const display_rect = &sink->display_rect;
    guint num, den, display_par_n, display_par_d;
    double display_ratio;
    gboolean success;

    gst_vaapi_display_get_pixel_aspect_ratio(
        sink->display,
        &display_par_n, &display_par_d
    );
    GST_DEBUG("display pixel-aspect-ratio %d/%d",
              display_par_n, display_par_d);

    success = gst_video_calculate_display_ratio(
        &num, &den,
        sink->video_width, sink->video_height,
        sink->video_par_n, sink->video_par_d,
        display_par_n, display_par_d
    );
    if (!success)
        return FALSE;
    GST_DEBUG("video size %dx%d, calculated render ratio %d/%d",
              sink->video_width, sink->video_height, num, den);

    if ((sink->video_height % den) == 0) {
        GST_DEBUG("keeping video height");
        display_rect->width  =
            gst_util_uint64_scale_int(sink->video_height, num, den);
        display_rect->height = sink->video_height;
    }
    else if ((sink->video_width % num) == 0) {
        GST_DEBUG("keeping video width");
        display_rect->width  = sink->video_width;
        display_rect->height =
            gst_util_uint64_scale_int(sink->video_width, den, num);
    }
    else {
        GST_DEBUG("approximating while keeping video height");
        display_rect->width  =
            gst_util_uint64_scale_int(sink->video_height, num, den);
        display_rect->height = sink->video_height;
    }
    display_ratio = (gdouble)display_rect->width / display_rect->height;
    GST_DEBUG("scaling to %ux%u", display_rect->width, display_rect->height);

    if (sink->fullscreen ||
        display_rect->width > width || display_rect->height > height) {
        if (sink->video_width > sink->video_height) {
            display_rect->width  = width;
            display_rect->height = width / display_ratio;
        }
        else {
            display_rect->width  = height * display_ratio;
            display_rect->height = height;
        }
    }
    GST_DEBUG("display size %ux%u", display_rect->width, display_rect->height);

    if (sink->fullscreen) {
        display_rect->x  = (width  - display_rect->width)  / 2;
        display_rect->y  = (height - display_rect->height) / 2;
    }
    else {
        display_rect->x  = 0;
        display_rect->y  = 0;
    }
    return TRUE;
}

static inline gboolean
gst_vaapisink_ensure_window(GstVaapiSink *sink, guint width, guint height)
{
    GstVaapiDisplay * const display = sink->display;

    if (!sink->window) {
#if USE_VAAPISINK_GLX
        if (sink->use_glx)
            sink->window = gst_vaapi_window_glx_new(display, width, height);
        else<commit_msg>Improve debug info for gst_vaapisink_ensure_render_rect().<commit_after>/*
 *  gstvaapisink.c - VA-API video sink
 *
 *  gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

/**
 * SECTION:gstvaapisink
 * @short_description: A VA-API based videosink
 *
 * vaapisink renders video frames to a drawable (X #Window) on a local
 * display using the Video Acceleration (VA) API. The element will
 * create its own internal window and render into it.
 */

#include "config.h"
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/vaapi/gstvaapivideobuffer.h>
#include <gst/vaapi/gstvaapivideosink.h>
#include <gst/vaapi/gstvaapidisplay_x11.h>
#include <gst/vaapi/gstvaapiwindow_x11.h>
#if USE_VAAPISINK_GLX
#include <gst/vaapi/gstvaapidisplay_glx.h>
#include <gst/vaapi/gstvaapiwindow_glx.h>
#endif
#include "gstvaapisink.h"

/* Supported interfaces */
#include <gst/interfaces/xoverlay.h>

#define GST_PLUGIN_NAME "vaapisink"
#define GST_PLUGIN_DESC "A VA-API based videosink"

GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapisink);
#define GST_CAT_DEFAULT gst_debug_vaapisink

/* ElementFactory information */
static const GstElementDetails gst_vaapisink_details =
    GST_ELEMENT_DETAILS(
        "VA-API sink",
        "Sink/Video",
        GST_PLUGIN_DESC,
        "<NAME> <<EMAIL>>");

/* Default template */
static GstStaticPadTemplate gst_vaapisink_sink_factory =
    GST_STATIC_PAD_TEMPLATE(
        "sink",
        GST_PAD_SINK,
        GST_PAD_ALWAYS,
        GST_STATIC_CAPS(GST_VAAPI_SURFACE_CAPS));

static void gst_vaapisink_iface_init(GType type);

GST_BOILERPLATE_FULL(
    GstVaapiSink,
    gst_vaapisink,
    GstVideoSink,
    GST_TYPE_VIDEO_SINK,
    gst_vaapisink_iface_init);

enum {
    PROP_0,

    PROP_USE_GLX,
    PROP_DISPLAY,
    PROP_FULLSCREEN,
    PROP_SYNCHRONOUS,
    PROP_USE_REFLECTION
};

/* GstImplementsInterface interface */

static gboolean
gst_vaapisink_implements_interface_supported(
    GstImplementsInterface *iface,
    GType                   type
)
{
    return (type == GST_VAAPI_TYPE_VIDEO_SINK ||
            type == GST_TYPE_X_OVERLAY);
}

static void
gst_vaapisink_implements_iface_init(GstImplementsInterfaceClass *iface)
{
    iface->supported = gst_vaapisink_implements_interface_supported;
}

/* GstVaapiVideoSink interface */

static GstVaapiDisplay *
gst_vaapisink_get_display(GstVaapiSink *sink);

static GstVaapiDisplay *
gst_vaapi_video_sink_do_get_display(GstVaapiVideoSink *sink)
{
    return gst_vaapisink_get_display(GST_VAAPISINK(sink));
}

static void
gst_vaapi_video_sink_iface_init(GstVaapiVideoSinkInterface *iface)
{
    iface->get_display = gst_vaapi_video_sink_do_get_display;
}

/* GstXOverlay interface */

static gboolean
gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, XID xid);

static GstFlowReturn
gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);

static void
gst_vaapisink_xoverlay_set_xid(GstXOverlay *overlay, XID xid)
{
    GstVaapiSink * const sink = GST_VAAPISINK(overlay);

    gst_vaapisink_ensure_window_xid(sink, xid);
}

static void
gst_vaapisink_xoverlay_expose(GstXOverlay *overlay)
{
    GstBaseSink * const base_sink = GST_BASE_SINK(overlay);
    GstBuffer *buffer;

    buffer = gst_base_sink_get_last_buffer(base_sink);
    if (buffer) {
        gst_vaapisink_show_frame(base_sink, buffer);
        gst_buffer_unref(buffer);
    }
}

static void
gst_vaapisink_xoverlay_iface_init(GstXOverlayClass *iface)
{
    iface->set_xwindow_id = gst_vaapisink_xoverlay_set_xid;
    iface->expose         = gst_vaapisink_xoverlay_expose;
}

static void
gst_vaapisink_iface_init(GType type)
{
    const GType g_define_type_id = type;

    G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
                          gst_vaapisink_implements_iface_init);
    G_IMPLEMENT_INTERFACE(GST_VAAPI_TYPE_VIDEO_SINK,
                          gst_vaapi_video_sink_iface_init);
    G_IMPLEMENT_INTERFACE(GST_TYPE_X_OVERLAY,
                          gst_vaapisink_xoverlay_iface_init);
}

static void
gst_vaapisink_destroy(GstVaapiSink *sink)
{
    if (sink->display) {
        g_object_unref(sink->display);
        sink->display = NULL;
    }

    if (sink->display_name) {
        g_free(sink->display_name);
        sink->display_name = NULL;
    }
}

static inline gboolean
gst_vaapisink_ensure_display(GstVaapiSink *sink)
{
    if (!sink->display) {
#if USE_VAAPISINK_GLX
        if (sink->use_glx)
            sink->display = gst_vaapi_display_glx_new(sink->display_name);
        else
#endif
            sink->display = gst_vaapi_display_x11_new(sink->display_name);
        if (!sink->display || !gst_vaapi_display_get_display(sink->display))
            return FALSE;
        g_object_set(sink, "synchronous", sink->synchronous, NULL);
    }
    return sink->display != NULL;
}

static gboolean
gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height)
{
    GstVaapiRectangle * const display_rect = &sink->display_rect;
    guint num, den, display_par_n, display_par_d;
    double display_ratio;
    gboolean success;

    GST_DEBUG("ensure render rect within %ux%u bounds", width, height);

    gst_vaapi_display_get_pixel_aspect_ratio(
        sink->display,
        &display_par_n, &display_par_d
    );
    GST_DEBUG("display pixel-aspect-ratio %d/%d",
              display_par_n, display_par_d);

    success = gst_video_calculate_display_ratio(
        &num, &den,
        sink->video_width, sink->video_height,
        sink->video_par_n, sink->video_par_d,
        display_par_n, display_par_d
    );
    if (!success)
        return FALSE;
    GST_DEBUG("video size %dx%d, calculated ratio %d/%d",
              sink->video_width, sink->video_height, num, den);

    if ((sink->video_height % den) == 0) {
        GST_DEBUG("keeping video height");
        display_rect->width  =
            gst_util_uint64_scale_int(sink->video_height, num, den);
        display_rect->height = sink->video_height;
    }
    else if ((sink->video_width % num) == 0) {
        GST_DEBUG("keeping video width");
        display_rect->width  = sink->video_width;
        display_rect->height =
            gst_util_uint64_scale_int(sink->video_width, den, num);
    }
    else {
        GST_DEBUG("approximating while keeping video height");
        display_rect->width  =
            gst_util_uint64_scale_int(sink->video_height, num, den);
        display_rect->height = sink->video_height;
    }
    display_ratio = (gdouble)display_rect->width / display_rect->height;
    GST_DEBUG("scaling to %ux%u", display_rect->width, display_rect->height);

    if (sink->fullscreen ||
        display_rect->width > width || display_rect->height > height) {
        if (sink->video_width > sink->video_height) {
            display_rect->width  = width;
            display_rect->height = width / display_ratio;
        }
        else {
            display_rect->width  = height * display_ratio;
            display_rect->height = height;
        }
    }

    if (sink->fullscreen) {
        display_rect->x  = (width  - display_rect->width)  / 2;
        display_rect->y  = (height - display_rect->height) / 2;
    }
    else {
        display_rect->x  = 0;
        display_rect->y  = 0;
    }

    GST_DEBUG("render rect (%d,%d):%ux%u",
              display_rect->x, display_rect->y,
              display_rect->width, display_rect->height);
    return TRUE;
}

static inline gboolean
gst_vaapisink_ensure_window(GstVaapiSink *sink, guint width, guint height)
{
    GstVaapiDisplay * const display = sink->display;

    if (!sink->window) {
#if USE_VAAPISINK_GLX
        if (sink->use_glx)
            sink->window = gst_vaapi_window_glx_new(display, width, height);
        else
{'fraction_non_alphanumeric': 0.0937593669444278, 'fraction_numerical': 0.00419639110365086, 'mean_word_length': 3.4663989290495314, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 171, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5258783', 'n_tokens_mistral': 6045, 'n_tokens_neox': 5966, 'n_words': 1120}