int quicktime_read_chunk(quicktime_t *file, char *output, int track, long chunk, long byte_start, long byte_len) { quicktime_set_position(file, quicktime_chunk_to_offset(file->atracks[track].track, chunk) + byte_start); if(quicktime_read_data(file, output, byte_len)) return 0; else return 1; }
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; }
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; }