OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 <algorithm> | |
6 #include <string> | |
7 | |
8 #include "base/callback.h" | |
9 #include "base/logging.h" | |
10 #include "base/message_loop.h" | |
11 #include "base/stl_util-inl.h" | |
12 #include "base/string_util.h" | |
13 #include "media/omx/omx_codec.h" | |
14 #include "media/base/buffers.h" | |
15 | |
16 namespace media { | |
17 | |
18 #if !defined(COMPILER_MSVC) | |
19 int const OmxCodec::kEosBuffer; | |
20 #endif | |
21 | |
22 template <typename T> | |
23 static void ResetPortHeader(const OmxCodec& dec, T* param) { | |
24 memset(param, 0, sizeof(T)); | |
25 param->nVersion.nVersion = dec.current_omx_spec_version(); | |
26 param->nSize = sizeof(T); | |
27 } | |
28 | |
29 OmxCodec::OmxCodec(MessageLoop* message_loop) | |
30 : input_buffer_count_(0), | |
31 input_buffer_size_(0), | |
32 input_port_(0), | |
33 input_eos_(false), | |
34 output_buffer_count_(0), | |
35 output_buffer_size_(0), | |
36 output_port_(0), | |
37 output_eos_(false), | |
38 state_(kEmpty), | |
39 next_state_(kEmpty), | |
40 component_handle_(NULL), | |
41 message_loop_(message_loop) { | |
42 } | |
43 | |
44 OmxCodec::~OmxCodec() { | |
45 DCHECK(state_ == kError || state_ == kEmpty); | |
46 DCHECK_EQ(0u, input_buffers_.size()); | |
47 DCHECK_EQ(0u, output_buffers_.size()); | |
48 DCHECK(available_input_buffers_.empty()); | |
49 DCHECK(pending_input_queue_.empty()); | |
50 DCHECK(processing_input_queue_.empty()); | |
51 } | |
52 | |
53 void OmxCodec::Setup(OmxConfigurator* configurator, | |
54 FeedDoneCallback* feed_done_callback, | |
55 FillDoneCallback* fill_done_callback) { | |
56 DCHECK_EQ(kEmpty, state_); | |
57 CHECK(configurator); | |
58 configurator_ = configurator; | |
59 feed_done_callback_.reset(feed_done_callback); | |
60 fill_done_callback_.reset(fill_done_callback); | |
61 } | |
62 | |
63 void OmxCodec::SetErrorCallback(Callback* callback) { | |
64 DCHECK_EQ(kEmpty, state_); | |
65 error_callback_.reset(callback); | |
66 } | |
67 | |
68 void OmxCodec::SetFormatCallback(FormatCallback* callback) { | |
69 DCHECK_EQ(kEmpty, state_); | |
70 format_callback_.reset(callback); | |
71 } | |
72 | |
73 void OmxCodec::Start() { | |
74 CHECK(configurator_); | |
75 | |
76 message_loop_->PostTask( | |
77 FROM_HERE, | |
78 NewRunnableMethod(this, &OmxCodec::StartTask)); | |
79 } | |
80 | |
81 void OmxCodec::Stop(Callback* callback) { | |
82 message_loop_->PostTask( | |
83 FROM_HERE, | |
84 NewRunnableMethod(this, &OmxCodec::StopTask, callback)); | |
85 } | |
86 | |
87 void OmxCodec::Feed(scoped_refptr<Buffer> buffer) { | |
88 message_loop_->PostTask( | |
89 FROM_HERE, | |
90 NewRunnableMethod(this, &OmxCodec::FeedTask, buffer)); | |
91 } | |
92 | |
93 void OmxCodec::Flush(Callback* callback) { | |
94 callback->Run(); | |
95 delete callback; | |
96 } | |
97 | |
98 OmxCodec::State OmxCodec::GetState() const { | |
99 return state_; | |
100 } | |
101 | |
102 void OmxCodec::SetState(State state) { | |
103 state_ = state; | |
104 } | |
105 | |
106 OmxCodec::State OmxCodec::GetNextState() const { | |
107 return next_state_; | |
108 } | |
109 | |
110 void OmxCodec::SetNextState(State state) { | |
111 next_state_ = state; | |
112 } | |
113 | |
114 void OmxCodec::StartTask() { | |
115 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
116 | |
117 StateTransitionTask(kLoaded); | |
118 } | |
119 | |
120 void OmxCodec::StopTask(Callback* callback) { | |
121 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
122 | |
123 stop_callback_.reset(callback); | |
124 | |
125 if (GetState() == kError) { | |
126 DoneStop(); | |
127 return; | |
128 } | |
129 | |
130 FreeInputQueue(); | |
131 | |
132 // TODO(hclam): We should wait for all output buffers to come back from | |
133 // output sink to proceed to stop. The proper way to do this is | |
134 // transition to a StopWaitingForBuffers state and wait until all buffers | |
135 // are received to proceed. | |
136 | |
137 if (GetState() == kExecuting) | |
138 StateTransitionTask(kIdle); | |
139 // TODO(hclam): The following two transitions may not be correct. | |
140 else if (GetState() == kPortSettingDisable) | |
141 StateTransitionTask(kIdle); | |
142 else if (GetState() == kPortSettingEnable) | |
143 StateTransitionTask(kIdle); | |
144 else if (GetState() == kIdle) | |
145 StateTransitionTask(kLoaded); | |
146 else if (GetState() == kLoaded) | |
147 StateTransitionTask(kEmpty); | |
148 } | |
149 | |
150 void OmxCodec::FeedTask(scoped_refptr<Buffer> buffer) { | |
151 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
152 | |
153 if (!CanAcceptInput()) { | |
154 feed_done_callback_->Run(buffer); | |
155 return; | |
156 } | |
157 | |
158 // Queue this input buffer. | |
159 pending_input_queue_.push(buffer); | |
160 | |
161 // Try to feed buffers into the decoder. | |
162 EmptyBufferTask(); | |
163 } | |
164 | |
165 // This method assumes OMX_AllocateBuffer() will allocate | |
166 // buffer internally. If this is not the case we need to | |
167 // call OMX_UseBuffer() to allocate buffer manually and | |
168 // assign to the headers. | |
169 bool OmxCodec::AllocateInputBuffers() { | |
170 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
171 | |
172 uint8* data = new uint8[input_buffer_size_]; | |
173 scoped_array<uint8> data_deleter(data); | |
174 | |
175 for (int i = 0; i < input_buffer_count_; ++i) { | |
176 OMX_BUFFERHEADERTYPE* buffer; | |
177 OMX_ERRORTYPE error = | |
178 OMX_UseBuffer(component_handle_, &buffer, input_port_, | |
179 NULL, input_buffer_size_, data); | |
180 if (error != OMX_ErrorNone) | |
181 return false; | |
182 input_buffers_.push_back(buffer); | |
183 available_input_buffers_.push(buffer); | |
184 } | |
185 return true; | |
186 } | |
187 | |
188 // This method assumes OMX_AllocateBuffer() will allocate buffer | |
189 // header internally. In additional to that memory that holds the | |
190 // header, the same method call will allocate memory for holding | |
191 // output data. If we use EGL images for holding output data, | |
192 // the memory allocation will be done externally. | |
193 bool OmxCodec::AllocateOutputBuffers() { | |
194 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
195 | |
196 for (int i = 0; i < output_buffer_count_; ++i) { | |
197 OMX_BUFFERHEADERTYPE* buffer; | |
198 OMX_ERRORTYPE error = | |
199 OMX_AllocateBuffer(component_handle_, &buffer, output_port_, | |
200 NULL, output_buffer_size_); | |
201 if (error != OMX_ErrorNone) | |
202 return false; | |
203 output_buffers_.push_back(buffer); | |
204 } | |
205 | |
206 return true; | |
207 } | |
208 | |
209 void OmxCodec::FreeInputBuffers() { | |
210 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
211 | |
212 // Calls to OMX to free buffers. | |
213 for (size_t i = 0; i < input_buffers_.size(); ++i) | |
214 OMX_FreeBuffer(component_handle_, input_port_, input_buffers_[i]); | |
215 input_buffers_.clear(); | |
216 | |
217 // Empty available buffer queue. | |
218 while (!available_input_buffers_.empty()) { | |
219 available_input_buffers_.pop(); | |
220 } | |
221 } | |
222 | |
223 void OmxCodec::FreeOutputBuffers() { | |
224 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
225 | |
226 // Calls to OMX to free buffers. | |
227 for (size_t i = 0; i < output_buffers_.size(); ++i) | |
228 OMX_FreeBuffer(component_handle_, output_port_, output_buffers_[i]); | |
229 output_buffers_.clear(); | |
230 } | |
231 | |
232 void OmxCodec::FreeInputQueue() { | |
233 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
234 | |
235 while (!pending_input_queue_.empty()) { | |
236 scoped_refptr<Buffer> buffer = pending_input_queue_.front(); | |
237 feed_done_callback_->Run(buffer); | |
238 pending_input_queue_.pop(); | |
239 } | |
240 | |
241 while (!processing_input_queue_.empty()) { | |
242 processing_input_queue_.pop(); | |
243 } | |
244 } | |
245 | |
246 // Sequence of actions in this transition: | |
247 // | |
248 // 1. Initialize OMX (To be removed.) | |
249 // 2. Map role name to component name. | |
250 // 3. Get handle of the OMX component | |
251 // 4. Get the port information. | |
252 // 5. Set role for the component. | |
253 // 6. Input/output ports media format configuration. | |
254 // 7. Obtain the information about the input port. | |
255 // 8. Obtain the information about the output port. | |
256 void OmxCodec::Transition_EmptyToLoaded() { | |
257 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
258 DCHECK_EQ(kEmpty, GetState()); | |
259 | |
260 static OMX_CALLBACKTYPE callback = { | |
261 &EventHandler, | |
262 &EmptyBufferCallback, | |
263 &FillBufferCallback | |
264 }; | |
265 | |
266 // 1. Initialize the OpenMAX Core. | |
267 // TODO(hclam): move this out. | |
268 OMX_ERRORTYPE omxresult = OMX_Init(); | |
269 if (omxresult != OMX_ErrorNone) { | |
270 LOG(ERROR) << "Failed to Init OpenMAX core"; | |
271 StateTransitionTask(kError); | |
272 return; | |
273 } | |
274 | |
275 // 2. Map role name to component name. | |
276 std::string role_name = configurator_->GetRoleName(); | |
277 OMX_U32 roles = 0; | |
278 omxresult = OMX_GetComponentsOfRole( | |
279 const_cast<OMX_STRING>(role_name.c_str()), | |
280 &roles, 0); | |
281 if (omxresult != OMX_ErrorNone || roles == 0) { | |
282 LOG(ERROR) << "Unsupported Role: " << role_name.c_str(); | |
283 StateTransitionTask(kError); | |
284 return; | |
285 } | |
286 const OMX_U32 kMaxRolePerComponent = 20; | |
287 CHECK(roles < kMaxRolePerComponent); | |
288 | |
289 OMX_U8** component_names = new OMX_U8*[roles]; | |
290 const int kMaxComponentNameLength = 256; | |
291 for (size_t i = 0; i < roles; ++i) | |
292 component_names[i] = new OMX_U8[kMaxComponentNameLength]; | |
293 | |
294 omxresult = OMX_GetComponentsOfRole( | |
295 const_cast<OMX_STRING>(role_name.c_str()), | |
296 &roles, component_names); | |
297 | |
298 // Use first component only. Copy the name of the first component | |
299 // so that we could free the memory. | |
300 std::string component_name; | |
301 if (omxresult == OMX_ErrorNone) | |
302 component_name = reinterpret_cast<char*>(component_names[0]); | |
303 | |
304 for (size_t i = 0; i < roles; ++i) | |
305 delete [] component_names[i]; | |
306 delete [] component_names; | |
307 | |
308 if (omxresult != OMX_ErrorNone || roles == 0) { | |
309 LOG(ERROR) << "Unsupported Role: " << role_name.c_str(); | |
310 StateTransitionTask(kError); | |
311 return; | |
312 } | |
313 | |
314 // 3. Get the handle to the component. After OMX_GetHandle(), | |
315 // the component is in loaded state. | |
316 OMX_STRING component = const_cast<OMX_STRING>(component_name.c_str()); | |
317 OMX_HANDLETYPE handle = reinterpret_cast<OMX_HANDLETYPE>(component_handle_); | |
318 omxresult = OMX_GetHandle(&handle, component, this, &callback); | |
319 component_handle_ = reinterpret_cast<OMX_COMPONENTTYPE*>(handle); | |
320 if (omxresult != OMX_ErrorNone) { | |
321 LOG(ERROR) << "Failed to Load the component: " << component; | |
322 StateTransitionTask(kError); | |
323 return; | |
324 } | |
325 | |
326 // 4. Get the port information. This will obtain information about the | |
327 // number of ports and index of the first port. | |
328 OMX_PORT_PARAM_TYPE port_param; | |
329 ResetPortHeader(*this, &port_param); | |
330 omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit, | |
331 &port_param); | |
332 if (omxresult != OMX_ErrorNone) { | |
333 LOG(ERROR) << "Failed to get Port Param"; | |
334 StateTransitionTask(kError); | |
335 return; | |
336 } | |
337 input_port_ = port_param.nStartPortNumber; | |
338 output_port_ = input_port_ + 1; | |
339 | |
340 // 5. Set role for the component because our component could | |
341 // have multiple roles. | |
342 OMX_PARAM_COMPONENTROLETYPE role_type; | |
343 ResetPortHeader(*this, &role_type); | |
344 base::strlcpy(reinterpret_cast<char*>(role_type.cRole), | |
345 role_name.c_str(), | |
346 OMX_MAX_STRINGNAME_SIZE); | |
347 role_type.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; | |
348 omxresult = OMX_SetParameter(component_handle_, | |
349 OMX_IndexParamStandardComponentRole, | |
350 &role_type); | |
351 if (omxresult != OMX_ErrorNone) { | |
352 LOG(ERROR) << "Failed to Set Role"; | |
353 StateTransitionTask(kError); | |
354 return; | |
355 } | |
356 | |
357 // 6. Input/output ports media format configuration. | |
358 if (!ConfigureIOPorts()) { | |
359 LOG(ERROR) << "Media format configurations failed"; | |
360 StateTransitionTask(kError); | |
361 return; | |
362 } | |
363 | |
364 // 7. Obtain the information about the input port. | |
365 // This will have the new mini buffer count in |port_format.nBufferCountMin|. | |
366 // Save this value to input_buf_count. | |
367 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
368 ResetPortHeader(*this, &port_format); | |
369 port_format.nPortIndex = input_port_; | |
370 omxresult = OMX_GetParameter(component_handle_, | |
371 OMX_IndexParamPortDefinition, | |
372 &port_format); | |
373 if (omxresult != OMX_ErrorNone) { | |
374 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
375 StateTransitionTask(kError); | |
376 return; | |
377 } | |
378 if (OMX_DirInput != port_format.eDir) { | |
379 LOG(ERROR) << "Expected input port"; | |
380 StateTransitionTask(kError); | |
381 return; | |
382 } | |
383 input_buffer_count_ = port_format.nBufferCountMin; | |
384 input_buffer_size_ = port_format.nBufferSize; | |
385 | |
386 // 8. Obtain the information about the output port. | |
387 ResetPortHeader(*this, &port_format); | |
388 port_format.nPortIndex = output_port_; | |
389 omxresult = OMX_GetParameter(component_handle_, | |
390 OMX_IndexParamPortDefinition, | |
391 &port_format); | |
392 if (omxresult != OMX_ErrorNone) { | |
393 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
394 StateTransitionTask(kError); | |
395 return; | |
396 } | |
397 if (OMX_DirOutput != port_format.eDir) { | |
398 LOG(ERROR) << "Expect Output Port"; | |
399 StateTransitionTask(kError); | |
400 return; | |
401 } | |
402 output_buffer_count_ = port_format.nBufferCountMin; | |
403 output_buffer_size_ = port_format.nBufferSize; | |
404 | |
405 // After we have done all the configurations, we are considered loaded. | |
406 DoneStateTransitionTask(); | |
407 } | |
408 | |
409 // Sequence of actions in this transition: | |
410 // | |
411 // 1. Send command to Idle state. | |
412 // 2. Allocate buffers for input port. | |
413 // 3. Allocate buffers for output port. | |
414 void OmxCodec::Transition_LoadedToIdle() { | |
415 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
416 DCHECK_EQ(kLoaded, GetState()); | |
417 | |
418 // 1. Sets decoder to idle state. | |
419 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
420 OMX_CommandStateSet, | |
421 OMX_StateIdle, 0); | |
422 if (omxresult != OMX_ErrorNone) { | |
423 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
424 StateTransitionTask(kError); | |
425 return; | |
426 } | |
427 | |
428 // 2. Allocate buffer for the input port. | |
429 if (!AllocateInputBuffers()) { | |
430 LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error"; | |
431 StateTransitionTask(kError); | |
432 return; | |
433 } | |
434 | |
435 // 3. Allocate buffer for the output port. | |
436 if (!AllocateOutputBuffers()) { | |
437 LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; | |
438 StateTransitionTask(kError); | |
439 return; | |
440 } | |
441 } | |
442 | |
443 // Sequence of actions in this transition: | |
444 // | |
445 // 1. Send command to Executing state. | |
446 void OmxCodec::Transition_IdleToExecuting() { | |
447 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
448 DCHECK_EQ(kIdle, GetState()); | |
449 | |
450 // Transist to executing state. | |
451 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
452 OMX_CommandStateSet, | |
453 OMX_StateExecuting, 0); | |
454 if (omxresult != OMX_ErrorNone) { | |
455 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
456 StateTransitionTask(kError); | |
457 return; | |
458 } | |
459 | |
460 // Simulate a format change. | |
461 ReportFormatChange(configurator_->input_format(), | |
462 configurator_->output_format()); | |
463 } | |
464 | |
465 // Sequence of actions in this transition: | |
466 // | |
467 // 1. Send command to disable output port. | |
468 // 2. Free buffers of the output port. | |
469 void OmxCodec::Transition_ExecutingToDisable() { | |
470 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
471 DCHECK_EQ(kExecuting, GetState()); | |
472 | |
473 // Send DISABLE command. | |
474 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
475 OMX_CommandPortDisable, | |
476 output_port_, 0); | |
477 if (omxresult != OMX_ErrorNone) { | |
478 LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; | |
479 StateTransitionTask(kError); | |
480 return; | |
481 } | |
482 | |
483 // Free output Buffer. | |
484 FreeOutputBuffers(); | |
485 } | |
486 | |
487 // Sequence of actions in this transition: | |
488 // | |
489 // 1. Send command to enable output port. | |
490 // 2. Get parameter of the output port. | |
491 // 3. Allocate buffers for the output port. | |
492 void OmxCodec::Transition_DisableToEnable() { | |
493 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
494 DCHECK_EQ(kPortSettingDisable, GetState()); | |
495 | |
496 // Send Enable command. | |
497 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
498 OMX_CommandPortEnable, | |
499 output_port_, 0); | |
500 if (omxresult != OMX_ErrorNone) { | |
501 LOG(ERROR) << "SendCommand(OMX_CommandPortEnable) failed"; | |
502 StateTransitionTask(kError); | |
503 return; | |
504 } | |
505 | |
506 // AllocateBuffers. | |
507 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
508 ResetPortHeader(*this, &port_format); | |
509 port_format.nPortIndex = output_port_; | |
510 omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamPortDefinition, | |
511 &port_format); | |
512 if (omxresult != OMX_ErrorNone) { | |
513 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
514 StateTransitionTask(kError); | |
515 return; | |
516 } | |
517 if (OMX_DirOutput != port_format.eDir) { | |
518 LOG(ERROR) << "Expected Output Port"; | |
519 StateTransitionTask(kError); | |
520 return; | |
521 } | |
522 | |
523 // Update the output format. | |
524 // TODO(jiesun): check if the format really change. ( we had omit some | |
525 // information such as frame rate / bit rate / vbv buffer info now. ) | |
526 OmxConfigurator::MediaFormat input_format, output_format; | |
527 output_format.video_header.height = port_format.format.video.nFrameHeight; | |
528 output_format.video_header.width = port_format.format.video.nFrameWidth; | |
529 output_format.video_header.stride = port_format.format.video.nStride; | |
530 input_format.video_header.height = output_format.video_header.height; | |
531 input_format.video_header.width = output_format.video_header.width; | |
532 input_format.video_header.stride = output_format.video_header.stride; | |
533 ReportFormatChange(input_format, output_format); | |
534 | |
535 // Update the ports in buffer. | |
536 output_buffer_count_ = port_format.nBufferCountActual; | |
537 output_buffer_size_ = port_format.nBufferSize; | |
538 if (!AllocateOutputBuffers()) { | |
539 LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; | |
540 StateTransitionTask(kError); | |
541 return; | |
542 } | |
543 } | |
544 | |
545 // Sequence of actions in this transition: | |
546 // | |
547 // 1. Send command to Idle state. | |
548 void OmxCodec::Transition_DisableToIdle() { | |
549 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
550 DCHECK_EQ(kPortSettingDisable, GetState()); | |
551 | |
552 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
553 OMX_CommandStateSet, | |
554 OMX_StateIdle, 0); | |
555 if (omxresult != OMX_ErrorNone) { | |
556 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
557 StateTransitionTask(kError); | |
558 return; | |
559 } | |
560 } | |
561 | |
562 // Sequence of actions in this transition: | |
563 // | |
564 // This transition does nothing. | |
565 void OmxCodec::Transition_EnableToExecuting() { | |
566 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
567 DCHECK_EQ(kPortSettingEnable, GetState()); | |
568 | |
569 // This transition is fake, nothing to do here. | |
570 DoneStateTransitionTask(); | |
571 } | |
572 | |
573 void OmxCodec::Transition_EnableToIdle() { | |
574 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
575 DCHECK_EQ(kPortSettingEnable, GetState()); | |
576 | |
577 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
578 OMX_CommandStateSet, | |
579 OMX_StateIdle, 0); | |
580 if (omxresult != OMX_ErrorNone) { | |
581 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
582 StateTransitionTask(kError); | |
583 return; | |
584 } | |
585 } | |
586 | |
587 // Sequence of actions in this transition: | |
588 // | |
589 // 1. Send command to Idle state. | |
590 void OmxCodec::Transition_ExecutingToIdle() { | |
591 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
592 DCHECK_EQ(kExecuting, GetState()); | |
593 | |
594 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
595 OMX_CommandStateSet, | |
596 OMX_StateIdle, 0); | |
597 if (omxresult != OMX_ErrorNone) { | |
598 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
599 StateTransitionTask(kError); | |
600 return; | |
601 } | |
602 } | |
603 | |
604 // Sequence of actions in this transition: | |
605 // | |
606 // 1. Send command to Loaded state | |
607 // 2. Free input buffers | |
608 // 2. Free output buffers | |
609 void OmxCodec::Transition_IdleToLoaded() { | |
610 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
611 DCHECK_EQ(kIdle, GetState()); | |
612 | |
613 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
614 OMX_CommandStateSet, | |
615 OMX_StateLoaded, 0); | |
616 if (omxresult != OMX_ErrorNone) { | |
617 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
618 StateTransitionTask(kError); | |
619 return; | |
620 } | |
621 | |
622 FreeInputBuffers(); | |
623 FreeOutputBuffers(); | |
624 } | |
625 | |
626 // Sequence of actions in this transition: | |
627 // | |
628 // 1. Free decoder handle | |
629 // 2. Uninitialize OMX (TODO(hclam): Remove this.) | |
630 void OmxCodec::Transition_LoadedToEmpty() { | |
631 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
632 DCHECK_EQ(kLoaded, GetState()); | |
633 | |
634 // Free the decoder handle. | |
635 OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_); | |
636 if (result != OMX_ErrorNone) { | |
637 LOG(ERROR) << "Terminate: OMX_FreeHandle() error. " | |
638 << "Error code: " << result; | |
639 } | |
640 component_handle_ = NULL; | |
641 | |
642 // Deinit OpenMAX | |
643 // TODO(hclam): move this out. | |
644 OMX_Deinit(); | |
645 | |
646 DoneStateTransitionTask(); | |
647 } | |
648 | |
649 // Sequence of actions in this transition: | |
650 // | |
651 // 1. Disable input port | |
652 // 2. Disable output port | |
653 // 3. Free input buffer | |
654 // 4. Free output buffer | |
655 // 5. Free decoder handle | |
656 // 6. Uninitialize OMX (TODO(hclam): Remove this.) | |
657 void OmxCodec::Transition_Error() { | |
658 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
659 DCHECK_NE(kError, GetState()); | |
660 | |
661 State old_state = GetState(); | |
662 SetState(kError); | |
663 | |
664 // If we are going to error state in the following states, we need to | |
665 // send a command to disable ports for us to free buffers. | |
666 if (old_state == kExecuting || old_state == kIdle || | |
667 old_state == kPortSettingEnable || old_state == kPortSettingDisable) { | |
668 DCHECK(component_handle_); | |
669 OMX_SendCommand(component_handle_, OMX_CommandPortDisable, input_port_, 0); | |
670 OMX_SendCommand(component_handle_, OMX_CommandPortDisable, output_port_, 0); | |
671 } | |
672 | |
673 // Free input and output buffers. | |
674 FreeInputBuffers(); | |
675 FreeOutputBuffers(); | |
676 | |
677 // Free input queues. | |
678 FreeInputQueue(); | |
679 | |
680 // Free decoder handle. | |
681 if (component_handle_) { | |
682 OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_); | |
683 if (result != OMX_ErrorNone) | |
684 LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result; | |
685 component_handle_ = NULL; | |
686 } | |
687 | |
688 // Deinit OpenMAX. | |
689 OMX_Deinit(); | |
690 | |
691 DoneStateTransitionTask(); | |
692 } | |
693 | |
694 void OmxCodec::PostStateTransitionTask(State new_state) { | |
695 message_loop_->PostTask( | |
696 FROM_HERE, | |
697 NewRunnableMethod(this, | |
698 &OmxCodec::StateTransitionTask, new_state)); | |
699 } | |
700 | |
701 void OmxCodec::StateTransitionTask(State new_state) { | |
702 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
703 | |
704 if (GetState() == kError) | |
705 return; | |
706 | |
707 // Save the next state. | |
708 SetNextState(new_state); | |
709 | |
710 // The following list defines all the possible state transitions | |
711 // for this object: | |
712 // | |
713 // TRANSITIONS | |
714 // 1. Empty -> Loaded | |
715 // 2. Loaded -> Idle | |
716 // 3. Idle -> Executing | |
717 // 4. Executing -> Disable | |
718 // 5. Executing -> Idle | |
719 // 6. Disable -> Enable | |
720 // 7. Disable -> Idle | |
721 // 8. Enable -> Executing | |
722 // 9. Enable -> Idle | |
723 // 10. Idle -> Loaded | |
724 // 11. Loaded -> Empty (TODO(hclam): To stopped instead.) | |
725 // 12. *ANYTHING* -> Error | |
726 if (GetState() == kEmpty && new_state == kLoaded) | |
727 Transition_EmptyToLoaded(); | |
728 else if (GetState() == kLoaded && new_state == kIdle) | |
729 Transition_LoadedToIdle(); | |
730 else if (GetState() == kIdle && new_state == kExecuting) | |
731 Transition_IdleToExecuting(); | |
732 else if (GetState() == kExecuting && new_state == kPortSettingDisable) | |
733 Transition_ExecutingToDisable(); | |
734 else if (GetState() == kPortSettingDisable && new_state == kPortSettingEnable) | |
735 Transition_DisableToEnable(); | |
736 else if (GetState() == kPortSettingDisable && new_state == kIdle) | |
737 Transition_DisableToIdle(); | |
738 else if (GetState() == kPortSettingEnable && new_state == kExecuting) | |
739 Transition_EnableToExecuting(); | |
740 else if (GetState() == kPortSettingEnable && new_state == kIdle) | |
741 Transition_EnableToIdle(); | |
742 else if (GetState() == kExecuting && new_state == kIdle) | |
743 Transition_ExecutingToIdle(); | |
744 else if (GetState() == kIdle && new_state == kLoaded) | |
745 Transition_IdleToLoaded(); | |
746 else if (GetState() == kLoaded && new_state == kEmpty) | |
747 Transition_LoadedToEmpty(); | |
748 else if (new_state == kError) | |
749 Transition_Error(); | |
750 } | |
751 | |
752 void OmxCodec::PostDoneStateTransitionTask() { | |
753 message_loop_->PostTask( | |
754 FROM_HERE, | |
755 NewRunnableMethod(this, &OmxCodec::DoneStateTransitionTask)); | |
756 } | |
757 | |
758 void OmxCodec::DoneStateTransitionTask() { | |
759 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
760 | |
761 if (GetState() == kError) { | |
762 ReportError(); | |
763 return; | |
764 } | |
765 | |
766 // Save the current state and completes the transition. | |
767 State old_state = GetState(); | |
768 SetState(GetNextState()); | |
769 | |
770 // The following list is to perform a state transition automatically | |
771 // based on the last transition done: | |
772 // | |
773 // LAST TRANSITION NEXT TRANSITION | |
774 // | |
775 // 1. Empty -> Loaded Laoded -> Idle | |
776 // 2. Loaded -> Idle Idle -> Executing | |
777 // 3. Idle -> Executing | |
778 // | |
779 // Because of the above reoute, once we kick start the transition | |
780 // from empty to loaded, this method will automatically route it | |
781 // executing eventually. | |
782 // | |
783 // The following sequence is for transition to the stopped state. | |
784 // | |
785 // LAST TRANSITION NEXT TRANSITION | |
786 // | |
787 // 4. Executing -> Idle Idle -> Loaded | |
788 // 5. Idle -> Loaded Loaded -> Empty | |
789 // TODO(hclam): should go to Stopped instead of Empty. | |
790 // | |
791 // During dynamic port seeting, the route of state transition is: | |
792 // | |
793 // LAST TRANSITION NEXT TRANSITION | |
794 // | |
795 // 6. Executing -> Disable Disable -> Enable | |
796 // 7. Disable -> Enable Enable -> Executing | |
797 if (old_state == kEmpty && GetState() == kLoaded) | |
798 StateTransitionTask(kIdle); | |
799 else if (old_state == kLoaded && GetState() == kIdle) | |
800 StateTransitionTask(kExecuting); | |
801 else if (old_state == kIdle && GetState() == kExecuting) { | |
802 // TODO(hclam): It is a little too late to issue read requests. | |
803 // This seems to introduce some latencies. | |
804 InitialEmptyBuffer(); | |
805 InitialFillBuffer(); | |
806 } | |
807 else if (old_state == kExecuting && GetState() == kPortSettingDisable) | |
808 StateTransitionTask(kPortSettingEnable); | |
809 else if (old_state == kPortSettingDisable && GetState() == kPortSettingEnable) | |
810 StateTransitionTask(kExecuting); | |
811 else if (old_state == kPortSettingEnable && GetState() == kExecuting) | |
812 InitialFillBuffer(); | |
813 else if (old_state == kPortSettingDisable && GetState() == kIdle) | |
814 StateTransitionTask(kLoaded); | |
815 else if (old_state == kPortSettingEnable && GetState() == kIdle) | |
816 StateTransitionTask(kLoaded); | |
817 else if (old_state == kExecuting && GetState() == kIdle) | |
818 StateTransitionTask(kLoaded); | |
819 else if (old_state == kIdle && GetState() == kLoaded) | |
820 StateTransitionTask(kEmpty); | |
821 else if (old_state == kLoaded && GetState() == kEmpty) | |
822 DoneStop(); | |
823 else { | |
824 NOTREACHED() << "Invalid state transition"; | |
825 } | |
826 } | |
827 | |
828 void OmxCodec::DoneStop() { | |
829 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
830 | |
831 if (!stop_callback_.get()) | |
832 return; | |
833 stop_callback_->Run(); | |
834 stop_callback_.reset(); | |
835 } | |
836 | |
837 void OmxCodec::ReportError() { | |
838 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
839 | |
840 if (!error_callback_.get()) | |
841 return; | |
842 error_callback_->Run(); | |
843 error_callback_.reset(); | |
844 } | |
845 | |
846 void OmxCodec::ReportFormatChange( | |
847 const OmxConfigurator::MediaFormat& input_format, | |
848 const OmxConfigurator::MediaFormat& output_format) { | |
849 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
850 | |
851 if (!format_callback_.get()) | |
852 return; | |
853 format_callback_->Run(input_format, output_format); | |
854 } | |
855 | |
856 bool OmxCodec::ConfigureIOPorts() { | |
857 OMX_PARAM_PORTDEFINITIONTYPE input_port_def, output_port_def; | |
858 OMX_ERRORTYPE omxresult = OMX_ErrorNone; | |
859 // Get default input port definition. | |
860 ResetPortHeader(*this, &input_port_def); | |
861 input_port_def.nPortIndex = input_port_; | |
862 omxresult = OMX_GetParameter(component_handle_, | |
863 OMX_IndexParamPortDefinition, | |
864 &input_port_def); | |
865 if (omxresult != OMX_ErrorNone) { | |
866 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) " | |
867 << "for input port failed"; | |
868 return false; | |
869 } | |
870 if (OMX_DirInput != input_port_def.eDir) { | |
871 LOG(ERROR) << "Expected Input Port"; | |
872 return false; | |
873 } | |
874 | |
875 // Get default output port definition. | |
876 ResetPortHeader(*this, &output_port_def); | |
877 output_port_def.nPortIndex = output_port_; | |
878 omxresult = OMX_GetParameter(component_handle_, | |
879 OMX_IndexParamPortDefinition, | |
880 &output_port_def); | |
881 if (omxresult != OMX_ErrorNone) { | |
882 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) " | |
883 << "for output port failed"; | |
884 return false; | |
885 } | |
886 if (OMX_DirOutput != output_port_def.eDir) { | |
887 LOG(ERROR) << "Expected Output Port"; | |
888 return false; | |
889 } | |
890 | |
891 return configurator_->ConfigureIOPorts(component_handle_, | |
892 &input_port_def, &output_port_def); | |
893 } | |
894 | |
895 bool OmxCodec::CanEmptyBuffer() { | |
896 // We can call empty buffer while we are in executing or enabling / disabling | |
897 // the output port. | |
898 return (GetState() == kExecuting || GetState() == kPortSettingDisable || | |
899 GetState() == kPortSettingEnable) && | |
900 (GetNextState() == kExecuting || GetNextState() == kPortSettingDisable || | |
901 GetNextState() == kPortSettingEnable); | |
902 } | |
903 | |
904 bool OmxCodec::CanFillBuffer() { | |
905 // Make sure that we are staying in the executing state and end-of-stream | |
906 // has not been reached. | |
907 return GetState() == kExecuting && GetState() == GetNextState(); | |
908 } | |
909 | |
910 bool OmxCodec::CanAcceptInput() { | |
911 // We can't take input buffer when in error state. | |
912 // TODO(hclam): Reject when in stopped state. | |
913 return GetState() != kError; | |
914 } | |
915 | |
916 bool OmxCodec::CanAcceptOutput() { | |
917 // Don't output request when in error state. | |
918 // TODO(hclam): Reject when in stopped state. | |
919 return GetState() != kError; | |
920 } | |
921 | |
922 void OmxCodec::EmptyBufferCompleteTask(OMX_BUFFERHEADERTYPE* buffer) { | |
923 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
924 | |
925 if (!CanEmptyBuffer()) | |
926 return; | |
927 | |
928 scoped_refptr<Buffer> stored_buffer = processing_input_queue_.front(); | |
929 processing_input_queue_.pop(); | |
930 | |
931 DCHECK_EQ(const_cast<OMX_U8*>(stored_buffer.get()->GetData()), | |
932 buffer->pBuffer); | |
933 | |
934 feed_done_callback_->Run(stored_buffer); | |
935 | |
936 // Enqueue the available buffer beacuse the decoder has consumed it. | |
937 available_input_buffers_.push(buffer); | |
938 | |
939 // Try to feed more data into the decoder. | |
940 EmptyBufferTask(); | |
941 } | |
942 | |
943 void OmxCodec::EmptyBufferTask() { | |
944 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
945 | |
946 if (!CanEmptyBuffer()) | |
947 return; | |
948 | |
949 // Loop for all available input data and input buffer for the | |
950 // decoder. When input has reached EOS we need to stop. | |
951 while (!pending_input_queue_.empty() && | |
952 !available_input_buffers_.empty() && | |
953 !input_eos_) { | |
954 scoped_refptr<Buffer> buffer = pending_input_queue_.front(); | |
955 pending_input_queue_.pop(); | |
956 processing_input_queue_.push(buffer); | |
957 | |
958 OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front(); | |
959 available_input_buffers_.pop(); | |
960 | |
961 input_eos_ = buffer->IsEndOfStream(); | |
962 | |
963 // setup |omx_buffer|. | |
964 omx_buffer->nInputPortIndex = input_port_; | |
965 omx_buffer->nOffset = 0; | |
966 omx_buffer->nFlags = 0; | |
967 omx_buffer->pBuffer = const_cast<OMX_U8*>(buffer.get()->GetData()); | |
968 omx_buffer->nFilledLen = buffer.get()->GetDataSize(); | |
969 omx_buffer->nAllocLen = omx_buffer->nFilledLen; | |
970 omx_buffer->pAppPrivate = this; | |
971 omx_buffer->nFlags |= input_eos_ ? OMX_BUFFERFLAG_EOS : 0; | |
972 omx_buffer->nTimeStamp = buffer->GetTimestamp().InMilliseconds(); | |
973 | |
974 // Give this buffer to OMX. | |
975 OMX_ERRORTYPE ret = OMX_EmptyThisBuffer(component_handle_, omx_buffer); | |
976 if (ret != OMX_ErrorNone) { | |
977 LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << ret; | |
978 StateTransitionTask(kError); | |
979 return; | |
980 } | |
981 } | |
982 } | |
983 | |
984 void OmxCodec::FillBufferCompleteTask(OMX_BUFFERHEADERTYPE* buffer) { | |
985 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
986 | |
987 // If we are not in a right state to receive this buffer then return | |
988 // immediately. | |
989 // This condition is hit when we disable the output port and we are | |
990 // not in executing state. In that case ignore the buffer. | |
991 // TODO(hclam): We should count the number of buffers received with | |
992 // this condition to make sure the disable command has completed. | |
993 if (!CanFillBuffer()) | |
994 return; | |
995 | |
996 // This buffer is received with decoded frame. Enqueue it and make it | |
997 // ready to be consumed by reads. | |
998 int buffer_id = kEosBuffer; | |
999 for (size_t i = 0; output_buffers_.size(); ++i) { | |
1000 if (output_buffers_[i] == buffer) { | |
1001 buffer_id = i; | |
1002 break; | |
1003 } | |
1004 } | |
1005 | |
1006 // If the buffer received from the component doesn't exist in our | |
1007 // list then we have an error. | |
1008 if (buffer_id == kEosBuffer) { | |
1009 LOG(ERROR) << "Received an unknown output buffer"; | |
1010 StateTransitionTask(kError); | |
1011 return; | |
1012 } | |
1013 | |
1014 // Determine if the buffer received is a end-of-stream buffer. If | |
1015 // the condition is true then assign a EOS id to the buffer. | |
1016 if (buffer->nFlags & OMX_BUFFERFLAG_EOS || !buffer->nFilledLen) { | |
1017 buffer_id = kEosBuffer; | |
1018 output_eos_ = true; | |
1019 } | |
1020 output_buffers_ready_.push(buffer_id); | |
1021 | |
1022 // Try to fulfill one read request. | |
1023 FulfillOneRead(); | |
1024 } | |
1025 | |
1026 void OmxCodec::FulfillOneRead() { | |
1027 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1028 | |
1029 if (!output_buffers_ready_.empty()) { | |
1030 int buffer_id = output_buffers_ready_.front(); | |
1031 output_buffers_ready_.pop(); | |
1032 | |
1033 // If the buffer is real then save it to the in-use list. | |
1034 // Otherwise if it is an end-of-stream buffer then just drop it. | |
1035 if (buffer_id != kEosBuffer) { | |
1036 fill_done_callback_->Run(output_buffers_[buffer_id]); | |
1037 BufferUsedCallback(buffer_id); //hack, we will change this really soon. | |
1038 } else { | |
1039 fill_done_callback_->Run(static_cast<OMX_BUFFERHEADERTYPE*>(NULL)); | |
1040 } | |
1041 } | |
1042 } | |
1043 | |
1044 void OmxCodec::BufferUsedCallback(int buffer_id) { | |
1045 // If this method is called on the message loop where OmxCodec belongs, we | |
1046 // execute the task directly to save posting another task. | |
1047 if (message_loop_ == MessageLoop::current()) { | |
1048 BufferUsedTask(buffer_id); | |
1049 return; | |
1050 } | |
1051 | |
1052 message_loop_->PostTask( | |
1053 FROM_HERE, | |
1054 NewRunnableMethod(this, &OmxCodec::BufferUsedTask, buffer_id)); | |
1055 } | |
1056 | |
1057 // Handling end-of-stream: | |
1058 // Note that after we first receive the end-of-stream, we'll continue | |
1059 // to call FillThisBuffer() with the next buffer receievd from | |
1060 // OmxOutputSink. The end result is we'll call at most | |
1061 // |output_buffer_count_| of FillThisBuffer() that are expected to | |
1062 // receive end-of-stream buffers from OpenMAX. | |
1063 // It is possible to not submit FillThisBuffer() after the first | |
1064 // end-of-stream buffer is received from OpenMAX, but that will complicate | |
1065 // the logic and so we rely on OpenMAX to do the right thing. | |
1066 void OmxCodec::BufferUsedTask(int buffer_id) { | |
1067 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1068 | |
1069 // Make sure an end-of-stream buffer id is not received here. | |
1070 CHECK(buffer_id != kEosBuffer); | |
1071 | |
1072 // We'll try to issue more FillThisBuffer() to the decoder. | |
1073 // If we can't do it now then just return. | |
1074 if (!CanFillBuffer()) | |
1075 return; | |
1076 | |
1077 CHECK(buffer_id >= 0 && | |
1078 buffer_id < static_cast<int>(output_buffers_.size())); | |
1079 OMX_BUFFERHEADERTYPE* omx_buffer = output_buffers_[buffer_id]; | |
1080 | |
1081 omx_buffer->nOutputPortIndex = output_port_; | |
1082 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; | |
1083 omx_buffer->pAppPrivate = this; | |
1084 OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer); | |
1085 if (OMX_ErrorNone != ret) { | |
1086 LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret; | |
1087 StateTransitionTask(kError); | |
1088 return; | |
1089 } | |
1090 } | |
1091 | |
1092 void OmxCodec::InitialEmptyBuffer() { | |
1093 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1094 | |
1095 if (!CanEmptyBuffer()) | |
1096 return; | |
1097 | |
1098 // Use EmptyBuffer() to use available input buffers to feed decoder. | |
1099 EmptyBufferTask(); | |
1100 } | |
1101 | |
1102 void OmxCodec::InitialFillBuffer() { | |
1103 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1104 | |
1105 if (!CanFillBuffer()) | |
1106 return; | |
1107 | |
1108 // Ask the decoder to fill the output buffers. | |
1109 for (size_t i = 0; i < output_buffers_.size(); ++i) { | |
1110 OMX_BUFFERHEADERTYPE* omx_buffer = output_buffers_[i]; | |
1111 omx_buffer->nOutputPortIndex = output_port_; | |
1112 // Need to clear the EOS flag. | |
1113 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; | |
1114 omx_buffer->pAppPrivate = this; | |
1115 OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer); | |
1116 | |
1117 if (OMX_ErrorNone != ret) { | |
1118 LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret; | |
1119 StateTransitionTask(kError); | |
1120 return; | |
1121 } | |
1122 } | |
1123 } | |
1124 | |
1125 void OmxCodec::EventHandlerInternal(OMX_HANDLETYPE component, | |
1126 OMX_EVENTTYPE event, | |
1127 OMX_U32 data1, | |
1128 OMX_U32 data2, | |
1129 OMX_PTR event_data) { | |
1130 switch(event) { | |
1131 case OMX_EventCmdComplete: { | |
1132 // If the last command was successful, we have completed | |
1133 // a state transition. So notify that we have done it | |
1134 // accordingly. | |
1135 OMX_COMMANDTYPE cmd = static_cast<OMX_COMMANDTYPE>(data1); | |
1136 if (cmd == OMX_CommandPortEnable) { | |
1137 PostDoneStateTransitionTask(); | |
1138 } else if (cmd == OMX_CommandPortDisable) { | |
1139 PostDoneStateTransitionTask(); | |
1140 } else if (cmd == OMX_CommandStateSet) { | |
1141 PostDoneStateTransitionTask(); | |
1142 } else { | |
1143 LOG(ERROR) << "Unknown command completed\n"; | |
1144 } | |
1145 break; | |
1146 } | |
1147 case OMX_EventError: | |
1148 if (OMX_ErrorInvalidState == (OMX_ERRORTYPE)data1) { | |
1149 // TODO(hclam): what to do here? | |
1150 } | |
1151 PostStateTransitionTask(kError); | |
1152 break; | |
1153 case OMX_EventPortSettingsChanged: | |
1154 PostStateTransitionTask(kPortSettingDisable); | |
1155 break; | |
1156 default: | |
1157 LOG(ERROR) << "Warning - Unknown event received\n"; | |
1158 break; | |
1159 } | |
1160 } | |
1161 | |
1162 void OmxCodec::EmptyBufferCallbackInternal( | |
1163 OMX_HANDLETYPE component, | |
1164 OMX_BUFFERHEADERTYPE* buffer) { | |
1165 message_loop_->PostTask( | |
1166 FROM_HERE, | |
1167 NewRunnableMethod(this, | |
1168 &OmxCodec::EmptyBufferCompleteTask, buffer)); | |
1169 } | |
1170 | |
1171 void OmxCodec::FillBufferCallbackInternal( | |
1172 OMX_HANDLETYPE component, | |
1173 OMX_BUFFERHEADERTYPE* buffer) { | |
1174 message_loop_->PostTask( | |
1175 FROM_HERE, | |
1176 NewRunnableMethod(this, | |
1177 &OmxCodec::FillBufferCompleteTask, buffer)); | |
1178 } | |
1179 | |
1180 // static | |
1181 OMX_ERRORTYPE OmxCodec::EventHandler(OMX_HANDLETYPE component, | |
1182 OMX_PTR priv_data, | |
1183 OMX_EVENTTYPE event, | |
1184 OMX_U32 data1, | |
1185 OMX_U32 data2, | |
1186 OMX_PTR event_data) { | |
1187 OmxCodec* decoder = static_cast<OmxCodec*>(priv_data); | |
1188 decoder->EventHandlerInternal(component, event, data1, data2, event_data); | |
1189 return OMX_ErrorNone; | |
1190 } | |
1191 | |
1192 // static | |
1193 OMX_ERRORTYPE OmxCodec::EmptyBufferCallback( | |
1194 OMX_HANDLETYPE component, | |
1195 OMX_PTR priv_data, | |
1196 OMX_BUFFERHEADERTYPE* buffer) { | |
1197 OmxCodec* decoder = static_cast<OmxCodec*>(priv_data); | |
1198 decoder->EmptyBufferCallbackInternal(component, buffer); | |
1199 return OMX_ErrorNone; | |
1200 } | |
1201 | |
1202 // static | |
1203 OMX_ERRORTYPE OmxCodec::FillBufferCallback( | |
1204 OMX_HANDLETYPE component, | |
1205 OMX_PTR priv_data, | |
1206 OMX_BUFFERHEADERTYPE* buffer) { | |
1207 OmxCodec* decoder = static_cast<OmxCodec*>(priv_data); | |
1208 decoder->FillBufferCallbackInternal(component, buffer); | |
1209 return OMX_ErrorNone; | |
1210 } | |
1211 | |
1212 } // namespace media | |
OLD | NEW |