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 DCHECK(!message_loop_); | |
34 message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run(); | |
35 message_loop_->PostTask(FROM_HERE, base::Bind( | |
36 &DecryptingVideoDecoder::DoInitialize, this, | |
37 stream, status_cb, statistics_cb)); | |
38 } | |
39 | |
40 void DecryptingVideoDecoder::Read(const ReadCB& read_cb) { | |
41 // Complete operation asynchronously on different stack of execution as per | |
42 // the API contract of VideoDecoder::Read() | |
43 message_loop_->PostTask(FROM_HERE, base::Bind( | |
44 &DecryptingVideoDecoder::DoRead, this, read_cb)); | |
45 } | |
46 | |
47 void DecryptingVideoDecoder::Reset(const base::Closure& closure) { | |
48 if (!message_loop_->BelongsToCurrentThread()) { | |
49 message_loop_->PostTask(FROM_HERE, base::Bind( | |
50 &DecryptingVideoDecoder::Reset, this, closure)); | |
51 return; | |
52 } | |
53 | |
54 DCHECK_NE(state_, kUninitialized); | |
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 // Defer the reset if a read is pending. | |
ddorwin
2012/10/01 18:43:20
Reply to conversation in other patch: Okay. Might
xhwang
2012/10/01 20:09:26
Done.
| |
62 if (!read_cb_.is_null()) | |
63 return; | |
64 | |
65 DoReset(); | |
66 } | |
67 | |
68 void DecryptingVideoDecoder::Stop(const base::Closure& closure) { | |
69 if (!message_loop_->BelongsToCurrentThread()) { | |
70 message_loop_->PostTask(FROM_HERE, base::Bind( | |
71 &DecryptingVideoDecoder::Stop, this, closure)); | |
72 return; | |
73 } | |
74 | |
75 DCHECK(stop_cb_.is_null()); | |
76 stop_cb_ = closure; | |
77 | |
78 decryptor_->StopVideoDecoder(); | |
79 | |
80 // Defer stopping if a read is pending. | |
ddorwin
2012/10/01 18:43:20
Same
xhwang
2012/10/01 20:09:26
Done.
| |
81 if (!read_cb_.is_null()) | |
82 return; | |
83 | |
84 DoStop(); | |
85 } | |
86 | |
87 DecryptingVideoDecoder::~DecryptingVideoDecoder() { | |
88 DCHECK_EQ(state_, kUninitialized); | |
89 } | |
90 | |
91 void DecryptingVideoDecoder::DoInitialize( | |
92 const scoped_refptr<DemuxerStream>& stream, | |
93 const PipelineStatusCB& status_cb, | |
94 const StatisticsCB& statistics_cb) { | |
95 DCHECK(stream); | |
ddorwin
2012/10/01 18:43:20
DCHECK thread.
xhwang
2012/10/01 20:09:26
Done.
| |
96 const VideoDecoderConfig& config = stream->video_decoder_config(); | |
97 if (!config.IsValidConfig()) { | |
98 DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); | |
ddorwin
2012/10/01 18:43:20
...stream config?
xhwang
2012/10/01 20:09:26
Done.
xhwang
2012/10/01 20:09:26
Done.
| |
99 status_cb.Run(PIPELINE_ERROR_DECODE); | |
100 return; | |
101 } | |
102 | |
103 // DecryptingVideoDecoder only accepts potentially encrypted stream. | |
104 if (!config.is_encrypted()) { | |
105 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | |
106 return; | |
107 } | |
108 | |
109 DCHECK(!demuxer_stream_); | |
110 demuxer_stream_ = stream; | |
111 statistics_cb_ = statistics_cb; | |
112 | |
113 DCHECK(decryptor_); | |
114 decryptor_->InitializeVideoDecoder( | |
115 config, | |
116 base::Bind(&DecryptingVideoDecoder::OnDecoderInitialized, this, | |
117 status_cb)); | |
118 } | |
119 | |
120 void DecryptingVideoDecoder::OnDecoderInitialized( | |
121 const PipelineStatusCB& status_cb, | |
ddorwin
2012/10/01 18:43:20
nit: Is there a reason the result comes AFTER the
xhwang
2012/10/01 20:09:26
This is because we need to bind the status_cb in t
| |
122 bool success) { | |
123 if (!message_loop_->BelongsToCurrentThread()) { | |
124 message_loop_->PostTask(FROM_HERE, base::Bind( | |
125 &DecryptingVideoDecoder::OnDecoderInitialized, this, | |
126 status_cb, success)); | |
127 return; | |
128 } | |
129 | |
130 DCHECK(!status_cb.is_null()); | |
131 | |
132 if (!success) { | |
133 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | |
134 return; | |
135 } | |
136 | |
137 // Success! | |
138 state_ = kNormal; | |
139 status_cb.Run(PIPELINE_OK); | |
140 } | |
141 | |
142 void DecryptingVideoDecoder::DoRead(const ReadCB& read_cb) { | |
143 DCHECK(message_loop_->BelongsToCurrentThread()); | |
144 DCHECK(!read_cb.is_null()); | |
145 CHECK_NE(state_, kUninitialized); | |
146 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
147 | |
148 // Return empty frames if decoding has finished. | |
149 if (state_ == kDecodeFinished) { | |
150 read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); | |
151 return; | |
152 } | |
153 | |
154 read_cb_ = read_cb; | |
155 ReadFromDemuxerStream(); | |
156 } | |
157 | |
158 void DecryptingVideoDecoder::ReadFromDemuxerStream() { | |
159 DCHECK(message_loop_->BelongsToCurrentThread()); | |
160 DCHECK_EQ(state_, kNormal); | |
161 DCHECK(!read_cb_.is_null()); | |
162 | |
163 demuxer_stream_->Read( | |
164 base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, this)); | |
165 } | |
166 | |
167 void DecryptingVideoDecoder::DecryptAndDecodeBuffer( | |
168 DemuxerStream::Status status, | |
169 const scoped_refptr<DecoderBuffer>& buffer) { | |
170 // In theory, we don't need to force post the task here, because we do a | |
ddorwin
2012/10/01 18:43:20
Isn't the old comment referring to the fact that t
xhwang
2012/10/01 20:09:26
That's a long story :)
The original comment is co
| |
171 // force task post in DeliverFrame(). Therefore, even if | |
172 // demuxer_stream_->Read() execute the read callback on the same execution | |
173 // stack we are still fine. But it looks like a force post task makes the | |
174 // logic more understandable and manageable, so why not? | |
ddorwin
2012/10/01 18:43:20
I would rephrase the "why not?" questions here and
xhwang
2012/10/01 20:09:26
Agreed. The right explanation is too long (see abo
| |
175 message_loop_->PostTask(FROM_HERE, base::Bind( | |
176 &DecryptingVideoDecoder::DoDecryptAndDecodeBuffer, this, | |
177 status, buffer)); | |
178 } | |
179 | |
180 void DecryptingVideoDecoder::DoDecryptAndDecodeBuffer( | |
181 DemuxerStream::Status status, | |
182 const scoped_refptr<DecoderBuffer>& buffer) { | |
183 DCHECK(message_loop_->BelongsToCurrentThread()); | |
184 DCHECK_EQ(state_, kNormal); | |
185 DCHECK(!read_cb_.is_null()); | |
186 DCHECK_EQ(!!buffer, status == DemuxerStream::kOk) << status; | |
187 | |
188 if (!stop_cb_.is_null()) { | |
189 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
190 if (!reset_cb_.is_null()) | |
191 DoReset(); | |
xhwang
2012/09/30 19:58:51
This is to handle the case where Stop() is called
ddorwin
2012/10/01 18:43:20
Does it make sense to call both callbacks? Do call
xhwang
2012/10/01 20:09:26
Yeah, I also prefer to updating the API to prevent
| |
192 | |
193 DoStop(); | |
194 return; | |
195 } | |
196 | |
197 if (!reset_cb_.is_null()) { | |
198 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
199 DoReset(); | |
200 return; | |
201 } | |
202 | |
203 if (status == DemuxerStream::kAborted) { | |
204 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
205 return; | |
206 } | |
207 | |
208 if (status == DemuxerStream::kConfigChanged) { | |
209 // TODO(xhwang): Add config change support. | |
210 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
211 return; | |
212 } | |
213 | |
214 DCHECK_EQ(status, DemuxerStream::kOk); | |
215 DCHECK(buffer); | |
216 | |
217 decryptor_->DecryptAndDecodeVideo( | |
218 buffer, base::Bind(&DecryptingVideoDecoder::DeliverFrame, this, | |
219 buffer->GetDataSize())); | |
220 } | |
221 | |
222 void DecryptingVideoDecoder::DeliverFrame( | |
223 int buffer_size, | |
224 Decryptor::Status status, | |
225 const scoped_refptr<VideoFrame>& frame) { | |
226 // We need to force task post here because the VideoDecodeCB can be executed | |
227 // synchronously in Reset()/Stop(). Instead of using more complicated logic in | |
ddorwin
2012/10/01 18:43:20
Oh, I didn't realize it was just for Reset and Sto
xhwang
2012/10/01 20:09:26
That's a valid point: paying a cost in the normal
| |
228 // those function to fix it, why not force task post here to make everything | |
229 // simple and clear? | |
230 message_loop_->PostTask(FROM_HERE, base::Bind( | |
231 &DecryptingVideoDecoder::DoDeliverFrame, this, | |
232 buffer_size, status, frame)); | |
233 } | |
234 | |
235 void DecryptingVideoDecoder::DoDeliverFrame( | |
236 int buffer_size, | |
237 Decryptor::Status status, | |
238 const scoped_refptr<VideoFrame>& frame) { | |
239 DCHECK(message_loop_->BelongsToCurrentThread()); | |
240 DCHECK_EQ(state_, kNormal); | |
241 DCHECK(!read_cb_.is_null()); | |
242 | |
243 if (!stop_cb_.is_null()) { | |
244 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
245 if (!reset_cb_.is_null()) | |
246 DoReset(); | |
247 | |
248 DoStop(); | |
249 return; | |
250 } | |
251 | |
252 if (!reset_cb_.is_null()) { | |
253 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
254 DoReset(); | |
255 return; | |
256 } | |
257 | |
258 if (status == Decryptor::kNoKey || status == Decryptor::kError) { | |
259 DCHECK(!frame); | |
260 state_ = kDecodeFinished; | |
261 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
262 return; | |
263 } | |
264 | |
265 // The buffer has been accepted by the decoder, let's report statistics. | |
266 if (buffer_size) { | |
267 PipelineStatistics statistics; | |
268 statistics.video_bytes_decoded = buffer_size; | |
269 statistics_cb_.Run(statistics); | |
270 } | |
271 | |
272 if (status == Decryptor::kNeedMoreData) { | |
273 DCHECK(!frame); | |
274 ReadFromDemuxerStream(); | |
275 return; | |
276 } | |
277 | |
278 DCHECK_EQ(status, Decryptor::kSuccess); | |
279 DCHECK(frame); | |
280 if (frame->IsEndOfStream()) | |
281 state_ = kDecodeFinished; | |
282 | |
283 base::ResetAndReturn(&read_cb_).Run(kOk, frame); | |
284 } | |
285 | |
286 void DecryptingVideoDecoder::DoReset() { | |
287 DCHECK(read_cb_.is_null()); | |
288 DCHECK_NE(state_, kUninitialized); | |
289 state_ = kNormal; | |
290 base::ResetAndReturn(&reset_cb_).Run(); | |
291 } | |
292 | |
293 void DecryptingVideoDecoder::DoStop() { | |
294 DCHECK(read_cb_.is_null()); | |
295 state_ = kUninitialized; | |
296 base::ResetAndReturn(&stop_cb_).Run(); | |
297 } | |
298 | |
299 } // namespace media | |
OLD | NEW |