/* * GStreamer * Copyright (C) 2009 Carl-Anton Ingmarsson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstvdpoutputbuffer.h" GST_DEBUG_CATEGORY_STATIC (gst_vdp_output_buffer_debug); #define GST_CAT_DEFAULT gst_vdp_output_buffer_debug #define DEBUG_INIT(bla) \ GST_DEBUG_CATEGORY_INIT (gst_vdp_output_buffer_debug, "vdpoutputbuffer", 0, "VDPAU output buffer"); GstVdpOutputBuffer * gst_vdp_output_buffer_new (GstVdpDevice * device, VdpRGBAFormat rgba_format, gint width, gint height, GError ** error) { GstVdpOutputBuffer *buffer; VdpStatus status; VdpOutputSurface surface; status = device->vdp_output_surface_create (device->device, rgba_format, width, height, &surface); if (status != VDP_STATUS_OK) goto create_error; buffer = (GstVdpOutputBuffer *) gst_mini_object_new (GST_TYPE_VDP_OUTPUT_BUFFER); buffer->device = g_object_ref (device); buffer->rgba_format = rgba_format; buffer->width = width; buffer->height = height; buffer->surface = surface; return buffer; create_error: g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ, "Couldn't create a VdpOutputSurface, error returned from vdpau was: %s", device->vdp_get_error_string (status)); return NULL; } static GObjectClass *gst_vdp_output_buffer_parent_class; static void gst_vdp_output_buffer_finalize (GstVdpOutputBuffer * buffer) { GstVdpDevice *device; VdpStatus status; if (gst_vdp_buffer_revive (GST_VDP_BUFFER_CAST (buffer))) return; device = buffer->device; status = device->vdp_output_surface_destroy (buffer->surface); if (status != VDP_STATUS_OK) GST_ERROR ("Couldn't destroy the buffers VdpOutputSurface, error returned was: %s", device->vdp_get_error_string (status)); g_object_unref (buffer->device); GST_MINI_OBJECT_CLASS (gst_vdp_output_buffer_parent_class)->finalize (GST_MINI_OBJECT (buffer)); } static void gst_vdp_output_buffer_init (GstVdpOutputBuffer * buffer, gpointer g_class) { buffer->device = NULL; buffer->surface = VDP_INVALID_HANDLE; } static void gst_vdp_output_buffer_class_init (gpointer g_class, gpointer class_data) { GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); gst_vdp_output_buffer_parent_class = g_type_class_peek_parent (g_class); mini_object_class->finalize = (GstMiniObjectFinalizeFunction) gst_vdp_output_buffer_finalize; } GType gst_vdp_output_buffer_get_type (void) { static GType _gst_vdp_output_buffer_type; if (G_UNLIKELY (_gst_vdp_output_buffer_type == 0)) { static const GTypeInfo info = { sizeof (GstBufferClass), NULL, NULL, gst_vdp_output_buffer_class_init, NULL, NULL, sizeof (GstVdpOutputBuffer), 0, (GInstanceInitFunc) gst_vdp_output_buffer_init, NULL }; _gst_vdp_output_buffer_type = g_type_register_static (GST_TYPE_VDP_BUFFER, "GstVdpOutputBuffer", &info, 0); DEBUG_INIT (); } return _gst_vdp_output_buffer_type; } typedef struct { VdpRGBAFormat format; GstStaticCaps caps; } GstVdpOutputBufferFormats; GstVdpOutputBufferFormats rgba_formats[] = { {VDP_RGBA_FORMAT_A8, GST_STATIC_CAPS ("video/x-raw-rgb, " "bpp = (int)8, " "depth = (int)8, " "endianness = (int)4321, " "red_mask = (int)0x00, " "green_mask = (int)0x00, " "blue_mask = (int)0x00, " "alpha_mask = (int)0xff")}, {VDP_RGBA_FORMAT_B10G10R10A2, GST_STATIC_CAPS ("video/x-raw-rgb, " "bpp = (int)32, " "depth = (int)30, " "endianness = (int)4321, " "red_mask = (int)0x000003fc, " "green_mask = (int)0x003ff000, " "blue_mask = (int)0xffc00000, " "alpha_mask = (int)0x00000003")}, {VDP_RGBA_FORMAT_B8G8R8A8, GST_STATIC_CAPS ("video/x-raw-rgb, " "bpp = (int)32, " "depth = (int)24, " "endianness = (int)4321, " "red_mask = (int)0x0000ff00, " "green_mask = (int)0x00ff0000, " "blue_mask = (int)0xff000000, " "alpha_mask = (int)0x000000ff")}, {VDP_RGBA_FORMAT_R10G10B10A2, GST_STATIC_CAPS ("video/x-raw-rgb, " "bpp = (int)32, " "depth = (int)30, " "endianness = (int)4321, " "red_mask = (int)0xffc00000, " "green_mask = (int)0x003ff000, " "blue_mask = (int)0x000003fc, " "alpha_mask = (int)0x00000003")}, {VDP_RGBA_FORMAT_R8G8B8A8, GST_STATIC_CAPS ("video/x-raw-rgb, " "bpp = (int)32, " "depth = (int)24, " "endianness = (int)4321, " "red_mask = (int)0xff000000, " "green_mask = (int)0x00ff0000, " "blue_mask = (int)0x0000ff00, " "alpha_mask = (int)0x000000ff")}, }; GstCaps * gst_vdp_output_buffer_get_template_caps (void) { GstCaps *caps, *rgb_caps; gint i; caps = gst_caps_new_empty (); rgb_caps = gst_caps_new_empty (); for (i = 0; i < G_N_ELEMENTS (rgba_formats); i++) { GstCaps *format_caps; format_caps = gst_caps_new_simple ("video/x-vdpau-output", "rgba-format", G_TYPE_INT, rgba_formats[i].format, "width", GST_TYPE_INT_RANGE, 1, 8192, "height", GST_TYPE_INT_RANGE, 1, 8192, NULL); gst_caps_append (caps, format_caps); format_caps = gst_static_caps_get (&rgba_formats[i].caps); format_caps = gst_caps_copy (format_caps); gst_caps_set_simple (format_caps, "width", GST_TYPE_INT_RANGE, 1, 8192, "height", GST_TYPE_INT_RANGE, 1, 8192, NULL); gst_caps_append (rgb_caps, format_caps); } gst_caps_append (caps, rgb_caps); return caps; } GstCaps * gst_vdp_output_buffer_get_allowed_caps (GstVdpDevice * device) { GstCaps *caps, *rgb_caps; gint i; g_return_val_if_fail (GST_IS_VDP_DEVICE (device), NULL); caps = gst_caps_new_empty (); rgb_caps = gst_caps_new_empty (); for (i = 0; i < G_N_ELEMENTS (rgba_formats); i++) { VdpStatus status; VdpBool is_supported; guint max_w, max_h; status = device->vdp_output_surface_query_capabilities (device->device, rgba_formats[i].format, &is_supported, &max_w, &max_h); if (status != VDP_STATUS_OK && status != VDP_STATUS_INVALID_RGBA_FORMAT) { GST_ERROR_OBJECT (device, "Could not get query VDPAU output surface capabilites, " "Error returned from vdpau was: %s", device->vdp_get_error_string (status)); goto error; } if (is_supported) { GstCaps *format_caps; format_caps = gst_caps_new_simple ("video/x-vdpau-output", "rgba-format", G_TYPE_INT, rgba_formats[i].format, "width", GST_TYPE_INT_RANGE, 1, max_w, "height", GST_TYPE_INT_RANGE, 1, max_h, NULL); gst_caps_append (caps, format_caps); format_caps = gst_static_caps_get (&rgba_formats[i].caps); format_caps = gst_caps_copy (format_caps); gst_caps_set_simple (format_caps, "width", GST_TYPE_INT_RANGE, 1, 8192, "height", GST_TYPE_INT_RANGE, 1, 8192, NULL); gst_caps_append (rgb_caps, format_caps); } } gst_caps_append (caps, rgb_caps); error: return caps; } gboolean gst_vdp_caps_to_rgba_format (GstCaps * caps, VdpRGBAFormat * rgba_format) { GstStructure *structure; gint c_bpp, c_depth, c_endianness, c_red_mask, c_green_mask, c_blue_mask, c_alpha_mask; gint i; g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); if (!gst_caps_is_fixed (caps)) return FALSE; structure = gst_caps_get_structure (caps, 0); if (!gst_structure_has_name (structure, "video/x-raw-rgb")) return FALSE; if (!gst_structure_get_int (structure, "bpp", &c_bpp) || !gst_structure_get_int (structure, "depth", &c_depth) || !gst_structure_get_int (structure, "endianness", &c_endianness) || !gst_structure_get_int (structure, "red_mask", &c_red_mask) || !gst_structure_get_int (structure, "green_mask", &c_green_mask) || !gst_structure_get_int (structure, "blue_mask", &c_blue_mask) || !gst_structure_get_int (structure, "alpha_mask", &c_alpha_mask)) return FALSE; for (i = 0; i < G_N_ELEMENTS (rgba_formats); i++) { gint bpp, depth, endianness, red_mask, green_mask, blue_mask, alpha_mask; GstCaps *rgb_caps = gst_static_caps_get (&rgba_formats[i].caps); structure = gst_caps_get_structure (rgb_caps, 0); gst_structure_get_int (structure, "bpp", &bpp); gst_structure_get_int (structure, "depth", &depth); gst_structure_get_int (structure, "endianness", &endianness); gst_structure_get_int (structure, "red_mask", &red_mask); gst_structure_get_int (structure, "green_mask", &green_mask); gst_structure_get_int (structure, "blue_mask", &blue_mask); gst_structure_get_int (structure, "alpha_mask", &alpha_mask); if (c_bpp == bpp && c_depth == depth && c_endianness == endianness && c_red_mask == red_mask && c_green_mask == green_mask && c_blue_mask == blue_mask && c_alpha_mask == alpha_mask) { gst_caps_unref (rgb_caps); *rgba_format = rgba_formats[i].format; return TRUE; } gst_caps_unref (rgb_caps); } return FALSE; } gboolean gst_vdp_output_buffer_calculate_size (GstVdpOutputBuffer * output_buf, guint * size) { g_return_val_if_fail (GST_IS_VDP_OUTPUT_BUFFER (output_buf), FALSE); switch (output_buf->rgba_format) { case VDP_RGBA_FORMAT_A8: { *size = output_buf->width * output_buf->height; break; } case VDP_RGBA_FORMAT_B10G10R10A2: case VDP_RGBA_FORMAT_B8G8R8A8: case VDP_RGBA_FORMAT_R10G10B10A2: case VDP_RGBA_FORMAT_R8G8B8A8: { *size = output_buf->width * output_buf->height * 4; break; } default: g_assert_not_reached (); return FALSE; } return TRUE; } gboolean gst_vdp_output_buffer_download (GstVdpOutputBuffer * output_buf, GstBuffer * outbuf, GError ** error) { guint8 *data[1]; guint32 stride[1]; GstVdpDevice *device; VdpOutputSurface surface; VdpStatus status; g_return_val_if_fail (GST_IS_VDP_OUTPUT_BUFFER (output_buf), FALSE); switch (output_buf->rgba_format) { case VDP_RGBA_FORMAT_A8: { stride[0] = output_buf->width; break; } case VDP_RGBA_FORMAT_B10G10R10A2: case VDP_RGBA_FORMAT_B8G8R8A8: case VDP_RGBA_FORMAT_R10G10B10A2: case VDP_RGBA_FORMAT_R8G8B8A8: { stride[0] = output_buf->width * 4; break; } default: return FALSE; } device = output_buf->device; surface = output_buf->surface; data[0] = GST_BUFFER_DATA (outbuf); GST_LOG_OBJECT (output_buf, "Entering vdp_output_surface_get_bits_native"); status = device->vdp_output_surface_get_bits_native (surface, NULL, (void *) data, stride); GST_LOG_OBJECT (output_buf, "Got status %d from vdp_output_get_bits_native", status); if (G_UNLIKELY (status != VDP_STATUS_OK)) { g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ, "Couldn't get data from vdpau, error returned from vdpau was: %s", device->vdp_get_error_string (status)); return FALSE; } return TRUE; }