/* GStreamer * * Copyright (C) 2014 Samsung Electronics. All rights reserved. * Author: Thiago Santos * * Copyright (C) 2021-2022 Centricular Ltd * Author: Edward Hervey * Author: Jan Schmidt * * 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. */ #ifndef _GST_ADAPTIVE_DEMUX_H_ #define _GST_ADAPTIVE_DEMUX_H_ #include #include #include #include "downloadhelper.h" #include "downloadrequest.h" #include "gstadaptivedemuxutils.h" G_BEGIN_DECLS #define GST_TYPE_ADAPTIVE_DEMUX \ (gst_adaptive_demux_ng_get_type()) #define GST_ADAPTIVE_DEMUX(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemux)) #define GST_ADAPTIVE_DEMUX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass)) #define GST_ADAPTIVE_DEMUX_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_ADAPTIVE_DEMUX,GstAdaptiveDemuxClass)) #define GST_IS_ADAPTIVE_DEMUX(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADAPTIVE_DEMUX)) #define GST_IS_ADAPTIVE_DEMUX_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADAPTIVE_DEMUX)) #define GST_ADAPTIVE_DEMUX_CAST(obj) ((GstAdaptiveDemux *)obj) #define GST_TYPE_ADAPTIVE_DEMUX2_STREAM \ (gst_adaptive_demux2_stream_get_type()) #define GST_ADAPTIVE_DEMUX2_STREAM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADAPTIVE_DEMUX2_STREAM,GstAdaptiveDemux2Stream)) #define GST_ADAPTIVE_DEMUX2_STREAM_CAST(obj) ((GstAdaptiveDemux2Stream *)obj) typedef struct _GstAdaptiveDemux2Stream GstAdaptiveDemux2Stream; typedef GstObjectClass GstAdaptiveDemux2StreamClass; /** * GST_ADAPTIVE_DEMUX_SINK_NAME: * * The name of the templates for the sink pad. */ #define GST_ADAPTIVE_DEMUX_SINK_NAME "sink" /** * GST_ADAPTIVE_DEMUX_SINK_PAD: * @obj: a #GstAdaptiveDemux * * Gives the pointer to the sink #GstPad object of the element. */ #define GST_ADAPTIVE_DEMUX_SINK_PAD(obj) (((GstAdaptiveDemux *) (obj))->sinkpad) #define GST_ADAPTIVE_DEMUX_IN_TRICKMODE_KEY_UNITS(obj) ((((GstAdaptiveDemux*)(obj))->segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) == GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) #define GST_ADAPTIVE_DEMUX2_STREAM_NEED_HEADER(obj) (((GstAdaptiveDemux2Stream *) (obj))->need_header) /** * GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME: * * Name of the ELEMENT type messages posted by dashdemux with statistics. * * Since: 1.6 */ #define GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME "adaptive-streaming-statistics" #define GST_ELEMENT_ERROR_FROM_ERROR(el, msg, err) G_STMT_START { \ gchar *__dbg = g_strdup_printf ("%s: %s", msg, err->message); \ GST_WARNING_OBJECT (el, "error: %s", __dbg); \ gst_element_message_full (GST_ELEMENT(el), GST_MESSAGE_ERROR, \ err->domain, err->code, \ NULL, __dbg, __FILE__, GST_FUNCTION, __LINE__); \ g_clear_error (&err); \ } G_STMT_END /* DEPRECATED */ #define GST_ADAPTIVE_DEMUX_FLOW_END_OF_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_1 /* Current fragment download should be aborted and restarted. The parent class * will call ::update_fragment_info() again to get the updated information. */ #define GST_ADAPTIVE_DEMUX_FLOW_RESTART_FRAGMENT GST_FLOW_CUSTOM_SUCCESS_2 /* The live stream has lost synchronization and the demuxer needs to be resetted */ #define GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC GST_FLOW_CUSTOM_SUCCESS_2 + 1 typedef enum _GstAdaptiveDemux2StreamState GstAdaptiveDemux2StreamState; typedef struct _GstAdaptiveDemux2StreamFragment GstAdaptiveDemux2StreamFragment; typedef struct _GstAdaptiveDemuxTrack GstAdaptiveDemuxTrack; typedef struct _GstAdaptiveDemuxPeriod GstAdaptiveDemuxPeriod; typedef struct _GstAdaptiveDemux GstAdaptiveDemux; typedef struct _GstAdaptiveDemuxClass GstAdaptiveDemuxClass; typedef struct _GstAdaptiveDemuxPrivate GstAdaptiveDemuxPrivate; struct _GstAdaptiveDemux2StreamFragment { /* The period-local stream time for the given fragment. */ GstClockTimeDiff stream_time; GstClockTime duration; gchar *uri; gint64 range_start; gint64 range_end; /* when chunked downloading is used, may be be updated need_another_chunk() */ gint chunk_size; /* when headers are needed */ gchar *header_uri; gint64 header_range_start; gint64 header_range_end; /* when index is needed */ gchar *index_uri; gint64 index_range_start; gint64 index_range_end; gboolean finished; }; struct _GstAdaptiveDemuxTrack { gint ref_count; /* Demux */ GstAdaptiveDemux *demux; /* Stream type */ GstStreamType type; /* Stream flags */ GstStreamFlags flags; /* Unique identifier */ gchar *stream_id; /* Period number this track belongs * to (MAXINT if not assigned to a period yet) */ guint period_num; /* Unique identifier of the internal stream produced * by parsebin for the Stream this track comes from */ gchar *upstream_stream_id; /* Generic *elementary stream* caps */ GstCaps *generic_caps; /* Generic metadata */ GstTagList *tags; /* The stream object */ GstStream *stream_object; /* If TRUE, this track should be filled */ gboolean selected; /* If TRUE, this track is currently being outputted */ gboolean active; /* If TRUE, it is no longer selected but still being outputted. */ gboolean draining; /* FIXME : Replace by actual track element */ GstElement *element; /* The level at which 100% buffering is achieved */ GstClockTime buffering_threshold; /* The sinkpad receives parsed elementary stream */ GstPad *sinkpad; /* The pending parsebin source pad (used in case streams from parsebin get updated) (ref taken) */ GstPad *pending_srcpad; /* Data storage */ GstQueueArray *queue; /* Sticky event storage for this track */ GstEventStore sticky_events; /* ============== */ /* Input tracking */ /* The track received EOS */ gboolean eos; /* Level to wait until download can commence */ GstClockTime waiting_del_level; /* Input segment and time (in running time) */ GstSegment input_segment; GstClockTimeDiff input_time; GstClockTimeDiff lowest_input_time; guint64 input_segment_seqnum; /* ================= */ /* Contents tracking */ /* Current level of queue in bytes and time */ guint64 level_bytes; GstClockTime level_time; /* =============== */ /* Output tracking */ /* Is the output thread waiting for data on this track ? */ gboolean waiting_add; /* If TRUE, the next pending GstSegment running time should be updated to the * time stored in update_next_segment_run_ts */ gboolean update_next_segment; /* Output segment and time (in running time) */ GstSegment output_segment; GstClockTimeDiff output_time; /* Track position and duration for emitting gap * events */ GstClockTime gap_position; GstClockTime gap_duration; /* Next running time position pending in queue */ GstClockTimeDiff next_position; /* If the next output buffer should be marked discont */ gboolean output_discont; }; enum _GstAdaptiveDemux2StreamState { GST_ADAPTIVE_DEMUX2_STREAM_STATE_STOPPED, /* Stream was stopped */ GST_ADAPTIVE_DEMUX2_STREAM_STATE_RESTART, /* Stream stopped but needs restart logic */ GST_ADAPTIVE_DEMUX2_STREAM_STATE_START_FRAGMENT, GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_LIVE, GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_OUTPUT_SPACE, GST_ADAPTIVE_DEMUX2_STREAM_STATE_WAITING_MANIFEST_UPDATE, GST_ADAPTIVE_DEMUX2_STREAM_STATE_DOWNLOADING, GST_ADAPTIVE_DEMUX2_STREAM_STATE_EOS, GST_ADAPTIVE_DEMUX2_STREAM_STATE_ERRORED }; struct _GstAdaptiveDemux2Stream { GstObject object; /* FIXME : transition to gstobject->parent */ GstAdaptiveDemux *demux; /* The period to which the stream belongs, set when adding the stream to the * demuxer */ GstAdaptiveDemuxPeriod *period; /* The tracks this stream targets */ GList *tracks; /* The internal parsebin, forward data to track */ GstElement *parsebin; GstPad *parsebin_sink; gulong pad_added_id, pad_removed_id; GstSegment parse_segment; /* TRUE if the current stream GstSegment should be sent downstream */ gboolean send_segment; /* TRUE if the stream GstSegment requires recalculation (from demuxer segment) */ gboolean compute_segment; /* first_and_live applies to compute_segment */ gboolean first_and_live; /* When restarting, what is the target position (in demux segment) to * begin at */ GstClockTime start_position; /* Track the current position (in demux segment) of the current fragment */ GstClockTime current_position; GstCaps *pending_caps; GstTagList *pending_tags; GList *pending_events; GstFlowReturn last_ret; GError *last_error; gboolean discont; /* download tooling */ gboolean need_header; gboolean need_index; gboolean downloading_header; gboolean downloading_index; /* persistent, reused download request for fragment data */ DownloadRequest *download_request; GstAdaptiveDemux2StreamState state; guint pending_cb_id; gboolean download_active; guint last_status_code; gboolean pending_tracks; /* if we need to discover tracks dynamically for this stream */ gboolean download_finished; gboolean cancelled; gboolean replaced; /* replaced in a bitrate switch (used with cancelled) */ gboolean starting_fragment; gboolean first_fragment_buffer; gint64 download_start_time; gint64 download_total_bytes; gint64 download_end_offset; guint64 current_download_rate; /* bitrate of the previous fragment (pre-queue2) */ guint64 last_bitrate; /* Total last download time, from request to completion */ GstClockTime last_download_time; /* Average for the last fragments */ guint64 moving_bitrate; guint moving_index; guint64 *fragment_bitrates; GstAdaptiveDemux2StreamFragment fragment; guint download_error_count; /* Last collection provided by parsebin */ GstStreamCollection *stream_collection; /* OR'd set of stream types in this stream */ GstStreamType stream_type; }; /** * GstAdaptiveDemuxPeriod: * * The opaque #GstAdaptiveDemuxPeriod data structure. */ struct _GstAdaptiveDemuxPeriod { gint ref_count; GstAdaptiveDemux *demux; /* TRUE if the streams of this period were prepared and can be started */ gboolean prepared; /* TRUE if there is another period after this one */ gboolean has_next_period; /* An increasing unique identifier for the period. * * Note: unrelated to dash period id (which can be identical across * periods) */ guint period_num; /* The list of GstAdaptiveDemux2Stream (ref hold) */ GList *streams; /* Current collection */ GstStreamCollection *collection; /* List of available GstAdaptiveDemuxTrack (ref hold) */ GList *tracks; /* Whether tracks were changed and need re-matching against outputs */ gboolean tracks_changed; }; /** * GstAdaptiveDemux: * * The opaque #GstAdaptiveDemux data structure. */ struct _GstAdaptiveDemux { /*< private >*/ GstBin bin; gint running; /*< protected >*/ GstPad *sinkpad; DownloadHelper *download_helper; /* Protected by TRACKS_LOCK */ /* Period used for output */ GstAdaptiveDemuxPeriod *output_period; /* Period used for input */ GstAdaptiveDemuxPeriod *input_period; GstSegment segment; gdouble instant_rate_multiplier; /* 1.0 by default, or from instant-rate seek */ gchar *manifest_uri; gchar *manifest_base_uri; /* Properties */ gfloat bandwidth_target_ratio; /* ratio of the available bitrate to use */ guint connection_speed; /* Available / bandwidth to use set by the application */ guint min_bitrate; /* Minimum bitrate to choose */ guint max_bitrate; /* Maximum bitrate to choose */ guint current_download_rate; /* Current estimate of download bitrate */ /* Buffering levels */ GstClockTime max_buffering_time; GstClockTime buffering_high_watermark_time; GstClockTime buffering_low_watermark_time; gdouble buffering_high_watermark_fragments; gdouble buffering_low_watermark_fragments; /* video/audio buffer level as minimum of the appropriate streams */ GstClockTime current_level_time_video; GstClockTime current_level_time_audio; gboolean have_group_id; guint group_id; guint next_stream_id; /* Realtime clock */ GstAdaptiveDemuxClock *realtime_clock; /* < private > */ GstAdaptiveDemuxPrivate *priv; }; /** * GstAdaptiveDemuxClass: * */ struct _GstAdaptiveDemuxClass { /*< private >*/ GstBinClass bin_class; /*< public >*/ /** * process_manifest: Parse the manifest * @demux: #GstAdaptiveDemux * @manifest: the manifest to be parsed * * Parse the manifest and add the created streams using * gst_adaptive_demux2_stream_new() * * Returns: %TRUE if successful */ gboolean (*process_manifest) (GstAdaptiveDemux * demux, GstBuffer * manifest); /** * get_manifest_update_interval: * @demux: #GstAdaptiveDemux * * Used during live streaming, the subclass should return the interval * between successive manifest updates * * Returns: the update interval in microseconds */ gint64 (*get_manifest_update_interval) (GstAdaptiveDemux * demux); /** * update_manifest: * @demux: #GstAdaptiveDemux * * During live streaming, this will be called for the subclass to update its * manifest with the new version. By default it fetches the manifest URI * and passes it to GstAdaptiveDemux::update_manifest_data(). * * Returns: #GST_FLOW_OK is all succeeded, #GST_FLOW_EOS if the stream ended * or #GST_FLOW_ERROR if an error happened */ GstFlowReturn (*update_manifest) (GstAdaptiveDemux * demux); /** * update_manifest_data: * @demux: #GstAdaptiveDemux * @buf: Downloaded manifest data * * During live streaming, this will be called for the subclass to update its * manifest with the new version * * Returns: #GST_FLOW_OK is all succeeded, #GST_FLOW_EOS if the stream ended * or #GST_FLOW_ERROR if an error happened */ GstFlowReturn (*update_manifest_data) (GstAdaptiveDemux * demux, GstBuffer * buf); gboolean (*is_live) (GstAdaptiveDemux * demux); GstClockTime (*get_duration) (GstAdaptiveDemux * demux); /** * reset: * @demux: #GstAdaptiveDemux * * Reset the internal state of the subclass, getting ready to restart with * a new stream afterwards */ void (*reset) (GstAdaptiveDemux * demux); /** * seek: * @demux: #GstAdaptiveDemux * @seek: a seek #GstEvent * * The demuxer should seek on all its streams to the specified position * in the seek event * * Returns: %TRUE if successful */ gboolean (*seek) (GstAdaptiveDemux * demux, GstEvent * seek); /** * has_next_period: * @demux: #GstAdaptiveDemux * * Checks if there is a next period following the current one. * DASH can have multiple medias chained in its manifest, when one finishes * this function is called to verify if there is a new period to be played * in sequence. * * Returns: %TRUE if there is another period */ gboolean (*has_next_period) (GstAdaptiveDemux * demux); /** * advance_period: * @demux: #GstAdaptiveDemux * * Advances the manifest to the next period. New streams should be created * using gst_adaptive_demux2_stream_new(). */ void (*advance_period) (GstAdaptiveDemux * demux); GstFlowReturn (*stream_seek) (GstAdaptiveDemux2Stream * stream, gboolean forward, GstSeekFlags flags, GstClockTimeDiff target_ts, GstClockTimeDiff * final_ts); gboolean (*stream_has_next_fragment) (GstAdaptiveDemux2Stream * stream); GstFlowReturn (*stream_advance_fragment) (GstAdaptiveDemux2Stream * stream); /** * stream_can_start: * @demux: The #GstAdaptiveDemux * @stream: a #GstAdaptiveDemux2Stream * * Called before starting a @stream. sub-classes can return %FALSE if more * information is required before it can be started. Sub-classes will have to * call gst_adaptive_demux2_stream_start() when the stream should be started. */ gboolean (*stream_can_start) (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream); /** * stream_update_tracks: * @demux: The #GstAdaptiveDemux * @stream: A #GstAdaptiveDemux2Stream * * Called whenever the base class collected a @collection on a @stream which has * pending tracks to be created. Subclasses should override this if they * create streams without tracks. * * * create the various tracks by analyzing the @stream stream_collection * * Set the track upstream_stream_id to the corresponding stream_id from the collection */ void (*stream_update_tracks) (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream); /** * need_another_chunk: * @stream: #GstAdaptiveDemux2Stream * * If chunked downloading is used (chunk_size != 0) this is called once a * chunk is finished to decide whether more has to be downloaded or not. * May update chunk_size to a different value */ gboolean (*need_another_chunk) (GstAdaptiveDemux2Stream * stream); /** * stream_update_fragment_info: * @stream: #GstAdaptiveDemux2Stream * * Requests the stream to set the information about the current fragment to its * current fragment struct * * Returns: #GST_FLOW_OK in success, #GST_FLOW_ERROR on error and #GST_FLOW_EOS * if there is no fragment. */ GstFlowReturn (*stream_update_fragment_info) (GstAdaptiveDemux2Stream * stream); /** * stream_select_bitrate: * @stream: #GstAdaptiveDemux2Stream * @bitrate: the bitrate to select (in bytes per second) * * The stream should try to select the bitrate that is the greater, but not * greater than the requested bitrate. If it needs a codec change it should * create the new stream using gst_adaptive_demux2_stream_new(). If it only * needs a caps change it should set the new caps using * gst_adaptive_demux2_stream_set_caps(). * * Returns: %TRUE if the stream changed bitrate, %FALSE otherwise */ gboolean (*stream_select_bitrate) (GstAdaptiveDemux2Stream * stream, guint64 bitrate); /** * stream_get_fragment_waiting_time: * @stream: #GstAdaptiveDemux2Stream * * For live streams, requests how much time should be waited before starting * to download the fragment. This is useful to avoid downloading a fragment that * isn't available yet. * * Returns: The waiting time in as a #GstClockTime */ GstClockTime (*stream_get_fragment_waiting_time) (GstAdaptiveDemux2Stream * stream); /** * start_fragment: * @demux: #GstAdaptiveDemux * @stream: #GstAdaptiveDemux2Stream * * Notifies the subclass that the given stream is starting the download * of a new fragment. Can be used to reset/init internal state that is * needed before each fragment, like decryption engines. * * Returns: %TRUE if successful. */ gboolean (*start_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream); /** * finish_fragment: * @demux: #GstAdaptiveDemux * @stream: #GstAdaptiveDemux2Stream * * Notifies the subclass that a fragment download was finished. * It can be used to cleanup internal state after a fragment and * also push any pending data before moving to the next fragment. */ GstFlowReturn (*finish_fragment) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream); /** * data_received: * @demux: #GstAdaptiveDemux * @stream: #GstAdaptiveDemux2Stream * @buffer: #GstBuffer * * Notifies the subclass that a fragment chunk was downloaded. The subclass * can look at the data and modify/push data as desired. * * Returns: #GST_FLOW_OK if successful, #GST_FLOW_ERROR in case of error. */ GstFlowReturn (*data_received) (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream, GstBuffer * buffer); /** * get_live_seek_range: * @demux: #GstAdaptiveDemux * @start: pointer to put the start position allowed to seek to * @stop: pointer to put the stop position allowed to seek to * * Gets the allowed seek start and stop positions for the current live stream * * Return: %TRUE if successful */ gboolean (*get_live_seek_range) (GstAdaptiveDemux * demux, gint64 * start, gint64 * stop); /** * get_presentation_offset: * @demux: #GstAdaptiveDemux * @stream: #GstAdaptiveDemux2Stream * * Gets the delay to apply to @stream. * * Return: a #GstClockTime representing the (positive) time offset to apply to * @stream. */ GstClockTime (*get_presentation_offset) (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream); /** * get_period_start_time: * @demux: #GstAdaptiveDemux * * Gets the start time of the current period. Timestamps are resetting to 0 * after each period but we have to maintain a continuous stream and running * time so need to know the start time of the current period. * * Return: a #GstClockTime representing the start time of the currently * selected period. */ GstClockTime (*get_period_start_time) (GstAdaptiveDemux *demux); /** * requires_periodical_playlist_update: * @demux: #GstAdaptiveDemux * * Some adaptive streaming protocols allow the client to download * the playlist once and build up the fragment list based on the * current fragment metadata. For those protocols the demuxer * doesn't need to periodically refresh the playlist. This vfunc * is relevant only for live playback scenarios. * * Return: %TRUE if the playlist needs to be refreshed periodically by the demuxer. */ gboolean (*requires_periodical_playlist_update) (GstAdaptiveDemux * demux); }; GType gst_adaptive_demux_ng_get_type (void); GType gst_adaptive_demux2_stream_get_type (void); gboolean gst_adaptive_demux2_add_stream (GstAdaptiveDemux *demux, GstAdaptiveDemux2Stream *stream); gboolean gst_adaptive_demux2_stream_add_track (GstAdaptiveDemux2Stream *stream, GstAdaptiveDemuxTrack *track); GstAdaptiveDemuxTrack *gst_adaptive_demux_track_new (GstAdaptiveDemux *demux, GstStreamType type, GstStreamFlags flags, gchar *stream_id, GstCaps *caps, GstTagList *tags); GstAdaptiveDemuxTrack *gst_adaptive_demux_track_ref (GstAdaptiveDemuxTrack *track); void gst_adaptive_demux_track_unref (GstAdaptiveDemuxTrack *track); void gst_adaptive_demux2_stream_set_caps (GstAdaptiveDemux2Stream * stream, GstCaps * caps); void gst_adaptive_demux2_stream_set_tags (GstAdaptiveDemux2Stream * stream, GstTagList * tags); void gst_adaptive_demux2_stream_fragment_clear (GstAdaptiveDemux2StreamFragment * f); GstFlowReturn gst_adaptive_demux2_stream_push_buffer (GstAdaptiveDemux2Stream * stream, GstBuffer * buffer); GstFlowReturn gst_adaptive_demux2_stream_advance_fragment (GstAdaptiveDemux * demux, GstAdaptiveDemux2Stream * stream, GstClockTime duration); gboolean gst_adaptive_demux_start_new_period (GstAdaptiveDemux * demux); void gst_adaptive_demux2_stream_start (GstAdaptiveDemux2Stream * stream); void gst_adaptive_demux2_stream_queue_event (GstAdaptiveDemux2Stream * stream, GstEvent * event); gboolean gst_adaptive_demux2_stream_is_selected (GstAdaptiveDemux2Stream *stream); gboolean gst_adaptive_demux2_stream_is_running (GstAdaptiveDemux2Stream * stream); GstClockTime gst_adaptive_demux2_get_monotonic_time (GstAdaptiveDemux * demux); GDateTime *gst_adaptive_demux2_get_client_now_utc (GstAdaptiveDemux * demux); gboolean gst_adaptive_demux2_is_running (GstAdaptiveDemux * demux); GstClockTime gst_adaptive_demux2_get_qos_earliest_time (GstAdaptiveDemux *demux); GstCaps * gst_codec_utils_caps_from_iso_rfc6831 (gchar * codec); gdouble gst_adaptive_demux_play_rate (GstAdaptiveDemux *demux); G_END_DECLS #endif