Example #1
0
long quicktime_read_audio(quicktime_t *file,
	char *audio_buffer, long samples, int track)
{
	quicktime_audio_map_t *track_map = &file->atracks[track];
	quicktime_trak_t *trak = track_map->track;
	int64_t position, chunk, chunk_sample;
	long ret, bytes, chunk_offset, chunk_len;
	int result = 0;

	position = file->atracks[track].current_position;
	quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, position);
	ret = 0;

	while( samples > 0 ) {
		int64_t offset = quicktime_sample_to_offset(file, trak, position);
		quicktime_set_position(file, offset);
		chunk_offset = position - chunk_sample;
		chunk_len = quicktime_chunk_samples(trak, chunk) - chunk_offset;
		if( chunk_len > samples ) chunk_len = samples;
		else ++chunk;
		chunk_sample = (position += chunk_len);
		bytes = quicktime_samples_to_bytes(trak, chunk_len);
		result = !quicktime_read_data(file, &audio_buffer[ret], bytes);
		if( result ) break;
		ret += bytes;
		samples -= chunk_len;
	}
//printf("quicktime_read_audio 5\n");

	track_map->current_position = position;
	track_map->current_chunk = chunk;
	return !result ? ret : 0;
}
Example #2
0
int quicktime_set_video_position(quicktime_t *file, int64_t frame, int track)
{
	int64_t offset, chunk_sample, chunk;
	quicktime_trak_t *trak;
	if(track >= file->total_vtracks)
	{
		fprintf(stderr, 
			"quicktime_set_video_position: frame=%lld track=%d >= file->total_vtracks %d\n", 
			frame,
			track,
			file->total_vtracks);
		track = file->total_vtracks - 1;
	}

	if(track < file->total_vtracks && track >= 0)
	{
		trak = file->vtracks[track].track;
		file->vtracks[track].current_position = frame;
		quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, frame);
		file->vtracks[track].current_chunk = chunk;
		offset = quicktime_sample_to_offset(file, trak, frame);
		quicktime_set_position(file, offset);
	}
	return 0;
}
Example #3
0
long quicktime_read_audio(quicktime_t *file, 
	char *audio_buffer, 
	long samples, 
	int track)
{
	int64_t chunk_sample, chunk;
	int result = 0, track_num;
	quicktime_trak_t *trak = file->atracks[track].track;
	int64_t fragment_len, chunk_end;
	int64_t start_position = file->atracks[track].current_position;
	int64_t position = file->atracks[track].current_position;
	int64_t start = position, end = position + samples;
	int64_t bytes, total_bytes = 0;
	int64_t buffer_offset;

//printf("quicktime_read_audio 1\n");
	quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, position);
	buffer_offset = 0;

	while(position < end && !result)
	{
		quicktime_set_audio_position(file, position, track);
		fragment_len = quicktime_chunk_samples(trak, chunk);
		chunk_end = chunk_sample + fragment_len;
		fragment_len -= position - chunk_sample;
		if(position + fragment_len > chunk_end) fragment_len = chunk_end - position;
		if(position + fragment_len > end) fragment_len = end - position;

		bytes = quicktime_samples_to_bytes(trak, fragment_len);
/*
 * printf("quicktime_read_audio 2 %llx %llx %d\n", 
 * quicktime_position(file), 
 * quicktime_position(file) + bytes, 
 * samples);
 * sleep(1);
 */
		result = !quicktime_read_data(file, &audio_buffer[buffer_offset], bytes);
//printf("quicktime_read_audio 4\n");

		total_bytes += bytes;
		position += fragment_len;
		chunk_sample = position;
		buffer_offset += bytes;
		chunk++;
	}
//printf("quicktime_read_audio 5\n");

// Create illusion of track being advanced only by samples
	file->atracks[track].current_position = start_position + samples;
	if(result) return 0;
	return total_bytes;
}
Example #4
0
long quicktime_sample_to_offset(quicktime_trak_t *trak, long sample)
{
    long chunk, chunk_sample, chunk_offset1, chunk_offset2;

    if (trak == NULL) {
        return -1;
    }

    quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, sample);
    chunk_offset1 = quicktime_chunk_to_offset(trak, chunk);
    chunk_offset2 = chunk_offset1 + quicktime_sample_range_size(trak, chunk_sample, sample);
    /*printf("quicktime_sample_to_offset chunk %d sample %d chunk_offset %d chunk_sample %d chunk_offset + samples %d\n", */
    /*	 chunk, sample, chunk_offset1, chunk_sample, chunk_offset2); */
    return chunk_offset2;
}
Example #5
0
int quicktime_set_video_position(quicktime_t *file, long frame, int track)
{
	long offset, chunk_sample, chunk;
	quicktime_trak_t *trak;

	if(file->total_vtracks)
	{
		trak = file->vtracks[track].track;
		file->vtracks[track].current_position = frame;
		quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, frame);
		file->vtracks[track].current_chunk = chunk;
		offset = quicktime_sample_to_offset(trak, frame);
		quicktime_set_position(file, offset);
		/*quicktime_update_positions(file); */
	}
	return 0;
}
Example #6
0
int quicktime_set_audio_position(quicktime_t *file, int64_t sample, int track)
{
	int64_t offset, chunk_sample, chunk;
	quicktime_trak_t *trak;

	if(track < file->total_atracks)
	{
		trak = file->atracks[track].track;
		file->atracks[track].current_position = sample;
		quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, sample);
		file->atracks[track].current_chunk = chunk;
		offset = quicktime_sample_to_offset(file, trak, sample);
		quicktime_set_position(file, offset);
	}
	else
		fprintf(stderr, "quicktime_set_audio_position: track >= file->total_atracks\n");

	return 0;
}
Example #7
0
long quicktime_read_audio(quicktime_t *file, char *audio_buffer, long samples, int track)
{
	long chunk_sample, chunk;
	int result = 1, track_num;
	quicktime_trak_t *trak = file->atracks[track].track;
	long fragment_len, chunk_end;
	long position = file->atracks[track].current_position;
	long start = position, end = position + samples;
	long bytes, total_bytes = 0;
	long buffer_offset;

	quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, position);
	buffer_offset = 0;

	while(position < end && result)
	{
		quicktime_set_audio_position(file, position, track);
		fragment_len = quicktime_chunk_samples(trak, chunk);
		chunk_end = chunk_sample + fragment_len;
		fragment_len -= position - chunk_sample;
		if(position + fragment_len > chunk_end) fragment_len = chunk_end - position;
		if(position + fragment_len > end) fragment_len = end - position;

		bytes = quicktime_samples_to_bytes(trak, fragment_len);
		result = quicktime_read_data(file, &audio_buffer[buffer_offset], bytes);

		total_bytes += bytes;
		position += fragment_len;
		chunk_sample = position;
		buffer_offset += bytes;
		chunk++;
	}

	file->atracks[track].current_position = position;
	if(!result) return 0;
	return total_bytes;
}
Example #8
0
static int decode(quicktime_t *file, 
					int16_t *output_i, 
					float *output_f, 
					long samples, 
					int track, 
					int channel)
{
	quicktime_audio_map_t *track_map = &(file->atracks[track]);
	quicktime_trak_t *trak = track_map->track;
	quicktime_qdm2_codec_t *codec = ((quicktime_codec_t*)track_map->codec)->priv;
	quicktime_stsd_table_t *stsd_table = &trak->mdia.minf.stbl.stsd.table[0];
	int64_t current_position = track_map->current_position;
	int64_t end_position = current_position + samples;
	quicktime_frma_t *frma = &stsd_table->frma;
	int channels = track_map->channels;
	int i, j;
	int debug = 0;
	int64_t chunk_sample;
	


// Initialize decoder
	if(!codec->decoder_initialized)
	{
		pthread_mutex_lock(&ffmpeg_lock);
		if(!ffmpeg_initialized)
		{
			ffmpeg_initialized = 1;
			av_register_all();
		}

		codec->decoder = avcodec_find_decoder(CODEC_ID_QDM2);
		if(!codec->decoder)
		{
			printf("qdm2.c: decode: no ffmpeg decoder found.\n");
			return 1;
		}

// allocate the codec and fill in header
		AVCodecContext *context = avcodec_alloc_context3(codec->decoder);
		codec->decoder_context = context;
		codec->decoder_context->sample_rate = trak->mdia.minf.stbl.stsd.table[0].sample_rate;
		codec->decoder_context->channels = track_map->channels;

		if(frma->data && frma->data_size)
		{
			context->extradata = (unsigned char *)frma->data;
			context->extradata_size = frma->data_size;
		}

		if(file->cpus > 1)
		{
//			avcodec_thread_init(context, file->cpus);
			context->thread_count = file->cpus;
		}
	
		if(avcodec_open2(context, codec->decoder, 0) < 0)
		{
			printf("qdm2.c: decode: avcodec_open failed.\n");
			return 1;
		}
		pthread_mutex_unlock(&ffmpeg_lock);
		
		codec->input_buffer = calloc(sizeof(int16_t),
			track_map->channels * OUTPUT_ALLOCATION);
		codec->input_allocated = OUTPUT_ALLOCATION;

			
		codec->decoder_initialized = 1;
	}


	if(samples > OUTPUT_ALLOCATION)
	{
		printf("qdm2: decode: can't decode more than 0x%x samples at a time.\n",
			OUTPUT_ALLOCATION);
		return 1;
	}

	if(debug)
	{
		printf("qdm2 decode: current_position=%jd end_position=%jd input_size=%d input_end=%jd\n",
			current_position, end_position, codec->input_size, codec->input_end);
	}

// printf("qdm2 decode: current_position=%lld end_position=%lld chunk_sample=%lld chunk=%lld\n", 
// current_position, 
// end_position,
// chunk_sample,
// chunk);


	if(current_position < codec->input_end - codec->input_size ||
		current_position > codec->input_end)
	{
// Desired start point is outside existing range.  Reposition buffer pointer
// to start time of nearest chunk and start over.
		quicktime_chunk_of_sample(&chunk_sample, 
			&codec->current_chunk, 
			trak, 
			current_position);
		codec->input_size = 0;
		codec->input_ptr = 0;
		codec->input_end = chunk_sample;
	}

// Decode complete chunks until samples is reached
	int total_chunks = trak->mdia.minf.stbl.stco.total_entries;
	while(codec->input_end < end_position)
	{
		int64_t offset = quicktime_chunk_to_offset(file, 
			trak, 
			codec->current_chunk);
		int64_t max_offset = quicktime_chunk_to_offset(file, 
			trak, 
			codec->current_chunk + 1);
		quicktime_set_position(file, offset);
		allocate_compressed(codec, 3);

		if(debug)
		{
			printf("qdm2 decode: input_end=%jd chunk=%jd offset=0x%jx\n", 
				codec->input_end, codec->current_chunk, offset);
		}

// Read fragments of chunk
		while(1)
		{
// Hit next chunk of audio
			if(max_offset > offset && quicktime_position(file) >= max_offset) break;
			if(!quicktime_read_data(file, 
				(char*)codec->compressed_buffer + codec->compressed_size, 
				3))
				break;
			if(codec->compressed_buffer[codec->compressed_size] != 0x82)
			{
//				printf("qdm2: decode: position=0x%llx\n", quicktime_position(file));
				break;
			}
			int fragment_size = 3 + ((codec->compressed_buffer[codec->compressed_size + 1] << 8) |
				codec->compressed_buffer[codec->compressed_size + 2]);
// Sanity check
			if(fragment_size > OUTPUT_ALLOCATION) break;
// Expand compressed buffer
			allocate_compressed(codec, 
				codec->compressed_size + fragment_size + 1024);
			if(!quicktime_read_data(file, 
				(char*)codec->compressed_buffer + codec->compressed_size + 3, 
				fragment_size - 3))
				break;

			codec->compressed_size += fragment_size;

			AVPacket avpkt;
			av_init_packet(&avpkt);
			avpkt.data = codec->compressed_buffer;
			avpkt.size = codec->compressed_size;

			int count = 0;
// Repeat this sequence until ffmpeg stops outputting samples
			while(1)
			{
				if(!codec->temp_buffer)
					codec->temp_buffer = calloc(sizeof(int16_t), OUTPUT_ALLOCATION);
				int bytes_decoded = OUTPUT_ALLOCATION * sizeof(int16_t);
				int result = quicktime_decode_audio3(codec->decoder_context, 
					codec->temp_buffer, &bytes_decoded, &avpkt);

// Shift compressed buffer
				if(result > 0)
				{
					avpkt.size -= result;
					avpkt.data += result;
					count += result;
				}

//printf("avcodec_decode_audio result=%d bytes_decoded=%d fragment_size=%d codec->compressed_size=%d\n", 
//result, bytes_decoded, fragment_size, codec->compressed_size);
/*
 * static FILE *test = 0;
 * if(!test) test = fopen("/tmp/debug", "w");
 * fwrite(codec->temp_buffer, 1, bytes_decoded, test);
 * fflush(test);
 */
				for(i = 0; i < bytes_decoded / channels / sizeof(int16_t); i++)
				{
					for(j = 0; j < channels; j++)
						codec->input_buffer[codec->input_ptr * channels + j] =
							codec->temp_buffer[i * channels + j];
					codec->input_ptr++;
					if(codec->input_ptr >= codec->input_allocated)
						codec->input_ptr = 0;
				}
				codec->input_end += bytes_decoded / channels / sizeof(int16_t);
				codec->input_size += bytes_decoded / channels / sizeof(int16_t);

				if(bytes_decoded <= 0) break;
			}

			if( count > 0 ) {
				memcpy(codec->compressed_buffer,
					codec->compressed_buffer + count,
					codec->compressed_size -= count);
			}
		}

		codec->current_chunk++;
		if(codec->current_chunk >= total_chunks) break;
	}

// Transfer from buffer to output
	int input_ptr = codec->input_ptr - (codec->input_end - current_position);
	if(input_ptr < 0) input_ptr += codec->input_allocated;
	if(output_i)
	{
		for(i = 0; i < samples; i++)
		{
			output_i[i] = codec->input_buffer[input_ptr * channels + channel];
			input_ptr++;
			if(input_ptr >= codec->input_allocated) input_ptr = 0;
		}
	}
	else
	if(output_f)
	{
		for(i = 0; i < samples; i++)
		{
			output_f[i] = (float)codec->input_buffer[input_ptr * channels + channel] / 32768.0;
			input_ptr++;
			if(input_ptr >= codec->input_allocated) input_ptr = 0;
		}
	}
	return 0;
}
Example #9
0
static int decode(quicktime_t *file, 
					int16_t *output_i, 
					float *output_f,
					long samples, 
					int track, 
					int channel)
{
	int result = 0;
	int64_t chunk, chunk_sample, chunk_samples;
	int64_t i, chunk_start, chunk_end;
	quicktime_trak_t *trak = file->atracks[track].track;
	quicktime_ima4_codec_t *codec = ((quicktime_codec_t*)file->atracks[track].codec)->priv;

/* Get the first chunk with this routine and then increase the chunk number. */
	quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, file->atracks[track].current_position);

/* Read chunks and extract ranges of samples until the output is full. */
	for(i = 0; i < samples && !result; )
	{
/* Get chunk we're on. */
		chunk_samples = quicktime_chunk_samples(trak, chunk);

		if(!codec->work_buffer ||
			codec->chunk != chunk ||
			codec->buffer_channel != channel)
		{
/* read a new chunk if necessary */
			result = ima4_decode_chunk(file, track, chunk, channel);
		}

/* Get boundaries from the chunk */
		chunk_start = 0;
		if(chunk_sample < file->atracks[track].current_position)
			chunk_start = file->atracks[track].current_position - chunk_sample;

		chunk_end = chunk_samples;
		if(chunk_sample + chunk_end > file->atracks[track].current_position + samples)
			chunk_end = file->atracks[track].current_position + samples - chunk_sample;

/* Read from the chunk */
		if(output_i)
		{
/*printf("decode_ima4 1 chunk %ld %ld-%ld output %ld\n", chunk, chunk_start + chunk_sample, chunk_end + chunk_sample, i); */
			while(chunk_start < chunk_end)
			{
				output_i[i++] = codec->work_buffer[chunk_start++];
			}
/*printf("decode_ima4 2\n"); */
		}
		else
		if(output_f)
		{
			while(chunk_start < chunk_end)
			{
				output_f[i++] = (float)codec->work_buffer[chunk_start++] / 32767;
			}
		}

		chunk++;
		chunk_sample += chunk_samples;
	}

	return result;
}
Example #10
0
static int decode(quicktime_t *file, 
					int16_t *output_i, 
					float *output_f, 
					long samples, 
					int track, 
					int channel)
{
	int result = 0;
	int bytes;
	int i, j;
	quicktime_audio_map_t *track_map = &(file->atracks[track]);
	quicktime_trak_t *trak = track_map->track;
	quicktime_vorbis_codec_t *codec = ((quicktime_codec_t*)track_map->codec)->priv;
	long current_position = track_map->current_position;
	long end_position = current_position + samples;
  	unsigned char *buffer;
// End of data in ogg buffer
	int eos = 0;
// End of file
	int eof = 0;
	float *pcm;
	int have_chunk = 0;


	if(samples > OUTPUT_ALLOCATION)
		printf("vorbis.c decode: can't read more than %p samples at a time.\n", OUTPUT_ALLOCATION);



	if(output_i) bzero(output_i, sizeof(int16_t) * samples);
	if(output_f) bzero(output_f, sizeof(float) * samples);







// Seeked outside output buffer's range or not initialized: restart
	if(current_position < codec->output_position - codec->output_size ||
		current_position > codec->output_position ||
		!codec->decode_initialized)
	{

		quicktime_chunk_of_sample(&codec->output_position, 
			&codec->chunk, 
			trak, 
			current_position);
// We know the first ogg packet in the chunk has a pcm_offset from the encoding.

		codec->output_size = 0;
		codec->output_end = 0;
		codec->chunk_samples = 0;



	
// Initialize and load initial buffer for decoding
		if(!codec->decode_initialized)
		{
			int init_chunk = 1;
			codec->decode_initialized = 1;

			codec->output = malloc(sizeof(float*) * track_map->channels);
			for(i = 0; i < track_map->channels; i++)
			{
				codec->output[i] = malloc(sizeof(float) * OUTPUT_ALLOCATION);
			}

			codec->output_allocated = OUTPUT_ALLOCATION;

        	ogg_sync_init(&codec->dec_oy); /* Now we can read pages */




			READ_CHUNK(init_chunk);
			init_chunk++;

   	 		if(ogg_sync_pageout(&codec->dec_oy, &codec->dec_og)!=1)
			{
				fprintf(stderr, "decode: ogg_sync_pageout: Must not be Vorbis data\n");
				return 1;
			}


    		ogg_stream_init(&codec->dec_os, ogg_page_serialno(&codec->dec_og));
    		vorbis_info_init(&codec->dec_vi);
    		vorbis_comment_init(&codec->dec_vc);

    		if(ogg_stream_pagein(&codec->dec_os, &codec->dec_og) < 0)
			{
    	  		fprintf(stderr,"decode: ogg_stream_pagein: stream version mismatch perhaps.\n");
    	  		return 1;
    		}

			if(ogg_stream_packetout(&codec->dec_os, &codec->dec_op) != 1)
			{
				fprintf(stderr, "decode: ogg_stream_packetout: Must not be Vorbis data\n");
    	  		return 1;
			}

			if(vorbis_synthesis_headerin(&codec->dec_vi, &codec->dec_vc, &codec->dec_op) < 0)
			{
				fprintf(stderr, "decode: vorbis_synthesis_headerin: not a vorbis header\n");
				return 1;
			}


			i = 0;
			while(i < 2)
			{
				while(i < 2)
				{
					result = ogg_sync_pageout(&codec->dec_oy, &codec->dec_og);
					if(result == 0) break;

					if(result == 1)
					{
						ogg_stream_pagein(&codec->dec_os, &codec->dec_og);

						while(i < 2)
						{
							result = ogg_stream_packetout(&codec->dec_os, &codec->dec_op);

							if(result == 0) break;

							if(result < 0)
							{
								fprintf(stderr, "decode: ogg_stream_packetout: corrupt secondary header\n");
								return 1;
							}

							vorbis_synthesis_headerin(&codec->dec_vi, &codec->dec_vc, &codec->dec_op);
							i++;




						}
					}
				}

				if(i < 2)
				{
					READ_CHUNK(init_chunk);
					init_chunk++;
				}

// Header should never span more than one chunk so assume it's done here
			}

			vorbis_synthesis_init(&codec->dec_vd, &codec->dec_vi);
			vorbis_block_init(&codec->dec_vd, &codec->dec_vb);

// Also the first chunk needed in decoding so don't reread after this.
			if(codec->chunk == init_chunk - 1) 
			{
				have_chunk = 1;
				codec->chunk++;
			}
		}




// Don't already have initial chunk from header
		if(!have_chunk)
		{
// Get initial chunk for decoding at new location
// From vorbisfile.c
/* clear out decoding machine state */
			ogg_stream_clear(&codec->dec_os);
			vorbis_dsp_clear(&codec->dec_vd);
			vorbis_block_clear(&codec->dec_vb);
    		ogg_sync_reset(&codec->dec_oy);

    		ogg_stream_init(&codec->dec_os, ogg_page_serialno(&codec->dec_og));
        	ogg_sync_init(&codec->dec_oy);
			vorbis_synthesis_init(&codec->dec_vd, &codec->dec_vi);
			vorbis_block_init(&codec->dec_vd, &codec->dec_vb);


			READ_CHUNK(codec->chunk);
			codec->chunk++;
			have_chunk = 1;
		}
	}

// Assume the chunk exists by now and rely on libogg to say if it's out of
// data.
	have_chunk = 1;











// Read chunks until output buffer is on or after end_position
	result = 0;
	while(codec->output_position < end_position)
	{


// Read chunk to decode if it hasn't been read yet.
		if(!have_chunk)
		{
			codec->chunk_samples = 0;

			READ_CHUNK(codec->chunk);
			if(result) break;
			codec->chunk++;
		}

		eos = 0;
		while(!eos)
		{
			result = ogg_sync_pageout(&codec->dec_oy, &codec->dec_og);







// Need more data from chunk
			if(result == 0)
			{
// End of chunk
				eos = 1;
			}
			else
// This stage checks for OggS and a checksum error.
// It doesn't tell if it's the end of a chunk.  Need to manually parse OggS
// pages to figure out how big the chunk is.
			if(result < 0)
			{
//printf("ogg_sync_pageout=-1\n");
				;
			}
			else
			{
				ogg_stream_pagein(&codec->dec_os, &codec->dec_og);



				while(!eos)
				{
//printf("decode 7\n");
					result = ogg_stream_packetout(&codec->dec_os, &codec->dec_op);

//printf("decode 8 %d\n", result);
					if(result == 0)
					{
//printf("ogg_stream_packetout=0\n");
// End of page
						eos = 1;
					}
					else
// This stage doesn't check for OggS.
					if(result < 0)
					{
//printf("ogg_stream_packetout=-1\n");
					}
					else
					{
						float **pcm;







						if(vorbis_synthesis(&codec->dec_vb, &codec->dec_op) == 0)
						{
							vorbis_synthesis_blockin(&codec->dec_vd, 
								&codec->dec_vb);
						}


						while((result = vorbis_synthesis_pcmout(&codec->dec_vd, &pcm)) > 0)
						{
//printf("vorbis_synthesis_pcmout=%x\n", result);
							for(i = 0; i < track_map->channels; i++)
							{
								float *output_channel = codec->output[i];
								float *input_channel = pcm[i];
								int k = codec->output_end;

								for(j = 0; j < result; j++)
								{
									output_channel[k++] = input_channel[j];
									if(k >= codec->output_allocated)
										k = 0;
								}
								
								if(i == track_map->channels - 1) 
									codec->output_end = k;
							}
//printf("codec->output_end = %d\n", codec->output_end);

							codec->output_position += result;
							codec->output_size += result;
							codec->chunk_samples += result;
							if(codec->output_size > codec->output_allocated)
								codec->output_size = codec->output_allocated;
							vorbis_synthesis_read(&codec->dec_vd, result);
						}
					}
//printf("decode 11\n");
				}

// Reset end of page so it isn't interpreted as an end of chunk
				eos = 0;
			}
		}


// Next chunk
		if(eos)
		{
//printf("decode 12 got=%x\n", codec->chunk_samples);
			have_chunk = 0;
		}
	}


// Fill silence
	while(codec->output_position < end_position)
	{
		for(i = 0; i < track_map->channels; i++)
			codec->output[i][codec->output_end] = 0;
		
		codec->output_end++;
		if(codec->output_end >= codec->output_allocated)
			codec->output_end = 0;
		codec->output_position++;
	}
//printf("decode 15\n");


//printf("decode 2 codec->output_position=%lld codec->output_end=%d codec->output_size=%d\n", 
//	codec->output_position, codec->output_end, codec->output_size);

	current_position = track_map->current_position;
	i = codec->output_end - (codec->output_position - current_position);
	j = 0;
	while(i < 0) i += codec->output_allocated;
	pcm = codec->output[channel];

	if(output_i)
	{
		for( ; j < samples; j++)
		{
			int sample = pcm[i] * 32767;
			CLAMP(sample, -32768, 32767);
			output_i[j] = sample;

			i++;
			if(i >= codec->output_allocated) i = 0;
		}
	}
	else
	if(output_f)
	{
		for( ; j < samples; j++)
		{
			output_f[j] = pcm[i];
			i++;
			if(i >= codec->output_allocated) i = 0;
		}
	}
//printf("decode 16\n");

	return 0;
}