Black screen when receiving video (resolved)

Hello
I’m using openvidu, kurento and react-native to build an app and I’m having the following problem, and that is that the screen that receives the streaming is black, transmits everything well, even on the web you can see the video that is transmitting but in the mobile only the local video is seen, the remote one does not.

This the screenshot of mobile app:

// video component
import React from ‘react’;
import {View, StatusBar, Text, BackHandler} from ‘react-native’;
import {compose, withState} from ‘recompose’;
import {RTCIceCandidate} from ‘react-native-webrtc’;
import InCallManager from ‘react-native-incall-manager’;
import Lottie from ‘lottie-react-native’;
import Display from ‘react-native-display’;

// module libraries
import * as webrtc from ‘…/…/utils/webrtc’;
import ReceiveScreen from ‘./ReceiveScreen’
import processToken from “./utils”;
import {ReleaseMediaSource} from “…/…/utils/webrtc”;
import client from “…/…/utils/client”;
import {showMessage} from “react-native-flash-message”;
import {CREATE_TOKEN} from “./graphql”;

type Props = {}

let i = 0;
let requestID = 0;
let AAA = {};
let participants = {};
let socket = null;
let s = true;

function sendMessage(message) {
if (socket) {
let jsonMessage = JSON.stringify({ id: requestID, …message});
console.log(">>>", jsonMessage);

	if (message.method === 'publishVideo') {
		AAA[`${requestID}`] = message['__sdp__'];
	}

	if (message.method === 'receiveVideoFrom') {
		AAA[`${requestID}`] = message.params.sender
	}

	requestID++;
	socket.send(jsonMessage);
}

}

class AlertVideoScreen extends React.Component {
state = {
remoteURL: []
};

componentDidMount() {
	//
	i = 0;
	requestID = 0;
	AAA = {};
	participants = {};
	socket = null;
	s = true;

	//
	InCallManager.setSpeakerphoneOn(true);
	InCallManager.setKeepScreenOn(true);

	// subscribe to back action
	BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);

	// create alert
	this.createAlert();
}

componentWillUnmount() {
	// unsubscribe from back action
	BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}

handleBackPress = () => {
	if (this.props.loading) {
		// make goBack task pending
		this.props.setBackPress(true);
		// show task in progress
		alert('Hay una tarea que se esta ejecutando, espere un momento.');
		return true;
	}

	// close video session
	if (this.props.localStream) {
		// exit from room
		this.leaveRoom();

		// release media source
		ReleaseMediaSource();

		//
		InCallManager.setSpeakerphoneOn(true);
		InCallManager.setKeepScreenOn(true);
	}

	// clear chat
	if (this.props.socket) {
		this.props.socket.close();
	}

	i = 0;
	AAA = {};
	participants = {};

	// go to back
	this.props.navigation.goBack();

	return true;
};

joinRoom = (data) => {
	this.props.setLoadingMessage('Iniciando\nvideo');
	const {token, sessionId} = data;
	sendMessage({
		"jsonrpc": "2.0",
		"method": "joinRoom",
		"params": {
			token: token,
			session: sessionId,
			platform: "Chrome 74.0.3729.157 on Linux 64-bit",
			metadata: JSON.stringify({
				clientData: 'Participant' + Math.floor(Math.random() * 100),
				userName: 'Participant' + Math.floor(Math.random() * 100),
				// hasAudio: true,
				// hasVideo: true
			}),
			secret: "",
			recorder: false
		}
	});
};

leaveRoom = () => {
	sendMessage({
		"jsonrpc": "2.0",
		"method": "leaveRoom",
		"id": "leaveRoom"
	});
};

messageProcessHandler = (message) => {
	const msg = JSON.parse(message.data);
	console.log("<<<", msg);
	if (msg.result && msg.result.value === 'pong') {
		setTimeout(() => {
			sendMessage({"jsonrpc": "2.0", "method": "ping"})
		}, 3000)
	}

	if (msg.error) {
		return;
	}

	if (msg.result && msg.result.metadata) {
		this.setState({
			userInfo: msg.result
		});
		//
		webrtc.startCommunication(sendMessage, msg.result.id, (stream) => {
			this.props.setLocalStream(stream.toURL());
			this.props.setLoading(false);

			// AAA = msg.result.value;
			msg.result.value.forEach((item, index) => {
				participants[item.id] = item;
				// AAA.push(item);
				if (!item['streams'] || !item['streams'][0] || !item['streams'][0].id) {
					return;
				}
				webrtc.receiveVideo(sendMessage, item['streams'][0].id, (pc) => {
					pc.onaddstream = (event) => {
						const {remoteURL = []} = this.state;
						this.setState({remoteURL: [...remoteURL, event.stream]});
					};
				});
			});
		});
		return
	}

	if (msg.method === 'iceCandidate') {
		webrtc.addIceCandidate(msg.params.senderConnectionId, new RTCIceCandidate(msg.params));
		return
	}

	if (msg.method === 'participantJoined') {
		participants[msg.params.id] = msg.params.id;
		// webrtc.receiveVideo(sendMessage, msg.params.id, (pc) => {
		// 	pc.onaddstream = (event) => {
		// 		const {remoteURL = []} = this.state;
		// 		this.setState({remoteURL: [...remoteURL, event.stream]});
		// 	};
		// });
		return;
	}

	if (msg.method === 'participantPublished') {
		participants[msg.params.id] = msg.params.id;
		const item = msg.params;
		if (!item['streams'] || !item['streams'][0] || !item['streams'][0].id) {
			return
		}
		webrtc.receiveVideo(sendMessage, item['streams'][0].id, (pc) => {
			// console.log('webrtc.receiveVideo.pc', pc);
			pc.onaddstream = (event) => {
				const {remoteURL = []} = this.state;
				this.setState({remoteURL: [...remoteURL, event.stream]});
			};
		});
		return;
	}

	if (msg.result && msg.result.sdpAnswer) {
		if (this.state.userInfo.id && s) { // && s
			s = false;
			webrtc.ProcessAnswer(this.state.userInfo.id, msg.result.sdpAnswer, (err) => {
				if (err) {
					alert(JSON.stringify(err));
				}
			});
		}

		if (this.state.userInfo.id != AAA[`${msg.id}`]) {
			webrtc.ProcessAnswer(AAA[`${msg.id}`], msg.result.sdpAnswer, (err) => {
				if (err) {
					alert(JSON.stringify(err));
				}
			});
		}

		return
	}

	switch (msg.id) {
		case 1:
			break;
		case 'participantLeft':
			this.participantLeft(msg.name);
			break;
		default:
		// console.error('Unrecognized message', msg.message);
	}
};

participantLeft = (name) => {
	if (participants[name]) {
		delete participants[name];
	}

	if (Object.keys(participants).length == 0) {
		this.setState({
			remoteURL: null
		});
	}
};

createAlert = () => {
	this.props.setLoadingMessage('Obteniendo\nalerta');
	const alertId = 'Session10' ; // this.props.navigation.getParam('alertId');
	this.props.setAlertId(alertId);
	// generate token to connect
	this.createToken(alertId);
};

createToken = (sessionId) => {
	// make state message
	this.props.setLoadingMessage('Creando\ntoken');
	client.mutate({
		mutation: CREATE_TOKEN,
		variables: {
			sessionId
		}
	}).then((response) => {
		if (response.data && response.data.createToken) {
			if (this.props.goBack) {
				this.handleBackPress();
			} else {
				this.initStreaming(response.data.createToken);
			}

		} else {
			//
			this.props.setError(true);
			this.props.setLoading(false);

			// show error
			showMessage({
				message: "Error",
				description: `Ha ocurrido un error al crear el token para generar el streaming del video`,
				type: "danger"
			});

			// go back
			this.handleBackPress();
		}
	}).catch(error => {
		//
		this.props.setError(true);
		this.props.setLoading(false);

		//
		showMessage({
			message: "Error",
			description: `Ha ocurrido el siguiente error al crear el token: ${error.message}`,
			type: "danger"
		});

		// go back
		this.handleBackPress();
	});
};

initStreaming = (response) => {
	const {endpoint} = response;

	this.props.setToken(response.token);

	// proccess token
	const openvidu = processToken(endpoint);

	this.props.setOpenVidu(openvidu);

	// start websocket connection with
	socket = new WebSocket(openvidu.wsUri);

	this.props.setSocket(socket);

	socket.onopen = () => {
		sendMessage({ "jsonrpc":"2.0","method":"ping","params":{"interval":5000} });
		this.joinRoom({token: endpoint, sessionId: openvidu.sessionId});
	};

	socket.onmessage = (message) => {
		this.messageProcessHandler(message);
	}
};

render() {
	if (this.props.loading) {
		return (
			<View style={{flexGrow: 1, alignItems: "center", justifyContent: "center"}}>
				<Text style={{textAlign: 'center'}}>{this.props.loadingMessage}</Text>
				<Lottie
					source={require('./data.json')}
					autoPlay
					loop
				/>
			</View>
		)
	}

	if (this.props.error) {
		return (
			<View style={{flexGrow: 1, alignItems: "center", justifyContent: "center"}}>
				<Lottie
					source={require('./error-animation-lottie.json')}
					autoPlay
					loop
				/>
			</View>
		)
	}

	return (
		<View style={{flexGrow: 1, backgroundColor: '#cacaca'}}>
			<StatusBar hidden={true} animated/>
			{this.state.backPress && <Text>Espere mientras se detiene la cámara</Text>}

			{
				this.state.remoteURL && this.state.remoteURL.map((item) => (
					<Display enable={item} style={{width: 100, height: 100}} key={item.toURL()}>
						<ReceiveScreen
							videoURL={item.toURL()}
						/>
					</Display>
				))
			}
			<View style={{width: 100, height: 100}}>
				<ReceiveScreen
					videoURL={this.props.localStream}
				/>
			</View>

			<Text>{JSON.stringify(this.state.remoteURL)}</Text>
		</View>
	)
}

}

export default compose(
// initial state
withState(‘loading’, ‘setLoading’, true),
withState(‘error’, ‘setError’, false),
withState(‘backPress’, ‘setBackPress’, false),
withState(‘loadingMessage’, ‘setLoadingMessage’, ‘’),
withState(‘localStream’, ‘setLocalStream’, null),
withState(‘recording’, ‘setRecording’, false),
withState(‘alertId’, ‘setAlertId’, null),
withState(‘socket’, ‘setSocket’, null),
withState(‘token’, ‘setToken’, null),
withState(‘session’, ‘setSession’, null),
withState(‘subscribers’, ‘setSubscribers’, []),
withState(‘openvidu’, ‘setOpenVidu’, []),
)(AlertVideoScreen);

// webrtc-utils.js
import {
mediaDevices,
RTCPeerConnection,
RTCSessionDescription,
} from ‘react-native-webrtc’;

let pcArray = {};
let isEnableAudio = true;
let isEnableVideo = true;
let localstream = null;
// const ICE_CONFIG = { ‘iceServers’: [{ url: ‘stun:stun.l.google.com:19302’ }] };
const ICE_CONFIG = {
iceServers: [
{
urls: ‘–’
},
{
urls: [

            "--"
        ],
        username: '--',
        credential: '--'
    },
]

};

/**
*

  • @param {*} _sendMessage

  • @param {*} _name

  • @param {*} callback
    */
    export function startCommunication(_sendMessage, _name, callback) {
    getStream(true).then(stream => {
    localstream = stream;
    let options = {
    mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true,
    },
    };
    let pc = createPC(_sendMessage, _name, true, options, false);

     pcArray[_name] = pc;
     callback(stream);
    

    });
    }

/**

  • @param {*} _sendMessae

  • @param {*} _name

  • @param {*} callback
    */
    export function receiveVideo(_sendMessae, _name, callback) {
    let options = {
    mandatory: {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true,
    },
    };
    let pc = createPC(_sendMessae, _name, true, options, true);
    pcArray[_name] = pc;

    callback(pc);
    }

/**
*
*

  • @param {*} isFront
  • @param {*} callback
    */
    export const getStream = (isFront) => new Promise((resolve, reject) => {
    mediaDevices.enumerateDevices().then(sourceInfos => {
    let videoSourceId;
    for (let i = 0; i < sourceInfos.length; i++) {
    const sourceInfo = sourceInfos[i];
    if (sourceInfo.kind === ‘videoinput’ && sourceInfo.label === (isFront ? ‘Camera 1, Facing front, Orientation 270’:’’)) {
    videoSourceId = sourceInfo.deviceId;
    }
    }
    mediaDevices.getUserMedia({
    audio: true,
    video: {
    mandatory: {
    minWidth: 500, // Provide your own width, height and frame rate here
    minHeight: 300,
    minFrameRate: 30
    },
    facingMode: (isFront ? “user” : “environment”),
    optional: (videoSourceId ? [{sourceId: videoSourceId}] : [])
    }
    })
    .then(stream => {
    // Got stream!
    resolve(stream)
    })
    .catch(error => {
    // Log error
    reject(error);
    });
    });
    });

/**
*
*
*

  • @param {*} sendMessage

  • @param {*} name

  • @param {*} isOffer

  • @param {*} options
    */
    export function createPC(sendMessage, name, isOffer, options, type) {
    let pc = new RTCPeerConnection(ICE_CONFIG);

    pc.onnegotiationneeded = () => {
    if (isOffer) {
    isOffer = false;
    createOffer();
    }
    };

    pc.onicecandidate = (event) => {
    if (event.candidate) {
    let msg = {
    jsonrpc:“2.0”,
    method:“onIceCandidate”,
    params: {
    candidate: event.candidate.candidate,
    endpointName: name,
    sdpMid: event.candidate.sdpMid,
    sdpMLineIndex: event.candidate.sdpMLineIndex
    },
    };
    sendMessage(msg);
    }
    };

    pc.oniceconnectionstatechange = (event) => {
    if (event.target.iceConnectionState === ‘disconnected’) {
    localstream.release();
    localstream = null;
    if (pc !== null) {
    pc.close();
    pc = null;
    }
    }
    };

    pc.onsignalingstatechange = (event) => {
    // console.log(‘onsignalingstatechange’, event);
    };

    // send local stream
    pc.addStream(localstream);

    function createOffer() {
    pc.createOffer(options).then(desc => {
    pc.setLocalDescription(desc).then(() => {
    let msg = {
    jsonrpc:“2.0”,
    method:“publishVideo”,
    params:{
    //sender: name,
    audioActive: true,
    doLoopback: false,
    frameRate: 30,
    hasAudio: true,
    hasVideo: true,
    sdpOffer: pc.localDescription.sdp,
    typeOfVideo: “CAMERA”,
    videoActive: true,
    videoDimensions: { width:640, height:480 }
    },
    sdp” : name,
    };
    if (type) {
    msg = {
    jsonrpc: “2.0”,
    method: “receiveVideoFrom”,
    params:{
    sender: name,
    sdpOffer: pc.localDescription.sdp,
    },
    sdp” : name,
    }
    }
    sendMessage(msg);
    });
    });
    }

    return pc;
    }
    /**

  • @param {*} name

  • @param {*} candidate
    */
    export function addIceCandidate(name, candidate) {
    // const sp = name.split(’_’)[0];
    let pc = pcArray[name] || pcArray[${name}_CAMERA] ; // || pcArray[sp];
    if (pc) {
    pc.addIceCandidate(candidate);
    }
    }

/**
*
*

  • @param {*} name
  • @param {*} sdp
  • @param {*} callback
    */
    export function ProcessAnswer(name, sdp, callback) {
    let pc = pcArray[name] || pcArray[${name}_CAMERA];
    if (pc) {
    let answer = {
    ‘type’: ‘answer’,
    ‘sdp’: sdp,
    };
    pc.setRemoteDescription(new RTCSessionDescription(answer), () => {
    callback();
    }, err => {
    callback(err);
    });
    }
    }

/**
*
*/
export function ReleaseMediaSource() {
if (localstream) {
localstream.release();
localstream = null;
}
if (pcArray !== null) {
for (let mem in pcArray) {
pcArray[mem].close();
delete pcArray[mem];
}
}
}

/**
*
*/
export function toggleAudio() {
if (localstream) {
isEnableAudio = !isEnableAudio;
localstream.getAudioTracks().forEach((track) => {
track.enabled = isEnableAudio;
});
}
return isEnableAudio;
}

/**
*
*/
export function toggleVideo() {
if (localstream) {
isEnableVideo = !isEnableVideo;
localstream.getVideoTracks().forEach((track) => {
track.enabled = isEnableVideo;
});
}
return isEnableVideo;
}

/**
*
*
*/
export function switchVideoType() {
if (localstream) {
localstream.getVideoTracks().forEach(track => {
track._switchCamera();
});
}
}

function logError(error) {
}

Tengo el mismo problema, pero la pantalla negra es variable.

@yaiceltg
I have same issue , how you solve it?

Any update guys? I am having the same issue!

looks like codecs issues
try to check sdp for codes lines
also if you are using h264 make sure device can handle stream profile