gstreamer/ext/resindvd/rsndec.c
Matthew Waters 640a65bf96 gst: don't use volatile to mean atomic
volatile is not sufficient to provide atomic guarantees and real atomics
should be used instead.  GCC 11 has started warning about using volatile
with atomic operations.

https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1719

Discovered in https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues/868

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2098>
2021-03-22 14:34:36 +11:00

473 lines
13 KiB
C

/* GStreamer
* Copyright (C) <2009> Jan Schmidt <thaytan@noraisin.net>
* Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/video/video.h>
#include <gst/audio/audio.h>
#include "rsndec.h"
GST_DEBUG_CATEGORY_STATIC (rsn_dec_debug);
#define GST_CAT_DEFAULT rsn_dec_debug
static GstStateChangeReturn rsn_dec_change_state (GstElement * element,
GstStateChange transition);
static void rsn_dec_dispose (GObject * gobj);
static void cleanup_child (RsnDec * self);
static GstBinClass *rsn_dec_parent_class = NULL;
static void
rsn_dec_class_init (RsnDecClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (rsn_dec_debug, "rsndec",
0, "Resin DVD stream decoder");
rsn_dec_parent_class = (GstBinClass *) g_type_class_peek_parent (klass);
object_class->dispose = rsn_dec_dispose;
element_class->change_state = GST_DEBUG_FUNCPTR (rsn_dec_change_state);
}
static gboolean
rsn_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
RsnDec *self = RSN_DEC (parent);
gboolean ret = TRUE;
const GstStructure *s = gst_event_get_structure (event);
const gchar *name = (s ? gst_structure_get_name (s) : NULL);
if (name && g_str_equal (name, "application/x-gst-dvd"))
ret = gst_pad_push_event (GST_PAD_CAST (self->srcpad), event);
else
ret = self->sink_event_func (pad, parent, event);
return ret;
}
static void
rsn_dec_init (RsnDec * self, RsnDecClass * klass)
{
GstPadTemplate *templ;
templ =
gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "sink");
g_assert (templ != NULL);
self->sinkpad =
GST_GHOST_PAD_CAST (gst_ghost_pad_new_no_target_from_template ("sink",
templ));
self->sink_event_func = GST_PAD_EVENTFUNC (self->sinkpad);
gst_pad_set_event_function (GST_PAD_CAST (self->sinkpad),
GST_DEBUG_FUNCPTR (rsn_dec_sink_event));
templ = gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src");
g_assert (templ != NULL);
self->srcpad =
GST_GHOST_PAD_CAST (gst_ghost_pad_new_no_target_from_template ("src",
templ));
gst_element_add_pad (GST_ELEMENT (self), GST_PAD_CAST (self->sinkpad));
gst_element_add_pad (GST_ELEMENT (self), GST_PAD_CAST (self->srcpad));
}
static void
rsn_dec_dispose (GObject * object)
{
RsnDec *self = (RsnDec *) object;
cleanup_child (self);
G_OBJECT_CLASS (rsn_dec_parent_class)->dispose (object);
}
static gboolean
rsn_dec_set_child (RsnDec * self, GstElement * new_child)
{
GstPad *child_pad;
if (self->current_decoder) {
gst_ghost_pad_set_target (self->srcpad, NULL);
gst_ghost_pad_set_target (self->sinkpad, NULL);
gst_bin_remove ((GstBin *) self, self->current_decoder);
self->current_decoder = NULL;
}
if (new_child == NULL)
return TRUE;
if (!gst_bin_add ((GstBin *) self, new_child))
return FALSE;
child_pad = gst_element_get_static_pad (new_child, "sink");
if (child_pad == NULL) {
return FALSE;
}
gst_ghost_pad_set_target (self->sinkpad, child_pad);
gst_object_unref (child_pad);
child_pad = gst_element_get_static_pad (new_child, "src");
if (child_pad == NULL) {
return FALSE;
}
gst_ghost_pad_set_target (self->srcpad, child_pad);
gst_object_unref (child_pad);
GST_DEBUG_OBJECT (self, "Add child %" GST_PTR_FORMAT, new_child);
self->current_decoder = new_child;
gst_element_sync_state_with_parent (new_child);
return TRUE;
}
static void
cleanup_child (RsnDec * self)
{
GST_DEBUG_OBJECT (self, "Removing child element");
(void) rsn_dec_set_child (self, NULL);
}
typedef struct
{
GstCaps *desired_caps;
GstCaps *decoder_caps;
} RsnDecFactoryFilterCtx;
static gboolean
rsndec_factory_filter (GstPluginFeature * feature, RsnDecFactoryFilterCtx * ctx)
{
GstElementFactory *factory;
guint rank;
const gchar *klass;
const GList *templates;
GList *walk;
gboolean can_sink = FALSE;
/* we only care about element factories */
if (!GST_IS_ELEMENT_FACTORY (feature))
return FALSE;
factory = GST_ELEMENT_FACTORY (feature);
klass =
gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
/* only decoders can play */
if (strstr (klass, "Decoder") == NULL)
return FALSE;
/* only select elements with autoplugging rank */
rank = gst_plugin_feature_get_rank (feature);
if (rank < GST_RANK_MARGINAL)
return FALSE;
/* See if the element has a sink pad that can possibly sink this caps */
/* get the templates from the element factory */
templates = gst_element_factory_get_static_pad_templates (factory);
for (walk = (GList *) templates; walk && !can_sink; walk = g_list_next (walk)) {
GstStaticPadTemplate *templ = walk->data;
/* we only care about the sink templates */
if (templ->direction == GST_PAD_SINK) {
GstCaps *intersect;
GstCaps *tmpl_caps;
/* try to intersect the caps with the caps of the template */
tmpl_caps = gst_static_caps_get (&templ->static_caps);
intersect = gst_caps_intersect (ctx->desired_caps, tmpl_caps);
gst_caps_unref (tmpl_caps);
/* check if the intersection is empty */
if (!gst_caps_is_empty (intersect)) {
/* non empty intersection, we can use this element */
can_sink = TRUE;
ctx->decoder_caps = gst_caps_merge (ctx->decoder_caps, intersect);
} else
gst_caps_unref (intersect);
}
}
if (can_sink) {
GST_DEBUG ("Found decoder element %s (%s)",
gst_element_factory_get_metadata (factory,
GST_ELEMENT_METADATA_LONGNAME),
gst_plugin_feature_get_name (feature));
}
return can_sink;
}
static gint
sort_by_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
{
gint diff;
const gchar *rname1, *rname2;
diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
if (diff != 0)
return diff;
rname1 = gst_plugin_feature_get_name (f1);
rname2 = gst_plugin_feature_get_name (f2);
diff = strcmp (rname2, rname1);
return diff;
}
static gpointer
_get_decoder_factories (gpointer arg)
{
GstElementClass *klass = arg;
GList *factories;
GstPadTemplate *templ = gst_element_class_get_pad_template (klass,
"sink");
RsnDecFactoryFilterCtx ctx = { NULL, };
GstCaps *raw;
gboolean raw_audio;
GstRegistry *registry = gst_registry_get ();
ctx.desired_caps = gst_pad_template_get_caps (templ);
raw =
gst_caps_from_string
("audio/x-raw,format=(string){ F32LE, F32BE, F64LE, F64BE }");
raw_audio = gst_caps_can_intersect (raw, ctx.desired_caps);
if (raw_audio) {
GstCaps *sub = gst_caps_subtract (ctx.desired_caps, raw);
ctx.desired_caps = sub;
} else {
gst_caps_ref (ctx.desired_caps);
}
gst_caps_unref (raw);
/* Set decoder caps to empty. Will be filled by the factory_filter */
ctx.decoder_caps = gst_caps_new_empty ();
GST_DEBUG ("Finding factories for caps: %" GST_PTR_FORMAT, ctx.desired_caps);
factories = gst_registry_feature_filter (registry,
(GstPluginFeatureFilter) rsndec_factory_filter, FALSE, &ctx);
/* If these are audio caps, we add audioconvert, which is not a decoder,
but allows raw audio to go through relatively unmolested - this will
come handy when we have to send placeholder silence to allow preroll
for those DVDs which have titles with no audio track. */
if (raw_audio) {
GstPluginFeature *feature;
GST_DEBUG ("These are audio caps, adding audioconvert");
feature =
gst_registry_find_feature (registry, "audioconvert",
GST_TYPE_ELEMENT_FACTORY);
if (feature) {
factories = g_list_append (factories, feature);
} else {
GST_WARNING ("Could not find feature audioconvert");
}
}
factories = g_list_sort (factories, (GCompareFunc) sort_by_ranks);
GST_DEBUG ("Available decoder caps %" GST_PTR_FORMAT, ctx.decoder_caps);
gst_caps_unref (ctx.decoder_caps);
gst_caps_unref (ctx.desired_caps);
return factories;
}
static GstStateChangeReturn
rsn_dec_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
RsnDec *self = RSN_DEC (element);
RsnDecClass *klass = RSN_DEC_GET_CLASS (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:{
GstElement *new_child;
const GList *decoder_factories;
new_child = gst_element_factory_make ("autoconvert", NULL);
decoder_factories = klass->get_decoder_factories (klass);
g_object_set (G_OBJECT (new_child), "factories", decoder_factories, NULL);
if (new_child == NULL || !rsn_dec_set_child (self, new_child))
ret = GST_STATE_CHANGE_FAILURE;
break;
}
case GST_STATE_CHANGE_READY_TO_PAUSED:
break;
default:
break;
}
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
ret =
GST_ELEMENT_CLASS (rsn_dec_parent_class)->change_state (element,
transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_NULL:
cleanup_child (self);
break;
default:
break;
}
return ret;
}
GType
rsn_dec_get_type (void)
{
static gsize type = 0;
if (g_once_init_enter (&type)) {
GType _type;
static const GTypeInfo type_info = {
sizeof (RsnDecClass),
NULL,
NULL,
(GClassInitFunc) rsn_dec_class_init,
NULL,
NULL,
sizeof (RsnDec),
0,
(GInstanceInitFunc) rsn_dec_init,
};
_type = g_type_register_static (GST_TYPE_BIN,
"RsnDec", &type_info, G_TYPE_FLAG_ABSTRACT);
g_once_init_leave (&type, _type);
}
return type;
}
/* Audio decoder subclass */
static GstStaticPadTemplate audio_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg,mpegversion=(int)1;"
"audio/x-private1-lpcm;"
"audio/x-private1-ac3;" "audio/ac3;" "audio/x-ac3;"
"audio/x-private1-dts; audio/x-raw,format=(string)"
GST_AUDIO_FORMATS_ALL)
);
static GstStaticPadTemplate audio_src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
);
G_DEFINE_TYPE (RsnAudioDec, rsn_audiodec, RSN_TYPE_DEC);
static const GList *
rsn_audiodec_get_decoder_factories (RsnDecClass * klass)
{
static GOnce gonce = G_ONCE_INIT;
g_once (&gonce, _get_decoder_factories, klass);
return (const GList *) gonce.retval;
}
static void
rsn_audiodec_class_init (RsnAudioDecClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
RsnDecClass *dec_class = RSN_DEC_CLASS (klass);
gst_element_class_add_static_pad_template (element_class,
&audio_src_template);
gst_element_class_add_static_pad_template (element_class,
&audio_sink_template);
gst_element_class_set_static_metadata (element_class, "RsnAudioDec",
"Audio/Decoder",
"Resin DVD audio stream decoder", "Jan Schmidt <thaytan@noraisin.net>");
dec_class->get_decoder_factories = rsn_audiodec_get_decoder_factories;
}
static void
rsn_audiodec_init (RsnAudioDec * self)
{
}
/* Video decoder subclass */
static GstStaticPadTemplate video_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/mpeg, "
"mpegversion = (int) [ 1, 2 ], " "systemstream = (bool) FALSE")
);
static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
);
G_DEFINE_TYPE (RsnVideoDec, rsn_videodec, RSN_TYPE_DEC);
static const GList *
rsn_videodec_get_decoder_factories (RsnDecClass * klass)
{
static GOnce gonce = G_ONCE_INIT;
g_once (&gonce, _get_decoder_factories, klass);
return (const GList *) gonce.retval;
}
static void
rsn_videodec_class_init (RsnAudioDecClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
RsnDecClass *dec_class = RSN_DEC_CLASS (klass);
gst_element_class_add_static_pad_template (element_class,
&video_src_template);
gst_element_class_add_static_pad_template (element_class,
&video_sink_template);
gst_element_class_set_static_metadata (element_class, "RsnVideoDec",
"Video/Decoder",
"Resin DVD video stream decoder", "Jan Schmidt <thaytan@noraisin.net>");
dec_class->get_decoder_factories = rsn_videodec_get_decoder_factories;
}
static void
rsn_videodec_init (RsnVideoDec * self)
{
}