| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
JavaScript real-time video processing using Canvas (2D, WebGL).
Because the web is not primarily designed for video processing, not many APIs exist that can support us here. But why would we even want to do this? Use cases include:
Sadly, there is no way for us to know when the browser renders the next video frame. There is a timeupdate event on the video element which at first sight might seem like a solution. But (presumably for performance reasons?), that event triggers unpredictably and therefore does not ensure that every frame will be captured (see video.js#4322, this Bugzilla bug).
Instead, we have to rely on the browser render cycle. We can use a requestAnimationFrame() loop to constantly process the current video frame.
In the test case implementations, we just go ahead and process every frame, no matter whether it actually changed. Further optimizations might help reduce the load on the main thread.
There is no direct way to read raw pixels from a video, or a video frame. Instead:
Side note: There is an API that allows us to get the current video frame as an image named createImageBitmap. Sadly, we cannot process this image any futher but would have to go through the canvas again. Adding to that, createImageBitmap() seems to be really really slow.
The test case implementations do not yet consider multi-threaded solutions like OffscreenCanvas, primarily due to the still very bad browser support.
Time for honesty: I have no clue about WebGL. It took me a few days to copy-paste and trial-error (mostly error) together those both test cases using WebGL. Resources I used include:
When using video elements, there is a bunch of browser behaviour that we need to keep in mind. Interesting here is:
As the test video file, we use the "Big Buck Bunny" short animated movie, in particular the 2D Full HD (1920x1080) 30fps version.
Source:
- Website for downloads: http://bbb3d.renderfarming.net/download.html
- Specific URL for video: http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4
The video file got prepared by
Of course, we want to get meaningful and consistent results when performance analysis each use case implementation. The following has been done to ensure this:
While all this certainly helps getting solid test results, there will always be things out of our control, such as:
All the performance profiling results documented below ran on the following system:
| Area | Details |
|---|---|
| CPU | Intel Core i7 8700K 6x 3.70Ghz |
| RAM | 32GB DDR4-3200 DIMM CL16 |
| GPU | NVIDIA GeForce GTX 1070 8GB |
| Storage | System: 512GB NVMe M.2 SSD, Project: 2TB 7.200rpm HDD |
| Operating System | Windows 10 Pro, Version 1909, Build 18363.778 |
Within each test case implementation, the start-analysis.bin.ts script is responsible for executing the performance analysis and writing the results onto the disk.
In particular, it follows these steps:
| Step | Description |
|---|---|
| 1 | Start the server that serves the frontend application build locally |
| 2 | Start the browser, and navigate to the URL serving the frontend application |
| 3 | Start the browser performance profiler recording |
| - | Wait for the test to finish |
| 4 | Stop the browser performance profiler recording |
| 5 | Write results to disk |
| 6 | Close browser |
| 7 | Close server |
Internally, we use Puppeteer to control a browser, and use the native NodeJS server API to serve the fronted to that browser.
Heads up!
For some reason, Chrome produces extremely high profiling values when running in headless mode. Thus, all tests are being executed with headless mode disabled.
To run a performance analysis on a use case, follow these steps:
The script will create the following two files within the results folder:
We are running the performance analysis with the following parameters:
The following table shows a short test summary. See further chapters for more details.
| Test case | Duration | Render duration | Extract duration | Comparison (duration) |
|---|---|---|---|---|
| 2D Canvas | ~6.64ms | ~0.49ms | ~6.15ms | 100% (baseline) |
| WebGL Canvas (Variant 1) | ~4.06ms | ~0.50ms | ~3.57ms | 61.14% |
| WebGL Canvas (Variant 2) | ~4.05ms | ~0.49ms | ~3.56ms | 61.00% |
While the performnace improvement is alright, the improvement in actual numbers - here possibly around 2.5ms - is a good reason to switch to a WebGL-based solution, especially when keeping the usual frame budget (16.66ms) in mind.
In this test case, we use a simple 2D canvas to render the video frame into and extract raw pixels from.
Implementation pointer: Video Processor
The following chart shows that durations are generally follow an average, although quite a few spikes in both directions exist at times.
The average duration is around 6.6ms to 6.7ms, with a few durations being slightly faster and some durations being significantly slower.
Rendering a video frame into a 2D canvas is generally very fast, taking about 0.5ms. A few times, rendering happens faster, and at times very slowly. Compared to the whole duration, the rendering step only accounts for a small amount of the overall time.
Reading raw pixels from an image rendered into a 2D canvas is generally very slow, taking between 6.1ms and 6.2ms. A few times, the pixel extraction happens a bit faster, other times it takes up considerable more time. Overall, this step is the main reason for the overall slow process.
This tracing profile looks fairly clean, the GPU access time can be clearly seen here.
In this test case, we use a WebGL canvas to render the video frame into and extract raw pixels from.
Implementation pointer: Video Processor
The following chart shows that durations are generally follow an average, although very few spikes in both directions exist at times.
The average duration is around 4.0ms to 4.1ms, with a few durations being slightly faster and some durations being significantly slower.
Rendering a video frame into a 2D canvas is generally very fast, taking about 0.5ms. A few times, rendering happens faster, and at times very slowly. Compared to the whole duration, the rendering step only accounts for a small amount of the overall time.
Reading raw pixels from an image rendered into a 2D canvas is generally slow, taking between 3.5ms and 3.6ms. A few times, the pixel extraction happens a tiny bit faster, other times it takes up considerable more time. Overall, this step is the main reason for the overall slow process.
This tracing profile looks fairly clean, the GPU access time can be clearly seen here.
In this test case, we use a WebGL canvas to render the video frame into and extract raw pixels from.
Implementation pointer: Video Processor
The following chart shows that durations are generally follow an average, although very few spikes in both directions exist at times.
The average duration is around 4.0ms to 4.1ms, with a few durations being slightly faster and some durations being significantly slower.
Rendering a video frame into a 2D canvas is generally very fast, taking about 0.5ms. A few times, rendering happens faster, and at times very slowly. Compared to the whole duration, the rendering step only accounts for a small amount of the overall time.
Reading raw pixels from an image rendered into a 2D canvas is generally slow, taking between 3.5ms and 3.6ms. A few times, the pixel extraction happens a tiny bit faster, other times it takes up considerable more time. Overall, this step is the main reason for the overall slow process.
This tracing profile looks fairly clean, the GPU access time can be clearly seen here.
| Back | FazBrowse Home | New Git URL |