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

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

Issue 10451051: Provide a Chrome-owned buffer to FFmpeg for video decoding, instead of (Closed) Base URL: https://src.chromium.org/chrome/trunk/src/
Patch Set: Created 8 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
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 "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback_helpers.h" 8 #include "base/callback_helpers.h"
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/message_loop.h" 10 #include "base/message_loop.h"
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 const base::Callback<MessageLoop*()>& message_loop_cb) 52 const base::Callback<MessageLoop*()>& message_loop_cb)
53 : message_loop_factory_cb_(message_loop_cb), 53 : message_loop_factory_cb_(message_loop_cb),
54 message_loop_(NULL), 54 message_loop_(NULL),
55 state_(kUninitialized), 55 state_(kUninitialized),
56 codec_context_(NULL), 56 codec_context_(NULL),
57 av_frame_(NULL), 57 av_frame_(NULL),
58 frame_rate_numerator_(0), 58 frame_rate_numerator_(0),
59 frame_rate_denominator_(0) { 59 frame_rate_denominator_(0) {
60 } 60 }
61 61
62 int FFmpegVideoDecoder::AllocBuffer(VideoFrame **ptr)
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 no doco, no unittest, single call-site ==> inline
63 {
64 VideoFrame::Format format = PixelFormatToVideoFormat(codec_context_->pix_fmt);
65 int w = codec_context_->width;
66 int h = codec_context_->height;
67 int ret, strides[4];
68 avcodec_align_dimensions(codec_context_, &w, &h);
69 if ((ret = av_image_check_size(w, h, 0, NULL)) < 0)
70 return ret;
71 if ((ret = av_image_fill_linesizes(strides, codec_context_->pix_fmt, w)) < 0)
72 return ret;
73 VideoFrame *buf = new VideoFrame::VideoFrame(format, codec_context_->width,
74 codec_context_->height,
75 kNoTimestamp(), kNoTimestamp());
76 if ((ret = buf->AllocateAlignedWithStrides(strides, h)) < 0) {
77 free(buf);
78 return ret;
79 }
80 *ptr = buf;
81 return 0;
82 }
83
84 int FFmpegVideoDecoder::GetVideoBuffer(AVFrame *frame)
85 {
86 VideoFrame *buf;
87 int ret = AllocBuffer(&buf);
88 if (ret < 0)
89 return ret;
90
91 buf->AddRef();
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 AddRef() is almost never the right thing to call.
92 frame->opaque = buf;
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 This sort of ascii-art alignment isn't adding anyt
93 frame->type = FF_BUFFER_TYPE_USER;
94 frame->pkt_pts = codec_context_->pkt ? codec_context_->pkt->pts : AV_NOP TS_VALUE;
95 frame->width = codec_context_->width;
96 frame->height = codec_context_->height;
97 frame->format = codec_context_->pix_fmt;
98
99 for (int i = 0; i < (buf->format() == media::VideoFrame::RGB32 ? 1 : 3); i++) {
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 I think you're assuming either RGB32 or YV12/YV16.
100 frame->base[i] =
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 Un-style-ish. Repeat buf->data(i)
101 frame->data[i] = buf->data(i);
102 frame->linesize[i] = buf->stride(i);
103 }
104
105 return 0;
106 }
107
108 static int c_GetVideoBuffer(AVCodecContext *s, AVFrame *frame)
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 the "c_" prefix is un-style-ish.
109 {
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 opening braces go on previous line (here and elsew
110 FFmpegVideoDecoder *vd = (FFmpegVideoDecoder *) s->opaque;
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 This isn't used until l.114, so it can be done the
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 use static_cast<>() instead of c-style casts (here
111 if (!(s->flags & CODEC_FLAG_EMU_EDGE)) {
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 When will this be true? (can you drop the "then" c
112 return avcodec_default_get_buffer(s, frame);
113 } else {
114 return vd->GetVideoBuffer(frame);
115 }
116 }
117
118 static void c_ReleaseVideoBuffer(AVCodecContext *s, AVFrame *frame)
119 {
120 if (!(s->flags & CODEC_FLAG_EMU_EDGE)) {
121 avcodec_default_release_buffer(s, frame);
122 } else {
123 VideoFrame *buf = (media::VideoFrame *) frame->opaque;
124 buf->Release();
125 memset(frame->data, 0, sizeof(frame->data));
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 why? (also, do you really mean to only set the fir
126 frame->opaque = NULL;
127 }
128 }
129
62 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, 130 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream,
63 const PipelineStatusCB& status_cb, 131 const PipelineStatusCB& status_cb,
64 const StatisticsCB& statistics_cb) { 132 const StatisticsCB& statistics_cb) {
65 if (!message_loop_) { 133 if (!message_loop_) {
66 message_loop_ = message_loop_factory_cb_.Run(); 134 message_loop_ = message_loop_factory_cb_.Run();
67 message_loop_factory_cb_.Reset(); 135 message_loop_factory_cb_.Reset();
68 136
69 message_loop_->PostTask(FROM_HERE, base::Bind( 137 message_loop_->PostTask(FROM_HERE, base::Bind(
70 &FFmpegVideoDecoder::Initialize, this, 138 &FFmpegVideoDecoder::Initialize, this,
71 stream, status_cb, statistics_cb)); 139 stream, status_cb, statistics_cb));
(...skipping 23 matching lines...) Expand all
95 163
96 // Initialize AVCodecContext structure. 164 // Initialize AVCodecContext structure.
97 codec_context_ = avcodec_alloc_context(); 165 codec_context_ = avcodec_alloc_context();
98 VideoDecoderConfigToAVCodecContext(config, codec_context_); 166 VideoDecoderConfigToAVCodecContext(config, codec_context_);
99 167
100 // Enable motion vector search (potentially slow), strong deblocking filter 168 // Enable motion vector search (potentially slow), strong deblocking filter
101 // for damaged macroblocks, and set our error detection sensitivity. 169 // for damaged macroblocks, and set our error detection sensitivity.
102 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; 170 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
103 codec_context_->err_recognition = AV_EF_CAREFUL; 171 codec_context_->err_recognition = AV_EF_CAREFUL;
104 codec_context_->thread_count = GetThreadCount(codec_context_->codec_id); 172 codec_context_->thread_count = GetThreadCount(codec_context_->codec_id);
173 codec_context_->opaque = this;
174 codec_context_->flags |= CODEC_FLAG_EMU_EDGE;
175 codec_context_->get_buffer = c_GetVideoBuffer;
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 Depending on ffmpeg dispatches these callbacks, it
176 codec_context_->release_buffer = c_ReleaseVideoBuffer;
105 177
106 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); 178 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
107 if (!codec) { 179 if (!codec) {
108 status_cb.Run(PIPELINE_ERROR_DECODE); 180 status_cb.Run(PIPELINE_ERROR_DECODE);
109 return; 181 return;
110 } 182 }
111 183
112 if (avcodec_open2(codec_context_, codec, NULL) < 0) { 184 if (avcodec_open2(codec_context_, codec, NULL) < 0) {
113 status_cb.Run(PIPELINE_ERROR_DECODE); 185 status_cb.Run(PIPELINE_ERROR_DECODE);
114 return; 186 return;
(...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after
349 // The decoder is in a bad state and not decoding correctly. 421 // The decoder is in a bad state and not decoding correctly.
350 // Checking for NULL avoids a crash in CopyPlane(). 422 // Checking for NULL avoids a crash in CopyPlane().
351 if (!av_frame_->data[VideoFrame::kYPlane] || 423 if (!av_frame_->data[VideoFrame::kYPlane] ||
352 !av_frame_->data[VideoFrame::kUPlane] || 424 !av_frame_->data[VideoFrame::kUPlane] ||
353 !av_frame_->data[VideoFrame::kVPlane]) { 425 !av_frame_->data[VideoFrame::kVPlane]) {
354 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; 426 LOG(ERROR) << "Video frame was produced yet has invalid frame data.";
355 *video_frame = NULL; 427 *video_frame = NULL;
356 return false; 428 return false;
357 } 429 }
358 430
359 // We've got a frame! Make sure we have a place to store it. 431 if (!(codec_context_->flags & CODEC_FLAG_EMU_EDGE)) {
Ami GONE FROM CHROMIUM 2012/05/26 22:52:34 Drop this test here and elsewhere?
360 *video_frame = AllocateVideoFrame(); 432 // We've got a frame! Make sure we have a place to store it.
361 if (!(*video_frame)) { 433 *video_frame = AllocateVideoFrame();
362 LOG(ERROR) << "Failed to allocate video frame"; 434 if (!(*video_frame)) {
363 return false; 435 LOG(ERROR) << "Failed to allocate video frame";
436 return false;
437 }
438 } else {
439 *video_frame = (media::VideoFrame *) av_frame_->opaque;
364 } 440 }
365 441
366 // Determine timestamp and calculate the duration based on the repeat picture 442 // Determine timestamp and calculate the duration based on the repeat picture
367 // count. According to FFmpeg docs, the total duration can be calculated as 443 // count. According to FFmpeg docs, the total duration can be calculated as
368 // follows: 444 // follows:
369 // fps = 1 / time_base 445 // fps = 1 / time_base
370 // 446 //
371 // duration = (1 / fps) + (repeat_pict) / (2 * fps) 447 // duration = (1 / fps) + (repeat_pict) / (2 * fps)
372 // = (2 + repeat_pict) / (2 * fps) 448 // = (2 + repeat_pict) / (2 * fps)
373 // = (2 + repeat_pict) / (2 * (1 / time_base)) 449 // = (2 + repeat_pict) / (2 * (1 / time_base))
374 DCHECK_LE(av_frame_->repeat_pict, 2); // Sanity check. 450 DCHECK_LE(av_frame_->repeat_pict, 2); // Sanity check.
375 AVRational doubled_time_base; 451 AVRational doubled_time_base;
376 doubled_time_base.num = frame_rate_denominator_; 452 doubled_time_base.num = frame_rate_denominator_;
377 doubled_time_base.den = frame_rate_numerator_ * 2; 453 doubled_time_base.den = frame_rate_numerator_ * 2;
378 454
379 (*video_frame)->SetTimestamp( 455 (*video_frame)->SetTimestamp(
380 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); 456 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque));
381 (*video_frame)->SetDuration( 457 (*video_frame)->SetDuration(
382 ConvertFromTimeBase(doubled_time_base, 2 + av_frame_->repeat_pict)); 458 ConvertFromTimeBase(doubled_time_base, 2 + av_frame_->repeat_pict));
383 459
384 // Copy the frame data since FFmpeg reuses internal buffers for AVFrame 460 if (!(codec_context_->flags & CODEC_FLAG_EMU_EDGE)) {
385 // output, meaning the data is only valid until the next 461 // Copy the frame data since FFmpeg reuses internal buffers for AVFrame
386 // avcodec_decode_video() call. 462 // output, meaning the data is only valid until the next
387 int y_rows = codec_context_->height; 463 // avcodec_decode_video() call.
388 int uv_rows = codec_context_->height; 464 int y_rows = codec_context_->height;
389 if (codec_context_->pix_fmt == PIX_FMT_YUV420P) { 465 int uv_rows = codec_context_->height;
390 uv_rows /= 2; 466 if (codec_context_->pix_fmt == PIX_FMT_YUV420P) {
467 uv_rows /= 2;
468 }
469
470 CopyYPlane(av_frame_->data[0], av_frame_->linesize[0], y_rows, *video_frame) ;
471 CopyUPlane(av_frame_->data[1], av_frame_->linesize[1], uv_rows, *video_frame );
472 CopyVPlane(av_frame_->data[2], av_frame_->linesize[2], uv_rows, *video_frame );
391 } 473 }
392 474
393 CopyYPlane(av_frame_->data[0], av_frame_->linesize[0], y_rows, *video_frame);
394 CopyUPlane(av_frame_->data[1], av_frame_->linesize[1], uv_rows, *video_frame);
395 CopyVPlane(av_frame_->data[2], av_frame_->linesize[2], uv_rows, *video_frame);
396
397 return true; 475 return true;
398 } 476 }
399 477
400 void FFmpegVideoDecoder::DeliverFrame( 478 void FFmpegVideoDecoder::DeliverFrame(
401 const scoped_refptr<VideoFrame>& video_frame) { 479 const scoped_refptr<VideoFrame>& video_frame) {
402 // Reset the callback before running to protect against reentrancy. 480 // Reset the callback before running to protect against reentrancy.
403 base::ResetAndReturn(&read_cb_).Run(kOk, video_frame); 481 base::ResetAndReturn(&read_cb_).Run(kOk, video_frame);
404 } 482 }
405 483
406 void FFmpegVideoDecoder::ReleaseFFmpegResources() { 484 void FFmpegVideoDecoder::ReleaseFFmpegResources() {
(...skipping 12 matching lines...) Expand all
419 scoped_refptr<VideoFrame> FFmpegVideoDecoder::AllocateVideoFrame() { 497 scoped_refptr<VideoFrame> FFmpegVideoDecoder::AllocateVideoFrame() {
420 VideoFrame::Format format = PixelFormatToVideoFormat(codec_context_->pix_fmt); 498 VideoFrame::Format format = PixelFormatToVideoFormat(codec_context_->pix_fmt);
421 size_t width = codec_context_->width; 499 size_t width = codec_context_->width;
422 size_t height = codec_context_->height; 500 size_t height = codec_context_->height;
423 501
424 return VideoFrame::CreateFrame(format, width, height, 502 return VideoFrame::CreateFrame(format, width, height,
425 kNoTimestamp(), kNoTimestamp()); 503 kNoTimestamp(), kNoTimestamp());
426 } 504 }
427 505
428 } // namespace media 506 } // namespace media
OLDNEW
« media/filters/ffmpeg_video_decoder.h ('K') | « media/filters/ffmpeg_video_decoder.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698