├── Makefile ├── README.md └── mod_aac.c /Makefile: -------------------------------------------------------------------------------- 1 | BASE=../../../.. 2 | #BASE=/usr/src/freeswitch 3 | 4 | include $(BASE)/build/modmake.rules 5 | 6 | LOCAL_LDFLAGS = -lfaac -lmp4v2 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mod_aac 2 | 3 | FreeSWITCH module for AAC Codec. https://en.wikipedia.org/wiki/FAAC 4 | 5 | This mod was written many years ago when I learning to write FreeSWITCH modules. It probably doesn't build on the current version of FreeSWITCH. 6 | 7 | The code is simple and hope still useful. 8 | 9 | Pull Request is welcome. 10 | 11 | ## ToDo: 12 | 13 | * [ ] Fix build on latest FreeSWITCH master 14 | * [ ] Add cmake or autotools build scripts 15 | * [ ] Add some test cases 16 | 17 | ## build 18 | 19 | Put this mod in the freeswitch source dir, your freeswitch should already been built and installed. 20 | 21 | ``` 22 | cd freeswitch/src/mod/ 23 | mkdir rts 24 | cd rts 25 | git clone https://github.com/rts-cn/mod_aac 26 | cd mod_aac 27 | make 28 | make install 29 | ``` 30 | 31 | ## load 32 | 33 | ``` 34 | load mod_aac 35 | ``` 36 | 37 | ## FAQ 38 | 39 | Q: What License? 40 | 41 | A: Same as FreeSWITCH. 42 | 43 | Q: Do you accept Pull Request? 44 | 45 | A: Sure. Thanks. 46 | -------------------------------------------------------------------------------- /mod_aac.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular of AAC Codec 3 | * Copyright (C) 2005-2020, Seven Du 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Module of AAC Codec 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Seven Du 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Seven Du 27 | * 28 | * 29 | * mod_aac.c -- AAC Audio Codec 30 | * 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | SWITCH_MODULE_LOAD_FUNCTION(mod_aac_load); 38 | SWITCH_MODULE_DEFINITION(mod_aac, mod_aac_load, NULL, NULL); 39 | 40 | typedef struct aac_context_s { 41 | faacEncHandle *encoder; 42 | switch_size_t input_samples; 43 | switch_size_t max_output_bytes; 44 | switch_buffer_t *buffer; 45 | uint8_t *input_buff; 46 | uint8_t *out_buff; 47 | uint8_t *asc; 48 | unsigned long len; 49 | } aac_context_t; 50 | 51 | static switch_status_t switch_aac_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) 52 | { 53 | int encoding, decoding; 54 | aac_context_t *context; 55 | faacEncConfigurationPtr faac_cfg; 56 | //unsigned long input_samples, max_output_bytes; 57 | 58 | encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); 59 | decoding = (flags & SWITCH_CODEC_FLAG_DECODE); 60 | 61 | if (!(encoding || decoding)) { 62 | return SWITCH_STATUS_FALSE; 63 | } else { 64 | if (codec->fmtp_in) { 65 | codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in); 66 | } 67 | } 68 | 69 | context = switch_core_alloc(codec->memory_pool, sizeof(*context)); 70 | memset(context, 0, sizeof(*context)); 71 | codec->private_info = context; 72 | context->encoder = faacEncOpen( 73 | codec->implementation->samples_per_second, 74 | codec->implementation->number_of_channels, 75 | &context->input_samples, &context->max_output_bytes); 76 | 77 | if (!context->encoder) goto error; 78 | 79 | switch_buffer_create_dynamic(&context->buffer, context->input_samples * 2, context->input_samples * 2 * 8, 0); 80 | context->input_buff = (uint8_t *)switch_core_alloc(codec->memory_pool, context->input_samples * 2); 81 | context->out_buff = (uint8_t *)switch_core_alloc(codec->memory_pool, context->max_output_bytes); 82 | faac_cfg = faacEncGetCurrentConfiguration(context->encoder); 83 | if (faac_cfg->version != FAAC_CFG_VERSION) { 84 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "wrong libfaac version (compiled for: %d, using %d)\n", FAAC_CFG_VERSION, faac_cfg->version); 85 | goto error; 86 | } 87 | 88 | // faac_cfg->aacObjectType = MAIN; 89 | faac_cfg->aacObjectType = LOW; 90 | faac_cfg->mpegVersion = MPEG4; 91 | faac_cfg->useTns = 0; 92 | faac_cfg->allowMidside = 1; 93 | // faac_cfg->bitRate = avctx->bit_rate / avctx->channels; 94 | // faac_cfg->bandWidth = avctx->cutoff; 95 | faac_cfg->outputFormat = 0; /* user raw stream */ 96 | faac_cfg->inputFormat = FAAC_INPUT_16BIT; 97 | // avctx->frame_size = samples_input / avctx->channels; 98 | 99 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "aac config:\n" 100 | " input samples: %lu\n" 101 | " max output bytes: %lu\n" 102 | " bitrate: %lu\n" 103 | " bandwidth: %u\n", 104 | context->input_samples, context->max_output_bytes, 105 | faac_cfg->bitRate, faac_cfg->bandWidth); 106 | 107 | if (!faacEncSetConfiguration(context->encoder, faac_cfg)) { 108 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "faac doesn't support this bitrate %lu\n", faac_cfg->bitRate); 109 | goto error; 110 | } 111 | 112 | { 113 | faacEncGetDecoderSpecificInfo(context->encoder, &context->asc, &context->len); 114 | 115 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "len: %lu %x %x\n", context->len, *(context->asc), *(context->asc+1) ); 116 | } 117 | 118 | return SWITCH_STATUS_SUCCESS; 119 | 120 | error: 121 | return SWITCH_STATUS_FALSE; 122 | } 123 | 124 | static switch_status_t switch_aac_encode(switch_codec_t *codec, 125 | switch_codec_t *other_codec, 126 | void *decoded_data, 127 | uint32_t decoded_data_len, 128 | uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, 129 | unsigned int *flag) 130 | { 131 | aac_context_t *context = (aac_context_t *)codec->private_info; 132 | switch_buffer_t *buffer = context->buffer; 133 | int ret = 0; 134 | switch_size_t size = 0; 135 | uint8_t *aac_input_buff = context->input_buff; 136 | 137 | /* encode buffer data */ 138 | if (decoded_data_len == 0) { 139 | switch_size_t remain_size = 0; 140 | //remain_size = switch_buffer_len(buffer); 141 | remain_size = switch_buffer_write(buffer, NULL, 0); 142 | memset(aac_input_buff, 0, context->input_samples * 2); 143 | if (remain_size > 0) { 144 | switch_buffer_read(buffer, aac_input_buff, remain_size); 145 | } 146 | 147 | ret = faacEncEncode(context->encoder, (int32_t *)aac_input_buff, remain_size, encoded_data, context->max_output_bytes); 148 | } else { 149 | size = switch_buffer_write(buffer, decoded_data, decoded_data_len); 150 | if (size < context->input_samples * 2) { 151 | //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "buffer size %lu \n", size); 152 | goto end; 153 | } 154 | 155 | memset(aac_input_buff, 0, context->input_samples * 2); 156 | switch_buffer_read(buffer, aac_input_buff, context->input_samples * 2); 157 | ret = faacEncEncode(context->encoder, (int32_t *)aac_input_buff, context->input_samples, encoded_data, context->max_output_bytes); 158 | } 159 | 160 | if (ret < 0) { 161 | *encoded_data_len = 0; 162 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "encode failed!\n"); 163 | return SWITCH_STATUS_FALSE; 164 | // *flag |= SFF_CNG; 165 | // return SWITCH_STATUS_SUCCESS; 166 | } 167 | 168 | end: 169 | //if (ret > 0) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ret: %d\n", ret); 170 | *encoded_data_len = ret; 171 | return SWITCH_STATUS_SUCCESS; 172 | } 173 | 174 | static switch_status_t switch_aac_decode(switch_codec_t *codec, 175 | switch_codec_t *other_codec, 176 | void *encoded_data, 177 | uint32_t encoded_data_len, 178 | uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, 179 | unsigned int *flag) 180 | { 181 | return SWITCH_STATUS_FALSE; 182 | } 183 | 184 | static switch_status_t switch_aac_destroy(switch_codec_t *codec) 185 | { 186 | aac_context_t *context = (aac_context_t *)codec->private_info; 187 | 188 | //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "destroy:====\n"); 189 | switch_safe_free(context->asc); 190 | switch_buffer_destroy(&context->buffer); 191 | if (context->encoder) faacEncClose(context->encoder); 192 | 193 | return SWITCH_STATUS_SUCCESS; 194 | } 195 | 196 | SWITCH_STANDARD_APP(aac_function) 197 | { 198 | switch_channel_t *channel = switch_core_session_get_channel(session); 199 | switch_frame_t *frame; 200 | switch_status_t status; 201 | switch_codec_t codec = { 0 }; 202 | //uint8_t buffer[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; 203 | //unsigned int flag; 204 | switch_codec_t *read_codec = NULL; 205 | switch_codec_t L16_codec = { 0 }; 206 | uint8_t *L16_out_buff = NULL; 207 | uint32_t L16_out_buff_datalen = 0; 208 | switch_size_t PCMU_packet_datalen; 209 | aac_context_t *context = NULL; 210 | uint8_t *encoded_data = NULL; 211 | uint32_t encoded_data_len = 0; 212 | //uint32_t len = 0; 213 | uint32_t rate = 0; 214 | MP4FileHandle aacfile = MP4_INVALID_FILE_HANDLE; 215 | MP4TrackId MP4track = 0; 216 | const char *file_path = data; 217 | 218 | switch_channel_answer(channel); 219 | 220 | read_codec = switch_core_session_get_read_codec(session); 221 | status = switch_core_codec_init(&codec, "AAC", "fmtp", 8000, 20, 1, SWITCH_CODEC_FLAG_ENCODE, NULL, switch_core_session_get_pool(session)); 222 | 223 | if (switch_core_codec_init(&L16_codec, "L16", NULL, 8000, 20, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { 224 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "init L16 error!\n"); 225 | return; 226 | } 227 | 228 | if (status != SWITCH_STATUS_SUCCESS) { 229 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't initialize codec"); 230 | return; 231 | } 232 | 233 | //PCMU_packet_datalen = read_codec->implementation->bits_per_second / (1000000 / read_codec->implementation->bits_per_secondmicroseconds_per_packet); 234 | PCMU_packet_datalen = 160; 235 | L16_out_buff_datalen = PCMU_packet_datalen * 2; 236 | L16_out_buff = (uint8_t *)malloc(L16_out_buff_datalen); 237 | context = (aac_context_t *)codec.private_info; 238 | encoded_data = context->out_buff; 239 | 240 | if (!file_path) file_path = "/tmp/test_aac.m4a"; 241 | 242 | aacfile = MP4CreateEx(file_path, 0, 1, 1, NULL, 0, NULL, 0); 243 | MP4SetTimeScale(aacfile, 90000); 244 | MP4track = MP4AddAudioTrack(aacfile, 8000, 1024, MP4_MPEG4_AUDIO_TYPE); 245 | MP4SetAudioProfileLevel(aacfile, 0x0F); 246 | MP4SetTrackESConfiguration(aacfile, MP4track, context->asc, context->len); 247 | 248 | while(switch_channel_ready(channel)) { 249 | status = switch_core_session_read_frame(session, &frame, SWITCH_IO_FLAG_SINGLE_READ, 0); 250 | 251 | if (!SWITCH_READ_ACCEPTABLE(status)) { 252 | break; 253 | } 254 | 255 | if (!frame) continue; 256 | 257 | if (switch_test_flag(frame, SFF_CNG)) { 258 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "this is cng\n"); 259 | continue; 260 | } 261 | 262 | /* do decode first */ 263 | memset(L16_out_buff, 0, 320); 264 | switch_core_codec_decode(read_codec, &L16_codec, frame->data, frame->datalen, 8000, L16_out_buff, &L16_out_buff_datalen, &rate, &frame->flags); 265 | 266 | //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "decode success len %d ----> len %d\n", frame->datalen, L16_out_buff_datalen); 267 | 268 | switch_core_codec_encode(&codec, NULL, L16_out_buff, L16_out_buff_datalen, 8000, encoded_data, &encoded_data_len, &rate, &frame->flags); 269 | 270 | if (encoded_data_len > 0) { 271 | MP4WriteSample(aacfile, MP4track, encoded_data, encoded_data_len, MP4_INVALID_DURATION, 0, 1); 272 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "read %d, encoded: %d\n", L16_out_buff_datalen, encoded_data_len); 273 | } 274 | } 275 | 276 | /* encode buffer data */ 277 | while(1) { 278 | memset(L16_out_buff, 0, 320); 279 | switch_core_codec_encode(&codec, NULL, L16_out_buff, 0, 8000, encoded_data, &encoded_data_len, &rate, NULL); 280 | 281 | if (encoded_data_len > 0) { 282 | MP4WriteSample(aacfile, MP4track, encoded_data, encoded_data_len, MP4_INVALID_DURATION, 0, 1); 283 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "encoded: %d\n", encoded_data_len); 284 | } 285 | if (encoded_data_len == 0) break; 286 | } 287 | 288 | MP4Close(aacfile, 0); 289 | switch_safe_free(L16_out_buff); 290 | if (codec.implementation) switch_core_codec_destroy(&codec); 291 | if (L16_codec.implementation) switch_core_codec_destroy(&L16_codec); 292 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "aac app end!\n"); 293 | } 294 | 295 | SWITCH_MODULE_LOAD_FUNCTION(mod_aac_load) 296 | { 297 | switch_application_interface_t *app_interface; 298 | switch_codec_interface_t *codec_interface; 299 | int mpf = 20000, spf = 160, bpf = 320, counta, countb; 300 | int rates[] = {0, 8000, 44100, 48000, 88200, 96000, 176400, 192000}; 301 | switch_payload_t ianacode[4] = { 0, 99, 99, 99 }; 302 | 303 | /* connect my internal structure to the blank pointer passed to me */ 304 | *module_interface = switch_loadable_module_create_module_interface(pool, modname); 305 | SWITCH_ADD_CODEC(codec_interface, "AAC"); 306 | 307 | for (counta = 1; counta <= 3; counta++) { 308 | for (countb = 1; countb > 0; countb--) { 309 | switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ 310 | ianacode[counta], /* the IANA code number */ 311 | "AAC", /* the IANA code name */ 312 | NULL, /* default fmtp to send (can be overridden by the init function) */ 313 | rates[counta], /* samples transferred per second */ 314 | rates[counta], /* actual samples transferred per second */ 315 | 0, /* bits transferred per second */ 316 | mpf * countb, /* number of microseconds per frame */ 317 | spf * countb, /* number of samples per frame */ 318 | bpf * countb, /* number of bytes per frame decompressed */ 319 | 0, /* number of bytes per frame compressed */ 320 | 1, /* number of channels represented */ 321 | 1, /* number of frames per network packet */ 322 | switch_aac_init, /* function to initialize a codec handle using this implementation */ 323 | switch_aac_encode, /* function to encode raw data into encoded data */ 324 | switch_aac_decode, /* function to decode encoded data into raw data */ 325 | switch_aac_destroy); /* deinitalize a codec handle using this implementation */ 326 | } 327 | spf = spf * 2; 328 | } 329 | 330 | SWITCH_ADD_APP(app_interface, "aac", "aac", "AAC test", aac_function, "", SAF_NONE); 331 | 332 | /* indicate that the module should continue to be loaded */ 333 | 334 | return SWITCH_STATUS_SUCCESS; 335 | } 336 | 337 | /* For Emacs: 338 | * Local Variables: 339 | * mode:c 340 | * indent-tabs-mode:t 341 | * tab-width:4 342 | * c-basic-offset:4 343 | * End: 344 | * For VIM: 345 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 346 | */ 347 | --------------------------------------------------------------------------------