日本免费精品_最新日韩一区_亚洲视频一区在线_a在线视频观看_天天射夜夜骑_粉嫩av一区二区三区_欧美中日韩免费视频_综合图区欧美_国内精品美女在线观看_午夜精品久久久久久久男人的天堂

首頁 > 編程 > HTML > 正文

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

2024-08-26 00:21:23
字體:
供稿:網(wǎng)友

緣起

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

步驟1

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

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

// 老的瀏覽器可能根本沒有實(shí)現(xiàn) mediaDevices,所以我們可以先設(shè)置一個空的對象if (navigator.mediaDevices === undefined) {    navigator.mediaDevices = {};}// 一些瀏覽器部分支持 mediaDevices。我們不能直接給對象設(shè)置 getUserMedia// 因?yàn)檫@樣可能會覆蓋已有的屬性。這里我們只會在沒有g(shù)etUserMedia屬性的時候添加它。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)它 - 那么就返回一個error到promise的reject來保持一個統(tǒng)一的接口        if (!getUserMedia) {            return Promise.reject(new Error('getUserMedia is not implemented in this browser'));        }        // 否則,為老的navigator.getUserMedia方法包裹一個Promise        return new Promise(function(resolve, reject) {            getUserMedia.call(navigator, constraints, resolve, reject);        });    };

步驟2

這是網(wǎng)上存在的一個方法,封裝了一個HZRecorder。基本上引用了這個方法。調(diào)用HZRecorder.get就可以調(diào)起錄音接口,這個方法傳入一個callback函數(shù),new HZRecorder后執(zhí)行callback函數(shù)且傳入一個實(shí)體化后的HZRecorder對象。可以通過該對象的方法實(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)建一個音頻環(huán)境對象      audioContext = window.AudioContext || window.webkitAudioContext;      var context = new audioContext();      //將聲音輸入這個對像      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方法的      // 第二個和第三個參數(shù)指的是輸入和輸出都是雙聲道。      var recorder = context.createScriptProcessor(bufferSize, 2, 2);      var audioData = {          size: 0          //錄音文件長度          , 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)識符               writeString('RIFF'); offset += 4;              // 下個地址開始到文件尾總字節(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ù),表示每個通道的播放速度               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)識符               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('無法錄音,請檢查設(shè)備狀態(tài)');            });    }};  window.HZRecorder = HZRecorder;

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

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

以下為解決這兩個問題的方案。

步驟3

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

1、修改HZRecorder里的audioData對象代碼。并引入網(wǎng)上一位大神的一個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, // 錄音文件長度    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)識符        writeString('RIFF');        offset += 4;        // 下個地址開始到文件尾總字節(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ù),表示每個通道的播放速度        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)識符        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)解決以上兩個問題。

步驟4

這里主要介紹怎么做錄音時的動效。我們的一個動效需求為:

html5,錄音

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

// 創(chuàng)建analyser節(jié)點(diǎn),獲取音頻時間和頻率數(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);    }    // 請求下一幀    requestAnimationFrame(drawSpectrum);})();

緣盡

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

ps:lamejs可參考這個github

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識閱讀請移步到HTML教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
久久久综合av| 国产网站av| 日韩欧美高清在线| 日韩午夜黄色| 91久久中文| 一区二区视频在线观看免费的| 亚洲男女av一区二区| 美国av一区二区| 欧美一级手机免费观看片| 欧美亚洲专区| 日韩精品视频中文字幕| 亚洲一区在线观看视频| 欧美日韩性视频| 国产日韩av一区二区| 亚洲开心激情| aaa欧美日韩| 日韩国产在线不卡视频| 亚洲中午字幕| 在线视频国内一区二区| 欧美日韩国产亚洲一区| 欧美日韩国产一区| 色国产在线视频| 国产色在线 com| 日韩欧美在线视频| 日韩精品视频在线观看网址| 日韩精品在线视频| 中文在线一区| 日韩欧美在线网址| 亚洲一区在线观看视频| 亚洲综合在线不卡| 在线观看免费国产成人软件| 亚洲永久字幕| 精品对白一区国产伦| 国产欧美日韩在线| 一本久久a久久精品亚洲| 精品色蜜蜜精品视频在线观看| 樱花草www在线| 一区二区在线视频播放| 日韩国产一区久久| www.狠狠| 中文字幕在线观看视频www| 欧美三级三级三级爽爽爽| 欧美国产一区视频在线观看| 国产嫩草影院久久久久| 日韩一级在线视频| 内射国产内射夫妻免费频道| 日韩免费精品视频| 97最新国自产拍视频在线完整在线看 | 区日韩二区欧美三区| 欧美三级在线视频| 91精品国产欧美日韩| 久久精品在线免费观看| 欧美日韩激情一区二区三区| 中文天堂资源在线| 91精品久久久久| 精品国产999| 精品一区二区在线观看视频| 一区二区高清视频| 日韩高清不卡一区二区| 91精品网站| av免费在线网站| 91精品国产自产在线| 国产69精品久久久久孕妇国产69久久| 日韩欧美中文视频| 一区二区日韩免费看| 深夜福利一区二区| 亚洲一区日韩在线| 色综合久久六月婷婷中文字幕| 午夜高潮免费视频| 国产裸体歌舞团一区二区| 国产综合成人久久大片91| 欧美不卡123| 精品一区二区在线观看视频| 亚洲福利精品在线| 亚洲福利视频专区| 国产91欧美| 国产一区在线不卡| 一区二区三区在线不卡| 免费视频一区三区| 欧美日韩精品免费在线观看视频 | 精品精品久久| 欧美日韩国产不卡| 日韩精品丝袜在线| 不卡av免费在线| 欧美专区中文字幕| 影音先锋中文字幕在线观看| 精品三区视频| 亚洲福利在线观看| 一本大道一区二区三区| 国产欧美久久久精品影院| 99精品在线播放| 日韩精品一二三四区| 精品日韩99亚洲| 亚洲视频中文字幕| 国产一区激情在线| 久久精品在线免费观看| 亚洲美女视频一区| 久久久久久自在自线| 久久精品日韩无码| 精品一区二区三区中文字幕| 中文字幕精品一区二区三区在线| 国产成人一二三区| www.eeuss影院| 精品不卡一区二区| 国产在线高清精品| 欧美日韩在线精品成人综合网| 91精品国产综合久久蜜臀| 免费国产h视频在线观看86| 91 中文字幕| 国产一区不卡在线| 亚洲福利在线看| 欧美不卡123| 91精品免费在线观看| 日韩亚洲欧美中文三级| 91精品国产91久久久久久三级| 欧美亚洲专区| 午夜av一区| 二区视频在线观看| 亚洲素人一区二区| 亚洲第一中文字幕| 中文字幕综合在线| 精品人妻一区二区三区视频| 黄色在线资源| 亚洲一二三不卡| 精品在线网站观看| 97视频在线| 婷婷久久综合网| 亚洲成年人影院在线| 日韩欧美亚洲视频| 日韩欧美国产网站| 国产一二三四| 亚洲精华国产欧美| 日韩三级视频在线播放| 精品中文在线| 久久婷婷国产| 又黄又www的网站| 国产精久久久久| 久久久另类综合| 国产视频一区三区| 欧美日韩国产免费观看| 91久久精品在线| 日韩国产亚洲欧美| 91精品国产欧美日韩| 91 中文字幕| 在线一区av| 91久久精品国产| 蜜桃视频中文字幕| 亚洲一级影院| 91精品国产综合久久香蕉最新版| 精品国产91乱高清在线观看| 精品视频三区| www.日韩免费| 日韩欧美国产亚洲| 国产欧美综合视频| 精品人妻一区二区免费视频| 日韩欧美国产午夜精品| 日韩欧美一二三| 亚洲va韩国va欧美va精品| 一区二区三区在线播放视频| 中文字幕在线观看播放| 精品中文在线| 日韩在线视频精品| 久久精品日韩无码| 日韩精品一区二区三区视频播放| 黄色片免费看| 最近免费中文字幕在线第一页| 国产男女av| 欧美日韩国产第一页| 亚洲一区资源| 91久久精品在线| 欧美一级免费观看| 日韩精品免费观看视频| 日韩欧美一级精品久久| 91精品国产91综合久久蜜臀| 国产自产视频| 久久精品一二三| 中文字幕久久av| 欧美色视频一区二区三区在线观看| 日韩精品乱码av一区二区| 亚洲国产福利视频| 久久久久黄色| 国产日韩中文字幕在线| 欧美国产一区视频在线观看| 欧美在线中文字幕| 国内精品不卡| 91久久视频| √新版天堂资源在线资源| 欧美日韩中文另类| 中文字幕精品一区二| 欧美日韩第一| 久久久综合av| 国产乱码午夜在线视频| wwwav91com| 日韩在线视频网| 日韩视频在线观看视频| 在线视频1区2区| 国产日产一区二区| 国产黄色在线免费观看| 不卡在线一区二区| 日韩精品丝袜在线| 日韩一级在线视频| 天堂精品高清1区2区3区| 蜜桃久久av一区| 日韩欧美久久| 亚洲一区中文字幕在线观看| 国产95在线|亚洲| 成人欧美亚洲| 亚洲一区在线观看视频| 99综合视频| 欧美激情视频一区二区三区在线播放| 欧美 日韩 综合| 欧美精品一区二区三区在线播放| 国产激情在线播放| 欧洲精品在线一区| 国产一级免费| 日韩在线不卡一区| 国产三级视频在线| 欧美 中文字幕| 91久久在线| 国产裸体歌舞团一区二区| www.精品视频| 国产一区激情在线| 国产在成人精品线拍偷自揄拍| 欧美成人vr18sexvr| 在线黄色国产电影| 欧洲一级精品| 欧美一卡2卡三卡4卡5免费| 一级片免费在线播放| 国产一级一片免费播放| 日韩欧美中文字幕公布| 99久久婷婷| 91精品视频播放| 国产成人精品免费在线| 国产福利精品导航| 日韩精品在线观| 日韩在线二区| 日韩免费高清一区二区| 国产视频一二区| 91精品国产91久久久| 久久久精品免费免费| 亚洲综合在线小说| 国产黄色精品| 欧美日韩激情在线一区二区三区 | 国产一区高清视频| 亚洲第一香蕉网| 欧美日韩国产综合网| 亚洲女人天堂色在线7777| 久久久水蜜桃| 欧美日韩一二| 国产三级在线| 久久精品免费在线观看| 婷婷久久综合九色国产成人| 91精品国产乱码久久蜜臀| 久久精品电影| 精品日韩欧美在线| 午夜国产视频 | 国产婷婷精品| 色欧美日韩亚洲| 精品视频1区2区3区| 欧美日韩精品综合| 亚洲 欧美 日韩系列| 欧美日韩国产一中文字不卡| 欧美,日韩,国产在线| 日韩免费电影网站| 中文字幕在线观看播放| 国产激情99| 国产成人中文字幕| 国产小视频在线观看| 欧美 日韩 综合| 91精品日本| 亚洲va韩国va欧美va精品| 在线三级av| 在线观看区一区二| 日韩欧美久久久| 亚洲成人7777| 日韩在线观看a| 欧美国产中文| av免费不卡国产观看| 欧美在线视频第一页| 日本中文字幕在线观看| 日韩在线观看精品| 日韩一级网站| 日韩激情一区| 日韩久久99| 免费视频中文字幕| 极品久久久久久| 日韩精品在线视频| 欧美日韩国产高清| 深夜福利一区| 福利在线国产| 日韩亚洲一区在线| 欧美 日韩 国产在线观看| 91精品在线观看入口| 亚洲乱码在线观看| 国产欧美第一页| 在线国产99| 91精品免费在线观看| 99久久久精品免费观看国产| 91久久久精品| 日韩中文字幕观看| 亚洲一区视频在线观看视频| 欧美日韩在线不卡视频| 日韩 欧美 中文| 亚洲高清在线观看一区| 一区二区日韩av| 国产欧美日韩在线视频| 国产天堂素人系列在线视频| 在线一区免费| 精品欧美日韩| 一级片免费网站| 欧美亚洲专区| 日韩三级免费观看| 国产日韩精品在线| 国产最新在线| 欧美日韩视频免费看| 欧美乱大交xxxxx免费| 欧美日韩国产中字| 精品国产乱码久久久久久牛牛| 不卡在线视频| 中文字幕精品视频在线| 色99中文字幕| 精品网站999| 天天综合天天做| 精品国产欧美日韩不卡在线观看| 日韩精品视频在线看| 国产偷国产偷亚洲清高网站 | 亚洲福利视频专区| 中文字幕在线观看播放| 深夜福利亚洲| 日韩精品国产欧美| 日韩精品在线观| 精品一区二区三区中文字幕| 欧美三级一区二区三区| 国产午夜精品久久| 精品视频1区2区3区| 91麻豆精品在线| av免费在线不卡| 亚洲电影中文字幕在线观看| 欧美日韩视频免费看| 日韩在线小视频| 日韩视频一区| 国产嫩草影院久久久久| av丝袜在线| 中文字幕第一页在线| 伊人亚洲视频| 一级片在线播放| 中文字幕精品一区二区三区在线| 欧美日韩亚洲91| 久草亚洲一区| 国产95在线|亚洲| 视频一区二区中文字幕| 日韩欧美在线影院| 国产日韩在线亚洲字幕中文| 国产三级在线观看视频| 精品成人久久久| 日韩在线不卡| 国产日韩电影| 99精品视频99| 最新中文字幕在线播放| 在线观看免费国产成人软件| 国产黄在线播放| 国产一二三四| 亚洲三级欧美| 精品999在线| 日韩国产91| 精品福利二区三区| 黄色一区二区在线| 在线国产99| 日韩精品视频在线免费观看| www.老鸭窝.com| 精品无人国产偷自产在线| 精品久久九九| 中文在线日韩| 在线欧美日韩国产| 在线观看区一区二| 国产视频不卡| 精品视频国产| 久久99久久久久久久噜噜| 91精品国产欧美日韩| 日韩中文在线中文网三级| 国产亚洲污的网站| 中文字幕在线欧美| 日本免费在线视频不卡一不卡二| 欧美日韩国产专区| 91欧美日韩| 日韩欧美在线第一页| 91精品国产高清91久久久久久| 亚洲综合在线视频| 日韩 欧美 亚洲| 亚洲v中文字幕| 欧美变态tickling挠脚心| 国产日韩在线视频| 国产高清一级片| 国产日韩中文字幕在线| 欧美三级网址| 国产福利久久| 不卡av免费在线| 日韩欧美高清在线视频| 中文字幕 乱码 中文乱码91| 精品在线一区二区三区|