Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: media/omx/omx_codec.cc

Issue 2255005: move from omx_codec to new omx_video_decode_engine... (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: remove omx_code, update omx_test to use omx engine Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/omx/omx_codec.h ('k') | media/omx/omx_codec_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « media/omx/omx_codec.h ('k') | media/omx/omx_codec_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698