Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 久久久影院亚洲精品,亚洲影视先锋,久久久久国产一级毛片高清版

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          h5混合開(kāi)發(fā)app,錄音

          TML5+API Reference


          <template>
          	<view>
          				<view @click="go();">開(kāi)始</view>
          				<view @click="so();">結(jié)束</view>
          	</view>
          </template>
          
          <script>
          	var r = null; 
          	export default {
          		data() {
          			return {
          			}
          		},
          		onLoad() {
          				// #ifdef APP-PLUS
          				// 監(jiān)聽(tīng)設(shè)備網(wǎng)絡(luò)狀態(tài)變化事件
          				plus.globalEvent.addEventListener('netchange', function(){});
          				r = plus.audio.getRecorder(); 
          				// #endif
          		},
          		methods: {
          			go(){ 
          				if ( r == null ) {
          						console.log("Device not ready!")
          						return; 
          					} 
          					r.record( {filename:"_doc/audio/"}, function () {
          						console.log("Audio record success!")
          					}, function ( e ) {
          						console.log("Audio record failed: " + e.message )
          					} );
          				    
          			},
          			so(){
          				r.stop(); 
          			}
          		}
          	}
          </script>


          這是用uniapp寫(xiě)的例子


          uni-app App 端內(nèi)置 HTML5+ 引擎,讓 js 可以直接調(diào)用豐富的原生能力。

          條件編譯調(diào)用 HTML5+

          小程序及 H5 等平臺(tái)是沒(méi)有 HTML5+ 擴(kuò)展規(guī)范的,因此在 uni-app 調(diào)用 HTML5+ 的擴(kuò)展規(guī)范時(shí),需要注意使用條件編譯。否則運(yùn)行到h5、小程序等平臺(tái)會(huì)出現(xiàn) plus is not defined錯(cuò)誤。

          // #ifdef APP-PLUS
          var appid = plus.runtime.appid;
          console.log('應(yīng)用的 appid 為:' + appid);
          // #endif

          uni-app不需要plus ready

          在html中使用plus的api,需要等待plus ready。 而uni-app不需要等,可以直接使用。而且如果你調(diào)用plus ready,反而不會(huì)觸發(fā)。

          uni-app中的事件監(jiān)聽(tīng)

          在普通的 H5+ 項(xiàng)目中,需要使用 document.addEventListener 監(jiān)聽(tīng)原生擴(kuò)展的事件。

          uni-app 中,沒(méi)有 document。可以使用
          plus.globalEvent.addEventListener
          來(lái)實(shí)現(xiàn)。

          // #ifdef APP-PLUS
          // 監(jiān)聽(tīng)設(shè)備網(wǎng)絡(luò)狀態(tài)變化事件
          plus.globalEvent.addEventListener('netchange', function(){});
          // #endif

          同理,在 uni-app 中使用 Native.js 時(shí),一些 Native.js 中對(duì)于原生事件的監(jiān)聽(tīng)同樣需要按照上面的方法去實(shí)現(xiàn)。

          從Recorder H5 GitHub開(kāi)源庫(kù)優(yōu)化后,對(duì)邊錄邊轉(zhuǎn)碼成小語(yǔ)音片段文件實(shí)時(shí)上傳服務(wù)器這種操作支持非常良好,因此以前不太好支持的H5語(yǔ)音通話已經(jīng)有了更好的突破空間。因此花了兩晚時(shí)間打造了一個(gè)H5語(yǔ)音通話聊天的demo。

          歡迎在線把玩:https://xiangyuecn.github.io/Recorder/

          一、把玩方法

          1. 準(zhǔn)備局域網(wǎng)內(nèi)兩臺(tái)設(shè)備(Peer A、Peer B)用最新版本瀏覽器(demo未適配低版本)分別打開(kāi)demo頁(yè)面(也可以是同一瀏覽器打開(kāi)兩個(gè)標(biāo)簽)
          2. 勾選頁(yè)面中的H5版語(yǔ)音通話聊天,在Peer A中點(diǎn)擊新建連接
          3. 把Peer A的本機(jī)信手動(dòng)復(fù)制傳輸給Peer B,粘貼到遠(yuǎn)程信息中,并點(diǎn)擊確定連接
          4. 把Peer B自動(dòng)生成的本機(jī)信息手動(dòng)復(fù)制傳輸給Peer A,粘貼到遠(yuǎn)程信息中,并點(diǎn)擊確定連接
          5. 雙方P2P連接已建立,使用頁(yè)面上方的錄音功能,隨時(shí)開(kāi)啟錄音,音頻數(shù)據(jù)會(huì)實(shí)時(shí)發(fā)送給對(duì)方

          局域網(wǎng)H5版對(duì)講機(jī)

          二、技術(shù)特性

          (1)數(shù)據(jù)傳輸

          github demo中考慮到減少對(duì)服務(wù)器的依賴,因此采用了WebRTC P2P傳輸功能,無(wú)需任何服務(wù)器支持即可實(shí)現(xiàn)局域網(wǎng)內(nèi)的兩個(gè)設(shè)備之間互相連接,連接代碼也算簡(jiǎn)單。有服務(wù)器支持可能就要逆天了,不過(guò)代碼也會(huì)更復(fù)雜。

          如果正式使用,可能不太會(huì)考慮使用WebRTC,用WebSocket通過(guò)服務(wù)器進(jìn)行轉(zhuǎn)發(fā)可能是最佳的選擇。

          WebRTC局域網(wǎng)P2P連接要點(diǎn)(實(shí)際代碼其實(shí)差不多,只不過(guò)多做了點(diǎn)兼容):

          /******Peer A(本機(jī))******/
          var peerA=new RTCPeerConnection(null,null)
          //開(kāi)啟會(huì)話,等待遠(yuǎn)程連接
          peerA.createOffer().then(function(offer){
           peerA.setLocalDescription(offer);
           peerAOffer=offer;
          });
          var peerAICEList=[......] //通過(guò)peerA.onicecandidate監(jiān)聽(tīng)獲得所有的ICE連接信息候選項(xiàng),如果有多個(gè)網(wǎng)絡(luò)適配器,就會(huì)有多個(gè)候選
          //創(chuàng)建連接通道對(duì)象,A端通過(guò)這個(gè)來(lái)進(jìn)行數(shù)據(jù)發(fā)送
          var peerAChannel=peerA.createDataChannel("RTC Test");
          /******Peer B(遠(yuǎn)程)******/
          var peerB=new RTCPeerConnection(null,null)
          //連接到Peer A
          peerB.setRemoteDescription(peerAOffer);
          //開(kāi)啟應(yīng)答會(huì)話,等待Peer A確認(rèn)連接
          peerB.createAnswer().then(function(answer){
           peerB.setLocalDescription(answer);
           peerBAnswer=answer;
          });
          //把Peer A的連接點(diǎn)都添加進(jìn)去
          peerB.addIceCandidate(......peerAICEList)
          var peerBICEList=[......] //通過(guò)peerB.onicecandidate監(jiān)聽(tīng)獲得所有的ICE連接信息候選項(xiàng),如果有多個(gè)網(wǎng)絡(luò)適配器,就會(huì)有多個(gè)候選
          var peerBChannel=... //通過(guò)peerB.ondatachannel得到連接通道對(duì)象,B端通過(guò)這個(gè)來(lái)進(jìn)行數(shù)據(jù)發(fā)送
          /*******最終完成連接********/
          //連接到Peer B
          peerA.setRemoteDescription(peerBAnswer);
          //把Peer B的連接點(diǎn)都添加進(jìn)去
          peerA.addIceCandidate(......peerBICEList)
          /*
          peerA peerB分別等待peerA/BChannel.onopen回調(diào)即完成P2P連接
          ,然后通過(guò)監(jiān)聽(tīng)peerA/BChannel.onmessage獲得對(duì)方發(fā)送的信息
          ,通過(guò)peerA/BChannel.send(data) 發(fā)送數(shù)據(jù)。
          */
          

          (2)音頻采集和編碼

          由于是在我的Recorder庫(kù)中新加的demo,因此音頻采集和編碼都是現(xiàn)成的,Recorder庫(kù)有好的兼容性和穩(wěn)定性,因此節(jié)省了最大頭的工作量。

          編碼最佳使用MP3格式,因?yàn)榇烁袷揭褍?yōu)化了實(shí)時(shí)編碼性能,可做到邊錄邊轉(zhuǎn)碼,16kbps 16khz的情況下可做到2kb每秒的文件大小,音質(zhì)還可以,實(shí)時(shí)傳輸時(shí)為3kb每秒,15分鐘大概3M的流量。

          用wav格式也可以,不過(guò)此格式編碼出來(lái)的數(shù)據(jù)量太大,16位 16khz接近50kb每秒的實(shí)時(shí)傳輸數(shù)據(jù),15分鐘要37M多流量。其他格式由于暫未對(duì)實(shí)時(shí)編碼進(jìn)行優(yōu)化,使用中會(huì)導(dǎo)致明顯卡頓。

          降噪、靜音檢測(cè)等高級(jí)功能是沒(méi)有的,畢竟是非專業(yè)人員 要求高點(diǎn)可以,但不要超出范圍太多啦。

          (3)音頻實(shí)時(shí)接收和播放

          接收到一個(gè)音頻片段后,本應(yīng)該是立即播放的,但由于編碼、網(wǎng)絡(luò)傳輸導(dǎo)致的延遲,可能上個(gè)片段還未播放完(甚至未開(kāi)始播放),因此需要緩沖處理。

          因?yàn)榇嬖诰彌_,就需要進(jìn)行實(shí)時(shí)同步處理,如果緩沖內(nèi)積壓了過(guò)多的音頻片段,會(huì)導(dǎo)致語(yǔ)音播放滯后太多,因此需要適當(dāng)進(jìn)行對(duì)數(shù)據(jù)進(jìn)行丟棄,實(shí)測(cè)發(fā)現(xiàn)網(wǎng)絡(luò)正常、設(shè)備性能靠譜的情況下基本沒(méi)有丟棄的數(shù)據(jù)。

          然后就是播放了,本應(yīng)是播完一個(gè)就播下一個(gè),測(cè)試發(fā)現(xiàn)這是不靠譜的。因?yàn)榻Y(jié)束一個(gè)片段后再開(kāi)始播放下一個(gè)發(fā)出聲音,這個(gè)過(guò)程會(huì)中斷比較長(zhǎng)時(shí)間,明顯感覺(jué)得出來(lái)中間存在短暫停頓。因此必須在片段未播完時(shí)準(zhǔn)備好下一個(gè)片段的播放,并且提前開(kāi)始播放,達(dá)到抹掉中間的停頓。

          我寫(xiě)了兩個(gè)播放方式:

          1. 實(shí)時(shí)解碼播放
          2. 雙Audio輪換播放

          最開(kāi)始用一個(gè)Audio停頓感太明顯,因此用兩個(gè)Audio輪換抹掉中間的停頓,但發(fā)現(xiàn)不同格式Auido播放差異巨大,播放wav非常流暢,但播放mp3還是存在停頓(后面用解碼的發(fā)現(xiàn)是得到的PCM時(shí)長(zhǎng)變長(zhǎng)了,導(dǎo)致事件觸發(fā)會(huì)出現(xiàn)誤差,為什么會(huì)變長(zhǎng)?怪異)。

          因此后面寫(xiě)了一個(gè)解碼然后再播放,mp3這次終于能正常連續(xù)播放了,wav格式和雙Audio的播放差異不大。實(shí)時(shí)解碼里面也用到了雙Audio中的技巧,其實(shí)也是用到了兩個(gè)BufferSource進(jìn)行類似的輪換操作,以抹掉兩個(gè)片段間的停頓。

          不過(guò)最終播放效果還是不夠好,音質(zhì)變差了點(diǎn),并且多了點(diǎn)噪音。如果有現(xiàn)成的播放代碼拿過(guò)來(lái)用就就好了。

          三、應(yīng)用場(chǎng)景

          1. 數(shù)據(jù)傳輸改成WebSocket,做個(gè)仿微信語(yǔ)音通話H5版還是可以的(受限于Recorder瀏覽器支持)
          2. 局域網(wǎng)H5版對(duì)講機(jī)(前端玩具)
          3. ......沒(méi)有想到

          完。

          TML5的權(quán)限越來(lái)越大了,瀏覽器可以直接調(diào)用攝像頭、麥克風(fēng)了,好激動(dòng)啊。我們要用純潔的HTML代碼造出自己的天地。

          視頻采集

          本篇介紹的栗子 都是在chrome 47 版本以上的,低版本的可能會(huì)出現(xiàn)白屏和錯(cuò)誤。

          1.安全環(huán)境

          隨著Chrome版本的升高,安全性問(wèn)題也越來(lái)越被重視,較新版本的Chrome瀏覽器在調(diào)用一些API時(shí)需要頁(yè)面處在安全環(huán)境中。本篇文章所介紹的API函數(shù),都需要在安全環(huán)境中執(zhí)行。如果處在非安全環(huán)境下 ( http頁(yè)面 ) 這些API就會(huì)有意想不到的問(wèn)題。

          比如 getUserMedia()就會(huì)報(bào)出警告,并執(zhí)行出錯(cuò)。

          而在設(shè)備枚舉enumerateDevices()時(shí),雖然不會(huì)報(bào)錯(cuò),但是他隱藏了設(shè)備label。

          注意:第一次在一個(gè)安全頁(yè)面下執(zhí)行enumerateDevices()時(shí)也會(huì)隱藏label,在允許使用攝像頭等設(shè)備后,第二次執(zhí)行才會(huì)顯示label。

          getUserMedia() no longer works on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. Seehttps://goo.gl/rStTGz for more details.

          根據(jù)谷歌的意思,常用的安全環(huán)境有如下

          • http://localhost

          • http://127.0.0.1

          • https 開(kāi)頭的地址頁(yè)面

          如果你做了一個(gè)視頻測(cè)試的頁(yè)面,想嘚瑟給局域網(wǎng)的其他人,但是又沒(méi)有域名證書(shū)怎么辦?

          這時(shí)候只能通過(guò)修改其他人的hosts文件了

          比如你的測(cè)試服務(wù)器IP地址是192.168.2.18,那么其他人的hosts文件修改如下:

          #localhost 127.0.0.1

          localhost 192.168.2.18

          當(dāng)使用別人的Chrome瀏覽器訪問(wèn) http://localhost/[getUserMediaTestPage]時(shí),就會(huì)順利的執(zhí)行這些API了。

          但是移動(dòng)端的瀏覽器并不認(rèn)localhost,就算你修改了hosts ,移動(dòng)端的瀏覽器根本不理你,解析都不解析。

          所以想在手機(jī)上測(cè)試,只能老老實(shí)實(shí)申請(qǐng)個(gè)證書(shū)了。

          2.設(shè)備枚舉

          在開(kāi)啟攝像頭之前,先要把可以使用的麥克風(fēng)和攝像頭 ( 輸入設(shè)備 ) 列出來(lái),如果沒(méi)有這兩樣設(shè)備也就無(wú)法繼續(xù)。

          代碼如下:

          <label for="audioDevice"> 錄音設(shè)備: </label>

          <select id="audioDevice">

          </select>

          <br>

          <label for="videoDevice"> 錄影設(shè)備: </label>

          <select id="videoDevice">

          </select>

          <script>

          navigator.mediaDevices.enumerateDevices().then(function (data) {

          data.forEach(function (item) {

          if(item.kind=="audioinput"){ //麥克風(fēng)

          document.getElementById("audioDevice").innerHTML += "<option value='"+ item.deviceId +"'>" + item.label + " </option> "

          }else if(item.kind=="videoinput"){ //攝像頭

          document.getElementById("videoDevice").innerHTML += "<option value='"+ item.deviceId +"'>" + item.label + " </option> "

          }

          })

          },function (error) {

          console.log(error);

          })

          </script>

          效果如下圖,和瀏覽器自己獲取的一模一樣。

          注意:上圖的實(shí)例中,瀏覽器地址欄最右邊的攝像頭標(biāo)識(shí)是需要使用 getUserMedia()函數(shù)時(shí)才會(huì)出現(xiàn)。

          <script>

          var getUserMedia = navigator.webkitGetUserMedia; //Chrome瀏覽器的方法

          getUserMedia.call(navigator, {

          video:true, // 開(kāi)啟音頻

          audio:true // 開(kāi)啟視頻

          }, function(stream){

          console.log(stream); // 成功獲取媒體流

          }, function(error){

          //處理媒體流創(chuàng)建失敗錯(cuò)誤

          });

          </script>

          這時(shí)候可以通過(guò)瀏覽器給出的菜單下拉選擇設(shè)備。

          3.設(shè)置參數(shù),預(yù)覽

          我們可以通過(guò)代碼來(lái)指定使用哪個(gè)攝像頭和麥克風(fēng)設(shè)備。

          也可以通過(guò)代碼設(shè)置視頻的寬、高和幀率。

          代碼如下:

          <video id="video" autoplay></video> <!-- 一定要有 autoplay -->

          <script>

          var getUserMedia = navigator.webkitGetUserMedia ;

          getUserMedia.call(navigator, {

          "audio":{

          "mandatory":{

          "sourceId":"" // 指定設(shè)備的 deviceId

          }

          },

          "video":{

          "optional":[

          {"minWidth":400},

          {"maxWidth":400}, // 數(shù)字類型,固定寬度

          {"minHeight":220},

          {"maxHeight":220}, // 數(shù)字類型,固定高度

          {"frameRate":"12"} // 幀率

          ],"mandatory":{

          "sourceId":"" // 指定設(shè)備的 deviceId

          }

          }

          }, function(stream){

          //綁定本地媒體流到video標(biāo)簽用于輸出

          document.getElementById("video").src = URL.createObjectURL(stream);

          }, function(error){

          //處理媒體流創(chuàng)建失敗錯(cuò)誤

          });

          </script>

          輸出的視頻流通過(guò)blob對(duì)象鏈接綁定到video標(biāo)簽輸出。

          這個(gè)deviceId就是從上文設(shè)備枚舉 enumerateDevices() 獲取到的。

          兩種設(shè)備,如果有一個(gè)deviceId填寫(xiě)不正確,就會(huì)報(bào)出一個(gè)DevicesNotFoundError的錯(cuò)誤。

          而且一旦指定了設(shè)備后,瀏覽器自己的設(shè)備選擇就會(huì)變成灰色不可選。

          視頻的寬高,并不會(huì)因?yàn)樘顚?xiě)的數(shù)值比例不合法而失真。

          比如你設(shè)定了寬度30,高度100,那么他會(huì)從視頻中心截取 30x100 的畫(huà)面,而不是把原畫(huà)面擠壓到這個(gè)30x100的尺寸。

          效果如下:

          如果您的預(yù)覽一片漆黑,或者只有一個(gè)小黑點(diǎn),那么說(shuō)明您的攝像頭正在被占用...

          吐槽:這個(gè)getUserMedia()函數(shù)的參數(shù),w3的官方文檔鏈接如下:

          https://www.w3.org/TR/mediacapture-streams/

          可是Chrome并沒(méi)有遵循它,而且差距還挺大...

          視頻保存

          1. 格式支持

          Chrome瀏覽器是大力推廣webm的視頻格式的。可以用MediaRecorder.isTypeSupported("video/webm")來(lái)測(cè)試是否支持這種類型的編碼。

          如果返回true,那么我們錄制的視頻就可以被保存為這種指定的格式。

          如果不指定,那么將會(huì)使用瀏覽器自動(dòng)指定的文件格式。文檔原話如下

          If this paramater is not specified, the UA will use a platform-specific default format.

          但是這個(gè)默認(rèn)值卻無(wú)法直接獲取,全靠猜...

          2. 視頻錄制 MediaRecorder

          我們使用 MediaRecorder來(lái)錄制視頻,參數(shù)是通過(guò)getUserMedia()獲取的媒體流。

          • 通過(guò)綁定ondataavailable事件,來(lái)獲取視頻片段數(shù)據(jù),并在內(nèi)存中累積。

          • 錄制的開(kāi)始和結(jié)束分別使用 start和stop 函數(shù)。

          • 執(zhí)行start之后會(huì)周期性觸發(fā)ondataavailable事件。

          • 執(zhí)行stop之后會(huì)停止觸發(fā)ondataavailable事件。

          • 錄制結(jié)束后,把累計(jì)的片段數(shù)據(jù)保存為blob對(duì)象,并從瀏覽器下載存為視頻文件。

          代碼如下:

          <script>

          var getUserMedia = navigator.webkitGetUserMedia ;

          var g_stream = null, g_recorder = null;

          function startPreview(){

          getUserMedia.call(navigator, {

          video:true,

          audio:true

          }, function(stream){

          g_stream = stream;

          }, function(error){

          });

          }

          function stopRecording(){

          g_recorder.stop();

          }

          function startRecording(){

          var chunks = [];

          g_recorder = new MediaRecorder(g_stream,{mimeType:"video/webm"});

          g_recorder.ondataavailable = function(e) {

          chunks.push(e.data);

          }

          g_recorder.onstop = function(e) {

          var blob = new Blob(chunks, { 'type' : 'video/webm' });

          var audioURL = URL.createObjectURL(blob);

          window.open(audioURL);

          }

          g_recorder.start();

          }

          </script>

          注意:本例并沒(méi)有填寫(xiě)視頻文件頭,所以保存出來(lái)的視頻文件沒(méi)有時(shí)間軌,無(wú)法快進(jìn)和跳躍。可以用格式工廠轉(zhuǎn)

          “莫基了”上面有一個(gè)錄制音頻的例子 傳送門(mén):http://t.cn/RvxZAeo

          這篇文章的DEMO請(qǐng)戳 這里:http://t.cn/RVt9Q6I

          ?―――――――――↓―――――――――?

          相關(guān)閱讀

          多屏互動(dòng)——H5中級(jí)進(jìn)階
          前端,想說(shuō)愛(ài)你不容易!
          無(wú)需Flash實(shí)現(xiàn)圖片裁剪——HTML5中級(jí)進(jìn)階

          作者信息

          作者來(lái)自力譜宿云 LeapCloud 團(tuán)隊(duì)_UX成員:王詩(shī)詩(shī) 【原創(chuàng)】
          力譜宿云 LeapCloud 團(tuán)隊(duì)首發(fā):https://blog.maxleap.cn/archives/1197
          歡迎關(guān)注微信訂閱號(hào):MaxLeap_yidongyanfa


          主站蜘蛛池模板: 日韩人妻精品一区二区三区视频| 午夜DV内射一区区| 国产亚洲一区二区精品| 一区二区视频在线| 无码人妻精品一区二区蜜桃AV| 88国产精品视频一区二区三区| 黑巨人与欧美精品一区| 国产伦精品一区二区免费| 精品动漫一区二区无遮挡| 一区二区三区视频在线观看| 一区二区三区在线视频播放| 国产精品高清一区二区三区不卡 | 国产成人欧美一区二区三区 | 中文字幕一区在线| 国产一区韩国女主播| 亚洲天堂一区二区三区四区| 亚洲无线码在线一区观看| 爆乳熟妇一区二区三区霸乳| 丰满岳乱妇一区二区三区| 一区二区三区福利| jizz免费一区二区三区| 精品乱子伦一区二区三区高清免费播放| 无码人妻精品一区二区三区9厂| 91精品国产一区二区三区左线| 国产a∨精品一区二区三区不卡| 国产一区二区三区在线2021| 精品少妇一区二区三区视频| 久久亚洲AV午夜福利精品一区| 国产成人无码一区二区三区| 久久久99精品一区二区| 国产美女视频一区| 国产精品熟女一区二区| 91久久精品国产免费一区| 韩国福利一区二区美女视频| 欧美av色香蕉一区二区蜜桃小说| 亚洲av日韩综合一区二区三区| 日韩精品久久一区二区三区| 午夜福利国产一区二区| 久久99精品国产一区二区三区| 一区二区三区福利视频免费观看| 人妖在线精品一区二区三区|