Chromium Code Reviews| 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 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 128 video_frame.swap(reinterpret_cast<VideoFrame**>(&frame->opaque)); | 128 video_frame.swap(reinterpret_cast<VideoFrame**>(&frame->opaque)); |
| 129 | 129 |
| 130 // The FFmpeg API expects us to zero the data pointers in | 130 // The FFmpeg API expects us to zero the data pointers in |
| 131 // this callback | 131 // this callback |
| 132 memset(frame->data, 0, sizeof(frame->data)); | 132 memset(frame->data, 0, sizeof(frame->data)); |
| 133 frame->opaque = NULL; | 133 frame->opaque = NULL; |
| 134 } | 134 } |
| 135 | 135 |
| 136 void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config, | 136 void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config, |
| 137 bool low_delay, | 137 bool low_delay, |
| 138 const PipelineStatusCB& status_cb) { | 138 const PipelineStatusCB& status_cb, |
| 139 const OutputCB& output_cb) { | |
| 139 DCHECK(task_runner_->BelongsToCurrentThread()); | 140 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 140 DCHECK(decode_cb_.is_null()); | |
| 141 DCHECK(!config.is_encrypted()); | 141 DCHECK(!config.is_encrypted()); |
| 142 DCHECK(!output_cb.is_null()); | |
| 142 | 143 |
| 143 FFmpegGlue::InitializeFFmpeg(); | 144 FFmpegGlue::InitializeFFmpeg(); |
| 144 | 145 |
| 145 config_ = config; | 146 config_ = config; |
| 146 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); | 147 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); |
| 147 | 148 |
| 148 if (!config.IsValidConfig() || !ConfigureDecoder(low_delay)) { | 149 if (!config.IsValidConfig() || !ConfigureDecoder(low_delay)) { |
| 149 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 150 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| 150 return; | 151 return; |
| 151 } | 152 } |
| 152 | 153 |
| 154 output_cb_ = BindToCurrentLoop(output_cb); | |
| 155 | |
| 153 // Success! | 156 // Success! |
| 154 state_ = kNormal; | 157 state_ = kNormal; |
| 155 initialize_cb.Run(PIPELINE_OK); | 158 initialize_cb.Run(PIPELINE_OK); |
| 156 } | 159 } |
| 157 | 160 |
| 158 void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, | 161 void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 159 const DecodeCB& decode_cb) { | 162 const DecodeCB& decode_cb) { |
| 160 DCHECK(task_runner_->BelongsToCurrentThread()); | 163 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 164 DCHECK(buffer); | |
| 161 DCHECK(!decode_cb.is_null()); | 165 DCHECK(!decode_cb.is_null()); |
| 162 CHECK_NE(state_, kUninitialized); | 166 CHECK_NE(state_, kUninitialized); |
| 163 CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; | 167 |
| 164 decode_cb_ = BindToCurrentLoop(decode_cb); | 168 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); |
| 165 | 169 |
| 166 if (state_ == kError) { | 170 if (state_ == kError) { |
| 167 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); | 171 decode_cb_bound.Run(kDecodeError); |
| 168 return; | 172 return; |
| 169 } | 173 } |
| 170 | 174 |
| 171 // Return empty frames if decoding has finished. | 175 // Return empty frames if decoding has finished. |
| 172 if (state_ == kDecodeFinished) { | 176 if (state_ == kDecodeFinished) { |
| 173 base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEOSFrame()); | 177 output_cb_.Run(VideoFrame::CreateEOSFrame()); |
| 178 decode_cb_bound.Run(kOk); | |
| 174 return; | 179 return; |
| 175 } | 180 } |
| 176 | 181 |
| 177 DecodeBuffer(buffer); | 182 DCHECK_EQ(state_, kNormal); |
| 178 } | |
| 179 | |
| 180 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { | |
| 181 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 182 DCHECK(decode_cb_.is_null()); | |
| 183 | |
| 184 avcodec_flush_buffers(codec_context_.get()); | |
| 185 state_ = kNormal; | |
| 186 task_runner_->PostTask(FROM_HERE, closure); | |
| 187 } | |
| 188 | |
| 189 void FFmpegVideoDecoder::Stop() { | |
| 190 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 191 | |
| 192 if (state_ == kUninitialized) | |
| 193 return; | |
| 194 | |
| 195 ReleaseFFmpegResources(); | |
| 196 state_ = kUninitialized; | |
| 197 } | |
| 198 | |
| 199 FFmpegVideoDecoder::~FFmpegVideoDecoder() { | |
| 200 DCHECK_EQ(kUninitialized, state_); | |
| 201 DCHECK(!codec_context_); | |
| 202 DCHECK(!av_frame_); | |
| 203 } | |
| 204 | |
| 205 void FFmpegVideoDecoder::DecodeBuffer( | |
| 206 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 207 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 208 DCHECK_NE(state_, kUninitialized); | |
| 209 DCHECK_NE(state_, kDecodeFinished); | |
| 210 DCHECK_NE(state_, kError); | |
| 211 DCHECK(!decode_cb_.is_null()); | |
| 212 DCHECK(buffer); | |
| 213 | 183 |
| 214 // During decode, because reads are issued asynchronously, it is possible to | 184 // During decode, because reads are issued asynchronously, it is possible to |
| 215 // receive multiple end of stream buffers since each decode is acked. When the | 185 // receive multiple end of stream buffers since each decode is acked. When the |
| 216 // first end of stream buffer is read, FFmpeg may still have frames queued | 186 // first end of stream buffer is read, FFmpeg may still have frames queued |
| 217 // up in the decoder so we need to go through the decode loop until it stops | 187 // up in the decoder so we need to go through the decode loop until it stops |
| 218 // giving sensible data. After that, the decoder should output empty | 188 // giving sensible data. After that, the decoder should output empty |
| 219 // frames. There are three states the decoder can be in: | 189 // frames. There are three states the decoder can be in: |
| 220 // | 190 // |
| 221 // kNormal: This is the starting state. Buffers are decoded. Decode errors | 191 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
| 222 // are discarded. | 192 // are discarded. |
| 223 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 | 193 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 |
| 224 // until no more data is returned to flush out remaining | 194 // until no more data is returned to flush out remaining |
| 225 // frames. The input buffer is ignored at this point. | 195 // frames. The input buffer is ignored at this point. |
| 226 // kDecodeFinished: All calls return empty frames. | 196 // kDecodeFinished: All calls return empty frames. |
| 227 // kError: Unexpected error happened. | 197 // kError: Unexpected error happened. |
| 228 // | 198 // |
| 229 // These are the possible state transitions. | 199 // These are the possible state transitions. |
| 230 // | 200 // |
| 231 // kNormal -> kFlushCodec: | 201 // kNormal -> kDecodeFinished: |
| 232 // When buffer->end_of_stream() is first true. | 202 // When EOS buffer is received. |
| 233 // kNormal -> kError: | 203 // kNormal -> kError: |
| 234 // A decoding error occurs and decoding needs to stop. | 204 // A decoding error occurs and decoding needs to stop. |
| 235 // kFlushCodec -> kDecodeFinished: | |
| 236 // When avcodec_decode_video2() returns 0 data. | |
| 237 // kFlushCodec -> kError: | |
| 238 // When avcodec_decode_video2() errors out. | |
| 239 // (any state) -> kNormal: | 205 // (any state) -> kNormal: |
| 240 // Any time Reset() is called. | 206 // Any time Reset() is called. |
| 241 | 207 |
| 242 // Transition to kFlushCodec on the first end of stream buffer. | 208 // Flush the decoder after receiving EOS buffer. |
| 243 if (state_ == kNormal && buffer->end_of_stream()) { | 209 if (buffer->end_of_stream()) { |
| 244 state_ = kFlushCodec; | 210 scoped_refptr<VideoFrame> video_frame; |
| 211 do { | |
| 212 if (!FFmpegDecode(buffer, &video_frame)) { | |
| 213 state_ = kError; | |
| 214 decode_cb_bound.Run(kDecodeError); | |
| 215 return; | |
| 216 } | |
| 217 if (video_frame) | |
| 218 output_cb_.Run(video_frame); | |
| 219 } while (video_frame); | |
| 220 output_cb_.Run(VideoFrame::CreateEOSFrame()); | |
| 221 state_ = kDecodeFinished; | |
| 222 } else { | |
| 223 scoped_refptr<VideoFrame> video_frame; | |
| 224 if (!FFmpegDecode(buffer, &video_frame)) { | |
| 225 state_ = kError; | |
| 226 decode_cb_bound.Run(kDecodeError); | |
| 227 return; | |
| 228 } | |
| 229 if (video_frame) | |
| 230 output_cb_.Run(video_frame); | |
|
xhwang
2014/05/29 22:15:14
You can probably wrap l.223-230 into a helper func
Sergey Ulanov
2014/06/03 00:08:11
Formatted this code to avoid duplication.
| |
| 245 } | 231 } |
| 246 | 232 |
| 247 scoped_refptr<VideoFrame> video_frame; | 233 decode_cb_bound.Run(kOk); |
| 248 if (!FFmpegDecode(buffer, &video_frame)) { | 234 } |
| 249 state_ = kError; | 235 |
| 250 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); | 236 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { |
| 237 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 238 | |
| 239 avcodec_flush_buffers(codec_context_.get()); | |
| 240 state_ = kNormal; | |
| 241 task_runner_->PostTask(FROM_HERE, closure); | |
| 242 } | |
| 243 | |
| 244 void FFmpegVideoDecoder::Stop() { | |
| 245 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 246 | |
| 247 if (state_ == kUninitialized) | |
| 251 return; | 248 return; |
| 252 } | |
| 253 | 249 |
| 254 if (!video_frame.get()) { | 250 ReleaseFFmpegResources(); |
| 255 if (state_ == kFlushCodec) { | 251 state_ = kUninitialized; |
| 256 DCHECK(buffer->end_of_stream()); | 252 } |
| 257 state_ = kDecodeFinished; | |
| 258 base::ResetAndReturn(&decode_cb_) | |
| 259 .Run(kOk, VideoFrame::CreateEOSFrame()); | |
| 260 return; | |
| 261 } | |
| 262 | 253 |
| 263 base::ResetAndReturn(&decode_cb_).Run(kNotEnoughData, NULL); | 254 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
| 264 return; | 255 DCHECK_EQ(kUninitialized, state_); |
| 265 } | 256 DCHECK(!codec_context_); |
| 266 | 257 DCHECK(!av_frame_); |
| 267 base::ResetAndReturn(&decode_cb_).Run(kOk, video_frame); | |
| 268 } | 258 } |
| 269 | 259 |
| 270 bool FFmpegVideoDecoder::FFmpegDecode( | 260 bool FFmpegVideoDecoder::FFmpegDecode( |
| 271 const scoped_refptr<DecoderBuffer>& buffer, | 261 const scoped_refptr<DecoderBuffer>& buffer, |
| 272 scoped_refptr<VideoFrame>* video_frame) { | 262 scoped_refptr<VideoFrame>* video_frame) { |
| 273 DCHECK(video_frame); | 263 DCHECK(video_frame); |
| 274 | 264 |
| 275 // Reset frame to default values. | 265 // Reset frame to default values. |
| 276 avcodec_get_frame_defaults(av_frame_.get()); | 266 avcodec_get_frame_defaults(av_frame_.get()); |
| 277 | 267 |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 365 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { | 355 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { |
| 366 ReleaseFFmpegResources(); | 356 ReleaseFFmpegResources(); |
| 367 return false; | 357 return false; |
| 368 } | 358 } |
| 369 | 359 |
| 370 av_frame_.reset(av_frame_alloc()); | 360 av_frame_.reset(av_frame_alloc()); |
| 371 return true; | 361 return true; |
| 372 } | 362 } |
| 373 | 363 |
| 374 } // namespace media | 364 } // namespace media |
| OLD | NEW |