* Copyright © 2008 Rémi Denis-Courmont
*
* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2.0
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
* 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 General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
#include <vlc_block.h>
#include <vlc_network.h>
+#include <limits.h>
#include <unistd.h>
#ifdef HAVE_POLL
# include <poll.h>
#endif
#include "rtp.h"
-#include <srtp.h>
-
-static bool fd_dead (int fd)
-{
- struct pollfd ufd = { .fd = fd, };
- return (poll (&ufd, 1, 0) > 0) && (ufd.revents & POLLHUP);
-}
+#ifdef HAVE_SRTP
+# include <srtp.h>
+#endif
/**
- * Gets a datagram from the network.
- * @param fd datagram file descriptor
- * @return a block or NULL on fatal error (socket dead)
+ * Processes a packet received from the RTP socket.
*/
-static block_t *rtp_dgram_recv (vlc_object_t *obj, int fd)
+static void rtp_process (demux_t *demux, block_t *block)
{
- block_t *block = block_Alloc (0xffff);
- ssize_t len;
+ demux_sys_t *sys = demux->p_sys;
- do
- {
- block_cleanup_push (block);
- len = net_Read (obj, fd, NULL,
- block->p_buffer, block->i_buffer, false);
- vlc_cleanup_pop ();
+ if (block->i_buffer < 2)
+ goto drop;
+ const uint8_t ptype = rtp_ptype (block);
+ if (ptype >= 72 && ptype <= 76)
+ goto drop; /* Muxed RTCP, ignore for now FIXME */
- if (((len <= 0) && fd_dead (fd)) || !vlc_object_alive (obj))
- { /* POLLHUP -> permanent (DCCP) socket error */
- block_Release (block);
- return NULL;
+#ifdef HAVE_SRTP
+ if (sys->srtp != NULL)
+ {
+ size_t len = block->i_buffer;
+ if (srtp_recv (sys->srtp, block->p_buffer, &len))
+ {
+ msg_Dbg (demux, "SRTP authentication/decryption failed");
+ goto drop;
}
+ block->i_buffer = len;
}
- while (len == -1);
-
- return block_Realloc (block, 0, len);
-}
-
-
-/**
- * Gets a framed RTP packet.
- * @param fd stream file descriptor
- * @return a block or NULL in case of fatal error
- */
-static block_t *rtp_stream_recv (vlc_object_t *obj, int fd)
-{
- ssize_t len = 0;
- uint8_t hdr[2]; /* frame header */
+#endif
- /* Receives the RTP frame header */
- do
- {
- ssize_t val = net_Read (obj, fd, NULL, hdr + len, 2 - len, false);
- if (val <= 0)
- return NULL;
- len += val;
+ /* TODO: use SDP and get rid of this hack */
+ if (unlikely(sys->autodetect))
+ { /* Autodetect payload type, _before_ rtp_queue() */
+ rtp_autodetect (demux, sys->session, block);
+ sys->autodetect = false;
}
- while (len < 2);
-
- block_t *block = block_Alloc (GetWBE (hdr));
- /* Receives the RTP packet */
- for (ssize_t i = 0; i < len;)
- {
- ssize_t val;
+ rtp_queue (demux, sys->session, block);
+ return;
+drop:
+ block_Release (block);
+}
- block_cleanup_push (block);
- val = net_Read (obj, fd, NULL,
- block->p_buffer + i, block->i_buffer - i, false);
- vlc_cleanup_pop ();
+static int rtp_timeout (mtime_t deadline)
+{
+ if (deadline == VLC_TS_INVALID)
+ return -1; /* infinite */
- if (val <= 0)
- {
- block_Release (block);
- return NULL;
- }
- i += val;
- }
+ mtime_t t = mdate ();
+ if (t >= deadline)
+ return 0;
- return block;
+ t = (deadline - t) / (CLOCK_FREQ / INT64_C(1000));
+ if (unlikely(t > INT_MAX))
+ return INT_MAX;
+ return t;
}
-
-static block_t *rtp_recv (demux_t *demux)
+/**
+ * RTP/RTCP session thread for datagram sockets
+ */
+void *rtp_dgram_thread (void *opaque)
{
- demux_sys_t *p_sys = demux->p_sys;
+ demux_t *demux = opaque;
+ demux_sys_t *sys = demux->p_sys;
+ mtime_t deadline = VLC_TS_INVALID;
+ int rtp_fd = sys->fd;
- for (block_t *block;; block_Release (block))
- {
- block = p_sys->framed_rtp
- ? rtp_stream_recv (VLC_OBJECT (demux), p_sys->fd)
- : rtp_dgram_recv (VLC_OBJECT (demux), p_sys->fd);
- if (block == NULL)
- {
- msg_Err (demux, "RTP flow stopped");
- break; /* fatal error */
- }
+ struct pollfd ufd[1];
+ ufd[0].fd = rtp_fd;
+ ufd[0].events = POLLIN;
- if (block->i_buffer < 2)
+ for (;;)
+ {
+ int n = poll (ufd, 1, rtp_timeout (deadline));
+ if (n == -1)
continue;
- /* FIXME */
- const uint8_t ptype = rtp_ptype (block);
- if (ptype >= 72 && ptype <= 76)
- continue; /* Muxed RTCP, ignore for now */
+ int canc = vlc_savecancel ();
+ if (n == 0)
+ goto dequeue;
- if (p_sys->srtp)
+ if (ufd[0].revents)
{
- size_t len = block->i_buffer;
- int canc, err;
+ n--;
+ if (unlikely(ufd[0].revents & POLLHUP))
+ break; /* RTP socket dead (DCCP only) */
+
+ block_t *block = block_Alloc (0xffff); /* TODO: p_sys->mru */
+ if (unlikely(block == NULL))
+ break; /* we are totallly screwed */
- canc = vlc_savecancel ();
- err = srtp_recv (p_sys->srtp, block->p_buffer, &len);
- vlc_restorecancel (canc);
- if (err)
+ ssize_t len = recv (rtp_fd, block->p_buffer, block->i_buffer, 0);
+ if (len != -1)
{
- msg_Dbg (demux, "SRTP authentication/decryption failed");
- continue;
+ block->i_buffer = len;
+ rtp_process (demux, block);
+ }
+ else
+ {
+ msg_Warn (demux, "RTP network error: %m");
+ block_Release (block);
}
- block->i_buffer = len;
}
- return block; /* success! */
+
+ dequeue:
+ if (!rtp_dequeue (demux, sys->session, &deadline))
+ deadline = VLC_TS_INVALID;
+ vlc_restorecancel (canc);
}
return NULL;
}
-
-static void timer_cleanup (void *timer)
-{
- vlc_timer_destroy (timer);
-}
-
-static void rtp_process (void *data);
-
-void *rtp_thread (void *data)
+/**
+ * RTP/RTCP session thread for stream sockets (framed RTP)
+ */
+void *rtp_stream_thread (void *opaque)
{
- demux_t *demux = data;
- demux_sys_t *p_sys = demux->p_sys;
- bool autodetect = true, reorder = false;
-
- if (vlc_timer_create (&p_sys->timer, rtp_process, data))
- return NULL;
- vlc_cleanup_push (timer_cleanup, &p_sys->timer);
+#ifndef WIN32
+ demux_t *demux = opaque;
+ demux_sys_t *sys = demux->p_sys;
+ int fd = sys->fd;
for (;;)
{
- block_t *block = rtp_recv (demux);
- if (block == NULL)
+ /* There is no reordering on stream sockets, so no timeout. */
+ ssize_t val;
+
+ uint16_t frame_len;
+ if (recv (fd, &frame_len, 2, MSG_WAITALL) != 2)
break;
- if (autodetect)
- { /* Autodetect payload type, _before_ rtp_queue() */
- /* No need for lock - the queue is empty. */
- if (rtp_autodetect (demux, p_sys->session, block))
- {
- block_Release (block);
- continue;
- }
- autodetect = false;
- }
+ block_t *block = block_Alloc (ntohs (frame_len));
+ if (unlikely(block == NULL))
+ break;
- vlc_mutex_lock (&p_sys->lock);
- rtp_queue (demux, p_sys->session, block);
- vlc_mutex_unlock (&p_sys->lock);
+ block_cleanup_push (block);
+ val = recv (fd, block->p_buffer, block->i_buffer, MSG_WAITALL);
+ vlc_cleanup_pop ();
+
+ if (val != (ssize_t)block->i_buffer)
+ {
+ block_Release (block);
+ break;
+ }
- rtp_process (demux);
+ int canc = vlc_savecancel ();
+ rtp_process (demux, block);
+ rtp_dequeue_force (demux, sys->session);
+ vlc_restorecancel (canc);
}
- vlc_cleanup_run ();
+#else
+ (void) opaque;
+#endif
return NULL;
}
-
-
-/**
- * Process one RTP packet from the de-jitter queue.
- */
-static void rtp_process (void *data)
-{
- demux_t *demux = data;
- demux_sys_t *p_sys = demux->p_sys;
- mtime_t deadline;
-
- vlc_mutex_lock (&p_sys->lock);
- if (rtp_dequeue (demux, p_sys->session, &deadline))
- vlc_timer_schedule (&p_sys->timer, true, deadline, 0);
- vlc_mutex_unlock (&p_sys->lock);
-}