OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/video/ffmpeg_video_decode_engine.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/logging.h" | |
9 #include "base/string_number_conversions.h" | |
10 #include "media/base/buffers.h" | |
11 #include "media/base/media_switches.h" | |
12 #include "media/base/video_decoder_config.h" | |
13 #include "media/base/video_util.h" | |
14 #include "media/ffmpeg/ffmpeg_common.h" | |
15 | |
16 namespace media { | |
17 | |
18 FFmpegVideoDecodeEngine::FFmpegVideoDecodeEngine() | |
19 : codec_context_(NULL), | |
20 av_frame_(NULL), | |
21 frame_rate_numerator_(0), | |
22 frame_rate_denominator_(0) { | |
23 } | |
24 | |
25 FFmpegVideoDecodeEngine::~FFmpegVideoDecodeEngine() { | |
26 Uninitialize(); | |
27 } | |
28 | |
29 bool FFmpegVideoDecodeEngine::Initialize(const VideoDecoderConfig& config) { | |
30 frame_rate_numerator_ = config.frame_rate_numerator(); | |
31 frame_rate_denominator_ = config.frame_rate_denominator(); | |
32 | |
33 // Always try to use three threads for video decoding. There is little reason | |
34 // not to since current day CPUs tend to be multi-core and we measured | |
35 // performance benefits on older machines such as P4s with hyperthreading. | |
36 // | |
37 // Handling decoding on separate threads also frees up the pipeline thread to | |
38 // continue processing. Although it'd be nice to have the option of a single | |
39 // decoding thread, FFmpeg treats having one thread the same as having zero | |
40 // threads (i.e., avcodec_decode_video() will execute on the calling thread). | |
41 // Yet another reason for having two threads :) | |
42 static const int kDecodeThreads = 2; | |
43 static const int kMaxDecodeThreads = 16; | |
44 | |
45 // Initialize AVCodecContext structure. | |
46 codec_context_ = avcodec_alloc_context(); | |
47 VideoDecoderConfigToAVCodecContext(config, codec_context_); | |
48 | |
49 // Enable motion vector search (potentially slow), strong deblocking filter | |
50 // for damaged macroblocks, and set our error detection sensitivity. | |
51 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; | |
52 codec_context_->error_recognition = FF_ER_CAREFUL; | |
53 | |
54 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | |
55 | |
56 // TODO(fbarchard): Improve thread logic based on size / codec. | |
57 // TODO(fbarchard): Fix bug affecting video-cookie.html | |
58 // 07/21/11(ihf): Still about 20 failures when enabling. | |
59 int decode_threads = (codec_context_->codec_id == CODEC_ID_THEORA) ? | |
60 1 : kDecodeThreads; | |
61 | |
62 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | |
63 std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads)); | |
64 if ((!threads.empty() && | |
65 !base::StringToInt(threads, &decode_threads)) || | |
66 decode_threads < 0 || decode_threads > kMaxDecodeThreads) { | |
67 decode_threads = kDecodeThreads; | |
68 } | |
69 | |
70 codec_context_->thread_count = decode_threads; | |
71 | |
72 av_frame_ = avcodec_alloc_frame(); | |
73 | |
74 // Open the codec! | |
75 return codec && avcodec_open(codec_context_, codec) >= 0; | |
76 } | |
77 | |
78 void FFmpegVideoDecodeEngine::Uninitialize() { | |
79 if (codec_context_) { | |
80 av_free(codec_context_->extradata); | |
81 avcodec_close(codec_context_); | |
82 av_free(codec_context_); | |
83 codec_context_ = NULL; | |
84 } | |
85 if (av_frame_) { | |
86 av_free(av_frame_); | |
87 av_frame_ = NULL; | |
88 } | |
89 frame_rate_numerator_ = 0; | |
90 frame_rate_denominator_ = 0; | |
91 } | |
92 | |
93 bool FFmpegVideoDecodeEngine::Decode(const scoped_refptr<Buffer>& buffer, | |
94 scoped_refptr<VideoFrame>* video_frame) { | |
95 DCHECK(video_frame); | |
96 | |
97 // Create a packet for input data. | |
98 // Due to FFmpeg API changes we no longer have const read-only pointers. | |
99 AVPacket packet; | |
100 av_init_packet(&packet); | |
101 packet.data = const_cast<uint8*>(buffer->GetData()); | |
102 packet.size = buffer->GetDataSize(); | |
103 | |
104 // Let FFmpeg handle presentation timestamp reordering. | |
105 codec_context_->reordered_opaque = buffer->GetTimestamp().InMicroseconds(); | |
106 | |
107 // This is for codecs not using get_buffer to initialize | |
108 // |av_frame_->reordered_opaque| | |
109 av_frame_->reordered_opaque = codec_context_->reordered_opaque; | |
110 | |
111 int frame_decoded = 0; | |
112 int result = avcodec_decode_video2(codec_context_, | |
113 av_frame_, | |
114 &frame_decoded, | |
115 &packet); | |
116 // Log the problem if we can't decode a video frame and exit early. | |
117 if (result < 0) { | |
118 LOG(ERROR) << "Error decoding a video frame with timestamp: " | |
119 << buffer->GetTimestamp().InMicroseconds() << " us, duration: " | |
120 << buffer->GetDuration().InMicroseconds() << " us, packet size: " | |
121 << buffer->GetDataSize() << " bytes"; | |
122 *video_frame = NULL; | |
123 return false; | |
124 } | |
125 | |
126 // If no frame was produced then signal that more data is required to | |
127 // produce more frames. This can happen under two circumstances: | |
128 // 1) Decoder was recently initialized/flushed | |
129 // 2) End of stream was reached and all internal frames have been output | |
130 if (frame_decoded == 0) { | |
131 *video_frame = NULL; | |
132 return true; | |
133 } | |
134 | |
135 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 | |
136 // The decoder is in a bad state and not decoding correctly. | |
137 // Checking for NULL avoids a crash in CopyPlane(). | |
138 if (!av_frame_->data[VideoFrame::kYPlane] || | |
139 !av_frame_->data[VideoFrame::kUPlane] || | |
140 !av_frame_->data[VideoFrame::kVPlane]) { | |
141 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; | |
142 *video_frame = NULL; | |
143 return false; | |
144 } | |
145 | |
146 // We've got a frame! Make sure we have a place to store it. | |
147 *video_frame = AllocateVideoFrame(); | |
148 if (!(*video_frame)) { | |
149 LOG(ERROR) << "Failed to allocate video frame"; | |
150 return false; | |
151 } | |
152 | |
153 // Determine timestamp and calculate the duration based on the repeat picture | |
154 // count. According to FFmpeg docs, the total duration can be calculated as | |
155 // follows: | |
156 // fps = 1 / time_base | |
157 // | |
158 // duration = (1 / fps) + (repeat_pict) / (2 * fps) | |
159 // = (2 + repeat_pict) / (2 * fps) | |
160 // = (2 + repeat_pict) / (2 * (1 / time_base)) | |
161 DCHECK_LE(av_frame_->repeat_pict, 2); // Sanity check. | |
162 AVRational doubled_time_base; | |
163 doubled_time_base.num = frame_rate_denominator_; | |
164 doubled_time_base.den = frame_rate_numerator_ * 2; | |
165 | |
166 (*video_frame)->SetTimestamp( | |
167 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); | |
168 (*video_frame)->SetDuration( | |
169 ConvertFromTimeBase(doubled_time_base, 2 + av_frame_->repeat_pict)); | |
170 | |
171 // Copy the frame data since FFmpeg reuses internal buffers for AVFrame | |
172 // output, meaning the data is only valid until the next | |
173 // avcodec_decode_video() call. | |
174 int y_rows = codec_context_->height; | |
175 int uv_rows = codec_context_->height; | |
176 if (codec_context_->pix_fmt == PIX_FMT_YUV420P) { | |
177 uv_rows /= 2; | |
178 } | |
179 | |
180 CopyYPlane(av_frame_->data[0], av_frame_->linesize[0], y_rows, *video_frame); | |
181 CopyUPlane(av_frame_->data[1], av_frame_->linesize[1], uv_rows, *video_frame); | |
182 CopyVPlane(av_frame_->data[2], av_frame_->linesize[2], uv_rows, *video_frame); | |
183 | |
184 return true; | |
185 } | |
186 | |
187 void FFmpegVideoDecodeEngine::Flush() { | |
188 avcodec_flush_buffers(codec_context_); | |
189 } | |
190 | |
191 scoped_refptr<VideoFrame> FFmpegVideoDecodeEngine::AllocateVideoFrame() { | |
192 VideoFrame::Format format = PixelFormatToVideoFormat(codec_context_->pix_fmt); | |
193 size_t width = codec_context_->width; | |
194 size_t height = codec_context_->height; | |
195 | |
196 return VideoFrame::CreateFrame(format, width, height, | |
197 kNoTimestamp, kNoTimestamp); | |
198 } | |
199 | |
200 } // namespace media | |
OLD | NEW |