diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 75fd9ac8cd..ec889d4446 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -67,7 +67,7 @@ Level3 Disabled ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../native;../native/ext/glew;../ext/zlib - USE_FFMPEG;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + USE_FFMPEG;_USE_DSHOW_;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS StreamingSIMDExtensions2 Fast @@ -107,7 +107,7 @@ false StreamingSIMDExtensions2 Fast - USE_FFMPEG;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + USE_FFMPEG;_USE_DSHOW_;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS Speed @@ -228,8 +228,10 @@ + + @@ -404,7 +406,10 @@ + + + diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index d463cbdb49..47d82998b0 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -74,6 +74,10 @@ extern "C" { } #endif // USE_FFMPEG +#ifdef _USE_DSHOW_ +#include "../HW/audioPlayer.h" +#endif // _USE_DSHOW_ + struct InputBuffer { u32 addr; u32 size; @@ -113,11 +117,21 @@ struct Atrac { } ~Atrac() { + CleanStuff(); + } + + void CleanStuff() { #ifdef USE_FFMPEG ReleaseFFMPEGContext(); #endif // USE_FFMPEG + +#ifdef _USE_DSHOW_ + deleteAtrac3Audio(atracID); +#endif // _USE_DSHOW_ + if (data_buf) delete [] data_buf; + data_buf = 0; } void DoState(PointerWrap &p) { @@ -128,6 +142,11 @@ struct Atrac { p.Do(first); p.Do(atracBufSize); p.Do(codeType); + + p.Do(currentSample); + p.Do(endSample); + p.Do(firstSampleoffset); + u32 has_data_buf = data_buf != NULL; p.Do(has_data_buf); if (has_data_buf) { @@ -147,10 +166,6 @@ struct Atrac { p.Do(atracBitrate); p.Do(atracBytesPerFrame); - p.Do(currentSample); - p.Do(endSample); - p.Do(firstSampleoffset); - p.Do(loopinfo); p.Do(loopinfoNum); @@ -254,6 +269,11 @@ void __AtracInit() { avcodec_register_all(); av_register_all(); #endif // USE_FFMPEG + +#ifdef _USE_DSHOW_ + initaudioEngine(); +#endif //_USE_DSHOW_ + } void __AtracDoState(PointerWrap &p) { @@ -268,6 +288,9 @@ void __AtracShutdown() { delete it->second; } atracMap.clear(); +#ifdef _USE_DSHOW_ + shutdownEngine(); +#endif // _USE_DSHOW_ } Atrac *getAtrac(int atracID) { @@ -447,6 +470,13 @@ u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) int addbytes = std::min(bytesToAdd, atrac->first.filesize - atrac->first.fileoffset); Memory::Memcpy(atrac->data_buf + atrac->first.fileoffset, atrac->first.addr + atrac->first.offset, addbytes); } +#ifdef _USE_DSHOW_ + audioEngine *engine = getaudioEngineByID(atracID); + if (engine && bytesToAdd > 0) { + engine->addStreamData(atrac->first.fileoffset - atrac->firstSampleoffset + 96, Memory::GetPointer(atrac->first.addr + atrac->first.offset), + bytesToAdd, atrac->currentSample); + } +#endif atrac->first.size += bytesToAdd; if (atrac->first.size > atrac->first.filesize) atrac->first.size = atrac->first.filesize; @@ -462,6 +492,10 @@ u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF DEBUG_LOG(HLE, "sceAtracDecodeData(%i, %08x, %08x, %08x, %08x)", atracID, outAddr, numSamplesAddr, finishFlagAddr, remainAddr); Atrac *atrac = getAtrac(atracID); +#ifdef _USE_DSHOW_ + audioEngine *engine = getaudioEngineByID(atracID); +#endif // _USE_DSHOW_ + u32 ret = 0; if (atrac != NULL) { // We already passed the end - return an error (many games check for this.) @@ -511,6 +545,24 @@ u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF } else #endif // USE_FFMPEG +#ifdef _USE_DSHOW_ + if (engine) { + static s16 buf[ATRAC_MAX_SAMPLES * 2]; + int gotsize = engine->getNextSamples((u8*)buf, ATRAC_MAX_SAMPLES * sizeof(s16) * atrac->atracChannels); + numSamples = gotsize / sizeof(s16) / atrac->atracChannels; + s16* in = buf; + s16* out = (s16*)Memory::GetPointer(outAddr); + for (int i = 0; i < numSamples; i++) { + s16 sampleL = *in++; + s16 sampleR = sampleL; + if (atrac->atracChannels == 2) + sampleR = *in++; + *out++ = sampleL; + if (atrac->atracOutputChannels == 2) + *out++ = sampleR; + } + } else +#endif // _USE_DSHOW { numSamples = atrac->endSample - atrac->currentSample; if (atrac->currentSample >= atrac->endSample) { @@ -534,6 +586,10 @@ u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF if (atrac->loopNum != 0 && (atrac->currentSample >= atrac->loopEndSample || (numSamples == 0 && atrac->first.size >= atrac->first.filesize))) { atrac->currentSample = atrac->loopStartSample; +#ifdef _USE_DSHOW_ + if (engine) + engine->setPlaySample(atrac->currentSample); +#endif // _USE_DSHOW if (atrac->loopNum > 0) atrac->loopNum --; } else if (atrac->currentSample >= atrac->endSample || @@ -784,6 +840,13 @@ u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFirstBuf, //return -1; } else { atrac->currentSample = sample; +#ifdef _USE_DSHOW_ + audioEngine *engine = getaudioEngineByID(atracID); + if (engine != NULL) + { + engine->setPlaySample(sample); + } +#endif // _USE_DSHOW_ #ifdef USE_FFMPEG if (atrac->codeType == PSP_MODE_AT_3 && atrac->pCodecCtx) { atrac->SeekToSample(sample); @@ -829,6 +892,17 @@ int64_t _AtracSeekbuffer(void *opaque, int64_t offset, int whence) int __AtracSetContext(Atrac *atrac, u32 buffer, u32 bufferSize) { +#ifdef _USE_DSHOW_ + if (atrac->codeType == PSP_MODE_AT_3_PLUS && atrac->atracChannels != 1) { + addAtrac3Audio(atrac->data_buf, atrac->first.size, atrac->atracID); + if (atrac->currentSample != 0) { + audioEngine *engine = getaudioEngineByID(atrac->atracID); + if (engine) + engine->setPlaySample(atrac->currentSample); + } + return 0; + } +#endif // _USE_DSHOW_ #ifdef USE_FFMPEG u8* tempbuf = (u8*)av_malloc(atrac->atracBufSize); @@ -910,22 +984,30 @@ int _AtracSetData(Atrac *atrac, u32 buffer, u32 bufferSize) atrac->first.writableBytes = (u32)std::max((int)bufferSize - (int)atrac->first.size, 0); atrac->first.offset = atrac->first.size; + // some games may reuse an atracID for playing sound + atrac->CleanStuff(); + #ifdef USE_FFMPEG if (atrac->codeType == PSP_MODE_AT_3) { WARN_LOG(HLE, "This is an atrac3 audio"); - // some games may reuse an atracID for playing sound - atrac->ReleaseFFMPEGContext(); - - if (atrac->data_buf) - delete [] atrac->data_buf; - atrac->data_buf = new u8[atrac->first.filesize]; Memory::Memcpy(atrac->data_buf, buffer, std::min(bufferSize, atrac->first.filesize)); return __AtracSetContext(atrac, buffer, bufferSize); - } else if (atrac->codeType == PSP_MODE_AT_3_PLUS) + } else if (atrac->codeType == PSP_MODE_AT_3_PLUS) { WARN_LOG(HLE, "This is an atrac3+ audio"); +#ifdef _USE_DSHOW_ + if (atrac->atracChannels == 1) { + WARN_LOG(HLE, "Unsupported mono atrac3+ audio!"); + } else { + atrac->data_buf = new u8[atrac->first.filesize]; + Memory::Memcpy(atrac->data_buf, buffer, std::min(bufferSize, atrac->first.filesize)); + + return __AtracSetContext(atrac, buffer, bufferSize); + } +#endif // _USE_DSHOW + } #endif // USE_FFMPEG return 0; diff --git a/Core/HW/OMAConvert.cpp b/Core/HW/OMAConvert.cpp new file mode 100644 index 0000000000..eaf625bd96 --- /dev/null +++ b/Core/HW/OMAConvert.cpp @@ -0,0 +1,358 @@ +#include "OMAConvert.h" + +namespace OMAConvert { + +const u32 OMA_EA3_MAGIC = 0x45413301; +const u8 OMA_CODECID_ATRAC3P = 1; +const int OMAHeaderSize = 96; +const int FMT_CHUNK_MAGIC = 0x20746D66; +const int DATA_CHUNK_MAGIC = 0x61746164; +const int SMPL_CHUNK_MAGIC = 0x6C706D73; +const int FACT_CHUNK_MAGIC = 0x74636166; +const int AT3_MAGIC = 0x0270; +const int AT3_PLUS_MAGIC = 0xFFFE; + +template void BigEndianWriteBuf(u8* buf, T x, int &pos) +{ + int k = sizeof(T); + for (int i = k - 1; i >= 0; i--) + { + buf[pos + i] = (u8)(x & 0xFF); + x >>= 8; + } + pos += k; +} + +template inline T getBufValue(T* buf, int offsetbytes) +{ + return *(T*)(((u8*)buf) + offsetbytes); +} + +inline void WriteBuf(u8* dst, int &pos, u8* src, int size) +{ + memcpy(dst + pos, src, size); + pos += size; +} + +bool isHeader(u8* audioStream, int offset) +{ + const u8 header1 = (u8)0x0F; + const u8 header2 = (u8)0xD0; + return (audioStream[offset] == header1) && (audioStream[offset+1] == header2); +} + +// header set to the headerbuf, and return it's size +int getOmaHeader(u8 codecId, u8 headerCode0, u8 headerCode1, u8 headerCode2, u8* headerbuf) +{ + int pos = 0; + BigEndianWriteBuf(headerbuf, (u32)OMA_EA3_MAGIC, pos); + BigEndianWriteBuf(headerbuf, (u16)OMAHeaderSize, pos); + BigEndianWriteBuf(headerbuf, (u16)-1, pos); + + // Unknown 24 bytes... + BigEndianWriteBuf(headerbuf, (u32)0x00000000, pos); + BigEndianWriteBuf(headerbuf, (u32)0x010f5000, pos); + BigEndianWriteBuf(headerbuf, (u32)0x00040000, pos); + BigEndianWriteBuf(headerbuf, (u32)0x0000f5ce, pos); + BigEndianWriteBuf(headerbuf, (u32)0xd2929132, pos); + BigEndianWriteBuf(headerbuf, (u32)0x2480451c, pos); + + BigEndianWriteBuf(headerbuf, (u8)codecId, pos); + BigEndianWriteBuf(headerbuf, (u8)headerCode0, pos); + BigEndianWriteBuf(headerbuf, (u8)headerCode1, pos); + BigEndianWriteBuf(headerbuf, (u8)headerCode2, pos); + + while (pos < OMAHeaderSize) BigEndianWriteBuf(headerbuf, (u8)0, pos); + + return pos; +} + +int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int frameSize) +{ + int endScan = limit - 1; + + // Most common case: the header can be found at each frameSize + int offset = curpos + frameSize - 8; + if (offset < endScan && isHeader(audioStream, offset)) + return offset; + for (int scan = curpos; scan < endScan; scan++) { + if (isHeader(audioStream, scan)) + return scan; + } + + return -1; +} + +void releaseStream(u8** stream) +{ + if (*stream) delete [] (*stream); + *stream = 0; +} + +// atrac3plus radio +// 352kbps 0xff +// 320kbps 0xe8 +// 256kbps 0xb9 +// 192kbps 0x8b +// 160kbps 0x74 +// 128kbps 0x5c +// 96kbps 0x45 +// 64kbps 0x2e +// 48kbps 0x22 +const u8 atrac3plusradio[] = {0xff, 0xe8, 0xb9, 0x8b, 0x74, 0x5c, 0x45, 0x2e, 0x22}; +const int atrac3plusradiosize = sizeof(atrac3plusradio); + +int convertStreamtoOMA(u8* audioStream, int audioSize, u8** outputStream) +{ + if (!isHeader(audioStream, 0)) + { + *outputStream = 0; + return 0; + } + u8 headerCode1 = audioStream[2]; + u8 headerCode2 = audioStream[3]; + + if (headerCode1 == 0x28) + { + bool bsupported = false; + for (int i = 0; i < atrac3plusradiosize; i++) { + if (atrac3plusradio[i] == headerCode2) + { + bsupported = true; + break; + } + } + if (bsupported == false) + { + *outputStream = 0; + return 0; + } + } + else + { + *outputStream = 0; + return 0; + } + + int frameSize = ((headerCode1 & 0x03) << 8) | (headerCode2 & 0xFF) * 8 + 0x10; + int numCompleteFrames = audioSize / (frameSize + 8); + int lastFrameSize = audioSize - (numCompleteFrames * (frameSize + 8)); + + int omaStreamSize = OMAHeaderSize + numCompleteFrames * frameSize + lastFrameSize; + + // Allocate an OMA stream size large enough (better too large than too short) + if (audioSize > omaStreamSize) omaStreamSize = audioSize; + u8* oma = new u8[omaStreamSize]; + int omapos = 0; + int audiopos = 0; + + omapos += getOmaHeader(OMA_CODECID_ATRAC3P, 0, headerCode1, headerCode2, oma); + while (audioSize - audiopos > 8) { + // Skip 8 bytes frame header + audiopos += 8; + int nextHeader = getNextHeaderPosition(audioStream, audiopos, audioSize, frameSize); + u8* frame = audioStream + audiopos; + int framelimit = audioSize - audiopos; + if (nextHeader >= 0) { + framelimit = nextHeader - audiopos; + audiopos = nextHeader; + } else + audiopos = audioSize; + WriteBuf(oma, omapos, frame, framelimit); + } + + *outputStream = oma; + return omapos; +} + +int getChunkOffset(u8* riff, int limit, int chunkMagic, int offset) { + for (int i = offset; i <= limit - 8;) { + if (getBufValue((int*)riff, i) == chunkMagic) + return i; + // Move to next chunk + int chunkSize = getBufValue((int*)riff, i + 4); + i += chunkSize + 8; + } + + return -1; +} + +int convertRIFFtoOMA(u8* riff, int riffSize, u8** outputStream, int* readSize) +{ + const int firstChunkOffset = 12; + int fmtChunkOffset = getChunkOffset(riff, riffSize, FMT_CHUNK_MAGIC, firstChunkOffset); + if (fmtChunkOffset < 0) { + *outputStream = 0; + return 0; + } + u8 codecId = getBufValue(riff, fmtChunkOffset + 0x30); + u8 headerCode0 = getBufValue(riff, fmtChunkOffset + 0x31); + u8 headerCode1 = getBufValue(riff, fmtChunkOffset + 0x32); + u8 headerCode2 = getBufValue(riff, fmtChunkOffset + 0x33); + + bool bsupported = false; + u16 magic = getBufValue((u16*)riff, fmtChunkOffset + 0x08); + if (magic == AT3_MAGIC) + { + u8 key = getBufValue((u8*)riff, fmtChunkOffset + 0x11); + u16 channel = getBufValue((u16*)riff, fmtChunkOffset + 0x0a); + switch (key) + { + case 0x20: + { + // 66kpbs + codecId = 0; + headerCode0 = 0x02; + headerCode1 = 0x20; + headerCode2 = 0x18; + } + break; + case 0x40: + { + // 132kpbs + codecId = 0; + headerCode0 = 0x00; + headerCode1 = 0x20; + headerCode2 = 0x30; + } + break; + default: + { + // 105kpbs + codecId = 0; + headerCode0 = 0x00; + headerCode1 = 0x20; + headerCode2 = 0x26; + } + break; + } + if (channel == 2) + bsupported = true; + else + bsupported = false; + } + else if (magic == AT3_PLUS_MAGIC && headerCode0 == 0x00 + && headerCode1 == 0x28) + { + for (int i = 0; i < atrac3plusradiosize; i++) { + if (atrac3plusradio[i] == headerCode2) + { + bsupported = true; + break; + } + } + } + + if (bsupported == false) + { + *outputStream = 0; + return 0; + } + + int dataChunkOffset = getChunkOffset(riff, riffSize, DATA_CHUNK_MAGIC, firstChunkOffset); + if (dataChunkOffset < 0) { + *outputStream = 0; + return 0; + } + int dataSize = getBufValue((int*)riff, dataChunkOffset + 4); + int datapos = dataChunkOffset + 8; + + u8* oma = new u8[OMAHeaderSize + dataSize]; + int omapos = 0; + + omapos += getOmaHeader(codecId, headerCode0, headerCode1, headerCode2, oma); + WriteBuf(oma, omapos, riff + datapos, dataSize); + + *outputStream = oma; + if (readSize) + *readSize = OMAHeaderSize + riffSize - datapos; + return omapos; +} + +int getOMANumberAudioChannels(u8* oma) +{ + int headerParameters = getBufValue((int*)oma, 0x20); + int channels = (headerParameters >> 18) & 0x7; + + return channels; +} + +int getRIFFSize(u8* riff, int bufsize) +{ + const int firstChunkOffset = 12; + int fmtChunkOffset = getChunkOffset(riff, bufsize, FMT_CHUNK_MAGIC, firstChunkOffset); + int dataChunkOffset = getChunkOffset(riff, bufsize, DATA_CHUNK_MAGIC, firstChunkOffset); + if (fmtChunkOffset < 0 || dataChunkOffset < 0) + return 0; + int dataSize = getBufValue((int*)riff, dataChunkOffset + 4); + return dataSize + dataChunkOffset + 8; +} + +int getRIFFLoopNum(u8* riff, int bufsize, int *startsample, int *endsample) +{ + const int firstChunkOffset = 12; + int dataChunkOffset = getChunkOffset(riff, bufsize, DATA_CHUNK_MAGIC, firstChunkOffset); + if (dataChunkOffset < 0) + return 0; + int smplChunkOffset = getChunkOffset(riff, dataChunkOffset, SMPL_CHUNK_MAGIC, firstChunkOffset); + if (smplChunkOffset < 0) + return 0; + int factChunkOffset = getChunkOffset(riff, dataChunkOffset, FACT_CHUNK_MAGIC, firstChunkOffset); + int atracSampleOffset = 0; + if (factChunkOffset >= 0) { + int factChunkSize = getBufValue((int*)riff, factChunkOffset + 4); + if (factChunkSize >= 8) { + atracSampleOffset = getBufValue((int*)riff, factChunkOffset + 12); + } + } + int smplChunkSize = getBufValue((int*)riff, smplChunkOffset + 4); + int checkNumLoops = getBufValue((int*)riff, smplChunkOffset + 36); + if (smplChunkSize >= 36 + checkNumLoops * 24) + { + // find loop info, simple return -1 now for endless loop + int numLoops = checkNumLoops; + int loopInfoAddr = smplChunkOffset + 44; + int loopcounts = (numLoops > 1) ? -1 : 0; + for (int i = 0; i < 1; i++) { + if (startsample) + *startsample = getBufValue((int*)riff, loopInfoAddr + 8) - atracSampleOffset; + if (endsample) + *endsample = getBufValue((int*)riff, loopInfoAddr + 12) - atracSampleOffset; + int playcount = getBufValue((int*)riff, loopInfoAddr + 20); + if (playcount != 1) + loopcounts = -1; + loopInfoAddr += 24; + } + return loopcounts; + } + return 0; +} + +int getRIFFendSample(u8* riff, int bufsize) +{ + const int firstChunkOffset = 12; + int dataChunkOffset = getChunkOffset(riff, bufsize, DATA_CHUNK_MAGIC, firstChunkOffset); + if (dataChunkOffset < 0) + return -1; + int factChunkOffset = getChunkOffset(riff, dataChunkOffset, FACT_CHUNK_MAGIC, firstChunkOffset); + if (factChunkOffset >= 0) { + int factChunkSize = getBufValue((int*)riff, factChunkOffset + 4); + if (factChunkSize >= 8) { + int endSample = getBufValue((int*)riff, factChunkOffset + 8); + return endSample; + } + } + return -1; +} + +int getRIFFChannels(u8* riff, int bufsize) +{ + const int firstChunkOffset = 12; + int fmtChunkOffset = getChunkOffset(riff, bufsize, FMT_CHUNK_MAGIC, firstChunkOffset); + if (fmtChunkOffset < 0) + return 0; + u16 channel = getBufValue((u16*)riff, fmtChunkOffset + 0x0a); + return channel; +} + +} // namespace OMAConvert \ No newline at end of file diff --git a/Core/HW/OMAConvert.h b/Core/HW/OMAConvert.h new file mode 100644 index 0000000000..c8b4b805f8 --- /dev/null +++ b/Core/HW/OMAConvert.h @@ -0,0 +1,27 @@ +// It can simply convert a at3+ file or stream to oma format +// Thanks to JPCSP project + +#pragma once + +#include "../../Globals.h" + +namespace OMAConvert { + + // output OMA to outputStream, and return it's size. You need to release it by use releaseStream() + int convertStreamtoOMA(u8* audioStream, int audioSize, u8** outputStream); + // output OMA to outputStream, and return it's size. You need to release it by use releaseStream() + int convertRIFFtoOMA(u8* riff, int riffSize, u8** outputStream, int *readSize = 0); + + void releaseStream(u8** stream); + + int getOMANumberAudioChannels(u8* oma); + + int getRIFFSize(u8* riff, int bufsize); + + int getRIFFLoopNum(u8* riff, int bufsize, int *startsample = 0, int *endsample = 0); + + int getRIFFendSample(u8* riff, int bufsize); + + int getRIFFChannels(u8* riff, int bufsize); +} // namespace OMAConvert + diff --git a/Core/HW/audioPlayer.cpp b/Core/HW/audioPlayer.cpp new file mode 100644 index 0000000000..08ea0dc6c5 --- /dev/null +++ b/Core/HW/audioPlayer.cpp @@ -0,0 +1,523 @@ +#ifdef _USE_DSHOW_ + +#include "audioPlayer.h" +#include +#include "qeditsimple.h" +#pragma comment(lib, "Strmiids.lib") +#pragma comment(lib, "Quartz.lib") + +#include "OMAConvert.h" +#include +#include "StdMutex.h" + +#include "../Core/System.h" + +#define JIF(x) if (FAILED(hr=(x))) \ + {return false;} +#define KIF(x) if (FAILED(hr=(x))) \ + {return hr;} +#define LIF(x) if (FAILED(hr=(x))) \ + {} + +// {2AE44C10-B451-4B01-9BBE-A5FBEF68C9D4} +static const GUID CLSID_AsyncStreamSource = +{ 0x2ae44c10, 0xb451, 0x4b01, { 0x9b, 0xbe, 0xa5, 0xfb, 0xef, 0x68, 0xc9, 0xd4 } }; + +// {268424D1-B6E9-4B28-8751-B7774F5ECF77} +static const GUID IID_IStreamSourceFilter = +{ 0x268424d1, 0xb6e9, 0x4b28, { 0x87, 0x51, 0xb7, 0x77, 0x4f, 0x5e, 0xcf, 0x77 } }; + +// We define the interface the app can use to program us +MIDL_INTERFACE("268424D1-B6E9-4B28-8751-B7774F5ECF77") +IStreamSourceFilter : public IFileSourceFilter +{ +public: + virtual STDMETHODIMP LoadStream(void *stream, LONGLONG readSize, LONGLONG streamSize, AM_MEDIA_TYPE *pmt ) = 0; + virtual STDMETHODIMP AddStreamData(LONGLONG offset, void *stream, LONGLONG addSize) = 0; + virtual STDMETHODIMP GetStreamInfo(LONGLONG *readSize, LONGLONG *streamSize) = 0; + virtual STDMETHODIMP SetReadSize(LONGLONG readSize) = 0; + virtual BOOL STDMETHODCALLTYPE IsReadPassEnd() = 0; +}; + +HRESULT GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin) +{ + IEnumPins *pEnum; + IPin *pPin; + pFilter->EnumPins(&pEnum); + while(pEnum->Next(1, &pPin, 0) == S_OK) + { + PIN_DIRECTION PinDirThis; + pPin->QueryDirection(&PinDirThis); + if (PinDir == PinDirThis) + { + pEnum->Release(); + *ppPin = pPin; + return S_OK; + } + pPin->Release(); + } + pEnum->Release(); + return E_FAIL; +} + +HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pFirst, IBaseFilter *pSecond) +{ + IPin *pOut = NULL, *pIn = NULL; + HRESULT hr = GetPin(pFirst, PINDIR_OUTPUT, &pOut); + if (FAILED(hr)) return hr; + hr = GetPin(pSecond, PINDIR_INPUT, &pIn); + if (FAILED(hr)) + { + pOut->Release(); + return E_FAIL; + } + hr = pGraph->Connect(pOut, pIn); + pIn->Release(); + pOut->Release(); + return hr; +} + +class CSampleGrabberCallback : public ISampleGrabberCB +{ +public: + CSampleGrabberCallback(audioPlayer *player) + { + m_player = player; + // 1MB round buffer size + m_bufsize = 0x100000; + m_buf = new u8[m_bufsize]; + isNeedReset = true; + m_readPos = m_writePos = 0; + } + ~CSampleGrabberCallback(){ if (m_buf) delete [] m_buf; } + STDMETHODIMP_(ULONG) AddRef() { return 2; } + STDMETHODIMP_(ULONG) Release() { return 1; } + + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) + { + return S_OK; + } + + STDMETHODIMP SampleCB(double Time, IMediaSample *pSample) + { + return S_OK; + } + + STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long buflen) + { + m_mutex.lock(); + if (isNeedReset) { + isNeedReset = false; + m_readPos = 0; + m_writePos = 0; + } + if (m_writePos + buflen <= m_bufsize) { + memcpy(m_buf + m_writePos, pBuffer, buflen); + } else { + int size = m_bufsize - m_writePos; + memcpy(m_buf + m_writePos, pBuffer, size); + memcpy(m_buf, pBuffer + size, buflen - size); + } + m_writePos = (m_writePos + buflen) % m_bufsize; + + // check how much space left to write + int space = (m_readPos - m_writePos + m_bufsize) % m_bufsize; + m_mutex.unlock(); + if (space < buflen * 3) { + m_player->pause(); + } + return S_OK; + } + + int getNextSamples(u8* buf, int wantedbufsize) { + int timecount = 0; + while (isNeedReset) { + Sleep(1); + timecount++; + if (timecount >= 10) + return 0; + } + m_mutex.lock(); + // check how much space left to read + int space = (m_writePos - m_readPos + m_bufsize) % m_bufsize; + if (m_readPos + wantedbufsize <= m_bufsize) { + memcpy(buf, m_buf + m_readPos, wantedbufsize); + } else { + int size = m_bufsize - m_readPos; + memcpy(buf, m_buf + m_readPos, size); + memcpy(buf + size, m_buf, wantedbufsize - size); + } + int bytesgot = min(wantedbufsize, space); + m_readPos = (m_readPos + bytesgot) % m_bufsize; + + // check how much space left to read + space = (m_writePos - m_readPos + m_bufsize) % m_bufsize; + m_mutex.unlock(); + if (space < wantedbufsize * 3) { + m_player->play(); + } + return bytesgot; + } + + void setResetflag(bool reset) { isNeedReset = reset; } +private: + audioPlayer *m_player; + u8* m_buf; + int m_bufsize; + int m_readPos; + int m_writePos; + bool isNeedReset; + std::recursive_mutex m_mutex; +}; + +bool addSampleGrabber(IGraphBuilder *pGB, IBaseFilter *pSrc, + ISampleGrabberCB *callback, void **outgrabber) +{ + HRESULT hr; + ISampleGrabber *pGrabber = 0; + IBaseFilter *pGrabberF = 0; + JIF(CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, + IID_IBaseFilter, (void **)&pGrabberF)); + JIF(pGB->AddFilter(pGrabberF, L"Sample Grabber")); + JIF(pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber)); + + AM_MEDIA_TYPE mt; + ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE)); + mt.majortype = MEDIATYPE_Audio; + mt.subtype = MEDIASUBTYPE_PCM; + JIF(pGrabber->SetMediaType(&mt)); + + JIF(ConnectFilters(pGB, pSrc, pGrabberF)); + pGrabberF->Release(); + + IBaseFilter *pNull = NULL; + JIF(CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, + IID_IBaseFilter, (void**)&pNull)); + JIF(pGB->AddFilter(pNull, L"NullRenderer")); + JIF(ConnectFilters(pGB, pGrabberF, pNull)); + + // Set one-shot mode and buffering. + JIF(pGrabber->SetOneShot(FALSE)); + JIF(pGrabber->SetBufferSamples(TRUE)); + + JIF(pGrabber->SetCallback(callback, 1)); + + // close the clock to run as fast as possible + IMediaFilter *pMediaFilter = 0; + pGB->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter); + pMediaFilter->SetSyncSource(0); + pMediaFilter->Release(); + + *outgrabber = (void*)pGrabber; + + return true; +} + +static volatile int g_volume = 60; + +audioPlayer::audioPlayer(void) +{ + m_playmode = -1; + m_volume = g_volume; + m_pMC = 0; + m_pGB = 0; + m_pMS = 0; + m_pGrabber = 0; + m_pGrabberCB = 0; + m_pStreamReader = 0; +} + + +audioPlayer::~audioPlayer(void) +{ + closeMedia(); +} + +bool audioPlayer::load(const char* filename, u8* stream, int readSize, int streamSize, bool samplebuffermode, bool isWave) +{ + if (m_playmode == 1) + return false; + WCHAR wstrfilename[MAX_PATH + 1]; + MultiByteToWideChar( CP_ACP, 0, filename ? filename : "stream", -1, + wstrfilename, MAX_PATH ); + wstrfilename[MAX_PATH] = 0; + + HRESULT hr; + JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + IID_IGraphBuilder, (void **)&m_pGB)); + IGraphBuilder *pGB=(IGraphBuilder*)m_pGB; + JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC)); + JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&m_pMS)); + + IBaseFilter *pSrc = 0; + JIF(CoCreateInstance(CLSID_AsyncStreamSource, NULL, CLSCTX_INPROC_SERVER, + IID_IBaseFilter, (void**)&pSrc)); + JIF(pGB->AddFilter(pSrc,wstrfilename)); + JIF(pSrc->QueryInterface(IID_IStreamSourceFilter,(void**)&m_pStreamReader)); + IStreamSourceFilter* pStreamReader = (IStreamSourceFilter*)m_pStreamReader; + AM_MEDIA_TYPE mt; + ZeroMemory(&mt, sizeof(mt)); + mt.majortype = MEDIATYPE_Stream; + mt.subtype = isWave ? MEDIASUBTYPE_WAVE : MEDIASUBTYPE_NULL; + if (filename) { + JIF(pStreamReader->Load(wstrfilename, &mt)); + } else { + JIF(pStreamReader->LoadStream(stream, readSize, streamSize, &mt)); + } + if (samplebuffermode) { + m_pGrabberCB = new CSampleGrabberCallback(this); + addSampleGrabber(pGB, pSrc, (ISampleGrabberCB*)m_pGrabberCB, &m_pGrabber); + pSrc->Release(); + m_playmode = 0; + play(); + } else { + IPin *pOut; + JIF(GetPin(pSrc, PINDIR_OUTPUT, &pOut)); + pSrc->Release(); + JIF(pGB->Render(pOut)); + pOut->Release(); + setVolume(m_volume); + m_playmode = 0; + } + + IMediaSeeking *pMS=(IMediaSeeking*)m_pMS; + m_startpos = 0; + JIF(pMS->GetStopPosition(&m_endpos)); + return true; +} + +bool audioPlayer::play() +{ + if ((!m_pMC) || (m_playmode == -1)) + return false; + IMediaControl *pMC = (IMediaControl*)m_pMC; + HRESULT hr; + JIF(pMC->Run()); + m_playmode = 1; + return true; +} + +bool audioPlayer::pause() +{ + if ((!m_pMC) || (m_playmode == -1)) + return false; + IMediaControl *pMC = (IMediaControl*)m_pMC; + HRESULT hr; + JIF(pMC->Pause()); + m_playmode = 2; + return true; +} + +bool audioPlayer::stop() +{ + if ((!m_pMC) || (m_playmode <= 0)) + return true; + IMediaControl *pMC = (IMediaControl*)m_pMC; + HRESULT hr; + JIF(pMC->Stop()); + m_playmode = 0; + return true; +} + +bool audioPlayer::closeMedia() +{ + if (m_pGrabber) { + ISampleGrabber *pGrabber = (ISampleGrabber *)m_pGrabber; + pGrabber->SetCallback(0, 1); + pGrabber->Release(); + } + if (m_pGrabberCB) + delete ((CSampleGrabberCallback*)m_pGrabberCB); + m_pGrabber = 0; + m_pGrabberCB = 0; + + stop(); + if (m_pMS) + ((IMediaSeeking*)m_pMS)->Release(); + if (m_pMC) + ((IMediaControl*)m_pMC)->Release(); + if (m_pStreamReader) + ((IStreamSourceFilter*)m_pStreamReader)->Release(); + if (m_pGB) + ((IGraphBuilder*)m_pGB)->Release(); + m_pMS = 0; + m_pMC = 0; + m_pStreamReader = 0; + m_pGB = 0; + m_playmode = -1; + return true; +} + +bool audioPlayer::setVolume(int volume) +{ + if ((volume < 0) || (volume > 100)) + return false; + m_volume = volume; + if (!m_pGB) + return true; + IBasicAudio *pBA = NULL; + HRESULT hr; + int now = -(int)(exp(log((double)10001)/100*(100-volume))-1+0.5); + JIF(((IGraphBuilder*)m_pGB)->QueryInterface(IID_IBasicAudio, (void**)&pBA)); + pBA->put_Volume(now); + pBA->Release(); + return true; +} + +bool audioPlayer::isEnd(long *mstimetoend) +{ + if (!m_pMS) + return false; + IMediaSeeking *pMS=(IMediaSeeking*)m_pMS; + LONGLONG curpos; + HRESULT hr; + JIF(pMS->GetCurrentPosition(&curpos)); + if (curpos >= m_endpos) + return true; + if (mstimetoend) + *mstimetoend = (m_endpos - curpos) / 10000; + return false; +} + +bool audioPlayer::setPlayPos(long ms) +{ + if (!m_pGB) + return false; + HRESULT hr; + IMediaSeeking *pMS = (IMediaSeeking*)m_pMS; + LONGLONG pos = ((LONGLONG)ms)*10000; + if (!m_pGrabberCB) { + JIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, + NULL, AM_SEEKING_NoPositioning)); + } else { + pause(); + JIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, + NULL, AM_SEEKING_NoPositioning)); + ((CSampleGrabberCallback*)m_pGrabberCB)->setResetflag(true); + play(); + } + return true; +} + +bool audioPlayer::getPlayPos(long *ms) +{ + if (!m_pGB) + return false; + HRESULT hr; + IMediaSeeking *pMS = (IMediaSeeking*)m_pMS; + LONGLONG curpos; + JIF(pMS->GetCurrentPosition(&curpos)); + if (ms) + *ms = curpos / 10000; + return true; +} + +int audioPlayer::getNextSamples(u8* buf, int wantedbufsize) +{ + if (!m_pGrabberCB || !m_pMC) { + memset(buf, 0, wantedbufsize); + return wantedbufsize; + } + return ((CSampleGrabberCallback*)m_pGrabberCB)->getNextSamples(buf, wantedbufsize); +} + +//////////////////////////////////////////////////////////////////////// +// audioEngine + +bool audioEngine::loadRIFFStream(u8* stream, int streamsize, int atracID) +{ + u8 *oma = 0; + m_ID = atracID; + m_channel = OMAConvert::getRIFFChannels(stream, streamsize); + bool bResult = false; + if (m_channel != 1) { + int readsize = 0; + int omasize = OMAConvert::convertRIFFtoOMA(stream, streamsize, &oma, &readsize); + if (omasize > 0){ + bResult = load(0, oma, readsize, omasize, true); + OMAConvert::releaseStream(&oma); + } + } + return bResult; +} + +bool audioEngine::closeStream() +{ + bool bResult = closeMedia(); + m_ID = -1; + return bResult; +} + +bool audioEngine::setPlaySample(int sample) +{ + return setPlayPos(((s64)sample) * 1000 / 44100); +} + +void audioEngine::addStreamData(int offset, u8* buf, int size, int cursample) +{ + if ((!m_pGB) || (m_channel == 1)) + return; + IStreamSourceFilter* pStreamReader = (IStreamSourceFilter*)m_pStreamReader; + + pStreamReader->AddStreamData(offset, buf, size); + + bool bsetpos = pStreamReader->IsReadPassEnd(); + if (bsetpos) + setPlaySample(cursample); +} + +////////////////////////////////////////////////////////////////////////////// +// + +std::map audioMap; +std::recursive_mutex atracsection; + +void addAtrac3Audio(u8* stream, int streamsize, int atracID) +{ + if (audioMap.find(atracID) != audioMap.end()) + return; + audioEngine *temp = new audioEngine(); + bool bResult = temp->loadRIFFStream(stream, streamsize, atracID); + atracsection.lock(); + audioMap[atracID] = temp; + atracsection.unlock(); + if (!bResult) + temp->closeMedia(); +} + +audioEngine* getaudioEngineByID(int atracID) +{ + if (audioMap.find(atracID) == audioMap.end()) { + return NULL; + } + return audioMap[atracID]; +} + +void deleteAtrac3Audio(int atracID) +{ + atracsection.lock(); + if (audioMap.find(atracID) != audioMap.end()) { + delete audioMap[atracID]; + audioMap.erase(atracID); + } + atracsection.unlock(); +} + +void initaudioEngine() +{ + CoInitialize(0); +} + +void shutdownEngine() +{ + atracsection.lock(); + for (auto it = audioMap.begin(); it != audioMap.end(); ++it) { + delete it->second; + } + audioMap.clear(); + atracsection.unlock(); + CoUninitialize(); + system("cleanAudios.bat"); +} + +#endif // _USE_DSHOW_ \ No newline at end of file diff --git a/Core/HW/audioPlayer.h b/Core/HW/audioPlayer.h new file mode 100644 index 0000000000..9a7b710110 --- /dev/null +++ b/Core/HW/audioPlayer.h @@ -0,0 +1,60 @@ +#pragma once + +#ifdef _USE_DSHOW_ + +#include "../../Globals.h" + +class audioPlayer +{ +public: + audioPlayer(void); + ~audioPlayer(void); + // if samplebuffermode is true, it would provide sample buffers instead play sounds + // if filename is set, then load a file, otherwise load from stream + bool load(const char* filename, u8* stream = 0, int readSize = 0, int streamSize = 0, + bool samplebuffermode = false, bool isWave = false); + bool play(); + bool pause(); + bool stop(); + bool closeMedia(); + bool setVolume(int volume); + bool isEnd(long *mstimetoend = 0); + bool setPlayPos(long ms); + bool getPlayPos(long *ms); + int getNextSamples(u8* buf, int wantedbufsize); +protected: + void *m_pGB; + void *m_pMC; + void *m_pMS; + void *m_pStreamReader; + void *m_pGrabber; + void *m_pGrabberCB; + // 0 for stop, 1 for playing, 2 for pause, -1 for not loaded files + int m_playmode; + int m_volume; +protected: + s64 m_startpos; + s64 m_endpos; +}; + +class audioEngine: public audioPlayer{ +public: + audioEngine(void):audioPlayer(), m_ID(-1){} + ~audioEngine(void){ closeStream();} + bool loadRIFFStream(u8* stream, int streamsize, int atracID); + bool closeStream(); + bool setPlaySample(int sample); + void addStreamData(int offset, u8* buf, int size, int cursample); +private: + int m_ID; + int m_channel; +}; + +void addAtrac3Audio(u8* stream, int streamsize, int atracID); +audioEngine* getaudioEngineByID(int atracID); +void deleteAtrac3Audio(int atracID); + +void initaudioEngine(); +void shutdownEngine(); + +#endif // _USE_DSHOW_ \ No newline at end of file diff --git a/Core/HW/qeditsimple.h b/Core/HW/qeditsimple.h new file mode 100644 index 0000000000..476d5a245a --- /dev/null +++ b/Core/HW/qeditsimple.h @@ -0,0 +1,381 @@ +// it's a simple mirror for directshow qedit.h + +#ifndef __QEDIT_SIMPLE_H__ +#define __QEDIT_SIMPLE_H__ + +#ifndef __ISampleGrabberCB_FWD_DEFINED__ +#define __ISampleGrabberCB_FWD_DEFINED__ +typedef interface ISampleGrabberCB ISampleGrabberCB; +#endif /* __ISampleGrabberCB_FWD_DEFINED__ */ + + +#ifndef __ISampleGrabber_FWD_DEFINED__ +#define __ISampleGrabber_FWD_DEFINED__ +typedef interface ISampleGrabber ISampleGrabber; +#endif /* __ISampleGrabber_FWD_DEFINED__ */ + +EXTERN_C const CLSID CLSID_SampleGrabber; + +#ifdef __cplusplus + +class DECLSPEC_UUID("C1F400A0-3F08-11d3-9F0B-006008039E37") +SampleGrabber; +#endif + +EXTERN_C const CLSID CLSID_NullRenderer; + +#ifdef __cplusplus + +class DECLSPEC_UUID("C1F400A4-3F08-11d3-9F0B-006008039E37") +NullRenderer; +#endif + +#ifndef __ISampleGrabberCB_INTERFACE_DEFINED__ +#define __ISampleGrabberCB_INTERFACE_DEFINED__ + +/* interface ISampleGrabberCB */ +/* [unique][helpstring][local][uuid][object] */ + + +EXTERN_C const IID IID_ISampleGrabberCB; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("0579154A-2B53-4994-B0D0-E773148EFF85") + ISampleGrabberCB : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SampleCB( + double SampleTime, + IMediaSample *pSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE BufferCB( + double SampleTime, + BYTE *pBuffer, + long BufferLen) = 0; + + }; + +#else /* C style interface */ + + typedef struct ISampleGrabberCBVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISampleGrabberCB * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISampleGrabberCB * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISampleGrabberCB * This); + + HRESULT ( STDMETHODCALLTYPE *SampleCB )( + ISampleGrabberCB * This, + double SampleTime, + IMediaSample *pSample); + + HRESULT ( STDMETHODCALLTYPE *BufferCB )( + ISampleGrabberCB * This, + double SampleTime, + BYTE *pBuffer, + long BufferLen); + + END_INTERFACE + } ISampleGrabberCBVtbl; + + interface ISampleGrabberCB + { + CONST_VTBL struct ISampleGrabberCBVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISampleGrabberCB_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define ISampleGrabberCB_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define ISampleGrabberCB_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define ISampleGrabberCB_SampleCB(This,SampleTime,pSample) \ + (This)->lpVtbl -> SampleCB(This,SampleTime,pSample) + +#define ISampleGrabberCB_BufferCB(This,SampleTime,pBuffer,BufferLen) \ + (This)->lpVtbl -> BufferCB(This,SampleTime,pBuffer,BufferLen) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE ISampleGrabberCB_SampleCB_Proxy( + ISampleGrabberCB * This, + double SampleTime, + IMediaSample *pSample); + + +void __RPC_STUB ISampleGrabberCB_SampleCB_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISampleGrabberCB_BufferCB_Proxy( + ISampleGrabberCB * This, + double SampleTime, + BYTE *pBuffer, + long BufferLen); + + +void __RPC_STUB ISampleGrabberCB_BufferCB_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __ISampleGrabberCB_INTERFACE_DEFINED__ */ + + +#ifndef __ISampleGrabber_INTERFACE_DEFINED__ +#define __ISampleGrabber_INTERFACE_DEFINED__ + +/* interface ISampleGrabber */ +/* [unique][helpstring][local][uuid][object] */ + + +EXTERN_C const IID IID_ISampleGrabber; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("6B652FFF-11FE-4fce-92AD-0266B5D7C78F") + ISampleGrabber : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetOneShot( + BOOL OneShot) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetMediaType( + const AM_MEDIA_TYPE *pType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( + AM_MEDIA_TYPE *pType) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetBufferSamples( + BOOL BufferThem) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer( + /* [out][in] */ long *pBufferSize, + /* [out] */ long *pBuffer) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentSample( + /* [retval][out] */ IMediaSample **ppSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetCallback( + ISampleGrabberCB *pCallback, + long WhichMethodToCallback) = 0; + + }; + +#else /* C style interface */ + + typedef struct ISampleGrabberVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISampleGrabber * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISampleGrabber * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISampleGrabber * This); + + HRESULT ( STDMETHODCALLTYPE *SetOneShot )( + ISampleGrabber * This, + BOOL OneShot); + + HRESULT ( STDMETHODCALLTYPE *SetMediaType )( + ISampleGrabber * This, + const AM_MEDIA_TYPE *pType); + + HRESULT ( STDMETHODCALLTYPE *GetConnectedMediaType )( + ISampleGrabber * This, + AM_MEDIA_TYPE *pType); + + HRESULT ( STDMETHODCALLTYPE *SetBufferSamples )( + ISampleGrabber * This, + BOOL BufferThem); + + HRESULT ( STDMETHODCALLTYPE *GetCurrentBuffer )( + ISampleGrabber * This, + /* [out][in] */ long *pBufferSize, + /* [out] */ long *pBuffer); + + HRESULT ( STDMETHODCALLTYPE *GetCurrentSample )( + ISampleGrabber * This, + /* [retval][out] */ IMediaSample **ppSample); + + HRESULT ( STDMETHODCALLTYPE *SetCallback )( + ISampleGrabber * This, + ISampleGrabberCB *pCallback, + long WhichMethodToCallback); + + END_INTERFACE + } ISampleGrabberVtbl; + + interface ISampleGrabber + { + CONST_VTBL struct ISampleGrabberVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISampleGrabber_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define ISampleGrabber_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define ISampleGrabber_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define ISampleGrabber_SetOneShot(This,OneShot) \ + (This)->lpVtbl -> SetOneShot(This,OneShot) + +#define ISampleGrabber_SetMediaType(This,pType) \ + (This)->lpVtbl -> SetMediaType(This,pType) + +#define ISampleGrabber_GetConnectedMediaType(This,pType) \ + (This)->lpVtbl -> GetConnectedMediaType(This,pType) + +#define ISampleGrabber_SetBufferSamples(This,BufferThem) \ + (This)->lpVtbl -> SetBufferSamples(This,BufferThem) + +#define ISampleGrabber_GetCurrentBuffer(This,pBufferSize,pBuffer) \ + (This)->lpVtbl -> GetCurrentBuffer(This,pBufferSize,pBuffer) + +#define ISampleGrabber_GetCurrentSample(This,ppSample) \ + (This)->lpVtbl -> GetCurrentSample(This,ppSample) + +#define ISampleGrabber_SetCallback(This,pCallback,WhichMethodToCallback) \ + (This)->lpVtbl -> SetCallback(This,pCallback,WhichMethodToCallback) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE ISampleGrabber_SetOneShot_Proxy( + ISampleGrabber * This, + BOOL OneShot); + + +void __RPC_STUB ISampleGrabber_SetOneShot_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISampleGrabber_SetMediaType_Proxy( + ISampleGrabber * This, + const AM_MEDIA_TYPE *pType); + + +void __RPC_STUB ISampleGrabber_SetMediaType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISampleGrabber_GetConnectedMediaType_Proxy( + ISampleGrabber * This, + AM_MEDIA_TYPE *pType); + + +void __RPC_STUB ISampleGrabber_GetConnectedMediaType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISampleGrabber_SetBufferSamples_Proxy( + ISampleGrabber * This, + BOOL BufferThem); + + +void __RPC_STUB ISampleGrabber_SetBufferSamples_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISampleGrabber_GetCurrentBuffer_Proxy( + ISampleGrabber * This, + /* [out][in] */ long *pBufferSize, + /* [out] */ long *pBuffer); + + +void __RPC_STUB ISampleGrabber_GetCurrentBuffer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISampleGrabber_GetCurrentSample_Proxy( + ISampleGrabber * This, + /* [retval][out] */ IMediaSample **ppSample); + + +void __RPC_STUB ISampleGrabber_GetCurrentSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISampleGrabber_SetCallback_Proxy( + ISampleGrabber * This, + ISampleGrabberCB *pCallback, + long WhichMethodToCallback); + + +void __RPC_STUB ISampleGrabber_SetCallback_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __ISampleGrabber_INTERFACE_DEFINED__ */ + +#endif // __QEDIT_SIMPLE_H__ \ No newline at end of file diff --git a/filter/asyncStreamflt/asyncflt.cpp b/filter/asyncStreamflt/asyncflt.cpp new file mode 100644 index 0000000000..433052e166 --- /dev/null +++ b/filter/asyncStreamflt/asyncflt.cpp @@ -0,0 +1,149 @@ +// A simple stream source filter based on the sample code +// It requires DirectShow BasicClass for compiling + +#include + +#include "asyncio.h" +#include "asyncrdr.h" + +#pragma warning(disable:4710) // 'function' not inlined (optimization) +#include "asyncflt.h" + +// +// Setup data for filter registration +// +const AMOVIESETUP_MEDIATYPE sudOpPinTypes = +{ &MEDIATYPE_Stream // clsMajorType +, &MEDIASUBTYPE_NULL }; // clsMinorType + +const AMOVIESETUP_PIN sudOpPin = +{ L"Output" // strName +, FALSE // bRendered +, TRUE // bOutput +, FALSE // bZero +, FALSE // bMany +, &CLSID_NULL // clsConnectsToFilter +, L"Input" // strConnectsToPin +, 1 // nTypes +, &sudOpPinTypes }; // lpTypes + +const AMOVIESETUP_FILTER sudAsync = +{ &CLSID_AsyncStreamSource // clsID +, L"Stream/File Source (Async.)" // strName +, MERIT_UNLIKELY // dwMerit +, 1 // nPins +, &sudOpPin }; // lpPin + + +// +// Object creation template +// +CFactoryTemplate g_Templates[1] = { + { L"Stream/File Source (Async.)" + , &CLSID_AsyncStreamSource + , CAsyncFilter::CreateInstance + , NULL + , &sudAsync } +}; + +int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); + + +//////////////////////////////////////////////////////////////////////// +// +// Exported entry points for registration and unregistration +// (in this case they only call through to default implementations). +// +//////////////////////////////////////////////////////////////////////// + +STDAPI DllRegisterServer() +{ + return AMovieDllRegisterServer2(TRUE); +} + +STDAPI DllUnregisterServer() +{ + return AMovieDllRegisterServer2(FALSE); +} + +// +// DllEntryPoint +// +extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); + +BOOL APIENTRY DllMain(HANDLE hModule, + DWORD dwReason, + LPVOID lpReserved) +{ + return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved); +} + + +//* Create a new instance of this class +CUnknown * WINAPI CAsyncFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) +{ + ASSERT(phr); + + // DLLEntry does the right thing with the return code and + // the returned value on failure + + return new CAsyncFilter(pUnk, phr); +} + + +BOOL CAsyncFilter::ReadTheFile(LPCTSTR lpszFileName) +{ + DWORD dwBytesRead; + + // Open the requested file + HANDLE hFile = CreateFile(lpszFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + DbgLog((LOG_TRACE, 2, TEXT("Could not open %s\n"), lpszFileName)); + return FALSE; + } + + // Determine the file size + ULARGE_INTEGER uliSize; + uliSize.LowPart = GetFileSize(hFile, &uliSize.HighPart); + + PBYTE pbMem = new BYTE[uliSize.LowPart]; + if (pbMem == NULL) + { + CloseHandle(hFile); + return FALSE; + } + + // Read the data from the file + if (!ReadFile(hFile, + (LPVOID) pbMem, + uliSize.LowPart, + &dwBytesRead, + NULL) || + (dwBytesRead != uliSize.LowPart)) + { + DbgLog((LOG_TRACE, 1, TEXT("Could not read file\n"))); + + delete [] pbMem; + CloseHandle(hFile); + return FALSE; + } + + // Save a pointer to the data that was read from the file + if (m_pbData) + delete [] m_pbData; + m_pbData = pbMem; + m_llSize = (LONGLONG)uliSize.QuadPart; + + // Close the file + CloseHandle(hFile); + + return TRUE; +} + diff --git a/filter/asyncStreamflt/asyncflt.def b/filter/asyncStreamflt/asyncflt.def new file mode 100644 index 0000000000..b3ada7e395 --- /dev/null +++ b/filter/asyncStreamflt/asyncflt.def @@ -0,0 +1,13 @@ +;=========================================================================== +; Copyright (c) 1992-2002 Microsoft Corporation. All Rights Reserved. +;=========================================================================== + +LIBRARY AsyncStreamflt.ax + +EXPORTS + DllMain PRIVATE + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + diff --git a/filter/asyncStreamflt/asyncflt.h b/filter/asyncStreamflt/asyncflt.h new file mode 100644 index 0000000000..d4deefdd4d --- /dev/null +++ b/filter/asyncStreamflt/asyncflt.h @@ -0,0 +1,305 @@ +// A simple stream source filter based on the sample code + +#include "StreamSourceflt.h" + +// NOTE: This filter does NOT support AVI format + +// +// Define an internal filter that wraps the base CBaseReader stuff +// + +class CMemStream : public CAsyncStream +{ +public: + CMemStream() : + m_llPosition(0) + { + } + + /* Initialization */ + void Init(LPBYTE pbData, LONGLONG llLength, DWORD dwKBPerSec = INFINITE) + { + m_pbData = pbData; + m_llLength = llLength; + m_dwKBPerSec = dwKBPerSec; + m_dwTimeStart = timeGetTime(); + m_llreadSize = llLength; + m_bisReadPassEnd = FALSE; + } + + HRESULT SetPointer(LONGLONG llPos) + { + if (llPos < 0 || llPos > m_llLength) { + return S_FALSE; + } else { + m_llPosition = llPos; + return S_OK; + } + } + + HRESULT Read(PBYTE pbBuffer, + DWORD dwBytesToRead, + BOOL bAlign, + LPDWORD pdwBytesRead) + { + CAutoLock lck(&m_csLock); + DWORD dwReadLength; + + /* Wait until the bytes are here! */ + DWORD dwTime = timeGetTime(); + + if (m_llPosition + dwBytesToRead > m_llLength) { + dwReadLength = (DWORD)(m_llLength - m_llPosition); + } else { + dwReadLength = dwBytesToRead; + } + DWORD dwTimeToArrive = + ((DWORD)m_llPosition + dwReadLength) / m_dwKBPerSec; + + if (dwTime - m_dwTimeStart < dwTimeToArrive) { + Sleep(dwTimeToArrive - dwTime + m_dwTimeStart); + } + + CopyMemory((PVOID)pbBuffer, (PVOID)(m_pbData + m_llPosition), + dwReadLength); + + m_llPosition += dwReadLength; + *pdwBytesRead = dwReadLength; + if (m_llPosition >= m_llreadSize) + m_bisReadPassEnd = TRUE; + else + m_bisReadPassEnd = FALSE; + return S_OK; + } + + LONGLONG Size(LONGLONG *pSizeAvailable) + { + LONGLONG llCurrentAvailable = + static_cast (UInt32x32To64((timeGetTime() - m_dwTimeStart),m_dwKBPerSec)); + + *pSizeAvailable = min(m_llLength, llCurrentAvailable); + return m_llLength; + } + + DWORD Alignment() + { + return 1; + } + + void Lock() + { + m_csLock.Lock(); + } + + void Unlock() + { + m_csLock.Unlock(); + } + +private: + CCritSec m_csLock; + PBYTE m_pbData; + LONGLONG m_llLength; + LONGLONG m_llPosition; + DWORD m_dwKBPerSec; + DWORD m_dwTimeStart; +public: + LONGLONG m_llreadSize; + BOOL m_bisReadPassEnd; +}; + +class CAsyncFilter : public CAsyncReader, public IStreamSourceFilter +{ +public: + CAsyncFilter(LPUNKNOWN pUnk, HRESULT *phr) : + CAsyncReader(NAME("Mem Reader"), pUnk, &m_Stream, phr), + m_pFileName(NULL), + m_pbData(NULL) + { + } + + ~CAsyncFilter() + { + delete [] m_pbData; + delete [] m_pFileName; + } + + static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *); + + DECLARE_IUNKNOWN + + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv) + { + if (riid == IID_IStreamSourceFilter) { + return GetInterface((IStreamSourceFilter *)this, ppv); + } else if (riid == IID_IFileSourceFilter) { + return GetInterface((IFileSourceFilter *)this, ppv); + } else { + return CAsyncReader::NonDelegatingQueryInterface(riid, ppv); + } + } + + /* IFileSourceFilter methods */ + + // Load a (new) file + STDMETHODIMP Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt) + { + CheckPointer(lpwszFileName, E_POINTER); + + // lstrlenW is one of the few Unicode functions that works on win95 + int cch = lstrlenW(lpwszFileName) + 1; + +#ifndef UNICODE + TCHAR *lpszFileName=0; + lpszFileName = new char[cch * 2]; + if (!lpszFileName) { + return E_OUTOFMEMORY; + } + WideCharToMultiByte(GetACP(), 0, lpwszFileName, -1, + lpszFileName, cch, NULL, NULL); +#else + TCHAR lpszFileName[MAX_PATH]={0}; + (void)StringCchCopy(lpszFileName, NUMELMS(lpszFileName), lpwszFileName); +#endif + CAutoLock lck(&m_csFilter); + + /* Check the file type */ + CMediaType cmt; + if (NULL == pmt) { + cmt.SetType(&MEDIATYPE_Stream); + cmt.SetSubtype(&MEDIASUBTYPE_NULL); + } else { + cmt = *pmt; + } + + if (!ReadTheFile(lpszFileName)) { +#ifndef UNICODE + delete [] lpszFileName; +#endif + return E_FAIL; + } + + m_Stream.Init(m_pbData, m_llSize); + + if (m_pFileName) + delete [] m_pFileName; + m_pFileName = new WCHAR[cch]; + + if (m_pFileName!=NULL) + CopyMemory(m_pFileName, lpwszFileName, cch*sizeof(WCHAR)); + + // this is not a simple assignment... pointers and format + // block (if any) are intelligently copied + m_mt = cmt; + + /* Work out file type */ + cmt.bTemporalCompression = TRUE; //??? + cmt.lSampleSize = 1; + + return S_OK; + } + + // Modeled on IPersistFile::Load + // Caller needs to CoTaskMemFree or equivalent. + + STDMETHODIMP GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt) + { + CheckPointer(ppszFileName, E_POINTER); + *ppszFileName = NULL; + + if (m_pFileName!=NULL) { + DWORD n = sizeof(WCHAR)*(1+lstrlenW(m_pFileName)); + + *ppszFileName = (LPOLESTR) CoTaskMemAlloc( n ); + if (*ppszFileName!=NULL) { + CopyMemory(*ppszFileName, m_pFileName, n); + } + } + + if (pmt!=NULL) { + CopyMediaType(pmt, &m_mt); + } + + return NOERROR; + } + + STDMETHODIMP LoadStream(void *stream, LONGLONG readSize, LONGLONG streamSize, AM_MEDIA_TYPE *pmt ) + { + CAutoLock lck(&m_csFilter); + + /* Check the file type */ + CMediaType cmt; + if (NULL == pmt) { + cmt.SetType(&MEDIATYPE_Stream); + cmt.SetSubtype(&MEDIASUBTYPE_NULL); + } else { + cmt = *pmt; + } + + if (m_pFileName) + delete [] m_pFileName; + m_pFileName = NULL; + PBYTE pbMem = new BYTE[streamSize]; + if (pbMem == NULL) + return E_FAIL; + CopyMemory(pbMem, stream, readSize); + if (m_pbData) + delete [] m_pbData; + m_pbData = pbMem; + m_llSize = streamSize; + + m_Stream.Init(m_pbData, m_llSize); + m_Stream.m_llreadSize = readSize; + + // this is not a simple assignment... pointers and format + // block (if any) are intelligently copied + m_mt = cmt; + + /* Work out file type */ + cmt.bTemporalCompression = TRUE; //??? + cmt.lSampleSize = 1; + + return S_OK; + } + + STDMETHODIMP AddStreamData(LONGLONG offset, void *stream, LONGLONG addSize) + { + if (m_pbData == NULL) + return E_FAIL; + if (addSize > m_llSize - offset) + addSize = m_llSize - offset; + CopyMemory(m_pbData + offset, stream, addSize); + if (offset + addSize > m_Stream.m_llreadSize) + m_Stream.m_llreadSize = offset + addSize; + return S_OK; + } + + STDMETHODIMP GetStreamInfo(LONGLONG *readSize, LONGLONG *streamSize) + { + if (readSize) + *readSize = m_Stream.m_llreadSize; + if (streamSize) + *streamSize = m_llSize; + return S_OK; + } + + STDMETHODIMP SetReadSize(LONGLONG readSize) + { + m_Stream.m_llreadSize = readSize; + return S_OK; + } + + BOOL STDMETHODCALLTYPE IsReadPassEnd() + { + return m_Stream.m_bisReadPassEnd; + } + +private: + BOOL CAsyncFilter::ReadTheFile(LPCTSTR lpszFileName); + +private: + LPWSTR m_pFileName; + LONGLONG m_llSize; + PBYTE m_pbData; + CMemStream m_Stream; +}; diff --git a/filter/asyncStreamflt/asyncflt.rc b/filter/asyncStreamflt/asyncflt.rc new file mode 100644 index 0000000000..02ab017ddf --- /dev/null +++ b/filter/asyncStreamflt/asyncflt.rc @@ -0,0 +1,53 @@ +//==========================================================================; +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +// PURPOSE. +// +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// +//--------------------------------------------------------------------------; + +#include + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 9,0,0,0 + PRODUCTVERSION 9,0,0,0 + FILEFLAGSMASK 0x30003fL +#ifdef _DEBUG + FILEFLAGS 0xbL +#else + FILEFLAGS 0xaL +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "ActiveMovie", "Filter dll" + VALUE "Comment", "Async Stream/File Source Filter" + VALUE "CompanyName", "NULL" + VALUE "FileDescription", "Stream/File Source (Async.)" + VALUE "FileVersion", "9.00" + VALUE "InternalName", "AsyncStreamflt.ax" + VALUE "LegalCopyright", "NULL" + VALUE "OLESelfRegister", "AM20" + VALUE "OriginalFilename", "AsyncStreamflt.ax" + VALUE "ProductName", "Async Stream/File Source Filter" + VALUE "ProductVersion", "9.00" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END \ No newline at end of file diff --git a/filter/asyncStreamflt/asyncflt.sln b/filter/asyncStreamflt/asyncflt.sln new file mode 100644 index 0000000000..323946fa5b --- /dev/null +++ b/filter/asyncStreamflt/asyncflt.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "async", "asyncflt.vcxproj", "{0413A576-4B1B-7294-5FA3-3F839282D877}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug Unicode|Win32 = Debug Unicode|Win32 + Debug|Win32 = Debug|Win32 + Release Unicode|Win32 = Release Unicode|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0413A576-4B1B-7294-5FA3-3F839282D877}.Debug Unicode|Win32.ActiveCfg = Debug Unicode|Win32 + {0413A576-4B1B-7294-5FA3-3F839282D877}.Debug|Win32.ActiveCfg = Debug|Win32 + {0413A576-4B1B-7294-5FA3-3F839282D877}.Release Unicode|Win32.ActiveCfg = Release Unicode|Win32 + {0413A576-4B1B-7294-5FA3-3F839282D877}.Release|Win32.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filter/asyncStreamflt/asyncflt.vcxproj b/filter/asyncStreamflt/asyncflt.vcxproj new file mode 100644 index 0000000000..40a56890b7 --- /dev/null +++ b/filter/asyncStreamflt/asyncflt.vcxproj @@ -0,0 +1,305 @@ + + + + + Debug Unicode + Win32 + + + Debug + Win32 + + + Release Unicode + Win32 + + + Release + Win32 + + + + async + + + + + + DynamicLibrary + false + + + DynamicLibrary + false + Unicode + + + DynamicLibrary + false + Unicode + + + DynamicLibrary + false + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\lib + .\Release\ + true + false + .\Debug_Unicode\ + .\Debug_Unicode\ + true + true + ..\lib + .\Release_Unicode\ + true + false + .\Debug\ + .\Debug\ + true + true + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + $(IncludePath) + $(LibraryPath) + $(IncludePath) + $(LibraryPath) + $(IncludePath) + $(LibraryPath) + $(IncludePath) + $(LibraryPath) + AsyncStreamflt.ax + AsyncStreamflt_u.ax + + + + OnlyExplicitInline + ..\include;%(AdditionalIncludeDirectories) + NDEBUG;_WINDOWS;_USRDLL;ASYNC_EXPORTS;INC_OLE2;STRICT;_WIN32_WINNT=0x0403;WIN32;_WIN32;_MT;_DLL;_X86_=1;WINVER=0x0403;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + streams.h + .\Release/asyncflt.pch + .\Release/ + .\Release/ + .\Release/ + Level4 + true + StdCall + Default + + + /MACHINE:I386 /IGNORE:4089 /IGNORE:4098 %(AdditionalOptions) + strmbase.lib;quartz.lib;winmm.lib;msvcrt.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + ..\lib\AsyncStreamflt.ax + true + %(AdditionalLibraryDirectories) + false + .\asyncflt.def + .\Release/asyncflt.pdb + + + true + .\Release/asyncflt.lib + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/asyncflt.tlb + + + NDEBUG;WIN32;%(PreprocessorDefinitions) + 0x0409 + ..\..\..\BaseClasses;%(AdditionalIncludeDirectories) + + + + + Disabled + ..\include;%(AdditionalIncludeDirectories) + INC_OLE2;STRICT;_WIN32_WINNT=0x0403;_WIN32;_MT;_DLL;_X86_=1;WINVER=0x0403;DBG=1;DEBUG;_DEBUG;WIN32;UNICODE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + streams.h + .\Debug_Unicode/asyncflt.pch + .\Debug_Unicode/ + .\Debug_Unicode/ + .\Debug_Unicode/ + Level4 + true + ProgramDatabase + StdCall + Default + + + /MACHINE:I386 /IGNORE:4089 /IGNORE:4098 %(AdditionalOptions) + strmbasd.lib;quartz.lib;winmm.lib;msvcrtd.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + Debug_Unicode/asyncflt.ax + true + %(AdditionalLibraryDirectories) + false + .\asyncflt.def + true + .\Debug_Unicode/asyncflt.lib + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug_Unicode/asyncflt.tlb + + + _DEBUG;WIN32;%(PreprocessorDefinitions) + 0x0409 + ..\..\..\BaseClasses;%(AdditionalIncludeDirectories) + + + + + OnlyExplicitInline + ..\include;%(AdditionalIncludeDirectories) + NDEBUG;_WINDOWS;_MBCS;_USRDLL;ASYNC_EXPORTS;INC_OLE2;STRICT;_WIN32_WINNT=0x0403;_WIN32;_MT;_DLL;_X86_=1;WINVER=0x0403;WIN32;UNICODE;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + streams.h + .\Release_Unicode/asyncflt.pch + .\Release_Unicode/ + .\Release_Unicode/ + .\Release_Unicode/ + Level4 + true + StdCall + Default + + + /MACHINE:I386 /IGNORE:4089 /IGNORE:4098 %(AdditionalOptions) + strmbase.lib;quartz.lib;winmm.lib;msvcrt.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + Release_Unicode/asyncflt.ax + true + %(AdditionalLibraryDirectories) + false + .\asyncflt.def + .\Release_Unicode/asyncflt.pdb + + + true + .\Release_Unicode/asyncflt.lib + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release_Unicode/asyncflt.tlb + + + NDEBUG;WIN32;%(PreprocessorDefinitions) + 0x0409 + ..\..\..\BaseClasses;%(AdditionalIncludeDirectories) + + + + + Disabled + ..\include;%(AdditionalIncludeDirectories) + INC_OLE2;STRICT;_WIN32_WINNT=0x0403;WIN32;_WIN32;_MT;_DLL;_X86_=1;WINVER=0x0403;DBG=1;DEBUG;_DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + streams.h + .\Debug/asyncflt.pch + .\Debug/ + .\Debug/ + .\Debug/ + Level4 + true + ProgramDatabase + StdCall + Default + + + /MACHINE:I386 /IGNORE:4089 /IGNORE:4098 %(AdditionalOptions) + strmbasd.lib;quartz.lib;winmm.lib;msvcrtd.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + Debug/asyncflt.ax + true + %(AdditionalLibraryDirectories) + false + .\asyncflt.def + true + .\Debug/asyncflt.lib + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/asyncflt.tlb + + + _DEBUG;WIN32;%(PreprocessorDefinitions) + 0x0409 + ..\..\..\BaseClasses;%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filter/asyncStreamflt/asyncflt.vcxproj.filters b/filter/asyncStreamflt/asyncflt.vcxproj.filters new file mode 100644 index 0000000000..9cfe53cf50 --- /dev/null +++ b/filter/asyncStreamflt/asyncflt.vcxproj.filters @@ -0,0 +1,52 @@ + + + + + {2cb60d77-18ca-45ad-a4f3-0d736ca63aac} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {1aece6a2-e345-4279-81ae-993564c5b48a} + h;hpp;hxx;hm;inl + + + {56fe04f5-c6f6-43c9-95e2-a95245afadd0} + ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filter/asyncStreamflt/asyncio.cpp b/filter/asyncStreamflt/asyncio.cpp new file mode 100644 index 0000000000..1532796d4a --- /dev/null +++ b/filter/asyncStreamflt/asyncio.cpp @@ -0,0 +1,708 @@ +//------------------------------------------------------------------------------ +// File: AsyncIo.cpp +// +// Desc: DirectShow sample code - base library with I/O functionality. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include "asyncio.h" + +// --- CAsyncRequest --- + + +// implementation of CAsyncRequest representing a single +// outstanding request. All the i/o for this object is done +// in the Complete method. + + +// init the params for this request. +// Read is not issued until the complete call +HRESULT +CAsyncRequest::Request( + CAsyncIo *pIo, + CAsyncStream *pStream, + LONGLONG llPos, + LONG lLength, + BOOL bAligned, + BYTE* pBuffer, + LPVOID pContext, // filter's context + DWORD_PTR dwUser) // downstream filter's context +{ + m_pIo = pIo; + m_pStream = pStream; + m_llPos = llPos; + m_lLength = lLength; + m_bAligned = bAligned; + m_pBuffer = pBuffer; + m_pContext = pContext; + m_dwUser = dwUser; + m_hr = VFW_E_TIMEOUT; // not done yet + + return S_OK; +} + + +// issue the i/o if not overlapped, and block until i/o complete. +// returns error code of file i/o +// +// +HRESULT +CAsyncRequest::Complete() +{ + m_pStream->Lock(); + + m_hr = m_pStream->SetPointer(m_llPos); + if(S_OK == m_hr) + { + DWORD dwActual; + + m_hr = m_pStream->Read(m_pBuffer, m_lLength, m_bAligned, &dwActual); + if(m_hr == OLE_S_FIRST) + { + if(m_pContext) + { + IMediaSample *pSample = reinterpret_cast(m_pContext); + pSample->SetDiscontinuity(TRUE); + m_hr = S_OK; + } + } + + if(FAILED(m_hr)) + { + } + else if(dwActual != (DWORD)m_lLength) + { + // tell caller size changed - probably because of EOF + m_lLength = (LONG) dwActual; + m_hr = S_FALSE; + } + else + { + m_hr = S_OK; + } + } + + m_pStream->Unlock(); + return m_hr; +} + + +// --- CAsyncIo --- + +// note - all events created manual reset + +CAsyncIo::CAsyncIo(CAsyncStream *pStream) + : m_hThread(NULL), + m_evWork(TRUE), + m_evDone(TRUE), + m_evStop(TRUE), + m_listWork(NAME("Work list")), + m_listDone(NAME("Done list")), + m_bFlushing(FALSE), + m_cItemsOut(0), + m_bWaiting(FALSE), + m_pStream(pStream) +{ + +} + + +CAsyncIo::~CAsyncIo() +{ + // move everything to the done list + BeginFlush(); + + // shutdown worker thread + CloseThread(); + + // empty the done list + POSITION pos = m_listDone.GetHeadPosition(); + while(pos) + { + CAsyncRequest* pRequest = m_listDone.GetNext(pos); + delete pRequest; + } + + m_listDone.RemoveAll(); +} + + +// ready for async activity - call this before calling Request. +// +// start the worker thread if we need to +// +// !!! use overlapped i/o if possible +HRESULT +CAsyncIo::AsyncActive(void) +{ + return StartThread(); +} + +// call this when no more async activity will happen before +// the next AsyncActive call +// +// stop the worker thread if active +HRESULT +CAsyncIo::AsyncInactive(void) +{ + return CloseThread(); +} + + +// add a request to the queue. +HRESULT +CAsyncIo::Request( + LONGLONG llPos, + LONG lLength, + BOOL bAligned, + BYTE * pBuffer, + LPVOID pContext, + DWORD_PTR dwUser) +{ + if(bAligned) + { + if(!IsAligned(llPos) || + !IsAligned(lLength) || + !IsAligned((LONG_PTR) pBuffer)) + { + return VFW_E_BADALIGN; + } + } + + CAsyncRequest* pRequest = new CAsyncRequest; + if (!pRequest) + return E_OUTOFMEMORY; + + HRESULT hr = pRequest->Request(this, + m_pStream, + llPos, + lLength, + bAligned, + pBuffer, + pContext, + dwUser); + if(SUCCEEDED(hr)) + { + // might fail if flushing + hr = PutWorkItem(pRequest); + } + + if(FAILED(hr)) + { + delete pRequest; + } + + return hr; +} + + +// wait for the next request to complete +HRESULT +CAsyncIo::WaitForNext( + DWORD dwTimeout, + LPVOID * ppContext, + DWORD_PTR * pdwUser, + LONG * pcbActual) +{ + CheckPointer(ppContext,E_POINTER); + CheckPointer(pdwUser,E_POINTER); + CheckPointer(pcbActual,E_POINTER); + + // some errors find a sample, others don't. Ensure that + // *ppContext is NULL if no sample found + *ppContext = NULL; + + // wait until the event is set, but since we are not + // holding the critsec when waiting, we may need to re-wait + for(;;) + { + if(!m_evDone.Wait(dwTimeout)) + { + // timeout occurred + return VFW_E_TIMEOUT; + } + + // get next event from list + CAsyncRequest* pRequest = GetDoneItem(); + if(pRequest) + { + // found a completed request + + // check if ok + HRESULT hr = pRequest->GetHResult(); + if(hr == S_FALSE) + { + // this means the actual length was less than + // requested - may be ok if he aligned the end of file + if((pRequest->GetActualLength() + + pRequest->GetStart()) == Size()) + { + hr = S_OK; + } + else + { + // it was an actual read error + hr = E_FAIL; + } + } + + // return actual bytes read + *pcbActual = pRequest->GetActualLength(); + + // return his context + *ppContext = pRequest->GetContext(); + *pdwUser = pRequest->GetUser(); + + delete pRequest; + return hr; + } + else + { + // Hold the critical section while checking the list state + CAutoLock lck(&m_csLists); + if(m_bFlushing && !m_bWaiting) + { + // can't block as we are between BeginFlush and EndFlush + + // but note that if m_bWaiting is set, then there are some + // items not yet complete that we should block for. + + return VFW_E_WRONG_STATE; + } + } + + // done item was grabbed between completion and + // us locking m_csLists. + } +} + + +// perform a synchronous read request on this thread. +// Need to hold m_csFile while doing this (done in request object) +HRESULT +CAsyncIo::SyncReadAligned( + LONGLONG llPos, + LONG lLength, + BYTE * pBuffer, + LONG * pcbActual, + PVOID pvContext) +{ + CheckPointer(pcbActual,E_POINTER); + + if(!IsAligned(llPos) || + !IsAligned(lLength) || + !IsAligned((LONG_PTR) pBuffer)) + { + return VFW_E_BADALIGN; + } + + CAsyncRequest request; + + HRESULT hr = request.Request(this, + m_pStream, + llPos, + lLength, + TRUE, + pBuffer, + pvContext, + 0); + if(FAILED(hr)) + return hr; + + hr = request.Complete(); + + // return actual data length + *pcbActual = request.GetActualLength(); + return hr; +} + + +HRESULT +CAsyncIo::Length(LONGLONG *pllTotal, LONGLONG *pllAvailable) +{ + CheckPointer(pllTotal,E_POINTER); + + *pllTotal = m_pStream->Size(pllAvailable); + return S_OK; +} + + +// cancel all items on the worklist onto the done list +// and refuse further requests or further WaitForNext calls +// until the end flush +// +// WaitForNext must return with NULL only if there are no successful requests. +// So Flush does the following: +// 1. set m_bFlushing ensures no more requests succeed +// 2. move all items from work list to the done list. +// 3. If there are any outstanding requests, then we need to release the +// critsec to allow them to complete. The m_bWaiting as well as ensuring +// that we are signalled when they are all done is also used to indicate +// to WaitForNext that it should continue to block. +// 4. Once all outstanding requests are complete, we force m_evDone set and +// m_bFlushing set and m_bWaiting false. This ensures that WaitForNext will +// not block when the done list is empty. +HRESULT +CAsyncIo::BeginFlush() +{ + // hold the lock while emptying the work list + { + CAutoLock lock(&m_csLists); + + // prevent further requests being queued. + // Also WaitForNext will refuse to block if this is set + // unless m_bWaiting is also set which it will be when we release + // the critsec if there are any outstanding). + m_bFlushing = TRUE; + + CAsyncRequest * preq; + while((preq = GetWorkItem()) != 0) + { + preq->Cancel(); + PutDoneItem(preq); + } + + // now wait for any outstanding requests to complete + if(m_cItemsOut > 0) + { + // can be only one person waiting + ASSERT(!m_bWaiting); + + // this tells the completion routine that we need to be + // signalled via m_evAllDone when all outstanding items are + // done. It also tells WaitForNext to continue blocking. + m_bWaiting = TRUE; + } + else + { + // all done + + // force m_evDone set so that even if list is empty, + // WaitForNext will not block + // don't do this until we are sure that all + // requests are on the done list. + m_evDone.Set(); + return S_OK; + } + } + + ASSERT(m_bWaiting); + + // wait without holding critsec + for(;;) + { + m_evAllDone.Wait(); + { + // hold critsec to check + CAutoLock lock(&m_csLists); + + if(m_cItemsOut == 0) + { + // now we are sure that all outstanding requests are on + // the done list and no more will be accepted + m_bWaiting = FALSE; + + // force m_evDone set so that even if list is empty, + // WaitForNext will not block + // don't do this until we are sure that all + // requests are on the done list. + m_evDone.Set(); + + return S_OK; + } + } + } +} + + +// end a flushing state +HRESULT +CAsyncIo::EndFlush() +{ + CAutoLock lock(&m_csLists); + + m_bFlushing = FALSE; + + ASSERT(!m_bWaiting); + + // m_evDone might have been set by BeginFlush - ensure it is + // set IFF m_listDone is non-empty + if(m_listDone.GetCount() > 0) + { + m_evDone.Set(); + } + else + { + m_evDone.Reset(); + } + + return S_OK; +} + + +// start the thread +HRESULT +CAsyncIo::StartThread(void) +{ + if(m_hThread) + { + return S_OK; + } + + // clear the stop event before starting + m_evStop.Reset(); + + DWORD dwThreadID; + m_hThread = CreateThread(NULL, + 0, + InitialThreadProc, + this, + 0, + &dwThreadID); + if(!m_hThread) + { + DWORD dwErr = GetLastError(); + return HRESULT_FROM_WIN32(dwErr); + } + + return S_OK; +} + + +// stop the thread and close the handle +HRESULT +CAsyncIo::CloseThread(void) +{ + // signal the thread-exit object + m_evStop.Set(); + + if(m_hThread) + { + WaitForSingleObject(m_hThread, INFINITE); + CloseHandle(m_hThread); + m_hThread = NULL; + } + + return S_OK; +} + + +// manage the list of requests. hold m_csLists and ensure +// that the (manual reset) event hevList is set when things on +// the list but reset when the list is empty. +// returns null if list empty +CAsyncRequest* +CAsyncIo::GetWorkItem() +{ + CAutoLock lck(&m_csLists); + CAsyncRequest * preq = m_listWork.RemoveHead(); + + // force event set correctly + if(m_listWork.GetCount() == 0) + { + m_evWork.Reset(); + } + + return preq; +} + + +// get an item from the done list +CAsyncRequest* +CAsyncIo::GetDoneItem() +{ + CAutoLock lock(&m_csLists); + CAsyncRequest * preq = m_listDone.RemoveHead(); + + // force event set correctly if list now empty + // or we're in the final stages of flushing + // Note that during flushing the way it's supposed to work is that + // everything is shoved on the Done list then the application is + // supposed to pull until it gets nothing more + // + // Thus we should not set m_evDone unconditionally until everything + // has moved to the done list which means we must wait until + // cItemsOut is 0 (which is guaranteed by m_bWaiting being TRUE). + + if(m_listDone.GetCount() == 0 && + (!m_bFlushing || m_bWaiting)) + { + m_evDone.Reset(); + } + + return preq; +} + + +// put an item on the work list - fail if bFlushing +HRESULT +CAsyncIo::PutWorkItem(CAsyncRequest* pRequest) +{ + CAutoLock lock(&m_csLists); + HRESULT hr; + + if(m_bFlushing) + { + hr = VFW_E_WRONG_STATE; + } + else if(m_listWork.AddTail(pRequest)) + { + // event should now be in a set state - force this + m_evWork.Set(); + + // start the thread now if not already started + hr = StartThread(); + + } + else + { + hr = E_OUTOFMEMORY; + } + + return(hr); +} + + +// put an item on the done list - ok to do this when +// flushing +HRESULT +CAsyncIo::PutDoneItem(CAsyncRequest* pRequest) +{ + ASSERT(CritCheckIn(&m_csLists)); + + if(m_listDone.AddTail(pRequest)) + { + // event should now be in a set state - force this + m_evDone.Set(); + return S_OK; + } + else + { + return E_OUTOFMEMORY; + } +} + + +// called on thread to process any active requests +void +CAsyncIo::ProcessRequests(void) +{ + // lock to get the item and increment the outstanding count + CAsyncRequest * preq = NULL; + + for(;;) + { + { + CAutoLock lock(&m_csLists); + + preq = GetWorkItem(); + if(preq == NULL) + { + // done + return; + } + + // one more item not on the done or work list + m_cItemsOut++; + + // release critsec + } + + preq->Complete(); + + // regain critsec to replace on done list + { + CAutoLock l(&m_csLists); + + PutDoneItem(preq); + + if(--m_cItemsOut == 0) + { + if(m_bWaiting) + m_evAllDone.Set(); + } + } + } +} + + +// the thread proc - assumes that DWORD thread param is the +// this pointer +DWORD +CAsyncIo::ThreadProc(void) +{ + HANDLE ahev[] = {m_evStop, m_evWork}; + + for(;;) + { + DWORD dw = WaitForMultipleObjects(2, + ahev, + FALSE, + INFINITE); + if(dw == WAIT_OBJECT_0+1) + { + // requests need processing + ProcessRequests(); + } + else + { + // any error or stop event - we should exit + return 0; + } + } +} + + +// perform a synchronous read request on this thread. +// may not be aligned - so we will have to buffer. +HRESULT +CAsyncIo::SyncRead( + LONGLONG llPos, + LONG lLength, + BYTE * pBuffer) +{ + if(IsAligned(llPos) && + IsAligned(lLength) && + IsAligned((LONG_PTR) pBuffer)) + { + LONG cbUnused; + return SyncReadAligned(llPos, lLength, pBuffer, &cbUnused, NULL); + } + + // not aligned with requirements - use buffered file handle. + //!!! might want to fix this to buffer the data ourselves? + + CAsyncRequest request; + + HRESULT hr = request.Request(this, + m_pStream, + llPos, + lLength, + FALSE, + pBuffer, + NULL, + 0); + + if(FAILED(hr)) + { + return hr; + } + + return request.Complete(); +} + + +// Return the alignment +HRESULT +CAsyncIo::Alignment(LONG *pAlignment) +{ + CheckPointer(pAlignment,E_POINTER); + + *pAlignment = Alignment(); + return S_OK; +} + + diff --git a/filter/asyncStreamflt/asyncio.h b/filter/asyncStreamflt/asyncio.h new file mode 100644 index 0000000000..a7b69d0682 --- /dev/null +++ b/filter/asyncStreamflt/asyncio.h @@ -0,0 +1,273 @@ +//------------------------------------------------------------------------------ +// File: AsyncIo.h +// +// Desc: DirectShow sample code - base library for I/O functionality. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __ASYNCIO_H__ +#define __ASYNCIO_H__ + +// +// definition of CAsyncFile object that performs file access. It provides +// asynchronous, unbuffered, aligned reads from a file, using a worker thread +// on win95 and potentially overlapped i/o if available. + +// !!! Need to use real overlapped i/o if available +// currently only uses worker thread, not overlapped i/o + + +class CAsyncIo; +class CAsyncStream; + +// +// Model the stream we read from based on a file-like interface +// +class CAsyncStream +{ +public: + virtual ~CAsyncStream() {}; + virtual HRESULT SetPointer(LONGLONG llPos) = 0; + virtual HRESULT Read(PBYTE pbBuffer, + DWORD dwBytesToRead, + BOOL bAlign, + LPDWORD pdwBytesRead) = 0; + + virtual LONGLONG Size(LONGLONG *pSizeAvailable = NULL) = 0; + virtual DWORD Alignment() = 0; + virtual void Lock() = 0; + virtual void Unlock() = 0; + //virtual void SetStopHandle(HANDLE hevStop) {} +}; + +// represents a single request and performs the i/o. Can be called on either +// worker thread or app thread, but must hold pcsFile across file accesses. +// (ie across SetFilePointer/ReadFile pairs) +class CAsyncRequest +{ + CAsyncIo *m_pIo; + CAsyncStream *m_pStream; + LONGLONG m_llPos; + BOOL m_bAligned; + LONG m_lLength; + BYTE* m_pBuffer; + LPVOID m_pContext; + DWORD_PTR m_dwUser; + HRESULT m_hr; + +public: + // init the params for this request. Issue the i/o + // if overlapped i/o is possible. + HRESULT Request( + CAsyncIo *pIo, + CAsyncStream *pStream, + LONGLONG llPos, + LONG lLength, + BOOL bAligned, + BYTE* pBuffer, + LPVOID pContext, // filter's context + DWORD_PTR dwUser); // downstream filter's context + + // issue the i/o if not overlapped, and block until i/o complete. + // returns error code of file i/o + HRESULT Complete(); + + // cancels the i/o. blocks until i/o is no longer pending + HRESULT Cancel() + { + return S_OK; + }; + + // accessor functions + LPVOID GetContext() + { + return m_pContext; + }; + + DWORD_PTR GetUser() + { + return m_dwUser; + }; + + HRESULT GetHResult() { + return m_hr; + }; + + // we set m_lLength to the actual length + LONG GetActualLength() { + return m_lLength; + }; + + LONGLONG GetStart() { + return m_llPos; + }; +}; + + +typedef CGenericList CRequestList; + +// this class needs a worker thread, but the ones defined in classes\base +// are not suitable (they assume you have one message sent or posted per +// request, whereas here for efficiency we want just to set an event when +// there is work on the queue). +// +// we create CAsyncRequest objects and queue them on m_listWork. The worker +// thread pulls them off, completes them and puts them on m_listDone. +// The events m_evWork and m_evDone are set when the corresponding lists are +// not empty. +// +// Synchronous requests are done on the caller thread. These should be +// synchronised by the caller, but to make sure we hold m_csFile across +// the SetFilePointer/ReadFile code. +// +// Flush by calling BeginFlush. This rejects all further requests (by +// setting m_bFlushing within m_csLists), cancels all requests and moves them +// to the done list, and sets m_evDone to ensure that no WaitForNext operations +// will block. Call EndFlush to cancel this state. +// +// we support unaligned calls to SyncRead. This is done by opening the file +// twice if we are using unbuffered i/o (m_dwAlign > 1). +// !!!fix this to buffer on top of existing file handle? +class CAsyncIo +{ + + CCritSec m_csReader; + CAsyncStream *m_pStream; + + CCritSec m_csLists; // locks access to the list and events + BOOL m_bFlushing; // true if between BeginFlush/EndFlush + + CRequestList m_listWork; + CRequestList m_listDone; + + CAMEvent m_evWork; // set when list is not empty + CAMEvent m_evDone; + + // for correct flush behaviour: all protected by m_csLists + LONG m_cItemsOut; // nr of items not on listDone or listWork + BOOL m_bWaiting; // TRUE if someone waiting for m_evAllDone + CAMEvent m_evAllDone; // signal when m_cItemsOut goes to 0 if m_cWaiting + + + CAMEvent m_evStop; // set when thread should exit + HANDLE m_hThread; + + LONGLONG Size() { + ASSERT(m_pStream != NULL); + return m_pStream->Size(); + }; + + // start the thread + HRESULT StartThread(void); + + // stop the thread and close the handle + HRESULT CloseThread(void); + + // manage the list of requests. hold m_csLists and ensure + // that the (manual reset) event hevList is set when things on + // the list but reset when the list is empty. + // returns null if list empty + CAsyncRequest* GetWorkItem(); + + // get an item from the done list + CAsyncRequest* GetDoneItem(); + + // put an item on the work list + HRESULT PutWorkItem(CAsyncRequest* pRequest); + + // put an item on the done list + HRESULT PutDoneItem(CAsyncRequest* pRequest); + + // called on thread to process any active requests + void ProcessRequests(void); + + // initial static thread proc calls ThreadProc with DWORD + // param as this + static DWORD WINAPI InitialThreadProc(LPVOID pv) { + CAsyncIo * pThis = (CAsyncIo*) pv; + return pThis->ThreadProc(); + }; + + DWORD ThreadProc(void); + +public: + + CAsyncIo(CAsyncStream *pStream); + ~CAsyncIo(); + + // open the file + HRESULT Open(LPCTSTR pName); + + // ready for async activity - call this before + // calling Request + HRESULT AsyncActive(void); + + // call this when no more async activity will happen before + // the next AsyncActive call + HRESULT AsyncInactive(void); + + // queue a requested read. must be aligned. + HRESULT Request( + LONGLONG llPos, + LONG lLength, + BOOL bAligned, + BYTE* pBuffer, + LPVOID pContext, + DWORD_PTR dwUser); + + // wait for the next read to complete + HRESULT WaitForNext( + DWORD dwTimeout, + LPVOID *ppContext, + DWORD_PTR * pdwUser, + LONG * pcbActual); + + // perform a read of an already aligned buffer + HRESULT SyncReadAligned( + LONGLONG llPos, + LONG lLength, + BYTE* pBuffer, + LONG* pcbActual, + PVOID pvContext); + + // perform a synchronous read. will be buffered + // if not aligned. + HRESULT SyncRead( + LONGLONG llPos, + LONG lLength, + BYTE* pBuffer); + + // return length + HRESULT Length(LONGLONG *pllTotal, LONGLONG* pllAvailable); + + // all Reader positions, read lengths and memory locations must + // be aligned to this. + HRESULT Alignment(LONG* pl); + + HRESULT BeginFlush(); + HRESULT EndFlush(); + + LONG Alignment() + { + return m_pStream->Alignment(); + }; + + BOOL IsAligned(LONG l) { + if ((l & (Alignment() -1)) == 0) { + return TRUE; + } else { + return FALSE; + } + }; + + BOOL IsAligned(LONGLONG ll) { + return IsAligned( (LONG) (ll & 0xffffffff)); + }; + + // Accessor + HANDLE StopEvent() const { return m_evDone; } +}; + +#endif // __ASYNCIO_H__ diff --git a/filter/asyncStreamflt/asyncrdr.cpp b/filter/asyncStreamflt/asyncrdr.cpp new file mode 100644 index 0000000000..a9061512d6 --- /dev/null +++ b/filter/asyncStreamflt/asyncrdr.cpp @@ -0,0 +1,436 @@ +//------------------------------------------------------------------------------ +// File: AsyncRdr.cpp +// +// Desc: DirectShow sample code - base library with I/O functionality. +// This file implements I/O source filter methods and output pin +// methods for CAsyncReader and CAsyncOutputPin. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include "asyncio.h" +#include "asyncrdr.h" + +#include + +#include "asyncflt.h" + +// --- CAsyncOutputPin implementation --- + +CAsyncOutputPin::CAsyncOutputPin( + HRESULT * phr, + CAsyncReader *pReader, + CAsyncIo *pIo, + CCritSec * pLock) + : CBasePin( + NAME("Async output pin"), + pReader, + pLock, + phr, + L"Output", + PINDIR_OUTPUT), + m_pReader(pReader), + m_pIo(pIo) +{ +} + +CAsyncOutputPin::~CAsyncOutputPin() +{ +} + + +STDMETHODIMP CAsyncOutputPin::NonDelegatingQueryInterface(REFIID riid, void** ppv) +{ + CheckPointer(ppv,E_POINTER); + + if(riid == IID_IAsyncReader) + { + m_bQueriedForAsyncReader = TRUE; + return GetInterface((IAsyncReader*) this, ppv); + } + else + { + return CBasePin::NonDelegatingQueryInterface(riid, ppv); + } +} + + +HRESULT CAsyncOutputPin::GetMediaType(int iPosition, CMediaType *pMediaType) +{ + if(iPosition < 0) + { + return E_INVALIDARG; + } + if(iPosition > 0) + { + return VFW_S_NO_MORE_ITEMS; + } + + CheckPointer(pMediaType,E_POINTER); + CheckPointer(m_pReader,E_UNEXPECTED); + + *pMediaType = *m_pReader->LoadType(); + + return S_OK; +} + + +HRESULT CAsyncOutputPin::CheckMediaType(const CMediaType* pType) +{ + CAutoLock lck(m_pLock); + + /* We treat MEDIASUBTYPE_NULL subtype as a wild card */ + if((m_pReader->LoadType()->majortype == pType->majortype) && + (m_pReader->LoadType()->subtype == MEDIASUBTYPE_NULL || + m_pReader->LoadType()->subtype == pType->subtype)) + { + return S_OK; + } + + return S_FALSE; +} + + +HRESULT CAsyncOutputPin::InitAllocator(IMemAllocator **ppAlloc) +{ + CheckPointer(ppAlloc,E_POINTER); + + HRESULT hr = NOERROR; + CMemAllocator *pMemObject = NULL; + + *ppAlloc = NULL; + + /* Create a default memory allocator */ + pMemObject = new CMemAllocator(NAME("Base memory allocator"), NULL, &hr); + if(pMemObject == NULL) + { + return E_OUTOFMEMORY; + } + if(FAILED(hr)) + { + delete pMemObject; + return hr; + } + + /* Get a reference counted IID_IMemAllocator interface */ + hr = pMemObject->QueryInterface(IID_IMemAllocator,(void **)ppAlloc); + if(FAILED(hr)) + { + delete pMemObject; + return E_NOINTERFACE; + } + + ASSERT(*ppAlloc != NULL); + return NOERROR; +} + + +// we need to return an addrefed allocator, even if it is the preferred +// one, since he doesn't know whether it is the preferred one or not. +STDMETHODIMP +CAsyncOutputPin::RequestAllocator( + IMemAllocator* pPreferred, + ALLOCATOR_PROPERTIES* pProps, + IMemAllocator ** ppActual) +{ + CheckPointer(pPreferred,E_POINTER); + CheckPointer(pProps,E_POINTER); + CheckPointer(ppActual,E_POINTER); + ASSERT(m_pIo); + + // we care about alignment but nothing else + if(!pProps->cbAlign || !m_pIo->IsAligned(pProps->cbAlign)) + { + m_pIo->Alignment(&pProps->cbAlign); + } + + ALLOCATOR_PROPERTIES Actual; + HRESULT hr; + + if(pPreferred) + { + hr = pPreferred->SetProperties(pProps, &Actual); + + if(SUCCEEDED(hr) && m_pIo->IsAligned(Actual.cbAlign)) + { + pPreferred->AddRef(); + *ppActual = pPreferred; + return S_OK; + } + } + + // create our own allocator + IMemAllocator* pAlloc; + hr = InitAllocator(&pAlloc); + if(FAILED(hr)) + { + return hr; + } + + //...and see if we can make it suitable + hr = pAlloc->SetProperties(pProps, &Actual); + if(SUCCEEDED(hr) && m_pIo->IsAligned(Actual.cbAlign)) + { + // we need to release our refcount on pAlloc, and addref + // it to pass a refcount to the caller - this is a net nothing. + *ppActual = pAlloc; + return S_OK; + } + + // failed to find a suitable allocator + pAlloc->Release(); + + // if we failed because of the IsAligned test, the error code will + // not be failure + if(SUCCEEDED(hr)) + { + hr = VFW_E_BADALIGN; + } + return hr; +} + + +// queue an aligned read request. call WaitForNext to get +// completion. +STDMETHODIMP CAsyncOutputPin::Request( + IMediaSample* pSample, + DWORD_PTR dwUser) // user context +{ + CheckPointer(pSample,E_POINTER); + + REFERENCE_TIME tStart, tStop; + HRESULT hr = pSample->GetTime(&tStart, &tStop); + if(FAILED(hr)) + { + return hr; + } + + LONGLONG llPos = tStart / UNITS; + LONG lLength = (LONG) ((tStop - tStart) / UNITS); + + LONGLONG llTotal=0, llAvailable=0; + + hr = m_pIo->Length(&llTotal, &llAvailable); + if(llPos + lLength > llTotal) + { + // the end needs to be aligned, but may have been aligned + // on a coarser alignment. + LONG lAlign; + m_pIo->Alignment(&lAlign); + + llTotal = (llTotal + lAlign -1) & ~(lAlign-1); + + if(llPos + lLength > llTotal) + { + lLength = (LONG) (llTotal - llPos); + + // must be reducing this! + ASSERT((llTotal * UNITS) <= tStop); + tStop = llTotal * UNITS; + pSample->SetTime(&tStart, &tStop); + } + } + + BYTE* pBuffer; + hr = pSample->GetPointer(&pBuffer); + if(FAILED(hr)) + { + return hr; + } + + return m_pIo->Request(llPos, + lLength, + TRUE, + pBuffer, + (LPVOID)pSample, + dwUser); +} + + +// sync-aligned request. just like a request/waitfornext pair. +STDMETHODIMP +CAsyncOutputPin::SyncReadAligned( + IMediaSample* pSample) +{ + CheckPointer(pSample,E_POINTER); + + REFERENCE_TIME tStart, tStop; + HRESULT hr = pSample->GetTime(&tStart, &tStop); + if(FAILED(hr)) + { + return hr; + } + + LONGLONG llPos = tStart / UNITS; + LONG lLength = (LONG) ((tStop - tStart) / UNITS); + + LONGLONG llTotal; + LONGLONG llAvailable; + + hr = m_pIo->Length(&llTotal, &llAvailable); + if(llPos + lLength > llTotal) + { + // the end needs to be aligned, but may have been aligned + // on a coarser alignment. + LONG lAlign; + m_pIo->Alignment(&lAlign); + + llTotal = (llTotal + lAlign -1) & ~(lAlign-1); + + if(llPos + lLength > llTotal) + { + lLength = (LONG) (llTotal - llPos); + + // must be reducing this! + ASSERT((llTotal * UNITS) <= tStop); + tStop = llTotal * UNITS; + pSample->SetTime(&tStart, &tStop); + } + } + + BYTE* pBuffer; + hr = pSample->GetPointer(&pBuffer); + if(FAILED(hr)) + { + return hr; + } + + LONG cbActual; + hr = m_pIo->SyncReadAligned(llPos, + lLength, + pBuffer, + &cbActual, + pSample); + + pSample->SetActualDataLength(cbActual); + return hr; +} + + +// +// collect the next ready sample +STDMETHODIMP +CAsyncOutputPin::WaitForNext( + DWORD dwTimeout, + IMediaSample** ppSample, // completed sample + DWORD_PTR * pdwUser) // user context +{ + CheckPointer(ppSample,E_POINTER); + + LONG cbActual; + IMediaSample* pSample=0; + + HRESULT hr = m_pIo->WaitForNext(dwTimeout, + (LPVOID*) &pSample, + pdwUser, + &cbActual); + + if(SUCCEEDED(hr)) + { + pSample->SetActualDataLength(cbActual); + } + + *ppSample = pSample; + return hr; +} + + +// +// synchronous read that need not be aligned. +STDMETHODIMP +CAsyncOutputPin::SyncRead( + LONGLONG llPosition, // absolute Io position + LONG lLength, // nr bytes required + BYTE* pBuffer) // write data here +{ + return m_pIo->SyncRead(llPosition, lLength, pBuffer); +} + + +// return the length of the file, and the length currently +// available locally. We only support locally accessible files, +// so they are always the same +STDMETHODIMP +CAsyncOutputPin::Length( + LONGLONG* pTotal, + LONGLONG* pAvailable) +{ + return m_pIo->Length(pTotal, pAvailable); +} + + +STDMETHODIMP +CAsyncOutputPin::BeginFlush(void) +{ + return m_pIo->BeginFlush(); +} + + +STDMETHODIMP +CAsyncOutputPin::EndFlush(void) +{ + return m_pIo->EndFlush(); +} + + +STDMETHODIMP +CAsyncOutputPin::Connect( + IPin * pReceivePin, + const AM_MEDIA_TYPE *pmt // optional media type +) +{ + return m_pReader->Connect(pReceivePin, pmt); +} + + + +// --- CAsyncReader implementation --- + +#pragma warning(disable:4355) + +CAsyncReader::CAsyncReader( + TCHAR *pName, + LPUNKNOWN pUnk, + CAsyncStream *pStream, + HRESULT *phr) + : CBaseFilter( + pName, + pUnk, + &m_csFilter, + CLSID_AsyncStreamSource, + NULL + ), + m_OutputPin( + phr, + this, + &m_Io, + &m_csFilter), + m_Io(pStream) +{ +} + +CAsyncReader::~CAsyncReader() +{ +} + +int CAsyncReader::GetPinCount() +{ + return 1; +} + +CBasePin * CAsyncReader::GetPin(int n) +{ + if((GetPinCount() > 0) && (n == 0)) + { + return &m_OutputPin; + } + else + { + return NULL; + } +} + + + diff --git a/filter/asyncStreamflt/asyncrdr.h b/filter/asyncStreamflt/asyncrdr.h new file mode 100644 index 0000000000..11f57c3cd6 --- /dev/null +++ b/filter/asyncStreamflt/asyncrdr.h @@ -0,0 +1,228 @@ +//------------------------------------------------------------------------------ +// File: AsyncRdr.h +// +// Desc: DirectShow sample code - base library for I/O functionality. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __ASYNCRDR_H__ +#define __ASYNCRDR_H__ + + +// +// AsyncRdr +// +// Defines an IO source filter. +// +// This filter (CAsyncReader) supports IBaseFilter and IFileSourceFilter interfaces from the +// filter object itself. It has a single output pin (CAsyncOutputPin) +// which supports IPin and IAsyncReader. +// +// This filter is essentially a wrapper for the CAsyncFile class that does +// all the work. +// + + +// the filter class (defined below) +class CAsyncReader; + + +// the output pin class +class CAsyncOutputPin + : public IAsyncReader, + public CBasePin +{ +protected: + CAsyncReader* m_pReader; + CAsyncIo * m_pIo; + + // This is set every time we're asked to return an IAsyncReader + // interface + // This allows us to know if the downstream pin can use + // this transport, otherwise we can hook up to thinks like the + // dump filter and nothing happens + BOOL m_bQueriedForAsyncReader; + + HRESULT InitAllocator(IMemAllocator **ppAlloc); + +public: + // constructor and destructor + CAsyncOutputPin( + HRESULT * phr, + CAsyncReader *pReader, + CAsyncIo *pIo, + CCritSec * pLock); + + ~CAsyncOutputPin(); + + // --- CUnknown --- + + // need to expose IAsyncReader + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID, void**); + + // --- IPin methods --- + STDMETHODIMP Connect( + IPin * pReceivePin, + const AM_MEDIA_TYPE *pmt // optional media type + ); + + // --- CBasePin methods --- + + // return the types we prefer - this will return the known + // file type + HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); + + // can we support this type? + HRESULT CheckMediaType(const CMediaType* pType); + + // Clear the flag so we see if IAsyncReader is queried for + HRESULT CheckConnect(IPin *pPin) + { + m_bQueriedForAsyncReader = FALSE; + return CBasePin::CheckConnect(pPin); + } + + // See if it was asked for + HRESULT CompleteConnect(IPin *pReceivePin) + { + if (m_bQueriedForAsyncReader) { + return CBasePin::CompleteConnect(pReceivePin); + } else { + +#ifdef VFW_E_NO_TRANSPORT + return VFW_E_NO_TRANSPORT; +#else + return E_FAIL; +#endif + } + } + + // Remove our connection status + HRESULT BreakConnect() + { + m_bQueriedForAsyncReader = FALSE; + return CBasePin::BreakConnect(); + } + + // --- IAsyncReader methods --- + // pass in your preferred allocator and your preferred properties. + // method returns the actual allocator to be used. Call GetProperties + // on returned allocator to learn alignment and prefix etc chosen. + // this allocator will be not be committed and decommitted by + // the async reader, only by the consumer. + STDMETHODIMP RequestAllocator( + IMemAllocator* pPreferred, + ALLOCATOR_PROPERTIES* pProps, + IMemAllocator ** ppActual); + + // queue a request for data. + // media sample start and stop times contain the requested absolute + // byte position (start inclusive, stop exclusive). + // may fail if sample not obtained from agreed allocator. + // may fail if start/stop position does not match agreed alignment. + // samples allocated from source pin's allocator may fail + // GetPointer until after returning from WaitForNext. + STDMETHODIMP Request( + IMediaSample* pSample, + DWORD_PTR dwUser); // user context + + // block until the next sample is completed or the timeout occurs. + // timeout (millisecs) may be 0 or INFINITE. Samples may not + // be delivered in order. If there is a read error of any sort, a + // notification will already have been sent by the source filter, + // and STDMETHODIMP will be an error. + STDMETHODIMP WaitForNext( + DWORD dwTimeout, + IMediaSample** ppSample, // completed sample + DWORD_PTR * pdwUser); // user context + + // sync read of data. Sample passed in must have been acquired from + // the agreed allocator. Start and stop position must be aligned. + // equivalent to a Request/WaitForNext pair, but may avoid the + // need for a thread on the source filter. + STDMETHODIMP SyncReadAligned( + IMediaSample* pSample); + + + // sync read. works in stopped state as well as run state. + // need not be aligned. Will fail if read is beyond actual total + // length. + STDMETHODIMP SyncRead( + LONGLONG llPosition, // absolute file position + LONG lLength, // nr bytes required + BYTE* pBuffer); // write data here + + // return total length of stream, and currently available length. + // reads for beyond the available length but within the total length will + // normally succeed but may block for a long period. + STDMETHODIMP Length( + LONGLONG* pTotal, + LONGLONG* pAvailable); + + // cause all outstanding reads to return, possibly with a failure code + // (VFW_E_TIMEOUT) indicating they were cancelled. + // these are defined on IAsyncReader and IPin + STDMETHODIMP BeginFlush(void); + STDMETHODIMP EndFlush(void); + +}; + + +// +// The filter object itself. Supports IBaseFilter through +// CBaseFilter and also IFileSourceFilter directly in this object + +class CAsyncReader : public CBaseFilter +{ + +protected: + // filter-wide lock + CCritSec m_csFilter; + + // all i/o done here + CAsyncIo m_Io; + + // our output pin + CAsyncOutputPin m_OutputPin; + + // Type we think our data is + CMediaType m_mt; + +public: + + // construction / destruction + + CAsyncReader( + TCHAR *pName, + LPUNKNOWN pUnk, + CAsyncStream *pStream, + HRESULT *phr); + + ~CAsyncReader(); + + + // --- CBaseFilter methods --- + int GetPinCount(); + CBasePin *GetPin(int n); + + // --- Access our media type + const CMediaType *LoadType() const + { + return &m_mt; + } + + virtual HRESULT Connect( + IPin * pReceivePin, + const AM_MEDIA_TYPE *pmt // optional media type + ) + { + return m_OutputPin.CBasePin::Connect(pReceivePin, pmt); + } +}; + + + +#endif //__ASYNCRDR_H__ diff --git a/filter/include/StreamSourceflt.h b/filter/include/StreamSourceflt.h new file mode 100644 index 0000000000..326d3735be --- /dev/null +++ b/filter/include/StreamSourceflt.h @@ -0,0 +1,26 @@ +// This is a header file for the stream/file source filter + +#ifndef __ASYNC_STREAMSOURCE_H__ +#define __ASYNC_STREAMSOURCE_H__ + +// {2AE44C10-B451-4B01-9BBE-A5FBEF68C9D4} +DEFINE_GUID(CLSID_AsyncStreamSource, +0x2ae44c10, 0xb451, 0x4b01, 0x9b, 0xbe, 0xa5, 0xfb, 0xef, 0x68, 0xc9, 0xd4); + +// {268424D1-B6E9-4B28-8751-B7774F5ECF77} +DEFINE_GUID(IID_IStreamSourceFilter, +0x268424d1, 0xb6e9, 0x4b28, 0x87, 0x51, 0xb7, 0x77, 0x4f, 0x5e, 0xcf, 0x77); + +// We define the interface the app can use to program us +MIDL_INTERFACE("268424D1-B6E9-4B28-8751-B7774F5ECF77") +IStreamSourceFilter : public IFileSourceFilter +{ +public: + virtual STDMETHODIMP LoadStream(void *stream, LONGLONG readSize, LONGLONG streamSize, AM_MEDIA_TYPE *pmt ) = 0; + virtual STDMETHODIMP AddStreamData(LONGLONG offset, void *stream, LONGLONG addSize) = 0; + virtual STDMETHODIMP GetStreamInfo(LONGLONG *readSize, LONGLONG *streamSize) = 0; + virtual STDMETHODIMP SetReadSize(LONGLONG readSize) = 0; + virtual BOOL STDMETHODCALLTYPE IsReadPassEnd() = 0; +}; + +#endif // __ASYNC_STREAMSOURCE_H__ \ No newline at end of file diff --git a/filter/lib/AsyncStreamflt.ax b/filter/lib/AsyncStreamflt.ax new file mode 100644 index 0000000000..744e7237b1 Binary files /dev/null and b/filter/lib/AsyncStreamflt.ax differ diff --git a/filter/lib/register.bat b/filter/lib/register.bat new file mode 100644 index 0000000000..b71d0863a6 --- /dev/null +++ b/filter/lib/register.bat @@ -0,0 +1 @@ +@regsvr32 AsyncStreamflt.ax \ No newline at end of file diff --git a/filter/lib/unregister.bat b/filter/lib/unregister.bat new file mode 100644 index 0000000000..76bb89e792 --- /dev/null +++ b/filter/lib/unregister.bat @@ -0,0 +1 @@ +@regsvr32 /u AsyncStreamflt.ax \ No newline at end of file