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 // This class interacts with OmxCodec and the VideoDecoderImpl | |
6 // in the media pipeline. | |
7 // | |
8 // THREADING SEMANTICS | |
9 // | |
10 // This class is created by OmxVideoDecoder and lives on the thread | |
11 // that it lives. This class is given the message loop | |
12 // for the above thread. The OMX callbacks are guaranteed to be | |
13 // executed on the hosting message loop. Because of that there's no need | |
14 // for locking anywhere. | |
15 | |
16 #include "media/video/omx_video_decode_engine.h" | |
17 | |
18 #include "base/logging.h" | |
19 #include "base/message_loop.h" | |
20 #include "base/string_util.h" | |
21 #include "media/base/buffers.h" | |
22 #include "media/base/pipeline.h" | |
23 | |
24 namespace media { | |
25 | |
26 OmxVideoDecodeEngine::OmxVideoDecodeEngine() | |
27 : width_(16), | |
28 height_(16), | |
29 message_loop_(NULL), | |
30 input_buffer_count_(0), | |
31 input_buffer_size_(0), | |
32 input_port_(0), | |
33 input_buffers_at_component_(0), | |
34 input_pending_request_(0), | |
35 input_queue_has_eos_(false), | |
36 input_has_fed_eos_(false), | |
37 input_port_flushed_(false), | |
38 output_buffer_count_(0), | |
39 output_buffer_size_(0), | |
40 output_port_(0), | |
41 output_buffers_at_component_(0), | |
42 output_pending_request_(0), | |
43 output_eos_(false), | |
44 output_port_flushed_(false), | |
45 il_state_(kIlNone), | |
46 expected_il_state_(kIlNone), | |
47 client_state_(kClientNotInitialized), | |
48 component_handle_(NULL), | |
49 need_free_input_buffers_(false), | |
50 need_free_output_buffers_(false), | |
51 flush_pending_(false), | |
52 output_frames_allocated_(false), | |
53 need_setup_output_port_(false) { | |
54 // TODO(wjia): change uses_egl_image_ to runtime setup | |
55 #if ENABLE_EGLIMAGE == 1 | |
56 uses_egl_image_ = true; | |
57 DLOG(INFO) << "Uses egl image for output"; | |
58 #else | |
59 uses_egl_image_ = false; | |
60 DLOG(INFO) << "Uses system memory for output"; | |
61 #endif | |
62 } | |
63 | |
64 OmxVideoDecodeEngine::~OmxVideoDecodeEngine() { | |
65 DCHECK(client_state_ == kClientNotInitialized || | |
66 client_state_ == kClientStopped); | |
67 DCHECK_EQ(il_state_, kIlNone); | |
68 DCHECK_EQ(0u, input_buffers_.size()); | |
69 DCHECK(free_input_buffers_.empty()); | |
70 DCHECK(available_input_buffers_.empty()); | |
71 DCHECK_EQ(0, input_buffers_at_component_); | |
72 DCHECK_EQ(0, output_buffers_at_component_); | |
73 DCHECK(output_frames_.empty()); | |
74 } | |
75 | |
76 template <typename T> | |
77 static void ResetParamHeader(const OmxVideoDecodeEngine& dec, T* param) { | |
78 memset(param, 0, sizeof(T)); | |
79 param->nVersion.nVersion = dec.current_omx_spec_version(); | |
80 param->nSize = sizeof(T); | |
81 } | |
82 | |
83 void OmxVideoDecodeEngine::Initialize( | |
84 MessageLoop* message_loop, | |
85 VideoDecodeEngine::EventHandler* event_handler, | |
86 VideoDecodeContext* context, | |
87 const VideoDecoderConfig& config) { | |
88 DCHECK_EQ(message_loop, MessageLoop::current()); | |
89 | |
90 message_loop_ = message_loop; | |
91 event_handler_ = event_handler; | |
92 | |
93 width_ = config.width(); | |
94 height_ = config.height(); | |
95 | |
96 // TODO(wjia): Find the right way to determine the codec type. | |
97 OmxConfigurator::MediaFormat input_format, output_format; | |
98 memset(&input_format, 0, sizeof(input_format)); | |
99 memset(&output_format, 0, sizeof(output_format)); | |
100 input_format.codec = OmxConfigurator::kCodecH264; | |
101 output_format.codec = OmxConfigurator::kCodecRaw; | |
102 configurator_.reset( | |
103 new OmxDecoderConfigurator(input_format, output_format)); | |
104 | |
105 // TODO(jiesun): We already ensure Initialize() is called in thread context, | |
106 // We should try to merge the following function into this function. | |
107 client_state_ = kClientInitializing; | |
108 InitializeTask(); | |
109 | |
110 VideoCodecInfo info; | |
111 // TODO(jiesun): ridiculous, we never fail initialization? | |
112 info.success = true; | |
113 info.provides_buffers = !uses_egl_image_; | |
114 info.stream_info.surface_type = | |
115 uses_egl_image_ ? VideoFrame::TYPE_GL_TEXTURE | |
116 : VideoFrame::TYPE_SYSTEM_MEMORY; | |
117 info.stream_info.surface_format = GetSurfaceFormat(); | |
118 info.stream_info.surface_width = config.width(); | |
119 info.stream_info.surface_height = config.height(); | |
120 event_handler_->OnInitializeComplete(info); | |
121 } | |
122 | |
123 // This method handles only input buffer, without coupling with output | |
124 void OmxVideoDecodeEngine::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { | |
125 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
126 DCHECK(!free_input_buffers_.empty()); | |
127 DCHECK_GT(input_pending_request_, 0); | |
128 | |
129 --input_pending_request_; | |
130 | |
131 if (!CanAcceptInput()) { | |
132 FinishEmptyBuffer(buffer); | |
133 return; | |
134 } | |
135 | |
136 if (buffer->IsEndOfStream()) { | |
137 DLOG(INFO) << "Input queue has EOS"; | |
138 input_queue_has_eos_ = true; | |
139 } | |
140 | |
141 OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front(); | |
142 free_input_buffers_.pop(); | |
143 | |
144 // setup |omx_buffer|. | |
145 omx_buffer->pBuffer = const_cast<OMX_U8*>(buffer->GetData()); | |
146 omx_buffer->nFilledLen = buffer->GetDataSize(); | |
147 omx_buffer->nAllocLen = omx_buffer->nFilledLen; | |
148 if (input_queue_has_eos_) | |
149 omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS; | |
150 else | |
151 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; | |
152 omx_buffer->nTimeStamp = buffer->GetTimestamp().InMicroseconds(); | |
153 omx_buffer->pAppPrivate = buffer.get(); | |
154 buffer->AddRef(); | |
155 available_input_buffers_.push(omx_buffer); | |
156 | |
157 // Try to feed buffers into the decoder. | |
158 EmptyBufferTask(); | |
159 | |
160 if (flush_pending_ && input_pending_request_ == 0) | |
161 StartFlush(); | |
162 } | |
163 | |
164 void OmxVideoDecodeEngine::Flush() { | |
165 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
166 DCHECK_EQ(il_state_, kIlExecuting); | |
167 | |
168 if (il_state_ != kIlExecuting) { | |
169 event_handler_->OnFlushComplete(); | |
170 return; | |
171 } | |
172 | |
173 client_state_ = kClientFlushing; | |
174 expected_il_state_ = kIlPause; | |
175 OnStateSetEventFunc = &OmxVideoDecodeEngine::PauseFromExecuting; | |
176 TransitionToState(OMX_StatePause); | |
177 } | |
178 | |
179 void OmxVideoDecodeEngine::PauseFromExecuting(OMX_STATETYPE state) { | |
180 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
181 | |
182 OnStateSetEventFunc = NULL; | |
183 il_state_ = kIlPause; | |
184 | |
185 if (input_pending_request_ == 0) | |
186 StartFlush(); | |
187 else | |
188 flush_pending_ = true; | |
189 } | |
190 | |
191 void OmxVideoDecodeEngine::StartFlush() { | |
192 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
193 DCHECK_EQ(input_pending_request_, 0); | |
194 DLOG(INFO) << "StartFlush"; | |
195 | |
196 while (!available_input_buffers_.empty()) | |
197 available_input_buffers_.pop(); | |
198 | |
199 flush_pending_ = false; | |
200 | |
201 // Flush input port first. | |
202 OnFlushEventFunc = &OmxVideoDecodeEngine::PortFlushDone; | |
203 OMX_ERRORTYPE omxresult; | |
204 omxresult = OMX_SendCommand(component_handle_, | |
205 OMX_CommandFlush, | |
206 input_port_, 0); | |
207 } | |
208 | |
209 bool OmxVideoDecodeEngine::InputPortFlushed() { | |
210 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
211 DCHECK_EQ(client_state_, kClientFlushing); | |
212 // Port flushed is defined by OpenMAX component had signal flush done and | |
213 // We had all buffers returned from demuxer and OpenMAX component. | |
214 int free_input_size = static_cast<int>(free_input_buffers_.size()); | |
215 return input_port_flushed_ && free_input_size == input_buffer_count_; | |
216 } | |
217 | |
218 bool OmxVideoDecodeEngine::OutputPortFlushed() { | |
219 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
220 DCHECK_EQ(client_state_, kClientFlushing); | |
221 // Port flushed is defined by OpenMAX component had signal flush done and | |
222 // We had all buffers returned from renderer and OpenMAX component. | |
223 return output_port_flushed_ && output_pending_request_ == 0; | |
224 } | |
225 | |
226 void OmxVideoDecodeEngine::ComponentFlushDone() { | |
227 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
228 DLOG(INFO) << "Component had been flushed!"; | |
229 | |
230 if (input_port_flushed_ && output_port_flushed_) { | |
231 event_handler_->OnFlushComplete(); | |
232 input_port_flushed_ = false; | |
233 output_port_flushed_ = false; | |
234 } | |
235 } | |
236 | |
237 void OmxVideoDecodeEngine::PortFlushDone(int port) { | |
238 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
239 DCHECK_NE(port, static_cast<int>(OMX_ALL)); | |
240 | |
241 if (port == input_port_) { | |
242 DLOG(INFO) << "Input Port had been flushed"; | |
243 DCHECK_EQ(input_buffers_at_component_, 0); | |
244 input_port_flushed_ = true; | |
245 // Flush output port next. | |
246 OMX_ERRORTYPE omxresult; | |
247 omxresult = OMX_SendCommand(component_handle_, | |
248 OMX_CommandFlush, | |
249 output_port_, 0); | |
250 return; | |
251 } | |
252 | |
253 if (port == output_port_) { | |
254 DLOG(INFO) << "Output Port had been flushed"; | |
255 DCHECK_EQ(output_buffers_at_component_, 0); | |
256 | |
257 output_port_flushed_ = true; | |
258 } | |
259 | |
260 if (kClientFlushing == client_state_ && | |
261 InputPortFlushed() && OutputPortFlushed()) | |
262 ComponentFlushDone(); | |
263 } | |
264 | |
265 void OmxVideoDecodeEngine::Seek() { | |
266 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
267 | |
268 DCHECK(client_state_ == kClientFlushing || // After a flush | |
269 client_state_ == kClientInitializing); // After an initialize. | |
270 | |
271 if (client_state_ == kClientFlushing) { | |
272 InitialReadBuffer(); | |
273 OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateExecuting; | |
274 TransitionToState(OMX_StateExecuting); | |
275 } | |
276 | |
277 event_handler_->OnSeekComplete(); | |
278 } | |
279 | |
280 int OmxVideoDecodeEngine::current_omx_spec_version() const { | |
281 return 0x00000101; | |
282 } | |
283 | |
284 VideoFrame::Format OmxVideoDecodeEngine::GetSurfaceFormat() const { | |
285 // TODO(jiesun): Both OmxHeaderType and EGLImage surface type could have | |
286 // different surface formats. | |
287 return uses_egl_image_ ? VideoFrame::RGBA : VideoFrame::YV12; | |
288 } | |
289 | |
290 void OmxVideoDecodeEngine::Uninitialize() { | |
291 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
292 | |
293 if (client_state_ == kClientError) { | |
294 OnStopDone(); | |
295 return; | |
296 } | |
297 | |
298 // TODO(wjia): add more state checking | |
299 if (kClientRunning == client_state_ || kClientFlushing == client_state_) { | |
300 client_state_ = kClientStopping; | |
301 DeinitFromExecuting(OMX_StateExecuting); | |
302 } | |
303 | |
304 // TODO(wjia): When FillThisBuffer() is added, engine state should be | |
305 // kStopping here. engine state should be set to kStopped in OnStopDone(); | |
306 // client_state_ = kClientStopping; | |
307 } | |
308 | |
309 void OmxVideoDecodeEngine::FinishEmptyBuffer(scoped_refptr<Buffer> buffer) { | |
310 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
311 | |
312 if (!input_queue_has_eos_) { | |
313 event_handler_->ProduceVideoSample(buffer); | |
314 ++input_pending_request_; | |
315 } | |
316 } | |
317 | |
318 void OmxVideoDecodeEngine::FinishFillBuffer(OMX_BUFFERHEADERTYPE* buffer) { | |
319 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
320 DCHECK(buffer); | |
321 | |
322 scoped_refptr<VideoFrame> frame; | |
323 frame = static_cast<VideoFrame*>(buffer->pAppPrivate); | |
324 | |
325 // We should not flush buffer to renderer during decoder flushing if decoder | |
326 // provides the buffer allocator. | |
327 if (kClientFlushing == client_state_ && !uses_egl_image_) return; | |
328 | |
329 PipelineStatistics statistics; | |
330 statistics.video_bytes_decoded = buffer->nFilledLen; | |
331 | |
332 frame->SetTimestamp(base::TimeDelta::FromMicroseconds(buffer->nTimeStamp)); | |
333 frame->SetDuration(frame->GetTimestamp() - last_pts_); | |
334 last_pts_ = frame->GetTimestamp(); | |
335 event_handler_->ConsumeVideoFrame(frame, statistics); | |
336 output_pending_request_--; | |
337 } | |
338 | |
339 void OmxVideoDecodeEngine::OnStopDone() { | |
340 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
341 | |
342 event_handler_->OnUninitializeComplete(); | |
343 } | |
344 | |
345 // Function sequence for initializing | |
346 void OmxVideoDecodeEngine::InitializeTask() { | |
347 DCHECK_EQ(il_state_, kIlNone); | |
348 | |
349 il_state_ = kIlNone; | |
350 expected_il_state_ = kIlLoaded; | |
351 output_port_state_ = kPortEnabled; | |
352 if (!CreateComponent()) { | |
353 StopOnError(); | |
354 return; | |
355 } | |
356 il_state_ = kIlLoaded; | |
357 | |
358 // TODO(wjia): Disabling output port is to work around racing condition | |
359 // due to bug in some vendor's driver. But it hits another bug. | |
360 // So temporarily fall back to enabling output port. Still keep the code | |
361 // disabling output port here. | |
362 // No need to respond to this PortDisable event | |
363 // OnPortDisableEventFunc = NULL; | |
364 // ChangePort(OMX_CommandPortDisable, output_port_); | |
365 // if (kClientError == client_state_) { | |
366 // StopOnError(); | |
367 // return; | |
368 // } | |
369 // output_port_state_ = kPortDisabled; | |
370 | |
371 // Transition component to Idle state | |
372 OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateIdle; | |
373 if (!TransitionToState(OMX_StateIdle)) { | |
374 StopOnError(); | |
375 return; | |
376 } | |
377 expected_il_state_ = kIlIdle; | |
378 | |
379 if (!AllocateInputBuffers()) { | |
380 LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error"; | |
381 client_state_ = kClientError; | |
382 StopOnError(); | |
383 return; | |
384 } | |
385 if (!AllocateOutputBuffers()) { | |
386 LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; | |
387 client_state_ = kClientError; | |
388 return; | |
389 } | |
390 } | |
391 | |
392 // Sequence of actions in this transition: | |
393 // | |
394 // 1. Initialize OMX (To be removed.) | |
395 // 2. Map role name to component name. | |
396 // 3. Get handle of the OMX component | |
397 // 4. Get the port information. | |
398 // 5. Set role for the component. | |
399 // 6. Input/output ports media format configuration. | |
400 // 7. Obtain the information about the input port. | |
401 // 8. Obtain the information about the output port. | |
402 bool OmxVideoDecodeEngine::CreateComponent() { | |
403 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
404 | |
405 static OMX_CALLBACKTYPE callback = { | |
406 &OmxVideoDecodeEngine::EventHandler, | |
407 &OmxVideoDecodeEngine::EmptyBufferCallback, | |
408 &OmxVideoDecodeEngine::FillBufferCallback | |
409 }; | |
410 | |
411 // 1. Initialize the OpenMAX Core. | |
412 // TODO(hclam): move this out. | |
413 OMX_ERRORTYPE omxresult = OMX_Init(); | |
414 if (omxresult != OMX_ErrorNone) { | |
415 LOG(ERROR) << "Failed to init OpenMAX core"; | |
416 client_state_ = kClientError; | |
417 return false; | |
418 } | |
419 | |
420 // 2. Map role name to component name. | |
421 std::string role_name = configurator_->GetRoleName(); | |
422 OMX_U32 roles = 0; | |
423 omxresult = OMX_GetComponentsOfRole( | |
424 const_cast<OMX_STRING>(role_name.c_str()), | |
425 &roles, 0); | |
426 if (omxresult != OMX_ErrorNone || roles == 0) { | |
427 LOG(ERROR) << "Unsupported Role: " << role_name.c_str(); | |
428 client_state_ = kClientError; | |
429 return false; | |
430 } | |
431 const OMX_U32 kMaxRolePerComponent = 20; | |
432 CHECK(roles < kMaxRolePerComponent); | |
433 | |
434 OMX_U8** component_names = new OMX_U8*[roles]; | |
435 const int kMaxComponentNameLength = 256; | |
436 for (size_t i = 0; i < roles; ++i) | |
437 component_names[i] = new OMX_U8[kMaxComponentNameLength]; | |
438 | |
439 omxresult = OMX_GetComponentsOfRole( | |
440 const_cast<OMX_STRING>(role_name.c_str()), | |
441 &roles, component_names); | |
442 | |
443 // Use first component only. Copy the name of the first component | |
444 // so that we could free the memory. | |
445 std::string component_name; | |
446 if (omxresult == OMX_ErrorNone) | |
447 component_name = reinterpret_cast<char*>(component_names[0]); | |
448 | |
449 for (size_t i = 0; i < roles; ++i) | |
450 delete [] component_names[i]; | |
451 delete [] component_names; | |
452 | |
453 if (omxresult != OMX_ErrorNone || roles == 0) { | |
454 LOG(ERROR) << "Unsupported Role: " << role_name.c_str(); | |
455 client_state_ = kClientError; | |
456 return false; | |
457 } | |
458 | |
459 // 3. Get the handle to the component. After OMX_GetHandle(), | |
460 // the component is in loaded state. | |
461 OMX_STRING component = const_cast<OMX_STRING>(component_name.c_str()); | |
462 omxresult = OMX_GetHandle(&component_handle_, component, this, &callback); | |
463 if (omxresult != OMX_ErrorNone) { | |
464 LOG(ERROR) << "Failed to Load the component: " << component; | |
465 client_state_ = kClientError; | |
466 return false; | |
467 } | |
468 | |
469 // 4. Get the port information. This will obtain information about the | |
470 // number of ports and index of the first port. | |
471 OMX_PORT_PARAM_TYPE port_param; | |
472 ResetParamHeader(*this, &port_param); | |
473 omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit, | |
474 &port_param); | |
475 if (omxresult != OMX_ErrorNone) { | |
476 LOG(ERROR) << "Failed to get Port Param"; | |
477 client_state_ = kClientError; | |
478 return false; | |
479 } | |
480 input_port_ = port_param.nStartPortNumber; | |
481 output_port_ = input_port_ + 1; | |
482 | |
483 // 5. Set role for the component because our component could | |
484 // have multiple roles. | |
485 OMX_PARAM_COMPONENTROLETYPE role_type; | |
486 ResetParamHeader(*this, &role_type); | |
487 base::strlcpy(reinterpret_cast<char*>(role_type.cRole), | |
488 role_name.c_str(), | |
489 OMX_MAX_STRINGNAME_SIZE); | |
490 role_type.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; | |
491 omxresult = OMX_SetParameter(component_handle_, | |
492 OMX_IndexParamStandardComponentRole, | |
493 &role_type); | |
494 if (omxresult != OMX_ErrorNone) { | |
495 LOG(ERROR) << "Failed to Set Role"; | |
496 client_state_ = kClientError; | |
497 return false; | |
498 } | |
499 | |
500 // 6. Input/output ports media format configuration. | |
501 if (!ConfigureIOPorts()) { | |
502 LOG(ERROR) << "Media format configurations failed"; | |
503 client_state_ = kClientError; | |
504 return false; | |
505 } | |
506 | |
507 // 7. Obtain the information about the input port. | |
508 // This will have the new mini buffer count in |port_format.nBufferCountMin|. | |
509 // Save this value to input_buf_count. | |
510 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
511 ResetParamHeader(*this, &port_format); | |
512 port_format.nPortIndex = input_port_; | |
513 omxresult = OMX_GetParameter(component_handle_, | |
514 OMX_IndexParamPortDefinition, | |
515 &port_format); | |
516 if (omxresult != OMX_ErrorNone) { | |
517 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
518 client_state_ = kClientError; | |
519 return false; | |
520 } | |
521 if (OMX_DirInput != port_format.eDir) { | |
522 LOG(ERROR) << "Expected input port"; | |
523 client_state_ = kClientError; | |
524 return false; | |
525 } | |
526 input_buffer_count_ = port_format.nBufferCountActual; | |
527 input_buffer_size_ = port_format.nBufferSize; | |
528 | |
529 // 8. Obtain the information about the output port. | |
530 ResetParamHeader(*this, &port_format); | |
531 port_format.nPortIndex = output_port_; | |
532 omxresult = OMX_GetParameter(component_handle_, | |
533 OMX_IndexParamPortDefinition, | |
534 &port_format); | |
535 if (omxresult != OMX_ErrorNone) { | |
536 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
537 client_state_ = kClientError; | |
538 return false; | |
539 } | |
540 if (OMX_DirOutput != port_format.eDir) { | |
541 LOG(ERROR) << "Expect Output Port"; | |
542 client_state_ = kClientError; | |
543 return false; | |
544 } | |
545 | |
546 // TODO(wjia): use same buffer recycling for EGLImage and system memory. | |
547 // Override buffer count when EGLImage is used. | |
548 if (uses_egl_image_) { | |
549 // TODO(wjia): remove hard-coded value | |
550 port_format.nBufferCountActual = port_format.nBufferCountMin = | |
551 output_buffer_count_ = 4; | |
552 | |
553 omxresult = OMX_SetParameter(component_handle_, | |
554 OMX_IndexParamPortDefinition, | |
555 &port_format); | |
556 if (omxresult != OMX_ErrorNone) { | |
557 LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) failed"; | |
558 client_state_ = kClientError; | |
559 return false; | |
560 } | |
561 } else { | |
562 output_buffer_count_ = port_format.nBufferCountActual; | |
563 } | |
564 output_buffer_size_ = port_format.nBufferSize; | |
565 | |
566 return true; | |
567 } | |
568 | |
569 // Event callback during initialization to handle DoneStateSet to idle | |
570 void OmxVideoDecodeEngine::DoneSetStateIdle(OMX_STATETYPE state) { | |
571 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
572 DCHECK_EQ(client_state_, kClientInitializing); | |
573 DCHECK_EQ(OMX_StateIdle, state); | |
574 DLOG(INFO) << "OMX video decode engine is in Idle"; | |
575 | |
576 il_state_ = kIlIdle; | |
577 | |
578 // start reading bit stream | |
579 InitialReadBuffer(); | |
580 OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateExecuting; | |
581 if (!TransitionToState(OMX_StateExecuting)) { | |
582 StopOnError(); | |
583 return; | |
584 } | |
585 expected_il_state_ = kIlExecuting; | |
586 } | |
587 | |
588 // Event callback during initialization to handle DoneStateSet to executing | |
589 void OmxVideoDecodeEngine::DoneSetStateExecuting(OMX_STATETYPE state) { | |
590 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
591 DCHECK(client_state_ == kClientInitializing || | |
592 client_state_ == kClientFlushing); | |
593 DCHECK_EQ(OMX_StateExecuting, state); | |
594 DLOG(INFO) << "OMX video decode engine is in Executing"; | |
595 | |
596 il_state_ = kIlExecuting; | |
597 client_state_ = kClientRunning; | |
598 OnStateSetEventFunc = NULL; | |
599 EmptyBufferTask(); | |
600 InitialFillBuffer(); | |
601 if (kClientError == client_state_) { | |
602 StopOnError(); | |
603 return; | |
604 } | |
605 } | |
606 | |
607 // Function for receiving output buffers. Hookup for buffer recycling | |
608 // and outside allocator. | |
609 void OmxVideoDecodeEngine::ProduceVideoFrame( | |
610 scoped_refptr<VideoFrame> video_frame) { | |
611 DCHECK(video_frame.get() && !video_frame->IsEndOfStream()); | |
612 output_pending_request_++; | |
613 | |
614 PipelineStatistics statistics; | |
615 | |
616 if (!CanAcceptOutput()) { | |
617 if (uses_egl_image_) { // return it to owner. | |
618 output_pending_request_--; | |
619 event_handler_->ConsumeVideoFrame(video_frame, statistics); | |
620 } | |
621 return; | |
622 } | |
623 | |
624 OMX_BUFFERHEADERTYPE* omx_buffer = FindOmxBuffer(video_frame); | |
625 if (omx_buffer) { | |
626 statistics.video_bytes_decoded = omx_buffer->nFilledLen; | |
627 | |
628 if (kClientRunning == client_state_) { | |
629 SendOutputBufferToComponent(omx_buffer); | |
630 } else if (kClientFlushing == client_state_) { | |
631 if (uses_egl_image_) { // return it to owner. | |
632 output_pending_request_--; | |
633 event_handler_->ConsumeVideoFrame(video_frame, statistics); | |
634 } | |
635 if (InputPortFlushed() && OutputPortFlushed()) | |
636 ComponentFlushDone(); | |
637 } | |
638 } else { | |
639 DCHECK(!output_frames_allocated_); | |
640 DCHECK(uses_egl_image_); | |
641 output_frames_.push_back(std::make_pair(video_frame, | |
642 static_cast<OMX_BUFFERHEADERTYPE*>(NULL))); | |
643 } | |
644 | |
645 DCHECK(static_cast<int>(output_frames_.size()) <= output_buffer_count_); | |
646 | |
647 if ((!output_frames_allocated_) && | |
648 static_cast<int>(output_frames_.size()) == output_buffer_count_) { | |
649 output_frames_allocated_ = true; | |
650 | |
651 if (need_setup_output_port_) { | |
652 SetupOutputPort(); | |
653 } | |
654 } | |
655 | |
656 if (kClientError == client_state_) { | |
657 StopOnError(); | |
658 return; | |
659 } | |
660 } | |
661 | |
662 // Reconfigure port | |
663 void OmxVideoDecodeEngine::OnPortSettingsChangedRun(int port, | |
664 OMX_INDEXTYPE index) { | |
665 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
666 DCHECK_EQ(client_state_, kClientRunning); | |
667 DCHECK_EQ(port, output_port_); | |
668 | |
669 // TODO(wjia): add buffer negotiation between decoder and renderer. | |
670 if (uses_egl_image_) { | |
671 DLOG(INFO) << "Port settings are changed"; | |
672 return; | |
673 } | |
674 | |
675 // TODO(wjia): remove this checking when all vendors observe same spec. | |
676 if (index > OMX_IndexComponentStartUnused) { | |
677 if (index != OMX_IndexParamPortDefinition) | |
678 return; | |
679 } | |
680 | |
681 OMX_PARAM_PORTDEFINITIONTYPE port_format; | |
682 ResetParamHeader(*this, &port_format); | |
683 port_format.nPortIndex = output_port_; | |
684 OMX_ERRORTYPE omxresult; | |
685 omxresult = OMX_GetParameter(component_handle_, | |
686 OMX_IndexParamPortDefinition, | |
687 &port_format); | |
688 if (omxresult != OMX_ErrorNone) { | |
689 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; | |
690 client_state_ = kClientError; | |
691 StopOnError(); | |
692 return; | |
693 } | |
694 if (OMX_DirOutput != port_format.eDir) { | |
695 LOG(ERROR) << "Expected Output Port"; | |
696 client_state_ = kClientError; | |
697 StopOnError(); | |
698 return; | |
699 } | |
700 | |
701 // Update the output format. | |
702 OmxConfigurator::MediaFormat output_format; | |
703 output_format.video_header.height = port_format.format.video.nFrameHeight; | |
704 output_format.video_header.width = port_format.format.video.nFrameWidth; | |
705 output_format.video_header.stride = port_format.format.video.nStride; | |
706 output_buffer_count_ = port_format.nBufferCountActual; | |
707 output_buffer_size_ = port_format.nBufferSize; | |
708 | |
709 if (kPortEnabled == output_port_state_) { | |
710 output_port_state_ = kPortDisabling; | |
711 OnPortDisableEventFunc = &OmxVideoDecodeEngine::OnPortDisableEventRun; | |
712 ChangePort(OMX_CommandPortDisable, output_port_); | |
713 if (kClientError == client_state_) { | |
714 StopOnError(); | |
715 return; | |
716 } | |
717 FreeOutputBuffers(); | |
718 } else { | |
719 OnPortDisableEventRun(output_port_); | |
720 } | |
721 } | |
722 | |
723 // Post output port disabling | |
724 void OmxVideoDecodeEngine::OnPortDisableEventRun(int port) { | |
725 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
726 DCHECK_EQ(client_state_, kClientRunning); | |
727 DCHECK_EQ(port, output_port_); | |
728 | |
729 output_port_state_ = kPortDisabled; | |
730 | |
731 // make sure all eglimages are available before enabling output port | |
732 if (output_frames_allocated_ || !uses_egl_image_) { | |
733 SetupOutputPort(); | |
734 if (kClientError == client_state_) { | |
735 StopOnError(); | |
736 return; | |
737 } | |
738 } else { | |
739 need_setup_output_port_ = true; | |
740 } | |
741 } | |
742 | |
743 // Enable output port and allocate buffers correspondingly | |
744 void OmxVideoDecodeEngine::SetupOutputPort() { | |
745 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
746 | |
747 need_setup_output_port_ = false; | |
748 | |
749 // Enable output port when necessary since the port could be waiting for | |
750 // buffers, instead of port reconfiguration. | |
751 if (kPortEnabled != output_port_state_) { | |
752 output_port_state_ = kPortEnabling; | |
753 OnPortEnableEventFunc = &OmxVideoDecodeEngine::OnPortEnableEventRun; | |
754 ChangePort(OMX_CommandPortEnable, output_port_); | |
755 if (kClientError == client_state_) { | |
756 return; | |
757 } | |
758 } | |
759 | |
760 // TODO(wjia): add state checking | |
761 // Update the ports in buffer if necessary | |
762 if (!AllocateOutputBuffers()) { | |
763 LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; | |
764 client_state_ = kClientError; | |
765 return; | |
766 } | |
767 } | |
768 | |
769 // Post output port enabling | |
770 void OmxVideoDecodeEngine::OnPortEnableEventRun(int port) { | |
771 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
772 DCHECK_EQ(port, output_port_); | |
773 DCHECK_EQ(client_state_, kClientRunning); | |
774 | |
775 output_port_state_ = kPortEnabled; | |
776 last_pts_ = base::TimeDelta::FromMilliseconds(0); | |
777 OnPortEnableEventFunc = NULL; | |
778 InitialFillBuffer(); | |
779 if (kClientError == client_state_) { | |
780 StopOnError(); | |
781 return; | |
782 } | |
783 } | |
784 | |
785 void OmxVideoDecodeEngine::DeinitFromExecuting(OMX_STATETYPE state) { | |
786 DCHECK_EQ(state, OMX_StateExecuting); | |
787 | |
788 DLOG(INFO) << "Deinit from Executing"; | |
789 OnStateSetEventFunc = &OmxVideoDecodeEngine::DeinitFromIdle; | |
790 TransitionToState(OMX_StateIdle); | |
791 expected_il_state_ = kIlIdle; | |
792 } | |
793 | |
794 void OmxVideoDecodeEngine::DeinitFromIdle(OMX_STATETYPE state) { | |
795 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
796 DCHECK_EQ(state, OMX_StateIdle); | |
797 | |
798 DLOG(INFO) << "Deinit from Idle"; | |
799 il_state_ = kIlIdle; | |
800 OnStateSetEventFunc = &OmxVideoDecodeEngine::DeinitFromLoaded; | |
801 TransitionToState(OMX_StateLoaded); | |
802 expected_il_state_ = kIlLoaded; | |
803 | |
804 if (!input_buffers_at_component_) | |
805 FreeInputBuffers(); | |
806 else | |
807 need_free_input_buffers_ = true; | |
808 | |
809 if (!output_buffers_at_component_) | |
810 FreeOutputBuffers(); | |
811 else | |
812 need_free_output_buffers_ = true; | |
813 } | |
814 | |
815 void OmxVideoDecodeEngine::DeinitFromLoaded(OMX_STATETYPE state) { | |
816 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
817 DCHECK_EQ(state, OMX_StateLoaded); | |
818 | |
819 DLOG(INFO) << "Deinit from Loaded"; | |
820 il_state_ = kIlLoaded; | |
821 if (component_handle_) { | |
822 OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_); | |
823 if (result != OMX_ErrorNone) | |
824 LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result; | |
825 component_handle_ = NULL; | |
826 } | |
827 il_state_ = expected_il_state_ = kIlNone; | |
828 | |
829 // kClientStopped is different from kClientNotInitialized. The former can't | |
830 // accept output buffers, while the latter can. | |
831 client_state_ = kClientStopped; | |
832 | |
833 OMX_Deinit(); | |
834 | |
835 OnStopDone(); | |
836 } | |
837 | |
838 void OmxVideoDecodeEngine::StopOnError() { | |
839 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
840 | |
841 client_state_ = kClientStopping; | |
842 | |
843 if (kIlExecuting == expected_il_state_) { | |
844 DeinitFromExecuting(OMX_StateExecuting); | |
845 } else if (kIlIdle == expected_il_state_) { | |
846 DeinitFromIdle(OMX_StateIdle); | |
847 } else if (kIlLoaded == expected_il_state_) { | |
848 DeinitFromLoaded(OMX_StateLoaded); | |
849 } else if (kIlPause == expected_il_state_) { | |
850 // TODO(jiesun): Make sure this works. | |
851 DeinitFromExecuting(OMX_StateExecuting); | |
852 } else { | |
853 NOTREACHED(); | |
854 } | |
855 } | |
856 | |
857 // Call OMX_UseBuffer() to avoid buffer copying when | |
858 // OMX_EmptyThisBuffer() is called | |
859 bool OmxVideoDecodeEngine::AllocateInputBuffers() { | |
860 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
861 | |
862 uint8* data = new uint8[input_buffer_size_]; | |
863 scoped_array<uint8> data_deleter(data); | |
864 | |
865 for (int i = 0; i < input_buffer_count_; ++i) { | |
866 OMX_BUFFERHEADERTYPE* buffer; | |
867 OMX_ERRORTYPE error = | |
868 OMX_UseBuffer(component_handle_, &buffer, input_port_, | |
869 this, input_buffer_size_, data); | |
870 if (error != OMX_ErrorNone) | |
871 return false; | |
872 buffer->nInputPortIndex = input_port_; | |
873 buffer->nOffset = 0; | |
874 buffer->nFlags = 0; | |
875 input_buffers_.push_back(buffer); | |
876 free_input_buffers_.push(buffer); | |
877 } | |
878 return true; | |
879 } | |
880 | |
881 // This method handles EGLImage and internal buffer cases. Any external | |
882 // allocation case is similar to EGLImage | |
883 bool OmxVideoDecodeEngine::AllocateOutputBuffers() { | |
884 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
885 | |
886 if (uses_egl_image_ && !output_frames_allocated_) { | |
887 DLOG(INFO) << "Output frames are not allocated yet"; | |
888 need_setup_output_port_ = true; | |
889 return true; | |
890 } | |
891 | |
892 for (int i = 0; i < output_buffer_count_; ++i) { | |
893 OMX_BUFFERHEADERTYPE* buffer; | |
894 scoped_refptr<VideoFrame> video_frame; | |
895 OMX_ERRORTYPE error; | |
896 if (uses_egl_image_) { | |
897 OutputFrame output_frame = output_frames_[i]; | |
898 video_frame = output_frame.first; | |
899 DCHECK(!output_frame.second); | |
900 error = OMX_UseEGLImage(component_handle_, &buffer, output_port_, | |
901 video_frame.get(), video_frame->private_buffer()); | |
902 if (error != OMX_ErrorNone) | |
903 return false; | |
904 output_frames_[i].second = buffer; | |
905 } else { | |
906 error = OMX_AllocateBuffer(component_handle_, &buffer, output_port_, | |
907 NULL, output_buffer_size_); | |
908 if (error != OMX_ErrorNone) | |
909 return false; | |
910 video_frame = CreateOmxBufferVideoFrame(buffer); | |
911 output_frames_.push_back(std::make_pair(video_frame, buffer)); | |
912 buffer->pAppPrivate = video_frame.get(); | |
913 } | |
914 } | |
915 | |
916 return true; | |
917 } | |
918 | |
919 scoped_refptr<VideoFrame> OmxVideoDecodeEngine::CreateOmxBufferVideoFrame( | |
920 OMX_BUFFERHEADERTYPE* omx_buffer) { | |
921 scoped_refptr<VideoFrame> video_frame; | |
922 uint8* data[VideoFrame::kMaxPlanes]; | |
923 int32 strides[VideoFrame::kMaxPlanes]; | |
924 | |
925 memset(data, 0, sizeof(data)); | |
926 memset(strides, 0, sizeof(strides)); | |
927 // TODO(jiesun): chroma format 4:2:0 only and 3 planes. | |
928 data[0] = omx_buffer->pBuffer; | |
929 data[1] = data[0] + width_ * height_; | |
930 data[2] = data[1] + width_ * height_ / 4; | |
931 strides[0] = width_; | |
932 strides[1] = strides[2] = width_ >> 1; | |
933 | |
934 VideoFrame::CreateFrameExternal( | |
935 VideoFrame::TYPE_SYSTEM_MEMORY, | |
936 VideoFrame::YV12, | |
937 width_, height_, 3, | |
938 data, strides, | |
939 kNoTimestamp, | |
940 kNoTimestamp, | |
941 omx_buffer, | |
942 &video_frame); | |
943 | |
944 return video_frame; | |
945 } | |
946 | |
947 void OmxVideoDecodeEngine::FreeInputBuffers() { | |
948 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
949 | |
950 // Empty available buffer queue. | |
951 while (!free_input_buffers_.empty()) { | |
952 free_input_buffers_.pop(); | |
953 } | |
954 | |
955 while (!available_input_buffers_.empty()) { | |
956 OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front(); | |
957 available_input_buffers_.pop(); | |
958 Buffer* stored_buffer = static_cast<Buffer*>(omx_buffer->pAppPrivate); | |
959 FinishEmptyBuffer(stored_buffer); | |
960 stored_buffer->Release(); | |
961 } | |
962 | |
963 // Calls to OMX to free buffers. | |
964 for (size_t i = 0; i < input_buffers_.size(); ++i) | |
965 OMX_FreeBuffer(component_handle_, input_port_, input_buffers_[i]); | |
966 input_buffers_.clear(); | |
967 | |
968 need_free_input_buffers_ = false; | |
969 } | |
970 | |
971 void OmxVideoDecodeEngine::FreeOutputBuffers() { | |
972 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
973 | |
974 // Calls to OMX to free buffers. | |
975 for (size_t i = 0; i < output_frames_.size(); ++i) { | |
976 OMX_BUFFERHEADERTYPE* omx_buffer = output_frames_[i].second; | |
977 CHECK(omx_buffer); | |
978 OMX_FreeBuffer(component_handle_, output_port_, omx_buffer); | |
979 } | |
980 output_frames_.clear(); | |
981 output_frames_allocated_ = false; | |
982 | |
983 need_free_output_buffers_ = false; | |
984 } | |
985 | |
986 bool OmxVideoDecodeEngine::ConfigureIOPorts() { | |
987 OMX_PARAM_PORTDEFINITIONTYPE input_port_def, output_port_def; | |
988 OMX_ERRORTYPE omxresult = OMX_ErrorNone; | |
989 // Get default input port definition. | |
990 ResetParamHeader(*this, &input_port_def); | |
991 input_port_def.nPortIndex = input_port_; | |
992 omxresult = OMX_GetParameter(component_handle_, | |
993 OMX_IndexParamPortDefinition, | |
994 &input_port_def); | |
995 if (omxresult != OMX_ErrorNone) { | |
996 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) " | |
997 << "for input port failed"; | |
998 return false; | |
999 } | |
1000 if (OMX_DirInput != input_port_def.eDir) { | |
1001 LOG(ERROR) << "Expected Input Port"; | |
1002 return false; | |
1003 } | |
1004 | |
1005 // Get default output port definition. | |
1006 ResetParamHeader(*this, &output_port_def); | |
1007 output_port_def.nPortIndex = output_port_; | |
1008 omxresult = OMX_GetParameter(component_handle_, | |
1009 OMX_IndexParamPortDefinition, | |
1010 &output_port_def); | |
1011 if (omxresult != OMX_ErrorNone) { | |
1012 LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) " | |
1013 << "for output port failed"; | |
1014 return false; | |
1015 } | |
1016 if (OMX_DirOutput != output_port_def.eDir) { | |
1017 LOG(ERROR) << "Expected Output Port"; | |
1018 return false; | |
1019 } | |
1020 | |
1021 return configurator_->ConfigureIOPorts( | |
1022 static_cast<OMX_COMPONENTTYPE*>(component_handle_), | |
1023 &input_port_def, &output_port_def); | |
1024 } | |
1025 | |
1026 bool OmxVideoDecodeEngine::CanEmptyBuffer() { | |
1027 // We can call empty buffer while we are in executing and EOS has | |
1028 // not been sent | |
1029 return (il_state_ == kIlExecuting && | |
1030 !input_has_fed_eos_); | |
1031 } | |
1032 | |
1033 bool OmxVideoDecodeEngine::CanFillBuffer() { | |
1034 // Make sure component is in the executing state and end-of-stream | |
1035 // has not been reached. | |
1036 return (il_state_ == kIlExecuting && | |
1037 !output_eos_ && | |
1038 (output_port_state_ == kPortEnabled || | |
1039 output_port_state_ == kPortEnabling)); | |
1040 } | |
1041 | |
1042 bool OmxVideoDecodeEngine::CanAcceptInput() { | |
1043 // We can't take input buffer when in error state. | |
1044 return (kClientError != client_state_ && | |
1045 kClientStopping != client_state_ && | |
1046 kClientStopped != client_state_ && | |
1047 !input_queue_has_eos_); | |
1048 } | |
1049 | |
1050 bool OmxVideoDecodeEngine::CanAcceptOutput() { | |
1051 return (kClientError != client_state_ && | |
1052 kClientStopping != client_state_ && | |
1053 kClientStopped != client_state_ && | |
1054 output_port_state_ == kPortEnabled && | |
1055 !output_eos_); | |
1056 } | |
1057 | |
1058 // TODO(wjia): There are several things need to be done here: | |
1059 // 1. Merge this method into EmptyThisBuffer(); | |
1060 // 2. Get rid of the while loop, this is not needed because when we call | |
1061 // OMX_EmptyThisBuffer we assume we *always* have an input buffer. | |
1062 void OmxVideoDecodeEngine::EmptyBufferTask() { | |
1063 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1064 | |
1065 if (!CanEmptyBuffer()) | |
1066 return; | |
1067 | |
1068 // Loop for all available input data and input buffer for the | |
1069 // decoder. When input has reached EOS we need to stop. | |
1070 while (!available_input_buffers_.empty() && | |
1071 !input_has_fed_eos_) { | |
1072 OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front(); | |
1073 available_input_buffers_.pop(); | |
1074 | |
1075 input_has_fed_eos_ = omx_buffer->nFlags & OMX_BUFFERFLAG_EOS; | |
1076 if (input_has_fed_eos_) { | |
1077 DLOG(INFO) << "Input has fed EOS"; | |
1078 } | |
1079 | |
1080 // Give this buffer to OMX. | |
1081 input_buffers_at_component_++; | |
1082 OMX_ERRORTYPE ret = OMX_EmptyThisBuffer(component_handle_, omx_buffer); | |
1083 if (ret != OMX_ErrorNone) { | |
1084 LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << ret; | |
1085 client_state_ = kClientError; | |
1086 return; | |
1087 } | |
1088 } | |
1089 } | |
1090 | |
1091 void OmxVideoDecodeEngine::InitialReadBuffer() { | |
1092 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1093 | |
1094 input_queue_has_eos_ = false; | |
1095 input_has_fed_eos_ = false; | |
1096 output_eos_ = false; | |
1097 | |
1098 DLOG(INFO) << "OmxVideoDecodeEngine::InitialReadBuffer"; | |
1099 for (size_t i = 0; i < free_input_buffers_.size(); i++) | |
1100 FinishEmptyBuffer(NULL); | |
1101 } | |
1102 | |
1103 void OmxVideoDecodeEngine::InitialFillBuffer() { | |
1104 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1105 // DCHECK(output_frames_allocated_); | |
1106 | |
1107 if (!CanFillBuffer()) | |
1108 return; | |
1109 | |
1110 DLOG(INFO) << "OmxVideoDecodeEngine::InitialFillBuffer"; | |
1111 | |
1112 // Ask the decoder to fill the output buffers. | |
1113 for (uint32 i = 0; i < output_frames_.size(); ++i) { | |
1114 OMX_BUFFERHEADERTYPE* omx_buffer = output_frames_[i].second; | |
1115 SendOutputBufferToComponent(omx_buffer); | |
1116 } | |
1117 } | |
1118 | |
1119 // helper functions | |
1120 // Send command to disable/enable port. | |
1121 void OmxVideoDecodeEngine::ChangePort(OMX_COMMANDTYPE cmd, int port_index) { | |
1122 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1123 | |
1124 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
1125 cmd, port_index, 0); | |
1126 if (omxresult != OMX_ErrorNone) { | |
1127 LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; | |
1128 client_state_ = kClientError; | |
1129 return; | |
1130 } | |
1131 } | |
1132 | |
1133 // Find if omx_buffer exists corresponding to video_frame | |
1134 OMX_BUFFERHEADERTYPE* OmxVideoDecodeEngine::FindOmxBuffer( | |
1135 scoped_refptr<VideoFrame> video_frame) { | |
1136 for (size_t i = 0; i < output_frames_.size(); ++i) { | |
1137 if (video_frame == output_frames_[i].first) | |
1138 return output_frames_[i].second; | |
1139 } | |
1140 return NULL; | |
1141 } | |
1142 | |
1143 OMX_STATETYPE OmxVideoDecodeEngine::GetComponentState() { | |
1144 OMX_STATETYPE eState; | |
1145 OMX_ERRORTYPE eError; | |
1146 | |
1147 eError = OMX_GetState(component_handle_, &eState); | |
1148 if (OMX_ErrorNone != eError) { | |
1149 LOG(ERROR) << "OMX_GetState failed"; | |
1150 StopOnError(); | |
1151 } | |
1152 | |
1153 return eState; | |
1154 } | |
1155 | |
1156 // send one output buffer to component | |
1157 void OmxVideoDecodeEngine::SendOutputBufferToComponent( | |
1158 OMX_BUFFERHEADERTYPE *omx_buffer) { | |
1159 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1160 | |
1161 if (!CanFillBuffer()) | |
1162 return; | |
1163 | |
1164 // clear EOS flag. | |
1165 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; | |
1166 omx_buffer->nOutputPortIndex = output_port_; | |
1167 output_buffers_at_component_++; | |
1168 OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer); | |
1169 | |
1170 if (OMX_ErrorNone != ret) { | |
1171 LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret; | |
1172 client_state_ = kClientError; | |
1173 return; | |
1174 } | |
1175 } | |
1176 | |
1177 // Send state transition command to component. | |
1178 bool OmxVideoDecodeEngine::TransitionToState(OMX_STATETYPE new_state) { | |
1179 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1180 | |
1181 OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, | |
1182 OMX_CommandStateSet, | |
1183 new_state, 0); | |
1184 if (omxresult != OMX_ErrorNone) { | |
1185 LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; | |
1186 client_state_ = kClientError; | |
1187 return false; | |
1188 } | |
1189 | |
1190 return true; | |
1191 } | |
1192 | |
1193 void OmxVideoDecodeEngine::EmptyBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer) { | |
1194 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1195 DCHECK_GT(input_buffers_at_component_, 0); | |
1196 | |
1197 Buffer* stored_buffer = static_cast<Buffer*>(buffer->pAppPrivate); | |
1198 buffer->pAppPrivate = NULL; | |
1199 if (client_state_ != kClientFlushing) | |
1200 FinishEmptyBuffer(stored_buffer); | |
1201 stored_buffer->Release(); | |
1202 | |
1203 // Enqueue the available buffer because the decoder has consumed it. | |
1204 free_input_buffers_.push(buffer); | |
1205 input_buffers_at_component_--; | |
1206 | |
1207 if (need_free_input_buffers_ && !input_buffers_at_component_) { | |
1208 FreeInputBuffers(); | |
1209 return; | |
1210 } | |
1211 | |
1212 // Try to feed more data into the decoder. | |
1213 EmptyBufferTask(); | |
1214 | |
1215 if (client_state_ == kClientFlushing && | |
1216 InputPortFlushed() && OutputPortFlushed()) | |
1217 ComponentFlushDone(); | |
1218 } | |
1219 | |
1220 void OmxVideoDecodeEngine::FillBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer) { | |
1221 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
1222 DCHECK_GT(output_buffers_at_component_, 0); | |
1223 | |
1224 output_buffers_at_component_--; | |
1225 | |
1226 if (need_free_output_buffers_ && !output_buffers_at_component_) { | |
1227 FreeOutputBuffers(); | |
1228 return; | |
1229 } | |
1230 | |
1231 PipelineStatistics statistics; | |
1232 statistics.video_bytes_decoded = buffer->nFilledLen; | |
1233 | |
1234 if (!CanAcceptOutput()) { | |
1235 if (uses_egl_image_) { | |
1236 scoped_refptr<VideoFrame> frame; | |
1237 frame = static_cast<VideoFrame*>(buffer->pAppPrivate); | |
1238 event_handler_->ConsumeVideoFrame(frame, statistics); | |
1239 output_pending_request_--; | |
1240 } | |
1241 return; | |
1242 } | |
1243 | |
1244 // This buffer is received with decoded frame. Enqueue it and make it | |
1245 // ready to be consumed by reads. | |
1246 | |
1247 if (buffer->nFlags & OMX_BUFFERFLAG_EOS) { | |
1248 output_eos_ = true; | |
1249 DLOG(INFO) << "Output has EOS"; | |
1250 } | |
1251 | |
1252 FinishFillBuffer(buffer); | |
1253 | |
1254 if (buffer->nFlags & OMX_BUFFERFLAG_EOS) { | |
1255 // Singal end of stream. | |
1256 scoped_refptr<VideoFrame> frame; | |
1257 VideoFrame::CreateEmptyFrame(&frame); | |
1258 event_handler_->ConsumeVideoFrame(frame, statistics); | |
1259 } | |
1260 | |
1261 if (client_state_ == kClientFlushing && | |
1262 InputPortFlushed() && OutputPortFlushed()) | |
1263 ComponentFlushDone(); | |
1264 } | |
1265 | |
1266 void OmxVideoDecodeEngine::EventHandlerCompleteTask(OMX_EVENTTYPE event, | |
1267 OMX_U32 data1, | |
1268 OMX_U32 data2) { | |
1269 switch (event) { | |
1270 case OMX_EventCmdComplete: { | |
1271 // If the last command was successful, we have completed | |
1272 // a state transition. So notify that we have done it | |
1273 // accordingly. | |
1274 OMX_COMMANDTYPE cmd = static_cast<OMX_COMMANDTYPE>(data1); | |
1275 if (cmd == OMX_CommandPortDisable) { | |
1276 if (OnPortDisableEventFunc) | |
1277 (this->*OnPortDisableEventFunc)(static_cast<int>(data2)); | |
1278 } else if (cmd == OMX_CommandPortEnable) { | |
1279 if (OnPortEnableEventFunc) | |
1280 (this->*OnPortEnableEventFunc)(static_cast<int>(data2)); | |
1281 } else if (cmd == OMX_CommandStateSet) { | |
1282 (this->*OnStateSetEventFunc)(static_cast<OMX_STATETYPE>(data2)); | |
1283 } else if (cmd == OMX_CommandFlush) { | |
1284 (this->*OnFlushEventFunc)(data2); | |
1285 } else { | |
1286 LOG(ERROR) << "Unknown command completed\n" << data1; | |
1287 } | |
1288 break; | |
1289 } | |
1290 case OMX_EventError: | |
1291 if (OMX_ErrorInvalidState == (OMX_ERRORTYPE)data1) { | |
1292 // TODO(hclam): what to do here? | |
1293 } | |
1294 StopOnError(); | |
1295 break; | |
1296 case OMX_EventPortSettingsChanged: | |
1297 // TODO(wjia): remove this hack when all vendors observe same spec. | |
1298 if (data1 < OMX_IndexComponentStartUnused) | |
1299 OnPortSettingsChangedRun(static_cast<int>(data1), | |
1300 static_cast<OMX_INDEXTYPE>(data2)); | |
1301 else | |
1302 OnPortSettingsChangedRun(static_cast<int>(data2), | |
1303 static_cast<OMX_INDEXTYPE>(data1)); | |
1304 break; | |
1305 default: | |
1306 LOG(ERROR) << "Warning - Unknown event received\n"; | |
1307 break; | |
1308 } | |
1309 } | |
1310 | |
1311 // static | |
1312 OMX_ERRORTYPE OmxVideoDecodeEngine::EventHandler(OMX_HANDLETYPE component, | |
1313 OMX_PTR priv_data, | |
1314 OMX_EVENTTYPE event, | |
1315 OMX_U32 data1, | |
1316 OMX_U32 data2, | |
1317 OMX_PTR event_data) { | |
1318 OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data); | |
1319 DCHECK_EQ(component, decoder->component_handle_); | |
1320 decoder->message_loop_->PostTask(FROM_HERE, | |
1321 NewRunnableMethod(decoder, | |
1322 &OmxVideoDecodeEngine::EventHandlerCompleteTask, | |
1323 event, data1, data2)); | |
1324 return OMX_ErrorNone; | |
1325 } | |
1326 | |
1327 // static | |
1328 OMX_ERRORTYPE OmxVideoDecodeEngine::EmptyBufferCallback( | |
1329 OMX_HANDLETYPE component, | |
1330 OMX_PTR priv_data, | |
1331 OMX_BUFFERHEADERTYPE* buffer) { | |
1332 OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data); | |
1333 DCHECK_EQ(component, decoder->component_handle_); | |
1334 decoder->message_loop_->PostTask(FROM_HERE, | |
1335 NewRunnableMethod(decoder, | |
1336 &OmxVideoDecodeEngine::EmptyBufferDoneTask, buffer)); | |
1337 return OMX_ErrorNone; | |
1338 } | |
1339 | |
1340 // static | |
1341 OMX_ERRORTYPE OmxVideoDecodeEngine::FillBufferCallback( | |
1342 OMX_HANDLETYPE component, | |
1343 OMX_PTR priv_data, | |
1344 OMX_BUFFERHEADERTYPE* buffer) { | |
1345 OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data); | |
1346 DCHECK_EQ(component, decoder->component_handle_); | |
1347 decoder->message_loop_->PostTask(FROM_HERE, | |
1348 NewRunnableMethod(decoder, | |
1349 &OmxVideoDecodeEngine::FillBufferDoneTask, buffer)); | |
1350 return OMX_ErrorNone; | |
1351 } | |
1352 | |
1353 } // namespace media | |
1354 | |
1355 // Disable refcounting for this object because this object only lives | |
1356 // on the video decoder thread and there's no need to refcount it. | |
1357 DISABLE_RUNNABLE_METHOD_REFCOUNT(media::OmxVideoDecodeEngine); | |
OLD | NEW |