| /* 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 = 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 <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); | |
| } |