#include #include #include "faac.h" // The FAAD includes redefine the same symbols as if they're not intended to // be used by the same program. #undef MAIN #undef SSR #undef LTP #include "funcprotos.h" #include "neaacdec.h" #include "quicktime.h" // Attempts to read more samples than this will crash #define OUTPUT_ALLOCATION 0x100000 #define CLAMP(x, y, z) ((x) = ((x) < (y) ? (y) : ((x) > (z) ? (z) : (x)))) typedef struct { // Decoder objects NeAACDecHandle decoder_handle; NeAACDecFrameInfo frame_info; NeAACDecConfigurationPtr decoder_config; int decoder_initialized; faacEncHandle encoder_handle; faacEncConfigurationPtr encoder_params; // Number of first sample in output relative to file int64_t output_position; // Number of frames int frame_size; int max_frame_bytes; // Interleaved samples float *input_buffer; // Number of frames int input_size; // Number of samples allocated int input_allocated; unsigned char *compressed_buffer; // Encoding objects int bitrate; int quantizer_quality; int encoder_initialized; } quicktime_mp4a_codec_t; static void delete_codec(quicktime_audio_map_t *atrack) { quicktime_mp4a_codec_t *codec = ((quicktime_codec_t*)atrack->codec)->priv; if(codec->decoder_initialized) { NeAACDecClose(codec->decoder_handle); } if(codec->encoder_initialized) { faacEncClose(codec->encoder_handle); if(codec->compressed_buffer) free(codec->compressed_buffer); if(codec->input_buffer) free(codec->input_buffer); } free(codec); } 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_mp4a_codec_t *codec = ((quicktime_codec_t*)track_map->codec)->priv; int64_t current_position = track_map->current_position; int64_t end_position = current_position + samples; quicktime_vbr_t *vbr = &track_map->vbr; if( quicktime_limit_samples(samples) ) return 1; // Initialize decoder if( !codec->decoder_initialized ) { unsigned long samplerate = trak->mdia.minf.stbl.stsd.table[0].sample_rate; // FAAD needs unsigned char here uint8_t channels = track_map->channels; quicktime_init_vbr(vbr, channels); codec->decoder_handle = NeAACDecOpen(); codec->decoder_config = NeAACDecGetCurrentConfiguration(codec->decoder_handle); codec->decoder_config->outputFormat = FAAD_FMT_FLOAT; // codec->decoder_config->defSampleRate = // trak->mdia.minf.stbl.stsd.table[0].sample_rate; NeAACDecSetConfiguration(codec->decoder_handle, codec->decoder_config); char *mp4_hdr = trak->mdia.minf.stbl.stsd.table[0].esds.mpeg4_header; int hdr_len = trak->mdia.minf.stbl.stsd.table[0].esds.mpeg4_header_size; if( !mp4_hdr || !hdr_len ) { quicktime_seek_vbr(track_map, 0, 0); quicktime_read_vbr(file, track_map); if( NeAACDecInit(codec->decoder_handle, quicktime_vbr_input(vbr), quicktime_vbr_input_size(vbr), &samplerate, &channels) < 0 ) return 1; } else { if( NeAACDecInit2(codec->decoder_handle, (unsigned char *)mp4_hdr, hdr_len, &samplerate, &channels) < 0 ) return 1; } //printf("decode %d samplerate=%d channels=%d\n", __LINE__, samplerate, channels); codec->decoder_initialized = 1; } if( quicktime_align_vbr(track_map, current_position) || codec->output_position != quicktime_vbr_output_end(vbr) ) { quicktime_seek_vbr(track_map, current_position, 1); codec->output_position = quicktime_vbr_output_end(vbr); } // Decode until buffer is full while( quicktime_vbr_output_end(vbr) < end_position ) { if(quicktime_read_vbr(file, track_map)) break; bzero(&codec->frame_info, sizeof(NeAACDecFrameInfo)); float *sample_buffer = NeAACDecDecode(codec->decoder_handle, &codec->frame_info, quicktime_vbr_input(vbr), quicktime_vbr_input_size(vbr)); quicktime_shift_vbr(track_map, quicktime_vbr_input_size(vbr)); int result = codec->frame_info.samples / track_map->channels; quicktime_store_vbr_float(track_map, sample_buffer, result); codec->output_position += result; } // Transfer from buffer to output if(output_i) quicktime_copy_vbr_int16(vbr, current_position, samples, output_i, channel); else if(output_f) quicktime_copy_vbr_float(vbr, current_position, samples, output_f, channel); return 0; } static int encode(quicktime_t *file, int16_t **input_i, float **input_f, int track, long samples) { int result = 0; quicktime_audio_map_t *track_map = &(file->atracks[track]); quicktime_trak_t *trak = track_map->track; quicktime_mp4a_codec_t *codec = ((quicktime_codec_t*)track_map->codec)->priv; int channels = quicktime_track_channels(file, track); int i, j, k; if(!codec->encoder_initialized) { unsigned long input_samples; unsigned long max_output_bytes; int sample_rate = quicktime_sample_rate(file, track); codec->encoder_initialized = 1; codec->encoder_handle = faacEncOpen(quicktime_sample_rate(file, track), channels, &input_samples, &max_output_bytes); codec->frame_size = input_samples / channels; codec->max_frame_bytes = max_output_bytes; codec->compressed_buffer = calloc(1, max_output_bytes); codec->encoder_params = faacEncGetCurrentConfiguration(codec->encoder_handle); // Parameters from ffmpeg codec->encoder_params->aacObjectType = LOW; codec->encoder_params->mpegVersion = MPEG4; codec->encoder_params->useTns = 0; codec->encoder_params->allowMidside = 1; codec->encoder_params->inputFormat = FAAC_INPUT_FLOAT; codec->encoder_params->outputFormat = 0; codec->encoder_params->bitRate = codec->bitrate / channels; codec->encoder_params->quantqual = codec->quantizer_quality; codec->encoder_params->bandWidth = sample_rate / 2; if(!faacEncSetConfiguration(codec->encoder_handle, codec->encoder_params)) { fprintf(stderr, "encode: unsupported MPEG-4 Audio configuration!@#!@#\n"); return 1; } // Create esds header unsigned char *buffer; unsigned long buffer_size; faacEncGetDecoderSpecificInfo(codec->encoder_handle, &buffer, &buffer_size); quicktime_set_mpeg4_header(&trak->mdia.minf.stbl.stsd.table[0], buffer, buffer_size); //breaks android player //trak->mdia.minf.stbl.stsd.table[0].version = 1; // Quicktime player needs this. trak->mdia.minf.stbl.stsd.table[0].compression_id = 0xfffe; } // Stack new audio at end of old audio int new_allocation = codec->input_size + samples; if(new_allocation > codec->input_allocated) { codec->input_buffer = realloc(codec->input_buffer, new_allocation * sizeof(float) * channels); codec->input_allocated = new_allocation; } float *output = (float*)codec->input_buffer + codec->input_size * channels; if(input_f) { for(i = 0; i < samples; i++) { for(j = 0; j < channels; j++) { *output++ = input_f[j][i] * 32767; } } } else if(input_i) { for(i = 0; i < samples; i++) { for(j = 0; j < channels; j++) { *output++ = (float)input_i[j][i]; } } } codec->input_size += samples; for(i = 0; i + codec->frame_size < codec->input_size; i += codec->frame_size) { int bytes = faacEncEncode(codec->encoder_handle, (int32_t*)(codec->input_buffer + i * channels), codec->frame_size * channels, codec->compressed_buffer, codec->max_frame_bytes); /* * printf("encode 1 %lld %d %d\n", * track_map->current_position, * codec->frame_size, * bytes); */ // Write out the packet if(bytes) { quicktime_write_vbr_frame(file, track, (char*)codec->compressed_buffer, bytes, codec->frame_size); } } for(j = i * channels, k = 0; j < codec->input_size * channels; j++, k++) { codec->input_buffer[k] = codec->input_buffer[j]; } codec->input_size -= i; return result; } static void flush(quicktime_t *file, int track) { quicktime_audio_map_t *track_map = &(file->atracks[track]); quicktime_mp4a_codec_t *codec = ((quicktime_codec_t*)track_map->codec)->priv; int channels = quicktime_track_channels(file, track); int i; if(codec->encoder_initialized) { for(i = 0; i < codec->input_size && i + codec->frame_size < codec->input_allocated; i += codec->frame_size) { int bytes = faacEncEncode(codec->encoder_handle, (int32_t*)(codec->input_buffer + i * channels), codec->frame_size * channels, codec->compressed_buffer, codec->max_frame_bytes); /* * printf("flush 1 %d %d %d\n", * codec->encoder_params->bitRate, * bytes, * codec->max_frame_bytes); */ // Write out the packet if(bytes) { quicktime_write_vbr_frame(file, track, (char*)codec->compressed_buffer, bytes, codec->frame_size); } } } } static int set_parameter(quicktime_t *file, int track, char *key, void *value) { quicktime_audio_map_t *atrack = &(file->atracks[track]); char *compressor = quicktime_compressor(atrack->track); if(quicktime_match_32(compressor, QUICKTIME_MP4A)) { quicktime_mp4a_codec_t *codec = ((quicktime_codec_t*)atrack->codec)->priv; if(!strcasecmp(key, "mp4a_bitrate")) { codec->bitrate = *(int*)value; } else if(!strcasecmp(key, "mp4a_quantqual")) { codec->quantizer_quality = *(int*)value; } } return 0; } void quicktime_init_codec_mp4a(quicktime_audio_map_t *atrack) { quicktime_codec_t *codec_base = (quicktime_codec_t*)atrack->codec; quicktime_mp4a_codec_t *codec; codec_base->priv = calloc(1, sizeof(quicktime_mp4a_codec_t)); codec_base->delete_acodec = delete_codec; codec_base->decode_audio = decode; codec_base->encode_audio = encode; codec_base->set_parameter = set_parameter; codec_base->flush = flush; codec_base->fourcc = QUICKTIME_MP4A; codec_base->title = "MPEG4 audio"; codec_base->desc = "Audio section of MPEG4 standard"; codec = (quicktime_mp4a_codec_t*)codec_base->priv; // Default encoding parameters here codec->bitrate = 256000; codec->quantizer_quality = 100; }