| 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/gpu/gpu_video_decoder.h" | |
| 6 | |
| 7 #include "base/command_line.h" | |
| 8 #include "chrome/gpu/gpu_channel.h" | |
| 9 #include "chrome/gpu/media/fake_gl_video_decode_engine.h" | |
| 10 #include "chrome/gpu/media/fake_gl_video_device.h" | |
| 11 #include "content/common/child_thread.h" | |
| 12 #include "content/common/gpu_messages.h" | |
| 13 #include "media/base/data_buffer.h" | |
| 14 #include "media/base/media_switches.h" | |
| 15 #include "media/base/video_frame.h" | |
| 16 | |
| 17 #if defined(OS_WIN) | |
| 18 #include "chrome/gpu/media/mft_angle_video_device.h" | |
| 19 #include "media/video/mft_h264_decode_engine.h" | |
| 20 #include <d3d9.h> | |
| 21 #endif | |
| 22 | |
| 23 struct GpuVideoDecoder::PendingAllocation { | |
| 24 size_t n; | |
| 25 size_t width; | |
| 26 size_t height; | |
| 27 media::VideoFrame::Format format; | |
| 28 std::vector<scoped_refptr<media::VideoFrame> >* frames; | |
| 29 Task* task; | |
| 30 }; | |
| 31 | |
| 32 void GpuVideoDecoder::OnChannelConnected(int32 peer_pid) { | |
| 33 } | |
| 34 | |
| 35 void GpuVideoDecoder::OnChannelError() { | |
| 36 } | |
| 37 | |
| 38 bool GpuVideoDecoder::OnMessageReceived(const IPC::Message& msg) { | |
| 39 bool handled = true; | |
| 40 IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoder, msg) | |
| 41 IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Initialize, | |
| 42 OnInitialize) | |
| 43 IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Destroy, | |
| 44 OnUninitialize) | |
| 45 IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Flush, | |
| 46 OnFlush) | |
| 47 IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Preroll, | |
| 48 OnPreroll) | |
| 49 IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_EmptyThisBuffer, | |
| 50 OnEmptyThisBuffer) | |
| 51 IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_ProduceVideoFrame, | |
| 52 OnProduceVideoFrame) | |
| 53 IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_VideoFrameAllocated, | |
| 54 OnVideoFrameAllocated) | |
| 55 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 56 IPC_END_MESSAGE_MAP() | |
| 57 DCHECK(handled); | |
| 58 return handled; | |
| 59 } | |
| 60 | |
| 61 bool GpuVideoDecoder::CreateInputTransferBuffer( | |
| 62 uint32 size, | |
| 63 base::SharedMemoryHandle* handle) { | |
| 64 input_transfer_buffer_.reset(new base::SharedMemory); | |
| 65 if (!input_transfer_buffer_.get()) | |
| 66 return false; | |
| 67 | |
| 68 if (!input_transfer_buffer_->CreateAndMapAnonymous(size)) | |
| 69 return false; | |
| 70 | |
| 71 if (!input_transfer_buffer_->ShareToProcess(renderer_handle_, handle)) | |
| 72 return false; | |
| 73 | |
| 74 return true; | |
| 75 } | |
| 76 | |
| 77 void GpuVideoDecoder::OnInitializeComplete(const VideoCodecInfo& info) { | |
| 78 info_ = info; | |
| 79 GpuVideoDecoderInitDoneParam param; | |
| 80 param.success = false; | |
| 81 param.input_buffer_handle = base::SharedMemory::NULLHandle(); | |
| 82 | |
| 83 if (!info.success) { | |
| 84 SendInitializeDone(param); | |
| 85 return; | |
| 86 } | |
| 87 | |
| 88 // TODO(jiesun): Check the assumption of input size < original size. | |
| 89 param.input_buffer_size = | |
| 90 info.stream_info.surface_width * info.stream_info.surface_height * 3 / 2; | |
| 91 if (!CreateInputTransferBuffer(param.input_buffer_size, | |
| 92 ¶m.input_buffer_handle)) { | |
| 93 SendInitializeDone(param); | |
| 94 return; | |
| 95 } | |
| 96 | |
| 97 param.success = true; | |
| 98 SendInitializeDone(param); | |
| 99 } | |
| 100 | |
| 101 void GpuVideoDecoder::OnUninitializeComplete() { | |
| 102 SendUninitializeDone(); | |
| 103 } | |
| 104 | |
| 105 void GpuVideoDecoder::OnFlushComplete() { | |
| 106 SendFlushDone(); | |
| 107 } | |
| 108 | |
| 109 void GpuVideoDecoder::OnSeekComplete() { | |
| 110 SendPrerollDone(); | |
| 111 } | |
| 112 | |
| 113 void GpuVideoDecoder::OnError() { | |
| 114 NOTIMPLEMENTED(); | |
| 115 } | |
| 116 | |
| 117 void GpuVideoDecoder::OnFormatChange(VideoStreamInfo stream_info) { | |
| 118 NOTIMPLEMENTED(); | |
| 119 } | |
| 120 | |
| 121 void GpuVideoDecoder::ProduceVideoSample(scoped_refptr<Buffer> buffer) { | |
| 122 SendEmptyBufferDone(); | |
| 123 } | |
| 124 | |
| 125 void GpuVideoDecoder::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame, | |
| 126 const PipelineStatistics& statistics) { | |
| 127 // TODO(sjl): Do something with the statistics... | |
| 128 | |
| 129 if (frame->IsEndOfStream()) { | |
| 130 SendConsumeVideoFrame(0, 0, 0, kGpuVideoEndOfStream); | |
| 131 return; | |
| 132 } | |
| 133 | |
| 134 int32 frame_id = -1; | |
| 135 for (VideoFrameMap::iterator i = video_frame_map_.begin(); | |
| 136 i != video_frame_map_.end(); ++i) { | |
| 137 if (i->second == frame) { | |
| 138 frame_id = i->first; | |
| 139 break; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 if (frame_id == -1) { | |
| 144 NOTREACHED() << "VideoFrame not recognized"; | |
| 145 return; | |
| 146 } | |
| 147 | |
| 148 SendConsumeVideoFrame(frame_id, frame->GetTimestamp().InMicroseconds(), | |
| 149 frame->GetDuration().InMicroseconds(), 0); | |
| 150 } | |
| 151 | |
| 152 void* GpuVideoDecoder::GetDevice() { | |
| 153 bool ret = gles2_decoder_->MakeCurrent(); | |
| 154 DCHECK(ret) << "Failed to switch context"; | |
| 155 | |
| 156 // Simply delegate the method call to GpuVideoDevice. | |
| 157 return video_device_->GetDevice(); | |
| 158 } | |
| 159 | |
| 160 void GpuVideoDecoder::AllocateVideoFrames( | |
| 161 int n, size_t width, size_t height, media::VideoFrame::Format format, | |
| 162 std::vector<scoped_refptr<media::VideoFrame> >* frames, Task* task) { | |
| 163 // Since the communication between Renderer and GPU process is by GL textures. | |
| 164 // We need to obtain a set of GL textures by sending IPC commands to the | |
| 165 // Renderer process. The recipient of these commands will be IpcVideoDecoder. | |
| 166 // | |
| 167 // After IpcVideoDecoder replied with a set of textures. We'll assign these | |
| 168 // textures to GpuVideoDevice. They will be used to generate platform | |
| 169 // specific VideoFrames objects that are used by VideoDecodeEngine. | |
| 170 // | |
| 171 // After GL textures are assigned we'll proceed with allocation the | |
| 172 // VideoFrames. GpuVideoDevice::CreateVideoFramesFromGlTextures() will be | |
| 173 // called. | |
| 174 // | |
| 175 // When GpuVideoDevice replied with a set of VideoFrames we'll give | |
| 176 // that to VideoDecodeEngine and the cycle of video frame allocation is done. | |
| 177 // | |
| 178 // Note that this method is called when there's no video frames allocated or | |
| 179 // they were all released. | |
| 180 DCHECK(video_frame_map_.empty()); | |
| 181 | |
| 182 // Save the parameters for allocation. | |
| 183 pending_allocation_.reset(new PendingAllocation()); | |
| 184 pending_allocation_->n = n; | |
| 185 pending_allocation_->width = width; | |
| 186 pending_allocation_->height = height; | |
| 187 pending_allocation_->format = format; | |
| 188 pending_allocation_->frames = frames; | |
| 189 pending_allocation_->task = task; | |
| 190 SendAllocateVideoFrames(n, width, height, format); | |
| 191 } | |
| 192 | |
| 193 void GpuVideoDecoder::ReleaseAllVideoFrames() { | |
| 194 // This method will first call to GpuVideoDevice to release all the resource | |
| 195 // associated with a VideoFrame. | |
| 196 // | |
| 197 // And then we'll call GpuVideoDevice::ReleaseVideoFrame() to remove the set | |
| 198 // of Gl textures associated with the context. | |
| 199 // | |
| 200 // And finally we'll send IPC commands to IpcVideoDecoder to destroy all | |
| 201 // GL textures generated. | |
| 202 bool ret = gles2_decoder_->MakeCurrent(); | |
| 203 DCHECK(ret) << "Failed to switch context"; | |
| 204 | |
| 205 for (VideoFrameMap::iterator i = video_frame_map_.begin(); | |
| 206 i != video_frame_map_.end(); ++i) { | |
| 207 video_device_->ReleaseVideoFrame(i->second); | |
| 208 } | |
| 209 video_frame_map_.clear(); | |
| 210 SendReleaseAllVideoFrames(); | |
| 211 } | |
| 212 | |
| 213 void GpuVideoDecoder::ConvertToVideoFrame( | |
| 214 void* buffer, | |
| 215 scoped_refptr<media::VideoFrame> frame, | |
| 216 Task* task) { | |
| 217 // This method is called by VideoDecodeEngine to upload a buffer to a | |
| 218 // VideoFrame. We should just delegate this to GpuVideoDevice which contains | |
| 219 // the actual implementation. | |
| 220 bool ret = gles2_decoder_->MakeCurrent(); | |
| 221 DCHECK(ret) << "Failed to switch context"; | |
| 222 | |
| 223 // Actually doing the upload on the main thread. | |
| 224 ret = video_device_->ConvertToVideoFrame(buffer, frame); | |
| 225 DCHECK(ret) << "Failed to upload video content to a VideoFrame."; | |
| 226 task->Run(); | |
| 227 delete task; | |
| 228 } | |
| 229 | |
| 230 void GpuVideoDecoder::Destroy(Task* task) { | |
| 231 // TODO(hclam): I still need to think what I should do here. | |
| 232 } | |
| 233 | |
| 234 void GpuVideoDecoder::SetVideoDecodeEngine(media::VideoDecodeEngine* engine) { | |
| 235 decode_engine_.reset(engine); | |
| 236 } | |
| 237 | |
| 238 void GpuVideoDecoder::SetGpuVideoDevice(GpuVideoDevice* device) { | |
| 239 video_device_.reset(device); | |
| 240 } | |
| 241 | |
| 242 GpuVideoDecoder::GpuVideoDecoder( | |
| 243 MessageLoop* message_loop, | |
| 244 int32 decoder_host_id, | |
| 245 IPC::Message::Sender* sender, | |
| 246 base::ProcessHandle handle, | |
| 247 gpu::gles2::GLES2Decoder* decoder) | |
| 248 : message_loop_(message_loop), | |
| 249 decoder_host_id_(decoder_host_id), | |
| 250 sender_(sender), | |
| 251 renderer_handle_(handle), | |
| 252 gles2_decoder_(decoder) { | |
| 253 memset(&info_, 0, sizeof(info_)); | |
| 254 | |
| 255 // TODO(jiesun): find a better way to determine which VideoDecodeEngine | |
| 256 // to return on current platform. | |
| 257 #if defined(OS_WIN) | |
| 258 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 259 if (command_line.HasSwitch(switches::kEnableAcceleratedDecoding)) { | |
| 260 // The following code are removed so that we don't link them. | |
| 261 // TODO(hclam): Enable the code once the crash is solved on XP. | |
| 262 // decode_engine_.reset(new media::MftH264DecodeEngine(true)); | |
| 263 // video_device_.reset(new MftAngleVideoDevice()); | |
| 264 } | |
| 265 #else | |
| 266 decode_engine_.reset(new FakeGlVideoDecodeEngine()); | |
| 267 video_device_.reset(new FakeGlVideoDevice()); | |
| 268 #endif | |
| 269 } | |
| 270 | |
| 271 GpuVideoDecoder::~GpuVideoDecoder() {} | |
| 272 | |
| 273 void GpuVideoDecoder::OnInitialize(const GpuVideoDecoderInitParam& param) { | |
| 274 // TODO(jiesun): codec id should come from |param|. | |
| 275 media::VideoCodecConfig config(media::kCodecH264, | |
| 276 param.width, | |
| 277 param.height, | |
| 278 param.frame_rate_num, | |
| 279 param.frame_rate_den, | |
| 280 NULL, | |
| 281 0); | |
| 282 decode_engine_->Initialize(message_loop_, this, this, config); | |
| 283 } | |
| 284 | |
| 285 void GpuVideoDecoder::OnUninitialize() { | |
| 286 decode_engine_->Uninitialize(); | |
| 287 } | |
| 288 | |
| 289 void GpuVideoDecoder::OnFlush() { | |
| 290 decode_engine_->Flush(); | |
| 291 } | |
| 292 | |
| 293 void GpuVideoDecoder::OnPreroll() { | |
| 294 decode_engine_->Seek(); | |
| 295 } | |
| 296 | |
| 297 void GpuVideoDecoder::OnEmptyThisBuffer( | |
| 298 const GpuVideoDecoderInputBufferParam& buffer) { | |
| 299 DCHECK(input_transfer_buffer_->memory()); | |
| 300 | |
| 301 uint8* src = static_cast<uint8*>(input_transfer_buffer_->memory()); | |
| 302 | |
| 303 scoped_refptr<Buffer> input_buffer; | |
| 304 uint8* dst = buffer.size ? new uint8[buffer.size] : NULL; | |
| 305 input_buffer = new media::DataBuffer(dst, buffer.size); | |
| 306 memcpy(dst, src, buffer.size); | |
| 307 SendEmptyBufferACK(); | |
| 308 | |
| 309 // Delegate the method call to VideoDecodeEngine. | |
| 310 decode_engine_->ConsumeVideoSample(input_buffer); | |
| 311 } | |
| 312 | |
| 313 void GpuVideoDecoder::OnProduceVideoFrame(int32 frame_id) { | |
| 314 VideoFrameMap::iterator i = video_frame_map_.find(frame_id); | |
| 315 if (i == video_frame_map_.end()) { | |
| 316 NOTREACHED() << "Received a request of unknown frame ID."; | |
| 317 } | |
| 318 | |
| 319 // Delegate the method call to VideoDecodeEngine. | |
| 320 decode_engine_->ProduceVideoFrame(i->second); | |
| 321 } | |
| 322 | |
| 323 void GpuVideoDecoder::OnVideoFrameAllocated(int32 frame_id, | |
| 324 std::vector<uint32> textures) { | |
| 325 bool ret = gles2_decoder_->MakeCurrent(); | |
| 326 DCHECK(ret) << "Failed to switch context"; | |
| 327 | |
| 328 // This method is called in response to a video frame allocation request sent | |
| 329 // to the Renderer process. | |
| 330 // We should use the textures to generate a VideoFrame by using | |
| 331 // GpuVideoDevice. The VideoFrame created is added to the internal map. | |
| 332 // If we have generated enough VideoFrame, we call |allocation_callack_| to | |
| 333 // complete the allocation process. | |
| 334 for (size_t i = 0; i < textures.size(); ++i) { | |
| 335 media::VideoFrame::GlTexture gl_texture; | |
| 336 // Translate the client texture id to service texture id. | |
| 337 ret = gles2_decoder_->GetServiceTextureId(textures[i], &gl_texture); | |
| 338 DCHECK(ret) << "Cannot translate client texture ID to service ID"; | |
| 339 textures[i] = gl_texture; | |
| 340 } | |
| 341 | |
| 342 // Use GpuVideoDevice to allocate VideoFrame objects. | |
| 343 scoped_refptr<media::VideoFrame> frame; | |
| 344 | |
| 345 ret = video_device_->CreateVideoFrameFromGlTextures( | |
| 346 pending_allocation_->width, pending_allocation_->height, | |
| 347 pending_allocation_->format, textures, &frame); | |
| 348 | |
| 349 DCHECK(ret) << "Failed to allocation VideoFrame from GL textures)"; | |
| 350 pending_allocation_->frames->push_back(frame); | |
| 351 video_frame_map_.insert(std::make_pair(frame_id, frame)); | |
| 352 | |
| 353 if (video_frame_map_.size() == pending_allocation_->n) { | |
| 354 pending_allocation_->task->Run(); | |
| 355 delete pending_allocation_->task; | |
| 356 pending_allocation_.reset(); | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 void GpuVideoDecoder::SendInitializeDone( | |
| 361 const GpuVideoDecoderInitDoneParam& param) { | |
| 362 if (!sender_->Send( | |
| 363 new GpuVideoDecoderHostMsg_InitializeACK(decoder_host_id(), param))) { | |
| 364 LOG(ERROR) << "GpuVideoDecoderMsg_InitializeACK failed"; | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 void GpuVideoDecoder::SendUninitializeDone() { | |
| 369 if (!sender_->Send( | |
| 370 new GpuVideoDecoderHostMsg_DestroyACK(decoder_host_id()))) { | |
| 371 LOG(ERROR) << "GpuVideoDecoderMsg_DestroyACK failed"; | |
| 372 } | |
| 373 } | |
| 374 | |
| 375 void GpuVideoDecoder::SendFlushDone() { | |
| 376 if (!sender_->Send(new GpuVideoDecoderHostMsg_FlushACK(decoder_host_id()))) { | |
| 377 LOG(ERROR) << "GpuVideoDecoderMsg_FlushACK failed"; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 void GpuVideoDecoder::SendPrerollDone() { | |
| 382 if (!sender_->Send(new GpuVideoDecoderHostMsg_PrerollDone( | |
| 383 decoder_host_id()))) { | |
| 384 LOG(ERROR) << "GpuVideoDecoderMsg_PrerollDone failed"; | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 void GpuVideoDecoder::SendEmptyBufferDone() { | |
| 389 if (!sender_->Send( | |
| 390 new GpuVideoDecoderHostMsg_EmptyThisBufferDone(decoder_host_id()))) { | |
| 391 LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBufferDone failed"; | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 void GpuVideoDecoder::SendEmptyBufferACK() { | |
| 396 if (!sender_->Send( | |
| 397 new GpuVideoDecoderHostMsg_EmptyThisBufferACK(decoder_host_id()))) { | |
| 398 LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBufferACK failed"; | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 void GpuVideoDecoder::SendConsumeVideoFrame( | |
| 403 int32 frame_id, int64 timestamp, int64 duration, int32 flags) { | |
| 404 if (!sender_->Send( | |
| 405 new GpuVideoDecoderHostMsg_ConsumeVideoFrame( | |
| 406 decoder_host_id(), frame_id, timestamp, duration, flags))) { | |
| 407 LOG(ERROR) << "GpuVideoDecodeHostMsg_ConsumeVideoFrame failed."; | |
| 408 } | |
| 409 } | |
| 410 | |
| 411 void GpuVideoDecoder::SendAllocateVideoFrames( | |
| 412 int n, size_t width, size_t height, media::VideoFrame::Format format) { | |
| 413 if (!sender_->Send( | |
| 414 new GpuVideoDecoderHostMsg_AllocateVideoFrames( | |
| 415 decoder_host_id(), n, width, height, | |
| 416 static_cast<int32>(format)))) { | |
| 417 LOG(ERROR) << "GpuVideoDecoderMsg_AllocateVideoFrames failed"; | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 void GpuVideoDecoder::SendReleaseAllVideoFrames() { | |
| 422 if (!sender_->Send( | |
| 423 new GpuVideoDecoderHostMsg_ReleaseAllVideoFrames( | |
| 424 decoder_host_id()))) { | |
| 425 LOG(ERROR) << "GpuVideoDecoderMsg_ReleaseAllVideoFrames failed"; | |
| 426 } | |
| 427 } | |
| OLD | NEW |