/***************************************************************************** * 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 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 ); }
static int vlclua_input_item_set_meta( lua_State *L ) { input_item_t *p_item = vlclua_input_item_get_internal( L ); lua_settop( L, 1 + 2 ); // two arguments const char *psz_name = luaL_checkstring( L, 2 ), *psz_value = luaL_checkstring( L, 3 ); #define META_TYPE( n, s ) { s, vlc_meta_ ## n }, static const struct { const char psz_name[15]; unsigned char type; } pp_meta_types[] = { META_TYPE( Title, "title" ) META_TYPE( Artist, "artist" ) META_TYPE( Genre, "genre" ) META_TYPE( Copyright, "copyright" ) META_TYPE( Album, "album" ) META_TYPE( TrackNumber, "track_number" ) META_TYPE( Description, "description" ) META_TYPE( Rating, "rating" ) META_TYPE( Date, "date" ) META_TYPE( Setting, "setting" ) META_TYPE( URL, "url" ) META_TYPE( Language, "language" ) META_TYPE( NowPlaying, "now_playing" ) META_TYPE( ESNowPlaying, "now_playing" ) META_TYPE( Publisher, "publisher" ) META_TYPE( EncodedBy, "encoded_by" ) META_TYPE( ArtworkURL, "artwork_url" ) META_TYPE( TrackID, "track_id" ) META_TYPE( TrackTotal, "track_total" ) META_TYPE( Director, "director" ) META_TYPE( Season, "season" ) META_TYPE( Episode, "episode" ) META_TYPE( ShowName, "show_name" ) META_TYPE( Actors, "actors" ) }; #undef META_TYPE static_assert( sizeof(pp_meta_types) == VLC_META_TYPE_COUNT * sizeof(pp_meta_types[0]), "Inconsistent meta data types" ); vlc_meta_type_t type = vlc_meta_Title; for( unsigned i = 0; i < VLC_META_TYPE_COUNT; i++ ) { if( !strcasecmp( pp_meta_types[i].psz_name, psz_name ) ) { type = pp_meta_types[i].type; input_item_SetMeta( p_item, type, psz_value ); return 1; } } vlc_meta_AddExtra( p_item->p_meta, psz_name, psz_value ); return 1; }
/***************************************************************************** * 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++; } }
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 ); }
/***************************************************************************** * 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; }
/***************************************************************************** * 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++; } }
/***************************************************************************** * 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; }
/***************************************************************************** * Control: *****************************************************************************/ static int Control( demux_t *p_demux, int i_query, va_list args ) { demux_sys_t *p_sys = p_demux->p_sys; double f, *pf; vlc_tick_t i64; vlc_tick_t *pi64; switch( i_query ) { case DEMUX_CAN_SEEK: *va_arg( args, bool * ) = true; return VLC_SUCCESS; case DEMUX_GET_POSITION: pf = va_arg( args, double* ); if( p_sys->i_length > 0 ) { double current = date_Get( &p_sys->pts ) - VLC_TICK_0; double length = p_sys->i_length; *pf = current / length; return VLC_SUCCESS; } return VLC_EGENERIC; case DEMUX_SET_POSITION: f = va_arg( args, double ); i64 = f * p_sys->i_length; if( i64 >= 0 && i64 <= p_sys->i_length ) { ModPlug_Seek( p_sys->f, MS_FROM_VLC_TICK(i64) ); date_Set( &p_sys->pts, VLC_TICK_0 + i64 ); return VLC_SUCCESS; } return VLC_EGENERIC; case DEMUX_GET_TIME: *va_arg( args, vlc_tick_t * ) = date_Get( &p_sys->pts ); return VLC_SUCCESS; case DEMUX_GET_LENGTH: pi64 = va_arg( args, vlc_tick_t * ); *pi64 = p_sys->i_length; return VLC_SUCCESS; case DEMUX_SET_TIME: i64 = va_arg( args, vlc_tick_t ); if( likely(i64 >= 0) && i64 <= p_sys->i_length ) { ModPlug_Seek( p_sys->f, MS_FROM_VLC_TICK( i64 ) ); date_Set( &p_sys->pts, VLC_TICK_0 + i64 ); return VLC_SUCCESS; } return VLC_EGENERIC; case DEMUX_HAS_UNSUPPORTED_META: { bool *pb_bool = va_arg( args, bool* ); *pb_bool = false; /* FIXME I am not sure of this one */ return VLC_SUCCESS; } case DEMUX_GET_META: { vlc_meta_t *p_meta = va_arg( args, vlc_meta_t * ); unsigned i_num_samples = ModPlug_NumSamples( p_sys->f ), i_num_instruments = ModPlug_NumInstruments( p_sys->f ); unsigned i_num_patterns = ModPlug_NumPatterns( p_sys->f ), i_num_channels = ModPlug_NumChannels( p_sys->f ); // unsigned modType = ModPlug_GetModuleType( p_sys->f ); char psz_temp[2048]; /* 32 * 240 max, but only need start */ char *psz_module_info, *psz_instrument_info; unsigned i_temp_index = 0; const char *psz_name = ModPlug_GetName( p_sys->f ); if( psz_name && *psz_name && IsUTF8( psz_name ) ) vlc_meta_SetTitle( p_meta, psz_name ); /* Comment field from artist - not in every type of MOD */ psz_name = ModPlug_GetMessage( p_sys->f ); if( psz_name && *psz_name && IsUTF8( psz_name ) ) vlc_meta_SetDescription( p_meta, psz_name ); /* Instruments only in newer MODs - so don't show if 0 */ if( asprintf( &psz_instrument_info, ", %i Instruments", i_num_instruments ) >= 0 ) { if( asprintf( &psz_module_info, "%i Channels, %i Patterns\n" "%i Samples%s\n", i_num_channels, i_num_patterns, i_num_samples, ( i_num_instruments ? psz_instrument_info : "" ) ) >= 0 ) { vlc_meta_AddExtra( p_meta, "Module Information", psz_module_info ); free( psz_module_info ); } free( psz_instrument_info ); } /* Make list of instruments (XM, IT, etc) */ if( i_num_instruments ) { i_temp_index = 0; for( unsigned i = 0; i < i_num_instruments && i_temp_index < sizeof(psz_temp); i++ ) { char lBuffer[33]; ModPlug_InstrumentName( p_sys->f, i, lBuffer ); if ( !lBuffer[0] || !IsUTF8( lBuffer ) ) continue; i_temp_index += snprintf( &psz_temp[i_temp_index], sizeof(psz_temp) - i_temp_index, "%s\n", lBuffer ); } vlc_meta_AddExtra( p_meta, "Instruments", psz_temp ); } /* Make list of samples */ for( unsigned int i = 0; i < i_num_samples && i_temp_index < sizeof(psz_temp); i++ ) { char psz_buffer[33]; ModPlug_SampleName( p_sys->f, i, psz_buffer ); if ( !psz_buffer[0] || !IsUTF8( psz_buffer ) ) continue; i_temp_index += snprintf( &psz_temp[i_temp_index], sizeof(psz_temp) - i_temp_index, "%s\n", psz_buffer ); } vlc_meta_AddExtra( p_meta, "Samples", psz_temp ); return VLC_SUCCESS; } case DEMUX_GET_FPS: /* meaningless */ return VLC_EGENERIC; case DEMUX_CAN_PAUSE: case DEMUX_CAN_CONTROL_PACE: case DEMUX_GET_PTS_DELAY: case DEMUX_SET_PAUSE_STATE: return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args ); default: return VLC_EGENERIC; } }
/***************************************************************************** * 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; }
/***************************************************************************** * 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 }
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--; } }
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 ); }