Index: media/mp4/mp4_stream_parser.cc |
diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc |
index 4f4a6db12818e03483bb2cfd59f884b4d94714b2..88bf8bae62372ed0d81600d8911bf2fb188eb4a3 100644 |
--- a/media/mp4/mp4_stream_parser.cc |
+++ b/media/mp4/mp4_stream_parser.cc |
@@ -100,13 +100,14 @@ bool MP4StreamParser::Parse(const uint8* buf, int size) { |
return false; |
} |
- if (!audio_buffers.empty() && |
- (audio_cb_.is_null() || !audio_cb_.Run(audio_buffers))) |
- return false; |
- if (!video_buffers.empty() && |
- (video_cb_.is_null() || !video_cb_.Run(video_buffers))) |
- return false; |
- |
+ if (!audio_buffers.empty()) { |
+ CHECK(!audio_cb_.is_null()); |
+ RCHECK(audio_cb_.Run(audio_buffers)); |
+ } |
+ if (!video_buffers.empty()) { |
+ CHECK(!video_cb_.is_null()); |
+ RCHECK(video_cb_.Run(video_buffers)); |
+ } |
return true; |
} |
@@ -166,6 +167,7 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { |
// RCHECK(entry.format == FOURCC_MP4A || |
// (entry.format == FOURCC_ENCA && |
// entry.sinf.format.format == FOURCC_MP4A)); |
+ RCHECK(EmitKeyNeeded(entry.sinf.info.track_encryption)); |
const ChannelLayout layout = |
AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount); |
@@ -181,6 +183,7 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { |
// RCHECK(entry.format == FOURCC_AVC1 || |
// (entry.format == FOURCC_ENCV && |
// entry.sinf.format.format == FOURCC_AVC1)); |
+ RCHECK(EmitKeyNeeded(entry.sinf.info.track_encryption)); |
// TODO(strobe): Recover correct crop box and pixel aspect ratio |
video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12, |
@@ -227,6 +230,14 @@ bool MP4StreamParser::ParseMoof(BoxReader* reader) { |
return true; |
} |
+bool MP4StreamParser::EmitKeyNeeded(const TrackEncryption& track_encryption) { |
+ if (!track_encryption.is_encrypted) return true; |
+ scoped_array<uint8> kid(new uint8[track_encryption.default_kid.size()]); |
+ memcpy(kid.get(), &track_encryption.default_kid[0], |
+ track_encryption.default_kid.size()); |
+ return need_key_cb_.Run(kid.Pass(), track_encryption.default_kid.size()); |
+} |
+ |
bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, |
BufferQueue* video_buffers, |
bool* err) { |
@@ -260,18 +271,45 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, |
// quite small compared to sample data, so this pattern is useful on |
// memory-constrained devices where the source buffer consumes a substantial |
// portion of the total system memory. |
- if (runs_.NeedsCENC()) { |
- queue_.PeekAt(runs_.cenc_offset() + moof_head_, &buf, &size); |
- return runs_.CacheCENC(buf, size); |
+ if (runs_.AuxInfoNeedsToBeCached()) { |
+ queue_.PeekAt(runs_.aux_info_offset() + moof_head_, &buf, &size); |
+ if (size < runs_.aux_info_size()) return false; |
+ *err = !runs_.CacheAuxInfo(buf, size); |
+ return !*err; |
} |
- queue_.PeekAt(runs_.offset() + moof_head_, &buf, &size); |
- if (size < runs_.size()) return false; |
+ queue_.PeekAt(runs_.sample_offset() + moof_head_, &buf, &size); |
+ if (size < runs_.sample_size()) return false; |
+ |
+ scoped_ptr<DecryptConfig> decrypt_config; |
+ if (runs_.is_encrypted()) |
+ decrypt_config = runs_.GetDecryptConfig(); |
- std::vector<uint8> frame_buf(buf, buf + runs_.size()); |
+ std::vector<uint8> frame_buf(buf, buf + runs_.sample_size()); |
if (video) { |
+ // TODO(strobe): move all this to separate method, add unittest |
RCHECK(AVC::ConvertToAnnexB(size_of_nalu_length_, &frame_buf)); |
+ |
+ if (decrypt_config.get()) { |
+ // XXX(strobe): decide how best to make these modifications |
ddorwin
2012/07/03 21:03:47
TODO
strobe_
2012/07/13 00:47:07
Sorry, "XXX" is personal shorthand for "resolve be
|
+ std::vector<SubsampleEntry>* subsamples = |
+ decrypt_config->mutable_subsamples(); |
+ |
+ const int nalu_size_diff = 4 - size_of_nalu_length_; |
+ size_t expected_size = runs_.sample_size() + |
+ decrypt_config->subsamples().size() * nalu_size_diff; |
+ RCHECK(frame_buf.size() == expected_size); |
+ for (size_t i = 0; i < decrypt_config->subsamples().size(); i++) |
+ (*subsamples)[i].clear_bytes += nalu_size_diff; |
+ } |
+ |
if (!parameter_sets_inserted_) { |
+ if (!runs_.is_keyframe()) { |
+ LOG(INFO) << "XXX skipping initial non-keyframe sample"; |
+ runs_.AdvanceSample(); |
+ return true; |
+ } |
+ |
const AVCDecoderConfigurationRecord* avc_config = NULL; |
for (size_t t = 0; t < moov_->tracks.size(); t++) { |
if (moov_->tracks[t].header.track_id == runs_.track_id()) { |
@@ -281,7 +319,14 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, |
} |
} |
RCHECK(avc_config != NULL); |
- RCHECK(AVC::InsertParameterSets(*avc_config, &frame_buf)); |
+ std::vector<uint8> param_sets; |
+ RCHECK(AVC::ConvertParameterSets(*avc_config, ¶m_sets)); |
+ frame_buf.insert(frame_buf.begin(), |
+ param_sets.begin(), param_sets.end()); |
+ if (decrypt_config.get()) { |
+ (*decrypt_config->mutable_subsamples())[0].clear_bytes += |
+ param_sets.size(); |
+ } |
parameter_sets_inserted_ = true; |
} |
} |
@@ -290,6 +335,9 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, |
StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), |
runs_.is_keyframe()); |
+ if (runs_.is_encrypted()) |
+ stream_buf->SetDecryptConfig(decrypt_config.Pass()); |
+ |
stream_buf->SetDuration(runs_.duration()); |
// We depend on the decoder performing frame reordering without reordering |
// timestamps, and only provide the decode timestamp in the buffer. |
@@ -299,7 +347,7 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, |
<< ", key=" << runs_.is_keyframe() |
<< ", dur=" << runs_.duration().InMilliseconds() |
<< ", dts=" << runs_.dts().InMilliseconds() |
- << ", size=" << runs_.size(); |
+ << ", size=" << runs_.sample_size(); |
if (audio) { |
audio_buffers->push_back(stream_buf); |