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 if (!message_loop_) { | |
ddorwin
2012/09/28 17:36:40
Initialize can be called multiple times? Or is thi
xhwang
2012/09/30 19:58:51
This is the same code we have in FFVD. But it's a
| |
34 message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run(); | |
35 message_loop_->PostTask(FROM_HERE, base::Bind( | |
36 &DecryptingVideoDecoder::Initialize, this, | |
37 stream, status_cb, statistics_cb)); | |
38 return; | |
39 } | |
40 | |
41 DCHECK(!demuxer_stream_); | |
42 DCHECK(stream); | |
43 demuxer_stream_ = stream; | |
ddorwin
2012/09/28 17:36:40
Can this class be reused after one of the followin
xhwang
2012/09/30 19:58:51
Done.
| |
44 | |
45 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config(); | |
46 if (!config.IsValidConfig()) { | |
47 DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); | |
48 status_cb.Run(PIPELINE_ERROR_DECODE); | |
49 return; | |
50 } | |
51 // DecryptingVideoDecoder only accepts potentially encrypted stream. | |
ddorwin
2012/09/28 17:36:40
Newline
xhwang
2012/09/30 19:58:51
Done.
| |
52 if (!config.is_encrypted()) { | |
53 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | |
54 return; | |
55 } | |
56 | |
57 status_cb_ = status_cb; | |
58 statistics_cb_ = statistics_cb; | |
59 | |
60 DCHECK(decryptor_); | |
61 decryptor_->InitializeVideoDecoder( | |
62 config, | |
63 base::Bind(&DecryptingVideoDecoder::OnDecoderInitialized, this)); | |
64 } | |
65 | |
66 void DecryptingVideoDecoder::Read(const ReadCB& read_cb) { | |
67 // Complete operation asynchronously on different stack of execution as per | |
68 // the API contract of VideoDecoder::Read() | |
69 message_loop_->PostTask(FROM_HERE, base::Bind( | |
70 &DecryptingVideoDecoder::DoRead, this, read_cb)); | |
71 } | |
72 | |
73 void DecryptingVideoDecoder::Reset(const base::Closure& closure) { | |
74 if (!message_loop_->BelongsToCurrentThread()) { | |
75 message_loop_->PostTask(FROM_HERE, base::Bind( | |
76 &DecryptingVideoDecoder::Reset, this, closure)); | |
77 return; | |
78 } | |
79 | |
80 DCHECK(reset_cb_.is_null()); | |
81 reset_cb_ = closure; | |
82 | |
83 decryptor_->CancelDecryptAndDecodeVideo(); | |
84 | |
85 // Defer the reset if a read is pending. | |
ddorwin
2012/09/28 17:36:40
Why? When will it be called then?
xhwang
2012/09/30 19:58:51
We can't finish the reset process if we have pendi
| |
86 if (!read_cb_.is_null()) | |
87 return; | |
88 | |
89 DoReset(); | |
90 } | |
91 | |
92 void DecryptingVideoDecoder::Stop(const base::Closure& closure) { | |
93 if (!message_loop_->BelongsToCurrentThread()) { | |
94 message_loop_->PostTask(FROM_HERE, base::Bind( | |
95 &DecryptingVideoDecoder::Stop, this, closure)); | |
96 return; | |
97 } | |
98 | |
99 DCHECK(stop_cb_.is_null()); | |
100 stop_cb_ = closure; | |
101 | |
102 decryptor_->StopVideoDecoder(); | |
103 | |
104 // Defer stopping if a read is pending. | |
ddorwin
2012/09/28 17:36:40
Same.
xhwang
2012/09/30 19:58:51
See above answer.
| |
105 if (!read_cb_.is_null()) | |
106 return; | |
107 | |
108 DoStop(); | |
109 } | |
110 | |
111 DecryptingVideoDecoder::~DecryptingVideoDecoder() { | |
ddorwin
2012/09/28 17:36:40
Do we need this? Presumably the parent is already
xhwang
2012/09/30 19:58:51
Correct. I added a DCHECK in the dtor so now we ne
| |
112 } | |
113 | |
114 void DecryptingVideoDecoder::OnDecoderInitialized(bool success) { | |
115 if (!message_loop_->BelongsToCurrentThread()) { | |
116 message_loop_->PostTask(FROM_HERE, base::Bind( | |
117 &DecryptingVideoDecoder::OnDecoderInitialized, this, success)); | |
118 return; | |
119 } | |
120 | |
121 DCHECK(!status_cb_.is_null()); | |
122 if (!success) { | |
ddorwin
2012/09/28 17:36:40
nit: newline since the above DCHECK is applicable
xhwang
2012/09/30 19:58:51
Done.
| |
123 base::ResetAndReturn(&status_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); | |
124 return; | |
125 } | |
126 | |
127 // Success! | |
128 state_ = kNormal; | |
129 base::ResetAndReturn(&status_cb_).Run(PIPELINE_OK); | |
130 } | |
131 | |
132 void DecryptingVideoDecoder::DoRead(const ReadCB& read_cb) { | |
133 DCHECK(message_loop_->BelongsToCurrentThread()); | |
134 DCHECK(!read_cb.is_null()); | |
135 CHECK_NE(state_, kUninitialized); | |
136 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
137 | |
138 // Return empty frames if decoding has finished. | |
139 if (state_ == kDecodeFinished) { | |
140 read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); | |
141 return; | |
142 } | |
143 | |
144 read_cb_ = read_cb; | |
145 ReadFromDemuxerStream(); | |
146 } | |
147 | |
148 void DecryptingVideoDecoder::ReadFromDemuxerStream() { | |
149 DCHECK_NE(state_, kUninitialized); | |
150 DCHECK_NE(state_, kDecodeFinished); | |
151 DCHECK(!read_cb_.is_null()); | |
152 | |
153 demuxer_stream_->Read( | |
154 base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, this)); | |
155 } | |
156 | |
157 void DecryptingVideoDecoder::DecryptAndDecodeBuffer( | |
158 DemuxerStream::Status status, | |
159 const scoped_refptr<DecoderBuffer>& buffer) { | |
160 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | |
161 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read | |
162 // callback on the same execution stack so we can get rid of forced task post. | |
163 message_loop_->PostTask(FROM_HERE, base::Bind( | |
164 &DecryptingVideoDecoder::DoDecryptAndDecodeBuffer, this, status, buffer)); | |
165 } | |
166 | |
167 void DecryptingVideoDecoder::DoDecryptAndDecodeBuffer( | |
168 DemuxerStream::Status status, | |
169 const scoped_refptr<DecoderBuffer>& buffer) { | |
170 DCHECK(message_loop_->BelongsToCurrentThread()); | |
171 DCHECK_NE(state_, kUninitialized); | |
ddorwin
2012/09/28 17:36:40
These two can be replaced with EQ kNormal.
xhwang
2012/09/30 19:58:51
Done.
| |
172 DCHECK_NE(state_, kDecodeFinished); | |
173 DCHECK(!read_cb_.is_null()); | |
174 | |
175 if (!stop_cb_.is_null()) { | |
176 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
ddorwin
2012/09/28 17:36:40
Not related to this CL, but odd that we use the da
xhwang
2012/09/30 19:58:51
I agree. +scherkus/fischman: How about we add kAbo
| |
177 DoStop(); | |
178 return; | |
179 } | |
180 | |
181 if (!reset_cb_.is_null()) { | |
182 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
183 DoReset(); | |
184 return; | |
185 } | |
186 | |
187 if (status == DemuxerStream::kAborted) { | |
188 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
189 return; | |
190 } | |
191 | |
192 if (status == DemuxerStream::kConfigChanged) { | |
193 // TODO(xhwang): Add config change support. | |
194 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
195 return; | |
196 } | |
197 | |
198 DCHECK_EQ(DemuxerStream::kOk, status); | |
ddorwin
2012/09/28 17:36:40
nit: Did we decide the other order for non-unittes
xhwang
2012/09/30 19:58:51
Yeah, as summarized by scherkus in another thread:
| |
199 DCHECK(buffer); | |
200 | |
201 decryptor_->DecryptAndDecodeVideo( | |
202 buffer, base::Bind(&DecryptingVideoDecoder::DeliverFrame, this, | |
203 buffer->GetDataSize())); | |
204 } | |
205 | |
206 void DecryptingVideoDecoder::DeliverFrame( | |
207 int buffer_size, | |
208 Decryptor::Status status, | |
209 const scoped_refptr<VideoFrame>& frame) { | |
210 // We need to force task post here because the VideoDecodeCB can be executed | |
211 // synchronously or asynchronously by the decryptor. | |
ddorwin
2012/09/28 17:36:40
Might be worth explaining why or pointing to an ex
xhwang
2012/09/30 19:58:51
Done.
| |
212 message_loop_->PostTask(FROM_HERE, base::Bind( | |
213 &DecryptingVideoDecoder::DoDeliverFrame, this, | |
214 buffer_size, status, frame)); | |
215 } | |
216 | |
217 void DecryptingVideoDecoder::DoDeliverFrame( | |
218 int buffer_size, | |
219 Decryptor::Status status, | |
220 const scoped_refptr<VideoFrame>& frame) { | |
221 DCHECK(message_loop_->BelongsToCurrentThread()); | |
222 | |
223 DCHECK_NE(state_, kUninitialized); | |
224 DCHECK_NE(state_, kDecodeFinished); | |
225 DCHECK(!read_cb_.is_null()); | |
226 | |
227 if (!stop_cb_.is_null()) { | |
228 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
229 DoStop(); | |
230 return; | |
231 } | |
232 | |
233 if (!reset_cb_.is_null()) { | |
234 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
235 DoReset(); | |
236 return; | |
237 } | |
238 | |
239 if (status == Decryptor::kNoKey || status == Decryptor::kError) { | |
240 DCHECK(!frame); | |
241 state_ = kDecodeFinished; | |
242 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
ddorwin
2012/09/28 17:36:40
Why is kNoKey a decode error? Where is the wait an
xhwang
2012/09/30 19:58:51
The wait and retry logic is in ProxyDecryptor so i
ddorwin
2012/10/01 18:43:20
But wouldn't this class always wrap the real Decry
| |
243 return; | |
244 } | |
245 | |
246 // The buffer has been accepted by the decoder, let's report statistics. | |
247 if (buffer_size) { | |
248 PipelineStatistics statistics; | |
249 statistics.video_bytes_decoded = buffer_size; | |
250 statistics_cb_.Run(statistics); | |
251 } | |
252 | |
253 if (status == Decryptor::kNeedMoreData) { | |
254 DCHECK(!frame); | |
255 ReadFromDemuxerStream(); | |
256 return; | |
257 } | |
258 | |
259 DCHECK_EQ(Decryptor::kSuccess, status); | |
ddorwin
2012/09/28 17:36:40
same
xhwang
2012/09/30 19:58:51
Done.
| |
260 DCHECK(frame); | |
261 if (frame->IsEndOfStream()) | |
262 state_ = kDecodeFinished; | |
263 | |
264 base::ResetAndReturn(&read_cb_).Run(kOk, frame); | |
265 } | |
266 | |
267 void DecryptingVideoDecoder::DoReset() { | |
268 DCHECK(read_cb_.is_null()); | |
269 state_ = kNormal; | |
ddorwin
2012/09/28 17:36:40
DCHECK(state_ != kUninitialized).
xhwang
2012/09/30 19:58:51
Done.
| |
270 base::ResetAndReturn(&reset_cb_).Run(); | |
271 } | |
272 | |
273 void DecryptingVideoDecoder::DoStop() { | |
274 DCHECK(read_cb_.is_null()); | |
275 state_ = kUninitialized; | |
276 base::ResetAndReturn(&stop_cb_).Run(); | |
277 } | |
278 | |
279 } // namespace media | |
OLD | NEW |