mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-24 06:56:26 +00:00
As the alphacombine is simplified to received matching pair of buffers, we can't just stop streaming when we receive EOS from downstream. Due to usage of queue, the moment we get this return value may differ. Though, by continuing pushing, we override the last_flowret on the pad which can make us miss that we effectively can combine all flow into EOS. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2238>
338 lines
9.8 KiB
C
338 lines
9.8 KiB
C
/* GStreamer
|
|
* Copyright (C) <2021> Collabora Ltd.
|
|
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.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., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:element-codecalphademux
|
|
* @title: CODEC Alpha Demuxer
|
|
*
|
|
* Extracts the CODEC (typically VP8/VP9) alpha stream stored as meta and
|
|
* exposes it as a stream. This element allow using single stream VP8/9
|
|
* decoders in order to decode both streams.
|
|
*
|
|
* ## Example launch line
|
|
* |[
|
|
* gst-launch-1.0 -v filesrc location=transparency.webm ! matroskademux !
|
|
* codecalphademux name=d
|
|
* d.video ! queue ! vp9dec ! autovideosink
|
|
* d.alpha ! queue ! vp9dec ! autovideosink
|
|
* ]| This pipeline splits and decode the video and the alpha stream, showing
|
|
* the result on seperate windows.
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gst/video/video.h>
|
|
#include <gst/base/gstflowcombiner.h>
|
|
|
|
#include "gstcodecalphademux.h"
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (codecalphademux_debug);
|
|
#define GST_CAT_DEFAULT (codecalphademux_debug)
|
|
|
|
struct _GstCodecAlphaDemux
|
|
{
|
|
GstElement parent;
|
|
|
|
GstPad *sink_pad;
|
|
GstPad *src_pad;
|
|
GstPad *alpha_pad;
|
|
|
|
GstFlowCombiner *flow_combiner;
|
|
};
|
|
|
|
#define gst_codec_alpha_demux_parent_class parent_class
|
|
G_DEFINE_TYPE_WITH_CODE (GstCodecAlphaDemux, gst_codec_alpha_demux,
|
|
GST_TYPE_ELEMENT,
|
|
GST_DEBUG_CATEGORY_INIT (codecalphademux_debug, "codecalphademux", 0,
|
|
"codecalphademux"));
|
|
|
|
GST_ELEMENT_REGISTER_DEFINE (codec_alpha_demux, "codecalphademux",
|
|
GST_RANK_NONE, GST_TYPE_CODEC_ALPHA_DEMUX);
|
|
|
|
static GstStaticPadTemplate gst_codec_alpha_demux_sink_template =
|
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("ANY")
|
|
);
|
|
|
|
static GstStaticPadTemplate gst_codec_alpha_demux_src_template =
|
|
GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("ANY")
|
|
);
|
|
|
|
static GstStaticPadTemplate gst_codec_alpha_demux_alpha_template =
|
|
GST_STATIC_PAD_TEMPLATE ("alpha",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("ANY")
|
|
);
|
|
|
|
static GstFlowReturn
|
|
gst_codec_alpha_demux_chain (GstPad * pad, GstObject * object,
|
|
GstBuffer * buffer)
|
|
{
|
|
GstCodecAlphaDemux *self = GST_CODEC_ALPHA_DEMUX (object);
|
|
GstVideoCodecAlphaMeta *alpha_meta =
|
|
gst_buffer_get_video_codec_alpha_meta (buffer);
|
|
GstBuffer *alpha_buffer = NULL;
|
|
GstClockTime pts = GST_BUFFER_PTS (buffer);
|
|
GstClockTime duration = GST_BUFFER_DURATION (buffer);
|
|
GstFlowReturn ret;
|
|
gboolean src_pad_eos, alpha_pad_eos;
|
|
|
|
if (alpha_meta)
|
|
alpha_buffer = gst_buffer_ref (alpha_meta->buffer);
|
|
|
|
/* To satisfy the alphacombine requirement, we need to push in pair here, so we
|
|
* can't just stop pushing at EOS. For this reason, remember if pad had a
|
|
* flow return of EOS and set it back if needed. */
|
|
src_pad_eos = (GST_PAD_LAST_FLOW_RETURN (self->src_pad) == GST_FLOW_EOS);
|
|
alpha_pad_eos = (GST_PAD_LAST_FLOW_RETURN (self->alpha_pad) == GST_FLOW_EOS);
|
|
|
|
if (src_pad_eos && alpha_pad_eos) {
|
|
gst_buffer_unref (alpha_buffer);
|
|
gst_buffer_unref (buffer);
|
|
return GST_FLOW_EOS;
|
|
}
|
|
|
|
ret = gst_pad_push (self->src_pad, buffer);
|
|
|
|
/* we lost ownership here */
|
|
buffer = NULL;
|
|
alpha_meta = NULL;
|
|
|
|
if (ret == GST_FLOW_OK && src_pad_eos)
|
|
gst_flow_combiner_update_pad_flow (self->flow_combiner, self->src_pad,
|
|
GST_FLOW_EOS);
|
|
else
|
|
gst_flow_combiner_update_flow (self->flow_combiner, ret);
|
|
|
|
if (alpha_buffer)
|
|
ret = gst_pad_push (self->alpha_pad, alpha_buffer);
|
|
else
|
|
ret = gst_pad_push_event (self->alpha_pad,
|
|
gst_event_new_gap (pts, duration));
|
|
|
|
if (ret == GST_FLOW_OK && alpha_pad_eos)
|
|
ret = gst_flow_combiner_update_pad_flow (self->flow_combiner,
|
|
self->alpha_pad, GST_FLOW_EOS);
|
|
else
|
|
ret = gst_flow_combiner_update_flow (self->flow_combiner, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_codec_alpha_demux_transform_caps (GstCaps * caps, gboolean codec_alpha)
|
|
{
|
|
if (!caps)
|
|
return NULL;
|
|
|
|
caps = gst_caps_copy (caps);
|
|
gst_caps_set_simple (caps, "codec-alpha", G_TYPE_BOOLEAN, codec_alpha, NULL);
|
|
|
|
return caps;
|
|
}
|
|
|
|
static GstEvent *
|
|
gst_codec_alpha_demux_transform_caps_event (GstEvent * src_event)
|
|
{
|
|
GstEvent *dst_event;
|
|
GstCaps *caps;
|
|
|
|
gst_event_parse_caps (src_event, &caps);
|
|
|
|
caps = gst_codec_alpha_demux_transform_caps (caps, FALSE);
|
|
dst_event = gst_event_new_caps (caps);
|
|
gst_event_set_seqnum (dst_event, gst_event_get_seqnum (src_event));
|
|
|
|
gst_caps_unref (caps);
|
|
gst_event_unref (src_event);
|
|
return dst_event;
|
|
}
|
|
|
|
static gboolean
|
|
gst_codec_alpha_demux_sink_event (GstPad * sink_pad, GstObject * parent,
|
|
GstEvent * event)
|
|
{
|
|
GstCodecAlphaDemux *self = GST_CODEC_ALPHA_DEMUX (parent);
|
|
|
|
switch (event->type) {
|
|
case GST_EVENT_FLUSH_STOP:
|
|
gst_flow_combiner_reset (self->flow_combiner);
|
|
break;
|
|
case GST_EVENT_CAPS:
|
|
event = gst_codec_alpha_demux_transform_caps_event (event);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return gst_pad_event_default (sink_pad, parent, event);
|
|
}
|
|
|
|
static gboolean
|
|
gst_codec_alpha_demux_sink_query (GstPad * sink_pad, GstObject * parent,
|
|
GstQuery * query)
|
|
{
|
|
GstQuery *peer_query;
|
|
GstCaps *caps;
|
|
gboolean ret;
|
|
|
|
switch (query->type) {
|
|
case GST_QUERY_CAPS:
|
|
gst_query_parse_caps (query, &caps);
|
|
caps = gst_codec_alpha_demux_transform_caps (caps, FALSE);
|
|
peer_query = gst_query_new_caps (caps);
|
|
gst_clear_caps (&caps);
|
|
break;
|
|
case GST_QUERY_ACCEPT_CAPS:
|
|
gst_query_parse_accept_caps (query, &caps);
|
|
caps = gst_codec_alpha_demux_transform_caps (caps, FALSE);
|
|
peer_query = gst_query_new_accept_caps (caps);
|
|
gst_clear_caps (&caps);
|
|
break;
|
|
default:
|
|
peer_query = query;
|
|
break;
|
|
}
|
|
|
|
ret = gst_pad_query_default (sink_pad, parent, peer_query);
|
|
if (!ret) {
|
|
if (peer_query != query)
|
|
gst_query_unref (peer_query);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (query->type) {
|
|
case GST_QUERY_CAPS:
|
|
gst_query_parse_caps_result (peer_query, &caps);
|
|
caps = gst_caps_copy (caps);
|
|
caps = gst_codec_alpha_demux_transform_caps (caps, TRUE);
|
|
gst_query_set_caps_result (query, caps);
|
|
gst_caps_unref (caps);
|
|
gst_query_unref (peer_query);
|
|
break;
|
|
case GST_QUERY_ACCEPT_CAPS:
|
|
{
|
|
gboolean result;
|
|
gst_query_parse_accept_caps_result (peer_query, &result);
|
|
gst_query_set_accept_caps_result (query, result);
|
|
gst_query_unref (peer_query);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_codec_alpha_demux_start (GstCodecAlphaDemux * self)
|
|
{
|
|
gst_flow_combiner_reset (self->flow_combiner);
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_codec_alpha_demux_change_state (GstElement * element,
|
|
GstStateChange transition)
|
|
{
|
|
GstCodecAlphaDemux *self = GST_CODEC_ALPHA_DEMUX (element);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
gst_codec_alpha_demux_start (self);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
}
|
|
|
|
static void
|
|
gst_codec_alpha_demux_dispose (GObject * object)
|
|
{
|
|
GstCodecAlphaDemux *self = GST_CODEC_ALPHA_DEMUX (object);
|
|
|
|
g_clear_object (&self->sink_pad);
|
|
g_clear_object (&self->src_pad);
|
|
g_clear_object (&self->alpha_pad);
|
|
g_clear_pointer (&self->flow_combiner, gst_flow_combiner_unref);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gst_codec_alpha_demux_class_init (GstCodecAlphaDemuxClass * klass)
|
|
{
|
|
GstElementClass *element_class = (GstElementClass *) klass;
|
|
GObjectClass *object_class = (GObjectClass *) klass;
|
|
|
|
gst_element_class_set_static_metadata (element_class,
|
|
"CODEC Alpha Demuxer", "Codec/Demuxer",
|
|
"Extract and expose as a stream the CODEC alpha.",
|
|
"Nicolas Dufresne <nicolas.dufresne@collabora.com>");
|
|
|
|
gst_element_class_add_static_pad_template (element_class,
|
|
&gst_codec_alpha_demux_sink_template);
|
|
gst_element_class_add_static_pad_template (element_class,
|
|
&gst_codec_alpha_demux_src_template);
|
|
gst_element_class_add_static_pad_template (element_class,
|
|
&gst_codec_alpha_demux_alpha_template);
|
|
|
|
element_class->change_state =
|
|
GST_DEBUG_FUNCPTR (gst_codec_alpha_demux_change_state);
|
|
|
|
object_class->dispose = GST_DEBUG_FUNCPTR (gst_codec_alpha_demux_dispose);
|
|
}
|
|
|
|
static void
|
|
gst_codec_alpha_demux_init (GstCodecAlphaDemux * self)
|
|
{
|
|
gst_element_create_all_pads (GST_ELEMENT (self));
|
|
self->sink_pad = gst_element_get_static_pad (GST_ELEMENT (self), "sink");
|
|
self->src_pad = gst_element_get_static_pad (GST_ELEMENT (self), "src");
|
|
self->alpha_pad = gst_element_get_static_pad (GST_ELEMENT (self), "alpha");
|
|
|
|
self->flow_combiner = gst_flow_combiner_new ();
|
|
gst_flow_combiner_add_pad (self->flow_combiner, self->src_pad);
|
|
gst_flow_combiner_add_pad (self->flow_combiner, self->alpha_pad);
|
|
|
|
GST_PAD_SET_PROXY_CAPS (self->sink_pad);
|
|
GST_PAD_SET_PROXY_CAPS (self->src_pad);
|
|
GST_PAD_SET_PROXY_CAPS (self->alpha_pad);
|
|
|
|
GST_PAD_SET_PROXY_SCHEDULING (self->sink_pad);
|
|
GST_PAD_SET_PROXY_SCHEDULING (self->src_pad);
|
|
GST_PAD_SET_PROXY_SCHEDULING (self->alpha_pad);
|
|
|
|
gst_pad_set_chain_function (self->sink_pad, gst_codec_alpha_demux_chain);
|
|
gst_pad_set_event_function (self->sink_pad, gst_codec_alpha_demux_sink_event);
|
|
gst_pad_set_query_function (self->sink_pad, gst_codec_alpha_demux_sink_query);
|
|
}
|