Optimize phishing page term feature extraction.
[chromium-blink-merge.git] / media / filters / ffmpeg_video_decoder.cc
blob381c566dffc45d4729e7b295d65e5416e2f012bc
1 // Copyright (c) 2011 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.
5 #include "media/filters/ffmpeg_video_decoder.h"
7 #include <deque>
9 #include "base/bind.h"
10 #include "base/task.h"
11 #include "media/base/callback.h"
12 #include "media/base/filters.h"
13 #include "media/base/filter_host.h"
14 #include "media/base/limits.h"
15 #include "media/base/video_frame.h"
16 #include "media/ffmpeg/ffmpeg_common.h"
17 #include "media/video/ffmpeg_video_decode_engine.h"
18 #include "media/video/video_decode_context.h"
20 namespace media {
22 FFmpegVideoDecoder::FFmpegVideoDecoder(MessageLoop* message_loop,
23 VideoDecodeContext* decode_context)
24 : message_loop_(message_loop),
25 state_(kUnInitialized),
26 decode_engine_(new FFmpegVideoDecodeEngine()),
27 decode_context_(decode_context) {
28 memset(&info_, 0, sizeof(info_));
31 FFmpegVideoDecoder::~FFmpegVideoDecoder() {}
33 void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
34 FilterCallback* callback,
35 StatisticsCallback* stats_callback) {
36 if (MessageLoop::current() != message_loop_) {
37 message_loop_->PostTask(
38 FROM_HERE,
39 NewRunnableMethod(this,
40 &FFmpegVideoDecoder::Initialize,
41 make_scoped_refptr(demuxer_stream),
42 callback, stats_callback));
43 return;
46 DCHECK_EQ(MessageLoop::current(), message_loop_);
47 DCHECK(!demuxer_stream_);
48 DCHECK(!initialize_callback_.get());
50 if (!demuxer_stream) {
51 host()->SetError(PIPELINE_ERROR_DECODE);
52 callback->Run();
53 delete callback;
54 delete stats_callback;
55 return;
58 demuxer_stream_ = demuxer_stream;
59 initialize_callback_.reset(callback);
60 statistics_callback_.reset(stats_callback);
62 AVStream* av_stream = demuxer_stream->GetAVStream();
63 if (!av_stream) {
64 VideoCodecInfo info = {0};
65 OnInitializeComplete(info);
66 return;
69 pts_stream_.Initialize(GetFrameDuration(av_stream));
71 int width = av_stream->codec->coded_width;
72 int height = av_stream->codec->coded_height;
74 int surface_width = GetSurfaceWidth(av_stream);
75 int surface_height = GetSurfaceHeight(av_stream);
77 if (surface_width > Limits::kMaxDimension ||
78 surface_height > Limits::kMaxDimension ||
79 (surface_width * surface_height) > Limits::kMaxCanvas) {
80 VideoCodecInfo info = {0};
81 OnInitializeComplete(info);
82 return;
85 VideoDecoderConfig config(CodecIDToVideoCodec(av_stream->codec->codec_id),
86 width, height,
87 surface_width, surface_height,
88 av_stream->r_frame_rate.num,
89 av_stream->r_frame_rate.den,
90 av_stream->codec->extradata,
91 av_stream->codec->extradata_size);
92 state_ = kInitializing;
93 decode_engine_->Initialize(message_loop_, this, NULL, config);
96 void FFmpegVideoDecoder::OnInitializeComplete(const VideoCodecInfo& info) {
97 DCHECK_EQ(MessageLoop::current(), message_loop_);
98 DCHECK(initialize_callback_.get());
100 info_ = info;
101 AutoCallbackRunner done_runner(initialize_callback_.release());
103 if (info.success) {
104 state_ = kNormal;
105 } else {
106 host()->SetError(PIPELINE_ERROR_DECODE);
110 void FFmpegVideoDecoder::Stop(FilterCallback* callback) {
111 if (MessageLoop::current() != message_loop_) {
112 message_loop_->PostTask(FROM_HERE,
113 NewRunnableMethod(this,
114 &FFmpegVideoDecoder::Stop,
115 callback));
116 return;
119 DCHECK_EQ(MessageLoop::current(), message_loop_);
120 DCHECK(!uninitialize_callback_.get());
122 uninitialize_callback_.reset(callback);
123 if (state_ != kUnInitialized)
124 decode_engine_->Uninitialize();
125 else
126 OnUninitializeComplete();
129 void FFmpegVideoDecoder::OnUninitializeComplete() {
130 DCHECK_EQ(MessageLoop::current(), message_loop_);
131 DCHECK(uninitialize_callback_.get());
133 AutoCallbackRunner done_runner(uninitialize_callback_.release());
134 state_ = kStopped;
136 // TODO(jiesun): Destroy the decoder context.
139 void FFmpegVideoDecoder::Pause(FilterCallback* callback) {
140 if (MessageLoop::current() != message_loop_) {
141 message_loop_->PostTask(FROM_HERE,
142 NewRunnableMethod(this,
143 &FFmpegVideoDecoder::Pause,
144 callback));
145 return;
148 AutoCallbackRunner done_runner(callback);
149 state_ = kPausing;
152 void FFmpegVideoDecoder::Flush(FilterCallback* callback) {
153 if (MessageLoop::current() != message_loop_) {
154 message_loop_->PostTask(FROM_HERE,
155 NewRunnableMethod(this,
156 &FFmpegVideoDecoder::Flush,
157 callback));
158 return;
161 DCHECK_EQ(MessageLoop::current(), message_loop_);
162 DCHECK(!flush_callback_.get());
164 state_ = kFlushing;
166 FlushBuffers();
168 flush_callback_.reset(callback);
170 decode_engine_->Flush();
173 void FFmpegVideoDecoder::OnFlushComplete() {
174 DCHECK_EQ(MessageLoop::current(), message_loop_);
175 DCHECK(flush_callback_.get());
177 AutoCallbackRunner done_runner(flush_callback_.release());
179 // Everything in the presentation time queue is invalid, clear the queue.
180 pts_stream_.Flush();
182 // Mark flush operation had been done.
183 state_ = kNormal;
186 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
187 if (MessageLoop::current() != message_loop_) {
188 message_loop_->PostTask(FROM_HERE,
189 NewRunnableMethod(this, &FFmpegVideoDecoder::Seek,
190 time, cb));
191 return;
194 DCHECK_EQ(MessageLoop::current(), message_loop_);
195 DCHECK(seek_cb_.is_null());
197 pts_stream_.Seek(time);
198 seek_cb_ = cb;
199 decode_engine_->Seek();
202 void FFmpegVideoDecoder::OnSeekComplete() {
203 DCHECK_EQ(MessageLoop::current(), message_loop_);
204 DCHECK(!seek_cb_.is_null());
206 ResetAndRunCB(&seek_cb_, PIPELINE_OK);
209 void FFmpegVideoDecoder::OnError() {
210 VideoFrameReady(NULL);
213 void FFmpegVideoDecoder::OnReadComplete(Buffer* buffer_in) {
214 scoped_refptr<Buffer> buffer(buffer_in);
215 message_loop_->PostTask(
216 FROM_HERE,
217 NewRunnableMethod(this,
218 &FFmpegVideoDecoder::OnReadCompleteTask,
219 buffer));
222 void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) {
223 DCHECK_EQ(MessageLoop::current(), message_loop_);
224 DCHECK_NE(state_, kStopped); // because of Flush() before Stop().
226 // During decode, because reads are issued asynchronously, it is possible to
227 // receive multiple end of stream buffers since each read is acked. When the
228 // first end of stream buffer is read, FFmpeg may still have frames queued
229 // up in the decoder so we need to go through the decode loop until it stops
230 // giving sensible data. After that, the decoder should output empty
231 // frames. There are three states the decoder can be in:
233 // kNormal: This is the starting state. Buffers are decoded. Decode errors
234 // are discarded.
235 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2
236 // until no more data is returned to flush out remaining
237 // frames. The input buffer is ignored at this point.
238 // kDecodeFinished: All calls return empty frames.
240 // These are the possible state transitions.
242 // kNormal -> kFlushCodec:
243 // When buffer->IsEndOfStream() is first true.
244 // kNormal -> kDecodeFinished:
245 // A catastrophic failure occurs, and decoding needs to stop.
246 // kFlushCodec -> kDecodeFinished:
247 // When avcodec_decode_video2() returns 0 data or errors out.
248 // (any state) -> kNormal:
249 // Any time buffer->IsDiscontinuous() is true.
251 // Transition to kFlushCodec on the first end of stream buffer.
252 if (state_ == kNormal && buffer->IsEndOfStream()) {
253 state_ = kFlushCodec;
256 // Push all incoming timestamps into the priority queue as long as we have
257 // not yet received an end of stream buffer. It is important that this line
258 // stay below the state transition into kFlushCodec done above.
260 // TODO(ajwong): This push logic, along with the pop logic below needs to
261 // be reevaluated to correctly handle decode errors.
262 if (state_ == kNormal) {
263 pts_stream_.EnqueuePts(buffer.get());
266 // Otherwise, attempt to decode a single frame.
267 decode_engine_->ConsumeVideoSample(buffer);
270 void FFmpegVideoDecoder::ProduceVideoFrame(
271 scoped_refptr<VideoFrame> video_frame) {
272 if (MessageLoop::current() != message_loop_) {
273 message_loop_->PostTask(
274 FROM_HERE,
275 NewRunnableMethod(this,
276 &FFmpegVideoDecoder::ProduceVideoFrame, video_frame));
277 return;
280 DCHECK_EQ(MessageLoop::current(), message_loop_);
282 // Synchronized flushing before stop should prevent this.
283 DCHECK_NE(state_, kStopped);
285 // If the decoding is finished, we just always return empty frames.
286 if (state_ == kDecodeFinished) {
287 // Signal VideoRenderer the end of the stream event.
288 VideoFrameReady(VideoFrame::CreateEmptyFrame());
290 // Fall through, because we still need to keep record of this frame.
293 // Notify decode engine the available of new frame.
294 decode_engine_->ProduceVideoFrame(video_frame);
297 void FFmpegVideoDecoder::ConsumeVideoFrame(
298 scoped_refptr<VideoFrame> video_frame,
299 const PipelineStatistics& statistics) {
300 DCHECK_EQ(MessageLoop::current(), message_loop_);
301 DCHECK_NE(state_, kStopped);
303 statistics_callback_->Run(statistics);
305 if (video_frame.get()) {
306 if (kPausing == state_ || kFlushing == state_) {
307 frame_queue_flushed_.push_back(video_frame);
308 if (kFlushing == state_)
309 FlushBuffers();
310 return;
313 // If we actually got data back, enqueue a frame.
314 pts_stream_.UpdatePtsAndDuration(video_frame.get());
316 video_frame->SetTimestamp(pts_stream_.current_pts());
317 video_frame->SetDuration(pts_stream_.current_duration());
319 VideoFrameReady(video_frame);
320 } else {
321 // When in kFlushCodec, any errored decode, or a 0-lengthed frame,
322 // is taken as a signal to stop decoding.
323 if (state_ == kFlushCodec) {
324 state_ = kDecodeFinished;
326 // Signal VideoRenderer the end of the stream event.
327 VideoFrameReady(VideoFrame::CreateEmptyFrame());
332 void FFmpegVideoDecoder::ProduceVideoSample(
333 scoped_refptr<Buffer> buffer) {
334 DCHECK_EQ(MessageLoop::current(), message_loop_);
335 DCHECK_NE(state_, kStopped);
337 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::OnReadComplete,
338 this));
341 int FFmpegVideoDecoder::width() {
342 DCHECK(info_.success);
343 return info_.surface_width;
346 int FFmpegVideoDecoder::height() {
347 DCHECK(info_.success);
348 return info_.surface_height;
351 void FFmpegVideoDecoder::FlushBuffers() {
352 while (!frame_queue_flushed_.empty()) {
353 scoped_refptr<VideoFrame> video_frame;
354 video_frame = frame_queue_flushed_.front();
355 frame_queue_flushed_.pop_front();
357 // Return frames back to the decode engine.
358 decode_engine_->ProduceVideoFrame(video_frame);
362 void FFmpegVideoDecoder::SetVideoDecodeEngineForTest(
363 VideoDecodeEngine* engine) {
364 decode_engine_.reset(engine);
367 } // namespace media