| 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 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 | 68 |
| 69 static size_t RoundUp(size_t value, size_t alignment) { | 69 static size_t RoundUp(size_t value, size_t alignment) { |
| 70 // Check that |alignment| is a power of 2. | 70 // Check that |alignment| is a power of 2. |
| 71 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); | 71 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); |
| 72 return ((value + (alignment - 1)) & ~(alignment - 1)); | 72 return ((value + (alignment - 1)) & ~(alignment - 1)); |
| 73 } | 73 } |
| 74 | 74 |
| 75 FFmpegVideoDecoder::FFmpegVideoDecoder( | 75 FFmpegVideoDecoder::FFmpegVideoDecoder( |
| 76 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) | 76 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) |
| 77 : task_runner_(task_runner), state_(kUninitialized), | 77 : task_runner_(task_runner), state_(kUninitialized), |
| 78 decode_nalus_(false) {} | 78 decode_nalus_(false), weak_factory_(this) {} |
| 79 | 79 |
| 80 int FFmpegVideoDecoder::GetVideoBuffer(struct AVCodecContext* codec_context, | 80 int FFmpegVideoDecoder::GetVideoBuffer(struct AVCodecContext* codec_context, |
| 81 AVFrame* frame, | 81 AVFrame* frame, |
| 82 int flags) { | 82 int flags) { |
| 83 // Don't use |codec_context_| here! With threaded decoding, | 83 // Don't use |codec_context_| here! With threaded decoding, |
| 84 // it will contain unsynchronized width/height/pix_fmt values, | 84 // it will contain unsynchronized width/height/pix_fmt values, |
| 85 // whereas |codec_context| contains the current threads's | 85 // whereas |codec_context| contains the current threads's |
| 86 // updated width/height/pix_fmt, which can change for adaptive | 86 // updated width/height/pix_fmt, which can change for adaptive |
| 87 // content. | 87 // content. |
| 88 VideoFrame::Format format = PixelFormatToVideoFormat(codec_context->pix_fmt); | 88 VideoFrame::Format format = PixelFormatToVideoFormat(codec_context->pix_fmt); |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 ReleaseVideoBufferImpl, | 146 ReleaseVideoBufferImpl, |
| 147 opaque, | 147 opaque, |
| 148 0); | 148 0); |
| 149 return 0; | 149 return 0; |
| 150 } | 150 } |
| 151 | 151 |
| 152 void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config, | 152 void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config, |
| 153 bool low_delay, | 153 bool low_delay, |
| 154 const PipelineStatusCB& status_cb, | 154 const PipelineStatusCB& status_cb, |
| 155 const OutputCB& output_cb) { | 155 const OutputCB& output_cb) { |
| 156 DVLOG(2) << __FUNCTION__; |
| 156 DCHECK(task_runner_->BelongsToCurrentThread()); | 157 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 157 DCHECK(!config.is_encrypted()); | 158 DCHECK(!config.is_encrypted()); |
| 158 DCHECK(!output_cb.is_null()); | 159 DCHECK(!output_cb.is_null()); |
| 159 | 160 |
| 160 FFmpegGlue::InitializeFFmpeg(); | 161 FFmpegGlue::InitializeFFmpeg(); |
| 161 | 162 |
| 162 config_ = config; | 163 config_ = config; |
| 163 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); | 164 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); |
| 164 | 165 |
| 165 if (!config.IsValidConfig() || !ConfigureDecoder(low_delay)) { | 166 if (!config.IsValidConfig() || !ConfigureDecoder(low_delay)) { |
| 166 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 167 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| 167 return; | 168 return; |
| 168 } | 169 } |
| 169 | 170 |
| 170 output_cb_ = BindToCurrentLoop(output_cb); | 171 output_cb_ = BindToCurrentLoop(output_cb); |
| 171 | 172 |
| 172 // Success! | 173 // Success! |
| 173 state_ = kNormal; | 174 state_ = kNormal; |
| 174 initialize_cb.Run(PIPELINE_OK); | 175 initialize_cb.Run(PIPELINE_OK); |
| 175 } | 176 } |
| 176 | 177 |
| 177 void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, | 178 void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 178 const DecodeCB& decode_cb) { | 179 const DecodeCB& decode_cb) { |
| 180 DVLOG(2) << __FUNCTION__; |
| 181 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 182 |
| 183 static int count = 1; |
| 184 count++; |
| 185 int delay = 1; |
| 186 if (count % 100 == 0) |
| 187 delay = 2000; |
| 188 |
| 189 //base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(delay)); |
| 190 //DecodeInternal(buffer, decode_cb); |
| 191 |
| 192 task_runner_->PostDelayedTask(FROM_HERE, |
| 193 base::Bind(&FFmpegVideoDecoder::DecodeInternal, |
| 194 weak_factory_.GetWeakPtr(), |
| 195 buffer, |
| 196 decode_cb), |
| 197 base::TimeDelta::FromMilliseconds(delay)); |
| 198 } |
| 199 |
| 200 void FFmpegVideoDecoder::DecodeInternal(const scoped_refptr<DecoderBuffer>& buff
er, |
| 201 const DecodeCB& decode_cb) { |
| 202 DVLOG(2) << __FUNCTION__; |
| 179 DCHECK(task_runner_->BelongsToCurrentThread()); | 203 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 180 DCHECK(buffer); | 204 DCHECK(buffer); |
| 181 DCHECK(!decode_cb.is_null()); | 205 DCHECK(!decode_cb.is_null()); |
| 182 CHECK_NE(state_, kUninitialized); | 206 CHECK_NE(state_, kUninitialized); |
| 183 | 207 |
| 184 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); | 208 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); |
| 185 | 209 |
| 186 if (state_ == kError) { | 210 if (state_ == kError) { |
| 187 decode_cb_bound.Run(kDecodeError); | 211 decode_cb_bound.Run(kDecodeError); |
| 188 return; | 212 return; |
| 189 } | 213 } |
| 190 | 214 |
| 191 if (state_ == kDecodeFinished) { | 215 if (state_ == kDecodeFinished) { |
| 192 decode_cb_bound.Run(kOk); | 216 decode_cb_bound.Run(kOk); |
| 193 return; | 217 return; |
| 194 } | 218 } |
| 195 | 219 |
| 220 //base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(40)); |
| 221 |
| 196 DCHECK_EQ(state_, kNormal); | 222 DCHECK_EQ(state_, kNormal); |
| 197 | 223 |
| 198 // During decode, because reads are issued asynchronously, it is possible to | 224 // During decode, because reads are issued asynchronously, it is possible to |
| 199 // receive multiple end of stream buffers since each decode is acked. When the | 225 // receive multiple end of stream buffers since each decode is acked. When the |
| 200 // first end of stream buffer is read, FFmpeg may still have frames queued | 226 // first end of stream buffer is read, FFmpeg may still have frames queued |
| 201 // up in the decoder so we need to go through the decode loop until it stops | 227 // up in the decoder so we need to go through the decode loop until it stops |
| 202 // giving sensible data. After that, the decoder should output empty | 228 // giving sensible data. After that, the decoder should output empty |
| 203 // frames. There are three states the decoder can be in: | 229 // frames. There are three states the decoder can be in: |
| 204 // | 230 // |
| 205 // kNormal: This is the starting state. Buffers are decoded. Decode errors | 231 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; | 327 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; |
| 302 av_frame_unref(av_frame_.get()); | 328 av_frame_unref(av_frame_.get()); |
| 303 return false; | 329 return false; |
| 304 } | 330 } |
| 305 | 331 |
| 306 scoped_refptr<VideoFrame> frame = | 332 scoped_refptr<VideoFrame> frame = |
| 307 reinterpret_cast<VideoFrame*>(av_buffer_get_opaque(av_frame_->buf[0])); | 333 reinterpret_cast<VideoFrame*>(av_buffer_get_opaque(av_frame_->buf[0])); |
| 308 frame->set_timestamp( | 334 frame->set_timestamp( |
| 309 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); | 335 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); |
| 310 *has_produced_frame = true; | 336 *has_produced_frame = true; |
| 337 |
| 311 output_cb_.Run(frame); | 338 output_cb_.Run(frame); |
| 312 | 339 |
| 313 av_frame_unref(av_frame_.get()); | 340 av_frame_unref(av_frame_.get()); |
| 314 return true; | 341 return true; |
| 315 } | 342 } |
| 316 | 343 |
| 317 void FFmpegVideoDecoder::ReleaseFFmpegResources() { | 344 void FFmpegVideoDecoder::ReleaseFFmpegResources() { |
| 318 codec_context_.reset(); | 345 codec_context_.reset(); |
| 319 av_frame_.reset(); | 346 av_frame_.reset(); |
| 320 } | 347 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 341 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { | 368 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { |
| 342 ReleaseFFmpegResources(); | 369 ReleaseFFmpegResources(); |
| 343 return false; | 370 return false; |
| 344 } | 371 } |
| 345 | 372 |
| 346 av_frame_.reset(av_frame_alloc()); | 373 av_frame_.reset(av_frame_alloc()); |
| 347 return true; | 374 return true; |
| 348 } | 375 } |
| 349 | 376 |
| 350 } // namespace media | 377 } // namespace media |
| OLD | NEW |