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