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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }