BOOL CP_BuildDirectory(CP_COMPOSITEFILE hComposite) { CPs_CompositeContext* pContext = (CPs_CompositeContext*)hComposite; unsigned int iOffset; CP_CHECKOBJECT(pContext); // Initialise structure pContext->m_pFirstSubFile = NULL; // Scan the composite for the file headers (ignore the end directory stuff) iOffset = 0; while (((iOffset + sizeof(CPs_PKFILE_HEADER)) < pContext->m_dwFileSize) && *(DWORD*)(pContext->m_pFileBase + iOffset) != CPC_PKFILE_DIRMAGIC) { CPs_PKFILE_HEADER* pHeader = (CPs_PKFILE_HEADER*)(pContext->m_pFileBase + iOffset); CPs_SubFile* pNewSubFile; if (pHeader->m_dwSig != CPC_PKFILE_MAGIC || (pHeader->m_wBITs & CPC_PKFILE_BITS_ENCRYPTED) || (pHeader->m_wBITs & CPC_PKFILE_BITS_STREAMED) || (pHeader->m_wMethod != CPC_PKFILE_METHOD_STORED && pHeader->m_wMethod != CPC_PKFILE_METHOD_DEFLATED)) { CP_TRACE0("ZIP format not understood"); return FALSE; } // Create subfile object pNewSubFile = (CPs_SubFile*)malloc(sizeof(*pNewSubFile)); pNewSubFile->m_pNext = pContext->m_pFirstSubFile; pContext->m_pFirstSubFile = pNewSubFile; // Init subfile members pNewSubFile->m_pcName = (char*)malloc(pHeader->m_wFilenameLen + 1); memcpy(pNewSubFile->m_pcName, pContext->m_pFileBase + iOffset + sizeof(*pHeader), pHeader->m_wFilenameLen); pNewSubFile->m_pcName[pHeader->m_wFilenameLen] = '\0'; pNewSubFile->m_wMethod = pHeader->m_wMethod; pNewSubFile->m_dwCRC32 = pHeader->m_dwCRC32; pNewSubFile->m_iCompressedSize = pHeader->m_dwCompressedSize; pNewSubFile->m_iUncompressedSize = pHeader->m_dwDecompressedSize; pNewSubFile->m_iFileOffset = iOffset + sizeof(*pHeader) + pHeader->m_wFilenameLen + pHeader->m_wExtraFieldLen; CP_TRACE1("SubFile:\"%s\"", pNewSubFile->m_pcName); // Skip to next file iOffset += sizeof(*pHeader) + pHeader->m_dwCompressedSize + pHeader->m_wFilenameLen + pHeader->m_wExtraFieldLen; } return TRUE; }
void CPP_OMFL_Uninitialise(CPs_OutputModule* pModule) { CPs_OutputContext_File* pContext = (CPs_OutputContext_File*)pModule->m_pModuleCookie; CP_CHECKOBJECT(pContext); CP_TRACE0("Wave out shutting down"); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); // If there is a File handle if (pContext->m_hFile) { // Stop any pending playing fclose(pContext->m_hFile); pContext->m_hFile = NULL; // Clean up CloseHandle(pModule->m_evtBlockFree); } free(pContext); pModule->m_pModuleCookie = NULL; }
void CPP_OMFL_Initialise(CPs_OutputModule* pModule, const CPs_FileInfo* pFileInfo, CP_HEQUALISER hEqualiser) { // This is called when some playing is required. // Do all allocation here so that we do not hold // resources while we are just sitting waiting for // something to happen. // Create a context CPs_OutputContext_File* pContext; CP_ASSERT(pModule->m_pModuleCookie == NULL); pContext = (CPs_OutputContext_File*)malloc(sizeof(CPs_OutputContext_File)); pModule->m_pModuleCookie = pContext; CP_TRACE0("File out initialising"); // Create sync object pModule->m_evtBlockFree = CreateEvent(NULL, FALSE, FALSE, NULL); // Setup thread prioity to lowest SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); pModule->m_pEqualiser = hEqualiser; pContext->m_bPaused = FALSE; pContext->m_hFile = NULL; }
DWORD WINAPI CPI_Player__EngineEP(void* pCookie) { BOOL bTerminateThread = FALSE; HRESULT hr_ComState; CPs_PlayerContext playercontext; playercontext.m_pBaseEngineParams = (CPs_PlayEngine*)pCookie; playercontext.m_bOutputActive = FALSE; playercontext.m_iProportion_TrackLength = 0; playercontext.m_iLastSentTime_Secs = -1; playercontext.m_iLastSentTime_Proportion = -1; playercontext.m_iInternalVolume = 100; CP_CHECKOBJECT(playercontext.m_pBaseEngineParams); CP_TRACE0("Cooler Engine Startup"); hr_ComState = CoInitialize(NULL); // Initialise CoDecs CP_InitialiseCodec_MPEG(&playercontext.m_CoDecs[CP_CODEC_MPEG]); CP_InitialiseCodec_WAV(&playercontext.m_CoDecs[CP_CODEC_WAV]); CP_InitialiseCodec_OGG(&playercontext.m_CoDecs[CP_CODEC_OGG]); CP_InitialiseCodec_WinAmpPlugin(&playercontext.m_CoDecs[CP_CODEC_WINAMPPLUGIN]); // Initialise output module if (options.decoder_output_mode > CP_OUTPUT_last) options.decoder_output_mode = CP_OUTPUT_last; playercontext.m_dwCurrentOutputModule = options.decoder_output_mode; CPI_Player_Output_Initialise_WaveMapper(&playercontext.m_OutputModules[CP_OUTPUT_WAVE]); CPI_Player_Output_Initialise_DirectSound(&playercontext.m_OutputModules[CP_OUTPUT_DIRECTSOUND]); CPI_Player_Output_Initialise_File(&playercontext.m_OutputModules[CP_OUTPUT_FILE]); playercontext.m_pCurrentOutputModule = &playercontext.m_OutputModules[playercontext.m_dwCurrentOutputModule]; // Initialise EQ CPI_Player_Equaliser_Initialise_Basic(&playercontext.m_Equaliser); { CPs_PlayEngine* player = (CPs_PlayEngine*)pCookie; player->m_pContext = &playercontext; } // Initialise USER32.DLL for this thread { MSG msgDummy; PeekMessage(&msgDummy, 0, WM_USER, WM_USER, PM_NOREMOVE); // Signal this thread ready for input SetEvent(playercontext.m_pBaseEngineParams->m_hEvtThreadReady); } do { // Process any pending messages BOOL bForceRefill = FALSE; MSG msg; DWORD dwWaitResult; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Decode engine message switch (msg.message) { case CPTM_QUIT: bTerminateThread = TRUE; break; case CPTM_OPENFILE: { char* pcFilename = (char*)msg.wParam; // If there is another pending openfile then ignore this one // This helps when this thread is non responsive (on an http connect for example) // and the user is hammering the hell out of the play button (as I always // do) - this will cause a number of open/closes to be placed into the // message queue which will tie up this thread for ages!! MSG msg2; if (PeekMessage(&msg2, NULL, CPTM_OPENFILE, CPTM_OPENFILE, PM_NOREMOVE) == FALSE) { CPs_CoDecModule* pNewCoDec; // If there is a CoDec playing then shut it down if (playercontext.m_pCurrentOutputModule->m_pCoDec) { playercontext.m_pCurrentOutputModule->m_pCoDec->CloseFile(playercontext.m_pCurrentOutputModule->m_pCoDec); playercontext.m_pCurrentOutputModule->m_pCoDec = NULL; } CP_TRACE1("Openfile \"%s\"", pcFilename); pNewCoDec = OpenCoDec(&playercontext, pcFilename); // If the open failed then request a new stream from the interface if (pNewCoDec == NULL) { PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsEndOfStream, 0); } // Check the file's format - if the sample rate, nChannels or sample size has changed // then clear the current output and shutdown output device (this will cause a gap // - but only when the format changes) else if (playercontext.m_bOutputActive == TRUE) { CPs_FileInfo FileInfo; pNewCoDec->GetFileInfo(pNewCoDec, &FileInfo); if (FileInfo.m_iFreq_Hz != playercontext.m_iOpenDevice_Freq_Hz || FileInfo.m_bStereo != playercontext.m_bOpenDevice_Stereo || FileInfo.m_b16bit != playercontext.m_bOpenDevice_16bit) { CP_TRACE0("Stream format changes - clearing stream"); EmptyOutputStream(&playercontext); StartPlay(pNewCoDec, &playercontext); bForceRefill = TRUE; } } playercontext.m_pCurrentOutputModule->m_pCoDec = pNewCoDec; } #ifdef _DEBUG else { CP_TRACE1("Openfile of \"%s\" ignored due to other opens in the queue", pcFilename); } #endif // Cleanup free(pcFilename); } break; case CPTM_SEEK: if (playercontext.m_bOutputActive == TRUE) { // Ignore message if there is another on it's way! MSG msg2; if (PeekMessage(&msg2, NULL, CPTM_SEEK, CPTM_SEEK, PM_NOREMOVE) == FALSE) { if (playercontext.m_pCurrentOutputModule->m_pCoDec) playercontext.m_pCurrentOutputModule->m_pCoDec->Seek(playercontext.m_pCurrentOutputModule->m_pCoDec, (int)msg.wParam, (int)msg.lParam); playercontext.m_pCurrentOutputModule->Flush(playercontext.m_pCurrentOutputModule); bForceRefill = TRUE; } } // FALLTHROUGH - to let coolplayer know playing has resumed (bugfix from seeking when paused) */ case CPTM_PLAY: if (playercontext.m_pCurrentOutputModule->m_pCoDec) { // If we don't have an output stage - initialise one now if (playercontext.m_bOutputActive == FALSE) { StartPlay(playercontext.m_pCurrentOutputModule->m_pCoDec, &playercontext); bForceRefill = TRUE; } playercontext.m_pCurrentOutputModule->SetPause(playercontext.m_pCurrentOutputModule, FALSE); PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsPlaying, 0); playercontext.m_iLastSentTime_Secs = -1; playercontext.m_iLastSentTime_Proportion = -1; UpdateProgress(&playercontext); } break; case CPTM_STOP: if (playercontext.m_pCurrentOutputModule->m_pCoDec) { playercontext.m_pCurrentOutputModule->m_pCoDec->CloseFile(playercontext.m_pCurrentOutputModule->m_pCoDec); playercontext.m_pCurrentOutputModule->m_pCoDec = NULL; } if (playercontext.m_bOutputActive == TRUE) { playercontext.m_bOutputActive = FALSE; playercontext.m_pCurrentOutputModule->Uninitialise(playercontext.m_pCurrentOutputModule); } PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsStopped, 0); break; case CPTM_PAUSE: CP_TRACE0("Pause"); if (playercontext.m_bOutputActive == TRUE) playercontext.m_pCurrentOutputModule->SetPause(playercontext.m_pCurrentOutputModule, TRUE); PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsPaused, 0); break; case CPTM_SETPROGRESSTRACKLENGTH: playercontext.m_iProportion_TrackLength = (int)msg.wParam; break; case CPTM_SENDSYNCCOOKIE: PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_SYNCCOOKIE, msg.wParam, 0); break; case CPTM_BLOCKMSGUNTILENDOFSTREAM: EmptyOutputStream(&playercontext); break; case CPTM_ENUMOUTPUTDEVICES: EnumOutputDevices(&playercontext); break; case CPTM_SETEQSETTINGS: { MSG msg2; CPs_EQSettings* pEQ = (CPs_EQSettings*)msg.wParam; // If there is another pending EQ message do no processing for this one (try to reduce noise) if (PeekMessage(&msg2, NULL, CPTM_SETEQSETTINGS, CPTM_OPENFILE, PM_NOREMOVE) == FALSE) { BOOL bEQEnableStateChanged; playercontext.m_Equaliser.ApplySettings(&playercontext.m_Equaliser, pEQ, &bEQEnableStateChanged); // Empty the buffers (this will cause a discontinuity in the music but at least // the EQ setting change will be immediate if (playercontext.m_bOutputActive == TRUE && playercontext.m_pCurrentOutputModule->OnEQChanged) playercontext.m_pCurrentOutputModule->OnEQChanged(playercontext.m_pCurrentOutputModule); } free(pEQ); } break; case CPTM_ONOUTPUTMODULECHANGE: { playercontext.m_dwCurrentOutputModule = options.decoder_output_mode; SetCurrentOutputModule(&playercontext, NULL, &bForceRefill); } break; case CPTM_ASSOCIATEFILEEXTENSIONS: AssociateFileExtensions(&playercontext); break; case CPTM_SETINTERNALVOLUME: playercontext.m_iInternalVolume = (int)msg.wParam; if (playercontext.m_bOutputActive == TRUE && playercontext.m_pCurrentOutputModule->SetInternalVolume) playercontext.m_pCurrentOutputModule->SetInternalVolume(playercontext.m_pCurrentOutputModule, playercontext.m_iInternalVolume); break; } } if (bTerminateThread) break; // Wait for either another message or a buffer expiry (if we have a player) if (playercontext.m_bOutputActive) { dwWaitResult = 0L; if (bForceRefill == FALSE) { if (playercontext.m_pCurrentOutputModule->m_evtBlockFree) dwWaitResult = MsgWaitForMultipleObjects(1, &playercontext.m_pCurrentOutputModule->m_evtBlockFree, FALSE, 1000, QS_POSTMESSAGE); else dwWaitResult = WAIT_OBJECT_0; } // If the buffer event is signaled then request a refill if (bForceRefill == TRUE || dwWaitResult == WAIT_OBJECT_0) { if (playercontext.m_pCurrentOutputModule->m_pCoDec) { playercontext.m_pCurrentOutputModule->RefillBuffers(playercontext.m_pCurrentOutputModule); if (playercontext.m_pCurrentOutputModule->m_pCoDec == NULL) { // Tell UI that we need another file to play PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsEndOfStream, 0); } else UpdateProgress(&playercontext); } // If output has finished everything that it was doing - close the engine else if (playercontext.m_pCurrentOutputModule->IsOutputComplete(playercontext.m_pCurrentOutputModule) == TRUE) { playercontext.m_bOutputActive = FALSE; playercontext.m_pCurrentOutputModule->Uninitialise(playercontext.m_pCurrentOutputModule); PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsStopped, 0); } } } else { WaitMessage(); } } while (bTerminateThread == FALSE); // Clean up output (if it's still active) if (playercontext.m_pCurrentOutputModule->m_pCoDec) { playercontext.m_pCurrentOutputModule->m_pCoDec->CloseFile(playercontext.m_pCurrentOutputModule->m_pCoDec); playercontext.m_pCurrentOutputModule->m_pCoDec = NULL; } if (playercontext.m_bOutputActive == TRUE) playercontext.m_pCurrentOutputModule->Uninitialise(playercontext.m_pCurrentOutputModule); // Clean up modules playercontext.m_Equaliser.Uninitialise(&playercontext.m_Equaliser); CleanupCoDecs(&playercontext); if (hr_ComState == S_OK) CoUninitialize(); CP_TRACE0("Cooler Engine terminating"); return 0; }
BOOL CircleBufferRead(CPs_CircleBuffer* pCBuffer, void* pDestBuffer, const unsigned int _iBytesToRead, unsigned int* pbBytesRead) { unsigned int iBytesToRead = _iBytesToRead; unsigned int iBytesRead = 0; DWORD dwWaitResult; BOOL bComplete = FALSE; CP_CHECKOBJECT(pCBuffer); while (iBytesToRead > 0 && bComplete == FALSE) { dwWaitResult = WaitForSingleObject(pCBuffer->m_evtDataAvailable, CIC_WAITTIMEOUT); if (dwWaitResult == WAIT_TIMEOUT) { CP_TRACE0("Circle buffer - did not fill in time!"); *pbBytesRead = iBytesRead; return FALSE; } EnterCriticalSection(&pCBuffer->m_csCircleBuffer); // Take what we can from the CBuffer if (pCBuffer->m_iReadCursor > pCBuffer->m_iWriteCursor) { unsigned int iChunkSize = pCBuffer->m_iBufferSize - pCBuffer->m_iReadCursor; if (iChunkSize > iBytesToRead) iChunkSize = iBytesToRead; // Perform the read memcpy((BYTE*)pDestBuffer + iBytesRead, pCBuffer->m_pBuffer + pCBuffer->m_iReadCursor, iChunkSize); iBytesRead += iChunkSize; iBytesToRead -= iChunkSize; pCBuffer->m_iReadCursor += iChunkSize; if (pCBuffer->m_iReadCursor >= pCBuffer->m_iBufferSize) pCBuffer->m_iReadCursor -= pCBuffer->m_iBufferSize; } if (iBytesToRead && pCBuffer->m_iReadCursor < pCBuffer->m_iWriteCursor) { unsigned int iChunkSize = pCBuffer->m_iWriteCursor - pCBuffer->m_iReadCursor; if (iChunkSize > iBytesToRead) iChunkSize = iBytesToRead; // Perform the read memcpy((BYTE*)pDestBuffer + iBytesRead, pCBuffer->m_pBuffer + pCBuffer->m_iReadCursor, iChunkSize); iBytesRead += iChunkSize; iBytesToRead -= iChunkSize; pCBuffer->m_iReadCursor += iChunkSize; } // Is there any more data to read if (pCBuffer->m_iReadCursor == pCBuffer->m_iWriteCursor) { if (pCBuffer->m_bComplete) bComplete = TRUE; } else SetEvent(pCBuffer->m_evtDataAvailable); LeaveCriticalSection(&pCBuffer->m_csCircleBuffer); } *pbBytesRead = iBytesRead; return bComplete ? FALSE : TRUE; }
unsigned int _stdcall EP_FillerThread(void* _pContext) { CPs_BufferFillerContext* pContext = (CPs_BufferFillerContext*)_pContext; HINTERNET hInternet; HINTERNET hURLStream; DWORD dwTimeout; BOOL bStreamComplete = FALSE; INTERNET_BUFFERS internetbuffer; BYTE bReadBuffer[CIC_READCHUNKSIZE]; CP_CHECKOBJECT(pContext); PostMessage(pContext->m_hWndNotify, CPNM_SETSTREAMINGSTATE, (WPARAM)TRUE, (LPARAM)0); // Check that we can open this file hInternet = InternetOpen("CoolPlayer", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0L); if (hInternet == NULL) { pContext->m_pCircleBuffer->SetComplete(pContext->m_pCircleBuffer); CP_TRACE0("EP_FillerThread::NoInternetOpen"); return 0; } dwTimeout = 2000; InternetSetOption(hInternet, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); hURLStream = InternetOpenUrl(hInternet, pContext->m_pcFlexiURL, NULL, 0, INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE, 0); if (hURLStream == NULL) { InternetCloseHandle(hInternet); pContext->m_pCircleBuffer->SetComplete(pContext->m_pCircleBuffer); CP_TRACE1("EP_FillerThread::NoOpenURL %s", pContext->m_pcFlexiURL); return 0; } // Setup the internet buffer internetbuffer.dwStructSize = sizeof(internetbuffer); internetbuffer.Next = NULL; internetbuffer.lpcszHeader = NULL; internetbuffer.lpvBuffer = bReadBuffer; internetbuffer.dwBufferLength = CIC_READCHUNKSIZE; // Perform reading while (pContext->m_bTerminate == FALSE && bStreamComplete == FALSE) { BOOL bReadResult; // Is our circle buffer full? if (pContext->m_pCircleBuffer->GetFreeSize(pContext->m_pCircleBuffer) < CIC_READCHUNKSIZE) { Sleep(20); continue; } // Read in another chunk - if we don't get any data that's ok - we would rather poll the // buffers than just hang on the socket (so the stream can be shutdown if needed) internetbuffer.dwBufferLength = CIC_READCHUNKSIZE; bReadResult = InternetReadFileEx(hURLStream, &internetbuffer, IRF_NO_WAIT, 0); if (bReadResult == FALSE) bStreamComplete = TRUE; if (internetbuffer.dwBufferLength) { pContext->m_pCircleBuffer->Write(pContext->m_pCircleBuffer, internetbuffer.lpvBuffer, internetbuffer.dwBufferLength); PostMessage(pContext->m_hWndNotify, CPNM_SETSTREAMINGSTATE, (WPARAM)TRUE, (LPARAM)(pContext->m_pCircleBuffer->GetUsedSize(pContext->m_pCircleBuffer)*100) / CIC_STREAMBUFFERSIZE); } else Sleep(20); } InternetCloseHandle(hURLStream); InternetCloseHandle(hInternet); pContext->m_pCircleBuffer->SetComplete(pContext->m_pCircleBuffer); PostMessage(pContext->m_hWndNotify, CPNM_SETSTREAMINGSTATE, (WPARAM)FALSE, (LPARAM)0); CP_TRACE0("EP_FillerThread normal shutdown"); return 0; }