The fp has a hardware lossless-JPEG engine. It compresses every still you take and it has never once touched a video frame. The engine works, the format is legal, the ratio is enough — what is tight is milliseconds per frame, and that number has now been measured.
fp 裡有一顆硬體的無損 JPEG 引擎。你拍的每一張照片都經過它, 而它從來沒有碰過任何一格影片。引擎是好的、格式是合法的、壓縮比也夠 —— 緊的是每一格有幾毫秒,而這個數字現在量出來了。
Before any of the arithmetic, the one thing that decides what you are allowed to plan: compression happens last.
在算任何數字之前,先記住決定你「能規劃什麼」的那件事: 壓縮發生在最後。
Measured on the camera, by wrapping the stills path's own encode call rather than cold-calling the engine. Three terms, and they genuinely separate:
在相機上量的,做法是把拍照路徑自己的編碼呼叫包起來, 而不是冷呼叫引擎。三個項,而且是真的可以拆開的:
The simulator searches this rather than looking it up, over the engine's own limits — tile width 32…512 in steps of 32, tile height even and at most 512, at most 326 tiles — plus TIFF's requirement that TileLength be a multiple of 16. That last constraint matters: without it the search picks 512×364 for FHD, which is genuinely faster and not a valid tag. With it, the search reproduces all four measured optima below exactly.
模擬器是搜尋出來的,不是查表,搜尋範圍就是引擎自己的限制 ——
tile 寬 32…512(32 的倍數)、tile 高是偶數且最多 512、最多 326 個 tile ——
再加上 TIFF 要求 TileLength 必須是 16 的倍數。
最後這個限制很關鍵:沒有它的話,搜尋會替 FHD 挑出 512×364 ——
那確實比較快,但不是合法的標籤值。加上它之後,
搜尋結果與下面四個實測最佳值完全一致。
| format格式 | best tile最佳 tile | tilestile 數 | encode編碼 | vs 256×256對比 256×256 |
|---|---|---|---|---|
| FHD 1936×1090 | 512×368 | 12 | 13.87 ms | −17.4% |
| OG3K 3024×2010 | 512×512 | 24 | 34.33 ms | −7.9% |
| UHD 3840×2160 | 480×432 | 40 | 44.91 ms | −12.9% |
| 6K 6064×4042 | 512×512 | 96 | 130.8 ms | −8.3% |
The codec and the raw path do not agree about what a sample is. The engine's setup function maps a bitdepth code to a width and refuses anything it does not recognise:
codec 跟 raw 路徑對「一個取樣有多寬」的看法不一致。 引擎的設定函式把位深代碼對應到寬度,認不得的一律拒絕:
| code代碼 | 0 | 1 | 2 | 3 | anything else其他任何值 |
|---|---|---|---|---|---|
| engine sample width引擎的取樣寬度 | 12 | 14 | 16 | 10 | rejected — the call returns failure直接拒絕 — 呼叫回傳失敗 |
The raw output path does have 8-bit — its own depth field carries 8, 10, 12 and 14, and the debug print has a branch for each. So 8-bit frames are something the camera can produce, and something the compressor will not touch.
raw 輸出那條路確實有 8-bit —— 它自己的位深欄位帶 8、10、12、14,而且除錯輸出每一種都有分支。 所以 8-bit 的畫面是相機做得出來、但壓縮器不會碰的東西。
That makes 8-bit the cheap answer to a bandwidth problem and compression the expensive one. 8-bit is lossy and instant; lossless compression keeps every bit and costs a large slice of the frame budget. Picking 8-bit in the simulator therefore switches compression off rather than stacking the two — which is what the hardware would do.
所以 8-bit 是頻寬問題的便宜解,壓縮是昂貴解。 8-bit 有損但立即;無損壓縮保留每一個位元,但吃掉一格預算裡很大一塊。 因此在模擬器裡選 8-bit 會把壓縮關掉,而不是兩者疊加 —— 硬體本來就是這樣。
Read out of the engine's own size table. It moves with the picture, and the spread is wide enough that it changes conclusions:
這些是從引擎自己的尺寸表讀出來的。它會隨畫面內容變動, 而且變動幅度大到足以改變結論:
TileByteCounts, which is the engine's own accounting.TileByteCounts 加總出來的,那是引擎自己的帳。This is the constraint that decides more than the engine speed does. In the current hook, one worker blocks on the encode, then blocks on the SD flush, and only then takes the next frame:
這個限制比引擎速度更決定成敗。在目前的 hook 裡, 一個 worker 先卡在編碼上,再卡在 SD 的 flush 上,然後才去拿下一格:
Everything above, wired together. The cost function only reads the frame size, so
the list is the camera's 13 sensor rasters (from the mode table at
0xC0B59DC0) rather than all 70 modes — the mode id never changes a
number here. Pick a raster and a frame rate independently, say how much is reduced
before the codec sees it, and the tile geometry is searched rather than guessed.
“Custom size” at the bottom of the list takes any width and height,
so you can cost a frame the camera does not currently have — it is checked
against the engine's real limits (width 8…16384 in steps of 8, height
2…16384 even, at most 326 tiles) and told to you when it falls outside
them.
把上面所有東西接起來。成本函數只讀畫面尺寸,
所以清單是相機的 13 個感光元件光柵(來自 0xC0B59DC0 的模式表),
而不是全部 70 個模式 —— mode id 在這裡不會改變任何一個數字。
光柵和格率各自獨立選,再指定進 codec 之前縮了多少,
tile 幾何是搜尋出來的而不是猜的。
清單最後的「custom size」可以輸入任意寬高,
所以你能替相機目前沒有的畫面算成本 ——
輸入會對照引擎的真實限制檢查(寬 8…16384 且是 8 的倍數、
高 2…16384 且是偶數、最多 326 個 tile),超出範圍會直接告訴你。
Two things about those numbers. Frame rate is yours to choose, not tied to the raster: every rate the mode table carries is in the list, including the exact NTSC variants (a mode that advertises 30 runs at 29.97, one that advertises 78 runs at 77.27), and the detail line tells you which rates stock firmware actually offers at the raster you picked. And the reduction is plain arithmetic on the raster — the real path also crops, which is why 3032×1708 at 1.5625× lands on 1936×1092 here against the 1936×1090 the camera writes.
關於這些數字有兩件事。格率由你選,不綁在光柵上:
模式表帶的每一個格率都在清單裡,包含精確的 NTSC 變體
(標稱 30 的模式實跑 29.97、標稱 78 的實跑 77.27),
而底下的明細行會告訴你原廠在你選的那個光柵上實際提供哪些格率。
另外,縮減在這裡是對光柵做單純的算術 —— 真實路徑還會裁切,
所以 3032×1708 在 1.5625× 之下這裡算出 1936×1092,
而相機實際寫的是 1936×1090。
Start with the compression switch. Turn it off and every number becomes the raw rate, so what compression buys is the difference between the two. The other three toggles only shape how it compresses, and at a setting where the engine has time to spare none of them move the total — that is correct, not a fault.
先動 compression 那個開關。
把它關掉,每個數字都會變成未壓縮的碼率,兩者的差就是壓縮實際換到的東西。
另外三個開關只影響怎麼壓;在一個引擎還有餘裕的設定下,
它們都不會改變 total —— 那是正確的,不是壞掉。
Compress the frames the engine can keep up with, write the rest uncompressed.
The format allows it outright — CinemaDNG is one file per frame and
Compression is a per-IFD tag, so a clip with a mix of both is valid and
no frame is dropped. Three things it does not solve:
引擎跟得上的那些幀就壓,其餘的直接寫未壓縮。
格式本身完全允許 —— CinemaDNG 是一格一個檔,
而 Compression 是每個 IFD 各自的標籤,
所以一段片子裡兩種混著存是合法的,而且不會掉任何一格。
但它有三件事解決不了:
The uncompressed frames still arrive at full rate. A card fails on sustained throughput, so “25% less on average” is not the same as “the clip that used to drop frames now does not.” Turn on partial-frame mode in the simulator to see the peak move instead.
沒被壓的那些幀還是整張全速送過來。
卡是在持續吞吐上垮掉的,所以「平均少了 25%」不等於
「本來會掉幀的片子現在不掉了」。
在模擬器裡打開「部分畫面模式」,就會看到尖峰跟著動。
The movie parser reads StripOffsets and never looks at
TileOffsets, TileWidth or Compression. The
stills path has a hardware LJPEG branch; the video path does not go through it.
A compressed clip may be export-only.
影片解析器只讀 StripOffsets,
從來不看 TileOffsets、TileWidth 或
Compression。拍照路徑有硬體 LJPEG 的分支;影片路徑不走那裡。
所以壓縮過的片子可能只能匯出,不能在機上播。
Number the files consecutively and the skipped time vanishes — motion speeds up. Keep the original numbers and you have gaps, and nobody has checked what the player does with those. Repeat the previous frame and you keep the timing but lose the I/O saving that was the point. Real variable-rate needs per-frame timestamps or a sidecar, and something that understands them.
檔名連號編下去,被跳過的時間就消失了 —— 動作會變快。
保留原本的編號就會有空號,而沒有人檢查過播放器拿到空號會怎樣。
重複前一格可以保住時間,但就失去了當初要省的 I/O。
真正的可變格率需要逐格時間戳或 sidecar,以及看得懂它們的東西。
So frame-skip cannot be hidden as an implementation detail. The three safe product shapes are: drop the whole clip one frame-rate step, mark compressed clips export-only and ship a sidecar timeline, or build a playback layer that understands variable rate. Until one of those exists this stays a research probe.
所以抽幀不能當成一個「實作細節」藏起來。
三個安全的產品形態是:整段片子降一階格率、
把壓縮過的片子標成只能匯出並附一個 sidecar 時間軸、
或者做一層看得懂可變格率的播放。
在這三者之一出現之前,這件事只是一個研究性的試探。
The simulator's third toggle compresses a fraction of each frame instead of a fraction of the frames. Arithmetically it turns the compressed fraction from a whole-frame ratchet into a continuous dial, and the useful consequence is that every frame ends up the same size — so the peak drops with the average instead of staying pinned at the uncompressed rate.
模擬器的第三個開關,壓的是每一格的一部分,而不是一部分的格。 算術上,它把「壓縮比例」從整格跳動的棘輪變成連續的旋鈕; 有用的後果是 每一格最後大小都一樣 —— 所以尖峰會跟著平均一起降,而不是一直卡在未壓縮的碼率上。
Compression=7 or an uncompressed strip; there is no half-and-half
encoding. Doing it for real means a custom container or a sidecar, which lands you
right back in the playback problem above. The toggle is there to make the
average-versus-peak difference visible, not because it can be shipped.Compression=7,要嘛是未壓縮的 strip,沒有一半一半的編碼方式。
真要做就得自訂容器或 sidecar,那又掉回上面那個播放問題。| target目標 | why理由 | |
|---|---|---|
| FHD 23.976 → SD | yes可以 | 27.3 ms serial against a 41.7 ms budget. The first thing to try.串列 27.3 ms,預算 41.7 ms。第一個該試的。 |
| FHD 29.97 → SD | likely應該可以 | Same 27.3 ms against 33.4 ms. Fine on a 94 MB/s card, marginal at 66.一樣 27.3 ms,對 33.4 ms。94 MB/s 的卡沒問題,66 就很勉強。 |
| OG3K 30p → SD | no | Engine 3% over (20% with contention), card 18% over. Both need solving and the engine has no clock knob.引擎超 3%(算爭用是 20%),卡超 18%。兩個都要解,而且引擎沒有時脈旋鈕。 |
| OG3K 30p → SSD | not needed不需要 | Uncompressed 275.6 MB/s already fits in 390. Compression buys recording time, not feasibility.未壓縮的 275.6 MB/s 本來就塞得進 390。壓縮買到的是錄影時間,不是可行性。 |
| OG3K 18/24 → SD | no | 70–85 ms per frame serial. Needs a real pipeline, not a tile change.串列每格 70–85 ms。需要的是真正的管線,不是換 tile。 |
| UHD, any rateUHD,任何格率 | no | Engine-bound at 44.91 ms. Nothing to do with the ratio.卡在引擎的 44.91 ms。跟壓縮比無關。 |
| 6K | no | 314% duty at 24p.24p 之下負載 314%。 |
Compression=7 video. This
is P0 — everything else is a probe until it is answered.Compression=7 的影片。
這是 P0 — 在它有答案之前,其餘全部都只是試探。The cost model, tile study and ratio measurements are from the in-camera lossless
work of 2026-09-15/16; the hardware inventory, engine ABI and cold-call analysis are
from the raw-compression research of 2026-08-20; the pipeline position comes from
the reduction-path work. A summary of all of it,
with the numbers this page uses, is in
research/imaging-hw/notes/LOSSLESS_DEVELOPMENT_SUMMARY.md in the
repository.
成本模型、tile 研究與壓縮比量測來自 2026-09-15/16 的機身內無損壓縮工作;
硬體清單、引擎 ABI 與冷呼叫分析來自 2026-08-20 的 raw 壓縮研究;
管線位置來自縮減路徑那邊的工作。
全部的彙整、以及這一頁用到的數字,在程式庫的
research/imaging-hw/notes/LOSSLESS_DEVELOPMENT_SUMMARY.md。