/***************************************************************************** * ParseText: parse an text subtitle packet and send it to the video output *****************************************************************************/ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; char *psz_subtitle = NULL; /* We cannot display a subpicture with no date */ if( p_block->i_pts <= VLC_TS_INVALID ) { msg_Warn( p_dec, "subtitle without a date" ); return NULL; } /* Check validity of packet data */ /* An "empty" line containing only \0 can be used to force and ephemer picture from the screen */ if( p_block->i_buffer < 1 ) { msg_Warn( p_dec, "no subtitle data" ); return NULL; } /* Should be resiliant against bad subtitles */ psz_subtitle = strndup( (const char *)p_block->p_buffer, p_block->i_buffer ); if( psz_subtitle == NULL ) return NULL; /* USF Subtitles are mandated to be UTF-8 -- make sure it is */ if (EnsureUTF8( psz_subtitle ) == NULL) { msg_Err( p_dec, "USF subtitles must be in UTF-8 format.\n" "This stream contains USF subtitles which aren't." ); } /* Create the subpicture unit */ p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); free( psz_subtitle ); return NULL; } /* Decode USF strings */ p_spu->p_region = ParseUSFString( p_dec, psz_subtitle ); p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; p_spu->i_original_picture_width = p_sys->i_original_width; p_spu->i_original_picture_height = p_sys->i_original_height; free( psz_subtitle ); return p_spu; }
/***************************************************************************** * Demux: The important stuff *****************************************************************************/ static int Demux( demux_t *p_demux ) { char *psz_line; input_item_t *p_input; input_item_t *p_current_input = GetCurrentItem(p_demux); input_item_node_t *p_subitems = input_item_node_Create( p_current_input ); while( (psz_line = stream_ReadLine( p_demux->s )) ) { char **ppsz_options = NULL; int i_options = 0; char *psz_name = NULL; if( !ParseLine( psz_line, &psz_name, &ppsz_options, &i_options ) ) { free( psz_line ); continue; } EnsureUTF8( psz_name ); for( int i = 0; i< i_options; i++ ) EnsureUTF8( ppsz_options[i] ); p_input = input_item_NewExt( "dvb://", psz_name, i_options, (const char**)ppsz_options, VLC_INPUT_OPTION_TRUSTED, -1 ); input_item_node_AppendItem( p_subitems, p_input ); vlc_gc_decref( p_input ); while( i_options-- ) free( ppsz_options[i_options] ); free( ppsz_options ); free( psz_line ); } input_item_node_PostAndDelete( p_subitems ); vlc_gc_decref(p_current_input); return 0; /* Needed for correct operation of go back */ }
static int vlclua_node_add_node( lua_State *L ) { services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L ); input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "node" ); if( *pp_node ) { if( lua_istable( L, -1 ) ) { lua_getfield( L, -1, "title" ); if( lua_isstring( L, -1 ) ) { const char *psz_name = lua_tostring( L, -1 ); input_item_node_t *p_input_node = input_item_node_Create( *pp_node ); input_item_t *p_input = input_item_NewWithType( VLC_OBJECT( p_sd ), "vlc://nop", psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE ); lua_pop( L, 1 ); if( p_input ) { lua_getfield( L, -1, "arturl" ); if( lua_isstring( L, -1 ) && strcmp( lua_tostring( L, -1 ), "" ) ) { char *psz_value = strdup( lua_tostring( L, -1 ) ); EnsureUTF8( psz_value ); msg_Dbg( p_sd, "ArtURL: %s", psz_value ); input_item_SetArtURL( p_input, psz_value ); free( psz_value ); } input_item_node_AppendItem( p_input_node, p_input ); input_item_node_PostAndDelete( p_input_node ); input_item_t **udata = (input_item_t **) lua_newuserdata( L, sizeof( input_item_t * ) ); *udata = p_input; if( luaL_newmetatable( L, "node" ) ) { lua_newtable( L ); luaL_register( L, NULL, vlclua_node_reg ); lua_setfield( L, -2, "__index" ); } lua_setmetatable( L, -2 ); } } else msg_Err( p_sd, "node:add_node: the \"title\" parameter can't be empty" ); } else msg_Err( p_sd, "Error parsing add_node arguments" ); } return 1; }
static int vlclua_sd_add_node( lua_State *L ) { services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L ); if( lua_istable( L, -1 ) ) { lua_getfield( L, -1, "title" ); if( lua_isstring( L, -1 ) ) { const char *psz_name = lua_tostring( L, -1 ); input_item_t *p_input = input_item_NewWithType( "vlc://nop", psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE ); lua_pop( L, 1 ); if( p_input ) { lua_getfield( L, -1, "arturl" ); if( lua_isstring( L, -1 ) && strcmp( lua_tostring( L, -1 ), "" ) ) { char *psz_value = strdup( lua_tostring( L, -1 ) ); EnsureUTF8( psz_value ); msg_Dbg( p_sd, "ArtURL: %s", psz_value ); /** @todo Ask for art download if not local file */ input_item_SetArtURL( p_input, psz_value ); free( psz_value ); } lua_pop( L, 1 ); lua_getfield( L, -1, "category" ); if( lua_isstring( L, -1 ) ) services_discovery_AddItem( p_sd, p_input, luaL_checkstring( L, -1 ) ); else services_discovery_AddItem( p_sd, p_input, NULL ); input_item_t **udata = (input_item_t **) lua_newuserdata( L, sizeof( input_item_t * ) ); *udata = p_input; if( luaL_newmetatable( L, "node" ) ) { lua_newtable( L ); luaL_register( L, NULL, vlclua_node_reg ); lua_setfield( L, -2, "__index" ); } lua_setmetatable( L, -2 ); } } else msg_Err( p_sd, "vlc.sd.add_node: the \"title\" parameter can't be empty" ); } else msg_Err( p_sd, "Error parsing add_node arguments" ); return 1; }
static void test (const char *in, const char *out) { bool isutf8 = !strcmp (in, out); char *str = strdup (in); if (str == NULL) abort (); if (isutf8) printf ("\"%s\" should be accepted...\n", in); else printf ("\"%s\" should be rewritten as \"%s\"...\n", in, out); if ((IsUTF8 (in) != NULL) != isutf8) { printf (" ERROR: IsUTF8 (%s) failed\n", in); exit (1); } if ((EnsureUTF8 (str) != NULL) != isutf8) { printf (" ERROR: EnsureUTF8 (%s) failed\n", in); exit (2); } if (strcmp (str, out)) { printf (" ERROR: got \"%s\"\n", str); exit (3); } if ((EnsureUTF8 (str) == NULL) || IsUTF8 (str) == NULL) { printf (" ERROR: EnsureUTF8 (%s) is not UTF-8\n", in); exit (4); } free (str); }
/***************************************************************************** * Read a line of text. Returns false on error or EOF. *****************************************************************************/ static bool ReadLine( char **ppsz_line, size_t *pi_size, FILE *p_file ) { ssize_t read = getline( ppsz_line, pi_size, p_file ); if( read == -1 ) { /* automatically free buffer on eof */ free( *ppsz_line ); *ppsz_line = NULL; return false; } if( read > 0 && (*ppsz_line)[ read - 1 ] == '\n' ) (*ppsz_line)[ read - 1 ] = '\0'; EnsureUTF8( *ppsz_line ); return true; }
static struct app *AddApp (services_discovery_t *sd, xcb_window_t xid) { services_discovery_sys_t *p_sys = sd->p_sys; char *mrl, *name; if (asprintf (&mrl, "window://0x%"PRIx8, xid) == -1) return NULL; xcb_get_property_reply_t *r = xcb_get_property_reply (p_sys->conn, xcb_get_property (p_sys->conn, 0, xid, p_sys->net_wm_name, 0, 0, 1023 /* max size */), NULL); if (r != NULL) { name = strndup (xcb_get_property_value (r), xcb_get_property_value_length (r)); if (name != NULL) EnsureUTF8 (name); /* don't trust third party apps too much ;-) */ free (r); } /* TODO: use WM_NAME (Latin-1) for very old apps */ else name = NULL; input_item_t *item = input_item_NewCard (mrl, name ? name : mrl); /* FIXME */ free (mrl); free (name); if (item == NULL) return NULL; struct app *app = malloc (sizeof (*app)); if (app == NULL) { input_item_Release (item); return NULL; } app->xid = xid; app->item = item; app->owner = sd; services_discovery_AddSubItem(sd, p_sys->apps_root, item); return app; }
/** * Decode an encoded URI component in place. * <b>This function does NOT decode entire URIs.</b> * It decodes components (e.g. host name, directory, file name). * Decoded URIs do not exist in the real world (see RFC3986 §2.4). * Complete URIs are always "encoded" (or they are syntaxically invalid). * * Note that URI encoding is different from Javascript escaping. Especially, * white spaces and Unicode non-ASCII code points are encoded differently. * * \return psz on success, NULL if it was not properly encoded */ char *decode_URI( char *psz ) { unsigned char *in = (unsigned char *)psz, *out = in, c; if( psz == NULL ) return NULL; while( ( c = *in++ ) != '\0' ) { switch( c ) { case '%': { char hex[3]; if( ( ( hex[0] = *in++ ) == 0 ) || ( ( hex[1] = *in++ ) == 0 ) ) return NULL; hex[2] = '\0'; *out++ = (unsigned char)strtoul( hex, NULL, 0x10 ); break; } case '+': /* This is HTTP forms, not URI decoding... */ *out++ = ' '; break; default: /* Inserting non-ASCII or non-printable characters is unsafe, * and no sane browser will send these unencoded */ if( ( c < 32 ) || ( c > 127 ) ) *out++ = '?'; else *out++ = c; } } *out = '\0'; EnsureUTF8( psz ); return psz; }
/** * Main demux callback function * @param p_demux: this demux object */ static int Demux( demux_t *p_demux ) { char *psz_line; char *psz_artist = NULL, *psz_album = NULL, *psz_genre = NULL, *psz_year = NULL; char *psz_author = NULL, *psz_title = NULL, *psz_copyright = NULL, *psz_cdnum = NULL, *psz_comments = NULL; mtime_t i_duration = -1; const char **ppsz_options = NULL; int i_options = 0, i_start = 0, i_stop = 0; bool b_cleanup = false; input_item_t *p_input; input_item_t *p_current_input = GetCurrentItem(p_demux); input_item_node_t *p_subitems = input_item_node_Create( p_current_input ); psz_line = vlc_stream_ReadLine( p_demux->s ); while( psz_line ) { char *psz_parse = psz_line; /* Skip leading tabs and spaces */ while( *psz_parse == ' ' || *psz_parse == '\t' || *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++; if( *psz_parse == '#' ) { /* Ignore comments */ } else if( *psz_parse ) { char *psz_mrl, *psz_option_next, *psz_option; char *psz_param, *psz_value; /* Get the MRL from the file. Note that this might contain parameters of form ?param1=value1¶m2=value2 in a RAM file */ psz_mrl = ProcessMRL( psz_parse, p_demux->p_sys->psz_prefix ); b_cleanup = true; if ( !psz_mrl ) goto error; /* We have the MRL, now we have to check for options and parse them from MRL */ psz_option = strchr( psz_mrl, '?' ); /* Look for start of options */ if( psz_option ) { /* Remove options from MRL because VLC can't get the file otherwise */ *psz_option = '\0'; psz_option++; psz_option_next = psz_option; while( 1 ) /* Process each option */ { /* Look for end of first option which maybe a & or \0 */ psz_option = psz_option_next; psz_option_next = strchr( psz_option, '&' ); if( psz_option_next ) { *psz_option_next = '\0'; psz_option_next++; } else psz_option_next = strchr( psz_option, '\0' ); /* Quit if options are over */ if( psz_option_next == psz_option ) break; /* Parse out param and value */ psz_param = psz_option; psz_value = strchr( psz_option, '=' ); if( psz_value == NULL ) break; *psz_value = '\0'; psz_value++; /* Take action based on parameter value in the below if else structure */ /* TODO: Remove any quotes surrounding values if required */ if( !strcmp( psz_param, "clipinfo" ) ) { ParseClipInfo( psz_value, &psz_artist, &psz_title, &psz_album, &psz_genre, &psz_year, &psz_cdnum, &psz_comments ); /* clipinfo has various sub parameters, which is parsed by this function */ } else if( !strcmp( psz_param, "author" ) ) { psz_author = vlc_uri_decode_duplicate(psz_value); EnsureUTF8( psz_author ); } else if( !strcmp( psz_param, "start" ) && strncmp( psz_mrl, "rtsp", 4 ) /* Our rtsp-real or our real demuxer is wrong */ ) { i_start = ParseTime( psz_value, strlen( psz_value ) ); char *temp; if( i_start ) { if( asprintf( &temp, ":start-time=%d", i_start ) != -1 ) INSERT_ELEM( ppsz_options, i_options, i_options, temp ); } } else if( !strcmp( psz_param, "end" ) ) { i_stop = ParseTime( psz_value, strlen( psz_value ) ); char *temp; if( i_stop ) { if( asprintf( &temp, ":stop-time=%d", i_stop ) != -1 ) INSERT_ELEM( ppsz_options, i_options, i_options, temp ); } } else if( !strcmp( psz_param, "title" ) ) { free( psz_title ); psz_title = vlc_uri_decode_duplicate(psz_value); EnsureUTF8( psz_title ); } else if( !strcmp( psz_param, "copyright" ) ) { psz_copyright = vlc_uri_decode_duplicate(psz_value); EnsureUTF8( psz_copyright ); } else { /* TODO: insert option anyway? Currently ignores*/ /* INSERT_ELEM( ppsz_options, i_options, i_options, psz_option ); */ } } } /* Create the input item and pump in all the options into playlist item */ p_input = input_item_NewExt( psz_mrl, psz_title, i_duration, ITEM_TYPE_UNKNOWN, ITEM_NET_UNKNOWN ); if( !p_input ) { free( psz_mrl ); goto error; } input_item_AddOptions( p_input, i_options, ppsz_options, 0 ); if( !EMPTY_STR( psz_artist ) ) input_item_SetArtist( p_input, psz_artist ); if( !EMPTY_STR( psz_author ) ) input_item_SetPublisher( p_input, psz_author ); if( !EMPTY_STR( psz_title ) ) input_item_SetTitle( p_input, psz_title ); if( !EMPTY_STR( psz_copyright ) ) input_item_SetCopyright( p_input, psz_copyright ); if( !EMPTY_STR( psz_album ) ) input_item_SetAlbum( p_input, psz_album ); if( !EMPTY_STR( psz_genre ) ) input_item_SetGenre( p_input, psz_genre ); if( !EMPTY_STR( psz_year ) ) input_item_SetDate( p_input, psz_year ); if( !EMPTY_STR( psz_cdnum ) ) input_item_SetTrackNum( p_input, psz_cdnum ); if( !EMPTY_STR( psz_comments ) ) input_item_SetDescription( p_input, psz_comments ); input_item_node_AppendItem( p_subitems, p_input ); vlc_gc_decref( p_input ); free( psz_mrl ); } error: /* Fetch another line */ free( psz_line ); psz_line = vlc_stream_ReadLine( p_demux->s ); if( !psz_line ) b_cleanup = true; if( b_cleanup ) { /* Cleanup state */ while( i_options-- ) free( (char*)ppsz_options[i_options] ); FREENULL( ppsz_options ); FREENULL( psz_artist ); FREENULL( psz_title ); FREENULL( psz_author ); FREENULL( psz_copyright ); FREENULL( psz_album ); FREENULL( psz_genre ); FREENULL( psz_year ); FREENULL( psz_cdnum ); FREENULL( psz_comments ); i_options = 0; i_duration = -1; i_start = 0; i_stop = 0; b_cleanup = false; } } input_item_node_PostAndDelete( p_subitems ); vlc_gc_decref(p_current_input); var_Destroy( p_demux, "m3u-extvlcopt" ); return 0; /* Needed for correct operation of go back */ }
/***************************************************************************** * ParseText: parse an text subtitle packet and send it to the video output *****************************************************************************/ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; char *psz_subtitle = NULL; if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) return NULL; /* We cannot display a subpicture with no date */ if( p_block->i_pts <= VLC_TS_INVALID ) { msg_Warn( p_dec, "subtitle without a date" ); return NULL; } /* Check validity of packet data */ /* An "empty" line containing only \0 can be used to force and ephemer picture from the screen */ if( p_block->i_buffer < 1 ) { msg_Warn( p_dec, "no subtitle data" ); return NULL; } /* Should be resiliant against bad subtitles */ psz_subtitle = malloc( p_block->i_buffer + 1 ); if( psz_subtitle == NULL ) return NULL; memcpy( psz_subtitle, p_block->p_buffer, p_block->i_buffer ); psz_subtitle[p_block->i_buffer] = '\0'; if( p_sys->iconv_handle == (vlc_iconv_t)-1 ) { if (EnsureUTF8( psz_subtitle ) == NULL) { msg_Err( p_dec, "failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file." ); } } else { if( p_sys->b_autodetect_utf8 ) { if( IsUTF8( psz_subtitle ) == NULL ) { msg_Dbg( p_dec, "invalid UTF-8 sequence: " "disabling UTF-8 subtitles autodetection" ); p_sys->b_autodetect_utf8 = false; } } if( !p_sys->b_autodetect_utf8 ) { size_t inbytes_left = strlen( psz_subtitle ); size_t outbytes_left = 6 * inbytes_left; char *psz_new_subtitle = xmalloc( outbytes_left + 1 ); char *psz_convert_buffer_out = psz_new_subtitle; const char *psz_convert_buffer_in = psz_subtitle; size_t ret = vlc_iconv( p_sys->iconv_handle, &psz_convert_buffer_in, &inbytes_left, &psz_convert_buffer_out, &outbytes_left ); *psz_convert_buffer_out++ = '\0'; free( psz_subtitle ); if( ( ret == (size_t)(-1) ) || inbytes_left ) { free( psz_new_subtitle ); msg_Err( p_dec, "failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file." ); return NULL; } psz_subtitle = realloc( psz_new_subtitle, psz_convert_buffer_out - psz_new_subtitle ); if( !psz_subtitle ) psz_subtitle = psz_new_subtitle; } } /* Create the subpicture unit */ p_spu = decoder_NewSubpictureText( p_dec ); if( !p_spu ) { free( psz_subtitle ); return NULL; } p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; p_spu_sys->region.inner_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align; p_spu_sys->region.p_segments = ParseSubtitles( &p_spu_sys->region.inner_align, psz_subtitle ); free( psz_subtitle ); return p_spu; }
void input_item_SetURI( input_item_t *p_i, const char *psz_uri ) { assert( psz_uri ); #ifndef NDEBUG if( !strstr( psz_uri, "://" ) || strchr( psz_uri, ' ' ) || strchr( psz_uri, '"' ) ) fprintf( stderr, "Warning: %s(\"%s\"): file path instead of URL.\n", __func__, psz_uri ); #endif vlc_mutex_lock( &p_i->lock ); free( p_i->psz_uri ); p_i->psz_uri = strdup( psz_uri ); p_i->i_type = GuessType( p_i ); if( p_i->psz_name ) ; else if( p_i->i_type == ITEM_TYPE_FILE || p_i->i_type == ITEM_TYPE_DIRECTORY ) { const char *psz_filename = strrchr( p_i->psz_uri, '/' ); if( psz_filename && *psz_filename == '/' ) psz_filename++; if( psz_filename && *psz_filename ) p_i->psz_name = strdup( psz_filename ); /* Make the name more readable */ if( p_i->psz_name ) { decode_URI( p_i->psz_name ); EnsureUTF8( p_i->psz_name ); } } else { /* Strip login and password from title */ int r; vlc_url_t url; vlc_UrlParse( &url, psz_uri, 0 ); if( url.psz_protocol ) { if( url.i_port > 0 ) r=asprintf( &p_i->psz_name, "%s://%s:%d%s", url.psz_protocol, url.psz_host, url.i_port, url.psz_path ? url.psz_path : "" ); else r=asprintf( &p_i->psz_name, "%s://%s%s", url.psz_protocol, url.psz_host ? url.psz_host : "", url.psz_path ? url.psz_path : "" ); } else { if( url.i_port > 0 ) r=asprintf( &p_i->psz_name, "%s:%d%s", url.psz_host, url.i_port, url.psz_path ? url.psz_path : "" ); else r=asprintf( &p_i->psz_name, "%s%s", url.psz_host, url.psz_path ? url.psz_path : "" ); } vlc_UrlClean( &url ); if( -1==r ) p_i->psz_name=NULL; /* recover from undefined value */ } vlc_mutex_unlock( &p_i->lock ); }
void vorbis_ParseComment( es_format_t *p_fmt, vlc_meta_t **pp_meta, const uint8_t *p_data, int i_data, int *i_attachments, input_attachment_t ***attachments, int *i_cover_score, int *i_cover_idx, int *i_seekpoint, seekpoint_t ***ppp_seekpoint, float (* ppf_replay_gain)[AUDIO_REPLAY_GAIN_MAX], float (* ppf_replay_peak)[AUDIO_REPLAY_GAIN_MAX] ) { int n; int i_comment; if( i_data < 8 ) return; n = GetDWLE(p_data); RM(4); if( n < 0 || n > i_data ) return; #if 0 if( n > 0 ) { /* TODO report vendor string ? */ char *psz_vendor = psz_vendor = strndup( p_data, n ); free( psz_vendor ); } #endif RM(n); if( i_data < 4 ) return; i_comment = GetDWLE(p_data); RM(4); if( i_comment <= 0 ) return; /* */ vlc_meta_t *p_meta = *pp_meta; if( !p_meta ) *pp_meta = p_meta = vlc_meta_New(); if( !p_meta ) return; /* */ bool hasTitle = false; bool hasArtist = false; bool hasGenre = false; bool hasCopyright = false; bool hasAlbum = false; bool hasTrackNum = false; bool hasDescription = false; bool hasRating = false; bool hasDate = false; bool hasLanguage = false; bool hasPublisher = false; bool hasEncodedBy = false; bool hasTrackTotal = false; chapters_array_t chapters_array = { 0, NULL }; for( ; i_comment > 0; i_comment-- ) { char *psz_comment; if( i_data < 4 ) break; n = GetDWLE(p_data); RM(4); if( n > i_data ) break; if( n <= 0 ) continue; psz_comment = strndup( (const char*)p_data, n ); RM(n); EnsureUTF8( psz_comment ); #define IF_EXTRACT(txt,var) \ if( !strncasecmp(psz_comment, txt, strlen(txt)) ) \ { \ const char *oldval = vlc_meta_Get( p_meta, vlc_meta_ ## var ); \ if( oldval && has##var) \ { \ char * newval; \ if( asprintf( &newval, "%s,%s", oldval, &psz_comment[strlen(txt)] ) == -1 ) \ newval = NULL; \ vlc_meta_Set( p_meta, vlc_meta_ ## var, newval ); \ free( newval ); \ } \ else \ vlc_meta_Set( p_meta, vlc_meta_ ## var, &psz_comment[strlen(txt)] ); \ has##var = true; \ } #define IF_EXTRACT_ONCE(txt,var) \ if( !strncasecmp(psz_comment, txt, strlen(txt)) && !has##var ) \ { \ vlc_meta_Set( p_meta, vlc_meta_ ## var, &psz_comment[strlen(txt)] ); \ has##var = true; \ } #define IF_EXTRACT_FMT(txt,var,fmt,target) \ IF_EXTRACT(txt,var)\ if( fmt && !strncasecmp(psz_comment, txt, strlen(txt)) )\ {\ if ( fmt->target ) free( fmt->target );\ fmt->target = strdup(&psz_comment[strlen(txt)]);\ } IF_EXTRACT("TITLE=", Title ) else IF_EXTRACT("ARTIST=", Artist ) else IF_EXTRACT("GENRE=", Genre ) else IF_EXTRACT("COPYRIGHT=", Copyright ) else IF_EXTRACT("ALBUM=", Album ) else if( !hasTrackNum && !strncasecmp(psz_comment, "TRACKNUMBER=", strlen("TRACKNUMBER=" ) ) ) { /* Yeah yeah, such a clever idea, let's put xx/xx inside TRACKNUMBER * Oh, and let's not use TRACKTOTAL or TOTALTRACKS... */ short unsigned u_track, u_total; if( sscanf( &psz_comment[strlen("TRACKNUMBER=")], "%hu/%hu", &u_track, &u_total ) == 2 ) { char str[6]; snprintf(str, 6, "%u", u_track); vlc_meta_Set( p_meta, vlc_meta_TrackNumber, str ); hasTrackNum = true; snprintf(str, 6, "%u", u_total); vlc_meta_Set( p_meta, vlc_meta_TrackTotal, str ); hasTrackTotal = true; } else { vlc_meta_Set( p_meta, vlc_meta_TrackNumber, &psz_comment[strlen("TRACKNUMBER=")] ); hasTrackNum = true; } } else IF_EXTRACT_ONCE("TRACKTOTAL=", TrackTotal ) else IF_EXTRACT_ONCE("TOTALTRACKS=", TrackTotal ) else IF_EXTRACT("DESCRIPTION=", Description ) else IF_EXTRACT("COMMENT=", Description ) else IF_EXTRACT("COMMENTS=", Description ) else IF_EXTRACT("RATING=", Rating ) else IF_EXTRACT("DATE=", Date ) else IF_EXTRACT_FMT("LANGUAGE=", Language, p_fmt, psz_language ) else IF_EXTRACT("ORGANIZATION=", Publisher ) else IF_EXTRACT("ENCODER=", EncodedBy ) else if( !strncasecmp( psz_comment, "METADATA_BLOCK_PICTURE=", strlen("METADATA_BLOCK_PICTURE="))) { if( attachments == NULL ) continue; uint8_t *p_picture; size_t i_size = vlc_b64_decode_binary( &p_picture, &psz_comment[strlen("METADATA_BLOCK_PICTURE=")]); input_attachment_t *p_attachment = ParseFlacPicture( p_picture, i_size, *i_attachments, i_cover_score, i_cover_idx ); free( p_picture ); if( p_attachment ) { TAB_APPEND_CAST( (input_attachment_t**), *i_attachments, *attachments, p_attachment ); } } else if ( ppf_replay_gain && ppf_replay_peak && !strncmp(psz_comment, "REPLAYGAIN_", 11) ) { char *p = strchr( psz_comment, '=' ); if (!p) continue; if ( !strncasecmp(psz_comment, "REPLAYGAIN_TRACK_GAIN=", 22) ) { (*ppf_replay_gain)[AUDIO_REPLAY_GAIN_TRACK] = us_atof( ++p ); } else if ( !strncasecmp(psz_comment, "REPLAYGAIN_ALBUM_GAIN=", 22) ) { (*ppf_replay_gain)[AUDIO_REPLAY_GAIN_ALBUM] = us_atof( ++p ); } else if ( !strncasecmp(psz_comment, "REPLAYGAIN_ALBUM_PEAK=", 22) ) { (*ppf_replay_peak)[AUDIO_REPLAY_GAIN_ALBUM] = us_atof( ++p ); } else if ( !strncasecmp(psz_comment, "REPLAYGAIN_TRACK_PEAK=", 22) ) { (*ppf_replay_peak)[AUDIO_REPLAY_GAIN_TRACK] = us_atof( ++p ); } } else if( !strncasecmp(psz_comment, "CHAPTER", 7) ) { unsigned int i_chapt; seekpoint_t *p_seekpoint = NULL; for( int i = 0; psz_comment[i] && psz_comment[i] != '='; i++ ) if( psz_comment[i] >= 'a' && psz_comment[i] <= 'z' ) psz_comment[i] -= 'a' - 'A'; if( strstr( psz_comment, "NAME=" ) && sscanf( psz_comment, "CHAPTER%uNAME=", &i_chapt ) == 1 ) { char *p = strchr( psz_comment, '=' ); p_seekpoint = getChapterEntry( i_chapt, &chapters_array ); if ( !p || ! p_seekpoint ) continue; if ( ! p_seekpoint->psz_name ) p_seekpoint->psz_name = strdup( ++p ); } else if( sscanf( psz_comment, "CHAPTER%u=", &i_chapt ) == 1 ) { unsigned int h, m, s, ms; char *p = strchr( psz_comment, '=' ); if( p && sscanf( ++p, "%u:%u:%u.%u", &h, &m, &s, &ms ) == 4 ) { p_seekpoint = getChapterEntry( i_chapt, &chapters_array ); if ( ! p_seekpoint ) continue; p_seekpoint->i_time_offset = (((int64_t)h * 3600 + (int64_t)m * 60 + (int64_t)s) * 1000 + ms) * 1000; } } } else if( strchr( psz_comment, '=' ) ) { /* generic (PERFORMER/LICENSE/ORGANIZATION/LOCATION/CONTACT/ISRC, * undocumented tags and replay gain ) */ char *p = strchr( psz_comment, '=' ); *p++ = '\0'; for( int i = 0; psz_comment[i]; i++ ) if( psz_comment[i] >= 'a' && psz_comment[i] <= 'z' ) psz_comment[i] -= 'a' - 'A'; vlc_meta_AddExtra( p_meta, psz_comment, p ); } #undef IF_EXTRACT free( psz_comment ); }
input_attachment_t* ParseFlacPicture( const uint8_t *p_data, int i_data, int i_attachments, int *i_cover_score, int *i_cover_idx ) { /* TODO: Merge with ID3v2 copy in modules/meta_engine/taglib.cpp. */ static const char pi_cover_score[] = { 0, /* Other */ 5, /* 32x32 PNG image that should be used as the file icon */ 4, /* File icon of a different size or format. */ 20, /* Front cover image of the album. */ 19, /* Back cover image of the album. */ 13, /* Inside leaflet page of the album. */ 18, /* Image from the album itself. */ 17, /* Picture of the lead artist or soloist. */ 16, /* Picture of the artist or performer. */ 14, /* Picture of the conductor. */ 15, /* Picture of the band or orchestra. */ 9, /* Picture of the composer. */ 8, /* Picture of the lyricist or text writer. */ 7, /* Picture of the recording location or studio. */ 10, /* Picture of the artists during recording. */ 11, /* Picture of the artists during performance. */ 6, /* Picture from a movie or video related to the track. */ 1, /* Picture of a large, coloured fish. */ 12, /* Illustration related to the track. */ 3, /* Logo of the band or performer. */ 2 /* Logo of the publisher (record company). */ }; int i_len; int i_type; char *psz_mime = NULL; char psz_name[128]; char *psz_description = NULL; input_attachment_t *p_attachment = NULL; if( i_data < 4 + 3*4 ) return NULL; #define RM(x) do { i_data -= (x); p_data += (x); } while(0) i_type = GetDWBE( p_data ); RM(4); i_len = GetDWBE( p_data ); RM(4); if( i_len < 0 || i_data < i_len + 4 ) goto error; psz_mime = strndup( (const char*)p_data, i_len ); RM(i_len); i_len = GetDWBE( p_data ); RM(4); if( i_len < 0 || i_data < i_len + 4*4 + 4) goto error; psz_description = strndup( (const char*)p_data, i_len ); RM(i_len); EnsureUTF8( psz_description ); RM(4*4); i_len = GetDWBE( p_data ); RM(4); if( i_len < 0 || i_len > i_data ) goto error; /* printf( "Picture type=%d mime=%s description='%s' file length=%d\n", i_type, psz_mime, psz_description, i_len ); */ snprintf( psz_name, sizeof(psz_name), "picture%d", i_attachments ); if( !strcasecmp( psz_mime, "image/jpeg" ) ) strcat( psz_name, ".jpg" ); else if( !strcasecmp( psz_mime, "image/png" ) ) strcat( psz_name, ".png" ); p_attachment = vlc_input_attachment_New( psz_name, psz_mime, psz_description, p_data, i_data ); if( i_type >= 0 && (unsigned int)i_type < sizeof(pi_cover_score)/sizeof(pi_cover_score[0]) && *i_cover_score < pi_cover_score[i_type] ) { *i_cover_idx = i_attachments; *i_cover_score = pi_cover_score[i_type]; } error: free( psz_mime ); free( psz_description ); return p_attachment; }
int OpenDemux( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; AVProbeData pd = { }; AVInputFormat *fmt = NULL; int64_t i_start_time = -1; bool b_can_seek; char *psz_url; const uint8_t *peek; int error; /* Init Probe data */ pd.buf_size = vlc_stream_Peek( p_demux->s, &peek, 2048 + 213 ); if( pd.buf_size <= 0 ) { msg_Warn( p_demux, "cannot peek" ); return VLC_EGENERIC; } pd.buf = malloc( pd.buf_size + AVPROBE_PADDING_SIZE ); if( unlikely(pd.buf == NULL) ) return VLC_ENOMEM; memcpy( pd.buf, peek, pd.buf_size ); memset( pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE ); if( p_demux->psz_file ) psz_url = strdup( p_demux->psz_file ); else { if( asprintf( &psz_url, "%s://%s", p_demux->psz_access, p_demux->psz_location ) == -1) psz_url = NULL; } if( psz_url != NULL ) msg_Dbg( p_demux, "trying url: %s", psz_url ); pd.filename = psz_url; vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek ); vlc_init_avformat(p_this); /* Guess format */ char *psz_format = var_InheritString( p_this, "avformat-format" ); if( psz_format ) { if( (fmt = av_find_input_format(psz_format)) ) msg_Dbg( p_demux, "forcing format: %s", fmt->name ); free( psz_format ); } if( fmt == NULL ) fmt = av_probe_input_format( &pd, 1 ); free( pd.buf ); if( fmt == NULL ) { msg_Dbg( p_demux, "couldn't guess format" ); free( psz_url ); return VLC_EGENERIC; } if( !p_demux->obj.force ) { static const char ppsz_blacklist[][16] = { /* Don't handle MPEG unless forced */ "mpeg", "vcd", "vob", "mpegts", /* libavformat's redirector won't work */ "redir", "sdp", /* Don't handle subtitles format */ "ass", "srt", "microdvd", /* No timestamps at all */ "hevc", "h264", "" }; for( int i = 0; *ppsz_blacklist[i]; i++ ) { if( !strcmp( fmt->name, ppsz_blacklist[i] ) ) { free( psz_url ); return VLC_EGENERIC; } } } /* Don't trigger false alarms on bin files */ if( !p_demux->obj.force && !strcmp( fmt->name, "psxstr" ) ) { int i_len; if( !p_demux->psz_file ) { free( psz_url ); return VLC_EGENERIC; } i_len = strlen( p_demux->psz_file ); if( i_len < 4 ) { free( psz_url ); return VLC_EGENERIC; } if( strcasecmp( &p_demux->psz_file[i_len - 4], ".str" ) && strcasecmp( &p_demux->psz_file[i_len - 4], ".xai" ) && strcasecmp( &p_demux->psz_file[i_len - 3], ".xa" ) ) { free( psz_url ); return VLC_EGENERIC; } } msg_Dbg( p_demux, "detected format: %s", fmt->name ); /* Fill p_demux fields */ p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys = xmalloc( sizeof( demux_sys_t ) ); p_sys->ic = 0; p_sys->fmt = fmt; p_sys->i_tk = 0; p_sys->tk = NULL; p_sys->tk_pcr = NULL; p_sys->i_ssa_order = 0; TAB_INIT( p_sys->i_attachments, p_sys->attachments); p_sys->p_title = NULL; /* Create I/O wrapper */ unsigned char * p_io_buffer = av_malloc( AVFORMAT_IOBUFFER_SIZE ); if( !p_io_buffer ) { free( psz_url ); CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->ic = avformat_alloc_context(); if( !p_sys->ic ) { av_free( p_io_buffer ); free( psz_url ); CloseDemux( p_this ); return VLC_ENOMEM; } AVIOContext *pb = p_sys->ic->pb = avio_alloc_context( p_io_buffer, AVFORMAT_IOBUFFER_SIZE, 0, p_demux, IORead, NULL, IOSeek ); if( !pb ) { av_free( p_io_buffer ); free( psz_url ); CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->ic->pb->seekable = b_can_seek ? AVIO_SEEKABLE_NORMAL : 0; error = avformat_open_input(&p_sys->ic, psz_url, p_sys->fmt, NULL); if( error < 0 ) { msg_Err( p_demux, "Could not open %s: %s", psz_url, vlc_strerror_c(AVUNERROR(error)) ); av_free( p_io_buffer ); av_free( pb ); p_sys->ic = NULL; free( psz_url ); CloseDemux( p_this ); return VLC_EGENERIC; } free( psz_url ); char *psz_opts = var_InheritString( p_demux, "avformat-options" ); AVDictionary *options[p_sys->ic->nb_streams ? p_sys->ic->nb_streams : 1]; options[0] = NULL; unsigned int nb_streams = p_sys->ic->nb_streams; for (unsigned i = 1; i < nb_streams; i++) options[i] = NULL; if (psz_opts) { vlc_av_get_options(psz_opts, &options[0]); for (unsigned i = 1; i < nb_streams; i++) { av_dict_copy(&options[i], options[0], 0); } free(psz_opts); } vlc_avcodec_lock(); /* avformat calls avcodec behind our back!!! */ error = avformat_find_stream_info( p_sys->ic, options ); /* FIXME: what if nb_streams change after that call? */ vlc_avcodec_unlock(); AVDictionaryEntry *t = NULL; while ((t = av_dict_get(options[0], "", t, AV_DICT_IGNORE_SUFFIX))) { msg_Err( p_demux, "Unknown option \"%s\"", t->key ); } av_dict_free(&options[0]); for (unsigned i = 1; i < nb_streams; i++) { av_dict_free(&options[i]); } if( error < 0 ) { msg_Warn( p_demux, "Could not find stream info: %s", vlc_strerror_c(AVUNERROR(error)) ); } for( unsigned i = 0; i < p_sys->ic->nb_streams; i++ ) { AVStream *s = p_sys->ic->streams[i]; const AVCodecParameters *cp = s->codecpar; es_out_id_t *es = NULL; es_format_t es_fmt; const char *psz_type = "unknown"; /* Do not use the cover art as a stream */ if( s->disposition == AV_DISPOSITION_ATTACHED_PIC ) { TAB_APPEND( p_sys->i_tk, p_sys->tk, NULL ); continue; } vlc_fourcc_t fcc = GetVlcFourcc( cp->codec_id ); switch( cp->codec_type ) { case AVMEDIA_TYPE_AUDIO: es_format_Init( &es_fmt, AUDIO_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); es_fmt.i_bitrate = cp->bit_rate; es_fmt.audio.i_channels = cp->channels; es_fmt.audio.i_rate = cp->sample_rate; es_fmt.audio.i_bitspersample = cp->bits_per_coded_sample; es_fmt.audio.i_blockalign = cp->block_align; psz_type = "audio"; if(cp->codec_id == AV_CODEC_ID_AAC_LATM) { es_fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M'); es_fmt.b_packetized = false; } else if(cp->codec_id == AV_CODEC_ID_AAC && strstr(p_sys->fmt->long_name, "raw ADTS AAC")) { es_fmt.i_original_fourcc = VLC_FOURCC('A','D','T','S'); es_fmt.b_packetized = false; } break; case AVMEDIA_TYPE_VIDEO: es_format_Init( &es_fmt, VIDEO_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); es_fmt.video.i_bits_per_pixel = cp->bits_per_coded_sample; /* Special case for raw video data */ if( cp->codec_id == AV_CODEC_ID_RAWVIDEO ) { msg_Dbg( p_demux, "raw video, pixel format: %i", cp->format ); if( GetVlcChroma( &es_fmt.video, cp->format ) != VLC_SUCCESS) { msg_Err( p_demux, "was unable to find a FourCC match for raw video" ); } else es_fmt.i_codec = es_fmt.video.i_chroma; } /* We need this for the h264 packetizer */ else if( cp->codec_id == AV_CODEC_ID_H264 && ( p_sys->fmt == av_find_input_format("flv") || p_sys->fmt == av_find_input_format("matroska") || p_sys->fmt == av_find_input_format("mp4") ) ) es_fmt.i_original_fourcc = VLC_FOURCC( 'a', 'v', 'c', '1' ); es_fmt.video.i_width = cp->width; es_fmt.video.i_height = cp->height; es_fmt.video.i_visible_width = es_fmt.video.i_width; es_fmt.video.i_visible_height = es_fmt.video.i_height; get_rotation(&es_fmt, s); # warning FIXME: implement palette transmission psz_type = "video"; es_fmt.video.i_frame_rate = s->codec->time_base.num; es_fmt.video.i_frame_rate_base = s->codec->time_base.den * __MAX( s->codec->ticks_per_frame, 1 ); es_fmt.video.i_sar_num = s->sample_aspect_ratio.num; if (s->sample_aspect_ratio.num > 0) es_fmt.video.i_sar_den = s->sample_aspect_ratio.den; else es_fmt.video.i_sar_den = 0; break; case AVMEDIA_TYPE_SUBTITLE: es_format_Init( &es_fmt, SPU_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); if( strncmp( p_sys->ic->iformat->name, "matroska", 8 ) == 0 && cp->codec_id == AV_CODEC_ID_DVD_SUBTITLE && cp->extradata != NULL && cp->extradata_size > 0 ) { char *psz_start; char *psz_buf = malloc( cp->extradata_size + 1); if( psz_buf != NULL ) { memcpy( psz_buf, cp->extradata , cp->extradata_size ); psz_buf[cp->extradata_size] = '\0'; psz_start = strstr( psz_buf, "size:" ); if( psz_start && vobsub_size_parse( psz_start, &es_fmt.subs.spu.i_original_frame_width, &es_fmt.subs.spu.i_original_frame_height ) == VLC_SUCCESS ) { msg_Dbg( p_demux, "original frame size: %dx%d", es_fmt.subs.spu.i_original_frame_width, es_fmt.subs.spu.i_original_frame_height ); } else { msg_Warn( p_demux, "reading original frame size failed" ); } psz_start = strstr( psz_buf, "palette:" ); if( psz_start && vobsub_palette_parse( psz_start, &es_fmt.subs.spu.palette[1] ) == VLC_SUCCESS ) { es_fmt.subs.spu.palette[0] = SPU_PALETTE_DEFINED; msg_Dbg( p_demux, "vobsub palette read" ); } else { msg_Warn( p_demux, "reading original palette failed" ); } free( psz_buf ); } } psz_type = "subtitle"; break; default: es_format_Init( &es_fmt, UNKNOWN_ES, 0 ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT if( cp->codec_type == AVMEDIA_TYPE_ATTACHMENT ) { input_attachment_t *p_attachment; psz_type = "attachment"; if( cp->codec_id == AV_CODEC_ID_TTF ) { AVDictionaryEntry *filename = av_dict_get( s->metadata, "filename", NULL, 0 ); if( filename && filename->value ) { p_attachment = vlc_input_attachment_New( filename->value, "application/x-truetype-font", NULL, cp->extradata, (int)cp->extradata_size ); if( p_attachment ) TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment ); } } else msg_Warn( p_demux, "unsupported attachment type (%u) in avformat demux", cp->codec_id ); } else #endif { if( cp->codec_type == AVMEDIA_TYPE_DATA ) psz_type = "data"; msg_Warn( p_demux, "unsupported track type (%u:%u) in avformat demux", cp->codec_type, cp->codec_id ); } break; } AVDictionaryEntry *language = av_dict_get( s->metadata, "language", NULL, 0 ); if ( language && language->value ) es_fmt.psz_language = strdup( language->value ); if( s->disposition & AV_DISPOSITION_DEFAULT ) es_fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1000; #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT if( cp->codec_type != AVMEDIA_TYPE_ATTACHMENT ) #endif if( cp->codec_type != AVMEDIA_TYPE_DATA ) { const bool b_ogg = !strcmp( p_sys->fmt->name, "ogg" ); const uint8_t *p_extra = cp->extradata; unsigned i_extra = cp->extradata_size; if( cp->codec_id == AV_CODEC_ID_THEORA && b_ogg ) { unsigned pi_size[3]; const void *pp_data[3]; unsigned i_count; for( i_count = 0; i_count < 3; i_count++ ) { if( i_extra < 2 ) break; pi_size[i_count] = GetWBE( p_extra ); pp_data[i_count] = &p_extra[2]; if( i_extra < pi_size[i_count] + 2 ) break; p_extra += 2 + pi_size[i_count]; i_extra -= 2 + pi_size[i_count]; } if( i_count > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, i_count ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->codec_id == AV_CODEC_ID_SPEEX && b_ogg ) { const uint8_t p_dummy_comment[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; unsigned pi_size[2]; const void *pp_data[2]; pi_size[0] = i_extra; pp_data[0] = p_extra; pi_size[1] = sizeof(p_dummy_comment); pp_data[1] = p_dummy_comment; if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, 2 ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->codec_id == AV_CODEC_ID_OPUS ) { const uint8_t p_dummy_comment[] = { 'O', 'p', 'u', 's', 'T', 'a', 'g', 's', 0, 0, 0, 0, /* Vendor String length */ /* Vendor String */ 0, 0, 0, 0, /* User Comment List Length */ }; unsigned pi_size[2]; const void *pp_data[2]; pi_size[0] = i_extra; pp_data[0] = p_extra; pi_size[1] = sizeof(p_dummy_comment); pp_data[1] = p_dummy_comment; if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, 2 ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->extradata_size > 0 ) { es_fmt.p_extra = malloc( i_extra ); if( es_fmt.p_extra ) { es_fmt.i_extra = i_extra; memcpy( es_fmt.p_extra, p_extra, i_extra ); } } es = es_out_Add( p_demux->out, &es_fmt ); if( s->disposition & AV_DISPOSITION_DEFAULT ) es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, es ); es_format_Clean( &es_fmt ); msg_Dbg( p_demux, "adding es: %s codec = %4.4s (%d)", psz_type, (char*)&fcc, cp->codec_id ); } TAB_APPEND( p_sys->i_tk, p_sys->tk, es ); } p_sys->tk_pcr = xcalloc( p_sys->i_tk, sizeof(*p_sys->tk_pcr) ); if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE ) i_start_time = p_sys->ic->start_time * 1000000 / AV_TIME_BASE; msg_Dbg( p_demux, "AVFormat(%s %s) supported stream", AVPROVIDER(LIBAVFORMAT), LIBAVFORMAT_IDENT ); msg_Dbg( p_demux, " - format = %s (%s)", p_sys->fmt->name, p_sys->fmt->long_name ); msg_Dbg( p_demux, " - start time = %"PRId64, i_start_time ); msg_Dbg( p_demux, " - duration = %"PRId64, ( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE ) ? p_sys->ic->duration * 1000000 / AV_TIME_BASE : -1 ); if( p_sys->ic->nb_chapters > 0 ) { p_sys->p_title = vlc_input_title_New(); p_sys->p_title->i_length = p_sys->ic->duration * 1000000 / AV_TIME_BASE; } for( unsigned i = 0; i < p_sys->ic->nb_chapters; i++ ) { seekpoint_t *s = vlc_seekpoint_New(); AVDictionaryEntry *title = av_dict_get( p_sys->ic->metadata, "title", NULL, 0); if( title && title->value ) { s->psz_name = strdup( title->value ); EnsureUTF8( s->psz_name ); msg_Dbg( p_demux, " - chapter %d: %s", i, s->psz_name ); } s->i_time_offset = p_sys->ic->chapters[i]->start * 1000000 * p_sys->ic->chapters[i]->time_base.num / p_sys->ic->chapters[i]->time_base.den - (i_start_time != -1 ? i_start_time : 0 ); TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s ); } ResetTime( p_demux, 0 ); return VLC_SUCCESS; }
/***************************************************************************** * Demux: The important stuff *****************************************************************************/ static int Demux( demux_t *p_demux ) { playlist_t *p_playlist; char *psz_line; playlist_item_t *p_current; vlc_bool_t b_play; p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); if( !p_playlist ) { msg_Err( p_demux, "can't find playlist" ); return -1; } b_play = E_(FindItem)( p_demux, p_playlist, &p_current ); playlist_ItemToNode( p_playlist, p_current ); p_current->input.i_type = ITEM_TYPE_PLAYLIST; while( (psz_line = stream_ReadLine( p_demux->s )) ) { playlist_item_t *p_item; char **ppsz_options = NULL; int i, i_options = 0; char *psz_name = NULL; if( !ParseLine( psz_line, &psz_name, &ppsz_options, &i_options ) ) { free( psz_line ); continue; } EnsureUTF8( psz_name ); p_item = playlist_ItemNew( p_playlist, "dvb:", psz_name ); for( i = 0; i< i_options; i++ ) { EnsureUTF8( ppsz_options[i] ); playlist_ItemAddOption( p_item, ppsz_options[i] ); } playlist_NodeAddItem( p_playlist, p_item, p_current->pp_parents[0]->i_view, p_current, PLAYLIST_APPEND, PLAYLIST_END ); /* We need to declare the parents of the node as the * * same of the parent's ones */ playlist_CopyParents( p_current, p_item ); vlc_input_item_CopyOptions( &p_current->input, &p_item->input ); while( i_options-- ) free( ppsz_options[i_options] ); if( ppsz_options ) free( ppsz_options ); free( psz_line ); } /* Go back and play the playlist */ if( b_play && p_playlist->status.p_item && p_playlist->status.p_item->i_children > 0 ) { playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, p_playlist->status.i_view, p_playlist->status.p_item, p_playlist->status.p_item->pp_children[0] ); } vlc_object_release( p_playlist ); return VLC_SUCCESS; }
int avformat_OpenDemux( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; AVInputFormat *fmt = NULL; vlc_tick_t i_start_time = VLC_TICK_INVALID; bool b_can_seek; const char *psz_url; int error; if( p_demux->psz_filepath ) psz_url = p_demux->psz_filepath; else psz_url = p_demux->psz_url; if( avformat_ProbeDemux( p_this, &fmt, psz_url ) != VLC_SUCCESS ) return VLC_EGENERIC; vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek ); /* Fill p_demux fields */ p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); if( !p_sys ) return VLC_ENOMEM; p_sys->ic = 0; p_sys->fmt = fmt; p_sys->tracks = NULL; p_sys->i_ssa_order = 0; TAB_INIT( p_sys->i_attachments, p_sys->attachments); p_sys->p_title = NULL; p_sys->i_seekpoint = 0; p_sys->i_update = 0; /* Create I/O wrapper */ unsigned char * p_io_buffer = av_malloc( AVFORMAT_IOBUFFER_SIZE ); if( !p_io_buffer ) { avformat_CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->ic = avformat_alloc_context(); if( !p_sys->ic ) { av_free( p_io_buffer ); avformat_CloseDemux( p_this ); return VLC_ENOMEM; } AVIOContext *pb = p_sys->ic->pb = avio_alloc_context( p_io_buffer, AVFORMAT_IOBUFFER_SIZE, 0, p_demux, IORead, NULL, IOSeek ); if( !pb ) { av_free( p_io_buffer ); avformat_CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->ic->pb->seekable = b_can_seek ? AVIO_SEEKABLE_NORMAL : 0; error = avformat_open_input(&p_sys->ic, psz_url, p_sys->fmt, NULL); if( error < 0 ) { msg_Err( p_demux, "Could not open %s: %s", psz_url, vlc_strerror_c(AVUNERROR(error)) ); av_free( pb->buffer ); av_free( pb ); p_sys->ic = NULL; avformat_CloseDemux( p_this ); return VLC_EGENERIC; } char *psz_opts = var_InheritString( p_demux, "avformat-options" ); unsigned nb_streams = p_sys->ic->nb_streams; AVDictionary *options[nb_streams ? nb_streams : 1]; options[0] = NULL; for (unsigned i = 1; i < nb_streams; i++) options[i] = NULL; if (psz_opts) { vlc_av_get_options(psz_opts, &options[0]); for (unsigned i = 1; i < nb_streams; i++) { av_dict_copy(&options[i], options[0], 0); } free(psz_opts); } vlc_avcodec_lock(); /* avformat calls avcodec behind our back!!! */ error = avformat_find_stream_info( p_sys->ic, options ); vlc_avcodec_unlock(); AVDictionaryEntry *t = NULL; while ((t = av_dict_get(options[0], "", t, AV_DICT_IGNORE_SUFFIX))) { msg_Err( p_demux, "Unknown option \"%s\"", t->key ); } av_dict_free(&options[0]); for (unsigned i = 1; i < nb_streams; i++) { av_dict_free(&options[i]); } nb_streams = p_sys->ic->nb_streams; /* it may have changed */ if( !nb_streams ) { msg_Err( p_demux, "No streams found"); avformat_CloseDemux( p_this ); return VLC_EGENERIC; } p_sys->tracks = calloc( nb_streams, sizeof(*p_sys->tracks) ); if( !p_sys->tracks ) { avformat_CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->i_tracks = nb_streams; if( error < 0 ) { msg_Warn( p_demux, "Could not find stream info: %s", vlc_strerror_c(AVUNERROR(error)) ); } for( unsigned i = 0; i < nb_streams; i++ ) { struct avformat_track_s *p_track = &p_sys->tracks[i]; AVStream *s = p_sys->ic->streams[i]; const AVCodecParameters *cp = s->codecpar; es_format_t es_fmt; const char *psz_type = "unknown"; /* Do not use the cover art as a stream */ if( s->disposition == AV_DISPOSITION_ATTACHED_PIC ) continue; vlc_fourcc_t fcc = GetVlcFourcc( cp->codec_id ); switch( cp->codec_type ) { case AVMEDIA_TYPE_AUDIO: es_format_Init( &es_fmt, AUDIO_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); es_fmt.i_bitrate = cp->bit_rate; es_fmt.audio.i_channels = cp->channels; es_fmt.audio.i_rate = cp->sample_rate; es_fmt.audio.i_bitspersample = cp->bits_per_coded_sample; es_fmt.audio.i_blockalign = cp->block_align; psz_type = "audio"; if(cp->codec_id == AV_CODEC_ID_AAC_LATM) { es_fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M'); es_fmt.b_packetized = false; } else if(cp->codec_id == AV_CODEC_ID_AAC && p_sys->fmt->long_name && strstr(p_sys->fmt->long_name, "raw ADTS AAC")) { es_fmt.i_original_fourcc = VLC_FOURCC('A','D','T','S'); es_fmt.b_packetized = false; } break; case AVMEDIA_TYPE_VIDEO: es_format_Init( &es_fmt, VIDEO_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); es_fmt.video.i_bits_per_pixel = cp->bits_per_coded_sample; /* Special case for raw video data */ if( cp->codec_id == AV_CODEC_ID_RAWVIDEO ) { msg_Dbg( p_demux, "raw video, pixel format: %i", cp->format ); if( GetVlcChroma( &es_fmt.video, cp->format ) != VLC_SUCCESS) { msg_Err( p_demux, "was unable to find a FourCC match for raw video" ); } else es_fmt.i_codec = es_fmt.video.i_chroma; } /* We need this for the h264 packetizer */ else if( cp->codec_id == AV_CODEC_ID_H264 && ( p_sys->fmt == av_find_input_format("flv") || p_sys->fmt == av_find_input_format("matroska") || p_sys->fmt == av_find_input_format("mp4") ) ) es_fmt.i_original_fourcc = VLC_FOURCC( 'a', 'v', 'c', '1' ); es_fmt.video.i_width = cp->width; es_fmt.video.i_height = cp->height; es_fmt.video.i_visible_width = es_fmt.video.i_width; es_fmt.video.i_visible_height = es_fmt.video.i_height; get_rotation(&es_fmt, s); # warning FIXME: implement palette transmission psz_type = "video"; AVRational rate; #if (LIBAVUTIL_VERSION_MICRO < 100) /* libav */ # if (LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 20, 0)) rate.num = s->time_base.num; rate.den = s->time_base.den; # else rate.num = s->codec->time_base.num; rate.den = s->codec->time_base.den; # endif rate.den *= __MAX( s->codec->ticks_per_frame, 1 ); #else /* ffmpeg */ rate = av_guess_frame_rate( p_sys->ic, s, NULL ); #endif if( rate.den && rate.num ) { es_fmt.video.i_frame_rate = rate.num; es_fmt.video.i_frame_rate_base = rate.den; } AVRational ar; #if (LIBAVUTIL_VERSION_MICRO < 100) /* libav */ ar.num = s->sample_aspect_ratio.num; ar.den = s->sample_aspect_ratio.den; #else ar = av_guess_sample_aspect_ratio( p_sys->ic, s, NULL ); #endif if( ar.num && ar.den ) { es_fmt.video.i_sar_den = ar.den; es_fmt.video.i_sar_num = ar.num; } break; case AVMEDIA_TYPE_SUBTITLE: es_format_Init( &es_fmt, SPU_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); if( strncmp( p_sys->ic->iformat->name, "matroska", 8 ) == 0 && cp->codec_id == AV_CODEC_ID_DVD_SUBTITLE && cp->extradata != NULL && cp->extradata_size > 0 ) { char *psz_start; char *psz_buf = malloc( cp->extradata_size + 1); if( psz_buf != NULL ) { memcpy( psz_buf, cp->extradata , cp->extradata_size ); psz_buf[cp->extradata_size] = '\0'; psz_start = strstr( psz_buf, "size:" ); if( psz_start && vobsub_size_parse( psz_start, &es_fmt.subs.spu.i_original_frame_width, &es_fmt.subs.spu.i_original_frame_height ) == VLC_SUCCESS ) { msg_Dbg( p_demux, "original frame size: %dx%d", es_fmt.subs.spu.i_original_frame_width, es_fmt.subs.spu.i_original_frame_height ); } else { msg_Warn( p_demux, "reading original frame size failed" ); } psz_start = strstr( psz_buf, "palette:" ); if( psz_start && vobsub_palette_parse( psz_start, &es_fmt.subs.spu.palette[1] ) == VLC_SUCCESS ) { es_fmt.subs.spu.palette[0] = SPU_PALETTE_DEFINED; msg_Dbg( p_demux, "vobsub palette read" ); } else { msg_Warn( p_demux, "reading original palette failed" ); } free( psz_buf ); } } else if( cp->codec_id == AV_CODEC_ID_DVB_SUBTITLE && cp->extradata_size > 3 ) { es_fmt.subs.dvb.i_id = GetWBE( cp->extradata ) | (GetWBE( cp->extradata + 2 ) << 16); } else if( cp->codec_id == AV_CODEC_ID_MOV_TEXT ) { if( cp->extradata_size && (es_fmt.p_extra = malloc(cp->extradata_size)) ) { memcpy( es_fmt.p_extra, cp->extradata, cp->extradata_size ); es_fmt.i_extra = cp->extradata_size; } } psz_type = "subtitle"; break; default: es_format_Init( &es_fmt, UNKNOWN_ES, 0 ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT if( cp->codec_type == AVMEDIA_TYPE_ATTACHMENT ) { input_attachment_t *p_attachment; psz_type = "attachment"; if( cp->codec_id == AV_CODEC_ID_TTF ) { AVDictionaryEntry *filename = av_dict_get( s->metadata, "filename", NULL, 0 ); if( filename && filename->value ) { p_attachment = vlc_input_attachment_New( filename->value, "application/x-truetype-font", NULL, cp->extradata, (int)cp->extradata_size ); if( p_attachment ) TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment ); } } else msg_Warn( p_demux, "unsupported attachment type (%u) in avformat demux", cp->codec_id ); } else #endif { if( cp->codec_type == AVMEDIA_TYPE_DATA ) psz_type = "data"; msg_Warn( p_demux, "unsupported track type (%u:%u) in avformat demux", cp->codec_type, cp->codec_id ); } break; } AVDictionaryEntry *language = av_dict_get( s->metadata, "language", NULL, 0 ); if ( language && language->value ) es_fmt.psz_language = strdup( language->value ); if( s->disposition & AV_DISPOSITION_DEFAULT ) es_fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1000; #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT if( cp->codec_type != AVMEDIA_TYPE_ATTACHMENT ) #endif if( cp->codec_type != AVMEDIA_TYPE_DATA ) { const bool b_ogg = !strcmp( p_sys->fmt->name, "ogg" ); const uint8_t *p_extra = cp->extradata; unsigned i_extra = cp->extradata_size; if( cp->codec_id == AV_CODEC_ID_THEORA && b_ogg ) { unsigned pi_size[3]; const void *pp_data[3]; unsigned i_count; for( i_count = 0; i_count < 3; i_count++ ) { if( i_extra < 2 ) break; pi_size[i_count] = GetWBE( p_extra ); pp_data[i_count] = &p_extra[2]; if( i_extra < pi_size[i_count] + 2 ) break; p_extra += 2 + pi_size[i_count]; i_extra -= 2 + pi_size[i_count]; } if( i_count > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, i_count ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->codec_id == AV_CODEC_ID_SPEEX && b_ogg ) { const uint8_t p_dummy_comment[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; unsigned pi_size[2]; const void *pp_data[2]; pi_size[0] = i_extra; pp_data[0] = p_extra; pi_size[1] = sizeof(p_dummy_comment); pp_data[1] = p_dummy_comment; if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, 2 ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->codec_id == AV_CODEC_ID_OPUS ) { const uint8_t p_dummy_comment[] = { 'O', 'p', 'u', 's', 'T', 'a', 'g', 's', 0, 0, 0, 0, /* Vendor String length */ /* Vendor String */ 0, 0, 0, 0, /* User Comment List Length */ }; unsigned pi_size[2]; const void *pp_data[2]; pi_size[0] = i_extra; pp_data[0] = p_extra; pi_size[1] = sizeof(p_dummy_comment); pp_data[1] = p_dummy_comment; if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, 2 ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->extradata_size > 0 && !es_fmt.i_extra ) { es_fmt.p_extra = malloc( i_extra ); if( es_fmt.p_extra ) { es_fmt.i_extra = i_extra; memcpy( es_fmt.p_extra, p_extra, i_extra ); } } p_track->p_es = es_out_Add( p_demux->out, &es_fmt ); if( p_track->p_es && (s->disposition & AV_DISPOSITION_DEFAULT) ) es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, p_track->p_es ); msg_Dbg( p_demux, "adding es: %s codec = %4.4s (%d)", psz_type, (char*)&fcc, cp->codec_id ); } es_format_Clean( &es_fmt ); } if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE ) i_start_time = FROM_AV_TS(p_sys->ic->start_time); msg_Dbg( p_demux, "AVFormat(%s %s) supported stream", AVPROVIDER(LIBAVFORMAT), LIBAVFORMAT_IDENT ); msg_Dbg( p_demux, " - format = %s (%s)", p_sys->fmt->name, p_sys->fmt->long_name ); msg_Dbg( p_demux, " - start time = %"PRId64, i_start_time ); msg_Dbg( p_demux, " - duration = %"PRId64, ( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE ) ? FROM_AV_TS(p_sys->ic->duration) : -1 ); if( p_sys->ic->nb_chapters > 0 ) { p_sys->p_title = vlc_input_title_New(); p_sys->p_title->i_length = FROM_AV_TS(p_sys->ic->duration); } for( unsigned i = 0; i < p_sys->ic->nb_chapters; i++ ) { seekpoint_t *s = vlc_seekpoint_New(); AVDictionaryEntry *title = av_dict_get( p_sys->ic->metadata, "title", NULL, 0); if( title && title->value ) { s->psz_name = strdup( title->value ); EnsureUTF8( s->psz_name ); msg_Dbg( p_demux, " - chapter %d: %s", i, s->psz_name ); } s->i_time_offset = vlc_tick_from_samples( p_sys->ic->chapters[i]->start * p_sys->ic->chapters[i]->time_base.num, p_sys->ic->chapters[i]->time_base.den ) - (i_start_time != VLC_TICK_INVALID ? i_start_time : 0 ); TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s ); } ResetTime( p_demux, 0 ); return VLC_SUCCESS; }
static int ReadICYMeta( stream_t *p_access ) { access_sys_t *p_sys = p_access->p_sys; uint8_t buffer; char *p, *psz_meta; int i_read; /* Read meta data length */ if( ReadData( p_access, &i_read, &buffer, 1 ) ) return VLC_EGENERIC; if( i_read != 1 ) return VLC_EGENERIC; const int i_size = buffer << 4; /* msg_Dbg( p_access, "ICY meta size=%u", i_size); */ psz_meta = malloc( i_size + 1 ); for( i_read = 0; i_read < i_size; ) { int i_tmp; if( ReadData( p_access, &i_tmp, (uint8_t *)&psz_meta[i_read], i_size - i_read ) || i_tmp <= 0 ) { free( psz_meta ); return VLC_EGENERIC; } i_read += i_tmp; } psz_meta[i_read] = '\0'; /* Just in case */ /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */ /* Now parse the meta */ /* Look for StreamTitle= */ p = strcasestr( (char *)psz_meta, "StreamTitle=" ); if( p ) { p += strlen( "StreamTitle=" ); if( *p == '\'' || *p == '"' ) { char closing[] = { p[0], ';', '\0' }; char *psz = strstr( &p[1], closing ); if( !psz ) psz = strchr( &p[1], ';' ); if( psz ) *psz = '\0'; p++; } else { char *psz = strchr( p, ';' ); if( psz ) *psz = '\0'; } if( !p_sys->psz_icy_title || strcmp( p_sys->psz_icy_title, p ) ) { free( p_sys->psz_icy_title ); char *psz_tmp = strdup( p ); p_sys->psz_icy_title = EnsureUTF8( psz_tmp ); if( !p_sys->psz_icy_title ) free( psz_tmp ); msg_Dbg( p_access, "New Icy-Title=%s", p_sys->psz_icy_title ); if( p_access->p_input_item ) input_item_SetMeta( p_access->p_input_item, vlc_meta_NowPlaying, p_sys->psz_icy_title ); } } free( psz_meta ); return VLC_SUCCESS; }
/***************************************************************************** * ParseText: parse an text subtitle packet and send it to the video output *****************************************************************************/ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; char *psz_subtitle = NULL; video_format_t fmt; /* We cannot display a subpicture with no date */ if( p_block->i_pts <= VLC_TS_INVALID ) { msg_Warn( p_dec, "subtitle without a date" ); return NULL; } /* Check validity of packet data */ /* An "empty" line containing only \0 can be used to force and ephemer picture from the screen */ if( p_block->i_buffer < 1 ) { msg_Warn( p_dec, "no subtitle data" ); return NULL; } /* Should be resiliant against bad subtitles */ psz_subtitle = malloc( p_block->i_buffer + 1 ); if( psz_subtitle == NULL ) return NULL; memcpy( psz_subtitle, p_block->p_buffer, p_block->i_buffer ); psz_subtitle[p_block->i_buffer] = '\0'; if( p_sys->iconv_handle == (vlc_iconv_t)-1 ) { if (EnsureUTF8( psz_subtitle ) == NULL) { msg_Err( p_dec, "failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file." ); } } else { if( p_sys->b_autodetect_utf8 ) { if( IsUTF8( psz_subtitle ) == NULL ) { msg_Dbg( p_dec, "invalid UTF-8 sequence: " "disabling UTF-8 subtitles autodetection" ); p_sys->b_autodetect_utf8 = false; } } if( !p_sys->b_autodetect_utf8 ) { size_t inbytes_left = strlen( psz_subtitle ); size_t outbytes_left = 6 * inbytes_left; char *psz_new_subtitle = xmalloc( outbytes_left + 1 ); char *psz_convert_buffer_out = psz_new_subtitle; const char *psz_convert_buffer_in = psz_subtitle; size_t ret = vlc_iconv( p_sys->iconv_handle, &psz_convert_buffer_in, &inbytes_left, &psz_convert_buffer_out, &outbytes_left ); *psz_convert_buffer_out++ = '\0'; free( psz_subtitle ); if( ( ret == (size_t)(-1) ) || inbytes_left ) { free( psz_new_subtitle ); msg_Err( p_dec, "failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file." ); return NULL; } psz_subtitle = realloc( psz_new_subtitle, psz_convert_buffer_out - psz_new_subtitle ); if( !psz_subtitle ) psz_subtitle = psz_new_subtitle; } } /* Create the subpicture unit */ p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); free( psz_subtitle ); return NULL; } /* Create a new subpicture region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_TEXT; fmt.i_width = fmt.i_height = 0; fmt.i_x_offset = fmt.i_y_offset = 0; p_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { msg_Err( p_dec, "cannot allocate SPU region" ); free( psz_subtitle ); decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } /* Decode and format the subpicture unit */ if( p_dec->fmt_in.i_codec != VLC_CODEC_SSA ) { /* Normal text subs, easy markup */ p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align; p_spu->p_region->i_x = p_sys->i_align ? 20 : 0; p_spu->p_region->i_y = 10; /* Remove formatting from string */ p_spu->p_region->psz_text = StripTags( psz_subtitle ); if( var_InheritBool( p_dec, "subsdec-formatted" ) ) { p_spu->p_region->psz_html = CreateHtmlSubtitle( &p_spu->p_region->i_align, psz_subtitle ); } p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; } else { /* Decode SSA/USF strings */ ParseSSAString( p_dec, psz_subtitle, p_spu ); p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; p_spu->i_original_picture_width = p_sys->i_original_width; p_spu->i_original_picture_height = p_sys->i_original_height; } free( psz_subtitle ); return p_spu; }
/* http://www.linuxtv.org/vdrwiki/index.php/Syntax_of_channels.conf or not... * Read the dvb-apps source code for reference. */ static input_item_t *ParseLine(char *line) { char *str, *end; line += strspn(line, " \t\r"); /* skip leading white spaces */ if (*line == '#') return NULL; /* skip comments */ /* Extract channel cute name */ char *name = strsep(&line, ":"); assert(name != NULL); EnsureUTF8(name); /* Extract central frequency */ str = strsep(&line, ":"); if (str == NULL) return NULL; unsigned long freq = strtoul(str, &end, 10); if (*end) return NULL; /* Extract tuning parameters */ str = strsep(&line, ":"); if (str == NULL) return NULL; char *mrl; if (!strcmp(str, "h") || !strcmp(str, "v")) { /* DVB-S */ char polarization = toupper(*str); /* TODO: sat no. */ str = strsep(&line, ":"); if (str == NULL) return NULL; /* baud rate */ str = strsep(&line, ":"); if (str == NULL) return NULL; unsigned long rate = strtoul(str, &end, 10); if (*end || rate > (ULONG_MAX / 1000u)) return NULL; rate *= 1000; if (asprintf(&mrl, "dvb-s://frequency=%"PRIu64":polarization=%c:srate=%lu", freq * UINT64_C(1000000), polarization, rate) == -1) mrl = NULL; } else if (!strncmp(str, "INVERSION_", 10)) { /* DVB-C or DVB-T */ int inversion; str += 10; if (strcmp(str, "AUTO")) inversion = -1; else if (strcmp(str, "OFF")) inversion = 0; else if (strcmp(str, "ON")) inversion = 1; else return NULL; str = strsep(&line, ":"); if (str == NULL) return NULL; if (strncmp(str, "BANDWIDTH_", 10)) { /* DVB-C */ unsigned long rate = strtoul(str, &end, 10); if (*end) return NULL; str = strsep(&line, ":"); const char *fec = ParseFEC(str); str = strsep(&line, ":"); const char *mod = ParseModulation(str); if (fec == NULL || mod == NULL) return NULL; if (asprintf(&mrl, "dvb-c://frequency=%lu:inversion:%d:srate=%lu:" "fec=%s:modulation=%s", freq, inversion, rate, fec, mod) == -1) mrl = NULL; } else { /* DVB-T */ unsigned bandwidth = atoi(str + 10); str = strsep(&line, ":"); const char *hp = ParseFEC(str); str = strsep(&line, ":"); const char *lp = ParseFEC(str); str = strsep(&line, ":"); const char *mod = ParseModulation(str); if (hp == NULL || lp == NULL || mod == NULL) return NULL; str = strsep(&line, ":"); if (str == NULL || strncmp(str, "TRANSMISSION_MODE_", 18)) return NULL; int xmit = atoi(str); if (xmit == 0) xmit = -1; /* AUTO */ str = strsep(&line, ":"); const char *guard = ParseGuard(str); if (guard == NULL) return NULL; str = strsep(&line, ":"); if (str == NULL || strncmp(str, "HIERARCHY_", 10)) return NULL; str += 10; int hierarchy = atoi(str); if (!strcmp(str, "AUTO")) hierarchy = -1; if (asprintf(&mrl, "dvb-t://frequency=%lu:inversion=%d:" "bandwidth=%u:code-rate-hp=%s:code-rate-lp=%s:" "modulation=%s:transmission=%d:guard=%s:" "hierarchy=%d", freq, inversion, bandwidth, hp, lp, mod, xmit, guard, hierarchy) == -1) mrl = NULL; } } else { /* ATSC */ const char *mod = ParseModulation(str); if (mod == NULL) return NULL; if (asprintf(&mrl, "atsc://frequency=%lu:modulation=%s", freq, mod) == -1) mrl = NULL; } if (unlikely(mrl == NULL)) return NULL; /* Video PID (TODO? set video track) */ strsep(&line, ":"); /* Audio PID (TODO? set audio track) */ strsep(&line, ":"); /* Extract SID */ str = strsep(&line, ":"); if (str == NULL) { free(mrl); return NULL; } unsigned long sid = strtoul(str, &end, 10); if (*end || sid > 65535) { free(mrl); return NULL; } char sid_opt[sizeof("program=65535")]; snprintf(sid_opt, sizeof(sid_opt), "program=%lu", sid); input_item_t *item = input_item_NewCard(mrl, name); free(mrl); if (item != NULL) input_item_AddOption(item, sid_opt, 0); return item; }
int vlclua_playlist_add_internal( vlc_object_t *p_this, lua_State *L, playlist_t *p_playlist, input_item_t *p_parent, bool b_play ) { int i_count = 0; input_item_node_t *p_parent_node = NULL; assert( p_parent || p_playlist ); /* playlist */ if( lua_istable( L, -1 ) ) { if( p_parent ) p_parent_node = input_item_node_Create( p_parent ); lua_pushnil( L ); /* playlist nil */ while( lua_next( L, -2 ) ) { /* playlist key item */ /* <Parse playlist item> */ if( lua_istable( L, -1 ) ) { lua_getfield( L, -1, "path" ); /* playlist key item path */ if( lua_isstring( L, -1 ) ) { char *psz_oldurl = NULL; const char *psz_path = NULL; char *psz_u8path = NULL; const char *psz_name = NULL; char **ppsz_options = NULL; int i_options = 0; mtime_t i_duration = -1; input_item_t *p_input; /* Read path and name */ if (p_parent) { psz_oldurl = input_item_GetURI( p_parent ); msg_Dbg( p_this, "old path: %s", psz_oldurl ); } psz_path = lua_tostring( L, -1 ); msg_Dbg( p_this, "Path: %s", psz_path ); lua_getfield( L, -2, "name" ); /* playlist key item path name */ if( lua_isstring( L, -1 ) ) { psz_name = lua_tostring( L, -1 ); msg_Dbg( p_this, "Name: %s", psz_name ); } else { if( !lua_isnil( L, -1 ) ) msg_Warn( p_this, "Playlist item name should be a string." ); psz_name = NULL; } /* Read duration */ lua_getfield( L, -3, "duration" ); /* playlist key item path name duration */ if( lua_isnumber( L, -1 ) ) { i_duration = (mtime_t)(lua_tonumber( L, -1 )*1e6); } else if( !lua_isnil( L, -1 ) ) { msg_Warn( p_this, "Playlist item duration should be a number (in seconds)." ); } lua_pop( L, 1 ); /* pop "duration" */ /* playlist key item path name */ /* Read options: item must be on top of stack */ lua_pushvalue( L, -3 ); /* playlist key item path name item */ vlclua_read_options( p_this, L, &i_options, &ppsz_options ); /* Create input item */ p_input = input_item_NewExt( psz_path, psz_name, i_options, (const char **)ppsz_options, VLC_INPUT_OPTION_TRUSTED, i_duration ); lua_pop( L, 3 ); /* pop "path name item" */ /* playlist key item */ /* Read meta data: item must be on top of stack */ vlclua_read_meta_data( p_this, L, p_input ); /* copy the original URL to the meta data, if "URL" is still empty */ char* url = input_item_GetURL( p_input ); if( url == NULL && p_parent) { EnsureUTF8( psz_oldurl ); msg_Dbg( p_this, "meta-URL: %s", psz_oldurl ); input_item_SetURL ( p_input, psz_oldurl ); } free( psz_oldurl ); free( url ); /* copy the psz_name to the meta data, if "Title" is still empty */ char* title = input_item_GetTitle( p_input ); if( title == NULL ) input_item_SetTitle ( p_input, psz_name ); free( title ); /* Read custom meta data: item must be on top of stack*/ vlclua_read_custom_meta_data( p_this, L, p_input ); /* Append item to playlist */ if( p_parent ) /* Add to node */ { input_item_CopyOptions( p_input, p_parent ); input_item_node_AppendItem( p_parent_node, p_input ); } else /* Play or Enqueue (preparse) */ /* FIXME: playlist_AddInput() can fail */ playlist_AddInput( p_playlist, p_input, PLAYLIST_APPEND | ( b_play ? PLAYLIST_GO : PLAYLIST_PREPARSE ), PLAYLIST_END, true, false ); i_count ++; /* increment counter */ vlc_gc_decref( p_input ); while( i_options > 0 ) free( ppsz_options[--i_options] ); free( ppsz_options ); free( psz_u8path ); } else { lua_pop( L, 1 ); /* pop "path" */ msg_Warn( p_this, "Playlist item's path should be a string" ); } /* playlist key item */ } else { msg_Warn( p_this, "Playlist item should be a table" ); } /* <Parse playlist item> */ lua_pop( L, 1 ); /* pop the value, keep the key for * the next lua_next() call */ /* playlist key */ } /* playlist */ if( p_parent ) { if( i_count ) input_item_node_PostAndDelete( p_parent_node ); else input_item_node_Delete( p_parent_node ); } } else { msg_Warn( p_this, "Playlist should be a table." ); } return i_count; }
/** * Non-MIDI Meta events handler */ static int HandleMeta (demux_t *p_demux, mtrk_t *tr) { stream_t *s = p_demux->s; demux_sys_t *p_sys = p_demux->p_sys; uint8_t *payload; uint8_t type; int32_t length; int ret = 0; if (stream_Read (s, &type, 1) != 1) return -1; length = ReadVarInt (s); if (length < 0) return -1; payload = malloc (length + 1); if ((payload == NULL) || (stream_Read (s, payload, length) != length)) { free (payload); return -1; } payload[length] = '\0'; switch (type) { case 0x00: /* Sequence Number */ break; case 0x01: /* Text (comment) */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Text : %s", (char *)payload); break; case 0x02: /* Copyright */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Copyright : %s", (char *)payload); break; case 0x03: /* Track name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Track name: %s", (char *)payload); break; case 0x04: /* Instrument name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Instrument: %s", (char *)payload); break; case 0x05: /* Lyric (one syllable) */ /*EnsureUTF8 ((char *)payload);*/ break; case 0x06: /* Marker text */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Marker : %s", (char *)payload); case 0x07: /* Cue point (WAVE filename) */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Cue point : %s", (char *)payload); break; case 0x08: /* Program/Patch name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Patch name: %s", (char *)payload); break; case 0x09: /* MIDI port name */ EnsureUTF8 ((char *)payload); msg_Dbg (p_demux, "MIDI port : %s", (char *)payload); break; case 0x2F: /* End of track */ if (tr->end != stream_Tell (s)) { msg_Err (p_demux, "misplaced end of track"); ret = -1; } break; case 0x51: /* Tempo */ if (length == 3) { uint32_t uspqn = (payload[0] << 16) | (payload[1] << 8) | payload[2]; unsigned tempo = 60 * 1000000 / (uspqn ? uspqn : 1); msg_Dbg (p_demux, "tempo: %uus/qn -> %u BPM", (unsigned)uspqn, tempo); if (tempo < TEMPO_MIN) { msg_Warn (p_demux, "tempo too slow -> %u BPM", TEMPO_MIN); tempo = TEMPO_MIN; } else if (tempo > TEMPO_MAX) { msg_Warn (p_demux, "tempo too fast -> %u BPM", TEMPO_MAX); tempo = TEMPO_MAX; } date_Change (&p_sys->pts, p_sys->ppqn * tempo, 60); } else ret = -1; break; case 0x54: /* SMPTE offset */ if (length == 5) msg_Warn (p_demux, "SMPTE offset not implemented"); else ret = -1; break; case 0x58: /* Time signature */ if (length == 4) ; else ret = -1; break; case 0x59: /* Key signature */ if (length == 2) ; else ret = -1; break; case 0x7f: /* Proprietary event */ msg_Dbg (p_demux, "ignored proprietary SMF Meta Event (%d bytes)", length); break; default: msg_Warn (p_demux, "unknown SMF Meta Event type 0x%02X (%d bytes)", type, length); } free (payload); return ret; }
static int CdTextParse( vlc_meta_t ***ppp_tracks, int *pi_tracks, const uint8_t *p_buffer, int i_buffer ) { char *pppsz_info[128][0x10]; int i_track_last = -1; if( i_buffer < 4 ) return -1; memset( pppsz_info, 0, sizeof(pppsz_info) ); for( int i = 0; i < (i_buffer-4)/18; i++ ) { const uint8_t *p_block = &p_buffer[4 + 18*i]; char psz_text[12+1]; const int i_pack_type = p_block[0]; if( i_pack_type < 0x80 || i_pack_type > 0x8f ) continue; const int i_track_number = (p_block[1] >> 0)&0x7f; const int i_extension_flag = ( p_block[1] >> 7)& 0x01; if( i_extension_flag ) continue; //const int i_sequence_number = p_block[2]; //const int i_charater_position = (p_block[3] >> 0) &0x0f; //const int i_block_number = (p_block[3] >> 4) &0x07; /* TODO unicode support * I need a sample */ //const int i_unicode = ( p_block[3] >> 7)&0x01; //const int i_crc = (p_block[4+12] << 8) | (p_block[4+13] << 0); /* */ memcpy( psz_text, &p_block[4], 12 ); psz_text[12] = '\0'; /* */ int i_track = i_track_number; char *psz_track = &psz_text[0]; while( i_track <= 127 && psz_track < &psz_text[12] ) { //fprintf( stderr, "t=%d psz_track=%p end=%p", i_track, (void *)psz_track, (void *)&psz_text[12] ); if( *psz_track ) { astrcat( &pppsz_info[i_track][i_pack_type-0x80], psz_track ); i_track_last = __MAX( i_track_last, i_track ); } i_track++; psz_track += 1 + strlen(psz_track); } } if( i_track_last < 0 ) return -1; vlc_meta_t **pp_tracks = calloc( i_track_last+1, sizeof(*pp_tracks) ); if( !pp_tracks ) goto exit; for( int j = 0; j < 0x10; j++ ) { for( int i = 0; i <= i_track_last; i++ ) { /* */ if( pppsz_info[i][j] ) EnsureUTF8( pppsz_info[i][j] ); /* */ const char *psz_default = pppsz_info[0][j]; const char *psz_value = pppsz_info[i][j]; if( !psz_value && !psz_default ) continue; vlc_meta_t *p_track = pp_tracks[i]; if( !p_track ) { p_track = pp_tracks[i] = vlc_meta_New(); if( !p_track ) continue; } switch( j ) { case 0x00: /* Album/Title */ if( i == 0 ) { vlc_meta_SetAlbum( p_track, psz_value ); } else { if( psz_value ) vlc_meta_SetTitle( p_track, psz_value ); if( psz_default ) vlc_meta_SetAlbum( p_track, psz_default ); } break; case 0x01: /* Performer */ vlc_meta_SetArtist( p_track, psz_value ? psz_value : psz_default ); break; case 0x05: /* Messages */ vlc_meta_SetDescription( p_track, psz_value ? psz_value : psz_default ); break; case 0x07: /* Genre */ vlc_meta_SetGenre( p_track, psz_value ? psz_value : psz_default ); break; /* FIXME unsupported: * 0x02: songwriter * 0x03: composer * 0x04: arrenger * 0x06: disc id */ } } } /* */ exit: for( int j = 0; j < 0x10; j++ ) for( int i = 0; i <= i_track_last; i++ ) free( pppsz_info[i][j] ); *ppp_tracks = pp_tracks; *pi_tracks = i_track_last+1; return pp_tracks ? 0 : -1; }
/***************************************************************************** * ParseText: parse an text subtitle packet and send it to the video output *****************************************************************************/ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; char *psz_subtitle = NULL; video_format_t fmt; /* We cannot display a subpicture with no date */ if( p_block->i_pts == 0 ) { msg_Warn( p_dec, "subtitle without a date" ); return NULL; } /* Check validity of packet data */ /* An "empty" line containing only \0 can be used to force and ephemer picture from the screen */ if( p_block->i_buffer < 1 ) { msg_Warn( p_dec, "no subtitle data" ); return NULL; } /* Should be resiliant against bad subtitles */ psz_subtitle = strndup( (const char *)p_block->p_buffer, p_block->i_buffer ); if( psz_subtitle == NULL ) return NULL; if( p_sys->iconv_handle == (vlc_iconv_t)-1 ) EnsureUTF8( psz_subtitle ); else { if( p_sys->b_autodetect_utf8 ) { if( IsUTF8( psz_subtitle ) == NULL ) { msg_Dbg( p_dec, "invalid UTF-8 sequence: " "disabling UTF-8 subtitles autodetection" ); p_sys->b_autodetect_utf8 = VLC_FALSE; } } if( !p_sys->b_autodetect_utf8 ) { size_t inbytes_left = strlen( psz_subtitle ); size_t outbytes_left = 6 * inbytes_left; char *psz_new_subtitle = malloc( outbytes_left + 1 ); char *psz_convert_buffer_out = psz_new_subtitle; const char *psz_convert_buffer_in = psz_subtitle; size_t ret = vlc_iconv( p_sys->iconv_handle, &psz_convert_buffer_in, &inbytes_left, &psz_convert_buffer_out, &outbytes_left ); *psz_convert_buffer_out++ = '\0'; free( psz_subtitle ); if( ( ret == (size_t)(-1) ) || inbytes_left ) { free( psz_new_subtitle ); msg_Err( p_dec, _("failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file.") ); return NULL; } psz_subtitle = realloc( psz_new_subtitle, psz_convert_buffer_out - psz_new_subtitle ); } } /* Create the subpicture unit */ p_spu = p_dec->pf_spu_buffer_new( p_dec ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); if( psz_subtitle ) free( psz_subtitle ); return NULL; } p_spu->b_pausable = VLC_TRUE; /* Create a new subpicture region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_FOURCC('T','E','X','T'); fmt.i_aspect = 0; fmt.i_width = fmt.i_height = 0; fmt.i_x_offset = fmt.i_y_offset = 0; p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt ); if( !p_spu->p_region ) { msg_Err( p_dec, "cannot allocate SPU region" ); if( psz_subtitle ) free( psz_subtitle ); p_dec->pf_spu_buffer_del( p_dec, p_spu ); return NULL; } /* Decode and format the subpicture unit */ if( p_dec->fmt_in.i_codec != VLC_FOURCC('s','s','a',' ') ) { /* Normal text subs, easy markup */ p_spu->i_flags = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align; p_spu->i_x = p_sys->i_align ? 20 : 0; p_spu->i_y = 10; /* Remove formatting from string */ StripTags( psz_subtitle ); p_spu->p_region->psz_text = psz_subtitle; p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = VLC_FALSE; } else { /* Decode SSA strings */ ParseSSAString( p_dec, psz_subtitle, p_spu ); p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = VLC_FALSE; p_spu->i_original_picture_width = p_sys->i_original_width; p_spu->i_original_picture_height = p_sys->i_original_height; if( psz_subtitle ) free( psz_subtitle ); } return p_spu; }
/**************************************************************************** * Filter: the whole thing **************************************************************************** * This function outputs subpictures at regular time intervals. ****************************************************************************/ static subpicture_t *Filter( filter_t *p_filter, mtime_t date ) { filter_sys_t *p_sys = p_filter->p_sys; subpicture_t *p_spu; video_format_t fmt; subpicture_region_t *p_region; int i_feed, i_item; rss_feed_t *p_feed; memset( &fmt, 0, sizeof(video_format_t) ); vlc_mutex_lock( &p_sys->lock ); /* Check if the feeds have been fetched and that we have some feeds */ /* TODO: check that we have items for each feeds */ if( !p_sys->b_fetched && p_sys->i_feeds > 0 ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } if( p_sys->last_date + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 ) /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */ * p_sys->i_speed > date ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } p_sys->last_date = date; p_sys->i_cur_char++; if( p_sys->i_cur_item == -1 ? p_sys->p_feeds[p_sys->i_cur_feed].psz_title[p_sys->i_cur_char] == 0 : p_sys->p_feeds[p_sys->i_cur_feed].p_items[p_sys->i_cur_item].psz_title[p_sys->i_cur_char] == 0 ) { p_sys->i_cur_char = 0; p_sys->i_cur_item++; if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items ) { if( p_sys->i_title == scroll_title ) p_sys->i_cur_item = -1; else p_sys->i_cur_item = 0; p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds; } } p_spu = filter_NewSubpicture( p_filter ); if( !p_spu ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } fmt.i_chroma = VLC_CODEC_TEXT; p_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { p_filter->pf_sub_buffer_del( p_filter, p_spu ); vlc_mutex_unlock( &p_sys->lock ); return NULL; } /* Generate the string that will be displayed. This string is supposed to be p_sys->i_length characters long. */ i_item = p_sys->i_cur_item; i_feed = p_sys->i_cur_feed; p_feed = &p_sys->p_feeds[i_feed]; if( ( p_feed->p_pic && p_sys->i_title == default_title ) || p_sys->i_title == hide_title ) { /* Don't display the feed's title if we have an image */ _snprintf( p_sys->psz_marquee, p_sys->i_length, "%s", p_sys->p_feeds[i_feed].p_items[i_item].psz_title +p_sys->i_cur_char ); // sunqueen modify } else if( ( !p_feed->p_pic && p_sys->i_title == default_title ) || p_sys->i_title == prepend_title ) { _snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s", p_sys->p_feeds[i_feed].psz_title, p_sys->p_feeds[i_feed].p_items[i_item].psz_title +p_sys->i_cur_char ); // sunqueen modify } else /* scrolling title */ { if( i_item == -1 ) _snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s", p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char, p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title ); // sunqueen modify else _snprintf( p_sys->psz_marquee, p_sys->i_length, "%s", p_sys->p_feeds[i_feed].p_items[i_item].psz_title +p_sys->i_cur_char ); // sunqueen modify } while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length ) { i_item++; if( i_item == p_sys->p_feeds[i_feed].i_items ) break; _snprintf( strchr( p_sys->psz_marquee, 0 ), p_sys->i_length - strlen( p_sys->psz_marquee ), " - %s", p_sys->p_feeds[i_feed].p_items[i_item].psz_title ); // sunqueen modify } /* Calls to snprintf might split multibyte UTF8 chars ... * which freetype doesn't like. */ { char *a = strdup( p_sys->psz_marquee ); char *a2 = a; char *b = p_sys->psz_marquee; EnsureUTF8( p_sys->psz_marquee ); /* we want to use ' ' instead of '?' for erroneous chars */ while( *b != '\0' ) { if( *b != *a ) *b = ' '; b++;a++; } free( a2 ); } p_spu->p_region->psz_text = strdup(p_sys->psz_marquee); if( p_sys->p_style->i_font_size > 0 ) p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size; p_spu->i_start = date; p_spu->i_stop = 0; p_spu->b_ephemer = true; /* where to locate the string: */ if( p_sys->i_pos < 0 ) { /* set to an absolute xy */ p_spu->p_region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP; p_spu->b_absolute = true; } else { /* set to one of the 9 relative locations */ p_spu->p_region->i_align = p_sys->i_pos; p_spu->b_absolute = false; } p_spu->p_region->i_x = p_sys->i_xoff; p_spu->p_region->i_y = p_sys->i_yoff; p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style ); if( p_feed->p_pic ) { /* Display the feed's image */ picture_t *p_pic = p_feed->p_pic; video_format_t fmt_out; memset( &fmt_out, 0, sizeof(video_format_t) ); fmt_out.i_chroma = VLC_CODEC_YUVA; fmt_out.i_sar_num = fmt_out.i_sar_den = 1; fmt_out.i_width = fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch; fmt_out.i_height = fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines; p_region = subpicture_region_New( &fmt_out ); if( !p_region ) { msg_Err( p_filter, "cannot allocate SPU region" ); } else { p_region->i_x = p_spu->p_region->i_x; p_region->i_y = p_spu->p_region->i_y; /* FIXME the copy is probably not needed anymore */ picture_Copy( p_region->p_picture, p_pic ); p_spu->p_region->p_next = p_region; /* Offset text to display right next to the image */ p_spu->p_region->i_x += fmt_out.i_visible_width; } } vlc_mutex_unlock( &p_sys->lock ); return p_spu; }
block_t *DirBlock (access_t *p_access) { access_sys_t *p_sys = p_access->p_sys; directory_t *current = p_sys->current; if (p_access->info.b_eof) return NULL; if (p_sys->header) { /* Startup: send the XSPF header */ static const char header[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n" " <trackList>\n"; block_t *block = block_Alloc (sizeof (header) - 1); if (!block) goto fatal; memcpy (block->p_buffer, header, sizeof (header) - 1); p_sys->header = false; return block; } if (current->i >= current->filec) { /* End of directory, go back to parent */ closedir (current->handle); p_sys->current = current->parent; free (current->uri); free (current->filev); #ifndef HAVE_OPENAT free (current->path); #endif free (current); if (p_sys->current == NULL) { /* End of XSPF playlist */ char *footer; int len = asprintf (&footer, " </trackList>\n" " <extension application=\"http://www.videolan.org/" "vlc/playlist/0\">\n" "%s" " </extension>\n" "</playlist>\n", p_sys->xspf_ext ? p_sys->xspf_ext : ""); if (unlikely(len == -1)) goto fatal; block_t *block = block_heap_Alloc (footer, len); p_access->info.b_eof = true; return block; } else { /* This was the end of a "subnode" */ /* Write the ID to the extension */ char *old_xspf_ext = p_sys->xspf_ext; if (old_xspf_ext != NULL && asprintf (&p_sys->xspf_ext, "%s </vlc:node>\n", old_xspf_ext ? old_xspf_ext : "") == -1) p_sys->xspf_ext = NULL; free (old_xspf_ext); } return NULL; } char *entry = current->filev[current->i++]; /* Handle recursion */ if (p_sys->mode != MODE_COLLAPSE) { DIR *handle; #ifdef HAVE_OPENAT int fd = vlc_openat (dirfd (current->handle), entry, O_RDONLY | O_DIRECTORY); if (fd == -1) { if (errno == ENOTDIR) goto notdir; goto skip; /* File cannot be opened... forget it */ } struct stat st; if (fstat (fd, &st) || p_sys->mode == MODE_NONE || has_inode_loop (current, st.st_dev, st.st_ino) || (handle = fdopendir (fd)) == NULL) { close (fd); goto skip; } #else char *path; if (asprintf (&path, "%s/%s", current->path, entry) == -1) goto skip; if ((handle = vlc_opendir (path)) == NULL) goto notdir; if (p_sys->mode == MODE_NONE) goto skip; #endif directory_t *sub = malloc (sizeof (*sub)); if (unlikely(sub == NULL)) { closedir (handle); #ifndef HAVE_OPENAT free (path); #endif goto skip; } sub->parent = current; sub->handle = handle; sub->filec = vlc_loaddir (handle, &sub->filev, visible, p_sys->compar); if (sub->filec < 0) sub->filev = NULL; sub->i = 0; #ifdef HAVE_OPENAT sub->device = st.st_dev; sub->inode = st.st_ino; #else sub->path = path; #endif p_sys->current = sub; char *encoded = encode_URI_component (entry); if (encoded == NULL || (asprintf (&sub->uri, "%s/%s", current->uri, encoded) == -1)) sub->uri = NULL; free (encoded); if (unlikely(sub->uri == NULL)) { free (entry); goto fatal; } /* Add node to XSPF extension */ char *old_xspf_ext = p_sys->xspf_ext; EnsureUTF8 (entry); char *title = convert_xml_special_chars (entry); if (old_xspf_ext != NULL && asprintf (&p_sys->xspf_ext, "%s <vlc:node title=\"%s\">\n", old_xspf_ext, title ? title : "?") == -1) p_sys->xspf_ext = NULL; free (old_xspf_ext); free (title); goto skip; } notdir: /* Skip files with ignored extensions */ if (p_sys->ignored_exts != NULL) { const char *ext = strrchr (entry, '.'); if (ext != NULL) { size_t extlen = strlen (++ext); for (const char *type = p_sys->ignored_exts, *end; type[0]; type = end + 1) { end = strchr (type, ','); if (end == NULL) end = type + strlen (type); if (type + extlen == end && !strncasecmp (ext, type, extlen)) { free (entry); return NULL; } if (*end == '\0') break; } } } char *encoded = encode_URI_component (entry); free (entry); if (encoded == NULL) goto fatal; int len = asprintf (&entry, " <track><location>%s/%s</location>\n" \ " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \ " <vlc:id>%d</vlc:id>\n" \ " </extension>\n" \ " </track>\n", current->uri, encoded, p_sys->i_item_count++); free (encoded); if (len == -1) goto fatal; /* Write the ID to the extension */ char *old_xspf_ext = p_sys->xspf_ext; if (old_xspf_ext != NULL && asprintf (&p_sys->xspf_ext, "%s <vlc:item tid=\"%i\" />\n", old_xspf_ext, p_sys->i_item_count - 1) == -1) p_sys->xspf_ext = NULL; free (old_xspf_ext); block_t *block = block_heap_Alloc (entry, len); if (unlikely(block == NULL)) goto fatal; return block; fatal: p_access->info.b_eof = true; return NULL; skip: free (entry); return NULL; }
/***************************************************************************** * Connect: *****************************************************************************/ static int Connect( stream_t *p_access ) { access_sys_t *p_sys = p_access->p_sys; vlc_url_t srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url; ssize_t val; /* Clean info */ free( p_sys->psz_location ); free( p_sys->psz_mime ); free( p_sys->psz_icy_genre ); free( p_sys->psz_icy_name ); free( p_sys->psz_icy_title ); vlc_http_auth_Init( &p_sys->auth ); vlc_http_auth_Init( &p_sys->proxy_auth ); p_sys->psz_location = NULL; p_sys->psz_mime = NULL; p_sys->i_icy_meta = 0; p_sys->i_icy_offset = 0; p_sys->psz_icy_name = NULL; p_sys->psz_icy_genre = NULL; p_sys->psz_icy_title = NULL; p_sys->b_has_size = false; p_sys->offset = 0; p_sys->size = 0; struct vlc_memstream stream; vlc_memstream_open(&stream); vlc_memstream_puts(&stream, "GET "); if( p_sys->b_proxy ) vlc_memstream_printf( &stream, "http://%s:%d", p_sys->url.psz_host, p_sys->url.i_port ); if( p_sys->url.psz_path == NULL || p_sys->url.psz_path[0] == '\0' ) vlc_memstream_putc( &stream, '/' ); else vlc_memstream_puts( &stream, p_sys->url.psz_path ); if( p_sys->url.psz_option != NULL ) vlc_memstream_printf( &stream, "?%s", p_sys->url.psz_option ); vlc_memstream_puts( &stream, " HTTP/1.0\r\n" ); vlc_memstream_printf( &stream, "Host: %s", p_sys->url.psz_host ); if( p_sys->url.i_port != 80 ) vlc_memstream_printf( &stream, ":%d", p_sys->url.i_port ); vlc_memstream_puts( &stream, "\r\n" ); /* User Agent */ vlc_memstream_printf( &stream, "User-Agent: %s\r\n", p_sys->psz_user_agent ); /* Referrer */ if (p_sys->psz_referrer) vlc_memstream_printf( &stream, "Referer: %s\r\n", p_sys->psz_referrer ); /* Authentication */ if( p_sys->url.psz_username != NULL && p_sys->url.psz_password != NULL ) { char *auth; auth = vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access), &p_sys->auth, "GET", p_sys->url.psz_path, p_sys->url.psz_username, p_sys->url.psz_password ); if( auth != NULL ) vlc_memstream_printf( &stream, "Authorization: %s\r\n", auth ); free( auth ); } /* Proxy Authentication */ if( p_sys->b_proxy && p_sys->proxy.psz_username != NULL && p_sys->proxy.psz_password != NULL ) { char *auth; auth = vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access), &p_sys->proxy_auth, "GET", p_sys->url.psz_path, p_sys->proxy.psz_username, p_sys->proxy.psz_password ); if( auth != NULL ) vlc_memstream_printf( &stream, "Proxy-Authorization: %s\r\n", auth ); free( auth ); } /* ICY meta data request */ vlc_memstream_puts( &stream, "Icy-MetaData: 1\r\n" ); vlc_memstream_puts( &stream, "\r\n" ); if( vlc_memstream_close( &stream ) ) return -1; /* Open connection */ assert(p_sys->stream == NULL); /* No open sockets (leaking fds is BAD) */ p_sys->stream = vlc_tls_SocketOpenTCP(VLC_OBJECT(p_access), srv.psz_host, srv.i_port); if (p_sys->stream == NULL) { msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port ); free( stream.ptr ); return -1; } msg_Dbg( p_access, "sending request:\n%s", stream.ptr ); val = vlc_tls_Write(p_sys->stream, stream.ptr, stream.length); free( stream.ptr ); if( val < (ssize_t)stream.length ) { msg_Err( p_access, "failed to send request" ); Disconnect( p_access ); return -2; } /* Read Answer */ char *psz = vlc_tls_GetLine(p_sys->stream); if( psz == NULL ) { msg_Err( p_access, "failed to read answer" ); goto error; } if( !strncmp( psz, "HTTP/1.", 7 ) ) { p_sys->i_code = atoi( &psz[9] ); msg_Dbg( p_access, "HTTP answer code %d", p_sys->i_code ); } else if( !strncmp( psz, "ICY", 3 ) ) { p_sys->i_code = atoi( &psz[4] ); msg_Dbg( p_access, "ICY answer code %d", p_sys->i_code ); p_sys->b_icecast = true; p_sys->b_reconnect = true; } else { msg_Err( p_access, "invalid HTTP reply '%s'", psz ); free( psz ); goto error; } /* Authentication error - We'll have to display the dialog */ if( p_sys->i_code == 401 ) { } /* Other fatal error */ else if( p_sys->i_code >= 400 ) { msg_Err( p_access, "error: %s", psz ); free( psz ); goto error; } free( psz ); for( ;; ) { char *p, *p_trailing; psz = vlc_tls_GetLine(p_sys->stream); if( psz == NULL ) { msg_Err( p_access, "failed to read answer" ); goto error; } /* msg_Dbg( p_input, "Line=%s", psz ); */ if( *psz == '\0' ) { free( psz ); break; } if( ( p = strchr( psz, ':' ) ) == NULL ) { msg_Err( p_access, "malformed header line: %s", psz ); free( psz ); goto error; } *p++ = '\0'; p += strspn( p, " \t" ); /* trim trailing white space */ p_trailing = p + strlen( p ); if( p_trailing > p ) { p_trailing--; while( ( *p_trailing == ' ' || *p_trailing == '\t' ) && p_trailing > p ) { *p_trailing = '\0'; p_trailing--; } } if( !strcasecmp( psz, "Content-Length" ) ) { uint64_t i_size = (uint64_t)atoll( p ); if(i_size > p_sys->size) { p_sys->b_has_size = true; p_sys->size = i_size; } } else if( !strcasecmp( psz, "Location" ) ) { char * psz_new_loc; /* This does not follow RFC 2068, but yet if the url is not absolute, * handle it as everyone does. */ if( p[0] == '/' ) { if( p_sys->url.i_port == 80 ) { if( asprintf(&psz_new_loc, "http://%s%s", p_sys->url.psz_host, p) < 0 ) goto error; } else { if( asprintf(&psz_new_loc, "http://%s:%d%s", p_sys->url.psz_host, p_sys->url.i_port, p) < 0 ) goto error; } } else { psz_new_loc = strdup( p ); } free( p_sys->psz_location ); p_sys->psz_location = psz_new_loc; } else if( !strcasecmp( psz, "Content-Type" ) ) { free( p_sys->psz_mime ); p_sys->psz_mime = strdup( p ); msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime ); } else if( !strcasecmp( psz, "Content-Encoding" ) ) { msg_Dbg( p_access, "Content-Encoding: %s", p ); } else if( !strcasecmp( psz, "Server" ) ) { msg_Dbg( p_access, "Server: %s", p ); if( !strncasecmp( p, "Icecast", 7 ) || !strncasecmp( p, "Nanocaster", 10 ) ) { /* Remember if this is Icecast * we need to force demux in this case without breaking * autodetection */ /* Let live 365 streams (nanocaster) piggyback on the icecast * routine. They look very similar */ p_sys->b_reconnect = true; p_sys->b_icecast = true; } } else if( !strcasecmp( psz, "Icy-MetaInt" ) ) { msg_Dbg( p_access, "Icy-MetaInt: %s", p ); p_sys->i_icy_meta = atoi( p ); if( p_sys->i_icy_meta < 0 ) p_sys->i_icy_meta = 0; if( p_sys->i_icy_meta > 1 ) { p_sys->i_icy_offset = p_sys->i_icy_meta; p_sys->b_icecast = true; } msg_Warn( p_access, "ICY metaint=%d", p_sys->i_icy_meta ); } else if( !strcasecmp( psz, "Icy-Name" ) ) { free( p_sys->psz_icy_name ); char *psz_tmp = strdup( p ); p_sys->psz_icy_name = EnsureUTF8( psz_tmp ); if( !p_sys->psz_icy_name ) free( psz_tmp ); else vlc_xml_decode( p_sys->psz_icy_name ); msg_Dbg( p_access, "Icy-Name: %s", p_sys->psz_icy_name ); if ( p_access->p_input_item ) input_item_SetMeta( p_access->p_input_item, vlc_meta_Title, p_sys->psz_icy_name ); p_sys->b_icecast = true; /* be on the safeside. set it here as well. */ p_sys->b_reconnect = true; } else if( !strcasecmp( psz, "Icy-Genre" ) ) { free( p_sys->psz_icy_genre ); char *psz_tmp = strdup( p ); p_sys->psz_icy_genre = EnsureUTF8( psz_tmp ); if( !p_sys->psz_icy_genre ) free( psz_tmp ); else vlc_xml_decode( p_sys->psz_icy_genre ); msg_Dbg( p_access, "Icy-Genre: %s", p_sys->psz_icy_genre ); if( p_access->p_input_item ) input_item_SetMeta( p_access->p_input_item, vlc_meta_Genre, p_sys->psz_icy_genre ); } else if( !strncasecmp( psz, "Icy-Notice", 10 ) ) { msg_Dbg( p_access, "Icy-Notice: %s", p ); } else if( !strncasecmp( psz, "icy-", 4 ) || !strncasecmp( psz, "ice-", 4 ) || !strncasecmp( psz, "x-audiocast", 11 ) ) { msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p ); } else if( !strcasecmp( psz, "www-authenticate" ) ) { msg_Dbg( p_access, "Authentication header: %s", p ); vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access), &p_sys->auth, p ); } else if( !strcasecmp( psz, "proxy-authenticate" ) ) { msg_Dbg( p_access, "Proxy authentication header: %s", p ); vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access), &p_sys->proxy_auth, p ); } else if( !strcasecmp( psz, "authentication-info" ) ) { msg_Dbg( p_access, "Authentication Info header: %s", p ); if( AuthCheckReply( p_access, p, &p_sys->url, &p_sys->auth ) ) goto error; } else if( !strcasecmp( psz, "proxy-authentication-info" ) ) { msg_Dbg( p_access, "Proxy Authentication Info header: %s", p ); if( AuthCheckReply( p_access, p, &p_sys->proxy, &p_sys->proxy_auth ) ) goto error; } free( psz ); } return 0; error: Disconnect( p_access ); return -2; }
static void ParseAPEvXTag( demux_meta_t *p_demux_meta, const uint8_t *p_data, int i_data ) { vlc_meta_t *p_meta; bool b_start; bool b_end; const uint8_t *p_header = NULL; int i_entry; if( i_data < APE_TAG_HEADERSIZE ) return; b_start = !strncmp( (char*)&p_data[0], "APETAGEX", 8 ); b_end = !strncmp( (char*)&p_data[i_data-APE_TAG_HEADERSIZE], "APETAGEX", 8 ); if( !b_end && !b_start ) return; if( !p_demux_meta->p_meta ) p_demux_meta->p_meta = vlc_meta_New(); p_meta = p_demux_meta->p_meta; if( b_start ) { p_header = &p_data[0]; p_data += APE_TAG_HEADERSIZE; i_data -= APE_TAG_HEADERSIZE; } if( b_end ) { p_header = &p_data[i_data-APE_TAG_HEADERSIZE]; i_data -= APE_TAG_HEADERSIZE; } if( i_data <= 0 ) return; i_entry = GetDWLE( &p_header[8+4+4] ); if( i_entry <= 0 ) return; while( i_entry > 0 && i_data >= 10 ) { const int i_size = GetDWLE( &p_data[0] ); const uint32_t flags = GetDWLE( &p_data[4] ); char psz_name[256]; int n; strlcpy( psz_name, (char*)&p_data[8], sizeof(psz_name) ); n = strlen( psz_name ); if( n <= 0 ) break; p_data += 8+n+1; i_data -= 8+n+1; if( i_data < i_size ) break; /* Retreive UTF-8 fields only */ if( ((flags>>1) & 0x03) == 0x00 ) { /* FIXME list are separated by '\0' */ char *psz_value = strndup( (char*)&p_data[0], i_size ); EnsureUTF8( psz_name ); EnsureUTF8( psz_value ); #define IS(s) (!strcasecmp( psz_name, s ) ) if( IS( "Title" ) ) vlc_meta_SetTitle( p_meta, psz_value ); else if( IS( "Artist" ) ) vlc_meta_SetArtist( p_meta, psz_value ); else if( IS( "Album" ) ) vlc_meta_SetAlbum( p_meta, psz_value ); else if( IS( "Publisher" ) ) vlc_meta_SetPublisher( p_meta, psz_value ); else if( IS( "Track" ) ) { char *p = strchr( psz_value, '/' ); if( p ) *p++ = '\0'; vlc_meta_SetTrackNum( p_meta, psz_value ); } else if( IS( "Comment" ) ) vlc_meta_SetDescription( p_meta, psz_value ); else if( IS( "Copyright" ) ) vlc_meta_SetCopyright( p_meta, psz_value ); else if( IS( "Year" ) ) vlc_meta_SetDate( p_meta, psz_value ); else if( IS( "Genre" ) ) vlc_meta_SetGenre( p_meta, psz_value ); else if( IS( "Language" ) ) vlc_meta_SetLanguage( p_meta, psz_value ); else vlc_meta_AddExtra( p_meta, psz_name, psz_value ); #undef IS free( psz_value ); } p_data += i_size; i_data -= i_size; i_entry--; } }