/* ** MPEG4IP plugin for FAAD2 ** Copyright (C) 2003 Bill May wmay@cisco.com ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ** ** $Id: faad2.cpp,v 1.3 2004/08/20 08:30:53 menno Exp $ **/ #include "faad2.h" #include #include #include #include #define DEBUG_SYNC 2 #ifndef M_LLU #define M_LLU M_64 #define LLU U64 #endif const char *aaclib="faad2"; /* * Create CAACodec class */ static codec_data_t *aac_codec_create (const char *compressor, int type, int profile, format_list_t *media_fmt, audio_info_t *audio, const uint8_t *userdata, uint32_t userdata_size, audio_vft_t *vft, void *ifptr) { aac_codec_t *aac; aac = (aac_codec_t *)malloc(sizeof(aac_codec_t)); memset(aac, 0, sizeof(aac_codec_t)); aac->m_vft = vft; aac->m_ifptr = ifptr; fmtp_parse_t *fmtp = NULL; // Start setting up FAAC stuff... aac->m_resync_with_header = 1; aac->m_record_sync_time = 1; aac->m_audio_inited = 0; // Use media_fmt to indicate that we're streaming. if (media_fmt != NULL) { // haven't checked for null buffer // This is not necessarilly right - it is, for the most part, but // we should be reading the fmtp statement, and looking at the config. // (like we do below in the userdata section... aac->m_freq = media_fmt->rtpmap->clock_rate; fmtp = parse_fmtp_for_mpeg4(media_fmt->fmt_param, vft->log_msg); if (fmtp != NULL) { userdata = fmtp->config_binary; userdata_size = fmtp->config_binary_len; } } aac->m_info = faacDecOpen(); unsigned long srate; unsigned char chan; if ((userdata == NULL && fmtp == NULL) || (faacDecInit2(aac->m_info, (uint8_t *)userdata, userdata_size, &srate, &chan) < 0)) { if (fmtp != NULL) free_fmtp_parse(fmtp); return NULL; } mp4AudioSpecificConfig mp4ASC; aac->m_output_frame_size = 1024; if (AudioSpecificConfig((unsigned char *)userdata, userdata_size, &mp4ASC)) { if (mp4ASC.frameLengthFlag) { aac->m_output_frame_size = 960; } } aac->m_freq = srate; aac->m_chans = chan; aac->m_faad_inited = 1; aac->m_msec_per_frame = aac->m_output_frame_size; aac->m_msec_per_frame *= M_LLU; aac->m_msec_per_frame /= aac->m_freq; // faad_init_bytestream(&m_info->ld, c_read_byte, c_bookmark, m_bytestream); aa_message(LOG_INFO, aaclib, "Setting freq to %d", aac->m_freq); #if DUMP_OUTPUT_TO_FILE aac->m_outfile = fopen("temp.raw", "w"); #endif if (fmtp != NULL) { free_fmtp_parse(fmtp); } return (codec_data_t *)aac; } void aac_close (codec_data_t *ptr) { if (ptr == NULL) { return; } aac_codec_t *aac = (aac_codec_t *)ptr; faacDecClose(aac->m_info); aac->m_info = NULL; #if DUMP_OUTPUT_TO_FILE fclose(aac->m_outfile); #endif free(aac); } /* * Handle pause - basically re-init the codec */ static void aac_do_pause (codec_data_t *ifptr) { aac_codec_t *aac = (aac_codec_t *)ifptr; aac->m_resync_with_header = 1; aac->m_record_sync_time = 1; aac->m_audio_inited = 0; aac->m_ignore_first_sample = 0; faacDecPostSeekReset(aac->m_info, 0); } /* * Decode task call for FAAC */ static int aac_decode (codec_data_t *ptr, uint64_t ts, int from_rtp, int *sync_frame, uint8_t *buffer, uint32_t buflen, void *userdata) { aac_codec_t *aac = (aac_codec_t *)ptr; unsigned long bytes_consummed; int bits = -1; // struct timezone tz; if (aac->m_record_sync_time) { aac->m_current_frame = 0; aac->m_record_sync_time = 0; aac->m_current_time = ts; aac->m_last_rtp_ts = ts; } else { if (aac->m_last_rtp_ts == ts) { aac->m_current_time += aac->m_msec_per_frame; aac->m_current_frame++; } else { aac->m_last_rtp_ts = ts; aac->m_current_time = ts; aac->m_current_frame = 0; } // Note - here m_current_time should pretty much always be >= rtpts. // If we're not, we most likely want to stop and resync. We don't // need to keep decoding - just decode this frame and indicate we // need a resync... That should handle fast forwards... We need // someway to handle reverses - perhaps if we're more than .5 seconds // later... } if (aac->m_faad_inited == 0) { /* * If not initialized, do so. */ abort(); unsigned long freq; unsigned char chans; faacDecInit(aac->m_info, (unsigned char *)buffer, buflen, &freq, &chans); aac->m_freq = freq; aac->m_chans = chans; aac->m_faad_inited = 1; } uint8_t *buff; unsigned long samples; bytes_consummed = buflen; //aa_message(LOG_DEBUG, aaclib, "decoding %d bits", buflen * 8); faacDecFrameInfo frame_info; buff = (uint8_t *)faacDecDecode(aac->m_info, &frame_info, buffer, buflen); if (buff != NULL) { bytes_consummed = frame_info.bytesconsumed; #if 0 aa_message(LOG_DEBUG, aaclib, LLU" bytes %d samples %d", ts, bytes_consummed, frame_info.samples); #endif if (aac->m_audio_inited != 0) { int tempchans = frame_info.channels; if (tempchans != aac->m_chans) { aa_message(LOG_NOTICE, aaclib, "chupdate - chans from data is %d", tempchans); } } else { int tempchans = frame_info.channels; if (tempchans == 0) { aa_message(LOG_ERR, aaclib, "initializing aac, returned channels are 0"); aac->m_resync_with_header = 1; aac->m_record_sync_time = 1; return bytes_consummed; } aac->m_chans = tempchans; aac->m_freq = frame_info.samplerate; aac->m_vft->audio_configure(aac->m_ifptr, aac->m_freq, aac->m_chans, (audio_format_t)AUDIO_S16SYS, aac->m_output_frame_size); uint8_t *now = aac->m_vft->audio_get_buffer(aac->m_ifptr); aac->m_audio_inited = 1; } /* * good result - give it to audio sync class */ #if DUMP_OUTPUT_TO_FILE fwrite(buff, aac->m_output_frame_size * 4, 1, aac->m_outfile); #endif if (frame_info.samples != 0) { aac->m_vft->audio_load_buffer(aac->m_ifptr, buff, frame_info.samples * 2, aac->m_last_ts, aac->m_resync_with_header); if (aac->m_resync_with_header == 1) { aac->m_resync_with_header = 0; #ifdef DEBUG_SYNC aa_message(LOG_DEBUG, aaclib, "Back to good at "LLU, aac->m_current_time); #endif } } } else { aa_message(LOG_ERR, aaclib, "error return is %d", frame_info.error); aac->m_resync_with_header = 1; #ifdef DEBUG_SYNC aa_message(LOG_ERR, aaclib, "Audio decode problem - at "LLU, aac->m_current_time); #endif } aac->m_last_ts = aac->m_current_time; return (bytes_consummed); } static const char *aac_compressors[] = { "aac ", "mp4a", "enca", NULL }; static int aac_codec_check (lib_message_func_t message, const char *compressor, int type, int profile, format_list_t *fptr, const uint8_t *userdata, uint32_t userdata_size #ifdef HAVE_PLUGIN_VERSION_0_8 ,CConfigSet *pConfig #endif ) { fmtp_parse_t *fmtp = NULL; if (compressor != NULL && strcasecmp(compressor, "MP4 FILE") == 0 && type != -1) { switch (type) { case MP4_MPEG2_AAC_MAIN_AUDIO_TYPE: case MP4_MPEG2_AAC_LC_AUDIO_TYPE: case MP4_MPEG2_AAC_SSR_AUDIO_TYPE: case MP4_MPEG4_AUDIO_TYPE: break; default: return -1; } } if (fptr != NULL && fptr->rtpmap != NULL && fptr->rtpmap->encode_name != NULL) { if (strcasecmp(fptr->rtpmap->encode_name, "mpeg4-generic") != 0) { return -1; } if (userdata == NULL) { fmtp = parse_fmtp_for_mpeg4(fptr->fmt_param, message); if (fmtp != NULL) { userdata = fmtp->config_binary; userdata_size = fmtp->config_binary_len; } } } if (userdata != NULL) { mpeg4_audio_config_t audio_config; decode_mpeg4_audio_config(userdata, userdata_size, &audio_config); message(LOG_DEBUG, "aac", "audio type is %d", audio_config.audio_object_type); if (fmtp != NULL) free_fmtp_parse(fmtp); if (audio_object_type_is_aac(&audio_config) == 0) { return -1; } #if 0 if (audio_config.audio_object_type == 17) { message(LOG_INFO, "aac", "audio type is legal ISMA, but not supported"); return -1; } #endif return 2; } #if 0 // I'm not sure I want to be here if we don't have an audio config if (compressor != NULL) { const char **lptr = aac_compressors; while (*lptr != NULL) { if (strcasecmp(*lptr, compressor) == 0) { return 2; } lptr++; } } #endif return -1; } #ifndef HAVE_PLUGIN_VERSION_0_8 AUDIO_CODEC_WITH_RAW_FILE_PLUGIN("faad2", aac_codec_create, aac_do_pause, aac_decode, NULL, aac_close, aac_codec_check, aac_file_check, aac_file_next_frame, aac_file_used_for_frame, aac_raw_file_seek_to, aac_file_eof ); #else AUDIO_CODEC_WITH_RAW_FILE_PLUGIN("faad2", aac_codec_create, aac_do_pause, aac_decode, NULL, aac_close, aac_codec_check, aac_file_check, aac_file_next_frame, aac_file_used_for_frame, aac_raw_file_seek_to, aac_file_eof, NULL, 0 ); #endif /* end file aa.cpp */