|
|
/*************************************************************************
* Copyright (C) Microsoft Corporation 1992. All rights reserved. * *************************************************************************/
/*
* aviread.c: read blocks from the avi file (using worker thread). * Only built in WIN32 case. */
//#define AVIREAD
#ifdef AVIREAD
#include <windows.h>
#include <mmsystem.h>
#include <ntavi.h> // This must be included, for both versions
#include <string.h> // needed for memmove in nt
#include <mmddk.h>
#include <memory.h>
#include "common.h"
#include "ntaviprt.h"
#include "aviread.h"
#include "aviffmt.h"
#include "graphic.h"
/*
* overview of operation: * * creation of a avird object (via avird_startread) creates an avird_header * data structure and a worker thread. The data structure is protected by * a critical section, and contains two semaphores. the semEmpty semaphore is * initialised to the number of buffers allocated (normally 2), and the * semFull semaphore is initialised to 0 (there are initially no full buffers). * * The worker thread then loops waiting on semEmpty to get empty buffers, * and once it finds them, filling them and signalling via semFull that they * are ready. It fills them by callbacks to a AVIRD_FUNC function that * we were given a pointer to on object creation. * * Getting a buffer via avird_getnextbuffer waits on semFull until there * are full buffers, and returns the first on the list, after moving * it to the 'in use' list. The caller * will use/play the data in the buffer, and then call avird_emptybuffer: * - this finds the buffer on the 'in use' list, moves it to the * 'empty' list and then signals the worker thread via * semEmpty. * * The worker thread checks the object state each time it is woken up. If this * state is 'closing' (bOK == FALSE), the thread frees all memory and exits. * avird_endread changes the object state and signals semEmpty to wake up the * worker thread. */
/*
* each buffer is represented by one of these headers */ typedef struct avird_buffer {
/*
* size of the buffer in bytes */ long lSize;
/* size of read data in bytes */ long lDataSize;
/*
* pointer to the next buffer in this state */ struct avird_buffer * pNextBuffer;
/* FALSE if buffer read failed */ BOOL bOK;
/* request sequence */ int nSeq;
/*
* pointer to the actual block of buffer data */ PBYTE pData; } AVIRD_BUFFER, * PAVIRD_BUFFER;
/* handles to HAVIRD are pointers to this data structure, but the
* contents of the struct are known only within this module */ typedef struct avird_header { /*
* always hold the critical section before checking/changing the * object state or any buffer state */ CRITICAL_SECTION critsec;
/*
* the count of this semaphore is the count of empty buffers * waiting to be picked up by the worker thread */ HANDLE semEmpty;
/*
* the count of this semaphore is the count of full buffers waiting * to be picked up by the caller. */ HANDLE semFull;
/* object state - FALSE indicates close-down request. */ BOOL bOK;
/* pointer to list of buffer headers ready to be filled */ PAVIRD_BUFFER pEmpty;
/* pointer to a list of buffer headers in use by the client */ PAVIRD_BUFFER pInUse;
/*
* pointer to an ordered list of buffer headers ready to be * picked up by the client. */ PAVIRD_BUFFER pFull;
/*
* function to call to fill a buffer */ AVIRD_FUNC pFunc;
/* instance arg to pass to pFunc() */ DWORD dwInstanceData;
/* size of next buffer to be read */ long lNextSize;
/* request sequence */ int nNext; /* total in sequence */ int nBlocks;
} AVIRD_HEADER, * PAVIRD_HEADER;
/* number of buffers to queue up */ #define MAX_Q_BUFS 4
/*
* worker thread function */ DWORD avird_worker(LPVOID lpvThreadData);
/*
* function to delete whole AVIRD_HEADER data structure. */ void avird_freeall(PAVIRD_HEADER phdr);
/*
* start an avird operation and return a handle to use in subsequent * calls. This will cause an asynchronous read (achieved using a separate * thread) to start reading the next few buffers */ HAVIRD avird_startread(AVIRD_FUNC func, DWORD dwInstanceData, long lFirstSize, int nFirst, int nBlocks) { PAVIRD_HEADER phdr; PAVIRD_BUFFER pbuf; int i; HANDLE hThread; DWORD dwThreadId; int nBufferSize;
/*
* allocate and init the header */ phdr = (PAVIRD_HEADER) LocalLock(LocalAlloc(LHND, sizeof(AVIRD_HEADER)));
if (phdr == NULL) { return(NULL); }
InitializeCriticalSection(&phdr->critsec); phdr->semEmpty = CreateSemaphore(NULL, MAX_Q_BUFS, MAX_Q_BUFS, NULL); phdr->semFull = CreateSemaphore(NULL, 0, MAX_Q_BUFS, NULL); phdr->bOK = TRUE;
phdr->pInUse = NULL; phdr->pFull = NULL; phdr->pEmpty = NULL;
phdr->pFunc = func; phdr->dwInstanceData = dwInstanceData; phdr->lNextSize = lFirstSize; phdr->nNext = nFirst; phdr->nBlocks = nBlocks;
/*
* round sizes up to 2k to reduce cost of small increases */ nBufferSize = (lFirstSize + 2047) & ~2047; /*
* allocate and init the buffers */ for (i = 0; i < MAX_Q_BUFS; i++) { pbuf = (PAVIRD_BUFFER) LocalLock(LocalAlloc(LHND, sizeof(AVIRD_BUFFER)));
pbuf->lSize = nBufferSize; pbuf->pData = (PBYTE) LocalLock(LocalAlloc(LHND, pbuf->lSize));
pbuf->pNextBuffer = phdr->pEmpty; phdr->pEmpty = pbuf; }
/*
* create the worker thread */ hThread = CreateThread(NULL, 0, avird_worker, (LPVOID)phdr, 0, &dwThreadId); if (hThread) { /* thread was created ok */ CloseHandle(hThread); return( phdr); } else { avird_freeall(phdr); return(NULL); } }
/*
* return the next buffer from an HAVIRD object. */ PBYTE avird_getnextbuffer(HAVIRD havird, long * plSize) { PAVIRD_HEADER phdr = havird; PAVIRD_BUFFER pbuf;
/* wait for a full buffer -report if actual wait needed*/ if (WaitForSingleObject(phdr->semFull, 0) == WAIT_TIMEOUT) { DPF(("..waiting..")); WaitForSingleObject(phdr->semFull, INFINITE); }
/* always hold critsec before messing with queues */ EnterCriticalSection(&phdr->critsec);
/* de-queue first full buffer and place on InUse queue */ pbuf = phdr->pFull; phdr->pFull = pbuf->pNextBuffer; pbuf->pNextBuffer = phdr->pInUse; phdr->pInUse = pbuf;
/* finished with critical section */ LeaveCriticalSection(&phdr->critsec);
if (!pbuf->bOK) { /* buffer read failed */ DPF(("reporting read failure on %d\n", pbuf->nSeq)); if (plSize) { *plSize = 0; } return(NULL); }
/* return size of buffer if requested */ if (plSize) { *plSize = pbuf->lDataSize; }
return(pbuf->pData); }
/*
* return to the queue a buffer that has been finished with (is now empty) * * causes the worker thread to be woken up and to start filling the buffer * again. */ void avird_emptybuffer(HAVIRD havird, PBYTE pBuffer) { PAVIRD_HEADER phdr = havird; PAVIRD_BUFFER pbuf, pprev;
/* always get the critsec before messing with queues */ EnterCriticalSection(&phdr->critsec);
pprev = NULL; for (pbuf = phdr->pInUse; pbuf != NULL; pbuf = pbuf->pNextBuffer) {
if (pbuf->pData == pBuffer) {
/* this is the buffer */ break; } pprev = pbuf; }
if (pbuf != NULL) { /* de-queue from InUse and place on empty q */ if (pprev) { pprev->pNextBuffer = pbuf->pNextBuffer; } else { phdr->pInUse = pbuf->pNextBuffer; } pbuf->pNextBuffer = phdr->pEmpty; phdr->pEmpty = pbuf;
/* mark as not validly read */ pbuf->bOK = FALSE;
/* signal that there is another buffer to fill */ ReleaseSemaphore(phdr->semEmpty, 1, NULL); } else { DPF(("buffer 0x%x not found on InUse list\n", pBuffer)); }
LeaveCriticalSection(&phdr->critsec);
}
/*
* delete an avird object. the worker thread will be stopped and all * data allocated will be freed. The HAVIRD handle is no longer valid after * this call. */ void avird_endread(HAVIRD havird) { PAVIRD_HEADER phdr = havird;
DPF(("killing an avird object\n"));
/* get the critsec before messing with states */ EnterCriticalSection(&phdr->critsec);
/* tell the worker thread to do all the work */ phdr->bOK = FALSE;
/* wake up the worker thread */ ReleaseSemaphore(phdr->semEmpty, 1, NULL);
/*
* we must hold the critsec past the semaphore signal: if we * release the critsec first, the worker thread might see the * state change before we have signalled the semaphore. He would * then potentially have destroyed the semaphore AND freed the * AVIRD_HEADER structure by the time we tried to signal the * semaphore. This way, we are sure that until we release the * critsec, everything is still valid */
LeaveCriticalSection(&phdr->critsec);
/* all done - phdr now may not exist */ }
/*
* worker thread function. * * loop waiting for semEmpty to tell us there are empty buffers. When * we see one, fill it with phdr->pFunc and move it to the * full queue. Each time we are woken up, check the state. If it * changes to false, delete the whole thing and exit. * * the argument we are passed is the PAVIRD_HEADER. */
DWORD avird_worker(LPVOID lpvThreadData) { PAVIRD_HEADER phdr = (PAVIRD_HEADER) lpvThreadData; PAVIRD_BUFFER pbuf, pprev; long lNextSize; HANDLE hmem;
DPF(("Worker %d started\n", GetCurrentThreadId()));
for (; ;) {
/* wait for an empty buffer (or state change) */ WaitForSingleObject(phdr->semEmpty, INFINITE);
/* get the critical section before touching the state, queues */ EnterCriticalSection(&phdr->critsec);
if (phdr->bOK == FALSE) { /* all over bar the shouting */ DPF(("%d exiting\n", GetCurrentThreadId())); avird_freeall(phdr); ExitThread(0); }
/* dequeue the first empty buffer */ pbuf = phdr->pEmpty;
Assert(pbuf != NULL);
phdr->pEmpty = pbuf->pNextBuffer;
lNextSize = phdr->lNextSize;
pbuf->nSeq = phdr->nNext++;
if (pbuf->nSeq < phdr->nBlocks) { /* we can now release the critsec until we need to re-Q the filled buf*/ LeaveCriticalSection(&phdr->critsec);
/* resize the buffer if not big enough */ if (pbuf->lSize < lNextSize) {
hmem = LocalHandle(pbuf->pData); LocalUnlock(hmem); LocalFree(hmem);
pbuf->lSize = ((lNextSize + 2047) & ~2047); pbuf->pData = LocalLock(LocalAlloc(LHND, pbuf->lSize)); }
/* record the data content of the buffer */ pbuf->lDataSize = lNextSize; /* call the filler function */ if ((*phdr->pFunc)(pbuf->pData, phdr->dwInstanceData, lNextSize, &lNextSize)) { pbuf->bOK = TRUE; } else { DPF(("filler reported failure on %d\n", pbuf->nSeq)); }
/* get the critsec before messing with q's or states */ EnterCriticalSection(&phdr->critsec);
/* size for next read */ phdr->lNextSize = lNextSize; }
/* place buffer at end of Full queue */ if (phdr->pFull == NULL) { phdr->pFull = pbuf; } else { for (pprev = phdr->pFull; pprev->pNextBuffer != NULL; ) { pprev = pprev->pNextBuffer; } pprev->pNextBuffer = pbuf; } pbuf->pNextBuffer = NULL;
LeaveCriticalSection(&phdr->critsec);
/* signal calling thread that there's another buffer for him */ ReleaseSemaphore(phdr->semFull, 1, NULL); } /* silence compiler */ return (0); }
/*
* free one buffer and buffer header */ void avird_freebuffer(PAVIRD_BUFFER pbuf) { HANDLE hmem;
hmem = LocalHandle( (PSTR)pbuf->pData); LocalUnlock(hmem); LocalFree(hmem);
hmem = LocalHandle( (PSTR)pbuf); LocalUnlock(hmem); LocalFree(hmem); }
/*
* function to delete whole AVIRD_HEADER data structure. * * called on calling thread if start-up fails, or on worker thread if * asked to shutdown. */ void avird_freeall(PAVIRD_HEADER phdr) { PAVIRD_BUFFER pbuf, pnext; HANDLE hmem;
if (phdr->semEmpty) { CloseHandle(phdr->semEmpty); }
if (phdr->semEmpty) { CloseHandle(phdr->semFull); }
DeleteCriticalSection(&phdr->critsec);
for (pbuf = phdr->pInUse; pbuf != NULL; pbuf = pnext) { DPF(("In Use buffers at EndRead\n"));
pnext = pbuf->pNextBuffer; avird_freebuffer(pbuf); }
for (pbuf = phdr->pEmpty; pbuf != NULL; pbuf = pnext) { pnext = pbuf->pNextBuffer; avird_freebuffer(pbuf); }
for (pbuf = phdr->pFull; pbuf != NULL; pbuf = pnext) { pnext = pbuf->pNextBuffer; avird_freebuffer(pbuf); }
hmem = LocalHandle((PSTR) phdr); LocalUnlock(hmem); LocalFree(hmem); }
#endif //AVIREAD
|