校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃

主頁 > 知識(shí)庫 > html5錄音功能實(shí)戰(zhàn)示例

html5錄音功能實(shí)戰(zhàn)示例

熱門標(biāo)簽:黃石ai電銷機(jī)器人呼叫中心 電話機(jī)器人技術(shù) 高德地圖標(biāo)注商戶怎么標(biāo) 欣鼎電銷機(jī)器人 效果 惡搞電話機(jī)器人 ok電銷機(jī)器人 如何查看地圖標(biāo)注 智能電銷機(jī)器人被禁用了么 地圖標(biāo)注軟件打印出來

緣起

由于項(xiàng)目需要,我們要在web端實(shí)現(xiàn)錄音功能。一開始,找到的方案有兩個(gè),一個(gè)是通過iframe,一個(gè)是html5的getUserMedia api。由于我們的錄音功能不需要兼容IE瀏覽器,所以毫不猶豫的選擇了html5提供的getUserMedia去實(shí)現(xiàn)?;舅悸肥菂⒖剂斯俜降腶pi文檔以及網(wǎng)上查找的一些方案做結(jié)合做出了適合項(xiàng)目需要的方案。但由于我們必須保證這個(gè)錄音功能能夠同時(shí)在pad端、pc端都可以打開,所以其中也踩了一些坑。以下為過程還原。

步驟1

由于新的api是通過navigator.mediaDevices.getUserMedia,且返回一個(gè)promise。

而舊的api是navigator.getUserMedia,于是做了一個(gè)兼容性。代碼如下:

// 老的瀏覽器可能根本沒有實(shí)現(xiàn) mediaDevices,所以我們可以先設(shè)置一個(gè)空的對(duì)象
if (navigator.mediaDevices === undefined) {
    navigator.mediaDevices = {};
}

// 一些瀏覽器部分支持 mediaDevices。我們不能直接給對(duì)象設(shè)置 getUserMedia
// 因?yàn)檫@樣可能會(huì)覆蓋已有的屬性。這里我們只會(huì)在沒有g(shù)etUserMedia屬性的時(shí)候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
    let getUserMedia =
        navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia;
    navigator.mediaDevices.getUserMedia = function(constraints) {
        // 首先,如果有g(shù)etUserMedia的話,就獲得它

        // 一些瀏覽器根本沒實(shí)現(xiàn)它 - 那么就返回一個(gè)error到promise的reject來保持一個(gè)統(tǒng)一的接口
        if (!getUserMedia) {
            return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
        }

        // 否則,為老的navigator.getUserMedia方法包裹一個(gè)Promise
        return new Promise(function(resolve, reject) {
            getUserMedia.call(navigator, constraints, resolve, reject);
        });
    };

步驟2

這是網(wǎng)上存在的一個(gè)方法,封裝了一個(gè)HZRecorder。基本上引用了這個(gè)方法。調(diào)用HZRecorder.get就可以調(diào)起錄音接口,這個(gè)方法傳入一個(gè)callback函數(shù),new HZRecorder后執(zhí)行callback函數(shù)且傳入一個(gè)實(shí)體化后的HZRecorder對(duì)象。可以通過該對(duì)象的方法實(shí)現(xiàn)開始錄音、暫停、停止、播放等功能。

var HZRecorder = function (stream, config) {  
    config = config || {};  
    config.sampleBits = config.sampleBits || 8;      //采樣數(shù)位 8, 16  
    config.sampleRate = config.sampleRate || (44100 / 6);   //采樣率(1/6 44100)  

      
    //創(chuàng)建一個(gè)音頻環(huán)境對(duì)象  
    audioContext = window.AudioContext || window.webkitAudioContext;  
    var context = new audioContext();  

    //將聲音輸入這個(gè)對(duì)像  
    var audioInput = context.createMediaStreamSource(stream);  
      
    //設(shè)置音量節(jié)點(diǎn)  
    var volume = context.createGain();  
    audioInput.connect(volume);  

    //創(chuàng)建緩存,用來緩存聲音  
    var bufferSize = 4096;  

    // 創(chuàng)建聲音的緩存節(jié)點(diǎn),createScriptProcessor方法的  
    // 第二個(gè)和第三個(gè)參數(shù)指的是輸入和輸出都是雙聲道。  
    var recorder = context.createScriptProcessor(bufferSize, 2, 2);  

    var audioData = {  
        size: 0          //錄音文件長(zhǎng)度  
        , buffer: []     //錄音緩存  
        , inputSampleRate: context.sampleRate    //輸入采樣率  
        , inputSampleBits: 16       //輸入采樣數(shù)位 8, 16  
        , outputSampleRate: config.sampleRate    //輸出采樣率  
        , oututSampleBits: config.sampleBits       //輸出采樣數(shù)位 8, 16  
        , input: function (data) {  
            this.buffer.push(new Float32Array(data));  
            this.size += data.length;  
        }  
        , compress: function () { //合并壓縮  
            //合并  
            var data = new Float32Array(this.size);  
            var offset = 0;  
            for (var i = 0; i < this.buffer.length; i++) {  
                data.set(this.buffer[i], offset);  
                offset += this.buffer[i].length;  
            }  
            //壓縮  
            var compression = parseInt(this.inputSampleRate / this.outputSampleRate);  
            var length = data.length / compression;  
            var result = new Float32Array(length);  
            var index = 0, j = 0;  
            while (index < length) {  
                result[index] = data[j];  
                j += compression;  
                index++;  
            }  
            return result;  
        }  
        , encodeWAV: function () {  
            var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);  
            var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);  
            var bytes = this.compress();  
            var dataLength = bytes.length * (sampleBits / 8);  
            var buffer = new ArrayBuffer(44 + dataLength);  
            var data = new DataView(buffer);  

            var channelCount = 1;//單聲道  
            var offset = 0;  

            var writeString = function (str) {  
                for (var i = 0; i < str.length; i++) {  
                    data.setUint8(offset + i, str.charCodeAt(i));  
                }  
            };  
              
            // 資源交換文件標(biāo)識(shí)符   
            writeString('RIFF'); offset += 4;  
            // 下個(gè)地址開始到文件尾總字節(jié)數(shù),即文件大小-8   
            data.setUint32(offset, 36 + dataLength, true); offset += 4;  
            // WAV文件標(biāo)志  
            writeString('WAVE'); offset += 4;  
            // 波形格式標(biāo)志   
            writeString('fmt '); offset += 4;  
            // 過濾字節(jié),一般為 0x10 = 16   
            data.setUint32(offset, 16, true); offset += 4;  
            // 格式類別 (PCM形式采樣數(shù)據(jù))   
            data.setUint16(offset, 1, true); offset += 2;  
            // 通道數(shù)   
            data.setUint16(offset, channelCount, true); offset += 2;  
            // 采樣率,每秒樣本數(shù),表示每個(gè)通道的播放速度   
            data.setUint32(offset, sampleRate, true); offset += 4;  
            // 波形數(shù)據(jù)傳輸率 (每秒平均字節(jié)數(shù)) 單聲道×每秒數(shù)據(jù)位數(shù)×每樣本數(shù)據(jù)位/8   
            data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;  
            // 快數(shù)據(jù)調(diào)整數(shù) 采樣一次占用字節(jié)數(shù) 單聲道×每樣本的數(shù)據(jù)位數(shù)/8   
            data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;  
            // 每樣本數(shù)據(jù)位數(shù)   
            data.setUint16(offset, sampleBits, true); offset += 2;  
            // 數(shù)據(jù)標(biāo)識(shí)符   
            writeString('data'); offset += 4;  
            // 采樣數(shù)據(jù)總數(shù),即數(shù)據(jù)總大小-44   
            data.setUint32(offset, dataLength, true); offset += 4;  
            // 寫入采樣數(shù)據(jù)   
            if (sampleBits === 8) {  
                for (var i = 0; i < bytes.length; i++, offset++) {  
                    var s = Math.max(-1, Math.min(1, bytes[i]));  
                    var val = s < 0 ? s * 0x8000 : s * 0x7FFF;  
                    val = parseInt(255 / (65535 / (val + 32768)));  
                    data.setInt8(offset, val, true);  
                }  
            } else {  
                for (var i = 0; i < bytes.length; i++, offset += 2) {  
                    var s = Math.max(-1, Math.min(1, bytes[i]));  
                    data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);  
                }  
            }  

            return new Blob([data], { type: 'audio/wav' });  
        }  
    };  

    //開始錄音  
    this.start = function () {  
        audioInput.connect(recorder);  
        recorder.connect(context.destination);  
    };  

    //停止  
    this.stop = function () {  
        recorder.disconnect();  
    };  
    
    // 結(jié)束
    this.end = function() {
        context.close();
    };
    
    // 繼續(xù)
    this.again = function() {
        recorder.connect(context.destination);
    };

    //獲取音頻文件  
    this.getBlob = function () {  
        this.stop();  
        return audioData.encodeWAV();  
    };  

    //回放  
    this.play = function (audio) {  
        audio.src = window.URL.createObjectURL(this.getBlob());  
    };  

    //上傳  
    this.upload = function (url, callback) {  
        var fd = new FormData();  
        fd.append('audioData', this.getBlob());  
        var xhr = new XMLHttpRequest();  
        if (callback) {  
            xhr.upload.addEventListener('progress', function (e) {  
                callback('uploading', e);  
            }, false);  
            xhr.addEventListener('load', function (e) {  
                callback('ok', e);  
            }, false);  
            xhr.addEventListener('error', function (e) {  
                callback('error', e);  
            }, false);  
            xhr.addEventListener('abort', function (e) {  
                callback('cancel', e);  
            }, false);  
        }  
        xhr.open('POST', url);  
        xhr.send(fd);  
    };  

    //音頻采集  
    recorder.onaudioprocess = function (e) {  
        audioData.input(e.inputBuffer.getChannelData(0));  
        //record(e.inputBuffer.getChannelData(0));  
    };  

};  

//拋出異常  
HZRecorder.throwError = function (message) {  
    throw new function () { this.toString = function () { return message; };};  
};  
//是否支持錄音  
HZRecorder.canRecording = (navigator.getUserMedia != null);  
//獲取錄音機(jī)  
HZRecorder.get = function (callback, config) {  
   if (callback) {
        navigator.mediaDevices
            .getUserMedia({ audio: true })
            .then(function(stream) {
                let rec = new HZRecorder(stream, config);
                callback(rec);
            })
            .catch(function(error) {
                HZRecorder.throwError('無法錄音,請(qǐng)檢查設(shè)備狀態(tài)');
            });
    }
};  
window.HZRecorder = HZRecorder;

以上,已經(jīng)可以滿足大部分的需求。但是我們要兼容pad端。我們的pad有幾個(gè)問題必須解決。

  • 錄音格式必須是mp3才能播放
  • window.URL.createObjectURL傳入blob數(shù)據(jù)在pad端報(bào)錯(cuò),轉(zhuǎn)不了

以下為解決這兩個(gè)問題的方案。

步驟3

以下為我實(shí)現(xiàn) 錄音格式為mp3 和 window.URL.createObjectURL傳入blob數(shù)據(jù)在pad端報(bào)錯(cuò) 的方案。

1、修改HZRecorder里的audioData對(duì)象代碼。并引入網(wǎng)上一位大神的一個(gè)js文件lamejs.js

const lame = new lamejs();
let audioData = {
    samplesMono: null,
    maxSamples: 1152,
    mp3Encoder: new lame.Mp3Encoder(1, context.sampleRate || 44100, config.bitRate || 128),
    dataBuffer: [],
    size: 0, // 錄音文件長(zhǎng)度
    buffer: [], // 錄音緩存
    inputSampleRate: context.sampleRate, // 輸入采樣率
    inputSampleBits: 16, // 輸入采樣數(shù)位 8, 16
    outputSampleRate: config.sampleRate, // 輸出采樣率
    oututSampleBits: config.sampleBits, // 輸出采樣數(shù)位 8, 16
    convertBuffer: function(arrayBuffer) {
        let data = new Float32Array(arrayBuffer);
        let out = new Int16Array(arrayBuffer.length);
        this.floatTo16BitPCM(data, out);
        return out;
    },
    floatTo16BitPCM: function(input, output) {
        for (let i = 0; i < input.length; i++) {
            let s = Math.max(-1, Math.min(1, input[i]));
            output[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
        }
    },
    appendToBuffer: function(mp3Buf) {
        this.dataBuffer.push(new Int8Array(mp3Buf));
    },
    encode: function(arrayBuffer) {
        this.samplesMono = this.convertBuffer(arrayBuffer);
        let remaining = this.samplesMono.length;
        for (let i = 0; remaining >= 0; i += this.maxSamples) {
            let left = this.samplesMono.subarray(i, i + this.maxSamples);
            let mp3buf = this.mp3Encoder.encodeBuffer(left);
            this.appendToBuffer(mp3buf);
            remaining -= this.maxSamples;
        }
    },
    finish: function() {
        this.appendToBuffer(this.mp3Encoder.flush());
        return new Blob(this.dataBuffer, { type: 'audio/mp3' });
    },
    input: function(data) {
        this.buffer.push(new Float32Array(data));
        this.size += data.length;
    },
    compress: function() {
        // 合并壓縮
        // 合并
        let data = new Float32Array(this.size);
        let offset = 0;
        for (let i = 0; i < this.buffer.length; i++) {
            data.set(this.buffer[i], offset);
            offset += this.buffer[i].length;
        }
        // 壓縮
        let compression = parseInt(this.inputSampleRate / this.outputSampleRate, 10);
        let length = data.length / compression;
        let result = new Float32Array(length);
        let index = 0;
        let j = 0;
        while (index < length) {
            result[index] = data[j];
            j += compression;
            index++;
        }
        return result;
    },
    encodeWAV: function() {
        let sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
        let sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
        let bytes = this.compress();
        let dataLength = bytes.length * (sampleBits / 8);
        let buffer = new ArrayBuffer(44 + dataLength);
        let data = new DataView(buffer);

        let channelCount = 1; // 單聲道
        let offset = 0;

        let writeString = function(str) {
            for (let i = 0; i < str.length; i++) {
                data.setUint8(offset + i, str.charCodeAt(i));
            }
        };

        // 資源交換文件標(biāo)識(shí)符
        writeString('RIFF');
        offset += 4;
        // 下個(gè)地址開始到文件尾總字節(jié)數(shù),即文件大小-8
        data.setUint32(offset, 36 + dataLength, true);
        offset += 4;
        // WAV文件標(biāo)志
        writeString('WAVE');
        offset += 4;
        // 波形格式標(biāo)志
        writeString('fmt ');
        offset += 4;
        // 過濾字節(jié),一般為 0x10 = 16
        data.setUint32(offset, 16, true);
        offset += 4;
        // 格式類別 (PCM形式采樣數(shù)據(jù))
        data.setUint16(offset, 1, true);
        offset += 2;
        // 通道數(shù)
        data.setUint16(offset, channelCount, true);
        offset += 2;
        // 采樣率,每秒樣本數(shù),表示每個(gè)通道的播放速度
        data.setUint32(offset, sampleRate, true);
        offset += 4;
        // 波形數(shù)據(jù)傳輸率 (每秒平均字節(jié)數(shù)) 單聲道×每秒數(shù)據(jù)位數(shù)×每樣本數(shù)據(jù)位/8
        data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true);
        offset += 4;
        // 快數(shù)據(jù)調(diào)整數(shù) 采樣一次占用字節(jié)數(shù) 單聲道×每樣本的數(shù)據(jù)位數(shù)/8
        data.setUint16(offset, channelCount * (sampleBits / 8), true);
        offset += 2;
        // 每樣本數(shù)據(jù)位數(shù)
        data.setUint16(offset, sampleBits, true);
        offset += 2;
        // 數(shù)據(jù)標(biāo)識(shí)符
        writeString('data');
        offset += 4;
        // 采樣數(shù)據(jù)總數(shù),即數(shù)據(jù)總大小-44
        data.setUint32(offset, dataLength, true);
        offset += 4;
        // 寫入采樣數(shù)據(jù)
        if (sampleBits === 8) {
            for (let i = 0; i < bytes.length; i++, offset++) {
                const s = Math.max(-1, Math.min(1, bytes[i]));
                let val = s < 0 ? s * 0x8000 : s * 0x7fff;
                val = parseInt(255 / (65535 / (val + 32768)), 10);
                data.setInt8(offset, val, true);
            }
        } else {
            for (let i = 0; i < bytes.length; i++, offset += 2) {
                const s = Math.max(-1, Math.min(1, bytes[i]));
                data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
            }
        }

        return new Blob([data], { type: 'audio/wav' });
    }
};

2、修改HZRecord的音頻采集的調(diào)用方法。

// 音頻采集
recorder.onaudioprocess = function(e) {
    audioData.encode(e.inputBuffer.getChannelData(0));
};

3、HZRecord的getBlob方法。

this.getBlob = function() {
    this.stop();
    return audioData.finish();
};

4、HZRecord的play方法。把blob轉(zhuǎn)base64url。

this.play = function(func) {
    readBlobAsDataURL(this.getBlob(), func);
};

function readBlobAsDataURL(data, callback) {
    let fileReader = new FileReader();
    fileReader.onload = function(e) {
        callback(e.target.result);
    };
    fileReader.readAsDataURL(data);
}

至此,已經(jīng)解決以上兩個(gè)問題。

步驟4

這里主要介紹怎么做錄音時(shí)的動(dòng)效。我們的一個(gè)動(dòng)效需求為:

根據(jù)傳入的音量大小,做一個(gè)圓弧動(dòng)態(tài)擴(kuò)展。

// 創(chuàng)建analyser節(jié)點(diǎn),獲取音頻時(shí)間和頻率數(shù)據(jù)
const analyser = context.createAnalyser();
audioInput.connect(analyser);
const inputAnalyser = new Uint8Array(1);
const wrapEle = $this.refs['wrap'];
let ctx = wrapEle.getContext('2d');
const width = wrapEle.width;
const height = wrapEle.height;
const center = {
    x: width / 2,
    y: height / 2
};

function drawArc(ctx, color, x, y, radius, beginAngle, endAngle) {
    ctx.beginPath();
    ctx.lineWidth = 1;
    ctx.strokeStyle = color;
    ctx.arc(x, y, radius, (Math.PI * beginAngle) / 180, (Math.PI * endAngle) / 180);
    ctx.stroke();
}

(function drawSpectrum() {
    analyser.getByteFrequencyData(inputAnalyser); // 獲取頻域數(shù)據(jù)
    ctx.clearRect(0, 0, width, height);
    // 畫線條
    for (let i = 0; i < 1; i++) {
        let value = inputAnalyser[i] / 3; // <===獲取數(shù)據(jù)
        let colors = [];
        if (value <= 16) {
            colors = ['#f5A631', '#f5A631', '#e4e4e4', '#e4e4e4', '#e4e4e4', '#e4e4e4'];
        } else if (value <= 32) {
            colors = ['#f5A631', '#f5A631', '#f5A631', '#f5A631', '#e4e4e4', '#e4e4e4'];
        } else {
            colors = ['#f5A631', '#f5A631', '#f5A631', '#f5A631', '#f5A631', '#f5A631'];
        }
        drawArc(ctx, colors[0], center.x, center.y, 52 + 16, -30, 30);
        drawArc(ctx, colors[1], center.x, center.y, 52 + 16, 150, 210);
        drawArc(ctx, colors[2], center.x, center.y, 52 + 32, -22.5, 22.5);
        drawArc(ctx, colors[3], center.x, center.y, 52 + 32, 157.5, 202.5);
        drawArc(ctx, colors[4], center.x, center.y, 52 + 48, -13, 13);
        drawArc(ctx, colors[5], center.x, center.y, 52 + 48, 167, 193);
    }

    // 請(qǐng)求下一幀
    requestAnimationFrame(drawSpectrum);
})();

緣盡

至此,一個(gè)完整的html5錄音功能方案已經(jīng)完成。有什么需要補(bǔ)充,不合理的地方的歡迎留言。

ps:lamejs可參考這個(gè)github

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

標(biāo)簽:金昌 盤錦 赤峰 綏化 阿壩 中山 萍鄉(xiāng) 聊城

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《html5錄音功能實(shí)戰(zhàn)示例》,本文關(guān)鍵詞  html5,錄音,功能,實(shí)戰(zhàn),示例,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《html5錄音功能實(shí)戰(zhàn)示例》相關(guān)的同類信息!
  • 本頁收集關(guān)于html5錄音功能實(shí)戰(zhàn)示例的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃
    国产乱子轮精品视频| 国产精品欧美经典| 色婷婷av一区二区三区软件| 国产一区激情在线| 国产一区二区三区观看| 国产资源在线一区| 国产盗摄女厕一区二区三区| 国产在线精品一区二区| 国产黄人亚洲片| av色综合久久天堂av综合| 成人一级片在线观看| av午夜一区麻豆| 欧美三级中文字幕| 日韩美一区二区三区| 精品少妇一区二区三区| 欧美国产视频在线| 亚洲精品国产无天堂网2021| 亚洲图片激情小说| 视频一区国产视频| 国产精品中文字幕欧美| www.亚洲激情.com| 韩国精品久久久| 7777精品伊人久久久大香线蕉的| 亚洲乱码国产乱码精品精可以看| 成人免费高清视频在线观看| 国产欧美一区二区在线观看| 激情伊人五月天久久综合| 欧美va在线播放| 久久99蜜桃精品| 26uuu亚洲综合色| 国产精品白丝jk白祙喷水网站| 精品捆绑美女sm三区| 美国av一区二区| 精品av综合导航| 国产福利一区二区三区视频在线| 国产丝袜欧美中文另类| 国产999精品久久| 国产精品初高中害羞小美女文| 99九九99九九九视频精品| 亚洲色图清纯唯美| 欧美性色黄大片手机版| 精品午夜久久福利影院| 久久综合九色综合欧美就去吻| 麻豆精品一区二区av白丝在线| 精品国产露脸精彩对白| 福利91精品一区二区三区| 亚洲日本va午夜在线影院| 色婷婷久久久综合中文字幕 | 波多野结衣中文字幕一区二区三区| 久久网这里都是精品| 成人av综合一区| 亚洲国产精品一区二区久久恐怖片 | 欧美午夜精品久久久久久超碰 | 欧美理论电影在线| 美女尤物国产一区| 欧美极品另类videosde| 91在线免费视频观看| 丝袜脚交一区二区| 久久精品视频一区二区三区| 成人毛片老司机大片| 亚洲国产婷婷综合在线精品| 日韩视频一区二区三区在线播放| 国产成人亚洲综合a∨婷婷图片| 国产精品久久久久久久久快鸭| 欧美视频一区二| 国产黄人亚洲片| 亚洲国产成人高清精品| 久久久久国产精品人| 日本丰满少妇一区二区三区| 精品中文字幕一区二区小辣椒| 国产99久久久久久免费看农村| 久久久亚洲精品一区二区三区| 久久精品国产精品亚洲精品| 欧美成人精品福利| 国产主播一区二区| 精品国产精品网麻豆系列| 久久99国产精品成人| 欧美精品一区二区三区在线| 国产一区二区网址| 国产午夜精品一区二区| 成人午夜大片免费观看| 中文字幕一区不卡| 色又黄又爽网站www久久| 一区二区三区色| 欧美日韩午夜在线| 蜜桃视频免费观看一区| 日韩精品一区二区三区在线| 国产一区二区精品久久| 国产精品日韩成人| 91九色02白丝porn| 蜜桃视频一区二区| 欧美国产在线观看| 91精品福利视频| 免费欧美日韩国产三级电影| 亚洲精品一区在线观看| www.日本不卡| 午夜视黄欧洲亚洲| 久久久亚洲精品石原莉奈| 99久久精品国产一区| 午夜精品国产更新| 久久精品人人做人人综合| 91在线视频播放地址| 麻豆久久久久久| 亚洲欧美激情小说另类| 欧美一区二区三区不卡| 成人动漫在线一区| 偷偷要91色婷婷| 欧美高清在线视频| 在线观看91精品国产麻豆| 国产成人午夜高潮毛片| 亚洲超丰满肉感bbw| 国产性做久久久久久| 欧美色综合影院| 国产精品2024| 奇米影视7777精品一区二区| 国产精品理伦片| 欧美一区二区三区在| 色综合欧美在线视频区| 国产在线精品一区在线观看麻豆| 亚洲精品videosex极品| 国产丝袜在线精品| 日韩欧美黄色影院| 欧美日韩精品一区二区| 成人午夜大片免费观看| 激情综合色播五月| 偷拍与自拍一区| 亚洲三级在线播放| 欧美国产日韩在线观看| 精品裸体舞一区二区三区| 欧美老肥妇做.爰bbww视频| 91小宝寻花一区二区三区| 国产成人午夜精品影院观看视频| 喷水一区二区三区| 婷婷一区二区三区| 亚洲一区二区在线免费观看视频| 欧美国产一区在线| 欧美经典三级视频一区二区三区| 欧美成人一区二区三区在线观看 | 国产综合一区二区| 蜜臀av一区二区在线观看| 亚洲国产成人av| 亚洲综合久久久久| 亚洲女爱视频在线| 在线成人免费观看| 欧美日韩aaaaa| 欧美激情一区二区三区蜜桃视频| 亚洲四区在线观看| 美国欧美日韩国产在线播放| 欧美日韩日本视频| 精品1区2区3区| 91麻豆精品国产无毒不卡在线观看| 色噜噜夜夜夜综合网| 91成人国产精品| 欧美视频一区二区三区| 9191国产精品| 91精品福利在线一区二区三区| 欧美乱妇15p| 精品国产伦一区二区三区观看体验 | 亚洲成人自拍偷拍| 日韩精品一二三四| 免费成人av在线播放| 久久99精品一区二区三区| 九九视频精品免费| 成人丝袜18视频在线观看| 不卡影院免费观看| 91国产免费看| 777午夜精品免费视频| 精品国产乱码久久久久久牛牛| 久久综合99re88久久爱| 国产精品成人在线观看| 亚洲大型综合色站| 美女在线一区二区| 成人黄色av电影| 欧美亚洲一区二区三区四区| 欧美一卡2卡三卡4卡5免费| 日韩欧美三级在线| 国产精品第四页| 蜜臀久久99精品久久久久久9| 国产一区二区三区视频在线播放| 高清不卡一区二区| 欧美日韩一区不卡| 久久午夜国产精品| 亚洲国产日日夜夜| 国产一本一道久久香蕉| 91丨porny丨最新| 精品久久久久久综合日本欧美| 国产精品久久久久久久岛一牛影视| 一区二区三区视频在线看| 国内精品免费在线观看| 色噜噜久久综合| 久久久亚洲国产美女国产盗摄| 综合网在线视频| 精品中文av资源站在线观看| 在线一区二区三区| 国产视频一区二区三区在线观看| 亚洲一二三专区| 99久久免费精品高清特色大片| 日韩女优视频免费观看| 一区二区高清免费观看影视大全| 国产一区美女在线|