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 |