• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 "media_omx_hidl_video_enc_test"
18 #ifdef __LP64__
19 #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
20 #endif
21 
22 #include <android-base/logging.h>
23 
24 #include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
25 #include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
26 #include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
27 #include <android/hardware/media/omx/1.0/IOmx.h>
28 #include <android/hardware/media/omx/1.0/IOmxBufferSource.h>
29 #include <android/hardware/media/omx/1.0/IOmxNode.h>
30 #include <android/hardware/media/omx/1.0/IOmxObserver.h>
31 #include <android/hardware/media/omx/1.0/types.h>
32 #include <android/hidl/allocator/1.0/IAllocator.h>
33 #include <android/hidl/memory/1.0/IMapper.h>
34 #include <android/hidl/memory/1.0/IMemory.h>
35 #include <gtest/gtest.h>
36 #include <hidl/GtestPrinter.h>
37 
38 using ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer;
39 using ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener;
40 using ::android::hardware::graphics::common::V1_0::BufferUsage;
41 using ::android::hardware::graphics::common::V1_0::PixelFormat;
42 using ::android::hardware::media::omx::V1_0::IGraphicBufferSource;
43 using ::android::hardware::media::omx::V1_0::IOmxBufferSource;
44 using ::android::hardware::media::omx::V1_0::IOmx;
45 using ::android::hardware::media::omx::V1_0::IOmxObserver;
46 using ::android::hardware::media::omx::V1_0::IOmxNode;
47 using ::android::hardware::media::omx::V1_0::Message;
48 using ::android::hardware::media::omx::V1_0::CodecBuffer;
49 using ::android::hardware::media::omx::V1_0::PortMode;
50 using ::android::hidl::allocator::V1_0::IAllocator;
51 using ::android::hidl::memory::V1_0::IMemory;
52 using ::android::hidl::memory::V1_0::IMapper;
53 using ::android::hardware::Return;
54 using ::android::hardware::Void;
55 using ::android::hardware::hidl_vec;
56 using ::android::hardware::hidl_string;
57 using ::android::sp;
58 
59 #include <getopt.h>
60 #include <media/hardware/HardwareAPI.h>
61 #include <media_video_hidl_test_common.h>
62 #include <system/window.h>
63 #include <fstream>
64 #include <variant>
65 
66 // Resource directory
67 std::string sResourceDir = "";
68 
69 // video encoder test fixture class
70 class VideoEncHidlTest
71     : public ::testing::TestWithParam<std::tuple<std::string, std::string, std::string>> {
72   public:
getTestCaseInfo() const73     ::std::string getTestCaseInfo() const {
74         return ::std::string() + "Component: " + component_ + " | " + "Role: " + role_ + " | " +
75                "Instance: " + instance_ + " | " + "Res: " + sResourceDir;
76     }
77 
SetUp()78     virtual void SetUp() override {
79         instance_ = std::get<0>(GetParam());
80         component_ = std::get<1>(GetParam());
81         role_ = std::get<2>(GetParam());
82         ASSERT_NE(sResourceDir.empty(), true);
83 
84         disableTest = false;
85         android::hardware::media::omx::V1_0::Status status;
86         omx = IOmx::getService(instance_);
87         ASSERT_NE(omx, nullptr);
88         observer =
89             new CodecObserver([this](Message msg, const BufferInfo* buffer) {
90                 handleMessage(msg, buffer);
91             });
92         ASSERT_NE(observer, nullptr);
93         if (component_.find("OMX.") != 0) disableTest = true;
94         EXPECT_TRUE(omx->allocateNode(component_, observer,
95                                       [&](android::hardware::media::omx::V1_0::Status _s,
96                                           sp<IOmxNode> const& _nl) {
97                                           status = _s;
98                                           this->omxNode = _nl;
99                                       })
100                             .isOk());
101         if (status == android::hardware::media::omx::V1_0::Status::NAME_NOT_FOUND) {
102             disableTest = true;
103             std::cout << "[   WARN   ] Test Disabled, component not present\n";
104             return;
105         }
106         ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
107         ASSERT_NE(omxNode, nullptr);
108         ASSERT_NE(role_.empty(), true) << "Invalid Component Role";
109         struct StringToName {
110             const char* Name;
111             standardComp CompName;
112         };
113         const StringToName kStringToName[] = {
114             {"h263", h263}, {"avc", avc}, {"mpeg4", mpeg4},
115             {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9},
116         };
117         const size_t kNumStringToName =
118             sizeof(kStringToName) / sizeof(kStringToName[0]);
119         const char* pch;
120         char substring[OMX_MAX_STRINGNAME_SIZE];
121         strcpy(substring, role_.c_str());
122         pch = strchr(substring, '.');
123         ASSERT_NE(pch, nullptr);
124         compName = unknown_comp;
125         for (size_t i = 0; i < kNumStringToName; ++i) {
126             if (!strcasecmp(pch + 1, kStringToName[i].Name)) {
127                 compName = kStringToName[i].CompName;
128                 break;
129             }
130         }
131         if (compName == unknown_comp) disableTest = true;
132         struct CompToCompression {
133             standardComp CompName;
134             OMX_VIDEO_CODINGTYPE eCompressionFormat;
135         };
136         static const CompToCompression kCompToCompression[] = {
137             {h263, OMX_VIDEO_CodingH263},   {avc, OMX_VIDEO_CodingAVC},
138             {mpeg4, OMX_VIDEO_CodingMPEG4}, {hevc, OMX_VIDEO_CodingHEVC},
139             {vp8, OMX_VIDEO_CodingVP8},     {vp9, OMX_VIDEO_CodingVP9},
140         };
141         static const size_t kNumCompToCompression =
142             sizeof(kCompToCompression) / sizeof(kCompToCompression[0]);
143         size_t i;
144         for (i = 0; i < kNumCompToCompression; ++i) {
145             if (kCompToCompression[i].CompName == compName) {
146                 eCompressionFormat = kCompToCompression[i].eCompressionFormat;
147                 break;
148             }
149         }
150         if (i == kNumCompToCompression) disableTest = true;
151         eosFlag = false;
152         prependSPSPPS = false;
153         timestampDevTest = false;
154         producer = nullptr;
155         source = nullptr;
156         isSecure = false;
157         size_t suffixLen = strlen(".secure");
158         if (component_.rfind(".secure") == component_.length() - suffixLen) {
159             isSecure = true;
160         }
161         if (isSecure) disableTest = true;
162         if (disableTest) std::cout << "[   WARN   ] Test Disabled \n";
163     }
164 
TearDown()165     virtual void TearDown() override {
166         if (omxNode != nullptr) {
167             // If you have encountered a fatal failure, it is possible that
168             // freeNode() will not go through. Instead of hanging the app.
169             // let it pass through and report errors
170             if (::testing::Test::HasFatalFailure()) return;
171             EXPECT_TRUE((omxNode->freeNode()).isOk());
172             omxNode = nullptr;
173         }
174     }
175 
176     // callback function to process messages received by onMessages() from IL
177     // client.
handleMessage(Message msg,const BufferInfo * buffer)178     void handleMessage(Message msg, const BufferInfo* buffer) {
179         (void)buffer;
180 
181         if (msg.type == Message::Type::FILL_BUFFER_DONE) {
182             if (msg.data.extendedBufferData.flags & OMX_BUFFERFLAG_EOS) {
183                 eosFlag = true;
184             }
185             if (msg.data.extendedBufferData.rangeLength != 0) {
186                 // Test if current timestamp is among the list of queued
187                 // timestamps
188                 if (timestampDevTest && ((msg.data.extendedBufferData.flags &
189                                           OMX_BUFFERFLAG_CODECCONFIG) == 0)) {
190                     bool tsHit = false;
191                     android::List<uint64_t>::iterator it =
192                         timestampUslist.begin();
193                     while (it != timestampUslist.end()) {
194                         if (*it == msg.data.extendedBufferData.timestampUs) {
195                             timestampUslist.erase(it);
196                             tsHit = true;
197                             break;
198                         }
199                         it++;
200                     }
201                     if (tsHit == false) {
202                         if (timestampUslist.empty() == false) {
203                             EXPECT_EQ(tsHit, true)
204                                 << "TimeStamp not recognized";
205                         } else {
206                             std::cout << "[   INFO   ] Received non-zero "
207                                          "output / TimeStamp not recognized \n";
208                         }
209                     }
210                 }
211 #define WRITE_OUTPUT 0
212 #if WRITE_OUTPUT
213                 static int count = 0;
214                 FILE* ofp = nullptr;
215                 if (count)
216                     ofp = fopen("out.bin", "ab");
217                 else
218                     ofp = fopen("out.bin", "wb");
219                 if (ofp != nullptr) {
220                     fwrite(static_cast<void*>(buffer->mMemory->getPointer()),
221                            sizeof(char),
222                            msg.data.extendedBufferData.rangeLength, ofp);
223                     fclose(ofp);
224                     count++;
225                 }
226 #endif
227             }
228         }
229     }
230 
231     enum standardComp {
232         h263,
233         avc,
234         mpeg4,
235         hevc,
236         vp8,
237         vp9,
238         unknown_comp,
239     };
240 
241     std::string component_;
242     std::string role_;
243     std::string instance_;
244 
245     sp<IOmx> omx;
246     sp<CodecObserver> observer;
247     sp<IOmxNode> omxNode;
248     standardComp compName;
249     OMX_VIDEO_CODINGTYPE eCompressionFormat;
250     bool disableTest;
251     bool eosFlag;
252     bool prependSPSPPS;
253     ::android::List<uint64_t> timestampUslist;
254     bool timestampDevTest;
255     bool isSecure;
256     sp<IGraphicBufferProducer> producer;
257     sp<IGraphicBufferSource> source;
258 
259    protected:
description(const std::string & description)260     static void description(const std::string& description) {
261         RecordProperty("description", description);
262     }
263 };
264 
265 // CodecProducerListener class
266 struct CodecProducerListener : public IProducerListener {
267    public:
CodecProducerListenerCodecProducerListener268     CodecProducerListener(int a, int b)
269         : freeBuffers(a), minUnDequeuedCount(b) {}
onBufferReleasedCodecProducerListener270     virtual ::android::hardware::Return<void> onBufferReleased() override {
271         android::Mutex::Autolock autoLock(bufferLock);
272         freeBuffers += 1;
273         return Void();
274     }
needsReleaseNotifyCodecProducerListener275     virtual ::android::hardware::Return<bool> needsReleaseNotify() override {
276         return true;
277     }
reduceCountCodecProducerListener278     void reduceCount() {
279         android::Mutex::Autolock autoLock(bufferLock);
280         freeBuffers -= 1;
281         EXPECT_GE(freeBuffers, minUnDequeuedCount);
282     }
283 
284     size_t freeBuffers;
285     size_t minUnDequeuedCount;
286     android::Mutex bufferLock;
287 };
288 
289 // Mock IOmxBufferSource class. GraphicBufferSource.cpp in libstagefright/omx/
290 // implements this class. Below class is introduced to test if callback
291 // functions are actually being called or not
292 struct MockBufferSource : public IOmxBufferSource {
293    public:
MockBufferSourceMockBufferSource294     MockBufferSource(sp<IOmxNode> node) {
295         callback = 0;
296         executing = false;
297         omxNode = node;
298     }
299     virtual Return<void> onOmxExecuting();
300     virtual Return<void> onOmxIdle();
301     virtual Return<void> onOmxLoaded();
302     virtual Return<void> onInputBufferAdded(uint32_t buffer);
303     virtual Return<void> onInputBufferEmptied(
304         uint32_t buffer, const ::android::hardware::hidl_handle& fence);
305 
306     int callback;
307     bool executing;
308     sp<IOmxNode> omxNode;
309     android::Vector<BufferInfo> iBuffer, oBuffer;
310 };
311 
onOmxExecuting()312 Return<void> MockBufferSource::onOmxExecuting() {
313     executing = true;
314     callback |= 0x1;
315     size_t index;
316     // Fetch a client owned input buffer and send an EOS
317     if ((index = getEmptyBufferID(&iBuffer)) < iBuffer.size()) {
318         android::hardware::media::omx::V1_0::Status status;
319         CodecBuffer t = iBuffer[index].omxBuffer;
320         t.type = CodecBuffer::Type::ANW_BUFFER;
321         native_handle_t* fenceNh = native_handle_create(0, 0);
322         EXPECT_NE(fenceNh, nullptr);
323         status = omxNode->emptyBuffer(iBuffer[index].id, t, OMX_BUFFERFLAG_EOS,
324                                       0, fenceNh);
325         native_handle_close(fenceNh);
326         native_handle_delete(fenceNh);
327         EXPECT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
328         iBuffer.editItemAt(index).owner = component;
329     }
330     return Void();
331 };
332 
onOmxIdle()333 Return<void> MockBufferSource::onOmxIdle() {
334     callback |= 0x2;
335     executing = false;
336     return Void();
337 };
338 
onOmxLoaded()339 Return<void> MockBufferSource::onOmxLoaded() {
340     callback |= 0x4;
341     return Void();
342 };
343 
onInputBufferAdded(uint32_t buffer)344 Return<void> MockBufferSource::onInputBufferAdded(uint32_t buffer) {
345     (void)buffer;
346     EXPECT_EQ(executing, false);
347     callback |= 0x8;
348     return Void();
349 };
350 
onInputBufferEmptied(uint32_t buffer,const::android::hardware::hidl_handle & fence)351 Return<void> MockBufferSource::onInputBufferEmptied(
352     uint32_t buffer, const ::android::hardware::hidl_handle& fence) {
353     (void)fence;
354     callback |= 0x10;
355     size_t i;
356     for (i = 0; i < iBuffer.size(); i++) {
357         if (iBuffer[i].id == buffer) {
358             iBuffer.editItemAt(i).owner = client;
359             break;
360         }
361     }
362     return Void();
363 };
364 
365 // request VOP refresh
requestIDR(sp<IOmxNode> omxNode,OMX_U32 portIndex)366 void requestIDR(sp<IOmxNode> omxNode, OMX_U32 portIndex) {
367     android::hardware::media::omx::V1_0::Status status;
368     OMX_CONFIG_INTRAREFRESHVOPTYPE param;
369     param.IntraRefreshVOP = OMX_TRUE;
370     status = setPortConfig(omxNode, OMX_IndexConfigVideoIntraVOPRefresh,
371                            portIndex, &param);
372     if (status != ::android::hardware::media::omx::V1_0::Status::OK)
373         std::cout << "[   INFO   ] unable to request IDR \n";
374 }
375 
376 // modify bitrate
changeBitrate(sp<IOmxNode> omxNode,OMX_U32 portIndex,uint32_t nBitrate)377 void changeBitrate(sp<IOmxNode> omxNode, OMX_U32 portIndex, uint32_t nBitrate) {
378     android::hardware::media::omx::V1_0::Status status;
379     OMX_VIDEO_CONFIG_BITRATETYPE param;
380     param.nEncodeBitrate = nBitrate;
381     status =
382         setPortConfig(omxNode, OMX_IndexConfigVideoBitrate, portIndex, &param);
383     if (status != ::android::hardware::media::omx::V1_0::Status::OK)
384         std::cout << "[   INFO   ] unable to change Bitrate \n";
385 }
386 
387 // modify framerate
changeFrameRate(sp<IOmxNode> omxNode,OMX_U32 portIndex,uint32_t xFramerate)388 Return<android::hardware::media::omx::V1_0::Status> changeFrameRate(
389     sp<IOmxNode> omxNode, OMX_U32 portIndex, uint32_t xFramerate) {
390     android::hardware::media::omx::V1_0::Status status;
391     OMX_CONFIG_FRAMERATETYPE param;
392     param.xEncodeFramerate = xFramerate;
393     status = setPortConfig(omxNode, OMX_IndexConfigVideoFramerate, portIndex,
394                            &param);
395     if (status != ::android::hardware::media::omx::V1_0::Status::OK)
396         std::cout << "[   INFO   ] unable to change Framerate \n";
397     return status;
398 }
399 
400 // modify intra refresh interval
changeRefreshPeriod(sp<IOmxNode> omxNode,OMX_U32 portIndex,uint32_t nRefreshPeriod)401 void changeRefreshPeriod(sp<IOmxNode> omxNode, OMX_U32 portIndex,
402                          uint32_t nRefreshPeriod) {
403     android::hardware::media::omx::V1_0::Status status;
404     OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE param;
405     param.nRefreshPeriod = nRefreshPeriod;
406     status = setPortConfig(omxNode,
407                            (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
408                            portIndex, &param);
409     if (status != ::android::hardware::media::omx::V1_0::Status::OK)
410         std::cout << "[   INFO   ] unable to change Refresh Period\n";
411 }
412 
413 // set intra refresh interval
setRefreshPeriod(sp<IOmxNode> omxNode,OMX_U32 portIndex,uint32_t nRefreshPeriod)414 void setRefreshPeriod(sp<IOmxNode> omxNode, OMX_U32 portIndex,
415                       uint32_t nRefreshPeriod) {
416     android::hardware::media::omx::V1_0::Status status;
417     OMX_VIDEO_PARAM_INTRAREFRESHTYPE param;
418     param.eRefreshMode = OMX_VIDEO_IntraRefreshCyclic;
419     param.nCirMBs = 0;
420     if (nRefreshPeriod == 0)
421         param.nCirMBs = 0;
422     else {
423         OMX_PARAM_PORTDEFINITIONTYPE portDef;
424         status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex,
425                               &portDef);
426         if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
427             param.nCirMBs =
428                 ((portDef.format.video.nFrameWidth + 15) >>
429                  4 * (portDef.format.video.nFrameHeight + 15) >> 4) /
430                 nRefreshPeriod;
431         }
432     }
433     status = setPortParam(omxNode, OMX_IndexParamVideoIntraRefresh, portIndex,
434                           &param);
435     if (status != ::android::hardware::media::omx::V1_0::Status::OK)
436         std::cout << "[   INFO   ] unable to set Refresh Period \n";
437 }
438 
setLatency(sp<IOmxNode> omxNode,OMX_U32 portIndex,uint32_t latency)439 void setLatency(sp<IOmxNode> omxNode, OMX_U32 portIndex, uint32_t latency) {
440     android::hardware::media::omx::V1_0::Status status;
441     OMX_PARAM_U32TYPE param;
442     param.nU32 = (OMX_U32)latency;
443     status = setPortConfig(omxNode, (OMX_INDEXTYPE)OMX_IndexConfigLatency,
444                            portIndex, &param);
445     if (status != ::android::hardware::media::omx::V1_0::Status::OK)
446         std::cout << "[   INFO   ] unable to set latency\n";
447 }
448 
getLatency(sp<IOmxNode> omxNode,OMX_U32 portIndex,uint32_t * latency)449 void getLatency(sp<IOmxNode> omxNode, OMX_U32 portIndex, uint32_t* latency) {
450     android::hardware::media::omx::V1_0::Status status;
451     OMX_PARAM_U32TYPE param;
452     status = getPortConfig(omxNode, (OMX_INDEXTYPE)OMX_IndexConfigLatency,
453                            portIndex, &param);
454     if (status != ::android::hardware::media::omx::V1_0::Status::OK)
455         std::cout << "[   INFO   ] unable to get latency\n";
456     else
457         *latency = param.nU32;
458 }
459 
460 // Set Default port param.
setDefaultPortParam(sp<IOmxNode> omxNode,OMX_U32 portIndex,OMX_VIDEO_CODINGTYPE eCompressionFormat,OMX_U32 nFrameWidth,OMX_U32 nFrameHeight,OMX_U32 nBitrate,OMX_U32 xFramerate)461 void setDefaultPortParam(sp<IOmxNode> omxNode, OMX_U32 portIndex,
462                          OMX_VIDEO_CODINGTYPE eCompressionFormat,
463                          OMX_U32 nFrameWidth, OMX_U32 nFrameHeight,
464                          OMX_U32 nBitrate, OMX_U32 xFramerate) {
465     android::hardware::media::omx::V1_0::Status status;
466     OMX_PARAM_PORTDEFINITIONTYPE portDef;
467     status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex,
468                           &portDef);
469     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
470     portDef.format.video.nFrameWidth = nFrameWidth;
471     portDef.format.video.nFrameHeight = nFrameHeight;
472     portDef.format.video.nBitrate = nBitrate;
473     portDef.format.video.xFramerate = xFramerate;
474     portDef.format.video.bFlagErrorConcealment = OMX_TRUE;
475     portDef.format.video.eCompressionFormat = eCompressionFormat;
476     portDef.format.video.eColorFormat = OMX_COLOR_FormatUnused;
477     status = setPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex,
478                           &portDef);
479     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
480 
481     std::vector<int32_t> arrProfile;
482     std::vector<int32_t> arrLevel;
483     enumerateProfileAndLevel(omxNode, portIndex, &arrProfile, &arrLevel);
484     if (arrProfile.empty() == true || arrLevel.empty() == true)
485         ASSERT_TRUE(false);
486     int32_t profile = arrProfile[0];
487     int32_t level = arrLevel[0];
488 
489     switch ((int)eCompressionFormat) {
490         case OMX_VIDEO_CodingAVC:
491             setupAVCPort(omxNode, portIndex,
492                          static_cast<OMX_VIDEO_AVCPROFILETYPE>(profile),
493                          static_cast<OMX_VIDEO_AVCLEVELTYPE>(level),
494                          xFramerate);
495             break;
496         case OMX_VIDEO_CodingHEVC:
497             setupHEVCPort(omxNode, portIndex,
498                           static_cast<OMX_VIDEO_HEVCPROFILETYPE>(profile),
499                           static_cast<OMX_VIDEO_HEVCLEVELTYPE>(level));
500             break;
501         case OMX_VIDEO_CodingH263:
502             setupH263Port(omxNode, portIndex,
503                           static_cast<OMX_VIDEO_H263PROFILETYPE>(profile),
504                           static_cast<OMX_VIDEO_H263LEVELTYPE>(level),
505                           xFramerate);
506             break;
507         case OMX_VIDEO_CodingMPEG4:
508             setupMPEG4Port(omxNode, portIndex,
509                            static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profile),
510                            static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(level),
511                            xFramerate);
512             break;
513         case OMX_VIDEO_CodingVP8:
514             setupVPXPort(omxNode, portIndex, xFramerate);
515             setupVP8Port(omxNode, portIndex,
516                          static_cast<OMX_VIDEO_VP8PROFILETYPE>(profile),
517                          static_cast<OMX_VIDEO_VP8LEVELTYPE>(level));
518             break;
519         case OMX_VIDEO_CodingVP9:
520             setupVPXPort(omxNode, portIndex, xFramerate);
521             setupVP9Port(omxNode, portIndex,
522                          static_cast<OMX_VIDEO_VP9PROFILETYPE>(profile),
523                          static_cast<OMX_VIDEO_VP9LEVELTYPE>(level));
524             break;
525         default:
526             break;
527     }
528 }
529 
530 // LookUpTable of clips and metadata for component testing
GetURLForComponent(char * URL)531 void GetURLForComponent(char* URL) {
532     strcat(URL, "bbb_352x288_420p_30fps_32frames.yuv");
533 }
534 
535 // blocking call to ensures application to Wait till all the inputs are consumed
waitOnInputConsumption(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer,bool inputDataIsMeta=false,sp<CodecProducerListener> listener=nullptr)536 void waitOnInputConsumption(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
537                             android::Vector<BufferInfo>* iBuffer,
538                             android::Vector<BufferInfo>* oBuffer,
539                             bool inputDataIsMeta = false,
540                             sp<CodecProducerListener> listener = nullptr) {
541     android::hardware::media::omx::V1_0::Status status;
542     Message msg;
543     int timeOut = TIMEOUT_COUNTER_Q;
544 
545     while (timeOut--) {
546         size_t i = 0;
547         status =
548             observer->dequeueMessage(&msg, DEFAULT_TIMEOUT_Q, iBuffer, oBuffer);
549         ASSERT_EQ(status,
550                   android::hardware::media::omx::V1_0::Status::TIMED_OUT);
551         // status == TIMED_OUT, it could be due to process time being large
552         // than DEFAULT_TIMEOUT or component needs output buffers to start
553         // processing.
554         if (inputDataIsMeta) {
555             if (listener->freeBuffers == iBuffer->size()) break;
556         } else {
557             for (; i < iBuffer->size(); i++) {
558                 if ((*iBuffer)[i].owner != client) break;
559             }
560             if (i == iBuffer->size()) break;
561         }
562 
563         // Dispatch an output buffer assuming outQueue.empty() is true
564         size_t index;
565         if ((index = getEmptyBufferID(oBuffer)) < oBuffer->size()) {
566             ASSERT_NO_FATAL_FAILURE(
567                 dispatchOutputBuffer(omxNode, oBuffer, index));
568             timeOut = TIMEOUT_COUNTER_Q;
569         }
570     }
571 }
572 
colorFormatConversion(BufferInfo * buffer,buffer_handle_t buff,PixelFormat format,std::ifstream & eleStream)573 int colorFormatConversion(BufferInfo* buffer, buffer_handle_t buff, PixelFormat format,
574                           std::ifstream& eleStream) {
575     android::GraphicBufferMapper& gbmapper = android::GraphicBufferMapper::get();
576 
577     android::Rect rect(0, 0, buffer->omxBuffer.attr.anwBuffer.width,
578                        buffer->omxBuffer.attr.anwBuffer.height);
579     android_ycbcr ycbcrLayout;
580     android::status_t error = android::NO_ERROR;
581 
582     if (format == PixelFormat::YV12 || format == PixelFormat::YCRCB_420_SP ||
583         format == PixelFormat::YCBCR_420_888) {
584         error = gbmapper.lockYCbCr(buff, buffer->omxBuffer.attr.anwBuffer.usage, rect,
585                                    &ycbcrLayout);
586         EXPECT_EQ(error, android::NO_ERROR);
587         if (error != android::NO_ERROR) return 1;
588 
589         int size = ((rect.getWidth() * rect.getHeight() * 3) >> 1);
590         char* img = new char[size];
591         if (img == nullptr) return 1;
592         eleStream.read(img, size);
593         if (eleStream.gcount() != size) {
594             delete[] img;
595             return 1;
596         }
597 
598         char* imgTmp = img;
599         char* ipBuffer = static_cast<char*>(ycbcrLayout.y);
600         for (size_t y = rect.getHeight(); y > 0; --y) {
601             memcpy(ipBuffer, imgTmp, rect.getWidth());
602             ipBuffer += ycbcrLayout.ystride;
603             imgTmp += rect.getWidth();
604         }
605 
606         if (format == PixelFormat::YV12)
607             EXPECT_EQ(ycbcrLayout.chroma_step, 1U);
608         else if (format == PixelFormat::YCRCB_420_SP)
609             EXPECT_EQ(ycbcrLayout.chroma_step, 2U);
610 
611         ipBuffer = static_cast<char*>(ycbcrLayout.cb);
612         for (size_t y = rect.getHeight() >> 1; y > 0; --y) {
613             for (int32_t x = 0; x < (rect.getWidth() >> 1); ++x) {
614                 ipBuffer[ycbcrLayout.chroma_step * x] = *imgTmp++;
615             }
616             ipBuffer += ycbcrLayout.cstride;
617         }
618         ipBuffer = static_cast<char*>(ycbcrLayout.cr);
619         for (size_t y = rect.getHeight() >> 1; y > 0; --y) {
620             for (int32_t x = 0; x < (rect.getWidth() >> 1); ++x) {
621                 ipBuffer[ycbcrLayout.chroma_step * x] = *imgTmp++;
622             }
623             ipBuffer += ycbcrLayout.cstride;
624         }
625 
626         delete[] img;
627 
628         error = gbmapper.unlock(buff);
629         EXPECT_EQ(error, android::NO_ERROR);
630         if (error != android::NO_ERROR) return 1;
631     } else {
632         void* data;
633         error = gbmapper.lock(buff, buffer->omxBuffer.attr.anwBuffer.usage, rect, &data);
634         EXPECT_EQ(error, android::NO_ERROR);
635         if (error != android::NO_ERROR) return 1;
636 
637         if (format == PixelFormat::BGRA_8888) {
638             char* ipBuffer = static_cast<char*>(data);
639             for (size_t y = rect.getHeight(); y > 0; --y) {
640                 eleStream.read(ipBuffer, rect.getWidth() * 4);
641                 if (eleStream.gcount() != rect.getWidth() * 4) return 1;
642                 ipBuffer += buffer->omxBuffer.attr.anwBuffer.stride * 4;
643             }
644         } else {
645             EXPECT_TRUE(false) << "un expected pixel format";
646             return 1;
647         }
648 
649         error = gbmapper.unlock(buff);
650         EXPECT_EQ(error, android::NO_ERROR);
651         if (error != android::NO_ERROR) return 1;
652     }
653 
654     return 0;
655 }
656 
fillGraphicBuffer(BufferInfo * buffer,PixelFormat format,std::ifstream & eleStream)657 int fillGraphicBuffer(BufferInfo* buffer, PixelFormat format,
658                       std::ifstream& eleStream) {
659     android::GraphicBufferMapper& gbmapper = android::GraphicBufferMapper::get();
660     buffer_handle_t buff;
661     android::status_t error = android::NO_ERROR;
662     gbmapper.importBuffer(
663             buffer->omxBuffer.nativeHandle, buffer->omxBuffer.attr.anwBuffer.width,
664             buffer->omxBuffer.attr.anwBuffer.height, buffer->omxBuffer.attr.anwBuffer.layerCount,
665             static_cast<android::PixelFormat>(format), buffer->omxBuffer.attr.anwBuffer.usage,
666             buffer->omxBuffer.attr.anwBuffer.stride, &buff);
667     EXPECT_EQ(error, android::NO_ERROR);
668     if (error != android::NO_ERROR) return 1;
669 
670     if (colorFormatConversion(buffer, buff, format, eleStream)) return 1;
671 
672     error = gbmapper.freeBuffer(buff);
673     EXPECT_EQ(error, android::NO_ERROR);
674     if (error != android::NO_ERROR) return 1;
675 
676     return 0;
677 }
678 
dispatchGraphicBuffer(sp<IOmxNode> omxNode,sp<IGraphicBufferProducer> producer,sp<CodecProducerListener> listener,android::Vector<BufferInfo> * buffArray,OMX_U32 portIndex,std::ifstream & eleStream,uint64_t timestamp)679 int dispatchGraphicBuffer(sp<IOmxNode> omxNode,
680                           sp<IGraphicBufferProducer> producer,
681                           sp<CodecProducerListener> listener,
682                           android::Vector<BufferInfo>* buffArray,
683                           OMX_U32 portIndex, std::ifstream& eleStream,
684                           uint64_t timestamp) {
685     android::hardware::media::omx::V1_0::Status status;
686     OMX_PARAM_PORTDEFINITIONTYPE portDef;
687 
688     status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex,
689                           &portDef);
690     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
691     if (status != ::android::hardware::media::omx::V1_0::Status::OK) return 1;
692 
693     enum {
694         // A flag returned by dequeueBuffer when the client needs to call
695         // requestBuffer immediately thereafter.
696         BUFFER_NEEDS_REALLOCATION = 0x1,
697         // A flag returned by dequeueBuffer when all mirrored slots should be
698         // released by the client. This flag should always be processed first.
699         RELEASE_ALL_BUFFERS = 0x2,
700     };
701 
702     int32_t slot;
703     int32_t result;
704     ::android::hardware::hidl_handle fence;
705     IGraphicBufferProducer::FrameEventHistoryDelta outTimestamps;
706     ::android::hardware::media::V1_0::AnwBuffer AnwBuffer;
707     PixelFormat format = PixelFormat::YCBCR_420_888;
708     producer->dequeueBuffer(
709         portDef.format.video.nFrameWidth, portDef.format.video.nFrameHeight,
710         format, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
711         true, [&](int32_t _s, int32_t const& _n1,
712                   ::android::hardware::hidl_handle const& _n2,
713                   IGraphicBufferProducer::FrameEventHistoryDelta const& _n3) {
714             result = _s;
715             slot = _n1;
716             fence = _n2;
717             outTimestamps = _n3;
718         });
719     if (result & BUFFER_NEEDS_REALLOCATION) {
720         producer->requestBuffer(
721             slot, [&](int32_t _s,
722                       ::android::hardware::media::V1_0::AnwBuffer const& _n1) {
723                 result = _s;
724                 AnwBuffer = _n1;
725             });
726         EXPECT_EQ(result, 0);
727         if (result != 0) return 1;
728         size_t i;
729         for (i = 0; i < buffArray->size(); i++) {
730             if ((*buffArray)[i].slot == -1) {
731                 buffArray->editItemAt(i).slot = slot;
732                 buffArray->editItemAt(i).omxBuffer.nativeHandle =
733                     AnwBuffer.nativeHandle;
734                 buffArray->editItemAt(i).omxBuffer.attr.anwBuffer =
735                     AnwBuffer.attr;
736                 break;
737             }
738         }
739         EXPECT_NE(i, buffArray->size());
740         if (i == buffArray->size()) return 1;
741     }
742     EXPECT_EQ(result, 0);
743     if (result != 0) return 1;
744 
745     // fill Buffer
746     BufferInfo buffer;
747     size_t i;
748     for (i = 0; i < buffArray->size(); i++) {
749         if ((*buffArray)[i].slot == slot) {
750             buffer = (*buffArray)[i];
751             break;
752         }
753     }
754     EXPECT_NE(i, buffArray->size());
755     if (i == buffArray->size()) return 1;
756     if (fillGraphicBuffer(&buffer, format, eleStream)) return 1;
757 
758     // queue Buffer
759     IGraphicBufferProducer::QueueBufferOutput output;
760     IGraphicBufferProducer::QueueBufferInput input;
761     android::hardware::media::V1_0::Rect rect;
762     rect.left = 0;
763     rect.top = 0;
764     rect.right = buffer.omxBuffer.attr.anwBuffer.width;
765     rect.bottom = buffer.omxBuffer.attr.anwBuffer.height;
766     input.timestamp = timestamp;
767     input.isAutoTimestamp = false;
768     input.dataSpace =
769         android::hardware::graphics::common::V1_0::Dataspace::UNKNOWN;
770     input.crop = rect;
771     input.scalingMode = 0;
772     input.transform = 0;
773     input.stickyTransform = 0;
774     input.fence = android::hardware::hidl_handle();
775     input.surfaceDamage =
776         android::hardware::hidl_vec<android::hardware::media::V1_0::Rect>{rect};
777     input.getFrameTimestamps = false;
778     producer->queueBuffer(
779         buffer.slot, input,
780         [&](int32_t _s, const IGraphicBufferProducer::QueueBufferOutput& _n1) {
781             result = _s;
782             output = _n1;
783         });
784     EXPECT_EQ(result, 0);
785     if (result != 0) return 1;
786 
787     listener->reduceCount();
788 
789     return 0;
790 }
791 
fillByteBuffer(sp<IOmxNode> omxNode,char * ipBuffer,OMX_U32 portIndexInput,std::ifstream & eleStream)792 int fillByteBuffer(sp<IOmxNode> omxNode, char* ipBuffer, OMX_U32 portIndexInput,
793                    std::ifstream& eleStream) {
794     android::hardware::media::omx::V1_0::Status status;
795     OMX_PARAM_PORTDEFINITIONTYPE portDef;
796     uint32_t i, j;
797 
798     status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndexInput,
799                           &portDef);
800     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
801 
802     int size = ((portDef.format.video.nFrameWidth *
803                  portDef.format.video.nFrameHeight * 3) >>
804                 1);
805     char* img = new char[size];
806     if (img == nullptr) return 1;
807     eleStream.read(img, size);
808     if (eleStream.gcount() != size) {
809         delete[] img;
810         return 1;
811     }
812 
813     char* Y = ipBuffer;
814     char* imgTmp = img;
815     for (j = 0; j < portDef.format.video.nFrameHeight; ++j) {
816         memcpy(Y, imgTmp, portDef.format.video.nFrameWidth);
817         Y += portDef.format.video.nStride;
818         imgTmp += portDef.format.video.nFrameWidth;
819     }
820 
821     if (portDef.format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
822         char* Cb = ipBuffer + (portDef.format.video.nFrameHeight *
823                                portDef.format.video.nStride);
824         char* Cr = Cb + 1;
825         for (j = 0; j<portDef.format.video.nFrameHeight>> 1; ++j) {
826             for (i = 0; i < (portDef.format.video.nFrameWidth >> 1); ++i) {
827                 Cb[2 * i] = *imgTmp++;
828             }
829             Cb += portDef.format.video.nStride;
830         }
831         for (j = 0; j<portDef.format.video.nFrameHeight>> 1; ++j) {
832             for (i = 0; i < (portDef.format.video.nFrameWidth >> 1); ++i) {
833                 Cr[2 * i] = *imgTmp++;
834             }
835             Cr += portDef.format.video.nStride;
836         }
837     } else if (portDef.format.video.eColorFormat ==
838                OMX_COLOR_FormatYUV420Planar) {
839         char* Cb = ipBuffer + (portDef.format.video.nFrameHeight *
840                                portDef.format.video.nStride);
841         char* Cr = Cb + ((portDef.format.video.nFrameHeight *
842                           portDef.format.video.nStride) >>
843                          2);
844         for (j = 0; j<portDef.format.video.nFrameHeight>> 1; ++j) {
845             memcpy(Cb, imgTmp, (portDef.format.video.nFrameWidth >> 1));
846             Cb += (portDef.format.video.nStride >> 1);
847             imgTmp += (portDef.format.video.nFrameWidth >> 1);
848         }
849         for (j = 0; j<portDef.format.video.nFrameHeight>> 1; ++j) {
850             memcpy(Cr, imgTmp, (portDef.format.video.nFrameWidth >> 1));
851             Cr += (portDef.format.video.nStride >> 1);
852             imgTmp += (portDef.format.video.nFrameWidth >> 1);
853         }
854     }
855 
856     delete[] img;
857     return 0;
858 }
859 
860 // Encode N Frames
encodeNFrames(sp<IOmxNode> omxNode,sp<CodecObserver> observer,OMX_U32 portIndexInput,OMX_U32 portIndexOutput,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer,uint32_t nFrames,uint32_t xFramerate,int bytesCount,std::ifstream & eleStream,::android::List<uint64_t> * timestampUslist=nullptr,bool signalEOS=true,bool inputDataIsMeta=false,sp<IGraphicBufferProducer> producer=nullptr,sp<CodecProducerListener> listener=nullptr)861 void encodeNFrames(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
862                    OMX_U32 portIndexInput, OMX_U32 portIndexOutput,
863                    android::Vector<BufferInfo>* iBuffer,
864                    android::Vector<BufferInfo>* oBuffer, uint32_t nFrames,
865                    uint32_t xFramerate, int bytesCount,
866                    std::ifstream& eleStream,
867                    ::android::List<uint64_t>* timestampUslist = nullptr,
868                    bool signalEOS = true, bool inputDataIsMeta = false,
869                    sp<IGraphicBufferProducer> producer = nullptr,
870                    sp<CodecProducerListener> listener = nullptr) {
871     android::hardware::media::omx::V1_0::Status status;
872     Message msg;
873     uint64_t timestamp = 0;
874     uint32_t flags = 0;
875     int timeOut = TIMEOUT_COUNTER_Q;
876     bool iQueued, oQueued;
877 
878     uint32_t ipCount = 0;
879     if (ipCount == 0) {
880         status = changeFrameRate(omxNode, portIndexOutput, (24U << 16));
881         if (status == ::android::hardware::media::omx::V1_0::Status::OK)
882             xFramerate = (24U << 16);
883     }
884     int32_t timestampIncr = (int)((float)1000000 / (xFramerate >> 16));
885     if (inputDataIsMeta) timestampIncr *= 1000;  // timestamp scale: Nano sec
886 
887     while (1) {
888         iQueued = oQueued = false;
889         status =
890             observer->dequeueMessage(&msg, DEFAULT_TIMEOUT_Q, iBuffer, oBuffer);
891         // Port Reconfiguration
892         if (status == android::hardware::media::omx::V1_0::Status::OK) {
893             ASSERT_EQ(msg.type, Message::Type::EVENT);
894             if (msg.data.eventData.event == OMX_EventPortSettingsChanged) {
895                 ASSERT_EQ(msg.data.eventData.data1, portIndexOutput);
896                 ASSERT_EQ(msg.data.eventData.data2,
897                           OMX_IndexConfigAndroidIntraRefresh);
898             } else if (msg.data.eventData.event == OMX_EventError) {
899                 ASSERT_TRUE(false) << "Received OMX_EventError, not sure why";
900             } else if (msg.data.eventData.event == OMX_EventDataSpaceChanged) {
901                 // TODO: how am i supposed to respond now?
902                 std::cout << "[   INFO   ] OMX_EventDataSpaceChanged \n";
903             } else {
904                 ASSERT_TRUE(false);
905             }
906         }
907 
908         if (nFrames == 0) break;
909 
910         // Dispatch input buffer
911         size_t index = 0;
912         if (inputDataIsMeta) {
913             if (listener->freeBuffers > listener->minUnDequeuedCount) {
914                 if (dispatchGraphicBuffer(omxNode, producer, listener, iBuffer,
915                                           portIndexInput, eleStream,
916                                           timestamp)) {
917                     if (::testing::Test::HasFailure())
918                         ASSERT_TRUE(false);
919                     else
920                         break;
921                 }
922                 timestamp += timestampIncr;
923                 nFrames--;
924                 ipCount++;
925                 iQueued = true;
926             }
927         } else {
928             if ((index = getEmptyBufferID(iBuffer)) < iBuffer->size()) {
929                 char* ipBuffer = static_cast<char*>(static_cast<void*>(
930                     (*iBuffer)[index].mMemory->getPointer()));
931                 ASSERT_LE(
932                     bytesCount,
933                     static_cast<int>((*iBuffer)[index].mMemory->getSize()));
934                 if (fillByteBuffer(omxNode, ipBuffer, portIndexInput,
935                                    eleStream))
936                     break;
937                 flags = OMX_BUFFERFLAG_ENDOFFRAME;
938                 if (signalEOS && (nFrames == 1)) flags |= OMX_BUFFERFLAG_EOS;
939                 ASSERT_NO_FATAL_FAILURE(dispatchInputBuffer(
940                     omxNode, iBuffer, index, bytesCount, flags, timestamp));
941                 if (timestampUslist) timestampUslist->push_back(timestamp);
942                 timestamp += timestampIncr;
943                 nFrames--;
944                 ipCount++;
945                 iQueued = true;
946             }
947         }
948         // Dispatch output buffer
949         if ((index = getEmptyBufferID(oBuffer)) < oBuffer->size()) {
950             ASSERT_NO_FATAL_FAILURE(
951                 dispatchOutputBuffer(omxNode, oBuffer, index));
952             oQueued = true;
953         }
954         // Reset Counters when either input or output buffer is dispatched
955         if (iQueued || oQueued)
956             timeOut = TIMEOUT_COUNTER_Q;
957         else
958             timeOut--;
959         if (timeOut == 0) {
960             ASSERT_TRUE(false) << "Wait on Input/Output is found indefinite";
961         }
962         // Runtime Param Configuration
963         if (ipCount == 15) {
964             changeBitrate(omxNode, portIndexOutput, 768000);
965             requestIDR(omxNode, portIndexOutput);
966             changeRefreshPeriod(omxNode, portIndexOutput, 15);
967         }
968     }
969 }
970 
971 // set component role
TEST_P(VideoEncHidlTest,SetRole)972 TEST_P(VideoEncHidlTest, SetRole) {
973     description("Test Set Component Role");
974     if (disableTest) return;
975     android::hardware::media::omx::V1_0::Status status;
976     status = setRole(omxNode, role_);
977     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
978 }
979 
980 // port format enumeration
TEST_P(VideoEncHidlTest,EnumeratePortFormat)981 TEST_P(VideoEncHidlTest, EnumeratePortFormat) {
982     description("Test Component on Mandatory Port Parameters (Port Format)");
983     if (disableTest) return;
984     android::hardware::media::omx::V1_0::Status status;
985     uint32_t kPortIndexInput = 0, kPortIndexOutput = 1;
986     OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatYUV420Planar;
987     OMX_U32 xFramerate = (30U << 16);
988     status = setRole(omxNode, role_);
989     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
990     OMX_PORT_PARAM_TYPE params;
991     status = getParam(omxNode, OMX_IndexParamVideoInit, &params);
992     if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
993         ASSERT_EQ(params.nPorts, 2U);
994         kPortIndexInput = params.nStartPortNumber;
995         kPortIndexOutput = kPortIndexInput + 1;
996     }
997     status =
998         setVideoPortFormat(omxNode, kPortIndexInput, OMX_VIDEO_CodingUnused,
999                            eColorFormat, xFramerate);
1000     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1001 
1002     status = setVideoPortFormat(omxNode, kPortIndexOutput, eCompressionFormat,
1003                                 OMX_COLOR_FormatUnused, 0U);
1004     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1005 }
1006 
1007 // Test IOmxBufferSource CallBacks
TEST_P(VideoEncHidlTest,BufferSourceCallBacks)1008 TEST_P(VideoEncHidlTest, BufferSourceCallBacks) {
1009     description("Test IOmxBufferSource CallBacks");
1010     if (disableTest) return;
1011     android::hardware::media::omx::V1_0::Status status;
1012     uint32_t kPortIndexInput = 0, kPortIndexOutput = 1;
1013     status = setRole(omxNode, role_);
1014     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1015     OMX_PORT_PARAM_TYPE params;
1016     status = getParam(omxNode, OMX_IndexParamVideoInit, &params);
1017     if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
1018         ASSERT_EQ(params.nPorts, 2U);
1019         kPortIndexInput = params.nStartPortNumber;
1020         kPortIndexOutput = kPortIndexInput + 1;
1021     }
1022 
1023     // Configure input port
1024     uint32_t nFrameWidth = 352;
1025     uint32_t nFrameHeight = 288;
1026     uint32_t xFramerate = (30U << 16);
1027     OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatAndroidOpaque;
1028     setupRAWPort(omxNode, kPortIndexInput, nFrameWidth, nFrameHeight, 0,
1029                  xFramerate, eColorFormat);
1030 
1031     sp<MockBufferSource> buffersource = new MockBufferSource(omxNode);
1032     ASSERT_NE(buffersource, nullptr);
1033     status = omxNode->setInputSurface(buffersource);
1034     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1035 
1036     // set port mode
1037     PortMode portMode[2];
1038     portMode[0] = PortMode::DYNAMIC_ANW_BUFFER;
1039     portMode[1] = PortMode::PRESET_BYTE_BUFFER;
1040     status = omxNode->setPortMode(kPortIndexInput, portMode[0]);
1041     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1042     status = omxNode->setPortMode(kPortIndexOutput, portMode[1]);
1043     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1044 
1045     // set state to idle
1046     ASSERT_NO_FATAL_FAILURE(changeStateLoadedtoIdle(
1047         omxNode, observer, &buffersource->iBuffer, &buffersource->oBuffer,
1048         kPortIndexInput, kPortIndexOutput, portMode));
1049     // set state to executing
1050     ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer));
1051     ASSERT_NO_FATAL_FAILURE(testEOS(omxNode, observer, &buffersource->iBuffer,
1052                                     &buffersource->oBuffer, false, eosFlag));
1053     // set state to idle
1054     ASSERT_NO_FATAL_FAILURE(changeStateExecutetoIdle(
1055         omxNode, observer, &buffersource->iBuffer, &buffersource->oBuffer));
1056     // set state to executing
1057     ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &buffersource->iBuffer,
1058                                                     &buffersource->oBuffer, kPortIndexInput,
1059                                                     kPortIndexOutput, portMode));
1060     // test for callbacks
1061     EXPECT_EQ(buffersource->callback, 31);
1062 }
1063 
1064 // test raw stream encode (input is byte buffers)
TEST_P(VideoEncHidlTest,EncodeTest)1065 TEST_P(VideoEncHidlTest, EncodeTest) {
1066     description("Test Encode");
1067     if (disableTest) return;
1068     android::hardware::media::omx::V1_0::Status status;
1069     uint32_t kPortIndexInput = 0, kPortIndexOutput = 1;
1070     status = setRole(omxNode, role_);
1071     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1072     OMX_PORT_PARAM_TYPE params;
1073     status = getParam(omxNode, OMX_IndexParamVideoInit, &params);
1074     if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
1075         ASSERT_EQ(params.nPorts, 2U);
1076         kPortIndexInput = params.nStartPortNumber;
1077         kPortIndexOutput = kPortIndexInput + 1;
1078     }
1079     char mURL[512];
1080     strcpy(mURL, sResourceDir.c_str());
1081     GetURLForComponent(mURL);
1082 
1083     std::ifstream eleStream;
1084 
1085     timestampDevTest = true;
1086 
1087     // Configure input port
1088     uint32_t nFrameWidth = 352;
1089     uint32_t nFrameHeight = 288;
1090     uint32_t xFramerate = (30U << 16);
1091     OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatUnused;
1092     OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
1093     portFormat.nIndex = 0;
1094     while (1) {
1095         status = getPortParam(omxNode, OMX_IndexParamVideoPortFormat,
1096                               kPortIndexInput, &portFormat);
1097         if (status != ::android::hardware::media::omx::V1_0::Status::OK) break;
1098         EXPECT_EQ(portFormat.eCompressionFormat, OMX_VIDEO_CodingUnused);
1099         if (OMX_COLOR_FormatYUV420SemiPlanar == portFormat.eColorFormat ||
1100             OMX_COLOR_FormatYUV420Planar == portFormat.eColorFormat) {
1101             eColorFormat = portFormat.eColorFormat;
1102             break;
1103         }
1104         portFormat.nIndex++;
1105         if (portFormat.nIndex == 512) break;
1106     }
1107     ASSERT_NE(eColorFormat, OMX_COLOR_FormatUnused);
1108     setupRAWPort(omxNode, kPortIndexInput, nFrameWidth, nFrameHeight, 0,
1109                  xFramerate, eColorFormat);
1110 
1111     // Configure output port
1112     uint32_t nBitRate = 512000;
1113     ASSERT_NO_FATAL_FAILURE(
1114         setDefaultPortParam(omxNode, kPortIndexOutput, eCompressionFormat,
1115                             nFrameWidth, nFrameHeight, nBitRate, xFramerate));
1116     setRefreshPeriod(omxNode, kPortIndexOutput, 0);
1117 
1118     unsigned int index;
1119     omxNode->getExtensionIndex(
1120         "OMX.google.android.index.prependSPSPPSToIDRFrames",
1121         [&status, &index](android::hardware::media::omx::V1_0::Status _s,
1122                           unsigned int _nl) {
1123             status = _s;
1124             index = _nl;
1125         });
1126     if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
1127         android::PrependSPSPPSToIDRFramesParams param;
1128         param.bEnable = OMX_TRUE;
1129         status = setParam(omxNode, static_cast<OMX_INDEXTYPE>(index), &param);
1130     }
1131     if (status != ::android::hardware::media::omx::V1_0::Status::OK)
1132         std::cout << "[   INFO   ] unable to prependSPSPPSToIDRFrames\n";
1133     else
1134         prependSPSPPS = true;
1135 
1136     // set port mode
1137     PortMode portMode[2];
1138     portMode[0] = portMode[1] = PortMode::PRESET_BYTE_BUFFER;
1139     status = omxNode->setPortMode(kPortIndexInput, portMode[0]);
1140     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1141     status = omxNode->setPortMode(kPortIndexOutput, portMode[1]);
1142     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1143 
1144     uint32_t latency = 0;
1145     setLatency(omxNode, kPortIndexInput, latency);
1146     getLatency(omxNode, kPortIndexInput, &latency);
1147 
1148     android::Vector<BufferInfo> iBuffer, oBuffer;
1149 
1150     // set state to idle
1151     ASSERT_NO_FATAL_FAILURE(
1152         changeStateLoadedtoIdle(omxNode, observer, &iBuffer, &oBuffer,
1153                                 kPortIndexInput, kPortIndexOutput, portMode));
1154     // set state to executing
1155     ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer));
1156 
1157     eleStream.open(mURL, std::ifstream::binary);
1158     ASSERT_EQ(eleStream.is_open(), true);
1159     ASSERT_NO_FATAL_FAILURE(encodeNFrames(
1160         omxNode, observer, kPortIndexInput, kPortIndexOutput, &iBuffer,
1161         &oBuffer, 32, xFramerate, (nFrameWidth * nFrameHeight * 3) >> 1,
1162         eleStream, &timestampUslist));
1163     eleStream.close();
1164     ASSERT_NO_FATAL_FAILURE(
1165         waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer));
1166     ASSERT_NO_FATAL_FAILURE(
1167         testEOS(omxNode, observer, &iBuffer, &oBuffer, false, eosFlag));
1168     if (timestampDevTest) EXPECT_EQ(timestampUslist.empty(), true);
1169 
1170     // set state to idle
1171     ASSERT_NO_FATAL_FAILURE(
1172         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
1173     // set state to executing
1174     ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
1175                                                     kPortIndexInput, kPortIndexOutput, portMode));
1176 }
1177 
1178 // test raw stream encode (input is ANW buffers)
TEST_P(VideoEncHidlTest,EncodeTestBufferMetaModes)1179 TEST_P(VideoEncHidlTest, EncodeTestBufferMetaModes) {
1180     description("Test Encode Input buffer metamodes");
1181     if (disableTest) return;
1182     android::hardware::media::omx::V1_0::Status status;
1183     uint32_t kPortIndexInput = 0, kPortIndexOutput = 1;
1184     status = setRole(omxNode, role_);
1185     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1186     OMX_PORT_PARAM_TYPE params;
1187     status = getParam(omxNode, OMX_IndexParamVideoInit, &params);
1188     if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
1189         ASSERT_EQ(params.nPorts, 2U);
1190         kPortIndexInput = params.nStartPortNumber;
1191         kPortIndexOutput = kPortIndexInput + 1;
1192     }
1193 
1194     // Configure input port
1195     uint32_t nFrameWidth = 352;
1196     uint32_t nFrameHeight = 288;
1197     uint32_t xFramerate = (30U << 16);
1198     OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatAndroidOpaque;
1199     setupRAWPort(omxNode, kPortIndexInput, nFrameWidth, nFrameHeight, 0,
1200                  xFramerate, eColorFormat);
1201 
1202     // Configure output port
1203     uint32_t nBitRate = 512000;
1204     ASSERT_NO_FATAL_FAILURE(
1205         setDefaultPortParam(omxNode, kPortIndexOutput, eCompressionFormat,
1206                             nFrameWidth, nFrameHeight, nBitRate, xFramerate));
1207     // CreateInputSurface
1208     EXPECT_TRUE(omx->createInputSurface(
1209                        [&](android::hardware::media::omx::V1_0::Status _s,
1210                            sp<IGraphicBufferProducer> const& _nl,
1211                            sp<IGraphicBufferSource> const& _n2) {
1212                            status = _s;
1213                            producer = _nl;
1214                            source = _n2;
1215                        })
1216                     .isOk());
1217     ASSERT_NE(producer, nullptr);
1218     ASSERT_NE(source, nullptr);
1219 
1220     // setMaxDequeuedBufferCount
1221     int32_t returnval;
1222     int32_t value;
1223     producer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
1224                     [&returnval, &value](int32_t _s, int32_t _n1) {
1225                         returnval = _s;
1226                         value = _n1;
1227                     });
1228     ASSERT_EQ(returnval, 0);
1229     OMX_PARAM_PORTDEFINITIONTYPE portDef;
1230     status = getPortParam(omxNode, OMX_IndexParamPortDefinition,
1231                           kPortIndexInput, &portDef);
1232     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1233     ASSERT_EQ(::android::OK,
1234               producer->setMaxDequeuedBufferCount(portDef.nBufferCountActual));
1235 
1236     // Connect :: Mock Producer Listener
1237     IGraphicBufferProducer::QueueBufferOutput qbo;
1238     sp<CodecProducerListener> listener =
1239         new CodecProducerListener(portDef.nBufferCountActual + value, value);
1240     producer->connect(
1241         listener, NATIVE_WINDOW_API_CPU, false,
1242         [&](int32_t _s, IGraphicBufferProducer::QueueBufferOutput const& _n1) {
1243             returnval = _s;
1244             qbo = _n1;
1245         });
1246     ASSERT_EQ(returnval, 0);
1247 
1248     portDef.nBufferCountActual = portDef.nBufferCountActual + value;
1249     status = setPortParam(omxNode, OMX_IndexParamPortDefinition,
1250                           kPortIndexInput, &portDef);
1251     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1252 
1253     // Do setInputSurface()
1254     // enable MetaMode on input port
1255     status = source->configure(
1256         omxNode, android::hardware::graphics::common::V1_0::Dataspace::UNKNOWN);
1257     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1258 
1259     // set port mode
1260     PortMode portMode[2];
1261     portMode[0] = PortMode::DYNAMIC_ANW_BUFFER;
1262     portMode[1] = PortMode::PRESET_BYTE_BUFFER;
1263     status = omxNode->setPortMode(kPortIndexInput, portMode[0]);
1264     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1265     status = omxNode->setPortMode(kPortIndexOutput, portMode[1]);
1266     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1267 
1268     char mURL[512];
1269     strcpy(mURL, sResourceDir.c_str());
1270     GetURLForComponent(mURL);
1271 
1272     uint32_t latency = 0;
1273     setLatency(omxNode, kPortIndexInput, latency);
1274     getLatency(omxNode, kPortIndexInput, &latency);
1275 
1276     std::ifstream eleStream;
1277 
1278     status = source->setSuspend(false, 0);
1279     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1280     status = source->setRepeatPreviousFrameDelayUs(100000);
1281     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1282     status = source->setMaxFps(24.0f);
1283     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1284     status = source->setTimeLapseConfig(24.0, 24.0);
1285     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1286     status = source->setTimeOffsetUs(-100);
1287     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1288     status = source->setStartTimeUs(10);
1289     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1290     status = source->setStopTimeUs(1000000);
1291     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1292     ::android::hardware::media::omx::V1_0::ColorAspects aspects;
1293     aspects.range =
1294         ::android::hardware::media::omx::V1_0::ColorAspects::Range::UNSPECIFIED;
1295     aspects.primaries = ::android::hardware::media::omx::V1_0::ColorAspects::
1296         Primaries::UNSPECIFIED;
1297     aspects.transfer = ::android::hardware::media::omx::V1_0::ColorAspects::
1298         Transfer::UNSPECIFIED;
1299     aspects.matrixCoeffs = ::android::hardware::media::omx::V1_0::ColorAspects::
1300         MatrixCoeffs::UNSPECIFIED;
1301     status = source->setColorAspects(aspects);
1302     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1303     int64_t stopTimeOffsetUs;
1304     source->getStopTimeOffsetUs(
1305         [&](android::hardware::media::omx::V1_0::Status _s, int64_t _n1) {
1306             status = _s;
1307             stopTimeOffsetUs = _n1;
1308         });
1309     EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1310 
1311     android::Vector<BufferInfo> iBuffer, oBuffer;
1312     // set state to idle
1313     ASSERT_NO_FATAL_FAILURE(
1314         changeStateLoadedtoIdle(omxNode, observer, &iBuffer, &oBuffer,
1315                                 kPortIndexInput, kPortIndexOutput, portMode));
1316     // set state to executing
1317     ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer));
1318 
1319     eleStream.open(mURL, std::ifstream::binary);
1320     ASSERT_EQ(eleStream.is_open(), true);
1321     ASSERT_NO_FATAL_FAILURE(encodeNFrames(
1322         omxNode, observer, kPortIndexInput, kPortIndexOutput, &iBuffer,
1323         &oBuffer, 1024, xFramerate, (nFrameWidth * nFrameHeight * 3) >> 1,
1324         eleStream, nullptr, false, true, producer, listener));
1325     eleStream.close();
1326     ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(omxNode, observer, &iBuffer,
1327                                                    &oBuffer, true, listener));
1328     ASSERT_NO_FATAL_FAILURE(
1329         testEOS(omxNode, observer, &iBuffer, &oBuffer, false, eosFlag));
1330 
1331     // set state to idle
1332     ASSERT_NO_FATAL_FAILURE(
1333         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
1334     EXPECT_EQ(portDef.nBufferCountActual, listener->freeBuffers);
1335     // set state to executing
1336     ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
1337                                                     kPortIndexInput, kPortIndexOutput, portMode));
1338 
1339     returnval = producer->disconnect(
1340         NATIVE_WINDOW_API_CPU, IGraphicBufferProducer::DisconnectMode::API);
1341     ASSERT_EQ(returnval, 0);
1342 }
1343 
1344 // Test end of stream
TEST_P(VideoEncHidlTest,EncodeTestEOS)1345 TEST_P(VideoEncHidlTest, EncodeTestEOS) {
1346     description("Test EOS");
1347     if (disableTest) return;
1348     android::hardware::media::omx::V1_0::Status status;
1349     uint32_t kPortIndexInput = 0, kPortIndexOutput = 1;
1350     status = setRole(omxNode, role_);
1351     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1352     OMX_PORT_PARAM_TYPE params;
1353     status = getParam(omxNode, OMX_IndexParamVideoInit, &params);
1354     if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
1355         ASSERT_EQ(params.nPorts, 2U);
1356         kPortIndexInput = params.nStartPortNumber;
1357         kPortIndexOutput = kPortIndexInput + 1;
1358     }
1359 
1360     // Configure input port
1361     uint32_t nFrameWidth = 352;
1362     uint32_t nFrameHeight = 288;
1363     uint32_t xFramerate = (30U << 16);
1364     OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatAndroidOpaque;
1365     setupRAWPort(omxNode, kPortIndexInput, nFrameWidth, nFrameHeight, 0,
1366                  xFramerate, eColorFormat);
1367 
1368     // CreateInputSurface
1369     EXPECT_TRUE(omx->createInputSurface(
1370                        [&](android::hardware::media::omx::V1_0::Status _s,
1371                            sp<IGraphicBufferProducer> const& _nl,
1372                            sp<IGraphicBufferSource> const& _n2) {
1373                            status = _s;
1374                            producer = _nl;
1375                            source = _n2;
1376                        })
1377                     .isOk());
1378     ASSERT_NE(producer, nullptr);
1379     ASSERT_NE(source, nullptr);
1380 
1381     // setMaxDequeuedBufferCount
1382     int32_t returnval;
1383     int32_t value;
1384     producer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
1385                     [&returnval, &value](int32_t _s, int32_t _n1) {
1386                         returnval = _s;
1387                         value = _n1;
1388                     });
1389     ASSERT_EQ(returnval, 0);
1390     OMX_PARAM_PORTDEFINITIONTYPE portDef;
1391     status = getPortParam(omxNode, OMX_IndexParamPortDefinition,
1392                           kPortIndexInput, &portDef);
1393     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1394     ASSERT_EQ(::android::OK,
1395               producer->setMaxDequeuedBufferCount(portDef.nBufferCountActual));
1396 
1397     // Connect :: Mock Producer Listener
1398     IGraphicBufferProducer::QueueBufferOutput qbo;
1399     sp<CodecProducerListener> listener =
1400         new CodecProducerListener(portDef.nBufferCountActual + value, value);
1401     producer->connect(
1402         listener, NATIVE_WINDOW_API_CPU, false,
1403         [&](int32_t _s, IGraphicBufferProducer::QueueBufferOutput const& _n1) {
1404             returnval = _s;
1405             qbo = _n1;
1406         });
1407     ASSERT_EQ(returnval, 0);
1408 
1409     portDef.nBufferCountActual = portDef.nBufferCountActual + value;
1410     status = setPortParam(omxNode, OMX_IndexParamPortDefinition,
1411                           kPortIndexInput, &portDef);
1412     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1413 
1414     // Do setInputSurface()
1415     // enable MetaMode on input port
1416     status = source->configure(
1417         omxNode, android::hardware::graphics::common::V1_0::Dataspace::UNKNOWN);
1418     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1419 
1420     // set port mode
1421     PortMode portMode[2];
1422     portMode[0] = PortMode::DYNAMIC_ANW_BUFFER;
1423     portMode[1] = PortMode::PRESET_BYTE_BUFFER;
1424     status = omxNode->setPortMode(kPortIndexInput, portMode[0]);
1425     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1426     status = omxNode->setPortMode(kPortIndexOutput, portMode[1]);
1427     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1428 
1429     android::Vector<BufferInfo> iBuffer, oBuffer;
1430     // set state to idle
1431     ASSERT_NO_FATAL_FAILURE(
1432         changeStateLoadedtoIdle(omxNode, observer, &iBuffer, &oBuffer,
1433                                 kPortIndexInput, kPortIndexOutput, portMode));
1434     // set state to executing
1435     ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer));
1436 
1437     // send EOS
1438     status = source->signalEndOfInputStream();
1439     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
1440     ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(omxNode, observer, &iBuffer,
1441                                                    &oBuffer, true, listener));
1442     ASSERT_NO_FATAL_FAILURE(
1443         testEOS(omxNode, observer, &iBuffer, &oBuffer, false, eosFlag));
1444 
1445     // set state to idle
1446     ASSERT_NO_FATAL_FAILURE(
1447         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
1448     EXPECT_EQ(portDef.nBufferCountActual, listener->freeBuffers);
1449     // set state to executing
1450     ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
1451                                                     kPortIndexInput, kPortIndexOutput, portMode));
1452 
1453     returnval = producer->disconnect(
1454         NATIVE_WINDOW_API_CPU, IGraphicBufferProducer::DisconnectMode::API);
1455     ASSERT_EQ(returnval, 0);
1456 }
1457 
1458 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VideoEncHidlTest);
1459 INSTANTIATE_TEST_SUITE_P(PerInstance, VideoEncHidlTest, testing::ValuesIn(kTestParameters),
1460                          android::hardware::PrintInstanceTupleNameToString<>);
1461 
main(int argc,char ** argv)1462 int main(int argc, char** argv) {
1463     kTestParameters = getTestParameters("video_encoder");
1464     ::testing::InitGoogleTest(&argc, argv);
1465 
1466     // Set the resource directory based on command line args.
1467     // Test will fail to set up if the argument is not set.
1468     for (int i = 1; i < argc; i++) {
1469         if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
1470             sResourceDir = argv[i + 1];
1471             break;
1472         }
1473     }
1474 
1475     return RUN_ALL_TESTS();
1476 }