OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/mp4/mp4_stream_parser.h" | 5 #include "media/mp4/mp4_stream_parser.h" |
6 | 6 |
7 #include "base/callback.h" | 7 #include "base/callback.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/time.h" | 9 #include "base/time.h" |
10 #include "media/base/audio_decoder_config.h" | 10 #include "media/base/audio_decoder_config.h" |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
93 } while (result && !err); | 93 } while (result && !err); |
94 | 94 |
95 if (err) { | 95 if (err) { |
96 DLOG(ERROR) << "Unknown error while parsing MP4"; | 96 DLOG(ERROR) << "Unknown error while parsing MP4"; |
97 queue_.Reset(); | 97 queue_.Reset(); |
98 moov_.reset(); | 98 moov_.reset(); |
99 ChangeState(kError); | 99 ChangeState(kError); |
100 return false; | 100 return false; |
101 } | 101 } |
102 | 102 |
103 if (!audio_buffers.empty() && | 103 if (!audio_buffers.empty()) { |
104 (audio_cb_.is_null() || !audio_cb_.Run(audio_buffers))) | 104 CHECK(!audio_cb_.is_null()); |
105 return false; | 105 RCHECK(audio_cb_.Run(audio_buffers)); |
106 if (!video_buffers.empty() && | 106 } |
107 (video_cb_.is_null() || !video_cb_.Run(video_buffers))) | 107 if (!video_buffers.empty()) { |
108 return false; | 108 CHECK(!video_cb_.is_null()); |
109 | 109 RCHECK(video_cb_.Run(video_buffers)); |
110 } | |
110 return true; | 111 return true; |
111 } | 112 } |
112 | 113 |
113 bool MP4StreamParser::ParseBox(bool* err) { | 114 bool MP4StreamParser::ParseBox(bool* err) { |
114 const uint8* buf; | 115 const uint8* buf; |
115 int size; | 116 int size; |
116 queue_.Peek(&buf, &size); | 117 queue_.Peek(&buf, &size); |
117 if (!size) return false; | 118 if (!size) return false; |
118 | 119 |
119 scoped_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox(buf, size, err)); | 120 scoped_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox(buf, size, err)); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
159 track->media.information.sample_table.description; | 160 track->media.information.sample_table.description; |
160 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) { | 161 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) { |
161 RCHECK(!samp_descr.audio_entries.empty()); | 162 RCHECK(!samp_descr.audio_entries.empty()); |
162 const AudioSampleEntry& entry = samp_descr.audio_entries[0]; | 163 const AudioSampleEntry& entry = samp_descr.audio_entries[0]; |
163 | 164 |
164 // TODO(strobe): We accept all format values, pending clarification on | 165 // TODO(strobe): We accept all format values, pending clarification on |
165 // the formats used for encrypted media (http://crbug.com/132351). | 166 // the formats used for encrypted media (http://crbug.com/132351). |
166 // RCHECK(entry.format == FOURCC_MP4A || | 167 // RCHECK(entry.format == FOURCC_MP4A || |
167 // (entry.format == FOURCC_ENCA && | 168 // (entry.format == FOURCC_ENCA && |
168 // entry.sinf.format.format == FOURCC_MP4A)); | 169 // entry.sinf.format.format == FOURCC_MP4A)); |
170 RCHECK(EmitKeyNeeded(entry.sinf.info.track_encryption)); | |
169 | 171 |
170 const ChannelLayout layout = | 172 const ChannelLayout layout = |
171 AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount); | 173 AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount); |
172 audio_config.Initialize(kCodecAAC, entry.samplesize, layout, | 174 audio_config.Initialize(kCodecAAC, entry.samplesize, layout, |
173 entry.samplerate, NULL, 0, false); | 175 entry.samplerate, NULL, 0, false); |
174 has_audio_ = true; | 176 has_audio_ = true; |
175 audio_track_id_ = track->header.track_id; | 177 audio_track_id_ = track->header.track_id; |
176 } | 178 } |
177 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { | 179 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { |
178 RCHECK(!samp_descr.video_entries.empty()); | 180 RCHECK(!samp_descr.video_entries.empty()); |
179 const VideoSampleEntry& entry = samp_descr.video_entries[0]; | 181 const VideoSampleEntry& entry = samp_descr.video_entries[0]; |
180 | 182 |
181 // RCHECK(entry.format == FOURCC_AVC1 || | 183 // RCHECK(entry.format == FOURCC_AVC1 || |
182 // (entry.format == FOURCC_ENCV && | 184 // (entry.format == FOURCC_ENCV && |
183 // entry.sinf.format.format == FOURCC_AVC1)); | 185 // entry.sinf.format.format == FOURCC_AVC1)); |
186 RCHECK(EmitKeyNeeded(entry.sinf.info.track_encryption)); | |
184 | 187 |
185 // TODO(strobe): Recover correct crop box and pixel aspect ratio | 188 // TODO(strobe): Recover correct crop box and pixel aspect ratio |
186 video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12, | 189 video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12, |
187 gfx::Size(entry.width, entry.height), | 190 gfx::Size(entry.width, entry.height), |
188 gfx::Rect(0, 0, entry.width, entry.height), | 191 gfx::Rect(0, 0, entry.width, entry.height), |
189 // Bogus duration used for framerate, since real | 192 // Bogus duration used for framerate, since real |
190 // framerate may be variable | 193 // framerate may be variable |
191 1000, track->media.header.timescale, | 194 1000, track->media.header.timescale, |
192 1, 1, | 195 1, 1, |
193 // No decoder-specific buffer needed for AVC; | 196 // No decoder-specific buffer needed for AVC; |
(...skipping 26 matching lines...) Expand all Loading... | |
220 bool MP4StreamParser::ParseMoof(BoxReader* reader) { | 223 bool MP4StreamParser::ParseMoof(BoxReader* reader) { |
221 RCHECK(moov_.get()); // Must already have initialization segment | 224 RCHECK(moov_.get()); // Must already have initialization segment |
222 MovieFragment moof; | 225 MovieFragment moof; |
223 RCHECK(moof.Parse(reader)); | 226 RCHECK(moof.Parse(reader)); |
224 RCHECK(runs_.Init(*moov_, moof)); | 227 RCHECK(runs_.Init(*moov_, moof)); |
225 new_segment_cb_.Run(runs_.GetMinDecodeTimestamp()); | 228 new_segment_cb_.Run(runs_.GetMinDecodeTimestamp()); |
226 ChangeState(kEmittingSamples); | 229 ChangeState(kEmittingSamples); |
227 return true; | 230 return true; |
228 } | 231 } |
229 | 232 |
233 bool MP4StreamParser::EmitKeyNeeded(const TrackEncryption& track_encryption) { | |
234 if (!track_encryption.is_encrypted) return true; | |
235 scoped_array<uint8> kid(new uint8[track_encryption.default_kid.size()]); | |
236 memcpy(kid.get(), &track_encryption.default_kid[0], | |
237 track_encryption.default_kid.size()); | |
238 return need_key_cb_.Run(kid.Pass(), track_encryption.default_kid.size()); | |
ddorwin
2012/06/26 23:16:20
For ISO, the initData is supposed to be the PSSH.
strobe_
2012/06/27 02:01:21
Oh, duh, that makes a lot more sense! Where's that
ddorwin
2012/07/03 21:03:46
This is based on discussions with others, though t
strobe_
2012/07/13 00:47:07
OK, comment added.
| |
239 } | |
240 | |
230 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, | 241 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, |
231 BufferQueue* video_buffers, | 242 BufferQueue* video_buffers, |
232 bool* err) { | 243 bool* err) { |
233 if (!runs_.RunValid()) { | 244 if (!runs_.RunValid()) { |
234 ChangeState(kParsingBoxes); | 245 ChangeState(kParsingBoxes); |
235 return true; | 246 return true; |
236 } | 247 } |
237 | 248 |
238 if (!runs_.SampleValid()) { | 249 if (!runs_.SampleValid()) { |
239 runs_.AdvanceRun(); | 250 runs_.AdvanceRun(); |
240 return true; | 251 return true; |
241 } | 252 } |
242 | 253 |
243 DCHECK(!(*err)); | 254 DCHECK(!(*err)); |
244 | 255 |
245 const uint8* buf; | 256 const uint8* buf; |
246 int size; | 257 int size; |
ddorwin
2012/06/26 06:09:19
buf_size would make this function easier to read.
strobe_
2012/07/13 00:47:07
Done.
strobe_
2012/07/13 00:47:07
Done.
| |
247 queue_.Peek(&buf, &size); | 258 queue_.Peek(&buf, &size); |
248 if (!size) return false; | 259 if (!size) return false; |
249 | 260 |
250 bool audio = has_audio_ && audio_track_id_ == runs_.track_id(); | 261 bool audio = has_audio_ && audio_track_id_ == runs_.track_id(); |
251 bool video = has_video_ && video_track_id_ == runs_.track_id(); | 262 bool video = has_video_ && video_track_id_ == runs_.track_id(); |
252 | 263 |
253 // Skip this entire track if it's not one we're interested in | 264 // Skip this entire track if it's not one we're interested in |
254 if (!audio && !video) runs_.AdvanceRun(); | 265 if (!audio && !video) runs_.AdvanceRun(); |
255 | 266 |
256 // Attempt to cache the auxiliary information first. Aux info is usually | 267 // Attempt to cache the auxiliary information first. Aux info is usually |
257 // placed in a contiguous block before the sample data, rather than being | 268 // placed in a contiguous block before the sample data, rather than being |
258 // interleaved. If we didn't cache it, this would require that we retain the | 269 // interleaved. If we didn't cache it, this would require that we retain the |
259 // start of the segment buffer while reading samples. Aux info is typically | 270 // start of the segment buffer while reading samples. Aux info is typically |
260 // quite small compared to sample data, so this pattern is useful on | 271 // quite small compared to sample data, so this pattern is useful on |
261 // memory-constrained devices where the source buffer consumes a substantial | 272 // memory-constrained devices where the source buffer consumes a substantial |
262 // portion of the total system memory. | 273 // portion of the total system memory. |
263 if (runs_.NeedsCENC()) { | 274 if (runs_.AuxInfoRequired()) { |
264 queue_.PeekAt(runs_.cenc_offset() + moof_head_, &buf, &size); | 275 queue_.PeekAt(runs_.aux_info_offset() + moof_head_, &buf, &size); |
265 return runs_.CacheCENC(buf, size); | 276 if (size < runs_.aux_info_size()) return false; |
277 *err = !runs_.CacheAuxInfo(buf, size); | |
278 return !*err; | |
266 } | 279 } |
267 | 280 |
268 queue_.PeekAt(runs_.offset() + moof_head_, &buf, &size); | 281 queue_.PeekAt(runs_.offset() + moof_head_, &buf, &size); |
269 if (size < runs_.size()) return false; | 282 if (size < runs_.size()) return false; |
270 | 283 |
284 scoped_ptr<DecryptConfig> decrypt_config; | |
285 if (runs_.is_encrypted()) | |
286 decrypt_config = runs_.GetDecryptConfig(); | |
287 | |
271 std::vector<uint8> frame_buf(buf, buf + runs_.size()); | 288 std::vector<uint8> frame_buf(buf, buf + runs_.size()); |
272 if (video) { | 289 if (video) { |
290 // TODO(strobe): move all this to separate method, add unittest | |
273 RCHECK(AVC::ConvertToAnnexB(size_of_nalu_length_, &frame_buf)); | 291 RCHECK(AVC::ConvertToAnnexB(size_of_nalu_length_, &frame_buf)); |
292 | |
293 if (decrypt_config.get()) { | |
294 const int nalu_size_diff = 4 - size_of_nalu_length_; | |
295 size_t expected_size = runs_.size() + | |
ddorwin
2012/06/26 06:09:19
expected size of what?
what is runs_.size()? It se
strobe_
2012/06/27 02:01:21
size() is the size of the current sample; IV and s
| |
296 decrypt_config->subsample_count() * nalu_size_diff; | |
297 RCHECK(frame_buf.size() == expected_size); | |
298 for (int i = 0; i < decrypt_config->subsample_count(); i++) | |
299 decrypt_config->mutable_subsamples()[i].clear_bytes += nalu_size_diff; | |
ddorwin
2012/06/26 06:09:19
Rather than mutable_subsamples, you could have an
strobe_
2012/06/27 02:01:21
Two reasons we need to mutate:
1. To convert each
ddorwin
2012/07/03 21:03:46
Since we are just passing the DecryptConfig to som
strobe_
2012/07/13 00:47:07
Done.
| |
300 } | |
301 | |
274 if (!parameter_sets_inserted_) { | 302 if (!parameter_sets_inserted_) { |
303 if (!runs_.is_keyframe()) { | |
304 LOG(INFO) << "XXX skipping initial non-keyframe sample"; | |
305 runs_.AdvanceSample(); | |
306 return true; | |
307 } | |
308 | |
275 const AVCDecoderConfigurationRecord* avc_config = NULL; | 309 const AVCDecoderConfigurationRecord* avc_config = NULL; |
276 for (size_t t = 0; t < moov_->tracks.size(); t++) { | 310 for (size_t t = 0; t < moov_->tracks.size(); t++) { |
277 if (moov_->tracks[t].header.track_id == runs_.track_id()) { | 311 if (moov_->tracks[t].header.track_id == runs_.track_id()) { |
278 avc_config = &moov_->tracks[t].media.information. | 312 avc_config = &moov_->tracks[t].media.information. |
279 sample_table.description.video_entries[0].avcc; | 313 sample_table.description.video_entries[0].avcc; |
280 break; | 314 break; |
281 } | 315 } |
282 } | 316 } |
283 RCHECK(avc_config != NULL); | 317 RCHECK(avc_config != NULL); |
284 RCHECK(AVC::InsertParameterSets(*avc_config, &frame_buf)); | 318 std::vector<uint8> param_sets; |
319 RCHECK(AVC::ConvertParameterSets(*avc_config, ¶m_sets)); | |
320 frame_buf.insert(frame_buf.begin(), | |
321 param_sets.begin(), param_sets.end()); | |
322 if (decrypt_config.get()) { | |
323 decrypt_config->mutable_subsamples()[0].clear_bytes += | |
ddorwin
2012/06/26 06:09:19
Hmm, another mutable use. I guess it's not as simp
strobe_
2012/06/27 02:01:21
see above
| |
324 param_sets.size(); | |
325 } | |
285 parameter_sets_inserted_ = true; | 326 parameter_sets_inserted_ = true; |
286 } | 327 } |
287 } | 328 } |
288 | 329 |
289 scoped_refptr<StreamParserBuffer> stream_buf = | 330 scoped_refptr<StreamParserBuffer> stream_buf = |
290 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), | 331 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), |
291 runs_.is_keyframe()); | 332 runs_.is_keyframe()); |
292 | 333 |
334 if (runs_.is_encrypted()) | |
335 stream_buf->SetDecryptConfig(decrypt_config.Pass()); | |
336 | |
293 stream_buf->SetDuration(runs_.duration()); | 337 stream_buf->SetDuration(runs_.duration()); |
294 // We depend on the decoder performing frame reordering without reordering | 338 // We depend on the decoder performing frame reordering without reordering |
295 // timestamps, and only provide the decode timestamp in the buffer. | 339 // timestamps, and only provide the decode timestamp in the buffer. |
296 stream_buf->SetTimestamp(runs_.dts()); | 340 stream_buf->SetTimestamp(runs_.dts()); |
297 | 341 |
298 DVLOG(3) << "Pushing frame: aud=" << audio | 342 DVLOG(3) << "Pushing frame: aud=" << audio |
299 << ", key=" << runs_.is_keyframe() | 343 << ", key=" << runs_.is_keyframe() |
300 << ", dur=" << runs_.duration().InMilliseconds() | 344 << ", dur=" << runs_.duration().InMilliseconds() |
301 << ", dts=" << runs_.dts().InMilliseconds() | 345 << ", dts=" << runs_.dts().InMilliseconds() |
302 << ", size=" << runs_.size(); | 346 << ", size=" << runs_.size(); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
336 return true; | 380 return true; |
337 } | 381 } |
338 | 382 |
339 void MP4StreamParser::ChangeState(State new_state) { | 383 void MP4StreamParser::ChangeState(State new_state) { |
340 DVLOG(2) << "Changing state: " << new_state; | 384 DVLOG(2) << "Changing state: " << new_state; |
341 state_ = new_state; | 385 state_ = new_state; |
342 } | 386 } |
343 | 387 |
344 } // namespace mp4 | 388 } // namespace mp4 |
345 } // namespace media | 389 } // namespace media |
OLD | NEW |