static void ParseSpeexComments( decoder_t *p_dec, ogg_packet *p_oggpacket ) { decoder_sys_t *p_sys = p_dec->p_sys; const SpeexMode *p_mode; assert( p_sys->p_header->mode < SPEEX_NB_MODES ); p_mode = speex_mode_list[p_sys->p_header->mode]; assert( p_mode != NULL ); if( !p_dec->p_description ) { p_dec->p_description = vlc_meta_New(); if( !p_dec->p_description ) return; } /* */ char *psz_mode; if( asprintf( &psz_mode, "%s%s", p_mode->modeName, p_sys->p_header->vbr ? " VBR" : "" ) >= 0 ) { vlc_meta_AddExtra( p_dec->p_description, _("Mode"), psz_mode ); free( psz_mode ); } /* TODO: finish comments parsing */ VLC_UNUSED( p_oggpacket ); }
void input_item_SetPreparsed( input_item_t *p_i, bool b_preparsed ) { bool b_send_event = false; vlc_mutex_lock( &p_i->lock ); if( !p_i->p_meta ) p_i->p_meta = vlc_meta_New(); int status = vlc_meta_GetStatus(p_i->p_meta); int new_status; if( b_preparsed ) new_status = status | ITEM_PREPARSED; else new_status = status & ~ITEM_PREPARSED; if( status != new_status ) { vlc_meta_SetStatus(p_i->p_meta, new_status); b_send_event = true; } vlc_mutex_unlock( &p_i->lock ); if( b_send_event ) { vlc_event_t event; event.type = vlc_InputItemPreparsedChanged; event.u.input_item_preparsed_changed.new_status = new_status; vlc_event_send( &p_i->event_manager, &event ); } }
void formatSnapshotItem( input_item_t *p_item ) { if( !p_item ) return; if( !p_item->p_meta ) p_item->p_meta = vlc_meta_New(); /* copy the snapshot mrl as a ArtURL */ if( p_item->p_meta ) { char* psz_uri = NULL; psz_uri = input_item_GetURI( p_item ); if( psz_uri ) input_item_SetArtURL( p_item, psz_uri ); free( psz_uri ); } /** * TODO: select the best mrl for displaying snapshots * - vlc://pause:10 => snapshot are displayed as Art * - file:///path/image.ext => snapshot are displayed as videos **/ input_item_SetURI( p_item, "vlc://pause:10" ); // input_item_AddOption( p_item, "fake-duration=10000", // VLC_INPUT_OPTION_TRUSTED ); }
/***************************************************************************** * ParseTheoraComments: *****************************************************************************/ static void ParseTheoraComments( decoder_t *p_dec ) { char *psz_name, *psz_value, *psz_comment; int i = 0; while ( i < p_dec->p_sys->tc.comments ) { psz_comment = strdup( p_dec->p_sys->tc.user_comments[i] ); if( !psz_comment ) break; psz_name = psz_comment; psz_value = strchr( psz_comment, '=' ); if( psz_value ) { *psz_value = '\0'; psz_value++; if( !p_dec->p_description ) p_dec->p_description = vlc_meta_New(); if( p_dec->p_description ) vlc_meta_AddExtra( p_dec->p_description, psz_name, psz_value ); } free( psz_comment ); i++; } }
static char *ArtCachePath( input_item_t *p_item ) { char psz_path[PATH_MAX+1]; /* FIXME */ vlc_mutex_lock( &p_item->lock ); if( !p_item->p_meta ) p_item->p_meta = vlc_meta_New(); if( !p_item->p_meta ) { vlc_mutex_unlock( &p_item->lock ); return NULL; } const char *psz_artist = vlc_meta_Get( p_item->p_meta, vlc_meta_Artist ); const char *psz_album = vlc_meta_Get( p_item->p_meta, vlc_meta_Album ); const char *psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title ); if( !psz_title ) psz_title = p_item->psz_name; if( (!psz_artist || !psz_album ) && !psz_title ) { vlc_mutex_unlock( &p_item->lock ); return NULL; } ArtCacheGetDirPath( psz_path, psz_title, psz_artist, psz_album ); vlc_mutex_unlock( &p_item->lock ); return strdup( psz_path ); }
static char *ArtCachePath( input_item_t *p_item ) { char* psz_path = NULL; const char *psz_artist; const char *psz_album; const char *psz_arturl; const char *psz_title; vlc_mutex_lock( &p_item->lock ); if( !p_item->p_meta ) p_item->p_meta = vlc_meta_New(); if( !p_item->p_meta ) goto end; psz_artist = vlc_meta_Get( p_item->p_meta, vlc_meta_Artist ); psz_album = vlc_meta_Get( p_item->p_meta, vlc_meta_Album ); psz_arturl = vlc_meta_Get( p_item->p_meta, vlc_meta_ArtworkURL ); psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title ); if( !psz_title ) psz_title = p_item->psz_name; if( (EMPTY_STR(psz_artist) || EMPTY_STR(psz_album) ) && !psz_arturl ) goto end; psz_path = ArtCacheGetDirPath( psz_arturl, psz_artist, psz_album, psz_title ); end: vlc_mutex_unlock( &p_item->lock ); return psz_path; }
/***************************************************************************** * InformationCreate: *****************************************************************************/ void matroska_segment_c::InformationCreate( ) { if( !sys.meta ) sys.meta = vlc_meta_New(); if( psz_title ) { vlc_meta_SetTitle( sys.meta, psz_title ); } }
void input_item_SetArtFetched( input_item_t *p_i, bool b_art_fetched ) { vlc_mutex_lock( &p_i->lock ); if( !p_i->p_meta ) p_i->p_meta = vlc_meta_New(); if( b_art_fetched ) p_i->p_meta->i_status |= ITEM_ART_FETCHED; else p_i->p_meta->i_status &= ~ITEM_ART_FETCHED; vlc_mutex_unlock( &p_i->lock ); }
void input_item_SetArtNotFound( input_item_t *p_i, bool b_not_found ) { vlc_mutex_lock( &p_i->lock ); if( !p_i->p_meta ) p_i->p_meta = vlc_meta_New(); if( b_not_found ) p_i->p_meta->i_status |= ITEM_ART_NOTFOUND; else p_i->p_meta->i_status &= ~ITEM_ART_NOTFOUND; vlc_mutex_unlock( &p_i->lock ); }
void input_item_SetMeta( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz_val ) { vlc_event_t event; vlc_mutex_lock( &p_i->lock ); if( !p_i->p_meta ) p_i->p_meta = vlc_meta_New(); vlc_meta_Set( p_i->p_meta, meta_type, psz_val ); vlc_mutex_unlock( &p_i->lock ); /* Notify interested third parties */ event.type = vlc_InputItemMetaChanged; event.u.input_item_meta_changed.meta_type = meta_type; vlc_event_send( &p_i->event_manager, &event ); }
/***************************************************************************** * ParseTheoraComments: *****************************************************************************/ static void ParseTheoraComments( decoder_t *p_dec ) { char *psz_name, *psz_value, *psz_comment; int i = 0; decoder_sys_t *p_sys = p_dec->p_sys; /* Regarding the th_comment structure: */ /* The metadata is stored as a series of (tag, value) pairs, in length-encoded string vectors. The first occurrence of the '=' character delimits the tag and value. A particular tag may occur more than once, and order is significant. The character set encoding for the strings is always UTF-8, but the tag names are limited to ASCII, and treated as case-insensitive. See the Theora specification, Section 6.3.3 for details. */ /* In filling in this structure, th_decode_headerin() will null-terminate the user_comment strings for safety. However, the bitstream format itself treats them as 8-bit clean vectors, possibly containing null characters, and so the length array should be treated as their authoritative length. */ while ( i < p_sys->tc.comments ) { int clen = p_sys->tc.comment_lengths[i]; if ( clen <= 0 || clen >= INT_MAX ) { i++; continue; } psz_comment = (char *)malloc( clen + 1 ); if( !psz_comment ) break; memcpy( (void*)psz_comment, (void*)p_sys->tc.user_comments[i], clen + 1 ); psz_name = psz_comment; psz_value = strchr( psz_comment, '=' ); if( psz_value ) { *psz_value = '\0'; psz_value++; if( !p_dec->p_description ) p_dec->p_description = vlc_meta_New(); /* TODO: Since psz_value can contain NULLs see if there is an * instance where we need to preserve the full length of this string */ if( p_dec->p_description ) vlc_meta_AddExtra( p_dec->p_description, psz_name, psz_value ); } free( psz_comment ); i++; } }
void input_item_SetArtFetched( input_item_t *p_i, bool b_art_fetched ) { vlc_mutex_lock( &p_i->lock ); if( !p_i->p_meta ) p_i->p_meta = vlc_meta_New(); int status = vlc_meta_GetStatus(p_i->p_meta); if( b_art_fetched ) status |= ITEM_ART_FETCHED; else status &= ~ITEM_ART_FETCHED; vlc_meta_SetStatus(p_i->p_meta, status); vlc_mutex_unlock( &p_i->lock ); }
void input_item_SetArtNotFound( input_item_t *p_i, bool b_not_found ) { vlc_mutex_lock( &p_i->lock ); if( !p_i->p_meta ) p_i->p_meta = vlc_meta_New(); int status = vlc_meta_GetStatus(p_i->p_meta); if( b_not_found ) status |= ITEM_ART_NOTFOUND; else status &= ~ITEM_ART_NOTFOUND; vlc_meta_SetStatus(p_i->p_meta, status); vlc_mutex_unlock( &p_i->lock ); }
static void fill_metas_with_results( fingerprint_request_t *p_r, acoustid_fingerprint_t *p_f ) { for( unsigned int i=0 ; i < p_f->results.count; i++ ) { acoustid_result_t *p_result = & p_f->results.p_results[ i ]; for ( unsigned int j=0 ; j < p_result->recordings.count; j++ ) { musicbrainz_recording_t *p_record = & p_result->recordings.p_recordings[ j ]; vlc_meta_t *p_meta = vlc_meta_New(); if ( p_meta ) { vlc_meta_Set( p_meta, vlc_meta_Title, p_record->psz_title ); vlc_meta_Set( p_meta, vlc_meta_Artist, p_record->psz_artist ); vlc_meta_AddExtra( p_meta, "musicbrainz-id", p_record->s_musicbrainz_id ); vlc_array_append( & p_r->results.metas_array, p_meta ); } } } }
void vlclua_read_custom_meta_data( vlc_object_t *p_this, lua_State *L, input_item_t *p_input ) { /* Lock the input item and create the meta table if needed */ vlc_mutex_lock( &p_input->lock ); if( !p_input->p_meta ) p_input->p_meta = vlc_meta_New(); /* ... item */ lua_getfield( L, -1, "meta" ); /* ... item meta */ if( lua_istable( L, -1 ) ) { lua_pushnil( L ); /* ... item meta nil */ while( lua_next( L, -2 ) ) { /* ... item meta key value */ if( !lua_isstring( L, -2 ) || !lua_isstring( L, -1 ) ) { msg_Err( p_this, "'meta' keys and values must be strings"); lua_pop( L, 1 ); /* pop "value" */ continue; } const char *psz_key = lua_tostring( L, -2 ); const char *psz_value = lua_tostring( L, -1 ); vlc_meta_AddExtra( p_input->p_meta, psz_key, psz_value ); lua_pop( L, 1 ); /* pop "value" */ } } lua_pop( L, 1 ); /* pop "meta" */ /* ... item -> back to original stack */ vlc_mutex_unlock( &p_input->lock ); }
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; }
/***************************************************************************** * Import meta data *****************************************************************************/ static void ImportMeta( access_t *p_access ) { access_sys_t *p_sys = p_access->p_sys; FILE *infofile = OpenRelativeFile( p_access, "info" ); if( !infofile ) return; vlc_meta_t *p_meta = vlc_meta_New(); p_sys->p_meta = p_meta; if( !p_meta ) { fclose( infofile ); return; } char *line = NULL; size_t line_len; char *psz_title = NULL, *psz_smalltext = NULL, *psz_date = NULL; while( ReadLine( &line, &line_len, infofile ) ) { if( !isalpha( (unsigned char)line[0] ) || line[1] != ' ' ) continue; char tag = line[0]; char *text = line + 2; if( tag == 'C' ) { char *psz_name = strchr( text, ' ' ); if( psz_name ) { *psz_name = '\0'; vlc_meta_AddExtra( p_meta, "Channel", psz_name + 1 ); } vlc_meta_AddExtra( p_meta, "Transponder", text ); } else if( tag == 'E' ) { unsigned i_id, i_start, i_length; if( sscanf( text, "%u %u %u", &i_id, &i_start, &i_length ) == 3 ) { char str[50]; struct tm tm; time_t start = i_start; localtime_r( &start, &tm ); /* TODO: locale */ strftime( str, sizeof(str), "%Y-%m-%d %H:%M", &tm ); vlc_meta_AddExtra( p_meta, "Date", str ); free( psz_date ); psz_date = strdup( str ); /* display in minutes */ i_length = ( i_length + 59 ) / 60; snprintf( str, sizeof(str), "%u:%02u", i_length / 60, i_length % 60 ); vlc_meta_AddExtra( p_meta, "Duration", str ); } } else if( tag == 'T' ) { free( psz_title ); psz_title = strdup( text ); vlc_meta_AddExtra( p_meta, "Title", text ); } else if( tag == 'S' ) { free( psz_smalltext ); psz_smalltext = strdup( text ); vlc_meta_AddExtra( p_meta, "Info", text ); } else if( tag == 'D' ) { for( char *p = text; *p; ++p ) { if( *p == '|' ) *p = '\n'; } vlc_meta_SetDescription( p_meta, text ); } /* FPS are required to convert between timestamps and frames */ else if( tag == 'F' ) { float fps = atof( text ); if( fps >= 1 ) p_sys->fps = fps; vlc_meta_AddExtra( p_meta, "Frame Rate", text ); } else if( tag == 'P' ) { vlc_meta_AddExtra( p_meta, "Priority", text ); } else if( tag == 'L' ) { vlc_meta_AddExtra( p_meta, "Lifetime", text ); } } /* create a meaningful title */ int i_len = 10 + ( psz_title ? strlen( psz_title ) : 0 ) + ( psz_smalltext ? strlen( psz_smalltext ) : 0 ) + ( psz_date ? strlen( psz_date ) : 0 ); char *psz_display = malloc( i_len ); if( psz_display ) { *psz_display = '\0'; if( psz_title ) strcat( psz_display, psz_title ); if( psz_title && psz_smalltext ) strcat( psz_display, " - " ); if( psz_smalltext ) strcat( psz_display, psz_smalltext ); if( ( psz_title || psz_smalltext ) && psz_date ) { strcat( psz_display, " (" ); strcat( psz_display, psz_date ); strcat( psz_display, ")" ); } if( *psz_display ) vlc_meta_SetTitle( p_meta, psz_display ); } free( psz_display ); free( psz_title ); free( psz_smalltext ); free( psz_date ); fclose( infofile ); }
bool matroska_segment_c::ParseSimpleTags( SimpleTag* pout_simple, KaxTagSimple *tag, int target_type ) { EbmlParser eparser ( &es, tag, &sys.demuxer, var_InheritBool( &sys.demuxer, "mkv-use-dummy" ) ); EbmlElement *el; size_t max_size = tag->GetSize(); size_t size = 0; if( !sys.meta ) sys.meta = vlc_meta_New(); msg_Dbg( &sys.demuxer, "| + Simple Tag "); try { while( ( el = eparser.Get() ) != NULL && size < max_size) { if( unlikely( !el->ValidateSize() ) ) { msg_Err( &sys.demuxer, "Error %s too big ignoring the tag", typeid(*el).name() ); delete ep; return false; } if( MKV_CHECKED_PTR_DECL ( ktn_ptr, KaxTagName, el ) ) { ktn_ptr->ReadData( es.I_O(), SCOPE_ALL_DATA ); pout_simple->tag_name = UTFstring( *ktn_ptr ).GetUTF8().c_str(); } else if( MKV_CHECKED_PTR_DECL ( kts_ptr, KaxTagString, el ) ) { kts_ptr->ReadData( es.I_O(), SCOPE_ALL_DATA ); pout_simple->value = UTFstring( *kts_ptr ).GetUTF8().c_str(); } else if( MKV_CHECKED_PTR_DECL ( ktl_ptr, KaxTagLangue, el ) ) { ktl_ptr->ReadData( es.I_O(), SCOPE_ALL_DATA ); pout_simple->lang = *ktl_ptr; } else if( MKV_CHECKED_PTR_DECL ( ktd_ptr, KaxTagDefault, el ) ) { VLC_UNUSED(ktd_ptr); // TODO: we do not care about this value, but maybe we should? } /*Tags can be nested*/ else if( MKV_CHECKED_PTR_DECL ( kts_ptr, KaxTagSimple, el) ) { SimpleTag st; // ParseSimpleTags will write to this variable // the SimpleTag is valid if ParseSimpleTags returns `true` if (ParseSimpleTags( &st, kts_ptr, target_type )) { pout_simple->sub_tags.push_back( st ); } } /*TODO Handle binary tags*/ size += el->HeadSize() + el->GetSize(); } } catch(...) { msg_Err( &sys.demuxer, "Error while reading Tag "); delete ep; return false; } if( pout_simple->tag_name.empty() ) { msg_Warn( &sys.demuxer, "Invalid MKV SimpleTag found."); return false; } for( int i = 0; metadata_map[i].key; i++ ) { if( pout_simple->tag_name == metadata_map[i].key && (metadata_map[i].target_type == 0 || target_type == metadata_map[i].target_type ) ) { vlc_meta_Set( sys.meta, metadata_map[i].type, pout_simple->value.c_str () ); msg_Dbg( &sys.demuxer, "| | + Meta %s: %s", pout_simple->tag_name.c_str (), pout_simple->value.c_str ()); goto done; } } msg_Dbg( &sys.demuxer, "| | + Meta %s: %s", pout_simple->tag_name.c_str (), pout_simple->value.c_str ()); vlc_meta_AddExtra( sys.meta, pout_simple->tag_name.c_str (), pout_simple->value.c_str ()); done: return true; }
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 ); }
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--; } }
/***************************************************************************** * ParseVorbisComments *****************************************************************************/ static void ParseVorbisComments( decoder_t *p_dec ) { char *psz_name, *psz_value, *psz_comment; int i = 0; while( i < p_dec->p_sys->vc.comments ) { psz_comment = strdup( p_dec->p_sys->vc.user_comments[i] ); if( !psz_comment ) break; psz_name = psz_comment; psz_value = strchr( psz_comment, '=' ); /* Don't add empty values */ if( psz_value && psz_value[1] != '\0') { *psz_value = '\0'; psz_value++; if( !strcasecmp( psz_name, "REPLAYGAIN_TRACK_GAIN" ) || !strcasecmp( psz_name, "RG_RADIO" ) ) { audio_replay_gain_t *r = &p_dec->fmt_out.audio_replay_gain; r->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true; r->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = us_atof( psz_value ); } else if( !strcasecmp( psz_name, "REPLAYGAIN_TRACK_PEAK" ) || !strcasecmp( psz_name, "RG_PEAK" ) ) { audio_replay_gain_t *r = &p_dec->fmt_out.audio_replay_gain; r->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true; r->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = us_atof( psz_value ); } else if( !strcasecmp( psz_name, "REPLAYGAIN_ALBUM_GAIN" ) || !strcasecmp( psz_name, "RG_AUDIOPHILE" ) ) { audio_replay_gain_t *r = &p_dec->fmt_out.audio_replay_gain; r->pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true; r->pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = us_atof( psz_value ); } else if( !strcasecmp( psz_name, "REPLAYGAIN_ALBUM_PEAK" ) ) { audio_replay_gain_t *r = &p_dec->fmt_out.audio_replay_gain; r->pb_peak[AUDIO_REPLAY_GAIN_ALBUM] = true; r->pf_peak[AUDIO_REPLAY_GAIN_ALBUM] = us_atof( psz_value ); } else if( !strcasecmp( psz_name, "METADATA_BLOCK_PICTURE" ) ) { /* Do nothing, for now */ } else { if( !p_dec->p_description ) p_dec->p_description = vlc_meta_New(); if( p_dec->p_description ) vlc_meta_AddExtra( p_dec->p_description, psz_name, psz_value ); } } free( psz_comment ); i++; } }
/***************************************************************************** * ParseID3Tag : parse an id3tag into the info structures *****************************************************************************/ static void ParseID3Tag( demux_meta_t *p_demux_meta, const uint8_t *p_data, int i_size ) { struct id3_tag *p_id3_tag; struct id3_frame *p_frame; vlc_meta_t *p_meta; int i; p_id3_tag = id3_tag_parse( p_data, i_size ); if( !p_id3_tag ) return; if( !p_demux_meta->p_meta ) p_demux_meta->p_meta = vlc_meta_New(); p_meta = p_demux_meta->p_meta; #define ID_IS( a ) (!strcmp( p_frame->id, a )) #define DESCR_IS( a) strstr( (char*)p_frame->description, a ) #define GET_STRING(frame,fidx) id3_ucs4_latin1duplicate( id3_field_getstring( &(frame)->fields[fidx] ) ) /* */ for( i = 0; (p_frame = id3_tag_findframe( p_id3_tag, "UFID", i )) != NULL; i++ ) { const char *psz_owner = id3_field_getlatin1( &p_frame->fields[0] ); if( !strncmp( psz_owner, "http://musicbrainz.org", 22 ) ) { id3_byte_t const * p_ufid; id3_length_t i_ufidlen; p_ufid = id3_field_getbinarydata( &p_frame->fields[1], &i_ufidlen ); char *psz_ufid = strndup( p_ufid, i_ufidlen ); vlc_meta_SetTrackID( p_meta, psz_ufid ); free( psz_ufid ); } } /* User defined text (TXXX) */ for( i = 0; (p_frame = id3_tag_findframe( p_id3_tag, "TXXX", i )) != NULL; i++ ) { /* 3 fields: 'encoding', 'description', 'value' */ char *psz_name = GET_STRING( p_frame, 1 ); char *psz_value = GET_STRING( p_frame, 2 ); vlc_meta_AddExtra( p_meta, psz_name, psz_value ); #if 0 if( !strncmp( psz_name, "MusicBrainz Artist Id", 21 ) ) vlc_meta_SetArtistID( p_meta, psz_value ); if( !strncmp( psz_desc, "MusicBrainz Album Id", 20 ) ) vlc_meta_SetAlbumID( p_meta, psz_value ); #endif free( psz_name ); free( psz_value ); } /* Relative volume adjustment */ for( i = 0; (p_frame = id3_tag_findframe( p_id3_tag, "RVA2", i )) != NULL; i++ ) { /* 2 fields: 'latin1', 'binary' */ const char *psz_type = id3_field_getlatin1( &p_frame->fields[0] ); if( !strcasecmp( psz_type, "track" ) || !strcasecmp( psz_type, "album" ) || !strcasecmp( psz_type, "normalize" ) ) { id3_byte_t const * p_data; id3_length_t i_data; p_data = id3_field_getbinarydata( &p_frame->fields[1], &i_data ); while( i_data >= 4 ) { const unsigned int i_peak_size = p_data[3]; const float f_temp = GetWBE( &p_data[1] ); const float f_gain = f_temp / 512.0; char psz_value[32]; if( i_data < i_peak_size + 4 ) break; /* only master volume */ if( p_data[0] == 0x01 ) { snprintf( psz_value, sizeof(psz_value), "%f", f_gain ); if( !strcasecmp( psz_type, "album" ) ) vlc_meta_AddExtra( p_meta, "REPLAYGAIN_ALBUM_GAIN", psz_value ); else vlc_meta_AddExtra( p_meta, "REPLAYGAIN_TRACK_GAIN", psz_value ); /* XXX I have no idea what peak unit is ... */ } i_data -= 4+i_peak_size; } } } /* TODO 'RGAD' if it is used somewhere */ /* T--- Text informations */ for( i = 0; (p_frame = id3_tag_findframe( p_id3_tag, "T", i )) != NULL; i++ ) { unsigned i_strings; /* Special case TXXX is not the same beast */ if( ID_IS( "TXXX" ) ) continue; i_strings = id3_field_getnstrings( &p_frame->fields[1] ); while( i_strings > 0 ) { char *psz_temp = id3_ucs4_utf8duplicate( id3_field_getstrings( &p_frame->fields[1], --i_strings ) ); if( ID_IS( ID3_FRAME_GENRE ) ) { char *psz_endptr; int i_genre = strtol( psz_temp, &psz_endptr, 10 ); if( psz_temp != psz_endptr && i_genre >= 0 && i_genre < NUM_GENRES ) { vlc_meta_SetGenre( p_meta, ppsz_genres[atoi(psz_temp)]); } else { /* Unknown genre */ vlc_meta_SetGenre( p_meta,psz_temp ); } } else if( ID_IS( ID3_FRAME_TITLE ) ) { vlc_meta_SetTitle( p_meta, psz_temp ); } else if( ID_IS( ID3_FRAME_ARTIST ) ) { vlc_meta_SetArtist( p_meta, psz_temp ); } else if( ID_IS( ID3_FRAME_YEAR ) ) { vlc_meta_SetDate( p_meta, psz_temp ); } else if( ID_IS( ID3_FRAME_COMMENT ) ) { vlc_meta_SetDescription( p_meta, psz_temp ); } else if( DESCR_IS( "Copyright" ) ) { vlc_meta_SetCopyright( p_meta, psz_temp ); } else if( DESCR_IS( "Publisher" ) ) { vlc_meta_SetPublisher( p_meta, psz_temp ); } else if( DESCR_IS( "Track number/position in set" ) ) { vlc_meta_SetTrackNum( p_meta, psz_temp ); } else if( DESCR_IS( "Album/movie/show title" ) ) { vlc_meta_SetAlbum( p_meta, psz_temp ); } else if( DESCR_IS( "Encoded by" ) ) { vlc_meta_SetEncodedBy( p_meta, psz_temp ); } else if( ID_IS ( "APIC" ) ) { msg_Dbg( p_demux_meta, "** Has APIC **" ); } else if( p_frame->description ) { /* Unhandled meta */ vlc_meta_AddExtra( p_meta, (char*)p_frame->description, psz_temp ); } free( psz_temp ); } } id3_tag_delete( p_id3_tag ); #undef GET_STRING #undef DESCR_IS #undef ID_IS }
/***************************************************************************** * DecodeBlock: *****************************************************************************/ static aout_buffer_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block; if( !pp_block || !*pp_block ) return NULL; p_block = *pp_block; if( p_block->i_flags&(BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { block_Release( p_block ); return NULL; } /* Remove ADTS header if we have decoder specific config */ if( p_dec->fmt_in.i_extra && p_block->i_buffer > 7 ) { if( p_block->p_buffer[0] == 0xff && ( p_block->p_buffer[1] & 0xf0 ) == 0xf0 ) /* syncword */ { /* ADTS header present */ size_t i_header_size; /* 7 bytes (+ 2 bytes for crc) */ i_header_size = 7 + ( ( p_block->p_buffer[1] & 0x01 ) ? 0 : 2 ); /* FIXME: multiple blocks per frame */ if( p_block->i_buffer > i_header_size ) { vlc_memcpy( p_block->p_buffer, p_block->p_buffer + i_header_size, p_block->i_buffer - i_header_size ); p_block->i_buffer -= i_header_size; } } } /* Append the block to the temporary buffer */ if( p_sys->i_buffer_size < p_sys->i_buffer + p_block->i_buffer ) { p_sys->i_buffer_size = p_sys->i_buffer + p_block->i_buffer; p_sys->p_buffer = realloc( p_sys->p_buffer, p_sys->i_buffer_size ); } if( p_block->i_buffer > 0 ) { vlc_memcpy( &p_sys->p_buffer[p_sys->i_buffer], p_block->p_buffer, p_block->i_buffer ); p_sys->i_buffer += p_block->i_buffer; p_block->i_buffer = 0; } if( p_dec->fmt_out.audio.i_rate == 0 && p_dec->fmt_in.i_extra > 0 ) { /* We have a decoder config so init the handle */ unsigned long i_rate; unsigned char i_channels; if( faacDecInit2( p_sys->hfaad, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra, &i_rate, &i_channels ) >= 0 ) { p_dec->fmt_out.audio.i_rate = i_rate; p_dec->fmt_out.audio.i_channels = i_channels; p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels = pi_channels_guessed[i_channels]; aout_DateInit( &p_sys->date, i_rate ); } } if( p_dec->fmt_out.audio.i_rate == 0 && p_sys->i_buffer ) { unsigned long i_rate; unsigned char i_channels; /* Init faad with the first frame */ if( faacDecInit( p_sys->hfaad, p_sys->p_buffer, p_sys->i_buffer, &i_rate, &i_channels ) < 0 ) { block_Release( p_block ); return NULL; } p_dec->fmt_out.audio.i_rate = i_rate; p_dec->fmt_out.audio.i_channels = i_channels; p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels = pi_channels_guessed[i_channels]; aout_DateInit( &p_sys->date, i_rate ); } if( p_block->i_pts != 0 && p_block->i_pts != aout_DateGet( &p_sys->date ) ) { aout_DateSet( &p_sys->date, p_block->i_pts ); } else if( !aout_DateGet( &p_sys->date ) ) { /* We've just started the stream, wait for the first PTS. */ block_Release( p_block ); p_sys->i_buffer = 0; return NULL; } /* Decode all data */ if( p_sys->i_buffer ) { void *samples; faacDecFrameInfo frame; aout_buffer_t *p_out; int i, j; samples = faacDecDecode( p_sys->hfaad, &frame, p_sys->p_buffer, p_sys->i_buffer ); if( frame.error > 0 ) { msg_Warn( p_dec, "%s", faacDecGetErrorMessage( frame.error ) ); /* Flush the buffer */ p_sys->i_buffer = 0; block_Release( p_block ); return NULL; } if( frame.channels <= 0 || frame.channels > 8 || frame.channels == 7 ) { msg_Warn( p_dec, "invalid channels count: %i", frame.channels ); /* Flush the buffer */ p_sys->i_buffer -= frame.bytesconsumed; if( p_sys->i_buffer > 0 ) { memmove( p_sys->p_buffer,&p_sys->p_buffer[frame.bytesconsumed], p_sys->i_buffer ); } block_Release( p_block ); return NULL; } if( frame.samples <= 0 ) { msg_Warn( p_dec, "decoded zero sample" ); /* Flush the buffer */ p_sys->i_buffer -= frame.bytesconsumed; if( p_sys->i_buffer > 0 ) { memmove( p_sys->p_buffer,&p_sys->p_buffer[frame.bytesconsumed], p_sys->i_buffer ); } block_Release( p_block ); return NULL; } /* We decoded a valid frame */ if( p_dec->fmt_out.audio.i_rate != frame.samplerate ) { aout_DateInit( &p_sys->date, frame.samplerate ); aout_DateSet( &p_sys->date, p_block->i_pts ); } p_block->i_pts = 0; /* PTS is valid only once */ p_dec->fmt_out.audio.i_rate = frame.samplerate; p_dec->fmt_out.audio.i_channels = frame.channels; p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels = pi_channels_guessed[frame.channels]; /* Adjust stream info when dealing with SBR/PS */ if( p_sys->b_sbr != frame.sbr || p_sys->b_ps != frame.ps ) { const char *psz_ext = (frame.sbr && frame.ps) ? "SBR+PS" : frame.sbr ? "SBR" : "PS"; msg_Dbg( p_dec, "AAC %s (channels: %u, samplerate: %lu)", psz_ext, frame.channels, frame.samplerate ); if( !p_dec->p_description ) p_dec->p_description = vlc_meta_New(); if( p_dec->p_description ) vlc_meta_AddExtra( p_dec->p_description, _("AAC extension"), psz_ext ); p_sys->b_sbr = frame.sbr; p_sys->b_ps = frame.ps; } /* Convert frame.channel_position to our own channel values */ p_dec->fmt_out.audio.i_physical_channels = 0; for( i = 0; i < frame.channels; i++ ) { /* Find the channel code */ for( j = 0; j < MAX_CHANNEL_POSITIONS; j++ ) { if( frame.channel_position[i] == pi_channels_in[j] ) break; } if( j >= MAX_CHANNEL_POSITIONS ) { msg_Warn( p_dec, "unknown channel ordering" ); /* Invent something */ j = i; } /* */ p_sys->pi_channel_positions[i] = pi_channels_out[j]; if( p_dec->fmt_out.audio.i_physical_channels & pi_channels_out[j] ) frame.channels--; /* We loose a duplicated channel */ else p_dec->fmt_out.audio.i_physical_channels |= pi_channels_out[j]; } p_dec->fmt_out.audio.i_original_channels = p_dec->fmt_out.audio.i_physical_channels; p_out = decoder_NewAudioBuffer(p_dec, frame.samples/frame.channels); if( p_out == NULL ) { p_sys->i_buffer = 0; block_Release( p_block ); return NULL; } p_out->start_date = aout_DateGet( &p_sys->date ); p_out->end_date = aout_DateIncrement( &p_sys->date, frame.samples / frame.channels ); DoReordering( (uint32_t *)p_out->p_buffer, samples, frame.samples / frame.channels, frame.channels, p_sys->pi_channel_positions ); p_sys->i_buffer -= frame.bytesconsumed; if( p_sys->i_buffer > 0 ) { memmove( p_sys->p_buffer, &p_sys->p_buffer[frame.bytesconsumed], p_sys->i_buffer ); } return p_out; } block_Release( p_block ); return NULL; }
/***************************************************************************** * Open: initializes ES structures *****************************************************************************/ static int Open( vlc_object_t * p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; const uint8_t *p_peek; uint8_t *p_streaminfo; int i_streaminfo; es_format_t fmt; /* Have a peep at the show. */ if( stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC; if( p_peek[0]!='f' || p_peek[1]!='L' || p_peek[2]!='a' || p_peek[3]!='C' ) { if( !p_demux->b_force ) return VLC_EGENERIC; /* User forced */ msg_Err( p_demux, "this doesn't look like a flac stream, " "continuing anyway" ); } p_sys = malloc( sizeof( demux_sys_t ) ); if( unlikely(p_sys == NULL) ) return VLC_ENOMEM; p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys; p_sys->b_start = true; p_sys->p_meta = NULL; memset( &p_sys->replay_gain, 0, sizeof(p_sys->replay_gain) ); p_sys->i_length = 0; p_sys->i_time_offset = 0; p_sys->i_pts = 0; p_sys->i_pts_start = 0; p_sys->p_es = NULL; TAB_INIT( p_sys->i_seekpoint, p_sys->seekpoint ); TAB_INIT( p_sys->i_attachments, p_sys->attachments); p_sys->i_cover_idx = 0; p_sys->i_cover_score = 0; /* We need to read and store the STREAMINFO metadata */ if( ReadMeta( p_demux, &p_streaminfo, &i_streaminfo ) ) { free( p_sys ); return VLC_EGENERIC; } /* Load the FLAC packetizer */ /* Store STREAMINFO for the decoder and packetizer */ p_streaminfo[4] |= 0x80; /* Fake this as the last metadata block */ es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_FLAC ); fmt.i_extra = i_streaminfo; fmt.p_extra = p_streaminfo; p_sys->p_packetizer = demux_PacketizerNew( p_demux, &fmt, "flac" ); if( !p_sys->p_packetizer ) { free( p_sys ); return VLC_EGENERIC; } if( p_sys->i_cover_idx < p_sys->i_attachments ) { char psz_url[128]; if( !p_sys->p_meta ) p_sys->p_meta = vlc_meta_New(); snprintf( psz_url, sizeof(psz_url), "attachment://%s", p_sys->attachments[p_sys->i_cover_idx]->psz_name ); vlc_meta_Set( p_sys->p_meta, vlc_meta_ArtworkURL, psz_url ); } vlc_audio_replay_gain_MergeFromMeta( &p_sys->replay_gain, p_sys->p_meta ); return VLC_SUCCESS; }
/***************************************************************************** * DecodeBlock: *****************************************************************************/ static block_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block; if( !pp_block || !*pp_block ) return NULL; p_block = *pp_block; if( p_block->i_flags&(BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { block_Release( p_block ); return NULL; } /* Remove ADTS header if we have decoder specific config */ if( p_dec->fmt_in.i_extra && p_block->i_buffer > 7 ) { if( p_block->p_buffer[0] == 0xff && ( p_block->p_buffer[1] & 0xf0 ) == 0xf0 ) /* syncword */ { /* ADTS header present */ size_t i_header_size; /* 7 bytes (+ 2 bytes for crc) */ i_header_size = 7 + ( ( p_block->p_buffer[1] & 0x01 ) ? 0 : 2 ); /* FIXME: multiple blocks per frame */ if( p_block->i_buffer > i_header_size ) { p_block->p_buffer += i_header_size; p_block->i_buffer -= i_header_size; } } } /* Append the block to the temporary buffer */ if( p_sys->i_buffer_size < p_sys->i_buffer + p_block->i_buffer ) { size_t i_buffer_size = p_sys->i_buffer + p_block->i_buffer; uint8_t *p_buffer = realloc( p_sys->p_buffer, i_buffer_size ); if( p_buffer ) { p_sys->i_buffer_size = i_buffer_size; p_sys->p_buffer = p_buffer; } else { p_block->i_buffer = 0; } } if( p_block->i_buffer > 0 ) { memcpy( &p_sys->p_buffer[p_sys->i_buffer], p_block->p_buffer, p_block->i_buffer ); p_sys->i_buffer += p_block->i_buffer; p_block->i_buffer = 0; } if( p_dec->fmt_out.audio.i_rate == 0 && p_dec->fmt_in.i_extra > 0 ) { /* We have a decoder config so init the handle */ unsigned long i_rate; unsigned char i_channels; if( faacDecInit2( p_sys->hfaad, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra, &i_rate, &i_channels ) >= 0 ) { p_dec->fmt_out.audio.i_rate = i_rate; p_dec->fmt_out.audio.i_channels = i_channels; p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels = pi_channels_guessed[i_channels]; date_Init( &p_sys->date, i_rate, 1 ); } } if( p_dec->fmt_out.audio.i_rate == 0 && p_sys->i_buffer ) { unsigned long i_rate; unsigned char i_channels; /* Init faad with the first frame */ if( faacDecInit( p_sys->hfaad, p_sys->p_buffer, p_sys->i_buffer, &i_rate, &i_channels ) < 0 ) { block_Release( p_block ); return NULL; } p_dec->fmt_out.audio.i_rate = i_rate; p_dec->fmt_out.audio.i_channels = i_channels; p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels = pi_channels_guessed[i_channels]; date_Init( &p_sys->date, i_rate, 1 ); } if( p_block->i_pts > VLC_TS_INVALID && p_block->i_pts != date_Get( &p_sys->date ) ) { date_Set( &p_sys->date, p_block->i_pts ); } else if( !date_Get( &p_sys->date ) ) { /* We've just started the stream, wait for the first PTS. */ block_Release( p_block ); p_sys->i_buffer = 0; return NULL; } /* Decode all data */ if( p_sys->i_buffer ) { void *samples; faacDecFrameInfo frame; block_t *p_out; samples = faacDecDecode( p_sys->hfaad, &frame, p_sys->p_buffer, p_sys->i_buffer ); if( frame.error > 0 ) { msg_Warn( p_dec, "%s", faacDecGetErrorMessage( frame.error ) ); if( frame.error == 21 || frame.error == 12 ) { /* * Once an "Unexpected channel configuration change" * or a "Invalid number of channels" error * occurs, it will occurs afterwards, and we got no sound. * Reinitialization of the decoder is required. */ unsigned long i_rate; unsigned char i_channels; faacDecHandle *hfaad; faacDecConfiguration *cfg,*oldcfg; oldcfg = faacDecGetCurrentConfiguration( p_sys->hfaad ); hfaad = faacDecOpen(); cfg = faacDecGetCurrentConfiguration( hfaad ); if( oldcfg->defSampleRate ) cfg->defSampleRate = oldcfg->defSampleRate; cfg->defObjectType = oldcfg->defObjectType; cfg->outputFormat = oldcfg->outputFormat; faacDecSetConfiguration( hfaad, cfg ); if( faacDecInit( hfaad, p_sys->p_buffer, p_sys->i_buffer, &i_rate,&i_channels ) < 0 ) { /* reinitialization failed */ faacDecClose( hfaad ); faacDecSetConfiguration( p_sys->hfaad, oldcfg ); } else { faacDecClose( p_sys->hfaad ); p_sys->hfaad = hfaad; p_dec->fmt_out.audio.i_rate = i_rate; p_dec->fmt_out.audio.i_channels = i_channels; p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels = pi_channels_guessed[i_channels]; date_Init( &p_sys->date, i_rate, 1 ); } } /* Flush the buffer */ p_sys->i_buffer = 0; block_Release( p_block ); return NULL; } if( frame.channels <= 0 || frame.channels > 8 || frame.channels == 7 ) { msg_Warn( p_dec, "invalid channels count: %i", frame.channels ); /* Flush the buffer */ p_sys->i_buffer -= frame.bytesconsumed; if( p_sys->i_buffer > 0 ) { memmove( p_sys->p_buffer,&p_sys->p_buffer[frame.bytesconsumed], p_sys->i_buffer ); } block_Release( p_block ); return NULL; } if( frame.samples <= 0 ) { msg_Warn( p_dec, "decoded zero sample" ); /* Flush the buffer */ p_sys->i_buffer -= frame.bytesconsumed; if( p_sys->i_buffer > 0 ) { memmove( p_sys->p_buffer,&p_sys->p_buffer[frame.bytesconsumed], p_sys->i_buffer ); } block_Release( p_block ); return NULL; } /* We decoded a valid frame */ if( p_dec->fmt_out.audio.i_rate != frame.samplerate ) { date_Init( &p_sys->date, frame.samplerate, 1 ); date_Set( &p_sys->date, p_block->i_pts ); } p_block->i_pts = VLC_TS_INVALID; /* PTS is valid only once */ p_dec->fmt_out.audio.i_rate = frame.samplerate; p_dec->fmt_out.audio.i_channels = frame.channels; /* Adjust stream info when dealing with SBR/PS */ bool b_sbr = (frame.sbr == 1) || (frame.sbr == 2); if( p_sys->b_sbr != b_sbr || p_sys->b_ps != frame.ps ) { const char *psz_ext = (b_sbr && frame.ps) ? "SBR+PS" : b_sbr ? "SBR" : "PS"; msg_Dbg( p_dec, "AAC %s (channels: %u, samplerate: %lu)", psz_ext, frame.channels, frame.samplerate ); if( !p_dec->p_description ) p_dec->p_description = vlc_meta_New(); if( p_dec->p_description ) vlc_meta_AddExtra( p_dec->p_description, _("AAC extension"), psz_ext ); p_sys->b_sbr = b_sbr; p_sys->b_ps = frame.ps; } /* Convert frame.channel_position to our own channel values */ p_dec->fmt_out.audio.i_physical_channels = 0; const uint32_t nbChannels = frame.channels; unsigned j; for( unsigned i = 0; i < nbChannels; i++ ) { /* Find the channel code */ for( j = 0; j < MAX_CHANNEL_POSITIONS; j++ ) { if( frame.channel_position[i] == pi_channels_in[j] ) break; } if( j >= MAX_CHANNEL_POSITIONS ) { msg_Warn( p_dec, "unknown channel ordering" ); /* Invent something */ j = i; } /* */ p_sys->pi_channel_positions[i] = pi_channels_out[j]; if( p_dec->fmt_out.audio.i_physical_channels & pi_channels_out[j] ) frame.channels--; /* We loose a duplicated channel */ else p_dec->fmt_out.audio.i_physical_channels |= pi_channels_out[j]; } if ( nbChannels != frame.channels ) { p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels = pi_channels_guessed[nbChannels]; } else { p_dec->fmt_out.audio.i_original_channels = p_dec->fmt_out.audio.i_physical_channels; } p_dec->fmt_out.audio.i_channels = nbChannels; p_out = decoder_NewAudioBuffer( p_dec, frame.samples / nbChannels ); if( p_out == NULL ) { p_sys->i_buffer = 0; block_Release( p_block ); return NULL; } p_out->i_pts = date_Get( &p_sys->date ); p_out->i_length = date_Increment( &p_sys->date, frame.samples / nbChannels ) - p_out->i_pts; DoReordering( (uint32_t *)p_out->p_buffer, samples, frame.samples / nbChannels, nbChannels, p_sys->pi_channel_positions ); p_sys->i_buffer -= frame.bytesconsumed; if( p_sys->i_buffer > 0 ) { memmove( p_sys->p_buffer, &p_sys->p_buffer[frame.bytesconsumed], p_sys->i_buffer ); } return p_out; } block_Release( p_block ); return NULL; }
/***************************************************************************** * Open: initializes ES structures *****************************************************************************/ static int Open( vlc_object_t * p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; const uint8_t *p_peek; es_format_t fmt; /* Have a peep at the show. */ if( vlc_stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC; if( p_peek[0]!='f' || p_peek[1]!='L' || p_peek[2]!='a' || p_peek[3]!='C' ) { if( !p_demux->obj.force && !demux_IsContentType( p_demux, "audio/flac" ) ) return VLC_EGENERIC; /* User forced */ msg_Err( p_demux, "this doesn't look like a flac stream, " "continuing anyway" ); } p_sys = malloc( sizeof( demux_sys_t ) ); if( unlikely(p_sys == NULL) ) return VLC_ENOMEM; p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys; p_sys->b_start = true; p_sys->i_next_block_flags = 0; p_sys->p_packetizer = NULL; p_sys->p_meta = NULL; p_sys->i_length = 0; p_sys->i_pts = VLC_TS_INVALID; p_sys->p_es = NULL; p_sys->p_current_block = NULL; TAB_INIT( p_sys->i_seekpoint, p_sys->seekpoint ); TAB_INIT( p_sys->i_attachments, p_sys->attachments); TAB_INIT( p_sys->i_title_seekpoints, p_sys->pp_title_seekpoints ); p_sys->i_cover_idx = 0; p_sys->i_cover_score = 0; es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_FLAC ); /* We need to read and store the STREAMINFO metadata into fmt extra */ if( ParseHeaders( p_demux, &fmt ) ) goto error; /* Load the FLAC packetizer */ p_sys->p_packetizer = demux_PacketizerNew( p_demux, &fmt, "flac" ); if( !p_sys->p_packetizer ) goto error; if( p_sys->i_cover_idx < p_sys->i_attachments ) { char psz_url[128]; if( !p_sys->p_meta ) p_sys->p_meta = vlc_meta_New(); snprintf( psz_url, sizeof(psz_url), "attachment://%s", p_sys->attachments[p_sys->i_cover_idx]->psz_name ); vlc_meta_Set( p_sys->p_meta, vlc_meta_ArtworkURL, psz_url ); } p_sys->p_es = es_out_Add( p_demux->out, &p_sys->p_packetizer->fmt_in ); if( !p_sys->p_es ) goto error; return VLC_SUCCESS; error: Close( p_this ); return VLC_EGENERIC; }