OMXHarness.cpp revision 134ee6a324c35f39e3576172e4eae4c6de6eb9dc
1/*
2 * Copyright (C) 2009 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 "OMXHarness"
19#include <utils/Log.h>
20
21#include "OMXHarness.h"
22
23#include <sys/time.h>
24
25#include <binder/ProcessState.h>
26#include <binder/IServiceManager.h>
27#include <binder/MemoryDealer.h>
28#include <media/IMediaPlayerService.h>
29#include <media/stagefright/DataSource.h>
30#include <media/stagefright/MediaBuffer.h>
31#include <media/stagefright/MediaDebug.h>
32#include <media/stagefright/MediaErrors.h>
33#include <media/stagefright/MediaExtractor.h>
34#include <media/stagefright/MediaSource.h>
35#include <media/stagefright/MetaData.h>
36#include <media/stagefright/OMXCodec.h>
37
38#define DEFAULT_TIMEOUT         500000
39
40namespace android {
41
42static int64_t getNowUs() {
43    struct timeval tv;
44    gettimeofday(&tv, NULL);
45
46    return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
47}
48
49Harness::Harness()
50    : mInitCheck(NO_INIT) {
51    mInitCheck = initOMX();
52}
53
54Harness::~Harness() {
55}
56
57status_t Harness::initCheck() const {
58    return mInitCheck;
59}
60
61status_t Harness::initOMX() {
62    sp<IServiceManager> sm = defaultServiceManager();
63    sp<IBinder> binder = sm->getService(String16("media.player"));
64    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
65    mOMX = service->getOMX();
66
67    return mOMX != 0 ? OK : NO_INIT;
68}
69
70void Harness::onMessage(const omx_message &msg) {
71    Mutex::Autolock autoLock(mLock);
72    mMessageQueue.push_back(msg);
73    mMessageAddedCondition.signal();
74}
75
76status_t Harness::dequeueMessageForNode(
77        IOMX::node_id node, omx_message *msg, int64_t timeoutUs) {
78    return dequeueMessageForNodeIgnoringBuffers(
79            node, NULL, NULL, msg, timeoutUs);
80}
81
82// static
83bool Harness::handleBufferMessage(
84        const omx_message &msg,
85        Vector<Buffer> *inputBuffers,
86        Vector<Buffer> *outputBuffers) {
87    switch (msg.type) {
88        case omx_message::EMPTY_BUFFER_DONE:
89        {
90            if (inputBuffers) {
91                for (size_t i = 0; i < inputBuffers->size(); ++i) {
92                    if ((*inputBuffers)[i].mID == msg.u.buffer_data.buffer) {
93                        inputBuffers->editItemAt(i).mFlags &= ~kBufferBusy;
94                        return true;
95                    }
96                }
97                CHECK(!"should not be here");
98            }
99            break;
100        }
101
102        case omx_message::FILL_BUFFER_DONE:
103        {
104            if (outputBuffers) {
105                for (size_t i = 0; i < outputBuffers->size(); ++i) {
106                    if ((*outputBuffers)[i].mID == msg.u.buffer_data.buffer) {
107                        outputBuffers->editItemAt(i).mFlags &= ~kBufferBusy;
108                        return true;
109                    }
110                }
111                CHECK(!"should not be here");
112            }
113            break;
114        }
115
116        default:
117            break;
118    }
119
120    return false;
121}
122
123status_t Harness::dequeueMessageForNodeIgnoringBuffers(
124        IOMX::node_id node,
125        Vector<Buffer> *inputBuffers,
126        Vector<Buffer> *outputBuffers,
127        omx_message *msg, int64_t timeoutUs) {
128    int64_t finishBy = getNowUs() + timeoutUs;
129
130    for (;;) {
131        Mutex::Autolock autoLock(mLock);
132        List<omx_message>::iterator it = mMessageQueue.begin();
133        while (it != mMessageQueue.end()) {
134            if ((*it).node == node) {
135                if (handleBufferMessage(*it, inputBuffers, outputBuffers)) {
136                    it = mMessageQueue.erase(it);
137                    continue;
138                }
139
140                *msg = *it;
141                mMessageQueue.erase(it);
142
143                return OK;
144            }
145
146            ++it;
147        }
148
149        status_t err = (timeoutUs < 0)
150            ? mMessageAddedCondition.wait(mLock)
151            : mMessageAddedCondition.waitRelative(
152                    mLock, (finishBy - getNowUs()) * 1000);
153
154        if (err == TIMED_OUT) {
155            return err;
156        }
157        CHECK_EQ(err, OK);
158    }
159}
160
161status_t Harness::getPortDefinition(
162        IOMX::node_id node, OMX_U32 portIndex,
163        OMX_PARAM_PORTDEFINITIONTYPE *def) {
164    def->nSize = sizeof(*def);
165    def->nVersion.s.nVersionMajor = 1;
166    def->nVersion.s.nVersionMinor = 0;
167    def->nVersion.s.nRevision = 0;
168    def->nVersion.s.nStep = 0;
169    def->nPortIndex = portIndex;
170    return mOMX->getParameter(
171            node, OMX_IndexParamPortDefinition, def, sizeof(*def));
172}
173
174#define EXPECT(condition, info) \
175    if (!(condition)) {         \
176        LOGE(info); printf("\n  * " info "\n"); return UNKNOWN_ERROR; \
177    }
178
179#define EXPECT_SUCCESS(err, info) \
180    EXPECT((err) == OK, info " failed")
181
182status_t Harness::allocatePortBuffers(
183        const sp<MemoryDealer> &dealer,
184        IOMX::node_id node, OMX_U32 portIndex,
185        Vector<Buffer> *buffers) {
186    buffers->clear();
187
188    OMX_PARAM_PORTDEFINITIONTYPE def;
189    status_t err = getPortDefinition(node, portIndex, &def);
190    EXPECT_SUCCESS(err, "getPortDefinition");
191
192    for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
193        Buffer buffer;
194        buffer.mMemory = dealer->allocate(def.nBufferSize);
195        buffer.mFlags = 0;
196        CHECK(buffer.mMemory != NULL);
197
198        err = mOMX->allocateBufferWithBackup(
199                node, portIndex, buffer.mMemory, &buffer.mID);
200        EXPECT_SUCCESS(err, "allocateBuffer");
201
202        buffers->push(buffer);
203    }
204
205    return OK;
206}
207
208status_t Harness::setRole(IOMX::node_id node, const char *role) {
209    OMX_PARAM_COMPONENTROLETYPE params;
210    params.nSize = sizeof(params);
211    params.nVersion.s.nVersionMajor = 1;
212    params.nVersion.s.nVersionMinor = 0;
213    params.nVersion.s.nRevision = 0;
214    params.nVersion.s.nStep = 0;
215    strncpy((char *)params.cRole, role, OMX_MAX_STRINGNAME_SIZE - 1);
216    params.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
217
218    return mOMX->setParameter(
219            node, OMX_IndexParamStandardComponentRole,
220            &params, sizeof(params));
221}
222
223struct NodeReaper {
224    NodeReaper(const sp<Harness> &harness, IOMX::node_id node)
225        : mHarness(harness),
226          mNode(node) {
227    }
228
229    ~NodeReaper() {
230        if (mNode != 0) {
231            mHarness->mOMX->freeNode(mNode);
232            mNode = 0;
233        }
234    }
235
236    void disarm() {
237        mNode = 0;
238    }
239
240private:
241    sp<Harness> mHarness;
242    IOMX::node_id mNode;
243
244    NodeReaper(const NodeReaper &);
245    NodeReaper &operator=(const NodeReaper &);
246};
247
248static sp<MediaSource> MakeSource(
249        const char *uri,
250        const char *mimeType) {
251    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri);
252
253    if (extractor == NULL) {
254        return NULL;
255    }
256
257    for (size_t i = 0; i < extractor->countTracks(); ++i) {
258        sp<MetaData> meta = extractor->getTrackMetaData(i);
259
260        const char *trackMIME;
261        CHECK(meta->findCString(kKeyMIMEType, &trackMIME));
262
263        if (!strcasecmp(trackMIME, mimeType)) {
264            return extractor->getTrack(i);
265        }
266    }
267
268    return NULL;
269}
270
271status_t Harness::testStateTransitions(
272        const char *componentName, const char *componentRole) {
273    if (strncmp(componentName, "OMX.", 4)) {
274        // Non-OMX components, i.e. software decoders won't execute this
275        // test.
276        return OK;
277    }
278
279    sp<MemoryDealer> dealer = new MemoryDealer(8 * 1024 * 1024);
280    IOMX::node_id node;
281
282    status_t err =
283        mOMX->allocateNode(componentName, this, &node);
284    EXPECT_SUCCESS(err, "allocateNode");
285
286    NodeReaper reaper(this, node);
287
288    err = setRole(node, componentRole);
289    EXPECT_SUCCESS(err, "setRole");
290
291    // Initiate transition Loaded->Idle
292    err = mOMX->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle);
293    EXPECT_SUCCESS(err, "sendCommand(go-to-Idle)");
294
295    omx_message msg;
296    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
297    // Make sure node doesn't just transition to idle before we are done
298    // allocating all input and output buffers.
299    EXPECT(err == TIMED_OUT,
300            "Component must not transition from loaded to idle before "
301            "all input and output buffers are allocated.");
302
303    // Now allocate buffers.
304    Vector<Buffer> inputBuffers;
305    err = allocatePortBuffers(dealer, node, 0, &inputBuffers);
306    EXPECT_SUCCESS(err, "allocatePortBuffers(input)");
307
308    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
309    CHECK_EQ(err, TIMED_OUT);
310
311    Vector<Buffer> outputBuffers;
312    err = allocatePortBuffers(dealer, node, 1, &outputBuffers);
313    EXPECT_SUCCESS(err, "allocatePortBuffers(output)");
314
315    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
316    EXPECT(err == OK
317            && msg.type == omx_message::EVENT
318            && msg.u.event_data.event == OMX_EventCmdComplete
319            && msg.u.event_data.data1 == OMX_CommandStateSet
320            && msg.u.event_data.data2 == OMX_StateIdle,
321           "Component did not properly transition to idle state "
322           "after all input and output buffers were allocated.");
323
324    // Initiate transition Idle->Executing
325    err = mOMX->sendCommand(node, OMX_CommandStateSet, OMX_StateExecuting);
326    EXPECT_SUCCESS(err, "sendCommand(go-to-Executing)");
327
328    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
329    EXPECT(err == OK
330            && msg.type == omx_message::EVENT
331            && msg.u.event_data.event == OMX_EventCmdComplete
332            && msg.u.event_data.data1 == OMX_CommandStateSet
333            && msg.u.event_data.data2 == OMX_StateExecuting,
334           "Component did not properly transition from idle to "
335           "executing state.");
336
337    for (size_t i = 0; i < outputBuffers.size(); ++i) {
338        err = mOMX->fillBuffer(node, outputBuffers[i].mID);
339        EXPECT_SUCCESS(err, "fillBuffer");
340
341        outputBuffers.editItemAt(i).mFlags |= kBufferBusy;
342    }
343
344    err = mOMX->sendCommand(node, OMX_CommandFlush, 1);
345    EXPECT_SUCCESS(err, "sendCommand(flush-output-port)");
346
347    err = dequeueMessageForNodeIgnoringBuffers(
348            node, &inputBuffers, &outputBuffers, &msg, DEFAULT_TIMEOUT);
349    EXPECT(err == OK
350            && msg.type == omx_message::EVENT
351            && msg.u.event_data.event == OMX_EventCmdComplete
352            && msg.u.event_data.data1 == OMX_CommandFlush
353            && msg.u.event_data.data2 == 1,
354           "Component did not properly acknowledge flushing the output port.");
355
356    for (size_t i = 0; i < outputBuffers.size(); ++i) {
357        EXPECT((outputBuffers[i].mFlags & kBufferBusy) == 0,
358               "Not all output buffers have been returned to us by the time "
359               "we received the flush-complete notification.");
360    }
361
362    for (size_t i = 0; i < outputBuffers.size(); ++i) {
363        err = mOMX->fillBuffer(node, outputBuffers[i].mID);
364        EXPECT_SUCCESS(err, "fillBuffer");
365
366        outputBuffers.editItemAt(i).mFlags |= kBufferBusy;
367    }
368
369    // Initiate transition Executing->Idle
370    err = mOMX->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle);
371    EXPECT_SUCCESS(err, "sendCommand(go-to-Idle)");
372
373    err = dequeueMessageForNodeIgnoringBuffers(
374            node, &inputBuffers, &outputBuffers, &msg, DEFAULT_TIMEOUT);
375    EXPECT(err == OK
376            && msg.type == omx_message::EVENT
377            && msg.u.event_data.event == OMX_EventCmdComplete
378            && msg.u.event_data.data1 == OMX_CommandStateSet
379            && msg.u.event_data.data2 == OMX_StateIdle,
380           "Component did not properly transition to from executing to "
381           "idle state.");
382
383    for (size_t i = 0; i < inputBuffers.size(); ++i) {
384        EXPECT((inputBuffers[i].mFlags & kBufferBusy) == 0,
385                "Not all input buffers have been returned to us by the "
386                "time we received the transition-to-idle complete "
387                "notification.");
388    }
389
390    for (size_t i = 0; i < outputBuffers.size(); ++i) {
391        EXPECT((outputBuffers[i].mFlags & kBufferBusy) == 0,
392                "Not all output buffers have been returned to us by the "
393                "time we received the transition-to-idle complete "
394                "notification.");
395    }
396
397    // Initiate transition Idle->Loaded
398    err = mOMX->sendCommand(node, OMX_CommandStateSet, OMX_StateLoaded);
399    EXPECT_SUCCESS(err, "sendCommand(go-to-Loaded)");
400
401    // Make sure node doesn't just transition to loaded before we are done
402    // freeing all input and output buffers.
403    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
404    CHECK_EQ(err, TIMED_OUT);
405
406    for (size_t i = 0; i < inputBuffers.size(); ++i) {
407        err = mOMX->freeBuffer(node, 0, inputBuffers[i].mID);
408        EXPECT_SUCCESS(err, "freeBuffer");
409    }
410
411    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
412    CHECK_EQ(err, TIMED_OUT);
413
414    for (size_t i = 0; i < outputBuffers.size(); ++i) {
415        err = mOMX->freeBuffer(node, 1, outputBuffers[i].mID);
416        EXPECT_SUCCESS(err, "freeBuffer");
417    }
418
419    err = dequeueMessageForNode(node, &msg, DEFAULT_TIMEOUT);
420    EXPECT(err == OK
421            && msg.type == omx_message::EVENT
422            && msg.u.event_data.event == OMX_EventCmdComplete
423            && msg.u.event_data.data1 == OMX_CommandStateSet
424            && msg.u.event_data.data2 == OMX_StateLoaded,
425           "Component did not properly transition to from idle to "
426           "loaded state after freeing all input and output buffers.");
427
428    err = mOMX->freeNode(node);
429    EXPECT_SUCCESS(err, "freeNode");
430
431    reaper.disarm();
432
433    node = 0;
434
435    return OK;
436}
437
438static const char *GetMimeFromComponentRole(const char *componentRole) {
439    struct RoleToMime {
440        const char *mRole;
441        const char *mMime;
442    };
443    const RoleToMime kRoleToMime[] = {
444        { "video_decoder.avc", "video/avc" },
445        { "video_decoder.mpeg4", "video/mp4v-es" },
446        { "video_decoder.h263", "video/3gpp" },
447
448        // we appear to use this as a synonym to amrnb.
449        { "audio_decoder.amr", "audio/3gpp" },
450
451        { "audio_decoder.amrnb", "audio/3gpp" },
452        { "audio_decoder.amrwb", "audio/amr-wb" },
453        { "audio_decoder.aac", "audio/mp4a-latm" },
454        { "audio_decoder.mp3", "audio/mpeg" }
455    };
456
457    for (size_t i = 0; i < sizeof(kRoleToMime) / sizeof(kRoleToMime[0]); ++i) {
458        if (!strcmp(componentRole, kRoleToMime[i].mRole)) {
459            return kRoleToMime[i].mMime;
460        }
461    }
462
463    return NULL;
464}
465
466static const char *GetURLForMime(const char *mime) {
467    struct MimeToURL {
468        const char *mMime;
469        const char *mURL;
470    };
471    static const MimeToURL kMimeToURL[] = {
472        { "video/avc",
473          "file:///sdcard/media_api/video/H264_AAC.3gp" },
474        { "video/mp4v-es", "file:///sdcard/media_api/video/gingerkids.MP4" },
475        { "video/3gpp",
476          "file:///sdcard/media_api/video/H263_500_AMRNB_12.3gp" },
477        { "audio/3gpp",
478          "file:///sdcard/media_api/video/H263_500_AMRNB_12.3gp" },
479        { "audio/amr-wb",
480          "file:///sdcard/media_api/music_perf/AMRWB/"
481          "NIN_AMR-WB_15.85kbps_16kbps.amr" },
482        { "audio/mp4a-latm",
483          "file:///sdcard/media_api/music_perf/AAC/"
484          "WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4" },
485        { "audio/mpeg",
486          "file:///sdcard/media_api/music_perf/MP3/"
487          "WC_256kbps_44.1khz_mono_CBR_DPA.mp3" }
488    };
489
490    for (size_t i = 0; i < sizeof(kMimeToURL) / sizeof(kMimeToURL[0]); ++i) {
491        if (!strcasecmp(kMimeToURL[i].mMime, mime)) {
492            return kMimeToURL[i].mURL;
493        }
494    }
495
496    return NULL;
497}
498
499static sp<MediaSource> CreateSourceForMime(const char *mime) {
500    const char *url = GetURLForMime(mime);
501    CHECK(url != NULL);
502
503    sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(url);
504
505    CHECK(extractor != NULL);
506
507    for (size_t i = 0; i < extractor->countTracks(); ++i) {
508        sp<MetaData> meta = extractor->getTrackMetaData(i);
509        CHECK(meta != NULL);
510
511        const char *trackMime;
512        CHECK(meta->findCString(kKeyMIMEType, &trackMime));
513
514        if (!strcasecmp(mime, trackMime)) {
515            return extractor->getTrack(i);
516        }
517    }
518
519    return NULL;
520}
521
522static double uniform_rand() {
523    return (double)rand() / RAND_MAX;
524}
525
526static bool CloseEnough(int64_t time1Us, int64_t time2Us) {
527#if 0
528    int64_t diff = time1Us - time2Us;
529    if (diff < 0) {
530        diff = -diff;
531    }
532
533    return diff <= 50000;
534#else
535    return time1Us == time2Us;
536#endif
537}
538
539status_t Harness::testSeek(
540        const char *componentName, const char *componentRole) {
541    bool isEncoder =
542        !strncmp(componentRole, "audio_encoder.", 14)
543        || !strncmp(componentRole, "video_encoder.", 14);
544
545    if (isEncoder) {
546        // Not testing seek behaviour for encoders.
547
548        printf("  * Not testing seek functionality for encoders.\n");
549        return OK;
550    }
551
552    const char *mime = GetMimeFromComponentRole(componentRole);
553
554    if (!mime) {
555        LOGI("Cannot perform seek test with this componentRole (%s)",
556             componentRole);
557
558        return OK;
559    }
560
561    sp<MediaSource> source = CreateSourceForMime(mime);
562
563    sp<MediaSource> seekSource = CreateSourceForMime(mime);
564    CHECK_EQ(seekSource->start(), OK);
565
566    sp<MediaSource> codec = OMXCodec::Create(
567            mOMX, source->getFormat(), false /* createEncoder */,
568            source, componentName);
569
570    CHECK(codec != NULL);
571
572    CHECK_EQ(codec->start(), OK);
573
574    int64_t durationUs;
575    CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
576
577    LOGI("stream duration is %lld us (%.2f secs)",
578         durationUs, durationUs / 1E6);
579
580    static const int32_t kNumIterations = 5000;
581
582    for (int32_t i = 0; i < kNumIterations; ++i) {
583        int64_t requestedSeekTimeUs;
584        int64_t actualSeekTimeUs;
585        MediaSource::ReadOptions options;
586
587        double r = uniform_rand();
588
589        if (r < 0.5) {
590            // 50% chance of just continuing to decode from last position.
591
592            requestedSeekTimeUs = -1;
593
594            LOGI("requesting linear read");
595        } else {
596            if (r < 0.55) {
597                // 5% chance of seeking beyond end of stream.
598
599                requestedSeekTimeUs = durationUs;
600
601                LOGI("requesting seek beyond EOF");
602            } else {
603                requestedSeekTimeUs =
604                    (int64_t)(uniform_rand() * durationUs);
605
606                LOGI("requesting seek to %lld us (%.2f secs)",
607                     requestedSeekTimeUs, requestedSeekTimeUs / 1E6);
608            }
609
610            MediaBuffer *buffer;
611            options.setSeekTo(requestedSeekTimeUs);
612            if (seekSource->read(&buffer, &options) != OK) {
613                CHECK_EQ(buffer, NULL);
614                actualSeekTimeUs = -1;
615            } else {
616                CHECK(buffer != NULL);
617                CHECK(buffer->meta_data()->findInt64(kKeyTime, &actualSeekTimeUs));
618                CHECK(actualSeekTimeUs >= 0);
619
620                buffer->release();
621                buffer = NULL;
622            }
623
624            LOGI("nearest keyframe is at %lld us (%.2f secs)",
625                 actualSeekTimeUs, actualSeekTimeUs / 1E6);
626        }
627
628        status_t err;
629        MediaBuffer *buffer;
630        for (;;) {
631            err = codec->read(&buffer, &options);
632            options.clearSeekTo();
633            if (err == INFO_FORMAT_CHANGED) {
634                CHECK_EQ(buffer, NULL);
635                continue;
636            }
637            if (err == OK) {
638                CHECK(buffer != NULL);
639                if (buffer->range_length() == 0) {
640                    buffer->release();
641                    buffer = NULL;
642                    continue;
643                }
644            } else {
645                CHECK_EQ(buffer, NULL);
646            }
647
648            break;
649        }
650
651        if (requestedSeekTimeUs < 0) {
652            // Linear read.
653            if (err != OK) {
654                CHECK_EQ(buffer, NULL);
655            } else {
656                CHECK(buffer != NULL);
657                buffer->release();
658                buffer = NULL;
659            }
660        } else if (actualSeekTimeUs < 0) {
661            CHECK(err != OK);
662            CHECK_EQ(buffer, NULL);
663        } else {
664            EXPECT(err == OK,
665                   "Expected a valid buffer to be returned from "
666                   "OMXCodec::read.");
667            CHECK(buffer != NULL);
668
669            int64_t bufferTimeUs;
670            CHECK(buffer->meta_data()->findInt64(kKeyTime, &bufferTimeUs));
671            if (!CloseEnough(bufferTimeUs, actualSeekTimeUs)) {
672                printf("\n  * Attempted seeking to %lld us (%.2f secs)",
673                       requestedSeekTimeUs, requestedSeekTimeUs / 1E6);
674                printf("\n  * Nearest keyframe is at %lld us (%.2f secs)",
675                       actualSeekTimeUs, actualSeekTimeUs / 1E6);
676                printf("\n  * Returned buffer was at %lld us (%.2f secs)\n\n",
677                       bufferTimeUs, bufferTimeUs / 1E6);
678
679                buffer->release();
680                buffer = NULL;
681
682                CHECK_EQ(codec->stop(), OK);
683
684                return UNKNOWN_ERROR;
685            }
686
687            buffer->release();
688            buffer = NULL;
689        }
690    }
691
692    CHECK_EQ(codec->stop(), OK);
693
694    return OK;
695}
696
697status_t Harness::test(
698        const char *componentName, const char *componentRole) {
699    printf("testing %s [%s] ... ", componentName, componentRole);
700    LOGI("testing %s [%s].", componentName, componentRole);
701
702    status_t err1 = testStateTransitions(componentName, componentRole);
703    status_t err2 = testSeek(componentName, componentRole);
704
705    if (err1 != OK) {
706        return err1;
707    }
708
709    return err2;
710}
711
712status_t Harness::testAll() {
713    List<IOMX::ComponentInfo> componentInfos;
714    status_t err = mOMX->listNodes(&componentInfos);
715    EXPECT_SUCCESS(err, "listNodes");
716
717    for (List<IOMX::ComponentInfo>::iterator it = componentInfos.begin();
718         it != componentInfos.end(); ++it) {
719        const IOMX::ComponentInfo &info = *it;
720        const char *componentName = info.mName.string();
721
722        for (List<String8>::const_iterator role_it = info.mRoles.begin();
723             role_it != info.mRoles.end(); ++role_it) {
724            const char *componentRole = (*role_it).string();
725
726            err = test(componentName, componentRole);
727
728            if (err == OK) {
729                printf("OK\n");
730            }
731        }
732    }
733
734    return OK;
735}
736
737}  // namespace android
738
739static void usage(const char *me) {
740    fprintf(stderr, "usage: %s\n"
741                    "  -h(elp)  Show this information\n"
742                    "  -s(eed)  Set the random seed\n"
743                    "    [ component role ]\n\n"
744                    "When launched without specifying a specific component "
745                    "and role, tool will test all available OMX components "
746                    "in all their supported roles. To determine available "
747                    "component names, use \"stagefright -l\"\n"
748                    "It's also a good idea to run a separate \"adb logcat\""
749                    " for additional debug and progress information.", me);
750
751    exit(0);
752}
753
754int main(int argc, char **argv) {
755    using namespace android;
756
757    android::ProcessState::self()->startThreadPool();
758    DataSource::RegisterDefaultSniffers();
759
760    const char *me = argv[0];
761
762    unsigned long seed = 0xdeadbeef;
763
764    int res;
765    while ((res = getopt(argc, argv, "hs:")) >= 0) {
766        switch (res) {
767            case 's':
768            {
769                char *end;
770                unsigned long x = strtoul(optarg, &end, 10);
771
772                if (*end != '\0' || end == optarg) {
773                    fprintf(stderr, "Malformed seed.\n");
774                    return 1;
775                }
776
777                seed = x;
778                break;
779            }
780
781            case '?':
782                fprintf(stderr, "\n");
783                // fall through
784
785            case 'h':
786            default:
787            {
788                usage(me);
789                exit(1);
790                break;
791            }
792        }
793    }
794
795    argc -= optind;
796    argv += optind;
797
798    printf("To reproduce the conditions for this test, launch "
799           "with \"%s -s %lu\"\n", me, seed);
800
801    srand(seed);
802
803    sp<Harness> h = new Harness;
804    CHECK_EQ(h->initCheck(), OK);
805
806    if (argc == 0) {
807        h->testAll();
808    } else if (argc == 2) {
809        if (h->test(argv[0], argv[1]) == OK) {
810            printf("OK\n");
811        }
812    }
813
814    return 0;
815}
816