OpenGL渲染视频图像笔记
目录
- GLContext.h
#pragma once #include <gl/gl.h> class GLContext { protected: int _format; //! 窗口句柄 HWND _hWnd; //! 绘制设备上下文 HDC _hDC; //! OpenGL上下文 HGLRC _hRC; public: GLContext() { _format = 0; _hWnd = 0; _hDC = 0; _hRC = 0; } ~GLContext() { shutdown(); } /** * 初始化GL */ bool setup( HWND hWnd,HDC hDC ) { _hWnd = hWnd; _hDC = hDC; unsigned PixelFormat; PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0, PFD_MAIN_PLANE, 0, 0, 0, 0 }; if(_format == 0) { PixelFormat = ChoosePixelFormat( _hDC, &pfd ); } else { PixelFormat = _format; } if(!SetPixelFormat( _hDC, PixelFormat, &pfd )) { return false; } _hRC = wglCreateContext( _hDC ); if(!wglMakeCurrent( _hDC, _hRC )) { return false; } return true; } /** * 销毁EGL */ void shutdown() { if( _hRC != NULL ) { wglMakeCurrent( NULL, NULL ); wglDeleteContext( _hRC ); _hRC = NULL; } if( _hDC != NULL ) { ReleaseDC( _hWnd, _hDC ); _hDC = NULL; } } /** * 交换缓冲区 */ void swapBuffer() { SwapBuffers(_hDC); } };
OpenGL绘制视频(固定管线)
#include <windows.h> #include <tchar.h> #include "FFVideoReader.hpp" #include "Thread.hpp" #include "Timestamp.hpp" #include "GLContext.h" #define WM_UPDATE_VIDEO WM_USER + 100 void getResourcePath(HINSTANCE hInstance, char pPath[1024]) { char szPathName[1024]; char szDriver[64]; char szPath[1024]; GetModuleFileNameA(hInstance, szPathName, sizeof(szPathName)); _splitpath(szPathName, szDriver, szPath, 0, 0); sprintf(pPath, "%s%s", szDriver, szPath); } class DecodeThread :public Thread { public: FFVideoReader _ffReader; HWND _hWnd; bool _exitFlag; Timestamp _timestamp; GLContext _glContext; unsigned _textureId; public: DecodeThread() { _exitFlag = false; _hWnd = 0; } virtual void setup(HWND hwnd, const char* fileName = "11.flv") { _hWnd = hwnd; _ffReader.setup(); _ffReader.load(fileName); _glContext.setup(hwnd, GetDC(hwnd)); //启用、创建纹理 glEnable(GL_TEXTURE_2D); _textureId = createTexture(_ffReader._screenW, _ffReader._screenH); } /** * 加载文件 */ virtual void load(const char* fileName) { _ffReader.load(fileName); } virtual void shutdown() { _exitFlag = true; Thread::join(); _glContext.shutdown(); } /** * 线程执行函数 */ virtual bool run() { _timestamp.update(); while (!_exitFlag) { FrameInfor* infor = new FrameInfor(); if (!_ffReader.readFrame(*infor)) { break; } double tims = infor->_pts * infor->_timeBase * 1000; //! 这里需要通知窗口进行重绘制更新,显示更新数据 PostMessage(_hWnd, WM_UPDATE_VIDEO, (WPARAM)infor, 0); double elsped = _timestamp.getElapsedTimeInMilliSec(); double sleeps = (tims - elsped); if (sleeps > 1) { Sleep((DWORD)sleeps); } } return true; } void updateTexture(FrameInfor* infor) { glBindTexture(GL_TEXTURE_2D, _textureId); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _ffReader._screenW, _ffReader._screenH, GL_RGB, GL_UNSIGNED_BYTE, infor->_data); } void render() { struct Vertex { float x, y; float u, v; }; RECT rt; GetClientRect(_hWnd, &rt); int w = rt.right - rt.left; int h = rt.bottom - rt.top; glClear(GL_COLOR_BUFFER_BIT); glClearColor(1, 0, 0, 1); glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, w, h, 0, -100, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D, _textureId); Vertex vertexs[] = { { 0, 0, 0, 0}, { 0, h, 0, 1}, { w, 0, 1, 0}, { 0, h, 0, 1}, { w, 0, 1, 0}, { w, h, 1, 1}, }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vertexs[0].x); glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vertexs[0].u); glDrawArrays(GL_TRIANGLES, 0, 6); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); _glContext.swapBuffer(); } protected: unsigned createTexture(int w, int h) { unsigned texId; glGenTextures(1, &texId); glBindTexture(GL_TEXTURE_2D, texId); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); return texId; } }; DecodeThread g_decode; LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_UPDATE_VIDEO: { FrameInfor* infor = (FrameInfor*)wParam; g_decode.updateTexture(infor); delete infor; g_decode.render(); } break; case WM_SIZE: break; case WM_CLOSE: case WM_DESTROY: g_decode.shutdown(); PostQuitMessage(0); break; default: break; } return DefWindowProc(hWnd, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { // 1 注册窗口类 ::WNDCLASSEXA winClass; winClass.lpszClassName = "FFVideoPlayer"; winClass.cbSize = sizeof(::WNDCLASSEX); winClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; winClass.lpfnWndProc = windowProc; winClass.hInstance = hInstance; winClass.hIcon = 0; winClass.hIconSm = 0; winClass.hCursor = LoadCursor(NULL, IDC_ARROW); winClass.hbrBackground = (HBRUSH)(BLACK_BRUSH); winClass.lpszMenuName = NULL; winClass.cbClsExtra = 0; winClass.cbWndExtra = 0; RegisterClassExA(&winClass); // 2 创建窗口 HWND hWnd = CreateWindowExA( NULL, "FFVideoPlayer", "FFVideoPlayer", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 480, 320, 0, 0, hInstance, 0 ); UpdateWindow(hWnd); ShowWindow(hWnd, SW_SHOW); char szPath[1024]; char szPathName[1024]; getResourcePath(hInstance, szPath); sprintf(szPathName, "%sdata/11.flv", szPath); g_decode.setup(hWnd, szPathName); g_decode.start(); MSG msg = { 0 }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } g_decode.shutdown(); return 0; }
可编程管线的实现
- shader.cpp
#pragma once #include <assert.h> class ShaderId { public: ShaderId() { _shaderId = -1; } int _shaderId; }; /** * 程序 */ class ProgramId { public: int _programId; ShaderId _vertex; ShaderId _fragment; public: ProgramId() { _programId = -1; } public: /** * 加载函数 */ bool createProgram( const char* vertex,const char* fragment ) { bool error = false; do { if (vertex) { _vertex._shaderId = glCreateShader( GL_VERTEX_SHADER ); glShaderSource( _vertex._shaderId, 1, &vertex, 0 ); glCompileShader( _vertex._shaderId ); GLint compileStatus; glGetShaderiv( _vertex._shaderId, GL_COMPILE_STATUS, &compileStatus ); error = compileStatus == GL_FALSE; if( error ) { GLchar messages[256]; glGetShaderInfoLog( _vertex._shaderId, sizeof(messages), 0,messages); assert( messages && 0 != 0); break; } } if (fragment) { _fragment._shaderId = glCreateShader( GL_FRAGMENT_SHADER ); glShaderSource( _fragment._shaderId, 1, &fragment, 0 ); glCompileShader( _fragment._shaderId ); GLint compileStatus; glGetShaderiv( _fragment._shaderId, GL_COMPILE_STATUS, &compileStatus ); error = compileStatus == GL_FALSE; if( error ) { GLchar messages[256]; glGetShaderInfoLog( _fragment._shaderId, sizeof(messages), 0,messages); assert( messages && 0 != 0); break; } } _programId = glCreateProgram( ); if (_vertex._shaderId) { glAttachShader( _programId, _vertex._shaderId); } if (_fragment._shaderId) { glAttachShader( _programId, _fragment._shaderId); } glLinkProgram( _programId ); GLint linkStatus; glGetProgramiv( _programId, GL_LINK_STATUS, &linkStatus ); if (linkStatus == GL_FALSE) { GLchar messages[256]; glGetProgramInfoLog( _programId, sizeof(messages), 0, messages); break; } glUseProgram(_programId); } while(false); if (error) { if (_fragment._shaderId) { glDeleteShader(_fragment._shaderId); _fragment._shaderId = 0; } if (_vertex._shaderId) { glDeleteShader(_vertex._shaderId); _vertex._shaderId = 0; } if (_programId) { glDeleteProgram(_programId); _programId = 0; } } return true; } /** * 使用程序 */ virtual void begin() { glUseProgram(_programId); } /** * 使用完成 */ virtual void end() { glUseProgram(0); } }; class PROGRAM_P2_UV2 :public ProgramId { public: typedef int attribute; typedef int uniform; public: attribute _position; attribute _uv; uniform _MVP; uniform _texture; public: PROGRAM_P2_UV2() { _position = -1; _uv = -1; _MVP = -1; _texture = -1; } ~PROGRAM_P2_UV2() { } /// 初始化函数 virtual bool initialize() { const char* vs = { "precision lowp float; " "uniform mat4 _MVP;" "attribute vec2 _position;" "attribute vec2 _uv;" "varying vec2 _outUV;" "void main()" "{" " _outUV = _uv;" " vec4 pos = vec4(_position,0,1);" " gl_Position = _MVP * pos;" "}" }; const char* ps = { "precision lowp float; " "uniform sampler2D _texture;" "varying vec2 _outUV;" "void main()" "{" " vec4 color = texture2D(_texture,_outUV);" " gl_FragColor = color;" "}" }; bool res = createProgram(vs,ps); if(res) { _position = glGetAttribLocation(_programId,"_position"); _uv = glGetAttribLocation(_programId,"_uv"); _texture = glGetUniformLocation(_programId,"_texture"); _MVP = glGetUniformLocation(_programId,"_MVP"); } return res; } /** * 使用程序 */ virtual void begin() { glUseProgram(_programId); glEnableVertexAttribArray(_position); glEnableVertexAttribArray(_uv); } /** * 使用完成 */ virtual void end() { glDisableVertexAttribArray(_position); glDisableVertexAttribArray(_uv); glUseProgram(0); } }; class PROGRAM_YUV :public ProgramId { public: typedef int attribute; typedef int uniform; public: attribute _position; attribute _uv; uniform _MVP; uniform _textureY; uniform _textureU; uniform _textureV; public: PROGRAM_YUV() { _position = -1; _uv = -1; _MVP = -1; _textureY = -1; _textureU = -1; _textureV = -1; } ~PROGRAM_YUV() { } /// 初始化函数 virtual bool initialize() { const char* vs = { "precision lowp float; " "uniform mat4 _MVP;" "attribute vec2 _position;" "attribute vec2 _uv;" "varying vec2 _outUV;" "void main()" "{" " _outUV = _uv;" " vec4 pos = vec4(_position,0,1);" " gl_Position = _MVP * pos;" "}" }; const char* ps = { "precision lowp float; " "uniform sampler2D _textureY;" "uniform sampler2D _textureU;" "uniform sampler2D _textureV;" "varying vec2 _outUV;" "void main()" "{" " vec3 yuv;" " vec3 rgb; " " yuv.x = texture2D(_textureY, _outUV).a;" " yuv.y = texture2D(_textureU, _outUV).a - 0.5;" " yuv.z = texture2D(_textureV, _outUV).a - 0.5;" " rgb = mat3( 1, 1, 1," " 0, -0.39465, 2.03210," " 1.13983, -0.58060, 0) * yuv;" " gl_FragColor = vec4(rgb, 1);" "}" }; bool res = createProgram(vs,ps); if(res) { _position = glGetAttribLocation(_programId,"_position"); _uv = glGetAttribLocation(_programId,"_uv"); _textureY = glGetUniformLocation(_programId,"_textureY"); _textureU = glGetUniformLocation(_programId,"_textureU"); _textureV = glGetUniformLocation(_programId,"_textureV"); _MVP = glGetUniformLocation(_programId,"_MVP"); } return res; } /** * 使用程序 */ virtual void begin() { glUseProgram(_programId); glEnableVertexAttribArray(_position); glEnableVertexAttribArray(_uv); } /** * 使用完成 */ virtual void end() { glDisableVertexAttribArray(_position); glDisableVertexAttribArray(_uv); glUseProgram(0); } };
#include <windows.h> #include <tchar.h> #include "glew/glew.h" #include "FFVideoReader.hpp" #include "Thread.hpp" #include "Timestamp.hpp" #include "GLContext.h" #include "CELLShader.hpp" #include "./CELLMath.hpp" #define WM_UPDATE_VIDEO WM_USER + 100 void getResourcePath(HINSTANCE hInstance, char pPath[1024]) { char szPathName[1024]; char szDriver[64]; char szPath[1024]; GetModuleFileNameA(hInstance, szPathName, sizeof(szPathName)); _splitpath(szPathName, szDriver, szPath, 0, 0); sprintf(pPath, "%s%s", szDriver, szPath); } class DecodeThread :public Thread { public: FFVideoReader _ffReader; HWND _hWnd; bool _exitFlag; Timestamp _timestamp; GLContext _glContext; unsigned _textureId; PROGRAM_P2_UV2 _shaderTex; public: DecodeThread() { _exitFlag = false; _hWnd = 0; } virtual void setup(HWND hwnd, const char* fileName = "11.flv") { _hWnd = hwnd; _ffReader.setup(); _ffReader.load(fileName); _glContext.setup(hwnd, GetDC(hwnd)); glewInit(); glEnable(GL_TEXTURE_2D); _textureId = createTexture(_ffReader._screenW, _ffReader._screenH); _shaderTex.initialize(); } /** * 加载文件 */ virtual void load(const char* fileName) { _ffReader.load(fileName); } virtual void shutdown() { _exitFlag = true; Thread::join(); _glContext.shutdown(); } /** * 线程执行函数 */ virtual bool run() { _timestamp.update(); while (!_exitFlag) { FrameInfor* infor = new FrameInfor(); if (!_ffReader.readFrame(*infor)) { break; } double tims = infor->_pts * infor->_timeBase * 1000; //! 这里需要通知窗口进行重绘制更新,显示更新数据 PostMessage(_hWnd, WM_UPDATE_VIDEO, (WPARAM)infor, 0); double elsped = _timestamp.getElapsedTimeInMilliSec(); double sleeps = (tims - elsped); if (sleeps > 1) { Sleep((DWORD)sleeps); } } return true; } void updateTexture(FrameInfor* infor) { glBindTexture(GL_TEXTURE_2D, _textureId); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _ffReader._screenW, _ffReader._screenH, GL_RGB, GL_UNSIGNED_BYTE, infor->_data); } void render() { struct Vertex { float x, y; float u, v; }; RECT rt; GetClientRect(_hWnd, &rt); int w = rt.right - rt.left; int h = rt.bottom - rt.top; glClear(GL_COLOR_BUFFER_BIT); glClearColor(1, 0, 0, 1); glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, w, h, 0, -100, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D, _textureId); Vertex vertexs[] = { { 0, 0, 0, 0}, { 0, h, 0, 1}, { w, 0, 1, 0}, { 0, h, 0, 1}, { w, 0, 1, 0}, { w, h, 1, 1}, }; CELL::matrix4 matMVP = CELL::ortho<float>(0, w, h, 0, -100, 100); _shaderTex.begin(); glUniformMatrix4fv(_shaderTex._MVP, 1, GL_FALSE, matMVP.data()); glUniform1i(_shaderTex._texture, 0); { glVertexAttribPointer(_shaderTex._position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertexs[0].x); glVertexAttribPointer(_shaderTex._uv, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertexs[0].u); glDrawArrays(GL_TRIANGLES, 0, 6); } _shaderTex.end(); _glContext.swapBuffer(); } protected: unsigned createTexture(int w, int h) { unsigned texId; glGenTextures(1, &texId); glBindTexture(GL_TEXTURE_2D, texId); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); return texId; } }; DecodeThread g_decode; LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_UPDATE_VIDEO: { FrameInfor* infor = (FrameInfor*)wParam; g_decode.updateTexture(infor); delete infor; g_decode.render(); } break; case WM_SIZE: break; case WM_CLOSE: case WM_DESTROY: g_decode.shutdown(); PostQuitMessage(0); break; default: break; } return DefWindowProc(hWnd, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { // 1 注册窗口类 ::WNDCLASSEXA winClass; winClass.lpszClassName = "FFVideoPlayer"; winClass.cbSize = sizeof(::WNDCLASSEX); winClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; winClass.lpfnWndProc = windowProc; winClass.hInstance = hInstance; winClass.hIcon = 0; winClass.hIconSm = 0; winClass.hCursor = LoadCursor(NULL, IDC_ARROW); winClass.hbrBackground = (HBRUSH)(BLACK_BRUSH); winClass.lpszMenuName = NULL; winClass.cbClsExtra = 0; winClass.cbWndExtra = 0; RegisterClassExA(&winClass); // 2 创建窗口 HWND hWnd = CreateWindowExA( NULL, "FFVideoPlayer", "FFVideoPlayer", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 480, 320, 0, 0, hInstance, 0 ); UpdateWindow(hWnd); ShowWindow(hWnd, SW_SHOW); char szPath[1024]; char szPathName[1024]; getResourcePath(hInstance, szPath); sprintf(szPathName, "%sdata/11.flv", szPath); g_decode.setup(hWnd, szPathName); g_decode.start(); MSG msg = { 0 }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } g_decode.shutdown(); return 0; }
shader实现YUV转RGB(YUV三张纹理)
- FFVideoReader.hpp
#pragma once extern "C" { #include <libavutil/imgutils.h> #include <libavutil/parseutils.h> #include <libswscale/swscale.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavformat/avio.h> #include <libavutil/file.h> } struct FrameInfor { AVFrame*_data; int _dataSize; int _width; int _height; int64_t _pts; double _timeBase; }; class FFVideoReader { public: AVFormatContext*_formatCtx; int _videoIndex; AVCodecContext* _codecCtx; AVCodec* _codec; AVFrame* _frame; public: int _screenW; int _screenH; int _imageSize; public: FFVideoReader() { _formatCtx = 0; _videoIndex = -1; _codecCtx = 0; _codec = 0; _frame = 0; _screenW = 0; _screenH = 0; } ~FFVideoReader() { av_free(_frame); avcodec_close(_codecCtx); avformat_close_input(&_formatCtx); } void setup() { av_register_all(); _formatCtx = avformat_alloc_context(); } int load(const char* filepath = "11.flv") { int ret = 0; //! 打开文件 if (avformat_open_input(&_formatCtx, filepath, NULL, NULL) != 0) { return -1; } //! 检测文件中是否存在数据流 if (avformat_find_stream_info(_formatCtx, NULL) < 0) { return -1; } //! 获取视频流索引 _videoIndex = -1; for (int i = 0; i < _formatCtx->nb_streams; i++) { if (_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { _videoIndex = i; break; } } /** * 没有视频流,则返回 */ if (_videoIndex == -1) { return -1; } _codecCtx = _formatCtx->streams[_videoIndex]->codec; double dur = _formatCtx->duration/double(AV_TIME_BASE); _codec = avcodec_find_decoder(_codecCtx->codec_id); if (_codec == NULL) { return -1; } /** * 打开解码器 */ if (avcodec_open2(_codecCtx, _codec, NULL) < 0) { return -1; } _frame = av_frame_alloc(); _screenW = _codecCtx->width; _screenH = _codecCtx->height; return 0; } bool readFrame(FrameInfor& infor) { AVPacket packet; av_init_packet(&packet); for (;;) { if (av_read_frame(_formatCtx, &packet)) { av_free_packet(&packet); return false; } if (packet.stream_index != _videoIndex) { continue; } int frame_finished = 0; int res = avcodec_decode_video2(_codecCtx, _frame, &frame_finished, &packet); if (frame_finished) { AVStream* streams = _formatCtx->streams[_videoIndex]; double tmbase = av_q2d(streams->time_base); int64_t pts = _frame->pts; av_packet_unref(&packet); infor._data = _frame; infor._dataSize = 0; infor._width = _screenW; infor._height = _screenH; infor._pts = _frame->pts; infor._timeBase = av_q2d(streams->time_base); return true; } } return false; } };
- CELLShader.hpp
#pragma once #include <assert.h> class ShaderId { public: ShaderId() { _shaderId = -1; } int _shaderId; }; /** * 程序 */ class ProgramId { public: int _programId; ShaderId _vertex; ShaderId _fragment; public: ProgramId() { _programId = -1; } public: /** * 加载函数 */ bool createProgram(const char* vertex, const char* fragment) { bool error = false; do { if (vertex) { _vertex._shaderId = glCreateShader(GL_VERTEX_SHADER); glShaderSource(_vertex._shaderId, 1, &vertex, 0); glCompileShader(_vertex._shaderId); GLint compileStatus; glGetShaderiv(_vertex._shaderId, GL_COMPILE_STATUS, &compileStatus); error = compileStatus == GL_FALSE; if (error) { GLchar messages[256]; glGetShaderInfoLog(_vertex._shaderId, sizeof(messages), 0, messages); assert(messages && 0 != 0); break; } } if (fragment) { _fragment._shaderId = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(_fragment._shaderId, 1, &fragment, 0); glCompileShader(_fragment._shaderId); GLint compileStatus; glGetShaderiv(_fragment._shaderId, GL_COMPILE_STATUS, &compileStatus); error = compileStatus == GL_FALSE; if (error) { GLchar messages[256]; glGetShaderInfoLog(_fragment._shaderId, sizeof(messages), 0, messages); assert(messages && 0 != 0); break; } } _programId = glCreateProgram(); if (_vertex._shaderId) { glAttachShader(_programId, _vertex._shaderId); } if (_fragment._shaderId) { glAttachShader(_programId, _fragment._shaderId); } glLinkProgram(_programId); GLint linkStatus; glGetProgramiv(_programId, GL_LINK_STATUS, &linkStatus); if (linkStatus == GL_FALSE) { GLchar messages[256]; glGetProgramInfoLog(_programId, sizeof(messages), 0, messages); break; } glUseProgram(_programId); } while (false); if (error) { if (_fragment._shaderId) { glDeleteShader(_fragment._shaderId); _fragment._shaderId = 0; } if (_vertex._shaderId) { glDeleteShader(_vertex._shaderId); _vertex._shaderId = 0; } if (_programId) { glDeleteProgram(_programId); _programId = 0; } } return true; } /** * 使用程序 */ virtual void begin() { glUseProgram(_programId); } /** * 使用完成 */ virtual void end() { glUseProgram(0); } }; class PROGRAM_YUV :public ProgramId { public: typedef int attribute; typedef int uniform; public: attribute _position; attribute _uv; uniform _MVP; uniform _textureY; uniform _textureU; uniform _textureV; public: PROGRAM_YUV() { _position = -1; _uv = -1; _MVP = -1; _textureY = -1; _textureU = -1; _textureV = -1; } ~PROGRAM_YUV() { } /// 初始化函数 virtual bool initialize() { const char* vs = { "precision lowp float; " "uniform mat4 _MVP;" "attribute vec2 _position;" "attribute vec2 _uv;" "varying vec2 _outUV;" "void main()" "{" " _outUV = _uv;" " vec4 pos = vec4(_position,0,1);" " gl_Position = _MVP * pos;" "}" }; const char* ps = { "precision lowp float; " "uniform sampler2D _textureY;" "uniform sampler2D _textureU;" "uniform sampler2D _textureV;" "varying vec2 _outUV;" "void main()" "{" " vec3 yuv;" " vec3 rgb; " " yuv.x = texture2D(_textureY, _outUV).a;" " yuv.y = texture2D(_textureU, _outUV).a - 0.5;" " yuv.z = texture2D(_textureV, _outUV).a - 0.5;" " rgb = mat3( 1, 1, 1," " 0, -0.39465, 2.03210," " 1.13983, -0.58060, 0) * yuv;" " gl_FragColor = vec4(rgb, 1);" "}" }; bool res = createProgram(vs, ps); if (res) { _position = glGetAttribLocation(_programId, "_position"); _uv = glGetAttribLocation(_programId, "_uv"); _textureY = glGetUniformLocation(_programId, "_textureY"); _textureU = glGetUniformLocation(_programId, "_textureU"); _textureV = glGetUniformLocation(_programId, "_textureV"); _MVP = glGetUniformLocation(_programId, "_MVP"); } return res; } /** * 使用程序 */ virtual void begin() { glUseProgram(_programId); glEnableVertexAttribArray(_position); glEnableVertexAttribArray(_uv); } /** * 使用完成 */ virtual void end() { glDisableVertexAttribArray(_position); glDisableVertexAttribArray(_uv); glUseProgram(0); } };
#include <windows.h> #include <tchar.h> #include "glew/glew.h" #include "FFVideoReader.hpp" #include "Thread.hpp" #include "Timestamp.hpp" #include "GLContext.h" #include "CELLShader.hpp" #include "CELLMath.hpp" #define WM_UPDATE_VIDEO WM_USER + 100 void getResourcePath(HINSTANCE hInstance, char pPath[1024]) { char szPathName[1024]; char szDriver[64]; char szPath[1024]; GetModuleFileNameA(hInstance, szPathName, sizeof(szPathName)); _splitpath(szPathName, szDriver, szPath, 0, 0); sprintf(pPath, "%s%s", szDriver, szPath); } class DecodeThread :public Thread { public: FFVideoReader _ffReader; HWND _hWnd; bool _exitFlag; Timestamp _timestamp; GLContext _glContext; unsigned _textureY; unsigned _textureU; unsigned _textureV; PROGRAM_YUV _shaderTex; public: DecodeThread() { _exitFlag = false; _hWnd = 0; } virtual void setup(HWND hwnd, const char* fileName = "11.flv") { _hWnd = hwnd; _ffReader.setup(); _ffReader.load(fileName); _glContext.setup(hwnd, GetDC(hwnd)); glewInit(); glEnable(GL_TEXTURE_2D); _textureY = createTexture(_ffReader._screenW, _ffReader._screenH); _textureU = createTexture(_ffReader._screenW / 2, _ffReader._screenH / 2); _textureV = createTexture(_ffReader._screenW / 2, _ffReader._screenH / 2); _shaderTex.initialize(); } /** * 加载文件 */ virtual void load(const char* fileName) { _ffReader.load(fileName); } virtual void shutdown() { _exitFlag = true; Thread::join(); _glContext.shutdown(); } /** * 线程执行函数 */ virtual bool run() { _timestamp.update(); while (!_exitFlag) { FrameInfor* infor = new FrameInfor(); if (!_ffReader.readFrame(*infor)) { break; } double tims = infor->_pts * infor->_timeBase * 1000; //! 这里需要通知窗口进行重绘制更新,显示更新数据 PostMessage(_hWnd, WM_UPDATE_VIDEO, (WPARAM)infor, 0); double elsped = _timestamp.getElapsedTimeInMilliSec(); double sleeps = (tims - elsped); if (sleeps > 1) { Sleep((DWORD)sleeps); } } return true; } void updateTexture(FrameInfor* infor) { glBindTexture(GL_TEXTURE_2D, _textureY); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _ffReader._screenW, _ffReader._screenH, GL_ALPHA, GL_UNSIGNED_BYTE, infor->_data->data[0]); glBindTexture(GL_TEXTURE_2D, _textureU); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _ffReader._screenW / 2, _ffReader._screenH / 2, GL_ALPHA, GL_UNSIGNED_BYTE, infor->_data->data[1]); glBindTexture(GL_TEXTURE_2D, _textureV); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _ffReader._screenW / 2, _ffReader._screenH / 2, GL_ALPHA, GL_UNSIGNED_BYTE, infor->_data->data[2]); } void render() { struct Vertex { float x, y; float u, v; }; RECT rt; GetClientRect(_hWnd, &rt); int w = rt.right - rt.left; int h = rt.bottom - rt.top; glClear(GL_COLOR_BUFFER_BIT); glClearColor(1, 0, 0, 1); glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, w, h, 0, -100, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _textureY); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, _textureU); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, _textureV); Vertex vertexs[] = { { 0, 0, 0, 0}, { 0, h, 0, 1}, { w, 0, 1, 0}, { 0, h, 0, 1}, { w, 0, 1, 0}, { w, h, 1, 1}, }; CELL::matrix4 matMVP = CELL::ortho<float>(0, w, h, 0, -100, 100); _shaderTex.begin(); glUniformMatrix4fv(_shaderTex._MVP, 1, GL_FALSE, matMVP.data()); glUniform1i(_shaderTex._textureY, 0); glUniform1i(_shaderTex._textureU, 1); glUniform1i(_shaderTex._textureV, 2); { glVertexAttribPointer(_shaderTex._position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertexs[0].x); glVertexAttribPointer(_shaderTex._uv, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertexs[0].u); glDrawArrays(GL_TRIANGLES, 0, 6); } _shaderTex.end(); _glContext.swapBuffer(); } protected: unsigned createTexture(int w, int h) { unsigned texId; glGenTextures(1, &texId); glBindTexture(GL_TEXTURE_2D, texId); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0); return texId; } }; DecodeThread g_decode; LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_UPDATE_VIDEO: { FrameInfor* infor = (FrameInfor*)wParam; g_decode.updateTexture(infor); delete infor; g_decode.render(); } break; case WM_SIZE: break; case WM_CLOSE: case WM_DESTROY: g_decode.shutdown(); PostQuitMessage(0); break; default: break; } return DefWindowProc(hWnd, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { // 1 注册窗口类 ::WNDCLASSEXA winClass; winClass.lpszClassName = "FFVideoPlayer"; winClass.cbSize = sizeof(::WNDCLASSEX); winClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; winClass.lpfnWndProc = windowProc; winClass.hInstance = hInstance; winClass.hIcon = 0; winClass.hIconSm = 0; winClass.hCursor = LoadCursor(NULL, IDC_ARROW); winClass.hbrBackground = (HBRUSH)(BLACK_BRUSH); winClass.lpszMenuName = NULL; winClass.cbClsExtra = 0; winClass.cbWndExtra = 0; RegisterClassExA(&winClass); // 2 创建窗口 HWND hWnd = CreateWindowExA( NULL, "FFVideoPlayer", "FFVideoPlayer", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 480, 320, 0, 0, hInstance, 0 ); UpdateWindow(hWnd); ShowWindow(hWnd, SW_SHOW); char szPath[1024]; char szPathName[1024]; getResourcePath(hInstance, szPath); sprintf(szPathName, "%sdata/11.flv", szPath); g_decode.setup(hWnd, szPathName); g_decode.start(); MSG msg = { 0 }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } g_decode.shutdown(); return 0; }
shader实现YUV转RGB(YUV单张纹理)
- 纹理宽度不变,高度乘以1.5
#pragma once #include <assert.h> class ShaderId { public: ShaderId() { _shaderId = -1; } int _shaderId; }; /** * 程序 */ class ProgramId { public: int _programId; ShaderId _vertex; ShaderId _fragment; public: ProgramId() { _programId = -1; } public: /** * 加载函数 */ bool createProgram(const char* vertex, const char* fragment) { bool error = false; do { if (vertex) { _vertex._shaderId = glCreateShader(GL_VERTEX_SHADER); glShaderSource(_vertex._shaderId, 1, &vertex, 0); glCompileShader(_vertex._shaderId); GLint compileStatus; glGetShaderiv(_vertex._shaderId, GL_COMPILE_STATUS, &compileStatus); error = compileStatus == GL_FALSE; if (error) { GLchar messages[256]; glGetShaderInfoLog(_vertex._shaderId, sizeof(messages), 0, messages); assert(messages && 0 != 0); break; } } if (fragment) { _fragment._shaderId = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(_fragment._shaderId, 1, &fragment, 0); glCompileShader(_fragment._shaderId); GLint compileStatus; glGetShaderiv(_fragment._shaderId, GL_COMPILE_STATUS, &compileStatus); error = compileStatus == GL_FALSE; if (error) { GLchar messages[256]; glGetShaderInfoLog(_fragment._shaderId, sizeof(messages), 0, messages); assert(messages && 0 != 0); break; } } _programId = glCreateProgram(); if (_vertex._shaderId) { glAttachShader(_programId, _vertex._shaderId); } if (_fragment._shaderId) { glAttachShader(_programId, _fragment._shaderId); } glLinkProgram(_programId); GLint linkStatus; glGetProgramiv(_programId, GL_LINK_STATUS, &linkStatus); if (linkStatus == GL_FALSE) { GLchar messages[256]; glGetProgramInfoLog(_programId, sizeof(messages), 0, messages); break; } glUseProgram(_programId); } while (false); if (error) { if (_fragment._shaderId) { glDeleteShader(_fragment._shaderId); _fragment._shaderId = 0; } if (_vertex._shaderId) { glDeleteShader(_vertex._shaderId); _vertex._shaderId = 0; } if (_programId) { glDeleteProgram(_programId); _programId = 0; } } return true; } /** * 使用程序 */ virtual void begin() { glUseProgram(_programId); } /** * 使用完成 */ virtual void end() { glUseProgram(0); } }; class PROGRAM_YUV1 :public ProgramId { public: typedef int attribute; typedef int uniform; public: attribute _position; attribute _uvY; attribute _uvU; attribute _uvV; uniform _MVP; uniform _textureYUV; public: PROGRAM_YUV1() { _position = -1; _uvY = -1; _uvU = -1; _uvV = -1; _MVP = -1; _textureYUV = -1; } ~PROGRAM_YUV1() { } /// 初始化函数 virtual bool initialize() { const char* vs = { "precision lowp float; " "uniform mat4 _MVP;" "attribute vec2 _position;" "attribute vec2 _uvY;" "attribute vec2 _uvU;" "attribute vec2 _uvV;" "varying vec2 _outUVY;" "varying vec2 _outUVU;" "varying vec2 _outUVV;" "void main()" "{" " _outUVY = _uvY;" " _outUVU = _uvU;" " _outUVV = _uvV;" " vec4 pos = vec4(_position,0,1);" " gl_Position = _MVP * pos;" "}" }; const char* ps = { "precision lowp float; " "uniform sampler2D _textureYUV;" "varying vec2 _outUVY;" "varying vec2 _outUVU;" "varying vec2 _outUVV;" "void main()" "{" " vec3 yuv;" " vec3 rgb; " " yuv.x = texture2D(_textureYUV, _outUVY).a;" " yuv.y = texture2D(_textureYUV, _outUVU).a - 0.5;" " yuv.z = texture2D(_textureYUV, _outUVV).a - 0.5;" " rgb = mat3( 1, 1, 1," " 0, -0.39465, 2.03210," " 1.13983, -0.58060, 0) * yuv;" " gl_FragColor = vec4(rgb, 1);" "}" }; bool res = createProgram(vs, ps); if (res) { _position = glGetAttribLocation(_programId, "_position"); _uvY = glGetAttribLocation(_programId, "_uvY"); _uvU = glGetAttribLocation(_programId, "_uvU"); _uvV = glGetAttribLocation(_programId, "_uvV"); _textureYUV = glGetUniformLocation(_programId, "_textureYUV"); _MVP = glGetUniformLocation(_programId, "_MVP"); } return res; } /** * 使用程序 */ virtual void begin() { glUseProgram(_programId); glEnableVertexAttribArray(_position); glEnableVertexAttribArray(_uvY); glEnableVertexAttribArray(_uvU); glEnableVertexAttribArray(_uvV); } /** * 使用完成 */ virtual void end() { glDisableVertexAttribArray(_position); glDisableVertexAttribArray(_uvY); glDisableVertexAttribArray(_uvU); glDisableVertexAttribArray(_uvV); glUseProgram(0); } };
#include <windows.h> #include <tchar.h> #include "glew/glew.h" #include "FFVideoReader.hpp" #include "Thread.hpp" #include "Timestamp.hpp" #include "GLContext.h" #include "CELLShader.hpp" #include "CELLMath.hpp" #define WM_UPDATE_VIDEO WM_USER + 100 void getResourcePath(HINSTANCE hInstance,char pPath[1024]) { char szPathName[1024]; char szDriver[64]; char szPath[1024]; GetModuleFileNameA(hInstance,szPathName,sizeof(szPathName)); _splitpath( szPathName, szDriver, szPath, 0, 0 ); sprintf(pPath,"%s%s",szDriver,szPath); } class DecodeThread :public Thread { public: FFVideoReader _ffReader; HWND _hWnd; bool _exitFlag; Timestamp _timestamp; GLContext _glContext; unsigned _textureYUV; PROGRAM_YUV1 _shaderTex; public: DecodeThread() { _exitFlag = false; _hWnd = 0; } virtual void setup(HWND hwnd,const char* fileName = "11.flv") { _hWnd = hwnd; _ffReader.setup(); _ffReader.load(fileName); _glContext.setup(hwnd,GetDC(hwnd)); glewInit(); glEnable(GL_TEXTURE_2D); _textureYUV = createTexture(_ffReader._screenW,_ffReader._screenH + _ffReader._screenH/2); _shaderTex.initialize(); } /** * 加载文件 */ virtual void load(const char* fileName) { _ffReader.load(fileName); } virtual void shutdown() { _exitFlag = true; Thread::join(); _glContext.shutdown(); } /** * 线程执行函数 */ virtual bool run() { _timestamp.update(); while(!_exitFlag) { FrameInfor* infor = new FrameInfor(); if (!_ffReader.readFrame(*infor)) { break; } double tims = infor->_pts * infor->_timeBase * 1000; //! 这里需要通知窗口进行重绘制更新,显示更新数据 PostMessage(_hWnd,WM_UPDATE_VIDEO,(WPARAM)infor,0); double elsped = _timestamp.getElapsedTimeInMilliSec(); double sleeps = (tims - elsped); if (sleeps > 1) { Sleep((DWORD)sleeps); } } return true; } void updateTexture(FrameInfor* infor) { glBindTexture(GL_TEXTURE_2D,_textureYUV); glTexSubImage2D(GL_TEXTURE_2D,0, 0, 0, _ffReader._screenW,_ffReader._screenH,GL_ALPHA,GL_UNSIGNED_BYTE,infor->_data->data[0]); glTexSubImage2D(GL_TEXTURE_2D,0, 0, _ffReader._screenH, _ffReader._screenW/2,_ffReader._screenH/2,GL_ALPHA,GL_UNSIGNED_BYTE,infor->_data->data[1]); glTexSubImage2D(GL_TEXTURE_2D,0, _ffReader._screenW/2, _ffReader._screenH, _ffReader._screenW/2,_ffReader._screenH/2,GL_ALPHA,GL_UNSIGNED_BYTE,infor->_data->data[2]); } void render() { struct Vertex { CELL::float2 pos; CELL::float2 uvY; CELL::float2 uvU; CELL::float2 uvV; }; RECT rt; GetClientRect(_hWnd,&rt); int w = rt.right - rt.left; int h = rt.bottom - rt.top; glClear(GL_COLOR_BUFFER_BIT); glClearColor(1,0,0,1); glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0,w,h,0,-100,100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D,_textureYUV); float yv = (float)_ffReader._screenH/(float)(_ffReader._screenH + _ffReader._screenH/2); float fw = _ffReader._screenW; float fh = _ffReader._screenH; float x = 0; float y = 0; float Yv = (float)fh/(float)(fh + fh/2); float Uu0 = 0; float Uv0 = Yv; float Uu1 = 0.5f; float Uv1 = 1.0f; float Vu0 = 0.5f; float Vv0 = Yv; float Vu1 = 1.0f; float Vv1 = 1.0f; Vertex vertex[] = { CELL::float2(x,y), CELL::float2(0,0), CELL::float2(Uu0,Uv0), CELL::float2(Vu0,Vv0), CELL::float2(x + w,y), CELL::float2(1,0), CELL::float2(Uu1,Uv0), CELL::float2(Vu1,Vv0), CELL::float2(x,y + h), CELL::float2(0,Yv), CELL::float2(Uu0,Uv1), CELL::float2(Vu0,Vv1), CELL::float2(x + w, y + h), CELL::float2(1,Yv), CELL::float2(Uu1,Uv1), CELL::float2(Vu1,Vv1), }; _shaderTex.begin(); CELL::matrix4 matMVP = CELL::ortho<float>(0,w,h,0,-100,100); glUniformMatrix4fv(_shaderTex._MVP, 1, false, matMVP.data()); glUniform1i(_shaderTex._textureYUV, 0); glVertexAttribPointer(_shaderTex._position,2,GL_FLOAT, false, sizeof(Vertex),vertex); glVertexAttribPointer(_shaderTex._uvY, 2,GL_FLOAT, false, sizeof(Vertex),&vertex[0].uvY); glVertexAttribPointer(_shaderTex._uvU, 2,GL_FLOAT, false, sizeof(Vertex),&vertex[0].uvU); glVertexAttribPointer(_shaderTex._uvV, 2,GL_FLOAT, false, sizeof(Vertex),&vertex[0].uvV); glDrawArrays(GL_TRIANGLE_STRIP,0,4); _shaderTex.end(); _glContext.swapBuffer(); } protected: unsigned createTexture(int w,int h) { unsigned texId; glGenTextures(1,&texId); glBindTexture(GL_TEXTURE_2D,texId); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D(GL_TEXTURE_2D,0,GL_ALPHA,w,h,0,GL_ALPHA,GL_UNSIGNED_BYTE,0); return texId; } }; DecodeThread g_decode; LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_UPDATE_VIDEO: { FrameInfor* infor = (FrameInfor*)wParam; g_decode.updateTexture(infor); delete infor; g_decode.render(); } break; case WM_SIZE: break; case WM_CLOSE: case WM_DESTROY: g_decode.shutdown(); PostQuitMessage(0); break; default: break; } return DefWindowProc( hWnd, msg, wParam, lParam ); } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) { // 1 注册窗口类 ::WNDCLASSEXA winClass; winClass.lpszClassName = "FFVideoPlayer"; winClass.cbSize = sizeof(::WNDCLASSEX); winClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; winClass.lpfnWndProc = windowProc; winClass.hInstance = hInstance; winClass.hIcon = 0; winClass.hIconSm = 0; winClass.hCursor = LoadCursor(NULL, IDC_ARROW); winClass.hbrBackground = (HBRUSH)(BLACK_BRUSH); winClass.lpszMenuName = NULL; winClass.cbClsExtra = 0; winClass.cbWndExtra = 0; RegisterClassExA(&winClass); // 2 创建窗口 HWND hWnd = CreateWindowExA( NULL, "FFVideoPlayer", "FFVideoPlayer", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 480, 320, 0, 0, hInstance, 0 ); UpdateWindow( hWnd ); ShowWindow(hWnd,SW_SHOW); char szPath[1024]; char szPathName[1024]; getResourcePath(hInstance,szPath); sprintf(szPathName,"%sdata/11.flv",szPath); g_decode.setup(hWnd,szPathName); g_decode.start(); MSG msg = {0}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } g_decode.shutdown(); return 0; }