Windows软件插件-视频渲染器

下载本渲染器

本视频渲染器渲染RGB32视频流。内置音频渲染器,可渲染PCM音频流。音频渲染器包含采样率转换算法,可以接受任何采样率。也可隐藏视频渲染窗口,只作为音频渲染器使用。本渲染器是脱离DirectShow的视频渲染器,在应用程序中可能更会使用到。

使用方法

首先,加载本视频渲染器DLL,获得渲染器模块句柄。

cpp 复制代码
	HMODULE hVRender = LoadLibrary(L"VRender.dll");

定义媒体信息和样本信息结构,定义导出函数指针:

cpp 复制代码
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);

获取导出函数的地址:

cpp 复制代码
	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结构提供初始化参数,包括:视频的宽高,音频采样率;并指定最初是否显示渲染窗口,初始化本渲染器:

cpp 复制代码
	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秒的数据量。

反复的调用视频渲染函数和音频渲染函数,样本数据将传递给渲染器,将获得视频图像和播放音频数据。如果渲染时间未到,函数将阻塞以等待渲染时间;如果已过样本的结束时间,将丢弃该样本,在渲染函数的下一次调用中,渲染下一个样本。

cpp 复制代码
	SAMPLE_INFO Vinfo;//样本信息结构已包含信息
	VRender_VideoRender(Vinfo);
	SAMPLE_INFO Ainfo;//样本信息结构已包含信息
	VRender_AudioRender(Ainfo);

其他导出函数实现对渲染器窗口的操作和渲染器的销毁:

cpp 复制代码
	VRender_Flush();//丢弃音频渲染缓冲区的所有数据
	VRender_Redraw();//重绘渲染器窗口
	VRender_Exit();//销毁渲染器
	VRender_FullScreen(TRUE);//参数TRUE,全屏渲染窗口。FALSE退出全屏
	VRender_VSetParent(TRUE,hParent,rect);//参数1为TRUE,将渲染窗口设置为hParent的子窗口,rect指定窗口大小和位置。参数1为FALSE时,窗口恢复为弹出窗口;此时忽略参数2,3
	VRender_Show(TRUE);//参数为TRUE,显示渲染窗口,FALSE隐藏渲染窗口
	

有关本视频渲染器的使用示例,可以看后续文章"Windows应用-播放视频"。

本视频渲染器DLL的全部代码

DLL.cpp

cpp 复制代码
#include "windows.h"
#include "mmsystem.h"
#pragma comment(lib, "winmm")
#include "mmdeviceapi.h"
#include "audioclient.h"
#include "resource.h"

#define REFTIMES_PER_SEC  10000000

template <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);//获取渲染窗口DC
	pVRender->hComDC = CreateCompatibleDC(pVRender->hDC);//创建兼容DC
	pVRender->hBitmap = CreateBitmap(pVRender->DRect.right - pVRender->DRect.left, pVRender->DRect.bottom - pVRender->DRect.top, 1, 32, NULL);//创建32位位图
	SelectObject(pVRender->hComDC, pVRender->hBitmap);//选择位图到兼容DC
	SetStretchBltMode(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);//创建兼容DC
			DeleteObject(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);//选择位图到兼容DC
			SetStretchBltMode(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

下载本渲染器

相关推荐
蒋星熠11 分钟前
(枚举专题)组合数枚举
c语言·数据结构·c++·算法
香菇滑稽之谈43 分钟前
模板方法模式的C++实现示例
开发语言·c++·设计模式·模板方法模式
Andlin1 小时前
CMakeList 知识系统学习系列(四):条件与循环
c++
轩源源1 小时前
封装哈希表实现unordered_map和unordered_set
开发语言·数据结构·c++·算法·哈希算法·散列表
浪九天1 小时前
面向高质量视频生成的扩散模型方法-算法、架构与实现【附核心代码】
python·深度学习·算法·机器学习·自然语言处理·数据挖掘·音视频
Vitalia1 小时前
⭐算法OJ⭐全排列【回溯】(C++实现)Permutations II
数据结构·c++·算法
熊峰峰2 小时前
数据结构第八节:红黑树(初阶)
开发语言·数据结构·c++·算法
313YPHU32 小时前
【音视频开发】第一章 音视频基础概念
音视频
梅茜Mercy2 小时前
C++:入门详解(关于C与C++基本差别)
java·c语言·c++
f狐0狸x3 小时前
【蓝桥杯每日一题】3.8
数据结构·c++·算法·蓝桥杯