1 /* 2 * Copyright (c) 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 "webrtc/modules/video_render/test/testAPI/testAPI.h" 12 13 #include <stdio.h> 14 15 #if defined(_WIN32) 16 #include <tchar.h> 17 #include <windows.h> 18 #include <assert.h> 19 #include <fstream> 20 #include <iostream> 21 #include <string> 22 #include <windows.h> 23 #include <ddraw.h> 24 25 #elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) 26 27 #include <X11/Xlib.h> 28 #include <X11/Xutil.h> 29 #include <iostream> 30 #include <sys/time.h> 31 32 #endif 33 34 #include "webrtc/common_types.h" 35 #include "webrtc/modules/interface/module_common_types.h" 36 #include "webrtc/modules/utility/interface/process_thread.h" 37 #include "webrtc/modules/video_render/include/video_render.h" 38 #include "webrtc/modules/video_render/include/video_render_defines.h" 39 #include "webrtc/system_wrappers/interface/sleep.h" 40 #include "webrtc/system_wrappers/interface/tick_util.h" 41 #include "webrtc/system_wrappers/interface/trace.h" 42 43 using namespace webrtc; 44 45 void GetTestVideoFrame(I420VideoFrame* frame, 46 uint8_t startColor); 47 int TestSingleStream(VideoRender* renderModule); 48 int TestFullscreenStream(VideoRender* &renderModule, 49 void* window, 50 const VideoRenderType videoRenderType); 51 int TestBitmapText(VideoRender* renderModule); 52 int TestMultipleStreams(VideoRender* renderModule); 53 int TestExternalRender(VideoRender* renderModule); 54 55 #define TEST_FRAME_RATE 30 56 #define TEST_TIME_SECOND 5 57 #define TEST_FRAME_NUM (TEST_FRAME_RATE*TEST_TIME_SECOND) 58 #define TEST_STREAM0_START_COLOR 0 59 #define TEST_STREAM1_START_COLOR 64 60 #define TEST_STREAM2_START_COLOR 128 61 #define TEST_STREAM3_START_COLOR 192 62 63 #if defined(WEBRTC_LINUX) 64 65 #define GET_TIME_IN_MS timeGetTime() 66 67 unsigned long timeGetTime() 68 { 69 struct timeval tv; 70 struct timezone tz; 71 unsigned long val; 72 73 gettimeofday(&tv, &tz); 74 val= tv.tv_sec*1000+ tv.tv_usec/1000; 75 return(val); 76 } 77 78 #elif defined(WEBRTC_MAC) 79 80 #include <unistd.h> 81 82 #define GET_TIME_IN_MS timeGetTime() 83 84 unsigned long timeGetTime() 85 { 86 return 0; 87 } 88 89 #else 90 91 #define GET_TIME_IN_MS ::timeGetTime() 92 93 #endif 94 95 using namespace std; 96 97 #if defined(_WIN32) 98 LRESULT CALLBACK WebRtcWinProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) 99 { 100 switch(uMsg) 101 { 102 case WM_DESTROY: 103 break; 104 case WM_COMMAND: 105 break; 106 } 107 return DefWindowProc(hWnd,uMsg,wParam,lParam); 108 } 109 110 int WebRtcCreateWindow(HWND &hwndMain,int winNum, int width, int height) 111 { 112 HINSTANCE hinst = GetModuleHandle(0); 113 WNDCLASSEX wcx; 114 wcx.hInstance = hinst; 115 wcx.lpszClassName = TEXT("VideoRenderTest"); 116 wcx.lpfnWndProc = (WNDPROC)WebRtcWinProc; 117 wcx.style = CS_DBLCLKS; 118 wcx.hIcon = LoadIcon (NULL, IDI_APPLICATION); 119 wcx.hIconSm = LoadIcon (NULL, IDI_APPLICATION); 120 wcx.hCursor = LoadCursor (NULL, IDC_ARROW); 121 wcx.lpszMenuName = NULL; 122 wcx.cbSize = sizeof (WNDCLASSEX); 123 wcx.cbClsExtra = 0; 124 wcx.cbWndExtra = 0; 125 wcx.hbrBackground = GetSysColorBrush(COLOR_3DFACE); 126 127 // Register our window class with the operating system. 128 // If there is an error, exit program. 129 if ( !RegisterClassEx (&wcx) ) 130 { 131 MessageBox( 0, TEXT("Failed to register window class!"),TEXT("Error!"), MB_OK|MB_ICONERROR ); 132 return 0; 133 } 134 135 // Create the main window. 136 hwndMain = CreateWindowEx( 137 0, // no extended styles 138 TEXT("VideoRenderTest"), // class name 139 TEXT("VideoRenderTest Window"), // window name 140 WS_OVERLAPPED |WS_THICKFRAME, // overlapped window 141 800, // horizontal position 142 0, // vertical position 143 width, // width 144 height, // height 145 (HWND) NULL, // no parent or owner window 146 (HMENU) NULL, // class menu used 147 hinst, // instance handle 148 NULL); // no window creation data 149 150 if (!hwndMain) 151 return -1; 152 153 // Show the window using the flag specified by the program 154 // that started the application, and send the application 155 // a WM_PAINT message. 156 157 ShowWindow(hwndMain, SW_SHOWDEFAULT); 158 UpdateWindow(hwndMain); 159 return 0; 160 } 161 162 #elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) 163 164 int WebRtcCreateWindow(Window *outWindow, Display **outDisplay, int winNum, int width, int height) // unsigned char* title, int titleLength) 165 166 { 167 int screen, xpos = 10, ypos = 10; 168 XEvent evnt; 169 XSetWindowAttributes xswa; // window attribute struct 170 XVisualInfo vinfo; // screen visual info struct 171 unsigned long mask; // attribute mask 172 173 // get connection handle to xserver 174 Display* _display = XOpenDisplay( NULL ); 175 176 // get screen number 177 screen = DefaultScreen(_display); 178 179 // put desired visual info for the screen in vinfo 180 if( XMatchVisualInfo(_display, screen, 24, TrueColor, &vinfo) != 0 ) 181 { 182 //printf( "Screen visual info match!\n" ); 183 } 184 185 // set window attributes 186 xswa.colormap = XCreateColormap(_display, DefaultRootWindow(_display), vinfo.visual, AllocNone); 187 xswa.event_mask = StructureNotifyMask | ExposureMask; 188 xswa.background_pixel = 0; 189 xswa.border_pixel = 0; 190 191 // value mask for attributes 192 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; 193 194 switch( winNum ) 195 { 196 case 0: 197 xpos = 200; 198 ypos = 200; 199 break; 200 case 1: 201 xpos = 300; 202 ypos = 200; 203 break; 204 default: 205 break; 206 } 207 208 // create a subwindow for parent (defroot) 209 Window _window = XCreateWindow(_display, DefaultRootWindow(_display), 210 xpos, ypos, 211 width, 212 height, 213 0, vinfo.depth, 214 InputOutput, 215 vinfo.visual, 216 mask, &xswa); 217 218 // Set window name 219 if( winNum == 0 ) 220 { 221 XStoreName(_display, _window, "VE MM Local Window"); 222 XSetIconName(_display, _window, "VE MM Local Window"); 223 } 224 else if( winNum == 1 ) 225 { 226 XStoreName(_display, _window, "VE MM Remote Window"); 227 XSetIconName(_display, _window, "VE MM Remote Window"); 228 } 229 230 // make x report events for mask 231 XSelectInput(_display, _window, StructureNotifyMask); 232 233 // map the window to the display 234 XMapWindow(_display, _window); 235 236 // wait for map event 237 do 238 { 239 XNextEvent(_display, &evnt); 240 } 241 while (evnt.type != MapNotify || evnt.xmap.event != _window); 242 243 *outWindow = _window; 244 *outDisplay = _display; 245 246 return 0; 247 } 248 #endif // LINUX 249 250 // Note: Mac code is in testApi_mac.mm. 251 252 class MyRenderCallback: public VideoRenderCallback 253 { 254 public: 255 MyRenderCallback() : 256 _cnt(0) 257 { 258 } 259 ; 260 ~MyRenderCallback() 261 { 262 } 263 ; 264 virtual int32_t RenderFrame(const uint32_t streamId, 265 I420VideoFrame& videoFrame) 266 { 267 _cnt++; 268 if (_cnt % 100 == 0) 269 { 270 printf("Render callback %d \n",_cnt); 271 } 272 return 0; 273 } 274 int32_t _cnt; 275 }; 276 277 void GetTestVideoFrame(I420VideoFrame* frame, 278 uint8_t startColor) { 279 // changing color 280 static uint8_t color = startColor; 281 282 memset(frame->buffer(kYPlane), color, frame->allocated_size(kYPlane)); 283 memset(frame->buffer(kUPlane), color, frame->allocated_size(kUPlane)); 284 memset(frame->buffer(kVPlane), color, frame->allocated_size(kVPlane)); 285 286 ++color; 287 } 288 289 int TestSingleStream(VideoRender* renderModule) { 290 int error = 0; 291 // Add settings for a stream to render 292 printf("Add stream 0 to entire window\n"); 293 const int streamId0 = 0; 294 VideoRenderCallback* renderCallback0 = renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f); 295 assert(renderCallback0 != NULL); 296 297 #ifndef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER 298 MyRenderCallback externalRender; 299 renderModule->AddExternalRenderCallback(streamId0, &externalRender); 300 #endif 301 302 printf("Start render\n"); 303 error = renderModule->StartRender(streamId0); 304 if (error != 0) { 305 // TODO(phoglund): This test will not work if compiled in release mode. 306 // This rather silly construct here is to avoid compilation errors when 307 // compiling in release. Release => no asserts => unused 'error' variable. 308 assert(false); 309 } 310 311 // Loop through an I420 file and render each frame 312 const int width = 352; 313 const int half_width = (width + 1) / 2; 314 const int height = 288; 315 316 I420VideoFrame videoFrame0; 317 videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width); 318 319 const uint32_t renderDelayMs = 500; 320 321 for (int i=0; i<TEST_FRAME_NUM; i++) { 322 GetTestVideoFrame(&videoFrame0, TEST_STREAM0_START_COLOR); 323 // Render this frame with the specified delay 324 videoFrame0.set_render_time_ms(TickTime::MillisecondTimestamp() 325 + renderDelayMs); 326 renderCallback0->RenderFrame(streamId0, videoFrame0); 327 SleepMs(1000/TEST_FRAME_RATE); 328 } 329 330 331 // Shut down 332 printf("Closing...\n"); 333 error = renderModule->StopRender(streamId0); 334 assert(error == 0); 335 336 error = renderModule->DeleteIncomingRenderStream(streamId0); 337 assert(error == 0); 338 339 return 0; 340 } 341 342 int TestFullscreenStream(VideoRender* &renderModule, 343 void* window, 344 const VideoRenderType videoRenderType) { 345 VideoRender::DestroyVideoRender(renderModule); 346 renderModule = VideoRender::CreateVideoRender(12345, window, true, videoRenderType); 347 348 TestSingleStream(renderModule); 349 350 VideoRender::DestroyVideoRender(renderModule); 351 renderModule = VideoRender::CreateVideoRender(12345, window, false, videoRenderType); 352 353 return 0; 354 } 355 356 int TestBitmapText(VideoRender* renderModule) { 357 #if defined(WIN32) 358 359 int error = 0; 360 // Add settings for a stream to render 361 printf("Add stream 0 to entire window\n"); 362 const int streamId0 = 0; 363 VideoRenderCallback* renderCallback0 = renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f); 364 assert(renderCallback0 != NULL); 365 366 printf("Adding Bitmap\n"); 367 DDCOLORKEY ColorKey; // black 368 ColorKey.dwColorSpaceHighValue = RGB(0, 0, 0); 369 ColorKey.dwColorSpaceLowValue = RGB(0, 0, 0); 370 HBITMAP hbm = (HBITMAP)LoadImage(NULL, 371 (LPCTSTR)_T("renderStartImage.bmp"), 372 IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); 373 renderModule->SetBitmap(hbm, 0, &ColorKey, 0.0f, 0.0f, 0.3f, 374 0.3f); 375 376 printf("Adding Text\n"); 377 renderModule->SetText(1, (uint8_t*) "WebRtc Render Demo App", 20, 378 RGB(255, 0, 0), RGB(0, 0, 0), 0.25f, 0.1f, 1.0f, 379 1.0f); 380 381 printf("Start render\n"); 382 error = renderModule->StartRender(streamId0); 383 assert(error == 0); 384 385 // Loop through an I420 file and render each frame 386 const int width = 352; 387 const int half_width = (width + 1) / 2; 388 const int height = 288; 389 390 I420VideoFrame videoFrame0; 391 videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width); 392 393 const uint32_t renderDelayMs = 500; 394 395 for (int i=0; i<TEST_FRAME_NUM; i++) { 396 GetTestVideoFrame(&videoFrame0, TEST_STREAM0_START_COLOR); 397 // Render this frame with the specified delay 398 videoFrame0.set_render_time_ms(TickTime::MillisecondTimestamp() + 399 renderDelayMs); 400 renderCallback0->RenderFrame(streamId0, videoFrame0); 401 SleepMs(1000/TEST_FRAME_RATE); 402 } 403 // Sleep and let all frames be rendered before closing 404 SleepMs(renderDelayMs*2); 405 406 407 // Shut down 408 printf("Closing...\n"); 409 ColorKey.dwColorSpaceHighValue = RGB(0,0,0); 410 ColorKey.dwColorSpaceLowValue = RGB(0,0,0); 411 renderModule->SetBitmap(NULL, 0, &ColorKey, 0.0f, 0.0f, 0.0f, 0.0f); 412 renderModule->SetText(1, NULL, 20, RGB(255,255,255), 413 RGB(0,0,0), 0.0f, 0.0f, 0.0f, 0.0f); 414 415 error = renderModule->StopRender(streamId0); 416 assert(error == 0); 417 418 error = renderModule->DeleteIncomingRenderStream(streamId0); 419 assert(error == 0); 420 #endif 421 422 return 0; 423 } 424 425 int TestMultipleStreams(VideoRender* renderModule) { 426 int error = 0; 427 428 // Add settings for a stream to render 429 printf("Add stream 0\n"); 430 const int streamId0 = 0; 431 VideoRenderCallback* renderCallback0 = 432 renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 0.45f, 0.45f); 433 assert(renderCallback0 != NULL); 434 printf("Add stream 1\n"); 435 const int streamId1 = 1; 436 VideoRenderCallback* renderCallback1 = 437 renderModule->AddIncomingRenderStream(streamId1, 0, 0.55f, 0.0f, 1.0f, 0.45f); 438 assert(renderCallback1 != NULL); 439 printf("Add stream 2\n"); 440 const int streamId2 = 2; 441 VideoRenderCallback* renderCallback2 = 442 renderModule->AddIncomingRenderStream(streamId2, 0, 0.0f, 0.55f, 0.45f, 1.0f); 443 assert(renderCallback2 != NULL); 444 printf("Add stream 3\n"); 445 const int streamId3 = 3; 446 VideoRenderCallback* renderCallback3 = 447 renderModule->AddIncomingRenderStream(streamId3, 0, 0.55f, 0.55f, 1.0f, 1.0f); 448 assert(renderCallback3 != NULL); 449 error = renderModule->StartRender(streamId0); 450 if (error != 0) { 451 // TODO(phoglund): This test will not work if compiled in release mode. 452 // This rather silly construct here is to avoid compilation errors when 453 // compiling in release. Release => no asserts => unused 'error' variable. 454 assert(false); 455 } 456 error = renderModule->StartRender(streamId1); 457 assert(error == 0); 458 error = renderModule->StartRender(streamId2); 459 assert(error == 0); 460 error = renderModule->StartRender(streamId3); 461 assert(error == 0); 462 463 // Loop through an I420 file and render each frame 464 const int width = 352; 465 const int half_width = (width + 1) / 2; 466 const int height = 288; 467 468 I420VideoFrame videoFrame0; 469 videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width); 470 I420VideoFrame videoFrame1; 471 videoFrame1.CreateEmptyFrame(width, height, width, half_width, half_width); 472 I420VideoFrame videoFrame2; 473 videoFrame2.CreateEmptyFrame(width, height, width, half_width, half_width); 474 I420VideoFrame videoFrame3; 475 videoFrame3.CreateEmptyFrame(width, height, width, half_width, half_width); 476 477 const uint32_t renderDelayMs = 500; 478 479 // Render frames with the specified delay. 480 for (int i=0; i<TEST_FRAME_NUM; i++) { 481 GetTestVideoFrame(&videoFrame0, TEST_STREAM0_START_COLOR); 482 483 videoFrame0.set_render_time_ms(TickTime::MillisecondTimestamp() + 484 renderDelayMs); 485 renderCallback0->RenderFrame(streamId0, videoFrame0); 486 487 GetTestVideoFrame(&videoFrame1, TEST_STREAM1_START_COLOR); 488 videoFrame1.set_render_time_ms(TickTime::MillisecondTimestamp() + 489 renderDelayMs); 490 renderCallback1->RenderFrame(streamId1, videoFrame1); 491 492 GetTestVideoFrame(&videoFrame2, TEST_STREAM2_START_COLOR); 493 videoFrame2.set_render_time_ms(TickTime::MillisecondTimestamp() + 494 renderDelayMs); 495 renderCallback2->RenderFrame(streamId2, videoFrame2); 496 497 GetTestVideoFrame(&videoFrame3, TEST_STREAM3_START_COLOR); 498 videoFrame3.set_render_time_ms(TickTime::MillisecondTimestamp() + 499 renderDelayMs); 500 renderCallback3->RenderFrame(streamId3, videoFrame3); 501 502 SleepMs(1000/TEST_FRAME_RATE); 503 } 504 505 // Shut down 506 printf("Closing...\n"); 507 error = renderModule->StopRender(streamId0); 508 assert(error == 0); 509 error = renderModule->DeleteIncomingRenderStream(streamId0); 510 assert(error == 0); 511 error = renderModule->StopRender(streamId1); 512 assert(error == 0); 513 error = renderModule->DeleteIncomingRenderStream(streamId1); 514 assert(error == 0); 515 error = renderModule->StopRender(streamId2); 516 assert(error == 0); 517 error = renderModule->DeleteIncomingRenderStream(streamId2); 518 assert(error == 0); 519 error = renderModule->StopRender(streamId3); 520 assert(error == 0); 521 error = renderModule->DeleteIncomingRenderStream(streamId3); 522 assert(error == 0); 523 524 return 0; 525 } 526 527 int TestExternalRender(VideoRender* renderModule) { 528 int error = 0; 529 MyRenderCallback *externalRender = new MyRenderCallback(); 530 531 const int streamId0 = 0; 532 VideoRenderCallback* renderCallback0 = 533 renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 534 1.0f, 1.0f); 535 assert(renderCallback0 != NULL); 536 error = renderModule->AddExternalRenderCallback(streamId0, externalRender); 537 if (error != 0) { 538 // TODO(phoglund): This test will not work if compiled in release mode. 539 // This rather silly construct here is to avoid compilation errors when 540 // compiling in release. Release => no asserts => unused 'error' variable. 541 assert(false); 542 } 543 544 error = renderModule->StartRender(streamId0); 545 assert(error == 0); 546 547 const int width = 352; 548 const int half_width = (width + 1) / 2; 549 const int height = 288; 550 I420VideoFrame videoFrame0; 551 videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width); 552 553 const uint32_t renderDelayMs = 500; 554 int frameCount = TEST_FRAME_NUM; 555 for (int i=0; i<frameCount; i++) { 556 videoFrame0.set_render_time_ms(TickTime::MillisecondTimestamp() + 557 renderDelayMs); 558 renderCallback0->RenderFrame(streamId0, videoFrame0); 559 SleepMs(33); 560 } 561 562 // Sleep and let all frames be rendered before closing 563 SleepMs(2*renderDelayMs); 564 565 // Shut down 566 printf("Closing...\n"); 567 error = renderModule->StopRender(streamId0); 568 assert(error == 0); 569 error = renderModule->DeleteIncomingRenderStream(streamId0); 570 assert(error == 0); 571 assert(frameCount == externalRender->_cnt); 572 573 delete externalRender; 574 externalRender = NULL; 575 576 return 0; 577 } 578 579 void RunVideoRenderTests(void* window, VideoRenderType windowType) { 580 #ifndef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER 581 windowType = kRenderExternal; 582 #endif 583 584 int myId = 12345; 585 586 // Create the render module 587 printf("Create render module\n"); 588 VideoRender* renderModule = NULL; 589 renderModule = VideoRender::CreateVideoRender(myId, 590 window, 591 false, 592 windowType); 593 assert(renderModule != NULL); 594 595 // ##### Test single stream rendering #### 596 printf("#### TestSingleStream ####\n"); 597 if (TestSingleStream(renderModule) != 0) { 598 printf ("TestSingleStream failed\n"); 599 } 600 601 // ##### Test fullscreen rendering #### 602 printf("#### TestFullscreenStream ####\n"); 603 if (TestFullscreenStream(renderModule, window, windowType) != 0) { 604 printf ("TestFullscreenStream failed\n"); 605 } 606 607 // ##### Test bitmap and text #### 608 printf("#### TestBitmapText ####\n"); 609 if (TestBitmapText(renderModule) != 0) { 610 printf ("TestBitmapText failed\n"); 611 } 612 613 // ##### Test multiple streams #### 614 printf("#### TestMultipleStreams ####\n"); 615 if (TestMultipleStreams(renderModule) != 0) { 616 printf ("TestMultipleStreams failed\n"); 617 } 618 619 // ##### Test multiple streams #### 620 printf("#### TestExternalRender ####\n"); 621 if (TestExternalRender(renderModule) != 0) { 622 printf ("TestExternalRender failed\n"); 623 } 624 625 delete renderModule; 626 renderModule = NULL; 627 628 printf("VideoRender unit tests passed.\n"); 629 } 630 631 // Note: The Mac main is implemented in testApi_mac.mm. 632 #if defined(_WIN32) 633 int _tmain(int argc, _TCHAR* argv[]) 634 #elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) 635 int main(int argc, char* argv[]) 636 #endif 637 #if !defined(WEBRTC_MAC) && !defined(WEBRTC_ANDROID) 638 { 639 // Create a window for testing. 640 void* window = NULL; 641 #if defined (_WIN32) 642 HWND testHwnd; 643 WebRtcCreateWindow(testHwnd, 0, 352, 288); 644 window = (void*)testHwnd; 645 VideoRenderType windowType = kRenderWindows; 646 #elif defined(WEBRTC_LINUX) 647 Window testWindow; 648 Display* display; 649 WebRtcCreateWindow(&testWindow, &display, 0, 352, 288); 650 VideoRenderType windowType = kRenderX11; 651 window = (void*)testWindow; 652 #endif // WEBRTC_LINUX 653 654 RunVideoRenderTests(window, windowType); 655 return 0; 656 } 657 #endif // !WEBRTC_MAC 658