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 ); } } } }
/***************************************************************************** * Control: *****************************************************************************/ static int Control( demux_t *p_demux, int i_query, va_list args ) { demux_sys_t *p_sys = p_demux->p_sys; bool *pb; double *p_dbl; vlc_meta_t *p_meta; switch( i_query ) { case DEMUX_CAN_PAUSE: case DEMUX_CAN_SEEK: case DEMUX_CAN_CONTROL_PACE: case DEMUX_CAN_CONTROL_RATE: case DEMUX_HAS_UNSUPPORTED_META: pb = va_arg( args, bool * ); *pb = false; return VLC_SUCCESS; case DEMUX_CAN_RECORD: pb = va_arg( args, bool * ); *pb = true; return VLC_SUCCESS; case DEMUX_GET_PTS_DELAY: *va_arg( args, vlc_tick_t * ) = VLC_TICK_FROM_MS(var_InheritInteger( p_demux, "live-caching" )); return VLC_SUCCESS; case DEMUX_GET_TIME: *va_arg( args, vlc_tick_t * ) = vlc_tick_now() - p_sys->i_starttime; return VLC_SUCCESS; case DEMUX_GET_LENGTH: *va_arg( args, vlc_tick_t * ) = 0; return VLC_SUCCESS; case DEMUX_GET_FPS: p_dbl = va_arg( args, double * ); *p_dbl = p_sys->f_fps; return VLC_SUCCESS; case DEMUX_GET_META: p_meta = va_arg( args, vlc_meta_t * ); vlc_meta_Set( p_meta, vlc_meta_Title, p_demux->psz_location ); return VLC_SUCCESS; default: return VLC_EGENERIC; } }
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 ); }
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; }
/***************************************************************************** * 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; }
/***************************************************************************** * Control: *****************************************************************************/ static int Control( demux_t *p_demux, int i_query, va_list args ) { demux_sys_t *p_sys = p_demux->p_sys; const int64_t i_start_time = p_sys->ic->start_time != AV_NOPTS_VALUE ? p_sys->ic->start_time : 0; double f, *pf; int64_t i64; switch( i_query ) { case DEMUX_CAN_SEEK: *va_arg( args, bool * ) = true; return VLC_SUCCESS; case DEMUX_GET_POSITION: pf = va_arg( args, double * ); *pf = 0.0; i64 = stream_Size( p_demux->s ); if( i64 > 0 ) { double current = vlc_stream_Tell( p_demux->s ); *pf = current / (double)i64; } if( (p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE) && (p_sys->i_pcr > 0) ) { *pf = (double)p_sys->i_pcr / (double)p_sys->ic->duration; } return VLC_SUCCESS; case DEMUX_SET_POSITION: { f = va_arg( args, double ); bool precise = va_arg( args, int ); i64 = p_sys->ic->duration * f + i_start_time; msg_Warn( p_demux, "DEMUX_SET_POSITION: %"PRId64, i64 ); /* If we have a duration, we prefer to seek by time but if we don't, or if the seek fails, try BYTE seeking */ if( p_sys->ic->duration == (int64_t)AV_NOPTS_VALUE || (av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BACKWARD ) < 0) ) { int64_t i_size = stream_Size( p_demux->s ); i64 = (i_size * f); msg_Warn( p_demux, "DEMUX_SET_BYTE_POSITION: %"PRId64, i64 ); if( av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BYTE ) < 0 ) return VLC_EGENERIC; ResetTime( p_demux, -1 ); } else { if( precise ) ResetTime( p_demux, i64 - i_start_time ); else ResetTime( p_demux, -1 ); } return VLC_SUCCESS; } case DEMUX_GET_LENGTH: if( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE ) *va_arg( args, vlc_tick_t * ) = FROM_AV_TS(p_sys->ic->duration); else *va_arg( args, vlc_tick_t * ) = 0; return VLC_SUCCESS; case DEMUX_GET_TIME: *va_arg( args, vlc_tick_t * ) = p_sys->i_pcr; return VLC_SUCCESS; case DEMUX_SET_TIME: { i64 = TO_AV_TS(va_arg( args, vlc_tick_t )); bool precise = va_arg( args, int ); msg_Warn( p_demux, "DEMUX_SET_TIME: %"PRId64, i64 ); if( av_seek_frame( p_sys->ic, -1, i64 + i_start_time, AVSEEK_FLAG_BACKWARD ) < 0 ) { return VLC_EGENERIC; } if( precise ) ResetTime( p_demux, i64 - i_start_time ); else ResetTime( p_demux, -1 ); return VLC_SUCCESS; } case DEMUX_HAS_UNSUPPORTED_META: { bool *pb_bool = va_arg( args, bool* ); *pb_bool = true; return VLC_SUCCESS; } case DEMUX_GET_META: { static const char names[][10] = { [vlc_meta_Title] = "title", [vlc_meta_Artist] = "artist", [vlc_meta_Genre] = "genre", [vlc_meta_Copyright] = "copyright", [vlc_meta_Album] = "album", //[vlc_meta_TrackNumber] -- TODO: parse number/total value [vlc_meta_Description] = "comment", //[vlc_meta_Rating] [vlc_meta_Date] = "date", [vlc_meta_Setting] = "encoder", //[vlc_meta_URL] [vlc_meta_Language] = "language", //[vlc_meta_NowPlaying] [vlc_meta_Publisher] = "publisher", [vlc_meta_EncodedBy] = "encoded_by", //[vlc_meta_ArtworkURL] //[vlc_meta_TrackID] //[vlc_meta_TrackTotal] }; vlc_meta_t *p_meta = va_arg( args, vlc_meta_t * ); AVDictionary *dict = p_sys->ic->metadata; for( unsigned i = 0; i < sizeof(names) / sizeof(*names); i++) { if( !names[i][0] ) continue; AVDictionaryEntry *e = av_dict_get( dict, names[i], NULL, 0 ); if( e != NULL && e->value != NULL && IsUTF8(e->value) ) vlc_meta_Set( p_meta, i, e->value ); } return VLC_SUCCESS; } case DEMUX_GET_ATTACHMENTS: { input_attachment_t ***ppp_attach = va_arg( args, input_attachment_t*** ); int *pi_int = va_arg( args, int * ); int i; if( p_sys->i_attachments <= 0 ) return VLC_EGENERIC; *ppp_attach = vlc_alloc( p_sys->i_attachments, sizeof(input_attachment_t*) ); if( *ppp_attach == NULL ) return VLC_EGENERIC; for( i = 0; i < p_sys->i_attachments; i++ ) { (*ppp_attach)[i] = vlc_input_attachment_Duplicate( p_sys->attachments[i] ); if((*ppp_attach)[i] == NULL) break; } *pi_int = i; return VLC_SUCCESS; } case DEMUX_GET_TITLE_INFO: { input_title_t ***ppp_title = va_arg( args, input_title_t *** ); int *pi_int = va_arg( args, int * ); int *pi_title_offset = va_arg( args, int * ); int *pi_seekpoint_offset = va_arg( args, int * ); if( !p_sys->p_title ) return VLC_EGENERIC; *ppp_title = malloc( sizeof( input_title_t*) ); if( *ppp_title == NULL ) return VLC_EGENERIC; (*ppp_title)[0] = vlc_input_title_Duplicate( p_sys->p_title ); *pi_int = (*ppp_title)[0] ? 1 : 0; *pi_title_offset = 0; *pi_seekpoint_offset = 0; return VLC_SUCCESS; } case DEMUX_SET_TITLE: { const int i_title = va_arg( args, int ); if( !p_sys->p_title || i_title != 0 ) return VLC_EGENERIC; return VLC_SUCCESS; } case DEMUX_SET_SEEKPOINT: { const int i_seekpoint = va_arg( args, int ); if( !p_sys->p_title ) return VLC_EGENERIC; i64 = TO_AV_TS(p_sys->p_title->seekpoint[i_seekpoint]->i_time_offset) + i_start_time; msg_Warn( p_demux, "DEMUX_SET_SEEKPOINT: %"PRId64, i64 ); if( av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BACKWARD ) < 0 ) { return VLC_EGENERIC; } ResetTime( p_demux, i64 - i_start_time ); return VLC_SUCCESS; } case DEMUX_TEST_AND_CLEAR_FLAGS: { unsigned *restrict flags = va_arg(args, unsigned *); *flags &= p_sys->i_update; p_sys->i_update &= ~*flags; return VLC_SUCCESS; } case DEMUX_GET_TITLE: if( p_sys->p_title == NULL ) return VLC_EGENERIC; *va_arg( args, int * ) = 0; return VLC_SUCCESS; case DEMUX_GET_SEEKPOINT: if( p_sys->p_title == NULL ) return VLC_EGENERIC; *va_arg( args, int * ) = p_sys->i_seekpoint; return VLC_SUCCESS; case DEMUX_CAN_PAUSE: case DEMUX_SET_PAUSE_STATE: case DEMUX_CAN_CONTROL_PACE: case DEMUX_GET_PTS_DELAY: return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args ); default: return VLC_EGENERIC; } }
/***************************************************************************** * 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; }
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 ); }