Merged release 0.15.2 from branch 'v0.15.x'
Conflicts: NEWS configure.ac
This commit is contained in:
		| @@ -40,53 +40,57 @@ flac_data_init(struct flac_data *data, struct decoder * decoder, | ||||
| 	data->tag = NULL; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| static void | ||||
| flac_find_float_comment(const FLAC__StreamMetadata *block, | ||||
| 			const char *cmnt, float *fl) | ||||
| 			const char *cmnt, float *fl, bool *found_r) | ||||
| { | ||||
| 	int offset = | ||||
| 	    FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, cmnt); | ||||
| 	int offset; | ||||
| 	size_t pos; | ||||
| 	int len; | ||||
| 	unsigned char tmp, *p; | ||||
|  | ||||
| 	if (offset >= 0) { | ||||
| 		size_t pos = strlen(cmnt) + 1;	/* 1 is for '=' */ | ||||
| 		int len = block->data.vorbis_comment.comments[offset].length | ||||
| 		    - pos; | ||||
| 		if (len > 0) { | ||||
| 			unsigned char tmp; | ||||
| 			unsigned char *p = &(block->data.vorbis_comment. | ||||
| 					     comments[offset].entry[pos]); | ||||
| 			tmp = p[len]; | ||||
| 			p[len] = '\0'; | ||||
| 			*fl = (float)atof((char *)p); | ||||
| 			p[len] = tmp; | ||||
| 	offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, | ||||
| 								     cmnt); | ||||
| 	if (offset < 0) | ||||
| 		return; | ||||
|  | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	pos = strlen(cmnt) + 1; /* 1 is for '=' */ | ||||
| 	len = block->data.vorbis_comment.comments[offset].length - pos; | ||||
| 	if (len <= 0) | ||||
| 		return; | ||||
|  | ||||
| 	return false; | ||||
| 	p = &block->data.vorbis_comment.comments[offset].entry[pos]; | ||||
| 	tmp = p[len]; | ||||
| 	p[len] = '\0'; | ||||
| 	*fl = (float)atof((char *)p); | ||||
| 	p[len] = tmp; | ||||
|  | ||||
| 	*found_r = true; | ||||
| } | ||||
|  | ||||
| /* replaygain stuff by AliasMrJones */ | ||||
| static void | ||||
| flac_parse_replay_gain(const FLAC__StreamMetadata *block, | ||||
| 		       struct flac_data *data) | ||||
| { | ||||
| 	bool found; | ||||
| 	bool found = false; | ||||
|  | ||||
| 	if (data->replay_gain_info) | ||||
| 		replay_gain_info_free(data->replay_gain_info); | ||||
|  | ||||
| 	data->replay_gain_info = replay_gain_info_new(); | ||||
|  | ||||
| 	found = flac_find_float_comment(block, "replaygain_album_gain", | ||||
| 					&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain) || | ||||
| 		flac_find_float_comment(block, "replaygain_album_peak", | ||||
| 					&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak) || | ||||
| 		flac_find_float_comment(block, "replaygain_track_gain", | ||||
| 					&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain) || | ||||
| 		flac_find_float_comment(block, "replaygain_track_peak", | ||||
| 					&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak); | ||||
| 	flac_find_float_comment(block, "replaygain_album_gain", | ||||
| 				&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain, | ||||
| 				&found); | ||||
| 	flac_find_float_comment(block, "replaygain_album_peak", | ||||
| 				&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak, | ||||
| 				&found); | ||||
| 	flac_find_float_comment(block, "replaygain_track_gain", | ||||
| 				&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain, | ||||
| 				&found); | ||||
| 	flac_find_float_comment(block, "replaygain_track_peak", | ||||
| 				&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak, | ||||
| 				&found); | ||||
|  | ||||
| 	if (!found) { | ||||
| 		replay_gain_info_free(data->replay_gain_info); | ||||
| @@ -106,25 +110,27 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, | ||||
| 	size_t char_tnum_length = 0; | ||||
| 	const char *comment = (const char*)entry->entry; | ||||
|  | ||||
| 	if (entry->length > name_length && | ||||
| 	    g_ascii_strncasecmp(comment, name, name_length) == 0) { | ||||
| 	        if (char_tnum != NULL) { | ||||
| 	            char_tnum_length = strlen(char_tnum); | ||||
| 		    if (entry->length > name_length + char_tnum_length + 2 && | ||||
| 		        comment[name_length] == '[' && | ||||
| 		        g_ascii_strncasecmp(comment + name_length + 1, | ||||
| 			char_tnum, char_tnum_length) == 0 && | ||||
| 			comment[name_length + char_tnum_length + 1] == ']') | ||||
| 			    name_length = name_length + char_tnum_length + 2; | ||||
| 		    else if (entry->length > name_length + char_tnum_length && | ||||
| 		        g_ascii_strncasecmp(comment + name_length, | ||||
| 		        char_tnum, char_tnum_length) == 0) | ||||
| 			    name_length = name_length + char_tnum_length; | ||||
| 	        } | ||||
| 	        if (comment[name_length] == '=') { | ||||
| 		    *length_r = entry->length - name_length - 1; | ||||
| 		    return comment + name_length + 1; | ||||
| 		} | ||||
| 	if (entry->length <= name_length || | ||||
| 	    g_ascii_strncasecmp(comment, name, name_length) != 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	if (char_tnum != NULL) { | ||||
| 		char_tnum_length = strlen(char_tnum); | ||||
| 		if (entry->length > name_length + char_tnum_length + 2 && | ||||
| 		    comment[name_length] == '[' && | ||||
| 		    g_ascii_strncasecmp(comment + name_length + 1, | ||||
| 					char_tnum, char_tnum_length) == 0 && | ||||
| 		    comment[name_length + char_tnum_length + 1] == ']') | ||||
| 			name_length = name_length + char_tnum_length + 2; | ||||
| 		else if (entry->length > name_length + char_tnum_length && | ||||
| 			 g_ascii_strncasecmp(comment + name_length, | ||||
| 					     char_tnum, char_tnum_length) == 0) | ||||
| 			name_length = name_length + char_tnum_length; | ||||
| 	} | ||||
|  | ||||
| 	if (comment[name_length] == '=') { | ||||
| 		*length_r = entry->length - name_length - 1; | ||||
| 		return comment + name_length + 1; | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| @@ -370,13 +376,15 @@ char* | ||||
| flac_cue_track(	const char* pathname, | ||||
| 		const unsigned int tnum) | ||||
| { | ||||
| 	FLAC__StreamMetadata* cs = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET); | ||||
| 	FLAC__bool success; | ||||
| 	FLAC__StreamMetadata* cs; | ||||
|  | ||||
| 	FLAC__metadata_get_cuesheet(pathname, &cs); | ||||
|  | ||||
| 	if (cs == NULL) | ||||
| 	success = FLAC__metadata_get_cuesheet(pathname, &cs); | ||||
| 	if (!success) | ||||
| 		return NULL; | ||||
|  | ||||
| 	assert(cs != NULL); | ||||
|  | ||||
| 	if (cs->data.cue_sheet.num_tracks <= 1) | ||||
| 	{ | ||||
| 		FLAC__metadata_object_delete(cs); | ||||
|   | ||||
| @@ -350,11 +350,11 @@ parse_id3_replay_gain_info(struct id3_tag *tag) | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_ID3TAG | ||||
| static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, | ||||
| 			  struct tag **mpd_tag, | ||||
| 			  struct replay_gain_info **replay_gain_info_r) | ||||
| { | ||||
| #ifdef HAVE_ID3TAG | ||||
| 	struct id3_tag *id3_tag = NULL; | ||||
| 	id3_length_t count; | ||||
| 	id3_byte_t const *id3_data; | ||||
| @@ -418,8 +418,34 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, | ||||
| 	id3_tag_delete(id3_tag); | ||||
|  | ||||
| 	g_free(allocated); | ||||
| } | ||||
| #else /* !HAVE_ID3TAG */ | ||||
| 	(void)mpd_tag; | ||||
| 	(void)replay_gain_info_r; | ||||
|  | ||||
| 	/* This code is enabled when libid3tag is disabled.  Instead | ||||
| 	   of parsing the ID3 frame, it just skips it. */ | ||||
|  | ||||
| 	mad_stream_skip(&data->stream, tagsize); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #ifndef HAVE_ID3TAG | ||||
| /** | ||||
|  * This function emulates libid3tag when it is disabled.  Instead of | ||||
|  * doing a real analyzation of the frame, it just checks whether the | ||||
|  * frame begins with the string "ID3".  If so, it returns the full | ||||
|  * length. | ||||
|  */ | ||||
| static signed long | ||||
| id3_tag_query(const void *p0, size_t length) | ||||
| { | ||||
| 	const char *p = p0; | ||||
|  | ||||
| 	return length > 3 && memcmp(p, "ID3", 3) == 0 | ||||
| 		? length | ||||
| 		: 0; | ||||
| } | ||||
| #endif /* !HAVE_ID3TAG */ | ||||
|  | ||||
| static enum mp3_action | ||||
| decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, | ||||
| @@ -433,7 +459,6 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, | ||||
| 			return DECODE_BREAK; | ||||
| 	} | ||||
| 	if (mad_header_decode(&data->frame.header, &data->stream)) { | ||||
| #ifdef HAVE_ID3TAG | ||||
| 		if ((data->stream).error == MAD_ERROR_LOSTSYNC && | ||||
| 		    (data->stream).this_frame) { | ||||
| 			signed long tagsize = id3_tag_query((data->stream). | ||||
| @@ -454,7 +479,6 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, | ||||
| 				return DECODE_CONT; | ||||
| 			} | ||||
| 		} | ||||
| #endif | ||||
| 		if (MAD_RECOVERABLE((data->stream).error)) { | ||||
| 			return DECODE_SKIP; | ||||
| 		} else { | ||||
| @@ -493,7 +517,6 @@ decodeNextFrame(struct mp3_data *data) | ||||
| 			return DECODE_BREAK; | ||||
| 	} | ||||
| 	if (mad_frame_decode(&data->frame, &data->stream)) { | ||||
| #ifdef HAVE_ID3TAG | ||||
| 		if ((data->stream).error == MAD_ERROR_LOSTSYNC) { | ||||
| 			signed long tagsize = id3_tag_query((data->stream). | ||||
| 							    this_frame, | ||||
| @@ -506,7 +529,6 @@ decodeNextFrame(struct mp3_data *data) | ||||
| 				return DECODE_CONT; | ||||
| 			} | ||||
| 		} | ||||
| #endif | ||||
| 		if (MAD_RECOVERABLE((data->stream).error)) { | ||||
| 			return DECODE_SKIP; | ||||
| 		} else { | ||||
|   | ||||
| @@ -73,9 +73,14 @@ directory_prune_empty(struct directory *directory) | ||||
| 	struct dirvec *dv = &directory->children; | ||||
|  | ||||
| 	for (i = dv->nr; --i >= 0; ) { | ||||
| 		directory_prune_empty(dv->base[i]); | ||||
| 		if (directory_is_empty(dv->base[i])) | ||||
| 			dirvec_delete(dv, dv->base[i]); | ||||
| 		struct directory *child = dv->base[i]; | ||||
|  | ||||
| 		directory_prune_empty(child); | ||||
|  | ||||
| 		if (directory_is_empty(child)) { | ||||
| 			dirvec_delete(dv, child); | ||||
| 			directory_free(child); | ||||
| 		} | ||||
| 	} | ||||
| 	if (!dv->nr) | ||||
| 		dirvec_destroy(dv); | ||||
|   | ||||
| @@ -448,8 +448,15 @@ my_shout_play(void *data, const void *chunk, size_t size, GError **error) | ||||
| static bool | ||||
| my_shout_pause(void *data) | ||||
| { | ||||
| 	struct shout_data *sd = (struct shout_data *)data; | ||||
| 	static const char silence[1020]; | ||||
|  | ||||
| 	if (shout_delay(sd->shout_conn) > 500) { | ||||
| 		/* cap the latency for unpause */ | ||||
| 		g_usleep(500000); | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	return my_shout_play(data, silence, sizeof(silence), NULL); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -77,6 +77,17 @@ audio_output_open(struct audio_output *ao, | ||||
| 	    audio_format_equals(audio_format, &ao->in_audio_format)) { | ||||
| 		assert(ao->pipe == mp); | ||||
|  | ||||
| 		if (ao->pause) { | ||||
| 			/* unpause with the CANCEL command; this is a | ||||
| 			   hack, but suits well for forcing the thread | ||||
| 			   to leave the ao_pause() thread, and we need | ||||
| 			   to flush the device buffer anyway */ | ||||
|  | ||||
| 			/* we're not using audio_output_cancel() here, | ||||
| 			   because that function is asynchronous */ | ||||
| 			ao_command(ao, AO_COMMAND_CANCEL); | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -86,6 +86,12 @@ struct audio_output { | ||||
| 	 */ | ||||
| 	bool open; | ||||
|  | ||||
| 	/** | ||||
| 	 * Is the device paused?  i.e. the output thread is in the | ||||
| 	 * ao_pause() loop. | ||||
| 	 */ | ||||
| 	bool pause; | ||||
|  | ||||
| 	/** | ||||
| 	 * If not NULL, the device has failed, and this timer is used | ||||
| 	 * to estimate how long it should stay disabled (unless | ||||
|   | ||||
| @@ -295,6 +295,7 @@ static void ao_pause(struct audio_output *ao) | ||||
| 	bool ret; | ||||
|  | ||||
| 	ao_plugin_cancel(ao->plugin, ao->data); | ||||
| 	ao->pause = true; | ||||
| 	ao_command_finished(ao); | ||||
|  | ||||
| 	do { | ||||
| @@ -304,6 +305,8 @@ static void ao_pause(struct audio_output *ao) | ||||
| 			break; | ||||
| 		} | ||||
| 	} while (ao->command == AO_COMMAND_NONE); | ||||
|  | ||||
| 	ao->pause = false; | ||||
| } | ||||
|  | ||||
| static gpointer audio_output_task(gpointer arg) | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
|  | ||||
| #include <glib.h> | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| static const char *const ape_tag_names[] = { | ||||
| @@ -95,15 +96,18 @@ tag_ape_load(const char *file) | ||||
|  | ||||
| 	/* find beginning of ape tag */ | ||||
| 	tagLen = GUINT32_FROM_LE(footer.length); | ||||
| 	if (tagLen < sizeof(footer)) | ||||
| 	if (tagLen <= sizeof(footer) + 10) | ||||
| 		goto fail; | ||||
| 	if (tagLen > 1024 * 1024) | ||||
| 		/* refuse to load more than one megabyte of tag data */ | ||||
| 		goto fail; | ||||
| 	if (fseek(fp, size - tagLen, SEEK_SET)) | ||||
| 		goto fail; | ||||
|  | ||||
| 	/* read tag into buffer */ | ||||
| 	tagLen -= sizeof(footer); | ||||
| 	if (tagLen <= 0) | ||||
| 		goto fail; | ||||
| 	assert(tagLen > 10); | ||||
|  | ||||
| 	buffer = g_malloc(tagLen); | ||||
| 	if (fread(buffer, 1, tagLen, fp) != tagLen) | ||||
| 		goto fail; | ||||
| @@ -121,7 +125,7 @@ tag_ape_load(const char *file) | ||||
|  | ||||
| 		/* get the key */ | ||||
| 		key = p; | ||||
| 		while (tagLen - size > 0 && *p != '\0') { | ||||
| 		while (tagLen > size && *p != '\0') { | ||||
| 			p++; | ||||
| 			tagLen--; | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										11
									
								
								src/update.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/update.c
									
									
									
									
									
								
							| @@ -430,7 +430,7 @@ update_container_file(	struct directory* directory, | ||||
| { | ||||
| 	char* vtrack = NULL; | ||||
| 	unsigned int tnum = 0; | ||||
| 	const char* pathname = map_directory_child_fs(directory, name); | ||||
| 	char* pathname = map_directory_child_fs(directory, name); | ||||
| 	struct directory* contdir = dirvec_find(&directory->children, name); | ||||
|  | ||||
| 	// directory exists already | ||||
| @@ -446,8 +446,10 @@ update_container_file(	struct directory* directory, | ||||
|  | ||||
| 			modified = true; | ||||
| 		} | ||||
| 		else | ||||
| 		else { | ||||
| 			g_free(pathname); | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	contdir = make_subdir(directory, name); | ||||
| @@ -473,6 +475,8 @@ update_container_file(	struct directory* directory, | ||||
| 		g_free(vtrack); | ||||
| 	} | ||||
|  | ||||
| 	g_free(pathname); | ||||
|  | ||||
| 	if (tnum == 1) | ||||
| 	{ | ||||
| 		delete_directory(contdir); | ||||
| @@ -767,7 +771,6 @@ static void * update_task(void *_path) | ||||
| { | ||||
| 	if (_path != NULL && !isRootDirectory(_path)) { | ||||
| 		updatePath((char *)_path); | ||||
| 		g_free(_path); | ||||
| 	} else { | ||||
| 		struct directory *directory = db_get_root(); | ||||
| 		struct stat st; | ||||
| @@ -776,6 +779,8 @@ static void * update_task(void *_path) | ||||
| 			updateDirectory(directory, &st); | ||||
| 	} | ||||
|  | ||||
| 	g_free(_path); | ||||
|  | ||||
| 	if (modified || !db_exists()) | ||||
| 		db_save(); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann