+++ /dev/null
-/*
- * Farsight Voice+Video library
- *
- * Copyright 2007 Collabora Ltd,
- * Copyright 2007 Nokia Corporation
- * @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>.
- * Copyright 2007 Wim Taymans <wim.taymans@gmail.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-wfdrtpbuffer
- *
- * This element reorders and removes duplicate RTP packets as they are received
- * from a network source.
- *
- * The element needs the clock-rate of the RTP payload in order to estimate the
- * delay. This information is obtained either from the caps on the sink pad or,
- * when no caps are present, from the #GstWfdRTPBuffer::request-pt-map signal.
- * To clear the previous pt-map use the #GstWfdRTPBuffer::clear-pt-map signal.
- *
- * The wfdrtpbuffer will wait for missing packets up to a configurable time
- * limit using the #GstWfdRTPBuffer:latency property. Packets arriving too
- * late are considered to be lost packets. If the #GstWfdRTPBuffer:do-lost
- * property is set, lost packets will result in a custom serialized downstream
- * event of name GstRTPPacketLost. The lost packet events are usually used by a
- * depayloader or other element to create concealment data or some other logic
- * to gracefully handle the missing packets.
- *
- * The jitterbuffer will use the DTS (or PTS if no DTS is set) of the incomming
- * buffer and the rtptime inside the RTP packet to create a PTS on the outgoing
- * buffer.
- *
- * The jitterbuffer can also be configured to send early retransmission events
- * upstream by setting the #GstWfdRTPBuffer:do-retransmission property. In
- * this mode, the jitterbuffer tries to estimate when a packet should arrive and
- * sends a custom upstream event named GstRTPRetransmissionRequest when the
- * packet is considered late. The initial expected packet arrival time is
- * calculated as follows:
- *
- * - If seqnum N arrived at time T, seqnum N+1 is expected to arrive at
- * T + packet-spacing + #GstWfdRTPBuffer:rtx-delay. The packet spacing is
- * calculated from the DTS (or PTS is no DTS) of two consecutive RTP
- * packets with different rtptime.
- *
- * - If seqnum N0 arrived at time T0 and seqnum Nm arrived at time Tm,
- * seqnum Ni is expected at time Ti = T0 + i*(Tm - T0)/(Nm - N0). Any
- * previously scheduled timeout is overwritten.
- *
- * - If seqnum N arrived, all seqnum older than
- * N - #GstWfdRTPBuffer:rtx-delay-reorder are considered late
- * immediately. This is to request fast feedback for abonormally reorder
- * packets before any of the previous timeouts is triggered.
- *
- * A late packet triggers the GstRTPRetransmissionRequest custom upstream
- * event. After the initial timeout expires and the retransmission event is
- * sent, the timeout is scheduled for
- * T + #GstWfdRTPBuffer:rtx-retry-timeout. If the missing packet did not
- * arrive after #GstWfdRTPBuffer:rtx-retry-timeout, a new
- * GstRTPRetransmissionRequest is sent upstream and the timeout is rescheduled
- * again for T + #GstWfdRTPBuffer:rtx-retry-timeout. This repeats until
- * #GstWfdRTPBuffer:rtx-retry-period elapsed, at which point no further
- * retransmission requests are sent and the regular logic is performed to
- * schedule a lost packet as discussed above.
- *
- * This element acts as a live element and so adds #GstWfdRTPBuffer:latency
- * to the pipeline.
- *
- * This element will automatically be used inside rtpbin.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch-1.0 rtspsrc location=rtsp://192.168.1.133:8554/mpeg1or2AudioVideoTest ! wfdrtpbuffer ! rtpmpvdepay ! mpeg2dec ! xvimagesink
- * ]| Connect to a streaming server and decode the MPEG video. The jitterbuffer is
- * inserted into the pipeline to smooth out network jitter and to reorder the
- * out-of-order RTP packets.
- * </refsect2>
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <gst/rtp/gstrtpbuffer.h>
-
-#include "gstwfdrtpbuffer.h"
-#include "wfdrtpbuffer.h"
-#include "rtpstats.h"
-
-GST_DEBUG_CATEGORY (wfdrtpbuffer_debug);
-#define GST_CAT_DEFAULT (wfdrtpbuffer_debug)
-
-/* WfdRTPBuffer signals and args */
-enum
-{
- SIGNAL_REQUEST_PT_MAP,
- SIGNAL_CLEAR_PT_MAP,
- SIGNAL_HANDLE_SYNC,
- SIGNAL_ON_NPT_STOP,
- SIGNAL_SET_ACTIVE,
- LAST_SIGNAL
-};
-
-#define DEFAULT_LATENCY_MS 200
-#define DEFAULT_DROP_ON_LATENCY FALSE
-#define DEFAULT_TS_OFFSET 0
-#define DEFAULT_DO_LOST FALSE
-#define DEFAULT_MODE WFD_RTP_BUFFER_MODE_SLAVE
-#define DEFAULT_PERCENT 0
-#define DEFAULT_DO_RETRANSMISSION FALSE
-#define DEFAULT_RTX_DELAY -1
-#define DEFAULT_RTX_DELAY_REORDER 3
-#define DEFAULT_RTX_RETRY_TIMEOUT -1
-#define DEFAULT_RTX_RETRY_PERIOD -1
-
-#define DEFAULT_AUTO_RTX_DELAY (20 * GST_MSECOND)
-#define DEFAULT_AUTO_RTX_TIMEOUT (40 * GST_MSECOND)
-
-enum
-{
- PROP_0,
- PROP_LATENCY,
- PROP_DROP_ON_LATENCY,
- PROP_TS_OFFSET,
- PROP_DO_LOST,
- PROP_MODE,
- PROP_PERCENT,
- PROP_DO_RETRANSMISSION,
- PROP_RTX_DELAY,
- PROP_RTX_DELAY_REORDER,
- PROP_RTX_RETRY_TIMEOUT,
- PROP_RTX_RETRY_PERIOD,
- PROP_STATS,
- PROP_LAST
-};
-
-#define JBUF_LOCK(priv) (g_mutex_lock (&(priv)->jbuf_lock))
-
-#define JBUF_LOCK_CHECK(priv,label) G_STMT_START { \
- JBUF_LOCK (priv); \
- if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK)) \
- goto label; \
-} G_STMT_END
-#define JBUF_UNLOCK(priv) (g_mutex_unlock (&(priv)->jbuf_lock))
-
-#define JBUF_WAIT_TIMER(priv) G_STMT_START { \
- GST_DEBUG ("waiting timer"); \
- (priv)->waiting_timer = TRUE; \
- g_cond_wait (&(priv)->jbuf_timer, &(priv)->jbuf_lock); \
- (priv)->waiting_timer = FALSE; \
- GST_DEBUG ("waiting timer done"); \
-} G_STMT_END
-#define JBUF_SIGNAL_TIMER(priv) G_STMT_START { \
- if (G_UNLIKELY ((priv)->waiting_timer)) { \
- GST_DEBUG ("signal timer"); \
- g_cond_signal (&(priv)->jbuf_timer); \
- } \
-} G_STMT_END
-
-#define JBUF_WAIT_EVENT(priv,label) G_STMT_START { \
- GST_DEBUG ("waiting event"); \
- (priv)->waiting_event = TRUE; \
- g_cond_wait (&(priv)->jbuf_event, &(priv)->jbuf_lock); \
- (priv)->waiting_event = FALSE; \
- GST_DEBUG ("waiting event done"); \
- if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK)) \
- goto label; \
-} G_STMT_END
-#define JBUF_SIGNAL_EVENT(priv) G_STMT_START { \
- if (G_UNLIKELY ((priv)->waiting_event)) { \
- GST_DEBUG ("signal event"); \
- g_cond_signal (&(priv)->jbuf_event); \
- } \
-} G_STMT_END
-
-#define JBUF_WAIT_QUERY(priv,label) G_STMT_START { \
- GST_DEBUG ("waiting query"); \
- (priv)->waiting_query = TRUE; \
- g_cond_wait (&(priv)->jbuf_query, &(priv)->jbuf_lock); \
- (priv)->waiting_query = FALSE; \
- GST_DEBUG ("waiting query done"); \
- if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK)) \
- goto label; \
-} G_STMT_END
-#define JBUF_SIGNAL_QUERY(priv,res) G_STMT_START { \
- (priv)->last_query = res; \
- if (G_UNLIKELY ((priv)->waiting_query)) { \
- GST_DEBUG ("signal query"); \
- g_cond_signal (&(priv)->jbuf_query); \
- } \
-} G_STMT_END
-
-
-struct _GstWfdRTPBufferPrivate
-{
- GstPad *sinkpad, *srcpad;
- GstPad *rtcpsinkpad;
-
- WfdRTPBuffer *jbuf;
- GMutex jbuf_lock;
- gboolean waiting_timer;
- GCond jbuf_timer;
- gboolean waiting_event;
- GCond jbuf_event;
- gboolean waiting_query;
- GCond jbuf_query;
- gboolean last_query;
- gboolean discont;
- gboolean ts_discont;
- gboolean active;
- guint64 out_offset;
-
- gboolean timer_running;
- GThread *timer_thread;
-
- /* properties */
- guint latency_ms;
- guint64 latency_ns;
- gboolean drop_on_latency;
- gint64 ts_offset;
- gboolean do_lost;
- gboolean do_retransmission;
- gint rtx_delay;
- gint rtx_delay_reorder;
- gint rtx_retry_timeout;
- gint rtx_retry_period;
-
- /* the last seqnum we pushed out */
- guint32 last_popped_seqnum;
- /* the next expected seqnum we push */
- guint32 next_seqnum;
- /* last output time */
- GstClockTime last_out_time;
- /* last valid input timestamp and rtptime pair */
- GstClockTime ips_dts;
- guint64 ips_rtptime;
- GstClockTime packet_spacing;
-
- /* the next expected seqnum we receive */
- GstClockTime last_in_dts;
- guint32 last_in_seqnum;
- guint32 next_in_seqnum;
-
- GArray *timers;
-
- /* start and stop ranges */
- GstClockTime npt_start;
- GstClockTime npt_stop;
- guint64 ext_timestamp;
- guint64 last_elapsed;
- guint64 estimated_eos;
- GstClockID eos_id;
-
- /* state */
- gboolean eos;
- guint last_percent;
-
- /* clock rate and rtp timestamp offset */
- gint last_pt;
- gint32 clock_rate;
- gint64 clock_base;
- gint64 prev_ts_offset;
-
- /* when we are shutting down */
- GstFlowReturn srcresult;
- gboolean blocked;
-
- /* for sync */
- GstSegment segment;
- GstClockID clock_id;
- GstClockTime timer_timeout;
- guint16 timer_seqnum;
- /* the latency of the upstream peer, we have to take this into account when
- * synchronizing the buffers. */
- GstClockTime peer_latency;
- guint64 ext_rtptime;
- GstBuffer *last_sr;
-
- /* some accounting */
- guint64 num_late;
- guint64 num_duplicates;
- guint64 num_rtx_requests;
- guint64 num_rtx_success;
- guint64 num_rtx_failed;
- gdouble avg_rtx_num;
- guint64 avg_rtx_rtt;
-
- /* for the jitter */
- GstClockTime last_dts;
- guint64 last_rtptime;
- GstClockTime avg_jitter;
-};
-
-typedef enum
-{
- TIMER_TYPE_EXPECTED,
- TIMER_TYPE_LOST,
- TIMER_TYPE_DEADLINE,
- TIMER_TYPE_EOS
-} TimerType;
-
-typedef struct
-{
- guint idx;
- guint16 seqnum;
- guint num;
- TimerType type;
- GstClockTime timeout;
- GstClockTime duration;
- GstClockTime rtx_base;
- GstClockTime rtx_delay;
- GstClockTime rtx_retry;
- GstClockTime rtx_last;
- guint num_rtx_retry;
-} TimerData;
-
-#define GST_WFD_RTP_BUFFER_GET_PRIVATE(o) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_WFD_RTP_BUFFER, \
- GstWfdRTPBufferPrivate))
-
-static GstStaticPadTemplate gst_wfd_rtp_buffer_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp, "
- "clock-rate = (int) [ 1, 2147483647 ]"
- /* "payload = (int) , "
- * "encoding-name = (string) "
- */ )
- );
-
-static GstStaticPadTemplate gst_wfd_rtp_buffer_sink_rtcp_template =
-GST_STATIC_PAD_TEMPLATE ("sink_rtcp",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-static GstStaticPadTemplate gst_wfd_rtp_buffer_src_template =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp"
- /* "payload = (int) , "
- * "clock-rate = (int) , "
- * "encoding-name = (string) "
- */ )
- );
-
-static guint gst_wfd_rtp_buffer_signals[LAST_SIGNAL] = { 0 };
-
-#define gst_wfd_rtp_buffer_parent_class parent_class
-G_DEFINE_TYPE (GstWfdRTPBuffer, gst_wfd_rtp_buffer, GST_TYPE_ELEMENT);
-
-/* object overrides */
-static void gst_wfd_rtp_buffer_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_wfd_rtp_buffer_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec);
-static void gst_wfd_rtp_buffer_finalize (GObject * object);
-
-/* element overrides */
-static GstStateChangeReturn gst_wfd_rtp_buffer_change_state (GstElement
- * element, GstStateChange transition);
-static GstPad *gst_wfd_rtp_buffer_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name, const GstCaps * filter);
-static void gst_wfd_rtp_buffer_release_pad (GstElement * element,
- GstPad * pad);
-static GstClock *gst_wfd_rtp_buffer_provide_clock (GstElement * element);
-
-/* pad overrides */
-static GstCaps *gst_wfd_rtp_buffer_getcaps (GstPad * pad, GstCaps * filter);
-static GstIterator *gst_wfd_rtp_buffer_iterate_internal_links (GstPad * pad,
- GstObject * parent);
-
-/* sinkpad overrides */
-static gboolean gst_wfd_rtp_buffer_sink_event (GstPad * pad,
- GstObject * parent, GstEvent * event);
-static GstFlowReturn gst_wfd_rtp_buffer_chain (GstPad * pad,
- GstObject * parent, GstBuffer * buffer);
-
-static gboolean gst_wfd_rtp_buffer_sink_rtcp_event (GstPad * pad,
- GstObject * parent, GstEvent * event);
-static GstFlowReturn gst_wfd_rtp_buffer_chain_rtcp (GstPad * pad,
- GstObject * parent, GstBuffer * buffer);
-
-static gboolean gst_wfd_rtp_buffer_sink_query (GstPad * pad,
- GstObject * parent, GstQuery * query);
-
-/* srcpad overrides */
-static gboolean gst_wfd_rtp_buffer_src_event (GstPad * pad,
- GstObject * parent, GstEvent * event);
-static gboolean gst_wfd_rtp_buffer_src_activate_mode (GstPad * pad,
- GstObject * parent, GstPadMode mode, gboolean active);
-static void gst_wfd_rtp_buffer_loop (GstWfdRTPBuffer * jitterbuffer);
-static gboolean gst_wfd_rtp_buffer_src_query (GstPad * pad,
- GstObject * parent, GstQuery * query);
-
-static void
-gst_wfd_rtp_buffer_clear_pt_map (GstWfdRTPBuffer * jitterbuffer);
-static GstClockTime
-gst_wfd_rtp_buffer_set_active (GstWfdRTPBuffer * jitterbuffer,
- gboolean active, guint64 base_time);
-static void do_handle_sync (GstWfdRTPBuffer * jitterbuffer);
-
-static void unschedule_current_timer (GstWfdRTPBuffer * jitterbuffer);
-static void remove_all_timers (GstWfdRTPBuffer * jitterbuffer);
-
-static void wait_next_timeout (GstWfdRTPBuffer * jitterbuffer);
-
-static GstStructure *gst_wfd_rtp_buffer_create_stats (GstWfdRTPBuffer *
- jitterbuffer);
-
-static void
-gst_wfd_rtp_buffer_class_init (GstWfdRTPBufferClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
-
- g_type_class_add_private (klass, sizeof (GstWfdRTPBufferPrivate));
-
- gobject_class->finalize = gst_wfd_rtp_buffer_finalize;
-
- gobject_class->set_property = gst_wfd_rtp_buffer_set_property;
- gobject_class->get_property = gst_wfd_rtp_buffer_get_property;
-
- /**
- * GstWfdRTPBuffer:latency:
- *
- * The maximum latency of the jitterbuffer. Packets will be kept in the buffer
- * for at most this time.
- */
- g_object_class_install_property (gobject_class, PROP_LATENCY,
- g_param_spec_uint ("latency", "Buffer latency in ms",
- "Amount of ms to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- * GstWfdRTPBuffer:drop-on-latency:
- *
- * Drop oldest buffers when the queue is completely filled.
- */
- g_object_class_install_property (gobject_class, PROP_DROP_ON_LATENCY,
- g_param_spec_boolean ("drop-on-latency",
- "Drop buffers when maximum latency is reached",
- "Tells the jitterbuffer to never exceed the given latency in size",
- DEFAULT_DROP_ON_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- * GstWfdRTPBuffer:ts-offset:
- *
- * Adjust GStreamer output buffer timestamps in the jitterbuffer with offset.
- * This is mainly used to ensure interstream synchronisation.
- */
- g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
- g_param_spec_int64 ("ts-offset", "Timestamp Offset",
- "Adjust buffer timestamps with offset in nanoseconds", G_MININT64,
- G_MAXINT64, DEFAULT_TS_OFFSET,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GstWfdRTPBuffer:do-lost:
- *
- * Send out a GstRTPPacketLost event downstream when a packet is considered
- * lost.
- */
- g_object_class_install_property (gobject_class, PROP_DO_LOST,
- g_param_spec_boolean ("do-lost", "Do Lost",
- "Send an event downstream when a packet is lost", DEFAULT_DO_LOST,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GstWfdRTPBuffer:mode:
- *
- * Control the buffering and timestamping mode used by the jitterbuffer.
- */
- g_object_class_install_property (gobject_class, PROP_MODE,
- g_param_spec_enum ("mode", "Mode",
- "Control the buffering algorithm in use", WFD_TYPE_RTP_BUFFER_MODE,
- DEFAULT_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- * GstWfdRTPBuffer:percent:
- *
- * The percent of the jitterbuffer that is filled.
- */
- g_object_class_install_property (gobject_class, PROP_PERCENT,
- g_param_spec_int ("percent", "percent",
- "The buffer filled percent", 0, 100,
- 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- /**
- * GstWfdRTPBuffer:do-retransmission:
- *
- * Send out a GstRTPRetransmission event upstream when a packet is considered
- * late and should be retransmitted.
- *
- * Since: 1.2
- */
- g_object_class_install_property (gobject_class, PROP_DO_RETRANSMISSION,
- g_param_spec_boolean ("do-retransmission", "Do Retransmission",
- "Send retransmission events upstream when a packet is late",
- DEFAULT_DO_RETRANSMISSION,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GstWfdRTPBuffer:rtx-delay:
- *
- * When a packet did not arrive at the expected time, wait this extra amount
- * of time before sending a retransmission event.
- *
- * When -1 is used, the max jitter will be used as extra delay.
- *
- * Since: 1.2
- */
- g_object_class_install_property (gobject_class, PROP_RTX_DELAY,
- g_param_spec_int ("rtx-delay", "RTX Delay",
- "Extra time in ms to wait before sending retransmission "
- "event (-1 automatic)", -1, G_MAXINT, DEFAULT_RTX_DELAY,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- * GstWfdRTPBuffer:rtx-delay-reorder:
- *
- * Assume that a retransmission event should be sent when we see
- * this much packet reordering.
- *
- * When -1 is used, the value will be estimated based on observed packet
- * reordering.
- *
- * Since: 1.2
- */
- g_object_class_install_property (gobject_class, PROP_RTX_DELAY_REORDER,
- g_param_spec_int ("rtx-delay-reorder", "RTX Delay Reorder",
- "Sending retransmission event when this much reordering (-1 automatic)",
- -1, G_MAXINT, DEFAULT_RTX_DELAY_REORDER,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- * GstWfdRTPBuffer::rtx-retry-timeout:
- *
- * When no packet has been received after sending a retransmission event
- * for this time, retry sending a retransmission event.
- *
- * When -1 is used, the value will be estimated based on observed round
- * trip time.
- *
- * Since: 1.2
- */
- g_object_class_install_property (gobject_class, PROP_RTX_RETRY_TIMEOUT,
- g_param_spec_int ("rtx-retry-timeout", "RTX Retry Timeout",
- "Retry sending a transmission event after this timeout in "
- "ms (-1 automatic)", -1, G_MAXINT, DEFAULT_RTX_RETRY_TIMEOUT,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- * GstWfdRTPBuffer:rtx-retry-period:
- *
- * The amount of time to try to get a retransmission.
- *
- * When -1 is used, the value will be estimated based on the jitterbuffer
- * latency and the observed round trip time.
- *
- * Since: 1.2
- */
- g_object_class_install_property (gobject_class, PROP_RTX_RETRY_PERIOD,
- g_param_spec_int ("rtx-retry-period", "RTX Retry Period",
- "Try to get a retransmission for this many ms "
- "(-1 automatic)", -1, G_MAXINT, DEFAULT_RTX_RETRY_PERIOD,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- * GstWfdRTPBuffer:stats:
- *
- * Various jitterbuffer statistics. This property returns a GstStructure
- * with name application/x-rtp-jitterbuffer-stats with the following fields:
- *
- * "rtx-count" G_TYPE_UINT64 The number of retransmissions requested
- * "rtx-success-count" G_TYPE_UINT64 The number of successful retransmissions
- * "rtx-per-packet" G_TYPE_DOUBLE Average number of RTX per packet
- * "rtx-rtt" G_TYPE_UINT64 Average round trip time per RTX
- *
- * Since: 1.4
- */
- g_object_class_install_property (gobject_class, PROP_STATS,
- g_param_spec_boxed ("stats", "Statistics",
- "Various statistics", GST_TYPE_STRUCTURE,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GstWfdRTPBuffer::request-pt-map:
- * @buffer: the object which received the signal
- * @pt: the pt
- *
- * Request the payload type as #GstCaps for @pt.
- */
- gst_wfd_rtp_buffer_signals[SIGNAL_REQUEST_PT_MAP] =
- g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstWfdRTPBufferClass,
- request_pt_map), NULL, NULL, g_cclosure_marshal_generic,
- GST_TYPE_CAPS, 1, G_TYPE_UINT);
- /**
- * GstWfdRTPBuffer::handle-sync:
- * @buffer: the object which received the signal
- * @struct: a GstStructure containing sync values.
- *
- * Be notified of new sync values.
- */
- gst_wfd_rtp_buffer_signals[SIGNAL_HANDLE_SYNC] =
- g_signal_new ("handle-sync", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstWfdRTPBufferClass,
- handle_sync), NULL, NULL, g_cclosure_marshal_VOID__BOXED,
- G_TYPE_NONE, 1, GST_TYPE_STRUCTURE | G_SIGNAL_TYPE_STATIC_SCOPE);
-
- /**
- * GstWfdRTPBuffer::on-npt-stop:
- * @buffer: the object which received the signal
- *
- * Signal that the jitterbufer has pushed the RTP packet that corresponds to
- * the npt-stop position.
- */
- gst_wfd_rtp_buffer_signals[SIGNAL_ON_NPT_STOP] =
- g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstWfdRTPBufferClass,
- on_npt_stop), NULL, NULL, g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0, G_TYPE_NONE);
-
- /**
- * GstWfdRTPBuffer::clear-pt-map:
- * @buffer: the object which received the signal
- *
- * Invalidate the clock-rate as obtained with the
- * #GstWfdRTPBuffer::request-pt-map signal.
- */
- gst_wfd_rtp_buffer_signals[SIGNAL_CLEAR_PT_MAP] =
- g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GstWfdRTPBufferClass, clear_pt_map), NULL, NULL,
- g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
-
- /**
- * GstWfdRTPBuffer::set-active:
- * @buffer: the object which received the signal
- *
- * Start pushing out packets with the given base time. This signal is only
- * useful in buffering mode.
- *
- * Returns: the time of the last pushed packet.
- */
- gst_wfd_rtp_buffer_signals[SIGNAL_SET_ACTIVE] =
- g_signal_new ("set-active", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GstWfdRTPBufferClass, set_active), NULL, NULL,
- g_cclosure_marshal_generic, G_TYPE_UINT64, 2, G_TYPE_BOOLEAN,
- G_TYPE_UINT64);
-
- gstelement_class->change_state =
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_change_state);
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_request_new_pad);
- gstelement_class->release_pad =
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_release_pad);
- gstelement_class->provide_clock =
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_provide_clock);
-
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_wfd_rtp_buffer_src_template));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_wfd_rtp_buffer_sink_template));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_wfd_rtp_buffer_sink_rtcp_template));
-
- gst_element_class_set_static_metadata (gstelement_class,
- "The Wi-Fi Display RTP packet jitter-buffer", "Filter/Network/RTP",
- "A buffer that deals with network jitter and other transmission faults",
- "Taewan kim <taewan79.kim@samsung.com>, "
- "YeJin Cho <cho.yejin@samsung.com>,"
- "Sangkyu Park <sk1122.park@samsung.com>");
-
- klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_clear_pt_map);
- klass->set_active = GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_set_active);
-
- GST_DEBUG_CATEGORY_INIT
- (wfdrtpbuffer_debug, "wfdrtpbuffer", 0, "WFD RTP Jitter Buffer");
-}
-
-static void
-gst_wfd_rtp_buffer_init (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv;
-
- priv = GST_WFD_RTP_BUFFER_GET_PRIVATE (jitterbuffer);
- jitterbuffer->priv = priv;
-
- priv->latency_ms = DEFAULT_LATENCY_MS;
- priv->latency_ns = priv->latency_ms * GST_MSECOND;
- priv->drop_on_latency = DEFAULT_DROP_ON_LATENCY;
- priv->do_lost = DEFAULT_DO_LOST;
- priv->do_retransmission = DEFAULT_DO_RETRANSMISSION;
- priv->rtx_delay = DEFAULT_RTX_DELAY;
- priv->rtx_delay_reorder = DEFAULT_RTX_DELAY_REORDER;
- priv->rtx_retry_timeout = DEFAULT_RTX_RETRY_TIMEOUT;
- priv->rtx_retry_period = DEFAULT_RTX_RETRY_PERIOD;
-
- priv->last_dts = -1;
- priv->last_rtptime = -1;
- priv->avg_jitter = 0;
- priv->timers = g_array_new (FALSE, TRUE, sizeof (TimerData));
- priv->jbuf = wfd_rtp_buffer_new ();
- g_mutex_init (&priv->jbuf_lock);
- g_cond_init (&priv->jbuf_timer);
- g_cond_init (&priv->jbuf_event);
- g_cond_init (&priv->jbuf_query);
-
- /* reset skew detection initialy */
- wfd_rtp_buffer_reset_skew (priv->jbuf);
- wfd_rtp_buffer_set_delay (priv->jbuf, priv->latency_ns);
- wfd_rtp_buffer_set_buffering (priv->jbuf, FALSE);
- priv->active = TRUE;
-
- priv->srcpad =
- gst_pad_new_from_static_template (&gst_wfd_rtp_buffer_src_template,
- "src");
-
- gst_pad_set_activatemode_function (priv->srcpad,
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_src_activate_mode));
- gst_pad_set_query_function (priv->srcpad,
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_src_query));
- gst_pad_set_event_function (priv->srcpad,
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_src_event));
-
- priv->sinkpad =
- gst_pad_new_from_static_template (&gst_wfd_rtp_buffer_sink_template,
- "sink");
-
- gst_pad_set_chain_function (priv->sinkpad,
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_chain));
- gst_pad_set_event_function (priv->sinkpad,
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_sink_event));
- gst_pad_set_query_function (priv->sinkpad,
- GST_DEBUG_FUNCPTR (gst_wfd_rtp_buffer_sink_query));
-
- gst_element_add_pad (GST_ELEMENT (jitterbuffer), priv->srcpad);
- gst_element_add_pad (GST_ELEMENT (jitterbuffer), priv->sinkpad);
-
- GST_OBJECT_FLAG_SET (jitterbuffer, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
-}
-
-#define IS_DROPABLE(it) (((it)->type == ITEM_TYPE_BUFFER) || ((it)->type == ITEM_TYPE_LOST))
-
-#define ITEM_TYPE_BUFFER 0
-#define ITEM_TYPE_LOST 1
-#define ITEM_TYPE_EVENT 2
-#define ITEM_TYPE_QUERY 3
-
-static WfdRTPBufferItem *
-alloc_item (gpointer data, guint type, GstClockTime dts, GstClockTime pts,
- guint seqnum, guint count, guint rtptime)
-{
- WfdRTPBufferItem *item;
-
- item = g_slice_new (WfdRTPBufferItem);
- item->data = data;
- item->next = NULL;
- item->prev = NULL;
- item->type = type;
- item->dts = dts;
- item->pts = pts;
- item->seqnum = seqnum;
- item->count = count;
- item->rtptime = rtptime;
-
- return item;
-}
-
-static void
-free_item (WfdRTPBufferItem * item)
-{
- if (item->data && item->type != ITEM_TYPE_QUERY)
- gst_mini_object_unref (item->data);
- g_slice_free (WfdRTPBufferItem, item);
-}
-
-static void
-gst_wfd_rtp_buffer_finalize (GObject * object)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (object);
- priv = jitterbuffer->priv;
-
- g_array_free (priv->timers, TRUE);
- g_mutex_clear (&priv->jbuf_lock);
- g_cond_clear (&priv->jbuf_timer);
- g_cond_clear (&priv->jbuf_event);
- g_cond_clear (&priv->jbuf_query);
-
- wfd_rtp_buffer_flush (priv->jbuf, (GFunc) free_item, NULL);
- g_object_unref (priv->jbuf);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static GstIterator *
-gst_wfd_rtp_buffer_iterate_internal_links (GstPad * pad, GstObject * parent)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstPad *otherpad = NULL;
- GstIterator *it = NULL;
- GValue val = { 0, };
-
- jitterbuffer = GST_WFD_RTP_BUFFER_CAST (parent);
-
- if (pad == jitterbuffer->priv->sinkpad) {
- otherpad = jitterbuffer->priv->srcpad;
- } else if (pad == jitterbuffer->priv->srcpad) {
- otherpad = jitterbuffer->priv->sinkpad;
- } else if (pad == jitterbuffer->priv->rtcpsinkpad) {
- it = gst_iterator_new_single (GST_TYPE_PAD, NULL);
- }
-
- if (it == NULL) {
- g_value_init (&val, GST_TYPE_PAD);
- g_value_set_object (&val, otherpad);
- it = gst_iterator_new_single (GST_TYPE_PAD, &val);
- g_value_unset (&val);
- }
-
- return it;
-}
-
-static GstPad *
-create_rtcp_sink (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "creating RTCP sink pad");
-
- priv->rtcpsinkpad =
- gst_pad_new_from_static_template
- (&gst_wfd_rtp_buffer_sink_rtcp_template, "sink_rtcp");
- gst_pad_set_chain_function (priv->rtcpsinkpad,
- gst_wfd_rtp_buffer_chain_rtcp);
- gst_pad_set_event_function (priv->rtcpsinkpad,
- (GstPadEventFunction) gst_wfd_rtp_buffer_sink_rtcp_event);
- gst_pad_set_iterate_internal_links_function (priv->rtcpsinkpad,
- gst_wfd_rtp_buffer_iterate_internal_links);
- gst_pad_set_active (priv->rtcpsinkpad, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (jitterbuffer), priv->rtcpsinkpad);
-
- return priv->rtcpsinkpad;
-}
-
-static void
-remove_rtcp_sink (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "removing RTCP sink pad");
-
- gst_pad_set_active (priv->rtcpsinkpad, FALSE);
-
- gst_element_remove_pad (GST_ELEMENT_CAST (jitterbuffer), priv->rtcpsinkpad);
- priv->rtcpsinkpad = NULL;
-}
-
-static GstPad *
-gst_wfd_rtp_buffer_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name, const GstCaps * filter)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstElementClass *klass;
- GstPad *result;
- GstWfdRTPBufferPrivate *priv;
-
- g_return_val_if_fail (templ != NULL, NULL);
- g_return_val_if_fail (GST_IS_WFD_RTP_BUFFER (element), NULL);
-
- jitterbuffer = GST_WFD_RTP_BUFFER_CAST (element);
- priv = jitterbuffer->priv;
- klass = GST_ELEMENT_GET_CLASS (element);
-
- GST_DEBUG_OBJECT (element, "requesting pad %s", GST_STR_NULL (name));
-
- /* figure out the template */
- if (templ == gst_element_class_get_pad_template (klass, "sink_rtcp")) {
- if (priv->rtcpsinkpad != NULL)
- goto exists;
-
- result = create_rtcp_sink (jitterbuffer);
- } else
- goto wrong_template;
-
- return result;
-
- /* ERRORS */
-wrong_template:
- {
- g_warning ("wfdrtpbuffer: this is not our template");
- return NULL;
- }
-exists:
- {
- g_warning ("wfdrtpbuffer: pad already requested");
- return NULL;
- }
-}
-
-static void
-gst_wfd_rtp_buffer_release_pad (GstElement * element, GstPad * pad)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
-
- g_return_if_fail (GST_IS_WFD_RTP_BUFFER (element));
- g_return_if_fail (GST_IS_PAD (pad));
-
- jitterbuffer = GST_WFD_RTP_BUFFER_CAST (element);
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (element, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
-
- if (priv->rtcpsinkpad == pad) {
- remove_rtcp_sink (jitterbuffer);
- } else
- goto wrong_pad;
-
- return;
-
- /* ERRORS */
-wrong_pad:
- {
- g_warning ("wfdrtpbuffer: asked to release an unknown pad");
- return;
- }
-}
-
-static GstClock *
-gst_wfd_rtp_buffer_provide_clock (GstElement * element)
-{
- return gst_system_clock_obtain ();
-}
-
-static void
-gst_wfd_rtp_buffer_clear_pt_map (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- /* this will trigger a new pt-map request signal, FIXME, do something better. */
-
- JBUF_LOCK (priv);
- priv->clock_rate = -1;
- /* do not clear current content, but refresh state for new arrival */
- GST_DEBUG_OBJECT (jitterbuffer, "reset jitterbuffer");
- wfd_rtp_buffer_reset_skew (priv->jbuf);
- JBUF_UNLOCK (priv);
-}
-
-static GstClockTime
-gst_wfd_rtp_buffer_set_active (GstWfdRTPBuffer * jbuf, gboolean active,
- guint64 offset)
-{
- GstWfdRTPBufferPrivate *priv;
- GstClockTime last_out;
- WfdRTPBufferItem *item;
-
- priv = jbuf->priv;
-
- JBUF_LOCK (priv);
- GST_DEBUG_OBJECT (jbuf, "setting active %d with offset %" GST_TIME_FORMAT,
- active, GST_TIME_ARGS (offset));
-
- if (active != priv->active) {
- /* add the amount of time spent in paused to the output offset. All
- * outgoing buffers will have this offset applied to their timestamps in
- * order to make them arrive in time in the sink. */
- priv->out_offset = offset;
- GST_DEBUG_OBJECT (jbuf, "out offset %" GST_TIME_FORMAT,
- GST_TIME_ARGS (priv->out_offset));
- priv->active = active;
- JBUF_SIGNAL_EVENT (priv);
- }
- if (!active) {
- wfd_rtp_buffer_set_buffering (priv->jbuf, TRUE);
- }
- if ((item = wfd_rtp_buffer_peek (priv->jbuf))) {
- /* head buffer timestamp and offset gives our output time */
- last_out = item->dts + priv->ts_offset;
- } else {
- /* use last known time when the buffer is empty */
- last_out = priv->last_out_time;
- }
- JBUF_UNLOCK (priv);
-
- return last_out;
-}
-
-static GstCaps *
-gst_wfd_rtp_buffer_getcaps (GstPad * pad, GstCaps * filter)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
- GstPad *other;
- GstCaps *caps;
- GstCaps *templ;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (gst_pad_get_parent (pad));
- priv = jitterbuffer->priv;
-
- other = (pad == priv->srcpad ? priv->sinkpad : priv->srcpad);
-
- caps = gst_pad_peer_query_caps (other, filter);
-
- templ = gst_pad_get_pad_template_caps (pad);
- if (caps == NULL) {
- GST_DEBUG_OBJECT (jitterbuffer, "use template");
- caps = templ;
- } else {
- GstCaps *intersect;
-
- GST_DEBUG_OBJECT (jitterbuffer, "intersect with template");
-
- intersect = gst_caps_intersect (caps, templ);
- gst_caps_unref (caps);
- gst_caps_unref (templ);
-
- caps = intersect;
- }
- gst_object_unref (jitterbuffer);
-
- return caps;
-}
-
-/*
- * Must be called with JBUF_LOCK held
- */
-
-static gboolean
-gst_wfd_rtp_buffer_sink_parse_caps (GstWfdRTPBuffer * jitterbuffer,
- GstCaps * caps)
-{
- GstWfdRTPBufferPrivate *priv;
- GstStructure *caps_struct;
- guint val;
- GstClockTime tval;
-
- priv = jitterbuffer->priv;
-
- /* first parse the caps */
- caps_struct = gst_caps_get_structure (caps, 0);
-
- GST_DEBUG_OBJECT (jitterbuffer, "got caps");
-
- /* we need a clock-rate to convert the rtp timestamps to GStreamer time and to
- * measure the amount of data in the buffer */
- if (!gst_structure_get_int (caps_struct, "clock-rate", &priv->clock_rate))
- goto error;
-
- if (priv->clock_rate <= 0)
- goto wrong_rate;
-
- GST_DEBUG_OBJECT (jitterbuffer, "got clock-rate %d", priv->clock_rate);
-
- wfd_rtp_buffer_set_clock_rate (priv->jbuf, priv->clock_rate);
-
- /* The clock base is the RTP timestamp corrsponding to the npt-start value. We
- * can use this to track the amount of time elapsed on the sender. */
- if (gst_structure_get_uint (caps_struct, "clock-base", &val))
- priv->clock_base = val;
- else
- priv->clock_base = -1;
-
- priv->ext_timestamp = priv->clock_base;
-
- GST_DEBUG_OBJECT (jitterbuffer, "got clock-base %" G_GINT64_FORMAT,
- priv->clock_base);
-
- if (gst_structure_get_uint (caps_struct, "seqnum-base", &val)) {
- /* first expected seqnum, only update when we didn't have a previous base. */
- if (priv->next_in_seqnum == -1)
- priv->next_in_seqnum = val;
- if (priv->next_seqnum == -1) {
- priv->next_seqnum = val;
- JBUF_SIGNAL_EVENT (priv);
- }
- }
-
- GST_DEBUG_OBJECT (jitterbuffer, "got seqnum-base %d", priv->next_in_seqnum);
-
- /* the start and stop times. The seqnum-base corresponds to the start time. We
- * will keep track of the seqnums on the output and when we reach the one
- * corresponding to npt-stop, we emit the npt-stop-reached signal */
- if (gst_structure_get_clock_time (caps_struct, "npt-start", &tval))
- priv->npt_start = tval;
- else
- priv->npt_start = 0;
-
- if (gst_structure_get_clock_time (caps_struct, "npt-stop", &tval))
- priv->npt_stop = tval;
- else
- priv->npt_stop = -1;
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
- GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop));
-
- return TRUE;
-
- /* ERRORS */
-error:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "No clock-rate in caps!");
- return FALSE;
- }
-wrong_rate:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "Invalid clock-rate %d", priv->clock_rate);
- return FALSE;
- }
-}
-
-static void
-gst_wfd_rtp_buffer_flush_start (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- JBUF_LOCK (priv);
- /* mark ourselves as flushing */
- priv->srcresult = GST_FLOW_FLUSHING;
- GST_DEBUG_OBJECT (jitterbuffer, "Disabling pop on queue");
- /* this unblocks any waiting pops on the src pad task */
- JBUF_SIGNAL_EVENT (priv);
- JBUF_SIGNAL_QUERY (priv, FALSE);
- JBUF_UNLOCK (priv);
-}
-
-static void
-gst_wfd_rtp_buffer_flush_stop (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- JBUF_LOCK (priv);
- GST_DEBUG_OBJECT (jitterbuffer, "Enabling pop on queue");
- /* Mark as non flushing */
- priv->srcresult = GST_FLOW_OK;
- gst_segment_init (&priv->segment, GST_FORMAT_TIME);
- priv->last_popped_seqnum = -1;
- priv->last_out_time = -1;
- priv->next_seqnum = -1;
- priv->ips_rtptime = -1;
- priv->ips_dts = GST_CLOCK_TIME_NONE;
- priv->packet_spacing = 0;
- priv->next_in_seqnum = -1;
- priv->clock_rate = -1;
- priv->last_pt = -1;
- priv->eos = FALSE;
- priv->estimated_eos = -1;
- priv->last_elapsed = 0;
- priv->ext_timestamp = -1;
- priv->avg_jitter = 0;
- priv->last_dts = -1;
- priv->last_rtptime = -1;
- GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
- wfd_rtp_buffer_flush (priv->jbuf, (GFunc) free_item, NULL);
- wfd_rtp_buffer_disable_buffering (priv->jbuf, FALSE);
- wfd_rtp_buffer_reset_skew (priv->jbuf);
- remove_all_timers (jitterbuffer);
- JBUF_UNLOCK (priv);
-}
-
-static gboolean
-gst_wfd_rtp_buffer_src_activate_mode (GstPad * pad, GstObject * parent,
- GstPadMode mode, gboolean active)
-{
- gboolean result;
- GstWfdRTPBuffer *jitterbuffer = NULL;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (parent);
-
- switch (mode) {
- case GST_PAD_MODE_PUSH:
- if (active) {
- /* allow data processing */
- gst_wfd_rtp_buffer_flush_stop (jitterbuffer);
-
- /* start pushing out buffers */
- GST_DEBUG_OBJECT (jitterbuffer, "Starting task on srcpad");
- result = gst_pad_start_task (jitterbuffer->priv->srcpad,
- (GstTaskFunction) gst_wfd_rtp_buffer_loop, jitterbuffer, NULL);
- } else {
- /* make sure all data processing stops ASAP */
- gst_wfd_rtp_buffer_flush_start (jitterbuffer);
-
- /* NOTE this will hardlock if the state change is called from the src pad
- * task thread because we will _join() the thread. */
- GST_DEBUG_OBJECT (jitterbuffer, "Stopping task on srcpad");
- result = gst_pad_stop_task (pad);
- }
- break;
- default:
- result = FALSE;
- break;
- }
- return result;
-}
-
-static GstStateChangeReturn
-gst_wfd_rtp_buffer_change_state (GstElement * element,
- GstStateChange transition)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
- GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (element);
- priv = jitterbuffer->priv;
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- JBUF_LOCK (priv);
- /* reset negotiated values */
- priv->clock_rate = -1;
- priv->clock_base = -1;
- priv->peer_latency = 0;
- priv->last_pt = -1;
- /* block until we go to PLAYING */
- priv->blocked = TRUE;
- priv->timer_running = TRUE;
- priv->timer_thread =
- g_thread_new ("timer", (GThreadFunc) wait_next_timeout, jitterbuffer);
- JBUF_UNLOCK (priv);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- JBUF_LOCK (priv);
- /* unblock to allow streaming in PLAYING */
- priv->blocked = FALSE;
- JBUF_SIGNAL_EVENT (priv);
- JBUF_SIGNAL_TIMER (priv);
- JBUF_UNLOCK (priv);
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- /* we are a live element because we sync to the clock, which we can only
- * do in the PLAYING state */
- if (ret != GST_STATE_CHANGE_FAILURE)
- ret = GST_STATE_CHANGE_NO_PREROLL;
- break;
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- JBUF_LOCK (priv);
- /* block to stop streaming when PAUSED */
- priv->blocked = TRUE;
- unschedule_current_timer (jitterbuffer);
- JBUF_UNLOCK (priv);
- if (ret != GST_STATE_CHANGE_FAILURE)
- ret = GST_STATE_CHANGE_NO_PREROLL;
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- JBUF_LOCK (priv);
- gst_buffer_replace (&priv->last_sr, NULL);
- priv->timer_running = FALSE;
- unschedule_current_timer (jitterbuffer);
- JBUF_SIGNAL_TIMER (priv);
- JBUF_SIGNAL_QUERY (priv, FALSE);
- JBUF_UNLOCK (priv);
- g_thread_join (priv->timer_thread);
- priv->timer_thread = NULL;
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-static gboolean
-gst_wfd_rtp_buffer_src_event (GstPad * pad, GstObject * parent,
- GstEvent * event)
-{
- gboolean ret = TRUE;
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
-
- jitterbuffer = GST_WFD_RTP_BUFFER_CAST (parent);
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_LATENCY:
- {
- GstClockTime latency;
-
- gst_event_parse_latency (event, &latency);
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "configuring latency of %" GST_TIME_FORMAT, GST_TIME_ARGS (latency));
-
- JBUF_LOCK (priv);
- /* adjust the overall buffer delay to the total pipeline latency in
- * buffering mode because if downstream consumes too fast (because of
- * large latency or queues, we would start rebuffering again. */
- if (wfd_rtp_buffer_get_mode (priv->jbuf) ==
- WFD_RTP_BUFFER_MODE_BUFFER) {
- wfd_rtp_buffer_set_delay (priv->jbuf, latency);
- }
- JBUF_UNLOCK (priv);
-
- ret = gst_pad_push_event (priv->sinkpad, event);
- break;
- }
- default:
- ret = gst_pad_push_event (priv->sinkpad, event);
- break;
- }
-
- return ret;
-}
-
-/* handles and stores the event in the jitterbuffer, must be called with
- * LOCK */
-static gboolean
-queue_event (GstWfdRTPBuffer * jitterbuffer, GstEvent * event)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- WfdRTPBufferItem *item;
- gboolean head;
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_CAPS:
- {
- GstCaps *caps;
-
- gst_event_parse_caps (event, &caps);
- if (!gst_wfd_rtp_buffer_sink_parse_caps (jitterbuffer, caps))
- goto wrong_caps;
-
- break;
- }
- case GST_EVENT_SEGMENT:
- gst_event_copy_segment (event, &priv->segment);
-
- /* we need time for now */
- if (priv->segment.format != GST_FORMAT_TIME)
- goto newseg_wrong_format;
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "newsegment: %" GST_SEGMENT_FORMAT, &priv->segment);
- break;
- case GST_EVENT_EOS:
- priv->eos = TRUE;
- wfd_rtp_buffer_disable_buffering (priv->jbuf, TRUE);
- break;
- default:
- break;
- }
-
-
- GST_DEBUG_OBJECT (jitterbuffer, "adding event");
- item = alloc_item (event, ITEM_TYPE_EVENT, -1, -1, -1, 0, -1);
- wfd_rtp_buffer_insert (priv->jbuf, item, &head, NULL);
- if (head)
- JBUF_SIGNAL_EVENT (priv);
-
- return TRUE;
-
- /* ERRORS */
-wrong_caps:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "received invalid caps");
- gst_event_unref (event);
- return FALSE;
- }
-newseg_wrong_format:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "received non TIME newsegment");
- gst_event_unref (event);
- return FALSE;
- }
-}
-
-static gboolean
-gst_wfd_rtp_buffer_sink_event (GstPad * pad, GstObject * parent,
- GstEvent * event)
-{
- gboolean ret = TRUE;
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (parent);
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_START:
- ret = gst_pad_push_event (priv->srcpad, event);
- gst_wfd_rtp_buffer_flush_start (jitterbuffer);
- /* wait for the loop to go into PAUSED */
- gst_pad_pause_task (priv->srcpad);
- break;
- case GST_EVENT_FLUSH_STOP:
- ret = gst_pad_push_event (priv->srcpad, event);
- ret =
- gst_wfd_rtp_buffer_src_activate_mode (priv->srcpad, parent,
- GST_PAD_MODE_PUSH, TRUE);
- break;
- default:
- if (GST_EVENT_IS_SERIALIZED (event)) {
- /* serialized events go in the queue */
- JBUF_LOCK (priv);
- if (priv->srcresult != GST_FLOW_OK) {
- /* Errors in sticky event pushing are no problem and ignored here
- * as they will cause more meaningful errors during data flow.
- * For EOS events, that are not followed by data flow, we still
- * return FALSE here though.
- */
- if (!GST_EVENT_IS_STICKY (event) ||
- GST_EVENT_TYPE (event) == GST_EVENT_EOS)
- goto out_flow_error;
- }
- /* refuse more events on EOS */
- if (priv->eos)
- goto out_eos;
- ret = queue_event (jitterbuffer, event);
- JBUF_UNLOCK (priv);
- } else {
- /* non-serialized events are forwarded downstream immediately */
- ret = gst_pad_push_event (priv->srcpad, event);
- }
- break;
- }
- return ret;
-
- /* ERRORS */
-out_flow_error:
- {
- GST_DEBUG_OBJECT (jitterbuffer,
- "refusing event, we have a downstream flow error: %s",
- gst_flow_get_name (priv->srcresult));
- JBUF_UNLOCK (priv);
- gst_event_unref (event);
- return FALSE;
- }
-out_eos:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "refusing event, we are EOS");
- JBUF_UNLOCK (priv);
- gst_event_unref (event);
- return FALSE;
- }
-}
-
-static gboolean
-gst_wfd_rtp_buffer_sink_rtcp_event (GstPad * pad, GstObject * parent,
- GstEvent * event)
-{
- gboolean ret = TRUE;
- GstWfdRTPBuffer *jitterbuffer;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (parent);
-
- GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_START:
- gst_event_unref (event);
- break;
- case GST_EVENT_FLUSH_STOP:
- gst_event_unref (event);
- break;
- default:
- ret = gst_pad_event_default (pad, parent, event);
- break;
- }
-
- return ret;
-}
-
-/*
- * Must be called with JBUF_LOCK held, will release the LOCK when emiting the
- * signal. The function returns GST_FLOW_ERROR when a parsing error happened and
- * GST_FLOW_FLUSHING when the element is shutting down. On success
- * GST_FLOW_OK is returned.
- */
-static GstFlowReturn
-gst_wfd_rtp_buffer_get_clock_rate (GstWfdRTPBuffer * jitterbuffer,
- guint8 pt)
-{
- GValue ret = { 0 };
- GValue args[2] = { {0}, {0} };
- GstCaps *caps;
- gboolean res;
-
- g_value_init (&args[0], GST_TYPE_ELEMENT);
- g_value_set_object (&args[0], jitterbuffer);
- g_value_init (&args[1], G_TYPE_UINT);
- g_value_set_uint (&args[1], pt);
-
- g_value_init (&ret, GST_TYPE_CAPS);
- g_value_set_boxed (&ret, NULL);
-
- JBUF_UNLOCK (jitterbuffer->priv);
- g_signal_emitv (args, gst_wfd_rtp_buffer_signals[SIGNAL_REQUEST_PT_MAP], 0,
- &ret);
- JBUF_LOCK_CHECK (jitterbuffer->priv, out_flushing);
-
- g_value_unset (&args[0]);
- g_value_unset (&args[1]);
- caps = (GstCaps *) g_value_dup_boxed (&ret);
- g_value_unset (&ret);
- if (!caps)
- goto no_caps;
-
- res = gst_wfd_rtp_buffer_sink_parse_caps (jitterbuffer, caps);
- gst_caps_unref (caps);
-
- if (G_UNLIKELY (!res))
- goto parse_failed;
-
- return GST_FLOW_OK;
-
- /* ERRORS */
-no_caps:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "could not get caps");
- return GST_FLOW_ERROR;
- }
-out_flushing:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "we are flushing");
- return GST_FLOW_FLUSHING;
- }
-parse_failed:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "parse failed");
- return GST_FLOW_ERROR;
- }
-}
-
-/* call with jbuf lock held */
-static GstMessage *
-check_buffering_percent (GstWfdRTPBuffer * jitterbuffer, gint percent)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GstMessage *message = NULL;
-
- if (percent == -1)
- return NULL;
-
- /* Post a buffering message */
- if (priv->last_percent != percent) {
- priv->last_percent = percent;
- message =
- gst_message_new_buffering (GST_OBJECT_CAST (jitterbuffer), percent);
- gst_message_set_buffering_stats (message, GST_BUFFERING_LIVE, -1, -1, -1);
- }
-
- return message;
-}
-
-static GstClockTime
-apply_offset (GstWfdRTPBuffer * jitterbuffer, GstClockTime timestamp)
-{
- GstWfdRTPBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- if (timestamp == -1)
- return -1;
-
- /* apply the timestamp offset, this is used for inter stream sync */
- timestamp += priv->ts_offset;
- /* add the offset, this is used when buffering */
- timestamp += priv->out_offset;
-
- return timestamp;
-}
-
-static TimerData *
-find_timer (GstWfdRTPBuffer * jitterbuffer, TimerType type, guint16 seqnum)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- TimerData *timer = NULL;
- gint i, len;
-
- len = priv->timers->len;
- for (i = 0; i < len; i++) {
- TimerData *test = &g_array_index (priv->timers, TimerData, i);
- if (test->seqnum == seqnum && test->type == type) {
- timer = test;
- break;
- }
- }
- return timer;
-}
-
-static void
-unschedule_current_timer (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
-
- if (priv->clock_id) {
- GST_DEBUG_OBJECT (jitterbuffer, "unschedule current timer");
- gst_clock_id_unschedule (priv->clock_id);
- priv->clock_id = NULL;
- }
-}
-
-static GstClockTime
-get_timeout (GstWfdRTPBuffer * jitterbuffer, TimerData * timer)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GstClockTime test_timeout;
-
- if ((test_timeout = timer->timeout) == -1)
- return -1;
-
- if (timer->type != TIMER_TYPE_EXPECTED) {
- /* add our latency and offset to get output times. */
- test_timeout = apply_offset (jitterbuffer, test_timeout);
- test_timeout += priv->latency_ns;
- }
- return test_timeout;
-}
-
-static void
-recalculate_timer (GstWfdRTPBuffer * jitterbuffer, TimerData * timer)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
-
- if (priv->clock_id) {
- GstClockTime timeout = get_timeout (jitterbuffer, timer);
-
- GST_DEBUG ("%" GST_TIME_FORMAT " <> %" GST_TIME_FORMAT,
- GST_TIME_ARGS (timeout), GST_TIME_ARGS (priv->timer_timeout));
-
- if (timeout == -1 || timeout < priv->timer_timeout)
- unschedule_current_timer (jitterbuffer);
- }
-}
-
-static TimerData *
-add_timer (GstWfdRTPBuffer * jitterbuffer, TimerType type,
- guint16 seqnum, guint num, GstClockTime timeout, GstClockTime delay,
- GstClockTime duration)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- TimerData *timer;
- gint len;
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "add timer %d for seqnum %d to %" GST_TIME_FORMAT ", delay %"
- GST_TIME_FORMAT, type, seqnum, GST_TIME_ARGS (timeout),
- GST_TIME_ARGS (delay));
-
- len = priv->timers->len;
- g_array_set_size (priv->timers, len + 1);
- timer = &g_array_index (priv->timers, TimerData, len);
- timer->idx = len;
- timer->type = type;
- timer->seqnum = seqnum;
- timer->num = num;
- timer->timeout = timeout + delay;
- timer->duration = duration;
- if (type == TIMER_TYPE_EXPECTED) {
- timer->rtx_base = timeout;
- timer->rtx_delay = delay;
- timer->rtx_retry = 0;
- }
- timer->num_rtx_retry = 0;
- recalculate_timer (jitterbuffer, timer);
- JBUF_SIGNAL_TIMER (priv);
-
- return timer;
-}
-
-static void
-reschedule_timer (GstWfdRTPBuffer * jitterbuffer, TimerData * timer,
- guint16 seqnum, GstClockTime timeout, GstClockTime delay, gboolean reset)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- gboolean seqchange, timechange;
- guint16 oldseq;
-
- seqchange = timer->seqnum != seqnum;
- timechange = timer->timeout != timeout;
-
- if (!seqchange && !timechange)
- return;
-
- oldseq = timer->seqnum;
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "replace timer for seqnum %d->%d to %" GST_TIME_FORMAT,
- oldseq, seqnum, GST_TIME_ARGS (timeout + delay));
-
- timer->timeout = timeout + delay;
- timer->seqnum = seqnum;
- if (reset) {
- timer->rtx_base = timeout;
- timer->rtx_delay = delay;
- timer->rtx_retry = 0;
- }
- if (seqchange)
- timer->num_rtx_retry = 0;
-
- if (priv->clock_id) {
- /* we changed the seqnum and there is a timer currently waiting with this
- * seqnum, unschedule it */
- if (seqchange && priv->timer_seqnum == oldseq)
- unschedule_current_timer (jitterbuffer);
- /* we changed the time, check if it is earlier than what we are waiting
- * for and unschedule if so */
- else if (timechange)
- recalculate_timer (jitterbuffer, timer);
- }
-}
-
-static TimerData *
-set_timer (GstWfdRTPBuffer * jitterbuffer, TimerType type,
- guint16 seqnum, GstClockTime timeout)
-{
- TimerData *timer;
-
- /* find the seqnum timer */
- timer = find_timer (jitterbuffer, type, seqnum);
- if (timer == NULL) {
- timer = add_timer (jitterbuffer, type, seqnum, 0, timeout, 0, -1);
- } else {
- reschedule_timer (jitterbuffer, timer, seqnum, timeout, 0, FALSE);
- }
- return timer;
-}
-
-static void
-remove_timer (GstWfdRTPBuffer * jitterbuffer, TimerData * timer)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- guint idx;
-
- if (priv->clock_id && priv->timer_seqnum == timer->seqnum)
- unschedule_current_timer (jitterbuffer);
-
- idx = timer->idx;
- GST_DEBUG_OBJECT (jitterbuffer, "removed index %d", idx);
- g_array_remove_index_fast (priv->timers, idx);
- timer->idx = idx;
-}
-
-static void
-remove_all_timers (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GST_DEBUG_OBJECT (jitterbuffer, "removed all timers");
- g_array_set_size (priv->timers, 0);
- unschedule_current_timer (jitterbuffer);
-}
-
-/* we just received a packet with seqnum and dts.
- *
- * First check for old seqnum that we are still expecting. If the gap with the
- * current seqnum is too big, unschedule the timeouts.
- *
- * If we have a valid packet spacing estimate we can set a timer for when we
- * should receive the next packet.
- * If we don't have a valid estimate, we remove any timer we might have
- * had for this packet.
- */
-static void
-update_timers (GstWfdRTPBuffer * jitterbuffer, guint16 seqnum,
- GstClockTime dts, gboolean do_next_seqnum)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- TimerData *timer = NULL;
- gint i, len;
-
- /* go through all timers and unschedule the ones with a large gap, also find
- * the timer for the seqnum */
- len = priv->timers->len;
- for (i = 0; i < len; i++) {
- TimerData *test = &g_array_index (priv->timers, TimerData, i);
- gint gap;
-
- gap = gst_rtp_buffer_compare_seqnum (test->seqnum, seqnum);
-
- GST_DEBUG_OBJECT (jitterbuffer, "%d, %d, #%d<->#%d gap %d", i,
- test->type, test->seqnum, seqnum, gap);
-
- if (gap == 0) {
- GST_DEBUG ("found timer for current seqnum");
- /* the timer for the current seqnum */
- timer = test;
- /* when no retransmission, we can stop now, we only need to find the
- * timer for the current seqnum */
- if (!priv->do_retransmission)
- break;
- } else if (gap > priv->rtx_delay_reorder) {
- /* max gap, we exceeded the max reorder distance and we don't expect the
- * missing packet to be this reordered */
- if (test->num_rtx_retry == 0 && test->type == TIMER_TYPE_EXPECTED)
- reschedule_timer (jitterbuffer, test, test->seqnum, -1, 0, FALSE);
- }
- }
-
- do_next_seqnum = do_next_seqnum && priv->packet_spacing > 0
- && priv->do_retransmission;
-
- if (timer && timer->type != TIMER_TYPE_DEADLINE) {
- if (timer->num_rtx_retry > 0) {
- GstClockTime rtx_last, delay;
-
- /* we scheduled a retry for this packet and now we have it */
- priv->num_rtx_success++;
- /* all the previous retry attempts failed */
- priv->num_rtx_failed += timer->num_rtx_retry - 1;
- /* number of retries before receiving the packet */
- if (priv->avg_rtx_num == 0.0)
- priv->avg_rtx_num = timer->num_rtx_retry;
- else
- priv->avg_rtx_num = (timer->num_rtx_retry + 7 * priv->avg_rtx_num) / 8;
- /* calculate the delay between retransmission request and receiving this
- * packet, start with when we scheduled this timeout last */
- rtx_last = timer->rtx_last;
- if (dts != GST_CLOCK_TIME_NONE && dts > rtx_last) {
- /* we have a valid delay if this packet arrived after we scheduled the
- * request */
- delay = dts - rtx_last;
- if (priv->avg_rtx_rtt == 0)
- priv->avg_rtx_rtt = delay;
- else
- priv->avg_rtx_rtt = (delay + 7 * priv->avg_rtx_rtt) / 8;
- } else
- delay = 0;
-
- GST_LOG_OBJECT (jitterbuffer,
- "RTX success %" G_GUINT64_FORMAT ", failed %" G_GUINT64_FORMAT
- ", requests %" G_GUINT64_FORMAT ", dups %" G_GUINT64_FORMAT
- ", avg-num %g, delay %" GST_TIME_FORMAT ", avg-rtt %" GST_TIME_FORMAT,
- priv->num_rtx_success, priv->num_rtx_failed, priv->num_rtx_requests,
- priv->num_duplicates, priv->avg_rtx_num, GST_TIME_ARGS (delay),
- GST_TIME_ARGS (priv->avg_rtx_rtt));
-
- /* don't try to estimate the next seqnum because this is a retransmitted
- * packet and it probably did not arrive with the expected packet
- * spacing. */
- do_next_seqnum = FALSE;
- }
- }
-
- if (do_next_seqnum) {
- GstClockTime expected, delay;
-
- /* calculate expected arrival time of the next seqnum */
- expected = dts + priv->packet_spacing;
-
- if (priv->rtx_delay == -1) {
- if (priv->avg_jitter == 0)
- delay = DEFAULT_AUTO_RTX_DELAY;
- else
- /* jitter is in nanoseconds, 2x jitter is a good margin */
- delay = priv->avg_jitter * 2;
- } else {
- delay = priv->rtx_delay * GST_MSECOND;
- }
-
- /* and update/install timer for next seqnum */
- if (timer)
- reschedule_timer (jitterbuffer, timer, priv->next_in_seqnum, expected,
- delay, TRUE);
- else
- add_timer (jitterbuffer, TIMER_TYPE_EXPECTED, priv->next_in_seqnum, 0,
- expected, delay, priv->packet_spacing);
- } else if (timer && timer->type != TIMER_TYPE_DEADLINE) {
- /* if we had a timer, remove it, we don't know when to expect the next
- * packet. */
- remove_timer (jitterbuffer, timer);
- }
-}
-
-static void
-calculate_packet_spacing (GstWfdRTPBuffer * jitterbuffer, guint32 rtptime,
- GstClockTime dts)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
-
- /* we need consecutive seqnums with a different
- * rtptime to estimate the packet spacing. */
- if (priv->ips_rtptime != rtptime) {
- /* rtptime changed, check dts diff */
- if (priv->ips_dts != -1 && dts != -1 && dts > priv->ips_dts) {
- priv->packet_spacing = dts - priv->ips_dts;
- GST_DEBUG_OBJECT (jitterbuffer,
- "new packet spacing %" GST_TIME_FORMAT,
- GST_TIME_ARGS (priv->packet_spacing));
- }
- priv->ips_rtptime = rtptime;
- priv->ips_dts = dts;
- }
-}
-
-static void
-calculate_expected (GstWfdRTPBuffer * jitterbuffer, guint32 expected,
- guint16 seqnum, GstClockTime dts, gint gap)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GstClockTime total_duration, duration, expected_dts;
- TimerType type;
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "dts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
- GST_TIME_ARGS (dts), GST_TIME_ARGS (priv->last_in_dts));
-
- /* the total duration spanned by the missing packets */
- if (dts >= priv->last_in_dts)
- total_duration = dts - priv->last_in_dts;
- else
- total_duration = 0;
-
- /* interpolate between the current time and the last time based on
- * number of packets we are missing, this is the estimated duration
- * for the missing packet based on equidistant packet spacing. */
- duration = total_duration / (gap + 1);
-
- GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT,
- GST_TIME_ARGS (duration));
-
- if (total_duration > priv->latency_ns) {
- GstClockTime gap_time;
- guint lost_packets;
-
- gap_time = total_duration - priv->latency_ns;
-
- if (duration > 0) {
- lost_packets = gap_time / duration;
- gap_time = lost_packets * duration;
- } else {
- lost_packets = gap;
- }
-
- /* too many lost packets, some of the missing packets are already
- * too late and we can generate lost packet events for them. */
- GST_DEBUG_OBJECT (jitterbuffer, "too many lost packets %" GST_TIME_FORMAT
- " > %" GST_TIME_FORMAT ", consider %u lost",
- GST_TIME_ARGS (total_duration), GST_TIME_ARGS (priv->latency_ns),
- lost_packets);
-
- /* this timer will fire immediately and the lost event will be pushed from
- * the timer thread */
- add_timer (jitterbuffer, TIMER_TYPE_LOST, expected, lost_packets,
- priv->last_in_dts + duration, 0, gap_time);
-
- expected += lost_packets;
- priv->last_in_dts += gap_time;
- }
-
- expected_dts = priv->last_in_dts + duration;
-
- if (priv->do_retransmission) {
- TimerData *timer;
-
- type = TIMER_TYPE_EXPECTED;
- /* if we had a timer for the first missing packet, update it. */
- if ((timer = find_timer (jitterbuffer, type, expected))) {
- GstClockTime timeout = timer->timeout;
-
- timer->duration = duration;
- if (timeout > expected_dts) {
- GstClockTime delay = timeout - expected_dts - timer->rtx_retry;
- reschedule_timer (jitterbuffer, timer, timer->seqnum, expected_dts,
- delay, TRUE);
- }
- expected++;
- expected_dts += duration;
- }
- } else {
- type = TIMER_TYPE_LOST;
- }
-
- while (gst_rtp_buffer_compare_seqnum (expected, seqnum) > 0) {
- add_timer (jitterbuffer, type, expected, 0, expected_dts, 0, duration);
- expected_dts += duration;
- expected++;
- }
-}
-
-static void
-calculate_jitter (GstWfdRTPBuffer * jitterbuffer, GstClockTime dts,
- guint rtptime)
-{
- gint32 rtpdiff;
- GstClockTimeDiff dtsdiff, rtpdiffns, diff;
- GstWfdRTPBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- if (G_UNLIKELY (dts == GST_CLOCK_TIME_NONE) || priv->clock_rate <= 0)
- goto no_time;
-
- if (priv->last_dts != -1)
- dtsdiff = dts - priv->last_dts;
- else
- dtsdiff = 0;
-
- if (priv->last_rtptime != -1)
- rtpdiff = rtptime - (guint32) priv->last_rtptime;
- else
- rtpdiff = 0;
-
- priv->last_dts = dts;
- priv->last_rtptime = rtptime;
-
- if (rtpdiff > 0)
- rtpdiffns =
- gst_util_uint64_scale_int (rtpdiff, GST_SECOND, priv->clock_rate);
- else
- rtpdiffns =
- -gst_util_uint64_scale_int (-rtpdiff, GST_SECOND, priv->clock_rate);
-
- diff = ABS (dtsdiff - rtpdiffns);
-
- /* jitter is stored in nanoseconds */
- priv->avg_jitter = (diff + (15 * priv->avg_jitter)) >> 4;
-
- GST_LOG_OBJECT (jitterbuffer,
- "dtsdiff %" GST_TIME_FORMAT " rtptime %" GST_TIME_FORMAT
- ", clock-rate %d, diff %" GST_TIME_FORMAT ", jitter: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (dtsdiff), GST_TIME_ARGS (rtpdiffns), priv->clock_rate,
- GST_TIME_ARGS (diff), GST_TIME_ARGS (priv->avg_jitter));
-
- return;
-
- /* ERRORS */
-no_time:
- {
- GST_DEBUG_OBJECT (jitterbuffer,
- "no dts or no clock-rate, can't calculate jitter");
- return;
- }
-}
-
-static GstFlowReturn
-gst_wfd_rtp_buffer_chain (GstPad * pad, GstObject * parent,
- GstBuffer * buffer)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
- guint16 seqnum;
- guint32 expected, rtptime;
- GstFlowReturn ret = GST_FLOW_OK;
- GstClockTime dts, pts;
- guint64 latency_ts;
- gboolean head;
- gint percent = -1;
- guint8 pt;
- GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
- gboolean do_next_seqnum = FALSE;
- WfdRTPBufferItem *item;
- GstMessage *msg = NULL;
-
- jitterbuffer = GST_WFD_RTP_BUFFER_CAST (parent);
-
- priv = jitterbuffer->priv;
-
- if (G_UNLIKELY (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp)))
- goto invalid_buffer;
-
- pt = gst_rtp_buffer_get_payload_type (&rtp);
- seqnum = gst_rtp_buffer_get_seq (&rtp);
- rtptime = gst_rtp_buffer_get_timestamp (&rtp);
- gst_rtp_buffer_unmap (&rtp);
-
- /* make sure we have PTS and DTS set */
- pts = GST_BUFFER_PTS (buffer);
- dts = GST_BUFFER_DTS (buffer);
- if (dts == -1)
- dts = pts;
- else if (pts == -1)
- pts = dts;
-
- /* take the DTS of the buffer. This is the time when the packet was
- * received and is used to calculate jitter and clock skew. We will adjust
- * this DTS with the smoothed value after processing it in the
- * jitterbuffer and assign it as the PTS. */
- /* bring to running time */
- dts = gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME, dts);
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "Received packet #%d at time %" GST_TIME_FORMAT ", discont %d", seqnum,
- GST_TIME_ARGS (dts), GST_BUFFER_IS_DISCONT (buffer));
-
- JBUF_LOCK_CHECK (priv, out_flushing);
-
- if (G_UNLIKELY (priv->last_pt != pt)) {
- GstCaps *caps;
-
- GST_DEBUG_OBJECT (jitterbuffer, "pt changed from %u to %u", priv->last_pt,
- pt);
-
- priv->last_pt = pt;
- /* reset clock-rate so that we get a new one */
- priv->clock_rate = -1;
-
- /* Try to get the clock-rate from the caps first if we can. If there are no
- * caps we must fire the signal to get the clock-rate. */
- if ((caps = gst_pad_get_current_caps (pad))) {
- gst_wfd_rtp_buffer_sink_parse_caps (jitterbuffer, caps);
- gst_caps_unref (caps);
- }
- }
-
- if (G_UNLIKELY (priv->clock_rate == -1)) {
- /* no clock rate given on the caps, try to get one with the signal */
- if (gst_wfd_rtp_buffer_get_clock_rate (jitterbuffer,
- pt) == GST_FLOW_FLUSHING)
- goto out_flushing;
-
- if (G_UNLIKELY (priv->clock_rate == -1))
- goto no_clock_rate;
- }
-
- /* don't accept more data on EOS */
- if (G_UNLIKELY (priv->eos))
- goto have_eos;
-
- calculate_jitter (jitterbuffer, dts, rtptime);
-
- expected = priv->next_in_seqnum;
-
- /* now check against our expected seqnum */
- if (G_LIKELY (expected != -1)) {
- gint gap;
-
- /* now calculate gap */
- gap = gst_rtp_buffer_compare_seqnum (expected, seqnum);
-
- GST_DEBUG_OBJECT (jitterbuffer, "expected #%d, got #%d, gap of %d",
- expected, seqnum, gap);
-
- if (G_LIKELY (gap == 0)) {
- /* packet is expected */
- calculate_packet_spacing (jitterbuffer, rtptime, dts);
- do_next_seqnum = TRUE;
- } else {
- gboolean reset = FALSE;
-
- if (gap < 0) {
- /* we received an old packet */
- if (G_UNLIKELY (gap < -RTP_MAX_MISORDER)) {
- /* too old packet, reset */
- GST_DEBUG_OBJECT (jitterbuffer, "reset: buffer too old %d < %d", gap,
- -RTP_MAX_MISORDER);
- reset = TRUE;
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "old packet received");
- }
- } else {
- /* new packet, we are missing some packets */
- if (G_UNLIKELY (gap > RTP_MAX_DROPOUT)) {
- /* packet too far in future, reset */
- GST_DEBUG_OBJECT (jitterbuffer, "reset: buffer too new %d > %d", gap,
- RTP_MAX_DROPOUT);
- reset = TRUE;
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap);
- /* fill in the gap with EXPECTED timers */
- calculate_expected (jitterbuffer, expected, seqnum, dts, gap);
-
- do_next_seqnum = TRUE;
- }
- }
- if (G_UNLIKELY (reset)) {
- GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
- wfd_rtp_buffer_flush (priv->jbuf, (GFunc) free_item, NULL);
- wfd_rtp_buffer_reset_skew (priv->jbuf);
- remove_all_timers (jitterbuffer);
- priv->last_popped_seqnum = -1;
- priv->next_seqnum = seqnum;
- do_next_seqnum = TRUE;
- JBUF_SIGNAL_EVENT (priv);
- }
- /* reset spacing estimation when gap */
- priv->ips_rtptime = -1;
- priv->ips_dts = GST_CLOCK_TIME_NONE;
- }
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "First buffer #%d", seqnum);
- /* we don't know what the next_in_seqnum should be, wait for the last
- * possible moment to push this buffer, maybe we get an earlier seqnum
- * while we wait */
- set_timer (jitterbuffer, TIMER_TYPE_DEADLINE, seqnum, dts);
- do_next_seqnum = TRUE;
- /* take rtptime and dts to calculate packet spacing */
- priv->ips_rtptime = rtptime;
- priv->ips_dts = dts;
- }
- if (do_next_seqnum) {
- priv->last_in_seqnum = seqnum;
- priv->last_in_dts = dts;
- priv->next_in_seqnum = (seqnum + 1) & 0xffff;
- }
-
- /* let's check if this buffer is too late, we can only accept packets with
- * bigger seqnum than the one we last pushed. */
- if (G_LIKELY (priv->last_popped_seqnum != -1)) {
- gint gap;
-
- gap = gst_rtp_buffer_compare_seqnum (priv->last_popped_seqnum, seqnum);
-
- /* priv->last_popped_seqnum >= seqnum, we're too late. */
- if (G_UNLIKELY (gap <= 0))
- goto too_late;
- }
-
- /* let's drop oldest packet if the queue is already full and drop-on-latency
- * is set. We can only do this when there actually is a latency. When no
- * latency is set, we just pump it in the queue and let the other end push it
- * out as fast as possible. */
- if (priv->latency_ms && priv->drop_on_latency) {
- latency_ts =
- gst_util_uint64_scale_int (priv->latency_ms, priv->clock_rate, 1000);
-
- if (G_UNLIKELY (wfd_rtp_buffer_get_ts_diff (priv->jbuf) >= latency_ts)) {
- WfdRTPBufferItem *old_item;
-
- old_item = wfd_rtp_buffer_peek (priv->jbuf);
-
- if (IS_DROPABLE (old_item)) {
- old_item = wfd_rtp_buffer_pop (priv->jbuf, &percent);
- GST_DEBUG_OBJECT (jitterbuffer, "Queue full, dropping old packet %p",
- old_item);
- priv->next_seqnum = (old_item->seqnum + 1) & 0xffff;
- free_item (old_item);
- }
- /* we might have removed some head buffers, signal the pushing thread to
- * see if it can push now */
- JBUF_SIGNAL_EVENT (priv);
- }
- }
-
- item = alloc_item (buffer, ITEM_TYPE_BUFFER, dts, pts, seqnum, 1, rtptime);
-
- /* now insert the packet into the queue in sorted order. This function returns
- * FALSE if a packet with the same seqnum was already in the queue, meaning we
- * have a duplicate. */
- if (G_UNLIKELY (!wfd_rtp_buffer_insert (priv->jbuf, item,
- &head, &percent)))
- goto duplicate;
-
- /* update timers */
- update_timers (jitterbuffer, seqnum, dts, do_next_seqnum);
-
- /* we had an unhandled SR, handle it now */
- if (priv->last_sr)
- do_handle_sync (jitterbuffer);
-
- if (G_UNLIKELY (head)) {
- /* signal addition of new buffer when the _loop is waiting. */
- if (G_LIKELY (priv->active))
- JBUF_SIGNAL_EVENT (priv);
-
- /* let's unschedule and unblock any waiting buffers. We only want to do this
- * when the head buffer changed */
- if (G_UNLIKELY (priv->clock_id)) {
- GST_DEBUG_OBJECT (jitterbuffer, "Unscheduling waiting new buffer");
- unschedule_current_timer (jitterbuffer);
- }
- }
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "Pushed packet #%d, now %d packets, head: %d, " "percent %d", seqnum,
- wfd_rtp_buffer_num_packets (priv->jbuf), head, percent);
-
- msg = check_buffering_percent (jitterbuffer, percent);
-
-finished:
- JBUF_UNLOCK (priv);
-
- if (msg)
- gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer), msg);
-
- return ret;
-
- /* ERRORS */
-invalid_buffer:
- {
- /* this is not fatal but should be filtered earlier */
- GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL),
- ("Received invalid RTP payload, dropping"));
- gst_buffer_unref (buffer);
- return GST_FLOW_OK;
- }
-no_clock_rate:
- {
- GST_WARNING_OBJECT (jitterbuffer,
- "No clock-rate in caps!, dropping buffer");
- gst_buffer_unref (buffer);
- goto finished;
- }
-out_flushing:
- {
- ret = priv->srcresult;
- GST_DEBUG_OBJECT (jitterbuffer, "flushing %s", gst_flow_get_name (ret));
- gst_buffer_unref (buffer);
- goto finished;
- }
-have_eos:
- {
- ret = GST_FLOW_EOS;
- GST_WARNING_OBJECT (jitterbuffer, "we are EOS, refusing buffer");
- gst_buffer_unref (buffer);
- goto finished;
- }
-too_late:
- {
- GST_WARNING_OBJECT (jitterbuffer, "Packet #%d too late as #%d was already"
- " popped, dropping", seqnum, priv->last_popped_seqnum);
- priv->num_late++;
- gst_buffer_unref (buffer);
- goto finished;
- }
-duplicate:
- {
- GST_WARNING_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping",
- seqnum);
- priv->num_duplicates++;
- free_item (item);
- goto finished;
- }
-}
-
-static GstClockTime
-compute_elapsed (GstWfdRTPBuffer * jitterbuffer, WfdRTPBufferItem * item)
-{
- guint64 ext_time, elapsed;
- guint32 rtp_time;
- GstWfdRTPBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
- rtp_time = item->rtptime;
-
- GST_LOG_OBJECT (jitterbuffer, "rtp %" G_GUINT32_FORMAT ", ext %"
- G_GUINT64_FORMAT, rtp_time, priv->ext_timestamp);
-
- if (rtp_time < priv->ext_timestamp) {
- ext_time = priv->ext_timestamp;
- } else {
- ext_time = gst_rtp_buffer_ext_timestamp (&priv->ext_timestamp, rtp_time);
- }
-
- if (ext_time > priv->clock_base)
- elapsed = ext_time - priv->clock_base;
- else
- elapsed = 0;
-
- elapsed = gst_util_uint64_scale_int (elapsed, GST_SECOND, priv->clock_rate);
- return elapsed;
-}
-
-static void
-update_estimated_eos (GstWfdRTPBuffer * jitterbuffer,
- WfdRTPBufferItem * item)
-{
- guint64 total, elapsed, left, estimated;
- GstClockTime out_time;
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
-
- if (priv->npt_stop == -1 || priv->ext_timestamp == -1
- || priv->clock_base == -1 || priv->clock_rate <= 0)
- return;
-
- /* compute the elapsed time */
- elapsed = compute_elapsed (jitterbuffer, item);
-
- /* do nothing if elapsed time doesn't increment */
- if (priv->last_elapsed && elapsed <= priv->last_elapsed)
- return;
-
- priv->last_elapsed = elapsed;
-
- /* this is the total time we need to play */
- total = priv->npt_stop - priv->npt_start;
- GST_LOG_OBJECT (jitterbuffer, "total %" GST_TIME_FORMAT,
- GST_TIME_ARGS (total));
-
- /* this is how much time there is left */
- if (total > elapsed)
- left = total - elapsed;
- else
- left = 0;
-
- /* if we have less time left that the size of the buffer, we will not
- * be able to keep it filled, disabled buffering then */
- if (left < wfd_rtp_buffer_get_delay (priv->jbuf)) {
- GST_DEBUG_OBJECT (jitterbuffer, "left %" GST_TIME_FORMAT
- ", disable buffering close to EOS", GST_TIME_ARGS (left));
- wfd_rtp_buffer_disable_buffering (priv->jbuf, TRUE);
- }
-
- /* this is the current time as running-time */
- out_time = item->dts;
-
- if (elapsed > 0)
- estimated = gst_util_uint64_scale (out_time, total, elapsed);
- else {
- /* if there is almost nothing left,
- * we may never advance enough to end up in the above case */
- if (total < GST_SECOND)
- estimated = GST_SECOND;
- else
- estimated = -1;
- }
- GST_LOG_OBJECT (jitterbuffer, "elapsed %" GST_TIME_FORMAT ", estimated %"
- GST_TIME_FORMAT, GST_TIME_ARGS (elapsed), GST_TIME_ARGS (estimated));
-
- if (estimated != -1 && priv->estimated_eos != estimated) {
- set_timer (jitterbuffer, TIMER_TYPE_EOS, -1, estimated);
- priv->estimated_eos = estimated;
- }
-}
-
-/* take a buffer from the queue and push it */
-static GstFlowReturn
-pop_and_push_next (GstWfdRTPBuffer * jitterbuffer, guint seqnum)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GstFlowReturn result = GST_FLOW_OK;
- WfdRTPBufferItem *item;
- GstBuffer *outbuf = NULL;
- GstEvent *outevent = NULL;
- GstQuery *outquery = NULL;
- GstClockTime dts, pts;
- gint percent = -1;
- gboolean do_push = TRUE;
- guint type;
- GstMessage *msg;
-
- /* when we get here we are ready to pop and push the buffer */
- item = wfd_rtp_buffer_pop (priv->jbuf, &percent);
- type = item->type;
-
- switch (type) {
- case ITEM_TYPE_BUFFER:
-
- /* we need to make writable to change the flags and timestamps */
- outbuf = gst_buffer_make_writable (item->data);
-
- if (G_UNLIKELY (priv->discont)) {
- /* set DISCONT flag when we missed a packet. We pushed the buffer writable
- * into the jitterbuffer so we can modify now. */
- GST_DEBUG_OBJECT (jitterbuffer, "mark output buffer discont");
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
- priv->discont = FALSE;
- }
- if (G_UNLIKELY (priv->ts_discont)) {
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC);
- priv->ts_discont = FALSE;
- }
-
- dts =
- gst_segment_to_position (&priv->segment, GST_FORMAT_TIME, item->dts);
- pts =
- gst_segment_to_position (&priv->segment, GST_FORMAT_TIME, item->pts);
-
- /* apply timestamp with offset to buffer now */
- GST_BUFFER_DTS (outbuf) = apply_offset (jitterbuffer, dts);
- GST_BUFFER_PTS (outbuf) = apply_offset (jitterbuffer, pts);
-
- /* update the elapsed time when we need to check against the npt stop time. */
- update_estimated_eos (jitterbuffer, item);
-
- priv->last_out_time = GST_BUFFER_PTS (outbuf);
- break;
- case ITEM_TYPE_LOST:
- priv->discont = TRUE;
- if (!priv->do_lost)
- do_push = FALSE;
- /* FALLTHROUGH */
- case ITEM_TYPE_EVENT:
- outevent = item->data;
- break;
- case ITEM_TYPE_QUERY:
- outquery = item->data;
- break;
- }
-
- /* now we are ready to push the buffer. Save the seqnum and release the lock
- * so the other end can push stuff in the queue again. */
- if (seqnum != -1) {
- priv->last_popped_seqnum = seqnum;
- priv->next_seqnum = (seqnum + item->count) & 0xffff;
- }
- msg = check_buffering_percent (jitterbuffer, percent);
- JBUF_UNLOCK (priv);
-
- item->data = NULL;
- free_item (item);
-
- if (msg)
- gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer), msg);
-
- switch (type) {
- case ITEM_TYPE_BUFFER:
- /* push buffer */
- GST_DEBUG_OBJECT (jitterbuffer,
- "Pushing buffer %d, dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT,
- seqnum, GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)),
- GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)));
- result = gst_pad_push (priv->srcpad, outbuf);
-
- JBUF_LOCK_CHECK (priv, out_flushing);
- break;
- case ITEM_TYPE_LOST:
- case ITEM_TYPE_EVENT:
- GST_DEBUG_OBJECT (jitterbuffer, "%sPushing event %" GST_PTR_FORMAT
- ", seqnum %d", do_push ? "" : "NOT ", outevent, seqnum);
-
- if (do_push)
- gst_pad_push_event (priv->srcpad, outevent);
- else
- gst_event_unref (outevent);
-
- result = GST_FLOW_OK;
-
- JBUF_LOCK_CHECK (priv, out_flushing);
- break;
- case ITEM_TYPE_QUERY:
- {
- gboolean res;
-
- res = gst_pad_peer_query (priv->srcpad, outquery);
-
- JBUF_LOCK_CHECK (priv, out_flushing);
- result = GST_FLOW_OK;
- GST_LOG_OBJECT (jitterbuffer, "did query %p, return %d", outquery, res);
- JBUF_SIGNAL_QUERY (priv, res);
- break;
- }
- }
- return result;
-
- /* ERRORS */
-out_flushing:
- {
- return priv->srcresult;
- }
-}
-
-#define GST_FLOW_WAIT GST_FLOW_CUSTOM_SUCCESS
-
-/* Peek a buffer and compare the seqnum to the expected seqnum.
- * If all is fine, the buffer is pushed.
- * If something is wrong, we wait for some event
- */
-static GstFlowReturn
-handle_next_buffer (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GstFlowReturn result = GST_FLOW_OK;
- WfdRTPBufferItem *item;
- guint seqnum;
- guint32 next_seqnum;
- gint gap;
-
- /* only push buffers when PLAYING and active and not buffering */
- if (priv->blocked || !priv->active ||
- wfd_rtp_buffer_is_buffering (priv->jbuf))
- return GST_FLOW_WAIT;
-
-again:
- /* peek a buffer, we're just looking at the sequence number.
- * If all is fine, we'll pop and push it. If the sequence number is wrong we
- * wait for a timeout or something to change.
- * The peeked buffer is valid for as long as we hold the jitterbuffer lock. */
- item = wfd_rtp_buffer_peek (priv->jbuf);
- if (item == NULL)
- goto wait;
-
- /* get the seqnum and the next expected seqnum */
- seqnum = item->seqnum;
- if (seqnum == -1)
- goto do_push;
-
- next_seqnum = priv->next_seqnum;
-
- /* get the gap between this and the previous packet. If we don't know the
- * previous packet seqnum assume no gap. */
- if (G_UNLIKELY (next_seqnum == -1)) {
- GST_DEBUG_OBJECT (jitterbuffer, "First buffer #%d", seqnum);
- /* we don't know what the next_seqnum should be, the chain function should
- * have scheduled a DEADLINE timer that will increment next_seqnum when it
- * fires, so wait for that */
- result = GST_FLOW_WAIT;
- } else {
- /* else calculate GAP */
- gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum);
-
- if (G_LIKELY (gap == 0)) {
- do_push:
- /* no missing packet, pop and push */
- result = pop_and_push_next (jitterbuffer, seqnum);
- } else if (G_UNLIKELY (gap < 0)) {
- WfdRTPBufferItem *item;
- /* if we have a packet that we already pushed or considered dropped, pop it
- * off and get the next packet */
- GST_DEBUG_OBJECT (jitterbuffer, "Old packet #%d, next #%d dropping",
- seqnum, next_seqnum);
- item = wfd_rtp_buffer_pop (priv->jbuf, NULL);
- free_item (item);
- goto again;
- } else {
- /* the chain function has scheduled timers to request retransmission or
- * when to consider the packet lost, wait for that */
- GST_DEBUG_OBJECT (jitterbuffer,
- "Sequence number GAP detected: expected %d instead of %d (%d missing)",
- next_seqnum, seqnum, gap);
- result = GST_FLOW_WAIT;
- }
- }
- return result;
-
-wait:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "no buffer, going to wait");
- if (priv->eos)
- result = GST_FLOW_EOS;
- else
- result = GST_FLOW_WAIT;
- return result;
- }
-}
-
-/* the timeout for when we expected a packet expired */
-static gboolean
-do_expected_timeout (GstWfdRTPBuffer * jitterbuffer, TimerData * timer,
- GstClockTime now)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GstEvent *event;
- guint delay, delay_ms, avg_rtx_rtt_ms;
- guint rtx_retry_timeout_ms, rtx_retry_period_ms;
- GstClockTime rtx_retry_period;
- GstClockTime rtx_retry_timeout;
- GstClock *clock;
-
- GST_DEBUG_OBJECT (jitterbuffer, "expected %d didn't arrive, now %"
- GST_TIME_FORMAT, timer->seqnum, GST_TIME_ARGS (now));
-
- if (priv->rtx_retry_timeout == -1) {
- if (priv->avg_rtx_rtt == 0)
- rtx_retry_timeout = DEFAULT_AUTO_RTX_TIMEOUT;
- else
- /* we want to ask for a retransmission after we waited for a
- * complete RTT and the additional jitter */
- rtx_retry_timeout = priv->avg_rtx_rtt + priv->avg_jitter * 2;
- } else {
- rtx_retry_timeout = priv->rtx_retry_timeout * GST_MSECOND;
- }
-
- if (priv->rtx_retry_period == -1) {
- /* we retry up to the configured jitterbuffer size but leaving some
- * room for the retransmission to arrive in time */
- rtx_retry_period = priv->latency_ns - rtx_retry_timeout;
- } else {
- rtx_retry_period = priv->rtx_retry_period * GST_MSECOND;
- }
-
- GST_DEBUG_OBJECT (jitterbuffer, "timeout %" GST_TIME_FORMAT ", period %"
- GST_TIME_FORMAT, GST_TIME_ARGS (rtx_retry_timeout),
- GST_TIME_ARGS (rtx_retry_period));
-
- delay = timer->rtx_delay + timer->rtx_retry;
-
- delay_ms = GST_TIME_AS_MSECONDS (delay);
- rtx_retry_timeout_ms = GST_TIME_AS_MSECONDS (rtx_retry_timeout);
- rtx_retry_period_ms = GST_TIME_AS_MSECONDS (rtx_retry_period);
- avg_rtx_rtt_ms = GST_TIME_AS_MSECONDS (priv->avg_rtx_rtt);
-
- event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
- gst_structure_new ("GstRTPRetransmissionRequest",
- "seqnum", G_TYPE_UINT, (guint) timer->seqnum,
- "running-time", G_TYPE_UINT64, timer->rtx_base,
- "delay", G_TYPE_UINT, delay_ms,
- "retry", G_TYPE_UINT, timer->num_rtx_retry,
- "frequency", G_TYPE_UINT, rtx_retry_timeout_ms,
- "period", G_TYPE_UINT, rtx_retry_period_ms,
- "deadline", G_TYPE_UINT, priv->latency_ms,
- "packet-spacing", G_TYPE_UINT64, priv->packet_spacing,
- "avg-rtt", G_TYPE_UINT, avg_rtx_rtt_ms, NULL));
-
- priv->num_rtx_requests++;
- timer->num_rtx_retry++;
-
- GST_OBJECT_LOCK (jitterbuffer);
- if ((clock = GST_ELEMENT_CLOCK (jitterbuffer))) {
- timer->rtx_last = gst_clock_get_time (clock);
- timer->rtx_last -= GST_ELEMENT_CAST (jitterbuffer)->base_time;
- } else {
- timer->rtx_last = now;
- }
- GST_OBJECT_UNLOCK (jitterbuffer);
-
- /* calculate the timeout for the next retransmission attempt */
- timer->rtx_retry += rtx_retry_timeout;
- GST_DEBUG_OBJECT (jitterbuffer, "base %" GST_TIME_FORMAT ", delay %"
- GST_TIME_FORMAT ", retry %" GST_TIME_FORMAT ", num_retry %u",
- GST_TIME_ARGS (timer->rtx_base), GST_TIME_ARGS (timer->rtx_delay),
- GST_TIME_ARGS (timer->rtx_retry), timer->num_rtx_retry);
-
- if (timer->rtx_retry + timer->rtx_delay > rtx_retry_period) {
- GST_DEBUG_OBJECT (jitterbuffer, "reschedule as LOST timer");
- /* too many retransmission request, we now convert the timer
- * to a lost timer, leave the num_rtx_retry as it is for stats */
- timer->type = TIMER_TYPE_LOST;
- timer->rtx_delay = 0;
- timer->rtx_retry = 0;
- }
- reschedule_timer (jitterbuffer, timer, timer->seqnum,
- timer->rtx_base + timer->rtx_retry, timer->rtx_delay, FALSE);
-
- JBUF_UNLOCK (priv);
- gst_pad_push_event (priv->sinkpad, event);
- JBUF_LOCK (priv);
-
- return FALSE;
-}
-
-/* a packet is lost */
-static gboolean
-do_lost_timeout (GstWfdRTPBuffer * jitterbuffer, TimerData * timer,
- GstClockTime now)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GstClockTime duration, timestamp;
- guint seqnum, lost_packets, num_rtx_retry, next_in_seqnum;
- gboolean late, head;
- GstEvent *event;
- WfdRTPBufferItem *item;
-
- seqnum = timer->seqnum;
- timestamp = apply_offset (jitterbuffer, timer->timeout);
- duration = timer->duration;
- if (duration == GST_CLOCK_TIME_NONE && priv->packet_spacing > 0)
- duration = priv->packet_spacing;
- lost_packets = MAX (timer->num, 1);
- late = timer->num > 0;
- num_rtx_retry = timer->num_rtx_retry;
-
- /* we had a gap and thus we lost some packets. Create an event for this. */
- if (lost_packets > 1)
- GST_DEBUG_OBJECT (jitterbuffer, "Packets #%d -> #%d lost", seqnum,
- seqnum + lost_packets - 1);
- else
- GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", seqnum);
-
- priv->num_late += lost_packets;
- priv->num_rtx_failed += num_rtx_retry;
-
- next_in_seqnum = (seqnum + lost_packets) & 0xffff;
-
- /* we now only accept seqnum bigger than this */
- if (gst_rtp_buffer_compare_seqnum (priv->next_in_seqnum, next_in_seqnum) > 0)
- priv->next_in_seqnum = next_in_seqnum;
-
- /* create paket lost event */
- event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
- gst_structure_new ("GstRTPPacketLost",
- "seqnum", G_TYPE_UINT, (guint) seqnum,
- "timestamp", G_TYPE_UINT64, timestamp,
- "duration", G_TYPE_UINT64, duration,
- "late", G_TYPE_BOOLEAN, late,
- "retry", G_TYPE_UINT, num_rtx_retry, NULL));
-
- item = alloc_item (event, ITEM_TYPE_LOST, -1, -1, seqnum, lost_packets, -1);
- wfd_rtp_buffer_insert (priv->jbuf, item, &head, NULL);
-
- /* remove timer now */
- remove_timer (jitterbuffer, timer);
- if (head)
- JBUF_SIGNAL_EVENT (priv);
-
- return TRUE;
-}
-
-static gboolean
-do_eos_timeout (GstWfdRTPBuffer * jitterbuffer, TimerData * timer,
- GstClockTime now)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
-
- GST_INFO_OBJECT (jitterbuffer, "got the NPT timeout");
- remove_timer (jitterbuffer, timer);
- if (!priv->eos) {
- /* there was no EOS in the buffer, put one in there now */
- queue_event (jitterbuffer, gst_event_new_eos ());
- }
- JBUF_SIGNAL_EVENT (priv);
-
- return TRUE;
-}
-
-static gboolean
-do_deadline_timeout (GstWfdRTPBuffer * jitterbuffer, TimerData * timer,
- GstClockTime now)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
-
- GST_INFO_OBJECT (jitterbuffer, "got deadline timeout");
-
- /* timer seqnum might have been obsoleted by caps seqnum-base,
- * only mess with current ongoing seqnum if still unknown */
- if (priv->next_seqnum == -1)
- priv->next_seqnum = timer->seqnum;
- remove_timer (jitterbuffer, timer);
- JBUF_SIGNAL_EVENT (priv);
-
- return TRUE;
-}
-
-static gboolean
-do_timeout (GstWfdRTPBuffer * jitterbuffer, TimerData * timer,
- GstClockTime now)
-{
- gboolean removed = FALSE;
-
- switch (timer->type) {
- case TIMER_TYPE_EXPECTED:
- removed = do_expected_timeout (jitterbuffer, timer, now);
- break;
- case TIMER_TYPE_LOST:
- removed = do_lost_timeout (jitterbuffer, timer, now);
- break;
- case TIMER_TYPE_DEADLINE:
- removed = do_deadline_timeout (jitterbuffer, timer, now);
- break;
- case TIMER_TYPE_EOS:
- removed = do_eos_timeout (jitterbuffer, timer, now);
- break;
- }
- return removed;
-}
-
-/* called when we need to wait for the next timeout.
- *
- * We loop over the array of recorded timeouts and wait for the earliest one.
- * When it timed out, do the logic associated with the timer.
- *
- * If there are no timers, we wait on a gcond until something new happens.
- */
-static void
-wait_next_timeout (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv = jitterbuffer->priv;
- GstClockTime now = 0;
-
- JBUF_LOCK (priv);
- while (priv->timer_running) {
- TimerData *timer = NULL;
- GstClockTime timer_timeout = -1;
- gint i, len;
-
- GST_DEBUG_OBJECT (jitterbuffer, "now %" GST_TIME_FORMAT,
- GST_TIME_ARGS (now));
-
- len = priv->timers->len;
- for (i = 0; i < len; i++) {
- TimerData *test = &g_array_index (priv->timers, TimerData, i);
- GstClockTime test_timeout = get_timeout (jitterbuffer, test);
- gboolean save_best = FALSE;
-
- GST_DEBUG_OBJECT (jitterbuffer, "%d, %d, %d, %" GST_TIME_FORMAT,
- i, test->type, test->seqnum, GST_TIME_ARGS (test_timeout));
-
- /* find the smallest timeout */
- if (timer == NULL) {
- save_best = TRUE;
- } else if (timer_timeout == -1) {
- /* we already have an immediate timeout, the new timer must be an
- * immediate timer with smaller seqnum to become the best */
- if (test_timeout == -1
- && (gst_rtp_buffer_compare_seqnum (test->seqnum,
- timer->seqnum) > 0))
- save_best = TRUE;
- } else if (test_timeout == -1) {
- /* first immediate timer */
- save_best = TRUE;
- } else if (test_timeout < timer_timeout) {
- /* earlier timer */
- save_best = TRUE;
- } else if (test_timeout == timer_timeout
- && (gst_rtp_buffer_compare_seqnum (test->seqnum,
- timer->seqnum) > 0)) {
- /* same timer, smaller seqnum */
- save_best = TRUE;
- }
- if (save_best) {
- GST_DEBUG_OBJECT (jitterbuffer, "new best %d", i);
- timer = test;
- timer_timeout = test_timeout;
- }
- }
- if (timer && !priv->blocked) {
- GstClock *clock;
- GstClockTime sync_time;
- GstClockID id;
- GstClockReturn ret;
- GstClockTimeDiff clock_jitter;
-
- if (timer_timeout == -1 || timer_timeout <= now) {
- do_timeout (jitterbuffer, timer, now);
- /* check here, do_timeout could have released the lock */
- if (!priv->timer_running)
- break;
- continue;
- }
-
- GST_OBJECT_LOCK (jitterbuffer);
- clock = GST_ELEMENT_CLOCK (jitterbuffer);
- if (!clock) {
- GST_OBJECT_UNLOCK (jitterbuffer);
- /* let's just push if there is no clock */
- GST_DEBUG_OBJECT (jitterbuffer, "No clock, timeout right away");
- now = timer_timeout;
- continue;
- }
-
- /* prepare for sync against clock */
- sync_time = timer_timeout + GST_ELEMENT_CAST (jitterbuffer)->base_time;
- /* add latency of peer to get input time */
- sync_time += priv->peer_latency;
-
- GST_DEBUG_OBJECT (jitterbuffer, "sync to timestamp %" GST_TIME_FORMAT
- " with sync time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (timer_timeout), GST_TIME_ARGS (sync_time));
-
- /* create an entry for the clock */
- id = priv->clock_id = gst_clock_new_single_shot_id (clock, sync_time);
- priv->timer_timeout = timer_timeout;
- priv->timer_seqnum = timer->seqnum;
- GST_OBJECT_UNLOCK (jitterbuffer);
-
- /* release the lock so that the other end can push stuff or unlock */
- JBUF_UNLOCK (priv);
-
- ret = gst_clock_id_wait (id, &clock_jitter);
-
- JBUF_LOCK (priv);
- if (!priv->timer_running) {
- gst_clock_id_unref (id);
- priv->clock_id = NULL;
- break;
- }
-
- if (ret != GST_CLOCK_UNSCHEDULED) {
- now = timer_timeout + MAX (clock_jitter, 0);
- GST_DEBUG_OBJECT (jitterbuffer, "sync done, %d, #%d, %" G_GINT64_FORMAT,
- ret, priv->timer_seqnum, clock_jitter);
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "sync unscheduled");
- }
- /* and free the entry */
- gst_clock_id_unref (id);
- priv->clock_id = NULL;
- } else {
- /* no timers, wait for activity */
- JBUF_WAIT_TIMER (priv);
- }
- }
- JBUF_UNLOCK (priv);
-
- GST_DEBUG_OBJECT (jitterbuffer, "we are stopping");
- return;
-}
-
-/*
- * This funcion implements the main pushing loop on the source pad.
- *
- * It first tries to push as many buffers as possible. If there is a seqnum
- * mismatch, we wait for the next timeouts.
- */
-static void
-gst_wfd_rtp_buffer_loop (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv;
- GstFlowReturn result = GST_FLOW_OK;
-
- priv = jitterbuffer->priv;
-
- JBUF_LOCK_CHECK (priv, flushing);
- do {
- result = handle_next_buffer (jitterbuffer);
- if (G_LIKELY (result == GST_FLOW_WAIT)) {
- /* now wait for the next event */
- JBUF_WAIT_EVENT (priv, flushing);
- result = GST_FLOW_OK;
- }
- }
- while (result == GST_FLOW_OK);
- /* store result for upstream */
- priv->srcresult = result;
- /* if we get here we need to pause */
- goto pause;
-
- /* ERRORS */
-flushing:
- {
- result = priv->srcresult;
- goto pause;
- }
-pause:
- {
- GstEvent *event;
-
- JBUF_SIGNAL_QUERY (priv, FALSE);
- JBUF_UNLOCK (priv);
-
- GST_DEBUG_OBJECT (jitterbuffer, "pausing task, reason %s",
- gst_flow_get_name (result));
- gst_pad_pause_task (priv->srcpad);
- if (result == GST_FLOW_EOS) {
- event = gst_event_new_eos ();
- gst_pad_push_event (priv->srcpad, event);
- }
- return;
- }
-}
-
-/* collect the info from the lastest RTCP packet and the jitterbuffer sync, do
- * some sanity checks and then emit the handle-sync signal with the parameters.
- * This function must be called with the LOCK */
-static void
-do_handle_sync (GstWfdRTPBuffer * jitterbuffer)
-{
- GstWfdRTPBufferPrivate *priv;
- guint64 base_rtptime, base_time;
- guint32 clock_rate;
- guint64 last_rtptime;
- guint64 clock_base;
- guint64 ext_rtptime, diff;
- gboolean valid = TRUE, keep = FALSE;
-
- priv = jitterbuffer->priv;
-
- /* get the last values from the jitterbuffer */
- wfd_rtp_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time,
- &clock_rate, &last_rtptime);
-
- clock_base = priv->clock_base;
- ext_rtptime = priv->ext_rtptime;
-
- GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %"
- G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT
- ", clock-base %" G_GUINT64_FORMAT ", last-rtptime %" G_GUINT64_FORMAT,
- ext_rtptime, base_rtptime, clock_rate, clock_base, last_rtptime);
-
- if (base_rtptime == -1 || clock_rate == -1 || base_time == -1) {
- /* we keep this SR packet for later. When we get a valid RTP packet the
- * above values will be set and we can try to use the SR packet */
- GST_DEBUG_OBJECT (jitterbuffer, "keeping for later, no RTP values");
- keep = TRUE;
- } else {
- /* we can't accept anything that happened before we did the last resync */
- if (base_rtptime > ext_rtptime) {
- GST_DEBUG_OBJECT (jitterbuffer, "dropping, older than base time");
- valid = FALSE;
- } else {
- /* the SR RTP timestamp must be something close to what we last observed
- * in the jitterbuffer */
- if (ext_rtptime > last_rtptime) {
- /* check how far ahead it is to our RTP timestamps */
- diff = ext_rtptime - last_rtptime;
- /* if bigger than 1 second, we drop it */
- if (diff > clock_rate) {
- GST_DEBUG_OBJECT (jitterbuffer, "too far ahead");
- /* should drop this, but some RTSP servers end up with bogus
- * way too ahead RTCP packet when repeated PAUSE/PLAY,
- * so still trigger rptbin sync but invalidate RTCP data
- * (sync might use other methods) */
- ext_rtptime = -1;
- }
- GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %"
- G_GUINT64_FORMAT, last_rtptime, diff);
- }
- }
- }
-
- if (keep) {
- GST_DEBUG_OBJECT (jitterbuffer, "keeping RTCP packet for later");
- } else if (valid) {
- GstStructure *s;
-
- s = gst_structure_new ("application/x-rtp-sync",
- "base-rtptime", G_TYPE_UINT64, base_rtptime,
- "base-time", G_TYPE_UINT64, base_time,
- "clock-rate", G_TYPE_UINT, clock_rate,
- "clock-base", G_TYPE_UINT64, clock_base,
- "sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
- "sr-buffer", GST_TYPE_BUFFER, priv->last_sr, NULL);
-
- GST_DEBUG_OBJECT (jitterbuffer, "signaling sync");
- gst_buffer_replace (&priv->last_sr, NULL);
- JBUF_UNLOCK (priv);
- g_signal_emit (jitterbuffer,
- gst_wfd_rtp_buffer_signals[SIGNAL_HANDLE_SYNC], 0, s);
- JBUF_LOCK (priv);
- gst_structure_free (s);
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "dropping RTCP packet");
- gst_buffer_replace (&priv->last_sr, NULL);
- }
-}
-
-static GstFlowReturn
-gst_wfd_rtp_buffer_chain_rtcp (GstPad * pad, GstObject * parent,
- GstBuffer * buffer)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
- GstFlowReturn ret = GST_FLOW_OK;
- guint32 ssrc;
- GstRTCPPacket packet;
- guint64 ext_rtptime;
- guint32 rtptime;
- GstRTCPBuffer rtcp = { NULL, };
-
- jitterbuffer = GST_WFD_RTP_BUFFER (parent);
-
- if (G_UNLIKELY (!gst_rtcp_buffer_validate (buffer)))
- goto invalid_buffer;
-
- priv = jitterbuffer->priv;
-
- gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp);
-
- if (!gst_rtcp_buffer_get_first_packet (&rtcp, &packet))
- goto empty_buffer;
-
- /* first packet must be SR or RR or else the validate would have failed */
- switch (gst_rtcp_packet_get_type (&packet)) {
- case GST_RTCP_TYPE_SR:
- gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, &rtptime,
- NULL, NULL);
- break;
- default:
- goto ignore_buffer;
- }
- gst_rtcp_buffer_unmap (&rtcp);
-
- GST_DEBUG_OBJECT (jitterbuffer, "received RTCP of SSRC %08x", ssrc);
-
- JBUF_LOCK (priv);
- /* convert the RTP timestamp to our extended timestamp, using the same offset
- * we used in the jitterbuffer */
- ext_rtptime = priv->jbuf->ext_rtptime;
- ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
-
- priv->ext_rtptime = ext_rtptime;
- gst_buffer_replace (&priv->last_sr, buffer);
-
- do_handle_sync (jitterbuffer);
- JBUF_UNLOCK (priv);
-
-done:
- gst_buffer_unref (buffer);
-
- return ret;
-
-invalid_buffer:
- {
- /* this is not fatal but should be filtered earlier */
- GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL),
- ("Received invalid RTCP payload, dropping"));
- ret = GST_FLOW_OK;
- goto done;
- }
-empty_buffer:
- {
- /* this is not fatal but should be filtered earlier */
- GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL),
- ("Received empty RTCP payload, dropping"));
- gst_rtcp_buffer_unmap (&rtcp);
- ret = GST_FLOW_OK;
- goto done;
- }
-ignore_buffer:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "ignoring RTCP packet");
- gst_rtcp_buffer_unmap (&rtcp);
- ret = GST_FLOW_OK;
- goto done;
- }
-}
-
-static gboolean
-gst_wfd_rtp_buffer_sink_query (GstPad * pad, GstObject * parent,
- GstQuery * query)
-{
- gboolean res = FALSE;
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (parent);
- priv = jitterbuffer->priv;
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_CAPS:
- {
- GstCaps *filter, *caps;
-
- gst_query_parse_caps (query, &filter);
- caps = gst_wfd_rtp_buffer_getcaps (pad, filter);
- gst_query_set_caps_result (query, caps);
- gst_caps_unref (caps);
- res = TRUE;
- break;
- }
- default:
- if (GST_QUERY_IS_SERIALIZED (query)) {
- WfdRTPBufferItem *item;
- gboolean head;
-
- JBUF_LOCK_CHECK (priv, out_flushing);
- if (wfd_rtp_buffer_get_mode (priv->jbuf) !=
- WFD_RTP_BUFFER_MODE_BUFFER) {
- GST_DEBUG_OBJECT (jitterbuffer, "adding serialized query");
- item = alloc_item (query, ITEM_TYPE_QUERY, -1, -1, -1, 0, -1);
- wfd_rtp_buffer_insert (priv->jbuf, item, &head, NULL);
- if (head)
- JBUF_SIGNAL_EVENT (priv);
- JBUF_WAIT_QUERY (priv, out_flushing);
- res = priv->last_query;
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "refusing query, we are buffering");
- res = FALSE;
- }
- JBUF_UNLOCK (priv);
- } else {
- res = gst_pad_query_default (pad, parent, query);
- }
- break;
- }
- return res;
- /* ERRORS */
-out_flushing:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "we are flushing");
- JBUF_UNLOCK (priv);
- return FALSE;
- }
-
-}
-
-static gboolean
-gst_wfd_rtp_buffer_src_query (GstPad * pad, GstObject * parent,
- GstQuery * query)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
- gboolean res = FALSE;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (parent);
- priv = jitterbuffer->priv;
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_LATENCY:
- {
- /* We need to send the query upstream and add the returned latency to our
- * own */
- GstClockTime min_latency, max_latency;
- gboolean us_live;
- GstClockTime our_latency;
-
- if ((res = gst_pad_peer_query (priv->sinkpad, query))) {
- gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
-
- GST_DEBUG_OBJECT (jitterbuffer, "Peer latency: min %"
- GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
- GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
-
- /* store this so that we can safely sync on the peer buffers. */
- JBUF_LOCK (priv);
- priv->peer_latency = min_latency;
- our_latency = priv->latency_ns;
- JBUF_UNLOCK (priv);
-
- GST_DEBUG_OBJECT (jitterbuffer, "Our latency: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (our_latency));
-
- /* we add some latency but can buffer an infinite amount of time */
- min_latency += our_latency;
- max_latency = -1;
-
- GST_DEBUG_OBJECT (jitterbuffer, "Calculated total latency : min %"
- GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
- GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
-
- gst_query_set_latency (query, TRUE, min_latency, max_latency);
- }
- break;
- }
- case GST_QUERY_POSITION:
- {
- GstClockTime start, last_out;
- GstFormat fmt;
-
- gst_query_parse_position (query, &fmt, NULL);
- if (fmt != GST_FORMAT_TIME) {
- res = gst_pad_query_default (pad, parent, query);
- break;
- }
-
- JBUF_LOCK (priv);
- start = priv->npt_start;
- last_out = priv->last_out_time;
- JBUF_UNLOCK (priv);
-
- GST_DEBUG_OBJECT (jitterbuffer, "npt start %" GST_TIME_FORMAT
- ", last out %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
- GST_TIME_ARGS (last_out));
-
- if (GST_CLOCK_TIME_IS_VALID (start) && GST_CLOCK_TIME_IS_VALID (last_out)) {
- /* bring 0-based outgoing time to stream time */
- gst_query_set_position (query, GST_FORMAT_TIME, start + last_out);
- res = TRUE;
- } else {
- res = gst_pad_query_default (pad, parent, query);
- }
- break;
- }
- case GST_QUERY_CAPS:
- {
- GstCaps *filter, *caps;
-
- gst_query_parse_caps (query, &filter);
- caps = gst_wfd_rtp_buffer_getcaps (pad, filter);
- gst_query_set_caps_result (query, caps);
- gst_caps_unref (caps);
- res = TRUE;
- break;
- }
- default:
- res = gst_pad_query_default (pad, parent, query);
- break;
- }
-
- return res;
-}
-
-static void
-gst_wfd_rtp_buffer_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (object);
- priv = jitterbuffer->priv;
-
- switch (prop_id) {
- case PROP_LATENCY:
- {
- guint new_latency, old_latency;
-
- new_latency = g_value_get_uint (value);
-
- JBUF_LOCK (priv);
- old_latency = priv->latency_ms;
- priv->latency_ms = new_latency;
- priv->latency_ns = priv->latency_ms * GST_MSECOND;
- wfd_rtp_buffer_set_delay (priv->jbuf, priv->latency_ns);
- JBUF_UNLOCK (priv);
-
- /* post message if latency changed, this will inform the parent pipeline
- * that a latency reconfiguration is possible/needed. */
- if (new_latency != old_latency) {
- GST_DEBUG_OBJECT (jitterbuffer, "latency changed to: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (new_latency * GST_MSECOND));
-
- gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer),
- gst_message_new_latency (GST_OBJECT_CAST (jitterbuffer)));
- }
- break;
- }
- case PROP_DROP_ON_LATENCY:
- JBUF_LOCK (priv);
- priv->drop_on_latency = g_value_get_boolean (value);
- JBUF_UNLOCK (priv);
- break;
- case PROP_TS_OFFSET:
- JBUF_LOCK (priv);
- priv->ts_offset = g_value_get_int64 (value);
- priv->ts_discont = TRUE;
- JBUF_UNLOCK (priv);
- break;
- case PROP_DO_LOST:
- JBUF_LOCK (priv);
- priv->do_lost = g_value_get_boolean (value);
- JBUF_UNLOCK (priv);
- break;
- case PROP_MODE:
- JBUF_LOCK (priv);
- wfd_rtp_buffer_set_mode (priv->jbuf, g_value_get_enum (value));
- JBUF_UNLOCK (priv);
- break;
- case PROP_DO_RETRANSMISSION:
- JBUF_LOCK (priv);
- priv->do_retransmission = g_value_get_boolean (value);
- JBUF_UNLOCK (priv);
- break;
- case PROP_RTX_DELAY:
- JBUF_LOCK (priv);
- priv->rtx_delay = g_value_get_int (value);
- JBUF_UNLOCK (priv);
- break;
- case PROP_RTX_DELAY_REORDER:
- JBUF_LOCK (priv);
- priv->rtx_delay_reorder = g_value_get_int (value);
- JBUF_UNLOCK (priv);
- break;
- case PROP_RTX_RETRY_TIMEOUT:
- JBUF_LOCK (priv);
- priv->rtx_retry_timeout = g_value_get_int (value);
- JBUF_UNLOCK (priv);
- break;
- case PROP_RTX_RETRY_PERIOD:
- JBUF_LOCK (priv);
- priv->rtx_retry_period = g_value_get_int (value);
- JBUF_UNLOCK (priv);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_wfd_rtp_buffer_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec)
-{
- GstWfdRTPBuffer *jitterbuffer;
- GstWfdRTPBufferPrivate *priv;
-
- jitterbuffer = GST_WFD_RTP_BUFFER (object);
- priv = jitterbuffer->priv;
-
- switch (prop_id) {
- case PROP_LATENCY:
- JBUF_LOCK (priv);
- g_value_set_uint (value, priv->latency_ms);
- JBUF_UNLOCK (priv);
- break;
- case PROP_DROP_ON_LATENCY:
- JBUF_LOCK (priv);
- g_value_set_boolean (value, priv->drop_on_latency);
- JBUF_UNLOCK (priv);
- break;
- case PROP_TS_OFFSET:
- JBUF_LOCK (priv);
- g_value_set_int64 (value, priv->ts_offset);
- JBUF_UNLOCK (priv);
- break;
- case PROP_DO_LOST:
- JBUF_LOCK (priv);
- g_value_set_boolean (value, priv->do_lost);
- JBUF_UNLOCK (priv);
- break;
- case PROP_MODE:
- JBUF_LOCK (priv);
- g_value_set_enum (value, wfd_rtp_buffer_get_mode (priv->jbuf));
- JBUF_UNLOCK (priv);
- break;
- case PROP_PERCENT:
- {
- gint percent;
-
- JBUF_LOCK (priv);
- if (priv->srcresult != GST_FLOW_OK)
- percent = 100;
- else
- percent = wfd_rtp_buffer_get_percent (priv->jbuf);
-
- g_value_set_int (value, percent);
- JBUF_UNLOCK (priv);
- break;
- }
- case PROP_DO_RETRANSMISSION:
- JBUF_LOCK (priv);
- g_value_set_boolean (value, priv->do_retransmission);
- JBUF_UNLOCK (priv);
- break;
- case PROP_RTX_DELAY:
- JBUF_LOCK (priv);
- g_value_set_int (value, priv->rtx_delay);
- JBUF_UNLOCK (priv);
- break;
- case PROP_RTX_DELAY_REORDER:
- JBUF_LOCK (priv);
- g_value_set_int (value, priv->rtx_delay_reorder);
- JBUF_UNLOCK (priv);
- break;
- case PROP_RTX_RETRY_TIMEOUT:
- JBUF_LOCK (priv);
- g_value_set_int (value, priv->rtx_retry_timeout);
- JBUF_UNLOCK (priv);
- break;
- case PROP_RTX_RETRY_PERIOD:
- JBUF_LOCK (priv);
- g_value_set_int (value, priv->rtx_retry_period);
- JBUF_UNLOCK (priv);
- break;
- case PROP_STATS:
- g_value_take_boxed (value,
- gst_wfd_rtp_buffer_create_stats (jitterbuffer));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static GstStructure *
-gst_wfd_rtp_buffer_create_stats (GstWfdRTPBuffer * jbuf)
-{
- GstStructure *s;
-
- JBUF_LOCK (jbuf->priv);
- s = gst_structure_new ("application/x-rtp-jitterbuffer-stats",
- "rtx-count", G_TYPE_UINT64, jbuf->priv->num_rtx_requests,
- "rtx-success-count", G_TYPE_UINT64, jbuf->priv->num_rtx_success,
- "rtx-per-packet", G_TYPE_DOUBLE, jbuf->priv->avg_rtx_num,
- "rtx-rtt", G_TYPE_UINT64, jbuf->priv->avg_rtx_rtt, NULL);
- JBUF_UNLOCK (jbuf->priv);
-
- return s;
-}
#define _SET_FRAMERATE_TO_CAPS 1
-
-#define GST_TYPE_WFD_RTSP_NAT_METHOD (gst_wfd_rtsp_nat_method_get_type())
-static GType
-gst_wfd_rtsp_nat_method_get_type (void)
-{
- static GType wfd_rtsp_nat_method_type = 0;
- static const GEnumValue wfd_rtsp_nat_method[] = {
- {GST_WFD_RTSP_NAT_NONE, "None", "none"},
- {GST_WFD_RTSP_NAT_DUMMY, "Send Dummy packets", "dummy"},
- {0, NULL, NULL},
- };
-
- if (!wfd_rtsp_nat_method_type) {
- wfd_rtsp_nat_method_type =
- g_enum_register_static ("GstWFDRTSPNatMethod", wfd_rtsp_nat_method);
- }
- return wfd_rtsp_nat_method_type;
-}
-
/* properties default values */
#define DEFAULT_LOCATION NULL
#define DEFAULT_DEBUG FALSE
#define DEFAULT_RETRY 20
#define DEFAULT_TCP_TIMEOUT 20000000
-#define DEFAULT_NAT_METHOD GST_WFD_RTSP_NAT_DUMMY
#define DEFAULT_PROXY NULL
#define DEFAULT_RTP_BLOCKSIZE 0
#define DEFAULT_USER_ID NULL
PROP_DEBUG,
PROP_RETRY,
PROP_TCP_TIMEOUT,
- PROP_NAT_METHOD,
PROP_PROXY,
PROP_RTP_BLOCKSIZE,
PROP_USER_ID,
#define CMD_CLOSE 3
#define CMD_WAIT 4
#define CMD_LOOP 5
-#define CMD_SET_PARAM 6
+#define CMD_SEND_REQUEST 6
#define GST_ELEMENT_PROGRESS(el, type, code, text) \
G_STMT_START { \
static gboolean gst_wfdrtspsrc_setup_auth (GstWFDRTSPSrc * src,
GstRTSPMessage * response);
-static GstRTSPResult gst_wfdrtspsrc_send_cb (GstRTSPExtension * ext,
- GstRTSPMessage * request, GstRTSPMessage * response, GstWFDRTSPSrc * src);
-
-
static void gst_wfdrtspsrc_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static gboolean gst_wfdrtspsrc_uri_set_uri (GstURIHandler * handler,
static gboolean gst_wfdrtspsrc_parse_methods (GstWFDRTSPSrc * src, GstRTSPMessage * response);
-static GstRTSPResult gst_wfdrtspsrc_send_keep_alive (GstWFDRTSPSrc * src);
-
static void gst_wfdrtspsrc_connection_flush (GstWFDRTSPSrc * src, gboolean flush);
static GstRTSPResult gst_wfdrtspsrc_get_video_parameter(GstWFDRTSPSrc * src, WFDMessage *msg);
static GstRTSPResult gst_wfdrtspsrc_get_audio_parameter(GstWFDRTSPSrc * src, WFDMessage *msg);
#ifdef ENABLE_WFD_MESSAGE
-
-void __wfd_config_message_init(GstWFDRTSPSrc * src)
+static gint
+__wfd_config_message_init(GstWFDRTSPSrc * src)
{
src->message_handle = dlopen(WFD_MESSAGE_FEATURES_PATH, RTLD_LAZY);
if (src->message_handle == NULL)
{
GST_ERROR("failed to init __wfd_config_message_init");
src->extended_wfd_message_support = FALSE;
+ return FALSE;
}
else
{
src->extended_wfd_message_support = TRUE;
}
- return;
+ return TRUE;
}
-void *
+static void *
__wfd_config_message_func(GstWFDRTSPSrc *src, const char *func)
{
return dlsym(src->message_handle, func);
}
-void
+static void
__wfd_config_message_close(GstWFDRTSPSrc *src)
{
GST_DEBUG("close wfd cofig message");
0, G_MAXUINT64, DEFAULT_TCP_TIMEOUT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (gobject_class, PROP_NAT_METHOD,
- g_param_spec_enum ("nat-method", "NAT Method",
- "Method to use for traversing firewalls and NAT",
- GST_TYPE_WFD_RTSP_NAT_METHOD, DEFAULT_NAT_METHOD,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
g_object_class_install_property (gobject_class, PROP_PROXY,
g_param_spec_string ("proxy", "Proxy",
"Proxy settings for HTTP tunneling. Format: [http://][user:passwd@]host[:port]",
gstelement_class->change_state = gst_wfdrtspsrc_change_state;
gstbin_class->handle_message = gst_wfdrtspsrc_handle_message;
-
- gst_wfd_rtsp_ext_list_init ();
}
static GstStructure *
{
GstPadTemplate *template = NULL;
GstPad * outpad = NULL;
-
+ gint result = FALSE;
#ifdef G_OS_WIN32
WSADATA wsa_data;
src->debug = DEFAULT_DEBUG;
src->retry = DEFAULT_RETRY;
gst_wfdrtspsrc_set_tcp_timeout (src, DEFAULT_TCP_TIMEOUT);
- src->nat_method = DEFAULT_NAT_METHOD;
gst_wfdrtspsrc_set_proxy (src, DEFAULT_PROXY);
src->rtp_blocksize = DEFAULT_RTP_BLOCKSIZE;
src->user_id = g_strdup (DEFAULT_USER_ID);
src->user_pw = g_strdup (DEFAULT_USER_PW);
src->user_agent = g_strdup (DEFAULT_USER_AGENT);
- src->ssr_timeout.tv_sec = 0;
- src->ssr_timeout.tv_usec = 0;
- src->is_paused = TRUE;
src->do_stop = FALSE;
src->audio_param = NULL;
src->video_param = NULL;
src->audio_param = gst_wfd_rtsp_set_default_audio_param ();
src->video_param = gst_wfd_rtsp_set_default_video_param ();
#ifdef ENABLE_WFD_MESSAGE
- __wfd_config_message_init(src);
+ result = __wfd_config_message_init(src);
+ if(result == FALSE)
+ return;
#endif
- /* get a list of all extensions */
- src->extensions = gst_wfd_rtsp_ext_list_get ();
-
- /* connect to send signal */
- gst_wfd_rtsp_ext_list_connect (src->extensions, "send",
- (GCallback) gst_wfdrtspsrc_send_cb, src);
-
/* init lock */
g_rec_mutex_init (&(src->task_rec_lock));
src = GST_WFDRTSPSRC (object);
GST_INFO ("finalize");
- gst_wfd_rtsp_ext_list_free (src->extensions);
gst_rtsp_url_free (src->conninfo.url);
if (src->conninfo.location)
g_free (src->conninfo.location);
case PROP_TCP_TIMEOUT:
gst_wfdrtspsrc_set_tcp_timeout (src, g_value_get_uint64 (value));
break;
- case PROP_NAT_METHOD:
- src->nat_method = g_value_get_enum (value);
- break;
case PROP_PROXY:
gst_wfdrtspsrc_set_proxy (src, g_value_get_string (value));
break;
g_value_set_uint64 (value, timeout);
break;
}
- case PROP_NAT_METHOD:
- g_value_set_enum (value, src->nat_method);
- break;
case PROP_PROXY:
{
gchar *str;
}
static GstRTSPResult
-gst_wfdrtspsrc_prepare_set_param (GstWFDRTSPSrc * src)
+gst_wfdrtspsrc_send_request (GstWFDRTSPSrc * src)
{
GstRTSPMessage request = { 0 };
GstRTSPMessage response = { 0 };
GstRTSPResult res = GST_RTSP_OK;
WFDResult wfd_res = WFD_OK;
- WFDMessage *msg_no= NULL;
+ WFDMessage *wfd_msg= NULL;
GstWFDParam param = WFD_PARAM_NONE;
- guint msglen = 0;
- GString *msglength = NULL;
- gchar *msg = NULL;
+ gchar *rtsp_body = NULL;
+ guint rtsp_body_length = 0;
+ GString *rtsp_body_length_str = NULL;
if (src->wfd_param == WFD_PARAM_NONE) {
GST_ERROR_OBJECT (src, "no wfd messag to be sent...");
if (res < 0)
goto error;
- /* Create set_parameter body to be sent in the request */
- WFDCONFIG_MESSAGE_NEW (&msg_no, wfd_res);
-
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to create wfd message...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ gst_rtsp_message_add_header (&request, GST_RTSP_HDR_USER_AGENT, (const gchar*)src->user_agent);
- WFDCONFIG_MESSAGE_INIT(msg_no, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to init wfd message...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ /* Create set_parameter body to be sent in the request */
+ WFDCONFIG_MESSAGE_NEW (&wfd_msg, error);
+ WFDCONFIG_MESSAGE_INIT(wfd_msg, error);
switch(param) {
-
- case WFD_ROUTE: {
- WFDCONFIG_SET_AUDIO_SINK_TYPE(msg_no, WFD_SECONDARY_SINK, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to set audio sink type...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ case WFD_ROUTE:
+ /* Note : RTSP M10 :
+ * Send RTSP SET_PARAMETER with wfd-route to change the WFD sink at which audio is rendered.
+ * Applies only when both a primary and secondary sinks are in WFD session with a WFD source.
+ */
+ WFDCONFIG_SET_AUDIO_SINK_TYPE(wfd_msg, WFD_SECONDARY_SINK, error);
break;
- }
- case WFD_CONNECTOR_TYPE: {
- WFDCONFIG_SET_CONNECTOR_TYPE(msg_no, WFD_CONNECTOR_VGA, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to set connector type...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ case WFD_CONNECTOR_TYPE:
+ /* Note : RTSP M11 :
+ * Send RTSP SET_PARAMETER with wfd-connector-type to indicate change of active connector type,
+ * when the WFD source and WFD sink support content protection.
+ */
+ WFDCONFIG_SET_CONNECTOR_TYPE(wfd_msg, WFD_CONNECTOR_VGA, error);
break;
- }
- case WFD_STANDBY: {
- WFDCONFIG_SET_STANDBY(msg_no, TRUE, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to set standby...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ case WFD_STANDBY:
+ /* Note : RTSP M12 :
+ * Send RTSP SET_PARAMETER with wfd-stanby to indicate that the sender is entering WFD stanby mode.
+ */
+ WFDCONFIG_SET_STANDBY(wfd_msg, TRUE, error);
break;
- }
- case WFD_IDR_REQUEST: {
- WFDCONFIG_SET_IDR_REQUESTER(msg_no, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to set IDR request...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ case WFD_IDR_REQUEST:
+ /* Note : RTSP M13 :
+ * Send RTSP SET_PARAMETER with wfd-idr-request to request IDR refresh.
+ */
+ WFDCONFIG_SET_IDR_REQUESTER(wfd_msg, error);
break;
- }
- case WFD_UIBC_CAPABILITY: {
- WFDCONFIG_SET_UIBC_CAPABILITY(msg_no, WFD_UIBC_INPUT_CAT_UNKNOWN, 0, NULL, 0, 0, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to set UIBC capability...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ case WFD_UIBC_CAPABILITY:
+ /* Note : RTSP M14 :
+ * Send RTSP SET_PARAMETER with wfd-uibc-capability to select UIBC to be used.
+ */
+ WFDCONFIG_SET_UIBC_CAPABILITY(wfd_msg, WFD_UIBC_INPUT_CAT_UNKNOWN, 0, NULL, 0, 0, error);
break;
- }
- case WFD_UIBC_SETTING: {
- WFDCONFIG_SET_UIBC_STATUS(msg_no, TRUE, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to set UIBC setting...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ case WFD_UIBC_SETTING:
+ /* Note : RTSP M15 :
+ * Send RTSP SET_PARAMETER with wfd-uibc-setting to enable or disable the UIBC.
+ */
+ WFDCONFIG_SET_UIBC_STATUS(wfd_msg, TRUE, error);
break;
- }
default:
GST_ERROR_OBJECT (src, "Unhandled WFD message type...");
break;
}
- WFDCONFIG_MESSAGE_DUMP(msg_no, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to dump wfd message...");
- res = GST_RTSP_ERROR;
- goto error;
- }
+ WFDCONFIG_MESSAGE_DUMP(wfd_msg);
+ WFDCONFIG_MESSAGE_AS_TEXT(wfd_msg, rtsp_body, error);
- WFDCONFIG_MESSAGE_AS_TEXT(msg_no, msg);
- if (msg == NULL) {
- GST_ERROR_OBJECT (src, "Failed to get wfd message as text...");
- res = GST_RTSP_ERROR;
+ if(rtsp_body == NULL)
goto error;
- }
- msglen = strlen(msg);
- msglength = g_string_new ("");
- g_string_append_printf (msglength,"%d",msglen);
+ rtsp_body_length = strlen(rtsp_body);
+ rtsp_body_length_str = g_string_new ("");
+ g_string_append_printf (rtsp_body_length_str,"%d",rtsp_body_length);
- res = gst_rtsp_message_add_header (&request, GST_RTSP_HDR_USER_AGENT, (const gchar*)src->user_agent);
+ GST_DEBUG ("WFD message body: %s", rtsp_body);
- GST_DEBUG ("WFD message body: %s", msg);
/* add content-length type */
- res = gst_rtsp_message_add_header (&request, GST_RTSP_HDR_CONTENT_LENGTH, g_string_free (msglength, FALSE));
- if (res != GST_RTSP_OK) {
- GST_ERROR_OBJECT (src, "Failed to add header to rtsp request...");
- goto error;
- }
+ gst_rtsp_message_add_header (&request, GST_RTSP_HDR_CONTENT_LENGTH, g_string_free (rtsp_body_length_str, FALSE));
/* adding wfdconfig data to request */
- res = gst_rtsp_message_set_body (&request,(guint8 *)msg, msglen);
+ res = gst_rtsp_message_set_body (&request,(guint8 *)rtsp_body, rtsp_body_length);
if (res != GST_RTSP_OK) {
GST_ERROR_OBJECT (src, "Failed to set body to rtsp request...");
goto error;
}
- WFDCONFIG_MESSAGE_FREE(msg_no);
+ WFDCONFIG_MESSAGE_FREE(wfd_msg);
/* send request message */
GST_DEBUG_OBJECT (src, "send reuest...");
/* ERRORS */
error:
{
- if(msg_no)
- WFDCONFIG_MESSAGE_FREE(msg_no);
+ if(wfd_msg)
+ WFDCONFIG_MESSAGE_FREE(wfd_msg);
gst_rtsp_message_unset (&request);
gst_rtsp_message_unset (&response);
+ if(wfd_res != WFD_OK) {
+ GST_ERROR_OBJECT(src, "Message config error : %d", wfd_res);
+ return GST_RTSP_ERROR;
+ }
return res;
}
}
if (gst_structure_has_name (s, "GstWFDIDRRequest")) {
/* Send IDR request */
src->wfd_param = WFD_IDR_REQUEST;
- gst_wfdrtspsrc_loop_send_cmd(src, CMD_SET_PARAM);
+ gst_wfdrtspsrc_loop_send_cmd(src, CMD_SEND_REQUEST);
}
break;
default:
return res;
}
-
-/* send a couple of dummy random packets on the receiver RTP port to the server,
- * this should make a firewall think we initiated the data transfer and
- * hopefully allow packets to go from the sender port to our RTP receiver port */
-static gboolean
-gst_wfdrtspsrc_send_dummy_packets (GstWFDRTSPSrc * src)
-{
- WFDRTSPManager *manager = src->manager;
-
- if (src->nat_method != GST_WFD_RTSP_NAT_DUMMY)
- return TRUE;
-
- if (manager) {
- if (manager->fakesrc && manager->udpsink[0]) {
- GST_DEBUG_OBJECT (src, "sending dummy packet to manager %p", manager);
- gst_element_set_state (manager->udpsink[0], GST_STATE_NULL);
- gst_element_set_state (manager->fakesrc, GST_STATE_NULL);
- gst_element_set_state (manager->udpsink[0], GST_STATE_PLAYING);
- gst_element_set_state (manager->fakesrc, GST_STATE_PLAYING);
- }
- }
-
- return TRUE;
-}
-
-
static void
gst_wfdrtspsrc_configure_caps (GstWFDRTSPSrc * src)
{
res &= gst_pad_send_event (manager->channelpad[1], event);
}
-#if 0
- if (source && manager->udpsrc[2]) {
- gst_event_ref (event);
- res &= gst_element_send_event (manager->udpsrc[2], event);
- } else if (manager->channelpad[2]) {
- gst_event_ref (event);
- if (GST_PAD_IS_SRC (manager->channelpad[2]))
- res &= gst_pad_push_event (manager->channelpad[2], event);
- else
- res &= gst_pad_send_event (manager->channelpad[2], event);
- }
-#endif
-
done:
gst_event_unref (event);
return GST_RTSP_OK;
}
-static GstRTSPResult
-gst_wfdrtspsrc_conninfo_reconnect (GstWFDRTSPSrc * src, GstWFDRTSPConnInfo * info)
-{
- GstRTSPResult res;
-
- GST_DEBUG_OBJECT (src, "reconnecting connection...");
- gst_wfdrtspsrc_conninfo_close (src, info, FALSE);
- res = gst_wfdrtspsrc_conninfo_connect (src, info);
-
- return res;
-}
-
static void
gst_wfdrtspsrc_connection_flush (GstWFDRTSPSrc * src, gboolean flush)
{
gst_wfdrtspsrc_handle_request (GstWFDRTSPSrc * src, GstRTSPConnection * conn,
GstRTSPMessage * request)
{
+ GstRTSPMethod method = GST_RTSP_INVALID;
+ GstRTSPVersion version = GST_RTSP_VERSION_INVALID;
GstRTSPMessage response = { 0 };
GstRTSPResult res = GST_RTSP_OK;
- GstRTSPMethod method;
+ WFDResult wfd_res = WFD_OK;
const gchar *uristr;
- GstRTSPVersion version;
- GstRTSPMethod options;
- gchar *str;
- guint8 *data;
- guint size;
-
- GST_DEBUG_OBJECT (src, "got server request message");
-
- res = gst_wfd_rtsp_ext_list_receive_request (src->extensions, request);
+ guint8 *data = NULL;
+ guint size = 0;
+ WFDMessage *wfd_msg = NULL;
- gst_rtsp_message_parse_request (request, &method, &uristr, &version);
+ res = gst_rtsp_message_parse_request (request, &method, &uristr, &version);
+ if (res < 0)
+ goto send_error;
if (version != GST_RTSP_VERSION_1_0) {
/* we can only handle 1.0 requests */
res = GST_RTSP_ENOTIMPL;
+ goto send_error;
}
+ GST_DEBUG_OBJECT (src, "got %s request", gst_rtsp_method_as_text(method));
+
if (src->debug)
wfd_rtsp_manager_message_dump (request);
switch(method) {
case GST_RTSP_OPTIONS:
{
- gchar *tmp = NULL;
- char *options_str = NULL;
-
- options = GST_RTSP_GET_PARAMETER |GST_RTSP_SET_PARAMETER;
-
- str = gst_rtsp_options_as_text (options);
-
- tmp = g_strdup (", org.wfa.wfd1.0");
-
- GST_LOG ("tmp = %s\n\n", tmp);
-
- options_str = (char *) malloc (strlen(tmp) + strlen (str) + 1);
- if (!options_str) {
- GST_ERROR ("Failed to allocate memory...");
- res = GST_RTSP_ENOMEM;
+ /* Note : RTSP M1 :
+ * A WFD source shall send an RTSP M1 request to a WFD sink to begin the RTSP procedures and a WFD Capability Negotiation.
+ * A WFD sink shall respond with an RTSP M1 response message which contains an RTSP OPTIONS.
+ */
+ res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
+ if (res < 0)
goto send_error;
- } else {
- strncpy (options_str, str, strlen (str));
- strncpy (options_str+strlen(str), tmp, strlen (tmp));
- options_str [strlen(tmp) + strlen (str)] = '\0';
- GST_LOG ("\n\noptions_str = %s\n\n", options_str);
- }
- GST_DEBUG_OBJECT (src, "Creating OPTIONS response");
-
- res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
- gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
- if(res < 0) {
- if(options_str)
- g_free (options_str);
- goto send_error;
- }
- gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, options_str);
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, (const gchar *)"org.wfa.wfd1.0");
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, gst_rtsp_options_as_text(GST_RTSP_SET_PARAMETER));
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, gst_rtsp_options_as_text(GST_RTSP_GET_PARAMETER));
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, gst_rtsp_options_as_text(GST_RTSP_TEARDOWN));
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, gst_rtsp_options_as_text(GST_RTSP_OPTIONS));
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, gst_rtsp_options_as_text(GST_RTSP_PLAY));
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, gst_rtsp_options_as_text(GST_RTSP_SETUP));
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_USER_AGENT, (const gchar*)src->user_agent);
- if(options_str)
- g_free (options_str);
break;
}
case GST_RTSP_GET_PARAMETER:
{
- gchar *msg = NULL;
- guint msglen = 0;
- GString *msglength = NULL;
- WFDMessage *msg3rep = NULL;
- WFDResult wfd_res = WFD_OK;
-
- GST_DEBUG_OBJECT (src, "Got GET_PARAMETER request");
+ gchar *rtsp_body = NULL;
+ guint rtsp_body_length = 0;
+ GString *rtsp_body_length_str = NULL;
res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
- if(res < 0)
+ if (res < 0)
goto send_error;
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE, "text/parameters");
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_USER_AGENT, (const gchar*)src->user_agent);
- gst_rtsp_message_get_body (request, &data, &size);
+ res = gst_rtsp_message_get_body (request, &data, &size);
+ if (res < 0)
+ goto send_error;
- /* Check if message is keep-alive if body is empty*/
- if(size==0) {
- res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, "OK",
- request);
- gst_rtsp_connection_reset_timeout (src->conninfo.connection);
+ if (size==0) {
+ /* Note : RTSP M16 : WFD keep-alive :
+ * The WFD keep-alive function is used to periodically ensure the status of WFD sesion.
+ * A WFD source indicates the timeout value via the "Session:" line in the RTSP M6 response.
+ * A WFD sink shall respond with an RTSP M16 request message upon successful receiving the RTSP M16 request message.
+ */
+ res = gst_rtsp_connection_reset_timeout (src->conninfo.connection);
if (res < 0)
goto send_error;
- break;
- }
- /* Creating WFD parameters supported to send as response for
- * GET_PARAMETER request from server*/
-
- WFDCONFIG_MESSAGE_NEW(&msg3rep, wfd_res);
- if(msg3rep == NULL || wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
- }
-
- WFDCONFIG_MESSAGE_INIT(msg3rep, wfd_res);
- WFDCONFIG_MESSAGE_PARSE_BUFFER(data, size, msg3rep, wfd_res);
- if(wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
- }
- WFDCONFIG_MESSAGE_DUMP(msg3rep, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to dump wfd message...");
+ break;
}
- /* Check if the request message has "wfd_audio_codecs" in it */
- if(msg3rep->audio_codecs) {
+ if(src->extended_wfd_message_support == FALSE)
+ goto message_config_error;
+
+ WFDCONFIG_MESSAGE_NEW(&wfd_msg, message_config_error);
+ WFDCONFIG_MESSAGE_INIT(wfd_msg, message_config_error);
+ WFDCONFIG_MESSAGE_PARSE_BUFFER(data, size, wfd_msg, message_config_error);
+ WFDCONFIG_MESSAGE_DUMP(wfd_msg);
+
+ /* Note : RTSP M3 :
+ * The WFD source shall send RTSP M3 request to the WFD sink to query the WFD sink's attributes and capabilities.
+ * A WFD sink shall respond with an RTSP M3 response message which contains the values of the requested parameters.
+ * The WFD source may query all parameters at once with a single RTSP M3 request message or may send separate RTSP M3 request message.
+ * The WFD sink shall only response with formats and settings that it can accept in the following RTSP M4 message exchnage.
+ */
+ /* Note : wfd-audio-codecs :
+ * The wfd-audio-codecs parameter specifies the audio formats supported in the WFD session.
+ * Valid audio codecs are LPCM, AAC, AC3.
+ * Primary sink should support one of audio codecs.
+ */
+ if(wfd_msg->audio_codecs) {
guint audio_codec = 0;
guint audio_sampling_frequency = 0;
guint audio_channels = 0;
guint audio_latency = 0;
- //reading audio parameters which are set from sink ini file
+
if(src->audio_param != NULL) {
if (gst_structure_has_field (src->audio_param, "audio_codec"))
gst_structure_get_uint (src->audio_param, "audio_codec", &audio_codec);
gst_structure_get_uint (src->audio_param, "audio_sampling_frequency", &audio_sampling_frequency);
}
- WFDCONFIG_SET_SUPPORTED_AUDIO_FORMAT(msg3rep, audio_codec,
- audio_sampling_frequency, audio_channels, 16, audio_latency, wfd_res);
+ WFDCONFIG_SET_SUPPORTED_AUDIO_FORMAT(wfd_msg,
+ audio_codec,
+ audio_sampling_frequency,
+ audio_channels,
+ 16,
+ audio_latency,
+ message_config_error);
}
- /* Check if the request message has "wfd_video_formats" in it */
- if(msg3rep->video_formats) {
+ /* Note : wfd-video-formats :
+ * The wfd-video-formats parameter specifies the supported video resolutions,
+ * H.644 codec profile, level, decoder latency, minimum slice size, slice encoding parameters
+ * and support for video frame rate control (including explicit frame rate change and implicit video frame skipping.
+ */
+ if(wfd_msg->video_formats) {
guint video_codec = 0;
guint video_native_resolution = 0;
guint video_cea_support = 0;
gint video_slice_enc_param = 0;
gint video_framerate_control_support = 0;
- //reading video parameters which are set from sink ini file
if (src->video_param != NULL) {
if (gst_structure_has_field (src->video_param, "video_codec"))
gst_structure_get_uint (src->video_param, "video_codec", &video_codec);
gst_structure_get_int (src->video_param, "video_framerate_control_support", &video_framerate_control_support);
}
-
- WFDCONFIG_SET_SUPPORTED_VIDEO_FORMAT(msg3rep,
+ WFDCONFIG_SET_SUPPORTED_VIDEO_FORMAT(wfd_msg,
video_codec,
WFD_VIDEO_CEA_RESOLUTION,
video_native_resolution,
video_minimum_slicing,
video_slice_enc_param,
video_framerate_control_support,
- wfd_res);
- }
-
- /* Check if the request has "wfd_client_rtp_ports" in it */
- if(msg3rep->client_rtp_ports) {
- /* Hardcoded as of now. This is to comply with dongle port settings.
- This should be derived from gst_wfdrtspsrc_alloc_udp_ports */
- src->primary_rtpport = 19000;
- src->secondary_rtpport = 0;
- WFDCONFIG_SET_PREFERD_RTP_PORT(msg3rep,
- WFD_RTSP_TRANS_RTP,
- WFD_RTSP_PROFILE_AVP,
- WFD_RTSP_LOWER_TRANS_UDP,
- src->primary_rtpport,
- src->secondary_rtpport,
- wfd_res);
+ message_config_error);
}
- if(wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
- }
-
- /* Check if the request has "wfd_3d_video_formats " in it */
- if(msg3rep->video_3d_formats) {
- /* TODO: Set preferred 3d_video_formats */
+ /* Note : wfd-3d-formats :
+ * The wfd-3d-formats parameter specifies the support for stereoscopic video capabilities.
+ */
+ if(wfd_msg->video_3d_formats) {
+ /* TODO : Set preferred video_3d_formats */
wfd_res = WFD_OK;
}
- /* When set preferred 3d_video_formats is done, following block need to be uncommented */
- /*if(wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
- }*/
-
- /* Check if the request has "wfd_content_protection " in it */
- if(msg3rep->content_protection) {
+ /* Note : wfd-content-protection :
+ * The wfd-content-protection parameter specifies whether the WFD sink supports the HDCP system 2.0/2.1 for content protection.
+ */
+ if(wfd_msg->content_protection) {
gint hdcp_version = 0;
gint hdcp_port_no = 0;
- //reading hdcp parameters which are set from sink ini file
+
if (src->hdcp_param != NULL) {
- if (gst_structure_has_field (src->hdcp_param, "hdcp_version")) {
+ if (gst_structure_has_field (src->hdcp_param, "hdcp_version"))
gst_structure_get_int (src->hdcp_param, "hdcp_version", &hdcp_version);
- GST_DEBUG_OBJECT(src, "hdcp version : %d", hdcp_version);
- }
-
- if (gst_structure_has_field (src->hdcp_param, "hdcp_port_no")) {
+ if (gst_structure_has_field (src->hdcp_param, "hdcp_port_no"))
gst_structure_get_int (src->hdcp_param, "hdcp_port_no", &hdcp_port_no);
- GST_DEBUG_OBJECT(src, "hdcp_port_no : %d", hdcp_port_no);
- }
- } else {
- GST_DEBUG_OBJECT(src, "No HDCP");
}
- WFDCONFIG_SET_CONTPROTECTION_TYPE(msg3rep, hdcp_version, (guint32)hdcp_port_no, wfd_res);
- }
- if(wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
+ WFDCONFIG_SET_CONTENT_PROTECTION_TYPE(wfd_msg,
+ hdcp_version,
+ (guint32)hdcp_port_no,
+ message_config_error);
}
- /* Check if the request has "wfd_display_edid" in it */
- if(msg3rep->display_edid) {
+
+ /* Note : wfd-display-edid :
+ * The wfd-display-edid parameter specifies the EDID of the display on which the content will be rendered.
+ * EDID data comes in multiples of 128-byte blocks of EDID data depending on the EDID structure that it supports.
+ * If a WFD sink reports wfd-connector-type as HDMI or DP or UDI, the WFD sink should return the EDID of the display that renders the streamed video.
+ * The WFD sink dongle without an integrated display or with an integrated display that is not being used to render streamed video
+ * shall not set the edid filed of the wfd-display-edid paramter to "none" regardless of whether an external display devices is attached or not.
+ */
+ if(wfd_msg->display_edid) {
/* TODO: Set preferred display_edid */
wfd_res = WFD_OK;
}
- /* when set preferred display_edid following block need to be uncommented. */
- /*if(wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
- }*/
-
- /* Check if the request has "wfd_coupled_sink" in it */
- if(msg3rep->coupled_sink) {
- //wfd_res = wfdconfig_set_coupled_sink(msg3rep, WFD_SINK_NOT_COUPLED, NULL);
+ /* Note : wfd-coupled-sink :
+ * The wfd-coupled-sink parameter is used by a WFD sink to convey its coupled status
+ * and if coupled to another WFD sink, the coupled WFD sink's MAC address
+ */
+ if(wfd_msg->coupled_sink) {
/* To test with dummy coupled sink address */
- WFDCONFIG_SET_COUPLED_SINK(msg3rep,WFD_SINK_COUPLED,(gchar *)"1.0.0.1:435", wfd_res);
+ WFDCONFIG_SET_COUPLED_SINK(wfd_msg,
+ WFD_SINK_COUPLED,
+ (gchar *)"1.0.0.1:435",
+ message_config_error);
}
- if(wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
+ /* Note : wfd-client-rtp-ports :
+ * The wfd-coupled-sink parameter is used by a WFD sink to convey the RTP port(s) that the WFD sink is listening on
+ * and by the a WFD source to indicate how audio, video or both audio and video payload will be encapsulated in the MPEG2-TS stream
+ * transmitted from the WFD source to the WFD sink.
+ */
+ if(wfd_msg->client_rtp_ports) {
+ /* Hardcoded as of now. This is to comply with dongle port settings.
+ This should be derived from gst_wfdrtspsrc_alloc_udp_ports */
+ src->primary_rtpport = 19000;
+ src->secondary_rtpport = 0;
+ WFDCONFIG_SET_PREFERD_RTP_PORT(wfd_msg,
+ WFD_RTSP_TRANS_RTP,
+ WFD_RTSP_PROFILE_AVP,
+ WFD_RTSP_LOWER_TRANS_UDP,
+ src->primary_rtpport,
+ src->secondary_rtpport,
+ message_config_error);
+ }
+
+ /* Note : wfd-I2C :
+ * The wfd-I2C parameter is used by a WFD source to inquire whether a WFD sink supports remote I2C read/write function or not.
+ * If the WFD sink supports remote I2C read/write function, it shall set the value of this parameter to the TCP port number
+ * to be used by the WFD source to exchange remote I2C read/write messaging transactions with the WFD sink.
+ */
+ if(wfd_msg->I2C) {
+ /* TODO */
+ wfd_res = WFD_OK;
+ }
+
+ /* Note : wfd-uibc-capability :
+ * The wfd-I2C parameter describes support for the use input back channel and related attributes.
+ * The WFD source indicates the TCP port number to be used for UIBC in the tcp-port filed in RTSP M4 and/or M14 request messages.
+ * The WFD sink uses "none" for the tcp-port filed of the wfd-uibc-capability paramter, in RTSP M3 response and M14 request message.
+ */
+ if(wfd_msg->uibc_capability) {
+ /* TODO */
+ wfd_res = WFD_OK;
+ }
+
+ /* Note : wfd-connector-type :
+ * The WFD source may send wfd-connector-type parameter to inquire about the connector type that is currently active in the WFD sink.
+ * The WFD sink shall not send wfd-connector-type parameter unless the WFD source support this parameter.
+ * The WFD sink dongle that is not connected to an external display and it is not acting as a WFD sink with embedded display
+ * (to render streamed content) shall return a value of "none". Otherwise, the WFD sink shall choose a non-reserved value.
+ */
+ if(wfd_msg->connector_type) {
+ /* TODO */
+ wfd_res = WFD_OK;
}
- WFDCONFIG_MESSAGE_AS_TEXT(msg3rep, msg);
- msglen = strlen(msg);
- msglength = g_string_new ("");
- g_string_append_printf (msglength,"%d",msglen);
+ /* Note : wfd-standby-resume-capability :
+ * The wfd-standby-resume-capability parameter describes support of both stanby control using
+ * a wfd-stanby parameter and resume control using PLAY and using triggered-method setting PLAY.
+ */
+ if(wfd_msg->standby_resume_capability) {
+ /* TODO */
+ wfd_res = WFD_OK;
+ }
- GST_DEBUG_OBJECT (src, "GET_PARAMETER response message body: %s\n\n", msg);
+ WFDCONFIG_MESSAGE_AS_TEXT(wfd_msg, rtsp_body, message_config_error);
- gst_rtsp_message_set_body (&response, (guint8*) msg, msglen);
+ rtsp_body_length = strlen(rtsp_body);
+ rtsp_body_length_str = g_string_new ("");
+ g_string_append_printf (rtsp_body_length_str,"%d", rtsp_body_length);
- WFDCONFIG_MESSAGE_FREE(msg3rep);
+ //gst_rtsp_message_add_header (&request, GST_RTSP_HDR_CONTENT_LENGTH, g_string_free (rtsp_body_length_str, FALSE));
+
+ res = gst_rtsp_message_set_body (&response, (guint8*)rtsp_body, rtsp_body_length);
+ if (res < 0)
+ goto send_error;
break;
-
}
case GST_RTSP_SET_PARAMETER:
{
- WFDMessage *msg = NULL;
- WFDResult wfd_res = WFD_OK;
-
- GST_DEBUG_OBJECT (src, "Got SET_PARAMETER request");
-
- res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
- gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
+ res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
if (res < 0)
goto send_error;
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_USER_AGENT, (const gchar*)src->user_agent);
- gst_rtsp_message_get_body (request, &data, &size);
- WFDCONFIG_MESSAGE_NEW(&msg, wfd_res);
- if(msg == NULL || wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
+ res = gst_rtsp_message_get_body (request, &data, &size);
+ if (res < 0)
goto send_error;
- }
- WFDCONFIG_MESSAGE_INIT(msg, wfd_res);
- if(wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
- }
- WFDCONFIG_MESSAGE_PARSE_BUFFER(data, size, msg, wfd_res);
- if(wfd_res != WFD_OK) {
- res = GST_RTSP_ERROR;
- goto send_error;
- }
+ if(src->extended_wfd_message_support == FALSE)
+ goto message_config_error;
+
+ WFDCONFIG_MESSAGE_NEW(&wfd_msg, message_config_error);
+ WFDCONFIG_MESSAGE_INIT(wfd_msg, message_config_error);
+ WFDCONFIG_MESSAGE_PARSE_BUFFER(data, size, wfd_msg, message_config_error);
+ WFDCONFIG_MESSAGE_DUMP(wfd_msg);
+
+ /* Note : RTSP M4 :
+ */
+ /* Note : wfd-trigger-method :
+ * The wfd-trigger-method parameter is used by a WFD source to trigger the WFD sink to initiate an operation with the WFD source.
+ */
+ if (wfd_msg->trigger_method) {
+ WFDTrigger trigger = WFD_TRIGGER_UNKNOWN;
+
+ WFDCONFIG_GET_TRIGGER_TYPE(wfd_msg, &trigger, message_config_error);
+
+ res = gst_wfdrtspsrc_connection_send (src, conn, &response, NULL);
+ if (res < 0)
+ goto send_error;
- WFDCONFIG_MESSAGE_DUMP(msg, wfd_res);
- if (wfd_res != WFD_OK) {
- GST_ERROR_OBJECT (src, "Failed to dump wfd message...");
+ GST_DEBUG_OBJECT (src, "got trigger method for %s", GST_STR_NULL(wfd_msg->trigger_method->wfd_trigger_method));
+ switch(trigger) {
+ case WFD_TRIGGER_PAUSE:
+ gst_wfdrtspsrc_loop_send_cmd (src, CMD_PAUSE);
+ break;
+ case WFD_TRIGGER_PLAY:
+ gst_wfdrtspsrc_loop_send_cmd (src, CMD_PLAY);
+ break;
+ case WFD_TRIGGER_TEARDOWN:
+ gst_wfdrtspsrc_loop_send_cmd (src, CMD_CLOSE);
+ break;
+ case WFD_TRIGGER_SETUP:
+ if (!gst_wfdrtspsrc_setup (src))
+ goto setup_failed;
+ break;
+ default:
+ break;
+ }
+ goto done;
}
- if (msg->audio_codecs || msg->video_formats || msg->video_3d_formats) {
- GstStructure *stream_info = NULL;
- stream_info = gst_structure_new ("WFDStreamInfo", NULL, NULL);
- /* check audio codec */
- if(msg->audio_codecs) {
- gst_wfdrtspsrc_get_audio_parameter(src, msg);
+ if (wfd_msg->audio_codecs || wfd_msg->video_formats || wfd_msg->video_3d_formats) {
+ GstStructure *stream_info = gst_structure_new ("WFDStreamInfo", NULL, NULL);
+
+ if(wfd_msg->audio_codecs) {
+ res = gst_wfdrtspsrc_get_audio_parameter(src, wfd_msg);
+ if(res != GST_RTSP_OK) {
+ goto message_config_error;
+ }
gst_structure_set (stream_info,
"audio_format", G_TYPE_STRING, src->audio_format,
NULL);
}
- /* check video formats */
- if(msg->video_formats) {
- gst_wfdrtspsrc_get_video_parameter(src, msg);
+ if(wfd_msg->video_formats) {
+ res = gst_wfdrtspsrc_get_video_parameter(src, wfd_msg);
+ if(res != GST_RTSP_OK) {
+ goto message_config_error;
+ }
gst_structure_set (stream_info,
"video_format", G_TYPE_STRING, "H264",
NULL);
}
- /* check video formats */
- if(msg->video_3d_formats) {
- guint count = 0;
- count = msg->video_3d_formats->count;
- GST_DEBUG_OBJECT (src, "got video 3d formats with %d count.", count);
+ if(wfd_msg->video_3d_formats) {
+ /* TO DO */
}
g_signal_emit (src, gst_wfdrtspsrc_signals[SIGNAL_UPDATE_MEDIA_INFO], 0, stream_info);
}
- /* check av format change timing */
- if (msg->av_format_change_timing) {
- if (src->state != GST_RTSP_STATE_PLAYING) {
- GST_WARNING_OBJECT(src, "could not handle wfd_av_format_change_timing before session is established");
- break;
- } else {
- guint64 pts=0LL, dts=0LL;
- gboolean need_to_flush = FALSE;
+ /* Note : wfd-presentation-url :
+ * The wfd-presentation-url parameter describes the Universial Resource Identified (URI)
+ * to be used in the RTSP Setup (RTSP M6) request message in order to setup the WFD session from the WFD sink to the WFD source.
+ */
+ if(wfd_msg->presentation_url) {
+ gchar *url0 = NULL, *url1 = NULL;
- pts = msg->av_format_change_timing->PTS;
- dts = msg->av_format_change_timing->DTS;
+ WFDCONFIG_GET_PRESENTATION_URL(wfd_msg, &url0, &url1, message_config_error);
+ g_free (src->conninfo.location);
+ src->conninfo.location = g_strdup (url0);
+ /* url1 is ignored as of now */
+ }
+
+ /* Note : wfd-client-rtp-ports :
+ * The wfd-coupled-sink parameter is used by a WFD sink to convey the RTP port(s) that the WFD sink is listening on
+ * and by the a WFD source to indicate how audio, video or both audio and video payload will be encapsulated in the MPEG2-TS stream
+ * transmitted from the WFD source to the WFD sink.
+ */
+ if(wfd_msg->client_rtp_ports) {
+ WFDRTSPTransMode trans = WFD_RTSP_TRANS_UNKNOWN;
+ WFDRTSPProfile profile = WFD_RTSP_PROFILE_UNKNOWN;
+ WFDRTSPLowerTrans lowertrans = WFD_RTSP_LOWER_TRANS_UNKNOWN;
+ guint32 rtp_port0 =0, rtp_port1 =0;
+
+ WFDCONFIG_GET_PREFERD_RTP_PORT(wfd_msg, &trans, &profile, &lowertrans, &rtp_port0, &rtp_port1, message_config_error);
+ }
+
+ /* Note : wfd-preferred-display-mode :
+ * The wfd-preferred-display-mode-supported field in a wfd-video-formats and/or in a wfd-3d-formats parameter in an RTSP M3 response message
+ * indicates whether a WFD sink supports the prefered display mod operation or not.
+ */
+ if(wfd_msg->preferred_display_mode) {
+ }
+
+ /* Note : wfd-av-format-change-timing :
+ * The wfd-av-format-change-timing parameter is used to signal the actual AV format change timing of the streaming data to the WFD sink.
+ * It shall be included in an RTSP M4 request message for WFD capability re-nogotiation after a WFD session has been established.
+ */
+ if (wfd_msg->av_format_change_timing) {
+ guint64 pts=0LL, dts=0LL;
+ gboolean need_to_flush = FALSE;
+
+ WFDCONFIG_GET_AV_FORMAT_CHANGE_TIMING(wfd_msg, &pts, &dts, message_config_error);
+
+ if (src->state == GST_RTSP_STATE_PLAYING) {
GST_DEBUG_OBJECT(src, "change format with PTS[%lld] and DTS[%lld]", pts, dts);
g_signal_emit (src, gst_wfdrtspsrc_signals[SIGNAL_AV_FORMAT_CHANGE], 0, (gpointer)&need_to_flush);
if (need_to_flush) {
- wfd_rtsp_manager_flush(src->manager, TRUE);
- wfd_rtsp_manager_flush(src->manager, FALSE);
+ wfd_rtsp_manager_flush(src->manager, TRUE);
+ wfd_rtsp_manager_flush(src->manager, FALSE);
}
}
}
- if(msg->presentation_url) {
- gchar *url0, *url1;
-
- WFDCONFIG_GET_PRESENTATION_URL(msg, &url0, &url1, wfd_res);
- if(wfd_res == WFD_OK) {
- g_free (src->conninfo.location);
- src->conninfo.location = g_strdup (url0);
- /* url1 is ignored as of now */
- } else {
- res = GST_RTSP_ERROR;
- goto send_error;
- }
+ /* Note : RTSP M10 :
+ */
+ /* Note : wfd-route :
+ * The wfd-route parameter provides a mechanism to specify the destination to which the audio stream is to be routed.
+ */
+ if(wfd_msg->route) {
+ /* TO DO*/
}
- if(msg->standby) {
+ /* Note : RTSP M12 :
+ */
+ /* Note : wfd-standby :
+ * The wfd-standby parameter is used to indicate that the sender is entering WFD stanby mode.
+ */
+ if(wfd_msg->standby) {
gboolean standby_enable = FALSE;
- WFDCONFIG_GET_STANDBY(msg, &standby_enable, wfd_res);
- if(wfd_res == WFD_OK)
- GST_DEBUG("M12 server set param request STANDBY %s", standby_enable?"ENABLE":"DISABLE");
+
+ WFDCONFIG_GET_STANDBY(wfd_msg, &standby_enable, message_config_error);
+
+ GST_DEBUG_OBJECT (src, "wfd source is entering stanby mode");
+ }
+
+ /* Note : RTSP M14 :
+ */
+ /* Note : wfd-uibc-capability :
+ * The wfd-uibc-capability parameter describes support for the use input back channel and related attributes.
+ * The WFD source indicates the TCP port number to be used for UIBC in the tcp-port filed in RTSP M4 and/or M14 request messages.
+ * The WFD sink uses "none" for the tcp-port filed of the wfd-uibc-capability paramter, in RTSP M3 response and M14 request message.
+ */
+ if(wfd_msg->uibc_capability) {
+ guint32 input_category = 0, input_type = 0, input_type_path_count = 0;
+ WFDHIDCTypePathPair *input_pair = NULL;
+ guint32 tcp_port = 0;
+
+ WFDCONFIG_GET_UIBC_CAPABILITY(wfd_msg, &input_category, &input_type,& input_pair, &input_type_path_count, &tcp_port, message_config_error);
+ }
+
+ /* Note : RTSP M15 :
+ */
+ /* Note : wfd-uibc-setting :
+ * The wfd-setting parameter is used to enable and disable the UIBC.
+ * The wfd-setting parameter may be included in the first RTSP M4 request meesage during the WFD capability negotiation,
+ * provided that the RTSP M4 request message contains the wfd-uibc-capability parameter.
+ */
+ if(wfd_msg->uibc_setting) {
+ gboolean uibc_enable = TRUE;
+
+ WFDCONFIG_GET_UIBC_STATUS(wfd_msg, &uibc_enable, message_config_error);
+
+ GST_DEBUG_OBJECT ("UIBC is %s", uibc_enable? "enabled" : "disabled");
}
- /* DEAD CODE It will be used for UIBC implementation
- else if(g_strrstr((gchar*)data,"wfd_standby")) {
- gboolean uibc_enable;
- wfd_res = wfdconfig_get_uibc_status(msg, &uibc_enable);
- if(wfd_res == WFD_OK)
- GST_DEBUG("M15 server set param request for UIBC %s", uibc_enable?"ENABLE":"DISABLE");
- }*/
break;
}
default:
{
- res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, "OK",
- request);
+ res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
if (res < 0)
goto send_error;
+
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_USER_AGENT, (const gchar*)src->user_agent);
+
break;
}
}
- if (src->debug)
- wfd_rtsp_manager_message_dump (&response);
-
- res = gst_wfdrtspsrc_connection_send (src, conn, &response, NULL);
+ res = gst_wfdrtspsrc_connection_send (src, conn, &response, src->ptcp_timeout);
if (res < 0)
goto send_error;
- /* Handling wfd_trigger_method: PAUSE */
- if(method == GST_RTSP_SET_PARAMETER && g_strrstr((gchar*)data,"PAUSE")) {
- GST_DEBUG_OBJECT (src, "trigger PAUSE \n");
- gst_wfdrtspsrc_loop_send_cmd (src, CMD_PAUSE);
- }
-
- /* Handling wfd_trigger_method: RESUME */
- if(method == GST_RTSP_SET_PARAMETER && g_strrstr((gchar*)data,"PLAY")) {
- GST_DEBUG_OBJECT (src, "trigger RESUME \n");
- gst_wfdrtspsrc_loop_send_cmd (src, CMD_PLAY);
- }
-
- /* Handling wfd_trigger_method: TEARDOWN */
- if(method == GST_RTSP_SET_PARAMETER && g_strrstr((gchar*)data,"TEARDOWN")) {
- GstBus *bus;
-
- GST_DEBUG_OBJECT (src, "trigger TEARDOWN \n");
-
- gst_wfdrtspsrc_loop_send_cmd (src, CMD_CLOSE);
-
- bus = gst_element_get_bus(GST_ELEMENT_CAST(src));
- if(!gst_bus_post(bus, gst_message_new_application(GST_OBJECT_CAST(src), gst_structure_new_empty("TEARDOWN")))){
- GST_ERROR_OBJECT(src, "Failed to send TEARDOWN message\n");
- }
- gst_object_unref(bus);
-
- goto exit;
- }
-
- /* Handling wfd_trigger_method: setup */
- if(method == GST_RTSP_SET_PARAMETER && g_strrstr((gchar*)data,"SETUP")) {
- src->state = GST_RTSP_STATE_INIT;
-
- /* setup streams */
- if (!gst_wfdrtspsrc_setup (src))
- goto setup_failed;
-
- src->state = GST_RTSP_STATE_READY;
- }
+done:
+ if (src->debug)
+ wfd_rtsp_manager_message_dump (&response);
-exit:
gst_rtsp_message_unset (request);
gst_rtsp_message_unset (&response);
+ WFDCONFIG_MESSAGE_FREE(wfd_msg);
return GST_RTSP_OK;
/* ERRORS */
setup_failed:
{
+ GST_ERROR_OBJECT(src, "Error: Could not setup(error)");
gst_rtsp_message_unset (request);
gst_rtsp_message_unset (&response);
+ WFDCONFIG_MESSAGE_FREE(wfd_msg);
return GST_RTSP_ERROR;
}
-send_error:
+message_config_error:
{
+ GST_ERROR_OBJECT(src, "Error: Message config error (%d)", wfd_res);
gst_rtsp_message_unset (request);
gst_rtsp_message_unset (&response);
- return res;
- }
-}
-
-/* send server keep-alive */
-static GstRTSPResult
-gst_wfdrtspsrc_send_keep_alive (GstWFDRTSPSrc * src)
-{
- GstRTSPMessage request = { 0 };
- GstRTSPResult res;
- GstRTSPMethod method;
- gchar *control;
-
- GST_INFO_OBJECT (src, "creating server keep-alive");
-
- /* find a method to use for keep-alive */
- if (src->methods & GST_RTSP_GET_PARAMETER)
- method = GST_RTSP_GET_PARAMETER;
- else
- method = GST_RTSP_OPTIONS;
-
- control = src->conninfo.url_str;
-
- if (control == NULL)
- goto no_control;
-
- res = gst_rtsp_message_init_request (&request, method, control);
- if (res < 0)
- goto send_error;
-
- if (src->debug && request.body != NULL)
- wfd_rtsp_manager_message_dump (&request);
-
- res =
- gst_wfdrtspsrc_connection_send (src, src->conninfo.connection, &request,
- NULL);
- if (res < 0)
- goto send_error;
-
- gst_rtsp_connection_reset_timeout (src->conninfo.connection);
- gst_rtsp_message_unset (&request);
-
- return GST_RTSP_OK;
-
- /* ERRORS */
-no_control:
- {
- GST_WARNING_OBJECT (src, "no control url to send keepalive");
- return GST_RTSP_OK;
+ WFDCONFIG_MESSAGE_FREE(wfd_msg);
+ return GST_RTSP_ERROR;
}
send_error:
{
- gchar *str = gst_rtsp_strresult (res);
-
- gst_rtsp_message_unset (&request);
- GST_ELEMENT_WARNING (src, RESOURCE, WRITE, (NULL),
- ("Could not send keep-alive. (%s)", str));
- g_free (str);
+ GST_ERROR_OBJECT(src, "Error: Could not send message");
+ gst_rtsp_message_unset (request);
+ gst_rtsp_message_unset (&response);
+ WFDCONFIG_MESSAGE_FREE(wfd_msg);
return res;
}
}
static GstFlowReturn
gst_wfdrtspsrc_loop_udp (GstWFDRTSPSrc * src)
{
- GstRTSPResult res;
+ GstRTSPResult res = GST_RTSP_OK;
GstRTSPMessage message = { 0 };
- gint retry = 0;
while (TRUE) {
GTimeVal tv_timeout;
/* we got interrupted, see what we have to do */
goto interrupt;
case GST_RTSP_ETIMEOUT:
- /* send keep-alive, ignore the result, a warning will be posted. */
- GST_DEBUG_OBJECT (src, "timeout, sending keep-alive");
- if ((res = gst_wfdrtspsrc_send_keep_alive (src)) == GST_RTSP_EINTR)
- goto interrupt;
- break;
+ /* timeout */
+ break;
case GST_RTSP_EEOF:
- /* server closed the connection. not very fatal for UDP, reconnect and
- * see what happens. */
+ /* server closed the connection.*/
GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL),
("The server closed the connection."));
goto connect_error;
case GST_RTSP_MESSAGE_RESPONSE:
/* we ignore response and data messages */
GST_DEBUG_OBJECT (src, "ignoring response message");
- if (src->debug)
- wfd_rtsp_manager_message_dump (&message);
- if (message.type_data.response.code == GST_RTSP_STS_UNAUTHORIZED) {
- GST_DEBUG_OBJECT (src, "but is Unauthorized response ...");
- if (gst_wfdrtspsrc_setup_auth (src, &message) && !(retry++)) {
- GST_DEBUG_OBJECT (src, "so retrying keep-alive");
- if ((res = gst_wfdrtspsrc_send_keep_alive (src)) == GST_RTSP_EINTR)
- goto interrupt;
- }
- } else {
- retry = 0;
- }
break;
case GST_RTSP_MESSAGE_DATA:
/* we ignore response and data messages */
GstRTSPResult res = GST_RTSP_OK;
GstRTSPStatusCode thecode = GST_RTSP_STS_OK;
- gst_wfd_rtsp_ext_list_before_send (src->extensions, request);
-
GST_DEBUG_OBJECT (src, "sending message");
if (src->debug)
if (thecode != GST_RTSP_STS_OK)
return GST_RTSP_OK;
- gst_wfd_rtsp_ext_list_after_send (src->extensions, request, response);
-
return GST_RTSP_OK;
/* ERRORS */
}
receive_error:
{
- switch (res) {
- case GST_RTSP_EEOF:
- GST_ERROR_OBJECT (src, "server closed connection, doing reconnect");
-#if 0
- if (try == 0) {
- try++;
- /* if reconnect succeeds, try again */
- if ((res = gst_wfdrtspsrc_conninfo_reconnect (src, &src->conninfo)) == 0)
- goto again;
- }
-#endif
- /* only try once after reconnect, then fallthrough and error out */
- default:
- {
- gchar *str = gst_rtsp_strresult (res);
+ gchar *str = gst_rtsp_strresult (res);
- GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
- ("Could not receive message. (%s)", str));
- g_free (str);
- break;
- }
- }
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
+ ("Could not receive message. (%s)", str));
+ g_free (str);
return res;
}
handle_request_failed:
GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), ("%s",
response->type_data.response.reason));
break;
- case GST_RTSP_STS_MOVED_PERMANENTLY:
- case GST_RTSP_STS_MOVE_TEMPORARILY:
- {
- gchar *new_location;
- GstRTSPLowerTrans transports;
-
- GST_DEBUG_OBJECT (src, "got redirection");
- /* if we don't have a Location Header, we must error */
- if (gst_rtsp_message_get_header (response, GST_RTSP_HDR_LOCATION,
- &new_location, 0) < 0)
- break;
-
- /* When we receive a redirect result, we go back to the INIT state after
- * parsing the new URI. The caller should do the needed steps to issue
- * a new setup when it detects this state change. */
- GST_DEBUG_OBJECT (src, "redirection to %s", new_location);
-
- /* save current transports */
- if (src->conninfo.url)
- transports = src->conninfo.url->transports;
- else
- transports = GST_RTSP_LOWER_TRANS_UNKNOWN;
-
- GError *err = NULL;
- gst_wfdrtspsrc_uri_set_uri (GST_URI_HANDLER (src), new_location, &err);
-
- /* set old transports */
- if (src->conninfo.url && transports != GST_RTSP_LOWER_TRANS_UNKNOWN)
- src->conninfo.url->transports = transports;
-
- src->need_redirect = TRUE;
- src->state = GST_RTSP_STATE_INIT;
- res = GST_RTSP_OK;
- break;
- }
case GST_RTSP_STS_NOT_ACCEPTABLE:
case GST_RTSP_STS_NOT_IMPLEMENTED:
case GST_RTSP_STS_METHOD_NOT_ALLOWED:
}
}
-static GstRTSPResult
-gst_wfdrtspsrc_send_cb (GstRTSPExtension * ext, GstRTSPMessage * request,
- GstRTSPMessage * response, GstWFDRTSPSrc * src)
-{
- return gst_wfdrtspsrc_send (src, src->conninfo.connection, request, response,
- NULL);
-}
-
/* parse the response and collect all the supported methods. We need this
* information so that we don't try to send an unsupported request to the
* at least DESCRIBE, SETUP, we always assume it supports PLAY as
* well. */
GST_DEBUG_OBJECT (src, "could not get OPTIONS");
- src->methods = GST_RTSP_DESCRIBE | GST_RTSP_SETUP;
+ src->methods = GST_RTSP_SETUP;
}
/* always assume PLAY, FIXME, extensions should be able to override
* this */
static GstRTSPResult
gst_wfdrtspsrc_create_transports_string (GstWFDRTSPSrc * src, gchar ** transports)
{
- GstRTSPResult res = GST_RTSP_OK;
GString *result;
*transports = NULL;
- res =
- gst_wfd_rtsp_ext_list_get_transports (src->extensions, GST_RTSP_LOWER_TRANS_UDP, transports);
-
- if (res < 0)
- return GST_RTSP_ERROR;
-
GST_DEBUG_OBJECT (src, "got transports %s", GST_STR_NULL (*transports));
/* extension listed transports, use those */
manager->control_connection = src->conninfo.connection;
GST_DEBUG_OBJECT (src, " setup: %p", manager->control_connection);
- /* see if we need to configure this manager */
- if (!gst_wfd_rtsp_ext_list_configure_stream (src->extensions, manager->caps)) {
- GST_DEBUG_OBJECT (src, "disabled by extension");
- goto no_setup;
- }
-
/* skip setup if we have no URL for it */
if (manager->conninfo.location == NULL) {
- GST_DEBUG_OBJECT (src, "no URL for set.");
+ GST_DEBUG_OBJECT (src, "no URL for setup");
goto no_setup;
}
gst_rtsp_message_unset (&response);
}
- gst_wfd_rtsp_ext_list_stream_select (src->extensions, url);
+ src->state = GST_RTSP_STATE_READY;
return TRUE;
if(message.type == GST_RTSP_MESSAGE_REQUEST) {
method = message.type_data.request.method;
- if(method == GST_RTSP_OPTIONS)
- gst_wfdrtspsrc_handle_request (src, src->conninfo.connection, &message);
- else
+ if(method == GST_RTSP_OPTIONS) {
+ res = gst_wfdrtspsrc_handle_request (src, src->conninfo.connection, &message);
+ if (res < GST_RTSP_OK)
+ goto connect_failed;
+ } else
goto methods_error;
+ } else {
+ GST_ERROR_OBJECT (src, "failed to receive options...");
+ goto methods_error;
}
/* create OPTIONS */
/* send OPTIONS */
GST_DEBUG_OBJECT (src, "send options...");
- if (gst_wfdrtspsrc_send (src, src->conninfo.connection, &request, &response,
- NULL) < 0)
+ if ((res = gst_wfdrtspsrc_send (src, src->conninfo.connection, &request, &response,
+ NULL)) < 0)
goto send_error;
/* parse OPTIONS */
}
-
+/* Note : RTSP M1~M6 :
+ * WFD session capability negotiation
+ */
static GstRTSPResult
gst_wfdrtspsrc_open (GstWFDRTSPSrc * src)
{
GstRTSPResult res;
- src->methods =
- GST_RTSP_SETUP | GST_RTSP_PLAY | GST_RTSP_PAUSE | GST_RTSP_TEARDOWN;
-
if ((res = gst_wfdrtspsrc_retrieve_wifi_parameters (src)) < 0)
goto open_failed;
}
}
-
+/* Note : RTSP M8 :
+ * Send TEARDOWN request to WFD source.
+ */
static GstRTSPResult
gst_wfdrtspsrc_close (GstWFDRTSPSrc * src, gboolean only_close)
{
return TRUE;
}
+/* Note : RTSP M7 :
+ * Send PLAY request to WFD source. WFD source begins audio and/or video streaming.
+ */
static GstRTSPResult
gst_wfdrtspsrc_play (GstWFDRTSPSrc * src)
{
gchar *setup_url;
GstRTSPConnection *conn;
- src->is_paused = FALSE;
-
-
GST_DEBUG_OBJECT (src, "PLAY...");
if (!(src->methods & GST_RTSP_PLAY))
if (!src->conninfo.connection || !src->conninfo.connected)
goto done;
- /* send some dummy packets before we activate the receive in the
- * udp sources */
- gst_wfdrtspsrc_send_dummy_packets (src);
-
gst_element_set_state (GST_ELEMENT_CAST (src), GST_STATE_PLAYING);
/* construct a control url */
}
}
+/* Note : RTSP M9 :
+ * Send PAUSE request to WFD source. WFD source pauses the audio video stream(s).
+ */
static GstRTSPResult
gst_wfdrtspsrc_pause (GstWFDRTSPSrc * src)
{
gst_rtsp_message_unset (&request);
gst_rtsp_message_unset (&response);
- src->is_paused = TRUE;
-
no_connection:
src->state = GST_RTSP_STATE_READY;
gst_wfdrtspsrc_connection_flush (src, FALSE);
/* we allow these to be interrupted */
- if (cmd == CMD_LOOP || cmd == CMD_PAUSE || cmd == CMD_SET_PARAM )
+ if (cmd == CMD_LOOP || cmd == CMD_PAUSE || cmd == CMD_SEND_REQUEST )
src->waiting = TRUE;
GST_OBJECT_UNLOCK (src);
case CMD_LOOP:
running = gst_wfdrtspsrc_loop (src);
break;
- case CMD_SET_PARAM:
- ret = gst_wfdrtspsrc_prepare_set_param (src);
+ case CMD_SEND_REQUEST:
+ ret = gst_wfdrtspsrc_send_request (src);
if (ret == GST_RTSP_OK)
running = TRUE;
break;
GST_OBJECT_LOCK (src);
+ src->state = GST_RTSP_STATE_INIT;
+
src->loop_cmd = CMD_WAIT;
if (src->task == NULL) {
WFDAudioFreq audio_frequency = WFD_FREQ_UNKNOWN;
guint audio_bitwidth = 0;
guint32 audio_latency = 0;
+ WFDResult wfd_res = WFD_OK;
WFDCONFIG_GET_PREFERED_AUDIO_FORMAT(msg, &audio_format, &audio_frequency, &audio_channels, &audio_bitwidth, &audio_latency);
+ if(wfd_res != WFD_OK) {
+ GST_ERROR("Failed to get prefered audio format.");
+ return GST_RTSP_ERROR;
+ }
src->audio_format = g_strdup(msg->audio_codecs->list->audio_format);
if(audio_frequency == WFD_FREQ_48000)
guint32 cslice_enc_params = 0;
guint cframe_rate_control = 0;
guint cvLatency = 0;
+ WFDResult wfd_res = WFD_OK;
+
WFDCONFIG_GET_PREFERED_VIDEO_FORMAT(msg, &cvCodec, &cNative, &cNativeResolution,
&cCEAResolution, &cVESAResolution, &cHHResolution,
&cProfile, &cLevel, &cvLatency, &cMaxHeight,
&cMaxWidth, &cmin_slice_size, &cslice_enc_params, &cframe_rate_control);
+ if(wfd_res != WFD_OK) {
+ GST_ERROR("Failed to get prefered video format.");
+ return GST_RTSP_ERROR;
+ }
#if 0
switch(cNative)
{
#define WFD_MESSAGE_SET_SUPPORTED_AUDIO_FORMAT "wfdconfig_set_supported_audio_format"
#define WFD_MESSAGE_SET_SUPPORTED_VIDEO_FORMAT "wfdconfig_set_supported_video_format"
#define WFD_MESSAGE_SET_PREFERD_RTP_PORT "wfdconfig_set_prefered_RTP_ports"
+#define WFD_MESSAGE_GET_PREFERD_RTP_PORT "wfdconfig_get_prefered_RTP_ports"
#define WFD_MESSAGE_SET_CONTPROTECTION_TYPE "wfdconfig_set_contentprotection_type"
#define WFD_MESSAGE_SET_COUPLED_SINK "wfdconfig_set_coupled_sink"
#define WFD_MESSAGE_GET_PRESENTATION_URL "wfdconfig_get_presentation_url"
#define WFD_MESSAGE_GET_UIBC_STATUS "wfdconfig_get_uibc_status"
#define WFD_MESSAGE_GET_PREFERED_AUDIO_FORMAT "wfdconfig_get_prefered_audio_format"
#define WFD_MESSAGE_GET_PREFERED_VIDEO_FORMAT "wfdconfig_get_prefered_video_format"
+#define WFD_MESSAGE_GET_TRIGGER_TYPE "wfdconfig_get_trigger_type"
+#define WFD_MESSAGE_GET_AV_FORMAT_CHANGE_TIMING "wfdconfig_get_av_format_change_timing"
-#define WFDCONFIG_MESSAGE_NEW(msg, result)\
-if (src->extended_wfd_message_support) {\
- WFDResult (*func)(WFDMessage **) = __wfd_config_message_func(src, WFD_MESSAGE_NEW);\
- result = func(msg);\
-}
+#define WFDCONFIG_MESSAGE_NEW(msg, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support) {\
+ WFDResult (*func)(WFDMessage **) = __wfd_config_message_func(src, WFD_MESSAGE_NEW);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg);\
+ if (G_UNLIKELY (wfd_res != WFD_OK) || G_LIKELY((*(msg)) == NULL)) \
+ goto label; \
+ }\
+} G_STMT_END
-#define WFDCONFIG_MESSAGE_INIT(msg, result)\
-if (src->extended_wfd_message_support) {\
- WFDResult (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_INIT);\
- result = func(msg);\
-}
+#define WFDCONFIG_MESSAGE_INIT(msg, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support) {\
+ WFDResult (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_INIT);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
-#define WFDCONFIG_SET_AUDIO_SINK_TYPE(msg, sink_number, result)\
-if (src->extended_wfd_message_support) {\
+#define WFDCONFIG_SET_AUDIO_SINK_TYPE(msg, sink_number, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support) {\
WFDResult (*func)(WFDMessage *, WFDSinkType) = __wfd_config_message_func(src, WFD_MESSAGE_SET_AUDIO_SINK_TYPE);\
- result = func(msg, sink_number);\
-}
-
-#define WFDCONFIG_SET_CONNECTOR_TYPE(msg, connector, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, WFDConnector) = __wfd_config_message_func(src, WFD_MESSAGE_SET_CONNECTOR_TYPE);\
- result = func(msg, connector);\
-}
-
-#define WFDCONFIG_SET_STANDBY(msg, standby_enable, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, gboolean) = __wfd_config_message_func(src, WFD_MESSAGE_SET_STANDBY);\
- result = func(msg, standby_enable);\
-}
-
-#define WFDCONFIG_SET_IDR_REQUESTER(msg, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_SET_IDR_REQUESTER);\
- result = func(msg);\
-}
-
-#define WFDCONFIG_SET_UIBC_CAPABILITY(msg, input_category, inp_type, inp_pair, inp_type_path_count, tcp_port, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, guint32 , guint32 , WFDHIDCTypePathPair *, guint32 , guint32 ) = __wfd_config_message_func(src, WFD_MESSAGE_SET_UIBC_CAPABILITY);\
- result = func(msg, input_category, inp_type, inp_pair, inp_type_path_count, tcp_port);\
-}
-
-#define WFDCONFIG_SET_UIBC_STATUS(msg, uibc_enable, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, gboolean) = __wfd_config_message_func(src, WFD_MESSAGE_SET_UIBC_STATUS);\
- result = func(msg, uibc_enable);\
-}
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, sink_number);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_SET_CONNECTOR_TYPE(msg, connector, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDConnector) = __wfd_config_message_func(src, WFD_MESSAGE_SET_CONNECTOR_TYPE);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, connector);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_SET_STANDBY(msg, standby_enable, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, gboolean) = __wfd_config_message_func(src, WFD_MESSAGE_SET_STANDBY);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, standby_enable);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_SET_IDR_REQUESTER(msg, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_SET_IDR_REQUESTER);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_SET_UIBC_CAPABILITY(msg, input_category, inp_type, inp_pair, inp_type_path_count, tcp_port, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, guint32 , guint32 , WFDHIDCTypePathPair *, guint32 , guint32 ) = __wfd_config_message_func(src, WFD_MESSAGE_SET_UIBC_CAPABILITY);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, input_category, inp_type, inp_pair, inp_type_path_count, tcp_port);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_SET_UIBC_STATUS(msg, uibc_enable, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, gboolean) = __wfd_config_message_func(src, WFD_MESSAGE_SET_UIBC_STATUS);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, uibc_enable);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
#define WFDCONFIG_MESSAGE_FREE(msg)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_FREE);\
- func(msg);\
- msg = NULL;\
-}
-
-#define WFDCONFIG_MESSAGE_DUMP(msg, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_DUMP);\
- result = func(msg);\
-}
-
-#define WFDCONFIG_MESSAGE_AS_TEXT(msg, text)\
-if (src->extended_wfd_message_support){\
- gchar* (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_AS_TEXT);\
- text = func(msg);\
-}
-
-#define WFDCONFIG_MESSAGE_PARSE_BUFFER(data, size, msg, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(const guint8 * , guint , WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_PARSE_BUFFER);\
- result = func(data, size, msg);\
-}
-
-#define WFDCONFIG_SET_SUPPORTED_AUDIO_FORMAT(msg, audio_codec, audio_sampling_frequency, audio_channels, audio_bit_width, audio_latency, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, WFDAudioFormats , guint , guint , guint , guint32 ) = __wfd_config_message_func(src, WFD_MESSAGE_SET_SUPPORTED_AUDIO_FORMAT);\
- result = func(msg, audio_codec, audio_sampling_frequency, audio_channels, audio_bit_width, audio_latency);\
-}
+G_STMT_START { \
+ if (src->extended_wfd_message_support && msg != NULL){\
+ WFDResult (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_FREE);\
+ if(func != NULL) {\
+ func(msg);\
+ msg = NULL;\
+ } else {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ }\
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_MESSAGE_DUMP(msg)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_DUMP);\
+ if(func != NULL) {\
+ func(msg);\
+ } else {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ }\
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_MESSAGE_AS_TEXT(msg, text, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ gchar* (*func)(WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_AS_TEXT);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ text = func(msg);\
+ if (!text)\
+ goto label;\
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_MESSAGE_PARSE_BUFFER(data, size, msg, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(const guint8 * , guint , WFDMessage *) = __wfd_config_message_func(src, WFD_MESSAGE_PARSE_BUFFER);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(data, size, msg);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_SET_SUPPORTED_AUDIO_FORMAT(msg, audio_codec, audio_sampling_frequency, audio_channels, audio_bit_width, audio_latency, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDAudioFormats , guint , guint , guint , guint32 ) = __wfd_config_message_func(src, WFD_MESSAGE_SET_SUPPORTED_AUDIO_FORMAT);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, audio_codec, audio_sampling_frequency, audio_channels, audio_bit_width, audio_latency);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
#define WFDCONFIG_SET_SUPPORTED_VIDEO_FORMAT(msg, video_codec, video_native, video_native_resolution, video_cea_support,\
video_vesa_support, video_hh_support, video_profile, video_level, video_latency, video_vertical_resolution,\
- video_horizontal_resolution, video_minimum_slicing, video_slice_enc_param, video_framerate_control_support, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, WFDVideoCodecs ,\
+ video_horizontal_resolution, video_minimum_slicing, video_slice_enc_param, video_framerate_control_support, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDVideoCodecs ,\
WFDVideoNativeResolution , guint64 ,\
guint64 , guint64 , guint64 ,\
guint , guint , guint32 , guint32 ,\
guint32 , guint32 , guint32 , guint ) = __wfd_config_message_func(src, WFD_MESSAGE_SET_SUPPORTED_VIDEO_FORMAT);\
- result = func(msg,\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg,\
video_codec,\
video_native,\
video_native_resolution,\
video_minimum_slicing,\
video_slice_enc_param,\
video_framerate_control_support);\
-}
-
-#define WFDCONFIG_SET_PREFERD_RTP_PORT(msg, trans, profile, lowertrans, rtp_port0, rtp_port1, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, WFDRTSPTransMode , WFDRTSPProfile , WFDRTSPLowerTrans , guint32 , guint32 ) = __wfd_config_message_func(src, WFD_MESSAGE_SET_PREFERD_RTP_PORT);\
- result = func(msg, trans, profile, lowertrans, rtp_port0, rtp_port1);\
-}
-
-#define WFDCONFIG_SET_CONTPROTECTION_TYPE(msg, hdcp_version, hdcp_port_no, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, WFDHDCPProtection , guint32) = __wfd_config_message_func(src, WFD_MESSAGE_SET_CONTPROTECTION_TYPE);\
- result = func(msg, hdcp_version, (guint32)hdcp_port_no);\
-}
-
-#define WFDCONFIG_SET_COUPLED_SINK(msg, status, sink_address, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, WFDCoupledSinkStatus , gchar *) = __wfd_config_message_func(src, WFD_MESSAGE_SET_COUPLED_SINK);\
- result = func(msg, status,(gchar *)sink_address);\
-}
-
-#define WFDCONFIG_GET_PRESENTATION_URL(msg, wfd_url0, wfd_url1, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, gchar **, gchar **) = __wfd_config_message_func(src, WFD_MESSAGE_GET_PRESENTATION_URL);\
- result = func(msg, wfd_url0, wfd_url1);\
-}
-
-#define WFDCONFIG_GET_STANDBY(msg, standby_enable, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, gboolean *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_STANDBY);\
- result = func(msg, standby_enable);\
-}
-
-#define WFDCONFIG_GET_UIBC_CAPABILITY(msg, input_category, inp_type, inp_pair, inp_type_path_count, tcp_port, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, guint32 *, guint32 *, WFDHIDCTypePathPair **,guint32 *, guint32 *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_UIBC_CAPABILITY);\
- result = func(msg, input_category, inp_type, inp_pair, inp_type_path_count, tcp_port);\
-}
-
-#define WFDCONFIG_GET_UIBC_STATUS(msg, uibc_enable, result)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, gboolean *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_UIBC_STATUS);\
- result = func(msg, uibc_enable);\
-}
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_SET_PREFERD_RTP_PORT(msg, trans, profile, lowertrans, rtp_port0, rtp_port1, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDRTSPTransMode , WFDRTSPProfile , WFDRTSPLowerTrans , guint32 , guint32 ) = __wfd_config_message_func(src, WFD_MESSAGE_SET_PREFERD_RTP_PORT);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, trans, profile, lowertrans, rtp_port0, rtp_port1);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_GET_PREFERD_RTP_PORT(msg, trans, profile, lowertrans, rtp_port0, rtp_port1, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDRTSPTransMode *, WFDRTSPProfile *, WFDRTSPLowerTrans *, guint32 *, guint32 *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_PREFERD_RTP_PORT);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, trans, profile, lowertrans, rtp_port0, rtp_port1);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+
+#define WFDCONFIG_SET_CONTENT_PROTECTION_TYPE(msg, hdcp_version, hdcp_port_no, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDHDCPProtection , guint32) = __wfd_config_message_func(src, WFD_MESSAGE_SET_CONTPROTECTION_TYPE);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, hdcp_version, (guint32)hdcp_port_no);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_SET_COUPLED_SINK(msg, status, sink_address, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDCoupledSinkStatus , gchar *) = __wfd_config_message_func(src, WFD_MESSAGE_SET_COUPLED_SINK);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, status,(gchar *)sink_address);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_GET_PRESENTATION_URL(msg, wfd_url0, wfd_url1, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, gchar **, gchar **) = __wfd_config_message_func(src, WFD_MESSAGE_GET_PRESENTATION_URL);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, wfd_url0, wfd_url1);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_GET_STANDBY(msg, standby_enable, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, gboolean *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_STANDBY);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, standby_enable);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_GET_UIBC_CAPABILITY(msg, input_category, inp_type, inp_pair, inp_type_path_count, tcp_port, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, guint32 *, guint32 *, WFDHIDCTypePathPair **,guint32 *, guint32 *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_UIBC_CAPABILITY);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, input_category, inp_type, inp_pair, inp_type_path_count, tcp_port);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_GET_UIBC_STATUS(msg, uibc_enable, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, gboolean *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_UIBC_STATUS);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, uibc_enable);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
#define WFDCONFIG_GET_PREFERED_AUDIO_FORMAT(msg, audio_format, audio_frequency, audio_channels, audio_bitwidth, audio_latency)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, WFDAudioFormats *, WFDAudioFreq *, WFDAudioChannels *, guint *, guint32 *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_PREFERED_AUDIO_FORMAT);\
- func(msg, audio_format, audio_frequency, audio_channels, audio_bitwidth, audio_latency);\
-}
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDAudioFormats *, WFDAudioFreq *, WFDAudioChannels *, guint *, guint32 *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_PREFERED_AUDIO_FORMAT);\
+ if(func != NULL) {\
+ wfd_res = func(msg, audio_format, audio_frequency, audio_channels, audio_bitwidth, audio_latency);\
+ } else {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ }\
+ }\
+} G_STMT_END
#define WFDCONFIG_GET_PREFERED_VIDEO_FORMAT(msg, cvCodec, cNative, cNativeResolution,\
cCEAResolution, cVESAResolution, cHHResolution, cProfile, cLevel, cvLatency, cMaxHeight,\
cMaxWidth, cmin_slice_size, cslice_enc_params, cframe_rate_control)\
-if (src->extended_wfd_message_support){\
- WFDResult (*func)(WFDMessage *, WFDVideoCodecs *,\
- WFDVideoNativeResolution *, guint64 *,\
- WFDVideoCEAResolution *, WFDVideoVESAResolution *,\
- WFDVideoHHResolution *, WFDVideoH264Profile *,\
- WFDVideoH264Level *, guint32 *, guint32 *,\
- guint32 *, guint32 *, guint32 *, guint *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_PREFERED_VIDEO_FORMAT);\
- func(msg, cvCodec, cNative, cNativeResolution,\
- cCEAResolution, cVESAResolution, cHHResolution,\
- cProfile, cLevel, cvLatency, cMaxHeight,\
- cMaxWidth, cmin_slice_size, cslice_enc_params, cframe_rate_control);\
-}
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDVideoCodecs *,\
+ WFDVideoNativeResolution *, guint64 *,\
+ WFDVideoCEAResolution *, WFDVideoVESAResolution *,\
+ WFDVideoHHResolution *, WFDVideoH264Profile *,\
+ WFDVideoH264Level *, guint32 *, guint32 *,\
+ guint32 *, guint32 *, guint32 *, guint *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_PREFERED_VIDEO_FORMAT);\
+ if(func != NULL) {\
+ wfd_res = func(msg, cvCodec, cNative, cNativeResolution,\
+ cCEAResolution, cVESAResolution, cHHResolution,\
+ cProfile, cLevel, cvLatency, cMaxHeight,\
+ cMaxWidth, cmin_slice_size, cslice_enc_params, cframe_rate_control);\
+ } else {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ }\
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_GET_TRIGGER_TYPE(msg, trigger, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, WFDTrigger *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_TRIGGER_TYPE);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, trigger);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_GET_AV_FORMAT_CHANGE_TIMING(msg, PTS, DTS, label)\
+G_STMT_START { \
+ if (src->extended_wfd_message_support){\
+ WFDResult (*func)(WFDMessage *, guint64 *, guint64 *) = __wfd_config_message_func(src, WFD_MESSAGE_GET_AV_FORMAT_CHANGE_TIMING);\
+ if(func == NULL) {\
+ wfd_res = WFD_NOT_IMPLEMENTED;\
+ goto label;\
+ }\
+ wfd_res = func(msg, PTS, DTS);\
+ if (G_UNLIKELY (wfd_res != WFD_OK)) \
+ goto label; \
+ }\
+} G_STMT_END
+
+#define WFDCONFIG_MESSAGE_CHECK(stmt, label) \
+G_STMT_START { \
+ if (G_UNLIKELY ((wfd_res = (stmt)) != WFD_OK)) \
+ goto label; \
+} G_STMT_END
+
#endif /*__WFD_RTSP_MACRO_H__*/