load_flac.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 12 Feb 2012 17:58:22 -0500
branchSDL-1.2
changeset 584 ea53a85853dd
parent 561 87bdb4c81c0b
child 601 05123263dab3
child 947 41422d16ee01
permissions -rw-r--r--
Fixed 1418 - crash on double free if loading WAV file failed
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 
    21   This is the source needed to decode a FLAC into a waveform.
    22   	~ Austen Dicken (admin@cvpcs.org).
    23 */
    24 
    25 #ifdef FLAC_MUSIC
    26 
    27 #include <stdio.h>
    28 #include <stdlib.h>
    29 #include <string.h>
    30 
    31 #include "SDL_mutex.h"
    32 #include "SDL_endian.h"
    33 #include "SDL_timer.h"
    34 
    35 #include "SDL_mixer.h"
    36 #include "dynamic_flac.h"
    37 #include "load_flac.h"
    38 
    39 #include <FLAC/stream_decoder.h>
    40 
    41 typedef struct {
    42 	SDL_RWops* sdl_src;
    43 	SDL_AudioSpec* sdl_spec;
    44 	Uint8** sdl_audio_buf;
    45 	Uint32* sdl_audio_len;
    46 	int sdl_audio_read;
    47 	FLAC__uint64 flac_total_samples;
    48 	unsigned flac_bps;
    49 } FLAC_SDL_Data;
    50 
    51 static FLAC__StreamDecoderReadStatus flac_read_load_cb(
    52 									const FLAC__StreamDecoder *decoder,
    53 									FLAC__byte buffer[],
    54 									size_t *bytes,
    55 									void *client_data)
    56 {
    57 	// make sure there is something to be reading
    58 	if (*bytes > 0) {
    59 		FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
    60 
    61 		*bytes = SDL_RWread (data->sdl_src, buffer, sizeof (FLAC__byte),
    62 								*bytes);
    63 
    64 		if(*bytes < 0) { // error in read
    65 			return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
    66 		}
    67 		else if(*bytes == 0) { // no data was read (EOF)
    68 			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
    69 		}
    70 		else { // data was read, continue
    71 			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
    72 		}
    73 	}
    74 	else {
    75 		return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
    76 	}
    77 }
    78 
    79 static FLAC__StreamDecoderSeekStatus flac_seek_load_cb(
    80 									const FLAC__StreamDecoder *decoder,
    81 									FLAC__uint64 absolute_byte_offset,
    82 									void *client_data)
    83 {
    84 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
    85 
    86 	if (SDL_RWseek (data->sdl_src, absolute_byte_offset, RW_SEEK_SET) < 0) {
    87 		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
    88 	}
    89 	else {
    90 		return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
    91 	}
    92 }
    93 
    94 static FLAC__StreamDecoderTellStatus flac_tell_load_cb(
    95 									const FLAC__StreamDecoder *decoder,
    96 									FLAC__uint64 *absolute_byte_offset,
    97 									void *client_data)
    98 {
    99 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   100 
   101 	int pos = SDL_RWtell (data->sdl_src);
   102 
   103 	if (pos < 0) {
   104 		return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
   105 	}
   106 	else {
   107 		*absolute_byte_offset = (FLAC__uint64)pos;
   108 		return FLAC__STREAM_DECODER_TELL_STATUS_OK;
   109 	}
   110 }
   111 
   112 static FLAC__StreamDecoderLengthStatus flac_length_load_cb(
   113 									const FLAC__StreamDecoder *decoder,
   114 									FLAC__uint64 *stream_length,
   115 									void *client_data)
   116 {
   117 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   118 
   119 	int pos = SDL_RWtell (data->sdl_src);
   120 	int length = SDL_RWseek (data->sdl_src, 0, RW_SEEK_END);
   121 
   122 	if (SDL_RWseek (data->sdl_src, pos, RW_SEEK_SET) != pos || length < 0) {
   123 		/* there was an error attempting to return the stream to the original
   124 		 * position, or the length was invalid. */
   125 		return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
   126 	}
   127 	else {
   128 		*stream_length = (FLAC__uint64)length;
   129 		return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
   130 	}
   131 }
   132 
   133 static FLAC__bool flac_eof_load_cb(const FLAC__StreamDecoder *decoder,
   134 									void *client_data)
   135 {
   136 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   137 
   138 	int pos = SDL_RWtell (data->sdl_src);
   139 	int end = SDL_RWseek (data->sdl_src, 0, RW_SEEK_END);
   140 
   141 	// was the original position equal to the end (a.k.a. the seek didn't move)?
   142 	if (pos == end) {
   143 		// must be EOF
   144 		return true;
   145 	}
   146 	else {
   147 		// not EOF, return to the original position
   148 		SDL_RWseek (data->sdl_src, pos, RW_SEEK_SET);
   149 
   150 		return false;
   151 	}
   152 }
   153 
   154 static FLAC__StreamDecoderWriteStatus flac_write_load_cb(
   155 									const FLAC__StreamDecoder *decoder,
   156 									const FLAC__Frame *frame,
   157 									const FLAC__int32 *const buffer[],
   158 									void *client_data)
   159 {
   160 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   161 	size_t i;
   162 	Uint8 *buf;
   163 
   164 	if (data->flac_total_samples == 0) {
   165 		SDL_SetError ("Given FLAC file does not specify its sample count.");
   166 		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   167 	}
   168 
   169 	if (data->sdl_spec->channels != 2 || data->flac_bps != 16) {
   170 		SDL_SetError ("Current FLAC support is only for 16 bit Stereo files.");
   171 		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   172 	}
   173 
   174 	// check if it is the first audio frame so we can initialize the output
   175 	// buffer
   176 	if (frame->header.number.sample_number == 0) {
   177 		*(data->sdl_audio_len) = data->sdl_spec->size;
   178 		data->sdl_audio_read = 0;
   179     	*(data->sdl_audio_buf) = SDL_malloc (*(data->sdl_audio_len));
   180 
   181     	if (*(data->sdl_audio_buf) == NULL) {
   182     		SDL_SetError
   183 					("Unable to allocate memory to store the FLAC stream.");
   184     		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
   185     	}
   186 	}
   187 
   188 	buf = *(data->sdl_audio_buf);
   189 
   190 	for (i = 0; i < frame->header.blocksize; i++) {
   191 		FLAC__int16 i16;
   192 		FLAC__uint16 ui16;
   193 
   194 		i16 = (FLAC__int16)buffer[0][i];
   195 		ui16 = (FLAC__uint16)i16;
   196 
   197 		*(buf + (data->sdl_audio_read++)) = (char)(ui16);
   198 		*(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8);
   199 
   200 		i16 = (FLAC__int16)buffer[1][i];
   201 		ui16 = (FLAC__uint16)i16;
   202 
   203 		*(buf + (data->sdl_audio_read++)) = (char)(ui16);
   204 		*(buf + (data->sdl_audio_read++)) = (char)(ui16 >> 8);
   205 	}
   206 
   207 	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
   208 }
   209 
   210 static void flac_metadata_load_cb(
   211 					const FLAC__StreamDecoder *decoder,
   212 					const FLAC__StreamMetadata *metadata,
   213 					void *client_data)
   214 {
   215 	FLAC_SDL_Data *data = (FLAC_SDL_Data *)client_data;
   216 	FLAC__uint64 total_samples;
   217 	unsigned bps;
   218 
   219 	if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
   220 		// save the metadata right now for use later on
   221 		*(data->sdl_audio_buf) = NULL;
   222 		*(data->sdl_audio_len) = 0;
   223 		memset (data->sdl_spec, '\0', sizeof (SDL_AudioSpec));
   224 
   225 		data->sdl_spec->format = AUDIO_S16;
   226 		data->sdl_spec->freq = (int)(metadata->data.stream_info.sample_rate);
   227 		data->sdl_spec->channels = (Uint8)(metadata->data.stream_info.channels);
   228 		data->sdl_spec->samples = 8192; /* buffer size */
   229 
   230 		total_samples = metadata->data.stream_info.total_samples;
   231 		bps = metadata->data.stream_info.bits_per_sample;
   232 
   233 		data->sdl_spec->size = total_samples * data->sdl_spec->channels *
   234 								(bps / 8);
   235 		data->flac_total_samples = total_samples;
   236 		data->flac_bps = bps;
   237 	}
   238 }
   239 
   240 static void flac_error_load_cb(
   241 				const FLAC__StreamDecoder *decoder,
   242 				FLAC__StreamDecoderErrorStatus status,
   243 				void *client_data)
   244 {
   245 	// print an SDL error based on the error status
   246 	switch (status) {
   247 		case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
   248 			SDL_SetError ("Error processing the FLAC file [LOST_SYNC].");
   249 		break;
   250 		case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
   251 			SDL_SetError ("Error processing the FLAC file [BAD_HEADER].");
   252 		break;
   253 		case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
   254 			SDL_SetError ("Error processing the FLAC file [CRC_MISMATCH].");
   255 		break;
   256 		case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM:
   257 			SDL_SetError ("Error processing the FLAC file [UNPARSEABLE].");
   258 		break;
   259 		default:
   260 			SDL_SetError ("Error processing the FLAC file [UNKNOWN].");
   261 		break;
   262 	}
   263 }
   264 
   265 /* don't call this directly; use Mix_LoadWAV_RW() for now. */
   266 SDL_AudioSpec *Mix_LoadFLAC_RW (SDL_RWops *src, int freesrc,
   267         SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
   268 {
   269 	FLAC__StreamDecoder *decoder = 0;
   270 	FLAC__StreamDecoderInitStatus init_status;
   271 	int was_error = 1;
   272 	int was_init = 0;
   273 	Uint32 samplesize;
   274 
   275 	// create the client data passing information
   276 	FLAC_SDL_Data* client_data;
   277 	client_data = (FLAC_SDL_Data *)SDL_malloc (sizeof (FLAC_SDL_Data));
   278 
   279 	if ((!src) || (!audio_buf) || (!audio_len))   /* sanity checks. */
   280 		goto done;
   281 
   282 	if (!Mix_Init(MIX_INIT_FLAC))
   283 		goto done;
   284 
   285 	if ((decoder = flac.FLAC__stream_decoder_new ()) == NULL) {
   286 		SDL_SetError ("Unable to allocate FLAC decoder.");
   287 		goto done;
   288 	}
   289 
   290 	init_status = flac.FLAC__stream_decoder_init_stream (decoder,
   291 								flac_read_load_cb, flac_seek_load_cb,
   292 								flac_tell_load_cb, flac_length_load_cb,
   293 								flac_eof_load_cb, flac_write_load_cb,
   294 								flac_metadata_load_cb, flac_error_load_cb,
   295 								client_data);
   296 
   297 	if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
   298 		SDL_SetError ("Unable to initialize FLAC stream decoder.");
   299 		goto done;
   300 	}
   301 
   302 	was_init = 1;
   303 
   304 	client_data->sdl_src = src;
   305 	client_data->sdl_spec = spec;
   306 	client_data->sdl_audio_buf = audio_buf;
   307 	client_data->sdl_audio_len = audio_len;
   308 
   309 	if (!flac.FLAC__stream_decoder_process_until_end_of_stream (decoder)) {
   310 		SDL_SetError ("Unable to process FLAC file.");
   311 		goto done;
   312 	}
   313 
   314 	was_error = 0;
   315 
   316 	/* Don't return a buffer that isn't a multiple of samplesize */
   317 	samplesize = ((spec->format & 0xFF) / 8) * spec->channels;
   318 	*audio_len &= ~(samplesize - 1);
   319 
   320 done:
   321 	if (was_init && decoder) {
   322 		flac.FLAC__stream_decoder_finish (decoder);
   323 	}
   324 
   325 	if (decoder) {
   326 		flac.FLAC__stream_decoder_delete (decoder);
   327 	}
   328 
   329 	if (src) {
   330 		if (freesrc)
   331 			SDL_RWclose (src);
   332 		else
   333 			SDL_RWseek (src, 0, RW_SEEK_SET);
   334 	}
   335 
   336 	if (was_error)
   337 		spec = NULL;
   338 
   339     return spec;
   340 }
   341 
   342 #endif // FLAC_MUSIC