Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012 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/decrypting_video_decoder.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback_helpers.h" | |
| 9 #include "base/location.h" | |
| 10 #include "base/message_loop_proxy.h" | |
| 11 #include "media/base/decoder_buffer.h" | |
| 12 #include "media/base/decryptor.h" | |
| 13 #include "media/base/demuxer_stream.h" | |
| 14 #include "media/base/pipeline.h" | |
| 15 #include "media/base/video_decoder_config.h" | |
| 16 #include "media/base/video_frame.h" | |
| 17 | |
| 18 namespace media { | |
| 19 | |
| 20 DecryptingVideoDecoder::DecryptingVideoDecoder( | |
| 21 const MessageLoopFactoryCB& message_loop_factory_cb, | |
| 22 Decryptor* decryptor) | |
| 23 : message_loop_factory_cb_(message_loop_factory_cb), | |
| 24 state_(kUninitialized), | |
| 25 decryptor_(decryptor) { | |
| 26 } | |
| 27 | |
| 28 void DecryptingVideoDecoder::Initialize( | |
| 29 const scoped_refptr<DemuxerStream>& stream, | |
| 30 const PipelineStatusCB& status_cb, | |
| 31 const StatisticsCB& statistics_cb) { | |
| 32 DCHECK(!message_loop_); | |
| 33 message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run(); | |
| 34 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 35 &DecryptingVideoDecoder::DoInitialize, this, | |
| 36 stream, status_cb, statistics_cb)); | |
| 37 } | |
| 38 | |
| 39 void DecryptingVideoDecoder::Read(const ReadCB& read_cb) { | |
| 40 // Complete operation asynchronously on different stack of execution as per | |
| 41 // the API contract of VideoDecoder::Read() | |
| 42 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 43 &DecryptingVideoDecoder::DoRead, this, read_cb)); | |
| 44 } | |
| 45 | |
| 46 void DecryptingVideoDecoder::Reset(const base::Closure& closure) { | |
| 47 if (!message_loop_->BelongsToCurrentThread()) { | |
| 48 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 49 &DecryptingVideoDecoder::Reset, this, closure)); | |
| 50 return; | |
| 51 } | |
| 52 | |
| 53 DCHECK_NE(state_, kUninitialized); | |
| 54 DCHECK(init_cb_.is_null()); // No Reset() during pending initialization. | |
| 55 DCHECK(stop_cb_.is_null()); // No Reset() during pending Stop(). | |
| 56 DCHECK(reset_cb_.is_null()); | |
| 57 reset_cb_ = closure; | |
| 58 | |
| 59 decryptor_->CancelDecryptAndDecodeVideo(); | |
| 60 | |
| 61 // Reset() cannot complete if the read callback is still pending. | |
| 62 // Defer the resetting process in this case. The |reset_cb_| will be fired | |
| 63 // after the read callback is fired - see DoDecryptAndDecodeBuffer() and | |
| 64 // DoDeliverFrame(). | |
| 65 if (!read_cb_.is_null()) | |
| 66 return; | |
| 67 | |
| 68 DoReset(); | |
| 69 } | |
| 70 | |
| 71 void DecryptingVideoDecoder::Stop(const base::Closure& closure) { | |
| 72 if (!message_loop_->BelongsToCurrentThread()) { | |
| 73 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 74 &DecryptingVideoDecoder::Stop, this, closure)); | |
| 75 return; | |
| 76 } | |
| 77 | |
| 78 DCHECK(stop_cb_.is_null()); | |
| 79 stop_cb_ = closure; | |
| 80 | |
| 81 decryptor_->StopVideoDecoder(); | |
| 82 | |
| 83 // Stop() cannot complete if the init or read callback is still pending. | |
| 84 // Defer the stopping process in these cases. The |stop_cb_| will be fired | |
| 85 // after the init or read callback is fired - see DoFinishInitialization(), | |
| 86 // DoDecryptAndDecodeBuffer() and DoDeliverFrame(). | |
| 87 if (!init_cb_.is_null() || !read_cb_.is_null()) | |
| 88 return; | |
| 89 | |
| 90 DoStop(); | |
| 91 } | |
| 92 | |
| 93 DecryptingVideoDecoder::~DecryptingVideoDecoder() { | |
| 94 DCHECK_EQ(state_, kUninitialized); | |
| 95 } | |
| 96 | |
| 97 void DecryptingVideoDecoder::DoInitialize( | |
| 98 const scoped_refptr<DemuxerStream>& stream, | |
| 99 const PipelineStatusCB& status_cb, | |
| 100 const StatisticsCB& statistics_cb) { | |
| 101 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 102 DCHECK(stream); | |
| 103 DCHECK_EQ(state_, kUninitialized); | |
| 104 | |
| 105 const VideoDecoderConfig& config = stream->video_decoder_config(); | |
| 106 if (!config.IsValidConfig()) { | |
| 107 DLOG(ERROR) << "Invalid video stream config: " | |
| 108 << config.AsHumanReadableString(); | |
| 109 status_cb.Run(PIPELINE_ERROR_DECODE); | |
| 110 return; | |
| 111 } | |
| 112 | |
| 113 // DecryptingVideoDecoder only accepts potentially encrypted stream. | |
| 114 if (!config.is_encrypted()) { | |
| 115 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | |
| 116 return; | |
| 117 } | |
| 118 | |
| 119 DCHECK(!demuxer_stream_); | |
| 120 demuxer_stream_ = stream; | |
| 121 statistics_cb_ = statistics_cb; | |
| 122 | |
| 123 init_cb_ = status_cb; | |
| 124 decryptor_->InitializeVideoDecoder(config, base::Bind( | |
| 125 &DecryptingVideoDecoder::FinishInitialization, this)); | |
| 126 } | |
| 127 | |
| 128 void DecryptingVideoDecoder::FinishInitialization(bool success) { | |
| 129 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 130 &DecryptingVideoDecoder::DoFinishInitialization, this, success)); | |
| 131 } | |
| 132 | |
| 133 void DecryptingVideoDecoder::DoFinishInitialization(bool success) { | |
| 134 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 135 DCHECK_EQ(state_, kUninitialized); | |
| 136 DCHECK(!init_cb_.is_null()); | |
| 137 DCHECK(reset_cb_.is_null()); | |
| 138 DCHECK(read_cb_.is_null()); | |
| 139 | |
| 140 if (!stop_cb_.is_null()) { | |
| 141 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); | |
| 142 DoStop(); | |
| 143 return; | |
| 144 } | |
| 145 | |
| 146 if (!success) { | |
| 147 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); | |
| 148 return; | |
| 149 } | |
| 150 | |
| 151 // Success! | |
| 152 state_ = kNormal; | |
| 153 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); | |
| 154 } | |
| 155 | |
| 156 void DecryptingVideoDecoder::DoRead(const ReadCB& read_cb) { | |
| 157 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 158 DCHECK(!read_cb.is_null()); | |
| 159 CHECK_NE(state_, kUninitialized); | |
| 160 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
| 161 | |
| 162 // Return empty frames if decoding has finished. | |
| 163 if (state_ == kDecodeFinished) { | |
| 164 read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); | |
| 165 return; | |
| 166 } | |
| 167 | |
| 168 read_cb_ = read_cb; | |
| 169 ReadFromDemuxerStream(); | |
| 170 } | |
| 171 | |
| 172 void DecryptingVideoDecoder::ReadFromDemuxerStream() { | |
| 173 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 174 DCHECK_EQ(state_, kNormal); | |
| 175 DCHECK(!read_cb_.is_null()); | |
| 176 | |
| 177 demuxer_stream_->Read( | |
| 178 base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, this)); | |
| 179 } | |
| 180 | |
| 181 void DecryptingVideoDecoder::DecryptAndDecodeBuffer( | |
| 182 DemuxerStream::Status status, | |
| 183 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 184 // In theory, we don't need to force post the task here, because we do a | |
| 185 // force task post in DeliverFrame(). Therefore, even if | |
| 186 // demuxer_stream_->Read() execute the read callback on the same execution | |
| 187 // stack we are still fine. But it looks like a force post task makes the | |
| 188 // logic more understandable and manageable, so why not? | |
| 189 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 190 &DecryptingVideoDecoder::DoDecryptAndDecodeBuffer, this, | |
| 191 status, buffer)); | |
| 192 } | |
| 193 | |
| 194 void DecryptingVideoDecoder::DoDecryptAndDecodeBuffer( | |
| 195 DemuxerStream::Status status, | |
| 196 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 197 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 198 DCHECK_EQ(state_, kNormal); | |
| 199 DCHECK(!read_cb_.is_null()); | |
| 200 DCHECK(status == DemuxerStream::kOk ? buffer : !buffer) << status; | |
|
scherkus (not reviewing)
2012/10/04 00:15:13
this is even more confusing
I was suggesting
DCHE
xhwang
2012/10/04 16:32:54
Done.
| |
| 201 | |
| 202 if (!stop_cb_.is_null()) { | |
| 203 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 204 if (!reset_cb_.is_null()) | |
| 205 DoReset(); | |
| 206 | |
| 207 DoStop(); | |
| 208 return; | |
| 209 } | |
| 210 | |
| 211 if (!reset_cb_.is_null()) { | |
| 212 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 213 DoReset(); | |
| 214 return; | |
| 215 } | |
| 216 | |
| 217 if (status == DemuxerStream::kAborted) { | |
| 218 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 219 return; | |
| 220 } | |
| 221 | |
| 222 if (status == DemuxerStream::kConfigChanged) { | |
| 223 // TODO(xhwang): Add config change support. | |
| 224 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 DCHECK_EQ(status, DemuxerStream::kOk); | |
| 229 decryptor_->DecryptAndDecodeVideo( | |
| 230 buffer, base::Bind(&DecryptingVideoDecoder::DeliverFrame, this, | |
| 231 buffer->GetDataSize())); | |
| 232 } | |
| 233 | |
| 234 void DecryptingVideoDecoder::DeliverFrame( | |
| 235 int buffer_size, | |
| 236 Decryptor::Status status, | |
| 237 const scoped_refptr<VideoFrame>& frame) { | |
| 238 // We need to force task post here because the VideoDecodeCB can be executed | |
| 239 // synchronously in Reset()/Stop(). Instead of using more complicated logic in | |
| 240 // those function to fix it, why not force task post here to make everything | |
| 241 // simple and clear? | |
| 242 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 243 &DecryptingVideoDecoder::DoDeliverFrame, this, | |
| 244 buffer_size, status, frame)); | |
| 245 } | |
| 246 | |
| 247 void DecryptingVideoDecoder::DoDeliverFrame( | |
| 248 int buffer_size, | |
| 249 Decryptor::Status status, | |
| 250 const scoped_refptr<VideoFrame>& frame) { | |
| 251 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 252 DCHECK_EQ(state_, kNormal); | |
| 253 DCHECK(!read_cb_.is_null()); | |
| 254 | |
| 255 if (!stop_cb_.is_null()) { | |
| 256 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 257 if (!reset_cb_.is_null()) | |
| 258 DoReset(); | |
| 259 | |
| 260 DoStop(); | |
| 261 return; | |
| 262 } | |
| 263 | |
| 264 if (!reset_cb_.is_null()) { | |
| 265 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 266 DoReset(); | |
| 267 return; | |
| 268 } | |
| 269 | |
| 270 if (status == Decryptor::kNoKey || status == Decryptor::kError) { | |
| 271 DCHECK(!frame); | |
| 272 state_ = kDecodeFinished; | |
| 273 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
| 274 return; | |
| 275 } | |
| 276 | |
| 277 // The buffer has been accepted by the decoder, let's report statistics. | |
| 278 if (buffer_size) { | |
| 279 PipelineStatistics statistics; | |
| 280 statistics.video_bytes_decoded = buffer_size; | |
| 281 statistics_cb_.Run(statistics); | |
| 282 } | |
| 283 | |
| 284 if (status == Decryptor::kNeedMoreData) { | |
| 285 DCHECK(!frame); | |
| 286 ReadFromDemuxerStream(); | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 DCHECK_EQ(status, Decryptor::kSuccess); | |
| 291 if (frame->IsEndOfStream()) | |
| 292 state_ = kDecodeFinished; | |
| 293 | |
| 294 base::ResetAndReturn(&read_cb_).Run(kOk, frame); | |
| 295 } | |
| 296 | |
| 297 void DecryptingVideoDecoder::DoReset() { | |
| 298 DCHECK(read_cb_.is_null()); | |
| 299 DCHECK_NE(state_, kUninitialized); | |
| 300 state_ = kNormal; | |
| 301 base::ResetAndReturn(&reset_cb_).Run(); | |
| 302 } | |
| 303 | |
| 304 void DecryptingVideoDecoder::DoStop() { | |
| 305 DCHECK(read_cb_.is_null()); | |
| 306 state_ = kUninitialized; | |
| 307 base::ResetAndReturn(&stop_cb_).Run(); | |
| 308 } | |
| 309 | |
| 310 } // namespace media | |
| OLD | NEW |