微信小程序BackgroundAudioManager播放m3u8格式音频

  • 小程序中实现背景音乐播放。在这个项目中,也踩了很多坑,记录一下。由于后台给我返回的音频流是m3u8格式的,没有得办法,安全性高,由于实现背景音乐播放,但是设置了backgroundAudioManager.protocol = 'hls';这个,导致,backgroundAudioManager.onEnded这个api,在ios无法检测到,从18年到现在微信至今没有解决,所留下的坑。
    在这里插入图片描述
实现功能
  • 切换上下首
  • 播放暂停
  • 列表播放
  • 随机播放
  • 单曲播放
  • 定时播放
  • 拖拽播放
  • 定位播放当前时间
背景音乐播放
  • 直接使用微信提供的api即可,backgroundAudioManager。从列表页进入这个播放详情页,在onLoad调用这个方法即可,然后需要传入一个id
_loadMusicDetail(musicId) {
        var i = wx.getStorageSync("openkey");
        let _this = this;
        if (null != i && "" != i) {
            var r = url + "&do=DetailVoice", d = {
                id: musicId,
                openkey: i
            };
            wx.showLoading({
                title: '网络加载中...',
            })
            http.Post(r, d, function (res) {
                if (res) {
                    if (res.index == 0) {
                        _this.setData({
                            pre_ok: false
                        })
                    } else {
                        _this.setData({
                            pre_ok: true
                        })
                    }
                    wx.setNavigationBarTitle({
                        title: res.bookname,
                    })
                    wx.setStorageSync('voiceData', res)
                    //设置全局播放
                    backgroundAudioManager.src = res.video_addr
                    backgroundAudioManager.title = res.title;
                    backgroundAudioManager.coverImgUrl = res.top_pic;
                    backgroundAudioManager.protocol = 'hls';
                    _this.setData({
                        coursedata: res,
                        listdata: res.datalist,
                        isPlaying: true,
                        next_ok: true,
                        videoid: res.id,
                        video_addr: res.video_addr
                    })
                    app.globalData.vid = res.id; //全局id
                    wx.hideLoading()
                }
            })
        }
    },

播放器初始化

  • 定义播放器这个api的初始化状态,然后在onload调用一下,否则不是执行播放。
  • 用于处理播放器时间,获取播放器时长
  • _setTime单独处理处理,用于播放总时长
  • _bindBGMEvent方法里面包括了对小程序整个背景音乐播放的核心,
  • backgroundAudioManager.onTimeUpdate这个api监听背景音乐播放进度更新事件,
  • backgroundAudioManager.onEnded监听背景音乐播放结束,但是由于我们设置了backgroundAudioManager.protocol = 'hls'这个。所以在ios中无法检测。通过其他方法处理一下。来检测是否播放结束。通过判断当前播放时长currentTimes和总时长duration比较,实现切换下一首歌曲_this.OnNext()
  • 播放切换下一首歌曲
if ((Number(_this.data.currentTimes) + 1 | 0) >= (Number(_this.data.duration) | 0) && Number(_this.data.currentTimes) > 0 && Number(_this.data.duration) > 0) {
                    // 监听放播放当前歌曲结束时,自动切换下一首
                    if (!flag) {
                        _this.OnNext()
                        flag = true
                    }
                } else {
                    flag = false
                }
  • 运行到后台却不能自动切换下一首。只要安卓可以。
判断机型
  • 目前实现的方法,只能先让安卓实现运行到后台可以接着播放,ios却不行,在data定义playSystem字段,如果是安卓设置为0,否则为1。这样在安卓的时候可以实现backgroundAudioManager.onEnded
_getSystemInfoSync() {
        let _this = this;
        wx.getSystemInfo({
          success: (res) => {
            if(res.system.includes('Android ')) {
                console.log('安卓')
                _this.setData({
                    playSystem:0
                })
            } else {
                _this.setData({
                    playSystem:1
                })
            }
          },
        })
    },
列表循环
  • 做列表,单曲,随机播放这功能是,我是在data定义一个````isSingle```一个字段。如果为0就是列表,1代表单曲。2代表随机播放。
  • 列表循环好做一下。播放完之后,调用一下_this.OnNext()即可。
orderPlay() {
        // 列表循环
        this.setData({
            isSingle: 1
        })
        wx.showToast({
            title: '单曲循环',
            icon: "none"
        })
    },

backgroundAudioManager.onTimeUpdate实现

if (this.data.isSingle === 0 && this.data.playSystem == 1) {
                console.log('1')
                //列表循环  只适用于ios
                if ((Number(_this.data.currentTimes) + 1 | 0) >= (Number(_this.data.duration) | 0) && Number(_this.data.currentTimes) > 0 && Number(_this.data.duration) > 0) {
                    // 监听放播放当前歌曲结束时,自动切换下一首
                    if (!flag) {
                        _this.OnNext()
                        flag = true
                    }
                } else {
                    flag = false
                }
            } 
单曲循环
  • 单曲循环,就是一直播放首歌曲就行,不调用下一首即可
singlePlay() {
        // 单曲循环
        this.setData({
            isSingle: 2
        })
        wx.showToast({
            title: '随机循环',
            icon: "none"
        })
    },

backgroundAudioManager.onTimeUpdate实现

if (this.data.isSingle === 1 && this.data.playSystem == 1) {
                // 单曲循环
                if ((Number(_this.data.currentTimes) + 1 | 0) >= (Number(_this.data.duration) | 0) && Number(_this.data.currentTimes) > 0 && Number(_this.data.duration) > 0) {
                    // 监听放播放当前歌曲结束时,自动切换下一首
                    this._loadMusicDetail(this.data.videoid)
                }
            }
随机播放
  • 随机播放,就需要单独处理一下,需要对listdata数组打乱,取某一个索引值,取到id,通过调用this._loadMusicDetail传过去就行
randomPlay() {
        // 随机循环
        this.setData({
            isSingle: 0
        })
        wx.showToast({
            title: '列表循环',
            icon: "none"
        })
    },

backgroundAudioManager.onTimeUpdate实现

if(this.data.isSingle === 2 && this.data.playSystem == 1) {
                // 随机播放
                if ((Number(_this.data.currentTimes) + 1 | 0) >= (Number(_this.data.duration) | 0) && Number(_this.data.currentTimes) > 0 && Number(_this.data.duration) > 0) {
                    // 监听放播放当前歌曲结束时,自动切换下一首
                    if (!flag) {
                        let data = this.data.listdata;
                        let index = Math.floor(Math.random() * data.length);
                        let item = data[index]
                        this._loadMusicDetail(item.id)
                        flag = true
                    } else {
                        flag = false
                    }
                }
            }
  • 初始化
_bindBGMEvent() {
        let _this = this;
        let flag = false
        backgroundAudioManager.onCanplay(() => {
            // 监听背景音频进入可播放状态事件。 但不保证后面可以流畅播放
            if (typeof backgroundAudioManager.duration != 'undefined') {
                //获取音频总时间
                _this._setTime()
            } else {
                setTimeout(() => {
                    _this._setTime()
                }, 1000)
            }
        })
        backgroundAudioManager.onTimeUpdate(() => {
            let currentTime = backgroundAudioManager.currentTime  //当前播放进度时间
            let duration = backgroundAudioManager.duration //总时长
            const durationFmt = _this._dateFormat(duration)
            // 判断时间是否有想等的 
            const currentFmt = _this._dateFormat(currentTime);
            // 获取到播放实时进度,实现从首页进入播放页面,接着播放
            wx.setStorageSync('currentTime', currentTime)
            _this.setData({
                progress: currentTime / duration * 100 | 0,
                duration: duration,
                currentTimes: currentTime,
                currentTime: `${currentFmt.min}:${currentFmt.sec}`,
                totalTime: `${durationFmt.min}:${durationFmt.sec}` //总时长
            })
            if (this.data.isSingle === 1 && this.data.playSystem == 1) {
                // 单曲循环
                if ((Number(_this.data.currentTimes) + 1 | 0) >= (Number(_this.data.duration) | 0) && Number(_this.data.currentTimes) > 0 && Number(_this.data.duration) > 0) {
                    // 监听放播放当前歌曲结束时,自动切换下一首
                    this._loadMusicDetail(this.data.videoid)
                }
            } else if(this.data.isSingle === 2 && this.data.playSystem == 1) {
                // 随机播放
                if ((Number(_this.data.currentTimes) + 1 | 0) >= (Number(_this.data.duration) | 0) && Number(_this.data.currentTimes) > 0 && Number(_this.data.duration) > 0) {
                    // 监听放播放当前歌曲结束时,自动切换下一首
                    if (!flag) {
                        let data = this.data.listdata;
                        let index = Math.floor(Math.random() * data.length);
                        let item = data[index]
                        this._loadMusicDetail(item.id)
                        flag = true
                    } else {
                        flag = false
                    }
                }
            }
            if (this.data.isSingle === 0 && this.data.playSystem == 1) {
                console.log('1')
                //列表循环  只适用于ios
                if ((Number(_this.data.currentTimes) + 1 | 0) >= (Number(_this.data.duration) | 0) && Number(_this.data.currentTimes) > 0 && Number(_this.data.duration) > 0) {
                    // 监听放播放当前歌曲结束时,自动切换下一首
                    if (!flag) {
                        _this.OnNext()
                        flag = true
                    }
                } else {
                    flag = false
                }
            } 


        })
        backgroundAudioManager.onEnded(() => {
        // 监听播放完  只适用于安卓
            console.log(_this.data.playSystem,'end')
            if(this.data.isSingle === 0 && _this.data.playSystem == 0) {
                if (!flag) {
                    _this.OnNext()
                    flag = true
                } else {
                    flag = false 
                }
            }

            if(this.data.isSingle === 1 && _this.data.playSystem == 0) {
                this._loadMusicDetail(this.data.videoid)
            }
            
            if(this.data.isSingle === 2 && _this.data.playSystem == 0) {
                if (!flag) {
                    let data = this.data.listdata;
                    let index = Math.floor(Math.random() * data.length);
                    let item = data[index]
                    this._loadMusicDetail(item.id)
                    flag = true
                } else {
                    flag = false
                }
            }

        })
    },
  • 处理播放总时长以及播放时间
_setTime() {
        let _this = this;
        duration = backgroundAudioManager.duration //获取播放总时长
        const durationFmt = this._dateFormat(duration)
        _this.setData({
            totalTime: `${durationFmt.min}:${durationFmt.sec}` //总时长
        })
    },
_dateFormat(sec) {
        //格式化时间
        const min = Math.floor(sec / 60) //分钟
        sec = Math.floor(sec % 60) //秒
        return {
            'min': this._parse0(min),
            'sec': this._parse0(sec)
        }
    },
    _parse0(sec) {
        // 补0
        return sec < 10 ? '0' + sec : sec
    },
拖拽播放
  • 拖拽进度条,拖拽到某个位置播放,微信也提供了api方法 backgroundAudioManager.seek,传入处理好的秒
SliderChange(e) {
        let _this = this;
        let value = e.detail.value;
        console.log(value, 'value')
        _this.data.progress = value;
        backgroundAudioManager.seek(backgroundAudioManager.duration * value / 100);
    },
播放与暂停
  • 其实播放与暂停只需要调用一下提供的api即可。其实在data里面定义一个变量,用来判断当前是播放状态还是暂停状态isPlaying
OnPlayPause() {
        // 播放事件
        if (this.data.isPlaying) {
            //正在播放,点击暂停
            backgroundAudioManager.pause()
        } else {
            // 点击播放
            backgroundAudioManager.play()
        }
        this.setData({
            isPlaying: !this.data.isPlaying
        })
    },
点击上一首
  • 点击上一首其实就是切换index,然后就是判断索引是否大于长度,如果大于就不让切换了,否则继续。
  • listdata就是列表,coursedata当前播放的数据。
OnNext() {
        // 下一首
        var _this = this;
        let listdata = _this.data.listdata;
        let { index } = _this.data.coursedata;
        _this.setData({
            next_ok: true,
            pre_ok: true
        })
        let i = ++index
        if (i >= listdata.length) {
            _this.setData({
                next_ok: false,
            })
            return
        }
        console.log(listdata,'listdata')
        this._loadMusicDetail(listdata[i].id)
        wx.removeStorageSync('voiceData')
        wx.removeStorageSync('currentTime')
    },
点击下一首
  • 点击下一首和点击上一首其实大差不差。逻辑几乎一模一样,一个加,一个减而已。
OnPre() {
        // 播放上一首
        var _this = this;
        let listdata = _this.data.listdata;
        let { index } = _this.data.coursedata;
        _this.setData({
            pre_ok: true,
            next_ok: true,
        })
        if (index <= 0) {
            _this.setData({
                pre_ok: false
            })
            index = 0
            return
        }
        this._loadMusicDetail(listdata[--index].id);
        wx.removeStorageSync('voiceData')
        wx.removeStorageSync('currentTime')
    },

定时关闭处理之一

  • 首先是在data定义一个数组array: ['不开启', '播放10分钟', '播放20分钟', '播放30分钟', '播放60分钟', '自定义'],
  • 当点击自定义的时候,就是一个时间
    在这里插入图片描述
  • 其实我做这个的时候,比如定义一个10分钟的话,就是10*60秒,最后得到600秒,然后调用一个方法,传过去,通过定时器,当时间结束之后,调用backgroundAudioManager.pause()关闭播放
bindPickerChange(e) {
        // 定时播放功能
        let i = e.detail.value
        if (i == 0) {
            let time = '';
            let value = '关闭定时器'
            this.setData({
                dateTime: "",
                dateTimeMin: "",
                dateTimeSec: ""
            })
            this.timeDate(time, value)

        } else if (i == 1) {
            let time = 10 * 60;
            let value = '播放10分钟'
            this.timeDate(time, value)

        } else if (i == 2) {
            let time = 20 * 60;
            let value = '播放20分钟'
            this.timeDate(time, value)

        } else if (i == 3) {
            let time = 30 * 60;
            let value = '播放30分钟'
            this.timeDate(time, value)

        } else if (i == 4) {
            let time = 60 * 60;
            let value = '播放60分钟'
            this.timeDate(time, value)
        } else {
            //自定义时间
            this.setData({
                isPickChangeList: false,
                isPickChangeTime: true
            })
        }
    },
  • isPickChangeTime设置为true的话,就是自定义的时间
    在这里插入图片描述
  • 对时间处理好之后,调用timeDate方法即可
bindTimeChange(e) {
        //自定义时间播放
        let date = e.detail.value.split(':')
        let h = Number((date[0] * 1) * 3600)
        let s = Number((date[1] * 1) * 60)
        let time = h + s
        if (time > 0) {
            this.timeDate(time, '')
            this.setData({
                isPickChangeTime: false,
                isPickChangeList: true
            })
        } else {
            wx.showToast({
                title: '请选择设置时间',
                icon: "none"
            })
            this.setData({
                isPickChangeList: false,
                isPickChangeTime: true
            })
        }
    },
  • timeDate是对传过来的时间处理
  • time传过来的时间(秒)
  • value提示内容
    在这里插入图片描述
timeDate(time, value) {
        let flag = false
        clearInterval(this.data.isTimePick)
        if (!flag) {
            if (value != '') {
                wx.showToast({
                    title: value,
                    icon: "none"
                })
            }

            if (time > 0) {
                this.data.isTimePick = setInterval(() => {
                    time--
                    if (time === 0) {
                        this.setData({
                            dateTime: "",
                            dateTimeMin: "",
                            dateTimeSec: "",
                            isPlaying: false
                        })
                        backgroundAudioManager.pause()
                        clearInterval(this.data.isTimePick)
                        wx.removeStorageSync('timingTime')
                        return
                    } else {
                        this.setData({
                            dateTime: Math.floor(time / 3600) < 10 ? '0' + Math.floor(time / 3600) : Math.floor(time / 3600),
                            dateTimeMin: Math.floor((time / 60 % 60)) < 10 ? '0' + Math.floor((time / 60 % 60)) : Math.floor((time / 60 % 60)),
                            dateTimeSec: Math.floor((time % 60)) < 10 ? '0' + Math.floor((time % 60)) : Math.floor((time % 60))
                        })
                    }
                    wx.setStorageSync('timingTime', time)
                }, 1000);
            }

        } else {
            flag = false
        }
    },
定位播放当前时间
  • 就是在详情播放背景音乐的时候。返回首页,想从首页一个按钮点击回到详情,并且定位到播放的位置。不能从头开始播放
  • 听到这个需求的时候,想到的就是通过本地存储。通过在backgroundAudioManager.onTimeUpdate方法执行本地存储。把每次播放的当前时间存储起来。
wx.setStorageSync('currentTime', currentTime)

在这里插入图片描述

  • 在首页,先取到本地的时间。还有详情的id
PlayStatusBtn: function () {
    let currentTime = wx.getStorageSync('currentTime')
    let timingTime = wx.getStorageSync('timingTime')
    wx.getBackgroundAudioPlayerState({
      success: function (t) {
        2 == t.status || null == o.globalData.vid ? wx.showModal({
          title: "提示",
          content: "没有正在播放的内容",
          showCancel: !1,
          success: function (t) { }
        }) : wx.navigateTo({
          url: "../detail/voice?id=" + o.globalData.vid +'&currentTime=' + currentTime+'&timingTime='+timingTime
        });
      },
    });
  },
  • 详情页中,在onload中执行,voiceData当前播放详情的数据,也是请求的是,把这个数据存在本地,只要当从首页进入的时候,在读取。
  • 通过判断time是否有值,如果有值,说明是从首页跳转过来的,否则直接调用
_this._loadMusicDetail(ids);

方法就行

let time = e.currentTime; //获取页面跳转时间
if (time && time > 0) {
            let res = wx.getStorageSync('voiceData')
            if (res.index == 0) {
                _this.setData({
                    pre_ok: false
                })
            } else {
                _this.setData({
                    pre_ok: true
                })
            }
            backgroundAudioManager.src = res.video_addr
            backgroundAudioManager.title = res.title;
            backgroundAudioManager.coverImgUrl = res.top_pic;

            backgroundAudioManager.protocol = 'hls';
            setTimeout(() => {
                backgroundAudioManager.seek(parseFloat(time))
            }, 1000)
            _this.setData({
                coursedata: res,
                listdata: res.datalist,
                videoid: res.id,
                isPlaying: true,
                next_ok: true,
                video_addr: res.video_addr
            })
            app.globalData.vid = res.id; //全局id
            _this.LoadComment(); //评论信息
        } else {
            _this._loadMusicDetail(ids);
        }
  • 以上就是关于背景音乐播放的全部代码,只提供大概思路。可以参考一下,如果谁工作中遇到问题,设置backgroundAudioManager.protocol = 'hls';在ios无法检测到backgroundAudioManager.onEnded这个api,有啥更好的办法,可以私聊一下哈
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值