diff options
Diffstat (limited to 'media')
17 files changed, 0 insertions, 4444 deletions
diff --git a/media/libstagefright/binding/DecoderData.cpp b/media/libstagefright/binding/DecoderData.cpp index aaf2fb32c..bd51d12aa 100644 --- a/media/libstagefright/binding/DecoderData.cpp +++ b/media/libstagefright/binding/DecoderData.cpp @@ -13,10 +13,6 @@ #include "mozilla/ArrayUtils.h" #include "include/ESDS.h" -#ifdef MOZ_RUST_MP4PARSE -#include "mp4parse.h" -#endif - using namespace stagefright; namespace mp4_demuxer @@ -187,26 +183,6 @@ MP4VideoInfo::Update(const MetaData* aMetaData, const char* aMimeType) } -#ifdef MOZ_RUST_MP4PARSE -void -MP4VideoInfo::Update(const mp4parse_track_info* track, - const mp4parse_track_video_info* video) -{ - if (track->codec == MP4PARSE_CODEC_AVC) { - mMimeType = MEDIA_MIMETYPE_VIDEO_AVC; - } else if (track->codec == MP4PARSE_CODEC_VP9) { - mMimeType = NS_LITERAL_CSTRING("video/vp9"); - } - mTrackId = track->track_id; - mDuration = track->duration; - mMediaTime = track->media_time; - mDisplay.width = video->display_width; - mDisplay.height = video->display_height; - mImage.width = video->image_width; - mImage.height = video->image_height; -} -#endif - bool MP4VideoInfo::IsValid() const { diff --git a/media/libstagefright/binding/MP4Metadata.cpp b/media/libstagefright/binding/MP4Metadata.cpp index eb5350704..686c2b67e 100644 --- a/media/libstagefright/binding/MP4Metadata.cpp +++ b/media/libstagefright/binding/MP4Metadata.cpp @@ -24,14 +24,6 @@ #include <stdint.h> #include <vector> -#ifdef MOZ_RUST_MP4PARSE -// OpusDecoder header is really needed only by MP4 in rust -#include "OpusDecoder.h" -#include "mp4parse.h" - -struct FreeMP4Parser { void operator()(mp4parse_parser* aPtr) { mp4parse_free(aPtr); } }; -#endif - using namespace stagefright; namespace mp4_demuxer @@ -104,65 +96,8 @@ private: bool mCanSeek; }; -#ifdef MOZ_RUST_MP4PARSE - -// Wrap an mp4_demuxer::Stream to remember the read offset. - -class RustStreamAdaptor { -public: - explicit RustStreamAdaptor(Stream* aSource) - : mSource(aSource) - , mOffset(0) - { - } - - ~RustStreamAdaptor() {} - - bool Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read); - -private: - Stream* mSource; - CheckedInt<size_t> mOffset; -}; - -class MP4MetadataRust -{ -public: - explicit MP4MetadataRust(Stream* aSource); - ~MP4MetadataRust(); - - static bool HasCompleteMetadata(Stream* aSource); - static already_AddRefed<mozilla::MediaByteBuffer> Metadata(Stream* aSource); - uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const; - mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType, - size_t aTrackNumber) const; - bool CanSeek() const; - - const CryptoFile& Crypto() const; - - bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID); - -private: - Maybe<uint32_t> TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const; - - CryptoFile mCrypto; - RefPtr<Stream> mSource; - RustStreamAdaptor mRustSource; - mozilla::UniquePtr<mp4parse_parser, FreeMP4Parser> mRustParser; -}; -#endif - MP4Metadata::MP4Metadata(Stream* aSource) : mStagefright(MakeUnique<MP4MetadataStagefright>(aSource)) -#ifdef MOZ_RUST_MP4PARSE - , mRust(MakeUnique<MP4MetadataRust>(aSource)) - , mPreferRust(false) - , mReportedAudioTrackTelemetry(false) - , mReportedVideoTrackTelemetry(false) -#ifndef RELEASE_OR_BETA - , mRustTestMode(MediaPrefs::RustTestMode()) -#endif -#endif { } @@ -202,70 +137,9 @@ MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const uint32_t numTracks = mStagefright->GetNumberTracks(aType); -#ifdef MOZ_RUST_MP4PARSE - if (!mRust) { - return numTracks; - } - - uint32_t numTracksRust = mRust->GetNumberTracks(aType); - MOZ_LOG(sLog, LogLevel::Info, ("%s tracks found: stagefright=%u rust=%u", - TrackTypeToString(aType), numTracks, numTracksRust)); - - bool numTracksMatch = numTracks == numTracksRust; - - if (aType == mozilla::TrackInfo::kAudioTrack && !mReportedAudioTrackTelemetry) { - Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_AUDIO, - numTracksMatch); - mReportedAudioTrackTelemetry = true; - } else if (aType == mozilla::TrackInfo::kVideoTrack && !mReportedVideoTrackTelemetry) { - Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_VIDEO, - numTracksMatch); - mReportedVideoTrackTelemetry = true; - } - - if (mPreferRust || ShouldPreferRust()) { - MOZ_LOG(sLog, LogLevel::Info, ("Preferring rust demuxer")); - mPreferRust = true; - return numTracksRust; - } -#endif // MOZ_RUST_MP4PARSE - return numTracks; } -#ifdef MOZ_RUST_MP4PARSE -bool MP4Metadata::ShouldPreferRust() const { - if (!mRust) { - return false; - } - // See if there's an Opus track. - uint32_t numTracks = mRust->GetNumberTracks(TrackInfo::kAudioTrack); - for (auto i = 0; i < numTracks; i++) { - auto info = mRust->GetTrackInfo(TrackInfo::kAudioTrack, i); - if (!info) { - return false; - } - if (info->mMimeType.EqualsASCII("audio/opus") || - info->mMimeType.EqualsASCII("audio/flac")) { - return true; - } - } - - numTracks = mRust->GetNumberTracks(TrackInfo::kVideoTrack); - for (auto i = 0; i < numTracks; i++) { - auto info = mRust->GetTrackInfo(TrackInfo::kVideoTrack, i); - if (!info) { - return false; - } - if (info->mMimeType.EqualsASCII("video/vp9")) { - return true; - } - } - // Otherwise, fall back. - return false; -} -#endif // MOZ_RUST_MP4PARSE - mozilla::UniquePtr<mozilla::TrackInfo> MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const @@ -273,56 +147,6 @@ MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType, mozilla::UniquePtr<mozilla::TrackInfo> info = mStagefright->GetTrackInfo(aType, aTrackNumber); -#ifdef MOZ_RUST_MP4PARSE - if (!mRust) { - return info; - } - - mozilla::UniquePtr<mozilla::TrackInfo> infoRust = - mRust->GetTrackInfo(aType, aTrackNumber); - -#ifndef RELEASE_OR_BETA - if (mRustTestMode && info) { - MOZ_DIAGNOSTIC_ASSERT(infoRust); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mId == info->mId); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mKind == info->mKind); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mLabel == info->mLabel); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mLanguage == info->mLanguage); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mEnabled == info->mEnabled); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mTrackId == info->mTrackId); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mMimeType == info->mMimeType); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mDuration == info->mDuration); - MOZ_DIAGNOSTIC_ASSERT(infoRust->mMediaTime == info->mMediaTime); - switch (aType) { - case mozilla::TrackInfo::kAudioTrack: { - AudioInfo *audioRust = infoRust->GetAsAudioInfo(), *audio = info->GetAsAudioInfo(); - MOZ_DIAGNOSTIC_ASSERT(audioRust->mRate == audio->mRate); - MOZ_DIAGNOSTIC_ASSERT(audioRust->mChannels == audio->mChannels); - MOZ_DIAGNOSTIC_ASSERT(audioRust->mBitDepth == audio->mBitDepth); - // TODO: These fields aren't implemented in the Rust demuxer yet. - //MOZ_DIAGNOSTIC_ASSERT(audioRust->mProfile != audio->mProfile); - //MOZ_DIAGNOSTIC_ASSERT(audioRust->mExtendedProfile != audio->mExtendedProfile); - break; - } - case mozilla::TrackInfo::kVideoTrack: { - VideoInfo *videoRust = infoRust->GetAsVideoInfo(), *video = info->GetAsVideoInfo(); - MOZ_DIAGNOSTIC_ASSERT(videoRust->mDisplay == video->mDisplay); - MOZ_DIAGNOSTIC_ASSERT(videoRust->mImage == video->mImage); - break; - } - default: - break; - } - } -#endif - - if (!mPreferRust) { - return info; - } - MOZ_ASSERT(infoRust); - return infoRust; -#endif - return info; } @@ -341,12 +165,6 @@ MP4Metadata::Crypto() const bool MP4Metadata::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID) { -#ifdef MOZ_RUST_MP4PARSE - if (mRust && mPreferRust && mRust->ReadTrackIndex(aDest, aTrackID)) { - return true; - } - aDest.Clear(); -#endif return mStagefright->ReadTrackIndex(aDest, aTrackID); } @@ -589,292 +407,4 @@ MP4MetadataStagefright::Metadata(Stream* aSource) return parser->Metadata(); } -#ifdef MOZ_RUST_MP4PARSE -bool -RustStreamAdaptor::Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read) -{ - if (!mOffset.isValid()) { - static LazyLogModule sLog("MP4Metadata"); - MOZ_LOG(sLog, LogLevel::Error, ("Overflow in source stream offset")); - return false; - } - bool rv = mSource->ReadAt(mOffset.value(), buffer, size, bytes_read); - if (rv) { - mOffset += *bytes_read; - } - return rv; -} - -// Wrapper to allow rust to call our read adaptor. -static intptr_t -read_source(uint8_t* buffer, uintptr_t size, void* userdata) -{ - MOZ_ASSERT(buffer); - MOZ_ASSERT(userdata); - - auto source = reinterpret_cast<RustStreamAdaptor*>(userdata); - size_t bytes_read = 0; - bool rv = source->Read(buffer, size, &bytes_read); - if (!rv) { - static LazyLogModule sLog("MP4Metadata"); - MOZ_LOG(sLog, LogLevel::Warning, ("Error reading source data")); - return -1; - } - return bytes_read; -} - -MP4MetadataRust::MP4MetadataRust(Stream* aSource) - : mSource(aSource) - , mRustSource(aSource) -{ - mp4parse_io io = { read_source, &mRustSource }; - mRustParser.reset(mp4parse_new(&io)); - MOZ_ASSERT(mRustParser); - - static LazyLogModule sLog("MP4Metadata"); - mp4parse_error rv = mp4parse_read(mRustParser.get()); - MOZ_LOG(sLog, LogLevel::Debug, ("rust parser returned %d\n", rv)); - Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS, - rv == MP4PARSE_OK); - if (rv != MP4PARSE_OK) { - MOZ_ASSERT(rv > 0); - Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_ERROR_CODE, rv); - } -} - -MP4MetadataRust::~MP4MetadataRust() -{ -} - -bool -TrackTypeEqual(TrackInfo::TrackType aLHS, mp4parse_track_type aRHS) -{ - switch (aLHS) { - case TrackInfo::kAudioTrack: - return aRHS == MP4PARSE_TRACK_TYPE_AUDIO; - case TrackInfo::kVideoTrack: - return aRHS == MP4PARSE_TRACK_TYPE_VIDEO; - default: - return false; - } -} - -uint32_t -MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const -{ - static LazyLogModule sLog("MP4Metadata"); - - uint32_t tracks; - auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks); - if (rv != MP4PARSE_OK) { - MOZ_LOG(sLog, LogLevel::Warning, - ("rust parser error %d counting tracks", rv)); - return 0; - } - MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %u tracks", tracks)); - - uint32_t total = 0; - for (uint32_t i = 0; i < tracks; ++i) { - mp4parse_track_info track_info; - rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info); - if (rv != MP4PARSE_OK) { - continue; - } - if (TrackTypeEqual(aType, track_info.track_type)) { - total += 1; - } - } - - return total; -} - -Maybe<uint32_t> -MP4MetadataRust::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const -{ - uint32_t tracks; - auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks); - if (rv != MP4PARSE_OK) { - return Nothing(); - } - - /* The MP4Metadata API uses a per-TrackType index of tracks, but mp4parse - (and libstagefright) use a global track index. Convert the index by - counting the tracks of the requested type and returning the global - track index when a match is found. */ - uint32_t perType = 0; - for (uint32_t i = 0; i < tracks; ++i) { - mp4parse_track_info track_info; - rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info); - if (rv != MP4PARSE_OK) { - continue; - } - if (TrackTypeEqual(aType, track_info.track_type)) { - if (perType == aTrackNumber) { - return Some(i); - } - perType += 1; - } - } - - return Nothing(); -} - -mozilla::UniquePtr<mozilla::TrackInfo> -MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType, - size_t aTrackNumber) const -{ - static LazyLogModule sLog("MP4Metadata"); - - Maybe<uint32_t> trackIndex = TrackTypeToGlobalTrackIndex(aType, aTrackNumber); - if (trackIndex.isNothing()) { - return nullptr; - } - - mp4parse_track_info info; - auto rv = mp4parse_get_track_info(mRustParser.get(), trackIndex.value(), &info); - if (rv != MP4PARSE_OK) { - MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_info returned %d", rv)); - return nullptr; - } -#ifdef DEBUG - const char* codec_string = "unrecognized"; - switch (info.codec) { - case MP4PARSE_CODEC_UNKNOWN: codec_string = "unknown"; break; - case MP4PARSE_CODEC_AAC: codec_string = "aac"; break; - case MP4PARSE_CODEC_OPUS: codec_string = "opus"; break; - case MP4PARSE_CODEC_FLAC: codec_string = "flac"; break; - case MP4PARSE_CODEC_AVC: codec_string = "h.264"; break; - case MP4PARSE_CODEC_VP9: codec_string = "vp9"; break; - case MP4PARSE_CODEC_MP3: codec_string = "mp3"; break; - } - MOZ_LOG(sLog, LogLevel::Debug, ("track codec %s (%u)\n", - codec_string, info.codec)); -#endif - - // This specialization interface is crazy. - UniquePtr<mozilla::TrackInfo> e; - switch (aType) { - case TrackInfo::TrackType::kAudioTrack: { - mp4parse_track_audio_info audio; - auto rv = mp4parse_get_track_audio_info(mRustParser.get(), trackIndex.value(), &audio); - if (rv != MP4PARSE_OK) { - MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv)); - return nullptr; - } - auto track = mozilla::MakeUnique<mozilla::AudioInfo>(); - if (info.codec == MP4PARSE_CODEC_OPUS) { - track->mMimeType = NS_LITERAL_CSTRING("audio/opus"); - // The Opus decoder expects the container's codec delay or - // pre-skip value, in microseconds, as a 64-bit int at the - // start of the codec-specific config blob. - MOZ_ASSERT(audio.codec_specific_config.data); - MOZ_ASSERT(audio.codec_specific_config.length >= 12); - uint16_t preskip = - LittleEndian::readUint16(audio.codec_specific_config.data + 10); - MOZ_LOG(sLog, LogLevel::Debug, - ("Copying opus pre-skip value of %d as CodecDelay.",(int)preskip)); - OpusDataDecoder::AppendCodecDelay(track->mCodecSpecificConfig, - mozilla::FramesToUsecs(preskip, 48000).value()); - } else if (info.codec == MP4PARSE_CODEC_AAC) { - track->mMimeType = MEDIA_MIMETYPE_AUDIO_AAC; - } else if (info.codec == MP4PARSE_CODEC_FLAC) { - track->mMimeType = MEDIA_MIMETYPE_AUDIO_FLAC; - } else if (info.codec == MP4PARSE_CODEC_MP3) { - track->mMimeType = MEDIA_MIMETYPE_AUDIO_MPEG; - } - track->mCodecSpecificConfig->AppendElements( - audio.codec_specific_config.data, - audio.codec_specific_config.length); - track->mRate = audio.sample_rate; - track->mChannels = audio.channels; - track->mBitDepth = audio.bit_depth; - track->mDuration = info.duration; - track->mMediaTime = info.media_time; - track->mTrackId = info.track_id; - e = Move(track); - } - break; - case TrackInfo::TrackType::kVideoTrack: { - mp4parse_track_video_info video; - auto rv = mp4parse_get_track_video_info(mRustParser.get(), trackIndex.value(), &video); - if (rv != MP4PARSE_OK) { - MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv)); - return nullptr; - } - auto track = mozilla::MakeUnique<MP4VideoInfo>(); - track->Update(&info, &video); - e = Move(track); - } - break; - default: - MOZ_LOG(sLog, LogLevel::Warning, ("unhandled track type %d", aType)); - return nullptr; - break; - } - - // No duration in track, use fragment_duration. - if (e && !e->mDuration) { - mp4parse_fragment_info info; - auto rv = mp4parse_get_fragment_info(mRustParser.get(), &info); - if (rv == MP4PARSE_OK) { - e->mDuration = info.fragment_duration; - } - } - - if (e && e->IsValid()) { - return e; - } - MOZ_LOG(sLog, LogLevel::Debug, ("TrackInfo didn't validate")); - - return nullptr; -} - -bool -MP4MetadataRust::CanSeek() const -{ - MOZ_ASSERT(false, "Not yet implemented"); - return false; -} - -const CryptoFile& -MP4MetadataRust::Crypto() const -{ - MOZ_ASSERT(false, "Not yet implemented"); - return mCrypto; -} - -bool -MP4MetadataRust::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID) -{ - uint8_t fragmented = false; - auto rv = mp4parse_is_fragmented(mRustParser.get(), aTrackID, &fragmented); - if (rv != MP4PARSE_OK) { - return false; - } - - if (fragmented) { - return true; - } - - // For non-fragmented mp4. - NS_WARNING("Not yet implemented"); - - return false; -} - -/*static*/ bool -MP4MetadataRust::HasCompleteMetadata(Stream* aSource) -{ - MOZ_ASSERT(false, "Not yet implemented"); - return false; -} - -/*static*/ already_AddRefed<mozilla::MediaByteBuffer> -MP4MetadataRust::Metadata(Stream* aSource) -{ - MOZ_ASSERT(false, "Not yet implemented"); - return nullptr; -} -#endif - } // namespace mp4_demuxer diff --git a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h index 87262c26a..383c4a13c 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h +++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h @@ -19,14 +19,6 @@ namespace stagefright class MetaData; } -#ifdef MOZ_RUST_MP4PARSE -extern "C" { -typedef struct mp4parse_track_info mp4parse_track_info; -typedef struct mp4parse_track_audio_info mp4parse_track_audio_info; -typedef struct mp4parse_track_video_info mp4parse_track_video_info; -} -#endif - namespace mp4_demuxer { @@ -80,11 +72,6 @@ public: void Update(const stagefright::MetaData* aMetaData, const char* aMimeType); -#ifdef MOZ_RUST_MP4PARSE - void Update(const mp4parse_track_info* track, - const mp4parse_track_video_info* video); -#endif - virtual bool IsValid() const override; }; diff --git a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h index 83eca69d3..2907f4c45 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h +++ b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h @@ -37,16 +37,6 @@ public: private: UniquePtr<MP4MetadataStagefright> mStagefright; -#ifdef MOZ_RUST_MP4PARSE - UniquePtr<MP4MetadataRust> mRust; - mutable bool mPreferRust; - mutable bool mReportedAudioTrackTelemetry; - mutable bool mReportedVideoTrackTelemetry; -#ifndef RELEASE_OR_BETA - mutable bool mRustTestMode; -#endif - bool ShouldPreferRust() const; -#endif }; } // namespace mp4_demuxer diff --git a/media/libstagefright/binding/include/mp4parse.h b/media/libstagefright/binding/include/mp4parse.h deleted file mode 100644 index 6650661cb..000000000 --- a/media/libstagefright/binding/include/mp4parse.h +++ /dev/null @@ -1,113 +0,0 @@ - -#ifndef cheddar_generated_mp4parse_h -#define cheddar_generated_mp4parse_h - - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdint.h> -#include <stdbool.h> - -// THIS FILE IS AUTOGENERATED BY mp4parse-rust/build.rs - DO NOT EDIT - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -typedef enum mp4parse_error { - MP4PARSE_OK = 0, - MP4PARSE_ERROR_BADARG = 1, - MP4PARSE_ERROR_INVALID = 2, - MP4PARSE_ERROR_UNSUPPORTED = 3, - MP4PARSE_ERROR_EOF = 4, - MP4PARSE_ERROR_IO = 5, -} mp4parse_error; - -typedef enum mp4parse_track_type { - MP4PARSE_TRACK_TYPE_VIDEO = 0, - MP4PARSE_TRACK_TYPE_AUDIO = 1, -} mp4parse_track_type; - -typedef enum mp4parse_codec { - MP4PARSE_CODEC_UNKNOWN, - MP4PARSE_CODEC_AAC, - MP4PARSE_CODEC_FLAC, - MP4PARSE_CODEC_OPUS, - MP4PARSE_CODEC_AVC, - MP4PARSE_CODEC_VP9, - MP4PARSE_CODEC_MP3, -} mp4parse_codec; - -typedef struct mp4parse_track_info { - mp4parse_track_type track_type; - mp4parse_codec codec; - uint32_t track_id; - uint64_t duration; - int64_t media_time; -} mp4parse_track_info; - -typedef struct mp4parse_codec_specific_config { - uint32_t length; - uint8_t const* data; -} mp4parse_codec_specific_config; - -typedef struct mp4parse_track_audio_info { - uint16_t channels; - uint16_t bit_depth; - uint32_t sample_rate; - mp4parse_codec_specific_config codec_specific_config; -} mp4parse_track_audio_info; - -typedef struct mp4parse_track_video_info { - uint32_t display_width; - uint32_t display_height; - uint16_t image_width; - uint16_t image_height; -} mp4parse_track_video_info; - -typedef struct mp4parse_fragment_info { - uint64_t fragment_duration; -} mp4parse_fragment_info; - -typedef struct mp4parse_parser mp4parse_parser; - -typedef struct mp4parse_io { - intptr_t (*read)(uint8_t* buffer, uintptr_t size, void* userdata); - void* userdata; -} mp4parse_io; - -/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`. -mp4parse_parser* mp4parse_new(mp4parse_io const* io); - -/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`. -void mp4parse_free(mp4parse_parser* parser); - -/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error. -mp4parse_error mp4parse_read(mp4parse_parser* parser); - -/// Return the number of tracks parsed by previous `mp4parse_read()` call. -mp4parse_error mp4parse_get_track_count(mp4parse_parser const* parser, uint32_t* count); - -/// Fill the supplied `mp4parse_track_info` with metadata for `track`. -mp4parse_error mp4parse_get_track_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_info* info); - -/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`. -mp4parse_error mp4parse_get_track_audio_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_audio_info* info); - -/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`. -mp4parse_error mp4parse_get_track_video_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_video_info* info); - -mp4parse_error mp4parse_get_fragment_info(mp4parse_parser* parser, mp4parse_fragment_info* info); - -mp4parse_error mp4parse_is_fragmented(mp4parse_parser* parser, uint32_t track_id, uint8_t* fragmented); - - - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/media/libstagefright/binding/mp4parse-cargo.patch b/media/libstagefright/binding/mp4parse-cargo.patch deleted file mode 100644 index 9b0ca7134..000000000 --- a/media/libstagefright/binding/mp4parse-cargo.patch +++ /dev/null @@ -1,45 +0,0 @@ -diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml -index ff9422c..814c4c6 100644 ---- a/media/libstagefright/binding/mp4parse/Cargo.toml -+++ b/media/libstagefright/binding/mp4parse/Cargo.toml -@@ -18,17 +18,11 @@ exclude = [ - ] - - [dependencies] --byteorder = "0.5.0" --afl = { version = "0.1.1", optional = true } --afl-plugin = { version = "0.1.1", optional = true } --abort_on_panic = { version = "1.0.0", optional = true } -+byteorder = "0.5.0" - - [dev-dependencies] - test-assembler = "0.1.2" - --[features] --fuzz = ["afl", "afl-plugin", "abort_on_panic"] -- - # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. - [profile.release] - debug-assertions = true -diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml -index aeeebc65..5c0836a 100644 ---- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml -+++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml -@@ -18,17 +18,9 @@ exclude = [ - "*.mp4", - ] - --build = "build.rs" -- - [dependencies] - "mp4parse" = {version = "0.6.0", path = "../mp4parse"} - --[build-dependencies] --rusty-cheddar = "0.3.2" -- --[features] --fuzz = ["mp4parse/fuzz"] -- - # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. - [profile.release] - debug-assertions = true diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml deleted file mode 100644 index affcef72b..000000000 --- a/media/libstagefright/binding/mp4parse/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "mp4parse" -version = "0.6.0" -authors = [ - "Ralph Giles <giles@mozilla.com>", - "Matthew Gregan <kinetik@flim.org>", - "Alfredo Yang <ayang@mozilla.com>", -] - -description = "Parser for ISO base media file format (mp4)" -documentation = "https://mp4parse-docs.surge.sh/mp4parse/" -license = "MPL-2.0" - -repository = "https://github.com/mozilla/mp4parse-rust" - -# Avoid complaints about trying to package test files. -exclude = [ - "*.mp4", -] - -[dependencies] -byteorder = "0.5.0" - -[dev-dependencies] -test-assembler = "0.1.2" - -# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. -[profile.release] -debug-assertions = true diff --git a/media/libstagefright/binding/mp4parse/src/boxes.rs b/media/libstagefright/binding/mp4parse/src/boxes.rs deleted file mode 100644 index 689439ade..000000000 --- a/media/libstagefright/binding/mp4parse/src/boxes.rs +++ /dev/null @@ -1,62 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -macro_rules! box_database { - ($($boxenum:ident $boxtype:expr),*,) => { - #[derive(Debug, Clone, Copy, PartialEq)] - pub enum BoxType { - $($boxenum),*, - UnknownBox(u32), - } - - impl From<u32> for BoxType { - fn from(t: u32) -> BoxType { - use self::BoxType::*; - match t { - $($boxtype => $boxenum),*, - _ => UnknownBox(t), - } - } - } - } -} - -box_database!( - FileTypeBox 0x66747970, // "ftyp" - MovieBox 0x6d6f6f76, // "moov" - MovieHeaderBox 0x6d766864, // "mvhd" - TrackBox 0x7472616b, // "trak" - TrackHeaderBox 0x746b6864, // "tkhd" - EditBox 0x65647473, // "edts" - MediaBox 0x6d646961, // "mdia" - EditListBox 0x656c7374, // "elst" - MediaHeaderBox 0x6d646864, // "mdhd" - HandlerBox 0x68646c72, // "hdlr" - MediaInformationBox 0x6d696e66, // "minf" - SampleTableBox 0x7374626c, // "stbl" - SampleDescriptionBox 0x73747364, // "stsd" - TimeToSampleBox 0x73747473, // "stts" - SampleToChunkBox 0x73747363, // "stsc" - SampleSizeBox 0x7374737a, // "stsz" - ChunkOffsetBox 0x7374636f, // "stco" - ChunkLargeOffsetBox 0x636f3634, // "co64" - SyncSampleBox 0x73747373, // "stss" - AVCSampleEntry 0x61766331, // "avc1" - AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec. - AVCConfigurationBox 0x61766343, // "avcC" - MP4AudioSampleEntry 0x6d703461, // "mp4a" - ESDBox 0x65736473, // "esds" - VP8SampleEntry 0x76703038, // "vp08" - VP9SampleEntry 0x76703039, // "vp09" - VPCodecConfigurationBox 0x76706343, // "vpcC" - FLACSampleEntry 0x664c6143, // "fLaC" - FLACSpecificBox 0x64664c61, // "dfLa" - OpusSampleEntry 0x4f707573, // "Opus" - OpusSpecificBox 0x644f7073, // "dOps" - ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec. - ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec. - MovieExtendsBox 0x6d766578, // "mvex" - MovieExtendsHeaderBox 0x6d656864, // "mehd" - QTWaveAtom 0x77617665, // "wave" - quicktime atom -); diff --git a/media/libstagefright/binding/mp4parse/src/lib.rs b/media/libstagefright/binding/mp4parse/src/lib.rs deleted file mode 100644 index 37606a5e2..000000000 --- a/media/libstagefright/binding/mp4parse/src/lib.rs +++ /dev/null @@ -1,1704 +0,0 @@ -//! Module for parsing ISO Base Media Format aka video/mp4 streams. - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -#![cfg_attr(feature = "fuzz", feature(plugin))] -#![cfg_attr(feature = "fuzz", plugin(afl_plugin))] -#[cfg(feature = "fuzz")] -extern crate afl; - -extern crate byteorder; -use byteorder::ReadBytesExt; -use std::io::{Read, Take}; -use std::io::Cursor; -use std::cmp; - -mod boxes; -use boxes::BoxType; - -// Unit tests. -#[cfg(test)] -mod tests; - -// Arbitrary buffer size limit used for raw read_bufs on a box. -const BUF_SIZE_LIMIT: u64 = 1024 * 1024; - -static DEBUG_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT; - -pub fn set_debug_mode(mode: bool) { - DEBUG_MODE.store(mode, std::sync::atomic::Ordering::SeqCst); -} - -#[inline(always)] -fn get_debug_mode() -> bool { - DEBUG_MODE.load(std::sync::atomic::Ordering::Relaxed) -} - -macro_rules! log { - ($($args:tt)*) => ( - if get_debug_mode() { - println!( $( $args )* ); - } - ) -} - -/// Describes parser failures. -/// -/// This enum wraps the standard `io::Error` type, unified with -/// our own parser error states and those of crates we use. -#[derive(Debug)] -pub enum Error { - /// Parse error caused by corrupt or malformed data. - InvalidData(&'static str), - /// Parse error caused by limited parser support rather than invalid data. - Unsupported(&'static str), - /// Reflect `std::io::ErrorKind::UnexpectedEof` for short data. - UnexpectedEOF, - /// Propagate underlying errors from `std::io`. - Io(std::io::Error), - /// read_mp4 terminated without detecting a moov box. - NoMoov, -} - -impl From<std::io::Error> for Error { - fn from(err: std::io::Error) -> Error { - match err.kind() { - std::io::ErrorKind::UnexpectedEof => Error::UnexpectedEOF, - _ => Error::Io(err), - } - } -} - -impl From<std::string::FromUtf8Error> for Error { - fn from(_: std::string::FromUtf8Error) -> Error { - Error::InvalidData("invalid utf8") - } -} - -/// Result shorthand using our Error enum. -pub type Result<T> = std::result::Result<T, Error>; - -/// Basic ISO box structure. -/// -/// mp4 files are a sequence of possibly-nested 'box' structures. Each box -/// begins with a header describing the length of the box's data and a -/// four-byte box type which identifies the type of the box. Together these -/// are enough to interpret the contents of that section of the file. -#[derive(Debug, Clone, Copy)] -struct BoxHeader { - /// Box type. - name: BoxType, - /// Size of the box in bytes. - size: u64, - /// Offset to the start of the contained data (or header size). - offset: u64, -} - -/// File type box 'ftyp'. -#[derive(Debug)] -struct FileTypeBox { - major_brand: u32, - minor_version: u32, - compatible_brands: Vec<u32>, -} - -/// Movie header box 'mvhd'. -#[derive(Debug)] -struct MovieHeaderBox { - pub timescale: u32, - duration: u64, -} - -/// Track header box 'tkhd' -#[derive(Debug, Clone)] -pub struct TrackHeaderBox { - track_id: u32, - pub disabled: bool, - pub duration: u64, - pub width: u32, - pub height: u32, -} - -/// Edit list box 'elst' -#[derive(Debug)] -struct EditListBox { - edits: Vec<Edit>, -} - -#[derive(Debug)] -struct Edit { - segment_duration: u64, - media_time: i64, - media_rate_integer: i16, - media_rate_fraction: i16, -} - -/// Media header box 'mdhd' -#[derive(Debug)] -struct MediaHeaderBox { - timescale: u32, - duration: u64, -} - -// Chunk offset box 'stco' or 'co64' -#[derive(Debug)] -struct ChunkOffsetBox { - offsets: Vec<u64>, -} - -// Sync sample box 'stss' -#[derive(Debug)] -struct SyncSampleBox { - samples: Vec<u32>, -} - -// Sample to chunk box 'stsc' -#[derive(Debug)] -struct SampleToChunkBox { - samples: Vec<SampleToChunk>, -} - -#[derive(Debug)] -struct SampleToChunk { - first_chunk: u32, - samples_per_chunk: u32, - sample_description_index: u32, -} - -// Sample size box 'stsz' -#[derive(Debug)] -struct SampleSizeBox { - sample_size: u32, - sample_sizes: Vec<u32>, -} - -// Time to sample box 'stts' -#[derive(Debug)] -struct TimeToSampleBox { - samples: Vec<Sample>, -} - -#[derive(Debug)] -struct Sample { - sample_count: u32, - sample_delta: u32, -} - -// Handler reference box 'hdlr' -#[derive(Debug)] -struct HandlerBox { - handler_type: u32, -} - -// Sample description box 'stsd' -#[derive(Debug)] -struct SampleDescriptionBox { - descriptions: Vec<SampleEntry>, -} - -#[derive(Debug, Clone)] -pub enum SampleEntry { - Audio(AudioSampleEntry), - Video(VideoSampleEntry), - Unknown, -} - -#[allow(non_camel_case_types)] -#[derive(Debug, Clone)] -pub struct ES_Descriptor { - pub audio_codec: CodecType, - pub audio_sample_rate: Option<u32>, - pub audio_channel_count: Option<u16>, - pub codec_specific_config: Vec<u8>, -} - -#[allow(non_camel_case_types)] -#[derive(Debug, Clone)] -pub enum AudioCodecSpecific { - ES_Descriptor(ES_Descriptor), - FLACSpecificBox(FLACSpecificBox), - OpusSpecificBox(OpusSpecificBox), -} - -#[derive(Debug, Clone)] -pub struct AudioSampleEntry { - data_reference_index: u16, - pub channelcount: u16, - pub samplesize: u16, - pub samplerate: u32, - pub codec_specific: AudioCodecSpecific, -} - -#[derive(Debug, Clone)] -pub enum VideoCodecSpecific { - AVCConfig(Vec<u8>), - VPxConfig(VPxConfigBox), -} - -#[derive(Debug, Clone)] -pub struct VideoSampleEntry { - data_reference_index: u16, - pub width: u16, - pub height: u16, - pub codec_specific: VideoCodecSpecific, -} - -/// Represent a Video Partition Codec Configuration 'vpcC' box (aka vp9). -#[derive(Debug, Clone)] -pub struct VPxConfigBox { - profile: u8, - level: u8, - pub bit_depth: u8, - pub color_space: u8, // Really an enum - pub chroma_subsampling: u8, - transfer_function: u8, - video_full_range: bool, - pub codec_init: Vec<u8>, // Empty for vp8/vp9. -} - -#[derive(Debug, Clone)] -pub struct FLACMetadataBlock { - pub block_type: u8, - pub data: Vec<u8>, -} - -/// Represet a FLACSpecificBox 'dfLa' -#[derive(Debug, Clone)] -pub struct FLACSpecificBox { - version: u8, - pub blocks: Vec<FLACMetadataBlock>, -} - -#[derive(Debug, Clone)] -struct ChannelMappingTable { - stream_count: u8, - coupled_count: u8, - channel_mapping: Vec<u8>, -} - -/// Represent an OpusSpecificBox 'dOps' -#[derive(Debug, Clone)] -pub struct OpusSpecificBox { - pub version: u8, - output_channel_count: u8, - pre_skip: u16, - input_sample_rate: u32, - output_gain: i16, - channel_mapping_family: u8, - channel_mapping_table: Option<ChannelMappingTable>, -} - -#[derive(Debug)] -pub struct MovieExtendsBox { - pub fragment_duration: Option<MediaScaledTime>, -} - -/// Internal data structures. -#[derive(Debug, Default)] -pub struct MediaContext { - pub timescale: Option<MediaTimeScale>, - /// Tracks found in the file. - pub tracks: Vec<Track>, - pub mvex: Option<MovieExtendsBox>, -} - -impl MediaContext { - pub fn new() -> MediaContext { - Default::default() - } -} - -#[derive(Debug, PartialEq)] -pub enum TrackType { - Audio, - Video, - Unknown, -} - -impl Default for TrackType { - fn default() -> Self { TrackType::Unknown } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum CodecType { - Unknown, - MP3, - AAC, - FLAC, - Opus, - H264, - VP9, - VP8, - EncryptedVideo, - EncryptedAudio, -} - -impl Default for CodecType { - fn default() -> Self { CodecType::Unknown } -} - -/// The media's global (mvhd) timescale in units per second. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct MediaTimeScale(pub u64); - -/// A time to be scaled by the media's global (mvhd) timescale. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct MediaScaledTime(pub u64); - -/// The track's local (mdhd) timescale. -/// Members are timescale units per second and the track id. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct TrackTimeScale(pub u64, pub usize); - -/// A time to be scaled by the track's local (mdhd) timescale. -/// Members are time in scale units and the track id. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct TrackScaledTime(pub u64, pub usize); - -/// A fragmented file contains no sample data in stts, stsc, and stco. -#[derive(Debug, Default)] -pub struct EmptySampleTableBoxes { - pub empty_stts : bool, - pub empty_stsc : bool, - pub empty_stco : bool, -} - -/// Check boxes contain data. -impl EmptySampleTableBoxes { - pub fn all_empty(&self) -> bool { - self.empty_stts & self.empty_stsc & self.empty_stco - } -} - -#[derive(Debug, Default)] -pub struct Track { - id: usize, - pub track_type: TrackType, - pub empty_duration: Option<MediaScaledTime>, - pub media_time: Option<TrackScaledTime>, - pub timescale: Option<TrackTimeScale>, - pub duration: Option<TrackScaledTime>, - pub track_id: Option<u32>, - pub codec_type: CodecType, - pub empty_sample_boxes: EmptySampleTableBoxes, - pub data: Option<SampleEntry>, - pub tkhd: Option<TrackHeaderBox>, // TODO(kinetik): find a nicer way to export this. -} - -impl Track { - fn new(id: usize) -> Track { - Track { id: id, ..Default::default() } - } -} - -struct BMFFBox<'a, T: 'a + Read> { - head: BoxHeader, - content: Take<&'a mut T>, -} - -struct BoxIter<'a, T: 'a + Read> { - src: &'a mut T, -} - -impl<'a, T: Read> BoxIter<'a, T> { - fn new(src: &mut T) -> BoxIter<T> { - BoxIter { src: src } - } - - fn next_box(&mut self) -> Result<Option<BMFFBox<T>>> { - let r = read_box_header(self.src); - match r { - Ok(h) => Ok(Some(BMFFBox { - head: h, - content: self.src.take(h.size - h.offset), - })), - Err(Error::UnexpectedEOF) => Ok(None), - Err(e) => Err(e), - } - } -} - -impl<'a, T: Read> Read for BMFFBox<'a, T> { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { - self.content.read(buf) - } -} - -impl<'a, T: Read> BMFFBox<'a, T> { - fn bytes_left(&self) -> usize { - self.content.limit() as usize - } - - fn get_header(&self) -> &BoxHeader { - &self.head - } - - fn box_iter<'b>(&'b mut self) -> BoxIter<BMFFBox<'a, T>> { - BoxIter::new(self) - } -} - -/// Read and parse a box header. -/// -/// Call this first to determine the type of a particular mp4 box -/// and its length. Used internally for dispatching to specific -/// parsers for the internal content, or to get the length to -/// skip unknown or uninteresting boxes. -fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> { - let size32 = try!(be_u32(src)); - let name = BoxType::from(try!(be_u32(src))); - let size = match size32 { - // valid only for top-level box and indicates it's the last box in the file. usually mdat. - 0 => return Err(Error::Unsupported("unknown sized box")), - 1 => { - let size64 = try!(be_u64(src)); - if size64 < 16 { - return Err(Error::InvalidData("malformed wide size")); - } - size64 - } - 2...7 => return Err(Error::InvalidData("malformed size")), - _ => size32 as u64, - }; - let offset = match size32 { - 1 => 4 + 4 + 8, - _ => 4 + 4, - }; - assert!(offset <= size); - Ok(BoxHeader { - name: name, - size: size, - offset: offset, - }) -} - -/// Parse the extra header fields for a full box. -fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> Result<(u8, u32)> { - let version = try!(src.read_u8()); - let flags_a = try!(src.read_u8()); - let flags_b = try!(src.read_u8()); - let flags_c = try!(src.read_u8()); - Ok((version, - (flags_a as u32) << 16 | (flags_b as u32) << 8 | (flags_c as u32))) -} - -/// Skip over the entire contents of a box. -fn skip_box_content<T: Read>(src: &mut BMFFBox<T>) -> Result<()> { - // Skip the contents of unknown chunks. - let to_skip = { - let header = src.get_header(); - log!("{:?} (skipped)", header); - (header.size - header.offset) as usize - }; - assert!(to_skip == src.bytes_left()); - skip(src, to_skip) -} - -/// Skip over the remain data of a box. -fn skip_box_remain<T: Read>(src: &mut BMFFBox<T>) -> Result<()> { - let remain = { - let header = src.get_header(); - let len = src.bytes_left(); - log!("remain {} (skipped) in {:?}", len, header); - len - }; - skip(src, remain) -} - -macro_rules! check_parser_state { - ( $src:expr ) => { - if $src.limit() > 0 { - log!("bad parser state: {} content bytes left", $src.limit()); - return Err(Error::InvalidData("unread box content or bad parser sync")); - } - } -} - -/// Read the contents of a box, including sub boxes. -/// -/// Metadata is accumulated in the passed-through `MediaContext` struct, -/// which can be examined later. -pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> { - let mut found_ftyp = false; - let mut found_moov = false; - // TODO(kinetik): Top-level parsing should handle zero-sized boxes - // rather than throwing an error. - let mut iter = BoxIter::new(f); - while let Some(mut b) = try!(iter.next_box()) { - // box ordering: ftyp before any variable length box (inc. moov), - // but may not be first box in file if file signatures etc. present - // fragmented mp4 order: ftyp, moov, pairs of moof/mdat (1-multiple), mfra - - // "special": uuid, wide (= 8 bytes) - // isom: moov, mdat, free, skip, udta, ftyp, moof, mfra - // iso2: pdin, meta - // iso3: meco - // iso5: styp, sidx, ssix, prft - // unknown, maybe: id32 - - // qt: pnot - - // possibly allow anything where all printable and/or all lowercase printable - // "four printable characters from the ISO 8859-1 character set" - match b.head.name { - BoxType::FileTypeBox => { - let ftyp = try!(read_ftyp(&mut b)); - found_ftyp = true; - log!("{:?}", ftyp); - } - BoxType::MovieBox => { - try!(read_moov(&mut b, context)); - found_moov = true; - } - _ => try!(skip_box_content(&mut b)), - }; - check_parser_state!(b.content); - if found_moov { - log!("found moov {}, could stop pure 'moov' parser now", if found_ftyp { - "and ftyp" - } else { - "but no ftyp" - }); - } - } - - // XXX(kinetik): This isn't perfect, as a "moov" with no contents is - // treated as okay but we haven't found anything useful. Needs more - // thought for clearer behaviour here. - if found_moov { - Ok(()) - } else { - Err(Error::NoMoov) - } -} - -fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<(MovieHeaderBox, Option<MediaTimeScale>)> { - let mvhd = try!(read_mvhd(f)); - if mvhd.timescale == 0 { - return Err(Error::InvalidData("zero timescale in mdhd")); - } - let timescale = Some(MediaTimeScale(mvhd.timescale as u64)); - Ok((mvhd, timescale)) -} - -fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<()> { - let mut iter = f.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::MovieHeaderBox => { - let (mvhd, timescale) = try!(parse_mvhd(&mut b)); - context.timescale = timescale; - log!("{:?}", mvhd); - } - BoxType::TrackBox => { - let mut track = Track::new(context.tracks.len()); - try!(read_trak(&mut b, &mut track)); - context.tracks.push(track); - } - BoxType::MovieExtendsBox => { - let mvex = try!(read_mvex(&mut b)); - log!("{:?}", mvex); - context.mvex = Some(mvex); - } - _ => try!(skip_box_content(&mut b)), - }; - check_parser_state!(b.content); - } - Ok(()) -} - -fn read_mvex<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieExtendsBox> { - let mut iter = src.box_iter(); - let mut fragment_duration = None; - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::MovieExtendsHeaderBox => { - let duration = try!(read_mehd(&mut b)); - fragment_duration = Some(duration); - }, - _ => try!(skip_box_content(&mut b)), - } - } - Ok(MovieExtendsBox { - fragment_duration: fragment_duration, - }) -} - -fn read_mehd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaScaledTime> { - let (version, _) = try!(read_fullbox_extra(src)); - let fragment_duration = match version { - 1 => try!(be_u64(src)), - 0 => try!(be_u32(src)) as u64, - _ => return Err(Error::InvalidData("unhandled mehd version")), - }; - Ok(MediaScaledTime(fragment_duration)) -} - -fn read_trak<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { - let mut iter = f.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::TrackHeaderBox => { - let tkhd = try!(read_tkhd(&mut b)); - track.track_id = Some(tkhd.track_id); - track.tkhd = Some(tkhd.clone()); - log!("{:?}", tkhd); - } - BoxType::EditBox => try!(read_edts(&mut b, track)), - BoxType::MediaBox => try!(read_mdia(&mut b, track)), - _ => try!(skip_box_content(&mut b)), - }; - check_parser_state!(b.content); - } - Ok(()) -} - -fn read_edts<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { - let mut iter = f.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::EditListBox => { - let elst = try!(read_elst(&mut b)); - let mut empty_duration = 0; - let mut idx = 0; - if elst.edits.len() > 2 { - return Err(Error::Unsupported("more than two edits")); - } - if elst.edits[idx].media_time == -1 { - empty_duration = elst.edits[idx].segment_duration; - if elst.edits.len() < 2 { - return Err(Error::InvalidData("expected additional edit")); - } - idx += 1; - } - track.empty_duration = Some(MediaScaledTime(empty_duration)); - if elst.edits[idx].media_time < 0 { - return Err(Error::InvalidData("unexpected negative media time in edit")); - } - track.media_time = Some(TrackScaledTime(elst.edits[idx].media_time as u64, - track.id)); - log!("{:?}", elst); - } - _ => try!(skip_box_content(&mut b)), - }; - check_parser_state!(b.content); - } - Ok(()) -} - -fn parse_mdhd<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<(MediaHeaderBox, Option<TrackScaledTime>, Option<TrackTimeScale>)> { - let mdhd = try!(read_mdhd(f)); - let duration = match mdhd.duration { - std::u64::MAX => None, - duration => Some(TrackScaledTime(duration, track.id)), - }; - if mdhd.timescale == 0 { - return Err(Error::InvalidData("zero timescale in mdhd")); - } - let timescale = Some(TrackTimeScale(mdhd.timescale as u64, track.id)); - Ok((mdhd, duration, timescale)) -} - -fn read_mdia<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { - let mut iter = f.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::MediaHeaderBox => { - let (mdhd, duration, timescale) = try!(parse_mdhd(&mut b, track)); - track.duration = duration; - track.timescale = timescale; - log!("{:?}", mdhd); - } - BoxType::HandlerBox => { - let hdlr = try!(read_hdlr(&mut b)); - match hdlr.handler_type { - 0x76696465 /* 'vide' */ => track.track_type = TrackType::Video, - 0x736f756e /* 'soun' */ => track.track_type = TrackType::Audio, - _ => (), - } - log!("{:?}", hdlr); - } - BoxType::MediaInformationBox => try!(read_minf(&mut b, track)), - _ => try!(skip_box_content(&mut b)), - }; - check_parser_state!(b.content); - } - Ok(()) -} - -fn read_minf<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { - let mut iter = f.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::SampleTableBox => try!(read_stbl(&mut b, track)), - _ => try!(skip_box_content(&mut b)), - }; - check_parser_state!(b.content); - } - Ok(()) -} - -fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { - let mut iter = f.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::SampleDescriptionBox => { - let stsd = try!(read_stsd(&mut b, track)); - log!("{:?}", stsd); - } - BoxType::TimeToSampleBox => { - let stts = try!(read_stts(&mut b)); - track.empty_sample_boxes.empty_stts = stts.samples.is_empty(); - log!("{:?}", stts); - } - BoxType::SampleToChunkBox => { - let stsc = try!(read_stsc(&mut b)); - track.empty_sample_boxes.empty_stsc = stsc.samples.is_empty(); - log!("{:?}", stsc); - } - BoxType::SampleSizeBox => { - let stsz = try!(read_stsz(&mut b)); - log!("{:?}", stsz); - } - BoxType::ChunkOffsetBox => { - let stco = try!(read_stco(&mut b)); - track.empty_sample_boxes.empty_stco = stco.offsets.is_empty(); - log!("{:?}", stco); - } - BoxType::ChunkLargeOffsetBox => { - let co64 = try!(read_co64(&mut b)); - log!("{:?}", co64); - } - BoxType::SyncSampleBox => { - let stss = try!(read_stss(&mut b)); - log!("{:?}", stss); - } - _ => try!(skip_box_content(&mut b)), - }; - check_parser_state!(b.content); - } - Ok(()) -} - -/// Parse an ftyp box. -fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> { - let major = try!(be_u32(src)); - let minor = try!(be_u32(src)); - let bytes_left = src.bytes_left(); - if bytes_left % 4 != 0 { - return Err(Error::InvalidData("invalid ftyp size")); - } - // Is a brand_count of zero valid? - let brand_count = bytes_left / 4; - let mut brands = Vec::new(); - for _ in 0..brand_count { - brands.push(try!(be_u32(src))); - } - Ok(FileTypeBox { - major_brand: major, - minor_version: minor, - compatible_brands: brands, - }) -} - -/// Parse an mvhd box. -fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> { - let (version, _) = try!(read_fullbox_extra(src)); - match version { - // 64 bit creation and modification times. - 1 => { - try!(skip(src, 16)); - } - // 32 bit creation and modification times. - 0 => { - try!(skip(src, 8)); - } - _ => return Err(Error::InvalidData("unhandled mvhd version")), - } - let timescale = try!(be_u32(src)); - let duration = match version { - 1 => try!(be_u64(src)), - 0 => { - let d = try!(be_u32(src)); - if d == std::u32::MAX { - std::u64::MAX - } else { - d as u64 - } - } - _ => return Err(Error::InvalidData("unhandled mvhd version")), - }; - // Skip remaining fields. - try!(skip(src, 80)); - Ok(MovieHeaderBox { - timescale: timescale, - duration: duration, - }) -} - -/// Parse a tkhd box. -fn read_tkhd<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackHeaderBox> { - let (version, flags) = try!(read_fullbox_extra(src)); - let disabled = flags & 0x1u32 == 0 || flags & 0x2u32 == 0; - match version { - // 64 bit creation and modification times. - 1 => { - try!(skip(src, 16)); - } - // 32 bit creation and modification times. - 0 => { - try!(skip(src, 8)); - } - _ => return Err(Error::InvalidData("unhandled tkhd version")), - } - let track_id = try!(be_u32(src)); - try!(skip(src, 4)); - let duration = match version { - 1 => try!(be_u64(src)), - 0 => try!(be_u32(src)) as u64, - _ => return Err(Error::InvalidData("unhandled tkhd version")), - }; - // Skip uninteresting fields. - try!(skip(src, 52)); - let width = try!(be_u32(src)); - let height = try!(be_u32(src)); - Ok(TrackHeaderBox { - track_id: track_id, - disabled: disabled, - duration: duration, - width: width, - height: height, - }) -} - -/// Parse a elst box. -fn read_elst<T: Read>(src: &mut BMFFBox<T>) -> Result<EditListBox> { - let (version, _) = try!(read_fullbox_extra(src)); - let edit_count = try!(be_u32(src)); - if edit_count == 0 { - return Err(Error::InvalidData("invalid edit count")); - } - let mut edits = Vec::new(); - for _ in 0..edit_count { - let (segment_duration, media_time) = match version { - 1 => { - // 64 bit segment duration and media times. - (try!(be_u64(src)), try!(be_i64(src))) - } - 0 => { - // 32 bit segment duration and media times. - (try!(be_u32(src)) as u64, try!(be_i32(src)) as i64) - } - _ => return Err(Error::InvalidData("unhandled elst version")), - }; - let media_rate_integer = try!(be_i16(src)); - let media_rate_fraction = try!(be_i16(src)); - edits.push(Edit { - segment_duration: segment_duration, - media_time: media_time, - media_rate_integer: media_rate_integer, - media_rate_fraction: media_rate_fraction, - }) - } - - Ok(EditListBox { - edits: edits, - }) -} - -/// Parse a mdhd box. -fn read_mdhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaHeaderBox> { - let (version, _) = try!(read_fullbox_extra(src)); - let (timescale, duration) = match version { - 1 => { - // Skip 64-bit creation and modification times. - try!(skip(src, 16)); - - // 64 bit duration. - (try!(be_u32(src)), try!(be_u64(src))) - } - 0 => { - // Skip 32-bit creation and modification times. - try!(skip(src, 8)); - - // 32 bit duration. - let timescale = try!(be_u32(src)); - let duration = { - // Since we convert the 32-bit duration to 64-bit by - // upcasting, we need to preserve the special all-1s - // ("unknown") case by hand. - let d = try!(be_u32(src)); - if d == std::u32::MAX { - std::u64::MAX - } else { - d as u64 - } - }; - (timescale, duration) - } - _ => return Err(Error::InvalidData("unhandled mdhd version")), - }; - - // Skip uninteresting fields. - try!(skip(src, 4)); - - Ok(MediaHeaderBox { - timescale: timescale, - duration: duration, - }) -} - -/// Parse a stco box. -fn read_stco<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> { - let (_, _) = try!(read_fullbox_extra(src)); - let offset_count = try!(be_u32(src)); - let mut offsets = Vec::new(); - for _ in 0..offset_count { - offsets.push(try!(be_u32(src)) as u64); - } - - // Padding could be added in some contents. - try!(skip_box_remain(src)); - - Ok(ChunkOffsetBox { - offsets: offsets, - }) -} - -/// Parse a co64 box. -fn read_co64<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> { - let (_, _) = try!(read_fullbox_extra(src)); - let offset_count = try!(be_u32(src)); - let mut offsets = Vec::new(); - for _ in 0..offset_count { - offsets.push(try!(be_u64(src))); - } - - // Padding could be added in some contents. - try!(skip_box_remain(src)); - - Ok(ChunkOffsetBox { - offsets: offsets, - }) -} - -/// Parse a stss box. -fn read_stss<T: Read>(src: &mut BMFFBox<T>) -> Result<SyncSampleBox> { - let (_, _) = try!(read_fullbox_extra(src)); - let sample_count = try!(be_u32(src)); - let mut samples = Vec::new(); - for _ in 0..sample_count { - samples.push(try!(be_u32(src))); - } - - // Padding could be added in some contents. - try!(skip_box_remain(src)); - - Ok(SyncSampleBox { - samples: samples, - }) -} - -/// Parse a stsc box. -fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> { - let (_, _) = try!(read_fullbox_extra(src)); - let sample_count = try!(be_u32(src)); - let mut samples = Vec::new(); - for _ in 0..sample_count { - let first_chunk = try!(be_u32(src)); - let samples_per_chunk = try!(be_u32(src)); - let sample_description_index = try!(be_u32(src)); - samples.push(SampleToChunk { - first_chunk: first_chunk, - samples_per_chunk: samples_per_chunk, - sample_description_index: sample_description_index, - }); - } - - // Padding could be added in some contents. - try!(skip_box_remain(src)); - - Ok(SampleToChunkBox { - samples: samples, - }) -} - -/// Parse a stsz box. -fn read_stsz<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleSizeBox> { - let (_, _) = try!(read_fullbox_extra(src)); - let sample_size = try!(be_u32(src)); - let sample_count = try!(be_u32(src)); - let mut sample_sizes = Vec::new(); - if sample_size == 0 { - for _ in 0..sample_count { - sample_sizes.push(try!(be_u32(src))); - } - } - - // Padding could be added in some contents. - try!(skip_box_remain(src)); - - Ok(SampleSizeBox { - sample_size: sample_size, - sample_sizes: sample_sizes, - }) -} - -/// Parse a stts box. -fn read_stts<T: Read>(src: &mut BMFFBox<T>) -> Result<TimeToSampleBox> { - let (_, _) = try!(read_fullbox_extra(src)); - let sample_count = try!(be_u32(src)); - let mut samples = Vec::new(); - for _ in 0..sample_count { - let sample_count = try!(be_u32(src)); - let sample_delta = try!(be_u32(src)); - samples.push(Sample { - sample_count: sample_count, - sample_delta: sample_delta, - }); - } - - // Padding could be added in some contents. - try!(skip_box_remain(src)); - - Ok(TimeToSampleBox { - samples: samples, - }) -} - -/// Parse a VPx Config Box. -fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> { - let (version, _) = try!(read_fullbox_extra(src)); - if version != 0 { - return Err(Error::Unsupported("unknown vpcC version")); - } - - let profile = try!(src.read_u8()); - let level = try!(src.read_u8()); - let (bit_depth, color_space) = { - let byte = try!(src.read_u8()); - ((byte >> 4) & 0x0f, byte & 0x0f) - }; - let (chroma_subsampling, transfer_function, video_full_range) = { - let byte = try!(src.read_u8()); - ((byte >> 4) & 0x0f, (byte >> 1) & 0x07, (byte & 1) == 1) - }; - - let codec_init_size = try!(be_u16(src)); - let codec_init = try!(read_buf(src, codec_init_size as usize)); - - // TODO(rillian): validate field value ranges. - Ok(VPxConfigBox { - profile: profile, - level: level, - bit_depth: bit_depth, - color_space: color_space, - chroma_subsampling: chroma_subsampling, - transfer_function: transfer_function, - video_full_range: video_full_range, - codec_init: codec_init, - }) -} - -fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock> { - let temp = try!(src.read_u8()); - let block_type = temp & 0x7f; - let length = try!(be_u24(src)); - if length as usize > src.bytes_left() { - return Err(Error::InvalidData( - "FLACMetadataBlock larger than parent box")); - } - let data = try!(read_buf(src, length as usize)); - Ok(FLACMetadataBlock { - block_type: block_type, - data: data, - }) -} - -fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> { - // Tags for elementary stream description - const ESDESCR_TAG: u8 = 0x03; - const DECODER_CONFIG_TAG: u8 = 0x04; - const DECODER_SPECIFIC_TAG: u8 = 0x05; - - let frequency_table = - vec![(0x1, 96000), (0x1, 88200), (0x2, 64000), (0x3, 48000), - (0x4, 44100), (0x5, 32000), (0x6, 24000), (0x7, 22050), - (0x8, 16000), (0x9, 12000), (0xa, 11025), (0xb, 8000), - (0xc, 7350)]; - - let (_, _) = try!(read_fullbox_extra(src)); - - let esds_size = src.head.size - src.head.offset - 4; - if esds_size > BUF_SIZE_LIMIT { - return Err(Error::InvalidData("esds box exceeds BUF_SIZE_LIMIT")); - } - let esds_array = try!(read_buf(src, esds_size as usize)); - - // Parsing DecoderConfig descriptor to get the object_profile_indicator - // for correct codec type, audio sample rate and channel counts. - let (object_profile_indicator, sample_frequency, channels) = { - let mut object_profile: u8 = 0; - let mut sample_frequency = None; - let mut channels = None; - - // clone a esds cursor for parsing. - let esds = &mut Cursor::new(&esds_array); - let next_tag = try!(esds.read_u8()); - - if next_tag != ESDESCR_TAG { - return Err(Error::Unsupported("fail to parse ES descriptor")); - } - - let esds_extend = try!(esds.read_u8()); - // extension tag start from 0x80. - let esds_end = if esds_extend >= 0x80 { - // skip remaining extension. - try!(skip(esds, 2)); - esds.position() + try!(esds.read_u8()) as u64 - } else { - esds.position() + esds_extend as u64 - }; - try!(skip(esds, 2)); - - let esds_flags = try!(esds.read_u8()); - - // Stream dependency flag, first bit from left most. - if esds_flags & 0x80 > 0 { - // Skip uninteresting fields. - try!(skip(esds, 2)); - } - - // Url flag, second bit from left most. - if esds_flags & 0x40 > 0 { - // Skip uninteresting fields. - let skip_es_len: usize = try!(esds.read_u8()) as usize + 2; - try!(skip(esds, skip_es_len)); - } - - // find DecoderConfig descriptor (tag = DECODER_CONFIG_TAG) - if esds_end > esds.position() { - let next_tag = try!(esds.read_u8()); - if next_tag == DECODER_CONFIG_TAG { - let dcds_extend = try!(esds.read_u8()); - // extension tag start from 0x80. - if dcds_extend >= 0x80 { - // skip remains extension and length. - try!(skip(esds, 3)); - } - - object_profile = try!(esds.read_u8()); - - // Skip uninteresting fields. - try!(skip(esds, 12)); - } - } - - - // find DecoderSpecific descriptor (tag = DECODER_SPECIFIC_TAG) - if esds_end > esds.position() { - let next_tag = try!(esds.read_u8()); - if next_tag == DECODER_SPECIFIC_TAG { - let dsds_extend = try!(esds.read_u8()); - // extension tag start from 0x80. - if dsds_extend >= 0x80 { - // skip remains extension and length. - try!(skip(esds, 3)); - } - - let audio_specific_config = try!(be_u16(esds)); - - let sample_index = (audio_specific_config & 0x07FF) >> 7; - - let channel_counts = (audio_specific_config & 0x007F) >> 3; - - sample_frequency = - frequency_table.iter().find(|item| item.0 == sample_index).map(|x| x.1); - - channels = Some(channel_counts); - } - } - - (object_profile, sample_frequency, channels) - }; - - let codec = match object_profile_indicator { - 0x40 | 0x41 => CodecType::AAC, - 0x6B => CodecType::MP3, - _ => CodecType::Unknown, - }; - - if codec == CodecType::Unknown { - return Err(Error::Unsupported("unknown audio codec")); - } - - Ok(ES_Descriptor { - audio_codec: codec, - audio_sample_rate: sample_frequency, - audio_channel_count: channels, - codec_specific_config: esds_array, - }) -} - -/// Parse `FLACSpecificBox`. -fn read_dfla<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACSpecificBox> { - let (version, flags) = try!(read_fullbox_extra(src)); - if version != 0 { - return Err(Error::Unsupported("unknown dfLa (FLAC) version")); - } - if flags != 0 { - return Err(Error::InvalidData("no-zero dfLa (FLAC) flags")); - } - let mut blocks = Vec::new(); - while src.bytes_left() > 0 { - let block = try!(read_flac_metadata(src)); - blocks.push(block); - } - // The box must have at least one meta block, and the first block - // must be the METADATA_BLOCK_STREAMINFO - if blocks.is_empty() { - return Err(Error::InvalidData("FLACSpecificBox missing metadata")); - } else if blocks[0].block_type != 0 { - println!("flac metadata block:\n {:?}", blocks[0]); - return Err(Error::InvalidData( - "FLACSpecificBox must have STREAMINFO metadata first")); - } else if blocks[0].data.len() != 34 { - return Err(Error::InvalidData( - "FLACSpecificBox STREAMINFO block is the wrong size")); - } - Ok(FLACSpecificBox { - version: version, - blocks: blocks, - }) -} - -/// Parse `OpusSpecificBox`. -fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> { - let version = try!(src.read_u8()); - if version != 0 { - return Err(Error::Unsupported("unknown dOps (Opus) version")); - } - - let output_channel_count = try!(src.read_u8()); - let pre_skip = try!(be_u16(src)); - let input_sample_rate = try!(be_u32(src)); - let output_gain = try!(be_i16(src)); - let channel_mapping_family = try!(src.read_u8()); - - let channel_mapping_table = if channel_mapping_family == 0 { - None - } else { - let stream_count = try!(src.read_u8()); - let coupled_count = try!(src.read_u8()); - let channel_mapping = try!(read_buf(src, output_channel_count as usize)); - - Some(ChannelMappingTable { - stream_count: stream_count, - coupled_count: coupled_count, - channel_mapping: channel_mapping, - }) - }; - - // TODO(kinetik): validate field value ranges. - Ok(OpusSpecificBox { - version: version, - output_channel_count: output_channel_count, - pre_skip: pre_skip, - input_sample_rate: input_sample_rate, - output_gain: output_gain, - channel_mapping_family: channel_mapping_family, - channel_mapping_table: channel_mapping_table, - }) -} - -/// Re-serialize the Opus codec-specific config data as an `OpusHead` packet. -/// -/// Some decoders expect the initialization data in the format used by the -/// Ogg and WebM encapsulations. To support this we prepend the `OpusHead` -/// tag and byte-swap the data from big- to little-endian relative to the -/// dOps box. -pub fn serialize_opus_header<W: byteorder::WriteBytesExt + std::io::Write>(opus: &OpusSpecificBox, dst: &mut W) -> Result<()> { - match dst.write(b"OpusHead") { - Err(e) => return Err(Error::from(e)), - Ok(bytes) => { - if bytes != 8 { - return Err(Error::InvalidData("Couldn't write OpusHead tag.")); - } - } - } - // In mp4 encapsulation, the version field is 0, but in ogg - // it is 1. While decoders generally accept zero as well, write - // out the version of the header we're supporting rather than - // whatever we parsed out of mp4. - try!(dst.write_u8(1)); - try!(dst.write_u8(opus.output_channel_count)); - try!(dst.write_u16::<byteorder::LittleEndian>(opus.pre_skip)); - try!(dst.write_u32::<byteorder::LittleEndian>(opus.input_sample_rate)); - try!(dst.write_i16::<byteorder::LittleEndian>(opus.output_gain)); - try!(dst.write_u8(opus.channel_mapping_family)); - match opus.channel_mapping_table { - None => {} - Some(ref table) => { - try!(dst.write_u8(table.stream_count)); - try!(dst.write_u8(table.coupled_count)); - match dst.write(&table.channel_mapping) { - Err(e) => return Err(Error::from(e)), - Ok(bytes) => { - if bytes != table.channel_mapping.len() { - return Err(Error::InvalidData("Couldn't write channel mapping table data.")); - } - } - } - } - }; - Ok(()) -} - -/// Parse a hdlr box. -fn read_hdlr<T: Read>(src: &mut BMFFBox<T>) -> Result<HandlerBox> { - let (_, _) = try!(read_fullbox_extra(src)); - - // Skip uninteresting fields. - try!(skip(src, 4)); - - let handler_type = try!(be_u32(src)); - - // Skip uninteresting fields. - try!(skip(src, 12)); - - let bytes_left = src.bytes_left(); - let _name = try!(read_null_terminated_string(src, bytes_left)); - - Ok(HandlerBox { - handler_type: handler_type, - }) -} - -/// Parse an video description inside an stsd box. -fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> { - let name = src.get_header().name; - track.codec_type = match name { - BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => CodecType::H264, - BoxType::VP8SampleEntry => CodecType::VP8, - BoxType::VP9SampleEntry => CodecType::VP9, - BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo, - _ => CodecType::Unknown, - }; - - // Skip uninteresting fields. - try!(skip(src, 6)); - - let data_reference_index = try!(be_u16(src)); - - // Skip uninteresting fields. - try!(skip(src, 16)); - - let width = try!(be_u16(src)); - let height = try!(be_u16(src)); - - // Skip uninteresting fields. - try!(skip(src, 14)); - - let _compressorname = try!(read_fixed_length_pascal_string(src, 32)); - - // Skip uninteresting fields. - try!(skip(src, 4)); - - // Skip clap/pasp/etc. for now. - let mut codec_specific = None; - let mut iter = src.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::AVCConfigurationBox => { - if (name != BoxType::AVCSampleEntry && - name != BoxType::AVC3SampleEntry && - name != BoxType::ProtectedVisualSampleEntry) || - codec_specific.is_some() { - return Err(Error::InvalidData("malformed video sample entry")); - } - let avcc_size = b.head.size - b.head.offset; - if avcc_size > BUF_SIZE_LIMIT { - return Err(Error::InvalidData("avcC box exceeds BUF_SIZE_LIMIT")); - } - let avcc = try!(read_buf(&mut b.content, avcc_size as usize)); - // TODO(kinetik): Parse avcC box? For now we just stash the data. - codec_specific = Some(VideoCodecSpecific::AVCConfig(avcc)); - } - BoxType::VPCodecConfigurationBox => { // vpcC - if (name != BoxType::VP8SampleEntry && - name != BoxType::VP9SampleEntry) || - codec_specific.is_some() { - return Err(Error::InvalidData("malformed video sample entry")); - } - let vpcc = try!(read_vpcc(&mut b)); - codec_specific = Some(VideoCodecSpecific::VPxConfig(vpcc)); - } - _ => try!(skip_box_content(&mut b)), - } - check_parser_state!(b.content); - } - - codec_specific - .map(|codec_specific| SampleEntry::Video(VideoSampleEntry { - data_reference_index: data_reference_index, - width: width, - height: height, - codec_specific: codec_specific, - })) - .ok_or_else(|| Error::InvalidData("malformed video sample entry")) -} - -fn read_qt_wave_atom<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> { - let mut codec_specific = None; - let mut iter = src.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::ESDBox => { - let esds = try!(read_esds(&mut b)); - codec_specific = Some(esds); - }, - _ => try!(skip_box_content(&mut b)), - } - } - - codec_specific.ok_or_else(|| Error::InvalidData("malformed audio sample entry")) -} - -/// Parse an audio description inside an stsd box. -fn read_audio_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> { - let name = src.get_header().name; - track.codec_type = match name { - // TODO(kinetik): stagefright inspects ESDS to detect MP3 (audio/mpeg). - BoxType::MP4AudioSampleEntry => CodecType::AAC, - BoxType::FLACSampleEntry => CodecType::FLAC, - BoxType::OpusSampleEntry => CodecType::Opus, - BoxType::ProtectedAudioSampleEntry => CodecType::EncryptedAudio, - _ => CodecType::Unknown, - }; - - // Skip uninteresting fields. - try!(skip(src, 6)); - - let data_reference_index = try!(be_u16(src)); - - // XXX(kinetik): This is "reserved" in BMFF, but some old QT MOV variant - // uses it, need to work out if we have to support it. Without checking - // here and reading extra fields after samplerate (or bailing with an - // error), the parser loses sync completely. - let version = try!(be_u16(src)); - - // Skip uninteresting fields. - try!(skip(src, 6)); - - let channelcount = try!(be_u16(src)); - let samplesize = try!(be_u16(src)); - - // Skip uninteresting fields. - try!(skip(src, 4)); - - let samplerate = try!(be_u32(src)); - - match version { - 0 => (), - 1 => { - // Quicktime sound sample description version 1. - // Skip uninteresting fields. - try!(skip(src, 16)); - }, - _ => return Err(Error::Unsupported("unsupported non-isom audio sample entry")), - } - - // Skip chan/etc. for now. - let mut codec_specific = None; - let mut iter = src.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - match b.head.name { - BoxType::ESDBox => { - if (name != BoxType::MP4AudioSampleEntry && - name != BoxType::ProtectedAudioSampleEntry) || - codec_specific.is_some() { - return Err(Error::InvalidData("malformed audio sample entry")); - } - - let esds = try!(read_esds(&mut b)); - track.codec_type = esds.audio_codec; - codec_specific = Some(AudioCodecSpecific::ES_Descriptor(esds)); - } - BoxType::FLACSpecificBox => { - if name != BoxType::FLACSampleEntry || - codec_specific.is_some() { - return Err(Error::InvalidData("malformed audio sample entry")); - } - let dfla = try!(read_dfla(&mut b)); - track.codec_type = CodecType::FLAC; - codec_specific = Some(AudioCodecSpecific::FLACSpecificBox(dfla)); - } - BoxType::OpusSpecificBox => { - if name != BoxType::OpusSampleEntry || - codec_specific.is_some() { - return Err(Error::InvalidData("malformed audio sample entry")); - } - let dops = try!(read_dops(&mut b)); - track.codec_type = CodecType::Opus; - codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops)); - } - BoxType::QTWaveAtom => { - let qt_esds = try!(read_qt_wave_atom(&mut b)); - track.codec_type = qt_esds.audio_codec; - codec_specific = Some(AudioCodecSpecific::ES_Descriptor(qt_esds)); - } - _ => try!(skip_box_content(&mut b)), - } - check_parser_state!(b.content); - } - - codec_specific - .map(|codec_specific| SampleEntry::Audio(AudioSampleEntry { - data_reference_index: data_reference_index, - channelcount: channelcount, - samplesize: samplesize, - samplerate: samplerate, - codec_specific: codec_specific, - })) - .ok_or_else(|| Error::InvalidData("malformed audio sample entry")) -} - -/// Parse a stsd box. -fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleDescriptionBox> { - let (_, _) = try!(read_fullbox_extra(src)); - - let description_count = try!(be_u32(src)); - let mut descriptions = Vec::new(); - - { - // TODO(kinetik): check if/when more than one desc per track? do we need to support? - let mut iter = src.box_iter(); - while let Some(mut b) = try!(iter.next_box()) { - let description = match track.track_type { - TrackType::Video => read_video_sample_entry(&mut b, track), - TrackType::Audio => read_audio_sample_entry(&mut b, track), - TrackType::Unknown => Err(Error::Unsupported("unknown track type")), - }; - let description = match description { - Ok(desc) => desc, - Err(Error::Unsupported(_)) => { - // read_{audio,video}_desc may have returned Unsupported - // after partially reading the box content, so we can't - // simply use skip_box_content here. - let to_skip = b.bytes_left(); - try!(skip(&mut b, to_skip)); - SampleEntry::Unknown - } - Err(e) => return Err(e), - }; - if track.data.is_none() { - track.data = Some(description.clone()); - } else { - log!("** don't know how to handle multiple descriptions **"); - } - descriptions.push(description); - check_parser_state!(b.content); - if descriptions.len() == description_count as usize { - break; - } - } - } - - // Padding could be added in some contents. - try!(skip_box_remain(src)); - - Ok(SampleDescriptionBox { - descriptions: descriptions, - }) -} - -/// Skip a number of bytes that we don't care to parse. -fn skip<T: Read>(src: &mut T, mut bytes: usize) -> Result<()> { - const BUF_SIZE: usize = 64 * 1024; - let mut buf = vec![0; BUF_SIZE]; - while bytes > 0 { - let buf_size = cmp::min(bytes, BUF_SIZE); - let len = try!(src.take(buf_size as u64).read(&mut buf)); - if len == 0 { - return Err(Error::UnexpectedEOF); - } - bytes -= len; - } - Ok(()) -} - -/// Read size bytes into a Vector or return error. -fn read_buf<T: ReadBytesExt>(src: &mut T, size: usize) -> Result<Vec<u8>> { - let mut buf = vec![0; size]; - let r = try!(src.read(&mut buf)); - if r != size { - return Err(Error::InvalidData("failed buffer read")); - } - Ok(buf) -} - -// TODO(kinetik): Find a copy of ISO/IEC 14496-1 to confirm various string encodings. -// XXX(kinetik): definition of "null-terminated" string is fuzzy, we have: -// - zero or more byte strings, with a single null terminating the string. -// - zero byte strings with no null terminator (i.e. zero space in the box for the string) -// - length-prefixed strings with no null terminator (e.g. bear_rotate_0.mp4) -fn read_null_terminated_string<T: ReadBytesExt>(src: &mut T, mut size: usize) -> Result<String> { - let mut buf = Vec::new(); - while size > 0 { - let c = try!(src.read_u8()); - if c == 0 { - break; - } - buf.push(c); - size -= 1; - } - String::from_utf8(buf).map_err(From::from) -} - -#[allow(dead_code)] -fn read_pascal_string<T: ReadBytesExt>(src: &mut T) -> Result<String> { - let len = try!(src.read_u8()); - let buf = try!(read_buf(src, len as usize)); - String::from_utf8(buf).map_err(From::from) -} - -// Weird string encoding with a length prefix and a fixed sized buffer which -// contains padding if the string doesn't fill the buffer. -fn read_fixed_length_pascal_string<T: Read>(src: &mut T, size: usize) -> Result<String> { - assert!(size > 0); - let len = cmp::min(try!(src.read_u8()) as usize, size - 1); - let buf = try!(read_buf(src, len)); - try!(skip(src, size - 1 - buf.len())); - String::from_utf8(buf).map_err(From::from) -} - -fn be_i16<T: ReadBytesExt>(src: &mut T) -> Result<i16> { - src.read_i16::<byteorder::BigEndian>().map_err(From::from) -} - -fn be_i32<T: ReadBytesExt>(src: &mut T) -> Result<i32> { - src.read_i32::<byteorder::BigEndian>().map_err(From::from) -} - -fn be_i64<T: ReadBytesExt>(src: &mut T) -> Result<i64> { - src.read_i64::<byteorder::BigEndian>().map_err(From::from) -} - -fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> { - src.read_u16::<byteorder::BigEndian>().map_err(From::from) -} - -fn be_u24<T: ReadBytesExt>(src: &mut T) -> Result<u32> { - src.read_uint::<byteorder::BigEndian>(3) - .map(|v| v as u32) - .map_err(From::from) -} - -fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> { - src.read_u32::<byteorder::BigEndian>().map_err(From::from) -} - -fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> { - src.read_u64::<byteorder::BigEndian>().map_err(From::from) -} diff --git a/media/libstagefright/binding/mp4parse/src/tests.rs b/media/libstagefright/binding/mp4parse/src/tests.rs deleted file mode 100644 index 7063b3790..000000000 --- a/media/libstagefright/binding/mp4parse/src/tests.rs +++ /dev/null @@ -1,860 +0,0 @@ -//! Module for parsing ISO Base Media Format aka video/mp4 streams. -//! Internal unit tests. - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use std::io::Cursor; -use super::read_mp4; -use super::MediaContext; -use super::Error; -extern crate test_assembler; -use self::test_assembler::*; - -use boxes::BoxType; - -enum BoxSize { - Short(u32), - Long(u64), - UncheckedShort(u32), - UncheckedLong(u64), - Auto, -} - -fn make_box<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>> - where F: Fn(Section) -> Section -{ - let mut section = Section::new(); - let box_size = Label::new(); - section = match size { - BoxSize::Short(size) | BoxSize::UncheckedShort(size) => section.B32(size), - BoxSize::Long(_) | BoxSize::UncheckedLong(_) => section.B32(1), - BoxSize::Auto => section.B32(&box_size), - }; - section = section.append_bytes(name); - section = match size { - // The spec allows the 32-bit size to be 0 to indicate unknown - // length streams. It's not clear if this is valid when using a - // 64-bit size, so prohibit it for now. - BoxSize::Long(size) => { - assert!(size > 0); - section.B64(size) - } - BoxSize::UncheckedLong(size) => section.B64(size), - _ => section, - }; - section = func(section); - match size { - BoxSize::Short(size) => { - if size > 0 { - assert_eq!(size as u64, section.size()) - } - } - BoxSize::Long(size) => assert_eq!(size, section.size()), - BoxSize::Auto => { - assert!(section.size() <= u32::max_value() as u64, - "Tried to use a long box with BoxSize::Auto"); - box_size.set_const(section.size()); - } - // Skip checking BoxSize::Unchecked* cases. - _ => (), - } - Cursor::new(section.get_contents().unwrap()) -} - -fn make_fullbox<F>(size: BoxSize, name: &[u8; 4], version: u8, func: F) -> Cursor<Vec<u8>> - where F: Fn(Section) -> Section -{ - make_box(size, name, |s| { - func(s.B8(version) - .B8(0) - .B8(0) - .B8(0)) - }) -} - -#[test] -fn read_box_header_short() { - let mut stream = make_box(BoxSize::Short(8), b"test", |s| s); - let header = super::read_box_header(&mut stream).unwrap(); - assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test" - assert_eq!(header.size, 8); -} - -#[test] -fn read_box_header_long() { - let mut stream = make_box(BoxSize::Long(16), b"test", |s| s); - let header = super::read_box_header(&mut stream).unwrap(); - assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test" - assert_eq!(header.size, 16); -} - -#[test] -fn read_box_header_short_unknown_size() { - let mut stream = make_box(BoxSize::Short(0), b"test", |s| s); - match super::read_box_header(&mut stream) { - Err(Error::Unsupported(s)) => assert_eq!(s, "unknown sized box"), - _ => panic!("unexpected result reading box with unknown size"), - }; -} - -#[test] -fn read_box_header_short_invalid_size() { - let mut stream = make_box(BoxSize::UncheckedShort(2), b"test", |s| s); - match super::read_box_header(&mut stream) { - Err(Error::InvalidData(s)) => assert_eq!(s, "malformed size"), - _ => panic!("unexpected result reading box with invalid size"), - }; -} - -#[test] -fn read_box_header_long_invalid_size() { - let mut stream = make_box(BoxSize::UncheckedLong(2), b"test", |s| s); - match super::read_box_header(&mut stream) { - Err(Error::InvalidData(s)) => assert_eq!(s, "malformed wide size"), - _ => panic!("unexpected result reading box with invalid size"), - }; -} - -#[test] -fn read_ftyp() { - let mut stream = make_box(BoxSize::Short(24), b"ftyp", |s| { - s.append_bytes(b"mp42") - .B32(0) // minor version - .append_bytes(b"isom") - .append_bytes(b"mp42") - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::FileTypeBox); - assert_eq!(stream.head.size, 24); - let parsed = super::read_ftyp(&mut stream).unwrap(); - assert_eq!(parsed.major_brand, 0x6d703432); // mp42 - assert_eq!(parsed.minor_version, 0); - assert_eq!(parsed.compatible_brands.len(), 2); - assert_eq!(parsed.compatible_brands[0], 0x69736f6d); // isom - assert_eq!(parsed.compatible_brands[1], 0x6d703432); // mp42 -} - -#[test] -fn read_truncated_ftyp() { - // We declare a 24 byte box, but only write 20 bytes. - let mut stream = make_box(BoxSize::UncheckedShort(24), b"ftyp", |s| { - s.append_bytes(b"mp42") - .B32(0) // minor version - .append_bytes(b"isom") - }); - let mut context = MediaContext::new(); - match read_mp4(&mut stream, &mut context) { - Err(Error::UnexpectedEOF) => (), - Ok(_) => assert!(false, "expected an error result"), - _ => assert!(false, "expected a different error result"), - } -} - -#[test] -fn read_ftyp_case() { - // Brands in BMFF are represented as a u32, so it would seem clear that - // 0x6d703432 ("mp42") is not equal to 0x4d503432 ("MP42"), but some - // demuxers treat these as case-insensitive strings, e.g. street.mp4's - // major brand is "MP42". I haven't seen case-insensitive - // compatible_brands (which we also test here), but it doesn't seem - // unlikely given the major_brand behaviour. - let mut stream = make_box(BoxSize::Auto, b"ftyp", |s| { - s.append_bytes(b"MP42") - .B32(0) // minor version - .append_bytes(b"ISOM") - .append_bytes(b"MP42") - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::FileTypeBox); - assert_eq!(stream.head.size, 24); - let parsed = super::read_ftyp(&mut stream).unwrap(); - assert_eq!(parsed.major_brand, 0x4d503432); - assert_eq!(parsed.minor_version, 0); - assert_eq!(parsed.compatible_brands.len(), 2); - assert_eq!(parsed.compatible_brands[0], 0x49534f4d); // ISOM - assert_eq!(parsed.compatible_brands[1], 0x4d503432); // MP42 -} - -#[test] -fn read_elst_v0() { - let mut stream = make_fullbox(BoxSize::Short(28), b"elst", 0, |s| { - s.B32(1) // list count - // first entry - .B32(1234) // duration - .B32(5678) // time - .B16(12) // rate integer - .B16(34) // rate fraction - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::EditListBox); - assert_eq!(stream.head.size, 28); - let parsed = super::read_elst(&mut stream).unwrap(); - assert_eq!(parsed.edits.len(), 1); - assert_eq!(parsed.edits[0].segment_duration, 1234); - assert_eq!(parsed.edits[0].media_time, 5678); - assert_eq!(parsed.edits[0].media_rate_integer, 12); - assert_eq!(parsed.edits[0].media_rate_fraction, 34); -} - -#[test] -fn read_elst_v1() { - let mut stream = make_fullbox(BoxSize::Short(56), b"elst", 1, |s| { - s.B32(2) // list count - // first entry - .B64(1234) // duration - .B64(5678) // time - .B16(12) // rate integer - .B16(34) // rate fraction - // second entry - .B64(1234) // duration - .B64(5678) // time - .B16(12) // rate integer - .B16(34) // rate fraction - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::EditListBox); - assert_eq!(stream.head.size, 56); - let parsed = super::read_elst(&mut stream).unwrap(); - assert_eq!(parsed.edits.len(), 2); - assert_eq!(parsed.edits[1].segment_duration, 1234); - assert_eq!(parsed.edits[1].media_time, 5678); - assert_eq!(parsed.edits[1].media_rate_integer, 12); - assert_eq!(parsed.edits[1].media_rate_fraction, 34); -} - -#[test] -fn read_mdhd_v0() { - let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| { - s.B32(0) - .B32(0) - .B32(1234) // timescale - .B32(5678) // duration - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MediaHeaderBox); - assert_eq!(stream.head.size, 32); - let parsed = super::read_mdhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, 5678); -} - -#[test] -fn read_mdhd_v1() { - let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| { - s.B64(0) - .B64(0) - .B32(1234) // timescale - .B64(5678) // duration - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MediaHeaderBox); - assert_eq!(stream.head.size, 44); - let parsed = super::read_mdhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, 5678); -} - -#[test] -fn read_mdhd_unknown_duration() { - let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| { - s.B32(0) - .B32(0) - .B32(1234) // timescale - .B32(::std::u32::MAX) // duration - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MediaHeaderBox); - assert_eq!(stream.head.size, 32); - let parsed = super::read_mdhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, ::std::u64::MAX); -} - -#[test] -fn read_mdhd_invalid_timescale() { - let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| { - s.B64(0) - .B64(0) - .B32(0) // timescale - .B64(5678) // duration - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MediaHeaderBox); - assert_eq!(stream.head.size, 44); - let r = super::parse_mdhd(&mut stream, &mut super::Track::new(0)); - assert_eq!(r.is_err(), true); -} - -#[test] -fn read_mvhd_v0() { - let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| { - s.B32(0) - .B32(0) - .B32(1234) - .B32(5678) - .append_repeated(0, 80) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MovieHeaderBox); - assert_eq!(stream.head.size, 108); - let parsed = super::read_mvhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, 5678); -} - -#[test] -fn read_mvhd_v1() { - let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| { - s.B64(0) - .B64(0) - .B32(1234) - .B64(5678) - .append_repeated(0, 80) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MovieHeaderBox); - assert_eq!(stream.head.size, 120); - let parsed = super::read_mvhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, 5678); -} - -#[test] -fn read_mvhd_invalid_timescale() { - let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| { - s.B64(0) - .B64(0) - .B32(0) - .B64(5678) - .append_repeated(0, 80) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MovieHeaderBox); - assert_eq!(stream.head.size, 120); - let r = super::parse_mvhd(&mut stream); - assert_eq!(r.is_err(), true); -} - -#[test] -fn read_mvhd_unknown_duration() { - let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| { - s.B32(0) - .B32(0) - .B32(1234) - .B32(::std::u32::MAX) - .append_repeated(0, 80) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MovieHeaderBox); - assert_eq!(stream.head.size, 108); - let parsed = super::read_mvhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, ::std::u64::MAX); -} - -#[test] -fn read_vpcc() { - let data_length = 12u16; - let mut stream = make_fullbox(BoxSize::Auto, b"vpcC", 0, |s| { - s.B8(2) - .B8(0) - .B8(0x82) - .B8(0) - .B16(data_length) - .append_repeated(42, data_length as usize) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox); - let r = super::read_vpcc(&mut stream); - assert!(r.is_ok()); -} - -#[test] -fn read_hdlr() { - let mut stream = make_fullbox(BoxSize::Short(45), b"hdlr", 0, |s| { - s.B32(0) - .append_bytes(b"vide") - .B32(0) - .B32(0) - .B32(0) - .append_bytes(b"VideoHandler") - .B8(0) // null-terminate string - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::HandlerBox); - assert_eq!(stream.head.size, 45); - let parsed = super::read_hdlr(&mut stream).unwrap(); - assert_eq!(parsed.handler_type, 0x76696465); // vide -} - -#[test] -fn read_hdlr_short_name() { - let mut stream = make_fullbox(BoxSize::Short(33), b"hdlr", 0, |s| { - s.B32(0) - .append_bytes(b"vide") - .B32(0) - .B32(0) - .B32(0) - .B8(0) // null-terminate string - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::HandlerBox); - assert_eq!(stream.head.size, 33); - let parsed = super::read_hdlr(&mut stream).unwrap(); - assert_eq!(parsed.handler_type, 0x76696465); // vide -} - -#[test] -fn read_hdlr_zero_length_name() { - let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| { - s.B32(0) - .append_bytes(b"vide") - .B32(0) - .B32(0) - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::HandlerBox); - assert_eq!(stream.head.size, 32); - let parsed = super::read_hdlr(&mut stream).unwrap(); - assert_eq!(parsed.handler_type, 0x76696465); // vide -} - -fn flac_streaminfo() -> Vec<u8> { - vec![ - 0x10, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x11, 0x00, - 0x38, 0x32, 0x0a, 0xc4, 0x42, 0xf0, 0x00, 0xc9, - 0xdf, 0xae, 0xb5, 0x66, 0xfc, 0x02, 0x15, 0xa3, - 0xb1, 0x54, 0x61, 0x47, 0x0f, 0xfb, 0x05, 0x00, - 0x33, 0xad, - ] -} - -#[test] -fn read_flac() { - let mut stream = make_box(BoxSize::Auto, b"fLaC", |s| { - s.append_repeated(0, 6) // reserved - .B16(1) // data reference index - .B32(0) // reserved - .B32(0) // reserved - .B16(2) // channel count - .B16(16) // bits per sample - .B16(0) // pre_defined - .B16(0) // reserved - .B32(44100 << 16) // Sample rate - .append_bytes(&make_dfla(FlacBlockType::StreamInfo, true, - &flac_streaminfo(), FlacBlockLength::Correct) - .into_inner()) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - let mut track = super::Track::new(0); - let r = super::read_audio_sample_entry(&mut stream, &mut track); - r.unwrap(); -} - -#[derive(Clone, Copy)] -enum FlacBlockType { - StreamInfo = 0, - _Padding = 1, - _Application = 2, - _Seektable = 3, - _Comment = 4, - _Cuesheet = 5, - _Picture = 6, - _Reserved, - _Invalid = 127, -} - -enum FlacBlockLength { - Correct, - Incorrect(usize), -} - -fn make_dfla(block_type: FlacBlockType, last: bool, data: &Vec<u8>, - data_length: FlacBlockLength) -> Cursor<Vec<u8>> { - assert!(data.len() < 1<<24); - make_fullbox(BoxSize::Auto, b"dfLa", 0, |s| { - let flag = match last { - true => 1, - false => 0, - }; - let size = match data_length { - FlacBlockLength::Correct => (data.len() as u32) & 0xffffff, - FlacBlockLength::Incorrect(size) => { - assert!(size < 1<<24); - (size as u32) & 0xffffff - } - }; - let block_type = (block_type as u32) & 0x7f; - s.B32(flag << 31 | block_type << 24 | size) - .append_bytes(data) - }) -} - -#[test] -fn read_dfla() { - let mut stream = make_dfla(FlacBlockType::StreamInfo, true, - &flac_streaminfo(), FlacBlockLength::Correct); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::FLACSpecificBox); - let dfla = super::read_dfla(&mut stream).unwrap(); - assert_eq!(dfla.version, 0); -} - -#[test] -fn long_flac_metadata() { - let streaminfo = flac_streaminfo(); - let mut stream = make_dfla(FlacBlockType::StreamInfo, true, - &streaminfo, - FlacBlockLength::Incorrect(streaminfo.len() + 4)); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::FLACSpecificBox); - let r = super::read_dfla(&mut stream); - assert!(r.is_err()); -} - -#[test] -fn read_opus() { - let mut stream = make_box(BoxSize::Auto, b"Opus", |s| { - s.append_repeated(0, 6) - .B16(1) // data reference index - .B32(0) - .B32(0) - .B16(2) // channel count - .B16(16) // bits per sample - .B16(0) - .B16(0) - .B32(48000 << 16) // Sample rate is always 48 kHz for Opus. - .append_bytes(&make_dops().into_inner()) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - let mut track = super::Track::new(0); - let r = super::read_audio_sample_entry(&mut stream, &mut track); - assert!(r.is_ok()); -} - -fn make_dops() -> Cursor<Vec<u8>> { - make_box(BoxSize::Auto, b"dOps", |s| { - s.B8(0) // version - .B8(2) // channel count - .B16(348) // pre-skip - .B32(44100) // original sample rate - .B16(0) // gain - .B8(0) // channel mapping - }) -} - -#[test] -fn read_dops() { - let mut stream = make_dops(); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::OpusSpecificBox); - let r = super::read_dops(&mut stream); - assert!(r.is_ok()); -} - -#[test] -fn serialize_opus_header() { - let opus = super::OpusSpecificBox { - version: 0, - output_channel_count: 1, - pre_skip: 342, - input_sample_rate: 24000, - output_gain: 0, - channel_mapping_family: 0, - channel_mapping_table: None, - }; - let mut v = Vec::<u8>::new(); - super::serialize_opus_header(&opus, &mut v).unwrap(); - assert!(v.len() == 19); - assert!(v == vec![ - 0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64, - 0x01, 0x01, 0x56, 0x01, - 0xc0, 0x5d, 0x00, 0x00, - 0x00, 0x00, 0x00, - ]); - let opus = super::OpusSpecificBox { - version: 0, - output_channel_count: 6, - pre_skip: 152, - input_sample_rate: 48000, - output_gain: 0, - channel_mapping_family: 1, - channel_mapping_table: Some(super::ChannelMappingTable { - stream_count: 4, - coupled_count: 2, - channel_mapping: vec![0, 4, 1, 2, 3, 5], - }), - }; - let mut v = Vec::<u8>::new(); - super::serialize_opus_header(&opus, &mut v).unwrap(); - assert!(v.len() == 27); - assert!(v == vec![ - 0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64, - 0x01, 0x06, 0x98, 0x00, - 0x80, 0xbb, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x04, 0x02, - 0x00, 0x04, 0x01, 0x02, 0x03, 0x05, - ]); -} - -#[test] -fn avcc_limit() { - let mut stream = make_box(BoxSize::Auto, b"avc1", |s| { - s.append_repeated(0, 6) - .B16(1) - .append_repeated(0, 16) - .B16(320) - .B16(240) - .append_repeated(0, 14) - .append_repeated(0, 32) - .append_repeated(0, 4) - .B32(0xffffffff) - .append_bytes(b"avcC") - .append_repeated(0, 100) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - let mut track = super::Track::new(0); - match super::read_video_sample_entry(&mut stream, &mut track) { - Err(Error::InvalidData(s)) => assert_eq!(s, "avcC box exceeds BUF_SIZE_LIMIT"), - Ok(_) => assert!(false, "expected an error result"), - _ => assert!(false, "expected a different error result"), - } -} - -#[test] -fn esds_limit() { - let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { - s.append_repeated(0, 6) - .B16(1) - .B32(0) - .B32(0) - .B16(2) - .B16(16) - .B16(0) - .B16(0) - .B32(48000 << 16) - .B32(0xffffffff) - .append_bytes(b"esds") - .append_repeated(0, 100) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - let mut track = super::Track::new(0); - match super::read_audio_sample_entry(&mut stream, &mut track) { - Err(Error::InvalidData(s)) => assert_eq!(s, "esds box exceeds BUF_SIZE_LIMIT"), - Ok(_) => assert!(false, "expected an error result"), - _ => assert!(false, "expected a different error result"), - } -} - -#[test] -fn esds_limit_2() { - let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { - s.append_repeated(0, 6) - .B16(1) - .B32(0) - .B32(0) - .B16(2) - .B16(16) - .B16(0) - .B16(0) - .B32(48000 << 16) - .B32(8) - .append_bytes(b"esds") - .append_repeated(0, 4) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - let mut track = super::Track::new(0); - match super::read_audio_sample_entry(&mut stream, &mut track) { - Err(Error::UnexpectedEOF) => (), - Ok(_) => assert!(false, "expected an error result"), - _ => assert!(false, "expected a different error result"), - } -} - -#[test] -fn read_elst_zero_entries() { - let mut stream = make_fullbox(BoxSize::Auto, b"elst", 0, |s| { - s.B32(0) - .B16(12) - .B16(34) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - match super::read_elst(&mut stream) { - Err(Error::InvalidData(s)) => assert_eq!(s, "invalid edit count"), - Ok(_) => assert!(false, "expected an error result"), - _ => assert!(false, "expected a different error result"), - } -} - -fn make_elst() -> Cursor<Vec<u8>> { - make_fullbox(BoxSize::Auto, b"elst", 1, |s| { - s.B32(1) - // first entry - .B64(1234) // duration - .B64(0xffffffffffffffff) // time - .B16(12) // rate integer - .B16(34) // rate fraction - }) -} - -#[test] -fn read_edts_bogus() { - // First edit list entry has a media_time of -1, so we expect a second - // edit list entry to be present to provide a valid media_time. - let mut stream = make_box(BoxSize::Auto, b"edts", |s| { - s.append_bytes(&make_elst().into_inner()) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - let mut track = super::Track::new(0); - match super::read_edts(&mut stream, &mut track) { - Err(Error::InvalidData(s)) => assert_eq!(s, "expected additional edit"), - Ok(_) => assert!(false, "expected an error result"), - _ => assert!(false, "expected a different error result"), - } -} - -#[test] -fn invalid_pascal_string() { - // String claims to be 32 bytes long (we provide 33 bytes to account for - // the 1 byte length prefix). - let pstr = "\x20xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; - let mut stream = Cursor::new(pstr); - // Reader wants to limit the total read length to 32 bytes, so any - // returned string must be no longer than 31 bytes. - let s = super::read_fixed_length_pascal_string(&mut stream, 32).unwrap(); - assert_eq!(s.len(), 31); -} - -#[test] -fn skip_padding_in_boxes() { - // Padding data could be added in the end of these boxes. Parser needs to skip - // them instead of returning error. - let box_names = vec![b"stts", b"stsc", b"stsz", b"stco", b"co64", b"stss"]; - - for name in box_names { - let mut stream = make_fullbox(BoxSize::Auto, name, 1, |s| { - s.append_repeated(0, 100) // add padding data - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - match name { - b"stts" => { - super::read_stts(&mut stream).expect("fail to skip padding: stts"); - }, - b"stsc" => { - super::read_stsc(&mut stream).expect("fail to skip padding: stsc"); - }, - b"stsz" => { - super::read_stsz(&mut stream).expect("fail to skip padding: stsz"); - }, - b"stco" => { - super::read_stco(&mut stream).expect("fail to skip padding: stco"); - }, - b"co64" => { - super::read_co64(&mut stream).expect("fail to skip padding: co64"); - }, - b"stss" => { - super::read_stss(&mut stream).expect("fail to skip padding: stss"); - }, - _ => (), - } - } -} - -#[test] -fn skip_padding_in_stsd() { - // Padding data could be added in the end of stsd boxes. Parser needs to skip - // them instead of returning error. - let avc = make_box(BoxSize::Auto, b"avc1", |s| { - s.append_repeated(0, 6) - .B16(1) - .append_repeated(0, 16) - .B16(320) - .B16(240) - .append_repeated(0, 14) - .append_repeated(0, 32) - .append_repeated(0, 4) - .B32(0xffffffff) - .append_bytes(b"avcC") - .append_repeated(0, 100) - }).into_inner(); - let mut stream = make_fullbox(BoxSize::Auto, b"stsd", 0, |s| { - s.B32(1) - .append_bytes(avc.as_slice()) - .append_repeated(0, 100) // add padding data - }); - - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - super::read_stsd(&mut stream, &mut super::Track::new(0)) - .expect("fail to skip padding: stsd"); -} - -#[test] -fn read_qt_wave_atom() { - let esds = make_fullbox(BoxSize::Auto, b"esds", 0, |s| { - s.B8(0x03) // elementary stream descriptor tag - .B8(0x0b) // esds length - .append_repeated(0, 2) - .B8(0x00) // flags - .B8(0x04) // decoder config descriptor tag - .B8(0x0d) // dcds length - .B8(0x6b) // mp3 - .append_repeated(0, 12) - }).into_inner(); - let wave = make_box(BoxSize::Auto, b"wave", |s| { - s.append_bytes(esds.as_slice()) - }).into_inner(); - let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { - s.append_repeated(0, 6) - .B16(1) // data_reference_count - .B16(1) // verion: qt -> 1 - .append_repeated(0, 6) - .B16(2) - .B16(16) - .append_repeated(0, 4) - .B32(48000 << 16) - .append_repeated(0, 16) - .append_bytes(wave.as_slice()) - }); - - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - let mut track = super::Track::new(0); - super::read_audio_sample_entry(&mut stream, &mut track) - .expect("fail to read qt wave atom"); - assert_eq!(track.codec_type, super::CodecType::MP3); -} diff --git a/media/libstagefright/binding/mp4parse/tests/afl.rs b/media/libstagefright/binding/mp4parse/tests/afl.rs deleted file mode 100644 index d4ffec0df..000000000 --- a/media/libstagefright/binding/mp4parse/tests/afl.rs +++ /dev/null @@ -1,53 +0,0 @@ -/// Regression tests from American Fuzzy Lop test cases. - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -/// These all caused panics at some point during development. - -extern crate mp4parse; - -use std::io::Cursor; - -/// https://github.com/mozilla/mp4parse-rust/issues/2 -/// -/// Test a box with 4-byte size, smaller than the smallest header. -#[test] -fn fuzz_2() { - let mut c = Cursor::new(b"\x00\x00\x00\x04\xa6\x00\x04\xa6".to_vec()); - let mut context = mp4parse::MediaContext::new(); - let _ = mp4parse::read_mp4(&mut c, &mut context); -} - -/// https://github.com/mozilla/mp4parse-rust/issues/4 -/// -/// Test a large (64 bit) box header with zero declared size. -#[test] -fn fuzz_4() { - let mut c = Cursor::new(b"\x00\x00\x00\x01\x30\x30\x30\x30\x00\x00\x00\x00\x00\x00\x00\x00".to_vec()); - let mut context = mp4parse::MediaContext::new(); - let _ = mp4parse::read_mp4(&mut c, &mut context); -} - -/// https://github.com/mozilla/mp4parse-rust/issues/5 -/// -/// Declares 202116104 compatible brands but does not supply them, -/// verifying read is properly bounded at the end of the stream. -#[test] -fn fuzz_5() { - let mut c = Cursor::new(b"\x30\x30\x30\x30\x66\x74\x79\x70\x30\x30\x30\x30\x30\x30\x30\x30".to_vec()); - let mut context = mp4parse::MediaContext::new(); - let _ = mp4parse::read_mp4(&mut c, &mut context); -} - -/// https://github.com/mozilla/mp4parse-rust/issues/6 -/// -/// Declares an ftyp box with a single invalid (short - 3 byte) compatible -/// brand and excludes the extra 3 bytes from the stream. -#[test] -fn fuzz_6() { - let mut c = Cursor::new(b"\x00\x00\x00\x13\x66\x74\x79\x70\x30\x30\x30\x30\x30\x30\x30\x30".to_vec()); - let mut context = mp4parse::MediaContext::new(); - let _ = mp4parse::read_mp4(&mut c, &mut context); -} diff --git a/media/libstagefright/binding/mp4parse/tests/minimal.mp4 b/media/libstagefright/binding/mp4parse/tests/minimal.mp4 Binary files differdeleted file mode 100644 index 9fe1e6722..000000000 --- a/media/libstagefright/binding/mp4parse/tests/minimal.mp4 +++ /dev/null diff --git a/media/libstagefright/binding/mp4parse/tests/public.rs b/media/libstagefright/binding/mp4parse/tests/public.rs deleted file mode 100644 index 1d36f5f5a..000000000 --- a/media/libstagefright/binding/mp4parse/tests/public.rs +++ /dev/null @@ -1,97 +0,0 @@ -/// Check if needed fields are still public. - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -extern crate mp4parse as mp4; - -use std::io::{Cursor, Read}; -use std::fs::File; - -// Taken from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53 -#[test] -fn public_api() { - let mut fd = File::open("tests/minimal.mp4").expect("Unknown file"); - let mut buf = Vec::new(); - fd.read_to_end(&mut buf).expect("File error"); - - let mut c = Cursor::new(&buf); - let mut context = mp4::MediaContext::new(); - mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed"); - assert_eq!(context.timescale, Some(mp4::MediaTimeScale(1000))); - for track in context.tracks { - match track.data { - Some(mp4::SampleEntry::Video(v)) => { - // track part - assert_eq!(track.duration, Some(mp4::TrackScaledTime(512, 0))); - assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0))); - assert_eq!(track.media_time, Some(mp4::TrackScaledTime(0, 0))); - assert_eq!(track.timescale, Some(mp4::TrackTimeScale(12800, 0))); - assert_eq!(v.width, 320); - assert_eq!(v.height, 240); - - // track.tkhd part - let tkhd = track.tkhd.unwrap(); - assert_eq!(tkhd.disabled, false); - assert_eq!(tkhd.duration, 40); - assert_eq!(tkhd.width, 20971520); - assert_eq!(tkhd.height, 15728640); - - // track.data part - assert_eq!(match v.codec_specific { - mp4::VideoCodecSpecific::AVCConfig(v) => { - assert!(v.len() > 0); - "AVC" - } - mp4::VideoCodecSpecific::VPxConfig(vpx) => { - // We don't enter in here, we just check if fields are public. - assert!(vpx.bit_depth > 0); - assert!(vpx.color_space > 0); - assert!(vpx.chroma_subsampling > 0); - assert!(vpx.codec_init.len() > 0); - "VPx" - } - }, "AVC"); - } - Some(mp4::SampleEntry::Audio(a)) => { - // track part - assert_eq!(track.duration, Some(mp4::TrackScaledTime(2944, 1))); - assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0))); - assert_eq!(track.media_time, Some(mp4::TrackScaledTime(1024, 1))); - assert_eq!(track.timescale, Some(mp4::TrackTimeScale(48000, 1))); - - // track.tkhd part - let tkhd = track.tkhd.unwrap(); - assert_eq!(tkhd.disabled, false); - assert_eq!(tkhd.duration, 62); - assert_eq!(tkhd.width, 0); - assert_eq!(tkhd.height, 0); - - // track.data part - assert_eq!(match a.codec_specific { - mp4::AudioCodecSpecific::ES_Descriptor(esds) => { - assert_eq!(esds.audio_codec, mp4::CodecType::AAC); - assert_eq!(esds.audio_sample_rate.unwrap(), 48000); - "ES" - } - mp4::AudioCodecSpecific::FLACSpecificBox(flac) => { - // STREAMINFO block must be present and first. - assert!(flac.blocks.len() > 0); - assert!(flac.blocks[0].block_type == 0); - assert!(flac.blocks[0].data.len() == 34); - "FLAC" - } - mp4::AudioCodecSpecific::OpusSpecificBox(opus) => { - // We don't enter in here, we just check if fields are public. - assert!(opus.version > 0); - "Opus" - } - }, "ES"); - assert!(a.samplesize > 0); - assert!(a.samplerate > 0); - } - Some(mp4::SampleEntry::Unknown) | None => {} - } - } -} diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml deleted file mode 100644 index 5c0836abe..000000000 --- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "mp4parse_capi" -version = "0.6.0" -authors = [ - "Ralph Giles <giles@mozilla.com>", - "Matthew Gregan <kinetik@flim.org>", - "Alfredo Yang <ayang@mozilla.com>", -] - -description = "Parser for ISO base media file format (mp4)" -documentation = "https://mp4parse-docs.surge.sh/mp4parse/" -license = "MPL-2.0" - -repository = "https://github.com/mozilla/mp4parse-rust" - -# Avoid complaints about trying to package test files. -exclude = [ - "*.mp4", -] - -[dependencies] -"mp4parse" = {version = "0.6.0", path = "../mp4parse"} - -# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. -[profile.release] -debug-assertions = true diff --git a/media/libstagefright/binding/mp4parse_capi/build.rs b/media/libstagefright/binding/mp4parse_capi/build.rs deleted file mode 100644 index 29f2a85ce..000000000 --- a/media/libstagefright/binding/mp4parse_capi/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -extern crate cheddar; - -fn main() { - println!("cargo:rerun-if-changed=src/lib.rs"); - // Generate mp4parse.h. - cheddar::Cheddar::new().expect("could not read manifest") - .insert_code("// THIS FILE IS AUTOGENERATED BY mp4parse-rust/build.rs - DO NOT EDIT\n\n") - .insert_code("// This Source Code Form is subject to the terms of the Mozilla Public\n") - .insert_code("// License, v. 2.0. If a copy of the MPL was not distributed with this\n") - .insert_code("// file, You can obtain one at https://mozilla.org/MPL/2.0/.") - .run_build("include/mp4parse.h"); -} diff --git a/media/libstagefright/binding/mp4parse_capi/src/lib.rs b/media/libstagefright/binding/mp4parse_capi/src/lib.rs deleted file mode 100644 index f52d8b169..000000000 --- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs +++ /dev/null @@ -1,870 +0,0 @@ -//! C API for mp4parse module. -//! -//! Parses ISO Base Media Format aka video/mp4 streams. -//! -//! # Examples -//! -//! ```rust -//! extern crate mp4parse_capi; -//! use std::io::Read; -//! -//! extern fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize { -//! let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) }; -//! let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) }; -//! match input.read(&mut buf) { -//! Ok(n) => n as isize, -//! Err(_) => -1, -//! } -//! } -//! -//! let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap(); -//! let io = mp4parse_capi::mp4parse_io { -//! read: buf_read, -//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void -//! }; -//! unsafe { -//! let parser = mp4parse_capi::mp4parse_new(&io); -//! let rv = mp4parse_capi::mp4parse_read(parser); -//! assert_eq!(rv, mp4parse_capi::mp4parse_error::MP4PARSE_OK); -//! mp4parse_capi::mp4parse_free(parser); -//! } -//! ``` - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -extern crate mp4parse; - -use std::io::Read; -use std::collections::HashMap; - -// Symbols we need from our rust api. -use mp4parse::MediaContext; -use mp4parse::TrackType; -use mp4parse::read_mp4; -use mp4parse::Error; -use mp4parse::SampleEntry; -use mp4parse::AudioCodecSpecific; -use mp4parse::VideoCodecSpecific; -use mp4parse::MediaTimeScale; -use mp4parse::MediaScaledTime; -use mp4parse::TrackTimeScale; -use mp4parse::TrackScaledTime; -use mp4parse::serialize_opus_header; -use mp4parse::CodecType; - -// rusty-cheddar's C enum generation doesn't namespace enum members by -// prefixing them, so we're forced to do it in our member names until -// https://github.com/Sean1708/rusty-cheddar/pull/35 is fixed. Importing -// the members into the module namespace avoids doubling up on the -// namespacing on the Rust side. -use mp4parse_error::*; -use mp4parse_track_type::*; - -#[repr(C)] -#[derive(PartialEq, Debug)] -pub enum mp4parse_error { - MP4PARSE_OK = 0, - MP4PARSE_ERROR_BADARG = 1, - MP4PARSE_ERROR_INVALID = 2, - MP4PARSE_ERROR_UNSUPPORTED = 3, - MP4PARSE_ERROR_EOF = 4, - MP4PARSE_ERROR_IO = 5, -} - -#[repr(C)] -#[derive(PartialEq, Debug)] -pub enum mp4parse_track_type { - MP4PARSE_TRACK_TYPE_VIDEO = 0, - MP4PARSE_TRACK_TYPE_AUDIO = 1, -} - -#[repr(C)] -#[derive(PartialEq, Debug)] -pub enum mp4parse_codec { - MP4PARSE_CODEC_UNKNOWN, - MP4PARSE_CODEC_AAC, - MP4PARSE_CODEC_FLAC, - MP4PARSE_CODEC_OPUS, - MP4PARSE_CODEC_AVC, - MP4PARSE_CODEC_VP9, - MP4PARSE_CODEC_MP3, -} - -#[repr(C)] -pub struct mp4parse_track_info { - pub track_type: mp4parse_track_type, - pub codec: mp4parse_codec, - pub track_id: u32, - pub duration: u64, - pub media_time: i64, // wants to be u64? understand how elst adjustment works - // TODO(kinetik): include crypto guff -} - -#[repr(C)] -pub struct mp4parse_codec_specific_config { - pub length: u32, - pub data: *const u8, -} - -impl Default for mp4parse_codec_specific_config { - fn default() -> Self { - mp4parse_codec_specific_config { - length: 0, - data: std::ptr::null_mut(), - } - } -} - -#[derive(Default)] -#[repr(C)] -pub struct mp4parse_track_audio_info { - pub channels: u16, - pub bit_depth: u16, - pub sample_rate: u32, - // TODO(kinetik): - // int32_t profile; - // int32_t extended_profile; // check types - codec_specific_config: mp4parse_codec_specific_config, -} - -#[repr(C)] -pub struct mp4parse_track_video_info { - pub display_width: u32, - pub display_height: u32, - pub image_width: u16, - pub image_height: u16, - // TODO(kinetik): - // extra_data - // codec_specific_config -} - -#[repr(C)] -pub struct mp4parse_fragment_info { - pub fragment_duration: u64, - // TODO: - // info in trex box. -} - -// Even though mp4parse_parser is opaque to C, rusty-cheddar won't let us -// use more than one member, so we introduce *another* wrapper. -struct Wrap { - context: MediaContext, - io: mp4parse_io, - poisoned: bool, - opus_header: HashMap<u32, Vec<u8>>, -} - -#[repr(C)] -#[allow(non_camel_case_types)] -pub struct mp4parse_parser(Wrap); - -impl mp4parse_parser { - fn context(&self) -> &MediaContext { - &self.0.context - } - - fn context_mut(&mut self) -> &mut MediaContext { - &mut self.0.context - } - - fn io_mut(&mut self) -> &mut mp4parse_io { - &mut self.0.io - } - - fn poisoned(&self) -> bool { - self.0.poisoned - } - - fn set_poisoned(&mut self, poisoned: bool) { - self.0.poisoned = poisoned; - } - - fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> { - &mut self.0.opus_header - } -} - -#[repr(C)] -#[derive(Clone)] -pub struct mp4parse_io { - pub read: extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize, - pub userdata: *mut std::os::raw::c_void, -} - -impl Read for mp4parse_io { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { - if buf.len() > isize::max_value() as usize { - return Err(std::io::Error::new(std::io::ErrorKind::Other, "buf length overflow in mp4parse_io Read impl")); - } - let rv = (self.read)(buf.as_mut_ptr(), buf.len(), self.userdata); - if rv >= 0 { - Ok(rv as usize) - } else { - Err(std::io::Error::new(std::io::ErrorKind::Other, "I/O error in mp4parse_io Read impl")) - } - } -} - -// C API wrapper functions. - -/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`. -#[no_mangle] -pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parser { - if io.is_null() || (*io).userdata.is_null() { - return std::ptr::null_mut(); - } - // is_null() isn't available on a fn type because it can't be null (in - // Rust) by definition. But since this value is coming from the C API, - // it *could* be null. Ideally, we'd wrap it in an Option to represent - // reality, but this causes rusty-cheddar to emit the wrong type - // (https://github.com/Sean1708/rusty-cheddar/issues/30). - if ((*io).read as *mut std::os::raw::c_void).is_null() { - return std::ptr::null_mut(); - } - let parser = Box::new(mp4parse_parser(Wrap { - context: MediaContext::new(), - io: (*io).clone(), - poisoned: false, - opus_header: HashMap::new(), - })); - Box::into_raw(parser) -} - -/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`. -#[no_mangle] -pub unsafe extern fn mp4parse_free(parser: *mut mp4parse_parser) { - assert!(!parser.is_null()); - let _ = Box::from_raw(parser); -} - -/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error. -#[no_mangle] -pub unsafe extern fn mp4parse_read(parser: *mut mp4parse_parser) -> mp4parse_error { - // Validate arguments from C. - if parser.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let mut context = (*parser).context_mut(); - let mut io = (*parser).io_mut(); - - let r = read_mp4(io, context); - match r { - Ok(_) => MP4PARSE_OK, - Err(Error::NoMoov) | Err(Error::InvalidData(_)) => { - // Block further calls. We've probable lost sync. - (*parser).set_poisoned(true); - MP4PARSE_ERROR_INVALID - } - Err(Error::Unsupported(_)) => MP4PARSE_ERROR_UNSUPPORTED, - Err(Error::UnexpectedEOF) => MP4PARSE_ERROR_EOF, - Err(Error::Io(_)) => { - // Block further calls after a read failure. - // Getting std::io::ErrorKind::UnexpectedEof is normal - // but our From trait implementation should have converted - // those to our Error::UnexpectedEOF variant. - (*parser).set_poisoned(true); - MP4PARSE_ERROR_IO - } - } -} - -/// Return the number of tracks parsed by previous `mp4parse_read()` call. -#[no_mangle] -pub unsafe extern fn mp4parse_get_track_count(parser: *const mp4parse_parser, count: *mut u32) -> mp4parse_error { - // Validate arguments from C. - if parser.is_null() || count.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - let context = (*parser).context(); - - // Make sure the track count fits in a u32. - if context.tracks.len() > u32::max_value() as usize { - return MP4PARSE_ERROR_INVALID; - } - *count = context.tracks.len() as u32; - MP4PARSE_OK -} - -/// Calculate numerator * scale / denominator, if possible. -/// -/// Applying the associativity of integer arithmetic, we divide first -/// and add the remainder after multiplying each term separately -/// to preserve precision while leaving more headroom. That is, -/// (n * s) / d is split into floor(n / d) * s + (n % d) * s / d. -/// -/// Return None on overflow or if the denominator is zero. -fn rational_scale(numerator: u64, denominator: u64, scale: u64) -> Option<u64> { - if denominator == 0 { - return None; - } - let integer = numerator / denominator; - let remainder = numerator % denominator; - match integer.checked_mul(scale) { - Some(integer) => remainder.checked_mul(scale) - .and_then(|remainder| (remainder/denominator).checked_add(integer)), - None => None, - } -} - -fn media_time_to_us(time: MediaScaledTime, scale: MediaTimeScale) -> Option<u64> { - let microseconds_per_second = 1000000; - rational_scale(time.0, scale.0, microseconds_per_second) -} - -fn track_time_to_us(time: TrackScaledTime, scale: TrackTimeScale) -> Option<u64> { - assert!(time.1 == scale.1); - let microseconds_per_second = 1000000; - rational_scale(time.0, scale.0, microseconds_per_second) -} - -/// Fill the supplied `mp4parse_track_info` with metadata for `track`. -#[no_mangle] -pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error { - if parser.is_null() || info.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context_mut(); - let track_index: usize = track_index as usize; - let info: &mut mp4parse_track_info = &mut *info; - - if track_index >= context.tracks.len() { - return MP4PARSE_ERROR_BADARG; - } - - info.track_type = match context.tracks[track_index].track_type { - TrackType::Video => MP4PARSE_TRACK_TYPE_VIDEO, - TrackType::Audio => MP4PARSE_TRACK_TYPE_AUDIO, - TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED, - }; - - info.codec = match context.tracks[track_index].data { - Some(SampleEntry::Audio(ref audio)) => match audio.codec_specific { - AudioCodecSpecific::OpusSpecificBox(_) => - mp4parse_codec::MP4PARSE_CODEC_OPUS, - AudioCodecSpecific::FLACSpecificBox(_) => - mp4parse_codec::MP4PARSE_CODEC_FLAC, - AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC => - mp4parse_codec::MP4PARSE_CODEC_AAC, - AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 => - mp4parse_codec::MP4PARSE_CODEC_MP3, - AudioCodecSpecific::ES_Descriptor(_) => - mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - }, - Some(SampleEntry::Video(ref video)) => match video.codec_specific { - VideoCodecSpecific::VPxConfig(_) => - mp4parse_codec::MP4PARSE_CODEC_VP9, - VideoCodecSpecific::AVCConfig(_) => - mp4parse_codec::MP4PARSE_CODEC_AVC, - }, - _ => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - }; - - let track = &context.tracks[track_index]; - - if let (Some(track_timescale), - Some(context_timescale)) = (track.timescale, - context.timescale) { - let media_time = - match track.media_time.map_or(Some(0), |media_time| { - track_time_to_us(media_time, track_timescale) }) { - Some(time) => time as i64, - None => return MP4PARSE_ERROR_INVALID, - }; - let empty_duration = - match track.empty_duration.map_or(Some(0), |empty_duration| { - media_time_to_us(empty_duration, context_timescale) }) { - Some(time) => time as i64, - None => return MP4PARSE_ERROR_INVALID, - }; - info.media_time = media_time - empty_duration; - - if let Some(track_duration) = track.duration { - match track_time_to_us(track_duration, track_timescale) { - Some(duration) => info.duration = duration, - None => return MP4PARSE_ERROR_INVALID, - } - } else { - // Duration unknown; stagefright returns 0 for this. - info.duration = 0 - } - } else { - return MP4PARSE_ERROR_INVALID - } - - info.track_id = match track.track_id { - Some(track_id) => track_id, - None => return MP4PARSE_ERROR_INVALID, - }; - - MP4PARSE_OK -} - -/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`. -#[no_mangle] -pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error { - if parser.is_null() || info.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context_mut(); - - if track_index as usize >= context.tracks.len() { - return MP4PARSE_ERROR_BADARG; - } - - let track = &context.tracks[track_index as usize]; - - match track.track_type { - TrackType::Audio => {} - _ => return MP4PARSE_ERROR_INVALID, - }; - - let audio = match track.data { - Some(ref data) => data, - None => return MP4PARSE_ERROR_INVALID, - }; - - let audio = match *audio { - SampleEntry::Audio(ref x) => x, - _ => return MP4PARSE_ERROR_INVALID, - }; - - (*info).channels = audio.channelcount; - (*info).bit_depth = audio.samplesize; - (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point - - match audio.codec_specific { - AudioCodecSpecific::ES_Descriptor(ref v) => { - if v.codec_specific_config.len() > std::u32::MAX as usize { - return MP4PARSE_ERROR_INVALID; - } - (*info).codec_specific_config.length = v.codec_specific_config.len() as u32; - (*info).codec_specific_config.data = v.codec_specific_config.as_ptr(); - if let Some(rate) = v.audio_sample_rate { - (*info).sample_rate = rate; - } - if let Some(channels) = v.audio_channel_count { - (*info).channels = channels; - } - } - AudioCodecSpecific::FLACSpecificBox(ref flac) => { - // Return the STREAMINFO metadata block in the codec_specific. - let streaminfo = &flac.blocks[0]; - if streaminfo.block_type != 0 || streaminfo.data.len() != 34 { - return MP4PARSE_ERROR_INVALID; - } - (*info).codec_specific_config.length = streaminfo.data.len() as u32; - (*info).codec_specific_config.data = streaminfo.data.as_ptr(); - } - AudioCodecSpecific::OpusSpecificBox(ref opus) => { - let mut v = Vec::new(); - match serialize_opus_header(opus, &mut v) { - Err(_) => { - return MP4PARSE_ERROR_INVALID; - } - Ok(_) => { - let header = (*parser).opus_header_mut(); - header.insert(track_index, v); - match header.get(&track_index) { - None => {} - Some(v) => { - if v.len() > std::u32::MAX as usize { - return MP4PARSE_ERROR_INVALID; - } - (*info).codec_specific_config.length = v.len() as u32; - (*info).codec_specific_config.data = v.as_ptr(); - } - } - } - } - } - } - - MP4PARSE_OK -} - -/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`. -#[no_mangle] -pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error { - if parser.is_null() || info.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context_mut(); - - if track_index as usize >= context.tracks.len() { - return MP4PARSE_ERROR_BADARG; - } - - let track = &context.tracks[track_index as usize]; - - match track.track_type { - TrackType::Video => {} - _ => return MP4PARSE_ERROR_INVALID, - }; - - let video = match track.data { - Some(ref data) => data, - None => return MP4PARSE_ERROR_INVALID, - }; - - let video = match *video { - SampleEntry::Video(ref x) => x, - _ => return MP4PARSE_ERROR_INVALID, - }; - - if let Some(ref tkhd) = track.tkhd { - (*info).display_width = tkhd.width >> 16; // 16.16 fixed point - (*info).display_height = tkhd.height >> 16; // 16.16 fixed point - } else { - return MP4PARSE_ERROR_INVALID; - } - (*info).image_width = video.width; - (*info).image_height = video.height; - - MP4PARSE_OK -} - -#[no_mangle] -pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error { - if parser.is_null() || info.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context(); - let info: &mut mp4parse_fragment_info = &mut *info; - - info.fragment_duration = 0; - - let duration = match context.mvex { - Some(ref mvex) => mvex.fragment_duration, - None => return MP4PARSE_ERROR_INVALID, - }; - - if let (Some(time), Some(scale)) = (duration, context.timescale) { - info.fragment_duration = match media_time_to_us(time, scale) { - Some(time_us) => time_us as u64, - None => return MP4PARSE_ERROR_INVALID, - } - } - - MP4PARSE_OK -} - -// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes. -#[no_mangle] -pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_id: u32, fragmented: *mut u8) -> mp4parse_error { - if parser.is_null() || (*parser).poisoned() { - return MP4PARSE_ERROR_BADARG; - } - - let context = (*parser).context_mut(); - let tracks = &context.tracks; - (*fragmented) = false as u8; - - if context.mvex.is_none() { - return MP4PARSE_OK; - } - - // check sample tables. - let mut iter = tracks.iter(); - match iter.find(|track| track.track_id == Some(track_id)) { - Some(track) if track.empty_sample_boxes.all_empty() => (*fragmented) = true as u8, - Some(_) => {}, - None => return MP4PARSE_ERROR_BADARG, - } - - MP4PARSE_OK -} - -#[cfg(test)] -extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize { - panic!("panic_read shouldn't be called in these tests"); -} - -#[cfg(test)] -extern fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize { - -1 -} - -#[cfg(test)] -extern fn valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize { - let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) }; - - let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) }; - match input.read(&mut buf) { - Ok(n) => n as isize, - Err(_) => -1, - } -} - -#[test] -fn new_parser() { - let mut dummy_value: u32 = 42; - let io = mp4parse_io { - read: panic_read, - userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, - }; - unsafe { - let parser = mp4parse_new(&io); - assert!(!parser.is_null()); - mp4parse_free(parser); - } -} - -#[test] -#[should_panic(expected = "assertion failed")] -fn free_null_parser() { - unsafe { - mp4parse_free(std::ptr::null_mut()); - } -} - -#[test] -fn get_track_count_null_parser() { - unsafe { - let mut count: u32 = 0; - let rv = mp4parse_get_track_count(std::ptr::null(), std::ptr::null_mut()); - assert!(rv == MP4PARSE_ERROR_BADARG); - let rv = mp4parse_get_track_count(std::ptr::null(), &mut count); - assert!(rv == MP4PARSE_ERROR_BADARG); - } -} - -#[test] -fn arg_validation() { - unsafe { - // Passing a null mp4parse_io is an error. - let parser = mp4parse_new(std::ptr::null()); - assert!(parser.is_null()); - - let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut(); - - // Passing an mp4parse_io with null members is an error. - let io = mp4parse_io { read: std::mem::transmute(null_mut), - userdata: null_mut }; - let parser = mp4parse_new(&io); - assert!(parser.is_null()); - - let io = mp4parse_io { read: panic_read, - userdata: null_mut }; - let parser = mp4parse_new(&io); - assert!(parser.is_null()); - - let mut dummy_value = 42; - let io = mp4parse_io { - read: std::mem::transmute(null_mut), - userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, - }; - let parser = mp4parse_new(&io); - assert!(parser.is_null()); - - // Passing a null mp4parse_parser is an error. - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(std::ptr::null_mut())); - - let mut dummy_info = mp4parse_track_info { - track_type: MP4PARSE_TRACK_TYPE_VIDEO, - codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - track_id: 0, - duration: 0, - media_time: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info)); - - let mut dummy_video = mp4parse_track_video_info { - display_width: 0, - display_height: 0, - image_width: 0, - image_height: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video)); - - let mut dummy_audio = Default::default(); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio)); - } -} - -#[test] -fn arg_validation_with_parser() { - unsafe { - let mut dummy_value = 42; - let io = mp4parse_io { - read: error_read, - userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, - }; - let parser = mp4parse_new(&io); - assert!(!parser.is_null()); - - // Our mp4parse_io read should simply fail with an error. - assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser)); - - // The parser is now poisoned and unusable. - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(parser)); - - // Null info pointers are an error. - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, std::ptr::null_mut())); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, std::ptr::null_mut())); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, std::ptr::null_mut())); - - let mut dummy_info = mp4parse_track_info { - track_type: MP4PARSE_TRACK_TYPE_VIDEO, - codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - track_id: 0, - duration: 0, - media_time: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, &mut dummy_info)); - - let mut dummy_video = mp4parse_track_video_info { - display_width: 0, - display_height: 0, - image_width: 0, - image_height: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, &mut dummy_video)); - - let mut dummy_audio = Default::default(); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio)); - - mp4parse_free(parser); - } -} - -#[test] -fn get_track_count_poisoned_parser() { - unsafe { - let mut dummy_value = 42; - let io = mp4parse_io { - read: error_read, - userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, - }; - let parser = mp4parse_new(&io); - assert!(!parser.is_null()); - - // Our mp4parse_io read should simply fail with an error. - assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser)); - - let mut count: u32 = 0; - let rv = mp4parse_get_track_count(parser, &mut count); - assert!(rv == MP4PARSE_ERROR_BADARG); - } -} - -#[test] -fn arg_validation_with_data() { - unsafe { - let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap(); - let io = mp4parse_io { read: valid_read, - userdata: &mut file as *mut _ as *mut std::os::raw::c_void }; - let parser = mp4parse_new(&io); - assert!(!parser.is_null()); - - assert_eq!(MP4PARSE_OK, mp4parse_read(parser)); - - let mut count: u32 = 0; - assert_eq!(MP4PARSE_OK, mp4parse_get_track_count(parser, &mut count)); - assert_eq!(2, count); - - let mut info = mp4parse_track_info { - track_type: MP4PARSE_TRACK_TYPE_VIDEO, - codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - track_id: 0, - duration: 0, - media_time: 0, - }; - assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 0, &mut info)); - assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO); - assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AVC); - assert_eq!(info.track_id, 1); - assert_eq!(info.duration, 40000); - assert_eq!(info.media_time, 0); - - assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 1, &mut info)); - assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_AUDIO); - assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AAC); - assert_eq!(info.track_id, 2); - assert_eq!(info.duration, 61333); - assert_eq!(info.media_time, 21333); - - let mut video = mp4parse_track_video_info { - display_width: 0, - display_height: 0, - image_width: 0, - image_height: 0, - }; - assert_eq!(MP4PARSE_OK, mp4parse_get_track_video_info(parser, 0, &mut video)); - assert_eq!(video.display_width, 320); - assert_eq!(video.display_height, 240); - assert_eq!(video.image_width, 320); - assert_eq!(video.image_height, 240); - - let mut audio = Default::default(); - assert_eq!(MP4PARSE_OK, mp4parse_get_track_audio_info(parser, 1, &mut audio)); - assert_eq!(audio.channels, 1); - assert_eq!(audio.bit_depth, 16); - assert_eq!(audio.sample_rate, 48000); - - // Test with an invalid track number. - let mut info = mp4parse_track_info { - track_type: MP4PARSE_TRACK_TYPE_VIDEO, - codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, - track_id: 0, - duration: 0, - media_time: 0, - }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 3, &mut info)); - assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO); - assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_UNKNOWN); - assert_eq!(info.track_id, 0); - assert_eq!(info.duration, 0); - assert_eq!(info.media_time, 0); - - let mut video = mp4parse_track_video_info { display_width: 0, - display_height: 0, - image_width: 0, - image_height: 0 }; - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 3, &mut video)); - assert_eq!(video.display_width, 0); - assert_eq!(video.display_height, 0); - assert_eq!(video.image_width, 0); - assert_eq!(video.image_height, 0); - - let mut audio = Default::default(); - assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 3, &mut audio)); - assert_eq!(audio.channels, 0); - assert_eq!(audio.bit_depth, 0); - assert_eq!(audio.sample_rate, 0); - - mp4parse_free(parser); - } -} - -#[test] -fn rational_scale_overflow() { - assert_eq!(rational_scale(17, 3, 1000), Some(5666)); - let large = 0x4000_0000_0000_0000; - assert_eq!(rational_scale(large, 2, 2), Some(large)); - assert_eq!(rational_scale(large, 4, 4), Some(large)); - assert_eq!(rational_scale(large, 2, 8), None); - assert_eq!(rational_scale(large, 8, 4), Some(large/2)); - assert_eq!(rational_scale(large + 1, 4, 4), Some(large+1)); - assert_eq!(rational_scale(large, 40, 1000), None); -} - -#[test] -fn media_time_overflow() { - let scale = MediaTimeScale(90000); - let duration = MediaScaledTime(9007199254710000); - assert_eq!(media_time_to_us(duration, scale), Some(100079991719000000)); -} - -#[test] -fn track_time_overflow() { - let scale = TrackTimeScale(44100, 0); - let duration = TrackScaledTime(4413527634807900, 0); - assert_eq!(track_time_to_us(duration, scale), Some(100079991719000000)); -} diff --git a/media/libstagefright/binding/update-rust.sh b/media/libstagefright/binding/update-rust.sh deleted file mode 100755 index a8a462f6d..000000000 --- a/media/libstagefright/binding/update-rust.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -e -# Script to update mp4parse-rust sources to latest upstream - -# Default version. -VER=v0.6.0 - -# Accept version or commit from the command line. -if test -n "$1"; then - VER=$1 -fi - -echo "Fetching sources..." -rm -rf _upstream -git clone https://github.com/mozilla/mp4parse-rust _upstream/mp4parse -pushd _upstream/mp4parse -git checkout ${VER} -echo "Verifying sources..." -pushd mp4parse -cargo test -popd -echo "Constructing C api header..." -pushd mp4parse_capi -cargo build -echo "Verifying sources..." -cargo test -popd -popd -rm -rf mp4parse -mkdir -p mp4parse/src -cp _upstream/mp4parse/mp4parse/Cargo.toml mp4parse/ -cp _upstream/mp4parse/mp4parse/src/*.rs mp4parse/src/ -mkdir -p mp4parse/tests -cp _upstream/mp4parse/mp4parse/tests/*.rs mp4parse/tests/ -cp _upstream/mp4parse/mp4parse/tests/*.mp4 mp4parse/tests/ -rm -rf mp4parse_capi -mkdir -p mp4parse_capi/src -cp _upstream/mp4parse/mp4parse_capi/Cargo.toml mp4parse_capi/ -cp _upstream/mp4parse/mp4parse_capi/build.rs mp4parse_capi/ -cp _upstream/mp4parse/mp4parse_capi/include/mp4parse.h include/ -cp _upstream/mp4parse/mp4parse_capi/src/*.rs mp4parse_capi/src/ - -echo "Applying patches..." -patch -p4 < mp4parse-cargo.patch - -echo "Cleaning up..." -rm -rf _upstream - -echo "Updating gecko Cargo.lock..." -pushd ../../../toolkit/library/rust/ -cargo update --package mp4parse_capi -popd -pushd ../../../toolkit/library/gtest/rust/ -cargo update --package mp4parse_capi -popd - -echo "Updated to ${VER}." |