246 lines
7.2 KiB
C
246 lines
7.2 KiB
C
/*
|
|
* Very simple AVIPLAYER
|
|
*
|
|
* Copyright 1999 Marcus Meissner
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Status:
|
|
* - plays .avi streams, video only
|
|
* - requires Microsoft avifil32.dll and builtin msvfw32.dll.
|
|
*
|
|
* Todo:
|
|
* - audio support (including synchronization etc)
|
|
* - replace DirectDraw by a 'normal' window, including dithering, controls
|
|
* etc.
|
|
*
|
|
* Bugs:
|
|
* - no time scheduling, video plays too fast using DirectDraw/XF86DGA
|
|
* - requires DirectDraw with all disadvantages.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
#include "windows.h"
|
|
#include "wingdi.h"
|
|
#include "mmsystem.h"
|
|
#include "ddraw.h"
|
|
#include "vfw.h"
|
|
|
|
|
|
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
|
|
{
|
|
int bytesline,i,n,pos;
|
|
time_t tstart,tend;
|
|
LONG cnt;
|
|
BITMAPINFOHEADER *bmi;
|
|
HRESULT hres;
|
|
PAVIFILE avif;
|
|
PAVISTREAM vids=NULL,auds=NULL;
|
|
AVIFILEINFO afi;
|
|
AVISTREAMINFO asi;
|
|
PGETFRAME vidgetframe=NULL;
|
|
LPDIRECTDRAW ddraw;
|
|
DDSURFACEDESC dsdesc;
|
|
LPDIRECTDRAWSURFACE dsurf;
|
|
LPDIRECTDRAWPALETTE dpal;
|
|
PALETTEENTRY palent[256];
|
|
|
|
AVIFileInit();
|
|
if (GetFileAttributes(cmdline) == INVALID_FILE_ATTRIBUTES) {
|
|
fprintf(stderr,"Usage: aviplay <avifilename>\n");
|
|
exit(1);
|
|
}
|
|
hres = AVIFileOpen(&avif,cmdline,OF_READ,NULL);
|
|
if (hres) {
|
|
fprintf(stderr,"AVIFileOpen: 0x%08lx\n",hres);
|
|
exit(1);
|
|
}
|
|
hres = AVIFileInfo(avif,&afi,sizeof(afi));
|
|
if (hres) {
|
|
fprintf(stderr,"AVIFileInfo: 0x%08lx\n",hres);
|
|
exit(1);
|
|
}
|
|
for (n=0;n<afi.dwStreams;n++) {
|
|
char buf[5];
|
|
PAVISTREAM ast;
|
|
|
|
hres = AVIFileGetStream(avif,&ast,0,n);
|
|
if (hres) {
|
|
fprintf(stderr,"AVIFileGetStream %d: 0x%08lx\n",n,hres);
|
|
exit(1);
|
|
}
|
|
hres = AVIStreamInfo(ast,&asi,sizeof(asi));
|
|
if (hres) {
|
|
fprintf(stderr,"AVIStreamInfo %d: 0x%08lx\n",n,hres);
|
|
exit(1);
|
|
}
|
|
fprintf(stderr,"[Stream %d: ",n);
|
|
buf[4]='\0';memcpy(buf,&(asi.fccType),4);
|
|
fprintf(stderr,"%s.",buf);
|
|
buf[4]='\0';memcpy(buf,&(asi.fccHandler),4);
|
|
fprintf(stderr,"%s, %s]\n",buf,asi.szName);
|
|
switch (asi.fccType) {
|
|
case streamtypeVIDEO:
|
|
vids = ast;
|
|
break;
|
|
case streamtypeAUDIO:
|
|
auds = ast;
|
|
break;
|
|
default: {
|
|
char type[5];
|
|
type[4]='\0';memcpy(type,&(asi.fccType),4);
|
|
|
|
fprintf(stderr,"Unhandled streamtype %s\n",type);
|
|
AVIStreamRelease(ast);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/********************* begin video setup ***********************************/
|
|
if (!vids) {
|
|
fprintf(stderr,"No video stream found. Good Bye.\n");
|
|
exit(0);
|
|
}
|
|
cnt = sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);
|
|
bmi = HeapAlloc(GetProcessHeap(),0,cnt);
|
|
hres = AVIStreamReadFormat(vids,0,bmi,&cnt);
|
|
if (hres) {
|
|
fprintf(stderr,"AVIStreamReadFormat vids: 0x%08lx\n",hres);
|
|
exit(1);
|
|
}
|
|
vidgetframe = NULL;
|
|
bmi->biCompression = 0; /* we want it in raw form, uncompressed */
|
|
/* recalculate the image size */
|
|
bmi->biSizeImage = ((bmi->biWidth*bmi->biBitCount+31)&~0x1f)*bmi->biPlanes*bmi->biHeight/8;
|
|
bytesline = ((bmi->biWidth*bmi->biBitCount+31)&~0x1f)*bmi->biPlanes/8;
|
|
vidgetframe = AVIStreamGetFrameOpen(vids,bmi);
|
|
if (!vidgetframe) {
|
|
fprintf(stderr,"AVIStreamGetFrameOpen: failed\n");
|
|
exit(1);
|
|
}
|
|
/********************* end video setup ***********************************/
|
|
|
|
/********************* begin display setup *******************************/
|
|
hres = DirectDrawCreate(NULL,&ddraw,NULL);
|
|
if (hres) {
|
|
fprintf(stderr,"DirectDrawCreate: 0x%08lx\n",hres);
|
|
exit(1);
|
|
}
|
|
hres = IDirectDraw_SetDisplayMode(ddraw,bmi->biWidth,bmi->biHeight,bmi->biBitCount);
|
|
if (hres) {
|
|
fprintf(stderr,"ddraw.SetDisplayMode: 0x%08lx (change resolution!)\n",hres);
|
|
exit(1);
|
|
}
|
|
dsdesc.dwSize=sizeof(dsdesc);
|
|
dsdesc.dwFlags = DDSD_CAPS;
|
|
dsdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
|
|
hres = IDirectDraw_CreateSurface(ddraw,&dsdesc,&dsurf,NULL);
|
|
if (hres) {
|
|
fprintf(stderr,"ddraw.CreateSurface: 0x%08lx\n",hres);
|
|
exit(1);
|
|
}
|
|
if (bmi->biBitCount==8) {
|
|
RGBQUAD *rgb = (RGBQUAD*)(bmi+1);
|
|
int i;
|
|
|
|
hres = IDirectDraw_CreatePalette(ddraw,DDPCAPS_8BIT,NULL,&dpal,NULL);
|
|
if (hres) {
|
|
fprintf(stderr,"ddraw.CreateSurface: 0x%08lx\n",hres);
|
|
exit(1);
|
|
}
|
|
IDirectDrawSurface_SetPalette(dsurf,dpal);
|
|
for (i=0;i<bmi->biClrUsed;i++) {
|
|
palent[i].peRed = rgb[i].rgbRed;
|
|
palent[i].peBlue = rgb[i].rgbBlue;
|
|
palent[i].peGreen = rgb[i].rgbGreen;
|
|
}
|
|
IDirectDrawPalette_SetEntries(dpal,0,0,bmi->biClrUsed,palent);
|
|
} else
|
|
dpal = NULL;
|
|
/********************* end display setup *******************************/
|
|
|
|
tstart = time(NULL);
|
|
pos = 0;
|
|
while (1) {
|
|
LPVOID decodedframe;
|
|
LPBITMAPINFOHEADER lpbmi;
|
|
LPVOID decodedbits;
|
|
|
|
/* video stuff */
|
|
if (!(decodedframe=AVIStreamGetFrame(vidgetframe,pos++)))
|
|
break;
|
|
lpbmi = (LPBITMAPINFOHEADER)decodedframe;
|
|
decodedbits = (LPVOID)(((DWORD)decodedframe)+lpbmi->biSize);
|
|
if (lpbmi->biBitCount == 8) {
|
|
/* can't detect palette change that way I think */
|
|
RGBQUAD *rgb = (RGBQUAD*)(lpbmi+1);
|
|
int i,palchanged;
|
|
|
|
/* skip used colorentries. */
|
|
decodedbits=(char*)decodedbits+bmi->biClrUsed*sizeof(RGBQUAD);
|
|
palchanged = 0;
|
|
for (i=0;i<bmi->biClrUsed;i++) {
|
|
if ( (palent[i].peRed != rgb[i].rgbRed) ||
|
|
(palent[i].peBlue != rgb[i].rgbBlue) ||
|
|
(palent[i].peGreen != rgb[i].rgbGreen)
|
|
) {
|
|
palchanged = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (palchanged) {
|
|
for (i=0;i<bmi->biClrUsed;i++) {
|
|
palent[i].peRed = rgb[i].rgbRed;
|
|
palent[i].peBlue = rgb[i].rgbBlue;
|
|
palent[i].peGreen = rgb[i].rgbGreen;
|
|
}
|
|
IDirectDrawPalette_SetEntries(dpal,0,0,bmi->biClrUsed,palent);
|
|
}
|
|
}
|
|
dsdesc.dwSize = sizeof(dsdesc);
|
|
hres = IDirectDrawSurface_Lock(dsurf,NULL,&dsdesc,DDLOCK_WRITEONLY,0);
|
|
if (hres) {
|
|
fprintf(stderr,"dsurf.Lock: 0x%08lx\n",hres);
|
|
exit(1);
|
|
}
|
|
/* Argh. AVIs are upside down. */
|
|
for (i=0;i<dsdesc.dwHeight;i++) {
|
|
memcpy( (char *)dsdesc.lpSurface+(i*dsdesc.u1.lPitch),
|
|
(char *)decodedbits+bytesline*(dsdesc.dwHeight-i-1),
|
|
bytesline
|
|
);
|
|
}
|
|
IDirectDrawSurface_Unlock(dsurf,dsdesc.lpSurface);
|
|
}
|
|
tend = time(NULL);
|
|
AVIStreamGetFrameClose(vidgetframe);
|
|
|
|
IDirectDrawSurface_Release(dsurf);
|
|
IDirectDraw_RestoreDisplayMode(ddraw);
|
|
IDirectDraw_Release(ddraw);
|
|
if (vids) AVIStreamRelease(vids);
|
|
if (auds) AVIStreamRelease(auds);
|
|
fprintf(stderr,"%d frames at %g frames/s\n",pos,pos*1.0/(tend-tstart));
|
|
AVIFileRelease(avif);
|
|
AVIFileExit();
|
|
return 0;
|
|
}
|