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 "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
76 DCHECK(format == VideoFrame::YV12 || format == VideoFrame::YV16); | 76 DCHECK(format == VideoFrame::YV12 || format == VideoFrame::YV16); |
77 | 77 |
78 int width = codec_context->width; | 78 int width = codec_context->width; |
79 int height = codec_context->height; | 79 int height = codec_context->height; |
80 int ret; | 80 int ret; |
81 if ((ret = av_image_check_size(width, height, 0, NULL)) < 0) | 81 if ((ret = av_image_check_size(width, height, 0, NULL)) < 0) |
82 return ret; | 82 return ret; |
83 | 83 |
84 scoped_refptr<VideoFrame> video_frame = | 84 scoped_refptr<VideoFrame> video_frame = |
85 VideoFrame::CreateFrame(format, width, height, | 85 VideoFrame::CreateFrame(format, width, height, |
86 natural_size_.width(), | |
87 natural_size_.height(), | |
86 kNoTimestamp(), kNoTimestamp()); | 88 kNoTimestamp(), kNoTimestamp()); |
87 | 89 |
88 for (int i = 0; i < 3; i++) { | 90 for (int i = 0; i < 3; i++) { |
89 frame->base[i] = video_frame->data(i); | 91 frame->base[i] = video_frame->data(i); |
90 frame->data[i] = video_frame->data(i); | 92 frame->data[i] = video_frame->data(i); |
91 frame->linesize[i] = video_frame->stride(i); | 93 frame->linesize[i] = video_frame->stride(i); |
92 } | 94 } |
93 | 95 |
94 frame->opaque = video_frame.release(); | 96 frame->opaque = video_frame.release(); |
95 frame->type = FF_BUFFER_TYPE_USER; | 97 frame->type = FF_BUFFER_TYPE_USER; |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
151 const VideoDecoderConfig& config = stream->video_decoder_config(); | 153 const VideoDecoderConfig& config = stream->video_decoder_config(); |
152 | 154 |
153 // TODO(scherkus): this check should go in Pipeline prior to creating | 155 // TODO(scherkus): this check should go in Pipeline prior to creating |
154 // decoder objects. | 156 // decoder objects. |
155 if (!config.IsValidConfig()) { | 157 if (!config.IsValidConfig()) { |
156 DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); | 158 DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); |
157 status_cb.Run(PIPELINE_ERROR_DECODE); | 159 status_cb.Run(PIPELINE_ERROR_DECODE); |
158 return; | 160 return; |
159 } | 161 } |
160 | 162 |
161 // Initialize AVCodecContext structure. | 163 if (!AllocateFFmpegResources(config)) { |
Ami GONE FROM CHROMIUM
2012/07/13 00:59:05
I wonder if it doesn't make sense to inline AFFMR(
| |
162 codec_context_ = avcodec_alloc_context3(NULL); | |
163 VideoDecoderConfigToAVCodecContext(config, codec_context_); | |
164 | |
165 // Enable motion vector search (potentially slow), strong deblocking filter | |
166 // for damaged macroblocks, and set our error detection sensitivity. | |
167 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; | |
168 codec_context_->err_recognition = AV_EF_CAREFUL; | |
169 codec_context_->thread_count = GetThreadCount(codec_context_->codec_id); | |
170 codec_context_->opaque = this; | |
171 codec_context_->flags |= CODEC_FLAG_EMU_EDGE; | |
172 codec_context_->get_buffer = GetVideoBufferImpl; | |
173 codec_context_->release_buffer = ReleaseVideoBufferImpl; | |
174 | |
175 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | |
176 if (!codec) { | |
177 status_cb.Run(PIPELINE_ERROR_DECODE); | |
178 return; | |
179 } | |
180 | |
181 if (avcodec_open2(codec_context_, codec, NULL) < 0) { | |
182 status_cb.Run(PIPELINE_ERROR_DECODE); | 164 status_cb.Run(PIPELINE_ERROR_DECODE); |
183 return; | 165 return; |
184 } | 166 } |
185 | 167 |
186 // Success! | 168 // Success! |
187 state_ = kNormal; | 169 state_ = kNormal; |
188 av_frame_ = avcodec_alloc_frame(); | |
189 natural_size_ = config.natural_size(); | 170 natural_size_ = config.natural_size(); |
190 frame_rate_numerator_ = config.frame_rate_numerator(); | 171 frame_rate_numerator_ = config.frame_rate_numerator(); |
191 frame_rate_denominator_ = config.frame_rate_denominator(); | 172 frame_rate_denominator_ = config.frame_rate_denominator(); |
Ami GONE FROM CHROMIUM
2012/07/13 00:59:05
...in which case these can go away
| |
192 status_cb.Run(PIPELINE_OK); | 173 status_cb.Run(PIPELINE_OK); |
193 } | 174 } |
194 | 175 |
195 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { | 176 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { |
196 // Complete operation asynchronously on different stack of execution as per | 177 // Complete operation asynchronously on different stack of execution as per |
197 // the API contract of VideoDecoder::Read() | 178 // the API contract of VideoDecoder::Read() |
198 message_loop_->PostTask(FROM_HERE, base::Bind( | 179 message_loop_->PostTask(FROM_HERE, base::Bind( |
199 &FFmpegVideoDecoder::DoRead, this, read_cb)); | 180 &FFmpegVideoDecoder::DoRead, this, read_cb)); |
200 } | 181 } |
201 | 182 |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
294 DCHECK_NE(state_, kUninitialized); | 275 DCHECK_NE(state_, kUninitialized); |
295 DCHECK_NE(state_, kDecodeFinished); | 276 DCHECK_NE(state_, kDecodeFinished); |
296 DCHECK(!read_cb_.is_null()); | 277 DCHECK(!read_cb_.is_null()); |
297 | 278 |
298 if (!reset_cb_.is_null()) { | 279 if (!reset_cb_.is_null()) { |
299 DeliverFrame(NULL); | 280 DeliverFrame(NULL); |
300 DoReset(); | 281 DoReset(); |
301 return; | 282 return; |
302 } | 283 } |
303 | 284 |
304 if (status != DemuxerStream::kOk) { | 285 if (status == DemuxerStream::kAborted) { |
305 DecoderStatus decoder_status = | 286 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
306 (status == DemuxerStream::kAborted) ? kOk : kDecodeError; | |
307 base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL); | |
308 return; | 287 return; |
309 } | 288 } |
310 | 289 |
290 if (status == DemuxerStream::kConfigChanged) { | |
291 if (!HandleConfigChange()) { | |
292 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
293 return; | |
294 } | |
295 | |
296 ReadFromDemuxerStream(); | |
297 return; | |
298 } | |
299 | |
311 // During decode, because reads are issued asynchronously, it is possible to | 300 // During decode, because reads are issued asynchronously, it is possible to |
312 // receive multiple end of stream buffers since each read is acked. When the | 301 // receive multiple end of stream buffers since each read is acked. When the |
313 // first end of stream buffer is read, FFmpeg may still have frames queued | 302 // first end of stream buffer is read, FFmpeg may still have frames queued |
314 // up in the decoder so we need to go through the decode loop until it stops | 303 // up in the decoder so we need to go through the decode loop until it stops |
315 // giving sensible data. After that, the decoder should output empty | 304 // giving sensible data. After that, the decoder should output empty |
316 // frames. There are three states the decoder can be in: | 305 // frames. There are three states the decoder can be in: |
317 // | 306 // |
318 // kNormal: This is the starting state. Buffers are decoded. Decode errors | 307 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
319 // are discarded. | 308 // are discarded. |
320 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 | 309 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
470 | 459 |
471 return true; | 460 return true; |
472 } | 461 } |
473 | 462 |
474 void FFmpegVideoDecoder::DeliverFrame( | 463 void FFmpegVideoDecoder::DeliverFrame( |
475 const scoped_refptr<VideoFrame>& video_frame) { | 464 const scoped_refptr<VideoFrame>& video_frame) { |
476 // Reset the callback before running to protect against reentrancy. | 465 // Reset the callback before running to protect against reentrancy. |
477 base::ResetAndReturn(&read_cb_).Run(kOk, video_frame); | 466 base::ResetAndReturn(&read_cb_).Run(kOk, video_frame); |
478 } | 467 } |
479 | 468 |
469 bool FFmpegVideoDecoder::AllocateFFmpegResources( | |
470 const VideoDecoderConfig& config) { | |
471 DCHECK(!codec_context_); | |
472 DCHECK(!av_frame_); | |
473 | |
474 // Initialize AVCodecContext structure. | |
475 codec_context_ = avcodec_alloc_context3(NULL); | |
476 VideoDecoderConfigToAVCodecContext(config, codec_context_); | |
477 | |
478 // Enable motion vector search (potentially slow), strong deblocking filter | |
479 // for damaged macroblocks, and set our error detection sensitivity. | |
480 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; | |
481 codec_context_->err_recognition = AV_EF_CAREFUL; | |
482 codec_context_->thread_count = GetThreadCount(codec_context_->codec_id); | |
483 codec_context_->opaque = this; | |
484 codec_context_->flags |= CODEC_FLAG_EMU_EDGE; | |
485 codec_context_->get_buffer = GetVideoBufferImpl; | |
486 codec_context_->release_buffer = ReleaseVideoBufferImpl; | |
487 | |
488 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | |
489 if (!codec) | |
490 return false; | |
491 | |
492 if (avcodec_open2(codec_context_, codec, NULL) < 0) | |
493 return false; | |
494 | |
495 av_frame_ = avcodec_alloc_frame(); | |
496 return true; | |
497 } | |
498 | |
480 void FFmpegVideoDecoder::ReleaseFFmpegResources() { | 499 void FFmpegVideoDecoder::ReleaseFFmpegResources() { |
481 if (codec_context_) { | 500 if (codec_context_) { |
482 av_free(codec_context_->extradata); | 501 av_free(codec_context_->extradata); |
483 avcodec_close(codec_context_); | 502 avcodec_close(codec_context_); |
484 av_free(codec_context_); | 503 av_free(codec_context_); |
485 codec_context_ = NULL; | 504 codec_context_ = NULL; |
486 } | 505 } |
487 if (av_frame_) { | 506 if (av_frame_) { |
488 av_free(av_frame_); | 507 av_free(av_frame_); |
489 av_frame_ = NULL; | 508 av_frame_ = NULL; |
490 } | 509 } |
491 } | 510 } |
492 | 511 |
512 bool FFmpegVideoDecoder::HandleConfigChange() { | |
513 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config(); | |
514 | |
515 if (!config.IsValidConfig()) | |
516 return false; | |
517 | |
518 ReleaseFFmpegResources(); | |
519 if (!AllocateFFmpegResources(config)) | |
520 return false; | |
521 | |
522 natural_size_ = config.natural_size(); | |
523 frame_rate_numerator_ = config.frame_rate_numerator(); | |
524 frame_rate_denominator_ = config.frame_rate_denominator(); | |
525 return true; | |
526 } | |
527 | |
493 } // namespace media | 528 } // namespace media |
OLD | NEW |