gstrtspsrc.c 130 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
49
50
51
52
53
54
55
56
57
58
59
/**
 * SECTION:element-rtspsrc
 *
 * <refsect2>
 * <para>
 * 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.
 * </para>
 * <para>
 * 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
 * protocols can be controlled with the "protocols" property.
 * </para>
 * <para>
 * rtspsrc currently understands SDP as the format of the session description.
60
 * For each stream listed in the SDP a new rtp_stream%d pad will be created
61
 * with caps derived from the SDP media description. This is a caps of mime type
62
 * "application/x-rtp" that can be connected to any available RTP depayloader
63
64
65
66
67
68
 * element. 
 * </para>
 * <para>
 * rtspsrc will internally instantiate an RTP session manager element
 * that will handle the RTCP messages to and from the server, jitter removal,
 * packet reordering along with providing a clock for the pipeline. 
69
70
 * This feature is currently fully implemented with the gstrtpbin in the
 * gst-plugins-bad module.
71
72
73
74
75
76
77
78
79
80
 * </para>
 * <para>
 * rtspsrc acts like a live source and will therefore only generate data in the 
 * PLAYING state.
 * </para>
 * <title>Example launch line</title>
 * <para>
 * <programlisting>
 * gst-launch rtspsrc location=rtsp://some.server/url ! fakesink
 * </programlisting>
81
 * Establish a connection to an RTSP server and send the raw RTP packets to a fakesink.
82
83
84
 * </para>
 * </refsect2>
 *
Wim Taymans's avatar
Wim Taymans committed
85
 * Last reviewed on 2006-08-18 (0.10.5)
86
 */
Wim Taymans's avatar
Wim Taymans committed
87
88
89
90
91
92

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

#include <unistd.h>
93
#include <stdlib.h>
Wim Taymans's avatar
Wim Taymans committed
94
#include <string.h>
95
96
97
#include <locale.h>
#include <stdio.h>
#include <stdarg.h>
Wim Taymans's avatar
Wim Taymans committed
98

99
#include <gst/sdp/gstsdpmessage.h>
100
#include <gst/rtp/gstrtppayloads.h>
101
102
#include <gst/rtsp/gstrtsprange.h>

Wim Taymans's avatar
Wim Taymans committed
103
104
#include "gstrtspsrc.h"

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

Wim Taymans's avatar
Wim Taymans committed
108
/* elementfactory information */
109
static const GstElementDetails gst_rtspsrc_details =
Wim Taymans's avatar
Wim Taymans committed
110
111
GST_ELEMENT_DETAILS ("RTSP packet receiver",
    "Source/Network",
Wim Taymans's avatar
Wim Taymans committed
112
    "Receive data over the network via RTSP (RFC 2326)",
113
114
115
    "Wim Taymans <wim@fluendo.com>\n"
    "Thijs Vermeir <thijs.vermeir@barco.com>\n"
    "Lutz Mueller <lutz@topfrose.de>");
Wim Taymans's avatar
Wim Taymans committed
116

117
static GstStaticPadTemplate rtptemplate = GST_STATIC_PAD_TEMPLATE ("stream%d",
Wim Taymans's avatar
Wim Taymans committed
118
119
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
120
    GST_STATIC_CAPS ("application/x-rtp; application/x-rdt"));
Wim Taymans's avatar
Wim Taymans committed
121

122
123
124
/* templates used internally */
static GstStaticPadTemplate anysrctemplate =
GST_STATIC_PAD_TEMPLATE ("internalsrc%d",
125
126
127
128
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

129
130
131
132
133
134
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
135
136
137
138
139
140
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

141
142
143
144
145
146
147
148
#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
#define DEFAULT_TCP_TIMEOUT      20000000
#define DEFAULT_LATENCY_MS       3000
#define DEFAULT_CONNECTION_SPEED 0
Wim Taymans's avatar
Wim Taymans committed
149
150
151
152
153
154
155

enum
{
  PROP_0,
  PROP_LOCATION,
  PROP_PROTOCOLS,
  PROP_DEBUG,
Wim Taymans's avatar
Wim Taymans committed
156
  PROP_RETRY,
157
  PROP_TIMEOUT,
158
  PROP_TCP_TIMEOUT,
159
  PROP_LATENCY,
160
  PROP_CONNECTION_SPEED
Wim Taymans's avatar
Wim Taymans committed
161
162
};

163
#define GST_TYPE_RTSP_LOWER_TRANS (gst_rtsp_lower_trans_get_type())
Wim Taymans's avatar
Wim Taymans committed
164
static GType
165
gst_rtsp_lower_trans_get_type (void)
Wim Taymans's avatar
Wim Taymans committed
166
{
167
168
  static GType rtsp_lower_trans_type = 0;
  static const GFlagsValue rtsp_lower_trans[] = {
169
170
171
    {GST_RTSP_LOWER_TRANS_UDP, "UDP Unicast Mode", "udp-unicast"},
    {GST_RTSP_LOWER_TRANS_UDP_MCAST, "UDP Multicast Mode", "udp-multicast"},
    {GST_RTSP_LOWER_TRANS_TCP, "TCP interleaved mode", "tcp"},
Wim Taymans's avatar
Wim Taymans committed
172
173
174
    {0, NULL, NULL},
  };

175
176
177
  if (!rtsp_lower_trans_type) {
    rtsp_lower_trans_type =
        g_flags_register_static ("GstRTSPLowerTrans", rtsp_lower_trans);
Wim Taymans's avatar
Wim Taymans committed
178
  }
179
  return rtsp_lower_trans_type;
Wim Taymans's avatar
Wim Taymans committed
180
181
182
}

static void gst_rtspsrc_base_init (gpointer g_class);
183
static void gst_rtspsrc_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
184

185
186
187
188
189
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
190
191
static void gst_rtspsrc_uri_handler_init (gpointer g_iface,
    gpointer iface_data);
192
static GstCaps *gst_rtspsrc_media_to_caps (gint pt, const GstSDPMedia * media);
Wim Taymans's avatar
Wim Taymans committed
193

194
195
static GstStateChangeReturn gst_rtspsrc_change_state (GstElement * element,
    GstStateChange transition);
196
static void gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message);
Wim Taymans's avatar
Wim Taymans committed
197

198
199
static void gst_rtspsrc_loop_send_cmd (GstRTSPSrc * src, gint cmd,
    gboolean flush);
200
201
202
static GstRTSPResult gst_rtspsrc_send_cb (GstRTSPExtension * ext,
    GstRTSPMessage * request, GstRTSPMessage * response, GstRTSPSrc * src);

203
static gboolean gst_rtspsrc_open (GstRTSPSrc * src);
204
static gboolean gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment);
205
206
207
static gboolean gst_rtspsrc_pause (GstRTSPSrc * src);
static gboolean gst_rtspsrc_close (GstRTSPSrc * src);

208
209
210
static gboolean gst_rtspsrc_uri_set_uri (GstURIHandler * handler,
    const gchar * uri);

211
static gboolean gst_rtspsrc_activate_streams (GstRTSPSrc * src);
Wim Taymans's avatar
Wim Taymans committed
212
static void gst_rtspsrc_loop (GstRTSPSrc * src);
213
214
static void gst_rtspsrc_stream_push_event (GstRTSPSrc * src,
    GstRTSPStream * stream, GstEvent * event);
215
static void gst_rtspsrc_push_event (GstRTSPSrc * src, GstEvent * event);
Wim Taymans's avatar
Wim Taymans committed
216

217
218
219
220
221
/* commands we send to out loop to notify it of events */
#define CMD_WAIT	0
#define CMD_RECONNECT	1
#define CMD_STOP	2

Wim Taymans's avatar
Wim Taymans committed
222
223
/*static guint gst_rtspsrc_signals[LAST_SIGNAL] = { 0 }; */

224
225
static void
_do_init (GType rtspsrc_type)
Wim Taymans's avatar
Wim Taymans committed
226
{
227
228
229
230
231
232
233
234
235
236
  static const GInterfaceInfo urihandler_info = {
    gst_rtspsrc_uri_handler_init,
    NULL,
    NULL
  };

  GST_DEBUG_CATEGORY_INIT (rtspsrc_debug, "rtspsrc", 0, "RTSP src");

  g_type_add_interface_static (rtspsrc_type, GST_TYPE_URI_HANDLER,
      &urihandler_info);
Wim Taymans's avatar
Wim Taymans committed
237
238
}

239
240
GST_BOILERPLATE_FULL (GstRTSPSrc, gst_rtspsrc, GstBin, GST_TYPE_BIN, _do_init);

Wim Taymans's avatar
Wim Taymans committed
241
242
243
244
245
246
247
248
249
250
251
252
static void
gst_rtspsrc_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&rtptemplate));

  gst_element_class_set_details (element_class, &gst_rtspsrc_details);
}

static void
253
gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
Wim Taymans's avatar
Wim Taymans committed
254
255
256
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
257
  GstBinClass *gstbin_class;
Wim Taymans's avatar
Wim Taymans committed
258
259
260

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
261
  gstbin_class = (GstBinClass *) klass;
Wim Taymans's avatar
Wim Taymans committed
262
263
264
265

  gobject_class->set_property = gst_rtspsrc_set_property;
  gobject_class->get_property = gst_rtspsrc_get_property;

266
267
  gobject_class->finalize = gst_rtspsrc_finalize;

Wim Taymans's avatar
Wim Taymans committed
268
  g_object_class_install_property (gobject_class, PROP_LOCATION,
Wim Taymans's avatar
Wim Taymans committed
269
270
      g_param_spec_string ("location", "RTSP Location",
          "Location of the RTSP url to read",
271
          DEFAULT_LOCATION, G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
272

Wim Taymans's avatar
Wim Taymans committed
273
  g_object_class_install_property (gobject_class, PROP_PROTOCOLS,
274
      g_param_spec_flags ("protocols", "Protocols",
275
          "Allowed lower transport protocols", GST_TYPE_RTSP_LOWER_TRANS,
276
          DEFAULT_PROTOCOLS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Wim Taymans's avatar
Wim Taymans committed
277

Wim Taymans's avatar
Wim Taymans committed
278
  g_object_class_install_property (gobject_class, PROP_DEBUG,
Wim Taymans's avatar
Wim Taymans committed
279
      g_param_spec_boolean ("debug", "Debug",
280
          "Dump request and response messages to stdout",
Wim Taymans's avatar
Wim Taymans committed
281
282
          DEFAULT_DEBUG, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

Wim Taymans's avatar
Wim Taymans committed
283
284
285
286
287
288
  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,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

289
290
  g_object_class_install_property (gobject_class, PROP_TIMEOUT,
      g_param_spec_uint64 ("timeout", "Timeout",
291
          "Retry TCP transport after UDP timeout microseconds (0 = disabled)",
292
293
294
          0, G_MAXUINT64, DEFAULT_TIMEOUT,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

295
296
297
298
299
300
  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,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

301
302
303
304
305
  g_object_class_install_property (gobject_class, PROP_LATENCY,
      g_param_spec_uint ("latency", "Buffer latency in ms",
          "Amount of ms to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

306
307
308
309
310
311
  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,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

Wim Taymans's avatar
Wim Taymans committed
312
  gstelement_class->change_state = gst_rtspsrc_change_state;
313
314

  gstbin_class->handle_message = gst_rtspsrc_handle_message;
315
316

  gst_rtsp_ext_list_init ();
Wim Taymans's avatar
Wim Taymans committed
317
318
319
}

static void
320
gst_rtspsrc_init (GstRTSPSrc * src, GstRTSPSrcClass * g_class)
Wim Taymans's avatar
Wim Taymans committed
321
{
322
  src->location = g_strdup (DEFAULT_LOCATION);
323
  src->url = NULL;
324

325
326
327
  /* get a list of all extensions */
  src->extensions = gst_rtsp_ext_list_get ();

328
329
330
  /* connect to send signal */
  gst_rtsp_ext_list_connect (src->extensions, "send",
      (GCallback) gst_rtspsrc_send_cb, src);
331

332
333
334
335
336
337
  /* 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 */
338
339
  src->state_rec_lock = g_new (GStaticRecMutex, 1);
  g_static_rec_mutex_init (src->state_rec_lock);
340
341
342
343
344

  /* protects access to the server connection */
  src->conn_rec_lock = g_new (GStaticRecMutex, 1);
  g_static_rec_mutex_init (src->conn_rec_lock);

345
  src->state = GST_RTSP_STATE_INVALID;
346
347
348
349
350
351
352
353
354
}

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

  rtspsrc = GST_RTSPSRC (object);

355
  gst_rtsp_ext_list_free (rtspsrc->extensions);
356
  g_free (rtspsrc->location);
357
  g_free (rtspsrc->req_location);
358
  g_free (rtspsrc->content_base);
359
  gst_rtsp_url_free (rtspsrc->url);
360
361
362
363

  /* free locks */
  g_static_rec_mutex_free (rtspsrc->stream_rec_lock);
  g_free (rtspsrc->stream_rec_lock);
364
365
  g_static_rec_mutex_free (rtspsrc->state_rec_lock);
  g_free (rtspsrc->state_rec_lock);
366
367
  g_static_rec_mutex_free (rtspsrc->conn_rec_lock);
  g_free (rtspsrc->conn_rec_lock);
368
369

  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
370
371
372
373
374
375
376
377
378
379
380
381
}

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:
382
383
      gst_rtspsrc_uri_set_uri (GST_URI_HANDLER (rtspsrc),
          g_value_get_string (value));
Wim Taymans's avatar
Wim Taymans committed
384
385
386
387
388
389
390
      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
391
392
393
    case PROP_RETRY:
      rtspsrc->retry = g_value_get_uint (value);
      break;
394
    case PROP_TIMEOUT:
395
      rtspsrc->udp_timeout = g_value_get_uint64 (value);
396
      break;
397
398
399
400
401
402
    case PROP_TCP_TIMEOUT:
    {
      guint64 timeout = g_value_get_uint64 (value);

      rtspsrc->tcp_timeout.tv_sec = timeout / G_USEC_PER_SEC;
      rtspsrc->tcp_timeout.tv_usec = timeout % G_USEC_PER_SEC;
403
404
405
406
407

      if (timeout != 0)
        rtspsrc->ptcp_timeout = &rtspsrc->tcp_timeout;
      else
        rtspsrc->ptcp_timeout = NULL;
408
409
      break;
    }
410
411
412
    case PROP_LATENCY:
      rtspsrc->latency = g_value_get_uint (value);
      break;
413
414
415
    case PROP_CONNECTION_SPEED:
      rtspsrc->connection_speed = g_value_get_uint (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
    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:
      g_value_set_string (value, rtspsrc->location);
      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
440
441
442
    case PROP_RETRY:
      g_value_set_uint (value, rtspsrc->retry);
      break;
443
    case PROP_TIMEOUT:
444
445
446
447
448
449
450
451
452
      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);
453
      break;
454
    }
455
456
457
    case PROP_LATENCY:
      g_value_set_uint (value, rtspsrc->latency);
      break;
458
459
460
    case PROP_CONNECTION_SPEED:
      g_value_set_uint (value, rtspsrc->connection_speed);
      break;
Wim Taymans's avatar
Wim Taymans committed
461
462
463
464
465
466
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

467
468
469
470
471
472
473
474
475
476
477
static gint
find_stream_by_id (GstRTSPStream * stream, gconstpointer a)
{
  gint id = GPOINTER_TO_INT (a);

  if (stream->id == id)
    return 0;

  return -1;
}

478
479
480
481
482
483
484
485
486
487
488
static gint
find_stream_by_channel (GstRTSPStream * stream, gconstpointer a)
{
  gint channel = GPOINTER_TO_INT (a);

  if (stream->channel[0] == channel || stream->channel[1] == channel)
    return 0;

  return -1;
}

489
490
491
492
493
494
495
496
497
498
499
static gint
find_stream_by_pt (GstRTSPStream * stream, gconstpointer a)
{
  gint pt = GPOINTER_TO_INT (a);

  if (stream->pt == pt)
    return 0;

  return -1;
}

500
501
502
503
504
505
506
static gint
find_stream_by_udpsrc (GstRTSPStream * stream, gconstpointer a)
{
  GstElement *src = (GstElement *) a;

  if (stream->udpsrc[0] == src)
    return 0;
507
508
  if (stream->udpsrc[1] == src)
    return 0;
509
510
511
512

  return -1;
}

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
static gint
find_stream_by_setup (GstRTSPStream * stream, gconstpointer a)
{
  /* check qualified setup_url */
  if (!strcmp (stream->setup_url, (gchar *) a))
    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;
}

530
531
532
533
534
535
536
537
538
539
540
541
GstRTSPStream *
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;
}

542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
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;
}

Wim Taymans's avatar
Wim Taymans committed
589
static GstRTSPStream *
590
gst_rtspsrc_create_stream (GstRTSPSrc * src, GstSDPMessage * sdp, gint idx)
Wim Taymans's avatar
Wim Taymans committed
591
{
592
  GstRTSPStream *stream;
593
594
595
  const gchar *control_url;
  const gchar *payload;
  const GstSDPMedia *media;
596
597

  /* get media, should not return NULL */
598
  media = gst_sdp_message_get_media (sdp, idx);
599
600
  if (media == NULL)
    return NULL;
601
602
603
604
605
606

  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;
607
  stream->added = FALSE;
608
  stream->disabled = FALSE;
609
  stream->id = src->numstreams++;
610
611
  stream->eos = FALSE;
  stream->discont = TRUE;
612

613
614
615
  /* collect bandwidth information for this steam */
  gst_rtspsrc_collect_bandwidth (src, sdp, media, stream);

616
617
  /* we must have a payload. No payload means we cannot create caps */
  /* FIXME, handle multiple formats. */
618
  if ((payload = gst_sdp_media_get_format (media, 0))) {
619
620
621
622
623
624
625
626
    stream->pt = atoi (payload);
    /* convert caps */
    stream->caps = gst_rtspsrc_media_to_caps (stream->pt, media);

    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. */
627
628
      if (find_stream (src, GINT_TO_POINTER (stream->pt),
              (gpointer) find_stream_by_pt)) {
629
630
631
632
        stream->container = TRUE;
      }
    }
  }
Wim Taymans's avatar
Wim Taymans committed
633

634
635
636
  /* 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. */
637
  control_url = gst_sdp_media_get_attribute_val (media, "control");
Wim Taymans's avatar
Wim Taymans committed
638

639

640
  GST_DEBUG_OBJECT (src, "stream %d, (%p)", stream->id, stream);
641
642
643
644
  GST_DEBUG_OBJECT (src, " pt: %d", stream->pt);
  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
645

646
  if (control_url != NULL) {
647
648
649
650
    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
651
652
     * likely build a URL that the server will fail to understand, this is ok,
     * we will fail then. */
653
654
    if (g_str_has_prefix (control_url, "rtsp://"))
      stream->setup_url = g_strdup (control_url);
655
656
657
    else if (src->content_base)
      stream->setup_url =
          g_strdup_printf ("%s%s", src->content_base, control_url);
658
    else
659
660
      stream->setup_url =
          g_strdup_printf ("%s/%s", src->req_location, control_url);
661
662
663
664
665
666
667
  }
  GST_DEBUG_OBJECT (src, " setup: %s", GST_STR_NULL (stream->setup_url));

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

  return stream;
668
669

  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
670
671
}

672
static void
673
gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream)
674
{
675
676
677
678
679
  gint i;

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

  if (stream->caps)
680
    gst_caps_unref (stream->caps);
681

682
  g_free (stream->control_url);
683
  g_free (stream->setup_url);
684

685
686
687
688
689
690
691
692
693
694
695
696
697
  for (i = 0; i < 2; i++) {
    GstElement *udpsrc = stream->udpsrc[i];

    if (udpsrc) {
      GstPad *pad;

      /* unlink the pad */
      pad = gst_element_get_pad (udpsrc, "src");
      if (stream->channelpad[i]) {
        gst_pad_unlink (pad, stream->channelpad[i]);
        gst_object_unref (stream->channelpad[i]);
        stream->channelpad[i] = NULL;
      }
698

699
700
701
702
703
704
      gst_element_set_state (udpsrc, GST_STATE_NULL);
      gst_bin_remove (GST_BIN_CAST (src), udpsrc);
      gst_object_unref (stream->udpsrc[i]);
      stream->udpsrc[i] = NULL;
    }
  }
705
706
707
708
709
710
  if (stream->udpsink) {
    gst_element_set_state (stream->udpsink, GST_STATE_NULL);
    gst_bin_remove (GST_BIN_CAST (src), stream->udpsink);
    gst_object_unref (stream->udpsink);
    stream->udpsink = NULL;
  }
711
712
713
714
715
716
717
718
  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;
  }
719
720
  g_free (stream);
}
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735

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;
736
737
738
739
740
741
742
743
744
  if (src->session) {
    if (src->session_sig_id) {
      g_signal_handler_disconnect (src->session, src->session_sig_id);
      src->session_sig_id = 0;
    }
    gst_element_set_state (src->session, GST_STATE_NULL);
    gst_bin_remove (GST_BIN_CAST (src), src->session);
    src->session = NULL;
  }
745
746
747
748
749
750
  src->numstreams = 0;
  if (src->props)
    gst_structure_free (src->props);
  src->props = NULL;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
751
752
753
754
755
756
757
758
759
760
761
#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
762
763
} G_STMT_END

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
764
765
766
767
#define PARSE_STRING(p, del, res)       \
G_STMT_START {                          \
  gchar *t = p;                         \
  p = strstr (p, del);                  \
768
  if (p == NULL) {                      \
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
769
    res = NULL;                         \
770
771
    p = t;                              \
  }                                     \
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
772
773
774
775
776
  else {                                \
    *p = '\0';                          \
    p++;                                \
    res = t;                            \
  }                                     \
Wim Taymans's avatar
Wim Taymans committed
777
778
} G_STMT_END

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
779
780
#define SKIP_SPACES(p)                  \
  while (*p && g_ascii_isspace (*p))    \
Wim Taymans's avatar
Wim Taymans committed
781
782
    p++;

783
784
785
786
/* rtpmap contains:
 *
 *  <payload> <encoding_name>/<clock_rate>[/<encoding_params>]
 */
Wim Taymans's avatar
Wim Taymans committed
787
static gboolean
788
gst_rtspsrc_parse_rtpmap (const gchar * rtpmap, gint * payload, gchar ** name,
Wim Taymans's avatar
Wim Taymans committed
789
790
791
792
    gint * rate, gchar ** params)
{
  gchar *p, *t;

793
  t = p = (gchar *) rtpmap;
Wim Taymans's avatar
Wim Taymans committed
794

Wim Taymans's avatar
Wim Taymans committed
795
796
  PARSE_INT (p, " ", *payload);
  if (*payload == -1)
Wim Taymans's avatar
Wim Taymans committed
797
798
    return FALSE;

Wim Taymans's avatar
Wim Taymans committed
799
  SKIP_SPACES (p);
Wim Taymans's avatar
Wim Taymans committed
800
801
802
  if (*p == '\0')
    return FALSE;

Wim Taymans's avatar
Wim Taymans committed
803
  PARSE_STRING (p, "/", *name);
804
  if (*name == NULL) {
805
    GST_DEBUG ("no rate, name %s", p);
806
807
    /* no rate, assume -1 then, this is not supposed to happen but RealMedia
     * streams seem to omit the rate. */
808
809
810
811
    *name = p;
    *rate = -1;
    return TRUE;
  }
Wim Taymans's avatar
Wim Taymans committed
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833

  t = p;
  p = strstr (p, "/");
  if (p == NULL) {
    *rate = atoi (t);
    return TRUE;
  }
  *p = '\0';
  p++;
  *rate = atoi (t);

  t = p;
  if (*p == '\0')
    return TRUE;
  *params = t;

  return TRUE;
}

/*
 *  Mapping of caps to and from SDP fields:
 *
834
 *   m=<media> <UDP port> RTP/AVP <payload> 
Wim Taymans's avatar
Wim Taymans committed
835
 *   a=rtpmap:<payload> <encoding_name>/<clock_rate>[/<encoding_params>]
836
 *   a=fmtp:<payload> <param>[=<value>];...
Wim Taymans's avatar
Wim Taymans committed
837
838
 */
static GstCaps *
839
gst_rtspsrc_media_to_caps (gint pt, const GstSDPMedia * media)
Wim Taymans's avatar
Wim Taymans committed
840
841
{
  GstCaps *caps;
842
843
  const gchar *rtpmap;
  const gchar *fmtp;
Wim Taymans's avatar
Wim Taymans committed
844
845
846
  gchar *name = NULL;
  gint rate = -1;
  gchar *params = NULL;
847
  gchar *tmp;
Wim Taymans's avatar
Wim Taymans committed
848
  GstStructure *s;
849
850
851
852
  gint payload = 0;
  gboolean ret;

  /* get and parse rtpmap */
853
  if ((rtpmap = gst_sdp_media_get_attribute_val (media, "rtpmap"))) {
854
855
856
857
858
859
860
861
    ret = gst_rtspsrc_parse_rtpmap (rtpmap, &payload, &name, &rate, &params);
    if (ret) {
      if (payload != pt) {
        /* we ignore the rtpmap if the payload type is different. */
        g_warning ("rtpmap of wrong payload type, ignoring");
        name = NULL;
        rate = -1;
        params = NULL;
Wim Taymans's avatar
Wim Taymans committed
862
      }
863
864
865
866
867
868
869
870
871
872
873
    } else {
      /* if we failed to parse the rtpmap for a dynamic payload type, we have an
       * error */
      if (pt >= 96)
        goto no_rtpmap;
      /* else we can ignore */
      g_warning ("error parsing rtpmap, ignoring");
    }
  } else {
    /* dynamic payloads need rtpmap or we fail */
    if (pt >= 96)
874
      goto no_rtpmap;
Wim Taymans's avatar
Wim Taymans committed
875
  }
876
877
878
  /* check if we have a rate, if not, we need to look up the rate from the
   * default rates based on the payload types. */
  if (rate == -1) {
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
    const GstRTPPayloadInfo *info;

    if (GST_RTP_PAYLOAD_IS_DYNAMIC (pt)) {
      /* dynamic types, use media and encoding_name */
      tmp = g_ascii_strdown (media->media, -1);
      info = gst_rtp_payload_info_for_name (tmp, name);
      g_free (tmp);
    } else {
      /* static types, use payload type */
      info = gst_rtp_payload_info_for_pt (pt);
    }

    if (info) {
      if ((rate = info->clock_rate) == 0)
        rate = -1;
    }
895
896
897
898
    /* we fail if we cannot find one */
    if (rate == -1)
      goto no_rate;
  }
Wim Taymans's avatar
Wim Taymans committed
899

900
  tmp = g_ascii_strdown (media->media, -1);
901
  caps = gst_caps_new_simple ("application/x-unknown",
902
903
      "media", G_TYPE_STRING, tmp, "payload", G_TYPE_INT, pt, NULL);
  g_free (tmp);
Wim Taymans's avatar
Wim Taymans committed
904
905
  s = gst_caps_get_structure (caps, 0);

906
  gst_structure_set (s, "clock-rate", G_TYPE_INT, rate, NULL);
Wim Taymans's avatar
Wim Taymans committed
907

908
  /* encoding name must be upper case */
909
  if (name != NULL) {
910
911
912
    tmp = g_ascii_strup (name, -1);
    gst_structure_set (s, "encoding-name", G_TYPE_STRING, tmp, NULL);
    g_free (tmp);
913
  }
Wim Taymans's avatar
Wim Taymans committed
914

915
  /* params must be lower case */
916
  if (params != NULL) {
917
918
919
    tmp = g_ascii_strdown (params, -1);
    gst_structure_set (s, "encoding-params", G_TYPE_STRING, tmp, NULL);
    g_free (tmp);
920
  }
Wim Taymans's avatar
Wim Taymans committed
921

Wim Taymans's avatar
Wim Taymans committed
922
  /* parse optional fmtp: field */
923
  if ((fmtp = gst_sdp_media_get_attribute_val (media, "fmtp"))) {
Wim Taymans's avatar
Wim Taymans committed
924
925
926
    gchar *p;
    gint payload = 0;

927
    p = (gchar *) fmtp;
Wim Taymans's avatar
Wim Taymans committed
928

929
    /* p is now of the format <payload> <param>[=<value>];... */
Wim Taymans's avatar
Wim Taymans committed
930
931
932
933
934
    PARSE_INT (p, " ", payload);
    if (payload != -1 && payload == pt) {
      gchar **pairs;
      gint i;

935
      /* <param>[=<value>] are separated with ';' */
Wim Taymans's avatar
Wim Taymans committed
936
937
      pairs = g_strsplit (p, ";", 0);
      for (i = 0; pairs[i]; i++) {
938
939
940
941
942
943
944
945
        gchar *valpos;
        gchar *val, *key;

        /* the key may not have a '=', the value can have other '='s */
        valpos = strstr (pairs[i], "=");
        if (valpos) {
          /* we have a '=' and thus a value, remove the '=' with \0 */
          *valpos = '\0';
Wim Taymans's avatar
Wim Taymans committed
946
          /* value is everything between '=' and ';'. FIXME, strip? */
947
948
949
950
          val = g_strstrip (valpos + 1);
        } else {
          /* simple <param>;.. is translated into <param>=1;... */
          val = "1";
Wim Taymans's avatar
Wim Taymans committed
951
        }
952
        /* strip the key of spaces, convert key to lowercase but not the value. */
953
        key = g_strstrip (pairs[i]);
954
955
956
957
958
        if (strlen (key) > 1) {
          tmp = g_ascii_strdown (key, -1);
          gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
          g_free (tmp);
        }
Wim Taymans's avatar
Wim Taymans committed
959
960
961
962
      }
      g_strfreev (pairs);
    }
  }
Wim Taymans's avatar
Wim Taymans committed
963
  return caps;
964
965
966
967
968
969
970

  /* ERRORS */
no_rtpmap:
  {
    g_warning ("rtpmap type not given for dynamic payload %d", pt);
    return NULL;
  }
971
972
973
974
975
no_rate:
  {
    g_warning ("rate unknown for payload type %d", pt);
    return NULL;
  }
Wim Taymans's avatar
Wim Taymans committed
976
977
978
}

static gboolean
979
gst_rtspsrc_alloc_udp_ports (GstRTSPStream * stream,
Wim Taymans's avatar
Wim Taymans committed
980
    gint * rtpport, gint * rtcpport)
Wim Taymans's avatar
Wim Taymans committed
981
982
{
  GstRTSPSrc *src;
983
984
  GstStateChangeReturn ret;
  GstElement *tmp, *udpsrc0, *udpsrc1;
Wim Taymans's avatar
Wim Taymans committed
985
986
  gint tmp_rtp, tmp_rtcp;
  guint count;
Wim Taymans's avatar
Wim Taymans committed
987
988
989

  src = stream->parent;

Wim Taymans's avatar
Wim Taymans committed
990
  tmp = NULL;
991
992
  udpsrc0 = NULL;
  udpsrc1 = NULL;
Wim Taymans's avatar
Wim Taymans committed
993
  count = 0;
Wim Taymans's avatar
Wim Taymans committed
994

995
  /* try to allocate 2 UDP ports, the RTP port should be an even
Wim Taymans's avatar
Wim Taymans committed
996
997
   * number and the RTCP port should be the next (uneven) port */
again:
998
999
1000
  udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0:0", NULL);
  if (udpsrc0 == NULL)
    goto no_udp_protocol;
Wim Taymans's avatar
Wim Taymans committed
1001

1002
  ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED);
1003
  if (ret == GST_STATE_CHANGE_FAILURE)
1004
    goto start_udp_failure;
Wim Taymans's avatar
Wim Taymans committed
1005

1006
  g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
Wim Taymans's avatar
Wim Taymans committed
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
  GST_DEBUG_OBJECT (src, "got RTP port %d", tmp_rtp);

  /* check if port is even */
  if ((tmp_rtp & 0x01) != 0) {
    /* port not even, close and allocate another */
    count++;
    if (count > src->retry)
      goto no_ports;

    GST_DEBUG_OBJECT (src, "RTP port not even, retry %d", count);
    /* have to keep port allocated so we can get a new one */
    if (tmp != NULL) {
      GST_DEBUG_OBJECT (src, "free temp");
      gst_element_set_state (tmp, GST_STATE_NULL);
      gst_object_unref (tmp);
    }
1023
    tmp = udpsrc0;
Wim Taymans's avatar
Wim Taymans committed
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
    GST_DEBUG_OBJECT (src, "retry %d", count);
    goto again;
  }
  /* free leftover temp element/port */
  if (tmp) {
    gst_element_set_state (tmp, GST_STATE_NULL);
    gst_object_unref (tmp);
    tmp = NULL;
  }

  /* allocate port+1 for RTCP now */
1035
1036
  udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
  if (udpsrc1 == NULL)
Wim Taymans's avatar
Wim Taymans committed
1037
1038
    goto no_udp_rtcp_protocol;

Wim Taymans's avatar
Wim Taymans committed
1039
1040
  /* set port */
  tmp_rtcp = tmp_rtp + 1;
1041
  g_object_set (G_OBJECT (udpsrc1), "port", tmp_rtcp, NULL);
Wim Taymans's avatar
Wim Taymans committed
1042

Wim Taymans's avatar
Wim Taymans committed
1043
  GST_DEBUG_OBJECT (src, "starting RTCP on port %d", tmp_rtcp);
1044
  ret = gst_element_set_state (udpsrc1, GST_STATE_PAUSED);
Wim Taymans's avatar
Wim Taymans committed
1045
1046
  /* FIXME, this could fail if the next port is not free, we
   * should retry with another port then */
1047
  if (ret == GST_STATE_CHANGE_FAILURE)
Wim Taymans's avatar
Wim Taymans committed
1048
1049
    goto start_rtcp_failure;

Wim Taymans's avatar
Wim Taymans committed
1050
  /* all fine, do port check */
1051
1052
  g_object_get (G_OBJECT (udpsrc0), "port", rtpport, NULL);
  g_object_get (G_OBJECT (udpsrc1), "port", rtcpport, NULL);
Wim Taymans's avatar
Wim Taymans committed
1053

1054
  /* this should not happen... */
Wim Taymans's avatar
Wim Taymans committed
1055
1056
1057
  if (*rtpport != tmp_rtp || *rtcpport != tmp_rtcp)
    goto port_error;

1058
1059
  /* we keep these elements, we configure all in configure_transport when the
   * server told us to really use the UDP ports. */
1060
1061
1062
1063
1064
1065
  stream->udpsrc[0] = gst_object_ref (udpsrc0);
  stream->udpsrc[1] = gst_object_ref (udpsrc1);

  /* they are ours now */
  gst_object_sink (udpsrc0);
  gst_object_sink (udpsrc1);
Wim Taymans's avatar
Wim Taymans committed
1066

Wim Taymans's avatar
Wim Taymans committed
1067
1068
  return TRUE;

Wim Taymans's avatar
Wim Taymans committed
1069
  /* ERRORS */
1070
no_udp_protocol:
Wim Taymans's avatar
Wim Taymans committed
1071
  {
1072
    GST_DEBUG_OBJECT (src, "could not get UDP source");
Wim Taymans's avatar
Wim Taymans committed
1073
    goto cleanup;
Wim Taymans's avatar
Wim Taymans committed
1074
  }
1075
start_udp_failure:
Wim Taymans's avatar
Wim Taymans committed
1076
  {
1077
    GST_DEBUG_OBJECT (src, "could not start UDP source");
Wim Taymans's avatar
Wim Taymans committed
1078
    goto cleanup;
Wim Taymans's avatar
Wim Taymans committed
1079
  }
Wim Taymans's avatar
Wim Taymans committed
1080
no_ports:
Wim Taymans's avatar
Wim Taymans committed
1081
  {
1082
1083
    GST_DEBUG_OBJECT (src, "could not allocate UDP port pair after %d retries",
        count);
Wim Taymans's avatar
Wim Taymans committed
1084
1085
1086
1087
    goto cleanup;
  }
no_udp_rtcp_protocol:
  {
1088
    GST_DEBUG_OBJECT (src, "could not get UDP source for RTCP");
Wim Taymans's avatar
Wim Taymans committed
1089
    goto cleanup;
Wim Taymans's avatar
Wim Taymans committed
1090
1091
1092
  }
start_rtcp_failure:
  {
1093
    GST_DEBUG_OBJECT (src, "could not start UDP source for RTCP");
Wim Taymans's avatar
Wim Taymans committed
1094
1095
1096
1097
    goto cleanup;
  }
port_error:
  {
1098
    GST_DEBUG_OBJECT (src, "ports don't match rtp: %d<->%d, rtcp: %d<->%d",
Wim Taymans's avatar
Wim Taymans committed
1099
1100
1101
1102
1103
1104
1105
1106
1107
        tmp_rtp, *rtpport, tmp_rtcp, *rtcpport);
    goto cleanup;
  }
cleanup:
  {
    if (tmp) {
      gst_element_set_state (tmp, GST_STATE_NULL);
      gst_object_unref (tmp);
    }
1108
1109
1110
    if (udpsrc0) {
      gst_element_set_state (udpsrc0, GST_STATE_NULL);
      gst_object_unref (udpsrc0);
Wim Taymans's avatar
Wim Taymans committed
1111
    }
1112
1113
1114
    if (udpsrc1) {
      gst_element_set_state (udpsrc1, GST_STATE_NULL);
      gst_object_unref (udpsrc1);
Wim Taymans's avatar
Wim Taymans committed
1115
    }
Wim Taymans's avatar
Wim Taymans committed
1116
1117
1118
1119
    return FALSE;
  }
}

1120
1121
1122
1123
static void
gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush)
{
  GstEvent *event;
1124
1125
1126
  gint cmd, i;
  GstState state;
  GList *walk;
1127
  GstClock *clock;
1128
  GstClockTime base_time = GST_CLOCK_TIME_NONE;
1129
1130
1131

  if (flush) {
    event = gst_event_new_flush_start ();
1132
    GST_DEBUG_OBJECT (src, "start flush");
1133
1134
    cmd = CMD_STOP;
    state = GST_STATE_PAUSED;
1135
1136
  } else {
    event = gst_event_new_flush_stop ();
1137
    GST_DEBUG_OBJECT (src, "stop flush");
1138
1139
    cmd = CMD_WAIT;
    state = GST_STATE_PLAYING;
1140
1141
1142
1143
1144
    clock = gst_element_get_clock (GST_ELEMENT_CAST (src));
    if (clock) {
      base_time = gst_clock_get_time (clock);
      gst_object_unref (clock);
    }
1145
1146
  }
  gst_rtspsrc_push_event (src, event);
1147
1148
1149
1150
1151
1152
1153
1154
  gst_rtspsrc_loop_send_cmd (src, cmd, flush);

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

    for (i = 0; i < 2; i++) {
      if (stream->udpsrc[i]) {
1155
1156
        if (base_time != -1)
          gst_element_set_base_time (stream->udpsrc[i], base_time);
1157
1158
1159
1160
        gst_element_set_state (stream->udpsrc[i], state);
      }
    }
  }
1161
1162
}

1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
static GstRTSPResult
gst_rtspsrc_connection_send (GstRTSPSrc * src, GstRTSPMessage * message,
    GTimeVal * timeout)
{
  GstRTSPResult ret;

  GST_RTSP_CONN_LOCK (src);
  ret = gst_rtsp_connection_send (src->connection, message, timeout);
  GST_RTSP_CONN_UNLOCK (src);

  return ret;
}

static GstRTSPResult
gst_rtspsrc_connection_receive (GstRTSPSrc * src, GstRTSPMessage * message,
    GTimeVal * timeout)
{
  GstRTSPResult ret;

  GST_RTSP_CONN_LOCK (src);
  ret = gst_rtsp_connection_receive (src->connection, message, timeout);
  GST_RTSP_CONN_UNLOCK (src);

  return ret;
}

1189
1190
1191
1192
1193
static gboolean
gst_rtspsrc_do_seek (GstRTSPSrc * src, GstSegment * segment)
{
  gboolean res;

1194
  src->state = GST_RTSP_STATE_SEEKING;
1195
  /* PLAY will add the range header now. */
1196
  src->need_range = TRUE;
1197

1198
  res = gst_rtspsrc_play (src, segment);
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248

  return res;
}

static gboolean
gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
{
  gboolean res;
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
  gint64 cur, stop;
  gboolean flush;
  gboolean update;
  GstSegment seeksegment = { 0, };

  if (event) {
    GST_DEBUG_OBJECT (src, "doing seek with event");

    gst_event_parse_seek (event, &rate, &format, &flags,
        &cur_type, &