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

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: Inject parameter sets 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
« no previous file with comments | « media/mp4/mp4_stream_parser.h ('k') | media/mp4/mp4_stream_parser_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
(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/mp4/box_definitions.h"
14 #include "media/mp4/box_reader.h"
15 #include "media/mp4/rcheck.h"
16
17 namespace media {
18 namespace mp4 {
19
20 MP4StreamParser::MP4StreamParser()
21 : state_(kWaitingForInit),
22 moof_head_(0),
23 mdat_tail_(0),
24 has_audio_(false),
25 has_video_(false) {
26 }
27
28 MP4StreamParser::~MP4StreamParser() {}
29
30 void MP4StreamParser::Init(const InitCB& init_cb,
31 const NewConfigCB& config_cb,
32 const NewBuffersCB& audio_cb,
33 const NewBuffersCB& video_cb,
34 const KeyNeededCB& key_needed_cb) {
35 DCHECK_EQ(state_, kWaitingForInit);
36 DCHECK(init_cb_.is_null());
37 DCHECK(!init_cb.is_null());
38 DCHECK(!config_cb.is_null());
39 DCHECK(!audio_cb.is_null() || !video_cb.is_null());
40 DCHECK(!key_needed_cb.is_null());
41
42 ChangeState(kParsingBoxes);
43 init_cb_ = init_cb;
44 config_cb_ = config_cb;
45 audio_cb_ = audio_cb;
46 video_cb_ = video_cb;
47 key_needed_cb_ = key_needed_cb;
48 }
49
50 void MP4StreamParser::Flush() {
51 DCHECK_NE(state_, kWaitingForInit);
52
53 queue_.Reset();
54 moof_head_ = 0;
55 mdat_tail_ = 0;
56 }
57
58 bool MP4StreamParser::Parse(const uint8* buf, int size) {
59 DCHECK_NE(state_, kWaitingForInit);
60
61 if (state_ == kError)
62 return false;
63
64 queue_.Push(buf, size);
65
66 BufferQueue audio_buffers;
67 BufferQueue video_buffers;
68
69 bool result, err = false;
70
71 do {
72 if (state_ == kParsingBoxes) {
73 if (mdat_tail_ > queue_.head()) {
74 result = queue_.Trim(mdat_tail_);
75 } else {
76 result = ParseBox(&err);
77 }
78 } else {
79 DCHECK_EQ(kEmittingSamples, state_);
80 result = EnqueueSample(&audio_buffers, &video_buffers, &err);
81 if (result) {
82 int64 max_clear = runs_.GetMaxClearOffset() + moof_head_;
83 DCHECK(max_clear <= queue_.tail());
84 err = !(ReadMDATsUntil(max_clear) && queue_.Trim(max_clear));
85 }
86 }
87 } while (result && !err);
88
89 if (err) {
90 DLOG(ERROR) << "Unknown error while parsing MP4";
91 queue_.Reset();
92 moov_.reset();
93 ChangeState(kError);
94 return false;
95 }
96
97 if (!audio_buffers.empty() &&
98 (audio_cb_.is_null() || !audio_cb_.Run(audio_buffers)))
99 return false;
100 if (!video_buffers.empty() &&
101 (video_cb_.is_null() || !video_cb_.Run(video_buffers)))
102 return false;
103
104 return true;
105 }
106
107 bool MP4StreamParser::ParseBox(bool* err) {
108 const uint8* buf;
109 int size;
110 queue_.Peek(&buf, &size);
111 if (!size) return false;
112
113 scoped_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox(buf, size, err));
114 if (reader.get() == NULL) return false;
115
116 if (reader->type() == FOURCC_MOOV) {
117 *err = !ParseMoov(reader.get());
118 } else if (reader->type() == FOURCC_MOOF) {
119 moof_head_ = queue_.head();
120 *err = !ParseMoof(reader.get());
121
122 // Set up first mdat offset for ParseMDATsUntil()
123 mdat_tail_ = queue_.head() + reader->size();
124 } else {
125 DVLOG(2) << "Skipping unrecognized top-level box: "
126 << FourCCToString(reader->type());
127 }
128
129 queue_.Pop(reader->size());
130 return !(*err);
131 }
132
133
134 bool MP4StreamParser::ParseMoov(BoxReader* reader) {
135 // TODO(strobe): Respect edit lists.
136 moov_.reset(new Movie);
137
138 RCHECK(moov_->Parse(reader));
139
140 has_audio_ = false;
141 has_video_ = false;
142 parameter_sets_inserted_ = false;
143
144 AudioDecoderConfig audio_config;
145 VideoDecoderConfig video_config;
146
147 for (std::vector<Track>::const_iterator track = moov_->tracks.begin();
148 track != moov_->tracks.end(); ++track) {
149 // TODO(strobe): Only the first audio and video track present in a file are
150 // used. (Track selection is better accomplished via Source IDs, though, so
151 // adding support for track selection within a stream is low-priority.)
152 const SampleDescription& samp_descr =
153 track->media.information.sample_table.description;
154 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) {
155 RCHECK(!samp_descr.audio_entries.empty());
156 const AudioSampleEntry& entry = samp_descr.audio_entries[0];
157
158 // TODO(strobe): We accept all format values, pending clarification on
159 // the formats used for encrypted media (http://crbug.com/132351).
160 // RCHECK(entry.format == FOURCC_MP4A ||
161 // (entry.format == FOURCC_ENCA &&
162 // entry.sinf.format.format == FOURCC_MP4A));
163
164 const ChannelLayout layout =
165 AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount);
166 audio_config.Initialize(kCodecAAC, entry.samplesize, layout,
167 entry.samplerate, NULL, 0, false);
168 has_audio_ = true;
169 audio_track_id_ = track->header.track_id;
170 }
171 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) {
172 RCHECK(!samp_descr.video_entries.empty());
173 const VideoSampleEntry& entry = samp_descr.video_entries[0];
174
175 // RCHECK(entry.format == FOURCC_AVC1 ||
176 // (entry.format == FOURCC_ENCV &&
177 // entry.sinf.format.format == FOURCC_AVC1));
178
179 // TODO(strobe): Recover correct crop box and pixel aspect ratio
180 video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12,
181 gfx::Size(entry.width, entry.height),
182 gfx::Rect(0, 0, entry.width, entry.height),
183 // Bogus duration used for framerate, since real
184 // framerate may be variable
185 1000, track->media.header.timescale,
186 1, 1,
187 // No decoder-specific buffer needed for AVC;
188 // SPS/PPS are embedded in the video stream
189 NULL, 0, false);
190 has_video_ = true;
191 video_track_id_ = track->header.track_id;
192
193 size_of_nalu_length_ = entry.avcc.length_size;
194 }
195 }
196
197 RCHECK(config_cb_.Run(audio_config, video_config));
198
199 base::TimeDelta duration;
200 if (moov_->extends.header.fragment_duration > 0) {
201 duration = TimeDeltaFromFrac(moov_->extends.header.fragment_duration,
202 moov_->header.timescale);
203 } else if (moov_->header.duration > 0) {
204 duration = TimeDeltaFromFrac(moov_->header.duration,
205 moov_->header.timescale);
206 } else {
207 duration = kInfiniteDuration();
208 }
209
210 init_cb_.Run(true, duration);
211 return true;
212 }
213
214 bool MP4StreamParser::ParseMoof(BoxReader* reader) {
215 RCHECK(moov_.get()); // Must already have initialization segment
216 MovieFragment moof;
217 RCHECK(moof.Parse(reader));
218 RCHECK(runs_.Init(*moov_, moof));
219 ChangeState(kEmittingSamples);
220 return true;
221 }
222
223 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers,
224 BufferQueue* video_buffers,
225 bool* err) {
226 if (!runs_.RunValid()) {
227 ChangeState(kParsingBoxes);
228 return true;
229 }
230
231 if (!runs_.SampleValid()) {
232 runs_.AdvanceRun();
233 return true;
234 }
235
236 DCHECK(!(*err));
237
238 const uint8* buf;
239 int size;
240 queue_.Peek(&buf, &size);
241 if (!size) return false;
242
243 bool audio = has_audio_ && audio_track_id_ == runs_.track_id();
244 bool video = has_video_ && video_track_id_ == runs_.track_id();
245
246 // Skip this entire track if it's not one we're interested in
247 if (!audio && !video) runs_.AdvanceRun();
248
249 // Attempt to cache the auxiliary information first. Aux info is usually
250 // placed in a contiguous block before the sample data, rather than being
251 // interleaved. If we didn't cache it, this would require that we retain the
252 // start of the segment buffer while reading samples. Aux info is typically
253 // quite small compared to sample data, so this pattern is useful on
254 // memory-constrained devices where the source buffer consumes a substantial
255 // portion of the total system memory.
256 if (runs_.NeedsCENC()) {
257 queue_.PeekAt(runs_.cenc_offset() + moof_head_, &buf, &size);
258 return runs_.CacheCENC(buf, size);
259 }
260
261 queue_.PeekAt(runs_.offset() + moof_head_, &buf, &size);
262 if (size < runs_.size()) return false;
263
264 std::vector<uint8> frame_buf(buf, buf + runs_.size());
265 if (video) {
266 RCHECK(AVC::ConvertToAnnexB(size_of_nalu_length_, &frame_buf));
267 if (!parameter_sets_inserted_) {
268 const AVCDecoderConfigurationRecord* avc_config = NULL;
269 for (size_t t = 0; t < moov_->tracks.size(); t++) {
270 if (moov_->tracks[t].header.track_id == runs_.track_id()) {
271 avc_config = &moov_->tracks[t].media.information.
272 sample_table.description.video_entries[0].avcc;
acolwell GONE FROM CHROMIUM 2012/06/13 14:16:06 wow. that is quite the dereference chain. Should w
strobe_ 2012/06/13 21:18:13 Done. (It shouldn't make a difference, but it make
273 }
274 }
275 RCHECK(avc_config != NULL);
276 RCHECK(AVC::InsertParameterSets(*avc_config, &frame_buf));
277 parameter_sets_inserted_ = true;
278 }
279 }
280
281 scoped_refptr<StreamParserBuffer> stream_buf =
282 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(),
283 runs_.is_keyframe());
284
285 stream_buf->SetDuration(runs_.duration());
286 // We depend on the decoder performing frame reordering without reordering
287 // timestamps, and only provide the decode timestamp in the buffer.
288 stream_buf->SetTimestamp(runs_.dts());
289
290 DVLOG(3) << "Pushing frame: aud=" << audio
291 << ", key=" << runs_.is_keyframe()
292 << ", dur=" << runs_.duration().InMilliseconds()
293 << ", dts=" << runs_.dts().InMilliseconds()
294 << ", size=" << runs_.size();
295
296 if (audio) {
297 audio_buffers->push_back(stream_buf);
298 } else {
299 video_buffers->push_back(stream_buf);
300 }
301
302 runs_.AdvanceSample();
303 return true;
304 }
305
306 bool MP4StreamParser::ReadMDATsUntil(const int64 tgt_offset) {
307 DCHECK(tgt_offset <= queue_.tail());
308
309 while (mdat_tail_ < tgt_offset) {
310 const uint8* buf;
311 int size;
312 queue_.PeekAt(mdat_tail_, &buf, &size);
313
314 FourCC type;
315 int box_sz;
316 bool err;
317 if (!BoxReader::StartTopLevelBox(buf, size, &type, &box_sz, &err))
318 return false;
319
320 if (type != FOURCC_MDAT) {
321 DLOG(WARNING) << "Unexpected type while parsing MDATs: "
322 << FourCCToString(type);
323 }
324
325 mdat_tail_ += box_sz;
326 }
327
328 return true;
329 }
330
331 void MP4StreamParser::ChangeState(State new_state) {
332 DVLOG(2) << "Changing state: " << new_state;
333 state_ = new_state;
334 }
335
336 } // namespace mp4
337 } // namespace media
OLDNEW
« no previous file with comments | « media/mp4/mp4_stream_parser.h ('k') | media/mp4/mp4_stream_parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698