Skip to content

Commit 102215e

Browse files
authored
Add files via upload
Frame resolution update inside the loop, Enumerate monitors
1 parent b339504 commit 102215e

File tree

1 file changed

+157
-103
lines changed

1 file changed

+157
-103
lines changed

SendWindowsToNDI/SendWindowsToNDI.cpp

Lines changed: 157 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,57 @@
1717
using namespace std;
1818

1919
vector<HWND> vec;
20+
HANDLE hDIB;
21+
vector<wstring> titles;
22+
23+
BOOL CALLBACK mycallbackMonitores(
24+
HMONITOR Arg1,
25+
HDC Arg2,
26+
LPRECT Arg3,
27+
LPARAM Arg4
28+
){
29+
30+
// Get monitor info resolution to only show it
31+
MONITORINFO minfo;
32+
//ZeroMemory(&minfo, sizeof(MONITORINFO));
33+
minfo.cbSize = sizeof(MONITORINFO);
34+
GetMonitorInfoA(Arg1, &minfo);
35+
int w = minfo.rcMonitor.right - minfo.rcMonitor.left;
36+
int h = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
37+
38+
// Get windows Desktop handle
39+
HWND hWnd = GetDesktopWindow();
40+
41+
// Fill the data with a name and the windows handle
42+
WCHAR windowTitle[100];
43+
wstring title(windowTitle);
44+
titles.push_back(title);
45+
vec.push_back(hWnd);
46+
47+
// show info in screen
48+
int& indexMonitor = *reinterpret_cast<int*>(Arg4);
49+
50+
cout << "[" << (int)vec.size() - 1 << "] " << "..." << "Desktop " << indexMonitor << " -- resx: " << w << " resy: " << h;
51+
if (minfo.dwFlags) {
52+
cout << " (Main Monitor)";
53+
}
54+
cout << "\n";
55+
56+
// new index to the next loop
57+
indexMonitor += 1;
58+
59+
return TRUE;
60+
}
61+
2062

2163
BOOL CALLBACK mycallback(HWND hwnd, LPARAM lParam) {
2264
const DWORD TITLE_SIZE = 1024;
2365
WCHAR windowTitle[TITLE_SIZE];
2466

2567
GetWindowTextW(hwnd, windowTitle, TITLE_SIZE);
26-
68+
2769
int length = ::GetWindowTextLength(hwnd);
28-
std::wstring title(&windowTitle[0]);
70+
wstring title(&windowTitle[0]);
2971

3072

3173
if (!IsWindowVisible(hwnd) || length == 0) {
@@ -35,16 +77,17 @@ BOOL CALLBACK mycallback(HWND hwnd, LPARAM lParam) {
3577

3678
// Retrieve the pointer passed into this callback, and re-'type' it.
3779
// The only way for a C API to pass arbitrary data is by means of a void*.
38-
std::vector<std::wstring>& titles = *reinterpret_cast<std::vector<std::wstring>*>(lParam);
80+
vector<wstring>& titles = *reinterpret_cast<vector<wstring>*>(lParam);
3981
titles.push_back(title);
4082
vec.push_back(hwnd);
41-
cout << "............ " << "[" << (int)titles.size() - 1 << "] " << hwnd << "..." << string(title.begin(), title.end()) << endl;
42-
83+
cout << "[" << (int)titles.size() - 1 << "] " << hwnd << "..." << string(title.begin(), title.end()) << endl;
4384
return TRUE;
4485
}
4586

46-
int SendWindow(HWND hWnd, const char* ndi_name)
47-
{
87+
// This function return a frame
88+
// that contain a bitmat buffer captured from de window
89+
NDIlib_video_frame_v2_t CreateFrameFromHWND(HWND hWnd) {
90+
4891
HDC hdcWindow;
4992
HDC hdcMemDC = NULL;
5093
HBITMAP hbmScreen = NULL;
@@ -53,170 +96,181 @@ int SendWindow(HWND hWnd, const char* ndi_name)
5396
// Retrieve the handle to a display device context for the client
5497
// area of the window.
5598
hdcWindow = GetDC(hWnd);
99+
100+
// Get the client area for size calculation
101+
RECT rcClient;
102+
GetClientRect(hWnd, &rcClient);
56103

104+
// We are going to open a Win32 wrapper at rcClient resolution
105+
int xres = rcClient.right - rcClient.left;
106+
int yres = rcClient.bottom - rcClient.top;
107+
57108
// Create a compatible DC which is used in a BitBlt from the window DC
58109
hdcMemDC = CreateCompatibleDC(hdcWindow);
59-
if (!hdcMemDC)
110+
111+
// Create a compatible bitmap from the Window DC
112+
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
113+
114+
// Select the compatible bitmap into the compatible memory DC.
115+
SelectObject(hdcMemDC, hbmScreen);
116+
117+
// Bit block transfer into our compatible memory DC.
118+
if (!BitBlt(hdcMemDC,
119+
0, 0,
120+
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
121+
hdcWindow,
122+
0, 0,
123+
SRCCOPY))
60124
{
61-
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
125+
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
62126
}
63127

64-
// Get the client area for size calculation
65-
RECT rcClient;
66-
GetClientRect(hWnd, &rcClient);
67-
68-
// We are going to open a Win32 wrapper at rcClient resolution at 29.97fps
69-
const int xres = rcClient.right - rcClient.left;
70-
const int yres = rcClient.bottom - rcClient.top;
128+
// Get the BITMAP from the HBITMAP
129+
//GetBitmapBits()
130+
GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);
131+
132+
BITMAPINFOHEADER bi;
133+
134+
bi.biSize = sizeof(BITMAPINFOHEADER);
135+
bi.biWidth = bmpScreen.bmWidth;
136+
bi.biHeight = -bmpScreen.bmHeight;
137+
bi.biPlanes = 1;
138+
bi.biBitCount = 32;
139+
bi.biCompression = BI_RGB;
140+
bi.biSizeImage = 0;
141+
bi.biXPelsPerMeter = 0;
142+
bi.biYPelsPerMeter = 0;
143+
bi.biClrUsed = 0;
144+
bi.biClrImportant = 0;
145+
146+
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
147+
148+
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
149+
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
150+
// have greater overhead than HeapAlloc.
151+
hDIB = GlobalAlloc(GHND, dwBmpSize);
152+
char *lpbitmap = (char *)GlobalLock(hDIB);
153+
154+
// Gets the "bits" from the bitmap and copies them into a buffer
155+
// which is pointed to by lpbitmap.
156+
GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
157+
158+
// at 29.97fps
71159
const int framerate_n = 30000;
72160
const int framerate_d = 1001;
73161

74-
//This is the best stretch mode
75-
//SetStretchBltMode(hdcWindow, HALFTONE);
162+
// Describe the frame.
163+
// Note another undocumented feature is that if you pass the pointer as NULL then it does not send the frame,
164+
// but it does still clock it correctly as if you did :) I bet you are glad you are reading this example.
165+
NDIlib_video_frame_v2_t frame;
166+
frame.xres = xres;
167+
frame.yres = yres;
168+
frame.FourCC = NDIlib_FourCC_type_BGRA;
169+
frame.frame_rate_N = framerate_n;
170+
frame.frame_rate_D = framerate_d;
171+
frame.p_data = (uint8_t*)lpbitmap;
172+
frame.line_stride_in_bytes = xres * 4;
76173

77-
// Create a compatible bitmap from the Window DC
78-
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
79-
if (!hbmScreen)
80-
{
81-
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
82-
}
174+
//Clean up
175+
DeleteObject(hbmScreen);
176+
DeleteObject(hdcMemDC);
177+
ReleaseDC(hWnd, hdcWindow);
83178

84-
// Select the compatible bitmap into the compatible memory DC.
85-
SelectObject(hdcMemDC, hbmScreen);
179+
return frame;
180+
}
181+
182+
int SendWindow(HWND hWnd, const char* ndi_name)
183+
{
86184

87185
///////////////////
88-
// Create an NDI source that is called "My Video" and is clocked to the video.
186+
// Create an NDI source and is clocked to the video.
89187
NDIlib_send_create_t NDI_send_create_desc;
90188
NDI_send_create_desc.p_ndi_name = ndi_name;
91189
NDIlib_send_instance_t m_pNDI_send = NDIlib_send_create(&NDI_send_create_desc);
92190
if (!m_pNDI_send) { return 0; };
93191

94192
cout << "\n";
95193

194+
// Loop
96195
for (int x = 0, dx = (rand() & 7), cnt = 0;; x += dx)
97196
{
197+
98198
// Get the system time
99199
wchar_t temp[128];
100200
swprintf(temp, 128, L"%06d", cnt++);
101201
wcout << temp << "\r";
102202

103-
// Bit block transfer into our compatible memory DC.
104-
if (!BitBlt(hdcMemDC,
105-
0, 0,
106-
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
107-
hdcWindow,
108-
0, 0,
109-
SRCCOPY))
110-
{
111-
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
112-
}
113-
114-
// Get the BITMAP from the HBITMAP
115-
GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);
116-
117-
BITMAPINFOHEADER bi;
118-
119-
bi.biSize = sizeof(BITMAPINFOHEADER);
120-
bi.biWidth = bmpScreen.bmWidth;
121-
bi.biHeight = -bmpScreen.bmHeight;
122-
bi.biPlanes = 1;
123-
bi.biBitCount = 32;
124-
bi.biCompression = BI_RGB;
125-
bi.biSizeImage = 0;
126-
bi.biXPelsPerMeter = 0;
127-
bi.biYPelsPerMeter = 0;
128-
bi.biClrUsed = 0;
129-
bi.biClrImportant = 0;
130-
131-
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
132-
133-
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
134-
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
135-
// have greater overhead than HeapAlloc.
136-
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
137-
char *lpbitmap = (char *)GlobalLock(hDIB);
138-
139-
// Gets the "bits" from the bitmap and copies them into a buffer
140-
// which is pointed to by lpbitmap.
141-
GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
142-
143-
// Describe the frame. Note another undocumented feature is that if you pass the pointer as NULL then it does not send the frame,
144-
// but it does still clock it correctly as if you did :) I bet you are glad you are reading this example.
145-
NDIlib_video_frame_v2_t frame;
146-
frame.xres = xres;
147-
frame.yres = yres;
148-
frame.FourCC = NDIlib_FourCC_type_BGRA;
149-
frame.frame_rate_N = framerate_n;
150-
frame.frame_rate_D = framerate_d;
151-
frame.p_data = (uint8_t*)lpbitmap;
152-
frame.line_stride_in_bytes = xres * 4;
153-
154203
// We now just send the frame
155204
if (m_pNDI_send)
156-
NDIlib_send_send_video_v2(m_pNDI_send, &frame);/**/
205+
NDIlib_send_send_video_v2(m_pNDI_send, &CreateFrameFromHWND(hWnd));
157206

158207
//Free memory heap
159208
GlobalFree(hDIB);
160209
}
161210

162-
//Clean up
163-
DeleteObject(hbmScreen);
164-
DeleteObject(hdcMemDC);
165-
ReleaseDC(hWnd, hdcWindow);
211+
166212

167213
return 0;
168214
}
169215

170216
int main()
171217
{
172-
std::cout << "\n" << endl;
173-
std::cout << "/////////////////////\n" << endl;
174-
std::cout << "Windows list!\n" << endl;
175-
176-
//info
177-
/*const DWORD info_SIZE = 1024;
178-
WCHAR infobuf[info_SIZE];
218+
219+
cout << "\n///////////////////// " ;
220+
cout << "Monitor list! \n" << endl;
179221

180-
GetComputerNameW(infobuf, (DWORD*)&info_SIZE);
181-
wcout << L"Equipo: " << infobuf << endl;
222+
//Enumera the monitors
223+
int g = 0;
224+
EnumDisplayMonitors(NULL, NULL, mycallbackMonitores, (LPARAM)&g);
182225

183-
GetUserNameW(infobuf, (DWORD*)&info_SIZE);
184-
wcout << L"Usuario: " << infobuf << endl;
185-
*/
226+
cout << "\n///////////////////// " ;
227+
cout << "Windows list! \n" << endl;
186228

187229
//Enumera las ventanas
188-
std::vector<std::wstring> titles;
189230
EnumWindows(mycallback, reinterpret_cast<LPARAM>(&titles));
190231

191232
//input seleccion
192233
int x;
193-
cout << "\nSelect a window: (number between " << 0 << " and " << (int)titles.size() - 1 << "): ";
234+
cout << "\nSelect a window or desktop: (number between " << 0 << " and " << (int)vec.size() - 1 << "): ";
194235
cin >> x;
195236

196-
//check si se introdujo un numero correcto
197-
while (cin.fail() || x < 0 || x >(int)titles.size() - 1) {
237+
//check user input
238+
while (cin.fail() || x < 0 || x >(int)vec.size() - 1) {
198239

199240
cout << "repetir: " << endl;
200241
cin.clear();
201242
cin.ignore(256, '\n');
202243
cin >> x;
203244
}
204-
cout << "ok" << endl;
205245

206-
// retransmite ventana por ndi
246+
cout << "ok" << endl;
207247
cout << "Broadcasting -------> " << vec[x] << " ... " << string(titles[x].begin(), titles[x].end());
208248

209-
// Not required, but "correct" (see the SDK documentation.
249+
// Not required, but "correct" (see the SDK documentation.)
210250
if (!NDIlib_initialize())
211251
{
212252
// Cannot run NDI. Most likely because the CPU is not sufficient (see SDK documentation).
213253
// you can check this directly with a call to NDIlib_is_supported_CPU()
214254
printf("Cannot run NDI.");
215255
return 0;
216256
}
257+
258+
//string sederName = string(titles[x].begin(), titles[x].end());
259+
const wchar_t* input = titles[x].c_str();
260+
261+
/*
262+
// make name to the sender description
263+
size_t size = (wcslen(input) + 1) * sizeof(wchar_t);
264+
char* buffer = new char[size];
265+
size_t convertedsize;
266+
wcstombs_s(&convertedsize, buffer, size, input, size);
267+
//const char* sederName = (const char*)buffer;
268+
*/
217269

218-
string b = string(titles[x].begin(), titles[x].end());
270+
// Broadcasting window to ndi
219271
SendWindow(vec[x], "__ndi");
272+
273+
//delete buffer;
220274

221275
// Not required, but nice
222276
NDIlib_destroy();

0 commit comments

Comments
 (0)