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