gstreamer/sys/vdpau/gstvdpvideopostprocess.c
Carl-Anton Ingmarsson bd20e5d077 vdpau: fixup GstFlowReturn handling
Previously the different decoders would discard errounous GstFlowReturns coming
from downstream. Now we properly return these further upstream so that we
properly error out on eg. negotiation problems.
2011-03-27 19:51:31 +02:00

1300 lines
35 KiB
C

/*
* GStreamer
* Copyright (C) 2009 Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>
*
* 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.
*/
/**
* SECTION:element-vdpauvideopostprocess
*
* FIXME:Describe vdpaumpegdec here.
*
* <refsect2>
* <title>Example launch line</title>
* |[
* gst-launch -v -m fakesrc ! vdpauvideopostprocess ! fakesink silent=TRUE
* ]|
* </refsect2>
*/
/*
* TODO:
* + add support for more postprocessing options
* + mixing videos. (This should perhaps be done in a separate element based on
* VdpOutputSurface)
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gst/gst.h>
#include <gst/video/gstvideosink.h>
#include "gstvdp/gstvdputils.h"
#include "gstvdp/gstvdpoutputbuffer.h"
#include "gstvdp/gstvdpoutputsrcpad.h"
#include "gstvdpvideopostprocess.h"
GST_DEBUG_CATEGORY_STATIC (gst_vdp_vpp_debug);
#define GST_CAT_DEFAULT gst_vdp_vpp_debug
/* Filter signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_DISPLAY,
PROP_FORCE_ASPECT_RATIO,
PROP_DEINTERLACE_MODE,
PROP_DEINTERLACE_METHOD,
PROP_NOISE_REDUCTION,
PROP_SHARPENING,
PROP_INVERSE_TELECINE
};
#define DEBUG_INIT(bla) \
GST_DEBUG_CATEGORY_INIT (gst_vdp_vpp_debug, "vdpauvideopostprocess", 0, "VDPAU video surface to output surface");
GST_BOILERPLATE_FULL (GstVdpVideoPostProcess, gst_vdp_vpp,
GstElement, GST_TYPE_ELEMENT, DEBUG_INIT);
static void gst_vdp_vpp_finalize (GObject * object);
#define GST_TYPE_VDP_DEINTERLACE_METHODS (gst_vdp_deinterlace_methods_get_type ())
static GType
gst_vdp_deinterlace_methods_get_type (void)
{
static GType deinterlace_methods_type = 0;
static const GEnumValue methods_types[] = {
{GST_VDP_DEINTERLACE_METHOD_BOB,
"Vertically scale a single field to the size of a single frame.",
"bob"},
{GST_VDP_DEINTERLACE_METHOD_TEMPORAL, "Motion Adaptive: Simple Detection",
"temporal"},
{GST_VDP_DEINTERLACE_METHOD_TEMPORAL_SPATIAL,
"Motion Adaptive: Advanced Detection", "temporal-spatial"},
{0, NULL, NULL},
};
if (!deinterlace_methods_type) {
deinterlace_methods_type =
g_enum_register_static ("GstVdpDeinterlaceMethods", methods_types);
}
return deinterlace_methods_type;
}
#define GST_TYPE_VDP_DEINTERLACE_MODES (gst_vdp_deinterlace_modes_get_type ())
static GType
gst_vdp_deinterlace_modes_get_type (void)
{
static GType deinterlace_modes_type = 0;
static const GEnumValue modes_types[] = {
{GST_VDP_DEINTERLACE_MODE_AUTO, "Auto detection", "auto"},
{GST_VDP_DEINTERLACE_MODE_INTERLACED, "Enfore deinterlacing", "interlaced"},
{GST_VDP_DEINTERLACE_MODE_DISABLED, "Run in passthrough mode", "disabled"},
{0, NULL, NULL},
};
if (!deinterlace_modes_type) {
deinterlace_modes_type =
g_enum_register_static ("GstVdpDeinterlaceModes", modes_types);
}
return deinterlace_modes_type;
}
static void
gst_vdp_vpp_set_attribute_float (GstVdpVideoPostProcess * vpp,
VdpVideoMixerAttribute attribute, gfloat value)
{
VdpVideoMixerAttribute attributes[1];
const void *attribute_values[1];
VdpStatus status;
attributes[0] = attribute;
attribute_values[0] = &value;
status =
vpp->device->vdp_video_mixer_set_attribute_values (vpp->mixer, 1,
attributes, attribute_values);
if (status != VDP_STATUS_OK) {
GST_WARNING_OBJECT (vpp,
"Couldn't set noise reduction level on mixer, "
"error returned from vdpau was: %s",
vpp->device->vdp_get_error_string (status));
}
}
static void
gst_vdp_vpp_activate_feature (GstVdpVideoPostProcess * vpp,
VdpVideoMixerFeature feature, gboolean activate)
{
VdpVideoMixerFeature features[1];
VdpBool enable[1];
VdpStatus status;
features[0] = feature;
if (activate)
enable[0] = VDP_TRUE;
else
enable[0] = VDP_FALSE;
status =
vpp->device->vdp_video_mixer_set_feature_enables (vpp->mixer, 1,
features, enable);
if (status != VDP_STATUS_OK) {
GST_WARNING_OBJECT (vpp, "Couldn't set deinterlace method on mixer, "
"error returned from vdpau was: %s",
vpp->device->vdp_get_error_string (status));
}
}
static VdpVideoMixerFeature
gst_vdp_feature_from_deinterlace_method (GstVdpDeinterlaceMethods method)
{
gint i;
VdpVideoMixerFeature feature = 0;
typedef struct
{
GstVdpDeinterlaceMethods method;
VdpVideoMixerFeature feature;
} VdpDeinterlaceMethod;
static const VdpDeinterlaceMethod deinterlace_methods[] = {
{GST_VDP_DEINTERLACE_METHOD_TEMPORAL,
VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL},
{GST_VDP_DEINTERLACE_METHOD_TEMPORAL_SPATIAL,
VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL}
};
for (i = 0; i < G_N_ELEMENTS (deinterlace_methods); i++) {
if (deinterlace_methods[i].method == method) {
feature = deinterlace_methods[i].feature;
break;
}
}
return feature;
}
static void
gst_vdp_vpp_activate_deinterlace_method (GstVdpVideoPostProcess * vpp,
GstVdpDeinterlaceMethods method, gboolean activate)
{
gst_vdp_vpp_activate_feature (vpp,
gst_vdp_feature_from_deinterlace_method (method), activate);
}
static void
gst_vdp_picture_clear (GstVdpPicture pic)
{
if (pic.buf) {
gst_buffer_unref (GST_BUFFER (pic.buf));
pic.buf = NULL;
}
}
static gboolean
gst_vdp_vpp_is_interlaced (GstVdpVideoPostProcess * vpp)
{
if (vpp->mode == GST_VDP_DEINTERLACE_MODE_INTERLACED)
return TRUE;
if (vpp->mode == GST_VDP_DEINTERLACE_MODE_AUTO && vpp->interlaced)
return TRUE;
return FALSE;
}
static guint
gst_vdp_vpp_get_required_pictures (GstVdpVideoPostProcess * vpp)
{
guint ret;
if (vpp->noise_reduction != 0.0)
return 2;
if (!gst_vdp_vpp_is_interlaced (vpp))
return 1;
switch (vpp->method) {
case GST_VDP_DEINTERLACE_METHOD_BOB:
ret = 1;
break;
case GST_VDP_DEINTERLACE_METHOD_TEMPORAL:
case GST_VDP_DEINTERLACE_METHOD_TEMPORAL_SPATIAL:
ret = 2;
break;
default:
g_assert_not_reached ();
break;
}
return ret;
}
static gboolean
gst_vdp_vpp_get_next_picture (GstVdpVideoPostProcess * vpp,
GstVdpPicture * current_pic,
guint32 * video_surfaces_past_count, VdpVideoSurface * video_surfaces_past,
guint32 * video_surfaces_future_count,
VdpVideoSurface * video_surfaces_future)
{
gint i;
gint required_pictures;
required_pictures = gst_vdp_vpp_get_required_pictures (vpp);
if (vpp->n_future_pictures < required_pictures)
return FALSE;
*current_pic = vpp->future_pictures[0];
for (i = 0; i < vpp->n_future_pictures - 1; i++) {
vpp->future_pictures[i] = vpp->future_pictures[i + 1];
}
vpp->future_pictures[vpp->n_future_pictures - 1].buf = NULL;
vpp->n_future_pictures--;
*video_surfaces_future_count = vpp->n_future_pictures;
for (i = 0; i < vpp->n_future_pictures; i++)
video_surfaces_future[i] = vpp->future_pictures[i].buf->surface;
*video_surfaces_past_count = vpp->n_past_pictures;
for (i = 0; i < vpp->n_past_pictures; i++)
video_surfaces_past[i] = vpp->past_pictures[i].buf->surface;
if (vpp->n_past_pictures == MAX_PICTURES) {
gst_vdp_picture_clear (vpp->past_pictures[MAX_PICTURES - 1]);
vpp->n_past_pictures--;
}
/* move every picture upwards one step in the array */
for (i = vpp->n_past_pictures; i > 0; i--) {
vpp->past_pictures[i] = vpp->past_pictures[i - 1];
}
/* add the picture to the past surfaces */
vpp->past_pictures[0] = *current_pic;
vpp->n_past_pictures++;
return TRUE;
}
static void
gst_vdp_vpp_add_buffer (GstVdpVideoPostProcess * vpp, GstVdpVideoBuffer * buf)
{
gboolean repeated;
gboolean tff;
gboolean onefield;
GstVdpPicture pic1, pic2;
if (!gst_vdp_vpp_is_interlaced (vpp)) {
pic1.buf = buf;
pic1.structure = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
pic1.timestamp = GST_BUFFER_TIMESTAMP (buf);
vpp->future_pictures[vpp->n_future_pictures++] = pic1;
return;
}
repeated = GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_RFF);
tff = GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_TFF);
onefield = GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_ONEFIELD);
pic1.buf = buf;
pic2.buf = (GstVdpVideoBuffer *) gst_buffer_ref (GST_BUFFER (buf));
if (tff) {
pic1.structure = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
pic2.structure = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
} else {
pic1.structure = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD;
pic2.structure = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
}
pic1.timestamp = GST_BUFFER_TIMESTAMP (buf);
pic2.timestamp = pic1.timestamp + vpp->field_duration;
if (repeated) {
vpp->future_pictures[vpp->n_future_pictures++] = pic1;
pic1.timestamp = pic2.timestamp;
vpp->future_pictures[vpp->n_future_pictures++] = pic1;
gst_buffer_ref (GST_BUFFER (pic1.buf));
pic2.timestamp += vpp->field_duration;
vpp->future_pictures[vpp->n_future_pictures++] = pic2;
} else if (!onefield) {
vpp->future_pictures[vpp->n_future_pictures++] = pic1;
vpp->future_pictures[vpp->n_future_pictures++] = pic2;
} else {
vpp->future_pictures[vpp->n_future_pictures++] = pic1;
gst_buffer_unref (GST_BUFFER (pic2.buf));
}
}
static void
gst_vdp_vpp_post_error (GstVdpVideoPostProcess * vpp, GError * error)
{
GstMessage *message;
message = gst_message_new_error (GST_OBJECT (vpp), error, NULL);
gst_element_post_message (GST_ELEMENT (vpp), message);
g_error_free (error);
}
static GstFlowReturn
gst_vdp_vpp_create_mixer (GstVdpVideoPostProcess * vpp)
{
#define VDP_NUM_MIXER_PARAMETER 3
#define MAX_NUM_FEATURES 5
VdpStatus status;
GstVdpDevice *device;
VdpVideoMixerFeature features[5];
guint n_features = 0;
VdpVideoMixerParameter parameters[VDP_NUM_MIXER_PARAMETER] = {
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE
};
const void *parameter_values[VDP_NUM_MIXER_PARAMETER];
parameter_values[0] = &vpp->width;
parameter_values[1] = &vpp->height;
parameter_values[2] = &vpp->chroma_type;
if (gst_vdp_vpp_is_interlaced (vpp)
&& vpp->method != GST_VDP_DEINTERLACE_METHOD_BOB) {
features[n_features++] =
gst_vdp_feature_from_deinterlace_method (vpp->method);
}
if (vpp->noise_reduction > 0.0)
features[n_features++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
if (vpp->sharpening != 0.0)
features[n_features++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
if (vpp->inverse_telecine)
features[n_features++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
device = vpp->device;
status =
device->vdp_video_mixer_create (device->device, n_features, features,
VDP_NUM_MIXER_PARAMETER, parameters, parameter_values, &vpp->mixer);
if (status != VDP_STATUS_OK) {
GST_ELEMENT_ERROR (vpp, RESOURCE, READ,
("Could not create vdpau video mixer"),
("Error returned from vdpau was: %s",
device->vdp_get_error_string (status)));
return GST_FLOW_ERROR;
}
if (vpp->noise_reduction > 0.0) {
gst_vdp_vpp_set_attribute_float (vpp,
VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL, vpp->noise_reduction);
}
if (vpp->sharpening != 0.0) {
gst_vdp_vpp_set_attribute_float (vpp,
VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL, vpp->sharpening);
}
return GST_FLOW_OK;
}
static gint
gst_greatest_common_divisor (gint a, gint b)
{
while (b != 0) {
int temp = a;
a = b;
b = temp % b;
}
return ABS (a);
}
static gboolean
gst_fraction_double (gint * n_out, gint * d_out)
{
gint n, d, gcd;
n = *n_out;
d = *d_out;
if (d == 0)
return FALSE;
if (n == 0 || (n == G_MAXINT && d == 1))
return TRUE;
gcd = gst_greatest_common_divisor (n, d);
n /= gcd;
d /= gcd;
if (G_MAXINT / 2 >= ABS (n)) {
n *= 2;
} else if (d >= 2) {
d /= 2;
} else
return FALSE;
*n_out = n;
*d_out = d;
return TRUE;
}
static gboolean
gst_vdp_vpp_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstVdpVideoPostProcess *vpp =
GST_VDP_VIDEO_POST_PROCESS (gst_pad_get_parent (pad));
GstStructure *structure;
GstCaps *video_caps = NULL;
gboolean res = FALSE;
GstCaps *allowed_caps, *output_caps, *src_caps;
/* check if the input is non native */
structure = gst_caps_get_structure (caps, 0);
if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
if (!gst_structure_get_fourcc (structure, "format", &vpp->fourcc))
goto done;
vpp->native_input = FALSE;
video_caps = gst_vdp_yuv_to_video_caps (caps);
if (!video_caps)
goto done;
if (!vpp->vpool)
vpp->vpool = gst_vdp_video_buffer_pool_new (vpp->device);
gst_vdp_buffer_pool_set_caps (vpp->vpool, video_caps);
} else {
vpp->native_input = TRUE;
video_caps = gst_caps_ref (caps);
if (vpp->vpool) {
g_object_unref (vpp->vpool);
vpp->vpool = NULL;
}
}
structure = gst_caps_get_structure (video_caps, 0);
if (!gst_structure_get_int (structure, "width", &vpp->width) ||
!gst_structure_get_int (structure, "height", &vpp->height) ||
!gst_structure_get_int (structure, "chroma-type",
(gint *) & vpp->chroma_type))
goto done;
/* get interlaced flag */
gst_structure_get_boolean (structure, "interlaced", &vpp->interlaced);
/* extract par */
if (gst_structure_has_field_typed (structure, "pixel-aspect-ratio",
GST_TYPE_FRACTION)) {
gst_structure_get_fraction (structure, "pixel-aspect-ratio", &vpp->par_n,
&vpp->par_d);
vpp->got_par = TRUE;
} else
vpp->got_par = FALSE;
allowed_caps = gst_pad_get_allowed_caps (vpp->srcpad);
if (G_UNLIKELY (!allowed_caps))
goto null_allowed_caps;
if (G_UNLIKELY (gst_caps_is_empty (allowed_caps)))
goto empty_allowed_caps;
GST_DEBUG ("allowed_caps: %" GST_PTR_FORMAT, allowed_caps);
output_caps = gst_vdp_video_to_output_caps (video_caps);
src_caps = gst_caps_intersect (output_caps, allowed_caps);
gst_caps_unref (allowed_caps);
gst_caps_unref (output_caps);
if (gst_caps_is_empty (src_caps))
goto not_negotiated;
gst_pad_fixate_caps (vpp->srcpad, src_caps);
if (gst_vdp_vpp_is_interlaced (vpp)) {
gint fps_n, fps_d;
if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) {
gst_fraction_double (&fps_n, &fps_d);
gst_caps_set_simple (src_caps, "framerate", GST_TYPE_FRACTION, fps_n,
fps_d, NULL);
vpp->field_duration = gst_util_uint64_scale (GST_SECOND, fps_d, fps_n);
}
gst_caps_set_simple (src_caps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
}
GST_DEBUG ("src_caps: %" GST_PTR_FORMAT, src_caps);
res = gst_pad_set_caps (vpp->srcpad, src_caps);
gst_caps_unref (src_caps);
done:
gst_object_unref (vpp);
if (video_caps)
gst_caps_unref (video_caps);
return res;
null_allowed_caps:
GST_ERROR_OBJECT (vpp, "Got null from gst_pad_get_allowed_caps");
goto done;
empty_allowed_caps:
GST_ERROR_OBJECT (vpp, "Got EMPTY caps from gst_pad_get_allowed_caps");
gst_caps_unref (allowed_caps);
goto done;
not_negotiated:
gst_caps_unref (src_caps);
GST_ERROR_OBJECT (vpp, "Couldn't find suitable output format");
goto done;
}
static void
gst_vdp_vpp_flush (GstVdpVideoPostProcess * vpp)
{
gint i;
for (i = 0; i < vpp->n_future_pictures; i++) {
gst_vdp_picture_clear (vpp->future_pictures[i]);
}
vpp->n_future_pictures = 0;
for (i = 0; i < vpp->n_past_pictures; i++) {
gst_vdp_picture_clear (vpp->past_pictures[i]);
}
vpp->n_past_pictures = 0;
}
static gboolean
gst_vdp_vpp_start (GstVdpVideoPostProcess * vpp)
{
gint i;
GError *err;
vpp->interlaced = FALSE;
vpp->field_duration = GST_CLOCK_TIME_NONE;
vpp->earliest_time = GST_CLOCK_TIME_NONE;
vpp->discont = FALSE;
vpp->mixer = VDP_INVALID_HANDLE;
vpp->vpool = NULL;
for (i = 0; i < MAX_PICTURES; i++) {
vpp->future_pictures[i].buf = NULL;
vpp->past_pictures[i].buf = NULL;
}
vpp->n_future_pictures = 0;
vpp->n_past_pictures = 0;
err = NULL;
vpp->device = gst_vdp_get_device (vpp->display, &err);
if (G_UNLIKELY (!vpp->device))
goto device_error;
g_object_set (G_OBJECT (vpp->srcpad), "device", vpp->device, NULL);
return TRUE;
device_error:
gst_vdp_vpp_post_error (vpp, err);
return FALSE;
}
static gboolean
gst_vdp_vpp_stop (GstVdpVideoPostProcess * vpp)
{
gst_vdp_vpp_flush (vpp);
if (vpp->vpool)
g_object_unref (vpp->vpool);
if (vpp->mixer != VDP_INVALID_HANDLE) {
GstVdpDevice *device = vpp->device;
VdpStatus status;
status = device->vdp_video_mixer_destroy (vpp->mixer);
if (status != VDP_STATUS_OK) {
GST_ELEMENT_ERROR (vpp, RESOURCE, READ,
("Could not destroy vdpau decoder"),
("Error returned from vdpau was: %s",
device->vdp_get_error_string (status)));
return FALSE;
}
}
return TRUE;
}
static GstFlowReturn
gst_vdp_vpp_drain (GstVdpVideoPostProcess * vpp)
{
GstVdpPicture current_pic;
guint32 video_surfaces_past_count;
VdpVideoSurface video_surfaces_past[MAX_PICTURES];
guint32 video_surfaces_future_count;
VdpVideoSurface video_surfaces_future[MAX_PICTURES];
GstFlowReturn ret;
while (gst_vdp_vpp_get_next_picture (vpp,
&current_pic,
&video_surfaces_past_count, video_surfaces_past,
&video_surfaces_future_count, video_surfaces_future)) {
GError *err;
GstVdpOutputBuffer *outbuf;
GstStructure *structure;
GstVideoRectangle src_r = { 0, }
, dest_r = {
0,};
VdpRect rect;
GstVdpDevice *device;
VdpStatus status;
err = NULL;
ret =
gst_vdp_output_src_pad_alloc_buffer ((GstVdpOutputSrcPad *) vpp->srcpad,
&outbuf, &err);
if (ret != GST_FLOW_OK)
goto output_pad_error;
src_r.w = vpp->width;
src_r.h = vpp->height;
if (vpp->got_par) {
gint new_width;
new_width = gst_util_uint64_scale_int (src_r.w, vpp->par_n, vpp->par_d);
src_r.x += (src_r.w - new_width) / 2;
src_r.w = new_width;
}
structure = gst_caps_get_structure (GST_BUFFER_CAPS (outbuf), 0);
if (!gst_structure_get_int (structure, "width", &dest_r.w) ||
!gst_structure_get_int (structure, "height", &dest_r.h))
goto invalid_caps;
if (vpp->force_aspect_ratio) {
GstVideoRectangle res_r;
gst_video_sink_center_rect (src_r, dest_r, &res_r, TRUE);
rect.x0 = res_r.x;
rect.x1 = res_r.w + res_r.x;
rect.y0 = res_r.y;
rect.y1 = res_r.h + res_r.y;
} else {
rect.x0 = 0;
rect.x1 = dest_r.w;
rect.y0 = 0;
rect.y1 = dest_r.h;
}
device = vpp->device;
status =
device->vdp_video_mixer_render (vpp->mixer, VDP_INVALID_HANDLE, NULL,
current_pic.structure, video_surfaces_past_count, video_surfaces_past,
current_pic.buf->surface, video_surfaces_future_count,
video_surfaces_future, NULL, outbuf->surface, NULL, &rect, 0, NULL);
if (status != VDP_STATUS_OK)
goto render_error;
GST_BUFFER_TIMESTAMP (outbuf) = current_pic.timestamp;
if (gst_vdp_vpp_is_interlaced (vpp))
GST_BUFFER_DURATION (outbuf) = vpp->field_duration;
else
GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (current_pic.buf);
if (GST_BUFFER_FLAG_IS_SET (current_pic.buf, GST_BUFFER_FLAG_DISCONT))
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
if (GST_BUFFER_FLAG_IS_SET (current_pic.buf, GST_BUFFER_FLAG_PREROLL))
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_PREROLL);
if (GST_BUFFER_FLAG_IS_SET (current_pic.buf, GST_BUFFER_FLAG_GAP))
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
err = NULL;
ret =
gst_vdp_output_src_pad_push ((GstVdpOutputSrcPad *) vpp->srcpad,
outbuf, &err);
if (ret != GST_FLOW_OK)
goto output_pad_error;
continue;
invalid_caps:
gst_buffer_unref (GST_BUFFER (outbuf));
GST_ELEMENT_ERROR (vpp, STREAM, FAILED, ("Invalid output caps"), (NULL));
ret = GST_FLOW_ERROR;
break;
render_error:
gst_buffer_unref (GST_BUFFER (outbuf));
GST_ELEMENT_ERROR (vpp, RESOURCE, READ,
("Could not postprocess frame"),
("Error returned from vdpau was: %s",
device->vdp_get_error_string (status)));
ret = GST_FLOW_ERROR;
break;
output_pad_error:
if (ret == GST_FLOW_ERROR && err != NULL)
gst_vdp_vpp_post_error (vpp, err);
break;
}
return ret;
}
static GstFlowReturn
gst_vdp_vpp_chain (GstPad * pad, GstBuffer * buffer)
{
GstVdpVideoPostProcess *vpp =
GST_VDP_VIDEO_POST_PROCESS (gst_pad_get_parent (pad));
GstClockTime qostime;
GstFlowReturn ret = GST_FLOW_OK;
GError *err;
GST_DEBUG ("chain");
/* can only do QoS if the segment is in TIME */
if (vpp->segment.format != GST_FORMAT_TIME)
goto no_qos;
/* QOS is done on the running time of the buffer, get it now */
qostime = gst_segment_to_running_time (&vpp->segment, GST_FORMAT_TIME,
GST_BUFFER_TIMESTAMP (buffer));
if (qostime != -1) {
gboolean need_skip;
GstClockTime earliest_time;
/* lock for getting the QoS parameters that are set (in a different thread)
* with the QOS events */
GST_OBJECT_LOCK (vpp);
earliest_time = vpp->earliest_time;
/* check for QoS, don't perform conversion for buffers
* that are known to be late. */
need_skip = GST_CLOCK_TIME_IS_VALID (earliest_time) && qostime != -1 &&
qostime <= earliest_time;
GST_OBJECT_UNLOCK (vpp);
if (need_skip) {
GST_DEBUG_OBJECT (vpp, "skipping transform: qostime %"
GST_TIME_FORMAT " <= %" GST_TIME_FORMAT,
GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
/* mark discont for next buffer */
vpp->discont = TRUE;
gst_buffer_unref (buffer);
return GST_FLOW_OK;
}
}
no_qos:
if (vpp->discont) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
vpp->discont = FALSE;
}
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))) {
GST_DEBUG_OBJECT (vpp, "Received discont buffer");
gst_vdp_vpp_flush (vpp);
}
if (!vpp->native_input) {
GstVdpVideoBuffer *video_buf;
err = NULL;
video_buf =
(GstVdpVideoBuffer *) gst_vdp_buffer_pool_get_buffer (vpp->vpool, &err);
if (G_UNLIKELY (!video_buf))
goto video_buf_error;
if (!gst_vdp_video_buffer_upload (video_buf, buffer, vpp->fourcc,
vpp->width, vpp->height)) {
gst_buffer_unref (GST_BUFFER (video_buf));
GST_ELEMENT_ERROR (vpp, RESOURCE, READ,
("Couldn't upload YUV data to vdpau"), (NULL));
ret = GST_FLOW_ERROR;
goto error;
}
gst_buffer_copy_metadata (GST_BUFFER (video_buf), buffer,
GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS);
gst_buffer_unref (buffer);
buffer = GST_BUFFER (video_buf);
}
if (G_UNLIKELY (vpp->mixer == VDP_INVALID_HANDLE)) {
ret = gst_vdp_vpp_create_mixer (vpp);
if (ret != GST_FLOW_OK)
goto error;
}
gst_vdp_vpp_add_buffer (vpp, GST_VDP_VIDEO_BUFFER (buffer));
ret = gst_vdp_vpp_drain (vpp);
done:
gst_object_unref (vpp);
return ret;
error:
gst_buffer_unref (buffer);
goto done;
video_buf_error:
gst_buffer_unref (GST_BUFFER (buffer));
gst_vdp_vpp_post_error (vpp, err);
ret = GST_FLOW_ERROR;
goto done;
}
static GstCaps *
gst_vdp_vpp_sink_getcaps (GstPad * pad)
{
GstVdpVideoPostProcess *vpp =
GST_VDP_VIDEO_POST_PROCESS (gst_pad_get_parent (pad));
GstCaps *caps;
if (vpp->device) {
caps = gst_vdp_video_buffer_get_allowed_caps (vpp->device);
} else {
GstElementClass *element_class = GST_ELEMENT_GET_CLASS (vpp);
GstPadTemplate *sink_template;
sink_template = gst_element_class_get_pad_template (element_class, "sink");
caps = gst_caps_copy (gst_pad_template_get_caps (sink_template));
}
GST_DEBUG ("returning caps: %" GST_PTR_FORMAT, caps);
gst_object_unref (vpp);
return caps;
}
static gboolean
gst_vdp_vpp_src_event (GstPad * pad, GstEvent * event)
{
GstVdpVideoPostProcess *vpp =
GST_VDP_VIDEO_POST_PROCESS (gst_pad_get_parent (pad));
gboolean res;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_QOS:
{
gdouble proportion;
GstClockTimeDiff diff;
GstClockTime timestamp;
gst_event_parse_qos (event, &proportion, &diff, &timestamp);
GST_OBJECT_LOCK (vpp);
vpp->earliest_time = timestamp + diff;
GST_OBJECT_UNLOCK (vpp);
res = gst_pad_event_default (pad, event);
break;
}
default:
res = gst_pad_event_default (pad, event);
}
gst_object_unref (vpp);
return res;
}
static gboolean
gst_vdp_vpp_sink_event (GstPad * pad, GstEvent * event)
{
GstVdpVideoPostProcess *vpp =
GST_VDP_VIDEO_POST_PROCESS (gst_pad_get_parent (pad));
gboolean res;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP:
{
GST_DEBUG_OBJECT (vpp, "flush stop");
gst_vdp_vpp_flush (vpp);
res = gst_pad_event_default (pad, event);
break;
}
case GST_EVENT_NEWSEGMENT:
{
gboolean update;
gdouble rate, applied_rate;
GstFormat format;
gint64 start, stop, time;
gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
&format, &start, &stop, &time);
GST_OBJECT_LOCK (vpp);
gst_segment_set_newsegment_full (&vpp->segment, update, rate,
applied_rate, format, start, stop, time);
GST_OBJECT_UNLOCK (vpp);
res = gst_pad_event_default (pad, event);
break;
}
default:
res = gst_pad_event_default (pad, event);
}
gst_object_unref (vpp);
return res;
}
static GstStateChangeReturn
gst_vdp_vpp_change_state (GstElement * element, GstStateChange transition)
{
GstVdpVideoPostProcess *vpp;
GstStateChangeReturn ret;
vpp = GST_VDP_VIDEO_POST_PROCESS (element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
if (!gst_vdp_vpp_start (vpp))
return GST_STATE_CHANGE_FAILURE;
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
if (!gst_vdp_vpp_stop (vpp))
ret = GST_STATE_CHANGE_FAILURE;
break;
default:
break;
}
return ret;
}
/* GObject vmethod implementations */
static void
gst_vdp_vpp_get_property (GObject * object, guint property_id, GValue * value,
GParamSpec * pspec)
{
GstVdpVideoPostProcess *vpp = GST_VDP_VIDEO_POST_PROCESS (object);
switch (property_id) {
case PROP_DISPLAY:
g_value_set_string (value, vpp->display);
break;
case PROP_FORCE_ASPECT_RATIO:
g_value_set_boolean (value, vpp->force_aspect_ratio);
break;
case PROP_DEINTERLACE_MODE:
g_value_set_enum (value, vpp->mode);
break;
case PROP_DEINTERLACE_METHOD:
g_value_set_enum (value, vpp->method);
break;
case PROP_NOISE_REDUCTION:
g_value_set_float (value, vpp->noise_reduction);
break;
case PROP_SHARPENING:
g_value_set_float (value, vpp->sharpening);
break;
case PROP_INVERSE_TELECINE:
g_value_set_boolean (value, vpp->inverse_telecine);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* GObject vmethod implementations */
static void
gst_vdp_vpp_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
GstVdpVideoPostProcess *vpp = GST_VDP_VIDEO_POST_PROCESS (object);
switch (property_id) {
case PROP_DISPLAY:
g_free (vpp->display);
vpp->display = g_value_dup_string (value);
break;
case PROP_FORCE_ASPECT_RATIO:
vpp->force_aspect_ratio = g_value_get_boolean (value);
break;
case PROP_DEINTERLACE_MODE:
vpp->mode = g_value_get_enum (value);
break;
case PROP_DEINTERLACE_METHOD:
{
GstVdpDeinterlaceMethods oldvalue;
oldvalue = vpp->method;
vpp->method = g_value_get_enum (value);
if (oldvalue == vpp->method)
break;
if (vpp->mixer != VDP_INVALID_HANDLE) {
if (oldvalue != GST_VDP_DEINTERLACE_METHOD_BOB)
gst_vdp_vpp_activate_deinterlace_method (vpp, oldvalue, FALSE);
if (vpp->method != GST_VDP_DEINTERLACE_METHOD_BOB)
gst_vdp_vpp_activate_deinterlace_method (vpp, oldvalue, TRUE);
}
break;
}
case PROP_NOISE_REDUCTION:
{
gfloat old_value;
old_value = vpp->noise_reduction;
vpp->noise_reduction = g_value_get_float (value);
if (vpp->noise_reduction == old_value)
break;
if (vpp->mixer != VDP_INVALID_HANDLE) {
if (vpp->noise_reduction == 0.0)
gst_vdp_vpp_activate_feature (vpp,
VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION, FALSE);
if (old_value == 0.0)
gst_vdp_vpp_activate_feature (vpp,
VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION, TRUE);
gst_vdp_vpp_set_attribute_float (vpp,
VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL,
vpp->noise_reduction);
}
break;
}
case PROP_SHARPENING:
{
gfloat old_value;
old_value = vpp->sharpening;
vpp->sharpening = g_value_get_float (value);
if (vpp->sharpening == old_value)
break;
if (vpp->mixer != VDP_INVALID_HANDLE) {
if (vpp->sharpening == 0.0)
gst_vdp_vpp_activate_feature (vpp,
VDP_VIDEO_MIXER_FEATURE_SHARPNESS, FALSE);
if (old_value == 0.0)
gst_vdp_vpp_activate_feature (vpp,
VDP_VIDEO_MIXER_FEATURE_SHARPNESS, TRUE);
gst_vdp_vpp_set_attribute_float (vpp,
VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL, vpp->sharpening);
}
break;
}
case PROP_INVERSE_TELECINE:
{
vpp->inverse_telecine = g_value_get_boolean (value);
if (vpp->mixer != VDP_INVALID_HANDLE) {
gst_vdp_vpp_activate_feature (vpp,
VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE, vpp->inverse_telecine);
}
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* GType vmethod implementations */
static void
gst_vdp_vpp_base_init (gpointer gclass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
GstCaps *src_caps, *sink_caps;
GstPadTemplate *src_template, *sink_template;
gst_element_class_set_details_simple (element_class,
"VdpauVideoPostProcess",
"Filter/Converter/Decoder/Video",
"Post process GstVdpVideoBuffers and output GstVdpOutputBuffers",
"Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>");
/* SRC PAD */
src_caps = gst_vdp_output_buffer_get_template_caps ();
src_template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
src_caps);
gst_element_class_add_pad_template (element_class, src_template);
/* SINK PAD */
sink_caps = gst_vdp_video_buffer_get_caps (FALSE, 0);
sink_template = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
sink_caps);
gst_element_class_add_pad_template (element_class, sink_template);
}
/* initialize the vdpaumpegdecoder's class */
static void
gst_vdp_vpp_class_init (GstVdpVideoPostProcessClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->get_property = gst_vdp_vpp_get_property;
gobject_class->set_property = gst_vdp_vpp_set_property;
gobject_class->finalize = gst_vdp_vpp_finalize;
g_object_class_install_property (gobject_class, PROP_DISPLAY,
g_param_spec_string ("display", "Display", "X Display name",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
"When enabled, the plugin will only scale up the input surface to the"
"maximum size where the aspect ratio can be preserved", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_DEINTERLACE_MODE,
g_param_spec_enum ("mode", "Deinterlace mode",
"Specifies if the element should deinterlace or not",
GST_TYPE_VDP_DEINTERLACE_MODES, GST_VDP_DEINTERLACE_MODE_AUTO,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_DEINTERLACE_METHOD,
g_param_spec_enum ("method", "Deinterlace method",
"Specifies which deinterlace method to use",
GST_TYPE_VDP_DEINTERLACE_METHODS, GST_VDP_DEINTERLACE_METHOD_BOB,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_NOISE_REDUCTION,
g_param_spec_float ("noise-reduction", "Noise reduction",
"The amount of noise reduction that should be done", 0.0, 1.0, 0.0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_SHARPENING,
g_param_spec_float ("sharpening", "Sharpening",
"The amount of sharpening or blurring to be applied", -1.0, 1.0, 0.0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_INVERSE_TELECINE,
g_param_spec_boolean ("inverse-telecine", "Inverse telecine",
"Whether inverse telecine should be used", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state = gst_vdp_vpp_change_state;
}
static void
gst_vdp_vpp_init (GstVdpVideoPostProcess * vpp,
GstVdpVideoPostProcessClass * gclass)
{
GstPadTemplate *src_template, *sink_template;
vpp->device = NULL;
vpp->mixer = VDP_INVALID_HANDLE;
vpp->display = NULL;
vpp->force_aspect_ratio = FALSE;
vpp->mode = GST_VDP_DEINTERLACE_MODE_AUTO;
vpp->method = GST_VDP_DEINTERLACE_METHOD_BOB;
vpp->noise_reduction = 0.0;
vpp->sharpening = 0.0;
/* SRC PAD */
src_template =
gst_element_class_get_pad_template (GST_ELEMENT_CLASS (gclass), "src");
vpp->srcpad = GST_PAD (gst_vdp_output_src_pad_new (src_template, "src"));
gst_element_add_pad (GST_ELEMENT (vpp), vpp->srcpad);
gst_pad_set_event_function (vpp->srcpad,
GST_DEBUG_FUNCPTR (gst_vdp_vpp_src_event));
/* SINK PAD */
sink_template =
gst_element_class_get_pad_template (GST_ELEMENT_CLASS (gclass), "sink");
vpp->sinkpad = gst_pad_new_from_template (sink_template, "sink");
gst_element_add_pad (GST_ELEMENT (vpp), vpp->sinkpad);
gst_pad_set_getcaps_function (vpp->sinkpad, gst_vdp_vpp_sink_getcaps);
gst_pad_set_setcaps_function (vpp->sinkpad, gst_vdp_vpp_sink_setcaps);
gst_pad_set_chain_function (vpp->sinkpad,
GST_DEBUG_FUNCPTR (gst_vdp_vpp_chain));
gst_pad_set_event_function (vpp->sinkpad,
GST_DEBUG_FUNCPTR (gst_vdp_vpp_sink_event));
}
static void
gst_vdp_vpp_finalize (GObject * object)
{
}