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_demuxer_stream.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback_helpers.h" | |
| 9 #include "base/location.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/message_loop_proxy.h" | |
| 12 #include "media/base/audio_decoder_config.h" | |
| 13 #include "media/base/video_decoder_config.h" | |
| 14 #include "media/base/bind_to_loop.h" | |
| 15 #include "media/base/decoder_buffer.h" | |
| 16 #include "media/base/decryptor.h" | |
| 17 #include "media/base/demuxer_stream.h" | |
| 18 #include "media/base/pipeline.h" | |
| 19 | |
| 20 namespace media { | |
| 21 | |
| 22 #define BIND_TO_LOOP(function) \ | |
| 23 media::BindToLoop(message_loop_, base::Bind(function, this)) | |
| 24 | |
| 25 DecryptingDemuxerStream::DecryptingDemuxerStream( | |
| 26 const MessageLoopFactoryCB& message_loop_factory_cb, | |
| 27 const RequestDecryptorNotificationCB& request_decryptor_notification_cb) | |
| 28 : message_loop_factory_cb_(message_loop_factory_cb), | |
| 29 state_(kUninitialized), | |
| 30 stream_type_(UNKNOWN), | |
| 31 request_decryptor_notification_cb_(request_decryptor_notification_cb), | |
| 32 decryptor_(NULL), | |
| 33 key_added_while_pending_decrypt_(false) { | |
| 34 } | |
| 35 | |
| 36 void DecryptingDemuxerStream::Initialize( | |
| 37 const scoped_refptr<DemuxerStream>& stream, | |
| 38 const PipelineStatusCB& status_cb) { | |
| 39 DCHECK(!message_loop_); | |
| 40 message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run(); | |
| 41 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 42 &DecryptingDemuxerStream::DoInitialize, this, | |
| 43 stream, status_cb)); | |
| 44 } | |
| 45 | |
| 46 void DecryptingDemuxerStream::Read(const ReadCB& read_cb) { | |
| 47 DVLOG(3) << "Read()"; | |
| 48 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 49 DCHECK(state_ == kIdle) << state_; | |
| 50 DCHECK(!read_cb.is_null()); | |
| 51 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported."; | |
| 52 | |
| 53 read_cb_ = read_cb; | |
| 54 state_ = kPendingDemuxerRead; | |
| 55 demuxer_stream_->Read( | |
| 56 base::Bind(&DecryptingDemuxerStream::DecryptBuffer, this)); | |
| 57 } | |
| 58 | |
| 59 void DecryptingDemuxerStream::Reset(const base::Closure& closure) { | |
| 60 if (!message_loop_->BelongsToCurrentThread()) { | |
| 61 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 62 &DecryptingDemuxerStream::Reset, this, closure)); | |
| 63 return; | |
| 64 } | |
| 65 | |
| 66 DVLOG(2) << "Reset() - state: " << state_; | |
| 67 DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_; | |
| 68 DCHECK(init_cb_.is_null()); // No Reset() during pending initialization. | |
| 69 DCHECK(reset_cb_.is_null()); | |
| 70 | |
| 71 reset_cb_ = closure; | |
| 72 | |
| 73 decryptor_->CancelDecrypt(GetDecryptorStreamType()); | |
| 74 | |
| 75 // Reset() cannot complete if the read callback is still pending. | |
| 76 // Defer the resetting process in this case. The |reset_cb_| will be fired | |
| 77 // after the read callback is fired - see DoDecryptBuffer() and | |
| 78 // DoDeliverBuffer(). | |
| 79 if (state_ == kPendingDemuxerRead || state_ == kPendingDecrypt) { | |
| 80 DCHECK(!read_cb_.is_null()); | |
| 81 return; | |
| 82 } | |
| 83 | |
| 84 if (state_ == kWaitingForKey) { | |
| 85 DCHECK(!read_cb_.is_null()); | |
| 86 pending_buffer_to_decrypt_ = NULL; | |
| 87 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
| 88 } | |
| 89 | |
| 90 DCHECK(read_cb_.is_null()); | |
| 91 DoReset(); | |
| 92 } | |
| 93 | |
| 94 const AudioDecoderConfig& DecryptingDemuxerStream::audio_decoder_config() { | |
| 95 DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_; | |
| 96 CHECK_EQ(stream_type_, AUDIO); | |
| 97 return *audio_config_; | |
| 98 } | |
| 99 | |
| 100 const VideoDecoderConfig& DecryptingDemuxerStream::video_decoder_config() { | |
| 101 DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_; | |
| 102 CHECK_EQ(stream_type_, VIDEO); | |
| 103 return *video_config_; | |
| 104 } | |
| 105 | |
| 106 DemuxerStream::Type DecryptingDemuxerStream::type() { | |
| 107 DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_; | |
| 108 return stream_type_; | |
| 109 } | |
| 110 | |
| 111 void DecryptingDemuxerStream::EnableBitstreamConverter() {} | |
|
ddorwin
2012/11/13 01:08:49
Should this ever be called?
xhwang
2012/11/13 21:10:21
Good question. GpuVideoDecoder will call this if t
| |
| 112 | |
| 113 DecryptingDemuxerStream::~DecryptingDemuxerStream() {} | |
| 114 | |
| 115 void DecryptingDemuxerStream::DoInitialize( | |
| 116 const scoped_refptr<DemuxerStream>& stream, | |
| 117 const PipelineStatusCB& status_cb) { | |
| 118 DVLOG(2) << "DoInitialize()"; | |
| 119 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 120 DCHECK_EQ(state_, kUninitialized) << state_; | |
| 121 | |
| 122 // Only valid potentially encrypted audio or video stream is accepted. | |
| 123 if (!((stream->type() == AUDIO && | |
| 124 stream->audio_decoder_config().IsValidConfig() && | |
| 125 stream->audio_decoder_config().is_encrypted()) || | |
| 126 (stream->type() == VIDEO && | |
| 127 stream->video_decoder_config().IsValidConfig() && | |
| 128 stream->video_decoder_config().is_encrypted()))) { | |
| 129 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); | |
| 130 return; | |
| 131 } | |
| 132 | |
| 133 DCHECK(!demuxer_stream_); | |
| 134 demuxer_stream_ = stream; | |
| 135 stream_type_ = stream->type(); | |
| 136 init_cb_ = status_cb; | |
| 137 | |
| 138 state_ = kDecryptorRequested; | |
| 139 request_decryptor_notification_cb_.Run( | |
| 140 BIND_TO_LOOP(&DecryptingDemuxerStream::SetDecryptor)); | |
| 141 } | |
| 142 | |
| 143 void DecryptingDemuxerStream::SetDecryptor(Decryptor* decryptor) { | |
| 144 DVLOG(2) << "SetDecryptor()"; | |
| 145 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 146 DCHECK_EQ(state_, kDecryptorRequested) << state_; | |
| 147 DCHECK(!init_cb_.is_null()); | |
| 148 DCHECK(!request_decryptor_notification_cb_.is_null()); | |
| 149 | |
| 150 request_decryptor_notification_cb_.Reset(); | |
| 151 decryptor_ = decryptor; | |
| 152 | |
| 153 switch (stream_type_) { | |
| 154 case AUDIO: { | |
| 155 const AudioDecoderConfig& input_audio_config = | |
| 156 demuxer_stream_->audio_decoder_config(); | |
| 157 audio_config_.reset(new AudioDecoderConfig()); | |
| 158 audio_config_->Initialize(input_audio_config.codec(), | |
| 159 input_audio_config.bits_per_channel(), | |
| 160 input_audio_config.channel_layout(), | |
| 161 input_audio_config.samples_per_second(), | |
| 162 input_audio_config.extra_data(), | |
| 163 input_audio_config.extra_data_size(), | |
| 164 false, // Output audio is not encrypted. | |
| 165 false); | |
| 166 break; | |
| 167 } | |
| 168 | |
| 169 case VIDEO: { | |
| 170 const VideoDecoderConfig& input_video_config = | |
| 171 demuxer_stream_->video_decoder_config(); | |
| 172 video_config_.reset(new VideoDecoderConfig()); | |
| 173 video_config_->Initialize(input_video_config.codec(), | |
| 174 input_video_config.profile(), | |
| 175 input_video_config.format(), | |
| 176 input_video_config.coded_size(), | |
| 177 input_video_config.visible_rect(), | |
| 178 input_video_config.natural_size(), | |
| 179 input_video_config.extra_data(), | |
| 180 input_video_config.extra_data_size(), | |
| 181 false, // Output video is not encrypted. | |
| 182 false); | |
| 183 break; | |
| 184 } | |
| 185 | |
| 186 default: | |
| 187 NOTREACHED(); | |
| 188 return; | |
| 189 } | |
| 190 | |
| 191 state_ = kIdle; | |
| 192 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); | |
| 193 } | |
| 194 | |
| 195 void DecryptingDemuxerStream::DecryptBuffer( | |
| 196 DemuxerStream::Status status, | |
| 197 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 198 // In theory, we don't need to force post the task here, because we do a | |
| 199 // force task post in DeliverBuffer(). Therefore, even if | |
| 200 // demuxer_stream_->Read() execute the read callback on the same execution | |
| 201 // stack we are still fine. But it looks like a force post task makes the | |
| 202 // logic more understandable and manageable, so why not? | |
| 203 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 204 &DecryptingDemuxerStream::DoDecryptBuffer, this, status, buffer)); | |
| 205 } | |
| 206 | |
| 207 void DecryptingDemuxerStream::DoDecryptBuffer( | |
| 208 DemuxerStream::Status status, | |
| 209 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 210 DVLOG(3) << "DoDecryptBuffer()"; | |
| 211 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 212 DCHECK_EQ(state_, kPendingDemuxerRead) << state_; | |
| 213 DCHECK(!read_cb_.is_null()); | |
| 214 DCHECK_EQ(buffer != NULL, status == kOk) << status; | |
| 215 | |
| 216 if (!reset_cb_.is_null()) { | |
| 217 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
| 218 DoReset(); | |
| 219 return; | |
| 220 } | |
| 221 | |
| 222 if (status == kAborted) { | |
| 223 DVLOG(2) << "DoDecryptBuffer() - kAborted."; | |
| 224 state_ = kIdle; | |
| 225 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
| 226 return; | |
| 227 } | |
| 228 | |
| 229 if (status == kConfigChanged) { | |
| 230 DVLOG(2) << "DoDecryptBuffer() - kConfigChanged."; | |
| 231 state_ = kIdle; | |
| 232 // TODO(xhwang): Support kConfigChanged! | |
| 233 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
| 234 return; | |
| 235 } | |
| 236 | |
| 237 if (buffer->IsEndOfStream()) { | |
| 238 DVLOG(2) << "DoDecryptBuffer() - EOS buffer."; | |
| 239 state_ = kIdle; | |
| 240 base::ResetAndReturn(&read_cb_).Run(status, buffer); | |
| 241 return; | |
| 242 } | |
| 243 | |
| 244 pending_buffer_to_decrypt_ = buffer; | |
| 245 state_ = kPendingDecrypt; | |
| 246 DecryptPendingBuffer(); | |
| 247 } | |
| 248 | |
| 249 void DecryptingDemuxerStream::DecryptPendingBuffer() { | |
| 250 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 251 DCHECK_EQ(state_, kPendingDecrypt) << state_; | |
| 252 decryptor_->Decrypt( | |
| 253 GetDecryptorStreamType(), | |
| 254 pending_buffer_to_decrypt_, | |
| 255 base::Bind(&DecryptingDemuxerStream::DeliverBuffer, this)); | |
| 256 } | |
| 257 | |
| 258 void DecryptingDemuxerStream::DeliverBuffer( | |
| 259 Decryptor::Status status, | |
| 260 const scoped_refptr<DecoderBuffer>& decrypted_buffer) { | |
| 261 // We need to force task post here because the DecryptCB can be executed | |
| 262 // synchronously in Reset(). Instead of using more complicated logic in | |
| 263 // those function to fix it, why not force task post here to make everything | |
| 264 // simple and clear? | |
| 265 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 266 &DecryptingDemuxerStream::DoDeliverBuffer, this, | |
| 267 status, decrypted_buffer)); | |
| 268 } | |
| 269 | |
| 270 void DecryptingDemuxerStream::DoDeliverBuffer( | |
| 271 Decryptor::Status status, | |
| 272 const scoped_refptr<DecoderBuffer>& decrypted_buffer) { | |
| 273 DVLOG(3) << "DoDeliverBuffer() - status: " << status; | |
| 274 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 275 DCHECK_EQ(state_, kPendingDecrypt) << state_; | |
| 276 DCHECK_NE(status, Decryptor::kNeedMoreData); | |
| 277 DCHECK(!read_cb_.is_null()); | |
| 278 DCHECK(pending_buffer_to_decrypt_); | |
| 279 | |
| 280 bool need_to_try_again_if_nokey = key_added_while_pending_decrypt_; | |
| 281 key_added_while_pending_decrypt_ = false; | |
| 282 | |
| 283 if (!reset_cb_.is_null()) { | |
| 284 pending_buffer_to_decrypt_ = NULL; | |
| 285 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
| 286 DoReset(); | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 DCHECK_EQ(status == Decryptor::kSuccess, decrypted_buffer.get() != NULL); | |
| 291 | |
| 292 if (status == Decryptor::kError) { | |
| 293 DVLOG(2) << "DoDeliverBuffer() - kError"; | |
| 294 pending_buffer_to_decrypt_ = NULL; | |
| 295 state_ = kIdle; | |
| 296 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
| 297 return; | |
| 298 } | |
| 299 | |
| 300 if (status == Decryptor::kNoKey) { | |
| 301 DVLOG(2) << "DoDeliverBuffer() - kNoKey"; | |
| 302 if (need_to_try_again_if_nokey) { | |
| 303 // The |state_| is still kPendingDecrypt. | |
| 304 DecryptPendingBuffer(); | |
| 305 return; | |
| 306 } | |
| 307 | |
| 308 state_ = kWaitingForKey; | |
| 309 return; | |
| 310 } | |
| 311 | |
| 312 DCHECK_EQ(status, Decryptor::kSuccess); | |
| 313 pending_buffer_to_decrypt_ = NULL; | |
| 314 state_ = kIdle; | |
| 315 base::ResetAndReturn(&read_cb_).Run(kOk, decrypted_buffer); | |
| 316 } | |
| 317 | |
| 318 void DecryptingDemuxerStream::OnKeyAdded() { | |
| 319 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 320 | |
| 321 if (state_ == kPendingDecrypt) { | |
| 322 key_added_while_pending_decrypt_ = true; | |
| 323 return; | |
| 324 } | |
| 325 | |
| 326 if (state_ == kWaitingForKey) { | |
| 327 state_ = kPendingDecrypt; | |
| 328 DecryptPendingBuffer(); | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 void DecryptingDemuxerStream::DoReset() { | |
| 333 DCHECK(init_cb_.is_null()); | |
| 334 DCHECK(read_cb_.is_null()); | |
| 335 state_ = kIdle; | |
| 336 base::ResetAndReturn(&reset_cb_).Run(); | |
| 337 } | |
| 338 | |
| 339 Decryptor::StreamType DecryptingDemuxerStream::GetDecryptorStreamType() const { | |
| 340 DCHECK(stream_type_ == AUDIO || stream_type_ == VIDEO); | |
| 341 return stream_type_ == AUDIO ? Decryptor::kAudio : Decryptor::kVideo; | |
| 342 } | |
| 343 | |
| 344 } // namespace media | |
| OLD | NEW |