1 /*
2 * Copyright 2012 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "examples/peerconnection/client/main_wnd.h"
12
13 #include <math.h>
14
15 #include "api/video/i420_buffer.h"
16 #include "examples/peerconnection/client/defaults.h"
17 #include "rtc_base/arraysize.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/logging.h"
20 #include "third_party/libyuv/include/libyuv/convert_argb.h"
21
22 ATOM MainWnd::wnd_class_ = 0;
23 const wchar_t MainWnd::kClassName[] = L"WebRTC_MainWnd";
24
25 namespace {
26
27 const char kConnecting[] = "Connecting... ";
28 const char kNoVideoStreams[] = "(no video streams either way)";
29 const char kNoIncomingStream[] = "(no incoming video)";
30
CalculateWindowSizeForText(HWND wnd,const wchar_t * text,size_t * width,size_t * height)31 void CalculateWindowSizeForText(HWND wnd,
32 const wchar_t* text,
33 size_t* width,
34 size_t* height) {
35 HDC dc = ::GetDC(wnd);
36 RECT text_rc = {0};
37 ::DrawTextW(dc, text, -1, &text_rc, DT_CALCRECT | DT_SINGLELINE);
38 ::ReleaseDC(wnd, dc);
39 RECT client, window;
40 ::GetClientRect(wnd, &client);
41 ::GetWindowRect(wnd, &window);
42
43 *width = text_rc.right - text_rc.left;
44 *width += (window.right - window.left) - (client.right - client.left);
45 *height = text_rc.bottom - text_rc.top;
46 *height += (window.bottom - window.top) - (client.bottom - client.top);
47 }
48
GetDefaultFont()49 HFONT GetDefaultFont() {
50 static HFONT font = reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
51 return font;
52 }
53
GetWindowText(HWND wnd)54 std::string GetWindowText(HWND wnd) {
55 char text[MAX_PATH] = {0};
56 ::GetWindowTextA(wnd, &text[0], ARRAYSIZE(text));
57 return text;
58 }
59
AddListBoxItem(HWND listbox,const std::string & str,LPARAM item_data)60 void AddListBoxItem(HWND listbox, const std::string& str, LPARAM item_data) {
61 LRESULT index = ::SendMessageA(listbox, LB_ADDSTRING, 0,
62 reinterpret_cast<LPARAM>(str.c_str()));
63 ::SendMessageA(listbox, LB_SETITEMDATA, index, item_data);
64 }
65
66 } // namespace
67
MainWnd(const char * server,int port,bool auto_connect,bool auto_call)68 MainWnd::MainWnd(const char* server,
69 int port,
70 bool auto_connect,
71 bool auto_call)
72 : ui_(CONNECT_TO_SERVER),
73 wnd_(NULL),
74 edit1_(NULL),
75 edit2_(NULL),
76 label1_(NULL),
77 label2_(NULL),
78 button_(NULL),
79 listbox_(NULL),
80 destroyed_(false),
81 nested_msg_(NULL),
82 callback_(NULL),
83 server_(server),
84 auto_connect_(auto_connect),
85 auto_call_(auto_call) {
86 char buffer[10];
87 snprintf(buffer, sizeof(buffer), "%i", port);
88 port_ = buffer;
89 }
90
~MainWnd()91 MainWnd::~MainWnd() {
92 RTC_DCHECK(!IsWindow());
93 }
94
Create()95 bool MainWnd::Create() {
96 RTC_DCHECK(wnd_ == NULL);
97 if (!RegisterWindowClass())
98 return false;
99
100 ui_thread_id_ = ::GetCurrentThreadId();
101 wnd_ =
102 ::CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, kClassName, L"WebRTC",
103 WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
104 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
105 CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), this);
106
107 ::SendMessage(wnd_, WM_SETFONT, reinterpret_cast<WPARAM>(GetDefaultFont()),
108 TRUE);
109
110 CreateChildWindows();
111 SwitchToConnectUI();
112
113 return wnd_ != NULL;
114 }
115
Destroy()116 bool MainWnd::Destroy() {
117 BOOL ret = FALSE;
118 if (IsWindow()) {
119 ret = ::DestroyWindow(wnd_);
120 }
121
122 return ret != FALSE;
123 }
124
RegisterObserver(MainWndCallback * callback)125 void MainWnd::RegisterObserver(MainWndCallback* callback) {
126 callback_ = callback;
127 }
128
IsWindow()129 bool MainWnd::IsWindow() {
130 return wnd_ && ::IsWindow(wnd_) != FALSE;
131 }
132
PreTranslateMessage(MSG * msg)133 bool MainWnd::PreTranslateMessage(MSG* msg) {
134 bool ret = false;
135 if (msg->message == WM_CHAR) {
136 if (msg->wParam == VK_TAB) {
137 HandleTabbing();
138 ret = true;
139 } else if (msg->wParam == VK_RETURN) {
140 OnDefaultAction();
141 ret = true;
142 } else if (msg->wParam == VK_ESCAPE) {
143 if (callback_) {
144 if (ui_ == STREAMING) {
145 callback_->DisconnectFromCurrentPeer();
146 } else {
147 callback_->DisconnectFromServer();
148 }
149 }
150 }
151 } else if (msg->hwnd == NULL && msg->message == UI_THREAD_CALLBACK) {
152 callback_->UIThreadCallback(static_cast<int>(msg->wParam),
153 reinterpret_cast<void*>(msg->lParam));
154 ret = true;
155 }
156 return ret;
157 }
158
SwitchToConnectUI()159 void MainWnd::SwitchToConnectUI() {
160 RTC_DCHECK(IsWindow());
161 LayoutPeerListUI(false);
162 ui_ = CONNECT_TO_SERVER;
163 LayoutConnectUI(true);
164 ::SetFocus(edit1_);
165
166 if (auto_connect_)
167 ::PostMessage(button_, BM_CLICK, 0, 0);
168 }
169
SwitchToPeerList(const Peers & peers)170 void MainWnd::SwitchToPeerList(const Peers& peers) {
171 LayoutConnectUI(false);
172
173 ::SendMessage(listbox_, LB_RESETCONTENT, 0, 0);
174
175 AddListBoxItem(listbox_, "List of currently connected peers:", -1);
176 Peers::const_iterator i = peers.begin();
177 for (; i != peers.end(); ++i)
178 AddListBoxItem(listbox_, i->second.c_str(), i->first);
179
180 ui_ = LIST_PEERS;
181 LayoutPeerListUI(true);
182 ::SetFocus(listbox_);
183
184 if (auto_call_ && peers.begin() != peers.end()) {
185 // Get the number of items in the list
186 LRESULT count = ::SendMessage(listbox_, LB_GETCOUNT, 0, 0);
187 if (count != LB_ERR) {
188 // Select the last item in the list
189 LRESULT selection = ::SendMessage(listbox_, LB_SETCURSEL, count - 1, 0);
190 if (selection != LB_ERR)
191 ::PostMessage(wnd_, WM_COMMAND,
192 MAKEWPARAM(GetDlgCtrlID(listbox_), LBN_DBLCLK),
193 reinterpret_cast<LPARAM>(listbox_));
194 }
195 }
196 }
197
SwitchToStreamingUI()198 void MainWnd::SwitchToStreamingUI() {
199 LayoutConnectUI(false);
200 LayoutPeerListUI(false);
201 ui_ = STREAMING;
202 }
203
MessageBox(const char * caption,const char * text,bool is_error)204 void MainWnd::MessageBox(const char* caption, const char* text, bool is_error) {
205 DWORD flags = MB_OK;
206 if (is_error)
207 flags |= MB_ICONERROR;
208
209 ::MessageBoxA(handle(), text, caption, flags);
210 }
211
StartLocalRenderer(webrtc::VideoTrackInterface * local_video)212 void MainWnd::StartLocalRenderer(webrtc::VideoTrackInterface* local_video) {
213 local_renderer_.reset(new VideoRenderer(handle(), 1, 1, local_video));
214 }
215
StopLocalRenderer()216 void MainWnd::StopLocalRenderer() {
217 local_renderer_.reset();
218 }
219
StartRemoteRenderer(webrtc::VideoTrackInterface * remote_video)220 void MainWnd::StartRemoteRenderer(webrtc::VideoTrackInterface* remote_video) {
221 remote_renderer_.reset(new VideoRenderer(handle(), 1, 1, remote_video));
222 }
223
StopRemoteRenderer()224 void MainWnd::StopRemoteRenderer() {
225 remote_renderer_.reset();
226 }
227
QueueUIThreadCallback(int msg_id,void * data)228 void MainWnd::QueueUIThreadCallback(int msg_id, void* data) {
229 ::PostThreadMessage(ui_thread_id_, UI_THREAD_CALLBACK,
230 static_cast<WPARAM>(msg_id),
231 reinterpret_cast<LPARAM>(data));
232 }
233
OnPaint()234 void MainWnd::OnPaint() {
235 PAINTSTRUCT ps;
236 ::BeginPaint(handle(), &ps);
237
238 RECT rc;
239 ::GetClientRect(handle(), &rc);
240
241 VideoRenderer* local_renderer = local_renderer_.get();
242 VideoRenderer* remote_renderer = remote_renderer_.get();
243 if (ui_ == STREAMING && remote_renderer && local_renderer) {
244 AutoLock<VideoRenderer> local_lock(local_renderer);
245 AutoLock<VideoRenderer> remote_lock(remote_renderer);
246
247 const BITMAPINFO& bmi = remote_renderer->bmi();
248 int height = abs(bmi.bmiHeader.biHeight);
249 int width = bmi.bmiHeader.biWidth;
250
251 const uint8_t* image = remote_renderer->image();
252 if (image != NULL) {
253 HDC dc_mem = ::CreateCompatibleDC(ps.hdc);
254 ::SetStretchBltMode(dc_mem, HALFTONE);
255
256 // Set the map mode so that the ratio will be maintained for us.
257 HDC all_dc[] = {ps.hdc, dc_mem};
258 for (size_t i = 0; i < arraysize(all_dc); ++i) {
259 SetMapMode(all_dc[i], MM_ISOTROPIC);
260 SetWindowExtEx(all_dc[i], width, height, NULL);
261 SetViewportExtEx(all_dc[i], rc.right, rc.bottom, NULL);
262 }
263
264 HBITMAP bmp_mem = ::CreateCompatibleBitmap(ps.hdc, rc.right, rc.bottom);
265 HGDIOBJ bmp_old = ::SelectObject(dc_mem, bmp_mem);
266
267 POINT logical_area = {rc.right, rc.bottom};
268 DPtoLP(ps.hdc, &logical_area, 1);
269
270 HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
271 RECT logical_rect = {0, 0, logical_area.x, logical_area.y};
272 ::FillRect(dc_mem, &logical_rect, brush);
273 ::DeleteObject(brush);
274
275 int x = (logical_area.x / 2) - (width / 2);
276 int y = (logical_area.y / 2) - (height / 2);
277
278 StretchDIBits(dc_mem, x, y, width, height, 0, 0, width, height, image,
279 &bmi, DIB_RGB_COLORS, SRCCOPY);
280
281 if ((rc.right - rc.left) > 200 && (rc.bottom - rc.top) > 200) {
282 const BITMAPINFO& bmi = local_renderer->bmi();
283 image = local_renderer->image();
284 int thumb_width = bmi.bmiHeader.biWidth / 4;
285 int thumb_height = abs(bmi.bmiHeader.biHeight) / 4;
286 StretchDIBits(dc_mem, logical_area.x - thumb_width - 10,
287 logical_area.y - thumb_height - 10, thumb_width,
288 thumb_height, 0, 0, bmi.bmiHeader.biWidth,
289 -bmi.bmiHeader.biHeight, image, &bmi, DIB_RGB_COLORS,
290 SRCCOPY);
291 }
292
293 BitBlt(ps.hdc, 0, 0, logical_area.x, logical_area.y, dc_mem, 0, 0,
294 SRCCOPY);
295
296 // Cleanup.
297 ::SelectObject(dc_mem, bmp_old);
298 ::DeleteObject(bmp_mem);
299 ::DeleteDC(dc_mem);
300 } else {
301 // We're still waiting for the video stream to be initialized.
302 HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
303 ::FillRect(ps.hdc, &rc, brush);
304 ::DeleteObject(brush);
305
306 HGDIOBJ old_font = ::SelectObject(ps.hdc, GetDefaultFont());
307 ::SetTextColor(ps.hdc, RGB(0xff, 0xff, 0xff));
308 ::SetBkMode(ps.hdc, TRANSPARENT);
309
310 std::string text(kConnecting);
311 if (!local_renderer->image()) {
312 text += kNoVideoStreams;
313 } else {
314 text += kNoIncomingStream;
315 }
316 ::DrawTextA(ps.hdc, text.c_str(), -1, &rc,
317 DT_SINGLELINE | DT_CENTER | DT_VCENTER);
318 ::SelectObject(ps.hdc, old_font);
319 }
320 } else {
321 HBRUSH brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
322 ::FillRect(ps.hdc, &rc, brush);
323 ::DeleteObject(brush);
324 }
325
326 ::EndPaint(handle(), &ps);
327 }
328
OnDestroyed()329 void MainWnd::OnDestroyed() {
330 PostQuitMessage(0);
331 }
332
OnDefaultAction()333 void MainWnd::OnDefaultAction() {
334 if (!callback_)
335 return;
336 if (ui_ == CONNECT_TO_SERVER) {
337 std::string server(GetWindowText(edit1_));
338 std::string port_str(GetWindowText(edit2_));
339 int port = port_str.length() ? atoi(port_str.c_str()) : 0;
340 callback_->StartLogin(server, port);
341 } else if (ui_ == LIST_PEERS) {
342 LRESULT sel = ::SendMessage(listbox_, LB_GETCURSEL, 0, 0);
343 if (sel != LB_ERR) {
344 LRESULT peer_id = ::SendMessage(listbox_, LB_GETITEMDATA, sel, 0);
345 if (peer_id != -1 && callback_) {
346 callback_->ConnectToPeer(peer_id);
347 }
348 }
349 } else {
350 ::MessageBoxA(wnd_, "OK!", "Yeah", MB_OK);
351 }
352 }
353
OnMessage(UINT msg,WPARAM wp,LPARAM lp,LRESULT * result)354 bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) {
355 switch (msg) {
356 case WM_ERASEBKGND:
357 *result = TRUE;
358 return true;
359
360 case WM_PAINT:
361 OnPaint();
362 return true;
363
364 case WM_SETFOCUS:
365 if (ui_ == CONNECT_TO_SERVER) {
366 SetFocus(edit1_);
367 } else if (ui_ == LIST_PEERS) {
368 SetFocus(listbox_);
369 }
370 return true;
371
372 case WM_SIZE:
373 if (ui_ == CONNECT_TO_SERVER) {
374 LayoutConnectUI(true);
375 } else if (ui_ == LIST_PEERS) {
376 LayoutPeerListUI(true);
377 }
378 break;
379
380 case WM_CTLCOLORSTATIC:
381 *result = reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_WINDOW));
382 return true;
383
384 case WM_COMMAND:
385 if (button_ == reinterpret_cast<HWND>(lp)) {
386 if (BN_CLICKED == HIWORD(wp))
387 OnDefaultAction();
388 } else if (listbox_ == reinterpret_cast<HWND>(lp)) {
389 if (LBN_DBLCLK == HIWORD(wp)) {
390 OnDefaultAction();
391 }
392 }
393 return true;
394
395 case WM_CLOSE:
396 if (callback_)
397 callback_->Close();
398 break;
399 }
400 return false;
401 }
402
403 // static
WndProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)404 LRESULT CALLBACK MainWnd::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
405 MainWnd* me =
406 reinterpret_cast<MainWnd*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
407 if (!me && WM_CREATE == msg) {
408 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lp);
409 me = reinterpret_cast<MainWnd*>(cs->lpCreateParams);
410 me->wnd_ = hwnd;
411 ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(me));
412 }
413
414 LRESULT result = 0;
415 if (me) {
416 void* prev_nested_msg = me->nested_msg_;
417 me->nested_msg_ = &msg;
418
419 bool handled = me->OnMessage(msg, wp, lp, &result);
420 if (WM_NCDESTROY == msg) {
421 me->destroyed_ = true;
422 } else if (!handled) {
423 result = ::DefWindowProc(hwnd, msg, wp, lp);
424 }
425
426 if (me->destroyed_ && prev_nested_msg == NULL) {
427 me->OnDestroyed();
428 me->wnd_ = NULL;
429 me->destroyed_ = false;
430 }
431
432 me->nested_msg_ = prev_nested_msg;
433 } else {
434 result = ::DefWindowProc(hwnd, msg, wp, lp);
435 }
436
437 return result;
438 }
439
440 // static
RegisterWindowClass()441 bool MainWnd::RegisterWindowClass() {
442 if (wnd_class_)
443 return true;
444
445 WNDCLASSEXW wcex = {sizeof(WNDCLASSEX)};
446 wcex.style = CS_DBLCLKS;
447 wcex.hInstance = GetModuleHandle(NULL);
448 wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
449 wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
450 wcex.lpfnWndProc = &WndProc;
451 wcex.lpszClassName = kClassName;
452 wnd_class_ = ::RegisterClassExW(&wcex);
453 RTC_DCHECK(wnd_class_ != 0);
454 return wnd_class_ != 0;
455 }
456
CreateChildWindow(HWND * wnd,MainWnd::ChildWindowID id,const wchar_t * class_name,DWORD control_style,DWORD ex_style)457 void MainWnd::CreateChildWindow(HWND* wnd,
458 MainWnd::ChildWindowID id,
459 const wchar_t* class_name,
460 DWORD control_style,
461 DWORD ex_style) {
462 if (::IsWindow(*wnd))
463 return;
464
465 // Child windows are invisible at first, and shown after being resized.
466 DWORD style = WS_CHILD | control_style;
467 *wnd = ::CreateWindowExW(ex_style, class_name, L"", style, 100, 100, 100, 100,
468 wnd_, reinterpret_cast<HMENU>(id),
469 GetModuleHandle(NULL), NULL);
470 RTC_DCHECK(::IsWindow(*wnd) != FALSE);
471 ::SendMessage(*wnd, WM_SETFONT, reinterpret_cast<WPARAM>(GetDefaultFont()),
472 TRUE);
473 }
474
CreateChildWindows()475 void MainWnd::CreateChildWindows() {
476 // Create the child windows in tab order.
477 CreateChildWindow(&label1_, LABEL1_ID, L"Static", ES_CENTER | ES_READONLY, 0);
478 CreateChildWindow(&edit1_, EDIT_ID, L"Edit",
479 ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, WS_EX_CLIENTEDGE);
480 CreateChildWindow(&label2_, LABEL2_ID, L"Static", ES_CENTER | ES_READONLY, 0);
481 CreateChildWindow(&edit2_, EDIT_ID, L"Edit",
482 ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, WS_EX_CLIENTEDGE);
483 CreateChildWindow(&button_, BUTTON_ID, L"Button", BS_CENTER | WS_TABSTOP, 0);
484
485 CreateChildWindow(&listbox_, LISTBOX_ID, L"ListBox",
486 LBS_HASSTRINGS | LBS_NOTIFY, WS_EX_CLIENTEDGE);
487
488 ::SetWindowTextA(edit1_, server_.c_str());
489 ::SetWindowTextA(edit2_, port_.c_str());
490 }
491
LayoutConnectUI(bool show)492 void MainWnd::LayoutConnectUI(bool show) {
493 struct Windows {
494 HWND wnd;
495 const wchar_t* text;
496 size_t width;
497 size_t height;
498 } windows[] = {
499 {label1_, L"Server"}, {edit1_, L"XXXyyyYYYgggXXXyyyYYYggg"},
500 {label2_, L":"}, {edit2_, L"XyXyX"},
501 {button_, L"Connect"},
502 };
503
504 if (show) {
505 const size_t kSeparator = 5;
506 size_t total_width = (ARRAYSIZE(windows) - 1) * kSeparator;
507
508 for (size_t i = 0; i < ARRAYSIZE(windows); ++i) {
509 CalculateWindowSizeForText(windows[i].wnd, windows[i].text,
510 &windows[i].width, &windows[i].height);
511 total_width += windows[i].width;
512 }
513
514 RECT rc;
515 ::GetClientRect(wnd_, &rc);
516 size_t x = (rc.right / 2) - (total_width / 2);
517 size_t y = rc.bottom / 2;
518 for (size_t i = 0; i < ARRAYSIZE(windows); ++i) {
519 size_t top = y - (windows[i].height / 2);
520 ::MoveWindow(windows[i].wnd, static_cast<int>(x), static_cast<int>(top),
521 static_cast<int>(windows[i].width),
522 static_cast<int>(windows[i].height), TRUE);
523 x += kSeparator + windows[i].width;
524 if (windows[i].text[0] != 'X')
525 ::SetWindowTextW(windows[i].wnd, windows[i].text);
526 ::ShowWindow(windows[i].wnd, SW_SHOWNA);
527 }
528 } else {
529 for (size_t i = 0; i < ARRAYSIZE(windows); ++i) {
530 ::ShowWindow(windows[i].wnd, SW_HIDE);
531 }
532 }
533 }
534
LayoutPeerListUI(bool show)535 void MainWnd::LayoutPeerListUI(bool show) {
536 if (show) {
537 RECT rc;
538 ::GetClientRect(wnd_, &rc);
539 ::MoveWindow(listbox_, 0, 0, rc.right, rc.bottom, TRUE);
540 ::ShowWindow(listbox_, SW_SHOWNA);
541 } else {
542 ::ShowWindow(listbox_, SW_HIDE);
543 InvalidateRect(wnd_, NULL, TRUE);
544 }
545 }
546
HandleTabbing()547 void MainWnd::HandleTabbing() {
548 bool shift = ((::GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
549 UINT next_cmd = shift ? GW_HWNDPREV : GW_HWNDNEXT;
550 UINT loop_around_cmd = shift ? GW_HWNDLAST : GW_HWNDFIRST;
551 HWND focus = GetFocus(), next;
552 do {
553 next = ::GetWindow(focus, next_cmd);
554 if (IsWindowVisible(next) &&
555 (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP)) {
556 break;
557 }
558
559 if (!next) {
560 next = ::GetWindow(focus, loop_around_cmd);
561 if (IsWindowVisible(next) &&
562 (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP)) {
563 break;
564 }
565 }
566 focus = next;
567 } while (true);
568 ::SetFocus(next);
569 }
570
571 //
572 // MainWnd::VideoRenderer
573 //
574
VideoRenderer(HWND wnd,int width,int height,webrtc::VideoTrackInterface * track_to_render)575 MainWnd::VideoRenderer::VideoRenderer(
576 HWND wnd,
577 int width,
578 int height,
579 webrtc::VideoTrackInterface* track_to_render)
580 : wnd_(wnd), rendered_track_(track_to_render) {
581 ::InitializeCriticalSection(&buffer_lock_);
582 ZeroMemory(&bmi_, sizeof(bmi_));
583 bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
584 bmi_.bmiHeader.biPlanes = 1;
585 bmi_.bmiHeader.biBitCount = 32;
586 bmi_.bmiHeader.biCompression = BI_RGB;
587 bmi_.bmiHeader.biWidth = width;
588 bmi_.bmiHeader.biHeight = -height;
589 bmi_.bmiHeader.biSizeImage =
590 width * height * (bmi_.bmiHeader.biBitCount >> 3);
591 rendered_track_->AddOrUpdateSink(this, rtc::VideoSinkWants());
592 }
593
~VideoRenderer()594 MainWnd::VideoRenderer::~VideoRenderer() {
595 rendered_track_->RemoveSink(this);
596 ::DeleteCriticalSection(&buffer_lock_);
597 }
598
SetSize(int width,int height)599 void MainWnd::VideoRenderer::SetSize(int width, int height) {
600 AutoLock<VideoRenderer> lock(this);
601
602 if (width == bmi_.bmiHeader.biWidth && height == bmi_.bmiHeader.biHeight) {
603 return;
604 }
605
606 bmi_.bmiHeader.biWidth = width;
607 bmi_.bmiHeader.biHeight = -height;
608 bmi_.bmiHeader.biSizeImage =
609 width * height * (bmi_.bmiHeader.biBitCount >> 3);
610 image_.reset(new uint8_t[bmi_.bmiHeader.biSizeImage]);
611 }
612
OnFrame(const webrtc::VideoFrame & video_frame)613 void MainWnd::VideoRenderer::OnFrame(const webrtc::VideoFrame& video_frame) {
614 {
615 AutoLock<VideoRenderer> lock(this);
616
617 rtc::scoped_refptr<webrtc::I420BufferInterface> buffer(
618 video_frame.video_frame_buffer()->ToI420());
619 if (video_frame.rotation() != webrtc::kVideoRotation_0) {
620 buffer = webrtc::I420Buffer::Rotate(*buffer, video_frame.rotation());
621 }
622
623 SetSize(buffer->width(), buffer->height());
624
625 RTC_DCHECK(image_.get() != NULL);
626 libyuv::I420ToARGB(buffer->DataY(), buffer->StrideY(), buffer->DataU(),
627 buffer->StrideU(), buffer->DataV(), buffer->StrideV(),
628 image_.get(),
629 bmi_.bmiHeader.biWidth * bmi_.bmiHeader.biBitCount / 8,
630 buffer->width(), buffer->height());
631 }
632 InvalidateRect(wnd_, NULL, TRUE);
633 }
634