yuv-render
A WebGL-based YUV video frame renderer providing I420 format video data rendering capabilities, featuring texture management, canvas control, and resource release functions.
Features
- Supports HTMLCanvasElement container binding
- Automatically initializes WebGL rendering environment
- Supports dynamic size adjustment
- Implements YUV triple-channel texture separated rendering
- Provides resource cleanup interface
Class Structure
typescript
class YUVRender {
// Properties
el: HTMLCanvasElement
private webglContext: WebGLRenderingContext | null
private yTexture: WebGLTexture | null
private uTexture: WebGLTexture | null
private vTexture: WebGLTexture | null
// Constructor
constructor(el: HTMLCanvasElement)
// Accessors
get height(): number
get width(): number
// Methods
setDimension(width: number, height: number): void
render(data: Uint8Array): void
clear(): void
dispose(): void
}API Documentation
constructor(el: HTMLCanvasElement)
Initializes the renderer, creates WebGL context and configures shader program
- Parameters:
el: Target canvas element
- Initialization process:
- Obtain WebGL context
- Create shader program
- Initialize buffers
- Create Y/U/V three texture channels
get width(): number
Gets current canvas width
- Returns:
numbertype canvas width value
get height(): number
Gets current canvas height
- Returns:
numbertype canvas height value
setDimension(width: number, height: number): void
Sets canvas display dimensions
- Parameters:
width: Canvas width in pixelsheight: Canvas height in pixels
- Behavior:
- Directly modifies canvas.width/canvas.height property
- Triggers WebGL viewport reset
render(data: Uint8Array): void
Renders YUV data to canvas
- Parameters:
data: I420 formatted video data (Uint8Array)
- Data processing flow:
- Notes:
- Data must comply with I420 format specification
- Dimensions must be set through setDimension in advance
- Texture data will be re-uploaded on each call
clear(): void
Clears canvas content
- Behavior:
- Calls WebGL's clear(COLOR_BUFFER_BIT) method
- Used to clear current frame rendering results
dispose(): void
Resource cleanup method
- Actions performed:
- Clear canvas content (call clear())
- Actively release WebGL context (WEBGL_lose_context)
- Remove canvas element
- Nullify all references
- Notes:
- Should be called when component is destroyed
- Instance becomes non-reusable after invocation
Usage Example
View Code
vue
<script setup lang="ts">
import { useTemplateRef, onMounted, shallowReactive } from 'vue';
import { VPButton } from 'vitepress/theme';
import { YUVRender } from 'yuv-render';
const scope = shallowReactive({
waiting: false
});
const canvas = useTemplateRef<HTMLCanvasElement>('canvas');
let yuv: YUVRender;
onMounted(()=> {
const el = canvas.value!;
yuv = new YUVRender(el);
yuv.setDimension(768, 320);
});
const start = () => {
scope.waiting = true;
fetch(`${import.meta.env.BASE_URL}frame.json`)
.then((res: Response) => res.json())
.then((frameArray: ArrayBuffer) => {
scope.waiting = false;
yuv.render(new Uint8Array(frameArray));
});
};
</script>
<template>
<div class="buttons">
<VPButton
text="Render"
:disabled="scope.waiting"
@click="start()"
/>
</div>
<div :class="{ 'yuv-render': true, 'v-waiting': scope.waiting }"
>
<canvas ref="canvas" />
<div className="v-loading-spinner" />
</div>
</template>
<style scoped lang="stylus">
.buttons {
margin-bottom: 16px;
}
.yuv-render
display block
box-sizing border-box
background-color #000
position relative
padding 0
width 768px
height 320px
canvas
position absolute
width 100%
height 100%
top 0
left 0
$primary-background-color = #2B333F
$primary-background-transparency = 0.7
$secondary-background-color = lighten($primary-background-color, 33%)
$secondary-background-transparency = 0.5
.v-loading-spinner
display none
position absolute
top 50%
left 50%
margin -25px 0 0 -25px
opacity 0.85
// Need to fix centered page layouts
text-align left
border 6px solid rgba($primary-background-color, $primary-background-transparency)
// border: 6px solid rgba(43, 51, 63, 0.5);
box-sizing border-box
background-clip padding-box
width 50px
height 50px
border-radius 25px
visibility hidden
.v-seeking .v-loading-spinner, .v-waiting .v-loading-spinner
display block
// add a delay before actual show the spinner
animation v-spinner-show 0s linear 0.3s forwards
.v-loading-spinner:before, .v-loading-spinner:after
content ''
position absolute
margin -6px
box-sizing inherit
width inherit
height inherit
border-radius inherit
// Keep 100% opacity so they don't show through each other
opacity 1
border inherit
border-color transparent
border-top-color white
// only animate when showing because it can be processor heavy
.v-seeking .v-loading-spinner:before, .v-seeking .v-loading-spinner:after, .v-waiting .v-loading-spinner:before, .v-waiting .v-loading-spinner:after
-webkit-animation v-spinner-spin 1.1s cubic-bezier(0.6, 0.2, 0, 0.8) infinite, v-spinner-fade 1.1s linear infinite
animation v-spinner-spin 1.1s cubic-bezier(0.6, 0.2, 0, 0.8) infinite, v-spinner-fade 1.1s linear infinite
.v-seeking .v-loading-spinner:before, .v-waiting .v-loading-spinner:before
border-top-color rgb(255, 255, 255)
.v-seeking .v-loading-spinner:after, .v-waiting .v-loading-spinner:after
border-top-color rgb(255, 255, 255)
-webkit-animation-delay 0.44s
animation-delay 0.44s
@keyframes v-spinner-show
to
visibility visible
@keyframes v-spinner-show
to
visibility visible
@keyframes v-spinner-spin
100%
transform rotate(360deg)
@keyframes v-spinner-spin
100%
-webkit-transform rotate(360deg)
@keyframes v-spinner-fade
0%
border-top-color $secondary-background-color
20%
border-top-color $secondary-background-color
35%
border-top-color white
60%
border-top-color $secondary-background-color
100%
border-top-color $secondary-background-color
@keyframes v-spinner-fade
0%
border-top-color $secondary-background-color
20%
border-top-color $secondary-background-color
35%
border-top-color white
60%
border-top-color $secondary-background-color
100%
border-top-color $secondary-background-color
</style>