mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
tav wip
This commit is contained in:
153
terranmon.txt
153
terranmon.txt
@@ -709,6 +709,7 @@ DCT-based compression, motion compensation, and efficient temporal coding.
|
||||
uint8 Video Flags
|
||||
- bit 0 = is interlaced (should be default for most non-archival TEV videos)
|
||||
- bit 1 = is NTSC framerate (repeat every 1000th frame)
|
||||
- bit 2 = is lossless mode
|
||||
uint8 Reserved, fill with zero
|
||||
|
||||
## Packet Types
|
||||
@@ -794,6 +795,158 @@ The format is designed to be compatible with SubRip and SAMI (without markups).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
TSVM Advanced Video (TAV) Format
|
||||
Created by Claude on 2025-09-13
|
||||
|
||||
TAV is a next-generation video codec for TSVM utilizing Discrete Wavelet Transform (DWT)
|
||||
similar to JPEG2000, providing superior compression efficiency and scalability compared
|
||||
to DCT-based codecs like TEV. Features include multi-resolution encoding, progressive
|
||||
transmission capability, and region-of-interest coding.
|
||||
|
||||
## Version History
|
||||
- Version 1.0: Initial DWT-based implementation with 5/3 reversible filter
|
||||
- Version 1.1: Added 9/7 irreversible filter for higher compression
|
||||
- Version 1.2: Multi-resolution pyramid encoding with up to 4 decomposition levels
|
||||
|
||||
# File Structure
|
||||
\x1F T S V M T A V
|
||||
[HEADER]
|
||||
[PACKET 0]
|
||||
[PACKET 1]
|
||||
[PACKET 2]
|
||||
...
|
||||
|
||||
## Header (32 bytes)
|
||||
uint8 Magic[8]: "\x1FTSVM TAV"
|
||||
uint8 Version: 1
|
||||
uint16 Width: video width in pixels
|
||||
uint16 Height: video height in pixels
|
||||
uint8 FPS: frames per second
|
||||
uint32 Total Frames: number of video frames
|
||||
uint8 Wavelet Filter Type: 0=5/3 reversible, 1=9/7 irreversible
|
||||
uint8 Decomposition Levels: number of DWT levels (1-4)
|
||||
uint8 Quality Index for Y channel (0-99; 100 denotes lossless)
|
||||
uint8 Quality Index for Co channel (0-99; 100 denotes lossless)
|
||||
uint8 Quality Index for Cg channel (0-99; 100 denotes lossless)
|
||||
uint8 Extra Feature Flags
|
||||
- bit 0 = has audio
|
||||
- bit 1 = has subtitle
|
||||
- bit 2 = progressive transmission enabled
|
||||
- bit 3 = region-of-interest coding enabled
|
||||
uint8 Video Flags
|
||||
- bit 0 = is interlaced
|
||||
- bit 1 = is NTSC framerate
|
||||
- bit 2 = is lossless mode
|
||||
- bit 3 = multi-resolution encoding
|
||||
uint8 Reserved[7]: fill with zeros
|
||||
|
||||
## Packet Types
|
||||
0x10: I-frame (intra-coded frame)
|
||||
0x11: P-frame (predicted frame with motion compensation)
|
||||
0x20: MP2 audio packet
|
||||
0x30: Subtitle in "Simple" format
|
||||
0xFF: sync packet
|
||||
|
||||
## Video Packet Structure
|
||||
uint8 Packet Type
|
||||
uint32 Compressed Size
|
||||
* Zstd-compressed Block Data
|
||||
|
||||
## Block Data (per 64x64 tile)
|
||||
uint8 Mode: encoding mode
|
||||
0x00 = SKIP (copy from previous frame)
|
||||
0x01 = INTRA (DWT-coded, no prediction)
|
||||
0x02 = INTER (DWT-coded with motion compensation)
|
||||
0x03 = MOTION (motion vector only, no residual)
|
||||
int16 Motion Vector X (1/4 pixel precision)
|
||||
int16 Motion Vector Y (1/4 pixel precision)
|
||||
float32 Rate Control Factor (4 bytes, little-endian)
|
||||
|
||||
## DWT Coefficient Structure (per tile)
|
||||
For each decomposition level L (from highest to lowest):
|
||||
uint16 LL_size: size of LL subband coefficients
|
||||
uint16 LH_size: size of LH subband coefficients
|
||||
uint16 HL_size: size of HL subband coefficients
|
||||
uint16 HH_size: size of HH subband coefficients
|
||||
int16[] LL_coeffs: quantized LL subband (low-low frequencies)
|
||||
int16[] LH_coeffs: quantized LH subband (low-high frequencies)
|
||||
int16[] HL_coeffs: quantized HL subband (high-low frequencies)
|
||||
int16[] HH_coeffs: quantized HH subband (high-high frequencies)
|
||||
|
||||
## DWT Implementation Details
|
||||
|
||||
### Wavelet Filters
|
||||
- 5/3 Reversible Filter (lossless capable):
|
||||
* Analysis: Low-pass [1/2, 1, 1/2], High-pass [-1/8, -1/4, 3/4, -1/4, -1/8]
|
||||
* Synthesis: Low-pass [1/4, 1/2, 1/4], High-pass [-1/16, -1/8, 3/8, -1/8, -1/16]
|
||||
|
||||
- 9/7 Irreversible Filter (higher compression):
|
||||
* Analysis: Daubechies 9/7 coefficients optimized for image compression
|
||||
* Provides better energy compaction than 5/3 but lossy reconstruction
|
||||
|
||||
### Decomposition Levels
|
||||
- Level 1: 64x64 → 32x32 (LL) + 3×32x32 subbands (LH,HL,HH)
|
||||
- Level 2: 32x32 → 16x16 (LL) + 3×16x16 subbands
|
||||
- Level 3: 16x16 → 8x8 (LL) + 3×8x8 subbands
|
||||
- Level 4: 8x8 → 4x4 (LL) + 3×4x4 subbands
|
||||
|
||||
### Quantization Strategy
|
||||
TAV uses different quantization steps for each subband based on human visual
|
||||
system sensitivity:
|
||||
- LL subbands: Fine quantization (preserve DC and low frequencies)
|
||||
- LH/HL subbands: Medium quantization (diagonal details less critical)
|
||||
- HH subbands: Coarse quantization (high frequency noise can be discarded)
|
||||
|
||||
### Progressive Transmission
|
||||
When enabled, coefficients are transmitted in order of visual importance:
|
||||
1. LL subband of highest decomposition level (thumbnail)
|
||||
2. Lower frequency subbands first
|
||||
3. Higher frequency subbands for refinement
|
||||
|
||||
## Motion Compensation
|
||||
- Search range: ±16 pixels (larger than TEV due to 64x64 tiles)
|
||||
- Sub-pixel precision: 1/4 pixel with bilinear interpolation
|
||||
- Tile size: 64x64 pixels (4x larger than TEV blocks)
|
||||
- Uses Sum of Absolute Differences (SAD) for motion estimation
|
||||
- Overlapped block motion compensation (OBMC) for smooth boundaries
|
||||
|
||||
## Colour Space
|
||||
TAV operates in YCoCg-R colour space with full resolution channels:
|
||||
- Y: Luma channel (full resolution, fine quantization)
|
||||
- Co: Orange-Cyan chroma (full resolution, aggressive quantization by default)
|
||||
- Cg: Green-Magenta chroma (full resolution, very aggressive quantization by default)
|
||||
|
||||
## Compression Features
|
||||
- 64x64 DWT tiles vs 16x16 DCT blocks in TEV
|
||||
- Multi-resolution representation enables scalable decoding
|
||||
- Better frequency localization than DCT
|
||||
- Reduced blocking artifacts due to overlapping basis functions
|
||||
- Region-of-Interest (ROI) coding for selective quality enhancement
|
||||
- Progressive transmission for bandwidth adaptation
|
||||
|
||||
## Performance Comparison
|
||||
Expected improvements over TEV:
|
||||
- 20-30% better compression efficiency
|
||||
- Reduced blocking artifacts
|
||||
- Scalable quality/resolution decoding
|
||||
- Better performance on natural images vs artificial content
|
||||
- Full resolution chroma preserves color detail while aggressive quantization maintains compression
|
||||
|
||||
## Hardware Acceleration Functions
|
||||
TAV decoder requires new GraphicsJSR223Delegate functions:
|
||||
- tavDecode(): Main DWT decoding function
|
||||
- tavDWT2D(): 2D DWT/IDWT transforms
|
||||
- tavQuantize(): Multi-band quantization
|
||||
- tavMotionCompensate(): 64x64 tile motion compensation
|
||||
|
||||
## Audio Support
|
||||
Reuses existing MP2 audio infrastructure from TEV/MOV formats for compatibility.
|
||||
|
||||
## Subtitle Support
|
||||
Uses same Simple Subtitle Format (SSF) as TEV for text overlay functionality.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Sound Adapter
|
||||
|
||||
Endianness: little
|
||||
|
||||
@@ -4023,4 +4023,409 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// TAV (TSVM Advanced Video) Hardware Acceleration Functions
|
||||
// =============================================================================
|
||||
|
||||
// 5/3 Reversible wavelet filter coefficients
|
||||
private val wavelet53LP = floatArrayOf(0.5f, 1.0f, 0.5f)
|
||||
private val wavelet53HP = floatArrayOf(-0.125f, -0.25f, 0.75f, -0.25f, -0.125f)
|
||||
|
||||
// 9/7 Irreversible wavelet filter coefficients (Daubechies)
|
||||
private val wavelet97LP = floatArrayOf(
|
||||
0.037828455507f, -0.023849465020f, -0.110624404418f, 0.377402855613f,
|
||||
0.852698679009f, 0.377402855613f, -0.110624404418f, -0.023849465020f, 0.037828455507f
|
||||
)
|
||||
private val wavelet97HP = floatArrayOf(
|
||||
0.064538882629f, -0.040689417609f, -0.418092273222f, 0.788485616406f,
|
||||
-0.418092273222f, -0.040689417609f, 0.064538882629f
|
||||
)
|
||||
|
||||
// Working buffers for DWT processing
|
||||
private val dwtTempBuffer = FloatArray(64 * 64)
|
||||
private val dwtSubbandLL = FloatArray(32 * 32)
|
||||
private val dwtSubbandLH = FloatArray(32 * 32)
|
||||
private val dwtSubbandHL = FloatArray(32 * 32)
|
||||
private val dwtSubbandHH = FloatArray(32 * 32)
|
||||
|
||||
/**
|
||||
* Main TAV decoder function - processes compressed TAV tile data
|
||||
* Called from JavaScript playtav.js decoder
|
||||
*/
|
||||
fun tavDecode(
|
||||
compressedDataPtr: Long,
|
||||
currentYPtr: Long, currentCoPtr: Long, currentCgPtr: Long,
|
||||
prevYPtr: Long, prevCoPtr: Long, prevCgPtr: Long,
|
||||
width: Int, height: Int,
|
||||
qY: Int, qCo: Int, qCg: Int,
|
||||
frameCounter: Int,
|
||||
debugMotionVectors: Boolean = false,
|
||||
waveletFilter: Int = 1,
|
||||
decompLevels: Int = 3,
|
||||
enableDeblocking: Boolean = true,
|
||||
isLossless: Boolean = false
|
||||
): Boolean {
|
||||
try {
|
||||
val tilesX = (width + 63) / 64 // 64x64 tiles
|
||||
val tilesY = (height + 63) / 64
|
||||
|
||||
// TODO: Decompress zstd data (placeholder)
|
||||
// val decompressedData = decompressZstd(compressedDataPtr)
|
||||
|
||||
// Process each tile
|
||||
for (tileY in 0 until tilesY) {
|
||||
for (tileX in 0 until tilesX) {
|
||||
val tileIdx = tileY * tilesX + tileX
|
||||
|
||||
// Read tile header (mode, motion vectors, rate control factor)
|
||||
// TODO: Parse actual tile data format
|
||||
val mode = 0x01 // TAV_MODE_INTRA (placeholder)
|
||||
val mvX = 0
|
||||
val mvY = 0
|
||||
val rcf = 1.0f
|
||||
|
||||
when (mode) {
|
||||
0x00 -> { // TAV_MODE_SKIP
|
||||
// Copy from previous frame
|
||||
copyTileFromPrevious(
|
||||
tileX, tileY,
|
||||
currentYPtr, currentCoPtr, currentCgPtr,
|
||||
prevYPtr, prevCoPtr, prevCgPtr,
|
||||
width, height
|
||||
)
|
||||
}
|
||||
0x01 -> { // TAV_MODE_INTRA
|
||||
// Decode DWT coefficients and reconstruct tile
|
||||
decodeDWTTile(
|
||||
tileX, tileY,
|
||||
currentYPtr, currentCoPtr, currentCgPtr,
|
||||
width, height,
|
||||
qY, qCo, qCg, rcf,
|
||||
waveletFilter, decompLevels,
|
||||
isLossless
|
||||
)
|
||||
}
|
||||
0x02 -> { // TAV_MODE_INTER
|
||||
// Decode DWT residual and apply motion compensation
|
||||
decodeDWTTileWithMotion(
|
||||
tileX, tileY, mvX, mvY,
|
||||
currentYPtr, currentCoPtr, currentCgPtr,
|
||||
prevYPtr, prevCoPtr, prevCgPtr,
|
||||
width, height,
|
||||
qY, qCo, qCg, rcf,
|
||||
waveletFilter, decompLevels,
|
||||
isLossless
|
||||
)
|
||||
}
|
||||
0x03 -> { // TAV_MODE_MOTION
|
||||
// Motion compensation only
|
||||
applyMotionCompensation64x64(
|
||||
tileX, tileY, mvX, mvY,
|
||||
currentYPtr, currentCoPtr, currentCgPtr,
|
||||
prevYPtr, prevCoPtr, prevCgPtr,
|
||||
width, height
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert YCoCg to RGB and render to display
|
||||
renderYCoCgToDisplay(
|
||||
currentYPtr, currentCoPtr, currentCgPtr,
|
||||
width, height
|
||||
)
|
||||
|
||||
return true
|
||||
|
||||
} catch (e: Exception) {
|
||||
println("TAV decode error: ${e.message}")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 2D DWT forward/inverse transform
|
||||
* Supports both 5/3 reversible and 9/7 irreversible filters
|
||||
*/
|
||||
fun tavDWT2D(
|
||||
inputPtr: Long, outputPtr: Long,
|
||||
width: Int, height: Int,
|
||||
levels: Int, filterType: Int,
|
||||
isForward: Boolean
|
||||
) {
|
||||
// Copy input data to working buffer
|
||||
for (i in 0 until width * height) {
|
||||
dwtTempBuffer[i] = UnsafeHelper.getFloat(inputPtr + i * 4L)
|
||||
}
|
||||
|
||||
if (isForward) {
|
||||
// Forward DWT - decompose into subbands
|
||||
for (level in 0 until levels) {
|
||||
val levelWidth = width shr level
|
||||
val levelHeight = height shr level
|
||||
|
||||
if (filterType == 0) {
|
||||
applyDWT53Forward(dwtTempBuffer, levelWidth, levelHeight)
|
||||
} else {
|
||||
applyDWT97Forward(dwtTempBuffer, levelWidth, levelHeight)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Inverse DWT - reconstruct from subbands
|
||||
for (level in levels - 1 downTo 0) {
|
||||
val levelWidth = width shr level
|
||||
val levelHeight = height shr level
|
||||
|
||||
if (filterType == 0) {
|
||||
applyDWT53Inverse(dwtTempBuffer, levelWidth, levelHeight)
|
||||
} else {
|
||||
applyDWT97Inverse(dwtTempBuffer, levelWidth, levelHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy result to output
|
||||
for (i in 0 until width * height) {
|
||||
UnsafeHelper.setFloat(outputPtr + i * 4L, dwtTempBuffer[i])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-band quantization for DWT subbands
|
||||
*/
|
||||
fun tavQuantize(
|
||||
subbandPtr: Long, quantTable: IntArray,
|
||||
width: Int, height: Int,
|
||||
isInverse: Boolean
|
||||
) {
|
||||
val size = width * height
|
||||
|
||||
if (isInverse) {
|
||||
// Dequantization
|
||||
for (i in 0 until size) {
|
||||
val quantized = UnsafeHelper.getShort(subbandPtr + i * 2L).toInt()
|
||||
val dequantized = quantized * quantTable[i % quantTable.size]
|
||||
UnsafeHelper.setFloat(subbandPtr + i * 4L, dequantized.toFloat())
|
||||
}
|
||||
} else {
|
||||
// Quantization
|
||||
for (i in 0 until size) {
|
||||
val value = UnsafeHelper.getFloat(subbandPtr + i * 4L)
|
||||
val quantized = (value / quantTable[i % quantTable.size]).toInt()
|
||||
UnsafeHelper.setShort(subbandPtr + i * 2L, quantized.toShort())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 64x64 tile motion compensation with bilinear interpolation
|
||||
*/
|
||||
fun tavMotionCompensate64x64(
|
||||
currentTilePtr: Long, refFramePtr: Long,
|
||||
tileX: Int, tileY: Int,
|
||||
mvX: Int, mvY: Int,
|
||||
width: Int, height: Int
|
||||
) {
|
||||
val tileSize = 64
|
||||
val startX = tileX * tileSize
|
||||
val startY = tileY * tileSize
|
||||
|
||||
// Motion vector in 1/4 pixel precision
|
||||
val refX = startX + (mvX / 4.0f)
|
||||
val refY = startY + (mvY / 4.0f)
|
||||
|
||||
for (y in 0 until tileSize) {
|
||||
for (x in 0 until tileSize) {
|
||||
val currentPixelIdx = (startY + y) * width + (startX + x)
|
||||
|
||||
if (currentPixelIdx >= 0 && currentPixelIdx < width * height) {
|
||||
// Bilinear interpolation for sub-pixel motion vectors
|
||||
val interpolatedValue = bilinearInterpolate(
|
||||
refFramePtr, width, height,
|
||||
refX + x, refY + y
|
||||
)
|
||||
|
||||
UnsafeHelper.setFloat(
|
||||
currentTilePtr + currentPixelIdx * 4L,
|
||||
interpolatedValue
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Private helper functions for TAV implementation
|
||||
|
||||
private fun copyTileFromPrevious(
|
||||
tileX: Int, tileY: Int,
|
||||
currentYPtr: Long, currentCoPtr: Long, currentCgPtr: Long,
|
||||
prevYPtr: Long, prevCoPtr: Long, prevCgPtr: Long,
|
||||
width: Int, height: Int
|
||||
) {
|
||||
val tileSize = 64
|
||||
val startX = tileX * tileSize
|
||||
val startY = tileY * tileSize
|
||||
|
||||
for (y in 0 until tileSize) {
|
||||
for (x in 0 until tileSize) {
|
||||
val pixelIdx = (startY + y) * width + (startX + x)
|
||||
if (pixelIdx >= 0 && pixelIdx < width * height) {
|
||||
val prevY = UnsafeHelper.getFloat(prevYPtr + pixelIdx * 4L)
|
||||
val prevCo = UnsafeHelper.getFloat(prevCoPtr + pixelIdx * 4L)
|
||||
val prevCg = UnsafeHelper.getFloat(prevCgPtr + pixelIdx * 4L)
|
||||
|
||||
UnsafeHelper.setFloat(currentYPtr + pixelIdx * 4L, prevY)
|
||||
UnsafeHelper.setFloat(currentCoPtr + pixelIdx * 4L, prevCo)
|
||||
UnsafeHelper.setFloat(currentCgPtr + pixelIdx * 4L, prevCg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun decodeDWTTile(
|
||||
tileX: Int, tileY: Int,
|
||||
currentYPtr: Long, currentCoPtr: Long, currentCgPtr: Long,
|
||||
width: Int, height: Int,
|
||||
qY: Int, qCo: Int, qCg: Int, rcf: Float,
|
||||
waveletFilter: Int, decompLevels: Int,
|
||||
isLossless: Boolean
|
||||
) {
|
||||
// TODO: Implement DWT tile decoding
|
||||
// 1. Read DWT coefficients from compressed data
|
||||
// 2. Dequantize subbands according to quality settings
|
||||
// 3. Apply inverse DWT to reconstruct 64x64 tile
|
||||
// 4. Copy reconstructed data to frame buffers
|
||||
|
||||
// Placeholder implementation
|
||||
val tileSize = 64
|
||||
val startX = tileX * tileSize
|
||||
val startY = tileY * tileSize
|
||||
|
||||
for (y in 0 until tileSize) {
|
||||
for (x in 0 until tileSize) {
|
||||
val pixelIdx = (startY + y) * width + (startX + x)
|
||||
if (pixelIdx >= 0 && pixelIdx < width * height) {
|
||||
// Placeholder: set to mid-gray
|
||||
UnsafeHelper.setFloat(currentYPtr + pixelIdx * 4L, 128.0f)
|
||||
UnsafeHelper.setFloat(currentCoPtr + pixelIdx * 4L, 0.0f)
|
||||
UnsafeHelper.setFloat(currentCgPtr + pixelIdx * 4L, 0.0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun decodeDWTTileWithMotion(
|
||||
tileX: Int, tileY: Int, mvX: Int, mvY: Int,
|
||||
currentYPtr: Long, currentCoPtr: Long, currentCgPtr: Long,
|
||||
prevYPtr: Long, prevCoPtr: Long, prevCgPtr: Long,
|
||||
width: Int, height: Int,
|
||||
qY: Int, qCo: Int, qCg: Int, rcf: Float,
|
||||
waveletFilter: Int, decompLevels: Int,
|
||||
isLossless: Boolean
|
||||
) {
|
||||
// TODO: Implement DWT residual decoding with motion compensation
|
||||
// 1. Apply motion compensation from previous frame
|
||||
// 2. Decode DWT residual coefficients
|
||||
// 3. Add residual to motion-compensated prediction
|
||||
|
||||
// Placeholder: apply motion compensation only
|
||||
applyMotionCompensation64x64(
|
||||
tileX, tileY, mvX, mvY,
|
||||
currentYPtr, currentCoPtr, currentCgPtr,
|
||||
prevYPtr, prevCoPtr, prevCgPtr,
|
||||
width, height
|
||||
)
|
||||
}
|
||||
|
||||
private fun applyMotionCompensation64x64(
|
||||
tileX: Int, tileY: Int, mvX: Int, mvY: Int,
|
||||
currentYPtr: Long, currentCoPtr: Long, currentCgPtr: Long,
|
||||
prevYPtr: Long, prevCoPtr: Long, prevCgPtr: Long,
|
||||
width: Int, height: Int
|
||||
) {
|
||||
tavMotionCompensate64x64(currentYPtr, prevYPtr, tileX, tileY, mvX, mvY, width, height)
|
||||
tavMotionCompensate64x64(currentCoPtr, prevCoPtr, tileX, tileY, mvX, mvY, width, height)
|
||||
tavMotionCompensate64x64(currentCgPtr, prevCgPtr, tileX, tileY, mvX, mvY, width, height)
|
||||
}
|
||||
|
||||
private fun applyDWT53Forward(data: FloatArray, width: Int, height: Int) {
|
||||
// TODO: Implement 5/3 forward DWT
|
||||
// Lifting scheme implementation for 5/3 reversible filter
|
||||
}
|
||||
|
||||
private fun applyDWT53Inverse(data: FloatArray, width: Int, height: Int) {
|
||||
// TODO: Implement 5/3 inverse DWT
|
||||
// Lifting scheme implementation for 5/3 reversible filter
|
||||
}
|
||||
|
||||
private fun applyDWT97Forward(data: FloatArray, width: Int, height: Int) {
|
||||
// TODO: Implement 9/7 forward DWT
|
||||
// Lifting scheme implementation for 9/7 irreversible filter
|
||||
}
|
||||
|
||||
private fun applyDWT97Inverse(data: FloatArray, width: Int, height: Int) {
|
||||
// TODO: Implement 9/7 inverse DWT
|
||||
// Lifting scheme implementation for 9/7 irreversible filter
|
||||
}
|
||||
|
||||
private fun bilinearInterpolate(
|
||||
dataPtr: Long, width: Int, height: Int,
|
||||
x: Float, y: Float
|
||||
): Float {
|
||||
val x0 = floor(x).toInt()
|
||||
val y0 = floor(y).toInt()
|
||||
val x1 = x0 + 1
|
||||
val y1 = y0 + 1
|
||||
|
||||
if (x0 < 0 || y0 < 0 || x1 >= width || y1 >= height) {
|
||||
return 0.0f // Out of bounds
|
||||
}
|
||||
|
||||
val fx = x - x0
|
||||
val fy = y - y0
|
||||
|
||||
val p00 = UnsafeHelper.getFloat(dataPtr + (y0 * width + x0) * 4L)
|
||||
val p10 = UnsafeHelper.getFloat(dataPtr + (y0 * width + x1) * 4L)
|
||||
val p01 = UnsafeHelper.getFloat(dataPtr + (y1 * width + x0) * 4L)
|
||||
val p11 = UnsafeHelper.getFloat(dataPtr + (y1 * width + x1) * 4L)
|
||||
|
||||
return p00 * (1 - fx) * (1 - fy) +
|
||||
p10 * fx * (1 - fy) +
|
||||
p01 * (1 - fx) * fy +
|
||||
p11 * fx * fy
|
||||
}
|
||||
|
||||
private fun renderYCoCgToDisplay(
|
||||
yPtr: Long, coPtr: Long, cgPtr: Long,
|
||||
width: Int, height: Int
|
||||
) {
|
||||
// Convert YCoCg to RGB and render to display
|
||||
val adapter = vm.getPeripheralByClass(GraphicsAdapter::class.java)
|
||||
if (adapter != null) {
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val idx = y * width + x
|
||||
val Y = UnsafeHelper.getFloat(yPtr + idx * 4L)
|
||||
val Co = UnsafeHelper.getFloat(coPtr + idx * 4L)
|
||||
val Cg = UnsafeHelper.getFloat(cgPtr + idx * 4L)
|
||||
|
||||
// YCoCg to RGB conversion
|
||||
val tmp = Y - Cg
|
||||
val G = Y + Cg
|
||||
val B = tmp - Co
|
||||
val R = tmp + Co
|
||||
|
||||
// Clamp to 0-255 and convert to 4-bit RGB for TSVM display
|
||||
val r4 = (R.toInt().coerceIn(0, 255) / 16).coerceIn(0, 15)
|
||||
val g4 = (G.toInt().coerceIn(0, 255) / 16).coerceIn(0, 15)
|
||||
val b4 = (B.toInt().coerceIn(0, 255) / 16).coerceIn(0, 15)
|
||||
|
||||
val color4096 = (r4 shl 8) or (g4 shl 4) or b4
|
||||
adapter.setPixel(x, y, color4096)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
505
video_encoder/encoder_tav.c
Normal file
505
video_encoder/encoder_tav.c
Normal file
@@ -0,0 +1,505 @@
|
||||
// Created by Claude on 2025-09-13.
|
||||
// TAV (TSVM Advanced Video) Encoder - DWT-based compression with full resolution YCoCg-R
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <zstd.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <getopt.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
// Float16 conversion functions (same as TEV)
|
||||
static inline uint16_t float_to_float16(float fval) {
|
||||
uint32_t fbits = *(uint32_t*)&fval;
|
||||
uint16_t sign = (fbits >> 16) & 0x8000;
|
||||
uint32_t val = (fbits & 0x7fffffff) + 0x1000;
|
||||
|
||||
if (val >= 0x47800000) {
|
||||
if ((fbits & 0x7fffffff) >= 0x47800000) {
|
||||
if (val < 0x7f800000)
|
||||
return sign | 0x7c00;
|
||||
return sign | 0x7c00 | ((fbits & 0x007fffff) >> 13);
|
||||
}
|
||||
return sign | 0x7bff;
|
||||
}
|
||||
if (val >= 0x38800000)
|
||||
return sign | ((val - 0x38000000) >> 13);
|
||||
if (val < 0x33000000)
|
||||
return sign;
|
||||
val = (fbits & 0x7fffffff) >> 23;
|
||||
|
||||
return sign | (((fbits & 0x7fffff) | 0x800000) +
|
||||
(0x800000 >> (val - 102))
|
||||
) >> (126 - val);
|
||||
}
|
||||
|
||||
static inline float float16_to_float(uint16_t hbits) {
|
||||
uint32_t mant = hbits & 0x03ff;
|
||||
uint32_t exp = hbits & 0x7c00;
|
||||
|
||||
if (exp == 0x7c00)
|
||||
exp = 0x3fc00;
|
||||
else if (exp != 0) {
|
||||
exp += 0x1c000;
|
||||
if (mant == 0 && exp > 0x1c400) {
|
||||
uint32_t fbits = ((hbits & 0x8000) << 16) | (exp << 13) | 0x3ff;
|
||||
return *(float*)&fbits;
|
||||
}
|
||||
}
|
||||
else if (mant != 0) {
|
||||
exp = 0x1c400;
|
||||
do {
|
||||
mant <<= 1;
|
||||
exp -= 0x400;
|
||||
} while ((mant & 0x400) == 0);
|
||||
mant &= 0x3ff;
|
||||
}
|
||||
|
||||
uint32_t fbits = ((hbits & 0x8000) << 16) | ((exp | mant) << 13);
|
||||
return *(float*)&fbits;
|
||||
}
|
||||
|
||||
// TSVM Advanced Video (TAV) format constants
|
||||
#define TAV_MAGIC "\x1F\x54\x53\x56\x4D\x54\x41\x56" // "\x1FTSVM TAV"
|
||||
#define TAV_VERSION 1 // Initial DWT implementation
|
||||
|
||||
// Tile encoding modes (64x64 tiles)
|
||||
#define TAV_MODE_SKIP 0x00 // Skip tile (copy from reference)
|
||||
#define TAV_MODE_INTRA 0x01 // Intra DWT coding (I-frame tiles)
|
||||
#define TAV_MODE_INTER 0x02 // Inter DWT coding with motion compensation
|
||||
#define TAV_MODE_MOTION 0x03 // Motion vector only (good prediction)
|
||||
|
||||
// Video packet types
|
||||
#define TAV_PACKET_IFRAME 0x10 // Intra frame (keyframe)
|
||||
#define TAV_PACKET_PFRAME 0x11 // Predicted frame
|
||||
#define TAV_PACKET_AUDIO_MP2 0x20 // MP2 audio
|
||||
#define TAV_PACKET_SUBTITLE 0x30 // Subtitle packet
|
||||
#define TAV_PACKET_SYNC 0xFF // Sync packet
|
||||
|
||||
// DWT settings
|
||||
#define TILE_SIZE 64
|
||||
#define MAX_DECOMP_LEVELS 4
|
||||
#define DEFAULT_DECOMP_LEVELS 3
|
||||
|
||||
// Wavelet filter types
|
||||
#define WAVELET_5_3_REVERSIBLE 0 // Lossless capable
|
||||
#define WAVELET_9_7_IRREVERSIBLE 1 // Higher compression
|
||||
|
||||
// Default settings
|
||||
#define DEFAULT_WIDTH 560
|
||||
#define DEFAULT_HEIGHT 448
|
||||
#define DEFAULT_FPS 30
|
||||
#define DEFAULT_QUALITY 2
|
||||
|
||||
static void generate_random_filename(char *filename) {
|
||||
srand(time(NULL));
|
||||
|
||||
const char charset[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const int charset_size = sizeof(charset) - 1;
|
||||
|
||||
// Start with the prefix
|
||||
strcpy(filename, "/tmp/");
|
||||
|
||||
// Generate 32 random characters
|
||||
for (int i = 0; i < 32; i++) {
|
||||
filename[5 + i] = charset[rand() % charset_size];
|
||||
}
|
||||
|
||||
// Add the .mp2 extension
|
||||
strcpy(filename + 37, ".mp2");
|
||||
filename[41] = '\0'; // Null terminate
|
||||
}
|
||||
|
||||
char TEMP_AUDIO_FILE[42];
|
||||
|
||||
|
||||
// Utility macros
|
||||
static inline int CLAMP(int x, int min, int max) {
|
||||
return x < min ? min : (x > max ? max : x);
|
||||
}
|
||||
static inline float FCLAMP(float x, float min, float max) {
|
||||
return x < min ? min : (x > max ? max : x);
|
||||
}
|
||||
|
||||
// MP2 audio rate table (same as TEV)
|
||||
static const int MP2_RATE_TABLE[] = {128, 160, 224, 320, 384, 384};
|
||||
|
||||
// Quality level to quantization mapping for different channels
|
||||
static const int QUALITY_Y[] = {90, 70, 50, 30, 15, 5}; // Luma (fine)
|
||||
static const int QUALITY_CO[] = {80, 60, 40, 20, 10, 3}; // Chroma Co (aggressive)
|
||||
static const int QUALITY_CG[] = {70, 50, 30, 15, 8, 2}; // Chroma Cg (very aggressive)
|
||||
|
||||
// DWT coefficient structure for each subband
|
||||
typedef struct {
|
||||
int16_t *coeffs;
|
||||
int width, height;
|
||||
int size;
|
||||
} dwt_subband_t;
|
||||
|
||||
// DWT tile structure
|
||||
typedef struct {
|
||||
dwt_subband_t *ll, *lh, *hl, *hh; // Subbands for each level
|
||||
int decomp_levels;
|
||||
int tile_x, tile_y;
|
||||
} dwt_tile_t;
|
||||
|
||||
// Motion vector structure
|
||||
typedef struct {
|
||||
int16_t mv_x, mv_y; // 1/4 pixel precision
|
||||
float rate_control_factor;
|
||||
} motion_vector_t;
|
||||
|
||||
// TAV encoder structure
|
||||
typedef struct {
|
||||
// Input/output files
|
||||
char *input_file;
|
||||
char *output_file;
|
||||
char *subtitle_file;
|
||||
FILE *output_fp;
|
||||
FILE *mp2_file;
|
||||
FILE *ffmpeg_video_pipe;
|
||||
|
||||
// Video parameters
|
||||
int width, height;
|
||||
int fps;
|
||||
int total_frames;
|
||||
int frame_count;
|
||||
|
||||
// Encoding parameters
|
||||
int quality_level;
|
||||
int quantizer_y, quantizer_co, quantizer_cg;
|
||||
int wavelet_filter;
|
||||
int decomp_levels;
|
||||
int bitrate_mode;
|
||||
int target_bitrate;
|
||||
|
||||
// Flags
|
||||
int progressive;
|
||||
int lossless;
|
||||
int enable_rcf;
|
||||
int enable_progressive_transmission;
|
||||
int enable_roi;
|
||||
int verbose;
|
||||
int test_mode;
|
||||
|
||||
// Frame buffers
|
||||
uint8_t *current_frame_rgb;
|
||||
uint8_t *previous_frame_rgb;
|
||||
float *current_frame_y, *current_frame_co, *current_frame_cg;
|
||||
float *previous_frame_y, *previous_frame_co, *previous_frame_cg;
|
||||
|
||||
// Tile processing
|
||||
int tiles_x, tiles_y;
|
||||
dwt_tile_t *tiles;
|
||||
motion_vector_t *motion_vectors;
|
||||
|
||||
// Compression
|
||||
ZSTD_CCtx *zstd_ctx;
|
||||
void *compressed_buffer;
|
||||
size_t compressed_buffer_size;
|
||||
|
||||
// Statistics
|
||||
size_t total_compressed_size;
|
||||
size_t total_uncompressed_size;
|
||||
|
||||
} tav_encoder_t;
|
||||
|
||||
// 5/3 Wavelet filter coefficients (reversible)
|
||||
static const float WAVELET_5_3_LP[] = {0.5f, 1.0f, 0.5f};
|
||||
static const float WAVELET_5_3_HP[] = {-0.125f, -0.25f, 0.75f, -0.25f, -0.125f};
|
||||
|
||||
// 9/7 Wavelet filter coefficients (irreversible - Daubechies)
|
||||
static const float WAVELET_9_7_LP[] = {
|
||||
0.037828455507f, -0.023849465020f, -0.110624404418f, 0.377402855613f,
|
||||
0.852698679009f, 0.377402855613f, -0.110624404418f, -0.023849465020f, 0.037828455507f
|
||||
};
|
||||
static const float WAVELET_9_7_HP[] = {
|
||||
0.064538882629f, -0.040689417609f, -0.418092273222f, 0.788485616406f,
|
||||
-0.418092273222f, -0.040689417609f, 0.064538882629f
|
||||
};
|
||||
|
||||
// Function prototypes
|
||||
static void show_usage(const char *program_name);
|
||||
static tav_encoder_t* create_encoder(void);
|
||||
static void cleanup_encoder(tav_encoder_t *enc);
|
||||
static int initialize_encoder(tav_encoder_t *enc);
|
||||
static int encode_frame(tav_encoder_t *enc, int frame_num, int is_keyframe);
|
||||
static void rgb_to_ycocg(const uint8_t *rgb, float *y, float *co, float *cg, int width, int height);
|
||||
static void dwt_2d_forward(float *input, dwt_tile_t *tile, int filter_type);
|
||||
static void dwt_2d_inverse(dwt_tile_t *tile, float *output, int filter_type);
|
||||
static void quantize_subbands(dwt_tile_t *tile, int q_y, int q_co, int q_cg, float rcf);
|
||||
static int estimate_motion_64x64(const float *current, const float *reference,
|
||||
int width, int height, int tile_x, int tile_y,
|
||||
motion_vector_t *mv);
|
||||
static size_t compress_tile_data(tav_encoder_t *enc, const dwt_tile_t *tiles,
|
||||
const motion_vector_t *mvs, int num_tiles,
|
||||
uint8_t packet_type);
|
||||
|
||||
// Show usage information
|
||||
static void show_usage(const char *program_name) {
|
||||
printf("TAV DWT-based Video Encoder\n");
|
||||
printf("Usage: %s [options] -i input.mp4 -o output.tav\n\n", program_name);
|
||||
printf("Options:\n");
|
||||
printf(" -i, --input FILE Input video file\n");
|
||||
printf(" -o, --output FILE Output video file (use '-' for stdout)\n");
|
||||
printf(" -s, --size WxH Video size (default: %dx%d)\n", DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||
printf(" -f, --fps N Output frames per second (enables frame rate conversion)\n");
|
||||
printf(" -q, --quality N Quality level 0-5 (default: 2)\n");
|
||||
printf(" -Q, --quantizer Y,Co,Cg Quantizer levels 0-100 for each channel\n");
|
||||
printf(" -w, --wavelet N Wavelet filter: 0=5/3 reversible, 1=9/7 irreversible (default: 1)\n");
|
||||
printf(" -d, --decomp N Decomposition levels 1-4 (default: 3)\n");
|
||||
printf(" -b, --bitrate N Target bitrate in kbps (enables bitrate control mode)\n");
|
||||
printf(" -p, --progressive Use progressive scan (default: interlaced)\n");
|
||||
printf(" -S, --subtitles FILE SubRip (.srt) or SAMI (.smi) subtitle file\n");
|
||||
printf(" -v, --verbose Verbose output\n");
|
||||
printf(" -t, --test Test mode: generate solid colour frames\n");
|
||||
printf(" --lossless Lossless mode: use 5/3 reversible wavelet\n");
|
||||
printf(" --enable-rcf Enable per-tile rate control (experimental)\n");
|
||||
printf(" --enable-progressive Enable progressive transmission\n");
|
||||
printf(" --enable-roi Enable region-of-interest coding\n");
|
||||
printf(" --help Show this help\n\n");
|
||||
|
||||
printf("Audio Rate by Quality:\n ");
|
||||
for (int i = 0; i < sizeof(MP2_RATE_TABLE) / sizeof(int); i++) {
|
||||
printf("%d: %d kbps\t", i, MP2_RATE_TABLE[i]);
|
||||
}
|
||||
printf("\n\nQuantizer Value by Quality:\n");
|
||||
printf(" Y (Luma): ");
|
||||
for (int i = 0; i < 6; i++) {
|
||||
printf("%d: Q%d ", i, QUALITY_Y[i]);
|
||||
}
|
||||
printf("\n Co (Chroma): ");
|
||||
for (int i = 0; i < 6; i++) {
|
||||
printf("%d: Q%d ", i, QUALITY_CO[i]);
|
||||
}
|
||||
printf("\n Cg (Chroma): ");
|
||||
for (int i = 0; i < 6; i++) {
|
||||
printf("%d: Q%d ", i, QUALITY_CG[i]);
|
||||
}
|
||||
|
||||
printf("\n\nFeatures:\n");
|
||||
printf(" - 64x64 DWT tiles with multi-resolution encoding\n");
|
||||
printf(" - Full resolution YCoCg-R color space\n");
|
||||
printf(" - Progressive transmission and ROI coding\n");
|
||||
printf(" - Motion compensation with ±16 pixel search range\n");
|
||||
printf(" - Lossless and lossy compression modes\n");
|
||||
|
||||
printf("\nExamples:\n");
|
||||
printf(" %s -i input.mp4 -o output.tav # Default settings\n", program_name);
|
||||
printf(" %s -i input.mkv -q 3 -w 1 -d 4 -o output.tav # High quality with 9/7 wavelet\n", program_name);
|
||||
printf(" %s -i input.avi --lossless -o output.tav # Lossless encoding\n", program_name);
|
||||
printf(" %s -i input.mp4 -b 800 -o output.tav # 800 kbps bitrate target\n", program_name);
|
||||
printf(" %s -i input.webm -S subs.srt -o output.tav # With subtitles\n", program_name);
|
||||
}
|
||||
|
||||
// Create encoder instance
|
||||
static tav_encoder_t* create_encoder(void) {
|
||||
tav_encoder_t *enc = calloc(1, sizeof(tav_encoder_t));
|
||||
if (!enc) return NULL;
|
||||
|
||||
// Set defaults
|
||||
enc->width = DEFAULT_WIDTH;
|
||||
enc->height = DEFAULT_HEIGHT;
|
||||
enc->fps = DEFAULT_FPS;
|
||||
enc->quality_level = DEFAULT_QUALITY;
|
||||
enc->wavelet_filter = WAVELET_9_7_IRREVERSIBLE;
|
||||
enc->decomp_levels = DEFAULT_DECOMP_LEVELS;
|
||||
enc->quantizer_y = QUALITY_Y[DEFAULT_QUALITY];
|
||||
enc->quantizer_co = QUALITY_CO[DEFAULT_QUALITY];
|
||||
enc->quantizer_cg = QUALITY_CG[DEFAULT_QUALITY];
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
// Initialize encoder resources
|
||||
static int initialize_encoder(tav_encoder_t *enc) {
|
||||
if (!enc) return -1;
|
||||
|
||||
// Calculate tile dimensions
|
||||
enc->tiles_x = (enc->width + TILE_SIZE - 1) / TILE_SIZE;
|
||||
enc->tiles_y = (enc->height + TILE_SIZE - 1) / TILE_SIZE;
|
||||
int num_tiles = enc->tiles_x * enc->tiles_y;
|
||||
|
||||
// Allocate frame buffers
|
||||
size_t frame_size = enc->width * enc->height;
|
||||
enc->current_frame_rgb = malloc(frame_size * 3);
|
||||
enc->previous_frame_rgb = malloc(frame_size * 3);
|
||||
enc->current_frame_y = malloc(frame_size * sizeof(float));
|
||||
enc->current_frame_co = malloc(frame_size * sizeof(float));
|
||||
enc->current_frame_cg = malloc(frame_size * sizeof(float));
|
||||
enc->previous_frame_y = malloc(frame_size * sizeof(float));
|
||||
enc->previous_frame_co = malloc(frame_size * sizeof(float));
|
||||
enc->previous_frame_cg = malloc(frame_size * sizeof(float));
|
||||
|
||||
// Allocate tile structures
|
||||
enc->tiles = malloc(num_tiles * sizeof(dwt_tile_t));
|
||||
enc->motion_vectors = malloc(num_tiles * sizeof(motion_vector_t));
|
||||
|
||||
// Initialize ZSTD compression
|
||||
enc->zstd_ctx = ZSTD_createCCtx();
|
||||
enc->compressed_buffer_size = ZSTD_compressBound(1024 * 1024); // 1MB max
|
||||
enc->compressed_buffer = malloc(enc->compressed_buffer_size);
|
||||
|
||||
if (!enc->current_frame_rgb || !enc->previous_frame_rgb ||
|
||||
!enc->current_frame_y || !enc->current_frame_co || !enc->current_frame_cg ||
|
||||
!enc->previous_frame_y || !enc->previous_frame_co || !enc->previous_frame_cg ||
|
||||
!enc->tiles || !enc->motion_vectors || !enc->zstd_ctx || !enc->compressed_buffer) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Main function
|
||||
int main(int argc, char *argv[]) {
|
||||
generate_random_filename(TEMP_AUDIO_FILE);
|
||||
|
||||
printf("Initialising encoder...\n");
|
||||
tav_encoder_t *enc = create_encoder();
|
||||
if (!enc) {
|
||||
fprintf(stderr, "Error: Failed to create encoder\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Command line option parsing (similar to TEV encoder)
|
||||
static struct option long_options[] = {
|
||||
{"input", required_argument, 0, 'i'},
|
||||
{"output", required_argument, 0, 'o'},
|
||||
{"size", required_argument, 0, 's'},
|
||||
{"fps", required_argument, 0, 'f'},
|
||||
{"quality", required_argument, 0, 'q'},
|
||||
{"quantizer", required_argument, 0, 'Q'},
|
||||
{"quantiser", required_argument, 0, 'Q'},
|
||||
{"wavelet", required_argument, 0, 'w'},
|
||||
{"decomp", required_argument, 0, 'd'},
|
||||
{"bitrate", required_argument, 0, 'b'},
|
||||
{"progressive", no_argument, 0, 'p'},
|
||||
{"subtitles", required_argument, 0, 'S'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"test", no_argument, 0, 't'},
|
||||
{"lossless", no_argument, 0, 1000},
|
||||
{"enable-rcf", no_argument, 0, 1001},
|
||||
{"enable-progressive", no_argument, 0, 1002},
|
||||
{"enable-roi", no_argument, 0, 1003},
|
||||
{"help", no_argument, 0, 1004},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int c, option_index = 0;
|
||||
while ((c = getopt_long(argc, argv, "i:o:s:f:q:Q:w:d:b:pS:vt", long_options, &option_index)) != -1) {
|
||||
switch (c) {
|
||||
case 'i':
|
||||
enc->input_file = strdup(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
enc->output_file = strdup(optarg);
|
||||
break;
|
||||
case 'q':
|
||||
enc->quality_level = CLAMP(atoi(optarg), 0, 5);
|
||||
enc->quantizer_y = QUALITY_Y[enc->quality_level];
|
||||
enc->quantizer_co = QUALITY_CO[enc->quality_level];
|
||||
enc->quantizer_cg = QUALITY_CG[enc->quality_level];
|
||||
break;
|
||||
case 'w':
|
||||
enc->wavelet_filter = CLAMP(atoi(optarg), 0, 1);
|
||||
break;
|
||||
case 'd':
|
||||
enc->decomp_levels = CLAMP(atoi(optarg), 1, MAX_DECOMP_LEVELS);
|
||||
break;
|
||||
case 'p':
|
||||
enc->progressive = 1;
|
||||
break;
|
||||
case 'v':
|
||||
enc->verbose = 1;
|
||||
break;
|
||||
case 't':
|
||||
enc->test_mode = 1;
|
||||
break;
|
||||
case 1000: // --lossless
|
||||
enc->lossless = 1;
|
||||
enc->wavelet_filter = WAVELET_5_3_REVERSIBLE;
|
||||
break;
|
||||
case 1001: // --enable-rcf
|
||||
enc->enable_rcf = 1;
|
||||
break;
|
||||
case 1004: // --help
|
||||
show_usage(argv[0]);
|
||||
cleanup_encoder(enc);
|
||||
return 0;
|
||||
default:
|
||||
show_usage(argv[0]);
|
||||
cleanup_encoder(enc);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enc->input_file || !enc->output_file) {
|
||||
fprintf(stderr, "Error: Input and output files must be specified\n");
|
||||
show_usage(argv[0]);
|
||||
cleanup_encoder(enc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (initialize_encoder(enc) != 0) {
|
||||
fprintf(stderr, "Error: Failed to initialize encoder\n");
|
||||
cleanup_encoder(enc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("TAV Encoder - DWT-based video compression\n");
|
||||
printf("Input: %s\n", enc->input_file);
|
||||
printf("Output: %s\n", enc->output_file);
|
||||
printf("Resolution: %dx%d\n", enc->width, enc->height);
|
||||
printf("Wavelet: %s\n", enc->wavelet_filter ? "9/7 irreversible" : "5/3 reversible");
|
||||
printf("Decomposition levels: %d\n", enc->decomp_levels);
|
||||
printf("Quality: Y=%d, Co=%d, Cg=%d\n", enc->quantizer_y, enc->quantizer_co, enc->quantizer_cg);
|
||||
|
||||
// TODO: Implement actual encoding pipeline
|
||||
printf("Note: TAV encoder implementation in progress...\n");
|
||||
|
||||
cleanup_encoder(enc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Cleanup encoder resources
|
||||
static void cleanup_encoder(tav_encoder_t *enc) {
|
||||
if (!enc) return;
|
||||
|
||||
if (enc->ffmpeg_video_pipe) {
|
||||
pclose(enc->ffmpeg_video_pipe);
|
||||
}
|
||||
if (enc->mp2_file) {
|
||||
fclose(enc->mp2_file);
|
||||
unlink(TEMP_AUDIO_FILE);
|
||||
}
|
||||
if (enc->output_fp) {
|
||||
fclose(enc->output_fp);
|
||||
}
|
||||
|
||||
free(enc->input_file);
|
||||
free(enc->output_file);
|
||||
free(enc->subtitle_file);
|
||||
free(enc->current_frame_rgb);
|
||||
free(enc->previous_frame_rgb);
|
||||
free(enc->current_frame_y);
|
||||
free(enc->current_frame_co);
|
||||
free(enc->current_frame_cg);
|
||||
free(enc->previous_frame_y);
|
||||
free(enc->previous_frame_co);
|
||||
free(enc->previous_frame_cg);
|
||||
free(enc->tiles);
|
||||
free(enc->motion_vectors);
|
||||
free(enc->compressed_buffer);
|
||||
|
||||
if (enc->zstd_ctx) {
|
||||
ZSTD_freeCCtx(enc->zstd_ctx);
|
||||
}
|
||||
|
||||
free(enc);
|
||||
}
|
||||
Reference in New Issue
Block a user