3795b42fd3ed1075949cefe3a5b81bae9b2ccaed
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-sdp.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #define GLIB_DISABLE_DEPRECATION_WARNINGS
21
22 /**
23  * SECTION:rtsp-sdp
24  * @short_description: Make SDP messages
25  * @see_also: #GstRTSPMedia
26  *
27  * Last reviewed on 2013-07-11 (1.0.0)
28  */
29
30 #include <string.h>
31
32 #include <gst/net/net.h>
33 #include <gst/sdp/gstmikey.h>
34
35 #include "rtsp-sdp.h"
36
37 static gboolean
38 get_info_from_tags (GstPad * pad, GstEvent ** event, gpointer user_data)
39 {
40   GstSDPMedia *media = (GstSDPMedia *) user_data;
41
42   if (GST_EVENT_TYPE (*event) == GST_EVENT_TAG) {
43     GstTagList *tags;
44     guint bitrate = 0;
45
46     gst_event_parse_tag (*event, &tags);
47
48     if (gst_tag_list_get_scope (tags) != GST_TAG_SCOPE_STREAM)
49       return TRUE;
50
51     if (!gst_tag_list_get_uint (tags, GST_TAG_MAXIMUM_BITRATE,
52             &bitrate) || bitrate == 0)
53       if (!gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &bitrate) ||
54           bitrate == 0)
55         return TRUE;
56
57     /* set bandwidth (kbits/s) */
58     gst_sdp_media_add_bandwidth (media, GST_SDP_BWTYPE_AS, bitrate / 1000);
59
60     return FALSE;
61
62   }
63
64   return TRUE;
65 }
66
67 static void
68 update_sdp_from_tags (GstRTSPStream * stream, GstSDPMedia * stream_media)
69 {
70   GstPad *src_pad;
71
72   src_pad = gst_rtsp_stream_get_srcpad (stream);
73   if (!src_pad)
74     return;
75
76   gst_pad_sticky_events_foreach (src_pad, get_info_from_tags, stream_media);
77
78   gst_object_unref (src_pad);
79 }
80
81 static guint
82 get_roc_from_stats (GstStructure * stats, guint ssrc)
83 {
84   const GValue *va, *v;
85   guint i, len;
86   /* initialize roc to something different than 0, so if we don't get
87      the proper ROC from the encoder, streaming should fail initially. */
88   guint roc = -1;
89
90   va = gst_structure_get_value (stats, "streams");
91   if (!va || !G_VALUE_HOLDS (va, GST_TYPE_ARRAY)) {
92     GST_WARNING ("stats doesn't have a valid 'streams' field");
93     return 0;
94   }
95
96   len = gst_value_array_get_size (va);
97
98   /* look if there's any SSRC that matches. */
99   for (i = 0; i < len; i++) {
100     GstStructure *stream;
101     v = gst_value_array_get_value (va, i);
102     if (v && (stream = g_value_get_boxed (v))) {
103       guint stream_ssrc;
104       gst_structure_get_uint (stream, "ssrc", &stream_ssrc);
105       if (stream_ssrc == ssrc) {
106         gst_structure_get_uint (stream, "roc", &roc);
107         break;
108       }
109     }
110   }
111
112   return roc;
113 }
114
115 static gboolean
116 mikey_add_crypto_sessions (GstRTSPStream * stream, GstMIKEYMessage * msg)
117 {
118   guint i;
119   GObject *session;
120   GstElement *encoder;
121   GValueArray *sources;
122   gboolean roc_found;
123
124   encoder = gst_rtsp_stream_get_srtp_encoder (stream);
125   if (encoder == NULL) {
126     GST_ERROR ("unable to get SRTP encoder from stream %p", stream);
127     return FALSE;
128   }
129
130   session = gst_rtsp_stream_get_rtpsession (stream);
131   if (session == NULL) {
132     GST_ERROR ("unable to get RTP session from stream %p", stream);
133     gst_object_unref (encoder);
134     return FALSE;
135   }
136
137   roc_found = FALSE;
138   g_object_get (session, "sources", &sources, NULL);
139   for (i = 0; sources && (i < sources->n_values); i++) {
140     GValue *val;
141     GObject *source;
142     guint32 ssrc;
143     gboolean is_sender;
144
145     val = g_value_array_get_nth (sources, i);
146     source = (GObject *) g_value_get_object (val);
147
148     g_object_get (source, "ssrc", &ssrc, "is-sender", &is_sender, NULL);
149
150     if (is_sender) {
151       guint32 roc = -1;
152       GstStructure *stats;
153
154       g_object_get (encoder, "stats", &stats, NULL);
155
156       if (stats) {
157         roc = get_roc_from_stats (stats, ssrc);
158         gst_structure_free (stats);
159       }
160
161       roc_found = ! !(roc != -1);
162       if (!roc_found) {
163         GST_ERROR ("unable to obtain ROC for stream %p with SSRC %u",
164             stream, ssrc);
165         goto cleanup;
166       }
167
168       GST_INFO ("stream %p with SSRC %u has a ROC of %u", stream, ssrc, roc);
169
170       gst_mikey_message_add_cs_srtp (msg, 0, ssrc, roc);
171     }
172   }
173
174 cleanup:
175   {
176     g_value_array_free (sources);
177
178     gst_object_unref (encoder);
179     g_object_unref (session);
180     return roc_found;
181   }
182 }
183
184 gboolean
185 gst_rtsp_sdp_make_media (GstSDPMessage * sdp, GstSDPInfo * info,
186     GstRTSPStream * stream, GstCaps * caps, GstRTSPProfile profile)
187 {
188   GstSDPMedia *smedia;
189   gchar *tmp;
190   GstRTSPLowerTrans ltrans;
191   GSocketFamily family;
192   const gchar *addrtype, *proto;
193   gchar *address;
194   guint ttl;
195   GstClockTime rtx_time;
196   gchar *base64;
197   GstMIKEYMessage *mikey_msg;
198
199   gst_sdp_media_new (&smedia);
200
201   if (gst_sdp_media_set_media_from_caps (caps, smedia) != GST_SDP_OK) {
202     goto caps_error;
203   }
204
205   gst_sdp_media_set_port_info (smedia, 0, 1);
206
207   switch (profile) {
208     case GST_RTSP_PROFILE_AVP:
209       proto = "RTP/AVP";
210       break;
211     case GST_RTSP_PROFILE_AVPF:
212       proto = "RTP/AVPF";
213       break;
214     case GST_RTSP_PROFILE_SAVP:
215       proto = "RTP/SAVP";
216       break;
217     case GST_RTSP_PROFILE_SAVPF:
218       proto = "RTP/SAVPF";
219       break;
220     default:
221       proto = "udp";
222       break;
223   }
224   gst_sdp_media_set_proto (smedia, proto);
225
226   if (info->is_ipv6) {
227     addrtype = "IP6";
228     family = G_SOCKET_FAMILY_IPV6;
229   } else {
230     addrtype = "IP4";
231     family = G_SOCKET_FAMILY_IPV4;
232   }
233
234   ltrans = gst_rtsp_stream_get_protocols (stream);
235   if (ltrans == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
236     GstRTSPAddress *addr;
237
238     addr = gst_rtsp_stream_get_multicast_address (stream, family);
239     if (addr == NULL)
240       goto no_multicast;
241
242     address = g_strdup (addr->address);
243     ttl = addr->ttl;
244     gst_rtsp_address_free (addr);
245   } else {
246     ttl = 16;
247     if (info->is_ipv6)
248       address = g_strdup ("::");
249     else
250       address = g_strdup ("0.0.0.0");
251   }
252
253   /* for the c= line */
254   gst_sdp_media_add_connection (smedia, "IN", addrtype, address, ttl, 1);
255   g_free (address);
256
257   /* the config uri */
258   tmp = gst_rtsp_stream_get_control (stream);
259   gst_sdp_media_add_attribute (smedia, "control", tmp);
260   g_free (tmp);
261
262   /* check for srtp */
263   mikey_msg = gst_mikey_message_new_from_caps (caps);
264   if (mikey_msg) {
265     /* add policy '0' for all sending SSRC */
266     if (!mikey_add_crypto_sessions (stream, mikey_msg))
267       goto crypto_sessions_error;
268
269     base64 = gst_mikey_message_base64_encode (mikey_msg);
270     if (base64) {
271       tmp = g_strdup_printf ("mikey %s", base64);
272       g_free (base64);
273       gst_sdp_media_add_attribute (smedia, "key-mgmt", tmp);
274       g_free (tmp);
275     }
276
277     gst_mikey_message_unref (mikey_msg);
278   }
279
280   /* RFC 7273 clock signalling */
281   {
282     GstBin *joined_bin = gst_rtsp_stream_get_joined_bin (stream);
283     GstClock *clock = gst_element_get_clock (GST_ELEMENT_CAST (joined_bin));
284     gchar *ts_refclk = NULL;
285     gchar *mediaclk = NULL;
286     guint rtptime, clock_rate;
287     GstClockTime running_time, base_time, clock_time;
288     GstRTSPPublishClockMode publish_clock_mode =
289         gst_rtsp_stream_get_publish_clock_mode (stream);
290
291     gst_rtsp_stream_get_rtpinfo (stream, &rtptime, NULL, &clock_rate,
292         &running_time);
293     base_time = gst_element_get_base_time (GST_ELEMENT_CAST (joined_bin));
294     g_assert (base_time != GST_CLOCK_TIME_NONE);
295     clock_time = running_time + base_time;
296
297     if (publish_clock_mode != GST_RTSP_PUBLISH_CLOCK_MODE_NONE && clock) {
298       if (GST_IS_NTP_CLOCK (clock) || GST_IS_PTP_CLOCK (clock)) {
299         if (publish_clock_mode == GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET) {
300           guint32 mediaclk_offset;
301
302           /* Calculate RTP time at the clock's epoch. That's the direct offset */
303           clock_time =
304               gst_util_uint64_scale (clock_time, clock_rate, GST_SECOND);
305
306           clock_time &= 0xffffffff;
307           mediaclk_offset =
308               G_GUINT64_CONSTANT (0xffffffff) + rtptime - clock_time;
309           mediaclk = g_strdup_printf ("direct=%u", (guint32) mediaclk_offset);
310         }
311
312         if (GST_IS_NTP_CLOCK (clock)) {
313           gchar *ntp_address;
314           guint ntp_port;
315
316           g_object_get (clock, "address", &ntp_address, "port", &ntp_port,
317               NULL);
318
319           if (ntp_port == 123)
320             ts_refclk = g_strdup_printf ("ntp=%s", ntp_address);
321           else
322             ts_refclk = g_strdup_printf ("ntp=%s:%u", ntp_address, ntp_port);
323
324           g_free (ntp_address);
325         } else {
326           guint64 ptp_clock_id;
327           guint ptp_domain;
328
329           g_object_get (clock, "grandmaster-clock-id", &ptp_clock_id, "domain",
330               &ptp_domain, NULL);
331
332           if (ptp_domain != 0)
333             ts_refclk =
334                 g_strdup_printf
335                 ("ptp=IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X:%u",
336                 (guint) (ptp_clock_id >> 56) & 0xff,
337                 (guint) (ptp_clock_id >> 48) & 0xff,
338                 (guint) (ptp_clock_id >> 40) & 0xff,
339                 (guint) (ptp_clock_id >> 32) & 0xff,
340                 (guint) (ptp_clock_id >> 24) & 0xff,
341                 (guint) (ptp_clock_id >> 16) & 0xff,
342                 (guint) (ptp_clock_id >> 8) & 0xff,
343                 (guint) (ptp_clock_id >> 0) & 0xff, ptp_domain);
344           else
345             ts_refclk =
346                 g_strdup_printf
347                 ("ptp=IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
348                 (guint) (ptp_clock_id >> 56) & 0xff,
349                 (guint) (ptp_clock_id >> 48) & 0xff,
350                 (guint) (ptp_clock_id >> 40) & 0xff,
351                 (guint) (ptp_clock_id >> 32) & 0xff,
352                 (guint) (ptp_clock_id >> 24) & 0xff,
353                 (guint) (ptp_clock_id >> 16) & 0xff,
354                 (guint) (ptp_clock_id >> 8) & 0xff,
355                 (guint) (ptp_clock_id >> 0) & 0xff);
356         }
357       }
358     }
359     if (clock)
360       gst_object_unref (clock);
361
362     if (!ts_refclk)
363       ts_refclk = g_strdup ("local");
364     if (!mediaclk)
365       mediaclk = g_strdup ("sender");
366
367     gst_sdp_media_add_attribute (smedia, "ts-refclk", ts_refclk);
368     gst_sdp_media_add_attribute (smedia, "mediaclk", mediaclk);
369     g_free (ts_refclk);
370     g_free (mediaclk);
371     gst_object_unref (joined_bin);
372   }
373
374   update_sdp_from_tags (stream, smedia);
375
376   if (profile == GST_RTSP_PROFILE_AVPF || profile == GST_RTSP_PROFILE_SAVPF) {
377     if ((rtx_time = gst_rtsp_stream_get_retransmission_time (stream))) {
378       /* ssrc multiplexed retransmit functionality */
379       guint rtx_pt = gst_rtsp_stream_get_retransmission_pt (stream);
380
381       if (rtx_pt == 0) {
382         g_warning ("failed to find an available dynamic payload type. "
383             "Not adding retransmission");
384       } else {
385         gchar *tmp;
386         GstStructure *s;
387         gint caps_pt, caps_rate;
388
389         s = gst_caps_get_structure (caps, 0);
390         if (s == NULL)
391           goto no_caps_info;
392
393         /* get payload type and clock rate */
394         gst_structure_get_int (s, "payload", &caps_pt);
395         gst_structure_get_int (s, "clock-rate", &caps_rate);
396
397         tmp = g_strdup_printf ("%d", rtx_pt);
398         gst_sdp_media_add_format (smedia, tmp);
399         g_free (tmp);
400
401         tmp = g_strdup_printf ("%d rtx/%d", rtx_pt, caps_rate);
402         gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
403         g_free (tmp);
404
405         tmp =
406             g_strdup_printf ("%d apt=%d;rtx-time=%" G_GINT64_FORMAT, rtx_pt,
407             caps_pt, GST_TIME_AS_MSECONDS (rtx_time));
408         gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
409         g_free (tmp);
410       }
411     }
412
413     if (gst_rtsp_stream_get_ulpfec_percentage (stream)) {
414       guint ulpfec_pt = gst_rtsp_stream_get_ulpfec_pt (stream);
415
416       if (ulpfec_pt == 0) {
417         g_warning ("failed to find an available dynamic payload type. "
418             "Not adding ulpfec");
419       } else {
420         gchar *tmp;
421         GstStructure *s;
422         gint caps_pt, caps_rate;
423
424         s = gst_caps_get_structure (caps, 0);
425         if (s == NULL)
426           goto no_caps_info;
427
428         /* get payload type and clock rate */
429         gst_structure_get_int (s, "payload", &caps_pt);
430         gst_structure_get_int (s, "clock-rate", &caps_rate);
431
432         tmp = g_strdup_printf ("%d", ulpfec_pt);
433         gst_sdp_media_add_format (smedia, tmp);
434         g_free (tmp);
435
436         tmp = g_strdup_printf ("%d ulpfec/%d", ulpfec_pt, caps_rate);
437         gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
438         g_free (tmp);
439
440         tmp =
441             g_strdup_printf ("%d apt=%d", ulpfec_pt, caps_pt);
442         gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
443         g_free (tmp);
444       }
445     }
446   }
447
448   gst_sdp_message_add_media (sdp, smedia);
449   gst_sdp_media_free (smedia);
450
451   return TRUE;
452
453   /* ERRORS */
454 caps_error:
455   {
456     gst_sdp_media_free (smedia);
457     GST_ERROR ("unable to set media from caps for stream %d",
458         gst_rtsp_stream_get_index (stream));
459     return FALSE;
460   }
461 no_multicast:
462   {
463     gst_sdp_media_free (smedia);
464     GST_ERROR ("stream %d has no multicast address",
465         gst_rtsp_stream_get_index (stream));
466     return FALSE;
467   }
468 no_caps_info:
469   {
470     gst_sdp_media_free (smedia);
471     GST_ERROR ("caps for stream %d have no structure",
472         gst_rtsp_stream_get_index (stream));
473     return FALSE;
474   }
475 crypto_sessions_error:
476   {
477     gst_sdp_media_free (smedia);
478     GST_ERROR ("unable to add MIKEY crypto sessions for stream %d",
479         gst_rtsp_stream_get_index (stream));
480     return FALSE;
481   }
482 }
483
484 /**
485  * gst_rtsp_sdp_from_media:
486  * @sdp: a #GstSDPMessage
487  * @info: (transfer none): a #GstSDPInfo
488  * @media: (transfer none): a #GstRTSPMedia
489  *
490  * Add @media specific info to @sdp. @info is used to configure the connection
491  * information in the SDP.
492  *
493  * Returns: TRUE on success.
494  */
495 gboolean
496 gst_rtsp_sdp_from_media (GstSDPMessage * sdp, GstSDPInfo * info,
497     GstRTSPMedia * media)
498 {
499   guint i, n_streams;
500   gchar *rangestr;
501   gboolean res;
502
503   n_streams = gst_rtsp_media_n_streams (media);
504
505   rangestr = gst_rtsp_media_get_range_string (media, FALSE, GST_RTSP_RANGE_NPT);
506   if (rangestr == NULL)
507     goto not_prepared;
508
509   gst_sdp_message_add_attribute (sdp, "range", rangestr);
510   g_free (rangestr);
511
512   res = TRUE;
513   for (i = 0; res && (i < n_streams); i++) {
514     GstRTSPStream *stream;
515
516     stream = gst_rtsp_media_get_stream (media, i);
517     res = gst_rtsp_sdp_from_stream (sdp, info, stream);
518     if (!res) {
519       GST_ERROR ("could not get SDP from stream %p", stream);
520       goto sdp_error;
521     }
522   }
523
524   {
525     GstNetTimeProvider *provider;
526
527     if ((provider =
528             gst_rtsp_media_get_time_provider (media, info->server_ip, 0))) {
529       GstClock *clock;
530       gchar *address, *str;
531       gint port;
532
533       g_object_get (provider, "clock", &clock, "address", &address, "port",
534           &port, NULL);
535
536       str = g_strdup_printf ("GstNetTimeProvider %s %s:%d %" G_GUINT64_FORMAT,
537           g_type_name (G_TYPE_FROM_INSTANCE (clock)), address, port,
538           gst_clock_get_time (clock));
539
540       gst_sdp_message_add_attribute (sdp, "x-gst-clock", str);
541       g_free (str);
542       gst_object_unref (clock);
543       g_free (address);
544       gst_object_unref (provider);
545     }
546   }
547
548   return res;
549
550   /* ERRORS */
551 not_prepared:
552   {
553     GST_ERROR ("media %p is not prepared", media);
554     return FALSE;
555   }
556 sdp_error:
557   {
558     GST_ERROR ("could not get SDP from media %p", media);
559     return FALSE;
560   }
561 }
562
563 /**
564  * gst_rtsp_sdp_from_stream:
565  * @sdp: a #GstSDPMessage
566  * @info: (transfer none): a #GstSDPInfo
567  * @stream: (transfer none): a #GstRTSPStream
568  *
569  * Add info from @stream to @sdp.
570  *
571  * Returns: TRUE on success.
572  */
573 gboolean
574 gst_rtsp_sdp_from_stream (GstSDPMessage * sdp, GstSDPInfo * info,
575     GstRTSPStream * stream)
576 {
577   GstCaps *caps;
578   GstRTSPProfile profiles;
579   guint mask;
580   gboolean res;
581
582   caps = gst_rtsp_stream_get_caps (stream);
583
584   if (caps == NULL) {
585     GST_ERROR ("stream %p has no caps", stream);
586     return FALSE;
587   }
588
589   /* make a new media for each profile */
590   profiles = gst_rtsp_stream_get_profiles (stream);
591   mask = 1;
592   res = TRUE;
593   while (res && (profiles >= mask)) {
594     GstRTSPProfile prof = profiles & mask;
595
596     if (prof)
597       res = gst_rtsp_sdp_make_media (sdp, info, stream, caps, prof);
598
599     mask <<= 1;
600   }
601   gst_caps_unref (caps);
602
603   return res;
604 }