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) { |
|
xhwang
2014/06/05 21:53:51
nit: Probably this should never happen (DecoderStr
Sergey Ulanov
2014/06/06 22:49:41
Done.
Sergey Ulanov
2014/06/06 23:12:34
Actually DecoderStream may call this method after
xhwang
2014/06/07 00:35:14
Can you add a TODO here?
| |
| 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. |
|
xhwang
2014/06/05 21:53:50
this is obsolete now?
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
| 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. |
|
xhwang
2014/06/05 21:53:50
When EOS buffer is received and the codec has been
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
| 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 bool repeat; |
| 243 if (state_ == kNormal && buffer->end_of_stream()) { | 209 do { |
| 244 state_ = kFlushCodec; | 210 bool produced_frame = false; |
| 211 if (!FFmpegDecode(buffer, &produced_frame)) { | |
|
xhwang
2014/06/05 21:53:50
|produced_frame| could be read as a noun. How abou
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
| 212 state_ = kError; | |
| 213 decode_cb_bound.Run(kDecodeError); | |
| 214 return; | |
| 215 } | |
| 216 // Repeat to flush the decoder after receiving EOS buffer. | |
| 217 repeat = buffer->end_of_stream() && produced_frame; | |
| 218 } while (repeat); | |
|
xhwang
2014/06/05 21:53:50
nit: how about just
while (buffer->end_of_stream(
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
| 219 | |
| 220 if (buffer->end_of_stream()) { | |
| 221 output_cb_.Run(VideoFrame::CreateEOSFrame()); | |
| 222 state_ = kDecodeFinished; | |
| 245 } | 223 } |
| 246 | 224 |
| 247 scoped_refptr<VideoFrame> video_frame; | 225 decode_cb_bound.Run(kOk); |
| 248 if (!FFmpegDecode(buffer, &video_frame)) { | 226 } |
| 249 state_ = kError; | 227 |
| 250 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); | 228 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { |
| 229 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 230 | |
| 231 avcodec_flush_buffers(codec_context_.get()); | |
| 232 state_ = kNormal; | |
| 233 task_runner_->PostTask(FROM_HERE, closure); | |
| 234 } | |
| 235 | |
| 236 void FFmpegVideoDecoder::Stop() { | |
| 237 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 238 | |
| 239 if (state_ == kUninitialized) | |
| 251 return; | 240 return; |
| 252 } | |
| 253 | 241 |
| 254 if (!video_frame.get()) { | 242 ReleaseFFmpegResources(); |
| 255 if (state_ == kFlushCodec) { | 243 state_ = kUninitialized; |
| 256 DCHECK(buffer->end_of_stream()); | 244 } |
| 257 state_ = kDecodeFinished; | |
| 258 base::ResetAndReturn(&decode_cb_) | |
| 259 .Run(kOk, VideoFrame::CreateEOSFrame()); | |
| 260 return; | |
| 261 } | |
| 262 | 245 |
| 263 base::ResetAndReturn(&decode_cb_).Run(kNotEnoughData, NULL); | 246 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
| 264 return; | 247 DCHECK_EQ(kUninitialized, state_); |
| 265 } | 248 DCHECK(!codec_context_); |
| 266 | 249 DCHECK(!av_frame_); |
| 267 base::ResetAndReturn(&decode_cb_).Run(kOk, video_frame); | |
| 268 } | 250 } |
| 269 | 251 |
| 270 bool FFmpegVideoDecoder::FFmpegDecode( | 252 bool FFmpegVideoDecoder::FFmpegDecode( |
| 271 const scoped_refptr<DecoderBuffer>& buffer, | 253 const scoped_refptr<DecoderBuffer>& buffer, |
| 272 scoped_refptr<VideoFrame>* video_frame) { | 254 bool* produced_frame) { |
| 273 DCHECK(video_frame); | 255 *produced_frame = false; |
|
xhwang
2014/06/05 21:53:51
DCHECK(!*produced_frame);
Sergey Ulanov
2014/06/06 22:49:41
Done.
| |
| 274 | 256 |
| 275 // Reset frame to default values. | 257 // Reset frame to default values. |
| 276 avcodec_get_frame_defaults(av_frame_.get()); | 258 avcodec_get_frame_defaults(av_frame_.get()); |
| 277 | 259 |
| 278 // Create a packet for input data. | 260 // Create a packet for input data. |
| 279 // Due to FFmpeg API changes we no longer have const read-only pointers. | 261 // Due to FFmpeg API changes we no longer have const read-only pointers. |
| 280 AVPacket packet; | 262 AVPacket packet; |
| 281 av_init_packet(&packet); | 263 av_init_packet(&packet); |
| 282 if (buffer->end_of_stream()) { | 264 if (buffer->end_of_stream()) { |
| 283 packet.data = NULL; | 265 packet.data = NULL; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 295 } | 277 } |
| 296 | 278 |
| 297 int frame_decoded = 0; | 279 int frame_decoded = 0; |
| 298 int result = avcodec_decode_video2(codec_context_.get(), | 280 int result = avcodec_decode_video2(codec_context_.get(), |
| 299 av_frame_.get(), | 281 av_frame_.get(), |
| 300 &frame_decoded, | 282 &frame_decoded, |
| 301 &packet); | 283 &packet); |
| 302 // Log the problem if we can't decode a video frame and exit early. | 284 // Log the problem if we can't decode a video frame and exit early. |
| 303 if (result < 0) { | 285 if (result < 0) { |
| 304 LOG(ERROR) << "Error decoding video: " << buffer->AsHumanReadableString(); | 286 LOG(ERROR) << "Error decoding video: " << buffer->AsHumanReadableString(); |
| 305 *video_frame = NULL; | |
| 306 return false; | 287 return false; |
| 307 } | 288 } |
| 308 | 289 |
| 309 // If no frame was produced then signal that more data is required to | 290 // If no frame was produced then signal that more data is required to |
| 310 // produce more frames. This can happen under two circumstances: | 291 // produce more frames. This can happen under two circumstances: |
| 311 // 1) Decoder was recently initialized/flushed | 292 // 1) Decoder was recently initialized/flushed |
| 312 // 2) End of stream was reached and all internal frames have been output | 293 // 2) End of stream was reached and all internal frames have been output |
| 313 if (frame_decoded == 0) { | 294 if (frame_decoded == 0) { |
| 314 *video_frame = NULL; | |
| 315 return true; | 295 return true; |
| 316 } | 296 } |
| 317 | 297 |
| 318 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 | 298 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 |
| 319 // The decoder is in a bad state and not decoding correctly. | 299 // The decoder is in a bad state and not decoding correctly. |
| 320 // Checking for NULL avoids a crash in CopyPlane(). | 300 // Checking for NULL avoids a crash in CopyPlane(). |
| 321 if (!av_frame_->data[VideoFrame::kYPlane] || | 301 if (!av_frame_->data[VideoFrame::kYPlane] || |
| 322 !av_frame_->data[VideoFrame::kUPlane] || | 302 !av_frame_->data[VideoFrame::kUPlane] || |
| 323 !av_frame_->data[VideoFrame::kVPlane]) { | 303 !av_frame_->data[VideoFrame::kVPlane]) { |
| 324 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; | 304 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; |
| 325 *video_frame = NULL; | |
| 326 return false; | 305 return false; |
| 327 } | 306 } |
| 328 | 307 |
| 329 if (!av_frame_->opaque) { | 308 if (!av_frame_->opaque) { |
| 330 LOG(ERROR) << "VideoFrame object associated with frame data not set."; | 309 LOG(ERROR) << "VideoFrame object associated with frame data not set."; |
| 331 return false; | 310 return false; |
| 332 } | 311 } |
| 333 *video_frame = static_cast<VideoFrame*>(av_frame_->opaque); | |
| 334 | 312 |
| 335 (*video_frame)->set_timestamp( | 313 scoped_refptr<VideoFrame> frame = static_cast<VideoFrame*>(av_frame_->opaque); |
| 314 frame->set_timestamp( | |
| 336 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); | 315 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); |
| 316 *produced_frame = true; | |
| 317 output_cb_.Run(frame); | |
| 337 | 318 |
| 338 return true; | 319 return true; |
| 339 } | 320 } |
| 340 | 321 |
| 341 void FFmpegVideoDecoder::ReleaseFFmpegResources() { | 322 void FFmpegVideoDecoder::ReleaseFFmpegResources() { |
| 342 codec_context_.reset(); | 323 codec_context_.reset(); |
| 343 av_frame_.reset(); | 324 av_frame_.reset(); |
| 344 } | 325 } |
| 345 | 326 |
| 346 bool FFmpegVideoDecoder::ConfigureDecoder(bool low_delay) { | 327 bool FFmpegVideoDecoder::ConfigureDecoder(bool low_delay) { |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 365 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { | 346 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { |
| 366 ReleaseFFmpegResources(); | 347 ReleaseFFmpegResources(); |
| 367 return false; | 348 return false; |
| 368 } | 349 } |
| 369 | 350 |
| 370 av_frame_.reset(av_frame_alloc()); | 351 av_frame_.reset(av_frame_alloc()); |
| 371 return true; | 352 return true; |
| 372 } | 353 } |
| 373 | 354 |
| 374 } // namespace media | 355 } // namespace media |
| OLD | NEW |