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_EQ(buffer != NULL, status == DemuxerStream::kOk) << status; |
| 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 |