mirror of
https://github.com/ossrs/srs.git
synced 2025-02-13 03:41:55 +00:00
SquashSRS4: Support av1 for Chrome M90 enabled it.
This commit is contained in:
parent
84e649be8b
commit
0b62216999
14 changed files with 980 additions and 965 deletions
|
@ -163,6 +163,7 @@ Other important wiki:
|
|||
|
||||
## V4 changes
|
||||
|
||||
* v4.0, 2021-04-29, RTC: Support av1 for Chrome M90. 4.0.91
|
||||
* v4.0, 2021-04-24, Change push-RTSP as deprecated feature.
|
||||
* v4.0, 2021-04-24, Player: Change the default from RTMP to HTTP-FLV.
|
||||
* v4.0, 2021-04-24, Disable CherryPy by --cherrypy=off. 4.0.90
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// to query the swf anti cache.
|
||||
function srs_get_version_code() { return "1.33"; }
|
||||
|
@ -12,10 +9,6 @@ function srs_get_player_modal() { return 740; }
|
|||
function srs_get_player_width() { return srs_get_player_modal() - 30; }
|
||||
function srs_get_player_height() { return srs_get_player_width() * 9 / 19; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* update the navigator, add same query string.
|
||||
*/
|
||||
|
@ -93,34 +86,10 @@ function build_default_flv_url() {
|
|||
|
||||
return uri;
|
||||
}
|
||||
// for the bandwidth tool to init page
|
||||
function build_default_bandwidth_rtmp_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var schema = 'rtmp';
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var port = (!query.port)? 1935:query.port;
|
||||
var vhost = "bandcheck.srs.com";
|
||||
var app = (!query.app)? "app":query.app;
|
||||
var key = (!query.key)? "35c9b402c12a7246868752e2878f7e0e":query.key;
|
||||
|
||||
var uri = schema + "://" + server;
|
||||
if (!is_default_port(schema, port)) {
|
||||
uri += ":" + port;
|
||||
}
|
||||
uri += "/" + app + "?key=" + key + "&vhost=" + vhost;
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
function build_default_rtc_url(query) {
|
||||
// Use target to overwrite server, vhost and eip.
|
||||
console.log('?target=x.x.x.x to overwrite server, vhost and eip.');
|
||||
if (query.target) {
|
||||
query.server = query.vhost = query.eip = query.target;
|
||||
query.user_query.eip = query.target;
|
||||
delete query.target;
|
||||
}
|
||||
// The format for query string to overwrite configs of server.
|
||||
console.log('?eip=x.x.x.x to overwrite candidate. 覆盖服务器candidate(外网IP)配置');
|
||||
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
|
||||
|
@ -165,253 +134,3 @@ function srs_init_rtc(id, query) {
|
|||
update_nav();
|
||||
$(id).val(build_default_rtc_url(query));
|
||||
}
|
||||
// for bw to init url
|
||||
// url: scheme://host:port/path?query#fragment
|
||||
function srs_init_bwt(rtmp_url, hls_url) {
|
||||
update_nav();
|
||||
|
||||
if (rtmp_url) {
|
||||
$(rtmp_url).val(build_default_bandwidth_rtmp_url());
|
||||
}
|
||||
}
|
||||
|
||||
// check whether can republish
|
||||
function srs_can_republish() {
|
||||
var browser = get_browser_agents();
|
||||
|
||||
if (browser.Chrome || browser.Firefox) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.MSIE || browser.QQBrowser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// without default values set.
|
||||
function srs_initialize_codec_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
) {
|
||||
$(sl_cameras).empty();
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
$(sl_cameras).append("<option value='" + i + "'>" + cameras[i] + "</option");
|
||||
}
|
||||
// optional: select the except matches
|
||||
matchs = ["virtual"];
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (cameras[i].toLowerCase().indexOf(matchs[j]) == -1) {
|
||||
$(sl_cameras + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// optional: select the first matched.
|
||||
matchs = ["truevision", "integrated"];
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (cameras[i].toLowerCase().indexOf(matchs[j]) >= 0) {
|
||||
$(sl_cameras + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$(sl_microphones).empty();
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
$(sl_microphones).append("<option value='" + i + "'>" + microphones[i] + "</option");
|
||||
}
|
||||
// optional: select the except matches
|
||||
matchs = ["default"];
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (microphones[i].toLowerCase().indexOf(matchs[j]) == -1) {
|
||||
$(sl_microphones + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// optional: select the first matched.
|
||||
matchs = ["realtek", "内置式麦克风"];
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (microphones[i].toLowerCase().indexOf(matchs[j]) >= 0) {
|
||||
$(sl_microphones + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$(sl_vcodec).empty();
|
||||
var vcodecs = ["h264", "vp6"];
|
||||
vcodecs = ["h264"]; // h264 only.
|
||||
for (var i = 0; i < vcodecs.length; i++) {
|
||||
$(sl_vcodec).append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_profile).empty();
|
||||
var profiles = ["baseline", "main"];
|
||||
for (var i = 0; i < profiles.length; i++) {
|
||||
$(sl_profile).append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_level).empty();
|
||||
var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
"2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
for (var i = 0; i < levels.length; i++) {
|
||||
$(sl_level).append("<option value='" + levels[i] + "'>" + levels[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_gop).empty();
|
||||
var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
"5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
for (var i = 0; i < gops.length; i++) {
|
||||
$(sl_gop).append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option");
|
||||
}
|
||||
|
||||
$(sl_size).empty();
|
||||
var sizes = ["176x144", "320x240", "352x240",
|
||||
"352x288", "480x360", "640x480", "720x480", "720x576", "800x600",
|
||||
"1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
for (i = 0; i < sizes.length; i++) {
|
||||
$(sl_size).append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_fps).empty();
|
||||
var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
for (i = 0; i < fpses.length; i++) {
|
||||
$(sl_fps).append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option");
|
||||
}
|
||||
|
||||
$(sl_bitrate).empty();
|
||||
var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
"950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
for (i = 0; i < bitrates.length; i++) {
|
||||
$(sl_bitrate).append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option");
|
||||
}
|
||||
|
||||
$(sl_acodec).empty();
|
||||
var bitrates = ["speex", "nellymoser", "pcma", "pcmu"];
|
||||
for (i = 0; i < bitrates.length; i++) {
|
||||
$(sl_acodec).append("<option value='" + bitrates[i] + "'>" + bitrates[i] + "</option");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* when publisher ready, init the page elements.
|
||||
*/
|
||||
function srs_publisher_initialize_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
) {
|
||||
srs_initialize_codec_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
);
|
||||
|
||||
//var profiles = ["baseline", "main"];
|
||||
$(sl_profile + " option[value='main']").attr("selected", true);
|
||||
|
||||
//var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
// "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
$(sl_level + " option[value='4.1']").attr("selected", true);
|
||||
|
||||
//var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
// "5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
$(sl_gop + " option[value='10']").attr("selected", true);
|
||||
|
||||
//var sizes = ["176x144", "320x240", "352x240",
|
||||
// "352x288", "480x360", "640x480", "720x480", "720x576", "800x600",
|
||||
// "1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
$(sl_size + " option[value='640x480']").attr("selected", true);
|
||||
|
||||
//var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
$(sl_fps + " option[value='20']").attr("selected", true);
|
||||
|
||||
//var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
// "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
$(sl_bitrate + " option[value='500']").attr("selected", true);
|
||||
|
||||
// speex
|
||||
$(sl_acodec + " option[value='speex']").attr("selected", true);
|
||||
}
|
||||
/**
|
||||
* for chat, use low latecy settings.
|
||||
*/
|
||||
function srs_chat_initialize_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
) {
|
||||
srs_initialize_codec_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
);
|
||||
|
||||
//var profiles = ["baseline", "main"];
|
||||
$(sl_profile + " option[value='baseline']").attr("selected", true);
|
||||
|
||||
//var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
// "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
$(sl_level + " option[value='3.1']").attr("selected", true);
|
||||
|
||||
//var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
// "5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
$(sl_gop + " option[value='2']").attr("selected", true);
|
||||
|
||||
//var sizes = ["176x144", "320x240", "352x240",
|
||||
// "352x288", "480x360", "640x480", "720x480", "720x576", "800x600",
|
||||
// "1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
$(sl_size + " option[value='480x360']").attr("selected", true);
|
||||
|
||||
//var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
$(sl_fps + " option[value='15']").attr("selected", true);
|
||||
|
||||
//var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
// "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
$(sl_bitrate + " option[value='350']").attr("selected", true);
|
||||
|
||||
// speex
|
||||
$(sl_acodec + " option[value='speex']").attr("selected", true);
|
||||
}
|
||||
/**
|
||||
* get the vcodec and acodec.
|
||||
*/
|
||||
function srs_publiser_get_codec(
|
||||
vcodec, acodec,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
) {
|
||||
acodec.codec = $(sl_acodec).val();
|
||||
acodec.device_code = $(sl_microphones).val();
|
||||
acodec.device_name = $(sl_microphones).text();
|
||||
|
||||
vcodec.device_code = $(sl_cameras).find("option:selected").val();
|
||||
vcodec.device_name = $(sl_cameras).find("option:selected").text();
|
||||
|
||||
vcodec.codec = $(sl_vcodec).find("option:selected").val();
|
||||
vcodec.profile = $(sl_profile).find("option:selected").val();
|
||||
vcodec.level = $(sl_level).find("option:selected").val();
|
||||
vcodec.fps = $(sl_fps).find("option:selected").val();
|
||||
vcodec.gop = $(sl_gop).find("option:selected").val();
|
||||
vcodec.size = $(sl_size).find("option:selected").val();
|
||||
vcodec.bitrate = $(sl_bitrate).find("option:selected").val();
|
||||
}
|
||||
|
|
501
trunk/research/players/js/srs.sdk.js
Normal file
501
trunk/research/players/js/srs.sdk.js
Normal file
|
@ -0,0 +1,501 @@
|
|||
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2021 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
|
||||
// Async-awat-prmise based SRS RTC Publisher.
|
||||
function SrsRtcPublisherAsync() {
|
||||
var self = {};
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
// @url The WebRTC url to play with, for example:
|
||||
// webrtc://r.ossrs.net/live/livestream
|
||||
// or specifies the API port:
|
||||
// webrtc://r.ossrs.net:11985/live/livestream
|
||||
// or autostart the publish:
|
||||
// webrtc://r.ossrs.net/live/livestream?autostart=true
|
||||
// or change the app from live to myapp:
|
||||
// webrtc://r.ossrs.net:11985/myapp/livestream
|
||||
// or change the stream from livestream to mystream:
|
||||
// webrtc://r.ossrs.net:11985/live/mystream
|
||||
// or set the api server to myapi.domain.com:
|
||||
// webrtc://myapi.domain.com/live/livestream
|
||||
// or set the candidate(ip) of answer:
|
||||
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
|
||||
// or force to access https API:
|
||||
// webrtc://r.ossrs.net/live/livestream?schema=https
|
||||
// or use plaintext, without SRTP:
|
||||
// webrtc://r.ossrs.net/live/livestream?encrypt=false
|
||||
// or any other information, will pass-by in the query:
|
||||
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
|
||||
// webrtc://r.ossrs.net/live/livestream?token=xxx
|
||||
self.publish = async function (url) {
|
||||
var conf = self.__internal.prepareUrl(url);
|
||||
self.pc.addTransceiver("audio", {direction: "sendonly"});
|
||||
self.pc.addTransceiver("video", {direction: "sendonly"});
|
||||
|
||||
var stream = await navigator.mediaDevices.getUserMedia(
|
||||
{audio: true, video: {height: {max: 320}}}
|
||||
);
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||
stream.getTracks().forEach(function (track) {
|
||||
self.pc.addTrack(track);
|
||||
});
|
||||
|
||||
var offer = await self.pc.createOffer();
|
||||
await self.pc.setLocalDescription(offer);
|
||||
var session = await new Promise(function (resolve, reject) {
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var data = {
|
||||
api: conf.apiUrl, streamurl: conf.streamUrl, clientip: null, sdp: offer.sdp
|
||||
};
|
||||
console.log("Generated offer: ", data);
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
|
||||
contentType: 'application/json', dataType: 'json'
|
||||
}).done(function (data) {
|
||||
console.log("Got answer: ", data);
|
||||
if (data.code) {
|
||||
reject(data);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
}).fail(function (reason) {
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
await self.pc.setRemoteDescription(
|
||||
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
|
||||
);
|
||||
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
|
||||
|
||||
// Notify about local stream when success.
|
||||
self.onaddstream && self.onaddstream({stream: stream});
|
||||
|
||||
return session;
|
||||
};
|
||||
|
||||
// Close the publisher.
|
||||
self.close = function () {
|
||||
self.pc.close();
|
||||
self.pc = null;
|
||||
};
|
||||
|
||||
// The callback when got local stream.
|
||||
self.onaddstream = function (event) {
|
||||
};
|
||||
|
||||
// Internal APIs.
|
||||
self.__internal = {
|
||||
defaultPath: '/rtc/v1/publish/',
|
||||
prepareUrl: function (webrtcUrl) {
|
||||
var urlObject = self.__internal.parse(webrtcUrl);
|
||||
|
||||
// If user specifies the schema, use it as API schema.
|
||||
var schema = urlObject.user_query.schema;
|
||||
schema = schema ? schema + ':' : window.location.protocol;
|
||||
|
||||
var port = urlObject.port || 1985;
|
||||
if (schema === 'https:') {
|
||||
port = urlObject.port || 443;
|
||||
}
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var api = urlObject.user_query.play || self.__internal.defaultPath;
|
||||
if (api.lastIndexOf('/') !== api.length - 1) {
|
||||
api += '/';
|
||||
}
|
||||
|
||||
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 + '?');
|
||||
|
||||
var streamUrl = urlObject.url;
|
||||
|
||||
return {apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port};
|
||||
},
|
||||
parse: function (url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = url.replace("rtmp://", "http://")
|
||||
.replace("webrtc://", "http://")
|
||||
.replace("rtc://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
|
||||
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.substr(app.indexOf("?"));
|
||||
app = app.substr(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.substr(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when vhost equals to server, and server is ip,
|
||||
// the vhost is __defaultVhost__
|
||||
if (a.hostname === vhost) {
|
||||
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||
if (re.test(a.hostname)) {
|
||||
vhost = "__defaultVhost__";
|
||||
}
|
||||
}
|
||||
|
||||
// parse the schema
|
||||
var schema = "rtmp";
|
||||
if (url.indexOf("://") > 0) {
|
||||
schema = url.substr(0, url.indexOf("://"));
|
||||
}
|
||||
|
||||
var port = a.port;
|
||||
if (!port) {
|
||||
if (schema === 'http') {
|
||||
port = 80;
|
||||
} else if (schema === 'https') {
|
||||
port = 443;
|
||||
} else if (schema === 'rtmp') {
|
||||
port = 1935;
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
url: url,
|
||||
schema: schema,
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
self.__internal.fill_query(a.search, ret);
|
||||
|
||||
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||
if (!ret.port) {
|
||||
if (schema === 'webrtc' || schema === 'rtc') {
|
||||
if (ret.user_query.schema === 'https') {
|
||||
ret.port = 443;
|
||||
} else if (window.location.href.indexOf('https://') === 0) {
|
||||
ret.port = 443;
|
||||
} else {
|
||||
// For WebRTC, SRS use 1985 as default API port.
|
||||
ret.port = 1985;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
fill_query: function (query_string, obj) {
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
if (query_string.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split again for angularjs.
|
||||
if (query_string.indexOf("?") >= 0) {
|
||||
query_string = query_string.split("?")[1];
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
var elem = queries[i];
|
||||
|
||||
var query = elem.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
obj.user_query[query[0]] = query[1];
|
||||
}
|
||||
|
||||
// alias domain for vhost.
|
||||
if (obj.domain) {
|
||||
obj.vhost = obj.domain;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.pc = new RTCPeerConnection(null);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
|
||||
// Async-await-promise based SRS RTC Player.
|
||||
function SrsRtcPlayerAsync() {
|
||||
var self = {};
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
// @url The WebRTC url to play with, for example:
|
||||
// webrtc://r.ossrs.net/live/livestream
|
||||
// or specifies the API port:
|
||||
// webrtc://r.ossrs.net:11985/live/livestream
|
||||
// or autostart the play:
|
||||
// webrtc://r.ossrs.net/live/livestream?autostart=true
|
||||
// or change the app from live to myapp:
|
||||
// webrtc://r.ossrs.net:11985/myapp/livestream
|
||||
// or change the stream from livestream to mystream:
|
||||
// webrtc://r.ossrs.net:11985/live/mystream
|
||||
// or set the api server to myapi.domain.com:
|
||||
// webrtc://myapi.domain.com/live/livestream
|
||||
// or set the candidate(ip) of answer:
|
||||
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
|
||||
// or force to access https API:
|
||||
// webrtc://r.ossrs.net/live/livestream?schema=https
|
||||
// or use plaintext, without SRTP:
|
||||
// webrtc://r.ossrs.net/live/livestream?encrypt=false
|
||||
// or any other information, will pass-by in the query:
|
||||
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
|
||||
// webrtc://r.ossrs.net/live/livestream?token=xxx
|
||||
self.play = async function(url) {
|
||||
var conf = self.__internal.prepareUrl(url);
|
||||
self.pc.addTransceiver("audio", {direction: "recvonly"});
|
||||
self.pc.addTransceiver("video", {direction: "recvonly"});
|
||||
|
||||
var offer = await self.pc.createOffer();
|
||||
await self.pc.setLocalDescription(offer);
|
||||
var session = await new Promise(function(resolve, reject) {
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var data = {
|
||||
api: conf.apiUrl, streamurl: conf.streamUrl, clientip: null, sdp: offer.sdp
|
||||
};
|
||||
console.log("Generated offer: ", data);
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
|
||||
contentType:'application/json', dataType: 'json'
|
||||
}).done(function(data) {
|
||||
console.log("Got answer: ", data);
|
||||
if (data.code) {
|
||||
reject(data); return;
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
}).fail(function(reason){
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
await self.pc.setRemoteDescription(
|
||||
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
|
||||
);
|
||||
return session;
|
||||
};
|
||||
|
||||
// Close the player.
|
||||
self.close = function() {
|
||||
self.pc.close();
|
||||
self.pc = null;
|
||||
};
|
||||
|
||||
// The callback when got remote stream.
|
||||
self.onaddstream = function (event) {};
|
||||
|
||||
// Internal APIs.
|
||||
self.__internal = {
|
||||
defaultPath: '/rtc/v1/play/',
|
||||
prepareUrl: function (webrtcUrl) {
|
||||
var urlObject = self.__internal.parse(webrtcUrl);
|
||||
|
||||
// If user specifies the schema, use it as API schema.
|
||||
var schema = urlObject.user_query.schema;
|
||||
schema = schema ? schema + ':' : window.location.protocol;
|
||||
|
||||
var port = urlObject.port || 1985;
|
||||
if (schema === 'https:') {
|
||||
port = urlObject.port || 443;
|
||||
}
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var api = urlObject.user_query.play || self.__internal.defaultPath;
|
||||
if (api.lastIndexOf('/') !== api.length - 1) {
|
||||
api += '/';
|
||||
}
|
||||
|
||||
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 + '?');
|
||||
|
||||
var streamUrl = urlObject.url;
|
||||
|
||||
return {apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port};
|
||||
},
|
||||
parse: function (url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = url.replace("rtmp://", "http://")
|
||||
.replace("webrtc://", "http://")
|
||||
.replace("rtc://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
|
||||
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.substr(app.indexOf("?"));
|
||||
app = app.substr(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.substr(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when vhost equals to server, and server is ip,
|
||||
// the vhost is __defaultVhost__
|
||||
if (a.hostname === vhost) {
|
||||
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||
if (re.test(a.hostname)) {
|
||||
vhost = "__defaultVhost__";
|
||||
}
|
||||
}
|
||||
|
||||
// parse the schema
|
||||
var schema = "rtmp";
|
||||
if (url.indexOf("://") > 0) {
|
||||
schema = url.substr(0, url.indexOf("://"));
|
||||
}
|
||||
|
||||
var port = a.port;
|
||||
if (!port) {
|
||||
if (schema === 'http') {
|
||||
port = 80;
|
||||
} else if (schema === 'https') {
|
||||
port = 443;
|
||||
} else if (schema === 'rtmp') {
|
||||
port = 1935;
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
url: url,
|
||||
schema: schema,
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
self.__internal.fill_query(a.search, ret);
|
||||
|
||||
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||
if (!ret.port) {
|
||||
if (schema === 'webrtc' || schema === 'rtc') {
|
||||
if (ret.user_query.schema === 'https') {
|
||||
ret.port = 443;
|
||||
} else if (window.location.href.indexOf('https://') === 0) {
|
||||
ret.port = 443;
|
||||
} else {
|
||||
// For WebRTC, SRS use 1985 as default API port.
|
||||
ret.port = 1985;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
fill_query: function (query_string, obj) {
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
if (query_string.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split again for angularjs.
|
||||
if (query_string.indexOf("?") >= 0) {
|
||||
query_string = query_string.split("?")[1];
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
var elem = queries[i];
|
||||
|
||||
var query = elem.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
obj.user_query[query[0]] = query[1];
|
||||
}
|
||||
|
||||
// alias domain for vhost.
|
||||
if (obj.domain) {
|
||||
obj.vhost = obj.domain;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.pc = new RTCPeerConnection(null);
|
||||
self.pc.onaddstream = function (event) {
|
||||
if (self.onaddstream) {
|
||||
self.onaddstream(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) {
|
||||
sender.getParameters().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(", ");
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
|
||||
<script type="text/javascript" src="js/srs.sdk.js"></script>
|
||||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
</head>
|
||||
|
@ -64,222 +65,6 @@
|
|||
</div>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
// Async-await-promise based SRS RTC Player.
|
||||
function SrsRtcPlayerAsync() {
|
||||
var self = {};
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
// @url The WebRTC url to play with, for example:
|
||||
// webrtc://r.ossrs.net/live/livestream
|
||||
// or specifies the API port:
|
||||
// webrtc://r.ossrs.net:11985/live/livestream
|
||||
// or autostart the play:
|
||||
// webrtc://r.ossrs.net/live/livestream?autostart=true
|
||||
// or change the app from live to myapp:
|
||||
// webrtc://r.ossrs.net:11985/myapp/livestream
|
||||
// or change the stream from livestream to mystream:
|
||||
// webrtc://r.ossrs.net:11985/live/mystream
|
||||
// or set the api server to myapi.domain.com:
|
||||
// webrtc://myapi.domain.com/live/livestream
|
||||
// or set the candidate(ip) of answer:
|
||||
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
|
||||
// or force to access https API:
|
||||
// webrtc://r.ossrs.net/live/livestream?schema=https
|
||||
// or use plaintext, without SRTP:
|
||||
// webrtc://r.ossrs.net/live/livestream?encrypt=false
|
||||
// or any other information, will pass-by in the query:
|
||||
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
|
||||
// webrtc://r.ossrs.net/live/livestream?token=xxx
|
||||
self.play = async function(url) {
|
||||
var conf = self.__internal.prepareUrl(url);
|
||||
self.pc.addTransceiver("audio", {direction: "recvonly"});
|
||||
self.pc.addTransceiver("video", {direction: "recvonly"});
|
||||
|
||||
var offer = await self.pc.createOffer();
|
||||
await self.pc.setLocalDescription(offer);
|
||||
var session = await new Promise(function(resolve, reject) {
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var data = {
|
||||
api: conf.apiUrl, streamurl: conf.streamUrl, clientip: null, sdp: offer.sdp
|
||||
};
|
||||
console.log("Generated offer: ", data);
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
|
||||
contentType:'application/json', dataType: 'json'
|
||||
}).done(function(data) {
|
||||
console.log("Got answer: ", data);
|
||||
if (data.code) {
|
||||
reject(data); return;
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
}).fail(function(reason){
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
await self.pc.setRemoteDescription(
|
||||
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
|
||||
);
|
||||
return session;
|
||||
};
|
||||
|
||||
// Close the publisher.
|
||||
self.close = function() {
|
||||
self.pc.close();
|
||||
};
|
||||
|
||||
// The callback when got remote stream.
|
||||
self.onaddstream = function (event) {};
|
||||
|
||||
// Internal APIs.
|
||||
self.__internal = {
|
||||
defaultPath: '/rtc/v1/play/',
|
||||
prepareUrl: function (webrtcUrl) {
|
||||
var urlObject = self.__internal.parse(webrtcUrl);
|
||||
|
||||
// If user specifies the schema, use it as API schema.
|
||||
var schema = urlObject.user_query.schema;
|
||||
schema = schema ? schema + ':' : window.location.protocol;
|
||||
|
||||
var port = urlObject.port || 1985;
|
||||
if (schema === 'https:') {
|
||||
port = urlObject.port || 443;
|
||||
}
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var api = urlObject.user_query.play || self.__internal.defaultPath;
|
||||
if (api.lastIndexOf('/') !== api.length - 1) {
|
||||
api += '/';
|
||||
}
|
||||
|
||||
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 + '?');
|
||||
|
||||
var streamUrl = urlObject.url;
|
||||
|
||||
return {apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port};
|
||||
},
|
||||
parse: function (url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = url.replace("rtmp://", "http://")
|
||||
.replace("webrtc://", "http://")
|
||||
.replace("rtc://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
|
||||
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.substr(app.indexOf("?"));
|
||||
app = app.substr(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.substr(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when vhost equals to server, and server is ip,
|
||||
// the vhost is __defaultVhost__
|
||||
if (a.hostname === vhost) {
|
||||
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||
if (re.test(a.hostname)) {
|
||||
vhost = "__defaultVhost__";
|
||||
}
|
||||
}
|
||||
|
||||
// parse the schema
|
||||
var schema = "rtmp";
|
||||
if (url.indexOf("://") > 0) {
|
||||
schema = url.substr(0, url.indexOf("://"));
|
||||
}
|
||||
|
||||
var port = a.port;
|
||||
if (!port) {
|
||||
if (schema === 'http') {
|
||||
port = 80;
|
||||
} else if (schema === 'https') {
|
||||
port = 443;
|
||||
} else if (schema === 'rtmp') {
|
||||
port = 1935;
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
url: url,
|
||||
schema: schema,
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
self.__internal.fill_query(a.search, ret);
|
||||
|
||||
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||
if (!ret.port) {
|
||||
if (schema === 'webrtc' || schema === 'rtc') {
|
||||
if (ret.user_query.schema === 'https') {
|
||||
ret.port = 443;
|
||||
} else if (window.location.href.indexOf('https://') === 0) {
|
||||
ret.port = 443;
|
||||
} else {
|
||||
// For WebRTC, SRS use 1985 as default API port.
|
||||
ret.port = 1985;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
fill_query: function (query_string, obj) {
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
if (query_string.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split again for angularjs.
|
||||
if (query_string.indexOf("?") >= 0) {
|
||||
query_string = query_string.split("?")[1];
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
var elem = queries[i];
|
||||
|
||||
var query = elem.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
obj.user_query[query[0]] = query[1];
|
||||
}
|
||||
|
||||
// alias domain for vhost.
|
||||
if (obj.domain) {
|
||||
obj.vhost = obj.domain;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.pc = new RTCPeerConnection(null);
|
||||
self.pc.onaddstream = function (event) {
|
||||
if (self.onaddstream) {
|
||||
self.onaddstream(event);
|
||||
}
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
var sdk = null; // Global handler to do cleanup when replaying.
|
||||
var startPlay = function() {
|
||||
$('#rtc_media_player').show();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
|
||||
<script type="text/javascript" src="js/srs.sdk.js"></script>
|
||||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
</head>
|
||||
|
@ -54,6 +55,10 @@
|
|||
<label></label>
|
||||
SessionID: <span id='sessionid'></span>
|
||||
|
||||
<label></label>
|
||||
Audio: <span id='acodecs'></span><br/>
|
||||
Video: <span id='vcodecs'></span>
|
||||
|
||||
<label></label>
|
||||
Simulator: <a href='#' id='simulator-drop'>Drop</a>
|
||||
|
||||
|
@ -65,232 +70,6 @@
|
|||
<script type="text/javascript">
|
||||
var pc = null; // Global handler to do cleanup when replaying.
|
||||
$(function(){
|
||||
// Async-awat-prmise based SRS RTC Publisher.
|
||||
function SrsRtcPublisherAsync() {
|
||||
var self = {};
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
// @url The WebRTC url to play with, for example:
|
||||
// webrtc://r.ossrs.net/live/livestream
|
||||
// or specifies the API port:
|
||||
// webrtc://r.ossrs.net:11985/live/livestream
|
||||
// or autostart the publish:
|
||||
// webrtc://r.ossrs.net/live/livestream?autostart=true
|
||||
// or change the app from live to myapp:
|
||||
// webrtc://r.ossrs.net:11985/myapp/livestream
|
||||
// or change the stream from livestream to mystream:
|
||||
// webrtc://r.ossrs.net:11985/live/mystream
|
||||
// or set the api server to myapi.domain.com:
|
||||
// webrtc://myapi.domain.com/live/livestream
|
||||
// or set the candidate(ip) of answer:
|
||||
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
|
||||
// or force to access https API:
|
||||
// webrtc://r.ossrs.net/live/livestream?schema=https
|
||||
// or use plaintext, without SRTP:
|
||||
// webrtc://r.ossrs.net/live/livestream?encrypt=false
|
||||
// or any other information, will pass-by in the query:
|
||||
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
|
||||
// webrtc://r.ossrs.net/live/livestream?token=xxx
|
||||
self.publish = async function (url) {
|
||||
var conf = self.__internal.prepareUrl(url);
|
||||
self.pc.addTransceiver("audio", {direction: "sendonly"});
|
||||
self.pc.addTransceiver("video", {direction: "sendonly"});
|
||||
|
||||
var stream = await navigator.mediaDevices.getUserMedia(
|
||||
{audio: true, video: {height: {max: 320}}}
|
||||
);
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||
stream.getTracks().forEach(function (track) {
|
||||
self.pc.addTrack(track);
|
||||
});
|
||||
|
||||
var offer = await self.pc.createOffer();
|
||||
await self.pc.setLocalDescription(offer);
|
||||
var session = await new Promise(function (resolve, reject) {
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var data = {
|
||||
api: conf.apiUrl, streamurl: conf.streamUrl, clientip: null, sdp: offer.sdp
|
||||
};
|
||||
console.log("Generated offer: ", data);
|
||||
|
||||
$.ajax({
|
||||
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
|
||||
contentType: 'application/json', dataType: 'json'
|
||||
}).done(function (data) {
|
||||
console.log("Got answer: ", data);
|
||||
if (data.code) {
|
||||
reject(data);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
}).fail(function (reason) {
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
await self.pc.setRemoteDescription(
|
||||
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
|
||||
);
|
||||
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
|
||||
|
||||
// Notify about local stream when success.
|
||||
self.onaddstream && self.onaddstream({stream: stream});
|
||||
|
||||
return session;
|
||||
};
|
||||
|
||||
// Close the publisher.
|
||||
self.close = function () {
|
||||
self.pc.close();
|
||||
};
|
||||
|
||||
// The callback when got local stream.
|
||||
self.onaddstream = function (event) {
|
||||
};
|
||||
|
||||
// Internal APIs.
|
||||
self.__internal = {
|
||||
defaultPath: '/rtc/v1/publish/',
|
||||
prepareUrl: function (webrtcUrl) {
|
||||
var urlObject = self.__internal.parse(webrtcUrl);
|
||||
|
||||
// If user specifies the schema, use it as API schema.
|
||||
var schema = urlObject.user_query.schema;
|
||||
schema = schema ? schema + ':' : window.location.protocol;
|
||||
|
||||
var port = urlObject.port || 1985;
|
||||
if (schema === 'https:') {
|
||||
port = urlObject.port || 443;
|
||||
}
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var api = urlObject.user_query.play || self.__internal.defaultPath;
|
||||
if (api.lastIndexOf('/') !== api.length - 1) {
|
||||
api += '/';
|
||||
}
|
||||
|
||||
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 + '?');
|
||||
|
||||
var streamUrl = urlObject.url;
|
||||
|
||||
return {apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port};
|
||||
},
|
||||
parse: function (url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = url.replace("rtmp://", "http://")
|
||||
.replace("webrtc://", "http://")
|
||||
.replace("rtc://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
|
||||
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.substr(app.indexOf("?"));
|
||||
app = app.substr(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.substr(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when vhost equals to server, and server is ip,
|
||||
// the vhost is __defaultVhost__
|
||||
if (a.hostname === vhost) {
|
||||
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||
if (re.test(a.hostname)) {
|
||||
vhost = "__defaultVhost__";
|
||||
}
|
||||
}
|
||||
|
||||
// parse the schema
|
||||
var schema = "rtmp";
|
||||
if (url.indexOf("://") > 0) {
|
||||
schema = url.substr(0, url.indexOf("://"));
|
||||
}
|
||||
|
||||
var port = a.port;
|
||||
if (!port) {
|
||||
if (schema === 'http') {
|
||||
port = 80;
|
||||
} else if (schema === 'https') {
|
||||
port = 443;
|
||||
} else if (schema === 'rtmp') {
|
||||
port = 1935;
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
url: url,
|
||||
schema: schema,
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
self.__internal.fill_query(a.search, ret);
|
||||
|
||||
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||
if (!ret.port) {
|
||||
if (schema === 'webrtc' || schema === 'rtc') {
|
||||
if (ret.user_query.schema === 'https') {
|
||||
ret.port = 443;
|
||||
} else if (window.location.href.indexOf('https://') === 0) {
|
||||
ret.port = 443;
|
||||
} else {
|
||||
// For WebRTC, SRS use 1985 as default API port.
|
||||
ret.port = 1985;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
fill_query: function (query_string, obj) {
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
if (query_string.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split again for angularjs.
|
||||
if (query_string.indexOf("?") >= 0) {
|
||||
query_string = query_string.split("?")[1];
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
var elem = queries[i];
|
||||
|
||||
var query = elem.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
obj.user_query[query[0]] = query[1];
|
||||
}
|
||||
|
||||
// alias domain for vhost.
|
||||
if (obj.domain) {
|
||||
obj.vhost = obj.domain;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.pc = new RTCPeerConnection(null);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
var sdk = null; // Global handler to do cleanup when republishing.
|
||||
var startPublish = function() {
|
||||
$('#rtc_media_player').show();
|
||||
|
@ -306,6 +85,14 @@
|
|||
$('#rtc_media_player').prop('srcObject', event.stream);
|
||||
};
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
|
||||
sdk.pc.onicegatheringstatechange = function (event) {
|
||||
if (sdk.pc.iceGatheringState === "complete") {
|
||||
$('#acodecs').html(SrsRtcFormatSenders(sdk.pc.getSenders(), "audio"));
|
||||
$('#vcodecs').html(SrsRtcFormatSenders(sdk.pc.getSenders(), "video"));
|
||||
}
|
||||
};
|
||||
|
||||
// For example:
|
||||
// webrtc://r.ossrs.net/live/livestream
|
||||
var url = $("#txt_url").val();
|
||||
|
|
|
@ -150,6 +150,35 @@
|
|||
<script type="text/javascript">
|
||||
var bandwidth = null;
|
||||
|
||||
// for bw to init url
|
||||
// url: scheme://host:port/path?query#fragment
|
||||
function srs_init_bwt(rtmp_url, hls_url) {
|
||||
update_nav();
|
||||
|
||||
if (rtmp_url) {
|
||||
$(rtmp_url).val(build_default_bandwidth_rtmp_url());
|
||||
}
|
||||
}
|
||||
// for the bandwidth tool to init page
|
||||
function build_default_bandwidth_rtmp_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var schema = 'rtmp';
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var port = (!query.port)? 1935:query.port;
|
||||
var vhost = "bandcheck.srs.com";
|
||||
var app = (!query.app)? "app":query.app;
|
||||
var key = (!query.key)? "35c9b402c12a7246868752e2878f7e0e":query.key;
|
||||
|
||||
var uri = schema + "://" + server;
|
||||
if (!is_default_port(schema, port)) {
|
||||
uri += ":" + port;
|
||||
}
|
||||
uri += "/" + app + "?key=" + key + "&vhost=" + vhost;
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
var autoLoadPage = function() {
|
||||
srs_init_bwt("#txt_url");
|
||||
|
||||
|
|
|
@ -438,6 +438,207 @@
|
|||
realtime_player.play(url);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* get the vcodec and acodec.
|
||||
*/
|
||||
function srs_publiser_get_codec(
|
||||
vcodec, acodec,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
) {
|
||||
acodec.codec = $(sl_acodec).val();
|
||||
acodec.device_code = $(sl_microphones).val();
|
||||
acodec.device_name = $(sl_microphones).text();
|
||||
|
||||
vcodec.device_code = $(sl_cameras).find("option:selected").val();
|
||||
vcodec.device_name = $(sl_cameras).find("option:selected").text();
|
||||
|
||||
vcodec.codec = $(sl_vcodec).find("option:selected").val();
|
||||
vcodec.profile = $(sl_profile).find("option:selected").val();
|
||||
vcodec.level = $(sl_level).find("option:selected").val();
|
||||
vcodec.fps = $(sl_fps).find("option:selected").val();
|
||||
vcodec.gop = $(sl_gop).find("option:selected").val();
|
||||
vcodec.size = $(sl_size).find("option:selected").val();
|
||||
vcodec.bitrate = $(sl_bitrate).find("option:selected").val();
|
||||
}
|
||||
/**
|
||||
* when publisher ready, init the page elements.
|
||||
*/
|
||||
function srs_publisher_initialize_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
) {
|
||||
srs_initialize_codec_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
);
|
||||
|
||||
//var profiles = ["baseline", "main"];
|
||||
$(sl_profile + " option[value='main']").attr("selected", true);
|
||||
|
||||
//var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
// "2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
$(sl_level + " option[value='4.1']").attr("selected", true);
|
||||
|
||||
//var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
// "5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
$(sl_gop + " option[value='10']").attr("selected", true);
|
||||
|
||||
//var sizes = ["176x144", "320x240", "352x240",
|
||||
// "352x288", "480x360", "640x480", "720x480", "720x576", "800x600",
|
||||
// "1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
$(sl_size + " option[value='640x480']").attr("selected", true);
|
||||
|
||||
//var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
$(sl_fps + " option[value='20']").attr("selected", true);
|
||||
|
||||
//var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
// "950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
$(sl_bitrate + " option[value='500']").attr("selected", true);
|
||||
|
||||
// speex
|
||||
$(sl_acodec + " option[value='speex']").attr("selected", true);
|
||||
}
|
||||
|
||||
// without default values set.
|
||||
function srs_initialize_codec_page(
|
||||
cameras, microphones,
|
||||
sl_cameras, sl_microphones, sl_vcodec, sl_profile, sl_level, sl_gop, sl_size, sl_fps, sl_bitrate,
|
||||
sl_acodec
|
||||
) {
|
||||
$(sl_cameras).empty();
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
$(sl_cameras).append("<option value='" + i + "'>" + cameras[i] + "</option");
|
||||
}
|
||||
// optional: select the except matches
|
||||
matchs = ["virtual"];
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (cameras[i].toLowerCase().indexOf(matchs[j]) == -1) {
|
||||
$(sl_cameras + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// optional: select the first matched.
|
||||
matchs = ["truevision", "integrated"];
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (cameras[i].toLowerCase().indexOf(matchs[j]) >= 0) {
|
||||
$(sl_cameras + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$(sl_microphones).empty();
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
$(sl_microphones).append("<option value='" + i + "'>" + microphones[i] + "</option");
|
||||
}
|
||||
// optional: select the except matches
|
||||
matchs = ["default"];
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (microphones[i].toLowerCase().indexOf(matchs[j]) == -1) {
|
||||
$(sl_microphones + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// optional: select the first matched.
|
||||
matchs = ["realtek", "内置式麦克风"];
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
for (var j = 0; j < matchs.length; j++) {
|
||||
if (microphones[i].toLowerCase().indexOf(matchs[j]) >= 0) {
|
||||
$(sl_microphones + " option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < matchs.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$(sl_vcodec).empty();
|
||||
var vcodecs = ["h264", "vp6"];
|
||||
vcodecs = ["h264"]; // h264 only.
|
||||
for (var i = 0; i < vcodecs.length; i++) {
|
||||
$(sl_vcodec).append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_profile).empty();
|
||||
var profiles = ["baseline", "main"];
|
||||
for (var i = 0; i < profiles.length; i++) {
|
||||
$(sl_profile).append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_level).empty();
|
||||
var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
"2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
for (var i = 0; i < levels.length; i++) {
|
||||
$(sl_level).append("<option value='" + levels[i] + "'>" + levels[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_gop).empty();
|
||||
var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
"5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
for (var i = 0; i < gops.length; i++) {
|
||||
$(sl_gop).append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option");
|
||||
}
|
||||
|
||||
$(sl_size).empty();
|
||||
var sizes = ["176x144", "320x240", "352x240",
|
||||
"352x288", "480x360", "640x480", "720x480", "720x576", "800x600",
|
||||
"1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
for (i = 0; i < sizes.length; i++) {
|
||||
$(sl_size).append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option");
|
||||
}
|
||||
|
||||
$(sl_fps).empty();
|
||||
var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
for (i = 0; i < fpses.length; i++) {
|
||||
$(sl_fps).append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option");
|
||||
}
|
||||
|
||||
$(sl_bitrate).empty();
|
||||
var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
"950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
for (i = 0; i < bitrates.length; i++) {
|
||||
$(sl_bitrate).append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option");
|
||||
}
|
||||
|
||||
$(sl_acodec).empty();
|
||||
var bitrates = ["speex", "nellymoser", "pcma", "pcmu"];
|
||||
for (i = 0; i < bitrates.length; i++) {
|
||||
$(sl_acodec).append("<option value='" + bitrates[i] + "'>" + bitrates[i] + "</option");
|
||||
}
|
||||
}
|
||||
|
||||
// check whether can republish
|
||||
function srs_can_republish() {
|
||||
var browser = get_browser_agents();
|
||||
|
||||
if (browser.Chrome || browser.Firefox) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.MSIE || browser.QQBrowser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
cat <<END >>/dev/null
|
||||
touch git2unix &&
|
||||
echo "bash `pwd`/git2unix.sh" >git2unix &&
|
||||
chmod +x git2unix &&
|
||||
sudo rm -f /bin/git2unix &&
|
||||
sudo mv git2unix /bin/git2unix
|
||||
END
|
||||
|
||||
dos2unix -V>/dev/null 2>&1
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then
|
||||
echo "dos2unix not found."
|
||||
echo " sudo yum install -y dos2unix"
|
||||
exit $ret
|
||||
fi
|
||||
|
||||
files=`git status|egrep "(modified|new file)"|awk -F ':' '{print $2}'|awk '{print $1}'|egrep "(.hpp$|.cpp$|.cc$|.h$|.c$|.txt$|.sh|.conf$)"`;
|
||||
for file in $files; do
|
||||
dos2unix $file;
|
||||
echo $file|grep ".sh$" >/dev/null 2>&1; EOF_SH=$?
|
||||
if [[ $EOF_SH -ne 0 && -f $file ]]; then
|
||||
echo "chmod -x $file"
|
||||
chmod -x $file;
|
||||
fi
|
||||
done
|
|
@ -134,65 +134,69 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
|||
|
||||
// For client to specifies the EIP of server.
|
||||
string eip = r->query_get("eip");
|
||||
string codec = r->query_get("codec");
|
||||
// For client to specifies whether encrypt by SRTP.
|
||||
string srtp = r->query_get("encrypt");
|
||||
string dtls = r->query_get("dtls");
|
||||
|
||||
srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, srtp=%s, dtls=%s",
|
||||
srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, srtp=%s, dtls=%s",
|
||||
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(),
|
||||
srtp.c_str(), dtls.c_str());
|
||||
codec.c_str(), srtp.c_str(), dtls.c_str()
|
||||
);
|
||||
|
||||
// The RTC user config object.
|
||||
SrsRtcUserConfig ruc;
|
||||
ruc.eip_ = eip;
|
||||
ruc.codec_ = codec;
|
||||
ruc.publish_ = false;
|
||||
ruc.dtls_ = (dtls != "false");
|
||||
|
||||
if (srtp.empty()) {
|
||||
ruc.srtp_ = _srs_config->get_rtc_server_encrypt();
|
||||
} else {
|
||||
ruc.srtp_ = (srtp != "false");
|
||||
}
|
||||
|
||||
// TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
|
||||
SrsSdp remote_sdp;
|
||||
if ((err = remote_sdp.parse(remote_sdp_str)) != srs_success) {
|
||||
if ((err = ruc.remote_sdp_.parse(remote_sdp_str)) != srs_success) {
|
||||
return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str());
|
||||
}
|
||||
|
||||
if ((err = check_remote_sdp(remote_sdp)) != srs_success) {
|
||||
if ((err = check_remote_sdp(ruc.remote_sdp_)) != srs_success) {
|
||||
return srs_error_wrap(err, "remote sdp check failed");
|
||||
}
|
||||
|
||||
SrsRequest request;
|
||||
request.app = app;
|
||||
request.stream = stream_name;
|
||||
ruc.req_->app = app;
|
||||
ruc.req_->stream = stream_name;
|
||||
|
||||
// TODO: FIXME: Parse vhost.
|
||||
// discovery vhost, resolve the vhost from config
|
||||
SrsConfDirective* parsed_vhost = _srs_config->get_vhost("");
|
||||
if (parsed_vhost) {
|
||||
request.vhost = parsed_vhost->arg0();
|
||||
ruc.req_->vhost = parsed_vhost->arg0();
|
||||
}
|
||||
|
||||
SrsSdp local_sdp;
|
||||
|
||||
// Config for SDP and session.
|
||||
local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(request.vhost);
|
||||
local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(request.vhost);
|
||||
local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(ruc.req_->vhost);
|
||||
local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(ruc.req_->vhost);
|
||||
|
||||
// Whether enabled.
|
||||
bool server_enabled = _srs_config->get_rtc_server_enabled();
|
||||
bool rtc_enabled = _srs_config->get_rtc_enabled(request.vhost);
|
||||
bool rtc_enabled = _srs_config->get_rtc_enabled(ruc.req_->vhost);
|
||||
if (server_enabled && !rtc_enabled) {
|
||||
srs_warn("RTC disabled in vhost %s", request.vhost.c_str());
|
||||
srs_warn("RTC disabled in vhost %s", ruc.req_->vhost.c_str());
|
||||
}
|
||||
if (!server_enabled || !rtc_enabled) {
|
||||
return srs_error_new(ERROR_RTC_DISABLED, "Disabled server=%d, rtc=%d, vhost=%s",
|
||||
server_enabled, rtc_enabled, request.vhost.c_str());
|
||||
server_enabled, rtc_enabled, ruc.req_->vhost.c_str());
|
||||
}
|
||||
|
||||
bool srtp_enabled = true;
|
||||
if (srtp.empty()) {
|
||||
srtp_enabled = _srs_config->get_rtc_server_encrypt();
|
||||
} else {
|
||||
srtp_enabled = (srtp != "false");
|
||||
}
|
||||
|
||||
bool dtls_enabled = (dtls != "false");
|
||||
|
||||
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
|
||||
SrsRtcConnection* session = NULL;
|
||||
if ((err = server_->create_session(&request, remote_sdp, local_sdp, eip, false, dtls_enabled, srtp_enabled, &session)) != srs_success) {
|
||||
return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", dtls_enabled, srtp_enabled, eip.c_str());
|
||||
if ((err = server_->create_session(&ruc, local_sdp, &session)) != srs_success) {
|
||||
return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", ruc.dtls_, ruc.srtp_, eip.c_str());
|
||||
}
|
||||
|
||||
ostringstream os;
|
||||
|
@ -213,7 +217,7 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
|
|||
res->set("sessionid", SrsJsonAny::str(session->username().c_str()));
|
||||
|
||||
srs_trace("RTC username=%s, dtls=%u, srtp=%u, offer=%dB, answer=%dB", session->username().c_str(),
|
||||
dtls_enabled, srtp_enabled, remote_sdp_str.length(), local_sdp_str.length());
|
||||
ruc.dtls_, ruc.srtp_, remote_sdp_str.length(), local_sdp_str.length());
|
||||
srs_trace("RTC remote offer: %s", srs_string_replace(remote_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
|
||||
srs_trace("RTC local answer: %s", local_sdp_str.c_str());
|
||||
|
||||
|
@ -301,7 +305,7 @@ srs_error_t SrsGoApiRtcPlay::exchange_sdp(SrsRequest* req, const SrsSdp& remote_
|
|||
}
|
||||
}
|
||||
|
||||
// Only choose one match opus codec.
|
||||
// Only choose one match opus.
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -498,52 +502,60 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHtt
|
|||
|
||||
// For client to specifies the EIP of server.
|
||||
string eip = r->query_get("eip");
|
||||
string codec = r->query_get("codec");
|
||||
|
||||
srs_trace("RTC publish %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s",
|
||||
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str());
|
||||
srs_trace("RTC publish %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s",
|
||||
streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(),
|
||||
codec.c_str()
|
||||
);
|
||||
|
||||
// The RTC user config object.
|
||||
SrsRtcUserConfig ruc;
|
||||
ruc.eip_ = eip;
|
||||
ruc.codec_ = codec;
|
||||
ruc.publish_ = true;
|
||||
ruc.dtls_ = ruc.srtp_ = true;
|
||||
|
||||
// TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
|
||||
SrsSdp remote_sdp;
|
||||
if ((err = remote_sdp.parse(remote_sdp_str)) != srs_success) {
|
||||
if ((err = ruc.remote_sdp_.parse(remote_sdp_str)) != srs_success) {
|
||||
return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str());
|
||||
}
|
||||
|
||||
if ((err = check_remote_sdp(remote_sdp)) != srs_success) {
|
||||
if ((err = check_remote_sdp(ruc.remote_sdp_)) != srs_success) {
|
||||
return srs_error_wrap(err, "remote sdp check failed");
|
||||
}
|
||||
|
||||
SrsRequest request;
|
||||
request.app = app;
|
||||
request.stream = stream_name;
|
||||
ruc.req_->app = app;
|
||||
ruc.req_->stream = stream_name;
|
||||
|
||||
// TODO: FIXME: Parse vhost.
|
||||
// discovery vhost, resolve the vhost from config
|
||||
SrsConfDirective* parsed_vhost = _srs_config->get_vhost("");
|
||||
if (parsed_vhost) {
|
||||
request.vhost = parsed_vhost->arg0();
|
||||
ruc.req_->vhost = parsed_vhost->arg0();
|
||||
}
|
||||
|
||||
SrsSdp local_sdp;
|
||||
|
||||
// TODO: FIXME: move to create_session.
|
||||
// Config for SDP and session.
|
||||
local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(request.vhost);
|
||||
local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(request.vhost);
|
||||
local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(ruc.req_->vhost);
|
||||
local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(ruc.req_->vhost);
|
||||
|
||||
// Whether enabled.
|
||||
bool server_enabled = _srs_config->get_rtc_server_enabled();
|
||||
bool rtc_enabled = _srs_config->get_rtc_enabled(request.vhost);
|
||||
bool rtc_enabled = _srs_config->get_rtc_enabled(ruc.req_->vhost);
|
||||
if (server_enabled && !rtc_enabled) {
|
||||
srs_warn("RTC disabled in vhost %s", request.vhost.c_str());
|
||||
srs_warn("RTC disabled in vhost %s", ruc.req_->vhost.c_str());
|
||||
}
|
||||
if (!server_enabled || !rtc_enabled) {
|
||||
return srs_error_new(ERROR_RTC_DISABLED, "Disabled server=%d, rtc=%d, vhost=%s",
|
||||
server_enabled, rtc_enabled, request.vhost.c_str());
|
||||
server_enabled, rtc_enabled, ruc.req_->vhost.c_str());
|
||||
}
|
||||
|
||||
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
|
||||
SrsRtcConnection* session = NULL;
|
||||
if ((err = server_->create_session(&request, remote_sdp, local_sdp, eip, true, true, true, &session)) != srs_success) {
|
||||
if ((err = server_->create_session(&ruc, local_sdp, &session)) != srs_success) {
|
||||
return srs_error_wrap(err, "create session");
|
||||
}
|
||||
|
||||
|
@ -674,7 +686,7 @@ srs_error_t SrsGoApiRtcPublish::exchange_sdp(SrsRequest* req, const SrsSdp& remo
|
|||
}
|
||||
}
|
||||
|
||||
// Only choose one match opus codec.
|
||||
// Only choose one match opus.
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1806,19 +1806,21 @@ const SrsContextId& SrsRtcConnection::context_id()
|
|||
return cid_;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcConnection::add_publisher(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp)
|
||||
srs_error_t SrsRtcConnection::add_publisher(SrsRtcUserConfig* ruc, SrsSdp& local_sdp)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRequest* req = ruc->req_;
|
||||
|
||||
SrsRtcStreamDescription* stream_desc = new SrsRtcStreamDescription();
|
||||
SrsAutoFree(SrsRtcStreamDescription, stream_desc);
|
||||
|
||||
// TODO: FIXME: Change to api of stream desc.
|
||||
if ((err = negotiate_publish_capability(req, remote_sdp, stream_desc)) != srs_success) {
|
||||
if ((err = negotiate_publish_capability(ruc, stream_desc)) != srs_success) {
|
||||
return srs_error_wrap(err, "publish negotiate");
|
||||
}
|
||||
|
||||
if ((err = generate_publish_local_sdp(req, local_sdp, stream_desc, remote_sdp.is_unified())) != srs_success) {
|
||||
if ((err = generate_publish_local_sdp(req, local_sdp, stream_desc, ruc->remote_sdp_.is_unified())) != srs_success) {
|
||||
return srs_error_wrap(err, "generate local sdp");
|
||||
}
|
||||
|
||||
|
@ -1846,10 +1848,12 @@ srs_error_t SrsRtcConnection::add_publisher(SrsRequest* req, const SrsSdp& remot
|
|||
}
|
||||
|
||||
// TODO: FIXME: Error when play before publishing.
|
||||
srs_error_t SrsRtcConnection::add_player(SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp)
|
||||
srs_error_t SrsRtcConnection::add_player(SrsRtcUserConfig* ruc, SrsSdp& local_sdp)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRequest* req = ruc->req_;
|
||||
|
||||
if (_srs_rtc_hijacker) {
|
||||
if ((err = _srs_rtc_hijacker->on_before_play(this, req)) != srs_success) {
|
||||
return srs_error_wrap(err, "before play");
|
||||
|
@ -1857,7 +1861,7 @@ srs_error_t SrsRtcConnection::add_player(SrsRequest* req, const SrsSdp& remote_s
|
|||
}
|
||||
|
||||
std::map<uint32_t, SrsRtcTrackDescription*> play_sub_relations;
|
||||
if ((err = negotiate_play_capability(req, remote_sdp, play_sub_relations)) != srs_success) {
|
||||
if ((err = negotiate_play_capability(ruc, play_sub_relations)) != srs_success) {
|
||||
return srs_error_wrap(err, "play negotiate");
|
||||
}
|
||||
|
||||
|
@ -1882,7 +1886,7 @@ srs_error_t SrsRtcConnection::add_player(SrsRequest* req, const SrsSdp& remote_s
|
|||
++it;
|
||||
}
|
||||
|
||||
if ((err = generate_play_local_sdp(req, local_sdp, stream_desc, remote_sdp.is_unified())) != srs_success) {
|
||||
if ((err = generate_play_local_sdp(req, local_sdp, stream_desc, ruc->remote_sdp_.is_unified())) != srs_success) {
|
||||
return srs_error_wrap(err, "generate local sdp");
|
||||
}
|
||||
|
||||
|
@ -2667,7 +2671,7 @@ bool srs_sdp_has_h264_profile(const SrsSdp& sdp, const string& profile)
|
|||
return false;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, const SrsSdp& remote_sdp, SrsRtcStreamDescription* stream_desc)
|
||||
srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRtcUserConfig* ruc, SrsRtcStreamDescription* stream_desc)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
|
@ -2675,13 +2679,16 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
|
|||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "stream description is NULL");
|
||||
}
|
||||
|
||||
SrsRequest* req = ruc->req_;
|
||||
const SrsSdp& remote_sdp = ruc->remote_sdp_;
|
||||
|
||||
bool nack_enabled = _srs_config->get_rtc_nack_enabled(req->vhost);
|
||||
bool twcc_enabled = _srs_config->get_rtc_twcc_enabled(req->vhost);
|
||||
// TODO: FIME: Should check packetization-mode=1 also.
|
||||
bool has_42e01f = srs_sdp_has_h264_profile(remote_sdp, "42e01f");
|
||||
|
||||
for (size_t i = 0; i < remote_sdp.media_descs_.size(); ++i) {
|
||||
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_[i];
|
||||
for (int i = 0; i < (int)remote_sdp.media_descs_.size(); ++i) {
|
||||
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_.at(i);
|
||||
|
||||
SrsRtcTrackDescription* track_desc = new SrsRtcTrackDescription();
|
||||
SrsAutoFree(SrsRtcTrackDescription, track_desc);
|
||||
|
@ -2711,28 +2718,66 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
|
|||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found opus payload type");
|
||||
}
|
||||
|
||||
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
||||
// if the playload is opus, and the encoding_param_ is channel
|
||||
SrsAudioPayload* audio_payload = new SrsAudioPayload(iter->payload_type_, iter->encoding_name_, iter->clock_rate_, ::atol(iter->encoding_param_.c_str()));
|
||||
audio_payload->set_opus_param_desc(iter->format_specific_param_);
|
||||
for (int j = 0; j < (int)payloads.size(); j++) {
|
||||
const SrsMediaPayloadType& payload = payloads.at(j);
|
||||
|
||||
// if the payload is opus, and the encoding_param_ is channel
|
||||
SrsAudioPayload* audio_payload = new SrsAudioPayload(payload.payload_type_, payload.encoding_name_, payload.clock_rate_, ::atol(payload.encoding_param_.c_str()));
|
||||
audio_payload->set_opus_param_desc(payload.format_specific_param_);
|
||||
|
||||
// TODO: FIXME: Only support some transport algorithms.
|
||||
for (int j = 0; j < (int)iter->rtcp_fb_.size(); ++j) {
|
||||
for (int k = 0; k < (int)payload.rtcp_fb_.size(); ++k) {
|
||||
const string& rtcp_fb = payload.rtcp_fb_.at(k);
|
||||
|
||||
if (nack_enabled) {
|
||||
if (iter->rtcp_fb_.at(j) == "nack" || iter->rtcp_fb_.at(j) == "nack pli") {
|
||||
audio_payload->rtcp_fbs_.push_back(iter->rtcp_fb_.at(j));
|
||||
if (rtcp_fb == "nack" || rtcp_fb == "nack pli") {
|
||||
audio_payload->rtcp_fbs_.push_back(rtcp_fb);
|
||||
}
|
||||
}
|
||||
if (twcc_enabled && remote_twcc_id) {
|
||||
if (iter->rtcp_fb_.at(j) == "transport-cc") {
|
||||
audio_payload->rtcp_fbs_.push_back(iter->rtcp_fb_.at(j));
|
||||
if (rtcp_fb == "transport-cc") {
|
||||
audio_payload->rtcp_fbs_.push_back(rtcp_fb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
track_desc->type_ = "audio";
|
||||
track_desc->set_codec_payload((SrsCodecPayload*)audio_payload);
|
||||
// Only choose one match opus codec.
|
||||
break;
|
||||
}
|
||||
} else if (remote_media_desc.is_video() && ruc->codec_ == "av1") {
|
||||
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("AV1X");
|
||||
if (payloads.empty()) {
|
||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid AV1 payload type");
|
||||
}
|
||||
|
||||
for (int j = 0; j < (int)payloads.size(); j++) {
|
||||
const SrsMediaPayloadType& payload = payloads.at(j);
|
||||
|
||||
// Generate video payload for av1.
|
||||
SrsVideoPayload* video_payload = new SrsVideoPayload(payload.payload_type_, payload.encoding_name_, payload.clock_rate_);
|
||||
|
||||
// TODO: FIXME: Only support some transport algorithms.
|
||||
for (int k = 0; k < (int)payload.rtcp_fb_.size(); ++k) {
|
||||
const string& rtcp_fb = payload.rtcp_fb_.at(k);
|
||||
|
||||
if (nack_enabled) {
|
||||
if (rtcp_fb == "nack" || rtcp_fb == "nack pli") {
|
||||
video_payload->rtcp_fbs_.push_back(rtcp_fb);
|
||||
}
|
||||
}
|
||||
if (twcc_enabled && remote_twcc_id) {
|
||||
if (rtcp_fb == "transport-cc") {
|
||||
video_payload->rtcp_fbs_.push_back(rtcp_fb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
track_desc->type_ = "video";
|
||||
track_desc->set_codec_payload((SrsCodecPayload*)video_payload);
|
||||
break;
|
||||
}
|
||||
} else if (remote_media_desc.is_video()) {
|
||||
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H264");
|
||||
if (payloads.empty()) {
|
||||
|
@ -2740,13 +2785,15 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
|
|||
}
|
||||
|
||||
std::deque<SrsMediaPayloadType> backup_payloads;
|
||||
for (std::vector<SrsMediaPayloadType>::iterator iter = payloads.begin(); iter != payloads.end(); ++iter) {
|
||||
if (iter->format_specific_param_.empty()) {
|
||||
backup_payloads.push_front(*iter);
|
||||
for (int j = 0; j < (int)payloads.size(); j++) {
|
||||
const SrsMediaPayloadType& payload = payloads.at(j);
|
||||
|
||||
if (payload.format_specific_param_.empty()) {
|
||||
backup_payloads.push_front(payload);
|
||||
continue;
|
||||
}
|
||||
H264SpecificParam h264_param;
|
||||
if ((err = srs_parse_h264_fmtp(iter->format_specific_param_, h264_param)) != srs_success) {
|
||||
if ((err = srs_parse_h264_fmtp(payload.format_specific_param_, h264_param)) != srs_success) {
|
||||
srs_error_reset(err); continue;
|
||||
}
|
||||
|
||||
|
@ -2754,21 +2801,23 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
|
|||
bool profile_matched = (!has_42e01f || h264_param.profile_level_id == "42e01f");
|
||||
|
||||
// Try to pick the "best match" H.264 payload type.
|
||||
if (h264_param.packetization_mode == "1" && h264_param.level_asymmerty_allow == "1" && profile_matched) {
|
||||
if (profile_matched && h264_param.packetization_mode == "1" && h264_param.level_asymmerty_allow == "1") {
|
||||
// if the playload is opus, and the encoding_param_ is channel
|
||||
SrsVideoPayload* video_payload = new SrsVideoPayload(iter->payload_type_, iter->encoding_name_, iter->clock_rate_);
|
||||
video_payload->set_h264_param_desc(iter->format_specific_param_);
|
||||
SrsVideoPayload* video_payload = new SrsVideoPayload(payload.payload_type_, payload.encoding_name_, payload.clock_rate_);
|
||||
video_payload->set_h264_param_desc(payload.format_specific_param_);
|
||||
|
||||
// TODO: FIXME: Only support some transport algorithms.
|
||||
for (int j = 0; j < (int)iter->rtcp_fb_.size(); ++j) {
|
||||
for (int k = 0; k < (int)payload.rtcp_fb_.size(); ++k) {
|
||||
const string& rtcp_fb = payload.rtcp_fb_.at(k);
|
||||
|
||||
if (nack_enabled) {
|
||||
if (iter->rtcp_fb_.at(j) == "nack" || iter->rtcp_fb_.at(j) == "nack pli") {
|
||||
video_payload->rtcp_fbs_.push_back(iter->rtcp_fb_.at(j));
|
||||
if (rtcp_fb == "nack" || rtcp_fb == "nack pli") {
|
||||
video_payload->rtcp_fbs_.push_back(rtcp_fb);
|
||||
}
|
||||
}
|
||||
if (twcc_enabled && remote_twcc_id) {
|
||||
if (iter->rtcp_fb_.at(j) == "transport-cc") {
|
||||
video_payload->rtcp_fbs_.push_back(iter->rtcp_fb_.at(j));
|
||||
if (rtcp_fb == "transport-cc") {
|
||||
video_payload->rtcp_fbs_.push_back(rtcp_fb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2779,34 +2828,35 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
|
|||
break;
|
||||
}
|
||||
|
||||
backup_payloads.push_back(*iter);
|
||||
backup_payloads.push_back(payload);
|
||||
}
|
||||
|
||||
// Try my best to pick at least one media payload type.
|
||||
if (!track_desc->media_ && ! backup_payloads.empty()) {
|
||||
SrsMediaPayloadType media_pt= backup_payloads.front();
|
||||
// if the playload is opus, and the encoding_param_ is channel
|
||||
SrsVideoPayload* video_payload = new SrsVideoPayload(media_pt.payload_type_, media_pt.encoding_name_, media_pt.clock_rate_);
|
||||
const SrsMediaPayloadType& payload = backup_payloads.front();
|
||||
|
||||
// if the playload is opus, and the encoding_param_ is channel
|
||||
SrsVideoPayload* video_payload = new SrsVideoPayload(payload.payload_type_, payload.encoding_name_, payload.clock_rate_);
|
||||
|
||||
std::vector<std::string> rtcp_fbs = media_pt.rtcp_fb_;
|
||||
// TODO: FIXME: Only support some transport algorithms.
|
||||
for (int j = 0; j < (int)rtcp_fbs.size(); ++j) {
|
||||
for (int k = 0; k < (int)payload.rtcp_fb_.size(); ++k) {
|
||||
const string& rtcp_fb = payload.rtcp_fb_.at(k);
|
||||
|
||||
if (nack_enabled) {
|
||||
if (rtcp_fbs.at(j) == "nack" || rtcp_fbs.at(j) == "nack pli") {
|
||||
video_payload->rtcp_fbs_.push_back(rtcp_fbs.at(j));
|
||||
if (rtcp_fb == "nack" || rtcp_fb == "nack pli") {
|
||||
video_payload->rtcp_fbs_.push_back(rtcp_fb);
|
||||
}
|
||||
}
|
||||
|
||||
if (twcc_enabled && remote_twcc_id) {
|
||||
if (rtcp_fbs.at(j) == "transport-cc") {
|
||||
video_payload->rtcp_fbs_.push_back(rtcp_fbs.at(j));
|
||||
if (rtcp_fb == "transport-cc") {
|
||||
video_payload->rtcp_fbs_.push_back(rtcp_fb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
track_desc->set_codec_payload((SrsCodecPayload*)video_payload);
|
||||
|
||||
srs_warn("choose backup H.264 payload type=%d", backup_payloads.front().payload_type_);
|
||||
srs_warn("choose backup H.264 payload type=%d", payload.payload_type_);
|
||||
}
|
||||
|
||||
// TODO: FIXME: Support RRTR?
|
||||
|
@ -2820,8 +2870,9 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
|
|||
track_desc->create_auxiliary_payload(remote_media_desc.find_media_with_encoding_name("ulpfec"));
|
||||
|
||||
std::string track_id;
|
||||
for (int i = 0; i < (int)remote_media_desc.ssrc_infos_.size(); ++i) {
|
||||
SrsSSRCInfo ssrc_info = remote_media_desc.ssrc_infos_.at(i);
|
||||
for (int j = 0; j < (int)remote_media_desc.ssrc_infos_.size(); ++j) {
|
||||
const SrsSSRCInfo& ssrc_info = remote_media_desc.ssrc_infos_.at(j);
|
||||
|
||||
// ssrc have same track id, will be description in the same track description.
|
||||
if(track_id != ssrc_info.msid_tracker_) {
|
||||
SrsRtcTrackDescription* track_desc_copy = track_desc->copy();
|
||||
|
@ -2839,8 +2890,9 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRequest* req, cons
|
|||
}
|
||||
|
||||
// set track fec_ssrc and rtx_ssrc
|
||||
for (int i = 0; i < (int)remote_media_desc.ssrc_groups_.size(); ++i) {
|
||||
SrsSSRCGroup ssrc_group = remote_media_desc.ssrc_groups_.at(i);
|
||||
for (int j = 0; j < (int)remote_media_desc.ssrc_groups_.size(); ++j) {
|
||||
const SrsSSRCGroup& ssrc_group = remote_media_desc.ssrc_groups_.at(j);
|
||||
|
||||
SrsRtcTrackDescription* track_desc = stream_desc->find_track_description_by_ssrc(ssrc_group.ssrcs_[0]);
|
||||
if (!track_desc) {
|
||||
continue;
|
||||
|
@ -2962,10 +3014,13 @@ srs_error_t SrsRtcConnection::generate_publish_local_sdp(SrsRequest* req, SrsSdp
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const SrsSdp& remote_sdp, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations)
|
||||
srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRtcUserConfig* ruc, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRequest* req = ruc->req_;
|
||||
const SrsSdp& remote_sdp = ruc->remote_sdp_;
|
||||
|
||||
bool nack_enabled = _srs_config->get_rtc_nack_enabled(req->vhost);
|
||||
bool twcc_enabled = _srs_config->get_rtc_twcc_enabled(req->vhost);
|
||||
// TODO: FIME: Should check packetization-mode=1 also.
|
||||
|
@ -2976,8 +3031,8 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S
|
|||
return srs_error_wrap(err, "fetch rtc source");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < remote_sdp.media_descs_.size(); ++i) {
|
||||
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_[i];
|
||||
for (int i = 0; i < (int)remote_sdp.media_descs_.size(); ++i) {
|
||||
const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_.at(i);
|
||||
|
||||
// Whether feature enabled in remote extmap.
|
||||
int remote_twcc_id = 0;
|
||||
|
@ -3002,6 +3057,14 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S
|
|||
|
||||
remote_payload = payloads.at(0);
|
||||
track_descs = source->get_track_desc("audio", "opus");
|
||||
} else if (remote_media_desc.is_video() && ruc->codec_ == "av1") {
|
||||
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("AV1X");
|
||||
if (payloads.empty()) {
|
||||
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid AV1 payload type");
|
||||
}
|
||||
|
||||
remote_payload = payloads.at(0);
|
||||
track_descs = source->get_track_desc("video", "AV1X");
|
||||
} else if (remote_media_desc.is_video()) {
|
||||
// TODO: check opus format specific param
|
||||
vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H264");
|
||||
|
@ -3011,7 +3074,7 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S
|
|||
|
||||
remote_payload = payloads.at(0);
|
||||
for (int j = 0; j < (int)payloads.size(); j++) {
|
||||
SrsMediaPayloadType& payload = payloads.at(j);
|
||||
const SrsMediaPayloadType& payload = payloads.at(j);
|
||||
|
||||
// If exists 42e01f profile, choose it; otherwise, use the first payload.
|
||||
// TODO: FIME: Should check packetization-mode=1 also.
|
||||
|
@ -3024,8 +3087,8 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S
|
|||
track_descs = source->get_track_desc("video", "H264");
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)track_descs.size(); ++i) {
|
||||
SrsRtcTrackDescription* track = track_descs[i]->copy();
|
||||
for (int j = 0; j < (int)track_descs.size(); ++j) {
|
||||
SrsRtcTrackDescription* track = track_descs.at(j)->copy();
|
||||
|
||||
// Use remote/source/offer PayloadType.
|
||||
track->media_->pt_of_publisher_ = track->media_->pt_;
|
||||
|
@ -3077,86 +3140,6 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, SrsRtcStreamDescription* req_stream_desc,
|
||||
std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRtcStream* source = NULL;
|
||||
if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) {
|
||||
return srs_error_wrap(err, "fetch rtc source");
|
||||
}
|
||||
|
||||
std::vector<SrsRtcTrackDescription*> src_track_descs;
|
||||
//negotiate audio media
|
||||
if(NULL != req_stream_desc->audio_track_desc_) {
|
||||
SrsRtcTrackDescription* req_audio_track = req_stream_desc->audio_track_desc_;
|
||||
int remote_twcc_id = req_audio_track->get_rtp_extension_id(kTWCCExt);
|
||||
|
||||
src_track_descs = source->get_track_desc("audio", "opus");
|
||||
if (src_track_descs.size() > 0) {
|
||||
// FIXME: use source sdp or subscribe sdp? native subscribe may have no sdp
|
||||
SrsRtcTrackDescription *track = src_track_descs[0]->copy();
|
||||
|
||||
// Use remote/source/offer PayloadType.
|
||||
track->media_->pt_of_publisher_ = track->media_->pt_;
|
||||
track->media_->pt_ = req_audio_track->media_->pt_;
|
||||
|
||||
if (req_audio_track->red_ && track->red_) {
|
||||
track->red_->pt_of_publisher_ = track->red_->pt_;
|
||||
track->red_->pt_ = req_audio_track->red_->pt_;
|
||||
}
|
||||
|
||||
track->del_rtp_extension_desc(kTWCCExt);
|
||||
if (remote_twcc_id > 0) {
|
||||
track->add_rtp_extension_desc(remote_twcc_id, kTWCCExt);
|
||||
}
|
||||
|
||||
track->mid_ = req_audio_track->mid_;
|
||||
sub_relations.insert(make_pair(track->ssrc_, track));
|
||||
track->set_direction("sendonly");
|
||||
track->ssrc_ = SrsRtcSSRCGenerator::instance()->generate_ssrc();
|
||||
}
|
||||
}
|
||||
|
||||
//negotiate video media
|
||||
std::vector<SrsRtcTrackDescription*> req_video_tracks = req_stream_desc->video_track_descs_;
|
||||
src_track_descs = source->get_track_desc("video", "h264");
|
||||
for(int i = 0; i < (int)req_video_tracks.size(); ++i) {
|
||||
SrsRtcTrackDescription* req_video = req_video_tracks.at(i);
|
||||
int remote_twcc_id = req_video->get_rtp_extension_id(kTWCCExt);
|
||||
|
||||
for(int j = 0; j < (int)src_track_descs.size(); ++j) {
|
||||
SrsRtcTrackDescription* src_video = src_track_descs.at(j);
|
||||
if(req_video->id_ == src_video->id_) {
|
||||
// FIXME: use source sdp or subscribe sdp? native subscribe may have no sdp
|
||||
SrsRtcTrackDescription *track = src_video->copy();
|
||||
|
||||
// Use remote/source/offer PayloadType.
|
||||
track->media_->pt_of_publisher_ = track->media_->pt_;
|
||||
track->media_->pt_ = req_video->media_->pt_;
|
||||
|
||||
if (req_video->red_ && track->red_) {
|
||||
track->red_->pt_of_publisher_ = track->red_->pt_;
|
||||
track->red_->pt_ = req_video->red_->pt_;
|
||||
}
|
||||
|
||||
track->del_rtp_extension_desc(kTWCCExt);
|
||||
if (remote_twcc_id > 0) {
|
||||
track->add_rtp_extension_desc(remote_twcc_id, kTWCCExt);
|
||||
}
|
||||
|
||||
track->mid_ = req_video->mid_;
|
||||
sub_relations.insert(make_pair(track->ssrc_, track));
|
||||
track->set_direction("sendonly");
|
||||
track->ssrc_ = SrsRtcSSRCGenerator::instance()->generate_ssrc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcConnection::fetch_source_capability(SrsRequest* req, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
|
|
@ -64,6 +64,7 @@ class SrsRtcVideoSendTrack;
|
|||
class SrsErrorPithyPrint;
|
||||
class SrsPithyPrint;
|
||||
class SrsStatistic;
|
||||
class SrsRtcUserConfig;
|
||||
|
||||
const uint8_t kSR = 200;
|
||||
const uint8_t kRR = 201;
|
||||
|
@ -476,8 +477,8 @@ public:
|
|||
void switch_to_context();
|
||||
const SrsContextId& context_id();
|
||||
public:
|
||||
srs_error_t add_publisher(SrsRequest* request, const SrsSdp& remote_sdp, SrsSdp& local_sdp);
|
||||
srs_error_t add_player(SrsRequest* request, const SrsSdp& remote_sdp, SrsSdp& local_sdp);
|
||||
srs_error_t add_publisher(SrsRtcUserConfig* ruc, SrsSdp& local_sdp);
|
||||
srs_error_t add_player(SrsRtcUserConfig* ruc, SrsSdp& local_sdp);
|
||||
public:
|
||||
// Before initialize, user must set the local SDP, which is used to inititlize DTLS.
|
||||
srs_error_t initialize(SrsRequest* r, bool dtls, bool srtp, std::string username);
|
||||
|
@ -525,12 +526,11 @@ public:
|
|||
private:
|
||||
srs_error_t on_binding_request(SrsStunPacket* r);
|
||||
// publish media capabilitiy negotiate
|
||||
srs_error_t negotiate_publish_capability(SrsRequest* req, const SrsSdp& remote_sdp, SrsRtcStreamDescription* stream_desc);
|
||||
srs_error_t negotiate_publish_capability(SrsRtcUserConfig* ruc, SrsRtcStreamDescription* stream_desc);
|
||||
srs_error_t generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc, bool unified_plan);
|
||||
// play media capabilitiy negotiate
|
||||
//TODO: Use StreamDescription to negotiate and remove first negotiate_play_capability function
|
||||
srs_error_t negotiate_play_capability(SrsRequest* req, const SrsSdp& remote_sdp, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||
srs_error_t negotiate_play_capability(SrsRequest* req, SrsRtcStreamDescription* req_stream_desc, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||
srs_error_t negotiate_play_capability(SrsRtcUserConfig* ruc, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||
srs_error_t fetch_source_capability(SrsRequest* req, std::map<uint32_t, SrsRtcTrackDescription*>& sub_relations);
|
||||
srs_error_t generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcStreamDescription* stream_desc, bool unified_plan);
|
||||
srs_error_t create_player(SrsRequest* request, std::map<uint32_t, SrsRtcTrackDescription*> sub_relations);
|
||||
|
|
|
@ -243,6 +243,18 @@ ISrsRtcServerHijacker::~ISrsRtcServerHijacker()
|
|||
{
|
||||
}
|
||||
|
||||
SrsRtcUserConfig::SrsRtcUserConfig()
|
||||
{
|
||||
req_ = new SrsRequest();
|
||||
publish_ = false;
|
||||
dtls_ = srtp_ = true;
|
||||
}
|
||||
|
||||
SrsRtcUserConfig::~SrsRtcUserConfig()
|
||||
{
|
||||
srs_freep(req_);
|
||||
}
|
||||
|
||||
SrsRtcServer::SrsRtcServer()
|
||||
{
|
||||
handler = NULL;
|
||||
|
@ -498,27 +510,26 @@ srs_error_t SrsRtcServer::listen_api()
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcServer::create_session(
|
||||
SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||
bool publish, bool dtls, bool srtp,
|
||||
SrsRtcConnection** psession
|
||||
) {
|
||||
srs_error_t SrsRtcServer::create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection** psession)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsContextId cid = _srs_context->get_id();
|
||||
|
||||
SrsRequest* req = ruc->req_;
|
||||
|
||||
SrsRtcStream* source = NULL;
|
||||
if ((err = _srs_rtc_sources->fetch_or_create(req, &source)) != srs_success) {
|
||||
return srs_error_wrap(err, "create source");
|
||||
}
|
||||
|
||||
if (publish && !source->can_publish()) {
|
||||
if (ruc->publish_ && !source->can_publish()) {
|
||||
return srs_error_new(ERROR_RTC_SOURCE_BUSY, "stream %s busy", req->get_stream_url().c_str());
|
||||
}
|
||||
|
||||
// TODO: FIXME: add do_create_session to error process.
|
||||
SrsRtcConnection* session = new SrsRtcConnection(this, cid);
|
||||
if ((err = do_create_session(session, req, remote_sdp, local_sdp, mock_eip, publish, dtls, srtp)) != srs_success) {
|
||||
if ((err = do_create_session(ruc, local_sdp, session)) != srs_success) {
|
||||
srs_freep(session);
|
||||
return srs_error_wrap(err, "create session");
|
||||
}
|
||||
|
@ -528,26 +539,25 @@ srs_error_t SrsRtcServer::create_session(
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcServer::do_create_session(
|
||||
SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||
bool publish, bool dtls, bool srtp
|
||||
)
|
||||
srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection* session)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
SrsRequest* req = ruc->req_;
|
||||
|
||||
// first add publisher/player for negotiate sdp media info
|
||||
if (publish) {
|
||||
if ((err = session->add_publisher(req, remote_sdp, local_sdp)) != srs_success) {
|
||||
if (ruc->publish_) {
|
||||
if ((err = session->add_publisher(ruc, local_sdp)) != srs_success) {
|
||||
return srs_error_wrap(err, "add publisher");
|
||||
}
|
||||
} else {
|
||||
if ((err = session->add_player(req, remote_sdp, local_sdp)) != srs_success) {
|
||||
if ((err = session->add_player(ruc, local_sdp)) != srs_success) {
|
||||
return srs_error_wrap(err, "add player");
|
||||
}
|
||||
}
|
||||
|
||||
// All tracks default as inactive, so we must enable them.
|
||||
session->set_all_tracks_status(req->get_stream_url(), publish, true);
|
||||
session->set_all_tracks_status(req->get_stream_url(), ruc->publish_, true);
|
||||
|
||||
std::string local_pwd = srs_random_str(32);
|
||||
std::string local_ufrag = "";
|
||||
|
@ -556,7 +566,7 @@ srs_error_t SrsRtcServer::do_create_session(
|
|||
while (true) {
|
||||
local_ufrag = srs_random_str(8);
|
||||
|
||||
username = local_ufrag + ":" + remote_sdp.get_ice_ufrag();
|
||||
username = local_ufrag + ":" + ruc->remote_sdp_.get_ice_ufrag();
|
||||
if (!_srs_rtc_manager->find_by_name(username)) {
|
||||
break;
|
||||
}
|
||||
|
@ -568,13 +578,13 @@ srs_error_t SrsRtcServer::do_create_session(
|
|||
local_sdp.set_fingerprint(_srs_rtc_dtls_certificate->get_fingerprint());
|
||||
|
||||
// We allows to mock the eip of server.
|
||||
if (!mock_eip.empty()) {
|
||||
if (!ruc->eip_.empty()) {
|
||||
string host;
|
||||
int port = _srs_config->get_rtc_server_listen();
|
||||
srs_parse_hostport(mock_eip, host, port);
|
||||
srs_parse_hostport(ruc->eip_, host, port);
|
||||
|
||||
local_sdp.add_candidate(host, port, "host");
|
||||
srs_trace("RTC: Use candidate mock_eip %s as %s:%d", mock_eip.c_str(), host.c_str(), port);
|
||||
srs_trace("RTC: Use candidate mock_eip %s as %s:%d", ruc->eip_.c_str(), host.c_str(), port);
|
||||
} else {
|
||||
std::vector<string> candidate_ips = get_candidate_ips();
|
||||
for (int i = 0; i < (int)candidate_ips.size(); ++i) {
|
||||
|
@ -587,11 +597,11 @@ srs_error_t SrsRtcServer::do_create_session(
|
|||
local_sdp.session_negotiate_ = local_sdp.session_config_;
|
||||
|
||||
// Setup the negotiate DTLS role.
|
||||
if (remote_sdp.get_dtls_role() == "active") {
|
||||
if (ruc->remote_sdp_.get_dtls_role() == "active") {
|
||||
local_sdp.session_negotiate_.dtls_role = "passive";
|
||||
} else if (remote_sdp.get_dtls_role() == "passive") {
|
||||
} else if (ruc->remote_sdp_.get_dtls_role() == "passive") {
|
||||
local_sdp.session_negotiate_.dtls_role = "active";
|
||||
} else if (remote_sdp.get_dtls_role() == "actpass") {
|
||||
} else if (ruc->remote_sdp_.get_dtls_role() == "actpass") {
|
||||
local_sdp.session_negotiate_.dtls_role = local_sdp.session_config_.dtls_role;
|
||||
} else {
|
||||
// @see: https://tools.ietf.org/html/rfc4145#section-4.1
|
||||
|
@ -601,13 +611,13 @@ srs_error_t SrsRtcServer::do_create_session(
|
|||
}
|
||||
local_sdp.set_dtls_role(local_sdp.session_negotiate_.dtls_role);
|
||||
|
||||
session->set_remote_sdp(remote_sdp);
|
||||
session->set_remote_sdp(ruc->remote_sdp_);
|
||||
// We must setup the local SDP, then initialize the session object.
|
||||
session->set_local_sdp(local_sdp);
|
||||
session->set_state(WAITING_STUN);
|
||||
|
||||
// Before session initialize, we must setup the local SDP.
|
||||
if ((err = session->initialize(req, dtls, srtp, username)) != srs_success) {
|
||||
if ((err = session->initialize(req, ruc->dtls_, ruc->srtp_, username)) != srs_success) {
|
||||
return srs_error_wrap(err, "init");
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <srs_app_reload.hpp>
|
||||
#include <srs_app_hourglass.hpp>
|
||||
#include <srs_app_hybrid.hpp>
|
||||
#include <srs_app_rtc_sdp.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -84,6 +85,25 @@ public:
|
|||
virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt, SrsRtcConnection* session, bool* pconsumed) = 0;
|
||||
};
|
||||
|
||||
// The user config for RTC publish or play.
|
||||
class SrsRtcUserConfig
|
||||
{
|
||||
public:
|
||||
// Original variables from API.
|
||||
SrsSdp remote_sdp_;
|
||||
std::string eip_;
|
||||
std::string codec_;
|
||||
|
||||
// Generated data.
|
||||
SrsRequest* req_;
|
||||
bool publish_;
|
||||
bool dtls_;
|
||||
bool srtp_;
|
||||
public:
|
||||
SrsRtcUserConfig();
|
||||
virtual ~SrsRtcUserConfig();
|
||||
};
|
||||
|
||||
// The RTC server instance, listen UDP port, handle UDP packet, manage RTC connections.
|
||||
class SrsRtcServer : public ISrsUdpMuxHandler, public ISrsFastTimer, public ISrsReloadHandler
|
||||
{
|
||||
|
@ -111,16 +131,9 @@ public:
|
|||
srs_error_t listen_api();
|
||||
public:
|
||||
// Peer start offering, we answer it.
|
||||
srs_error_t create_session(
|
||||
SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp, const std::string& mock_eip,
|
||||
bool publish, bool dtls, bool srtp,
|
||||
SrsRtcConnection** psession
|
||||
);
|
||||
srs_error_t create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection** psession);
|
||||
private:
|
||||
srs_error_t do_create_session(
|
||||
SrsRtcConnection* session, SrsRequest* req, const SrsSdp& remote_sdp, SrsSdp& local_sdp,
|
||||
const std::string& mock_eip, bool publish, bool dtls, bool srtp
|
||||
);
|
||||
srs_error_t do_create_session(SrsRtcUserConfig* ruc, SrsSdp& local_sdp, SrsRtcConnection* session);
|
||||
public:
|
||||
SrsRtcConnection* find_session_by_username(const std::string& ufrag);
|
||||
// interface ISrsFastTimer
|
||||
|
|
|
@ -26,6 +26,6 @@
|
|||
|
||||
#define VERSION_MAJOR 4
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 90
|
||||
#define VERSION_REVISION 91
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue