OLD | NEW |
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 Loading... |
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 |
OLD | NEW |