OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/filters/ffmpeg_video_decoder.h" | 5 #include "media/filters/ffmpeg_video_decoder.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
128 video_frame.swap(reinterpret_cast<VideoFrame**>(&frame->opaque)); | 128 video_frame.swap(reinterpret_cast<VideoFrame**>(&frame->opaque)); |
129 | 129 |
130 // The FFmpeg API expects us to zero the data pointers in | 130 // The FFmpeg API expects us to zero the data pointers in |
131 // this callback | 131 // this callback |
132 memset(frame->data, 0, sizeof(frame->data)); | 132 memset(frame->data, 0, sizeof(frame->data)); |
133 frame->opaque = NULL; | 133 frame->opaque = NULL; |
134 } | 134 } |
135 | 135 |
136 void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config, | 136 void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config, |
137 bool low_delay, | 137 bool low_delay, |
138 const PipelineStatusCB& status_cb) { | 138 const PipelineStatusCB& status_cb, |
139 const OutputCB& output_cb) { | |
139 DCHECK(task_runner_->BelongsToCurrentThread()); | 140 DCHECK(task_runner_->BelongsToCurrentThread()); |
140 DCHECK(decode_cb_.is_null()); | |
141 DCHECK(!config.is_encrypted()); | 141 DCHECK(!config.is_encrypted()); |
142 DCHECK(!output_cb.is_null()); | |
142 | 143 |
143 FFmpegGlue::InitializeFFmpeg(); | 144 FFmpegGlue::InitializeFFmpeg(); |
144 | 145 |
145 config_ = config; | 146 config_ = config; |
146 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); | 147 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); |
147 | 148 |
148 if (!config.IsValidConfig() || !ConfigureDecoder(low_delay)) { | 149 if (!config.IsValidConfig() || !ConfigureDecoder(low_delay)) { |
149 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 150 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
150 return; | 151 return; |
151 } | 152 } |
152 | 153 |
154 output_cb_ = BindToCurrentLoop(output_cb); | |
155 | |
153 // Success! | 156 // Success! |
154 state_ = kNormal; | 157 state_ = kNormal; |
155 initialize_cb.Run(PIPELINE_OK); | 158 initialize_cb.Run(PIPELINE_OK); |
156 } | 159 } |
157 | 160 |
158 void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, | 161 void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
159 const DecodeCB& decode_cb) { | 162 const DecodeCB& decode_cb) { |
160 DCHECK(task_runner_->BelongsToCurrentThread()); | 163 DCHECK(task_runner_->BelongsToCurrentThread()); |
164 DCHECK(buffer); | |
161 DCHECK(!decode_cb.is_null()); | 165 DCHECK(!decode_cb.is_null()); |
162 CHECK_NE(state_, kUninitialized); | 166 CHECK_NE(state_, kUninitialized); |
163 CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; | 167 |
164 decode_cb_ = BindToCurrentLoop(decode_cb); | 168 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); |
165 | 169 |
166 if (state_ == kError) { | 170 if (state_ == kError) { |
167 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); | 171 decode_cb_bound.Run(kDecodeError); |
168 return; | 172 return; |
169 } | 173 } |
170 | 174 |
171 // Return empty frames if decoding has finished. | 175 // Return empty frames if decoding has finished. |
172 if (state_ == kDecodeFinished) { | 176 if (state_ == kDecodeFinished) { |
173 base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEOSFrame()); | 177 output_cb_.Run(VideoFrame::CreateEOSFrame()); |
178 decode_cb_bound.Run(kOk); | |
174 return; | 179 return; |
175 } | 180 } |
176 | 181 |
177 DecodeBuffer(buffer); | 182 DCHECK_EQ(state_, kNormal); |
178 } | |
179 | |
180 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { | |
181 DCHECK(task_runner_->BelongsToCurrentThread()); | |
182 DCHECK(decode_cb_.is_null()); | |
183 | |
184 avcodec_flush_buffers(codec_context_.get()); | |
185 state_ = kNormal; | |
186 task_runner_->PostTask(FROM_HERE, closure); | |
187 } | |
188 | |
189 void FFmpegVideoDecoder::Stop() { | |
190 DCHECK(task_runner_->BelongsToCurrentThread()); | |
191 | |
192 if (state_ == kUninitialized) | |
193 return; | |
194 | |
195 ReleaseFFmpegResources(); | |
196 state_ = kUninitialized; | |
197 } | |
198 | |
199 FFmpegVideoDecoder::~FFmpegVideoDecoder() { | |
200 DCHECK_EQ(kUninitialized, state_); | |
201 DCHECK(!codec_context_); | |
202 DCHECK(!av_frame_); | |
203 } | |
204 | |
205 void FFmpegVideoDecoder::DecodeBuffer( | |
206 const scoped_refptr<DecoderBuffer>& buffer) { | |
207 DCHECK(task_runner_->BelongsToCurrentThread()); | |
208 DCHECK_NE(state_, kUninitialized); | |
209 DCHECK_NE(state_, kDecodeFinished); | |
210 DCHECK_NE(state_, kError); | |
211 DCHECK(!decode_cb_.is_null()); | |
212 DCHECK(buffer); | |
213 | 183 |
214 // During decode, because reads are issued asynchronously, it is possible to | 184 // During decode, because reads are issued asynchronously, it is possible to |
215 // receive multiple end of stream buffers since each decode is acked. When the | 185 // receive multiple end of stream buffers since each decode is acked. When the |
216 // first end of stream buffer is read, FFmpeg may still have frames queued | 186 // first end of stream buffer is read, FFmpeg may still have frames queued |
217 // up in the decoder so we need to go through the decode loop until it stops | 187 // up in the decoder so we need to go through the decode loop until it stops |
218 // giving sensible data. After that, the decoder should output empty | 188 // giving sensible data. After that, the decoder should output empty |
219 // frames. There are three states the decoder can be in: | 189 // frames. There are three states the decoder can be in: |
220 // | 190 // |
221 // kNormal: This is the starting state. Buffers are decoded. Decode errors | 191 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
222 // are discarded. | 192 // are discarded. |
223 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 | 193 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 |
224 // until no more data is returned to flush out remaining | 194 // until no more data is returned to flush out remaining |
225 // frames. The input buffer is ignored at this point. | 195 // frames. The input buffer is ignored at this point. |
226 // kDecodeFinished: All calls return empty frames. | 196 // kDecodeFinished: All calls return empty frames. |
227 // kError: Unexpected error happened. | 197 // kError: Unexpected error happened. |
228 // | 198 // |
229 // These are the possible state transitions. | 199 // These are the possible state transitions. |
230 // | 200 // |
231 // kNormal -> kFlushCodec: | 201 // kNormal -> kDecodeFinished: |
232 // When buffer->end_of_stream() is first true. | 202 // When EOS buffer is received. |
233 // kNormal -> kError: | 203 // kNormal -> kError: |
234 // A decoding error occurs and decoding needs to stop. | 204 // A decoding error occurs and decoding needs to stop. |
235 // kFlushCodec -> kDecodeFinished: | |
236 // When avcodec_decode_video2() returns 0 data. | |
237 // kFlushCodec -> kError: | |
238 // When avcodec_decode_video2() errors out. | |
239 // (any state) -> kNormal: | 205 // (any state) -> kNormal: |
240 // Any time Reset() is called. | 206 // Any time Reset() is called. |
241 | 207 |
242 // Transition to kFlushCodec on the first end of stream buffer. | 208 // Flush the decoder after receiving EOS buffer. |
243 if (state_ == kNormal && buffer->end_of_stream()) { | 209 if (buffer->end_of_stream()) { |
244 state_ = kFlushCodec; | 210 scoped_refptr<VideoFrame> video_frame; |
211 do { | |
212 if (!FFmpegDecode(buffer, &video_frame)) { | |
213 state_ = kError; | |
214 decode_cb_bound.Run(kDecodeError); | |
215 return; | |
216 } | |
217 if (video_frame) | |
218 output_cb_.Run(video_frame); | |
219 } while (video_frame); | |
220 output_cb_.Run(VideoFrame::CreateEOSFrame()); | |
221 state_ = kDecodeFinished; | |
222 } else { | |
223 scoped_refptr<VideoFrame> video_frame; | |
224 if (!FFmpegDecode(buffer, &video_frame)) { | |
225 state_ = kError; | |
226 decode_cb_bound.Run(kDecodeError); | |
227 return; | |
228 } | |
229 if (video_frame) | |
230 output_cb_.Run(video_frame); | |
xhwang
2014/05/29 22:15:14
You can probably wrap l.223-230 into a helper func
Sergey Ulanov
2014/06/03 00:08:11
Formatted this code to avoid duplication.
| |
245 } | 231 } |
246 | 232 |
247 scoped_refptr<VideoFrame> video_frame; | 233 decode_cb_bound.Run(kOk); |
248 if (!FFmpegDecode(buffer, &video_frame)) { | 234 } |
249 state_ = kError; | 235 |
250 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); | 236 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { |
237 DCHECK(task_runner_->BelongsToCurrentThread()); | |
238 | |
239 avcodec_flush_buffers(codec_context_.get()); | |
240 state_ = kNormal; | |
241 task_runner_->PostTask(FROM_HERE, closure); | |
242 } | |
243 | |
244 void FFmpegVideoDecoder::Stop() { | |
245 DCHECK(task_runner_->BelongsToCurrentThread()); | |
246 | |
247 if (state_ == kUninitialized) | |
251 return; | 248 return; |
252 } | |
253 | 249 |
254 if (!video_frame.get()) { | 250 ReleaseFFmpegResources(); |
255 if (state_ == kFlushCodec) { | 251 state_ = kUninitialized; |
256 DCHECK(buffer->end_of_stream()); | 252 } |
257 state_ = kDecodeFinished; | |
258 base::ResetAndReturn(&decode_cb_) | |
259 .Run(kOk, VideoFrame::CreateEOSFrame()); | |
260 return; | |
261 } | |
262 | 253 |
263 base::ResetAndReturn(&decode_cb_).Run(kNotEnoughData, NULL); | 254 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
264 return; | 255 DCHECK_EQ(kUninitialized, state_); |
265 } | 256 DCHECK(!codec_context_); |
266 | 257 DCHECK(!av_frame_); |
267 base::ResetAndReturn(&decode_cb_).Run(kOk, video_frame); | |
268 } | 258 } |
269 | 259 |
270 bool FFmpegVideoDecoder::FFmpegDecode( | 260 bool FFmpegVideoDecoder::FFmpegDecode( |
271 const scoped_refptr<DecoderBuffer>& buffer, | 261 const scoped_refptr<DecoderBuffer>& buffer, |
272 scoped_refptr<VideoFrame>* video_frame) { | 262 scoped_refptr<VideoFrame>* video_frame) { |
273 DCHECK(video_frame); | 263 DCHECK(video_frame); |
274 | 264 |
275 // Reset frame to default values. | 265 // Reset frame to default values. |
276 avcodec_get_frame_defaults(av_frame_.get()); | 266 avcodec_get_frame_defaults(av_frame_.get()); |
277 | 267 |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
365 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { | 355 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { |
366 ReleaseFFmpegResources(); | 356 ReleaseFFmpegResources(); |
367 return false; | 357 return false; |
368 } | 358 } |
369 | 359 |
370 av_frame_.reset(av_frame_alloc()); | 360 av_frame_.reset(av_frame_alloc()); |
371 return true; | 361 return true; |
372 } | 362 } |
373 | 363 |
374 } // namespace media | 364 } // namespace media |
OLD | NEW |