SurfaceMediaSource_test.cpp revision 1a2fafbaa36390a06cc9a066fcbe147c8c47ea77
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 mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); 61 mSMS->setSynchronousMode(true); 62 mSTC = new SurfaceTextureClient(mSMS); 63 mANW = mSTC; 64 65 } 66 67 68 virtual void TearDown() { 69 mSMS.clear(); 70 mSTC.clear(); 71 mANW.clear(); 72 } 73 74 const int mYuvTexWidth;// = 64; 75 const int mYuvTexHeight;// = 66; 76 77 sp<SurfaceMediaSource> mSMS; 78 sp<SurfaceTextureClient> mSTC; 79 sp<ANativeWindow> mANW; 80 81}; 82 83void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) { 84 LOGV("One Buffer Pass"); 85 ANativeWindowBuffer* anb; 86 ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); 87 ASSERT_TRUE(anb != NULL); 88 89 sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); 90 ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); 91 92 // Fill the buffer with the a checkerboard pattern 93 uint8_t* img = NULL; 94 buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); 95 SurfaceMediaSourceTest::fillYV12Buffer(img, width, height, buf->getStride()); 96 buf->unlock(); 97 98 ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); 99} 100 101sp<MPEG4Writer> SurfaceMediaSourceTest::setUpWriter(OMXClient &client ) { 102 // Writing to a file 103 const char *fileName = "/sdcard/outputSurfEnc.mp4"; 104 sp<MetaData> enc_meta = new MetaData; 105 enc_meta->setInt32(kKeyBitRate, 300000); 106 enc_meta->setInt32(kKeyFrameRate, 30); 107 108 enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); 109 110 sp<MetaData> meta = mSMS->getFormat(); 111 112 int32_t width, height, stride, sliceHeight, colorFormat; 113 CHECK(meta->findInt32(kKeyWidth, &width)); 114 CHECK(meta->findInt32(kKeyHeight, &height)); 115 CHECK(meta->findInt32(kKeyStride, &stride)); 116 CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); 117 CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); 118 119 enc_meta->setInt32(kKeyWidth, width); 120 enc_meta->setInt32(kKeyHeight, height); 121 enc_meta->setInt32(kKeyIFramesInterval, 1); 122 enc_meta->setInt32(kKeyStride, stride); 123 enc_meta->setInt32(kKeySliceHeight, sliceHeight); 124 // TODO: overwriting the colorformat since the format set by GRAlloc 125 // could be wrong or not be read by OMX 126 enc_meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); 127 // colorFormat); 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 // OMX_COLOR_FormatYUV420Planar)); // )); 229 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 230 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 231 232 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 233} 234 235// Pass the buffer with the wrong height and weight and should not be accepted 236TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) { 237 LOGV("Testing Wrong size BufferPass ******************************"); 238 239 // setting the client side buffer size different than the server size 240 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 241 10, 10, HAL_PIXEL_FORMAT_YV12)); 242 // OMX_COLOR_FormatYUV420Planar)); // )); 243 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 244 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 245 246 ANativeWindowBuffer* anb; 247 248 // make sure we get an error back when dequeuing! 249 ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); 250} 251 252 253// pass multiple buffers from the native_window the SurfaceMediaSource 254// A dummy writer is used to simulate actual MPEG4Writer 255TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { 256 LOGV("Testing MultiBufferPass, Dummy Recorder *********************"); 257 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 258 0, 0, HAL_PIXEL_FORMAT_YV12)); 259 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 260 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 261 SimpleDummyRecorder writer(mSMS); 262 writer.start(); 263 264 int32_t nFramesCount = 0; 265 while (nFramesCount < 300) { 266 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 267 268 ASSERT_EQ(NO_ERROR, writer.readFromSource()); 269 270 nFramesCount++; 271 } 272 writer.stop(); 273} 274 275// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource 276// A dummy writer is used to simulate actual MPEG4Writer 277TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassLag) { 278 LOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************"); 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 SimpleDummyRecorder writer(mSMS); 284 writer.start(); 285 286 int32_t nFramesCount = 1; 287 const int FRAMES_LAG = mSMS->getBufferCount() - 1; 288 while (nFramesCount <= 300) { 289 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 290 // Forcing the writer to lag behind a few frames 291 if (nFramesCount > FRAMES_LAG) { 292 ASSERT_EQ(NO_ERROR, writer.readFromSource()); 293 } 294 nFramesCount++; 295 } 296 writer.stop(); 297} 298 299// pass multiple buffers from the native_window the SurfaceMediaSource 300// A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer 301TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassThreaded) { 302 LOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********"); 303 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 304 0, 0, HAL_PIXEL_FORMAT_YV12)); 305 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 306 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 307 308 DummyRecorder writer(mSMS); 309 writer.start(); 310 311 int32_t nFramesCount = 0; 312 while (nFramesCount <= 300) { 313 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 314 315 nFramesCount++; 316 } 317 writer.stop(); 318} 319 320// Test to examine the actual encoding. Temporarily disabled till the 321// colorformat and encoding from GRAlloc data is resolved 322TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuFilledYV12BufferNpotWrite) { 323 LOGV("Testing the whole pipeline with actual Recorder"); 324 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 325 0, 0, HAL_PIXEL_FORMAT_YV12)); // OMX_COLOR_FormatYUV420Planar)); // )); 326 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 327 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 328 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