Skip to content
This repository was archived by the owner on Oct 25, 2024. It is now read-only.

Commit 3717273

Browse files
committed
Publish media data with WebTransport.
1 parent 556f762 commit 3717273

File tree

8 files changed

+436
-51
lines changed

8 files changed

+436
-51
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (C) <2021> Intel Corporation
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
/* eslint-disable require-jsdoc */
6+
/* global VideoEncoder */
7+
8+
let bidirectionalStreamWritable, videoEncoder, frameBuffer, sendStreamWriter;
9+
// 4 bytes for frame size before each frame. The 1st byte is reserved, always 0.
10+
const sizePrefix = 4;
11+
12+
onmessage = (e) => {
13+
if (e.data[0] === 'video-source') {
14+
readVideoData(e.data[1]);
15+
} else if (e.data[0] === 'send-stream') {
16+
bidirectionalStreamWritable = e.data[1];
17+
sendStreamWriter = bidirectionalStreamWritable.getWriter();
18+
writeTrackId();
19+
initVideoEncoder();
20+
}
21+
};
22+
23+
async function videoOutput(chunk, metadata) {
24+
if (bidirectionalStreamWritable) {
25+
if (!frameBuffer ||
26+
frameBuffer.byteLength < chunk.byteLength + sizePrefix) {
27+
frameBuffer = new ArrayBuffer(chunk.byteLength + sizePrefix);
28+
}
29+
const bufferView = new Uint8Array(frameBuffer, sizePrefix);
30+
chunk.copyTo(bufferView);
31+
const dataView =
32+
new DataView(frameBuffer, 0, chunk.byteLength + sizePrefix);
33+
dataView.setUint32(0, chunk.byteLength);
34+
await sendStreamWriter.ready;
35+
await sendStreamWriter.write(dataView);
36+
console.log('Write a frame.');
37+
}
38+
}
39+
40+
function videoError(error) {
41+
console.log('Encode error, ' + error);
42+
}
43+
44+
async function writeTrackId() {
45+
const id = new Uint8Array(16);
46+
id[16] = 2;
47+
await sendStreamWriter.ready;
48+
sendStreamWriter.write(id);
49+
}
50+
51+
function initVideoEncoder() {
52+
videoEncoder = new VideoEncoder({output: videoOutput, error: videoError});
53+
videoEncoder.configure({
54+
codec: 'avc1.4d002a',
55+
width: 640,
56+
height: 480,
57+
framerate: 30,
58+
latencyMode: 'realtime',
59+
avc: {format: 'annexb'},
60+
});
61+
}
62+
63+
// Read data from video track.
64+
async function readVideoData(readable) {
65+
const reader = readable.getReader();
66+
while (true) {
67+
const {value, done} = await reader.read();
68+
if (done) {
69+
console.log('MediaStream ends.');
70+
break;
71+
}
72+
videoEncoder.encode(value);
73+
value.close();
74+
}
75+
}

src/samples/conference/public/scripts/quic.js

+64-13
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,62 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44

5+
/* eslint-disable require-jsdoc */
6+
57
'use strict';
68

79
let quicChannel = null;
810
let bidirectionalStream = null;
9-
let writeTask;
10-
const conference=new Owt.Conference.ConferenceClient();
11+
let writeTask, mediaStream, mediaWorker, conferenceId, myId;
12+
13+
const conference = new Owt.Conference.ConferenceClient({
14+
webTransportConfiguration: {
15+
serverCertificateFingerprints: [{
16+
value:
17+
'DD:A8:11:FD:A1:08:17:41:36:CD:1A:33:1E:CF:AE:0D:46:3D:15:16:2C:67:C5:A2:06:35:C2:0E:88:A1:9E:C6',
18+
algorithm: 'sha-256',
19+
}]
20+
}
21+
});
1122
conference.addEventListener('streamadded', async (event) => {
1223
console.log(event.stream);
13-
if (event.stream.source.data) {
14-
const subscription = await conference.subscribe(event.stream);
24+
if (event.stream.origin == myId) {
25+
mixStream(
26+
conferenceId, event.stream.id, 'common',
27+
'http://jianjunz-nuc-ubuntu.sh.intel.com:3001');
28+
}
29+
if (event.stream.source.data || event.stream.source.video) {
30+
const subscription = await conference.subscribe(
31+
event.stream,
32+
{audio: false, video: {codecs: ['h264']}, transport: {type: 'quic'}});
1533
const reader = subscription.stream.readable.getReader();
1634
while (true) {
1735
const {value, done} = await reader.read();
1836
if (done) {
1937
console.log('Subscription ends.');
2038
break;
2139
}
22-
console.log('Received data: '+value);
40+
console.log('Received data: ' + value);
2341
}
2442
}
2543
});
2644

2745
function updateConferenceStatus(message) {
2846
document.getElementById('conference-status').innerHTML +=
29-
('<p>' + message + '</p>');
47+
('<p>' + message + '</p>');
3048
}
3149

3250

3351
function joinConference() {
3452
return new Promise((resolve, reject) => {
35-
createToken(undefined, 'user', 'presenter', resp => {
36-
conference.join(resp).then(() => {
53+
createToken(undefined, 'user', 'presenter', token => {
54+
conference.join(token).then((info) => {
55+
conferenceId = info.id;
56+
myId = info.self.id;
3757
updateConferenceStatus('Connected to conference server.');
3858
resolve();
3959
});
40-
});
60+
}, 'http://jianjunz-nuc-ubuntu.sh.intel.com:3001');
4161
});
4262
};
4363

@@ -55,10 +75,31 @@ function createRandomContentSessionId() {
5575
return id;
5676
}
5777

78+
async function attachReader(stream) {
79+
const reader = stream.readable.getReader();
80+
while (true) {
81+
const {value, done} = await reader.read();
82+
if (done) {
83+
console.log('Ends.');
84+
break;
85+
}
86+
console.log('Received data: ' + value);
87+
}
88+
}
89+
5890
async function createSendChannel() {
5991
bidirectionalStream = await conference.createSendStream();
60-
const localStream=new Owt.Base.LocalStream(bidirectionalStream, new Owt.Base.StreamSourceInfo(undefined, undefined,true));
61-
const publication = await conference.publish(localStream);
92+
const localStream = new Owt.Base.LocalStream(
93+
bidirectionalStream,
94+
new Owt.Base.StreamSourceInfo(undefined, 'camera', undefined));
95+
attachReader(bidirectionalStream);
96+
const publication = await conference.publish(
97+
localStream, {video: {codec: 'h264'}, transport: {type: 'quic'}});
98+
// const localStream = new Owt.Base.LocalStream(
99+
// bidirectionalStream,
100+
// new Owt.Base.StreamSourceInfo(undefined, undefined, true));
101+
// const publication = await conference.publish(
102+
// localStream, {transport: {type: 'quic'}});
62103
console.log(publication);
63104
updateConferenceStatus('Created send channel.');
64105
}
@@ -79,6 +120,16 @@ async function writeUuid() {
79120
return;
80121
}
81122

123+
async function writeVideoData() {
124+
mediaStream = await navigator.mediaDevices.getUserMedia({video: true});
125+
const track = new MediaStreamTrackProcessor(mediaStream.getVideoTracks()[0]);
126+
mediaWorker = new Worker('./scripts/media-worker.js');
127+
mediaWorker.postMessage(['video-source', track.readable], [track.readable]);
128+
mediaWorker.postMessage(
129+
['send-stream', bidirectionalStream.writable],
130+
[bidirectionalStream.writable]);
131+
}
132+
82133
async function writeData() {
83134
const encoder = new TextEncoder();
84135
const encoded = encoder.encode('message', {stream: true});
@@ -98,8 +149,8 @@ document.getElementById('start-sending').addEventListener('click', async () => {
98149
updateConferenceStatus('Stream is not created.');
99150
return;
100151
}
101-
await writeUuid();
102-
writeTask = setInterval(writeData, 2000);
152+
writeVideoData();
153+
// writeTask = setInterval(writeData, 2000);
103154
updateConferenceStatus('Started sending.');
104155
});
105156

src/sdk/base/publication.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -195,17 +195,29 @@ export class PublishOptions {
195195
// eslint-disable-next-line require-jsdoc
196196
constructor(audio, video, transport) {
197197
/**
198-
* @member {?Array<Owt.Base.AudioEncodingParameters> | ?Array<RTCRtpEncodingParameters>} audio
198+
* @member {?Array<Owt.Base.AudioEncodingParameters> |
199+
* ?Array<RTCRtpEncodingParameters> | ?AudioEncoderConfig } audio
199200
* @instance
200201
* @memberof Owt.Base.PublishOptions
201-
* @desc Parameters for audio RtpSender. Publishing with RTCRtpEncodingParameters is an experimental feature. It is subject to change.
202+
* @desc Parameters for audio RtpSender when transport's type is 'webrtc' or
203+
* configuration of audio encoder when transport's type is 'quic'.
204+
* Publishing with RTCRtpEncodingParameters is an experimental feature. It
205+
* is subject to change.
206+
* @see {@link https://www.w3.org/TR/webrtc/#rtcrtpencodingparameters|RTCRtpEncodingParameters}
207+
* @see {@link https://w3c.github.io/webcodecs/#dictdef-audioencoderconfig|AudioEncoderConfig}
202208
*/
203209
this.audio = audio;
204210
/**
205-
* @member {?Array<Owt.Base.VideoEncodingParameters> | ?Array<RTCRtpEncodingParameters>} video
211+
* @member {?Array<Owt.Base.VideoEncodingParameters> |
212+
* ?Array<RTCRtpEncodingParameters> | ?VideoEncoderConfig } video
206213
* @instance
207214
* @memberof Owt.Base.PublishOptions
208-
* @desc Parameters for video RtpSender. Publishing with RTCRtpEncodingParameters is an experimental feature. It is subject to change.
215+
* @desc Parameters for video RtpSender when transport's type is 'webrtc' or
216+
* configuration of video encoder when transport's type is 'quic'.
217+
* Publishing with RTCRtpEncodingParameters is an experimental feature. It
218+
* is subject to change.
219+
* @see {@link https://www.w3.org/TR/webrtc/#rtcrtpencodingparameters|RTCRtpEncodingParameters}
220+
* @see {@link https://w3c.github.io/webcodecs/#dictdef-videoencoderconfig|VideoEncoderConfig}
209221
*/
210222
this.video = video;
211223
/**

src/sdk/base/transport.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ export class TransportConstraints {
2929
* @member {Array.<Owt.Base.TransportType>} type
3030
* @instance
3131
* @memberof Owt.Base.TransportConstraints
32-
* @desc Transport type for publication and subscription.
32+
* @desc Transport type for publication and subscription. 'quic' is only
33+
* supported in conference mode when WebTransport is supported by client and
34+
* enabled at server side.
3335
*/
3436
this.type = type;
3537
/**

src/sdk/conference/client.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import * as StreamModule from '../base/stream.js';
1616
import {Participant} from './participant.js';
1717
import {ConferenceInfo} from './info.js';
1818
import {ConferencePeerConnectionChannel} from './channel.js';
19-
import {QuicConnection} from './quicconnection.js';
19+
import {QuicConnection} from './webtransport/connection.js';
2020
import {RemoteMixedStream, ActiveAudioInputChangeEvent, LayoutChangeEvent}
2121
from './mixedstream.js';
2222
import * as StreamUtilsModule from './streamutils.js';
@@ -464,16 +464,16 @@ export const ConferenceClient = function(config, signalingImpl) {
464464
* @instance
465465
* @desc Publish a LocalStream to conference server. Other participants will be able to subscribe this stream when it is successfully published.
466466
* @param {Owt.Base.LocalStream} stream The stream to be published.
467-
* @param {(Owt.Base.PublishOptions|RTCRtpTransceiver[])} options If options is a PublishOptions, the stream will be published as options specified. If options is a list of RTCRtpTransceivers, each track in the first argument must have a corresponding RTCRtpTransceiver here, and the track will be published with the RTCRtpTransceiver associated with it.
467+
* @param {(Owt.Base.PublishOptions|RTCRtpTransceiver[])} options If options is a PublishOptions, the stream will be published as options specified. If options is a list of RTCRtpTransceivers, each track in the first argument must have a corresponding RTCRtpTransceiver here, and the track will be published with the RTCRtpTransceiver associated with it. If the type of transport is quic, PublishOptions.audio should be AudioEncoderConfig, and PublishOptions.video should be VideoEncoderConfig.
468468
* @param {string[]} videoCodecs Video codec names for publishing. Valid values are 'VP8', 'VP9' and 'H264'. This parameter only valid when the second argument is PublishOptions and options.video is RTCRtpEncodingParameters. Publishing with RTCRtpEncodingParameters is an experimental feature. This parameter is subject to change.
469469
* @return {Promise<Publication, Error>} Returned promise will be resolved with a newly created Publication once specific stream is successfully published, or rejected with a newly created Error if stream is invalid or options cannot be satisfied. Successfully published means PeerConnection is established and server is able to process media data.
470470
*/
471471
this.publish = function(stream, options, videoCodecs) {
472472
if (!(stream instanceof StreamModule.LocalStream)) {
473473
return Promise.reject(new ConferenceError('Invalid stream.'));
474474
}
475-
if (stream.source.data) {
476-
return quicTransportChannel.publish(stream);
475+
if (options?.transport?.type === 'quic') {
476+
return quicTransportChannel.publish(stream, options, videoCodecs);
477477
}
478478
if (publishChannels.has(stream.mediaStream.id)) {
479479
return Promise.reject(new ConferenceError(
@@ -501,13 +501,16 @@ export const ConferenceClient = function(config, signalingImpl) {
501501
'Invalid source info. A remote stream is either a data stream or ' +
502502
'a media stream.'));
503503
}
504+
}
505+
if (options?.transport?.type === 'quic') {
504506
if (quicTransportChannel) {
505-
return quicTransportChannel.subscribe(stream);
507+
return quicTransportChannel.subscribe(stream, options);
506508
} else {
507509
return Promise.reject(new TypeError('WebTransport is not supported.'));
508510
}
511+
} else {
512+
return peerConnectionChannel.subscribe(stream, options);
509513
}
510-
return peerConnectionChannel.subscribe(stream, options);
511514
};
512515

513516
/**

src/sdk/conference/subscription.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class AudioSubscriptionConstraints {
104104
* @member {?Array.<Owt.Base.AudioCodecParameters>} codecs
105105
* @instance
106106
* @memberof Owt.Conference.AudioSubscriptionConstraints
107-
* @desc Codecs accepted. If none of `codecs` supported by both sides, connection fails. Leave it undefined will use all possible codecs.
107+
* @desc Codecs accepted. Please only include 1 item if transport is "quic". For "webrtc" transport, if none of `codecs` supported by both sides, connection fails. Leave it undefined will use all possible codecs.
108108
*/
109109
this.codecs = codecs;
110110
}
@@ -124,7 +124,7 @@ export class VideoSubscriptionConstraints {
124124
* @member {?Array.<Owt.Base.VideoCodecParameters>} codecs
125125
* @instance
126126
* @memberof Owt.Conference.VideoSubscriptionConstraints
127-
* @desc Codecs accepted. If none of `codecs` supported by both sides, connection fails. Leave it undefined will use all possible codecs.
127+
* @desc Codecs accepted. Please only include 1 item if transport is "quic". For "webrtc" transport, if none of `codecs` supported by both sides, connection fails. Leave it undefined will use all possible codecs.
128128
*/
129129
this.codecs = codecs;
130130
/**

0 commit comments

Comments
 (0)