SurfaceMediaSource_test.cpp revision a361483bb5dbd3bbf132c5b99b2df7d197c3fc50
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; 75 const int mYuvTexHeight; 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 128 129 sp<MediaSource> encoder = 130 OMXCodec::Create( 131 client.interface(), enc_meta, true /* createEncoder */, mSMS); 132 133 sp<MPEG4Writer> writer = new MPEG4Writer(fileName); 134 writer->addSource(encoder); 135 136 return writer; 137} 138 139// Fill a YV12 buffer with a multi-colored checkerboard pattern 140void SurfaceMediaSourceTest::fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { 141 const int blockWidth = w > 16 ? w / 16 : 1; 142 const int blockHeight = h > 16 ? h / 16 : 1; 143 const int yuvTexOffsetY = 0; 144 int yuvTexStrideY = stride; 145 int yuvTexOffsetV = yuvTexStrideY * h; 146 int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 147 int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; 148 int yuvTexStrideU = yuvTexStrideV; 149 for (int x = 0; x < w; x++) { 150 for (int y = 0; y < h; y++) { 151 int parityX = (x / blockWidth) & 1; 152 int parityY = (y / blockHeight) & 1; 153 unsigned char intensity = (parityX ^ parityY) ? 63 : 191; 154 buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; 155 if (x < w / 2 && y < h / 2) { 156 buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; 157 if (x * 2 < w / 2 && y * 2 < h / 2) { 158 buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = 159 buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = 160 buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = 161 buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = 162 intensity; 163 } 164 } 165 } 166 } 167} 168 169// Fill a YV12 buffer with red outside a given rectangle and green inside it. 170void SurfaceMediaSourceTest::fillYV12BufferRect(uint8_t* buf, int w, 171 int h, int stride, const android_native_rect_t& rect) { 172 const int yuvTexOffsetY = 0; 173 int yuvTexStrideY = stride; 174 int yuvTexOffsetV = yuvTexStrideY * h; 175 int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 176 int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; 177 int yuvTexStrideU = yuvTexStrideV; 178 for (int x = 0; x < w; x++) { 179 for (int y = 0; y < h; y++) { 180 bool inside = rect.left <= x && x < rect.right && 181 rect.top <= y && y < rect.bottom; 182 buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; 183 if (x < w / 2 && y < h / 2) { 184 bool inside = rect.left <= 2*x && 2*x < rect.right && 185 rect.top <= 2*y && 2*y < rect.bottom; 186 buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; 187 buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = 188 inside ? 16 : 255; 189 } 190 } 191 } 192} ///////// End of class SurfaceMediaSourceTest 193 194/////////////////////////////////////////////////////////////////// 195// Class to imitate the recording ///////////////////////////// 196// //////////////////////////////////////////////////////////////// 197struct SimpleDummyRecorder { 198 sp<MediaSource> mSource; 199 200 SimpleDummyRecorder 201 (const sp<MediaSource> &source): mSource(source) {} 202 203 status_t start() { return mSource->start();} 204 status_t stop() { return mSource->stop();} 205 206 // fakes reading from a media source 207 status_t readFromSource() { 208 MediaBuffer *buffer; 209 status_t err = mSource->read(&buffer); 210 if (err != OK) { 211 return err; 212 } 213 buffer->release(); 214 buffer = NULL; 215 return OK; 216 } 217}; 218 219/////////////////////////////////////////////////////////////////// 220// TESTS 221// Just pass one buffer from the native_window to the SurfaceMediaSource 222TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotOneBufferPass) { 223 LOGV("Testing OneBufferPass ******************************"); 224 225 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 226 0, 0, HAL_PIXEL_FORMAT_YV12)); 227 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 228 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 229 230 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 231} 232 233// Pass the buffer with the wrong height and weight and should not be accepted 234TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) { 235 LOGV("Testing Wrong size BufferPass ******************************"); 236 237 // setting the client side buffer size different than the server size 238 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 239 10, 10, HAL_PIXEL_FORMAT_YV12)); 240 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 241 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 242 243 ANativeWindowBuffer* anb; 244 245 // make sure we get an error back when dequeuing! 246 ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); 247} 248 249 250// pass multiple buffers from the native_window the SurfaceMediaSource 251// A dummy writer is used to simulate actual MPEG4Writer 252TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { 253 LOGV("Testing MultiBufferPass, Dummy Recorder *********************"); 254 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 255 0, 0, HAL_PIXEL_FORMAT_YV12)); 256 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 257 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 258 259 SimpleDummyRecorder writer(mSMS); 260 writer.start(); 261 262 int32_t nFramesCount = 0; 263 while (nFramesCount < 300) { 264 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 265 266 ASSERT_EQ(NO_ERROR, writer.readFromSource()); 267 268 nFramesCount++; 269 } 270 writer.stop(); 271} 272 273// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource 274// A dummy writer is used to simulate actual MPEG4Writer 275TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassLag) { 276 LOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************"); 277 278 ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), 279 0, 0, HAL_PIXEL_FORMAT_YV12)); 280 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 281 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 282 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)); 326 ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), 327 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); 328 OMXClient client; 329 CHECK_EQ(OK, client.connect()); 330 331 sp<MPEG4Writer> writer = setUpWriter(client); 332 int64_t start = systemTime(); 333 CHECK_EQ(OK, writer->start()); 334 335 int32_t nFramesCount = 0; 336 while (nFramesCount <= 300) { 337 oneBufferPass(mYuvTexWidth, mYuvTexHeight); 338 nFramesCount++; 339 } 340 341 CHECK_EQ(OK, writer->stop()); 342 writer.clear(); 343 int64_t end = systemTime(); 344 client.disconnect(); 345} 346 347 348} // namespace android 349