B4J Question [ABMaterial] Stream Webcam on ABMVideo Component

luisg2401

Member
Licensed User
Hello, after looking it up on forums and tutorial I've managed to stream my webcam on an HTML video tag using the following code:

B4X:
var video = document.getElementById('video');

            navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia;

            If(navigator.getUserMedia){
                navigator.getUserMedia({video: true}, streamWebCam, throwError);
            }

            function streamWebCam (stream) {
                window.localStream = stream;
                //NOT video.src = window.URL.createObjectURL(stream) since createObjectURL is deprecated
                video.srcObject = stream;
                video.play();
            }

            function throwError (e) {
                alert(e.name);
            }

and I've been trying to do this but with the ABMVideo component from ABMaterial, since I need the video to show on a ABMModalSheet, but the only way I've managed to make the video stream on the ABMVideo component is by running the code above at the beginning of the ConnectPage method, and then calling retreiving the src from the video tag, but this isn't ideal, because that means the camera will be opened as soon as the page loads, not when the modal is shown. Is there any way to do this?
 

Mashiane

Expert
Licensed User
Longtime User
Upvote 0

luisg2401

Member
Licensed User
In november 2017 I did a custom component to take a picture using webcam, perhaps looking at some of the code might help. Here is the link, https://www.b4x.com/android/forum/threads/abmaterial-mashcameraplain.86132/#content

There you click a button to take a pick. I like your code to take a video, please share the rest of it. Thanks.

Thanks for sharing that custom component, I'll make sure to take a look at it later, but I've managed to solve my issue using javascript and jquery to add the html objects to a modal.

sure, I can share the code, since it may help someone that may have the same problems that I had:


As I said, I created a modal for taking the picture, when I click the button to open it, I use this code:

B4X:
Sub btnpicture_Clicked(Target As String)
    Dim myModal As ABMModalSheet = page.ModalSheet("PictureModal")
    Dim button As ABMButton = myModal.Footer.Component("pictureOk")
    button.Enabled = False
    Log("boton al abrir modal: " & button.Enabled)
    Dim script As String = $"
                            $("#cajavideo-r2c1").append("<video id='webcam' width=320 height=240></video>");
                            $("#cajavideo-r2c2").append("<canvas id='canvas' width=640 height=480 hidden></canvas>"); 
                            $("#cajavideo-r2c2").append("<img id='foto' width=320 height=240></img>");
    "$
    page.ws.Eval(script,Null)
    asignarcamara
   
    page.ShowModalSheetAbsolute("PictureModal","20%","20%","55%", "55%")
End Sub

then, when the button to take the picture is clicked:

B4X:
Sub btnpicturemodal_Clicked(Target As String)
    Dim myModal As ABMModalSheet = page.ModalSheet("PictureModal")
    Dim button As ABMButton = myModal.Footer.Component("pictureOk")
    button.Enabled = True
    button.Refresh
    Log("boton al tomar foto: " & button.Enabled)
    Dim script As String = $"
                            var video = document.getElementById('webcam');
                            var canvas = document.getElementById('canvas');
                            var context = canvas.getContext('2d');  
                            var img = document.getElementById('foto');
                           
                            context.drawImage(video, 0, 0);
                            img.src = canvas.toDataURL();
    "$
    page.ws.Eval(script,Null)
End Sub

you can take as many pictures as you want, but the picture is only saved by clicking an 'Ok' button on the modal:

B4X:
Sub pictureOk_Clicked(Target As String)
    Dim imagen64 As String = ""
    Dim res As Future
    Dim script As String = $"
                            var img = document.getElementById('foto');
                            return img.src
    "$
    res = page.ws.EvalWithResult(script,Null)
    page.ws.Flush
    Log(res.Value)
    imagen64 = res.Value
    
    Dim su As StringUtils
    
    Dim aux2 As String = imagen64.Replace("data:image/png;base64,","")
    Dim decoded() As Byte = su.DecodeBase64(aux2)
    
    'Eliminando la foto subida previamente
    Dim listaArchivos As List = File.ListFiles (File.DirApp & "/www/" & ABMShared.AppName & "/" & SubFolder & "/temporalfolder/")
    For Each elem As String In listaArchivos
        If elem.Contains(".") Then
            Dim nombre() As String = Regex.Split("__--__", elem.Replace(".","__--__"))
            If nombre(0) = "ProfilePic" Then
                File.Delete (File.DirApp & "/www/" & ABMShared.AppName & "/" & SubFolder & "/temporalfolder/", elem)
                Log ("Eliminando: " & elem)
            End If
        End If
    Next
    
    Dim out As OutputStream = File.OpenOutput(File.DirApp & "/www/" & ABMShared.AppName & "/" & SubFolder & "/temporalfolder/", "ProfilePic.png",False)
    out.WriteBytes(decoded, 0, decoded.Length)
    out.Close
    
    eliminarobjmodal
    page.CloseModalSheet("PictureModal")
End Sub

whether Ok or Cancel is clicked on the modal, I delete the html components and delete the information from the camera (because even if you delet the <video> tag, that won't release the camera:

B4X:
Sub eliminarobjmodal
    Dim script As String = $"
                            var aux = window.localStream;
                            aux.getTracks()[0].stop();
                           
                            $("#webcam").remove();
                            $("#canvas").remove();
                            $("#foto").remove();
    "$
    page.ws.Eval(script,Null)
End Sub
 
Upvote 0

luisg2401

Member
Licensed User
Thanks a lot @luisg2401 , what happens when you call the code in your first post from a button click, doesnt it activate the camera?I could use this a great deal, you have given me a perfect idea... Ta!

oh yes, sorry, I forgot to add two important methods, when the first button is clicked, I call a method (called "asignarcamara") that opens the media:


B4X:
Sub asignarcamara()
    Dim script As String = $"
            var video = document.getElementById('webcam');
           
            navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia;

            If(navigator.getUserMedia){
                navigator.getUserMedia({video: true}, streamWebCam, throwError);
            }

            function streamWebCam (stream) {
                window.localStream = stream;
                video.srcObject = stream;
                video.play();
            }

            function throwError (e) {
                alert(e.name);
            }
    "$
    page.ws.Eval(script,Null)
End Sub

and, in order to be able to return the base64-coded image taken from the camera, it's important to change the max text message size:

B4X:
Sub SetMaxTextMessage(size As Int)
    Dim jo As JavaObject = ws
    jo = jo.GetFieldJO("session").RunMethod("getPolicy", Null)
    jo.RunMethod("setMaxTextMessageSize", Array(size))
End Sub

I call this method at the beginning of the page Websocket_Connected method (I use the websocket class given by the id) thanks to the person who answered me on another thread I opened https://www.b4x.com/android/forum/threads/message-too-large-exception.91164/reply?quote=576152:

B4X:
Private Sub WebSocket_Connected (WebSocket1 As WebSocket)
    '----------------------MODIFICATION-------------------------------
    Log("Connected")
    ws = WebSocket1
    SetMaxTextMessage(2048000)
    ...
 
Upvote 0

luisg2401

Member
Licensed User
It would be nice though if the video could be recorded in some way, lets say 20 seconds and then it stops. Any ideas?

to record video, I truly have no idea lol, but I'm pretty sure that using javascript it should be possible, there's a library called WebRTC that might help with that
 
Upvote 0
Top