chore(i18n): finish translating code comments and scripts to English
CI / Format and clippy (push) Successful in 1m57s
CI / Dependency audit (push) Successful in 13s
CI / Check desktop app (Linux) (push) Successful in 3m54s
CI / Tests (Linux) (push) Failing after 7m25s
Release / Create release (push) Successful in 6s
Release / CLI (linux-aarch64) (push) Successful in 1m48s
Release / CLI (linux-x86_64) (push) Successful in 2m39s
Release / Desktop app (linux-aarch64) (push) Successful in 10m9s
Release / Desktop app (linux-x86_64) (push) Successful in 12m20s
Release / Desktop app (windows) (push) Successful in 7m32s
Release / Build latest.json (push) Successful in 5s
CI / Format and clippy (push) Successful in 1m57s
CI / Dependency audit (push) Successful in 13s
CI / Check desktop app (Linux) (push) Successful in 3m54s
CI / Tests (Linux) (push) Failing after 7m25s
Release / Create release (push) Successful in 6s
Release / CLI (linux-aarch64) (push) Successful in 1m48s
Release / CLI (linux-x86_64) (push) Successful in 2m39s
Release / Desktop app (linux-aarch64) (push) Successful in 10m9s
Release / Desktop app (linux-x86_64) (push) Successful in 12m20s
Release / Desktop app (windows) (push) Successful in 7m32s
Release / Build latest.json (push) Successful in 5s
Completes the English-only standardization for code, comments, scripts and CI templates (UI/CLI-facing strings stay bilingual pt-BR/en, untouched). Covers the remaining crates/, apps/gui/ui/ and apps/gui/src-tauri/ comments, .gitea/ workflow and template comments, .githooks/ hooks, and the scripts/ source comments for the files already renamed in a prior commit (check.sh, generate-logo.py, install-hooks.sh). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
This commit is contained in:
1 parent
da212e6926
commit
6328220c60
129 files changed
+1961
-1834
No files matched your search
@@ -1,7 +1,7 @@
|
||||
//! Reconstrução de vídeo MP4/MOV sem o átomo `moov` (gravação interrompida de câmera ou drone,
|
||||
//! arquivo cortado). Os quadros ficam no `mdat` como unidades NAL com prefixo de tamanho; o
|
||||
//! índice (`moov`) é refeito a partir delas: tamanhos de amostra, quadros-chave, tempo e, para
|
||||
//! H.264, a ordem de apresentação (POC) quando há quadros B. O áudio não é reconstruído.
|
||||
//! Reconstructs MP4/MOV video files missing their `moov` atom (interrupted camera/drone recordings
|
||||
//! or truncated files). Video frames sit in `mdat` as length-prefixed NAL units; we rebuild the
|
||||
//! `moov` index from them: sample sizes, keyframes, timing, and for H.264, presentation order
|
||||
//! (POC) when B-frames are present. Audio tracks are not reconstructed.
|
||||
|
||||
use crate::source::{read_vec, Source};
|
||||
|
||||
@@ -58,7 +58,7 @@ impl<'a> Bits<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove os bytes de "emulation prevention" (00 00 03 -> 00 00).
|
||||
/// Strips H.264/HEVC emulation prevention bytes (`00 00 03` -> `00 00`).
|
||||
fn unescape(nal: &[u8]) -> Vec<u8> {
|
||||
let mut out = Vec::with_capacity(nal.len());
|
||||
let mut z = 0;
|
||||
@@ -86,7 +86,7 @@ pub struct Sps264 {
|
||||
pub poc_type: u32,
|
||||
pub log2_max_poc_lsb: u32,
|
||||
pub frame_mbs_only: bool,
|
||||
/// (unidades por tique, escala de tempo)
|
||||
/// `(num_units_in_tick, time_scale)` from VUI timing info.
|
||||
pub timing: Option<(u32, u32)>,
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ pub fn parse_sps264(nal: &[u8]) -> Option<Sps264> {
|
||||
};
|
||||
s.width = (w * 16).checked_sub((cl + cr) * sub_w)?;
|
||||
s.height = ((2 - s.frame_mbs_only as u32) * h * 16).checked_sub((ct + cb) * sub_h)?;
|
||||
// VUI até o timing_info.
|
||||
// Parse VUI parameters up to `timing_info_present_flag`.
|
||||
if b.bit()? == 1 {
|
||||
if b.bit()? == 1 && b.u(8)? == 255 {
|
||||
b.skip(32);
|
||||
@@ -202,7 +202,7 @@ pub fn parse_sps264(nal: &[u8]) -> Option<Sps264> {
|
||||
Some(s)
|
||||
}
|
||||
|
||||
/// (primeiro_mb == 0, tipo de fatia, pps_id, frame_num, idr, poc_lsb) do cabeçalho de fatia H.264.
|
||||
/// Parses `(first_mb == 0, slice_type, pps_id, frame_num, is_idr, poc_lsb)` from an H.264 slice header.
|
||||
struct SliceInfo {
|
||||
frame_num: u32,
|
||||
first_mb0: bool,
|
||||
@@ -240,7 +240,7 @@ fn slice264(nal: &[u8], sps: &Sps264, pic_order_present: bool) -> Option<SliceIn
|
||||
})
|
||||
}
|
||||
|
||||
/// bottom_field_pic_order_in_frame_present_flag do PPS.
|
||||
/// Extracts `bottom_field_pic_order_in_frame_present_flag` from an H.264 PPS.
|
||||
fn pps_pic_order_present(nal: &[u8]) -> bool {
|
||||
let rbsp = unescape(&nal[1..]);
|
||||
let mut b = Bits::new(&rbsp);
|
||||
@@ -278,7 +278,7 @@ pub fn parse_sps_hevc(nal: &[u8]) -> Option<SpsHevc> {
|
||||
*v = b.u(8)? as u8;
|
||||
let _ = i;
|
||||
}
|
||||
// general: 2+1+5 bits, 32 compat, 48 flags, 8 level => lemos 12 bytes (1+4+6+1)
|
||||
// general profile/tier/level: 2+1+5 bits, 32 compat, 48 constraint flags, 8 level => 12 bytes (1+4+6+1)
|
||||
let mut sub_profile = vec![];
|
||||
let mut sub_level = vec![];
|
||||
for _ in 0..max_sub {
|
||||
@@ -320,9 +320,9 @@ pub fn parse_sps_hevc(nal: &[u8]) -> Option<SpsHevc> {
|
||||
Some(s)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------- amostras
|
||||
// -------------------------------------------------------------- samples
|
||||
|
||||
/// Configuração de codec (SPS/PPS/VPS) extraída de um vídeo íntegro da mesma câmera.
|
||||
/// Codec parameter sets (SPS/PPS/VPS) extracted from a donor video recorded by the same camera.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MediaConfig {
|
||||
pub codec: Codec,
|
||||
@@ -331,9 +331,9 @@ pub struct MediaConfig {
|
||||
pub vps: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Procura o `avcC`/`hvcC` de um MP4 completo (lê até `max` bytes a partir de `off`).
|
||||
/// Extracts `avcC`/`hvcC` codec configuration from an intact MP4 (reading up to `max` bytes from `off`).
|
||||
pub fn config_from_mp4(src: &dyn Source, off: u64, max: u64) -> Option<MediaConfig> {
|
||||
// O moov costuma estar no começo ou no fim: procura nos dois lugares.
|
||||
// The `moov` atom is typically near the beginning or the end of the file: check both.
|
||||
let size = src.size();
|
||||
let head = read_vec(src, off, max.min(4 << 20) as usize).ok()?;
|
||||
let mut hay: Vec<Vec<u8>> = vec![head];
|
||||
@@ -416,7 +416,7 @@ pub enum Codec {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Sample {
|
||||
/// Posição absoluta (na origem) do primeiro byte da amostra (prefixo de tamanho incluso).
|
||||
/// Absolute source offset of the first byte of the sample (including the 4-byte length prefix).
|
||||
off: u64,
|
||||
size: u32,
|
||||
sync: bool,
|
||||
@@ -425,9 +425,9 @@ struct Sample {
|
||||
}
|
||||
|
||||
pub struct Rebuilt {
|
||||
/// Bytes do arquivo original antes do `mdat` (ftyp etc.).
|
||||
/// Leading atoms from the original file before `mdat` (`ftyp`, etc.).
|
||||
pub pre_len: u64,
|
||||
/// Onde começam e terminam os dados dos quadros na origem.
|
||||
/// Start and end offsets of the video sample payload in the source.
|
||||
pub payload: (u64, u64),
|
||||
pub moov: Vec<u8>,
|
||||
pub frames: usize,
|
||||
@@ -439,7 +439,7 @@ pub struct Rebuilt {
|
||||
}
|
||||
|
||||
impl Rebuilt {
|
||||
/// Cabeçalho `mdat` de 64 bits para o payload.
|
||||
/// 16-byte 64-bit `mdat` atom header covering the sample payload.
|
||||
pub fn mdat_header(&self) -> Vec<u8> {
|
||||
let size = 16 + (self.payload.1 - self.payload.0);
|
||||
let mut v = Vec::with_capacity(16);
|
||||
@@ -457,7 +457,7 @@ fn hevc_nal_ok(b0: u8, b1: u8) -> bool {
|
||||
b0 & 0x81 == 0 && ((b0 >> 1) & 0x3F) <= 40 && b1 & 0x07 != 0 && (b1 >> 3) == 0
|
||||
}
|
||||
|
||||
/// Cabeçalho do átomo `mdat` que começa em `at`: (início dos dados, tamanho declarado ou 0).
|
||||
/// Parses the `mdat` header at `at`: returns `(payload_start, declared_end_or_0)`.
|
||||
fn mdat_at(src: &dyn Source, at: u64) -> Option<(u64, u64)> {
|
||||
let h = read_vec(src, at, 16).ok()?;
|
||||
if h.len() < 8 || &h[4..8] != b"mdat" {
|
||||
@@ -471,7 +471,7 @@ fn mdat_at(src: &dyn Source, at: u64) -> Option<(u64, u64)> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconstrói o índice de um MP4 que começa em `off`, sem `moov`. `avail` limita a leitura.
|
||||
/// Rebuilds the `moov` index for a headerless/truncated MP4 starting at `off` (reading at most `avail` bytes).
|
||||
pub fn rebuild(src: &dyn Source, off: u64, avail: u64, refs: &[MediaConfig]) -> Option<Rebuilt> {
|
||||
if let Some(r) = rebuild_with(src, off, avail, None) {
|
||||
return Some(r);
|
||||
@@ -486,7 +486,7 @@ fn rebuild_with(
|
||||
avail: u64,
|
||||
preset: Option<&MediaConfig>,
|
||||
) -> Option<Rebuilt> {
|
||||
// Átomos antes do mdat.
|
||||
// Parse leading atoms before `mdat`.
|
||||
let mut p = 0u64;
|
||||
let mut pre_end = None;
|
||||
for _ in 0..16 {
|
||||
@@ -520,7 +520,7 @@ fn rebuild_with(
|
||||
(off + pre_len + declared).min(hard_end)
|
||||
};
|
||||
|
||||
// Lê o payload em blocos, interpretando NALs.
|
||||
// Scan the `mdat` payload in chunks, parsing length-prefixed NAL units.
|
||||
let mut samples: Vec<Sample> = vec![];
|
||||
let mut cache: Vec<u8> = vec![];
|
||||
let mut cache_off = data_start;
|
||||
@@ -536,7 +536,7 @@ fn rebuild_with(
|
||||
let s = (o - *cache_off) as usize;
|
||||
cache.get(s..s + n).map(|x| x.to_vec())
|
||||
};
|
||||
// Detecta o codec: o que forma a cadeia mais longa de unidades NAL válidas.
|
||||
// Detect the codec (H.264 vs HEVC) that yields the longest initial chain of valid NAL units.
|
||||
let chain = |c: Codec| -> usize {
|
||||
let Ok(head) = read_vec(src, data_start, 64 << 10) else {
|
||||
return 0;
|
||||
@@ -599,10 +599,10 @@ fn rebuild_with(
|
||||
let mut idr_total = 0usize;
|
||||
let mut idr_bad = 0usize;
|
||||
let mut cur: Option<Sample> = None;
|
||||
let mut group_start: Option<u64> = None; // início do grupo de NALs não-VCL que precede um quadro
|
||||
let mut group_start: Option<u64> = None; // start offset of the non-VCL NAL group preceding a frame
|
||||
let mut nal_count = 0usize;
|
||||
let mut max_len = 0u64;
|
||||
// POC (H.264, tipo 0)
|
||||
// POC (H.264 pic_order_cnt_type 0)
|
||||
let mut prev_msb: i64 = 0;
|
||||
let mut prev_lsb: i64 = 0;
|
||||
|
||||
@@ -617,7 +617,7 @@ fn rebuild_with(
|
||||
};
|
||||
let mut ok = ok;
|
||||
if ok && nal_count >= 3 && len > (max_len * 6).max(1 << 20) {
|
||||
// Um "tamanho" muito maior que os já vistos é lixo (áudio lido como vídeo).
|
||||
// A declared NAL length far larger than valid range is interleaved audio or garbage.
|
||||
ok = false;
|
||||
}
|
||||
if ok {
|
||||
@@ -629,7 +629,7 @@ fn rebuild_with(
|
||||
max_len = max_len.max(len);
|
||||
}
|
||||
if !valid {
|
||||
// Pode ser áudio intercalado: procura a próxima unidade NAL plausível.
|
||||
// Likely an interleaved audio chunk: scan ahead for the next plausible video NAL sequence.
|
||||
let found = scan_nal(
|
||||
src,
|
||||
codec,
|
||||
@@ -642,7 +642,7 @@ fn rebuild_with(
|
||||
);
|
||||
match found {
|
||||
Some(q) if nal_count > 0 => {
|
||||
// O que veio antes fecha o quadro atual.
|
||||
// Flush the current frame before starting the next one.
|
||||
if let Some(mut s) = cur.take() {
|
||||
s.size = (group_start.unwrap_or(pos) - s.off) as u32;
|
||||
samples.push(s);
|
||||
@@ -734,9 +734,9 @@ fn rebuild_with(
|
||||
let _ = ty;
|
||||
if is_vcl {
|
||||
if first_of_pic {
|
||||
// fecha o quadro anterior
|
||||
// flush previous frame
|
||||
if let Some(mut s) = cur.take() {
|
||||
// tamanho até o início do grupo atual (ou deste NAL)
|
||||
// frame size up to the start of the current non-VCL group (or this VCL NAL)
|
||||
let end = group_start.unwrap_or(pos);
|
||||
s.size = (end - s.off) as u32;
|
||||
samples.push(s);
|
||||
@@ -816,7 +816,7 @@ fn rebuild_with(
|
||||
return None;
|
||||
}
|
||||
let (num, den) = match timing {
|
||||
Some((units, scale)) if codec == Codec::Avc => (scale, 2 * units), // quadros por segundo = scale / (2 * units)
|
||||
Some((units, scale)) if codec == Codec::Avc => (scale, 2 * units), // fps = time_scale / (2 * num_units_in_tick)
|
||||
Some((units, scale)) => (scale, units),
|
||||
None => (30, 1),
|
||||
};
|
||||
@@ -827,10 +827,10 @@ fn rebuild_with(
|
||||
(num, den)
|
||||
};
|
||||
let fps = num as f64 / den as f64;
|
||||
// Escala: tique do quadro = den, escala de tempo = num.
|
||||
// Timescale: frame tick = den, timescale = num.
|
||||
let (timescale, dur) = (num, den);
|
||||
|
||||
// Ordem de apresentação (POC) por trecho entre IDR.
|
||||
// Compute presentation order (ctts composition offsets) from POC within each IDR GOP.
|
||||
let mut ctts: Option<Vec<i64>> = None;
|
||||
if samples.iter().all(|s| s.poc.is_some()) {
|
||||
let mut offs = vec![0i64; samples.len()];
|
||||
@@ -936,7 +936,7 @@ fn build_moov(
|
||||
let movie_ts = 1000u32;
|
||||
let movie_dur = media_dur * movie_ts as u64 / timescale as u64;
|
||||
|
||||
// Entrada de amostra
|
||||
// Visual sample entry (`avc1` / `hvc1`)
|
||||
let mut entry = vec![];
|
||||
entry.extend_from_slice(&[0; 6]);
|
||||
entry.extend_from_slice(&1u16.to_be_bytes());
|
||||
@@ -975,18 +975,18 @@ fn build_moov(
|
||||
Codec::Hevc => {
|
||||
let h = hevc.unwrap();
|
||||
let mut c = vec![1];
|
||||
c.extend_from_slice(&h.ptl[..12.min(h.ptl.len())]); // espaço/tier/perfil, compat, restrições, nível
|
||||
c.extend_from_slice(&h.ptl[..12.min(h.ptl.len())]); // profile_space/tier/profile_idc, profile_compatibility, constraint_indicator, level_idc
|
||||
c.truncate(13);
|
||||
while c.len() < 13 {
|
||||
c.push(0);
|
||||
}
|
||||
c.extend_from_slice(&0xF000u16.to_be_bytes()); // segmentação espacial
|
||||
c.push(0xFC); // paralelismo
|
||||
c.extend_from_slice(&0xF000u16.to_be_bytes()); // min_spatial_segmentation_idc
|
||||
c.push(0xFC); // parallelismType
|
||||
c.push(0xFC | h.chroma as u8);
|
||||
c.push(0xF8 | (h.bit_depth_luma - 8) as u8);
|
||||
c.push(0xF8 | (h.bit_depth_chroma - 8) as u8);
|
||||
c.extend_from_slice(&0u16.to_be_bytes()); // avgFrameRate
|
||||
c.push(0x0B); // constFrameRate=0, 1 camada temporal, lengthSizeMinusOne=3
|
||||
c.push(0x0B); // constantFrameRate=0, numTemporalLayers=1, lengthSizeMinusOne=3
|
||||
let arrays: [(u8, &[Vec<u8>]); 3] = [(32, vps), (33, sps), (34, pps)];
|
||||
let present: Vec<_> = arrays.iter().filter(|(_, v)| !v.is_empty()).collect();
|
||||
c.push(present.len() as u8);
|
||||
@@ -1047,7 +1047,7 @@ fn build_moov(
|
||||
.collect();
|
||||
let mut stbl_parts = vec![stsd, stts];
|
||||
if let Some(c) = ctts {
|
||||
// entradas agrupadas
|
||||
// run-length encoded entries
|
||||
let mut runs: Vec<(u32, u32)> = vec![];
|
||||
for &v in c {
|
||||
match runs.last_mut() {
|
||||
@@ -1080,7 +1080,7 @@ fn build_moov(
|
||||
let vmhd = full(b"vmhd", 0, 1, &[0; 8]);
|
||||
let minf = bx(b"minf", &[vmhd, dinf, stbl].concat());
|
||||
let mut mdhd_body = vec![];
|
||||
mdhd_body.extend_from_slice(&[0; 8]); // criação/modificação
|
||||
mdhd_body.extend_from_slice(&[0; 8]); // creation_time / modification_time
|
||||
mdhd_body.extend_from_slice(&u32b(timescale));
|
||||
mdhd_body.extend_from_slice(&u32b(media_dur as u32));
|
||||
mdhd_body.extend_from_slice(&0x55C4u16.to_be_bytes()); // "und"
|
||||
@@ -1100,8 +1100,8 @@ fn build_moov(
|
||||
tkhd_body.extend_from_slice(&[0; 4]);
|
||||
tkhd_body.extend_from_slice(&u32b(movie_dur as u32));
|
||||
tkhd_body.extend_from_slice(&[0; 8]);
|
||||
tkhd_body.extend_from_slice(&[0; 4]); // camada, grupo
|
||||
tkhd_body.extend_from_slice(&[0; 4]); // volume, reservado
|
||||
tkhd_body.extend_from_slice(&[0; 4]); // layer, alternate_group
|
||||
tkhd_body.extend_from_slice(&[0; 4]); // volume, reserved
|
||||
for m in matrix {
|
||||
tkhd_body.extend_from_slice(&u32b(m));
|
||||
}
|
||||
@@ -1136,7 +1136,7 @@ fn build_moov(
|
||||
bx(b"moov", &[mvhd, trak].concat())
|
||||
}
|
||||
|
||||
/// A unidade NAL parece de verdade? Valida o cabeçalho de fatia e o tamanho de SPS/PPS/AUD.
|
||||
/// Checks whether a candidate NAL unit looks structurally valid (slice header, SPS/PPS/AUD sizes).
|
||||
fn nal_plausible(
|
||||
codec: Codec,
|
||||
head: &[u8],
|
||||
@@ -1167,7 +1167,7 @@ fn nal_plausible(
|
||||
return false;
|
||||
}
|
||||
if let Some(sp) = sps {
|
||||
// frame_num de IDR é sempre 0
|
||||
// IDR slice `frame_num` is always 0
|
||||
if t == 5 {
|
||||
if let Some(fnum) = b.u(sp.log2_max_frame_num) {
|
||||
return fnum == 0;
|
||||
@@ -1194,7 +1194,7 @@ fn nal_plausible(
|
||||
}
|
||||
}
|
||||
|
||||
/// Quantas unidades NAL válidas seguidas (sem vãos) começam em `at` (até `max`).
|
||||
/// Counts how many consecutive valid length-prefixed NAL units start at `at` (up to `max`).
|
||||
fn chain_len(
|
||||
buf: &[u8],
|
||||
base: u64,
|
||||
@@ -1232,8 +1232,8 @@ fn chain_len(
|
||||
n
|
||||
}
|
||||
|
||||
/// Próxima posição em [from, limit) onde começa uma sequência de unidades NAL plausíveis.
|
||||
/// Prefere o primeiro ponto com duas ou mais unidades seguidas; senão, uma unidade isolada.
|
||||
/// Finds the next offset in `[from, limit)` where a plausible NAL unit sequence begins.
|
||||
/// Prefers an offset with two or more back-to-back valid NALs; falls back to a single valid NAL.
|
||||
fn scan_nal(
|
||||
src: &dyn Source,
|
||||
codec: Codec,
|
||||
@@ -1247,7 +1247,7 @@ fn scan_nal(
|
||||
if limit <= from + 8 {
|
||||
return None;
|
||||
}
|
||||
// Lê um pouco além do limite para poder seguir a cadeia de unidades.
|
||||
// Read slightly past `limit` so we can verify the following NAL in the chain.
|
||||
let want =
|
||||
((limit - from) as usize + (cap as usize).min(4 << 20)).min((data_end - from) as usize);
|
||||
let buf = read_vec(src, from, want).ok()?;
|
||||
@@ -1259,7 +1259,7 @@ fn scan_nal(
|
||||
if l2 >= 2 && l2 <= cap && q + 4 + l2 <= data_end {
|
||||
let nb = buf[i + 4];
|
||||
let good_head = match codec {
|
||||
// Um quadro novo começa por parâmetros/AUD/SEI ou por fatia do macrobloco 0.
|
||||
// A new access unit starts with parameter sets/AUD/SEI or a slice at macroblock 0.
|
||||
Codec::Avc => {
|
||||
avc_nal_ok(nb)
|
||||
&& (matches!(nb & 0x1F, 6..=9)
|
||||
|
||||
Reference in new issue
Block a user