| 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 "media/filters/gpu_video_decoder.h" | 
|  | 6 | 
|  | 7 #include "base/bind.h" | 
|  | 8 #include "base/message_loop.h" | 
|  | 9 #include "media/base/demuxer_stream.h" | 
|  | 10 #include "media/base/filter_host.h" | 
|  | 11 #include "media/base/video_decoder_config.h" | 
|  | 12 #include "media/ffmpeg/ffmpeg_common.h" | 
|  | 13 | 
|  | 14 namespace media { | 
|  | 15 | 
|  | 16 // Size of shared-memory segments we allocate.  Since we reuse them we let them | 
|  | 17 // be on the beefy side. | 
|  | 18 static const size_t kSharedMemorySegmentBytes = 100 << 10; | 
|  | 19 | 
|  | 20 GpuVideoDecoder::GpuVideoDecoder( | 
|  | 21     MessageLoop* message_loop, | 
|  | 22     Factories* factories) | 
|  | 23     : message_loop_(message_loop), | 
|  | 24       factories_(factories), | 
|  | 25       flush_in_progress_(false), | 
|  | 26       demuxer_read_in_progress_(false), | 
|  | 27       next_picture_buffer_id_(0), | 
|  | 28       next_bitstream_buffer_id_(0) { | 
|  | 29   DCHECK(message_loop_ && factories_.get()); | 
|  | 30 } | 
|  | 31 | 
|  | 32 GpuVideoDecoder::~GpuVideoDecoder() { | 
|  | 33   DCHECK(!vda_);  // Stop should have been already called. | 
|  | 34   for (size_t i = 0; i < available_shm_segments_.size(); ++i) | 
|  | 35     delete available_shm_segments_[i]; | 
|  | 36   available_shm_segments_.clear(); | 
|  | 37   for (std::map<int32, BufferPair>::iterator it = | 
|  | 38            bitstream_buffers_in_decoder_.begin(); | 
|  | 39        it != bitstream_buffers_in_decoder_.end(); ++it) { | 
|  | 40     it->second.first->first->Close(); | 
|  | 41   } | 
|  | 42   bitstream_buffers_in_decoder_.clear(); | 
|  | 43 } | 
|  | 44 | 
|  | 45 void GpuVideoDecoder::Stop(const base::Closure& callback) { | 
|  | 46   if (MessageLoop::current() != message_loop_) { | 
|  | 47     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 48         &GpuVideoDecoder::Stop, this, callback)); | 
|  | 49     return; | 
|  | 50   } | 
|  | 51   if (!vda_) | 
|  | 52     return; | 
|  | 53   vda_->Destroy(); | 
|  | 54   vda_ = NULL; | 
|  | 55   callback.Run(); | 
|  | 56 } | 
|  | 57 | 
|  | 58 void GpuVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { | 
|  | 59   if (MessageLoop::current() != message_loop_) { | 
|  | 60     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 61         &GpuVideoDecoder::Seek, this, time, cb)); | 
|  | 62     return; | 
|  | 63   } | 
|  | 64   pts_stream_.Seek(time); | 
|  | 65   cb.Run(PIPELINE_OK); | 
|  | 66 } | 
|  | 67 | 
|  | 68 void GpuVideoDecoder::Pause(const base::Closure& callback)  { | 
|  | 69   if (MessageLoop::current() != message_loop_) { | 
|  | 70     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 71         &GpuVideoDecoder::Pause, this, callback)); | 
|  | 72     return; | 
|  | 73   } | 
|  | 74   callback.Run(); | 
|  | 75 } | 
|  | 76 | 
|  | 77 void GpuVideoDecoder::Flush(const base::Closure& callback)  { | 
|  | 78   if (MessageLoop::current() != message_loop_) { | 
|  | 79     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 80         &GpuVideoDecoder::Flush, this, callback)); | 
|  | 81     return; | 
|  | 82   } | 
|  | 83   if (!vda_) | 
|  | 84     return; | 
|  | 85   DCHECK(pending_flush_cb_.is_null()); | 
|  | 86   pending_flush_cb_ = callback; | 
|  | 87   pts_stream_.Flush(); | 
|  | 88   vda_->Reset(); | 
|  | 89 } | 
|  | 90 | 
|  | 91 void GpuVideoDecoder::Initialize(DemuxerStream* demuxer_stream, | 
|  | 92                                  const PipelineStatusCB& callback, | 
|  | 93                                  const StatisticsCallback& stats_callback) { | 
|  | 94   if (MessageLoop::current() != message_loop_) { | 
|  | 95     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 96         &GpuVideoDecoder::Initialize, this, | 
|  | 97         make_scoped_refptr(demuxer_stream), callback, stats_callback)); | 
|  | 98     return; | 
|  | 99   } | 
|  | 100 | 
|  | 101   DCHECK(!demuxer_stream_); | 
|  | 102   if (!demuxer_stream) { | 
|  | 103     callback.Run(PIPELINE_ERROR_DECODE); | 
|  | 104     return; | 
|  | 105   } | 
|  | 106 | 
|  | 107   const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); | 
|  | 108   // TODO(scherkus): this check should go in PipelineImpl prior to creating | 
|  | 109   // decoder objects. | 
|  | 110   if (!config.IsValidConfig()) { | 
|  | 111     DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); | 
|  | 112     callback.Run(PIPELINE_ERROR_DECODE); | 
|  | 113     return; | 
|  | 114   } | 
|  | 115 | 
|  | 116   vda_ = factories_->CreateVideoDecodeAccelerator(config.profile(), this); | 
|  | 117   if (!vda_) { | 
|  | 118     callback.Run(DECODER_ERROR_NOT_SUPPORTED); | 
|  | 119     return; | 
|  | 120   } | 
|  | 121 | 
|  | 122   demuxer_stream_ = demuxer_stream; | 
|  | 123   statistics_callback_ = stats_callback; | 
|  | 124 | 
|  | 125   demuxer_stream_->EnableBitstreamConverter(); | 
|  | 126 | 
|  | 127   pts_stream_.Initialize(GetFrameDuration(config)); | 
|  | 128   natural_size_ = config.natural_size(); | 
|  | 129 | 
|  | 130   callback.Run(PIPELINE_OK); | 
|  | 131 } | 
|  | 132 | 
|  | 133 void GpuVideoDecoder::Read(const ReadCB& callback) { | 
|  | 134   if (MessageLoop::current() != message_loop_) { | 
|  | 135     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 136         &GpuVideoDecoder::Read, this, callback)); | 
|  | 137     return; | 
|  | 138   } | 
|  | 139 | 
|  | 140   DCHECK(pending_read_cb_.is_null()); | 
|  | 141   pending_read_cb_ = callback; | 
|  | 142 | 
|  | 143   if (!ready_video_frames_.empty()) { | 
|  | 144     DeliverFrame(ready_video_frames_.front()); | 
|  | 145     ready_video_frames_.pop_front(); | 
|  | 146     return; | 
|  | 147   } | 
|  | 148   EnsureDemuxOrDecode(); | 
|  | 149 } | 
|  | 150 | 
|  | 151 void GpuVideoDecoder::RequestBufferDecode(const scoped_refptr<Buffer>& buffer) { | 
|  | 152   if (MessageLoop::current() != message_loop_) { | 
|  | 153     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 154         &GpuVideoDecoder::RequestBufferDecode, this, buffer)); | 
|  | 155     return; | 
|  | 156   } | 
|  | 157   demuxer_read_in_progress_ = false; | 
|  | 158 | 
|  | 159   if (!vda_) | 
|  | 160     return; | 
|  | 161 | 
|  | 162   if (buffer->IsEndOfStream()) { | 
|  | 163     if (!flush_in_progress_) { | 
|  | 164       flush_in_progress_ = true; | 
|  | 165       vda_->Flush(); | 
|  | 166     } | 
|  | 167     return; | 
|  | 168   } | 
|  | 169 | 
|  | 170   size_t size = buffer->GetDataSize(); | 
|  | 171   SHMBuffer* shm_buffer = GetSHM(size); | 
|  | 172   memcpy(shm_buffer->first->memory(), buffer->GetData(), size); | 
|  | 173   BitstreamBuffer bitstream_buffer( | 
|  | 174       next_bitstream_buffer_id_++, shm_buffer->first->handle(), size); | 
|  | 175   bool inserted = bitstream_buffers_in_decoder_.insert(std::make_pair( | 
|  | 176       bitstream_buffer.id(), std::make_pair(shm_buffer, buffer))).second; | 
|  | 177   DCHECK(inserted); | 
|  | 178   pts_stream_.EnqueuePts(buffer.get()); | 
|  | 179 | 
|  | 180   vda_->Decode(bitstream_buffer); | 
|  | 181 } | 
|  | 182 | 
|  | 183 const gfx::Size& GpuVideoDecoder::natural_size() { | 
|  | 184   return natural_size_; | 
|  | 185 } | 
|  | 186 | 
|  | 187 void GpuVideoDecoder::NotifyInitializeDone() { | 
|  | 188   NOTREACHED() << "GpuVideoDecodeAcceleratorHost::Initialize is synchronous!"; | 
|  | 189 } | 
|  | 190 | 
|  | 191 void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, | 
|  | 192                                             const gfx::Size& size) { | 
|  | 193   if (MessageLoop::current() != message_loop_) { | 
|  | 194     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 195         &GpuVideoDecoder::ProvidePictureBuffers, this, count, size)); | 
|  | 196     return; | 
|  | 197   } | 
|  | 198   std::vector<uint32> texture_ids; | 
|  | 199   if (!factories_->CreateTextures(count, size, &texture_ids)) { | 
|  | 200     NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); | 
|  | 201     return; | 
|  | 202   } | 
|  | 203 | 
|  | 204   if (!vda_) | 
|  | 205     return; | 
|  | 206 | 
|  | 207   std::vector<PictureBuffer> picture_buffers; | 
|  | 208   for (size_t i = 0; i < texture_ids.size(); ++i) { | 
|  | 209     picture_buffers.push_back(PictureBuffer( | 
|  | 210         next_picture_buffer_id_++, size, texture_ids[i])); | 
|  | 211     bool inserted = picture_buffers_in_decoder_.insert(std::make_pair( | 
|  | 212         picture_buffers.back().id(), picture_buffers.back())).second; | 
|  | 213     DCHECK(inserted); | 
|  | 214   } | 
|  | 215   vda_->AssignPictureBuffers(picture_buffers); | 
|  | 216 } | 
|  | 217 | 
|  | 218 void GpuVideoDecoder::DismissPictureBuffer(int32 id) { | 
|  | 219   if (MessageLoop::current() != message_loop_) { | 
|  | 220     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 221         &GpuVideoDecoder::DismissPictureBuffer, this, id)); | 
|  | 222     return; | 
|  | 223   } | 
|  | 224   std::map<int32, PictureBuffer>::iterator it = | 
|  | 225       picture_buffers_in_decoder_.find(id); | 
|  | 226   if (it == picture_buffers_in_decoder_.end()) { | 
|  | 227     NOTREACHED() << "Missing picture buffer: " << id; | 
|  | 228     return; | 
|  | 229   } | 
|  | 230   if (!factories_->DeleteTexture(it->second.texture_id())) { | 
|  | 231     NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); | 
|  | 232     return; | 
|  | 233   } | 
|  | 234   picture_buffers_in_decoder_.erase(it); | 
|  | 235 } | 
|  | 236 | 
|  | 237 static void ResetAndRunCB(VideoDecoder::ReadCB* cb, | 
|  | 238                           scoped_refptr<VideoFrame> frame) { | 
|  | 239   DCHECK(!cb->is_null()); | 
|  | 240   VideoDecoder::ReadCB tmp_cb(*cb); | 
|  | 241   cb->Reset(); | 
|  | 242   tmp_cb.Run(frame); | 
|  | 243 } | 
|  | 244 | 
|  | 245 void GpuVideoDecoder::PictureReady(const media::Picture& picture) { | 
|  | 246   if (MessageLoop::current() != message_loop_) { | 
|  | 247     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 248         &GpuVideoDecoder::PictureReady, this, picture)); | 
|  | 249     return; | 
|  | 250   } | 
|  | 251   std::map<int32, PictureBuffer>::iterator it = | 
|  | 252       picture_buffers_in_decoder_.find(picture.picture_buffer_id()); | 
|  | 253   if (it == picture_buffers_in_decoder_.end()) { | 
|  | 254     NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id(); | 
|  | 255     NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); | 
|  | 256     return; | 
|  | 257   } | 
|  | 258   const PictureBuffer& pb = it->second; | 
|  | 259 | 
|  | 260   // Update frame's timestamp. | 
|  | 261   base::TimeDelta timestamp; | 
|  | 262   base::TimeDelta duration; | 
|  | 263   std::map<int32, BufferPair>::const_iterator buf_it = | 
|  | 264       bitstream_buffers_in_decoder_.find(picture.bitstream_buffer_id()); | 
|  | 265   if (buf_it != bitstream_buffers_in_decoder_.end()) { | 
|  | 266     // Sufficiently out-of-order decoding could have already called | 
|  | 267     // NotifyEndOfBitstreamBuffer on this buffer, but that's ok since we only | 
|  | 268     // need the buffer's time info for best-effort PTS updating. | 
|  | 269     timestamp = buf_it->second.second->GetTimestamp(); | 
|  | 270     duration = buf_it->second.second->GetDuration(); | 
|  | 271   } | 
|  | 272 | 
|  | 273   scoped_refptr<VideoFrame> frame(VideoFrame::WrapNativeTexture( | 
|  | 274       pb.texture_id(), pb.size().width(), | 
|  | 275       pb.size().height(), timestamp, duration, | 
|  | 276       base::Bind(&GpuVideoDecoder::ReusePictureBuffer, this, | 
|  | 277                  picture.picture_buffer_id()))); | 
|  | 278   pts_stream_.UpdatePtsAndDuration(frame.get()); | 
|  | 279   frame->SetTimestamp(pts_stream_.current_pts()); | 
|  | 280   frame->SetDuration(pts_stream_.current_duration()); | 
|  | 281 | 
|  | 282   // Deliver the frame. | 
|  | 283   DeliverFrame(frame); | 
|  | 284 } | 
|  | 285 | 
|  | 286 void GpuVideoDecoder::DeliverFrame(const scoped_refptr<VideoFrame>& frame) { | 
|  | 287     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 288         &GpuVideoDecoder::DeliverFrameOutOfLine, this, frame)); | 
|  | 289 } | 
|  | 290 | 
|  | 291 void GpuVideoDecoder::DeliverFrameOutOfLine( | 
|  | 292     const scoped_refptr<VideoFrame>& frame) { | 
|  | 293   if (pending_read_cb_.is_null()) { | 
|  | 294     ready_video_frames_.push_back(frame); | 
|  | 295     return; | 
|  | 296   } | 
|  | 297   ResetAndRunCB(&pending_read_cb_, frame); | 
|  | 298 } | 
|  | 299 | 
|  | 300 void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) { | 
|  | 301   if (MessageLoop::current() != message_loop_) { | 
|  | 302     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 303         &GpuVideoDecoder::ReusePictureBuffer, this, picture_buffer_id)); | 
|  | 304     return; | 
|  | 305   } | 
|  | 306   if (!vda_) | 
|  | 307     return; | 
|  | 308   vda_->ReusePictureBuffer(picture_buffer_id); | 
|  | 309 } | 
|  | 310 | 
|  | 311 GpuVideoDecoder::SHMBuffer* GpuVideoDecoder::GetSHM(size_t min_size) { | 
|  | 312   DCHECK(MessageLoop::current() == message_loop_); | 
|  | 313   if (available_shm_segments_.empty() || | 
|  | 314       available_shm_segments_.back()->second < min_size) { | 
|  | 315     size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); | 
|  | 316     base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate); | 
|  | 317     DCHECK(shm); | 
|  | 318     return new SHMBuffer(shm, size_to_allocate); | 
|  | 319   } | 
|  | 320   SHMBuffer* ret = available_shm_segments_.back(); | 
|  | 321   available_shm_segments_.pop_back(); | 
|  | 322   return ret; | 
|  | 323 } | 
|  | 324 | 
|  | 325 void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) { | 
|  | 326   DCHECK(MessageLoop::current() == message_loop_); | 
|  | 327   available_shm_segments_.push_back(shm_buffer); | 
|  | 328 } | 
|  | 329 | 
|  | 330 void GpuVideoDecoder::NotifyEndOfStream() { | 
|  | 331   if (MessageLoop::current() != message_loop_) { | 
|  | 332     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 333         &GpuVideoDecoder::NotifyEndOfStream, this)); | 
|  | 334     return; | 
|  | 335   } | 
|  | 336   DeliverFrame(VideoFrame::CreateEmptyFrame()); | 
|  | 337 } | 
|  | 338 | 
|  | 339 void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { | 
|  | 340   if (MessageLoop::current() != message_loop_) { | 
|  | 341     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 342         &GpuVideoDecoder::NotifyEndOfBitstreamBuffer, this, id)); | 
|  | 343     return; | 
|  | 344   } | 
|  | 345 | 
|  | 346   std::map<int32, BufferPair>::iterator it = | 
|  | 347       bitstream_buffers_in_decoder_.find(id); | 
|  | 348   if (it == bitstream_buffers_in_decoder_.end()) { | 
|  | 349     NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); | 
|  | 350     NOTREACHED() << "Missing bitstream buffer: " << id; | 
|  | 351     return; | 
|  | 352   } | 
|  | 353   PutSHM(it->second.first); | 
|  | 354   const scoped_refptr<Buffer>& buffer = it->second.second; | 
|  | 355   if (buffer->GetDataSize()) { | 
|  | 356     PipelineStatistics statistics; | 
|  | 357     statistics.video_bytes_decoded = buffer->GetDataSize(); | 
|  | 358     statistics_callback_.Run(statistics); | 
|  | 359   } | 
|  | 360   bitstream_buffers_in_decoder_.erase(it); | 
|  | 361 | 
|  | 362   if (!pending_read_cb_.is_null()) { | 
|  | 363     DCHECK(ready_video_frames_.empty()); | 
|  | 364     EnsureDemuxOrDecode(); | 
|  | 365   } | 
|  | 366 } | 
|  | 367 | 
|  | 368 void GpuVideoDecoder::EnsureDemuxOrDecode() { | 
|  | 369   DCHECK(MessageLoop::current() == message_loop_); | 
|  | 370   if (demuxer_read_in_progress_ || !bitstream_buffers_in_decoder_.empty()) | 
|  | 371     return; | 
|  | 372   demuxer_read_in_progress_ = true; | 
|  | 373   demuxer_stream_->Read(base::Bind( | 
|  | 374       &GpuVideoDecoder::RequestBufferDecode, this)); | 
|  | 375 } | 
|  | 376 | 
|  | 377 void GpuVideoDecoder::NotifyFlushDone() { | 
|  | 378   if (MessageLoop::current() != message_loop_) { | 
|  | 379     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 380         &GpuVideoDecoder::NotifyFlushDone, this)); | 
|  | 381     return; | 
|  | 382   } | 
|  | 383   DCHECK(flush_in_progress_); | 
|  | 384   flush_in_progress_ = false; | 
|  | 385 } | 
|  | 386 | 
|  | 387 void GpuVideoDecoder::NotifyResetDone() { | 
|  | 388   if (MessageLoop::current() != message_loop_) { | 
|  | 389     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 390         &GpuVideoDecoder::NotifyResetDone, this)); | 
|  | 391     return; | 
|  | 392   } | 
|  | 393   ResetAndRunCB(&pending_flush_cb_); | 
|  | 394 } | 
|  | 395 | 
|  | 396 void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { | 
|  | 397   if (MessageLoop::current() != message_loop_) { | 
|  | 398     message_loop_->PostTask(FROM_HERE, base::Bind( | 
|  | 399         &GpuVideoDecoder::NotifyError, this, error)); | 
|  | 400     return; | 
|  | 401   } | 
|  | 402   vda_ = NULL; | 
|  | 403   DLOG(ERROR) << "VDA Error: " << error; | 
|  | 404   host()->SetError(PIPELINE_ERROR_DECODE); | 
|  | 405 } | 
|  | 406 | 
|  | 407 | 
|  | 408 }  // namespace media | 
| OLD | NEW | 
|---|