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; }
void SoftAACEncoder2::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError) { return; } List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); if (!mSentCodecSpecificData) { // The very first thing we want to output is the codec specific // data. It does not require any input data but we will need an // output buffer to store it in. if (outQueue.empty()) { return; } if (AACENC_OK != aacEncEncode(mAACEncoder, NULL, NULL, NULL, NULL)) { ALOGE("Unable to initialize encoder for profile / sample-rate / bit-rate / channels"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } OMX_U32 actualBitRate = aacEncoder_GetParam(mAACEncoder, AACENC_BITRATE); if (mBitRate != actualBitRate) { ALOGW("Requested bitrate %u unsupported, using %u", mBitRate, actualBitRate); } AACENC_InfoStruct encInfo; if (AACENC_OK != aacEncInfo(mAACEncoder, &encInfo)) { ALOGE("Failed to get AAC encoder info"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; if (outHeader->nOffset + encInfo.confSize > outHeader->nAllocLen) { ALOGE("b/34617444"); android_errorWriteLog(0x534e4554,"34617444"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } outHeader->nFilledLen = encInfo.confSize; outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG; uint8_t *out = outHeader->pBuffer + outHeader->nOffset; memcpy(out, encInfo.confBuf, encInfo.confSize); outQueue.erase(outQueue.begin()); outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); mSentCodecSpecificData = true; } size_t numBytesPerInputFrame = mNumChannels * kNumSamplesPerFrame * sizeof(int16_t); // Limit input size so we only get one ELD frame if (mAACProfile == OMX_AUDIO_AACObjectELD && numBytesPerInputFrame > 512) { numBytesPerInputFrame = 512; } for (;;) { // We do the following until we run out of buffers. while (mInputSize < numBytesPerInputFrame) { // As long as there's still input data to be read we // will drain "kNumSamplesPerFrame * mNumChannels" samples // into the "mInputFrame" buffer and then encode those // as a unit into an output buffer. if (mSawInputEOS || inQueue.empty()) { return; } BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; const void *inData = inHeader->pBuffer + inHeader->nOffset; size_t copy = numBytesPerInputFrame - mInputSize; if (copy > inHeader->nFilledLen) { copy = inHeader->nFilledLen; } if (mInputFrame == NULL) { mInputFrame = new int16_t[numBytesPerInputFrame / sizeof(int16_t)]; mAllocatedFrameSize = numBytesPerInputFrame; } else if (mAllocatedFrameSize != numBytesPerInputFrame) { ALOGE("b/34621073: changed size from %d to %d", (int)mAllocatedFrameSize, (int)numBytesPerInputFrame); android_errorWriteLog(0x534e4554,"34621073"); delete mInputFrame; mInputFrame = new int16_t[numBytesPerInputFrame / sizeof(int16_t)]; mAllocatedFrameSize = numBytesPerInputFrame; } if (mInputSize == 0) { mInputTimeUs = inHeader->nTimeStamp; } memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy); mInputSize += copy; inHeader->nOffset += copy; inHeader->nFilledLen -= copy; // "Time" on the input buffer has in effect advanced by the // number of audio frames we just advanced nOffset by. inHeader->nTimeStamp += (copy * 1000000ll / mSampleRate) / (mNumChannels * sizeof(int16_t)); if (inHeader->nFilledLen == 0) { if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { mSawInputEOS = true; // Pad any remaining data with zeroes. memset((uint8_t *)mInputFrame + mInputSize, 0, numBytesPerInputFrame - mInputSize); mInputSize = numBytesPerInputFrame; } inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); inData = NULL; inHeader = NULL; inInfo = NULL; } } // At this point we have all the input data necessary to encode // a single frame, all we need is an output buffer to store the result // in. if (outQueue.empty()) { return; } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; uint8_t *outPtr = (uint8_t *)outHeader->pBuffer + outHeader->nOffset; size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset; AACENC_InArgs inargs; AACENC_OutArgs outargs; memset(&inargs, 0, sizeof(inargs)); memset(&outargs, 0, sizeof(outargs)); inargs.numInSamples = numBytesPerInputFrame / sizeof(int16_t); void* inBuffer[] = { (unsigned char *)mInputFrame }; INT inBufferIds[] = { IN_AUDIO_DATA }; INT inBufferSize[] = { (INT)numBytesPerInputFrame }; INT inBufferElSize[] = { sizeof(int16_t) }; AACENC_BufDesc inBufDesc; inBufDesc.numBufs = sizeof(inBuffer) / sizeof(void*); inBufDesc.bufs = (void**)&inBuffer; inBufDesc.bufferIdentifiers = inBufferIds; inBufDesc.bufSizes = inBufferSize; inBufDesc.bufElSizes = inBufferElSize; void* outBuffer[] = { outPtr }; INT outBufferIds[] = { OUT_BITSTREAM_DATA }; INT outBufferSize[] = { 0 }; INT outBufferElSize[] = { sizeof(UCHAR) }; AACENC_BufDesc outBufDesc; outBufDesc.numBufs = sizeof(outBuffer) / sizeof(void*); outBufDesc.bufs = (void**)&outBuffer; outBufDesc.bufferIdentifiers = outBufferIds; outBufDesc.bufSizes = outBufferSize; outBufDesc.bufElSizes = outBufferElSize; // Encode the mInputFrame, which is treated as a modulo buffer AACENC_ERROR encoderErr = AACENC_OK; size_t nOutputBytes = 0; do { memset(&outargs, 0, sizeof(outargs)); outBuffer[0] = outPtr; outBufferSize[0] = outAvailable - nOutputBytes; encoderErr = aacEncEncode(mAACEncoder, &inBufDesc, &outBufDesc, &inargs, &outargs); if (encoderErr == AACENC_OK) { outPtr += outargs.numOutBytes; nOutputBytes += outargs.numOutBytes; if (outargs.numInSamples > 0) { int numRemainingSamples = inargs.numInSamples - outargs.numInSamples; if (numRemainingSamples > 0) { memmove(mInputFrame, &mInputFrame[outargs.numInSamples], sizeof(int16_t) * numRemainingSamples); } inargs.numInSamples -= outargs.numInSamples; } } } while (encoderErr == AACENC_OK && inargs.numInSamples > 0); outHeader->nFilledLen = nOutputBytes; outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; if (mSawInputEOS) { // We also tag this output buffer with EOS if it corresponds // to the final input buffer. outHeader->nFlags = OMX_BUFFERFLAG_EOS; } outHeader->nTimeStamp = mInputTimeUs; #if 0 ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)", nOutputBytes, mInputTimeUs, outHeader->nFlags); hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen); #endif outQueue.erase(outQueue.begin()); outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); outHeader = NULL; outInfo = NULL; mInputSize = 0; } }
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; }
/***************************************************************************** * OpenDecoder: open the encoder. *****************************************************************************/ static int OpenEncoder(vlc_object_t *p_this) { encoder_t *p_enc = (encoder_t *)p_this; config_ChainParse(p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg); int i_aot; switch (p_enc->fmt_out.i_codec) { case VLC_CODEC_MP4A: i_aot = var_InheritInteger(p_enc, ENC_CFG_PREFIX "profile"); break; case VLC_FOURCC('l', 'a', 'a', 'c'): i_aot = PROFILE_AAC_LC; break; case VLC_FOURCC('h', 'a', 'a', 'c'): i_aot = PROFILE_AAC_HE; break; case VLC_FOURCC('s', 'a', 'a', 'c'): i_aot = PROFILE_AAC_HE_v2; break; default: return VLC_EGENERIC; } if (p_enc->fmt_in.audio.i_channels != 2) if (i_aot == PROFILE_AAC_HE_v2 || i_aot == PROFILE_AAC_ELD) { msg_Err(p_enc, "Selected profile %d can only be used with stereo", i_aot); return VLC_EGENERIC; } uint16_t channel_config; CHANNEL_MODE mode; switch (p_enc->fmt_in.audio.i_channels) { case 1: mode = MODE_1; channel_config = AOUT_CHAN_CENTER; break; case 2: mode = MODE_2; channel_config = AOUT_CHANS_STEREO; break; case 3: mode = MODE_1_2; channel_config = AOUT_CHANS_3_0; break; case 4: mode = MODE_1_2_1; channel_config = AOUT_CHANS_4_CENTER_REAR; break; case 5: mode = MODE_1_2_2; channel_config = AOUT_CHANS_5_0; break; case 6: mode = MODE_1_2_2_1; channel_config = AOUT_CHANS_5_1; break; case 8: mode = MODE_1_2_2_2_1; channel_config = AOUT_CHANS_7_1; break; default: msg_Err(p_enc, "we do not support > 8 input channels, this input has %i", p_enc->fmt_in.audio.i_channels); return VLC_EGENERIC; } p_enc->fmt_in.audio.i_physical_channels = channel_config; msg_Info(p_enc, "Initializing AAC Encoder, %i channels", p_enc->fmt_in.audio.i_channels); /* Allocate the memory needed to store the encoder's structure */ encoder_sys_t *p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)); if (unlikely(!p_sys)) return VLC_ENOMEM; p_enc->p_sys = p_sys; p_enc->fmt_in.i_codec = VLC_CODEC_S16N; p_enc->fmt_out.i_cat = AUDIO_ES; p_enc->fmt_out.i_codec = VLC_CODEC_MP4A; p_sys->i_pts_last = 0; AACENC_ERROR erraac; erraac = aacEncOpen(&p_sys->handle, 0, p_enc->fmt_in.audio.i_channels); if (erraac != AACENC_OK) { msg_Err(p_enc, "Unable to open encoder: %s", fdkaac_error(erraac)); free(p_sys); return VLC_EGENERIC; } #define SET_PARAM(P, V) do { \ AACENC_ERROR err = aacEncoder_SetParam(p_sys->handle, AACENC_ ## P, V); \ if (err != AACENC_OK) { \ msg_Err(p_enc, "Couldn't set " #P " to value %d: %s", V, fdkaac_error(err)); \ goto error; \ } \ } while(0) SET_PARAM(AOT, i_aot); bool b_eld_sbr = var_InheritBool(p_enc, ENC_CFG_PREFIX "sbr"); if (i_aot == PROFILE_AAC_ELD && b_eld_sbr) SET_PARAM(SBR_MODE, 1); SET_PARAM(SAMPLERATE, p_enc->fmt_out.audio.i_rate); SET_PARAM(CHANNELMODE, mode); SET_PARAM(CHANNELORDER, CH_ORDER_WG4); int i_vbr = var_InheritInteger(p_enc, ENC_CFG_PREFIX "vbr"); if (i_vbr != 0) { if ((i_aot == PROFILE_AAC_HE || i_aot == PROFILE_AAC_HE_v2) && i_vbr > 3) { msg_Warn(p_enc, "Maximum VBR quality for this profile is 3, setting vbr=3"); i_vbr = 3; } SET_PARAM(BITRATEMODE, i_vbr); } else { int i_bitrate = p_enc->fmt_out.i_bitrate; if (i_bitrate == 0) { i_bitrate = 96 * p_enc->fmt_in.audio.i_channels * p_enc->fmt_out.audio.i_rate / 44; if (i_aot == PROFILE_AAC_HE || i_aot == PROFILE_AAC_HE_v2 || b_eld_sbr) i_bitrate /= 2; p_enc->fmt_out.i_bitrate = i_bitrate; msg_Info(p_enc, "Setting optimal bitrate of %i", i_bitrate); } SET_PARAM(BITRATE, i_bitrate); } SET_PARAM(TRANSMUX, 0); SET_PARAM(SIGNALING_MODE, (int)var_InheritInteger(p_enc, ENC_CFG_PREFIX "signaling")); SET_PARAM(AFTERBURNER, !!var_InheritBool(p_enc, ENC_CFG_PREFIX "afterburner")); #undef SET_PARAM erraac = aacEncEncode(p_sys->handle, NULL, NULL, NULL, NULL); if (erraac != AACENC_OK) { msg_Err(p_enc, "Unable to initialize the encoder: %s", fdkaac_error(erraac)); goto error; } AACENC_InfoStruct info = { 0 }; erraac = aacEncInfo(p_sys->handle, &info); if (erraac != AACENC_OK) { msg_Err(p_enc, "Unable to get the encoder info: %s", fdkaac_error(erraac)); goto error; } /* The maximum packet size is 6144 bits aka 768 bytes per channel. */ p_sys->i_maxoutputsize = 768*p_enc->fmt_in.audio.i_channels; p_enc->fmt_in.audio.i_bitspersample = 16; p_sys->i_frame_size = info.frameLength; p_sys->i_encoderdelay = info.encoderDelay; p_enc->fmt_out.i_extra = info.confSize; if (p_enc->fmt_out.i_extra) { p_enc->fmt_out.p_extra = malloc(p_enc->fmt_out.i_extra); if (p_enc->fmt_out.p_extra == NULL) { msg_Err(p_enc, "Unable to allocate fmt_out.p_extra"); goto error; } memcpy(p_enc->fmt_out.p_extra, info.confBuf, p_enc->fmt_out.i_extra); } p_enc->pf_encode_audio = EncodeAudio; #ifndef NDEBUG // TODO: Add more debug info to this config printout msg_Dbg(p_enc, "fmt_out.p_extra = %i", p_enc->fmt_out.i_extra); #endif return VLC_SUCCESS; error: CloseEncoder(p_this); return VLC_EGENERIC; }
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; }