Привет, я пытаюсь взять звук из открытого потока PortAudio, закодировать его с помощью opus, декодировать и снова воспроизвести с помощью portaudio.
Я делаю это в качестве прототипа просто для того, чтобы попытаться понять механику этих систем, поэтому нет особого интереса следовать этому конкретному потоку.
Дело в том, что portaudio предоставляет буферы там, где OPUS нужны кадры. Ми мысли привели меня к этому в сторону portaudio:
err = (Pa_ReadStream(stream, readBuffer, FRAMES_PER_BUFFER));
if (err = paNoError){
qDebug()<<"Fail read";
qDebug()<<Pa_GetErrorText(err);
// blockingRecord = false;
}
while (pos<FRAMES_PER_BUFFER){
memcpy(frameBuffer,readBuffer+(pos*FRAME_SIZE*NUM_CHANNELS),FRAME_SIZE*CHANNELS);
compressedSound = om.encodeOpus(frameBuffer);
unCompressedSound = om.decodeOpus(compressedSound);
memcpy(readBuffer+(pos*FRAME_SIZE*NUM_CHANNELS),unCompressedSound,FRAME_SIZE*CHANNELS);
pos++;
}
pos = 0;
err = (Pa_WriteStream(stream, readBuffer, FRAMES_PER_BUFFER));
if (err != paNoError)
{
qDebug() << "FAIL WRITE";
qDebug()<<Pa_GetErrorText(err);
//blockingRecord = false;
}
И это на стороне OPUS:
unsigned char * OpusManager::encodeOpus(unsigned char *frame){
memcpy(encoded, frame, FRAME_SIZE*CHANNELS);
int ret = opus_encode(enc, encoded, FRAME_SIZE, compressed_buffer, encoded_data_size);
if (ret<0){
qDebug()<<"Failure while compressing sound";
return NULL;
}
return (compressed_buffer);
}
unsigned char * OpusManager::decodeOpus(unsigned char *frame){
int ret= opus_decode(dec, frame, encoded_data_size, decoded, FRAME_SIZE, 0);
if (ret<0){
qDebug()<<"Failure while decompressing sound";
return NULL;
}
memcpy(uncompressed_buffer, decoded, FRAME_SIZE*CHANNELS);
return (uncompressed_buffer);
}
Никаких ошибок без вложения и совершенного звука. При кодировании я не получаю никаких ошибок до вызова PA_Writestream, где я получаю PaError «Выходной поток переполнен». Я предполагаю, что способ получения фреймов, которые я реализовал, должен быть неправильным, но не могу найти информацию, чтобы помочь мне в этом.
Похоже, ваша интерпретация параметров opus frame_size для opus_encode и opus_decode неверна. Если я правильно понимаю ваш код, вы записываете пакет с размерами кадров FRAMES_PER_BUFFER и затем пытаетесь превратить его в N пакетов размером FRAME_SIZE. Вместо этого мне кажется, что Opus хочет превратить ваш пакет FRAMES_PER_BUFFER в другой пакет с равным количеством кадров, и при этом использует только его параметр FRAME_SIZE в качестве некоторого параметра контроля качества для процесса кодирования. Ниже вы найдете полный образец, который, я считаю, делает то, что вы хотите. Поиграйте с магическим числом «480» в encode () / decode () и услышите изменение качества звука.
int opusErr;
PaError paErr;
std::string s;
int const channels = 2;
int const bufferSize = 480;
int const sampleRate = 48000;
int const durationSeconds = 5;
opus_int32 enc_bytes;
opus_int32 dec_bytes;
int framesProcessed = 0;
std::vector<unsigned short> captured(bufferSize * channels);
std::vector<unsigned short> decoded(bufferSize * channels);
// * 2: byte count, 16 bit samples
std::vector<unsigned char> encoded(bufferSize * channels * 2);
// initialize opus
OpusEncoder* enc = opus_encoder_create(
sampleRate, channels, OPUS_APPLICATION_AUDIO, &opusErr);
if (opusErr != OPUS_OK)
{
std::cout << "opus_encoder_create failed: " << opusErr << "\n";
std::getline(std::cin, s);
return 1;
}
OpusDecoder* dec = opus_decoder_create(
sampleRate, channels, &opusErr);
if (opusErr != OPUS_OK)
{
std::cout << "opus_decoder_create failed: " << opusErr << "\n";
std::getline(std::cin, s);
return 1;
}
// initialize portaudio
if ((paErr = Pa_Initialize()) != paNoError)
{
std::cout << "Pa_Initialize failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
PaStream* stream = nullptr;
if ((paErr = Pa_OpenDefaultStream(&stream,
channels, channels, paInt16, sampleRate,
bufferSize, nullptr, nullptr)) != paNoError)
{
std::cout << "Pa_OpenDefaultStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
// start stream
if ((paErr = Pa_StartStream(stream)) != paNoError)
{
std::cout << "Pa_StartStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
// capture, encode, decode & render durationSeconds of audio
while (framesProcessed < sampleRate * durationSeconds)
{
if ((paErr = Pa_ReadStream(stream,
captured.data(), bufferSize)) != paNoError)
{
std::cout << "Pa_ReadStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
if ((enc_bytes = opus_encode(enc, reinterpret_cast<opus_int16 const*>(
captured.data()), 480, encoded.data(), encoded.size())) < 0)
{
std::cout << "opus_encode failed: " << enc_bytes << "\n";
std::getline(std::cin, s);
return 1;
}
if ((dec_bytes = opus_decode(dec, encoded.data(), enc_bytes,
reinterpret_cast<opus_int16*>(decoded.data()), 480, 0)) < 0)
{
std::cout << "opus_decode failed: " << dec_bytes << "\n";
std::getline(std::cin, s);
return 1;
}
if ((paErr = Pa_WriteStream(stream, decoded.data(), bufferSize)) != paNoError)
{
std::cout << "Pa_WriteStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
framesProcessed += bufferSize;
}
// stop stream
if ((paErr = Pa_StopStream(stream)) != paNoError)
{
std::cout << "Pa_StopStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
// cleanup portaudio
if ((paErr = Pa_CloseStream(stream)) != paNoError)
{
std::cout << "Pa_CloseStream failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
if ((paErr = Pa_Terminate()) != paNoError)
{
std::cout << "Pa_Terminate failed: " << Pa_GetErrorText(paErr) << "\n";
std::getline(std::cin, s);
return 1;
}
// cleanup opus
opus_decoder_destroy(dec);
opus_encoder_destroy(enc);