Skip to content

Commit c842e75

Browse files
committed
Support HEVC.
1 parent eb4a65b commit c842e75

File tree

5 files changed

+201
-0
lines changed

5 files changed

+201
-0
lines changed

mp4parse/src/boxes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ box_database!(
146146
AVCConfigurationBox 0x6176_6343, // "avcC"
147147
H263SampleEntry 0x7332_3633, // "s263"
148148
H263SpecificBox 0x6432_3633, // "d263"
149+
HEVCSampleEntry 0x6865_7631, // "hev1"
150+
HEVCSampleEntry2 0x6876_6331, // "hvc1"
151+
HEVCConfigurationBox 0x6876_6343, // "hvcC"
149152
MP4AudioSampleEntry 0x6d70_3461, // "mp4a"
150153
MP4VideoSampleEntry 0x6d70_3476, // "mp4v"
151154
#[cfg(feature = "3gpp")]

mp4parse/src/lib.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,7 @@ pub enum VideoCodecSpecific {
11611161
AV1Config(AV1ConfigBox),
11621162
ESDSConfig(TryVec<u8>),
11631163
H263Config(TryVec<u8>),
1164+
HEVCConfig(HEVCConfigBox),
11641165
}
11651166

11661167
#[derive(Debug)]
@@ -1233,6 +1234,44 @@ impl AV1ConfigBox {
12331234
}
12341235
}
12351236

1237+
// Spec 8.3.2.1.2 HEVCDecoderConfigurationRecord
1238+
#[derive(Debug)]
1239+
pub struct NALUnit {
1240+
pub nal_unit_length: u16,
1241+
pub nal_unit_content: TryVec<u8>,
1242+
}
1243+
1244+
#[derive(Debug)]
1245+
pub struct HEVCArrayInfo {
1246+
pub array_completeness: u8,
1247+
pub nal_unit_type: u8,
1248+
pub num_nalus: u16,
1249+
pub nal_units: TryVec<NALUnit>,
1250+
}
1251+
1252+
#[derive(Debug)]
1253+
pub struct HEVCConfigBox {
1254+
pub configuration_version: u8,
1255+
pub general_profile_space: u8,
1256+
pub general_tier_flag: u8,
1257+
pub general_profile_idc: u8,
1258+
pub general_profile_compatibility_flags: u32,
1259+
pub general_constraint_indicator_flags: u64,
1260+
pub general_level_idc: u8,
1261+
pub min_spatial_segmentation_idc: u16,
1262+
pub parallelism_type: u8,
1263+
pub chroma_format_idc: u8,
1264+
pub bit_depth_luma_minus8: u8,
1265+
pub bit_depth_chroma_minus8: u8,
1266+
pub avg_frame_rate: u16,
1267+
pub constant_frame_rate: u8,
1268+
pub num_temporal_layers: u8,
1269+
pub temporal_id_nested: u8,
1270+
pub length_size_minus_one: u8,
1271+
pub num_of_arrays: u8,
1272+
pub array_infos: TryVec<HEVCArrayInfo>,
1273+
}
1274+
12361275
#[derive(Debug)]
12371276
pub struct FLACMetadataBlock {
12381277
pub block_type: u8,
@@ -2060,6 +2099,7 @@ pub enum CodecType {
20602099
LPCM, // QT
20612100
ALAC,
20622101
H263,
2102+
HEVC,
20632103
#[cfg(feature = "3gpp")]
20642104
AMRNB,
20652105
#[cfg(feature = "3gpp")]
@@ -4927,6 +4967,102 @@ fn read_av1c<T: Read>(src: &mut BMFFBox<T>) -> Result<AV1ConfigBox> {
49274967
})
49284968
}
49294969

4970+
fn read_hvcc<T: Read>(src: &mut BMFFBox<T>) -> Result<HEVCConfigBox> {
4971+
let configuration_version = src.read_u8()?;
4972+
let (general_profile_space, general_tier_flag, general_profile_idc) = {
4973+
let byte = src.read_u8()?;
4974+
((byte >> 6) & 0x03, (byte >> 5) & 0x01, byte & 0x1F)
4975+
};
4976+
let general_profile_compatibility_flags = src.read_u32::<byteorder::BigEndian>()?;
4977+
let general_constraint_indicator_flags = {
4978+
let flag_high = src.read_u32::<byteorder::BigEndian>()?;
4979+
let flag_low: u16 = src.read_u16::<byteorder::BigEndian>()?;
4980+
(flag_high as u64) << 16 | flag_low as u64
4981+
};
4982+
let general_level_idc = src.read_u8()?;
4983+
let (min_spatial_segmentation_idc, parallelism_type, chroma_format_idc) = {
4984+
let byte = src.read_u32::<byteorder::BigEndian>()?;
4985+
(
4986+
((byte >> 16) & 0x0FFF) as u16,
4987+
((byte >> 8) & 0x03) as u8,
4988+
(byte & 0x03) as u8,
4989+
)
4990+
};
4991+
let (bit_depth_luma_minus8, bit_depth_chroma_minus8) = {
4992+
let byte = src.read_u16::<byteorder::BigEndian>()?;
4993+
(((byte >> 8) & 0x07) as u8, (byte & 0x07) as u8)
4994+
};
4995+
let avg_frame_rate = src.read_u16::<byteorder::BigEndian>()?;
4996+
let (constant_frame_rate, num_temporal_layers, temporal_id_nested, length_size_minus_one) = {
4997+
let byte = src.read_u8()?;
4998+
(
4999+
(byte >> 6) & 0x03,
5000+
(byte >> 3) & 0x07,
5001+
(byte >> 2) & 0x01,
5002+
byte & 0x03,
5003+
)
5004+
};
5005+
let num_of_arrays = src.read_u8()?;
5006+
let mut array_infos = TryVec::new();
5007+
for _i in 0..num_of_arrays {
5008+
let array_info = read_hevc_arry_info(src)?;
5009+
array_infos.push(array_info)?;
5010+
}
5011+
Ok(HEVCConfigBox {
5012+
configuration_version,
5013+
general_profile_space,
5014+
general_tier_flag,
5015+
general_profile_idc,
5016+
general_profile_compatibility_flags,
5017+
general_constraint_indicator_flags,
5018+
general_level_idc,
5019+
min_spatial_segmentation_idc,
5020+
parallelism_type,
5021+
chroma_format_idc,
5022+
bit_depth_luma_minus8,
5023+
bit_depth_chroma_minus8,
5024+
avg_frame_rate,
5025+
constant_frame_rate,
5026+
num_temporal_layers,
5027+
temporal_id_nested,
5028+
length_size_minus_one,
5029+
num_of_arrays,
5030+
array_infos,
5031+
})
5032+
}
5033+
5034+
fn read_hevc_arry_info<T: Read>(src: &mut BMFFBox<T>) -> Result<HEVCArrayInfo> {
5035+
let (array_completeness, nal_unit_type) = {
5036+
let byte = src.read_u8()?;
5037+
((byte >> 7) & 0x01, byte & 0x3F)
5038+
};
5039+
let num_nalus = src.read_u16::<byteorder::BigEndian>()?;
5040+
let mut nal_units = TryVec::new();
5041+
for _i in 0..num_nalus {
5042+
let nal = read_nal_unit(src)?;
5043+
nal_units.push(nal)?;
5044+
}
5045+
Ok(HEVCArrayInfo {
5046+
array_completeness,
5047+
nal_unit_type,
5048+
num_nalus,
5049+
nal_units,
5050+
})
5051+
}
5052+
5053+
fn read_nal_unit<T: Read>(src: &mut BMFFBox<T>) -> Result<NALUnit> {
5054+
let nal_unit_length = src.read_u16::<byteorder::BigEndian>()?;
5055+
let mut nal_unit_content = TryVec::new();
5056+
for _i in 0..nal_unit_length {
5057+
let byte = src.read_u8()?;
5058+
nal_unit_content.push(byte)?;
5059+
}
5060+
Ok(NALUnit {
5061+
nal_unit_length,
5062+
nal_unit_content,
5063+
})
5064+
}
5065+
49305066
fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock> {
49315067
let temp = src.read_u8()?;
49325068
let block_type = temp & 0x7f;
@@ -5456,6 +5592,8 @@ fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleEntry>
54565592
BoxType::AV1SampleEntry => CodecType::AV1,
54575593
BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo,
54585594
BoxType::H263SampleEntry => CodecType::H263,
5595+
BoxType::HEVCSampleEntry => CodecType::HEVC,
5596+
BoxType::HEVCSampleEntry2 => CodecType::HEVC,
54595597
_ => {
54605598
debug!("Unsupported video codec, box {:?} found", name);
54615599
CodecType::Unknown
@@ -5567,6 +5705,13 @@ fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleEntry>
55675705
debug!("{:?} (sinf)", sinf);
55685706
protection_info.push(sinf)?;
55695707
}
5708+
BoxType::HEVCConfigurationBox => {
5709+
if name != BoxType::HEVCSampleEntry && name != BoxType::HEVCSampleEntry2 {
5710+
return Status::StsdBadVideoSampleEntry.into();
5711+
}
5712+
let hvcc = read_hvcc(&mut b)?;
5713+
codec_specific = Some(VideoCodecSpecific::HEVCConfig(hvcc));
5714+
}
55705715
_ => {
55715716
debug!("Unsupported video codec, box {:?} found", b.head.name);
55725717
skip_box_content(&mut b)?;

mp4parse/tests/hevc_white_frame.mp4

3.28 KB
Binary file not shown.

mp4parse/tests/public.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ static AVIF_CORRUPT_IMAGES_DIR: &str = "tests/corrupt";
215215
// The 1 frame h263 3gp file can be generated by ffmpeg with command
216216
// "ffmpeg -i [input file] -f 3gp -vcodec h263 -vf scale=176x144 -frames:v 1 -an output.3gp"
217217
static VIDEO_H263_3GP: &str = "tests/bbb_sunflower_QCIF_30fps_h263_noaudio_1f.3gp";
218+
// The 1 frame hevc mp4 file generated by ffmpeg with command
219+
// "ffmpeg -f lavfi -i color=c=white:s=640x480 -c:v libx265 -frames:v 1 -pix_fmt yuv420p hevc_white_frame.mp4"
220+
static VIDEO_HEVC_MP4: &str = "tests/hevc_white_frame.mp4";
218221
// The 1 frame AMR-NB 3gp file can be generated by ffmpeg with command
219222
// "ffmpeg -i [input file] -f 3gp -acodec amr_nb -ar 8000 -ac 1 -frames:a 1 -vn output.3gp"
220223
#[cfg(feature = "3gpp")]
@@ -286,6 +289,9 @@ fn public_api() {
286289
mp4::VideoCodecSpecific::H263Config(ref _h263) => {
287290
"H263"
288291
}
292+
mp4::VideoCodecSpecific::HEVCConfig(ref _hevc) => {
293+
"HEVC"
294+
}
289295
},
290296
"AVC"
291297
);
@@ -1435,6 +1441,51 @@ fn public_video_h263() {
14351441
}
14361442
}
14371443

1444+
#[test]
1445+
fn public_video_hevc() {
1446+
let mut fd = File::open(VIDEO_HEVC_MP4).expect("Unknown file");
1447+
let mut buf = Vec::new();
1448+
fd.read_to_end(&mut buf).expect("File error");
1449+
1450+
let mut c = Cursor::new(&buf);
1451+
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
1452+
for track in context.tracks {
1453+
let stsd = track.stsd.expect("expected an stsd");
1454+
let v = match stsd.descriptions.first().expect("expected a SampleEntry") {
1455+
mp4::SampleEntry::Video(ref v) => v,
1456+
_ => panic!("expected a VideoSampleEntry"),
1457+
};
1458+
assert_eq!(v.codec_type, mp4::CodecType::HEVC);
1459+
assert_eq!(v.width, 640);
1460+
assert_eq!(v.height, 480);
1461+
let _codec_specific = match &v.codec_specific {
1462+
mp4::VideoCodecSpecific::HEVCConfig(config) => {
1463+
assert_eq!(config.configuration_version, 1);
1464+
assert_eq!(config.general_profile_space, 0);
1465+
assert_eq!(config.general_tier_flag, 0);
1466+
assert_eq!(config.general_profile_idc, 1);
1467+
assert_eq!(config.general_profile_compatibility_flags, 1610612736);
1468+
assert_eq!(config.general_constraint_indicator_flags, 158329674399744);
1469+
assert_eq!(config.general_level_idc, 90);
1470+
assert_eq!(config.min_spatial_segmentation_idc, 0);
1471+
assert_eq!(config.parallelism_type, 0);
1472+
assert_eq!(config.chroma_format_idc, 1);
1473+
assert_eq!(config.bit_depth_luma_minus8, 0);
1474+
assert_eq!(config.bit_depth_chroma_minus8, 0);
1475+
assert_eq!(config.avg_frame_rate, 0);
1476+
assert_eq!(config.constant_frame_rate, 0);
1477+
assert_eq!(config.num_temporal_layers, 1);
1478+
assert_eq!(config.temporal_id_nested, 1);
1479+
assert_eq!(config.length_size_minus_one, 3);
1480+
assert_eq!(config.num_of_arrays, 4);
1481+
}
1482+
_ => {
1483+
panic!("expected a HEVCConfig",);
1484+
}
1485+
};
1486+
}
1487+
}
1488+
14381489
#[test]
14391490
#[cfg(feature = "3gpp")]
14401491
fn public_audio_amrnb() {

mp4parse_capi/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ pub enum Mp4parseCodec {
100100
Ec3,
101101
Alac,
102102
H263,
103+
HEVC,
103104
#[cfg(feature = "3gpp")]
104105
AMRNB,
105106
#[cfg(feature = "3gpp")]
@@ -962,6 +963,7 @@ fn mp4parse_get_track_video_info_safe(
962963
VideoCodecSpecific::AV1Config(_) => Mp4parseCodec::Av1,
963964
VideoCodecSpecific::AVCConfig(_) => Mp4parseCodec::Avc,
964965
VideoCodecSpecific::H263Config(_) => Mp4parseCodec::H263,
966+
VideoCodecSpecific::HEVCConfig(_) => Mp4parseCodec::HEVC,
965967
#[cfg(feature = "mp4v")]
966968
VideoCodecSpecific::ESDSConfig(_) => Mp4parseCodec::Mp4v,
967969
#[cfg(not(feature = "mp4v"))]

0 commit comments

Comments
 (0)