您当前的位置:首页 > 电脑百科 > 程序开发 > 移动端 > H5

H5实时解码音频并播放

时间:2022-08-17 11:37:02  来源:  作者:音视频开发老舅

音视频的格式是一个有歧义的说法。我们熟知的诸如Flv、Mp4、Mov啥的都是包装格式,可以理解为一种容器,就像一个盒子。里面放到是经过编码的音视频数据,而这些音视频数据都有自己的编码格式,如AAC、H264、H265等等。 今天要展示的是从直播流中获取到的音频编码数据进行解码并使用H5的音频API进行播放的过程。

这些格式分别是

1. speex

2. aac

3. mp3

这些格式都有开源的解码库,不过都是c库,在H5中需要通过emscripten编译成js执行。

引入头文件

#ifdef USE_SPEEX#include <speex/speex.h>#endif#ifdef USE_AAC#include "aacDecoder/include/neaacdec.h"// #include "libfdk-aac/libAACdec/include/aacdecoder_lib.h"#endif#ifdef USE_MP3#include "libmad/mad.h"//#include "libid3tag/tag.h"#endif

定义变量

int bufferLength;int bufferFilled;u8 *outputBuffer;#ifdef USE_AAC    faacDecHandle faacHandle;#endif#ifdef USE_SPEEX    i16 *audioOutput;    void *speexState;    SpeexBits speexBits;#endif#ifdef USE_MP3    MP3Decoder mp3Decoder;#endif

bufferLength 用于指定缓冲区的长度,bufferFilled用于指示缓冲中没有使用的数据,outputBuffer用来存放解码后的数据。 MP3Decoder是自己写的一个类,需要定义这几个成员

mad_stream inputStream;mad_frame frame;mad_synth synth;

初始化

outputBuffer = (u8 *)malloc(bufferLength);#ifdef USE_SPEEX    audioOutput = (i16 *)malloc(640);    auto mode = speex_lib_get_mode(SPEEX_MODEID_WB);    speexState = speex_decoder_init(mode);    speex_bits_init(&speexBits);#endif#ifdef USE_AAC    faacHandle = faacDecOpen();#endif

mp3的初始化

mad_stream_init(&inputStream);mad_frame_init(&frame);mad_synth_init(&synth);

解码

input对象中包含了经过协议拆包后的原始音频数据(RTMP协议或Flv格式中的格式)缓冲大小虽然是自己定义,但必须遵循下面的规则

aac:1024的倍数(AAC一帧的播放时间是= 1024 * 1000/44100 = 22.32ms)

speex:320的倍数(320 * 1000/16000 = 20ms)

MP3:576的倍数(双声道1152 * 1000 /44100 = 26.122ms)

根据这些数据可以估算缓冲大小引起的音频的延时,然后需要和视频的延迟进行同步。

#ifdef USE_SPEEX    if (input.length() <= 11)    {        memset(output, 0, 640);    }    else    {        speex_bits_read_from(&speexBits, (const char *)input, 52);        speex_decode_int(speexState, &speexBits, audioOutput);        memcpy(output, audioOutput, 640);    }    return 640;#endif#ifdef USE_AAC    //0 = AAC sequence header ,1 = AAC raw     if (input.readB<1, u8>())    {        faacDecFrameInfo frame_info;        auto pcm_data = faacDecDecode(faacHandle, &frame_info, (unsigned char *)input.point(), input.length());        if (frame_info.error > 0)        {            emscripten_log(1, "!!%sn", NeAACDecGetErrorMessage(frame_info.error));        }        else        {        int samplesBytes = frame_info.samples << 1;        memcpy(output, pcm_data, samplesBytes);        return samplesBytes;        }    }    else    {        unsigned long samplerate;        unsigned char channels;        auto config = faacDecGetCurrentConfiguration(faacHandle);        config->defObjectType = LTP;        faacDecSetConfiguration(faacHandle,config);        faacDecInit2(faacHandle, (unsigned char *)input.point(), 4, &samplerate, &channels);        emscripten_log(0, "aac samplerate:%d channels:%d", samplerate, channels);    }#endif

mp3 比较复杂,这里不贴代码了,主要是mad库不能直接调用其提供的API,直播流中的MP3数据和mp3文件的格式有所不同导致。如果本文火的话,我就详细说明。

C++音视频开发学习资料:点击领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

释放资源

#ifdef USE_AAC    faacDecClose(faacHandle);#endif#ifdef USE_SPEEX    speex_decoder_destroy(speexState);    speex_bits_destroy(&speexBits);    free(audioOutput);#endif    free(outputBuffer);

mp3

mad_synth_finish(&synth);mad_frame_finish(&frame);

播放

创建AudioContext对象

window.AudioContext = window.AudioContext || window.webkitAudioContext;var context = new window.AudioContext();

创建audioBuffer

var audioBuffers = []var audioBuffer = context.createBuffer(channels, frameCount, samplerate);

播放音频(带缓冲)

var playNextBuffer = function() {                    isPlaying = false;                    if (audioBuffers.length) {                        playAudio(audioBuffers.shift());                    }                    if (audioBuffers.length > 1) audioBuffers.shift();                    //console.log(audioBuffers.length)                };                var copyAudioOutputArray = resampled ? function(target) {                    for (var i = 0; i < allFrameCount; i++) {                        var j = i << 1;                        target[j] = target[j + 1] = audioOutputArray[i] / 32768;                    }                } : function(target) {                    for (var i = 0; i < allFrameCount; i++) {                        target[i] = audioOutputArray[i] / 32768;                    }                };                var copyToCtxBuffer = channels > 1 ? function(fromBuffer) {                    for (var channel = 0; channel < channels; channel++) {                        var nowBuffering = audioBuffer.getChannelData(channel);                        if (fromBuffer) {                            for (var i = 0; i < frameCount; i++) {                                nowBuffering[i] = fromBuffer[i * (channel + 1)];                            }                        } else {                            for (var i = 0; i < frameCount; i++) {                                nowBuffering[i] = audioOutputArray[i * (channel + 1)] / 32768;                            }                        }                    }                } : function(fromBuffer) {                    var nowBuffering = audioBuffer.getChannelData(0);                    if (fromBuffer) nowBuffering.set(fromBuffer);                    else copyAudioOutputArray(nowBuffering);                };                var playAudio = function(fromBuffer) {                    if (isPlaying) {                        var buffer = new Float32Array(resampled ? allFrameCount * 2 : allFrameCount);                        copyAudioOutputArray(buffer);                        audioBuffers.push(buffer);                        return;                    }                    isPlaying = true;                    copyToCtxBuffer(fromBuffer);                    var source = context.createBufferSource();                    source.buffer = audioBuffer;                    source.connect(context.destination);                    source.onended = playNextBuffer;                    //setTimeout(playNextBuffer, audioBufferTime-audioBuffers.length*200);                    source.start();                };

其中playNextBuffer 函数用于从缓冲中取出数据 copyAudioOutputArray 函数用于将音频数据转化成浮点数。 copyToCtxBuffer 函数用于将音频数据拷贝进可以播放的缓冲数组中。 这些函数对单声道和双声道进行了处理

var resampled = samplerate < 22050;

对于频率小于22khz的数据,我们需要复制一份,模拟成22khz,因为H5只支持大于22khz的数据。



Tags:音频   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
AI干掉声优?音频大模型追逐“图灵时刻”
七十年前,“人工智能之父”图灵提出,如果人无法判断屏幕的另一侧究竟是人还是机器,就证明机器具备了人一样的智能。这一经典的图灵测试如同北斗星一般,指引着AI行业的工作者们不...【详细内容】
2024-04-03  Search: 音频  点击:(6)  评论:(0)  加入收藏
录制音频被另作商用是否侵权?全国首例“AI声音侵权案”审理
□ 本报记者 徐伟伦AI技术的应用,为我们的生活带来巨大变化。当AI技术应用到声音领域,是否面临侵权风险?司法又将如何回应?近日,北京互联网法院组成五人合议庭,依法公开审理全国首...【详细内容】
2023-12-20  Search: 音频  点击:(68)  评论:(0)  加入收藏
如何用Java实现音频合成和声音识别?
音频合成和声音识别在Java中是一个相对复杂的任务,但是有一些强大的库和工具可以帮助我们实现这些功能。下面将提供一个基本的指南,介绍如何用Java实现音频合成和声音识别。1...【详细内容】
2023-12-15  Search: 音频  点击:(187)  评论:(0)  加入收藏
研究人员发现新型攻击方式,可通过图像和音频操纵大模型
随着大语言模型(LLM)开始整合多模态功能,攻击者可能会在图像和音频中隐藏恶意指令,利用这些指令操纵AI聊天机器人(例如ChatGPT)背后的LLM对用户提示的响应。在2023年欧洲黑帽大会...【详细内容】
2023-12-07  Search: 音频  点击:(138)  评论:(0)  加入收藏
修复Windows 10上“未安装音频输出设备”的错误
许多Windows 10用户,尤其是那些使用HP或Dell笔记本电脑和PC的用户,都会遇到一个错误,上面写着“未安装音频输出设备”。这意味着你无法收听计算机上的任何声音,这让你很难放松,也...【详细内容】
2023-11-24  Search: 音频  点击:(176)  评论:(0)  加入收藏
抖音音频怎么提取出来?教你一招收集音频素材!
抖音音频怎么提取出来?在提取音频时,需要注意保持音频的质量和完整性。如果使用的提取工具或方法不当,可能会导致音频质量受损或出现杂音等问题。同时,需要注意版权问题。如果视...【详细内容】
2023-10-25  Search: 音频  点击:(228)  评论:(0)  加入收藏
解决电脑音频服务未运行问题的方法
在使用电脑的过程中,有时会遇到这样的问题:电脑开机后,小喇叭图标上显示一个叉,提示音频服务未运行。当尝试播放音乐或进行其他需要音频功能的活动时,会发现音频服务不能正常使用...【详细内容】
2023-10-25  Search: 音频  点击:(123)  评论:(0)  加入收藏
亚马逊宣布关闭旗下音频社交竞品应用 Amp
IT之家 10 月 6 日消息,Clubhouse 音频社交应用在 2021 年前后一度大火,亚马逊此前推出了一款名为 Amp 的同类应用,以试图与 Clubhouse 竞争。根据亚马逊数字音乐总监 Steve Bo...【详细内容】
2023-10-07  Search: 音频  点击:(139)  评论:(0)  加入收藏
播客,会成为音频界的“小红书”吗?
2022年初,当一些新消费品牌从小红书等流量池撤退,转而在播客平台试水,人们曾预测播客的“商业化元年”已至。一年后,熟悉播客的人一定不难发现,出现在播客里的品牌更多元,且更多是...【详细内容】
2023-09-25  Search: 音频  点击:(54)  评论:(0)  加入收藏
如何在WordPress中添加音频音乐播放器小部件
您想在WordPress中添加音频音乐播放器小部件吗?音乐播放器是与观众分享歌曲、播客、采访和其他音频文件的好方法。这有助于在您的网站上创建更有趣和引人入胜的体验。在本文...【详细内容】
2023-09-04  Search: 音频  点击:(366)  评论:(0)  加入收藏
▌简易百科推荐
京东快递H5项目接入vite实战
本文介绍了如何在开发阶段将vite应用于vue 2.x 工程,从而提高研发的开发体验与效率。主要涉及如何兼容process变量,如何处理 node-sass 与 dart-sass冲突,以及路径别名的兼容处...【详细内容】
2023-03-17  东东程序猿  今日头条  Tags:H5   点击:(232)  评论:(0)  加入收藏
什么是HTML5?HTML5的含义、元素和好处
译者 | 李睿审校 | 孙淑娟HTML5是超文本标记语言(HTML)的第五版,网络浏览器使用它来可视化代码。它在网站功能、网页内容开发等方面有一些改进。 ​HTML的发展 ​在万维网的...【详细内容】
2023-03-16  李睿  51CTO  Tags:HTML5   点击:(262)  评论:(0)  加入收藏
H5直播技术起航
作者:京东科技 吴磊音视频基本概念视频格式就是通常所说的.mp4,.flv,.ogv,.webm等。简单来说,它其实就是一个盒子,用来将实际的视频流以一定的顺序放入,确保播放的有序和完整性...【详细内容】
2023-01-10  京东云开发者  今日头条  Tags:H5   点击:(239)  评论:(0)  加入收藏
Canvas从入门到实战
1、什么是Canvas?HTML5 提供Canvas API,其本质上是一个DOM元素,可以看成是浏览器提供一块画布供我们在上面渲染2D或者3D图形。由于3D绘制上下文(webgl)目前在很多浏览器上兼...【详细内容】
2022-12-24  闪念基因  今日头条  Tags:Canvas   点击:(223)  评论:(0)  加入收藏
网站程序开发使用的html5你了解吗
首先可以肯定,html5(简称h5)将在很多年内成为互联网的主流。那到底什么是h5呢?想了解h5,先要了解它的前身html和被它终结的flash:2000年左右的前端静态网页格式是html的,仅支持ie,n...【详细内容】
2022-09-26  易企优     Tags:html5   点击:(499)  评论:(0)  加入收藏
使用 JavaScript 和 HTML5 Canvas 绘制图表
你将要创造什么在本教程中,我将向您展示如何使用 JavaScript 和画布以饼图和圆环图的形式显示数字信息。什么是饼图?图表是一种统计工具,用于以图形方式表示数值数据。饼图将...【详细内容】
2022-08-31  兴趣编程网  今日头条  Tags:   点击:(505)  评论:(0)  加入收藏
一看就能学会的H5视频推流方案
环境部署1、 配置、安装 Nginx;# ./configure --sbin-path=/usr/local/nginx/nginx --conf-path=/usr/local/nginx/nginx.pid --with-http_ssl_module --with-pcre=/usr/loca...【详细内容】
2022-08-27  音视频开发老舅    Tags:H5   点击:(619)  评论:(0)  加入收藏
H5实时解码音频并播放
音视频的格式是一个有歧义的说法。我们熟知的诸如Flv、Mp4、Mov啥的都是包装格式,可以理解为一种容器,就像一个盒子。里面放到是经过编码的音视频数据,而这些音视频数据都有自...【详细内容】
2022-08-17  音视频开发老舅    Tags:音频   点击:(686)  评论:(0)  加入收藏
实战演示 H5 性能分析
W3C标准是浏览器标准,一般浏览器都支持W3C标准,它规定使用者可以通过api查询性能信息,可借用W3C协议完成自动化H5性能测试。 W3C官网: https://www.w3.org/TR/navigation-timing...【详细内容】
2022-06-06  CeshirenTester    Tags: H5   点击:(329)  评论:(0)  加入收藏
HTML5新特性
①语义化标签,可以让页面有更加完善的结构,让页面的元素有含义,同时利于被搜索引擎解析,有利于SEO,主要标签包括下面的标签:html5新的常用标签②增强型表单可以通过input的type属...【详细内容】
2022-05-07  Celinf    Tags:HTML5   点击:(415)  评论:(0)  加入收藏
站内最新
站内热门
站内头条