#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>
}
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")
#pragma comment(lib,"OpenGL32.lib")
struct FrameInfor
{
void* _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;
AVFrame* _frameRGB;
SwsContext* _convertCtx;
public:
int _screenW;
int _screenH;
int _imageSize;
public:
FFVideoReader()
{
_formatCtx = 0;
_videoIndex = -1;
_codecCtx = 0;
_codec = 0;
_frame = 0;
_frameRGB = 0;
_convertCtx = 0;
_screenW = 0;
_screenH = 0;
}
~FFVideoReader()
{
sws_freeContext(_convertCtx);
av_free(_frameRGB);
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();
_frameRGB = av_frame_alloc();
_screenW = _codecCtx->width;
_screenH = _codecCtx->height;
_convertCtx = sws_getContext(
_codecCtx->width
, _codecCtx->height
, _codecCtx->pix_fmt
, _codecCtx->width
, _codecCtx->height
, AV_PIX_FMT_RGB24
, SWS_BICUBIC
, NULL
, NULL
, NULL
);
int numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, _codecCtx->width,_codecCtx->height);
uint8_t*buffer = (uint8_t *) av_malloc (numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *)_frameRGB, buffer, AV_PIX_FMT_RGB24,_codecCtx->width, _codecCtx->height);
_imageSize = numBytes;
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;
//yuv转rgb,cpu上进行,速度慢
char buf[128];
sprintf(buf,"pts = %I64d dts = %I64d\n",packet.pts,packet.dts);
int res = sws_scale(
_convertCtx
, (const uint8_t* const*)_frame->data
, _frame->linesize
, 0
, _codecCtx->height
, _frameRGB->data
, _frameRGB->linesize
);
av_packet_unref(&packet);
infor._data = _frameRGB->data[0];
infor._dataSize = _imageSize;
infor._width = _screenW;
infor._height = _screenH;
infor._pts = _frame->pts;
infor._timeBase = av_q2d(streams->time_base);
return true;
}
}
return false;
}
};
#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);
}
};
#pragma once
#include "glew/glew.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);
}
};
// 05openglshaderplay.cpp : 定义应用程序的入口点。
//
#define __STDC_CONSTANT_MACROS
#include "framework.h"
#include "05openglshaderplay.h"
#include "glew/glew.h"
#include "FFVideoReader.hpp"
#include "Thread.hpp"
#include "Timestamp.hpp"
#include "GLContext.h"
#include "CELLShader.hpp"
#include "CELLMath.hpp"
#define MAX_LOADSTRING 100
#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;
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_MY05OPENGLSHADERPLAY, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY05OPENGLSHADERPLAY));
char szPath[1024];
char szPathName[1024];
getResourcePath(hInstance, szPath);
sprintf(szPathName, "%s/11.flv", szPath);
g_decode.setup(hWnd, "11.flv");
g_decode.start();
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
g_decode.shutdown();
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY05OPENGLSHADERPLAY));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MY05OPENGLSHADERPLAY);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_UPDATE_VIDEO:
{
FrameInfor* infor = (FrameInfor*)wParam;
g_decode.updateTexture(infor);
delete infor;
g_decode.render();
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
g_decode.shutdown();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}