您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
5. GLVideoView完成使用shader显示视频~1
发布时间:2021-06-11 16:17:08编辑:雪饮阅读()
前面实现了样本大小获取,那么样本宽高这些也应该一并获取出来:xplay/app/src/main/cpp/FFDecode.cpp:
{
#include <libavcodec/avcodec.h>
}
#include "FFDecode.h"
#include "XLog.h"
bool FFDecode::Open(XParameter para)
{
if(!para.para) return false;
AVCodecParameters *p = para.para;
//1 查找解码器
AVCodec *cd = avcodec_find_decoder(p->codec_id);
if(!cd)
{
XLOGE("avcodec_find_decoder %d failed!",p->codec_id);
return false;
}
XLOGI("avcodec_find_decoder success!");
//2 创建解码上下文,并复制参数
codec = avcodec_alloc_context3(cd);
avcodec_parameters_to_context(codec,p);
codec->thread_count = 8;
//3 打开解码器
int re = avcodec_open2(codec,0,0);
if(re != 0)
{
char buf[1024] = {0};
av_strerror(re,buf,sizeof(buf)-1);
XLOGE("%s",buf);
return false;
}
if(codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
this->isAudio = false;
}
else
{
this->isAudio = true;
}
XLOGI("avcodec_open2 success!");
return true;
}
bool FFDecode::SendPacket(XData pkt)
{
if(pkt.size<=0 || !pkt.data)return false;
if(!codec)
{
return false;
}
int re = avcodec_send_packet(codec,(AVPacket*)pkt.data);
if(re != 0)
{
return false;
}
return true;
}
//从线程中获取解码结果
XData FFDecode::RecvFrame()
{
if(!codec)
{
return XData();
}
if(!frame)
{
frame = av_frame_alloc();
}
int re = avcodec_receive_frame(codec,frame);
if(re != 0)
{
return XData();
}
XData d;
d.data = (unsigned char *)frame;
if(codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
d.size = (frame->linesize[0] + frame->linesize[1] + frame->linesize[2])*frame->height;
d.width = frame->width;
d.height = frame->height;
}
else
{
//样本字节数 * 单通道样本数 * 通道数
d.size = av_get_bytes_per_sample((AVSampleFormat)frame->format)*frame->nb_samples*2;
}
memcpy(d.datas,frame->data,sizeof(d.datas));
return d;
}
并且GLVideoView要实现接口IvideoView: 为了兼容处理被观察者,所以IvideoView还需要继承IObserver 上面关于获取样本尺寸时候需要在 上面绘制的具体实现,这里就实现在 那么其定义于: 关于三维绘制及材质的具体实现 xplay/app/src/main/cpp/XShader.h:
关于材质的三层绘制,还要细化下实现: 至于其定义: 然后在 暂时不知道具体原因,先贴图祭天
#include "XTexture.h"
void GLVideoView::SetRender(void *win)
{
view = win;
}
void GLVideoView::Render(XData data)
{
if(!view) return;
if(!txt)
{
txt = XTexture::Create();
txt->Init(view);
}
txt->Draw(data.datas,data.width,data.height);
}
#define XPLAY_GLVIDEOVIEW_H
#include "XData.h"
#include "IVideoView.h"
class XTexture;
class GLVideoView: public IVideoView {
public:
virtual void SetRender(void *win);
virtual void Render(XData data);
protected:
void *view = 0;
XTexture *txt = 0;
};
#endif //XPLAY_GLVIDEOVIEW_H
#define XPLAY_IVIDEOVIEW_H
#include "XData.h"
#include "IObserver.h"
class IVideoView:public IObserver
{
public:
virtual void SetRender(void *win) = 0;
virtual void Render(XData data) = 0;
virtual void Update(XData data);
};
#endif //XPLAY_IVIDEOVIEW_H
#define XPLAY_XDATA_H
struct XData {
unsigned char *data = 0;
unsigned char *datas[8] = {0};
int size = 0;
bool isAudio = false;
int width = 0;
int height = 0;
void Drop();
};
#endif //XPLAY_XDATA_H
#include <EGL/egl.h>
#include "XEGL.h"
#include "XLog.h"
class CXEGL:public XEGL
{
public:
EGLDisplay display = EGL_NO_DISPLAY;
EGLSurface surface = EGL_NO_SURFACE;
EGLContext context = EGL_NO_CONTEXT;
virtual void Draw()
{
if(display == EGL_NO_DISPLAY || surface == EGL_NO_SURFACE)
{
return;
}
eglSwapBuffers(display,surface);
}
virtual bool Init(void *win)
{
ANativeWindow *nwin = (ANativeWindow *)win;
//初始化EGL
//1 获取EGLDisplay对象显示设备
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(display == EGL_NO_DISPLAY)
{
XLOGE("eglGetDisplay failed!");
return false;
}
XLOGE("eglGetDisplay success!");
//2 初始化Display
if(EGL_TRUE != eglInitialize(display,0,0))
{
XLOGE("eglInitialize failed!");
return false;
}
XLOGE("eglInitialize success!");
//3 获取配置并创建surface
EGLint configSpec [] = {
EGL_RED_SIZE,8,
EGL_GREEN_SIZE,8,
EGL_BLUE_SIZE,8,
EGL_SURFACE_TYPE,EGL_WINDOW_BIT,
EGL_NONE
};
EGLConfig config = 0;
EGLint numConfigs = 0;
if(EGL_TRUE != eglChooseConfig(display,configSpec,&config,1,&numConfigs))
{
XLOGE("eglChooseConfig failed!");
return false;
}
XLOGE("eglChooseConfig success!");
surface = eglCreateWindowSurface(display,config,nwin,NULL);
//4 创建并打开EGL上下文
const EGLint ctxAttr[] = { EGL_CONTEXT_CLIENT_VERSION ,2, EGL_NONE};
context = eglCreateContext(display,config,EGL_NO_CONTEXT,ctxAttr);
if(context == EGL_NO_CONTEXT)
{
XLOGE("eglCreateContext failed!");
return false;
}
XLOGE("eglCreateContext success!");
if(EGL_TRUE != eglMakeCurrent(display,surface,surface,context))
{
XLOGE("eglMakeCurrent failed!");
return false;
}
XLOGE("eglMakeCurrent success!");
return true;
}
};
XEGL *XEGL::Get()
{
static CXEGL egl;
return &egl;
}
#define XPLAY_XEGL_H
class XEGL
{
public:
virtual bool Init(void *win) = 0;
virtual void Draw() = 0;
static XEGL *Get();
protected:
XEGL(){}
};
#endif //XPLAY_XEGL_H
#include "XLog.h"
#include <GLES2/gl2.h>
//顶点着色器glsl
#define GET_STR(x) #x
static const char *vertexShader = GET_STR(
attribute vec4 aPosition; //顶点坐标
attribute vec2 aTexCoord; //材质顶点坐标
varying vec2 vTexCoord; //输出的材质坐标
void main(){
vTexCoord = vec2(aTexCoord.x,1.0-aTexCoord.y);
gl_Position = aPosition;
}
);
//片元着色器,软解码和部分x86硬解码
static const char *fragYUV420P = GET_STR(
precision mediump float; //精度
varying vec2 vTexCoord; //顶点着色器传递的坐标
uniform sampler2D yTexture; //输入的材质(不透明灰度,单像素)
uniform sampler2D uTexture;
uniform sampler2D vTexture;
void main(){
vec3 yuv;
vec3 rgb;
yuv.r = texture2D(yTexture,vTexCoord).r;
yuv.g = texture2D(uTexture,vTexCoord).r - 0.5;
yuv.b = texture2D(vTexture,vTexCoord).r - 0.5;
rgb = mat3(1.0, 1.0, 1.0,
0.0,-0.39465,2.03211,
1.13983,-0.58060,0.0)*yuv;
//输出像素颜色
gl_FragColor = vec4(rgb,1.0);
}
);
static GLuint InitShader(const char *code,GLint type)
{
//创建shader
GLuint sh = glCreateShader(type);
if(sh == 0)
{
XLOGE("glCreateShader %d failed!",type);
return 0;
}
//加载shader
glShaderSource(sh,
1, //shader数量
&code, //shader代码
0); //代码长度
//编译shader
glCompileShader(sh);
//获取编译情况
GLint status;
glGetShaderiv(sh,GL_COMPILE_STATUS,&status);
if(status == 0)
{
XLOGE("glCompileShader failed!");
return 0;
}
XLOGE("glCompileShader success!");
return sh;
}
bool XShader::Init()
{
//顶点和片元shader初始化
//顶点shader初始化
vsh = InitShader(vertexShader,GL_VERTEX_SHADER);
if(vsh == 0)
{
XLOGE("InitShader GL_VERTEX_SHADER failed!");
return false;
}
XLOGE("InitShader GL_VERTEX_SHADER success!");
//片元yuv420 shader初始化
fsh = InitShader(fragYUV420P,GL_FRAGMENT_SHADER);
if(fsh == 0)
{
XLOGE("InitShader GL_FRAGMENT_SHADER failed!");
return false;
}
XLOGE("InitShader GL_FRAGMENT_SHADER success!");
/////////////////////////////////////////////////////////////
//创建渲染程序
program = glCreateProgram();
if(program == 0)
{
XLOGE("glCreateProgram failed!");
return false;
}
//渲染程序中加入着色器代码
glAttachShader(program,vsh);
glAttachShader(program,fsh);
//链接程序
glLinkProgram(program);
GLint status = 0;
glGetProgramiv(program,GL_LINK_STATUS,&status);
if(status != GL_TRUE)
{
XLOGE("glLinkProgram failed!");
return false;
}
glUseProgram(program);
XLOGE("glLinkProgram success!");
/////////////////////////////////////////////////////////////
//加入三维顶点数据两个三角形组成正方形
static float vers[] = {
1.0f,-1.0f,0.0f,
-1.0f,-1.0f,0.0f,
1.0f,1.0f,0.0f,
-1.0f,1.0f,0.0f,
};
GLuint apos = (GLuint)glGetAttribLocation(program,"aPosition");
glEnableVertexAttribArray(apos);
//传递顶点
glVertexAttribPointer(apos,3,GL_FLOAT,GL_FALSE,12,vers);
//加入材质坐标数据
static float txts[] = {
1.0f,0.0f , //右下
0.0f,0.0f,
1.0f,1.0f,
0.0,1.0
};
GLuint atex = (GLuint)glGetAttribLocation(program,"aTexCoord");
glEnableVertexAttribArray(atex);
glVertexAttribPointer(atex,2,GL_FLOAT,GL_FALSE,8,txts);
//材质纹理初始化
//设置纹理层
glUniform1i( glGetUniformLocation(program,"yTexture"),0); //对于纹理第1层
glUniform1i( glGetUniformLocation(program,"uTexture"),1); //对于纹理第2层
glUniform1i( glGetUniformLocation(program,"vTexture"),2); //对于纹理第3层
XLOGI("初始化Shader成功!");
return true;
}
void XShader::Draw()
{
if(!program)
return;
//三维绘制
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
}
void XShader::GetTexture(unsigned int index,int width,int height, unsigned char *buf)
{
if(texts[index] == 0)
{
//材质初始化
glGenTextures(1,&texts[index]);
//设置纹理属性
glBindTexture(GL_TEXTURE_2D,texts[index]);
//缩小的过滤器
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
//设置纹理的格式和大小
glTexImage2D(GL_TEXTURE_2D,
0, //细节基本 0默认
GL_LUMINANCE,//gpu内部格式亮度,灰度图
width,height, //拉升到全屏
0, //边框
GL_LUMINANCE,//数据的像素格式亮度,灰度图要与上面一致
GL_UNSIGNED_BYTE, //像素的数据类型
NULL //纹理的数据
);
}
//激活第1层纹理,绑定到创建的opengl纹理
glActiveTexture(GL_TEXTURE0+index);
glBindTexture(GL_TEXTURE_2D,texts[index]);
//替换纹理内容
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width,height,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf);
}
及其定义:
#define XPLAY_XSHADER_H
class XShader
{
public:
virtual bool Init();
//获取材质并映射到内存
virtual void GetTexture(unsigned int index,int width,int height, unsigned char *buf);
virtual void Draw();
protected:
unsigned int vsh = 0;
unsigned int fsh = 0;
unsigned int program = 0;
unsigned int texts[100] = {0};
};
#endif //XPLAY_XSHADER_H
#include "XLog.h"
#include "XEGL.h"
#include "XShader.h"
class CXTexture:public XTexture
{
public:
XShader sh;
virtual bool Init(void *win)
{
if(!win)
{
XLOGE("XTexture Init failed win is NULL");
return false;
}
if(!XEGL::Get()->Init(win))return false;
sh.Init();
return true;
}
virtual void Draw(unsigned char *data[],int width,int height)
{
sh.GetTexture(0,width,height,data[0]); // Y
sh.GetTexture(1,width/2,height/2,data[1]); // U
sh.GetTexture(2,width/2,height/2,data[2]); // V
sh.Draw();
XEGL::Get()->Draw();
}
};
XTexture *XTexture::Create()
{
return new CXTexture();
}
#define XPLAY_XTEXTURE_H
class XTexture
{
public:
static XTexture *Create();
virtual bool Init(void *win) = 0;
virtual void Draw(unsigned char *data[],int width,int height) = 0;
};
#endif //XPLAY_XTEXTURE_H
#include <string>
#include <android/native_window_jni.h>
#include "FFDemux.h"
#include "XLog.h"
#include "FFDecode.h"
#include "XEGL.h"
#include "XShader.h"
#include "IVideoView.h"
#include "GLVideoView.h"
class TestObs:public IObserver
{
public:
void Update(XData d)
{
//XLOGI("TestObs Update data size is %d",d.size);
}
};
IVideoView *view = NULL;
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_xplay_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
//XLOGI("S begin!");
//XSleep(3000);
//XLOGI("S end!");
//return env->NewStringUTF(hello.c_str());
///////////////////////////////////
///测试用代码
TestObs *tobs = new TestObs();
IDemux *de = new FFDemux();
//de->AddObs(tobs);
de->Open("/sdcard/1080.mp4");
IDecode *vdecode = new FFDecode();
vdecode->Open(de->GetVPara());
IDecode *adecode = new FFDecode();
adecode->Open(de->GetAPara());
de->AddObs(vdecode);
de->AddObs(adecode);
view = new GLVideoView();
vdecode->AddObs(view);
//vdecode->Open();
de->Start();
vdecode->Start();
adecode->Start();
//XSleep(3000);
//de->Stop();
/*for(;;)
{
XData d = de->Read();
XLOGI("Read data size is %d",d.size);
}*/
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_xplay_XPlay_InitView(JNIEnv *env, jobject instance, jobject surface) {
// TODO
ANativeWindow *win = ANativeWindow_fromSurface(env,surface);
view->SetRender(win);
//XEGL::Get()->Init(win);
//XShader shader;
//shader.Init();
}
雷电4上出现了花屏现象,但是用老师的视频(v1080.mp4)就可以
关键字词:shader