+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at 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:rtsp-client
+ * @short_description: A client connection state
+ * @see_also: #GstRTSPServer, #GstRTSPThreadPool
+ *
+ * The client object handles the connection with a client for as long as a TCP
+ * connection is open.
+ *
+ * A #GstRTSPWFDClient is created by #GstRTSPServer when a new connection is
+ * accepted and it inherits the #GstRTSPMountPoints, #GstRTSPSessionPool,
+ * #GstRTSPAuth and #GstRTSPThreadPool from the server.
+ *
+ * The client connection should be configured with the #GstRTSPConnection using
+ * gst_rtsp_wfd_client_set_connection() before it can be attached to a #GMainContext
+ * using gst_rtsp_wfd_client_attach(). From then on the client will handle requests
+ * on the connection.
+ *
+ * Use gst_rtsp_wfd_client_session_filter() to iterate or modify all the
+ * #GstRTSPSession objects managed by the client object.
+ *
+ * Last reviewed on 2013-07-11 (1.0.0)
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "rtsp-client-ext.h"
+#include "rtsp-media-factory-wfd.h"
+#include "rtsp-sdp.h"
+#include "rtsp-params.h"
+#include "rtsp-media-ext.h"
+#include "gstwfdmessage-ext.h"
+
+#define GST_RTSP_EXT_CLIENT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_EXT_CLIENT, GstRTSPExtClientPrivate))
+
+struct _GstRTSPExtClientPrivate
+{
+ GstRTSPMediaExt *media;
+ guint resend_packets;
+ guint prev_max_seqnum;
+ guint prev_fraction_lost;
+ guint32 prev_max_packets_lost;
+ gboolean first_rtcp;
+ guint consecutive_low_bitrate_count;
+
+ guint tizen_retransmission_rtp_port;
+ guint tizen_retransmission_rtcp_port;
+ guint tizen_fec_t_max;
+ guint tizen_fec_p_max;
+ guint tizen_latency_mode;
+};
+
+#define WFD_MOUNT_POINT "/wfd1.0/streamid=0"
+#define UNSTABLE_NETWORK_INTERVAL 15
+#define MIN_PORT_NUM 1024
+#define MAX_PORT_NUM 65535
+#define TIZEN_RETRANSMISSION_RTP_PORT_NONE 0
+#define TIZEN_RETRANSMISSION_RTCP_PORT_NONE 0
+#define MIN_FEC_T_NUM 2
+#define MAX_FEC_T_NUM 100
+#define MIN_FEC_P_NUM 2
+#define MAX_FEC_P_NUM 100
+#define TIZEN_T_MAX_NONE 0
+#define TIZEN_P_MAX_NONE 0
+#define TIZEN_USER_AGENT "TIZEN"
+#define DEFAULT_WFD_TIMEOUT 60
+
+GST_DEBUG_CATEGORY_STATIC (rtsp_ext_client_debug);
+#define GST_CAT_DEFAULT rtsp_ext_client_debug
+
+static gboolean ext_configure_client_media (GstRTSPClient * client,
+ GstRTSPMedia * media, GstRTSPStream * stream, GstRTSPContext * ctx);
+static void handle_ext_stats (GstRTSPWFDClient * client, GstStructure * stats);
+static gchar* handle_ext_m3_req_msg (GstRTSPWFDClient * client, gchar * data);
+static void handle_ext_set_param_msg (GstRTSPWFDClient * client, gchar * data);
+
+static void handle_ext_m3_res_msg (GstRTSPWFDClient * client, gchar * data);
+static gchar* handle_ext_m4_req_msg (GstRTSPWFDClient * client, gchar * data);
+
+static void gst_rtsp_ext_client_finalize (GObject * obj);
+
+G_DEFINE_TYPE (GstRTSPExtClient, gst_rtsp_ext_client, GST_TYPE_RTSP_WFD_CLIENT);
+
+static void
+gst_rtsp_ext_client_class_init (GstRTSPExtClientClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstRTSPClientClass *rtsp_client_class;
+ GstRTSPWFDClientClass *wfd_client_class;
+
+ g_type_class_add_private (klass, sizeof (GstRTSPExtClientPrivate));
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ rtsp_client_class = GST_RTSP_CLIENT_CLASS (klass);
+ wfd_client_class = GST_RTSP_WFD_CLIENT_CLASS (klass);
+
+ gobject_class->finalize = gst_rtsp_ext_client_finalize;
+
+ rtsp_client_class->configure_client_media = ext_configure_client_media;
+ wfd_client_class->wfd_rtp_stats = handle_ext_stats;
+ wfd_client_class->wfd_handle_m3_req_msg = handle_ext_m3_req_msg;
+ wfd_client_class->wfd_handle_m3_res_msg = handle_ext_m3_res_msg;
+ wfd_client_class->wfd_handle_m4_req_msg = handle_ext_m4_req_msg;
+ wfd_client_class->wfd_handle_set_param_msg = handle_ext_set_param_msg;
+
+ GST_DEBUG_CATEGORY_INIT (rtsp_ext_client_debug, "rtspextclient", 0,
+ "GstRTSPExtClient");
+}
+
+static void
+gst_rtsp_ext_client_init (GstRTSPExtClient * client)
+{
+ GstRTSPExtClientPrivate *priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (client);
+
+ client->priv = priv;
+ priv->resend_packets = 0;
+ priv->prev_max_seqnum = 0;
+ priv->prev_fraction_lost = 0;
+ priv->prev_max_packets_lost = 0;
+ priv->first_rtcp = FALSE;
+ priv->consecutive_low_bitrate_count = 0;
+ priv->tizen_retransmission_rtp_port = TIZEN_RETRANSMISSION_RTP_PORT_NONE;
+ priv->tizen_retransmission_rtcp_port = TIZEN_RETRANSMISSION_RTCP_PORT_NONE;
+ priv->tizen_fec_t_max = TIZEN_T_MAX_NONE;
+ priv->tizen_fec_p_max = TIZEN_P_MAX_NONE;
+ priv->tizen_latency_mode = GST_WFD_TIZEN_LATENCY_NONE;
+
+ GST_INFO_OBJECT (client, "Client is initialized");
+
+ return;
+}
+
+/* A client is finalized when the connection is broken */
+static void
+gst_rtsp_ext_client_finalize (GObject * obj)
+{
+ GstRTSPExtClient *client = GST_RTSP_EXT_CLIENT (obj);
+// GstRTSPExtClientPrivate *priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (client);
+
+ GST_INFO ("finalize client %p", client);
+
+ G_OBJECT_CLASS (gst_rtsp_ext_client_parent_class)->finalize (obj);
+}
+
+/**
+ * gst_rtsp_ext_client_new:
+ *
+ * Create a new #GstRTSPExtClient instance.
+ *
+ * Returns: a new #GstRTSPExtClient
+ */
+GstRTSPExtClient *
+gst_rtsp_ext_client_new (void)
+{
+ GstRTSPExtClient *result;
+
+ result = g_object_new (GST_TYPE_RTSP_EXT_CLIENT, NULL);
+
+ return result;
+}
+
+static gboolean
+ext_configure_client_media (GstRTSPClient * client, GstRTSPMedia * media,
+ GstRTSPStream * stream, GstRTSPContext * ctx)
+{
+ GstRTSPMediaExt* _media = NULL;
+ GstRTSPExtClient *_client = GST_RTSP_EXT_CLIENT (client);
+ GstRTSPExtClientPrivate *priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (_client);
+
+ _media = GST_RTSP_MEDIA_EXT (media);
+
+ if (GST_IS_RTSP_MEDIA_EXT (_media)) {
+ if (_media != priv->media) {
+ GST_ERROR_OBJECT (client, "Different media!");
+ priv->media = _media;
+ }
+ }
+
+ return GST_RTSP_WFD_CLIENT_CLASS (gst_rtsp_ext_client_parent_class)->
+ configure_client_media (client, media, stream, ctx);
+}
+
+static gboolean
+_set_venc_bitrate (GstRTSPWFDClient * client, gint bitrate)
+{
+ GstRTSPClient *parent_client = GST_RTSP_CLIENT_CAST (client);
+
+ GstRTSPMediaFactory *factory = NULL;
+ GstRTSPMountPoints *mount_points = NULL;
+ gchar *path = NULL;
+ gint matched = 0;
+ gboolean ret = TRUE;
+
+ if (!(mount_points = gst_rtsp_client_get_mount_points (parent_client))) {
+ ret = FALSE;
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no mount points...");
+ goto no_mount_points;
+ }
+
+ path = g_strdup (WFD_MOUNT_POINT);
+ if (!path) {
+ ret = FALSE;
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no path...");
+ goto no_path;
+ }
+
+ if (!(factory = gst_rtsp_mount_points_match (mount_points, path, &matched))) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no factory...");
+ ret = FALSE;
+ goto no_factory;
+ }
+
+ gst_rtsp_media_factory_wfd_set_venc_bitrate (factory, bitrate);
+ ret = TRUE;
+
+ g_object_unref (factory);
+
+no_factory:
+ g_free (path);
+no_path:
+ g_object_unref (mount_points);
+no_mount_points:
+ return ret;
+}
+
+static gboolean
+_get_venc_bitrate (GstRTSPWFDClient * client, gint * bitrate)
+{
+ GstRTSPClient *parent_client = GST_RTSP_CLIENT_CAST (client);
+
+ GstRTSPMediaFactory *factory = NULL;
+ GstRTSPMountPoints *mount_points = NULL;
+ gchar *path = NULL;
+ gint matched = 0;
+ gboolean ret = TRUE;
+
+ if (!(mount_points = gst_rtsp_client_get_mount_points (parent_client))) {
+ ret = FALSE;
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no mount points...");
+ goto no_mount_points;
+ }
+
+ path = g_strdup (WFD_MOUNT_POINT);
+ if (!path) {
+ ret = FALSE;
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no path...");
+ goto no_path;
+ }
+
+ if (!(factory = gst_rtsp_mount_points_match (mount_points, path, &matched))) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no factory...");
+ ret = FALSE;
+ goto no_factory;
+ }
+
+ gst_rtsp_media_factory_wfd_get_venc_bitrate (factory, bitrate);
+ ret = TRUE;
+
+ g_object_unref (factory);
+
+no_factory:
+ g_free (path);
+no_path:
+ g_object_unref (mount_points);
+no_mount_points:
+ return ret;
+}
+
+static gboolean
+_get_config_bitrate (GstRTSPWFDClient * client, guint32 * min, guint32 * max)
+{
+ GstRTSPClient *parent_client = GST_RTSP_CLIENT_CAST (client);
+
+ GstRTSPMediaFactory *factory = NULL;
+ GstRTSPMountPoints *mount_points = NULL;
+ gchar *path = NULL;
+ gint matched = 0;
+ gboolean ret = TRUE;
+
+ if (!(mount_points = gst_rtsp_client_get_mount_points (parent_client))) {
+ ret = FALSE;
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no mount points...");
+ goto no_mount_points;
+ }
+
+ path = g_strdup (WFD_MOUNT_POINT);
+ if (!path) {
+ ret = FALSE;
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no path...");
+ goto no_path;
+ }
+
+ if (!(factory = gst_rtsp_mount_points_match (mount_points, path, &matched))) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no factory...");
+ ret = FALSE;
+ goto no_factory;
+ }
+
+ gst_rtsp_media_factory_wfd_get_config_bitrate (factory, min, max);
+ ret = TRUE;
+
+ g_object_unref (factory);
+
+no_factory:
+ g_free (path);
+no_path:
+ g_object_unref (mount_points);
+no_mount_points:
+ return ret;
+}
+
+static gboolean
+_bitrate_config (GstRTSPWFDClient * client, gint bitrate, guint32 min_bitrate)
+{
+ GstRTSPExtClient *_client = GST_RTSP_EXT_CLIENT (client);
+ GstRTSPExtClientPrivate *priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (_client);
+ gint prev_bitrate;
+
+ _get_venc_bitrate (client, &prev_bitrate);
+
+ if (prev_bitrate != bitrate) {
+ _set_venc_bitrate (client, bitrate);
+ GST_INFO_OBJECT (client, "[UDP] New Bitrate value [%d]", bitrate);
+ }
+
+ if (prev_bitrate == min_bitrate && prev_bitrate == bitrate)
+ priv->consecutive_low_bitrate_count++;
+ else
+ priv->consecutive_low_bitrate_count = 0;
+
+ if (priv->consecutive_low_bitrate_count >= UNSTABLE_NETWORK_INTERVAL) {
+ /* Network congestion happens. Add logic for popup warning or something else */
+ GST_WARNING_OBJECT (client, "Network unstable");
+ priv->consecutive_low_bitrate_count = 0;
+ }
+
+ return TRUE;
+}
+
+static void
+handle_ext_stats (GstRTSPWFDClient * client, GstStructure * stats)
+{
+ GstRTSPExtClient *_client = GST_RTSP_EXT_CLIENT (client);
+ GstRTSPExtClientPrivate *priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (_client);
+ guint latest_resend_packets = 0;
+
+ g_return_val_if_fail (priv != NULL, FALSE);
+
+ latest_resend_packets = gst_rtsp_media_ext_get_resent_packets (priv->media);
+
+ GST_INFO_OBJECT (client, "Re-sent RTP packets : %d", latest_resend_packets);
+
+ /* calculation to decide bitrate */
+ {
+ static gint32 next_k = 40;
+ static gint32 next_p = 0;
+ guint32 min_bitrate = 0;
+ guint32 max_bitrate = 0;
+ guint fraction_lost = 0;
+ guint max_seqnum = 0;
+ gint packetslost;
+ gint bitrate = 0;
+ gint temp_fraction_lost = 0;
+ gint statistics_fraction_lost = 0;
+ gfloat thretholdValue = 0;
+ static gint fraction_lost_MA = 0;
+
+ gst_structure_get_uint (stats, "rb-fractionlost", &fraction_lost);
+ gst_structure_get_uint (stats, "rb-exthighestseq", &max_seqnum);
+ gst_structure_get_int (stats, "rb-packetslost", &packetslost);
+
+ _get_venc_bitrate (client, &bitrate);
+ GST_INFO_OBJECT (client, "[UDP] Current Bitrate value [%d]", bitrate);
+
+ _get_config_bitrate (client, &min_bitrate, &max_bitrate);
+ GST_INFO_OBJECT (client, "[UDP] min [%d], max [%d]", min_bitrate,
+ max_bitrate);
+
+ if (priv->resend_packets == latest_resend_packets)
+ fraction_lost = 0;
+ priv->resend_packets = latest_resend_packets;
+
+ if (priv->prev_max_seqnum == max_seqnum)
+ goto config;
+
+ if (priv->first_rtcp == FALSE) {
+ GST_DEBUG_OBJECT (client, "Ignoring first receiver report");
+ priv->prev_fraction_lost = 0;
+ priv->prev_max_packets_lost = packetslost;
+ priv->prev_max_seqnum = max_seqnum;
+ fraction_lost_MA = 0;
+ priv->first_rtcp = TRUE;
+ return;
+ }
+
+ if (priv->prev_fraction_lost == 0)
+ thretholdValue = 1.0;
+ else
+ thretholdValue = 0.8;
+
+ if (fraction_lost > 0) {
+ temp_fraction_lost = fraction_lost * 100 / 256;
+ GST_DEBUG_OBJECT (client, "fraction lost from sink RR [%d]",
+ temp_fraction_lost);
+ } else {
+ if ((max_seqnum > priv->prev_max_seqnum)
+ && (packetslost > priv->prev_max_packets_lost))
+ temp_fraction_lost =
+ (((packetslost - priv->prev_max_packets_lost) * 100) / (max_seqnum -
+ priv->prev_max_seqnum));
+ GST_DEBUG_OBJECT (client, "fraction lost calculated [%d]",
+ temp_fraction_lost);
+ }
+ statistics_fraction_lost =
+ (gint) (temp_fraction_lost * thretholdValue +
+ priv->prev_fraction_lost * (1 - thretholdValue));
+ fraction_lost_MA =
+ (fraction_lost_MA * 7 + statistics_fraction_lost * 5) / 8;
+
+ if (fraction_lost_MA > 100)
+ fraction_lost_MA = 100;
+
+ GST_DEBUG_OBJECT (client,
+ "statistics fraction lost = %d, fraction lost MA = %d",
+ statistics_fraction_lost, fraction_lost_MA);
+
+ if (temp_fraction_lost > 0) {
+ guint32 temp_change_bandwith_amount = 0;
+ gint32 fec_step = 0;
+
+ if (statistics_fraction_lost >= 5) {
+ temp_change_bandwith_amount = max_bitrate - min_bitrate;
+ fec_step = 10;
+ } else if (temp_fraction_lost >= 3) {
+ temp_change_bandwith_amount = (max_bitrate - min_bitrate) / 2;
+ fec_step = 5;
+ } else {
+ temp_change_bandwith_amount = (max_bitrate - min_bitrate) / 4;
+ fec_step = 3;
+ }
+
+ GST_DEBUG_OBJECT (client,
+ "LOSS case, statistics fraction lost = %d percent, temp change"
+ "bandwith amount = %d bit", statistics_fraction_lost,
+ temp_change_bandwith_amount);
+
+ if (next_p >= 100)
+ next_k -= fec_step;
+ else
+ next_p += fec_step;
+
+ if (next_k < 10)
+ next_k = 10;
+
+ if (next_p > 100)
+ next_p = 100;
+
+ if (bitrate <= min_bitrate) {
+ bitrate = min_bitrate;
+ priv->prev_fraction_lost = statistics_fraction_lost;
+ priv->prev_max_packets_lost = packetslost;
+ goto config;
+ }
+
+ bitrate -= temp_change_bandwith_amount;
+
+ if (bitrate < min_bitrate)
+ bitrate = min_bitrate;
+
+ } else if (0 == temp_fraction_lost && fraction_lost_MA < 1) {
+ gint32 fec_step = 0;
+
+ if (0 == priv->prev_fraction_lost) {
+ bitrate += 512 * 1024;
+ fec_step = 10;
+ } else {
+ bitrate += 100 * 1024;
+ fec_step = 5;
+ }
+
+ if (bitrate > max_bitrate)
+ bitrate = max_bitrate;
+
+ if (next_p <= 0)
+ next_k += fec_step;
+ else
+ next_p -= fec_step;
+
+ if (next_k > 100)
+ next_k = 100;
+
+ if (next_p < 0)
+ next_p = 0;
+
+ if (bitrate >= max_bitrate) {
+ GST_DEBUG_OBJECT (client, "bitrate can not be increased");
+ bitrate = max_bitrate;
+ priv->prev_fraction_lost = statistics_fraction_lost;
+ priv->prev_max_seqnum = max_seqnum;
+ priv->prev_max_packets_lost = packetslost;
+ goto config;
+ }
+
+ }
+
+ priv->prev_fraction_lost = statistics_fraction_lost;
+ priv->prev_max_seqnum = max_seqnum;
+ priv->prev_max_packets_lost = packetslost;
+
+ GST_INFO_OBJECT (client, "final bitrate is %d", bitrate);
+
+ config:
+ _bitrate_config (client, bitrate, min_bitrate);
+ gst_rtsp_media_ext_set_next_param (priv->media, next_k, next_p);
+ }
+}
+
+static gchar *
+handle_ext_m3_req_msg (GstRTSPWFDClient * client, gchar * data)
+{
+ gchar *tmp = NULL;
+ gchar *sink_user_agent = NULL;
+ GstWFDExtMessage *msg = NULL;
+ GstWFDResult wfd_res = GST_WFD_EINVAL;
+ gboolean is_appended = FALSE;
+
+ g_return_if_fail (client != NULL);
+ g_return_val_if_fail (data != NULL, NULL);
+
+ sink_user_agent = gst_rtsp_wfd_client_get_sink_user_agent (client);
+
+ if (sink_user_agent && strstr (sink_user_agent, TIZEN_USER_AGENT)) {
+
+ GST_INFO_OBJECT (client,
+ "Setting tizen extended features on wfd message...");
+
+ wfd_res = gst_wfd_ext_message_new (&msg);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to create wfd message...");
+ goto error;
+ }
+
+ wfd_res = gst_wfd_ext_message_init (msg);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to init wfd message...");
+ goto error;
+ }
+
+ GST_INFO_OBJECT (client,
+ "Setting tizen extended features on wfd message...");
+
+ wfd_res = gst_wfd_ext_message_set_tizen_retransmission (msg, 0, 0);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set tizen retransmission on wfd message...");
+ goto error;
+ }
+
+ wfd_res = gst_wfd_ext_message_set_tizen_fec (msg, 0, 0);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to set tizen fec on wfd message...");
+ goto error;
+ }
+
+ wfd_res = gst_wfd_ext_message_set_tizen_latency_mode (msg, 0);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set tizen latency mode on wfd message...");
+ goto error;
+ }
+
+ tmp = gst_wfd_ext_message_param_names_as_text (msg);
+ if (tmp) {
+ data = g_strconcat (data, tmp, NULL);
+ g_free (tmp);
+ is_appended = TRUE;
+ } else {
+ GST_ERROR_OBJECT (client,
+ "Failed to gst_wfd_ext_message_param_names_as_text");
+ goto error;
+ }
+ }
+ if (msg != NULL)
+ gst_wfd_ext_message_free (msg);
+
+ if (sink_user_agent != NULL)
+ g_free (sink_user_agent);
+
+ if (is_appended == FALSE)
+ return NULL;
+ else
+ return data;
+
+error:
+ if (msg != NULL)
+ gst_wfd_ext_message_free (msg);
+
+ if (sink_user_agent != NULL)
+ g_free (sink_user_agent);
+
+ return NULL;
+}
+
+static void
+handle_ext_m3_res_msg (GstRTSPWFDClient * client, gchar * data)
+{
+ GstWFDExtMessage *msg = NULL;
+ GstRTSPExtClientPrivate *ext_priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (client);
+ GstWFDResult wfd_res = GST_WFD_EINVAL;
+
+ g_return_if_fail (ext_priv != NULL);
+ g_return_val_if_fail (data != NULL, NULL);
+
+ wfd_res = gst_wfd_ext_message_new (&msg);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to create wfd message...");
+ goto error;
+ }
+
+ wfd_res = gst_wfd_ext_message_init (msg);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to init wfd message...");
+ goto error;
+ }
+
+ wfd_res =
+ gst_wfd_ext_message_parse_buffer ((const guint8 *) data, strlen (data),
+ msg);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to parse buffer...");
+ goto error;
+ }
+
+ /* Get tizen extended features from WFD message. */
+ if (msg->tizen_retransmission) {
+
+ guint rtp_port = TIZEN_RETRANSMISSION_RTP_PORT_NONE;
+ guint rtcp_port = TIZEN_RETRANSMISSION_RTCP_PORT_NONE;
+ wfd_res =
+ gst_wfd_ext_message_get_tizen_retransmission (msg, &rtp_port,
+ &rtcp_port);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client,
+ "Failed to get tizen retransmission from wfd message...");
+ goto error;
+ }
+
+ if (rtp_port >= MIN_PORT_NUM && rtp_port <= MAX_PORT_NUM)
+ ext_priv->tizen_retransmission_rtp_port = rtp_port;
+
+ if (rtcp_port >= MIN_PORT_NUM && rtcp_port <= MAX_PORT_NUM)
+ ext_priv->tizen_retransmission_rtcp_port = rtcp_port;
+
+ GST_DEBUG_OBJECT (client, "Tizen retransmission rtp_port[%d] rtcp_port[%d]",
+ ext_priv->tizen_retransmission_rtp_port,
+ ext_priv->tizen_retransmission_rtcp_port);
+ }
+
+ if (msg->tizen_fec) {
+
+ guint fec_t_max = TIZEN_T_MAX_NONE;
+ guint fec_p_max = TIZEN_P_MAX_NONE;
+
+ wfd_res = gst_wfd_ext_message_get_tizen_fec (msg, &fec_t_max, &fec_p_max);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to get tizen fec from wfd message...");
+ goto error;
+ }
+
+ if (fec_t_max >= MIN_FEC_T_NUM && fec_t_max <= MAX_FEC_T_NUM)
+ ext_priv->tizen_fec_t_max = fec_t_max;
+
+ if (fec_p_max >= MIN_FEC_P_NUM && fec_p_max <= MAX_FEC_P_NUM)
+ ext_priv->tizen_fec_p_max = fec_p_max;
+
+ GST_DEBUG_OBJECT (client, "Tizen fec t_max[%d] p_max[%d]",
+ ext_priv->tizen_fec_t_max, ext_priv->tizen_fec_p_max);
+ }
+
+ if (msg->tizen_latency_mode) {
+ wfd_res =
+ gst_wfd_ext_message_get_tizen_latency_mode (msg,
+ &ext_priv->tizen_latency_mode);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client,
+ "Failed to get tizen latency mode on wfd message...");
+ goto error;
+ }
+ GST_DEBUG_OBJECT (client, "Tizen latency mode[%d]",
+ ext_priv->tizen_latency_mode);
+ }
+
+ if (msg != NULL)
+ gst_wfd_ext_message_free (msg);
+
+ return;
+error:
+
+ if (msg != NULL)
+ gst_wfd_ext_message_free (msg);
+
+ return;
+}
+
+static void
+media_ext_constructed (GstRTSPMediaFactory * factory, GstRTSPMedia * media,
+ GstRTSPExtClient * client)
+{
+ GstRTSPExtClientPrivate *priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (client);
+ g_return_if_fail (priv != NULL);
+
+ priv->media = GST_RTSP_MEDIA_EXT (media);
+
+ if (priv->tizen_retransmission_rtp_port != TIZEN_RETRANSMISSION_RTP_PORT_NONE
+ && priv->tizen_retransmission_rtcp_port !=
+ TIZEN_RETRANSMISSION_RTCP_PORT_NONE) {
+ GST_DEBUG_OBJECT (client, "Tizen retransmission rtp_port[%d] rtcp_port[%d]",
+ priv->tizen_retransmission_rtp_port,
+ priv->tizen_retransmission_rtcp_port);
+ gst_rtsp_media_ext_set_extended_mode (priv->media, MEDIA_EXT_MODE_RESEND);
+ gst_rtsp_media_ext_set_retrans_port (priv->media,
+ priv->tizen_retransmission_rtp_port);
+ }
+ if (priv->tizen_fec_t_max != TIZEN_T_MAX_NONE
+ && priv->tizen_fec_p_max != TIZEN_P_MAX_NONE) {
+ GST_DEBUG_OBJECT (client, "Tizen fec t_max[%d] p_max[%d]",
+ priv->tizen_fec_t_max, priv->tizen_fec_p_max);
+ gst_rtsp_media_ext_set_extended_mode (priv->media, MEDIA_EXT_MODE_FEC);
+ gst_rtsp_media_ext_set_fec_value (priv->media, priv->tizen_fec_t_max,
+ priv->tizen_fec_p_max);
+ }
+ if (priv->tizen_latency_mode != GST_WFD_TIZEN_LATENCY_NONE) {
+ GST_DEBUG_OBJECT (client, "Tizen latency mode[%d]",
+ priv->tizen_latency_mode);
+ gst_rtsp_media_ext_set_latency_mode (priv->media, priv->tizen_latency_mode);
+ }
+}
+
+static void
+gst_wfd_ext_listen_media_constructed (GstRTSPWFDClient * client)
+{
+ GstRTSPMediaFactory *factory = NULL;
+ GstRTSPMountPoints *mount_points = NULL;
+ gchar *path = NULL;
+ gint matched = 0;
+ GstRTSPClient *parent_client = GST_RTSP_CLIENT_CAST (client);
+
+ GstRTSPExtClient *_client = GST_RTSP_EXT_CLIENT (client);
+
+ if (!(mount_points = gst_rtsp_client_get_mount_points (parent_client))) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no mount points...");
+ goto no_mount_points;
+ }
+
+ path = g_strdup (WFD_MOUNT_POINT);
+ if (!path) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no path...");
+ goto no_path;
+ }
+
+ if (!(factory = gst_rtsp_mount_points_match (mount_points, path, &matched))) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set negotiated resolution: no factory...");
+ goto no_factory;
+ }
+
+ g_signal_connect (factory, "media-constructed",
+ (GCallback) media_ext_constructed, _client);
+
+no_factory:
+ g_free (path);
+no_path:
+ g_object_unref (mount_points);
+no_mount_points:
+ return;
+}
+
+static void
+handle_ext_set_param_msg (GstRTSPWFDClient * client, gchar * data)
+{
+ GstRTSPExtClientPrivate *priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (client);
+
+ g_return_if_fail (priv != NULL);
+ g_return_val_if_fail (data != NULL, NULL);
+
+ return;
+}
+
+static gchar *
+handle_ext_m4_req_msg (GstRTSPWFDClient * client, gchar * data)
+{
+ GstWFDExtMessage *msg = NULL;
+ gchar *tmp = NULL;
+ GstRTSPExtClientPrivate *ext_priv = GST_RTSP_EXT_CLIENT_GET_PRIVATE (client);
+ GstWFDResult wfd_res = GST_WFD_EINVAL;
+ gboolean is_appended = FALSE;
+
+ g_return_if_fail (ext_priv != NULL);
+ g_return_val_if_fail (data != NULL, NULL);
+
+ wfd_res = gst_wfd_ext_message_new (&msg);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to create wfd message...");
+ goto error;
+ }
+
+ wfd_res = gst_wfd_ext_message_init (msg);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to init wfd message...");
+ goto error;
+ }
+
+ GST_INFO_OBJECT (client, "Setting extended features on wfd message...");
+
+ if (ext_priv->tizen_retransmission_rtp_port !=
+ TIZEN_RETRANSMISSION_RTP_PORT_NONE
+ && ext_priv->tizen_retransmission_rtcp_port !=
+ TIZEN_RETRANSMISSION_RTCP_PORT_NONE) {
+
+ wfd_res =
+ gst_wfd_ext_message_set_tizen_retransmission (msg,
+ ext_priv->tizen_retransmission_rtp_port,
+ ext_priv->tizen_retransmission_rtcp_port);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set tizen retransmission on wfd message...");
+ goto error;
+ }
+ GST_DEBUG_OBJECT (client, "Tizen retransmission rtp_port[%d] rtcp_port[%d]",
+ ext_priv->tizen_retransmission_rtp_port,
+ ext_priv->tizen_retransmission_rtcp_port);
+ }
+
+ if (ext_priv->tizen_fec_t_max != TIZEN_T_MAX_NONE
+ && ext_priv->tizen_fec_p_max != TIZEN_P_MAX_NONE) {
+
+ wfd_res =
+ gst_wfd_ext_message_set_tizen_fec (msg, ext_priv->tizen_fec_t_max,
+ ext_priv->tizen_fec_p_max);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client, "Failed to set tizen fec on wfd message...");
+ goto error;
+ }
+ GST_DEBUG_OBJECT (client, "Tizen fec t_max[%d] p_max[%d]",
+ ext_priv->tizen_fec_t_max, ext_priv->tizen_fec_p_max);
+ }
+
+ if (ext_priv->tizen_latency_mode != GST_WFD_TIZEN_LATENCY_NONE) {
+
+ wfd_res =
+ gst_wfd_ext_message_set_tizen_latency_mode (msg,
+ ext_priv->tizen_latency_mode);
+ if (wfd_res != GST_WFD_OK) {
+ GST_ERROR_OBJECT (client,
+ "Failed to set tizen latency mode on wfd message...");
+ goto error;
+ }
+ GST_DEBUG_OBJECT (client, "Tizen latency mode[%d]",
+ ext_priv->tizen_latency_mode);
+ }
+
+ tmp = gst_wfd_ext_message_as_text (msg);
+ if (tmp) {
+ data = g_strconcat (data, tmp, NULL);
+ g_free (tmp);
+ is_appended = TRUE;
+ } else {
+ GST_ERROR_OBJECT (client, "Failed to gst_wfd_ext_message_as_text");
+ goto error;
+ }
+
+ if (msg != NULL)
+ gst_wfd_ext_message_free (msg);
+
+ gst_wfd_ext_listen_media_constructed (client);
+
+ if (is_appended == FALSE) {
+ return NULL;
+ } else {
+ return data;
+ };
+
+ return data;
+error:
+ if (tmp != NULL)
+ g_free (tmp);
+
+ if (msg != NULL)
+ gst_wfd_ext_message_free (msg);
+
+ return NULL;
+}