Compare commits
31 Commits
send_audio
...
master
Author | SHA1 | Date |
---|---|---|
|
7c64255cbd | |
|
9c8c15a924 | |
|
3dd6a84c5d | |
|
5179cc5aa7 | |
|
8d8c759e3e | |
|
3ac6763cba | |
|
356be092d4 | |
|
4043aae31a | |
|
ee855524dd | |
|
d4cf9b8d70 | |
|
81a6bc7c6f | |
|
9893ed5da2 | |
|
f8daa9c277 | |
|
34306dcaca | |
|
287bd759a1 | |
|
0bfb8fe405 | |
|
8eb7adb0fd | |
|
00672f25fe | |
|
aced2e2308 | |
|
2fd9f33a68 | |
|
012a95f475 | |
|
6a2639661f | |
|
35febd4b41 | |
|
dc9dd314d6 | |
|
7086e4cd21 | |
|
d10c751a06 | |
|
16962bf871 | |
|
f2299b38db | |
|
281bb13a73 | |
|
498932385e | |
|
5374dae17e |
20
build.sh
20
build.sh
|
@ -1,3 +1,17 @@
|
||||||
#g++ -shared -fPIC -std=c++17 $(python3 -m pybind11 --includes) -I./include -L./lib -L/usr/lib/x86_64-linux-gnu -lMRTCEngine -lpython3.10 -Wl,-rpath='$ORIGIN/lib' -o rtc_plugins$(python3-config --extension-suffix) rtc_plugins.cpp util/RTCContext.cpp
|
g++ -shared -fPIC \
|
||||||
|
-I/usr/include/python3.10 -I/usr/include/python3.10/numpy -I./include \
|
||||||
g++ -shared -fPIC -std=c++17 $(python3 -m pybind11 --includes) -I./include -L./lib -L/usr/lib/x86_64-linux-gnu -Wl,--no-as-needed -lMRTCEngine -Wl,--as-needed -lpython3.10 -Wl,-rpath='$ORIGIN/lib' -o rtc_plugins$(python3-config --extension-suffix) rtc_plugins.cpp util/RTCContext.cpp
|
-L./lib -L/usr/lib/x86_64-linux-gnu \
|
||||||
|
-DRTC_NUMPY_IMPL \
|
||||||
|
rtc_plugins.cpp util/RTCContext.cpp \
|
||||||
|
-lMRTCEngine -lboost_python310 -lboost_numpy310 -lpython3.10 \
|
||||||
|
-Wl,-rpath='$ORIGIN/lib' \
|
||||||
|
-o rtc_plugins.so
|
||||||
|
#g++ -shared -fPIC \
|
||||||
|
# -I/usr/include/python3.10 -I/usr/include/python3.10/numpy -I./include \
|
||||||
|
# -I/usr/include/python3.10 -I/usr/include/numpy \
|
||||||
|
# -Wl,-rpath='$ORIGIN/lib' \
|
||||||
|
# -lboost_python310 -lpython3.10 \
|
||||||
|
# -lMRTCEngine -lboost_python310 -lpython3.10 \
|
||||||
|
# -L$(python3 -c "import numpy; print(numpy.get_include())") \
|
||||||
|
# rtc_plugins.cpp util/RTCContext.cpp \
|
||||||
|
# -o rtc_plugins.so
|
||||||
|
|
174
rtc_plugins.cpp
174
rtc_plugins.cpp
|
@ -1,11 +1,20 @@
|
||||||
#include "util/RTCContext.h"
|
// rtc_plugins.cpp
|
||||||
#include <pybind11/pybind11.h>
|
|
||||||
#include <pybind11/numpy.h> // pybind11 的 NumPy 支持
|
|
||||||
|
|
||||||
namespace py = pybind11;
|
#define IMPLEMENT_NUMPY_API // 标记这是实现文件
|
||||||
|
#include "util/numpyStub.h"
|
||||||
#include "util/RTCContext.h"
|
#include "util/RTCContext.h"
|
||||||
|
|
||||||
int init(const char* selfUserId, const char* selfDisplayName, const char* selfRoomId) {
|
// 提供转换接口
|
||||||
|
void** get_numpy_api() {
|
||||||
|
return (void**)RTC_PLUGINS_ARRAY_API;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace py = boost::python;
|
||||||
|
|
||||||
|
int init(const char* selfUserId, const char* selfDisplayName, const char* selfRoomId, boost::python::object callback) {
|
||||||
|
|
||||||
|
RTCContext::instance().setPyCallback(callback);
|
||||||
bool res = RTCContext::instance().init(selfUserId, selfDisplayName, selfRoomId);
|
bool res = RTCContext::instance().init(selfUserId, selfDisplayName, selfRoomId);
|
||||||
if (res) {
|
if (res) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -14,6 +23,7 @@ int init(const char* selfUserId, const char* selfDisplayName, const char* selfRo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int initRecv(const char* destRoomId, const char* srcUserId, const int16_t destChannelIndex) {
|
int initRecv(const char* destRoomId, const char* srcUserId, const int16_t destChannelIndex) {
|
||||||
|
|
||||||
bool res = RTCContext::instance().initRecv(destRoomId, srcUserId, destChannelIndex);
|
bool res = RTCContext::instance().initRecv(destRoomId, srcUserId, destChannelIndex);
|
||||||
if (res) {
|
if (res) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -21,7 +31,7 @@ int initRecv(const char* destRoomId, const char* srcUserId, const int16_t destCh
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int initSend(const char* srcRoomId, const char* destRoomId, const int16_t destChannelIndex, const int16_t channelNum) {
|
int initSend(const char* srcRoomId, const char* destRoomId, const int16_t destChannelIndex, const uint8_t channelNum) {
|
||||||
bool res = RTCContext::instance().initSend(srcRoomId, destRoomId, destChannelIndex, channelNum);
|
bool res = RTCContext::instance().initSend(srcRoomId, destRoomId, destChannelIndex, channelNum);
|
||||||
if (res) {
|
if (res) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -29,84 +39,92 @@ int initSend(const char* srcRoomId, const char* destRoomId, const int16_t destCh
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// NumPy 数据交互(关键修改)
|
|
||||||
py::array_t<int16_t> getNumpyData() {
|
py::object create_int16_array() {
|
||||||
return py::array_t<int16_t>(
|
// 1. 定义数组维度(1维,长度为 4)
|
||||||
RTCContext::instance().getNumpyData() // 假设返回的是已有 NumPy 数组
|
npy_intp dims[1] = {4};
|
||||||
|
|
||||||
|
// 2. 创建原生 C 数组(int16_t 数据)
|
||||||
|
int16_t data[4] = {1, 2, -3, 4}; // 示例数据
|
||||||
|
|
||||||
|
// 3. 通过 NumPy C API 创建 PyObject*
|
||||||
|
PyObject* py_array = PyArray_SimpleNewFromData(
|
||||||
|
1, // 维度数
|
||||||
|
dims, // 各维度大小
|
||||||
|
NPY_INT16, // 数据类型(np.int16)
|
||||||
|
data // 数据指针
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
int sendCustomAudioData(int16_t destChannelIndex, py::array_t<int16_t> inputArray,
|
if (!py_array) {
|
||||||
int32_t sampleRate, uint64_t channelNum, uint64_t dataLen) {
|
throw std::runtime_error("Failed to create NumPy array");
|
||||||
//py::gil_scoped_release release;
|
|
||||||
|
|
||||||
py::array_t<int16_t> contiguous = py::array::ensure(inputArray);
|
|
||||||
if (!contiguous) throw py::value_error("Array conversion failed");
|
|
||||||
auto buf = contiguous.request();
|
|
||||||
if (buf.size != dataLen) {
|
|
||||||
throw py::value_error("Array length does not match dataLen");
|
|
||||||
}
|
}
|
||||||
std::vector<int16_t> localCopy(static_cast<int16_t*>(buf.ptr),
|
|
||||||
static_cast<int16_t*>(buf.ptr) + buf.size);
|
|
||||||
|
|
||||||
return RTCContext::instance().sendCustomAudioData(
|
// 4. 转换为 py::object(自动管理引用计数)
|
||||||
destChannelIndex, localCopy.data(), sampleRate, channelNum, dataLen
|
return py::object(py::handle<>(py_array));
|
||||||
);
|
|
||||||
//return RTCContext::instance().sendCustomAudioData(
|
|
||||||
// destChannelIndex, buf.ptr, sampleRate, channelNum, dataLen
|
|
||||||
//);
|
|
||||||
}
|
|
||||||
py::list getListData() {
|
|
||||||
return RTCContext::instance().getListData();
|
|
||||||
}
|
|
||||||
int getSize() {
|
|
||||||
return RTCContext::instance().getSize();
|
|
||||||
}
|
|
||||||
RetAudioFrame getData() {
|
|
||||||
return RTCContext::instance().getData();
|
|
||||||
}
|
|
||||||
int16_t getDataCount() {
|
|
||||||
return RTCContext::instance().getDataCount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_MODULE(rtc_plugins, m) {
|
int sendCustomAudioData(int16_t destChannelIndex, py::object pD,
|
||||||
// 可选:暴露 RetAudioFrame 类(需额外绑定)
|
int32_t sampleRate, uint64_t channelNum, uint64_t dataLen) {
|
||||||
py::class_<RetAudioFrame>(m, "RetAudioFrame")
|
try {
|
||||||
// 定义 data 属性(包含 getter 和 setter)
|
// 强制转换为 int16 连续数组
|
||||||
.def_property("data",
|
PyObject* py_array = PyArray_FROM_OTF(
|
||||||
// Getter:返回 NumPy 数组
|
pD.ptr(),
|
||||||
[](RetAudioFrame& self) {
|
NPY_INT16,
|
||||||
return py::array_t<short>(
|
NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST
|
||||||
{self.dataCount}, // 数组形状
|
);
|
||||||
{sizeof(short)}, // 步长
|
if (!py_array) {
|
||||||
self.data.get() // 数据指针(原始内存)
|
throw std::runtime_error("Failed to convert input to int16 array");
|
||||||
);
|
}
|
||||||
},
|
|
||||||
// Setter:从 NumPy 数组复制数据
|
|
||||||
[](RetAudioFrame& self, py::array_t<short> arr) {
|
|
||||||
auto buf = arr.request(); // 获取数组信息
|
|
||||||
self.data.reset(new short[buf.size]); // 重新分配内存
|
|
||||||
std::memcpy(
|
|
||||||
self.data.get(), // 目标指针
|
|
||||||
buf.ptr, // 源数据指针
|
|
||||||
buf.size * sizeof(short) // 数据大小
|
|
||||||
);
|
|
||||||
self.dataCount = buf.size; // 更新数据长度
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.def_readwrite("dataCount", &RetAudioFrame::dataCount)
|
|
||||||
.def_readwrite("sampleRate", &RetAudioFrame::sampleRate)
|
|
||||||
.def_readwrite("numChannels", &RetAudioFrame::numChannels)
|
|
||||||
.def_readwrite("channelIndex", &RetAudioFrame::channelIndex);
|
|
||||||
m.def("init", &init);
|
|
||||||
m.def("initRecv", &initRecv);
|
|
||||||
m.def("initSend", &initSend);
|
|
||||||
m.def("sendCustomAudioData", &sendCustomAudioData);
|
|
||||||
m.def("getSize", &getSize);
|
|
||||||
m.def("getData", &getData);
|
|
||||||
m.def("getNumpyData", &getNumpyData);
|
|
||||||
m.def("getListData", &getListData);
|
|
||||||
m.def("getDataCount", &getDataCount);
|
|
||||||
|
|
||||||
|
// 修复点:使用花括号初始化
|
||||||
|
py::object arr{py::handle<>(py_array)};
|
||||||
|
|
||||||
|
// 检查数据长度
|
||||||
|
PyArrayObject* npArray = reinterpret_cast<PyArrayObject*>(arr.ptr());
|
||||||
|
if (PyArray_SIZE(npArray) != static_cast<npy_intp>(dataLen)) {
|
||||||
|
Py_DECREF(py_array);
|
||||||
|
throw std::runtime_error("Array length does not match dataLen");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理数据...
|
||||||
|
void* dataPtr = PyArray_DATA(npArray);
|
||||||
|
int ret = RTCContext::instance().sendCustomAudioData(
|
||||||
|
destChannelIndex, dataPtr, sampleRate, channelNum, dataLen
|
||||||
|
);
|
||||||
|
Py_DECREF(py_array); // 释放临时数组
|
||||||
|
return ret;
|
||||||
|
} catch (...) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Invalid audio data");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_numpy() {
|
||||||
|
// 直接调用底层函数,绕过宏的问题
|
||||||
|
if (_import_array() < 0) {
|
||||||
|
PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import");
|
||||||
|
throw std::runtime_error("NumPy initialization failed");
|
||||||
|
}
|
||||||
|
std::cout << "NumPy API addr: " << PyArray_API << std::endl;
|
||||||
|
}
|
||||||
|
BOOST_PYTHON_MODULE(rtc_plugins) {
|
||||||
|
try {
|
||||||
|
init_numpy();
|
||||||
|
void** numpyApi = (void**)PyArray_API;
|
||||||
|
if (!numpyApi || !numpyApi[93]) { // 93是PyArray_SimpleNew的偏移量
|
||||||
|
std::cout << "NumPy API corrupt! Key functions missing." << std::endl;
|
||||||
|
PyErr_Print();
|
||||||
|
throw std::runtime_error("Invalid NumPy API state");
|
||||||
|
} else {
|
||||||
|
RTCContext::instance().setNumpyApi(numpyApi);
|
||||||
|
std::cout << "set numpyApi succ:" << numpyApi[93] << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
py::def("init", &init);
|
||||||
|
py::def("initRecv", &initRecv);
|
||||||
|
py::def("initSend", &initSend);
|
||||||
|
py::def("sendCustomAudioData", &sendCustomAudioData);
|
||||||
|
} catch (...) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Module initialization failed");
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,45 +0,0 @@
|
||||||
import rtc_plugins
|
|
||||||
import time
|
|
||||||
import sounddevice as sd
|
|
||||||
import numpy as np
|
|
||||||
import mmap
|
|
||||||
import os
|
|
||||||
from ctypes import c_int16
|
|
||||||
import struct
|
|
||||||
|
|
||||||
srcUserId = "srcUser12"
|
|
||||||
destUserId = "destUser12"
|
|
||||||
|
|
||||||
srcDisplayName = "srcDisplayName12"
|
|
||||||
destDisplayName = "destDisplayName12"
|
|
||||||
srcRoomId = "srcRoom12"
|
|
||||||
#destRoomId = "destRoomId12"
|
|
||||||
destRoomId = srcRoomId
|
|
||||||
srcChannelIndex = 46
|
|
||||||
destChannelIndex = 47
|
|
||||||
def my_callback_r(shmName, dataSize, dataCount, sampleRate, numChannels, channelIndex):
|
|
||||||
print(f"my_callback_r, dataSize:{dataSize}, dataCount:{dataCount}, sampleRate:{sampleRate}, numChannels:{numChannels}, channelIndex:{channelIndex}")
|
|
||||||
print(f"data:{shmName}")
|
|
||||||
print("after my_callback_r")
|
|
||||||
|
|
||||||
ret = rtc_plugins.init(destUserId, destDisplayName, destRoomId, my_callback_r)
|
|
||||||
if ret != 0:
|
|
||||||
print(f"init fail, ret:{ret}")
|
|
||||||
exit(1)
|
|
||||||
ret = rtc_plugins.initRecv(destRoomId, srcUserId, destChannelIndex)
|
|
||||||
if ret != 0:
|
|
||||||
print(f"initRecv fail, ret:{ret}")
|
|
||||||
exit(1)
|
|
||||||
#ret = rtc_plugins.initSend(destRoomId, srcRoomId, srcChannelIndex, 1)
|
|
||||||
#if ret != 0:
|
|
||||||
# print(f"initSend fail, ret:{ret}")
|
|
||||||
# exit(1)
|
|
||||||
|
|
||||||
sampleRate = 16000
|
|
||||||
while True:
|
|
||||||
frame = rtc_plugins.getListData()
|
|
||||||
sd.play(frame, sampleRate)
|
|
||||||
sd.wait()
|
|
||||||
print(f"get frame:{frame}")
|
|
||||||
|
|
||||||
time.sleep(0.005)
|
|
|
@ -1,51 +0,0 @@
|
||||||
import rtc_plugins
|
|
||||||
import time
|
|
||||||
import numpy as np
|
|
||||||
from scipy.io import wavfile
|
|
||||||
|
|
||||||
srcUserId = "srcUser12"
|
|
||||||
destUserId = "destUser12"
|
|
||||||
|
|
||||||
srcDisplayName = "srcDisplayName12"
|
|
||||||
destDisplayName = "destDisplayName12"
|
|
||||||
srcRoomId = "srcRoom12"
|
|
||||||
#destRoomId = "destRoomId12"
|
|
||||||
destRoomId = srcRoomId
|
|
||||||
srcChannelIndex = 46
|
|
||||||
destChannelIndex = 47
|
|
||||||
|
|
||||||
ret = rtc_plugins.init(srcUserId, srcDisplayName, srcRoomId)
|
|
||||||
if ret != 0:
|
|
||||||
print(f"init fail, ret:{ret}")
|
|
||||||
exit(1)
|
|
||||||
ret = rtc_plugins.initSend(srcRoomId, destRoomId, destChannelIndex, 1)
|
|
||||||
if ret != 0:
|
|
||||||
print(f"initSend fail, ret:{ret}")
|
|
||||||
exit(1)
|
|
||||||
#audioData = np.array([0, 1, -1, 0], dtype=np.int16)
|
|
||||||
sampleRate, audioData = wavfile.read("xusample1.wav")
|
|
||||||
print(f"sampleRate:{sampleRate} HZ")
|
|
||||||
print(f"shape:{audioData.shape}")
|
|
||||||
print(f"type:{audioData.dtype}")
|
|
||||||
if audioData.dtype != np.int16:
|
|
||||||
audioData = (audioData * 32767).astype(np.int16)
|
|
||||||
ret = rtc_plugins.sendCustomAudioData(destChannelIndex, audioData, sampleRate, 1, len(audioData))
|
|
||||||
if ret != 0:
|
|
||||||
print(f"send fail, ret:{ret}")
|
|
||||||
print("send succ")
|
|
||||||
ret = rtc_plugins.initRecv(srcRoomId, srcUserId, srcChannelIndex)
|
|
||||||
if ret != 0:
|
|
||||||
print(f"initRecv fail, ret:{ret}")
|
|
||||||
exit(1)
|
|
||||||
for i in range(100):
|
|
||||||
ret = rtc_plugins.sendCustomAudioData(destChannelIndex, audioData, sampleRate, 1, len(audioData))
|
|
||||||
if ret != 0:
|
|
||||||
print(f"send fail, ret:{ret}")
|
|
||||||
|
|
||||||
#size = rtc_plugins.getSize()
|
|
||||||
#print(f"data size:{size}")
|
|
||||||
#frame = rtc_plugins.getListData()
|
|
||||||
#print(f"get frame:{frame}")
|
|
||||||
#dataCount = rtc_plugins.getDataCount()
|
|
||||||
#print(f"data count:{dataCount}")
|
|
||||||
time.sleep(3)
|
|
|
@ -17,7 +17,7 @@ ret = rtc_plugins.init(srcUserId, srcDisplayName, srcRoomId, my_callback)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
print(f"init fail, ret:{ret}")
|
print(f"init fail, ret:{ret}")
|
||||||
exit(1)
|
exit(1)
|
||||||
ret = rtc_plugins.initSend(srcRoomId, destRoomId, destChannelIndex)
|
ret = rtc_plugins.initSend(srcRoomId, destRoomId, destChannelIndex, 1)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
print(f"initSend fail, ret:{ret}")
|
print(f"initSend fail, ret:{ret}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
|
@ -4,7 +4,6 @@ import numpy as np
|
||||||
import mmap
|
import mmap
|
||||||
import os
|
import os
|
||||||
from ctypes import c_int16
|
from ctypes import c_int16
|
||||||
import struct
|
|
||||||
|
|
||||||
srcUserId = "srcUser12"
|
srcUserId = "srcUser12"
|
||||||
destUserId = "destUser12"
|
destUserId = "destUser12"
|
||||||
|
@ -16,12 +15,11 @@ srcRoomId = "srcRoom12"
|
||||||
destRoomId = srcRoomId
|
destRoomId = srcRoomId
|
||||||
srcChannelIndex = 46
|
srcChannelIndex = 46
|
||||||
destChannelIndex = 47
|
destChannelIndex = 47
|
||||||
def my_callback_r(shmName, dataSize, dataCount, sampleRate, numChannels, channelIndex):
|
def my_callback(shmName, dataSize, dataCount, sampleRate, numChannels, channelIndex):
|
||||||
print(f"my_callback_r, dataSize:{dataSize}, dataCount:{dataCount}, sampleRate:{sampleRate}, numChannels:{numChannels}, channelIndex:{channelIndex}")
|
print(f"dataSize:{dataSize}, dataCount:{dataCount}, sampleRate:{sampleRate}, numChannels:{numChannels}, channelIndex:{channelIndex}")
|
||||||
print(f"data:{shmName}")
|
print(f"data:{shmName}")
|
||||||
print("after my_callback_r")
|
|
||||||
|
|
||||||
ret = rtc_plugins.init(destUserId, destDisplayName, destRoomId, my_callback_r)
|
ret = rtc_plugins.init(destUserId, destDisplayName, destRoomId, my_callback)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
print(f"init fail, ret:{ret}")
|
print(f"init fail, ret:{ret}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -36,17 +34,10 @@ if ret != 0:
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
print("recv")
|
print("recv")
|
||||||
audioData = np.array([0, 1, -1, 0], dtype=np.int16)
|
#audioData = np.array([0, 1, -1, 0], dtype=np.int16)
|
||||||
ret = rtc_plugins.sendCustomAudioData(srcChannelIndex, audioData, 48000, 1, len(audioData))
|
#ret = rtc_plugins.sendCustomAudioData(srcChannelIndex, audioData, 48000, 1, len(audioData))
|
||||||
if ret != 0:
|
#if ret != 0:
|
||||||
print(f"resend fail, ret:{ret}")
|
# print(f"resend fail, ret:{ret}")
|
||||||
else:
|
#else:
|
||||||
print("resend succ")
|
# print("resend succ")
|
||||||
size = rtc_plugins.getSize()
|
time.sleep(30)
|
||||||
print(f"data size:{size}")
|
|
||||||
#frame = rtc_plugins.getNumpyData()
|
|
||||||
frame = rtc_plugins.getListData()
|
|
||||||
print(f"get frame:{frame}")
|
|
||||||
dataCount = rtc_plugins.getDataCount()
|
|
||||||
print(f"data count:{dataCount}")
|
|
||||||
time.sleep(0.005)
|
|
||||||
|
|
|
@ -12,10 +12,8 @@ srcRoomId = "srcRoom12"
|
||||||
destRoomId = srcRoomId
|
destRoomId = srcRoomId
|
||||||
srcChannelIndex = 46
|
srcChannelIndex = 46
|
||||||
destChannelIndex = 47
|
destChannelIndex = 47
|
||||||
def my_callback(shmName, dataSize, dataCount, sampleRate, numChannels, channelIndex):
|
def my_callback(npData, dataCount, sampleRate, numChannels, channelIndex):
|
||||||
print(f"my_callback, dataSize:{dataSize}, dataCount:{dataCount}, sampleRate:{sampleRate}, numChannels:{numChannels}, channelIndex:{channelIndex}")
|
print(f"dataCount:{dataCount}, sampleRate:{sampleRate}, numChannels:{numChannels}, channelIndex:{channelIndex}")
|
||||||
print(f"data:{shmName}")
|
|
||||||
print("after my_callback_r")
|
|
||||||
ret = rtc_plugins.init(srcUserId, srcDisplayName, srcRoomId, my_callback)
|
ret = rtc_plugins.init(srcUserId, srcDisplayName, srcRoomId, my_callback)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
print(f"init fail, ret:{ret}")
|
print(f"init fail, ret:{ret}")
|
||||||
|
@ -36,11 +34,4 @@ for i in range(100):
|
||||||
ret = rtc_plugins.sendCustomAudioData(destChannelIndex, audioData, 48000, 1, len(audioData))
|
ret = rtc_plugins.sendCustomAudioData(destChannelIndex, audioData, 48000, 1, len(audioData))
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
print(f"send fail, ret:{ret}")
|
print(f"send fail, ret:{ret}")
|
||||||
|
time.sleep(30)
|
||||||
size = rtc_plugins.getSize()
|
|
||||||
print(f"data size:{size}")
|
|
||||||
frame = rtc_plugins.getListData()
|
|
||||||
print(f"get frame:{frame}")
|
|
||||||
dataCount = rtc_plugins.getDataCount()
|
|
||||||
print(f"data count:{dataCount}")
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "RTCContext.h"
|
#include "RTCContext.h"
|
||||||
|
#define GIL
|
||||||
|
|
||||||
void RTCContext::onRoom(uint32_t typeId, RTCENGINE_NAMESPACE::MRTCRoomInfo& roomInfo) {
|
void RTCContext::onRoom(uint32_t typeId, RTCENGINE_NAMESPACE::MRTCRoomInfo& roomInfo) {
|
||||||
//std::cout << "RTCContext::onRoom():" << roomInfo.roomId << "," << roomInfo.displayName << "," << roomInfo.userId << "," << roomInfo.message;
|
//std::cout << "RTCContext::onRoom():" << roomInfo.roomId << "," << roomInfo.displayName << "," << roomInfo.userId << "," << roomInfo.message;
|
||||||
|
@ -6,31 +7,30 @@ void RTCContext::onRoom(uint32_t typeId, RTCENGINE_NAMESPACE::MRTCRoomInfo& room
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
isOnRoom_ = true;
|
isOnRoom_ = true;
|
||||||
}
|
}
|
||||||
void RTCContext::onConsumer(uint32_t msgId, const char* roomId, const char* peerId, RTCENGINE_NAMESPACE::MRTCConsumerInfo& consumerInfo) {
|
void RTCContext::onConsumer(uint32_t msgId, const char* roomId, const char* peerId,
|
||||||
//std::cout << "RTCContext::onConsumer()" << std::endl;
|
RTCENGINE_NAMESPACE::MRTCConsumerInfo& consumerInfo) {
|
||||||
std::cout << "RTCContext::onConsumer():msgId:" << msgId << ", roomId:" << consumerInfo.roomId << ", displayName:"
|
std::cout << "RTCContext::onConsumer():" << consumerInfo.roomId << "," << consumerInfo.displayName << ","
|
||||||
<< consumerInfo.displayName << ", channelIndex" << (int)consumerInfo.channelIndex;
|
<< consumerInfo.channelIndex << std::endl;
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
if (isRecv_) {
|
||||||
isOnConsumer_ = true;
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
std::cout << "registerSoundLevelListener" << std::endl;
|
std::cout << "registerSoundLevelListener" << std::endl;
|
||||||
int16_t ret1 = rtcEngine_->registerSoundLevelListener(mrtc::TYPE_AUDIO_SOURCE_CUSTOM, roomId,
|
int16_t ret1 = rtcEngine_->registerSoundLevelListener(mrtc::TYPE_AUDIO_SOURCE_CUSTOM, consumerInfo.roomId,
|
||||||
peerId, consumerInfo.channelIndex, this);
|
peerId, consumerInfo.channelIndex, this);
|
||||||
if (0 != ret1)
|
if (0 != ret1) {
|
||||||
{
|
std::cout << "RTCContext::instance().registerSoundLevelListener() inUser failed, ret:" << ret1;
|
||||||
std::cout << "RTCContext::instance().registerSoundLevelListener() inUser failed, ret:" << ret1;
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "muteAudio" << std::endl;
|
std::cout << "muteAudio" << std::endl;
|
||||||
int16_t ret2 = rtcEngine_->muteAudio(roomId, peerId, mrtc::TYPE_AUDIO_SOURCE_CUSTOM,
|
int16_t ret2 = rtcEngine_->muteAudio(consumerInfo.roomId, peerId, mrtc::TYPE_AUDIO_SOURCE_CUSTOM,
|
||||||
false, consumerInfo.channelIndex);
|
false, consumerInfo.channelIndex);
|
||||||
if (0 != ret2)
|
if (0 != ret2) {
|
||||||
{
|
std::cout << "RTCContext::instance().muteAudio() failed, ret:" << ret2;
|
||||||
std::cout << "RTCContext::instance().muteAudio() failed, ret:" << ret2;
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "init recv succ" << std::endl;
|
std::cout << "init recv succ" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void RTCContext::onRender(const char* roomId, const char* peerId,
|
void RTCContext::onRender(const char* roomId, const char* peerId,
|
||||||
RTCENGINE_NAMESPACE::MRTCVideoSourceType sourceType, const RTCENGINE_NAMESPACE::MRTCVideoFrame& videoFrame) {
|
RTCENGINE_NAMESPACE::MRTCVideoSourceType sourceType, const RTCENGINE_NAMESPACE::MRTCVideoFrame& videoFrame) {
|
||||||
|
@ -45,7 +45,6 @@ void RTCContext::onCallBackMessage(uint32_t msgId, const char* msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "RTCContext::onCallBackMessage(), msgId:" << msgId << ", msg:" << msg << std::endl;
|
std::cout << "RTCContext::onCallBackMessage(), msgId:" << msgId << ", msg:" << msg << std::endl;
|
||||||
//std::cout << "RTCContext::onCallBackMessage()" << std::endl;
|
|
||||||
}
|
}
|
||||||
void RTCContext::onCallBackCustomData(RTCENGINE_NAMESPACE::MRTCCustomDataObject object) {
|
void RTCContext::onCallBackCustomData(RTCENGINE_NAMESPACE::MRTCCustomDataObject object) {
|
||||||
//std::cout << "RTCContext::onCallBackCustomData(), obj:" << object.peerId << "," << object.data << "," << object.data_length;
|
//std::cout << "RTCContext::onCallBackCustomData(), obj:" << object.peerId << "," << object.data << "," << object.data_length;
|
||||||
|
@ -57,25 +56,151 @@ void RTCContext::onSoundLevelUpdate(const char* roomId, const char* peerId, uint
|
||||||
std::cout << "RTCContext::onSoundLevelUpdate()" << std::endl;
|
std::cout << "RTCContext::onSoundLevelUpdate()" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printTimestamp() {
|
|
||||||
// 获取系统当前时间点
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
|
|
||||||
// 转换为时间戳(秒 + 毫秒)
|
|
||||||
auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
|
||||||
now.time_since_epoch()).count();
|
|
||||||
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
now.time_since_epoch()).count() % 1000;
|
|
||||||
|
|
||||||
// 转换为本地时间(可读格式)
|
|
||||||
std::time_t time = std::chrono::system_clock::to_time_t(now);
|
|
||||||
std::cout << "Timestamp: " << timestamp << "." << milliseconds << std::endl;
|
|
||||||
}
|
|
||||||
void RTCContext::onAudioProcess(const char* roomId, const char* peerId,
|
void RTCContext::onAudioProcess(const char* roomId, const char* peerId,
|
||||||
mrtc::MRTCAudioFrame& audioFrame,
|
mrtc::MRTCAudioFrame& audioFrame,
|
||||||
mrtc::MRTCAudioSourceType audioSourceType)
|
mrtc::MRTCAudioSourceType audioSourceType)
|
||||||
{
|
{
|
||||||
setData(audioFrame);
|
namespace np = boost::python::numpy;
|
||||||
|
namespace py = boost::python;
|
||||||
|
Py_Initialize(); // 初始化 Python
|
||||||
|
np::initialize();
|
||||||
|
std::cout << "=== 开始音频处理 ===" << std::endl;
|
||||||
|
std::cout << "audioFrame:" << audioFrame.dataCount << "," << audioFrame.sampleRate << "," <<
|
||||||
|
audioFrame.numChannels << "," << audioFrame.channelIndex << std::endl;
|
||||||
|
|
||||||
|
// 1. 获取GIL
|
||||||
|
std::cout << "[1] 获取GIL锁..." << std::endl;
|
||||||
|
#ifdef GIL
|
||||||
|
PyGILState_STATE gstate = PyGILState_Ensure();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2. 输入参数校验
|
||||||
|
std::cout << "[2] 检查输入参数..." << std::endl;
|
||||||
|
std::cout << " dataCount: " << audioFrame.dataCount
|
||||||
|
<< " (max: " << std::numeric_limits<npy_intp>::max() << ")" << std::endl;
|
||||||
|
|
||||||
|
if (!audioFrame.data || audioFrame.dataCount <= 0) {
|
||||||
|
std::cout << "[ERROR] 无效音频数据指针或长度" << std::endl;
|
||||||
|
throw std::invalid_argument("Invalid audio frame data");
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t data_size = audioFrame.dataCount * sizeof(int16_t);
|
||||||
|
|
||||||
|
std::cout << "step1" << std::endl;
|
||||||
|
namespace py = boost::python;
|
||||||
|
namespace np = boost::python::numpy;
|
||||||
|
npy_intp shape[1] = { static_cast<npy_intp>(audioFrame.dataCount) };
|
||||||
|
std::cout << "step2" << std::endl;
|
||||||
|
|
||||||
|
// 7. 执行回调
|
||||||
|
if (!pyCallback_.is_none()) {
|
||||||
|
std::cout << "[7] 准备执行Python回调..." << std::endl;
|
||||||
|
// 增加引用计数防止提前释放
|
||||||
|
//Py_INCREF(pyCallback_.ptr());
|
||||||
|
try {
|
||||||
|
|
||||||
|
//PyGILState_STATE gstate = PyGILState_Ensure();
|
||||||
|
std::cout << "data:" << audioFrame.data << std::endl;
|
||||||
|
std::cout << "当前线程是否持有 GIL: " << PyGILState_Check() << std::endl;
|
||||||
|
np::dtype dtype = np::dtype::get_builtin<int16_t>();
|
||||||
|
std::cout << "init dtype" << std::endl;
|
||||||
|
if (!Py_IsInitialized()) {
|
||||||
|
std::cerr << "Python 解释器未初始化!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
py::object str_repr = py::str(dtype);
|
||||||
|
std::cout << "str_repr" << std::endl;
|
||||||
|
if(str_repr.ptr() != Py_None) {
|
||||||
|
std::cout << "str_repr is not null" << std::endl;
|
||||||
|
std::string dtype_str = py::extract<std::string>(str_repr);
|
||||||
|
std::cout << "数据类型: " << dtype_str << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "数据类型: None" << std::endl;
|
||||||
|
}
|
||||||
|
} catch (const py::error_already_set&) {
|
||||||
|
std::cout<< "数据类型转换错误" << std::endl;
|
||||||
|
PyErr_Clear();
|
||||||
|
}
|
||||||
|
std::cout << "数据形状: " << shape[0] << std::endl;
|
||||||
|
np::ndarray audioArray = np::from_data(
|
||||||
|
audioFrame.data, // 数据指针
|
||||||
|
dtype, // 数据类型 (int16)
|
||||||
|
py::make_tuple(shape[0]), // 形状 (1D)
|
||||||
|
py::make_tuple(sizeof(int16_t)), // 步长
|
||||||
|
py::object() // 所有者(Python管理)
|
||||||
|
);
|
||||||
|
std::cout << " 数据拷贝完成" << std::endl;
|
||||||
|
pyCallback_(
|
||||||
|
audioArray, // numpy 数组
|
||||||
|
data_size, // 数据大小
|
||||||
|
audioFrame.dataCount,
|
||||||
|
audioFrame.sampleRate,
|
||||||
|
audioFrame.numChannels,
|
||||||
|
audioFrame.channelIndex
|
||||||
|
);
|
||||||
|
std::cout << " after callback" << std::endl;
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
PyObject *type, *value, *traceback;
|
||||||
|
PyErr_Fetch(&type, &value, &traceback);
|
||||||
|
if (value) {
|
||||||
|
PyObject* str = PyObject_Str(value);
|
||||||
|
if (str) {
|
||||||
|
std::cerr << "Python Error: " << PyUnicode_AsUTF8(str) << std::endl;
|
||||||
|
Py_DECREF(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_XDECREF(type);
|
||||||
|
Py_XDECREF(value);
|
||||||
|
Py_XDECREF(traceback);
|
||||||
|
//PyErr_Print();
|
||||||
|
throw std::runtime_error("Python callback error");
|
||||||
|
}
|
||||||
|
std::cout << " 回调执行成功" << std::endl;
|
||||||
|
|
||||||
|
} catch (const py::error_already_set& e) {
|
||||||
|
std::cerr << "[PYTHON ERROR] ";
|
||||||
|
PyErr_Print(); // 自动打印到stderr
|
||||||
|
// 可选:获取更详细的错误信息
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
PyObject *type, *value, *traceback;
|
||||||
|
PyErr_Fetch(&type, &value, &traceback);
|
||||||
|
std::cerr << "Details: "
|
||||||
|
<< PyUnicode_AsUTF8(PyObject_Str(value)) << std::endl;
|
||||||
|
PyErr_Restore(type, value, traceback);
|
||||||
|
}
|
||||||
|
//Py_DECREF(pyCallback_.ptr());
|
||||||
|
} catch (...) {
|
||||||
|
std::cout << "[ERROR] 回调执行失败" << std::endl;
|
||||||
|
|
||||||
|
//Py_DECREF(pyCallback_.ptr());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
//Py_DECREF(pyCallback_.ptr());
|
||||||
|
} else {
|
||||||
|
std::cout << "[7] 无回调函数设置" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 释放资源
|
||||||
|
std::cout << "[8] 释放共享内存资源..." << std::endl;
|
||||||
|
|
||||||
|
std::cout << "[9] 释放GIL..." << std::endl;
|
||||||
|
#ifdef GIL
|
||||||
|
PyGILState_Release(gstate);
|
||||||
|
#endif
|
||||||
|
std::cout << "=== 音频处理完成 ===" << std::endl;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cout << "[EXCEPTION] 异常捕获: " << e.what() << std::endl;
|
||||||
|
#ifdef GIL
|
||||||
|
PyGILState_Release(gstate);
|
||||||
|
#endif
|
||||||
|
std::cerr << "Audio process error: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
#ifdef GIL
|
||||||
|
PyGILState_Release(gstate);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCContext::onProducer(uint32_t msgId, mrtc::MRTCProducerInfo& info)
|
void RTCContext::onProducer(uint32_t msgId, mrtc::MRTCProducerInfo& info)
|
||||||
|
@ -85,6 +210,12 @@ void RTCContext::onProducer(uint32_t msgId, mrtc::MRTCProducerInfo& info)
|
||||||
}
|
}
|
||||||
bool RTCContext::init(const char* selfUserId, const char* selfDisplayName, const char* selfRoomId)
|
bool RTCContext::init(const char* selfUserId, const char* selfDisplayName, const char* selfRoomId)
|
||||||
{
|
{
|
||||||
|
std::cout << "init, numpyApi_:" << numpyApi_[93] << std::endl;
|
||||||
|
if (!numpyApi_ || !numpyApi_[93]) { // 93是PyArray_SimpleNew的偏移量
|
||||||
|
std::cout << "numpyApi_ is null in init" << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "numpyApi_ is not null in init" << std::endl;
|
||||||
|
}
|
||||||
mrtc::IMRTCEngineFactory * rtcFactory = mrtc::getMRTCEngineFactory();
|
mrtc::IMRTCEngineFactory * rtcFactory = mrtc::getMRTCEngineFactory();
|
||||||
if (!rtcFactory)
|
if (!rtcFactory)
|
||||||
{
|
{
|
||||||
|
@ -130,21 +261,20 @@ bool RTCContext::init(const char* selfUserId, const char* selfDisplayName, const
|
||||||
std::cout << "RTCContext::instance().registerListener() failed" << std::endl;
|
std::cout << "RTCContext::instance().registerListener() failed" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//namespace py = boost::python;
|
||||||
|
//namespace np = boost::python::numpy;
|
||||||
|
//Py_Initialize(); // 初始化 Python
|
||||||
|
//np::initialize();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool RTCContext::initRecv(const char* destRoomId, const char* srcUserId, const int16_t destChannelIndex)
|
bool RTCContext::initRecv(const char* destRoomId, const char* srcUserId, const int16_t destChannelIndex)
|
||||||
{
|
{
|
||||||
while (!isOnConsumer_)
|
isRecv_ = true;
|
||||||
{
|
|
||||||
std::cout << "wait for OnConsumer" << std::endl;
|
|
||||||
sleep(3);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
bool RTCContext::initSend(const char* srcRoomId, const char* destRoomId, const int16_t destChannelIndex,
|
bool RTCContext::initSend(const char* srcRoomId, const char* destRoomId, const int16_t destChannelIndex, uint8_t channelNum)
|
||||||
const uint8_t channelNum)
|
|
||||||
{
|
{
|
||||||
while (!isOnRoom_)
|
while (!isOnRoom_)
|
||||||
{
|
{
|
||||||
|
@ -165,11 +295,11 @@ bool RTCContext::initSend(const char* srcRoomId, const char* destRoomId, const i
|
||||||
}
|
}
|
||||||
|
|
||||||
mrtc::MRTCAudioOption option;
|
mrtc::MRTCAudioOption option;
|
||||||
|
option.channel = channelNum;
|
||||||
if (std::string(srcRoomId) != std::string(destRoomId)) {
|
if (std::string(srcRoomId) != std::string(destRoomId)) {
|
||||||
strcpy(option.dstRoomId, destRoomId);
|
strcpy(option.dstRoomId, destRoomId);
|
||||||
}
|
}
|
||||||
option.channelIndex = destChannelIndex;
|
option.channelIndex = destChannelIndex;
|
||||||
option.channel = channelNum;
|
|
||||||
std::cout << "startCustomAudio" << std::endl;
|
std::cout << "startCustomAudio" << std::endl;
|
||||||
int16_t ret2 = rtcEngine_->startCustomAudio(option);
|
int16_t ret2 = rtcEngine_->startCustomAudio(option);
|
||||||
if (ret2 != 0)
|
if (ret2 != 0)
|
||||||
|
@ -180,12 +310,16 @@ bool RTCContext::initSend(const char* srcRoomId, const char* destRoomId, const i
|
||||||
std::cout << "init send succ" << std::endl;
|
std::cout << "init send succ" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool RTCContext::initGIL() {
|
||||||
|
isGIL_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
void RTCContext::destorySend(const int16_t selfChannelIndex)
|
void RTCContext::destorySend(const int16_t selfChannelIndex)
|
||||||
{
|
{
|
||||||
rtcEngine_->stopCustomAudio(selfChannelIndex);
|
rtcEngine_->stopCustomAudio(selfChannelIndex);
|
||||||
}
|
}
|
||||||
int16_t RTCContext::sendAudioData(uint8_t channelIndex, const void* pData, int32_t nSampleRate, uint64_t nNumberOfChannels, uint64_t dataLength)
|
int16_t RTCContext::sendAudioData(uint8_t channelIndex, const void* pData, int32_t nSampleRate, uint64_t nNumberOfChannels,
|
||||||
|
uint64_t dataLength)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
if (pData_)
|
if (pData_)
|
||||||
|
@ -207,7 +341,8 @@ int16_t RTCContext::sendCustomAudioData(const int16_t channelIndex, void* custom
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
std::cout << "customData addr is:" << customData << std::endl;
|
std::cout << "customData addr is:" << customData << std::endl;
|
||||||
return rtcEngine_->sendCustomAudioData(channelIndex, customData, sampleRate, channelNum, dataLen);
|
return rtcEngine_->sendCustomAudioData(channelIndex, customData, sampleRate,
|
||||||
|
channelNum, dataLen);
|
||||||
}
|
}
|
||||||
mrtc::IMRTCEngine* RTCContext::getRtcEngine() const
|
mrtc::IMRTCEngine* RTCContext::getRtcEngine() const
|
||||||
{
|
{
|
||||||
|
@ -224,74 +359,12 @@ void RTCContext::setpData(void* pData)
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
pData_ = pData;
|
pData_ = pData;
|
||||||
}
|
}
|
||||||
|
void RTCContext::setPyCallback(boost::python::object callback) {
|
||||||
void RTCContext::setData(const mrtc::MRTCAudioFrame& frame) {
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
std::lock_guard<std::mutex> lock(dataMutex_);
|
pyCallback_ = callback;
|
||||||
if (dataSize_ == totalSize_) {
|
|
||||||
bottom_ = (bottom_ + 1) % totalSize_;
|
|
||||||
dataSize_--;
|
|
||||||
}
|
|
||||||
RetAudioFrame newFrame;
|
|
||||||
newFrame.dataCount = frame.dataCount;
|
|
||||||
newFrame.sampleRate = frame.sampleRate;
|
|
||||||
newFrame.numChannels = frame.numChannels;
|
|
||||||
newFrame.channelIndex = frame.channelIndex;
|
|
||||||
newFrame.data = std::make_unique<int16_t[]>(frame.dataCount);
|
|
||||||
std::memcpy(newFrame.data.get(), frame.data, frame.dataCount* sizeof(int16_t));
|
|
||||||
data_[head_] = std::move(newFrame);
|
|
||||||
head_ = (head_ + 1) % totalSize_;
|
|
||||||
dataSize_++;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
void RTCContext::setNumpyApi(void **numpyApi) {
|
||||||
RetAudioFrame RTCContext::getData() {
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
//std::lock_guard<std::mutex> lock(dataMutex_);
|
numpyApi_ = numpyApi;
|
||||||
if (dataSize_ > 0) {
|
std::cout << "setNupyApi, numpyApi_:" << numpyApi_[93] << std::endl;
|
||||||
RetAudioFrame frame = std::move(data_[bottom_]); // 移动而非拷贝
|
|
||||||
bottom_ = (bottom_ + 1) % totalSize_;
|
|
||||||
dataSize_--;
|
|
||||||
return frame; // 返回值优化(RVO)会生效
|
|
||||||
}
|
|
||||||
return {}; // 返回空对象
|
|
||||||
}
|
}
|
||||||
py::array_t<int16_t> RTCContext::getNumpyData() {
|
|
||||||
std::cout << "step1" << std::endl;
|
|
||||||
std::lock_guard<std::mutex> lock(dataMutex_);
|
|
||||||
RetAudioFrame frame = getData();
|
|
||||||
std::cout << "step2" << std::endl;
|
|
||||||
int16_t* dataPtr = frame.data.get(); // 你的数据指针
|
|
||||||
std::cout << "step3" << std::endl;
|
|
||||||
size_t length = frame.dataCount; // 数据长度
|
|
||||||
std::cout << "step4" << std::endl;
|
|
||||||
if (!dataPtr || length == 0) {
|
|
||||||
return py::array_t<int16_t>({0}); // 返回空数组
|
|
||||||
}
|
|
||||||
|
|
||||||
// 直接构造 pybind11 的 NumPy 数组(自动管理内存)
|
|
||||||
py::array_t<int16_t> result({static_cast<py::ssize_t>(length)});
|
|
||||||
auto buf = result.mutable_unchecked();
|
|
||||||
for (size_t i = 0; i < length; i++) {
|
|
||||||
buf[i] = dataPtr[i];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
py::list RTCContext::getListData() {
|
|
||||||
std::lock_guard<std::mutex> lock(dataMutex_);
|
|
||||||
RetAudioFrame frame = getData();
|
|
||||||
py::list result;
|
|
||||||
if (frame.data) {
|
|
||||||
for (int i = 0; i < frame.dataCount; i++) {
|
|
||||||
result.append(frame.data.get()[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
int16_t RTCContext::getDataCount() {
|
|
||||||
std::lock_guard<std::mutex> lock(dataMutex_);
|
|
||||||
RetAudioFrame frame = getData();
|
|
||||||
return frame.dataCount;
|
|
||||||
}
|
|
||||||
int16_t RTCContext::getSize() {
|
|
||||||
std::lock_guard<std::mutex> lock(dataMutex_);
|
|
||||||
return dataSize_;
|
|
||||||
}
|
|
|
@ -1,6 +1,9 @@
|
||||||
// RTCContext.h
|
// RTCContext.h
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
//#include "numpyConfig.h"
|
||||||
|
#include "numpyStub.h"
|
||||||
|
|
||||||
#include "IMRTCEngine.hpp"
|
#include "IMRTCEngine.hpp"
|
||||||
#include "MRTCEngineDefine.hpp"
|
#include "MRTCEngineDefine.hpp"
|
||||||
#include "IMRTCEngineFactory.hpp"
|
#include "IMRTCEngineFactory.hpp"
|
||||||
|
@ -15,26 +18,23 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
// pybind11 头文件
|
#include <boost/python.hpp>
|
||||||
#include <pybind11/pybind11.h>
|
#include <boost/python/detail/wrap_python.hpp>
|
||||||
#include <pybind11/numpy.h>
|
#include <boost/python/numpy.hpp>
|
||||||
#include <pybind11/stl.h>
|
#include <boost/python/detail/prefix.hpp>
|
||||||
namespace py = pybind11;
|
#include <boost/python/module.hpp>
|
||||||
//#include <numpy/arrayobject.h>
|
#include <boost/python/def.hpp>
|
||||||
|
#include <numpy/ndarrayobject.h>
|
||||||
|
#include <numpy/arrayobject.h>
|
||||||
|
|
||||||
|
|
||||||
|
// 必须声明外部变量(关键!)
|
||||||
|
//#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
#define ENV_PRODUCT
|
#define ENV_PRODUCT
|
||||||
//#define SEND_MODE
|
//#define SEND_MODE
|
||||||
|
|
||||||
// 音频数据帧
|
|
||||||
struct RetAudioFrame
|
|
||||||
{
|
|
||||||
std::unique_ptr<int16_t[]> data;
|
|
||||||
int dataCount = 0;
|
|
||||||
int sampleRate = 48000;
|
|
||||||
int numChannels = 1;
|
|
||||||
int channelIndex = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RTCContext :
|
class RTCContext :
|
||||||
public RTCENGINE_NAMESPACE::IMRTCRoomCallBack,
|
public RTCENGINE_NAMESPACE::IMRTCRoomCallBack,
|
||||||
public RTCENGINE_NAMESPACE::IMRTCConsumerCallBack,
|
public RTCENGINE_NAMESPACE::IMRTCConsumerCallBack,
|
||||||
|
@ -60,22 +60,37 @@ public:
|
||||||
static RTCContext instance;
|
static RTCContext instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
static void** numpy_api() {
|
||||||
|
static void** api = [](){
|
||||||
|
// 强制初始化NumPy
|
||||||
|
if (_import_array() < 0) {
|
||||||
|
PyErr_Print();
|
||||||
|
throw std::runtime_error("NumPy initialization failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void** ptr = reinterpret_cast<void**>(RTC_PLUGINS_ARRAY_API);
|
||||||
|
std::cout << "ptr:" << ptr << std::endl;
|
||||||
|
if (!ptr || !ptr[93]) {
|
||||||
|
std::cerr << "NumPy API corrupt! Expected at 93: "
|
||||||
|
<< (ptr ? (void*)ptr[93] : nullptr) << std::endl;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}();
|
||||||
|
return api;
|
||||||
|
}
|
||||||
RTCContext(const RTCContext&) = delete;
|
RTCContext(const RTCContext&) = delete;
|
||||||
RTCContext& operator=(const RTCContext&) = delete;
|
RTCContext& operator=(const RTCContext&) = delete;
|
||||||
mrtc::IMRTCEngine* getRtcEngine() const;
|
mrtc::IMRTCEngine* getRtcEngine() const;
|
||||||
bool init(const char* selfUserId, const char* selfDisplayName, const char* selfRoomId);
|
bool init(const char* selfUserId, const char* selfDisplayName, const char* selfRoomId);
|
||||||
bool initRecv(const char* destRoomId, const char* srcUserId, const int16_t destChannelIndex);
|
bool initRecv(const char* destRoomId, const char* srcUserId, const int16_t destChannelIndex);
|
||||||
bool initSend(const char* srcRoomId, const char* destRoomId, const int16_t destChannelIndex, const uint8_t channelNum);
|
bool initSend(const char* srcRoomId, const char* destRoomId, const int16_t destChannelIndex, uint8_t channelNum);
|
||||||
int16_t getSize();
|
bool initGIL();
|
||||||
void setData(const mrtc::MRTCAudioFrame& frame);
|
|
||||||
RetAudioFrame getData();
|
|
||||||
py::array_t<int16_t> getNumpyData();
|
|
||||||
py::list getListData();
|
|
||||||
int16_t getDataCount();
|
|
||||||
|
|
||||||
void* getpData() const;
|
void* getpData() const;
|
||||||
void setpData(void* pData);
|
void setpData(void* pData);
|
||||||
|
void setPyCallback(boost::python::object callback);
|
||||||
|
void setNumpyApi(void** numpyApi);
|
||||||
|
|
||||||
int16_t sendAudioData(uint8_t channelIndex = 0, const void* pData = nullptr, int32_t nSampleRate = 48000, uint64_t nNumberOfChannels = 2, uint64_t dataLength = 0);
|
int16_t sendAudioData(uint8_t channelIndex = 0, const void* pData = nullptr, int32_t nSampleRate = 48000, uint64_t nNumberOfChannels = 2, uint64_t dataLength = 0);
|
||||||
int16_t sendCustomAudioData(const int16_t channelIndex, void* customData, int32_t sampleRate,
|
int16_t sendCustomAudioData(const int16_t channelIndex, void* customData, int32_t sampleRate,
|
||||||
|
@ -85,21 +100,17 @@ public:
|
||||||
private:
|
private:
|
||||||
RTCContext()
|
RTCContext()
|
||||||
{
|
{
|
||||||
data_.resize(totalSize_);
|
|
||||||
}
|
}
|
||||||
mutable std::mutex mutex_;
|
mutable std::mutex mutex_;
|
||||||
mrtc::IMRTCEngine * rtcEngine_ = nullptr;
|
mrtc::IMRTCEngine * rtcEngine_ = nullptr;
|
||||||
void* pData_ = nullptr;
|
void* pData_ = nullptr;
|
||||||
bool isOnRoom_ = false;
|
bool isOnRoom_ = false;
|
||||||
bool isOnConsumer_ = false;
|
bool isRecv_ = false;
|
||||||
bool isJoinMultiRoom_ = false;
|
bool isJoinMultiRoom_ = false;
|
||||||
bool isMultiRoom_ = false;
|
bool isMultiRoom_ = false;
|
||||||
std::vector<RetAudioFrame> data_;
|
bool isGIL_ = false;
|
||||||
mutable std::mutex dataMutex_;
|
boost::python::object pyCallback_;
|
||||||
const int16_t totalSize_ = 100;
|
void ** numpyApi_;
|
||||||
int16_t dataSize_ = 0;
|
|
||||||
int16_t bottom_ = 0;
|
|
||||||
int16_t head_= 0;
|
|
||||||
void onRoom(uint32_t typeId, RTCENGINE_NAMESPACE::MRTCRoomInfo& roomInfo);
|
void onRoom(uint32_t typeId, RTCENGINE_NAMESPACE::MRTCRoomInfo& roomInfo);
|
||||||
void onConsumer(uint32_t msgId, const char* roomId, const char* peerId, RTCENGINE_NAMESPACE::MRTCConsumerInfo& consumerInfo);
|
void onConsumer(uint32_t msgId, const char* roomId, const char* peerId, RTCENGINE_NAMESPACE::MRTCConsumerInfo& consumerInfo);
|
||||||
void onRender(const char* roomId, const char* peerId,
|
void onRender(const char* roomId, const char* peerId,
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// numpy_interface.h (新建)
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef IMPLEMENT_NUMPY_API
|
||||||
|
// 主模块实现路径
|
||||||
|
#define PY_ARRAY_UNIQUE_SYMBOL RTC_PLUGINS_ARRAY_API
|
||||||
|
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
||||||
|
#include <numpy/arrayobject.h>
|
||||||
|
#else
|
||||||
|
// 用户头文件路径
|
||||||
|
extern void* RTC_PLUGINS_ARRAY_API[]; // 严格匹配NumPy类型
|
||||||
|
#endif
|
Loading…
Reference in New Issue