Remove Rust MP4 parser

Part 1 for #58
This commit is contained in:
wolfbeast
2018-03-13 10:00:19 +01:00
committed by Roy Tam
parent 3adb72e0f0
commit 1ef84eb532
28 changed files with 0 additions and 4526 deletions
-1
View File
@@ -53,7 +53,6 @@ MOZ_PROFILE_MIGRATOR=1
MOZ_APP_STATIC_INI=1
MOZ_WEBGL_CONFORMANT=1
MOZ_JSDOWNLOADS=1
MOZ_RUST_MP4PARSE=1
MOZ_RUST_URLPARSE=1
MOZ_WEBRTC=1
MOZ_WEBEXTENSIONS=1
-4
View File
@@ -156,10 +156,6 @@ private:
DECL_MEDIA_PREF("media.ogg.flac.enabled", FlacInOgg, bool, false);
DECL_MEDIA_PREF("media.flac.enabled", FlacEnabled, bool, true);
#if defined(MOZ_RUST_MP4PARSE) && !defined(RELEASE_OR_BETA)
DECL_MEDIA_PREF("media.rust.test_mode", RustTestMode, bool, false);
#endif
public:
// Manage the singleton:
static MediaPrefs& GetSingleton();
@@ -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
{
@@ -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
@@ -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;
};
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
);
File diff suppressed because it is too large Load Diff
@@ -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);
}
@@ -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);
}
@@ -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 => {}
}
}
}
@@ -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
@@ -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");
}
@@ -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));
}
@@ -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}."
-2
View File
@@ -31,8 +31,6 @@ MOZ_XULRUNNER=
MOZ_CAPTURE=1
MOZ_RAW=1
MOZ_RUST_MP4PARSE=1
# use custom widget for html:select
MOZ_USE_NATIVE_POPUP_WINDOWS=1
-3
View File
@@ -2432,9 +2432,6 @@ fi
# Propagate feature switches for code written in rust from confvars.sh
if test -n "$MOZ_RUST"; then
if test -n "$MOZ_RUST_MP4PARSE"; then
AC_DEFINE(MOZ_RUST_MP4PARSE)
fi
if test -n "$MOZ_RUST_URLPARSE"; then
AC_DEFINE(MOZ_RUST_URLPARSE)
fi
@@ -6926,39 +6926,6 @@
"n_buckets": 1000,
"description": "The time (in milliseconds) that it took to display a selected source to the user."
},
"MEDIA_RUST_MP4PARSE_SUCCESS": {
"alert_emails": ["giles@mozilla.com", "kinetik@flim.org"],
"expires_in_version": "55",
"kind": "boolean",
"bug_numbers": [1220885],
"description": "(Bug 1220885) Whether the rust mp4 demuxer successfully parsed a stream segment.",
"cpp_guard": "MOZ_RUST_MP4PARSE"
},
"MEDIA_RUST_MP4PARSE_ERROR_CODE": {
"alert_emails": ["giles@mozilla.com", "kinetik@flim.org"],
"expires_in_version": "55",
"kind": "enumerated",
"n_values": 32,
"bug_numbers": [1238420],
"description": "The error code reported when an MP4 parse attempt has failed.0 = OK, 1 = bad argument, 2 = invalid data, 3 = unsupported, 4 = unexpected end of file, 5 = read error.",
"cpp_guard": "MOZ_RUST_MP4PARSE"
},
"MEDIA_RUST_MP4PARSE_TRACK_MATCH_AUDIO": {
"alert_emails": ["giles@mozilla.com", "kinetik@flim.org"],
"expires_in_version": "55",
"kind": "boolean",
"bug_numbers": [1231169],
"description": "Whether rust and stagefight mp4 parser audio track results match.",
"cpp_guard": "MOZ_RUST_MP4PARSE"
},
"MEDIA_RUST_MP4PARSE_TRACK_MATCH_VIDEO": {
"alert_emails": ["giles@mozilla.com", "kinetik@flim.org"],
"expires_in_version": "55",
"kind": "boolean",
"bug_numbers": [1231169],
"description": "Whether rust and stagefight mp4 parser video track results match.",
"cpp_guard": "MOZ_RUST_MP4PARSE"
},
"MEDIA_WMF_DECODE_ERROR": {
"expires_in_version": "55",
"kind": "enumerated",
-20
View File
@@ -3,7 +3,6 @@ name = "gkrust-gtest"
version = "0.1.0"
dependencies = [
"gkrust-shared 0.1.0",
"mp4parse-gtest 0.1.0",
"nsstring-gtest 0.1.0",
]
@@ -16,7 +15,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "gkrust-shared"
version = "0.1.0"
dependencies = [
"mp4parse_capi 0.6.0",
"nsstring 0.1.0",
"rust_url_capi 0.0.1",
]
@@ -41,24 +39,6 @@ name = "matches"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "mp4parse"
version = "0.6.0"
dependencies = [
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mp4parse-gtest"
version = "0.1.0"
[[package]]
name = "mp4parse_capi"
version = "0.6.0"
dependencies = [
"mp4parse 0.6.0",
]
[[package]]
name = "nsstring"
version = "0.1.0"
-1
View File
@@ -6,7 +6,6 @@ license = "MPL-2.0"
description = "Testing code for libgkrust"
[dependencies]
mp4parse-gtest = { path = "../../../../dom/media/gtest" }
nsstring-gtest = { path = "../../../../xpcom/rust/nsstring/gtest" }
gkrust-shared = { path = "../../rust/shared" }
-1
View File
@@ -3,5 +3,4 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
extern crate gkrust_shared;
extern crate mp4parse_gtest;
extern crate nsstring_gtest;
-15
View File
@@ -14,7 +14,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "gkrust-shared"
version = "0.1.0"
dependencies = [
"mp4parse_capi 0.6.0",
"nsstring 0.1.0",
"rust_url_capi 0.0.1",
]
@@ -39,20 +38,6 @@ name = "matches"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "mp4parse"
version = "0.6.0"
dependencies = [
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mp4parse_capi"
version = "0.6.0"
dependencies = [
"mp4parse 0.6.0",
]
[[package]]
name = "nsstring"
version = "0.1.0"
-1
View File
@@ -6,7 +6,6 @@ license = "MPL-2.0"
description = "Shared Rust code for libxul"
[dependencies]
mp4parse_capi = { path = "../../../../media/libstagefright/binding/mp4parse_capi" }
nsstring = { path = "../../../../xpcom/rust/nsstring" }
rust_url_capi = { path = "../../../../netwerk/base/rust-url-capi" }
-1
View File
@@ -2,6 +2,5 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
extern crate mp4parse_capi;
extern crate nsstring;
extern crate rust_url_capi;