Here is a (WIP) patch for fuse (and fuse utils), to implement FMF V2 format (or V1.5?)
(Sorry Fred, a totally forget about your idea: [patches:#390], so I did not implement your concept ... yet)-:
First of all with the new code (and format), we can record timex hires and hicolor movies. I test it with timmy.
What is new in this format:
- the whole file is double linked, so a video player (ffmpeg) can play the file natively as any ordinary video file
- There are index frames in the file, with PTS
- The screen types are changed, now we use only 'N'ormal and 'T'imex hires screens, because we do not need 'hicolor' and 'normal timex'
- The 'new frame' chunk changed
- The screen area ($ - slice) Y coord and height coded on 1 byte (instead of 2)
- The ($)lice chunk now contain a 'compressed size' length, so we can skip this chunk without decode.
- The compression scheme is changed
The fuse patch implement the new format recording.
The fuse-utils patch implement the V1 and V2 format.
Here is the description of chunks:
Fuse Movie File V2:
File Header: off len data description 0 4 "FMF_" Magic header 4 2 "V2" Version 6 1 <e|E> Endianness (e - little / E- big) 7 1 <U|4|Z> Compression ( U - uncompressed / 4 - LZ4 compressed / Z - zlib compressed ) 8 1 # Frame rate ( 1:# ) 9 1 <N|T> Screen type (Spectrum, Timex HiRes) 10 1 <A|B|C|D|E> timing code 11 1 <P|U|A> Sound encoding 12 2 Freq Sound freq in Hz 14 1 <S|M> Sound stereo / mono 15 1 "\n" padding (<new line>) Head length: 16 Data length: 0 e.g. FMF_V2eZ\001$AZ\000\175M -> little endian compressed normal screen, 48k timing, u-Law mono 32000Hz sound
Data Data chunk header off len data description 0 1 <$|S|N|T|I|x|X> Data chunk type 1 ? data chunk specific data
$ -> screen area (slice) off len data description 0 1 "$" Marker 1 1 x X coord (0-39) 2 1 y Y coord (0-239) 3 1 w width (1-40) 4 1 h height (1-240) 5 2 length-1 length of compressed data (max. 3x 1.5x40x240 -> 43200) 7 ? runlength encoded data bytes bitmap1; attrib1 ZX$/HiCol ($/C) bitmap1; bitmap2; attrib HiRes (R) runlength encoding: abcdefghbb#gg# -> two identical byte plus len code a #+2 len Head length: 7 Data length: (uint16_t)[5] + 1
S -> sound chunk off len data description 0 1 "S" Marker 1 1 <P|U|A> sound encoding type P-> 16bit signed PCM, U -> u-Law, A -> A-Law 2 2 Freq sound freq in Hz always LE 4 1 <S|M> channels S-> stereo, M-> mono 5 2 length-1 length in frames (0-65535) -> 1-65536 sound frame (always LE) 7 ? data (16bit PCM LE or BE) Head length: 7 Data length: (uint16_t)[5] + 1
I -> Index Frame marker off len data description 0 1 "I" Marker 1 2 prev-1 length of previouse frame in byte (sound and all slices up to prev I or X) 3 2 next-1 length of next frame in byte (sound and all slices up to next I or X) 5 1 high bits of lens,pts bit0, bit1 -> prev(9,10); bit2, bit3 -> next(9,10), bit4-bit7 -> pts(33-36) 6 4 pts presentation time stamp in milliseconds Head length: 10 Data length: 0 After an "I" chunk there is a New frame (N|C|R) and a full $lice 0,0 40x240 from byte offset 16. So the next chunk always a (N)ew frame (or "I")!
N|T -> New Frame (Normal or Timex) off len data description 0 1 "N"|"T" Marker 1 1 # frame rate 1:# (1:1 -> ~50/s, 1:2 -> ~25/s ...) 2 1 <A|B|C|D> frame timing code A - 16, 48, TC2048, TC2068, Scorpion, SE B - 128, +2, +2A, +3, +3E C - TS2068 D - Pentagon E - 48 NTSC Head length: 3 Data length: 0 In a frame there are no, one or several screen rectangle (and of course sound data), changed from the previouse frame. In an "N" frame there is a bitmap and an attribute, in an "R" frame there are two bitmap and an atribute part in $lices.
x -> link chunk off len data description 0 1 "x" Marker 1 2 prev-1 length of previouse frame in byte (sound and all slices up to prev I or x) 3 2 next-1 length of next frame in byte (sound and all slices up to next I, x or X) 5 1 high bits of lens bit0, bit1 -> prev(9,10); bit2, bit3 -> next(9,10) Head length: 6 Data length: 0
X -> End marker off len data description 0 1 "X" Marker 1 2 prev-1 length of previouse frame in byte (sound and all slices up to prev I or x) 3 1 high bits of length bit0, bit1 -> prev(9,10) 4 4 pts presentation time stamp in milliseconds 8 2 "\r\n" padding Head length: 10 Data length: 0 It marks the end of the last frame. So we can concatenate several FMF file without any problem...
Note1: all 16bit and 32 bit data writen as LE only sound samples writen with machine native endianness (same as in V1!)
Note2: all screen position data only 1 byte length (max y=239, max w=240)
Note3: there is no "X" Timex normal screen type (the same as $)
Note4: the whole file is "fully" double linked with "x", "I" (and "X") chunks, so we can easily fast rewind and forward
in the file without sync lost. The max distance between link chunks is 256k, ( the max compressed block length is 256k too )
Note5: recommended to insert at least one "I" frame per 1-5 sec
Note6: New frame (N|R|C chunk) exactly means: show prevoiuse frame and now start a new frame, so X "show" the very last frame
Note7: the "I" chunk marks the "prev" frame's PTS, so the just (fully) decoded one's
--Compression--
z|Z -> compressed block off len data description 0 1 "z"|"Z" Marker 1 2 prev-1 length of previouse block 3 2 next-1 length of next block in byte 5 1 high bits of lens,pts bit0, bit1 -> prev(9,10); bit2, bit3 -> next(9,10), bit4-bit7 -> pts(33-36) 6 4 pts presentation time stamp in milliseconds 10 ? raw deflate stream Head length: 10 Data length: 0
Note1: In a "Z" block (after uncomressed it) there is at least one "I" chunk.
Note2: In a "z" block (after uncomressed it) there is no any "I" chunk.
Note3: The maximum length of a compressed block is 256k
Compressed file
File header (not compressed) z 1 len1 pts [ First block start with a full screen slice ($) data chunk data chunk I len1 pts len2 after i frame, we start with a full screen slice ($) ... ] Z len1 len2 pts [ data chunk data chunk data chunk ... ] z len2 len3 pts [ data chunk data chunk data chunk ... ] ... X len1