• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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