/* GStreamer * Copyright (C) <2021> Collabora Ltd. * Author: Nicolas Dufresne * * 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 #include #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 = GST_FLOW_EOS; if (alpha_meta) alpha_buffer = gst_buffer_ref (alpha_meta->buffer); ret = gst_pad_push (self->src_pad, buffer); ret = gst_flow_combiner_update_flow (self->flow_combiner, ret); /* we lost ownership here */ buffer = NULL; alpha_meta = NULL; if (alpha_buffer) { ret = gst_pad_push (self->alpha_pad, alpha_buffer); } else { gst_pad_push_event (self->alpha_pad, gst_event_new_gap (pts, duration)); ret = GST_PAD_LAST_FLOW_RETURN (self->alpha_pad); } 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 "); 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); }