v0.7.18: support H.265

There's no transcoding, so if the browser/player doesn't support these
formats, they don't play. But it will record and allow downloads, and
it seems to be working with Chrome on macOS at least.

Fixes #33
This commit is contained in:
Scott Lamb 2025-01-28 09:04:59 -08:00
parent dd30d5bcf8
commit b4836f3abb
5 changed files with 22 additions and 20 deletions

View File

@ -8,8 +8,10 @@ upgrades, e.g. `v0.6.x` -> `v0.7.x`. The config file format and
[API](ref/api.md) currently have no stability guarantees, so they may change [API](ref/api.md) currently have no stability guarantees, so they may change
even on minor releases, e.g. `v0.7.5` -> `v0.7.6`. even on minor releases, e.g. `v0.7.5` -> `v0.7.6`.
## unreleased ## v0.7.18 (2025-01-28)
* support recording H.265 ([#33](https://github.com/scottlamb/moonfire-nvr/issues/33)).
Browser support may vary.
* bump minimum Rust version to 1.82. * bump minimum Rust version to 1.82.
* improve error message on timeout opening stream. * improve error message on timeout opening stream.
* use `jiff` for time manipulations. * use `jiff` for time manipulations.

26
server/Cargo.lock generated
View File

@ -98,9 +98,9 @@ checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]] [[package]]
name = "bitstream-io" name = "bitstream-io"
version = "1.10.0" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e445576659fd04a57b44cbd00aa37aaa815ebefa0aa3cb677a6b5e63d883074f" checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
[[package]] [[package]]
name = "blake3" name = "blake3"
@ -536,9 +536,9 @@ dependencies = [
[[package]] [[package]]
name = "four-cc" name = "four-cc"
version = "0.1.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3958af68a31b1d1384d3f39b6aa33eb14b6009065b5ca305ddd9712a4237124f" checksum = "795cbfc56d419a7ce47ccbb7504dd9a5b7c484c083c356e797de08bd988d9629"
[[package]] [[package]]
name = "futures" name = "futures"
@ -658,9 +658,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]] [[package]]
name = "h264-reader" name = "h264-reader"
version = "0.7.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd118dcc322cc71cfc33254a19ebece92cfaaf6d4b4793fec3f7f44fbc4150df" checksum = "036a78b2620d92f0ec57690bc792b3bb87348632ee5225302ba2e66a48021c6c"
dependencies = [ dependencies = [
"bitstream-io", "bitstream-io",
"hex-slice", "hex-slice",
@ -1346,9 +1346,9 @@ dependencies = [
[[package]] [[package]]
name = "mp4ra-rust" name = "mp4ra-rust"
version = "0.1.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be9daf03b43bf3842962947c62ba40f411e46a58774c60838038f04a67d17626" checksum = "fdbc3d3867085d66ac6270482e66f3dd2c5a18451a3dc9ad7269e94844a536b7"
dependencies = [ dependencies = [
"four-cc", "four-cc",
] ]
@ -1826,9 +1826,9 @@ dependencies = [
[[package]] [[package]]
name = "retina" name = "retina"
version = "0.4.11" version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d83a8e0892d8e0836cd9be6cc413ab3f1c574276e047ac6e27e3027832d00e" checksum = "2135e9c5a2ffcab6428221605faa39f1cca4bef6fc5756dbfd02ab57cf68f670"
dependencies = [ dependencies = [
"base64", "base64",
"bitstream-io", "bitstream-io",
@ -1840,7 +1840,6 @@ dependencies = [
"jiff", "jiff",
"log", "log",
"memchr", "memchr",
"once_cell",
"pin-project", "pin-project",
"pretty-hex", "pretty-hex",
"rand", "rand",
@ -1855,11 +1854,10 @@ dependencies = [
[[package]] [[package]]
name = "rfc6381-codec" name = "rfc6381-codec"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4395f46a67f0d57c57f6a5361f3a9a0c0183a19cab3998892ecdc003de6d8037" checksum = "ed54c20f5c3ec82eab6d998b313dc75ec5d5650d4f57675e61d72489040297fd"
dependencies = [ dependencies = [
"four-cc",
"mp4ra-rust", "mp4ra-rust",
"mpeg4-audio-const", "mpeg4-audio-const",
] ]

View File

@ -24,7 +24,7 @@ members = ["base", "db"]
[workspace.dependencies] [workspace.dependencies]
base64 = "0.22.0" base64 = "0.22.0"
h264-reader = "0.7.0" h264-reader = "0.8.0"
itertools = "0.12.0" itertools = "0.12.0"
jiff = "0.1.8" jiff = "0.1.8"
nix = "0.27.0" nix = "0.27.0"
@ -62,7 +62,7 @@ password-hash = "0.5.0"
pretty-hex = { workspace = true } pretty-hex = { workspace = true }
protobuf = "3.0" protobuf = "3.0"
reffers = "0.7.0" reffers = "0.7.0"
retina = "0.4.11" retina = "0.4.12"
ring = { workspace = true } ring = { workspace = true }
rusqlite = { workspace = true } rusqlite = { workspace = true }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@ -133,7 +133,7 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
) )
})?; })?;
let sps = ctx let sps = ctx
.sps_by_id(h264_reader::nal::pps::ParamSetId::from_u32(0).unwrap()) .sps_by_id(h264_reader::nal::sps::SeqParamSetId::from_u32(0).unwrap())
.ok_or_else(|| { .ok_or_else(|| {
err!( err!(
Unimplemented, Unimplemented,

View File

@ -170,7 +170,7 @@ fn params_to_sample_entry(
.with_aspect_ratio(aspect) .with_aspect_ratio(aspect)
.build() .build()
.map_err(|e| err!(Unknown, source(e)))?, .map_err(|e| err!(Unknown, source(e)))?,
rfc6381_codec: "avc1.4d401e".to_string(), rfc6381_codec: params.rfc6381_codec().to_owned(),
width, width,
height, height,
pasp_h_spacing: aspect.0, pasp_h_spacing: aspect.0,
@ -192,7 +192,9 @@ impl RetinaStreamInner {
let video_i = session let video_i = session
.streams() .streams()
.iter() .iter()
.position(|s| s.media() == "video" && matches!(s.encoding_name(), "h264" | "jpeg")) .position(|s| {
s.media() == "video" && matches!(s.encoding_name(), "h264" | "h265" | "jpeg")
})
.ok_or_else(|| { .ok_or_else(|| {
err!( err!(
FailedPrecondition, FailedPrecondition,