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) { |
xhwang
2014/06/05 21:53:51
nit: Probably this should never happen (DecoderStr
Sergey Ulanov
2014/06/06 22:49:41
Done.
Sergey Ulanov
2014/06/06 23:12:34
Actually DecoderStream may call this method after
xhwang
2014/06/07 00:35:14
Can you add a TODO here?
| |
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. |
xhwang
2014/06/05 21:53:50
this is obsolete now?
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
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. |
xhwang
2014/06/05 21:53:50
When EOS buffer is received and the codec has been
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
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 bool repeat; |
243 if (state_ == kNormal && buffer->end_of_stream()) { | 209 do { |
244 state_ = kFlushCodec; | 210 bool produced_frame = false; |
211 if (!FFmpegDecode(buffer, &produced_frame)) { | |
xhwang
2014/06/05 21:53:50
|produced_frame| could be read as a noun. How abou
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
212 state_ = kError; | |
213 decode_cb_bound.Run(kDecodeError); | |
214 return; | |
215 } | |
216 // Repeat to flush the decoder after receiving EOS buffer. | |
217 repeat = buffer->end_of_stream() && produced_frame; | |
218 } while (repeat); | |
xhwang
2014/06/05 21:53:50
nit: how about just
while (buffer->end_of_stream(
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
219 | |
220 if (buffer->end_of_stream()) { | |
221 output_cb_.Run(VideoFrame::CreateEOSFrame()); | |
222 state_ = kDecodeFinished; | |
245 } | 223 } |
246 | 224 |
247 scoped_refptr<VideoFrame> video_frame; | 225 decode_cb_bound.Run(kOk); |
248 if (!FFmpegDecode(buffer, &video_frame)) { | 226 } |
249 state_ = kError; | 227 |
250 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); | 228 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { |
229 DCHECK(task_runner_->BelongsToCurrentThread()); | |
230 | |
231 avcodec_flush_buffers(codec_context_.get()); | |
232 state_ = kNormal; | |
233 task_runner_->PostTask(FROM_HERE, closure); | |
234 } | |
235 | |
236 void FFmpegVideoDecoder::Stop() { | |
237 DCHECK(task_runner_->BelongsToCurrentThread()); | |
238 | |
239 if (state_ == kUninitialized) | |
251 return; | 240 return; |
252 } | |
253 | 241 |
254 if (!video_frame.get()) { | 242 ReleaseFFmpegResources(); |
255 if (state_ == kFlushCodec) { | 243 state_ = kUninitialized; |
256 DCHECK(buffer->end_of_stream()); | 244 } |
257 state_ = kDecodeFinished; | |
258 base::ResetAndReturn(&decode_cb_) | |
259 .Run(kOk, VideoFrame::CreateEOSFrame()); | |
260 return; | |
261 } | |
262 | 245 |
263 base::ResetAndReturn(&decode_cb_).Run(kNotEnoughData, NULL); | 246 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
264 return; | 247 DCHECK_EQ(kUninitialized, state_); |
265 } | 248 DCHECK(!codec_context_); |
266 | 249 DCHECK(!av_frame_); |
267 base::ResetAndReturn(&decode_cb_).Run(kOk, video_frame); | |
268 } | 250 } |
269 | 251 |
270 bool FFmpegVideoDecoder::FFmpegDecode( | 252 bool FFmpegVideoDecoder::FFmpegDecode( |
271 const scoped_refptr<DecoderBuffer>& buffer, | 253 const scoped_refptr<DecoderBuffer>& buffer, |
272 scoped_refptr<VideoFrame>* video_frame) { | 254 bool* produced_frame) { |
273 DCHECK(video_frame); | 255 *produced_frame = false; |
xhwang
2014/06/05 21:53:51
DCHECK(!*produced_frame);
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
274 | 256 |
275 // Reset frame to default values. | 257 // Reset frame to default values. |
276 avcodec_get_frame_defaults(av_frame_.get()); | 258 avcodec_get_frame_defaults(av_frame_.get()); |
277 | 259 |
278 // Create a packet for input data. | 260 // Create a packet for input data. |
279 // Due to FFmpeg API changes we no longer have const read-only pointers. | 261 // Due to FFmpeg API changes we no longer have const read-only pointers. |
280 AVPacket packet; | 262 AVPacket packet; |
281 av_init_packet(&packet); | 263 av_init_packet(&packet); |
282 if (buffer->end_of_stream()) { | 264 if (buffer->end_of_stream()) { |
283 packet.data = NULL; | 265 packet.data = NULL; |
(...skipping 11 matching lines...) Expand all Loading... | |
295 } | 277 } |
296 | 278 |
297 int frame_decoded = 0; | 279 int frame_decoded = 0; |
298 int result = avcodec_decode_video2(codec_context_.get(), | 280 int result = avcodec_decode_video2(codec_context_.get(), |
299 av_frame_.get(), | 281 av_frame_.get(), |
300 &frame_decoded, | 282 &frame_decoded, |
301 &packet); | 283 &packet); |
302 // Log the problem if we can't decode a video frame and exit early. | 284 // Log the problem if we can't decode a video frame and exit early. |
303 if (result < 0) { | 285 if (result < 0) { |
304 LOG(ERROR) << "Error decoding video: " << buffer->AsHumanReadableString(); | 286 LOG(ERROR) << "Error decoding video: " << buffer->AsHumanReadableString(); |
305 *video_frame = NULL; | |
306 return false; | 287 return false; |
307 } | 288 } |
308 | 289 |
309 // If no frame was produced then signal that more data is required to | 290 // If no frame was produced then signal that more data is required to |
310 // produce more frames. This can happen under two circumstances: | 291 // produce more frames. This can happen under two circumstances: |
311 // 1) Decoder was recently initialized/flushed | 292 // 1) Decoder was recently initialized/flushed |
312 // 2) End of stream was reached and all internal frames have been output | 293 // 2) End of stream was reached and all internal frames have been output |
313 if (frame_decoded == 0) { | 294 if (frame_decoded == 0) { |
314 *video_frame = NULL; | |
315 return true; | 295 return true; |
316 } | 296 } |
317 | 297 |
318 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 | 298 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 |
319 // The decoder is in a bad state and not decoding correctly. | 299 // The decoder is in a bad state and not decoding correctly. |
320 // Checking for NULL avoids a crash in CopyPlane(). | 300 // Checking for NULL avoids a crash in CopyPlane(). |
321 if (!av_frame_->data[VideoFrame::kYPlane] || | 301 if (!av_frame_->data[VideoFrame::kYPlane] || |
322 !av_frame_->data[VideoFrame::kUPlane] || | 302 !av_frame_->data[VideoFrame::kUPlane] || |
323 !av_frame_->data[VideoFrame::kVPlane]) { | 303 !av_frame_->data[VideoFrame::kVPlane]) { |
324 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; | 304 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; |
325 *video_frame = NULL; | |
326 return false; | 305 return false; |
327 } | 306 } |
328 | 307 |
329 if (!av_frame_->opaque) { | 308 if (!av_frame_->opaque) { |
330 LOG(ERROR) << "VideoFrame object associated with frame data not set."; | 309 LOG(ERROR) << "VideoFrame object associated with frame data not set."; |
331 return false; | 310 return false; |
332 } | 311 } |
333 *video_frame = static_cast<VideoFrame*>(av_frame_->opaque); | |
334 | 312 |
335 (*video_frame)->set_timestamp( | 313 scoped_refptr<VideoFrame> frame = static_cast<VideoFrame*>(av_frame_->opaque); |
314 frame->set_timestamp( | |
336 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); | 315 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); |
316 *produced_frame = true; | |
317 output_cb_.Run(frame); | |
337 | 318 |
338 return true; | 319 return true; |
339 } | 320 } |
340 | 321 |
341 void FFmpegVideoDecoder::ReleaseFFmpegResources() { | 322 void FFmpegVideoDecoder::ReleaseFFmpegResources() { |
342 codec_context_.reset(); | 323 codec_context_.reset(); |
343 av_frame_.reset(); | 324 av_frame_.reset(); |
344 } | 325 } |
345 | 326 |
346 bool FFmpegVideoDecoder::ConfigureDecoder(bool low_delay) { | 327 bool FFmpegVideoDecoder::ConfigureDecoder(bool low_delay) { |
(...skipping 18 matching lines...) Expand all Loading... | |
365 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { | 346 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { |
366 ReleaseFFmpegResources(); | 347 ReleaseFFmpegResources(); |
367 return false; | 348 return false; |
368 } | 349 } |
369 | 350 |
370 av_frame_.reset(av_frame_alloc()); | 351 av_frame_.reset(av_frame_alloc()); |
371 return true; | 352 return true; |
372 } | 353 } |
373 | 354 |
374 } // namespace media | 355 } // namespace media |
OLD | NEW |