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(status == DemuxerStream::kOk ? buffer : !buffer) << status; | |
scherkus (not reviewing)
2012/10/04 00:15:13
this is even more confusing
I was suggesting
DCHE
xhwang
2012/10/04 16:32:54
Done.
| |
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 |