Taking snapshot from video stream and save to a directory

Hi,

I'm using the video player component to stream video from a url created by streaming a webcam through VLC player. I tried binding the following to a button press but it doesn't work.

	videoPlayer = event.source.parent.getComponent('VideoPlayer')
	
	snapshot = videoPlayer.takeSnapshot()
	
	filePath = 'C:/Users/EngineeringVM/Documents/Images/snapshot.jpg'
	system.file.writeFile(filePath, snapshot)

What's the correct approach to do so?

In perspective, system.file.writeFile is gateway scoped, so if the file is getting written. you'll find it there. Perspective does not have access to the local file structure.

Here are a couple of related threads that could be of use here:

Since when does Perspective use event.source ??

1 Like

@J_Ch77: This post is tagged as Perspective; did you buy chance mean Vision? If so, that's a completely different answer.

No, I mean Perspective.

1 Like

So:

  1. The video player does not have a method takeSnapshot.
  2. You can't directly write files on the user's behalf even if it did, because browsers won't let you (as mentioned).
  3. You don't use event.source in Perspective (as mentioned).

What you're asking for is fundamentally not possible at the moment. If we were to add a 'snapshot' functionality to the video player, it would have to return a byte array that you could then prompt the user to download using system.perspective.download. But without us introducing that function first party, you don't have many options. Perhaps @victordcq could come up with a way to 'inject' a button via the markdown component that opened the videoplayer with a dedicated print stylesheet; that's the trick the power chart uses to "print" itself.

i'll take a look next week, shouldnt be to hard i think

Any updates on this?

right

i had somthing setup, but the resolution wasnt perfect yet, the button wasnt sizing properly...
and my designer keeps freezing when i tried to export it so it got delayed xd

i havent maanged to fix these problems yet but i can past the code in here i guess instead of providng a demo.
i hope its not related to the js injection, cuz if it is it might break your designer too xd causion is adviced if you cant wait

{
  "custom": {},
  "params": {},
  "props": {},
  "root": {
    "children": [
      {
        "meta": {
          "domId": "videoplayerId",
          "name": "VideoPlayer"
        },
        "position": {
          "height": 288,
          "width": 512,
          "x": 26,
          "y": 87
        },
        "props": {
          "source": "/main/test.mp4"
        },
        "type": "ia.display.video-player"
      },
      {
        "meta": {
          "name": "Markdown"
        },
        "position": {
          "height": 50,
          "width": 418,
          "x": 36,
          "y": 409
        },
        "propConfig": {
          "props.source": {
            "binding": {
              "config": {
                "expression": "{../VideoPlayer.meta.domId}"
              },
              "transforms": [
                {
                  "code": "\t#make the propName the key to write too in the view.custom \n\t\n\tcode \u003d  \"\"\"\u003cdiv style\u003d\\\"width:100%; height:100%;\\\"\u003e\u003cbutton style\u003d\\\"width:100%; height:100%;\\\" onclick\u003d\\\" let canvas \u003d document.createElement(\u0027canvas\u0027);\n\t\tlet video \u003d document.querySelector(\u0027#\"\"\"+value+\"\"\" video\u0027);\t\n\t\tconsole.log(video);\t\n\t\tconst rect \u003d video.getBoundingClientRect();\n\t\tcanvas.width \u003d 1980;\n\t\tcanvas.height \u003d 1200;\t\t\n\t\tlet ctx \u003d canvas.getContext(\u00272d\u0027);\n\t\tctx.drawImage( video, 0,0 );\t\t\n\t\tlet imageURL \u003d canvas.toDataURL(\u0027image/png\u0027);\n\t\tconsole.log(imageURL);\n\t\tconst contentType \u003d \u0027image/png\u0027;\t\t\n\t\tconst byteCharacters \u003d atob(imageURL.substr(`data:${contentType};base64,`.length));\n\t\tconst byteArrays \u003d [];\t\t\n\t\tfor (let offset \u003d 0; offset \u003c byteCharacters.length; offset +\u003d 1024) {\n\t\t    const slice \u003d byteCharacters.slice(offset, offset + 1024);\t\t\n\t\t    const byteNumbers \u003d new Array(slice.length);\n\t\t    for (let i \u003d 0; i \u003c slice.length; i++) {\n\t\t        byteNumbers[i] \u003d slice.charCodeAt(i);\n\t\t    }\t\t\n\t\t    const byteArray \u003d new Uint8Array(byteNumbers);\t\t\n\t\t    byteArrays.push(byteArray);\n\t\t}\n\t\tconst blob \u003d new Blob(byteArrays, {type: contentType});\n\t\tconst blobUrl \u003d window.URL.createObjectURL(blob);\t\t\n\t\twindow.open(blobUrl, \u0027_blank\u0027);\t\t  \n\t\\\" \u003e screenshot \u003c/button\u003e\u003c/div\u003e\"\"\".replace(\"\\n\", \"\").replace(\"\\t\", \"\")\n\treturn code\n\t",
                  "type": "script"
                }
              ],
              "type": "expr"
            }
          }
        },
        "props": {
          "markdown": {
            "escapeHtml": false
          },
          "style": {
            "classes": "fixMarkdownElementHeight"
          }
        },
        "type": "ia.display.markdown"
      }
    ],
    "meta": {
      "name": "root"
    },
    "type": "ia.container.coord"
  }
}