Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
6b3b026
temp changes
philipch07 Feb 20, 2025
d68bae8
updated test format and added doc comments to sps
philipch07 Feb 20, 2025
d6d3c39
remove old test file
philipch07 Feb 20, 2025
0d4f22d
added docs
philipch07 Feb 20, 2025
6e56dce
fix fmt
philipch07 Feb 21, 2025
aae01d0
improve test cov
philipch07 Feb 21, 2025
af39dab
incomplete temp h265 changes
philipch07 Feb 24, 2025
e3e8785
wip -- lennart will pick up later
philipch07 Mar 30, 2025
aca3cde
feat(h265): reimplementation
lennartkloock Apr 12, 2025
0bfe307
test(h265): remove broken tests
lennartkloock Apr 12, 2025
7e6fd42
test(h265): fix tests
lennartkloock Apr 12, 2025
7000f76
chore(h265): add ImHex hexpat file
lennartkloock Apr 12, 2025
b9775a4
chore(h265): add ImHex example
lennartkloock Apr 12, 2025
d89d9db
test(h265): remove print fn
lennartkloock Apr 12, 2025
411329d
fix(h265): remove dbg!
lennartkloock Apr 12, 2025
dd3bb87
fix(h256): some fixes and new tests
lennartkloock Apr 12, 2025
76a34a7
test(h265): more samples
lennartkloock Apr 12, 2025
c5d1ae2
docs(h265): update ImHex files
lennartkloock Apr 12, 2025
d7e5fbc
docs(h265): add changelog file
lennartkloock Apr 12, 2025
67c2b9c
fix(h265): lints
lennartkloock Apr 12, 2025
f879b34
docs(h265): replace ImHex project with actual sample file
lennartkloock Apr 12, 2025
ea9230a
feat(h265): improved semantics compliance
lennartkloock Apr 15, 2025
0054be7
fix(h265): hrd parameter parsing and broken tests
lennartkloock Apr 15, 2025
3fd62cb
fix(h265): fix hexpat
lennartkloock Apr 15, 2025
1704788
fix(h265): use NonZero where applicable
lennartkloock Apr 15, 2025
9baff0a
fix(h265): range checks and st_ref_pic_set
lennartkloock Apr 15, 2025
0655e71
fix(h265): lints
lennartkloock Apr 15, 2025
e5f197c
fix(h265): remove unused test
lennartkloock Apr 15, 2025
c5b57f9
test(h265): sps functions
lennartkloock Apr 16, 2025
02d7bd2
fix(h265): st_ref_pic_set range checks
lennartkloock Apr 16, 2025
ce2a821
test(h265): vui parameters
lennartkloock Apr 16, 2025
bdc1088
docs(h265): add docs
lennartkloock Apr 18, 2025
771c54c
docs(h265): improve config docs
lennartkloock Apr 18, 2025
cd971f4
fix(h265): address some comments
lennartkloock Apr 19, 2025
07c38e8
refactor(h265): clean up types
lennartkloock Apr 19, 2025
81c7eb3
fix(h265): docs
lennartkloock Apr 19, 2025
bb04cb9
test(h265): update
lennartkloock Apr 19, 2025
7ff262a
fix(h265): rbsp_trailing_bits
lennartkloock Apr 19, 2025
4845931
fix(h265): typo
lennartkloock Apr 19, 2025
52b59a3
docs: update change log
lennartkloock Apr 19, 2025
03e6013
docs(h265): root docs
lennartkloock Apr 19, 2025
d913302
test(h265): add hrd parameter test
lennartkloock Apr 21, 2025
e347caf
test(h265): scc extension
lennartkloock Apr 21, 2025
75d7cd7
fix(h265): st_ref_pic_set
lennartkloock Apr 23, 2025
aaef9ee
fix(h265): lints
lennartkloock Apr 23, 2025
15815db
docs(h265): remove other crates section
lennartkloock Apr 23, 2025
20b92e3
fix(h256): secure st_ref_pic_set allocations
lennartkloock Apr 23, 2025
863bf57
fix(h265): address comments
lennartkloock Apr 23, 2025
83c5bbf
fix(h265): address comment
lennartkloock Apr 24, 2025
689697a
fix(h265): new lint
lennartkloock Apr 24, 2025
6f5a2f0
fix(h265): remove palette_max_size check
lennartkloock Apr 24, 2025
c729ec0
fix(h265): profile related things and config record parsing
lennartkloock Apr 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified assets/hevc_aac.flv
Binary file not shown.
31 changes: 31 additions & 0 deletions changes.d/pr-425.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[[scuffle-bytes-util]]
category = "feat"
description = "add nal emulation prevention io"
authors = ["@lennartkloock"]

[[scuffle-flv]]
category = "chore"
description = "update tests to use new h265 version"
authors = ["@lennartkloock"]

[[scuffle-h264]]
category = "refactor"
description = "move nal emulation prevention io to scuffle-bytes-util"
breaking = true
authors = ["@lennartkloock"]

[[scuffle-h265]]
category = "refactor"
description = "reimplement h265 SPS parsing"
breaking = true
authors = ["@lennartkloock"]

[[scuffle-mp4]]
category = "chore"
description = "update to use new h265 version"
authors = ["@lennartkloock"]

[[scuffle-transmuxer]]
category = "chore"
description = "update to use new h265 version"
authors = ["@lennartkloock"]
2 changes: 2 additions & 0 deletions crates/bytes-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod bit_read;
mod bit_write;
mod bytes_cursor;
mod cow;
mod nal_emulation_prevention;
pub mod zero_copy;

pub use bit_read::BitReader;
Expand All @@ -26,3 +27,4 @@ pub use cow::string::StringCow;
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub use cow::string::serde::StringCowDeserializer;
pub use nal_emulation_prevention::EmulationPreventionIo;
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/// A wrapper around a [`std::io::Read`] or [`std::io::Write`] that automatically inserts or removes
/// emulation prevention bytes, when reading or writing respectively.
/// [NAL](https://en.wikipedia.org/wiki/Network_Abstraction_Layer) emulation prevention bytes, when reading or writing respectively.
///
/// Defined by:
/// - ISO/IEC 14496-10 - 7.4.1.1
/// - ISO/IEC 23008-2 - 7.4.2.3
pub struct EmulationPreventionIo<I> {
inner: I,
zero_count: u8,
Expand Down
149 changes: 62 additions & 87 deletions crates/flv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ mod tests {
use scuffle_av1::seq::SequenceHeaderObu;
use scuffle_bytes_util::StringCow;
use scuffle_h264::Sps;
use scuffle_h265::{ConstantFrameRate, NumTemporalLayers};

use crate::audio::AudioData;
use crate::audio::body::AudioTagBody;
Expand Down Expand Up @@ -568,59 +569,16 @@ mod tests {
); // AAC
assert_eq!(
on_meta_data.videocodecid,
Some(OnMetaDataVideoCodecId::Legacy(VideoCodecId::Avc))
); // AVC
assert_eq!(on_meta_data.duration, Some(0.0)); // 0 seconds (this was a live stream)
assert_eq!(on_meta_data.width, Some(2560.0));
assert_eq!(on_meta_data.height, Some(1440.0));
assert_eq!(on_meta_data.framerate, Some(144.0));
Some(OnMetaDataVideoCodecId::Enhanced(VideoFourCc::Hevc))
); // HEVC
assert_eq!(on_meta_data.duration, Some(2.038));
assert_eq!(on_meta_data.width, Some(3840.0));
assert_eq!(on_meta_data.height, Some(2160.0));
assert_eq!(on_meta_data.framerate, Some(60.0));
assert!(on_meta_data.videodatarate.is_some());
assert!(on_meta_data.audiodatarate.is_some());
}

// Audio Sequence Header Tag
{
let tag = tags.next().expect("expected tag");
assert_eq!(tag.timestamp_ms, 0);
assert_eq!(tag.stream_id, 0);

let (body, sound_rate, sound_size, sound_type) = match tag.data {
FlvTagData::Audio(AudioData {
body,
header:
AudioTagHeader::Legacy(LegacyAudioTagHeader {
sound_rate,
sound_size,
sound_type,
..
}),
}) => (body, sound_rate, sound_size, sound_type),
_ => panic!("expected audio data"),
};

assert_eq!(sound_rate, SoundRate::Hz44000);
assert_eq!(sound_size, SoundSize::Bit16);
assert_eq!(sound_type, SoundType::Stereo);

// Audio data should be an AAC sequence header
let data = match body {
AudioTagBody::Legacy(LegacyAudioTagBody::Aac(AacAudioData::SequenceHeader(data))) => data,
_ => panic!("expected aac sequence header"),
};

// The aac sequence header should be able to be decoded into an aac decoder
// configuration record
let aac_decoder_configuration_record =
PartialAudioSpecificConfig::parse(&data).expect("expected aac decoder configuration record");

assert_eq!(
aac_decoder_configuration_record.audio_object_type,
AudioObjectType::AacLowComplexity
);
assert_eq!(aac_decoder_configuration_record.sampling_frequency, 48000);
assert_eq!(aac_decoder_configuration_record.channel_configuration, 2);
}

// Video Sequence Header Tag
{
let tag = tags.next().expect("expected tag");
Expand All @@ -642,16 +600,15 @@ mod tests {

assert_eq!(frame_type, VideoFrameType::KeyFrame);

assert_eq!(config.configuration_version, 1);
assert_eq!(config.avg_frame_rate, 0);
assert_eq!(config.constant_frame_rate, 0);
assert_eq!(config.num_temporal_layers, 1);
assert_eq!(config.constant_frame_rate, ConstantFrameRate::Unknown);
assert_eq!(config.num_temporal_layers, NumTemporalLayers::NotScalable);

// We should be able to find a SPS NAL unit in the sequence header
let Some(sps) = config
.arrays
.iter()
.find(|a| a.nal_unit_type == scuffle_h265::NaluType::Sps)
.find(|a| a.nal_unit_type == scuffle_h265::NALUnitType::SpsNut)
.and_then(|v| v.nalus.first())
else {
panic!("expected sps");
Expand All @@ -661,32 +618,63 @@ mod tests {
let Some(_) = config
.arrays
.iter()
.find(|a| a.nal_unit_type == scuffle_h265::NaluType::Pps)
.find(|a| a.nal_unit_type == scuffle_h265::NALUnitType::PpsNut)
.and_then(|v| v.nalus.first())
else {
panic!("expected pps");
};

// We should be able to decode the SPS NAL unit
let sps = scuffle_h265::Sps::parse(sps.clone()).expect("expected sps");
let sps = scuffle_h265::SpsNALUnit::parse(io::Cursor::new(sps.clone())).expect("expected sps");

insta::assert_debug_snapshot!(sps);
}

// Audio Sequence Header Tag
{
let tag = tags.next().expect("expected tag");
assert_eq!(tag.timestamp_ms, 0);
assert_eq!(tag.stream_id, 0);

let (body, sound_rate, sound_size, sound_type) = match tag.data {
FlvTagData::Audio(AudioData {
body,
header:
AudioTagHeader::Legacy(LegacyAudioTagHeader {
sound_rate,
sound_size,
sound_type,
..
}),
}) => (body, sound_rate, sound_size, sound_type),
_ => panic!("expected audio data"),
};

assert_eq!(sound_rate, SoundRate::Hz44000);
assert_eq!(sound_size, SoundSize::Bit16);
assert_eq!(sound_type, SoundType::Stereo);

// Audio data should be an AAC sequence header
let data = match body {
AudioTagBody::Legacy(LegacyAudioTagBody::Aac(AacAudioData::SequenceHeader(data))) => data,
_ => panic!("expected aac sequence header"),
};

// The aac sequence header should be able to be decoded into an aac decoder
// configuration record
let aac_decoder_configuration_record =
PartialAudioSpecificConfig::parse(&data).expect("expected aac decoder configuration record");

assert_eq!(sps.frame_rate, 144.0);
assert_eq!(sps.width, 2560);
assert_eq!(sps.height, 1440);
assert_eq!(
sps.color_config,
Some(scuffle_h265::ColorConfig {
full_range: false,
color_primaries: 1,
transfer_characteristics: 1,
matrix_coefficients: 1,
})
)
aac_decoder_configuration_record.audio_object_type,
AudioObjectType::AacLowComplexity
);
assert_eq!(aac_decoder_configuration_record.sampling_frequency, 48000);
assert_eq!(aac_decoder_configuration_record.channel_configuration, 2);
}

// Rest of the tags should be video / audio data
let mut last_timestamp = 0;
let mut read_seq_end = false;
for tag in tags {
assert!(tag.timestamp_ms >= last_timestamp || tag.timestamp_ms == 0); // Timestamps should be monotonically increasing or 0
assert_eq!(tag.stream_id, 0);
Expand Down Expand Up @@ -719,29 +707,16 @@ mod tests {
body:
VideoTagBody::Enhanced(ExVideoTagBody::NoMultitrack {
video_four_cc: VideoFourCc::Hevc,
packet,
..
}),
}) => {
match frame_type {
VideoFrameType::KeyFrame => (),
VideoFrameType::InterFrame => (),
_ => panic!("expected keyframe or interframe"),
}

match packet {
VideoPacket::CodedFrames(_) => assert!(!read_seq_end),
VideoPacket::CodedFramesX { .. } => assert!(!read_seq_end),
VideoPacket::SequenceEnd => {
assert!(!read_seq_end);
read_seq_end = true;
}
_ => panic!("expected hevc nalu packet: {packet:?}"),
};
}
}) => match frame_type {
VideoFrameType::KeyFrame => (),
VideoFrameType::InterFrame => (),
VideoFrameType::Command => (),
_ => panic!("expected keyframe, interframe or command"),
},
_ => panic!("unexpected data"),
};
}

assert!(read_seq_end);
}
}
Loading
Loading