Tuesday, May 16, 2006

pre-fetching video


bool CPlaylistDlg::PreFetchVideo()
{
CAviToBmp vid;
for (int i = 0; i < BANKS; i++) {
int count = m_Bank[i].GetSize();
for (int j = 0; j < count; j++) {
LPCSTR Path = m_Bank[i][j].m_Path;
if (IsVideo(Path)) {
if (!vid.Open(Path))
return(FALSE);
}
}
}
return(TRUE);
}

This takes approx. 10 seconds for all 340 clips. We can't pre-open the clips because AVIStreamGetFrameOpen won't open more than 75 AVI files simultaneously (it fails with no error message). Not sure why just opening and closing the clips helps. Something to do with disk caching? Not sure how *much* it helps either.

The 10 second delay can be avoided by doing the pre-fetch in a background thread. Note the CoInitialize! AviFileOpen fails without it. This gives me a bad feeling that VfW isn't thread-safe...

UINT CPlaylistDlg::PreFetchVideoThread(LPVOID pParam)
{
CoInitialize(0);
CPlaylistDlg *pd = (CPlaylistDlg *)pParam;
if (!pd->PreFetchVideo())
AfxMessageBox("Can't pre-fetch video");
CoUninitialize();
return(0);
}

in CPlaylistDlg::Cache:
AfxBeginThread(PreFetchVideoThread, this,
THREAD_PRIORITY_BELOW_NORMAL, 0, 0, 0);

The 75 clip limit is apparently NOT due to XVID, it occurs with Cinepack too.

Test to determine how much of Open delay is due to AVIStreamGetFrameOpen. Result for 100 Opens:

total avg min max
Open 4.915 .049 .007 .087
GetFr. 0.790 .008 .007 .009

A second run gave very similar results:

Open 3.119 .031 .007 .087
GetFr. 0.741 .007 .007 .009

Result: AVIStreamGetFrameOpen is less than 15% of the average, but more importantly, it has a static cost of ~8 milliseconds. This makes a reasonable case for trying the partial pre-open strategy (pre-open all clips but without doing AVIStreamGetFrameOpen, and then do AVIStreamGetFrameOpen when the clip is selected).

Wednesday, May 10, 2006

exporting a looped movie

Tried exporting a bidirectional movie (plays forward and then plays in reverse). It's smoother than just letting the video jump back to the beginning but there's still a visible transition when the rings change direction.

int PassFrames = m_RangeLast - m_RangeFirst + 1;
int TotalFrames = (PassFrames - 1) * 2;
pd.Create();
int j = m_RangeLast;
pd.SetRange(0, TotalFrames);
for (int i = 0; i < TotalFrames; i++) {
if (i >= PassFrames)
sm.Seek(--j);
sm.Read();
HBITMAP bm = View->MakeDIB(&OutFrameSize, &m_InFrameSize, GetScaleToFit());
bool retc = bta.AddFrame(bm);
DeleteObject(bm);
if (!m_DisplayOutput) // prevent view from painting
View->ValidateRect(NULL); // must revalidate before SetPos
pd.SetPos(i);
if (pd.Canceled())
return(IDCANCEL);
}

Lissajous

Tried it. It's too predictable and hugs the edge of the frame too much. Not so good! The random motion is better.

Tuesday, May 09, 2006

video mirroring quadrant selection


CSize FrmSz(m_Size);
CSize SrcSz(vp->GetFrameSize());
CPoint SrcPt;
if (m_st.Mirror) {
FrmSz.cx = (FrmSz.cx + 1) >> 1;
FrmSz.cy = (FrmSz.cy + 1) >> 1;
switch (Quadrant) {
case 0: // upper left
SrcPt = CPoint(0, SrcSz.cy - 1);
SrcSz.cx >>= 1;
SrcSz.cy >>= 1;
SrcSz.cy = -SrcSz.cy;
break;
case 1: // upper right
SrcPt = CPoint(SrcSz.cx - 1, SrcSz.cy - 1);
SrcSz.cx >>= 1;
SrcSz.cy >>= 1;
SrcSz.cx = -SrcSz.cx;
SrcSz.cy = -SrcSz.cy;
break;
case 2: // lower left
SrcPt = CPoint(0, 0);
SrcSz.cx >>= 1;
SrcSz.cy >>= 1;
break;
case 3: // lower right
SrcPt = CPoint(SrcSz.cx - 1, 0);
SrcSz.cx >>= 1;
SrcSz.cy >>= 1;
SrcSz.cx = -SrcSz.cx;
break;
}
} else {
SrcPt = CPoint(0, SrcSz.cy - 1);
SrcSz.cy = -SrcSz.cy;
}
HDC sdc;
vp->GetDC(&sdc);
StretchBlt(dc, 0, 0, FrmSz.cx, FrmSz.cy, sdc,
SrcPt.x, SrcPt.y, SrcSz.cx, SrcSz.cy, m_VideoList.GetROP());
vp->ReleaseDC(sdc);