2025-04-10 16:54:02 +08:00
|
|
|
|
// rtc_plugins.cpp
|
|
|
|
|
|
2025-04-10 18:14:30 +08:00
|
|
|
|
#define IMPLEMENT_NUMPY_API // 标记这是实现文件
|
|
|
|
|
#include "util/numpyStub.h"
|
|
|
|
|
#include "util/RTCContext.h"
|
2025-04-10 17:47:00 +08:00
|
|
|
|
|
|
|
|
|
// 提供转换接口
|
|
|
|
|
void** get_numpy_api() {
|
2025-04-10 18:04:09 +08:00
|
|
|
|
return (void**)RTC_PLUGINS_ARRAY_API;
|
2025-04-10 17:47:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-04-09 16:18:50 +08:00
|
|
|
|
|
2025-04-09 18:24:55 +08:00
|
|
|
|
namespace py = boost::python;
|
2025-04-09 16:18:50 +08:00
|
|
|
|
|
2025-04-09 16:51:49 +08:00
|
|
|
|
int init(const char* selfUserId, const char* selfDisplayName, const char* selfRoomId, boost::python::object callback) {
|
2025-04-10 16:40:29 +08:00
|
|
|
|
if (!PyArray_API) {
|
|
|
|
|
std::cout << "PyArray_API is null in outer init" << std::endl;
|
|
|
|
|
} else {
|
|
|
|
|
std::cout << "PyArray_API is not null in outer init" << std::endl;
|
|
|
|
|
}
|
2025-04-09 16:18:50 +08:00
|
|
|
|
RTCContext::instance().setPyCallback(callback);
|
|
|
|
|
bool res = RTCContext::instance().init(selfUserId, selfDisplayName, selfRoomId);
|
|
|
|
|
if (res) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int initRecv(const char* destRoomId, const char* srcUserId, const int16_t destChannelIndex) {
|
2025-04-10 16:40:29 +08:00
|
|
|
|
if (!PyArray_API) {
|
|
|
|
|
std::cout << "PyArray_API is null in outer initRecv" << std::endl;
|
|
|
|
|
} else {
|
|
|
|
|
std::cout << "PyArray_API is not null in outer initRecv" << std::endl;
|
|
|
|
|
}
|
2025-04-09 16:18:50 +08:00
|
|
|
|
bool res = RTCContext::instance().initRecv(destRoomId, srcUserId, destChannelIndex);
|
|
|
|
|
if (res) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2025-04-09 10:34:51 +08:00
|
|
|
|
}
|
2025-04-15 23:21:48 +08:00
|
|
|
|
int initSend(const char* srcRoomId, const char* destRoomId, const int16_t destChannelIndex, const int16_t channelNum) {
|
|
|
|
|
bool res = RTCContext::instance().initSend(srcRoomId, destRoomId, destChannelIndex, channelNum);
|
2025-04-09 16:18:50 +08:00
|
|
|
|
if (res) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-16 15:30:55 +08:00
|
|
|
|
int getSize() {
|
|
|
|
|
return RTCContext::instance().getSize();
|
|
|
|
|
}
|
2025-04-10 09:57:22 +08:00
|
|
|
|
|
2025-04-10 10:29:57 +08:00
|
|
|
|
py::object create_int16_array() {
|
|
|
|
|
// 1. 定义数组维度(1维,长度为 4)
|
|
|
|
|
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 // 数据指针
|
|
|
|
|
);
|
2025-04-10 10:00:50 +08:00
|
|
|
|
|
2025-04-10 10:29:57 +08:00
|
|
|
|
if (!py_array) {
|
|
|
|
|
throw std::runtime_error("Failed to create NumPy array");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 转换为 py::object(自动管理引用计数)
|
|
|
|
|
return py::object(py::handle<>(py_array));
|
|
|
|
|
}
|
2025-04-10 10:38:57 +08:00
|
|
|
|
|
2025-04-10 10:37:11 +08:00
|
|
|
|
int sendCustomAudioData(int16_t destChannelIndex, py::object pD,
|
|
|
|
|
int32_t sampleRate, uint64_t channelNum, uint64_t dataLen) {
|
|
|
|
|
try {
|
|
|
|
|
// 强制转换为 int16 连续数组
|
|
|
|
|
PyObject* py_array = PyArray_FROM_OTF(
|
|
|
|
|
pD.ptr(),
|
|
|
|
|
NPY_INT16,
|
|
|
|
|
NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST
|
|
|
|
|
);
|
|
|
|
|
if (!py_array) {
|
|
|
|
|
throw std::runtime_error("Failed to convert input to int16 array");
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 10:40:38 +08:00
|
|
|
|
// 修复点:使用花括号初始化
|
|
|
|
|
py::object arr{py::handle<>(py_array)};
|
2025-04-10 10:37:11 +08:00
|
|
|
|
|
|
|
|
|
// 检查数据长度
|
2025-04-10 10:38:57 +08:00
|
|
|
|
PyArrayObject* npArray = reinterpret_cast<PyArrayObject*>(arr.ptr());
|
2025-04-10 10:37:11 +08:00
|
|
|
|
if (PyArray_SIZE(npArray) != static_cast<npy_intp>(dataLen)) {
|
2025-04-10 10:40:38 +08:00
|
|
|
|
Py_DECREF(py_array);
|
2025-04-10 10:37:11 +08:00
|
|
|
|
throw std::runtime_error("Array length does not match dataLen");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理数据...
|
|
|
|
|
void* dataPtr = PyArray_DATA(npArray);
|
2025-04-10 10:40:38 +08:00
|
|
|
|
int ret = RTCContext::instance().sendCustomAudioData(
|
2025-04-10 10:37:11 +08:00
|
|
|
|
destChannelIndex, dataPtr, sampleRate, channelNum, dataLen
|
|
|
|
|
);
|
2025-04-10 10:40:38 +08:00
|
|
|
|
Py_DECREF(py_array); // 释放临时数组
|
|
|
|
|
return ret;
|
2025-04-10 10:37:11 +08:00
|
|
|
|
} catch (...) {
|
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "Invalid audio data");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-10 10:38:57 +08:00
|
|
|
|
|
2025-04-10 10:37:11 +08:00
|
|
|
|
/*
|
|
|
|
|
int sendCustomAudioData(const int16_t destChannelIndex, py::object pyData, int32_t sampleRate, uint64_t channelNum,
|
2025-04-09 16:18:50 +08:00
|
|
|
|
uint64_t dataLen) {
|
2025-04-10 09:21:12 +08:00
|
|
|
|
try {
|
2025-04-10 10:37:11 +08:00
|
|
|
|
//py::object pyData = create_int16_array();
|
2025-04-10 09:21:12 +08:00
|
|
|
|
std::cout << "step 1" << std::endl;
|
|
|
|
|
// 1. 检查输入有效性
|
|
|
|
|
if (pyData.ptr() == nullptr) {
|
|
|
|
|
throw std::runtime_error("Input data is NULL");
|
|
|
|
|
}
|
2025-04-10 09:07:06 +08:00
|
|
|
|
|
2025-04-10 09:21:12 +08:00
|
|
|
|
std::cout << "step 2" << std::endl;
|
2025-04-10 09:24:34 +08:00
|
|
|
|
std::cout << "pyData ptr is:" << pyData.ptr() << std::endl;
|
2025-04-10 10:03:58 +08:00
|
|
|
|
if (!pyData.ptr() || !Py_IsInitialized() || !PyObject_TypeCheck(pyData.ptr(), &PyBaseObject_Type)) {
|
2025-04-10 09:50:05 +08:00
|
|
|
|
throw std::runtime_error("Invalid Python object");
|
|
|
|
|
}
|
2025-04-10 09:57:22 +08:00
|
|
|
|
std::cout << "step 2" << std::endl;
|
2025-04-10 10:03:58 +08:00
|
|
|
|
// 2. 检查是否是 numpy 数组
|
2025-04-10 09:21:12 +08:00
|
|
|
|
if (!PyArray_Check(pyData.ptr())) {
|
2025-04-10 09:24:34 +08:00
|
|
|
|
std::cout << "input is notnumpy" << std::endl;
|
2025-04-10 09:21:12 +08:00
|
|
|
|
throw std::runtime_error("Input is not a numpy array");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "step 3" << std::endl;
|
|
|
|
|
// 3. 转换为 PyArrayObject
|
|
|
|
|
PyArrayObject* npArray = reinterpret_cast<PyArrayObject*>(pyData.ptr());
|
2025-04-10 09:07:06 +08:00
|
|
|
|
|
2025-04-10 09:21:12 +08:00
|
|
|
|
std::cout << "step 4" << std::endl;
|
|
|
|
|
// 4. 检查数据类型是否为 int16
|
|
|
|
|
if (PyArray_TYPE(npArray) != NPY_INT16) {
|
|
|
|
|
throw std::runtime_error("Array must be of type int16 (np.int16)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "step 5" << std::endl;
|
|
|
|
|
// 5. 检查数据是否连续
|
|
|
|
|
if (!PyArray_ISCONTIGUOUS(npArray)) {
|
|
|
|
|
throw std::runtime_error("Array must be contiguous in memory");
|
|
|
|
|
}
|
|
|
|
|
std::cout << "step 6" << std::endl;
|
|
|
|
|
|
|
|
|
|
// 6. 获取数据指针
|
|
|
|
|
void* dataPtr = PyArray_DATA(npArray);
|
|
|
|
|
if (dataPtr == nullptr) {
|
|
|
|
|
throw std::runtime_error("Invalid data pointer");
|
|
|
|
|
}
|
|
|
|
|
std::cout << "step 7" << std::endl;
|
|
|
|
|
return RTCContext::instance().sendCustomAudioData(destChannelIndex, dataPtr, sampleRate, channelNum, dataLen);
|
|
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
std::cout << "error:" << e.what() << std::endl;
|
|
|
|
|
return -1;
|
2025-04-10 09:07:06 +08:00
|
|
|
|
}
|
2025-04-09 16:18:50 +08:00
|
|
|
|
}
|
2025-04-10 10:37:11 +08:00
|
|
|
|
*/
|
2025-04-10 10:16:21 +08:00
|
|
|
|
|
2025-04-10 10:23:57 +08:00
|
|
|
|
|
|
|
|
|
void init_numpy() {
|
|
|
|
|
// 直接调用底层函数,绕过宏的问题
|
|
|
|
|
if (_import_array() < 0) {
|
2025-04-10 10:16:21 +08:00
|
|
|
|
PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import");
|
2025-04-10 10:23:57 +08:00
|
|
|
|
throw std::runtime_error("NumPy initialization failed");
|
2025-04-10 10:13:37 +08:00
|
|
|
|
}
|
2025-04-10 16:26:44 +08:00
|
|
|
|
std::cout << "NumPy API addr: " << PyArray_API << std::endl;
|
2025-04-10 10:13:37 +08:00
|
|
|
|
}
|
2025-04-09 16:18:50 +08:00
|
|
|
|
BOOST_PYTHON_MODULE(rtc_plugins) {
|
2025-04-10 10:23:57 +08:00
|
|
|
|
try {
|
|
|
|
|
init_numpy();
|
2025-04-10 20:51:28 +08:00
|
|
|
|
void** numpyApi = (void**)PyArray_API;
|
|
|
|
|
if (!numpyApi || !numpyApi[93]) { // 93是PyArray_SimpleNew的偏移量
|
2025-04-10 18:41:23 +08:00
|
|
|
|
std::cout << "NumPy API corrupt! Key functions missing." << std::endl;
|
|
|
|
|
PyErr_Print();
|
|
|
|
|
throw std::runtime_error("Invalid NumPy API state");
|
2025-04-10 20:51:28 +08:00
|
|
|
|
} else {
|
|
|
|
|
RTCContext::instance().setNumpyApi(numpyApi);
|
2025-04-10 21:15:38 +08:00
|
|
|
|
std::cout << "set numpyApi succ:" << numpyApi[93] << std::endl;
|
2025-04-10 18:41:23 +08:00
|
|
|
|
}
|
2025-04-10 10:23:57 +08:00
|
|
|
|
|
2025-04-10 20:51:28 +08:00
|
|
|
|
/*
|
2025-04-10 16:26:44 +08:00
|
|
|
|
if (!PyArray_API) {
|
|
|
|
|
std::cout << "PyArray_API is null" << std::endl;
|
2025-04-10 16:30:08 +08:00
|
|
|
|
} else {
|
|
|
|
|
std::cout << "PyArray_API is not null" << std::endl;
|
2025-04-10 16:26:44 +08:00
|
|
|
|
}
|
2025-04-10 20:51:28 +08:00
|
|
|
|
*/
|
2025-04-10 16:26:44 +08:00
|
|
|
|
|
2025-04-10 10:23:57 +08:00
|
|
|
|
py::def("init", &init);
|
|
|
|
|
py::def("initRecv", &initRecv);
|
|
|
|
|
py::def("initSend", &initSend);
|
|
|
|
|
py::def("sendCustomAudioData", &sendCustomAudioData);
|
2025-04-16 15:30:55 +08:00
|
|
|
|
py::def("getSize", &getSize);
|
2025-04-10 10:23:57 +08:00
|
|
|
|
} catch (...) {
|
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "Module initialization failed");
|
|
|
|
|
}
|
2025-04-09 16:18:50 +08:00
|
|
|
|
}
|