我目前正在用C++开发一个语音传输软件,使用PortAudio和Opus处理声音相关部分,使用Boost Asio处理网络部分。
我在尝试在循环中设置编码和解码时遇到了困难。
更准确地说,我有一个函数来处理我的项目的“呼叫”部分,当两个人接受彼此的呼叫时,这个函数就会启动。
我得到了2个不同的函数,一个当客户端接受调用,第二个当客户端发起调用。
这两个函数都是以相同的方式构建的,我开始初始化我需要的每个库,然后监听套接字以获取音频数据,并将编码后的音频数据发送到套接字。
我的问题是:我尝试在while语句中使用简单的条件来实现这一点:if(clientConnected){}但代码似乎只执行一次循环,然后阻塞,我不知道为什么。
(我知道这是因为std::cout〈〈dataSize只打印一次)
这是两个调用函数之一(receiveCall)(如果你有任何问题问我):
void Window::receiveCall(const std::string& ip) {
// Initialize PortAudio and Opus
PortAudioWrapper portAudio;
OpusWrapper opus;
if (!portAudio.OpenDefaultStream(1, 1, paFloat32, 48000, 960))
{
std::cerr << "Failed to open PortAudio stream" << std::endl;
return;
}
if (!opus.Init(48000, 2, OPUS_APPLICATION_VOIP))
{
std::cerr << "Failed to initialize Opus" << std::endl;
return;
}
if (!portAudio.StartStream())
{
std::cerr << "Failed to start PortAudio stream" << std::endl;
return;
}
boost::asio::io_service ioService;
boost::asio::ip::tcp::socket socket(ioService);
boost::asio::ip::tcp::acceptor acceptor(ioService);
try
{
// Create an acceptor to listen for incoming connections
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ip), 12345);
acceptor.open(endpoint.protocol());
acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor.bind(endpoint);
acceptor.listen();
std::cout << "Waiting for incoming connection on " << ip << ":12345" << std::endl;
// Wait for a client to connect
acceptor.accept(socket);
std::cout << "Connected to client at " << socket.remote_endpoint().address().to_string() << std::endl;
bool clientConnected = true;
// Create a buffer to hold the audio data
std::vector<unsigned char> buffer(1276);
// Send and receive audio data
while (clientConnected)
{
// Read audio data from PortAudio
float audioData[960 * 2 * sizeof(float)];
portAudio.readStream(audioData, 960);
// Compress the audio data using Opus
int dataSize = opus.Encode(audioData, 960, buffer.data(), 1276);
std::cout << "dataSize : "<< dataSize << std::endl;
// Send the compressed audio data to the client
boost::asio::write(socket, boost::asio::buffer(buffer.data(), dataSize));
// Receive compressed audio data from the client
boost::system::error_code error;
dataSize = socket.read_some(boost::asio::buffer(buffer), error);
if (error == boost::asio::error::eof)
{
std::cout << "Client disconnected" << std::endl;
clientConnected = false; // set flag to false when client disconnects
}
else if (error)
{
std::cerr << "Error receiving audio data: " << error.message() << std::endl;
clientConnected = false; // set flag to false when client disconnects
}
// Decompress the audio data using Opus
float decodedData[960 * 2 * sizeof(float)];
int numSamples = opus.Decode(buffer.data(), dataSize, decodedData, 960);
// Write the decompressed audio data to PortAudio
portAudio.writeStream(decodedData, numSamples);
}
}
catch (std::exception& e) {
std::cerr << "Exception in receiveCall: " << e.what() << std::endl;
}
// Cleanup code
socket.close();
acceptor.close();
portAudio.StopStream();
}
问题可能出在循环中,但我不知道可能是什么。
先谢谢你了。
1条答案
按热度按时间hyrbngr71#
Opus端和缓冲区尺寸(带警告)使用2通道。然而,您只使用一个通道初始化PA流。这可能是一个错误。
此外,这里缺少帧。您的代码简单地假设在一次调用中“发送”的任何内容也会在对
read_some
的一次调用中接收。顾名思义,情况并非如此。它读作“some”。既然你似乎知道
1276
是编码数据大小的上限,你可以通过硬编码所选的缓冲区长度,通道数和样本格式来避免帧。然后你可以使用asio::read来复合读取一个或多个read_some
操作来读取“整个缓冲区”(或EOF):当然,这需要发送忽略
dataSize
才能工作:但是,
Decode
调用需要知道dataSize
,因此您仍然需要传输它。其他问题
我认为
audioData
和decodedAudio
缓冲区的尺寸是关闭的。幸运的是在“太大”的一面。我认为* sizeof(float)
因素是错误的。无论如何,我都会使用C++数组:现在您也可以避免在不同的地方硬编码
960
幻数了!发送big-endian datasize
使用Boost Endian在网络上可移植地表示32位整数:
现在发送可能看起来像:
和接收类似,但在两个步骤:
整合
**一个
Coliru输出:
当然,“对等体”不会发送回包含opus数据的有效帧。