@webex/plugin-meetings 3.0.0-beta.6 → 3.0.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,7 +38,8 @@ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflec
38
38
  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
39
39
 
40
40
  var RemoteMediaEvents = {
41
- SourceUpdate: _receiveSlot.ReceiveSlotEvents.SourceUpdate
41
+ SourceUpdate: _receiveSlot.ReceiveSlotEvents.SourceUpdate,
42
+ Stopped: 'stopped'
42
43
  };
43
44
  exports.RemoteMediaEvents = RemoteMediaEvents;
44
45
 
@@ -146,6 +147,10 @@ var RemoteMedia = /*#__PURE__*/function (_EventsScope) {
146
147
  this.cancelMediaRequest(commit);
147
148
  (_this$receiveSlot = this.receiveSlot) === null || _this$receiveSlot === void 0 ? void 0 : _this$receiveSlot.removeAllListeners();
148
149
  this.receiveSlot = undefined;
150
+ this.emit({
151
+ file: 'multistream/remoteMedia',
152
+ function: 'stop'
153
+ }, RemoteMediaEvents.Stopped, {});
149
154
  }
150
155
  /**
151
156
  * Sends a new media request. This method can only be used for receiver-selected policy,
@@ -203,7 +208,7 @@ var RemoteMedia = /*#__PURE__*/function (_EventsScope) {
203
208
 
204
209
  if (this.receiveSlot) {
205
210
  var scope = {
206
- file: 'meeting/remoteMedia',
211
+ file: 'multistream/remoteMedia',
207
212
  function: 'setupEventListeners'
208
213
  };
209
214
  this.receiveSlot.on(_receiveSlot.ReceiveSlotEvents.SourceUpdate, function (data) {
@@ -1 +1 @@
1
- {"version":3,"names":["RemoteMediaEvents","SourceUpdate","ReceiveSlotEvents","getMaxFs","paneSize","maxFs","LoggerProxy","logger","warn","remoteMediaCounter","RemoteMedia","receiveSlot","mediaRequestManager","options","setupEventListeners","id","commit","cancelMediaRequest","removeAllListeners","undefined","csi","mediaRequestId","Error","addRequest","policyInfo","policy","receiveSlots","codecInfo","resolution","codec","cancelRequest","scope","file","function","on","data","emit","mediaType","memberId","sourceState","stream","EventsScope"],"sources":["remoteMedia.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport EventsScope from '../common/events/events-scope';\n\nimport {MediaRequestId, MediaRequestManager} from './mediaRequestManager';\nimport {CSI, ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';\n\nexport const RemoteMediaEvents = {\n SourceUpdate: ReceiveSlotEvents.SourceUpdate,\n};\n\nexport type RemoteVideoResolution =\n | 'thumbnail' // the smallest possible resolution, 90p or less\n | 'very small' // 180p or less\n | 'small' // 360p or less\n | 'medium' // 720p or less\n | 'large' // 1080p or less\n | 'best'; // highest possible resolution\n\n/**\n * Converts pane size into h264 maxFs\n * @param {PaneSize} paneSize\n * @returns {number}\n */\nexport function getMaxFs(paneSize: RemoteVideoResolution): number {\n let maxFs;\n\n switch (paneSize) {\n case 'thumbnail':\n maxFs = 60;\n break;\n case 'very small':\n maxFs = 240;\n break;\n case 'small':\n maxFs = 920;\n break;\n case 'medium':\n maxFs = 3600;\n break;\n case 'large':\n maxFs = 8192;\n break;\n case 'best':\n maxFs = 8192; // for now 'best' is 1080p, so same as 'large'\n break;\n default:\n LoggerProxy.logger.warn(\n `RemoteMedia#getMaxFs --> unsupported paneSize: ${paneSize}, using \"medium\" instead`\n );\n maxFs = 3600;\n }\n\n return maxFs;\n}\n\ntype Options = {\n resolution?: RemoteVideoResolution; // applies only to groups of type MC.MediaType.VideoMain and MC.MediaType.VideoSlides\n};\n\nexport type RemoteMediaId = string;\n\nlet remoteMediaCounter = 0;\n\n/**\n * Class representing a remote audio/video stream.\n *\n * Internally it is associated with a specific receive slot\n * and a media request for it.\n */\nexport class RemoteMedia extends EventsScope {\n private receiveSlot?: ReceiveSlot;\n\n private readonly mediaRequestManager: MediaRequestManager;\n\n private readonly options: Options;\n\n private mediaRequestId?: MediaRequestId;\n\n public readonly id: RemoteMediaId;\n\n /**\n * Constructs RemoteMedia instance\n *\n * @param receiveSlot\n * @param mediaRequestManager\n * @param options\n */\n constructor(\n receiveSlot: ReceiveSlot,\n mediaRequestManager: MediaRequestManager,\n options?: Options\n ) {\n super();\n remoteMediaCounter += 1;\n this.receiveSlot = receiveSlot;\n this.mediaRequestManager = mediaRequestManager;\n this.options = options || {};\n this.setupEventListeners();\n this.id = `RM${remoteMediaCounter}-${this.receiveSlot.id}`;\n }\n\n /**\n * Invalidates the remote media by clearing the reference to a receive slot and\n * cancelling the media request.\n * After this call the remote media is unusable.\n *\n * @param {boolean} commit - whether to commit the cancellation of the media request\n * @internal\n */\n public stop(commit: boolean = true) {\n this.cancelMediaRequest(commit);\n this.receiveSlot?.removeAllListeners();\n this.receiveSlot = undefined;\n }\n\n /**\n * Sends a new media request. This method can only be used for receiver-selected policy,\n * because only in that policy we have a 1-1 relationship between RemoteMedia and MediaRequest\n * and the request id is then stored in this RemoteMedia instance.\n * For active-speaker policy, the same request is shared among many RemoteMedia instances,\n * so it's managed through RemoteMediaGroup\n *\n * @internal\n */\n public sendMediaRequest(csi: CSI, commit: boolean) {\n if (this.mediaRequestId) {\n this.cancelMediaRequest(false);\n }\n\n if (!this.receiveSlot) {\n throw new Error('sendMediaRequest() called on an invalidated RemoteMedia instance');\n }\n\n this.mediaRequestId = this.mediaRequestManager.addRequest(\n {\n policyInfo: {\n policy: 'receiver-selected',\n csi,\n },\n receiveSlots: [this.receiveSlot],\n codecInfo: this.options.resolution && {\n codec: 'h264',\n maxFs: getMaxFs(this.options.resolution),\n },\n },\n commit\n );\n }\n\n /**\n * @internal\n */\n public cancelMediaRequest(commit: boolean) {\n if (this.mediaRequestId) {\n this.mediaRequestManager.cancelRequest(this.mediaRequestId, commit);\n this.mediaRequestId = undefined;\n }\n }\n\n /**\n * registers event listeners on the receive slot and forwards all the events\n */\n private setupEventListeners() {\n if (this.receiveSlot) {\n const scope = {\n file: 'meeting/remoteMedia',\n function: 'setupEventListeners',\n };\n\n this.receiveSlot.on(ReceiveSlotEvents.SourceUpdate, (data) => {\n this.emit(scope, RemoteMediaEvents.SourceUpdate, data);\n });\n }\n }\n\n /**\n * Getter for mediaType\n */\n public get mediaType() {\n return this.receiveSlot?.mediaType;\n }\n\n /**\n * Getter for memberId\n */\n public get memberId() {\n return this.receiveSlot?.memberId;\n }\n\n /**\n * Getter for csi\n */\n public get csi() {\n return this.receiveSlot?.csi;\n }\n\n /**\n * Getter for source state\n */\n public get sourceState() {\n return this.receiveSlot?.sourceState;\n }\n\n /**\n * Getter for remote media stream\n */\n public get stream() {\n return this.receiveSlot?.stream;\n }\n\n /**\n * @internal\n * @returns {ReceiveSlot}\n */\n public getUnderlyingReceiveSlot() {\n return this.receiveSlot;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"names":["RemoteMediaEvents","SourceUpdate","ReceiveSlotEvents","Stopped","getMaxFs","paneSize","maxFs","LoggerProxy","logger","warn","remoteMediaCounter","RemoteMedia","receiveSlot","mediaRequestManager","options","setupEventListeners","id","commit","cancelMediaRequest","removeAllListeners","undefined","emit","file","function","csi","mediaRequestId","Error","addRequest","policyInfo","policy","receiveSlots","codecInfo","resolution","codec","cancelRequest","scope","on","data","mediaType","memberId","sourceState","stream","EventsScope"],"sources":["remoteMedia.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport EventsScope from '../common/events/events-scope';\n\nimport {MediaRequestId, MediaRequestManager} from './mediaRequestManager';\nimport {CSI, ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';\n\nexport const RemoteMediaEvents = {\n SourceUpdate: ReceiveSlotEvents.SourceUpdate,\n Stopped: 'stopped',\n};\n\nexport type RemoteVideoResolution =\n | 'thumbnail' // the smallest possible resolution, 90p or less\n | 'very small' // 180p or less\n | 'small' // 360p or less\n | 'medium' // 720p or less\n | 'large' // 1080p or less\n | 'best'; // highest possible resolution\n\n/**\n * Converts pane size into h264 maxFs\n * @param {PaneSize} paneSize\n * @returns {number}\n */\nexport function getMaxFs(paneSize: RemoteVideoResolution): number {\n let maxFs;\n\n switch (paneSize) {\n case 'thumbnail':\n maxFs = 60;\n break;\n case 'very small':\n maxFs = 240;\n break;\n case 'small':\n maxFs = 920;\n break;\n case 'medium':\n maxFs = 3600;\n break;\n case 'large':\n maxFs = 8192;\n break;\n case 'best':\n maxFs = 8192; // for now 'best' is 1080p, so same as 'large'\n break;\n default:\n LoggerProxy.logger.warn(\n `RemoteMedia#getMaxFs --> unsupported paneSize: ${paneSize}, using \"medium\" instead`\n );\n maxFs = 3600;\n }\n\n return maxFs;\n}\n\ntype Options = {\n resolution?: RemoteVideoResolution; // applies only to groups of type MC.MediaType.VideoMain and MC.MediaType.VideoSlides\n};\n\nexport type RemoteMediaId = string;\n\nlet remoteMediaCounter = 0;\n\n/**\n * Class representing a remote audio/video stream.\n *\n * Internally it is associated with a specific receive slot\n * and a media request for it.\n */\nexport class RemoteMedia extends EventsScope {\n private receiveSlot?: ReceiveSlot;\n\n private readonly mediaRequestManager: MediaRequestManager;\n\n private readonly options: Options;\n\n private mediaRequestId?: MediaRequestId;\n\n public readonly id: RemoteMediaId;\n\n /**\n * Constructs RemoteMedia instance\n *\n * @param receiveSlot\n * @param mediaRequestManager\n * @param options\n */\n constructor(\n receiveSlot: ReceiveSlot,\n mediaRequestManager: MediaRequestManager,\n options?: Options\n ) {\n super();\n remoteMediaCounter += 1;\n this.receiveSlot = receiveSlot;\n this.mediaRequestManager = mediaRequestManager;\n this.options = options || {};\n this.setupEventListeners();\n this.id = `RM${remoteMediaCounter}-${this.receiveSlot.id}`;\n }\n\n /**\n * Invalidates the remote media by clearing the reference to a receive slot and\n * cancelling the media request.\n * After this call the remote media is unusable.\n *\n * @param {boolean} commit - whether to commit the cancellation of the media request\n * @internal\n */\n public stop(commit: boolean = true) {\n this.cancelMediaRequest(commit);\n this.receiveSlot?.removeAllListeners();\n this.receiveSlot = undefined;\n this.emit(\n {\n file: 'multistream/remoteMedia',\n function: 'stop',\n },\n RemoteMediaEvents.Stopped,\n {}\n );\n }\n\n /**\n * Sends a new media request. This method can only be used for receiver-selected policy,\n * because only in that policy we have a 1-1 relationship between RemoteMedia and MediaRequest\n * and the request id is then stored in this RemoteMedia instance.\n * For active-speaker policy, the same request is shared among many RemoteMedia instances,\n * so it's managed through RemoteMediaGroup\n *\n * @internal\n */\n public sendMediaRequest(csi: CSI, commit: boolean) {\n if (this.mediaRequestId) {\n this.cancelMediaRequest(false);\n }\n\n if (!this.receiveSlot) {\n throw new Error('sendMediaRequest() called on an invalidated RemoteMedia instance');\n }\n\n this.mediaRequestId = this.mediaRequestManager.addRequest(\n {\n policyInfo: {\n policy: 'receiver-selected',\n csi,\n },\n receiveSlots: [this.receiveSlot],\n codecInfo: this.options.resolution && {\n codec: 'h264',\n maxFs: getMaxFs(this.options.resolution),\n },\n },\n commit\n );\n }\n\n /**\n * @internal\n */\n public cancelMediaRequest(commit: boolean) {\n if (this.mediaRequestId) {\n this.mediaRequestManager.cancelRequest(this.mediaRequestId, commit);\n this.mediaRequestId = undefined;\n }\n }\n\n /**\n * registers event listeners on the receive slot and forwards all the events\n */\n private setupEventListeners() {\n if (this.receiveSlot) {\n const scope = {\n file: 'multistream/remoteMedia',\n function: 'setupEventListeners',\n };\n\n this.receiveSlot.on(ReceiveSlotEvents.SourceUpdate, (data) => {\n this.emit(scope, RemoteMediaEvents.SourceUpdate, data);\n });\n }\n }\n\n /**\n * Getter for mediaType\n */\n public get mediaType() {\n return this.receiveSlot?.mediaType;\n }\n\n /**\n * Getter for memberId\n */\n public get memberId() {\n return this.receiveSlot?.memberId;\n }\n\n /**\n * Getter for csi\n */\n public get csi() {\n return this.receiveSlot?.csi;\n }\n\n /**\n * Getter for source state\n */\n public get sourceState() {\n return this.receiveSlot?.sourceState;\n }\n\n /**\n * Getter for remote media stream\n */\n public get stream() {\n return this.receiveSlot?.stream;\n }\n\n /**\n * @internal\n * @returns {ReceiveSlot}\n */\n public getUnderlyingReceiveSlot() {\n return this.receiveSlot;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/plugin-meetings",
3
- "version": "3.0.0-beta.6",
3
+ "version": "3.0.0-beta.8",
4
4
  "description": "",
5
5
  "license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
6
6
  "contributors": [
@@ -28,12 +28,12 @@
28
28
  ]
29
29
  },
30
30
  "devDependencies": {
31
- "@webex/plugin-meetings": "3.0.0-beta.6",
32
- "@webex/test-helper-chai": "3.0.0-beta.6",
33
- "@webex/test-helper-mocha": "3.0.0-beta.6",
34
- "@webex/test-helper-mock-webex": "3.0.0-beta.6",
35
- "@webex/test-helper-retry": "3.0.0-beta.6",
36
- "@webex/test-helper-test-users": "3.0.0-beta.6",
31
+ "@webex/plugin-meetings": "3.0.0-beta.8",
32
+ "@webex/test-helper-chai": "3.0.0-beta.8",
33
+ "@webex/test-helper-mocha": "3.0.0-beta.8",
34
+ "@webex/test-helper-mock-webex": "3.0.0-beta.8",
35
+ "@webex/test-helper-retry": "3.0.0-beta.8",
36
+ "@webex/test-helper-test-users": "3.0.0-beta.8",
37
37
  "chai": "^4.3.4",
38
38
  "chai-as-promised": "^7.1.1",
39
39
  "jsdom-global": "3.0.2",
@@ -41,18 +41,17 @@
41
41
  "typed-emitter": "^2.1.0"
42
42
  },
43
43
  "dependencies": {
44
- "@webex/common": "3.0.0-beta.6",
44
+ "@webex/common": "3.0.0-beta.8",
45
45
  "@webex/internal-media-core": "^0.0.17-beta",
46
- "@webex/internal-plugin-conversation": "3.0.0-beta.6",
47
- "@webex/internal-plugin-device": "3.0.0-beta.6",
48
- "@webex/internal-plugin-mercury": "3.0.0-beta.6",
49
- "@webex/internal-plugin-metrics": "3.0.0-beta.6",
50
- "@webex/internal-plugin-support": "3.0.0-beta.6",
51
- "@webex/internal-plugin-user": "3.0.0-beta.6",
52
- "@webex/plugin-people": "3.0.0-beta.6",
53
- "@webex/plugin-rooms": "3.0.0-beta.6",
54
- "@webex/ts-sdp": "^1.0.1",
55
- "@webex/webex-core": "3.0.0-beta.6",
46
+ "@webex/internal-plugin-conversation": "3.0.0-beta.8",
47
+ "@webex/internal-plugin-device": "3.0.0-beta.8",
48
+ "@webex/internal-plugin-mercury": "3.0.0-beta.8",
49
+ "@webex/internal-plugin-metrics": "3.0.0-beta.8",
50
+ "@webex/internal-plugin-support": "3.0.0-beta.8",
51
+ "@webex/internal-plugin-user": "3.0.0-beta.8",
52
+ "@webex/plugin-people": "3.0.0-beta.8",
53
+ "@webex/plugin-rooms": "3.0.0-beta.8",
54
+ "@webex/webex-core": "3.0.0-beta.8",
56
55
  "bowser": "^2.11.0",
57
56
  "btoa": "^1.2.1",
58
57
  "dotenv": "^4.0.0",
@@ -7,6 +7,7 @@ import {CSI, ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';
7
7
 
8
8
  export const RemoteMediaEvents = {
9
9
  SourceUpdate: ReceiveSlotEvents.SourceUpdate,
10
+ Stopped: 'stopped',
10
11
  };
11
12
 
12
13
  export type RemoteVideoResolution =
@@ -112,6 +113,14 @@ export class RemoteMedia extends EventsScope {
112
113
  this.cancelMediaRequest(commit);
113
114
  this.receiveSlot?.removeAllListeners();
114
115
  this.receiveSlot = undefined;
116
+ this.emit(
117
+ {
118
+ file: 'multistream/remoteMedia',
119
+ function: 'stop',
120
+ },
121
+ RemoteMediaEvents.Stopped,
122
+ {}
123
+ );
115
124
  }
116
125
 
117
126
  /**
@@ -164,7 +173,7 @@ export class RemoteMedia extends EventsScope {
164
173
  private setupEventListeners() {
165
174
  if (this.receiveSlot) {
166
175
  const scope = {
167
- file: 'meeting/remoteMedia',
176
+ file: 'multistream/remoteMedia',
168
177
  function: 'setupEventListeners',
169
178
  };
170
179
 
@@ -184,8 +184,16 @@ describe('RemoteMedia', () => {
184
184
  it('cancels media request, unsets the receive slot and removes all the listeners from it', () => {
185
185
  const cancelMediaRequestSpy = sinon.spy(remoteMedia, 'cancelMediaRequest');
186
186
 
187
+ let stoppedListenerCalled = false;
188
+
189
+ remoteMedia.on(RemoteMediaEvents.Stopped, () => {
190
+ stoppedListenerCalled = true;
191
+ });
192
+
187
193
  remoteMedia.stop(true);
188
194
 
195
+ assert.isTrue(stoppedListenerCalled);
196
+
189
197
  assert.calledOnce(cancelMediaRequestSpy);
190
198
  assert.calledWith(cancelMediaRequestSpy, true);
191
199
 
@@ -542,6 +542,64 @@ describe('RemoteMediaManager', () => {
542
542
  });
543
543
  });
544
544
 
545
+ it('stops all current video remoteMedia instances when switching to new layout', async () => {
546
+ const audioStopStubs = [];
547
+ const videoStopStubs = [];
548
+
549
+ const config = cloneDeep(DefaultTestConfiguration);
550
+
551
+ // start with the stage layout because it has both active speaker and receiver selected panes
552
+ config.video.initialLayoutId = 'Stage';
553
+
554
+ remoteMediaManager = new RemoteMediaManager(
555
+ fakeReceiveSlotManager,
556
+ fakeMediaRequestManagers,
557
+ config
558
+ );
559
+
560
+ // mock all stop() methods for all remote audio objects we get with AudioCreated event
561
+ remoteMediaManager.on(Event.AudioCreated, (audio: RemoteMediaGroup) => {
562
+ audio
563
+ .getRemoteMedia()
564
+ .forEach((remoteAudio) => audioStopStubs.push(sinon.stub(remoteAudio, 'stop')));
565
+ });
566
+
567
+ // mock all stop() methods for all remote video objects we get with VideoLayoutChanged event
568
+ remoteMediaManager.on(Event.VideoLayoutChanged, (layoutInfo: VideoLayoutChangedEventData) => {
569
+ Object.values(layoutInfo.activeSpeakerVideoPanes).forEach((group) =>
570
+ group
571
+ .getRemoteMedia()
572
+ .forEach((remoteMedia) => videoStopStubs.push(sinon.stub(remoteMedia, 'stop')))
573
+ );
574
+
575
+ Object.values(layoutInfo.memberVideoPanes).forEach((pane) => {
576
+ videoStopStubs.push(sinon.stub(pane, 'stop'));
577
+ });
578
+ });
579
+
580
+ await remoteMediaManager.start();
581
+
582
+ // sanity check that we've got all our stop() mocks setup correctly
583
+ assert.strictEqual(audioStopStubs.length, 3);
584
+ assert.strictEqual(videoStopStubs.length, 10); // 10 = 6 thumbnail panes + 4 stage panes
585
+
586
+ // next, we'll change the layout, we don't care about the new video panes from the new layout, so unregister the event listeners
587
+ remoteMediaManager.removeAllListeners();
588
+
589
+ await remoteMediaManager.setLayout('AllEqual');
590
+
591
+ // check that NONE of the audio RemoteMedia instances were stopped
592
+ audioStopStubs.forEach((audioStopStub) => {
593
+ assert.notCalled(audioStopStub);
594
+ });
595
+
596
+ // check that ALL of the video RemoteMedia instances were stopped
597
+ videoStopStubs.forEach((videoStopStub) => {
598
+ assert.calledOnce(videoStopStub);
599
+ assert.calledWith(videoStopStub, false);
600
+ });
601
+ });
602
+
545
603
  describe('switching between different receiver selected layouts', () => {
546
604
  let fakeSlots: {[key: ReceiveSlotId]: FakeSlot};
547
605
  let slotCounter: number;
@@ -1,124 +0,0 @@
1
- "use strict";
2
-
3
- var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
4
-
5
- var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
6
-
7
- _Object$defineProperty(exports, "__esModule", {
8
- value: true
9
- });
10
-
11
- exports.default = void 0;
12
-
13
- var _parseInt2 = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/parse-int"));
14
-
15
- var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys"));
16
-
17
- var _tsSdp = require("@webex/ts-sdp");
18
-
19
- var PeerConnectionUtils = {}; // max-fs values for all H264 profile levels
20
-
21
- var maxFsForProfileLevel = {
22
- 10: 99,
23
- 11: 396,
24
- 12: 396,
25
- 13: 396,
26
- 20: 396,
27
- 21: 792,
28
- 22: 1620,
29
- 30: 1620,
30
- 31: 3600,
31
- 32: 5120,
32
- 40: 8192,
33
- 41: 8192,
34
- 42: 8704,
35
- 50: 22080,
36
- 51: 36864,
37
- 52: 36864,
38
- 60: 139264,
39
- 61: 139264,
40
- 62: 139264
41
- };
42
- var framesPerSecond = 30;
43
- /**
44
- * Convert C line to IPv4
45
- * @param {string} sdp
46
- * @returns {string}
47
- */
48
-
49
- PeerConnectionUtils.convertCLineToIpv4 = function (sdp) {
50
- var replaceSdp = sdp; // TODO: remove this once linus supports Ipv6 c line.currently linus rejects SDP with c line having ipv6 candidates we are
51
- // mocking ipv6 to ipv4 candidates
52
- // https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-299232
53
-
54
- replaceSdp = replaceSdp.replace(/c=IN IP6 .*/gi, 'c=IN IP4 0.0.0.0');
55
- return replaceSdp;
56
- };
57
- /**
58
- * estimate profile levels for max-fs & max-mbps values
59
- * @param {string} sdp
60
- * @param {number} maxFsValue
61
- * @returns {string}
62
- */
63
-
64
-
65
- PeerConnectionUtils.adjustH264Profile = function (sdp, maxFsValue) {
66
- // converting with ts-sdp parser, no munging
67
- var parsedSdp = (0, _tsSdp.parse)(sdp);
68
- parsedSdp.avMedia.forEach(function (media) {
69
- if (media.type === 'video') {
70
- media.codecs.forEach(function (codec) {
71
- var _codec$name;
72
-
73
- if (((_codec$name = codec.name) === null || _codec$name === void 0 ? void 0 : _codec$name.toUpperCase()) === 'H264') {
74
- // there should really be just 1 fmtp line, but just in case, we process all of them
75
- codec.fmtParams = codec.fmtParams.map(function (fmtp) {
76
- var parsedRegex = fmtp.match(/(.*)profile-level-id=(\w{4})(\w{2})(.*)/);
77
-
78
- if (parsedRegex && parsedRegex.length === 5) {
79
- var stuffBeforeProfileLevelId = parsedRegex[1];
80
- var profile = parsedRegex[2].toLowerCase();
81
- var levelId = (0, _parseInt2.default)(parsedRegex[3], 16);
82
- var stuffAfterProfileLevelId = parsedRegex[4];
83
-
84
- if (!maxFsForProfileLevel[levelId]) {
85
- throw new Error("found unsupported h264 profile level id value in the SDP: ".concat(levelId));
86
- }
87
-
88
- if (maxFsForProfileLevel[levelId] === maxFsValue) {
89
- // profile level already matches our desired max-fs value, so we don't need to do anything
90
- return fmtp;
91
- }
92
-
93
- if (maxFsForProfileLevel[levelId] < maxFsValue) {
94
- // profile level has too low max-fs, so we need to override it (this is upgrading)
95
- return "".concat(fmtp, ";max-fs=").concat(maxFsValue, ";max-mbps=").concat(maxFsValue * framesPerSecond);
96
- } // profile level has too high max-fs value, so we need to use a lower level
97
- // find highest level that has the matching maxFs
98
-
99
-
100
- var newLevelId = (0, _keys.default)(maxFsForProfileLevel).reverse().find(function (key) {
101
- return maxFsForProfileLevel[key] === maxFsValue;
102
- });
103
-
104
- if (newLevelId) {
105
- // Object.keys returns keys as strings, so we need to parse it to an int again and then convert to hex
106
- var newLevelIdHex = (0, _parseInt2.default)(newLevelId, 10).toString(16);
107
- return "".concat(stuffBeforeProfileLevelId, "profile-level-id=").concat(profile).concat(newLevelIdHex, ";max-mbps=").concat(maxFsValue * framesPerSecond).concat(stuffAfterProfileLevelId);
108
- }
109
-
110
- throw new Error("unsupported maxFsValue: ".concat(maxFsValue));
111
- }
112
-
113
- return fmtp;
114
- });
115
- }
116
- });
117
- }
118
- });
119
- return parsedSdp.toString();
120
- };
121
-
122
- var _default = PeerConnectionUtils;
123
- exports.default = _default;
124
- //# sourceMappingURL=util.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["PeerConnectionUtils","maxFsForProfileLevel","framesPerSecond","convertCLineToIpv4","sdp","replaceSdp","replace","adjustH264Profile","maxFsValue","parsedSdp","parse","avMedia","forEach","media","type","codecs","codec","name","toUpperCase","fmtParams","map","fmtp","parsedRegex","match","length","stuffBeforeProfileLevelId","profile","toLowerCase","levelId","stuffAfterProfileLevelId","Error","newLevelId","reverse","find","key","newLevelIdHex","toString"],"sources":["util.ts"],"sourcesContent":["import { parse } from '@webex/ts-sdp';\n\ninterface IPeerConnectionUtils {\n convertCLineToIpv4: (sdp: string) => string;\n adjustH264Profile: (sdp: string, maxFsValue: number) => string;\n}\n\nconst PeerConnectionUtils = {} as IPeerConnectionUtils;\n\n// max-fs values for all H264 profile levels\nconst maxFsForProfileLevel = {\n 10: 99,\n 11: 396,\n 12: 396,\n 13: 396,\n 20: 396,\n 21: 792,\n 22: 1620,\n 30: 1620,\n 31: 3600,\n 32: 5120,\n 40: 8192,\n 41: 8192,\n 42: 8704,\n 50: 22080,\n 51: 36864,\n 52: 36864,\n 60: 139264,\n 61: 139264,\n 62: 139264,\n};\n\nconst framesPerSecond = 30;\n\n/**\n * Convert C line to IPv4\n * @param {string} sdp\n * @returns {string}\n */\nPeerConnectionUtils.convertCLineToIpv4 = (sdp: string) => {\n let replaceSdp = sdp;\n\n // TODO: remove this once linus supports Ipv6 c line.currently linus rejects SDP with c line having ipv6 candidates we are\n // mocking ipv6 to ipv4 candidates\n // https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-299232\n replaceSdp = replaceSdp.replace(/c=IN IP6 .*/gi, 'c=IN IP4 0.0.0.0');\n\n return replaceSdp;\n};\n\n/**\n * estimate profile levels for max-fs & max-mbps values\n * @param {string} sdp\n * @param {number} maxFsValue\n * @returns {string}\n */\nPeerConnectionUtils.adjustH264Profile = (sdp: string, maxFsValue: number) => {\n // converting with ts-sdp parser, no munging\n const parsedSdp = parse(sdp);\n\n parsedSdp.avMedia.forEach((media) => {\n if (media.type === 'video') {\n media.codecs.forEach((codec) => {\n if (codec.name?.toUpperCase() === 'H264') {\n // there should really be just 1 fmtp line, but just in case, we process all of them\n codec.fmtParams = codec.fmtParams.map((fmtp) => {\n const parsedRegex = fmtp.match(/(.*)profile-level-id=(\\w{4})(\\w{2})(.*)/);\n\n if (parsedRegex && parsedRegex.length === 5) {\n const stuffBeforeProfileLevelId = parsedRegex[1];\n const profile = parsedRegex[2].toLowerCase();\n const levelId = parseInt(parsedRegex[3], 16);\n const stuffAfterProfileLevelId = parsedRegex[4];\n\n if (!maxFsForProfileLevel[levelId]) {\n throw new Error(\n `found unsupported h264 profile level id value in the SDP: ${levelId}`\n );\n }\n\n if (maxFsForProfileLevel[levelId] === maxFsValue) {\n // profile level already matches our desired max-fs value, so we don't need to do anything\n return fmtp;\n }\n if (maxFsForProfileLevel[levelId] < maxFsValue) {\n // profile level has too low max-fs, so we need to override it (this is upgrading)\n return `${fmtp};max-fs=${maxFsValue};max-mbps=${maxFsValue * framesPerSecond}`;\n }\n\n // profile level has too high max-fs value, so we need to use a lower level\n\n // find highest level that has the matching maxFs\n const newLevelId = Object.keys(maxFsForProfileLevel)\n .reverse()\n .find((key) => maxFsForProfileLevel[key] === maxFsValue);\n\n if (newLevelId) {\n // Object.keys returns keys as strings, so we need to parse it to an int again and then convert to hex\n const newLevelIdHex = parseInt(newLevelId, 10).toString(16);\n\n return `${stuffBeforeProfileLevelId}profile-level-id=${profile}${newLevelIdHex};max-mbps=${maxFsValue * framesPerSecond}${stuffAfterProfileLevelId}`;\n }\n\n throw new Error(`unsupported maxFsValue: ${maxFsValue}`);\n }\n\n return fmtp;\n });\n }\n });\n }\n });\n\n return parsedSdp.toString();\n};\n\nexport default PeerConnectionUtils;\n"],"mappings":"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"}
@@ -1,117 +0,0 @@
1
- import { parse } from '@webex/ts-sdp';
2
-
3
- interface IPeerConnectionUtils {
4
- convertCLineToIpv4: (sdp: string) => string;
5
- adjustH264Profile: (sdp: string, maxFsValue: number) => string;
6
- }
7
-
8
- const PeerConnectionUtils = {} as IPeerConnectionUtils;
9
-
10
- // max-fs values for all H264 profile levels
11
- const maxFsForProfileLevel = {
12
- 10: 99,
13
- 11: 396,
14
- 12: 396,
15
- 13: 396,
16
- 20: 396,
17
- 21: 792,
18
- 22: 1620,
19
- 30: 1620,
20
- 31: 3600,
21
- 32: 5120,
22
- 40: 8192,
23
- 41: 8192,
24
- 42: 8704,
25
- 50: 22080,
26
- 51: 36864,
27
- 52: 36864,
28
- 60: 139264,
29
- 61: 139264,
30
- 62: 139264,
31
- };
32
-
33
- const framesPerSecond = 30;
34
-
35
- /**
36
- * Convert C line to IPv4
37
- * @param {string} sdp
38
- * @returns {string}
39
- */
40
- PeerConnectionUtils.convertCLineToIpv4 = (sdp: string) => {
41
- let replaceSdp = sdp;
42
-
43
- // TODO: remove this once linus supports Ipv6 c line.currently linus rejects SDP with c line having ipv6 candidates we are
44
- // mocking ipv6 to ipv4 candidates
45
- // https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-299232
46
- replaceSdp = replaceSdp.replace(/c=IN IP6 .*/gi, 'c=IN IP4 0.0.0.0');
47
-
48
- return replaceSdp;
49
- };
50
-
51
- /**
52
- * estimate profile levels for max-fs & max-mbps values
53
- * @param {string} sdp
54
- * @param {number} maxFsValue
55
- * @returns {string}
56
- */
57
- PeerConnectionUtils.adjustH264Profile = (sdp: string, maxFsValue: number) => {
58
- // converting with ts-sdp parser, no munging
59
- const parsedSdp = parse(sdp);
60
-
61
- parsedSdp.avMedia.forEach((media) => {
62
- if (media.type === 'video') {
63
- media.codecs.forEach((codec) => {
64
- if (codec.name?.toUpperCase() === 'H264') {
65
- // there should really be just 1 fmtp line, but just in case, we process all of them
66
- codec.fmtParams = codec.fmtParams.map((fmtp) => {
67
- const parsedRegex = fmtp.match(/(.*)profile-level-id=(\w{4})(\w{2})(.*)/);
68
-
69
- if (parsedRegex && parsedRegex.length === 5) {
70
- const stuffBeforeProfileLevelId = parsedRegex[1];
71
- const profile = parsedRegex[2].toLowerCase();
72
- const levelId = parseInt(parsedRegex[3], 16);
73
- const stuffAfterProfileLevelId = parsedRegex[4];
74
-
75
- if (!maxFsForProfileLevel[levelId]) {
76
- throw new Error(
77
- `found unsupported h264 profile level id value in the SDP: ${levelId}`
78
- );
79
- }
80
-
81
- if (maxFsForProfileLevel[levelId] === maxFsValue) {
82
- // profile level already matches our desired max-fs value, so we don't need to do anything
83
- return fmtp;
84
- }
85
- if (maxFsForProfileLevel[levelId] < maxFsValue) {
86
- // profile level has too low max-fs, so we need to override it (this is upgrading)
87
- return `${fmtp};max-fs=${maxFsValue};max-mbps=${maxFsValue * framesPerSecond}`;
88
- }
89
-
90
- // profile level has too high max-fs value, so we need to use a lower level
91
-
92
- // find highest level that has the matching maxFs
93
- const newLevelId = Object.keys(maxFsForProfileLevel)
94
- .reverse()
95
- .find((key) => maxFsForProfileLevel[key] === maxFsValue);
96
-
97
- if (newLevelId) {
98
- // Object.keys returns keys as strings, so we need to parse it to an int again and then convert to hex
99
- const newLevelIdHex = parseInt(newLevelId, 10).toString(16);
100
-
101
- return `${stuffBeforeProfileLevelId}profile-level-id=${profile}${newLevelIdHex};max-mbps=${maxFsValue * framesPerSecond}${stuffAfterProfileLevelId}`;
102
- }
103
-
104
- throw new Error(`unsupported maxFsValue: ${maxFsValue}`);
105
- }
106
-
107
- return fmtp;
108
- });
109
- }
110
- });
111
- }
112
- });
113
-
114
- return parsedSdp.toString();
115
- };
116
-
117
- export default PeerConnectionUtils;
@@ -1,389 +0,0 @@
1
- // SDPs generated by browsers always have CRLF (\r\n) endings,
2
- // here we're using tagged template literals to ensures that our SDP fixtures also have CRLF endings
3
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
- const ensureCRLF = (strings: any, ...tags: string[]) => {
5
- const stringsWithCRLF = strings.raw.map((str: string) => str.replace(/\n/g, '\r\n'));
6
-
7
- // now construct the output the same way as it's done by default for template literals
8
- let output = stringsWithCRLF[0];
9
-
10
- tags.forEach((tag, index) => {
11
- output += tag + stringsWithCRLF[index + 1];
12
- });
13
-
14
- return output;
15
- };
16
-
17
- // example SDP that has an audio and 2 video m-lines, video contains multiple codecs
18
- // it's basically a trimmed down version of an SDP example from Chrome
19
- export const SDP_MULTIPLE_VIDEO_CODECS = ensureCRLF`v=0
20
- o=- 3328550572590672467 2 IN IP4 127.0.0.1
21
- s=-
22
- t=0 0
23
- a=group:BUNDLE 0 1 2
24
- a=extmap-allow-mixed
25
- a=msid-semantic: WMS 38872d1a-f4c3-4234-b162-bb47bc55ec57
26
- m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 106
27
- c=IN IP4 0.0.0.0
28
- a=ice-ufrag:vPie
29
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
30
- a=ice-options:trickle
31
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
32
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
33
- a=setup:actpass
34
- a=mid:0
35
- a=rtcp-mux
36
- a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
37
- a=sendrecv
38
- a=rtpmap:111 opus/48000/2
39
- a=rtcp-fb:111 transport-cc
40
- a=fmtp:111 minptime=10;useinbandfec=1
41
- a=rtpmap:63 red/48000/2
42
- a=fmtp:63 111/111
43
- a=rtpmap:9 G722/8000
44
- a=rtpmap:0 PCMU/8000
45
- a=rtpmap:8 PCMA/8000
46
- a=rtpmap:106 CN/32000
47
- a=rtcp:9 IN IP4 0.0.0.0
48
- a=msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 0283c960-c78d-4164-a6f3-394342273ad4
49
- a=ssrc:3693867886 cname:TGydLcYyPoQiRBlB
50
- a=ssrc:3693867886 msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 0283c960-c78d-4164-a6f3-394342273ad4
51
- m=video 9 UDP/TLS/RTP/SAVPF 100 101 127 121 114 115 116 117 118
52
- c=IN IP4 0.0.0.0
53
- a=ice-ufrag:vPie
54
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
55
- a=ice-options:trickle
56
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
57
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
58
- a=setup:actpass
59
- a=mid:1
60
- a=rtcp-mux
61
- a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
62
- a=sendrecv
63
- a=rtpmap:100 VP9/90000
64
- a=rtcp-fb:100 goog-remb
65
- a=rtcp-fb:100 transport-cc
66
- a=rtcp-fb:100 ccm fir
67
- a=rtcp-fb:100 nack
68
- a=rtcp-fb:100 nack pli
69
- a=fmtp:100 profile-id=2
70
- a=rtpmap:101 rtx/90000
71
- a=fmtp:101 apt=100
72
- a=rtpmap:127 H264/90000
73
- a=rtcp-fb:127 goog-remb
74
- a=rtcp-fb:127 transport-cc
75
- a=rtcp-fb:127 ccm fir
76
- a=rtcp-fb:127 nack
77
- a=rtcp-fb:127 nack pli
78
- a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
79
- a=rtpmap:121 rtx/90000
80
- a=fmtp:121 apt=127
81
- a=rtpmap:114 H264/90000
82
- a=rtcp-fb:114 goog-remb
83
- a=rtcp-fb:114 transport-cc
84
- a=rtcp-fb:114 ccm fir
85
- a=rtcp-fb:114 nack
86
- a=rtcp-fb:114 nack pli
87
- a=fmtp:114 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f
88
- a=rtpmap:115 rtx/90000
89
- a=fmtp:115 apt=114
90
- a=rtpmap:116 red/90000
91
- a=rtpmap:117 rtx/90000
92
- a=fmtp:117 apt=116
93
- a=rtpmap:118 ulpfec/90000
94
- a=rtcp-rsize
95
- a=rtcp:9 IN IP4 0.0.0.0
96
- a=msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 c0ea0a74-c991-4005-ab6a-3a29f23e67b7
97
- a=ssrc:597013044 cname:TGydLcYyPoQiRBlB
98
- a=ssrc:597013044 msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 c0ea0a74-c991-4005-ab6a-3a29f23e67b7
99
- m=video 9 UDP/TLS/RTP/SAVPF 102 122 127 121 108 109
100
- c=IN IP4 0.0.0.0
101
- a=ice-ufrag:vPie
102
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
103
- a=ice-options:trickle
104
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
105
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
106
- a=setup:actpass
107
- a=mid:2
108
- a=rtcp-mux
109
- a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
110
- a=recvonly
111
- a=rtpmap:102 VP9/90000
112
- a=rtcp-fb:102 goog-remb
113
- a=rtcp-fb:102 transport-cc
114
- a=rtcp-fb:102 ccm fir
115
- a=rtcp-fb:102 nack
116
- a=rtcp-fb:102 nack pli
117
- a=fmtp:102 profile-id=1
118
- a=rtpmap:122 rtx/90000
119
- a=fmtp:122 apt=102
120
- a=rtpmap:127 H264/90000
121
- a=rtcp-fb:127 goog-remb
122
- a=rtcp-fb:127 transport-cc
123
- a=rtcp-fb:127 ccm fir
124
- a=rtcp-fb:127 nack
125
- a=rtcp-fb:127 nack pli
126
- a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
127
- a=rtpmap:121 rtx/90000
128
- a=fmtp:121 apt=127
129
- a=rtpmap:108 H264/90000
130
- a=rtcp-fb:108 goog-remb
131
- a=rtcp-fb:108 transport-cc
132
- a=rtcp-fb:108 ccm fir
133
- a=rtcp-fb:108 nack
134
- a=rtcp-fb:108 nack pli
135
- a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
136
- a=rtpmap:109 rtx/90000
137
- a=fmtp:109 apt=108
138
- a=rtcp-rsize
139
- a=rtcp:9 IN IP4 0.0.0.0
140
- `;
141
-
142
- // same as SDP_MULTIPLE_VIDEO_CODECS but with h264 profile level changed from 3.1 to 3.0
143
- export const SDP_MULTIPLE_VIDEO_CODECS_WITH_LOWERED_H264_PROFILE_LEVEL = ensureCRLF`v=0
144
- o=- 3328550572590672467 2 IN IP4 127.0.0.1
145
- s=-
146
- t=0 0
147
- a=group:BUNDLE 0 1 2
148
- a=extmap-allow-mixed
149
- a=msid-semantic: WMS 38872d1a-f4c3-4234-b162-bb47bc55ec57
150
- m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 106
151
- c=IN IP4 0.0.0.0
152
- a=ice-ufrag:vPie
153
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
154
- a=ice-options:trickle
155
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
156
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
157
- a=setup:actpass
158
- a=mid:0
159
- a=rtcp-mux
160
- a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
161
- a=sendrecv
162
- a=rtpmap:111 opus/48000/2
163
- a=rtcp-fb:111 transport-cc
164
- a=fmtp:111 minptime=10;useinbandfec=1
165
- a=rtpmap:63 red/48000/2
166
- a=fmtp:63 111/111
167
- a=rtpmap:9 G722/8000
168
- a=rtpmap:0 PCMU/8000
169
- a=rtpmap:8 PCMA/8000
170
- a=rtpmap:106 CN/32000
171
- a=rtcp:9 IN IP4 0.0.0.0
172
- a=msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 0283c960-c78d-4164-a6f3-394342273ad4
173
- a=ssrc:3693867886 cname:TGydLcYyPoQiRBlB
174
- a=ssrc:3693867886 msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 0283c960-c78d-4164-a6f3-394342273ad4
175
- m=video 9 UDP/TLS/RTP/SAVPF 100 101 127 121 114 115 116 117 118
176
- c=IN IP4 0.0.0.0
177
- a=ice-ufrag:vPie
178
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
179
- a=ice-options:trickle
180
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
181
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
182
- a=setup:actpass
183
- a=mid:1
184
- a=rtcp-mux
185
- a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
186
- a=sendrecv
187
- a=rtpmap:100 VP9/90000
188
- a=rtcp-fb:100 goog-remb
189
- a=rtcp-fb:100 transport-cc
190
- a=rtcp-fb:100 ccm fir
191
- a=rtcp-fb:100 nack
192
- a=rtcp-fb:100 nack pli
193
- a=fmtp:100 profile-id=2
194
- a=rtpmap:101 rtx/90000
195
- a=fmtp:101 apt=100
196
- a=rtpmap:127 H264/90000
197
- a=rtcp-fb:127 goog-remb
198
- a=rtcp-fb:127 transport-cc
199
- a=rtcp-fb:127 ccm fir
200
- a=rtcp-fb:127 nack
201
- a=rtcp-fb:127 nack pli
202
- a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001e;max-mbps=48600
203
- a=rtpmap:121 rtx/90000
204
- a=fmtp:121 apt=127
205
- a=rtpmap:114 H264/90000
206
- a=rtcp-fb:114 goog-remb
207
- a=rtcp-fb:114 transport-cc
208
- a=rtcp-fb:114 ccm fir
209
- a=rtcp-fb:114 nack
210
- a=rtcp-fb:114 nack pli
211
- a=fmtp:114 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001e;max-mbps=48600
212
- a=rtpmap:115 rtx/90000
213
- a=fmtp:115 apt=114
214
- a=rtpmap:116 red/90000
215
- a=rtpmap:117 rtx/90000
216
- a=fmtp:117 apt=116
217
- a=rtpmap:118 ulpfec/90000
218
- a=rtcp-rsize
219
- a=rtcp:9 IN IP4 0.0.0.0
220
- a=msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 c0ea0a74-c991-4005-ab6a-3a29f23e67b7
221
- a=ssrc:597013044 cname:TGydLcYyPoQiRBlB
222
- a=ssrc:597013044 msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 c0ea0a74-c991-4005-ab6a-3a29f23e67b7
223
- m=video 9 UDP/TLS/RTP/SAVPF 102 122 127 121 108 109
224
- c=IN IP4 0.0.0.0
225
- a=ice-ufrag:vPie
226
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
227
- a=ice-options:trickle
228
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
229
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
230
- a=setup:actpass
231
- a=mid:2
232
- a=rtcp-mux
233
- a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
234
- a=recvonly
235
- a=rtpmap:102 VP9/90000
236
- a=rtcp-fb:102 goog-remb
237
- a=rtcp-fb:102 transport-cc
238
- a=rtcp-fb:102 ccm fir
239
- a=rtcp-fb:102 nack
240
- a=rtcp-fb:102 nack pli
241
- a=fmtp:102 profile-id=1
242
- a=rtpmap:122 rtx/90000
243
- a=fmtp:122 apt=102
244
- a=rtpmap:127 H264/90000
245
- a=rtcp-fb:127 goog-remb
246
- a=rtcp-fb:127 transport-cc
247
- a=rtcp-fb:127 ccm fir
248
- a=rtcp-fb:127 nack
249
- a=rtcp-fb:127 nack pli
250
- a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001e;max-mbps=48600
251
- a=rtpmap:121 rtx/90000
252
- a=fmtp:121 apt=127
253
- a=rtpmap:108 H264/90000
254
- a=rtcp-fb:108 goog-remb
255
- a=rtcp-fb:108 transport-cc
256
- a=rtcp-fb:108 ccm fir
257
- a=rtcp-fb:108 nack
258
- a=rtcp-fb:108 nack pli
259
- a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01e;max-mbps=48600
260
- a=rtpmap:109 rtx/90000
261
- a=fmtp:109 apt=108
262
- a=rtcp-rsize
263
- a=rtcp:9 IN IP4 0.0.0.0
264
- `;
265
-
266
-
267
- // same as SDP_MULTIPLE_VIDEO_CODECS, but has max-fs appended to all H264 fmtp lines
268
- export const SDP_MULTIPLE_VIDEO_CODECS_WITH_MAX_FS = ensureCRLF`v=0
269
- o=- 3328550572590672467 2 IN IP4 127.0.0.1
270
- s=-
271
- t=0 0
272
- a=group:BUNDLE 0 1 2
273
- a=extmap-allow-mixed
274
- a=msid-semantic: WMS 38872d1a-f4c3-4234-b162-bb47bc55ec57
275
- m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 106
276
- c=IN IP4 0.0.0.0
277
- a=ice-ufrag:vPie
278
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
279
- a=ice-options:trickle
280
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
281
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
282
- a=setup:actpass
283
- a=mid:0
284
- a=rtcp-mux
285
- a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
286
- a=sendrecv
287
- a=rtpmap:111 opus/48000/2
288
- a=rtcp-fb:111 transport-cc
289
- a=fmtp:111 minptime=10;useinbandfec=1
290
- a=rtpmap:63 red/48000/2
291
- a=fmtp:63 111/111
292
- a=rtpmap:9 G722/8000
293
- a=rtpmap:0 PCMU/8000
294
- a=rtpmap:8 PCMA/8000
295
- a=rtpmap:106 CN/32000
296
- a=rtcp:9 IN IP4 0.0.0.0
297
- a=msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 0283c960-c78d-4164-a6f3-394342273ad4
298
- a=ssrc:3693867886 cname:TGydLcYyPoQiRBlB
299
- a=ssrc:3693867886 msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 0283c960-c78d-4164-a6f3-394342273ad4
300
- m=video 9 UDP/TLS/RTP/SAVPF 100 101 127 121 114 115 116 117 118
301
- c=IN IP4 0.0.0.0
302
- a=ice-ufrag:vPie
303
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
304
- a=ice-options:trickle
305
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
306
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
307
- a=setup:actpass
308
- a=mid:1
309
- a=rtcp-mux
310
- a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
311
- a=sendrecv
312
- a=rtpmap:100 VP9/90000
313
- a=rtcp-fb:100 goog-remb
314
- a=rtcp-fb:100 transport-cc
315
- a=rtcp-fb:100 ccm fir
316
- a=rtcp-fb:100 nack
317
- a=rtcp-fb:100 nack pli
318
- a=fmtp:100 profile-id=2
319
- a=rtpmap:101 rtx/90000
320
- a=fmtp:101 apt=100
321
- a=rtpmap:127 H264/90000
322
- a=rtcp-fb:127 goog-remb
323
- a=rtcp-fb:127 transport-cc
324
- a=rtcp-fb:127 ccm fir
325
- a=rtcp-fb:127 nack
326
- a=rtcp-fb:127 nack pli
327
- a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f;max-fs=8192;max-mbps=245760
328
- a=rtpmap:121 rtx/90000
329
- a=fmtp:121 apt=127
330
- a=rtpmap:114 H264/90000
331
- a=rtcp-fb:114 goog-remb
332
- a=rtcp-fb:114 transport-cc
333
- a=rtcp-fb:114 ccm fir
334
- a=rtcp-fb:114 nack
335
- a=rtcp-fb:114 nack pli
336
- a=fmtp:114 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f;max-fs=8192;max-mbps=245760
337
- a=rtpmap:115 rtx/90000
338
- a=fmtp:115 apt=114
339
- a=rtpmap:116 red/90000
340
- a=rtpmap:117 rtx/90000
341
- a=fmtp:117 apt=116
342
- a=rtpmap:118 ulpfec/90000
343
- a=rtcp-rsize
344
- a=rtcp:9 IN IP4 0.0.0.0
345
- a=msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 c0ea0a74-c991-4005-ab6a-3a29f23e67b7
346
- a=ssrc:597013044 cname:TGydLcYyPoQiRBlB
347
- a=ssrc:597013044 msid:38872d1a-f4c3-4234-b162-bb47bc55ec57 c0ea0a74-c991-4005-ab6a-3a29f23e67b7
348
- m=video 9 UDP/TLS/RTP/SAVPF 102 122 127 121 108 109
349
- c=IN IP4 0.0.0.0
350
- a=ice-ufrag:vPie
351
- a=ice-pwd:QWp1jfvbc4iUBXfLVSxhDFIN
352
- a=ice-options:trickle
353
- a=candidate:1 1 udp 2130706431 10.50.173.46 56821 typ host
354
- a=fingerprint:sha-256 60:00:B9:57:5D:EC:CE:C7:7D:57:EA:EF:AF:37:23:64:63:39:F7:F3:D5:01:C7:18:C7:6E:B9:B4:46:19:40:06
355
- a=setup:actpass
356
- a=mid:2
357
- a=rtcp-mux
358
- a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
359
- a=recvonly
360
- a=rtpmap:102 VP9/90000
361
- a=rtcp-fb:102 goog-remb
362
- a=rtcp-fb:102 transport-cc
363
- a=rtcp-fb:102 ccm fir
364
- a=rtcp-fb:102 nack
365
- a=rtcp-fb:102 nack pli
366
- a=fmtp:102 profile-id=1
367
- a=rtpmap:122 rtx/90000
368
- a=fmtp:122 apt=102
369
- a=rtpmap:127 H264/90000
370
- a=rtcp-fb:127 goog-remb
371
- a=rtcp-fb:127 transport-cc
372
- a=rtcp-fb:127 ccm fir
373
- a=rtcp-fb:127 nack
374
- a=rtcp-fb:127 nack pli
375
- a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f;max-fs=8192;max-mbps=245760
376
- a=rtpmap:121 rtx/90000
377
- a=fmtp:121 apt=127
378
- a=rtpmap:108 H264/90000
379
- a=rtcp-fb:108 goog-remb
380
- a=rtcp-fb:108 transport-cc
381
- a=rtcp-fb:108 ccm fir
382
- a=rtcp-fb:108 nack
383
- a=rtcp-fb:108 nack pli
384
- a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f;max-fs=8192;max-mbps=245760
385
- a=rtpmap:109 rtx/90000
386
- a=fmtp:109 apt=108
387
- a=rtcp-rsize
388
- a=rtcp:9 IN IP4 0.0.0.0
389
- `;