SurfaceMediaSource_test.cpp revision abf0610a8cea021548f2909e1d47d656206f641c
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17// #define LOG_NDEBUG 0 18#define LOG_TAG "SurfaceMediaSource_test" 19 20#include <gtest/gtest.h> 21#include <utils/String8.h> 22#include <utils/Errors.h> 23#include <fcntl.h> 24#include <unistd.h> 25 26#include <media/stagefright/SurfaceMediaSource.h> 27#include <media/mediarecorder.h> 28 29#include <ui/GraphicBuffer.h> 30#include <gui/SurfaceTextureClient.h> 31#include <gui/ISurfaceComposer.h> 32#include <gui/Surface.h> 33#include <gui/SurfaceComposerClient.h> 34 35#include <binder/ProcessState.h> 36#include <ui/FramebufferNativeWindow.h> 37 38#include <media/stagefright/foundation/ADebug.h> 39#include <media/stagefright/MediaBufferGroup.h> 40#include <media/stagefright/MediaDefs.h> 41#include <media/stagefright/MetaData.h> 42#include <media/stagefright/OMXClient.h> 43#include <media/stagefright/OMXCodec.h> 44#include <OMX_Component.h> 45 46#include "DummyRecorder.h" 47 48 49namespace android { 50 51class GLTest : public ::testing::Test { 52protected: 53 54 GLTest(): 55 mEglDisplay(EGL_NO_DISPLAY), 56 mEglSurface(EGL_NO_SURFACE), 57 mEglContext(EGL_NO_CONTEXT) { 58 } 59 60 virtual void SetUp() { 61 ALOGV("GLTest::SetUp()"); 62 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 63 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 64 ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); 65 66 EGLint majorVersion; 67 EGLint minorVersion; 68 EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); 69 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 70 RecordProperty("EglVersionMajor", majorVersion); 71 RecordProperty("EglVersionMajor", minorVersion); 72 73 EGLint numConfigs = 0; 74 EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, 75 1, &numConfigs)); 76 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 77 78 char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); 79 if (displaySecsEnv != NULL) { 80 mDisplaySecs = atoi(displaySecsEnv); 81 if (mDisplaySecs < 0) { 82 mDisplaySecs = 0; 83 } 84 } else { 85 mDisplaySecs = 0; 86 } 87 88 if (mDisplaySecs > 0) { 89 mComposerClient = new SurfaceComposerClient; 90 ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); 91 92 mSurfaceControl = mComposerClient->createSurface( 93 String8("Test Surface"), 0, 94 getSurfaceWidth(), getSurfaceHeight(), 95 PIXEL_FORMAT_RGB_888, 0); 96 97 ASSERT_TRUE(mSurfaceControl != NULL); 98 ASSERT_TRUE(mSurfaceControl->isValid()); 99 100 SurfaceComposerClient::openGlobalTransaction(); 101 ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); 102 ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); 103 SurfaceComposerClient::closeGlobalTransaction(); 104 105 sp<ANativeWindow> window = mSurfaceControl->getSurface(); 106 mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, 107 window.get(), NULL); 108 } else { 109 ALOGV("No actual display. Choosing EGLSurface based on SurfaceMediaSource"); 110 sp<ISurfaceTexture> sms = new SurfaceMediaSource( 111 getSurfaceWidth(), getSurfaceHeight()); 112 sp<SurfaceTextureClient> stc = new SurfaceTextureClient(sms); 113 sp<ANativeWindow> window = stc; 114 115 mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, 116 window.get(), NULL); 117 } 118 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 119 ASSERT_NE(EGL_NO_SURFACE, mEglSurface); 120 121 mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, 122 getContextAttribs()); 123 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 124 ASSERT_NE(EGL_NO_CONTEXT, mEglContext); 125 126 EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, 127 mEglContext)); 128 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 129 130 EGLint w, h; 131 EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w)); 132 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 133 EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h)); 134 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 135 RecordProperty("EglSurfaceWidth", w); 136 RecordProperty("EglSurfaceHeight", h); 137 138 glViewport(0, 0, w, h); 139 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 140 } 141 142 virtual void TearDown() { 143 // Display the result 144 if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) { 145 eglSwapBuffers(mEglDisplay, mEglSurface); 146 sleep(mDisplaySecs); 147 } 148 149 if (mComposerClient != NULL) { 150 mComposerClient->dispose(); 151 } 152 if (mEglContext != EGL_NO_CONTEXT) { 153 eglDestroyContext(mEglDisplay, mEglContext); 154 } 155 if (mEglSurface != EGL_NO_SURFACE) { 156 eglDestroySurface(mEglDisplay, mEglSurface); 157 } 158 if (mEglDisplay != EGL_NO_DISPLAY) { 159 eglTerminate(mEglDisplay); 160 } 161 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 162 } 163 164 virtual EGLint const* getConfigAttribs() { 165 ALOGV("GLTest getConfigAttribs"); 166 static EGLint sDefaultConfigAttribs[] = { 167 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, 168 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 169 EGL_RED_SIZE, 8, 170 EGL_GREEN_SIZE, 8, 171 EGL_BLUE_SIZE, 8, 172 EGL_ALPHA_SIZE, 8, 173 EGL_DEPTH_SIZE, 16, 174 EGL_STENCIL_SIZE, 8, 175 EGL_NONE }; 176 177 return sDefaultConfigAttribs; 178 } 179 180 virtual EGLint const* getContextAttribs() { 181 static EGLint sDefaultContextAttribs[] = { 182 EGL_CONTEXT_CLIENT_VERSION, 2, 183 EGL_NONE }; 184 185 return sDefaultContextAttribs; 186 } 187 188 virtual EGLint getSurfaceWidth() { 189 return 512; 190 } 191 192 virtual EGLint getSurfaceHeight() { 193 return 512; 194 } 195 196 void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) { 197 GLuint shader = glCreateShader(shaderType); 198 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 199 if (shader) { 200 glShaderSource(shader, 1, &pSource, NULL); 201 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 202 glCompileShader(shader); 203 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 204 GLint compiled = 0; 205 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 206 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 207 if (!compiled) { 208 GLint infoLen = 0; 209 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 210 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 211 if (infoLen) { 212 char* buf = (char*) malloc(infoLen); 213 if (buf) { 214 glGetShaderInfoLog(shader, infoLen, NULL, buf); 215 printf("Shader compile log:\n%s\n", buf); 216 free(buf); 217 FAIL(); 218 } 219 } else { 220 char* buf = (char*) malloc(0x1000); 221 if (buf) { 222 glGetShaderInfoLog(shader, 0x1000, NULL, buf); 223 printf("Shader compile log:\n%s\n", buf); 224 free(buf); 225 FAIL(); 226 } 227 } 228 glDeleteShader(shader); 229 shader = 0; 230 } 231 } 232 ASSERT_TRUE(shader != 0); 233 *outShader = shader; 234 } 235 236 void createProgram(const char* pVertexSource, const char* pFragmentSource, 237 GLuint* outPgm) { 238 GLuint vertexShader, fragmentShader; 239 { 240 SCOPED_TRACE("compiling vertex shader"); 241 loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader); 242 if (HasFatalFailure()) { 243 return; 244 } 245 } 246 { 247 SCOPED_TRACE("compiling fragment shader"); 248 loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader); 249 if (HasFatalFailure()) { 250 return; 251 } 252 } 253 254 GLuint program = glCreateProgram(); 255 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 256 if (program) { 257 glAttachShader(program, vertexShader); 258 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 259 glAttachShader(program, fragmentShader); 260 ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 261 glLinkProgram(program); 262 GLint linkStatus = GL_FALSE; 263 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); 264 if (linkStatus != GL_TRUE) { 265 GLint bufLength = 0; 266 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); 267 if (bufLength) { 268 char* buf = (char*) malloc(bufLength); 269 if (buf) { 270 glGetProgramInfoLog(program, bufLength, NULL, buf); 271 printf("Program link log:\n%s\n", buf); 272 free(buf); 273 FAIL(); 274 } 275 } 276 glDeleteProgram(program); 277 program = 0; 278 } 279 } 280 glDeleteShader(vertexShader); 281 glDeleteShader(fragmentShader); 282 ASSERT_TRUE(program != 0); 283 *outPgm = program; 284 } 285 286 static int abs(int value) { 287 return value > 0 ? value : -value; 288 } 289 290 ::testing::AssertionResult checkPixel(int x, int y, int r, 291 int g, int b, int a, int tolerance=2) { 292 GLubyte pixel[4]; 293 String8 msg; 294 glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); 295 GLenum err = glGetError(); 296 if (err != GL_NO_ERROR) { 297 msg += String8::format("error reading pixel: %#x", err); 298 while ((err = glGetError()) != GL_NO_ERROR) { 299 msg += String8::format(", %#x", err); 300 } 301 fprintf(stderr, "pixel check failure: %s\n", msg.string()); 302 return ::testing::AssertionFailure( 303 ::testing::Message(msg.string())); 304 } 305 if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { 306 msg += String8::format("r(%d isn't %d)", pixel[0], r); 307 } 308 if (g >= 0 && abs(g - int(pixel[1])) > tolerance) { 309 if (!msg.isEmpty()) { 310 msg += " "; 311 } 312 msg += String8::format("g(%d isn't %d)", pixel[1], g); 313 } 314 if (b >= 0 && abs(b - int(pixel[2])) > tolerance) { 315 if (!msg.isEmpty()) { 316 msg += " "; 317 } 318 msg += String8::format("b(%d isn't %d)", pixel[2], b); 319 } 320 if (a >= 0 && abs(a - int(pixel[3])) > tolerance) { 321 if (!msg.isEmpty()) { 322 msg += " "; 323 } 324 msg += String8::format("a(%d isn't %d)", pixel[3], a); 325 } 326 if (!msg.isEmpty()) { 327 fprintf(stderr, "pixel check failure: %s\n", msg.string()); 328 return ::testing::AssertionFailure( 329 ::testing::Message(msg.string())); 330 } else { 331 return ::testing::AssertionSuccess(); 332 } 333 } 334 335 int mDisplaySecs; 336 sp<SurfaceComposerClient> mComposerClient; 337 sp<SurfaceControl> mSurfaceControl; 338 339 EGLDisplay mEglDisplay; 340 EGLSurface mEglSurface; 341 EGLContext mEglContext; 342 EGLConfig mGlConfig; 343}; 344 345/////////////////////////////////////////////////////////////////////// 346// Class for the NON-GL tests 347/////////////////////////////////////////////////////////////////////// 348class SurfaceMediaSourceTest : public ::testing::Test { 349public: 350 351 SurfaceMediaSourceTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { } 352 void oneBufferPass(int width, int height ); 353 void oneBufferPassNoFill(int width, int height ); 354 static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ; 355 static void fillYV12BufferRect(uint8_t* buf, int w, int h, 356 int stride, const android_native_rect_t& rect) ; 357protected: 358 359 virtual void SetUp() { 360 android::ProcessState::self()->startThreadPool(); 361 mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); 362 mSMS->setSynchronousMode(true); 363 // Manual cast is required to avoid constructor ambiguity 364 mSTC = new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >( mSMS)); 365 mANW = mSTC; 366 } 367 368 virtual void TearDown() { 369 mSMS.clear(); 370 mSTC.clear(); 371 mANW.clear(); 372 } 373 374 const int mYuvTexWidth; 375 const int mYuvTexHeight; 376 377 sp<SurfaceMediaSource> mSMS; 378 sp<SurfaceTextureClient> mSTC; 379 sp<ANativeWindow> mANW; 380}; 381 382/////////////////////////////////////////////////////////////////////// 383// Class for the GL tests 384/////////////////////////////////////////////////////////////////////// 385class SurfaceMediaSourceGLTest : public GLTest { 386public: 387 388 SurfaceMediaSourceGLTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { } 389 virtual EGLint const* getConfigAttribs(); 390 void oneBufferPassGL(int num = 0); 391 static sp<MediaRecorder> setUpMediaRecorder(int fileDescriptor, int videoSource, 392 int outputFormat, int videoEncoder, int width, int height, int fps); 393protected: 394 395 virtual void SetUp() { 396 ALOGV("SMS-GLTest::SetUp()"); 397 android::ProcessState::self()->startThreadPool(); 398 mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); 399 mSTC = new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >( mSMS)); 400 mANW = mSTC; 401 402 // Doing the setup related to the GL Side 403 GLTest::SetUp(); 404 } 405 406 virtual void TearDown() { 407 mSMS.clear(); 408 mSTC.clear(); 409 mANW.clear(); 410 GLTest::TearDown(); 411 } 412 413 void setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr); 414 415 const int mYuvTexWidth; 416 const int mYuvTexHeight; 417 418 sp<SurfaceMediaSource> mSMS; 419 sp<SurfaceTextureClient> mSTC; 420 sp<ANativeWindow> mANW; 421}; 422 423///////////////////////////////////////////////////////////////////// 424// Methods in SurfaceMediaSourceGLTest 425///////////////////////////////////////////////////////////////////// 426EGLint const* SurfaceMediaSourceGLTest::getConfigAttribs() { 427 ALOGV("SurfaceMediaSourceGLTest getConfigAttribs"); 428 static EGLint sDefaultConfigAttribs[] = { 429 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 430 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 431 EGL_RED_SIZE, 8, 432 EGL_GREEN_SIZE, 8, 433 EGL_BLUE_SIZE, 8, 434 EGL_RECORDABLE_ANDROID, EGL_TRUE, 435 EGL_NONE }; 436 437 return sDefaultConfigAttribs; 438} 439 440// One pass of dequeuing and queuing a GLBuffer 441void SurfaceMediaSourceGLTest::oneBufferPassGL(int num) { 442 int d = num % 50; 443 float f = 0.2f; // 0.1f * d; 444 445 glClearColor(0, 0.3, 0, 0.6); 446 glClear(GL_COLOR_BUFFER_BIT); 447 448 glEnable(GL_SCISSOR_TEST); 449 glScissor(4 + d, 4 + d, 4, 4); 450 glClearColor(1.0 - f, f, f, 1.0); 451 glClear(GL_COLOR_BUFFER_BIT); 452 453 glScissor(24 + d, 48 + d, 4, 4); 454 glClearColor(f, 1.0 - f, f, 1.0); 455 glClear(GL_COLOR_BUFFER_BIT); 456 457 glScissor(37 + d, 17 + d, 4, 4); 458 glClearColor(f, f, 1.0 - f, 1.0); 459 glClear(GL_COLOR_BUFFER_BIT); 460 461 // The following call dequeues and queues the buffer 462 eglSwapBuffers(mEglDisplay, mEglSurface); 463 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 464 glDisable(GL_SCISSOR_TEST); 465} 466 467// Set up the MediaRecorder which runs in the same process as mediaserver 468sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int videoSource, 469 int outputFormat, int videoEncoder, int width, int height, int fps) { 470 sp<MediaRecorder> mr = new MediaRecorder(); 471 mr->setVideoSource(videoSource); 472 mr->setOutputFormat(outputFormat); 473 mr->setVideoEncoder(videoEncoder); 474 mr->setOutputFile(fd, 0, 0); 475 mr->setVideoSize(width, height); 476 mr->setVideoFrameRate(fps); 477 mr->prepare(); 478 ALOGV("Starting MediaRecorder..."); 479 CHECK_EQ((status_t)OK, mr->start()); 480 return mr; 481} 482 483// query the mediarecorder for a surfacemeidasource and create an egl surface with that 484void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr) { 485 sp<ISurfaceTexture> iST = mr->querySurfaceMediaSourceFromMediaServer(); 486 mSTC = new SurfaceTextureClient(iST); 487 mANW = mSTC; 488 489 mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, 490 mANW.get(), NULL); 491 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 492 ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ; 493 494 EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, 495 mEglContext)); 496 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 497} 498 499 500///////////////////////////////////////////////////////////////////// 501// Methods in SurfaceMediaSourceTest 502///////////////////////////////////////////////////////////////////// 503 504// One pass of dequeuing and queuing the buffer. Fill it in with 505// cpu YV12 buffer 506void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) { 507 ANativeWindowBuffer* anb; 508 ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); 509 ASSERT_TRUE(anb != NULL); 510 511 sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); 512 ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); 513 514 // Fill the buffer with the a checkerboard pattern 515 uint8_t* img = NULL; 516 buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); 517 SurfaceMediaSourceTest::fillYV12Buffer(img, width, height, buf->getStride()); 518 buf->unlock(); 519 520 ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); 521} 522 523// Dequeuing and queuing the buffer without really filling it in. 524void SurfaceMediaSourceTest::oneBufferPassNoFill(int width, int height ) { 525 ANativeWindowBuffer* anb; 526 ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); 527 ASSERT_TRUE(anb != NULL); 528 529 sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); 530 // ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); 531 // We do not fill the buffer in. Just queue it back. 532 ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); 533} 534 535// Fill a YV12 buffer with a multi-colored checkerboard pattern 536void SurfaceMediaSourceTest::fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { 537 const int blockWidth = w > 16 ? w / 16 : 1; 538 const int blockHeight = h > 16 ? h / 16 : 1; 539 const int yuvTexOffsetY = 0; 540 int yuvTexStrideY = stride; 541 int yuvTexOffsetV = yuvTexStrideY * h; 542 int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 543 int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; 544 int yuvTexStrideU = yuvTexStrideV; 545 for (int x = 0; x < w; x++) { 546 for (int y = 0; y < h; y++) { 547 int parityX = (x / blockWidth) & 1; 548 int parityY = (y / blockHeight) & 1; 549 unsigned char intensity = (parityX ^ parityY) ? 63 : 191; 550 buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; 551 if (x < w / 2 && y < h / 2) { 552 buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; 553 if (x * 2 < w / 2 && y * 2 < h / 2) { 554 buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = 555 buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = 556 buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = 557 buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = 558 intensity; 559 } 560 } 561 } 562 } 563} 564 565// Fill a YV12 buffer with red outside a given rectangle and green inside it. 566void SurfaceMediaSourceTest::fillYV12BufferRect(uint8_t* buf, int w, 567 int h, int stride, const android_native_rect_t& rect) { 568 const int yuvTexOffsetY = 0; 569 int yuvTexStrideY = stride; 570 int yuvTexOffsetV = yuvTexStrideY * h; 571 int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 572 int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; 573 int yuvTexStrideU = yuvTexStrideV; 574 for (int x = 0; x < w; x++) { 575 for (int y = 0; y < h; y++) { 576 bool inside = rect.left <= x && x < rect.right && 577 rect.top <= y && y < rect.bottom; 578 buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; 579 if (x < w / 2 && y < h / 2) { 580 bool inside = rect.left <= 2*x && 2*x < rect.right && 581 rect.top <= 2*y && 2*y < rect.bottom; 582 buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; 583 buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = 584 inside ? 16 : 255; 585 } 586 } 587 } 588} ///////// End of class SurfaceMediaSourceTest 589 590/////////////////////////////////////////////////////////////////// 591// Class to imitate the recording ///////////////////////////// 592// //////////////////////////////////////////////////////////////// 593struct SimpleDummyRecorder { 594 sp<MediaSource> mSource; 595 596 SimpleDummyRecorder 597 (const sp<MediaSource> &source): mSource(source) {} 598 599 status_t start() { return mSource->start();} 600 status_t stop() { return mSource->stop();} 601 602 // fakes reading from a media source 603 status_t readFromSource() { 604 MediaBuffer *buffer; 605 status_t err = mSource->read(&buffer); 606 if (err != OK) { 607 return err; 608 } 609 buffer->release(); 610 buffer = NULL; 611 return OK; 612 } 613}; 614/////////////////////////////////////////////////////////////////// 615// TESTS 616// SurfaceMediaSourceTest class contains tests that fill the buffers 617// using the cpu calls 618// SurfaceMediaSourceGLTest class contains tests that fill the buffers 619// using the GL calls. 620// TODO: None of the tests actually verify the encoded images.. so at this point, 621// these are mostly functionality tests + visual inspection 622////////////////////////////////////////////////////////////////////// 623 624// Just pass one buffer from the native_window to the SurfaceMediaSource 625// Dummy Encoder 626static int testId = 1; 627TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotOneBufferPass) { 628 ALOGV("Test # %d", testId++); 629 ALOGV("Testing OneBufferPass ******************************"); 630 631 ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), 632 HAL_PIXEL_FORMAT_YV12)); 633 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 634} 635 636// Pass the buffer with the wrong height and weight and should not be accepted 637// Dummy Encoder 638TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) { 639 ALOGV("Test # %d", testId++); 640 ALOGV("Testing Wrong size BufferPass ******************************"); 641 642 // setting the client side buffer size different than the server size 643 ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), 644 10, 10)); 645 ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), 646 HAL_PIXEL_FORMAT_YV12)); 647 648 ANativeWindowBuffer* anb; 649 650 // Note: make sure we get an ERROR back when dequeuing! 651 ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); 652} 653 654// pass multiple buffers from the native_window the SurfaceMediaSource 655// Dummy Encoder 656TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { 657 ALOGV("Test # %d", testId++); 658 ALOGV("Testing MultiBufferPass, Dummy Recorder *********************"); 659 ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), 660 HAL_PIXEL_FORMAT_YV12)); 661 662 SimpleDummyRecorder writer(mSMS); 663 writer.start(); 664 665 int32_t nFramesCount = 0; 666 while (nFramesCount < 300) { 667 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 668 669 ASSERT_EQ(NO_ERROR, writer.readFromSource()); 670 671 nFramesCount++; 672 } 673 writer.stop(); 674} 675 676// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource 677// Dummy Encoder 678TEST_F(SurfaceMediaSourceTest, DISABLED_DummyLagEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { 679 ALOGV("Test # %d", testId++); 680 ALOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************"); 681 682 ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), 683 HAL_PIXEL_FORMAT_YV12)); 684 685 SimpleDummyRecorder writer(mSMS); 686 writer.start(); 687 688 int32_t nFramesCount = 1; 689 const int FRAMES_LAG = mSMS->getBufferCount() - 1; 690 while (nFramesCount <= 300) { 691 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 692 // Forcing the writer to lag behind a few frames 693 if (nFramesCount > FRAMES_LAG) { 694 ASSERT_EQ(NO_ERROR, writer.readFromSource()); 695 } 696 nFramesCount++; 697 } 698 writer.stop(); 699} 700 701// pass multiple buffers from the native_window the SurfaceMediaSource 702// A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer 703TEST_F(SurfaceMediaSourceTest, DISABLED_DummyThreadedEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { 704 ALOGV("Test # %d", testId++); 705 ALOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********"); 706 ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), 707 HAL_PIXEL_FORMAT_YV12)); 708 709 DummyRecorder writer(mSMS); 710 writer.start(); 711 712 int32_t nFramesCount = 0; 713 while (nFramesCount <= 300) { 714 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 715 716 nFramesCount++; 717 } 718 writer.stop(); 719} 720 721// Test to examine actual encoding using mediarecorder 722// We use the mediaserver to create a mediarecorder and send 723// it back to us. So SurfaceMediaSource lives in the same process 724// as the mediaserver. 725// Very close to the actual camera, except that the 726// buffers are filled and queueud by the CPU instead of GL. 727TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaServer) { 728 ALOGV("Test # %d", testId++); 729 ALOGV("************** Testing the whole pipeline with actual MediaRecorder ***********"); 730 ALOGV("************** SurfaceMediaSource is same process as mediaserver ***********"); 731 732 const char *fileName = "/sdcard/outputSurfEncMSource.mp4"; 733 int fd = open(fileName, O_RDWR | O_CREAT, 0744); 734 if (fd < 0) { 735 ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd); 736 } 737 CHECK(fd >= 0); 738 739 sp<MediaRecorder> mr = SurfaceMediaSourceGLTest::setUpMediaRecorder(fd, 740 VIDEO_SOURCE_GRALLOC_BUFFER, 741 OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, 742 mYuvTexHeight, 30); 743 // get the reference to the surfacemediasource living in 744 // mediaserver that is created by stagefrightrecorder 745 sp<ISurfaceTexture> iST = mr->querySurfaceMediaSourceFromMediaServer(); 746 mSTC = new SurfaceTextureClient(iST); 747 mANW = mSTC; 748 ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); 749 ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), 750 HAL_PIXEL_FORMAT_YV12)); 751 752 int32_t nFramesCount = 0; 753 while (nFramesCount <= 300) { 754 oneBufferPassNoFill(mYuvTexWidth, mYuvTexHeight); 755 nFramesCount++; 756 ALOGV("framesCount = %d", nFramesCount); 757 } 758 759 ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU)); 760 ALOGV("Stopping MediaRecorder..."); 761 CHECK_EQ((status_t)OK, mr->stop()); 762 mr.clear(); 763 close(fd); 764} 765 766////////////////////////////////////////////////////////////////////// 767// GL tests 768///////////////////////////////////////////////////////////////////// 769 770// Test to examine whether we can choose the Recordable Android GLConfig 771// DummyRecorder used- no real encoding here 772TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWriter) { 773 ALOGV("Test # %d", testId++); 774 ALOGV("Verify creating a surface w/ right config + dummy writer*********"); 775 776 mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); 777 mSTC = new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >( mSMS)); 778 mANW = mSTC; 779 780 DummyRecorder writer(mSMS); 781 writer.start(); 782 783 mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, 784 mANW.get(), NULL); 785 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 786 ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ; 787 788 EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, 789 mEglContext)); 790 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 791 792 int32_t nFramesCount = 0; 793 while (nFramesCount <= 300) { 794 oneBufferPassGL(); 795 nFramesCount++; 796 ALOGV("framesCount = %d", nFramesCount); 797 } 798 799 EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, 800 EGL_NO_CONTEXT)); 801 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 802 eglDestroySurface(mEglDisplay, mEglSurface); 803 mEglSurface = EGL_NO_SURFACE; 804 805 writer.stop(); 806} 807// Test to examine whether we can render GL buffers in to the surface 808// created with the native window handle 809TEST_F(SurfaceMediaSourceGLTest, RenderingToRecordableEGLSurfaceWorks) { 810 ALOGV("Test # %d", testId++); 811 ALOGV("RenderingToRecordableEGLSurfaceWorks *********************"); 812 // Do the producer side of things 813 glClearColor(0.6, 0.6, 0.6, 0.6); 814 glClear(GL_COLOR_BUFFER_BIT); 815 816 glEnable(GL_SCISSOR_TEST); 817 glScissor(4, 4, 4, 4); 818 glClearColor(1.0, 0.0, 0.0, 1.0); 819 glClear(GL_COLOR_BUFFER_BIT); 820 821 glScissor(24, 48, 4, 4); 822 glClearColor(0.0, 1.0, 0.0, 1.0); 823 glClear(GL_COLOR_BUFFER_BIT); 824 825 glScissor(37, 17, 4, 4); 826 glClearColor(0.0, 0.0, 1.0, 1.0); 827 glClear(GL_COLOR_BUFFER_BIT); 828 829 EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); 830 EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); 831 EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); 832 EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); 833 834 EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255)); 835 EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255)); 836 EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255)); 837 EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153)); 838 EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153)); 839 EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153)); 840 EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153)); 841 EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153)); 842 EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153)); 843 EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153)); 844 EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153)); 845 EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153)); 846 EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153)); 847 EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153)); 848 EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153)); 849 EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); 850} 851 852// Test to examine the actual encoding with GL buffers 853// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource 854// The same pattern is rendered every frame 855TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaSameImageEachBufNpotWrite) { 856 ALOGV("Test # %d", testId++); 857 ALOGV("************** Testing the whole pipeline with actual Recorder ***********"); 858 ALOGV("************** GL Filling the buffers ***********"); 859 // Note: No need to set the colorformat for the buffers. The colorformat is 860 // in the GRAlloc buffers itself. 861 862 const char *fileName = "/sdcard/outputSurfEncMSourceGL.mp4"; 863 int fd = open(fileName, O_RDWR | O_CREAT, 0744); 864 if (fd < 0) { 865 ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd); 866 } 867 CHECK(fd >= 0); 868 869 sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER, 870 OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30); 871 872 // get the reference to the surfacemediasource living in 873 // mediaserver that is created by stagefrightrecorder 874 setUpEGLSurfaceFromMediaRecorder(mr); 875 876 int32_t nFramesCount = 0; 877 while (nFramesCount <= 300) { 878 oneBufferPassGL(); 879 nFramesCount++; 880 ALOGV("framesCount = %d", nFramesCount); 881 } 882 883 EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, 884 EGL_NO_CONTEXT)); 885 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 886 eglDestroySurface(mEglDisplay, mEglSurface); 887 mEglSurface = EGL_NO_SURFACE; 888 889 ALOGV("Stopping MediaRecorder..."); 890 CHECK_EQ((status_t)OK, mr->stop()); 891 mr.clear(); 892 close(fd); 893} 894 895// Test to examine the actual encoding from the GL Buffers 896// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource 897// A different pattern is rendered every frame 898TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaDiffImageEachBufNpotWrite) { 899 ALOGV("Test # %d", testId++); 900 ALOGV("************** Testing the whole pipeline with actual Recorder ***********"); 901 ALOGV("************** Diff GL Filling the buffers ***********"); 902 // Note: No need to set the colorformat for the buffers. The colorformat is 903 // in the GRAlloc buffers itself. 904 905 const char *fileName = "/sdcard/outputSurfEncMSourceGLDiff.mp4"; 906 int fd = open(fileName, O_RDWR | O_CREAT, 0744); 907 if (fd < 0) { 908 ALOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd); 909 } 910 CHECK(fd >= 0); 911 912 sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER, 913 OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30); 914 915 // get the reference to the surfacemediasource living in 916 // mediaserver that is created by stagefrightrecorder 917 setUpEGLSurfaceFromMediaRecorder(mr); 918 919 int32_t nFramesCount = 0; 920 while (nFramesCount <= 300) { 921 oneBufferPassGL(nFramesCount); 922 nFramesCount++; 923 ALOGV("framesCount = %d", nFramesCount); 924 } 925 926 EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, 927 EGL_NO_CONTEXT)); 928 ASSERT_EQ(EGL_SUCCESS, eglGetError()); 929 eglDestroySurface(mEglDisplay, mEglSurface); 930 mEglSurface = EGL_NO_SURFACE; 931 932 ALOGV("Stopping MediaRecorder..."); 933 CHECK_EQ((status_t)OK, mr->stop()); 934 mr.clear(); 935 close(fd); 936} 937} // namespace android 938