Mediasoup基本框架學習

相關連接
  1. WebRtc 框架學習(一)
  2. Mediasoup基本框架學習
  3. 使用docker 搭建MediaSoup服務
  4. MediaSoup-demo模塊增加nginx

Mediasoup 的基本結構

mediasoup design
  • mediasoup 模塊
  1. javaScript層開發的對外接口層,Node.js服務。提供Signal服務(房間服務、SDP、等數據)。
  2. C/C++ 模塊,用於媒體數據交換層(ICE, DTLS, RTP and so on),UDP類型數據交換。

兩個模塊相互通信,但是開發者無需關心C/C++模塊,只要關心JavaScript Api使用即可。

  • mediasoup Work模塊架構
  1. C/C++ Worker模塊包括Router和WebRtcTransport。

  2. Router用於流媒體複製轉發,WebRtcTransport維護與客戶端的通信。
    截圖

  3. Worker:

    ​ 一個Worker代表着一個運行在單核CPU上並處理Router實例的mediasoup C++子進程;

  4. Router:

    ​ Router用於注入、選擇和轉發通過Transport實例創建的媒體流;類似於房間功能,一個Router只能在一個worker進程中產生;

  5. Transport:

    ​ Transport將終端與MediaSoup Router連接起來,並通過在其上創建的Producer和Consumer實例實現雙向媒體傳輸,實 現了下面3種Transport:WebRtcTransport,PlainRtpTransport,PipeTransport.

  6. 安裝

    安裝mediasoup的Node.js模塊通過NPM工具

    $ npm install mediasoup@3 --save
    

    Mediasoup C/C++模塊必須已經編譯而且安裝完畢,並且在目標服務器上已經可以使用。安裝時會自動編譯。注意編譯報錯,並及時解決。
    在這裏插入圖片描述
    安裝完成後當前目錄下會有 node_modules 文件夾 和package-lock.json文件

  7. mediasoup重要目錄結構

    進入node_moduels目錄下查看mediasoup目錄結構
    在這裏插入圖片描述

    1. worker目錄:mediasoup的c++模塊,用於生產 mediasoup-work進程的二進制文件和源代碼
    2. lib目錄:mediasoup的Node.js模塊,用於對外提供接口,用於創建mediasoup-work進程,並且充當第三方程序和該進程通信的中間層。
    3. test目錄:具體的示例代碼,可以看看如同啓動mediasoup-work模塊,如何創建router(room)等
  8. mediasoup Node.js 模塊說明
    ​ Node.js模塊主要提供API接口,用於創建mediasoup-work進程,並且提供控制該進程的接口,充當其它進程和mediasoup-work進程通信的橋樑。

    ​ 注:加粗部分是重點模塊,需要詳細看代碼

    • AudioLevelObserver.js: 用於檢測聲音的大小, 通過C++檢測音頻聲音返回應用層,通過Observer接收並展示音頻大小
    • Channel.js:實現於C++模塊的通信部分
    • Consume.js: 消費媒體數據(video audio)
    • EnhancedEventEmitter.js:EventEmitter的封裝,C++底層向上層發送事件
    • Logger.js:日誌模塊
    • PipeTransport.js:控制Router之間的轉發
    • PlainRtpTransport.js:控制普通的rtp傳輸通道,如FFmpeg等不經過瀏覽器rtp協議的數據傳輸
    • Producer.js:生產媒體數據,音頻或視頻
    • Router.js:代表一個房間或者一個路由器
    • Transport.js:所有傳輸的的基類(父類)
    • WebRtcTransport.js:瀏覽器使用的傳輸接口。
    • Worker.js:用於創建mediasoup-work進程的類,一個房間只能在一個Worker裏。
    • Error.js:錯誤信息的定義
    • Index.js:Mediasoup的庫,上層引入Mediasoup最先導入的庫,也爲庫的索引
    • Ortc.js: 其與SDP相對應,以對象的形式標識SDP,如編解碼參數,編解碼器,幀 率等,以對象方式去存儲。
    • ScalabilityModes.js:擴容模塊,廣播等功能可以用到。
    • SupportedRtpCapabilities.js:對通訊能力的支持,實際上是媒體協商相關的東西,如你支持的幀率, 碼率,編解碼器是什麼等
  9. 代碼調用

    Node.js服務調用mediasoup接口

    //your Node.js application:
    const mediasoup = require("mediasoup");
    

    Node.js模塊通過管道(Unix pipe)和mediasoup-worker進程間通信。所以無法實現管理進程和工作進程分離在不同主機上面。

    worker.js模塊調用說明:

    //簡單的創建一個Worker進程,具體參考test目錄裏面的示例代碼
    const os = require('os');
    const process = require('process');
    const { toBeType } = require('jest-tobetype');
    const mediasoup = require('../');
    const { createWorker, observer } = mediasoup;
    const { InvalidStateError } = require('../lib/errors');
    
    expect.extend({ toBeType });
    
    let worker;
    
    beforeEach(() => worker && !worker.closed && worker.close());
    afterEach(() => worker && !worker.closed && worker.close());
    
    test('createWorker() succeeds', async () =>
    {
    	const onObserverNewWorker = jest.fn();
    
    	observer.once('newworker', onObserverNewWorker);
    
    	worker = await createWorker();
    
    	expect(onObserverNewWorker).toHaveBeenCalledTimes(1);
    	expect(onObserverNewWorker).toHaveBeenCalledWith(worker);
    	expect(worker).toBeType('object');
    	expect(worker.pid).toBeType('number');
    	expect(worker.closed).toBe(false);
    
    	worker.close();
    	expect(worker.closed).toBe(true);
    
    	// eslint-disable-next-line require-atomic-updates
    	worker = await createWorker(
    		{
    			logLevel            : 'debug',
    			logTags             : [ 'info' ],
    			rtcMinPort          : 0,
    			rtcMaxPort          : 9999,
    			dtlsCertificateFile : 'test/data/dtls-cert.pem',
    			dtlsPrivateKeyFile  : 'test/data/dtls-key.pem',
    			appData             : { bar: 456 }
    		});
    	expect(worker).toBeType('object');
    	expect(worker.pid).toBeType('number');
    	expect(worker.closed).toBe(false);
    	expect(worker.appData).toEqual({ bar: 456 });
    
    	worker.close();
    	expect(worker.closed).toBe(true);
    }, 2000);
    

    Router 接口調用:

    //簡單的創建一個Worker進程後下發創建router命令,具體參考test目錄裏面的示例代碼
    const { toBeType } = require('jest-tobetype');
    const mediasoup = require('../');
    const { createWorker } = mediasoup;
    const { InvalidStateError } = require('../lib/errors');
    
    expect.extend({ toBeType });
    
    let worker;
    
    beforeEach(() => worker && !worker.closed && worker.close());
    afterEach(() => worker && !worker.closed && worker.close());
    
    const mediaCodecs =
    [
    	{
    		kind       : 'audio',
    		mimeType   : 'audio/opus',
    		clockRate  : 48000,
    		channels   : 2,
    		parameters :
    		{
    			useinbandfec : 1,
    			foo          : 'bar'
    		}
    	},
    	{
    		kind      : 'video',
    		mimeType  : 'video/VP8',
    		clockRate : 90000
    	},
    	{
    		kind       : 'video',
    		mimeType   : 'video/H264',
    		clockRate  : 90000,
    		parameters :
    		{
    			'level-asymmetry-allowed' : 1,
    			'packetization-mode'      : 1,
    			'profile-level-id'        : '4d0032'
    		},
    		rtcpFeedback : [] // Will be ignored.
    	}
    ];
    
    test('worker.createRouter() succeeds', async () =>
    {
    	worker = await createWorker();
    
    	const onObserverNewRouter = jest.fn();
    
    	worker.observer.once('newrouter', onObserverNewRouter);
    
    	const router = await worker.createRouter({ mediaCodecs, appData: { foo: 123 } });
    
    	expect(onObserverNewRouter).toHaveBeenCalledTimes(1);
    	expect(onObserverNewRouter).toHaveBeenCalledWith(router);
    	expect(router.id).toBeType('string');
    	expect(router.closed).toBe(false);
    	expect(router.rtpCapabilities).toBeType('object');
    	expect(router.rtpCapabilities.codecs).toBeType('array');
    	expect(router.rtpCapabilities.headerExtensions).toBeType('array');
    	expect(router.appData).toEqual({ foo: 123 });
    
    	await expect(worker.dump())
    		.resolves
    		.toEqual({ pid: worker.pid, routerIds: [ router.id ] });
    
    	await expect(router.dump())
    		.resolves
    		.toMatchObject(
    			{
    				id                               : router.id,
    				transportIds                     : [],
    				rtpObserverIds                   : [],
    				mapProducerIdConsumerIds         : {},
    				mapConsumerIdProducerId          : {},
    				mapProducerIdObserverIds         : {},
    				mapDataProducerIdDataConsumerIds : {},
    				mapDataConsumerIdDataProducerId  : {}
    			});
    
    	// Private API.
    	expect(worker._routers.size).toBe(1);
    
    	worker.close();
    
    	expect(router.closed).toBe(true);
    
    	// Private API.
    	expect(worker._routers.size).toBe(0);
    }, 2000);
    
  10. mediasoup 修改

    Node.js部分可以用go語言重新編寫,pip通信也可修改爲tcp網絡通信,這樣可以進行分佈式部署,將管理服務和Worker進程分佈式部署。

  11. 搭建 mediasoup-demo docker測試環境

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章