| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/command_line.h" | |
| 6 #include "base/logging.h" | |
| 7 #include "base/synchronization/waitable_event.h" | |
| 8 #include "testing/gtest/include/gtest/gtest.h" | |
| 9 #include "third_party/openmax/il/OMX_Component.h" | |
| 10 #include "third_party/openmax/il/OMX_Core.h" | |
| 11 | |
| 12 namespace media { | |
| 13 | |
| 14 // Defines the maximum number of buffers created for I/O ports. | |
| 15 static const int kMaxBufferNum = 256; | |
| 16 | |
| 17 template <typename T> | |
| 18 static void ResetHeader(T* param) { | |
| 19 memset(param, 0, sizeof(T)); | |
| 20 // TODO(hclam): Make this version number configurable. | |
| 21 param->nVersion.nVersion = 0x00000101; | |
| 22 param->nSize = sizeof(T); | |
| 23 } | |
| 24 | |
| 25 class OmxTest : public testing::Test { | |
| 26 public: | |
| 27 OmxTest() | |
| 28 : handle_(NULL), | |
| 29 event_(false, false), | |
| 30 empty_buffer_(false, false), | |
| 31 fill_buffer_(false, false), | |
| 32 last_event_type_(OMX_EventMax), | |
| 33 last_event_data1_(0), | |
| 34 last_event_data2_(0) { | |
| 35 memset(input_buffers_, 0, sizeof(input_buffers_)); | |
| 36 memset(output_buffers_, 0, sizeof(output_buffers_)); | |
| 37 } | |
| 38 | |
| 39 protected: | |
| 40 virtual void SetUp() { | |
| 41 // Initialize OpenMAX. | |
| 42 EXPECT_EQ(OMX_ErrorNone, OMX_Init()); | |
| 43 } | |
| 44 | |
| 45 virtual void TearDown() { | |
| 46 EXPECT_EQ(OMX_ErrorNone, OMX_Deinit()); | |
| 47 } | |
| 48 | |
| 49 void InitComponent(std::string component_name) { | |
| 50 // TODO(hclam): Remove static when bug in driver is fixed. | |
| 51 static OMX_CALLBACKTYPE callback = { &EventHandler, | |
| 52 &EmptyBufferCallback, | |
| 53 &FillBufferCallback }; | |
| 54 | |
| 55 OMX_ERRORTYPE omxresult = OMX_GetHandle( | |
| 56 (void**)&handle_, | |
| 57 const_cast<OMX_STRING>(component_name.c_str()), | |
| 58 this, &callback); | |
| 59 EXPECT_EQ(OMX_ErrorNone, omxresult); | |
| 60 CHECK(handle_); | |
| 61 } | |
| 62 | |
| 63 void DeinitComponent() { | |
| 64 if (handle_) | |
| 65 OMX_FreeHandle(handle_); | |
| 66 } | |
| 67 | |
| 68 void AllocateBuffers(int port) { | |
| 69 int count = 0; | |
| 70 int size = 0; | |
| 71 OMX_BUFFERHEADERTYPE** buffers = NULL; | |
| 72 if (port == input_port_) { | |
| 73 count = input_buffer_count_; | |
| 74 size = input_buffer_size_; | |
| 75 buffers = input_buffers_; | |
| 76 } else if (port == output_port_) { | |
| 77 count = output_buffer_count_; | |
| 78 size = output_buffer_size_; | |
| 79 buffers = output_buffers_; | |
| 80 } else { | |
| 81 NOTREACHED() << "Not a valid port"; | |
| 82 } | |
| 83 for (int i = 0; i < count; ++i) { | |
| 84 EXPECT_EQ(OMX_ErrorNone, | |
| 85 OMX_AllocateBuffer(handle_, buffers + i, | |
| 86 port, NULL, size)); | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 void ReleaseBuffers(int port) { | |
| 91 int count = 0; | |
| 92 OMX_BUFFERHEADERTYPE** buffers = NULL; | |
| 93 if (port == input_port_) { | |
| 94 count = input_buffer_count_; | |
| 95 buffers = input_buffers_; | |
| 96 } else if (port == output_port_) { | |
| 97 count = output_buffer_count_; | |
| 98 buffers = output_buffers_; | |
| 99 } else { | |
| 100 NOTREACHED() << "Not a valid port"; | |
| 101 } | |
| 102 for (int i = 0; i < count; ++i) { | |
| 103 CHECK(buffers[i]); | |
| 104 EXPECT_EQ(OMX_ErrorNone, | |
| 105 OMX_FreeBuffer(handle_, port, buffers[i])); | |
| 106 buffers[i] = NULL; | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 void TransitionLoadedToIdle() { | |
| 111 EXPECT_EQ(OMX_ErrorNone, | |
| 112 OMX_SendCommand(handle_, OMX_CommandStateSet, | |
| 113 OMX_StateIdle, 0)); | |
| 114 AllocateBuffers(input_port_); | |
| 115 AllocateBuffers(output_port_); | |
| 116 event_.Wait(); | |
| 117 EXPECT_EQ(OMX_EventCmdComplete, last_event_type_); | |
| 118 EXPECT_EQ(OMX_CommandStateSet, last_event_data1_); | |
| 119 EXPECT_EQ(OMX_StateIdle, last_event_data2_); | |
| 120 } | |
| 121 | |
| 122 void TransitionIdleToLoaded() { | |
| 123 EXPECT_EQ(OMX_ErrorNone, | |
| 124 OMX_SendCommand(handle_, OMX_CommandStateSet, | |
| 125 OMX_StateLoaded, 0)); | |
| 126 ReleaseBuffers(input_port_); | |
| 127 ReleaseBuffers(output_port_); | |
| 128 event_.Wait(); | |
| 129 EXPECT_EQ(OMX_EventCmdComplete, last_event_type_); | |
| 130 EXPECT_EQ(OMX_CommandStateSet, last_event_data1_); | |
| 131 EXPECT_EQ(OMX_StateLoaded, last_event_data2_); | |
| 132 } | |
| 133 | |
| 134 void TransitionIdleToExecuting() { | |
| 135 EXPECT_EQ(OMX_ErrorNone, | |
| 136 OMX_SendCommand(handle_, OMX_CommandStateSet, | |
| 137 OMX_StateExecuting, 0)); | |
| 138 event_.Wait(); | |
| 139 EXPECT_EQ(OMX_EventCmdComplete, last_event_type_); | |
| 140 EXPECT_EQ(OMX_CommandStateSet, last_event_data1_); | |
| 141 EXPECT_EQ(OMX_StateExecuting, last_event_data2_); | |
| 142 } | |
| 143 | |
| 144 void TransitionExecutingToIdle() { | |
| 145 EXPECT_EQ(OMX_ErrorNone, | |
| 146 OMX_SendCommand(handle_, OMX_CommandStateSet, | |
| 147 OMX_StateIdle, 0)); | |
| 148 event_.Wait(); | |
| 149 EXPECT_EQ(OMX_EventCmdComplete, last_event_type_); | |
| 150 EXPECT_EQ(OMX_CommandStateSet, last_event_data1_); | |
| 151 EXPECT_EQ(OMX_StateIdle, last_event_data2_); | |
| 152 } | |
| 153 | |
| 154 void GetComponentsOfRole(std::string role) { | |
| 155 OMX_U32 roles = 0; | |
| 156 OMX_U8** component_names = NULL; | |
| 157 const int kSize = 256; | |
| 158 | |
| 159 LOG(INFO) << "GetComponentsOfRole: " << role; | |
| 160 EXPECT_EQ(OMX_ErrorNone, OMX_GetComponentsOfRole( | |
| 161 const_cast<OMX_STRING>(role.c_str()), &roles, 0)); | |
| 162 | |
| 163 // TODO(hclam): Should assert the component number. | |
| 164 LOG(INFO) << "Components: " << roles; | |
| 165 | |
| 166 if (roles) { | |
| 167 component_names = new OMX_U8*[roles]; | |
| 168 for (size_t i = 0; i < roles; ++i) | |
| 169 component_names[i] = new OMX_U8[kSize]; | |
| 170 | |
| 171 OMX_U32 roles_backup = roles; | |
| 172 EXPECT_EQ(OMX_ErrorNone, | |
| 173 OMX_GetComponentsOfRole( | |
| 174 const_cast<OMX_STRING>(role.c_str()), | |
| 175 &roles, component_names)); | |
| 176 ASSERT_EQ(roles_backup, roles); | |
| 177 | |
| 178 for (size_t i = 0; i < roles; ++i) { | |
| 179 LOG(INFO) << "Component name: " << component_names[i]; | |
| 180 delete [] component_names[i]; | |
| 181 } | |
| 182 delete [] component_names; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 OMX_ERRORTYPE EventHandlerInternal( | |
| 187 OMX_HANDLETYPE component, OMX_EVENTTYPE event, | |
| 188 OMX_U32 data1, OMX_U32 data2, OMX_PTR event_data) { | |
| 189 last_event_type_ = event; | |
| 190 last_event_data1_ = static_cast<int>(data1); | |
| 191 last_event_data2_ = static_cast<int>(data2); | |
| 192 // TODO(hclam): Save |event_data|. | |
| 193 event_.Signal(); | |
| 194 return OMX_ErrorNone; | |
| 195 } | |
| 196 | |
| 197 OMX_ERRORTYPE EmptyBufferCallbackInternal( | |
| 198 OMX_HANDLETYPE component, OMX_BUFFERHEADERTYPE* buffer) { | |
| 199 // TODO(hclam): Add code here. | |
| 200 empty_buffer_.Signal(); | |
| 201 return OMX_ErrorNone; | |
| 202 } | |
| 203 | |
| 204 OMX_ERRORTYPE FillBufferCallbackInternal( | |
| 205 OMX_HANDLETYPE component, OMX_BUFFERHEADERTYPE* buffer) { | |
| 206 // TODO(hclam): Add code here. | |
| 207 fill_buffer_.Signal(); | |
| 208 return OMX_ErrorNone; | |
| 209 } | |
| 210 | |
| 211 // Static callback methods. | |
| 212 static OMX_ERRORTYPE EventHandler( | |
| 213 OMX_HANDLETYPE component, OMX_PTR priv_data, | |
| 214 OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2, | |
| 215 OMX_PTR event_data) { | |
| 216 return static_cast<OmxTest*>(priv_data) | |
| 217 ->EventHandlerInternal(component, | |
| 218 event, data1, data2, event_data); | |
| 219 } | |
| 220 | |
| 221 static OMX_ERRORTYPE EmptyBufferCallback( | |
| 222 OMX_HANDLETYPE component, OMX_PTR priv_data, | |
| 223 OMX_BUFFERHEADERTYPE* buffer) { | |
| 224 return static_cast<OmxTest*>(priv_data) | |
| 225 ->EmptyBufferCallbackInternal(component, buffer); | |
| 226 } | |
| 227 | |
| 228 static OMX_ERRORTYPE FillBufferCallback( | |
| 229 OMX_HANDLETYPE component, OMX_PTR priv_data, | |
| 230 OMX_BUFFERHEADERTYPE* buffer) { | |
| 231 return static_cast<OmxTest*>(priv_data) | |
| 232 ->FillBufferCallbackInternal(component, buffer); | |
| 233 } | |
| 234 | |
| 235 OMX_COMPONENTTYPE* handle_; | |
| 236 int input_port_; | |
| 237 int output_port_; | |
| 238 int input_buffer_count_; | |
| 239 int input_buffer_size_; | |
| 240 int output_buffer_count_; | |
| 241 int output_buffer_size_; | |
| 242 OMX_BUFFERHEADERTYPE* input_buffers_[kMaxBufferNum]; | |
| 243 OMX_BUFFERHEADERTYPE* output_buffers_[kMaxBufferNum]; | |
| 244 | |
| 245 base::WaitableEvent event_; | |
| 246 base::WaitableEvent empty_buffer_; | |
| 247 base::WaitableEvent fill_buffer_; | |
| 248 | |
| 249 OMX_EVENTTYPE last_event_type_; | |
| 250 int last_event_data1_; | |
| 251 int last_event_data2_; | |
| 252 }; | |
| 253 | |
| 254 class OmxVideoDecoderTest : public OmxTest { | |
| 255 protected: | |
| 256 void Configure(OMX_VIDEO_CODINGTYPE codec, | |
| 257 int width, int height) { | |
| 258 // Obtain port IDs. | |
| 259 OMX_PORT_PARAM_TYPE port_param; | |
| 260 ResetHeader(&port_param); | |
| 261 EXPECT_EQ(OMX_ErrorNone, | |
| 262 OMX_GetParameter(handle_, | |
| 263 OMX_IndexParamVideoInit, | |
| 264 &port_param)); | |
| 265 | |
| 266 input_port_ = port_param.nStartPortNumber; | |
| 267 output_port_ = port_param.nStartPortNumber + 1; | |
| 268 LOG(INFO) << "Input port number: " << input_port_; | |
| 269 LOG(INFO) << "Output port number: " << output_port_; | |
| 270 | |
| 271 // Get and set parameters for input port. | |
| 272 LOG(INFO) << "Input port width: " << width; | |
| 273 LOG(INFO) << "Input port height: " << height; | |
| 274 LOG(INFO) << "Input port codec: " << codec; | |
| 275 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
| 276 ResetHeader(&port_format); | |
| 277 port_format.nPortIndex = input_port_; | |
| 278 EXPECT_EQ(OMX_ErrorNone, | |
| 279 OMX_GetParameter(handle_, | |
| 280 OMX_IndexParamPortDefinition, | |
| 281 &port_format)); | |
| 282 EXPECT_EQ(OMX_DirInput, port_format.eDir); | |
| 283 port_format.format.video.eCompressionFormat = codec; | |
| 284 port_format.format.video.nFrameWidth = width; | |
| 285 port_format.format.video.nFrameHeight = height; | |
| 286 EXPECT_EQ(OMX_ErrorNone, | |
| 287 OMX_SetParameter(handle_, | |
| 288 OMX_IndexParamPortDefinition, | |
| 289 &port_format)); | |
| 290 | |
| 291 // TODO(hclam): Add configurations to output port. | |
| 292 | |
| 293 // Get Parameters for input port. | |
| 294 ResetHeader(&port_format); | |
| 295 port_format.nPortIndex = input_port_; | |
| 296 EXPECT_EQ(OMX_ErrorNone, | |
| 297 OMX_GetParameter(handle_, | |
| 298 OMX_IndexParamPortDefinition, | |
| 299 &port_format)); | |
| 300 EXPECT_EQ(OMX_DirInput, port_format.eDir); | |
| 301 input_buffer_count_ = port_format.nBufferCountMin; | |
| 302 input_buffer_size_ = port_format.nBufferSize; | |
| 303 CHECK(input_buffer_count_ < kMaxBufferNum); | |
| 304 | |
| 305 // Get parameters for output port. | |
| 306 ResetHeader(&port_format); | |
| 307 port_format.nPortIndex = output_port_; | |
| 308 EXPECT_EQ(OMX_ErrorNone, | |
| 309 OMX_GetParameter(handle_, | |
| 310 OMX_IndexParamPortDefinition, | |
| 311 &port_format)); | |
| 312 EXPECT_EQ(OMX_DirOutput, port_format.eDir); | |
| 313 output_buffer_count_ = port_format.nBufferCountMin; | |
| 314 output_buffer_size_ = port_format.nBufferSize; | |
| 315 CHECK(output_buffer_count_ < kMaxBufferNum); | |
| 316 | |
| 317 LOG(INFO) << "Input buffer count: " << input_buffer_count_; | |
| 318 LOG(INFO) << "Input buffer size: " << input_buffer_size_; | |
| 319 LOG(INFO) << "Output buffer count: " << output_buffer_count_; | |
| 320 LOG(INFO) << "Output buffer size: " << output_buffer_size_; | |
| 321 } | |
| 322 | |
| 323 std::string component() { | |
| 324 return CommandLine::ForCurrentProcess() | |
| 325 ->GetSwitchValueASCII("video-decoder-component"); | |
| 326 } | |
| 327 | |
| 328 OMX_VIDEO_CODINGTYPE codec() { | |
| 329 std::string codec = CommandLine::ForCurrentProcess() | |
| 330 ->GetSwitchValueASCII("video-decoder-codec"); | |
| 331 if (codec == "h264") | |
| 332 return OMX_VIDEO_CodingAVC; | |
| 333 return OMX_VIDEO_CodingAutoDetect; | |
| 334 } | |
| 335 }; | |
| 336 | |
| 337 TEST_F(OmxTest, SimpleInit) { | |
| 338 // A empty test case will test basic init/deinit of OpenMAX library. | |
| 339 } | |
| 340 | |
| 341 TEST_F(OmxTest, GetComponentsOfRole) { | |
| 342 // Roles video decoders. | |
| 343 GetComponentsOfRole("video_decoder.avc"); | |
| 344 GetComponentsOfRole("video_decoder.mpeg4"); | |
| 345 GetComponentsOfRole("video_decoder.vc1"); | |
| 346 | |
| 347 // TODO(hclam): Add roles of encoders. | |
| 348 } | |
| 349 | |
| 350 TEST_F(OmxVideoDecoderTest, GetHandle) { | |
| 351 // TODO(hclam): Should use GetComponentsOfRole instead. | |
| 352 InitComponent(component()); | |
| 353 DeinitComponent(); | |
| 354 } | |
| 355 | |
| 356 TEST_F(OmxVideoDecoderTest, Configuration) { | |
| 357 InitComponent(component()); | |
| 358 // TODO(hclam): Make resolution configurable. | |
| 359 Configure(codec(), 1024, 768); | |
| 360 DeinitComponent(); | |
| 361 } | |
| 362 | |
| 363 TEST_F(OmxVideoDecoderTest, TransitionToIdle) { | |
| 364 InitComponent(component()); | |
| 365 Configure(codec(), 1024, 768); | |
| 366 TransitionLoadedToIdle(); | |
| 367 TransitionIdleToLoaded(); | |
| 368 DeinitComponent(); | |
| 369 } | |
| 370 | |
| 371 TEST_F(OmxVideoDecoderTest, FreeHandleWhenIdle) { | |
| 372 InitComponent(component()); | |
| 373 Configure(codec(), 1024, 768); | |
| 374 TransitionLoadedToIdle(); | |
| 375 DeinitComponent(); | |
| 376 } | |
| 377 | |
| 378 TEST_F(OmxVideoDecoderTest, TransitionToExecuting) { | |
| 379 InitComponent(component()); | |
| 380 Configure(codec(), 1024, 768); | |
| 381 TransitionLoadedToIdle(); | |
| 382 TransitionIdleToExecuting(); | |
| 383 TransitionExecutingToIdle(); | |
| 384 TransitionIdleToLoaded(); | |
| 385 DeinitComponent(); | |
| 386 } | |
| 387 | |
| 388 TEST_F(OmxVideoDecoderTest, FreeHandleWhenExecuting) { | |
| 389 InitComponent(component()); | |
| 390 Configure(codec(), 1024, 768); | |
| 391 TransitionLoadedToIdle(); | |
| 392 TransitionIdleToExecuting(); | |
| 393 DeinitComponent(); | |
| 394 } | |
| 395 | |
| 396 TEST_F(OmxVideoDecoderTest, CallbacksAreCopied) { | |
| 397 // Allocate a callback struct on stack and clear it with zero. | |
| 398 // This make sure OpenMAX library will copy the content of the | |
| 399 // struct. | |
| 400 OMX_CALLBACKTYPE callback = { &EventHandler, | |
| 401 &EmptyBufferCallback, | |
| 402 &FillBufferCallback }; | |
| 403 | |
| 404 OMX_ERRORTYPE omxresult = OMX_GetHandle( | |
| 405 (void**)&handle_, | |
| 406 const_cast<OMX_STRING>(component().c_str()), | |
| 407 this, &callback); | |
| 408 EXPECT_EQ(OMX_ErrorNone, omxresult); | |
| 409 CHECK(handle_); | |
| 410 memset(&callback, 0, sizeof(callback)); | |
| 411 | |
| 412 // Then configure the component as usual. | |
| 413 Configure(codec(), 1024, 768); | |
| 414 TransitionLoadedToIdle(); | |
| 415 TransitionIdleToExecuting(); | |
| 416 TransitionExecutingToIdle(); | |
| 417 TransitionIdleToLoaded(); | |
| 418 DeinitComponent(); | |
| 419 } | |
| 420 | |
| 421 } // namespace media | |
| OLD | NEW |