mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-05-03 08:58:45 +00:00
563 lines
16 KiB
C
563 lines
16 KiB
C
/* Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
* option. This file may not be copied, modified, or distributed
|
|
* except according to those terms.
|
|
*/
|
|
|
|
#include "demuxer.h"
|
|
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
typedef struct
|
|
{
|
|
gchar *long_name;
|
|
gchar *description;
|
|
gchar *classification;
|
|
gchar *author;
|
|
void *create_instance;
|
|
GstCaps *input_caps;
|
|
GstCaps *output_caps;
|
|
} ElementData;
|
|
static GHashTable *demuxers;
|
|
|
|
/* Declarations for Rust code */
|
|
extern gboolean demuxers_register (void *plugin);
|
|
extern void *demuxer_new (GstRsDemuxer * demuxer, void *create_instance);
|
|
extern void demuxer_drop (void *rsdemuxer);
|
|
|
|
extern gboolean demuxer_start (void *rsdemuxer, uint64_t upstream_size,
|
|
gboolean random_access);
|
|
extern gboolean demuxer_stop (void *rsdemuxer);
|
|
|
|
extern gboolean demuxer_is_seekable (void *rsdemuxer);
|
|
extern gboolean demuxer_get_position (void *rsdemuxer, uint64_t * position);
|
|
extern gboolean demuxer_get_duration (void *rsdemuxer, uint64_t * duration);
|
|
|
|
extern gboolean demuxer_seek (void *rsdemuxer, uint64_t start, uint64_t stop,
|
|
uint64_t * offset);
|
|
extern GstFlowReturn demuxer_handle_buffer (void *rsdemuxer,
|
|
GstBuffer * buffer);
|
|
extern void demuxer_end_of_stream (void *rsdemuxer);
|
|
|
|
extern void cstring_drop (void *str);
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_rs_demuxer_debug);
|
|
#define GST_CAT_DEFAULT gst_rs_demuxer_debug
|
|
|
|
static void gst_rs_demuxer_finalize (GObject * object);
|
|
static gboolean gst_rs_demuxer_sink_activate (GstPad * pad, GstObject * parent);
|
|
static gboolean gst_rs_demuxer_sink_activate_mode (GstPad * pad,
|
|
GstObject * parent, GstPadMode mode, gboolean active);
|
|
static GstFlowReturn gst_rs_demuxer_sink_chain (GstPad * pad,
|
|
GstObject * parent, GstBuffer * buf);
|
|
static gboolean gst_rs_demuxer_sink_event (GstPad * pad, GstObject * parent,
|
|
GstEvent * event);
|
|
static gboolean gst_rs_demuxer_src_query (GstPad * pad, GstObject * parent,
|
|
GstQuery * query);
|
|
static gboolean gst_rs_demuxer_src_event (GstPad * pad, GstObject * parent,
|
|
GstEvent * event);
|
|
static GstStateChangeReturn gst_rs_demuxer_change_state (GstElement * element,
|
|
GstStateChange transition);
|
|
static void gst_rs_demuxer_loop (GstRsDemuxer * demuxer);
|
|
|
|
static GObjectClass *parent_class;
|
|
|
|
static void
|
|
gst_rs_demuxer_class_init (GstRsDemuxerClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *gstelement_class;
|
|
ElementData *data = g_hash_table_lookup (demuxers,
|
|
GSIZE_TO_POINTER (G_TYPE_FROM_CLASS (klass)));
|
|
GstPadTemplate *templ;
|
|
g_assert (data != NULL);
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
gstelement_class = GST_ELEMENT_CLASS (klass);
|
|
|
|
gobject_class->finalize = gst_rs_demuxer_finalize;
|
|
|
|
gstelement_class->change_state = gst_rs_demuxer_change_state;
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class,
|
|
data->long_name, data->classification, data->description, data->author);
|
|
|
|
templ =
|
|
gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
|
data->input_caps);
|
|
gst_element_class_add_pad_template (gstelement_class, templ);
|
|
|
|
templ =
|
|
gst_pad_template_new ("src_%u", GST_PAD_SRC, GST_PAD_SOMETIMES,
|
|
data->output_caps);
|
|
gst_element_class_add_pad_template (gstelement_class, templ);
|
|
}
|
|
|
|
static void
|
|
gst_rs_demuxer_init (GstRsDemuxer * demuxer, GstRsDemuxerClass * klass)
|
|
{
|
|
ElementData *data = g_hash_table_lookup (demuxers,
|
|
GSIZE_TO_POINTER (G_TYPE_FROM_CLASS (klass)));
|
|
GstPadTemplate *templ;
|
|
g_assert (data != NULL);
|
|
|
|
demuxer->instance = demuxer_new (demuxer, data->create_instance);
|
|
|
|
templ =
|
|
gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "sink");
|
|
demuxer->sinkpad = gst_pad_new_from_template (templ, "sink");
|
|
|
|
gst_pad_set_activate_function (demuxer->sinkpad,
|
|
gst_rs_demuxer_sink_activate);
|
|
gst_pad_set_activatemode_function (demuxer->sinkpad,
|
|
gst_rs_demuxer_sink_activate_mode);
|
|
gst_pad_set_chain_function (demuxer->sinkpad, gst_rs_demuxer_sink_chain);
|
|
gst_pad_set_event_function (demuxer->sinkpad, gst_rs_demuxer_sink_event);
|
|
gst_element_add_pad (GST_ELEMENT (demuxer), demuxer->sinkpad);
|
|
|
|
demuxer->flow_combiner = gst_flow_combiner_new ();
|
|
|
|
GST_DEBUG_OBJECT (demuxer, "Instantiating");
|
|
}
|
|
|
|
static void
|
|
gst_rs_demuxer_finalize (GObject * object)
|
|
{
|
|
GstRsDemuxer *demuxer = GST_RS_DEMUXER (object);
|
|
|
|
GST_DEBUG_OBJECT (demuxer, "Finalizing");
|
|
gst_flow_combiner_free (demuxer->flow_combiner);
|
|
demuxer_drop (demuxer->instance);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
gst_rs_demuxer_sink_activate (GstPad * pad, GstObject * parent)
|
|
{
|
|
GstRsDemuxer *demuxer = GST_RS_DEMUXER (parent);
|
|
GstQuery *query;
|
|
GstPadMode mode = GST_PAD_MODE_PUSH;
|
|
|
|
query = gst_query_new_scheduling ();
|
|
if (!gst_pad_peer_query (pad, query)) {
|
|
gst_query_unref (query);
|
|
return FALSE;
|
|
}
|
|
// TODO
|
|
//if (gst_query_has_scheduling_mode_with_flags (query, GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE)) {
|
|
// GST_DEBUG_OBJECT (demuxer, "Activating in PULL mode");
|
|
// mode = GST_PAD_MODE_PULL;
|
|
//} else {
|
|
//GST_DEBUG_OBJECT (demuxer, "Activating in PUSH mode");
|
|
//}
|
|
gst_query_unref (query);
|
|
|
|
demuxer->upstream_size = -1;
|
|
query = gst_query_new_duration (GST_FORMAT_BYTES);
|
|
if (gst_pad_peer_query (pad, query)) {
|
|
gst_query_parse_duration (query, NULL, &demuxer->upstream_size);
|
|
}
|
|
gst_query_unref (query);
|
|
|
|
return gst_pad_activate_mode (pad, mode, TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
gst_rs_demuxer_sink_activate_mode (GstPad * pad,
|
|
GstObject * parent, GstPadMode mode, gboolean active)
|
|
{
|
|
GstRsDemuxer *demuxer = GST_RS_DEMUXER (parent);
|
|
gboolean res = TRUE;
|
|
|
|
GST_DEBUG_OBJECT (demuxer, "%s pad in %s mode",
|
|
(active ? "Activating" : "Deactivating"), gst_pad_mode_get_name (mode));
|
|
|
|
if (active) {
|
|
GST_DEBUG_OBJECT (demuxer, "Starting");
|
|
if (!demuxer_start (demuxer->instance, demuxer->upstream_size,
|
|
mode == GST_PAD_MODE_PULL ? TRUE : FALSE)) {
|
|
res = FALSE;
|
|
goto out;
|
|
}
|
|
if (mode == GST_PAD_MODE_PULL)
|
|
gst_pad_start_task (pad, (GstTaskFunction) gst_rs_demuxer_loop, demuxer,
|
|
NULL);
|
|
} else {
|
|
if (mode == GST_PAD_MODE_PULL)
|
|
gst_pad_stop_task (pad);
|
|
}
|
|
|
|
out:
|
|
|
|
return res;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_rs_demuxer_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
|
{
|
|
GstRsDemuxer *demuxer = GST_RS_DEMUXER (parent);
|
|
GstFlowReturn res;
|
|
|
|
GST_TRACE_OBJECT (demuxer, "Handling buffer %p", buf);
|
|
|
|
res = demuxer_handle_buffer (demuxer->instance, buf);
|
|
|
|
GST_TRACE_OBJECT (demuxer, "Handling buffer returned %s",
|
|
gst_flow_get_name (res));
|
|
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
gst_rs_demuxer_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|
{
|
|
GstRsDemuxer *demuxer = GST_RS_DEMUXER (parent);
|
|
gboolean res = FALSE;
|
|
|
|
switch (GST_EVENT_TYPE (event)) {
|
|
case GST_EVENT_SEGMENT:{
|
|
// TODO
|
|
res = TRUE;
|
|
gst_event_unref (event);
|
|
break;
|
|
}
|
|
case GST_EVENT_EOS:
|
|
GST_DEBUG_OBJECT (demuxer, "Got EOS");
|
|
demuxer_end_of_stream (demuxer->instance);
|
|
res = gst_pad_event_default (pad, parent, event);
|
|
break;
|
|
default:
|
|
res = gst_pad_event_default (pad, parent, event);
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
gst_rs_demuxer_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
|
|
{
|
|
GstRsDemuxer *demuxer = GST_RS_DEMUXER (parent);
|
|
gboolean res = FALSE;
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
case GST_QUERY_POSITION:{
|
|
GstFormat format;
|
|
|
|
gst_query_parse_position (query, &format, NULL);
|
|
if (format == GST_FORMAT_TIME) {
|
|
gint64 position;
|
|
|
|
if (demuxer_get_position (demuxer->instance, &position)) {
|
|
GST_DEBUG_OBJECT (demuxer, "Returning position %" GST_TIME_FORMAT,
|
|
GST_TIME_ARGS (position));
|
|
gst_query_set_position (query, format, position);
|
|
res = TRUE;
|
|
} else {
|
|
res = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case GST_QUERY_DURATION:{
|
|
GstFormat format;
|
|
|
|
gst_query_parse_duration (query, &format, NULL);
|
|
if (format == GST_FORMAT_TIME) {
|
|
gint64 duration;
|
|
|
|
if (demuxer_get_duration (demuxer->instance, &duration)) {
|
|
GST_DEBUG_OBJECT (demuxer, "Returning duration %" GST_TIME_FORMAT,
|
|
GST_TIME_ARGS (duration));
|
|
gst_query_set_duration (query, format, duration);
|
|
res = TRUE;
|
|
} else {
|
|
res = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
res = gst_pad_query_default (pad, parent, query);
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
gst_rs_demuxer_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|
{
|
|
GstRsDemuxer *demuxer = GST_RS_DEMUXER (parent);
|
|
gboolean res = FALSE;
|
|
|
|
switch (GST_EVENT_TYPE (event)) {
|
|
case GST_EVENT_SEEK:{
|
|
// TODO
|
|
g_assert_not_reached ();
|
|
gst_event_unref (event);
|
|
break;
|
|
}
|
|
default:
|
|
res = gst_pad_event_default (pad, parent, event);
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_rs_demuxer_change_state (GstElement * element, GstStateChange transition)
|
|
{
|
|
GstRsDemuxer *demuxer = GST_RS_DEMUXER (element);
|
|
GstStateChangeReturn result;
|
|
|
|
GST_DEBUG_OBJECT (demuxer, "Change state %s to %s",
|
|
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
|
|
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
demuxer->offset = 0;
|
|
gst_segment_init (&demuxer->segment, GST_FORMAT_TIME);
|
|
demuxer->group_id = gst_util_group_id_next ();
|
|
demuxer->segment_seqnum = gst_util_seqnum_next ();
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (result == GST_STATE_CHANGE_FAILURE)
|
|
return result;
|
|
result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
if (result == GST_STATE_CHANGE_FAILURE)
|
|
return result;
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_PAUSED_TO_READY:{
|
|
guint i;
|
|
|
|
/* Ignore stop failures */
|
|
GST_DEBUG_OBJECT (demuxer, "Stopping");
|
|
demuxer_stop (demuxer->instance);
|
|
|
|
gst_flow_combiner_clear (demuxer->flow_combiner);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (demuxer->srcpads); i++) {
|
|
if (demuxer->srcpads[i])
|
|
gst_element_remove_pad (GST_ELEMENT (demuxer), demuxer->srcpads[i]);
|
|
}
|
|
memset (demuxer->srcpads, 0, sizeof (demuxer->srcpads));
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
gst_rs_demuxer_loop (GstRsDemuxer * demuxer)
|
|
{
|
|
// TODO
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
void
|
|
gst_rs_demuxer_add_stream (GstRsDemuxer * demuxer, guint32 index,
|
|
GstCaps * caps, const gchar * stream_id)
|
|
{
|
|
GstPad *pad;
|
|
GstPadTemplate *templ;
|
|
GstEvent *event;
|
|
gchar *name, *full_stream_id;
|
|
|
|
g_assert (demuxer->srcpads[index] == NULL);
|
|
|
|
GST_DEBUG_OBJECT (demuxer,
|
|
"Adding stream %u with format %" GST_PTR_FORMAT " and stream id %s",
|
|
index, caps, stream_id);
|
|
|
|
templ =
|
|
gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (demuxer),
|
|
"src_%u");
|
|
|
|
name = g_strdup_printf ("src_%u", index);
|
|
pad = demuxer->srcpads[index] = gst_pad_new_from_template (templ, name);
|
|
g_free (name);
|
|
|
|
gst_pad_set_query_function (pad, gst_rs_demuxer_src_query);
|
|
gst_pad_set_event_function (pad, gst_rs_demuxer_src_event);
|
|
|
|
gst_pad_set_active (pad, TRUE);
|
|
|
|
full_stream_id =
|
|
gst_pad_create_stream_id (pad, GST_ELEMENT (demuxer), stream_id);
|
|
event = gst_event_new_stream_start (full_stream_id);
|
|
gst_event_set_group_id (event, demuxer->group_id);
|
|
g_free (full_stream_id);
|
|
gst_pad_push_event (pad, event);
|
|
|
|
event = gst_event_new_caps (caps);
|
|
gst_pad_push_event (pad, event);
|
|
|
|
event = gst_event_new_segment (&demuxer->segment);
|
|
gst_event_set_seqnum (event, demuxer->segment_seqnum);
|
|
gst_pad_push_event (pad, event);
|
|
|
|
gst_flow_combiner_add_pad (demuxer->flow_combiner, pad);
|
|
gst_element_add_pad (GST_ELEMENT (demuxer), pad);
|
|
}
|
|
|
|
void
|
|
gst_rs_demuxer_added_all_streams (GstRsDemuxer * demuxer)
|
|
{
|
|
GST_DEBUG_OBJECT (demuxer, "No more pads");
|
|
|
|
gst_element_no_more_pads (GST_ELEMENT (demuxer));
|
|
demuxer->group_id = gst_util_group_id_next ();
|
|
}
|
|
|
|
void
|
|
gst_rs_demuxer_stream_format_changed (GstRsDemuxer * demuxer, guint32 index,
|
|
GstCaps * caps)
|
|
{
|
|
GstEvent *event;
|
|
|
|
g_assert (demuxer->srcpads[index] != NULL);
|
|
|
|
GST_DEBUG_OBJECT (demuxer, "Format changed for stream %u: %" GST_PTR_FORMAT,
|
|
index, caps);
|
|
|
|
event = gst_event_new_caps (caps);
|
|
|
|
gst_pad_push_event (demuxer->srcpads[index], event);
|
|
}
|
|
|
|
void
|
|
gst_rs_demuxer_stream_eos (GstRsDemuxer * demuxer, guint32 index)
|
|
{
|
|
GstCaps *caps;
|
|
GstEvent *event;
|
|
|
|
g_assert (index == -1 || demuxer->srcpads[index] != NULL);
|
|
|
|
GST_DEBUG_OBJECT (demuxer, "EOS for stream %u", index);
|
|
|
|
event = gst_event_new_eos ();
|
|
if (index == -1) {
|
|
gint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (demuxer->srcpads); i++) {
|
|
if (demuxer->srcpads[i])
|
|
gst_pad_push_event (demuxer->srcpads[i], gst_event_ref (event));
|
|
}
|
|
|
|
gst_event_unref (event);
|
|
} else {
|
|
gst_pad_push_event (demuxer->srcpads[index], event);
|
|
}
|
|
}
|
|
|
|
|
|
GstFlowReturn
|
|
gst_rs_demuxer_stream_push_buffer (GstRsDemuxer * demuxer, guint32 index,
|
|
GstBuffer * buffer)
|
|
{
|
|
GstFlowReturn res = GST_FLOW_OK;
|
|
|
|
g_assert (demuxer->srcpads[index] != NULL);
|
|
|
|
GST_DEBUG_OBJECT (demuxer, "Pushing buffer %p for pad %u", buffer, index);
|
|
res = gst_pad_push (demuxer->srcpads[index], buffer);
|
|
GST_DEBUG_OBJECT (demuxer, "Pushed buffer returned: %s",
|
|
gst_flow_get_name (res));
|
|
res = gst_flow_combiner_update_flow (demuxer->flow_combiner, res);
|
|
GST_DEBUG_OBJECT (demuxer, "Combined return: %s", gst_flow_get_name (res));
|
|
|
|
return res;
|
|
}
|
|
|
|
void
|
|
gst_rs_demuxer_remove_all_streams (GstRsDemuxer * demuxer)
|
|
{
|
|
guint i;
|
|
|
|
GST_DEBUG_OBJECT (demuxer, "Removing all streams");
|
|
gst_flow_combiner_clear (demuxer->flow_combiner);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (demuxer->srcpads); i++) {
|
|
if (demuxer->srcpads[i])
|
|
gst_element_remove_pad (GST_ELEMENT (demuxer), demuxer->srcpads[i]);
|
|
}
|
|
memset (demuxer->srcpads, 0, sizeof (demuxer->srcpads));
|
|
}
|
|
|
|
static gpointer
|
|
gst_rs_demuxer_init_class (gpointer data)
|
|
{
|
|
demuxers = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
GST_DEBUG_CATEGORY_INIT (gst_rs_demuxer_debug, "rsdemux", 0,
|
|
"Rust demuxer base class");
|
|
|
|
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
|
|
}
|
|
|
|
gboolean
|
|
gst_rs_demuxer_register (GstPlugin * plugin, const gchar * name,
|
|
const gchar * long_name, const gchar * description,
|
|
const gchar * classification, const gchar * author, GstRank rank,
|
|
void *create_instance, GstCaps * input_caps, GstCaps * output_caps)
|
|
{
|
|
GOnce gonce = G_ONCE_INIT;
|
|
GTypeInfo type_info = {
|
|
sizeof (GstRsDemuxerClass),
|
|
NULL,
|
|
NULL,
|
|
(GClassInitFunc) gst_rs_demuxer_class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof (GstRsDemuxer),
|
|
0,
|
|
(GInstanceInitFunc) gst_rs_demuxer_init
|
|
};
|
|
GType type;
|
|
gchar *type_name;
|
|
ElementData *data;
|
|
|
|
g_once (&gonce, gst_rs_demuxer_init_class, NULL);
|
|
|
|
GST_DEBUG ("Registering for %" GST_PTR_FORMAT ": %s", plugin, name);
|
|
GST_DEBUG (" long name: %s", long_name);
|
|
GST_DEBUG (" description: %s", description);
|
|
GST_DEBUG (" classification: %s", classification);
|
|
GST_DEBUG (" author: %s", author);
|
|
GST_DEBUG (" rank: %d", rank);
|
|
GST_DEBUG (" input caps: %" GST_PTR_FORMAT, input_caps);
|
|
GST_DEBUG (" output caps: %" GST_PTR_FORMAT, output_caps);
|
|
|
|
data = g_new0 (ElementData, 1);
|
|
data->long_name = g_strdup (long_name);
|
|
data->description = g_strdup (description);
|
|
data->classification = g_strdup (classification);
|
|
data->author = g_strdup (author);
|
|
data->create_instance = create_instance;
|
|
data->input_caps = gst_caps_ref (input_caps);
|
|
data->output_caps = gst_caps_ref (output_caps);
|
|
|
|
type_name = g_strconcat ("RsDemuxer-", name, NULL);
|
|
type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &type_info, 0);
|
|
g_free (type_name);
|
|
|
|
g_hash_table_insert (demuxers, GSIZE_TO_POINTER (type), data);
|
|
|
|
if (!gst_element_register (plugin, name, rank, type))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|