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_decrypt_pending_(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_; | |
scherkus (not reviewing)
2012/11/14 22:22:30
EQ
xhwang
2012/11/15 00:10:00
Done.
| |
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() { | |
112 demuxer_stream_->EnableBitstreamConverter(); | |
113 } | |
114 | |
115 DecryptingDemuxerStream::~DecryptingDemuxerStream() {} | |
116 | |
117 void DecryptingDemuxerStream::DoInitialize( | |
118 const scoped_refptr<DemuxerStream>& stream, | |
119 const PipelineStatusCB& status_cb) { | |
120 DVLOG(2) << "DoInitialize()"; | |
121 DCHECK(message_loop_->BelongsToCurrentThread()); | |
122 DCHECK_EQ(state_, kUninitialized) << state_; | |
123 | |
124 // Only valid potentially encrypted audio or video stream is accepted. | |
125 if (!((stream->type() == AUDIO && | |
126 stream->audio_decoder_config().IsValidConfig() && | |
127 stream->audio_decoder_config().is_encrypted()) || | |
128 (stream->type() == VIDEO && | |
129 stream->video_decoder_config().IsValidConfig() && | |
130 stream->video_decoder_config().is_encrypted()))) { | |
131 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); | |
132 return; | |
133 } | |
134 | |
135 DCHECK(!demuxer_stream_); | |
136 demuxer_stream_ = stream; | |
137 stream_type_ = stream->type(); | |
138 init_cb_ = status_cb; | |
139 | |
140 state_ = kDecryptorRequested; | |
141 request_decryptor_notification_cb_.Run( | |
142 BIND_TO_LOOP(&DecryptingDemuxerStream::SetDecryptor)); | |
143 } | |
144 | |
145 void DecryptingDemuxerStream::SetDecryptor(Decryptor* decryptor) { | |
146 DVLOG(2) << "SetDecryptor()"; | |
147 DCHECK(message_loop_->BelongsToCurrentThread()); | |
148 DCHECK_EQ(state_, kDecryptorRequested) << state_; | |
149 DCHECK(!init_cb_.is_null()); | |
150 DCHECK(!request_decryptor_notification_cb_.is_null()); | |
151 | |
152 request_decryptor_notification_cb_.Reset(); | |
153 decryptor_ = decryptor; | |
154 | |
155 switch (stream_type_) { | |
156 case AUDIO: { | |
157 const AudioDecoderConfig& input_audio_config = | |
158 demuxer_stream_->audio_decoder_config(); | |
159 audio_config_.reset(new AudioDecoderConfig()); | |
160 audio_config_->Initialize(input_audio_config.codec(), | |
161 input_audio_config.bits_per_channel(), | |
162 input_audio_config.channel_layout(), | |
163 input_audio_config.samples_per_second(), | |
164 input_audio_config.extra_data(), | |
165 input_audio_config.extra_data_size(), | |
166 false, // Output audio is not encrypted. | |
167 false); | |
168 break; | |
169 } | |
170 | |
171 case VIDEO: { | |
172 const VideoDecoderConfig& input_video_config = | |
173 demuxer_stream_->video_decoder_config(); | |
174 video_config_.reset(new VideoDecoderConfig()); | |
175 video_config_->Initialize(input_video_config.codec(), | |
176 input_video_config.profile(), | |
177 input_video_config.format(), | |
178 input_video_config.coded_size(), | |
179 input_video_config.visible_rect(), | |
180 input_video_config.natural_size(), | |
181 input_video_config.extra_data(), | |
182 input_video_config.extra_data_size(), | |
183 false, // Output video is not encrypted. | |
184 false); | |
185 break; | |
186 } | |
187 | |
188 default: | |
189 NOTREACHED(); | |
190 return; | |
191 } | |
192 | |
193 decryptor_->RegisterKeyAddedCB( | |
194 GetDecryptorStreamType(), | |
195 BIND_TO_LOOP(&DecryptingDemuxerStream::OnKeyAdded)); | |
196 | |
197 state_ = kIdle; | |
198 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); | |
199 } | |
200 | |
201 void DecryptingDemuxerStream::DecryptBuffer( | |
202 DemuxerStream::Status status, | |
203 const scoped_refptr<DecoderBuffer>& buffer) { | |
204 // In theory, we don't need to force post the task here, because we do a | |
205 // force task post in DeliverBuffer(). Therefore, even if | |
206 // demuxer_stream_->Read() execute the read callback on the same execution | |
207 // stack we are still fine. But it looks like a force post task makes the | |
208 // logic more understandable and manageable, so why not? | |
209 message_loop_->PostTask(FROM_HERE, base::Bind( | |
210 &DecryptingDemuxerStream::DoDecryptBuffer, this, status, buffer)); | |
211 } | |
212 | |
213 void DecryptingDemuxerStream::DoDecryptBuffer( | |
214 DemuxerStream::Status status, | |
215 const scoped_refptr<DecoderBuffer>& buffer) { | |
216 DVLOG(3) << "DoDecryptBuffer()"; | |
217 DCHECK(message_loop_->BelongsToCurrentThread()); | |
218 DCHECK_EQ(state_, kPendingDemuxerRead) << state_; | |
219 DCHECK(!read_cb_.is_null()); | |
220 DCHECK_EQ(buffer != NULL, status == kOk) << status; | |
221 | |
222 if (!reset_cb_.is_null()) { | |
223 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
224 DoReset(); | |
225 return; | |
226 } | |
227 | |
228 if (status == kAborted) { | |
229 DVLOG(2) << "DoDecryptBuffer() - kAborted."; | |
230 state_ = kIdle; | |
231 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
232 return; | |
233 } | |
234 | |
235 if (status == kConfigChanged) { | |
236 DVLOG(2) << "DoDecryptBuffer() - kConfigChanged."; | |
237 state_ = kIdle; | |
238 // TODO(xhwang): Support kConfigChanged! | |
239 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
240 return; | |
241 } | |
242 | |
243 if (buffer->IsEndOfStream()) { | |
244 DVLOG(2) << "DoDecryptBuffer() - EOS buffer."; | |
245 state_ = kIdle; | |
246 base::ResetAndReturn(&read_cb_).Run(status, buffer); | |
247 return; | |
248 } | |
249 | |
250 pending_buffer_to_decrypt_ = buffer; | |
251 state_ = kPendingDecrypt; | |
252 DecryptPendingBuffer(); | |
253 } | |
254 | |
255 void DecryptingDemuxerStream::DecryptPendingBuffer() { | |
256 DCHECK(message_loop_->BelongsToCurrentThread()); | |
257 DCHECK_EQ(state_, kPendingDecrypt) << state_; | |
258 decryptor_->Decrypt( | |
259 GetDecryptorStreamType(), | |
260 pending_buffer_to_decrypt_, | |
261 base::Bind(&DecryptingDemuxerStream::DeliverBuffer, this)); | |
262 } | |
263 | |
264 void DecryptingDemuxerStream::DeliverBuffer( | |
265 Decryptor::Status status, | |
266 const scoped_refptr<DecoderBuffer>& decrypted_buffer) { | |
267 // We need to force task post here because the DecryptCB can be executed | |
268 // synchronously in Reset(). Instead of using more complicated logic in | |
269 // those function to fix it, why not force task post here to make everything | |
270 // simple and clear? | |
271 message_loop_->PostTask(FROM_HERE, base::Bind( | |
272 &DecryptingDemuxerStream::DoDeliverBuffer, this, | |
273 status, decrypted_buffer)); | |
274 } | |
275 | |
276 void DecryptingDemuxerStream::DoDeliverBuffer( | |
277 Decryptor::Status status, | |
278 const scoped_refptr<DecoderBuffer>& decrypted_buffer) { | |
279 DVLOG(3) << "DoDeliverBuffer() - status: " << status; | |
280 DCHECK(message_loop_->BelongsToCurrentThread()); | |
281 DCHECK_EQ(state_, kPendingDecrypt) << state_; | |
282 DCHECK_NE(status, Decryptor::kNeedMoreData); | |
283 DCHECK(!read_cb_.is_null()); | |
284 DCHECK(pending_buffer_to_decrypt_); | |
285 | |
286 bool need_to_try_again_if_nokey = key_added_while_decrypt_pending_; | |
287 key_added_while_decrypt_pending_ = false; | |
288 | |
289 if (!reset_cb_.is_null()) { | |
290 pending_buffer_to_decrypt_ = NULL; | |
291 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
292 DoReset(); | |
293 return; | |
294 } | |
295 | |
296 DCHECK_EQ(status == Decryptor::kSuccess, decrypted_buffer.get() != NULL); | |
297 | |
298 if (status == Decryptor::kError) { | |
299 DVLOG(2) << "DoDeliverBuffer() - kError"; | |
300 pending_buffer_to_decrypt_ = NULL; | |
301 state_ = kIdle; | |
302 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | |
303 return; | |
304 } | |
305 | |
306 if (status == Decryptor::kNoKey) { | |
307 DVLOG(2) << "DoDeliverBuffer() - kNoKey"; | |
308 if (need_to_try_again_if_nokey) { | |
309 // The |state_| is still kPendingDecrypt. | |
310 DecryptPendingBuffer(); | |
311 return; | |
312 } | |
313 | |
314 state_ = kWaitingForKey; | |
315 return; | |
316 } | |
317 | |
318 DCHECK_EQ(status, Decryptor::kSuccess); | |
319 pending_buffer_to_decrypt_ = NULL; | |
320 state_ = kIdle; | |
321 base::ResetAndReturn(&read_cb_).Run(kOk, decrypted_buffer); | |
322 } | |
323 | |
324 void DecryptingDemuxerStream::OnKeyAdded() { | |
325 DCHECK(message_loop_->BelongsToCurrentThread()); | |
326 | |
327 if (state_ == kPendingDecrypt) { | |
328 key_added_while_decrypt_pending_ = true; | |
329 return; | |
330 } | |
331 | |
332 if (state_ == kWaitingForKey) { | |
333 state_ = kPendingDecrypt; | |
334 DecryptPendingBuffer(); | |
335 } | |
336 } | |
337 | |
338 void DecryptingDemuxerStream::DoReset() { | |
339 DCHECK(init_cb_.is_null()); | |
340 DCHECK(read_cb_.is_null()); | |
341 state_ = kIdle; | |
342 base::ResetAndReturn(&reset_cb_).Run(); | |
343 } | |
344 | |
345 Decryptor::StreamType DecryptingDemuxerStream::GetDecryptorStreamType() const { | |
346 DCHECK(stream_type_ == AUDIO || stream_type_ == VIDEO); | |
347 return stream_type_ == AUDIO ? Decryptor::kAudio : Decryptor::kVideo; | |
348 } | |
349 | |
350 } // namespace media | |
OLD | NEW |