Microsoft-3D-Movie-Maker/kauai/SRC/VIDEO.CPP
2022-05-03 16:31:19 -07:00

763 lines
17 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Author: ShonK
Project: Kauai
Copyright (c) Microsoft Corporation
Graphical video object implementation.
***************************************************************************/
#include "frame.h"
#ifdef WIN
#include "mciavi.h"
#endif //WIN
ASSERTNAME
RTCLASS(GVID)
RTCLASS(GVDS)
RTCLASS(GVDW)
BEGIN_CMD_MAP_BASE(GVDS)
END_CMD_MAP(&GVDS::FCmdAll, pvNil, kgrfcmmAll)
const long kcmhlGvds = kswMin; //put videos at the head of the list
/***************************************************************************
Create a video - either an HWND based one, or just a video stream,
depending on fHwndBased. pgobBase is assumed to be valid for the life
of the video.
***************************************************************************/
PGVID GVID::PgvidNew(PFNI pfni, PGOB pgobBase, bool fHwndBased, long hid)
{
AssertPo(pfni, ffniFile);
AssertPo(pgobBase, 0);
if (fHwndBased)
return GVDW::PgvdwNew(pfni, pgobBase, hid);
return GVDS::PgvdsNew(pfni, pgobBase, hid);
}
/***************************************************************************
Constructor for a generic video.
***************************************************************************/
GVID::GVID(long hid) : GVID_PAR(hid)
{
AssertBaseThis(0);
}
/***************************************************************************
Constructor for video stream class.
***************************************************************************/
GVDS::GVDS(long hid) : GVDS_PAR(hid)
{
AssertBaseThis(0);
#ifdef WIN
AVIFileInit();
#endif //WIN
}
/***************************************************************************
Destructor for video stream class.
***************************************************************************/
GVDS::~GVDS(void)
{
AssertBaseThis(0);
#ifdef WIN
if (hNil != _hdd)
DrawDibClose(_hdd);
if (pvNil != _pavig)
AVIStreamGetFrameClose(_pavig);
if (pvNil != _pavis)
AVIStreamRelease(_pavis);
if (pvNil != _pavif)
AVIFileRelease(_pavif);
AVIFileExit();
#endif //WIN
}
/***************************************************************************
Initialize a video stream object.
***************************************************************************/
bool GVDS::_FInit(PFNI pfni, PGOB pgobBase)
{
AssertBaseThis(0);
AssertPo(pfni, ffniFile);
AssertPo(pgobBase, 0);
#ifdef WIN
STN stn;
AVIFILEINFO afi;
_pgobBase = pgobBase;
pfni->GetStnPath(&stn);
if (0 != AVIFileOpen(&_pavif, stn.Psz(),
OF_READ | OF_SHARE_DENY_WRITE, pvNil))
{
_pavif = pvNil;
goto LFail;
}
if (0 != AVIFileGetStream(_pavif, &_pavis, streamtypeVIDEO, 0))
{
_pavis = pvNil;
goto LFail;
}
if (pvNil == (_pavig = AVIStreamGetFrameOpen(_pavis, pvNil)))
goto LFail;
if (0 != AVIFileInfo(_pavif, &afi, size(afi)))
goto LFail;
_dxp = afi.dwWidth;
_dyp = afi.dwHeight;
if (0 > (_nfrMac = AVIStreamLength(_pavis)))
goto LFail;
if (0 > (_dnfr = AVIStreamStart(_pavis)))
goto LFail;
if (hNil == (_hdd = DrawDibOpen()))
{
LFail:
PushErc(ercCantOpenVideo);
return fFalse;
}
_nfrCur = 0;
_nfrMarked = -1;
return fTrue;
#else //!WIN
RawRtn();
return fFalse;
#endif //!WIN
}
/***************************************************************************
Create a new video stream object.
***************************************************************************/
PGVDS GVDS::PgvdsNew(PFNI pfni, PGOB pgobBase, long hid)
{
AssertPo(pfni, ffniFile);
PGVDS pgvds;
if (hid == hidNil)
hid = CMH::HidUnique();
if (pvNil == (pgvds = NewObj GVDS(hid)))
return pvNil;
if (!pgvds->_FInit(pfni, pgobBase))
{
ReleasePpo(&pgvds);
return pvNil;
}
return pgvds;
}
/***************************************************************************
Return the number of frames in the video.
***************************************************************************/
long GVDS::NfrMac(void)
{
AssertThis(0);
return _nfrMac;
}
/***************************************************************************
Return the current frame of the video.
***************************************************************************/
long GVDS::NfrCur(void)
{
AssertThis(0);
return _nfrCur;
}
/***************************************************************************
Advance to a particular frame. If we are playing, stop playing. This
only changes internal state and doesn't mark anything.
***************************************************************************/
void GVDS::GotoNfr(long nfr)
{
AssertThis(0);
AssertIn(nfr, 0, _nfrMac);
Stop();
_nfrCur = nfr;
}
/***************************************************************************
Return whether or not the video is playing.
***************************************************************************/
bool GVDS::FPlaying(void)
{
AssertThis(0);
return _fPlaying;
}
/***************************************************************************
Start playing at the current frame. This assumes the gob is valid
until the video is stopped or nuked. The gob should call this video's
Draw method in its Draw method.
***************************************************************************/
bool GVDS::FPlay(RC *prc)
{
AssertThis(0);
AssertNilOrVarMem(prc);
Stop();
if (!vpcex->FAddCmh(this, kcmhlGvds, kgrfcmmAll))
return fFalse;
SetRcPlay(prc);
_fPlaying = fTrue;
#ifdef WIN
_tsPlay = TsCurrent() - AVIStreamSampleToTime(_pavis, _nfrCur + _dnfr);
#endif //WIN
return fTrue;
}
/***************************************************************************
Set the rectangle to play into.
***************************************************************************/
void GVDS::SetRcPlay(RC *prc)
{
AssertThis(0);
AssertNilOrVarMem(prc);
if (pvNil == prc)
_rcPlay.Set(0, 0, _dxp, _dyp);
else
_rcPlay = *prc;
}
/***************************************************************************
Stop playing.
***************************************************************************/
void GVDS::Stop(void)
{
AssertThis(0);
vpcex->RemoveCmh(this, kcmhlGvds);
_fPlaying = fFalse;
}
/***************************************************************************
Intercepts all commands, so we get to play our movie no matter what.
***************************************************************************/
bool GVDS::FCmdAll(PCMD pcmd)
{
AssertThis(0);
AssertVarMem(pcmd);
if (!_fPlaying)
{
Stop();
return fFalse;
}
// update _nfrCur
#ifdef WIN
_nfrCur = AVIStreamTimeToSample(_pavis, TsCurrent() - _tsPlay) - _dnfr;
if (_nfrCur >= _nfrMac)
_nfrCur = _nfrMac - 1;
else if (_nfrCur < 0)
_nfrCur = 0;
#endif //WIN
if (_nfrCur != _nfrMarked)
{
_pgobBase->InvalRc(&_rcPlay, kginMark);
_nfrMarked = _nfrCur;
}
if (_nfrCur >= _nfrMac - 1)
Stop();
return fFalse;
}
/***************************************************************************
Call this to draw the current state of the video image.
***************************************************************************/
void GVDS::Draw(PGNV pgnv, RC *prc)
{
AssertThis(0);
AssertPo(pgnv, 0);
AssertVarMem(prc);
RC rc;
#ifdef WIN
BITMAPINFOHEADER *pbi;
if (pvNil == (pbi = (BITMAPINFOHEADER *)AVIStreamGetFrame(_pavig,
_nfrCur + _dnfr)))
{
return;
}
pgnv->DrawDib(_hdd, pbi, prc);
#endif //WIN
}
/***************************************************************************
Get the normal rectangle for the movie (top-left at (0, 0)).
***************************************************************************/
void GVDS::GetRc(RC *prc)
{
AssertThis(0);
AssertVarMem(prc);
prc->Set(0, 0, _dxp, _dyp);
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a GVDS.
***************************************************************************/
void GVDS::AssertValid(ulong grf)
{
GVDS_PAR::AssertValid(0);
AssertPo(_pgobBase, 0);
//REVIEW shonk: fill in GVDS::AssertValid
}
#endif //DEBUG
/***************************************************************************
Create a new video window.
***************************************************************************/
PGVDW GVDW::PgvdwNew(PFNI pfni, PGOB pgobBase, long hid)
{
AssertPo(pfni, ffniFile);
PGVDW pgvdw;
if (hid == hidNil)
hid = CMH::HidUnique();
if (pvNil == (pgvdw = NewObj GVDW(hid)))
return pvNil;
if (!pgvdw->_FInit(pfni, pgobBase))
{
ReleasePpo(&pgvdw);
return pvNil;
}
return pgvdw;
}
/***************************************************************************
Constructor for a video window.
***************************************************************************/
GVDW::GVDW(long hid) : GVDW_PAR(hid)
{
AssertBaseThis(0);
}
/***************************************************************************
Destructor for a video window.
***************************************************************************/
GVDW::~GVDW(void)
{
AssertBaseThis(0);
#ifdef WIN
if (_fDeviceOpen)
{
MCI_GENERIC_PARMS mci;
PSNDV psndv;
mciSendCommand(_lwDevice, MCI_CLOSE, MCI_WAIT, (long)&mci);
if (pvNil != vpsndm &&
pvNil != (psndv = vpsndm->PsndvFromCtg(kctgWave)))
{
psndv->Suspend(fFalse);
}
}
#endif //WIN
}
/***************************************************************************
Initialize the GVDW.
***************************************************************************/
bool GVDW::_FInit(PFNI pfni, PGOB pgobBase)
{
AssertPo(pfni, ffniFile);
AssertPo(pgobBase, 0);
_pgobBase = pgobBase;
#ifdef WIN
MCI_ANIM_OPEN_PARMS mciOpen;
MCI_STATUS_PARMS mciStatus;
MCI_ANIM_RECT_PARMS mciRect;
STN stn;
PSNDV psndv;
pfni->GetStnPath(&stn);
ClearPb(&mciOpen, size(mciOpen));
mciOpen.lpstrDeviceType = PszLit("avivideo");
mciOpen.lpstrElementName = stn.Psz();
mciOpen.dwStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_DISABLED;
mciOpen.hWndParent = _pgobBase->HwndContainer();
if (0 != mciSendCommand(0, MCI_OPEN,
MCI_OPEN_TYPE | MCI_OPEN_ELEMENT |
MCI_ANIM_OPEN_PARENT | MCI_ANIM_OPEN_WS,
(long)&mciOpen))
{
goto LFail;
}
_lwDevice = mciOpen.wDeviceID;
_fDeviceOpen = fTrue;
if (pvNil != vpsndm &&
pvNil != (psndv = vpsndm->PsndvFromCtg(kctgWave)))
{
psndv->Suspend(fTrue);
}
// get the hwnd
ClearPb(&mciStatus, size(mciStatus));
// REVIEW shonk: mmsystem.h defines MCI_ANIM_STATUS_HWND as 0x00004003,
// which doesn't give us the hwnd. 4001 does!
mciStatus.dwItem = 0x00004001;
if (0 != mciSendCommand(_lwDevice, MCI_STATUS, MCI_STATUS_ITEM,
(long)&mciStatus))
{
goto LFail;
}
_hwndMovie = (HWND)mciStatus.dwReturn;
// get the length
ClearPb(&mciStatus, size(mciStatus));
mciStatus.dwItem = MCI_STATUS_LENGTH;
mciStatus.dwTrack = 1;
if (0 != mciSendCommand(_lwDevice, MCI_STATUS, MCI_STATUS_ITEM,
(long)&mciStatus))
{
goto LFail;
}
_nfrMac = mciStatus.dwReturn;
// get the rectangle
if (0 != mciSendCommand(_lwDevice, MCI_WHERE, MCI_ANIM_WHERE_SOURCE,
(long)&mciRect))
{
goto LFail;
}
_rcPlay = (RC)mciRect.rc;
_dxp = _rcPlay.Dxp();
_dyp = _rcPlay.Dyp();
mciSendCommand(_lwDevice, MCI_REALIZE, MCI_ANIM_REALIZE_BKGD, 0);
_cactPal = vcactRealize;
return fTrue;
LFail:
#endif //WIN
#ifdef MAC
RawRtn(); //REVIEW shonk: Mac: implement GVDW::_FInit
#endif //MAC
PushErc(ercCantOpenVideo);
return fFalse;
}
/***************************************************************************
Return the number of frames in the video.
***************************************************************************/
long GVDW::NfrMac(void)
{
AssertThis(0);
return _nfrMac;
}
/***************************************************************************
Return the current frame of the video.
***************************************************************************/
long GVDW::NfrCur(void)
{
AssertThis(0);
#ifdef WIN
MCI_STATUS_PARMS mciStatus;
// get the position
ClearPb(&mciStatus, size(mciStatus));
mciStatus.dwItem = MCI_STATUS_POSITION;
mciStatus.dwTrack = 1;
if (0 != mciSendCommand(_lwDevice, MCI_STATUS, MCI_STATUS_ITEM,
(long)&mciStatus))
{
Warn("getting position failed");
return 0;
}
return mciStatus.dwReturn;
#endif //WIN
#ifdef MAC
RawRtn(); //REVIEW shonk: Mac: implement GVDW::NfrCur
return 0;
#endif //MAC
}
/***************************************************************************
Advance to a particular frame. If we are playing, stop playing. This
only changes internal state and doesn't mark anything.
***************************************************************************/
void GVDW::GotoNfr(long nfr)
{
AssertThis(0);
AssertIn(nfr, 0, _nfrMac);
#ifdef WIN
MCI_SEEK_PARMS mciSeek;
ClearPb(&mciSeek, size(mciSeek));
mciSeek.dwTo = nfr;
if (0 != mciSendCommand(_lwDevice, MCI_SEEK, MCI_TO,
(long)&mciSeek))
{
Warn("seeking failed");
}
#endif //WIN
#ifdef MAC
RawRtn(); //REVIEW shonk: Mac: implement GVDW::GotoNfr
#endif //MAC
}
/***************************************************************************
Return whether or not the video is playing.
***************************************************************************/
bool GVDW::FPlaying(void)
{
AssertThis(0);
if (!_fPlaying)
return fFalse;
#ifdef WIN
MCI_STATUS_PARMS mciStatus;
// get the mode
ClearPb(&mciStatus, size(mciStatus));
mciStatus.dwItem = MCI_STATUS_MODE;
mciStatus.dwTrack = 1;
if (0 == mciSendCommand(_lwDevice, MCI_STATUS, MCI_STATUS_ITEM,
(long)&mciStatus) &&
(MCI_MODE_STOP == mciStatus.dwReturn ||
MCI_MODE_PAUSE == mciStatus.dwReturn))
{
_fPlaying = fFalse;
}
#endif //WIN
#ifdef MAC
RawRtn(); //REVIEW shonk: Mac: implement GVDW::NfrCur
#endif //MAC
return _fPlaying;
}
/***************************************************************************
Start playing at the current frame. This assumes the gob is valid
until the video is stopped or nuked. The gob should call this video's
Draw method in its Draw method.
***************************************************************************/
bool GVDW::FPlay(RC *prc)
{
AssertThis(0);
AssertNilOrVarMem(prc);
Stop();
#ifdef WIN
MCI_ANIM_PLAY_PARMS mciPlay;
// get the play rectangle
SetRcPlay(prc);
// position the hwnd
_SetRc();
// start the movie playing
ClearPb(&mciPlay, size(mciPlay));
if (0 != mciSendCommand(_lwDevice, MCI_PLAY, MCI_MCIAVI_PLAY_WINDOW,
(long)&mciPlay))
{
return fFalse;
}
_fPlaying = fTrue;
return fTrue;
#endif //WIN
#ifdef MAC
RawRtn(); //REVIEW shonk: Mac: implement GVDW::NfrCur
return fFalse;
#endif //MAC
}
/***************************************************************************
Set the rectangle to play into.
***************************************************************************/
void GVDW::SetRcPlay(RC *prc)
{
AssertThis(0);
AssertNilOrVarMem(prc);
if (pvNil == prc)
_rcPlay.Set(0, 0, _dxp, _dyp);
else
_rcPlay = *prc;
}
/***************************************************************************
Stop playing.
***************************************************************************/
void GVDW::Stop(void)
{
AssertThis(0);
if (!_fPlaying)
return;
#ifdef WIN
MCI_GENERIC_PARMS mciPause;
ClearPb(&mciPause, size(mciPause));
mciSendCommand(_lwDevice, MCI_PAUSE, 0, (long)&mciPause);
#endif //WIN
#ifdef MAC
RawRtn(); //REVIEW shonk: Mac: implement GVDW::Stop
#endif //MAC
_fPlaying = fFalse;
}
/***************************************************************************
Call this to draw the current state of the video image.
***************************************************************************/
void GVDW::Draw(PGNV pgnv, RC *prc)
{
AssertThis(0);
AssertPo(pgnv, 0);
AssertVarMem(prc);
_SetRc();
}
/***************************************************************************
Position the hwnd associated with the video to match the GOB's position.
***************************************************************************/
void GVDW::_SetRc(void)
{
AssertThis(0);
RC rcGob, rc;
_pgobBase->GetRc(&rcGob, cooHwnd);
rc = _rcPlay;
rc.Offset(rcGob.xpLeft, rcGob.ypTop);
if (_rc != rc || !_fVisible)
{
#ifdef WIN
MoveWindow(_hwndMovie, rc.xpLeft, rc.ypTop,
rc.Dxp(), rc.Dyp(), fTrue);
if (!_fVisible)
{
MCI_ANIM_WINDOW_PARMS mciWindow;
// show the playback window
ClearPb(&mciWindow, size(mciWindow));
mciWindow.nCmdShow = SW_SHOW;
mciSendCommand(_lwDevice, MCI_WINDOW, MCI_ANIM_WINDOW_STATE,
(long)&mciWindow);
_fVisible = fTrue;
}
#endif //WIN
#ifdef MAC
RawRtn(); //REVIEW shonk: Mac: implement GVDW::_SetRc
#endif //MAC
_rc = rc;
}
if (_cactPal != vcactRealize)
{
#ifdef WIN
mciSendCommand(_lwDevice, MCI_REALIZE, MCI_ANIM_REALIZE_BKGD, 0);
#endif //WIN
#ifdef MAC
RawRtn(); //REVIEW shonk: Mac: implement GVDW::_SetRc
#endif //MAC
_cactPal = vcactRealize;
}
}
/***************************************************************************
Get the normal rectangle for the movie (top-left at (0, 0)).
***************************************************************************/
void GVDW::GetRc(RC *prc)
{
AssertThis(0);
AssertVarMem(prc);
prc->Set(0, 0, _dxp, _dyp);
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a GVDW.
***************************************************************************/
void GVDW::AssertValid(ulong grf)
{
GVDW_PAR::AssertValid(0);
Assert(_hwndMovie != hNil, 0);
AssertPo(_pgobBase, 0);
}
#endif //DEBUG