MediaRecorder簡單實現(xiàn)屏幕共享

功能特色:

使用前端技術(shù)實現(xiàn)局域網(wǎng)可用的屏幕共享程序龄砰,僅用于學(xué)習(xí)研究,商業(yè)化還需要解決很多問題暮芭。

技術(shù)要點

  • MediaRecorder(chrome)
  • nodejs
  • scoket.io

架構(gòu)

傳播者(transmitter)> 中轉(zhuǎn)器(nodejs)> 接收者(receiver)

項目目錄結(jié)構(gòu)

  • client
    • transmitter.html
    • receiver.html

index.js(中轉(zhuǎn)器)

實現(xiàn)細(xì)節(jié)

傳播者【transmitter.html】

// 總體思路:獲取到錄屏數(shù)據(jù)arrayBuffer之后發(fā)送給中轉(zhuǎn)器

// 1烈疚、socket連接
var socket = io('ws://' + location.hostname + ':9009', {
  query: {
    role: 'servant', // 身份標(biāo)記
  }
})
var connected = false;

socket.on('connect', function () {
  connected = true;
});

socket.on('disconnect', function () {
  connected = false;
})

// 錄屏授權(quán)【chrome】
navigator.mediaDevices.getDisplayMedia({
  video: true, // 這里我只需要視頻,不要音頻
}).then(function (stream) {
  var mediaRecorder = new MediaRecorder(stream, {
    mimeType: 'video/webm\;codecs=vp8', //  這個很關(guān)鍵 #01
  });

  // 媒體片段
  mediaRecorder.ondataavailable = function (e) {
    if (e.data.size && connected) {
      e.data.arrayBuffer().then(function (arrayBuffer) {
        socket.emit('media', arrayBuffer); // 發(fā)送給中轉(zhuǎn)器
      })
    }
  }

  mediaRecorder.start(200); // 每200毫秒觸發(fā)一次片段
})

中轉(zhuǎn)器【index.js】(nodejs執(zhí)行這個腳本)

const Koa = require('koa');
const { createServer } = require('http')
const { Server } = require('socket.io');
const koaStatic = require('koa-static')
const koaBody = require('koa-body');
const path = require('path');

const app = new Koa();
const httpServer = createServer(app.callback());

// 啟動一個靜態(tài)服務(wù)器
app.use(koaBody({ multipart: true }))
app.use(koaStatic(path.resolve(__dirname, './client')));
app.listen(9000, () => console.log('服務(wù)已啟動: 9000'));

// 啟動一個socket服務(wù)器
const io = new Server(httpServer, { cors: true, maxhttpbuffersize: 1e6 * 50 });
let firstData; // 記錄開播的第一個數(shù)據(jù)塊

io.on('connection', (socket) => {
  const { role } = socket.handshake.query;

  if (role === 'transmitter') firstData = null; // 【傳播者】如果重新開播斗蒋,要刷新一下

  socket.join("default-room"); // 全部加入同一個房間

   // 開播過程中【 接收者】加入捌斧,是需要優(yōu)先接收第一個數(shù)據(jù)塊不然會異常
  if (firstData) io.sockets.to("default-room").emit('mediaData', firstData);

  // 收到【傳播者】發(fā)送的媒體數(shù)據(jù)arrayBuffer
  socket.on('media', function (data) {
    if (!firstData) firstData = data;

    socket.to("default-room").emit('mediaData', data) // 發(fā)送媒體數(shù)據(jù)給【接收者】
  })
})

httpServer.listen(9009);

接收者【receiver.html】

<body>
  <video id="video" style="width: 1280px; height: 720px;"></video>
  <button id="look">觀看</button>
</body>
window.onload = function () {
  var video = document.querySelector('#video');
  var btnLook = document.querySelector('#look');
  var mediaSource = new MediaSource();
  var chunks = [];
  var sourceBuffer;

  video.src = URL.createObjectURL(mediaSource);
  mediaSource.addEventListener('sourceopen', sourceOpen)

  function sourceOpen() {
    console.log('sourceOpen');
    URL.revokeObjectURL(video.src);
    var mime = 'video/webm; codecs=vp8'; // 這個很關(guān)鍵 #01
    sourceBuffer = mediaSource.addSourceBuffer(mime);
    sourceBuffer.mode = 'sequence';
  }

  // 把媒體片段加入視頻播放緩存區(qū)
  function appendSourceBuffer() {
    if (chunks.length && sourceBuffer && !sourceBuffer.updating && mediaSource.readyState === 'open') {
      sourceBuffer.appendBuffer(chunks.shift());
      if (video.paused) video.play();
    }

    window.requestAnimationFrame(appendSourceBuffer);
  }

  // 開始播放
  btnLook.addEventListener('click', function () {
    appendSourceBuffer();
    btnLook.remove();
  });

  // 連接socket
  var socket = io('ws://' + location.hostname + ':9009', {
    query: {
      role: 'receiver'
    }
  })

  var connected = false;

  socket.on('connect', function () {
    connected = true;
  });

  socket.on('disconnect', function () {
    connected = false;
  })

  // 收到【傳播者】發(fā)來的數(shù)據(jù)arrayBuffer
  socket.on('mediaData', function (data) {
    chunks.push(data);
  });
}

期間遇到的難點

  • MediaRecorder與mediaSource.addSourceBuffer的miniType有匹配關(guān)系,與編碼格式有關(guān)泉沾。
  • 使用socket發(fā)送數(shù)據(jù)不易過大捞蚂,不然容易斷開
  • 【接收者】需要優(yōu)先接收【傳播者】開播的第一個媒體片段,不然addSourceBuffer之后會觸發(fā)mediaSource關(guān)閉

結(jié)語

查了很多國內(nèi)外的論壇資料才把這個場景下的功能需求完成跷究,還有很多關(guān)于媒體方面的知識欠缺姓迅,還望路過的友人不吝賜教。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俊马,一起剝皮案震驚了整個濱河市丁存,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌柴我,老刑警劉巖解寝,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異屯换,居然都是意外死亡编丘,警方通過查閱死者的電腦和手機与学,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門彤悔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人索守,你說我怎么就攤上這事晕窑。” “怎么了卵佛?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵杨赤,是天一觀的道長。 經(jīng)常有香客問我截汪,道長疾牲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任衙解,我火速辦了婚禮阳柔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蚓峦。我一直安慰自己,他們只是感情好暑椰,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布一汽。 她就那樣靜靜地躺著,像睡著了一般岩喷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上均驶,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天妇穴,我揣著相機與錄音,去河邊找鬼腾它。 笑死,一個胖子當(dāng)著我的面吹牛曲梗,可吹牛的內(nèi)容都是我干的妓忍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼定罢,長吁一口氣:“原來是場噩夢啊……” “哼旁瘫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惠况,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤稠屠,失蹤者是張志新(化名)和其女友劉穎台诗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拉队,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡粱快,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了漫雷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡与柑,死狀恐怖蓄坏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情结蟋,我是刑警寧澤渔彰,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站宝惰,受9級特大地震影響乳丰,放射性物質(zhì)發(fā)生泄漏内贮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一什燕、第九天 我趴在偏房一處隱蔽的房頂上張望竞端。 院中可真熱鬧,春花似錦技俐、人聲如沸统台。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仇穗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枝冀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工宾茂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拴还,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓端盆,卻偏偏與公主長得像费封,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子焚鹊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容