首页 » PHP教程 » hederutf8php技巧_微信小轨范wxcreateInnerAudioContext安卓手机不能播放语音问题

hederutf8php技巧_微信小轨范wxcreateInnerAudioContext安卓手机不能播放语音问题

访客 2024-12-18 0

扫一扫用手机浏览

文章目录 [+]

但是,将组件按为新的wx.createInnerAudioContext()后碰着了一个大坑,即部分安卓手机播放时后端报错,无法播放语音文件。
在电脑上调试、浏览器上访问、苹果手机上都可以,便是安装手机网上提这个问题的人很多,但都没有供应完全的办理方案,微信官方也没有供应办理方案,只是让大家提交代码片断,只说是与机型不兼容。

真机调试时,后台缺点代码:

hederutf8php技巧_微信小轨范wxcreateInnerAudioContext安卓手机不能播放语音问题

errCode: 10001, errMsg: "errCode:62, err:load or init native decode so fail"。

hederutf8php技巧_微信小轨范wxcreateInnerAudioContext安卓手机不能播放语音问题
(图片来自网络侵删)

同时音频工具监听缺点的事宜,也能监听到缺点,缺点信息:

监听音频播放缺点事宜 {errCode: 0, errMsg: ""}

为了项目进展,没有办法,先退回去用旧的wx.createAudioContext()组件,这个组件虽然功能差一些,但基本上能用,先凑互助。
后来,有韶光后进一步在研究了手机兼容问题、音频文件做事Header、保存文件OSS问题、音频文件内部格式问题后,终于找到理解决方案,特此完全记录一下,希望对碰着类似问题的同行有用。

二、缘故原由剖析

刚碰到这个问题也是一头雾水,百度上能找到的干系信息很少,微信小程序社区提这个问题的人很多,将这个缺点代码在社区上检索,有7000多条信息,但都是反馈问题的,没有找到可用的办理方案,不过线索找到了几条。

2.1 检测组件利用方法

刚开始疑惑程序编写方法不对,而微信小程序的官方手册先容内容很少,还不随意马虎理解,官方手册将工具申明放到头上,并且按const申明,并且只有一个SRC,我的项目中要用到多个动态SRC,并且SRC是用户选择不同景点,播放不同的语音先容。

const innerAudioContext = wx.createInnerAudioContext();

刚开始,是疑惑此问题,后专门写了一个页面,按照官方的写法,结果一样,仍旧弗成。
后来反复考试测验写各种方案,将工具放到this,this.data仍旧弗成,测试过将工具所有按钮事宜,监控事宜都写了来跟踪各种情形 ,结果仍是一样,在测试平台、ios真机测试行,安卓真机测试弗成。

2.2 文件要求header问题打消

先退回旧组件连续利用,旧的组件须要在页面文件添加组件,为了在实现多个工具用一个后台组件自己定义了play和pause图标,定义了一个audio组,并隐蔽起来,实现与inner工具差不多的效果,但掌握起来比较麻烦,且没有监控事宜,后续做播放统计会有一些问题。

不甘心退回用旧组件,一有韶光连续研究该问题,在微信社区创造谈论此问题的很多,但没有明确答案,基本上说是不兼容,问题缘故原由是微信小程序的组件不兼容,只能等微信升级,但是这个问题从2019年3月就广泛涌现了啊,一年多了微信还没升级吗。
后来找到一个兄弟说办理了是request heder问题,修正要求的heaer即可,但没有给出方法啊,也没有进一步回答。

反复研究request header,在我用的VUE的request确实是判断header,后台header须要调协为octect-stream,但是我后台已经是mp3文件了啊,不论用新旧组件,都只有一个src参数,指向后端地址,没有地方加header啊,除非后台将文件读取到内存,然后再以stream反馈给前端,但是以为利用OSS便是让前端可以直接通过链接办法,以流办法利用多媒体文件啊,如果再读回做事器内存,再反馈给前端不对啊,该当没有必要。

if (headers['content-type'] === 'application/octet-stream;charset=UTF-8') { return res.data}

连续研究,如果利用OSS或者其它做事器用链接访问,都无法增加header的要求头,办理这个方案的兄弟该当因此流办法供应给前端小程序的,不是往后端OSS工具文件做事供应的小程序的。

2.3 文件做事器问题剖析

连续下载微信社区上干系的代码片段,创造一个故意思的问题,有iac运用一个代码片段,第一次播放报错,等待一下子,再点击播放按钮时,就不会报错了。
以为这个问题是否该当是做事器与小程序的冲突所致,文件前端还没有获取完成,前端就播放了,将自动播放去掉,修正为自播放,或者在onCanPlay事宜中再播放,还有错,问题没有得到办理。
想到自己用的OSS做事器是阿里,小程序是腾讯,是不是这两个公司相互限定作成的问题。
沿着这个思路,检讨文件做事位置进行测试,果真将其它做事器基本上行,自己在阿里OSS上的弗成,以为是阿里和腾讯相互限定的结果,但想想不会啊,打算机上和苹果打算机上行的啊。
于是可以播放的文件下载下来,上传到自己的OSS上进行测试,竟然能够播放,于是想明白了,是不是文件本身有问题啊。
至此,打消了小程序写法、要求头参数、文件做事器问题,此问题该当是文件本身问题,于此顺着此思路,还真找到了干系资料,办理了此问题。

三、MP3文件标签剖析

由于我是自己学习,前后端都比较理解,顺着MP3文件格式问题思路连续,查找,果真在微信小程序开拓社区找到了下面文章,该文章先容引用了MP3文件格式标记问题,并且提到了讯飞语音合成的mp3不能播放,转百度语音合造诣以。
并在这个问题的回答文件中,先容了一个给mp3文件添加标记的程序,这两个方案结合在一起给理解决问题的思路与方法。

https://developers.weixin.qq.com/community/develop/article/doc/000460e9bd4c982e1609f4f725b013

3.1 MP3文件格式解析

MP3 文件大体分为三部分:TAG_V2(ID3V2),音频数据,TAG_V1(ID3V1),详细的见下面文章,不再赘述。

https://blog.csdn.net/datamining2005/article/details/78954367

3.2 讯飞语音合成MP3文件格式

根据3.1先容的内容,下载格式工厂,对讯飞语音合成文件信息进行解析,果真只有文件size,其它信息都没有,如下图。

其余找了下MP3音乐文件解析如下,有详细的信息。

用十六进制文件找开,头上没有id3,后面没有tag

至此,可以判断是讯飞合成的语音文件是利用lame3.1格式对音频进行编码,然后以扩展名MP3进行识别,在游览器、微信小程序调试器、苹果真机调试这两个信息足够解析并播放,但安卓版微信小程序是标签进行识别的,无法正常播放。

对此,对讯飞合成用格式工厂进行转换,然后上传到阿里OSS文件上,测试能够正常播放。
至此,问题的缘故原由定位清楚:

1、讯飞语音合成的MP3文件只是用lame3.1格式进行编码,并以mp3扩展名进行标识识别,并没用在文件中添加MP3音频文件前后增加TAG_V2(ID3V2)、TAG_V1(ID3V1)标记。

2、微信小程序安卓上的音频播放器有BUG,是按音频文件内部的TAG来识别和解析文件,对付TAG禁绝确的,不能播放,但这个问题在打算机浏览器和苹果打算机上存在,只存在安卓系统,因此前端工程师很难办理这个问题,导致微信社区上千个这样的问题没有完全的办理方案回答。
特殊是,很多有时现现的问题,缘故原由是文件的问题,不是不兼容问题。

四、修复方案

找到根本缘故原由后,参照下面方案进行修复,剖析讯飞语音合成文件的资料和参数,没有找到添加TAG的方法,没办法自己添加吧,感谢两位同行兄弟供应的思路。

https://www.cnblogs.com/ztysir/p/5513853.html

4.1 语音文件添加TAG方法

添加两个方法,分别根据传入的音频文件名称,作者,专辑名,添加到头和尾部,这两个方法基本上借用上面兄弟的代码。

/ 合成mp3文件的tag @songName 名称 @artistName 作者 @albumName 专辑 @return 128字节的D3V1字符串 @return / private static byte[] composeD3V1(String songName, String artistName, String albumName) { try { byte[] tagByteArray = new byte[128]; byte[] songNameByteArray = songName.getBytes("GBK"); byte[] artistNameByteArray = artistName.getBytes("GBK"); byte[] albumNameByteArray = albumName.getBytes("GBK"); int songNameByteArrayLength = songNameByteArray.length; int artistNameByteArrayLength = artistNameByteArray.length; int albumNameByteArrayLength = albumNameByteArray.length; songNameByteArrayLength = songNameByteArrayLength > 30 ? 30 : songNameByteArrayLength; artistNameByteArrayLength = artistNameByteArrayLength > 30 ? 30 : artistNameByteArrayLength; albumNameByteArrayLength = albumNameByteArrayLength > 30 ? 30 : albumNameByteArrayLength; System.arraycopy("TAG".getBytes(), 0, tagByteArray, 0, 3); System.arraycopy(songNameByteArray, 0, tagByteArray, 3, songNameByteArrayLength); System.arraycopy(artistNameByteArray, 0, tagByteArray, 33, artistNameByteArrayLength); System.arraycopy(albumNameByteArray, 0, tagByteArray, 63, albumNameByteArrayLength); // 将流派显示为指定音乐的流派 tagByteArray[127] = (byte) 0xFF; return tagByteArray; } catch (Exception e) { log.error("添加MP3文件TAG标签非常", e); } return new byte[0]; }/ 合成mp3文件的id @songName 名称 @artistName 作者 @albumName 专辑 @return 128字节的D3V2字符串 @return /public static byte[] composeD3V2(String songName, String artistName, String albumName) { try { byte[] encodeByte = {3}; // 03 表示的UTF8编码 byte[] tagByteArray; byte[] tagHeadByteArray; byte[] tagFrameHeadByteArray; byte[] songNameByteArray = songName.getBytes("UTF-8"); byte[] artistNameByteArray = artistName.getBytes("UTF-8"); byte[] albumNameByteArray = albumName.getBytes("UTF-8"); final int tagHeadLength = 10; final int tagFrameHeadLength = 10; final int tagFrameEncodeLength = 1; final int tagFillByteLength = 20; // 这个添补字节是我看到其他MP3文件ID3标签都会在尾端添加的数据,为了保险起见我也加上了 int byteArrayOffset = 0; int songNameByteArrayLength = songNameByteArray.length; int artistNameByteArrayLength = artistNameByteArray.length; int albumNameByteArrayLength = albumNameByteArray.length; int songNameFrameTotalLength = songNameByteArrayLength + tagFrameEncodeLength; int artistNameFrameTotalLength = artistNameByteArrayLength + tagFrameEncodeLength; int albumNameFrameTotalLength = albumNameByteArrayLength + tagFrameEncodeLength; int tagTotalLength = tagHeadLength + tagFrameHeadLength + songNameByteArrayLength + tagFrameHeadLength + artistNameByteArrayLength + tagFrameHeadLength + albumNameByteArrayLength + tagFillByteLength; int tagContentLength = tagTotalLength - tagHeadLength; tagByteArray = new byte[tagTotalLength]; tagHeadByteArray = new byte[tagHeadLength]; System.arraycopy("ID3".getBytes(), 0, tagHeadByteArray, 0, 3); tagHeadByteArray[3] = 3; tagHeadByteArray[4] = 0; tagHeadByteArray[5] = 0; tagHeadByteArray[6] = (byte) ((tagContentLength >> 7 >> 7 >> 7) % 128); tagHeadByteArray[7] = (byte) ((tagContentLength >> 7 >> 7) % 128); tagHeadByteArray[8] = (byte) ((tagContentLength >> 7) % 128); tagHeadByteArray[9] = (byte) (tagContentLength % 128); System.arraycopy(tagHeadByteArray, 0, tagByteArray, byteArrayOffset, tagHeadLength); byteArrayOffset += tagHeadLength; tagFrameHeadByteArray = new byte[tagFrameHeadLength]; System.arraycopy("TIT2".getBytes(), 0, tagFrameHeadByteArray, 0, 4); tagFrameHeadByteArray[4] = (byte) ((songNameFrameTotalLength >> 8 >> 8 >> 8) % 256); tagFrameHeadByteArray[5] = (byte) ((songNameFrameTotalLength >> 8 >> 8) % 256); tagFrameHeadByteArray[6] = (byte) ((songNameFrameTotalLength >> 8) % 256); tagFrameHeadByteArray[7] = (byte) (songNameFrameTotalLength % 256); tagFrameHeadByteArray[8] = 0; tagFrameHeadByteArray[9] = 0; System.arraycopy(tagFrameHeadByteArray, 0, tagByteArray, byteArrayOffset, tagFrameHeadLength); byteArrayOffset += tagFrameHeadLength; System.arraycopy(encodeByte, 0, tagByteArray, byteArrayOffset, tagFrameEncodeLength); byteArrayOffset += tagFrameEncodeLength; System.arraycopy(songNameByteArray, 0, tagByteArray, byteArrayOffset, songNameByteArrayLength); byteArrayOffset += songNameByteArrayLength; tagFrameHeadByteArray = new byte[tagFrameHeadLength]; System.arraycopy("TPE1".getBytes(), 0, tagFrameHeadByteArray, 0, 4); tagFrameHeadByteArray[4] = (byte) ((artistNameFrameTotalLength >> 8 >> 8 >> 8) % 256); tagFrameHeadByteArray[5] = (byte) ((artistNameFrameTotalLength >> 8 >> 8) % 256); tagFrameHeadByteArray[6] = (byte) ((artistNameFrameTotalLength >> 8) % 256); tagFrameHeadByteArray[7] = (byte) (artistNameFrameTotalLength % 256); tagFrameHeadByteArray[8] = 0; tagFrameHeadByteArray[9] = 0; System.arraycopy(tagFrameHeadByteArray, 0, tagByteArray, byteArrayOffset, tagFrameHeadLength); byteArrayOffset += tagFrameHeadLength; System.arraycopy(encodeByte, 0, tagByteArray, byteArrayOffset, tagFrameEncodeLength); byteArrayOffset += tagFrameEncodeLength; System.arraycopy(artistNameByteArray, 0, tagByteArray, byteArrayOffset, artistNameByteArrayLength); byteArrayOffset += artistNameByteArrayLength; tagFrameHeadByteArray = new byte[tagFrameHeadLength]; System.arraycopy("TALB".getBytes(), 0, tagFrameHeadByteArray, 0, 4); tagFrameHeadByteArray[4] = (byte) ((albumNameFrameTotalLength >> 8 >> 8 >> 8) % 256); tagFrameHeadByteArray[5] = (byte) ((albumNameFrameTotalLength >> 8 >> 8) % 256); tagFrameHeadByteArray[6] = (byte) ((albumNameFrameTotalLength >> 8) % 256); tagFrameHeadByteArray[7] = (byte) (albumNameFrameTotalLength % 256); tagFrameHeadByteArray[8] = 0; tagFrameHeadByteArray[9] = 0; System.arraycopy(tagFrameHeadByteArray, 0, tagByteArray, byteArrayOffset, tagFrameHeadLength); byteArrayOffset += tagFrameHeadLength; System.arraycopy(encodeByte, 0, tagByteArray, byteArrayOffset, tagFrameEncodeLength); byteArrayOffset += tagFrameEncodeLength; System.arraycopy(albumNameByteArray, 0, tagByteArray, byteArrayOffset, albumNameByteArrayLength); return tagByteArray; } catch (Exception e) { log.error("添加MP3文件TAG标签非常", e); } return new byte[0];}4.2 语音合成文件中添加

语音合成文件是分多个byte[]数据,4.1的两上方未能返回的也是byte[]数据,利用ByteUtils.concat,可以将之个byte[]合并起来,再反馈给前端并保存到OSS中,通过这个方法自动处理的文件就具有TAG头和尾,经由测试前端可以正常访问。

byte[] mp3Tag = XunFeiUtil.composeD3V2("景点先容", "聪慧趣游", "趣游信息");InputStream is = new ByteArrayInputStream(ByteUtils.concat(mp3Tag, result));ossFileUtils.uploadFile2OSS(is, fileName);4.3、前端实现方法

const app = getApp()Page({data: {iac: null,value: 0, //正在播放时长duration: 0, //总时长isplay: false,isloading: false,isdrag: false,src: "https://xiyoutianxia.oss-cn-hangzhou.aliyuncs.com/upload/000.mp3"},onLoad: function(options) {let that = this;that.initInnerAudioContext();},//播放play: function() {if (this.data.isplay) {this.data.iac.pause();} else {this.data.iac.play();}},sliderChanging: function(e) {let that = this;if (!that.data.isdrag) {let drplay = that.data.isplay;that.setData({isdrag: true,drplay: drplay});if (drplay) {that.data.iac.pause();}}},// 进度条拖拽sliderChange: function(e) {console.log('sliderChange');let that = this;let value = parseInt(e.detail.value);let iac = that.data.iac;that.setData({isdrag: false});iac.seek(value);if (that.data.drplay) {iac.play();}},// 页面卸载时停滞播放onUnload() {//停滞播放this.data.iac.stop();this.data.iac.destroy();},initInnerAudioContext() {let that = this;// 创建音频高下文let iac = wx.createInnerAudioContext();iac = wx.createInnerAudioContext();iac.src = that.data.src;// 监听音频播放事宜iac.onPlay(() => {console.log('onPlay');that.setData({isplay: true});});// 监听音频播放进度更新事宜iac.onTimeUpdate(function() {if (that.data.isdrag) {return;}let duration = iac.duration;let value = iac.currentTime;that.setData({duration: duration,value: value});});iac.onWaiting(function() {console.log('onWaiting');that.setData({isloading: true});});iac.onCanplay(function() {console.log('onCanplay')that.setData({isloading: false});});// 监听音频停息事宜iac.onPause(function() {console.log('onPause');that.setData({isplay: false});});// 监听音频停滞事宜iac.onStop(function() {console.log('onStop');that.setData({isplay: false});iac.seek(0);});// 监听音频自然播放至结束的事宜iac.onEnded(function() {console.log('onEnded');that.setData({isplay: false});iac.seek(0);});// 监听音频播放缺点事宜iac.onError(err => {console.log('监听音频播放缺点事宜', err, iac.src);that.setData({isplay: false,isloading: false});wx.showToast({icon: 'none',title: '音频播放出错!
',});});that.setData({iac: iac});},//格式化秒 00:00format_sec(sec, is_milli) {if (!sec) {return '00:00';}sec = parseFloat(sec + '');if (is_milli) {sec = sec / 1000;}let min = Math.floor(sec / 60);min = (min < 10 ? '0' : '') + min;sec = Math.round(sec % 60);sec = (sec < 10 ? '0' : '') + sec;return min + ':' + sec;}})
五、小结

这个问题隐蔽得比较深,月朔看是前端兼容性问题,深入剖析后是小程序前端BUG再加上数据的问题导致的,数据问题不是总会涌现,导致这个问题比较难复现和终极办理。

相关文章