Assigning stream video to player entity using peerjs

Hi, I’m not a pro and I got stuck for couple weeks on having the stream video through peerjs into player entity. The idea is to get connected into 1 “public” room and showing each of webcam stream which assigned to each player entity.

The basic flow are connect to peerjs socket.io server, emit player to the room, getusermedia, each connected user will get call.on(stream), answer with (stream), assign other user stream into videotexture, assign videotexture to cloned material and assign cloned material into cloned entity.

var Network = pc.createScript('network');

Network.id = null;
Network.socket = null;

Network.prototype.initialize = function() {
    this.player = this.app.root.findByName("FPS Character Controller");
    this.other = this.app.root.findByName("Other FPS Character Controller");
    
    var url_string = window.location.href;
    var url = new URL(url_string);
    var idaw = url.searchParams.get("idaw");

          var socket = io.connect('https://someserver/',{
            transports: ["polling"]
            });
                    Network.socket = socket;

                    const myPeer = new Peer(idaw, {
                    host: 'someserver',
                    port: 443,
                    path:'/peerjs',
                    secure: true
                    });

                    const peers = {};
                    var self = this;

                    navigator.mediaDevices.getUserMedia({
                    video: true,
                    audio: true
                    }).then(stream => {

                    myPeer.on('call', call => {
                        call.answer(stream);
                        const video = document.createElement('video');
                        call.on('stream', userVideoStream => {
                        addVideoStream(video, userVideoStream, call.id);
                        });
                        
                    });

                    socket.on('user-connected', (userId) => {
                        connectToNewUser(userId, stream);
                    });

                    });


                    socket.on ('playerData', function (data) {
                        self.initializePlayers(data);
                    });

                    socket.on ('playerJoined', function (data) {
                        self.addPlayer(data);
                    });

                    socket.on ('playerMoved', function (data) {
                        self.movePlayer(data);
                    });

                    socket.on ('killPlayer', function (data) {
                        self.removePlayer(data);
                        if (peers[data]) peers[data].close();
                    });

                    myPeer.on('open', id => {
                    socket.emit('join-room', 'public', id);
                    socket.emit ('initialize', id);
                    });

                    

                    function connectToNewUser(userId, stream) {
                        const call = myPeer.call(userId, stream);
                        const video = document.createElement('video');
                        call.on('stream', userVideoStream => {
                            addVideoStream(video, userVideoStream, userId);
                        });
                        call.on('close', () => {
                            video.remove();
                        });

                        peers[userId] = call;
                        console.log('connect to new user');
                    }

                    function addVideoStream(video, stream, userId) {
                      var videoTexture = new pc.Texture(this.app.graphicsDevice, {
                        format: pc.PIXELFORMAT_R8_G8_B8,
                        minFilter: pc.FILTER_LINEAR_MIPMAP_LINEAR,
                        magFilter: pc.FILTER_LINEAR,
                        addressU: pc.ADDRESS_CLAMP_TO_EDGE,
                        addressV: pc.ADDRESS_CLAMP_TO_EDGE,
                        mipmaps: true,
                        autoMipmap: true
                    });
                    videoTexture.name = "vidTexture"+userId;
                    this.app.root.addChild(videoTexture);
                    
                    //set video to videotexture
                    video.srcObject = stream;
                    video.addEventListener('loadedmetadata', () => {
                    this.app.assets.find("vidTexture"+userId).setSource(video);
                    video.play();
                    });


                    // Get the material that we want to play the video on and assign the new video
                    // texture to it.
                    var newMaterial = this.app.root.findByName('Purple').clone();
                    newMaterial.emissiveMap = this.app.assets.find("vidTexture"+userId);
                    newMaterial.name = "vidMaterial"+userId;
                    this.app.root.addChild(newMaterial);
                    newMaterial.update();

                    //assign material to entity by userid
                    var liveEntity = this.app.root.findByName(userId).findByName('videoLive').clone();
                    liveEntity.name = "liveEntity"+userId;
                    liveEntity.render.meshInstances[0].material = this.app.assets.find("vidMaterial"+userId);

                    this.app.root.findByName(userId).addChild(liveEntity);
                    }

    
};

Network.prototype.initializePlayers = function (data) {
    this.players = data.players;
    Network.id = data.id;

    for(var id in this.players){
        if(id != Network.id){
            this.players[id].entity = this.createPlayerEntity(this.players[id]);
        }
    }
    this.initialized = true;
    console.log('initialized');
};

Network.prototype.addPlayer = function (data) {
    this.players[data.id] = data;
    this.players[data.id].entity = this.createPlayerEntity(data);
};

Network.prototype.movePlayer = function (data) {
    if (this.initialized && !this.players[data.id].deleted) {
        this.players[data.id].entity.rigidbody.teleport(data.x, data.y, data.z);
    }
};

Network.prototype.removePlayer = function (data) {
    if (this.players[data].entity) {
        this.players[data].entity.destroy ();
        this.players[data].deleted = true;
    }
};

Network.prototype.createPlayerEntity = function (data) {
    var newPlayer = this.other.clone();
    newPlayer.enabled = true;

    newPlayer.findByName("TextOther").element.text = data.id;

    this.other.getParent().addChild(newPlayer);

    if (data){
        newPlayer.rigidbody.teleport(data.x, data.y, data.z);
       
    return newPlayer;
    }
};

This is the entity to be cloned :
image

Please help, it means so much. Thanks.

What issue exactly are you encountering? Assigning the stream into the videotexture? Assigning the videotexture to a cloned material? Assigning the material into an entity?

All I’ve got is the peer connection is ok. But assigning stream to videotexture, material and entity is not working. Is there anything wrong with my code? Thanks.

Hi @Hypnotized_Introvert,

I think this line won’t work since setSource isn’t a method of an asset, and this method returns an asset:

this.app.assets.find("vidTexture"+userId).setSource(video);

Try studying the following project on how to go from a video stream to uploading it to a video texture:
https://developer.playcanvas.com/en/tutorials/video-textures/

2 Likes

Hey @Hypnotized_Introvert,

In addition to comparing your code with the one available on the Video Textures tutorial page as Leonidas mentioned, I’d also take a look at this forum thread, where users managed to implement something very similar. Let me know if you run into further trouble.

1 Like

I’ve also found this older project of mine that uses a web camera, if available, to render the video stream on a texture:

https://playcanvas.com/project/698371/overview/webcam-video-texture

1 Like