c++ - 如何使用Opus编码和解码音频数据?

标签 c++ qt audio opus

我正在进行语音聊天,我需要压缩音频数据。我通过Qt Framework记录和播放音频数据。如果我在不压缩的情况下录制和播放音频数据,一切都很好。如果我压缩,解压缩和播放音频数据,我只会听到刺耳的声音。

编辑:我看了演示代码,我尝试使用该代码。
我可以听到一些声音,但是非常慢。如果我将pcm_bytes的大小增加到例如40000,听起来会更好,但我的声音仍然滞后且声音开裂。

这是一行(底部的audioinput.cpp):

speaker->write((const char*)pcm_bytes,3840);

codecopus.cpp:
#include "codecopus.h"

CodecOpus::CodecOpus()
{

}

void CodecOpus::initDecoder(opus_int32 samplingRate, int channels) //decoder
{
    int error;
    decoderState = opus_decoder_create(samplingRate,channels,&error);
    if(error == OPUS_OK){
        std::cout << "Created Opus Decoder struct" << std::endl;
    }

}

void CodecOpus::initEncoder(opus_int32 samplingRate, int channels) // Encoder
{
    int error;
    encoderState = opus_encoder_create(samplingRate,channels,OPUS_APPLICATION_VOIP,&error);
    error = opus_encoder_ctl(encoderState,OPUS_SET_BITRATE(64000));
    if(error == OPUS_OK){
        std::cout << "Created Opus Encoder struct" << std::endl;
    }
}

opus_int32 CodecOpus::encodeData(const opus_int16 *pcm, int frameSize, unsigned char *data, opus_int32 maxDataBytes) //Encoder
{
    opus_int32 i = opus_encode(encoderState,pcm,frameSize,data,maxDataBytes);
    return i;
}

int CodecOpus::decodeData(const unsigned char *data, opus_int32 numberOfBytes,opus_int16* pcm,int frameSizeInSec) //Decoder
{

    int i = opus_decode(decoderState,data,numberOfBytes,pcm,frameSizeInSec,0);

    return i;
}

CodecOpus::~CodecOpus()
{
    opus_decoder_destroy(this->decoderState);
    opus_encoder_destroy(this->encoderState);

}

audioinput.h:
#ifndef AUDIOINPUT_H
#define AUDIOINPUT_H
#include <QAudioFormat>
#include <iostream>
#include <QAudioInput>
#include <QAudioOutput>
#include <thread>
#include "codecopus.h"
#include "QDebug"
class AudioInput : public QObject
{
    Q_OBJECT

public:
    AudioInput();
    ~AudioInput();
    void startRecording();
    void CreateNewAudioThread();

private:

    CodecOpus opus;
    unsigned char cbits[4000] = {};
    opus_int16 in[960*2*sizeof(opus_int16)] = {};
    opus_int16 out[5760*2] = {};

    unsigned char *pcm_bytes;

    int MAX_FRAME_SIZE;

    QAudioFormat audioFormat;
    QAudioInput *audioInput;
    QIODevice *mic;
    QByteArray data;
    int micFrameSize;


    QAudioOutput *audioOutput;
    QIODevice *speaker;
    QAudioFormat speakerAudioFormat;




public slots:
    void OnAudioNotfiy();
};

#endif // AUDIOINPUT_H

audioinput.cpp:
#include "audioinput.h"

AudioInput::AudioInput() : audioFormat(),pcm_bytes(new unsigned char[40000])
{
    audioFormat.setSampleRate(48000);
    audioFormat.setChannelCount(2);
    audioFormat.setSampleSize(16);
    audioFormat.setSampleType(QAudioFormat::SignedInt);
    audioFormat.setByteOrder(QAudioFormat::LittleEndian);
    audioFormat.setCodec("audio/pcm");


    speakerAudioFormat.setSampleRate(48000);
    speakerAudioFormat.setChannelCount(2);
    speakerAudioFormat.setSampleSize(16);
    speakerAudioFormat.setSampleType(QAudioFormat::SignedInt);
    speakerAudioFormat.setByteOrder(QAudioFormat::LittleEndian);
    speakerAudioFormat.setCodec("audio/pcm");

    QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
    if(!info.isFormatSupported(audioFormat)){
        std::cout << "Mic Format not supported!" << std::endl;
        audioFormat = info.nearestFormat(audioFormat);
    }
    QAudioDeviceInfo speakerInfo = QAudioDeviceInfo::defaultOutputDevice();
    if(!speakerInfo.isFormatSupported(speakerAudioFormat)){
        std::cout << "Speaker Format is not supported!" << std::endl;
        speakerAudioFormat = info.nearestFormat(speakerAudioFormat);

    }
    std::cout << speakerAudioFormat.sampleRate() << audioFormat.sampleRate() << speakerAudioFormat.channelCount() << audioFormat.channelCount() << std::endl;
    audioInput = new QAudioInput(audioFormat);
    audioOutput = new QAudioOutput(speakerAudioFormat);
    audioInput->setNotifyInterval(20);
    micFrameSize = (audioFormat.sampleRate()/1000)*20;

    opus.initEncoder(audioFormat.sampleRate(),audioFormat.channelCount());
    opus.initDecoder(speakerAudioFormat.sampleRate(),speakerAudioFormat.channelCount());

    MAX_FRAME_SIZE = 6*960;



    connect(audioInput,SIGNAL(notify()),this,SLOT(OnAudioNotfiy()));
}

AudioInput::~AudioInput()
{

}

void AudioInput::startRecording()
{

    mic = audioInput->start();
    speaker = audioOutput->start();
    std::cout << "Recording started!" << std::endl;


}


void AudioInput::CreateNewAudioThread()
{
    std::thread t1(&AudioInput::startRecording,this);
    t1.detach();
}





void AudioInput::OnAudioNotfiy()
{
    data = mic->readAll();


    std::cout << "data size" <<data.size() << std::endl;
    if(data.size() > 0){
    pcm_bytes = reinterpret_cast<unsigned char*>(data.data());

//convert

    for(int i=0;i<2*960;i++){ //TODO HARDCODED
        in[i]=pcm_bytes[2*i+1]<<8|pcm_bytes[2*i];
    }
    opus_int32 compressedBytes = opus.encodeData(in,960,cbits,4000);

    opus_int32 decompressedBytes = opus.decodeData(cbits,compressedBytes,out,MAX_FRAME_SIZE);

    for(int i = 0; i<2*decompressedBytes;i++) //TODO HARDCODED
    {
        pcm_bytes[2*i]=out[i]&0xFF;
        pcm_bytes[2*i+1]=(out[i]>>8)&0xFF;
    }


    speaker->write((const char*)pcm_bytes,3840);
}

}

最佳答案

1)您仅编码960个字节,而缓冲区要大得多。您必须将缓冲区分成几个相等的部分,然后将它们传递给编码器。零件的大小为120、240、480、960、1920和2880。

2)从char数组转换为opus_int16数组/从opus_int16数组转换为char数组时,请使用qFromLittleEndian()/ qToLittleEndian()函数或类型转换。这样可以防止破裂和较差的声音质量。

例:

void voice::slot_read_audio_input()
{

    //    Audio settings:
    //    Sample Rate=48000
    //    Sample Size=16
    //    Channel Count=1
    //    Byte Order=Little Endian
    //    Sample Type= UnSignedInt

    //    Encoder settings:
    //    Sample Rate=48000
    //    Channel Count=1
    //    OPUS_APPLICATION_VOIP

    //    Decoder settings:
    //    Sample Rate=48000
    //    Channel Count=1

    QByteArray audio_buffer;//mic
    QByteArray output_audio_buffer;//speaker

    int const OPUS_INT_SIZE=2;//sizeof(opus_int16)
    int const FRAME_SIZE=960;
    int const MAX_FRAME_SIZE=1276;
    int FRAME_COUNT=3840/FRAME_SIZE/OPUS_INT_SIZE;// 3840 is a sample size= voice_input->bytesReady;

    opus_int16 input_frame[FRAME_SIZE] = {};
    opus_int16 output_frame[FRAME_SIZE] = {};
    unsigned char compressed_frame[MAX_FRAME_SIZE] = {};
    unsigned char decompressed_frame[FRAME_SIZE*OPUS_INT_SIZE] = {};

    audio_buffer.resize(voice_input->bytesReady());   
    output_audio_buffer.resize(FRAME_SIZE*OPUS_INT_SIZE);

    input->read(audio_buffer.data(),audio_buffer.size());

    for(int i=0;i<FRAME_COUNT;i++)
    {
        //    convert from LittleEndian
        for(int j=0;j<FRAME_SIZE;j++)
        {
            input_frame[j]=qFromLittleEndian<opus_int16>(audio_buffer.data()+j*OPUS_INT_SIZE);
            //    or use this:
            //    input_frame[j]=static_cast<short>(static_cast<unsigned char>(audio_buffer.at(OPUS_INT_SIZE*j+1))<<8|static_cast<unsigned char>(audio_buffer.at(OPUS_INT_SIZE*j)));
        }

        opus_int32 compressedBytes = opus_encode(enc, input_frame,FRAME_SIZE,compressed_frame,MAX_FRAME_SIZE);
        opus_int32 decompressedBytes = opus_decode(dec,compressed_frame,compressedBytes,output_frame,FRAME_SIZE,0);

        //    conver to LittleEndian
        for(int j = 0; j<decompressedBytes;j++)
        {
            qToLittleEndian(output_frame[j],output_audio_buffer.data()+j*OPUS_INT_SIZE);
            //    or use this:
            //    decompressed_frame[OPUS_INT_SIZE*j]=output_frame[j]&0xFF;
            //    decompressed_frame[OPUS_INT_SIZE*j+1]=(output_frame[j]>>8)&0xFF;
        }

        audio_buffer.remove(0,FRAME_SIZE*OPUS_INT_SIZE);
        output->write(output_audio_buffer,FRAME_SIZE*OPUS_INT_SIZE);
        //    or use this:
        //    output->write(reinterpret_cast<char*>(decompressed_frame),FRAME_SIZE*OPUS_INT_SIZE);
    }
}

关于c++ - 如何使用Opus编码和解码音频数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51638654/

相关文章:

c++ - 在 Visual Studio Post-Build 事件中使用 lnk.exe 从 .lib 文件中删除特定对象

c# - C++ inside of C# 通信性能

c++ - 使用继承类从模板类继承

c++ - 在 qglwidget 的顶部添加按钮

android - qt 5.4 qtwebview : failing to load local HTML from qrc on android

c++ - 在测试用例中读取 qdebug?

javascript - 设置 Javascript 音频对象的 MIME 类型

c++ - Lua - 反射 - 获取对象上的函数/字段列表?

android - 停止后MediaPlayer无法播放

audio - 在不改变音高的情况下提高语速