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/crypto/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_) { | |
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; | |
44 | |
45 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config(); | |
46 | |
47 if (!config.IsValidConfig()) { | |
48 DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); | |
49 status_cb.Run(PIPELINE_ERROR_DECODE); | |
50 return; | |
51 } | |
52 | |
53 // DecryptingVideoDecoder only accepts potentially encrypted stream. | |
54 if (!config.is_encrypted()) { | |
55 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | |
56 return; | |
57 } | |
58 | |
59 status_cb_ = status_cb; | |
60 statistics_cb_ = statistics_cb; | |
61 | |
62 decryptor_->InitializeVideoDecoder( | |
63 config, | |
64 base::Bind(&DecryptingVideoDecoder::OnDecoderInitDone, this)); | |
65 } | |
66 | |
67 void DecryptingVideoDecoder::Read(const ReadCB& read_cb) { | |
68 // Complete operation asynchronously on different stack of execution as per | |
69 // the API contract of VideoDecoder::Read() | |
70 message_loop_->PostTask(FROM_HERE, base::Bind( | |
71 &DecryptingVideoDecoder::DoRead, this, read_cb)); | |
72 } | |
73 | |
74 void DecryptingVideoDecoder::Reset(const base::Closure& closure) { | |
75 if (!message_loop_->BelongsToCurrentThread()) { | |
76 message_loop_->PostTask(FROM_HERE, base::Bind( | |
77 &DecryptingVideoDecoder::Reset, this, closure)); | |
78 return; | |
79 } | |
80 | |
81 decryptor_->ResetVideoDecoder(); | |
82 reset_cb_ = closure; | |
ddorwin
2012/09/21 00:36:08
What happens if Reset() is called twice before the
xhwang
2012/09/25 23:52:32
Good call. I think VideoDecoder interface should d
| |
83 | |
84 // Defer the reset if a read is pending. | |
85 if (!read_cb_.is_null()) | |
86 return; | |
87 | |
88 DoReset(); | |
89 } | |
90 | |
91 void DecryptingVideoDecoder::Stop(const base::Closure& closure) { | |
92 if (!message_loop_->BelongsToCurrentThread()) { | |
93 message_loop_->PostTask(FROM_HERE, base::Bind( | |
94 &DecryptingVideoDecoder::Stop, this, closure)); | |
95 return; | |
96 } | |
97 | |
98 decryptor_->StopVideoDecoder(); | |
99 stop_cb_ = closure; | |
100 | |
101 // Defer stopping if a read is pending. | |
102 if (!read_cb_.is_null()) | |
103 return; | |
104 | |
105 DoStop(); | |
106 } | |
107 | |
108 DecryptingVideoDecoder::~DecryptingVideoDecoder() { | |
109 } | |
110 | |
111 void DecryptingVideoDecoder::OnDecoderInitDone(bool success) { | |
112 if (!message_loop_->BelongsToCurrentThread()) { | |
113 message_loop_->PostTask(FROM_HERE, base::Bind( | |
114 &DecryptingVideoDecoder::OnDecoderInitDone, this, success)); | |
115 return; | |
116 } | |
117 | |
118 DCHECK(!status_cb_.is_null()); | |
119 if (!success) | |
120 base::ResetAndReturn(&status_cb_).Run(PIPELINE_ERROR_DECODE); | |
121 | |
122 // Success! | |
123 state_ = kNormal; | |
124 base::ResetAndReturn(&status_cb_).Run(PIPELINE_OK); | |
125 } | |
126 | |
127 void DecryptingVideoDecoder::DoRead(const ReadCB& read_cb) { | |
128 DCHECK(message_loop_->BelongsToCurrentThread()); | |
129 DCHECK(!read_cb.is_null()); | |
130 CHECK_NE(state_, kUninitialized); | |
131 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
132 | |
133 // Return empty frames if decoding has finished. | |
134 if (state_ == kDecodeFinished) { | |
135 read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); | |
136 return; | |
137 } | |
138 | |
139 read_cb_ = read_cb; | |
140 ReadFromDemuxerStream(); | |
141 } | |
142 | |
143 void DecryptingVideoDecoder::ReadFromDemuxerStream() { | |
144 DCHECK_NE(state_, kUninitialized); | |
145 DCHECK_NE(state_, kDecodeFinished); | |
146 DCHECK(!read_cb_.is_null()); | |
147 | |
148 demuxer_stream_->Read( | |
149 base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, this)); | |
150 } | |
151 | |
152 void DecryptingVideoDecoder::DecryptAndDecodeBuffer( | |
153 DemuxerStream::Status status, | |
154 const scoped_refptr<DecoderBuffer>& buffer) { | |
155 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | |
156 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read | |
157 // callback on the same execution stack so we can get rid of forced task post. | |
158 message_loop_->PostTask(FROM_HERE, base::Bind( | |
159 &DecryptingVideoDecoder::DoDecryptAndDecodeBuffer, this, status, buffer)); | |
160 } | |
161 | |
162 void DecryptingVideoDecoder::DoDecryptAndDecodeBuffer( | |
163 DemuxerStream::Status status, | |
164 const scoped_refptr<DecoderBuffer>& buffer) { | |
165 DCHECK(message_loop_->BelongsToCurrentThread()); | |
166 DCHECK_NE(state_, kUninitialized); | |
167 DCHECK_NE(state_, kDecodeFinished); | |
168 DCHECK(!read_cb_.is_null()); | |
169 | |
170 if (!stop_cb_.is_null()) { | |
171 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
172 DoStop(); | |
173 return; | |
174 } | |
175 | |
176 if (!reset_cb_.is_null()) { | |
177 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
178 DoReset(); | |
179 return; | |
180 } | |
181 | |
182 if (status == DemuxerStream::kAborted) { | |
183 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
184 return; | |
185 } | |
186 | |
187 if (status == DemuxerStream::kConfigChanged) { | |
188 // TODO(xhwang): Add config change support. | |
189 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
190 return; | |
191 } | |
192 | |
193 DCHECK_EQ(DemuxerStream::kOk, status); | |
194 DCHECK(buffer); | |
195 | |
196 decryptor_->DecryptAndDecodeVideo( | |
197 buffer, base::Bind(&DecryptingVideoDecoder::DeliverFrame, this, | |
198 buffer->GetDataSize())); | |
199 } | |
200 | |
201 void DecryptingVideoDecoder::DeliverFrame( | |
202 int buffer_size, | |
203 Decryptor::Status status, | |
204 const scoped_refptr<VideoFrame>& frame) { | |
205 if (!message_loop_->BelongsToCurrentThread()) { | |
206 message_loop_->PostTask(FROM_HERE, base::Bind( | |
207 &DecryptingVideoDecoder::DeliverFrame, this, | |
208 buffer_size, status, frame)); | |
209 return; | |
210 } | |
211 | |
212 DCHECK_NE(state_, kUninitialized); | |
213 DCHECK_NE(state_, kDecodeFinished); | |
214 DCHECK(!read_cb_.is_null()); | |
215 | |
216 if (status == Decryptor::kNoKey || status == Decryptor::kError) { | |
217 DCHECK(!frame); | |
218 state_ = kDecodeFinished; | |
219 base::ResetAndReturn(&read_cb_).Run(kDecryptError, NULL); | |
220 return; | |
221 } | |
222 | |
223 // The buffer has been accepted by the decoder, let's report statistics. | |
224 if (buffer_size) { | |
225 PipelineStatistics statistics; | |
226 statistics.video_bytes_decoded = buffer_size; | |
227 statistics_cb_.Run(statistics); | |
228 } | |
229 | |
230 if (status == Decryptor::kNeedMoreData) { | |
231 DCHECK(!frame); | |
232 ReadFromDemuxerStream(); | |
233 return; | |
234 } | |
235 | |
236 DCHECK_EQ(Decryptor::kSuccess, status); | |
237 DCHECK(frame); | |
238 if (frame->IsEndOfStream()) | |
239 state_ = kDecodeFinished; | |
240 | |
241 base::ResetAndReturn(&read_cb_).Run(kOk, frame); | |
242 } | |
243 | |
244 void DecryptingVideoDecoder::DoReset() { | |
245 DCHECK(read_cb_.is_null()); | |
246 state_ = kNormal; | |
247 base::ResetAndReturn(&reset_cb_).Run(); | |
248 } | |
249 | |
250 void DecryptingVideoDecoder::DoStop() { | |
251 DCHECK(read_cb_.is_null()); | |
252 state_ = kUninitialized; | |
253 base::ResetAndReturn(&stop_cb_).Run(); | |
254 } | |
255 | |
256 } // namespace media | |
OLD | NEW |