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