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

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

Issue 1669002: remove AVFrame Dependency (Closed)
Patch Set: more patch Created 10 years, 8 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
« no previous file with comments | « media/filters/video_decoder_impl.h ('k') | media/filters/video_decoder_impl_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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/video_decoder_impl.h" 5 #include "media/filters/video_decoder_impl.h"
6 6
7 #include "base/task.h" 7 #include "base/task.h"
8 #include "media/base/filters.h" 8 #include "media/base/filters.h"
9 #include "media/base/limits.h" 9 #include "media/base/limits.h"
10 #include "media/base/video_frame.h" 10 #include "media/base/video_frame.h"
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 // not yet received an end of stream buffer. It is important that this line 132 // not yet received an end of stream buffer. It is important that this line
133 // stay below the state transition into kFlushCodec done above. 133 // stay below the state transition into kFlushCodec done above.
134 // 134 //
135 // TODO(ajwong): This push logic, along with the pop logic below needs to 135 // TODO(ajwong): This push logic, along with the pop logic below needs to
136 // be reevaluated to correctly handle decode errors. 136 // be reevaluated to correctly handle decode errors.
137 if (state_ == kNormal) { 137 if (state_ == kNormal) {
138 pts_heap_.Push(buffer->GetTimestamp()); 138 pts_heap_.Push(buffer->GetTimestamp());
139 } 139 }
140 140
141 // Otherwise, attempt to decode a single frame. 141 // Otherwise, attempt to decode a single frame.
142 AVFrame* yuv_frame = avcodec_alloc_frame();
143 bool* got_frame = new bool; 142 bool* got_frame = new bool;
143 scoped_refptr<VideoFrame>* video_frame = new scoped_refptr<VideoFrame>(NULL);
144 decode_engine_->DecodeFrame( 144 decode_engine_->DecodeFrame(
145 buffer, 145 buffer,
146 yuv_frame, 146 video_frame,
147 got_frame, 147 got_frame,
148 NewRunnableMethod(this, 148 NewRunnableMethod(this,
149 &VideoDecoderImpl::OnDecodeComplete, 149 &VideoDecoderImpl::OnDecodeComplete,
150 yuv_frame, 150 video_frame,
151 got_frame, 151 got_frame,
152 done_runner.release())); 152 done_runner.release()));
153 } 153 }
154 154
155 void VideoDecoderImpl::OnDecodeComplete(AVFrame* yuv_frame, bool* got_frame, 155 void VideoDecoderImpl::OnDecodeComplete(scoped_refptr<VideoFrame>* video_frame,
156 Task* done_cb) { 156 bool* got_frame, Task* done_cb) {
157 // Note: The |done_runner| must be declared *last* to ensure proper 157 // Note: The |done_runner| must be declared *last* to ensure proper
158 // destruction order. 158 // destruction order.
159 scoped_ptr_malloc<AVFrame, ScopedPtrAVFree> yuv_frame_deleter(yuv_frame);
160 scoped_ptr<bool> got_frame_deleter(got_frame); 159 scoped_ptr<bool> got_frame_deleter(got_frame);
160 scoped_ptr<scoped_refptr<VideoFrame> > video_frame_deleter(video_frame);
161 AutoTaskRunner done_runner(done_cb); 161 AutoTaskRunner done_runner(done_cb);
162 162
163 // If we actually got data back, enqueue a frame. 163 // If we actually got data back, enqueue a frame.
164 if (*got_frame) { 164 if (*got_frame) {
165 last_pts_ = FindPtsAndDuration(*time_base_, pts_heap_, last_pts_, 165 last_pts_ = FindPtsAndDuration(*time_base_, pts_heap_, last_pts_,
166 yuv_frame); 166 video_frame->get());
167 167
168 // Pop off a pts on a successful decode since we are "using up" one 168 // Pop off a pts on a successful decode since we are "using up" one
169 // timestamp. 169 // timestamp.
170 // 170 //
171 // TODO(ajwong): Do we need to pop off a pts when avcodec_decode_video2() 171 // TODO(ajwong): Do we need to pop off a pts when avcodec_decode_video2()
172 // returns < 0? The rationale is that when get_picture_ptr == 0, we skip 172 // returns < 0? The rationale is that when get_picture_ptr == 0, we skip
173 // popping a pts because no frame was produced. However, when 173 // popping a pts because no frame was produced. However, when
174 // avcodec_decode_video2() returns false, it is a decode error, which 174 // avcodec_decode_video2() returns false, it is a decode error, which
175 // if it means a frame is dropped, may require us to pop one more time. 175 // if it means a frame is dropped, may require us to pop one more time.
176 if (!pts_heap_.IsEmpty()) { 176 if (!pts_heap_.IsEmpty()) {
177 pts_heap_.Pop(); 177 pts_heap_.Pop();
178 } else { 178 } else {
179 NOTREACHED() << "Attempting to decode more frames than were input."; 179 NOTREACHED() << "Attempting to decode more frames than were input.";
180 } 180 }
181 181
182 if (!EnqueueVideoFrame( 182 (*video_frame)->SetTimestamp(last_pts_.timestamp);
183 decode_engine_->GetSurfaceFormat(), last_pts_, yuv_frame)) { 183 (*video_frame)->SetDuration(last_pts_.duration);
184 // On an EnqueueEmptyFrame error, error out the whole pipeline and 184 EnqueueVideoFrame(*video_frame);
185 // set the state to kDecodeFinished.
186 SignalPipelineError();
187 }
188 } else { 185 } else {
189 // When in kFlushCodec, any errored decode, or a 0-lengthed frame, 186 // When in kFlushCodec, any errored decode, or a 0-lengthed frame,
190 // is taken as a signal to stop decoding. 187 // is taken as a signal to stop decoding.
191 if (state_ == kFlushCodec) { 188 if (state_ == kFlushCodec) {
192 state_ = kDecodeFinished; 189 state_ = kDecodeFinished;
193 EnqueueEmptyFrame(); 190 EnqueueEmptyFrame();
194 } 191 }
195 } 192 }
196 } 193 }
197 194
198 bool VideoDecoderImpl::EnqueueVideoFrame(VideoFrame::Format surface_format, 195 void VideoDecoderImpl::EnqueueVideoFrame(
199 const TimeTuple& time, 196 const scoped_refptr<VideoFrame>& video_frame) {
200 const AVFrame* frame) {
201 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675
202 // The decoder is in a bad state and not decoding correctly.
203 // Checking for NULL avoids a crash in CopyPlane().
204 if (!frame->data[VideoFrame::kYPlane] ||
205 !frame->data[VideoFrame::kUPlane] ||
206 !frame->data[VideoFrame::kVPlane]) {
207 return true;
208 }
209
210 scoped_refptr<VideoFrame> video_frame;
211 VideoFrame::CreateFrame(surface_format, width_, height_,
212 time.timestamp, time.duration, &video_frame);
213 if (!video_frame) {
214 return false;
215 }
216
217 // Copy the frame data since FFmpeg reuses internal buffers for AVFrame
218 // output, meaning the data is only valid until the next
219 // avcodec_decode_video() call.
220 // TODO(scherkus): figure out pre-allocation/buffer cycling scheme.
221 // TODO(scherkus): is there a cleaner way to figure out the # of planes?
222 CopyPlane(VideoFrame::kYPlane, *video_frame, frame);
223 CopyPlane(VideoFrame::kUPlane, *video_frame, frame);
224 CopyPlane(VideoFrame::kVPlane, *video_frame, frame);
225 EnqueueResult(video_frame); 197 EnqueueResult(video_frame);
226 return true;
227 }
228
229 void VideoDecoderImpl::CopyPlane(size_t plane,
230 const VideoFrame& video_frame,
231 const AVFrame* frame) {
232 DCHECK(video_frame.width() % 2 == 0);
233 const uint8* source = frame->data[plane];
234 const size_t source_stride = frame->linesize[plane];
235 uint8* dest = video_frame.data(plane);
236 const size_t dest_stride = video_frame.stride(plane);
237 size_t bytes_per_line = video_frame.width();
238 size_t copy_lines = video_frame.height();
239 if (plane != VideoFrame::kYPlane) {
240 bytes_per_line /= 2;
241 if (video_frame.format() == VideoFrame::YV12) {
242 copy_lines = (copy_lines + 1) / 2;
243 }
244 }
245 DCHECK(bytes_per_line <= source_stride && bytes_per_line <= dest_stride);
246 for (size_t i = 0; i < copy_lines; ++i) {
247 memcpy(dest, source, bytes_per_line);
248 source += source_stride;
249 dest += dest_stride;
250 }
251 } 198 }
252 199
253 void VideoDecoderImpl::EnqueueEmptyFrame() { 200 void VideoDecoderImpl::EnqueueEmptyFrame() {
254 scoped_refptr<VideoFrame> video_frame; 201 scoped_refptr<VideoFrame> video_frame;
255 VideoFrame::CreateEmptyFrame(&video_frame); 202 VideoFrame::CreateEmptyFrame(&video_frame);
256 EnqueueResult(video_frame); 203 EnqueueResult(video_frame);
257 } 204 }
258 205
259 VideoDecoderImpl::TimeTuple VideoDecoderImpl::FindPtsAndDuration( 206 VideoDecoderImpl::TimeTuple VideoDecoderImpl::FindPtsAndDuration(
260 const AVRational& time_base, 207 const AVRational& time_base,
261 const PtsHeap& pts_heap, 208 const PtsHeap& pts_heap,
262 const TimeTuple& last_pts, 209 const TimeTuple& last_pts,
263 const AVFrame* frame) { 210 const VideoFrame* frame) {
264 TimeTuple pts; 211 TimeTuple pts;
265 212
266 // Default |repeat_pict| to 0 because if there is no frame information, 213 // First search the VideoFrame for the pts. This is the most authoritative.
267 // we just assume the frame only plays for one time_base. 214 // Make a special exclusion for the value pts == 0. Though this is
268 int repeat_pict = 0; 215 // technically a valid value, it seems a number of ffmpeg codecs will
216 // mistakenly always set pts to 0.
217 DCHECK(frame);
218 base::TimeDelta timestamp = frame->GetTimestamp();
219 if (timestamp != StreamSample::kInvalidTimestamp &&
220 timestamp.ToInternalValue() != 0) {
221 pts.timestamp = ConvertTimestamp(time_base, timestamp.ToInternalValue());
222 pts.duration = ConvertTimestamp(time_base, 1 + frame->GetRepeatCount());
223 return pts;
224 }
269 225
270 // First search the AVFrame for the pts. This is the most authoritative. 226 if (!pts_heap.IsEmpty()) {
271 // Make a special exclusion for the value frame->pts == 0. Though this 227 // If the frame did not have pts, try to get the pts from the |pts_heap|.
272 // is technically a valid value, it seems a number of ffmpeg codecs will
273 // mistakenly always set frame->pts to 0.
274 //
275 // Oh, and we have to cast AV_NOPTS_VALUE since it ends up becoming unsigned
276 // because the value they use doesn't fit in a signed 64-bit number which
277 // produces a signedness comparison warning on gcc.
278 if (frame &&
279 (frame->pts != static_cast<int64_t>(AV_NOPTS_VALUE)) &&
280 (frame->pts != 0)) {
281 pts.timestamp = ConvertTimestamp(time_base, frame->pts);
282 repeat_pict = frame->repeat_pict;
283 } else if (!pts_heap.IsEmpty()) {
284 // If the frame did not have pts, try to get the pts from the
285 // |pts_heap|.
286 pts.timestamp = pts_heap.Top(); 228 pts.timestamp = pts_heap.Top();
287 } else { 229 } else {
288 DCHECK(last_pts.timestamp != StreamSample::kInvalidTimestamp); 230 DCHECK(last_pts.timestamp != StreamSample::kInvalidTimestamp);
289 DCHECK(last_pts.duration != StreamSample::kInvalidTimestamp); 231 DCHECK(last_pts.duration != StreamSample::kInvalidTimestamp);
290 // Unable to read the pts from anywhere. Time to guess. 232 // Unable to read the pts from anywhere. Time to guess.
291 pts.timestamp = last_pts.timestamp + last_pts.duration; 233 pts.timestamp = last_pts.timestamp + last_pts.duration;
292 } 234 }
293 235
294 // Fill in the duration while accounting for repeated frames. 236 // Fill in the duration while accounting for repeated frames.
295 // 237 pts.duration = ConvertTimestamp(time_base, 1);
296 // TODO(ajwong): Make sure this formula is correct.
297 pts.duration = ConvertTimestamp(time_base, 1 + repeat_pict);
298 238
299 return pts; 239 return pts;
300 } 240 }
301 241
302 void VideoDecoderImpl::SignalPipelineError() { 242 void VideoDecoderImpl::SignalPipelineError() {
303 host()->SetError(PIPELINE_ERROR_DECODE); 243 host()->SetError(PIPELINE_ERROR_DECODE);
304 state_ = kDecodeFinished; 244 state_ = kDecodeFinished;
305 } 245 }
306 246
307 void VideoDecoderImpl::SetVideoDecodeEngineForTest( 247 void VideoDecoderImpl::SetVideoDecodeEngineForTest(
308 VideoDecodeEngine* engine) { 248 VideoDecodeEngine* engine) {
309 decode_engine_.reset(engine); 249 decode_engine_.reset(engine);
310 } 250 }
311 251
312 } // namespace media 252 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/video_decoder_impl.h ('k') | media/filters/video_decoder_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698