@stream-io/video-client 0.0.3 → 0.0.5
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.
- package/CHANGELOG.md +14 -0
- package/dist/index.browser.es.js +10 -8
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +10 -8
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +10 -8
- package/dist/index.es.js.map +1 -1
- package/dist/src/events/recording.d.ts +3 -0
- package/dist/src/gen/coordinator/index.d.ts +68 -11
- package/package.json +1 -1
- package/src/events/__tests__/recording.test.ts +14 -3
- package/src/events/recording.ts +15 -3
- package/src/gen/coordinator/index.ts +68 -11
- package/src/helpers/sdp-munging.ts +7 -11
- package/src/rtc/publisher.ts +1 -1
|
@@ -12,4 +12,7 @@ export declare const watchCallRecordingStopped: (state: CallState) => (event: St
|
|
|
12
12
|
* Watches for `call.broadcasting_started` events.
|
|
13
13
|
*/
|
|
14
14
|
export declare const watchCallBroadcastingStarted: (state: CallState) => (event: StreamVideoEvent) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Watches for `call.broadcasting_stopped` events.
|
|
17
|
+
*/
|
|
15
18
|
export declare const watchCallBroadcastingStopped: (state: CallState) => (event: StreamVideoEvent) => void;
|
|
@@ -969,12 +969,6 @@ export interface CallResponse {
|
|
|
969
969
|
* @memberof CallResponse
|
|
970
970
|
*/
|
|
971
971
|
blocked_user_ids: Array<string>;
|
|
972
|
-
/**
|
|
973
|
-
*
|
|
974
|
-
* @type {boolean}
|
|
975
|
-
* @memberof CallResponse
|
|
976
|
-
*/
|
|
977
|
-
broadcasting: boolean;
|
|
978
972
|
/**
|
|
979
973
|
* The unique identifier for a call (<type>:<id>)
|
|
980
974
|
* @type {string}
|
|
@@ -1008,17 +1002,17 @@ export interface CallResponse {
|
|
|
1008
1002
|
[key: string]: any;
|
|
1009
1003
|
};
|
|
1010
1004
|
/**
|
|
1011
|
-
*
|
|
1012
|
-
* @type {
|
|
1005
|
+
*
|
|
1006
|
+
* @type {EgressResponse}
|
|
1013
1007
|
* @memberof CallResponse
|
|
1014
1008
|
*/
|
|
1015
|
-
|
|
1009
|
+
egress: EgressResponse;
|
|
1016
1010
|
/**
|
|
1017
|
-
*
|
|
1011
|
+
* Date/time when the call ended
|
|
1018
1012
|
* @type {string}
|
|
1019
1013
|
* @memberof CallResponse
|
|
1020
1014
|
*/
|
|
1021
|
-
|
|
1015
|
+
ended_at?: string;
|
|
1022
1016
|
/**
|
|
1023
1017
|
* Call ID
|
|
1024
1018
|
* @type {string}
|
|
@@ -1989,6 +1983,69 @@ export interface EdgeResponse {
|
|
|
1989
1983
|
*/
|
|
1990
1984
|
yellow: number;
|
|
1991
1985
|
}
|
|
1986
|
+
/**
|
|
1987
|
+
*
|
|
1988
|
+
* @export
|
|
1989
|
+
* @interface EgressHLSResponse
|
|
1990
|
+
*/
|
|
1991
|
+
export interface EgressHLSResponse {
|
|
1992
|
+
/**
|
|
1993
|
+
*
|
|
1994
|
+
* @type {string}
|
|
1995
|
+
* @memberof EgressHLSResponse
|
|
1996
|
+
*/
|
|
1997
|
+
playlist_url: string;
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
*
|
|
2001
|
+
* @export
|
|
2002
|
+
* @interface EgressRTMPResponse
|
|
2003
|
+
*/
|
|
2004
|
+
export interface EgressRTMPResponse {
|
|
2005
|
+
/**
|
|
2006
|
+
*
|
|
2007
|
+
* @type {string}
|
|
2008
|
+
* @memberof EgressRTMPResponse
|
|
2009
|
+
*/
|
|
2010
|
+
name: string;
|
|
2011
|
+
/**
|
|
2012
|
+
*
|
|
2013
|
+
* @type {string}
|
|
2014
|
+
* @memberof EgressRTMPResponse
|
|
2015
|
+
*/
|
|
2016
|
+
stream_key: string;
|
|
2017
|
+
/**
|
|
2018
|
+
*
|
|
2019
|
+
* @type {string}
|
|
2020
|
+
* @memberof EgressRTMPResponse
|
|
2021
|
+
*/
|
|
2022
|
+
url: string;
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
*
|
|
2026
|
+
* @export
|
|
2027
|
+
* @interface EgressResponse
|
|
2028
|
+
*/
|
|
2029
|
+
export interface EgressResponse {
|
|
2030
|
+
/**
|
|
2031
|
+
*
|
|
2032
|
+
* @type {boolean}
|
|
2033
|
+
* @memberof EgressResponse
|
|
2034
|
+
*/
|
|
2035
|
+
broadcasting: boolean;
|
|
2036
|
+
/**
|
|
2037
|
+
*
|
|
2038
|
+
* @type {EgressHLSResponse}
|
|
2039
|
+
* @memberof EgressResponse
|
|
2040
|
+
*/
|
|
2041
|
+
hls?: EgressHLSResponse;
|
|
2042
|
+
/**
|
|
2043
|
+
*
|
|
2044
|
+
* @type {Array<EgressRTMPResponse>}
|
|
2045
|
+
* @memberof EgressResponse
|
|
2046
|
+
*/
|
|
2047
|
+
rtmps: Array<EgressRTMPResponse>;
|
|
2048
|
+
}
|
|
1992
2049
|
/**
|
|
1993
2050
|
*
|
|
1994
2051
|
* @export
|
package/package.json
CHANGED
|
@@ -30,25 +30,36 @@ describe('recording and broadcasting events', () => {
|
|
|
30
30
|
|
|
31
31
|
it('handles call.broadcasting_started events', () => {
|
|
32
32
|
const state = new CallState();
|
|
33
|
+
state.setMetadata({
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
egress: {
|
|
36
|
+
broadcasting: false,
|
|
37
|
+
hls: {
|
|
38
|
+
playlist_url: '',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
33
42
|
const handler = watchCallBroadcastingStarted(state);
|
|
34
43
|
// @ts-ignore
|
|
35
44
|
handler({
|
|
36
45
|
type: 'call.broadcasting_started',
|
|
37
46
|
hls_playlist_url: 'https://example.com/playlist.m3u8',
|
|
38
47
|
});
|
|
39
|
-
expect(state.metadata?.broadcasting).toBe(true);
|
|
40
|
-
expect(state.metadata?.
|
|
48
|
+
expect(state.metadata?.egress.broadcasting).toBe(true);
|
|
49
|
+
expect(state.metadata?.egress.hls?.playlist_url).toBe(
|
|
41
50
|
'https://example.com/playlist.m3u8',
|
|
42
51
|
);
|
|
43
52
|
});
|
|
44
53
|
|
|
45
54
|
it('handles call.broadcasting_stopped events', () => {
|
|
46
55
|
const state = new CallState();
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
state.setMetadata({});
|
|
47
58
|
const handler = watchCallBroadcastingStopped(state);
|
|
48
59
|
// @ts-ignore
|
|
49
60
|
handler({
|
|
50
61
|
type: 'call.broadcasting_stopped',
|
|
51
62
|
});
|
|
52
|
-
expect(state.metadata?.broadcasting).toBe(false);
|
|
63
|
+
expect(state.metadata?.egress.broadcasting).toBe(false);
|
|
53
64
|
});
|
|
54
65
|
});
|
package/src/events/recording.ts
CHANGED
|
@@ -35,18 +35,30 @@ export const watchCallBroadcastingStarted = (state: CallState) => {
|
|
|
35
35
|
if (event.type !== 'call.broadcasting_started') return;
|
|
36
36
|
state.setMetadata((metadata) => ({
|
|
37
37
|
...metadata!,
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
egress: {
|
|
39
|
+
...metadata!.egress,
|
|
40
|
+
broadcasting: true,
|
|
41
|
+
hls: {
|
|
42
|
+
...metadata!.egress.hls,
|
|
43
|
+
playlist_url: event.hls_playlist_url,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
40
46
|
}));
|
|
41
47
|
};
|
|
42
48
|
};
|
|
43
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Watches for `call.broadcasting_stopped` events.
|
|
52
|
+
*/
|
|
44
53
|
export const watchCallBroadcastingStopped = (state: CallState) => {
|
|
45
54
|
return function onCallBroadcastingStopped(event: StreamVideoEvent) {
|
|
46
55
|
if (event.type !== 'call.broadcasting_stopped') return;
|
|
47
56
|
state.setMetadata((metadata) => ({
|
|
48
57
|
...metadata!,
|
|
49
|
-
|
|
58
|
+
egress: {
|
|
59
|
+
...metadata!.egress,
|
|
60
|
+
broadcasting: false,
|
|
61
|
+
},
|
|
50
62
|
}));
|
|
51
63
|
};
|
|
52
64
|
};
|
|
@@ -968,12 +968,6 @@ export interface CallResponse {
|
|
|
968
968
|
* @memberof CallResponse
|
|
969
969
|
*/
|
|
970
970
|
blocked_user_ids: Array<string>;
|
|
971
|
-
/**
|
|
972
|
-
*
|
|
973
|
-
* @type {boolean}
|
|
974
|
-
* @memberof CallResponse
|
|
975
|
-
*/
|
|
976
|
-
broadcasting: boolean;
|
|
977
971
|
/**
|
|
978
972
|
* The unique identifier for a call (<type>:<id>)
|
|
979
973
|
* @type {string}
|
|
@@ -1005,17 +999,17 @@ export interface CallResponse {
|
|
|
1005
999
|
*/
|
|
1006
1000
|
custom: { [key: string]: any };
|
|
1007
1001
|
/**
|
|
1008
|
-
*
|
|
1009
|
-
* @type {
|
|
1002
|
+
*
|
|
1003
|
+
* @type {EgressResponse}
|
|
1010
1004
|
* @memberof CallResponse
|
|
1011
1005
|
*/
|
|
1012
|
-
|
|
1006
|
+
egress: EgressResponse;
|
|
1013
1007
|
/**
|
|
1014
|
-
*
|
|
1008
|
+
* Date/time when the call ended
|
|
1015
1009
|
* @type {string}
|
|
1016
1010
|
* @memberof CallResponse
|
|
1017
1011
|
*/
|
|
1018
|
-
|
|
1012
|
+
ended_at?: string;
|
|
1019
1013
|
/**
|
|
1020
1014
|
* Call ID
|
|
1021
1015
|
* @type {string}
|
|
@@ -1971,6 +1965,69 @@ export interface EdgeResponse {
|
|
|
1971
1965
|
*/
|
|
1972
1966
|
yellow: number;
|
|
1973
1967
|
}
|
|
1968
|
+
/**
|
|
1969
|
+
*
|
|
1970
|
+
* @export
|
|
1971
|
+
* @interface EgressHLSResponse
|
|
1972
|
+
*/
|
|
1973
|
+
export interface EgressHLSResponse {
|
|
1974
|
+
/**
|
|
1975
|
+
*
|
|
1976
|
+
* @type {string}
|
|
1977
|
+
* @memberof EgressHLSResponse
|
|
1978
|
+
*/
|
|
1979
|
+
playlist_url: string;
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
*
|
|
1983
|
+
* @export
|
|
1984
|
+
* @interface EgressRTMPResponse
|
|
1985
|
+
*/
|
|
1986
|
+
export interface EgressRTMPResponse {
|
|
1987
|
+
/**
|
|
1988
|
+
*
|
|
1989
|
+
* @type {string}
|
|
1990
|
+
* @memberof EgressRTMPResponse
|
|
1991
|
+
*/
|
|
1992
|
+
name: string;
|
|
1993
|
+
/**
|
|
1994
|
+
*
|
|
1995
|
+
* @type {string}
|
|
1996
|
+
* @memberof EgressRTMPResponse
|
|
1997
|
+
*/
|
|
1998
|
+
stream_key: string;
|
|
1999
|
+
/**
|
|
2000
|
+
*
|
|
2001
|
+
* @type {string}
|
|
2002
|
+
* @memberof EgressRTMPResponse
|
|
2003
|
+
*/
|
|
2004
|
+
url: string;
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
*
|
|
2008
|
+
* @export
|
|
2009
|
+
* @interface EgressResponse
|
|
2010
|
+
*/
|
|
2011
|
+
export interface EgressResponse {
|
|
2012
|
+
/**
|
|
2013
|
+
*
|
|
2014
|
+
* @type {boolean}
|
|
2015
|
+
* @memberof EgressResponse
|
|
2016
|
+
*/
|
|
2017
|
+
broadcasting: boolean;
|
|
2018
|
+
/**
|
|
2019
|
+
*
|
|
2020
|
+
* @type {EgressHLSResponse}
|
|
2021
|
+
* @memberof EgressResponse
|
|
2022
|
+
*/
|
|
2023
|
+
hls?: EgressHLSResponse;
|
|
2024
|
+
/**
|
|
2025
|
+
*
|
|
2026
|
+
* @type {Array<EgressRTMPResponse>}
|
|
2027
|
+
* @memberof EgressResponse
|
|
2028
|
+
*/
|
|
2029
|
+
rtmps: Array<EgressRTMPResponse>;
|
|
2030
|
+
}
|
|
1974
2031
|
/**
|
|
1975
2032
|
*
|
|
1976
2033
|
* @export
|
|
@@ -69,7 +69,7 @@ const getMediaSection = (sdp: string, mediaType: 'video' | 'audio') => {
|
|
|
69
69
|
sdp.split(/(\r\n|\r|\n)/).forEach((line) => {
|
|
70
70
|
const isValidLine = /^([a-z])=(.*)/.test(line);
|
|
71
71
|
if (!isValidLine) return;
|
|
72
|
-
/*
|
|
72
|
+
/*
|
|
73
73
|
NOTE: according to https://www.rfc-editor.org/rfc/rfc8866.pdf
|
|
74
74
|
Each media description starts with an "m=" line and continues to the next media description or the end of the whole session description, whichever comes first
|
|
75
75
|
*/
|
|
@@ -170,7 +170,7 @@ export const removeCodec = (
|
|
|
170
170
|
sdp: string,
|
|
171
171
|
mediaType: 'video' | 'audio',
|
|
172
172
|
codecToRemove: string,
|
|
173
|
-
) => {
|
|
173
|
+
): string => {
|
|
174
174
|
const section = getMediaSection(sdp, mediaType);
|
|
175
175
|
const mediaSection = section?.media;
|
|
176
176
|
if (!mediaSection) {
|
|
@@ -190,30 +190,26 @@ export const removeCodec = (
|
|
|
190
190
|
mediaSection.original,
|
|
191
191
|
`${mediaSection.mediaWithPorts} ${newCodecOrder}`,
|
|
192
192
|
)
|
|
193
|
-
.replace(new RegExp(`${rtpMap.original}[\r\n
|
|
194
|
-
.replace(
|
|
195
|
-
fmtp?.original ? new RegExp(`${fmtp?.original}[\r\n|\r|\n]`) : '',
|
|
196
|
-
'',
|
|
197
|
-
); // remove the corresponding fmtp line
|
|
193
|
+
.replace(new RegExp(`${rtpMap.original}[\r\n]+`), '') // remove the corresponding rtpmap line
|
|
194
|
+
.replace(fmtp?.original ? new RegExp(`${fmtp?.original}[\r\n]+`) : '', ''); // remove the corresponding fmtp line
|
|
198
195
|
};
|
|
199
196
|
|
|
200
197
|
/**
|
|
201
198
|
* Gets the fmtp line corresponding to opus
|
|
202
199
|
*/
|
|
203
|
-
const getOpusFmtp = (sdp: string) => {
|
|
200
|
+
const getOpusFmtp = (sdp: string): Fmtp | undefined => {
|
|
204
201
|
const section = getMediaSection(sdp, 'audio');
|
|
205
202
|
const rtpMap = section?.rtpMap.find((r) => r.codec.toLowerCase() === 'opus');
|
|
206
203
|
const codecId = rtpMap?.payload;
|
|
207
204
|
if (codecId) {
|
|
208
|
-
|
|
209
|
-
return fmtp;
|
|
205
|
+
return section?.fmtp.find((f) => f.payload === codecId);
|
|
210
206
|
}
|
|
211
207
|
};
|
|
212
208
|
|
|
213
209
|
/**
|
|
214
210
|
* Returns an SDP with DTX enabled or disabled.
|
|
215
211
|
*/
|
|
216
|
-
export const toggleDtx = (sdp: string, enable: boolean) => {
|
|
212
|
+
export const toggleDtx = (sdp: string, enable: boolean): string => {
|
|
217
213
|
const opusFmtp = getOpusFmtp(sdp);
|
|
218
214
|
if (opusFmtp) {
|
|
219
215
|
const matchDtx = /usedtx=(\d)/.exec(opusFmtp.config);
|
package/src/rtc/publisher.ts
CHANGED
|
@@ -342,7 +342,7 @@ export class Publisher {
|
|
|
342
342
|
const offer = await this.publisher.createOffer();
|
|
343
343
|
let sdp = offer.sdp;
|
|
344
344
|
if (sdp) {
|
|
345
|
-
toggleDtx(sdp, this.isDtxEnabled);
|
|
345
|
+
sdp = toggleDtx(sdp, this.isDtxEnabled);
|
|
346
346
|
if (isReactNative()) {
|
|
347
347
|
if (this.preferredVideoCodec) {
|
|
348
348
|
sdp = setPreferredCodec(sdp, 'video', this.preferredVideoCodec);
|