nlsCppSdk/encoder/nlsEncoder.cpp (232 lines of code) (raw):
/*
* Copyright 2015 Alibaba Group Holding Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nlsEncoder.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <sstream>
#include "nlog.h"
#include "nlsGlobal.h"
#include "opus/opus.h"
#include "opus/opus_defines.h"
#ifdef ENABLE_OGGOPUS
#include "oggopusEncoder.h"
#endif
#define DEFAULT_CHANNELS 1
//#define OPU_DEBUG
namespace AlibabaNls {
NlsEncoder::NlsEncoder() {
nlsEncoder_ = NULL;
encoder_type_ = ENCODER_NONE;
}
#ifdef ENABLE_OGGOPUS
static size_t oggopusEncodedData(const uint8_t *encoded_data, int len,
void *user_data) {
NlsEncoder *encoder = reinterpret_cast<NlsEncoder *>(user_data);
if (encoder && encoded_data) {
encoder->pushbackEncodedData(encoded_data, len);
}
return len;
}
int NlsEncoder::pushbackEncodedData(const uint8_t *encoded_data, int data_len) {
encoded_data_.Pushback(encoded_data, data_len);
return Success;
}
#endif
int NlsEncoder::getFrameSampleBytes() {
if (nlsEncoder_ == NULL) {
LOG_ERROR("nlsEncoder_ is inexistent, pls create first");
return -(EncoderInexistent);
}
int frame_sample_bytes = 0;
if (encoder_type_ == ENCODER_OPU) {
frame_sample_bytes = DefaultOpusFrameSize;
} else if (encoder_type_ == ENCODER_OPUS) {
#ifdef ENABLE_OGGOPUS
frame_sample_bytes =
(static_cast<OggOpusDataEncoder *>(nlsEncoder_))->GetFrameSampleBytes();
#endif
} else {
LOG_ERROR("donnot setting encoder type.");
return -(InvalidEncoderType);
}
return frame_sample_bytes;
}
int NlsEncoder::createNlsEncoder(ENCODER_TYPE type, int channels,
const int sampleRate, int *errorCode) {
int ret = Success;
int tmpCode = 0;
int channel_num = channels;
if (channel_num < 0) {
channel_num = DEFAULT_CHANNELS;
}
if (nlsEncoder_ != NULL) {
LOG_WARN("nlsEncoder_ is existent, pls destroy first");
return -(EncoderExistent);
}
if (type == ENCODER_OPU) {
nlsEncoder_ = opus_encoder_create(sampleRate, channel_num,
OPUS_APPLICATION_VOIP, &tmpCode);
if (nlsEncoder_) {
opus_encoder_ctl(
(OpusEncoder *)nlsEncoder_,
OPUS_SET_VBR(
1)); /* 动态码率:OPUS_SET_VBR(1), 固定码率 : OPUS_SET_VBR(0) */
opus_encoder_ctl(
(OpusEncoder *)nlsEncoder_,
OPUS_SET_BITRATE(
27800)); /* 指定opus编码码率, 比特率从 6kb / s 到 510 kb / s,
想要压缩比大一些就设置码率小一点 */
opus_encoder_ctl((OpusEncoder *)nlsEncoder_,
OPUS_SET_COMPLEXITY(8)); /* 计算复杂度 */
opus_encoder_ctl(
(OpusEncoder *)nlsEncoder_,
OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); /* 设置针对语音优化 */
encoder_type_ = type;
ret = Success;
LOG_DEBUG("opus_encoder_create for OPU mode success");
} else {
LOG_ERROR("encoder create failed");
nlsEncoder_ = NULL;
ret = -(OpusEncoderCreateFailed);
}
*errorCode = tmpCode;
} else if (type == ENCODER_OPUS) {
#ifdef ENABLE_OGGOPUS
nlsEncoder_ = new OggOpusDataEncoder();
if (nlsEncoder_ == NULL) {
LOG_ERROR("nlsEncoder_ new OggOpusDataEncoder failed");
return -(OggOpusEncoderCreateFailed);
}
ret = (static_cast<OggOpusDataEncoder *>(nlsEncoder_))
->OggopusEncoderCreate(oggopusEncodedData, this, sampleRate);
if (ret == Success) {
/* 这里暂时未开放编码码率和计算复杂度的设置 */
(static_cast<OggOpusDataEncoder *>(nlsEncoder_))->SetBitrate(27800);
(static_cast<OggOpusDataEncoder *>(nlsEncoder_))
->SetSampleRate(sampleRate);
(static_cast<OggOpusDataEncoder *>(nlsEncoder_))->SetComplexity(8);
LOG_DEBUG("OggopusEncoderCreate for OPUS mode success");
} else {
LOG_ERROR("OggopusEncoderCreate failed, errorcode:%d", ret);
}
#endif
encoder_type_ = type;
}
return ret;
}
int NlsEncoder::nlsEncoding(const uint8_t *frameBuff, const int frameLen,
unsigned char *outputBuffer, int outputSize) {
if (!frameBuff || frameLen <= 0 || !outputBuffer || outputSize <= 0) {
LOG_ERROR("invalid params");
return 0;
}
unsigned char *outputTmp = NULL;
outputTmp = (unsigned char *)malloc(outputSize);
if (!outputTmp) {
return 0;
}
memset(outputTmp, 0x0, outputSize);
int encoderSize = -1;
/* 1. 灌入数据开始编码 */
if (encoder_type_ == ENCODER_OPU) {
// uint8 to int16
int16_t *interBuffer = (int16_t *)malloc(frameLen);
if (interBuffer == NULL) {
LOG_ERROR("interBuffer malloc failed");
free(outputTmp);
return 0;
}
int i = 0;
for (i = 0; i < frameLen; i += 2) {
interBuffer[i / 2] =
(int16_t)((frameBuff[i + 1] << 8 & 0xff00) | (frameBuff[i] & 0xff));
}
encoderSize = opus_encode((OpusEncoder *)nlsEncoder_, interBuffer,
frameLen / 2, outputTmp, outputSize);
// LOG_DEBUG("frameLen:%d, outputSize:%d, encoderSize:%d",
// frameLen, outputSize, encoderSize);
if (encoderSize < 0) {
free(interBuffer);
free(outputTmp);
return encoderSize;
}
if (interBuffer) free(interBuffer);
interBuffer = NULL;
} else if (encoder_type_ == ENCODER_OPUS) {
#ifdef ENABLE_OGGOPUS
encoderSize = (static_cast<OggOpusDataEncoder *>(nlsEncoder_))
->OggopusEncode((const char *)frameBuff, frameLen);
if (encoderSize != Success) {
LOG_ERROR("OggopusEncode failed, ret %d", encoderSize);
free(outputTmp);
return encoderSize;
}
#endif
}
/* 2. 取出编码后数据 */
if (encoder_type_ == ENCODER_OPU) {
*(outputBuffer + 0) = (unsigned char)encoderSize;
memcpy((outputBuffer + 1), outputTmp, encoderSize);
encoderSize += 1;
} else if (encoder_type_ == ENCODER_OPUS) {
#ifdef ENABLE_OGGOPUS
encoderSize = 0;
int data_len = 0;
data_len = encoded_data_.ElementNum();
if (data_len > 0) {
int array_idx = 0;
int element_idx = 0;
data_len = (data_len > outputSize) ? outputSize : data_len;
encoderSize = encoded_data_.Get(outputTmp, data_len, &array_idx,
&element_idx, true);
if (encoderSize > 0) {
// LOG_DEBUG("opus encoded %dbytes", encoderSize);
memcpy(outputBuffer, outputTmp, encoderSize);
}
}
#endif
}
#ifdef OPU_DEBUG
if (encoderSize > 0) {
std::ofstream ofs;
if (encoder_type_ == ENCODER_OPU) {
ofs.open("./mid_out.opu",
std::ios::out | std::ios::app | std::ios::binary);
} else if (encoder_type_ == ENCODER_OPUS) {
ofs.open("./mid_out.ogg",
std::ios::out | std::ios::app | std::ios::binary);
}
if (ofs.is_open()) {
ofs.write((const char *)outputBuffer, encoderSize);
ofs.flush();
ofs.close();
}
}
#endif
if (outputTmp) free(outputTmp);
outputTmp = NULL;
return encoderSize;
}
int NlsEncoder::nlsEncoderSoftRestart() {
if (!nlsEncoder_) {
LOG_WARN("nlsEncoder is inexistent");
return -(EncoderInexistent);
}
if (encoder_type_ == ENCODER_OPUS) {
#ifdef ENABLE_OGGOPUS
(static_cast<OggOpusDataEncoder *>(nlsEncoder_))->OggopusSoftRestart();
#endif
}
return Success;
}
int NlsEncoder::destroyNlsEncoder() {
if (!nlsEncoder_) {
LOG_WARN("nlsEncoder is inexistent");
return -(EncoderInexistent);
}
if (encoder_type_ == ENCODER_OPU) {
opus_encoder_destroy((OpusEncoder *)nlsEncoder_);
nlsEncoder_ = NULL;
} else if (encoder_type_ == ENCODER_OPUS) {
#ifdef ENABLE_OGGOPUS
(static_cast<OggOpusDataEncoder *>(nlsEncoder_))->OggopusDestroy();
encoded_data_.Clear();
delete static_cast<OggOpusDataEncoder *>(nlsEncoder_);
nlsEncoder_ = NULL;
#endif
}
encoder_type_ = ENCODER_NONE;
return Success;
}
} // namespace AlibabaNls