Bug 1417050: Add support for AV1 in MP4. r=jya

Differential Revision: https://phabricator.services.mozilla.com/D3365

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Thomas Daede 2018-08-20 23:56:39 +00:00
parent 019b59f8b5
commit 2868cd8be4
11 changed files with 151 additions and 3 deletions

View file

@ -718,7 +718,8 @@ IsVP9CodecString(const nsAString& aCodec)
bool
IsAV1CodecString(const nsAString& aCodec)
{
return aCodec.EqualsLiteral("av1"); // AV1
return aCodec.EqualsLiteral("av1") ||
StartsWith(NS_ConvertUTF16toUTF8(aCodec), "av01");
}
UniquePtr<TrackInfo>

View file

@ -126,6 +126,8 @@ MP4VideoInfo::Update(const Mp4parseTrackInfo* track,
mMimeType = NS_LITERAL_CSTRING("video/avc");
} else if (track->codec == MP4PARSE_CODEC_VP9) {
mMimeType = NS_LITERAL_CSTRING("video/vp9");
} else if (track->codec == MP4PARSE_CODEC_AV1) {
mMimeType = NS_LITERAL_CSTRING("video/av1");
} else if (track->codec == MP4PARSE_CODEC_MP4V) {
mMimeType = NS_LITERAL_CSTRING("video/mp4v-es");
}

View file

@ -117,6 +117,14 @@ MP4Decoder::GetTracksInfo(const MediaContainerType& aType, MediaResult& aError)
tracks.AppendElement(std::move(trackInfo));
continue;
}
#ifdef MOZ_AV1
if (IsAV1CodecString(codec)) {
tracks.AppendElement(
CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
NS_LITERAL_CSTRING("video/") + NS_ConvertUTF16toUTF8(codec), aType));
continue;
}
#endif
if (isVideo && IsWhitelistedH264Codec(codec)) {
auto trackInfo =
CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(

View file

@ -255,6 +255,7 @@ MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
case MP4PARSE_CODEC_ALAC: codec_string = "alac"; break;
case MP4PARSE_CODEC_AVC: codec_string = "h.264"; break;
case MP4PARSE_CODEC_VP9: codec_string = "vp9"; break;
case MP4PARSE_CODEC_AV1: codec_string = "av1"; break;
case MP4PARSE_CODEC_MP3: codec_string = "mp3"; break;
case MP4PARSE_CODEC_MP4V: codec_string = "mp4v"; break;
case MP4PARSE_CODEC_JPEG: codec_string = "jpeg"; break;

View file

@ -22,6 +22,7 @@ typedef enum {
MP4PARSE_CODEC_OPUS,
MP4PARSE_CODEC_AVC,
MP4PARSE_CODEC_VP9,
MP4PARSE_CODEC_AV1,
MP4PARSE_CODEC_MP3,
MP4PARSE_CODEC_MP4V,
MP4PARSE_CODEC_JPEG,

View file

@ -121,6 +121,8 @@ box_database!(
VP8SampleEntry 0x76703038, // "vp08"
VP9SampleEntry 0x76703039, // "vp09"
VPCodecConfigurationBox 0x76706343, // "vpcC"
AV1SampleEntry 0x61763031, // "av01"
AV1CodecConfigurationBox 0x61763143, // "av1C"
FLACSampleEntry 0x664c6143, // "fLaC"
FLACSpecificBox 0x64664c61, // "dfLa"
OpusSampleEntry 0x4f707573, // "Opus"

View file

@ -324,6 +324,7 @@ pub struct AudioSampleEntry {
pub enum VideoCodecSpecific {
AVCConfig(Vec<u8>),
VPxConfig(VPxConfigBox),
AV1Config(AV1ConfigBox),
ESDSConfig(Vec<u8>),
}
@ -350,6 +351,21 @@ pub struct VPxConfigBox {
pub codec_init: Vec<u8>, // Empty for vp8/vp9.
}
#[derive(Debug, Clone)]
pub struct AV1ConfigBox {
pub profile: u8,
pub level: u8,
pub tier: u8,
pub bit_depth: u8,
pub monochrome: bool,
pub chroma_subsampling_x: u8,
pub chroma_subsampling_y: u8,
pub chroma_sample_position: u8,
pub initial_presentation_delay_present: bool,
pub initial_presentation_delay_minus_one: u8,
pub config_obus: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct FLACMetadataBlock {
pub block_type: u8,
@ -456,7 +472,7 @@ pub enum CodecType {
Opus,
H264, // 14496-10
MP4V, // 14496-2
VP10,
AV1,
VP9,
VP8,
EncryptedVideo,
@ -1369,6 +1385,55 @@ fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
})
}
fn read_av1c<T: Read>(src: &mut BMFFBox<T>) -> Result<AV1ConfigBox> {
let marker_byte = src.read_u8()?;
if !(marker_byte & 0x80 == 0x80) {
return Err(Error::Unsupported("missing av1C marker bit"));
}
if !(marker_byte & 0x7f == 0x01) {
return Err(Error::Unsupported("missing av1C marker bit"));
}
let profile_byte = src.read_u8()?;
let profile = (profile_byte & 0xe0) >> 5;
let level = profile_byte & 0x1f;
let flags_byte = src.read_u8()?;
let tier = (flags_byte & 0x80) >> 7;
let bit_depth = match flags_byte & 0x60 {
0x60 => 12,
0x40 => 10,
_ => 8
};
let monochrome = flags_byte & 0x10 == 0x10;
let chroma_subsampling_x = (flags_byte & 0x08) >> 3;
let chroma_subsampling_y = (flags_byte & 0x04) >> 2;
let chroma_sample_position = flags_byte & 0x03;
let delay_byte = src.read_u8()?;
let initial_presentation_delay_present = (delay_byte & 0x10) == 0x10;
let initial_presentation_delay_minus_one =
if initial_presentation_delay_present {
delay_byte & 0x0f
} else {
0
};
let config_obus_size = src.bytes_left();
let config_obus = read_buf(src, config_obus_size as usize)?;
Ok(AV1ConfigBox {
profile,
level,
tier,
bit_depth,
monochrome,
chroma_subsampling_x,
chroma_subsampling_y,
chroma_sample_position,
initial_presentation_delay_present,
initial_presentation_delay_minus_one,
config_obus
})
}
fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock> {
let temp = src.read_u8()?;
let block_type = temp & 0x7f;
@ -1761,6 +1826,7 @@ fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<(CodecType,
BoxType::MP4VideoSampleEntry => CodecType::MP4V,
BoxType::VP8SampleEntry => CodecType::VP8,
BoxType::VP9SampleEntry => CodecType::VP9,
BoxType::AV1SampleEntry => CodecType::AV1,
BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo,
_ => {
debug!("Unsupported video codec, box {:?} found", name);
@ -1811,6 +1877,13 @@ fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<(CodecType,
let vpcc = read_vpcc(&mut b)?;
codec_specific = Some(VideoCodecSpecific::VPxConfig(vpcc));
}
BoxType::AV1CodecConfigurationBox => {
if name != BoxType::AV1SampleEntry {
return Err(Error::InvalidData("malformed video sample entry"));
}
let av1c = read_av1c(&mut b)?;
codec_specific = Some(VideoCodecSpecific::AV1Config(av1c));
}
BoxType::ESDBox => {
if name != BoxType::MP4VideoSampleEntry || codec_specific.is_some() {
return Err(Error::InvalidData("malformed video sample entry"));

View file

@ -12,6 +12,7 @@ use std::fs::File;
static MINI_MP4: &'static str = "tests/minimal.mp4";
static AUDIO_EME_MP4: &'static str = "tests/bipbop-cenc-audioinit.mp4";
static VIDEO_EME_MP4: &'static str = "tests/bipbop_480wp_1001kbps-cenc-video-key1-init.mp4";
static VIDEO_AV1_MP4: &'static str = "tests/tiny_av1.mp4";
// Taken from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53
#[test]
@ -60,6 +61,9 @@ fn public_api() {
assert!(!mp4v.is_empty());
"MP4V"
}
mp4::VideoCodecSpecific::AV1Config(_av1c) => {
"AV1"
}
}, "AVC");
}
Some(mp4::SampleEntry::Audio(a)) => {
@ -216,3 +220,56 @@ fn public_video_cenc() {
assert_eq!(pssh.box_content, pssh_box);
}
}
#[test]
fn public_video_av1() {
let mut fd = File::open(VIDEO_AV1_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");
for track in context.tracks {
assert_eq!(track.codec_type, mp4::CodecType::AV1);
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(12288, 0)));
assert_eq!(v.width, 64);
assert_eq!(v.height, 64);
// track.tkhd part
let tkhd = track.tkhd.unwrap();
assert_eq!(tkhd.disabled, false);
assert_eq!(tkhd.duration, 42);
assert_eq!(tkhd.width, 4194304);
assert_eq!(tkhd.height, 4194304);
match v.codec_specific {
mp4::VideoCodecSpecific::AV1Config(av1c) => {
// TODO: test av1c fields once ffmpeg is updated
assert_eq!(av1c.profile, 0);
assert_eq!(av1c.level, 0);
assert_eq!(av1c.tier, 0);
assert_eq!(av1c.bit_depth, 8);
assert_eq!(av1c.monochrome, false);
assert_eq!(av1c.chroma_subsampling_x, 1);
assert_eq!(av1c.chroma_subsampling_y, 1);
assert_eq!(av1c.chroma_sample_position, 0);
assert_eq!(av1c.initial_presentation_delay_present, false);
assert_eq!(av1c.initial_presentation_delay_minus_one, 0);
},
_ => assert!(false, "Invalid test condition"),
}
},
_ => {
assert!(false, "Invalid test condition");
}
}
}
}

Binary file not shown.

View file

@ -94,6 +94,7 @@ pub enum Mp4parseCodec {
Opus,
Avc,
Vp9,
Av1,
Mp3,
Mp4v,
Jpeg, // for QT JPEG atom in video track
@ -432,6 +433,8 @@ pub unsafe extern fn mp4parse_get_track_info(parser: *mut Mp4parseParser, track_
Some(SampleEntry::Video(ref video)) => match video.codec_specific {
VideoCodecSpecific::VPxConfig(_) =>
Mp4parseCodec::Vp9,
VideoCodecSpecific::AV1Config(_) =>
Mp4parseCodec::Av1,
VideoCodecSpecific::AVCConfig(_) =>
Mp4parseCodec::Avc,
VideoCodecSpecific::ESDSConfig(_) => // MP4V (14496-2) video is unsupported.

View file

@ -4,7 +4,7 @@
set -e
# Default version.
VER="643f48e137592e6318f6c780448374324908da31"
VER="681ce17e3a600e31844a9abbad6d46e413b2beba"
# Accept version or commit from the command line.
if test -n "$1"; then