精华内容
下载资源
问答
  • opus解码demo

    2021-04-18 20:19:51
    opus解码demo
  • Opus解码

    2020-02-27 19:26:53
    最近项目中用到了语音编码opus,在网上搜了一下,资料非常少,而且没有一个完整的教程,现在简单记录下来opus的使用方法。 首先介绍一下opus Opus Opus编码器 是一个有损声音编码的格式,由互联网工程任务组(IETF...

    最近项目中用到了语音编码opus,在网上搜了一下,资料非常少,而且没有一个完整的教程,现在简单记录下来opus的使用方法。


    首先介绍一下opus


    Opus

    Opus编码器 是一个有损声音编码的格式,由互联网工程任务组(IETF)进来开发,适用于网络上的实时声音传输,标准格式为RFC 6716。Opus 格式是一个开放格式,使用上没有任何专利或限制。

    特性

    Opus的前身是celt编码器。在当今的有损音频格式争夺上,拥有众多不同编码器的AAC格式打败了同样颇有潜力的Musepack、Vorbis等格式,而在Opus格式诞生后,情况似乎不同了。通过诸多的对比测试,低码率下Opsu完胜曾经优势明显的HE AAC,中码率就已经可以媲敌码率高出30%左右的AAC格式,而高码率下更接近原始音频。

    以上来自百度百科(PS:百度百科对opus的介绍都很少)


    简单来说,opus是一个高保真的适合在网络中传输的开源的语音编码格式,相对于其他编码格式来讲,保真性更好,但体积会稍微大一些。官网地址:http://www.opus-codec.org/


    怎么用呢?

    首先你可以使用编译好的so库直接使用,或者也可以使用源码自己根据需求生成so库来使用,当然,你也可以直接将源码使用到自己工程各中,这就是开源的好处。好了下面介绍如何编译。

    我是通过Eclipse来编译的,首先在opus官网下载源代码,解压。

    编码工作需要ndk编程所以需要一些NDK编程的知识。

    在工程中创建OpusTool类,该类用于调用native层的方法。

    
     
    1. package com.ione.opustool;
    2. public class OpusTool {
    3. public native String nativeGetString();
    4. public native int encode_wav_file(String in_path, String out_path);
    5. public native int decode_opus_file(String in_path, String out_path);
    6. }

    其中nativeGetString()方法是用来测试jni调用是否成功的测试方法,encode_wav_file(String in_path, String out_path);和 decode_opus_file(String in_path, String out_path);分别是用来编解码。以上三个方法均需声明为native,用来调用jni的c函数。然后在项目根目录下打开命令行,使用javah命令生成.h文件,即:

    javah -classpath .\bin\classes -d jni com.ione.opustool.OpusTool

    其中.\bin\classes为指定OpusTool.class的路径,-d jni为在当前目录下生成jni文件夹,用来存放native层代码。回车之后便在工程的根目录下生成了jni文件夹以及com_ione_opustool_OpusTool.h文件。如:




    
     
    1. /* DO NOT EDIT THIS FILE - it is machine generated */
    2. #include <jni.h>
    3. /* Header for class com_ione_opustool_OpusTool */
    4. #ifndef _Included_com_ione_opustool_OpusTool
    5. #define _Included_com_ione_opustool_OpusTool
    6. #ifdef __cplusplus
    7. extern "C" {
    8. #endif
    9. /*
    10. * Class: com_ione_opustool_OpusTool
    11. * Method: nativeGetString
    12. * Signature: ()Ljava/lang/String;
    13. */
    14. JNIEXPORT jstring JNICALL Java_com_ione_opustool_OpusTool_nativeGetString
    15. (JNIEnv *, jobject);
    16. /*
    17. * Class: com_ione_opustool_OpusTool
    18. * Method: encode_wav_file
    19. * Signature: (Ljava/lang/String;Ljava/lang/String;)I
    20. */
    21. JNIEXPORT jint JNICALL Java_com_ione_opustool_OpusTool_encode_1wav_1file
    22. (JNIEnv *, jobject, jstring, jstring);
    23. /*
    24. * Class: com_ione_opustool_OpusTool
    25. * Method: decode_opus_file
    26. * Signature: (Ljava/lang/String;Ljava/lang/String;)I
    27. */
    28. JNIEXPORT jint JNICALL Java_com_ione_opustool_OpusTool_decode_1opus_1file
    29. (JNIEnv *, jobject, jstring, jstring);
    30. #ifdef __cplusplus
    31. }
    32. #endif
    33. #endif

    接下来复制一份 com_ione_opustool_OpusTool.h文件到jni目录,修改名称为com_ione_opustool_OpusTool.c修改内容为:

    
     
    1. #include <com_ione_opustool_OpusTool.h>
    2. JNIEXPORT jstring JNICALL Java_com_ione_opustool_OpusTool_nativeGetString
    3. JNIEnv * env, jobject obj) {
    4. return (*env)->NewStringUTF(env, "Hello Opus");
    5. }
    6. JNIEXPORT jint JNICALL Java_com_ione_opustool_OpusTool_encode_1wav_1file(
    7. JNIEnv *env, jobject obj, jstring wav_path, jstring opus_path) {
    8. return 0;
    9. }
    10. JNIEXPORT jint JNICALL Java_com_ione_opustool_OpusTool_decode_1opus_1file(
    11. JNIEnv *env, jobject obj, jstring wav_path, jstring opus_path) {
    12. return 0;
    13. }
    然后创建并配置makefile和android.mk文件,后面会给出。记得配置NDK_Builder。
    此时可以调用 OpusTool类的nativeGetString()方法查看返回数据是否正常,若为Hello Opus 则jni调用成功。可以继续下面的工作。

    在jni目录下创建libopus文件夹,将Opus源码粘贴到该文件夹下,即celt、include、silk、src文件夹以及config文件,当然不是所有的文件都用的上,可以根据自记得需求进行拷贝。配置好makefile等配置文件后即可编译工程,如果编译顺利,则说明配置文件没有问题,继续操作。在src文件加下创建opus_tool.c文件用来进行音频文件的编解码的c实现。

    opus_tool.c

    
     
    1. /*****************************************************************************
    2. # -*- coding:utf-8 -*-
    3. # author: ione
    4. # create date: 2014-11-27
    5. *****************************************************************************/
    6. #include "android_log.h"
    7. #include "opus.h"
    8. #include "opus_types.h"
    9. #include "opus_multistream.h"
    10. #define SAMPLE_RATE 16000
    11. #define CHANNEL_NUM 1
    12. #define BIT_RATE 16000
    13. #define BIT_PER_SAMPLE 16
    14. #define WB_FRAME_SIZE 320
    15. #define DATA_SIZE 1024 * 1024 * 4
    16. int encode(char* in, int len, unsigned char* opus, int* opus_len) {
    17. int err = 0;
    18. opus_int32 skip = 0;
    19. OpusEncoder *enc = opus_encoder_create(SAMPLE_RATE, CHANNEL_NUM,
    20. OPUS_APPLICATION_VOIP, &err);
    21. if (err != OPUS_OK) {
    22. fprintf( stderr, "cannnot create opus encoder: %s\n",
    23. opus_strerror(err));
    24. enc = NULL;
    25. return -1;
    26. }
    27. opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
    28. opus_encoder_ctl(enc, OPUS_SET_BITRATE(BIT_RATE));
    29. opus_encoder_ctl(enc, OPUS_SET_VBR( 1));
    30. opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY( 10));
    31. opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC( 0));
    32. opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(OPUS_AUTO));
    33. opus_encoder_ctl(enc, OPUS_SET_DTX( 0));
    34. opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC( 0));
    35. opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&skip));
    36. opus_encoder_ctl(enc, OPUS_SET_LSB_DEPTH( 16));
    37. short frame_size = WB_FRAME_SIZE;
    38. int frame_bytes = (frame_size << 1);
    39. opus_int16 *frame = (opus_int16 *) in;
    40. unsigned char *cbits = opus;
    41. while (len > frame_bytes) {
    42. int nbytes = opus_encode(enc, frame, frame_size, cbits + sizeof( char),
    43. 640 - sizeof( short));
    44. if (nbytes > frame_size * 2 || nbytes < 0) {
    45. return -1;
    46. }
    47. cbits[ 0] = nbytes;
    48. frame += WB_FRAME_SIZE;
    49. cbits += nbytes + sizeof( char);
    50. len -= frame_bytes;
    51. *opus_len += nbytes + sizeof( char);
    52. }
    53. opus_encoder_destroy(enc);
    54. return 0;
    55. }
    56. int decode(unsigned char* in, int len, short* out, int* out_len) {
    57. int err = 0;
    58. opus_int32 skip = 0;
    59. *out_len = 0;
    60. OpusDecoder *dec = opus_decoder_create(SAMPLE_RATE, 1, &err);
    61. if (err != OPUS_OK) {
    62. fprintf( stderr, "cannnot decode opus: %s\n", opus_strerror(err));
    63. dec = NULL;
    64. return -1;
    65. }
    66. short frame_size = WB_FRAME_SIZE;
    67. opus_int16 *frame = (opus_int16 *) in;
    68. while (len > 0) {
    69. int nbytes = in[ 0];
    70. if (nbytes <= 0) {
    71. return -1;
    72. }
    73. int decode_len = opus_decode(dec, in + sizeof( char), nbytes, out,
    74. frame_size, 0);
    75. if (decode_len != frame_size) {
    76. return -1;
    77. }
    78. in += sizeof( char) + nbytes;
    79. out += frame_size;
    80. len -= nbytes - sizeof( char);
    81. *out_len += frame_size;
    82. }
    83. opus_decoder_destroy(dec);
    84. return 0;
    85. }
    86. int encode_wav_file(char *in_file_path, char *out_file_path) {
    87. FILE *fin = fopen(in_file_path, "rb");
    88. if (fin == NULL || fin == 0) {
    89. return -1;
    90. }
    91. char *in = ( char*) malloc(DATA_SIZE);
    92. memset(in, 0, DATA_SIZE);
    93. int len = fread(in, 1, DATA_SIZE, fin);
    94. if (len == 0) {
    95. return -1;
    96. }
    97. FILE *fout = fopen(out_file_path, "wb");
    98. if (fout == NULL || fout == 0) {
    99. return -1;
    100. }
    101. unsigned char *out = ( unsigned char*) malloc(DATA_SIZE);
    102. memset(out, 0, DATA_SIZE);
    103. int out_len = 0;
    104. encode(in, len, out, &out_len);
    105. if (len < 0) {
    106. return -1;
    107. }
    108. fwrite(out, 1, out_len * sizeof( unsigned char), fout);
    109. free(in);
    110. free(out);
    111. fclose(fin);
    112. fclose(fout);
    113. return len;
    114. }
    115. int make_wav_header(FILE *out, int len) {
    116. int size = 0;
    117. int *sz = &size;
    118. int number;
    119. int * nm = &number;
    120. // RIFF 4 bytes
    121. fseek(out, 0, SEEK_SET);
    122. fputs( "RIFF", out);
    123. // len 4 bytes
    124. len = (len + 44 - 8);
    125. fwrite(&len, 2, 1, out);
    126. number = 0;
    127. fwrite(nm, 2, 1, out);
    128. // WAVE 4 bytes + "fmt " 4 bytes
    129. fputs( "WAVEfmt ", out);
    130. // size1 4 bytes
    131. number = 16;
    132. fwrite(nm, 2, 1, out);
    133. number = 0;
    134. fwrite(nm, 2, 1, out);
    135. // format tag 2 bytes
    136. number = 1;
    137. fwrite(nm, 2, 1, out);
    138. // channel 2 bytes
    139. number = CHANNEL_NUM;
    140. fwrite(nm, 2, 1, out);
    141. // sample rate 4 bytes
    142. number = SAMPLE_RATE;
    143. fwrite(nm, 2, 1, out);
    144. number = 0;
    145. fwrite(nm, 2, 1, out);
    146. //byte per seconds 4 bytes
    147. number = 22664;
    148. fwrite(nm, 2, 1, out);
    149. number = 0;
    150. fwrite(nm, 2, 1, out);
    151. // block align 2 bytes
    152. number = CHANNEL_NUM * BIT_PER_SAMPLE / 8;
    153. fwrite(nm, 2, 1, out);
    154. // bitPerSample 2 bytes
    155. number = 16;
    156. fwrite(nm, 2, 1, out);
    157. // "data" 4 bytes
    158. fputs( "data", out);
    159. // size2 4 bytes
    160. size = (size - 36);
    161. fwrite(sz, 2, 1, out);
    162. number = 0;
    163. fwrite(nm, 2, 1, out);
    164. return 0;
    165. }
    166. int decode_opus_file(char *in_file_path, char *out_file_path) {
    167. printf( "%s\n", in_file_path);
    168. FILE *fin = fopen(in_file_path, "rb");
    169. if (fin == NULL || fin == 0) {
    170. return -1;
    171. }
    172. unsigned char *in = ( unsigned char *) malloc(DATA_SIZE);
    173. memset(in, 0, DATA_SIZE);
    174. int len = fread(in, 1, DATA_SIZE, fin);
    175. FILE *fout = fopen(out_file_path, "wb");
    176. if (fout == NULL || fout == 0) {
    177. return -1;
    178. }
    179. short *out = ( short *) malloc(DATA_SIZE);
    180. memset(out, 0, DATA_SIZE);
    181. int out_len = 0;
    182. out += 44;
    183. decode(in, len, ( short *) out, &out_len);
    184. if (len < 0) {
    185. return -1;
    186. }
    187. fwrite(out, 1, out_len * sizeof( short), fout);
    188. int err = make_wav_header(fout, out_len);
    189. free(in);
    190. free(out);
    191. fclose(fin);
    192. fclose(fout);
    193. return out_len;
    194. }
    配置makefile文件添加opus_tool.c文件,然后编译,即可在libs目录下生成.so文件


    至此,native层操作已经完成,so库也已经通过编译得到。

    下一篇将会介绍如何使用该so库。

    音频编码之opus(二)

    展开全文
  • FFmpeg解码opus音频文件

    千次阅读 2018-07-13 11:41:15
    FFmpeg库对于音视频的编...下面是对opus解码的主要步骤:导入ffmpeg相关库:#include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavcodec/avcodec.h" #...

    FFmpeg库对于音视频的编解码都做了相应的处理,使用起来相当方便,iOS的原生播放器并不支持opus格式的音频,因此需要对opus格式进行解码。

    下面是对opus解码的主要步骤:

    导入ffmpeg相关库:

    #include "libavformat/avformat.h"
    #include "libswscale/swscale.h"
    #include "libavcodec/avcodec.h"
    #include "libavformat/avio.h"
    #include "libswresample/swresample.h"
    #include "libavutil/avutil.h"
    

    定义相关的属性及变量:

    {
        
        AVFormatContext *pFormatCtx;  //文件格式
        AVCodecContext  *pCodecCtx;
        AVFrame *pFrame;
        char *_audioBuffer;
        NSUInteger _audioBufferSize;
        AVPacket _packet, _currentPacket;
     
    }
     
     
    /* Initialize with movie at moviePath. Output dimensions are set to source dimensions. */
    -(id)initWithAudio:(NSString *)moviePath;
     
    -(void)closeAudio;
     
    @property (nonatomic, retain) NSMutableArray *audioPacketQueue;
    @property (nonatomic, assign) AVCodecContext *_audioCodecContext;  //编码格式
    @property (nonatomic, assign) AudioQueueBufferRef emptyAudioBuffer;  //音频缓存
    @property (nonatomic, assign) int audioPacketQueueSize;
    @property (nonatomic, assign) AVStream *_audioStream;
    

    解码的实现及重采样、播放得引入位置

    -(id)initWithAudio:(NSString *)moviePath {
        
        if (!(self=[super init])) return nil;
        
        AVCodec    *pCodec;
        
        // Register all formats and codecs
        av_register_all();  //注册所有可解码类型
        
        // Open video file
        
        if(avformat_open_input(&pFormatCtx, [moviePath cStringUsingEncoding:NSUTF8StringEncoding], NULL, NULL)!=0)
        {
            NSLog(@"Couldn't open file");
            goto initError; // Couldn't open file
            
        }
        
        // Retrieve stream information 从文件中提取流信息
        if(avformat_find_stream_info(pFormatCtx,NULL)<0)
        {
            NSLog(@"Couldn't find stream information");
            goto initError; // Couldn't find stream information
            
        }
        // Find the first audio stream
        audioStream = -1;
        
        for(int i=0; i<pFormatCtx->nb_streams; i++) //区分视频流和音频流
            if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO) //找到音频流
            {
                audioStream =i;
                break;
            }
        if(audioStream==-1)
        {
            NSLog(@"Didn't find a audio stream");
            goto initError; // Didn't find a audio stream
            
        }
        
        // Get a pointer to the codec context for the audio stream
        
        pCodecCtx = pFormatCtx->streams[audioStream]->codec;
        
        // Find the decoder for the audio stream
        
        pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  //寻找解码器
        if(pCodec==NULL)
        {
            av_log(NULL, AV_LOG_ERROR, "Unsupported codec!\n");
    //        NSLog(@"Codec not found");
            goto initError; // Codec not found
            
        }
        
        //通知编码器我们能处理截断的bit流
        if(pCodec->capabilities& CODEC_CAP_TRUNCATED)
            pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;
        
        
        // Open codec 打开编解码器
     
        if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
        {
            NSLog(@"Could not open codec");
            goto initError; // Could not open codec
            
        }
        
        // Allocate audio frame 为解码帧分配内存,以便存储解码后的内容
        pFrame=avcodec_alloc_frame();
        
        [self setupAudioDecoder];
        
        return self;
        
    initError:
        [self release];
        return nil;
    }
     
    -(void)dealloc {
        
        // Free the YUV frame
        av_free(pFrame);
        
        // Close the codec
        if (_audioCodecContext) avcodec_close(_audioCodecContext);
        
        // Close the audio file
        if (pFormatCtx) avformat_close_input(&pFormatCtx);
        
        [super dealloc];
    }
     
    - (void)setupAudioDecoder {
        
        if (audioStream >= 0) {
            _audioBufferSize = 2048;//192000
            _audioBuffer = av_malloc(_audioBufferSize);
            _inBuffer = NO;
    //        音频的编码格式
            
            uint8_t *pktdata;
            int pktsize;
            int got_frame = 0;
            long start = clock();   
           
            
            if (player != nil) {
                [player stop];
                player = nil;
            }
            player = [[PCMDataPlayer alloc] init];
            
            
            while (av_read_frame(pFormatCtx, &_packet) >= 0) {
                if (_packet.stream_index == audioStream) {
                    pktdata = _packet.data;
                    pktsize = _packet.size;
                    if (pktsize > 0) {
                        //解码
                        int len = avcodec_decode_audio4(pCodecCtx, pFrame, &got_frame, &(_packet));
                        if (len < 0)
                        {
                            printf("Error while decoding.\n");
                            av_free(pFrame);
                            break;  //解包出现问题,跳过此包
                        }
                        
                        if(_audioBuffer > 0)
                        {
                            
                            //此处是调用函数进行重采样
                            int datasize = [self AudioResamplingWithAVCodecContext:pCodecCtx andAVFrame:pFrame andOutfmt:1 andOutchannels:1 andOutRate:44100 andOutbuf:((uint8_t *)_audioBuffer)];
                            
                            [self readNextPCMData:datasize];
                        
                        }
                        pktsize -= len;
                        pktdata += len;
                    }
                }
                av_free_packet(&(_packet));
     
            }
            
            long end = clock();
            printf("cost time :%f\n",(double)(end-start)/(double)CLOCKS_PER_SEC);
            free(_audioBuffer);
            if (pCodecCtx!=NULL)
            {
                avcodec_close(pCodecCtx);
            }
     
            
        } else {
            pFormatCtx->streams[audioStream]->discard = AVDISCARD_ALL;
            audioStream = -1;
        }
    }
     
    - (void)readNextPCMData:(int)readLength
    {
            if (readLength > 0) {
                
                //此处为使用audioqueue播放解码后的pcm音频流的接口
                [player play:_audioBuffer length:readLength];
            } 
            else {
                
                if (player) {
                    [player stop];
                }
                
                if (_audioBuffer) {
                    free(_audioBuffer);
                }
                _audioBuffer = NULL;
            }
    }
     
    - (void)closeAudio
    {
        [player stop];
        primed=NO;
    }
    
    关于audioqueue的使用参见: Audioqueue播放pcm文件
    展开全文
  • Opus音频编解码源码

    2016-12-26 09:30:39
    Opus音频编解码源码opus-1.1.3
  • 音视频编解码--Opus解码系列1

    千次阅读 2020-08-24 09:13:10
    Opus 遵从 Internet Engineering Task Force (IETF)RFC 6716标准,整合了 Skype's SILK 解码和 Xiph.Org's CELT 解码的技术。 新的WebRTC中WebRTC中默认是采用Opus编码,Opus编码是由silk编码和celt编码合并在一起...

     

    1 简介

     

    Opus 是一个完全开源,免费的,通用性高的音频解码器。Opus 在网络上有着无与伦比的交互式语音和音乐传播功能,但也可以用来存储,在流媒体上使用。Opus 遵从 Internet Engineering Task Force (IETF)  RFC 6716 标准,整合了 Skype's SILK 解码和 Xiph.Org's CELT 解码的技术。

    新的WebRTCWebRTC中默认是采用Opus编码,Opus编码是由silk编码和celt编码合并在一起,silk编码是由skype公司开源的一种语音编码,特别适合人声,适合于Voip语音通信。celtmp3,aac类似,适合于传输音乐。

    Opus编解码器的范围从6 kbit / s的窄带单声道语音扩展到510 kbit / s的宽带立体声音乐,算法延迟范围从5 ms到65.2 ms。

     

    1. 控制参数

    Opus编解码器包含许多控制参数,这些参数可以在编解码器的常规操作期间动态更改,而不会中断从编码器到解码器的音频流。 这些参数仅影响编码器,因为它们对位流的任何影响都通过带内信号通知,这样解码器可以解码任意Opus流而无需发送任何带外信号。 任何Opus实施都可以添加或修改这些控制参数,而不会影响互操作性。 下面列出了参考编码器中最重要的编码器控制参数。

    1.1.1 比特率

     

    Opus支持6 kb /秒到510 kb / s的比特率,比特率越高,质量越好。对于20 ms的帧大小,这些是各种配置下Opus的比特率最佳点

     

    1.1.2 通道数

    Opus单独流可以支持单通道和立体声。在立体声解码器中解码单声道帧时,左声道和右声道相同,而在单声道解码器中解码立体声帧时,单声道输出是左声道和右声道的平均值。

    1.1.3 音频带宽

          Opus支持8 kHz(窄带)到48 kHz(全频)。就像通道数一样,任何解码器都可以解码在任何带宽下编码的音频。

    1.1.4 帧长

    Opus支持帧长度为:2.5、5、10、20、40、60毫秒。Opus还支持将多个帧组成一个120ms的包进行处理。在实际应用中,每秒发送较少的数据包会略微提高编码效率,也会降低比特率,因为它减少了IP,UDP和RTP标头的开销。 但是,这样操作增加了等待时间和对数据包丢失的敏感性,丢失一个数据包会造成更大一部分音频的丢失。同时对于20 ms以上的帧,音频增益值会变小。 因此,对于大多数应用而言,20 ms帧是一个不错的选择。

    1.1.5 复杂度

    Opus编码过程的各个方面都可以在CPU复杂度和质量/比特率之间进行权衡。 在参考编码器中,使用从0到10的整数选择复杂度,其中0是最低的复杂度,而10是最高的。 这种折衷计算有很多例子,包括:

    • 音调分析白化滤镜[WHITENING]的顺序,
    • 短期噪声整形滤波器的阶数,
    • 残留信号的延迟决策量化中的状态数,以及
    • 使用某些比特流功能,例如可变的时频分辨率和音调后置滤波器。

    1.1.6 丢包弹性

       音频编解码器通常利用帧间相关性来降低误码率,但代价是错误传播:当丢失一个数据包之后,在解码器能够准确地重建语音信号之前,需要优先接收几个数据包。 Opus利用帧间相关性的程度可以即时调整,以在比特率和错误传播量之间进行有效权衡。

     

    1.1.7 前向纠错(FEC)

       提供防止丢包的鲁棒性的另一种机制是带内前向纠错(FEC)。被确定为包含感知性重要语音信息(如开始或瞬变)的数据包将再次以较低的比特率进行编码,并将此重新编码的信息添加到后续数据包中。

     

    1.1.8 恒定/可变比特率

     

       使用可变比特率(VBR)(默认设置)时,Opus效率更高。当在相对较慢的连接上需要低延迟传输时,也可以使用受约束的VBR。这以模拟“位存储库”的方式使用VBR,基本等效于MP3(MPEG 1,第3层)和AAC(高级音频编码)称为CBR(即由于位存储库不是真正的CBR)。在某些特殊应用中,确实需要CBR技术,其主要原因有两个两个方面:

    • 当传输时,每个压缩帧仅仅支持一种固定大小的情况
    • 当一个高度受限或高度敏感的音频流被用于加密时,例如是否包含录制提示、STRP-VBR等。

     

       只要速率不受输入信号的影响(例如,为了匹配不断变化的网络条件),即使使用敏感数据,也可以允许比特率变化。为此,应用程序仍应在CBR模式下运行Opus,但在发送每个数据包之前需要更改目标速率。

     

    1.1.9 不连续传输(DTX)

     

       不连续传输(DTX)降低了静音或背景噪声期间的比特率。启用DTX时,每400毫秒仅编码一帧。

    2 Opus数据帧

    Opus编码器产生“数据包”,每个“数据包”是一组连续的字节,旨在作为单个单元进行传输。这里描述的数据包不包括通常在传输层数据包中发现的IP,UDP或RTP包头部分。一个数据包可以包含多个音频帧,只要它们共享一组公共参数即可,包括编解码操作模式,音频带宽,帧大小和通道数(单声道与立体声)。

    2.1 TOC字节

           Opus封包务必包含至少一个字节。 该字节形成目录表头(Table-Of-Contents,TOC),,该字节协商数据包使用哪种模式和配置。 它由配置编号“ config”,立体声标志“ s”和帧计数Code “ c”组成,其排列如图1所示。

     

    TOC字节的前五位(标记为“ config”)对32种可能的操作模式,音频带宽和帧大小配置之一进行编码。 如上所述,可以在三种可能的操作模式下组合LP(SILK)层和MDCT(CELT)层:

        1.仅用SILK模式:用于带宽小于或等于WB的低比特率连接,

        2.混合模式:以中等比特率进行SWB或FB语音的混合(SILK + CELT)模式,以及

    3.仅用CELT模式:用于非常低延迟的语音传输以及音乐传输(从NB到FB)。

     

    32种可能的配置分别标识数据包使用这些操作模式中的哪一种,以及音频带宽和帧大小。 表2列出了每种配置的参数。

     

    每个范围中的配置编号(例如,对于NB SILK-仅0 ... 3)以相同的顺序依次对应帧大小内的各个值大小。 例如,配置0的帧大小为10 ms,配置3的帧大小为60 ms。

     

    标记为“ s”的另一位用信号表示单声道与立体声,其中0表示单声道,而1表示立体声。

    标记为“ c”的TOC字节的其余两位编码每个数据包的帧数(Code 0到3),如下所示:

        0:数据包中的1个帧

        1:数据包中的2个帧,每个数据帧具有相同的压缩大小

        2:数据包中的2个帧,每个数据帧具有不同的压缩大小

        3:数据包中任意数量的帧

        根据“ c”的值将数据包称为Code 0数据包,Code 1数据包等。

    2.2 帧打包方式

    本节介绍如何根据TOC字节中每个“ c”值打包帧。

    2.2.1 帧长编码

    当一个数据包包含多个VBR帧(即Code2或3)时,这些帧中一个或多个的压缩长度以一个或两个字节的序列表示,第一个字节的含义如下:

        0:无帧(不连续传输(DTX)或丢失的数据包)

        1 ... 251:帧的长度(以字节为单位)

        252 ... 255:第二个帧的字节长度。 总长度是(second_byte*4)+first_byte

    2.2.2 Code 0:单帧包

           对于Code 0的封包来说,TOC字节之后紧跟这一个帧的N-1个字节的压缩数据(其中N是数据包的大小),如下图所示:

     

    2.2.3 Code 1:相同压缩数据大小的双帧包

    对于Code 1的封包来说,TOC字节之后紧跟着是第一帧的(N-1)/2个字节的压缩数据,然后是第二帧的(N-1)/2个字节的压缩数据。针对Code 1类型数据包,其压缩数据的有效载荷字节的数目N-1,必须是偶数。

     

    2.2.4 Code 2:不同压缩数据大小的双帧包

    对于Code 2数据包,TOC字节之后紧跟着一个或两字节的序列,用于指明第一帧的长度(在图4中标记为N1),长度之后跟N1字节的第一帧压缩数据。 剩余的(N-N1-2) 或 (N-N1-3)字节是第二帧的压缩数据,如图4所示。Code 2包必须包含足够的字节来表示有效长度。 例如,一个1字节的Code 2数据包总是无效的,而第二个字节在252 ... 255范围内的2字节的Code 2数据包也是无效的。第一帧的长度N1,也必须是不大于解码所有Code 2数据包的长度后剩余的有效负载大小。 例如,这也会使第二字节在1 ... 251范围内的2字节Code 2数据包也无效(唯一有效的2字节Code 2数据包是两个帧的长度均为零的数据包)。

     

    2.2.5 Code 3:包含确定帧数的数据包

    Code 3数据包通过增加称为“ Opus填充”的附加填充以明确制定当前数据包中的帧数,同时指示此填充是在Opus层而不是在传输层添加的。 Code 3的数据包必须至少有2个字节。 TOC字节后跟一个字节,该字节在第2到7位(图5中标记为“ M”)对数据包中的帧数进行编码,而第1位指示是否插入了Opus填充(图5中标记为“ p”) ),位0表示VBR信息(在图5中标记为“ v”)。 M的值绝对不能为零,并且数据包中包含的音频持续时间不得超过120ms。 这将任何帧大小的最大帧数限制为48(对于2.5 ms帧),同时限制最长帧的帧的大小。 图5说明了帧计数字节的布局。

    使用Opus填充时,填充的字节数将被编码,紧跟在帧字节之后的字节中。从0 ... 254的值用于指示填充大小的字节为0 ... 254个填充字节,该字节标识总共的填充字节大小。如果值为255,则标识填充的大小为254个字节,再加上下一个字节中填充值。在这种情况下,数据包中必须至少有一个字节。额外的填充字节出现在数据包的末尾,必须由编码器设置为零,以避免创建隐蔽通道。但是,解码器必须接受填充字节的任何值。

     

       尽管此编码提供了多种方法来指示给定数量的填充字节,但是每种编码都使用不同数量的字节来指示填充大小,因此将在不同程度上增加总数据包的大小。例如,要向数据包中添加255个字节,请将填充位p设置为1,在帧计数字节后插入一个值为254的单个字节,然后将254个填充值为零的填充字节附加到包的末尾。要向数据包中添加256个字节,请将填充位设置为1,在帧计数字节后分别插入两个字节,其值分别为255和0,并在数据包末尾附加254个填充值为零的填充字节。通过多次使用值255,可以创建任何特定的所需大小的数据包。令P为用于指示填充大小的标头字节数加上其自身的填充字节数(即P为添加到数据包的总字节数)。然后,P必须不超过N-2。

     

       在CBR情况下,令R = N-2-P为减去(可选)填充后数据包中剩余的字节数。然后,每个帧的压缩长度(以字节为单位)等于R / M。值R必须是M [R6]的非负整数倍。随后是所有M帧的压缩数据,每个压缩数据的大小为R / M字节,如图6所示。

     

     

    展开全文
  • 解码库在windows、android、linux上使用 opus windows上使用 软件环境 windows10 Visual Studio 2017 opus 1.3 下载地址: eclipse 2018-12 java8 使用Visual Studio 2017编译opus库 打开->项目/解决方案->选择win...
  • opus解码移植stm32f407

    2018-07-25 09:54:57
    移植好的opus音频编码,测试编解码正常。采用freertos实时系统。
  • 下载完成后解压源码,在VS2015上打开源码 opus-1.3-beta\win32\VS2015\opus.sln,共有5个项目,其中opus项目是opus解码器的主要实现以及对外部提供接口,实际写程序的时候一定是基于该项目中接口的,opus_demo是...

    1.源码下载及编译
    编解码器版本采用opus-1.3-beta,opus官网:https://www.opus-codec.org/

    下载完成后解压源码,在VS2015上打开源码  opus-1.3-beta\win32\VS2015\opus.sln,共有5个项目,其中opus项目是opus编解码器的主要实现以及对外部提供接口,实际写程序的时候一定是基于该项目中接口的,opus_demo是基于opus项目写的一个命令行程序,可以通过命令行调用opus项目的编解码功能,操作实际的音频数据。另外三个是基于opus项目的测试程序。分别测试编码功能、解码功能以及API接口

    右键点击opus_demo,设为启动项,并生成解决方案。生成之后就可以关闭VS了(因为主要在命令行下进行编解码操作)。

    进入 opus-1.3-beta\win32\VS2015\Win32\Debug,里面有一个opus_demo.exe,在当前文件夹下SHIFT+右键选择在此处打开命令窗口,在命令台下使用源码目录README文档中的指令就可以操作。

    Usage: opus_demo [-e] <application> <sampling rate (Hz)> <channels (1/2)>
             <bits per second> [options] <input> <output>
           opus_demo -d <sampling rate (Hz)> <channels (1/2)> [options]
             <input> <output>
     
    mode: voip | audio | restricted-lowdelay
    options:
      -e                : only runs the encoder (output the bit-stream)
      -d                : only runs the decoder (reads the bit-stream as input)
      -cbr              : enable constant bitrate; default: variable bitrate
      -cvbr             : enable constrained variable bitrate; default:
                          unconstrained
      -bandwidth <NB|MB|WB|SWB|FB>
                        : audio bandwidth (from narrowband to fullband);
                          default: sampling rate
      -framesize <2.5|5|10|20|40|60>
                        : frame size in ms; default: 20
      -max_payload <bytes>
                        : maximum payload size in bytes, default: 1024
      -complexity <comp>
                        : complexity, 0 (lowest) ... 10 (highest); default: 10
      -inbandfec        : enable SILK inband FEC
      -forcemono        : force mono encoding, even for stereo input
      -dtx              : enable SILK DTX
      -loss <perc>      : simulate packet loss, in percent (0-100); default: 0
     
    input and output are little-endian signed 16-bit PCM files or opus
    bitstreams with simple opus_demo proprietary framing.


     举例:

    opus_demo -e voip 48000 1 16000 -framesize 10 C:\Users\tengxc\Desktop\zhou.pcm c:\Users\tengxc\Desktop\zhou.opus


    2.opus编码器接口
    opus编解码器接口的定义主要在opus工程源码中的opus.h文件中。以C语言的方式提供。

    编码器接口中有一个重要的结构体变量 struct OpusEncoder,它表示编码器状态,结构体中包含很多变量用来统计opus编码器的状态,使用时用先 typedef struct OpusEncoder OpusEncoder;


    创建编码器:
    opus接口中创建编码器的方式有两种,因此与之对应的释放编码器的方式也有两种,创建编码器时可以选用
    OpusEncoder* enc;
    OpusEncoder  *opus_encoder_create(opus_int32 Fs, int channels, int application, int *error);
    举例:enc = opus_encoder_create(16000, 1,  OPUS_APPLICATION_VOIP, &err_num);

    参数:采样率、通道数、应用类型、错误码

    或者
    OpusEncoder *enc;
    size = opus_encoder_get_size(channels);
    enc = malloc(size);
    err_num = opus_encoder_init(enc, Fs, channels, application);
    参数:同上

    两种创建编码器的方式差异在于表示编码器状态的结构体内存申请方式不一样,第一种内存的申请方式为opus_encoder_create函数申请内存后返回一个首地址指针,第二种是自己申请一块内存后用opus_encoder_init函数填充。


    编码音频数据:
    编码音频数据也分为两种方式,一种是float型,一种是int型。
    opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes);
    举例:opus_int32 len = opus_encode(enc, pcm, 960, out, 4000);
    参数:解码器状态指针、音频原始数据、以样本个数为单位的帧长度、编码完成的数据、最大的编码后数据长度

    或者
    opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes);
    举例:opus_int32 len = opus_encode(enc, pcm, 960, out, 4000);
    参数:同上

    在这两种编码方式中,浮点型编码是更加损耗CPU,但是因此获得的编码精度更高,所以音质更加精准,而16位in整型编码是更多机器支持的,编码精度稍低,对CPU的损耗也小一点。

    释放编码器:
    当采用opus_encoder_create创建编码器时,需要用opus_encoder_destroy释放。
    opus_encoder_destroy(OpusEncoder *st);
    举例:opus_encoder_destroy(enc);
    参数:编码器状态结构指针

    当采用opus_encoder_init创建编码器时,需要用free释放。
    举例:free(enc);        //由于前面是使用malloc申请的内存
    参数:编码器状态结构指针

    修改编码参数:
    opus支持编码器运行时的参数修改。
    int opus_encoder_ctl(OpusEncoder *st, int request, ...);
    举例:opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
    参数:编码器状态结构指针、设置的参数

    展开全文
  • opus解码的使用

    万次阅读 2017-09-18 11:43:34
    opus简介以及优点:Opus是一个有损声音编码的格式,由Xiph.Org基金会开发,之后由互联网工程任务组(IETF)进行标准化,目标用希望用单一格式包含声音和语音,取代Speex和Vorbis,且适用于网络上低延迟的即时声音...
  • ffmpeg对48k采样率,双声道的opus解码成pcm,再把pcm重采样成8k,16k,44.1k,48k的采样率等操作 ffmpeg对pcm编程成opus,可以对8k,16k,32k采样率的pcm重采样48k,再编码成opus,目前只能对双声道的pcm做重采样,...
  • opus 0.16编解码工具

    2013-05-30 13:53:59
    opus解码器,可以将wav文件转化为opus文件格式
  • opus音频编解码算法

    2016-11-07 15:04:19
    资源中有三个文档:opus设计标准,opus-API函数使用手册,opus-OGG封包格式,这些文档也可以在opus官网上下载。
  • opus音频编解码实现walkgeek的例程,供opus爱好者学习
  • Opus 应该提高 mp3、vorbis 和 speex 编解码器的质量/比特率。 它可用于减少手机游戏中音乐和语音对话的大小。 opus的相关资料: 设置/安装 你需要 HAXE 和 OPENFL。 haxelib install openfl-opus 用法/API ...
  • opus-1.2.1音频编码解码,android opus语音编解码库的生成和应用
  • Opus 音频编解码器的 Ada 绑定。 地位 只绑定了编码器和解码器。 TODO 显示了一些尚未绑定的函数。 其他一些函数,如opus_encoder_get_size 、 opus_encoder_init 、 opus_decoder_get_size和opus_decoder_init将...
  • 如果我们想在浏览器中将原始的opus数据包解码为PCM,则有两种方法可以做到这一点: 使用可使用Emscripten移植的javascript版本的解码器。 使用Web Audio API方法音频 第一种方法非常简单,但是它需要携带一个大...
  • 该项目将指导新手玩家怎么将一个opus音频库,编译为js,并运行在浏览器中。 emscripten环境搭建 由于emscripten的环境搭建需要很多安装包下载速度极慢,这里推介大家使用docker镜像来编译 这里推介两个镜像 ...
  • 使用crossbridge编译后的opus解码库,可自己用c编写调用代码,再使用crossbridge编译成swc就可以在flash中使用了
  • 最新的opus音频编解码源代码

    热门讨论 2013-07-18 16:01:09
    opus的源代码 ,很好的编解码开源代码
  • 再进行OPUS编码与解码的时候,走了许多弯路。以下介绍一下实现OPUS编码与解码的步骤。 1. 去官网或者github找到能调通的源码,生成出相应的库opus.lib 2.在自己的项目中,把.h和.lib添加到工程里面(VS项目右键...
  • 解码器杂谈:浅析 Opus

    千次阅读 2019-05-24 15:49:31
    谈起 Opus,对于编解码器有所了解的同学也许会知道,Opus 是由两个编解码器——Silk 和 Celt 融合而成。为什么来自两个组织的编解码器会合二为一,Opus 的性能又如何,本文将简述一下 Opus 的前世今生和部分技术分析...
  • 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...
  • Opus解码遇到的怪事

    千次阅读 2017-07-01 17:23:00
    UMU 的代码是把源文件解码出来的 sample 先 resample 成 AV_SAMPLE_FMT_S16 格式,然后再交给 opus encoder 去编码的,但是编完用 ffprobe 查看,发现 SampleFormat 变成 AV_SAMPLE_FMT_FLTP。   那么第一个...
  • 作品 获取测试文件 $ curl -OL https://opus-codec.org/docs/opus_testvectors-rfc8251.tar.gz
  • 关于audiopus是Opus 1.3版的高级绑定。 最初,此板条箱旨在使宁静板条箱能够在Wi上构建音频功能。关于audiopus是Opus 1.3版的高级绑定。 最初,制作此板条箱是为了让宁静的板条箱能够在Windows,Linux和Mac上构建...
  • 这是 VHDL 中 IETF RFC 6716 Opus 音频编解码器的 ip 核心实现。 执照 版权所有 Gokul Das B 2015 此 HDL 和相关的源代码(文档)描述了开放硬件,并根据 CERN OHL v. 1.2 获得许可。 您可以根据 CERN OHL v.1.2 ...
  • opus1.2.1 语音编解码 android cmake ndk

    千次阅读 2018-07-18 16:44:46
    opus 是 speex 的升级版  编译方式: 1.官网下载opus https://opus-codec.org/ ...3.把opus 源码copy到cpp目录下 我做了一些删减 把很多没用的东西删减了,只用了跟编解码相关联的代码。(可以看我上传的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,980
精华内容 1,192
关键字:

opus解码