当前位置: 首页 > news >正文

Opus从入门到精通(四)Opus解码程序实现

Opus从入门到精通(四)Opus解码程序实现

前面我们已经成功将PCM文件编码成Opus,写入文件,接下来我们实现解码.

初始化解码器

解码器创建依赖于编码前数据的采样率,声道数:

OpusDecoder *pOpusDec = opus_decoder_create(sampleRateInHz, channelConfig, &error);
if (error != OPUS_OK)
{fprintf(stderr, "Cannot create decoder: %s\n", opus_strerror(error));goto failure;
}

创建MP3编码器

由于我们解码后的数据直接写入文件,无法通过vlc等播放器播放,我们通过lame将解码后的数据再编码成mp3,当然,你要乐意也可以直接价格wav头输出成wav.
通过采样率,声道数等创建lame编码器:

lame_global_flags *lame = NULL;
lame = lame_init();
lame_set_in_samplerate(lame, sampleRateInHz);//设置输入采样率
lame_set_num_channels(lame, channelConfig);//输入流的声道
lame_set_out_samplerate(lame, sampleRateInHz);//设置输出采样率
lame_set_brate(lame, 16);//设置码率
lame_set_quality(lame, 7);
lame_init_params(lame);

读取文件

前面我们编码的音频设置的码率是16kbits,并且一帧20毫秒,那么一帧的文件大小是16000 * 8 / 1000 * 20 = 40byte,我们每次读取一帧,循环读取文件:

#define READ_BUFFER_SIZE 40
#define WRITE_BUFFER_SIZE 520fbytes = (unsigned char*)malloc(WRITE_BUFFER_SIZE);
out = (short *)malloc(640 * sizeof(short));while (1)
{unsigned char data[READ_BUFFER_SIZE];num_read = fread(data, 1, READ_BUFFER_SIZE, fin);if (num_read > 0){int output_samples = opus_decode(pOpusDec, data, num_read, out, DECODE_OUTPUT_SIZE, 0);int result = lame_encode_buffer(lame, out, out,output_samples, fbytes, WRITE_BUFFER_SIZE);if (fwrite(fbytes, 1, result, fout) != (unsigned)(result)){printf("write error,output_samples = %d, result = %d\n",output_samples,result);goto failure;}}else{int result = lame_encode_flush(lame, fbytes, WRITE_BUFFER_SIZE);if(result >= 0){if (fwrite(fbytes, 1, result, fout) != (unsigned)(result)){fprintf(stderr, "222 Error writing.\n");goto failure;}}lame_close(lame);break;}
}

存在问题

这样一个简单的opus解码器程序就出来了,但是这样有个问题,我们解码的时候必须事先知道原始音频的采样率,声道数,帧大小等.
而且必须保证编码后的数据每一帧大小都相同,这样就要求编码时必须配置成CBR,即固定码率.
我们假设有这么一个文件结构,里面不仅可以保持编码后的opus数据,而且可以保存帧率等信息,并且每帧之间都有帧间隔标记,这样就可以解决上面这些问题.
那么有没有这么一种文件结构呢?还真有,opus编码的标准封装ogg就是干这个事情的,后面的文章我们分析opus的ogg封装格式.

示例代码地址:https://github.com/qingkouwei/opus2mp3

如果对你有帮助的话点个赞吧!!!

相关文章:

  • 【CT】LeetCode手撕—102. 二叉树的层序遍历
  • 如何查看当前的gruop_id 的kafka 消费情况 这个可以查看到是否存在消费阻塞问题
  • 记录:UA_Client_readValueAttribute 读取失败 C0错误码
  • RabbitMQ延迟消息(通过死信交换机实现)
  • 电子画册制作与传统画册相比,有哪些优势?
  • nc网络收发测试-tcp客户端\TCP服务器\UDP\UDP广播
  • 仿element-ui 实现自己组件库 <3>
  • 前端 JS 经典:Vue 状态仓库持久化
  • 24年河北自考报名流程详细教程汇总
  • Python实战:小说分词统计-数据可视化
  • 前端框架之 MVVM
  • 金融与大模型:引领行业未来的创新融合
  • 6spark期末复习
  • 探索 Spring Boot 集成缓存功能的最佳实践
  • Ajax的应用
  • 【Amaple教程】5. 插件
  • docker容器内的网络抓包
  • gops —— Go 程序诊断分析工具
  • jdbc就是这么简单
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • Objective-C 中关联引用的概念
  • php面试题 汇集2
  • Python爬虫--- 1.3 BS4库的解析器
  • vue.js框架原理浅析
  • 汉诺塔算法
  • 记录:CentOS7.2配置LNMP环境记录
  • 盘点那些不知名却常用的 Git 操作
  • 日剧·日综资源集合(建议收藏)
  • 少走弯路,给Java 1~5 年程序员的建议
  • 数据可视化之 Sankey 桑基图的实现
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • $refs 、$nextTic、动态组件、name的使用
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (十二)Flink Table API
  • (小白学Java)Java简介和基本配置
  • (一)、python程序--模拟电脑鼠走迷宫
  • (转)jQuery 基础
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .net SqlSugarHelper
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .net程序集学习心得
  • .NET和.COM和.CN域名区别
  • .Net接口调试与案例
  • @AliasFor注解
  • @Data注解的作用
  • @ohos.systemParameterEnhance系统参数接口调用:控制设备硬件(执行shell命令方式)
  • @property python知乎_Python3基础之:property
  • [Angular] 笔记 16:模板驱动表单 - 选择框与选项
  • [BUAA软工]第一次博客作业---阅读《构建之法》
  • [BUG] Authentication Error
  • [BZOJ1040][P2607][ZJOI2008]骑士[树形DP+基环树]