Play AVI with DirectX |
Windows NT: Requires version 3.1 or later.
Windows: Requires Windows 95 or later.
Windows CE: Unsupported.
Header: Declared in vfw.h.
Import Library: Use vfw32.lib.
variabili globali:La seconda riga permette di accedere a un oggetto di tipo critical section. Se qualcuno vuole approfondire l'argomento c'è tutto quello che desidera su MSDN.
CRITICAL_SECTION m_csAccessBuffercodice:
AVIFileInit();
InitializeCriticalSection(&m_csAccessBuffer);
variabili globali:Apro il file AVI (il cui nome è nella stringa filename) e preparo due variabili pasMovie e asiMovie.
PAVISTREAM m_pasMovie;
AVISTREAMINFO m_asiMovie;codice:
ZeroMemory(&m_asiMovie,sizeof(m_asiMovie));
if (AVIStreamOpenFromFile(&m_pasMovie,filename,streamtypeVIDEO, 0,OF_READ,NULL))
{
AVIFileExit();
return FALSE;
}
LPDIRECTDRAW lpdd;
DDSURFACEDESC ddsd;In questo modo ho ottenuto dall'interfaccia IDirectDraw il pixel format della surface in cui voglio renderizzare il filmato. Questo codice può essere sostituito da quello che richiede il pixel format dalla surface scelta.
ZeroMemory(&ddsd,sizeof(DDSURFACEDESC));
ddsd.dwSize = sizeof(DDSURFACEDESC);
if(lpdd->GetDisplayMode(&ddsd)==0) return Initialize(&ddsd.ddpfPixelFormat);
variabili globali:restituisce informazioni varie e sulla dimensione dell'header
LPBITMAPV4HEADER m_lpb4hTargetFmt; Il formato della superficie di Output
LPBITMAPINFOHEADER m_lpScrFmt; Il formato della superficie di Input
LPBYTE m_lpInput, m_lpOutput;
variabili locali:
LONG lFmtLenght;
DWORD bitsize;AVIStreamFormatSize(m_pasMovie,0,&lFmtLenght);
m_lpScrFmt = (LPBITMAPINFOHEADER)stdalloc(lFmtLenght);al posto di stdalloc usate il metodo di allocazione prefetito
m_lpb4hTargetFmt = (LPBITMAPV4HEADER)stdalloc(max(lFmtLenght, sizeof(BITMAPV4HEADER)));
ZeroMemory(m_lpb4hTargetFmt,sizeof(BITMAPV4HEADER));
AVIStreamReadFormat(m_pasMovie,0,m_lpScrFmt,&lFmtLenght);
Chiediamo il numero di frame:
m_lFrames = AVIStreamLength(m_pasMovie);Chiediamo informazioni sulla compressione:
AVIStreamInfo(m_pasMovie,&m_asiMovie,sizeof(AVISTREAMINFO));A questo punto hai le informazioni sul tipo di compressione:
Creiamo la caratteristiche della superficie destinazione:
memcpy(m_lpb4hTargetFmt,m_lpScrFmt,lFmtLenght);Settiamo il bit depth della destinazione con il pixelformat passato alla procedura e ottenuto dal DirectDraw:
m_lpb4hTargetFmt->bV4Size = max(lFmtLenght,sizeof(BITMAPV4HEADER));
m_lpb4hTargetFmt->bV4BitCount = lppf->dwRGBBitCount;Per comodità precalcoliamo i byte per pixel:
bitsize = (m_lpb4hTargetFmt->bV4BitCount+7)>>3;Per via empirica a questo punto io ho fatto questi settaggi. Funzionano ed è meglio tenerli così
m_lpb4hTargetFmt->bV4V4Compression = BI_BITFIELDS;Prendiamo i valori delle maschere di BIT direttamente dal pixel format del directdraw, un puntatore LPDDPIXELFORMAT lppf che avevo ottenuto precedentemente e ho passato adesso.
if ((m_lpb4hTargetFmt->bV4BitCount==24)
|| (m_lpb4hTargetFmt->bV4BitCount==32))
m_lpb4hTargetFmt->bV4V4Compression = BI_RGB;m_lpb4hTargetFmt->bV4ClrUsed = 0;
m_lpb4hTargetFmt->bV4RedMask = lppf->dwRBitMask;Precalcoliamo le dimensioni delle superfici di rendering, serviranno in futuro.
m_lpb4hTargetFmt->bV4GreenMask = lppf->dwGBitMask;
m_lpb4hTargetFmt->bV4BlueMask = lppf->dwBBitMask;
m_lpb4hTargetFmt->bV4AlphaMask = lppf->dwRGBAlphaBitMask;
m_lLinePitch = m_lpb4hTargetFmt->bV4Width * bitsize;Se invece il file suggerisce una dimensione usiamo questa
m_lLength = m_lpb4hTargetFmt->bV4SizeImage = m_lLinePitch * m_lpb4hTargetFmt->bV4Height;
if (m_asiMovie.dwSuggestedBufferSize)
m_lLength = m_asiMovie.dwSuggestedBufferSize;
m_hicDecompressor = ICDecompressOpen(ICTYPE_VIDEO,Allochiamo la memoria per le superfici di input e output:
m_asiMovie.fccHandler,
m_lpScrFmt,
(LPBITMAPINFOHEADER)m_lpb4hTargetFmt);
if (!m_hicDecompressor)
return FALSE;
m_lpInput = (BYTE *) stdalloc(m_lLength);
m_lpOutput = (BYTE *) stdalloc(m_lpb4hTargetFmt->bV4SizeImage);ICDecompressBegin(m_hicDecompressor,m_lpScrFmt,
(LPBITMAPINFOHEADER)m_lpb4hTargetFmt);m_iTimeTick = (1000*m_asiMovie.dwScale +
(m_asiMovie.dwRate>>1)) /m_asiMovie.dwRate;
variabili Globali:Questa procedura richiede in ingresso un integer iFrame che indica il numero del frame di cui si vuole fare il rendering.
UINT m_update;
codice:
m_update++;
if (iFrame<m_lFrames) {
AVIStreamRead(m_pasMovie, iFrame, 1, m_lpInput, m_lLength, NULL, NULL);
EnterCriticalSection(&m_csAccessBuffer);
ICDecompress(m_hicDecompressor, 0, m_lpScrFmt, m_lpInput,
(LPBITMAPINFOHEADER)m_lpb4hTargetFmt, m_lpOutput);
LeaveCriticalSection(&m_csAccessBuffer);
return m_lpOutput;
}
chiudo il decompressore
if (m_hicDecompressor) {libero la memoria dei buffer io
ICDecompressEnd(m_hicDecompressor);
ICClose(m_hicDecompressor);
}
stdfree(m_lpOutput);Libero la memroria delle strutture bitmap
stdfree(m_lpInput);
stdfree(m_lpScrFmt);Rilascio lo stream AVI
stdfree(m_lpb4hTargetFmt);
AVIStreamRelease(m_pasMovie);Come al solito al posto di stdfree usate la procedura che usate voi solitamente per liberare la memoria.
AVIFileExit();
DeleteCriticalSection(&m_csAccessBuffer);
Variabili globali:
PAVISTREAM m_pasSound; Handle dello stream sonoro nel file AVI
AVISTREAMINFO m_asiSound; Informazioni sullo stream sonoro
DWORD m_dwLoadPos, m_dwLoadSize;
DWORD m_dwSpf;
m_iSoundFramesAhead = 0;
m_dwSoundFrame = m_dwBufferSize = 0;
m_dwLoadPos = m_dwLoadSize = 0;
m_dwSpf = 0;
m_lpdsbTon = NULL;ZeroMemory(&m_pasSound,sizeof(m_pasSound));
m_lpSoundScrFmt = 0;
if (AVIStreamOpenFromFile(&m_pasSound,filename,streamtypeAUDIO,
0,OF_READ,NULL))
{
AVIFileExit();
return FALSE;
}m_iSoundFramesAhead = 0; Spiegazione successiva dei Frames AHead
variabili globali:Come prima, adesso prendiamo le informazioni dal buffer sonoro:
LPWAVEFORMATEX lpSoundScrFmt;
DWORD m_dwBufferSize, m_dwLoadSize, m_dwSoundFrame;
LPDIRECTSOUNDBUFFER m_lpdsbTon;
variabili locali:
LONG lFmtLenght;
DSBUFFERDESC dsbd;
AVIStreamFormatSize(m_pasSound,0,&lFmtLenght);Preparazione del buffer sonoro:
m_lpSoundScrFmt = (WAVEFORMATEX *)stdalloc(lFmtLenght);
AVIStreamReadFormat(m_pasSound,0,m_lpSoundScrFmt,&lFmtLenght);
AVIStreamInfo(m_pasSound,&m_asiSound,sizeof(AVISTREAMINFO));
m_dwBufferSize = (m_lpSoundScrFmt->nAvgBytesPerSec * dwBufferTime) / 1000;La frequenza del buffer è m_lpSoundScrFmt->nSamplesPerSec
m_dwLoadSize = (m_lpSoundScrFmt->nAvgBytesPerSec + m_dwFps - 1 ) / m_dwFps;
m_dwSoundFrame = m_dwBufferSize / m_dwLoadSize;dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = 0;
dsbd.dwBufferBytes = m_dwBufferSize;dsbd.dwReserved = 0;
dsbd.lpwfxFormat = m_lpSoundScrFmt;
if(m_lpSoundScrFmt->nChannels==1) {lpds è un puntatore a una IDirectSound Interface. Da questo creo il buffer secondario:
m_dwLoadSize&=~0x1; correzzione per allineamento
m_dwSpf = m_dwLoadSize>>1; 16 bit, mono
}
else {
m_dwLoadSize&=~0x3; correzzione per allineamento
m_dwSpf = m_dwLoadSize>>2; 16 bit, stereo
}
if(lpds)Usare i frame ahead per riempire il buffer sonoro
if (lpds->CreateSoundBuffer(&dsbd,&m_lpdsbTon,NULL)!=0) {
CloseSound();
return FALSE;
}m_dwLoadPos = 0;
Frames ahead, i frame prima del video per avere un minimo di prestreaming del suono:
m_iSoundFramesAhead = m_asiSound.dwInitialFrames/m_asiSound.dwScale;Calcolo della dimensione dei frame di solo suono che possono essere caricati in memoria:
m_dwSoundSpace = m_dwSoundFrame >> 1;
m_dwAheadSpace = m_iSoundFramesAhead - 1;
iEligible = min(m_dwAheadSpace, m_dwSoundSpace);Riempimento del sound buffer. ReadNextFrame è spiegata di seguito.
if(iEligible<1) iEligible = 1;
for(UINT i=0;i<iEligible;i++) ReadNextFrame();Inizio del play:
return (m_lpdsbTon) ? (m_lpdsbTon->Play(0,0,DSBPLAY_LOOPING)==DS_OK) : FALSE;
Riempiamo il buffer sonoro
DWORD dwSize1,dwSize2;Finalmente ricevo i dati dallo stream direttamente[senza dover usare l'ACM, visto che il suono occupa il meno in un filmato]
LPVOID Data1,Data2;
HRESULT hr;
hr = m_lpdsbTon->Lock(m_dwLoadPos*m_dwLoadSize, m_dwLoadSize,&Data1,&dwSize1,&Data2,&dwSize2,0);
if (hr!=0) return hr;
AVIStreamRead(m_pasSound, m_lIndex * m_dwSpf, m_dwSpf,Data1,m_dwLoadSize,NULL,NULL);Uso un riempimento ciclico:
hr=m_lpdsbTon->Unlock(Data1,dwSize1,Data2,dwSize2);
m_dwLoadPos++;
if(m_dwLoadPos>=m_dwSoundFrame) m_dwLoadPos = 0;
Shutdown della sezione sonoro
if (m_lpSoundScrFmt) {
stdfree(m_lpSoundScrFmt);
m_lpSoundScrFmt = NULL;
}
if (m_lpdsbTon)
{
m_lpdsbTon->Release();
m_lpdsbTon = NULL;
}
Paolo Medici, che ultimamente aveva del tempo da perdere in retorica. Dalla Serie delle Guide Veloci per fare Software Miliardari | Queste informazioni sono parte mie, parte degli esempi del DirectX e parte di un tutorial che ho trovato su Internet di cui non mi ricordo la provenienza, ma solo l'autore: Gert Wollny. | Discuti questo articolo nel forum |