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