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 message_loop_(NULL), | |
| 25 state_(kUninitialized), | |
| 26 decryptor_(decryptor) { | |
| 27 } | |
| 28 | |
| 29 void DecryptingVideoDecoder::Initialize( | |
| 30 const scoped_refptr<DemuxerStream>& stream, | |
| 31 const PipelineStatusCB& status_cb, | |
| 32 const StatisticsCB& statistics_cb) { | |
| 33 if (!message_loop_) { | |
|
ddorwin
2012/09/28 17:36:40
Initialize can be called multiple times? Or is thi
xhwang
2012/09/30 19:58:51
This is the same code we have in FFVD. But it's a
| |
| 34 message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run(); | |
| 35 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 36 &DecryptingVideoDecoder::Initialize, this, | |
| 37 stream, status_cb, statistics_cb)); | |
| 38 return; | |
| 39 } | |
| 40 | |
| 41 DCHECK(!demuxer_stream_); | |
| 42 DCHECK(stream); | |
| 43 demuxer_stream_ = stream; | |
|
ddorwin
2012/09/28 17:36:40
Can this class be reused after one of the followin
xhwang
2012/09/30 19:58:51
Done.
| |
| 44 | |
| 45 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config(); | |
| 46 if (!config.IsValidConfig()) { | |
| 47 DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); | |
| 48 status_cb.Run(PIPELINE_ERROR_DECODE); | |
| 49 return; | |
| 50 } | |
| 51 // DecryptingVideoDecoder only accepts potentially encrypted stream. | |
|
ddorwin
2012/09/28 17:36:40
Newline
xhwang
2012/09/30 19:58:51
Done.
| |
| 52 if (!config.is_encrypted()) { | |
| 53 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | |
| 54 return; | |
| 55 } | |
| 56 | |
| 57 status_cb_ = status_cb; | |
| 58 statistics_cb_ = statistics_cb; | |
| 59 | |
| 60 DCHECK(decryptor_); | |
| 61 decryptor_->InitializeVideoDecoder( | |
| 62 config, | |
| 63 base::Bind(&DecryptingVideoDecoder::OnDecoderInitialized, this)); | |
| 64 } | |
| 65 | |
| 66 void DecryptingVideoDecoder::Read(const ReadCB& read_cb) { | |
| 67 // Complete operation asynchronously on different stack of execution as per | |
| 68 // the API contract of VideoDecoder::Read() | |
| 69 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 70 &DecryptingVideoDecoder::DoRead, this, read_cb)); | |
| 71 } | |
| 72 | |
| 73 void DecryptingVideoDecoder::Reset(const base::Closure& closure) { | |
| 74 if (!message_loop_->BelongsToCurrentThread()) { | |
| 75 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 76 &DecryptingVideoDecoder::Reset, this, closure)); | |
| 77 return; | |
| 78 } | |
| 79 | |
| 80 DCHECK(reset_cb_.is_null()); | |
| 81 reset_cb_ = closure; | |
| 82 | |
| 83 decryptor_->CancelDecryptAndDecodeVideo(); | |
| 84 | |
| 85 // Defer the reset if a read is pending. | |
|
ddorwin
2012/09/28 17:36:40
Why? When will it be called then?
xhwang
2012/09/30 19:58:51
We can't finish the reset process if we have pendi
| |
| 86 if (!read_cb_.is_null()) | |
| 87 return; | |
| 88 | |
| 89 DoReset(); | |
| 90 } | |
| 91 | |
| 92 void DecryptingVideoDecoder::Stop(const base::Closure& closure) { | |
| 93 if (!message_loop_->BelongsToCurrentThread()) { | |
| 94 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 95 &DecryptingVideoDecoder::Stop, this, closure)); | |
| 96 return; | |
| 97 } | |
| 98 | |
| 99 DCHECK(stop_cb_.is_null()); | |
| 100 stop_cb_ = closure; | |
| 101 | |
| 102 decryptor_->StopVideoDecoder(); | |
| 103 | |
| 104 // Defer stopping if a read is pending. | |
|
ddorwin
2012/09/28 17:36:40
Same.
xhwang
2012/09/30 19:58:51
See above answer.
| |
| 105 if (!read_cb_.is_null()) | |
| 106 return; | |
| 107 | |
| 108 DoStop(); | |
| 109 } | |
| 110 | |
| 111 DecryptingVideoDecoder::~DecryptingVideoDecoder() { | |
|
ddorwin
2012/09/28 17:36:40
Do we need this? Presumably the parent is already
xhwang
2012/09/30 19:58:51
Correct. I added a DCHECK in the dtor so now we ne
| |
| 112 } | |
| 113 | |
| 114 void DecryptingVideoDecoder::OnDecoderInitialized(bool success) { | |
| 115 if (!message_loop_->BelongsToCurrentThread()) { | |
| 116 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 117 &DecryptingVideoDecoder::OnDecoderInitialized, this, success)); | |
| 118 return; | |
| 119 } | |
| 120 | |
| 121 DCHECK(!status_cb_.is_null()); | |
| 122 if (!success) { | |
|
ddorwin
2012/09/28 17:36:40
nit: newline since the above DCHECK is applicable
xhwang
2012/09/30 19:58:51
Done.
| |
| 123 base::ResetAndReturn(&status_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); | |
| 124 return; | |
| 125 } | |
| 126 | |
| 127 // Success! | |
| 128 state_ = kNormal; | |
| 129 base::ResetAndReturn(&status_cb_).Run(PIPELINE_OK); | |
| 130 } | |
| 131 | |
| 132 void DecryptingVideoDecoder::DoRead(const ReadCB& read_cb) { | |
| 133 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 134 DCHECK(!read_cb.is_null()); | |
| 135 CHECK_NE(state_, kUninitialized); | |
| 136 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
| 137 | |
| 138 // Return empty frames if decoding has finished. | |
| 139 if (state_ == kDecodeFinished) { | |
| 140 read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); | |
| 141 return; | |
| 142 } | |
| 143 | |
| 144 read_cb_ = read_cb; | |
| 145 ReadFromDemuxerStream(); | |
| 146 } | |
| 147 | |
| 148 void DecryptingVideoDecoder::ReadFromDemuxerStream() { | |
| 149 DCHECK_NE(state_, kUninitialized); | |
| 150 DCHECK_NE(state_, kDecodeFinished); | |
| 151 DCHECK(!read_cb_.is_null()); | |
| 152 | |
| 153 demuxer_stream_->Read( | |
| 154 base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, this)); | |
| 155 } | |
| 156 | |
| 157 void DecryptingVideoDecoder::DecryptAndDecodeBuffer( | |
| 158 DemuxerStream::Status status, | |
| 159 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 160 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | |
| 161 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read | |
| 162 // callback on the same execution stack so we can get rid of forced task post. | |
| 163 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 164 &DecryptingVideoDecoder::DoDecryptAndDecodeBuffer, this, status, buffer)); | |
| 165 } | |
| 166 | |
| 167 void DecryptingVideoDecoder::DoDecryptAndDecodeBuffer( | |
| 168 DemuxerStream::Status status, | |
| 169 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 170 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 171 DCHECK_NE(state_, kUninitialized); | |
|
ddorwin
2012/09/28 17:36:40
These two can be replaced with EQ kNormal.
xhwang
2012/09/30 19:58:51
Done.
| |
| 172 DCHECK_NE(state_, kDecodeFinished); | |
| 173 DCHECK(!read_cb_.is_null()); | |
| 174 | |
| 175 if (!stop_cb_.is_null()) { | |
| 176 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
|
ddorwin
2012/09/28 17:36:40
Not related to this CL, but odd that we use the da
xhwang
2012/09/30 19:58:51
I agree. +scherkus/fischman: How about we add kAbo
| |
| 177 DoStop(); | |
| 178 return; | |
| 179 } | |
| 180 | |
| 181 if (!reset_cb_.is_null()) { | |
| 182 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 183 DoReset(); | |
| 184 return; | |
| 185 } | |
| 186 | |
| 187 if (status == DemuxerStream::kAborted) { | |
| 188 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 189 return; | |
| 190 } | |
| 191 | |
| 192 if (status == DemuxerStream::kConfigChanged) { | |
| 193 // TODO(xhwang): Add config change support. | |
| 194 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
| 195 return; | |
| 196 } | |
| 197 | |
| 198 DCHECK_EQ(DemuxerStream::kOk, status); | |
|
ddorwin
2012/09/28 17:36:40
nit: Did we decide the other order for non-unittes
xhwang
2012/09/30 19:58:51
Yeah, as summarized by scherkus in another thread:
| |
| 199 DCHECK(buffer); | |
| 200 | |
| 201 decryptor_->DecryptAndDecodeVideo( | |
| 202 buffer, base::Bind(&DecryptingVideoDecoder::DeliverFrame, this, | |
| 203 buffer->GetDataSize())); | |
| 204 } | |
| 205 | |
| 206 void DecryptingVideoDecoder::DeliverFrame( | |
| 207 int buffer_size, | |
| 208 Decryptor::Status status, | |
| 209 const scoped_refptr<VideoFrame>& frame) { | |
| 210 // We need to force task post here because the VideoDecodeCB can be executed | |
| 211 // synchronously or asynchronously by the decryptor. | |
|
ddorwin
2012/09/28 17:36:40
Might be worth explaining why or pointing to an ex
xhwang
2012/09/30 19:58:51
Done.
| |
| 212 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 213 &DecryptingVideoDecoder::DoDeliverFrame, this, | |
| 214 buffer_size, status, frame)); | |
| 215 } | |
| 216 | |
| 217 void DecryptingVideoDecoder::DoDeliverFrame( | |
| 218 int buffer_size, | |
| 219 Decryptor::Status status, | |
| 220 const scoped_refptr<VideoFrame>& frame) { | |
| 221 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 222 | |
| 223 DCHECK_NE(state_, kUninitialized); | |
| 224 DCHECK_NE(state_, kDecodeFinished); | |
| 225 DCHECK(!read_cb_.is_null()); | |
| 226 | |
| 227 if (!stop_cb_.is_null()) { | |
| 228 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 229 DoStop(); | |
| 230 return; | |
| 231 } | |
| 232 | |
| 233 if (!reset_cb_.is_null()) { | |
| 234 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 235 DoReset(); | |
| 236 return; | |
| 237 } | |
| 238 | |
| 239 if (status == Decryptor::kNoKey || status == Decryptor::kError) { | |
| 240 DCHECK(!frame); | |
| 241 state_ = kDecodeFinished; | |
| 242 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
|
ddorwin
2012/09/28 17:36:40
Why is kNoKey a decode error? Where is the wait an
xhwang
2012/09/30 19:58:51
The wait and retry logic is in ProxyDecryptor so i
ddorwin
2012/10/01 18:43:20
But wouldn't this class always wrap the real Decry
| |
| 243 return; | |
| 244 } | |
| 245 | |
| 246 // The buffer has been accepted by the decoder, let's report statistics. | |
| 247 if (buffer_size) { | |
| 248 PipelineStatistics statistics; | |
| 249 statistics.video_bytes_decoded = buffer_size; | |
| 250 statistics_cb_.Run(statistics); | |
| 251 } | |
| 252 | |
| 253 if (status == Decryptor::kNeedMoreData) { | |
| 254 DCHECK(!frame); | |
| 255 ReadFromDemuxerStream(); | |
| 256 return; | |
| 257 } | |
| 258 | |
| 259 DCHECK_EQ(Decryptor::kSuccess, status); | |
|
ddorwin
2012/09/28 17:36:40
same
xhwang
2012/09/30 19:58:51
Done.
| |
| 260 DCHECK(frame); | |
| 261 if (frame->IsEndOfStream()) | |
| 262 state_ = kDecodeFinished; | |
| 263 | |
| 264 base::ResetAndReturn(&read_cb_).Run(kOk, frame); | |
| 265 } | |
| 266 | |
| 267 void DecryptingVideoDecoder::DoReset() { | |
| 268 DCHECK(read_cb_.is_null()); | |
| 269 state_ = kNormal; | |
|
ddorwin
2012/09/28 17:36:40
DCHECK(state_ != kUninitialized).
xhwang
2012/09/30 19:58:51
Done.
| |
| 270 base::ResetAndReturn(&reset_cb_).Run(); | |
| 271 } | |
| 272 | |
| 273 void DecryptingVideoDecoder::DoStop() { | |
| 274 DCHECK(read_cb_.is_null()); | |
| 275 state_ = kUninitialized; | |
| 276 base::ResetAndReturn(&stop_cb_).Run(); | |
| 277 } | |
| 278 | |
| 279 } // namespace media | |
| OLD | NEW |