add_integer: remove callback parameter
[vlc/asuraparaju-public.git] / modules / access_output / livehttp.c
blobd95539b2308f230cdfc800e213a8ee1f9d8a4736
1 /*****************************************************************************
2 * livehttp.c: Live HTTP Streaming
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 the VideoLAN team
5 * Copyright (C) 2009-2010 by Keary Griffin
6 * $Id$
8 * Authors: Keary Griffin <kearygriffin at gmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <sys/types.h>
34 #include <time.h>
35 #include <fcntl.h>
36 #include <errno.h>
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_sout.h>
41 #include <vlc_block.h>
42 #include <vlc_fs.h>
43 #include <vlc_strings.h>
44 #include <vlc_charset.h>
46 #ifndef O_LARGEFILE
47 # define O_LARGEFILE 0
48 #endif
50 #define STR_ENDLIST "#EXT-X-ENDLIST\n"
52 #define MAX_RENAME_RETRIES 10
54 /*****************************************************************************
55 * Module descriptor
56 *****************************************************************************/
57 static int Open ( vlc_object_t * );
58 static void Close( vlc_object_t * );
60 #define SOUT_CFG_PREFIX "sout-livehttp-"
61 #define SEGLEN_TEXT N_("Segment length")
62 #define SEGLEN_LONGTEXT N_("Length of TS stream segments")
64 #define SPLITANYWHERE_TEXT N_("Split segments anywhere")
65 #define SPLITANYWHERE_LONGTEXT N_("Don't require a keyframe before splitting "\
66 "a segment. Needed for audio only.")
68 #define NUMSEGS_TEXT N_("Number of segments")
69 #define NUMSEGS_LONGTEXT N_("Number of segments to include in index")
71 #define INDEX_TEXT N_("Index file")
72 #define INDEX_LONGTEXT N_("Path to the index file to create")
74 #define INDEXURL_TEXT N_("Full URL to put in index file")
75 #define INDEXURL_LONGTEXT N_("Full URL to put in index file. "\
76 "Use #'s to represent segment number")
78 #define DELSEGS_TEXT N_("Delete segments")
79 #define DELSEGS_LONGTEXT N_("Delete segments when they are no longer needed")
81 #define RATECONTROL_TEXT N_("Use muxers rate control mechanism")
83 vlc_module_begin ()
84 set_description( N_("HTTP Live streaming output") )
85 set_shortname( N_("LiveHTTP" ))
86 add_shortcut( "livehttp" )
87 set_capability( "sout access", 50 )
88 set_category( CAT_SOUT )
89 set_subcategory( SUBCAT_SOUT_ACO )
90 add_integer( SOUT_CFG_PREFIX "seglen", 10, SEGLEN_TEXT, SEGLEN_LONGTEXT, true )
91 add_integer( SOUT_CFG_PREFIX "numsegs", 0, NUMSEGS_TEXT, NUMSEGS_LONGTEXT, true )
92 add_bool( SOUT_CFG_PREFIX "splitanywhere", false, NULL,
93 SPLITANYWHERE_TEXT, SPLITANYWHERE_LONGTEXT, true )
94 add_bool( SOUT_CFG_PREFIX "delsegs", true, NULL,
95 DELSEGS_TEXT, DELSEGS_LONGTEXT, true )
96 add_bool( SOUT_CFG_PREFIX "ratecontrol", false, NULL,
97 RATECONTROL_TEXT, RATECONTROL_TEXT, true )
98 add_string( SOUT_CFG_PREFIX "index", NULL,
99 INDEX_TEXT, INDEX_LONGTEXT, true )
100 add_string( SOUT_CFG_PREFIX "index-url", NULL,
101 INDEXURL_TEXT, INDEXURL_LONGTEXT, true )
102 set_callbacks( Open, Close )
103 vlc_module_end ()
106 /*****************************************************************************
107 * Exported prototypes
108 *****************************************************************************/
109 static const char *const ppsz_sout_options[] = {
110 "seglen",
111 "splitanywhere",
112 "numsegs",
113 "delsegs",
114 "index",
115 "index-url",
116 "ratecontrol",
117 NULL
120 static ssize_t Write( sout_access_out_t *, block_t * );
121 static int Seek ( sout_access_out_t *, off_t );
122 static int Control( sout_access_out_t *, int, va_list );
124 struct sout_access_out_sys_t
126 char *psz_cursegPath;
127 char *psz_indexPath;
128 char *psz_indexUrl;
129 mtime_t i_opendts;
130 mtime_t i_seglenm;
131 uint32_t i_segment;
132 size_t i_seglen;
133 int i_handle;
134 unsigned i_numsegs;
135 bool b_delsegs;
136 bool b_ratecontrol;
137 bool b_splitanywhere;
140 /*****************************************************************************
141 * Open: open the file
142 *****************************************************************************/
143 static int Open( vlc_object_t *p_this )
145 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
146 sout_access_out_sys_t *p_sys;
147 char *psz_idx;
149 config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
151 if( !p_access->psz_path )
153 msg_Err( p_access, "no file name specified" );
154 return VLC_EGENERIC;
157 if( !( p_sys = malloc ( sizeof( *p_sys ) ) ) )
158 return VLC_ENOMEM;
160 p_sys->i_seglen = var_GetInteger( p_access, SOUT_CFG_PREFIX "seglen" );
161 p_sys->i_seglenm = CLOCK_FREQ * p_sys->i_seglen;
163 p_sys->i_numsegs = var_GetInteger( p_access, SOUT_CFG_PREFIX "numsegs" );
164 p_sys->b_splitanywhere = var_GetBool( p_access, SOUT_CFG_PREFIX "splitanywhere" );
165 p_sys->b_delsegs = var_GetBool( p_access, SOUT_CFG_PREFIX "delsegs" );
166 p_sys->b_ratecontrol = var_GetBool( p_access, SOUT_CFG_PREFIX "ratecontrol") ;
168 p_sys->psz_indexPath = NULL;
169 psz_idx = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "index" );
170 if ( psz_idx )
172 char *psz_tmp;
173 psz_tmp = str_format( p_access, psz_idx );
174 free( psz_idx );
175 if ( !psz_tmp )
177 free( p_sys );
178 return VLC_ENOMEM;
180 path_sanitize( psz_tmp );
181 p_sys->psz_indexPath = psz_tmp;
182 vlc_unlink( p_sys->psz_indexPath );
185 p_sys->psz_indexUrl = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "index-url" );
187 p_access->p_sys = p_sys;
188 p_sys->i_handle = -1;
189 p_sys->i_segment = 0;
190 p_sys->psz_cursegPath = NULL;
192 p_access->pf_write = Write;
193 p_access->pf_seek = Seek;
194 p_access->pf_control = Control;
196 return VLC_SUCCESS;
199 #define SEG_NUMBER_PLACEHOLDER "#"
200 /*****************************************************************************
201 * formatSegmentPath: create segment path name based on seg #
202 *****************************************************************************/
203 static char *formatSegmentPath( sout_access_out_t *p_access, char *psz_path, uint32_t i_seg, bool b_sanitize )
205 char *psz_result;
206 char *psz_firstNumSign;
208 if ( ! ( psz_result = str_format( p_access, psz_path ) ) )
209 return NULL;
211 psz_firstNumSign = psz_result + strcspn( psz_result, SEG_NUMBER_PLACEHOLDER );
212 if ( *psz_firstNumSign ) {
213 char *psz_newResult;
214 int i_cnt = strspn( psz_firstNumSign, SEG_NUMBER_PLACEHOLDER );
215 int ret;
217 *psz_firstNumSign = '\0';
218 ret = asprintf( &psz_newResult, "%s%0*d%s", psz_result, i_cnt, i_seg, psz_firstNumSign + i_cnt );
219 free ( psz_result );
220 if ( ret < 0 )
221 return NULL;
222 psz_result = psz_newResult;
225 if ( b_sanitize )
226 path_sanitize( psz_result );
228 return psz_result;
231 /************************************************************************
232 * updateIndexAndDel: If necessary, update index file & delete old segments
233 ************************************************************************/
234 static int updateIndexAndDel( sout_access_out_t *p_access, sout_access_out_sys_t *p_sys, bool b_isend )
237 uint32_t i_firstseg;
239 if ( p_sys->i_numsegs == 0 || p_sys->i_segment < p_sys->i_numsegs )
240 i_firstseg = 1;
241 else
242 i_firstseg = ( p_sys->i_segment - p_sys->i_numsegs ) + 1;
244 // First update index
245 if ( p_sys->psz_indexPath )
247 int val;
248 FILE *fp;
249 char *psz_idxTmp;
250 if ( asprintf( &psz_idxTmp, "%s.tmp", p_sys->psz_indexPath ) < 0)
251 return -1;
253 fp = vlc_fopen( psz_idxTmp, "wt");
254 if ( !fp )
256 msg_Err( p_access, "cannot open index file `%s'", psz_idxTmp );
257 free( psz_idxTmp );
258 return -1;
261 if ( fprintf( fp, "#EXTM3U\n#EXT-X-TARGETDURATION:%zu\n#EXT-X-MEDIA-SEQUENCE:%"PRIu32"\n", p_sys->i_seglen, i_firstseg ) < 0 )
263 free( psz_idxTmp );
264 fclose( fp );
265 return -1;
268 char *psz_idxFormat = p_sys->psz_indexUrl ? p_sys->psz_indexUrl : p_access->psz_path;
269 for ( uint32_t i = i_firstseg; i <= p_sys->i_segment; i++ )
271 char *psz_name;
272 if ( ! ( psz_name = formatSegmentPath( p_access, psz_idxFormat, i, false ) ) )
274 free( psz_idxTmp );
275 fclose( fp );
276 return -1;
278 val = fprintf( fp, "#EXTINF:%zu,\n%s\n", p_sys->i_seglen, psz_name );
279 free( psz_name );
280 if ( val < 0 )
282 free( psz_idxTmp );
283 fclose( fp );
284 return -1;
288 if ( b_isend )
290 if ( fputs ( STR_ENDLIST, fp ) < 0)
292 free( psz_idxTmp );
293 fclose( fp ) ;
294 return -1;
298 fclose( fp );
300 val = vlc_rename ( psz_idxTmp, p_sys->psz_indexPath);
302 if ( val < 0 )
304 vlc_unlink( psz_idxTmp );
305 msg_Err( p_access, "Error moving LiveHttp index file" );
307 else
308 msg_Info( p_access, "LiveHttpIndexComplete: %s" , p_sys->psz_indexPath );
310 free( psz_idxTmp );
313 // Then take care of deletion
314 if ( p_sys->b_delsegs && i_firstseg > 1 )
316 char *psz_name = formatSegmentPath( p_access, p_access->psz_path, i_firstseg-1, true );
317 if ( psz_name )
319 vlc_unlink( psz_name );
320 free( psz_name );
323 return 0;
326 /*****************************************************************************
327 * closeCurrentSegment: Close the segment file
328 *****************************************************************************/
329 static void closeCurrentSegment( sout_access_out_t *p_access, sout_access_out_sys_t *p_sys, bool b_isend )
331 if ( p_sys->i_handle >= 0 )
333 close( p_sys->i_handle );
334 p_sys->i_handle = -1;
335 if ( p_sys->psz_cursegPath )
337 msg_Info( p_access, "LiveHttpSegmentComplete: %s (%"PRIu32")" , p_sys->psz_cursegPath, p_sys->i_segment );
338 free( p_sys->psz_cursegPath );
339 p_sys->psz_cursegPath = 0;
340 updateIndexAndDel( p_access, p_sys, b_isend );
345 /*****************************************************************************
346 * Close: close the target
347 *****************************************************************************/
348 static void Close( vlc_object_t * p_this )
350 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
351 sout_access_out_sys_t *p_sys = p_access->p_sys;
354 closeCurrentSegment( p_access, p_sys, true );
355 free( p_sys->psz_indexUrl );
356 free( p_sys->psz_indexPath );
357 free( p_sys );
359 msg_Dbg( p_access, "livehttp access output closed" );
362 static int Control( sout_access_out_t *p_access, int i_query, va_list args )
364 sout_access_out_sys_t *p_sys = p_access->p_sys;
366 switch( i_query )
368 case ACCESS_OUT_CONTROLS_PACE:
370 bool *pb = va_arg( args, bool * );
371 *pb = !p_sys->b_ratecontrol;
372 //*pb = true;
373 break;
376 default:
377 return VLC_EGENERIC;
379 return VLC_SUCCESS;
382 /*****************************************************************************
383 * openNextFile: Open the segment file
384 *****************************************************************************/
385 static ssize_t openNextFile( sout_access_out_t *p_access, sout_access_out_sys_t *p_sys )
387 int fd;
389 uint32_t i_newseg = p_sys->i_segment + 1;
391 char *psz_seg = formatSegmentPath( p_access, p_access->psz_path, i_newseg, true );
392 if ( !psz_seg )
393 return -1;
395 fd = vlc_open( psz_seg, O_WRONLY | O_CREAT | O_LARGEFILE |
396 O_TRUNC, 0666 );
397 if ( fd == -1 )
399 msg_Err( p_access, "cannot open `%s' (%m)", psz_seg );
400 free( psz_seg );
401 return -1;
404 msg_Dbg( p_access, "Successfully opened livehttp file: %s (%"PRIu32")" , psz_seg, i_newseg );
406 //free( psz_seg );
407 p_sys->psz_cursegPath = psz_seg;
408 p_sys->i_handle = fd;
409 p_sys->i_segment = i_newseg;
410 return fd;
413 /*****************************************************************************
414 * Write: standard write on a file descriptor.
415 *****************************************************************************/
416 static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
418 size_t i_write = 0;
419 sout_access_out_sys_t *p_sys = p_access->p_sys;
421 while( p_buffer )
423 if ( p_sys->i_handle >= 0 && ( p_sys->b_splitanywhere || ( p_buffer->i_flags & BLOCK_FLAG_TYPE_I ) ) && ( p_buffer->i_dts-p_sys->i_opendts ) > p_sys->i_seglenm )
425 closeCurrentSegment( p_access, p_sys, false );
427 if ( p_buffer->i_buffer > 0 && p_sys->i_handle < 0 )
429 p_sys->i_opendts = p_buffer->i_dts;
430 if ( openNextFile( p_access, p_sys ) < 0 )
431 return -1;
433 ssize_t val = write ( p_sys->i_handle,
434 p_buffer->p_buffer, p_buffer->i_buffer );
435 if ( val == -1 )
437 if ( errno == EINTR )
438 continue;
439 block_ChainRelease ( p_buffer );
440 return -1;
443 if ( (size_t)val >= p_buffer->i_buffer )
445 block_t *p_next = p_buffer->p_next;
446 block_Release (p_buffer);
447 p_buffer = p_next;
449 else
451 p_buffer->p_buffer += val;
452 p_buffer->i_buffer -= val;
454 i_write += val;
456 return i_write;
459 /*****************************************************************************
460 * Seek: seek to a specific location in a file
461 *****************************************************************************/
462 static int Seek( sout_access_out_t *p_access, off_t i_pos )
464 (void) i_pos;
465 msg_Err( p_access, "livehttp sout access cannot seek" );
466 return -1;