rtsp-client: make sure sessmedia will not get freed while used
[platform/upstream/gstreamer.git] / subprojects / gst-rtsp-server / gst / rtsp-server / rtsp-session.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /**
20  * SECTION:rtsp-session
21  * @short_description: An object to manage media
22  * @see_also: #GstRTSPSessionPool, #GstRTSPSessionMedia, #GstRTSPMedia
23  *
24  * The #GstRTSPSession is identified by an id, unique in the
25  * #GstRTSPSessionPool that created the session and manages media and its
26  * configuration.
27  *
28  * A #GstRTSPSession has a timeout that can be retrieved with
29  * gst_rtsp_session_get_timeout(). You can check if the sessions is expired with
30  * gst_rtsp_session_is_expired(). gst_rtsp_session_touch() will reset the
31  * expiration counter of the session.
32  *
33  * When a client configures a media with SETUP, a session will be created to
34  * keep track of the configuration of that media. With
35  * gst_rtsp_session_manage_media(), the media is added to the managed media
36  * in the session. With gst_rtsp_session_release_media() the media can be
37  * released again from the session. Managed media is identified in the sessions
38  * with a url. Use gst_rtsp_session_get_media() to get the media that matches
39  * (part of) the given url.
40  *
41  * The media in a session can be iterated with gst_rtsp_session_filter().
42  *
43  * Last reviewed on 2013-07-11 (1.0.0)
44  */
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include <string.h>
50
51 #include "rtsp-session.h"
52
53 struct _GstRTSPSessionPrivate
54 {
55   GMutex lock;                  /* protects everything but sessionid and create_time */
56   gchar *sessionid;
57
58   guint timeout;
59   gboolean timeout_always_visible;
60   GMutex last_access_lock;
61   gint64 last_access_monotonic_time;
62   gint64 last_access_real_time;
63   gint expire_count;
64
65   GList *medias;
66   guint medias_cookie;
67   guint extra_time_timeout;
68 };
69
70 #undef DEBUG
71
72 #define DEFAULT_TIMEOUT        60
73 #define NO_TIMEOUT              -1
74 #define DEFAULT_ALWAYS_VISIBLE  FALSE
75 #define DEFAULT_EXTRA_TIMEOUT 5
76
77 enum
78 {
79   PROP_0,
80   PROP_SESSIONID,
81   PROP_TIMEOUT,
82   PROP_TIMEOUT_ALWAYS_VISIBLE,
83   PROP_EXTRA_TIME_TIMEOUT,
84   PROP_LAST
85 };
86
87 GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
88 #define GST_CAT_DEFAULT rtsp_session_debug
89
90 static void gst_rtsp_session_get_property (GObject * object, guint propid,
91     GValue * value, GParamSpec * pspec);
92 static void gst_rtsp_session_set_property (GObject * object, guint propid,
93     const GValue * value, GParamSpec * pspec);
94 static void gst_rtsp_session_finalize (GObject * obj);
95
96 G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
97
98 static void
99 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
100 {
101   GObjectClass *gobject_class;
102
103   gobject_class = G_OBJECT_CLASS (klass);
104
105   gobject_class->get_property = gst_rtsp_session_get_property;
106   gobject_class->set_property = gst_rtsp_session_set_property;
107   gobject_class->finalize = gst_rtsp_session_finalize;
108
109   g_object_class_install_property (gobject_class, PROP_SESSIONID,
110       g_param_spec_string ("sessionid", "Sessionid", "the session id",
111           NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
112           G_PARAM_STATIC_STRINGS));
113
114   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
115       g_param_spec_uint ("timeout", "timeout",
116           "the timeout of the session (0 = never)", 0, G_MAXUINT,
117           DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
118
119   g_object_class_install_property (gobject_class, PROP_TIMEOUT_ALWAYS_VISIBLE,
120       g_param_spec_boolean ("timeout-always-visible", "Timeout Always Visible ",
121           "timeout always visible in header",
122           DEFAULT_ALWAYS_VISIBLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
123
124   /**
125    * GstRTSPSession::extra-timeout:
126    *
127    * Extra time to add to the timeout, in seconds. This only affects the
128    * time until a session is considered timed out and is not signalled
129    * in the RTSP request responses. Only the value of the timeout
130    * property is signalled in the request responses.
131    *
132    * Default value is 5 seconds.
133    * If the application is using a buffer that is configured to hold
134    * amount of data equal to the sessiontimeout, extra-timeout can be
135    * set to zero to prevent loss of data
136    *
137    * Since: 1.18
138    */
139   g_object_class_install_property (gobject_class, PROP_EXTRA_TIME_TIMEOUT,
140       g_param_spec_uint ("extra-timeout",
141           "Add extra time to timeout ", "Add extra time to timeout", 0,
142           G_MAXUINT, DEFAULT_EXTRA_TIMEOUT,
143           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
144
145
146   GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
147       "GstRTSPSession");
148 }
149
150 static void
151 gst_rtsp_session_init (GstRTSPSession * session)
152 {
153   GstRTSPSessionPrivate *priv;
154
155   session->priv = priv = gst_rtsp_session_get_instance_private (session);
156
157   GST_INFO ("init session %p", session);
158
159   g_mutex_init (&priv->lock);
160   g_mutex_init (&priv->last_access_lock);
161   priv->timeout = DEFAULT_TIMEOUT;
162   priv->extra_time_timeout = DEFAULT_EXTRA_TIMEOUT;
163
164   gst_rtsp_session_touch (session);
165 }
166
167 static void
168 gst_rtsp_session_finalize (GObject * obj)
169 {
170   GstRTSPSession *session;
171   GstRTSPSessionPrivate *priv;
172
173   session = GST_RTSP_SESSION (obj);
174   priv = session->priv;
175
176   GST_INFO ("finalize session %p", session);
177
178   /* free all media */
179   g_list_free_full (priv->medias, g_object_unref);
180
181   /* free session id */
182   g_free (priv->sessionid);
183   g_mutex_clear (&priv->last_access_lock);
184   g_mutex_clear (&priv->lock);
185
186   G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
187 }
188
189 static void
190 gst_rtsp_session_get_property (GObject * object, guint propid,
191     GValue * value, GParamSpec * pspec)
192 {
193   GstRTSPSession *session = GST_RTSP_SESSION (object);
194   GstRTSPSessionPrivate *priv = session->priv;
195
196   switch (propid) {
197     case PROP_SESSIONID:
198       g_value_set_string (value, priv->sessionid);
199       break;
200     case PROP_TIMEOUT:
201       g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
202       break;
203     case PROP_TIMEOUT_ALWAYS_VISIBLE:
204       g_value_set_boolean (value, priv->timeout_always_visible);
205       break;
206     case PROP_EXTRA_TIME_TIMEOUT:
207       g_value_set_uint (value, priv->extra_time_timeout);
208       break;
209     default:
210       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
211   }
212 }
213
214 static void
215 gst_rtsp_session_set_property (GObject * object, guint propid,
216     const GValue * value, GParamSpec * pspec)
217 {
218   GstRTSPSession *session = GST_RTSP_SESSION (object);
219   GstRTSPSessionPrivate *priv = session->priv;
220
221   switch (propid) {
222     case PROP_SESSIONID:
223       g_free (priv->sessionid);
224       priv->sessionid = g_value_dup_string (value);
225       break;
226     case PROP_TIMEOUT:
227       gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
228       break;
229     case PROP_TIMEOUT_ALWAYS_VISIBLE:
230       g_mutex_lock (&priv->lock);
231       priv->timeout_always_visible = g_value_get_boolean (value);
232       g_mutex_unlock (&priv->lock);
233       break;
234     case PROP_EXTRA_TIME_TIMEOUT:
235       g_mutex_lock (&priv->lock);
236       priv->extra_time_timeout = g_value_get_uint (value);
237       g_mutex_unlock (&priv->lock);
238       break;
239     default:
240       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
241   }
242 }
243
244 /**
245  * gst_rtsp_session_manage_media:
246  * @sess: a #GstRTSPSession
247  * @path: the path for the media
248  * @media: (transfer full): a #GstRTSPMedia
249  *
250  * Manage the media object @obj in @sess. @path will be used to retrieve this
251  * media from the session with gst_rtsp_session_get_media().
252  *
253  * Ownership is taken from @media.
254  *
255  * Returns: (transfer none): a new @GstRTSPSessionMedia object.
256  */
257 GstRTSPSessionMedia *
258 gst_rtsp_session_manage_media (GstRTSPSession * sess, const gchar * path,
259     GstRTSPMedia * media)
260 {
261   GstRTSPSessionPrivate *priv;
262   GstRTSPSessionMedia *result;
263   GstRTSPMediaStatus status;
264
265   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
266   g_return_val_if_fail (path != NULL, NULL);
267   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
268   status = gst_rtsp_media_get_status (media);
269   g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
270       GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
271
272   priv = sess->priv;
273
274   result = gst_rtsp_session_media_new (path, media);
275
276   g_mutex_lock (&priv->lock);
277   priv->medias = g_list_prepend (priv->medias, result);
278   priv->medias_cookie++;
279   g_mutex_unlock (&priv->lock);
280
281   GST_INFO ("manage new media %p in session %p", media, result);
282
283   return result;
284 }
285
286 static void
287 gst_rtsp_session_unset_transport_keepalive (GstRTSPSessionMedia * sessmedia)
288 {
289   GstRTSPMedia *media;
290   guint i, n_streams;
291
292   media = gst_rtsp_session_media_get_media (sessmedia);
293   n_streams = gst_rtsp_media_n_streams (media);
294
295   for (i = 0; i < n_streams; i++) {
296     GstRTSPStreamTransport *transport =
297         gst_rtsp_session_media_get_transport (sessmedia, i);
298
299     if (!transport)
300       continue;
301
302     gst_rtsp_stream_transport_set_keepalive (transport, NULL, NULL, NULL);
303   }
304 }
305
306 /**
307  * gst_rtsp_session_release_media:
308  * @sess: a #GstRTSPSession
309  * @media: (transfer none): a #GstRTSPMedia
310  *
311  * Release the managed @media in @sess, freeing the memory allocated by it.
312  *
313  * Returns: %TRUE if there are more media session left in @sess.
314  */
315 gboolean
316 gst_rtsp_session_release_media (GstRTSPSession * sess,
317     GstRTSPSessionMedia * media)
318 {
319   GstRTSPSessionPrivate *priv;
320   GList *find;
321   gboolean more;
322
323   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
324   g_return_val_if_fail (media != NULL, FALSE);
325
326   priv = sess->priv;
327
328   g_mutex_lock (&priv->lock);
329   find = g_list_find (priv->medias, media);
330   if (find) {
331     priv->medias = g_list_delete_link (priv->medias, find);
332     priv->medias_cookie++;
333   }
334   more = (priv->medias != NULL);
335   g_mutex_unlock (&priv->lock);
336
337   if (find && !more)
338     gst_rtsp_session_unset_transport_keepalive (media);
339
340   if (find)
341     g_object_unref (media);
342
343   return more;
344 }
345
346 static GstRTSPSessionMedia *
347 _gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
348     gint * matched, gboolean dup)
349 {
350   GstRTSPSessionPrivate *priv;
351   GstRTSPSessionMedia *result;
352   GList *walk;
353   gint best;
354
355   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
356   g_return_val_if_fail (path != NULL, NULL);
357
358   priv = sess->priv;
359   result = NULL;
360   best = 0;
361
362   g_mutex_lock (&priv->lock);
363   for (walk = priv->medias; walk; walk = g_list_next (walk)) {
364     GstRTSPSessionMedia *test;
365
366     test = (GstRTSPSessionMedia *) walk->data;
367
368     /* find largest match */
369     if (gst_rtsp_session_media_matches (test, path, matched)) {
370       if (best < *matched) {
371         result = test;
372         best = *matched;
373       }
374     }
375   }
376
377   if (result && dup)
378     result = g_object_ref (result);
379   g_mutex_unlock (&priv->lock);
380
381   *matched = best;
382
383   return result;
384 }
385
386 /**
387  * gst_rtsp_session_get_media:
388  * @sess: a #GstRTSPSession
389  * @path: the path for the media
390  * @matched: (out): the amount of matched characters
391  *
392  * Gets the session media for @path. @matched will contain the number of matched
393  * characters of @path.
394  *
395  * Returns: (transfer none) (nullable): the configuration for @path in @sess.
396  */
397 GstRTSPSessionMedia *
398 gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
399     gint * matched)
400 {
401   return _gst_rtsp_session_get_media (sess, path, matched, FALSE);
402 }
403
404 /**
405  * gst_rtsp_session_dup_media:
406  * @sess: a #GstRTSPSession
407  * @path: the path for the media
408  * @matched: (out): the amount of matched characters
409  *
410  * Gets the session media for @path, increasing its reference count. @matched
411  * will contain the number of matched characters of @path.
412  *
413  * Returns: (transfer full) (nullable): the configuration for @path in @sess,
414  * should be unreferenced when no longer needed.
415  *
416  * Since: 1.20
417  */
418 GstRTSPSessionMedia *
419 gst_rtsp_session_dup_media (GstRTSPSession * sess, const gchar * path,
420     gint * matched)
421 {
422   return _gst_rtsp_session_get_media (sess, path, matched, TRUE);
423 }
424
425 /**
426  * gst_rtsp_session_filter:
427  * @sess: a #GstRTSPSession
428  * @func: (scope call) (allow-none): a callback
429  * @user_data: (closure): user data passed to @func
430  *
431  * Call @func for each media in @sess. The result value of @func determines
432  * what happens to the media. @func will be called with @sess
433  * locked so no further actions on @sess can be performed from @func.
434  *
435  * If @func returns #GST_RTSP_FILTER_REMOVE, the media will be removed from
436  * @sess.
437  *
438  * If @func returns #GST_RTSP_FILTER_KEEP, the media will remain in @sess.
439  *
440  * If @func returns #GST_RTSP_FILTER_REF, the media will remain in @sess but
441  * will also be added with an additional ref to the result #GList of this
442  * function..
443  *
444  * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for all media.
445  *
446  * Returns: (element-type GstRTSPSessionMedia) (transfer full): a GList with all
447  * media for which @func returned #GST_RTSP_FILTER_REF. After usage, each
448  * element in the #GList should be unreffed before the list is freed.
449  */
450 GList *
451 gst_rtsp_session_filter (GstRTSPSession * sess,
452     GstRTSPSessionFilterFunc func, gpointer user_data)
453 {
454   GstRTSPSessionPrivate *priv;
455   GList *result, *walk, *next;
456   GHashTable *visited;
457   guint cookie;
458
459   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
460
461   priv = sess->priv;
462
463   result = NULL;
464   if (func)
465     visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
466
467   g_mutex_lock (&priv->lock);
468 restart:
469   cookie = priv->medias_cookie;
470   for (walk = priv->medias; walk; walk = next) {
471     GstRTSPSessionMedia *media = walk->data;
472     GstRTSPFilterResult res;
473     gboolean changed;
474
475     next = g_list_next (walk);
476
477     if (func) {
478       /* only visit each media once */
479       if (g_hash_table_contains (visited, media))
480         continue;
481
482       g_hash_table_add (visited, g_object_ref (media));
483       g_mutex_unlock (&priv->lock);
484
485       res = func (sess, media, user_data);
486
487       g_mutex_lock (&priv->lock);
488     } else {
489       res = GST_RTSP_FILTER_REF;
490     }
491
492     changed = (cookie != priv->medias_cookie);
493
494     switch (res) {
495       case GST_RTSP_FILTER_REMOVE:
496         if (changed) {
497           GList *l;
498
499           walk = NULL;
500
501           for (l = priv->medias; l; l = l->next) {
502             if (l->data == media) {
503               walk = l;
504               break;
505             }
506           }
507         }
508
509         /* The media might have been removed from the list while the mutex was
510          * unlocked above. In that case there's nothing else to do here as the
511          * only reference to the media owned by this function is in the
512          * visited hash table and that is released in the end
513          */
514         if (walk) {
515           priv->medias = g_list_delete_link (priv->medias, walk);
516           g_object_unref (media);
517         }
518
519         cookie = ++priv->medias_cookie;
520         break;
521       case GST_RTSP_FILTER_REF:
522         result = g_list_prepend (result, g_object_ref (media));
523         break;
524       case GST_RTSP_FILTER_KEEP:
525       default:
526         break;
527     }
528     if (changed)
529       goto restart;
530   }
531   g_mutex_unlock (&priv->lock);
532
533   if (func)
534     g_hash_table_unref (visited);
535
536   return result;
537 }
538
539 /**
540  * gst_rtsp_session_new:
541  * @sessionid: a session id
542  *
543  * Create a new #GstRTSPSession instance with @sessionid.
544  *
545  * Returns: (transfer full): a new #GstRTSPSession
546  */
547 GstRTSPSession *
548 gst_rtsp_session_new (const gchar * sessionid)
549 {
550   GstRTSPSession *result;
551
552   g_return_val_if_fail (sessionid != NULL, NULL);
553
554   result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
555
556   return result;
557 }
558
559 /**
560  * gst_rtsp_session_get_sessionid:
561  * @session: a #GstRTSPSession
562  *
563  * Get the sessionid of @session.
564  *
565  * Returns: (transfer none) (nullable): the sessionid of @session.
566  * The value remains valid as long as @session is alive.
567  */
568 const gchar *
569 gst_rtsp_session_get_sessionid (GstRTSPSession * session)
570 {
571   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
572
573   return session->priv->sessionid;
574 }
575
576 /**
577  * gst_rtsp_session_get_header:
578  * @session: a #GstRTSPSession
579  *
580  * Get the string that can be placed in the Session header field.
581  *
582  * Returns: (transfer full) (nullable): the Session header of @session.
583  * g_free() after usage.
584  */
585 gchar *
586 gst_rtsp_session_get_header (GstRTSPSession * session)
587 {
588   GstRTSPSessionPrivate *priv;
589   gchar *result;
590
591   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
592
593   priv = session->priv;
594
595
596   g_mutex_lock (&priv->lock);
597   if (priv->timeout_always_visible || priv->timeout != 60)
598     result = g_strdup_printf ("%s;timeout=%d", priv->sessionid, priv->timeout);
599   else
600     result = g_strdup (priv->sessionid);
601   g_mutex_unlock (&priv->lock);
602
603   return result;
604 }
605
606 /**
607  * gst_rtsp_session_set_timeout:
608  * @session: a #GstRTSPSession
609  * @timeout: the new timeout
610  *
611  * Configure @session for a timeout of @timeout seconds. The session will be
612  * cleaned up when there is no activity for @timeout seconds.
613  */
614 void
615 gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
616 {
617   GstRTSPSessionPrivate *priv;
618
619   g_return_if_fail (GST_IS_RTSP_SESSION (session));
620
621   priv = session->priv;
622
623   g_mutex_lock (&priv->lock);
624   priv->timeout = timeout;
625   g_mutex_unlock (&priv->lock);
626 }
627
628 /**
629  * gst_rtsp_session_get_timeout:
630  * @session: a #GstRTSPSession
631  *
632  * Get the timeout value of @session.
633  *
634  * Returns: the timeout of @session in seconds.
635  */
636 guint
637 gst_rtsp_session_get_timeout (GstRTSPSession * session)
638 {
639   GstRTSPSessionPrivate *priv;
640   guint res;
641
642   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
643
644   priv = session->priv;
645
646   g_mutex_lock (&priv->lock);
647   res = priv->timeout;
648   g_mutex_unlock (&priv->lock);
649
650   return res;
651 }
652
653 /**
654  * gst_rtsp_session_touch:
655  * @session: a #GstRTSPSession
656  *
657  * Update the last_access time of the session to the current time.
658  */
659 void
660 gst_rtsp_session_touch (GstRTSPSession * session)
661 {
662   GstRTSPSessionPrivate *priv;
663
664   g_return_if_fail (GST_IS_RTSP_SESSION (session));
665
666   priv = session->priv;
667
668   g_mutex_lock (&priv->last_access_lock);
669   priv->last_access_monotonic_time = g_get_monotonic_time ();
670   priv->last_access_real_time = g_get_real_time ();
671   g_mutex_unlock (&priv->last_access_lock);
672 }
673
674 /**
675  * gst_rtsp_session_prevent_expire:
676  * @session: a #GstRTSPSession
677  *
678  * Prevent @session from expiring.
679  */
680 void
681 gst_rtsp_session_prevent_expire (GstRTSPSession * session)
682 {
683   g_return_if_fail (GST_IS_RTSP_SESSION (session));
684
685   g_atomic_int_add (&session->priv->expire_count, 1);
686 }
687
688 /**
689  * gst_rtsp_session_allow_expire:
690  * @session: a #GstRTSPSession
691  *
692  * Allow @session to expire. This method must be called an equal
693  * amount of time as gst_rtsp_session_prevent_expire().
694  */
695 void
696 gst_rtsp_session_allow_expire (GstRTSPSession * session)
697 {
698   g_atomic_int_add (&session->priv->expire_count, -1);
699 }
700
701 /**
702  * gst_rtsp_session_next_timeout_usec:
703  * @session: a #GstRTSPSession
704  * @now: the current monotonic time
705  *
706  * Get the amount of milliseconds till the session will expire.
707  *
708  * Returns: the amount of milliseconds since the session will time out.
709  */
710 gint
711 gst_rtsp_session_next_timeout_usec (GstRTSPSession * session, gint64 now)
712 {
713   GstRTSPSessionPrivate *priv;
714   gint res;
715   GstClockTime last_access, now_ns;
716
717   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
718
719   priv = session->priv;
720
721   g_mutex_lock (&priv->lock);
722   /* If timeout is set to 0, we never timeout */
723   if (priv->timeout == 0) {
724     g_mutex_unlock (&priv->lock);
725     return NO_TIMEOUT;
726   }
727   g_mutex_unlock (&priv->lock);
728
729   g_mutex_lock (&priv->last_access_lock);
730   if (g_atomic_int_get (&priv->expire_count) != 0) {
731     /* touch session when the expire count is not 0 */
732     priv->last_access_monotonic_time = g_get_monotonic_time ();
733     priv->last_access_real_time = g_get_real_time ();
734   }
735
736   last_access = GST_USECOND * (priv->last_access_monotonic_time);
737
738   /* add timeout allow for priv->extra_time_timeout
739    * seconds of extra time */
740   last_access += priv->timeout * GST_SECOND +
741       (priv->extra_time_timeout * GST_SECOND);
742
743   g_mutex_unlock (&priv->last_access_lock);
744
745   now_ns = GST_USECOND * now;
746
747   if (last_access > now_ns) {
748     res = GST_TIME_AS_MSECONDS (last_access - now_ns);
749   } else {
750     res = 0;
751   }
752
753   return res;
754 }
755
756 /****** Deprecated API *******/
757
758 /**
759  * gst_rtsp_session_next_timeout:
760  * @session: a #GstRTSPSession
761  * @now: (transfer none): the current system time
762  *
763  * Get the amount of milliseconds till the session will expire.
764  *
765  * Returns: the amount of milliseconds since the session will time out.
766  *
767  * Deprecated: Use gst_rtsp_session_next_timeout_usec() instead.
768  */
769 #ifndef GST_REMOVE_DEPRECATED
770 G_GNUC_BEGIN_IGNORE_DEPRECATIONS gint
771 gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
772 {
773   GstRTSPSessionPrivate *priv;
774   gint res;
775   GstClockTime last_access, now_ns;
776
777   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
778   g_return_val_if_fail (now != NULL, -1);
779
780   priv = session->priv;
781
782   g_mutex_lock (&priv->last_access_lock);
783   if (g_atomic_int_get (&priv->expire_count) != 0) {
784     /* touch session when the expire count is not 0 */
785     priv->last_access_monotonic_time = g_get_monotonic_time ();
786     priv->last_access_real_time = g_get_real_time ();
787   }
788
789   last_access = GST_USECOND * (priv->last_access_real_time);
790
791   /* add timeout allow for priv->extra_time_timeout
792    * seconds of extra time */
793   last_access += priv->timeout * GST_SECOND +
794       (priv->extra_time_timeout * GST_SECOND);
795
796   g_mutex_unlock (&priv->last_access_lock);
797
798   now_ns = GST_TIMEVAL_TO_TIME (*now);
799
800   if (last_access > now_ns) {
801     res = GST_TIME_AS_MSECONDS (last_access - now_ns);
802   } else {
803     res = 0;
804   }
805
806   return res;
807 }
808
809 G_GNUC_END_IGNORE_DEPRECATIONS
810 #endif
811 /**
812  * gst_rtsp_session_is_expired_usec:
813  * @session: a #GstRTSPSession
814  * @now: the current monotonic time
815  *
816  * Check if @session timeout out.
817  *
818  * Returns: %TRUE if @session timed out
819  */
820     gboolean
821 gst_rtsp_session_is_expired_usec (GstRTSPSession * session, gint64 now)
822 {
823   gboolean res;
824
825   res = (gst_rtsp_session_next_timeout_usec (session, now) == 0);
826
827   return res;
828 }
829
830
831 /****** Deprecated API *******/
832
833 /**
834  * gst_rtsp_session_is_expired:
835  * @session: a #GstRTSPSession
836  * @now: (transfer none): the current system time
837  *
838  * Check if @session timeout out.
839  *
840  * Returns: %TRUE if @session timed out
841  *
842  * Deprecated: Use gst_rtsp_session_is_expired_usec() instead.
843  */
844 #ifndef GST_REMOVE_DEPRECATED
845 G_GNUC_BEGIN_IGNORE_DEPRECATIONS gboolean
846 gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
847 {
848   gboolean res;
849
850   res = gst_rtsp_session_next_timeout_usec (session,
851       (now->tv_sec * G_USEC_PER_SEC) + (now->tv_usec));
852
853   return res;
854 }
855
856 G_GNUC_END_IGNORE_DEPRECATIONS
857 #endif