Add support media config param for JS player
This commit is contained in:
+55
-41
@@ -38,6 +38,12 @@ export class VideoRTC extends HTMLElement {
|
|||||||
*/
|
*/
|
||||||
this.mode = 'webrtc,mse,hls,mjpeg';
|
this.mode = 'webrtc,mse,hls,mjpeg';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Config] Requested medias (video, audio, microphone).
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.media = 'video,audio';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [config] Run stream when not displayed on the screen. Default `false`.
|
* [config] Run stream when not displayed on the screen. Default `false`.
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@@ -129,8 +135,8 @@ export class VideoRTC extends HTMLElement {
|
|||||||
this.ondata = null;
|
this.ondata = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [internal] Handlers list for receiving JSON from WebSocket
|
* [internal] Handlers list for receiving JSON from WebSocket.
|
||||||
* @type {Object.<string,Function>}}
|
* @type {Object.<string,Function>}
|
||||||
*/
|
*/
|
||||||
this.onmessage = null;
|
this.onmessage = null;
|
||||||
}
|
}
|
||||||
@@ -177,7 +183,9 @@ export class VideoRTC extends HTMLElement {
|
|||||||
|
|
||||||
/** @param {Function} isSupported */
|
/** @param {Function} isSupported */
|
||||||
codecs(isSupported) {
|
codecs(isSupported) {
|
||||||
return this.CODECS.filter(codec => isSupported(`video/mp4; codecs="${codec}"`)).join();
|
return this.CODECS
|
||||||
|
.filter(codec => this.media.indexOf(codec.indexOf('vc1') > 0 ? 'video' : 'audio') >= 0)
|
||||||
|
.filter(codec => isSupported(`video/mp4; codecs="${codec}"`)).join();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -302,6 +310,9 @@ export class VideoRTC extends HTMLElement {
|
|||||||
|
|
||||||
this.pcState = WebSocket.CLOSED;
|
this.pcState = WebSocket.CLOSED;
|
||||||
if (this.pc) {
|
if (this.pc) {
|
||||||
|
this.pc.getSenders().forEach(sender => {
|
||||||
|
if (sender.track) sender.track.stop();
|
||||||
|
});
|
||||||
this.pc.close();
|
this.pc.close();
|
||||||
this.pc = null;
|
this.pc = null;
|
||||||
}
|
}
|
||||||
@@ -466,10 +477,6 @@ export class VideoRTC extends HTMLElement {
|
|||||||
onwebrtc() {
|
onwebrtc() {
|
||||||
const pc = new RTCPeerConnection(this.pcConfig);
|
const pc = new RTCPeerConnection(this.pcConfig);
|
||||||
|
|
||||||
/** @type {HTMLVideoElement} */
|
|
||||||
const video2 = document.createElement('video');
|
|
||||||
video2.addEventListener('loadeddata', ev => this.onpcvideo(ev), {once: true});
|
|
||||||
|
|
||||||
pc.addEventListener('icecandidate', ev => {
|
pc.addEventListener('icecandidate', ev => {
|
||||||
if (ev.candidate && this.mode.indexOf('webrtc/tcp') >= 0 && ev.candidate.protocol === 'udp') return;
|
if (ev.candidate && this.mode.indexOf('webrtc/tcp') >= 0 && ev.candidate.protocol === 'udp') return;
|
||||||
|
|
||||||
@@ -477,21 +484,14 @@ export class VideoRTC extends HTMLElement {
|
|||||||
this.send({type: 'webrtc/candidate', value: candidate});
|
this.send({type: 'webrtc/candidate', value: candidate});
|
||||||
});
|
});
|
||||||
|
|
||||||
pc.addEventListener('track', ev => {
|
|
||||||
// when stream already init
|
|
||||||
if (video2.srcObject !== null) return;
|
|
||||||
|
|
||||||
// when audio track not exist in Chrome
|
|
||||||
if (ev.streams.length === 0) return;
|
|
||||||
|
|
||||||
// when audio track not exist in Firefox
|
|
||||||
if (ev.streams[0].id[0] === '{') return;
|
|
||||||
|
|
||||||
video2.srcObject = ev.streams[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
pc.addEventListener('connectionstatechange', () => {
|
pc.addEventListener('connectionstatechange', () => {
|
||||||
if (pc.connectionState === 'failed' || pc.connectionState === 'disconnected') {
|
if (pc.connectionState === 'connected') {
|
||||||
|
const tracks = pc.getReceivers().map(receiver => receiver.track);
|
||||||
|
/** @type {HTMLVideoElement} */
|
||||||
|
const video2 = document.createElement('video');
|
||||||
|
video2.addEventListener('loadeddata', () => this.onpcvideo(video2), {once: true});
|
||||||
|
video2.srcObject = new MediaStream(tracks);
|
||||||
|
} else if (pc.connectionState === 'failed' || pc.connectionState === 'disconnected') {
|
||||||
pc.close(); // stop next events
|
pc.close(); // stop next events
|
||||||
|
|
||||||
this.pcState = WebSocket.CLOSED;
|
this.pcState = WebSocket.CLOSED;
|
||||||
@@ -521,14 +521,8 @@ export class VideoRTC extends HTMLElement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Safari doesn't support "offerToReceiveVideo"
|
this.createOffer(pc).then(offer => {
|
||||||
pc.addTransceiver('video', {direction: 'recvonly'});
|
this.send({type: 'webrtc/offer', value: offer.sdp});
|
||||||
pc.addTransceiver('audio', {direction: 'recvonly'});
|
|
||||||
|
|
||||||
pc.createOffer().then(offer => {
|
|
||||||
pc.setLocalDescription(offer).then(() => {
|
|
||||||
this.send({type: 'webrtc/offer', value: offer.sdp});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pcState = WebSocket.CONNECTING;
|
this.pcState = WebSocket.CONNECTING;
|
||||||
@@ -536,31 +530,51 @@ export class VideoRTC extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ev {Event}
|
* @param pc {RTCPeerConnection}
|
||||||
|
* @return {Promise<RTCSessionDescriptionInit>}
|
||||||
*/
|
*/
|
||||||
onpcvideo(ev) {
|
async createOffer(pc) {
|
||||||
if (!this.pc) return;
|
try {
|
||||||
|
if (this.media.indexOf('microphone') >= 0) {
|
||||||
|
const media = await navigator.mediaDevices.getUserMedia({audio: true});
|
||||||
|
media.getTracks().forEach(track => {
|
||||||
|
pc.addTransceiver(track, {direction: 'sendonly'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
|
||||||
/** @type {HTMLVideoElement} */
|
for (const kind of ['video', 'audio']) {
|
||||||
const video2 = ev.target;
|
if (this.media.indexOf(kind) >= 0) {
|
||||||
const state = this.pc.connectionState;
|
pc.addTransceiver(kind, {direction: 'recvonly'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Firefox doesn't support pc.connectionState
|
const offer = await pc.createOffer();
|
||||||
if (state === 'connected' || state === 'connecting' || !state) {
|
await pc.setLocalDescription(offer);
|
||||||
|
return offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param video2 {HTMLVideoElement}
|
||||||
|
*/
|
||||||
|
onpcvideo(video2) {
|
||||||
|
if (this.pc) {
|
||||||
// Video+Audio > Video, H265 > H264, Video > Audio, WebRTC > MSE
|
// Video+Audio > Video, H265 > H264, Video > Audio, WebRTC > MSE
|
||||||
let rtcPriority = 0, msePriority = 0;
|
let rtcPriority = 0, msePriority = 0;
|
||||||
|
|
||||||
/** @type {MediaStream} */
|
/** @type {MediaStream} */
|
||||||
const ms = video2.srcObject;
|
const stream = video2.srcObject;
|
||||||
if (ms.getVideoTracks().length > 0) rtcPriority += 0x220;
|
if (stream.getVideoTracks().length > 0) rtcPriority += 0x220;
|
||||||
if (ms.getAudioTracks().length > 0) rtcPriority += 0x102;
|
if (stream.getAudioTracks().length > 0) rtcPriority += 0x102;
|
||||||
|
|
||||||
if (this.mseCodecs.indexOf('hvc1.') >= 0) msePriority += 0x230;
|
if (this.mseCodecs.indexOf('hvc1.') >= 0) msePriority += 0x230;
|
||||||
if (this.mseCodecs.indexOf('avc1.') >= 0) msePriority += 0x210;
|
if (this.mseCodecs.indexOf('avc1.') >= 0) msePriority += 0x210;
|
||||||
if (this.mseCodecs.indexOf('mp4a.') >= 0) msePriority += 0x101;
|
if (this.mseCodecs.indexOf('mp4a.') >= 0) msePriority += 0x101;
|
||||||
|
|
||||||
if (rtcPriority >= msePriority) {
|
if (rtcPriority >= msePriority) {
|
||||||
this.video.srcObject = ms;
|
this.video.srcObject = stream;
|
||||||
this.play();
|
this.play();
|
||||||
|
|
||||||
this.pcState = WebSocket.OPEN;
|
this.pcState = WebSocket.OPEN;
|
||||||
|
|||||||
Reference in New Issue
Block a user