Created
February 6, 2018 15:35
-
-
Save twocity/dce4b79884a8dfd73dd3fa4fffbb26ee to your computer and use it in GitHub Desktop.
AVI format parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Header.RIFF | |
import okio.Buffer | |
import okio.BufferedSource | |
import okio.Okio | |
import java.io.File | |
class AVIFormat private constructor(val riff: ChunkGroup) { | |
val chunks by lazy { riff.spread() } | |
private fun ChunkGroup.spread(): List<Chunk> { | |
return chunks.fold(listOf()) { acc, chunk -> | |
acc.toMutableList().apply { | |
add(chunk) | |
if (chunk is ChunkGroup) { | |
addAll(chunk.spread()) | |
} | |
}.toList() | |
} | |
} | |
fun find(type: String): Chunk = findAll(type).first() | |
fun findAll(type: String): List<Chunk> = chunks.filter { it.type == type } | |
companion object { | |
fun parse(file: File): AVIFormat { | |
val buffer = Okio.buffer(Okio.source(file)) | |
val header = buffer.readFourCC() | |
require(header == RIFF.name, { "Not RIFF format file" }) | |
val size = buffer.readIntLe() | |
val type = buffer.readFourCC() | |
require(type.trim() == "AVI", { "Not AVI container, found $type" }) | |
val buf = Buffer() | |
buffer.readFully(buf, size.toLong() - 4) | |
val riff = ChunkGroup(RIFF, size = size - 4, type = type, buffer = buf) | |
return AVIFormat(riff) | |
} | |
} | |
override fun toString(): String = riff.toString() | |
} | |
fun BufferedSource.readFourCC(): String = readIntLe().toFourCC() | |
private fun Int.toFourCC(): String { | |
val chars = charArrayOf(and(0xff).toChar(), | |
shr(8).and(0xff).toChar(), | |
shr(16).and(0xff).toChar(), | |
shr(24).and(0xff).toChar()) | |
return String(chars) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Header.LIST | |
import okio.Buffer | |
import okio.BufferedSource | |
enum class Header { | |
RIFF, LIST | |
} | |
/** | |
* The basic type of Chunk: | |
* ``` | |
* type, size, buffer | |
* ``` | |
* | |
* @param type chunk's type name | |
* @param size of buffer | |
* @param buffer the raw data of chunk | |
* @property totalSize length of chunk | |
*/ | |
open class Chunk(val type: String, val size: Int, val buffer: BufferedSource) { | |
open val totalSize = 4 + 4 + size // sizeOf(type) + sizeOf(size) | |
} | |
/** | |
* The basic type of Chunk Container: | |
* ``` | |
* header, type, size, buffer | |
* ``` | |
* | |
* @param header chunk container's type | |
* @param type chunk's type name | |
* @param size of buffer | |
* @param buffer the raw data of chunk | |
* @property totalSize length of chunk | |
* @property chunks container's sub chunks | |
**/ | |
class ChunkGroup(val header: Header, type: String, | |
size: Int, buffer: BufferedSource) : Chunk(type, size, buffer) { | |
override val totalSize: Int = 4 + super.totalSize // sizeOf(header) + sizeOf(type) + sizeOf(size) | |
val chunks: List<Chunk> by lazy { scanChunks() } | |
private fun scanChunks(): List<Chunk> { | |
val chunks = mutableListOf<Chunk>() | |
var consumed = 4 + 4 + 4 // header + type + size | |
while (!buffer.exhausted()) { | |
val c = buffer.readChunk() | |
consumed += c.totalSize | |
if (consumed % 2 != 0) { | |
// skip padding | |
consumed += 1 | |
buffer.skip(1) | |
} | |
chunks.add(c) | |
} | |
require(consumed == totalSize) | |
return chunks.toList() | |
} | |
private fun BufferedSource.readChunk(): Chunk { | |
val header = readFourCC() | |
return when (header) { | |
LIST.name -> { | |
val size = readIntLe() | |
val type = readFourCC() | |
val sink = Buffer() | |
readFully(sink, size.toLong() - 4) | |
ChunkGroup(header = LIST, type = type, size = size - 4, buffer = sink) | |
} | |
else -> { | |
val size = readIntLe() | |
val sink = Buffer() | |
readFully(sink, size.toLong()) | |
Chunk(type = header, size = size, buffer = sink) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
AVI 格式解析:https://blog.twocities.me/avi_format_parsing.html