mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 20:25:34 +08:00
Add opus codec test
This commit is contained in:
0
src/media/audio/decode/opus_decoder.cpp
Normal file
0
src/media/audio/decode/opus_decoder.cpp
Normal file
0
src/media/audio/decode/opus_decoder.h
Normal file
0
src/media/audio/decode/opus_decoder.h
Normal file
0
src/media/audio/encode/opus_encoder.cpp
Normal file
0
src/media/audio/encode/opus_encoder.cpp
Normal file
0
src/media/audio/encode/opus_encoder.h
Normal file
0
src/media/audio/encode/opus_encoder.h
Normal file
39
tests/opus/OpusDecoderImpl.cpp
Normal file
39
tests/opus/OpusDecoderImpl.cpp
Normal 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() {}
|
||||||
28
tests/opus/OpusDecoderImpl.h
Normal file
28
tests/opus/OpusDecoderImpl.h
Normal 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
|
||||||
116
tests/opus/OpusEncoderImpl.cpp
Normal file
116
tests/opus/OpusEncoderImpl.cpp
Normal 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() {}
|
||||||
33
tests/opus/OpusEncoderImpl.h
Normal file
33
tests/opus/OpusEncoderImpl.h
Normal 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
10
tests/opus/base_type.h
Normal 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
31
tests/opus/main.cpp
Normal 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
96
tests/opus/opus_test.cpp
Normal 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;
|
||||||
|
}
|
||||||
17
xmake.lua
17
xmake.lua
@@ -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")
|
||||||
Reference in New Issue
Block a user