gstrtspsrc.c 192 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
/* GStreamer
2
3
 * Copyright (C) <2005,2006> Wim Taymans <wim at fluendo dot com>
 *               <2006> Lutz Mueller <lutz at topfrose dot de>
Wim Taymans's avatar
Wim Taymans committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/*
 * Unless otherwise indicated, Source Code is licensed under MIT license.
 * See further explanation attached in License Statement (distributed in the file
 * LICENSE).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
43
44
45
46
47
48
/**
 * SECTION:element-rtspsrc
 *
 * Makes a connection to an RTSP server and read the data.
 * rtspsrc strictly follows RFC 2326 and therefore does not (yet) support
 * RealMedia/Quicktime/Microsoft extensions.
49
 *
50
51
52
 * RTSP supports transport over TCP or UDP in unicast or multicast mode. By
 * default rtspsrc will negotiate a connection in the following order:
 * UDP unicast/UDP multicast/TCP. The order cannot be changed but the allowed
53
54
 * protocols can be controlled with the #GstRTSPSrc:protocols property.
 *
55
 * rtspsrc currently understands SDP as the format of the session description.
56
 * For each stream listed in the SDP a new rtp_stream%d pad will be created
57
 * with caps derived from the SDP media description. This is a caps of mime type
58
 * "application/x-rtp" that can be connected to any available RTP depayloader
Stefan Kost's avatar
Stefan Kost committed
59
 * element.
60
 *
61
62
 * rtspsrc will internally instantiate an RTP session manager element
 * that will handle the RTCP messages to and from the server, jitter removal,
Stefan Kost's avatar
Stefan Kost committed
63
 * packet reordering along with providing a clock for the pipeline.
64
 * This feature is implemented using the gstrtpbin element.
65
 *
Stefan Kost's avatar
Stefan Kost committed
66
 * rtspsrc acts like a live source and will therefore only generate data in the
67
 * PLAYING state.
68
69
 *
 * <refsect2>
70
 * <title>Example launch line</title>
71
 * |[
72
 * gst-launch rtspsrc location=rtsp://some.server/url ! fakesink
73
74
 * ]| Establish a connection to an RTSP server and send the raw RTP packets to a
 * fakesink.
75
76
 * </refsect2>
 *
Wim Taymans's avatar
Wim Taymans committed
77
 * Last reviewed on 2006-08-18 (0.10.5)
78
 */
Wim Taymans's avatar
Wim Taymans committed
79
80
81
82
83

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

84
#ifdef HAVE_UNISTD_H
Wim Taymans's avatar
Wim Taymans committed
85
#include <unistd.h>
86
#endif /* HAVE_UNISTD_H */
87
#include <stdlib.h>
Wim Taymans's avatar
Wim Taymans committed
88
#include <string.h>
89
90
91
#include <locale.h>
#include <stdio.h>
#include <stdarg.h>
Wim Taymans's avatar
Wim Taymans committed
92

93
#include <gst/sdp/gstsdpmessage.h>
94
#include <gst/rtp/gstrtppayloads.h>
95

96
97
#include "gst/gst-i18n-plugin.h"

Wim Taymans's avatar
Wim Taymans committed
98
99
#include "gstrtspsrc.h"

100
101
102
103
#ifdef G_OS_WIN32
#include <winsock2.h>
#endif

104
GST_DEBUG_CATEGORY_STATIC (rtspsrc_debug);
Wim Taymans's avatar
Wim Taymans committed
105
106
#define GST_CAT_DEFAULT (rtspsrc_debug)

107
static GstStaticPadTemplate rtptemplate = GST_STATIC_PAD_TEMPLATE ("stream%d",
Wim Taymans's avatar
Wim Taymans committed
108
109
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
110
    GST_STATIC_CAPS ("application/x-rtp; application/x-rdt"));
Wim Taymans's avatar
Wim Taymans committed
111

112
113
114
/* templates used internally */
static GstStaticPadTemplate anysrctemplate =
GST_STATIC_PAD_TEMPLATE ("internalsrc%d",
115
116
117
118
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

119
120
121
122
123
124
static GstStaticPadTemplate anysinktemplate =
GST_STATIC_PAD_TEMPLATE ("internalsink%d",
    GST_PAD_SINK,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

Wim Taymans's avatar
Wim Taymans committed
125
126
127
128
129
130
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

131
132
133
134
135
136
137
enum _GstRtspSrcRtcpSyncMode
{
  RTCP_SYNC_ALWAYS,
  RTCP_SYNC_INITIAL,
  RTCP_SYNC_RTP
};

138
139
140
141
142
143
144
145
enum _GstRtspSrcBufferMode
{
  BUFFER_MODE_NONE,
  BUFFER_MODE_SLAVE,
  BUFFER_MODE_BUFFER,
  BUFFER_MODE_AUTO
};

146
147
148
149
150
151
#define GST_TYPE_RTSP_SRC_BUFFER_MODE (gst_rtsp_src_buffer_mode_get_type())
static GType
gst_rtsp_src_buffer_mode_get_type (void)
{
  static GType buffer_mode_type = 0;
  static const GEnumValue buffer_modes[] = {
152
153
154
155
    {BUFFER_MODE_NONE, "Only use RTP timestamps", "none"},
    {BUFFER_MODE_SLAVE, "Slave receiver to sender clock", "slave"},
    {BUFFER_MODE_BUFFER, "Do low/high watermark buffering", "buffer"},
    {BUFFER_MODE_AUTO, "Choose mode depending on stream live", "auto"},
156
157
158
159
160
161
162
163
164
165
    {0, NULL, NULL},
  };

  if (!buffer_mode_type) {
    buffer_mode_type =
        g_enum_register_static ("GstRTSPSrcBufferMode", buffer_modes);
  }
  return buffer_mode_type;
}

166
167
168
169
170
#define DEFAULT_LOCATION         NULL
#define DEFAULT_PROTOCOLS        GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP
#define DEFAULT_DEBUG            FALSE
#define DEFAULT_RETRY            20
#define DEFAULT_TIMEOUT          5000000
171
#define DEFAULT_UDP_BUFFER_SIZE  0x80000
172
#define DEFAULT_TCP_TIMEOUT      20000000
173
#define DEFAULT_LATENCY_MS       2000
174
#define DEFAULT_CONNECTION_SPEED 0
175
#define DEFAULT_NAT_METHOD       GST_RTSP_NAT_DUMMY
176
#define DEFAULT_DO_RTCP          TRUE
Wim Taymans's avatar
Wim Taymans committed
177
#define DEFAULT_PROXY            NULL
178
#define DEFAULT_RTP_BLOCKSIZE    0
Wim Taymans's avatar
Wim Taymans committed
179
180
#define DEFAULT_USER_ID          NULL
#define DEFAULT_USER_PW          NULL
181
#define DEFAULT_BUFFER_MODE      BUFFER_MODE_AUTO
182
#define DEFAULT_PORT_RANGE       NULL
183
#define DEFAULT_SHORT_HEADER     FALSE
Wim Taymans's avatar
Wim Taymans committed
184
185
186
187
188
189
190

enum
{
  PROP_0,
  PROP_LOCATION,
  PROP_PROTOCOLS,
  PROP_DEBUG,
Wim Taymans's avatar
Wim Taymans committed
191
  PROP_RETRY,
192
  PROP_TIMEOUT,
193
  PROP_TCP_TIMEOUT,
194
  PROP_LATENCY,
195
196
  PROP_CONNECTION_SPEED,
  PROP_NAT_METHOD,
197
  PROP_DO_RTCP,
Wim Taymans's avatar
Wim Taymans committed
198
  PROP_PROXY,
199
  PROP_RTP_BLOCKSIZE,
200
201
  PROP_USER_ID,
  PROP_USER_PW,
202
  PROP_BUFFER_MODE,
203
  PROP_PORT_RANGE,
204
  PROP_UDP_BUFFER_SIZE,
205
  PROP_SHORT_HEADER,
206
  PROP_LAST
Wim Taymans's avatar
Wim Taymans committed
207
208
};

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#define GST_TYPE_RTSP_NAT_METHOD (gst_rtsp_nat_method_get_type())
static GType
gst_rtsp_nat_method_get_type (void)
{
  static GType rtsp_nat_method_type = 0;
  static const GEnumValue rtsp_nat_method[] = {
    {GST_RTSP_NAT_NONE, "None", "none"},
    {GST_RTSP_NAT_DUMMY, "Send Dummy packets", "dummy"},
    {0, NULL, NULL},
  };

  if (!rtsp_nat_method_type) {
    rtsp_nat_method_type =
        g_enum_register_static ("GstRTSPNatMethod", rtsp_nat_method);
  }
  return rtsp_nat_method_type;
}

227
static void gst_rtspsrc_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
228

229
230
231
232
233
static void gst_rtspsrc_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_rtspsrc_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

Wim Taymans's avatar
Wim Taymans committed
234
235
static void gst_rtspsrc_uri_handler_init (gpointer g_iface,
    gpointer iface_data);
236
237
238
239

static void gst_rtspsrc_sdp_attributes_to_caps (GArray * attributes,
    GstCaps * caps);

Wim Taymans's avatar
Wim Taymans committed
240
241
242
static gboolean gst_rtspsrc_set_proxy (GstRTSPSrc * rtsp, const gchar * proxy);
static void gst_rtspsrc_set_tcp_timeout (GstRTSPSrc * rtspsrc, guint64 timeout);

243
static GstCaps *gst_rtspsrc_media_to_caps (gint pt, const GstSDPMedia * media);
Wim Taymans's avatar
Wim Taymans committed
244

245
246
static GstStateChangeReturn gst_rtspsrc_change_state (GstElement * element,
    GstStateChange transition);
247
static gboolean gst_rtspsrc_send_event (GstElement * element, GstEvent * event);
248
static void gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message);
Wim Taymans's avatar
Wim Taymans committed
249

250
251
252
static gboolean gst_rtspsrc_setup_auth (GstRTSPSrc * src,
    GstRTSPMessage * response);

253
254
static void gst_rtspsrc_loop_send_cmd (GstRTSPSrc * src, gint cmd,
    gboolean flush);
255
256
257
static GstRTSPResult gst_rtspsrc_send_cb (GstRTSPExtension * ext,
    GstRTSPMessage * request, GstRTSPMessage * response, GstRTSPSrc * src);

258
259
260
261
262
static GstRTSPResult gst_rtspsrc_open (GstRTSPSrc * src, gboolean async);
static GstRTSPResult gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment,
    gboolean async);
static GstRTSPResult gst_rtspsrc_pause (GstRTSPSrc * src, gboolean idle,
    gboolean async);
263
264
static GstRTSPResult gst_rtspsrc_close (GstRTSPSrc * src, gboolean async,
    gboolean only_close);
265

266
267
268
static gboolean gst_rtspsrc_uri_set_uri (GstURIHandler * handler,
    const gchar * uri);

269
static gboolean gst_rtspsrc_activate_streams (GstRTSPSrc * src);
270
static gboolean gst_rtspsrc_loop (GstRTSPSrc * src);
271
272
273
274
static gboolean gst_rtspsrc_stream_push_event (GstRTSPSrc * src,
    GstRTSPStream * stream, GstEvent * event, gboolean source);
static gboolean gst_rtspsrc_push_event (GstRTSPSrc * src, GstEvent * event,
    gboolean source);
Wim Taymans's avatar
Wim Taymans committed
275

276
/* commands we send to out loop to notify it of events */
277
278
279
280
281
282
#define CMD_OPEN	0
#define CMD_PLAY	1
#define CMD_PAUSE	2
#define CMD_CLOSE	3
#define CMD_WAIT	4
#define CMD_RECONNECT	5
283
#define CMD_LOOP	6
284
285
286
287
288
289
290
291
292

#define GST_ELEMENT_PROGRESS(el, type, code, text)      \
G_STMT_START {                                          \
  gchar *__txt = _gst_element_error_printf text;        \
  gst_element_post_message (GST_ELEMENT_CAST (el),      \
      gst_message_new_progress (GST_OBJECT_CAST (el),   \
          GST_PROGRESS_TYPE_ ##type, code, __txt));     \
  g_free (__txt);                                       \
} G_STMT_END
293

Wim Taymans's avatar
Wim Taymans committed
294
/*static guint gst_rtspsrc_signals[LAST_SIGNAL] = { 0 }; */
Wim Taymans's avatar
Wim Taymans committed
295
296
297
#define gst_rtspsrc_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstRTSPSrc, gst_rtspsrc, GST_TYPE_BIN,
    G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_rtspsrc_uri_handler_init));
Wim Taymans's avatar
Wim Taymans committed
298
299

static void
300
gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
Wim Taymans's avatar
Wim Taymans committed
301
302
303
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
304
  GstBinClass *gstbin_class;
Wim Taymans's avatar
Wim Taymans committed
305
306
307

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
308
  gstbin_class = (GstBinClass *) klass;
Wim Taymans's avatar
Wim Taymans committed
309

Wim Taymans's avatar
Wim Taymans committed
310
311
  GST_DEBUG_CATEGORY_INIT (rtspsrc_debug, "rtspsrc", 0, "RTSP src");

Wim Taymans's avatar
Wim Taymans committed
312
313
314
  gobject_class->set_property = gst_rtspsrc_set_property;
  gobject_class->get_property = gst_rtspsrc_get_property;

315
316
  gobject_class->finalize = gst_rtspsrc_finalize;

Wim Taymans's avatar
Wim Taymans committed
317
  g_object_class_install_property (gobject_class, PROP_LOCATION,
Wim Taymans's avatar
Wim Taymans committed
318
319
      g_param_spec_string ("location", "RTSP Location",
          "Location of the RTSP url to read",
Wim Taymans's avatar
Wim Taymans committed
320
          DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
321

Wim Taymans's avatar
Wim Taymans committed
322
  g_object_class_install_property (gobject_class, PROP_PROTOCOLS,
323
      g_param_spec_flags ("protocols", "Protocols",
324
          "Allowed lower transport protocols", GST_TYPE_RTSP_LOWER_TRANS,
Wim Taymans's avatar
Wim Taymans committed
325
          DEFAULT_PROTOCOLS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
326

Wim Taymans's avatar
Wim Taymans committed
327
  g_object_class_install_property (gobject_class, PROP_DEBUG,
Wim Taymans's avatar
Wim Taymans committed
328
      g_param_spec_boolean ("debug", "Debug",
329
          "Dump request and response messages to stdout",
Wim Taymans's avatar
Wim Taymans committed
330
          DEFAULT_DEBUG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
331

Wim Taymans's avatar
Wim Taymans committed
332
333
334
335
  g_object_class_install_property (gobject_class, PROP_RETRY,
      g_param_spec_uint ("retry", "Retry",
          "Max number of retries when allocating RTP ports.",
          0, G_MAXUINT16, DEFAULT_RETRY,
Wim Taymans's avatar
Wim Taymans committed
336
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
337

338
339
  g_object_class_install_property (gobject_class, PROP_TIMEOUT,
      g_param_spec_uint64 ("timeout", "Timeout",
340
          "Retry TCP transport after UDP timeout microseconds (0 = disabled)",
341
          0, G_MAXUINT64, DEFAULT_TIMEOUT,
Wim Taymans's avatar
Wim Taymans committed
342
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
343

344
345
346
347
  g_object_class_install_property (gobject_class, PROP_TCP_TIMEOUT,
      g_param_spec_uint64 ("tcp-timeout", "TCP Timeout",
          "Fail after timeout microseconds on TCP connections (0 = disabled)",
          0, G_MAXUINT64, DEFAULT_TCP_TIMEOUT,
Wim Taymans's avatar
Wim Taymans committed
348
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
349

350
351
352
  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,
Wim Taymans's avatar
Wim Taymans committed
353
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
354

355
356
357
358
  g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
      g_param_spec_uint ("connection-speed", "Connection Speed",
          "Network connection speed in kbps (0 = unknown)",
          0, G_MAXINT / 1000, DEFAULT_CONNECTION_SPEED,
Wim Taymans's avatar
Wim Taymans committed
359
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
360

361
362
363
364
  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_RTSP_NAT_METHOD, DEFAULT_NAT_METHOD,
Wim Taymans's avatar
Wim Taymans committed
365
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
366

367
368
369
370
371
372
373
374
375
376
  /**
   * GstRTSPSrc::do-rtcp
   *
   * Enable RTCP support. Some old server don't like RTCP and then this property
   * needs to be set to FALSE.
   *
   * Since: 0.10.15
   */
  g_object_class_install_property (gobject_class, PROP_DO_RTCP,
      g_param_spec_boolean ("do-rtcp", "Do RTCP",
377
          "Send RTCP packets, disable for old incompatible server.",
Wim Taymans's avatar
Wim Taymans committed
378
          DEFAULT_DO_RTCP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
379

Wim Taymans's avatar
Wim Taymans committed
380
381
382
383
  /**
   * GstRTSPSrc::proxy
   *
   * Set the proxy parameters. This has to be a string of the format
384
   * [http://][user:passwd@]host[:port].
Wim Taymans's avatar
Wim Taymans committed
385
386
387
388
389
   *
   * Since: 0.10.15
   */
  g_object_class_install_property (gobject_class, PROP_PROXY,
      g_param_spec_string ("proxy", "Proxy",
390
          "Proxy settings for HTTP tunneling. Format: [http://][user:passwd@]host[:port]",
Wim Taymans's avatar
Wim Taymans committed
391
          DEFAULT_PROXY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
392

393
394
395
396
397
398
399
400
401
402
403
  /**
   * GstRTSPSrc::rtp_blocksize
   *
   * RTP package size to suggest to server.
   *
   * Since: 0.10.16
   */
  g_object_class_install_property (gobject_class, PROP_RTP_BLOCKSIZE,
      g_param_spec_uint ("rtp-blocksize", "RTP Blocksize",
          "RTP package size to suggest to server (0 = disabled)",
          0, 65536, DEFAULT_RTP_BLOCKSIZE,
Wim Taymans's avatar
Wim Taymans committed
404
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
405

406
407
408
  g_object_class_install_property (gobject_class,
      PROP_USER_ID,
      g_param_spec_string ("user-id", "user-id",
Wim Taymans's avatar
Wim Taymans committed
409
410
          "RTSP location URI user id for authentication", DEFAULT_USER_ID,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
411
412
  g_object_class_install_property (gobject_class, PROP_USER_PW,
      g_param_spec_string ("user-pw", "user-pw",
Wim Taymans's avatar
Wim Taymans committed
413
414
          "RTSP location URI user password for authentication", DEFAULT_USER_PW,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
415

416
417
418
419
420
421
422
423
424
425
426
427
428
  /**
   * GstRTSPSrc::buffer-mode:
   *
   * Control the buffering and timestamping mode used by the jitterbuffer.
   *
   * Since: 0.10.22
   */
  g_object_class_install_property (gobject_class, PROP_BUFFER_MODE,
      g_param_spec_enum ("buffer-mode", "Buffer Mode",
          "Control the buffering algorithm in use",
          GST_TYPE_RTSP_SRC_BUFFER_MODE, DEFAULT_BUFFER_MODE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

429
430
431
432
433
434
435
436
437
438
439
440
441
442
  /**
   * GstRTSPSrc::port-range:
   *
   * Configure the client port numbers that can be used to recieve RTP and
   * RTCP.
   *
   * Since: 0.10.25
   */
  g_object_class_install_property (gobject_class, PROP_PORT_RANGE,
      g_param_spec_string ("port-range", "Port range",
          "Client port range that can be used to receive RTP and RTCP data, "
          "eg. 3000-3005 (NULL = no restrictions)", DEFAULT_PORT_RANGE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

443
444
445
446
447
448
449
450
451
452
453
454
455
  /**
   * GstRTSPSrc::udp-buffer-size:
   *
   * Size of the kernel UDP receive buffer in bytes.
   *
   * Since: 0.10.26
   */
  g_object_class_install_property (gobject_class, PROP_UDP_BUFFER_SIZE,
      g_param_spec_int ("udp-buffer-size", "UDP Buffer Size",
          "Size of the kernel UDP receive buffer in bytes, 0=default",
          0, G_MAXINT, DEFAULT_UDP_BUFFER_SIZE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

456
457
458
459
460
461
462
  /**
   * GstRTSPSrc::short-header:
   *
   * Only send the basic RTSP headers for broken encoders.
   *
   * Since: 0.10.31
   */
463
464
465
466
467
  g_object_class_install_property (gobject_class, PROP_SHORT_HEADER,
      g_param_spec_boolean ("short-header", "Short Header",
          "Only send the basic RTSP headers for broken encoders",
          DEFAULT_SHORT_HEADER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

468
  gstelement_class->send_event = gst_rtspsrc_send_event;
Wim Taymans's avatar
Wim Taymans committed
469
  gstelement_class->change_state = gst_rtspsrc_change_state;
470

Wim Taymans's avatar
Wim Taymans committed
471
472
473
474
475
476
477
478
479
480
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&rtptemplate));

  gst_element_class_set_details_simple (gstelement_class,
      "RTSP packet receiver", "Source/Network",
      "Receive data over the network via RTSP (RFC 2326)",
      "Wim Taymans <wim@fluendo.com>, "
      "Thijs Vermeir <thijs.vermeir@barco.com>, "
      "Lutz Mueller <lutz@topfrose.de>");

481
  gstbin_class->handle_message = gst_rtspsrc_handle_message;
482
483

  gst_rtsp_ext_list_init ();
Wim Taymans's avatar
Wim Taymans committed
484
485
}

486

Wim Taymans's avatar
Wim Taymans committed
487
static void
Wim Taymans's avatar
Wim Taymans committed
488
gst_rtspsrc_init (GstRTSPSrc * src)
Wim Taymans's avatar
Wim Taymans committed
489
{
490
491
492
493
494
495
496
497
#ifdef G_OS_WIN32
  WSADATA wsa_data;

  if (WSAStartup (MAKEWORD (2, 2), &wsa_data) != 0) {
    GST_ERROR_OBJECT (src, "WSAStartup failed: 0x%08x", WSAGetLastError ());
  }
#endif

498
  src->conninfo.location = g_strdup (DEFAULT_LOCATION);
Wim Taymans's avatar
Wim Taymans committed
499
500
501
502
503
504
505
506
507
508
509
510
511
  src->protocols = DEFAULT_PROTOCOLS;
  src->debug = DEFAULT_DEBUG;
  src->retry = DEFAULT_RETRY;
  src->udp_timeout = DEFAULT_TIMEOUT;
  gst_rtspsrc_set_tcp_timeout (src, DEFAULT_TCP_TIMEOUT);
  src->latency = DEFAULT_LATENCY_MS;
  src->connection_speed = DEFAULT_CONNECTION_SPEED;
  src->nat_method = DEFAULT_NAT_METHOD;
  src->do_rtcp = DEFAULT_DO_RTCP;
  gst_rtspsrc_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);
512
  src->buffer_mode = DEFAULT_BUFFER_MODE;
513
514
  src->client_port_range.min = 0;
  src->client_port_range.max = 0;
515
  src->udp_buffer_size = DEFAULT_UDP_BUFFER_SIZE;
516
  src->short_header = DEFAULT_SHORT_HEADER;
517

518
519
520
  /* get a list of all extensions */
  src->extensions = gst_rtsp_ext_list_get ();

521
522
523
  /* connect to send signal */
  gst_rtsp_ext_list_connect (src->extensions, "send",
      (GCallback) gst_rtspsrc_send_cb, src);
524

525
526
527
528
529
530
  /* protects the streaming thread in interleaved mode or the polling
   * thread in UDP mode. */
  src->stream_rec_lock = g_new (GStaticRecMutex, 1);
  g_static_rec_mutex_init (src->stream_rec_lock);

  /* protects our state changes from multiple invocations */
531
532
  src->state_rec_lock = g_new (GStaticRecMutex, 1);
  g_static_rec_mutex_init (src->state_rec_lock);
533

534
  src->state = GST_RTSP_STATE_INVALID;
535
536

  GST_OBJECT_FLAG_SET (src, GST_ELEMENT_IS_SOURCE);
537
538
539
540
541
542
543
544
545
}

static void
gst_rtspsrc_finalize (GObject * object)
{
  GstRTSPSrc *rtspsrc;

  rtspsrc = GST_RTSPSRC (object);

546
  gst_rtsp_ext_list_free (rtspsrc->extensions);
547
548
  g_free (rtspsrc->conninfo.location);
  gst_rtsp_url_free (rtspsrc->conninfo.url);
549
  g_free (rtspsrc->conninfo.url_str);
550
551
  g_free (rtspsrc->user_id);
  g_free (rtspsrc->user_pw);
552

553
554
555
556
557
  if (rtspsrc->sdp) {
    gst_sdp_message_free (rtspsrc->sdp);
    rtspsrc->sdp = NULL;
  }

558
559
560
  /* free locks */
  g_static_rec_mutex_free (rtspsrc->stream_rec_lock);
  g_free (rtspsrc->stream_rec_lock);
561
562
  g_static_rec_mutex_free (rtspsrc->state_rec_lock);
  g_free (rtspsrc->state_rec_lock);
563

564
565
566
567
#ifdef G_OS_WIN32
  WSACleanup ();
#endif

568
  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
569
570
}

Wim Taymans's avatar
Wim Taymans committed
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
/* a proxy string of the format [user:passwd@]host[:port] */
static gboolean
gst_rtspsrc_set_proxy (GstRTSPSrc * rtsp, const gchar * proxy)
{
  gchar *p, *at, *col;

  g_free (rtsp->proxy_user);
  rtsp->proxy_user = NULL;
  g_free (rtsp->proxy_passwd);
  rtsp->proxy_passwd = NULL;
  g_free (rtsp->proxy_host);
  rtsp->proxy_host = NULL;
  rtsp->proxy_port = 0;

  p = (gchar *) proxy;

  if (p == NULL)
    return TRUE;

590
591
592
593
  /* we allow http:// in front but ignore it */
  if (g_str_has_prefix (p, "http://"))
    p += 7;

Wim Taymans's avatar
Wim Taymans committed
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
  at = strchr (p, '@');
  if (at) {
    /* look for user:passwd */
    col = strchr (proxy, ':');
    if (col == NULL || col > at)
      return FALSE;

    rtsp->proxy_user = g_strndup (p, col - p);
    col++;
    rtsp->proxy_passwd = g_strndup (col, at - col);

    /* move to host */
    p = at + 1;
  }
  col = strchr (p, ':');

  if (col) {
    /* everything before the colon is the hostname */
    rtsp->proxy_host = g_strndup (p, col - p);
    p = col + 1;
    rtsp->proxy_port = strtoul (p, (char **) &p, 10);
  } else {
    rtsp->proxy_host = g_strdup (p);
    rtsp->proxy_port = 8080;
  }
  return TRUE;
}

Wim Taymans's avatar
Wim Taymans committed
622
623
624
625
626
627
628
629
630
631
632
633
static void
gst_rtspsrc_set_tcp_timeout (GstRTSPSrc * rtspsrc, guint64 timeout)
{
  rtspsrc->tcp_timeout.tv_sec = timeout / G_USEC_PER_SEC;
  rtspsrc->tcp_timeout.tv_usec = timeout % G_USEC_PER_SEC;

  if (timeout != 0)
    rtspsrc->ptcp_timeout = &rtspsrc->tcp_timeout;
  else
    rtspsrc->ptcp_timeout = NULL;
}

Wim Taymans's avatar
Wim Taymans committed
634
635
636
637
638
639
640
641
642
643
static void
gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
    GParamSpec * pspec)
{
  GstRTSPSrc *rtspsrc;

  rtspsrc = GST_RTSPSRC (object);

  switch (prop_id) {
    case PROP_LOCATION:
644
645
      gst_rtspsrc_uri_set_uri (GST_URI_HANDLER (rtspsrc),
          g_value_get_string (value));
Wim Taymans's avatar
Wim Taymans committed
646
647
648
649
650
651
652
      break;
    case PROP_PROTOCOLS:
      rtspsrc->protocols = g_value_get_flags (value);
      break;
    case PROP_DEBUG:
      rtspsrc->debug = g_value_get_boolean (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
653
654
655
    case PROP_RETRY:
      rtspsrc->retry = g_value_get_uint (value);
      break;
656
    case PROP_TIMEOUT:
657
      rtspsrc->udp_timeout = g_value_get_uint64 (value);
658
      break;
659
    case PROP_TCP_TIMEOUT:
Wim Taymans's avatar
Wim Taymans committed
660
      gst_rtspsrc_set_tcp_timeout (rtspsrc, g_value_get_uint64 (value));
661
      break;
662
663
664
    case PROP_LATENCY:
      rtspsrc->latency = g_value_get_uint (value);
      break;
665
666
667
    case PROP_CONNECTION_SPEED:
      rtspsrc->connection_speed = g_value_get_uint (value);
      break;
668
669
670
    case PROP_NAT_METHOD:
      rtspsrc->nat_method = g_value_get_enum (value);
      break;
671
672
673
    case PROP_DO_RTCP:
      rtspsrc->do_rtcp = g_value_get_boolean (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
674
675
676
    case PROP_PROXY:
      gst_rtspsrc_set_proxy (rtspsrc, g_value_get_string (value));
      break;
677
678
679
    case PROP_RTP_BLOCKSIZE:
      rtspsrc->rtp_blocksize = g_value_get_uint (value);
      break;
680
681
682
683
684
685
686
687
688
689
    case PROP_USER_ID:
      if (rtspsrc->user_id)
        g_free (rtspsrc->user_id);
      rtspsrc->user_id = g_value_dup_string (value);
      break;
    case PROP_USER_PW:
      if (rtspsrc->user_pw)
        g_free (rtspsrc->user_pw);
      rtspsrc->user_pw = g_value_dup_string (value);
      break;
690
691
692
    case PROP_BUFFER_MODE:
      rtspsrc->buffer_mode = g_value_get_enum (value);
      break;
693
694
695
696
697
698
699
700
701
702
703
704
705
706
    case PROP_PORT_RANGE:
    {
      const gchar *str;

      str = g_value_get_string (value);
      if (str) {
        sscanf (str, "%u-%u",
            &rtspsrc->client_port_range.min, &rtspsrc->client_port_range.max);
      } else {
        rtspsrc->client_port_range.min = 0;
        rtspsrc->client_port_range.max = 0;
      }
      break;
    }
707
708
709
    case PROP_UDP_BUFFER_SIZE:
      rtspsrc->udp_buffer_size = g_value_get_int (value);
      break;
710
711
712
    case PROP_SHORT_HEADER:
      rtspsrc->short_header = g_value_get_boolean (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
  GstRTSPSrc *rtspsrc;

  rtspsrc = GST_RTSPSRC (object);

  switch (prop_id) {
    case PROP_LOCATION:
729
      g_value_set_string (value, rtspsrc->conninfo.location);
Wim Taymans's avatar
Wim Taymans committed
730
731
732
733
734
735
736
      break;
    case PROP_PROTOCOLS:
      g_value_set_flags (value, rtspsrc->protocols);
      break;
    case PROP_DEBUG:
      g_value_set_boolean (value, rtspsrc->debug);
      break;
Wim Taymans's avatar
Wim Taymans committed
737
738
739
    case PROP_RETRY:
      g_value_set_uint (value, rtspsrc->retry);
      break;
740
    case PROP_TIMEOUT:
741
742
743
744
745
746
747
748
749
      g_value_set_uint64 (value, rtspsrc->udp_timeout);
      break;
    case PROP_TCP_TIMEOUT:
    {
      guint64 timeout;

      timeout = rtspsrc->tcp_timeout.tv_sec * G_USEC_PER_SEC +
          rtspsrc->tcp_timeout.tv_usec;
      g_value_set_uint64 (value, timeout);
750
      break;
751
    }
752
753
754
    case PROP_LATENCY:
      g_value_set_uint (value, rtspsrc->latency);
      break;
755
756
757
    case PROP_CONNECTION_SPEED:
      g_value_set_uint (value, rtspsrc->connection_speed);
      break;
758
759
760
    case PROP_NAT_METHOD:
      g_value_set_enum (value, rtspsrc->nat_method);
      break;
761
762
763
    case PROP_DO_RTCP:
      g_value_set_boolean (value, rtspsrc->do_rtcp);
      break;
Wim Taymans's avatar
Wim Taymans committed
764
765
766
767
768
769
770
771
772
773
774
775
776
    case PROP_PROXY:
    {
      gchar *str;

      if (rtspsrc->proxy_host) {
        str =
            g_strdup_printf ("%s:%d", rtspsrc->proxy_host, rtspsrc->proxy_port);
      } else {
        str = NULL;
      }
      g_value_take_string (value, str);
      break;
    }
777
778
779
    case PROP_RTP_BLOCKSIZE:
      g_value_set_uint (value, rtspsrc->rtp_blocksize);
      break;
780
781
782
783
784
785
    case PROP_USER_ID:
      g_value_set_string (value, rtspsrc->user_id);
      break;
    case PROP_USER_PW:
      g_value_set_string (value, rtspsrc->user_pw);
      break;
786
787
788
    case PROP_BUFFER_MODE:
      g_value_set_enum (value, rtspsrc->buffer_mode);
      break;
789
790
791
792
793
794
795
796
797
798
799
800
801
    case PROP_PORT_RANGE:
    {
      gchar *str;

      if (rtspsrc->client_port_range.min != 0) {
        str = g_strdup_printf ("%u-%u", rtspsrc->client_port_range.min,
            rtspsrc->client_port_range.max);
      } else {
        str = NULL;
      }
      g_value_take_string (value, str);
      break;
    }
802
803
804
    case PROP_UDP_BUFFER_SIZE:
      g_value_set_int (value, rtspsrc->udp_buffer_size);
      break;
805
806
807
    case PROP_SHORT_HEADER:
      g_value_set_boolean (value, rtspsrc->short_header);
      break;
Wim Taymans's avatar
Wim Taymans committed
808
809
810
811
812
813
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

814
static gint
815
find_stream_by_id (GstRTSPStream * stream, gint * id)
816
{
817
  if (stream->id == *id)
818
819
820
821
822
    return 0;

  return -1;
}

823
static gint
824
find_stream_by_channel (GstRTSPStream * stream, gint * channel)
825
{
826
  if (stream->channel[0] == *channel || stream->channel[1] == *channel)
827
828
829
830
831
    return 0;

  return -1;
}

832
static gint
833
find_stream_by_pt (GstRTSPStream * stream, gint * pt)
834
{
835
  if (stream->pt == *pt)
836
837
838
839
840
    return 0;

  return -1;
}

841
842
843
844
845
846
847
static gint
find_stream_by_udpsrc (GstRTSPStream * stream, gconstpointer a)
{
  GstElement *src = (GstElement *) a;

  if (stream->udpsrc[0] == src)
    return 0;
848
849
  if (stream->udpsrc[1] == src)
    return 0;
850
851
852
853

  return -1;
}

854
855
856
857
static gint
find_stream_by_setup (GstRTSPStream * stream, gconstpointer a)
{
  /* check qualified setup_url */
858
  if (!strcmp (stream->conninfo.location, (gchar *) a))
859
860
861
862
863
864
865
866
867
868
869
870
    return 0;
  /* check original control_url */
  if (!strcmp (stream->control_url, (gchar *) a))
    return 0;

  /* check if qualified setup_url ends with string */
  if (g_str_has_suffix (stream->control_url, (gchar *) a))
    return 0;

  return -1;
}

871
static GstRTSPStream *
872
873
874
875
876
877
878
879
880
881
882
find_stream (GstRTSPSrc * src, gconstpointer data, gconstpointer func)
{
  GList *lstream;

  /* find and get stream */
  if ((lstream = g_list_find_custom (src->streams, data, (GCompareFunc) func)))
    return (GstRTSPStream *) lstream->data;

  return NULL;
}

883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
static const GstSDPBandwidth *
gst_rtspsrc_get_bandwidth (GstRTSPSrc * src, const GstSDPMessage * sdp,
    const GstSDPMedia * media, const gchar * type)
{
  guint i, len;

  /* first look in the media specific section */
  len = gst_sdp_media_bandwidths_len (media);
  for (i = 0; i < len; i++) {
    const GstSDPBandwidth *bw = gst_sdp_media_get_bandwidth (media, i);

    if (strcmp (bw->bwtype, type) == 0)
      return bw;
  }
  /* then look in the message specific section */
  len = gst_sdp_message_bandwidths_len (sdp);
  for (i = 0; i < len; i++) {
    const GstSDPBandwidth *bw = gst_sdp_message_get_bandwidth (sdp, i);

    if (strcmp (bw->bwtype, type) == 0)
      return bw;
  }
  return NULL;
}

static void
gst_rtspsrc_collect_bandwidth (GstRTSPSrc * src, const GstSDPMessage * sdp,
    const GstSDPMedia * media, GstRTSPStream * stream)
{
  const GstSDPBandwidth *bw;

  if ((bw = gst_rtspsrc_get_bandwidth (src, sdp, media, GST_SDP_BWTYPE_AS)))
    stream->as_bandwidth = bw->bandwidth;
  else
    stream->as_bandwidth = -1;

  if ((bw = gst_rtspsrc_get_bandwidth (src, sdp, media, GST_SDP_BWTYPE_RR)))
    stream->rr_bandwidth = bw->bandwidth;
  else
    stream->rr_bandwidth = -1;

  if ((bw = gst_rtspsrc_get_bandwidth (src, sdp, media, GST_SDP_BWTYPE_RS)))
    stream->rs_bandwidth = bw->bandwidth;
  else
    stream->rs_bandwidth = -1;
}

930
931
932
933
static void
gst_rtspsrc_do_stream_connection (GstRTSPSrc * src, GstRTSPStream * stream,
    const GstSDPConnection * conn)
{
934
935
936
937
  if (conn->nettype == NULL || strcmp (conn->nettype, "IN") != 0)
    return;

  if (conn->addrtype == NULL)
938
939
940
941
942
943
944
945
946
947
    return;

  /* check for IPV6 */
  if (strcmp (conn->addrtype, "IP4") == 0)
    stream->is_ipv6 = FALSE;
  else if (strcmp (conn->addrtype, "IP6") == 0)
    stream->is_ipv6 = TRUE;
  else
    return;

948
  /* save address */
949
950
  g_free (stream->destination);
  stream->destination = g_strdup (conn->address);
951
952
953
954
955

  /* check for multicast */
  stream->is_multicast =
      gst_sdp_address_is_multicast (conn->nettype, conn->addrtype,
      conn->address);
956
  stream->ttl = conn->ttl;
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
}

/* Go over the connections for a stream.
 * - If we are dealing with IPV6, we will setup IPV6 sockets for sending and
 *   receiving.
 * - If we are dealing with a localhost address, we disable multicast
 */
static void
gst_rtspsrc_collect_connections (GstRTSPSrc * src, const GstSDPMessage * sdp,
    const GstSDPMedia * media, GstRTSPStream * stream)
{
  const GstSDPConnection *conn;
  guint i, len;

  /* first look in the media specific section */
  len = gst_sdp_media_connections_len (media);
  for (i = 0; i < len; i++) {
    conn = gst_sdp_media_get_connection (media, i);

    gst_rtspsrc_do_stream_connection (src, stream, conn);
  }
  /* then look in the message specific section */
  if ((conn = gst_sdp_message_get_connection (sdp))) {
    gst_rtspsrc_do_stream_connection (src, stream, conn);
  }
}

Wim Taymans's avatar
Wim Taymans committed
984
static GstRTSPStream *
985
gst_rtspsrc_create_stream (GstRTSPSrc * src, GstSDPMessage * sdp, gint idx)
Wim Taymans's avatar
Wim Taymans committed
986
{
987
  GstRTSPStream *stream;
988
989
990
  const gchar *control_url;
  const gchar *payload;
  const GstSDPMedia *media;
991
992

  /* get media, should not return NULL */
993
  media = gst_sdp_message_get_media (sdp, idx);
994
995
  if (media == NULL)
    return NULL;
996
997
998
999
1000
1001

  stream = g_new0 (GstRTSPStream, 1);
  stream->parent = src;
  /* we mark the pad as not linked, we will mark it as OK when we add the pad to
   * the element. */
  stream->last_ret = GST_FLOW_NOT_LINKED;
1002
  stream->added = FALSE;
1003
  stream->disabled = FALSE;
1004
  stream->id = src->numstreams++;
1005
1006
  stream->eos = FALSE;
  stream->discont = TRUE;
1007
1008
  stream->seqbase = -1;
  stream->timebase = -1;
1009

1010
1011
  /* collect bandwidth information for this steam. FIXME, configure in the RTP
   * session manager to scale RTCP. */
1012
1013
  gst_rtspsrc_collect_bandwidth (src, sdp, media, stream);

1014
1015
1016
  /* collect connection info */
  gst_rtspsrc_collect_connections (src, sdp, media, stream);

1017
  /* we must have a payload. No payload means we cannot create caps */
1018
1019
1020
1021
1022
  /* FIXME, handle multiple formats. The problem here is that we just want to
   * take the first available format that we can handle but in order to do that
   * we need to scan for depayloader plugins. Scanning for payloader plugins is
   * also suboptimal because the user maybe just wants to save the raw stream
   * and then we don't care. */
1023
  if ((payload = gst_sdp_media_get_format (media, 0))) {
1024
1025
1026
1027
    stream->pt = atoi (payload);
    /* convert caps */
    stream->caps = gst_rtspsrc_media_to_caps (stream->pt, media);

1028
1029
1030
1031
1032
    GST_DEBUG ("mapping sdp session level attributes to caps");
    gst_rtspsrc_sdp_attributes_to_caps (sdp->attributes, stream->caps);
    GST_DEBUG ("mapping sdp media level attributes to caps");
    gst_rtspsrc_sdp_attributes_to_caps (media->attributes, stream->caps);

1033
1034
1035
1036
    if (stream->pt >= 96) {
      /* If we have a dynamic payload type, see if we have a stream with the
       * same payload number. If there is one, they are part of the same
       * container and we only need to add one pad. */
1037
      if (find_stream (src, &stream->pt, (gpointer) find_stream_by_pt)) {
1038
        stream->container = TRUE;
1039
1040
        GST_DEBUG ("found another stream with pt %d, marking as container",
            stream->pt);
1041
1042
1043
      }
    }
  }
1044
1045
  /* collect port number */
  stream->port = gst_sdp_media_get_port (media);
Wim Taymans's avatar
Wim Taymans committed
1046

1047
1048
1049
  /* get control url to construct the setup url. The setup url is used to
   * configure the transport of the stream and is used to identity the stream in
   * the RTP-Info header field returned from PLAY. */
1050
  control_url = gst_sdp_media_get_attribute_val (media, "control");
1051
  if (control_url == NULL)
Wim Taymans's avatar
Wim Taymans committed
1052
    control_url = gst_sdp_message_get_attribute_val_n (sdp, "control", 0);
Wim Taymans's avatar
Wim Taymans committed
1053

1054
  GST_DEBUG_OBJECT (src, "stream %d, (%p)", stream->id, stream);
1055
  GST_DEBUG_OBJECT (src, " pt: %d", stream->pt);
1056
  GST_DEBUG_OBJECT (src, " port: %d", stream->port);
1057
1058
1059
  GST_DEBUG_OBJECT (src, " container: %d", stream->container);
  GST_DEBUG_OBJECT (src, " caps: %" GST_PTR_FORMAT, stream->caps);
  GST_DEBUG_OBJECT (src, " control: %s", GST_STR_NULL (control_url));
Wim Taymans's avatar
Wim Taymans committed
1060

1061
  if (control_url != NULL) {
1062
1063
1064
1065
    stream->control_url = g_strdup (control_url);
    /* Build a fully qualified url using the content_base if any or by prefixing
     * the original request.
     * If the control_url starts with a '/' or a non rtsp: protocol we will most
1066
1067
     * likely build a URL that the server will fail to understand, this is ok,
     * we will fail then. */
1068
    if (g_str_has_prefix (control_url, "rtsp://"))
1069
      stream->conninfo.location = g_strdup (control_url);
1070
1071
1072
1073
    else {
      const gchar *base;
      gboolean has_slash;

Wim Taymans's avatar
Wim Taymans committed
1074
1075
1076
      if (g_strcmp0 (control_url, "*") == 0)
        control_url = "";

1077
1078
1079
      if (src->control)
        base = src->control;
      else if (src->content_base)
1080
        base = src->content_base;
1081
1082
      else if (src->conninfo.url_str)
        base = src->conninfo.url_str;
1083
1084
1085
1086
1087
1088
1089
1090
      else
        base = "/";

      /* check if the base ends or control starts with / */
      has_slash = g_str_has_prefix (control_url, "/");
      has_slash = has_slash || g_str_has_suffix (base, "/");

      /* concatenate the two strings, insert / when not present */
1091
      stream->conninfo.location =
1092
1093
          g_strdup_printf ("%s%s%s", base, has_slash ? "" : "/", control_url);
    }
1094
  }
1095
1096
  GST_DEBUG_OBJECT (src, " setup: %s",
      GST_STR_NULL (stream->conninfo.location));
1097
1098
1099
1100
1101

  /* we keep track of all streams */
  src->streams = g_list_append (src->streams, stream);

  return stream;
1102
1103

  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
1104
1105
}

1106
static void
1107
gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream)
1108
{
1109
1110
1111
1112
1113
  gint i;

  GST_DEBUG_OBJECT (src, "free stream %p", stream);

  if (stream->caps)
1114
    gst_caps_unref (stream->caps);
1115

1116
  g_free (stream->destination);
1117
  g_free (stream->control_url);
1118
  g_free (stream->conninfo.location);
1119

1120
  for (i = 0; i < 2; i++) {
1121
1122
1123
    if (stream->udpsrc[i]) {
      gst_element_set_state (stream->udpsrc[i], GST_STATE_NULL);
      gst_bin_remove (GST_BIN_CAST (src), stream->udpsrc[i]);
1124
1125
1126
      gst_object_unref (stream->udpsrc[i]);
      stream->udpsrc[i] = NULL;
    }
1127
1128
1129
1130
    if (stream->channelpad[i]) {
      gst_object_unref (stream->channelpad[i]);
      stream->channelpad[i] = NULL;
    }
1131
1132
1133
1134
1135
1136
    if (stream->udpsink[i]) {
      gst_element_set_state (stream->udpsink[i], GST_STATE_NULL);
      gst_bin_remove (GST_BIN_CAST (src), stream->udpsink[i]);
      gst_object_unref (stream->udpsink[i]);
      stream->udpsink[i] = NULL;
    }
1137
  }
1138
1139
1140
1141
1142
  if (stream->fakesrc) {
    gst_element_set_state (stream->fakesrc, GST_STATE_NULL);
    gst_bin_remove (GST_BIN_CAST (src), stream->fakesrc);
    gst_object_unref (stream->fakesrc);
    stream->fakesrc = NULL;
1143
  }
1144
1145
1146
1147
1148
1149
1150
1151
  if (stream->srcpad) {
    gst_pad_set_active (stream->srcpad, FALSE);
    if (stream->added) {
      gst_element_remove_pad (GST_ELEMENT_CAST (src), stream->srcpad);
      stream->added = FALSE;
    }
    stream->srcpad = NULL;
  }
1152
1153
1154
1155
  if (stream->rtcppad) {
    gst_object_unref (stream->rtcppad);
    stream->rtcppad = NULL;
  }
1156
1157
1158
1159
  if (stream->session) {
    g_object_unref (stream->session);
    stream->session = NULL;
  }
1160
1161
  g_free (stream);
}
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176

static void
gst_rtspsrc_cleanup (GstRTSPSrc * src)
{
  GList *walk;

  GST_DEBUG_OBJECT (src, "cleanup");

  for (walk = src->streams; walk; walk = g_list_next (walk)) {
    GstRTSPStream *stream = (GstRTSPStream *) walk->data;

    gst_rtspsrc_stream_free (src, stream);
  }
  g_list_free (src->streams);
  src->streams = NULL;
1177
1178
1179
1180
  if (src->manager) {
    if (src->manager_sig_id) {
      g_signal_handler_disconnect (src->manager, src->manager_sig_id);
      src->manager_sig_id = 0;
1181
    }
1182
1183
1184
    gst_element_set_state (src->manager, GST_STATE_NULL);
    gst_bin_remove (GST_BIN_CAST (src), src->manager);
    src->manager = NULL;
1185
  }
1186
1187
1188
1189
  src->numstreams = 0;
  if (src->props)
    gst_structure_free (src->props);
  src->props = NULL;
Wim Taymans's avatar
Wim Taymans committed
1190
1191
1192
1193

  g_free (src->content_base);
  src->content_base = NULL;

1194
1195
1196
  g_free (src->control);
  src->control = NULL;

Wim Taymans's avatar
Wim Taymans committed
1197
1198
1199
  if (src->range)
    gst_rtsp_range_free (src->range);
  src->range = NULL;
1200

1201
1202
  /* don't clear the SDP when it was used in the url */
  if (src->sdp && !src->from_sdp) {
1203
1204
1205
    gst_sdp_message_free (src->sdp);
    src->sdp = NULL;
  }
1206
1207
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
#define PARSE_INT(p, del, res)          \
G_STMT_START {                          \
  gchar *t = p;                         \
  p = strstr (p, del);                  \
  if (p == NULL)                        \
    res = -1;                           \
  else {                                \
    *p = '\0';                          \
    p++;                                \
    res = atoi (t);                     \
  }                                     \
Wim Taymans's avatar
Wim Taymans committed
1219
1220
} G_STMT_END

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1221
1222
1223
1224
#define PARSE_STRING(p, del, res)       \
G_STMT_START {                          \
  gchar *t = p;                         \
  p = strstr (p, del);                  \
1225
  if (p == NULL) {                      \
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1226
    res = NULL;                         \
1227
1228
    p = t;                              \
  }                                     \
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1229
1230
1231
1232
1233
  else {                                \
    *p = '\0';                          \
    p++;                                \
    res = t;                            \
  }                                     \
Wim Taymans's avatar
Wim Taymans committed
1234
1235
} G_STMT_END

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1236
1237
#define SKIP_SPACES(p)                  \
  while (*p && g_ascii_isspace (*p))    \
Wim Taymans's avatar