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

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

Issue 2854002: merge video_decoder_impl.cc and ffmpeg_video_decoder.cc (Closed)
Patch Set: remove comments Created 10 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
« no previous file with comments | « media/filters/video_decoder_impl.h ('k') | media/media.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/filters/video_decoder_impl.h"
6
7 #include "base/task.h"
8 #include "media/base/filters.h"
9 #include "media/base/limits.h"
10 #include "media/base/video_frame.h"
11 #include "media/ffmpeg/ffmpeg_common.h"
12 #include "media/filters/ffmpeg_interfaces.h"
13 #include "media/filters/video_decode_engine.h"
14 #include "media/ffmpeg/ffmpeg_util.h"
15
16 namespace media {
17
18 VideoDecoderImpl::VideoDecoderImpl(VideoDecodeEngine* engine)
19 : width_(0),
20 height_(0),
21 time_base_(new AVRational()),
22 state_(kNormal),
23 decode_engine_(engine) {
24 }
25
26 VideoDecoderImpl::~VideoDecoderImpl() {
27 }
28
29 void VideoDecoderImpl::DoInitialize(DemuxerStream* demuxer_stream,
30 bool* success,
31 Task* done_cb) {
32 AutoTaskRunner done_runner(done_cb);
33 *success = false;
34
35 // Get the AVStream by querying for the provider interface.
36 AVStreamProvider* av_stream_provider;
37 if (!demuxer_stream->QueryInterface(&av_stream_provider)) {
38 return;
39 }
40 AVStream* av_stream = av_stream_provider->GetAVStream();
41
42 time_base_->den = av_stream->r_frame_rate.num;
43 time_base_->num = av_stream->r_frame_rate.den;
44
45 // TODO(ajwong): We don't need these extra variables if |media_format_| has
46 // them. Remove.
47 width_ = av_stream->codec->width;
48 height_ = av_stream->codec->height;
49 if (width_ > Limits::kMaxDimension ||
50 height_ > Limits::kMaxDimension ||
51 (width_ * height_) > Limits::kMaxCanvas) {
52 return;
53 }
54
55 // Only set kMimeType when derived class has not done so.
56 if (!media_format_.Contains(MediaFormat::kMimeType))
57 media_format_.SetAsString(MediaFormat::kMimeType,
58 mime_type::kUncompressedVideo);
59 media_format_.SetAsInteger(MediaFormat::kWidth, width_);
60 media_format_.SetAsInteger(MediaFormat::kHeight, height_);
61
62 decode_engine_->Initialize(
63 message_loop(),
64 av_stream,
65 NewCallback(this, &VideoDecoderImpl::OnEmptyBufferDone),
66 NewCallback(this, &VideoDecoderImpl::OnDecodeComplete),
67 NewRunnableMethod(this,
68 &VideoDecoderImpl::OnInitializeComplete,
69 success,
70 done_runner.release()));
71 }
72
73 void VideoDecoderImpl::OnInitializeComplete(bool* success, Task* done_cb) {
74 AutoTaskRunner done_runner(done_cb);
75
76 *success = decode_engine_->state() == VideoDecodeEngine::kNormal;
77 }
78
79 void VideoDecoderImpl::DoSeek(base::TimeDelta time, Task* done_cb) {
80 // Everything in the presentation time queue is invalid, clear the queue.
81 while (!pts_heap_.IsEmpty())
82 pts_heap_.Pop();
83
84 // We're back where we started. It should be completely safe to flush here
85 // since DecoderBase uses |expecting_discontinuous_| to verify that the next
86 // time DoDecode() is called we will have a discontinuous buffer.
87 //
88 // TODO(ajwong): Should we put a guard here to prevent leaving kError.
89 state_ = kNormal;
90
91 decode_engine_->Flush(done_cb);
92 }
93
94 void VideoDecoderImpl::DoDecode(Buffer* buffer) {
95 // TODO(ajwong): This DoDecode() and OnDecodeComplete() set of functions is
96 // too complicated to easily unittest. The test becomes fragile. Try to
97 // find a way to reorganize into smaller units for testing.
98
99 // During decode, because reads are issued asynchronously, it is possible to
100 // receive multiple end of stream buffers since each read is acked. When the
101 // first end of stream buffer is read, FFmpeg may still have frames queued
102 // up in the decoder so we need to go through the decode loop until it stops
103 // giving sensible data. After that, the decoder should output empty
104 // frames. There are three states the decoder can be in:
105 //
106 // kNormal: This is the starting state. Buffers are decoded. Decode errors
107 // are discarded.
108 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2
109 // until no more data is returned to flush out remaining
110 // frames. The input buffer is ignored at this point.
111 // kDecodeFinished: All calls return empty frames.
112 //
113 // These are the possible state transitions.
114 //
115 // kNormal -> kFlushCodec:
116 // When buffer->IsEndOfStream() is first true.
117 // kNormal -> kDecodeFinished:
118 // A catastrophic failure occurs, and decoding needs to stop.
119 // kFlushCodec -> kDecodeFinished:
120 // When avcodec_decode_video2() returns 0 data or errors out.
121 // (any state) -> kNormal:
122 // Any time buffer->IsDiscontinuous() is true.
123 //
124 // If the decoding is finished, we just always return empty frames.
125 if (state_ == kDecodeFinished) {
126 EnqueueEmptyFrame();
127 OnEmptyBufferDone(NULL);
128 return;
129 }
130
131 // Transition to kFlushCodec on the first end of stream buffer.
132 if (state_ == kNormal && buffer->IsEndOfStream()) {
133 state_ = kFlushCodec;
134 }
135
136 // Push all incoming timestamps into the priority queue as long as we have
137 // not yet received an end of stream buffer. It is important that this line
138 // stay below the state transition into kFlushCodec done above.
139 //
140 // TODO(ajwong): This push logic, along with the pop logic below needs to
141 // be reevaluated to correctly handle decode errors.
142 if (state_ == kNormal &&
143 buffer->GetTimestamp() != StreamSample::kInvalidTimestamp) {
144 pts_heap_.Push(buffer->GetTimestamp());
145 }
146
147 // Otherwise, attempt to decode a single frame.
148 decode_engine_->EmptyThisBuffer(buffer);
149 }
150
151 void VideoDecoderImpl::OnDecodeComplete(scoped_refptr<VideoFrame> video_frame) {
152 if (video_frame.get()) {
153 // If we actually got data back, enqueue a frame.
154 last_pts_ = FindPtsAndDuration(*time_base_, &pts_heap_, last_pts_,
155 video_frame.get());
156
157 video_frame->SetTimestamp(last_pts_.timestamp);
158 video_frame->SetDuration(last_pts_.duration);
159 EnqueueVideoFrame(video_frame);
160 } else {
161 // When in kFlushCodec, any errored decode, or a 0-lengthed frame,
162 // is taken as a signal to stop decoding.
163 if (state_ == kFlushCodec) {
164 state_ = kDecodeFinished;
165 EnqueueEmptyFrame();
166 }
167 }
168
169 OnEmptyBufferDone(NULL);
170 }
171
172 void VideoDecoderImpl::OnEmptyBufferDone(scoped_refptr<Buffer> buffer) {
173 // TODO(jiesun): what |DecodeBase::OnDecodeComplete| done is just
174 // what should be done in EmptyThisBufferCallback.
175 // Currently we just ignore the returned buffer.
176 DecoderBase<VideoDecoder, VideoFrame>::OnDecodeComplete();
177 }
178
179 void VideoDecoderImpl::EnqueueVideoFrame(
180 const scoped_refptr<VideoFrame>& video_frame) {
181 EnqueueResult(video_frame);
182 }
183
184 void VideoDecoderImpl::EnqueueEmptyFrame() {
185 scoped_refptr<VideoFrame> video_frame;
186 VideoFrame::CreateEmptyFrame(&video_frame);
187 EnqueueResult(video_frame);
188 }
189
190 VideoDecoderImpl::TimeTuple VideoDecoderImpl::FindPtsAndDuration(
191 const AVRational& time_base,
192 PtsHeap* pts_heap,
193 const TimeTuple& last_pts,
194 const VideoFrame* frame) {
195 TimeTuple pts;
196
197 // First search the VideoFrame for the pts. This is the most authoritative.
198 // Make a special exclusion for the value pts == 0. Though this is
199 // technically a valid value, it seems a number of FFmpeg codecs will
200 // mistakenly always set pts to 0.
201 //
202 // TODO(scherkus): FFmpegVideoDecodeEngine should be able to detect this
203 // situation and set the timestamp to kInvalidTimestamp.
204 DCHECK(frame);
205 base::TimeDelta timestamp = frame->GetTimestamp();
206 if (timestamp != StreamSample::kInvalidTimestamp &&
207 timestamp.ToInternalValue() != 0) {
208 pts.timestamp = timestamp;
209 } else if (!pts_heap->IsEmpty()) {
210 // If the frame did not have pts, try to get the pts from the |pts_heap|.
211 pts.timestamp = pts_heap->Top();
212 pts_heap->Pop();
213 } else if (last_pts.timestamp != StreamSample::kInvalidTimestamp &&
214 last_pts.duration != StreamSample::kInvalidTimestamp) {
215 // Guess assuming this frame was the same as the last frame.
216 pts.timestamp = last_pts.timestamp + last_pts.duration;
217 } else {
218 // Now we really have no clue!!! Mark an invalid timestamp and let the
219 // video renderer handle it (i.e., drop frame).
220 pts.timestamp = StreamSample::kInvalidTimestamp;
221 }
222
223 // Fill in the duration, using the frame itself as the authoratative source.
224 base::TimeDelta duration = frame->GetDuration();
225 if (duration != StreamSample::kInvalidTimestamp &&
226 duration.ToInternalValue() != 0) {
227 pts.duration = duration;
228 } else {
229 // Otherwise assume a normal frame duration.
230 pts.duration = ConvertTimestamp(time_base, 1);
231 }
232
233 return pts;
234 }
235
236 void VideoDecoderImpl::SignalPipelineError() {
237 host()->SetError(PIPELINE_ERROR_DECODE);
238 state_ = kDecodeFinished;
239 }
240
241 void VideoDecoderImpl::SetVideoDecodeEngineForTest(
242 VideoDecodeEngine* engine) {
243 decode_engine_.reset(engine);
244 }
245
246 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/video_decoder_impl.h ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698