status_t SoftAACEncoder2::setAudioParams() { // We call this whenever sample rate, number of channels or bitrate change // in reponse to setParameter calls. ALOGV("setAudioParams: %lu Hz, %lu channels, %lu bps", mSampleRate, mNumChannels, mBitRate); if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT, getAOTFromProfile(mAACProfile))) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mSampleRate)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mBitRate)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE, getChannelMode(mNumChannels))) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } return OK; }
AACFileWriter::AACFileWriter( const char* filename, int samplerate, int channel ) { m_samplerate = samplerate; m_channel = channel; m_aacfile = fopen( filename, "wb" ); if(!m_aacfile) { return; } CHANNEL_MODE channle_mode = channel == 1 ? MODE_1 : MODE_2; AACENC_ERROR ErrorStatus; if(( ErrorStatus = aacEncOpen( &m_hAacEncoder, 0, 2 ) ) != AACENC_OK) { return; } ErrorStatus = aacEncoder_SetParam( m_hAacEncoder, AACENC_AOT, AOT_SBR ); ErrorStatus = aacEncoder_SetParam( m_hAacEncoder, AACENC_BITRATE, 8 * 3500 ); ErrorStatus = aacEncoder_SetParam( m_hAacEncoder, AACENC_SAMPLERATE, samplerate ); ErrorStatus = aacEncoder_SetParam( m_hAacEncoder, AACENC_CHANNELMODE, channle_mode ); ErrorStatus = aacEncEncode( m_hAacEncoder, NULL, NULL, NULL, NULL ); AACENC_InfoStruct encInfo; ErrorStatus = aacEncInfo( m_hAacEncoder, &encInfo ); m_framesize = encInfo.frameLength*channel; m_pInputbuf = new char[m_framesize * 2]; m_outofbyte = new char[encInfo.maxOutBufBytes]; m_in_eisize = 2; m_in_bufsize = m_framesize * sizeof( int16_t ); m_in_buf_id = IN_AUDIO_DATA; m_encinBuf.bufElSizes = &m_in_eisize; m_encinBuf.bufferIdentifiers = &m_in_buf_id; m_encinBuf.bufSizes = &m_in_bufsize; m_encinBuf.numBufs = 1; m_out_eisize = 2; m_out_bufsize = encInfo.maxOutBufBytes; m_out_buf_id = OUT_BITSTREAM_DATA; m_encoutBuf.bufElSizes = &m_out_eisize; m_encoutBuf.bufferIdentifiers = &m_out_buf_id; m_encoutBuf.bufSizes = &m_out_bufsize; m_encoutBuf.numBufs = 1; m_encoutBuf.bufs = (void **)&m_outofbyte; m_in_args.numAncBytes = 0; m_bInit = true; }
int AacEncoderInit(AacEncoderContext *_pAacCtx) { FdkaacConfig *pFdkaac; CHANNEL_MODE mode; pFdkaac = (FdkaacConfig *)_pAacCtx; if (aacEncOpen(&pFdkaac->pEncoder, 1, pFdkaac->channelMode) != AACENC_OK) { pFdkaac->pEncoder = NULL; Debug("err"); return -1; } if((aacEncoder_SetParam(pFdkaac->pEncoder, AACENC_AOT, pFdkaac->objectType)) != AACENC_OK){ Debug("error"); return -1; } int nRet = aacEncoder_SetParam(pFdkaac->pEncoder, AACENC_SAMPLERATE, pFdkaac->nSampleRate); if( nRet != AACENC_OK){ Debug("error, nRet = %d", nRet); return -1; } if(aacEncoder_SetParam(pFdkaac->pEncoder, AACENC_CHANNELMODE, pFdkaac->channelMode) != AACENC_OK) { Debug("error"); return -1; } if(aacEncoder_SetParam(pFdkaac->pEncoder, AACENC_TRANSMUX, pFdkaac->transportType) != AACENC_OK) { Debug("error"); return -1; } // TODO add bitrate control /* if (aacEncoder_SetParam(pFdkaac->pEncoder, AACENC_BITRATE, pFdkaac->nBitrate) != AACENC_OK) { return false; } */ if (aacEncEncode(pFdkaac->pEncoder, NULL, NULL, NULL, NULL) != AACENC_OK) { Debug("error"); return -1; } if (aacEncInfo(pFdkaac->pEncoder, &pFdkaac->info) != AACENC_OK) { Debug("error"); return -1; } // calculate input size pFdkaac->nInputSize = pFdkaac->nChannels * pFdkaac->info.frameLength * pFdkaac->nSampleSize; pFdkaac->pConvertBuf = (uint8_t*)malloc(pFdkaac->nInputSize); return 0; }
status_t SoftAACEncoder2::setAudioParams() { // We call this whenever sample rate, number of channels, bitrate or SBR mode change // in reponse to setParameter calls. ALOGV("setAudioParams: %u Hz, %u channels, %u bps, %i sbr mode, %i sbr ratio", mSampleRate, mNumChannels, mBitRate, mSBRMode, mSBRRatio); if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT, getAOTFromProfile(mAACProfile))) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mSampleRate)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mBitRate)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE, getChannelMode(mNumChannels))) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } if (mSBRMode != -1 && mAACProfile == OMX_AUDIO_AACObjectELD) { if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_MODE, mSBRMode)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } } /* SBR ratio parameter configurations: 0: Default configuration wherein SBR ratio is configured depending on audio object type by the FDK. 1: Downsampled SBR (default for ELD) 2: Dualrate SBR (default for HE-AAC) */ if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_RATIO, mSBRRatio)) { ALOGE("Failed to set AAC encoder parameters"); return UNKNOWN_ERROR; } return OK; }
static av_cold int aac_encode_init(AVCodecContext *avctx) { AACContext *s = avctx->priv_data; int ret = AVERROR(EINVAL); AACENC_InfoStruct info = { 0 }; CHANNEL_MODE mode; AACENC_ERROR err; int aot = FF_PROFILE_AAC_LOW + 1; int sce = 0, cpe = 0; if ((err = aacEncOpen(&s->handle, 0, avctx->channels)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to open the encoder: %s\n", aac_get_error(err)); goto error; } if (avctx->profile != FF_PROFILE_UNKNOWN) aot = avctx->profile + 1; if ((err = aacEncoder_SetParam(s->handle, AACENC_AOT, aot)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set the AOT %d: %s\n", aot, aac_get_error(err)); goto error; } if (aot == FF_PROFILE_AAC_ELD + 1 && s->eld_sbr) { if ((err = aacEncoder_SetParam(s->handle, AACENC_SBR_MODE, 1)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to enable SBR for ELD: %s\n", aac_get_error(err)); goto error; } } if ((err = aacEncoder_SetParam(s->handle, AACENC_SAMPLERATE, avctx->sample_rate)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set the sample rate %d: %s\n", avctx->sample_rate, aac_get_error(err)); goto error; } switch (avctx->channels) { case 1: mode = MODE_1; sce = 1; cpe = 0; break; case 2: mode = MODE_2; sce = 0; cpe = 1; break; case 3: mode = MODE_1_2; sce = 1; cpe = 1; break; case 4: mode = MODE_1_2_1; sce = 2; cpe = 1; break; case 5: mode = MODE_1_2_2; sce = 1; cpe = 2; break; case 6: mode = MODE_1_2_2_1; sce = 2; cpe = 2; break; /* The version macro is introduced the same time as the 7.1 support, so this should suffice. */ #ifdef AACENCODER_LIB_VL0 case 8: sce = 2; cpe = 3; if (avctx->channel_layout == AV_CH_LAYOUT_7POINT1) { mode = MODE_7_1_REAR_SURROUND; } else { // MODE_1_2_2_2_1 and MODE_7_1_FRONT_CENTER use the same channel layout mode = MODE_7_1_FRONT_CENTER; } break; #endif default: av_log(avctx, AV_LOG_ERROR, "Unsupported number of channels %d\n", avctx->channels); goto error; } if ((err = aacEncoder_SetParam(s->handle, AACENC_CHANNELMODE, mode)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set channel mode %d: %s\n", mode, aac_get_error(err)); goto error; } if ((err = aacEncoder_SetParam(s->handle, AACENC_CHANNELORDER, 1)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set wav channel order %d: %s\n", mode, aac_get_error(err)); goto error; } if (avctx->flags & CODEC_FLAG_QSCALE || s->vbr) { int mode = s->vbr ? s->vbr : avctx->global_quality; if (mode < 1 || mode > 5) { av_log(avctx, AV_LOG_WARNING, "VBR quality %d out of range, should be 1-5\n", mode); mode = av_clip(mode, 1, 5); } av_log(avctx, AV_LOG_WARNING, "Note, the VBR setting is unsupported and only works with " "some parameter combinations\n"); if ((err = aacEncoder_SetParam(s->handle, AACENC_BITRATEMODE, mode)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set the VBR bitrate mode %d: %s\n", mode, aac_get_error(err)); goto error; } } else { if (avctx->bit_rate <= 0) { if (avctx->profile == FF_PROFILE_AAC_HE_V2) { sce = 1; cpe = 0; } avctx->bit_rate = (96*sce + 128*cpe) * avctx->sample_rate / 44; if (avctx->profile == FF_PROFILE_AAC_HE || avctx->profile == FF_PROFILE_AAC_HE_V2 || avctx->profile == FF_PROFILE_MPEG2_AAC_HE || s->eld_sbr) avctx->bit_rate /= 2; } if ((err = aacEncoder_SetParam(s->handle, AACENC_BITRATE, avctx->bit_rate)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set the bitrate %d: %s\n", avctx->bit_rate, aac_get_error(err)); goto error; } } /* Choose bitstream format - if global header is requested, use * raw access units, otherwise use ADTS. */ if ((err = aacEncoder_SetParam(s->handle, AACENC_TRANSMUX, avctx->flags & CODEC_FLAG_GLOBAL_HEADER ? 0 : s->latm ? 10 : 2)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set the transmux format: %s\n", aac_get_error(err)); goto error; } if (s->latm && s->header_period) { if ((err = aacEncoder_SetParam(s->handle, AACENC_HEADER_PERIOD, s->header_period)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set header period: %s\n", aac_get_error(err)); goto error; } } /* If no signaling mode is chosen, use explicit hierarchical signaling * if using mp4 mode (raw access units, with global header) and * implicit signaling if using ADTS. */ if (s->signaling < 0) s->signaling = avctx->flags & CODEC_FLAG_GLOBAL_HEADER ? 2 : 0; if ((err = aacEncoder_SetParam(s->handle, AACENC_SIGNALING_MODE, s->signaling)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set signaling mode %d: %s\n", s->signaling, aac_get_error(err)); goto error; } if ((err = aacEncoder_SetParam(s->handle, AACENC_AFTERBURNER, s->afterburner)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set afterburner to %d: %s\n", s->afterburner, aac_get_error(err)); goto error; } if (avctx->cutoff > 0) { if (avctx->cutoff < (avctx->sample_rate + 255) >> 8 || avctx->cutoff > 20000) { av_log(avctx, AV_LOG_ERROR, "cutoff valid range is %d-20000\n", (avctx->sample_rate + 255) >> 8); goto error; } if ((err = aacEncoder_SetParam(s->handle, AACENC_BANDWIDTH, avctx->cutoff)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set the encoder bandwidth to %d: %s\n", avctx->cutoff, aac_get_error(err)); goto error; } }
int main(int argc, char *argv[]) { int bitrate = 64000; int ch; const char *infile, *outfile; FILE *out; void *wav; int format, sample_rate, channels, bits_per_sample; int input_size; uint8_t* input_buf; int16_t* convert_buf; int aot = 2; int afterburner = 1; int eld_sbr = 0; int vbr = 0; HANDLE_AACENCODER handle; CHANNEL_MODE mode; AACENC_InfoStruct info = { 0 }; while ((ch = getopt(argc, argv, "r:t:a:s:v:")) != -1) { switch (ch) { case 'r': bitrate = atoi(optarg); break; case 't': aot = atoi(optarg); break; case 'a': afterburner = atoi(optarg); break; case 's': eld_sbr = atoi(optarg); break; case 'v': vbr = atoi(optarg); break; case '?': default: usage(argv[0]); return 1; } } if (argc - optind < 2) { usage(argv[0]); return 1; } infile = argv[optind]; outfile = argv[optind + 1]; wav = wav_read_open(infile); if (!wav) { fprintf(stderr, "Unable to open wav file %s\n", infile); return 1; } if (!wav_get_header(wav, &format, &channels, &sample_rate, &bits_per_sample, NULL)) { fprintf(stderr, "Bad wav file %s\n", infile); return 1; } if (format != 1) { fprintf(stderr, "Unsupported WAV format %d\n", format); return 1; } if (bits_per_sample != 16) { fprintf(stderr, "Unsupported WAV sample depth %d\n", bits_per_sample); return 1; } switch (channels) { case 1: mode = MODE_1; break; case 2: mode = MODE_2; break; case 3: mode = MODE_1_2; break; case 4: mode = MODE_1_2_1; break; case 5: mode = MODE_1_2_2; break; case 6: mode = MODE_1_2_2_1; break; default: fprintf(stderr, "Unsupported WAV channels %d\n", channels); return 1; } if (aacEncOpen(&handle, 0, channels) != AACENC_OK) { fprintf(stderr, "Unable to open encoder\n"); return 1; } if (aacEncoder_SetParam(handle, AACENC_AOT, aot) != AACENC_OK) { fprintf(stderr, "Unable to set the AOT\n"); return 1; } if (aot == 39 && eld_sbr) { if (aacEncoder_SetParam(handle, AACENC_SBR_MODE, 1) != AACENC_OK) { fprintf(stderr, "Unable to set SBR mode for ELD\n"); return 1; } } if (aacEncoder_SetParam(handle, AACENC_SAMPLERATE, sample_rate) != AACENC_OK) { fprintf(stderr, "Unable to set the AOT\n"); return 1; } if (aacEncoder_SetParam(handle, AACENC_CHANNELMODE, mode) != AACENC_OK) { fprintf(stderr, "Unable to set the channel mode\n"); return 1; } if (aacEncoder_SetParam(handle, AACENC_CHANNELORDER, 1) != AACENC_OK) { fprintf(stderr, "Unable to set the wav channel order\n"); return 1; } if (vbr) { if (aacEncoder_SetParam(handle, AACENC_BITRATEMODE, vbr) != AACENC_OK) { fprintf(stderr, "Unable to set the VBR bitrate mode\n"); return 1; } } else { if (aacEncoder_SetParam(handle, AACENC_BITRATE, bitrate) != AACENC_OK) { fprintf(stderr, "Unable to set the bitrate\n"); return 1; } } if (aacEncoder_SetParam(handle, AACENC_TRANSMUX, 2) != AACENC_OK) { fprintf(stderr, "Unable to set the ADTS transmux\n"); return 1; } if (aacEncoder_SetParam(handle, AACENC_AFTERBURNER, afterburner) != AACENC_OK) { fprintf(stderr, "Unable to set the afterburner mode\n"); return 1; } if (aacEncEncode(handle, NULL, NULL, NULL, NULL) != AACENC_OK) { fprintf(stderr, "Unable to initialize the encoder\n"); return 1; } if (aacEncInfo(handle, &info) != AACENC_OK) { fprintf(stderr, "Unable to get the encoder info\n"); return 1; } out = fopen(outfile, "wb"); if (!out) { perror(outfile); return 1; } input_size = channels*2*info.frameLength; input_buf = (uint8_t*) malloc(input_size); convert_buf = (int16_t*) malloc(input_size); while (1) { AACENC_BufDesc in_buf = { 0 }, out_buf = { 0 }; AACENC_InArgs in_args = { 0 }; AACENC_OutArgs out_args = { 0 }; int in_identifier = IN_AUDIO_DATA; int in_size, in_elem_size; int out_identifier = OUT_BITSTREAM_DATA; int out_size, out_elem_size; int read, i; void *in_ptr, *out_ptr; uint8_t outbuf[20480]; AACENC_ERROR err; read = wav_read_data(wav, input_buf, input_size); for (i = 0; i < read/2; i++) { const uint8_t* in = &input_buf[2*i]; convert_buf[i] = in[0] | (in[1] << 8); } if (read <= 0) { in_args.numInSamples = -1; } else { in_ptr = convert_buf; in_size = read; in_elem_size = 2; in_args.numInSamples = read/2; in_buf.numBufs = 1; in_buf.bufs = &in_ptr; in_buf.bufferIdentifiers = &in_identifier; in_buf.bufSizes = &in_size; in_buf.bufElSizes = &in_elem_size; } out_ptr = outbuf; out_size = sizeof(outbuf); out_elem_size = 1; out_buf.numBufs = 1; out_buf.bufs = &out_ptr; out_buf.bufferIdentifiers = &out_identifier; out_buf.bufSizes = &out_size; out_buf.bufElSizes = &out_elem_size; if ((err = aacEncEncode(handle, &in_buf, &out_buf, &in_args, &out_args)) != AACENC_OK) { if (err == AACENC_ENCODE_EOF) break; fprintf(stderr, "Encoding failed\n"); return 1; } if (out_args.numOutBytes == 0) continue; fwrite(outbuf, 1, out_args.numOutBytes, out); } free(input_buf); free(convert_buf); fclose(out); wav_read_close(wav); aacEncClose(&handle); return 0; }
void *io_thread_a2dp_source_aac(void *arg) { struct ba_transport *t = (struct ba_transport *)arg; const a2dp_aac_t *cconfig = (a2dp_aac_t *)t->a2dp.cconfig; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_cleanup_push(CANCEL_ROUTINE(io_thread_release), t); HANDLE_AACENCODER handle; AACENC_InfoStruct aacinf; AACENC_ERROR err; /* create AAC encoder without the Meta Data module */ const unsigned int channels = transport_get_channels(t); if ((err = aacEncOpen(&handle, 0x07, channels)) != AACENC_OK) { error("Couldn't open AAC encoder: %s", aacenc_strerror(err)); goto fail_open; } pthread_cleanup_push(CANCEL_ROUTINE(aacEncClose), &handle); unsigned int aot = AOT_NONE; unsigned int bitrate = AAC_GET_BITRATE(*cconfig); unsigned int samplerate = transport_get_sampling(t); unsigned int channelmode = channels == 1 ? MODE_1 : MODE_2; switch (cconfig->object_type) { case AAC_OBJECT_TYPE_MPEG2_AAC_LC: #if AACENCODER_LIB_VERSION <= 0x03040C00 /* 3.4.12 */ aot = AOT_MP2_AAC_LC; break; #endif case AAC_OBJECT_TYPE_MPEG4_AAC_LC: aot = AOT_AAC_LC; break; case AAC_OBJECT_TYPE_MPEG4_AAC_LTP: aot = AOT_AAC_LTP; break; case AAC_OBJECT_TYPE_MPEG4_AAC_SCA: aot = AOT_AAC_SCAL; break; } if ((err = aacEncoder_SetParam(handle, AACENC_AOT, aot)) != AACENC_OK) { error("Couldn't set audio object type: %s", aacenc_strerror(err)); goto fail_init; } if ((err = aacEncoder_SetParam(handle, AACENC_BITRATE, bitrate)) != AACENC_OK) { error("Couldn't set bitrate: %s", aacenc_strerror(err)); goto fail_init; } if ((err = aacEncoder_SetParam(handle, AACENC_SAMPLERATE, samplerate)) != AACENC_OK) { error("Couldn't set sampling rate: %s", aacenc_strerror(err)); goto fail_init; } if ((err = aacEncoder_SetParam(handle, AACENC_CHANNELMODE, channelmode)) != AACENC_OK) { error("Couldn't set channel mode: %s", aacenc_strerror(err)); goto fail_init; } if (cconfig->vbr) { if ((err = aacEncoder_SetParam(handle, AACENC_BITRATEMODE, config.aac_vbr_mode)) != AACENC_OK) { error("Couldn't set VBR bitrate mode %u: %s", config.aac_vbr_mode, aacenc_strerror(err)); goto fail_init; } } if ((err = aacEncoder_SetParam(handle, AACENC_AFTERBURNER, config.aac_afterburner)) != AACENC_OK) { error("Couldn't enable afterburner: %s", aacenc_strerror(err)); goto fail_init; } if ((err = aacEncoder_SetParam(handle, AACENC_TRANSMUX, TT_MP4_LATM_MCP1)) != AACENC_OK) { error("Couldn't enable LATM transport type: %s", aacenc_strerror(err)); goto fail_init; } if ((err = aacEncoder_SetParam(handle, AACENC_HEADER_PERIOD, 1)) != AACENC_OK) { error("Couldn't set LATM header period: %s", aacenc_strerror(err)); goto fail_init; } if ((err = aacEncEncode(handle, NULL, NULL, NULL, NULL)) != AACENC_OK) { error("Couldn't initialize AAC encoder: %s", aacenc_strerror(err)); goto fail_init; } if ((err = aacEncInfo(handle, &aacinf)) != AACENC_OK) { error("Couldn't get encoder info: %s", aacenc_strerror(err)); goto fail_init; } int in_buffer_identifier = IN_AUDIO_DATA; int out_buffer_identifier = OUT_BITSTREAM_DATA; int in_buffer_element_size = sizeof(int16_t); int out_buffer_element_size = 1; int16_t *in_buffer, *in_buffer_head; uint8_t *out_buffer, *out_payload; int in_buffer_size; int out_payload_size; AACENC_BufDesc in_buf = { .numBufs = 1, .bufs = (void **)&in_buffer_head, .bufferIdentifiers = &in_buffer_identifier, .bufSizes = &in_buffer_size, .bufElSizes = &in_buffer_element_size, }; AACENC_BufDesc out_buf = { .numBufs = 1, .bufs = (void **)&out_payload, .bufferIdentifiers = &out_buffer_identifier, .bufSizes = &out_payload_size, .bufElSizes = &out_buffer_element_size, }; AACENC_InArgs in_args = { 0 }; AACENC_OutArgs out_args = { 0 }; in_buffer_size = in_buffer_element_size * aacinf.inputChannels * aacinf.frameLength; out_payload_size = aacinf.maxOutBufBytes; in_buffer = malloc(in_buffer_size); out_buffer = malloc(sizeof(rtp_header_t) + out_payload_size); pthread_cleanup_push(CANCEL_ROUTINE(free), in_buffer); pthread_cleanup_push(CANCEL_ROUTINE(free), out_buffer); if (in_buffer == NULL || out_buffer == NULL) { error("Couldn't create data buffers: %s", strerror(ENOMEM)); goto fail; } uint16_t seq_number = random(); uint32_t timestamp = random(); /* initialize RTP header (the constant part) */ rtp_header_t *rtp_header = (rtp_header_t *)out_buffer; memset(rtp_header, 0, sizeof(*rtp_header)); rtp_header->version = 2; rtp_header->paytype = 96; /* anchor for RTP payload - audioMuxElement (RFC 3016) */ out_payload = (uint8_t *)&rtp_header->csrc[rtp_header->cc]; /* helper variable used during payload fragmentation */ const size_t rtp_header_len = out_payload - out_buffer; /* initial input buffer head position and the available size */ size_t in_samples = in_buffer_size / in_buffer_element_size; in_buffer_head = in_buffer; struct pollfd pfds[] = { { t->event_fd, POLLIN, 0 }, { -1, POLLIN, 0 }, }; struct io_sync io_sync = { .sampling = samplerate, }; debug("Starting IO loop: %s", bluetooth_profile_to_string(t->profile, t->codec)); for (;;) { pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ssize_t samples; /* add PCM socket to the poll if transport is active */ pfds[1].fd = t->state == TRANSPORT_ACTIVE ? t->a2dp.pcm.fd : -1; if (poll(pfds, sizeof(pfds) / sizeof(*pfds), -1) == -1) { error("Transport poll error: %s", strerror(errno)); goto fail; } if (pfds[0].revents & POLLIN) { /* dispatch incoming event */ eventfd_t event; eventfd_read(pfds[0].fd, &event); io_sync.frames = 0; continue; } /* read data from the FIFO - this function will block */ if ((samples = io_thread_read_pcm(&t->a2dp.pcm, in_buffer_head, in_samples)) <= 0) { if (samples == -1) error("FIFO read error: %s", strerror(errno)); goto fail; } pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); if (io_sync.frames == 0) { gettimestamp(&io_sync.ts); io_sync.ts0 = io_sync.ts; } if (!config.a2dp_volume || !t->a2dp.supports_dbus_volume) /* scale volume or mute audio signal */ io_thread_scale_pcm(t, in_buffer_head, samples, channels); /* overall input buffer size */ samples += in_buffer_head - in_buffer; /* in the encoding loop head is used for reading */ in_buffer_head = in_buffer; while ((in_args.numInSamples = samples) != 0) { if ((err = aacEncEncode(handle, &in_buf, &out_buf, &in_args, &out_args)) != AACENC_OK) error("AAC encoding error: %s", aacenc_strerror(err)); if (out_args.numOutBytes > 0) { size_t payload_len_max = t->mtu_write - rtp_header_len; size_t payload_len = out_args.numOutBytes; rtp_header->timestamp = htonl(timestamp); /* If the size of the RTP packet exceeds writing MTU, the RTP payload * should be fragmented. According to the RFC 3016, fragmentation of * the audioMuxElement requires no extra header - the payload should * be fragmented and spread across multiple RTP packets. * * TODO: Confirm that the fragmentation logic is correct. * * This code has been tested with Jabra Move headset, however the * outcome of this test is not positive. Fragmented packets are not * recognized by the device. */ for (;;) { ssize_t ret; size_t len; len = payload_len > payload_len_max ? payload_len_max : payload_len; rtp_header->markbit = len < payload_len_max; rtp_header->seq_number = htons(++seq_number); if ((ret = write(t->bt_fd, out_buffer, rtp_header_len + len)) == -1) { if (errno == ECONNRESET || errno == ENOTCONN) { /* exit the thread upon BT socket disconnection */ debug("BT socket disconnected"); goto fail; } error("BT socket write error: %s", strerror(errno)); break; } /* break if the last part of the payload has been written */ if ((payload_len -= ret - rtp_header_len) == 0) break; /* move rest of data to the beginning of the payload */ debug("Payload fragmentation: extra %zd bytes", payload_len); memmove(out_payload, out_payload + ret, payload_len); } } /* progress the head position by the number of samples consumed by the * encoder, also adjust the number of samples in the input buffer */ in_buffer_head += out_args.numInSamples; samples -= out_args.numInSamples; /* keep data transfer at a constant bit rate, also * get a timestamp for the next RTP frame */ timestamp += io_thread_time_sync(&io_sync, out_args.numInSamples / channels); t->delay = io_sync.delay; } /* move leftovers to the beginning */ if (samples > 0 && in_buffer != in_buffer_head) memmove(in_buffer, in_buffer_head, samples * in_buffer_element_size); /* reposition input buffer head */ in_buffer_head = in_buffer + samples; in_samples = in_buffer_size / in_buffer_element_size - samples; } fail: pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_cleanup_pop(1); pthread_cleanup_pop(1); fail_init: pthread_cleanup_pop(1); fail_open: pthread_cleanup_pop(1); return NULL; } #endif void *io_thread_rfcomm(void *arg) { struct ba_transport *t = (struct ba_transport *)arg; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_cleanup_push(CANCEL_ROUTINE(io_thread_release), t); uint8_t mic_gain = t->rfcomm.sco->sco.mic_gain; uint8_t spk_gain = t->rfcomm.sco->sco.spk_gain; char buffer[64]; struct at_command at; int i; struct pollfd pfds[] = { { t->event_fd, POLLIN, 0 }, { t->bt_fd, POLLIN, 0 }, }; /* HSP only supports CVSD */ if (t->profile == BLUETOOTH_PROFILE_HSP_HS || t->profile == BLUETOOTH_PROFILE_HSP_AG) t->rfcomm.sco->sco.codec = TRANSPORT_SCO_CODEC_CVSD; debug("Starting RFCOMM loop: %s", bluetooth_profile_to_string(t->profile, t->codec)); for (;;) { const char *response = "OK"; ssize_t ret; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if (poll(pfds, sizeof(pfds) / sizeof(*pfds), -1) == -1) { error("Transport poll error: %s", strerror(errno)); goto fail; } pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); if (pfds[0].revents & POLLIN) { /* dispatch incoming event */ eventfd_t event; eventfd_read(pfds[0].fd, &event); if (mic_gain != t->rfcomm.sco->sco.mic_gain) { mic_gain = t->rfcomm.sco->sco.mic_gain; debug("Setting microphone gain: %d", mic_gain); sprintf(buffer, "+VGM=%d", mic_gain); io_thread_write_at_response(pfds[1].fd, buffer); } if (spk_gain != t->rfcomm.sco->sco.spk_gain) { spk_gain = t->rfcomm.sco->sco.spk_gain; debug("Setting speaker gain: %d", spk_gain); sprintf(buffer, "+VGS=%d", mic_gain); io_thread_write_at_response(pfds[1].fd, buffer); } continue; } if ((ret = read(pfds[1].fd, buffer, sizeof(buffer))) == -1) { switch (errno) { case ECONNABORTED: case ECONNRESET: case ENOTCONN: case ETIMEDOUT: /* exit the thread upon socket disconnection */ debug("RFCOMM disconnected: %s", strerror(errno)); transport_set_state(t, TRANSPORT_ABORTED); goto fail; default: error("RFCOMM read error: %s", strerror(errno)); continue; } } /* Parse AT command received from the headset. */ if (at_parse(buffer, &at)) { warn("Invalid AT command: %s", buffer); continue; } debug("AT command: %s=%s", at.command, at.value); if (strcmp(at.command, "RING") == 0) { } else if (strcmp(at.command, "+CKPD") == 0 && atoi(at.value) == 200) { } else if (strcmp(at.command, "+VGM") == 0) t->rfcomm.sco->sco.mic_gain = mic_gain = atoi(at.value); else if (strcmp(at.command, "+VGS") == 0) t->rfcomm.sco->sco.spk_gain = spk_gain = atoi(at.value); else if (strcmp(at.command, "+IPHONEACCEV") == 0) { char *ptr = at.value; size_t count = atoi(strsep(&ptr, ",")); char tmp; while (count-- && ptr != NULL) switch (tmp = *strsep(&ptr, ",")) { case '1': if (ptr != NULL) t->device->xapl.accev_battery = atoi(strsep(&ptr, ",")); break; case '2': if (ptr != NULL) t->device->xapl.accev_docked = atoi(strsep(&ptr, ",")); break; default: warn("Unsupported IPHONEACCEV key: %c", tmp); strsep(&ptr, ","); } } else if (strcmp(at.command, "+XAPL") == 0) { unsigned int vendor, product; unsigned int version, features; if (sscanf(at.value, "%x-%x-%u,%u", &vendor, &product, &version, &features) == 4) { t->device->xapl.vendor_id = vendor; t->device->xapl.product_id = product; t->device->xapl.version = version; t->device->xapl.features = features; response = "+XAPL=BlueALSA,0"; } else { warn("Invalid XAPL value: %s", at.value); response = "ERROR"; } } else if (strcmp(at.command, "+BRSF") == 0) { uint32_t hf_features = strtoul(at.value, NULL, 10); debug("Got HFP HF features: 0x%x", hf_features); uint32_t ag_features = HFP_AG_FEATURES; #if defined(ENABLE_MSBC) if (config.enable_msbc) { if (hf_features & HFP_HF_FEAT_CODEC) { ag_features |= HFP_AG_FEAT_CODEC; } } #endif if ((ag_features & HFP_AG_FEAT_CODEC) == 0) { /* Codec negotiation is not supported, hence no wideband audio support. AT+BAC is not sent */ t->rfcomm.sco->sco.codec = TRANSPORT_SCO_CODEC_CVSD; } t->rfcomm.sco->sco.hf_features = hf_features; snprintf(buffer, sizeof(buffer), "+BRSF: %u", ag_features); io_thread_write_at_response(pfds[1].fd, buffer); } else if (strcmp(at.command, "+BAC") == 0 && at.type == AT_CMD_TYPE_SET) { debug("Supported codecs: %s", at.value); /* In case some headsets send BAC even if we don't * advertise support for it, just OK and ignore */ #if defined(ENABLE_MSBC) /* Split codecs string */ gchar **codecs = g_strsplit(at.value, ",", 0); for (i = 0; codecs[i]; i++) { gchar *codec = codecs[i]; uint32_t codec_value = strtoul(codec, NULL, 10); if (codec_value == HFP_CODEC_MSBC) { t->rfcomm.sco->sco.codec = TRANSPORT_SCO_CODEC_MSBC; } } g_strfreev(codecs); #endif /* Default to CVSD if no other was found */ if (t->rfcomm.sco->sco.codec == TRANSPORT_SCO_CODEC_UNKNOWN) t->rfcomm.sco->sco.codec = TRANSPORT_SCO_CODEC_CVSD; } else if (strcmp(at.command, "+CIND") == 0) { if ( at.type == AT_CMD_TYPE_GET) { io_thread_write_at_response(pfds[1].fd, "+CIND: 0,0,1,4,0,4,0"); } else if(at.type == AT_CMD_TYPE_TEST) { io_thread_write_at_response(pfds[1].fd, "+CIND: " "(\"call\",(0,1))" ",(\"callsetup\",(0-3))" ",(\"service\",(0-1))" ",(\"signal\",(0-5))" ",(\"roam\",(0,1))" ",(\"battchg\",(0-5))" ",(\"callheld\",(0-2))" ); } } else if (strcmp(at.command, "+CMER") == 0 && at.type == AT_CMD_TYPE_SET) { /* +CMER is the last step of the "Service Level Connection establishment" procedure */ /* Send OK */ io_thread_write_at_response(pfds[1].fd, response); /* Send codec select if anything besides CVSD was found */ if (t->rfcomm.sco->sco.codec > TRANSPORT_SCO_CODEC_CVSD) { snprintf(buffer, sizeof(buffer), "+BCS: %u", t->rfcomm.sco->sco.codec); io_thread_write_at_response(pfds[1].fd, buffer); } continue; } else if (strcmp(at.command, "+BCS") == 0 && at.type == AT_CMD_TYPE_SET) { debug("Got codec selected: %d", atoi(at.value)); } else if (strcmp(at.command, "+BTRH") == 0 && at.type == AT_CMD_TYPE_GET) { } else if (strcmp(at.command, "+NREC") == 0 && at.type == AT_CMD_TYPE_SET) { } else if (strcmp(at.command, "+CCWA") == 0 && at.type == AT_CMD_TYPE_SET) { } else if (strcmp(at.command, "+BIA") == 0 && at.type == AT_CMD_TYPE_SET) { } else if (strcmp(at.command, "+CHLD") == 0 && at.type == AT_CMD_TYPE_TEST) { io_thread_write_at_response(pfds[1].fd, "+CHLD: (0,1,2,3)"); } else { warn("Unsupported AT command: %s=%s", at.command, at.value); response = "ERROR"; } io_thread_write_at_response(pfds[1].fd, response); } fail: pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_cleanup_pop(1); return NULL; } void *io_thread_sco(void *arg) { struct ba_transport *t = (struct ba_transport *)arg; /* this buffer has to be bigger than SCO MTU */ const size_t buffer_size = 512; struct sbc_state *sbc = NULL; int16_t *buffer = malloc(buffer_size); pthread_cleanup_push(CANCEL_ROUTINE(free), buffer); pthread_cleanup_push(CANCEL_ROUTINE(free), sbc); if (buffer == NULL) { error("Couldn't create data buffers: %s", strerror(ENOMEM)); goto fail; } struct pollfd pfds[] = { { t->event_fd, POLLIN, 0 }, { -1, POLLIN, 0 }, //bt { -1, 0, 0 }, // shm pcm mic follower by shm pcm spk { -1, 0, 0 }, { -1, 0, 0 }, { -1, 0, 0 }, }; struct io_sync io_sync = { .frames = 0, }; debug("Starting IO loop: %s", bluetooth_profile_to_string(t->profile, t->codec)); for (;;) { if (t->sco.codec == TRANSPORT_SCO_CODEC_MSBC) { pfds[1].fd = t->bt_fd; } else { pfds[1].fd = t->sco.mic_pcm.shm != NULL ? t->bt_fd : -1; } int shm_mic_nr_pfds = libshm_nr_pollfd(t->sco.mic_pcm.shm); int shm_spk_nr_pfds = libshm_nr_pollfd(t->sco.spk_pcm.shm); libshm_populate_pollfd(t->sco.mic_pcm.shm, &pfds[2]); libshm_populate_pollfd(t->sco.spk_pcm.shm, &pfds[2 + shm_mic_nr_pfds]); if (poll(pfds, 2 + shm_mic_nr_pfds + shm_spk_nr_pfds, -1) == -1) { error("Transport poll error: %s", strerror(errno)); goto fail; } if (pfds[0].revents & POLLIN) { /* dispatch incoming event */ eventfd_t event; eventfd_read(pfds[0].fd, &event); /* It is required to release SCO if we are not transferring audio, * because it will free Bluetooth bandwidth - microphone signal is * transfered even though we are not reading from it! */ if (t->sco.spk_pcm.shm == NULL && t->sco.mic_pcm.shm == NULL) { transport_release_bt_sco(t); io_sync.frames = 0; } else { debug("Trying to acquire"); transport_acquire_bt_sco(t); #if defined(ENABLE_MSBC) /* This can be called again, make sure it is "reentrant" */ if (t->sco.codec == TRANSPORT_SCO_CODEC_MSBC) { sbc = iothread_initialize_msbc(sbc); if (!sbc) goto fail; } #endif } io_sync.sampling = transport_get_sampling(t); continue; } if (io_sync.frames == 0) { gettimestamp(&io_sync.ts); io_sync.ts0 = io_sync.ts; } int poll_mic = libshm_poll(t->sco.mic_pcm.shm, &pfds[2], shm_mic_nr_pfds); int poll_spk = libshm_poll(t->sco.spk_pcm.shm, &pfds[2 + shm_mic_nr_pfds], shm_spk_nr_pfds); if (pfds[1].revents & POLLIN) { // bluetooth socket incoming #if defined(ENABLE_MSBC) if (t->sco.codec == TRANSPORT_SCO_CODEC_MSBC) { iothread_handle_incoming_msbc(t, sbc); } else #endif { ssize_t len; if ((len = read(pfds[1].fd, buffer, buffer_size)) == -1) { debug("SCO read error: %s", strerror(errno)); continue; } libshm_write_all(t->sco.mic_pcm.shm, buffer, len); } } if (poll_spk & POLLIN) { #if defined(ENABLE_MSBC) if (t->sco.codec == TRANSPORT_SCO_CODEC_MSBC) { iothread_handle_outgoing_msbc(t, sbc); } else #endif { ssize_t samples = t->mtu_write / sizeof(int16_t); /* read data from the FIFO - this function will block */ if ((samples = io_thread_read_pcm(&t->sco.spk_pcm, buffer, samples)) <= 0) { if (samples == -1) error("FIFO read error: %s", strerror(errno)); continue; } write(t->bt_fd, buffer, samples * sizeof(int16_t)); } } /* mSBC output is synchronized to input, no need for this */ if (t->sco.codec != TRANSPORT_SCO_CODEC_MSBC) { /* keep data transfer at a constant bit rate */ io_thread_time_sync(&io_sync, 48 / 2); t->delay = io_sync.delay; } } fail: pthread_cleanup_pop(1); pthread_cleanup_pop(1); return NULL; }
static gboolean gst_fdkaacenc_set_format (GstAudioEncoder * enc, GstAudioInfo * info) { GstFdkAacEnc *self = GST_FDKAACENC (enc); gboolean ret = FALSE; GstCaps *allowed_caps; GstCaps *src_caps; AACENC_ERROR err; gint transmux = 0, aot = AOT_AAC_LC; gint mpegversion = 4; CHANNEL_MODE channel_mode; AACENC_InfoStruct enc_info = { 0 }; gint bitrate; if (self->enc) { /* drain */ gst_fdkaacenc_handle_frame (enc, NULL); aacEncClose (&self->enc); } allowed_caps = gst_pad_get_allowed_caps (GST_AUDIO_ENCODER_SRC_PAD (self)); GST_DEBUG_OBJECT (self, "allowed caps: %" GST_PTR_FORMAT, allowed_caps); if (allowed_caps && gst_caps_get_size (allowed_caps) > 0) { GstStructure *s = gst_caps_get_structure (allowed_caps, 0); const gchar *str = NULL; if ((str = gst_structure_get_string (s, "stream-format"))) { if (strcmp (str, "adts") == 0) { GST_DEBUG_OBJECT (self, "use ADTS format for output"); transmux = 2; } else if (strcmp (str, "adif") == 0) { GST_DEBUG_OBJECT (self, "use ADIF format for output"); transmux = 1; } else if (strcmp (str, "raw") == 0) { GST_DEBUG_OBJECT (self, "use RAW format for output"); transmux = 0; } } gst_structure_get_int (s, "mpegversion", &mpegversion); } if (allowed_caps) gst_caps_unref (allowed_caps); err = aacEncOpen (&self->enc, 0, GST_AUDIO_INFO_CHANNELS (info)); if (err != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to open encoder: %d\n", err); return FALSE; } aot = AOT_AAC_LC; if ((err = aacEncoder_SetParam (self->enc, AACENC_AOT, aot)) != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to set AOT %d: %d\n", aot, err); return FALSE; } if ((err = aacEncoder_SetParam (self->enc, AACENC_SAMPLERATE, GST_AUDIO_INFO_RATE (info))) != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to set sample rate %d: %d\n", GST_AUDIO_INFO_RATE (info), err); return FALSE; } if (GST_AUDIO_INFO_CHANNELS (info) == 1) { channel_mode = MODE_1; self->need_reorder = FALSE; self->aac_positions = NULL; } else { guint64 in_channel_mask, out_channel_mask; gint i; for (i = 0; i < G_N_ELEMENTS (channel_layouts); i++) { if (channel_layouts[i].channels != GST_AUDIO_INFO_CHANNELS (info)) continue; gst_audio_channel_positions_to_mask (&GST_AUDIO_INFO_POSITION (info, 0), GST_AUDIO_INFO_CHANNELS (info), FALSE, &in_channel_mask); gst_audio_channel_positions_to_mask (channel_layouts[i].positions, channel_layouts[i].channels, FALSE, &out_channel_mask); if (in_channel_mask == out_channel_mask) { channel_mode = channel_layouts[i].mode; self->need_reorder = memcmp (channel_layouts[i].positions, &GST_AUDIO_INFO_POSITION (info, 0), GST_AUDIO_INFO_CHANNELS (info) * sizeof (GstAudioChannelPosition)) != 0; self->aac_positions = channel_layouts[i].positions; break; } } if (i == G_N_ELEMENTS (channel_layouts)) { GST_ERROR_OBJECT (self, "Couldn't find a valid channel layout"); return FALSE; } } if ((err = aacEncoder_SetParam (self->enc, AACENC_CHANNELMODE, channel_mode)) != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to set channel mode %d: %d", channel_mode, err); return FALSE; } /* MPEG channel order */ if ((err = aacEncoder_SetParam (self->enc, AACENC_CHANNELORDER, 0)) != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to set channel order %d: %d", channel_mode, err); return FALSE; } bitrate = self->bitrate; /* See * http://wiki.hydrogenaud.io/index.php?title=Fraunhofer_FDK_AAC#Recommended_Sampling_Rate_and_Bitrate_Combinations */ if (bitrate == 0) { if (GST_AUDIO_INFO_CHANNELS (info) == 1) { if (GST_AUDIO_INFO_RATE (info) < 16000) { bitrate = 8000; } else if (GST_AUDIO_INFO_RATE (info) == 16000) { bitrate = 16000; } else if (GST_AUDIO_INFO_RATE (info) < 32000) { bitrate = 24000; } else if (GST_AUDIO_INFO_RATE (info) == 32000) { bitrate = 32000; } else if (GST_AUDIO_INFO_RATE (info) <= 44100) { bitrate = 56000; } else { bitrate = 160000; } } else if (GST_AUDIO_INFO_CHANNELS (info) == 2) { if (GST_AUDIO_INFO_RATE (info) < 16000) { bitrate = 16000; } else if (GST_AUDIO_INFO_RATE (info) == 16000) { bitrate = 24000; } else if (GST_AUDIO_INFO_RATE (info) < 22050) { bitrate = 32000; } else if (GST_AUDIO_INFO_RATE (info) < 32000) { bitrate = 40000; } else if (GST_AUDIO_INFO_RATE (info) == 32000) { bitrate = 96000; } else if (GST_AUDIO_INFO_RATE (info) <= 44100) { bitrate = 112000; } else { bitrate = 320000; } } else { /* 5, 5.1 */ if (GST_AUDIO_INFO_RATE (info) < 32000) { bitrate = 160000; } else if (GST_AUDIO_INFO_RATE (info) <= 44100) { bitrate = 240000; } else { bitrate = 320000; } } } if ((err = aacEncoder_SetParam (self->enc, AACENC_TRANSMUX, transmux)) != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to set transmux %d: %d", transmux, err); return FALSE; } if ((err = aacEncoder_SetParam (self->enc, AACENC_BITRATE, bitrate)) != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to set bitrate %d: %d", bitrate, err); return FALSE; } if ((err = aacEncEncode (self->enc, NULL, NULL, NULL, NULL)) != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to initialize encoder: %d", err); return FALSE; } if ((err = aacEncInfo (self->enc, &enc_info)) != AACENC_OK) { GST_ERROR_OBJECT (self, "Unable to get encoder info: %d", err); return FALSE; } gst_audio_encoder_set_frame_max (enc, 1); gst_audio_encoder_set_frame_samples_min (enc, enc_info.frameLength); gst_audio_encoder_set_frame_samples_max (enc, enc_info.frameLength); gst_audio_encoder_set_hard_min (enc, FALSE); self->outbuf_size = enc_info.maxOutBufBytes; self->samples_per_frame = enc_info.frameLength; src_caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, mpegversion, "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info), "framed", G_TYPE_BOOLEAN, TRUE, "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info), NULL); /* raw */ if (transmux == 0) { GstBuffer *codec_data = gst_buffer_new_wrapped (g_memdup (enc_info.confBuf, enc_info.confSize), enc_info.confSize); gst_caps_set_simple (src_caps, "codec_data", GST_TYPE_BUFFER, codec_data, "stream-format", G_TYPE_STRING, "raw", NULL); gst_buffer_unref (codec_data); } else if (transmux == 1) { gst_caps_set_simple (src_caps, "stream-format", G_TYPE_STRING, "adif", NULL); } else if (transmux == 2) { gst_caps_set_simple (src_caps, "stream-format", G_TYPE_STRING, "adts", NULL); } else { g_assert_not_reached (); } gst_codec_utils_aac_caps_set_level_and_profile (src_caps, enc_info.confBuf, enc_info.confSize); ret = gst_audio_encoder_set_output_format (enc, src_caps); gst_caps_unref (src_caps); return ret; }
static void *libfdk_create(obs_data_t settings, obs_encoder_t encoder) { bool hasFdkHandle = false; libfdk_encoder_t *enc = 0; int bitrate = (int)obs_data_get_int(settings, "bitrate") * 1000; int afterburner = obs_data_get_bool(settings, "afterburner") ? 1 : 0; audio_t audio = obs_encoder_audio(encoder); int mode = 0; AACENC_ERROR err; if (!bitrate) { blog(LOG_ERROR, "Invalid bitrate"); return NULL; } enc = bzalloc(sizeof(libfdk_encoder_t)); enc->encoder = encoder; enc->channels = (int)audio_output_get_channels(audio); enc->sample_rate = audio_output_get_sample_rate(audio); switch(enc->channels) { case 1: mode = MODE_1; break; case 2: mode = MODE_2; break; case 3: mode = MODE_1_2; break; case 4: mode = MODE_1_2_1; break; case 5: mode = MODE_1_2_2; break; case 6: mode = MODE_1_2_2_1; break; default: blog(LOG_ERROR, "Invalid channel count"); goto fail; } CHECK_LIBFDK(aacEncOpen(&enc->fdkhandle, 0, enc->channels)); hasFdkHandle = true; CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AOT, 2)); // MPEG-4 AAC-LC CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_SAMPLERATE, enc->sample_rate)); CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELMODE, mode)); CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELORDER, 1)); CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATEMODE, 0)); CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATE, bitrate)); CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, 0)); CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AFTERBURNER, afterburner)); CHECK_LIBFDK(aacEncEncode(enc->fdkhandle, NULL, NULL, NULL, NULL)); CHECK_LIBFDK(aacEncInfo(enc->fdkhandle, &enc->info)); enc->frame_size_bytes = enc->info.frameLength * 2 * enc->channels; enc->packet_buffer_size = enc->channels * 768; if(enc->packet_buffer_size < 8192) enc->packet_buffer_size = 8192; enc->packet_buffer = bmalloc(enc->packet_buffer_size); blog(LOG_INFO, "libfdk_aac encoder created"); blog(LOG_INFO, "libfdk_aac bitrate: %d, channels: %d", bitrate / 1000, enc->channels); return enc; fail: if(hasFdkHandle) aacEncClose(&enc->fdkhandle); if(enc->packet_buffer) bfree(enc->packet_buffer); if(enc) bfree(enc); blog(LOG_WARNING, "libfdk_aac encoder creation failed"); return 0; }
int aacenc_init(HANDLE_AACENCODER *encoder, const aacenc_param_t *params, const pcm_sample_description_t *format, AACENC_InfoStruct *info) { int channel_mode; int aot; LIB_INFO lib_info; *encoder = 0; aacenc_get_lib_info(&lib_info); if ((channel_mode = aacenc_channel_mode(format)) == 0) { fprintf(stderr, "ERROR: unsupported channel layout\n"); goto FAIL; } if (aacEncOpen(encoder, 0, 0) != AACENC_OK) { fprintf(stderr, "ERROR: aacEncOpen() failed\n"); goto FAIL; } aot = (params->profile ? params->profile : AOT_AAC_LC); if (aacEncoder_SetParam(*encoder, AACENC_AOT, aot) != AACENC_OK) { fprintf(stderr, "ERROR: unsupported profile\n"); goto FAIL; } if (params->bitrate_mode == 0) aacEncoder_SetParam(*encoder, AACENC_BITRATE, params->bitrate); else if (aacEncoder_SetParam(*encoder, AACENC_BITRATEMODE, params->bitrate_mode) != AACENC_OK) { fprintf(stderr, "ERROR: unsupported bitrate mode\n"); goto FAIL; } if (aacEncoder_SetParam(*encoder, AACENC_SAMPLERATE, format->sample_rate) != AACENC_OK) { fprintf(stderr, "ERROR: unsupported sample rate\n"); goto FAIL; } if (aacEncoder_SetParam(*encoder, AACENC_CHANNELMODE, channel_mode) != AACENC_OK) { fprintf(stderr, "ERROR: unsupported channel mode\n"); goto FAIL; } aacEncoder_SetParam(*encoder, AACENC_BANDWIDTH, params->bandwidth); aacEncoder_SetParam(*encoder, AACENC_CHANNELORDER, 1); aacEncoder_SetParam(*encoder, AACENC_AFTERBURNER, !!params->afterburner); aacEncoder_SetParam(*encoder, AACENC_SBR_MODE, params->lowdelay_sbr); #if AACENCODER_LIB_VL0 > 3 || (AACENCODER_LIB_VL0==3 && AACENCODER_LIB_VL1>=4) if (lib_info.version > 0x03040800) aacEncoder_SetParam(*encoder, AACENC_SBR_RATIO, params->sbr_ratio); #endif if (aacEncoder_SetParam(*encoder, AACENC_TRANSMUX, params->transport_format) != AACENC_OK) { fprintf(stderr, "ERROR: unsupported transport format\n"); goto FAIL; } if (aacEncoder_SetParam(*encoder, AACENC_SIGNALING_MODE, params->sbr_signaling) != AACENC_OK) { fprintf(stderr, "ERROR: failed to set SBR signaling mode\n"); goto FAIL; } if (params->adts_crc_check) aacEncoder_SetParam(*encoder, AACENC_PROTECTION, 1); if (params->header_period) aacEncoder_SetParam(*encoder, AACENC_HEADER_PERIOD, params->header_period); if (aacEncEncode(*encoder, 0, 0, 0, 0) != AACENC_OK) { fprintf(stderr, "ERROR: encoder initialization failed\n"); goto FAIL; } if (aacEncInfo(*encoder, info) != AACENC_OK) { fprintf(stderr, "ERROR: cannot retrieve encoder info\n"); goto FAIL; } return 0; FAIL: if (encoder) aacEncClose(encoder); return -1; }