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

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

Powered by Google App Engine
This is Rietveld 408576698