精华内容
下载资源
问答
  • Qt 之 opus编码

    2021-04-18 18:59:45
    项目需要实现语音翻译功能,对接的图灵的websocket api, 第一步是实现了pcm数据的上传,第二步实现pcm to opus编码,来压缩数据量。所以仅仅使用到了opus编码功能,通过opus的设置,实现了1/8的压缩效果。 opus...

    背景

    项目需要实现语音翻译功能,对接的图灵的websocket api, 第一步是实现了pcm数据的上传,第二步实现pcm to opus编码,来压缩数据量。所以仅仅使用到了opus的编码功能,通过opus的设置,实现了1/8的压缩效果。

    opus简介

    opus是一个有损声音编码的格式,由IETF开发,没有任何专利或限制,适用于网络上的实时声音传输,标准格式为RFC 6716,其技术来源于Skype的SILK及Xiph.Org的CELT编码.
    opus文件是一个用开放的Opus编解码器编码的数字音频文件。Opus文件使用Ogg容器格式。目前一些知名的媒体播放器如VLC支持直接播放.opus文件,而其他的播放器则需要先安装一个外部的Opus编解码器(“libopus”)。

    Opus开发团队提供了一套参考工具,用于获取.opus文件的信息(“opusinfo”),并将它们转换为/从WAV(“opusenc”、“opusdec”)。在Microsoft Windows上,可以使用安装了免费编码器包的foobar2000创建Opus编码的音频。

     主要特性如下:
    
    • 6 kb /秒到510 kb / s的比特率
    • 采样率从8 kHz(窄带)到48 kHz(全频)
    • 帧大小从2.5毫秒到60毫秒
    • 支持恒定比特率(CBR)和可变比特率(VBR)
    • 从窄带到全频段的音频带宽
    • 支持语音和音乐
    • 支持单声道和立体声
    • 支持多达255个频道(多数据流的帧)
    • 可动态调节比特率,音频带宽和帧大小
    • 良好的鲁棒性丢失率和数据包丢失隐藏(PLC)
    • 浮点和定点实现

    opus使用约束

    • 采样率约束:

      输入信号的采样率(Hz),必须是8000、12000、16000、24000、或48000。
      OpusEncoder* opus_encoder_create(opus_int32 Fs, int channels, int application, int *error);
      函数参数中的Fs就是采样率。

    • 帧长约束:

      opus为了对一个帧进行编码,必须正确地用音频数据的帧(2.5, 5, 10, 20, 40 or 60 ms)来调用opus_encode()或opus_encode_float()函数。

      比如,在48kHz的采样率下,opus_encode()参数中的合法的frame_size(单通道的帧大小)值只有:120, 240, 480, 960, 1920, 2880。即:
      frame_size = 采样率 * 帧时间。

      因为需要满足帧时间长度为10,20,40,60ms这些才能编码opus,因而需要对输入数据进行缓冲裁剪

    • 兼容opus的容器格式:
      有ogg,ts,mkv。但ts无法播放,mkv只能foobar播放,ogg能用foobar,vlc播放。因而不再考虑opus合成到ts

    opus环境搭建

    linux环境和arm交叉编译

    首先需要在opus官网上下载opus相关的源码资料
    http://www.opus-codec.org/
    在downloads里面可以看到全部的源码下载

    这里我们需要下载
    opus-tools-0.2.1.tar.gz和opus-1.3.1.tar.gz
    下载后可以在ubuntu里解压
    然后

    ./configure
    

    (如果是其余平台如Mips或Arm,需要添加 --host=(交叉编译链),在ARM和mips平台推荐使用–enable-fixed-point命令关闭浮点运算)

    #!/bin/sh
    export export PATH=/home/flourier/work/gcctool/gcc-linaro-4.9-2015.02-3-x86_64_arm-linux-gnueabihf/bin:$PATH
    export CC=arm-linux-gnueabihf-gcc
    export CXX=arm-linux-gnueabihf-g++
    

    指定你的host,你的交叉编译工具,还有安装目录等

    ./configure --host=arm-linux-gnueabihf --enable-fixed-point --disable-float-api CFLAGS="-O2 -mfpu=neon -mfloat-abi=hard" HAVE_ARM_NEON_INTR=1 --prefix /home/zyb/opus-arm/install
    

    然后

    make && make install
    

    之后,会出现一堆供测试用的可执行文件, arm下:
    在这里插入图片描述
    在这里插入图片描述
    linux下:
    在这里插入图片描述

    编码功能模块

    由于项目需要实时编码,并且qt提供了非常方便的QIODevice,所以数据大概流程是:

    在这里插入图片描述

    1. 从IODevice,拿到音频设备pcm数据
    2. 通过降噪处理后,push到一个缓存buf中
    3. opus编码线程不断的从缓存buf中,将数据去除,进行编码
    4. 将编码后的数据通过websocket发送的云端请求

    opus实现

    创建opus编码器

    opus_int16 pcm_bytes[FRAME_SIZE * MAX_CHANNELS];
    unsigned char cbits[MAX_PACKET_SIZE];
    OpusEncoder *g_encoder = nullptr;
    
    bool creatOpusEncoder(uint32_t sampleRate,uint16_t channels,int err)
    {
        g_encoder = opus_encoder_create(sampleRate, channels, OPUS_APPLICATION_AUDIO, &err);
        qDebug() << "opus_encoder_create  ::::" <<  QString::fromLocal8Bit(opus_strerror(err));
        if (!g_encoder || err < 0) {
            qDebug() << "failed to create an encoder" <<  QString::fromLocal8Bit(opus_strerror(err));
            fprintf(stderr, "failed to create an encoder: %s\n", opus_strerror(err));
            if (!g_encoder) {
                opus_encoder_destroy(g_encoder);
            }
            return false;
        }
    
        opus_encoder_ctl(g_encoder, OPUS_SET_VBR(0));//0:CBR, 1:VBR
        opus_encoder_ctl(g_encoder, OPUS_SET_VBR_CONSTRAINT(true));
        opus_encoder_ctl(g_encoder, OPUS_SET_BITRATE(32000));
        opus_encoder_ctl(g_encoder, OPUS_SET_COMPLEXITY(8));//8    0~10
        opus_encoder_ctl(g_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
        opus_encoder_ctl(g_encoder, OPUS_SET_LSB_DEPTH(16));
        opus_encoder_ctl(g_encoder, OPUS_SET_DTX(0));
        opus_encoder_ctl(g_encoder, OPUS_SET_INBAND_FEC(0));
        //opus_encoder_ctl(g_encoder, OPUS_SET_PACKET_LOSS_PERC(0));
    
        return  true;
    }
    

    其中的参数,需要根据实际的硬件才设置
    比如本项目降噪后的数据是:

    output_cfg.audio_channels = 1; //输出音频通道数,1通道数据
    output_cfg.sample_rate = 16000; //输出音频采样率,16k
    output_cfg.audio_format = kHrscAudioFormatPcm16Bit; //输出音频位数,16bit
    帧长为20ms
    所以比特率为 16000*2 = 32000
    采样率为16000,所以一毫秒为16 20ms为320个采样,每个采样是16bit即 2个byte,所以每帧大小为640

    编码

    可以
    传入一段长度的pcm数据,可以是frame_size*N,即数据帧的整数倍,可以根据次参数,来调节进行最大的优化

    QByteArray pcmData2opus(void *audio_data,unsigned int aduio_data_size )
    {
        QByteArray opusdata;
    
        const uint16_t *data = (uint16_t *) (audio_data);
        size_t size = aduio_data_size/2;
        size_t index = 0;
        size_t step = static_cast<size_t>(FRAME_SIZE * 1);
        size_t frameCount = 0;
        size_t readCount = 0;
    
        while (index < size) {
            memset(&pcm_bytes, 0, sizeof(pcm_bytes));
            if ((index + step) <= size) {
                memcpy(pcm_bytes, data + index, step * sizeof(uint16_t));
                index += step;
            } else {
                readCount = size - index;
                memcpy(pcm_bytes, data + index, (readCount) * sizeof(uint16_t));
                index += readCount;
            }
            int nbBytes = opus_encode(g_encoder, pcm_bytes, 1 * FRAME_SIZE, cbits, MAX_PACKET_SIZE);
            if (nbBytes < 0) {
                qDebug() << "encode failed:" << opus_strerror(nbBytes);
                break;
            }
            ++frameCount;
            qDebug()<< "total size= "<<size << "framecount =" << frameCount  << index << nbBytes;
            opusdata.append((char *) cbits, static_cast<size_t>(nbBytes));
        }
    
        return  opusdata;
    }
    

    其中需要注意的是每帧大小为640,而opus_encode的函数原型为:

    opus_int32 opus_encode (OpusEncoder *       st,
            const opus_int16 *  pcm,
            int                 frame_size,
            unsigned char *     data,
            opus_int32          max_data_bytes 
    )   
    

    其中pcm数据类型为opus_int16是short类型,所以frame_size应该是 640/2 =320
    也可以直接传入一帧数据,进行编码,比如下面的编码线程实现:

    void OpusProcess::run()
    {
        m_State = STATE_STARTED;
        m_dataBuf.clear();
        int err=0;
        bool b = creatOpusEncoder(ONLINE_ASR_AUDIO_SAMPLE_RATE,ONLINE_CHANNEL,err);
    
        while (true) {
            if (m_State == STATE_STOPPED)
                break;
            if(m_dataBuf.length() >= FRAME_SIZE)
            {
                m_Mutex.lock();
                QByteArray tmpdata = m_dataBuf.left(FRAME_SIZE);
                quint16 nbBytes =(quint16)opus_encode(g_encoder, (opus_int16 *)tmpdata.data(), FRAME_SIZE/2, cbits, MAX_PACKET_SIZE);
    
                emit speechDataEncoded(QByteArray des((char *) cbits, nbBytes));
    
                m_dataBuf = m_dataBuf.mid(FRAME_SIZE);
                m_Mutex.unlock();
            }
            else {
                //qWarning() << "ring buffer data is not enough, need wait";
                usleep(100 * 1000);
            }
        }
    
        destroyOpusEncoder();
        m_State = STATE_STOPPED;
        qDebug() << "OpusProcess thread end";
    }
    

    线程结束后释放编码器:

    bool destroyOpusEncoder()
    {
        if(g_encoder)
        {
            opus_encoder_destroy(g_encoder);
            qDebug() << "*****opus_encoder_destroy********";
        }
    }
    

    注意事项:

    1.对于连续的一段声音,一定只能用一个解码器(不能创建之后释放再去创建解码器)
    2 发送的云端的opus编码数据,一般是要加头信息的,用来告诉这段数据的长度,音频数据的每帧都要加头的。

    验证

    为了验证自己编码后的数据是否正确,可以用编码后的数据,再进行解码,然后把解码数据存储都一个文件,来进行播放,如果播放正常,基本数据都正常
    所以,附上解码代码

    OpusDecoder *g_decoder = nullptr;
    
    ```cpp
    bool creatOpusDecoder(uint32_t sampleRate,uint16_t channels,int err)
    {
        g_decoder = opus_decoder_create(sampleRate, channels, &err);
        qDebug() << "opus_decoder_create  ::::" <<  QString::fromLocal8Bit(opus_strerror(err));
        if (!g_decoder || err < 0) {
            qDebug() << "failed to create an encoder" <<  QString::fromLocal8Bit(opus_strerror(err));
            fprintf(stderr, "failed to create an encoder: %s\n", opus_strerror(err));
            if (!g_decoder) {
                opus_decoder_destroy(g_decoder);
            }
            return false;
        }
    
        return  true;
    }
    

    保存文件:

    QFile f("pcm.data");
    bool bisOk = f.open(QIODevice::WriteOnly | QIODevice::Truncate);
    if(bisOk2== true)
    {
        qDebug() << "save file size=" << testpcm.size();
        f.write(testpcm.data(), testpcm.size());
        f.close();
    }else{
        qDebug() << " pcm.data voice file open failed";
    }
    

    需要更详细的了解opus可以去官网了解 https://www.opus-codec.org/

    另附笔者参考的demo
    https://download.csdn.net/download/u011942101/16744647

    展开全文
  • ffmpeg对48k采样率,双声道的...ffmpeg对pcm编程成opus,可以对8k,16k,32k采样率的pcm重采样48k,再编码opus,目前只能对双声道的pcm做重采样,单声道还存在问题 代码:https://github.com/Hoke/opus-audio ...

    ffmpeg对48k采样率,双声道的opus解码成pcm,再把pcm重采样成8k,16k,44.1k,48k的采样率等操作

    ffmpeg对pcm编程成opus,可以对8k,16k,32k采样率的pcm重采样48k,再编码成opus,目前只能对双声道的pcm做重采样,单声道还存在问题

    展开全文
  • android ios opus语音编码压缩库编译

    千次阅读 2017-05-28 16:44:34
    据说Opus 比speex库,aac各方面性能更好,也很适合做网络语音通话。 采样率 : 音频取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。一般...
    据说Opus 比speex库,aac各方面性能更好,也很适合做网络语音通话。
    

    采样率 :
    音频取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。一般采样率有 8000HZ 16000HZ 44100HZ。
    比特率:
    每秒的传输速率(位速, 也叫比特率)。如256kbps 或256000bps, 其中的 b 是 bit, ps 是每秒的意思,表示每秒256000bit的容量。
    8bit=1Byte
    帧:
    帧记录了一个声音单元,其长度为样本长度(采样位数)和通道数的乘积。采样率X通道数X采样位数=8000X1X16=128000bit=16000Byte。
    Opus压缩的声音需要pcm编码.

    Android Opus编译过程
    编译好下载地址:http://download.csdn.net/download/undiif123/9854940

    1.打开android studio,点击sdk-manager->android sdk勾选红色箭头进行安装ndk等库,ndk需要设置一下环境变量,设置后cmd就可以直接编译了。

    这里写图片描述

    2.下载Opus_1.1.4 解压后把本文件名命名jni,celt_headers.mk,celt_sources.mk,opus_headers.mk,opus_sources.mk,silk_headers.mk,silk_sources.mk 把这mk放在jni同级目录中
    3.配置android.mk

    LOCAL_PATH := $(call my-dir)  #加载当前路径
    include $(CLEAR_VARS)
    include celt_sources.mk   #加载celt 所有.c的 mk
    include silk_sources.mk  #加载silk 所有.c 的mk
    include opus_sources.mk #加载opus 所有.c 的mk
    MY_MODULE_DIR       := newopus  #库的名称
    LOCAL_MODULE        := $(MY_MODULE_DIR)
    SILK_SOURCES += $(SILK_SOURCES_FIXED)
    #编译的源代码.c
    CELT_SOURCES += $(CELT_SOURCES_ARM)
    SILK_SOURCES += $(SILK_SOURCES_ARM)
    LOCAL_SRC_FILES     := opusmain.c\   #封装接口opusmain.c
    $(CELT_SOURCES) $(SILK_SOURCES) $(OPUS_SOURCES)
    
    LOCAL_LDLIBS        := -lm –llog  #加载系统的库 日志库
    
    LOCAL_C_INCLUDES    := \  #包含头文件
    $(LOCAL_PATH)/include \
    $(LOCAL_PATH)/silk \
    $(LOCAL_PATH)/silk/fixed \
    $(LOCAL_PATH)/celt
    #附加编译选项
    LOCAL_CFLAGS        := -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
    LOCAL_CFLAGS        += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT=1 -DDISABLE_FLOAT_API -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -O3 -fno-math-errno
    LOCAL_CPPFLAGS      := -DBSD=1
    LOCAL_CPPFLAGS      += -ffast-math -O3 -funroll-loops
    include $(BUILD_SHARED_LIBRARY)  #编译动态库设置

    4.配置Application.mk

    APP_ABI := armeabi armeabi-v7a  #编译运行的系统,平台
    NDK_TOOLCHAIN_VERSION=4.9 #指定交叉编译器
    APP_PLATFORM := android-19   #设定ndk编译的版本

    5.以上配置完编译
    这里写图片描述
    Cmd 到项目中 有jni文件夹 进行ndk-build编译就ok了 把libopus复制到项目libs对应的armeabi armeabi-v7a 目录下

    封装接口源代码:

    Opusmain.c
    #include <jni.h>
    #include <string.h>
    #include <android/log.h>
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <limits.h>
    #include <stdint.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #if (!defined WIN32 && !defined _WIN32) || defined(__MINGW32__)
    #include <unistd.h>
    #else
    #include <process.h>
    #define getpid _getpid
    #endif
    #include "opus_multistream.h"
    #include "opus.h"
    #include "../src/opus_private.h"
    
    #define MAX_PACKET (1500)
    #define SAMPLES (48000*30)
    #define SSAMPLES (SAMPLES/3)
    #define MAX_FRAME_SAMP (5760)
    //每采集一帧大小为160short=320个字节
    //帧的计算,例如 采样率8000 单通道 采样位数16bit
    //1秒采集到数据8000X1X16=16000Byte
    //网络语音传输20ms一次 也就是说1秒发50次320Byte的包 320Byte=160Short
    //采集一次是160Short
    #define FRAME_SIZE 160
    #define SAMPLE_RATE 8000
    #define CHANNELS 1
    #define APPLICATION OPUS_APPLICATION_AUDIO
    //参数调整压缩倍数可设置7000 声音还能正常,压缩率160short 压缩成18short
    #define BITRATE 8000
    
    #define MAX_FRAME_SIZE 6*960
    #define MAX_PACKET_SIZE (3*1276)
    OpusEncoder *encoder;
    OpusDecoder *decoder;
    
    int bandwidth = OPUS_AUTO;
    int use_vbr = 0;
    int cvbr=1;
    int complexity = 4;
    int packet_loss_perc=0;
    
    //初始化语音
    //com_gauss_ opus _encode java层的包名,Opus类名,open函数名 这样定义好java就可以直接调用了
    jint Java_com_gauss_opus_encode_Speex_open(JNIEnv *env,jobject thiz,jint compression){
    
       int err;
        encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, APPLICATION, &err);
       if (err<0)
       {
    
          return 0;
       }
    
       err = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));
       if (err<0)
       {      
          return 0;
       }
    
       opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(bandwidth));
       opus_encoder_ctl(encoder, OPUS_SET_VBR(use_vbr));
       opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(cvbr));
      opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(compression));          opus_encoder_ctl(encoder,   OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
       decoder = opus_decoder_create(SAMPLE_RATE, CHANNELS, &err);
       if (err<0)
       {      
          return 0;
       }
       return 1;
    
    }
    JNIEXPORT
    jint Java_com_gauss_opus_encode_Opus_getFrameSize(JNIEnv *env,jobject thiz)
    {
        return  FRAME_SIZE;
    }
    //压缩
    JNIEXPORT
    jint Java_com_gauss_opus_encode_Opus_encode(JNIEnv *env,jobject thiz,                                                  jshortArray lin, jint offset, jbyteArray encoded, jint size)
    {
        int err;
        int i;
        int nbBytes;
        unsigned short cbits[480];
        unsigned short out[160];
    
        if(encoder == NULL){
            return 0;
        }
      //读取参数 数组
        (*env)->GetShortArrayRegion(env,lin,offset,size,out);
    nbBytes = opus_encode(encoder,out , size, cbits, 480);//
    //设定压缩完的数据到数组中
        (*env)->SetByteArrayRegion(env,encoded, 0, nbBytes, (jshort*)cbits);
    
        return nbBytes;
    
    }
    //解压
    JNIEXPORT
    jint Java_com_gauss_opus_encode_Opus_decode(JNIEnv *env,jobject thiz,
                                                           jbyteArray encoded, jshortArray lin, int size)
    {
        int err;
        int i;
        int frame_size;
    
        int nbBytes;
        unsigned char cbits[MAX_PACKET_SIZE];
        unsigned short out[160];
    
        unsigned short deout[160];
        if(decoder == NULL){
            return 0;
        }
    
        (*env)->GetByteArrayRegion(env, encoded,0,size,out);
        frame_size = opus_decode(decoder, out, size, deout, MAX_FRAME_SIZE, 0);
    
        if(frame_size <0){
            return 0;
        }
    
        int nSize = frame_size*CHANNELS;
        (*env)->SetShortArrayRegion(env,lin, 0, nSize, (jshort*)deout);
    
        return nSize;
    
    }
    
    
    //释放语音对象
    JNIEXPORT
    void Java_com_gauss_opus_encode_Opus_close(JNIEnv *env,jobject thiz)
    {
    
      if(encoder !=NULL){
       opus_encoder_destroy(encoder);
      }
     if(encoder !=NULL){
      opus_decoder_destroy(decoder);
      }
    }

    IOS编译

    1.使用build-libopus.sh 脚本编译,修改一下参数
    VERSION=1.1.4 #opus库的版本
    SDKVERSION=”10.3” #ios sdk版本
    MINIOSVERSION=”8.0” #最低版本8.0
    2.然后使用终端编译build-libopus.sh
    3.然后把libopus.a 和include 加入工程中。

    #import "OpusCode1.h"
    
    @implementation OpusCode1
    - (id)init {
        if (self = [super init]) {
            codecOpenedTimes = 0;
        }
        return self;
    }
    
    /*
     quality value 1 ~ 10
     */
    #define BITRATE 8000
    #define MAX_FRAME_SIZE 6*960
    - (void)open:(int)quality {
        if ((quality < 1) || (quality > 10)) {
            return;
        }
        if (codecOpenedTimes++ != 0) {
            return;
        }
        else {
    
            encodeFrameSize = 160;
            decodeFrameSize = 160;
            int bandwidth = OPUS_AUTO;
            int use_vbr = 0;
            int cvbr=1;
            int packet_loss_perc=0;
            //opus
            int err;
            encoder = opus_encoder_create(8000, 1, OPUS_APPLICATION_AUDIO, &err);
    
    
            err = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));
            opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(bandwidth));
            opus_encoder_ctl(encoder, OPUS_SET_VBR(use_vbr));
            opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(cvbr));
            opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(quality));
            opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
    
    
            decoder = opus_decoder_create(8000, 1, &err);
        }
    }
    
    - (NSData *)encode:(short *)pcmBuffer length:(int)lengthOfShorts {
        if (codecOpenedTimes == 0) {
            return nil;
        }
    
        //opus
    
        int nbBytes;
        unsigned short cbits[480];
        NSMutableData *decodedData = [NSMutableData dataWithCapacity:20];
    
        short input_frame[encodeFrameSize];
    
        memset((Byte*)input_frame,0,encodeFrameSize*2);
    
        memcpy(input_frame, pcmBuffer, encodeFrameSize * sizeof(short));
        nbBytes = opus_encode(encoder, input_frame, encodeFrameSize, cbits, 480);
        [decodedData appendBytes:cbits length:nbBytes];
    
        return decodedData;
    }
    
    - (int)decode:(unsigned char *)encodedBytes length:(int)lengthOfBytes output:(short *)decoded {
        if ( ! codecOpenedTimes)
            return 0;
    
        opus_int16 out[MAX_FRAME_SIZE];
    
        char cbits[200];
        memcpy(cbits, encodedBytes, lengthOfBytes);
        int frame_size;
        frame_size = opus_decode(decoder, (short*)encodedBytes, lengthOfBytes, decoded, MAX_FRAME_SIZE, 0);
        return decodeFrameSize;
    }
    
    - (void)close {
        if (--codecOpenedTimes != 0) {
            return;
        }
    
        if(encoder !=NULL){
        opus_encoder_destroy(encoder);
        }
        if(decoder !=NULL){
        opus_decoder_destroy(decoder);
        }
    }
    
    - (void)dealloc {
        [self close];
    #if !__has_feature(objc_arc)
    //    [super dealloc];
    #endif
    }
    
    @end
    展开全文
  • 据说Opus 比speex库,aac各方面性能更好,也很适合做网络语音通话。采样率 :音频取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。一般采样...

    据说Opus 比speex库,aac各方面性能更好,也很适合做网络语音通话。

    采样率 :

    音频取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。一般采样率有 8000HZ 16000HZ 44100HZ。

    比特率:

    每秒的传输速率(位速, 也叫比特率)。如256kbps 或256000bps, 其中的 b 是 bit, ps 是每秒的意思,表示每秒256000bit的容量。

    8bit=1Byte

    帧:

    帧记录了一个声音单元,其长度为样本长度(采样位数)和通道数的乘积。采样率X通道数X采样位数=8000X1X16=128000bit=16000Byte。

    Opus压缩的声音需要pcm编码.

    1.打开android studio,点击sdk-manager->android sdk勾选红色箭头进行安装ndk等库,ndk需要设置一下环境变量,设置后cmd就可以直接编译了。

    e1822b1e55b7ade3e30ae5bbccfaf0b5.png

    2.下载Opus_1.1.4 解压后把本文件名命名jni,celt_headers.mk,celt_sources.mk,opus_headers.mk,opus_sources.mk,silk_headers.mk,silk_sources.mk 把这mk放在jni同级目录中

    3.配置android.mk

    LOCAL_PATH := $(call my-dir) #加载当前路径

    include $(CLEAR_VARS)

    include celt_sources.mk #加载celt 所有.c的 mk

    include silk_sources.mk #加载silk 所有.c 的mk

    include opus_sources.mk #加载opus 所有.c 的mk

    MY_MODULE_DIR := newopus #库的名称

    LOCAL_MODULE := $(MY_MODULE_DIR)

    SILK_SOURCES += $(SILK_SOURCES_FIXED)

    #编译的源代码.c

    CELT_SOURCES += $(CELT_SOURCES_ARM)

    SILK_SOURCES += $(SILK_SOURCES_ARM)

    LOCAL_SRC_FILES := opusmain.c\ #封装接口opusmain.c

    $(CELT_SOURCES) $(SILK_SOURCES) $(OPUS_SOURCES)

    LOCAL_LDLIBS := -lm –llog #加载系统的库 日志库

    LOCAL_C_INCLUDES := \ #包含头文件

    $(LOCAL_PATH)/include \

    $(LOCAL_PATH)/silk \

    $(LOCAL_PATH)/silk/fixed \

    $(LOCAL_PATH)/celt

    #附加编译选项

    LOCAL_CFLAGS := -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64

    LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT=1 -DDISABLE_FLOAT_API -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -O3 -fno-math-errno

    LOCAL_CPPFLAGS := -DBSD=1

    LOCAL_CPPFLAGS += -ffast-math -O3 -funroll-loops

    include $(BUILD_SHARED_LIBRARY) #编译动态库设置

    4.配置Application.mk

    APP_ABI := armeabi armeabi-v7a #编译运行的系统,平台

    NDK_TOOLCHAIN_VERSION=4.9 #指定交叉编译器

    APP_PLATFORM := android-19 #设定ndk编译的版本

    5.以上配置完编译

    be53a26188e6456a597da8a24a3df257.png

    Cmd 到项目中 有jni文件夹 进行ndk-build编译就ok了 把libopus复制到项目libs对应的armeabi armeabi-v7a 目录下

    封装接口源代码:

    Opusmain.c

    #include

    #include

    #include

    #ifdef HAVE_CONFIG_H

    #include "config.h"

    #endif

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #if (!defined WIN32 && !defined _WIN32) || defined(__MINGW32__)

    #include

    #else

    #include

    #define getpid _getpid

    #endif

    #include "opus_multistream.h"

    #include "opus.h"

    #include "../src/opus_private.h"

    #define MAX_PACKET (1500)

    #define SAMPLES (48000*30)

    #define SSAMPLES (SAMPLES/3)

    #define MAX_FRAME_SAMP (5760)

    //每采集一帧大小为160short=320个字节

    //帧的计算,例如 采样率8000 单通道 采样位数16bit

    //1秒采集到数据8000X1X16=16000Byte

    //网络语音传输20ms一次 也就是说1秒发50次320Byte的包 320Byte=160Short

    //采集一次是160Short

    #define FRAME_SIZE 160

    #define SAMPLE_RATE 8000

    #define CHANNELS 1

    #define APPLICATION OPUS_APPLICATION_AUDIO

    //参数调整压缩倍数可设置7000 声音还能正常,压缩率160short 压缩成18short

    #define BITRATE 8000

    #define MAX_FRAME_SIZE 6*960

    #define MAX_PACKET_SIZE (3*1276)

    OpusEncoder *encoder;

    OpusDecoder *decoder;

    int bandwidth = OPUS_AUTO;

    int use_vbr = 0;

    int cvbr=1;

    int complexity = 4;

    int packet_loss_perc=0;

    //初始化语音

    //com_gauss_ opus _encode java层的包名,Opus类名,open函数名 这样定义好java就可以直接调用了

    jint Java_com_gauss_opus_encode_Speex_open(JNIEnv *env,jobject thiz,jint compression){

    int err;

    encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, APPLICATION, &err);

    if (err<0)

    {

    return 0;

    }

    err = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));

    if (err<0)

    {

    return 0;

    }

    opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(bandwidth));

    opus_encoder_ctl(encoder, OPUS_SET_VBR(use_vbr));

    opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(cvbr));

    opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(compression)); opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));

    decoder = opus_decoder_create(SAMPLE_RATE, CHANNELS, &err);

    if (err<0)

    {

    return 0;

    }

    return 1;

    }

    JNIEXPORT

    jint Java_com_gauss_opus_encode_Opus_getFrameSize(JNIEnv *env,jobject thiz)

    {

    return FRAME_SIZE;

    }

    //压缩

    JNIEXPORT

    jint Java_com_gauss_opus_encode_Opus_encode(JNIEnv *env,jobject thiz, jshortArray lin, jint offset, jbyteArray encoded, jint size)

    {

    int err;

    int i;

    int nbBytes;

    unsigned short cbits[480];

    unsigned short out[160];

    if(encoder == NULL){

    return 0;

    }

    //读取参数 数组

    (*env)->GetShortArrayRegion(env,lin,offset,size,out);

    nbBytes = opus_encode(encoder,out , size, cbits, 480);//

    //设定压缩完的数据到数组中

    (*env)->SetByteArrayRegion(env,encoded, 0, nbBytes, (jshort*)cbits);

    return nbBytes;

    }

    //解压

    JNIEXPORT

    jint Java_com_gauss_opus_encode_Opus_decode(JNIEnv *env,jobject thiz,

    jbyteArray encoded, jshortArray lin, int size)

    {

    int err;

    int i;

    int frame_size;

    int nbBytes;

    unsigned char cbits[MAX_PACKET_SIZE];

    unsigned short out[160];

    unsigned short deout[160];

    if(decoder == NULL){

    return 0;

    }

    (*env)->GetByteArrayRegion(env, encoded,0,size,out);

    frame_size = opus_decode(decoder, out, size, deout, MAX_FRAME_SIZE, 0);

    if(frame_size <0){

    return 0;

    }

    int nSize = frame_size*CHANNELS;

    (*env)->SetShortArrayRegion(env,lin, 0, nSize, (jshort*)deout);

    return nSize;

    }

    //释放语音对象

    JNIEXPORT

    void Java_com_gauss_opus_encode_Opus_close(JNIEnv *env,jobject thiz)

    {

    if(encoder !=NULL){

    opus_encoder_destroy(encoder);

    }

    if(encoder !=NULL){

    opus_decoder_destroy(decoder);

    }

    }

    IOS编译

    1.使用build-libopus.sh 脚本编译,修改一下参数

    VERSION=1.1.4 #opus库的版本

    SDKVERSION=”10.3” #ios sdk版本

    MINIOSVERSION=”8.0” #最低版本8.0

    2.然后使用终端编译build-libopus.sh

    3.然后把libopus.a 和include 加入工程中。

    #import "OpusCode1.h"

    @implementation OpusCode1

    - (id)init {

    if (self = [super init]) {

    codecOpenedTimes = 0;

    }

    return self;

    }

    /* quality value 1 ~ 10 */

    #define BITRATE 8000

    #define MAX_FRAME_SIZE 6*960

    - (void)open:(int)quality {

    if ((quality < 1) || (quality > 10)) {

    return;

    }

    if (codecOpenedTimes++ != 0) {

    return;

    }

    else {

    encodeFrameSize = 160;

    decodeFrameSize = 160;

    int bandwidth = OPUS_AUTO;

    int use_vbr = 0;

    int cvbr=1;

    int packet_loss_perc=0;

    //opus

    int err;

    encoder = opus_encoder_create(8000, 1, OPUS_APPLICATION_AUDIO, &err);

    err = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));

    opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(bandwidth));

    opus_encoder_ctl(encoder, OPUS_SET_VBR(use_vbr));

    opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(cvbr));

    opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(quality));

    opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));

    decoder = opus_decoder_create(8000, 1, &err);

    }

    }

    - (NSData *)encode:(short *)pcmBuffer length:(int)lengthOfShorts {

    if (codecOpenedTimes == 0) {

    return nil;

    }

    //opus

    int nbBytes;

    unsigned short cbits[480];

    NSMutableData *decodedData = [NSMutableData dataWithCapacity:20];

    short input_frame[encodeFrameSize];

    memset((Byte*)input_frame,0,encodeFrameSize*2);

    memcpy(input_frame, pcmBuffer, encodeFrameSize * sizeof(short));

    nbBytes = opus_encode(encoder, input_frame, encodeFrameSize, cbits, 480);

    [decodedData appendBytes:cbits length:nbBytes];

    return decodedData;

    }

    - (int)decode:(unsigned char *)encodedBytes length:(int)lengthOfBytes output:(short *)decoded {

    if ( ! codecOpenedTimes)

    return 0;

    opus_int16 out[MAX_FRAME_SIZE];

    char cbits[200];

    memcpy(cbits, encodedBytes, lengthOfBytes);

    int frame_size;

    frame_size = opus_decode(decoder, (short*)encodedBytes, lengthOfBytes, decoded, MAX_FRAME_SIZE, 0);

    return decodeFrameSize;

    }

    - (void)close {

    if (--codecOpenedTimes != 0) {

    return;

    }

    if(encoder !=NULL){

    opus_encoder_destroy(encoder);

    }

    if(decoder !=NULL){

    opus_decoder_destroy(decoder);

    }

    }

    - (void)dealloc {

    [self close];

    #if !__has_feature(objc_arc)

    // [super dealloc];

    #endif

    }

    @end

    展开全文
  • 目录 PCM(脉冲编码调制) ...Opus(声音编码格式) 特性 播放 技术细节 VP8视频压缩格式 简介 突破创新 技术分析 H.264数字视频压缩格式 背景介绍 优势 特点 PCM(脉冲编码调制) ...
  • PCM转换成OPUS编码 Opus是一个有损声音编码的格式,由Xiph.Org基金会开发,之后由IETF(互联网工程任务组)进行标准化,目标是希望用单一格式包含声音和语音,取代Speex和Vorbis,且适用于网络上低延迟的即时声音...
  • AAC-LD/ELD it is either 480 or 512 PCM samples per frame and channel. http://opus-codec.org/downloads/ https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz build for android: htt
  • pcm音频是未压缩的数据,占空间,在存储或者传输时一般都会选择先进行有损压缩(比如aac,MP3等等)。pcm音频数据在文件存储时一般都按照planner格式存储,例如: 声道1声道2声道3..声道1声道2声道3...... 常见音频...
  • 然后跑的时候,我把例子中的编码器改成了opus的: codec = avcodec_find_decoder((AV_CODEC_ID_MP2); codec = avcodec_find_decoder(AV_CODEC_ID_OPUS); 出现了: ./decode_audio ./out.opus ./out.pcm ...
  • opus编码器+解码器

    千次阅读 2019-06-19 11:55:33
    将两部分代码放入opus-1.3.1/src/中 gcc test_encode.c -I …/silk -I …/include/ -I …/celt -L …/.libs/ -lopus -o encode -lm gcc test_decode.c -I …/silk -I …/include/ -I …/celt -L …/.libs/ -lopus -o...
  • 这是一个简单的演示,展示了如何使用BASS音频库的附加组件来记录PCM音频数据并将其编码OPUS音频数据。 此OPUS音频数据可以直接播放,也可以解码为PCM音频数据(如果需要)。 确实,我在不到1小时的时间内就完成...
  • 再进行OPUS编码与解码的时候,走了许多弯路。以下介绍一下实现OPUS编码与解码的步骤。 1. 去官网或者github找到能调通的源码,生成出相应的库opus.lib 2.在自己的项目中,把.h和.lib添加到工程里面(VS项目右键...
  • ffmpeg 实现pcmopus

    千次阅读 热门讨论 2020-11-22 14:39:04
    首先可能要重新编译ffmpeg,支持libopus,添加./configure --enable-encoder=opus --enable-encoder=libopus --enable-libopus 具体怎么编译可以自己网上查找,此次不做介绍, 代码 // // Created by hhy on ...
  • PCM音频编码Opus。 将Opus音频解码为PCM。 输入音频的采样率从8000Hz到48000Hz。 不同的镜架尺寸。 单声道或立体声输入音频。 以字节或短裤为单位输入。 以字节或短裤为单位输出。 从字节转换为短裤,...
  • webrtc opus 设置与编码

    千次阅读 2019-08-30 17:23:05
    webrtc opus bg57iv3 扩展头 格式 audio encoder Received session description:{ "sdp":"v=0 o=-74895446367583955282IN IP4127.0.0.1 s=- t=00 a=group:BUNDLE01 a=msid-semantic:WMS stream_id ...
  • Opus功能十分强大,内置了FEC和DTX功能,Opus对FEC的使用方式是动态的,也就是说Opus能根据对端反馈的丢包率来动态的使用FEC功能,48k采样率,单声道、16k码率并且帧长度为20ms时,这种情况下只有在丢包率高于20%的...
  • Opus

    2019-12-28 18:28:09
    文章目录OpusOpus 简介技术特点下载安装Opus 主要接口接口声明使用技巧使用动态编码码率指定编码码率语音信号优化示例 Opus Opus 简介 Opus 编码是一个有损声音编码的格式,由互联网工程任务组(IETF)进来开发,标准...
  • OPUS/SILK/SPEEX 音频编码比较

    万次阅读 2016-01-12 14:35:47
    音频编码调研Opushttp://www.opus-codec.org/Opus是一种混合式(hybrid)的编码器,有Skype的SILK和Xiph.org的CELT融合而成,于2012年9月,由Internet Engineering Task Force (IETF)标准化RFC 6716。Skype官方博客...
  • 大家知道Opus内置了两种编码器:CELT和SILK,并且可以针对采样率、采样间隔、码率、通道数……等属性进行设置。创建的参数设置,是从sdp来的。本文的主要目的,是来看看sdp中的信息,是如何对应到native世界里的代码...
  • opus_encode_decode-1.rar

    2021-09-29 10:38:42
    这个是将pous官方的demo修改为使用VS2015上直接调用的类,可以直接调用将pcm文件编码opus,且将opus解码为pcm文件,opus是一款强大的效率极高的编解码器
  • 而在语音通信界有一个强悍的音频格式编码opus.经过实测,压缩比最高可以达到1:10。100KB 压缩后 10KB虽然是有损压缩,但是根据实际对比试听,几乎听不出差别。而且还原度还比mp3高,压缩比也比mp3高。用来压缩传输...
  • 在Android4.1以后google就提供了MediaCodec这个类来为用户提供音视频的编码解码功能(虽然支持的格式不是很多)。 对于MediaCodec类,我们需要大致介绍一下: 官网提供的概述: In broad terms, a codec ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 805
精华内容 322
关键字:

opuspcm编码