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