下载本渲染器
本视频渲染器渲染RGB32视频流。内置音频渲染器,可渲染PCM音频流。音频渲染器包含采样率转换算法,可以接受任何采样率。也可隐藏视频渲染窗口,只作为音频渲染器使用。本渲染器是脱离DirectShow的视频渲染器,在应用程序中可能更会使用到。
使用方法
首先,加载本视频渲染器DLL,获得渲染器模块句柄。
HMODULE hVRender = LoadLibrary(L"VRender.dll");
定义媒体信息和样本信息结构,定义导出函数指针:
struct INFO//媒体信息
{int VideoWidth = 0;//视频宽,单位像素int VideoHeight = 0;//视频高,单位像素LONGLONG FrameCur = 0;//帧持续时间,单位100纳秒LONGLONG DUR = 0;//媒体时长,单位100纳秒DWORD AudioSamplePerSec = 0;//音频样本采样率DWORD StreamCount = 0;//流数量
};struct SAMPLE_INFO//样本信息
{BOOL B;//为TRUE,样本为第1个样本DWORD STAR;//运行开始时间,单位毫秒LONGLONG star;//样本开始时间,单位100纳秒LONGLONG end;//样本结束时间,单位100纳秒BYTE* pB;//样本缓冲区指针int len;//样本的字节大小HANDLE hRun;//“运行”事件句柄HANDLE hSeek;//“定位”事件句柄
};typedef HWND(__cdecl *MYPROC_VRender_Init)(INFO info, BOOL Show);
typedef int(__cdecl *MYPROC_VRender_Flush)();
typedef int(__cdecl *MYPROC_VRender_Redraw)();
typedef int(__cdecl *MYPROC_VRender_Exit)();
typedef int(__cdecl *MYPROC_VRender_FullScreen)(BOOL B);
typedef int(__cdecl *MYPROC_VRender_VSetParent)(BOOL B, HWND hParent, RECT rect);
typedef int(__cdecl *MYPROC_VRender_Show)(BOOL B);
typedef int(__cdecl *MYPROC_VRender_VideoRender)(SAMPLE_INFO info);
typedef int(__cdecl *MYPROC_VRender_AudioRender)(SAMPLE_INFO info);
获取导出函数的地址:
MYPROC_VRender_Init VRender_Init = NULL;MYPROC_VRender_Flush VRender_Flush = NULL;MYPROC_VRender_Redraw VRender_Redraw = NULL;MYPROC_VRender_Exit VRender_Exit = NULL;MYPROC_VRender_FullScreen VRender_FullScreen = NULL;MYPROC_VRender_VSetParent VRender_VSetParent = NULL;MYPROC_VRender_Show VRender_Show = NULL;MYPROC_VRender_VideoRender VRender_VideoRender = NULL;MYPROC_VRender_AudioRender VRender_AudioRender = NULL;if (hVRender){VRender_Init = (MYPROC_VRender_Init)GetProcAddress(hVRender, "Init");VRender_Flush = (MYPROC_VRender_Flush)GetProcAddress(hVRender, "Flush");VRender_Redraw = (MYPROC_VRender_Redraw)GetProcAddress(hVRender, "Redraw");VRender_Exit = (MYPROC_VRender_Exit)GetProcAddress(hVRender, "Exit");VRender_FullScreen = (MYPROC_VRender_FullScreen)GetProcAddress(hVRender, "FullScreen");VRender_VSetParent = (MYPROC_VRender_VSetParent)GetProcAddress(hVRender, "VSetParent");VRender_Show=(MYPROC_VRender_Show)GetProcAddress(hVRender, "Show");VRender_VideoRender = (MYPROC_VRender_VideoRender)GetProcAddress(hVRender, "VideoRender");VRender_AudioRender = (MYPROC_VRender_AudioRender)GetProcAddress(hVRender, "AudioRender");}
使用INFO结构提供初始化参数,包括:视频的宽高,音频采样率;并指定最初是否显示渲染窗口,初始化本渲染器:
INFO info;info.VideoWidth = 1200;info.VideoHeight = 608;info.AudioSamplePerSec = 44100;if (VRender_Init != NULL)//如果地址不为空VRender_Init(info, TRUE);
此时将创建视频渲染窗口,并初始化默认音频渲染端点。
使用样本信息结构SAMPLE_INFO提供样本信息,包括:
1.如果是第1帧,结构的B参数为TRUE,通知渲染器开始计时;(渲染器使用timeGetTime函数获取电脑开机以来的毫秒)。
2.媒体源的开始时间,单位毫秒。
3.提供“运行”和“定位”事件句柄。也可以不提供“定位”事件句柄,但必须提供“运行”事件句柄,通过检测“运行”事件信号,确定音频客户端是运行还是停止。DirectShow过滤器“定位”后会将样本时间戳从0开始,但本渲染器样本时间不是从0开始,仍使用样本在媒体中的实际时间。
4.提供样本的开始时间和结束时间。提供样本数据的缓冲区指针。样本的字节大小。视频样本是1帧RGB32的数据,音频样本大小必须小于1秒的数据量。
反复的调用视频渲染函数和音频渲染函数,样本数据将传递给渲染器,将获得视频图像和播放音频数据。如果渲染时间未到,函数将阻塞以等待渲染时间;如果已过样本的结束时间,将丢弃该样本,在渲染函数的下一次调用中,渲染下一个样本。
SAMPLE_INFO Vinfo;//样本信息结构已包含信息VRender_VideoRender(Vinfo);SAMPLE_INFO Ainfo;//样本信息结构已包含信息VRender_AudioRender(Ainfo);
其他导出函数实现对渲染器窗口的操作和渲染器的销毁:
VRender_Flush();//丢弃音频渲染缓冲区的所有数据VRender_Redraw();//重绘渲染器窗口VRender_Exit();//销毁渲染器VRender_FullScreen(TRUE);//参数TRUE,全屏渲染窗口。FALSE退出全屏VRender_VSetParent(TRUE,hParent,rect);//参数1为TRUE,将渲染窗口设置为hParent的子窗口,rect指定窗口大小和位置。参数1为FALSE时,窗口恢复为弹出窗口;此时忽略参数2,3VRender_Show(TRUE);//参数为TRUE,显示渲染窗口,FALSE隐藏渲染窗口
有关本视频渲染器的使用示例,可以看后续文章“Windows应用-播放视频”。
本视频渲染器DLL的全部代码
DLL.cpp
#include "windows.h"
#include "mmsystem.h"
#pragma comment(lib, "winmm")
#include "mmdeviceapi.h"
#include "audioclient.h"
#include "resource.h"#define REFTIMES_PER_SEC 10000000template <class T> void SafeRelease(T** ppT)
{if (*ppT){(*ppT)->Release();*ppT = NULL;}
}struct INFO//媒体信息
{int VideoWidth = 0;//视频宽,单位像素int VideoHeight = 0;//视频高,单位像素LONGLONG FrameCur = 0;//帧持续时间,单位100纳秒LONGLONG DUR = 0;//媒体时长,单位100纳秒DWORD AudioSamplePerSec = 0;//音频样本采样率DWORD StreamCount = 0;//流数量
};struct SAMPLE_INFO//样本信息
{BOOL B;//为TRUE,样本为第1个样本DWORD STAR;//运行开始时间,单位毫秒LONGLONG star;//样本开始时间,单位100纳秒LONGLONG end;//样本结束时间,单位100纳秒BYTE* pB;//样本缓冲区指针int len;//样本的字节大小HANDLE hRun;//“运行”事件句柄HANDLE hSeek;//“定位”事件句柄
};struct INITPARAM
{UINT Width=640;UINT Height=480;BOOL Show=TRUE;UINT AudioSamplePerSec = 0;
};class VRender
{
public:VRender(){hExit = CreateEvent(NULL, TRUE, FALSE, NULL);//手动重置,初始无信号pAudioBuffer = new BYTE[48000 * 4];if(S_OK != InitAudioClient())MessageBox(NULL, L"获取默认音频渲染端点失败", L"VRender", MB_OK);}~VRender(){delete[] pAudioBuffer; CloseHandle(hExit);SafeRelease(&pAudioClient); SafeRelease(&pRenderClient);}INFO info;HWND hwnd;//视频渲染窗口句柄HDC hDC;HDC hComDC;HBRUSH hBrush = NULL;BYTE* pAudioBuffer = NULL;//音频“采样率转换”目标缓冲区指针int index = 0; short LLast = 0, RLast = 0;int WriteOutput(UINT DSamples, UINT SSamples, short F, short N, BYTE*& pD, BOOL BL);int Convert(UINT DSamples, BYTE* pD, int& len, UINT SSamples, BYTE* pS, int Len);//转换采样率UINT AudioSamplePerSec = 0;//输入音频采样率IAudioClient* pAudioClient = NULL;//默认音频渲染端点客户端接口IAudioRenderClient *pRenderClient = NULL; //音频服务接口WAVEFORMATEX wfx;UINT32 bufferFrameCount;//申请的端点缓冲区总大小,单位音频帧HRESULT InitAudioClient();//初始化默认音频渲染端点LONGLONG STAR;//视频流开始时间,单位毫秒LONGLONG ASTAR;//音频流开始时间,单位毫秒RECT SRect, DRect;//源矩形,目标矩形UINT cbBuffer;//视频渲染缓冲区大小HBITMAP hBitmap;int PreWindowState = -1;//先前的窗口状态int WindowState = -1;//标记窗口状态;1弹出窗口,2子窗口,3全屏幕窗口HWND hParent;//记录作为子窗口时,父窗口句柄RECT ChildRect;//记录作为子窗口时,窗口大小和位置HANDLE hExit = NULL;//“退出”事件句柄
};VRender* pVRender = NULL;//VRender对象指针HRESULT VRender::InitAudioClient()//初始化默认音频渲染端点
{REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;IMMDeviceEnumerator* pEnumerator = NULL;HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);if (hr != S_OK){MessageBox(NULL, L"创建设备枚举器失败", L"VRender", MB_OK); return hr;}IMMDevice* pDevice = NULL;hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); if (hr != S_OK){MessageBox(NULL, L"获取默认音频渲染端点失败", L"VRender", MB_OK); SafeRelease(&pEnumerator); return hr;}hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);if (hr != S_OK){MessageBox(NULL, L"获取音频渲染端点客户端失败", L"VRender", MB_OK); SafeRelease(&pEnumerator); SafeRelease(&pDevice); return hr;}wfx.wFormatTag = 1;//默认音频渲染端点只接受PCM或FLOAT格式wfx.nChannels = 2;wfx.nSamplesPerSec = 48000;//默认音频渲染端点只接受48000采样率wfx.nAvgBytesPerSec = 48000 * 4;wfx.nBlockAlign = 4;wfx.wBitsPerSample = 16;wfx.cbSize = 0;hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsRequestedDuration, 0, &wfx, NULL);//创建端点缓冲区,可以容纳1秒的音频数据if (hr != S_OK){MessageBox(NULL, L"音频客户端初始化失败!", L"VRender", MB_OK); SafeRelease(&pEnumerator); SafeRelease(&pDevice); SafeRelease(&pAudioClient); return hr;}hr = pAudioClient->GetBufferSize(&bufferFrameCount);//获取申请的端点缓冲区总大小,单位音频帧if (hr != S_OK){MessageBox(NULL, L"获取缓冲区大小失败!", L"VRender", MB_OK); SafeRelease(&pEnumerator); SafeRelease(&pDevice); SafeRelease(&pAudioClient); return hr;}hr = pAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)&pRenderClient);//获取音频服务if (hr != S_OK){MessageBox(NULL, L"音频服务没有打开!", L"VRender", MB_OK); SafeRelease(&pEnumerator); SafeRelease(&pDevice); SafeRelease(&pAudioClient); return hr;}SafeRelease(&pEnumerator); SafeRelease(&pDevice);return hr;
}void PopupWindow(HWND hwnd)//更改为弹出窗口
{SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW);//指定窗口扩展样式SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE | WS_BORDER | WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);//指定窗口样式int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);//获取主显示器的宽度,以像素为单位int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);//获取主显示器的高度,以像素为单位RECT rect = pVRender->SRect;AdjustWindowRectEx(&rect, WS_POPUP | WS_VISIBLE | WS_BORDER | WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, FALSE, WS_EX_APPWINDOW); // 根据客户区大小计算窗口大小SetParent(pVRender->hwnd, NULL);//指定无父窗口SetWindowPos(pVRender->hwnd, HWND_NOTOPMOST, (ScreenWidth - (rect.right - rect.left)) / 2, (ScreenHeight - (rect.bottom - rect.top)) / 2, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW);pVRender->PreWindowState = pVRender->WindowState;//记录先前的状态pVRender->WindowState = 1;//设置窗口状态标志
}void ChildWindow(HWND hwnd, HWND hParent, RECT rect)//更改为子窗口
{pVRender->hParent = hParent; pVRender->ChildRect = rect;//记录父窗口句柄,子窗口大小和位置SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW);//指定窗口扩展样式SetWindowLong(hwnd, GWL_STYLE, WS_VISIBLE | WS_CHILD);//更改窗口样式为子窗口SetParent(hwnd, hParent);//指定父窗口MoveWindow(hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0);//设置窗口大小ShowWindow(hwnd, SW_SHOW);//显示窗口pVRender->PreWindowState = pVRender->WindowState;//记录先前的状态pVRender->WindowState = 2;//设置窗口状态标志
}void FullScreen(HWND hwnd)//更改为全屏窗口
{SetWindowLong(hwnd, GWL_EXSTYLE, 0);//指定窗口扩展样式SetWindowLong(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);//更改窗口样式int screenWidth = GetSystemMetrics(SM_CXSCREEN);int screenHeight = GetSystemMetrics(SM_CYSCREEN);SetParent(pVRender->hwnd, NULL);//指定无父窗口SetWindowPos(pVRender->hwnd, HWND_TOPMOST, 0, 0, screenWidth, screenHeight, SWP_SHOWWINDOW);//设置窗口为始终在顶层,大小为整个屏幕pVRender->PreWindowState = pVRender->WindowState;//记录先前的状态pVRender->WindowState = 3;//设置窗口状态标志
}void VSetDRect(HWND hwnd, RECT rect)//按原始图像高宽比设置目标矩形
{RECT r; r.left = 0; r.top = 0; r.right = rect.right - rect.left; r.bottom = rect.bottom - rect.top;double Db = (double)r.bottom / (double)r.right; double Sb = (double)pVRender->SRect.bottom / (double)pVRender->SRect.right;if (Db >= Sb)//如果渲染窗口客户区高宽比大{pVRender->DRect.left =0; pVRender->DRect.right = r.right;int n = (int)(((double)r.bottom - (double)r.right * Sb) / 2);pVRender->DRect.top = n; pVRender->DRect.bottom = r.bottom - n;}else{pVRender->DRect.top = 0; pVRender->DRect.bottom = r.bottom;int m = (int)(((double)r.right - (double)r.bottom / Sb) / 2);pVRender->DRect.left = m; pVRender->DRect.right = r.right - m;}RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT);
}LRESULT CALLBACK VRenderWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)//视频渲染窗口过程函数
{RECT rect; LONG Style = 0, ExStyle = 0; switch (uMsg){case WM_SIZE://窗口大小改变GetClientRect(hwnd, &rect);VSetDRect(hwnd, rect);//改变渲染窗口大小时,不更改图像宽高比break;case WM_KEYDOWN://按下某个键if (VK_ESCAPE == wParam)//收到ESC键,退出全屏{if (pVRender->WindowState==3)//如果窗口为全屏幕{if (pVRender->PreWindowState == 2)//如果先前的状态为子窗口{ChildWindow(pVRender->hwnd, pVRender->hParent, pVRender->ChildRect);}else {PopupWindow(pVRender->hwnd);}}}break;case WM_PAINT:GetClientRect(pVRender->hwnd, &rect);FillRect(pVRender->hDC, &rect, pVRender->hBrush);break;case WM_CLOSE://点击了窗口关闭按钮ShowWindow(hwnd, SW_HIDE);//隐藏窗口return TRUE;//不调用DefWindowProc,防止销毁窗口}return DefWindowProc(hwnd, uMsg, wParam, lParam);
}DWORD WINAPI WindowThread(LPVOID pParam)
{VRender vRender;pVRender = &vRender;INITPARAM* pwh = (INITPARAM*)pParam;pVRender->SRect.left = 0; pVRender->SRect.top = 0; pVRender->SRect.right = pwh->Width; pVRender->SRect.bottom = pwh->Height;pVRender->cbBuffer = pwh->Width * pwh->Height * 4;pVRender->AudioSamplePerSec = pwh->AudioSamplePerSec;HMODULE hModule = GetModuleHandle(L"VRender");WNDCLASSEX wcx;wcx.cbSize = sizeof(wcx);//结构的大小wcx.style = CS_HREDRAW | CS_VREDRAW; //窗口类样式wcx.lpfnWndProc = VRenderWndProc; //指向窗口过程wcx.cbClsExtra = 0; //没有额外的类内存wcx.cbWndExtra = 0; //没有额外的窗口内存wcx.hInstance = hModule; //实例句柄HICON hIcon = (HICON)LoadImage(hModule, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 48, 48, 0);wcx.hIcon = hIcon; //指定图标wcx.hCursor = LoadCursor(NULL, IDC_ARROW); //预定义的光标pVRender->hBrush= CreateSolidBrush(RGB(0, 0, 0)); wcx.hbrBackground = pVRender->hBrush;//背景笔刷wcx.lpszMenuName =NULL; //菜单资源名称wcx.lpszClassName = L"VRenderWindow"; //窗口类的名称wcx.hIconSm = hIcon;//小图标RegisterClassEx(&wcx);//注册窗口类int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);//获取主显示器的宽度,以像素为单位int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);//获取主显示器的高度,以像素为单位RECT rect = pVRender->DRect = pVRender->SRect;//赋值目标矩形int Width, Height;//窗口的宽高if (pwh->Show)//如果要求窗口最初可见,创建弹出窗口{AdjustWindowRectEx(&rect, WS_POPUP | WS_VISIBLE | WS_BORDER | WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, FALSE, WS_EX_APPWINDOW);//根据客户区大小计算窗口大小Width = rect.right - rect.left; Height = rect.bottom - rect.top;//窗口的宽高pVRender->hwnd = CreateWindowEx(//创建"视频渲染窗口"WS_EX_APPWINDOW, //窗口扩展样式 L"VRenderWindow", //类名 L"VRender", //窗口名称 WS_POPUP | WS_VISIBLE | WS_BORDER | WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, //窗口样式 (ScreenWidth - Width) / 2, //水平居中(ScreenHeight - Height) / 2, //垂直居中Width, //宽度 Height, //高度 NULL, //没有父窗口或所有者窗口NULL, //使用的菜单 hModule, //实例句柄 NULL); //没有窗口创建数据pVRender->WindowState = 1;}else//如果要求窗口最初不可见{AdjustWindowRectEx(&rect, WS_POPUP, FALSE, WS_EX_TOOLWINDOW);Width = rect.right - rect.left; Height = rect.bottom - rect.top;//窗口的宽高pVRender->hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,L"VRenderWindow",L"VRender",WS_POPUP,(ScreenWidth - Width) / 2, //水平居中(ScreenHeight - Height) / 2, //垂直居中Width, //宽度 Height, //高度 NULL,NULL,hModule, NULL);pVRender->WindowState = 2;}pVRender->hDC = GetDC(pVRender->hwnd);//获取渲染窗口DCpVRender->hComDC = CreateCompatibleDC(pVRender->hDC);//创建兼容DCpVRender->hBitmap = CreateBitmap(pVRender->DRect.right - pVRender->DRect.left, pVRender->DRect.bottom - pVRender->DRect.top, 1, 32, NULL);//创建32位位图SelectObject(pVRender->hComDC, pVRender->hBitmap);//选择位图到兼容DCSetStretchBltMode(pVRender->hDC, COLORONCOLOR);//设置拉伸绘制模式MSG msg;BOOL fGotMessage;while ((fGotMessage = GetMessage(&msg, pVRender->hwnd, 0, 0)) != 0 && fGotMessage != -1){TranslateMessage(&msg);DispatchMessage(&msg);}ReleaseDC(pVRender->hwnd, pVRender->hDC); DeleteObject(pVRender->hBitmap); DeleteDC(pVRender->hComDC); pVRender = NULL;return 1;
}int VRender::WriteOutput(UINT DSamples, UINT SSamples, short F, short N, BYTE*& pD, BOOL BL)//参数1目标采样率,参数2源采样率,参数3左值,参数4右值,参数5目标缓冲区指针,参数6TRUE左声道,FALSE右声道
{int Count = 0;double Star = (double)index / (double)SSamples;//起始时间double End = (double)(index + 1) / (double)SSamples;//结束时间int DIndex = (int)(Star * (double)DSamples);//计算目标起始索引
Agan:double Dx = (double)DIndex / (double)DSamples;//计算目标时间if (Dx < Star){DIndex++;goto Agan;}if (Dx >= End){if (BL)//如果写左声道值{if (Count == 1){pD -= 2;}else if (Count>1){pD -= Count * 4 - 2;}return Count * 2;}else//如果写右声道值{if (Count >= 1){pD -= 2;}return Count * 2;}}Count++; DIndex++;short sh = (short)((double)F + (double)(N - F)*(Dx - Star) / (End - Star));CopyMemory(pD, &sh, 2); pD += 4;goto Agan;
}int VRender::Convert(UINT DSamples, BYTE* pD, int& len, UINT SSamples, BYTE* pS, int Len)//参数1目标采样率,参数2目标缓冲区,参数3累计目标的字节大小,参数4源采样率,参数5源缓冲区指针,参数6源缓冲区大小
{BYTE* pDD = pD;int Count = Len / 4;//获取单个样本的数量for (int i = 0; i < Count; i++){short LFirst, RFirst, LNext, RNext;if (i == 0)//如果是第1个样本{if (index == 0){LFirst = *((short*)(pS + i * 4)); RFirst = *((short*)(pS + i * 4 + 2)); LNext = *((short*)(pS + (i + 1) * 4)); RNext = *((short*)(pS + (i + 1) * 4 + 2));len += WriteOutput(DSamples, SSamples, LFirst, LNext, pDD, TRUE); len += WriteOutput(DSamples, SSamples, RFirst, RNext, pDD, FALSE);}else{LFirst = LLast; RFirst = RLast; LNext = *((short*)(pS + i * 4)); RNext = *((short*)(pS + i * 4));len += WriteOutput(DSamples, SSamples, LFirst, LNext, pDD, TRUE); len += WriteOutput(DSamples, SSamples, RFirst, RNext, pDD, FALSE);LFirst = *((short*)(pS + i * 4)); RFirst = *((short*)(pS + i * 4 + 2)); LNext = *((short*)(pS + (i + 1) * 4)); RNext = *((short*)(pS + (i + 1) * 4 + 2));len += WriteOutput(DSamples, SSamples, LFirst, LNext, pDD, TRUE); len += WriteOutput(DSamples, SSamples, RFirst, RNext, pDD, FALSE);}}else if (i == Count - 1)//如果是最后1个样本{LLast = *((short*)(pS + i * 4)); RLast = *((short*)(pS + i * 4 + 2));}else{LFirst = *((short*)(pS + i * 4)); RFirst = *((short*)(pS + i * 4 + 2)); LNext = *((short*)(pS + (i + 1) * 4)); RNext = *((short*)(pS + (i + 1) * 4 + 2));len += WriteOutput(DSamples, SSamples, LFirst, LNext, pDD, TRUE); len += WriteOutput(DSamples, SSamples, RFirst, RNext, pDD, FALSE);}index++;}return 0;
}//下面是导出函数定义#ifdef __cplusplus // If used by C++ code,
extern "C" { // we need to export the C interface
#endif__declspec(dllexport) HWND __cdecl Init(INFO info, BOOL Show)//初始化{if (pVRender != NULL)//如果线程正在运行,不再创建新的线程,只更新参数{pVRender->info = info;pVRender->SRect.left = 0; pVRender->SRect.top = 0; pVRender->SRect.right = info.VideoWidth; pVRender->SRect.bottom = info.VideoHeight;pVRender->DRect = pVRender->SRect;pVRender->cbBuffer = info.VideoWidth * info.VideoHeight * 4;pVRender->AudioSamplePerSec= info.AudioSamplePerSec;DeleteDC(pVRender->hComDC);pVRender->hComDC = CreateCompatibleDC(pVRender->hDC);//创建兼容DCDeleteObject(pVRender->hBitmap);pVRender->hBitmap = CreateBitmap(pVRender->DRect.right - pVRender->DRect.left, pVRender->DRect.bottom - pVRender->DRect.top, 1, 32, NULL);//创建32位位图SelectObject(pVRender->hComDC, pVRender->hBitmap);//选择位图到兼容DCSetStretchBltMode(pVRender->hDC, COLORONCOLOR);//设置拉伸绘制模式return pVRender->hwnd;}INITPARAM wh; wh.Width = info.VideoWidth; wh.Height = info.VideoHeight; wh.Show = Show; wh.AudioSamplePerSec = info.AudioSamplePerSec;CreateThread(NULL, 0, WindowThread, &wh, 0, NULL);Sleep(1000);//等待窗口创建完成return pVRender->hwnd;}__declspec(dllexport) int __cdecl Flush()//刷新{if (pVRender){HRESULT hr = pVRender->pAudioClient->Reset();return 0;}return 1;}__declspec(dllexport) int __cdecl Redraw()//重绘渲染窗口{if (pVRender){RedrawWindow(pVRender->hwnd, NULL, NULL, RDW_INTERNALPAINT);return 0;}return 1;}__declspec(dllexport) int __cdecl Exit(void)//退出{if (pVRender){SetEvent(pVRender->hExit);//设置“退出”有信号HRESULT hr = pVRender->pAudioClient->Stop();PostMessage(pVRender->hwnd, WM_QUIT, 0, 0);//发送WM_QUIT消息,用于退出渲染窗口线程Sleep(500);//等待窗口线程退出UnregisterClass(L"VRenderWindow", GetModuleHandle(L"VRender"));//删除窗口类注册return 0;}return 1;}__declspec(dllexport) int __cdecl FullScreen(BOOL B)//B为TRUE时,指定全屏播放模式;FALSE退出全屏{if (pVRender){if (B){FullScreen(pVRender->hwnd); return 0;}else{PopupWindow(pVRender->hwnd); return 0;}}return 1;}__declspec(dllexport) int __cdecl VSetParent(BOOL B, HWND hParent, RECT rect)//参数1为TRUE时,为播放窗口指定父窗口,并指定播放窗口大小;参数1为FALSE时,窗口恢复为弹出窗口{if (pVRender){if (B){ChildWindow(pVRender->hwnd, hParent, rect);//更改为子窗口return 0;}else{PopupWindow(pVRender->hwnd);//更改为弹出窗口return 0;}}return 1;}__declspec(dllexport) int __cdecl Show(BOOL B)//B为TRUE时,显示渲染窗口;FALSE隐藏渲染窗口{if (pVRender){if (B){ShowWindow(pVRender->hwnd, SW_SHOW); return 0;}else{ShowWindow(pVRender->hwnd, SW_HIDE); return 0;}}return 1;}__declspec(dllexport) int __cdecl VideoRender(SAMPLE_INFO info)//视频渲染函数{Agan:if (info.hSeek){DWORD mSeek = WaitForSingleObject(info.hSeek, 0);//检测“定位信号”,不等待if (mSeek == WAIT_OBJECT_0)//如果正在定位{return 1;//解除阻塞}}if (info.hRun){DWORD mRun = WaitForSingleObject(info.hRun, 0);//检测“运行”,不等待if (mRun != WAIT_OBJECT_0)//如果已暂停或停止{return 1;//解除阻塞}}DWORD mExit = WaitForSingleObject(pVRender->hExit, 0);//检测“退出”,不等待if (mExit == WAIT_OBJECT_0)//如果有“退出”信号{return 1;//解除阻塞}if (info.B)//如果是此次运行的第1帧{pVRender->STAR = (LONGLONG)info.STAR - info.star / 10000;}if ((LONGLONG)timeGetTime() - pVRender->STAR > info.end / 10000)//如果当前时间超过帧渲染结束时间,跳过该帧return 1;if ((LONGLONG)timeGetTime() - pVRender->STAR < info.star / 10000)//如果当前时间小于帧渲染开始时间,等待goto Agan;SetBitmapBits(pVRender->hBitmap, pVRender->cbBuffer, info.pB);StretchBlt(pVRender->hDC, pVRender->DRect.left, pVRender->DRect.top, pVRender->DRect.right - pVRender->DRect.left, pVRender->DRect.bottom - pVRender->DRect.top,pVRender->hComDC, pVRender->SRect.left, pVRender->SRect.top, pVRender->SRect.right - pVRender->SRect.left, pVRender->SRect.bottom - pVRender->SRect.top, SRCCOPY);return 0;}__declspec(dllexport) int __cdecl AudioRender(SAMPLE_INFO info)//音频渲染函数{HRESULT hr;Agan:if (info.hSeek){DWORD mSeek = WaitForSingleObject(info.hSeek, 0);//检测“定位信号”,不等待if (mSeek == WAIT_OBJECT_0)//如果正在定位{return 1;//解除阻塞}}if (info.hRun){DWORD mRun = WaitForSingleObject(info.hRun, 0);//检测“运行”,不等待if (mRun != WAIT_OBJECT_0)//如果已暂停或停止{hr = pVRender->pAudioClient->Stop(); //停止音频流return 1;//解除阻塞}if (info.B)//如果是此次运行的第1个样本{if (mRun == WAIT_OBJECT_0)//如果正在运行{hr = pVRender->pAudioClient->Reset();//刷新所有挂起的数据hr = pVRender->pAudioClient->Start();//启动音频流}}}DWORD mExit = WaitForSingleObject(pVRender->hExit, 0);//检测“退出”,不等待if (mExit == WAIT_OBJECT_0)//如果有“退出”信号{return 1;//解除阻塞}if (info.B)//如果是此次运行的第1个样本{pVRender->ASTAR = (LONGLONG)info.STAR - info.star / 10000;}if ((LONGLONG)timeGetTime() - pVRender->ASTAR > info.end / 10000)//如果当前时间超过帧渲染结束时间,跳过该帧return 1;if ((LONGLONG)timeGetTime() - pVRender->ASTAR < info.star / 10000)//如果当前时间小于帧渲染开始时间,等待goto Agan;BYTE* pB = NULL; int len = 0;if (pVRender->AudioSamplePerSec != 48000)//如果采样率不是48000{if (info.B)pVRender->index = 0;pB = pVRender->pAudioBuffer;pVRender->Convert(48000, pB, len, pVRender->AudioSamplePerSec, info.pB, info.len);//将采样率转换为48000}else//采样率是48000{pB = info.pB; len = info.len;}BYTE* pData = NULL; DWORD flags = 0;UINT32 numFramesPadding, numFramesAvailable;hr = pVRender->pAudioClient->GetCurrentPadding(&numFramesPadding);//获取未播放的音频帧数numFramesAvailable = pVRender->bufferFrameCount - numFramesPadding;//计算缓冲区中可填充的帧数if ((long)(numFramesAvailable * pVRender->wfx.nBlockAlign) < len)//如果可填充的大小,小于传递的有效数据的大小{Sleep(10); goto Agan;//等待并阻塞}hr = pVRender->pRenderClient->GetBuffer((UINT32)(len / pVRender->wfx.nBlockAlign), &pData);CopyMemory(pData, pB, len);hr = pVRender->pRenderClient->ReleaseBuffer(len / pVRender->wfx.nBlockAlign, flags);//释放GetBuffer获取的缓冲区return 0;}#ifdef __cplusplus
}
#endif
下载本渲染器