
只有ChunkID等于"RIFF"或"LIST"的Chunk才能包含子Chunk,并在ChunkData的首部用一个4字节的type字段标记他自己的类型,如图:

整个AVI文件各个Chunk的嵌套关系如图:

以下代码可以初步分解AVI文件结构:
#include <stdio.h>
#include <stdint.h>
#define AVICHUNKTYPE_LIST 0x4c495354
#define AVICHUNKTYPE_RIFF 0x52494646
#define AVICHUNKTYPE_hdrl 0x6864726c
#define AVICHUNKTYPE_avih 0x61766968
#define AVICHUNKTYPE_strl 0x7374726c
#define AVICHUNKTYPE_strh 0x73747268
#define AVICHUNKTYPE_strf 0x73747266
#define AVICHUNKTYPE_strn 0x7374726e
#define AVICHUNKTYPE_vprp 0x76707270
#define AVICHUNKTYPE_INFO 0x494e464f
#define AVICHUNKTYPE_ISFT 0x49534654
#define AVICHUNKTYPE_JUNK 0x4a554e4b
#define AVICHUNKTYPE_movi 0x6d6f7669
#define AVICHUNKTYPE_idx1 0x69647831
static int64_t getFileSize(FILE *fp){
_fseeki64(fp, 0, SEEK_END);
return _ftelli64(fp);
}
static uint32_t readU32BE(FILE *fp, int64_t offset){
uint8_t buffer[8];
if(offset >= 0){
_fseeki64(fp, offset, SEEK_SET);
}
fread(buffer, 4, 1, fp);
uint32_t value = buffer[0];
value = (value << 8) | buffer[1];
value = (value << 8) | buffer[2];
value = (value << 8) | buffer[3];
return value;
}
static uint32_t readU32LE(FILE *fp, int64_t offset){
uint8_t buffer[8];
if(offset >= 0){
_fseeki64(fp, offset, SEEK_SET);
}
fread(buffer, 4, 1, fp);
uint32_t value = buffer[3];
value = (value << 8) | buffer[2];
value = (value << 8) | buffer[1];
value = (value << 8) | buffer[0];
return value;
}
static void typeToString(char *buffer, uint32_t type){
char *p = (char*)&type;
buffer[0] = p[3];
buffer[1] = p[2];
buffer[2] = p[1];
buffer[3] = p[0];
buffer[4] = 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
static void readChunk_avih(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
}
static void readChunk_strh(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
}
static void readChunk_strf(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
}
static void readChunk_strn(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
}
static void readChunk_vprp(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
}
static void readChunk_ISFT(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
}
static void readChunk_movi(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
}
static void readChunk_idx1(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
}
////////////////////////////////////////////////////////////////////////////////////////////////////
static void printAVIStruct(int64_t fileStart, uint32_t chunkSize, char *typeStr, uint8_t depth){
printf("%10lld ", fileStart);
while(depth--) printf("-----");
printf(" %s %u\n", typeStr, chunkSize);
}
static void readChunk_xxxx(FILE *fp, int64_t fileStart, int64_t fileEnd, uint8_t depth){
char typeBuffer[8];
uint32_t type;
uint32_t size;
while(fileStart+8 <= fileEnd){
type = readU32BE(fp, fileStart+0);
size = readU32LE(fp, fileStart+4);
if(size > fileEnd-fileStart-8){
size = fileEnd - fileStart - 8;
}
if(type == AVICHUNKTYPE_RIFF){
if(fileEnd > fileStart+8+size){
fileEnd = fileStart+8+size;
}
fileStart += 12;
size -= 4;
}else if(type == AVICHUNKTYPE_LIST){
type = readU32BE(fp, fileStart+8);
fileStart += 12;
size -= 4;
}else{
fileStart += 8;
}
typeToString(typeBuffer, type);
printAVIStruct(fileStart, size, typeBuffer, depth);
switch(type){
case AVICHUNKTYPE_RIFF:
readChunk_xxxx(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_hdrl:
readChunk_xxxx(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_avih:
readChunk_avih(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_strl:
readChunk_xxxx(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_strh:
readChunk_strh(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_strf:
readChunk_strf(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_strn:
readChunk_strn(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_vprp:
readChunk_vprp(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_INFO:
readChunk_xxxx(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_ISFT:
readChunk_ISFT(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_movi:
readChunk_movi(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_idx1:
readChunk_idx1(fp, fileStart, fileStart+size, depth+1);
break;
case AVICHUNKTYPE_JUNK:
break;
default:
printf("#define AVICHUNKTYPE_%s 0x%08x\n", typeBuffer, type);
break;
}
fileStart += size+(size%2);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[]){
if(argc < 2) return 0;
FILE *fp = fopen(argv[1], "rb");
if(fp == NULL) return -1;
int64_t fileSize = getFileSize(fp);
if(fileSize <= 8){
fclose(fp);
return -2;
}
readChunk_xxxx(fp, 0, fileSize, 1);
fclose(fp);
return 0;
}
运行效果如图:

网友评论