随着多媒体技术的飞速发展,多媒体技术广泛应用于信息邻域中,出现了各种各样的多媒体数据文件,AVI文件就是常见的一种。比起Windows其它的资源性数据文件,AVI文件的结构要复杂得多。本文探讨编程实现生成256色压缩和非压缩的AVI动画文件。
一、AVI文件格式分析
AVI文件是Microsoft公司制定的一种RIFF(Resource Interchange File Format)文件格式,主要由各种各样的块(chunk)构成,块包括块头和块体两部分,块头的结构如下:
struct chunkhead {
unsigned char type[4];
unsigned long size;
}
前4个字节标识块的类型,后一个无符号长整型数记载块体的长度;而块体可能是基本数据,也可能是一个具有块头和块体结构的子块。但最底层子块的块体必须是基本数据,称为数据块,而称非底层子块为构造块。数据块的块标识指明数据的性质,例如:
块标识 |
数据性质 |
“avih” |
AVI头 |
“strh” |
数据流头 |
“strf” |
数据流格式 |
“00db” |
非压缩视频数据 |
“00dc” |
压缩视频数据 |
“00dx” |
压缩视频数据 |
“01wb” |
音频数据 |
而构造块的标识一律都是“LIST”,故也称为LIST块,为进一步区别其属性,在块体的前面,用4个字节来标识其类型,例如:
标识字节 |
类型 |
“hdrl” |
AVI总参数头构造 |
“strl” |
流参数构造 |
“movi” |
流数据构造 |
整个AVI文件就是一个以“RIFF”为标识的构造块,其构造类型为“AVI ”,它包括两个必备的LIST块和一个可选的索引块。索引块的标识为“idx1”,其作用是在播放时可以随机地访问任一幅画面或任一段声音。第一个LIST块为参数块,其底层块记载视频音频格式参数,分别采用BMP和WAV文件的格式规范。限于篇幅,本文只讨论不含音频数据的256色AVI动画文件的生成。第一个LIST块的层次结构描述如下:
偏址 |
内容 |
长度 |
0xc |
LIST块头1 |
8 |
0x14 |
“hdrl”, LIST块标识 |
4 |
0x18 |
avih块头 |
8 |
0x20 |
avih块体 |
0x38 |
0x58 |
LIST块头2 |
8 |
0x60 |
“strl”, LIST块标识 |
4 |
0x64 |
strh块头 |
8 |
0x6c |
strh块体 |
0x38 |
0xa4 |
strf块头 |
8 |
0xac |
bmp头 |
0x28 |
0xd4 |
调色板数据 |
4*n |
其中n为实际使用的颜色数(n≤256),LIST块头2记载块体的长度为116+4*n,LIST块头1记载块体的长度为192+4*n。至于avih块体、strh块体、bmp头的结构,请参阅程序。
第二个LIST块为记录块,其底层块记载视频数据,视频数据块的块标识可能是“00db”或“00dc”等等,而视频数据可能是非压缩的或压缩的。第二个LIST块的层次结构描述如下:
内容 |
长度 |
第二个LIST块块头 |
8 |
“movi”,第二个LIST块标识 |
4 |
第一个视频数据块块头 |
8 |
第一帧视频数据 |
size1 |
… … |
… |
第N个视频数据块块头 |
8 |
第N帧视频数据 |
sizeN |
第二个LIST块块头记载块体的长度为4+8*N+size1+size2+…sizeN。
AVI文件中还可以有一种“废块”,标识为“JUNK”,是数据块格式,其作用是便于在文件中随机地增删新旧视频音频记录,而不致于造成AVI文件写操作中定位的困难。本文在生成AVI动画时不考虑JUNK块。
二、RLE8压缩方法
视频数据采用BMP格式,对于非压缩情形,它是以图像的左下角为起点,按照从左至右,由下而上的次序,将图像数据存入文件,256色图像是一个字节记录一点;采用压缩方式处理数据也是按照上述次序,而压缩方法有多种。本文介绍Microsoft Windows的游程编码RLE8(Run Length Encodeing)压缩方法:对数据流中同一数据游程进行压缩,即以两个字节代表一串相同数值的数据,第一个字节表示有多少个相同的数据,第二个字节表示此相同的数据,这两个字节最多能代替255个连续重复出现的数据,如果连续重复出现的数据的个数超过255个,则必须两组或两组以上的压缩码来代表,这就是RLE8的计数方式;对于一串互不相同的数据,则采用识别码,识别码共有4种,其第一个字节必须是0,如下定义:
l 0x00 0x00表示一行图像数据的结束。RLE8压缩法以行为压缩单元,每行数据经压缩后,在末端加入0x00 0x00两个字节。
l 0x00 0x01表示所有图像数据的结束。当所有的行都压缩处理后,就加入0x00 0x01两个字节,作为结束标志。
l 0x00 0x02 X Y 表示向右移X点,向下移Y点。
l 0x00 N…表示有N点不同值的图像数据。如果不同值的图像数据为奇数个,则必须在数据末端加入0x00 ,以维持压缩数据长度为偶数个字节。
例如:有一串原始数据为0x16 0x16 0x16 0x16 0x42 0x36 0x04 ,用RLE8压缩数据为0x04 0x16 0x00 0x03 0x42 0x36 0x04 0x00。
注意:当不同点的个数为1或2时,应视为相同(个数为1)的情形来处理。
三、AVI动画生成
本程序用Turbo C2.0在标准模式下开发而成。在屏幕上显示“AVI Animator!”的动画,然后按照AVI动画的文件格式生成动画文件animator.avi。程序中compression的值为0,则生成256色非压缩的动画文件;compression的值为1,则应用Microsoft Windows的RLE8压缩法压缩生成256色压缩格式的动画文件。生成的动画文件animator. avi可直接在Windows下播放。本程序在Turbo C2.0 Small模式下编译通过。程序代码如下:
#include <stdio.h>
#include <io.h>
#include <math.h>
#include <alloc.h>
#include <stdlib.h>
#include <graphics.h>
#define widthbytes(i) ((i+31)/32*4)
int frames,colors,linebytes,color,bkcolor;
int left,top,right,bottom,bmpwidth,bmpheight;
unsigned long compression,imagebytes,
offset,*bmpsize;
struct chunkhead
{
unsigned char type[4];
unsigned long size;
}riff,list,block,subblock;
struct aviheader
{
unsigned long microsecperframe,
maxbytespersec,reserved1;
unsigned long flags,totalframes,
initialframes,streams;
unsigned long suggestedbuffersize,width,
height,reserved[4];
}avih;
struct avistreamheader
{
unsigned char type[4];
unsigned long compression,reserved1,
reserved2,reserved3;
unsigned long streams,quality,
initialframes,totalframes;
unsigned long suggestedbuffersize,
samplesize,reserved[2];
unsigned int width,height;
}strh;
struct bitmapinfo
{
unsigned long size,width,height;
unsigned int plane,bitsperpixel;
unsigned long compression,imagesize,
xpels,ypels;
unsigned long colorused,colorimportant;
}bmp;
struct idxchunk
{
unsigned char type[4];
unsigned long flags,offset,length;
}idx;
void getacolor(int k,int acolor[3])
{
int j,color[16]={0,1,2,3,4,5,22,7,56,57,58,
59,60,61,62,63};
j=color[k];
if(j==22) j=20;
outportb(0x3c7,((unsigned char)j));
acolor[0]=inportb(0x3c9);
acolor[1]=inportb(0x3c9);
acolor[2]=inportb(0x3c9);
}
void getcolortable(int colortable[16][3])
{
int i,j;
int color[16]={0,1,2,3,4,5,22,7,56,57,58,
59,60,61,62,63};
for(i=0;i<16;i++) {
j=color[i];
if(i==6) j=20;
outportb(0x3c7,((unsigned char)j));
colortable[i][0]=inportb(0x3c9);
colortable[i][1]=inportb(0x3c9);
colortable[i][2]=inportb(0x3c9);
}
}
void createchunkhead(FILE *fp)
{
int i,colortable[16][3];
unsigned char pal[16][4],type[4]=" ";
linebytes=widthbytes(8*bmpwidth);
memcpy(riff.type,"RIFF",4);
riff.size=224L+4*colors+24*frames
+(unsigned long)linebytes*bmpheight*frames;
fwrite(&riff,sizeof(riff),1,fp);
memcpy(type,"AVI ",4);
fwrite(type,4,1,fp);
memcpy(list.type,"LIST",4);
list.size=(unsigned long)192+4*colors;
fwrite(&list,sizeof(list),1,fp);
memcpy(type,"hdrl",4);
fwrite(type,4,1,fp);
memcpy(block.type,"avih",4);
block.size=0x38;
fwrite(&block,sizeof(block),1,fp);
avih.microsecperframe=0x1046b;
avih.maxbytespersec=0x640;
avih.reserved1=0;
avih.flags=0x810;avih.totalframes=frames;
avih.initialframes=0;avih.streams=1;
avih.suggestedbuffersize=(unsigned
long)linebytes*bmpheight;
avih.width=bmpwidth;avih.height=bmpheight;
avih.reserved[0]=0;avih.reserved[1]=0;
avih.reserved[2]=0;avih.reserved[3]=0;
fwrite(&avih,sizeof(avih),1,fp);
memcpy(block.type,"LIST",4);
block.size=(unsigned long)116+4*colors;
fwrite(&block,sizeof(block),1,fp);
memcpy(type,"strl",4);
fwrite(type,4,1,fp);
memcpy(subblock.type,"strh",4);
subblock.size=0x38;
fwrite(&subblock,sizeof(subblock),1,fp);
memcpy(strh.type,"vids",4);
strh.compression=0;strh.reserved1=0;
strh.reserved2=0;strh.reserved3=0;
strh.streams=1;strh.quality=15;
strh.initialframes=0;strh.totalframes=frames;
strh.suggestedbuffersize=(unsigned long)linebytes*bmpheight;
strh.samplesize=0;
strh.reserved[0]=0;strh.reserved[1]=0;
strh.width=bmpwidth;strh.height=bmpheight;
fwrite(&strh,sizeof(strh),1,fp);
memcpy(subblock.type,"strf",4);
subblock.size=0x28+4*colors;
fwrite(&subblock,sizeof(subblock),1,fp);
bmp.size=0x28;
bmp.width=bmpwidth;
bmp.height=bmpheight;
bmp.plane=1;bmp.bitsperpixel=8;
bmp.compression=compression;
bmp.imagesize=(unsigned
long)linebytes*bmpheight;
bmp.xpels=0x0ece;bmp.ypels=0xec4;
bmp.colorused=colors;
bmp.colorimportant=colors;
fwrite(&bmp,sizeof(bmp),1,fp);
getcolortable(colortable);
for(i=0;i<colors;i++) {
pal[i][0]=(unsigned char)colortable[i][2]<<2;
pal[i][1]=(unsigned char)colortable[i][1]<<2;
pal[i][2]=(unsigned char)colortable[i][0]<<2;
pal[i][3]=0;
}
fwrite(pal,1,4*colors,fp);
memcpy(list.type,"LIST",4);
list.size=4L+8*frames+
(unsigned long)linebytes*bmpheight*frames;
fwrite(&list,sizeof(list),1,fp);
memcpy(type,"movi",4);
fwrite(type,4,1,fp);
}
|