2024年3月12日发(作者:)
AVI录像机开发
江洪
摘要:使用微软的
进行视频捕获,使用
进行音频捕获,使用
XVID
编解码
库
进行视频编码,结合
AVI
文件的结构,开发了可以实现录像功能的实用程序,录像
存储在
AVI
文件中。
关键词:
AVI
;录像机;
XVID
;视频;音频
1
引言
AVI
文件是微软公司开发的一种多媒体文件格式。视频和
音频数据交错存储在文件中,其中的图像和声音数据大都经过
编码后进行存放。
AVI
文件的使用非常广泛,大多数媒体播放
器都支持此类文件。下面将开发一个
AVI
录像机程序,可实现
AVI
文件的录像功能。
视频部分,宽度为
640
,高度为
480
,每秒帧数为
20
,编
码采用
XVID
。
音频部分,声道数为
1
,采样位数为
8
,采样频率为
8000
,编码采用
PCM
。
使用
进行视频捕获。该库是
VC
中自带的库,提
供了对
AVI
视频进行处理的功能。
使用
进行音频捕获。该库也是
VC
中自带的库,
提供了对声音进行处理的功能。
进行图像编码,需用到
动态链接库。这是一个
开源的
XVID
编码解码库,可以从
下载该库的源
代码。
2AVI
文件结构
要开发
AVI
录像机程序,需要对
AVI
文件格式有比较清
楚的了解。
AVI
文件是微软公司推出的一种多媒体文件格式。
该文件通常包括一个视频流和一个音频流。
AVI
文件由一个
RIFF
头,两个列表
hdrl
(用于描述媒体
流格式)和
movi
列表(用于保存媒体流数据),以及一个可选
的索引块组成。
整个
AVI
文件结构如表
1
所示。
表
1
段名字段名长度说明
ChunkId4
值为’
RIFF
’表示这是一个
RIFF
文件
RIFF
头
Size4
整个
AVI
文件大小
-8
Flag4
值为’
AVI
‘表示这是一个
AVI
文件
Chunkid4
值为’
LIST
’,表示这是一个列表
hdrl
列表
Size4
该列表长度,不包括’
LIST
’和列表长度字
段,设为
294
段名字段名长度说明
hdrl
列表
Flag4
值为’
hdrl
’,表示这是一个
AVI
头段,设
为
294
Fcc4
值为’
avih
’,表示这是
AVI
主信息头
Cb4
主信息头大小,不包括
fcc
和
cb
两个字
段,设为
56
dwMicroSecPerFrame4
视频帧间隔时间(以微秒为单位),设
50000
dwMaxBytesPerSec4AVI
文件的最大数据率,可设
0
dwPaddingGranularity4
数据填充的粒度,可设
0
dwFlags4AVI
全局标记,设为
0x0110
主信息头
dwTotalFrames4
总帧数
dwInitialFrames4
为交互格式指定初始帧数(非交互格式为
0
)
dwStreams4
AVI
文件包含的流的个数(通常为
2
,即
1
个视频流,
1
个音频流)
dwSuggestedBufferSize4
建议读取本文件的缓存大小,可设
0
dwWidth4
视频图像的宽(以像素为单位),设
640
dwHeight4
视频图像的高(以像素为单位),设
480
dwReserved16
保留,设
0
ChunkId4
值为’
LIST
’,这是一个子列表
视频流子
4
该列表长度,不包括’
LIST
’和列表长度
列表
Size
字段,设
116
Flag4
值为’
strl
’,表示这是
AVI
流列表
fcc4
值为’
strh
’,表示这是
AVI
流头
cb4
流头大小,不包括
fcc
和
cb
两字段,设
56
fccType4
流类型,视频流为’
vids
’
fccHandler4
流的处理者,视频流设为’
XVID
’
dwFlags4
标记,设
0
wPriority2
流的优先级,设
0
wLanguage2
流的语言,设
0
dwInitialFrames4
初始帧数,设
0
dwScale4
时间尺度,设
1
视频流头
dwRate4
流速率,视频流设为每秒帧数
dwStart4
流的开始时间,设
0
dwLength4
流的长度,视频流设为总帧数
dwSuggestedBufferSize4
读取这个流数据建议使用的缓存大小,可
设
0
dwQuality4
流数据的质量指标,可设
0
dwSampleSize4
采样大小,设
0
left2
视频流左坐标,设
0
top2
视频流上坐标,设
0
right2
视频流右坐标,设图像宽,
640
bottom2
视频流下坐标,设图像高,
480
2011.01
69
段名字段名长度说明
视频流描述
Chunkid4
值为’
strf
’,表示这是流的具体格式
列表
Size4
流描述头大小,设
40
biSize4
本结构大小,设
40
biWidth4
图像宽,设
640
biHeight4
图像高,设
480
biPlanes2
位平面数,设
1
视频流描述
biBitCount2
颜色位数,设
24
头
biCompression4
压缩方式,设’
XVID
’
biSizeImage4
图像大小,可设
0
biXPelsPerMeter4
横向分辨率,设
0
biYPelsPerMeter4
纵向分辨率,设
0
biClrImportant4
重要颜色数,设
0
biClrUsed4
使用颜色数,设
0
ChunkId4
值为’
LIST
’,这是一个子列表
音频流子列
表
Size4
该列表长度,不包括’
LIST
’和列表长度
字段,设
94
Flag4
值为’
strl
’,表示这是
AVI
流列表
Fcc4
值为’
strh
’,表示这是
AVI
流头
Cb4
流头大小,不包括
fcc
和
cb
两字段,设
56
fccType4
流类型,音频流为’
auds
’
fccHandler4
流的处理者,音频流设
0
dwFlags4
标记,设
0
wPriority2
流的优先级,设
0
wLanguage2
流的语言,设
0
dwInitialFrames4
初始帧数,设
0
dwScale4
时间尺度,设
1
音频流头
dwRate4
流速率,音频流设为每秒字节数
dwStart4
流的开始时间,设
0
dwLength4
流的长度,音频流设为总音频数据大小
dwSuggestedBuffer-
Size
4
读取这个流数据建议使用的缓存大小,可设
0
dwQuality4
流数据的质量指标,可设
0
dwSampleSize4
采样大小,设
1
left2
保留,设
0
top
2
保留,设
0
right2
保留,设
0
bottom2
保留,设
0
音频流描述
Chunkid4
值为’
strf
’,表示这是流的具体格式
列表
Size4
流描述头大小,设
18
wFormatTag2
编码格式,设为
1
,即
PCM
nChannels2
声道数,设
1
音频流描述
nSamplesPerSec4
采样频率,设
8000
头
nAvgBytesPerSec4
每秒字节数,设
8000
nBlockAlign2
每个采样占字节数,设
1
wBitsPerSample2
采样位数,设
8
cbSize2
附加字节数,设
0
ChunkId4
值为’
LIST
’,这是一个列表
movi
列表
Size4
该列表长度,不包括’
LIST
’和列表长度字段
Flag4
值为’
movi
’,此列表保存视频和音频数据
Chunkid4
值为’
01wb
’表示音频数据
,
值为’
00dc
’
视频
/
音频
表示视频数据
数据
(
若干
)
Size4
本数据块大小
Data
实际的视频或音频数据,如数据块大小为
奇数,需在数据块末尾补
1
字节
0
70
2011.01
段名字段名长度说明
Fcc4
值为’
idx1
’,表示这是索引列表
索引列表
Cb4
该列表长度,不包括’
idx1
’和列表长度
字段
dwChunkId4
值为’
01wb
’表示音频数据
,
值为’
00dc
’
表示视频数据
索引(若干)
dwFlags4
块标志
(0x10
为关键帧
,0x00
为非关键帧
)
dwOffset4
相对于
movi
列表的偏移量
dwSize4
本数据块的大小
视频流和音频流子列表中包括一些基本的视频和音频参
数,如编码格式、声道数、采样频率等。
movi
列表中保存实际的视频和音频数据。其中视频和音
频数据都是交错存放的。视频数据以
00dc
开头,其后
4
个字
节是该块数据长度,然后是实际的视频数据。音频数据以
01wb
开头,其后
4
个字节是该块数据长度,然后是实际的音
频数据。
索引段以
idx1
开头,用于保存
movi
列表中各块的索引信息。
库
微软提供了
库,头文件名为
vfw.h
。该库提供了
对视频处理的支持,可以利用该库进行视频捕获。
4
微软多媒体开发库
微软提供了
,头文件名为
mmsystem.h
。该库提
供了对声音处理的支持。其中
waveIn
系列函数用于声音输入。
可以利用该库进行音频捕获。
5XVID
编码解码库
XVID
库中提供了
XVID
数据的编码操作。
编码前首先调用
xvid_global(NULL,XVID_GBL_INIT,
&xvid_gbl_init,NULL);
进行初始化。其中
XVID_GBL_INIT
表示
进行初始化。
xvid_gbl_init
是一个
xvid_gbl_init_t
类型的变量,
该类型用于
XVID
初始化。
然后调用
xvid_encore(NULL,XVID_ENC_CREATE,
&xvid_enc_create,NULL)
;创建一个编码实例。其中
XVID_ENC_CREATE
表示创建编码实例。
xvid_enc_create
是一
个
xvid_enc_create_t
类型的变量,该类型用于建立编码实例。
如果
xvid_encore
返回
0
,则可从
xvid_enc_create
中取得编码实
例的句柄。
xvid_enc_create_t
结构定义如下:
typedefstruct{
intversion;//
版本号
intprofile;//XVID
级别
intwidth;//
图像宽度
intheight;//
图像高度
intnum_zones;
xvid_enc_zone_t*zones;
intnum_plugins;
xvid_enc_plugin_t*plugins;
intnum_threads;
intmax_bframes;
intglobal;
intfincr;//
帧率增量
intfbase;//
帧间隔时间
intmax_key_interval;
intframe_drop_ratio;
intbquant_ratio;
intbquant_offset;
intmin_quant[3];
intmax_quant[3];
void*handle;
}xvid_enc_create_t;
调用
xvid_encore(enc_handle,XVID_ENC_ENCODE,
&xvid_enc_frame,&xvid_enc_stats);
进行
XVID
编码。
其中
XVID_ENC_ENCODE
表示进行
XVID
编码。
xvid_enc_frame
是一个
xvid_enc_frame_t
类型的变量,该类
型用于描述要进行编码的帧。
xvid_enc_frame_t
定义如下:
typedefstruct{
intversion;//
版本号
intvol_flags;
unsignedchar*quant_intra_matrix;
unsignedchar*quant_inter_matrix;
intpar;
intpar_width;
intpar_height;
intfincr;
intvop_flags;
intmotion;
xvid_image_tinput;//
输入图像
inttype;//
编码类型,一般设
XVID_TYPE_AUTO
intquant;//
帧量化器
intbframe_threshold;
void*bitstream;//
输出缓冲区
intlength;//
输出缓冲区长度
intout_flags;
}xvid_enc_frame_t;
其中
xvid_image_t
是另一个结构,用于表示输入图像,其
定义如下:
typedefstruct{
intcsp;//
使用的色场空间
void*plane[4];//
图像平面,最多
4
个,一般只使用
plane[0]
intstride[4];//
表示每行像素占用字节数
}xvid_image_t;
xvid_enc_stats
是一个
xvid_enc_stats_t
类型的变量,该类型
用于表示编码帧的状态。
xvid_enc_stats_t
定义如下:
typedefstruct{
intversion;//
版本号
inttype;
intquant;
intvol_flags;
intvop_flags;
intlength;
inthlength;
intkblks;
intmblks;
intublks;
intsse_y;
intsse_u;
intsse_v;
}xvid_enc_stats_t;
调用
xvid_encore(enc_handle,XVID_ENC_DESTROY,NULL,
NULL);
结束一个编码实例。其中
enc_handle
是先前得到的编
码实例句柄,
XVID_ENC_DESTROY
表示撤销编码实例。
6
代码解析
使用如下代码段进行录像预览:
//
显示捕获窗口
mywnd=newCWnd;
mywnd->Create(NULL,"",WS_CHILD|WS_VISIBLE,
CRect(0,0,videowidth,videoheight),this,1234);
mywnd->ShowWindow(SW_SHOW);
mywnd->GetWindowRect(rect);
ghCapWnd=capCreateCaptureWindow("MyOwnCapture
Window",
WS_CHILD|WS_VISIBLE,0,0,
(),(),
mywnd->GetSafeHwnd(),1235);
capDriverConnect(ghCapWnd,0);
capCaptureGetSetup(ghCapWnd,&CapParms,sizeof(CAP-
TUREPARMS));
estMicroSecPerFrame=1000000L/vide-
oframes;
Enabled=FALSE;
ntrol=FALSE;
=TRUE;
ort=VK_ESCAPE;
LeftMouse=FALSE;
RightMouse=FALSE;
capCaptureSetSetup(ghCapWnd,&CapParms,sizeof(CAP-
TUREPARMS));
capPreviewRate(ghCapWnd,videoframes/2);
capPreviewScale(ghCapWnd,TRUE);
capPreview(ghCapWnd,TRUE);
使用如下代码将捕获到图像转换为
HBITMAP
:
HBITMAPCopyWindowToBitmap(void)//
复制捕获窗口图像
2011.01
71
//
到
HBITMAP
{
HDChScrDC,hMemDC;
intnX,nY,nX2,nY2;
intnWidth,nHeight;
intxScrn,yScrn;
RECTrect1;
::GetWindowRect(ghCapWnd,&Rect);
::GetWindowRect(AfxGetMainWnd()->m_hWnd,&rect1);
if(IsRectEmpty(&Rect))returnNULL;
hScrDC=CreateDC("DISPLAY",NULL,NULL,NULL);
hMemDC=CreateCompatibleDC(hScrDC);
nX=+3;
nY=+31;
nX2=;
nY2=;
xScrn=GetDeviceCaps(hScrDC,HORZRES);
yScrn=GetDeviceCaps(hScrDC,VERTRES);
if(nX<0)nX=0;
if(nY<0)nY=0;
if(nX2>xScrn)nX2=xScrn;
if(nY2>yScrn)nY2=yScrn;
nWidth=nX2-nX;
nHeight=nY2-nY;
if(hBitmap==NULL)
hBitmap=CreateCompatibleBitmap(hScrDC,nWidth,
nHeight);
hOldBitmap=(HBITMAP)SelectObject(hMemDC,hBitmap);
BitBlt(hMemDC,0,0,nWidth,nHeight,hScrDC,nX,nY,SRC-
COPY);
hBitmap=(HBITMAP)SelectObject(hMemDC,hOldBitmap);
DeleteDC(hScrDC);
DeleteDC(hMemDC);
returnhBitmap;
}
使用如下代码将
HBITMAP
转换到内存:
//HBITMAP
复制到
buf
内存
BOOLCopyBmpToMemory(HBITMAPhBitmap,LPSTRbuf)
{
HDChDC;
intiBits;
WORDwBitCount;
DWORDdwPaletteSize=0,dwBmBitsSize=0,dwDIBSize=0,
dwWritten=0;
BITMAPBitmap;
BITMAPFILEHEADERbmfHdr;
BITMAPINFOHEADERbi;
LPBITMAPINFOHEADERlpbi;
HANDLEhDib,hPal,hOldPal=NULL;
hDC=CreateDC("DISPLAY",NULL,NULL,NULL);
iBits=GetDeviceCaps(hDC,BITSPIXEL)*GetDeviceCaps
72
2011.01
(hDC,PLANES);
DeleteDC(hDC);
if(iBits<=1)
wBitCount=1;
elseif(iBits<=4)
wBitCount=4;
elseif(iBits<=8)
wBitCount=8;
else
wBitCount=24;
GetObject(hBitmap,sizeof(Bitmap),(LPSTR)&Bitmap);
=sizeof(BITMAPINFOHEADER);
h=h;
ht=ht;
es=1;
ount=wBitCount;
ression=BI_RGB;
Image=0;
sPerMeter=0;
sPerMeter=0;
mportant=0;
sed=0;
dwBmBitsSize=((h*wBitCount+31)/32)
*4*ht;
hDib=GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+
sizeof(BITMAPINFOHEADER));
lpbi=(LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi=bi;
hPal=GetStockObject(DEFAULT_PALETTE);
if(hPal)
{
hDC=::GetDC(NULL);
hOldPal=::SelectPalette(hDC,(HPALETTE)hPal,FALSE);
RealizePalette(hDC);
}
GetDIBits(hDC,hBitmap,0,(UINT)ht,
(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dw-
PaletteSize,
(BITMAPINFO*)lpbi,DIB_RGB_COLORS);
if(hOldPal)
{
::SelectPalette(hDC,(HPALETTE)hOldPal,TRUE);
RealizePalette(hDC);
::ReleaseDC(NULL,hDC);
}
=0x4D42;
dwDIBSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAP-
INFOHEADER)+
dwPaletteSize+dwBmBitsSize;
=dwDIBSize;
rved1=0;
rved2=0;
its=(DWORD)sizeof(BITMAPFILEHEADER)+
(DWORD)sizeof(BITMAPINFOHEADER)+dw-
PaletteSize;
memcpy(buf,(LPSTR)&bmfHdr,14);
memcpy(buf+14,(LPSTR)lpbi,dwDIBSize);
GlobalUnlock(hDib);
GlobalFree(hDib);
returnTRUE;
}
使用如下代码写
AVI
头:
voidWriteAviHeader(void)//
写
AVI
文件头
{
charbuf[1000];
structAVIMAINHEADERah;
structAVISTREAMHEADERash;
BITMAPINFOHEADERbih;
unsignedlonglen;
WAVEFORMATEXwf;
fseek(avifp,0,SEEK_SET);
buf[0]='R';buf[1]='I';buf[2]='F';buf[3]='F';
fwrite(buf,1,4,avifp);//
写入
RIFF
标记
buf[0]=0;buf[1]=0;buf[2]=0;buf[3]=0;
fwrite(buf,1,4,avifp);//
写入文件总长度
buf[0]='A';buf[1]='V';buf[2]='I';buf[3]='';fwrite(buf,1,4,
avifp);
buf[0]='L';buf[1]='I';buf[2]='S';buf[3]='T';fwrite(buf,1,4,
avifp);
len=294;fwrite(&len,1,4,avifp);
buf[0]='h';buf[1]='d';buf[2]='r';buf[3]='l';fwrite(buf,1,4,avifp);
memset(&ah,0,sizeof(ah));
[0]='a';[1]='v';[2]='i';[3]='h';
=sizeof(ah)-8;
oSecPerFrame=1000000L/videoframes;
s=0x110;
ams=2;
h=videowidth;
ht=videoheight;
fwrite(&ah,1,sizeof(ah),avifp);//
写入主信息头
buf[0]='L';buf[1]='I';buf[2]='S';buf[3]='T';fwrite(buf,1,4,
avifp);
buf[0]=0x74;buf[1]=0;buf[2]=0;buf[3]=0;fwrite(buf,1,4,avifp);
buf[0]='s';buf[1]='t';buf[2]='r';buf[3]='l';fwrite(buf,1,4,avifp);
memset(&ash,0,sizeof(ash));
[0]='s';[1]='t';[2]='r';[3]='h';
=sizeof(ash)-8;
e[0]='v';e[1]='i';
e[2]='d';e[3]='s';
dler[0]='X';dler[1]='V';
dler[2]='I';dler[3]='D';
e=1;
=videoframes;
th=0;
=0;=0;
=videowidth;=video-
height;
fwrite(&ash,1,sizeof(ash),avifp);//
写入视频流头
buf[0]='s';buf[1]='t';buf[2]='r';buf[3]='f';fwrite(buf,1,4,avifp);
buf[0]=0x28;buf[1]=0;buf[2]=0;buf[3]=0;fwrite(buf,1,4,avifp);
memset(&bih,0,sizeof(bih));
=0x28;
h=videowidth;
ht=videoheight;
es=1;
ount=0x18;
ression=1145656920L;
fwrite(&bih,1,sizeof(bih),avifp);//
写入视频流信息头
buf[0]='L';buf[1]='I';buf[2]='S';buf[3]='T';fwrite(buf,1,4,
avifp);
buf[0]=0x5e;buf[1]=0;buf[2]=0;buf[3]=0;fwrite(buf,1,4,avifp);
buf[0]='s';buf[1]='t';buf[2]='r';buf[3]='l';fwrite(buf,1,4,avifp);
memset(&ash,0,sizeof(ash));
[0]='s';[1]='t';[2]='r';[3]='h';
=sizeof(ash)-8;
e[0]='a';e[1]='u';
e[2]='d';e[3]='s';
dler[0]=0;dler[1]=0;
dler[2]=0;dler[3]=0;
e=1;
=audiochannels*audiobits/8*audiorate;
leSize=1;
fwrite(&ash,1,sizeof(ash),avifp);//
写入音频流头
buf[0]='s';buf[1]='t';buf[2]='r';buf[3]='f';fwrite(buf,1,4,avifp);
buf[0]=0x12;buf[1]=0;buf[2]=0;buf[3]=0;fwrite(buf,1,4,avifp);
memset(&wf,0,sizeof(wf));
tTag=1;
els=audiochannels;
esPerSec=audiorate;
tesPerSec=audiochannels*audiobits/8*audiorate;
Align=audiochannels*audiobits/8;
erSample=audiobits;
=0;
fwrite(&wf,1,sizeof(wf),avifp);//
写入音频流信息头
//
写入
movi
列表
buf[0]='L';buf[1]='I';buf[2]='S';buf[3]='T';fwrite(buf,1,4,
avifp);
buf[0]=0;buf[1]=0;buf[2]=0;buf[3]=0;fwrite(buf,1,4,avifp);
buf[0]='m';buf[1]='o';buf[2]='v';buf[3]='i';fwrite(buf,1,4,
avifp);
}
使用如下代码写
AVI
索引,使用一个链表存储索引信息。
voidWriteAviIndex(void)//
写
AVI
文件索引
{
2011.01
73
charbuf[1000];
unsignedlongindexsize;
AVIINDEX*p;
fseek(avifp,0,SEEK_END);
buf[0]='i';buf[1]='d';buf[2]='x';buf[3]='1';
fwrite(buf,1,4,avifp);//
写入索引标记
indexsize=16*indexcount;
fwrite(&indexsize,1,4,avifp);//
写入索引段长度
if(aviindexlist->next!=NULL)
{
p=aviindexlist->next;
while(1)//
将链表中记录写入索引
{
fwrite(&p->dwChunkId,1,4,avifp);
fwrite(&p->dwFlags,1,4,avifp);
fwrite(&p->dwOffset,1,4,avifp);
fwrite(&p->dwSize,1,4,avifp);
if(p->next==NULL)break;
p=p->next;
}
}
}
使用如下代码更新
AVI
头:
voidUpdateAviHeader(void)//
更新
AVI
文件头
{
longlength;
length=326+movisize+16*indexcount;
fseek(avifp,4,SEEK_SET);
fwrite(&length,1,4,avifp);//
更新文件总长度
fseek(avifp,48,SEEK_SET);
fwrite(&totalframes,1,4,avifp);//
更新主信息头帧数
fseek(avifp,140,SEEK_SET);
fwrite(&totalframes,1,4,avifp);//
更新视频流头帧数
fseek(avifp,264,SEEK_SET);
fwrite(&audiosize,1,4,avifp);//
更新声音数据长度
fseek(avifp,318,SEEK_SET);
length=movisize+4;
fwrite(&length,1,4,avifp);//
更新
movi
列表长度
}
使用如下代码写
AVI
流:
UINTWriteAviStream(LPVOIDpParam)//
写
AVI
文件流
{
HINSTANCEhHandle;
typedefint(*Txvid_global)(void*handle,intopt,void
*param1,void*param2);
Txvid_globalxvid_global;
typedefint(*Txvid_encore)(void*handle,intopt,void
*param1,void*param2);
Txvid_encorexvid_encore;
74
2011.01
xvid_gbl_init_txvid_gbl_init;
void*enc_handle=NULL;
xvid_enc_create_txvid_enc_create;
xvid_enc_frame_txvid_enc_frame;
xvid_enc_stats_txvid_enc_stats;
unsignedchar*buf1=NULL,*buf2=NULL;
unsignedcharbuf[1000];
LPSTRbuf3;
DWORDlen,len1;
AVIINDEX*newlist,*thelist;
unsignedlongstartindex;
unsignedlongtick1,tick2;
unsignedinti;
WAVEFORMATEXwaveformat;
buf3=newchar[60000000];
tTag=1;
els=audiochannels;
esPerSec=audiorate;
tesPerSec=audiochannels*audiobits/
8*audiorate;
Align=audiochannels*audiobits/8;
erSample=audiobits;
=0;
waveInOpen(&wavein_handle,WAVE_MAPPER,&wavefor-
mat,NULL,
0L,CALLBACK_WINDOW);
=(char*)GlobalLock(GlobalAlloc
(GMEM_MOVEABLE|
GMEM_SHARE,60000000L));
memset(,0,60000000L);
erLength=60000000L;
sRecorded=0;
=0;
s=0;
s=0;
waveInPrepareHeader(wavein_handle,&waveinhdr,sizeof
(WAVEHDR));
waveInAddBuffer(wavein_handle,&waveinhdr,sizeof(WAVE-
HDR));
waveInStart(wavein_handle);//
开始录音
startindex=4;
hHandle=LoadLibrary("");//
加载
xvid_global=(Txvid_global)GetProcAddress(hHandle,"
xvid_global");
xvid_encore=(Txvid_encore)GetProcAddress(hHandle,"
xvid_encore");
memset(&xvid_gbl_init,0,sizeof(xvid_gbl_init_t));
xvid_gbl_n=XVID_VERSION;
xvid_gbl__flags=0;
xvid_gbl_=0;
xvid_global(NULL,XVID_GBL_INIT,&xvid_gbl_init,NULL);
//
全局初始化
memset(&xvid_enc_create,0,sizeof(xvid_enc_create_t));
xvid_enc_n=XVID_VERSION;
xvid_enc_=videowidth;
xvid_enc_=videoheight;
xvid_enc_e=XVID_PROFILE_S_L0;
xvid_enc_=1;
xvid_enc_=3;
if(xvid_encore(NULL,XVID_ENC_CREATE,&xvid_enc_create,
NULL)==0)
//
创建编码实例
{
enc_handle=xvid_enc_;
buf1=(unsignedchar*)malloc(5000000);
buf2=(unsignedchar*)malloc(5000000);
tick1=GetTickCount();
while(1)
{
tick2=GetTickCount();
if(indexcount%(videoframes+1)==0)//
写入音频流
{
waveInReset(wavein_handle);
buf[0]='0';buf[1]='1';buf[2]='w';buf[3]='b';
fwrite(buf,1,4,avifp);
len=sRecorded;
if(len%2!=0)len++;
fwrite(&len,1,4,avifp);
for(i=1;i<=sRecorded;i++)
buf3[i-1]=[i-1];
waveInUnprepareHeader(wavein_handle,&waveinhdr,
sizeof(WAVEHDR));
GlobalFree(GlobalHandle());
waveInClose(wavein_handle);
tTag=1;
els=audiochannels;
esPerSec=audiorate;
tesPerSec=audiochannels*audio-
bits/8*audiorate;
Align=audiochannels*audiobits/8;
erSample=audiobits;
=0;
waveInOpen(&wavein_handle,WAVE_MAPPER,
&waveformat,NULL,
0L,CALLBACK_WINDOW);
=(char*)GlobalLock(GlobalAlloc
(GMEM_MOVEABLE|
GMEM_SHARE,60000000L));
memset(,0,60000000L);
erLength=60000000L;
sRecorded=0;
=0;
s=0;
s=0;
waveInPrepareHeader(wavein_handle,&waveinhdr,
sizeof(WAVEHDR));
waveInAddBuffer(wavein_handle,&waveinhdr,
sizeof(WAVEHDR));
waveInStart(wavein_handle);
fwrite(buf3,1,len,avifp);
indexcount++;
movisize=movisize+len+8;
audiosize=audiosize+len;
newlist=newAVIINDEX;//
更新索引链表
newlist->dwChunkId=1651978544L;
newlist->dwFlags=0x10;
newlist->dwOffset=startindex;
newlist->dwSize=len;
newlist->next=NULL;
startindex=startindex+len+8;
thelist=aviindexlist;
while(1)
{
if(thelist->next==NULL)break;
thelist=thelist->next;
}
thelist->next=newlist;
}
if(tick2-tick1>=(1000/videoframes)||
totalframes/videoframes+1 (audiochannels*audiobits/8*audiorate)+1) // 写入视频流 { CopyBmpToMemory(CopyWindowToBitmap(),(char*) buf2); memset(&xvid_enc_frame,0,sizeof(xvid_enc_frame_t)); xvid_enc_n=XVID_VERSION; xvid_enc_eam=buf1; xvid_enc_=-1; xvid_enc_[0]=buf2+54; xvid_enc_=XVID_CSP_BGR| XVID_CSP_VFLIP; xvid_enc_[0]=videowidth*3; xvid_enc_=6; xvid_enc_=XVID_TYPE_AUTO; memset(&xvid_enc_stats,0,sizeof(xvid_enc_stats_t)); xvid_enc_n=XVID_VERSION; len=xvid_encore(enc_handle,XVID_ENC_ENCODE, &xvid_enc_frame, &xvid_enc_stats);//XVID 编码 if(len>0) { buf[0]='0';buf[1]='0';buf[2]='d';buf[3]='c';fwrite(buf, 1,4,avifp); (下转第 83 页) 2011.01 75 w(lv); // 将组织好的对话框布局添加到历史记录选择对话框中 tentView(ll); } 从上述代码中可以看出,列表( ListView )中选项的内容 都是由其适配器( Adapter )提供的,本应用中适配器的实现采 用的是继承 BaseAdapter 的方式。当 ListView 需要选项时,首 先会调用其对应 Adapter 的 getCount 方法获取选项的个数,然 后依次调用 getView 方法获取各个选项对象。本应用程序中, 每个选项都是由 TextView 实现的。 完成了上述代码的开发后,所有的代码开发工作就都完成了。 图 2 、图 3 所示的界面。 图 6bin 子目录下的 apk 包 7 结语 通过开发旅游景点位置查询的应用程序,读者可以基本掌 6 应用程序的打包发布 由于 Android 平台下的应用程序一般都是使用 Eclipse 开发 握 Android 平台下 Google 地图应用的开发,了解如何向地图中 添加自定义的内容。 本应用的功能还不够完善,读者可以自行进一步扩展。例 如结合 GPS 定位,开发出从当前位置到查询的旅游景点自动 路线导航的功能等。 (收稿日期: 2010-10-17 ) 的,因此打包与发布的工作非常简单。只要在开发完成后在 Eclipse 中运行一下项目,然后就可以在项目文件夹下的 bin 子 文件夹下找到可以用于安装发布的 APK 包,如图 6 所示。 将此 APK 包安装到基于 Android 平台的手机中即可运行, 在联网正确的状态下可以成功获取景点经纬度并显示如图 1 、 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! (上接第 75 页) len1=len; if(len%2!=0)len1++; fwrite(&len1,1,4,avifp); fwrite(buf1,1,len1,avifp); indexcount++; totalframes++; movisize=movisize+len1+8; newlist=newAVIINDEX;// 更新索引链表 newlist->dwChunkId=1667510320L; if(xvid_enc_==XVID_TYPE_IVOP) newlist->dwFlags=0x10; else newlist->dwFlags=0x00; newlist->dwOffset=startindex; newlist->dwSize=len; newlist->next=NULL; startindex=startindex+len1+8; thelist=aviindexlist; while(1) { if(thelist->next==NULL)break; thelist=thelist->next; } thelist->next=newlist; } tick1=tick2; } if(recording==0)break; Sleep(1); } free(buf1); free(buf2); xvid_encore(enc_handle,XVID_ENC_DESTROY,NULL, NULL);// 撤消编码实例 } FreeLibrary(hHandle); WriteAviIndex(); UpdateAviHeader(); fclose(avifp); waveInReset(wavein_handle); waveInUnprepareHeader(wavein_handle,&waveinhdr,sizeof (WAVEHDR)); GlobalFree(GlobalHandle()); waveInClose(wavein_handle);// 关闭声音输入 deletebuf3; return0; } 7 结语 AVI 录像机程序开发完成,对 AVI 多媒体文件的录制,具 有一定的实用价值。 (收稿日期: 2010-11-03 ) 2011.01 83


发布评论