Kurento 端点顺序以录制截屏视频
Kurento endpoints order in order to record screencast
我正在尝试使用 kurento
媒体服务器录制网络摄像头,这是我在后端使用的功能:
var startScreen = (sessionId, ws, sdpOffer, callback) => {
console.log("Start screen")
getKurentoClient((error, kurentoClient) => {
if (error) {
return callback(error)
}
kurentoClient.create('MediaPipeline', (error, pipeline) => {
if (error) {
return callback(error)
}
var recordParams = {
stopOnEndOfStream: true,
mediaProfile: 'WEBM_VIDEO_ONLY',
uri: 'file:///PATH/TO/VIDEO/screen.webm'
}
pipeline.create('RecorderEndpoint', recordParams, (error, recorder) => {
if (error) return callback(error)
screenRecorder = recorder
recorder.record(() => console.log("recording"))
pipeline.create('WebRtcEndpoint', (error, webRtcEndpoint) => {
if (error) {
pipeline.release()
return callback(error)
}
screenRtcEndpoint = webRtcEndpoint
if (candidatesQueue[sessionId]) {
while(candidatesQueue[sessionId].length) {
var candidate = candidatesQueue[sessionId].shift()
webRtcEndpoint.addIceCandidate(candidate)
}
}
webRtcEndpoint.on('OnIceCandidate', (event) => {
var candidate = kurento.register.complexTypes.IceCandidate(event.candidate)
ws.send(JSON.stringify({
id: 'iceCandidateScreen',
candidate: candidate
}))
})
webRtcEndpoint.processOffer(sdpOffer, (error, sdpAnswer) => {
if (error) {
pipeline.release()
return callback(error)
}
sessions[sessionId] = {
'pipeline': pipeline,
'webRtcEndpoint': webRtcEndpoint
}
webRtcEndpoint.connect(webRtcEndpoint, error => {
if (error) {
return callback(error)
}
console.log("Connect webrtcEndpoint")
webRtcEndpoint.connect(screenRecorder, error => {
if (error) {
return callback(error)
}
console.log("connect to the screen recorder")
})
callback(null, sdpAnswer)
})
})
webRtcEndpoint.gatherCandidates((error) => {
if (error) {
return callback(error)
}
})
})
})
})
})
}
管道看起来像这样:
mediaPipeline -> recorderEndpoint and recorder.record -> WebRtcEndpoint connect webrtcendpoint -> connect recorder endpoint
在前端我这样做:
mediaConstrains = {
audio: false,
video: {
mandatory: {
maxWidth: 640,
maxHeight: 480,
maxFrameRate: 15,
minFrameRate: 1
}
}
}
var getMediaConstrains = () => mediaConstrains
var setMediaConstrains = (config) => {
mediaConstrains = config
}
var startScreen = () => {
var options = {
mediaConstrains: mediaConstrains,
onicecandidate: onIceCandidate,
configuration: { iceServers: [
{'url': 'turn:numb.viagenie.ca:3478',
'username': 'email@email.com',
'credential': 'passwordrandom'}
] }
}
webRtcPeerScreen = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function (error) {
if (error) {
return onError(error)
}
this.generateOffer(onOfferScreen)
})
}
这仅适用于无法正常工作的截屏视频,我正在使用几乎完全相同的代码来录制完全正常工作的网络摄像头,这是停止功能:
var stop = () => {
if (webRtcPeerWebcam && webRtcPeerScreen) {
webRtcPeerWebcam.dispose()
webRtcPeerWebcam = null
webRtcPeerScreen.dispose()
webRtcPeerScreen = null
var message = {
id: 'stop'
}
sendMessage(message)
}
}
我在前端配置管道,但是,我将此消息发送到后端,然后此 wss 消息调用此函数:
var stop = (sessionId) => {
if (sessions[sessionId]) {
var pipeline = sessions[sessionId].pipeline
pipeline.release()
delete sessions[sessionId]
delete candidatesQueue[sessionId]
}
}
我认为这可能是录制截屏视频不起作用的问题,或者我可能未正确连接管道
总之谢谢!
PD:我在 KMS 日志中发现了这个:
KurentoMediaElementImpl MediaElementImpl.cpp:434 mediaFlowOutStateChange() <kmswebrtcendpoint373> Media N OT Flowing OUT in pad default with type video
非常推荐在RecorderEndpoint
上调用stop
方法再释放,否则无法保证媒体已经写入文件。
根据您使用的 KMS 版本,您甚至可以等待事件 (Stopped
) 以确保所有媒体都已写入文件。
此外,您的代码似乎没有将 WebrtcEndpoint
与 RecorderEndpoint
连接起来,检查一下。
编辑:
您还应该检查 RecorderEndpoint
是否正确接收媒体以及 WebRtcEndpoint
是否从网络接收。您可以使用 MediaFlowOutStateChange
和 MediaFlowInStateChange
以及 isMediaFlowingIn
isMediaFlowingOut
方法(有关接受的参数,请参阅 kmd)
第一个也是最重要的问题是,在 SDP 协商之前,您正在处理候选人并收集这些候选人。这行不通,所以我认为您的 webrtc 根本无法工作,无论您如何连接端点。
其次,您在 WebRtcEndpoint 之前创建记录器,然后调用 record
。录像机没有任何连接,也没有媒体流入。我建议您在将 WebRtcEndpoint 连接到记录器并在媒体流动后调用 record
方法。为此,您可以像这样
添加事件监听器 MediaStateChanged
webRtcEndpoint.on('MediaStateChanged', function (event) {
if ((event.oldState !== event.newState) && (event.newState === 'CONNECTED')) {
// Start recording
recorderEndpoint.record();
}
});
创建后立即将 WebRtcEndpoint 连接到记录器。
此外,作为旁注,这条线没有意义,因为端点是 sendonly
webRtcEndpoint.connect(webRtcEndpoint, error => {
总结一下,这就是我的建议。不要忘记填补您可能发现的任何空白,因为如果不填补 OnIceCandidate
等的回调,它将无法工作。
var startScreen = (sessionId, ws, sdpOffer, callback) => {
console.log("Start screen")
getKurentoClient((error, kurentoClient) => {
if (error) {
return callback(error)
}
kurentoClient.create('MediaPipeline', (error, pipeline) => {
pipeline.create('RecorderEndpoint', recordParams, (error, recorder) => {
pipeline.create('WebRtcEndpoint', (error, webRtcEndpoint) => {
webRtcEndpoint.connect(screenRecorder, error => {
webRtcEndpoint.processOffer(sdpOffer, (error, sdpAnswer) => {
// The SDP negotiation must be completed before processing candidates
callback(null, sdpAnswer)
if (candidatesQueue[sessionId]) {
while (candidatesQueue[sessionId].length) {
var candidate = candidatesQueue[sessionId].shift()
webRtcEndpoint.addIceCandidate(candidate)
}
}
webRtcEndpoint.on('MediaStateChanged', function(event) {
// This will start recording right after media starts flowing
if ((event.oldState !== event.newState) && (event.newState === 'CONNECTED')) {
// Start recording
recorderEndpoint.record();
}
})
webRtcEndpoint.on('OnIceCandidate', (event) => { /* your code here */ })
// Candidates must be gathered after the negotiation is completed, and the
// event handler is bound
webRtcEndpoint.gatherCandidates((error) => { /* your code here */ })
})
})
})
})
})
})
}
我正在尝试使用 kurento
媒体服务器录制网络摄像头,这是我在后端使用的功能:
var startScreen = (sessionId, ws, sdpOffer, callback) => {
console.log("Start screen")
getKurentoClient((error, kurentoClient) => {
if (error) {
return callback(error)
}
kurentoClient.create('MediaPipeline', (error, pipeline) => {
if (error) {
return callback(error)
}
var recordParams = {
stopOnEndOfStream: true,
mediaProfile: 'WEBM_VIDEO_ONLY',
uri: 'file:///PATH/TO/VIDEO/screen.webm'
}
pipeline.create('RecorderEndpoint', recordParams, (error, recorder) => {
if (error) return callback(error)
screenRecorder = recorder
recorder.record(() => console.log("recording"))
pipeline.create('WebRtcEndpoint', (error, webRtcEndpoint) => {
if (error) {
pipeline.release()
return callback(error)
}
screenRtcEndpoint = webRtcEndpoint
if (candidatesQueue[sessionId]) {
while(candidatesQueue[sessionId].length) {
var candidate = candidatesQueue[sessionId].shift()
webRtcEndpoint.addIceCandidate(candidate)
}
}
webRtcEndpoint.on('OnIceCandidate', (event) => {
var candidate = kurento.register.complexTypes.IceCandidate(event.candidate)
ws.send(JSON.stringify({
id: 'iceCandidateScreen',
candidate: candidate
}))
})
webRtcEndpoint.processOffer(sdpOffer, (error, sdpAnswer) => {
if (error) {
pipeline.release()
return callback(error)
}
sessions[sessionId] = {
'pipeline': pipeline,
'webRtcEndpoint': webRtcEndpoint
}
webRtcEndpoint.connect(webRtcEndpoint, error => {
if (error) {
return callback(error)
}
console.log("Connect webrtcEndpoint")
webRtcEndpoint.connect(screenRecorder, error => {
if (error) {
return callback(error)
}
console.log("connect to the screen recorder")
})
callback(null, sdpAnswer)
})
})
webRtcEndpoint.gatherCandidates((error) => {
if (error) {
return callback(error)
}
})
})
})
})
})
}
管道看起来像这样:
mediaPipeline -> recorderEndpoint and recorder.record -> WebRtcEndpoint connect webrtcendpoint -> connect recorder endpoint
在前端我这样做:
mediaConstrains = {
audio: false,
video: {
mandatory: {
maxWidth: 640,
maxHeight: 480,
maxFrameRate: 15,
minFrameRate: 1
}
}
}
var getMediaConstrains = () => mediaConstrains
var setMediaConstrains = (config) => {
mediaConstrains = config
}
var startScreen = () => {
var options = {
mediaConstrains: mediaConstrains,
onicecandidate: onIceCandidate,
configuration: { iceServers: [
{'url': 'turn:numb.viagenie.ca:3478',
'username': 'email@email.com',
'credential': 'passwordrandom'}
] }
}
webRtcPeerScreen = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function (error) {
if (error) {
return onError(error)
}
this.generateOffer(onOfferScreen)
})
}
这仅适用于无法正常工作的截屏视频,我正在使用几乎完全相同的代码来录制完全正常工作的网络摄像头,这是停止功能:
var stop = () => {
if (webRtcPeerWebcam && webRtcPeerScreen) {
webRtcPeerWebcam.dispose()
webRtcPeerWebcam = null
webRtcPeerScreen.dispose()
webRtcPeerScreen = null
var message = {
id: 'stop'
}
sendMessage(message)
}
}
我在前端配置管道,但是,我将此消息发送到后端,然后此 wss 消息调用此函数:
var stop = (sessionId) => {
if (sessions[sessionId]) {
var pipeline = sessions[sessionId].pipeline
pipeline.release()
delete sessions[sessionId]
delete candidatesQueue[sessionId]
}
}
我认为这可能是录制截屏视频不起作用的问题,或者我可能未正确连接管道
总之谢谢!
PD:我在 KMS 日志中发现了这个:
KurentoMediaElementImpl MediaElementImpl.cpp:434 mediaFlowOutStateChange() <kmswebrtcendpoint373> Media N OT Flowing OUT in pad default with type video
非常推荐在RecorderEndpoint
上调用stop
方法再释放,否则无法保证媒体已经写入文件。
根据您使用的 KMS 版本,您甚至可以等待事件 (Stopped
) 以确保所有媒体都已写入文件。
此外,您的代码似乎没有将 WebrtcEndpoint
与 RecorderEndpoint
连接起来,检查一下。
编辑:
您还应该检查 RecorderEndpoint
是否正确接收媒体以及 WebRtcEndpoint
是否从网络接收。您可以使用 MediaFlowOutStateChange
和 MediaFlowInStateChange
以及 isMediaFlowingIn
isMediaFlowingOut
方法(有关接受的参数,请参阅 kmd)
第一个也是最重要的问题是,在 SDP 协商之前,您正在处理候选人并收集这些候选人。这行不通,所以我认为您的 webrtc 根本无法工作,无论您如何连接端点。
其次,您在 WebRtcEndpoint 之前创建记录器,然后调用 record
。录像机没有任何连接,也没有媒体流入。我建议您在将 WebRtcEndpoint 连接到记录器并在媒体流动后调用 record
方法。为此,您可以像这样
MediaStateChanged
webRtcEndpoint.on('MediaStateChanged', function (event) {
if ((event.oldState !== event.newState) && (event.newState === 'CONNECTED')) {
// Start recording
recorderEndpoint.record();
}
});
创建后立即将 WebRtcEndpoint 连接到记录器。
此外,作为旁注,这条线没有意义,因为端点是 sendonly
webRtcEndpoint.connect(webRtcEndpoint, error => {
总结一下,这就是我的建议。不要忘记填补您可能发现的任何空白,因为如果不填补 OnIceCandidate
等的回调,它将无法工作。
var startScreen = (sessionId, ws, sdpOffer, callback) => {
console.log("Start screen")
getKurentoClient((error, kurentoClient) => {
if (error) {
return callback(error)
}
kurentoClient.create('MediaPipeline', (error, pipeline) => {
pipeline.create('RecorderEndpoint', recordParams, (error, recorder) => {
pipeline.create('WebRtcEndpoint', (error, webRtcEndpoint) => {
webRtcEndpoint.connect(screenRecorder, error => {
webRtcEndpoint.processOffer(sdpOffer, (error, sdpAnswer) => {
// The SDP negotiation must be completed before processing candidates
callback(null, sdpAnswer)
if (candidatesQueue[sessionId]) {
while (candidatesQueue[sessionId].length) {
var candidate = candidatesQueue[sessionId].shift()
webRtcEndpoint.addIceCandidate(candidate)
}
}
webRtcEndpoint.on('MediaStateChanged', function(event) {
// This will start recording right after media starts flowing
if ((event.oldState !== event.newState) && (event.newState === 'CONNECTED')) {
// Start recording
recorderEndpoint.record();
}
})
webRtcEndpoint.on('OnIceCandidate', (event) => { /* your code here */ })
// Candidates must be gathered after the negotiation is completed, and the
// event handler is bound
webRtcEndpoint.gatherCandidates((error) => { /* your code here */ })
})
})
})
})
})
})
}