Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

542 lines
13 KiB

4 years ago
  1. /*************************************************************************
  2. * Copyright (C) Microsoft Corporation 1992. All rights reserved.
  3. *
  4. *************************************************************************/
  5. /*
  6. * aviread.c: read blocks from the avi file (using worker thread).
  7. * Only built in WIN32 case.
  8. */
  9. //#define AVIREAD
  10. #ifdef AVIREAD
  11. #include <windows.h>
  12. #include <mmsystem.h>
  13. #include <ntavi.h> // This must be included, for both versions
  14. #include <string.h> // needed for memmove in nt
  15. #include <mmddk.h>
  16. #include <memory.h>
  17. #include "common.h"
  18. #include "ntaviprt.h"
  19. #include "aviread.h"
  20. #include "aviffmt.h"
  21. #include "graphic.h"
  22. /*
  23. * overview of operation:
  24. *
  25. * creation of a avird object (via avird_startread) creates an avird_header
  26. * data structure and a worker thread. The data structure is protected by
  27. * a critical section, and contains two semaphores. the semEmpty semaphore is
  28. * initialised to the number of buffers allocated (normally 2), and the
  29. * semFull semaphore is initialised to 0 (there are initially no full buffers).
  30. *
  31. * The worker thread then loops waiting on semEmpty to get empty buffers,
  32. * and once it finds them, filling them and signalling via semFull that they
  33. * are ready. It fills them by callbacks to a AVIRD_FUNC function that
  34. * we were given a pointer to on object creation.
  35. *
  36. * Getting a buffer via avird_getnextbuffer waits on semFull until there
  37. * are full buffers, and returns the first on the list, after moving
  38. * it to the 'in use' list. The caller
  39. * will use/play the data in the buffer, and then call avird_emptybuffer:
  40. * - this finds the buffer on the 'in use' list, moves it to the
  41. * 'empty' list and then signals the worker thread via
  42. * semEmpty.
  43. *
  44. * The worker thread checks the object state each time it is woken up. If this
  45. * state is 'closing' (bOK == FALSE), the thread frees all memory and exits.
  46. * avird_endread changes the object state and signals semEmpty to wake up the
  47. * worker thread.
  48. */
  49. /*
  50. * each buffer is represented by one of these headers
  51. */
  52. typedef struct avird_buffer {
  53. /*
  54. * size of the buffer in bytes
  55. */
  56. long lSize;
  57. /* size of read data in bytes */
  58. long lDataSize;
  59. /*
  60. * pointer to the next buffer in this state
  61. */
  62. struct avird_buffer * pNextBuffer;
  63. /* FALSE if buffer read failed */
  64. BOOL bOK;
  65. /* request sequence */
  66. int nSeq;
  67. /*
  68. * pointer to the actual block of buffer data
  69. */
  70. PBYTE pData;
  71. } AVIRD_BUFFER, * PAVIRD_BUFFER;
  72. /* handles to HAVIRD are pointers to this data structure, but the
  73. * contents of the struct are known only within this module
  74. */
  75. typedef struct avird_header {
  76. /*
  77. * always hold the critical section before checking/changing the
  78. * object state or any buffer state
  79. */
  80. CRITICAL_SECTION critsec;
  81. /*
  82. * the count of this semaphore is the count of empty buffers
  83. * waiting to be picked up by the worker thread
  84. */
  85. HANDLE semEmpty;
  86. /*
  87. * the count of this semaphore is the count of full buffers waiting
  88. * to be picked up by the caller.
  89. */
  90. HANDLE semFull;
  91. /* object state - FALSE indicates close-down request. */
  92. BOOL bOK;
  93. /* pointer to list of buffer headers ready to be filled */
  94. PAVIRD_BUFFER pEmpty;
  95. /* pointer to a list of buffer headers in use by the client */
  96. PAVIRD_BUFFER pInUse;
  97. /*
  98. * pointer to an ordered list of buffer headers ready to be
  99. * picked up by the client.
  100. */
  101. PAVIRD_BUFFER pFull;
  102. /*
  103. * function to call to fill a buffer
  104. */
  105. AVIRD_FUNC pFunc;
  106. /* instance arg to pass to pFunc() */
  107. DWORD dwInstanceData;
  108. /* size of next buffer to be read */
  109. long lNextSize;
  110. /* request sequence */
  111. int nNext;
  112. /* total in sequence */
  113. int nBlocks;
  114. } AVIRD_HEADER, * PAVIRD_HEADER;
  115. /* number of buffers to queue up */
  116. #define MAX_Q_BUFS 4
  117. /*
  118. * worker thread function
  119. */
  120. DWORD avird_worker(LPVOID lpvThreadData);
  121. /*
  122. * function to delete whole AVIRD_HEADER data structure.
  123. */
  124. void avird_freeall(PAVIRD_HEADER phdr);
  125. /*
  126. * start an avird operation and return a handle to use in subsequent
  127. * calls. This will cause an asynchronous read (achieved using a separate
  128. * thread) to start reading the next few buffers
  129. */
  130. HAVIRD
  131. avird_startread(AVIRD_FUNC func, DWORD dwInstanceData, long lFirstSize,
  132. int nFirst, int nBlocks)
  133. {
  134. PAVIRD_HEADER phdr;
  135. PAVIRD_BUFFER pbuf;
  136. int i;
  137. HANDLE hThread;
  138. DWORD dwThreadId;
  139. int nBufferSize;
  140. /*
  141. * allocate and init the header
  142. */
  143. phdr = (PAVIRD_HEADER) LocalLock(LocalAlloc(LHND, sizeof(AVIRD_HEADER)));
  144. if (phdr == NULL) {
  145. return(NULL);
  146. }
  147. InitializeCriticalSection(&phdr->critsec);
  148. phdr->semEmpty = CreateSemaphore(NULL, MAX_Q_BUFS, MAX_Q_BUFS, NULL);
  149. phdr->semFull = CreateSemaphore(NULL, 0, MAX_Q_BUFS, NULL);
  150. phdr->bOK = TRUE;
  151. phdr->pInUse = NULL;
  152. phdr->pFull = NULL;
  153. phdr->pEmpty = NULL;
  154. phdr->pFunc = func;
  155. phdr->dwInstanceData = dwInstanceData;
  156. phdr->lNextSize = lFirstSize;
  157. phdr->nNext = nFirst;
  158. phdr->nBlocks = nBlocks;
  159. /*
  160. * round sizes up to 2k to reduce cost of small increases
  161. */
  162. nBufferSize = (lFirstSize + 2047) & ~2047;
  163. /*
  164. * allocate and init the buffers
  165. */
  166. for (i = 0; i < MAX_Q_BUFS; i++) {
  167. pbuf = (PAVIRD_BUFFER) LocalLock(LocalAlloc(LHND, sizeof(AVIRD_BUFFER)));
  168. pbuf->lSize = nBufferSize;
  169. pbuf->pData = (PBYTE) LocalLock(LocalAlloc(LHND, pbuf->lSize));
  170. pbuf->pNextBuffer = phdr->pEmpty;
  171. phdr->pEmpty = pbuf;
  172. }
  173. /*
  174. * create the worker thread
  175. */
  176. hThread = CreateThread(NULL, 0, avird_worker, (LPVOID)phdr, 0, &dwThreadId);
  177. if (hThread) {
  178. /* thread was created ok */
  179. CloseHandle(hThread);
  180. return( phdr);
  181. } else {
  182. avird_freeall(phdr);
  183. return(NULL);
  184. }
  185. }
  186. /*
  187. * return the next buffer from an HAVIRD object.
  188. */
  189. PBYTE
  190. avird_getnextbuffer(HAVIRD havird, long * plSize)
  191. {
  192. PAVIRD_HEADER phdr = havird;
  193. PAVIRD_BUFFER pbuf;
  194. /* wait for a full buffer -report if actual wait needed*/
  195. if (WaitForSingleObject(phdr->semFull, 0) == WAIT_TIMEOUT) {
  196. DPF(("..waiting.."));
  197. WaitForSingleObject(phdr->semFull, INFINITE);
  198. }
  199. /* always hold critsec before messing with queues */
  200. EnterCriticalSection(&phdr->critsec);
  201. /* de-queue first full buffer and place on InUse queue */
  202. pbuf = phdr->pFull;
  203. phdr->pFull = pbuf->pNextBuffer;
  204. pbuf->pNextBuffer = phdr->pInUse;
  205. phdr->pInUse = pbuf;
  206. /* finished with critical section */
  207. LeaveCriticalSection(&phdr->critsec);
  208. if (!pbuf->bOK) {
  209. /* buffer read failed */
  210. DPF(("reporting read failure on %d\n", pbuf->nSeq));
  211. if (plSize) {
  212. *plSize = 0;
  213. }
  214. return(NULL);
  215. }
  216. /* return size of buffer if requested */
  217. if (plSize) {
  218. *plSize = pbuf->lDataSize;
  219. }
  220. return(pbuf->pData);
  221. }
  222. /*
  223. * return to the queue a buffer that has been finished with (is now empty)
  224. *
  225. * causes the worker thread to be woken up and to start filling the buffer
  226. * again.
  227. */
  228. void
  229. avird_emptybuffer(HAVIRD havird, PBYTE pBuffer)
  230. {
  231. PAVIRD_HEADER phdr = havird;
  232. PAVIRD_BUFFER pbuf, pprev;
  233. /* always get the critsec before messing with queues */
  234. EnterCriticalSection(&phdr->critsec);
  235. pprev = NULL;
  236. for (pbuf = phdr->pInUse; pbuf != NULL; pbuf = pbuf->pNextBuffer) {
  237. if (pbuf->pData == pBuffer) {
  238. /* this is the buffer */
  239. break;
  240. }
  241. pprev = pbuf;
  242. }
  243. if (pbuf != NULL) {
  244. /* de-queue from InUse and place on empty q */
  245. if (pprev) {
  246. pprev->pNextBuffer = pbuf->pNextBuffer;
  247. } else {
  248. phdr->pInUse = pbuf->pNextBuffer;
  249. }
  250. pbuf->pNextBuffer = phdr->pEmpty;
  251. phdr->pEmpty = pbuf;
  252. /* mark as not validly read */
  253. pbuf->bOK = FALSE;
  254. /* signal that there is another buffer to fill */
  255. ReleaseSemaphore(phdr->semEmpty, 1, NULL);
  256. } else {
  257. DPF(("buffer 0x%x not found on InUse list\n", pBuffer));
  258. }
  259. LeaveCriticalSection(&phdr->critsec);
  260. }
  261. /*
  262. * delete an avird object. the worker thread will be stopped and all
  263. * data allocated will be freed. The HAVIRD handle is no longer valid after
  264. * this call.
  265. */
  266. void
  267. avird_endread(HAVIRD havird)
  268. {
  269. PAVIRD_HEADER phdr = havird;
  270. DPF(("killing an avird object\n"));
  271. /* get the critsec before messing with states */
  272. EnterCriticalSection(&phdr->critsec);
  273. /* tell the worker thread to do all the work */
  274. phdr->bOK = FALSE;
  275. /* wake up the worker thread */
  276. ReleaseSemaphore(phdr->semEmpty, 1, NULL);
  277. /*
  278. * we must hold the critsec past the semaphore signal: if we
  279. * release the critsec first, the worker thread might see the
  280. * state change before we have signalled the semaphore. He would
  281. * then potentially have destroyed the semaphore AND freed the
  282. * AVIRD_HEADER structure by the time we tried to signal the
  283. * semaphore. This way, we are sure that until we release the
  284. * critsec, everything is still valid
  285. */
  286. LeaveCriticalSection(&phdr->critsec);
  287. /* all done - phdr now may not exist */
  288. }
  289. /*
  290. * worker thread function.
  291. *
  292. * loop waiting for semEmpty to tell us there are empty buffers. When
  293. * we see one, fill it with phdr->pFunc and move it to the
  294. * full queue. Each time we are woken up, check the state. If it
  295. * changes to false, delete the whole thing and exit.
  296. *
  297. * the argument we are passed is the PAVIRD_HEADER.
  298. */
  299. DWORD
  300. avird_worker(LPVOID lpvThreadData)
  301. {
  302. PAVIRD_HEADER phdr = (PAVIRD_HEADER) lpvThreadData;
  303. PAVIRD_BUFFER pbuf, pprev;
  304. long lNextSize;
  305. HANDLE hmem;
  306. DPF(("Worker %d started\n", GetCurrentThreadId()));
  307. for (; ;) {
  308. /* wait for an empty buffer (or state change) */
  309. WaitForSingleObject(phdr->semEmpty, INFINITE);
  310. /* get the critical section before touching the state, queues */
  311. EnterCriticalSection(&phdr->critsec);
  312. if (phdr->bOK == FALSE) {
  313. /* all over bar the shouting */
  314. DPF(("%d exiting\n", GetCurrentThreadId()));
  315. avird_freeall(phdr);
  316. ExitThread(0);
  317. }
  318. /* dequeue the first empty buffer */
  319. pbuf = phdr->pEmpty;
  320. Assert(pbuf != NULL);
  321. phdr->pEmpty = pbuf->pNextBuffer;
  322. lNextSize = phdr->lNextSize;
  323. pbuf->nSeq = phdr->nNext++;
  324. if (pbuf->nSeq < phdr->nBlocks) {
  325. /* we can now release the critsec until we need to re-Q the filled buf*/
  326. LeaveCriticalSection(&phdr->critsec);
  327. /* resize the buffer if not big enough */
  328. if (pbuf->lSize < lNextSize) {
  329. hmem = LocalHandle(pbuf->pData);
  330. LocalUnlock(hmem);
  331. LocalFree(hmem);
  332. pbuf->lSize = ((lNextSize + 2047) & ~2047);
  333. pbuf->pData = LocalLock(LocalAlloc(LHND, pbuf->lSize));
  334. }
  335. /* record the data content of the buffer */
  336. pbuf->lDataSize = lNextSize;
  337. /* call the filler function */
  338. if ((*phdr->pFunc)(pbuf->pData, phdr->dwInstanceData, lNextSize,
  339. &lNextSize)) {
  340. pbuf->bOK = TRUE;
  341. } else {
  342. DPF(("filler reported failure on %d\n", pbuf->nSeq));
  343. }
  344. /* get the critsec before messing with q's or states */
  345. EnterCriticalSection(&phdr->critsec);
  346. /* size for next read */
  347. phdr->lNextSize = lNextSize;
  348. }
  349. /* place buffer at end of Full queue */
  350. if (phdr->pFull == NULL) {
  351. phdr->pFull = pbuf;
  352. } else {
  353. for (pprev = phdr->pFull; pprev->pNextBuffer != NULL; ) {
  354. pprev = pprev->pNextBuffer;
  355. }
  356. pprev->pNextBuffer = pbuf;
  357. }
  358. pbuf->pNextBuffer = NULL;
  359. LeaveCriticalSection(&phdr->critsec);
  360. /* signal calling thread that there's another buffer for him */
  361. ReleaseSemaphore(phdr->semFull, 1, NULL);
  362. }
  363. /* silence compiler */
  364. return (0);
  365. }
  366. /*
  367. * free one buffer and buffer header
  368. */
  369. void
  370. avird_freebuffer(PAVIRD_BUFFER pbuf)
  371. {
  372. HANDLE hmem;
  373. hmem = LocalHandle( (PSTR)pbuf->pData);
  374. LocalUnlock(hmem);
  375. LocalFree(hmem);
  376. hmem = LocalHandle( (PSTR)pbuf);
  377. LocalUnlock(hmem);
  378. LocalFree(hmem);
  379. }
  380. /*
  381. * function to delete whole AVIRD_HEADER data structure.
  382. *
  383. * called on calling thread if start-up fails, or on worker thread if
  384. * asked to shutdown.
  385. */
  386. void
  387. avird_freeall(PAVIRD_HEADER phdr)
  388. {
  389. PAVIRD_BUFFER pbuf, pnext;
  390. HANDLE hmem;
  391. if (phdr->semEmpty) {
  392. CloseHandle(phdr->semEmpty);
  393. }
  394. if (phdr->semEmpty) {
  395. CloseHandle(phdr->semFull);
  396. }
  397. DeleteCriticalSection(&phdr->critsec);
  398. for (pbuf = phdr->pInUse; pbuf != NULL; pbuf = pnext) {
  399. DPF(("In Use buffers at EndRead\n"));
  400. pnext = pbuf->pNextBuffer;
  401. avird_freebuffer(pbuf);
  402. }
  403. for (pbuf = phdr->pEmpty; pbuf != NULL; pbuf = pnext) {
  404. pnext = pbuf->pNextBuffer;
  405. avird_freebuffer(pbuf);
  406. }
  407. for (pbuf = phdr->pFull; pbuf != NULL; pbuf = pnext) {
  408. pnext = pbuf->pNextBuffer;
  409. avird_freebuffer(pbuf);
  410. }
  411. hmem = LocalHandle((PSTR) phdr);
  412. LocalUnlock(hmem);
  413. LocalFree(hmem);
  414. }
  415. #endif //AVIREAD