Add opus codec test

This commit is contained in:
dijunkun
2023-11-21 22:30:25 -08:00
parent 19506af831
commit e44c5b1cc7
12 changed files with 368 additions and 2 deletions

View File

View File

View File

View File

View File

@@ -0,0 +1,39 @@
#include "OpusDecoderImpl.h"
#define MAX_FRAME_SIZE 6 * 960
#define CHANNELS 2
OpusDecoderImpl::OpusDecoderImpl(int sampleRate, int channel) {
int err;
decoder = opus_decoder_create(sampleRate, channel, &err);
opus_decoder_ctl(decoder, OPUS_SET_LSB_DEPTH(16));
sample_rate = sample_rate;
channel_num = channel;
if (err < 0 || decoder == NULL) {
printf("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>\n");
return;
}
pcm_file = fopen("decode.pcm", "wb+");
}
bool OpusDecoderImpl::Decode(unsigned char* in_data, int len) {
unsigned char pcm_bytes[MAX_FRAME_SIZE * CHANNELS * 2];
opus_int16 out[MAX_FRAME_SIZE * CHANNELS];
auto frame_size = opus_decode(decoder, in_data, len, out, MAX_FRAME_SIZE, 0);
if (frame_size < 0) {
printf("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>\n");
return false;
}
for (auto i = 0; i < channel_num * frame_size; i++) {
pcm_bytes[2 * i] = out[i] & 0xFF;
pcm_bytes[2 * i + 1] = (out[i] >> 8) & 0xFF;
}
fwrite(pcm_bytes, sizeof(short), frame_size * channel_num, pcm_file);
fflush(pcm_file);
return true;
}
OpusDecoderImpl::~OpusDecoderImpl() {}

View File

@@ -0,0 +1,28 @@
#ifndef __OPUSDECODERIMPL_H
#define __OPUSDECODERIMPL_H
#include <stdio.h>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include "base_type.h"
#include "opus/opus.h"
class OpusDecoderImpl {
private:
/* data */
OpusDecoder *decoder;
int sample_rate;
int channel_num;
FILE *pcm_file;
public:
bool Decode(unsigned char *in_data, int len);
OpusDecoderImpl(int sampleRate, int channel);
~OpusDecoderImpl();
};
#endif

View File

@@ -0,0 +1,116 @@
#include "OpusEncoderImpl.h"
#include <stdlib.h>
#include <unistd.h>
#include <cstring>
#include "OpusDecoderImpl.h"
#define MAX_PACKET_SIZE 3 * 1276
OpusEncoderImpl::OpusEncoderImpl(int sampleRate, int channel)
: channel_num(channel), sample_rate(sampleRate) {
int err;
int applications[3] = {OPUS_APPLICATION_AUDIO, OPUS_APPLICATION_VOIP,
OPUS_APPLICATION_RESTRICTED_LOWDELAY};
encoder = opus_encoder_create(sampleRate, channel_num, applications[1], &err);
if (err != OPUS_OK || encoder == NULL) {
printf("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>opus <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><CAA7>\n");
}
opus_encoder_ctl(encoder, OPUS_SET_VBR(0)); // 0:CBR, 1:VBR
opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(true));
opus_encoder_ctl(encoder, OPUS_SET_BITRATE(96000));
opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(8)); // 8 0~10
opus_encoder_ctl(encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
opus_encoder_ctl(encoder,
OPUS_SET_LSB_DEPTH(16)); // ÿ<><C3BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>16<31><36>bit<69><74>2<EFBFBD><32>byte
opus_encoder_ctl(encoder, OPUS_SET_DTX(0));
opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(0));
EncodeRun();
}
// every pcm frame takes 23ms
void OpusEncoderImpl::Feed(unsigned char *data, int len) {
mutex.lock();
for (auto i = 0; i < len; i++) {
pcm_queue.emplace(data[i]);
}
mutex.unlock();
}
bool OpusEncoderImpl::PopFrame(StreamInfo &info) {
if (info_queue.size() > 0) {
access_mutex.lock();
info = info_queue.front();
info_queue.pop();
access_mutex.unlock();
return true;
}
return false;
}
// 48000 sample rate<74><65>48 samples/ms * 20ms * 2 channel = 1920
void OpusEncoderImpl::EncodeRun() {
m_thread = std::make_unique<std::thread>([this]() {
const int frame_size = 48 * 20; // 960
const int input_len = sizeof(opus_int16) * frame_size * 2;
OpusDecoderImpl decoder(48000, channel_num);
opus_int16 input_data[frame_size * 2] = {0};
unsigned char input_buffer[input_len] = {0};
unsigned char out_data[MAX_PACKET_SIZE] = {0};
while (isRuning) {
if (pcm_queue.size() >= input_len) {
mutex.lock();
for (int i = 0; i < input_len; i++) {
input_buffer[i] = pcm_queue.front();
pcm_queue.pop();
}
mutex.unlock();
auto ret = opus_encode(encoder, (opus_int16 *)input_buffer, frame_size,
out_data, MAX_PACKET_SIZE);
if (ret < 0) {
printf("opus decode failed, %d\n", ret);
break;
}
unsigned char *opus_buffer = (unsigned char *)malloc(ret);
memcpy(opus_buffer, out_data, ret);
decoder.Decode(opus_buffer, ret);
StreamInfo info;
info.data = opus_buffer;
info.len = ret;
info.dts = 20;
access_mutex.lock();
info_queue.push(info);
access_mutex.unlock();
} else {
usleep(1000);
}
}
});
}
void OpusEncoderImpl::Stop() {
isRuning = false;
m_thread->join();
while (pcm_queue.size() > 0) {
pcm_queue.pop();
}
opus_encoder_destroy(encoder);
}
OpusEncoderImpl::~OpusEncoderImpl() {}

View File

@@ -0,0 +1,33 @@
#ifndef __OPUSENCODERIMPL_H
#define __OPUSENCODERIMPL_H
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include "base_type.h"
#include "opus/opus.h"
class OpusEncoderImpl {
private:
OpusEncoder *encoder;
const int channel_num;
int sample_rate;
std::queue<StreamInfo> info_queue;
std::queue<unsigned char> pcm_queue;
std::mutex mutex;
bool isRuning = true;
std::mutex access_mutex;
std::unique_ptr<std::thread> m_thread;
public:
OpusEncoderImpl(int sampleRate, int channel);
void Feed(unsigned char *data, int len);
bool PopFrame(StreamInfo &info);
void EncodeRun();
void Stop();
~OpusEncoderImpl();
};
#endif

10
tests/opus/base_type.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __BASE_TYPE_H__
#define __BASE_TYPE_H__
typedef struct StreamInfo {
unsigned char *data;
int len;
int dts;
} StreamInfo;
#endif

31
tests/opus/main.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include <fstream>
#include <iostream>
#include <vector>
#include "OpusEncoderImpl.h"
#include "opus/opus.h"
int main() {
OpusEncoderImpl* opusEncoder = new OpusEncoderImpl(48000, 2);
std::ifstream inputFile("ls.pcm", std::ios::binary);
if (!inputFile) {
std::cerr << "Failed to open input file." << std::endl;
return -1;
}
char sample[960];
while (inputFile.read(sample, 960)) {
opusEncoder->Feed((unsigned char*)sample, 960);
}
// // <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>opus<75><73>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD>̣߳<DFB3><CCA3><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><D6BB>Ϊ<EFBFBD>˷<EFBFBD><CBB7><EFBFBD>
// StreamInfo info;
// while (opusEncoder.PopFrame(info)) {
// .....
// }
opusEncoder->Stop();
return 0;
}

96
tests/opus/opus_test.cpp Normal file
View File

@@ -0,0 +1,96 @@
#include <fstream>
#include <iostream>
#include <vector>
// Opus<75><73><EFBFBD><EFBFBD><EBBAAF>
#include <opus/opus.h>
#include <iostream>
#include <vector>
#define SAMPLE_RATE 48000
#define CHANNELS 2
#define FRAME_SIZE 960
#define APPLICATION OPUS_APPLICATION_AUDIO
// <20><><EFBFBD><EFBFBD><EBBAAF>
int encode(const std::vector<opus_int16>& pcm,
std::vector<unsigned char>& opus) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int error;
OpusEncoder* encoder =
opus_encoder_create(SAMPLE_RATE, CHANNELS, APPLICATION, &error);
if (error != OPUS_OK) {
std::cerr << "Failed to create encoder: " << opus_strerror(error)
<< std::endl;
return error;
}
// <20><><EFBFBD>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
opus_encoder_ctl(encoder, OPUS_SET_BITRATE(64000));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С
int maxOpusSize = FRAME_SIZE * CHANNELS * sizeof(opus_int16);
opus.resize(maxOpusSize);
// <20><><EFBFBD><EFBFBD>
int encodedSize =
opus_encode(encoder, pcm.data(), FRAME_SIZE, opus.data(), maxOpusSize);
if (encodedSize < 0) {
std::cerr << "Encoding error: " << opus_strerror(encodedSize) << std::endl;
return encodedSize;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
opus_encoder_destroy(encoder);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD>С
opus.resize(encodedSize);
return 0;
}
int main(int argc, char** argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " input.pcm output.opus" << std::endl;
return -1;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>
std::ifstream inputFile(argv[1], std::ios::binary);
if (!inputFile) {
std::cerr << "Failed to open input file." << std::endl;
return -1;
}
// <20><>ȡPCM<43><4D><EFBFBD><EFBFBD>
std::vector<opus_int16> pcmData;
opus_int16 sample;
while (inputFile.read(reinterpret_cast<char*>(&sample), sizeof(opus_int16))) {
pcmData.push_back(sample);
}
// <20><><EFBFBD><EFBFBD>ΪOpus<75><73>ʽ
std::vector<unsigned char> opusData;
int result = encode(pcmData, opusData);
if (result != 0) {
std::cerr << "Encoding failed with error code " << result << std::endl;
return result;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>
std::ofstream outputFile(argv[2], std::ios::binary);
if (!outputFile) {
std::cerr << "Failed to open output file." << std::endl;
return -1;
}
// д<><D0B4>Opus<75><73><EFBFBD><EFBFBD>
outputFile.write(reinterpret_cast<const char*>(opusData.data()),
opusData.size());
// <20><><EFBFBD><EFBFBD>
std::cout << "Encoding complete. size:" << pcmData.size() * 2 << std::endl;
return 0;
}

View File

@@ -27,7 +27,7 @@ elseif is_os("macosx") then
add_ldflags("-ld_classic", {force = true}) add_ldflags("-ld_classic", {force = true})
end end
add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.11.0", "openfec") add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.11.0", "openfec", "libopus 1.4")
add_packages("spdlog", "openfec") add_packages("spdlog", "openfec")
includes("thirdparty") includes("thirdparty")
@@ -188,6 +188,11 @@ target("media")
"src/media/video/encode/ffmpeg", "src/media/video/encode/ffmpeg",
"src/media/video/decode/ffmpeg", {public = true}) "src/media/video/decode/ffmpeg", {public = true})
end end
add_packages("opus")
add_files("src/media/audio/encode/*.cpp",
"src/media/audio/decode/*.cpp")
add_includedirs("src/media/audio/encode",
"src/media/audio/decode", {public = true})
target("qos") target("qos")
set_kind("static") set_kind("static")
@@ -257,4 +262,12 @@ target("projectx")
-- set_kind("binary") -- set_kind("binary")
-- add_packages("openfec") -- add_packages("openfec")
-- add_files("tests/fec/simple_server.cpp") -- add_files("tests/fec/simple_server.cpp")
-- add_includedirs("tests/fec") -- add_includedirs("tests/fec")
target("opus_test")
set_kind("binary")
add_packages("libopus")
add_files("tests/opus/OpusEncoderImpl.cpp",
"tests/opus/OpusDecoderImpl.cpp",
"tests/opus/main.cpp")
add_includedirs("tests/opus")