2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
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.
11 #include "webrtc/modules/video_render/test/testAPI/testAPI.h"
25 #elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
28 #include <X11/Xutil.h>
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"
43 using namespace webrtc;
45 void GetTestVideoFrame(I420VideoFrame* frame,
47 int TestSingleStream(VideoRender* renderModule);
48 int TestFullscreenStream(VideoRender* &renderModule,
50 const VideoRenderType videoRenderType);
51 int TestBitmapText(VideoRender* renderModule);
52 int TestMultipleStreams(VideoRender* renderModule);
53 int TestExternalRender(VideoRender* renderModule);
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
63 #if defined(WEBRTC_LINUX)
65 #define GET_TIME_IN_MS timeGetTime()
67 unsigned long timeGetTime()
73 gettimeofday(&tv, &tz);
74 val= tv.tv_sec*1000+ tv.tv_usec/1000;
78 #elif defined(WEBRTC_MAC)
82 #define GET_TIME_IN_MS timeGetTime()
84 unsigned long timeGetTime()
91 #define GET_TIME_IN_MS ::timeGetTime()
98 LRESULT CALLBACK WebRtcWinProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
107 return DefWindowProc(hWnd,uMsg,wParam,lParam);
110 int WebRtcCreateWindow(HWND &hwndMain,int winNum, int width, int height)
112 HINSTANCE hinst = GetModuleHandle(0);
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);
125 wcx.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
127 // Register our window class with the operating system.
128 // If there is an error, exit program.
129 if ( !RegisterClassEx (&wcx) )
131 MessageBox( 0, TEXT("Failed to register window class!"),TEXT("Error!"), MB_OK|MB_ICONERROR );
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
145 (HWND) NULL, // no parent or owner window
146 (HMENU) NULL, // class menu used
147 hinst, // instance handle
148 NULL); // no window creation data
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.
157 ShowWindow(hwndMain, SW_SHOWDEFAULT);
158 UpdateWindow(hwndMain);
162 #elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
164 int WebRtcCreateWindow(Window *outWindow, Display **outDisplay, int winNum, int width, int height) // unsigned char* title, int titleLength)
167 int screen, xpos = 10, ypos = 10;
169 XSetWindowAttributes xswa; // window attribute struct
170 XVisualInfo vinfo; // screen visual info struct
171 unsigned long mask; // attribute mask
173 // get connection handle to xserver
174 Display* _display = XOpenDisplay( NULL );
177 screen = DefaultScreen(_display);
179 // put desired visual info for the screen in vinfo
180 if( XMatchVisualInfo(_display, screen, 24, TrueColor, &vinfo) != 0 )
182 //printf( "Screen visual info match!\n" );
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;
191 // value mask for attributes
192 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
208 // create a subwindow for parent (defroot)
209 Window _window = XCreateWindow(_display, DefaultRootWindow(_display),
221 XStoreName(_display, _window, "VE MM Local Window");
222 XSetIconName(_display, _window, "VE MM Local Window");
224 else if( winNum == 1 )
226 XStoreName(_display, _window, "VE MM Remote Window");
227 XSetIconName(_display, _window, "VE MM Remote Window");
230 // make x report events for mask
231 XSelectInput(_display, _window, StructureNotifyMask);
233 // map the window to the display
234 XMapWindow(_display, _window);
236 // wait for map event
239 XNextEvent(_display, &evnt);
241 while (evnt.type != MapNotify || evnt.xmap.event != _window);
243 *outWindow = _window;
244 *outDisplay = _display;
250 // Note: Mac code is in testApi_mac.mm.
252 class MyRenderCallback: public VideoRenderCallback
264 virtual int32_t RenderFrame(const uint32_t streamId,
265 I420VideoFrame& videoFrame)
270 printf("Render callback %d \n",_cnt);
277 void GetTestVideoFrame(I420VideoFrame* frame,
278 uint8_t startColor) {
280 static uint8_t color = startColor;
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));
289 int TestSingleStream(VideoRender* renderModule) {
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);
297 printf("Start render\n");
298 error = renderModule->StartRender(streamId0);
300 // TODO(phoglund): This test will not work if compiled in release mode.
301 // This rather silly construct here is to avoid compilation errors when
302 // compiling in release. Release => no asserts => unused 'error' variable.
306 // Loop through an I420 file and render each frame
307 const int width = 352;
308 const int half_width = (width + 1) / 2;
309 const int height = 288;
311 I420VideoFrame videoFrame0;
312 videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width);
314 const uint32_t renderDelayMs = 500;
316 for (int i=0; i<TEST_FRAME_NUM; i++) {
317 GetTestVideoFrame(&videoFrame0, TEST_STREAM0_START_COLOR);
318 // Render this frame with the specified delay
319 videoFrame0.set_render_time_ms(TickTime::MillisecondTimestamp()
321 renderCallback0->RenderFrame(streamId0, videoFrame0);
322 SleepMs(1000/TEST_FRAME_RATE);
327 printf("Closing...\n");
328 error = renderModule->StopRender(streamId0);
331 error = renderModule->DeleteIncomingRenderStream(streamId0);
337 int TestFullscreenStream(VideoRender* &renderModule,
339 const VideoRenderType videoRenderType) {
340 VideoRender::DestroyVideoRender(renderModule);
341 renderModule = VideoRender::CreateVideoRender(12345, window, true, videoRenderType);
343 TestSingleStream(renderModule);
345 VideoRender::DestroyVideoRender(renderModule);
346 renderModule = VideoRender::CreateVideoRender(12345, window, false, videoRenderType);
351 int TestBitmapText(VideoRender* renderModule) {
355 // Add settings for a stream to render
356 printf("Add stream 0 to entire window\n");
357 const int streamId0 = 0;
358 VideoRenderCallback* renderCallback0 = renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f);
359 assert(renderCallback0 != NULL);
361 printf("Adding Bitmap\n");
362 DDCOLORKEY ColorKey; // black
363 ColorKey.dwColorSpaceHighValue = RGB(0, 0, 0);
364 ColorKey.dwColorSpaceLowValue = RGB(0, 0, 0);
365 HBITMAP hbm = (HBITMAP)LoadImage(NULL,
366 (LPCTSTR)_T("renderStartImage.bmp"),
367 IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
368 renderModule->SetBitmap(hbm, 0, &ColorKey, 0.0f, 0.0f, 0.3f,
371 printf("Adding Text\n");
372 renderModule->SetText(1, (uint8_t*) "WebRtc Render Demo App", 20,
373 RGB(255, 0, 0), RGB(0, 0, 0), 0.25f, 0.1f, 1.0f,
376 printf("Start render\n");
377 error = renderModule->StartRender(streamId0);
380 // Loop through an I420 file and render each frame
381 const int width = 352;
382 const int half_width = (width + 1) / 2;
383 const int height = 288;
385 I420VideoFrame videoFrame0;
386 videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width);
388 const uint32_t renderDelayMs = 500;
390 for (int i=0; i<TEST_FRAME_NUM; i++) {
391 GetTestVideoFrame(&videoFrame0, TEST_STREAM0_START_COLOR);
392 // Render this frame with the specified delay
393 videoFrame0.set_render_time_ms(TickTime::MillisecondTimestamp() +
395 renderCallback0->RenderFrame(streamId0, videoFrame0);
396 SleepMs(1000/TEST_FRAME_RATE);
398 // Sleep and let all frames be rendered before closing
399 SleepMs(renderDelayMs*2);
403 printf("Closing...\n");
404 ColorKey.dwColorSpaceHighValue = RGB(0,0,0);
405 ColorKey.dwColorSpaceLowValue = RGB(0,0,0);
406 renderModule->SetBitmap(NULL, 0, &ColorKey, 0.0f, 0.0f, 0.0f, 0.0f);
407 renderModule->SetText(1, NULL, 20, RGB(255,255,255),
408 RGB(0,0,0), 0.0f, 0.0f, 0.0f, 0.0f);
410 error = renderModule->StopRender(streamId0);
413 error = renderModule->DeleteIncomingRenderStream(streamId0);
420 int TestMultipleStreams(VideoRender* renderModule) {
423 // Add settings for a stream to render
424 printf("Add stream 0\n");
425 const int streamId0 = 0;
426 VideoRenderCallback* renderCallback0 =
427 renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 0.45f, 0.45f);
428 assert(renderCallback0 != NULL);
429 printf("Add stream 1\n");
430 const int streamId1 = 1;
431 VideoRenderCallback* renderCallback1 =
432 renderModule->AddIncomingRenderStream(streamId1, 0, 0.55f, 0.0f, 1.0f, 0.45f);
433 assert(renderCallback1 != NULL);
434 printf("Add stream 2\n");
435 const int streamId2 = 2;
436 VideoRenderCallback* renderCallback2 =
437 renderModule->AddIncomingRenderStream(streamId2, 0, 0.0f, 0.55f, 0.45f, 1.0f);
438 assert(renderCallback2 != NULL);
439 printf("Add stream 3\n");
440 const int streamId3 = 3;
441 VideoRenderCallback* renderCallback3 =
442 renderModule->AddIncomingRenderStream(streamId3, 0, 0.55f, 0.55f, 1.0f, 1.0f);
443 assert(renderCallback3 != NULL);
444 error = renderModule->StartRender(streamId0);
446 // TODO(phoglund): This test will not work if compiled in release mode.
447 // This rather silly construct here is to avoid compilation errors when
448 // compiling in release. Release => no asserts => unused 'error' variable.
451 error = renderModule->StartRender(streamId1);
453 error = renderModule->StartRender(streamId2);
455 error = renderModule->StartRender(streamId3);
458 // Loop through an I420 file and render each frame
459 const int width = 352;
460 const int half_width = (width + 1) / 2;
461 const int height = 288;
463 I420VideoFrame videoFrame0;
464 videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width);
465 I420VideoFrame videoFrame1;
466 videoFrame1.CreateEmptyFrame(width, height, width, half_width, half_width);
467 I420VideoFrame videoFrame2;
468 videoFrame2.CreateEmptyFrame(width, height, width, half_width, half_width);
469 I420VideoFrame videoFrame3;
470 videoFrame3.CreateEmptyFrame(width, height, width, half_width, half_width);
472 const uint32_t renderDelayMs = 500;
474 // Render frames with the specified delay.
475 for (int i=0; i<TEST_FRAME_NUM; i++) {
476 GetTestVideoFrame(&videoFrame0, TEST_STREAM0_START_COLOR);
478 videoFrame0.set_render_time_ms(TickTime::MillisecondTimestamp() +
480 renderCallback0->RenderFrame(streamId0, videoFrame0);
482 GetTestVideoFrame(&videoFrame1, TEST_STREAM1_START_COLOR);
483 videoFrame1.set_render_time_ms(TickTime::MillisecondTimestamp() +
485 renderCallback1->RenderFrame(streamId1, videoFrame1);
487 GetTestVideoFrame(&videoFrame2, TEST_STREAM2_START_COLOR);
488 videoFrame2.set_render_time_ms(TickTime::MillisecondTimestamp() +
490 renderCallback2->RenderFrame(streamId2, videoFrame2);
492 GetTestVideoFrame(&videoFrame3, TEST_STREAM3_START_COLOR);
493 videoFrame3.set_render_time_ms(TickTime::MillisecondTimestamp() +
495 renderCallback3->RenderFrame(streamId3, videoFrame3);
497 SleepMs(1000/TEST_FRAME_RATE);
501 printf("Closing...\n");
502 error = renderModule->StopRender(streamId0);
504 error = renderModule->DeleteIncomingRenderStream(streamId0);
506 error = renderModule->StopRender(streamId1);
508 error = renderModule->DeleteIncomingRenderStream(streamId1);
510 error = renderModule->StopRender(streamId2);
512 error = renderModule->DeleteIncomingRenderStream(streamId2);
514 error = renderModule->StopRender(streamId3);
516 error = renderModule->DeleteIncomingRenderStream(streamId3);
522 int TestExternalRender(VideoRender* renderModule) {
524 MyRenderCallback *externalRender = new MyRenderCallback();
526 const int streamId0 = 0;
527 VideoRenderCallback* renderCallback0 =
528 renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f,
530 assert(renderCallback0 != NULL);
531 error = renderModule->AddExternalRenderCallback(streamId0, externalRender);
533 // TODO(phoglund): This test will not work if compiled in release mode.
534 // This rather silly construct here is to avoid compilation errors when
535 // compiling in release. Release => no asserts => unused 'error' variable.
539 error = renderModule->StartRender(streamId0);
542 const int width = 352;
543 const int half_width = (width + 1) / 2;
544 const int height = 288;
545 I420VideoFrame videoFrame0;
546 videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width);
548 const uint32_t renderDelayMs = 500;
549 int frameCount = TEST_FRAME_NUM;
550 for (int i=0; i<frameCount; i++) {
551 videoFrame0.set_render_time_ms(TickTime::MillisecondTimestamp() +
553 renderCallback0->RenderFrame(streamId0, videoFrame0);
557 // Sleep and let all frames be rendered before closing
558 SleepMs(2*renderDelayMs);
561 printf("Closing...\n");
562 error = renderModule->StopRender(streamId0);
564 error = renderModule->DeleteIncomingRenderStream(streamId0);
566 assert(frameCount == externalRender->_cnt);
568 delete externalRender;
569 externalRender = NULL;
574 void RunVideoRenderTests(void* window, VideoRenderType windowType) {
577 // Create the render module
578 printf("Create render module\n");
579 VideoRender* renderModule = NULL;
580 renderModule = VideoRender::CreateVideoRender(myId,
584 assert(renderModule != NULL);
586 // ##### Test single stream rendering ####
587 printf("#### TestSingleStream ####\n");
588 if (TestSingleStream(renderModule) != 0) {
589 printf ("TestSingleStream failed\n");
592 // ##### Test fullscreen rendering ####
593 printf("#### TestFullscreenStream ####\n");
594 if (TestFullscreenStream(renderModule, window, windowType) != 0) {
595 printf ("TestFullscreenStream failed\n");
598 // ##### Test bitmap and text ####
599 printf("#### TestBitmapText ####\n");
600 if (TestBitmapText(renderModule) != 0) {
601 printf ("TestBitmapText failed\n");
604 // ##### Test multiple streams ####
605 printf("#### TestMultipleStreams ####\n");
606 if (TestMultipleStreams(renderModule) != 0) {
607 printf ("TestMultipleStreams failed\n");
610 // ##### Test multiple streams ####
611 printf("#### TestExternalRender ####\n");
612 if (TestExternalRender(renderModule) != 0) {
613 printf ("TestExternalRender failed\n");
619 printf("VideoRender unit tests passed.\n");
622 // Note: The Mac main is implemented in testApi_mac.mm.
624 int _tmain(int argc, _TCHAR* argv[])
625 #elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
626 int main(int argc, char* argv[])
628 #if !defined(WEBRTC_MAC) && !defined(WEBRTC_ANDROID)
630 // Create a window for testing.
634 WebRtcCreateWindow(testHwnd, 0, 352, 288);
635 window = (void*)testHwnd;
636 VideoRenderType windowType = kRenderWindows;
637 #elif defined(WEBRTC_LINUX)
640 WebRtcCreateWindow(&testWindow, &display, 0, 352, 288);
641 VideoRenderType windowType = kRenderX11;
642 window = (void*)testWindow;
643 #endif // WEBRTC_LINUX
645 RunVideoRenderTests(window, windowType);
648 #endif // !WEBRTC_MAC