Windows 视频解码及显示[转]
目录
创建窗口
#pragma once #include <windows.h> #include <tchar.h> LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; case WM_SIZE: break; case WM_CLOSE: case WM_DESTROY: 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 = CreateWindowEx( NULL, "FFVideoPlayer", "FFVideoPlayer", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 480, 320, 0, 0, hInstance, 0 ); UpdateWindow(hWnd); ShowWindow(hWnd, SW_SHOW); MSG msg = { 0 }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
单线程解码并且绘制
#pragma once #include <windows.h> #include <tchar.h> #include "FFVideoReader.hpp" HBITMAP g_hBmp = 0; HDC g_hMem = 0; BYTE* g_imageBuf = 0; FFVideoReader g_reader; LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hWnd, &ps); BitBlt(hdc, 0, 0, g_reader._screenW, g_reader._screenH, g_hMem, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); } break; case WM_SIZE: break; case WM_CLOSE: case WM_DESTROY: PostQuitMessage(0); break; case WM_TIMER: { BYTE* data = (BYTE*)g_reader.readFrame(); for (int i = 0; i < g_reader._screenW * g_reader._screenH * 3; i += 3) { //RGB -> BGR g_imageBuf[i + 0] = data[i + 2]; g_imageBuf[i + 1] = data[i + 1]; g_imageBuf[i + 2] = data[i + 0]; } InvalidateRect(hWnd, 0, 0); //窗口刷新 调用WM_PAINT } break; default: break; } return DefWindowProc(hWnd, msg, wParam, lParam); } 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); } 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 = CreateWindowEx( 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); HDC hDC = GetDC(hWnd); g_hMem = ::CreateCompatibleDC(hDC); g_reader.setup(); g_reader.load(szPathName); BITMAPINFO bmpInfor = { 0 }; bmpInfor.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfor.bmiHeader.biWidth = g_reader._screenW; bmpInfor.bmiHeader.biHeight = -g_reader._screenH; bmpInfor.bmiHeader.biPlanes = 1; bmpInfor.bmiHeader.biBitCount = 24; bmpInfor.bmiHeader.biCompression = BI_RGB; bmpInfor.bmiHeader.biSizeImage = 0; bmpInfor.bmiHeader.biXPelsPerMeter = 0; bmpInfor.bmiHeader.biYPelsPerMeter = 0; bmpInfor.bmiHeader.biClrUsed = 0; bmpInfor.bmiHeader.biClrImportant = 0; g_hBmp = CreateDIBSection(hDC, &bmpInfor, DIB_RGB_COLORS, (void**)&g_imageBuf, 0, 0); SelectObject(g_hMem, g_hBmp); SetTimer(hWnd, 1, 40, 0); MSG msg = { 0 }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
多线程解码并且绘制
#pragma once #include <windows.h> class Thread { public: DWORD _threadId; HANDLE _thread; protected: /** * 线程入口函数 */ static DWORD CALLBACK threadEnter(void* pVoid) { Thread* pThis = (Thread*)pVoid; if (pThis) { pThis->run(); } return 0; } public: Thread() { _thread = 0; _threadId = 0; } virtual ~Thread() { join(); } virtual bool run() { return true; } /** * 启动线程函数 */ virtual bool start() { if (_thread != 0) { return false; } else { //HIGH_PRIORITY_CLASS _thread = CreateThread(0,0,&Thread::threadEnter,this,0,&_threadId); return true; } } /** * 等待退出函数 */ virtual void join() { if (_thread) { WaitForSingleObject(_thread,0xFFFFFFFF); CloseHandle(_thread); _thread = 0; } } };
#include <windows.h> #include <tchar.h> #include "FFVideoReader.hpp" #include "Thread.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); } BYTE* g_imageBuf = 0; HDC g_hMem = 0; HBITMAP g_hBmp = 0; class DecodeThread :public Thread { public: FFVideoReader _ffReader; HWND _hWnd; bool _exitFlag; public: DecodeThread() { _ffReader.setup(); _hWnd = 0; _exitFlag = false; } void exitThread() { _exitFlag = true; join(); } /** * 加载文件 */ virtual void load(const char* fileName) { _ffReader.load(fileName); } /** * 线程执行函数 */ virtual bool run() { while (!_exitFlag) { FrameInfor infor; if (!_ffReader.readFrame(infor)) { break; } BYTE* data = (BYTE*)infor._data; for (int i = 0; i < infor._dataSize; i += 3) { g_imageBuf[i + 0] = data[i + 2]; g_imageBuf[i + 1] = data[i + 1]; g_imageBuf[i + 2] = data[i + 0]; } InvalidateRect(_hWnd, 0, 0); } return true; } }; DecodeThread g_decode; LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hWnd, &ps); if (g_hMem) { BITMAPINFO bmpInfor; GetObject(g_hBmp, sizeof(bmpInfor), &bmpInfor); BitBlt(hdc, 0, 0, bmpInfor.bmiHeader.biWidth, bmpInfor.bmiHeader.biHeight, g_hMem, 0, 0, SRCCOPY); } EndPaint(hWnd, &ps); } break; case WM_SIZE: break; case WM_CLOSE: case WM_DESTROY: g_decode.exitThread(); 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); HDC hDC = GetDC(hWnd); g_hMem = ::CreateCompatibleDC(hDC); g_decode._hWnd = hWnd; g_decode.load(szPathName); BITMAPINFO bmpInfor; bmpInfor.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfor.bmiHeader.biWidth = g_decode._ffReader._screenW; bmpInfor.bmiHeader.biHeight = -g_decode._ffReader._screenH; bmpInfor.bmiHeader.biPlanes = 1; bmpInfor.bmiHeader.biBitCount = 24; bmpInfor.bmiHeader.biCompression = BI_RGB; bmpInfor.bmiHeader.biSizeImage = 0; bmpInfor.bmiHeader.biXPelsPerMeter = 0; bmpInfor.bmiHeader.biYPelsPerMeter = 0; bmpInfor.bmiHeader.biClrUsed = 0; bmpInfor.bmiHeader.biClrImportant = 0; g_hBmp = CreateDIBSection(hDC, &bmpInfor, DIB_RGB_COLORS, (void**)&g_imageBuf, 0, 0); SelectObject(g_hMem, g_hBmp); g_decode.start(); MSG msg = { 0 }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
速率控制
#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 { 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; 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; } void* readFrame() { AVPacket packet; av_init_packet(&packet); for (;;) { if (av_read_frame(_formatCtx, &packet)) { av_free_packet(&packet); return 0; } 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; 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); return _frameRGB->data[0]; } } return 0; } };
- 时间:
#pragma once #include <windows.h> class Timestamp { public: Timestamp() { QueryPerformanceFrequency(&_frequency); QueryPerformanceCounter(&_startCount); } ~Timestamp() {} void update() { QueryPerformanceCounter(&_startCount); } /** * 获取当前秒 */ double getElapsedSecond() { return getElapsedTimeInMicroSec() * 0.000001; } /** * 获取毫秒 */ double getElapsedTimeInMilliSec() { return this->getElapsedTimeInMicroSec() * 0.001; } /** * 获取微妙 */ double getElapsedTimeInMicroSec() { LARGE_INTEGER endCount; QueryPerformanceCounter(&endCount); double startTimeInMicroSec = _startCount.QuadPart * (1000000.0 / _frequency.QuadPart); double endTimeInMicroSec = endCount.QuadPart * (1000000.0 / _frequency.QuadPart); return endTimeInMicroSec - startTimeInMicroSec; } protected: LARGE_INTEGER _frequency; LARGE_INTEGER _startCount; };
#include <windows.h> #include <tchar.h> #include "FFVideoReader.hpp" #include "Thread.hpp" #include "Timestamp.hpp" 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; BYTE* _imageBuf; HDC _hMem; HBITMAP _hBmp; bool _exitFlag; Timestamp _timestamp; public: DecodeThread() { _exitFlag = false; _hWnd = 0; } virtual void setup(HWND hwnd,const char* fileName = "11.flv") { _hWnd = hwnd; _ffReader.setup(); _ffReader.load(fileName); HDC hDC = GetDC(hwnd); _hMem = ::CreateCompatibleDC(hDC); BITMAPINFO bmpInfor; bmpInfor.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfor.bmiHeader.biWidth = _ffReader._screenW; bmpInfor.bmiHeader.biHeight = -_ffReader._screenH; bmpInfor.bmiHeader.biPlanes = 1; bmpInfor.bmiHeader.biBitCount = 24; bmpInfor.bmiHeader.biCompression = BI_RGB; bmpInfor.bmiHeader.biSizeImage = 0; bmpInfor.bmiHeader.biXPelsPerMeter = 0; bmpInfor.bmiHeader.biYPelsPerMeter = 0; bmpInfor.bmiHeader.biClrUsed = 0; bmpInfor.bmiHeader.biClrImportant = 0; _hBmp = CreateDIBSection(hDC,&bmpInfor,DIB_RGB_COLORS,(void**)&_imageBuf,0,0); SelectObject(_hMem,_hBmp); } /** * 加载文件 */ virtual void load(const char* fileName) { _ffReader.load(fileName); } virtual void join() { _exitFlag = true; Thread::join(); } /** * 线程执行函数 */ virtual bool run() { _timestamp.update(); while(!_exitFlag) { FrameInfor infor; if (!_ffReader.readFrame(infor)) { break; } double tims = infor._pts * infor._timeBase * 1000; BYTE* data = (BYTE*)infor._data; for (int i = 0 ;i < infor._dataSize; i += 3 ) { _imageBuf[i + 0 ] = data[i + 2]; _imageBuf[i + 1 ] = data[i + 1]; _imageBuf[i + 2 ] = data[i + 0]; } //! 这里需要通知窗口进行重绘制更新,显示更新数据 InvalidateRect(_hWnd,0,0); double elsped = _timestamp.getElapsedTimeInMilliSec(); double sleeps = (tims - elsped); if (sleeps > 1) { Sleep((DWORD)sleeps); } } return true; } }; DecodeThread g_decode; LRESULT CALLBACK windowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hWnd, &ps); if (g_decode._hMem) { BITMAPINFO bmpInfor; GetObject(g_decode._hBmp,sizeof(bmpInfor),&bmpInfor); BitBlt(hdc,0,0,bmpInfor.bmiHeader.biWidth,bmpInfor.bmiHeader.biHeight,g_decode._hMem,0,0,SRCCOPY); } EndPaint(hWnd, &ps); } break; case WM_SIZE: break; case WM_CLOSE: case WM_DESTROY: g_decode.join(); 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); } return 0; }