1 /*****************************************************************************
2 * livehttp.c: Live HTTP Streaming
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 the VideoLAN team
5 * Copyright (C) 2009-2010 by Keary Griffin
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 /*****************************************************************************
27 *****************************************************************************/
33 #include <sys/types.h>
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
41 #include <vlc_block.h>
43 #include <vlc_strings.h>
44 #include <vlc_charset.h>
47 # define O_LARGEFILE 0
50 #define STR_ENDLIST "#EXT-X-ENDLIST\n"
52 #define MAX_RENAME_RETRIES 10
54 /*****************************************************************************
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")
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, NULL
, SEGLEN_TEXT
, SEGLEN_LONGTEXT
, true )
91 add_integer( SOUT_CFG_PREFIX
"numsegs", 0, NULL
, 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
)
106 /*****************************************************************************
107 * Exported prototypes
108 *****************************************************************************/
109 static const char *const ppsz_sout_options
[] = {
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
;
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
;
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" );
157 if( !( p_sys
= malloc ( sizeof( *p_sys
) ) ) )
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" );
173 psz_tmp
= str_format( p_access
, psz_idx
);
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
;
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
)
206 char *psz_firstNumSign
;
208 if ( ! ( psz_result
= str_format( p_access
, psz_path
) ) )
211 psz_firstNumSign
= psz_result
+ strcspn( psz_result
, SEG_NUMBER_PLACEHOLDER
);
212 if ( *psz_firstNumSign
) {
214 int i_cnt
= strspn( psz_firstNumSign
, SEG_NUMBER_PLACEHOLDER
);
217 *psz_firstNumSign
= '\0';
218 ret
= asprintf( &psz_newResult
, "%s%0*d%s", psz_result
, i_cnt
, i_seg
, psz_firstNumSign
+ i_cnt
);
222 psz_result
= psz_newResult
;
226 path_sanitize( 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
)
239 if ( p_sys
->i_numsegs
== 0 || p_sys
->i_segment
< p_sys
->i_numsegs
)
242 i_firstseg
= ( p_sys
->i_segment
- p_sys
->i_numsegs
) + 1;
244 // First update index
245 if ( p_sys
->psz_indexPath
)
250 if ( asprintf( &psz_idxTmp
, "%s.tmp", p_sys
->psz_indexPath
) < 0)
253 fp
= vlc_fopen( psz_idxTmp
, "wt");
256 msg_Err( p_access
, "cannot open index file `%s'", psz_idxTmp
);
261 if ( fprintf( fp
, "#EXTM3U\n#EXT-X-TARGETDURATION:%zu\n#EXT-X-MEDIA-SEQUENCE:%"PRIu32
"\n", p_sys
->i_seglen
, i_firstseg
) < 0 )
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
++ )
272 if ( ! ( psz_name
= formatSegmentPath( p_access
, psz_idxFormat
, i
, false ) ) )
278 val
= fprintf( fp
, "#EXTINF:%zu,\n%s\n", p_sys
->i_seglen
, psz_name
);
290 if ( fputs ( STR_ENDLIST
, fp
) < 0)
300 val
= vlc_rename ( psz_idxTmp
, p_sys
->psz_indexPath
);
304 vlc_unlink( psz_idxTmp
);
305 msg_Err( p_access
, "Error moving LiveHttp index file" );
308 msg_Info( p_access
, "LiveHttpIndexComplete: %s" , p_sys
->psz_indexPath
);
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 );
319 vlc_unlink( psz_name
);
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
);
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
;
368 case ACCESS_OUT_CONTROLS_PACE
:
370 bool *pb
= va_arg( args
, bool * );
371 *pb
= !p_sys
->b_ratecontrol
;
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
)
389 uint32_t i_newseg
= p_sys
->i_segment
+ 1;
391 char *psz_seg
= formatSegmentPath( p_access
, p_access
->psz_path
, i_newseg
, true );
395 fd
= vlc_open( psz_seg
, O_WRONLY
| O_CREAT
| O_LARGEFILE
|
399 msg_Err( p_access
, "cannot open `%s' (%m)", psz_seg
);
404 msg_Dbg( p_access
, "Successfully opened livehttp file: %s (%"PRIu32
")" , psz_seg
, i_newseg
);
407 p_sys
->psz_cursegPath
= psz_seg
;
408 p_sys
->i_handle
= fd
;
409 p_sys
->i_segment
= i_newseg
;
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
)
419 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
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 )
433 ssize_t val
= write ( p_sys
->i_handle
,
434 p_buffer
->p_buffer
, p_buffer
->i_buffer
);
437 if ( errno
== EINTR
)
439 block_ChainRelease ( p_buffer
);
443 if ( (size_t)val
>= p_buffer
->i_buffer
)
445 block_t
*p_next
= p_buffer
->p_next
;
446 block_Release (p_buffer
);
451 p_buffer
->p_buffer
+= val
;
452 p_buffer
->i_buffer
-= val
;
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
)
465 msg_Err( p_access
, "livehttp sout access cannot seek" );