Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update srs.sdk.js to support whip and whep #7

Merged
merged 4 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions srs-player/public/class-srs-player-public.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public function embed_player_handler($atts = [], $content = null, $tag = '') {
if (empty($q['url']) && empty($q['src'])) {
return __('Please specify the url of stream', 'srs-player');
}
// convert & to &.
$q['url'] = html_entity_decode($q['url']);

$url = $q['url'];
if (empty($url)) $url = $q['src'];
$id = 'srs-player-' . $this->random_str(32);
Expand Down Expand Up @@ -91,6 +94,9 @@ public function embed_publisher_handler($atts = [], $content = null, $tag = '')
if (empty($q['url']) && empty($q['src'])) {
return __('Please specify the url of stream', 'srs-publisher');
}
// convert & to &.
$q['url'] = html_entity_decode($q['url']);

$url = $q['url'];
if (empty($url)) $url = $q['src'];
$id = 'srs-publisher-' . $this->random_str(32);
Expand Down
14 changes: 14 additions & 0 deletions srs-player/public/js/srs.player.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@
return console.log(`Play by srs.sdk.js for ${self.url}`);
}

if (self.url.indexOf('whep') > 0) {
const sdk = new SrsRtcWhipWhepAsync();
self.dom.prop('srcObject', sdk.stream);
sdk.play(self.url);
return console.log(`Play by srs.sdk.js for ${self.url}`);
}

console.error(`URL is not supported:${self.url}`);
};

Expand Down Expand Up @@ -78,6 +85,13 @@
return console.log(`Publish by srs.sdk.js for ${self.url}`);
}

if (self.url.indexOf('whip') > 0) {
const sdk = new SrsRtcWhipWhepAsync();
self.dom.prop('srcObject', sdk.stream);
sdk.publish(self.url);
return console.log(`Publish by srs.sdk.js for ${self.url}`);
}

console.error(`URL is not supported:${self.url}`);
};

Expand Down
185 changes: 179 additions & 6 deletions srs-player/public/js/srs.sdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ function SrsRtcPublisherAsync() {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "sendonly"});
self.pc.addTransceiver("video", {direction: "sendonly"});
//self.pc.addTransceiver("video", {direction: "sendonly"});
Copy link
Member

@chundonglinlin chundonglinlin Dec 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excessive comments?

TRANS_BY_GPT4

//self.pc.addTransceiver("audio", {direction: "sendonly"});

if (!navigator.mediaDevices && window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
throw new SrsError('HttpsRequiredError', `Please use HTTPS or localhost to publish, read https://github.com/ossrs/srs/issues/2762#issuecomment-983147576`);
Expand Down Expand Up @@ -81,7 +83,7 @@ function SrsRtcPublisherAsync() {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status !== 200) return reject(xhr);
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
const data = JSON.parse(xhr.responseText);
console.log("Got answer: ", data);
return data.code ? reject(xhr) : resolve(data);
Expand Down Expand Up @@ -132,14 +134,14 @@ function SrsRtcPublisherAsync() {
api += '/';
}

apiUrl = schema + '//' + urlObject.server + ':' + port + api;
var apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
apiUrl = apiUrl.replace(api + '&', api + '?');

var streamUrl = urlObject.url;

Expand Down Expand Up @@ -300,6 +302,8 @@ function SrsRtcPlayerAsync() {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "recvonly"});
self.pc.addTransceiver("video", {direction: "recvonly"});
//self.pc.addTransceiver("video", {direction: "recvonly"});
Copy link
Member

@chundonglinlin chundonglinlin Dec 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

多余的注释?

//self.pc.addTransceiver("audio", {direction: "recvonly"});

var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
Expand All @@ -314,7 +318,7 @@ function SrsRtcPlayerAsync() {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status !== 200) return reject(xhr);
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
const data = JSON.parse(xhr.responseText);
console.log("Got answer: ", data);
return data.code ? reject(xhr) : resolve(data);
Expand Down Expand Up @@ -365,14 +369,14 @@ function SrsRtcPlayerAsync() {
api += '/';
}

apiUrl = schema + '//' + urlObject.server + ':' + port + api;
var apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
apiUrl = apiUrl.replace(api + '&', api + '?');

var streamUrl = urlObject.url;

Expand Down Expand Up @@ -506,3 +510,172 @@ function SrsRtcPlayerAsync() {
return self;
}

// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-awat-prmise based SRS RTC Publisher by WHIP.
function SrsRtcWhipWhepAsync() {
var self = {};

// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
self.constraints = {
audio: true,
video: {
width: {ideal: 320, max: 576}
}
};

// See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
// @url The WebRTC url to publish with, for example:
// http://localhost:1985/rtc/v1/whip/?app=live&stream=livestream
self.publish = async function (url) {
if (url.indexOf('/whip/') === -1) throw new Error(`invalid WHIP url ${url}`);

self.pc.addTransceiver("audio", {direction: "sendonly"});
self.pc.addTransceiver("video", {direction: "sendonly"});

if (!navigator.mediaDevices && window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
throw new SrsError('HttpsRequiredError', `Please use HTTPS or localhost to publish, read https://github.com/ossrs/srs/issues/2762#issuecomment-983147576`);
}
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);

// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);

// Notify about local track when stream is ok.
self.ontrack && self.ontrack({track: track});
});

var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
const answer = await new Promise(function (resolve, reject) {
console.log("Generated offer: ", offer);

const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
const data = xhr.responseText;
console.log("Got answer: ", data);
return data.code ? reject(xhr) : resolve(data);
}
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/sdp');
xhr.send(offer.sdp);
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: answer})
);

return self.__internal.parseId(url, offer.sdp, answer);
};

// See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
// @url The WebRTC url to play with, for example:
// http://localhost:1985/rtc/v1/whep/?app=live&stream=livestream
self.play = async function(url) {
if (url.indexOf('/whip-play/') === -1 && url.indexOf('/whep/') === -1) throw new Error(`invalid WHEP url ${url}`);

self.pc.addTransceiver("audio", {direction: "recvonly"});
self.pc.addTransceiver("video", {direction: "recvonly"});

var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
const answer = await new Promise(function(resolve, reject) {
console.log("Generated offer: ", offer);

const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
const data = xhr.responseText;
console.log("Got answer: ", data);
return data.code ? reject(xhr) : resolve(data);
}
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/sdp');
xhr.send(offer.sdp);
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: answer})
);

return self.__internal.parseId(url, offer.sdp, answer);
};

// Close the publisher.
self.close = function () {
self.pc && self.pc.close();
self.pc = null;
};

// The callback when got local stream.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
self.ontrack = function (event) {
// Add track to stream of SDK.
self.stream.addTrack(event.track);
};

self.pc = new RTCPeerConnection(null);

// To keep api consistent between player and publisher.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
// @see https://webrtc.org/getting-started/media-devices
self.stream = new MediaStream();

// Internal APIs.
self.__internal = {
parseId: (url, offer, answer) => {
let sessionid = offer.substr(offer.indexOf('a=ice-ufrag:') + 'a=ice-ufrag:'.length);
sessionid = sessionid.substr(0, sessionid.indexOf('\n') - 1) + ':';
sessionid += answer.substr(answer.indexOf('a=ice-ufrag:') + 'a=ice-ufrag:'.length);
sessionid = sessionid.substr(0, sessionid.indexOf('\n'));

const a = document.createElement("a");
a.href = url;
return {
sessionid: sessionid, // Should be ice-ufrag of answer:offer.
simulator: a.protocol + '//' + a.host + '/rtc/v1/nack/',
};
},
};

// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
self.pc.ontrack = function(event) {
if (self.ontrack) {
self.ontrack(event);
}
};

return self;
}

// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
function SrsRtcFormatSenders(senders, kind) {
var codecs = [];
senders.forEach(function (sender) {
var params = sender.getParameters();
params && params.codecs && params.codecs.forEach(function(c) {
if (kind && sender.track.kind !== kind) {
return;
}

if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
return;
}

var s = '';

s += c.mimeType.replace('audio/', '').replace('video/', '');
s += ', ' + c.clockRate + 'HZ';
if (sender.track.kind === "audio") {
s += ', channels: ' + c.channels;
}
s += ', pt: ' + c.payloadType;

codecs.push(s);
});
});
return codecs.join(", ");
}