FFmpeg 解码出来 AVFrame 后,要渲染出来正确的图像,还是有不少问题的,下面来看看几种常见的渲染方式吧(以yuv格式为例)。
使用 AVFrame 的 width,height 直接渲染
在 width 和 lineSize 不一样的情况下,渲染出来的图像会有乱码。
int width - frame->width;
int width = frame->height;
glTexImage2D(texture_y, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[0]);
glTexImage2D(texture_u, 0, GL_LUMINANCE, width/2, height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[1]);
glTexImage2D(texture_v, 0, GL_LUMINANCE, width/2, height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[2]);
使用 AVFrame 的 lineSize,height 直接渲染
在 width 和 lineSize 不一样的情况下,渲染出来的图像会有绿边。
int width - frame->width;
int width = frame->height;
glTexImage2D(texture_y, 0, GL_LUMINANCE, frame->linesize[0], height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[0]);
glTexImage2D(texture_u, 0, GL_LUMINANCE, frame->linesize[1], height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[1]);
glTexImage2D(texture_v, 0, GL_LUMINANCE, frame->linesize[2], height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[2]);
裁剪有效数据后渲染
直接裁剪内存,性能开销比较大。
int w - frame->width;
int h = frame->height;
if(y == nullptr){
y = new unsigned char[w*h];
}
if(u == nullptr){
u = new unsigned char[w*h/4];
}
if(v == nullptr){
v = new unsigned char[w*h/4];
}}
int l1 = frame->linesize[0];
int l2 = frame->linesize[1];
int l3 = frame->linesize[2];
for(int i= 0; i < h ; i++)
{
memcpy(y + w*i,frame->data[0] + l1* i, sizeof( unsigned char)*w);
}
for(int i= 0 ; i < h/2 ; i++)
{
memcpy(u + w/2*i,frame->data[1] + l2 * i, sizeof(unsigned char)*w/2);
memcpy(v + w/2*i,frame->data[2] + l3 * i, sizeof(unsigned char)*w/2);
}
glTexImage2D(texture_y, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, y);
glTexImage2D(texture_u, 0, GL_LUMINANCE, w/2, h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, u);
glTexImage2D(texture_v, 0, GL_LUMINANCE, w/2, h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, v);
渲染后再进行裁剪
按 lineSize 去渲染,渲染的时候需要改变纹理坐标。
int width - frame->width;
int width = frame->height;
glTexImage2D(texture_y, 0, GL_LUMINANCE, frame->linesize[0], height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[0]);
glTexImage2D(texture_u, 0, GL_LUMINANCE, frame->linesize[1], height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[1]);
glTexImage2D(texture_v, 0, GL_LUMINANCE, frame->linesize[2], height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[2]);
float textureCoords[] = {
0.0f, 0.0f, // bottom left
0.0f, 1.0f, // top left
1.0f, 1.0f, // top right
1.0f, 0.0f, // bottom right
};
textureCoords[5] = 1.0f * width/frame->linesize[0];
textureCoords[7] = 1.0f * width/frame->linesize[0];
glBindBuffer(GL_ARRAY_BUFFER, textureVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoords), textureCoords, GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
通过对比上面的四种渲染方式,我们发现第四种不仅最高效,而且没有异常问题,因此值得推荐。