Mediasoup基本框架学习

Mediasoup基本框架学习

2020-04-09 21:46:39发布 浏览数:9482
概述:Mediasoup基本框架学习

Mediasoup 的基本结构

mediasoup design


mediasoup 模块

  1. javaScript层开发的对外接口层,Node.js服务。提供Signal服务(房间服务、SDP、等数据)。 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:
  4. ​ 一个Worker代表着一个运行在单核CPU上并处理Router实例的mediasoup C++子进程;
  5. Router:
  6. ​ Router用于注入、选择和转发通过Transport实例创建的媒体流;类似于房间功能,一个Router只能在一个worker进程中产生;
  7. Transport:
  8. ​ Transport将终端与MediaSoup Router连接起来,并通过在其上创建的Producer和Consumer实例实现双向媒体传输,实 现了下面3种Transport:WebRtcTransport,PlainRtpTransport,PipeTransport.
  9. 安装
  10. 安装mediasoup的Node.js模块通过NPM工具
$ npm install mediasoup@3 --save
1
  1. Mediasoup C/C++模块必须已经编译而且安装完毕,并且在目标服务器上已经可以使用。安装时会自动编译。注意编译报错,并及时解决。

  2. 安装完成后当前目录下会有 node_modules 文件夹 和package-lock.json文件
  3. mediasoup重要目录结构
  4. 进入node_moduels目录下查看mediasoup目录结构
  5. worker目录:mediasoup的c++模块,用于生产 mediasoup-work进程的二进制文件和源代码 lib目录:mediasoup的Node.js模块,用于对外提供接口,用于创建mediasoup-work进程,并且充当第三方程序和该进程通信的中间层。 test目录:具体的示例代码,可以看看如同启动mediasoup-work模块,如何创建router(room)等
  6. mediasoup Node.js 模块说明
  7. ​ Node.js模块主要提供API接口,用于创建mediasoup-work进程,并且提供控制该进程的接口,充当其它进程和mediasoup-work进程通信的桥梁。
  8. ​ 注:加粗部分是重点模块,需要详细看代码
  9. 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:对通讯能力的支持,实际上是媒体协商相关的东西,如你支持的帧率, 码率,编解码器是什么等
  10. 代码调用
  11. Node.js服务调用mediasoup接口
//your Node.js application:
const mediasoup = require("mediasoup");
12
  1. Node.js模块通过管道(Unix pipe)和mediasoup-worker进程间通信。所以无法实现管理进程和工作进程分离在不同主机上面。
  2. 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);
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. 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);
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. mediasoup 修改
  2. Node.js部分可以用go语言重新编写,pip通信也可修改为tcp网络通信,这样可以进行分布式部署,将管理服务和Worker进程分布式部署。
  3. 待续


请先
登录
后评论
0 条评论
暂时没有评论
最新文章
更多