void MP4TrackDemuxer::UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples) { for (size_t i = 0; i < aSamples.Length(); i++) { MediaRawData* sample = aSamples[i]; // Collect telemetry from h264 Annex B SPS. if (mNeedSPSForTelemetry && mp4_demuxer::AnnexB::HasSPS(sample)) { RefPtr<MediaByteBuffer> extradata = mp4_demuxer::AnnexB::ExtractExtraData(sample); mNeedSPSForTelemetry = AccumulateSPSTelemetry(extradata); } if (sample->mCrypto.mValid) { nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter()); writer->mCrypto.mMode = mInfo->mCrypto.mMode; writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize; writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId); } if (mInfo->GetAsVideoInfo()) { sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData; } } if (mNextKeyframeTime.isNothing() || aSamples.LastElement()->mTime >= mNextKeyframeTime.value().ToMicroseconds()) { SetNextKeyFrameTime(); } }
already_AddRefed<MediaRawData> MP4TrackDemuxer::GetNextSample() { RefPtr<MediaRawData> sample = mIterator->GetNext(); if (!sample) { return nullptr; } if (mInfo->GetAsVideoInfo()) { sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData; if (mIsH264) { mp4_demuxer::H264::FrameType type = mp4_demuxer::H264::GetFrameType(sample); switch (type) { case mp4_demuxer::H264::FrameType::I_FRAME: MOZ_FALLTHROUGH; case mp4_demuxer::H264::FrameType::OTHER: { bool keyframe = type == mp4_demuxer::H264::FrameType::I_FRAME; if (sample->mKeyframe != keyframe) { NS_WARNING(nsPrintfCString("Frame incorrectly marked as %skeyframe " "@ pts:%" PRId64 " dur:%" PRId64 " dts:%" PRId64, keyframe ? "" : "non-", sample->mTime.ToMicroseconds(), sample->mDuration.ToMicroseconds(), sample->mTimecode.ToMicroseconds()) .get()); sample->mKeyframe = keyframe; } break; } case mp4_demuxer::H264::FrameType::INVALID: NS_WARNING( nsPrintfCString("Invalid H264 frame @ pts:%" PRId64 " dur:%" PRId64 " dts:%" PRId64, sample->mTime.ToMicroseconds(), sample->mDuration.ToMicroseconds(), sample->mTimecode.ToMicroseconds()) .get()); // We could reject the sample now, however demuxer errors are fatal. // So we keep the invalid frame, relying on the H264 decoder to // handle the error later. // TODO: make demuxer errors non-fatal. break; } } } if (sample->mCrypto.mValid) { nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter()); writer->mCrypto.mMode = mInfo->mCrypto.mMode; // Only use the default key parsed from the moov if we haven't already got // one from the sample group description. if (writer->mCrypto.mKeyId.Length() == 0) { writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize; writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId); } } return sample.forget(); }
UniquePtr<TrackInfo> CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( const nsACString& aCodecMIMEType, const MediaContainerType& aContainerType) { UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aCodecMIMEType); if (trackInfo) { VideoInfo* videoInfo = trackInfo->GetAsVideoInfo(); if (videoInfo) { Maybe<int32_t> maybeWidth = aContainerType.ExtendedType().GetWidth(); if (maybeWidth && *maybeWidth > 0) { videoInfo->mImage.width = *maybeWidth; } Maybe<int32_t> maybeHeight = aContainerType.ExtendedType().GetHeight(); if (maybeHeight && *maybeHeight > 0) { videoInfo->mImage.height = *maybeHeight; } } else if (trackInfo->GetAsAudioInfo()) { AudioInfo* audioInfo = trackInfo->GetAsAudioInfo(); Maybe<int32_t> maybeChannels = aContainerType.ExtendedType().GetChannels(); if (maybeChannels && *maybeChannels > 0) { audioInfo->mChannels = *maybeChannels; } Maybe<int32_t> maybeSamplerate = aContainerType.ExtendedType().GetSamplerate(); if (maybeSamplerate && *maybeSamplerate > 0) { audioInfo->mRate = *maybeSamplerate; } } } return trackInfo; }
void MP4TrackDemuxer::UpdateSamples(nsTArray<nsRefPtr<MediaRawData>>& aSamples) { for (size_t i = 0; i < aSamples.Length(); i++) { MediaRawData* sample = aSamples[i]; if (sample->mCrypto.valid) { nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter()); writer->mCrypto.mode = mInfo->mCrypto.mode; writer->mCrypto.iv_size = mInfo->mCrypto.iv_size; writer->mCrypto.key.AppendElements(mInfo->mCrypto.key); } if (mInfo->GetAsVideoInfo()) { sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData; } } if (mNextKeyframeTime.isNothing() || aSamples.LastElement()->mTime >= mNextKeyframeTime.value().ToMicroseconds()) { SetNextKeyFrameTime(); } }
TEST(stagefright_MPEG4Metadata, test_case_mp4) { for (size_t test = 0; test < ArrayLength(testFiles); ++test) { nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename); ASSERT_FALSE(buffer.IsEmpty()); RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length()); EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream)); RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream); EXPECT_TRUE(metadataBuffer); MP4Metadata metadata(stream); EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack)); EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kAudioTrack)); EXPECT_EQ(testFiles[test].mNumberVideoTracks, metadata.GetNumberTracks(TrackInfo::kVideoTrack)); EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack)); EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1))); EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0)); EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0)); UniquePtr<TrackInfo> trackInfo = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0); if (testFiles[test].mNumberVideoTracks == 0) { EXPECT_TRUE(!trackInfo); } else { EXPECT_TRUE(!!trackInfo); if (trackInfo) { const VideoInfo* videoInfo = trackInfo->GetAsVideoInfo(); EXPECT_TRUE(!!videoInfo); if (videoInfo) { EXPECT_TRUE(videoInfo->IsValid()); EXPECT_TRUE(videoInfo->IsVideo()); EXPECT_EQ(testFiles[test].mWidth, videoInfo->mDisplay.width); EXPECT_EQ(testFiles[test].mHeight, videoInfo->mDisplay.height); FallibleTArray<mp4_demuxer::Index::Indice> indices; EXPECT_TRUE(metadata.ReadTrackIndex(indices, videoInfo->mTrackId)); } } } EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0)); EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0)); // We can see anywhere in any MPEG4. EXPECT_TRUE(metadata.CanSeek()); EXPECT_FALSE(metadata.Crypto().valid); } }
UniquePtr<TrackInfo> CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( const nsACString& aCodecMIMEType, const MediaContainerType& aContainerType) { UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aCodecMIMEType); if (trackInfo) { VideoInfo* videoInfo = trackInfo->GetAsVideoInfo(); if (videoInfo) { Maybe<int32_t> maybeWidth = aContainerType.ExtendedType().GetWidth(); if (maybeWidth && *maybeWidth > 0) { videoInfo->mImage.width = *maybeWidth; } Maybe<int32_t> maybeHeight = aContainerType.ExtendedType().GetHeight(); if (maybeHeight && *maybeHeight > 0) { videoInfo->mImage.height = *maybeHeight; } } } return trackInfo; }