Skip to content

Commit

Permalink
H.265 support
Browse files Browse the repository at this point in the history
  • Loading branch information
scottlamb committed Jan 28, 2025
1 parent efc69e6 commit a192bc4
Show file tree
Hide file tree
Showing 18 changed files with 2,620 additions and 129 deletions.
14 changes: 9 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ jobs:
matrix:
rust:
- stable
- '1.70'
- '1.79'
include:
- rust: stable
extra_components: rustfmt
fail-fast: false
runs-on: ubuntu-20.04
steps:
- name: Checkout
Expand All @@ -34,11 +35,11 @@ jobs:
toolchain: ${{ matrix.rust }}
components: ${{ matrix.extra_components }}
- name: Build (no features)
run: cargo build --all-targets --workspace
run: cargo build --no-default-features --all-targets --workspace
- name: Build (all features)
run: cargo build --all-features --all-targets --workspace
- name: Test (no features)
run: cargo test --all-targets --workspace
run: cargo test --no-default-features --all-targets --workspace
- name: Test (all features)
run: cargo test --all-features --all-targets --workspace
- name: Check fuzz tests
Expand All @@ -49,9 +50,12 @@ jobs:
- name: Check fuzz workspace formatting
if: matrix.rust == 'stable'
run: cd fuzz && cargo fmt --all --check
- name: Clippy on main workspace
- name: Clippy on main workspace (no features)
if: matrix.rust == 'stable'
run: cargo clippy --workspace -- -D warnings
run: cargo clippy --no-default-features --workspace -- -D warnings
- name: Clippy on main workspace (all features)
if: matrix.rust == 'stable'
run: cargo clippy --all-features --workspace -- -D warnings
- name: Clippy on fuzz workspace
if: matrix.rust == 'stable'
run: cd fuzz && cargo clippy --workspace -- -D warnings
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## unreleased

* support H.265 ([#57](https://github.com/scottlamb/retina/issues/57)).
* fix `Connecting via TCP to known-broken RTSP server` log line on
connection to a non-broken server.

Expand Down
21 changes: 10 additions & 11 deletions Cargo.lock

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

10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ categories = ["network-programming", "multimedia"]
description = "high-level RTSP multimedia streaming library"
repository = "https://github.com/scottlamb/retina"
include = ["src/**/*", "benches", "Cargo.toml"]
rust-version = "1.70"
rust-version = "1.79"

[features]
default = ["h265"]
h265 = []

[package.metadata.docs.rs]
# https://docs.rs/about/metadata
Expand All @@ -23,10 +27,10 @@ all-features = true

[dependencies]
base64 = "0.22.0"
bitstream-io = "1.1"
bitstream-io = "2.6"
bytes = "1.0.1"
futures = "0.3.14"
h264-reader = "0.7.0"
h264-reader = "0.8.0"
hex = "0.4.3"
http-auth = "0.1.2"
jiff = "0.1.8"
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,16 @@ Progress:
* [ ] synchronous with std only
* codec depacketization
* [x] video
* [x] H.264
* [x] H.264 (RFC 6184](https://datatracker.ietf.org/doc/html/rfc6184))
* [ ] SVC
* [ ] periodic infra refresh
* [x] multiple slices per picture
* [ ] multiple SPS/PPS
* [ ] interleaved mode
* [x] AAC output format
* [ ] Annex B output format ([#44](https://github.com/scottlamb/retina/issues/44))
* [x] ([RFC 6184](https://datatracker.ietf.org/doc/html/rfc6184))
* [x] MJPEG
* [x] ([RFC 2435](https://datatracker.ietf.org/doc/html/rfc2435))
* [x] H.265 ([RFC 7798](https://tools.ietf.org/html/rfc7798))
* [x] MJPEG ([RFC 2435](https://datatracker.ietf.org/doc/html/rfc2435))
* audio
* [x] AAC
* [ ] interleaving
Expand Down
5 changes: 3 additions & 2 deletions examples/client/src/mp4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -711,8 +711,9 @@ pub async fn run(opts: Opts) -> Result<(), Error> {
let video_stream_i = if !opts.no_video {
let s = session.streams().iter().position(|s| {
if s.media() == "video" {
if s.encoding_name() == "h264" || s.encoding_name() == "jpeg" {
log::info!("Using h264 video stream");
let encoding_name = s.encoding_name();
if matches!(encoding_name, "h264" | "h265" | "jpeg") {
log::info!("Using {encoding_name} video stream");
return true;
}
log::info!(
Expand Down
27 changes: 12 additions & 15 deletions fuzz/Cargo.lock

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

13 changes: 13 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ memchr = "2.7.4"

[dependencies.retina]
path = ".."
features = ["h265"]

# Prevent this from interfering with workspaces
[workspace]
Expand All @@ -27,6 +28,12 @@ path = "fuzz_targets/depacketize_h264.rs"
test = false
doc = false

[[bin]]
name = "depacketize_h265"
path = "fuzz_targets/depacketize_h265.rs"
test = false
doc = false

[[bin]]
name = "roundtrip_h264"
path = "fuzz_targets/roundtrip_h264.rs"
Expand All @@ -38,3 +45,9 @@ name = "depacketize_jpeg"
path = "fuzz_targets/depacketize_jpeg.rs"
test = false
doc = false

[[bin]]
name = "h265_nal"
path = "fuzz_targets/h265_nal.rs"
test = false
doc = false
60 changes: 60 additions & 0 deletions fuzz/fuzz_targets/depacketize_h265.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (C) 2021 Scott Lamb <[email protected]>
// SPDX-License-Identifier: MIT OR Apache-2.0

#![no_main]
use libfuzzer_sys::fuzz_target;
use std::num::NonZeroU32;

fuzz_target!(|data: &[u8]| {
let mut data = data;
let mut depacketizer = retina::codec::Depacketizer::new(
"video", "h265", 90_000, None, Some("profile-id=1;sprop-sps=QgEBAWAAAAMAsAAAAwAAAwBaoAWCAeFja5JFL83BQYFBAAADAAEAAAMADKE=;sprop-pps=RAHA8saNA7NA;sprop-vps=QAEMAf//AWAAAAMAsAAAAwAAAwBarAwAAAMABAAAAwAyqA==")).unwrap();
let mut timestamp = retina::Timestamp::new(0, NonZeroU32::new(90_000).unwrap(), 0).unwrap();
let mut sequence_number: u16 = 0;
let conn_ctx = retina::ConnectionContext::dummy();
let stream_ctx = retina::StreamContext::dummy();
let pkt_ctx = retina::PacketContext::dummy();
loop {
let (hdr, rest) = match data.split_first() {
Some(r) => r,
None => return,
};
let ts_change = (hdr & 0b001) != 0;
let mark = (hdr & 0b010) != 0;
let loss = (hdr & 0b100) != 0;
let len = usize::from(hdr >> 3);
if rest.len() < len {
return;
}
let (payload, rest) = rest.split_at(len);
data = rest;
if loss {
sequence_number = sequence_number.wrapping_add(1);
}
if ts_change {
timestamp = timestamp.try_add(1).unwrap();
}
let pkt = retina::rtp::ReceivedPacketBuilder {
ctx: pkt_ctx,
stream_id: 0,
timestamp,
ssrc: 0,
sequence_number,
loss: u16::from(loss),
payload_type: 96,
mark,
}
.build(payload.iter().copied())
.unwrap();
// println!("pkt: {:#?}", pkt);
if depacketizer.push(pkt).is_err() {
return;
}
while let Some(item) = depacketizer.pull(&conn_ctx, &stream_ctx).transpose() {
if item.is_err() {
return;
}
}
sequence_number = sequence_number.wrapping_add(1);
}
});
23 changes: 23 additions & 0 deletions fuzz/fuzz_targets/h265_nal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (C) 2024 Scott Lamb <[email protected]>
// SPDX-License-Identifier: MIT OR Apache-2.0

#![no_main]
use libfuzzer_sys::fuzz_target;

use retina::codec::h265::nal;

fuzz_target!(|data: &[u8]| {
let Ok((h, bits)) = nal::split(data) else {
return;
};

match h.unit_type() {
nal::UnitType::SpsNut => {
let _ = nal::Sps::from_bits(bits);
}
nal::UnitType::PpsNut => {
let _ = nal::Pps::from_bits(bits);
}
_ => {}
}
});
21 changes: 13 additions & 8 deletions src/client/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -943,14 +943,19 @@ mod tests {
assert_eq!(p.streams[0].media(), "video");
assert_eq!(p.streams[0].encoding_name(), "h265");
assert_eq!(p.streams[0].rtp_payload_type, 98);
assert!(p.streams[0].parameters().is_none());
assert_eq!(p.streams[1].media(), "audio");
assert_eq!(p.streams[1].encoding_name(), "pcma");
assert_eq!(p.streams[1].rtp_payload_type, 8);
match p.streams[1].parameters().unwrap() {
ParametersRef::Audio(_) => {}
_ => panic!(),
};

if cfg!(feature = "h265") {
assert!(p.streams[0].parameters().is_some());
assert_eq!(p.streams[1].media(), "audio");
assert_eq!(p.streams[1].encoding_name(), "pcma");
assert_eq!(p.streams[1].rtp_payload_type, 8);
match p.streams[1].parameters().unwrap() {
ParametersRef::Audio(_) => {}
_ => panic!(),
};
} else {
assert!(p.streams[0].parameters().is_none());
}
}

#[test]
Expand Down
Loading

0 comments on commit a192bc4

Please sign in to comment.