Пример захвата потока с SIP, захвата RTMP-потока с другого сервера, микширования потоков и ретрансляции результата на RTMP-сервер
Данный пример показывает, как можно сделать вызов на SIP, получить от SIP стороны аудио и видео трафик, захватить RTMP-поток с другого сервера, смикшировать аудиопотоки, добавить их к звонку и затем перенаправить полученный поток на RTMP-сервер
Image RemovedImage Added
Код примера
Пример представляет собой REST-клиента, написанного на JavaScript и находится по следующему пути:
...
Здесь host - адрес WCS-сервера.
Работа с кодом примера
Для разбора кода возьмем версию файла sip-as-rtmp-4.js с хешем c306c1bbf49bfcbd8e24be927ae95f63b7dbaaba ecbadc3, которая находится здесь и доступна для
скачивания в соответствующей сборке 2.0.5.28.2747.212
1. Загрузка тестового RTMP-плеера на страницу для дальнейшего тестирования воспроизведения RTMP потока.кодОтправка REST / HTTP - запросов.
code
Отправка происходит методом POST с ContentType application/json AJAX запросом с использованием фреймворка jquery.
Code Block |
---|
|
function loadPlayersendREST() {
detectFlash(url, data, successHandler, errorHandler) {
console.info("url: " + url);
var attributes = {}console.info("data: " + data);
attributes.id = "player";
$.ajax({
attributes.name = "player";url: url,
attributes.styleclass="center-block";
beforeSend: function var( flashvarsxhr =) {};
var pathToSWF = "../../dependencies/rtmp_player/player.swf";
var elementId = "player" xhr.overrideMimeType( "text/plain;" );
var params = { };,
params.menu = "true";type: 'POST',
params.swliveconnect = "true";
contentType: params.allowfullscreen = "true";
'application/json',
params.allowscriptaccess = "always";data: data,
params.bgcolor = "#777777";
swfobject.embedSWF(pathToSWF, elementId, "350", "400", "11.2.202", "expressInstall.swf", flashvars, params, attributes);
} |
2. Отправка REST / HTTP - запросов.
код
Отправка происходит методом POST с ContentType application/json AJAX запросом с использованием фреймворка jquery.
Code Block |
---|
|
function sendREST(url, data, successHandler, errorHandler) {
console.info("url: " + url);
console.info("data: " + data success: (successHandler === undefined) ? handleAjaxSuccess : successHandler,
error: (errorHandler === undefined) ? handleAjaxError : errorHandler
});
} |
2. Создание исходящего звонка при помощи REST-запроса /call/startup
code
Из текстовых форм собираются данные для установки соединения и звонка (RESTCall)
Code Block |
---|
|
var url = field("restUrl") + "/call/startup";
callId = generateCallID();
$("#sipCallId").ajaxval({callId);
...
url: url,
var RESTCall = {};
RESTCall.toStream = beforeSend: function ( xhr ) {field("rtmpStream");
RESTCall.hasAudio = field("hasAudio");
RESTCall.hasVideo = xhr.overrideMimeTypefield( "text/plain;hasVideo" );
RESTCall.callId },= callId;
RESTCall.sipLogin = field("sipLogin");
type: 'POST',
RESTCall.sipAuthenticationName = field("sipAuthenticationName");
RESTCall.sipPassword contentType: 'application/json',= field("sipPassword");
RESTCall.sipPort = field("sipPort");
data: data,
RESTCall.sipDomain = field("sipDomain");
RESTCall.sipOutboundProxy success:= (successHandler === undefined) ? handleAjaxSuccess : successHandler,
error: (errorHandler === undefined) ? handleAjaxError : errorHandler
});
} |
3. Создание исходящего звонка при помощи REST-запроса /call/startup
код
Из текстовых форм собираются данные для установки соединения и звонка (RESTCall)
Code Block |
---|
|
var urlfield("sipOutboundProxy");
RESTCall.appKey = field("appKey");
RESTCall.sipRegisterRequired = field("sipRegisterRequired");
for (var key in RESTCall) {
setCookie(key, RESTCall[key]);
}
RESTCall.callee = field("restUrlcallee") + "/call/startup";
callIdvar data = generateCallID();
$("#sipCallId").val(callIdJSON.stringify(RESTCall);
var connection = {}sendREST(url, data);
connection.sipLogin = field("sipLogin");
connection.sipAuthenticationName = field("sipAuthenticationName");startCheckCallStatus(); |
3. Захват RTMP-потока с другого сервера запросом /pull/rtmp/pull
code
Code Block |
---|
|
var pullRtmp = function(uri, fn) {
connection.sipPassword = field("sipPassword"console.log("Pull rtmp " + uri);
connection.sipPort = send(field("sipPortrestUrl");
+ "/pull/rtmp/pull", {
connection.sipDomain = field("sipDomain");
connection.sipOutboundProxy = field("sipOutboundProxy");uri: uri
connection.appKey = field("appKey");
}).then(
connection.sipRegisterRequired = field("sipRegisterRequired");
fn(STREAM_STATUS.PENDING)
for (var key in connection) ).catch(function(e){
setCookie(key, connection[key]console.error(e);
}
var RESTCall = {};
RESTCall.toStream = field("rtmpStream");fn(STREAM_STATUS.FAILED);
});
}; |
4. Остановка захвата RTMP-потока с другого сервера запросом /pull/rtmp/terminate
code
Code Block |
---|
|
var terminateRtmp = function(uri, fn) {
RESTCall.hasAudio = field("hasAudio");
RESTCall.hasVideo = field("hasVideo");
RESTCall.callId = callId;console.log("Terminate rtmp " + uri);
send(field("restUrl") + "/pull/rtmp/terminate", {
uri: uri
RESTCall.sipLogin = field("sipLogin");
}).then(
RESTCall.sipAuthenticationName = field("sipAuthenticationName");fn(STREAM_STATUS.STOPPED)
RESTCall.sipPassword = field("sipPassword");
).catch(function(e) {
RESTCall.sipPort = field("sipPort"fn(STREAM_STATUS.FAILED);
RESTCall.sipDomain = field("sipDomain"console.error(e);
RESTCall.sipOutboundProxy})
}; |
5. Запуск микшера запросом /mixer/startup
code
Code Block |
---|
|
var startMixer = fieldfunction("sipOutboundProxy");streamName) {
RESTCall.appKey = field("appKey"console.log("Start mixer " + streamName);
RESTCall.sipRegisterRequired = return send(field("sipRegisterRequiredrestUrl");
+ "/mixer/startup", {
for (var key in RESTCall) {uri: "mixer://" + streamName,
setCookie(key, RESTCall[key]);localStreamName: streamName
}
RESTCall.callee = field("callee");
);
}; |
6. Остановка микшера запросом /mixer/terminate
code
Code Block |
---|
|
var stopMixer = function(streamName, fn) {
var data = JSON.stringify(RESTCall);
sendREST(url, dataconsole.log("Stop mixer " + streamName);
startCheckCallStatus(); |
4. Захват RTMP-потока с другого сервера запросом /pull/rtmp/pull
код
Code Block |
---|
|
var pullRtmp = function(uri, fn)return send(field("restUrl") + "/mixer/terminate", {
console.log("Pull rtmp " + uri);
send(field("restUrl") + "/pull/rtmp/pull", {: "mixer://" + streamName,
urilocalStreamName: uristreamName
}).then(
fn(STREAM_STATUS.PENDING)
).catch(function(e){
;
}; |
7. Добавление/удаление потоков в микшер запросами /mixer/add и /mixer/remove
code
Code Block |
---|
|
if ($(ctx).is(':checked')) {
// console.error(e);Add stream to mixer
fn(STREAM_STATUS.FAILED);
});
}; |
5. Остановка захвата RTMP-потока с другого сервера запросом /pull/rtmp/terminate
код
Code Block |
---|
|
var terminateRtmp = function(uri, fn) {
console.log("Terminate rtmp " + uri);
send(field("restUrl") + "/pull/rtmp/terminate", {
uri: uri
}).then(
fn(STREAM_STATUS.STOPPED)
).catch(function(e) {
fn(STREAM_STATUS.FAILED);
console.error(e);
})
}; |
6. Запуск микшера запросом /mixer/startup
код
Code Block |
---|
|
var startMixer = function(streamName) {
console.log("Start mixer " + streamName);
return send(field("restUrl") + "/mixer/startup", {
uri: "mixer://" + streamName,
localStreamName: streamName
});
}; |
7. Остановка микшера запросом /mixer/terminate
код
Code Block |
---|
|
var stopMixer = function(streamName, fn) {
console.log("Stop mixer " + streamName);
return send(fieldsend(field("restUrl") + "/mixer/terminateadd", {
uri: "mixer://" + streamName,
localStreamName: streamName
});
}; |
8. Добавление/удаление потоков в микшер запросами /mixer/add и /mixer/remove
код
Code Block |
---|
|
if ($(ctx).is(':checked')) {
mixerStream,
localStreamName: mixerStream,
// Add stream toremoteStreamName: mixerstream
send(field("restUrl") + "/mixer/add", }).then(function(){
uri: "mixer://" + mixerStream,
console.log("added");
});
} else localStreamName: mixerStream,{
// Remove stream from remoteStreamName: streammixer
}).then(function(){
send(field("restUrl") + "/mixer/remove", {
uri: console.log("added");"mixer://" + mixerStream,
});
} else {
localStreamName: mixerStream,
// Remove stream fromremoteStreamName: mixerstream
send(field("restUrl") + "/mixer/remove", }).then(function(){
uri: "mixer://" + mixerStream,console.log("removed");
});
} |
8. Добавление выходного потока микшера в звонок запросом /call/inject
code
Code Block |
---|
|
function injectStreamBtn(ctx) {
var streamName localStreamName: mixerStream,
= $("#injectStream").val();
if (!streamName) {
remoteStreamName: stream
$("#injectStream").parent().addClass('has-error');
}).then(function(){return false;
}
var $that = console.log("removed"$(ctx);
});
} |
9. Добавление выходного потока микшера в звонок запросом /call/inject
код
Code Block |
---|
|
function injectStreamBtn(ctx) {
send(field("restUrl") + "/call/inject_stream", {
var streamName =callId: $("#injectStream#sipCallId").val();,
if (!streamName) {
streamName: streamName
$("#injectStream"}).parentthen().addClass('has-error');
function(){
return false;
$that.removeClass('btn-success').addClass('btn-danger');
}
var $that = $(ctx.parents().closest('.input-group').children('input').attr('disabled', true);
send(field("restUrl") + "/call/inject_stream",}).catch(function() {
callId: $("#sipCallId").val(),$that.removeClass('btn-danger').addClass('btn-success');
streamName: streamName$that.parents().closest('.input-group').children('input').attr('disabled', false);
}).then(function(){
$that.removeClass('btn-success').addClass('btn-danger');
$that.parents().closest('.input-group').children('input').attr('disabled', true);
}).catch(function() {
;
} |
9. Ретрансляция звонка на RTMP-сервер в поток запросом /push/startup
code
Code Block |
---|
|
function startRtmpStream() {
if (!rtmpStreamStarted) {
rtmpStreamStarted = true;
var url = $that.removeClass('btn-danger').addClass('btn-success')field("restUrl") + "/push/startup";
$that.parents().closest('.input-group').children('input').attr('disabled', false);
});
} |
...
код
Code Block |
---|
|
function startRtmpStream() {
var RESTObj = {};
var options = {};
if (!rtmpStreamStarted($("#mute").is(':checked')) {
rtmpStreamStartedoptions.action = true"mute";
} var url = field("restUrl") + "/push/startup";
else if ($("#music").is(':checked')) {
options.action = "sound_on";
var RESTObjoptions.soundFile = {}"sample.wav";
var options = {};
RESTObj.streamName if= ($field("#mute").is(':checked')) {rtmpStream");
options.actionRESTObj.rtmpUrl = field("mutertmpUrl");
RESTObj.options = options;
} else if ($console.log("#music").is(':checked')) {
Start rtmp");
sendREST(url, JSON.stringify(RESTObj), startupRtmpSuccessHandler, startupRtmpErrorHandler);
options.action = "sound_on" sendDataToPlayer();
startCheckTransponderStatus();
}
} |
10. Включение/отключение звука ретранслируемого RTMP-потока.
Отключение звука /push/mute code
Code Block |
---|
|
function options.soundFile = "sample.wav";mute() {
if }(rtmpStreamStarted) {
RESTObj.streamName = field$("rtmpStream#mute");
RESTObj.rtmpUrl = field("rtmpUrl".prop('disabled', true);
var RESTObj.options = options{};
console.log("Start rtmp");
RESTObj.mediaSessionId = rtmpMediaSessionId;
var sendREST(url, JSON.stringify(RESTObj), startupRtmpSuccessHandler, startupRtmpErrorHandler) = field("restUrl") + "/push/mute";
sendDataToPlayer();
startCheckTransponderStatus(sendREST(url, JSON.stringify(RESTObj), muteSuccessHandler, muteErrorHandler);
}
} |
11. Включение /отключение звука ретранслируемого RTMP-потока.Отключение звука /push/mute кодunmute code
Code Block |
---|
|
function muteunmute() {
if (rtmpStreamStarted) {
$("#mute").prop('disabled', true);
var RESTObj = {};
RESTObj.mediaSessionId = rtmpMediaSessionId;
var url = field("restUrl") + "/push/muteunmute";
sendREST(url, JSON.stringify(RESTObj), muteSuccessHandler, muteErrorHandler);
}
} |
Включение звука 11. Включение/отключение дополнительной звуковой дорожки из файла в ретранслируемом RTMP-потоке.
Включение звуковой дорожки из файла /push/unmute кодsound_on code
Code Block |
---|
|
function unmutesoundOn() {
if (rtmpStreamStarted) {
$("#mute#music").prop('disabled', true);
var RESTObj = {};
RESTObj.mediaSessionId = rtmpMediaSessionId;
RESTObj.soundFile = "sample.wav";
RESTObj.loop = false;
var url = field("restUrl") + "/push/unmutesound_on";
sendREST(url, JSON.stringify(RESTObj), muteSuccessHandlerinjectSoundSuccessHandler, muteErrorHandler);
}
} |
12. Включение/отключение дополнительной звуковой дорожки из файла в ретранслируемом RTMP-потоке.
...
injectSoundErrorHandler);
}
} |
Отключение звуковой дорожки /push/sound_on кодoff code
Code Block |
---|
|
function soundOnsoundOff() {
if (rtmpStreamStarted) {
$("#music").prop('disabled', true);
var RESTObj = {};
RESTObj.mediaSessionId = rtmpMediaSessionId;
RESTObj.soundFile = "sample.wav"};
RESTObj.loopmediaSessionId = falsertmpMediaSessionId;
var url = field("restUrl") + "/push/sound_onoff";
sendREST(url, JSON.stringify(RESTObj), injectSoundSuccessHandler, injectSoundErrorHandler);
}
} |
Отключение звуковой дорожки /push/sound_off код12. Завершение звонка запросом /call/terminate.
code
Code Block |
---|
|
function soundOffhangup() {
var url = field("restUrl") + "/call/terminate";
var currentCallId = { if (rtmpStreamStarted) {callId: callId };
var data $("#music").prop('disabled', true= JSON.stringify(currentCallId);
var RESTObj = {};sendREST(url, data);
} |
13. Отображение RTMP URL на странице для копирования в сторонний плеер
code
Code Block |
---|
|
function sendDataToPlayer() {
RESTObj.mediaSessionIdvar host = rtmpMediaSessionId;field("rtmpUrl")
var url = field.replace("restUrl") + "/push/sound_off";
localhost", window.location.hostname)
sendREST(url, JSON.stringify(RESTObj), injectSoundSuccessHandler, injectSoundErrorHandler);
}
} |
13. Завершение звонка запросом /call/terminate.
код
Code Block |
---|
|
function hangup() {replace("127.0.0.1", window.location.hostname);
var urlrtmpStreamPrefix = field("restUrl") + "/call/terminate"rtmp_";
var currentCallIdurl = {host callId: callId };
var data = JSON.stringify(currentCallId+ "/" + rtmpStreamPrefix + field("rtmpStream");
sendREST$("#player").text(url, data);
} |