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_); | 37 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 41 DCHECK(!demuxer_stream_); | 38 DCHECK(!demuxer_stream_); |
| 42 DCHECK(initialize_callback_.is_null()); | |
| 43 | 39 |
| 44 if (!demuxer_stream) { | 40 if (!demuxer_stream) { |
| 45 host()->SetError(PIPELINE_ERROR_DECODE); | 41 host()->SetError(PIPELINE_ERROR_DECODE); |
| 46 callback.Run(); | 42 callback.Run(); |
| 47 return; | 43 return; |
| 48 } | 44 } |
| 49 | 45 |
| 50 demuxer_stream_ = demuxer_stream; | 46 demuxer_stream_ = demuxer_stream; |
| 51 initialize_callback_ = callback; | |
| 52 statistics_callback_ = stats_callback; | 47 statistics_callback_ = stats_callback; |
| 53 | 48 |
| 54 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); | 49 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); |
| 55 | 50 |
| 56 pts_stream_.Initialize(GetFrameDuration(config)); | 51 pts_stream_.Initialize(GetFrameDuration(config)); |
| 57 | 52 |
| 58 natural_size_ = config.natural_size(); | 53 natural_size_ = config.natural_size(); |
| 59 if (natural_size_.width() > Limits::kMaxDimension || | 54 if (natural_size_.width() > Limits::kMaxDimension || |
| 60 natural_size_.height() > Limits::kMaxDimension || | 55 natural_size_.height() > Limits::kMaxDimension || |
| 61 natural_size_.GetArea() > Limits::kMaxCanvas) { | 56 natural_size_.GetArea() > Limits::kMaxCanvas) { |
| 62 OnInitializeComplete(false); | 57 host()->SetError(PIPELINE_ERROR_DECODE); |
| 58 callback.Run(); | |
| 63 return; | 59 return; |
| 64 } | 60 } |
| 65 | 61 |
| 66 state_ = kInitializing; | 62 if (!decode_engine_->Initialize(config)) { |
| 67 decode_engine_->Initialize(this, config); | 63 host()->SetError(PIPELINE_ERROR_DECODE); |
| 68 } | 64 callback.Run(); |
| 65 return; | |
| 66 } | |
| 69 | 67 |
| 70 void FFmpegVideoDecoder::OnInitializeComplete(bool success) { | 68 state_ = kNormal; |
| 71 DCHECK_EQ(MessageLoop::current(), message_loop_); | 69 callback.Run(); |
| 72 DCHECK(!initialize_callback_.is_null()); | |
| 73 | |
| 74 if (success) { | |
| 75 state_ = kNormal; | |
| 76 } else { | |
| 77 host()->SetError(PIPELINE_ERROR_DECODE); | |
| 78 } | |
| 79 ResetAndRunCB(&initialize_callback_); | |
| 80 } | 70 } |
| 81 | 71 |
| 82 void FFmpegVideoDecoder::Stop(const base::Closure& callback) { | 72 void FFmpegVideoDecoder::Stop(const base::Closure& callback) { |
| 83 if (MessageLoop::current() != message_loop_) { | 73 if (MessageLoop::current() != message_loop_) { |
| 84 message_loop_->PostTask(FROM_HERE, base::Bind( | 74 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 85 &FFmpegVideoDecoder::Stop, this, callback)); | 75 &FFmpegVideoDecoder::Stop, this, callback)); |
| 86 return; | 76 return; |
| 87 } | 77 } |
| 88 | 78 |
| 89 DCHECK_EQ(MessageLoop::current(), message_loop_); | 79 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 90 DCHECK(uninitialize_callback_.is_null()); | |
| 91 | 80 |
| 92 uninitialize_callback_ = callback; | 81 decode_engine_->Uninitialize(); |
| 93 if (state_ != kUnInitialized) | 82 state_ = kUninitialized; |
| 94 decode_engine_->Uninitialize(); | 83 callback.Run(); |
| 95 else | |
| 96 OnUninitializeComplete(); | |
| 97 } | 84 } |
| 98 | 85 |
| 99 void FFmpegVideoDecoder::OnUninitializeComplete() { | 86 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
| 87 if (MessageLoop::current() != message_loop_) { | |
| 88 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 89 &FFmpegVideoDecoder::Seek, this, time, cb)); | |
| 90 return; | |
| 91 } | |
| 92 | |
| 100 DCHECK_EQ(MessageLoop::current(), message_loop_); | 93 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 101 DCHECK(!uninitialize_callback_.is_null()); | |
| 102 | 94 |
| 103 state_ = kStopped; | 95 pts_stream_.Seek(time); |
| 104 | 96 cb.Run(PIPELINE_OK); |
| 105 ResetAndRunCB(&uninitialize_callback_); | |
| 106 } | 97 } |
| 107 | 98 |
| 108 void FFmpegVideoDecoder::Pause(const base::Closure& callback) { | 99 void FFmpegVideoDecoder::Pause(const base::Closure& callback) { |
| 109 if (MessageLoop::current() != message_loop_) { | 100 if (MessageLoop::current() != message_loop_) { |
| 110 message_loop_->PostTask(FROM_HERE, base::Bind( | 101 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 111 &FFmpegVideoDecoder::Pause, this, callback)); | 102 &FFmpegVideoDecoder::Pause, this, callback)); |
| 112 return; | 103 return; |
| 113 } | 104 } |
| 114 | 105 |
| 115 state_ = kPausing; | |
| 116 callback.Run(); | 106 callback.Run(); |
| 117 } | 107 } |
| 118 | 108 |
| 119 void FFmpegVideoDecoder::Flush(const base::Closure& callback) { | 109 void FFmpegVideoDecoder::Flush(const base::Closure& callback) { |
| 120 if (MessageLoop::current() != message_loop_) { | 110 if (MessageLoop::current() != message_loop_) { |
| 121 message_loop_->PostTask(FROM_HERE, base::Bind( | 111 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 122 &FFmpegVideoDecoder::Flush, this, callback)); | 112 &FFmpegVideoDecoder::Flush, this, callback)); |
| 123 return; | 113 return; |
| 124 } | 114 } |
| 125 | 115 |
| 126 DCHECK_EQ(MessageLoop::current(), message_loop_); | 116 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 | 117 |
| 135 decode_engine_->Flush(); | 118 decode_engine_->Flush(); |
| 119 pts_stream_.Flush(); | |
| 120 state_ = kNormal; | |
| 121 callback.Run(); | |
| 136 } | 122 } |
| 137 | 123 |
| 138 void FFmpegVideoDecoder::OnFlushComplete() { | 124 void FFmpegVideoDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& buffer) { |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
Why have this be a separate function? It makes De
scherkus (not reviewing)
2011/11/03 04:55:59
Done.
| |
| 139 DCHECK_EQ(MessageLoop::current(), message_loop_); | 125 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 140 DCHECK(!flush_callback_.is_null()); | 126 DCHECK_NE(state_, kUninitialized); |
| 141 | 127 DCHECK_NE(state_, kDecodeFinished); |
| 142 // Everything in the presentation time queue is invalid, clear the queue. | 128 DCHECK(!frame_ready_cb_.is_null()); |
| 143 pts_stream_.Flush(); | |
| 144 | |
| 145 // Mark flush operation had been done. | |
| 146 state_ = kNormal; | |
| 147 | |
| 148 ResetAndRunCB(&flush_callback_); | |
| 149 } | |
| 150 | |
| 151 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { | |
| 152 if (MessageLoop::current() != message_loop_) { | |
| 153 message_loop_->PostTask(FROM_HERE, | |
| 154 base::Bind(&FFmpegVideoDecoder::Seek, this, | |
| 155 time, cb)); | |
| 156 return; | |
| 157 } | |
| 158 | |
| 159 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 160 DCHECK(seek_cb_.is_null()); | |
| 161 | |
| 162 pts_stream_.Seek(time); | |
| 163 seek_cb_ = cb; | |
| 164 decode_engine_->Seek(); | |
| 165 } | |
| 166 | |
| 167 void FFmpegVideoDecoder::OnSeekComplete() { | |
| 168 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 169 DCHECK(!seek_cb_.is_null()); | |
| 170 | |
| 171 ResetAndRunCB(&seek_cb_, PIPELINE_OK); | |
| 172 } | |
| 173 | |
| 174 void FFmpegVideoDecoder::OnError() { | |
| 175 VideoFrameReady(NULL); | |
| 176 } | |
| 177 | |
| 178 void FFmpegVideoDecoder::OnReadComplete(Buffer* buffer_in) { | |
| 179 scoped_refptr<Buffer> buffer(buffer_in); | |
| 180 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 181 &FFmpegVideoDecoder::OnReadCompleteTask, this, buffer)); | |
| 182 } | |
| 183 | |
| 184 void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) { | |
| 185 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 186 DCHECK_NE(state_, kStopped); // because of Flush() before Stop(). | |
| 187 | 129 |
| 188 // During decode, because reads are issued asynchronously, it is possible to | 130 // 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 | 131 // 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 | 132 // 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 | 133 // 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 | 134 // giving sensible data. After that, the decoder should output empty |
| 193 // frames. There are three states the decoder can be in: | 135 // frames. There are three states the decoder can be in: |
| 194 // | 136 // |
| 195 // kNormal: This is the starting state. Buffers are decoded. Decode errors | 137 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
| 196 // are discarded. | 138 // are discarded. |
| 197 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 | 139 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 |
| 198 // until no more data is returned to flush out remaining | 140 // until no more data is returned to flush out remaining |
| 199 // frames. The input buffer is ignored at this point. | 141 // frames. The input buffer is ignored at this point. |
| 200 // kDecodeFinished: All calls return empty frames. | 142 // kDecodeFinished: All calls return empty frames. |
| 201 // | 143 // |
| 202 // These are the possible state transitions. | 144 // These are the possible state transitions. |
| 203 // | 145 // |
| 204 // kNormal -> kFlushCodec: | 146 // kNormal -> kFlushCodec: |
| 205 // When buffer->IsEndOfStream() is first true. | 147 // When buffer->IsEndOfStream() is first true. |
| 206 // kNormal -> kDecodeFinished: | 148 // kNormal -> kDecodeFinished: |
| 207 // A catastrophic failure occurs, and decoding needs to stop. | 149 // A catastrophic failure occurs, and decoding needs to stop. |
| 208 // kFlushCodec -> kDecodeFinished: | 150 // kFlushCodec -> kDecodeFinished: |
| 209 // When avcodec_decode_video2() returns 0 data or errors out. | 151 // When avcodec_decode_video2() returns 0 data or errors out. |
| 210 // (any state) -> kNormal: | 152 // (any state) -> kNormal: |
| 211 // Any time buffer->IsDiscontinuous() is true. | 153 // Any time Flush() is called. |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
I'm surprised a Flush() can recover from a "catast
scherkus (not reviewing)
2011/11/03 04:55:59
Done.
| |
| 212 | 154 |
| 213 // Transition to kFlushCodec on the first end of stream buffer. | 155 // Transition to kFlushCodec on the first end of stream buffer. |
| 214 if (state_ == kNormal && buffer->IsEndOfStream()) { | 156 if (state_ == kNormal && buffer->IsEndOfStream()) { |
| 215 state_ = kFlushCodec; | 157 state_ = kFlushCodec; |
| 216 } | 158 } |
| 217 | 159 |
| 218 // Push all incoming timestamps into the priority queue as long as we have | 160 // 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 | 161 // not yet received an end of stream buffer. It is important that this line |
| 220 // stay below the state transition into kFlushCodec done above. | 162 // stay below the state transition into kFlushCodec done above. |
| 221 if (state_ == kNormal) { | 163 if (state_ == kNormal) { |
| 222 pts_stream_.EnqueuePts(buffer.get()); | 164 pts_stream_.EnqueuePts(buffer.get()); |
| 223 } | 165 } |
| 224 | 166 |
| 225 // Otherwise, attempt to decode a single frame. | 167 scoped_refptr<VideoFrame> video_frame; |
| 226 decode_engine_->ConsumeVideoSample(buffer); | 168 if (!decode_engine_->Decode(buffer, &video_frame)) { |
| 169 state_ = kDecodeFinished; | |
| 170 host()->SetError(PIPELINE_ERROR_DECODE); | |
| 171 return; | |
| 172 } | |
| 173 | |
| 174 // Any successful decode counts! | |
| 175 if (buffer->GetDataSize()) { | |
| 176 PipelineStatistics statistics; | |
| 177 statistics.video_bytes_decoded = buffer->GetDataSize(); | |
| 178 statistics_callback_.Run(statistics); | |
| 179 } | |
| 180 | |
| 181 // If we didn't get a frame then we've either completely finished decoding or | |
| 182 // we need more data. | |
| 183 if (!video_frame) { | |
| 184 if (state_ == kFlushCodec) { | |
| 185 state_ = kDecodeFinished; | |
| 186 DeliverFrame(VideoFrame::CreateEmptyFrame()); | |
| 187 return; | |
| 188 } | |
| 189 | |
| 190 ReadFromDemuxerStream(); | |
| 191 return; | |
| 192 } | |
| 193 | |
| 194 // If we got a frame make sure its timestamp is correct before sending it off. | |
| 195 pts_stream_.UpdatePtsAndDuration(video_frame.get()); | |
| 196 video_frame->SetTimestamp(pts_stream_.current_pts()); | |
| 197 video_frame->SetDuration(pts_stream_.current_duration()); | |
| 198 | |
| 199 DeliverFrame(video_frame); | |
| 227 } | 200 } |
| 228 | 201 |
| 229 void FFmpegVideoDecoder::ProduceVideoFrame( | 202 void FFmpegVideoDecoder::Read(const FrameReadyCB& callback) { |
| 230 scoped_refptr<VideoFrame> video_frame) { | |
| 231 if (MessageLoop::current() != message_loop_) { | 203 if (MessageLoop::current() != message_loop_) { |
| 232 if (state_ != kStopped) { | 204 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 233 message_loop_->PostTask(FROM_HERE, base::Bind( | 205 &FFmpegVideoDecoder::Read, this, callback)); |
| 234 &FFmpegVideoDecoder::ProduceVideoFrame, this, video_frame)); | |
| 235 } | |
| 236 return; | 206 return; |
| 237 } | 207 } |
| 238 | 208 |
| 239 DCHECK_EQ(MessageLoop::current(), message_loop_); | 209 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 210 CHECK(!callback.is_null()); | |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
Here and below s/CHECK/DCHECK/?
scherkus (not reviewing)
2011/11/03 04:55:59
maybe... I feel like this is programmer error and
| |
| 211 CHECK(frame_ready_cb_.is_null()) << "Overlapping decodes are not supported."; | |
| 240 | 212 |
| 241 // Synchronized flushing before stop should prevent this. | 213 // This can happen during shutdown after Stop() has been called. |
| 242 DCHECK_NE(state_, kStopped); | 214 if (state_ == kUninitialized) { |
| 243 | 215 // XXXXXXX |
| 244 // If the decoding is finished, we just always return empty frames. | 216 // just drop the callback? do we want to honour every callback no matter |
| 245 if (state_ == kDecodeFinished) { | 217 // what? |
|
scherkus (not reviewing)
2011/11/01 04:18:26
FYI
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Will the caller ever block progress if it doesn't
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
I say drop the callback.
scherkus (not reviewing)
2011/11/03 04:55:59
Done.
| |
| 246 // Signal VideoRenderer the end of the stream event. | 218 return; |
| 247 VideoFrameReady(VideoFrame::CreateEmptyFrame()); | |
| 248 | |
| 249 // Fall through, because we still need to keep record of this frame. | |
| 250 } | 219 } |
| 251 | 220 |
| 252 // Notify decode engine the available of new frame. | 221 // Return empty frames if decoding has finished. |
| 253 decode_engine_->ProduceVideoFrame(video_frame); | 222 if (state_ == kDecodeFinished) { |
| 254 } | 223 callback.Run(VideoFrame::CreateEmptyFrame()); |
| 224 return; | |
| 225 } | |
| 255 | 226 |
| 256 void FFmpegVideoDecoder::ConsumeVideoFrame( | 227 frame_ready_cb_ = callback; |
| 257 scoped_refptr<VideoFrame> video_frame, | 228 ReadFromDemuxerStream(); |
| 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; | |
| 270 } | |
| 271 | |
| 272 // If we actually got data back, enqueue a frame. | |
| 273 pts_stream_.UpdatePtsAndDuration(video_frame.get()); | |
| 274 | |
| 275 video_frame->SetTimestamp(pts_stream_.current_pts()); | |
| 276 video_frame->SetDuration(pts_stream_.current_duration()); | |
| 277 | |
| 278 VideoFrameReady(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 } | |
| 290 | |
| 291 void FFmpegVideoDecoder::ProduceVideoSample( | |
| 292 scoped_refptr<Buffer> buffer) { | |
| 293 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 294 DCHECK_NE(state_, kStopped); | |
| 295 | |
| 296 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::OnReadComplete, | |
| 297 this)); | |
| 298 } | 229 } |
| 299 | 230 |
| 300 gfx::Size FFmpegVideoDecoder::natural_size() { | 231 gfx::Size FFmpegVideoDecoder::natural_size() { |
| 301 return natural_size_; | 232 return natural_size_; |
| 302 } | 233 } |
| 303 | 234 |
| 304 void FFmpegVideoDecoder::FlushBuffers() { | 235 void FFmpegVideoDecoder::ReadFromDemuxerStream() { |
| 305 while (!frame_queue_flushed_.empty()) { | 236 DCHECK_NE(state_, kUninitialized); |
| 306 scoped_refptr<VideoFrame> video_frame; | 237 DCHECK_NE(state_, kDecodeFinished); |
| 307 video_frame = frame_queue_flushed_.front(); | 238 DCHECK(!frame_ready_cb_.is_null()); |
| 308 frame_queue_flushed_.pop_front(); | |
| 309 | 239 |
| 310 // Return frames back to the decode engine. | 240 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::DecodeBuffer, this)); |
| 311 decode_engine_->ProduceVideoFrame(video_frame); | |
| 312 } | |
| 313 } | 241 } |
| 314 | 242 |
| 315 void FFmpegVideoDecoder::SetVideoDecodeEngineForTest( | 243 void FFmpegVideoDecoder::DecodeBuffer(Buffer* buffer) { |
| 316 VideoDecodeEngine* engine) { | 244 // TODO(scherkus): change DemuxerStream::Read() to use scoped_refptr<> for |
|
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
This TODO should be in demuxer_stream.h
scherkus (not reviewing)
2011/11/03 04:55:59
TODO was fixed in r108178
| |
| 317 decode_engine_.reset(engine); | 245 // callback. |
| 246 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 247 &FFmpegVideoDecoder::DoDecodeBuffer, this, make_scoped_refptr(buffer))); | |
| 248 } | |
| 249 | |
| 250 void FFmpegVideoDecoder::DeliverFrame( | |
| 251 const scoped_refptr<VideoFrame>& video_frame) { | |
| 252 // Reset the callback before running to protect against reentrancy. | |
| 253 FrameReadyCB frame_ready_cb = frame_ready_cb_; | |
| 254 frame_ready_cb_.Reset(); | |
| 255 frame_ready_cb.Run(video_frame); | |
| 318 } | 256 } |
| 319 | 257 |
| 320 } // namespace media | 258 } // namespace media |
| OLD | NEW |