Bug 1437351 - Update mp4parse to v0.10.0. r?kinetik draft
authorRalph Giles <giles@mozilla.com>
Tue, 13 Feb 2018 16:58:43 -0800
changeset 754934 407c65acf3c753aaf278f1449aa63c3c8c452047
parent 754933 f693a15fb2b401a32fca44c3d77b01894fbc1cd1
child 754935 a12a724a6ff2af5f100e192d0a0b8126c6da4c68
push id99063
push userbmo:giles@thaumas.net
push dateWed, 14 Feb 2018 17:26:43 +0000
reviewerskinetik
bugs1437351
milestone60.0a1
Bug 1437351 - Update mp4parse to v0.10.0. r?kinetik Import v0.10.0 of the mp4parse and mp4parse_capi crates and update dependencies. Reduces library size by removing debug tracing in release builds. Also adds recognition of the ALAC codec, although we don't plan to support it. MozReview-Commit-ID: F1bnotCmbDf
media/mp4parse-rust/mp4parse.h
media/mp4parse-rust/mp4parse/Cargo.toml
media/mp4parse-rust/mp4parse/src/boxes.rs
media/mp4parse-rust/mp4parse/src/lib.rs
media/mp4parse-rust/mp4parse/src/tests.rs
media/mp4parse-rust/mp4parse/tests/public.rs
media/mp4parse-rust/mp4parse_capi/Cargo.toml
media/mp4parse-rust/mp4parse_capi/src/lib.rs
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
--- a/media/mp4parse-rust/mp4parse.h
+++ b/media/mp4parse-rust/mp4parse.h
@@ -11,27 +11,28 @@ extern "C" {
 
 // THIS FILE IS AUTOGENERATED BY mp4parse_capi/build.rs - DO NOT EDIT
 
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdbool.h>
 
 typedef enum {
-  MP4PARSE_CODEC_UNKNOWN = 0,
-  MP4PARSE_CODEC_AAC = 1,
-  MP4PARSE_CODEC_FLAC = 2,
-  MP4PARSE_CODEC_OPUS = 3,
-  MP4PARSE_CODEC_AVC = 4,
-  MP4PARSE_CODEC_VP9 = 5,
-  MP4PARSE_CODEC_MP3 = 6,
-  MP4PARSE_CODEC_MP4V = 7,
-  MP4PARSE_CODEC_JPEG = 8,
-  MP4PARSE_CODEC_AC3 = 9,
-  MP4PARSE_CODEC_EC3 = 10,
+  MP4PARSE_CODEC_UNKNOWN,
+  MP4PARSE_CODEC_AAC,
+  MP4PARSE_CODEC_FLAC,
+  MP4PARSE_CODEC_OPUS,
+  MP4PARSE_CODEC_AVC,
+  MP4PARSE_CODEC_VP9,
+  MP4PARSE_CODEC_MP3,
+  MP4PARSE_CODEC_MP4V,
+  MP4PARSE_CODEC_JPEG,
+  MP4PARSE_CODEC_AC3,
+  MP4PARSE_CODEC_EC3,
+  MP4PARSE_CODEC_ALAC,
 } Mp4parseCodec;
 
 typedef enum {
   MP4PARSE_STATUS_OK = 0,
   MP4PARSE_STATUS_BAD_ARG = 1,
   MP4PARSE_STATUS_INVALID = 2,
   MP4PARSE_STATUS_UNSUPPORTED = 3,
   MP4PARSE_STATUS_EOF = 4,
@@ -164,21 +165,16 @@ Mp4parseStatus mp4parse_get_track_video_
 /*
  * A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
  */
 Mp4parseStatus mp4parse_is_fragmented(Mp4parseParser *parser,
                                       uint32_t track_id,
                                       uint8_t *fragmented);
 
 /*
- * Enable `mp4_parser` log.
- */
-void mp4parse_log(bool enable);
-
-/*
  * Allocate an `Mp4parseParser*` to read from the supplied `Mp4parseIo`.
  */
 Mp4parseParser *mp4parse_new(const Mp4parseIo *io);
 
 /*
  * Run the `Mp4parseParser*` allocated by `mp4parse_new()` until EOF or error.
  */
 Mp4parseStatus mp4parse_read(Mp4parseParser *parser);
--- a/media/mp4parse-rust/mp4parse/Cargo.toml
+++ b/media/mp4parse-rust/mp4parse/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "mp4parse"
-version = "0.9.1"
+version = "0.10.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://docs.rs/mp4parse/"
@@ -16,16 +16,16 @@ repository = "https://github.com/mozilla
 
 # Avoid complaints about trying to package test files.
 exclude = [
   "*.mp4",
 ]
 
 
 [dependencies]
-byteorder = "1.0.0"
+byteorder = "1.2.1"
 bitreader = { version = "0.3.0" }
-num-traits = "0.1.37"
+num-traits = "0.2.0"
 mp4parse_fallible = { version = "0.0.1", optional = true }
+log = "0.4"
 
 [dev-dependencies]
 test-assembler = "0.1.2"
-
--- a/media/mp4parse-rust/mp4parse/src/boxes.rs
+++ b/media/mp4parse-rust/mp4parse/src/boxes.rs
@@ -133,9 +133,10 @@ box_database!(
     ProtectionSystemSpecificHeaderBox 0x70737368, // "pssh"
     SchemeInformationBox              0x73636869, // "schi"
     TrackEncryptionBox                0x74656e63, // "tenc"
     ProtectionSchemeInformationBox    0x73696e66, // "sinf"
     OriginalFormatBox                 0x66726d61, // "frma"
     MP3AudioSampleEntry               0x2e6d7033, // ".mp3" - from F4V.
     CompositionOffsetBox              0x63747473, // "ctts"
     LPCMAudioSampleEntry              0x6C70636D, // "lpcm" - quicktime atom
+    ALACSpecificBox                   0x616C6163, // "alac" - Also used by ALACSampleEntry
 );
--- a/media/mp4parse-rust/mp4parse/src/lib.rs
+++ b/media/mp4parse-rust/mp4parse/src/lib.rs
@@ -1,16 +1,19 @@
 //! 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(feature = "fuzz")]
 extern crate afl;
 
+#[macro_use]
+extern crate log;
+
 extern crate byteorder;
 extern crate bitreader;
 extern crate num_traits;
 use byteorder::{ReadBytesExt, WriteBytesExt};
 use bitreader::{BitReader, ReadInto};
 use std::io::{Read, Take};
 use std::io::Cursor;
 use std::cmp;
@@ -31,35 +34,16 @@ mod tests;
 
 // Arbitrary buffer size limit used for raw read_bufs on a box.
 const BUF_SIZE_LIMIT: usize = 1024 * 1024;
 
 // Max table length. Calculating in worth case for one week long video, one
 // frame per table entry in 30 fps.
 const TABLE_SIZE_LIMIT: u32 = 30 * 60 * 60 * 24 * 7;
 
-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 )* );
-        }
-    )
-}
-
 // TODO: vec_push() and vec_reserve() needs to be replaced when Rust supports
 // fallible memory allocation in raw_vec.
 #[allow(unreachable_code)]
 pub fn vec_push<T>(vec: &mut Vec<T>, val: T) -> std::result::Result<(), ()> {
     #[cfg(feature = "mp4parse_fallible")]
     {
         return vec.try_push(val);
     }
@@ -311,16 +295,17 @@ pub struct ES_Descriptor {
 }
 
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone)]
 pub enum AudioCodecSpecific {
     ES_Descriptor(ES_Descriptor),
     FLACSpecificBox(FLACSpecificBox),
     OpusSpecificBox(OpusSpecificBox),
+    ALACSpecificBox(ALACSpecificBox),
     MP3,
     LPCM,
 }
 
 #[derive(Debug, Clone)]
 pub struct AudioSampleEntry {
     data_reference_index: u16,
     pub channelcount: u32,
@@ -387,16 +372,23 @@ pub struct OpusSpecificBox {
     output_channel_count: u8,
     pre_skip: u16,
     input_sample_rate: u32,
     output_gain: i16,
     channel_mapping_family: u8,
     channel_mapping_table: Option<ChannelMappingTable>,
 }
 
+/// Represent an ALACSpecificBox 'alac'
+#[derive(Debug, Clone)]
+pub struct ALACSpecificBox {
+    version: u8,
+    pub data: Vec<u8>,
+}
+
 #[derive(Debug)]
 pub struct MovieExtendsBox {
     pub fragment_duration: Option<MediaScaledTime>,
 }
 
 pub type ByteData = Vec<u8>;
 
 #[derive(Debug, Default)]
@@ -459,16 +451,17 @@ pub enum CodecType {
     H264,   // 14496-10
     MP4V,   // 14496-2
     VP10,
     VP9,
     VP8,
     EncryptedVideo,
     EncryptedAudio,
     LPCM,   // QT
+    ALAC,
 }
 
 impl Default for CodecType {
     fn default() -> Self { CodecType::Unknown }
 }
 
 /// The media's global (mvhd) timescale in units per second.
 #[derive(Debug, Copy, Clone, PartialEq)]
@@ -568,17 +561,17 @@ impl<'a, T: Read> BMFFBox<'a, T> {
         BoxIter::new(self)
     }
 }
 
 impl<'a, T: Read> Drop for BMFFBox<'a, T> {
     fn drop(&mut self) {
         if self.content.limit() > 0 {
             let name: FourCC = From::from(self.head.name);
-            log!("Dropping {} bytes in '{}'", self.content.limit(), name);
+            debug!("Dropping {} bytes in '{}'", self.content.limit(), name);
         }
     }
 }
 
 /// 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
@@ -622,38 +615,38 @@ fn read_fullbox_extra<T: ReadBytesExt>(s
         (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);
+        debug!("{:?} (skipped)", header);
         (header.size - header.offset) as usize
     };
     assert_eq!(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);
+        debug!("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());
+            debug!("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,
@@ -679,27 +672,27 @@ pub fn read_mp4<T: Read>(f: &mut T, cont
         // 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 = read_ftyp(&mut b)?;
                 found_ftyp = true;
-                log!("{:?}", ftyp);
+                debug!("{:?}", ftyp);
             }
             BoxType::MovieBox => {
                 read_moov(&mut b, context)?;
                 found_moov = true;
             }
             _ => 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 {
+            debug!("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
@@ -723,31 +716,31 @@ fn parse_mvhd<T: Read>(f: &mut BMFFBox<T
 
 fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<()> {
     let mut iter = f.box_iter();
     while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::MovieHeaderBox => {
                 let (mvhd, timescale) = parse_mvhd(&mut b)?;
                 context.timescale = timescale;
-                log!("{:?}", mvhd);
+                debug!("{:?}", mvhd);
             }
             BoxType::TrackBox => {
                 let mut track = Track::new(context.tracks.len());
                 read_trak(&mut b, &mut track)?;
                 vec_push(&mut context.tracks, track)?;
             }
             BoxType::MovieExtendsBox => {
                 let mvex = read_mvex(&mut b)?;
-                log!("{:?}", mvex);
+                debug!("{:?}", mvex);
                 context.mvex = Some(mvex);
             }
             BoxType::ProtectionSystemSpecificHeaderBox => {
                 let pssh = read_pssh(&mut b)?;
-                log!("{:?}", pssh);
+                debug!("{:?}", pssh);
                 vec_push(&mut context.psshs, pssh)?;
             }
             _ => skip_box_content(&mut b)?,
         };
         check_parser_state!(b.content);
     }
     Ok(())
 }
@@ -820,17 +813,17 @@ fn read_mehd<T: Read>(src: &mut BMFFBox<
 fn read_trak<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
     let mut iter = f.box_iter();
     while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::TrackHeaderBox => {
                 let tkhd = read_tkhd(&mut b)?;
                 track.track_id = Some(tkhd.track_id);
                 track.tkhd = Some(tkhd.clone());
-                log!("{:?}", tkhd);
+                debug!("{:?}", tkhd);
             }
             BoxType::EditBox => read_edts(&mut b, track)?,
             BoxType::MediaBox => read_mdia(&mut b, track)?,
             _ => skip_box_content(&mut b)?,
         };
         check_parser_state!(b.content);
     }
     Ok(())
@@ -855,17 +848,17 @@ fn read_edts<T: Read>(f: &mut BMFFBox<T>
                     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::<u64>(elst.edits[idx].media_time as u64,
                                                         track.id));
-                log!("{:?}", elst);
+                debug!("{:?}", elst);
             }
             _ => skip_box_content(&mut b)?,
         };
         check_parser_state!(b.content);
     }
     Ok(())
 }
 
@@ -885,27 +878,27 @@ fn parse_mdhd<T: Read>(f: &mut BMFFBox<T
 fn read_mdia<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
     let mut iter = f.box_iter();
     while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::MediaHeaderBox => {
                 let (mdhd, duration, timescale) = parse_mdhd(&mut b, track)?;
                 track.duration = duration;
                 track.timescale = timescale;
-                log!("{:?}", mdhd);
+                debug!("{:?}", mdhd);
             }
             BoxType::HandlerBox => {
                 let hdlr = read_hdlr(&mut b)?;
 
                 match hdlr.handler_type.value.as_ref() {
                     "vide" => track.track_type = TrackType::Video,
                     "soun" => track.track_type = TrackType::Audio,
                     _ => (),
                 }
-                log!("{:?}", hdlr);
+                debug!("{:?}", hdlr);
             }
             BoxType::MediaInformationBox => read_minf(&mut b, track)?,
             _ => skip_box_content(&mut b)?,
         };
         check_parser_state!(b.content);
     }
     Ok(())
 }
@@ -923,51 +916,51 @@ fn read_minf<T: Read>(f: &mut BMFFBox<T>
 }
 
 fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
     let mut iter = f.box_iter();
     while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::SampleDescriptionBox => {
                 let stsd = read_stsd(&mut b, track)?;
-                log!("{:?}", stsd);
+                debug!("{:?}", stsd);
             }
             BoxType::TimeToSampleBox => {
                 let stts = read_stts(&mut b)?;
-                log!("{:?}", stts);
+                debug!("{:?}", stts);
                 track.stts = Some(stts);
             }
             BoxType::SampleToChunkBox => {
                 let stsc = read_stsc(&mut b)?;
-                log!("{:?}", stsc);
+                debug!("{:?}", stsc);
                 track.stsc = Some(stsc);
             }
             BoxType::SampleSizeBox => {
                 let stsz = read_stsz(&mut b)?;
-                log!("{:?}", stsz);
+                debug!("{:?}", stsz);
                 track.stsz = Some(stsz);
             }
             BoxType::ChunkOffsetBox => {
                 let stco = read_stco(&mut b)?;
-                log!("{:?}", stco);
+                debug!("{:?}", stco);
                 track.stco = Some(stco);
             }
             BoxType::ChunkLargeOffsetBox => {
                 let co64 = read_co64(&mut b)?;
-                log!("{:?}", co64);
+                debug!("{:?}", co64);
                 track.stco = Some(co64);
             }
             BoxType::SyncSampleBox => {
                 let stss = read_stss(&mut b)?;
-                log!("{:?}", stss);
+                debug!("{:?}", stss);
                 track.stss = Some(stss);
             }
             BoxType::CompositionOffsetBox => {
                 let ctts = read_ctts(&mut b)?;
-                log!("{:?}", ctts);
+                debug!("{:?}", ctts);
                 track.ctts = Some(ctts);
             }
             _ => skip_box_content(&mut b)?,
         };
         check_parser_state!(b.content);
     }
     Ok(())
 }
@@ -1404,17 +1397,17 @@ fn find_descriptor(data: &[u8], esds: &m
             },
             DECODER_CONFIG_TAG => {
                 read_dc_descriptor(descriptor, esds)?;
             },
             DECODER_SPECIFIC_TAG => {
                 read_ds_descriptor(descriptor, esds)?;
             },
             _ => {
-                log!("Unsupported descriptor, tag {}", tag);
+                debug!("Unsupported descriptor, tag {}", tag);
             },
         }
 
         remains = &remains[end as usize .. remains.len()];
     }
 
     Ok(())
 }
@@ -1457,17 +1450,17 @@ fn read_ds_descriptor(data: &[u8], esds:
     if depend_on_core_order > 0 {
         bit_reader.skip(14)?;   // codeCoderDelay
     }
     bit_reader.skip(1)?;        // extensionFlag
 
     // When channel_counts is 0, we need to parse the program_config_element
     // to calculate the channel counts.
     if channel_counts == 0 {
-        log!("Parsing program_config_element for channel counts");
+        debug!("Parsing program_config_element for channel counts");
 
         bit_reader.skip(4)?;    // element_instance_tag
         bit_reader.skip(2)?;    // object_type
         bit_reader.skip(4)?;    // sampling_frequency_index
         let num_front_channel: u8 = ReadInto::read(bit_reader, 4)?;
         let num_side_channel: u8 = ReadInto::read(bit_reader, 4)?;
         let num_back_channel:u8 = ReadInto::read(bit_reader, 4)?;
         let num_lfe_channel: u8 = ReadInto::read(bit_reader, 2)?;
@@ -1684,16 +1677,38 @@ pub fn serialize_opus_header<W: byteorde
                     }
                 }
             }
         }
     };
     Ok(())
 }
 
+/// Parse `ALACSpecificBox`.
+fn read_alac<T: Read>(src: &mut BMFFBox<T>) -> Result<ALACSpecificBox> {
+    let (version, flags) = read_fullbox_extra(src)?;
+    if version != 0 {
+        return Err(Error::Unsupported("unknown alac (ALAC) version"));
+    }
+    if flags != 0 {
+        return Err(Error::InvalidData("no-zero alac (ALAC) flags"));
+    }
+
+    let length = src.bytes_left();
+    if length != 24 && length != 48 {
+        return Err(Error::InvalidData("ALACSpecificBox magic cookie is the wrong size"));
+    }
+    let data = read_buf(src, length)?;
+
+    Ok(ALACSpecificBox {
+        version: version,
+        data: data,
+    })
+}
+
 /// Parse a hdlr box.
 fn read_hdlr<T: Read>(src: &mut BMFFBox<T>) -> Result<HandlerBox> {
     let (_, _) = read_fullbox_extra(src)?;
 
     // Skip uninteresting fields.
     skip(src, 4)?;
 
     let handler_type = FourCC::from(be_u32(src)?);
@@ -1714,17 +1729,17 @@ fn read_video_sample_entry<T: Read>(src:
     let name = src.get_header().name;
     let codec_type = match name {
         BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => CodecType::H264,
         BoxType::MP4VideoSampleEntry => CodecType::MP4V,
         BoxType::VP8SampleEntry => CodecType::VP8,
         BoxType::VP9SampleEntry => CodecType::VP9,
         BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo,
         _ => {
-            log!("Unsupported video codec, box {:?} found", name);
+            debug!("Unsupported video codec, box {:?} found", name);
             CodecType::Unknown
         }
     };
 
     // Skip uninteresting fields.
     skip(src, 6)?;
 
     let data_reference_index = be_u16(src)?;
@@ -1748,17 +1763,17 @@ fn read_video_sample_entry<T: Read>(src:
                 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;
                 let avcc = read_buf(&mut b.content, avcc_size as usize)?;
-                log!("{:?} (avcc)", avcc);
+                debug!("{:?} (avcc)", avcc);
                 // 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 &&
                     name != BoxType::ProtectedVisualSampleEntry) ||
                     codec_specific.is_some() {
@@ -1776,21 +1791,21 @@ fn read_video_sample_entry<T: Read>(src:
                 let esds = read_buf(&mut b.content, esds_size as usize)?;
                 codec_specific = Some(VideoCodecSpecific::ESDSConfig(esds));
             }
             BoxType::ProtectionSchemeInformationBox => {
                 if name != BoxType::ProtectedVisualSampleEntry {
                     return Err(Error::InvalidData("malformed video sample entry"));
                 }
                 let sinf = read_sinf(&mut b)?;
-                log!("{:?} (sinf)", sinf);
+                debug!("{:?} (sinf)", sinf);
                 vec_push(&mut protection_info, sinf)?;
             }
             _ => {
-                log!("Unsupported video codec, box {:?} found", b.head.name);
+                debug!("Unsupported video codec, box {:?} found", b.head.name);
                 skip_box_content(&mut b)?;
             }
         }
         check_parser_state!(b.content);
     }
 
     Ok(codec_specific.map_or((CodecType::Unknown, SampleEntry::Unknown),
         |codec_specific| (codec_type, SampleEntry::Video(VideoSampleEntry {
@@ -1895,32 +1910,41 @@ fn read_audio_sample_entry<T: Read>(src:
                 if name != BoxType::OpusSampleEntry ||
                     codec_specific.is_some() {
                     return Err(Error::InvalidData("malformed audio sample entry"));
                 }
                 let dops = read_dops(&mut b)?;
                 codec_type = CodecType::Opus;
                 codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops));
             }
+            BoxType::ALACSpecificBox => {
+                if name != BoxType::ALACSpecificBox ||
+                    codec_specific.is_some() {
+                    return Err(Error::InvalidData("malformed audio sample entry"));
+                }
+                let alac = read_alac(&mut b)?;
+                codec_type = CodecType::ALAC;
+                codec_specific = Some(AudioCodecSpecific::ALACSpecificBox(alac));
+            }
             BoxType::QTWaveAtom => {
                 let qt_esds = read_qt_wave_atom(&mut b)?;
                 codec_type = qt_esds.audio_codec;
                 codec_specific = Some(AudioCodecSpecific::ES_Descriptor(qt_esds));
             }
             BoxType::ProtectionSchemeInformationBox => {
                 if name != BoxType::ProtectedAudioSampleEntry {
                     return Err(Error::InvalidData("malformed audio sample entry"));
                 }
                 let sinf = read_sinf(&mut b)?;
-                log!("{:?} (sinf)", sinf);
+                debug!("{:?} (sinf)", sinf);
                 codec_type = CodecType::EncryptedAudio;
                 vec_push(&mut protection_info, sinf)?;
             }
             _ => {
-                log!("Unsupported audio codec, box {:?} found", b.head.name);
+                debug!("Unsupported audio codec, box {:?} found", b.head.name);
                 skip_box_content(&mut b)?;
             }
         }
         check_parser_state!(b.content);
     }
 
     Ok(codec_specific.map_or((CodecType::Unknown, SampleEntry::Unknown),
         |codec_specific| (codec_type, SampleEntry::Audio(AudioSampleEntry {
@@ -1963,17 +1987,17 @@ fn read_stsd<T: Read>(src: &mut BMFFBox<
                     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 **");
+                debug!("** don't know how to handle multiple descriptions **");
             }
             vec_push(&mut descriptions, description)?;
             check_parser_state!(b.content);
             if descriptions.len() == description_count as usize {
                 break;
             }
         }
     }
--- a/media/mp4parse-rust/mp4parse/src/tests.rs
+++ b/media/mp4parse-rust/mp4parse/src/tests.rs
@@ -649,16 +649,38 @@ fn serialize_opus_header() {
             0x01, 0x06, 0x98, 0x00,
             0x80, 0xbb, 0x00, 0x00,
             0x00, 0x00, 0x01, 0x04, 0x02,
             0x00, 0x04, 0x01, 0x02, 0x03, 0x05,
     ]);
 }
 
 #[test]
+fn read_alac() {
+    let mut stream = make_box(BoxSize::Auto, b"alac", |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_fullbox(BoxSize::Auto, b"alac", 0, |s| {
+             s.append_bytes(&vec![0xfa; 24])
+         }).into_inner())
+    });
+    let mut iter = super::BoxIter::new(&mut stream);
+    let mut stream = iter.next_box().unwrap().unwrap();
+    let r = super::read_audio_sample_entry(&mut stream);
+    assert!(r.is_ok());
+}
+
+#[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)
--- a/media/mp4parse-rust/mp4parse/tests/public.rs
+++ b/media/mp4parse-rust/mp4parse/tests/public.rs
@@ -91,16 +91,20 @@ fn public_api() {
                         assert_eq!(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"
                     }
+                    mp4::AudioCodecSpecific::ALACSpecificBox(alac) => {
+                        assert!(alac.data.len() == 24 || alac.data.len() == 48);
+                        "ALAC"
+                    }
                     mp4::AudioCodecSpecific::MP3 => {
                         "MP3"
                     }
                     mp4::AudioCodecSpecific::LPCM => {
                         "LPCM"
                     }
                 }, "ES");
                 assert!(a.samplesize > 0);
--- a/media/mp4parse-rust/mp4parse_capi/Cargo.toml
+++ b/media/mp4parse-rust/mp4parse_capi/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "mp4parse_capi"
-version = "0.9.1"
+version = "0.10.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://docs.rs/mp4parse_capi/"
@@ -13,19 +13,18 @@ license = "MPL-2.0"
 
 repository = "https://github.com/mozilla/mp4parse-rust"
 
 # Avoid complaints about trying to package test files.
 exclude = [
   "*.mp4",
 ]
 
-[badges]
-travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
+build = false
 
 [dependencies]
-byteorder = "1.0.0"
+byteorder = "1.2.1"
+log = "0.4"
 
 # To enable fallible memory allocation, add 'features = ["mp4parse_fallible"]'
 # in mp4parse brace.
-mp4parse = {version = "0.9.1", path = "../mp4parse", features = ["mp4parse_fallible"]}
-num-traits = "0.1.37"
-
+mp4parse = {version = "0.10.0", path = "../mp4parse", features = ["mp4parse_fallible"]}
+num-traits = "0.2.0"
--- a/media/mp4parse-rust/mp4parse_capi/src/lib.rs
+++ b/media/mp4parse-rust/mp4parse_capi/src/lib.rs
@@ -93,16 +93,17 @@ pub enum Mp4parseCodec {
     Opus,
     Avc,
     Vp9,
     Mp3,
     Mp4v,
     Jpeg,   // for QT JPEG atom in video track
     Ac3,
     Ec3,
+    Alac,
 }
 
 impl Default for Mp4parseCodec {
     fn default() -> Self { Mp4parseCodec::Unknown }
 }
 
 #[repr(C)]
 #[derive(Default, Debug)]
@@ -292,22 +293,16 @@ pub unsafe extern fn mp4parse_new(io: *c
 
 /// Free an `Mp4parseParser*` allocated by `mp4parse_new()`.
 #[no_mangle]
 pub unsafe extern fn mp4parse_free(parser: *mut Mp4parseParser) {
     assert!(!parser.is_null());
     let _ = Box::from_raw(parser);
 }
 
-/// Enable `mp4_parser` log.
-#[no_mangle]
-pub unsafe extern fn mp4parse_log(enable: bool) {
-    mp4parse::set_debug_mode(enable);
-}
-
 /// Run the `Mp4parseParser*` allocated by `mp4parse_new()` until EOF or error.
 #[no_mangle]
 pub unsafe extern fn mp4parse_read(parser: *mut Mp4parseParser) -> Mp4parseStatus {
     // Validate arguments from C.
     if parser.is_null() || (*parser).poisoned() {
         return Mp4parseStatus::BadArg;
     }
 
@@ -424,16 +419,18 @@ pub unsafe extern fn mp4parse_get_track_
             AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC =>
                 Mp4parseCodec::Aac,
             AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 =>
                 Mp4parseCodec::Mp3,
             AudioCodecSpecific::ES_Descriptor(_) | AudioCodecSpecific::LPCM =>
                 Mp4parseCodec::Unknown,
             AudioCodecSpecific::MP3 =>
                 Mp4parseCodec::Mp3,
+            AudioCodecSpecific::ALACSpecificBox(_) =>
+                Mp4parseCodec::Alac,
         },
         Some(SampleEntry::Video(ref video)) => match video.codec_specific {
             VideoCodecSpecific::VPxConfig(_) =>
                 Mp4parseCodec::Vp9,
             VideoCodecSpecific::AVCConfig(_) =>
                 Mp4parseCodec::Avc,
             VideoCodecSpecific::ESDSConfig(_) => // MP4V (14496-2) video is unsupported.
                 Mp4parseCodec::Unknown,
@@ -560,16 +557,20 @@ pub unsafe extern fn mp4parse_get_track_
                             return Mp4parseStatus::Invalid;
                         }
                         (*info).extra_data.length = v.len() as u32;
                         (*info).extra_data.data = v.as_ptr();
                     }
                 }
             }
         }
+        AudioCodecSpecific::ALACSpecificBox(ref alac) => {
+            (*info).extra_data.length = alac.data.len() as u32;
+            (*info).extra_data.data = alac.data.as_ptr();
+        }
         AudioCodecSpecific::MP3 | AudioCodecSpecific::LPCM => (),
     }
 
     if let Some(p) = audio.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
         if let Some(ref tenc) = p.tenc {
             (*info).protected_data.is_encrypted = tenc.is_encrypted;
             (*info).protected_data.iv_size = tenc.iv_size;
             (*info).protected_data.kid.set_data(&(tenc.kid));
@@ -1137,24 +1138,16 @@ fn new_parser() {
     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_eq!(rv, Mp4parseStatus::BadArg);
         let rv = mp4parse_get_track_count(std::ptr::null(), &mut count);
         assert_eq!(rv, Mp4parseStatus::BadArg);
     }
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -595,17 +595,17 @@ dependencies = [
  "cubeb 0.3.2",
  "cubeb-backend 0.2.0",
  "cubeb-core 0.1.0",
  "cubeb-pulse 0.0.2",
  "encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_glue 0.1.0",
  "geckoservo 0.0.1",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "mp4parse_capi 0.9.1",
+ "mp4parse_capi 0.10.0",
  "netwerk_helper 0.0.1",
  "nserror 0.1.0",
  "nsstring 0.1.0",
  "prefs_parser 0.0.1",
  "rust_url_capi 0.0.1",
  "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "u2fhid 0.1.0",
  "webrender_bindings 0.1.0",
@@ -847,35 +847,37 @@ dependencies = [
 
 [[package]]
 name = "moz_cbor"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "mp4parse"
-version = "0.9.1"
+version = "0.10.0"
 dependencies = [
  "bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "mp4parse_fallible 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse-gtest"
 version = "0.1.0"
 
 [[package]]
 name = "mp4parse_capi"
-version = "0.9.1"
+version = "0.10.0"
 dependencies = [
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "mp4parse 0.9.1",
- "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mp4parse 0.10.0",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse_fallible"
 version = "0.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -938,16 +940,21 @@ dependencies = [
 ]
 
 [[package]]
 name = "num-traits"
 version = "0.1.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "num-traits"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "num_cpus"
 version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1767,16 +1774,17 @@ dependencies = [
 "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
 "checksum moz_cbor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20c82a57087fd5990d7122dbff1607c3b20c3d2958e9d9ad9765aab415e2c91c"
 "checksum mp4parse_fallible 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6626c2aef76eb8f984eef02e475883d3fe9112e114720446c5810fc5f045cd30"
 "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
 "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
 "checksum nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
 "checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
 "checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
+"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10"
 "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
 "checksum ordered-float 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da12c96037889ae0be29dd2bdd260e5a62a7df24e6466d5a15bb8131c1c200a8"
 "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
 "checksum parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "37f364e2ce5efa24c7d0b6646d5bb61145551a0112f107ffd7499f1a3e322fbd"
 "checksum parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6c677d78851950b3aec390e681a411f78cc250cba277d4f578758a377f727970"
 "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
 "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
 "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -593,17 +593,17 @@ dependencies = [
  "cubeb 0.3.2",
  "cubeb-backend 0.2.0",
  "cubeb-core 0.1.0",
  "cubeb-pulse 0.0.2",
  "encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_glue 0.1.0",
  "geckoservo 0.0.1",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "mp4parse_capi 0.9.1",
+ "mp4parse_capi 0.10.0",
  "netwerk_helper 0.0.1",
  "nserror 0.1.0",
  "nsstring 0.1.0",
  "prefs_parser 0.0.1",
  "rust_url_capi 0.0.1",
  "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "u2fhid 0.1.0",
  "webrender_bindings 0.1.0",
@@ -845,31 +845,33 @@ dependencies = [
 
 [[package]]
 name = "moz_cbor"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "mp4parse"
-version = "0.9.1"
+version = "0.10.0"
 dependencies = [
  "bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "mp4parse_fallible 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse_capi"
-version = "0.9.1"
+version = "0.10.0"
 dependencies = [
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "mp4parse 0.9.1",
- "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mp4parse 0.10.0",
+ "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse_fallible"
 version = "0.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -925,16 +927,21 @@ dependencies = [
 ]
 
 [[package]]
 name = "num-traits"
 version = "0.1.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "num-traits"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "num_cpus"
 version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1769,16 +1776,17 @@ dependencies = [
 "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
 "checksum moz_cbor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20c82a57087fd5990d7122dbff1607c3b20c3d2958e9d9ad9765aab415e2c91c"
 "checksum mp4parse_fallible 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6626c2aef76eb8f984eef02e475883d3fe9112e114720446c5810fc5f045cd30"
 "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
 "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
 "checksum nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
 "checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
 "checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
+"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10"
 "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d"
 "checksum ordered-float 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da12c96037889ae0be29dd2bdd260e5a62a7df24e6466d5a15bb8131c1c200a8"
 "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
 "checksum parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "37f364e2ce5efa24c7d0b6646d5bb61145551a0112f107ffd7499f1a3e322fbd"
 "checksum parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6c677d78851950b3aec390e681a411f78cc250cba277d4f578758a377f727970"
 "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
 "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
 "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"