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

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: rebase 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_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_EQ(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() {
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
OLDNEW
« no previous file with comments | « media/filters/decrypting_demuxer_stream.h ('k') | media/filters/decrypting_demuxer_stream_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698