rtsp-stream: obtain stream position from pad
[platform/upstream/gstreamer.git] / 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
46 #include <string.h>
47
48 #include "rtsp-session.h"
49
50 #define GST_RTSP_SESSION_GET_PRIVATE(obj)  \
51        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_SESSION, GstRTSPSessionPrivate))
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 };
68
69 #undef DEBUG
70
71 #define DEFAULT_TIMEOUT        60
72 #define NO_TIMEOUT              -1
73 #define DEFAULT_ALWAYS_VISIBLE  FALSE
74
75 enum
76 {
77   PROP_0,
78   PROP_SESSIONID,
79   PROP_TIMEOUT,
80   PROP_TIMEOUT_ALWAYS_VISIBLE,
81   PROP_LAST
82 };
83
84 GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
85 #define GST_CAT_DEFAULT rtsp_session_debug
86
87 static void gst_rtsp_session_get_property (GObject * object, guint propid,
88     GValue * value, GParamSpec * pspec);
89 static void gst_rtsp_session_set_property (GObject * object, guint propid,
90     const GValue * value, GParamSpec * pspec);
91 static void gst_rtsp_session_finalize (GObject * obj);
92
93 G_DEFINE_TYPE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
94
95 static void
96 gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
97 {
98   GObjectClass *gobject_class;
99
100   g_type_class_add_private (klass, sizeof (GstRTSPSessionPrivate));
101
102   gobject_class = G_OBJECT_CLASS (klass);
103
104   gobject_class->get_property = gst_rtsp_session_get_property;
105   gobject_class->set_property = gst_rtsp_session_set_property;
106   gobject_class->finalize = gst_rtsp_session_finalize;
107
108   g_object_class_install_property (gobject_class, PROP_SESSIONID,
109       g_param_spec_string ("sessionid", "Sessionid", "the session id",
110           NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
111           G_PARAM_STATIC_STRINGS));
112
113   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
114       g_param_spec_uint ("timeout", "timeout",
115           "the timeout of the session (0 = never)", 0, G_MAXUINT,
116           DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
117
118   g_object_class_install_property (gobject_class, PROP_TIMEOUT_ALWAYS_VISIBLE,
119       g_param_spec_boolean ("timeout-always-visible", "Timeout Always Visible ",
120           "timeout always visible in header",
121           DEFAULT_ALWAYS_VISIBLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
122
123   GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
124       "GstRTSPSession");
125 }
126
127 static void
128 gst_rtsp_session_init (GstRTSPSession * session)
129 {
130   GstRTSPSessionPrivate *priv = GST_RTSP_SESSION_GET_PRIVATE (session);
131
132   session->priv = priv;
133
134   GST_INFO ("init session %p", session);
135
136   g_mutex_init (&priv->lock);
137   g_mutex_init (&priv->last_access_lock);
138   priv->timeout = DEFAULT_TIMEOUT;
139
140   gst_rtsp_session_touch (session);
141 }
142
143 static void
144 gst_rtsp_session_finalize (GObject * obj)
145 {
146   GstRTSPSession *session;
147   GstRTSPSessionPrivate *priv;
148
149   session = GST_RTSP_SESSION (obj);
150   priv = session->priv;
151
152   GST_INFO ("finalize session %p", session);
153
154   /* free all media */
155   g_list_free_full (priv->medias, g_object_unref);
156
157   /* free session id */
158   g_free (priv->sessionid);
159   g_mutex_clear (&priv->last_access_lock);
160   g_mutex_clear (&priv->lock);
161
162   G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
163 }
164
165 static void
166 gst_rtsp_session_get_property (GObject * object, guint propid,
167     GValue * value, GParamSpec * pspec)
168 {
169   GstRTSPSession *session = GST_RTSP_SESSION (object);
170   GstRTSPSessionPrivate *priv = session->priv;
171
172   switch (propid) {
173     case PROP_SESSIONID:
174       g_value_set_string (value, priv->sessionid);
175       break;
176     case PROP_TIMEOUT:
177       g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
178       break;
179     case PROP_TIMEOUT_ALWAYS_VISIBLE:
180       g_value_set_boolean (value, priv->timeout_always_visible);
181       break;
182     default:
183       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
184   }
185 }
186
187 static void
188 gst_rtsp_session_set_property (GObject * object, guint propid,
189     const GValue * value, GParamSpec * pspec)
190 {
191   GstRTSPSession *session = GST_RTSP_SESSION (object);
192   GstRTSPSessionPrivate *priv = session->priv;
193
194   switch (propid) {
195     case PROP_SESSIONID:
196       g_free (priv->sessionid);
197       priv->sessionid = g_value_dup_string (value);
198       break;
199     case PROP_TIMEOUT:
200       gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
201       break;
202     case PROP_TIMEOUT_ALWAYS_VISIBLE:
203       g_mutex_lock (&priv->lock);
204       priv->timeout_always_visible = g_value_get_boolean (value);
205       g_mutex_unlock (&priv->lock);
206       break;
207     default:
208       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
209   }
210 }
211
212 /**
213  * gst_rtsp_session_manage_media:
214  * @sess: a #GstRTSPSession
215  * @path: the path for the media
216  * @media: (transfer full): a #GstRTSPMedia
217  *
218  * Manage the media object @obj in @sess. @path will be used to retrieve this
219  * media from the session with gst_rtsp_session_get_media().
220  *
221  * Ownership is taken from @media.
222  *
223  * Returns: (transfer none): a new @GstRTSPSessionMedia object.
224  */
225 GstRTSPSessionMedia *
226 gst_rtsp_session_manage_media (GstRTSPSession * sess, const gchar * path,
227     GstRTSPMedia * media)
228 {
229   GstRTSPSessionPrivate *priv;
230   GstRTSPSessionMedia *result;
231   GstRTSPMediaStatus status;
232
233   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
234   g_return_val_if_fail (path != NULL, NULL);
235   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
236   status = gst_rtsp_media_get_status (media);
237   g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
238       GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
239
240   priv = sess->priv;
241
242   result = gst_rtsp_session_media_new (path, media);
243
244   g_mutex_lock (&priv->lock);
245   priv->medias = g_list_prepend (priv->medias, result);
246   priv->medias_cookie++;
247   g_mutex_unlock (&priv->lock);
248
249   GST_INFO ("manage new media %p in session %p", media, result);
250
251   return result;
252 }
253
254 static void
255 gst_rtsp_session_unset_transport_keepalive (GstRTSPSessionMedia * sessmedia)
256 {
257   GstRTSPMedia *media;
258   guint i, n_streams;
259
260   media = gst_rtsp_session_media_get_media (sessmedia);
261   n_streams = gst_rtsp_media_n_streams (media);
262
263   for (i = 0; i < n_streams; i++) {
264     GstRTSPStreamTransport *transport =
265         gst_rtsp_session_media_get_transport (sessmedia, i);
266
267     if (!transport)
268       continue;
269
270     gst_rtsp_stream_transport_set_keepalive (transport, NULL, NULL, NULL);
271   }
272 }
273
274 /**
275  * gst_rtsp_session_release_media:
276  * @sess: a #GstRTSPSession
277  * @media: (transfer none): a #GstRTSPMedia
278  *
279  * Release the managed @media in @sess, freeing the memory allocated by it.
280  *
281  * Returns: %TRUE if there are more media session left in @sess.
282  */
283 gboolean
284 gst_rtsp_session_release_media (GstRTSPSession * sess,
285     GstRTSPSessionMedia * media)
286 {
287   GstRTSPSessionPrivate *priv;
288   GList *find;
289   gboolean more;
290
291   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
292   g_return_val_if_fail (media != NULL, FALSE);
293
294   priv = sess->priv;
295
296   g_mutex_lock (&priv->lock);
297   find = g_list_find (priv->medias, media);
298   if (find) {
299     priv->medias = g_list_delete_link (priv->medias, find);
300     priv->medias_cookie++;
301   }
302   more = (priv->medias != NULL);
303   g_mutex_unlock (&priv->lock);
304
305   if (find && !more)
306     gst_rtsp_session_unset_transport_keepalive (media);
307
308   if (find)
309     g_object_unref (media);
310
311   return more;
312 }
313
314 /**
315  * gst_rtsp_session_get_media:
316  * @sess: a #GstRTSPSession
317  * @path: the path for the media
318  * @matched: (out): the amount of matched characters
319  *
320  * Get the session media for @path. @matched will contain the number of matched
321  * characters of @path.
322  *
323  * Returns: (transfer none): the configuration for @path in @sess.
324  */
325 GstRTSPSessionMedia *
326 gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
327     gint * matched)
328 {
329   GstRTSPSessionPrivate *priv;
330   GstRTSPSessionMedia *result;
331   GList *walk;
332   gint best;
333
334   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
335   g_return_val_if_fail (path != NULL, NULL);
336
337   priv = sess->priv;
338   result = NULL;
339   best = 0;
340
341   g_mutex_lock (&priv->lock);
342   for (walk = priv->medias; walk; walk = g_list_next (walk)) {
343     GstRTSPSessionMedia *test;
344
345     test = (GstRTSPSessionMedia *) walk->data;
346
347     /* find largest match */
348     if (gst_rtsp_session_media_matches (test, path, matched)) {
349       if (best < *matched) {
350         result = test;
351         best = *matched;
352       }
353     }
354   }
355   g_mutex_unlock (&priv->lock);
356
357   *matched = best;
358
359   return result;
360 }
361
362 /**
363  * gst_rtsp_session_filter:
364  * @sess: a #GstRTSPSession
365  * @func: (scope call) (allow-none): a callback
366  * @user_data: (closure): user data passed to @func
367  *
368  * Call @func for each media in @sess. The result value of @func determines
369  * what happens to the media. @func will be called with @sess
370  * locked so no further actions on @sess can be performed from @func.
371  *
372  * If @func returns #GST_RTSP_FILTER_REMOVE, the media will be removed from
373  * @sess.
374  *
375  * If @func returns #GST_RTSP_FILTER_KEEP, the media will remain in @sess.
376  *
377  * If @func returns #GST_RTSP_FILTER_REF, the media will remain in @sess but
378  * will also be added with an additional ref to the result #GList of this
379  * function..
380  *
381  * When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for all media.
382  *
383  * Returns: (element-type GstRTSPSessionMedia) (transfer full): a GList with all
384  * media for which @func returned #GST_RTSP_FILTER_REF. After usage, each
385  * element in the #GList should be unreffed before the list is freed.
386  */
387 GList *
388 gst_rtsp_session_filter (GstRTSPSession * sess,
389     GstRTSPSessionFilterFunc func, gpointer user_data)
390 {
391   GstRTSPSessionPrivate *priv;
392   GList *result, *walk, *next;
393   GHashTable *visited;
394   guint cookie;
395
396   g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
397
398   priv = sess->priv;
399
400   result = NULL;
401   if (func)
402     visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
403
404   g_mutex_lock (&priv->lock);
405 restart:
406   cookie = priv->medias_cookie;
407   for (walk = priv->medias; walk; walk = next) {
408     GstRTSPSessionMedia *media = walk->data;
409     GstRTSPFilterResult res;
410     gboolean changed;
411
412     next = g_list_next (walk);
413
414     if (func) {
415       /* only visit each media once */
416       if (g_hash_table_contains (visited, media))
417         continue;
418
419       g_hash_table_add (visited, g_object_ref (media));
420       g_mutex_unlock (&priv->lock);
421
422       res = func (sess, media, user_data);
423
424       g_mutex_lock (&priv->lock);
425     } else
426       res = GST_RTSP_FILTER_REF;
427
428     changed = (cookie != priv->medias_cookie);
429
430     switch (res) {
431       case GST_RTSP_FILTER_REMOVE:
432         if (changed)
433           priv->medias = g_list_remove (priv->medias, media);
434         else
435           priv->medias = g_list_delete_link (priv->medias, walk);
436         cookie = ++priv->medias_cookie;
437         g_object_unref (media);
438         break;
439       case GST_RTSP_FILTER_REF:
440         result = g_list_prepend (result, g_object_ref (media));
441         break;
442       case GST_RTSP_FILTER_KEEP:
443       default:
444         break;
445     }
446     if (changed)
447       goto restart;
448   }
449   g_mutex_unlock (&priv->lock);
450
451   if (func)
452     g_hash_table_unref (visited);
453
454   return result;
455 }
456
457 /**
458  * gst_rtsp_session_new:
459  * @sessionid: a session id
460  *
461  * Create a new #GstRTSPSession instance with @sessionid.
462  *
463  * Returns: (transfer full): a new #GstRTSPSession
464  */
465 GstRTSPSession *
466 gst_rtsp_session_new (const gchar * sessionid)
467 {
468   GstRTSPSession *result;
469
470   g_return_val_if_fail (sessionid != NULL, NULL);
471
472   result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
473
474   return result;
475 }
476
477 /**
478  * gst_rtsp_session_get_sessionid:
479  * @session: a #GstRTSPSession
480  *
481  * Get the sessionid of @session.
482  *
483  * Returns: (transfer none): the sessionid of @session. The value remains valid
484  * as long as @session is alive.
485  */
486 const gchar *
487 gst_rtsp_session_get_sessionid (GstRTSPSession * session)
488 {
489   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
490
491   return session->priv->sessionid;
492 }
493
494 /**
495  * gst_rtsp_session_get_header:
496  * @session: a #GstRTSPSession
497  *
498  * Get the string that can be placed in the Session header field.
499  *
500  * Returns: (transfer full): the Session header of @session. g_free() after usage.
501  */
502 gchar *
503 gst_rtsp_session_get_header (GstRTSPSession * session)
504 {
505   GstRTSPSessionPrivate *priv;
506   gchar *result;
507
508   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
509
510   priv = session->priv;
511
512
513   g_mutex_lock (&priv->lock);
514   if (priv->timeout_always_visible || priv->timeout != 60)
515     result = g_strdup_printf ("%s;timeout=%d", priv->sessionid, priv->timeout);
516   else
517     result = g_strdup (priv->sessionid);
518   g_mutex_unlock (&priv->lock);
519
520   return result;
521 }
522
523 /**
524  * gst_rtsp_session_set_timeout:
525  * @session: a #GstRTSPSession
526  * @timeout: the new timeout
527  *
528  * Configure @session for a timeout of @timeout seconds. The session will be
529  * cleaned up when there is no activity for @timeout seconds.
530  */
531 void
532 gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
533 {
534   GstRTSPSessionPrivate *priv;
535
536   g_return_if_fail (GST_IS_RTSP_SESSION (session));
537
538   priv = session->priv;
539
540   g_mutex_lock (&priv->lock);
541   priv->timeout = timeout;
542   g_mutex_unlock (&priv->lock);
543 }
544
545 /**
546  * gst_rtsp_session_get_timeout:
547  * @session: a #GstRTSPSession
548  *
549  * Get the timeout value of @session.
550  *
551  * Returns: the timeout of @session in seconds.
552  */
553 guint
554 gst_rtsp_session_get_timeout (GstRTSPSession * session)
555 {
556   GstRTSPSessionPrivate *priv;
557   guint res;
558
559   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
560
561   priv = session->priv;
562
563   g_mutex_lock (&priv->lock);
564   res = priv->timeout;
565   g_mutex_unlock (&priv->lock);
566
567   return res;
568 }
569
570 /**
571  * gst_rtsp_session_touch:
572  * @session: a #GstRTSPSession
573  *
574  * Update the last_access time of the session to the current time.
575  */
576 void
577 gst_rtsp_session_touch (GstRTSPSession * session)
578 {
579   GstRTSPSessionPrivate *priv;
580
581   g_return_if_fail (GST_IS_RTSP_SESSION (session));
582
583   priv = session->priv;
584
585   g_mutex_lock (&priv->last_access_lock);
586   priv->last_access_monotonic_time = g_get_monotonic_time ();
587   priv->last_access_real_time = g_get_real_time ();
588   g_mutex_unlock (&priv->last_access_lock);
589 }
590
591 /**
592  * gst_rtsp_session_prevent_expire:
593  * @session: a #GstRTSPSession
594  *
595  * Prevent @session from expiring.
596  */
597 void
598 gst_rtsp_session_prevent_expire (GstRTSPSession * session)
599 {
600   g_return_if_fail (GST_IS_RTSP_SESSION (session));
601
602   g_atomic_int_add (&session->priv->expire_count, 1);
603 }
604
605 /**
606  * gst_rtsp_session_allow_expire:
607  * @session: a #GstRTSPSession
608  *
609  * Allow @session to expire. This method must be called an equal
610  * amount of time as gst_rtsp_session_prevent_expire().
611  */
612 void
613 gst_rtsp_session_allow_expire (GstRTSPSession * session)
614 {
615   g_atomic_int_add (&session->priv->expire_count, -1);
616 }
617
618 /**
619  * gst_rtsp_session_next_timeout_usec:
620  * @session: a #GstRTSPSession
621  * @now: the current monotonic time
622  *
623  * Get the amount of milliseconds till the session will expire.
624  *
625  * Returns: the amount of milliseconds since the session will time out.
626  */
627 gint
628 gst_rtsp_session_next_timeout_usec (GstRTSPSession * session, gint64 now)
629 {
630   GstRTSPSessionPrivate *priv;
631   gint res;
632   GstClockTime last_access, now_ns;
633
634   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
635
636   priv = session->priv;
637
638   g_mutex_lock (&priv->lock);
639   /* If timeout is set to 0, we never timeout */
640   if (priv->timeout == 0) {
641     g_mutex_unlock (&priv->lock);
642     return NO_TIMEOUT;
643   }
644   g_mutex_unlock (&priv->lock);
645
646   g_mutex_lock (&priv->last_access_lock);
647   if (g_atomic_int_get (&priv->expire_count) != 0) {
648     /* touch session when the expire count is not 0 */
649     priv->last_access_monotonic_time = g_get_monotonic_time ();
650     priv->last_access_real_time = g_get_real_time ();
651   }
652
653   last_access = GST_USECOND * (priv->last_access_monotonic_time);
654
655   /* add timeout allow for 5 seconds of extra time */
656   last_access += priv->timeout * GST_SECOND + (5 * GST_SECOND);
657   g_mutex_unlock (&priv->last_access_lock);
658
659   now_ns = GST_USECOND * now;
660
661   if (last_access > now_ns) {
662     res = GST_TIME_AS_MSECONDS (last_access - now_ns);
663   } else {
664     res = 0;
665   }
666
667   return res;
668 }
669
670 /****** Deprecated API *******/
671
672 /**
673  * gst_rtsp_session_next_timeout:
674  * @session: a #GstRTSPSession
675  * @now: (transfer none): the current system time
676  *
677  * Get the amount of milliseconds till the session will expire.
678  *
679  * Returns: the amount of milliseconds since the session will time out.
680  *
681  * Deprecated: Use gst_rtsp_session_next_timeout_usec() instead.
682  */
683 #ifndef GST_REMOVE_DEPRECATED
684 #ifdef GST_DISABLE_DEPRECATED
685 gint gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now);
686 #endif
687
688 gint
689 gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
690 {
691   GstRTSPSessionPrivate *priv;
692   gint res;
693   GstClockTime last_access, now_ns;
694
695   g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
696   g_return_val_if_fail (now != NULL, -1);
697
698   priv = session->priv;
699
700   g_mutex_lock (&priv->last_access_lock);
701   if (g_atomic_int_get (&priv->expire_count) != 0) {
702     /* touch session when the expire count is not 0 */
703     priv->last_access_monotonic_time = g_get_monotonic_time ();
704     priv->last_access_real_time = g_get_real_time ();
705   }
706
707   last_access = GST_USECOND * (priv->last_access_real_time);
708
709   /* add timeout allow for 5 seconds of extra time */
710   last_access += priv->timeout * GST_SECOND + (5 * GST_SECOND);
711   g_mutex_unlock (&priv->last_access_lock);
712
713   now_ns = GST_TIMEVAL_TO_TIME (*now);
714
715   if (last_access > now_ns) {
716     res = GST_TIME_AS_MSECONDS (last_access - now_ns);
717   } else {
718     res = 0;
719   }
720
721   return res;
722 }
723 #endif
724
725 /**
726  * gst_rtsp_session_is_expired_usec:
727  * @session: a #GstRTSPSession
728  * @now: the current monotonic time
729  *
730  * Check if @session timeout out.
731  *
732  * Returns: %TRUE if @session timed out
733  */
734 gboolean
735 gst_rtsp_session_is_expired_usec (GstRTSPSession * session, gint64 now)
736 {
737   gboolean res;
738
739   res = (gst_rtsp_session_next_timeout_usec (session, now) == 0);
740
741   return res;
742 }
743
744
745 /****** Deprecated API *******/
746
747 /**
748  * gst_rtsp_session_is_expired:
749  * @session: a #GstRTSPSession
750  * @now: (transfer none): the current system time
751  *
752  * Check if @session timeout out.
753  *
754  * Returns: %TRUE if @session timed out
755  *
756  * Deprecated: Use gst_rtsp_session_is_expired_usec() instead.
757  */
758 #ifndef GST_REMOVE_DEPRECATED
759 #ifdef GST_DISABLE_DEPRECATED
760 gboolean gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now);
761 #endif
762 gboolean
763 gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
764 {
765   gboolean res;
766
767   res = (gst_rtsp_session_next_timeout (session, now) == 0);
768
769   return res;
770 }
771 #endif