音频压缩编码 opus 附完整C++代码示例
绝大数人都知道mp3格式编码,以及aac,amr等压缩格式编码。
而在语音通信界有一个强悍的音频格式编码opus.
经过实测,压缩比最高可以达到1:10。
100KB 压缩后 10KB
虽然是有损压缩,
但是根据实际对比试听,
几乎听不出差别。
而且还原度还比mp3高,压缩比也比mp3高。
用来压缩传输音频,绝对是一大杀器。
项目官方地址:
维基上的描述:
Opus是一个有损声音编码的格式,由Xiph.Org基金会开发,之后由互联网工程任务组(IETF)进行标准化,目标用希望用单一格式包含声音和语音,取代Speex和Vorbis,且适用于网络上低延迟的即时声音传输,标准格式定义于RFC 6716文件。Opus格式是一个开放格式,使用上没有任何专利或限制。
Opus集成了两种声音编码的技术:以语音编码为导向的SILK和低延迟的CELT。Opus可以无缝调节高低比特率。在编码器内部它在较低比特率时使用线性预测编码在高比特率时候使用变换编码(在高低比特率交界处也使用两者结合的编码方式)。Opus具有非常低的算法延迟(默认为22.5 ms),非常适合用于低延迟语音通话的编码,像是网络上的即时声音流、即时同步声音旁白等等,此外Opus也可以通过降低编码比特率,达成更低的算法延迟,最低可以到5 ms。在多个听觉盲测中,Opus都比MP3、AAC、HE-AAC等常见格式,有更低的延迟和更好的声音压缩率。
更重要的是要看这条:
Opus被提出用于在IETF上标准化新的音频格式,最终被IETF编解码器工作组接受和授予。它基于Xiph.Org基金会和Skype技术公司两项最初分开的标准提案。其主要开发人员包括Jean-Marc Valin(Xiph.Org,Octasic,Mozilla Corporation),Koen Vos(Skype)和Timothy B. Terriberry(Xiph.Org,Mozilla Corporation)。其中包括Juin-Hwey(Raymond)Chen(Broadcom),Gregory Maxwell(Xiph.Org,Wikimedia)和Christopher Montgomery(Xiph.Org)也参与其中。
这个项目被几大公司加持,也难怪能有如此出色的压缩比。
由于是纯C代码,比较好理解。
抽了点空,俺写个wav压缩解压的示例代码,
分享出来,权当抛砖引玉。
示例代码位置:
https://github.com/cpuimage/opus/blob/master/example/opus.cpp
贴上完整C++代码:
#include <opus_types.h> #include <opus.h> #include <cstring> #include <memory> #include <vector> // https://github.com/mackron/dr_libs/blob/master/dr_wav.h #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" #define FRAME_SIZE 480 #define MAX_FRAME_SIZE (6*FRAME_SIZE) #define MAX_CHANNELS 1 #define MAX_PACKET_SIZE (3*1276) #pragma pack(push) #pragma pack(1) struct WavInfo { uint16_t channels; uint32_t sampleRate; uint32_t bitsPerSample; }; #pragma pack(pop) #ifndef nullptr #define nullptr NULL #endif class FileStream { public: FileStream() { cur_pos = ; } void Append(const char *data, size_t size) { if (cur_pos + size > Size()) { vec.resize(cur_pos + size); } memcpy(vec.data() + cur_pos, data, size); cur_pos += size; } void AppendU32(uint32_t val) { Append((char *) (&val), sizeof(val)); } char *Data() { return vec.data(); } size_t Size() { return vec.size(); } size_t Read(void *buff, size_t elemSize, size_t elemCount) { size_t readed = std::min((vec.size() - cur_pos), (elemCount * elemSize)) / elemSize; ) { memcpy(buff, vec.data() + cur_pos, readed * elemSize); cur_pos += readed * elemSize; } return readed; } bool SeekCur(int offset) { if (cur_pos + offset > vec.size()) { cur_pos = !vec.empty() ? (vec.size() - ) : ; return false; } else { cur_pos += offset; return true; } } ) { cur_pos = ; return SeekCur(offset); } bool WriteToFile(const char *filename) { FILE *fin = fopen(filename, "wb"); if (!fin) { return false; } fseek(fin, , SEEK_SET); fwrite(vec.data(), sizeof(char), vec.size(), fin); fclose(fin); return true; } bool ReadFromFile(const char *filename) { FILE *fin = fopen(filename, "rb"); if (!fin) { return false; } fseek(fin, , SEEK_END); long fileSize = ftell(fin); vec.resize(static_cast<unsigned long long int>(fileSize)); fseek(fin, , SEEK_SET); fread(vec.data(), sizeof(char), vec.size(), fin); fclose(fin); return true; } private: std::vector<char> vec; size_t cur_pos; }; bool Wav2Opus(FileStream *input, FileStream *output); bool Opus2Wav(FileStream *input, FileStream *output); bool wav2stream(char *input, FileStream *output); bool stream2wav(FileStream *input, char *output); bool wavWrite_int16(char *filename, int16_t *buffer, int sampleRate, uint32_t totalSampleCount) { drwav_data_format format = {}; format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64. format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes. format.channels = ; format.sampleRate = (drwav_uint32) sampleRate; format.bitsPerSample = ; drwav *pWav = drwav_open_file_write(filename, &format); if (pWav) { drwav_uint64 samplesWritten = drwav_write(pWav, totalSampleCount, buffer); drwav_uninit(pWav); if (samplesWritten != totalSampleCount) { fprintf(stderr, "ERROR\n"); return false; } return true; } return false; } int16_t *wavRead_int16(char *filename, uint32_t *sampleRate, uint64_t *totalSampleCount) { unsigned int channels; int16_t *buffer = drwav_open_and_read_file_s16(filename, &channels, sampleRate, totalSampleCount); if (buffer == nullptr) { fprintf(stderr, "ERROR\n"); return nullptr; } ) { drwav_free(buffer); buffer = nullptr; *sampleRate = ; *totalSampleCount = ; } return buffer; } bool wav2stream(char *input, FileStream *output) { uint32_t sampleRate = ; uint64_t totalSampleCount = ; int16_t *wavBuffer = wavRead_int16(input, &sampleRate, &totalSampleCount); if (wavBuffer == nullptr) return false; WavInfo info = {}; info.bitsPerSample = ; info.sampleRate = sampleRate; info.channels = ; output->SeekBeg(); output->Append((char *) &info, sizeof(info)); output->Append((char *) wavBuffer, totalSampleCount * sizeof(int16_t)); free(wavBuffer); return true; } bool stream2wav(FileStream *input, char *output) { WavInfo info = {}; input->SeekBeg(); size_t read = input->Read(&info, ); ) { return false; } size_t totalSampleCount = (input->Size() - ; return wavWrite_int16(output, (int16_t *) (input->Data() + sizeof(info)), info.sampleRate, static_cast<uint32_t>(totalSampleCount)); } bool Wav2Opus(FileStream *input, FileStream *output) { WavInfo in_info = {}; input->SeekBeg(); size_t read = input->Read(&in_info, ); ) { return false; } uint32_t bitsPerSample = in_info.bitsPerSample; uint32_t sampleRate = in_info.sampleRate; uint16_t channels = in_info.channels; ; if (channels > MAX_CHANNELS) { return false; } OpusEncoder *encoder = opus_encoder_create(sampleRate, channels, OPUS_APPLICATION_AUDIO, &err); ) { fprintf(stderr, "failed to create an encoder: %s\n", opus_strerror(err)); if (!encoder) { opus_encoder_destroy(encoder); } return false; } const uint16_t *data = (uint16_t *) (input->Data() + sizeof(in_info)); size_t size = (input->Size() - ; opus_int16 pcm_bytes[FRAME_SIZE * MAX_CHANNELS]; size_t index = ; size_t step = static_cast<size_t>(FRAME_SIZE * channels); FileStream encodedData; unsigned char cbits[MAX_PACKET_SIZE]; size_t frameCount = ; size_t readCount = ; while (index < size) { memset(&pcm_bytes, , 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(encoder, pcm_bytes, channels * FRAME_SIZE, cbits, MAX_PACKET_SIZE); ) { fprintf(stderr, "encode failed: %s\n", opus_strerror(nbBytes)); break; } ++frameCount; encodedData.AppendU32(static_cast<uint32_t>(nbBytes)); encodedData.Append((char *) cbits, static_cast<size_t>(nbBytes)); } WavInfo info = {}; info.bitsPerSample = bitsPerSample; info.sampleRate = sampleRate; info.channels = channels; output->SeekBeg(); output->Append((char *) &info, sizeof(info)); output->Append(encodedData.Data(), encodedData.Size()); opus_encoder_destroy(encoder); return true; } bool Opus2Wav(FileStream *input, FileStream *output) { WavInfo info = {}; input->SeekBeg(); size_t read = input->Read(&info, ); ) { return false; } int channels = info.channels; if (channels > MAX_CHANNELS) { return false; } output->SeekBeg(); output->Append((char *) &info, sizeof(info)); ; OpusDecoder *decoder = opus_decoder_create(info.sampleRate, channels, &err); ) { fprintf(stderr, "failed to create decoder: %s\n", opus_strerror(err)); if (!decoder) { opus_decoder_destroy(decoder); } return false; } unsigned char cbits[MAX_PACKET_SIZE]; opus_int16 out[MAX_FRAME_SIZE * MAX_CHANNELS]; ; while (true) { uint32_t nbBytes; size_t readed = input->Read(&nbBytes, ); ) { break; } if (nbBytes > sizeof(cbits)) { fprintf(stderr, "nbBytes > sizeof(cbits)\n"); break; } readed = input->Read(cbits, sizeof(char), nbBytes); if (readed != nbBytes) { fprintf(stderr, "readed != nbBytes\n"); break; } ); ) { fprintf(stderr, "decoder failed: %s\n", opus_strerror(frame_size)); break; } ++frameCount; output->Append((])); } opus_decoder_destroy(decoder); return true; } void splitpath(const char *path, char *drv, char *dir, char *name, char *ext) { const char *end; const char *p; const char *s; ] && path[] == ':') { if (drv) { *drv++ = *path++; *drv++ = *path++; *drv = '\0'; } } else if (drv) *drv = '\0'; for (end = path; *end && *end != ':';) end++; for (p = end; p > path && *--p != '\\' && *p != '/';) if (*p == '.') { end = p; break; } if (ext) for (s = end; (*ext = *s++);) ext++; for (p = end; p > path;) if (*--p == '\\' || *p == '/') { p++; break; } if (name) { for (s = p; s < end;) *name++ = *s++; *name = '\0'; } if (dir) { for (s = path; s < p;) *dir++ = *s++; *dir = '\0'; } } void opus2wav(const char *in_file, char *out_file) { FileStream input; FileStream output; input.ReadFromFile(in_file); Opus2Wav(&input, &output); stream2wav(&output, out_file); } void wav2opus(char *in_file, char *out_file) { FileStream input; FileStream output; wav2stream(in_file, &input); Wav2Opus(&input, &output); output.WriteToFile(out_file); } int main(int argc, char *argv[]) { printf("Opus Demo\n"); printf("blog:http://tntmonks.cnblogs.com/\n"); printf("e-mail:gaozhihan@vip.qq.com\n"); ) ; ]; ]; ]; ]; ]; ]; splitpath(in_file, drive, dir, fname, ext); ) { sprintf(out_file, "%s%s%s.out", drive, dir, fname); wav2opus(in_file, out_file); } ) { sprintf(out_file, "%s%s%s_out.wav", drive, dir, fname); opus2wav(in_file, out_file); } printf("done.\n"); printf("press any key to exit.\n"); getchar(); ; }
项目地址:
https://github.com/cpuimage/opus
示例具体流程为:
1.压缩
加载wav(拖放wav文件到可执行文件上)->压缩->保存为out
2.解压
加载out(拖放out文件到可执行文件上)->解压->保存为wav
示例比较简单,用cmake即可进行编译示例代码,详情见CMakeLists.txt。
若有其他相关问题或者需求也可以邮件联系俺探讨。
邮箱地址是:
gaozhihan@vip.qq.com
音频压缩编码 opus 附完整C++代码示例的更多相关文章
- 音频降噪算法 附完整C代码
降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...
- 基于RNN的音频降噪算法 (附完整C代码)
前几天无意间看到一个项目rnnoise. 项目地址: https://github.com/xiph/rnnoise 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可 ...
- 基于傅里叶变换的音频重采样算法 (附完整c代码)
前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...
- 音频增益响度分析 ReplayGain 附完整C代码示例
人们所熟知的图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了 ...
- 音频增益响度分析 ReplayGain 附完整C代码示例【转】
转自:http://www.cnblogs.com/cpuimage/p/8846951.html 人们所熟知的图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像 ...
- WebRTC 音频采样算法 附完整C++示例代码
之前有大概介绍了音频采样相关的思路,详情见<简洁明了的插值音频重采样算法例子 (附完整C代码)>. 音频方面的开源项目很多很多. 最知名的莫过于谷歌开源的WebRTC, 其中的音频模块就包 ...
- 音频自动增益 与 静音检测 算法 附完整C代码
前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...
- 音频自动增益 与 静音检测 算法 附完整C代码【转】
转自:https://www.cnblogs.com/cpuimage/p/8908551.html 前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用 ...
- 经典傅里叶算法小集合 附完整c代码
前面写过关于傅里叶算法的应用例子. <基于傅里叶变换的音频重采样算法 (附完整c代码)> 当然也就是举个例子,主要是学习傅里叶变换. 这个重采样思路还有点瑕疵, 稍微改一下,就可以支持多通 ...
随机推荐
- 深入解析C语言数组和指针
概述 指针是C语言的重点,同时也是让初学者认为最难理解的部分.有人说它是C语言的灵魂,只有深入理解指针才能说理解了C语言.暂且撇开这些观点不谈.这章是我在阅读<C和指针>这本书的读书笔记. ...
- 《HelloGitHub》第 24 期(两周年)
公告 今天是<HelloGitHub>月刊 两周年.当时发布第一期的时候,根本没有想到可以走到现在. 这两年,HelloGitHub 项目有过辉煌的时刻:连续 3 天 GitHub 趋势首 ...
- easyui控件写法造成的错误
<input id="driver" name="driver" class="easyui-combobox" data-optio ...
- 20162330 实验二 《Java面向对象程序设计》 实验报告
2016-2017-2 实验报告目录: 1 2 3 4 5 20162330 实验二 <Java面向对象程序设计> 实验报告 课程名称:<程序设计与数据结构> 学生班级:162 ...
- 20162318 实验三《 敏捷开发与XP实践》实验报告
北京电子科技学院(BESTI) 实 验 报 告 课程:程序设计与数据结构 班级:1623班 姓名:张泰毓 指导老师:娄老师.王老师 实验日期:2017年5月12日 实验密级:非密级 实验器材:带Lin ...
- 2017-2018-1 1623 bug终结者 冲刺006
bug终结者 冲刺006 by 20162328 蔡文琛 今日任务:音频素材添加 又是新的一天,小组项目有了很大的起色,已经可以在手机上试玩了. 添加背景音乐能使我们的游戏锦上添花. 音频资源需求 需 ...
- LR回放https协议脚本失败: 错误 -27778: 在尝试与主机“www.baidu.com”connect 时发生 SSL 协议错误
今天用LR录制脚本协议为https协议,回放脚本时出现报错: Action.c(14): 错误 -27778: 在尝试与主机"www.baidu.com"connect 时发生 S ...
- SENet
 \(\bf F_{tr}\) 为标准卷积操作 \(\bf F_{sq}\) 为 Global Average Pooling \(\bf F_{ex}\) 为两层全连接网络(可以看做两个1×1卷积 ...
- crlf注入攻击
1.crlf 注入攻击. 原理:http数据包通过\r\n\r\n来分开http header何http body 实现:首先这种攻击发生在应用层,且发生在服务器返回给我们的http reponse没 ...
- PCB名詞解釋:通孔、盲孔、埋孔(转载)
文章转载自:https://www.researchmfg.com/2011/07/pth-blind-hole-buried-hole/ PCB名詞解釋:通孔.盲孔.埋孔 Posted by 工作熊 ...