Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(725)

Side by Side Diff: media/filters/decrypting_demuxer_stream.cc

Issue 11342031: Add DecryptingDemuxerStream. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: needs to add RegisterKeyAddedCB() Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698