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