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