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
GST_DEBUG_CATEGORY_STATIC (rtspsrc_debug);
Wim Taymans's avatar
Wim Taymans committed
101
102
#define GST_CAT_DEFAULT (rtspsrc_debug)

Wim Taymans's avatar
Wim Taymans committed
103
static GstStaticPadTemplate rtptemplate = GST_STATIC_PAD_TEMPLATE ("stream_%u",
Wim Taymans's avatar
Wim Taymans committed
104
105
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
106
    GST_STATIC_CAPS ("application/x-rtp; application/x-rdt"));
Wim Taymans's avatar
Wim Taymans committed
107

108
109
/* templates used internally */
static GstStaticPadTemplate anysrctemplate =
Wim Taymans's avatar
Wim Taymans committed
110
GST_STATIC_PAD_TEMPLATE ("internalsrc_%u",
111
112
113
114
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

115
static GstStaticPadTemplate anysinktemplate =
Wim Taymans's avatar
Wim Taymans committed
116
GST_STATIC_PAD_TEMPLATE ("internalsink_%u",
117
118
119
120
    GST_PAD_SINK,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

Wim Taymans's avatar
Wim Taymans committed
121
122
123
124
125
126
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

127
128
129
130
131
132
133
enum _GstRtspSrcRtcpSyncMode
{
  RTCP_SYNC_ALWAYS,
  RTCP_SYNC_INITIAL,
  RTCP_SYNC_RTP
};

134
135
136
137
138
139
140
141
enum _GstRtspSrcBufferMode
{
  BUFFER_MODE_NONE,
  BUFFER_MODE_SLAVE,
  BUFFER_MODE_BUFFER,
  BUFFER_MODE_AUTO
};

142
143
144
145
146
147
#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[] = {
148
149
150
151
    {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"},
152
153
154
155
156
157
158
159
160
161
    {0, NULL, NULL},
  };

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

162
163
164
165
166
#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
167
#define DEFAULT_UDP_BUFFER_SIZE  0x80000
168
#define DEFAULT_TCP_TIMEOUT      20000000
169
#define DEFAULT_LATENCY_MS       2000
170
#define DEFAULT_CONNECTION_SPEED 0
171
#define DEFAULT_NAT_METHOD       GST_RTSP_NAT_DUMMY
172
#define DEFAULT_DO_RTCP          TRUE
Wim Taymans's avatar
Wim Taymans committed
173
#define DEFAULT_PROXY            NULL
174
#define DEFAULT_RTP_BLOCKSIZE    0
Wim Taymans's avatar
Wim Taymans committed
175
176
#define DEFAULT_USER_ID          NULL
#define DEFAULT_USER_PW          NULL
177
#define DEFAULT_BUFFER_MODE      BUFFER_MODE_AUTO
178
#define DEFAULT_PORT_RANGE       NULL
179
#define DEFAULT_SHORT_HEADER     FALSE
Wim Taymans's avatar
Wim Taymans committed
180
181
182
183
184
185
186

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

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#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;
}

223
static void gst_rtspsrc_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
224

225
226
227
228
229
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
230
231
static void gst_rtspsrc_uri_handler_init (gpointer g_iface,
    gpointer iface_data);
232
233
234
235

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

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

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

241
242
static GstStateChangeReturn gst_rtspsrc_change_state (GstElement * element,
    GstStateChange transition);
243
static gboolean gst_rtspsrc_send_event (GstElement * element, GstEvent * event);
244
static void gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message);
Wim Taymans's avatar
Wim Taymans committed
245

246
247
248
static gboolean gst_rtspsrc_setup_auth (GstRTSPSrc * src,
    GstRTSPMessage * response);

249
static void gst_rtspsrc_loop_send_cmd (GstRTSPSrc * src, gint cmd);
250
251
252
static GstRTSPResult gst_rtspsrc_send_cb (GstRTSPExtension * ext,
    GstRTSPMessage * request, GstRTSPMessage * response, GstRTSPSrc * src);

253
254
255
256
257
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);
258
259
static GstRTSPResult gst_rtspsrc_close (GstRTSPSrc * src, gboolean async,
    gboolean only_close);
260

261
static gboolean gst_rtspsrc_uri_set_uri (GstURIHandler * handler,
262
    const gchar * uri, GError ** error);
263

264
static gboolean gst_rtspsrc_activate_streams (GstRTSPSrc * src);
265
static gboolean gst_rtspsrc_loop (GstRTSPSrc * src);
266
267
268
269
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
270

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

#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
288

Wim Taymans's avatar
Wim Taymans committed
289
/*static guint gst_rtspsrc_signals[LAST_SIGNAL] = { 0 }; */
Wim Taymans's avatar
Wim Taymans committed
290
291
292
#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
293
294

static void
295
gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
Wim Taymans's avatar
Wim Taymans committed
296
297
298
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
299
  GstBinClass *gstbin_class;
Wim Taymans's avatar
Wim Taymans committed
300
301
302

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

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

Wim Taymans's avatar
Wim Taymans committed
307
308
309
  gobject_class->set_property = gst_rtspsrc_set_property;
  gobject_class->get_property = gst_rtspsrc_get_property;

310
311
  gobject_class->finalize = gst_rtspsrc_finalize;

Wim Taymans's avatar
Wim Taymans committed
312
  g_object_class_install_property (gobject_class, PROP_LOCATION,
Wim Taymans's avatar
Wim Taymans committed
313
314
      g_param_spec_string ("location", "RTSP Location",
          "Location of the RTSP url to read",
Wim Taymans's avatar
Wim Taymans committed
315
          DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
316

Wim Taymans's avatar
Wim Taymans committed
317
  g_object_class_install_property (gobject_class, PROP_PROTOCOLS,
318
      g_param_spec_flags ("protocols", "Protocols",
319
          "Allowed lower transport protocols", GST_TYPE_RTSP_LOWER_TRANS,
Wim Taymans's avatar
Wim Taymans committed
320
          DEFAULT_PROTOCOLS, 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_DEBUG,
Wim Taymans's avatar
Wim Taymans committed
323
      g_param_spec_boolean ("debug", "Debug",
324
          "Dump request and response messages to stdout",
Wim Taymans's avatar
Wim Taymans committed
325
          DEFAULT_DEBUG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
326

Wim Taymans's avatar
Wim Taymans committed
327
328
329
330
  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
331
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
332

333
334
  g_object_class_install_property (gobject_class, PROP_TIMEOUT,
      g_param_spec_uint64 ("timeout", "Timeout",
335
          "Retry TCP transport after UDP timeout microseconds (0 = disabled)",
336
          0, G_MAXUINT64, DEFAULT_TIMEOUT,
Wim Taymans's avatar
Wim Taymans committed
337
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
338

339
340
341
342
  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
343
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
344

345
346
347
  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
348
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
349

350
  g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
351
      g_param_spec_uint64 ("connection-speed", "Connection Speed",
352
          "Network connection speed in kbps (0 = unknown)",
353
          0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
Wim Taymans's avatar
Wim Taymans committed
354
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
355

356
357
358
359
  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
360
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
361

362
363
364
365
366
367
368
369
370
371
  /**
   * 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",
372
          "Send RTCP packets, disable for old incompatible server.",
Wim Taymans's avatar
Wim Taymans committed
373
          DEFAULT_DO_RTCP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
374

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

388
389
390
391
392
393
394
395
396
397
398
  /**
   * 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
399
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
400

401
402
403
  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
404
405
          "RTSP location URI user id for authentication", DEFAULT_USER_ID,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
406
407
  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
408
409
          "RTSP location URI user password for authentication", DEFAULT_USER_PW,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
410

411
412
413
414
415
416
417
418
419
420
421
422
423
  /**
   * 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));

424
425
426
427
428
429
430
431
432
433
434
435
436
437
  /**
   * 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));

438
439
440
441
442
443
444
445
446
447
448
449
450
  /**
   * 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));

451
452
453
454
455
456
457
  /**
   * GstRTSPSrc::short-header:
   *
   * Only send the basic RTSP headers for broken encoders.
   *
   * Since: 0.10.31
   */
458
459
460
461
462
  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));

463
  gstelement_class->send_event = gst_rtspsrc_send_event;
Wim Taymans's avatar
Wim Taymans committed
464
  gstelement_class->change_state = gst_rtspsrc_change_state;
465

Wim Taymans's avatar
Wim Taymans committed
466
467
468
469
470
471
472
473
474
475
  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>");

476
  gstbin_class->handle_message = gst_rtspsrc_handle_message;
477
478

  gst_rtsp_ext_list_init ();
Wim Taymans's avatar
Wim Taymans committed
479
480
}

481

Wim Taymans's avatar
Wim Taymans committed
482
static void
Wim Taymans's avatar
Wim Taymans committed
483
gst_rtspsrc_init (GstRTSPSrc * src)
Wim Taymans's avatar
Wim Taymans committed
484
{
485
  src->conninfo.location = g_strdup (DEFAULT_LOCATION);
Wim Taymans's avatar
Wim Taymans committed
486
487
488
489
490
491
492
493
494
495
496
497
498
  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);
499
  src->buffer_mode = DEFAULT_BUFFER_MODE;
500
501
  src->client_port_range.min = 0;
  src->client_port_range.max = 0;
502
  src->udp_buffer_size = DEFAULT_UDP_BUFFER_SIZE;
503
  src->short_header = DEFAULT_SHORT_HEADER;
504

505
506
507
  /* get a list of all extensions */
  src->extensions = gst_rtsp_ext_list_get ();

508
509
510
  /* connect to send signal */
  gst_rtsp_ext_list_connect (src->extensions, "send",
      (GCallback) gst_rtspsrc_send_cb, src);
511

512
513
  /* protects the streaming thread in interleaved mode or the polling
   * thread in UDP mode. */
Wim Taymans's avatar
Wim Taymans committed
514
  g_rec_mutex_init (&src->stream_rec_lock);
515
516

  /* protects our state changes from multiple invocations */
Wim Taymans's avatar
Wim Taymans committed
517
  g_rec_mutex_init (&src->state_rec_lock);
518

519
  src->state = GST_RTSP_STATE_INVALID;
520

Wim Taymans's avatar
Wim Taymans committed
521
  GST_OBJECT_FLAG_SET (src, GST_ELEMENT_FLAG_SOURCE);
522
523
524
525
526
527
528
529
530
}

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

  rtspsrc = GST_RTSPSRC (object);

531
  gst_rtsp_ext_list_free (rtspsrc->extensions);
532
533
  g_free (rtspsrc->conninfo.location);
  gst_rtsp_url_free (rtspsrc->conninfo.url);
534
  g_free (rtspsrc->conninfo.url_str);
535
536
  g_free (rtspsrc->user_id);
  g_free (rtspsrc->user_pw);
537

538
539
540
541
542
  if (rtspsrc->sdp) {
    gst_sdp_message_free (rtspsrc->sdp);
    rtspsrc->sdp = NULL;
  }

543
  /* free locks */
Wim Taymans's avatar
Wim Taymans committed
544
545
  g_rec_mutex_clear (&rtspsrc->stream_rec_lock);
  g_rec_mutex_clear (&rtspsrc->state_rec_lock);
546
547

  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
548
549
}

Wim Taymans's avatar
Wim Taymans committed
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
/* 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;

569
570
571
572
  /* we allow http:// in front but ignore it */
  if (g_str_has_prefix (p, "http://"))
    p += 7;

Wim Taymans's avatar
Wim Taymans committed
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  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
601
602
603
604
605
606
607
608
609
610
611
612
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
613
614
615
616
617
618
619
620
621
622
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:
623
      gst_rtspsrc_uri_set_uri (GST_URI_HANDLER (rtspsrc),
624
          g_value_get_string (value), NULL);
Wim Taymans's avatar
Wim Taymans committed
625
626
627
628
629
630
631
      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
632
633
634
    case PROP_RETRY:
      rtspsrc->retry = g_value_get_uint (value);
      break;
635
    case PROP_TIMEOUT:
636
      rtspsrc->udp_timeout = g_value_get_uint64 (value);
637
      break;
638
    case PROP_TCP_TIMEOUT:
Wim Taymans's avatar
Wim Taymans committed
639
      gst_rtspsrc_set_tcp_timeout (rtspsrc, g_value_get_uint64 (value));
640
      break;
641
642
643
    case PROP_LATENCY:
      rtspsrc->latency = g_value_get_uint (value);
      break;
644
    case PROP_CONNECTION_SPEED:
645
      rtspsrc->connection_speed = g_value_get_uint64 (value);
646
      break;
647
648
649
    case PROP_NAT_METHOD:
      rtspsrc->nat_method = g_value_get_enum (value);
      break;
650
651
652
    case PROP_DO_RTCP:
      rtspsrc->do_rtcp = g_value_get_boolean (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
653
654
655
    case PROP_PROXY:
      gst_rtspsrc_set_proxy (rtspsrc, g_value_get_string (value));
      break;
656
657
658
    case PROP_RTP_BLOCKSIZE:
      rtspsrc->rtp_blocksize = g_value_get_uint (value);
      break;
659
660
661
662
663
664
665
666
667
668
    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;
669
670
671
    case PROP_BUFFER_MODE:
      rtspsrc->buffer_mode = g_value_get_enum (value);
      break;
672
673
674
675
676
677
678
679
680
681
682
683
684
685
    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;
    }
686
687
688
    case PROP_UDP_BUFFER_SIZE:
      rtspsrc->udp_buffer_size = g_value_get_int (value);
      break;
689
690
691
    case PROP_SHORT_HEADER:
      rtspsrc->short_header = g_value_get_boolean (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
    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:
708
      g_value_set_string (value, rtspsrc->conninfo.location);
Wim Taymans's avatar
Wim Taymans committed
709
710
711
712
713
714
715
      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
716
717
718
    case PROP_RETRY:
      g_value_set_uint (value, rtspsrc->retry);
      break;
719
    case PROP_TIMEOUT:
720
721
722
723
724
725
726
727
728
      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);
729
      break;
730
    }
731
732
733
    case PROP_LATENCY:
      g_value_set_uint (value, rtspsrc->latency);
      break;
734
    case PROP_CONNECTION_SPEED:
735
      g_value_set_uint64 (value, rtspsrc->connection_speed);
736
      break;
737
738
739
    case PROP_NAT_METHOD:
      g_value_set_enum (value, rtspsrc->nat_method);
      break;
740
741
742
    case PROP_DO_RTCP:
      g_value_set_boolean (value, rtspsrc->do_rtcp);
      break;
Wim Taymans's avatar
Wim Taymans committed
743
744
745
746
747
748
749
750
751
752
753
754
755
    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;
    }
756
757
758
    case PROP_RTP_BLOCKSIZE:
      g_value_set_uint (value, rtspsrc->rtp_blocksize);
      break;
759
760
761
762
763
764
    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;
765
766
767
    case PROP_BUFFER_MODE:
      g_value_set_enum (value, rtspsrc->buffer_mode);
      break;
768
769
770
771
772
773
774
775
776
777
778
779
780
    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;
    }
781
782
783
    case PROP_UDP_BUFFER_SIZE:
      g_value_set_int (value, rtspsrc->udp_buffer_size);
      break;
784
785
786
    case PROP_SHORT_HEADER:
      g_value_set_boolean (value, rtspsrc->short_header);
      break;
Wim Taymans's avatar
Wim Taymans committed
787
788
789
790
791
792
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

793
static gint
794
find_stream_by_id (GstRTSPStream * stream, gint * id)
795
{
796
  if (stream->id == *id)
797
798
799
800
801
    return 0;

  return -1;
}

802
static gint
803
find_stream_by_channel (GstRTSPStream * stream, gint * channel)
804
{
805
  if (stream->channel[0] == *channel || stream->channel[1] == *channel)
806
807
808
809
810
    return 0;

  return -1;
}

811
static gint
812
find_stream_by_pt (GstRTSPStream * stream, gint * pt)
813
{
814
  if (stream->pt == *pt)
815
816
817
818
819
    return 0;

  return -1;
}

820
821
822
823
824
825
826
static gint
find_stream_by_udpsrc (GstRTSPStream * stream, gconstpointer a)
{
  GstElement *src = (GstElement *) a;

  if (stream->udpsrc[0] == src)
    return 0;
827
828
  if (stream->udpsrc[1] == src)
    return 0;
829
830
831
832

  return -1;
}

833
834
835
836
static gint
find_stream_by_setup (GstRTSPStream * stream, gconstpointer a)
{
  /* check qualified setup_url */
837
  if (!strcmp (stream->conninfo.location, (gchar *) a))
838
839
840
841
842
843
844
845
846
847
848
849
    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;
}

850
static GstRTSPStream *
851
852
853
854
855
856
857
858
859
860
861
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;
}

862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
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
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;
}

909
910
911
912
static void
gst_rtspsrc_do_stream_connection (GstRTSPSrc * src, GstRTSPStream * stream,
    const GstSDPConnection * conn)
{
913
914
915
916
  if (conn->nettype == NULL || strcmp (conn->nettype, "IN") != 0)
    return;

  if (conn->addrtype == NULL)
917
918
919
920
921
922
923
924
925
926
    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;

927
  /* save address */
928
929
  g_free (stream->destination);
  stream->destination = g_strdup (conn->address);
930
931
932
933
934

  /* check for multicast */
  stream->is_multicast =
      gst_sdp_address_is_multicast (conn->nettype, conn->addrtype,
      conn->address);
935
  stream->ttl = conn->ttl;
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
}

/* 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
963
static GstRTSPStream *
964
gst_rtspsrc_create_stream (GstRTSPSrc * src, GstSDPMessage * sdp, gint idx)
Wim Taymans's avatar
Wim Taymans committed
965
{
966
  GstRTSPStream *stream;
967
968
969
  const gchar *control_url;
  const gchar *payload;
  const GstSDPMedia *media;
970
971

  /* get media, should not return NULL */
972
  media = gst_sdp_message_get_media (sdp, idx);
973
974
  if (media == NULL)
    return NULL;
975
976
977
978
979
980

  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;
981
  stream->added = FALSE;
982
  stream->disabled = FALSE;
983
  stream->id = src->numstreams++;
984
985
  stream->eos = FALSE;
  stream->discont = TRUE;
986
987
  stream->seqbase = -1;
  stream->timebase = -1;
988

989
990
  /* collect bandwidth information for this steam. FIXME, configure in the RTP
   * session manager to scale RTCP. */
991
992
  gst_rtspsrc_collect_bandwidth (src, sdp, media, stream);

993
994
995
  /* collect connection info */
  gst_rtspsrc_collect_connections (src, sdp, media, stream);

996
  /* we must have a payload. No payload means we cannot create caps */
997
998
999
1000
1001
  /* 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. */
1002
  if ((payload = gst_sdp_media_get_format (media, 0))) {
1003
1004
1005
1006
    stream->pt = atoi (payload);
    /* convert caps */
    stream->caps = gst_rtspsrc_media_to_caps (stream->pt, media);

1007
1008
1009
1010
1011
    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);

1012
1013
1014
1015
    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. */
1016
      if (find_stream (src, &stream->pt, (gpointer) find_stream_by_pt)) {
1017
        stream->container = TRUE;
1018
1019
        GST_DEBUG ("found another stream with pt %d, marking as container",
            stream->pt);
1020
1021
1022
      }
    }
  }
1023
1024
  /* collect port number */
  stream->port = gst_sdp_media_get_port (media);
Wim Taymans's avatar
Wim Taymans committed
1025

1026
1027
1028
  /* 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. */
1029
  control_url = gst_sdp_media_get_attribute_val (media, "control");
1030
  if (control_url == NULL)
Wim Taymans's avatar
Wim Taymans committed
1031
    control_url = gst_sdp_message_get_attribute_val_n (sdp, "control", 0);
Wim Taymans's avatar
Wim Taymans committed
1032

1033
  GST_DEBUG_OBJECT (src, "stream %d, (%p)", stream->id, stream);
1034
  GST_DEBUG_OBJECT (src, " pt: %d", stream->pt);
1035
  GST_DEBUG_OBJECT (src, " port: %d", stream->port);
1036
1037
1038
  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
1039

1040
  if (control_url != NULL) {
1041
1042
1043
1044
    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
1045
1046
     * likely build a URL that the server will fail to understand, this is ok,
     * we will fail then. */
1047
    if (g_str_has_prefix (control_url, "rtsp://"))
1048
      stream->conninfo.location = g_strdup (control_url);
1049
1050
1051
1052
    else {
      const gchar *base;
      gboolean has_slash;

Wim Taymans's avatar
Wim Taymans committed
1053
1054
1055
      if (g_strcmp0 (control_url, "*") == 0)
        control_url = "";

1056
1057
1058
      if (src->control)
        base = src->control;
      else if (src->content_base)
1059
        base = src->content_base;
1060
1061
      else if (src->conninfo.url_str)
        base = src->conninfo.url_str;
1062
1063
1064
1065
1066
1067
1068
1069
      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 */
1070
      stream->conninfo.location =
1071
1072
          g_strdup_printf ("%s%s%s", base, has_slash ? "" : "/", control_url);
    }
1073
  }
1074
1075
  GST_DEBUG_OBJECT (src, " setup: %s",
      GST_STR_NULL (stream->conninfo.location));
1076
1077
1078
1079
1080

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

  return stream;
1081
1082

  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
1083
1084
}

1085
static void
1086
gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream)
1087
{
1088
1089
1090
1091
1092
  gint i;

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

  if (stream->caps)
1093
    gst_caps_unref (stream->caps);
1094

1095
  g_free (stream->destination);
1096
  g_free (stream->control_url);
1097
  g_free (stream->conninfo.location);
1098

1099
  for (i = 0; i < 2; i++) {
1100
1101
1102
    if (stream->udpsrc[i]) {
      gst_element_set_state (stream->udpsrc[i], GST_STATE_NULL);
      gst_bin_remove (GST_BIN_CAST (src), stream->udpsrc[i]);
1103
1104
1105
      gst_object_unref (stream->udpsrc[i]);
      stream->udpsrc[i] = NULL;
    }
1106
1107
1108
1109
    if (stream->channelpad[i]) {
      gst_object_unref (stream->channelpad[i]);
      stream->channelpad[i] = NULL;
    }
1110
1111
1112
1113
1114
1115
    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;
    }
1116
  }
1117
1118
1119
1120
1121
  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;
1122
  }
1123
1124
1125
1126
1127
1128
1129
1130
  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;
  }
1131
1132
1133
1134
  if (stream->rtcppad) {
    gst_object_unref (stream->rtcppad);
    stream->rtcppad = NULL;
  }
1135
1136
1137
1138
  if (stream->session) {
    g_object_unref (stream->session);
    stream->session = NULL;
  }
1139
1140
  g_free (stream);
}
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155

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;
1156
1157
1158
1159
  if (src->manager) {
    if (src->manager_sig_id) {
      g_signal_handler_disconnect (src->manager, src->manager_sig_id);
      src->manager_sig_id = 0;
1160
    }
1161
1162
1163
    gst_element_set_state (src->manager, GST_STATE_NULL);
    gst_bin_remove (GST_BIN_CAST (src), src->manager);
    src->manager = NULL;
1164
  }
1165
1166
1167
1168
  src->numstreams = 0;
  if (src->props)
    gst_structure_free (src->props);
  src->props = NULL;
Wim Taymans's avatar
Wim Taymans committed
1169
1170
1171
1172

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

1173
1174
1175
  g_free (src->control);
  src->control = NULL;

Wim Taymans's avatar
Wim Taymans committed
1176
1177
1178
  if (src->range)
    gst_rtsp_range_free (src->range);
  src->range = NULL;
1179

1180
1181
  /* don't clear the SDP when it was used in the url */
  if (src->sdp && !src->from_sdp) {
1182
1183
1184
    gst_sdp_message_free (src->sdp);
    src->sdp = NULL;
  }
1185
1186
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
#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
1198
1199
} G_STMT_END

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1200
1201
1202
1203
#define PARSE_STRING(p, del, res)       \
G_STMT_START {                          \
  gchar *t = p;                         \
  p = strstr (p, del);                  \
1204
  if (p == NULL) {                      \
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1205
    res = NULL;                         \