| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/renderer/gpu_video_decoder_host.h" | |
| 6 | |
| 7 #include "content/common/gpu/gpu_messages.h" | |
| 8 #include "content/common/message_router.h" | |
| 9 #include "media/base/pipeline.h" | |
| 10 #include "media/video/video_decode_context.h" | |
| 11 | |
| 12 GpuVideoDecoderHost::GpuVideoDecoderHost(MessageRouter* router, | |
| 13 IPC::Message::Sender* ipc_sender, | |
| 14 int context_route_id, | |
| 15 int32 decoder_host_id) | |
| 16 : router_(router), | |
| 17 ipc_sender_(ipc_sender), | |
| 18 context_route_id_(context_route_id), | |
| 19 message_loop_(NULL), | |
| 20 event_handler_(NULL), | |
| 21 context_(NULL), | |
| 22 width_(0), | |
| 23 height_(0), | |
| 24 state_(kStateUninitialized), | |
| 25 decoder_host_id_(decoder_host_id), | |
| 26 decoder_id_(0), | |
| 27 input_buffer_busy_(false), | |
| 28 current_frame_id_(0) { | |
| 29 } | |
| 30 | |
| 31 GpuVideoDecoderHost::~GpuVideoDecoderHost() {} | |
| 32 | |
| 33 void GpuVideoDecoderHost::OnChannelError() { | |
| 34 ipc_sender_ = NULL; | |
| 35 } | |
| 36 | |
| 37 bool GpuVideoDecoderHost::OnMessageReceived(const IPC::Message& msg) { | |
| 38 bool handled = true; | |
| 39 IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoderHost, msg) | |
| 40 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_CreateVideoDecoderDone, | |
| 41 OnCreateVideoDecoderDone) | |
| 42 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_InitializeACK, | |
| 43 OnInitializeDone) | |
| 44 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_DestroyACK, | |
| 45 OnUninitializeDone) | |
| 46 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_FlushACK, | |
| 47 OnFlushDone) | |
| 48 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_PrerollDone, | |
| 49 OnPrerollDone) | |
| 50 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferACK, | |
| 51 OnEmptyThisBufferACK) | |
| 52 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferDone, | |
| 53 OnProduceVideoSample) | |
| 54 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_ConsumeVideoFrame, | |
| 55 OnConsumeVideoFrame) | |
| 56 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_AllocateVideoFrames, | |
| 57 OnAllocateVideoFrames) | |
| 58 IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_ReleaseAllVideoFrames, | |
| 59 OnReleaseAllVideoFrames) | |
| 60 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 61 IPC_END_MESSAGE_MAP() | |
| 62 DCHECK(handled); | |
| 63 return handled; | |
| 64 } | |
| 65 | |
| 66 void GpuVideoDecoderHost::Initialize( | |
| 67 MessageLoop* message_loop, | |
| 68 VideoDecodeEngine::EventHandler* event_handler, | |
| 69 media::VideoDecodeContext* context, | |
| 70 const media::VideoDecoderConfig& config) { | |
| 71 DCHECK_EQ(kStateUninitialized, state_); | |
| 72 DCHECK(!message_loop_); | |
| 73 message_loop_ = message_loop; | |
| 74 event_handler_ = event_handler; | |
| 75 context_ = context; | |
| 76 width_ = config.width(); | |
| 77 height_ = config.height(); | |
| 78 | |
| 79 if (MessageLoop::current() != message_loop) { | |
| 80 message_loop->PostTask( | |
| 81 FROM_HERE, | |
| 82 NewRunnableMethod(this, &GpuVideoDecoderHost::CreateVideoDecoder)); | |
| 83 return; | |
| 84 } | |
| 85 CreateVideoDecoder(); | |
| 86 } | |
| 87 | |
| 88 void GpuVideoDecoderHost::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { | |
| 89 if (MessageLoop::current() != message_loop_) { | |
| 90 message_loop_->PostTask( | |
| 91 FROM_HERE, | |
| 92 NewRunnableMethod( | |
| 93 this, &GpuVideoDecoderHost::ConsumeVideoSample, buffer)); | |
| 94 return; | |
| 95 } | |
| 96 | |
| 97 DCHECK_NE(state_, kStateUninitialized); | |
| 98 DCHECK_NE(state_, kStateFlushing); | |
| 99 | |
| 100 // We never own input buffers, therefore when client in flush state, it | |
| 101 // never call us with EmptyThisBuffer. | |
| 102 if (state_ != kStateNormal) | |
| 103 return; | |
| 104 | |
| 105 input_buffer_queue_.push_back(buffer); | |
| 106 SendConsumeVideoSample(); | |
| 107 } | |
| 108 | |
| 109 void GpuVideoDecoderHost::ProduceVideoFrame(scoped_refptr<VideoFrame> frame) { | |
| 110 if (MessageLoop::current() != message_loop_) { | |
| 111 message_loop_->PostTask( | |
| 112 FROM_HERE, | |
| 113 NewRunnableMethod( | |
| 114 this, &GpuVideoDecoderHost::ProduceVideoFrame, frame)); | |
| 115 return; | |
| 116 } | |
| 117 | |
| 118 DCHECK_NE(state_, kStateUninitialized); | |
| 119 | |
| 120 // During flush client of this object will call this method to return all | |
| 121 // video frames. We should only ignore such method calls if we are in error | |
| 122 // state. | |
| 123 if (state_ == kStateError) | |
| 124 return; | |
| 125 | |
| 126 // Check that video frame is valid. | |
| 127 if (!frame || frame->format() == media::VideoFrame::EMPTY || | |
| 128 frame->IsEndOfStream()) { | |
| 129 return; | |
| 130 } | |
| 131 | |
| 132 SendProduceVideoFrame(frame); | |
| 133 } | |
| 134 | |
| 135 void GpuVideoDecoderHost::Uninitialize() { | |
| 136 if (MessageLoop::current() != message_loop_) { | |
| 137 message_loop_->PostTask( | |
| 138 FROM_HERE, | |
| 139 NewRunnableMethod(this, &GpuVideoDecoderHost::Uninitialize)); | |
| 140 return; | |
| 141 } | |
| 142 | |
| 143 if (!ipc_sender_->Send(new GpuVideoDecoderMsg_Destroy(decoder_id_))) { | |
| 144 LOG(ERROR) << "GpuVideoDecoderMsg_Destroy failed"; | |
| 145 event_handler_->OnError(); | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 void GpuVideoDecoderHost::Flush() { | |
| 150 if (MessageLoop::current() != message_loop_) { | |
| 151 message_loop_->PostTask( | |
| 152 FROM_HERE, NewRunnableMethod(this, &GpuVideoDecoderHost::Flush)); | |
| 153 return; | |
| 154 } | |
| 155 | |
| 156 state_ = kStateFlushing; | |
| 157 if (!ipc_sender_->Send(new GpuVideoDecoderMsg_Flush(decoder_id_))) { | |
| 158 LOG(ERROR) << "GpuVideoDecoderMsg_Flush failed"; | |
| 159 event_handler_->OnError(); | |
| 160 return; | |
| 161 } | |
| 162 | |
| 163 input_buffer_queue_.clear(); | |
| 164 // TODO(jiesun): because GpuVideoDeocder/GpuVideoDecoder are asynchronously. | |
| 165 // We need a way to make flush logic more clear. but I think ring buffer | |
| 166 // should make the busy flag obsolete, therefore I will leave it for now. | |
| 167 input_buffer_busy_ = false; | |
| 168 } | |
| 169 | |
| 170 void GpuVideoDecoderHost::Seek() { | |
| 171 if (MessageLoop::current() != message_loop_) { | |
| 172 message_loop_->PostTask( | |
| 173 FROM_HERE, NewRunnableMethod(this, &GpuVideoDecoderHost::Seek)); | |
| 174 return; | |
| 175 } | |
| 176 | |
| 177 if (!ipc_sender_->Send(new GpuVideoDecoderMsg_Preroll(decoder_id_))) { | |
| 178 LOG(ERROR) << "GpuVideoDecoderMsg_Preroll failed"; | |
| 179 event_handler_->OnError(); | |
| 180 return; | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 void GpuVideoDecoderHost::CreateVideoDecoder() { | |
| 185 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 186 | |
| 187 // Add the route so we'll receive messages. | |
| 188 router_->AddRoute(decoder_host_id_, this); | |
| 189 | |
| 190 if (!ipc_sender_->Send( | |
| 191 new GpuChannelMsg_CreateVideoDecoder(context_route_id_, | |
| 192 decoder_host_id_))) { | |
| 193 LOG(ERROR) << "GpuChannelMsg_CreateVideoDecoder failed"; | |
| 194 event_handler_->OnError(); | |
| 195 return; | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 void GpuVideoDecoderHost::OnCreateVideoDecoderDone(int32 decoder_id) { | |
| 200 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 201 decoder_id_ = decoder_id; | |
| 202 | |
| 203 // TODO(hclam): Initialize |param| with the right values. | |
| 204 GpuVideoDecoderInitParam param; | |
| 205 param.width = width_; | |
| 206 param.height = height_; | |
| 207 | |
| 208 if (!ipc_sender_->Send( | |
| 209 new GpuVideoDecoderMsg_Initialize(decoder_id, param))) { | |
| 210 LOG(ERROR) << "GpuVideoDecoderMsg_Initialize failed"; | |
| 211 event_handler_->OnError(); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 void GpuVideoDecoderHost::OnInitializeDone( | |
| 216 const GpuVideoDecoderInitDoneParam& param) { | |
| 217 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 218 | |
| 219 bool success = param.success && | |
| 220 base::SharedMemory::IsHandleValid(param.input_buffer_handle); | |
| 221 | |
| 222 if (success) { | |
| 223 input_transfer_buffer_.reset( | |
| 224 new base::SharedMemory(param.input_buffer_handle, false)); | |
| 225 success = input_transfer_buffer_->Map(param.input_buffer_size); | |
| 226 } | |
| 227 state_ = success ? kStateNormal : kStateError; | |
| 228 | |
| 229 // TODO(hclam): There's too many unnecessary copies for width and height! | |
| 230 // Need to clean it up. | |
| 231 // TODO(hclam): Need to fill in more information. | |
| 232 media::VideoCodecInfo info; | |
| 233 info.success = success; | |
| 234 info.stream_info.surface_width = width_; | |
| 235 info.stream_info.surface_height = height_; | |
| 236 event_handler_->OnInitializeComplete(info); | |
| 237 } | |
| 238 | |
| 239 void GpuVideoDecoderHost::OnUninitializeDone() { | |
| 240 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 241 | |
| 242 input_transfer_buffer_.reset(); | |
| 243 router_->RemoveRoute(decoder_host_id_); | |
| 244 context_->ReleaseAllVideoFrames(); | |
| 245 event_handler_->OnUninitializeComplete(); | |
| 246 } | |
| 247 | |
| 248 void GpuVideoDecoderHost::OnFlushDone() { | |
| 249 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 250 | |
| 251 state_ = kStateNormal; | |
| 252 event_handler_->OnFlushComplete(); | |
| 253 } | |
| 254 | |
| 255 void GpuVideoDecoderHost::OnPrerollDone() { | |
| 256 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 257 | |
| 258 state_ = kStateNormal; | |
| 259 event_handler_->OnSeekComplete(); | |
| 260 } | |
| 261 | |
| 262 void GpuVideoDecoderHost::OnEmptyThisBufferACK() { | |
| 263 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 264 | |
| 265 input_buffer_busy_ = false; | |
| 266 SendConsumeVideoSample(); | |
| 267 } | |
| 268 | |
| 269 void GpuVideoDecoderHost::OnProduceVideoSample() { | |
| 270 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 271 DCHECK_EQ(kStateNormal, state_); | |
| 272 | |
| 273 event_handler_->ProduceVideoSample(NULL); | |
| 274 } | |
| 275 | |
| 276 void GpuVideoDecoderHost::OnConsumeVideoFrame(int32 frame_id, int64 timestamp, | |
| 277 int64 duration, int32 flags) { | |
| 278 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 279 | |
| 280 scoped_refptr<VideoFrame> frame; | |
| 281 if (flags & kGpuVideoEndOfStream) { | |
| 282 VideoFrame::CreateEmptyFrame(&frame); | |
| 283 } else { | |
| 284 frame = video_frame_map_[frame_id]; | |
| 285 DCHECK(frame) << "Invalid frame ID received"; | |
| 286 | |
| 287 frame->SetDuration(base::TimeDelta::FromMicroseconds(duration)); | |
| 288 frame->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp)); | |
| 289 } | |
| 290 | |
| 291 media::PipelineStatistics statistics; | |
| 292 // TODO(sjl): Fill in statistics. | |
| 293 | |
| 294 event_handler_->ConsumeVideoFrame(frame, statistics); | |
| 295 } | |
| 296 | |
| 297 void GpuVideoDecoderHost::OnAllocateVideoFrames( | |
| 298 int32 n, uint32 width, uint32 height, int32 format) { | |
| 299 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 300 DCHECK_EQ(0u, video_frames_.size()); | |
| 301 | |
| 302 context_->AllocateVideoFrames( | |
| 303 n, width, height, static_cast<media::VideoFrame::Format>(format), | |
| 304 &video_frames_, | |
| 305 NewRunnableMethod(this, | |
| 306 &GpuVideoDecoderHost::OnAllocateVideoFramesDone)); | |
| 307 } | |
| 308 | |
| 309 void GpuVideoDecoderHost::OnReleaseAllVideoFrames() { | |
| 310 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 311 | |
| 312 context_->ReleaseAllVideoFrames(); | |
| 313 video_frame_map_.clear(); | |
| 314 video_frames_.clear(); | |
| 315 } | |
| 316 | |
| 317 void GpuVideoDecoderHost::OnAllocateVideoFramesDone() { | |
| 318 if (MessageLoop::current() != message_loop_) { | |
| 319 message_loop_->PostTask( | |
| 320 FROM_HERE, | |
| 321 NewRunnableMethod( | |
| 322 this, &GpuVideoDecoderHost::OnAllocateVideoFramesDone)); | |
| 323 return; | |
| 324 } | |
| 325 | |
| 326 // After video frame allocation is done we add these frames to a map and | |
| 327 // send them to the GPU process. | |
| 328 DCHECK(video_frames_.size()) << "No video frames allocated"; | |
| 329 for (size_t i = 0; i < video_frames_.size(); ++i) { | |
| 330 DCHECK(video_frames_[i]); | |
| 331 video_frame_map_.insert( | |
| 332 std::make_pair(current_frame_id_, video_frames_[i])); | |
| 333 SendVideoFrameAllocated(current_frame_id_, video_frames_[i]); | |
| 334 ++current_frame_id_; | |
| 335 } | |
| 336 } | |
| 337 | |
| 338 void GpuVideoDecoderHost::SendVideoFrameAllocated( | |
| 339 int32 frame_id, scoped_refptr<media::VideoFrame> frame) { | |
| 340 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 341 | |
| 342 std::vector<uint32> textures; | |
| 343 for (size_t i = 0; i < frame->planes(); ++i) { | |
| 344 textures.push_back(frame->gl_texture(i)); | |
| 345 } | |
| 346 | |
| 347 if (!ipc_sender_->Send(new GpuVideoDecoderMsg_VideoFrameAllocated( | |
| 348 decoder_id_, frame_id, textures))) { | |
| 349 LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBuffer failed"; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 void GpuVideoDecoderHost::SendConsumeVideoSample() { | |
| 354 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 355 | |
| 356 if (input_buffer_busy_ || input_buffer_queue_.empty()) | |
| 357 return; | |
| 358 input_buffer_busy_ = true; | |
| 359 | |
| 360 scoped_refptr<Buffer> buffer = input_buffer_queue_.front(); | |
| 361 input_buffer_queue_.pop_front(); | |
| 362 | |
| 363 // Send input data to GPU process. | |
| 364 GpuVideoDecoderInputBufferParam param; | |
| 365 param.offset = 0; | |
| 366 param.size = buffer->GetDataSize(); | |
| 367 param.timestamp = buffer->GetTimestamp().InMicroseconds(); | |
| 368 memcpy(input_transfer_buffer_->memory(), buffer->GetData(), param.size); | |
| 369 | |
| 370 if (!ipc_sender_->Send( | |
| 371 new GpuVideoDecoderMsg_EmptyThisBuffer(decoder_id_, param))) { | |
| 372 LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBuffer failed"; | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 void GpuVideoDecoderHost::SendProduceVideoFrame( | |
| 377 scoped_refptr<media::VideoFrame> frame) { | |
| 378 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
| 379 | |
| 380 // TODO(hclam): I should mark a frame being used to DCHECK and make sure | |
| 381 // user doesn't use it the second time. | |
| 382 // TODO(hclam): Derive a faster way to lookup the frame ID. | |
| 383 bool found = false; | |
| 384 int32 frame_id = 0; | |
| 385 for (VideoFrameMap::iterator i = video_frame_map_.begin(); | |
| 386 i != video_frame_map_.end(); ++i) { | |
| 387 if (frame == i->second) { | |
| 388 frame_id = i->first; | |
| 389 found = true; | |
| 390 break; | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 DCHECK(found) << "Invalid video frame received"; | |
| 395 if (found && !ipc_sender_->Send( | |
| 396 new GpuVideoDecoderMsg_ProduceVideoFrame(decoder_id_, frame_id))) { | |
| 397 LOG(ERROR) << "GpuVideoDecoderMsg_ProduceVideoFrame failed"; | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 DISABLE_RUNNABLE_METHOD_REFCOUNT(GpuVideoDecoderHost); | |
| OLD | NEW |