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

Side by Side Diff: media/mp4/mp4_stream_parser.cc

Issue 10536014: Implement ISO BMFF support in Media Source. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: A thorough sign-ification Created 8 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
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/mp4/mp4_stream_parser.h"
6
7 #include "base/callback.h"
8 #include "base/logging.h"
9 #include "base/time.h"
10 #include "media/base/audio_decoder_config.h"
11 #include "media/base/stream_parser_buffer.h"
12 #include "media/base/video_decoder_config.h"
13 #include "media/filters/ffmpeg_glue.h"
14 #include "media/mp4/box_definitions.h"
15 #include "media/mp4/box_reader.h"
16 #include "media/mp4/rcheck.h"
17
18 namespace media {
19 namespace mp4 {
20
21 MP4StreamParser::MP4StreamParser()
22 : state_(kWaitingForInit),
23 moof_head_(0),
24 mdat_tail_(0),
25 has_audio_(false),
26 has_video_(false) {
27 }
28
29 MP4StreamParser::~MP4StreamParser() {}
30
31 void MP4StreamParser::Init(const InitCB& init_cb,
32 const NewConfigCB& config_cb,
33 const NewBuffersCB& audio_cb,
34 const NewBuffersCB& video_cb,
35 const KeyNeededCB& key_needed_cb) {
36 DCHECK_EQ(state_, kWaitingForInit);
37 DCHECK(init_cb_.is_null());
38 DCHECK(!init_cb.is_null());
39 DCHECK(!config_cb.is_null());
40 DCHECK(!audio_cb.is_null() || !video_cb.is_null());
41 DCHECK(!key_needed_cb.is_null());
42
43 // TODO(strobe): Codec searches fail unless the FFmpegGlue singleton is
44 // initialized. Find the appropriate place to put this initialization.
45 FFmpegGlue::GetInstance();
46
47 ChangeState(kParsingBoxes);
48 init_cb_ = init_cb;
49 config_cb_ = config_cb;
50 audio_cb_ = audio_cb;
51 video_cb_ = video_cb;
52 key_needed_cb_ = key_needed_cb;
53 }
54
55 void MP4StreamParser::Flush() {
56 DCHECK_NE(state_, kWaitingForInit);
57
58 queue_.Reset();
59 moof_head_ = 0;
60 mdat_tail_ = 0;
61 }
62
63 bool MP4StreamParser::Parse(const uint8* buf, int size) {
64 DCHECK_NE(state_, kWaitingForInit);
65
66 if (state_ == kError)
67 return false;
68
69 queue_.Push(buf, size);
70
71 BufferQueue audio_buffers;
72 BufferQueue video_buffers;
73
74 bool result, err = false;
75
76 do {
77 if (state_ == kParsingBoxes) {
78 if (mdat_tail_ > queue_.head()) {
79 result = queue_.Trim(mdat_tail_);
80 } else {
81 result = ParseBox(&err);
82 }
83 } else {
84 DCHECK_EQ(kEmittingSamples, state_);
85 result = EnqueueSample(&audio_buffers, &video_buffers, &err);
86 if (result) {
87 int64 max_clear = runs_.GetMaxClearOffset() + moof_head_;
88 DCHECK(max_clear <= queue_.tail());
89 err = !(ReadMDATsUntil(max_clear) && queue_.Trim(max_clear));
90 }
91 }
92 } while (result && !err);
93
94 if (err) {
95 DLOG(ERROR) << "Unknown error while parsing MP4";
96 queue_.Reset();
97 moov_.reset();
98 ChangeState(kError);
99 return false;
100 }
101
102 if (!audio_buffers.empty() &&
103 (audio_cb_.is_null() || !audio_cb_.Run(audio_buffers)))
104 return false;
105 if (!video_buffers.empty() &&
106 (video_cb_.is_null() || !video_cb_.Run(video_buffers)))
107 return false;
108
109 return true;
110 }
111
112 bool MP4StreamParser::ParseBox(bool* err) {
113 const uint8* buf;
114 int size;
115 queue_.Peek(&buf, &size);
116 if (!size) return false;
117
118 scoped_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox(buf, size, err));
119 if (reader.get() == NULL) return false;
120
121 if (reader->type() == FOURCC_MOOV) {
122 *err = !ParseMoov(reader.get());
123 } else if (reader->type() == FOURCC_MOOF) {
124 moof_head_ = queue_.head();
125 *err = !ParseMoof(reader.get());
126
127 // Set up first mdat offset for ParseMDATsUntil()
128 mdat_tail_ = queue_.head() + reader->size();
129 } else {
130 DVLOG(2) << "Skipping unrecognized top-level box: "
131 << FourCCToString(reader->type());
132 }
133
134 queue_.Pop(reader->size());
135 return !(*err);
136 }
137
138
139 bool MP4StreamParser::ParseMoov(BoxReader* reader) {
140 // TODO(strobe): respect edit lists
141 if (moov_.get()) {
142 DLOG(FATAL) << "TODO: Reinitialization not yet supported.";
143 return false;
144 }
145
146 moov_.reset(new Movie);
147
148 RCHECK(moov_->Parse(reader));
149
150 has_audio_ = false;
151 has_video_ = false;
152
153 // Select first video and audio track
154 AudioDecoderConfig audio_config;
155 VideoDecoderConfig video_config;
156
157 for (auto track = moov_->tracks.begin();
158 track != moov_->tracks.end(); ++track) {
159 // TODO(strobe): We only take the first of each type of track for now
160 const SampleDescription& samp_descr =
161 track->media.information.sample_table.description;
162 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) {
163 RCHECK(!samp_descr.audio_entries.empty());
164 const AudioSampleEntry& entry = samp_descr.audio_entries[0];
165
166 // TODO(strobe): this does not conform to specs for protected audio in the
167 // test files provided by Widevine. Need clarification on the scheme and
168 // whether we expect to deviate from it.
169 //RCHECK(entry.format == FOURCC_MP4A ||
170 // (entry.format == FOURCC_ENCA &&
171 // entry.sinf.format.format == FOURCC_MP4A));
172
173 const ChannelLayout layout =
174 ConvertAACChannelCountToChannelLayout(entry.channelcount);
175 audio_config.Initialize(kCodecAAC, entry.samplesize, layout,
176 entry.samplerate, NULL, 0, false);
177 has_audio_ = true;
178 audio_track_id_ = track->header.track_id;
179 }
180 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) {
181 RCHECK(!samp_descr.video_entries.empty());
182 const VideoSampleEntry& entry = samp_descr.video_entries[0];
183
184 //RCHECK(entry.format == FOURCC_AVC1 ||
185 // (entry.format == FOURCC_ENCV &&
186 // entry.sinf.format.format == FOURCC_AVC1));
187
188 // TODO(strobe): Profile level recovery
189 // TODO(strobe): Crop box (if needed)
190 // TODO(strobe): Rectangle semantics
191 // TODO(strobe): PAR (if needed)
192 // TODO(strobe): Ensure framerate is not used
193 video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12,
194 gfx::Size(entry.width, entry.height),
195 gfx::Rect(0, 0, entry.width, entry.height),
196 1000, track->media.header.timescale,
197 1, 1,
198 NULL, 0, false);
199 has_video_ = true;
200 video_track_id_ = track->header.track_id;
201
202 size_of_nalu_length_ = entry.avcc.length_size;
203 }
204 }
205
206 RCHECK(config_cb_.Run(audio_config, video_config));
207
208 base::TimeDelta duration;
209 if (moov_->extends.header.fragment_duration > 0) {
210 duration = TimeDeltaFromFrac(moov_->extends.header.fragment_duration,
211 moov_->header.timescale);
212 } else if (moov_->header.duration > 0) {
213 duration = TimeDeltaFromFrac(moov_->header.duration,
214 moov_->header.timescale);
215 } else {
216 duration = kInfiniteDuration();
217 }
218
219 init_cb_.Run(true, duration);
220 return true;
221 }
222
223 bool MP4StreamParser::ParseMoof(BoxReader* reader) {
224 RCHECK(moov_.get()); // Must already have initialization segment
225 MovieFragment moof;
226 RCHECK(moof.Parse(reader));
227 RCHECK(runs_.Init(*moov_, moof));
228 ChangeState(kEmittingSamples);
229 return true;
230 }
231
232 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers,
233 BufferQueue* video_buffers,
234 bool* err) {
235 if (!runs_.RunValid()) {
236 ChangeState(kParsingBoxes);
237 return true;
238 }
239
240 if (!runs_.SampleValid()) {
241 runs_.AdvanceRun();
242 return true;
243 }
244
245 DCHECK(!(*err));
246
247 const uint8* buf;
248 int size;
249 queue_.Peek(&buf, &size);
250 if (!size) return false;
251
252 bool audio = has_audio_ && audio_track_id_ == runs_.track_id();
253 bool video = has_video_ && video_track_id_ == runs_.track_id();
254
255 // Skip this entire track if it's not one we're interested in
256 if (!audio && !video) runs_.AdvanceRun();
257
258 // Attempt to cache the auxiliary information first. Aux info is usually
259 // placed in a contiguous block before the sample data, rather than being
260 // interleaved. If we didn't cache it, this would require that we retain the
261 // start of the segment buffer while reading samples. Aux info is typically
262 // quite small compared to sample data, so this pattern is useful on
263 // memory-constrained devices where the source buffer consumes a substantial
264 // portion of the total system memory.
265 if (runs_.NeedsCENC()) {
266 queue_.PeekAt(runs_.cenc_offset() + moof_head_, &buf, &size);
267 return runs_.CacheCENC(buf, size);
268 }
269
270 queue_.PeekAt(runs_.offset() + moof_head_, &buf, &size);
271 if (size < runs_.size()) return false;
272
273 std::vector<uint8> frame_buf(buf, buf + runs_.size());
274 if (video) {
275 // TODO(strobe): reinject SPS/PPS at every keyframe (or whatever the
276 // spec says to do).
277 RCHECK(ConvertAVCCToAnnexB(size_of_nalu_length_, &frame_buf));
278 }
279
280 scoped_refptr<StreamParserBuffer> stream_buf =
281 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(),
282 runs_.is_keyframe());
283
284 stream_buf->SetDuration(runs_.duration());
285 stream_buf->SetTimestamp(runs_.dts());
286 // TODO(strobe): verify that Chrome's various media backends perform
287 // B-frame reordering by zipping the un-reordered frame timestamp stream
288 // with the reordered frame data stream, such that we only need to pass
289 // DTS to the source buffer. (Doing so will always carry a risk for
290 // bizarre videos that do things like have a DTS stream that isn't a
291 // strict reordering of the CTS stream, but I'm in favor of ignoring that
292 // case.)
293
294 DVLOG(3) << "Pushing frame: aud=" << audio
295 << ", dur=" << runs_.duration().InMilliseconds()
296 << ", dts=" << runs_.dts().InMilliseconds()
297 << ", size=" << runs_.size();
298
299 if (audio) {
300 audio_buffers->push_back(stream_buf);
301 } else {
302 video_buffers->push_back(stream_buf);
303 }
304
305 runs_.AdvanceSample();
306 return true;
307 }
308
309 bool MP4StreamParser::ReadMDATsUntil(const int64 tgt_offset) {
310 DCHECK(tgt_offset <= queue_.tail());
311
312 while (mdat_tail_ < tgt_offset) {
313 const uint8* buf;
314 int size;
315 queue_.PeekAt(mdat_tail_, &buf, &size);
316
317 FourCC type;
318 int box_sz;
319 bool err;
320 if (!BoxReader::StartTopLevelBox(buf, size, &type, &box_sz, &err))
321 return false;
322
323 if (type != FOURCC_MDAT) {
324 DLOG(WARNING) << "Unexpected type while parsing MDATs: "
325 << FourCCToString(type);
326 }
327
328 mdat_tail_ += box_sz;
329 }
330
331 return true;
332 }
333
334 void MP4StreamParser::ChangeState(State new_state) {
335 DVLOG(2) << "Changing state: " << new_state;
336 state_ = new_state;
337 }
338
339 } // namespace mp4
340 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698