SurfaceMediaSource_test.cpp revision b6f2fced47866a5d095b2b18d2e28d7c4321e3a6
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_TAG "SurfaceMediaSource_test" 18// #define LOG_NDEBUG 0 19 20#include <gtest/gtest.h> 21#include <utils/String8.h> 22#include <utils/Errors.h> 23 24#include <media/stagefright/SurfaceMediaSource.h> 25 26#include <gui/SurfaceTextureClient.h> 27#include <ui/GraphicBuffer.h> 28#include <surfaceflinger/ISurfaceComposer.h> 29#include <surfaceflinger/Surface.h> 30#include <surfaceflinger/SurfaceComposerClient.h> 31 32#include <binder/ProcessState.h> 33#include <ui/FramebufferNativeWindow.h> 34 35#include <media/stagefright/MediaDebug.h> 36#include <media/stagefright/MediaDefs.h> 37#include <media/stagefright/MetaData.h> 38#include <media/stagefright/MPEG4Writer.h> 39#include <media/stagefright/OMXClient.h> 40#include <media/stagefright/OMXCodec.h> 41#include <OMX_Component.h> 42 43#include "DummyRecorder.h" 44 45namespace android { 46 47 48class SurfaceMediaSourceTest : public ::testing::Test { 49public: 50 51 SurfaceMediaSourceTest( ): mYuvTexWidth(64), mYuvTexHeight(66) { } 52 sp<MPEG4Writer> setUpWriter(OMXClient &client ); 53 void oneBufferPass(int width, int height ); 54 static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ; 55 static void fillYV12BufferRect(uint8_t* buf, int w, int h, 56 int stride, const android_native_rect_t& rect) ; 57protected: 58 59 virtual void SetUp() { 60 android::ProcessState::self()->startThreadPool(); 61 mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); 62 mSMS->setSynchronousMode(true); 63 mSTC = new SurfaceTextureClient(mSMS); 64 mANW = mSTC; 65 66 } 67 68 69 virtual void TearDown() { 70 mSMS.clear(); 71 mSTC.clear(); 72 mANW.clear(); 73 } 74 75 const int mYuvTexWidth; 76 const int mYuvTexHeight; 77 78 sp<SurfaceMediaSource> mSMS; 79 sp<SurfaceTextureClient> mSTC; 80 sp<ANativeWindow> mANW; 81 82}; 83 84void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) { 85 LOGV("One Buffer Pass"); 86 ANativeWindowBuffer* anb; 87 ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); 88 ASSERT_TRUE(anb != NULL); 89 90 sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); 91 ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); 92 93 // Fill the buffer with the a checkerboard pattern 94 uint8_t* img = NULL; 95 buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); 96 SurfaceMediaSourceTest::fillYV12Buffer(img, width, height, buf->getStride()); 97 buf->unlock(); 98 99 ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); 100} 101 102sp<MPEG4Writer> SurfaceMediaSourceTest::setUpWriter(OMXClient &client ) { 103 // Writing to a file 104 const char *fileName = "/sdcard/outputSurfEnc.mp4"; 105 sp<MetaData> enc_meta = new MetaData; 106 enc_meta->setInt32(kKeyBitRate, 300000); 107 enc_meta->setInt32(kKeyFrameRate, 30); 108 109 enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); 110 111 sp<MetaData> meta = mSMS->getFormat(); 112 113 int32_t width, height, stride, sliceHeight, colorFormat; 114 CHECK(meta->findInt32(kKeyWidth, &width)); 115 CHECK(meta->findInt32(kKeyHeight, &height)); 116 CHECK(meta->findInt32(kKeyStride, &stride)); 117 CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); 118 CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); 119 120 enc_meta->setInt32(kKeyWidth, width); 121 enc_meta->setInt32(kKeyHeight, height); 122 enc_meta->setInt32(kKeyIFramesInterval, 1); 123 enc_meta->setInt32(kKeyStride, stride); 124 enc_meta->setInt32(kKeySliceHeight, sliceHeight); 125 // TODO: overwriting the colorformat since the format set by GRAlloc 126 // could be wrong or not be read by OMX 127 enc_meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); 128 129 130 sp<MediaSource> encoder = 131 OMXCodec::Create( 132 client.interface(), enc_meta, true /* createEncoder */, mSMS); 133 134 sp<MPEG4Writer> writer = new MPEG4Writer(fileName); 135 writer->addSource(encoder); 136 137 return writer; 138} 139 140// Fill a YV12 buffer with a multi-colored checkerboard pattern 141void SurfaceMediaSourceTest::fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { 142 const int blockWidth = w > 16 ? w / 16 : 1; 143 const int blockHeight = h > 16 ? h / 16 : 1; 144 const int yuvTexOffsetY = 0; 145 int yuvTexStrideY = stride; 146 int yuvTexOffsetV = yuvTexStrideY * h; 147 int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 148 int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; 149 int yuvTexStrideU = yuvTexStrideV; 150 for (int x = 0; x < w; x++) { 151 for (int y = 0; y < h; y++) { 152 int parityX = (x / blockWidth) & 1; 153 int parityY = (y / blockHeight) & 1; 154 unsigned char intensity = (parityX ^ parityY) ? 63 : 191; 155 buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; 156 if (x < w / 2 && y < h / 2) { 157 buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; 158 if (x * 2 < w / 2 && y * 2 < h / 2) { 159 buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = 160 buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = 161 buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = 162 buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = 163 intensity; 164 } 165 } 166 } 167 } 168} 169 170// Fill a YV12 buffer with red outside a given rectangle and green inside it. 171void SurfaceMediaSourceTest::fillYV12BufferRect(uint8_t* buf, int w, 172 int h, int stride, const android_native_rect_t& rect) { 173 const int yuvTexOffsetY = 0; 174 int yuvTexStrideY = stride; 175 int yuvTexOffsetV = yuvTexStrideY * h; 176 int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 177 int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; 178 int yuvTexStrideU = yuvTexStrideV; 179 for (int x = 0; x < w; x++) { 180 for (int y = 0; y < h; y++) { 181 bool inside = rect.left <= x && x < rect.right && 182 rect.top <= y && y < rect.bottom; 183 buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; 184 if (x < w / 2 && y < h / 2) { 185 bool inside = rect.left <= 2*x && 2*x < rect.right && 186 rect.top <= 2*y && 2*y < rect.bottom; 187 buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; 188 buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = 189 inside ? 16 : 255; 190 } 191 } 192 } 193} ///////// End of class SurfaceMediaSourceTest 194 195/////////////////////////////////////////////////////////////////// 196// Class to imitate the recording ///////////////////////////// 197// //////////////////////////////////////////////////////////////// 198struct SimpleDummyRecorder { 199 sp<MediaSource> mSource; 200 201 SimpleDummyRecorder 202 (const sp<MediaSource> &source): mSource(source) {} 203 204 status_t start() { return mSource->start();} 205 status_t stop() { return mSource->stop();} 206 207 // fakes reading from a media source 208 status_t readFromSource() { 209 MediaBuffer *buffer; 210 status_t err = mSource->read(&buffer); 211 if (err != OK) { 212 return err; 213 } 214 buffer->release(); 215 buffer = NULL; 216 return OK; 217 } 218}; 219 220/////////////////////////////////////////////////////////////////// 221// TESTS 222// Just pass one buffer from the native_window to the SurfaceMediaSource 223TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotOneBufferPass) { 224 LOGV("Testing OneBufferPass ******************************"); 225 226 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 227 0, 0, HAL_PIXEL_FORMAT_YV12)); 228 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 229 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 230 231 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 232} 233 234// Pass the buffer with the wrong height and weight and should not be accepted 235TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) { 236 LOGV("Testing Wrong size BufferPass ******************************"); 237 238 // setting the client side buffer size different than the server size 239 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 240 10, 10, HAL_PIXEL_FORMAT_YV12)); 241 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 242 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 243 244 ANativeWindowBuffer* anb; 245 246 // make sure we get an error back when dequeuing! 247 ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); 248} 249 250 251// pass multiple buffers from the native_window the SurfaceMediaSource 252// A dummy writer is used to simulate actual MPEG4Writer 253TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { 254 LOGV("Testing MultiBufferPass, Dummy Recorder *********************"); 255 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 256 0, 0, HAL_PIXEL_FORMAT_YV12)); 257 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 258 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 259 260 SimpleDummyRecorder writer(mSMS); 261 writer.start(); 262 263 int32_t nFramesCount = 0; 264 while (nFramesCount < 300) { 265 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 266 267 ASSERT_EQ(NO_ERROR, writer.readFromSource()); 268 269 nFramesCount++; 270 } 271 writer.stop(); 272} 273 274// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource 275// A dummy writer is used to simulate actual MPEG4Writer 276TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassLag) { 277 LOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************"); 278 279 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 280 0, 0, HAL_PIXEL_FORMAT_YV12)); 281 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 282 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 283 284 SimpleDummyRecorder writer(mSMS); 285 writer.start(); 286 287 int32_t nFramesCount = 1; 288 const int FRAMES_LAG = mSMS->getBufferCount() - 1; 289 while (nFramesCount <= 300) { 290 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 291 // Forcing the writer to lag behind a few frames 292 if (nFramesCount > FRAMES_LAG) { 293 ASSERT_EQ(NO_ERROR, writer.readFromSource()); 294 } 295 nFramesCount++; 296 } 297 writer.stop(); 298} 299 300// pass multiple buffers from the native_window the SurfaceMediaSource 301// A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer 302TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassThreaded) { 303 LOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********"); 304 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 305 0, 0, HAL_PIXEL_FORMAT_YV12)); 306 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 307 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 308 309 DummyRecorder writer(mSMS); 310 writer.start(); 311 312 int32_t nFramesCount = 0; 313 while (nFramesCount <= 300) { 314 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 315 316 nFramesCount++; 317 } 318 writer.stop(); 319} 320 321// Test to examine the actual encoding. Temporarily disabled till the 322// colorformat and encoding from GRAlloc data is resolved 323TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuFilledYV12BufferNpotWrite) { 324 LOGV("Testing the whole pipeline with actual Recorder"); 325 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 326 0, 0, HAL_PIXEL_FORMAT_YV12)); 327 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 328 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 329 OMXClient client; 330 CHECK_EQ(OK, client.connect()); 331 332 sp<MPEG4Writer> writer = setUpWriter(client); 333 int64_t start = systemTime(); 334 CHECK_EQ(OK, writer->start()); 335 336 int32_t nFramesCount = 0; 337 while (nFramesCount <= 300) { 338 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 339 nFramesCount++; 340 } 341 342 CHECK_EQ(OK, writer->stop()); 343 writer.clear(); 344 int64_t end = systemTime(); 345 client.disconnect(); 346} 347 348 349} // namespace android 350