forked from mirrors/gecko-dev
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:
parent
019b59f8b5
commit
2868cd8be4
11 changed files with 151 additions and 3 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
media/mp4parse-rust/mp4parse/tests/tiny_av1.mp4
Normal file
BIN
media/mp4parse-rust/mp4parse/tests/tiny_av1.mp4
Normal file
Binary file not shown.
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue