diff --git a/CHANGES.md b/CHANGES.md index 28b00780..6077b34c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,8 @@ ## develop +- [ADD] `Mp4MediaStream` の対応コーデックに H.265 を追加する + - @sile - [ADD] `Mp4MediaStream` の対応コーデックに AV1 を追加する - @sile - [ADD] `Mp4MediaStream` の対応コーデックに VP9 を追加する diff --git a/packages/mp4-media-stream/README.md b/packages/mp4-media-stream/README.md index 681b567e..fab96df7 100644 --- a/packages/mp4-media-stream/README.md +++ b/packages/mp4-media-stream/README.md @@ -32,6 +32,7 @@ video.srcObject = stream - 映像: - H.264 + - H.265 - VP8 - VP9 - AV1 diff --git a/packages/mp4-media-stream/wasm/src/mp4.rs b/packages/mp4-media-stream/wasm/src/mp4.rs index 9867d3dd..6b601ef2 100644 --- a/packages/mp4-media-stream/wasm/src/mp4.rs +++ b/packages/mp4-media-stream/wasm/src/mp4.rs @@ -5,8 +5,8 @@ use serde::Serialize; use shiguredo_mp4::{ aux::SampleTableAccessor, boxes::{ - Av01Box, Avc1Box, FtypBox, HdlrBox, IgnoredBox, MoovBox, Mp4aBox, OpusBox, SampleEntry, - StblBox, TrakBox, Vp08Box, Vp09Box, + Av01Box, Avc1Box, FtypBox, HdlrBox, Hev1Box, IgnoredBox, MoovBox, Mp4aBox, OpusBox, + SampleEntry, StblBox, TrakBox, Vp08Box, Vp09Box, }, BaseBox, Decode, Either, Encode, }; @@ -39,6 +39,55 @@ impl VideoDecoderConfig { } } + pub fn from_hev1_box(b: &Hev1Box) -> Self { + let mut description = Vec::new(); + b.hvcc_box.encode(&mut description).expect("unreachable"); + description.drain(..8); // ボックスヘッダ部分を取り除く + + let mut constraints = b + .hvcc_box + .general_constraint_indicator_flags + .get() + .to_be_bytes() + .to_vec(); + while constraints.len() > 1 && constraints.last() == Some(&0) { + constraints.pop(); + } + + Self { + // ISO / IEC 14496-15 E.3 + codec: format!( + "hev1.{}.{:X}.{}.{}", + match b.hvcc_box.general_profile_space.get() { + 1 => format!("A{}", b.hvcc_box.general_profile_idc.get()), + 2 => format!("B{}", b.hvcc_box.general_profile_idc.get()), + 3 => format!("C{}", b.hvcc_box.general_profile_idc.get()), + v => format!("{v}"), + }, + b.hvcc_box + .general_profile_compatibility_flags + .reverse_bits(), + format!( + "{}{}", + if b.hvcc_box.general_tier_flag.get() == 0 { + 'L' + } else { + 'H' + }, + b.hvcc_box.general_level_idc + ), + constraints + .into_iter() + .map(|b| format!("{:02X}", b)) + .collect::>() + .join(".") + ), + description, + coded_width: b.visual.width, + coded_height: b.visual.height, + } + } + pub fn from_vp08_box(b: &Vp08Box) -> Self { Self { codec: "vp8".to_owned(), @@ -163,6 +212,7 @@ impl Track { match sample_table.stbl_box().stsd_box.entries.first() { Some(SampleEntry::Avc1(_)) => (), + Some(SampleEntry::Hev1(_)) => (), Some(SampleEntry::Vp08(_)) => (), Some(SampleEntry::Vp09(_)) => (), Some(SampleEntry::Av01(_)) => (), @@ -256,6 +306,9 @@ impl Mp4 { SampleEntry::Avc1(b) => { video_configs.push(VideoDecoderConfig::from_avc1_box(b)); } + SampleEntry::Hev1(b) => { + video_configs.push(VideoDecoderConfig::from_hev1_box(b)); + } SampleEntry::Vp08(b) => { video_configs.push(VideoDecoderConfig::from_vp08_box(b)); } diff --git a/packages/mp4-media-stream/wasm/src/player.rs b/packages/mp4-media-stream/wasm/src/player.rs index 10728463..fbf33c17 100644 --- a/packages/mp4-media-stream/wasm/src/player.rs +++ b/packages/mp4-media-stream/wasm/src/player.rs @@ -180,6 +180,10 @@ impl TrackPlayer { let config = VideoDecoderConfig::from_avc1_box(b); WasmApi::create_video_decoder(self.player_id, config).await } + SampleEntry::Hev1(b) => { + let config = VideoDecoderConfig::from_hev1_box(b); + WasmApi::create_video_decoder(self.player_id, config).await + } SampleEntry::Vp08(b) => { let config = VideoDecoderConfig::from_vp08_box(b); WasmApi::create_video_decoder(self.player_id, config).await