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