【问题标题】:Joining Portaudio and Opus加入 Portaudio 和 Opus
【发布时间】:2014-10-17 23:15:24
【问题描述】:

您好,我正在尝试从打开的 PortAudio Stream 中获取声音,使用 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。我想采取我实施的框架的方式一定是错误的,但找不到帮助我解决这个问题的信息。

【问题讨论】:

    标签: c++ portaudio opus


    【解决方案1】:

    您对 Opus 的 frame_size 参数对 opus_encode 和 opus_decode 的解释似乎不正确。如果我正确理解您的代码,您正在记录一个大小为 FRAMES_PER_BUFFER 帧的数据包,然后尝试将其转换为 N 个大小为 FRAME_SIZE 的数据包。相反,在我看来,Opus 想要将您的 FRAMES_PER_BUFFER 数据包转换为另一个相同帧数的数据包,并且在这样做时,仅使用它的 FRAME_SIZE 参数作为编码过程的某种质量控制参数。您将在下面找到一个完整的示例,我相信它可以满足您的需求。在 encode()/decode() 中玩弄 '480' 幻数,听听音频质量的变化。

    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);
    

    【讨论】:

    • 谢谢老兄,解决了这个问题。正如你所说,我误导了作品中的帧/缓冲区概念^^
    猜你喜欢
    • 2012-11-19
    • 2014-12-11
    • 1970-01-01
    • 2014-01-09
    • 1970-01-01
    • 2013-01-08
    • 2018-11-27
    • 2023-04-10
    • 2023-04-05
    相关资源
    最近更新 更多