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

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 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698