Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(538)

Side by Side Diff: media/filters/ffmpeg_video_decoder.cc

Issue 297553002: Add callback in VideoDecoder and AudioDecoder to return decoded frames. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 av_buffer_create(frame->data[0], 133 av_buffer_create(frame->data[0],
134 VideoFrame::AllocationSize(format, coded_size), 134 VideoFrame::AllocationSize(format, coded_size),
135 ReleaseVideoBufferImpl, 135 ReleaseVideoBufferImpl,
136 opaque, 136 opaque,
137 0); 137 0);
138 return 0; 138 return 0;
139 } 139 }
140 140
141 void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config, 141 void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config,
142 bool low_delay, 142 bool low_delay,
143 const PipelineStatusCB& status_cb) { 143 const PipelineStatusCB& status_cb,
144 const OutputCB& output_cb) {
144 DCHECK(task_runner_->BelongsToCurrentThread()); 145 DCHECK(task_runner_->BelongsToCurrentThread());
145 DCHECK(decode_cb_.is_null());
146 DCHECK(!config.is_encrypted()); 146 DCHECK(!config.is_encrypted());
147 DCHECK(!output_cb.is_null());
147 148
148 FFmpegGlue::InitializeFFmpeg(); 149 FFmpegGlue::InitializeFFmpeg();
149 150
150 config_ = config; 151 config_ = config;
151 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); 152 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb);
152 153
153 if (!config.IsValidConfig() || !ConfigureDecoder(low_delay)) { 154 if (!config.IsValidConfig() || !ConfigureDecoder(low_delay)) {
154 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); 155 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
155 return; 156 return;
156 } 157 }
157 158
159 output_cb_ = BindToCurrentLoop(output_cb);
160
158 // Success! 161 // Success!
159 state_ = kNormal; 162 state_ = kNormal;
160 initialize_cb.Run(PIPELINE_OK); 163 initialize_cb.Run(PIPELINE_OK);
161 } 164 }
162 165
163 void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, 166 void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
164 const DecodeCB& decode_cb) { 167 const DecodeCB& decode_cb) {
165 DCHECK(task_runner_->BelongsToCurrentThread()); 168 DCHECK(task_runner_->BelongsToCurrentThread());
169 DCHECK(buffer);
166 DCHECK(!decode_cb.is_null()); 170 DCHECK(!decode_cb.is_null());
167 CHECK_NE(state_, kUninitialized); 171 CHECK_NE(state_, kUninitialized);
168 CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; 172
169 decode_cb_ = BindToCurrentLoop(decode_cb); 173 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb);
170 174
171 if (state_ == kError) { 175 if (state_ == kError) {
172 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); 176 decode_cb_bound.Run(kDecodeError);
173 return; 177 return;
174 } 178 }
175 179
176 // Return empty frames if decoding has finished.
177 if (state_ == kDecodeFinished) { 180 if (state_ == kDecodeFinished) {
178 base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEOSFrame()); 181 output_cb_.Run(VideoFrame::CreateEOSFrame());
182 decode_cb_bound.Run(kOk);
179 return; 183 return;
180 } 184 }
181 185
182 DecodeBuffer(buffer); 186 DCHECK_EQ(state_, kNormal);
183 }
184
185 void FFmpegVideoDecoder::Reset(const base::Closure& closure) {
186 DCHECK(task_runner_->BelongsToCurrentThread());
187 DCHECK(decode_cb_.is_null());
188
189 avcodec_flush_buffers(codec_context_.get());
190 state_ = kNormal;
191 task_runner_->PostTask(FROM_HERE, closure);
192 }
193
194 void FFmpegVideoDecoder::Stop() {
195 DCHECK(task_runner_->BelongsToCurrentThread());
196
197 if (state_ == kUninitialized)
198 return;
199
200 ReleaseFFmpegResources();
201 state_ = kUninitialized;
202 }
203
204 FFmpegVideoDecoder::~FFmpegVideoDecoder() {
205 DCHECK_EQ(kUninitialized, state_);
206 DCHECK(!codec_context_);
207 DCHECK(!av_frame_);
208 }
209
210 void FFmpegVideoDecoder::DecodeBuffer(
211 const scoped_refptr<DecoderBuffer>& buffer) {
212 DCHECK(task_runner_->BelongsToCurrentThread());
213 DCHECK_NE(state_, kUninitialized);
214 DCHECK_NE(state_, kDecodeFinished);
215 DCHECK_NE(state_, kError);
216 DCHECK(!decode_cb_.is_null());
217 DCHECK(buffer);
218 187
219 // During decode, because reads are issued asynchronously, it is possible to 188 // During decode, because reads are issued asynchronously, it is possible to
220 // receive multiple end of stream buffers since each decode is acked. When the 189 // receive multiple end of stream buffers since each decode is acked. When the
221 // first end of stream buffer is read, FFmpeg may still have frames queued 190 // first end of stream buffer is read, FFmpeg may still have frames queued
222 // up in the decoder so we need to go through the decode loop until it stops 191 // up in the decoder so we need to go through the decode loop until it stops
223 // giving sensible data. After that, the decoder should output empty 192 // giving sensible data. After that, the decoder should output empty
224 // frames. There are three states the decoder can be in: 193 // frames. There are three states the decoder can be in:
225 // 194 //
226 // kNormal: This is the starting state. Buffers are decoded. Decode errors 195 // kNormal: This is the starting state. Buffers are decoded. Decode errors
227 // are discarded. 196 // are discarded.
228 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2
229 // until no more data is returned to flush out remaining
230 // frames. The input buffer is ignored at this point.
231 // kDecodeFinished: All calls return empty frames. 197 // kDecodeFinished: All calls return empty frames.
232 // kError: Unexpected error happened. 198 // kError: Unexpected error happened.
233 // 199 //
234 // These are the possible state transitions. 200 // These are the possible state transitions.
235 // 201 //
236 // kNormal -> kFlushCodec: 202 // kNormal -> kDecodeFinished:
237 // When buffer->end_of_stream() is first true. 203 // When EOS buffer is received and the codec has been flushed.
238 // kNormal -> kError: 204 // kNormal -> kError:
239 // A decoding error occurs and decoding needs to stop. 205 // A decoding error occurs and decoding needs to stop.
240 // kFlushCodec -> kDecodeFinished:
241 // When avcodec_decode_video2() returns 0 data.
242 // kFlushCodec -> kError:
243 // When avcodec_decode_video2() errors out.
244 // (any state) -> kNormal: 206 // (any state) -> kNormal:
245 // Any time Reset() is called. 207 // Any time Reset() is called.
246 208
247 // Transition to kFlushCodec on the first end of stream buffer. 209 bool has_produced_frame;
248 if (state_ == kNormal && buffer->end_of_stream()) { 210 do {
249 state_ = kFlushCodec; 211 has_produced_frame = false;
212 if (!FFmpegDecode(buffer, &has_produced_frame)) {
213 state_ = kError;
214 decode_cb_bound.Run(kDecodeError);
215 return;
216 }
217 // Repeat to flush the decoder after receiving EOS buffer.
218 } while (buffer->end_of_stream() && has_produced_frame);
219
220 if (buffer->end_of_stream()) {
221 output_cb_.Run(VideoFrame::CreateEOSFrame());
222 state_ = kDecodeFinished;
250 } 223 }
251 224
252 scoped_refptr<VideoFrame> video_frame; 225 decode_cb_bound.Run(kOk);
253 if (!FFmpegDecode(buffer, &video_frame)) { 226 }
254 state_ = kError; 227
255 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)
256 return; 240 return;
257 }
258 241
259 if (!video_frame.get()) { 242 ReleaseFFmpegResources();
260 if (state_ == kFlushCodec) { 243 state_ = kUninitialized;
261 DCHECK(buffer->end_of_stream()); 244 }
262 state_ = kDecodeFinished;
263 base::ResetAndReturn(&decode_cb_)
264 .Run(kOk, VideoFrame::CreateEOSFrame());
265 return;
266 }
267 245
268 base::ResetAndReturn(&decode_cb_).Run(kNotEnoughData, NULL); 246 FFmpegVideoDecoder::~FFmpegVideoDecoder() {
269 return; 247 DCHECK_EQ(kUninitialized, state_);
270 } 248 DCHECK(!codec_context_);
271 249 DCHECK(!av_frame_);
272 base::ResetAndReturn(&decode_cb_).Run(kOk, video_frame);
273 } 250 }
274 251
275 bool FFmpegVideoDecoder::FFmpegDecode( 252 bool FFmpegVideoDecoder::FFmpegDecode(
276 const scoped_refptr<DecoderBuffer>& buffer, 253 const scoped_refptr<DecoderBuffer>& buffer,
277 scoped_refptr<VideoFrame>* video_frame) { 254 bool* has_produced_frame) {
278 DCHECK(video_frame); 255 DCHECK(!*has_produced_frame);
279 256
280 // Create a packet for input data. 257 // Create a packet for input data.
281 // Due to FFmpeg API changes we no longer have const read-only pointers. 258 // Due to FFmpeg API changes we no longer have const read-only pointers.
282 AVPacket packet; 259 AVPacket packet;
283 av_init_packet(&packet); 260 av_init_packet(&packet);
284 if (buffer->end_of_stream()) { 261 if (buffer->end_of_stream()) {
285 packet.data = NULL; 262 packet.data = NULL;
286 packet.size = 0; 263 packet.size = 0;
287 } else { 264 } else {
288 packet.data = const_cast<uint8*>(buffer->data()); 265 packet.data = const_cast<uint8*>(buffer->data());
289 packet.size = buffer->data_size(); 266 packet.size = buffer->data_size();
290 267
291 // Let FFmpeg handle presentation timestamp reordering. 268 // Let FFmpeg handle presentation timestamp reordering.
292 codec_context_->reordered_opaque = buffer->timestamp().InMicroseconds(); 269 codec_context_->reordered_opaque = buffer->timestamp().InMicroseconds();
293 } 270 }
294 271
295 int frame_decoded = 0; 272 int frame_decoded = 0;
296 int result = avcodec_decode_video2(codec_context_.get(), 273 int result = avcodec_decode_video2(codec_context_.get(),
297 av_frame_.get(), 274 av_frame_.get(),
298 &frame_decoded, 275 &frame_decoded,
299 &packet); 276 &packet);
300 // Log the problem if we can't decode a video frame and exit early. 277 // Log the problem if we can't decode a video frame and exit early.
301 if (result < 0) { 278 if (result < 0) {
302 LOG(ERROR) << "Error decoding video: " << buffer->AsHumanReadableString(); 279 LOG(ERROR) << "Error decoding video: " << buffer->AsHumanReadableString();
303 *video_frame = NULL;
304 return false; 280 return false;
305 } 281 }
306 282
307 // FFmpeg says some codecs might have multiple frames per packet. Previous 283 // FFmpeg says some codecs might have multiple frames per packet. Previous
308 // discussions with rbultje@ indicate this shouldn't be true for the codecs 284 // discussions with rbultje@ indicate this shouldn't be true for the codecs
309 // we use. 285 // we use.
310 DCHECK_EQ(result, packet.size); 286 DCHECK_EQ(result, packet.size);
311 287
312 // If no frame was produced then signal that more data is required to 288 // If no frame was produced then signal that more data is required to
313 // produce more frames. This can happen under two circumstances: 289 // produce more frames. This can happen under two circumstances:
314 // 1) Decoder was recently initialized/flushed 290 // 1) Decoder was recently initialized/flushed
315 // 2) End of stream was reached and all internal frames have been output 291 // 2) End of stream was reached and all internal frames have been output
316 if (frame_decoded == 0) { 292 if (frame_decoded == 0) {
317 *video_frame = NULL;
318 return true; 293 return true;
319 } 294 }
320 295
321 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 296 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675
322 // The decoder is in a bad state and not decoding correctly. 297 // The decoder is in a bad state and not decoding correctly.
323 // Checking for NULL avoids a crash in CopyPlane(). 298 // Checking for NULL avoids a crash in CopyPlane().
324 if (!av_frame_->data[VideoFrame::kYPlane] || 299 if (!av_frame_->data[VideoFrame::kYPlane] ||
325 !av_frame_->data[VideoFrame::kUPlane] || 300 !av_frame_->data[VideoFrame::kUPlane] ||
326 !av_frame_->data[VideoFrame::kVPlane]) { 301 !av_frame_->data[VideoFrame::kVPlane]) {
327 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; 302 LOG(ERROR) << "Video frame was produced yet has invalid frame data.";
328 *video_frame = NULL;
329 av_frame_unref(av_frame_.get()); 303 av_frame_unref(av_frame_.get());
330 return false; 304 return false;
331 } 305 }
332 306
333 *video_frame = 307 scoped_refptr<VideoFrame> frame =
334 reinterpret_cast<VideoFrame*>(av_buffer_get_opaque(av_frame_->buf[0])); 308 reinterpret_cast<VideoFrame*>(av_buffer_get_opaque(av_frame_->buf[0]));
335 309 frame->set_timestamp(
336 (*video_frame)->set_timestamp(
337 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); 310 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque));
311 *has_produced_frame = true;
312 output_cb_.Run(frame);
338 313
339 av_frame_unref(av_frame_.get()); 314 av_frame_unref(av_frame_.get());
340 return true; 315 return true;
341 } 316 }
342 317
343 void FFmpegVideoDecoder::ReleaseFFmpegResources() { 318 void FFmpegVideoDecoder::ReleaseFFmpegResources() {
344 codec_context_.reset(); 319 codec_context_.reset();
345 av_frame_.reset(); 320 av_frame_.reset();
346 } 321 }
347 322
(...skipping 16 matching lines...) Expand all
364 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { 339 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) {
365 ReleaseFFmpegResources(); 340 ReleaseFFmpegResources();
366 return false; 341 return false;
367 } 342 }
368 343
369 av_frame_.reset(av_frame_alloc()); 344 av_frame_.reset(av_frame_alloc());
370 return true; 345 return true;
371 } 346 }
372 347
373 } // namespace media 348 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698