Halve the per-frame allocation and copy on the m2ts NAL video path
The NAL path called length_prefixed_to_annex_b, which allocates a whole-frame Vec of its own, then copied the result into a second whole-frame Vec — two full-frame allocations and two full-frame copies per video frame. The crate already has append_length_prefixed_as_annex_b, which writes the conversion straight into a destination buffer; it is the same code path with the intermediate removed. The destination is also sized once up front instead of starting from Vec::new(), which re-grew from zero capacity inside every conversion. On a UHD HEVC title muxed to m2ts:// — ~200k video frames averaging ~310 KB of ES at 60 Mb/s — that removes roughly 62 GB of allocation and 62 GB of memcpy. Behaviour is unchanged: the existing tsmux conversion tests, including the non-NAL passthrough and Annex-B default pair added last round, all still pass. A first attempt reused a persistent scratch buffer across frames, which does not work: the buffer is handed out as Cow::Owned and so can never be returned. A right-sized single allocation gets most of the win without restructuring the function around the borrow.
This commit is contained in:
+11
-3
@@ -4,7 +4,7 @@
|
||||
//! packets. Each frame is wrapped in a PES header, split into TS packets,
|
||||
//! and prepended with the 4-byte TP_extra_header.
|
||||
|
||||
use super::hevc::{avcc_to_annex_b, hvcc_to_annex_b, length_prefixed_to_annex_b};
|
||||
use super::hevc::{append_length_prefixed_as_annex_b, avcc_to_annex_b, hvcc_to_annex_b};
|
||||
use crate::disc::Codec;
|
||||
use std::io::{self, Write};
|
||||
|
||||
@@ -181,7 +181,10 @@ impl<W: Write> TsMuxer<W> {
|
||||
// unchanged, so borrow `data` directly rather than copying it; only
|
||||
// NAL video needs an owned Annex-B conversion buffer.
|
||||
let es_data: std::borrow::Cow<'_, [u8]> = if is_video && self.is_nal_video(track) {
|
||||
let mut annex_b = Vec::new();
|
||||
// Size the buffer once for the whole frame. `Vec::new()` re-grew from
|
||||
// zero capacity on every frame, reallocating repeatedly inside a single
|
||||
// ~310 KB conversion. The slack covers any prepended parameter sets.
|
||||
let mut annex_b = Vec::with_capacity(data.len() + 1024);
|
||||
if keyframe && !self.params_written[track] {
|
||||
if let Some(ref cp) = self.codec_privates[track] {
|
||||
// avcC and hvcC are DIFFERENT box layouts; parsing one with
|
||||
@@ -198,7 +201,12 @@ impl<W: Write> TsMuxer<W> {
|
||||
}
|
||||
self.params_written[track] = true;
|
||||
}
|
||||
annex_b.extend_from_slice(&length_prefixed_to_annex_b(data));
|
||||
// Write the conversion STRAIGHT into the destination.
|
||||
// `length_prefixed_to_annex_b` allocates a whole-frame Vec of its own
|
||||
// and we then copied it in, so every video frame cost two full-frame
|
||||
// allocations and two full-frame copies. At ~200k frames averaging
|
||||
// ~310 KB of ES on a UHD, that is ~124 GB of pointless memcpy.
|
||||
append_length_prefixed_as_annex_b(&mut annex_b, data);
|
||||
std::borrow::Cow::Owned(annex_b)
|
||||
} else {
|
||||
if is_video {
|
||||
|
||||
Reference in New Issue
Block a user