Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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.h" | |
| 9 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 10 #include "base/task.h" | |
| 11 #include "media/base/demuxer_stream.h" | 9 #include "media/base/demuxer_stream.h" |
| 12 #include "media/base/filter_host.h" | 10 #include "media/base/filter_host.h" |
| 13 #include "media/base/limits.h" | 11 #include "media/base/limits.h" |
| 12 #include "media/base/video_decoder_config.h" | |
| 14 #include "media/base/video_frame.h" | 13 #include "media/base/video_frame.h" |
| 15 #include "media/ffmpeg/ffmpeg_common.h" | 14 #include "media/ffmpeg/ffmpeg_common.h" |
| 16 #include "media/video/ffmpeg_video_decode_engine.h" | 15 #include "media/video/ffmpeg_video_decode_engine.h" |
| 17 | 16 |
| 18 namespace media { | 17 namespace media { |
| 19 | 18 |
| 20 FFmpegVideoDecoder::FFmpegVideoDecoder(MessageLoop* message_loop) | 19 FFmpegVideoDecoder::FFmpegVideoDecoder(MessageLoop* message_loop) |
| 21 : message_loop_(message_loop), | 20 : message_loop_(message_loop), |
| 22 state_(kUnInitialized), | 21 state_(kUninitialized), |
| 23 decode_engine_(new FFmpegVideoDecodeEngine()) { | 22 decode_engine_(new FFmpegVideoDecodeEngine()) { |
| 24 } | 23 } |
| 25 | 24 |
| 26 FFmpegVideoDecoder::~FFmpegVideoDecoder() {} | 25 FFmpegVideoDecoder::~FFmpegVideoDecoder() {} |
| 27 | 26 |
| 28 void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream, | 27 void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream, |
| 29 const base::Closure& callback, | 28 const base::Closure& callback, |
| 30 const StatisticsCallback& stats_callback) { | 29 const StatisticsCallback& stats_callback) { |
| 31 if (MessageLoop::current() != message_loop_) { | 30 if (MessageLoop::current() != message_loop_) { |
| 32 message_loop_->PostTask( | 31 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 33 FROM_HERE, | 32 &FFmpegVideoDecoder::Initialize, this, |
| 34 base::Bind(&FFmpegVideoDecoder::Initialize, this, | 33 make_scoped_refptr(demuxer_stream), callback, stats_callback)); |
| 35 make_scoped_refptr(demuxer_stream), | |
| 36 callback, stats_callback)); | |
| 37 return; | 34 return; |
| 38 } | 35 } |
| 39 | 36 |
| 40 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 41 DCHECK(!demuxer_stream_); | 37 DCHECK(!demuxer_stream_); |
| 42 DCHECK(initialize_callback_.is_null()); | |
| 43 | 38 |
| 44 if (!demuxer_stream) { | 39 if (!demuxer_stream) { |
| 45 host()->SetError(PIPELINE_ERROR_DECODE); | 40 host()->SetError(PIPELINE_ERROR_DECODE); |
| 46 callback.Run(); | 41 callback.Run(); |
| 47 return; | 42 return; |
| 48 } | 43 } |
| 49 | 44 |
| 50 demuxer_stream_ = demuxer_stream; | 45 demuxer_stream_ = demuxer_stream; |
| 51 initialize_callback_ = callback; | |
| 52 statistics_callback_ = stats_callback; | 46 statistics_callback_ = stats_callback; |
| 53 | 47 |
| 54 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); | 48 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); |
| 55 | 49 |
| 56 pts_stream_.Initialize(GetFrameDuration(config)); | 50 // TODO(scherkus): this check should go in PipelineImpl prior to creating |
| 51 // decoder objects. | |
| 52 if (!config.IsValidConfig()) { | |
| 53 DLOG(ERROR) << "Invalid video stream -" | |
| 54 << " codec: " << config.codec() | |
| 55 << " format: " << config.format() | |
| 56 << " coded size: [" << config.coded_size().width() | |
| 57 << "," << config.coded_size().height() << "]" | |
| 58 << " visible rect: [" << config.visible_rect().x() | |
| 59 << "," << config.visible_rect().y() | |
| 60 << "," << config.visible_rect().width() | |
| 61 << "," << config.visible_rect().height() << "]" | |
| 62 << " natural size: [" << config.natural_size().width() | |
| 63 << "," << config.natural_size().height() << "]" | |
| 64 << " frame rate: " << config.frame_rate_numerator() | |
| 65 << "/" << config.frame_rate_denominator() | |
| 66 << " aspect ratio: " << config.aspect_ratio_numerator() | |
| 67 << "/" << config.aspect_ratio_denominator(); | |
| 57 | 68 |
| 58 natural_size_ = config.natural_size(); | 69 host()->SetError(PIPELINE_ERROR_DECODE); |
| 59 if (natural_size_.width() > Limits::kMaxDimension || | 70 callback.Run(); |
| 60 natural_size_.height() > Limits::kMaxDimension || | |
| 61 natural_size_.GetArea() > Limits::kMaxCanvas) { | |
| 62 OnInitializeComplete(false); | |
| 63 return; | 71 return; |
| 64 } | 72 } |
| 65 | 73 |
| 66 state_ = kInitializing; | 74 pts_stream_.Initialize(GetFrameDuration(config)); |
| 67 decode_engine_->Initialize(this, config); | 75 natural_size_ = config.natural_size(); |
| 68 } | |
| 69 | 76 |
| 70 void FFmpegVideoDecoder::OnInitializeComplete(bool success) { | 77 if (!decode_engine_->Initialize(config)) { |
| 71 DCHECK_EQ(MessageLoop::current(), message_loop_); | 78 host()->SetError(PIPELINE_ERROR_DECODE); |
| 72 DCHECK(!initialize_callback_.is_null()); | 79 callback.Run(); |
| 80 return; | |
| 81 } | |
| 73 | 82 |
| 74 if (success) { | 83 state_ = kNormal; |
| 75 state_ = kNormal; | 84 callback.Run(); |
| 76 } else { | |
| 77 host()->SetError(PIPELINE_ERROR_DECODE); | |
| 78 } | |
| 79 ResetAndRunCB(&initialize_callback_); | |
| 80 } | 85 } |
| 81 | 86 |
| 82 void FFmpegVideoDecoder::Stop(const base::Closure& callback) { | 87 void FFmpegVideoDecoder::Stop(const base::Closure& callback) { |
| 83 if (MessageLoop::current() != message_loop_) { | 88 if (MessageLoop::current() != message_loop_) { |
| 84 message_loop_->PostTask(FROM_HERE, base::Bind( | 89 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 85 &FFmpegVideoDecoder::Stop, this, callback)); | 90 &FFmpegVideoDecoder::Stop, this, callback)); |
| 86 return; | 91 return; |
| 87 } | 92 } |
| 88 | 93 |
| 89 DCHECK_EQ(MessageLoop::current(), message_loop_); | 94 decode_engine_->Uninitialize(); |
| 90 DCHECK(uninitialize_callback_.is_null()); | 95 state_ = kUninitialized; |
| 91 | 96 callback.Run(); |
| 92 uninitialize_callback_ = callback; | |
| 93 if (state_ != kUnInitialized) | |
| 94 decode_engine_->Uninitialize(); | |
| 95 else | |
| 96 OnUninitializeComplete(); | |
| 97 } | 97 } |
| 98 | 98 |
| 99 void FFmpegVideoDecoder::OnUninitializeComplete() { | 99 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
| 100 DCHECK_EQ(MessageLoop::current(), message_loop_); | 100 if (MessageLoop::current() != message_loop_) { |
| 101 DCHECK(!uninitialize_callback_.is_null()); | 101 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 102 &FFmpegVideoDecoder::Seek, this, time, cb)); | |
| 103 return; | |
| 104 } | |
| 102 | 105 |
| 103 state_ = kStopped; | 106 pts_stream_.Seek(time); |
| 104 | 107 cb.Run(PIPELINE_OK); |
| 105 ResetAndRunCB(&uninitialize_callback_); | |
| 106 } | 108 } |
| 107 | 109 |
| 108 void FFmpegVideoDecoder::Pause(const base::Closure& callback) { | 110 void FFmpegVideoDecoder::Pause(const base::Closure& callback) { |
| 109 if (MessageLoop::current() != message_loop_) { | 111 if (MessageLoop::current() != message_loop_) { |
| 110 message_loop_->PostTask(FROM_HERE, base::Bind( | 112 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 111 &FFmpegVideoDecoder::Pause, this, callback)); | 113 &FFmpegVideoDecoder::Pause, this, callback)); |
| 112 return; | 114 return; |
| 113 } | 115 } |
| 114 | 116 |
| 115 state_ = kPausing; | |
| 116 callback.Run(); | 117 callback.Run(); |
| 117 } | 118 } |
| 118 | 119 |
| 119 void FFmpegVideoDecoder::Flush(const base::Closure& callback) { | 120 void FFmpegVideoDecoder::Flush(const base::Closure& callback) { |
| 120 if (MessageLoop::current() != message_loop_) { | 121 if (MessageLoop::current() != message_loop_) { |
| 121 message_loop_->PostTask(FROM_HERE, base::Bind( | 122 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 122 &FFmpegVideoDecoder::Flush, this, callback)); | 123 &FFmpegVideoDecoder::Flush, this, callback)); |
| 123 return; | 124 return; |
| 124 } | 125 } |
| 125 | 126 |
| 126 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 127 DCHECK(flush_callback_.is_null()); | |
| 128 | |
| 129 state_ = kFlushing; | |
| 130 | |
| 131 FlushBuffers(); | |
| 132 | |
| 133 flush_callback_ = callback; | |
| 134 | |
| 135 decode_engine_->Flush(); | 127 decode_engine_->Flush(); |
| 128 pts_stream_.Flush(); | |
| 129 state_ = kNormal; | |
| 130 callback.Run(); | |
| 136 } | 131 } |
| 137 | 132 |
| 138 void FFmpegVideoDecoder::OnFlushComplete() { | 133 void FFmpegVideoDecoder::Read(const ReadCB& callback) { |
| 139 DCHECK_EQ(MessageLoop::current(), message_loop_); | 134 // Method must immediately return. |
| 140 DCHECK(!flush_callback_.is_null()); | 135 message_loop_->PostTask(FROM_HERE, base::Bind( |
|
scherkus (not reviewing)
2011/11/03 04:55:59
FYI forced post task since VideoRendererBase::Fram
Ami GONE FROM CHROMIUM
2011/11/03 16:40:00
This is a better comment than what you have in the
scherkus (not reviewing)
2011/11/03 20:34:38
Done.
| |
| 141 | 136 &FFmpegVideoDecoder::DoRead, this, callback)); |
| 142 // Everything in the presentation time queue is invalid, clear the queue. | |
| 143 pts_stream_.Flush(); | |
| 144 | |
| 145 // Mark flush operation had been done. | |
| 146 state_ = kNormal; | |
| 147 | |
| 148 ResetAndRunCB(&flush_callback_); | |
| 149 } | 137 } |
| 150 | 138 |
| 151 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { | 139 gfx::Size FFmpegVideoDecoder::natural_size() { |
|
Ami GONE FROM CHROMIUM
2011/11/03 16:40:00
return const&?
scherkus (not reviewing)
2011/11/03 20:34:38
make ALL the return types const&!
| |
| 152 if (MessageLoop::current() != message_loop_) { | 140 return natural_size_; |
| 153 message_loop_->PostTask(FROM_HERE, | 141 } |
| 154 base::Bind(&FFmpegVideoDecoder::Seek, this, | 142 |
| 155 time, cb)); | 143 void FFmpegVideoDecoder::DoRead(const ReadCB& callback) { |
| 156 return; | 144 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 145 CHECK(!callback.is_null()); | |
| 146 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
| 147 | |
| 148 // This can happen during shutdown after Stop() has been called. | |
| 149 if (state_ == kUninitialized) { | |
| 150 return; | |
| 157 } | 151 } |
| 158 | 152 |
| 159 DCHECK_EQ(MessageLoop::current(), message_loop_); | 153 // Return empty frames if decoding has finished. |
| 160 DCHECK(seek_cb_.is_null()); | 154 if (state_ == kDecodeFinished) { |
| 155 callback.Run(VideoFrame::CreateEmptyFrame()); | |
| 156 return; | |
| 157 } | |
| 161 | 158 |
| 162 pts_stream_.Seek(time); | 159 read_cb_ = callback; |
| 163 seek_cb_ = cb; | 160 ReadFromDemuxerStream(); |
| 164 decode_engine_->Seek(); | |
| 165 } | 161 } |
| 166 | 162 |
| 167 void FFmpegVideoDecoder::OnSeekComplete() { | |
| 168 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 169 DCHECK(!seek_cb_.is_null()); | |
| 170 | 163 |
| 171 ResetAndRunCB(&seek_cb_, PIPELINE_OK); | 164 void FFmpegVideoDecoder::ReadFromDemuxerStream() { |
| 165 DCHECK_NE(state_, kUninitialized); | |
| 166 DCHECK_NE(state_, kDecodeFinished); | |
| 167 DCHECK(!read_cb_.is_null()); | |
| 168 | |
| 169 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::DecodeBuffer, this)); | |
| 172 } | 170 } |
| 173 | 171 |
| 174 void FFmpegVideoDecoder::OnError() { | 172 void FFmpegVideoDecoder::DecodeBuffer(const scoped_refptr<Buffer>& buffer) { |
| 175 VideoFrameReady(NULL); | 173 message_loop_->PostTask(FROM_HERE, base::Bind( |
|
scherkus (not reviewing)
2011/11/03 04:55:59
FYI forced post task since FFmpegDemuxerStream::Re
scherkus (not reviewing)
2011/11/03 20:34:38
Comment added.
| |
| 174 &FFmpegVideoDecoder::DoDecodeBuffer, this, buffer)); | |
| 176 } | 175 } |
| 177 | 176 |
| 178 void FFmpegVideoDecoder::OnReadComplete(const scoped_refptr<Buffer>& buffer) { | 177 void FFmpegVideoDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& buffer) { |
| 179 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 180 &FFmpegVideoDecoder::OnReadCompleteTask, this, buffer)); | |
| 181 } | |
| 182 | |
| 183 void FFmpegVideoDecoder::OnReadCompleteTask( | |
| 184 const scoped_refptr<Buffer>& buffer) { | |
| 185 DCHECK_EQ(MessageLoop::current(), message_loop_); | 178 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 186 DCHECK_NE(state_, kStopped); // because of Flush() before Stop(). | 179 DCHECK_NE(state_, kUninitialized); |
| 180 DCHECK_NE(state_, kDecodeFinished); | |
| 181 DCHECK(!read_cb_.is_null()); | |
| 187 | 182 |
| 188 // During decode, because reads are issued asynchronously, it is possible to | 183 // During decode, because reads are issued asynchronously, it is possible to |
| 189 // receive multiple end of stream buffers since each read is acked. When the | 184 // receive multiple end of stream buffers since each read is acked. When the |
| 190 // first end of stream buffer is read, FFmpeg may still have frames queued | 185 // first end of stream buffer is read, FFmpeg may still have frames queued |
| 191 // up in the decoder so we need to go through the decode loop until it stops | 186 // up in the decoder so we need to go through the decode loop until it stops |
| 192 // giving sensible data. After that, the decoder should output empty | 187 // giving sensible data. After that, the decoder should output empty |
| 193 // frames. There are three states the decoder can be in: | 188 // frames. There are three states the decoder can be in: |
| 194 // | 189 // |
| 195 // kNormal: This is the starting state. Buffers are decoded. Decode errors | 190 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
| 196 // are discarded. | 191 // are discarded. |
| 197 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 | 192 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 |
| 198 // until no more data is returned to flush out remaining | 193 // until no more data is returned to flush out remaining |
| 199 // frames. The input buffer is ignored at this point. | 194 // frames. The input buffer is ignored at this point. |
| 200 // kDecodeFinished: All calls return empty frames. | 195 // kDecodeFinished: All calls return empty frames. |
| 201 // | 196 // |
| 202 // These are the possible state transitions. | 197 // These are the possible state transitions. |
| 203 // | 198 // |
| 204 // kNormal -> kFlushCodec: | 199 // kNormal -> kFlushCodec: |
| 205 // When buffer->IsEndOfStream() is first true. | 200 // When buffer->IsEndOfStream() is first true. |
| 206 // kNormal -> kDecodeFinished: | 201 // kNormal -> kDecodeFinished: |
| 207 // A catastrophic failure occurs, and decoding needs to stop. | 202 // A decoding error occurs and decoding needs to stop. |
| 208 // kFlushCodec -> kDecodeFinished: | 203 // kFlushCodec -> kDecodeFinished: |
| 209 // When avcodec_decode_video2() returns 0 data or errors out. | 204 // When avcodec_decode_video2() returns 0 data or errors out. |
| 210 // (any state) -> kNormal: | 205 // (any state) -> kNormal: |
| 211 // Any time buffer->IsDiscontinuous() is true. | 206 // Any time Flush() is called. |
| 212 | 207 |
| 213 // Transition to kFlushCodec on the first end of stream buffer. | 208 // Transition to kFlushCodec on the first end of stream buffer. |
| 214 if (state_ == kNormal && buffer->IsEndOfStream()) { | 209 if (state_ == kNormal && buffer->IsEndOfStream()) { |
| 215 state_ = kFlushCodec; | 210 state_ = kFlushCodec; |
| 216 } | 211 } |
| 217 | 212 |
| 218 // Push all incoming timestamps into the priority queue as long as we have | 213 // Push all incoming timestamps into the priority queue as long as we have |
| 219 // not yet received an end of stream buffer. It is important that this line | 214 // not yet received an end of stream buffer. It is important that this line |
| 220 // stay below the state transition into kFlushCodec done above. | 215 // stay below the state transition into kFlushCodec done above. |
| 221 if (state_ == kNormal) { | 216 if (state_ == kNormal) { |
| 222 pts_stream_.EnqueuePts(buffer.get()); | 217 pts_stream_.EnqueuePts(buffer.get()); |
| 223 } | 218 } |
| 224 | 219 |
| 225 // Otherwise, attempt to decode a single frame. | 220 scoped_refptr<VideoFrame> video_frame; |
| 226 decode_engine_->ConsumeVideoSample(buffer); | 221 if (!decode_engine_->Decode(buffer, &video_frame)) { |
| 227 } | 222 state_ = kDecodeFinished; |
| 228 | 223 DeliverFrame(VideoFrame::CreateEmptyFrame()); |
| 229 void FFmpegVideoDecoder::ProduceVideoFrame( | 224 host()->SetError(PIPELINE_ERROR_DECODE); |
| 230 scoped_refptr<VideoFrame> video_frame) { | |
| 231 if (MessageLoop::current() != message_loop_) { | |
| 232 if (state_ != kStopped) { | |
| 233 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 234 &FFmpegVideoDecoder::ProduceVideoFrame, this, video_frame)); | |
| 235 } | |
| 236 return; | 225 return; |
| 237 } | 226 } |
| 238 | 227 |
| 239 DCHECK_EQ(MessageLoop::current(), message_loop_); | 228 // Any successful decode counts! |
| 240 | 229 if (buffer->GetDataSize()) { |
| 241 // Synchronized flushing before stop should prevent this. | 230 PipelineStatistics statistics; |
| 242 DCHECK_NE(state_, kStopped); | 231 statistics.video_bytes_decoded = buffer->GetDataSize(); |
| 243 | 232 statistics_callback_.Run(statistics); |
| 244 // If the decoding is finished, we just always return empty frames. | |
| 245 if (state_ == kDecodeFinished) { | |
| 246 // Signal VideoRenderer the end of the stream event. | |
| 247 VideoFrameReady(VideoFrame::CreateEmptyFrame()); | |
| 248 | |
| 249 // Fall through, because we still need to keep record of this frame. | |
| 250 } | 233 } |
| 251 | 234 |
| 252 // Notify decode engine the available of new frame. | 235 // If we didn't get a frame then we've either completely finished decoding or |
| 253 decode_engine_->ProduceVideoFrame(video_frame); | 236 // we need more data. |
| 254 } | 237 if (!video_frame) { |
| 255 | 238 if (state_ == kFlushCodec) { |
| 256 void FFmpegVideoDecoder::ConsumeVideoFrame( | 239 state_ = kDecodeFinished; |
| 257 scoped_refptr<VideoFrame> video_frame, | 240 DeliverFrame(VideoFrame::CreateEmptyFrame()); |
| 258 const PipelineStatistics& statistics) { | |
| 259 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 260 DCHECK_NE(state_, kStopped); | |
| 261 | |
| 262 statistics_callback_.Run(statistics); | |
| 263 | |
| 264 if (video_frame.get()) { | |
| 265 if (kPausing == state_ || kFlushing == state_) { | |
| 266 frame_queue_flushed_.push_back(video_frame); | |
| 267 if (kFlushing == state_) | |
| 268 FlushBuffers(); | |
| 269 return; | 241 return; |
| 270 } | 242 } |
| 271 | 243 |
| 272 // If we actually got data back, enqueue a frame. | 244 ReadFromDemuxerStream(); |
| 273 pts_stream_.UpdatePtsAndDuration(video_frame.get()); | 245 return; |
| 246 } | |
| 274 | 247 |
| 275 video_frame->SetTimestamp(pts_stream_.current_pts()); | 248 // If we got a frame make sure its timestamp is correct before sending it off. |
| 276 video_frame->SetDuration(pts_stream_.current_duration()); | 249 pts_stream_.UpdatePtsAndDuration(video_frame.get()); |
| 250 video_frame->SetTimestamp(pts_stream_.current_pts()); | |
| 251 video_frame->SetDuration(pts_stream_.current_duration()); | |
| 277 | 252 |
| 278 VideoFrameReady(video_frame); | 253 DeliverFrame(video_frame); |
| 279 } else { | |
| 280 // When in kFlushCodec, any errored decode, or a 0-lengthed frame, | |
| 281 // is taken as a signal to stop decoding. | |
| 282 if (state_ == kFlushCodec) { | |
| 283 state_ = kDecodeFinished; | |
| 284 | |
| 285 // Signal VideoRenderer the end of the stream event. | |
| 286 VideoFrameReady(VideoFrame::CreateEmptyFrame()); | |
| 287 } | |
| 288 } | |
| 289 } | 254 } |
| 290 | 255 |
| 291 void FFmpegVideoDecoder::ProduceVideoSample( | 256 void FFmpegVideoDecoder::DeliverFrame( |
| 292 scoped_refptr<Buffer> buffer) { | 257 const scoped_refptr<VideoFrame>& video_frame) { |
| 293 DCHECK_EQ(MessageLoop::current(), message_loop_); | 258 // Reset the callback before running to protect against reentrancy. |
| 294 DCHECK_NE(state_, kStopped); | 259 ReadCB read_cb = read_cb_; |
| 295 | 260 read_cb_.Reset(); |
| 296 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::OnReadComplete, this)); | 261 read_cb.Run(video_frame); |
| 297 } | |
| 298 | |
| 299 gfx::Size FFmpegVideoDecoder::natural_size() { | |
| 300 return natural_size_; | |
| 301 } | |
| 302 | |
| 303 void FFmpegVideoDecoder::FlushBuffers() { | |
| 304 while (!frame_queue_flushed_.empty()) { | |
| 305 scoped_refptr<VideoFrame> video_frame; | |
| 306 video_frame = frame_queue_flushed_.front(); | |
| 307 frame_queue_flushed_.pop_front(); | |
| 308 | |
| 309 // Return frames back to the decode engine. | |
| 310 decode_engine_->ProduceVideoFrame(video_frame); | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 void FFmpegVideoDecoder::SetVideoDecodeEngineForTest( | |
| 315 VideoDecodeEngine* engine) { | |
| 316 decode_engine_.reset(engine); | |
| 317 } | 262 } |
| 318 | 263 |
| 319 } // namespace media | 264 } // namespace media |
| OLD | NEW |