Skip to content

Commit

Permalink
minor H.264 cleanups
Browse files Browse the repository at this point in the history
* refine logic for identifying a key frame. If an AU has both IDR and
  non-IDR slices, it should be a non-key frame, so start from saying
  it's a key frame and then say it's not when it encounters a non-IDR
  slice.
* avoid some branches via newer slice methods.
* spruce up some comments.
  • Loading branch information
scottlamb committed Jan 26, 2025
1 parent 75fc033 commit efc69e6
Showing 1 changed file with 18 additions and 12 deletions.
30 changes: 18 additions & 12 deletions src/codec/h264.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (C) 2021 Scott Lamb <[email protected]>
// SPDX-License-Identifier: MIT OR Apache-2.0

//! [H.264](https://www.itu.int/rec/T-REC-H.264-201906-I/en)-encoded video.
//! [H.264](https://www.itu.int/rec/T-REC-H.264-201906-I/en)-encoded video,
//! with RTP encoding as in [RFC 6184](https://tools.ietf.org/html/rfc6184).
use std::convert::TryFrom;
use std::fmt::Write;
Expand All @@ -22,9 +23,12 @@ use super::VideoFrame;
/// and produces unfragmented NAL units as specified in [RFC
/// 6184](https://tools.ietf.org/html/rfc6184).
///
/// This doesn't inspect the contents of the NAL units, so it doesn't depend on or
/// verify compliance with H.264 section 7.4.1.2.3 "Order of NAL units and coded
/// pictures and association to access units".
/// This inspects the contents of the NAL units only minimally, and largely for
/// logging. In particular, it doesn't completely enforce verify compliance with
/// H.264 section 7.4.1.2.3 "Order of NAL units and coded pictures and
/// association to access units". For compatibility with some broken cameras
/// that change timestamps mid-AU, it does extend AUs if they end with parameter
/// sets. See `can_end_au`.
///
/// Currently expects that the stream starts at an access unit boundary unless
/// packet loss is indicated.
Expand Down Expand Up @@ -324,11 +328,10 @@ impl Depacketizer {
let loss = pkt.loss();
let timestamp = pkt.timestamp();
let mut data = pkt.into_payload_bytes();
if data.is_empty() {
return Err("Empty NAL".into());
}
// https://tools.ietf.org/html/rfc6184#section-5.2
let nal_header = data[0];
let Some(&nal_header) = data.first() else {
return Err("Empty NAL".into());
};
if (nal_header >> 7) != 0 {
return Err(format!("NAL header {nal_header:02x} has F bit set"));
}
Expand Down Expand Up @@ -623,7 +626,7 @@ impl Depacketizer {
fn finalize_access_unit(&mut self, au: AccessUnit, reason: &str) -> Result<VideoFrame, String> {
let mut piece_idx = 0;
let mut retained_len = 0usize;
let mut is_random_access_point = false;
let mut is_random_access_point = true;
let mut is_disposable = true;
let mut new_sps = None;
let mut new_pps = None;
Expand Down Expand Up @@ -655,7 +658,10 @@ impl Depacketizer {
new_pps = Some(to_bytes(nal.hdr, nal.len, nal_pieces));
}
}
UnitType::SliceLayerWithoutPartitioningIdr => is_random_access_point = true,
UnitType::SliceDataPartitionALayer
| UnitType::SliceDataPartitionBLayer
| UnitType::SliceDataPartitionCLayer
| UnitType::SliceLayerWithoutPartitioningNonIdr => is_random_access_point = false,
_ => {}
}
if nal.hdr.nal_ref_idc() != 0 {
Expand Down Expand Up @@ -1085,7 +1091,7 @@ impl InternalParameters {

/// Returns true iff the bytes of `nal` equal the bytes of `[hdr, ..data]`.
fn nal_matches(nal: &[u8], hdr: NalHeader, pieces: &[Bytes]) -> bool {
if nal.is_empty() || nal[0] != u8::from(hdr) {
if nal.first() != Some(&u8::from(hdr)) {
return false;
}
let mut nal_pos = 1;
Expand All @@ -1102,7 +1108,7 @@ fn nal_matches(nal: &[u8], hdr: NalHeader, pieces: &[Bytes]) -> bool {
nal_pos == nal.len()
}

/// Saves the given NAL to a contiguous Bytes.
/// Saves the given NAL to a contiguous `Bytes`.
fn to_bytes(hdr: NalHeader, len: u32, pieces: &[Bytes]) -> Bytes {
let len = usize::try_from(len).expect("u32 fits in usize");
let mut out = Vec::with_capacity(len);
Expand Down

0 comments on commit efc69e6

Please sign in to comment.