1 /*****************************************************************************
2 * livehttp.c: Live HTTP Streaming
3 *****************************************************************************
4 * Copyright © 2001, 2002 VLC authors and VideoLAN
5 * Copyright © 2009-2010 by Keary Griffin
7 * Authors: Keary Griffin <kearygriffin at gmail.com>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <sys/types.h>
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
40 #include <vlc_block.h>
42 #include <vlc_strings.h>
43 #include <vlc_charset.h>
46 # define O_LARGEFILE 0
49 #define STR_ENDLIST "#EXT-X-ENDLIST\n"
51 #define MAX_RENAME_RETRIES 10
53 /*****************************************************************************
55 *****************************************************************************/
56 static int Open ( vlc_object_t
* );
57 static void Close( vlc_object_t
* );
59 #define SOUT_CFG_PREFIX "sout-livehttp-"
60 #define SEGLEN_TEXT N_("Segment length")
61 #define SEGLEN_LONGTEXT N_("Length of TS stream segments")
63 #define SPLITANYWHERE_TEXT N_("Split segments anywhere")
64 #define SPLITANYWHERE_LONGTEXT N_("Don't require a keyframe before splitting "\
65 "a segment. Needed for audio only.")
67 #define NUMSEGS_TEXT N_("Number of segments")
68 #define NUMSEGS_LONGTEXT N_("Number of segments to include in index")
70 #define INDEX_TEXT N_("Index file")
71 #define INDEX_LONGTEXT N_("Path to the index file to create")
73 #define INDEXURL_TEXT N_("Full URL to put in index file")
74 #define INDEXURL_LONGTEXT N_("Full URL to put in index file. "\
75 "Use #'s to represent segment number")
77 #define DELSEGS_TEXT N_("Delete segments")
78 #define DELSEGS_LONGTEXT N_("Delete segments when they are no longer needed")
80 #define RATECONTROL_TEXT N_("Use muxers rate control mechanism")
83 set_description( N_("HTTP Live streaming output") )
84 set_shortname( N_("LiveHTTP" ))
85 add_shortcut( "livehttp" )
86 set_capability( "sout access", 0 )
87 set_category( CAT_SOUT
)
88 set_subcategory( SUBCAT_SOUT_ACO
)
89 add_integer( SOUT_CFG_PREFIX
"seglen", 10, SEGLEN_TEXT
, SEGLEN_LONGTEXT
, true )
90 add_integer( SOUT_CFG_PREFIX
"numsegs", 0, NUMSEGS_TEXT
, NUMSEGS_LONGTEXT
, true )
91 add_bool( SOUT_CFG_PREFIX
"splitanywhere", false,
92 SPLITANYWHERE_TEXT
, SPLITANYWHERE_LONGTEXT
, true )
93 add_bool( SOUT_CFG_PREFIX
"delsegs", true,
94 DELSEGS_TEXT
, DELSEGS_LONGTEXT
, true )
95 add_bool( SOUT_CFG_PREFIX
"ratecontrol", false,
96 RATECONTROL_TEXT
, RATECONTROL_TEXT
, true )
97 add_string( SOUT_CFG_PREFIX
"index", NULL
,
98 INDEX_TEXT
, INDEX_LONGTEXT
, true )
99 add_string( SOUT_CFG_PREFIX
"index-url", NULL
,
100 INDEXURL_TEXT
, INDEXURL_LONGTEXT
, true )
101 set_callbacks( Open
, Close
)
105 /*****************************************************************************
106 * Exported prototypes
107 *****************************************************************************/
108 static const char *const ppsz_sout_options
[] = {
119 static ssize_t
Write( sout_access_out_t
*, block_t
* );
120 static int Seek ( sout_access_out_t
*, off_t
);
121 static int Control( sout_access_out_t
*, int, va_list );
123 struct sout_access_out_sys_t
125 char *psz_cursegPath
;
136 bool b_splitanywhere
;
139 /*****************************************************************************
140 * Open: open the file
141 *****************************************************************************/
142 static int Open( vlc_object_t
*p_this
)
144 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
145 sout_access_out_sys_t
*p_sys
;
148 config_ChainParse( p_access
, SOUT_CFG_PREFIX
, ppsz_sout_options
, p_access
->p_cfg
);
150 if( !p_access
->psz_path
)
152 msg_Err( p_access
, "no file name specified" );
156 if( !( p_sys
= malloc ( sizeof( *p_sys
) ) ) )
159 p_sys
->i_seglen
= var_GetInteger( p_access
, SOUT_CFG_PREFIX
"seglen" );
160 p_sys
->i_seglenm
= CLOCK_FREQ
* p_sys
->i_seglen
;
162 p_sys
->i_numsegs
= var_GetInteger( p_access
, SOUT_CFG_PREFIX
"numsegs" );
163 p_sys
->b_splitanywhere
= var_GetBool( p_access
, SOUT_CFG_PREFIX
"splitanywhere" );
164 p_sys
->b_delsegs
= var_GetBool( p_access
, SOUT_CFG_PREFIX
"delsegs" );
165 p_sys
->b_ratecontrol
= var_GetBool( p_access
, SOUT_CFG_PREFIX
"ratecontrol") ;
167 p_sys
->psz_indexPath
= NULL
;
168 psz_idx
= var_GetNonEmptyString( p_access
, SOUT_CFG_PREFIX
"index" );
172 psz_tmp
= str_format( p_access
, psz_idx
);
179 path_sanitize( psz_tmp
);
180 p_sys
->psz_indexPath
= psz_tmp
;
181 vlc_unlink( p_sys
->psz_indexPath
);
184 p_sys
->psz_indexUrl
= var_GetNonEmptyString( p_access
, SOUT_CFG_PREFIX
"index-url" );
186 p_access
->p_sys
= p_sys
;
187 p_sys
->i_handle
= -1;
188 p_sys
->i_segment
= 0;
189 p_sys
->psz_cursegPath
= NULL
;
191 p_access
->pf_write
= Write
;
192 p_access
->pf_seek
= Seek
;
193 p_access
->pf_control
= Control
;
198 #define SEG_NUMBER_PLACEHOLDER "#"
199 /*****************************************************************************
200 * formatSegmentPath: create segment path name based on seg #
201 *****************************************************************************/
202 static char *formatSegmentPath( sout_access_out_t
*p_access
, char *psz_path
, uint32_t i_seg
, bool b_sanitize
)
205 char *psz_firstNumSign
;
207 if ( ! ( psz_result
= str_format( p_access
, psz_path
) ) )
210 psz_firstNumSign
= psz_result
+ strcspn( psz_result
, SEG_NUMBER_PLACEHOLDER
);
211 if ( *psz_firstNumSign
) {
213 int i_cnt
= strspn( psz_firstNumSign
, SEG_NUMBER_PLACEHOLDER
);
216 *psz_firstNumSign
= '\0';
217 ret
= asprintf( &psz_newResult
, "%s%0*d%s", psz_result
, i_cnt
, i_seg
, psz_firstNumSign
+ i_cnt
);
221 psz_result
= psz_newResult
;
225 path_sanitize( psz_result
);
230 /************************************************************************
231 * updateIndexAndDel: If necessary, update index file & delete old segments
232 ************************************************************************/
233 static int updateIndexAndDel( sout_access_out_t
*p_access
, sout_access_out_sys_t
*p_sys
, bool b_isend
)
238 if ( p_sys
->i_numsegs
== 0 || p_sys
->i_segment
< p_sys
->i_numsegs
)
241 i_firstseg
= ( p_sys
->i_segment
- p_sys
->i_numsegs
) + 1;
243 // First update index
244 if ( p_sys
->psz_indexPath
)
249 if ( asprintf( &psz_idxTmp
, "%s.tmp", p_sys
->psz_indexPath
) < 0)
252 fp
= vlc_fopen( psz_idxTmp
, "wt");
255 msg_Err( p_access
, "cannot open index file `%s'", psz_idxTmp
);
260 if ( fprintf( fp
, "#EXTM3U\n#EXT-X-TARGETDURATION:%zu\n#EXT-X-MEDIA-SEQUENCE:%"PRIu32
"\n", p_sys
->i_seglen
, i_firstseg
) < 0 )
267 char *psz_idxFormat
= p_sys
->psz_indexUrl
? p_sys
->psz_indexUrl
: p_access
->psz_path
;
268 for ( uint32_t i
= i_firstseg
; i
<= p_sys
->i_segment
; i
++ )
271 if ( ! ( psz_name
= formatSegmentPath( p_access
, psz_idxFormat
, i
, false ) ) )
277 val
= fprintf( fp
, "#EXTINF:%zu,\n%s\n", p_sys
->i_seglen
, psz_name
);
289 if ( fputs ( STR_ENDLIST
, fp
) < 0)
299 val
= vlc_rename ( psz_idxTmp
, p_sys
->psz_indexPath
);
303 vlc_unlink( psz_idxTmp
);
304 msg_Err( p_access
, "Error moving LiveHttp index file" );
307 msg_Info( p_access
, "LiveHttpIndexComplete: %s" , p_sys
->psz_indexPath
);
312 // Then take care of deletion
313 if ( p_sys
->b_delsegs
&& i_firstseg
> 1 )
315 char *psz_name
= formatSegmentPath( p_access
, p_access
->psz_path
, i_firstseg
-1, true );
318 vlc_unlink( psz_name
);
325 /*****************************************************************************
326 * closeCurrentSegment: Close the segment file
327 *****************************************************************************/
328 static void closeCurrentSegment( sout_access_out_t
*p_access
, sout_access_out_sys_t
*p_sys
, bool b_isend
)
330 if ( p_sys
->i_handle
>= 0 )
332 close( p_sys
->i_handle
);
333 p_sys
->i_handle
= -1;
334 if ( p_sys
->psz_cursegPath
)
336 msg_Info( p_access
, "LiveHttpSegmentComplete: %s (%"PRIu32
")" , p_sys
->psz_cursegPath
, p_sys
->i_segment
);
337 free( p_sys
->psz_cursegPath
);
338 p_sys
->psz_cursegPath
= 0;
339 updateIndexAndDel( p_access
, p_sys
, b_isend
);
344 /*****************************************************************************
345 * Close: close the target
346 *****************************************************************************/
347 static void Close( vlc_object_t
* p_this
)
349 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
350 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
353 closeCurrentSegment( p_access
, p_sys
, true );
354 free( p_sys
->psz_indexUrl
);
355 free( p_sys
->psz_indexPath
);
358 msg_Dbg( p_access
, "livehttp access output closed" );
361 static int Control( sout_access_out_t
*p_access
, int i_query
, va_list args
)
363 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
367 case ACCESS_OUT_CONTROLS_PACE
:
369 bool *pb
= va_arg( args
, bool * );
370 *pb
= !p_sys
->b_ratecontrol
;
381 /*****************************************************************************
382 * openNextFile: Open the segment file
383 *****************************************************************************/
384 static ssize_t
openNextFile( sout_access_out_t
*p_access
, sout_access_out_sys_t
*p_sys
)
388 uint32_t i_newseg
= p_sys
->i_segment
+ 1;
390 char *psz_seg
= formatSegmentPath( p_access
, p_access
->psz_path
, i_newseg
, true );
394 fd
= vlc_open( psz_seg
, O_WRONLY
| O_CREAT
| O_LARGEFILE
|
398 msg_Err( p_access
, "cannot open `%s' (%m)", psz_seg
);
403 msg_Dbg( p_access
, "Successfully opened livehttp file: %s (%"PRIu32
")" , psz_seg
, i_newseg
);
406 p_sys
->psz_cursegPath
= psz_seg
;
407 p_sys
->i_handle
= fd
;
408 p_sys
->i_segment
= i_newseg
;
412 /*****************************************************************************
413 * Write: standard write on a file descriptor.
414 *****************************************************************************/
415 static ssize_t
Write( sout_access_out_t
*p_access
, block_t
*p_buffer
)
418 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
422 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
)
424 closeCurrentSegment( p_access
, p_sys
, false );
426 if ( p_buffer
->i_buffer
> 0 && p_sys
->i_handle
< 0 )
428 p_sys
->i_opendts
= p_buffer
->i_dts
;
429 if ( openNextFile( p_access
, p_sys
) < 0 )
432 ssize_t val
= write ( p_sys
->i_handle
,
433 p_buffer
->p_buffer
, p_buffer
->i_buffer
);
436 if ( errno
== EINTR
)
438 block_ChainRelease ( p_buffer
);
442 if ( (size_t)val
>= p_buffer
->i_buffer
)
444 block_t
*p_next
= p_buffer
->p_next
;
445 block_Release (p_buffer
);
450 p_buffer
->p_buffer
+= val
;
451 p_buffer
->i_buffer
-= val
;
458 /*****************************************************************************
459 * Seek: seek to a specific location in a file
460 *****************************************************************************/
461 static int Seek( sout_access_out_t
*p_access
, off_t i_pos
)
464 msg_Err( p_access
, "livehttp sout access cannot seek" );