1 /*****************************************************************************
2 * m3u.c: a meta demux to parse m3u and asx playlists
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: m3u.c,v 1.13 2003/02/27 13:19:43 gbazin Exp $
7 * Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
8 * Gildas Bazin <gbazin@netcourrier.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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
29 #include <string.h> /* strdup() */
33 #include <vlc/input.h>
34 #include <vlc_playlist.h>
36 #include <sys/types.h>
38 /*****************************************************************************
39 * Constants and structures
40 *****************************************************************************/
49 int i_type; /* playlist type (m3u/asx) */
52 /*****************************************************************************
54 *****************************************************************************/
55 static int Activate ( vlc_object_t * );
56 static void Deactivate( vlc_object_t * );
57 static int Demux ( input_thread_t * );
59 /*****************************************************************************
61 *****************************************************************************/
63 set_description( _("m3u/asx metademux") );
64 set_capability( "demux", 10 );
65 set_callbacks( Activate, Deactivate );
66 add_shortcut( "m3u" );
67 add_shortcut( "asx" );
68 add_shortcut( "html" );
71 /*****************************************************************************
72 * Activate: initializes m3u demux structures
73 *****************************************************************************/
74 static int Activate( vlc_object_t * p_this )
76 input_thread_t *p_input = (input_thread_t *)p_this;
81 /* Initialize access plug-in structures. */
82 if( p_input->i_mtu == 0 )
85 p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
88 p_input->pf_demux = Demux;
89 p_input->pf_rewind = NULL;
91 /* Check for m3u/asx file extension */
92 psz_ext = strrchr ( p_input->psz_name, '.' );
95 if( !strcasecmp( psz_ext, ".m3u") ||
96 ( p_input->psz_demux && !strncmp(p_input->psz_demux, "m3u", 3) ) )
100 else if( !strcasecmp( psz_ext, ".asx") ||
101 ( p_input->psz_demux && !strncmp(p_input->psz_demux, "asx", 3) ) )
105 else if( !strcasecmp( psz_ext, ".html") ||
106 ( p_input->psz_demux && !strncmp(p_input->psz_demux, "html", 4) ) )
112 /* we had no luck looking at the file extention, so we have a look
113 * at the content. This is useful for .asp, .php and similar files
114 * that are actually html. Also useful for som asx files that have
115 * another extention */
119 int i_size = input_Peek( p_input, &p_peek, MAX_LINE );
120 i_size -= sizeof("<html>") - 1;
123 && strncasecmp( p_peek, "<html>", sizeof("<html>") - 1 )
124 && strncasecmp( p_peek, "<asx", sizeof("<asx") - 1 ) )
133 else if ( !strncasecmp( p_peek, "<html>", sizeof("<html>") -1 ) )
137 else if ( !strncasecmp( p_peek, "<asx", sizeof("<asx") -1 ) )
144 if( !( p_m3u = malloc( sizeof( demux_sys_t ) ) ) )
146 msg_Err( p_input, "out of memory" );
149 p_input->p_demux_data = p_m3u;
151 p_m3u->i_type = i_type;
156 /*****************************************************************************
157 * Deactivate: frees unused data
158 *****************************************************************************/
159 static void Deactivate( vlc_object_t *p_this )
161 input_thread_t *p_input = (input_thread_t *)p_this;
162 demux_sys_t *p_m3u = (demux_sys_t *)p_input->p_demux_data ;
167 /*****************************************************************************
168 * Demux: reads and demuxes data packets
169 *****************************************************************************
170 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
171 *****************************************************************************/
172 static void ProcessLine ( input_thread_t *p_input , demux_sys_t *p_m3u
173 , playlist_t *p_playlist , char psz_line[MAX_LINE] )
175 char *psz_bol, *psz_name;
179 /* Remove unnecessary tabs or spaces at the beginning of line */
180 while( *psz_bol == ' ' || *psz_bol == '\t' ||
181 *psz_bol == '\n' || *psz_bol == '\r' )
184 if( p_m3u->i_type == TYPE_M3U )
186 /* Check for comment line */
187 if( *psz_bol == '#' )
188 /*line is comment or extended info, ignored for now */
191 else if ( p_m3u->i_type == TYPE_ASX )
193 /* We are dealing with ASX files.
194 * We are looking for "<ref href=" xml markups that
195 * begins with "mms://", "http://" or "file://" */
199 strncasecmp( psz_bol, "ref", sizeof("ref") - 1 ) )
202 if( !*psz_bol ) return;
205 strncasecmp( psz_bol, "href", sizeof("href") - 1 ) )
208 if( !*psz_bol ) return;
211 strncasecmp( psz_bol, "mms://",
212 sizeof("mms://") - 1 ) &&
213 strncasecmp( psz_bol, "http://",
214 sizeof("http://") - 1 ) &&
215 strncasecmp( psz_bol, "file://",
216 sizeof("file://") - 1 ) )
219 if( !*psz_bol ) return;
221 psz_eol = strchr( psz_bol, '"');
229 /* We are dealing with a html file with embedded
230 * video. We are looking for "<param name="filename"
231 * value=" html markups that begin with "http://" */
235 strncasecmp( psz_bol, "param", sizeof("param") - 1 ) )
238 if( !*psz_bol ) return;
241 strncasecmp( psz_bol, "filename", sizeof("filename") - 1 ) )
244 if( !*psz_bol ) return;
247 strncasecmp( psz_bol, "http://",
248 sizeof("http://") - 1 ) )
251 if( !*psz_bol ) return;
253 psz_eol = strchr( psz_bol, '"');
262 if ( !*psz_bol ) return;
265 * From now on, we know we've got a meaningful line
268 /* check for a protocol name */
269 /* for URL, we should look for "://"
270 * for MRL (Media Resource Locator) ([[<access>][/<demux>]:][<source>]),
271 * we should look for ":"
272 * so we end up looking simply for ":"*/
273 /* PB: on some file systems, ':' are valid characters though*/
275 while( *psz_name && *psz_name!=':' )
280 if ( *psz_name && ( psz_name == psz_bol + 1 ) )
282 /* if it is not an URL,
283 * as it is unlikely to be an MRL (PB: if it is ?)
284 * it should be an absolute file name with the drive letter */
285 if ( *(psz_name+1) == '/' )/* "*:/" */
287 if ( *(psz_name+2) != '/' )/* not "*://" */
288 while ( *psz_name ) *psz_name++;/* so now (*psz_name==0) */
290 else while ( *psz_name ) *psz_name++;/* "*:*"*/
294 /* if the line doesn't specify a protocol name,
295 * check if the line has an absolute or relative path */
297 if( !*psz_name && *psz_bol != '/' )
298 /* If this line doesn't begin with a '/' */
303 && *(psz_bol+1)!=':' )
304 /* if this line doesn't begin with
305 * "/" or "\" or "*:" or "*:\" or "*:/" or "\\" */
308 /* assume the path is relative to the path of the m3u file. */
309 char *psz_path = strdup( p_input->psz_name );
312 psz_name = strrchr( psz_path, '/' );
314 psz_name = strrchr( psz_path, '\\' );
315 if ( ! psz_name ) psz_name = strrchr( psz_path, '/' );
317 if( psz_name ) *psz_name = '\0';
318 else *psz_path = '\0';
320 psz_name = malloc( strlen(psz_path) + strlen(psz_bol) + 2 );
321 sprintf( psz_name, "%s/%s", psz_path, psz_bol );
323 if ( *psz_path != '\0' )
325 psz_name = malloc( strlen(psz_path) + strlen(psz_bol) + 2 );
326 sprintf( psz_name, "%s\\%s", psz_path, psz_bol );
328 else psz_name = strdup( psz_bol );
334 psz_name = strdup( psz_bol );
337 playlist_Add( p_playlist, psz_name,
338 PLAYLIST_APPEND, PLAYLIST_END );
343 static int Demux ( input_thread_t *p_input )
345 data_packet_t *p_data;
346 char *p_buf, psz_line[MAX_LINE], eol_tok;
347 int i_size, i_bufpos, i_linepos = 0;
348 playlist_t *p_playlist;
349 vlc_bool_t b_discard = VLC_FALSE;
351 demux_sys_t *p_m3u = (demux_sys_t *)p_input->p_demux_data;
353 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
357 msg_Err( p_input, "can't find playlist" );
361 p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
363 /* Depending on wether we are dealing with an m3u/asf file, the end of
364 * line token will be different */
365 if( p_m3u->i_type == TYPE_ASX || p_m3u->i_type == TYPE_HTML )
370 while( ( i_size = input_SplitBuffer( p_input, &p_data, MAX_LINE ) ) > 0 )
372 i_bufpos = 0; p_buf = p_data->p_payload_start;
376 /* Build a line < MAX_LINE */
377 while( p_buf[i_bufpos] != eol_tok && i_size )
379 if( i_linepos == MAX_LINE || b_discard == VLC_TRUE )
381 /* line is bigger than MAX_LINE, discard it */
383 b_discard = VLC_TRUE;
387 if ( eol_tok != '\n' || p_buf[i_bufpos] != '\r' )
389 psz_line[i_linepos] = p_buf[i_bufpos];
394 i_size--; i_bufpos++;
397 /* Check if we need more data */
398 if( !i_size ) continue;
400 i_size--; i_bufpos++;
401 b_discard = VLC_FALSE;
403 /* Check for empty line */
404 if( !i_linepos ) continue;
406 psz_line[i_linepos] = '\0';
408 ProcessLine ( p_input, p_m3u , p_playlist , psz_line );
411 input_DeletePacket( p_input->p_method_data, p_data );
414 if ( i_linepos && b_discard != VLC_TRUE && eol_tok == '\n' )
416 psz_line[i_linepos] = '\0';
418 ProcessLine ( p_input, p_m3u , p_playlist , psz_line );
421 vlc_object_release( p_playlist );