packetizer: hevc: don't double store poc prev msb/lsb
[vlc.git] / modules / stream_out / vod.c
blob9db3d709f071534e5fde94f7a149df0cba2f77cc
1 /*****************************************************************************
2 * vod.c: rtsp VoD server module
3 *****************************************************************************
4 * Copyright (C) 2003-2006, 2010 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Gildas Bazin <gbazin@videolan.org>
9 * Pierre Ynard
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_input.h>
37 #include <vlc_sout.h>
38 #include <vlc_block.h>
40 #include <vlc_vod.h>
41 #include <vlc_url.h>
42 #include <vlc_network.h>
43 #include <vlc_memstream.h>
45 #include <assert.h>
47 #include "rtp.h"
49 /*****************************************************************************
50 * Exported prototypes
51 *****************************************************************************/
53 typedef struct media_es_t media_es_t;
55 struct media_es_t
57 int es_id;
58 rtp_format_t rtp_fmt;
59 rtsp_stream_id_t *rtsp_id;
62 struct vod_media_t
64 /* VoD server */
65 vod_t *p_vod;
67 /* RTSP server */
68 rtsp_stream_t *rtsp;
70 /* ES list */
71 int i_es;
72 media_es_t **es;
73 const char *psz_mux;
75 /* Infos */
76 mtime_t i_length;
79 struct vod_sys_t
81 char *psz_rtsp_path;
83 /* */
84 vlc_thread_t thread;
85 block_fifo_t *p_fifo_cmd;
88 /* rtsp delayed command (to avoid deadlock between vlm/httpd) */
89 typedef enum
91 RTSP_CMD_TYPE_STOP,
92 RTSP_CMD_TYPE_ADD,
93 RTSP_CMD_TYPE_DEL,
94 } rtsp_cmd_type_t;
96 /* */
97 typedef struct
99 int i_type;
100 vod_media_t *p_media;
101 char *psz_arg;
102 } rtsp_cmd_t;
104 static vod_media_t *MediaNew( vod_t *, const char *, input_item_t * );
105 static void MediaDel( vod_t *, vod_media_t * );
106 static void MediaAskDel ( vod_t *, vod_media_t * );
108 static void* CommandThread( void *obj );
109 static void CommandPush( vod_t *, rtsp_cmd_type_t, vod_media_t *,
110 const char *psz_arg );
112 /*****************************************************************************
113 * Open: Starts the RTSP server module
114 *****************************************************************************/
115 int OpenVoD( vlc_object_t *p_this )
117 vod_t *p_vod = (vod_t *)p_this;
118 vod_sys_t *p_sys = NULL;
119 char *psz_url;
121 p_vod->p_sys = p_sys = malloc( sizeof( vod_sys_t ) );
122 if( !p_sys ) goto error;
124 psz_url = var_InheritString( p_vod, "rtsp-host" );
126 if( psz_url == NULL )
127 p_sys->psz_rtsp_path = strdup( "/" );
128 else
130 vlc_url_t url;
131 vlc_UrlParse( &url, psz_url );
132 free( psz_url );
134 if( url.psz_path == NULL )
135 p_sys->psz_rtsp_path = strdup( "/" );
136 else
137 if( !( strlen( url.psz_path ) > 0
138 && url.psz_path[strlen( url.psz_path ) - 1] == '/' ) )
140 if( asprintf( &p_sys->psz_rtsp_path, "%s/", url.psz_path ) == -1 )
142 p_sys->psz_rtsp_path = NULL;
143 vlc_UrlClean( &url );
144 goto error;
147 else
148 p_sys->psz_rtsp_path = strdup( url.psz_path );
150 vlc_UrlClean( &url );
153 p_vod->pf_media_new = MediaNew;
154 p_vod->pf_media_del = MediaAskDel;
156 p_sys->p_fifo_cmd = block_FifoNew();
157 if( vlc_clone( &p_sys->thread, CommandThread, p_vod, VLC_THREAD_PRIORITY_LOW ) )
159 msg_Err( p_vod, "cannot spawn rtsp vod thread" );
160 block_FifoRelease( p_sys->p_fifo_cmd );
161 goto error;
164 return VLC_SUCCESS;
166 error:
167 if( p_sys )
169 free( p_sys->psz_rtsp_path );
170 free( p_sys );
173 return VLC_EGENERIC;
176 /*****************************************************************************
177 * Close:
178 *****************************************************************************/
179 void CloseVoD( vlc_object_t * p_this )
181 vod_t *p_vod = (vod_t *)p_this;
182 vod_sys_t *p_sys = p_vod->p_sys;
184 /* Stop command thread */
185 vlc_cancel( p_sys->thread );
186 vlc_join( p_sys->thread, NULL );
188 while( block_FifoCount( p_sys->p_fifo_cmd ) > 0 )
190 rtsp_cmd_t cmd;
191 block_t *p_block_cmd = block_FifoGet( p_sys->p_fifo_cmd );
192 memcpy( &cmd, p_block_cmd->p_buffer, sizeof(cmd) );
193 block_Release( p_block_cmd );
194 if ( cmd.i_type == RTSP_CMD_TYPE_DEL )
195 MediaDel(p_vod, cmd.p_media);
196 free( cmd.psz_arg );
198 block_FifoRelease( p_sys->p_fifo_cmd );
200 free( p_sys->psz_rtsp_path );
201 free( p_sys );
204 /*****************************************************************************
205 * Media handling
206 *****************************************************************************/
207 static vod_media_t *MediaNew( vod_t *p_vod, const char *psz_name,
208 input_item_t *p_item )
210 vod_media_t *p_media = calloc( 1, sizeof(vod_media_t) );
211 if( !p_media )
212 return NULL;
214 p_media->p_vod = p_vod;
215 p_media->rtsp = NULL;
216 TAB_INIT( p_media->i_es, p_media->es );
217 p_media->psz_mux = NULL;
218 p_media->i_length = input_item_GetDuration( p_item );
220 vlc_mutex_lock( &p_item->lock );
221 msg_Dbg( p_vod, "media '%s' has %i declared ES", psz_name, p_item->i_es );
222 for( int i = 0; i < p_item->i_es; i++ )
224 es_format_t *p_fmt = p_item->es[i];
226 switch( p_fmt->i_codec )
228 case VLC_FOURCC( 'm', 'p', '2', 't' ):
229 p_media->psz_mux = "ts";
230 break;
231 case VLC_FOURCC( 'm', 'p', '2', 'p' ):
232 p_media->psz_mux = "ps";
233 break;
235 assert(p_media->psz_mux == NULL || p_item->i_es == 1);
237 media_es_t *p_es = calloc( 1, sizeof(media_es_t) );
238 if( !p_es )
239 continue;
241 p_es->es_id = p_fmt->i_id;
242 p_es->rtsp_id = NULL;
244 if (rtp_get_fmt(VLC_OBJECT(p_vod), p_fmt, p_media->psz_mux,
245 &p_es->rtp_fmt) != VLC_SUCCESS)
247 free(p_es);
248 continue;
251 TAB_APPEND( p_media->i_es, p_media->es, p_es );
252 msg_Dbg(p_vod, " - added ES %u %s (%4.4s)",
253 p_es->rtp_fmt.payload_type, p_es->rtp_fmt.ptname,
254 (char *)&p_fmt->i_codec);
256 vlc_mutex_unlock( &p_item->lock );
258 if (p_media->i_es == 0)
260 msg_Err(p_vod, "no ES was added to the media, aborting");
261 goto error;
264 msg_Dbg(p_vod, "adding media '%s'", psz_name);
266 CommandPush( p_vod, RTSP_CMD_TYPE_ADD, p_media, psz_name );
267 return p_media;
269 error:
270 MediaDel(p_vod, p_media);
271 return NULL;
274 static void MediaSetup( vod_t *p_vod, vod_media_t *p_media,
275 const char *psz_name )
277 vod_sys_t *p_sys = p_vod->p_sys;
278 char *psz_path;
280 if( asprintf( &psz_path, "%s%s", p_sys->psz_rtsp_path, psz_name ) < 0 )
281 return;
283 p_media->rtsp = RtspSetup(VLC_OBJECT(p_vod), p_media, psz_path);
284 free( psz_path );
286 if (p_media->rtsp == NULL)
287 return;
289 for (int i = 0; i < p_media->i_es; i++)
291 media_es_t *p_es = p_media->es[i];
292 p_es->rtsp_id = RtspAddId(p_media->rtsp, NULL, 0,
293 p_es->rtp_fmt.clock_rate, -1);
297 static void MediaAskDel ( vod_t *p_vod, vod_media_t *p_media )
299 msg_Dbg( p_vod, "deleting media" );
300 CommandPush( p_vod, RTSP_CMD_TYPE_DEL, p_media, NULL );
303 static void MediaDel( vod_t *p_vod, vod_media_t *p_media )
305 (void) p_vod;
307 if (p_media->rtsp != NULL)
309 for (int i = 0; i < p_media->i_es; i++)
311 media_es_t *p_es = p_media->es[i];
312 if (p_es->rtsp_id != NULL)
313 RtspDelId(p_media->rtsp, p_es->rtsp_id);
315 RtspUnsetup(p_media->rtsp);
318 for( int i = 0; i < p_media->i_es; i++ )
320 free( p_media->es[i]->rtp_fmt.fmtp );
321 free( p_media->es[i] );
323 free( p_media->es );
325 free( p_media );
328 static void CommandPush( vod_t *p_vod, rtsp_cmd_type_t i_type,
329 vod_media_t *p_media, const char *psz_arg )
331 rtsp_cmd_t cmd;
332 block_t *p_cmd;
334 cmd.i_type = i_type;
335 cmd.p_media = p_media;
336 if( psz_arg )
337 cmd.psz_arg = strdup(psz_arg);
338 else
339 cmd.psz_arg = NULL;
341 p_cmd = block_Alloc( sizeof(rtsp_cmd_t) );
342 memcpy( p_cmd->p_buffer, &cmd, sizeof(cmd) );
344 block_FifoPut( p_vod->p_sys->p_fifo_cmd, p_cmd );
347 static void* CommandThread( void *obj )
349 vod_t *p_vod = (vod_t*)obj;
350 vod_sys_t *p_sys = p_vod->p_sys;
352 for( ;; )
354 block_t *p_block_cmd = block_FifoGet( p_sys->p_fifo_cmd );
355 rtsp_cmd_t cmd;
357 if( !p_block_cmd )
358 break;
360 int canc = vlc_savecancel ();
361 memcpy( &cmd, p_block_cmd->p_buffer, sizeof(cmd) );
362 block_Release( p_block_cmd );
364 /* */
365 switch( cmd.i_type )
367 case RTSP_CMD_TYPE_ADD:
368 MediaSetup(p_vod, cmd.p_media, cmd.psz_arg);
369 break;
370 case RTSP_CMD_TYPE_DEL:
371 MediaDel(p_vod, cmd.p_media);
372 break;
373 case RTSP_CMD_TYPE_STOP:
374 vod_MediaControl( p_vod, cmd.p_media, cmd.psz_arg, VOD_MEDIA_STOP );
375 break;
377 default:
378 break;
381 free( cmd.psz_arg );
382 vlc_restorecancel (canc);
385 return NULL;
388 /*****************************************************************************
389 * SDPGenerateVoD
390 * FIXME: needs to be merged more?
391 *****************************************************************************/
392 char *SDPGenerateVoD( const vod_media_t *p_media, const char *rtsp_url )
394 assert(rtsp_url != NULL);
395 /* Check against URL format rtsp://[<ipv6>]:<port>/<path> */
396 bool ipv6 = strlen( rtsp_url ) > 7 && rtsp_url[7] == '[';
398 /* Dummy destination address for RTSP */
399 struct sockaddr_storage dst;
400 socklen_t dstlen = ipv6 ? sizeof( struct sockaddr_in6 )
401 : sizeof( struct sockaddr_in );
402 memset (&dst, 0, dstlen);
403 dst.ss_family = ipv6 ? AF_INET6 : AF_INET;
404 #ifdef HAVE_SA_LEN
405 dst.ss_len = dstlen;
406 #endif
408 struct vlc_memstream sdp;
410 if( vlc_sdp_Start( &sdp, VLC_OBJECT( p_media->p_vod ), "sout-rtp-",
411 NULL, 0, (struct sockaddr *)&dst, dstlen ) )
412 return NULL;
414 if( p_media->i_length > 0 )
416 lldiv_t d = lldiv( p_media->i_length / 1000, 1000 );
417 sdp_AddAttribute( &sdp, "range"," npt=0-%lld.%03u", d.quot,
418 (unsigned)d.rem );
421 sdp_AddAttribute( &sdp, "control", "%s", rtsp_url );
423 /* No locking needed, the ES table can't be modified now */
424 for( int i = 0; i < p_media->i_es; i++ )
426 media_es_t *p_es = p_media->es[i];
427 rtp_format_t *rtp_fmt = &p_es->rtp_fmt;
428 const char *mime_major; /* major MIME type */
430 switch( rtp_fmt->cat )
432 case VIDEO_ES:
433 mime_major = "video";
434 break;
435 case AUDIO_ES:
436 mime_major = "audio";
437 break;
438 case SPU_ES:
439 mime_major = "text";
440 break;
441 default:
442 continue;
445 sdp_AddMedia( &sdp, mime_major, "RTP/AVP", 0,
446 rtp_fmt->payload_type, false, 0,
447 rtp_fmt->ptname, rtp_fmt->clock_rate, rtp_fmt->channels,
448 rtp_fmt->fmtp );
450 char *track_url = RtspAppendTrackPath( p_es->rtsp_id, rtsp_url );
451 if( track_url != NULL )
453 sdp_AddAttribute( &sdp, "control", "%s", track_url );
454 free( track_url );
458 return vlc_memstream_close( &sdp ) ? NULL : sdp.ptr;
461 int vod_check_range(vod_media_t *p_media, const char *psz_session,
462 int64_t start, int64_t end)
464 (void) psz_session;
466 if (p_media->i_length > 0 && (start > p_media->i_length
467 || end > p_media->i_length))
468 return VLC_EGENERIC;
470 return VLC_SUCCESS;
473 /* TODO: add support in the VLM for queueing proper PLAY requests with
474 * start and end times, fetch whether the input is seekable... and then
475 * clean this up */
476 void vod_play(vod_media_t *p_media, const char *psz_session,
477 int64_t *start, int64_t end)
479 if (vod_check_range(p_media, psz_session, *start, end) != VLC_SUCCESS)
480 return;
482 /* We're passing the #vod{} sout chain here */
483 vod_MediaControl(p_media->p_vod, p_media, psz_session,
484 VOD_MEDIA_PLAY, "vod", start);
487 void vod_pause(vod_media_t *p_media, const char *psz_session, int64_t *npt)
489 vod_MediaControl(p_media->p_vod, p_media, psz_session,
490 VOD_MEDIA_PAUSE, npt);
493 void vod_stop(vod_media_t *p_media, const char *psz_session)
495 CommandPush(p_media->p_vod, RTSP_CMD_TYPE_STOP, p_media, psz_session);
499 const char *vod_get_mux(const vod_media_t *p_media)
501 return p_media->psz_mux;
505 /* Match an RTP id to a VoD media ES and RTSP track to initialize it
506 * with the data that was already set up */
507 int vod_init_id(vod_media_t *p_media, const char *psz_session, int es_id,
508 sout_stream_id_sys_t *sout_id, rtp_format_t *rtp_fmt,
509 uint32_t *ssrc, uint16_t *seq_init)
511 media_es_t *p_es;
513 if (p_media->psz_mux != NULL)
515 assert(p_media->i_es == 1);
516 p_es = p_media->es[0];
518 else
520 p_es = NULL;
521 /* No locking needed, the ES table can't be modified now */
522 for (int i = 0; i < p_media->i_es; i++)
524 if (p_media->es[i]->es_id == es_id)
526 p_es = p_media->es[i];
527 break;
530 if (p_es == NULL)
531 return VLC_EGENERIC;
534 memcpy(rtp_fmt, &p_es->rtp_fmt, sizeof(*rtp_fmt));
535 if (p_es->rtp_fmt.fmtp != NULL)
536 rtp_fmt->fmtp = strdup(p_es->rtp_fmt.fmtp);
538 return RtspTrackAttach(p_media->rtsp, psz_session, p_es->rtsp_id,
539 sout_id, ssrc, seq_init);
542 /* Remove references to the RTP id from its RTSP track */
543 void vod_detach_id(vod_media_t *p_media, const char *psz_session,
544 sout_stream_id_sys_t *sout_id)
546 RtspTrackDetach(p_media->rtsp, psz_session, sout_id);