js渲染yuv或rgb数据
有时候我们在web端拿到了一份解码后的数据,不管是yuv还是rgb,都想先验证下数据是否是正确的,那么就需要在前端渲染出来看看效果 以下分别提供了这两种方式的验证方法
RGB888
我们可以先拿到canvas元素,进而将我们的rgb数据通过putImageData设置给canvas去渲染,其中核心代码如下
let data;
let width, height;
let canvas = document. getElementById ( "canvas-test" ) ;
let ctx = canvas. getContext ( "2d" ) ;
var imgData = ctx. createImageData ( width, height) ;
for ( var i = 0 , j = 0 ; i < imgData. data. length; i += 4 , j += 3 ) {
imgData. data[ i + 0 ] = data[ j + 0 ] ;
imgData. data[ i + 1 ] = data[ j + 1 ] ;
imgData. data[ i + 2 ] = data[ j + 2 ] ;
imgData. data[ i + 3 ] = 255 ;
}
ctx. putImageData ( imgData, 0 , 0 ) ;
YUV420P
以下js代码是一个yuv420p数据的webgl渲染代码,是摘自网上的,具体出处也不得而知了,本人亲测可用 里面关于片元着色器的YUV转RGB的矩阵具体是怎么推导而来的,笔者希望有大神解惑一下
function Texture ( gl ) {
this . gl = gl;
this . texture = gl. createTexture ( ) ;
gl. bindTexture ( gl. TEXTURE_2D , this . texture) ;
gl. texParameteri ( gl. TEXTURE_2D , gl. TEXTURE_MAG_FILTER , gl. LINEAR ) ;
gl. texParameteri ( gl. TEXTURE_2D , gl. TEXTURE_MIN_FILTER , gl. LINEAR ) ;
gl. texParameteri ( gl. TEXTURE_2D , gl. TEXTURE_WRAP_S , gl. CLAMP_TO_EDGE ) ;
gl. texParameteri ( gl. TEXTURE_2D , gl. TEXTURE_WRAP_T , gl. CLAMP_TO_EDGE ) ;
}
Texture . prototype. bind = function ( n, program, name ) {
var gl = this . gl;
gl. activeTexture ( [ gl. TEXTURE0 , gl. TEXTURE1 , gl. TEXTURE2 ] [ n] ) ;
gl. bindTexture ( gl. TEXTURE_2D , this . texture) ;
gl. uniform1i ( gl. getUniformLocation ( program, name) , n) ;
} ;
Texture . prototype. fill = function ( width, height, data ) {
var gl = this . gl;
gl. bindTexture ( gl. TEXTURE_2D , this . texture) ;
gl. texImage2D ( gl. TEXTURE_2D , 0 , gl. LUMINANCE , width, height, 0 , gl. LUMINANCE , gl. UNSIGNED_BYTE , data) ;
} ;
function WebGLPlayer ( canvas, options ) {
this . canvas = canvas;
this . gl = canvas. getContext ( "webgl" ) || canvas. getContext ( "experimental-webgl" ) ;
this . initGL ( options) ;
}
WebGLPlayer . prototype. initGL = function ( options ) {
if ( ! this . gl) {
console. log ( "[ER] WebGL not supported." ) ;
return ;
}
var gl = this . gl;
gl. pixelStorei ( gl. UNPACK_ALIGNMENT , 1 ) ;
var program = gl. createProgram ( ) ;
var vertexShaderSource = [
"attribute highp vec4 aVertexPosition;" ,
"attribute vec2 aTextureCoord;" ,
"varying highp vec2 vTextureCoord;" ,
"void main(void) {" ,
" gl_Position = aVertexPosition;" ,
" vTextureCoord = aTextureCoord;" ,
"}"
] . join ( "\n" ) ;
var vertexShader = gl. createShader ( gl. VERTEX_SHADER ) ;
gl. shaderSource ( vertexShader, vertexShaderSource) ;
gl. compileShader ( vertexShader) ;
var fragmentShaderSource = [
"precision highp float;" ,
"varying lowp vec2 vTextureCoord;" ,
"uniform sampler2D YTexture;" ,
"uniform sampler2D UTexture;" ,
"uniform sampler2D VTexture;" ,
"const mat4 YUV2RGB = mat4" ,
"(" ,
" 1.1643828125, 0, 1.59602734375, -.87078515625," ,
" 1.1643828125, -.39176171875, -.81296875, .52959375," ,
" 1.1643828125, 2.017234375, 0, -1.081390625," ,
" 0, 0, 0, 1" ,
");" ,
"void main(void) {" ,
" gl_FragColor = vec4( texture2D(YTexture, vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB;" ,
"}"
] . join ( "\n" ) ;
var fragmentShader = gl. createShader ( gl. FRAGMENT_SHADER ) ;
gl. shaderSource ( fragmentShader, fragmentShaderSource) ;
gl. compileShader ( fragmentShader) ;
gl. attachShader ( program, vertexShader) ;
gl. attachShader ( program, fragmentShader) ;
gl. linkProgram ( program) ;
gl. useProgram ( program) ;
if ( ! gl. getProgramParameter ( program, gl. LINK_STATUS ) ) {
console. log ( "[ER] Shader link failed." ) ;
}
var vertexPositionAttribute = gl. getAttribLocation ( program, "aVertexPosition" ) ;
gl. enableVertexAttribArray ( vertexPositionAttribute) ;
var textureCoordAttribute = gl. getAttribLocation ( program, "aTextureCoord" ) ;
gl. enableVertexAttribArray ( textureCoordAttribute) ;
var verticesBuffer = gl. createBuffer ( ) ;
gl. bindBuffer ( gl. ARRAY_BUFFER , verticesBuffer) ;
gl. bufferData ( gl. ARRAY_BUFFER , new Float32Array ( [ 1.0 , 1.0 , 0.0 , - 1.0 , 1.0 , 0.0 , 1.0 , - 1.0 , 0.0 , - 1.0 , - 1.0 , 0.0 ] ) , gl. STATIC_DRAW ) ;
gl. vertexAttribPointer ( vertexPositionAttribute, 3 , gl. FLOAT , false , 0 , 0 ) ;
var texCoordBuffer = gl. createBuffer ( ) ;
gl. bindBuffer ( gl. ARRAY_BUFFER , texCoordBuffer) ;
gl. bufferData ( gl. ARRAY_BUFFER , new Float32Array ( [ 1.0 , 0.0 , 0.0 , 0.0 , 1.0 , 1.0 , 0.0 , 1.0 ] ) , gl. STATIC_DRAW ) ;
gl. vertexAttribPointer ( textureCoordAttribute, 2 , gl. FLOAT , false , 0 , 0 ) ;
gl. y = new Texture ( gl) ;
gl. u = new Texture ( gl) ;
gl. v = new Texture ( gl) ;
gl. y . bind ( 0 , program, "YTexture" ) ;
gl. u . bind ( 1 , program, "UTexture" ) ;
gl. v . bind ( 2 , program, "VTexture" ) ;
}
WebGLPlayer . prototype. renderFrame = function (
videoFrame,
width,
height,
uOffset,
vOffset
) {
if ( ! this . gl) {
console. log ( "[ER] Render frame failed due to WebGL not supported." ) ;
return ;
}
var gl = this . gl;
gl. viewport ( 0 , 0 , gl. canvas. width, gl. canvas. height) ;
gl. clearColor ( 0.0 , 0.0 , 0.0 , 0.0 ) ;
gl. clear ( gl. COLOR_BUFFER_BIT ) ;
gl. y. fill ( width, height, videoFrame. subarray ( 0 , uOffset) ) ;
gl. u. fill ( width >> 1 , height >> 1 , videoFrame. subarray ( uOffset, uOffset + vOffset) ) ;
gl. v. fill ( width >> 1 , height >> 1 , videoFrame. subarray ( uOffset + vOffset, videoFrame. length) ) ;
gl. drawArrays ( gl. TRIANGLE_STRIP , 0 , 4 ) ;
} ;
WebGLPlayer . prototype. fullscreen = function ( ) {
var canvas = this . canvas;
if ( canvas. RequestFullScreen) {
canvas. RequestFullScreen ( ) ;
} else if ( canvas. webkitRequestFullScreen) {
canvas. webkitRequestFullScreen ( ) ;
} else if ( canvas. mozRequestFullScreen) {
canvas. mozRequestFullScreen ( ) ;
} else if ( canvas. msRequestFullscreen) {
canvas. msRequestFullscreen ( ) ;
} else {
alert ( "This browser doesn't supporter fullscreen" ) ;
}
} ;
WebGLPlayer . prototype. exitfullscreen = function ( ) {
if ( document. exitFullscreen) {
document. exitFullscreen ( ) ;
} else if ( document. webkitExitFullscreen) {
document. webkitExitFullscreen ( ) ;
} else if ( document. mozCancelFullScreen) {
document. mozCancelFullScreen ( ) ;
} else if ( document. msExitFullscreen) {
document. msExitFullscreen ( ) ;
} else {
alert ( "Exit fullscreen doesn't work" ) ;
}
}
参考