wskeee
wskeee
  • 发布:2021-08-04 11:45
  • 更新:2021-08-06 15:11
  • 阅读:1014

【报Bug】微信小程序 iphone 手机,交替播放(暂停)音频和录音(暂停),导致部分录音丢失

分类:uni小程序sdk

产品分类: uni小程序SDK

手机系统: iOS

手机系统版本号: IOS 14

手机厂商: 苹果

手机机型: iphone11,iphone12(估计iphone系列)

页面类型: vue

SDK版本号: 2.19.0

示例代码:

主功能代码

<template>  
    <view class="lp-record">  
        <!-- 多选控件 -->  
        <view class="conbox record">  
            <view class="panel source-player" v-if="source">  
                <view class="head">原音播放</view>  
                <audio-player ref="audioPlayer" :audio="{src:source}"></audio-player>  
            </view>  

            <view class="panel">  
                <!-- 此处可放置倒计时,可根据需要自行添加 -->  
                <view class="time">  
                    {{showRecordTime}}  
                </view>  
                <view class="c999">  
                    最短{{minTime}}秒,最长{{maxTime}}秒  
                </view>  
                <view class="record-box" @touchmove.stop.prevent="onMoveHandler">  
                    <!-- 空占位 -->  
                    <view class="btn empty" v-if="isRecording" style="background: unset;"></view>  
                    <view class="btn recording" @touchstart="onTouchStartHandler"   
                        @touchend="onTouchEndHandler">  
                        <text class="ws-icon" :class="{flash:isRecording&&!isRecordPaused}" style="font-size: 35rpx;">  
                            {{isRecording&&!isRecordPaused ? '暂停' : '录音'}}  
                        </text>  
                    </view>  
                    <view class="btn confirm" @touchstart.stop="onRecordEndHandler" v-if="isRecording"><text  
                            class="ws-icon">结束</text></view>  
                    <view class="btn confirm" @touchstart.stop="onConfirmHandler" v-if="!isRecording && playPath"><text  
                            class="gui-icons">确认</text></view>  
                </view>  
                <view class="c666 fz32 domess">  
                    {{isRecording ? (isRecordPaused ? '已暂停' : '录音中') : (playPath ? '录音完成' : '点击开始录音')}}  
                </view>  
                <view v-if="playPath">  
                    <view class="head">录音播放</view>  
                    <audio-player :audio="{src:playPath}"></audio-player>  
                </view>  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {  
                frame: 50, // 执行绘画的频率,单位毫秒  
                recordTime: 0, //录音时长  
                isRecording: false, //是否录制中  
                isRecordPaused: false, //是否录制暂停  
                playing: 0, //是否播放中  
                playPath: "",  

                recorderManager: null, //录音  
                innerAudioContext: null, //播放  

                source: 'https://statici.jpworld.cn/files/aaaaaa0802.mp3',  
                maxTime: 600,  
                minTime: 5,  
                canPuase: true,  
            }  
        },  
        computed: {  
            showRecordTime() {  
                var strs = "";  
                var m = Math.floor(this.recordTime / 60);  
                if (m < 10) strs = "0" + m;  

                var s = this.recordTime % 60;  
                strs += (s < 10) ? ":0" + s : ":" + s;  

                return strs  
            },  
        },  
        onLoad() {  
            console.log('onload');  
            var _this = this;  

            this.initValue();  
            //获取录音权限  
            try {  
                uni.authorize({  
                    scope: 'scope.record',  
                    success() {}  
                })  
            } catch (e) {  
                console.error(e);  
            }  
            this.showPicker();  
        },  
        beforeDestroy() {  
            if (this.isRecording) {  
                this.recordEnd(true);  
            }  
            this.stopVoice();  
            this.pauseTime();  
        },  
        onReady() {  
            console.log('onReady');  
        },  
        methods: {  
            //-----------------------------------------------------------------------------------------------  
            //  
            // action  
            //  
            //-----------------------------------------------------------------------------------------------  
            //组件数据初始化  进入时、关闭时调用初始化  
            initValue() {},  
            //显示组件  
            showPicker() {  
                this.recordReset();  
                this.initSound();  
                //this.$emit('open');  
            },  
            //关闭组件  
            closePicker() {  
                //点遮罩 点取消关闭说明用户不想修改,所以这里对数据进行初始化  
                //this.initValue();   
                if (this.isRecording) {  
                    this.recordEnd();  
                }  
                this.destorySound();  
                this.stopVoice();  
                //this.$emit('close');  
            },  

            recordReset: function() {  
                this.playPath = ""; //音频地址        
                this.stopVoice();  
                this.resetTime();  
            },  

            recordStart: function() {  
                let _this = this;  

                console.log('recordStart', this.recorderManager);  

                _this.resetTime();  

                // 开始录音  
                this.recorderManager.start({  
                    duration: this.maxTime * 1000  
                });  

            },  

            recordPause: function() {  
                this.recorderManager.pause();  
            },  

            recordResume: function() {  
                this.recorderManager.resume();  
            },  

            recordEnd: function(force) {  
                let recordTime = this.recordTime;  
                force = !!force;  

                if (!force && recordTime < this.minTime) {  
                    if (recordTime <= 0) {  
                        //==点击事件==;  
                        return false;  
                    }  
                    //==小于5秒==;  
                    uni.showToast({  
                        title: "不能小于" + this.minTime + "秒,请重新录制",  
                        icon: "none"  
                    })  
                    return false;  
                }  
                console.log('recordEnd');  
                this.recorderManager.stop();  
            },  

            playVoice() {  
                if (this.playPath) {  
                    this.innerAudioContext.src = this.playPath;  
                    this.innerAudioContext.play();  
                    this.playing = 1;  
                }  
            },  
            stopVoice() {  
                if (this.innerAudioContext) {  
                    this.innerAudioContext.stop();  
                }  

                this.playing = 0;  
            },  
            //-----------------------------------------------------------------------------------------------  
            //  
            // Source  
            //  
            //-----------------------------------------------------------------------------------------------  
            initSound: function() {  
                console.log('initSound');  
                this.recorderManager = uni.getRecorderManager(); //录音  
                this.innerAudioContext = uni.createInnerAudioContext(); //播放  
                var _this = this;  

                this.recorderManager.onStart(function() {  
                    console.log('开始录音');  
                    _this.startTime();  
                    _this.isRecording = true;  
                });  

                //录音暂停事件  
                this.recorderManager.onPause(function() {  
                    console.log('录音暂停');  
                    _this.isRecordPaused = true;  
                    _this.pauseTime();  
                });  

                // 录音恢复事件  
                this.recorderManager.onResume(function() {  
                    console.log('录音继续');  
                    _this.isRecordPaused = false;  
                    _this.startTime();  
                })  

                //录音停止事件  
                this.recorderManager.onStop(function(res) {  
                    console.log('开始结束' + JSON.stringify(res));  
                    _this.pauseTime();  
                    _this.isRecording = false;  
                    _this.playPath = res.tempFilePath;  
                    _this.onConfirmHandler();  
                });  
            },  

            destorySound: function() {  
                if (this.recorderManager) {  
                    this.recorderManager.stop();  
                }  
                if (this.innerAudioContext) {  
                    this.innerAudioContext.stop();  
                }  
            },  
            //-----------------------------------------------------------------------------------------------  
            //  
            // Timer  
            //  
            //-----------------------------------------------------------------------------------------------  
            resetTime: function() {  
                this.recordTime = 0;  
                this.pauseTime();  

            },  

            startTime: function() {  
                let _this = this;  
                this.pauseTime();  
                _this.timeObj = setInterval(function() {  
                    _this.recordTime++;  
                    //this.$refs.recordClock.setValue(_this.recordTime / _this.maxTime);  
                    if (_this.recordTime == _this.maxTime) _this.recordEnd();  
                }, 1000);  

            },  

            pauseTime: function() {  
                clearInterval(this.timeObj);  
            },  
            //-----------------------------------------------------------------------------------------------  
            //  
            // Draw  
            //  
            //-----------------------------------------------------------------------------------------------  

            //-----------------------------------------------------------------------------------------------  
            //  
            // handler  
            //  
            //-----------------------------------------------------------------------------------------------  
            /**  
             * 事件取消  
             */  
            onMoveHandler() {  
                return false;  
            },  

            /**  
             *   
             */  
            onTouchStartHandler: function() {  
                console.log('onTouchStartHandler');  
                // 可以暂停情况下,开始录制  
                if (this.canPuase) {  
                    if (this.isRecording) {  
                        this.isRecordPaused ? this.recordResume() : this.recordPause();  
                    } else {  
                        this.recordReset();  
                        this.recordStart();  
                    }  
                } else {  
                    this.recordReset();  
                }  
            },  

            /**  
             * 长按  
             */  
            onLongpressHandler: function() {  
                // 不可以暂停情况下,开始录制  
                if (!this.canPuase) {  
                    this.recordStart();  
                }  

            },  

            /**  
             * 长按结束  
             */  
            onTouchEndHandler: function() {  
                if (!this.canPuase) {  
                    this.recordEnd();  
                }  
            },  

            onRecordEndHandler: function() {  
                this.recordEnd();  
            },  

            //点击确定  
            onConfirmHandler() {  
                // var data = {},list = {},textStr = "",indexStr = "";                                
                this.$emit('recconfirm', this.playPath)  

                //确定后更新默认初始值,这样再次进入默认初值就是最后选择的  
                // this.defaultArr = textStr;  
                //关闭  
                this.closePicker();  
            },  
        }  
    }  
</script>  

<style lang="scss">  
    .lp-record {  
        position: relative;  
        z-index: 99;  

        .mask {  
            position: fixed;  
            z-index: 1000;  
            top: 0;  
            right: 0;  
            left: 0;  
            bottom: 0;  
            background: rgba(0, 0, 0, 0.6);  
        }  

        .conbox {  
            background: #fff;  
        }  

        .c666 {  
            color: #666;  
        }  

        .c999 {  
            color: #999;  
        }  

        .fz28 {  
            font-size: 28rpx;  
        }  

        .fz32 {  
            font-size: 32rpx;  
        }  

        .source-player {  
            padding: 50rpx 0rpx;  
            border-bottom: solid 1px #eeeeee;  

            .head {  
                text-align: left;  
                font-size: 30rpx;  
                margin-bottom: 20rpx;  
            }  
        }  

        .record {  
            text-align: center;  

            .time {  
                text-align: center;  
                font-size: 60rpx;  
                color: #000;  
                line-height: 100rpx;  
                margin-top: 50rpx;  
            }  

            .domess {  
                margin-bottom: 50rpx;  
            }  

            .record-box {  
                display: flex;  
                flex-direction: row;  
                justify-content: center;  
                align-items: center;  
            }  

            .btn {  
                display: flex;  
                justify-content: center;  
                align-items: center;  
                width: 90rpx;  
                height: 90rpx;  
                border-radius: 50%;  
                color: #fff;  
                background-color: $uni-color-primary;  

                text {  
                    font-size: 40rpx;  
                }  
            }  

            .recording {  
                //position: absolute;  
                //top: 10px;  
                //left: 10px;  
                margin: 20rpx;  
                width: 80px;  
                height: 80px;  
                background-color: $uni-color-error;  
                z-index: 100;  
                font-size: 35px;  
            }  

            .stop {}  

            .play {  
                margin-left: 5rpx;  
            }  

            .confirm {}  

        }  

    }  

    .flash {  
        animation: 2s opacity2 0s infinite;  
        -webkit-animation: 2s opacity2 0s infinite;  
        -moz-animation: 2s opacity2 0s infinite;  
    }  

    @keyframes opacity2 {  
        0% {  
            opacity: 0.1  
        }  

        50% {  
            opacity: .8;  
        }  

        100% {  
            opacity: 0.1;  
        }  
    }  

    @-webkit-keyframes opacity2 {  
        0% {  
            opacity: 0.1  
        }  

        50% {  
            opacity: .8;  
        }  

        100% {  
            opacity: 0.1;  
        }  
    }  

    @-moz-keyframes opacity2 {  
        0% {  
            opacity: 0.1  
        }  

        50% {  
            opacity: .8;  
        }  

        100% {  
            opacity: 0.1;  
        }  
    }  
</style>  

播放组件代码;

<template>  
    <view class="gui-player" :class="{mini:mini,nobroud:nobroud}">  
        <!-- 播放控制 -->  
        <view class="gui-player-console">  
            <view class="gui-player-console-c">  
                <text class="gui-player-tool gui-icons" style="font-size:33rpx;" @tap="pause" v-if="playStatus == 1">暂停</text>  
                <text class="gui-player-tool gui-icons" style="font-size:33rpx;margin-left: 4rpx;" @tap="play" v-if="playStatus == 2">播放</text>  
            </view>  
            <!-- 播放进度 -->  
            <view v-if="!mini" class="progress-bar">  
                {{playTime}} / {{timeFormat(audioLength)}}  
            </view>  
        </view>  
    </view>  
</template>  
<script>  
    export default {  
        props: {  
            color: {  
                type: String,  
                default: '#333'  
            },  
            mini: {  
                type: Boolean,  
                default: false  
            },  
            nobroud:{  
                type: Boolean,  
                default: false  
            },  
            autoPlay: {  
                type: Boolean,  
                default: false  
            },  
            audio: {  
                type: Object,  
                default: {  
                    title: "我们都一样",  
                    singer: "张杰",  
                    epname: "杰哥精选",  
                    coverImgUrl: "https://7465-test01-632ffe-1258717418.tcb.qcloud.la/personal/player/images/jie02.jpg?sign=00e5e68d81145037000a162e2220736a&t=1556345760",  
                    src: "https://7465-test01-632ffe-1258717418.tcb.qcloud.la/personal/player/song/%E6%88%91%E4%BB%AC%E9%83%BD%E4%B8%80%E6%A0%B7%20-%20%E5%BC%A0%E6%9D%B0.mp3?sign=008d62b6bea06a8a6814b5f284fac0ac&t=1556345730"  
                }  
            }  
        },  
        data() {  
            return {  
                playStatus: 2,  
                player: null,  
                playTime: '00:00',  
                timer: null,  
                audioLength: 1  
            }  
        },  
        beforeDestroy() {  
            this.destroy();  
        },  
        created: function() {  
            this.player = uni.createInnerAudioContext();  
            this.player.onTimeUpdate(() => {  
                try {  
                    if (this.playStatus != 1) {  
                        return;  
                    }  
                    this.audioLength = this.player.duration;  
                    // 调整进度  
                    var progress = this.player.currentTime / this.audioLength;  
                    progress = Math.round(progress * 100);  
                    var ref = this.$refs.graceSingleSlider;  
                    if (ref) {  
                        ref.setProgress(progress);  
                    }  
                    this.playTime = this.timeFormat(this.player.currentTime);  
                } catch (e) {};  
            });  
            this.player.onPlay(() => {  
                this.playStatus = 1;  
                this.audioLength = this.player.duration;  
            });  
            this.player.onPause(() => {  
                this.playStatus = 2;  
            });  
            this.player.onEnded(() => {  
                this.playStatus = 2;  
            });  

            this.load(this.audio, this.autoPlay);  
        },  
        methods: {  
            load: function(audio, autoPlay) {  
                //this.player.title = audio.title;  
                //this.player.singer = audio.singer;  
                //this.player.coverImgUrl = audio.coverImgUrl;  
                console.log(audio);  
                this.player.src = audio.src;  
                autoPlay && this.player.play();  
            },  
            progressChange: function(e) {  
                if (this.timer != null) {  
                    clearTimeout(this.timer);  
                }  
                this.player.pause();  
                var needTime = this.audioLength * e / 100;  
                needTime = Math.round(needTime);  
                this.playTime = this.timeFormat(needTime);  
                this.timer = setTimeout(() => {  
                    this.player.seek(needTime);  
                    this.player.play();  
                }, 800);  
            },  
            timeFormat: function(s) {  
                s = Math.round(s);  
                if (s < 60) {  
                    if (s < 10) {  
                        return '00:0' + s;  
                    }  
                    return '00:' + s;  
                } else {  
                    var second = s % 60;  
                    s = s - second;  
                    var minute = s / 60;  
                    if (minute < 10) {  
                        minute = '0' + minute;  
                    }  
                    if (second < 10) {  
                        second = '0' + second;  
                    }  
                    return minute + ':' + second;  
                }  

            },  

            pause: function() {  
                console.log('pause');  
                this.player && this.player.pause();  
            },  
            play: function() {  
                console.log('play',this.player.src);  
                this.player && this.player.play();  
            },  
            destroy: function() {  
                this.player && this.player.destroy();  
            },  
        }  
    }  
</script>  
<style lang="scss" scoped>  
    .gui-player {  
        padding: 30rpx;  
        border: solid 1px #eee;  
        border-radius: 10rpx;  

        .gui-player-console {  
            display: flex;  
            justify-content: center;  
            align-items: center;  
        }  

        .gui-player-tool {  
            width: 96rpx;  
            line-height: 96rpx;  
            text-align: center;  
            font-size: 33rpx;  
            display: block;  
            flex-shrink: 0;  
            color: #fff;  
        }  

        .gui-player-console-c {  
            display: flex;  
            align-items: center;  
            background: #28b28b;  
            border-radius: 50%;  
            width: 96rpx;  
            height: 96rpx;  
        }  

        .progress-bar {  
            flex: 1;  
            padding: 25rpx;  
        }  
    }  

    .mini ,.nobroud{  
        padding: unset;  
        border: unset;  
    }  
</style>  

操作步骤:

1、点原音播放几秒后暂停
2、点录音几秒后暂停
3、交替1、2点多次
4、结束录音

预期结果:

录音完成

实际结果:

录音不完整,如暂停多次后,录音会丢失10几20秒,暂停次数越多,丢失得越多。

bug描述:

交替播放(暂停)音频和录音(暂停),导致部分录音丢失

2021-08-04 11:45 负责人:无 分享
已邀请:
DCloud_UNI_GSQ

DCloud_UNI_GSQ

到底是微信小程序还是 APP?如果确认是微信小程序的话,需反馈到微信小程序社区。

  • wskeee (作者)

    微信小程序,APP我没有测过

    2021-08-07 10:29

  • wskeee (作者)

    微信社区需要原生代码,我现在是uniapp代码怎么给他们反馈?https://developers.weixin.qq.com/community/develop/doc/000808deed8378249ee9f82965b800 微信社区好像也有类似的反馈,但到现在也没解决。

    2021-08-07 10:32

  • DCloud_UNI_GSQ

    回复 wskeee: 原生代码写个最简单的示例分享为代码片段即可。反馈的人多了应该会得到重视。

    2021-08-08 16:54

该问题目前已经被锁定, 无法添加新回复