Пример захвата потока с 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) {
var attributes = {}console.info("url: " + url);
attributes.id = "player"console.info("data: " + data);
attributes.name = "player";
$.ajax({
attributes.styleclass="center-block";
url: url,
var flashvars = {};
beforeSend: function ( xhr ) {
var pathToSWF =xhr.overrideMimeType( "../../dependencies/rtmp_player/player.swf"text/plain;" );
var elementId = "player";
},
var params = {};type: 'POST',
params.menu = "true";
contentType: params.swliveconnect = "true";
'application/json',
params.allowfullscreen = "true";data: data,
params.allowscriptaccess = "always";
success: params.bgcolor = "#777777";
swfobject.embedSWF(pathToSWF, elementId, "350", "400", "11.2.202", "expressInstall.swf", flashvars, params, attributes(successHandler === undefined) ? handleAjaxSuccess : successHandler,
error: (errorHandler === undefined) ? handleAjaxError : errorHandler
});
} |
2. Отправка REST / HTTP - запросов.
код
Отправка происходит методом POST с ContentType application/json AJAX запросом с использованием фреймворка jquery.
...
Создание исходящего звонка при помощи REST-запроса /call/startup
code
Из текстовых форм собираются данные для установки соединения и звонка (RESTCall)
Code Block |
---|
|
function sendREST(url, data, successHandler, errorHandler)var {
url = console.infofield("url: restUrl") + url)"/call/startup";
console.info("data: " + datacallId = 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,field("sipOutboundProxy");
RESTCall.appKey = field("appKey");
RESTCall.sipRegisterRequired = field("sipRegisterRequired");
error: for (errorHandler === undefined) ? handleAjaxError : errorHandler
});
} |
3. Создание исходящего звонка при помощи REST-запроса /call/startup
код
Из текстовых форм собираются данные для установки соединения и звонка (RESTCall)
Code Block |
---|
|
var urlvar key in RESTCall) {
setCookie(key, RESTCall[key]);
}
RESTCall.callee = field("restUrlcallee");
+ "/call/startup";
callId var data = generateCallIDJSON.stringify(RESTCall);
$("#sipCallId").val(callIdsendREST(url, data);
...
var RESTCall = {};
RESTCall.toStream = field("rtmpStream");startCheckCallStatus(); |
3. Захват RTMP-потока с другого сервера запросом /pull/rtmp/pull
code
Code Block |
---|
|
var pullRtmp = function(uri, fn) {
RESTCall.hasAudio = field("hasAudio"console.log("Pull rtmp " + uri);
RESTCall.hasVideo = send(field("hasVideorestUrl");
+ "/pull/rtmp/pull", {
RESTCall.callId = callId;
RESTCall.sipLogin = field("sipLogin");uri: uri
RESTCall.sipAuthenticationName = field("sipAuthenticationName");}).then(
RESTCall.sipPassword = field("sipPassword"); fn(STREAM_STATUS.PENDING)
RESTCall.sipPort = field("sipPort");
).catch(function(e){
RESTCall.sipDomain = field("sipDomain"console.error(e);
RESTCall.sipOutboundProxy = field("sipOutboundProxy"fn(STREAM_STATUS.FAILED);
RESTCall.appKey});
}; |
4. Остановка захвата RTMP-потока с другого сервера запросом /pull/rtmp/terminate
code
Code Block |
---|
|
var terminateRtmp = field("appKey");function(uri, fn) {
RESTCall.sipRegisterRequired = field("sipRegisterRequired");
console.log("Terminate rtmp " + uri);
for (var key in RESTCall)send(field("restUrl") + "/pull/rtmp/terminate", {
setCookie(key, RESTCall[key]);uri: uri
}
).then(
RESTCall.callee = field("callee");
var data = JSON.stringify(RESTCall);
sendREST(url, data fn(STREAM_STATUS.STOPPED)
).catch(function(e) {
fn(STREAM_STATUS.FAILED);
console.error(e);
startCheckCallStatus(})
}; |
4. Захват RTMP-потока с другого сервера 5. Запуск микшера запросом /pullmixer/rtmp/pullstartup
кодcode
Code Block |
---|
|
var pullRtmpstartMixer = function(uri, fnstreamName) {
console.log("PullStart rtmpmixer " + uristreamName);
return send(field("restUrl") + "/pullmixer/rtmp/pullstartup", {
uri: "mixer: uri
//" + streamName,
}).then(
localStreamName: streamName
fn(STREAM_STATUS.PENDING)
).catch(function(e)});
}; |
6. Остановка микшера запросом /mixer/terminate
code
Code Block |
---|
|
var stopMixer = function(streamName, fn) {
console.log("Stop mixer " + console.error(estreamName);
return fn(STREAM_STATUS.FAILED);send(field("restUrl") + "/mixer/terminate", {
uri: "mixer://" + streamName,
localStreamName: streamName
});
}; |
5. Остановка захвата RTMP-потока с другого сервера запросом /pull/rtmp/terminate
код7. Добавление/удаление потоков в микшер запросами /mixer/add и /mixer/remove
code
Code Block |
---|
|
var terminateRtmp = function(uri, fnif ($(ctx).is(':checked')) {
console.log("Terminate rtmp " + uri);
// Add stream to mixer
send(field("restUrl") + "/pullmixer/rtmp/terminateadd", {
uri: uri
"mixer://" + mixerStream,
}).then(
fn(STREAM_STATUS.STOPPED)
localStreamName: mixerStream,
).catch remoteStreamName: stream
}).then(function(e) {
fn(STREAM_STATUS.FAILED console.log("added");
console.error(e});
})
}; |
6. Запуск микшера запросом /mixer/startup
код
Code Block |
---|
|
var startMixer = function(streamName) {
console.log("Start mixer " + streamName);
else {
// Remove stream from mixer
return send(field("restUrl") + "/mixer/startupremove", {
uri: "mixer://" + streamNamemixerStream,
localStreamName: streamNamemixerStream,
});
}; |
7. Остановка микшера запросом /mixer/terminate
код
Code Block |
---|
|
var stopMixer = function(streamName, fn) {
console.log("Stop mixer " + streamName);
remoteStreamName: stream
return send(field("restUrl") + "/mixer/terminate", {
}).then(function(){
uri: "mixer://" + streamName,console.log("removed");
localStreamName: streamName});
});
}; |
8. Добавление /удаление потоков в микшер запросами /mixer/add и /mixer/removeкодвыходного потока микшера в звонок запросом /call/inject
code
Code Block |
---|
|
iffunction ($injectStreamBtn(ctx).is(':checked')) {
var streamName = $("#injectStream").val();
// Add stream to mixerif (!streamName) {
send(field$("restUrl") + "/mixer/add", {#injectStream").parent().addClass('has-error');
return false;
uri: "mixer://" + mixerStream, }
var $that = $(ctx);
localStreamName: mixerStream,send(field("restUrl") + "/call/inject_stream", {
callId: $("#sipCallId").val(),
remoteStreamName: stream
streamName: streamName
}).then(function(){
console.log("added"$that.removeClass('btn-success').addClass('btn-danger');
})$that.parents().closest('.input-group').children('input').attr('disabled', true);
} else).catch(function() {
// Remove stream from mixer
send(field("restUrl") + "/mixer/remove", {
uri: "mixer://" + mixerStream,$that.removeClass('btn-danger').addClass('btn-success');
$that.parents().closest('.input-group').children('input').attr('disabled', false);
});
} |
9. Ретрансляция звонка на RTMP-сервер в поток запросом /push/startup
code
Code Block |
---|
|
function startRtmpStream() {
if (!rtmpStreamStarted) {
localStreamName: mixerStream,
rtmpStreamStarted = true;
remoteStreamName: stream
var url = field("restUrl") + "/push/startup";
var }).then(function(){RESTObj = {};
var options console.log("removed")= {};
});
} |
9. Добавление выходного потока микшера в звонок запросом /call/inject
код
Code Block |
---|
|
function injectStreamBtn(ctxif ($("#mute").is(':checked')) {
var streamName options.action = $("#injectStreammute").val();
if (!streamName) {
} else if ($("#injectStream#music").parent().addClassis('has-error:checked');) {
return false;
options.action }= "sound_on";
var $that = $(ctx);
send(field("restUrl") + "/call/inject_stream", {options.soundFile = "sample.wav";
callId: $("#sipCallId").val(),}
streamName: RESTObj.streamName
}).then(function(){= field("rtmpStream");
$that.removeClass('btn-success').addClass('btn-danger')RESTObj.rtmpUrl = field("rtmpUrl");
$that.parents().closest('.input-group').children('input').attr('disabled', true)RESTObj.options = options;
}).catch(function() { console.log("Start rtmp");
$that.removeClass('btn-danger').addClass('btn-success'sendREST(url, JSON.stringify(RESTObj), startupRtmpSuccessHandler, startupRtmpErrorHandler);
sendDataToPlayer();
$that.parents().closest('.input-group').children('input').attr('disabled', false);
startCheckTransponderStatus();
});
} |
10. Ретрансляция звонка на RTMP-сервер в поток запросом Включение/отключение звука ретранслируемого RTMP-потока.
Отключение звука /push/startupкодmute code
Code Block |
---|
|
function startRtmpStreammute() {
if (!rtmpStreamStarted) {
rtmpStreamStarted =$("#mute").prop('disabled', true);
var urlRESTObj = field("restUrl") + "/push/startup"{};
var RESTObj.mediaSessionId = {}rtmpMediaSessionId;
var optionsurl = {} field("restUrl") + "/push/mute";
if ($("#mute").is(':checked')sendREST(url, JSON.stringify(RESTObj), muteSuccessHandler, muteErrorHandler);
}
} |
Включение звука /push/unmute code
Code Block |
---|
|
function unmute() {
if (rtmpStreamStarted) {
options.action = $("mute"#mute").prop('disabled', true);
}var else if ($("#music").is(':checked')) {RESTObj = {};
options.actionRESTObj.mediaSessionId = "sound_on"rtmpMediaSessionId;
var url options.soundFile = "sample.wav= field("restUrl") + "/push/unmute";
}
RESTObj.streamName = field("rtmpStream"sendREST(url, JSON.stringify(RESTObj), muteSuccessHandler, muteErrorHandler);
RESTObj.rtmpUrl = field("rtmpUrl");}
} |
11. Включение/отключение дополнительной звуковой дорожки из файла в ретранслируемом RTMP-потоке.
Включение звуковой дорожки из файла /push/sound_on code
Code Block |
---|
|
function soundOn() {
if RESTObj.options = options;(rtmpStreamStarted) {
console.log$("Start rtmp"#music").prop('disabled', true);
sendREST(url, JSON.stringify(RESTObj), startupRtmpSuccessHandler, startupRtmpErrorHandler);
var RESTObj = {};
RESTObj.mediaSessionId = sendDataToPlayer()rtmpMediaSessionId;
startCheckTransponderStatus()RESTObj.soundFile = "sample.wav";
}
} |
...
Отключение звука /push/mute код
Code Block |
---|
|
function mute() { RESTObj.loop = false;
if (rtmpStreamStarted) {
var url = $field("#mute").prop('disabled', true)restUrl") + "/push/sound_on";
var RESTObj = {}sendREST(url, JSON.stringify(RESTObj), injectSoundSuccessHandler, injectSoundErrorHandler);
RESTObj.mediaSessionId = rtmpMediaSessionId;
var url = field("restUrl") + "/push/mute";}
} |
Отключение звуковой дорожки /push/sound_off code
Code Block |
---|
|
function soundOff() {
if (rtmpStreamStarted) {
sendREST(url, JSON.stringify(RESTObj), muteSuccessHandler, muteErrorHandler)$("#music").prop('disabled', true);
}
} |
Включение звука /push/unmute код
Code Block |
---|
|
function unmute() {
var ifRESTObj (rtmpStreamStarted)= {};
$("#mute").prop('disabled', true)RESTObj.mediaSessionId = rtmpMediaSessionId;
var RESTObjurl = {};
RESTObj.mediaSessionId = rtmpMediaSessionId;
var url = field("restUrl") + "/push/unmutesound_off";
sendREST(url, JSON.stringify(RESTObj), muteSuccessHandlerinjectSoundSuccessHandler, muteErrorHandlerinjectSoundErrorHandler);
}
} |
12. Включение/отключение дополнительной звуковой дорожки из файла в ретранслируемом RTMP-потоке.Включение звуковой дорожки из файла /push/sound_on кодЗавершение звонка запросом /call/terminate.
code
Code Block |
---|
|
function soundOnhangup() {
var ifurl = field(rtmpStreamStarted"restUrl") {
+ "/call/terminate";
var currentCallId = { $("#music").prop('disabled', true)callId: callId };
var data = var RESTObj = {};
JSON.stringify(currentCallId);
sendREST(url, data);
} |
13. RTMP URL displaying on the page to copy to a third party player
code
Code Block |
---|
|
function sendDataToPlayer() {
var host RESTObj.mediaSessionId = rtmpMediaSessionId;field("rtmpUrl")
RESTObj.soundFile = "sample.wav";
RESTObj.loop = false;.replace("localhost", window.location.hostname)
var url = field("restUrl") + "/push/sound_on";
sendREST(url, JSON.stringify(RESTObj), injectSoundSuccessHandler, injectSoundErrorHandler);
}
} |
Отключение звуковой дорожки /push/sound_off код
Code Block |
---|
|
function soundOff() {
if (rtmpStreamStarted) {
$("#music").prop('disabled', true);
var RESTObj = {};
RESTObj.mediaSessionId = rtmpMediaSessionId;
var url = field("restUrl") + "/push/sound_off";
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);
} |