diff --git a/LiveProxy.sln b/LiveProxy.sln new file mode 100644 index 0000000..04b7668 --- /dev/null +++ b/LiveProxy.sln @@ -0,0 +1,97 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LiveProxy", "LiveProxy\LiveProxy.vcxproj", "{EEE559F5-304D-4000-BC9E-3A30131A4F21}" + ProjectSection(ProjectDependencies) = postProject + {FC8C364C-0BAA-42B3-90AD-D2E17516202B} = {FC8C364C-0BAA-42B3-90AD-D2E17516202B} + {A760DF71-E428-4D4A-B9B8-68A9A02670A7} = {A760DF71-E428-4D4A-B9B8-68A9A02670A7} + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551} = {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551} + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D} = {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "live", "live", "{F437834D-E0CF-456A-B81A-5458C1BFC728}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BasicUsageEnvironment", "live\BasicUsageEnvironment\BasicUsageEnvironment.vcxproj", "{FC8C364C-0BAA-42B3-90AD-D2E17516202B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "groupsock", "live\groupsock\groupsock.vcxproj", "{A760DF71-E428-4D4A-B9B8-68A9A02670A7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liveMedia", "live\liveMedia\liveMedia.vcxproj", "{A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UsageEnvironment", "live\UsageEnvironment\UsageEnvironment.vcxproj", "{1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Debug|Any CPU.Build.0 = Debug|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Debug|Win32.ActiveCfg = Debug|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Debug|Win32.Build.0 = Debug|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Release|Any CPU.ActiveCfg = Release|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Release|Any CPU.Build.0 = Release|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Release|Mixed Platforms.Build.0 = Release|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Release|Win32.ActiveCfg = Release|Win32 + {EEE559F5-304D-4000-BC9E-3A30131A4F21}.Release|Win32.Build.0 = Release|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Debug|Win32.ActiveCfg = Debug|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Debug|Win32.Build.0 = Debug|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Release|Any CPU.ActiveCfg = Release|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Release|Mixed Platforms.Build.0 = Release|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Release|Win32.ActiveCfg = Release|Win32 + {FC8C364C-0BAA-42B3-90AD-D2E17516202B}.Release|Win32.Build.0 = Release|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Debug|Win32.ActiveCfg = Debug|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Debug|Win32.Build.0 = Debug|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Release|Any CPU.ActiveCfg = Release|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Release|Mixed Platforms.Build.0 = Release|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Release|Win32.ActiveCfg = Release|Win32 + {A760DF71-E428-4D4A-B9B8-68A9A02670A7}.Release|Win32.Build.0 = Release|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Debug|Win32.ActiveCfg = Debug|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Debug|Win32.Build.0 = Debug|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Release|Any CPU.ActiveCfg = Release|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Release|Mixed Platforms.Build.0 = Release|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Release|Win32.ActiveCfg = Release|Win32 + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551}.Release|Win32.Build.0 = Release|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Debug|Win32.ActiveCfg = Debug|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Debug|Win32.Build.0 = Debug|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Release|Any CPU.ActiveCfg = Release|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Release|Mixed Platforms.Build.0 = Release|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Release|Win32.ActiveCfg = Release|Win32 + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FC8C364C-0BAA-42B3-90AD-D2E17516202B} = {F437834D-E0CF-456A-B81A-5458C1BFC728} + {A760DF71-E428-4D4A-B9B8-68A9A02670A7} = {F437834D-E0CF-456A-B81A-5458C1BFC728} + {A1DDCF99-E9DC-4BBA-B87A-DBF7CC41A551} = {F437834D-E0CF-456A-B81A-5458C1BFC728} + {1A9E01CD-54C2-4C6C-BC78-2F3B7883D23D} = {F437834D-E0CF-456A-B81A-5458C1BFC728} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + VisualSVNWorkingCopyRoot = . + EndGlobalSection +EndGlobal diff --git a/LiveProxy.v11.suo b/LiveProxy.v11.suo new file mode 100644 index 0000000..2e93d59 Binary files /dev/null and b/LiveProxy.v11.suo differ diff --git a/LiveProxy/H264VideoSink.cpp b/LiveProxy/H264VideoSink.cpp new file mode 100644 index 0000000..6c2f83b --- /dev/null +++ b/LiveProxy/H264VideoSink.cpp @@ -0,0 +1,132 @@ +#include "stdafx.h" +#include "H264VideoSink.h" +#include +#include +#include +#include "MediaQueue.h" +#pragma unmanaged + +static int frame_count; + +/** +Static construtor to create our implemenation of a video size +*/ +H264VideoSink* H264VideoSink::createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId) { + return new H264VideoSink(env, subsession, streamId); +} + +/** +Singleton constructor +*/ +H264VideoSink::H264VideoSink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId): + MyVideoSink(env), m_fSubsession(subsession) +{ + TRACE_INFO("Video sink constructor"); + m_fStreamId = strDup(streamId); + //m_bufferSize=253440;//2*DUMMY_SINK_RECEIVE_BUFFER_SIZE; + //m_fPos = 0; + uint8_t startCode[] = {0x00, 0x00,0x01}; + + //m_buffer = new unsigned char[m_bufferSize]; + //m_frameQueue=new CMediaQueue(200); + m_decoder=new CVideoDecoder("H264"); + AddData(startCode, sizeof(startCode)); + //InitializeCriticalSection(&m_criticalSection); + //m_ready=1; +} + +/** +Destructor +*/ +H264VideoSink::~H264VideoSink() +{ + TRACE_INFO("Cleaning up video sink"); + //m_ready=0; + //if(m_buffer!=NULL) + // delete [] m_buffer; + //m_buffer = NULL; + //m_bufferSize = 0; + + if(m_fStreamId!=NULL) + delete [] m_fStreamId; + m_fStreamId = NULL; + + //if(m_frameQueue!=NULL) + // delete m_frameQueue; + //m_frameQueue=NULL; +} + +/** +Initialize the video buffer with the header information + +void H264VideoSink::AddData(uint8_t* aData, int aSize) +{ + memcpy(m_buffer + m_fPos, aData, aSize); + m_fPos += aSize; +} + */ + +/** +Keep retrieving frames from the source +*/ +Boolean H264VideoSink::continuePlaying() +{ + if (fSource == NULL) + { + TRACE_ERROR("no source for continue play"); + return False; + } + TRACE_DEBUG("continuePlaying. BufferSize=%d",m_bufferSize); + fSource->getNextFrame(m_buffer + m_fPos, m_bufferSize - m_fPos, afterGettingFrame, this, onSourceClosure, this); + return True; +} + +/** +Called by the live555 code once we have a frame +*/ +void H264VideoSink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, + struct timeval presentationTime, unsigned durationInMicroseconds) +{ + TRACE_DEBUG("afterGettingFrame. FrameSize=%d",frameSize); + H264VideoSink* sink = (H264VideoSink*)clientData; + sink->afterGettingFrame1(frameSize, presentationTime); + if (sink->continuePlaying() == false) + { + TRACE_ERROR("Continue play failed closing source"); + sink->onSourceClosure(clientData); + } +} + +/** +Prepare the video frame for decoding +*/ +void H264VideoSink::afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) +{ + int got_frame = 0; + unsigned int size = frameSize; + unsigned char *pBuffer = m_buffer + m_fPos; + uint8_t* data = (uint8_t*)pBuffer; + uint8_t startCode4[] = {0x00, 0x00, 0x00, 0x01}; + uint8_t startCode3[] = {0x00, 0x00, 0x01}; + FrameInfo *frame=NULL; + + if(size<4){ + return; + } + if(memcmp(startCode3, pBuffer, sizeof(startCode3)) == 0) + { + data += 3; + }else if(memcmp(startCode4, pBuffer, sizeof(startCode4)) == 0){ + data += 4; + }else{ + pBuffer -= 3; + size += 3; + } + + // send the frame out to the decoder + frame=m_decoder->DecodeFrame(pBuffer, size); + if(frame!=NULL) + m_frameQueue->put(frame); +} + + \ No newline at end of file diff --git a/LiveProxy/H264VideoSink.h b/LiveProxy/H264VideoSink.h new file mode 100644 index 0000000..2d87717 --- /dev/null +++ b/LiveProxy/H264VideoSink.h @@ -0,0 +1,57 @@ +#pragma once +#include +#include +#include +#include +#include +#include "live.h" +#include "MediaSink.hh" +#include "MediaQueue.h" +#include "VideoDecoder.h" +#include "MyVideoSink.h" + +#define DUMMY_SINK_RECEIVE_BUFFER_SIZE 100000 + + +/** +Our implementation of a MediaSink. +The media sink recevices the video frames from the rtsp stream +then passes them to our /sa CVideoDecoder for processing. This class +is also the keeper of our decoded frame queue /sa CMediaQueue +*/ +class H264VideoSink : public MyVideoSink { + +// static methods +public: + static H264VideoSink * createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId = NULL); + +protected: + static void afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, timeval presentationTime, unsigned durationInMicroseconds); + +public: + //StreamTrack* get_StreamTrack(){return m_tk;} + //void set_StreamTrack(StreamTrack* tk){m_tk=tk;} + //CMediaQueue* get_FrameQueue(){return m_ready==1? m_frameQueue:NULL;} + virtual ~H264VideoSink(); + + +protected: + H264VideoSink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId); + void afterGettingFrame1(unsigned frameSize, struct timeval presentationTime); + //void AddData(uint8_t* aData, int aSize); + Boolean continuePlaying(); + + +//Members +protected: + CVideoDecoder * m_decoder; + //unsigned char * m_buffer; + //unsigned int m_bufferSize; + MediaSubsession& m_fSubsession; + //CRITICAL_SECTION m_criticalSection; + char * m_fStreamId; + //CMediaQueue* m_frameQueue; + //int m_fPos; + //int m_ready; +}; + diff --git a/LiveProxy/LiveProxy.vcxproj b/LiveProxy/LiveProxy.vcxproj new file mode 100644 index 0000000..f92d0d9 --- /dev/null +++ b/LiveProxy/LiveProxy.vcxproj @@ -0,0 +1,126 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {EEE559F5-304D-4000-BC9E-3A30131A4F21} + LiveProxy + + + + DynamicLibrary + true + Unicode + false + v110 + + + DynamicLibrary + false + true + Unicode + v110 + + + + + + + + + + + + + true + $(IncludePath) + $(SolutionDir)ffmpeg\lib;$(SolutionDir)live\lib;$(LibraryPath) + $(ProjectDir)bin\Debug + + + $(SolutionDir)Cisco Source Filter\Common\DirectShow;$(SolutionDir)live\BasicUsageEnvironment\include;$(SolutionDir)live\UsageEnvironment\include;$(SolutionDir)live\groupsock\include;$(SolutionDir)live\liveMedia\include;$(SolutionDir)ffmpeg\include\;$(IncludePath) + $(SolutionDir)ffmpeg\lib;$(SolutionDir)live\lib;$(LibraryPath) + $(ProjectDir)bin\Release + + + + Level3 + Disabled + WIN32;_WINDOWS;NDEBUG;_USRDLL;_LIB;_UNICODE;UNICODE;_ITERATOR_DEBUG_LEVEL=0;_CRT_SECURE_NO_WARNINGS;MY_DLL;%(PreprocessorDefinitions) + Cdecl + false + C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\ffmpeg\include;C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\live\UsageEnvironment\include;C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\live\groupsock\include;C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\live\BasicUsageEnvironment\include;C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\live\liveMedia\include;%(AdditionalIncludeDirectories) + MultiThreadedDLL + + + true + + + $(SolutionDir)live\lib;%(AdditionalLibraryDirectories) + %(AdditionalDependencies) + + + copy $(ProjectDir)support\*.dll $(OutputPath) + + + copy $(ProjectDir)support $(OutDir) + + + + + Level3 + Disabled + false + false + _MBCS;MY_DLL;%(PreprocessorDefinitions) + + + true + false + false + + + $(SolutionDir)live\lib;%(AdditionalLibraryDirectories) + false + + + copy $(ProjectDir)support $(OutDir) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LiveProxy/LiveProxy.vcxproj.filters b/LiveProxy/LiveProxy.vcxproj.filters new file mode 100644 index 0000000..c0a3dcc --- /dev/null +++ b/LiveProxy/LiveProxy.vcxproj.filters @@ -0,0 +1,84 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/LiveProxy/LiveProxy.vcxproj.user b/LiveProxy/LiveProxy.vcxproj.user new file mode 100644 index 0000000..a375ae3 --- /dev/null +++ b/LiveProxy/LiveProxy.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/LiveProxy/MP4VVideoSink.cpp b/LiveProxy/MP4VVideoSink.cpp new file mode 100644 index 0000000..9c8f03e --- /dev/null +++ b/LiveProxy/MP4VVideoSink.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include "MP4VVideoSink.h" +#include +#include +#include +#include "MediaQueue.h" +#pragma unmanaged + +static int frame_count; + +/** +Static construtor to create our implemenation of a video size +*/ +MP4VVideoSink* MP4VVideoSink::createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId) { + return new MP4VVideoSink(env, subsession, streamId); +} + +/** +Singleton constructor +*/ +MP4VVideoSink::MP4VVideoSink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId): + MyVideoSink(env), m_fSubsession(subsession) +{ + TRACE_INFO("Video sink constructor"); + m_fStreamId = strDup(streamId); + //m_bufferSize=253440;//2*DUMMY_SINK_RECEIVE_BUFFER_SIZE; + //m_fPos = 0; + //MediaSubsession *thissession = new MediaSubsession(subsession); + uint8_t startCode[] = {0x00, 0x00, 0x01}; + /*unsigned configLen; + unsigned char* configData + = parseGeneralConfigStr(thissession->fmtp_config(), configLen); + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + AddData(configData, configLen); + delete[] configData; + */ + //m_buffer = new unsigned char[m_bufferSize]; + //m_frameQueue=new CMediaQueue(200); + m_decoder=new CVideoDecoder("MP4V"); + AddData(startCode, sizeof(startCode)); + //InitializeCriticalSection(&m_criticalSection); + //m_ready=1; +} + +/** +Destructor +*/ +MP4VVideoSink::~MP4VVideoSink() +{ + TRACE_INFO("Cleaning up video sink"); + //m_ready=0; + //if(m_buffer!=NULL) + // delete [] m_buffer; + //m_buffer = NULL; + //m_bufferSize = 0; + + if(m_fStreamId!=NULL) + delete [] m_fStreamId; + m_fStreamId = NULL; + + //if(m_frameQueue!=NULL) + // delete m_frameQueue; + //m_frameQueue=NULL; +} + +/** +Initialize the video buffer with the header information + +void MP4VVideoSink::AddData(uint8_t* aData, int aSize) +{ + memcpy(m_buffer + m_fPos, aData, aSize); + m_fPos += aSize; +} + */ + +/** +Keep retrieving frames from the source +*/ +Boolean MP4VVideoSink::continuePlaying() +{ + if (fSource == NULL) + { + TRACE_ERROR("no source for continue play"); + return False; + } + TRACE_DEBUG("continuePlaying. BufferSize=%d",m_bufferSize); + fSource->getNextFrame(m_buffer + m_fPos, m_bufferSize - m_fPos, afterGettingFrame, this, onSourceClosure, this); + return True; +} + +/** +Called by the live555 code once we have a frame +*/ +void MP4VVideoSink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, + struct timeval presentationTime, unsigned durationInMicroseconds) +{ + TRACE_DEBUG("afterGettingFrame. FrameSize=%d",frameSize); + MP4VVideoSink* sink = (MP4VVideoSink*)clientData; + sink->afterGettingFrame1(frameSize, presentationTime); + if (sink->continuePlaying() == false) + { + TRACE_ERROR("Continue play failed closing source"); + sink->onSourceClosure(clientData); + } +} + +/** +Prepare the video frame for decoding +*/ +void MP4VVideoSink::afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) +{ + int got_frame = 0; + unsigned int size = frameSize; + unsigned char *pBuffer = m_buffer + m_fPos; + uint8_t* data = (uint8_t*)pBuffer; + uint8_t startCode4[] = {0x00, 0x00, 0x00, 0x01}; + uint8_t startCode3[] = {0x00, 0x00, 0x01}; + FrameInfo *frame=NULL; + + if(size<4){ + return; + } + if(memcmp(startCode3, pBuffer, sizeof(startCode3)) == 0) + { + data += 3; + }else if(memcmp(startCode4, pBuffer, sizeof(startCode4)) == 0){ + data += 4; + }else{ + pBuffer -= 3; + size += 3; + } + + // send the frame out to the decoder + frame=m_decoder->DecodeFrame(pBuffer, size); + if(frame!=NULL) + m_frameQueue->put(frame); +} + + \ No newline at end of file diff --git a/LiveProxy/MP4VVideoSink.h b/LiveProxy/MP4VVideoSink.h new file mode 100644 index 0000000..f7a2592 --- /dev/null +++ b/LiveProxy/MP4VVideoSink.h @@ -0,0 +1,57 @@ +#pragma once +#include +#include +#include +#include +#include +#include "live.h" +#include "MediaSink.hh" +#include "MediaQueue.h" +#include "VideoDecoder.h" +#include "MyVideoSink.h" + +#define DUMMY_SINK_RECEIVE_BUFFER_SIZE 100000 + + +/** +Our implementation of a MediaSink. +The media sink recevices the video frames from the rtsp stream +then passes them to our /sa CVideoDecoder for processing. This class +is also the keeper of our decoded frame queue /sa CMediaQueue +*/ +class MP4VVideoSink : public MyVideoSink { + +// static methods +public: + static MP4VVideoSink * createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId = NULL); + +protected: + static void afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, timeval presentationTime, unsigned durationInMicroseconds); + +public: + //StreamTrack* get_StreamTrack(){return m_tk;} + //void set_StreamTrack(StreamTrack* tk){m_tk=tk;} + //CMediaQueue* get_FrameQueue(){return m_ready==1? m_frameQueue:NULL;} + virtual ~MP4VVideoSink(); + + +protected: + MP4VVideoSink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId); + void afterGettingFrame1(unsigned frameSize, struct timeval presentationTime); + //void AddData(uint8_t* aData, int aSize); + Boolean continuePlaying(); + + +//Members +protected: + CVideoDecoder * m_decoder; + //unsigned char * m_buffer; + //unsigned int m_bufferSize; + MediaSubsession& m_fSubsession; + //CRITICAL_SECTION m_criticalSection; + char * m_fStreamId; + //CMediaQueue* m_frameQueue; + //int m_fPos; + //int m_ready; +}; + diff --git a/LiveProxy/MediaQueue.cpp b/LiveProxy/MediaQueue.cpp new file mode 100644 index 0000000..03e4225 --- /dev/null +++ b/LiveProxy/MediaQueue.cpp @@ -0,0 +1,139 @@ +#include "stdafx.h" +#include "MediaQueue.h" +#include "trace.h" + + +/** +The queue of video frames. + +Initalize the queue with the specified size +\param queueSize The size of the video queue +*/ +CMediaQueue::CMediaQueue(int queueSize) +{ + count = 0; + size=queueSize; + head = tail = NULL; + ptr = pstatic = (MediaQueue *)malloc(sizeof(MediaQueue)*queueSize); + ZeroMemory(pstatic, sizeof(MediaQueue)*queueSize); + head = ptr; + readpos = writepos = head; + for (int i = 1; i < queueSize; i++) + { + ptr->next = pstatic+i; + ptr = ptr->next; + } + + tail = ptr; + tail->next = head; + ptr = head; + hFrameListLock = CreateMutex(NULL,FALSE,NULL); + hRecvEvent = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +/** +Cleanup the queue +*/ +CMediaQueue::~CMediaQueue() +{ + CloseHandle(hRecvEvent); + CloseHandle(hFrameListLock); + + //empty the queue + while (count >= 0) + { + //remove a frame so we can add one + count--; + if (readpos!=NULL && readpos->frame != NULL) + { + free(readpos->frame); + readpos->frame = NULL; + } + readpos = readpos->next; + } + free(pstatic); + +} + + +/** +Add a new video frame to the queue +*/ +void CMediaQueue::put(FrameInfo* frame) +{ + if(ptr == NULL) + return; + + WaitForSingleObject(hFrameListLock,INFINITE); + if (count >= size) + { + //remove a frame so we can add one + count = size-1; + if (readpos->frame) + { + free(readpos->frame); + readpos->frame = NULL; + } + readpos = readpos->next; + } + + //add the frame + writepos->frame = frame; + writepos = writepos->next; + count++; + + if (count <=1) + { + SetEvent(hRecvEvent); + } + ReleaseMutex(hFrameListLock); +} + +/** +Remove a video frame from the queue +*/ +FrameInfo* CMediaQueue::get() +{ + FrameInfo* frame = NULL; + + if (count < 1) + { + TRACE_WARN("No frames in queue, waiting"); + WaitForSingleObject(hRecvEvent, 500); + } + + ResetEvent(hRecvEvent); + + WaitForSingleObject(hFrameListLock,INFINITE); + if(count > 0) + { + frame = readpos->frame; + readpos->frame = NULL; + readpos = readpos->next; + count--; + }else{ + TRACE_ERROR("No frames in queue"); + } + + ReleaseMutex(hFrameListLock); + return(frame); +} + +/** +Empty the queue +*/ +void CMediaQueue::reset() +{ + WaitForSingleObject(hFrameListLock,INFINITE); + ptr = readpos; + while (ptr->frame) + { + free(ptr->frame); + ptr->frame = NULL; + ptr = ptr->next; + } + writepos = readpos; + count = 0; + ReleaseMutex(hFrameListLock); + +} \ No newline at end of file diff --git a/LiveProxy/MediaQueue.h b/LiveProxy/MediaQueue.h new file mode 100644 index 0000000..ebb2714 --- /dev/null +++ b/LiveProxy/MediaQueue.h @@ -0,0 +1,89 @@ +#pragma once + +/** +The type, size and timestamp of the frame +In live mode time is always 0 +*/ +typedef struct __FrameHeader +{ + long TimeStamp; + long FrameType; + long FrameLen; +}FrameHeader; + +/** +The frame header and the buffer +this is what we queue up +*/ +typedef struct __FrameInfo +{ + FrameHeader frameHead; + char* pdata; +}FrameInfo; + + +/** +This is the queue of video frames +*/ +typedef struct __MediaQueue +{ + FrameInfo *frame; + struct __MediaQueue *next; +}MediaQueue; + + +/** +This class manages the queue of of video frames +*/ +class CMediaQueue +{ + MediaQueue *head, *tail; + MediaQueue *writepos, *readpos; + MediaQueue *ptr, *pstatic; + HANDLE hFrameListLock; + HANDLE hRecvEvent; + int count; + int size; + +public: + CMediaQueue(int queueSize); + ~CMediaQueue(); + + void put(FrameInfo* frame); + FrameInfo* get(); + void reset(); + +public: + /** + The capacity of the queue + */ + int get_Size() + { + return(size); + } + + /** + The number of frames in the queue + */ + int get_Count() + { + return(count); + } + + /** + Helper to see if queue is empty + */ + int get_isEmpty() + { + return(count<=0?1:0); + } + + /** + remove all items from the queue + */ + int empty() + { + SetEvent(hRecvEvent); + return(count=0); + } +}; diff --git a/LiveProxy/MyRTSPClient.cpp b/LiveProxy/MyRTSPClient.cpp new file mode 100644 index 0000000..211431c --- /dev/null +++ b/LiveProxy/MyRTSPClient.cpp @@ -0,0 +1,52 @@ +#pragma once +#include "stdafx.h" +#include "MyRTSPClient.h" + + +/** +Static method to create an new instance of our version of a RTSPClient +*/ +MyRTSPClient* MyRTSPClient::createNew(UsageEnvironment& env, char const* rtspURL, char const* format) +{ + int level= g_logLevel; + + return new MyRTSPClient(env, rtspURL, format, level, "RTSP", 0); +} + +/** +Singleton constructor +*/ +MyRTSPClient::MyRTSPClient(UsageEnvironment& env, char const* rtspURL, char const* format, + int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum) + : RTSPClient(env,rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum), m_sink(NULL) +{ + TRACE_INFO("Created RTSP client"); + TRACE_INFO(rtspURL); + + //m_sink=MyVideoSink::createNew(env, *scs.subsession, "RTSP_STREAM", format);//rtspClient->url()); + + if (!strcmp(format, "H264")) + { + m_sink = H264VideoSink::createNew(env, *scs.subsession, "RTSP_STREAM"); + } else { + if (!strcmp(format, "MP4V")) + { + m_sink = MP4VVideoSink::createNew(env, *scs.subsession, "RTSP_STREAM"); + } else { + TRACE_INFO("Unsupported Codec"); + } + } + +} + +/** +Destructor. +No work at this level +*/ +MyRTSPClient::~MyRTSPClient() +{ + TRACE_INFO("Destroy RTSP client"); + if(m_sink!=NULL) + delete m_sink; + m_sink=NULL; +} \ No newline at end of file diff --git a/LiveProxy/MyRTSPClient.h b/LiveProxy/MyRTSPClient.h new file mode 100644 index 0000000..ab04fbd --- /dev/null +++ b/LiveProxy/MyRTSPClient.h @@ -0,0 +1,36 @@ +#pragma once +#include "stdafx.h" +#include "StreamClientState.h" +#include "MediaSink.hh" +#include "live.h" +#include "MediaQueue.h" +#include "H264VideoSink.h"; +#include "MP4VVideoSink.h"; + + +/** +Our implementation of an RTSPClient + +This class is used to manage the filter video sink /sa MyVideoSink +and implement the rtsp interface to receive video frames see both +/sa CStreammedia and /sa CPushPinCisco +*/ +class MyRTSPClient: public RTSPClient { +public: + static MyRTSPClient* createNew(UsageEnvironment& env, char const* rtspURL, char const* format); + virtual ~MyRTSPClient(); + +protected: + MyRTSPClient(UsageEnvironment& env, char const* rtspURL, char const* format, int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum); + +public: + StreamTrack* get_StreamTrack(){return tk;} + void set_StreamTrack(StreamTrack* tk){this->tk=tk;} + + MyVideoSink * get_sink(){return m_sink;} + +public: + StreamClientState scs; + MyVideoSink * m_sink; + StreamTrack * tk; +}; \ No newline at end of file diff --git a/LiveProxy/MyUsageEnvironment.cpp b/LiveProxy/MyUsageEnvironment.cpp new file mode 100644 index 0000000..2d2ebb9 --- /dev/null +++ b/LiveProxy/MyUsageEnvironment.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include "MyUsageEnvironment.h" +#include "trace.h" + +CMyUsageEnvironment* +CMyUsageEnvironment::createNew(TaskScheduler& taskScheduler) { + return new CMyUsageEnvironment(taskScheduler); +} + + + +CMyUsageEnvironment::CMyUsageEnvironment(TaskScheduler& taskScheduler) : BasicUsageEnvironment(taskScheduler) +{ +} + + +CMyUsageEnvironment::~CMyUsageEnvironment(void) +{ +} + +UsageEnvironment& CMyUsageEnvironment::operator<<(char const* str) { + if (str == NULL) str = "(NULL)"; // sanity check + m_logstream << str; + write(); + return *this; +} + +UsageEnvironment& CMyUsageEnvironment::operator<<(int i) { + m_logstream << i; + return *this; +} + +UsageEnvironment& CMyUsageEnvironment::operator<<(unsigned u) { + m_logstream << u; + return *this; +} + +UsageEnvironment& CMyUsageEnvironment::operator<<(double d) { + m_logstream << d; + return *this; +} + +UsageEnvironment& CMyUsageEnvironment::operator<<(void* p) { + m_logstream << p; + return *this; +} + +void CMyUsageEnvironment::write(){ + int len = get_size(); + if(m_logstream.str ()[len-1] == '\n') + { + std::string str(m_logstream.str()); + TRACE_DEBUG(str.c_str()); + m_logstream.str(std::string()); + } +} \ No newline at end of file diff --git a/LiveProxy/MyUsageEnvironment.h b/LiveProxy/MyUsageEnvironment.h new file mode 100644 index 0000000..9a55990 --- /dev/null +++ b/LiveProxy/MyUsageEnvironment.h @@ -0,0 +1,35 @@ +#pragma once +#include "stdafx.h" +#include "basicusageenvironment.hh" +#include + +/** +Create our own usage environment class so we can capture +the log information and write it our trace log +*/ +class CMyUsageEnvironment : + public BasicUsageEnvironment +{ +public: + static CMyUsageEnvironment* createNew(TaskScheduler& taskScheduler); +public: + + CMyUsageEnvironment(TaskScheduler& taskScheduler); + ~CMyUsageEnvironment(void); + + int get_size(){return m_logstream.tellp();} + + //overloads + virtual UsageEnvironment& operator<<(char const* str); + virtual UsageEnvironment& operator<<(int i); + virtual UsageEnvironment& operator<<(unsigned u); + virtual UsageEnvironment& operator<<(double d); + virtual UsageEnvironment& operator<<(void* p); + +private: + void write(); + +private: + std::stringstream m_logstream; +}; + diff --git a/LiveProxy/MyVideoSink.cpp b/LiveProxy/MyVideoSink.cpp new file mode 100644 index 0000000..cf5b89e --- /dev/null +++ b/LiveProxy/MyVideoSink.cpp @@ -0,0 +1,137 @@ +#include "stdafx.h" +#include "MyVideoSink.h" +#include +#include +#include +#include "MediaQueue.h" +#pragma unmanaged + +static int frame_count; + +/** +Static construtor to create our implemenation of a video sink + +MyVideoSink* MyVideoSink::createNew(UsageEnvironment& env) { + return new MyVideoSink(env); +} +*/ +/** +Singleton constructor +*/ +MyVideoSink::MyVideoSink(UsageEnvironment& env): +MediaSink(env) , m_ready(0) +{ + TRACE_INFO("Video sink constructor"); + //m_fStreamId = strDup(streamId); + //m_format = strDup(format); + m_bufferSize=253440;//2*DUMMY_SINK_RECEIVE_BUFFER_SIZE; + m_fPos = 0; + //uint8_t startCode[] = {0x00, 0x00,0x01}; + + m_buffer = new unsigned char[m_bufferSize]; + m_frameQueue=new CMediaQueue(200); + //m_decoder=new CVideoDecoder(m_format); + //AddData(startCode, sizeof(startCode)); + InitializeCriticalSection(&m_criticalSection); + m_ready=1; +} + +/** +Destructor +*/ +MyVideoSink::~MyVideoSink() +{ + TRACE_INFO("Cleaning up video sink"); + m_ready=0; + if(m_buffer!=NULL) + delete [] m_buffer; + m_buffer = NULL; + m_bufferSize = 0; + + //if(m_fStreamId!=NULL) + // delete [] m_fStreamId; + //m_fStreamId = NULL; + + //m_format = NULL; + + if(m_frameQueue!=NULL) + delete m_frameQueue; + m_frameQueue=NULL; +} + +/** +Initialize the video buffer with the header information +*/ +void MyVideoSink::AddData(uint8_t* aData, int aSize) +{ + memcpy(m_buffer + m_fPos, aData, aSize); + m_fPos += aSize; +} + + +/** +Keep retrieving frames from the source + +Boolean MyVideoSink::continuePlaying() +{ + if (fSource == NULL) + { + TRACE_ERROR("no source for continue play"); + return False; + } + TRACE_DEBUG("continuePlaying. BufferSize=%d",m_bufferSize); + fSource->getNextFrame(m_buffer + m_fPos, m_bufferSize - m_fPos, afterGettingFrame, this, onSourceClosure, this); + return True; +} +*/ +/** +Called by the live555 code once we have a frame + +void MyVideoSink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, + struct timeval presentationTime, unsigned durationInMicroseconds) +{ + TRACE_DEBUG("afterGettingFrame. FrameSize=%d",frameSize); + MyVideoSink* sink = (MyVideoSink*)clientData; + sink->afterGettingFrame1(frameSize, presentationTime); + if (sink->continuePlaying() == false) + { + TRACE_ERROR("Continue play failed closing source"); + sink->onSourceClosure(clientData); + } +} +*/ + +/** +Prepare the video frame for decoding + +void MyVideoSink::afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) +{ + int got_frame = 0; + unsigned int size = frameSize; + unsigned char *pBuffer = m_buffer + m_fPos; + uint8_t* data = (uint8_t*)pBuffer; + uint8_t startCode4[] = {0x00, 0x00, 0x00, 0x01}; + uint8_t startCode3[] = {0x00, 0x00, 0x01}; + FrameInfo *frame=NULL; + + if(size<4){ + return; + } + if(memcmp(startCode3, pBuffer, sizeof(startCode3)) == 0) + { + data += 3; + }else if(memcmp(startCode4, pBuffer, sizeof(startCode4)) == 0){ + data += 4; + }else{ + pBuffer -= 3; + size += 3; + } + + // send the frame out to the decoder + frame=m_decoder->DecodeFrame(pBuffer, size); + if(frame!=NULL) + m_frameQueue->put(frame); +} +*/ + + \ No newline at end of file diff --git a/LiveProxy/MyVideoSink.h b/LiveProxy/MyVideoSink.h new file mode 100644 index 0000000..acfe5aa --- /dev/null +++ b/LiveProxy/MyVideoSink.h @@ -0,0 +1,57 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "live.h" +#include "MediaQueue.h" +#include "VideoDecoder.h" + +#define DUMMY_SINK_RECEIVE_BUFFER_SIZE 100000 + + +/** +Our implementation of a MediaSink. +The media sink recevices the video frames from the rtsp stream +then passes them to our /sa CVideoDecoder for processing. This class +is also the keeper of our decoded frame queue /sa CMediaQueue +*/ +class MyVideoSink : public MediaSink { + +// static methods +public: + static MyVideoSink * createNew(UsageEnvironment& env); + +protected: + //static void afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, timeval presentationTime, unsigned durationInMicroseconds); + +public: + //StreamTrack* get_StreamTrack(){return m_tk;} + //void set_StreamTrack(StreamTrack* tk){m_tk=tk;} + CMediaQueue* get_FrameQueue(){return m_ready==1? m_frameQueue:NULL;} + virtual ~MyVideoSink(); + + +protected: + MyVideoSink(UsageEnvironment& env); + //void afterGettingFrame1(unsigned frameSize, struct timeval presentationTime); + void AddData(uint8_t* aData, int aSize); + //Boolean continuePlaying(); + + +//Members +protected: + //CVideoDecoder * m_decoder; + unsigned char * m_buffer; + unsigned int m_bufferSize; + //MediaSubsession& m_fSubsession; + CRITICAL_SECTION m_criticalSection; + //char * m_fStreamId; + //char * m_format; + CMediaQueue* m_frameQueue; + int m_fPos; + int m_ready; +}; + diff --git a/LiveProxy/StreamClient.cpp b/LiveProxy/StreamClient.cpp new file mode 100644 index 0000000..fecd844 --- /dev/null +++ b/LiveProxy/StreamClient.cpp @@ -0,0 +1,28 @@ +// Implementation of "StreamClientState": +#pragma once +#include "stdafx.h" +#include "StreamClientState.h" +#include "live.h" + +/** +Stream client constructor. +Clear out of public propertices +*/ +StreamClientState::StreamClientState() + : iter(NULL), session(NULL), subsession(NULL), streamTimerTask(NULL), duration(0.0) { +} + +/** +Stream client destructor. +Clean up the env +*/ +StreamClientState::~StreamClientState() { + delete iter; + if (session != NULL) { + // We also need to delete "session", and unschedule "streamTimerTask" (if set) + UsageEnvironment& env = session->envir(); // alias + + env.taskScheduler().unscheduleDelayedTask(streamTimerTask); + Medium::close(session); + } +} \ No newline at end of file diff --git a/LiveProxy/StreamClientState.h b/LiveProxy/StreamClientState.h new file mode 100644 index 0000000..82277e7 --- /dev/null +++ b/LiveProxy/StreamClientState.h @@ -0,0 +1,22 @@ +#pragma once +#include "stdafx.h" +#include "MyVideoSink.h" +#include "live.h" + +// + +/** +Define a class to hold per-stream state that we maintain throughout each stream's lifetime: +*/ +class StreamClientState { +public: + StreamClientState(); + virtual ~StreamClientState(); + +public: + MediaSubsessionIterator* iter; + MediaSession* session; + MediaSubsession* subsession; + TaskToken streamTimerTask; + double duration; +}; \ No newline at end of file diff --git a/LiveProxy/UnitTest/LiveProxyUnitTest.cpp b/LiveProxy/UnitTest/LiveProxyUnitTest.cpp new file mode 100644 index 0000000..dc3af8d --- /dev/null +++ b/LiveProxy/UnitTest/LiveProxyUnitTest.cpp @@ -0,0 +1,702 @@ +// PushSourceUnitTest.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include +#include +#include +#include +#include + +//#include "..\Cisco\PushSource\TransportUrl.h" +#include "..\trace.h" +#include "..\live.h" +#include "..\MediaQueue.h" +#include "..\VideoDecoder.h" +#include "..\MyUsageEnvironment.h" + +std::ofstream logFile; +bool FileExists(const char *fileName) +{ + std::ifstream infile(fileName); + return infile.good(); +} +int countLines(LPCSTR file) { + int number_of_lines = 0; + std::string line; + std::ifstream myfile(file); + + while (std::getline(myfile, line)) + ++number_of_lines; + return number_of_lines; +} +void Write(int pass, char * lpszFormat, ...) +{ + static char szMsg[512]; + va_list argList; + va_start(argList, lpszFormat); + try + { + vsprintf(szMsg,lpszFormat, argList); + + WORD color=FOREGROUND_BLUE; + + if(pass==1) + color=FOREGROUND_GREEN; + + if(pass==2) + color=FOREGROUND_RED; + + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); // Get handle to standard output + SetConsoleTextAttribute(hConsole, color|BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE|BACKGROUND_INTENSITY); + std::cout << szMsg << std::endl; + logFile << szMsg << std::endl; + } + catch(...) + { + strcpy(szMsg ,"DebugHelper:Invalid string format!"); + } + va_end(argList); +} +//bool TransportTest(LPCTSTR str, const char * server, const char * port, const char *camera,const char * domain, const char * user, const char * password, +// int width, int height, bool secure, int framerate, int loglevel, int frameqsize,const char * token, const char * sessionID) +//{ +// USES_CONVERSION; +// bool rc=true; +// Write(1,T2A(str)); +// TransportUrl *url=new TransportUrl(str); +// +// //scheme +// if(0==strcmp(url->get_Scheme(),"ciscosource")) +// { +// Write(1,"\tPASS: Scheme"); +// }else{ +// Write(2,"\tFAIL: Scheme = %s",url->get_Scheme()); +// rc=false; +// } +// +// //domain +// if(0==strcmp(url->get_Domain(),domain)) +// { +// Write(1,"\tPASS: Domain"); +// }else{ +// Write(2,"\tFAIL: Domain = %s",url->get_Domain()); +// rc=false; +// } +// +// //server +// if(0==strcmp(url->get_Server(),server)) +// { +// Write(1,"\tPASS: Server"); +// }else{ +// Write(2,"\tFAIL: Server = %s",url->get_Server()); +// rc=false; +// } +// +// //port +// if(0==strcmp(url->get_Port(),port)) +// { +// Write(1,"\tPASS: Port"); +// }else{ +// Write(2,"\tFAIL: Port = %s",url->get_Port()); +// rc=false; +// } +// //camera +// if(0==strcmp(url->get_Camera(),camera)) +// { +// Write(1,"\tPASS: Camera"); +// }else{ +// Write(2,"\tFAIL: Camera = %s",url->get_Camera()); +// rc=false; +// } +// //user +// if(0==strcmp(url->get_UserName(),user)) +// { +// Write(1,"\tPASS: User"); +// }else{ +// Write(2,"\tFAIL: User = %s",url->get_UserName()); +// rc=false; +// } +// //password +// if(0==strcmp(url->get_Password(),password)) +// { +// Write(1,"\tPASS: Password"); +// }else{ +// Write(2,"\tFAIL: Password = %s",url->get_Password()); +// rc=false; +// } +// //width +// if(url->get_Width()==width) +// { +// Write(1,"\tPASS: Width"); +// }else{ +// Write(2,"\tFAIL: Width = %d",url->get_Width()); +// rc=false; +// } +// //Height +// if(url->get_Height()==height) +// { +// Write(1,"\tPASS: Height"); +// }else{ +// Write(2,"\tFAIL: Height = %d",url->get_Height()); +// rc=false; +// } +// +// //secure +// if(url->get_IsSecure()==secure) +// { +// Write(1,"\tPASS: Secure"); +// if(port <0) +// { +// if(url->get_IsSecure()) +// { +// if(0==strcmp(url->get_Port(),"443")) +// { +// Write(1,"\tPASS: Is Secure Port"); +// }else{ +// Write(2,"\tFAIL: Is Secure Port = %s <> 443",url->get_Port()); +// rc=false; +// } +// }else{ +// if(0==strcmp(url->get_Port(),"80")) +// { +// Write(1,"\tPASS: Is Secure Port"); +// }else{ +// Write(2,"\tFAIL: Is Secure Port = %s <> 80",url->get_Port()); +// rc=false; +// } +// } +// } +// }else{ +// Write(2,"\tFAIL: Secure = %s",url->get_IsSecure()?"True":"False"); +// rc=false; +// } +// +// //Framerate +// if(url->get_Framerate()==framerate) +// { +// Write(1,"\tPASS: Frame Rate"); +// }else{ +// Write(2,"\tFAIL: Frame Rate = %d",url->get_Framerate()); +// rc=false; +// } +// +// //Frame Queue Size +// if(url->get_FrameQueueSize()==frameqsize) +// { +// Write(1,"\tPASS: FrameQueueSize"); +// }else{ +// Write(2,"\tFAIL: FrameQueueSize = %d",url->get_FrameQueueSize()); +// rc=false; +// } +// //Log Level +// if(g_logLevel==loglevel) +// { +// Write(1,"\tPASS: Log Level"); +// }else{ +// Write(2,"\tFAIL: Log Level = %d",g_logLevel); +// rc=false; +// } +// +// //Check the RTSP value +// if(0==strcmp(token,"") ) +// { +// if(0 ==strcmp(sessionID,"")) +// { +// if(!url->hasUrl() && 0==strcmp(url->get_RtspUrl(),"")) +// { +// //ok now set an address as see if we pass +// url->set_Token("device_host","my_fake_token"); +// std::string rtsp="rtsp://device_host/" + std::string(camera) + "?token=my_fake_token"; +// if(url->hasUrl() && 0==strcmp(url->get_RtspUrl(),rtsp.c_str())) +// { +// Write(1,"\tPASS: RTSP Address"); +// }else{ +// Write(2,"\tFAIL: RTSP Address Token = %s",url->get_RtspUrl()); +// } +// }else{ +// Write(2,"\tFAIL: RTSP Address URL = %s",url->get_RtspUrl()); +// rc=false; +// } +// } +// }else{ +// std::string rtsp="rtsp://"+std::string(server)+"/" + std::string(camera) + "?token="+std::string(token); +// if(url->hasUrl() && 0==strcmp(url->get_RtspUrl(),rtsp.c_str())) +// { +// Write(1,"\tPASS: RTSP Address"); +// }else{ +// Write(2,"\tFAIL: RTSP Address = %s",url->get_RtspUrl()); +// rc=false; +// } +// } +// +// //Check the SessionID value +// if(0!=strcmp(sessionID,"")) +// { +// //rtsp://10.0.0.21/StreamingSetting?version=1.0&sessionID=76535169&action=getRTSPStream&ChannelID=1&ChannelName=Channel1 +// std::string rtsp="rtsp://"+std::string(server)+"/StreamingSetting?version=1.0&sessionID="+std::string(sessionID)+"&action=getRTSPStream&ChannelID=1&ChannelName=Channel1"; +// if(url->hasUrl() && 0==strcmp(url->get_RtspUrl(),rtsp.c_str())) +// { +// Write(1,"\tPASS: RTSP Address Session ID"); +// }else{ +// Write(2,"\tFAIL: RTSP Address Session ID = %s",url->get_RtspUrl()); +// rc=false; +// } +// } +// +// if(0==strcmp(url->get_Scheme(),"ciscosource")) +// { +// Write(1,"\tPASS: Log Level"); +// }else{ +// Write(2,"\tFAIL: Log Level = %d",g_logLevel); +// rc=false; +// } +// return rc; +//} +void writeTestHeader(char * header) +{ + Write(0,"--------------------------------------"); + Write(0,header); + Write(0,"--------------------------------------"); +} +//bool TransportTest1() +//{ +// LPCTSTR url=L"ciscosource://ec2-54-219-61-94.us-west-1.compute.amazonaws.com?camera=98620a82-327f-4094-8537-385b21eaee04&User=admin&Password=Admin123&Width=720&Height=480"; +// const char * server="ec2-54-219-61-94.us-west-1.compute.amazonaws.com"; +// const char * port="443"; +// const char * camera="98620a82-327f-4094-8537-385b21eaee04"; +// const char * domain=""; +// const char * user="admin"; +// const char * password="Admin123"; +// int width=720; +// int height=480; +// bool secure =true; +// int framerate=12; +// int loglevel=0; +// int frameqsize=200; +// const char * token=""; +// const char * sessionID=""; +// +// writeTestHeader("Transport Test #1 - Typical"); +// return TransportTest(url, server, port,camera, domain,user,password,width, height,secure,framerate,loglevel,frameqsize,token,sessionID); +//} +//bool TransportTest2() +//{ +// LPCTSTR url=L"ciscosource://ec2-54-219-61-94.us-west-1.compute.amazonaws.com:443?camera=98620a82-327f-4094-8537-385b21eaee04&Domain=HorthSystems&User=David&Password=Horth&Width=720&Height=480&Secure=True&Framerate=30&Loglevel=5&FrameQueueSize=100"; +// const char * server="ec2-54-219-61-94.us-west-1.compute.amazonaws.com"; +// const char * port="443"; +// const char * camera="98620a82-327f-4094-8537-385b21eaee04"; +// const char * domain="HorthSystems"; +// const char * user="David"; +// const char * password="Horth"; +// int width=720; +// int height=480; +// bool secure =true; +// int framerate=30; +// int loglevel=5; +// int frameqsize=100; +// const char * token=""; +// const char * sessionID=""; +// +// writeTestHeader("Transport Test #2 - Full"); +// return TransportTest(url, server, port,camera, domain,user,password,width, height,secure,framerate,loglevel,frameqsize,token,sessionID); +//} +//bool TransportTest3() +//{ +// LPCTSTR url=L"ciscosource://ec2-54-219-61-94.us-west-1.compute.amazonaws.com:?camera=98620a82-327f-4094-8537-385b21eaee04&Token=98620a82-327f-4094-8537-385b21eaee04^LVEAMO^50^0^0^1382283819^e57cd1b438d6dba6356b5b0952c21f2bf56c5d27&Width=720&Height=480"; +// const char * server="ec2-54-219-61-94.us-west-1.compute.amazonaws.com"; +// const char * port="443"; +// const char * camera="98620a82-327f-4094-8537-385b21eaee04"; +// const char * domain=""; +// const char * user=""; +// const char * password=""; +// int width=720; +// int height=480; +// bool secure =true; +// int framerate=12; +// int loglevel=0; +// int frameqsize=200; +// const char * token="98620a82-327f-4094-8537-385b21eaee04^LVEAMO^50^0^0^1382283819^e57cd1b438d6dba6356b5b0952c21f2bf56c5d27"; +// const char * sessionID=""; +// +// writeTestHeader("Transport Test #3 - User supplied token"); +// return TransportTest(url, server, port,camera, domain,user,password,width, height,secure,framerate,loglevel,frameqsize,token, sessionID); +//} +//bool TransportTest4() +//{ +// LPCTSTR url=L"ciscosource://10.0.0.22?SessionId=70762529&Width=1920&Height=1024"; +// const char * server="10.0.0.22"; +// const char * port="443"; +// const char * camera=""; +// const char * domain=""; +// const char * user=""; +// const char * password=""; +// int width=1920; +// int height=1024; +// bool secure =true; +// int framerate=12; +// int loglevel=0; +// int frameqsize=200; +// const char * token=""; +// const char * sessionID="70762529"; +// +// writeTestHeader("Transport Test #4 - Direct Camera Access"); +// return TransportTest(url, server, port,camera, domain,user,password,width, height,secure,framerate,loglevel,frameqsize,token, sessionID); +//} + +bool MediaQueueTest1() +{ + bool rc=true; + writeTestHeader("Media Queue Test #1 - Positive"); +#define QSize 200 + CMediaQueue *q=new CMediaQueue(QSize); + if(q->get_Size()==QSize) + { + Write(1,"PASS: Queue size ok"); + }else{ + Write(2,"FAIL: Queue size is wrong"); + rc=false; + } + + if(q->get_Count()==0) + { + Write(1,"PASS: Queue count ok"); + }else{ + Write(2,"FAIL: Queue count is wrong"); + rc=false; + } + + if(q->get_isEmpty()) + { + Write(1,"PASS: Queue isempty ok"); + }else{ + Write(2,"FAIL: Queue isempty is wrong"); + rc=false; + } + + //add 50 items to the q + for(int i=0;i<50;i++) + { + q->put(new FrameInfo()); + } + + if(q->get_Count()==50) + { + Write(1,"PASS: Queue count ok"); + }else{ + Write(2,"FAIL: Queue count is wrong"); + rc=false; + } + + if(!q->get_isEmpty()) + { + Write(1,"PASS: Queue isempty ok"); + }else{ + Write(2,"FAIL: Queue isempty is wrong"); + rc=false; + } + + //now get all 50 + for(int i=0;i<50;i++) + { + FrameInfo* fi=q->get(); + if(fi==NULL) + { + Write(2,"FAIL: Retrieving frame %d",i); + } + } + + if(q->get_Count()==0) + { + Write(1,"PASS: Queue count ok"); + }else{ + Write(2,"FAIL: Queue count is wrong"); + rc=false; + } + + if(q->get_isEmpty()) + { + Write(1,"PASS: Queue isempty ok"); + }else{ + Write(2,"FAIL: Queue isempty is wrong"); + rc=false; + } + return rc; +} +bool MediaQueueTest2() +{ + bool rc=true; + writeTestHeader("Media Queue Test #1 - Negative"); +#define QSize 100 + CMediaQueue *q=new CMediaQueue(QSize); + if(q->get_Size()==QSize) + { + Write(1,"PASS: Queue size ok"); + }else{ + Write(2,"FAIL: Queue size is wrong"); + rc=false; + } + + //add 50 items to the q + for(int i=0;i<10;i++) + { + q->put(new FrameInfo()); + } + + //now try to get 11 + for(int i=0;i<10;i++) + { + FrameInfo* fi=q->get(); + if(fi==NULL) + { + Write(2,"FAIL: Retrieving frame %d",i); + } + } + + //no get frame 11 + int startTime, endTime, totalTime; + startTime = time(NULL); + FrameInfo* fi=q->get(); + endTime = time(NULL); + if(fi==NULL) + { + Write(1,"PASS: Retrieving frame 11 is NULL"); + }else{ + Write(2,"FAIL: Queue should be empty"); + } + + totalTime = endTime - startTime; + if(totalTime>1&&totalTime<3) + { + Write(1,"PASS: Get empty frame timing ok"); + }else{ + Write(2,"FAIL: Bad timing for empty frame total time=%d",totalTime); + } + + if(q->get_Count()==0) + { + Write(1,"PASS: Queue count ok"); + }else{ + Write(2,"FAIL: Queue count is wrong"); + rc=false; + } + return rc; +} + +//use this address for our test (MUST BE CHANGED each test) +#define filename "rtsp://10.0.0.22/StreamingSetting?version=1.0&sessionID=95652231&action=getRTSPStream&ChannelID=1&ChannelName=Channel1" + +bool VideoDecoder1() +{ + bool rc=true; + writeTestHeader("Video Decoder Test #1 - Positive"); + return rc; +} + +bool RtspTest1() +{ + writeTestHeader("RTSP Test #1 - open stream"); + CstreamMedia * _streamMedia=new CstreamMedia(); + int ret = _streamMedia->rtspClientOpenStream((const char *)filename); + if(ret!=0) + { + Write(2,"FAIL: Open Stream ret=%d",ret); + } + Write(1,"PASS: Open Stream ret=%d",ret); + _streamMedia->rtspClientCloseStream(); + return ret==0; +} +bool RtspTest2() +{ + writeTestHeader("RTSP Test #2 - Get Media Info"); + MediaInfo videoMediaInfo; + + CstreamMedia * _streamMedia=new CstreamMedia(); + int ret = _streamMedia->rtspClientOpenStream((const char *)filename); + if(ret!=0) + { + Write(2,"FAIL: Open Stream ret=%d",ret); + return false; + } + + Write(1,"PASS: Open Stream ret=%d",ret); + ret=_streamMedia->rtspClinetGetMediaInfo(CODEC_TYPE_VIDEO, videoMediaInfo); + bool rc=(0==strcmp("H264",videoMediaInfo.codecName)); + if(ret!=0 || !rc) + { + Write(2,"FAIL: Get Media Info ret=%d",ret); + return false; + } + Write(1,"PASS: Get Media Info"); + + _streamMedia->rtspClientCloseStream(); + return ret==0; +} +bool RtspTest3() +{ + bool rc=true; + writeTestHeader("RTSP Test #2 - Play Stream"); + MediaInfo videoMediaInfo; + + CstreamMedia * _streamMedia=new CstreamMedia(); + int ret = _streamMedia->rtspClientOpenStream((const char *)filename); + if(ret!=0) + { + Write(2,"FAIL: Open Stream ret=%d",ret); + } + Write(1,"PASS: Open Stream ret=%d",ret); + ret=_streamMedia->rtspClientPlayStream(filename); + if(ret==0) + { + Write(1,"PASS: Play Stream ret=%d",ret); + }else{ + Write(2,"FAIL: Play Stream ret=%d",ret); + rc=false; + } + + //do something here while we wait for frames to arrive + int loop = 100; + while(loop>0) + { + ::Sleep(250); + loop--; + } + int c=_streamMedia->GetQueueSize(); + if(c==0) + { + Write(2,"FAIL: We should have gotten at least one frame by now"); + rc=false; + }else{ + Write(1,"PASS: We have frames=%d",c); + } + _streamMedia->rtspClientCloseStream(); + return rc; +} + +#define logfile "C:\\ProgramData\\CiscoFilter\\Logs\\CiscoFilterLog.log" +bool LogFileTest1() +{ + bool rc=true; + writeTestHeader("LogFileTest #1 - Log file test"); + + //delete old log file + if(FileExists(logfile)) + std::remove(logfile); + + if(0==countLines(logfile)) + { + Write(1,"PASS: LogFileTest"); + }else{ + Write(2, "File does not exist"); + } + + g_logLevel=0; + TRACE_CRITICAL("test"); + + g_logLevel=1; + TRACE_CRITICAL("test"); + + return rc; +} + +bool EnvTest1() +{ + writeTestHeader("CiscoUsageEnvironment Test #1 - write to trace helper"); + BasicTaskScheduler* scheduler = BasicTaskScheduler::createNew(); + if (scheduler == NULL) + { + Write(2, "BasicTaskScheduler fail"); + return false; + } + CMyUsageEnvironment* env = CMyUsageEnvironment::createNew(*scheduler); + if (env == NULL) + { + Write(2, "BasicUsageEnvironment fail"); + return false; + } + *env << "test"; + if(0 < env->get_size()) + { + Write(1,"PASS: MyUsageEnvironment"); + }else{ + Write(2, "stream should not be empty"); + } + *env << 5 << 5.3 ; + if(0 < env->get_size()) + { + Write(1,"PASS: MyUsageEnvironment"); + }else{ + Write(2, "stream should be empty"); + } + *env << "\n"; + if(0==env->get_size()) + { + Write(1,"PASS: MyUsageEnvironment"); + }else{ + Write(2, "stream should be empty"); + } + + return true; +} + + +int _tmain(int argc, _TCHAR* argv[]) +{ + + logFile << "Writing this to a file.\n"; + logFile.open ("UnitTest.log"); + Write(0,"Starting unit tests"); + //Write(0,"\n\nTransport URL Tests"); + //bool tt1=TransportTest1(); + //bool tt2=TransportTest2(); + //bool tt3=TransportTest3(); + //bool tt4=TransportTest4(); + + //these depend on a valid url + Write(0,"\n\nMedia Queue Tests"); + bool mq1=MediaQueueTest1(); + bool mq2=MediaQueueTest2(); + + Write(0,"\n\Log file test"); + bool lft1=LogFileTest1(); + + Write(0,"\n\nMyUsageEnvirnoment Test"); + bool cue1=EnvTest1(); + + Write(0,"\n\nVideo Decoder Tests"); + bool vc1=VideoDecoder1(); + + //these depend on a valid url + Write(0,"\n\nRTSP Tests"); + bool rtsp1=RtspTest1(); + if(!rtsp1) + { + Write(2,"Open failed bailing out of all RTSP tests"); + goto wrapup; + } + bool rtsp2=RtspTest2(); + bool rtsp3=RtspTest3(); + + +wrapup: + logFile.close(); + + Write(0,"\n\n"); + if(rtsp1&& rtsp2&&rtsp3) + { + Write(1,"<+++++++++++++++++++++++++++++++++++++++>"); + Write(1,"< All Test Pasesed! >"); + Write(1,"<+++++++++++++++++++++++++++++++++++++++>"); + }else{ + Write(2,"|---------------------------------------|"); + Write(2,"| Unit tests failed |"); + Write(2,"|---------------------------------------|"); + } + + Write(0,"\n\n"); + std::cout << "Press Enter to exit" < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LiveProxy/UnitTest/LiveProxyUnitTest.vcxproj b/LiveProxy/UnitTest/LiveProxyUnitTest.vcxproj new file mode 100644 index 0000000..3371e59 --- /dev/null +++ b/LiveProxy/UnitTest/LiveProxyUnitTest.vcxproj @@ -0,0 +1,142 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8F2BEEDF-184C-4A25-843D-79FF0029A866} + PushSourceUnitTest + Win32Proj + + + + Application + v100 + Unicode + true + true + + + Application + v100 + true + Unicode + + + + + + + + + + + + + <_ProjectFileVersion>11.0.60610.1 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)Cisco Source Filter\Common\DirectShow;$(SolutionDir)live\BasicUsageEnvironment\include;$(SolutionDir)live\UsageEnvironment\include;$(SolutionDir)live\groupsock\include;$(SolutionDir)live\liveMedia\include;$(SolutionDir)ffmpeg\include\;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + $(SolutionDir)Cisco Source Filter\Common\DirectShow;$(SolutionDir)live\BasicUsageEnvironment\include;$(SolutionDir)live\UsageEnvironment\include;$(SolutionDir)live\groupsock\include;$(SolutionDir)live\liveMedia\include;$(SolutionDir)ffmpeg\include\;$(IncludePath) + $(SolutionDir)live\lib;$(SolutionDir)ffmpeg\lib;$(LibraryPath) + + + + Disabled + WIN32;CONSOLE;NDEBUG;_USRDLL;_LIB;W7;PX;A6;T7;DECODE_INTEL;_CRT_SECURE_NO_DEPRECATE;PUSHSOURCE_EXPORTS;%(PreprocessorDefinitions) + Use + Level3 + + + true + Console + NotSet + $(SolutionDir)Cisco Source Filter\Common\libs;$(SolutionDir)ffmpeg\lib;$(SolutionDir)live\lib;%(AdditionalLibraryDirectories) + %(AdditionalDependencies) + false + + + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + Use + Level3 + ProgramDatabase + + + true + Console + true + true + MachineX86 + $(SolutionDir)Cisco Source Filter\Common\libs;$(SolutionDir)ffmpeg\lib;$(SolutionDir)live\lib;%(AdditionalLibraryDirectories) + %(AdditionalDependencies) + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + {fea627f9-a115-4efb-b489-e28991bc8458} + + + + + + + ..\..\VSM7.0.1SDK\WebServicesDLLs\VSOMWebService.dll + + + + + + \ No newline at end of file diff --git a/LiveProxy/UnitTest/ReadMe.txt b/LiveProxy/UnitTest/ReadMe.txt new file mode 100644 index 0000000..e1849f0 --- /dev/null +++ b/LiveProxy/UnitTest/ReadMe.txt @@ -0,0 +1,33 @@ +======================================================================== + CONSOLE APPLICATION : PushSourceUnitTest Project Overview +======================================================================== + +AppWizard has created this PushSourceUnitTest application for you. + +This file contains a summary of what you will find in each of the files that +make up your PushSourceUnitTest application. + + +PushSourceUnitTest.vcproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +PushSourceUnitTest.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named PushSourceUnitTest.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/LiveProxy/UnitTest/stdafx.cpp b/LiveProxy/UnitTest/stdafx.cpp new file mode 100644 index 0000000..e4feef0 --- /dev/null +++ b/LiveProxy/UnitTest/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// PushSourceUnitTest.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/LiveProxy/UnitTest/stdafx.h b/LiveProxy/UnitTest/stdafx.h new file mode 100644 index 0000000..f36f9d4 --- /dev/null +++ b/LiveProxy/UnitTest/stdafx.h @@ -0,0 +1,28 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include +#include +#include +#include // std::toupper +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + +// TODO: reference additional headers your program requires here diff --git a/LiveProxy/UnitTest/targetver.h b/LiveProxy/UnitTest/targetver.h new file mode 100644 index 0000000..6fe8eb7 --- /dev/null +++ b/LiveProxy/UnitTest/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + diff --git a/LiveProxy/VideoDecoder.cpp b/LiveProxy/VideoDecoder.cpp new file mode 100644 index 0000000..78e6510 --- /dev/null +++ b/LiveProxy/VideoDecoder.cpp @@ -0,0 +1,209 @@ +#include "stdafx.h" +#include "VideoDecoder.h" +#include "MediaQueue.h" +#include "trace.h" +#pragma unmanaged + +/** Create a FrameInfo object. +* inline helper to create FrameInfo object +\param frame_size Default frame size +*/ +inline FrameInfo* FrameNew(int frame_size = 4096) +{ + FrameInfo* frame = (FrameInfo*)malloc(sizeof(FrameInfo)+frame_size); + if (frame == NULL) + return(NULL); + frame->pdata = (char *)frame + sizeof(FrameInfo);//new char[frame_size]; + frame->frameHead.FrameLen = frame_size; + return(frame); +} + +/** VideoDecoder Contrstructor. +* Allocates a AVFrame that will be reused by the decoder. Always +* initializes the ffmpeg decoding library, to accept a H264 frame +*/ +CVideoDecoder::CVideoDecoder(char const* format) +{ + try + { + TRACE_INFO("Register codecs"); + m_frame = avcodec_alloc_frame(); + avcodec_register_all(); + + TRACE_INFO("Find codec"); + if (!strcmp(format,"H264")) + { + m_codec = avcodec_find_decoder(CODEC_ID_H264); + }else{ + if (!strcmp(format,"MP4V")) + { + m_codec = avcodec_find_decoder(CODEC_ID_MPEG4); + } + } + if ( m_codec != NULL) + { + TRACE_INFO("Decoder found"); + }else{ + TRACE_ERROR("Codec decoder not found"); + } + + TRACE_INFO("Allocate code context"); + m_codecContext = avcodec_alloc_context3(m_codec); + m_codecContext->flags = 0; + + TRACE_INFO("open codec"); + int ret=avcodec_open2(m_codecContext, m_codec, NULL); + if (ret < 0) + { + TRACE_ERROR("Error opening codec ret=%d",ret); + }else{ + TRACE_INFO("AV Codec found and opened"); + } + + m_codecContext->flags2 |= CODEC_FLAG2_CHUNKS; + + } + catch (...) + { + TRACE_WARN("Ignoring Exception"); + } +} + +/** VideoDecoder descructor. + Cleans up the ffmpeg environment and + frees the AVFrame +*/ + +CVideoDecoder::~CVideoDecoder() +{ + TRACE_INFO("Cleaning up video sing"); + if (m_frame!=NULL) + { + avcodec_close(m_codecContext); + av_free(m_frame); + } + m_frame = NULL; + + if (m_codecContext!=NULL) + av_free(m_codecContext); + m_codecContext = NULL; +} + +/** Decoder. + The supplied buffer should contain an H264 video frame, then DecodeFrame + will pass the buffer to the avcode_decode_video2 method. Once decoded we then + use the get_picture command to convert the frame to RGB24. The RGB24 buffer is + then used to create a FrameInfo object which is placed on our video queue. + + \param pBuffer Memory buffer holding an H264 frame + \param size Size of the buffer +*/ +FrameInfo* CVideoDecoder::DecodeFrame(unsigned char *pBuffer, int size) +{ + FrameInfo *p_block=NULL; + uint8_t startCode4[] = {0x00, 0x00, 0x00, 0x01}; + int got_frame = 0; + AVPacket packet; + + //Initialize optional fields of a packet with default values. + av_init_packet(&packet); + + //set the buffer and the size + packet.data = pBuffer; + packet.size = size; + + while (packet.size > sizeof(startCode4)) + { + //Decode the video frame of size avpkt->size from avpkt->data into picture. + int len = avcodec_decode_video2(m_codecContext, m_frame, &got_frame, &packet); + if(len<0) + { + TRACE_ERROR("Failed to decode video len=%d",len); + break; + } + + //sometime we dont get the whole frame, so move + //forward and try again + if ( !got_frame ) + { + packet.size -= len; + packet.data += len; + continue; + } + + //allocate a working frame to store our rgb image + AVFrame * rgb = avcodec_alloc_frame(); + if(rgb==NULL) + { + TRACE_ERROR("Failed to allocate new av frame"); + return NULL; + } + + //Allocate and return an SwsContext. + struct SwsContext * scale_ctx = sws_getContext(m_codecContext->width, + m_codecContext->height, + m_codecContext->pix_fmt, + m_codecContext->width, + m_codecContext->height, + PIX_FMT_BGR24, + SWS_BICUBIC, + NULL, + NULL, + NULL); + if (scale_ctx == NULL) + { + TRACE_ERROR("Failed to get context"); + continue; + } + + //Calculate the size in bytes that a picture of the given width and height would occupy if stored in the given picture format. + int numBytes = avpicture_get_size(PIX_FMT_RGB24, + m_codecContext->width, + m_codecContext->height); + + try{ + //create one of our FrameInfo objects + p_block = FrameNew(numBytes); + if(p_block==NULL){ + + //cleanup the working buffer + av_free(rgb); + sws_freeContext(scale_ctx); + scale_ctx=NULL; + return NULL; + } + + //Fill our frame buffer with the rgb image + avpicture_fill((AVPicture*)rgb, + (uint8_t*)p_block->pdata, + PIX_FMT_RGB24, + m_codecContext->width, + m_codecContext->height); + + //Scale the image slice in srcSlice and put the resulting scaled slice in the image in dst. + sws_scale(scale_ctx, + m_frame->data, + m_frame->linesize, + 0, + m_codecContext->height, + rgb->data, + rgb->linesize); + + //set the frame header to indicate rgb24 + p_block->frameHead.FrameType = (long)(PIX_FMT_RGB24); + p_block->frameHead.TimeStamp = 0; + } + catch(...) + { + TRACE_ERROR("EXCEPTION: in afterGettingFrame1 "); + } + + //cleanup the working buffer + av_free(rgb); + sws_freeContext(scale_ctx); + + //we got our frame no its time to move on + break; + } + return p_block; +} diff --git a/LiveProxy/VideoDecoder.h b/LiveProxy/VideoDecoder.h new file mode 100644 index 0000000..c760f2f --- /dev/null +++ b/LiveProxy/VideoDecoder.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include +#include +#include "MediaSink.hh" +#include "MediaQueue.h" + +extern "C" +{ +#ifndef __STDC_CONSTANT_MACROS +# define __STDC_CONSTANT_MACROS +#endif +#include +#include +#include "libswscale/swscale.h" +} +class CVideoDecoder +{ +public: + CVideoDecoder(char const* format); + ~CVideoDecoder(); + FrameInfo* DecodeFrame(unsigned char * pBuffer, int size); + +private: + AVCodec *m_codec; + AVCodecContext *m_codecContext; + AVFrame *m_frame; + +}; + diff --git a/LiveProxy/live.cpp b/LiveProxy/live.cpp new file mode 100644 index 0000000..6fe732f --- /dev/null +++ b/LiveProxy/live.cpp @@ -0,0 +1,1047 @@ +#include "stdafx.h" +#include "MyRTSPClient.h" +#include "MyUsageEnvironment.h" +#include "live.h" + + +#define keepAliveTimer 60 * 1000000 //how many micro seconds between each keep alive + +static unsigned rtspClientCount = 0; // Counts how many streams (i.e., "RTSPClient"s) are currently in use. + +HANDLE hDataThreadReady; + +/** +opens the RTSPClient stream +*/ +void openURL(UsageEnvironment& env, char const* rtspURL); + +void openURL(UsageEnvironment& env, char const* rtspURL); +// RTSP 'response handlers': +void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString); +void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString); +void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString); + +void onScheduledDelayedTask(void* clientData); + +// Other event handler functions: +void subsessionAfterPlaying(void* clientData); // called when a stream's subsession (e.g., audio or video substream) ends +void subsessionByeHandler(void* clientData); // called when a RTCP "BYE" is received for a subsession +void streamTimerHandler(void* clientData); +static void StreamClose(void *p_private); +//void StreamClose(void *p_private); + +// called at the end of a stream's expected duration (if the stream has not already signaled its end using a RTCP "BYE") + +// Used to iterate through each stream's 'subsessions', setting up each one: +void setupNextSubsession(RTSPClient* rtspClient); + +// Used to shut down and close a stream (including its "RTSPClient" object): +void shutdownStream(RTSPClient* rtspClient, int exitCode = 1); + +// A function that outputs a string that identifies each stream (for debugging output). Modify this if you wish: +UsageEnvironment& operator<<(UsageEnvironment& env, const RTSPClient& rtspClient) { + return env << "[URL:\"" << rtspClient.url() << "\"]: "; +} + +// A function that outputs a string that identifies each subsession (for debugging output). Modify this if you wish: +UsageEnvironment& operator<<(UsageEnvironment& env, const MediaSubsession& subsession) { + return env << subsession.mediumName() << "/" << subsession.codecName(); +} + + + +/*char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/ +static int b64_decode( char *dest, char *src ) +{ + const char *dest_start = dest; + int i_level; + int last = 0; + int b64[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ + }; + + for( i_level = 0; *src != '\0'; src++ ) + { + int c; + + c = b64[(unsigned int)*src]; + if( c == -1 ) + { + continue; + } + + switch( i_level ) + { + case 0: + i_level++; + break; + case 1: + *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 ); + i_level++; + break; + case 2: + *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f ); + i_level++; + break; + case 3: + *dest++ = ( ( last &0x03 ) << 6 ) | c; + i_level = 0; + } + last = c; + } + + *dest = '\0'; + + return dest - dest_start; +} + +static unsigned char* parseH264ConfigStr( char const* configStr, + unsigned int& configSize ) +{ + char *dup, *psz; + int i, i_records = 1; + + if( configSize ) + configSize = 0; + + if( configStr == NULL || *configStr == '\0' ) + return NULL; + + psz = dup = _strdup( configStr ); + + /* Count the number of comma's */ + for( psz = dup; *psz != '\0'; ++psz ) + { + if( *psz == ',') + { + ++i_records; + *psz = '\0'; + } + } + + unsigned char *cfg = new unsigned char[5 * strlen(dup)]; + psz = dup; + for( i = 0; i < i_records; i++ ) + { + cfg[configSize++] = 0x00; + cfg[configSize++] = 0x00; + cfg[configSize++] = 0x00; + cfg[configSize++] = 0x01; + + configSize += b64_decode( (char*)&cfg[configSize], psz ); + psz += strlen(psz)+1; + } + + //free( dup ); + return cfg; +} + + +/*------------------------------------------------------------*/ +/*------------------------------------------------------------*/ +static void TaskInterrupt( void *p_private ) + { + char *event = (char*)p_private; + + /* Avoid lock */ + *event = 0xff; + TRACE_ERROR( "TaskInterrupt"); + } + +DWORD WINAPI rtspRecvDataThread( LPVOID lpParam ) +{ + TRACE_INFO("Starting receive data thread"); + CstreamMedia* rtspClient = (CstreamMedia*)lpParam; + char* event = &(rtspClient->event); + + // Begin by setting up our usage environment: + TaskScheduler* scheduler = BasicTaskScheduler::createNew(); + UsageEnvironment* env = CMyUsageEnvironment::createNew(*scheduler); + + // Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish + // to receive (even if more than stream uses the same "rtsp://" URL). + MyRTSPClient* client = MyRTSPClient::createNew(*env, rtspClient->get_Url(), rtspClient->get_Format()); + + if (client == NULL) { + TRACE_ERROR("Failed to create a RTSP client for URL %s, Msg %s", rtspClient->get_Url(), env->getResultMsg()); + return -1; + } + + client->set_StreamTrack(rtspClient->stream[0]); + ++rtspClientCount; + + rtspClient->scheduler=scheduler; + rtspClient->env=env; + rtspClient->rtsp=client; + + + // Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream. + // Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response. + // Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop: + TRACE_INFO("Sending describe command"); + client->sendDescribeCommand(continueAfterDESCRIBE); + + // All subsequent activity takes place within the event loop: + TRACE_INFO("Starting the event loop"); + env->taskScheduler().doEventLoop(); // does not return + + return 0; // We never actually get to this line; this is only to prevent a possible compiler warning + +} + +/** +Begin by creating a "RTSPClient" object. Note that there is a +separate "RTSPClient" object for each stream that we wish +to receive (even if more than stream uses the same "rtsp://" URL). +not that we need that in this filter +*/ +void openURL(UsageEnvironment& env, char const* rtspURL) { + // Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish + // to receive (even if more than stream uses the same "rtsp://" URL). + TRACE_INFO("open URL %s ",rtspURL); + MyRTSPClient* client = MyRTSPClient::createNew(env, rtspURL, "H264"); + if (client == NULL) { + TRACE_ERROR("Failed to create a RTSP client msg: %s for URL %s ",env.getResultMsg(), rtspURL); + env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg() << "\n"; + return; + } + + ++rtspClientCount; + + // Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream. + // Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response. + // Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop: + client->sendDescribeCommand(continueAfterDESCRIBE); +} + + +/*------------------------------------------------------------*/ +// Implementation of the RTSP 'response handlers': +/*------------------------------------------------------------*/ +/** +Response to the sendDescribeCommand +*/ +void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) { + TRACE_INFO("Continue after describe Code=%d Result=%s",resultCode,resultString); + + UsageEnvironment& env = rtspClient->envir(); // alias + + StreamClientState& scs = ((MyRTSPClient*)rtspClient)->scs; // alias + + if (resultCode != 0) { + TRACE_ERROR( "Failed to get a SDP description:"); + goto cleanup; + } + + char* sdpDescription = resultString; + TRACE_INFO("Got a SDP description: %s", sdpDescription); + + // Create a media session object from this SDP description: + scs.session = MediaSession::createNew(env, sdpDescription); + delete[] sdpDescription; // because we don't need it anymore + if (scs.session == NULL) { + TRACE_ERROR( "Failed to create a MediaSession object from the SDP description: {0}",env.getResultMsg()); + goto cleanup; + } else if (!scs.session->hasSubsessions()) { + TRACE_ERROR( "This session has no media subsessions (i.e., no m=lines)"); + goto cleanup; + } + + // Then, create and set up our data source objects for the session. We do this by iterating over the session's 'subsessions', + // calling "MediaSubsession::initiate()", and then sending a RTSP "SETUP" command, on each one. + // (Each 'subsession' will have its own data source.) + scs.iter = new MediaSubsessionIterator(*scs.session); + setupNextSubsession(rtspClient); + return; + + // An unrecoverable error occurred with this stream. +cleanup: + shutdownStream(rtspClient); + +} + +/** +Called after the DESCRIBE response +*/ +void setupNextSubsession(RTSPClient* rtspClient) +{ + TRACE_INFO("Setup next session"); + UsageEnvironment& env = rtspClient->envir(); // alias + StreamClientState& scs = ((MyRTSPClient*)rtspClient)->scs; // alias + + scs.subsession = scs.iter->next(); + + if (scs.subsession != NULL) + { + if (!scs.subsession->initiate()) + { + TRACE_ERROR( "Failed to initiate the subsession: &s",env.getResultMsg()); + setupNextSubsession(rtspClient); // give up on this subsession; go to the next one + } else { + TRACE_INFO("Initiated the subsession. Ports %d-%d", scs.subsession->clientPortNum(), scs.subsession->clientPortNum()+1); + // Continue setting up this subsession, by sending a RTSP "SETUP" command: + rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP); + } + return; + } + + // We've finished setting up all of the subsessions. Now, send a RTSP "PLAY" command to start the streaming: + scs.duration = scs.session->playEndTime() - scs.session->playStartTime(); + + TRACE_INFO("Sending Play Command"); + rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY); +} + +/** +Response to the sendSetupCommand +*/ +void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString) { + TRACE_INFO("Continue after setup Code=%d Result=%s",resultCode, resultString); + do { + UsageEnvironment& env = rtspClient->envir(); // alias + StreamClientState& scs = ((MyRTSPClient*)rtspClient)->scs; // alias + + if (resultCode != 0) { + TRACE_ERROR( "Failed to set up the subsession: %s", env.getResultMsg()); + break; + } + + TRACE_INFO( "Set up the subsession (client ports %d - %d)",scs.subsession->clientPortNum(), scs.subsession->clientPortNum()); + + // Having successfully setup the subsession, create a data sink for it, and call "startPlaying()" on it. + // (This will prepare the data sink to receive data; the actual flow of data from the client won't start happening until later, + // after we've sent a RTSP "PLAY" command.) + MyRTSPClient* client=(MyRTSPClient*)rtspClient; + scs.subsession->sink = client->get_sink(); + + if (scs.subsession->sink == NULL) { + TRACE_ERROR( "Failed to set up the subsession: %s", env.getResultMsg()); + break; + } + + TRACE_INFO( "Created a data sink for the subsession"); + scs.subsession->miscPtr = rtspClient; // a hack to let subsession handle functions get the "RTSPClient" from the subsession + scs.subsession->sink->startPlaying(*(scs.subsession->readSource()), + subsessionAfterPlaying, scs.subsession); + // Also set a handler to be called if a RTCP "BYE" arrives for this subsession: + if (scs.subsession->rtcpInstance() != NULL) { + scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, scs.subsession); + } + } while (0); + + // Set up the next subsession, if any: + setupNextSubsession(rtspClient); +} + + +/** +Response to the sendSetupCommand +*/ +void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString) { + TRACE_INFO("Continue after play Code=%d Result=%s",resultCode, resultString); + + UsageEnvironment& env = rtspClient->envir(); // alias + StreamClientState& scs = ((MyRTSPClient*)rtspClient)->scs; // alias + + + if (resultCode != 0) + { + TRACE_ERROR( "Failed to start playing session: %s", resultString); + shutdownStream(rtspClient); + return; + } + + SetEvent(hDataThreadReady); + TRACE_INFO( "Started playing session"); + + //setup our keep alive using a delayed task + TRACE_INFO( "Schedule keep alive task"); + env.taskScheduler().scheduleDelayedTask(keepAliveTimer, onScheduledDelayedTask, rtspClient); +} + +/*------------------------------------------------------------*/ +// Implementation of the other event handlers: +/*------------------------------------------------------------*/ +void subsessionAfterPlaying(void* clientData) +{ + TRACE_INFO("Session after playing"); + MediaSubsession* subsession = (MediaSubsession*)clientData; + RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr); + + // Begin by closing this subsession's stream: + Medium::close(subsession->sink); + subsession->sink = NULL; + + // Next, check whether *all* subsessions' streams have now been closed: + MediaSession& session = subsession->parentSession(); + MediaSubsessionIterator iter(session); + while ((subsession = iter.next()) != NULL) + { + if (subsession->sink != NULL) + return; // this subsession is still active + } + + // All subsessions' streams have now been closed, so shutdown the client: + shutdownStream(rtspClient); +} + +/** +*/ +void subsessionByeHandler(void* clientData) +{ + TRACE_INFO("Session after bye"); + MediaSubsession* subsession = (MediaSubsession*)clientData; + RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr; + UsageEnvironment& env = rtspClient->envir(); // alias + + TRACE_INFO( "Received RTCP \"BYE\" on subsession"); + + // Now act as if the subsession had closed: + subsessionAfterPlaying(subsession); +} + +/** +The stream has timed out and needs to be shutdown +*/ +void streamTimerHandler(void* clientData) +{ + TRACE_INFO("Stream timer handler"); + MyRTSPClient* rtspClient = (MyRTSPClient*)clientData; + StreamClientState& scs = rtspClient->scs; // alias + + scs.streamTimerTask = NULL; + + // Shut down the stream: + shutdownStream(rtspClient); +} + +/** +Cleanup abd shutdown the stream +*/ +void shutdownStream(RTSPClient* rtspClient, int exitCode) +{ + TRACE_INFO("Shutdown stream ExitCode=%d",exitCode); + UsageEnvironment& env = rtspClient->envir(); // alias + StreamClientState& scs = ((MyRTSPClient*)rtspClient)->scs; // alias + + // First, check whether any subsessions have still to be closed: + if (scs.session != NULL) { + Boolean someSubsessionsWereActive = False; + MediaSubsessionIterator iter(*scs.session); + MediaSubsession* subsession; + + while ((subsession = iter.next()) != NULL) { + if (subsession->sink != NULL) { + Medium::close(subsession->sink); + subsession->sink = NULL; + someSubsessionsWereActive = True; + } + } + + if (someSubsessionsWereActive) { + // Send a RTSP "TEARDOWN" command, to tell the server to shutdown the stream. + // Don't bother handling the response to the "TEARDOWN". + rtspClient->sendTeardownCommand(*scs.session, NULL); + } + } + + TRACE_INFO( "Closing the stream"); + Medium::close(rtspClient); + // Note that this will also cause this stream's "StreamClientState" structure to get reclaimed. + + if (--rtspClientCount == 0) { + // The final stream has ended, so exit the application now. + // (Of course, if you're embedding this code into your own application, you might want to comment this out.) + exit(exitCode); + } +} + +void onScheduledDelayedTask(void* clientData) +{ + try + { + TRACE_DEBUG("On delayed task (ie Keep Alive)"); + RTSPClient* rtspClient = (RTSPClient*)(clientData); + UsageEnvironment& env = rtspClient->envir(); // alias + StreamClientState& scs = ((MyRTSPClient*)rtspClient)->scs; // alias + MediaSession *session = scs.session; + + char * ret =NULL; + // Get the session parameter to do a keep alive + rtspClient->getMediaSessionParameter(*session,NULL,ret); + free(ret); + + //setup our keep alive using a delayed task + env.taskScheduler().scheduleDelayedTask(keepAliveTimer, onScheduledDelayedTask, rtspClient); + + } + catch(...) + { + TRACE_ERROR("Exception thrown during keep alive"); + } +} + +/*------------------------------------------------------------*/ +// CstreamMedia class implementation +/*------------------------------------------------------------*/ + +/** +Constructor +*/ +CstreamMedia::CstreamMedia() +{ + stream = NULL; + ms = NULL; + scheduler = NULL; + env = NULL; + rtsp = NULL; + b_tcp_stream = 0; // normal udp + i_stream = 0; + nostream = 0; + event = 0; + m_state = RTSP_STATE_IDLE; + m_recvThreadFlag = TRUE; + hDataThreadReady=NULL; + hRecvEvent=NULL; + hRecvDataThread=NULL; + hFrameListLock=NULL; +} + +/** +Destructor +Cleanup the streams +*/ +CstreamMedia::~CstreamMedia() +{ + StreamTrack *tr; + m_state = RTSP_STATE_IDLE; + m_recvThreadFlag = FALSE; + if (stream!=NULL) + { + for (int i = 0; i < i_stream; i++) + { + tr = (StreamTrack*)stream[i]; + if (tr) + delete tr; + } + free(stream); + } + stream = NULL; + + if (ms!=NULL) + Medium::close(ms); + ms = NULL; + + if(rtsp!=NULL) + { + RTSPClient::close(rtsp); + MyRTSPClient * crtsp=(MyRTSPClient*)rtsp; + delete crtsp; + } + rtsp = NULL; + + if(env!=NULL) + env->reclaim(); + env = NULL; + + if (scheduler!=NULL) + delete scheduler; + scheduler = NULL; + + //CloseHandle(hRecvEvent); + //CloseHandle(hRecvDataThread); +} + +/** +Opens an RTSP stream using the supplied filename as the URL +returns 0 if the stream is opened successfully +*/ +int CstreamMedia::rtspClientOpenStream(const char* url) +{ + TRACE_INFO("RTSP Open Stream URL=%s",url); + if (m_state >= RTSP_STATE_OPENED) + { + TRACE_ERROR("rtspClientOpenStream already open"); + return 0; + } + + int verbosityLevel = 0; + int tunnelOverHTTPPortNum = 0; + char* sdpDescription; + MediaSubsessionIterator *iter = NULL; + MediaSubsession *sub = NULL; + StreamTrack *tk; + + stream = NULL; + ms = NULL; + scheduler = NULL; + env = NULL; + rtsp = NULL; + i_stream = 0; + nostream = 0; + event = 0; + + TRACE_DEBUG("create BasicTaskScheduler"); + scheduler = BasicTaskScheduler::createNew(); + if (scheduler == NULL) + { + TRACE_ERROR( "BasicTaskScheduler fail"); + goto fail; + } + + TRACE_DEBUG("create BasicUsageEnvironment"); + env = CMyUsageEnvironment::createNew(*scheduler); + if (env == NULL) + { + TRACE_ERROR( "BasicUsageEnvironment fail"); + goto fail; + } + + TRACE_DEBUG("create RTSPClient"); + rtsp = RTSPClient::createNew(*env, verbosityLevel, "MyFilter"); + if (rtsp == NULL) + { + TRACE_ERROR( "create rtsp client fail"); + goto fail; + } + + TRACE_DEBUG("send rtp options"); + if (rtsp->sendOptionsCmd(url) == NULL) + { + TRACE_ERROR( "send optioncmd fail"); + goto fail; + } + + sdpDescription = rtsp->describeURL(url); + if (sdpDescription == NULL) + { + TRACE_ERROR( "rtspClient->describeStatus"); + goto fail; + } + + ms = MediaSession::createNew(*env, sdpDescription); + delete[] sdpDescription; + if (ms == NULL) + { + TRACE_ERROR( "create MediaSession fail"); + goto fail; + } + + iter = new MediaSubsessionIterator(*ms); + while( ( sub = iter->next() ) != NULL ) + { + Boolean bInit; + + int i_buffer = 0; + unsigned const thresh = 200000; /* RTP reorder threshold .2 second (default .1) */ + + if( !strcmp( sub->mediumName(), "video" ) ) + i_buffer = 2000000; + else continue; + + if( !strcmp( sub->codecName(), "X-ASF-PF" ) ) + bInit = sub->initiate( 4 ); /* Constant ? */ + else + bInit = sub->initiate(); + + if( !bInit ) + { + TRACE_ERROR( "RTP subsession {0} / {1} failed",sub->mediumName(),sub->codecName()); + } + else + { + if( sub->rtpSource() != NULL ) + { + int fd = sub->rtpSource()->RTPgs()->socketNum(); + + /* Increase the buffer size */ + if( i_buffer > 0 ) + increaseReceiveBufferTo(*env, fd, i_buffer ); + + /* Increase the RTP reorder timebuffer just a bit */ + sub->rtpSource()->setPacketReorderingThresholdTime(thresh); + } + + + /* Issue the SETUP */ + if(rtsp) + { + if( !rtsp->setupMediaSubsession(*sub, false, b_tcp_stream)) + { + /* if we get an unsupported transport error, toggle TCP use and try again */ + if( !strstr(env->getResultMsg(), "461 Unsupported Transport") + || !rtsp->setupMediaSubsession( *sub, false,b_tcp_stream ? false : true)) + { + TRACE_ERROR( "SETUP of '%s/%s failed (%s)",sub->mediumName(),sub->codecName(),env->getResultMsg()) ; + continue; + } + } + } + + /* Check if we will receive data from this subsession for this track */ + if( sub->readSource() == NULL ) + continue; + + tk = new StreamTrack; + if( !tk ) + { + delete iter; + TRACE_ERROR( "Failed to allocate a stream track! Yikes!!"); + return (-1); + } + tk->pstreammedia = this; + tk->sub = sub; + tk->waiting = 0; + ZeroMemory(&tk->mediainfo, sizeof(MediaInfo)); + tk->mediainfo.b_packetized = 1; + strncpy(tk->mediainfo.codecName,sub->codecName(), sizeof(tk->mediainfo.codecName)); + tk->mediainfo.duration = sub->playEndTime()-sub->playStartTime(); + + if( !strcmp( sub->mediumName(), "video" ) ) + { + TRACE_INFO("Found video session"); + tk->i_buffer = DUMMY_SINK_RECEIVE_BUFFER_SIZE; + tk->p_buffer = new char[tk->i_buffer]; + tk->mediainfo.codecType = CODEC_TYPE_VIDEO; + tk->mediainfo.i_format = FOURCC('u','n','d','f'); + tk->mediainfo.video.fps = sub->videoFPS(); + tk->mediainfo.video.height = sub->videoHeight(); + tk->mediainfo.video.width = sub->videoWidth(); + TRACE_INFO("codec = %s", sub->codecName()); + if( !strcmp( sub->codecName(), "MPV" ) ) + { + tk->mediainfo.i_format = FOURCC( 'm', 'p', 'g', 'v' ); + } + else if( !strcmp( sub->codecName(), "H263" ) || + !strcmp( sub->codecName(), "H263-1998" ) || + !strcmp( sub->codecName(), "H263-2000" ) ) + { + tk->mediainfo.i_format = FOURCC( 'H', '2', '6', '3' ); + } + else if( !strcmp( sub->codecName(), "H261" ) ) + { + tk->mediainfo.i_format = FOURCC( 'H', '2', '6', '1' ); + } + else if( !strcmp( sub->codecName(), "H264" ) ) + { + unsigned int i_extra = 0; + char *p_extra = NULL; + + tk->mediainfo.i_format = FOURCC( 'h', '2', '6', '4' ); + tk->mediainfo.b_packetized = false; + this->m_format = "H264"; + + if((p_extra=(char *)parseH264ConfigStr( sub->fmtp_spropparametersets(), i_extra ) ) ) + { + tk->mediainfo.extra_size = i_extra; + tk->mediainfo.extra = new char[i_extra]; + memcpy(tk->mediainfo.extra, p_extra, i_extra ); + + delete[] p_extra; + } + } + else if( !strcmp( sub->codecName(), "JPEG" ) ) + { + tk->mediainfo.i_format = FOURCC( 'M', 'J', 'P', 'G' ); + this->m_format = "JPEG"; + } + else if( !strcmp( sub->codecName(), "MP4V-ES" ) ) + { + unsigned int i_extra; + char *p_extra; + + tk->mediainfo.i_format = FOURCC( 'm', 'p', '4', 'v' ); + this->m_format = "MP4V"; + + if( ( p_extra = (char *)parseGeneralConfigStr( sub->fmtp_config(), i_extra ) ) ) + { + tk->mediainfo.extra_size = i_extra; + tk->mediainfo.extra = new char[i_extra]; + memcpy(tk->mediainfo.extra, p_extra, i_extra ); + delete[] p_extra; + } + } + else if( !strcmp( sub->codecName(), "MP2P" ) || + !strcmp( sub->codecName(), "MP1S" ) ) + { + ; + } + else if( !strcmp( sub->codecName(), "X-ASF-PF" ) ) + { + ; + } + } + + + if( sub->rtcpInstance() != NULL ) + { + ;//sub->rtcpInstance()->setByeHandler( StreamClose, tk ); + } + + stream = (StreamTrack**)realloc( stream, sizeof( StreamTrack ) * (i_stream+1)); + stream[i_stream++] = tk; + + } + + }//while find track; + + delete iter; + if (i_stream <= 0) + { + TRACE_ERROR( "don't find stream"); + goto fail; + } + m_state = RTSP_STATE_OPENED; + + return(0); + +fail: + m_state = RTSP_STATE_IDLE; + if (stream==NULL) + free(stream); + stream = NULL; + + if (ms==NULL) + Medium::close(ms); + ms = NULL; + + if(rtsp==NULL) + { + RTSPClient::close(rtsp); + MyRTSPClient * crtsp=(MyRTSPClient*)rtsp; + delete crtsp; + } + rtsp = NULL; + + if(env==NULL) + env->reclaim(); + env = NULL; + + if (scheduler==NULL) + delete scheduler; + scheduler = NULL; + + return(-1); +} + +/** +Start streaming video from the open stream +/sa CstreamMedia::rtspClientOpenStream(const char* url) +*/ +int CstreamMedia::rtspClientPlayStream(const char* url) +{ + TRACE_INFO("RTSP play Stream URL=%s",url); + this->m_url=url; + if (m_state == RTSP_STATE_OPENED) + { + event = 0; + hDataThreadReady = CreateEvent(NULL, FALSE, FALSE, NULL); + hRecvEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + hRecvDataThread= CreateThread(NULL, 0, rtspRecvDataThread, this, 0, NULL); + + Sleep(5); + } + m_state = RTSP_STATE_PLAYING; + + return 0; +} + +/** +Query the RTSP stream for media information +ie width and height of the frame + +*/ +int CstreamMedia::rtspClinetGetMediaInfo(enum CodecType codectype, MediaInfo& mediainfo) +{ + TRACE_INFO("RTSP get media info"); + StreamTrack *tr; + for (int i = 0; i < i_stream; i++) + { + tr = (StreamTrack*)stream[i]; + if (tr->mediainfo.codecType == codectype) + { + mediainfo = tr->mediainfo; + return(0); + } + } + + return(-1); +} + +/** +close the stream and clean things up +*/ +int CstreamMedia::rtspClientCloseStream(void) +{ + TRACE_INFO("RTSP close stream"); + StreamTrack *tr; + + if (m_state == RTSP_STATE_IDLE) + { + TRACE_WARN( "nothting to do?"); + return(0); + } + + event = 0xff; + + + TRACE_INFO( "Tear down the media session"); + if (rtsp!= NULL && ms !=NULL) + { + rtsp->teardownMediaSession(*ms); + } + + m_state = RTSP_STATE_IDLE; + nostream = 1; + + TRACE_INFO( "Terminate the receive thread"); + if (WaitForSingleObject(hRecvEvent, 500) == WAIT_TIMEOUT) + { + TRACE_INFO( "rtspClientCloseStream WAIT_TIMEOUT"); + DWORD dwExitCode = 0; + TerminateThread(hRecvDataThread, dwExitCode); + } + + + if (stream) + { + for (int i = 0; i < i_stream; i++) + { + tr = (StreamTrack*)stream[i]; + if (tr) + { + delete tr; + } + + } + + free(stream); + stream = NULL; + } + + if (ms !=NULL) + Medium::close(ms); + ms = NULL; + + if(rtsp!=NULL) + RTSPClient::close(rtsp); + rtsp = NULL; + + if(env!=NULL) + env->reclaim(); + env = NULL; + + if (scheduler!=NULL) + delete scheduler; + scheduler = NULL; + + if (hDataThreadReady != INVALID_HANDLE_VALUE) + CloseHandle(hDataThreadReady); + hDataThreadReady=NULL; + + if (hRecvEvent != INVALID_HANDLE_VALUE) + CloseHandle(hRecvEvent); + hRecvEvent=NULL; + + if (hRecvDataThread != INVALID_HANDLE_VALUE) + CloseHandle(hRecvDataThread); + hRecvDataThread=NULL; + + TRACE_INFO( "rtspClientCloseStream"); + return(0); +} + +/** +Get a video frame from the sink's video queue +*/ +bool CstreamMedia::GetFrame(BYTE *pData, int bufferSize) +{ + FrameInfo* frame = NULL; + if((MyRTSPClient*)rtsp==NULL) + { + TRACE_ERROR( "Lost RTSP session graph needs to be restarted"); + return false; + } + + if(m_state != RTSP_STATE_PLAYING) + { + TRACE_DEBUG( "RTSP stream is not playing"); + return false; + } + + try{ + MyVideoSink *sink = ((MyRTSPClient*)rtsp)->get_sink(); // alias + if(sink!=NULL && sink->get_FrameQueue() != NULL) + { + frame = sink->get_FrameQueue()->get(); + if (frame!=NULL && bufferSize>=frame->frameHead.FrameLen) + { + memcpy((char *)pData, frame->pdata, frame->frameHead.FrameLen); + TRACE_DEBUG( "!!rtspClientReadFrame len = %d count = %d", frame->frameHead.FrameLen , sink->get_FrameQueue()->get_Count()); + DeleteFrame(frame); + return (true); + } + + TRACE_ERROR( "no frames left in queue rtspClientReadFrame fail"); + return false; + } + else{ + TRACE_ERROR( "Lost the video sink, better result the graph"); + } + } + catch(...) + { + TRACE_ERROR("Exception get frame"); + } + return false; +} + +/* +Sense we allocated the frame on our heap +we need to destory it +**/ +void CstreamMedia::DeleteFrame(FrameInfo* frame) +{ + try + { + if(frame!=NULL) + free(frame); + frame=NULL; + } + catch(...) + { + } +} + +/** +Report back the number of frames left in our video queue +/sa MyRTSPClient +*/ +int CstreamMedia::GetQueueSize() +{ + if(rtsp==NULL) + { + return 0; + } + MyVideoSink *sink = ((MyRTSPClient*)rtsp)->get_sink(); // alias + if(sink==NULL || sink->get_FrameQueue()==NULL) + { + return 0; + } + return sink->get_FrameQueue()->get_Count(); +} \ No newline at end of file diff --git a/LiveProxy/live.h b/LiveProxy/live.h new file mode 100644 index 0000000..a352fe3 --- /dev/null +++ b/LiveProxy/live.h @@ -0,0 +1,133 @@ +#pragma once +#include "UsageEnvironment.hh" +#include "BasicUsageEnvironment.hh" +#include "GroupsockHelper.hh" +#include "liveMedia.hh" +#include "MediaQueue.h" + + +#ifdef MY_DLL + #define MY_DLL_EXPORTS __declspec(dllexport) +#else + #define MY_DLL_EXPORTS __declspec(dllimport) +#endif //MY_DLL + +//some type shortcuts +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned int fourcc_t; + +#include "trace.h" + +// +#define FOURCC( a, b, c, d ) ( ((uint32_t)a) | ( ((uint32_t)b) << 8 ) | ( ((uint32_t)c) << 16 ) | ( ((uint32_t)d) << 24 ) ) + + +//The different types of codecs +enum CodecType { + CODEC_TYPE_UNKNOWN = -1, + CODEC_TYPE_VIDEO, + CODEC_TYPE_AUDIO, + CODEC_TYPE_DATA, + CODEC_TYPE_SUBTITLE, + CODEC_TYPE_ATTACHMENT, + CODEC_TYPE_NB +}; + +//The current state of the RTSP stream +enum RTSPClientState { + RTSP_STATE_IDLE, /**< not initialized */ + RTSP_STATE_OPENED, + RTSP_STATE_PLAYING, /**< initialized and receiving data */ + RTSP_STATE_PAUSED, /**< initialized, but not receiving data */ + RTSP_STATE_LOADING, + RTSP_STATE_ERROR +}; + + +//If we could get the video format +//this is where would put it +typedef struct __VideoFormat +{ + int width; + int height; + int fps; + int bitrate; +}VideoFormat; + + +typedef struct __MediaInfo +{ + enum CodecType codecType; + fourcc_t i_format; + char codecName[50]; + VideoFormat video; + int duration; + int b_packetized; + char* extra; + int extra_size; +}MediaInfo; + + +class CstreamMedia; +typedef struct __StreamTrack +{ + CstreamMedia * pstreammedia; + int waiting; + MediaInfo mediainfo; + MediaSubsession* sub; + char* p_buffer; + unsigned int i_buffer; + +}StreamTrack; + + +//our worker thread to receive frames on +DWORD WINAPI rtspRecvDataThread( LPVOID lpParam ); + +/** +*/ +class MY_DLL_EXPORTS CstreamMedia +{ + +private: + bool m_recvThreadFlag; + MediaSession *ms; + TaskScheduler *scheduler; + UsageEnvironment *env ; + RTSPClient *rtsp; + int i_stream; + StreamTrack **stream; + int b_tcp_stream; + std::string m_url; + std::string m_format; + enum RTSPClientState m_state; + char event; + HANDLE hFrameListLock; + HANDLE hRecvDataThread; + HANDLE hDataThreadReady; + HANDLE hRecvEvent; + int nostream; + + public: + CstreamMedia(); + ~CstreamMedia(); + + //The rtsp thread + int rtspClientOpenStream(const char* filename); + int rtspClientPlayStream(const char* url); + int rtspClinetGetMediaInfo(enum CodecType codectype, MediaInfo& mediainfo); + int rtspClientCloseStream(void); + + //Queue management + bool GetFrame(BYTE * pData, int bufferSize); + void DeleteFrame(FrameInfo* frame); + int GetQueueSize(); + const char * get_Url(){return m_url.c_str();} + const char * get_Format(){return m_format.c_str();} + +private: + //our data thread + friend DWORD WINAPI rtspRecvDataThread( LPVOID lpParam ); +}; diff --git a/LiveProxy/stdafx.h b/LiveProxy/stdafx.h new file mode 100644 index 0000000..6fd2d59 --- /dev/null +++ b/LiveProxy/stdafx.h @@ -0,0 +1,49 @@ +//============================================================================= +// Copyright (c) 2013 Horth Systems. All rights reserved. +// +// This file contains trade secrets of Horth Systems. No part may be reproduced or +// transmitted in any form by any means or for any purpose without the express +// written permission of Horth Systems. +// +// $File:$ +// $Revision:$ +// $Date:$ +// $Author:$ +//============================================================================= + + + +#pragma once +#include +#include +#include // std::toupper +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +//Link in our dependent library +//live555 libraries +#pragma comment (lib,"liveMedia.lib") +#pragma comment (lib,"BasicUsageEnvironment.lib") +#pragma comment (lib,"groupsock.lib") +#pragma comment (lib,"UsageEnvironment.lib") + +//ffmpeg libraries +#pragma comment (lib,"avcodec.lib") +#pragma comment (lib,"avutil.lib") +#pragma comment (lib,"swscale.lib") + +//windows libs +#pragma comment (lib,"wsock32.lib") +//#pragma comment (lib,"webservices.lib") +#pragma comment (lib,"winmm.lib") +#pragma comment (lib,"Ws2_32.lib") diff --git a/LiveProxy/support/avcodec-55.dll b/LiveProxy/support/avcodec-55.dll new file mode 100644 index 0000000..ceaa020 Binary files /dev/null and b/LiveProxy/support/avcodec-55.dll differ diff --git a/LiveProxy/support/avdevice-55.dll b/LiveProxy/support/avdevice-55.dll new file mode 100644 index 0000000..df52386 Binary files /dev/null and b/LiveProxy/support/avdevice-55.dll differ diff --git a/LiveProxy/support/avfilter-3.dll b/LiveProxy/support/avfilter-3.dll new file mode 100644 index 0000000..67d78cb Binary files /dev/null and b/LiveProxy/support/avfilter-3.dll differ diff --git a/LiveProxy/support/avformat-55.dll b/LiveProxy/support/avformat-55.dll new file mode 100644 index 0000000..5cc576d Binary files /dev/null and b/LiveProxy/support/avformat-55.dll differ diff --git a/LiveProxy/support/avutil-52.dll b/LiveProxy/support/avutil-52.dll new file mode 100644 index 0000000..30da0fd Binary files /dev/null and b/LiveProxy/support/avutil-52.dll differ diff --git a/LiveProxy/support/postproc-52.dll b/LiveProxy/support/postproc-52.dll new file mode 100644 index 0000000..5d3cf06 Binary files /dev/null and b/LiveProxy/support/postproc-52.dll differ diff --git a/LiveProxy/trace.cpp b/LiveProxy/trace.cpp new file mode 100644 index 0000000..a0f4a45 --- /dev/null +++ b/LiveProxy/trace.cpp @@ -0,0 +1,51 @@ +#pragma once +#include "stdafx.h" +#include +#include "trace.h" + + +extern "C" +{ + int g_logLevel=-1; + logHandler* loggingCallback=NULL; + /** + Native interface to managed logger + */ + _declspec(dllexport) void Write(int level, const char * msg) + { + if(loggingCallback!=NULL) + loggingCallback(level, msg); + } + + _declspec(dllexport) void InitializeTraceHelper(int level,logHandler* callback){ + g_logLevel=level; + loggingCallback=callback; + } + /** + Creates the message + */ + _declspec(dllexport) void Log(int level, const char * functionName, const char * lpszFormat, ...) + { + if(level>g_logLevel)return; + static char szMsg[2048]; + va_list argList; + va_start(argList, lpszFormat); + try + { + vsprintf(szMsg,lpszFormat, argList); + } + catch(...) + { + strcpy(szMsg ,"DebugHelper:Invalid string format!"); + } + va_end(argList); + std::string logMsg=static_cast( &(std::ostringstream() << ::GetCurrentProcessId() << "," << ::GetCurrentThreadId() << "," << functionName << ", " << szMsg) )->str(); + try{ + Write(level, logMsg.c_str()); + } + catch(...) + { + } + } + +} diff --git a/LiveProxy/trace.h b/LiveProxy/trace.h new file mode 100644 index 0000000..9a1cee5 --- /dev/null +++ b/LiveProxy/trace.h @@ -0,0 +1,26 @@ +#pragma once +#include "stdafx.h" + +#define LOGLEVEL_OFF 0 +#define LOGLEVEL_CRITICAL 1 +#define LOGLEVEL_ERROR 2 +#define LOGLEVEL_WARN 3 +#define LOGLEVEL_INFO 4 +#define LOGLEVEL_VERBOSE 5 +#define LOGLEVEL_DEBUG 6 + +#define TRACE_CRITICAL(x,...) Log(LOGLEVEL_CRITICAL, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_ERROR(x,...) Log(LOGLEVEL_ERROR, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_WARN(x,...) Log(LOGLEVEL_WARN, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_INFO(x,...) Log(LOGLEVEL_INFO, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_VERBOSE(x,...) Log(LOGLEVEL_VERBOSE, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_DEBUG(x,...) Log(LOGLEVEL_DEBUG, __FUNCTION__, x, ##__VA_ARGS__) + +extern int g_logLevel; + +extern "C" +{ + typedef void (logHandler)(int level, const char* msg); + _declspec(dllexport) void InitializeTraceHelper(int level,logHandler* callback); + _declspec(dllexport) void Log(int level, const char * functionName, const char * lpszFormat, ...); +} diff --git a/README.md b/README.md index 09de7d0..57362b1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,70 @@ RTSPSource ========== -A DirectShow Source Filter that uses RTSP to get video streams +Description +=========== + + A DirectShow Source Filter that uses RTSP to get video streams + +References & Attribution +======================== + + This project contains snapshots of several other open source libraries or projects + that are included to ensure that the correct versions of these external libraries + are used: + + LiveProxy - https://github.com/dhorth/LiveProxy + + ffMpeg - http://ffmpeg.zeranoe.com/builds/ + Codecs used by the Filter + + live555 - http://www.live555.com/liveMedia/ + Support for RTSP + + Directshowlib - http://directshownet.sourceforge.net/ + .NET access to DirectShow Used by Samples + + Log4net - http://logging.apache.org/log4net/ + Logging libraries Used by Samples + + This project is based on the vsm7dsf project at https://github.com/fyeh/vsm7dsf but has been modified to work with Generic RTSP sources + +Microsoft pre-req's +=================== + + Microsoft Visual C++ 2008 Service Pack 1 Redistributable Package MFC Security Update + Microsoft Visual C++ 2010 Service Pack 1 Redistributable Package MFC Security Update + Visual C++ Redistributable for Visual Studio 2012 Update 4 + .NET 4 + + +Project Contents +================ + + PushSource - The Actual DirectShow Filter. + + HelperLib - C# managed library. Used to integrate the managed Cisco VSM SDK with the unmanaged direct show filter. + + SampleGrabber - C# Application useful for testing the DSF. + +Building +======== + + Build LiveProxy first then build RTSPSource. Do this for whichever configuration (Debug or Release) you are building for. + IE build Liveproxy for Debug then RTSPSource for Debug. When you are ready to release, build LiveProxy for Release then RTSPSource for Release. + +Using +===== + + After a successful build, the RTSPSource.ax filter will be registered on the system. Any program that uses DirectShow + to render a URL in the specified format will load the filter. + + URL should be in the format: + + rtspsource://&Width=&Height= + + Note that Width and Height are required and must match the width and height of the stream specified. + + EG: is you would normally used an RTSP URL of "rtsp://192.168.1.1/stream1" to play an RTSP stream from the video source, + the URL to invoke this source filter would be "rtspsource://192.168.1.1/stream1&width=704&height=480". + \ No newline at end of file diff --git a/RTSP DS Filter.sln b/RTSP DS Filter.sln new file mode 100644 index 0000000..ba04bc7 --- /dev/null +++ b/RTSP DS Filter.sln @@ -0,0 +1,85 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{F7699AC6-9855-46B0-8939-B21A4782A711}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleGrabber", "RTSP Source Filter\Samples\SampleGrabber\SampleGrabber.csproj", "{CB0B2491-179E-4974-840E-845FEB58E411}" + ProjectSection(ProjectDependencies) = postProject + {FEA627F9-A115-4EFB-B489-E28991BC8458} = {FEA627F9-A115-4EFB-B489-E28991BC8458} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RTSPSource", "RTSP Source Filter\PushSource\pushsource.vcxproj", "{39469123-3EB7-45F3-A15B-3854DFD2C2EB}" + ProjectSection(ProjectDependencies) = postProject + {FEA627F9-A115-4EFB-B489-E28991BC8458} = {FEA627F9-A115-4EFB-B489-E28991BC8458} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelpLib", "RTSP Source Filter\HelpLib\HelpLib.csproj", "{FEA627F9-A115-4EFB-B489-E28991BC8458}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CB0B2491-179E-4974-840E-845FEB58E411}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Debug|Win32.ActiveCfg = Debug|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Debug|x86.ActiveCfg = Debug|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Release|Any CPU.Build.0 = Release|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Release|Win32.ActiveCfg = Release|Any CPU + {CB0B2491-179E-4974-840E-845FEB58E411}.Release|x86.ActiveCfg = Release|Any CPU + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Debug|Win32.ActiveCfg = Debug|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Debug|Win32.Build.0 = Debug|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Debug|x86.ActiveCfg = Debug|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Debug|x86.Build.0 = Debug|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Release|Any CPU.ActiveCfg = Release|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Release|Mixed Platforms.Build.0 = Release|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Release|Win32.ActiveCfg = Release|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Release|Win32.Build.0 = Release|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Release|x86.ActiveCfg = Release|Win32 + {39469123-3EB7-45F3-A15B-3854DFD2C2EB}.Release|x86.Build.0 = Release|Win32 + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Debug|Win32.ActiveCfg = Debug|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Debug|x86.ActiveCfg = Debug|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Release|Any CPU.Build.0 = Release|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Release|Win32.ActiveCfg = Release|Any CPU + {FEA627F9-A115-4EFB-B489-E28991BC8458}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CB0B2491-179E-4974-840E-845FEB58E411} = {F7699AC6-9855-46B0-8939-B21A4782A711} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + VisualSVNWorkingCopyRoot = . + EndGlobalSection + GlobalSection(RocketSvnScc) = preSolution + Svn-Managed = True + Manager = Rocket SVN - Subversion Support for Visual Studio + EndGlobalSection + GlobalSection(SpecExplorer.ActivityCompletionStatus) = preSolution + SpecExplorer.ActivityCompletionStatus = + EndGlobalSection +EndGlobal diff --git a/RTSP DS Filter.suo b/RTSP DS Filter.suo new file mode 100644 index 0000000..fd487d8 Binary files /dev/null and b/RTSP DS Filter.suo differ diff --git a/RTSP DS Filter.v11.suo b/RTSP DS Filter.v11.suo new file mode 100644 index 0000000..b3c41d1 Binary files /dev/null and b/RTSP DS Filter.v11.suo differ diff --git a/RTSP Source Filter/BuildStuff/DirectShowLib-2005.dll b/RTSP Source Filter/BuildStuff/DirectShowLib-2005.dll new file mode 100644 index 0000000..ba11a16 Binary files /dev/null and b/RTSP Source Filter/BuildStuff/DirectShowLib-2005.dll differ diff --git a/RTSP Source Filter/BuildStuff/vcredist_x86.exe b/RTSP Source Filter/BuildStuff/vcredist_x86.exe new file mode 100644 index 0000000..05d0a05 Binary files /dev/null and b/RTSP Source Filter/BuildStuff/vcredist_x86.exe differ diff --git a/RTSP Source Filter/BuildStuff/videocamera_run.ico b/RTSP Source Filter/BuildStuff/videocamera_run.ico new file mode 100644 index 0000000..2ab4a4f Binary files /dev/null and b/RTSP Source Filter/BuildStuff/videocamera_run.ico differ diff --git a/RTSP Source Filter/Common/DirectShow/amextra.h b/RTSP Source Filter/Common/DirectShow/amextra.h new file mode 100644 index 0000000..3caf64c --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/amextra.h @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// File: AMExtra.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __AMEXTRA__ +#define __AMEXTRA__ + +// Simple rendered input pin +// +// NOTE if your filter queues stuff before rendering then it may not be +// appropriate to use this class +// +// In that case queue the end of stream condition until the last sample +// is actually rendered and flush the condition appropriately + +class CRenderedInputPin : public CBaseInputPin +{ +public: + + CRenderedInputPin(__in_opt LPCTSTR pObjectName, + __in CBaseFilter *pFilter, + __in CCritSec *pLock, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); +#ifdef UNICODE + CRenderedInputPin(__in_opt LPCSTR pObjectName, + __in CBaseFilter *pFilter, + __in CCritSec *pLock, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); +#endif + + // Override methods to track end of stream state + STDMETHODIMP EndOfStream(); + STDMETHODIMP EndFlush(); + + HRESULT Active(); + HRESULT Run(REFERENCE_TIME tStart); + +protected: + + // Member variables to track state + BOOL m_bAtEndOfStream; // Set by EndOfStream + BOOL m_bCompleteNotified; // Set when we notify for EC_COMPLETE + +private: + void DoCompleteHandling(); +}; + +#endif // __AMEXTRA__ + diff --git a/RTSP Source Filter/Common/DirectShow/amfilter.h b/RTSP Source Filter/Common/DirectShow/amfilter.h new file mode 100644 index 0000000..a7a1ec0 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/amfilter.h @@ -0,0 +1,1587 @@ +//------------------------------------------------------------------------------ +// File: AMFilter.h +// +// Desc: DirectShow base classes - efines class hierarchy for streams +// architecture. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __FILTER__ +#define __FILTER__ + +/* The following classes are declared in this header: */ + +class CBaseMediaFilter; // IMediaFilter support +class CBaseFilter; // IBaseFilter,IMediaFilter support +class CBasePin; // Abstract base class for IPin interface +class CEnumPins; // Enumerate input and output pins +class CEnumMediaTypes; // Enumerate the pin's preferred formats +class CBaseOutputPin; // Adds data provider member functions +class CBaseInputPin; // Implements IMemInputPin interface +class CMediaSample; // Basic transport unit for IMemInputPin +class CBaseAllocator; // General list guff for most allocators +class CMemAllocator; // Implements memory buffer allocation + + +//===================================================================== +//===================================================================== +// +// QueryFilterInfo and QueryPinInfo AddRef the interface pointers +// they return. You can use the macro below to release the interface. +// +//===================================================================== +//===================================================================== + +#define QueryFilterInfoReleaseGraph(fi) if ((fi).pGraph) (fi).pGraph->Release(); + +#define QueryPinInfoReleaseFilter(pi) if ((pi).pFilter) (pi).pFilter->Release(); + +//===================================================================== +//===================================================================== +// Defines CBaseMediaFilter +// +// Abstract base class implementing IMediaFilter. +// +// Typically you will derive your filter from CBaseFilter rather than +// this, unless you are implementing an object such as a plug-in +// distributor that needs to support IMediaFilter but not IBaseFilter. +// +// Note that IMediaFilter is derived from IPersist to allow query of +// class id. +//===================================================================== +//===================================================================== + +class AM_NOVTABLE CBaseMediaFilter : public CUnknown, + public IMediaFilter +{ + +protected: + + FILTER_STATE m_State; // current state: running, paused + IReferenceClock *m_pClock; // this filter's reference clock + // note: all filters in a filter graph use the same clock + + // offset from stream time to reference time + CRefTime m_tStart; + + CLSID m_clsid; // This filters clsid + // used for serialization + CCritSec *m_pLock; // Object we use for locking + +public: + + CBaseMediaFilter( + __in_opt LPCTSTR pName, + __inout_opt LPUNKNOWN pUnk, + __in CCritSec *pLock, + REFCLSID clsid); + + virtual ~CBaseMediaFilter(); + + DECLARE_IUNKNOWN + + // override this to say what interfaces we support where + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); + + // + // --- IPersist method --- + // + + STDMETHODIMP GetClassID(__out CLSID *pClsID); + + // --- IMediaFilter methods --- + + STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State); + + STDMETHODIMP SetSyncSource(__inout_opt IReferenceClock *pClock); + + STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock); + + // default implementation of Stop and Pause just record the + // state. Override to activate or de-activate your filter. + // Note that Run when called from Stopped state will call Pause + // to ensure activation, so if you are a source or transform + // you will probably not need to override Run. + STDMETHODIMP Stop(); + STDMETHODIMP Pause(); + + + // the start parameter is the difference to be added to the + // sample's stream time to get the reference time for + // its presentation + STDMETHODIMP Run(REFERENCE_TIME tStart); + + // --- helper methods --- + + // return the current stream time - ie find out what + // stream time should be appearing now + virtual HRESULT StreamTime(CRefTime& rtStream); + + // Is the filter currently active? (running or paused) + BOOL IsActive() { + CAutoLock cObjectLock(m_pLock); + return ((m_State == State_Paused) || (m_State == State_Running)); + }; +}; + +//===================================================================== +//===================================================================== +// Defines CBaseFilter +// +// An abstract class providing basic IBaseFilter support for pin +// enumeration and filter information reading. +// +// We cannot derive from CBaseMediaFilter since methods in IMediaFilter +// are also in IBaseFilter and would be ambiguous. Since much of the code +// assumes that they derive from a class that has m_State and other state +// directly available, we duplicate code from CBaseMediaFilter rather than +// having a member variable. +// +// Derive your filter from this, or from a derived object such as +// CTransformFilter. +//===================================================================== +//===================================================================== + + +class AM_NOVTABLE CBaseFilter : public CUnknown, // Handles an IUnknown + public IBaseFilter, // The Filter Interface + public IAMovieSetup // For un/registration +{ + +friend class CBasePin; + +protected: + FILTER_STATE m_State; // current state: running, paused + IReferenceClock *m_pClock; // this graph's ref clock + CRefTime m_tStart; // offset from stream time to reference time + CLSID m_clsid; // This filters clsid + // used for serialization + CCritSec *m_pLock; // Object we use for locking + + WCHAR *m_pName; // Full filter name + IFilterGraph *m_pGraph; // Graph we belong to + IMediaEventSink *m_pSink; // Called with notify events + LONG m_PinVersion; // Current pin version + +public: + + CBaseFilter( + __in_opt LPCTSTR pName, // Object description + __inout_opt LPUNKNOWN pUnk, // IUnknown of delegating object + __in CCritSec *pLock, // Object who maintains lock + REFCLSID clsid); // The clsid to be used to serialize this filter + + CBaseFilter( + __in_opt LPCTSTR pName, // Object description + __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object + __in CCritSec *pLock, // Object who maintains lock + REFCLSID clsid, // The clsid to be used to serialize this filter + __inout HRESULT *phr); // General OLE return code +#ifdef UNICODE + CBaseFilter( + __in_opt LPCSTR pName, // Object description + __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object + __in CCritSec *pLock, // Object who maintains lock + REFCLSID clsid); // The clsid to be used to serialize this filter + + CBaseFilter( + __in_opt LPCSTR pName, // Object description + __in_opt LPUNKNOWN pUnk, // IUnknown of delegating object + __in CCritSec *pLock, // Object who maintains lock + REFCLSID clsid, // The clsid to be used to serialize this filter + __inout HRESULT *phr); // General OLE return code +#endif + ~CBaseFilter(); + + DECLARE_IUNKNOWN + + // override this to say what interfaces we support where + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); +#ifdef DEBUG_DS + STDMETHODIMP_(ULONG) NonDelegatingRelease(); +#endif + + // + // --- IPersist method --- + // + + STDMETHODIMP GetClassID(__out CLSID *pClsID); + + // --- IMediaFilter methods --- + + STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State); + + STDMETHODIMP SetSyncSource(__in_opt IReferenceClock *pClock); + + STDMETHODIMP GetSyncSource(__deref_out_opt IReferenceClock **pClock); + + + // override Stop and Pause so we can activate the pins. + // Note that Run will call Pause first if activation needed. + // Override these if you want to activate your filter rather than + // your pins. + STDMETHODIMP Stop(); + STDMETHODIMP Pause(); + + // the start parameter is the difference to be added to the + // sample's stream time to get the reference time for + // its presentation + STDMETHODIMP Run(REFERENCE_TIME tStart); + + // --- helper methods --- + + // return the current stream time - ie find out what + // stream time should be appearing now + virtual HRESULT StreamTime(CRefTime& rtStream); + + // Is the filter currently active? + BOOL IsActive() { + CAutoLock cObjectLock(m_pLock); + return ((m_State == State_Paused) || (m_State == State_Running)); + }; + + // Is this filter stopped (without locking) + BOOL IsStopped() { + return (m_State == State_Stopped); + }; + + // + // --- IBaseFilter methods --- + // + + // pin enumerator + STDMETHODIMP EnumPins( + __deref_out IEnumPins ** ppEnum); + + + // default behaviour of FindPin assumes pin ids are their names + STDMETHODIMP FindPin( + LPCWSTR Id, + __deref_out IPin ** ppPin + ); + + STDMETHODIMP QueryFilterInfo( + __out FILTER_INFO * pInfo); + + STDMETHODIMP JoinFilterGraph( + __inout_opt IFilterGraph * pGraph, + __in_opt LPCWSTR pName); + + // return a Vendor information string. Optional - may return E_NOTIMPL. + // memory returned should be freed using CoTaskMemFree + // default implementation returns E_NOTIMPL + STDMETHODIMP QueryVendorInfo( + __deref_out LPWSTR* pVendorInfo + ); + + // --- helper methods --- + + // send an event notification to the filter graph if we know about it. + // returns S_OK if delivered, S_FALSE if the filter graph does not sink + // events, or an error otherwise. + HRESULT NotifyEvent( + long EventCode, + LONG_PTR EventParam1, + LONG_PTR EventParam2); + + // return the filter graph we belong to + __out_opt IFilterGraph *GetFilterGraph() { + return m_pGraph; + } + + // Request reconnect + // pPin is the pin to reconnect + // pmt is the type to reconnect with - can be NULL + // Calls ReconnectEx on the filter graph + HRESULT ReconnectPin(IPin *pPin, __in_opt AM_MEDIA_TYPE const *pmt); + + // find out the current pin version (used by enumerators) + virtual LONG GetPinVersion(); + void IncrementPinVersion(); + + // you need to supply these to access the pins from the enumerator + // and for default Stop and Pause/Run activation. + virtual int GetPinCount() PURE; + virtual CBasePin *GetPin(int n) PURE; + + // --- IAMovieSetup methods --- + + STDMETHODIMP Register(); // ask filter to register itself + STDMETHODIMP Unregister(); // and unregister itself + + // --- setup helper methods --- + // (override to return filters setup data) + + virtual __out_opt LPAMOVIESETUP_FILTER GetSetupData(){ return NULL; } + +}; + + +//===================================================================== +//===================================================================== +// Defines CBasePin +// +// Abstract class that supports the basics of IPin +//===================================================================== +//===================================================================== + +class AM_NOVTABLE CBasePin : public CUnknown, public IPin, public IQualityControl +{ + +protected: + + WCHAR * m_pName; // This pin's name + IPin *m_Connected; // Pin we have connected to + PIN_DIRECTION m_dir; // Direction of this pin + CCritSec *m_pLock; // Object we use for locking + bool m_bRunTimeError; // Run time error generated + bool m_bCanReconnectWhenActive; // OK to reconnect when active + bool m_bTryMyTypesFirst; // When connecting enumerate + // this pin's types first + CBaseFilter *m_pFilter; // Filter we were created by + IQualityControl *m_pQSink; // Target for Quality messages + LONG m_TypeVersion; // Holds current type version + CMediaType m_mt; // Media type of connection + + CRefTime m_tStart; // time from NewSegment call + CRefTime m_tStop; // time from NewSegment + double m_dRate; // rate from NewSegment + +#ifdef DEBUG + LONG m_cRef; // Ref count tracing +#endif + + // displays pin connection information + +#ifdef DEBUG + void DisplayPinInfo(IPin *pReceivePin); + void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt); +#else + void DisplayPinInfo(IPin *pReceivePin) {}; + void DisplayTypeInfo(IPin *pPin, const CMediaType *pmt) {}; +#endif + + // used to agree a media type for a pin connection + + // given a specific media type, attempt a connection (includes + // checking that the type is acceptable to this pin) + HRESULT + AttemptConnection( + IPin* pReceivePin, // connect to this pin + const CMediaType* pmt // using this type + ); + + // try all the media types in this enumerator - for each that + // we accept, try to connect using ReceiveConnection. + HRESULT TryMediaTypes( + IPin *pReceivePin, // connect to this pin + __in_opt const CMediaType *pmt, // proposed type from Connect + IEnumMediaTypes *pEnum); // try this enumerator + + // establish a connection with a suitable mediatype. Needs to + // propose a media type if the pmt pointer is null or partially + // specified - use TryMediaTypes on both our and then the other pin's + // enumerator until we find one that works. + HRESULT AgreeMediaType( + IPin *pReceivePin, // connect to this pin + const CMediaType *pmt); // proposed type from Connect + +public: + + CBasePin( + __in_opt LPCTSTR pObjectName, // Object description + __in CBaseFilter *pFilter, // Owning filter who knows about pins + __in CCritSec *pLock, // Object who implements the lock + __inout HRESULT *phr, // General OLE return code + __in_opt LPCWSTR pName, // Pin name for us + PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT +#ifdef UNICODE + CBasePin( + __in_opt LPCSTR pObjectName, // Object description + __in CBaseFilter *pFilter, // Owning filter who knows about pins + __in CCritSec *pLock, // Object who implements the lock + __inout HRESULT *phr, // General OLE return code + __in_opt LPCWSTR pName, // Pin name for us + PIN_DIRECTION dir); // Either PINDIR_INPUT or PINDIR_OUTPUT +#endif + virtual ~CBasePin(); + + DECLARE_IUNKNOWN + + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); + STDMETHODIMP_(ULONG) NonDelegatingRelease(); + STDMETHODIMP_(ULONG) NonDelegatingAddRef(); + + // --- IPin methods --- + + // take lead role in establishing a connection. Media type pointer + // may be null, or may point to partially-specified mediatype + // (subtype or format type may be GUID_NULL). + STDMETHODIMP Connect( + IPin * pReceivePin, + __in_opt const AM_MEDIA_TYPE *pmt // optional media type + ); + + // (passive) accept a connection from another pin + STDMETHODIMP ReceiveConnection( + IPin * pConnector, // this is the initiating connecting pin + const AM_MEDIA_TYPE *pmt // this is the media type we will exchange + ); + + STDMETHODIMP Disconnect(); + + STDMETHODIMP ConnectedTo(__deref_out IPin **pPin); + + STDMETHODIMP ConnectionMediaType(__out AM_MEDIA_TYPE *pmt); + + STDMETHODIMP QueryPinInfo( + __out PIN_INFO * pInfo + ); + + STDMETHODIMP QueryDirection( + __out PIN_DIRECTION * pPinDir + ); + + STDMETHODIMP QueryId( + __deref_out LPWSTR * Id + ); + + // does the pin support this media type + STDMETHODIMP QueryAccept( + const AM_MEDIA_TYPE *pmt + ); + + // return an enumerator for this pins preferred media types + STDMETHODIMP EnumMediaTypes( + __deref_out IEnumMediaTypes **ppEnum + ); + + // return an array of IPin* - the pins that this pin internally connects to + // All pins put in the array must be AddReffed (but no others) + // Errors: "Can't say" - FAIL, not enough slots - return S_FALSE + // Default: return E_NOTIMPL + // The filter graph will interpret NOT_IMPL as any input pin connects to + // all visible output pins and vice versa. + // apPin can be NULL if nPin==0 (not otherwise). + STDMETHODIMP QueryInternalConnections( + __out_ecount_part(*nPin,*nPin) IPin* *apPin, // array of IPin* + __inout ULONG *nPin // on input, the number of slots + // on output the number of pins + ) { return E_NOTIMPL; } + + // Called when no more data will be sent + STDMETHODIMP EndOfStream(void); + + // Begin/EndFlush still PURE + + // NewSegment notifies of the start/stop/rate applying to the data + // about to be received. Default implementation records data and + // returns S_OK. + // Override this to pass downstream. + STDMETHODIMP NewSegment( + REFERENCE_TIME tStart, + REFERENCE_TIME tStop, + double dRate); + + //================================================================================ + // IQualityControl methods + //================================================================================ + + STDMETHODIMP Notify(IBaseFilter * pSender, Quality q); + + STDMETHODIMP SetSink(IQualityControl * piqc); + + // --- helper methods --- + + // Returns true if the pin is connected. false otherwise. + BOOL IsConnected(void) {return (m_Connected != NULL); }; + // Return the pin this is connected to (if any) + IPin * GetConnected() { return m_Connected; }; + + // Check if our filter is currently stopped + BOOL IsStopped() { + return (m_pFilter->m_State == State_Stopped); + }; + + // find out the current type version (used by enumerators) + virtual LONG GetMediaTypeVersion(); + void IncrementTypeVersion(); + + // switch the pin to active (paused or running) mode + // not an error to call this if already active + virtual HRESULT Active(void); + + // switch the pin to inactive state - may already be inactive + virtual HRESULT Inactive(void); + + // Notify of Run() from filter + virtual HRESULT Run(REFERENCE_TIME tStart); + + // check if the pin can support this specific proposed type and format + virtual HRESULT CheckMediaType(const CMediaType *) PURE; + + // set the connection to use this format (previously agreed) + virtual HRESULT SetMediaType(const CMediaType *); + + // check that the connection is ok before verifying it + // can be overridden eg to check what interfaces will be supported. + virtual HRESULT CheckConnect(IPin *); + + // Set and release resources required for a connection + virtual HRESULT BreakConnect(); + virtual HRESULT CompleteConnect(IPin *pReceivePin); + + // returns the preferred formats for a pin + virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType); + + // access to NewSegment values + REFERENCE_TIME CurrentStopTime() { + return m_tStop; + } + REFERENCE_TIME CurrentStartTime() { + return m_tStart; + } + double CurrentRate() { + return m_dRate; + } + + // Access name + LPWSTR Name() { return m_pName; }; + + // Can reconnectwhen active? + void SetReconnectWhenActive(bool bCanReconnect) + { + m_bCanReconnectWhenActive = bCanReconnect; + } + + bool CanReconnectWhenActive() + { + return m_bCanReconnectWhenActive; + } + +protected: + STDMETHODIMP DisconnectInternal(); +}; + + +//===================================================================== +//===================================================================== +// Defines CEnumPins +// +// Pin enumerator class that works by calling CBaseFilter. This interface +// is provided by CBaseFilter::EnumPins and calls GetPinCount() and +// GetPin() to enumerate existing pins. Needs to be a separate object so +// that it can be cloned (creating an existing object at the same +// position in the enumeration) +// +//===================================================================== +//===================================================================== + +class CEnumPins : public IEnumPins // The interface we support +{ + int m_Position; // Current ordinal position + int m_PinCount; // Number of pins available + CBaseFilter *m_pFilter; // The filter who owns us + LONG m_Version; // Pin version information + LONG m_cRef; + + typedef CGenericList CPinList; + + CPinList m_PinCache; // These pointers have not been AddRef'ed and + // so they should not be dereferenced. They are + // merely kept to ID which pins have been enumerated. + +#ifdef DEBUG + DWORD m_dwCookie; +#endif + + /* If while we are retrieving a pin for example from the filter an error + occurs we assume that our internal state is stale with respect to the + filter (someone may have deleted all the pins). We can check before + starting whether or not the operation is likely to fail by asking the + filter what it's current version number is. If the filter has not + overriden the GetPinVersion method then this will always match */ + + BOOL AreWeOutOfSync() { + return (m_pFilter->GetPinVersion() == m_Version ? FALSE : TRUE); + }; + + /* This method performs the same operations as Reset, except is does not clear + the cache of pins already enumerated. */ + + STDMETHODIMP Refresh(); + +public: + + CEnumPins( + __in CBaseFilter *pFilter, + __in_opt CEnumPins *pEnumPins); + + virtual ~CEnumPins(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IEnumPins + STDMETHODIMP Next( + ULONG cPins, // place this many pins... + __out_ecount(cPins) IPin ** ppPins, // ...in this array of IPin* + __out_opt ULONG * pcFetched // actual count passed returned here + ); + + STDMETHODIMP Skip(ULONG cPins); + STDMETHODIMP Reset(); + STDMETHODIMP Clone(__deref_out IEnumPins **ppEnum); + + +}; + + +//===================================================================== +//===================================================================== +// Defines CEnumMediaTypes +// +// Enumerates the preferred formats for input and output pins +//===================================================================== +//===================================================================== + +class CEnumMediaTypes : public IEnumMediaTypes // The interface we support +{ + int m_Position; // Current ordinal position + CBasePin *m_pPin; // The pin who owns us + LONG m_Version; // Media type version value + LONG m_cRef; +#ifdef DEBUG + DWORD m_dwCookie; +#endif + + /* The media types a filter supports can be quite dynamic so we add to + the general IEnumXXXX interface the ability to be signaled when they + change via an event handle the connected filter supplies. Until the + Reset method is called after the state changes all further calls to + the enumerator (except Reset) will return E_UNEXPECTED error code */ + + BOOL AreWeOutOfSync() { + return (m_pPin->GetMediaTypeVersion() == m_Version ? FALSE : TRUE); + }; + +public: + + CEnumMediaTypes( + __in CBasePin *pPin, + __in_opt CEnumMediaTypes *pEnumMediaTypes); + + virtual ~CEnumMediaTypes(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IEnumMediaTypes + STDMETHODIMP Next( + ULONG cMediaTypes, // place this many pins... + __out_ecount(cMediaTypes) AM_MEDIA_TYPE ** ppMediaTypes, // ...in this array + __out_opt ULONG * pcFetched // actual count passed + ); + + STDMETHODIMP Skip(ULONG cMediaTypes); + STDMETHODIMP Reset(); + STDMETHODIMP Clone(__deref_out IEnumMediaTypes **ppEnum); +}; + + + + +//===================================================================== +//===================================================================== +// Defines CBaseOutputPin +// +// class derived from CBasePin that can pass buffers to a connected pin +// that supports IMemInputPin. Supports IPin. +// +// Derive your output pin from this. +// +//===================================================================== +//===================================================================== + +class AM_NOVTABLE CBaseOutputPin : public CBasePin +{ + +protected: + + IMemAllocator *m_pAllocator; + IMemInputPin *m_pInputPin; // interface on the downstreaminput pin + // set up in CheckConnect when we connect. + +public: + + CBaseOutputPin( + __in_opt LPCTSTR pObjectName, + __in CBaseFilter *pFilter, + __in CCritSec *pLock, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); +#ifdef UNICODE + CBaseOutputPin( + __in_opt LPCSTR pObjectName, + __in CBaseFilter *pFilter, + __in CCritSec *pLock, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); +#endif + // override CompleteConnect() so we can negotiate an allocator + virtual HRESULT CompleteConnect(IPin *pReceivePin); + + // negotiate the allocator and its buffer size/count and other properties + // Calls DecideBufferSize to set properties + virtual HRESULT DecideAllocator(IMemInputPin * pPin, __deref_out IMemAllocator ** pAlloc); + + // override this to set the buffer size and count. Return an error + // if the size/count is not to your liking. + // The allocator properties passed in are those requested by the + // input pin - use eg the alignment and prefix members if you have + // no preference on these. + virtual HRESULT DecideBufferSize( + IMemAllocator * pAlloc, + __inout ALLOCATOR_PROPERTIES * ppropInputRequest + ) PURE; + + // returns an empty sample buffer from the allocator + virtual HRESULT GetDeliveryBuffer(__deref_out IMediaSample ** ppSample, + __in_opt REFERENCE_TIME * pStartTime, + __in_opt REFERENCE_TIME * pEndTime, + DWORD dwFlags); + + // deliver a filled-in sample to the connected input pin + // note - you need to release it after calling this. The receiving + // pin will addref the sample if it needs to hold it beyond the + // call. + virtual HRESULT Deliver(IMediaSample *); + + // override this to control the connection + virtual HRESULT InitAllocator(__deref_out IMemAllocator **ppAlloc); + HRESULT CheckConnect(IPin *pPin); + HRESULT BreakConnect(); + + // override to call Commit and Decommit + HRESULT Active(void); + HRESULT Inactive(void); + + // we have a default handling of EndOfStream which is to return + // an error, since this should be called on input pins only + STDMETHODIMP EndOfStream(void); + + // called from elsewhere in our filter to pass EOS downstream to + // our connected input pin + virtual HRESULT DeliverEndOfStream(void); + + // same for Begin/EndFlush - we handle Begin/EndFlush since it + // is an error on an output pin, and we have Deliver methods to + // call the methods on the connected pin + STDMETHODIMP BeginFlush(void); + STDMETHODIMP EndFlush(void); + virtual HRESULT DeliverBeginFlush(void); + virtual HRESULT DeliverEndFlush(void); + + // deliver NewSegment to connected pin - you will need to + // override this if you queue any data in your output pin. + virtual HRESULT DeliverNewSegment( + REFERENCE_TIME tStart, + REFERENCE_TIME tStop, + double dRate); + + //================================================================================ + // IQualityControl methods + //================================================================================ + + // All inherited from CBasePin and not overridden here. + // STDMETHODIMP Notify(IBaseFilter * pSender, Quality q); + // STDMETHODIMP SetSink(IQualityControl * piqc); +}; + + +//===================================================================== +//===================================================================== +// Defines CBaseInputPin +// +// derive your standard input pin from this. +// you need to supply GetMediaType and CheckConnect etc (see CBasePin), +// and you need to supply Receive to do something more useful. +// +//===================================================================== +//===================================================================== + +class AM_NOVTABLE CBaseInputPin : public CBasePin, + public IMemInputPin +{ + +protected: + + IMemAllocator *m_pAllocator; // Default memory allocator + + // allocator is read-only, so received samples + // cannot be modified (probably only relevant to in-place + // transforms + BYTE m_bReadOnly; + + // in flushing state (between BeginFlush and EndFlush) + // if TRUE, all Receives are returned with S_FALSE + BYTE m_bFlushing; + + // Sample properties - initalized in Receive + AM_SAMPLE2_PROPERTIES m_SampleProps; + +public: + + CBaseInputPin( + __in_opt LPCTSTR pObjectName, + __in CBaseFilter *pFilter, + __in CCritSec *pLock, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); +#ifdef UNICODE + CBaseInputPin( + __in_opt LPCSTR pObjectName, + __in CBaseFilter *pFilter, + __in CCritSec *pLock, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); +#endif + virtual ~CBaseInputPin(); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + // return the allocator interface that this input pin + // would like the output pin to use + STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator); + + // tell the input pin which allocator the output pin is actually + // going to use. + STDMETHODIMP NotifyAllocator( + IMemAllocator * pAllocator, + BOOL bReadOnly); + + // do something with this media sample + STDMETHODIMP Receive(IMediaSample *pSample); + + // do something with these media samples + STDMETHODIMP ReceiveMultiple ( + __in_ecount(nSamples) IMediaSample **pSamples, + long nSamples, + __out long *nSamplesProcessed); + + // See if Receive() blocks + STDMETHODIMP ReceiveCanBlock(); + + // Default handling for BeginFlush - call at the beginning + // of your implementation (makes sure that all Receive calls + // fail). After calling this, you need to free any queued data + // and then call downstream. + STDMETHODIMP BeginFlush(void); + + // default handling for EndFlush - call at end of your implementation + // - before calling this, ensure that there is no queued data and no thread + // pushing any more without a further receive, then call downstream, + // then call this method to clear the m_bFlushing flag and re-enable + // receives + STDMETHODIMP EndFlush(void); + + // this method is optional (can return E_NOTIMPL). + // default implementation returns E_NOTIMPL. Override if you have + // specific alignment or prefix needs, but could use an upstream + // allocator + STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps); + + // Release the pin's allocator. + HRESULT BreakConnect(); + + // helper method to check the read-only flag + BOOL IsReadOnly() { + return m_bReadOnly; + }; + + // helper method to see if we are flushing + BOOL IsFlushing() { + return m_bFlushing; + }; + + // Override this for checking whether it's OK to process samples + // Also call this from EndOfStream. + virtual HRESULT CheckStreaming(); + + // Pass a Quality notification on to the appropriate sink + HRESULT PassNotify(Quality& q); + + + //================================================================================ + // IQualityControl methods (from CBasePin) + //================================================================================ + + STDMETHODIMP Notify(IBaseFilter * pSender, Quality q); + + // no need to override: + // STDMETHODIMP SetSink(IQualityControl * piqc); + + + // switch the pin to inactive state - may already be inactive + virtual HRESULT Inactive(void); + + // Return sample properties pointer + AM_SAMPLE2_PROPERTIES * SampleProps() { + ASSERT(m_SampleProps.cbData != 0); + return &m_SampleProps; + } + +}; + +/////////////////////////////////////////////////////////////////////////// +// CDynamicOutputPin +// + +class CDynamicOutputPin : public CBaseOutputPin, + public IPinFlowControl +{ +public: +#ifdef UNICODE + CDynamicOutputPin( + __in_opt LPCSTR pObjectName, + __in CBaseFilter *pFilter, + __in CCritSec *pLock, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); +#endif + + CDynamicOutputPin( + __in_opt LPCTSTR pObjectName, + __in CBaseFilter *pFilter, + __in CCritSec *pLock, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); + + ~CDynamicOutputPin(); + + // IUnknown Methods + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + // IPin Methods + STDMETHODIMP Disconnect(void); + + // IPinFlowControl Methods + STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent); + + // Set graph config info + void SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent); + + #ifdef DEBUG + virtual HRESULT Deliver(IMediaSample *pSample); + virtual HRESULT DeliverEndOfStream(void); + virtual HRESULT DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate); + #endif // DEBUG + + HRESULT DeliverBeginFlush(void); + HRESULT DeliverEndFlush(void); + + HRESULT Inactive(void); + HRESULT Active(void); + virtual HRESULT CompleteConnect(IPin *pReceivePin); + + virtual HRESULT StartUsingOutputPin(void); + virtual void StopUsingOutputPin(void); + virtual bool StreamingThreadUsingOutputPin(void); + + HRESULT ChangeOutputFormat + ( + const AM_MEDIA_TYPE *pmt, + REFERENCE_TIME tSegmentStart, + REFERENCE_TIME tSegmentStop, + double dSegmentRate + ); + HRESULT ChangeMediaType(const CMediaType *pmt); + HRESULT DynamicReconnect(const CMediaType *pmt); + +protected: + HRESULT SynchronousBlockOutputPin(void); + HRESULT AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent); + HRESULT UnblockOutputPin(void); + + void BlockOutputPin(void); + void ResetBlockState(void); + + static HRESULT WaitEvent(HANDLE hEvent); + + enum BLOCK_STATE + { + NOT_BLOCKED, + PENDING, + BLOCKED + }; + + // This lock should be held when the following class members are + // being used: m_hNotifyCallerPinBlockedEvent, m_BlockState, + // m_dwBlockCallerThreadID and m_dwNumOutstandingOutputPinUsers. + CCritSec m_BlockStateLock; + + // This event should be signaled when the output pin is + // not blocked. This is a manual reset event. For more + // information on events, see the documentation for + // CreateEvent() in the Windows SDK. + HANDLE m_hUnblockOutputPinEvent; + + // This event will be signaled when block operation succeedes or + // when the user cancels the block operation. The block operation + // can be canceled by calling IPinFlowControl2::Block( 0, NULL ) + // while the block operation is pending. + HANDLE m_hNotifyCallerPinBlockedEvent; + + // The state of the current block operation. + BLOCK_STATE m_BlockState; + + // The ID of the thread which last called IPinFlowControl::Block(). + // For more information on thread IDs, see the documentation for + // GetCurrentThreadID() in the Windows SDK. + DWORD m_dwBlockCallerThreadID; + + // The number of times StartUsingOutputPin() has been sucessfully + // called and a corresponding call to StopUsingOutputPin() has not + // been made. When this variable is greater than 0, the streaming + // thread is calling IPin::NewSegment(), IPin::EndOfStream(), + // IMemInputPin::Receive() or IMemInputPin::ReceiveMultiple(). The + // streaming thread could also be calling: DynamicReconnect(), + // ChangeMediaType() or ChangeOutputFormat(). The output pin cannot + // be blocked while the output pin is being used. + DWORD m_dwNumOutstandingOutputPinUsers; + + // This event should be set when the IMediaFilter::Stop() is called. + // This is a manual reset event. It is also set when the output pin + // delivers a flush to the connected input pin. + HANDLE m_hStopEvent; + IGraphConfig* m_pGraphConfig; + + // TRUE if the output pin's allocator's samples are read only. + // Otherwise FALSE. For more information, see the documentation + // for IMemInputPin::NotifyAllocator(). + BOOL m_bPinUsesReadOnlyAllocator; + +private: + HRESULT Initialize(void); + HRESULT ChangeMediaTypeHelper(const CMediaType *pmt); + + #ifdef DEBUG + void AssertValid(void); + #endif // DEBUG +}; + +class CAutoUsingOutputPin +{ +public: + CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr ); + ~CAutoUsingOutputPin(); + +private: + CDynamicOutputPin* m_pOutputPin; +}; + +inline CAutoUsingOutputPin::CAutoUsingOutputPin( __in CDynamicOutputPin* pOutputPin, __inout HRESULT* phr ) : + m_pOutputPin(NULL) +{ + // The caller should always pass in valid pointers. + ASSERT( NULL != pOutputPin ); + ASSERT( NULL != phr ); + + // Make sure the user initialized phr. + ASSERT( S_OK == *phr ); + + HRESULT hr = pOutputPin->StartUsingOutputPin(); + if( FAILED( hr ) ) + { + *phr = hr; + return; + } + + m_pOutputPin = pOutputPin; +} + +inline CAutoUsingOutputPin::~CAutoUsingOutputPin() +{ + if( NULL != m_pOutputPin ) + { + m_pOutputPin->StopUsingOutputPin(); + } +} + +#ifdef DEBUG + +inline HRESULT CDynamicOutputPin::Deliver(IMediaSample *pSample) +{ + // The caller should call StartUsingOutputPin() before calling this + // method. + ASSERT(StreamingThreadUsingOutputPin()); + + return CBaseOutputPin::Deliver(pSample); +} + +inline HRESULT CDynamicOutputPin::DeliverEndOfStream(void) +{ + // The caller should call StartUsingOutputPin() before calling this + // method. + ASSERT( StreamingThreadUsingOutputPin() ); + + return CBaseOutputPin::DeliverEndOfStream(); +} + +inline HRESULT CDynamicOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) +{ + // The caller should call StartUsingOutputPin() before calling this + // method. + ASSERT(StreamingThreadUsingOutputPin()); + + return CBaseOutputPin::DeliverNewSegment(tStart, tStop, dRate); +} + +#endif // DEBUG + +//===================================================================== +//===================================================================== +// Memory allocators +// +// the shared memory transport between pins requires the input pin +// to provide a memory allocator that can provide sample objects. A +// sample object supports the IMediaSample interface. +// +// CBaseAllocator handles the management of free and busy samples. It +// allocates CMediaSample objects. CBaseAllocator is an abstract class: +// in particular it has no method of initializing the list of free +// samples. CMemAllocator is derived from CBaseAllocator and initializes +// the list of samples using memory from the standard IMalloc interface. +// +// If you want your buffers to live in some special area of memory, +// derive your allocator object from CBaseAllocator. If you derive your +// IMemInputPin interface object from CBaseMemInputPin, you will get +// CMemAllocator-based allocation etc for free and will just need to +// supply the Receive handling, and media type / format negotiation. +//===================================================================== +//===================================================================== + + +//===================================================================== +//===================================================================== +// Defines CMediaSample +// +// an object of this class supports IMediaSample and represents a buffer +// for media data with some associated properties. Releasing it returns +// it to a freelist managed by a CBaseAllocator derived object. +//===================================================================== +//===================================================================== + +class CMediaSample : public IMediaSample2 // The interface we support +{ + +protected: + + friend class CBaseAllocator; + + /* Values for dwFlags - these are used for backward compatiblity + only now - use AM_SAMPLE_xxx + */ + enum { Sample_SyncPoint = 0x01, /* Is this a sync point */ + Sample_Preroll = 0x02, /* Is this a preroll sample */ + Sample_Discontinuity = 0x04, /* Set if start of new segment */ + Sample_TypeChanged = 0x08, /* Has the type changed */ + Sample_TimeValid = 0x10, /* Set if time is valid */ + Sample_MediaTimeValid = 0x20, /* Is the media time valid */ + Sample_TimeDiscontinuity = 0x40, /* Time discontinuity */ + Sample_StopValid = 0x100, /* Stop time valid */ + Sample_ValidFlags = 0x1FF + }; + + /* Properties, the media sample class can be a container for a format + change in which case we take a copy of a type through the SetMediaType + interface function and then return it when GetMediaType is called. As + we do no internal processing on it we leave it as a pointer */ + + DWORD m_dwFlags; /* Flags for this sample */ + /* Type specific flags are packed + into the top word + */ + DWORD m_dwTypeSpecificFlags; /* Media type specific flags */ + __field_ecount_opt(m_cbBuffer) LPBYTE m_pBuffer; /* Pointer to the complete buffer */ + LONG m_lActual; /* Length of data in this sample */ + LONG m_cbBuffer; /* Size of the buffer */ + CBaseAllocator *m_pAllocator; /* The allocator who owns us */ + CMediaSample *m_pNext; /* Chaining in free list */ + REFERENCE_TIME m_Start; /* Start sample time */ + REFERENCE_TIME m_End; /* End sample time */ + LONGLONG m_MediaStart; /* Real media start position */ + LONG m_MediaEnd; /* A difference to get the end */ + AM_MEDIA_TYPE *m_pMediaType; /* Media type change data */ + DWORD m_dwStreamId; /* Stream id */ +public: + LONG m_cRef; /* Reference count */ + + +public: + + CMediaSample( + __in_opt LPCTSTR pName, + __in_opt CBaseAllocator *pAllocator, + __inout_opt HRESULT *phr, + __in_bcount_opt(length) LPBYTE pBuffer = NULL, + LONG length = 0); +#ifdef UNICODE + CMediaSample( + __in_opt LPCSTR pName, + __in_opt CBaseAllocator *pAllocator, + __inout_opt HRESULT *phr, + __in_bcount_opt(length) LPBYTE pBuffer = NULL, + LONG length = 0); +#endif + + virtual ~CMediaSample(); + + /* Note the media sample does not delegate to its owner */ + + STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // set the buffer pointer and length. Used by allocators that + // want variable sized pointers or pointers into already-read data. + // This is only available through a CMediaSample* not an IMediaSample* + // and so cannot be changed by clients. + HRESULT SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes); + + // Get me a read/write pointer to this buffer's memory. + STDMETHODIMP GetPointer(__deref_out BYTE ** ppBuffer); + + STDMETHODIMP_(LONG) GetSize(void); + + // get the stream time at which this sample should start and finish. + STDMETHODIMP GetTime( + __out REFERENCE_TIME * pTimeStart, // put time here + __out REFERENCE_TIME * pTimeEnd + ); + + // Set the stream time at which this sample should start and finish. + STDMETHODIMP SetTime( + __in_opt REFERENCE_TIME * pTimeStart, // put time here + __in_opt REFERENCE_TIME * pTimeEnd + ); + STDMETHODIMP IsSyncPoint(void); + STDMETHODIMP SetSyncPoint(BOOL bIsSyncPoint); + STDMETHODIMP IsPreroll(void); + STDMETHODIMP SetPreroll(BOOL bIsPreroll); + + STDMETHODIMP_(LONG) GetActualDataLength(void); + STDMETHODIMP SetActualDataLength(LONG lActual); + + // these allow for limited format changes in band + + STDMETHODIMP GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType); + STDMETHODIMP SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType); + + // returns S_OK if there is a discontinuity in the data (this same is + // not a continuation of the previous stream of data + // - there has been a seek). + STDMETHODIMP IsDiscontinuity(void); + // set the discontinuity property - TRUE if this sample is not a + // continuation, but a new sample after a seek. + STDMETHODIMP SetDiscontinuity(BOOL bDiscontinuity); + + // get the media times for this sample + STDMETHODIMP GetMediaTime( + __out LONGLONG * pTimeStart, + __out LONGLONG * pTimeEnd + ); + + // Set the media times for this sample + STDMETHODIMP SetMediaTime( + __in_opt LONGLONG * pTimeStart, + __in_opt LONGLONG * pTimeEnd + ); + + // Set and get properties (IMediaSample2) + STDMETHODIMP GetProperties( + DWORD cbProperties, + __out_bcount(cbProperties) BYTE * pbProperties + ); + + STDMETHODIMP SetProperties( + DWORD cbProperties, + __in_bcount(cbProperties) const BYTE * pbProperties + ); +}; + + +//===================================================================== +//===================================================================== +// Defines CBaseAllocator +// +// Abstract base class that manages a list of media samples +// +// This class provides support for getting buffers from the free list, +// including handling of commit and (asynchronous) decommit. +// +// Derive from this class and override the Alloc and Free functions to +// allocate your CMediaSample (or derived) objects and add them to the +// free list, preparing them as necessary. +//===================================================================== +//===================================================================== + +class AM_NOVTABLE CBaseAllocator : public CUnknown,// A non delegating IUnknown + public IMemAllocatorCallbackTemp, // The interface we support + public CCritSec // Provides object locking +{ + class CSampleList; + friend class CSampleList; + + /* Trick to get at protected member in CMediaSample */ + static CMediaSample * &NextSample(__in CMediaSample *pSample) + { + return pSample->m_pNext; + }; + + /* Mini list class for the free list */ + class CSampleList + { + public: + CSampleList() : m_List(NULL), m_nOnList(0) {}; +#ifdef DEBUG + ~CSampleList() + { + ASSERT(m_nOnList == 0); + }; +#endif + CMediaSample *Head() const { return m_List; }; + CMediaSample *Next(__in CMediaSample *pSample) const { return CBaseAllocator::NextSample(pSample); }; + int GetCount() const { return m_nOnList; }; + void Add(__inout CMediaSample *pSample) + { + ASSERT(pSample != NULL); + CBaseAllocator::NextSample(pSample) = m_List; + m_List = pSample; + m_nOnList++; + }; + CMediaSample *RemoveHead() + { + CMediaSample *pSample = m_List; + if (pSample != NULL) { + m_List = CBaseAllocator::NextSample(m_List); + m_nOnList--; + } + return pSample; + }; + void Remove(__inout CMediaSample *pSample); + + public: + CMediaSample *m_List; + int m_nOnList; + }; +protected: + + CSampleList m_lFree; // Free list + + /* Note to overriders of CBaseAllocator. + + We use a lazy signalling mechanism for waiting for samples. + This means we don't call the OS if no waits occur. + + In order to implement this: + + 1. When a new sample is added to m_lFree call NotifySample() which + calls ReleaseSemaphore on m_hSem with a count of m_lWaiting and + sets m_lWaiting to 0. + This must all be done holding the allocator's critical section. + + 2. When waiting for a sample call SetWaiting() which increments + m_lWaiting BEFORE leaving the allocator's critical section. + + 3. Actually wait by calling WaitForSingleObject(m_hSem, INFINITE) + having left the allocator's critical section. The effect of + this is to remove 1 from the semaphore's count. You MUST call + this once having incremented m_lWaiting. + + The following are then true when the critical section is not held : + (let nWaiting = number about to wait or waiting) + + (1) if (m_lFree.GetCount() != 0) then (m_lWaiting == 0) + (2) m_lWaiting + Semaphore count == nWaiting + + We would deadlock if + nWaiting != 0 && + m_lFree.GetCount() != 0 && + Semaphore count == 0 + + But from (1) if m_lFree.GetCount() != 0 then m_lWaiting == 0 so + from (2) Semaphore count == nWaiting (which is non-0) so the + deadlock can't happen. + */ + + HANDLE m_hSem; // For signalling + long m_lWaiting; // Waiting for a free element + long m_lCount; // how many buffers we have agreed to provide + long m_lAllocated; // how many buffers are currently allocated + long m_lSize; // agreed size of each buffer + long m_lAlignment; // agreed alignment + long m_lPrefix; // agreed prefix (preceeds GetPointer() value) + BOOL m_bChanged; // Have the buffer requirements changed + + // if true, we are decommitted and can't allocate memory + BOOL m_bCommitted; + // if true, the decommit has happened, but we haven't called Free yet + // as there are still outstanding buffers + BOOL m_bDecommitInProgress; + + // Notification interface + IMemAllocatorNotifyCallbackTemp *m_pNotify; + + BOOL m_fEnableReleaseCallback; + + // called to decommit the memory when the last buffer is freed + // pure virtual - need to override this + virtual void Free(void) PURE; + + // override to allocate the memory when commit called + virtual HRESULT Alloc(void); + +public: + + CBaseAllocator( + __in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *, + BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE); +#ifdef UNICODE + CBaseAllocator( + __in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *, + BOOL bEvent = TRUE, BOOL fEnableReleaseCallback = FALSE); +#endif + virtual ~CBaseAllocator(); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + STDMETHODIMP SetProperties( + __in ALLOCATOR_PROPERTIES* pRequest, + __out ALLOCATOR_PROPERTIES* pActual); + + // return the properties actually being used on this allocator + STDMETHODIMP GetProperties( + __out ALLOCATOR_PROPERTIES* pProps); + + // override Commit to allocate memory. We handle the GetBuffer + //state changes + STDMETHODIMP Commit(); + + // override this to handle the memory freeing. We handle any outstanding + // GetBuffer calls + STDMETHODIMP Decommit(); + + // get container for a sample. Blocking, synchronous call to get the + // next free buffer (as represented by an IMediaSample interface). + // on return, the time etc properties will be invalid, but the buffer + // pointer and size will be correct. The two time parameters are + // optional and either may be NULL, they may alternatively be set to + // the start and end times the sample will have attached to it + // bPrevFramesSkipped is not used (used only by the video renderer's + // allocator where it affects quality management in direct draw). + + STDMETHODIMP GetBuffer(__deref_out IMediaSample **ppBuffer, + __in_opt REFERENCE_TIME * pStartTime, + __in_opt REFERENCE_TIME * pEndTime, + DWORD dwFlags); + + // final release of a CMediaSample will call this + STDMETHODIMP ReleaseBuffer(IMediaSample *pBuffer); + // obsolete:: virtual void PutOnFreeList(CMediaSample * pSample); + + STDMETHODIMP SetNotify(IMemAllocatorNotifyCallbackTemp *pNotify); + + STDMETHODIMP GetFreeCount(__out LONG *plBuffersFree); + + // Notify that a sample is available + void NotifySample(); + + // Notify that we're waiting for a sample + void SetWaiting() { m_lWaiting++; }; +}; + + +//===================================================================== +//===================================================================== +// Defines CMemAllocator +// +// this is an allocator based on CBaseAllocator that allocates sample +// buffers in main memory (from 'new'). You must call SetProperties +// before calling Commit. +// +// we don't free the memory when going into Decommit state. The simplest +// way to implement this without complicating CBaseAllocator is to +// have a Free() function, called to go into decommit state, that does +// nothing and a ReallyFree function called from our destructor that +// actually frees the memory. +//===================================================================== +//===================================================================== + +// Make me one from quartz.dll +STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator); + +class CMemAllocator : public CBaseAllocator +{ + +protected: + + LPBYTE m_pBuffer; // combined memory for all buffers + + // override to free the memory when decommit completes + // - we actually do nothing, and save the memory until deletion. + void Free(void); + + // called from the destructor (and from Alloc if changing size/count) to + // actually free up the memory + void ReallyFree(void); + + // overriden to allocate the memory when commit called + HRESULT Alloc(void); + +public: + /* This goes in the factory template table to create new instances */ + static CUnknown *CreateInstance(__inout_opt LPUNKNOWN, __inout HRESULT *); + + STDMETHODIMP SetProperties( + __in ALLOCATOR_PROPERTIES* pRequest, + __out ALLOCATOR_PROPERTIES* pActual); + + CMemAllocator(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, __inout HRESULT *); +#ifdef UNICODE + CMemAllocator(__in_opt LPCSTR , __inout_opt LPUNKNOWN, __inout HRESULT *); +#endif + ~CMemAllocator(); +}; + +// helper used by IAMovieSetup implementation +STDAPI +AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata + , IFilterMapper * pIFM + , BOOL bRegister ); + + +/////////////////////////////////////////////////////////////////////////// +// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ +/////////////////////////////////////////////////////////////////////////// + +#endif /* __FILTER__ */ + + + diff --git a/RTSP Source Filter/Common/DirectShow/cache.h b/RTSP Source Filter/Common/DirectShow/cache.h new file mode 100644 index 0000000..a2d5752 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/cache.h @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// File: Cache.h +// +// Desc: DirectShow base classes - efines a non-MFC generic cache class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +/* This class implements a simple cache. A cache object is instantiated + with the number of items it is to hold. An item is a pointer to an + object derived from CBaseObject (helps reduce memory leaks). The cache + can then have objects added to it and removed from it. The cache size + is fixed at construction time and may therefore run out or be flooded. + If it runs out it returns a NULL pointer, if it fills up it also returns + a NULL pointer instead of a pointer to the object just inserted */ + +/* Making these classes inherit from CBaseObject does nothing for their + functionality but it allows us to check there are no memory leaks */ + +/* WARNING Be very careful when using this class, what it lets you do is + store and retrieve objects so that you can minimise object creation + which in turns improves efficiency. However the object you store is + exactly the same as the object you get back which means that it short + circuits the constructor initialisation phase. This means any class + variables the object has (eg pointers) are highly likely to be invalid. + Therefore ensure you reinitialise the object before using it again */ + + +#ifndef __CACHE__ +#define __CACHE__ + + +class CCache : CBaseObject { + + /* Make copy constructor and assignment operator inaccessible */ + + CCache(const CCache &refCache); + CCache &operator=(const CCache &refCache); + +private: + + /* These are initialised in the constructor. The first variable points to + an array of pointers, each of which points to a CBaseObject derived + object. The m_iCacheSize is the static fixed size for the cache and the + m_iUsed defines the number of places filled with objects at any time. + We fill the array of pointers from the start (ie m_ppObjects[0] first) + and then only add and remove objects from the end position, so in this + respect the array of object pointers should be treated as a stack */ + + CBaseObject **m_ppObjects; + const INT m_iCacheSize; + INT m_iUsed; + +public: + + CCache(__in_opt LPCTSTR pName,INT iItems); + virtual ~CCache(); + + /* Add an item to the cache */ + CBaseObject *AddToCache(__in CBaseObject *pObject); + + /* Remove an item from the cache */ + CBaseObject *RemoveFromCache(); + + /* Delete all the objects held in the cache */ + void RemoveAll(void); + + /* Return the cache size which is set during construction */ + INT GetCacheSize(void) const {return m_iCacheSize;}; +}; + +#endif /* __CACHE__ */ + diff --git a/RTSP Source Filter/Common/DirectShow/checkbmi.h b/RTSP Source Filter/Common/DirectShow/checkbmi.h new file mode 100644 index 0000000..9761dae --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/checkbmi.h @@ -0,0 +1,120 @@ +// Copyright (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved. + +#ifndef _CHECKBMI_H_ +#define _CHECKBMI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Helper +__inline BOOL MultiplyCheckOverflow(DWORD a, DWORD b, __deref_out_range(==, a * b) DWORD *pab) { + *pab = a * b; + if ((a == 0) || (((*pab) / a) == b)) { + return TRUE; + } + return FALSE; +} + + +// Checks if the fields in a BITMAPINFOHEADER won't generate +// overlows and buffer overruns +// This is not a complete check and does not guarantee code using this structure will be secure +// from attack +// Bugs this is guarding against: +// 1. Total structure size calculation overflowing +// 2. biClrUsed > 256 for 8-bit palettized content +// 3. Total bitmap size in bytes overflowing +// 4. biSize < size of the base structure leading to accessessing random memory +// 5. Total structure size exceeding know size of data +// + +__success(return != 0) __inline BOOL ValidateBitmapInfoHeader( + const BITMAPINFOHEADER *pbmi, // pointer to structure to check + __out_range(>=, sizeof(BITMAPINFOHEADER)) DWORD cbSize // size of memory block containing structure +) +{ + DWORD dwWidthInBytes; + DWORD dwBpp; + DWORD dwWidthInBits; + DWORD dwHeight; + DWORD dwSizeImage; + DWORD dwClrUsed; + + // Reject bad parameters - do the size check first to avoid reading bad memory + if (cbSize < sizeof(BITMAPINFOHEADER) || + pbmi->biSize < sizeof(BITMAPINFOHEADER) || + pbmi->biSize > 4096) { + return FALSE; + } + + // Reject 0 size + if (pbmi->biWidth == 0 || pbmi->biHeight == 0) { + return FALSE; + } + + // Use bpp of 200 for validating against further overflows if not set for compressed format + dwBpp = 200; + + if (pbmi->biBitCount > dwBpp) { + return FALSE; + } + + // Strictly speaking abs can overflow so cast explicitly to DWORD + dwHeight = (DWORD)abs(pbmi->biHeight); + + if (!MultiplyCheckOverflow(dwBpp, (DWORD)pbmi->biWidth, &dwWidthInBits)) { + return FALSE; + } + + // Compute correct width in bytes - rounding up to 4 bytes + dwWidthInBytes = (dwWidthInBits / 8 + 3) & ~3; + + if (!MultiplyCheckOverflow(dwWidthInBytes, dwHeight, &dwSizeImage)) { + return FALSE; + } + + // Fail if total size is 0 - this catches indivual quantities being 0 + // Also don't allow huge values > 1GB which might cause arithmetic + // errors for users + if (dwSizeImage > 0x40000000 || + pbmi->biSizeImage > 0x40000000) { + return FALSE; + } + + // Fail if biClrUsed looks bad + if (pbmi->biClrUsed > 256) { + return FALSE; + } + + if (pbmi->biClrUsed == 0 && pbmi->biBitCount <= 8 && pbmi->biBitCount > 0) { + dwClrUsed = (1 << pbmi->biBitCount); + } else { + dwClrUsed = pbmi->biClrUsed; + } + + // Check total size + if (cbSize < pbmi->biSize + dwClrUsed * sizeof(RGBQUAD) + + (pbmi->biCompression == BI_BITFIELDS ? 3 * sizeof(DWORD) : 0)) { + return FALSE; + } + + // If it is RGB validate biSizeImage - lots of code assumes the size is correct + if (pbmi->biCompression == BI_RGB || pbmi->biCompression == BI_BITFIELDS) { + if (pbmi->biSizeImage != 0) { + DWORD dwBits = (DWORD)pbmi->biWidth * (DWORD)pbmi->biBitCount; + DWORD dwWidthInBytes = ((DWORD)((dwBits+31) & (~31)) / 8); + DWORD dwTotalSize = (DWORD)abs(pbmi->biHeight) * dwWidthInBytes; + if (dwTotalSize > pbmi->biSizeImage) { + return FALSE; + } + } + } + return TRUE; +} + +#ifdef __cplusplus +} +#endif + +#endif // _CHECKBMI_H_ diff --git a/RTSP Source Filter/Common/DirectShow/combase.h b/RTSP Source Filter/Common/DirectShow/combase.h new file mode 100644 index 0000000..44ca535 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/combase.h @@ -0,0 +1,305 @@ +//------------------------------------------------------------------------------ +// File: ComBase.h +// +// Desc: DirectShow base classes - defines a class hierarchy for creating +// COM objects. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +/* + +a. Derive your COM object from CUnknown + +b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT * + and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls + to. The HRESULT * allows error codes to be passed around constructors and + the TCHAR * is a descriptive name that can be printed on the debugger. + + It is important that constructors only change the HRESULT * if they have + to set an ERROR code, if it was successful then leave it alone or you may + overwrite an error code from an object previously created. + + When you call a constructor the descriptive name should be in static store + as we do not copy the string. To stop large amounts of memory being used + in retail builds by all these static strings use the NAME macro, + + CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr); + if (FAILED(hr)) { + return hr; + } + + In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class + knows not to do anything with objects that don't have a name. + +c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and + TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an + error, or just simply pass it through to the constructor. + + The object creation will fail in the class factory if the HRESULT indicates + an error (ie FAILED(HRESULT) == TRUE) + +d. Create a FactoryTemplate with your object's class id and CreateInstance + function. + +Then (for each interface) either + +Multiple inheritance + +1. Also derive it from ISomeInterface +2. Include DECLARE_IUNKNOWN in your class definition to declare + implementations of QueryInterface, AddRef and Release that + call the outer unknown +3. Override NonDelegatingQueryInterface to expose ISomeInterface by + code something like + + if (riid == IID_ISomeInterface) { + return GetInterface((ISomeInterface *) this, ppv); + } else { + return CUnknown::NonDelegatingQueryInterface(riid, ppv); + } + +4. Declare and implement the member functions of ISomeInterface. + +or: Nested interfaces + +1. Declare a class derived from CUnknown +2. Include DECLARE_IUNKNOWN in your class definition +3. Override NonDelegatingQueryInterface to expose ISomeInterface by + code something like + + if (riid == IID_ISomeInterface) { + return GetInterface((ISomeInterface *) this, ppv); + } else { + return CUnknown::NonDelegatingQueryInterface(riid, ppv); + } + +4. Implement the member functions of ISomeInterface. Use GetOwner() to + access the COM object class. + +And in your COM object class: + +5. Make the nested class a friend of the COM object class, and declare + an instance of the nested class as a member of the COM object class. + + NOTE that because you must always pass the outer unknown and an hResult + to the CUnknown constructor you cannot use a default constructor, in + other words you will have to make the member variable a pointer to the + class and make a NEW call in your constructor to actually create it. + +6. override the NonDelegatingQueryInterface with code like this: + + if (riid == IID_ISomeInterface) { + return m_pImplFilter-> + NonDelegatingQueryInterface(IID_ISomeInterface, ppv); + } else { + return CUnknown::NonDelegatingQueryInterface(riid, ppv); + } + +You can have mixed classes which support some interfaces via multiple +inheritance and some via nested classes + +*/ + +#ifndef __COMBASE__ +#define __COMBASE__ + +// Filter Setup data structures no defined in axextend.idl + +typedef REGPINTYPES +AMOVIESETUP_MEDIATYPE, * PAMOVIESETUP_MEDIATYPE, * FAR LPAMOVIESETUP_MEDIATYPE; + +typedef REGFILTERPINS +AMOVIESETUP_PIN, * PAMOVIESETUP_PIN, * FAR LPAMOVIESETUP_PIN; + +typedef struct _AMOVIESETUP_FILTER +{ + const CLSID * clsID; + const WCHAR * strName; + DWORD dwMerit; + UINT nPins; + const AMOVIESETUP_PIN * lpPin; +} +AMOVIESETUP_FILTER, * PAMOVIESETUP_FILTER, * FAR LPAMOVIESETUP_FILTER; + +/* The DLLENTRY module initialises the module handle on loading */ + +extern HINSTANCE g_hInst; + +/* On DLL load remember which platform we are running on */ + +extern DWORD g_amPlatform; +extern OSVERSIONINFO g_osInfo; // Filled in by GetVersionEx + +/* Version of IUnknown that is renamed to allow a class to support both + non delegating and delegating IUnknowns in the same COM object */ + +#ifndef INONDELEGATINGUNKNOWN_DEFINED +DECLARE_INTERFACE(INonDelegatingUnknown) +{ + STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE; + STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE; +}; +#define INONDELEGATINGUNKNOWN_DEFINED +#endif + +typedef INonDelegatingUnknown *PNDUNKNOWN; + + +/* This is the base object class that supports active object counting. As + part of the debug facilities we trace every time a C++ object is created + or destroyed. The name of the object has to be passed up through the class + derivation list during construction as you cannot call virtual functions + in the constructor. The downside of all this is that every single object + constructor has to take an object name parameter that describes it */ + +class CBaseObject +{ + +private: + + // Disable the copy constructor and assignment by default so you will get + // compiler errors instead of unexpected behaviour if you pass objects + // by value or assign objects. + CBaseObject(const CBaseObject& objectSrc); // no implementation + void operator=(const CBaseObject& objectSrc); // no implementation + +private: + static LONG m_cObjects; /* Total number of objects active */ + +protected: +#ifdef DEBUG + DWORD m_dwCookie; /* Cookie identifying this object */ +#endif + + +public: + + /* These increment and decrement the number of active objects */ + + CBaseObject(__in_opt LPCTSTR pName); +#ifdef UNICODE + CBaseObject(__in_opt LPCSTR pName); +#endif + ~CBaseObject(); + + /* Call this to find if there are any CUnknown derived objects active */ + + static LONG ObjectsActive() { + return m_cObjects; + }; +}; + + +/* An object that supports one or more COM interfaces will be based on + this class. It supports counting of total objects for DLLCanUnloadNow + support, and an implementation of the core non delegating IUnknown */ + +class AM_NOVTABLE CUnknown : public INonDelegatingUnknown, + public CBaseObject +{ +private: + const LPUNKNOWN m_pUnknown; /* Owner of this object */ + +protected: /* So we can override NonDelegatingRelease() */ + volatile LONG m_cRef; /* Number of reference counts */ + +public: + + CUnknown(__in_opt LPCTSTR pName, __in_opt LPUNKNOWN pUnk); + virtual ~CUnknown() {}; + + // This is redundant, just use the other constructor + // as we never touch the HRESULT in this anyway + CUnknown(__in_opt LPCTSTR Name, __in_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr); +#ifdef UNICODE + CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk); + CUnknown(__in_opt LPCSTR pName, __in_opt LPUNKNOWN pUnk,__inout_opt HRESULT *phr); +#endif + + /* Return the owner of this object */ + + LPUNKNOWN GetOwner() const { + return m_pUnknown; + }; + + /* Called from the class factory to create a new instance, it is + pure virtual so it must be overriden in your derived class */ + + /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */ + + /* Non delegating unknown implementation */ + + STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **); + STDMETHODIMP_(ULONG) NonDelegatingAddRef(); + STDMETHODIMP_(ULONG) NonDelegatingRelease(); +}; + +/* Return an interface pointer to a requesting client + performing a thread safe AddRef as necessary */ + +STDAPI GetInterface(LPUNKNOWN pUnk, __out void **ppv); + +/* A function that can create a new COM object */ + +typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(__in_opt LPUNKNOWN pUnkOuter, __inout_opt HRESULT *phr); + +/* A function (can be NULL) which is called from the DLL entrypoint + routine for each factory template: + + bLoading - TRUE on DLL load, FALSE on DLL unload + rclsid - the m_ClsID of the entry +*/ +typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid); + +/* Create one of these per object class in an array so that + the default class factory code can create new instances */ + +class CFactoryTemplate { + +public: + + const WCHAR * m_Name; + const CLSID * m_ClsID; + LPFNNewCOMObject m_lpfnNew; + LPFNInitRoutine m_lpfnInit; + const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter; + + BOOL IsClassID(REFCLSID rclsid) const { + return (IsEqualCLSID(*m_ClsID,rclsid)); + }; + + CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout_opt HRESULT *phr) const { + CheckPointer(phr,NULL); + return m_lpfnNew(pUnk, phr); + }; +}; + + +/* You must override the (pure virtual) NonDelegatingQueryInterface to return + interface pointers (using GetInterface) to the interfaces your derived + class supports (the default implementation only supports IUnknown) */ + +#define DECLARE_IUNKNOWN \ + STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) { \ + return GetOwner()->QueryInterface(riid,ppv); \ + }; \ + STDMETHODIMP_(ULONG) AddRef() { \ + return GetOwner()->AddRef(); \ + }; \ + STDMETHODIMP_(ULONG) Release() { \ + return GetOwner()->Release(); \ + }; + + + +HINSTANCE LoadOLEAut32(); + + +#endif /* __COMBASE__ */ + + + + diff --git a/RTSP Source Filter/Common/DirectShow/cprop.h b/RTSP Source Filter/Common/DirectShow/cprop.h new file mode 100644 index 0000000..a030f8f --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/cprop.h @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +// File: CProp.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __CPROP__ +#define __CPROP__ + +// Base property page class. Filters typically expose custom properties by +// implementing special control interfaces, examples are IDirectDrawVideo +// and IQualProp on renderers. This allows property pages to be built that +// use the given interface. Applications such as the ActiveMovie OCX query +// filters for the property pages they support and expose them to the user +// +// This class provides all the framework for a property page. A property +// page is a COM object that supports IPropertyPage. We should be created +// with a resource ID for the dialog which we will load when required. We +// should also be given in the constructor a resource ID for a title string +// we will load from the DLLs STRINGTABLE. The property page titles must be +// stored in resource files so that they can be easily internationalised +// +// We have a number of virtual methods (not PURE) that may be overriden in +// derived classes to query for interfaces and so on. These functions have +// simple implementations here that just return NOERROR. Derived classes +// will almost definately have to override the message handler method called +// OnReceiveMessage. We have a static dialog procedure that calls the method +// so that derived classes don't have to fiddle around with the this pointer + +class AM_NOVTABLE CBasePropertyPage : public IPropertyPage, public CUnknown +{ +protected: + + LPPROPERTYPAGESITE m_pPageSite; // Details for our property site + HWND m_hwnd; // Window handle for the page + HWND m_Dlg; // Actual dialog window handle + BOOL m_bDirty; // Has anything been changed + int m_TitleId; // Resource identifier for title + int m_DialogId; // Dialog resource identifier + + static INT_PTR CALLBACK DialogProc(HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +private: + BOOL m_bObjectSet ; // SetObject has been called or not. +public: + + CBasePropertyPage(__in_opt LPCTSTR pName, // Debug only name + __inout_opt LPUNKNOWN pUnk, // COM Delegator + int DialogId, // Resource ID + int TitleId); // To get tital + +#ifdef UNICODE + CBasePropertyPage(__in_opt LPCSTR pName, + __inout_opt LPUNKNOWN pUnk, + int DialogId, + int TitleId); +#endif + virtual ~CBasePropertyPage() { }; + DECLARE_IUNKNOWN + + // Override these virtual methods + + virtual HRESULT OnConnect(IUnknown *pUnknown) { return NOERROR; }; + virtual HRESULT OnDisconnect() { return NOERROR; }; + virtual HRESULT OnActivate() { return NOERROR; }; + virtual HRESULT OnDeactivate() { return NOERROR; }; + virtual HRESULT OnApplyChanges() { return NOERROR; }; + virtual INT_PTR OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); + + // These implement an IPropertyPage interface + + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + STDMETHODIMP_(ULONG) NonDelegatingRelease(); + STDMETHODIMP_(ULONG) NonDelegatingAddRef(); + STDMETHODIMP SetPageSite(__in_opt LPPROPERTYPAGESITE pPageSite); + STDMETHODIMP Activate(HWND hwndParent, LPCRECT prect,BOOL fModal); + STDMETHODIMP Deactivate(void); + STDMETHODIMP GetPageInfo(__out LPPROPPAGEINFO pPageInfo); + STDMETHODIMP SetObjects(ULONG cObjects, __in_ecount_opt(cObjects) LPUNKNOWN *ppUnk); + STDMETHODIMP Show(UINT nCmdShow); + STDMETHODIMP Move(LPCRECT prect); + STDMETHODIMP IsPageDirty(void) { return m_bDirty ? S_OK : S_FALSE; } + STDMETHODIMP Apply(void); + STDMETHODIMP Help(LPCWSTR lpszHelpDir) { return E_NOTIMPL; } + STDMETHODIMP TranslateAccelerator(__inout LPMSG lpMsg) { return E_NOTIMPL; } +}; + +#endif // __CPROP__ + diff --git a/RTSP Source Filter/Common/DirectShow/ctlutil.h b/RTSP Source Filter/Common/DirectShow/ctlutil.h new file mode 100644 index 0000000..e3f7085 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/ctlutil.h @@ -0,0 +1,923 @@ +//------------------------------------------------------------------------------ +// File: CtlUtil.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// Base classes implementing IDispatch parsing for the basic control dual +// interfaces. Derive from these and implement just the custom method and +// property methods. We also implement CPosPassThru that can be used by +// renderers and transforms to pass by IMediaPosition and IMediaSeeking + +#ifndef __CTLUTIL__ +#define __CTLUTIL__ + +// OLE Automation has different ideas of TRUE and FALSE + +#define OATRUE (-1) +#define OAFALSE (0) + + +// It's possible that we could replace this class with CreateStdDispatch + +class CBaseDispatch +{ + ITypeInfo * m_pti; + +public: + + CBaseDispatch() : m_pti(NULL) {} + ~CBaseDispatch(); + + /* IDispatch methods */ + STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo); + + STDMETHODIMP GetTypeInfo( + REFIID riid, + UINT itinfo, + LCID lcid, + __deref_out ITypeInfo ** pptinfo); + + STDMETHODIMP GetIDsOfNames( + REFIID riid, + __in_ecount(cNames) LPOLESTR * rgszNames, + UINT cNames, + LCID lcid, + __out_ecount(cNames) DISPID * rgdispid); +}; + + +class AM_NOVTABLE CMediaControl : + public IMediaControl, + public CUnknown +{ + CBaseDispatch m_basedisp; + +public: + + CMediaControl(const TCHAR *, LPUNKNOWN); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + /* IDispatch methods */ + STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo); + + STDMETHODIMP GetTypeInfo( + UINT itinfo, + LCID lcid, + __deref_out ITypeInfo ** pptinfo); + + STDMETHODIMP GetIDsOfNames( + REFIID riid, + __in_ecount(cNames) LPOLESTR * rgszNames, + UINT cNames, + LCID lcid, + __out_ecount(cNames) DISPID * rgdispid); + + STDMETHODIMP Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + __in DISPPARAMS * pdispparams, + __out_opt VARIANT * pvarResult, + __out_opt EXCEPINFO * pexcepinfo, + __out_opt UINT * puArgErr); +}; + + +class AM_NOVTABLE CMediaEvent : + public IMediaEventEx, + public CUnknown +{ + CBaseDispatch m_basedisp; + +public: + + CMediaEvent(__in_opt LPCTSTR, __in_opt LPUNKNOWN); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + /* IDispatch methods */ + STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo); + + STDMETHODIMP GetTypeInfo( + UINT itinfo, + LCID lcid, + __deref_out ITypeInfo ** pptinfo); + + STDMETHODIMP GetIDsOfNames( + REFIID riid, + __in_ecount(cNames) LPOLESTR * rgszNames, + UINT cNames, + LCID lcid, + __out_ecount(cNames) DISPID * rgdispid); + + STDMETHODIMP Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + __in DISPPARAMS * pdispparams, + __out_opt VARIANT * pvarResult, + __out_opt EXCEPINFO * pexcepinfo, + __out_opt UINT * puArgErr); +}; + + +class AM_NOVTABLE CMediaPosition : + public IMediaPosition, + public CUnknown +{ + CBaseDispatch m_basedisp; + + +public: + + CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN); + CMediaPosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT *phr); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + /* IDispatch methods */ + STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo); + + STDMETHODIMP GetTypeInfo( + UINT itinfo, + LCID lcid, + __deref_out ITypeInfo ** pptinfo); + + STDMETHODIMP GetIDsOfNames( + REFIID riid, + __in_ecount(cNames) LPOLESTR * rgszNames, + UINT cNames, + LCID lcid, + __out_ecount(cNames) DISPID * rgdispid); + + STDMETHODIMP Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + __in DISPPARAMS * pdispparams, + __out_opt VARIANT * pvarResult, + __out_opt EXCEPINFO * pexcepinfo, + __out_opt UINT * puArgErr); + +}; + + +// OA-compatibility means that we must use double as the RefTime value, +// and REFERENCE_TIME (essentially a LONGLONG) within filters. +// this class converts between the two + +class COARefTime : public CRefTime { +public: + + COARefTime() { + }; + + COARefTime(CRefTime t) + : CRefTime(t) + { + }; + + COARefTime(REFERENCE_TIME t) + : CRefTime(t) + { + }; + + COARefTime(double d) { + m_time = (LONGLONG) (d * 10000000); + }; + + operator double() { + return double(m_time) / 10000000; + }; + + operator REFERENCE_TIME() { + return m_time; + }; + + COARefTime& operator=(const double& rd) { + m_time = (LONGLONG) (rd * 10000000); + return *this; + } + + COARefTime& operator=(const REFERENCE_TIME& rt) { + m_time = rt; + return *this; + } + + inline BOOL operator==(const COARefTime& rt) + { + return m_time == rt.m_time; + }; + + inline BOOL operator!=(const COARefTime& rt) + { + return m_time != rt.m_time; + }; + + inline BOOL operator < (const COARefTime& rt) + { + return m_time < rt.m_time; + }; + + inline BOOL operator > (const COARefTime& rt) + { + return m_time > rt.m_time; + }; + + inline BOOL operator >= (const COARefTime& rt) + { + return m_time >= rt.m_time; + }; + + inline BOOL operator <= (const COARefTime& rt) + { + return m_time <= rt.m_time; + }; + + inline COARefTime operator+(const COARefTime& rt) + { + return COARefTime(m_time + rt.m_time); + }; + + inline COARefTime operator-(const COARefTime& rt) + { + return COARefTime(m_time - rt.m_time); + }; + + inline COARefTime operator*(LONG l) + { + return COARefTime(m_time * l); + }; + + inline COARefTime operator/(LONG l) + { + return COARefTime(m_time / l); + }; + +private: + // Prevent bugs from constructing from LONG (which gets + // converted to double and then multiplied by 10000000 + COARefTime(LONG); + LONG operator=(LONG); +}; + + +// A utility class that handles IMediaPosition and IMediaSeeking on behalf +// of single-input pin renderers, or transform filters. +// +// Renderers will expose this from the filter; transform filters will +// expose it from the output pin and not the renderer. +// +// Create one of these, giving it your IPin* for your input pin, and delegate +// all IMediaPosition methods to it. It will query the input pin for +// IMediaPosition and respond appropriately. +// +// Call ForceRefresh if the pin connection changes. +// +// This class no longer caches the upstream IMediaPosition or IMediaSeeking +// it acquires it on each method call. This means ForceRefresh is not needed. +// The method is kept for source compatibility and to minimise the changes +// if we need to put it back later for performance reasons. + +class CPosPassThru : public IMediaSeeking, public CMediaPosition +{ + IPin *m_pPin; + + HRESULT GetPeer(__deref_out IMediaPosition **ppMP); + HRESULT GetPeerSeeking(__deref_out IMediaSeeking **ppMS); + +public: + + CPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *); + DECLARE_IUNKNOWN + + HRESULT ForceRefresh() { + return S_OK; + }; + + // override to return an accurate current position + virtual HRESULT GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime) { + return E_FAIL; + } + + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv); + + // IMediaSeeking methods + STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities ); + STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities ); + STDMETHODIMP SetTimeFormat(const GUID * pFormat); + STDMETHODIMP GetTimeFormat(__out GUID *pFormat); + STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat); + STDMETHODIMP IsFormatSupported( const GUID * pFormat); + STDMETHODIMP QueryPreferredFormat( __out GUID *pFormat); + STDMETHODIMP ConvertTimeFormat(__out LONGLONG * pTarget, + __in_opt const GUID * pTargetFormat, + LONGLONG Source, + __in_opt const GUID * pSourceFormat ); + STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags + , __inout_opt LONGLONG * pStop, DWORD StopFlags ); + + STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop ); + STDMETHODIMP GetCurrentPosition( __out LONGLONG * pCurrent ); + STDMETHODIMP GetStopPosition( __out LONGLONG * pStop ); + STDMETHODIMP SetRate( double dRate); + STDMETHODIMP GetRate( __out double * pdRate); + STDMETHODIMP GetDuration( __out LONGLONG *pDuration); + STDMETHODIMP GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest ); + STDMETHODIMP GetPreroll( __out LONGLONG *pllPreroll ); + + // IMediaPosition properties + STDMETHODIMP get_Duration(__out REFTIME * plength); + STDMETHODIMP put_CurrentPosition(REFTIME llTime); + STDMETHODIMP get_StopTime(__out REFTIME * pllTime); + STDMETHODIMP put_StopTime(REFTIME llTime); + STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime); + STDMETHODIMP put_PrerollTime(REFTIME llTime); + STDMETHODIMP get_Rate(__out double * pdRate); + STDMETHODIMP put_Rate(double dRate); + STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime); + STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward); + STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward); + +private: + HRESULT GetSeekingLongLong( HRESULT (__stdcall IMediaSeeking::*pMethod)( LONGLONG * ), + __out LONGLONG * pll ); +}; + + +// Adds the ability to return a current position + +class CRendererPosPassThru : public CPosPassThru +{ + CCritSec m_PositionLock; // Locks access to our position + LONGLONG m_StartMedia; // Start media time last seen + LONGLONG m_EndMedia; // And likewise the end media + BOOL m_bReset; // Have media times been set + +public: + + // Used to help with passing media times through graph + + CRendererPosPassThru(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, IPin *); + HRESULT RegisterMediaTime(IMediaSample *pMediaSample); + HRESULT RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime); + HRESULT GetMediaTime(__out LONGLONG *pStartTime,__out_opt LONGLONG *pEndTime); + HRESULT ResetMediaTime(); + HRESULT EOS(); +}; + +STDAPI CreatePosPassThru( + __in_opt LPUNKNOWN pAgg, + BOOL bRenderer, + IPin *pPin, + __deref_out IUnknown **ppPassThru +); + +// A class that handles the IDispatch part of IBasicAudio and leaves the +// properties and methods themselves pure virtual. + +class AM_NOVTABLE CBasicAudio : public IBasicAudio, public CUnknown +{ + CBaseDispatch m_basedisp; + +public: + + CBasicAudio(__in_opt LPCTSTR, __in_opt LPUNKNOWN); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + /* IDispatch methods */ + STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo); + + STDMETHODIMP GetTypeInfo( + UINT itinfo, + LCID lcid, + __deref_out ITypeInfo ** pptinfo); + + STDMETHODIMP GetIDsOfNames( + REFIID riid, + __in_ecount(cNames) LPOLESTR * rgszNames, + UINT cNames, + LCID lcid, + __out_ecount(cNames) DISPID * rgdispid); + + STDMETHODIMP Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + __in DISPPARAMS * pdispparams, + __out_opt VARIANT * pvarResult, + __out_opt EXCEPINFO * pexcepinfo, + __out_opt UINT * puArgErr); +}; + + +// A class that handles the IDispatch part of IBasicVideo and leaves the +// properties and methods themselves pure virtual. + +class AM_NOVTABLE CBaseBasicVideo : public IBasicVideo2, public CUnknown +{ + CBaseDispatch m_basedisp; + +public: + + CBaseBasicVideo(__in_opt LPCTSTR, __in_opt LPUNKNOWN); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + /* IDispatch methods */ + STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo); + + STDMETHODIMP GetTypeInfo( + UINT itinfo, + LCID lcid, + __deref_out ITypeInfo ** pptinfo); + + STDMETHODIMP GetIDsOfNames( + REFIID riid, + __in_ecount(cNames) LPOLESTR * rgszNames, + UINT cNames, + LCID lcid, + __out_ecount(cNames) DISPID * rgdispid); + + STDMETHODIMP Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + __in DISPPARAMS * pdispparams, + __out_opt VARIANT * pvarResult, + __out_opt EXCEPINFO * pexcepinfo, + __out_opt UINT * puArgErr); + + STDMETHODIMP GetPreferredAspectRatio( + __out long *plAspectX, + __out long *plAspectY) + { + return E_NOTIMPL; + } +}; + + +// A class that handles the IDispatch part of IVideoWindow and leaves the +// properties and methods themselves pure virtual. + +class AM_NOVTABLE CBaseVideoWindow : public IVideoWindow, public CUnknown +{ + CBaseDispatch m_basedisp; + +public: + + CBaseVideoWindow(__in_opt LPCTSTR, __in_opt LPUNKNOWN); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + /* IDispatch methods */ + STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo); + + STDMETHODIMP GetTypeInfo( + UINT itinfo, + LCID lcid, + __deref_out ITypeInfo ** pptinfo); + + STDMETHODIMP GetIDsOfNames( + REFIID riid, + __in_ecount(cNames) LPOLESTR * rgszNames, + UINT cNames, + LCID lcid, + __out_ecount(cNames) DISPID * rgdispid); + + STDMETHODIMP Invoke( + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + __in DISPPARAMS * pdispparams, + __out_opt VARIANT * pvarResult, + __out_opt EXCEPINFO * pexcepinfo, + __out_opt UINT * puArgErr); +}; + + +// abstract class to help source filters with their implementation +// of IMediaPosition. Derive from this and set the duration (and stop +// position). Also override NotifyChange to do something when the properties +// change. + +class AM_NOVTABLE CSourcePosition : public CMediaPosition +{ + +public: + CSourcePosition(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *); + + // IMediaPosition methods + STDMETHODIMP get_Duration(__out REFTIME * plength); + STDMETHODIMP put_CurrentPosition(REFTIME llTime); + STDMETHODIMP get_StopTime(__out REFTIME * pllTime); + STDMETHODIMP put_StopTime(REFTIME llTime); + STDMETHODIMP get_PrerollTime(__out REFTIME * pllTime); + STDMETHODIMP put_PrerollTime(REFTIME llTime); + STDMETHODIMP get_Rate(__out double * pdRate); + STDMETHODIMP put_Rate(double dRate); + STDMETHODIMP CanSeekForward(__out LONG *pCanSeekForward); + STDMETHODIMP CanSeekBackward(__out LONG *pCanSeekBackward); + + // override if you can return the data you are actually working on + STDMETHODIMP get_CurrentPosition(__out REFTIME * pllTime) { + return E_NOTIMPL; + }; + +protected: + + // we call this to notify changes. Override to handle them + virtual HRESULT ChangeStart() PURE; + virtual HRESULT ChangeStop() PURE; + virtual HRESULT ChangeRate() PURE; + + COARefTime m_Duration; + COARefTime m_Start; + COARefTime m_Stop; + double m_Rate; + + CCritSec * m_pLock; +}; + +class AM_NOVTABLE CSourceSeeking : + public IMediaSeeking, + public CUnknown +{ + +public: + + DECLARE_IUNKNOWN; + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + // IMediaSeeking methods + + STDMETHODIMP IsFormatSupported(const GUID * pFormat); + STDMETHODIMP QueryPreferredFormat(__out GUID *pFormat); + STDMETHODIMP SetTimeFormat(const GUID * pFormat); + STDMETHODIMP IsUsingTimeFormat(const GUID * pFormat); + STDMETHODIMP GetTimeFormat(__out GUID *pFormat); + STDMETHODIMP GetDuration(__out LONGLONG *pDuration); + STDMETHODIMP GetStopPosition(__out LONGLONG *pStop); + STDMETHODIMP GetCurrentPosition(__out LONGLONG *pCurrent); + STDMETHODIMP GetCapabilities( __out DWORD * pCapabilities ); + STDMETHODIMP CheckCapabilities( __inout DWORD * pCapabilities ); + STDMETHODIMP ConvertTimeFormat( __out LONGLONG * pTarget, + __in_opt const GUID * pTargetFormat, + LONGLONG Source, + __in_opt const GUID * pSourceFormat ); + + STDMETHODIMP SetPositions( __inout_opt LONGLONG * pCurrent, DWORD CurrentFlags + , __inout_opt LONGLONG * pStop, DWORD StopFlags ); + + STDMETHODIMP GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop ); + + STDMETHODIMP GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest ); + STDMETHODIMP SetRate( double dRate); + STDMETHODIMP GetRate( __out double * pdRate); + STDMETHODIMP GetPreroll(__out LONGLONG *pPreroll); + + +protected: + + // ctor + CSourceSeeking(__in_opt LPCTSTR, __in_opt LPUNKNOWN, __inout HRESULT*, __in CCritSec *); + + // we call this to notify changes. Override to handle them + virtual HRESULT ChangeStart() PURE; + virtual HRESULT ChangeStop() PURE; + virtual HRESULT ChangeRate() PURE; + + CRefTime m_rtDuration; // length of stream + CRefTime m_rtStart; // source will start here + CRefTime m_rtStop; // source will stop here + double m_dRateSeeking; + + // seeking capabilities + DWORD m_dwSeekingCaps; + + CCritSec * m_pLock; +}; + + +// Base classes supporting Deferred commands. + +// Deferred commands are queued by calls to methods on the IQueueCommand +// interface, exposed by the filtergraph and by some filters. A successful +// call to one of these methods will return an IDeferredCommand interface +// representing the queued command. +// +// A CDeferredCommand object represents a single deferred command, and exposes +// the IDeferredCommand interface as well as other methods permitting time +// checks and actual execution. It contains a reference to the CCommandQueue +// object on which it is queued. +// +// CCommandQueue is a base class providing a queue of CDeferredCommand +// objects, and methods to add, remove, check status and invoke the queued +// commands. A CCommandQueue object would be part of an object that +// implemented IQueueCommand. + +class CCmdQueue; + +// take a copy of the params and store them. Release any allocated +// memory in destructor + +class CDispParams : public DISPPARAMS +{ +public: + CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr = NULL); + ~CDispParams(); +}; + + +// CDeferredCommand lifetime is controlled by refcounts. Caller of +// InvokeAt.. gets a refcounted interface pointer, and the CCmdQueue +// object also holds a refcount on us. Calling Cancel or Invoke takes +// us off the CCmdQueue and thus reduces the refcount by 1. Once taken +// off the queue we cannot be put back on the queue. + +class CDeferredCommand + : public CUnknown, + public IDeferredCommand +{ +public: + + CDeferredCommand( + __inout CCmdQueue * pQ, + __in_opt LPUNKNOWN pUnk, // aggregation outer unk + __inout HRESULT * phr, + __in LPUNKNOWN pUnkExecutor, // object that will execute this cmd + REFTIME time, + __in GUID* iid, + long dispidMethod, + short wFlags, + long cArgs, + __in_ecount(cArgs) VARIANT* pDispParams, + __out VARIANT* pvarResult, + __out short* puArgErr, + BOOL bStream + ); + + DECLARE_IUNKNOWN + + // override this to publicise our interfaces + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __out void **ppv); + + // IDeferredCommand methods + STDMETHODIMP Cancel(); + STDMETHODIMP Confidence( + __out LONG* pConfidence); + STDMETHODIMP Postpone( + REFTIME newtime); + STDMETHODIMP GetHResult( + __out HRESULT* phrResult); + + // other public methods + + HRESULT Invoke(); + + // access methods + + // returns TRUE if streamtime, FALSE if presentation time + BOOL IsStreamTime() { + return m_bStream; + }; + + CRefTime GetTime() { + return m_time; + }; + + REFIID GetIID() { + return *m_iid; + }; + + long GetMethod() { + return m_dispidMethod; + }; + + short GetFlags() { + return m_wFlags; + }; + + DISPPARAMS* GetParams() { + return &m_DispParams; + }; + + VARIANT* GetResult() { + return m_pvarResult; + }; + +protected: + + CCmdQueue* m_pQueue; + + // pUnk for the interface that we will execute the command on + LPUNKNOWN m_pUnk; + + // stored command data + REFERENCE_TIME m_time; + GUID* m_iid; + long m_dispidMethod; + short m_wFlags; + VARIANT* m_pvarResult; + BOOL m_bStream; + CDispParams m_DispParams; + DISPID m_DispId; // For get and put + + // we use this for ITypeInfo access + CBaseDispatch m_Dispatch; + + // save retval here + HRESULT m_hrResult; +}; + + +// a list of CDeferredCommand objects. this is a base class providing +// the basics of access to the list. If you want to use CDeferredCommand +// objects then your queue needs to be derived from this class. + +class AM_NOVTABLE CCmdQueue +{ +public: + CCmdQueue(__inout_opt HRESULT *phr = NULL); + virtual ~CCmdQueue(); + + // returns a new CDeferredCommand object that will be initialised with + // the parameters and will be added to the queue during construction. + // returns S_OK if successfully created otherwise an error and + // no object has been queued. + virtual HRESULT New( + __out CDeferredCommand **ppCmd, + __in LPUNKNOWN pUnk, + REFTIME time, + __in GUID* iid, + long dispidMethod, + short wFlags, + long cArgs, + __in_ecount(cArgs) VARIANT* pDispParams, + __out VARIANT* pvarResult, + __out short* puArgErr, + BOOL bStream + ); + + // called by the CDeferredCommand object to add and remove itself + // from the queue + virtual HRESULT Insert(__in CDeferredCommand* pCmd); + virtual HRESULT Remove(__in CDeferredCommand* pCmd); + + // Command-Due Checking + // + // There are two schemes of synchronisation: coarse and accurate. In + // coarse mode, you wait till the time arrives and then execute the cmd. + // In accurate mode, you wait until you are processing the sample that + // will appear at the time, and then execute the command. It's up to the + // filter which one it will implement. The filtergraph will always + // implement coarse mode for commands queued at the filtergraph. + // + // If you want coarse sync, you probably want to wait until there is a + // command due, and then execute it. You can do this by calling + // GetDueCommand. If you have several things to wait for, get the + // event handle from GetDueHandle() and when this is signalled then call + // GetDueCommand. Stream time will only advance between calls to Run and + // EndRun. Note that to avoid an extra thread there is no guarantee that + // if the handle is set there will be a command ready. Each time the + // event is signalled, call GetDueCommand (probably with a 0 timeout); + // This may return E_ABORT. + // + // If you want accurate sync, you must call GetCommandDueFor, passing + // as a parameter the stream time of the samples you are about to process. + // This will return: + // -- a stream-time command due at or before that stream time + // -- a presentation-time command due at or before the + // time that stream time will be presented (only between Run + // and EndRun calls, since outside of this, the mapping from + // stream time to presentation time is not known. + // -- any presentation-time command due now. + // This means that if you want accurate synchronisation on samples that + // might be processed during Paused mode, you need to use + // stream-time commands. + // + // In all cases, commands remain queued until Invoked or Cancelled. The + // setting and resetting of the event handle is managed entirely by this + // queue object. + + // set the clock used for timing + virtual HRESULT SetSyncSource(__in_opt IReferenceClock*); + + // switch to run mode. Streamtime to Presentation time mapping known. + virtual HRESULT Run(REFERENCE_TIME tStreamTimeOffset); + + // switch to Stopped or Paused mode. Time mapping not known. + virtual HRESULT EndRun(); + + // return a pointer to the next due command. Blocks for msTimeout + // milliseconds until there is a due command. + // Stream-time commands will only become due between Run and Endrun calls. + // The command remains queued until invoked or cancelled. + // Returns E_ABORT if timeout occurs, otherwise S_OK (or other error). + // Returns an AddRef-ed object + virtual HRESULT GetDueCommand(__out CDeferredCommand ** ppCmd, long msTimeout); + + // return the event handle that will be signalled whenever + // there are deferred commands due for execution (when GetDueCommand + // will not block). + HANDLE GetDueHandle() { + return HANDLE(m_evDue); + }; + + // return a pointer to a command that will be due for a given time. + // Pass in a stream time here. The stream time offset will be passed + // in via the Run method. + // Commands remain queued until invoked or cancelled. + // This method will not block. It will report VFW_E_NOT_FOUND if there + // are no commands due yet. + // Returns an AddRef-ed object + virtual HRESULT GetCommandDueFor(REFERENCE_TIME tStream, __out CDeferredCommand**ppCmd); + + // check if a given time is due (TRUE if it is due yet) + BOOL CheckTime(CRefTime time, BOOL bStream) { + + // if no clock, nothing is due! + if (!m_pClock) { + return FALSE; + } + + // stream time + if (bStream) { + + // not valid if not running + if (!m_bRunning) { + return FALSE; + } + // add on known stream time offset to get presentation time + time += m_StreamTimeOffset; + } + + CRefTime Now; + m_pClock->GetTime((REFERENCE_TIME*)&Now); + return (time <= Now); + }; + +protected: + + // protect access to lists etc + CCritSec m_Lock; + + // commands queued in presentation time are stored here + CGenericList m_listPresentation; + + // commands queued in stream time are stored here + CGenericList m_listStream; + + // set when any commands are due + CAMEvent m_evDue; + + // creates an advise for the earliest time required, if any + void SetTimeAdvise(void); + + // advise id from reference clock (0 if no outstanding advise) + DWORD_PTR m_dwAdvise; + + // advise time is for this presentation time + CRefTime m_tCurrentAdvise; + + // the reference clock we are using (addrefed) + IReferenceClock* m_pClock; + + // true when running + BOOL m_bRunning; + + // contains stream time offset when m_bRunning is true + CRefTime m_StreamTimeOffset; +}; + +#endif // __CTLUTIL__ diff --git a/RTSP Source Filter/Common/DirectShow/ddmm.h b/RTSP Source Filter/Common/DirectShow/ddmm.h new file mode 100644 index 0000000..c773d58 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/ddmm.h @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// File: DDMM.h +// +// Desc: DirectShow base classes - efines routines for using DirectDraw +// on a multimonitor system. +// +// Copyright (c) 1995-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifdef __cplusplus +extern "C" { /* Assume C declarations for C++ */ +#endif /* __cplusplus */ + +// DDRAW.H might not include these +#ifndef DDENUM_ATTACHEDSECONDARYDEVICES +#define DDENUM_ATTACHEDSECONDARYDEVICES 0x00000001L +#endif + +typedef HRESULT (*PDRAWCREATE)(IID *,LPDIRECTDRAW *,LPUNKNOWN); +typedef HRESULT (*PDRAWENUM)(LPDDENUMCALLBACKA, LPVOID); + +IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR, PDRAWCREATE, PDRAWENUM); +IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR, PDRAWCREATE, LPDIRECTDRAWENUMERATEEXA); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/RTSP Source Filter/Common/DirectShow/dllsetup.h b/RTSP Source Filter/Common/DirectShow/dllsetup.h new file mode 100644 index 0000000..aaac2ec --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/dllsetup.h @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// File: DllSetup.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// To be self registering, OLE servers must +// export functions named DllRegisterServer +// and DllUnregisterServer. To allow use of +// custom and default implementations the +// defaults are named AMovieDllRegisterServer +// and AMovieDllUnregisterServer. +// +// To the use the default implementation you +// must provide stub functions. +// +// i.e. STDAPI DllRegisterServer() +// { +// return AMovieDllRegisterServer(); +// } +// +// STDAPI DllUnregisterServer() +// { +// return AMovieDllUnregisterServer(); +// } +// +// +// AMovieDllRegisterServer calls IAMovieSetup.Register(), and +// AMovieDllUnregisterServer calls IAMovieSetup.Unregister(). + +STDAPI AMovieDllRegisterServer2( BOOL ); +STDAPI AMovieDllRegisterServer(); +STDAPI AMovieDllUnregisterServer(); + +// helper functions +STDAPI EliminateSubKey( HKEY, LPCTSTR ); + + +STDAPI +AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata + , IFilterMapper2 * pIFM2 + , BOOL bRegister ); + diff --git a/RTSP Source Filter/Common/DirectShow/dxmperf.h b/RTSP Source Filter/Common/DirectShow/dxmperf.h new file mode 100644 index 0000000..dc58ad7 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/dxmperf.h @@ -0,0 +1,250 @@ +//------------------------------------------------------------------------------ +// File: DXMPerf.h +// +// Desc: Macros for DirectShow performance logging. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef _DXMPERF_H_ +#define _DXMPERF_H_ + +#include +#include "perflog.h" + +#ifdef _IA64_ +extern "C" unsigned __int64 __getReg( int whichReg ); +#pragma intrinsic(__getReg) +#endif // _IA64_ + + +inline ULONGLONG _RDTSC( void ) { +#ifdef _X86_ + LARGE_INTEGER li; + __asm { + _emit 0x0F + _emit 0x31 + mov li.LowPart,eax + mov li.HighPart,edx + } + return li.QuadPart; + +#if 0 // This isn't tested yet + +#elif defined (_IA64_) + +#define INL_REGID_APITC 3116 + return __getReg( INL_REGID_APITC ); + +#endif // 0 + +#else // unsupported platform + // not implemented on non x86/IA64 platforms + return 0; +#endif // _X86_/_IA64_ +} + +#define DXMPERF_VIDEOREND 0x00000001 +#define DXMPERF_AUDIOGLITCH 0x00000002 +//#define GETTIME_BIT 0x00000001 +//#define AUDIOREND_BIT 0x00000004 +//#define FRAMEDROP_BIT 0x00000008 +#define AUDIOBREAK_BIT 0x00000010 +#define DXMPERF_AUDIORECV 0x00000020 +#define DXMPERF_AUDIOSLAVE 0x00000040 +#define DXMPERF_AUDIOBREAK 0x00000080 + +#define PERFLOG_CTOR( name, iface ) +#define PERFLOG_DTOR( name, iface ) +#define PERFLOG_DELIVER( name, source, dest, sample, pmt ) +#define PERFLOG_RECEIVE( name, source, dest, sample, pmt ) +#define PERFLOG_RUN( name, iface, time, oldstate ) +#define PERFLOG_PAUSE( name, iface, oldstate ) +#define PERFLOG_STOP( name, iface, oldstate ) +#define PERFLOG_JOINGRAPH( name, iface, graph ) +#define PERFLOG_GETBUFFER( allocator, sample ) +#define PERFLOG_RELBUFFER( allocator, sample ) +#define PERFLOG_CONNECT( connector, connectee, status, pmt ) +#define PERFLOG_RXCONNECT( connector, connectee, status, pmt ) +#define PERFLOG_DISCONNECT( disconnector, disconnectee, status ) + +#define PERFLOG_GETTIME( clock, time ) /*{ \ + PERFINFO_WMI_GETTIME perfData; \ + if (NULL != g_pTraceEvent) { \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_GETTIME; \ + perfData.data.cycleCounter = _RDTSC(); \ + perfData.data.dshowClock = (ULONGLONG) (time); \ + if (g_perfMasks[GETTIME_INDEX] & GETTIME_BIT) \ + (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \ + } \ + }*/ + +#define PERFLOG_AUDIOREND( clocktime, sampletime, psample, bytetime, cbytes ) /*{ \ + PERFINFO_WMI_AVREND perfData; \ + if (NULL != g_pTraceEvent) { \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_AUDIOREND; \ + perfData.data.cycleCounter = _RDTSC(); \ + perfData.data.dshowClock = (clocktime); \ + perfData.data.sampleTime = (sampletime); \ + if (g_perfMasks[AUDIOREND_INDEX] & AUDIOREND_BIT) \ + (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \ + } \ + }*/ + +#define PERFLOG_AUDIORECV(StreamTime,SampleStart,SampleStop,Discontinuity,Duration) \ + if (PerflogEnableFlags & DXMPERF_AUDIORECV) { \ + PERFINFO_WMI_AUDIORECV perfData; \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_AUDIORECV; \ + perfData.data.streamTime = StreamTime; \ + perfData.data.sampleStart = SampleStart; \ + perfData.data.sampleStop = SampleStop; \ + perfData.data.discontinuity = Discontinuity; \ + perfData.data.hwduration = Duration; \ + PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \ + } + +#define PERFLOG_AUDIOSLAVE(MasterClock,SlaveClock,ErrorAccum,LastHighErrorSeen,LastLowErrorSeen) \ + if (PerflogEnableFlags & DXMPERF_AUDIOSLAVE) { \ + PERFINFO_WMI_AUDIOSLAVE perfData; \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_AUDIOSLAVE; \ + perfData.data.masterClock = MasterClock; \ + perfData.data.slaveClock = SlaveClock; \ + perfData.data.errorAccum = ErrorAccum; \ + perfData.data.lastHighErrorSeen = LastHighErrorSeen;\ + perfData.data.lastLowErrorSeen = LastLowErrorSeen; \ + PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \ + } + +#define PERFLOG_AUDIOADDBREAK(IterNextWrite,OffsetNextWrite,IterWrite,OffsetWrite) \ + if (PerflogEnableFlags & DXMPERF_AUDIOBREAK) { \ + PERFINFO_WMI_AUDIOADDBREAK perfData; \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_AUDIOADDBREAK; \ + perfData.data.iterNextWrite = IterNextWrite; \ + perfData.data.offsetNextWrite = OffsetNextWrite; \ + perfData.data.iterWrite = IterWrite; \ + perfData.data.offsetWrite = OffsetWrite; \ + PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); \ + } + +#define PERFLOG_VIDEOREND( sampletime, clocktime, psample ) \ + if (PerflogEnableFlags & DXMPERF_VIDEOREND) { \ + PERFINFO_WMI_AVREND perfData; \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_VIDEOREND; \ + perfData.data.cycleCounter = _RDTSC(); \ + perfData.data.dshowClock = (clocktime); \ + perfData.data.sampleTime = (sampletime); \ + PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \ + } + +#define PERFLOG_AUDIOGLITCH( instance, glitchtype, currenttime, previoustime ) \ + if (PerflogEnableFlags & DXMPERF_AUDIOGLITCH) { \ + PERFINFO_WMI_AUDIOGLITCH perfData; \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_DSOUNDGLITCH; \ + perfData.data.cycleCounter = _RDTSC(); \ + perfData.data.glitchType = (glitchtype); \ + perfData.data.sampleTime = (currenttime); \ + perfData.data.previousTime = (previoustime); \ + perfData.data.instanceId = (instance); \ + PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \ + } + +#define PERFLOG_FRAMEDROP( sampletime, clocktime, psample, renderer ) /*{ \ + PERFINFO_WMI_FRAMEDROP perfData; \ + if (NULL != g_pTraceEvent) { \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_FRAMEDROP; \ + perfData.data.cycleCounter = _RDTSC(); \ + perfData.data.dshowClock = (clocktime); \ + perfData.data.frameTime = (sampletime); \ + if (g_perfMasks[FRAMEDROP_INDEX] & FRAMEDROP_BIT) \ + (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \ + } \ + }*/ + +/* +#define PERFLOG_AUDIOBREAK( nextwrite, writepos, msecs ) { \ + PERFINFO_WMI_AUDIOBREAK perfData; \ + if (NULL != g_pTraceEvent) { \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_AUDIOBREAK; \ + perfData.data.cycleCounter = _RDTSC(); \ + perfData.data.dshowClock = (writepos); \ + perfData.data.sampleTime = (nextwrite); \ + perfData.data.sampleDuration = (msecs); \ + if (g_perfMasks[AUDIOBREAK_INDEX] & AUDIOBREAK_BIT) \ + (*g_pTraceEvent)( g_traceHandle, (PEVENT_TRACE_HEADER) &perfData ); \ + } \ + } +*/ + +#define PERFLOG_AUDIOBREAK( nextwrite, writepos, msecs ) \ + if (PerflogEnableFlags & AUDIOBREAK_BIT) { \ + PERFINFO_WMI_AUDIOBREAK perfData; \ + memset( &perfData, 0, sizeof( perfData ) ); \ + perfData.header.Size = sizeof( perfData ); \ + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; \ + perfData.header.Guid = GUID_AUDIOBREAK; \ + perfData.data.cycleCounter = _RDTSC(); \ + perfData.data.dshowClock = (writepos); \ + perfData.data.sampleTime = (nextwrite); \ + perfData.data.sampleDuration = (msecs); \ + PerflogTraceEvent ((PEVENT_TRACE_HEADER) &perfData); \ + } \ + + +inline +VOID PERFLOG_STREAMTRACE( + ULONG Level, + ULONG Id, + ULONGLONG DShowClock, + ULONGLONG Data1, + ULONGLONG Data2, + ULONGLONG Data3, + ULONGLONG Data4 + ) +{ + if (Level <= PerflogModuleLevel) + { + PERFINFO_WMI_STREAMTRACE perfData; + memset( &perfData, 0, sizeof( perfData ) ); + perfData.header.Size = sizeof( perfData ); + perfData.header.Flags = WNODE_FLAG_TRACED_GUID; + perfData.header.Guid = GUID_STREAMTRACE; + perfData.data.dshowClock = DShowClock; + perfData.data.id = Id; + perfData.data.data[0] = Data1; + perfData.data.data[1] = Data2; + perfData.data.data[2] = Data3; + perfData.data.data[3] = Data4; + PerflogTraceEvent((PEVENT_TRACE_HEADER) &perfData); + } +} + + +#endif // _DXMPERF_H_ diff --git a/RTSP Source Filter/Common/DirectShow/fourcc.h b/RTSP Source Filter/Common/DirectShow/fourcc.h new file mode 100644 index 0000000..f4f71e9 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/fourcc.h @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// File: FourCC.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// FOURCCMap +// +// provides a mapping between old-style multimedia format DWORDs +// and new-style GUIDs. +// +// A range of 4 billion GUIDs has been allocated to ensure that this +// mapping can be done straightforwardly one-to-one in both directions. +// +// January 95 + + +#ifndef __FOURCC__ +#define __FOURCC__ + + +// Multimedia format types are marked with DWORDs built from four 8-bit +// chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include +// a subtype GUID. In order to simplify the mapping, GUIDs in the range: +// XXXXXXXX-0000-0010-8000-00AA00389B71 +// are reserved for FOURCCs. + +class FOURCCMap : public GUID +{ + +public: + FOURCCMap(); + FOURCCMap(DWORD Fourcc); + FOURCCMap(const GUID *); + + + DWORD GetFOURCC(void); + void SetFOURCC(DWORD fourcc); + void SetFOURCC(const GUID *); + +private: + void InitGUID(); +}; + +#define GUID_Data2 0 +#define GUID_Data3 0x10 +#define GUID_Data4_1 0xaa000080 +#define GUID_Data4_2 0x719b3800 + +inline void +FOURCCMap::InitGUID() { + Data2 = GUID_Data2; + Data3 = GUID_Data3; + ((DWORD *)Data4)[0] = GUID_Data4_1; + ((DWORD *)Data4)[1] = GUID_Data4_2; +} + +inline +FOURCCMap::FOURCCMap() { + InitGUID(); + SetFOURCC( DWORD(0)); +} + +inline +FOURCCMap::FOURCCMap(DWORD fourcc) +{ + InitGUID(); + SetFOURCC(fourcc); +} + +inline +FOURCCMap::FOURCCMap(const GUID * pGuid) +{ + InitGUID(); + SetFOURCC(pGuid); +} + +inline void +FOURCCMap::SetFOURCC(const GUID * pGuid) +{ + FOURCCMap * p = (FOURCCMap*) pGuid; + SetFOURCC(p->GetFOURCC()); +} + +inline void +FOURCCMap::SetFOURCC(DWORD fourcc) +{ + Data1 = fourcc; +} + +inline DWORD +FOURCCMap::GetFOURCC(void) +{ + return Data1; +} + +#endif /* __FOURCC__ */ + diff --git a/RTSP Source Filter/Common/DirectShow/measure.h b/RTSP Source Filter/Common/DirectShow/measure.h new file mode 100644 index 0000000..0babc86 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/measure.h @@ -0,0 +1,222 @@ +//------------------------------------------------------------------------------ +// File: Measure.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +/* + The idea is to pepper the source code with interesting measurements and + have the last few thousand of these recorded in a circular buffer that + can be post-processed to give interesting numbers. + + WHAT THE LOG LOOKS LIKE: + + Time (sec) Type Delta Incident_Name + 0.055,41 NOTE -. Incident Nine - Another note + 0.055,42 NOTE 0.000,01 Incident Nine - Another note + 0.055,44 NOTE 0.000,02 Incident Nine - Another note + 0.055,45 STOP -. Incident Eight - Also random + 0.055,47 START -. Incident Seven - Random + 0.055,49 NOTE 0.000,05 Incident Nine - Another note + ------- ---------------- + 0.125,60 STOP 0.000,03 Msr_Stop + 0.125,62 START -. Msr_Start + 0.125,63 START -. Incident Two - Start/Stop + 0.125,65 STOP 0.000,03 Msr_Start + 0.125,66 START -. Msr_Stop + 0.125,68 STOP 0.000,05 Incident Two - Start/Stop + 0.125,70 STOP 0.000,04 Msr_Stop + 0.125,72 START -. Msr_Start + 0.125,73 START -. Incident Two - Start/Stop + 0.125,75 STOP 0.000,03 Msr_Start + 0.125,77 START -. Msr_Stop + 0.125,78 STOP 0.000,05 Incident Two - Start/Stop + 0.125,80 STOP 0.000,03 Msr_Stop + 0.125,81 NOTE -. Incident Three - single Note + 0.125,83 START -. Incident Four - Start, no stop + 0.125,85 START -. Incident Five - Single Start/Stop + 0.125,87 STOP 0.000,02 Incident Five - Single Start/Stop + +Number Average StdDev Smallest Largest Incident_Name + 10 0.000,58 0.000,10 0.000,55 0.000,85 Incident One - Note + 50 0.000,05 0.000,00 0.000,05 0.000,05 Incident Two - Start/Stop + 1 -. -. -. -. Incident Three - single Note + 0 -. -. -. -. Incident Four - Start, no stop + 1 0.000,02 -. 0.000,02 0.000,02 Incident Five - Single Start/Stop + 0 -. -. -. -. Incident Six - zero occurrences + 100 0.000,25 0.000,12 0.000,02 0.000,62 Incident Seven - Random + 100 0.000,79 0.000,48 0.000,02 0.001,92 Incident Eight - Also random + 5895 0.000,01 0.000,01 0.000,01 0.000,56 Incident Nine - Another note + 10 0.000,03 0.000,00 0.000,03 0.000,04 Msr_Note + 50 0.000,03 0.000,00 0.000,03 0.000,04 Msr_Start + 50 0.000,04 0.000,03 0.000,03 0.000,31 Msr_Stop + + WHAT IT MEANS: + The log shows what happened and when. Each line shows the time at which + something happened (see WHAT YOU CODE below) what it was that happened + and (if approporate) the time since the corresponding previous event + (that's the delta column). + + The statistics show how many times each event occurred, what the average + delta time was, also the standard deviation, largest and smalles delta. + + WHAT YOU CODE: + + Before anything else executes: - register your ids + + int id1 = Msr_Register("Incident One - Note"); + int id2 = Msr_Register("Incident Two - Start/Stop"); + int id3 = Msr_Register("Incident Three - single Note"); + etc. + + At interesting moments: + + // To measure a repetitive event - e.g. end of bitblt to screen + Msr_Note(Id9); // e.g. "video frame hiting the screen NOW!" + + or + + // To measure an elapsed time e.g. time taken to decode an MPEG B-frame + Msr_Start(Id2); // e.g. "Starting to decode MPEG B-frame" + . . . + MsrStop(Id2); // "Finished MPEG decode" + + At the end: + + HANDLE hFile; + hFile = CreateFile("Perf.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + Msr_Dump(hFile); // This writes the log out to the file + CloseHandle(hFile); + + or + + Msr_Dump(NULL); // This writes it to DbgLog((LOG_TRACE,0, ... )); + // but if you are writing it out to the debugger + // then the times are probably all garbage because + // the debugger can make things run awfully slow. + + A given id should be used either for start / stop or Note calls. If Notes + are mixed in with Starts and Stops their statistics will be gibberish. + + If you code the calls in upper case i.e. MSR_START(idMunge); then you get + macros which will turn into nothing unless PERF is defined. + + You can reset the statistical counts for a given id by calling Reset(Id). + They are reset by default at the start. + It logs Reset as a special incident, so you can see it in the log. + + The log is a circular buffer in storage (to try to minimise disk I/O). + It overwrites the oldest entries once full. The statistics include ALL + incidents since the last Reset, whether still visible in the log or not. +*/ + +#ifndef __MEASURE__ +#define __MEASURE__ + +#ifdef PERF +#define MSR_INIT() Msr_Init() +#define MSR_TERMINATE() Msr_Terminate() +#define MSR_REGISTER(a) Msr_Register(a) +#define MSR_RESET(a) Msr_Reset(a) +#define MSR_CONTROL(a) Msr_Control(a) +#define MSR_START(a) Msr_Start(a) +#define MSR_STOP(a) Msr_Stop(a) +#define MSR_NOTE(a) Msr_Note(a) +#define MSR_INTEGER(a,b) Msr_Integer(a,b) +#define MSR_DUMP(a) Msr_Dump(a) +#define MSR_DUMPSTATS(a) Msr_DumpStats(a) +#else +#define MSR_INIT() ((void)0) +#define MSR_TERMINATE() ((void)0) +#define MSR_REGISTER(a) 0 +#define MSR_RESET(a) ((void)0) +#define MSR_CONTROL(a) ((void)0) +#define MSR_START(a) ((void)0) +#define MSR_STOP(a) ((void)0) +#define MSR_NOTE(a) ((void)0) +#define MSR_INTEGER(a,b) ((void)0) +#define MSR_DUMP(a) ((void)0) +#define MSR_DUMPSTATS(a) ((void)0) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// This must be called first - (called by the DllEntry) + +void WINAPI Msr_Init(void); + + +// Call this last to clean up (or just let it fall off the end - who cares?) + +void WINAPI Msr_Terminate(void); + + +// Call this to get an Id for an "incident" that you can pass to Start, Stop or Note +// everything that's logged is called an "incident". + +int WINAPI Msr_Register(__in LPTSTR Incident); + + +// Reset the statistical counts for an incident + +void WINAPI Msr_Reset(int Id); + + +// Reset all the counts for all incidents +#define MSR_RESET_ALL 0 +#define MSR_PAUSE 1 +#define MSR_RUN 2 + +void WINAPI Msr_Control(int iAction); + + +// log the start of an operation + +void WINAPI Msr_Start(int Id); + + +// log the end of an operation + +void WINAPI Msr_Stop(int Id); + + +// log a one-off or repetitive operation + +void WINAPI Msr_Note(int Id); + + +// log an integer (on which we can see statistics later) +void WINAPI Msr_Integer(int Id, int n); + + +// print out all the vaialable log (it may have wrapped) and then the statistics. +// When the log wraps you lose log but the statistics are still complete. +// hFIle==NULL => use DbgLog +// otherwise hFile must have come from CreateFile or OpenFile. + +void WINAPI Msr_Dump(HANDLE hFile); + + +// just dump the statistics - never mind the log + +void WINAPI Msr_DumpStats(HANDLE hFile); + +// Type definitions in case you want to declare a pointer to the dump functions +// (makes it a trifle easier to do dynamic linking +// i.e. LoadModule, GetProcAddress and call that) + +// Typedefs so can declare MSR_DUMPPROC *MsrDumpStats; or whatever +typedef void WINAPI MSR_DUMPPROC(HANDLE hFile); +typedef void WINAPI MSR_CONTROLPROC(int iAction); + + +#ifdef __cplusplus +} +#endif + +#endif // __MEASURE__ diff --git a/RTSP Source Filter/Common/DirectShow/msgthrd.h b/RTSP Source Filter/Common/DirectShow/msgthrd.h new file mode 100644 index 0000000..208f03c --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/msgthrd.h @@ -0,0 +1,120 @@ +//------------------------------------------------------------------------------ +// File: MsgThrd.h +// +// Desc: DirectShow base classes - provides support for a worker thread +// class to which one can asynchronously post messages. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// Message class - really just a structure. +// +class CMsg { +public: + UINT uMsg; + DWORD dwFlags; + LPVOID lpParam; + CAMEvent *pEvent; + + CMsg(UINT u, DWORD dw, __inout_opt LPVOID lp, __in_opt CAMEvent *pEvnt) + : uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {} + + CMsg() + : uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {} +}; + +// This is the actual thread class. It exports all the usual thread control +// functions. The created thread is different from a normal WIN32 thread in +// that it is prompted to perform particaular tasks by responding to messages +// posted to its message queue. +// +class AM_NOVTABLE CMsgThread { +private: + static DWORD WINAPI DefaultThreadProc(__inout LPVOID lpParam); + DWORD m_ThreadId; + HANDLE m_hThread; + +protected: + + // if you want to override GetThreadMsg to block on other things + // as well as this queue, you need access to this + CGenericList m_ThreadQueue; + CCritSec m_Lock; + HANDLE m_hSem; + LONG m_lWaiting; + +public: + CMsgThread() + : m_ThreadId(0), + m_hThread(NULL), + m_lWaiting(0), + m_hSem(NULL), + // make a list with a cache of 5 items + m_ThreadQueue(NAME("MsgThread list"), 5) + { + } + + ~CMsgThread(); + // override this if you want to block on other things as well + // as the message loop + void virtual GetThreadMsg(__out CMsg *msg); + + // override this if you want to do something on thread startup + virtual void OnThreadInit() { + }; + + BOOL CreateThread(); + + BOOL WaitForThreadExit(__out LPDWORD lpdwExitCode) { + if (m_hThread != NULL) { + WaitForSingleObject(m_hThread, INFINITE); + return GetExitCodeThread(m_hThread, lpdwExitCode); + } + return FALSE; + } + + DWORD ResumeThread() { + return ::ResumeThread(m_hThread); + } + + DWORD SuspendThread() { + return ::SuspendThread(m_hThread); + } + + int GetThreadPriority() { + return ::GetThreadPriority(m_hThread); + } + + BOOL SetThreadPriority(int nPriority) { + return ::SetThreadPriority(m_hThread, nPriority); + } + + HANDLE GetThreadHandle() { + return m_hThread; + } + + DWORD GetThreadId() { + return m_ThreadId; + } + + + void PutThreadMsg(UINT uMsg, DWORD dwMsgFlags, + __in_opt LPVOID lpMsgParam, __in_opt CAMEvent *pEvent = NULL) { + CAutoLock lck(&m_Lock); + CMsg* pMsg = new CMsg(uMsg, dwMsgFlags, lpMsgParam, pEvent); + m_ThreadQueue.AddTail(pMsg); + if (m_lWaiting != 0) { + ReleaseSemaphore(m_hSem, m_lWaiting, 0); + m_lWaiting = 0; + } + } + + // This is the function prototype of the function that the client + // supplies. It is always called on the created thread, never on + // the creator thread. + // + virtual LRESULT ThreadMessageProc( + UINT uMsg, DWORD dwFlags, __inout_opt LPVOID lpParam, __in_opt CAMEvent *pEvent) = 0; +}; + diff --git a/RTSP Source Filter/Common/DirectShow/mtype.h b/RTSP Source Filter/Common/DirectShow/mtype.h new file mode 100644 index 0000000..9402f06 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/mtype.h @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// File: MtType.h +// +// Desc: DirectShow base classes - defines a class that holds and manages +// media type information. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __MTYPE__ +#define __MTYPE__ + +/* Helper class that derived pin objects can use to compare media + types etc. Has same data members as the struct AM_MEDIA_TYPE defined + in the streams IDL file, but also has (non-virtual) functions */ + +class CMediaType : public _AMMediaType { + +public: + + ~CMediaType(); + CMediaType(); + CMediaType(const GUID * majortype); + CMediaType(const AM_MEDIA_TYPE&, __out_opt HRESULT* phr = NULL); + CMediaType(const CMediaType&, __out_opt HRESULT* phr = NULL); + + CMediaType& operator=(const CMediaType&); + CMediaType& operator=(const AM_MEDIA_TYPE&); + + BOOL operator == (const CMediaType&) const; + BOOL operator != (const CMediaType&) const; + + HRESULT Set(const CMediaType& rt); + HRESULT Set(const AM_MEDIA_TYPE& rt); + + BOOL IsValid() const; + + const GUID *Type() const { return &majortype;} ; + void SetType(const GUID *); + const GUID *Subtype() const { return &subtype;} ; + void SetSubtype(const GUID *); + + BOOL IsFixedSize() const {return bFixedSizeSamples; }; + BOOL IsTemporalCompressed() const {return bTemporalCompression; }; + ULONG GetSampleSize() const; + + void SetSampleSize(ULONG sz); + void SetVariableSize(); + void SetTemporalCompression(BOOL bCompressed); + + // read/write pointer to format - can't change length without + // calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer + + BYTE* Format() const {return pbFormat; }; + ULONG FormatLength() const { return cbFormat; }; + + void SetFormatType(const GUID *); + const GUID *FormatType() const {return &formattype; }; + BOOL SetFormat(__in_bcount(length) BYTE *pFormat, ULONG length); + void ResetFormatBuffer(); + BYTE* AllocFormatBuffer(ULONG length); + BYTE* ReallocFormatBuffer(ULONG length); + + void InitMediaType(); + + BOOL MatchesPartial(const CMediaType* ppartial) const; + BOOL IsPartiallySpecified(void) const; +}; + + +/* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE + structure which is useful when using the IEnumMediaFormats interface as + the implementation allocates the structures which you must later delete */ + +void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt); +AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc); +HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource); +void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt); + +// Initialize a media type from a WAVEFORMATEX + +STDAPI CreateAudioMediaType( + const WAVEFORMATEX *pwfx, + __out AM_MEDIA_TYPE *pmt, + BOOL bSetFormat); + +#endif /* __MTYPE__ */ + diff --git a/RTSP Source Filter/Common/DirectShow/outputq.h b/RTSP Source Filter/Common/DirectShow/outputq.h new file mode 100644 index 0000000..7e60b53 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/outputq.h @@ -0,0 +1,137 @@ +//------------------------------------------------------------------------------ +// File: OutputQ.h +// +// Desc: DirectShow base classes - defines the COutputQueue class, which +// makes a queue of samples and sends them to an output pin. The +// class will optionally send the samples to the pin directly. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +typedef CGenericList CSampleList; + +class COutputQueue : public CCritSec +{ +public: + // Constructor + COutputQueue(IPin *pInputPin, // Pin to send stuff to + __inout HRESULT *phr, // 'Return code' + BOOL bAuto = TRUE, // Ask pin if blocks + BOOL bQueue = TRUE, // Send through queue (ignored if + // bAuto set) + LONG lBatchSize = 1, // Batch + BOOL bBatchExact = FALSE,// Batch exactly to BatchSize + LONG lListSize = // Likely number in the list + DEFAULTCACHE, + DWORD dwPriority = // Priority of thread to create + THREAD_PRIORITY_NORMAL, + bool bFlushingOpt = false // flushing optimization + ); + ~COutputQueue(); + + // enter flush state - discard all data + void BeginFlush(); // Begin flushing samples + + // re-enable receives (pass this downstream) + void EndFlush(); // Complete flush of samples - downstream + // pin guaranteed not to block at this stage + + void EOS(); // Call this on End of stream + + void SendAnyway(); // Send batched samples anyway (if bBatchExact set) + + void NewSegment( + REFERENCE_TIME tStart, + REFERENCE_TIME tStop, + double dRate); + + HRESULT Receive(IMediaSample *pSample); + + // do something with these media samples + HRESULT ReceiveMultiple ( + __in_ecount(nSamples) IMediaSample **pSamples, + long nSamples, + __out long *nSamplesProcessed); + + void Reset(); // Reset m_hr ready for more data + + // See if its idle or not + BOOL IsIdle(); + + // give the class an event to fire after everything removed from the queue + void SetPopEvent(HANDLE hEvent); + +protected: + static DWORD WINAPI InitialThreadProc(__in LPVOID pv); + DWORD ThreadProc(); + BOOL IsQueued() + { + return m_List != NULL; + }; + + // The critical section MUST be held when this is called + void QueueSample(IMediaSample *pSample); + + BOOL IsSpecialSample(IMediaSample *pSample) + { + return (DWORD_PTR)pSample > (DWORD_PTR)(LONG_PTR)(-16); + }; + + // Remove and Release() batched and queued samples + void FreeSamples(); + + // Notify the thread there is something to do + void NotifyThread(); + + +protected: + // Queue 'messages' + #define SEND_PACKET ((IMediaSample *)(LONG_PTR)(-2)) // Send batch + #define EOS_PACKET ((IMediaSample *)(LONG_PTR)(-3)) // End of stream + #define RESET_PACKET ((IMediaSample *)(LONG_PTR)(-4)) // Reset m_hr + #define NEW_SEGMENT ((IMediaSample *)(LONG_PTR)(-5)) // send NewSegment + + // new segment packet is always followed by one of these + struct NewSegmentPacket { + REFERENCE_TIME tStart; + REFERENCE_TIME tStop; + double dRate; + }; + + // Remember input stuff + IPin * const m_pPin; + IMemInputPin * m_pInputPin; + BOOL const m_bBatchExact; + LONG const m_lBatchSize; + + CSampleList * m_List; + HANDLE m_hSem; + CAMEvent m_evFlushComplete; + HANDLE m_hThread; + __field_ecount_opt(m_lBatchSize) IMediaSample ** m_ppSamples; + __range(0, m_lBatchSize) LONG m_nBatched; + + // Wait optimization + LONG m_lWaiting; + // Flush synchronization + BOOL m_bFlushing; + + // flushing optimization. some downstream filters have trouble + // with the queue's flushing optimization. other rely on it + BOOL m_bFlushed; + bool m_bFlushingOpt; + + // Terminate now + BOOL m_bTerminate; + + // Send anyway flag for batching + BOOL m_bSendAnyway; + + // Deferred 'return code' + HRESULT volatile m_hr; + + // an event that can be fired after every deliver + HANDLE m_hEventPop; +}; + diff --git a/RTSP Source Filter/Common/DirectShow/perflog.h b/RTSP Source Filter/Common/DirectShow/perflog.h new file mode 100644 index 0000000..05d6404 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/perflog.h @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// File: perflog.h +// +// Desc: Performance logging framework. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + +typedef struct _PERFLOG_LOGGING_PARAMS { + GUID ControlGuid; + void (*OnStateChanged)(void); + ULONG NumberOfTraceGuids; + TRACE_GUID_REGISTRATION TraceGuids[ANYSIZE_ARRAY]; +} PERFLOG_LOGGING_PARAMS, *PPERFLOG_LOGGING_PARAMS; + +BOOL +PerflogInitIfEnabled( + IN HINSTANCE hInstance, + __in PPERFLOG_LOGGING_PARAMS LogParams + ); + +BOOL +PerflogInitialize ( + __in PPERFLOG_LOGGING_PARAMS LogParams + ); + +VOID +PerflogShutdown ( + VOID + ); + +VOID +PerflogTraceEvent ( + __in PEVENT_TRACE_HEADER Event + ); + +extern ULONG PerflogEnableFlags; +extern UCHAR PerflogEnableLevel; +extern ULONG PerflogModuleLevel; +extern TRACEHANDLE PerflogTraceHandle; +extern TRACEHANDLE PerflogRegHandle; + +#define PerflogTracingEnabled() (PerflogTraceHandle != 0) + +#define PerflogEvent( _x_ ) PerflogTraceEventLevel _x_ + +VOID +PerflogTraceEventLevel( + ULONG Level, + __in PEVENT_TRACE_HEADER Event + ); + +VOID +PerflogTraceEvent ( + __in PEVENT_TRACE_HEADER Event + ); diff --git a/RTSP Source Filter/Common/DirectShow/perfstruct.h b/RTSP Source Filter/Common/DirectShow/perfstruct.h new file mode 100644 index 0000000..b57657c --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/perfstruct.h @@ -0,0 +1,194 @@ +//------------------------------------------------------------------------------ +// File: PerfStruct.h +// +// Desc: Structures for DirectShow performance logging. +// +// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef _PERFSTRUCT_H_ +#define _PERFSTRUCT_H_ + +#include +#include + +// {28CF047A-2437-4b24-B653-B9446A419A69} +DEFINE_GUID(GUID_DSHOW_CTL, +0x28cf047a, 0x2437, 0x4b24, 0xb6, 0x53, 0xb9, 0x44, 0x6a, 0x41, 0x9a, 0x69); + +// {D0DA7AD6-AE80-4de5-AAFC-C126711E7593} +DEFINE_GUID(GUID_VIDEOREND, +0xd0da7ad6, 0xae80, 0x4de5, 0xaa, 0xfc, 0xc1, 0x26, 0x71, 0x1e, 0x75, 0x93); + +// {DC70AC3E-93E5-48db-88AB-E42064EC276A} +DEFINE_GUID(GUID_DSOUNDGLITCH, +0xdc70ac3e, 0x93e5, 0x48db, 0x88, 0xab, 0xe4, 0x20, 0x64, 0xec, 0x27, 0x6a); + +// {3d7e7d93-2fc8-4a07-a719-e0922ff2899} +DEFINE_GUID(GUID_STREAMTRACE, +0x3d7e7d93, 0x2fc8, 0x4a07, 0xa7, 0x19, 0xe0, 0x92, 0x2f, 0xf2, 0x89, 0x9e); + +// AZFIX: the following GUIDs aren't useful right now. + +// {3C33F7F5-EE54-493c-BA25-1656539C05AC} +DEFINE_GUID(GUID_GETTIME, +0x3c33f7f5, 0xee54, 0x493c, 0xba, 0x25, 0x16, 0x56, 0x53, 0x9c, 0x5, 0xac); + +// {CC44B44D-8169-4952-9E4A-A4E13295E492} +DEFINE_GUID(GUID_AUDIOREND, +0xcc44b44d, 0x8169, 0x4952, 0x9e, 0x4a, 0xa4, 0xe1, 0x32, 0x95, 0xe4, 0x92); + +// {775D19BF-4D8B-4de6-8DC9-66BAC7B310A2} +DEFINE_GUID(GUID_FRAMEDROP, +0x775d19bf, 0x4d8b, 0x4de6, 0x8d, 0xc9, 0x66, 0xba, 0xc7, 0xb3, 0x10, 0xa2); + +// {56D29065-EFBE-42dc-8C29-E325DC9C27D5} +DEFINE_GUID(GUID_AUDIOBREAK, +0x56d29065, 0xefbe, 0x42dc, 0x8c, 0x29, 0xe3, 0x25, 0xdc, 0x9c, 0x27, 0xd5); + +// {E1E6EA87-95A8-497e-BFBA-0295AEBCC707} +DEFINE_GUID(GUID_AUDIORECV, +0xe1e6ea87, 0x95a8, 0x497e, 0xbf, 0xba, 0x2, 0x95, 0xae, 0xbc, 0xc7, 0x7); + +// {10F7768A-B1E7-4242-AD90-A2D44683D9F0} +DEFINE_GUID(GUID_AUDIOSLAVE, +0x10f7768a, 0xb1e7, 0x4242, 0xad, 0x90, 0xa2, 0xd4, 0x46, 0x83, 0xd9, 0xf0); + +// {8983803D-691A-49bc-8FF6-962A39C0198F} +DEFINE_GUID(GUID_AUDIOADDBREAK, +0x8983803d, 0x691a, 0x49bc, 0x8f, 0xf6, 0x96, 0x2a, 0x39, 0xc0, 0x19, 0x8f); + +#define GLITCHTYPE_DSOUNDFIRSTGOOD 0 +#define GLITCHTYPE_DSOUNDFIRSTBAD 1 + +typedef struct PERFINFO_DSHOW_AUDIOGLITCH { + ULONGLONG cycleCounter; + DWORD glitchType; + LONGLONG sampleTime; + LONGLONG previousTime; + ULONG_PTR instanceId; +} PERFINFO_DSHOW_AUDIOGLITCH, *PPERFINFO_DSHOW_AUDIOGLITCH; + +typedef struct PERFINFO_WMI_AUDIOGLITCH { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_AUDIOGLITCH data; +} PERFINFO_WMI_AUDIO_GLITCH, *PPERFINFO_WMI_AUDIOGLITCH; + +typedef struct PERFINFO_DSHOW_GETTIME { + ULONGLONG cycleCounter; + ULONGLONG dshowClock; +} PERFINFO_DSHOW_GETTIME, *PPERFINFO_DSHOW_GETTIME; + +typedef struct PERFINFO_WMI_GETTIME { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_GETTIME data; +} PERFINFO_WMI_GETTIME, *PPERFINFO_WMI_GETTIME; + +typedef struct PERFINFO_DSHOW_AVREND { + ULONGLONG cycleCounter; + ULONGLONG dshowClock; + ULONGLONG sampleTime; +} PERFINFO_DSHOW_AVREND, *PPERFINFO_DSHOW_AVREND; + +typedef struct PERFINFO_WMI_AVREND { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_AVREND data; +} PERFINFO_WMI_AVREND, *PPERFINFO_WMI_AVREND; + +typedef struct PERFINFO_DSHOW_AUDIOBREAK { + ULONGLONG cycleCounter; + ULONGLONG dshowClock; + ULONGLONG sampleTime; + ULONGLONG sampleDuration; +} PERFINFO_DSHOW_AUDIOBREAK, *PPERFINFO_DSHOW_AUDIOBREAK; + +typedef struct PERFINFO_WMI_AUDIOBREAK { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_AUDIOBREAK data; +} PERFINFO_WMI_AUDIOBREAK, *PPERFINFO_WMI_AUDIOBREAK; + +typedef struct PERFINFO_DSHOW_FRAMEDROP { + ULONGLONG cycleCounter; + ULONGLONG dshowClock; + ULONGLONG frameTime; +} PERFINFO_DSHOW_FRAMEDROP, *PPERFINFO_DSHOW_FRAMEDROP; + +typedef struct PERFINFO_WMI_FRAMEDROP { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_FRAMEDROP data; +} PERFINFO_WMI_FRAMEDROP, *PPERFINFO_WMI_FRAMEDROP; + +#define PERFINFO_STREAMTRACE_MPEG2DEMUX_PTS_TRANSLATION 1 +#define PERFINFO_STREAMTRACE_MPEG2DEMUX_SAMPLE_RECEIVED 2 +#define PERFINFO_STREAMTRACE_VMR_BEGIN_ADVISE 3 +#define PERFINFO_STREAMTRACE_VMR_END_ADVISE 4 +#define PERFINFO_STREAMTRACE_VMR_RECEIVE 5 +#define PERFINFO_STREAMTRACE_VMR_BEGIN_DEINTERLACE 6 +#define PERFINFO_STREAMTRACE_VMR_END_DEINTERLACE 7 +#define PERFINFO_STREAMTRACE_VMR_BEGIN_DECODE 8 +#define PERFINFO_STREAMTRACE_VMR_END_DECODE 9 +#define PERFINFO_STREAMTRACE_VMR_DROPPED_FRAME 10 +#define PERFINFO_STREAMTRACE_ENCDEC_DTFILTERINPUT 11 +#define PERFINFO_STREAMTRACE_ENCDEC_DTFILTEROUTPUT 12 +#define PERFINFO_STREAMTRACE_ENCDEC_ETFILTERINPUT 13 +#define PERFINFO_STREAMTRACE_ENCDEC_ETFILTEROUTPUT 14 +#define PERFINFO_STREAMTRACE_ENCDEC_XDSCODECINPUT 15 +#define PERFINFO_STREAMTRACE_SBE_DVRANALYSISINPUT_RECEIVE 16 +#define PERFINFO_STREAMTRACE_SBE_DVRANALYSISINPUT_DELIVER 17 +#define PERFINFO_STREAMTRACE_SBE_DVRINPUTPIN_RECEIVE 18 +#define PERFINFO_STREAMTRACE_SBE_DVROUTPUTPIN_RECEIVE 19 +#define PERFINFO_STREAMTRACE_VMR_RENDER_TIME 20 + +typedef struct _PERFINFO_DSHOW_STREAMTRACE { + ULONG id; + ULONG reserved; + ULONGLONG dshowClock; + ULONGLONG data[ 4 ]; +} PERFINFO_DSHOW_STREAMTRACE, *PPERFINFO_DSHOW_STREAMTRACE; + +typedef struct _PERFINFO_WMI_STREAMTRACE { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_STREAMTRACE data; +} PERFINFO_WMI_STREAMTRACE, *PPERFINFO_WMI_STREAMTRACE; + + +typedef struct PERFINFO_DSHOW_AUDIORECV { + LONGLONG streamTime ; + LONGLONG sampleStart ; + LONGLONG sampleStop ; + LONGLONG hwduration ; + BOOL discontinuity ; +} PERFINFO_DSHOW_AUDIORECV, *PPERFINFO_DSHOW_AUDIORECV; + +typedef struct PERFINFO_WMI_AUDIORECV { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_AUDIORECV data; +} PERFINFO_WMI_AUDIORECV, *PPERFINFO_WMI_AUDIORECV; + +typedef struct PERFINFO_DSHOW_AUDIOSLAVE { + LONGLONG masterClock ; + LONGLONG slaveClock ; + LONGLONG errorAccum ; + LONGLONG lastHighErrorSeen ; + LONGLONG lastLowErrorSeen ; +} PERFINFO_DSHOW_AUDIOSLAVE, *PPERFINFO_DSHOW_AUDIOSLAVE; + +typedef struct PERFINFO_WMI_AUDIOSLAVE { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_AUDIOSLAVE data; +} PERFINFO_WMI_AUDIOSLAVE, *PPERFINFO_WMI_AUDIOSLAVE; + +typedef struct PERFINFO_DSHOW_AUDIOADDBREAK { + DWORD iterNextWrite ; + DWORD offsetNextWrite ; + DWORD iterWrite ; + DWORD offsetWrite ; +} PERFINFO_DSHOW_AUDIOADDBREAK, *PPERFINFO_DSHOW_AUDIOADDBREAK; + +typedef struct PERFINFO_WMI_AUDIOADDBREAK { + EVENT_TRACE_HEADER header; + PERFINFO_DSHOW_AUDIOADDBREAK data; +} PERFINFO_WMI_AUDIOADDBREAK, *PPERFINFO_WMI_AUDIOADDBREAK; + +#endif // _PREFSTRUCT_H_ diff --git a/RTSP Source Filter/Common/DirectShow/pstream.h b/RTSP Source Filter/Common/DirectShow/pstream.h new file mode 100644 index 0000000..2e278ab --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/pstream.h @@ -0,0 +1,114 @@ +//------------------------------------------------------------------------------ +// File: PStream.h +// +// Desc: DirectShow base classes - defines a class for persistent properties +// of filters. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __PSTREAM__ +#define __PSTREAM__ + +// Base class for persistent properties of filters +// (i.e. filter properties in saved graphs) + +// The simplest way to use this is: +// 1. Arrange for your filter to inherit this class +// 2. Implement in your class WriteToStream and ReadFromStream +// These will override the "do nothing" functions here. +// 3. Change your NonDelegatingQueryInterface to handle IPersistStream +// 4. Implement SizeMax to return the number of bytes of data you save. +// If you save UNICODE data, don't forget a char is 2 bytes. +// 5. Whenever your data changes, call SetDirty() +// +// At some point you may decide to alter, or extend the format of your data. +// At that point you will wish that you had a version number in all the old +// saved graphs, so that you can tell, when you read them, whether they +// represent the old or new form. To assist you in this, this class +// writes and reads a version number. +// When it writes, it calls GetSoftwareVersion() to enquire what version +// of the software we have at the moment. (In effect this is a version number +// of the data layout in the file). It writes this as the first thing in the data. +// If you want to change the version, implement (override) GetSoftwareVersion(). +// It reads this from the file into mPS_dwFileVersion before calling ReadFromStream, +// so in ReadFromStream you can check mPS_dwFileVersion to see if you are reading +// an old version file. +// Normally you should accept files whose version is no newer than the software +// version that's reading them. + + +// CPersistStream +// +// Implements IPersistStream. +// See 'OLE Programmers Reference (Vol 1):Structured Storage Overview' for +// more implementation information. +class CPersistStream : public IPersistStream { + private: + + // Internal state: + + protected: + DWORD mPS_dwFileVersion; // version number of file (being read) + BOOL mPS_fDirty; + + public: + + // IPersistStream methods + + STDMETHODIMP IsDirty() + {return (mPS_fDirty ? S_OK : S_FALSE);} // note FALSE means clean + STDMETHODIMP Load(LPSTREAM pStm); + STDMETHODIMP Save(LPSTREAM pStm, BOOL fClearDirty); + STDMETHODIMP GetSizeMax(__out ULARGE_INTEGER * pcbSize) + // Allow 24 bytes for version. + { pcbSize->QuadPart = 12*sizeof(WCHAR)+SizeMax(); return NOERROR; } + + // implementation + + CPersistStream(IUnknown *punk, __inout HRESULT *phr); + ~CPersistStream(); + + HRESULT SetDirty(BOOL fDirty) + { mPS_fDirty = fDirty; return NOERROR;} + + + // override to reveal IPersist & IPersistStream + // STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv); + + // --- IPersist --- + + // You must override this to provide your own class id + STDMETHODIMP GetClassID(__out CLSID *pClsid) PURE; + + // overrideable if you want + // file version number. Override it if you ever change format + virtual DWORD GetSoftwareVersion(void) { return 0; } + + + //========================================================================= + // OVERRIDE THESE to read and write your data + // OVERRIDE THESE to read and write your data + // OVERRIDE THESE to read and write your data + + virtual int SizeMax() {return 0;} + virtual HRESULT WriteToStream(IStream *pStream); + virtual HRESULT ReadFromStream(IStream *pStream); + //========================================================================= + + private: + +}; + + +// --- Useful helpers --- + + +// Writes an int to an IStream as UNICODE. +STDAPI WriteInt(IStream *pIStream, int n); + +// inverse of WriteInt +STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr); + +#endif // __PSTREAM__ diff --git a/RTSP Source Filter/Common/DirectShow/pullpin.h b/RTSP Source Filter/Common/DirectShow/pullpin.h new file mode 100644 index 0000000..db4f407 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/pullpin.h @@ -0,0 +1,152 @@ +//------------------------------------------------------------------------------ +// File: PullPin.h +// +// Desc: DirectShow base classes - defines CPullPin class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __PULLPIN_H__ +#define __PULLPIN_H__ + +// +// CPullPin +// +// object supporting pulling data from an IAsyncReader interface. +// Given a start/stop position, calls a pure Receive method with each +// IMediaSample received. +// +// This is essentially for use in a MemInputPin when it finds itself +// connected to an IAsyncReader pin instead of a pushing pin. +// + +class CPullPin : public CAMThread +{ + IAsyncReader* m_pReader; + REFERENCE_TIME m_tStart; + REFERENCE_TIME m_tStop; + REFERENCE_TIME m_tDuration; + BOOL m_bSync; + + enum ThreadMsg { + TM_Pause, // stop pulling and wait for next message + TM_Start, // start pulling + TM_Exit, // stop and exit + }; + + ThreadMsg m_State; + + // override pure thread proc from CAMThread + DWORD ThreadProc(void); + + // running pull method (check m_bSync) + void Process(void); + + // clean up any cancelled i/o after a flush + void CleanupCancelled(void); + + // suspend thread from pulling, eg during seek + HRESULT PauseThread(); + + // start thread pulling - create thread if necy + HRESULT StartThread(); + + // stop and close thread + HRESULT StopThread(); + + // called from ProcessAsync to queue and collect requests + HRESULT QueueSample( + __inout REFERENCE_TIME& tCurrent, + REFERENCE_TIME tAlignStop, + BOOL bDiscontinuity); + + HRESULT CollectAndDeliver( + REFERENCE_TIME tStart, + REFERENCE_TIME tStop); + + HRESULT DeliverSample( + IMediaSample* pSample, + REFERENCE_TIME tStart, + REFERENCE_TIME tStop); + +protected: + IMemAllocator * m_pAlloc; + +public: + CPullPin(); + virtual ~CPullPin(); + + // returns S_OK if successfully connected to an IAsyncReader interface + // from this object + // Optional allocator should be proposed as a preferred allocator if + // necessary + // bSync is TRUE if we are to use sync reads instead of the + // async methods. + HRESULT Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync); + + // disconnect any connection made in Connect + HRESULT Disconnect(); + + // agree an allocator using RequestAllocator - optional + // props param specifies your requirements (non-zero fields). + // returns an error code if fail to match requirements. + // optional IMemAllocator interface is offered as a preferred allocator + // but no error occurs if it can't be met. + virtual HRESULT DecideAllocator( + IMemAllocator* pAlloc, + __inout_opt ALLOCATOR_PROPERTIES * pProps); + + // set start and stop position. if active, will start immediately at + // the new position. Default is 0 to duration + HRESULT Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop); + + // return the total duration + HRESULT Duration(__out REFERENCE_TIME* ptDuration); + + // start pulling data + HRESULT Active(void); + + // stop pulling data + HRESULT Inactive(void); + + // helper functions + LONGLONG AlignDown(LONGLONG ll, LONG lAlign) { + // aligning downwards is just truncation + return ll & ~(lAlign-1); + }; + + LONGLONG AlignUp(LONGLONG ll, LONG lAlign) { + // align up: round up to next boundary + return (ll + (lAlign -1)) & ~(lAlign -1); + }; + + // GetReader returns the (addrefed) IAsyncReader interface + // for SyncRead etc + IAsyncReader* GetReader() { + m_pReader->AddRef(); + return m_pReader; + }; + + // -- pure -- + + // override this to handle data arrival + // return value other than S_OK will stop data + virtual HRESULT Receive(IMediaSample*) PURE; + + // override this to handle end-of-stream + virtual HRESULT EndOfStream(void) PURE; + + // called on runtime errors that will have caused pulling + // to stop + // these errors are all returned from the upstream filter, who + // will have already reported any errors to the filtergraph. + virtual void OnError(HRESULT hr) PURE; + + // flush this pin and all downstream + virtual HRESULT BeginFlush() PURE; + virtual HRESULT EndFlush() PURE; + +}; + +#endif //__PULLPIN_H__ diff --git a/RTSP Source Filter/Common/DirectShow/refclock.h b/RTSP Source Filter/Common/DirectShow/refclock.h new file mode 100644 index 0000000..df822e0 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/refclock.h @@ -0,0 +1,184 @@ +//------------------------------------------------------------------------------ +// File: RefClock.h +// +// Desc: DirectShow base classes - defines the IReferenceClock interface. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __BASEREFCLOCK__ +#define __BASEREFCLOCK__ + +#include + +const UINT RESOLUTION = 1; /* High resolution timer */ +const INT ADVISE_CACHE = 4; /* Default cache size */ +const LONGLONG MAX_TIME = 0x7FFFFFFFFFFFFFFF; /* Maximum LONGLONG value */ + +inline LONGLONG WINAPI ConvertToMilliseconds(const REFERENCE_TIME& RT) +{ + /* This converts an arbitrary value representing a reference time + into a MILLISECONDS value for use in subsequent system calls */ + + return (RT / (UNITS / MILLISECONDS)); +} + +/* This class hierarchy will support an IReferenceClock interface so + that an audio card (or other externally driven clock) can update the + system wide clock that everyone uses. + + The interface will be pretty thin with probably just one update method + This interface has not yet been defined. + */ + +/* This abstract base class implements the IReferenceClock + * interface. Classes that actually provide clock signals (from + * whatever source) have to be derived from this class. + * + * The abstract class provides implementations for: + * CUnknown support + * locking support (CCritSec) + * client advise code (creates a thread) + * + * Question: what can we do about quality? Change the timer + * resolution to lower the system load? Up the priority of the + * timer thread to force more responsive signals? + * + * During class construction we create a worker thread that is destroyed during + * destuction. This thread executes a series of WaitForSingleObject calls, + * waking up when a command is given to the thread or the next wake up point + * is reached. The wakeup points are determined by clients making Advise + * calls. + * + * Each advise call defines a point in time when they wish to be notified. A + * periodic advise is a series of these such events. We maintain a list of + * advise links and calculate when the nearest event notification is due for. + * We then call WaitForSingleObject with a timeout equal to this time. The + * handle we wait on is used by the class to signal that something has changed + * and that we must reschedule the next event. This typically happens when + * someone comes in and asks for an advise link while we are waiting for an + * event to timeout. + * + * While we are modifying the list of advise requests we + * are protected from interference through a critical section. Clients are NOT + * advised through callbacks. One shot clients have an event set, while + * periodic clients have a semaphore released for each event notification. A + * semaphore allows a client to be kept up to date with the number of events + * actually triggered and be assured that they can't miss multiple events being + * set. + * + * Keeping track of advises is taken care of by the CAMSchedule class. + */ + +class CBaseReferenceClock +: public CUnknown, public IReferenceClock, public CCritSec, public IReferenceClockTimerControl +{ +protected: + virtual ~CBaseReferenceClock(); // Don't let me be created on the stack! +public: + CBaseReferenceClock(__in_opt LPCTSTR pName, + __inout_opt LPUNKNOWN pUnk, + __inout HRESULT *phr, + __inout_opt CAMSchedule * pSched = 0 ); + + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); + + DECLARE_IUNKNOWN + + /* IReferenceClock methods */ + // Derived classes must implement GetPrivateTime(). All our GetTime + // does is call GetPrivateTime and then check so that time does not + // go backwards. A return code of S_FALSE implies that the internal + // clock has gone backwards and GetTime time has halted until internal + // time has caught up. (Don't know if this will be much use to folk, + // but it seems odd not to use the return code for something useful.) + STDMETHODIMP GetTime(__out REFERENCE_TIME *pTime); + // When this is called, it sets m_rtLastGotTime to the time it returns. + + /* Provide standard mechanisms for scheduling events */ + + /* Ask for an async notification that a time has elapsed */ + STDMETHODIMP AdviseTime( + REFERENCE_TIME baseTime, // base reference time + REFERENCE_TIME streamTime, // stream offset time + HEVENT hEvent, // advise via this event + __out DWORD_PTR *pdwAdviseCookie// where your cookie goes + ); + + /* Ask for an asynchronous periodic notification that a time has elapsed */ + STDMETHODIMP AdvisePeriodic( + REFERENCE_TIME StartTime, // starting at this time + REFERENCE_TIME PeriodTime, // time between notifications + HSEMAPHORE hSemaphore, // advise via a semaphore + __out DWORD_PTR *pdwAdviseCookie// where your cookie goes + ); + + /* Cancel a request for notification(s) - if the notification was + * a one shot timer then this function doesn't need to be called + * as the advise is automatically cancelled, however it does no + * harm to explicitly cancel a one-shot advise. It is REQUIRED that + * clients call Unadvise to clear a Periodic advise setting. + */ + + STDMETHODIMP Unadvise(DWORD_PTR dwAdviseCookie); + + /* Methods for the benefit of derived classes or outer objects */ + + // GetPrivateTime() is the REAL clock. GetTime is just a cover for + // it. Derived classes will probably override this method but not + // GetTime() itself. + // The important point about GetPrivateTime() is it's allowed to go + // backwards. Our GetTime() will keep returning the LastGotTime + // until GetPrivateTime() catches up. + virtual REFERENCE_TIME GetPrivateTime(); + + /* Provide a method for correcting drift */ + STDMETHODIMP SetTimeDelta( const REFERENCE_TIME& TimeDelta ); + + CAMSchedule * GetSchedule() const { return m_pSchedule; } + + // IReferenceClockTimerControl methods + // + // Setting a default of 0 disables the default of 1ms + STDMETHODIMP SetDefaultTimerResolution( + REFERENCE_TIME timerResolution // in 100ns + ); + STDMETHODIMP GetDefaultTimerResolution( + __out REFERENCE_TIME* pTimerResolution // in 100ns + ); + +private: + REFERENCE_TIME m_rtPrivateTime; // Current best estimate of time + DWORD m_dwPrevSystemTime; // Last vaule we got from timeGetTime + REFERENCE_TIME m_rtLastGotTime; // Last time returned by GetTime + REFERENCE_TIME m_rtNextAdvise; // Time of next advise + UINT m_TimerResolution; + +#ifdef PERF + int m_idGetSystemTime; +#endif + +// Thread stuff +public: + void TriggerThread() // Wakes thread up. Need to do this if + { // time to next advise needs reevaluating. + EXECUTE_ASSERT(SetEvent(m_pSchedule->GetEvent())); + } + + +private: + BOOL m_bAbort; // Flag used for thread shutdown + HANDLE m_hThread; // Thread handle + + HRESULT AdviseThread(); // Method in which the advise thread runs + static DWORD __stdcall AdviseThreadFunction(__in LPVOID); // Function used to get there + +protected: + CAMSchedule * m_pSchedule; + + void Restart (IN REFERENCE_TIME rtMinTime = 0I64) ; +}; + +#endif + diff --git a/RTSP Source Filter/Common/DirectShow/reftime.h b/RTSP Source Filter/Common/DirectShow/reftime.h new file mode 100644 index 0000000..0ed32f6 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/reftime.h @@ -0,0 +1,116 @@ +//------------------------------------------------------------------------------ +// File: RefTime.h +// +// Desc: DirectShow base classes - defines CRefTime, a class that manages +// reference times. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// +// CRefTime +// +// Manage reference times. +// Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual) +// functions providing simple comparison, conversion and arithmetic. +// +// A reference time (at the moment) is a unit of seconds represented in +// 100ns units as is used in the Win32 FILETIME structure. BUT the time +// a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it +// will either be stream time or reference time depending upon context +// +// This class provides simple arithmetic operations on reference times +// +// keep non-virtual otherwise the data layout will not be the same as +// REFERENCE_TIME + + +// ----- +// note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but +// you will need to do so explicitly +// ----- + + +#ifndef __REFTIME__ +#define __REFTIME__ + + +const LONGLONG MILLISECONDS = (1000); // 10 ^ 3 +const LONGLONG NANOSECONDS = (1000000000); // 10 ^ 9 +const LONGLONG UNITS = (NANOSECONDS / 100); // 10 ^ 7 + +/* Unfortunately an inline function here generates a call to __allmul + - even for constants! +*/ +#define MILLISECONDS_TO_100NS_UNITS(lMs) \ + Int32x32To64((lMs), (UNITS / MILLISECONDS)) + +class CRefTime +{ +public: + + // *MUST* be the only data member so that this class is exactly + // equivalent to a REFERENCE_TIME. + // Also, must be *no virtual functions* + + REFERENCE_TIME m_time; + + inline CRefTime() + { + // default to 0 time + m_time = 0; + }; + + inline CRefTime(LONG msecs) + { + m_time = MILLISECONDS_TO_100NS_UNITS(msecs); + }; + + inline CRefTime(REFERENCE_TIME rt) + { + m_time = rt; + }; + + inline operator REFERENCE_TIME() const + { + return m_time; + }; + + inline CRefTime& operator=(const CRefTime& rt) + { + m_time = rt.m_time; + return *this; + }; + + inline CRefTime& operator=(const LONGLONG ll) + { + m_time = ll; + return *this; + }; + + inline CRefTime& operator+=(const CRefTime& rt) + { + return (*this = *this + rt); + }; + + inline CRefTime& operator-=(const CRefTime& rt) + { + return (*this = *this - rt); + }; + + inline LONG Millisecs(void) + { + return (LONG)(m_time / (UNITS / MILLISECONDS)); + }; + + inline LONGLONG GetUnits(void) + { + return m_time; + }; +}; + +const LONGLONG TimeZero = 0; + +#endif /* __REFTIME__ */ + diff --git a/RTSP Source Filter/Common/DirectShow/renbase.h b/RTSP Source Filter/Common/DirectShow/renbase.h new file mode 100644 index 0000000..8634c6b --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/renbase.h @@ -0,0 +1,478 @@ +//------------------------------------------------------------------------------ +// File: RenBase.h +// +// Desc: DirectShow base classes - defines a generic ActiveX base renderer +// class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __RENBASE__ +#define __RENBASE__ + +// Forward class declarations + +class CBaseRenderer; +class CBaseVideoRenderer; +class CRendererInputPin; + +// This is our input pin class that channels calls to the renderer + +class CRendererInputPin : public CBaseInputPin +{ +protected: + + CBaseRenderer *m_pRenderer; + +public: + + CRendererInputPin(__inout CBaseRenderer *pRenderer, + __inout HRESULT *phr, + __in_opt LPCWSTR Name); + + // Overriden from the base pin classes + + HRESULT BreakConnect(); + HRESULT CompleteConnect(IPin *pReceivePin); + HRESULT SetMediaType(const CMediaType *pmt); + HRESULT CheckMediaType(const CMediaType *pmt); + HRESULT Active(); + HRESULT Inactive(); + + // Add rendering behaviour to interface functions + + STDMETHODIMP QueryId(__deref_out LPWSTR *Id); + STDMETHODIMP EndOfStream(); + STDMETHODIMP BeginFlush(); + STDMETHODIMP EndFlush(); + STDMETHODIMP Receive(IMediaSample *pMediaSample); + + // Helper + IMemAllocator inline *Allocator() const + { + return m_pAllocator; + } +}; + +// Main renderer class that handles synchronisation and state changes + +class CBaseRenderer : public CBaseFilter +{ +protected: + + friend class CRendererInputPin; + + friend void CALLBACK EndOfStreamTimer(UINT uID, // Timer identifier + UINT uMsg, // Not currently used + DWORD_PTR dwUser, // User information + DWORD_PTR dw1, // Windows reserved + DWORD_PTR dw2); // Is also reserved + + CRendererPosPassThru *m_pPosition; // Media seeking pass by object + CAMEvent m_RenderEvent; // Used to signal timer events + CAMEvent m_ThreadSignal; // Signalled to release worker thread + CAMEvent m_evComplete; // Signalled when state complete + BOOL m_bAbort; // Stop us from rendering more data + BOOL m_bStreaming; // Are we currently streaming + DWORD_PTR m_dwAdvise; // Timer advise cookie + IMediaSample *m_pMediaSample; // Current image media sample + BOOL m_bEOS; // Any more samples in the stream + BOOL m_bEOSDelivered; // Have we delivered an EC_COMPLETE + CRendererInputPin *m_pInputPin; // Our renderer input pin object + CCritSec m_InterfaceLock; // Critical section for interfaces + CCritSec m_RendererLock; // Controls access to internals + IQualityControl * m_pQSink; // QualityControl sink + BOOL m_bRepaintStatus; // Can we signal an EC_REPAINT + // Avoid some deadlocks by tracking filter during stop + volatile BOOL m_bInReceive; // Inside Receive between PrepareReceive + // And actually processing the sample + REFERENCE_TIME m_SignalTime; // Time when we signal EC_COMPLETE + UINT m_EndOfStreamTimer; // Used to signal end of stream + CCritSec m_ObjectCreationLock; // This lock protects the creation and + // of m_pPosition and m_pInputPin. It + // ensures that two threads cannot create + // either object simultaneously. + +public: + + CBaseRenderer(REFCLSID RenderClass, // CLSID for this renderer + __in_opt LPCTSTR pName, // Debug ONLY description + __inout_opt LPUNKNOWN pUnk, // Aggregated owner object + __inout HRESULT *phr); // General OLE return code + + ~CBaseRenderer(); + + // Overriden to say what interfaces we support and where + + virtual HRESULT GetMediaPositionInterface(REFIID riid, __deref_out void **ppv); + STDMETHODIMP NonDelegatingQueryInterface(REFIID, __deref_out void **); + + virtual HRESULT SourceThreadCanWait(BOOL bCanWait); + +#ifdef DEBUG + // Debug only dump of the renderer state + void DisplayRendererState(); +#endif + virtual HRESULT WaitForRenderTime(); + virtual HRESULT CompleteStateChange(FILTER_STATE OldState); + + // Return internal information about this filter + + BOOL IsEndOfStream() { return m_bEOS; }; + BOOL IsEndOfStreamDelivered() { return m_bEOSDelivered; }; + BOOL IsStreaming() { return m_bStreaming; }; + void SetAbortSignal(BOOL bAbort) { m_bAbort = bAbort; }; + virtual void OnReceiveFirstSample(IMediaSample *pMediaSample) { }; + CAMEvent *GetRenderEvent() { return &m_RenderEvent; }; + + // Permit access to the transition state + + void Ready() { m_evComplete.Set(); }; + void NotReady() { m_evComplete.Reset(); }; + BOOL CheckReady() { return m_evComplete.Check(); }; + + virtual int GetPinCount(); + virtual CBasePin *GetPin(int n); + FILTER_STATE GetRealState(); + void SendRepaint(); + void SendNotifyWindow(IPin *pPin,HWND hwnd); + BOOL OnDisplayChange(); + void SetRepaintStatus(BOOL bRepaint); + + // Override the filter and pin interface functions + + STDMETHODIMP Stop(); + STDMETHODIMP Pause(); + STDMETHODIMP Run(REFERENCE_TIME StartTime); + STDMETHODIMP GetState(DWORD dwMSecs, __out FILTER_STATE *State); + STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin); + + // These are available for a quality management implementation + + virtual void OnRenderStart(IMediaSample *pMediaSample); + virtual void OnRenderEnd(IMediaSample *pMediaSample); + virtual HRESULT OnStartStreaming() { return NOERROR; }; + virtual HRESULT OnStopStreaming() { return NOERROR; }; + virtual void OnWaitStart() { }; + virtual void OnWaitEnd() { }; + virtual void PrepareRender() { }; + +#ifdef PERF + REFERENCE_TIME m_trRenderStart; // Just before we started drawing + // Set in OnRenderStart, Used in OnRenderEnd + int m_idBaseStamp; // MSR_id for frame time stamp + int m_idBaseRenderTime; // MSR_id for true wait time + int m_idBaseAccuracy; // MSR_id for time frame is late (int) +#endif + + // Quality management implementation for scheduling rendering + + virtual BOOL ScheduleSample(IMediaSample *pMediaSample); + virtual HRESULT GetSampleTimes(IMediaSample *pMediaSample, + __out REFERENCE_TIME *pStartTime, + __out REFERENCE_TIME *pEndTime); + + virtual HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample, + __out REFERENCE_TIME *ptrStart, + __out REFERENCE_TIME *ptrEnd); + + // Lots of end of stream complexities + + void TimerCallback(); + void ResetEndOfStreamTimer(); + HRESULT NotifyEndOfStream(); + virtual HRESULT SendEndOfStream(); + virtual HRESULT ResetEndOfStream(); + virtual HRESULT EndOfStream(); + + // Rendering is based around the clock + + void SignalTimerFired(); + virtual HRESULT CancelNotification(); + virtual HRESULT ClearPendingSample(); + + // Called when the filter changes state + + virtual HRESULT Active(); + virtual HRESULT Inactive(); + virtual HRESULT StartStreaming(); + virtual HRESULT StopStreaming(); + virtual HRESULT BeginFlush(); + virtual HRESULT EndFlush(); + + // Deal with connections and type changes + + virtual HRESULT BreakConnect(); + virtual HRESULT SetMediaType(const CMediaType *pmt); + virtual HRESULT CompleteConnect(IPin *pReceivePin); + + // These look after the handling of data samples + + virtual HRESULT PrepareReceive(IMediaSample *pMediaSample); + virtual HRESULT Receive(IMediaSample *pMediaSample); + virtual BOOL HaveCurrentSample(); + virtual IMediaSample *GetCurrentSample(); + virtual HRESULT Render(IMediaSample *pMediaSample); + + // Derived classes MUST override these + virtual HRESULT DoRenderSample(IMediaSample *pMediaSample) PURE; + virtual HRESULT CheckMediaType(const CMediaType *) PURE; + + // Helper + void WaitForReceiveToComplete(); +}; + + +// CBaseVideoRenderer is a renderer class (see its ancestor class) and +// it handles scheduling of media samples so that they are drawn at the +// correct time by the reference clock. It implements a degradation +// strategy. Possible degradation modes are: +// Drop frames here (only useful if the drawing takes significant time) +// Signal supplier (upstream) to drop some frame(s) - i.e. one-off skip. +// Signal supplier to change the frame rate - i.e. ongoing skipping. +// Or any combination of the above. +// In order to determine what's useful to try we need to know what's going +// on. This is done by timing various operations (including the supplier). +// This timing is done by using timeGetTime as it is accurate enough and +// usually cheaper than calling the reference clock. It also tells the +// truth if there is an audio break and the reference clock stops. +// We provide a number of public entry points (named OnXxxStart, OnXxxEnd) +// which the rest of the renderer calls at significant moments. These do +// the timing. + +// the number of frames that the sliding averages are averaged over. +// the rule is (1024*NewObservation + (AVGPERIOD-1) * PreviousAverage)/AVGPERIOD +#define AVGPERIOD 4 +#define DO_MOVING_AVG(avg,obs) (avg = (1024*obs + (AVGPERIOD-1)*avg)/AVGPERIOD) +// Spot the bug in this macro - I can't. but it doesn't work! + +class CBaseVideoRenderer : public CBaseRenderer, // Base renderer class + public IQualProp, // Property page guff + public IQualityControl // Allow throttling +{ +protected: + + // Hungarian: + // tFoo is the time Foo in mSec (beware m_tStart from filter.h) + // trBar is the time Bar by the reference clock + + //****************************************************************** + // State variables to control synchronisation + //****************************************************************** + + // Control of sending Quality messages. We need to know whether + // we are in trouble (e.g. frames being dropped) and where the time + // is being spent. + + // When we drop a frame we play the next one early. + // The frame after that is likely to wait before drawing and counting this + // wait as spare time is unfair, so we count it as a zero wait. + // We therefore need to know whether we are playing frames early or not. + + int m_nNormal; // The number of consecutive frames + // drawn at their normal time (not early) + // -1 means we just dropped a frame. + +#ifdef PERF + BOOL m_bDrawLateFrames; // Don't drop any frames (debug and I'm + // not keen on people using it!) +#endif + + BOOL m_bSupplierHandlingQuality;// The response to Quality messages says + // our supplier is handling things. + // We will allow things to go extra late + // before dropping frames. We will play + // very early after he has dropped one. + + // Control of scheduling, frame dropping etc. + // We need to know where the time is being spent so as to tell whether + // we should be taking action here, signalling supplier or what. + // The variables are initialised to a mode of NOT dropping frames. + // They will tell the truth after a few frames. + // We typically record a start time for an event, later we get the time + // again and subtract to get the elapsed time, and we average this over + // a few frames. The average is used to tell what mode we are in. + + // Although these are reference times (64 bit) they are all DIFFERENCES + // between times which are small. An int will go up to 214 secs before + // overflow. Avoiding 64 bit multiplications and divisions seems + // worth while. + + + + // Audio-video throttling. If the user has turned up audio quality + // very high (in principle it could be any other stream, not just audio) + // then we can receive cries for help via the graph manager. In this case + // we put in a wait for some time after rendering each frame. + int m_trThrottle; + + // The time taken to render (i.e. BitBlt) frames controls which component + // needs to degrade. If the blt is expensive, the renderer degrades. + // If the blt is cheap it's done anyway and the supplier degrades. + int m_trRenderAvg; // Time frames are taking to blt + int m_trRenderLast; // Time for last frame blt + int m_tRenderStart; // Just before we started drawing (mSec) + // derived from timeGetTime. + + // When frames are dropped we will play the next frame as early as we can. + // If it was a false alarm and the machine is fast we slide gently back to + // normal timing. To do this, we record the offset showing just how early + // we really are. This will normally be negative meaning early or zero. + int m_trEarliness; + + // Target provides slow long-term feedback to try to reduce the + // average sync offset to zero. Whenever a frame is actually rendered + // early we add a msec or two, whenever late we take off a few. + // We add or take off 1/32 of the error time. + // Eventually we should be hovering around zero. For a really bad case + // where we were (say) 300mSec off, it might take 100 odd frames to + // settle down. The rate of change of this is intended to be slower + // than any other mechanism in Quartz, thereby avoiding hunting. + int m_trTarget; + + // The proportion of time spent waiting for the right moment to blt + // controls whether we bother to drop a frame or whether we reckon that + // we're doing well enough that we can stand a one-frame glitch. + int m_trWaitAvg; // Average of last few wait times + // (actually we just average how early + // we were). Negative here means LATE. + + // The average inter-frame time. + // This is used to calculate the proportion of the time used by the + // three operations (supplying us, waiting, rendering) + int m_trFrameAvg; // Average inter-frame time + int m_trDuration; // duration of last frame. + +#ifdef PERF + // Performance logging identifiers + int m_idTimeStamp; // MSR_id for frame time stamp + int m_idEarliness; // MSR_id for earliness fudge + int m_idTarget; // MSR_id for Target fudge + int m_idWaitReal; // MSR_id for true wait time + int m_idWait; // MSR_id for wait time recorded + int m_idFrameAccuracy; // MSR_id for time frame is late (int) + int m_idRenderAvg; // MSR_id for Render time recorded (int) + int m_idSchLateTime; // MSR_id for lateness at scheduler + int m_idQualityRate; // MSR_id for Quality rate requested + int m_idQualityTime; // MSR_id for Quality time requested + int m_idDecision; // MSR_id for decision code + int m_idDuration; // MSR_id for duration of a frame + int m_idThrottle; // MSR_id for audio-video throttling + //int m_idDebug; // MSR_id for trace style debugging + //int m_idSendQuality; // MSR_id for timing the notifications per se +#endif // PERF + REFERENCE_TIME m_trRememberStampForPerf; // original time stamp of frame + // with no earliness fudges etc. +#ifdef PERF + REFERENCE_TIME m_trRememberFrameForPerf; // time when previous frame rendered + + // debug... + int m_idFrameAvg; + int m_idWaitAvg; +#endif + + // PROPERTY PAGE + // This has edit fields that show the user what's happening + // These member variables hold these counts. + + int m_cFramesDropped; // cumulative frames dropped IN THE RENDERER + int m_cFramesDrawn; // Frames since streaming started seen BY THE + // RENDERER (some may be dropped upstream) + + // Next two support average sync offset and standard deviation of sync offset. + LONGLONG m_iTotAcc; // Sum of accuracies in mSec + LONGLONG m_iSumSqAcc; // Sum of squares of (accuracies in mSec) + + // Next two allow jitter calculation. Jitter is std deviation of frame time. + REFERENCE_TIME m_trLastDraw; // Time of prev frame (for inter-frame times) + LONGLONG m_iSumSqFrameTime; // Sum of squares of (inter-frame time in mSec) + LONGLONG m_iSumFrameTime; // Sum of inter-frame times in mSec + + // To get performance statistics on frame rate, jitter etc, we need + // to record the lateness and inter-frame time. What we actually need are the + // data above (sum, sum of squares and number of entries for each) but the data + // is generated just ahead of time and only later do we discover whether the + // frame was actually drawn or not. So we have to hang on to the data + int m_trLate; // hold onto frame lateness + int m_trFrame; // hold onto inter-frame time + + int m_tStreamingStart; // if streaming then time streaming started + // else time of last streaming session + // used for property page statistics +#ifdef PERF + LONGLONG m_llTimeOffset; // timeGetTime()*10000+m_llTimeOffset==ref time +#endif + +public: + + + CBaseVideoRenderer(REFCLSID RenderClass, // CLSID for this renderer + __in_opt LPCTSTR pName, // Debug ONLY description + __inout_opt LPUNKNOWN pUnk, // Aggregated owner object + __inout HRESULT *phr); // General OLE return code + + ~CBaseVideoRenderer(); + + // IQualityControl methods - Notify allows audio-video throttling + + STDMETHODIMP SetSink( IQualityControl * piqc); + STDMETHODIMP Notify( IBaseFilter * pSelf, Quality q); + + // These provide a full video quality management implementation + + void OnRenderStart(IMediaSample *pMediaSample); + void OnRenderEnd(IMediaSample *pMediaSample); + void OnWaitStart(); + void OnWaitEnd(); + HRESULT OnStartStreaming(); + HRESULT OnStopStreaming(); + void ThrottleWait(); + + // Handle the statistics gathering for our quality management + + void PreparePerformanceData(int trLate, int trFrame); + virtual void RecordFrameLateness(int trLate, int trFrame); + virtual void OnDirectRender(IMediaSample *pMediaSample); + virtual HRESULT ResetStreamingTimes(); + BOOL ScheduleSample(IMediaSample *pMediaSample); + HRESULT ShouldDrawSampleNow(IMediaSample *pMediaSample, + __inout REFERENCE_TIME *ptrStart, + __inout REFERENCE_TIME *ptrEnd); + + virtual HRESULT SendQuality(REFERENCE_TIME trLate, REFERENCE_TIME trRealStream); + STDMETHODIMP JoinFilterGraph(__inout_opt IFilterGraph * pGraph, __in_opt LPCWSTR pName); + + // + // Do estimates for standard deviations for per-frame + // statistics + // + // *piResult = (llSumSq - iTot * iTot / m_cFramesDrawn - 1) / + // (m_cFramesDrawn - 2) + // or 0 if m_cFramesDrawn <= 3 + // + HRESULT GetStdDev( + int nSamples, + __out int *piResult, + LONGLONG llSumSq, + LONGLONG iTot + ); +public: + + // IQualProp property page support + + STDMETHODIMP get_FramesDroppedInRenderer(__out int *cFramesDropped); + STDMETHODIMP get_FramesDrawn(__out int *pcFramesDrawn); + STDMETHODIMP get_AvgFrameRate(__out int *piAvgFrameRate); + STDMETHODIMP get_Jitter(__out int *piJitter); + STDMETHODIMP get_AvgSyncOffset(__out int *piAvg); + STDMETHODIMP get_DevSyncOffset(__out int *piDev); + + // Implement an IUnknown interface and expose IQualProp + + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out VOID **ppv); +}; + +#endif // __RENBASE__ + diff --git a/RTSP Source Filter/Common/DirectShow/schedule.h b/RTSP Source Filter/Common/DirectShow/schedule.h new file mode 100644 index 0000000..65ed402 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/schedule.h @@ -0,0 +1,128 @@ +//------------------------------------------------------------------------------ +// File: Schedule.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __CAMSchedule__ +#define __CAMSchedule__ + +class CAMSchedule : private CBaseObject +{ +public: + virtual ~CAMSchedule(); + // ev is the event we should fire if the advise time needs re-evaluating + CAMSchedule( HANDLE ev ); + + DWORD GetAdviseCount(); + REFERENCE_TIME GetNextAdviseTime(); + + // We need a method for derived classes to add advise packets, we return the cookie + DWORD_PTR AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic ); + // And a way to cancel + HRESULT Unadvise(DWORD_PTR dwAdviseCookie); + + // Tell us the time please, and we'll dispatch the expired events. We return the time of the next event. + // NB: The time returned will be "useless" if you start adding extra Advises. But that's the problem of + // whoever is using this helper class (typically a clock). + REFERENCE_TIME Advise( const REFERENCE_TIME & rtTime ); + + // Get the event handle which will be set if advise time requires re-evaluation. + HANDLE GetEvent() const { return m_ev; } + +private: + // We define the nodes that will be used in our singly linked list + // of advise packets. The list is ordered by time, with the + // elements that will expire first at the front. + class CAdvisePacket + { + public: + CAdvisePacket() + {} + + CAdvisePacket * m_next; + DWORD_PTR m_dwAdviseCookie; + REFERENCE_TIME m_rtEventTime; // Time at which event should be set + REFERENCE_TIME m_rtPeriod; // Periodic time + HANDLE m_hNotify; // Handle to event or semephore + BOOL m_bPeriodic; // TRUE => Periodic event + + CAdvisePacket( __inout_opt CAdvisePacket * next, LONGLONG time ) : m_next(next), m_rtEventTime(time) + {} + + void InsertAfter( __inout CAdvisePacket * p ) + { + p->m_next = m_next; + m_next = p; + } + + int IsZ() const // That is, is it the node that represents the end of the list + { return m_next == 0; } + + CAdvisePacket * RemoveNext() + { + CAdvisePacket *const next = m_next; + CAdvisePacket *const new_next = next->m_next; + m_next = new_next; + return next; + } + + void DeleteNext() + { + delete RemoveNext(); + } + + CAdvisePacket * Next() const + { + CAdvisePacket * result = m_next; + if (result->IsZ()) result = 0; + return result; + } + + DWORD_PTR Cookie() const + { return m_dwAdviseCookie; } + }; + + // Structure is: + // head -> elmt1 -> elmt2 -> z -> null + // So an empty list is: head -> z -> null + // Having head & z as links makes insertaion, + // deletion and shunting much easier. + CAdvisePacket head, z; // z is both a tail and a sentry + + volatile DWORD_PTR m_dwNextCookie; // Strictly increasing + volatile DWORD m_dwAdviseCount; // Number of elements on list + + CCritSec m_Serialize; + + // AddAdvisePacket: adds the packet, returns the cookie (0 if failed) + DWORD_PTR AddAdvisePacket( __inout CAdvisePacket * pPacket ); + // Event that we should set if the packed added above will be the next to fire. + const HANDLE m_ev; + + // A Shunt is where we have changed the first element in the + // list and want it re-evaluating (i.e. repositioned) in + // the list. + void ShuntHead(); + + // Rather than delete advise packets, we cache them for future use + CAdvisePacket * m_pAdviseCache; + DWORD m_dwCacheCount; + enum { dwCacheMax = 5 }; // Don't bother caching more than five + + void Delete( __inout CAdvisePacket * pLink );// This "Delete" will cache the Link + +// Attributes and methods for debugging +public: +#ifdef DEBUG + void DumpLinkedList(); +#else + void DumpLinkedList() {} +#endif + +}; + +#endif // __CAMSchedule__ diff --git a/RTSP Source Filter/Common/DirectShow/seekpt.h b/RTSP Source Filter/Common/DirectShow/seekpt.h new file mode 100644 index 0000000..26abdf3 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/seekpt.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// File: SeekPT.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __seekpt_h__ +#define __seekpt_h__ + + +class CSeekingPassThru : public ISeekingPassThru, public CUnknown +{ +public: + static CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr); + CSeekingPassThru(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr); + ~CSeekingPassThru(); + + DECLARE_IUNKNOWN; + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); + + STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin); + +private: + CPosPassThru *m_pPosPassThru; +}; + +#endif diff --git a/RTSP Source Filter/Common/DirectShow/source.h b/RTSP Source Filter/Common/DirectShow/source.h new file mode 100644 index 0000000..e6e451b --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/source.h @@ -0,0 +1,172 @@ +//------------------------------------------------------------------------------ +// File: Source.h +// +// Desc: DirectShow base classes - defines classes to simplify creation of +// ActiveX source filters that support continuous generation of data. +// No support is provided for IMediaControl or IMediaPosition. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// +// Derive your source filter from CSource. +// During construction either: +// Create some CSourceStream objects to manage your pins +// Provide the user with a means of doing so eg, an IPersistFile interface. +// +// CSource provides: +// IBaseFilter interface management +// IMediaFilter interface management, via CBaseFilter +// Pin counting for CBaseFilter +// +// Derive a class from CSourceStream to manage your output pin types +// Implement GetMediaType/1 to return the type you support. If you support multiple +// types then overide GetMediaType/3, CheckMediaType and GetMediaTypeCount. +// Implement Fillbuffer() to put data into one buffer. +// +// CSourceStream provides: +// IPin management via CBaseOutputPin +// Worker thread management + +#ifndef __CSOURCE__ +#define __CSOURCE__ + +class CSourceStream; // The class that will handle each pin + + +// +// CSource +// +// Override construction to provide a means of creating +// CSourceStream derived objects - ie a way of creating pins. +class CSource : public CBaseFilter { +public: + + CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr); + CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid); +#ifdef UNICODE + CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr); + CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid); +#endif + ~CSource(); + + int GetPinCount(void); + CBasePin *GetPin(int n); + + // -- Utilities -- + + CCritSec* pStateLock(void) { return &m_cStateLock; } // provide our critical section + + HRESULT AddPin(__in CSourceStream *); + HRESULT RemovePin(__in CSourceStream *); + + STDMETHODIMP FindPin( + LPCWSTR Id, + __deref_out IPin ** ppPin + ); + + int FindPinNumber(__in IPin *iPin); + +protected: + + int m_iPins; // The number of pins on this filter. Updated by CSourceStream + // constructors & destructors. + CSourceStream **m_paStreams; // the pins on this filter. + + CCritSec m_cStateLock; // Lock this to serialize function accesses to the filter state + +}; + + +// +// CSourceStream +// +// Use this class to manage a stream of data that comes from a +// pin. +// Uses a worker thread to put data on the pin. +class CSourceStream : public CAMThread, public CBaseOutputPin { +public: + + CSourceStream(__in_opt LPCTSTR pObjectName, + __inout HRESULT *phr, + __inout CSource *pms, + __in_opt LPCWSTR pName); +#ifdef UNICODE + CSourceStream(__in_opt LPCSTR pObjectName, + __inout HRESULT *phr, + __inout CSource *pms, + __in_opt LPCWSTR pName); +#endif + virtual ~CSourceStream(void); // virtual destructor ensures derived class destructors are called too. + +protected: + + CSource *m_pFilter; // The parent of this stream + + // * + // * Data Source + // * + // * The following three functions: FillBuffer, OnThreadCreate/Destroy, are + // * called from within the ThreadProc. They are used in the creation of + // * the media samples this pin will provide + // * + + // Override this to provide the worker thread a means + // of processing a buffer + virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE; + + // Called as the thread is created/destroyed - use to perform + // jobs such as start/stop streaming mode + // If OnThreadCreate returns an error the thread will exit. + virtual HRESULT OnThreadCreate(void) {return NOERROR;}; + virtual HRESULT OnThreadDestroy(void) {return NOERROR;}; + virtual HRESULT OnThreadStartPlay(void) {return NOERROR;}; + + // * + // * Worker Thread + // * + + HRESULT Active(void); // Starts up the worker thread + HRESULT Inactive(void); // Exits the worker thread. + +public: + // thread commands + enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT}; + HRESULT Init(void) { return CallWorker(CMD_INIT); } + HRESULT Exit(void) { return CallWorker(CMD_EXIT); } + HRESULT Run(void) { return CallWorker(CMD_RUN); } + HRESULT Pause(void) { return CallWorker(CMD_PAUSE); } + HRESULT Stop(void) { return CallWorker(CMD_STOP); } + +protected: + Command GetRequest(void) { return (Command) CAMThread::GetRequest(); } + BOOL CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); } + + // override these if you want to add thread commands + virtual DWORD ThreadProc(void); // the thread function + + virtual HRESULT DoBufferProcessingLoop(void); // the loop executed whilst running + + + // * + // * AM_MEDIA_TYPE support + // * + + // If you support more than one media type then override these 2 functions + virtual HRESULT CheckMediaType(const CMediaType *pMediaType); + virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType); // List pos. 0-n + + // If you support only one type then override this fn. + // This will only be called by the default implementations + // of CheckMediaType and GetMediaType(int, CMediaType*) + // You must override this fn. or the above 2! + virtual HRESULT GetMediaType(__inout CMediaType *pMediaType) {return E_UNEXPECTED;} + + STDMETHODIMP QueryId( + __deref_out LPWSTR * Id + ); +}; + +#endif // __CSOURCE__ + diff --git a/RTSP Source Filter/Common/DirectShow/streams.h b/RTSP Source Filter/Common/DirectShow/streams.h new file mode 100644 index 0000000..72c6fd0 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/streams.h @@ -0,0 +1,202 @@ +//------------------------------------------------------------------------------ +// File: Streams.h +// +// Desc: DirectShow base classes - defines overall streams architecture. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __STREAMS__ +#define __STREAMS__ + +#ifdef _MSC_VER +// disable some level-4 warnings, use #pragma warning(enable:###) to re-enable +#pragma warning(disable:4100) // warning C4100: unreferenced formal parameter +#pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union +#pragma warning(disable:4511) // warning C4511: copy constructor could not be generated +#pragma warning(disable:4512) // warning C4512: assignment operator could not be generated +#pragma warning(disable:4514) // warning C4514: "unreferenced inline function has been removed" + +#if _MSC_VER>=1100 +#define AM_NOVTABLE __declspec(novtable) +#else +#define AM_NOVTABLE +#endif +#endif // MSC_VER + + +// Because of differences between Visual C++ and older Microsoft SDKs, +// you may have defined _DEBUG without defining DEBUG. This logic +// ensures that both will be set if Visual C++ sets _DEBUG. +#ifdef _DEBUG +#ifndef DEBUG +#define DEBUG +#endif +#endif + + +#include +#include +#include +#include +#include + + +#ifndef NUMELMS +#if _WIN32_WINNT < 0x0600 + #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0])) +#else + #define NUMELMS(aa) ARRAYSIZE(aa) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////// +// The following definitions come from the Platform SDK and are required if +// the applicaiton is being compiled with the headers from Visual C++ 6.0. +/////////////////////////////////////////////////// //////////////////////// +#ifndef InterlockedExchangePointer + #define InterlockedExchangePointer(Target, Value) \ + (PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value)) +#endif + +#ifndef _WAVEFORMATEXTENSIBLE_ +#define _WAVEFORMATEXTENSIBLE_ +typedef struct { + WAVEFORMATEX Format; + union { + WORD wValidBitsPerSample; /* bits of precision */ + WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */ + WORD wReserved; /* If neither applies, set to zero. */ + } Samples; + DWORD dwChannelMask; /* which channels are */ + /* present in stream */ + GUID SubFormat; +} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE; +#endif // !_WAVEFORMATEXTENSIBLE_ + +#if !defined(WAVE_FORMAT_EXTENSIBLE) +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif // !defined(WAVE_FORMAT_EXTENSIBLE) + +#ifndef GetWindowLongPtr + #define GetWindowLongPtrA GetWindowLongA + #define GetWindowLongPtrW GetWindowLongW + #ifdef UNICODE + #define GetWindowLongPtr GetWindowLongPtrW + #else + #define GetWindowLongPtr GetWindowLongPtrA + #endif // !UNICODE +#endif // !GetWindowLongPtr + +#ifndef SetWindowLongPtr + #define SetWindowLongPtrA SetWindowLongA + #define SetWindowLongPtrW SetWindowLongW + #ifdef UNICODE + #define SetWindowLongPtr SetWindowLongPtrW + #else + #define SetWindowLongPtr SetWindowLongPtrA + #endif // !UNICODE +#endif // !SetWindowLongPtr + +#ifndef GWLP_WNDPROC + #define GWLP_WNDPROC (-4) +#endif +#ifndef GWLP_HINSTANCE + #define GWLP_HINSTANCE (-6) +#endif +#ifndef GWLP_HWNDPARENT + #define GWLP_HWNDPARENT (-8) +#endif +#ifndef GWLP_USERDATA + #define GWLP_USERDATA (-21) +#endif +#ifndef GWLP_ID + #define GWLP_ID (-12) +#endif +#ifndef DWLP_MSGRESULT + #define DWLP_MSGRESULT 0 +#endif +#ifndef DWLP_DLGPROC + #define DWLP_DLGPROC DWLP_MSGRESULT + sizeof(LRESULT) +#endif +#ifndef DWLP_USER + #define DWLP_USER DWLP_DLGPROC + sizeof(DLGPROC) +#endif + + +#pragma warning(push) +#pragma warning(disable: 4312 4244) +// _GetWindowLongPtr +// Templated version of GetWindowLongPtr, to suppress spurious compiler warning. +template +T _GetWindowLongPtr(HWND hwnd, int nIndex) +{ + return (T)GetWindowLongPtr(hwnd, nIndex); +} + +// _SetWindowLongPtr +// Templated version of SetWindowLongPtr, to suppress spurious compiler warning. +template +LONG_PTR _SetWindowLongPtr(HWND hwnd, int nIndex, T p) +{ + return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)p); +} +#pragma warning(pop) + +/////////////////////////////////////////////////////////////////////////// +// End Platform SDK definitions +/////////////////////////////////////////////////////////////////////////// + + +#include // Generated IDL header file for streams interfaces +#include // required by amvideo.h + +#include // Helper class for REFERENCE_TIME management +#include // Debug support for logging and ASSERTs +#include // ActiveMovie video interfaces and definitions +//include amaudio.h explicitly if you need it. it requires the DX SDK. +//#include // ActiveMovie audio interfaces and definitions +#include // General helper classes for threads etc +#include // Base COM classes to support IUnknown +#include // Filter registration support functions +#include // Performance measurement +#include // Light weight com function prototypes + +#include // Simple cache container class +#include // Non MFC generic list class +#include // CMsgThread +#include // Helper class for managing media types +#include // conversions between FOURCCs and GUIDs +#include // generated from control.odl +#include // control interface utility classes +#include // event code definitions +#include // Main streams architecture class hierachy +#include // Generic transform filter +#include // Generic transform-in-place filter +#include // declaration of type GUIDs and well-known clsids +#include // Generic source filter +#include // Output pin queueing +#include // HRESULT status and error definitions +#include // Base class for writing ActiveX renderers +#include // Helps with filters that manage windows +#include // Implements the IVideoWindow interface +#include // Specifically video related classes +#include // Base clock class +#include // System clock +#include // IPersistStream helper class +#include // Video Transform Filter base class +#include +#include // Base property page class +#include // IAMStreamControl support +#include // External device control interface defines +#include // audio filter device error event codes + + + +#else + #ifdef DEBUG + #pragma message("STREAMS.H included TWICE") + #endif +#endif // __STREAMS__ + diff --git a/RTSP Source Filter/Common/DirectShow/strmctl.h b/RTSP Source Filter/Common/DirectShow/strmctl.h new file mode 100644 index 0000000..4077e6c --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/strmctl.h @@ -0,0 +1,157 @@ +//------------------------------------------------------------------------------ +// File: StrmCtl.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __strmctl_h__ +#define __strmctl_h__ + +class CBaseStreamControl : public IAMStreamControl +{ +public: + // Used by the implementation + enum StreamControlState + { STREAM_FLOWING = 0x1000, + STREAM_DISCARDING + }; + +private: + enum StreamControlState m_StreamState; // Current stream state + enum StreamControlState m_StreamStateOnStop; // State after next stop + // (i.e.Blocking or Discarding) + + REFERENCE_TIME m_tStartTime; // MAX_TIME implies none + REFERENCE_TIME m_tStopTime; // MAX_TIME implies none + DWORD m_dwStartCookie; // Cookie for notification to app + DWORD m_dwStopCookie; // Cookie for notification to app + volatile BOOL m_bIsFlushing; // No optimization pls! + volatile BOOL m_bStopSendExtra; // bSendExtra was set + volatile BOOL m_bStopExtraSent; // the extra one was sent + + CCritSec m_CritSec; // CritSec to guard above attributes + + // Event to fire when we can come + // out of blocking, or to come out of waiting + // to discard if we change our minds. + // + CAMEvent m_StreamEvent; + + // All of these methods execute immediately. Helpers for others. + // + void ExecuteStop(); + void ExecuteStart(); + void CancelStop(); + void CancelStart(); + + // Some things we need to be told by our owning filter + // Your pin must also expose IAMStreamControl when QI'd for it! + // + IReferenceClock * m_pRefClock; // Need it to set advises + // Filter must tell us via + // SetSyncSource + IMediaEventSink * m_pSink; // Event sink + // Filter must tell us after it + // creates it in JoinFilterGraph() + FILTER_STATE m_FilterState; // Just need it! + // Filter must tell us via + // NotifyFilterState + REFERENCE_TIME m_tRunStart; // Per the Run call to the filter + + // This guy will return one of the three StreamControlState's. Here's what + // the caller should do for each one: + // + // STREAM_FLOWING: Proceed as usual (render or pass the sample on) + // STREAM_DISCARDING: Calculate the time 'til *pSampleStop and wait + // that long for the event handle + // (GetStreamEventHandle()). If the wait + // expires, throw the sample away. If the event + // fires, call me back - I've changed my mind. + // + enum StreamControlState CheckSampleTimes( __in const REFERENCE_TIME * pSampleStart, + __in const REFERENCE_TIME * pSampleStop ); + +public: + // You don't have to tell us much when we're created, but there are other + // obligations that must be met. See SetSyncSource & NotifyFilterState + // below. + // + CBaseStreamControl(__inout_opt HRESULT *phr = NULL); + ~CBaseStreamControl(); + + // If you want this class to work properly, there are thing you need to + // (keep) telling it. Filters with pins that use this class + // should ensure that they pass through to this method any calls they + // receive on their SetSyncSource. + + // We need a clock to see what time it is. This is for the + // "discard in a timely fashion" logic. If we discard everything as + // quick as possible, a whole 60 minute file could get discarded in the + // first 10 seconds, and if somebody wants to turn streaming on at 30 + // minutes into the file, and they make the call more than a few seconds + // after the graph is run, it may be too late! + // So we hold every sample until it's time has gone, then we discard it. + // The filter should call this when it gets a SetSyncSource + // + void SetSyncSource( IReferenceClock * pRefClock ) + { + CAutoLock lck(&m_CritSec); + if (m_pRefClock) m_pRefClock->Release(); + m_pRefClock = pRefClock; + if (m_pRefClock) m_pRefClock->AddRef(); + } + + // Set event sink for notifications + // The filter should call this in its JoinFilterGraph after it creates the + // IMediaEventSink + // + void SetFilterGraph( IMediaEventSink *pSink ) { + m_pSink = pSink; + } + + // Since we schedule in stream time, we need the tStart and must track the + // state of our owning filter. + // The app should call this ever state change + // + void NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart = 0 ); + + // Filter should call Flushing(TRUE) in BeginFlush, + // and Flushing(FALSE) in EndFlush. + // + void Flushing( BOOL bInProgress ); + + + // The two main methods of IAMStreamControl + + // Class adds default values suitable for immediate + // muting and unmuting of the stream. + + STDMETHODIMP StopAt( const REFERENCE_TIME * ptStop = NULL, + BOOL bSendExtra = FALSE, + DWORD dwCookie = 0 ); + STDMETHODIMP StartAt( const REFERENCE_TIME * ptStart = NULL, + DWORD dwCookie = 0 ); + STDMETHODIMP GetInfo( __out AM_STREAM_INFO *pInfo); + + // Helper function for pin's receive method. Call this with + // the sample and we'll tell you what to do with it. We'll do a + // WaitForSingleObject within this call if one is required. This is + // a "What should I do with this sample?" kind of call. We'll tell the + // caller to either flow it or discard it. + // If pSample is NULL we evaluate based on the current state + // settings + enum StreamControlState CheckStreamState( IMediaSample * pSample ); + +private: + // These don't require locking, but we are relying on the fact that + // m_StreamState can be retrieved with integrity, and is a snap shot that + // may have just been, or may be just about to be, changed. + HANDLE GetStreamEventHandle() const { return m_StreamEvent; } + enum StreamControlState GetStreamState() const { return m_StreamState; } + BOOL IsStreaming() const { return m_StreamState == STREAM_FLOWING; } +}; + +#endif diff --git a/RTSP Source Filter/Common/DirectShow/sysclock.h b/RTSP Source Filter/Common/DirectShow/sysclock.h new file mode 100644 index 0000000..bf9192c --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/sysclock.h @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// File: SysClock.h +// +// Desc: DirectShow base classes - defines a system clock implementation of +// IReferenceClock. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __SYSTEMCLOCK__ +#define __SYSTEMCLOCK__ + +// +// Base clock. Uses timeGetTime ONLY +// Uses most of the code in the base reference clock. +// Provides GetTime +// + +class CSystemClock : public CBaseReferenceClock, public IAMClockAdjust, public IPersist +{ +public: + // We must be able to create an instance of ourselves + static CUnknown * WINAPI CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr); + CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr); + + DECLARE_IUNKNOWN + + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); + + // Yield up our class id so that we can be persisted + // Implement required Ipersist method + STDMETHODIMP GetClassID(__out CLSID *pClsID); + + // IAMClockAdjust methods + STDMETHODIMP SetClockDelta(REFERENCE_TIME rtDelta); +}; //CSystemClock + +#endif /* __SYSTEMCLOCK__ */ diff --git a/RTSP Source Filter/Common/DirectShow/transfrm.h b/RTSP Source Filter/Common/DirectShow/transfrm.h new file mode 100644 index 0000000..36c2e0d --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/transfrm.h @@ -0,0 +1,304 @@ +//------------------------------------------------------------------------------ +// File: Transfrm.h +// +// Desc: DirectShow base classes - defines classes from which simple +// transform codecs may be derived. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// It assumes the codec has one input and one output stream, and has no +// interest in memory management, interface negotiation or anything else. +// +// derive your class from this, and supply Transform and the media type/format +// negotiation functions. Implement that class, compile and link and +// you're done. + + +#ifndef __TRANSFRM__ +#define __TRANSFRM__ + +// ====================================================================== +// This is the com object that represents a simple transform filter. It +// supports IBaseFilter, IMediaFilter and two pins through nested interfaces +// ====================================================================== + +class CTransformFilter; + +// ================================================== +// Implements the input pin +// ================================================== + +class CTransformInputPin : public CBaseInputPin +{ + friend class CTransformFilter; + +protected: + CTransformFilter *m_pTransformFilter; + + +public: + + CTransformInputPin( + __in_opt LPCTSTR pObjectName, + __inout CTransformFilter *pTransformFilter, + __inout HRESULT * phr, + __in_opt LPCWSTR pName); +#ifdef UNICODE + CTransformInputPin( + __in_opt LPCSTR pObjectName, + __inout CTransformFilter *pTransformFilter, + __inout HRESULT * phr, + __in_opt LPCWSTR pName); +#endif + + STDMETHODIMP QueryId(__deref_out LPWSTR * Id) + { + return AMGetWideString(L"In", Id); + } + + // Grab and release extra interfaces if required + + HRESULT CheckConnect(IPin *pPin); + HRESULT BreakConnect(); + HRESULT CompleteConnect(IPin *pReceivePin); + + // check that we can support this output type + HRESULT CheckMediaType(const CMediaType* mtIn); + + // set the connection media type + HRESULT SetMediaType(const CMediaType* mt); + + // --- IMemInputPin ----- + + // here's the next block of data from the stream. + // AddRef it yourself if you need to hold it beyond the end + // of this call. + STDMETHODIMP Receive(IMediaSample * pSample); + + // provide EndOfStream that passes straight downstream + // (there is no queued data) + STDMETHODIMP EndOfStream(void); + + // passes it to CTransformFilter::BeginFlush + STDMETHODIMP BeginFlush(void); + + // passes it to CTransformFilter::EndFlush + STDMETHODIMP EndFlush(void); + + STDMETHODIMP NewSegment( + REFERENCE_TIME tStart, + REFERENCE_TIME tStop, + double dRate); + + // Check if it's OK to process samples + virtual HRESULT CheckStreaming(); + + // Media type +public: + CMediaType& CurrentMediaType() { return m_mt; }; + +}; + +// ================================================== +// Implements the output pin +// ================================================== + +class CTransformOutputPin : public CBaseOutputPin +{ + friend class CTransformFilter; + +protected: + CTransformFilter *m_pTransformFilter; + +public: + + // implement IMediaPosition by passing upstream + IUnknown * m_pPosition; + + CTransformOutputPin( + __in_opt LPCTSTR pObjectName, + __inout CTransformFilter *pTransformFilter, + __inout HRESULT * phr, + __in_opt LPCWSTR pName); +#ifdef UNICODE + CTransformOutputPin( + __in_opt LPCSTR pObjectName, + __inout CTransformFilter *pTransformFilter, + __inout HRESULT * phr, + __in_opt LPCWSTR pName); +#endif + ~CTransformOutputPin(); + + // override to expose IMediaPosition + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); + + // --- CBaseOutputPin ------------ + + STDMETHODIMP QueryId(__deref_out LPWSTR * Id) + { + return AMGetWideString(L"Out", Id); + } + + // Grab and release extra interfaces if required + + HRESULT CheckConnect(IPin *pPin); + HRESULT BreakConnect(); + HRESULT CompleteConnect(IPin *pReceivePin); + + // check that we can support this output type + HRESULT CheckMediaType(const CMediaType* mtOut); + + // set the connection media type + HRESULT SetMediaType(const CMediaType *pmt); + + // called from CBaseOutputPin during connection to ask for + // the count and size of buffers we need. + HRESULT DecideBufferSize( + IMemAllocator * pAlloc, + __inout ALLOCATOR_PROPERTIES *pProp); + + // returns the preferred formats for a pin + HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType); + + // inherited from IQualityControl via CBasePin + STDMETHODIMP Notify(IBaseFilter * pSender, Quality q); + + // Media type +public: + CMediaType& CurrentMediaType() { return m_mt; }; +}; + + +class AM_NOVTABLE CTransformFilter : public CBaseFilter +{ + +public: + + // map getpin/getpincount for base enum of pins to owner + // override this to return more specialised pin objects + + virtual int GetPinCount(); + virtual CBasePin * GetPin(int n); + STDMETHODIMP FindPin(LPCWSTR Id, __deref_out IPin **ppPin); + + // override state changes to allow derived transform filter + // to control streaming start/stop + STDMETHODIMP Stop(); + STDMETHODIMP Pause(); + +public: + + CTransformFilter(__in_opt LPCTSTR , __inout_opt LPUNKNOWN, REFCLSID clsid); +#ifdef UNICODE + CTransformFilter(__in_opt LPCSTR , __inout_opt LPUNKNOWN, REFCLSID clsid); +#endif + ~CTransformFilter(); + + // ================================================================= + // ----- override these bits --------------------------------------- + // ================================================================= + + // These must be supplied in a derived class + + virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut); + + // check if you can support mtIn + virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE; + + // check if you can support the transform from this input to this output + virtual HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) PURE; + + // this goes in the factory template table to create new instances + // static CCOMObject * CreateInstance(__inout_opt LPUNKNOWN, HRESULT *); + + // call the SetProperties function with appropriate arguments + virtual HRESULT DecideBufferSize( + IMemAllocator * pAllocator, + __inout ALLOCATOR_PROPERTIES *pprop) PURE; + + // override to suggest OUTPUT pin media types + virtual HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType) PURE; + + + + // ================================================================= + // ----- Optional Override Methods ----------------------- + // ================================================================= + + // you can also override these if you want to know about streaming + virtual HRESULT StartStreaming(); + virtual HRESULT StopStreaming(); + + // override if you can do anything constructive with quality notifications + virtual HRESULT AlterQuality(Quality q); + + // override this to know when the media type is actually set + virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt); + + // chance to grab extra interfaces on connection + virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin); + virtual HRESULT BreakConnect(PIN_DIRECTION dir); + virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin); + + // chance to customize the transform process + virtual HRESULT Receive(IMediaSample *pSample); + + // Standard setup for output sample + HRESULT InitializeOutputSample(IMediaSample *pSample, __deref_out IMediaSample **ppOutSample); + + // if you override Receive, you may need to override these three too + virtual HRESULT EndOfStream(void); + virtual HRESULT BeginFlush(void); + virtual HRESULT EndFlush(void); + virtual HRESULT NewSegment( + REFERENCE_TIME tStart, + REFERENCE_TIME tStop, + double dRate); + +#ifdef PERF + // Override to register performance measurement with a less generic string + // You should do this to avoid confusion with other filters + virtual void RegisterPerfId() + {m_idTransform = MSR_REGISTER(TEXT("Transform"));} +#endif // PERF + + +// implementation details + +protected: + +#ifdef PERF + int m_idTransform; // performance measuring id +#endif + BOOL m_bEOSDelivered; // have we sent EndOfStream + BOOL m_bSampleSkipped; // Did we just skip a frame + BOOL m_bQualityChanged; // Have we degraded? + + // critical section protecting filter state. + + CCritSec m_csFilter; + + // critical section stopping state changes (ie Stop) while we're + // processing a sample. + // + // This critical section is held when processing + // events that occur on the receive thread - Receive() and EndOfStream(). + // + // If you want to hold both m_csReceive and m_csFilter then grab + // m_csFilter FIRST - like CTransformFilter::Stop() does. + + CCritSec m_csReceive; + + // these hold our input and output pins + + friend class CTransformInputPin; + friend class CTransformOutputPin; + CTransformInputPin *m_pInput; + CTransformOutputPin *m_pOutput; +}; + +#endif /* __TRANSFRM__ */ + + diff --git a/RTSP Source Filter/Common/DirectShow/transip.h b/RTSP Source Filter/Common/DirectShow/transip.h new file mode 100644 index 0000000..45fb4e9 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/transip.h @@ -0,0 +1,250 @@ +//------------------------------------------------------------------------------ +// File: TransIP.h +// +// Desc: DirectShow base classes - defines classes from which simple +// Transform-In-Place filters may be derived. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// +// The difference between this and Transfrm.h is that Transfrm copies the data. +// +// It assumes the filter has one input and one output stream, and has no +// interest in memory management, interface negotiation or anything else. +// +// Derive your class from this, and supply Transform and the media type/format +// negotiation functions. Implement that class, compile and link and +// you're done. + + +#ifndef __TRANSIP__ +#define __TRANSIP__ + +// ====================================================================== +// This is the com object that represents a simple transform filter. It +// supports IBaseFilter, IMediaFilter and two pins through nested interfaces +// ====================================================================== + +class CTransInPlaceFilter; + +// Several of the pin functions call filter functions to do the work, +// so you can often use the pin classes unaltered, just overriding the +// functions in CTransInPlaceFilter. If that's not enough and you want +// to derive your own pin class, override GetPin in the filter to supply +// your own pin classes to the filter. + +// ================================================== +// Implements the input pin +// ================================================== + +class CTransInPlaceInputPin : public CTransformInputPin +{ + +protected: + CTransInPlaceFilter * const m_pTIPFilter; // our filter + BOOL m_bReadOnly; // incoming stream is read only + +public: + + CTransInPlaceInputPin( + __in_opt LPCTSTR pObjectName, + __inout CTransInPlaceFilter *pFilter, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); + + // --- IMemInputPin ----- + + // Provide an enumerator for media types by getting one from downstream + STDMETHODIMP EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum ); + + // Say whether media type is acceptable. + HRESULT CheckMediaType(const CMediaType* pmt); + + // Return our upstream allocator + STDMETHODIMP GetAllocator(__deref_out IMemAllocator ** ppAllocator); + + // get told which allocator the upstream output pin is actually + // going to use. + STDMETHODIMP NotifyAllocator(IMemAllocator * pAllocator, + BOOL bReadOnly); + + // Allow the filter to see what allocator we have + // N.B. This does NOT AddRef + __out IMemAllocator * PeekAllocator() const + { return m_pAllocator; } + + // Pass this on downstream if it ever gets called. + STDMETHODIMP GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES *pProps); + + HRESULT CompleteConnect(IPin *pReceivePin); + + inline const BOOL ReadOnly() { return m_bReadOnly ; } + +}; // CTransInPlaceInputPin + +// ================================================== +// Implements the output pin +// ================================================== + +class CTransInPlaceOutputPin : public CTransformOutputPin +{ + +protected: + // m_pFilter points to our CBaseFilter + CTransInPlaceFilter * const m_pTIPFilter; + +public: + + CTransInPlaceOutputPin( + __in_opt LPCTSTR pObjectName, + __inout CTransInPlaceFilter *pFilter, + __inout HRESULT *phr, + __in_opt LPCWSTR pName); + + + // --- CBaseOutputPin ------------ + + // negotiate the allocator and its buffer size/count + // Insists on using our own allocator. (Actually the one upstream of us). + // We don't override this - instead we just agree the default + // then let the upstream filter decide for itself on reconnect + // virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc); + + // Provide a media type enumerator. Get it from upstream. + STDMETHODIMP EnumMediaTypes( __deref_out IEnumMediaTypes **ppEnum ); + + // Say whether media type is acceptable. + HRESULT CheckMediaType(const CMediaType* pmt); + + // This just saves the allocator being used on the output pin + // Also called by input pin's GetAllocator() + void SetAllocator(IMemAllocator * pAllocator); + + __out_opt IMemInputPin * ConnectedIMemInputPin() + { return m_pInputPin; } + + // Allow the filter to see what allocator we have + // N.B. This does NOT AddRef + __out IMemAllocator * PeekAllocator() const + { return m_pAllocator; } + + HRESULT CompleteConnect(IPin *pReceivePin); + +}; // CTransInPlaceOutputPin + + +class AM_NOVTABLE CTransInPlaceFilter : public CTransformFilter +{ + +public: + + // map getpin/getpincount for base enum of pins to owner + // override this to return more specialised pin objects + + virtual CBasePin *GetPin(int n); + +public: + + // Set bModifiesData == false if your derived filter does + // not modify the data samples (for instance it's just copying + // them somewhere else or looking at the timestamps). + + CTransInPlaceFilter(__in_opt LPCTSTR, __inout_opt LPUNKNOWN, REFCLSID clsid, __inout HRESULT *, + bool bModifiesData = true); +#ifdef UNICODE + CTransInPlaceFilter(__in_opt LPCSTR, __inout_opt LPUNKNOWN, REFCLSID clsid, __inout HRESULT *, + bool bModifiesData = true); +#endif + // The following are defined to avoid undefined pure virtuals. + // Even if they are never called, they will give linkage warnings/errors + + // We override EnumMediaTypes to bypass the transform class enumerator + // which would otherwise call this. + HRESULT GetMediaType(int iPosition, __inout CMediaType *pMediaType) + { DbgBreak("CTransInPlaceFilter::GetMediaType should never be called"); + return E_UNEXPECTED; + } + + // This is called when we actually have to provide our own allocator. + HRESULT DecideBufferSize(IMemAllocator*, __inout ALLOCATOR_PROPERTIES *); + + // The functions which call this in CTransform are overridden in this + // class to call CheckInputType with the assumption that the type + // does not change. In Debug builds some calls will be made and + // we just ensure that they do not assert. + HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut) + { + return S_OK; + }; + + + // ================================================================= + // ----- You may want to override this ----------------------------- + // ================================================================= + + HRESULT CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin); + + // chance to customize the transform process + virtual HRESULT Receive(IMediaSample *pSample); + + // ================================================================= + // ----- You MUST override these ----------------------------------- + // ================================================================= + + virtual HRESULT Transform(IMediaSample *pSample) PURE; + + // this goes in the factory template table to create new instances + // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *); + + +#ifdef PERF + // Override to register performance measurement with a less generic string + // You should do this to avoid confusion with other filters + virtual void RegisterPerfId() + {m_idTransInPlace = MSR_REGISTER(TEXT("TransInPlace"));} +#endif // PERF + + +// implementation details + +protected: + + __out_opt IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource); + +#ifdef PERF + int m_idTransInPlace; // performance measuring id +#endif // PERF + bool m_bModifiesData; // Does this filter change the data? + + // these hold our input and output pins + + friend class CTransInPlaceInputPin; + friend class CTransInPlaceOutputPin; + + __out CTransInPlaceInputPin *InputPin() const + { + return (CTransInPlaceInputPin *)m_pInput; + }; + __out CTransInPlaceOutputPin *OutputPin() const + { + return (CTransInPlaceOutputPin *)m_pOutput; + }; + + // Helper to see if the input and output types match + BOOL TypesMatch() + { + return InputPin()->CurrentMediaType() == + OutputPin()->CurrentMediaType(); + } + + // Are the input and output allocators different? + BOOL UsingDifferentAllocators() const + { + return InputPin()->PeekAllocator() != OutputPin()->PeekAllocator(); + } +}; // CTransInPlaceFilter + +#endif /* __TRANSIP__ */ + diff --git a/RTSP Source Filter/Common/DirectShow/videoctl.h b/RTSP Source Filter/Common/DirectShow/videoctl.h new file mode 100644 index 0000000..440d81a --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/videoctl.h @@ -0,0 +1,168 @@ +//------------------------------------------------------------------------------ +// File: VideoCtl.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __VIDEOCTL__ +#define __VIDEOCTL__ + +// These help with property page implementations. The first can be used to +// load any string from a resource file. The buffer to load into is passed +// as an input parameter. The same buffer is the return value if the string +// was found otherwise it returns TEXT(""). The GetDialogSize is passed the +// resource ID of a dialog box and returns the size of it in screen pixels + +#define STR_MAX_LENGTH 256 +LPTSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPTSTR pBuffer, int iResourceID); + +#ifdef UNICODE +#define WideStringFromResource StringFromResource +LPSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPSTR pBuffer, int iResourceID); +#else +LPWSTR WINAPI WideStringFromResource(__out_ecount(STR_MAX_LENGTH) LPWSTR pBuffer, int iResourceID); +#endif + + +BOOL WINAPI GetDialogSize(int iResourceID, // Dialog box resource identifier + DLGPROC pDlgProc, // Pointer to dialog procedure + LPARAM lParam, // Any user data wanted in pDlgProc + __out SIZE *pResult);// Returns the size of dialog box + +// Class that aggregates an IDirectDraw interface + +class CAggDirectDraw : public IDirectDraw, public CUnknown +{ +protected: + + LPDIRECTDRAW m_pDirectDraw; + +public: + + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv); + + // Constructor and destructor + + CAggDirectDraw(__in_opt LPCTSTR pName,__inout_opt LPUNKNOWN pUnk) : + CUnknown(pName,pUnk), + m_pDirectDraw(NULL) { }; + + virtual CAggDirectDraw::~CAggDirectDraw() { }; + + // Set the object we should be aggregating + void SetDirectDraw(__inout LPDIRECTDRAW pDirectDraw) { + m_pDirectDraw = pDirectDraw; + } + + // IDirectDraw methods + + STDMETHODIMP Compact(); + STDMETHODIMP CreateClipper(DWORD dwFlags,__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper,__inout_opt IUnknown *pUnkOuter); + STDMETHODIMP CreatePalette(DWORD dwFlags,__in LPPALETTEENTRY lpColorTable,__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette,__inout_opt IUnknown *pUnkOuter); + STDMETHODIMP CreateSurface(__in LPDDSURFACEDESC lpDDSurfaceDesc,__deref_out LPDIRECTDRAWSURFACE *lplpDDSurface,__inout_opt IUnknown *pUnkOuter); + STDMETHODIMP DuplicateSurface(__in LPDIRECTDRAWSURFACE lpDDSurface,__deref_out LPDIRECTDRAWSURFACE *lplpDupDDSurface); + STDMETHODIMP EnumDisplayModes(DWORD dwSurfaceDescCount,__in LPDDSURFACEDESC lplpDDSurfaceDescList,__in LPVOID lpContext,__in LPDDENUMMODESCALLBACK lpEnumCallback); + STDMETHODIMP EnumSurfaces(DWORD dwFlags,__in LPDDSURFACEDESC lpDDSD,__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpEnumCallback); + STDMETHODIMP FlipToGDISurface(); + STDMETHODIMP GetCaps(__out LPDDCAPS lpDDDriverCaps,__out LPDDCAPS lpDDHELCaps); + STDMETHODIMP GetDisplayMode(__out LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP GetFourCCCodes(__inout LPDWORD lpNumCodes,__out_ecount(*lpNumCodes) LPDWORD lpCodes); + STDMETHODIMP GetGDISurface(__deref_out LPDIRECTDRAWSURFACE *lplpGDIDDSurface); + STDMETHODIMP GetMonitorFrequency(__out LPDWORD lpdwFrequency); + STDMETHODIMP GetScanLine(__out LPDWORD lpdwScanLine); + STDMETHODIMP GetVerticalBlankStatus(__out LPBOOL lpblsInVB); + STDMETHODIMP Initialize(__in GUID *lpGUID); + STDMETHODIMP RestoreDisplayMode(); + STDMETHODIMP SetCooperativeLevel(HWND hWnd,DWORD dwFlags); + STDMETHODIMP SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp); + STDMETHODIMP WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent); +}; + + +// Class that aggregates an IDirectDrawSurface interface + +class CAggDrawSurface : public IDirectDrawSurface, public CUnknown +{ +protected: + + LPDIRECTDRAWSURFACE m_pDirectDrawSurface; + +public: + + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv); + + // Constructor and destructor + + CAggDrawSurface(__in_opt LPCTSTR pName,__inout_opt LPUNKNOWN pUnk) : + CUnknown(pName,pUnk), + m_pDirectDrawSurface(NULL) { }; + + virtual ~CAggDrawSurface() { }; + + // Set the object we should be aggregating + void SetDirectDrawSurface(__inout LPDIRECTDRAWSURFACE pDirectDrawSurface) { + m_pDirectDrawSurface = pDirectDrawSurface; + } + + // IDirectDrawSurface methods + + STDMETHODIMP AddAttachedSurface(__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface); + STDMETHODIMP AddOverlayDirtyRect(__in LPRECT lpRect); + STDMETHODIMP Blt(__in LPRECT lpDestRect,__in LPDIRECTDRAWSURFACE lpDDSrcSurface,__in LPRECT lpSrcRect,DWORD dwFlags,__in LPDDBLTFX lpDDBltFx); + STDMETHODIMP BltBatch(__in_ecount(dwCount) LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags); + STDMETHODIMP BltFast(DWORD dwX,DWORD dwY,__in LPDIRECTDRAWSURFACE lpDDSrcSurface,__in LPRECT lpSrcRect,DWORD dwTrans); + STDMETHODIMP DeleteAttachedSurface(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface); + STDMETHODIMP EnumAttachedSurfaces(__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback); + STDMETHODIMP EnumOverlayZOrders(DWORD dwFlags,__in LPVOID lpContext,__in LPDDENUMSURFACESCALLBACK lpfnCallback); + STDMETHODIMP Flip(__in LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags); + STDMETHODIMP GetAttachedSurface(__in LPDDSCAPS lpDDSCaps,__deref_out LPDIRECTDRAWSURFACE *lplpDDAttachedSurface); + STDMETHODIMP GetBltStatus(DWORD dwFlags); + STDMETHODIMP GetCaps(__out LPDDSCAPS lpDDSCaps); + STDMETHODIMP GetClipper(__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper); + STDMETHODIMP GetColorKey(DWORD dwFlags,__out LPDDCOLORKEY lpDDColorKey); + STDMETHODIMP GetDC(__out HDC *lphDC); + STDMETHODIMP GetFlipStatus(DWORD dwFlags); + STDMETHODIMP GetOverlayPosition(__out LPLONG lpdwX,__out LPLONG lpdwY); + STDMETHODIMP GetPalette(__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette); + STDMETHODIMP GetPixelFormat(__out LPDDPIXELFORMAT lpDDPixelFormat); + STDMETHODIMP GetSurfaceDesc(__out LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP Initialize(__in LPDIRECTDRAW lpDD,__in LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP IsLost(); + STDMETHODIMP Lock(__in LPRECT lpDestRect,__inout LPDDSURFACEDESC lpDDSurfaceDesc,DWORD dwFlags,HANDLE hEvent); + STDMETHODIMP ReleaseDC(HDC hDC); + STDMETHODIMP Restore(); + STDMETHODIMP SetClipper(__in LPDIRECTDRAWCLIPPER lpDDClipper); + STDMETHODIMP SetColorKey(DWORD dwFlags,__in LPDDCOLORKEY lpDDColorKey); + STDMETHODIMP SetOverlayPosition(LONG dwX,LONG dwY); + STDMETHODIMP SetPalette(__in LPDIRECTDRAWPALETTE lpDDPalette); + STDMETHODIMP Unlock(__in LPVOID lpSurfaceData); + STDMETHODIMP UpdateOverlay(__in LPRECT lpSrcRect,__in LPDIRECTDRAWSURFACE lpDDDestSurface,__in LPRECT lpDestRect,DWORD dwFlags,__in LPDDOVERLAYFX lpDDOverlayFX); + STDMETHODIMP UpdateOverlayDisplay(DWORD dwFlags); + STDMETHODIMP UpdateOverlayZOrder(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSReference); +}; + + +class CLoadDirectDraw +{ + LPDIRECTDRAW m_pDirectDraw; // The DirectDraw driver instance + HINSTANCE m_hDirectDraw; // Handle to the loaded library + +public: + + CLoadDirectDraw(); + ~CLoadDirectDraw(); + + HRESULT LoadDirectDraw(__in LPSTR szDevice); + void ReleaseDirectDraw(); + HRESULT IsDirectDrawLoaded(); + LPDIRECTDRAW GetDirectDraw(); + BOOL IsDirectDrawVersion1(); +}; + +#endif // __VIDEOCTL__ + diff --git a/RTSP Source Filter/Common/DirectShow/vssver2.scc b/RTSP Source Filter/Common/DirectShow/vssver2.scc new file mode 100644 index 0000000..8fa138e Binary files /dev/null and b/RTSP Source Filter/Common/DirectShow/vssver2.scc differ diff --git a/RTSP Source Filter/Common/DirectShow/vtrans.h b/RTSP Source Filter/Common/DirectShow/vtrans.h new file mode 100644 index 0000000..7122392 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/vtrans.h @@ -0,0 +1,143 @@ +//------------------------------------------------------------------------------ +// File: VTrans.h +// +// Desc: DirectShow base classes - defines a video transform class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// This class is derived from CTransformFilter, but is specialised to handle +// the requirements of video quality control by frame dropping. +// This is a non-in-place transform, (i.e. it copies the data) such as a decoder. + +class CVideoTransformFilter : public CTransformFilter +{ + public: + + CVideoTransformFilter(__in_opt LPCTSTR, __inout_opt LPUNKNOWN, REFCLSID clsid); + ~CVideoTransformFilter(); + HRESULT EndFlush(); + + // ================================================================= + // ----- override these bits --------------------------------------- + // ================================================================= + // The following methods are in CTransformFilter which is inherited. + // They are mentioned here for completeness + // + // These MUST be supplied in a derived class + // + // NOTE: + // virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut); + // virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE; + // virtual HRESULT CheckTransform + // (const CMediaType* mtIn, const CMediaType* mtOut) PURE; + // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *); + // virtual HRESULT DecideBufferSize + // (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE; + // virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE; + // + // These MAY also be overridden + // + // virtual HRESULT StopStreaming(); + // virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt); + // virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin); + // virtual HRESULT BreakConnect(PIN_DIRECTION dir); + // virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin); + // virtual HRESULT EndOfStream(void); + // virtual HRESULT BeginFlush(void); + // virtual HRESULT EndFlush(void); + // virtual HRESULT NewSegment + // (REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate); +#ifdef PERF + + // If you override this - ensure that you register all these ids + // as well as any of your own, + virtual void RegisterPerfId() { + m_idSkip = MSR_REGISTER(TEXT("Video Transform Skip frame")); + m_idFrameType = MSR_REGISTER(TEXT("Video transform frame type")); + m_idLate = MSR_REGISTER(TEXT("Video Transform Lateness")); + m_idTimeTillKey = MSR_REGISTER(TEXT("Video Transform Estd. time to next key")); + CTransformFilter::RegisterPerfId(); + } +#endif + + protected: + + // =========== QUALITY MANAGEMENT IMPLEMENTATION ======================== + // Frames are assumed to come in three types: + // Type 1: an AVI key frame or an MPEG I frame. + // This frame can be decoded with no history. + // Dropping this frame means that no further frame can be decoded + // until the next type 1 frame. + // Type 1 frames are sync points. + // Type 2: an AVI non-key frame or an MPEG P frame. + // This frame cannot be decoded unless the previous type 1 frame was + // decoded and all type 2 frames since have been decoded. + // Dropping this frame means that no further frame can be decoded + // until the next type 1 frame. + // Type 3: An MPEG B frame. + // This frame cannot be decoded unless the previous type 1 or 2 frame + // has been decoded AND the subsequent type 1 or 2 frame has also + // been decoded. (This requires decoding the frames out of sequence). + // Dropping this frame affects no other frames. This implementation + // does not allow for these. All non-sync-point frames are treated + // as being type 2. + // + // The spacing of frames of type 1 in a file is not guaranteed. There MUST + // be a type 1 frame at (well, near) the start of the file in order to start + // decoding at all. After that there could be one every half second or so, + // there could be one at the start of each scene (aka "cut", "shot") or + // there could be no more at all. + // If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED + // without losing all the rest of the movie. There is no way to tell whether + // this is the case, so we find that we are in the gambling business. + // To try to improve the odds, we record the greatest interval between type 1s + // that we have seen and we bet on things being no worse than this in the + // future. + + // You can tell if it's a type 1 frame by calling IsSyncPoint(). + // there is no architected way to test for a type 3, so you should override + // the quality management here if you have B-frames. + + int m_nKeyFramePeriod; // the largest observed interval between type 1 frames + // 1 means every frame is type 1, 2 means every other. + + int m_nFramesSinceKeyFrame; // Used to count frames since the last type 1. + // becomes the new m_nKeyFramePeriod if greater. + + BOOL m_bSkipping; // we are skipping to the next type 1 frame + +#ifdef PERF + int m_idFrameType; // MSR id Frame type. 1=Key, 2="non-key" + int m_idSkip; // MSR id skipping + int m_idLate; // MSR id lateness + int m_idTimeTillKey; // MSR id for guessed time till next key frame. +#endif + + virtual HRESULT StartStreaming(); + + HRESULT AbortPlayback(HRESULT hr); // if something bad happens + + HRESULT Receive(IMediaSample *pSample); + + HRESULT AlterQuality(Quality q); + + BOOL ShouldSkipFrame(IMediaSample * pIn); + + int m_itrLate; // lateness from last Quality message + // (this overflows at 214 secs late). + int m_tDecodeStart; // timeGetTime when decode started. + int m_itrAvgDecode; // Average decode time in reference units. + + BOOL m_bNoSkip; // debug - no skipping. + + // We send an EC_QUALITY_CHANGE notification to the app if we have to degrade. + // We send one when we start degrading, not one for every frame, this means + // we track whether we've sent one yet. + BOOL m_bQualityChanged; + + // When non-zero, don't pass anything to renderer until next keyframe + // If there are few keys, give up and eventually draw something + int m_nWaitForKey; +}; diff --git a/RTSP Source Filter/Common/DirectShow/winctrl.h b/RTSP Source Filter/Common/DirectShow/winctrl.h new file mode 100644 index 0000000..f18ba82 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/winctrl.h @@ -0,0 +1,224 @@ +//------------------------------------------------------------------------------ +// File: WinCtrl.h +// +// Desc: DirectShow base classes - defines classes for video control +// interfaces. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WINCTRL__ +#define __WINCTRL__ + +#define ABSOL(x) (x < 0 ? -x : x) +#define NEGAT(x) (x > 0 ? -x : x) + +// Helper +BOOL WINAPI PossiblyEatMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +class CBaseControlWindow : public CBaseVideoWindow, public CBaseWindow +{ +protected: + + CBaseFilter *m_pFilter; // Pointer to owning media filter + CBasePin *m_pPin; // Controls media types for connection + CCritSec *m_pInterfaceLock; // Externally defined critical section + COLORREF m_BorderColour; // Current window border colour + BOOL m_bAutoShow; // What happens when the state changes + HWND m_hwndOwner; // Owner window that we optionally have + HWND m_hwndDrain; // HWND to post any messages received + BOOL m_bCursorHidden; // Should we hide the window cursor + +public: + + // Internal methods for other objects to get information out + + HRESULT DoSetWindowStyle(long Style,long WindowLong); + HRESULT DoGetWindowStyle(__out long *pStyle,long WindowLong); + BOOL IsAutoShowEnabled() { return m_bAutoShow; }; + COLORREF GetBorderColour() { return m_BorderColour; }; + HWND GetOwnerWindow() { return m_hwndOwner; }; + BOOL IsCursorHidden() { return m_bCursorHidden; }; + + inline BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return ::PossiblyEatMessage(m_hwndDrain, uMsg, wParam, lParam); + } + + // Derived classes must call this to set the pin the filter is using + // We don't have the pin passed in to the constructor (as we do with + // the CBaseFilter object) because filters typically create the + // pins dynamically when requested in CBaseFilter::GetPin. This can + // not be called from our constructor because is is a virtual method + + void SetControlWindowPin(CBasePin *pPin) { + m_pPin = pPin; + } + +public: + + CBaseControlWindow(__inout CBaseFilter *pFilter, // Owning media filter + __in CCritSec *pInterfaceLock, // Locking object + __in_opt LPCTSTR pName, // Object description + __inout_opt LPUNKNOWN pUnk, // Normal COM ownership + __inout HRESULT *phr); // OLE return code + + // These are the properties we support + + STDMETHODIMP put_Caption(__in BSTR strCaption); + STDMETHODIMP get_Caption(__out BSTR *pstrCaption); + STDMETHODIMP put_AutoShow(long AutoShow); + STDMETHODIMP get_AutoShow(__out long *AutoShow); + STDMETHODIMP put_WindowStyle(long WindowStyle); + STDMETHODIMP get_WindowStyle(__out long *pWindowStyle); + STDMETHODIMP put_WindowStyleEx(long WindowStyleEx); + STDMETHODIMP get_WindowStyleEx(__out long *pWindowStyleEx); + STDMETHODIMP put_WindowState(long WindowState); + STDMETHODIMP get_WindowState(__out long *pWindowState); + STDMETHODIMP put_BackgroundPalette(long BackgroundPalette); + STDMETHODIMP get_BackgroundPalette(__out long *pBackgroundPalette); + STDMETHODIMP put_Visible(long Visible); + STDMETHODIMP get_Visible(__out long *pVisible); + STDMETHODIMP put_Left(long Left); + STDMETHODIMP get_Left(__out long *pLeft); + STDMETHODIMP put_Width(long Width); + STDMETHODIMP get_Width(__out long *pWidth); + STDMETHODIMP put_Top(long Top); + STDMETHODIMP get_Top(__out long *pTop); + STDMETHODIMP put_Height(long Height); + STDMETHODIMP get_Height(__out long *pHeight); + STDMETHODIMP put_Owner(OAHWND Owner); + STDMETHODIMP get_Owner(__out OAHWND *Owner); + STDMETHODIMP put_MessageDrain(OAHWND Drain); + STDMETHODIMP get_MessageDrain(__out OAHWND *Drain); + STDMETHODIMP get_BorderColor(__out long *Color); + STDMETHODIMP put_BorderColor(long Color); + STDMETHODIMP get_FullScreenMode(__out long *FullScreenMode); + STDMETHODIMP put_FullScreenMode(long FullScreenMode); + + // And these are the methods + + STDMETHODIMP SetWindowForeground(long Focus); + STDMETHODIMP NotifyOwnerMessage(OAHWND hwnd,long uMsg,LONG_PTR wParam,LONG_PTR lParam); + STDMETHODIMP GetMinIdealImageSize(__out long *pWidth,__out long *pHeight); + STDMETHODIMP GetMaxIdealImageSize(__out long *pWidth,__out long *pHeight); + STDMETHODIMP SetWindowPosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetWindowPosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight); + STDMETHODIMP GetRestorePosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight); + STDMETHODIMP HideCursor(long HideCursor); + STDMETHODIMP IsCursorHidden(__out long *CursorHidden); +}; + +// This class implements the IBasicVideo interface + +class CBaseControlVideo : public CBaseBasicVideo +{ +protected: + + CBaseFilter *m_pFilter; // Pointer to owning media filter + CBasePin *m_pPin; // Controls media types for connection + CCritSec *m_pInterfaceLock; // Externally defined critical section + +public: + + // Derived classes must provide these for the implementation + + virtual HRESULT IsDefaultTargetRect() PURE; + virtual HRESULT SetDefaultTargetRect() PURE; + virtual HRESULT SetTargetRect(RECT *pTargetRect) PURE; + virtual HRESULT GetTargetRect(RECT *pTargetRect) PURE; + virtual HRESULT IsDefaultSourceRect() PURE; + virtual HRESULT SetDefaultSourceRect() PURE; + virtual HRESULT SetSourceRect(RECT *pSourceRect) PURE; + virtual HRESULT GetSourceRect(RECT *pSourceRect) PURE; + virtual HRESULT GetStaticImage(__inout long *pBufferSize,__out_bcount_part(*pBufferSize, *pBufferSize) long *pDIBImage) PURE; + + // Derived classes must override this to return a VIDEOINFO representing + // the video format. We cannot call IPin ConnectionMediaType to get this + // format because various filters dynamically change the type when using + // DirectDraw such that the format shows the position of the logical + // bitmap in a frame buffer surface, so the size might be returned as + // 1024x768 pixels instead of 320x240 which is the real video dimensions + + __out virtual VIDEOINFOHEADER *GetVideoFormat() PURE; + + // Helper functions for creating memory renderings of a DIB image + + HRESULT GetImageSize(__in VIDEOINFOHEADER *pVideoInfo, + __out LONG *pBufferSize, + __in RECT *pSourceRect); + + HRESULT CopyImage(IMediaSample *pMediaSample, + __in VIDEOINFOHEADER *pVideoInfo, + __inout LONG *pBufferSize, + __out_bcount_part(*pBufferSize, *pBufferSize) BYTE *pVideoImage, + __in RECT *pSourceRect); + + // Override this if you want notifying when the rectangles change + virtual HRESULT OnUpdateRectangles() { return NOERROR; }; + virtual HRESULT OnVideoSizeChange(); + + // Derived classes must call this to set the pin the filter is using + // We don't have the pin passed in to the constructor (as we do with + // the CBaseFilter object) because filters typically create the + // pins dynamically when requested in CBaseFilter::GetPin. This can + // not be called from our constructor because is is a virtual method + + void SetControlVideoPin(__inout CBasePin *pPin) { + m_pPin = pPin; + } + + // Helper methods for checking rectangles + virtual HRESULT CheckSourceRect(__in RECT *pSourceRect); + virtual HRESULT CheckTargetRect(__in RECT *pTargetRect); + +public: + + CBaseControlVideo(__inout CBaseFilter *pFilter, // Owning media filter + __in CCritSec *pInterfaceLock, // Serialise interface + __in_opt LPCTSTR pName, // Object description + __inout_opt LPUNKNOWN pUnk, // Normal COM ownership + __inout HRESULT *phr); // OLE return code + + // These are the properties we support + + STDMETHODIMP get_AvgTimePerFrame(__out REFTIME *pAvgTimePerFrame); + STDMETHODIMP get_BitRate(__out long *pBitRate); + STDMETHODIMP get_BitErrorRate(__out long *pBitErrorRate); + STDMETHODIMP get_VideoWidth(__out long *pVideoWidth); + STDMETHODIMP get_VideoHeight(__out long *pVideoHeight); + STDMETHODIMP put_SourceLeft(long SourceLeft); + STDMETHODIMP get_SourceLeft(__out long *pSourceLeft); + STDMETHODIMP put_SourceWidth(long SourceWidth); + STDMETHODIMP get_SourceWidth(__out long *pSourceWidth); + STDMETHODIMP put_SourceTop(long SourceTop); + STDMETHODIMP get_SourceTop(__out long *pSourceTop); + STDMETHODIMP put_SourceHeight(long SourceHeight); + STDMETHODIMP get_SourceHeight(__out long *pSourceHeight); + STDMETHODIMP put_DestinationLeft(long DestinationLeft); + STDMETHODIMP get_DestinationLeft(__out long *pDestinationLeft); + STDMETHODIMP put_DestinationWidth(long DestinationWidth); + STDMETHODIMP get_DestinationWidth(__out long *pDestinationWidth); + STDMETHODIMP put_DestinationTop(long DestinationTop); + STDMETHODIMP get_DestinationTop(__out long *pDestinationTop); + STDMETHODIMP put_DestinationHeight(long DestinationHeight); + STDMETHODIMP get_DestinationHeight(__out long *pDestinationHeight); + + // And these are the methods + + STDMETHODIMP GetVideoSize(__out long *pWidth,__out long *pHeight); + STDMETHODIMP SetSourcePosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetSourcePosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight); + STDMETHODIMP GetVideoPaletteEntries(long StartIndex,long Entries,__out long *pRetrieved,__out_ecount_part(Entries, *pRetrieved) long *pPalette); + STDMETHODIMP SetDefaultSourcePosition(); + STDMETHODIMP IsUsingDefaultSource(); + STDMETHODIMP SetDestinationPosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetDestinationPosition(__out long *pLeft,__out long *pTop,__out long *pWidth,__out long *pHeight); + STDMETHODIMP SetDefaultDestinationPosition(); + STDMETHODIMP IsUsingDefaultDestination(); + STDMETHODIMP GetCurrentImage(__inout long *pBufferSize,__out_bcount_part(*pBufferSize, *pBufferSize) long *pVideoImage); +}; + +#endif // __WINCTRL__ + diff --git a/RTSP Source Filter/Common/DirectShow/winutil.h b/RTSP Source Filter/Common/DirectShow/winutil.h new file mode 100644 index 0000000..641b53a --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/winutil.h @@ -0,0 +1,419 @@ +//------------------------------------------------------------------------------ +// File: WinUtil.h +// +// Desc: DirectShow base classes - defines generic handler classes. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// Make sure that you call PrepareWindow to initialise the window after +// the object has been constructed. It is a separate method so that +// derived classes can override useful methods like MessageLoop. Also +// any derived class must call DoneWithWindow in its destructor. If it +// doesn't a message may be retrieved and call a derived class member +// function while a thread is executing the base class destructor code + +#ifndef __WINUTIL__ +#define __WINUTIL__ + +const int DEFWIDTH = 320; // Initial window width +const int DEFHEIGHT = 240; // Initial window height +const int CAPTION = 256; // Maximum length of caption +const int TIMELENGTH = 50; // Maximum length of times +const int PROFILESTR = 128; // Normal profile string +const WORD PALVERSION = 0x300; // GDI palette version +const LONG PALETTE_VERSION = (LONG) 1; // Initial palette version +const COLORREF VIDEO_COLOUR = 0; // Defaults to black background +const HANDLE hMEMORY = (HANDLE) (-1); // Says to open as memory file + +#define WIDTH(x) ((*(x)).right - (*(x)).left) +#define HEIGHT(x) ((*(x)).bottom - (*(x)).top) +#define SHOWSTAGE TEXT("WM_SHOWSTAGE") +#define SHOWSTAGETOP TEXT("WM_SHOWSTAGETOP") +#define REALIZEPALETTE TEXT("WM_REALIZEPALETTE") + +class AM_NOVTABLE CBaseWindow +{ +protected: + + HINSTANCE m_hInstance; // Global module instance handle + HWND m_hwnd; // Handle for our window + HDC m_hdc; // Device context for the window + LONG m_Width; // Client window width + LONG m_Height; // Client window height + BOOL m_bActivated; // Has the window been activated + LPTSTR m_pClassName; // Static string holding class name + DWORD m_ClassStyles; // Passed in to our constructor + DWORD m_WindowStyles; // Likewise the initial window styles + DWORD m_WindowStylesEx; // And the extended window styles + UINT m_ShowStageMessage; // Have the window shown with focus + UINT m_ShowStageTop; // Makes the window WS_EX_TOPMOST + UINT m_RealizePalette; // Makes us realize our new palette + HDC m_MemoryDC; // Used for fast BitBlt operations + HPALETTE m_hPalette; // Handle to any palette we may have + BYTE m_bNoRealize; // Don't realize palette now + BYTE m_bBackground; // Should we realise in background + BYTE m_bRealizing; // already realizing the palette + CCritSec m_WindowLock; // Serialise window object access + BOOL m_bDoGetDC; // Should this window get a DC + bool m_bDoPostToDestroy; // Use PostMessage to destroy + CCritSec m_PaletteLock; // This lock protects m_hPalette. + // It should be held anytime the + // program use the value of m_hPalette. + + // Maps windows message procedure into C++ methods + friend LRESULT CALLBACK WndProc(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam); // Other parameter + + virtual LRESULT OnPaletteChange(HWND hwnd, UINT Message); + +public: + + CBaseWindow(BOOL bDoGetDC = TRUE, bool bPostToDestroy = false); + +#ifdef DEBUG + virtual ~CBaseWindow(); +#endif + + virtual HRESULT DoneWithWindow(); + virtual HRESULT PrepareWindow(); + virtual HRESULT InactivateWindow(); + virtual HRESULT ActivateWindow(); + virtual BOOL OnSize(LONG Width, LONG Height); + virtual BOOL OnClose(); + virtual RECT GetDefaultRect(); + virtual HRESULT UninitialiseWindow(); + virtual HRESULT InitialiseWindow(HWND hwnd); + + HRESULT CompleteConnect(); + HRESULT DoCreateWindow(); + + HRESULT PerformanceAlignWindow(); + HRESULT DoShowWindow(LONG ShowCmd); + void PaintWindow(BOOL bErase); + void DoSetWindowForeground(BOOL bFocus); + virtual HRESULT SetPalette(HPALETTE hPalette); + void SetRealize(BOOL bRealize) + { + m_bNoRealize = !bRealize; + } + + // Jump over to the window thread to set the current palette + HRESULT SetPalette(); + void UnsetPalette(void); + virtual HRESULT DoRealisePalette(BOOL bForceBackground = FALSE); + + void LockPaletteLock(); + void UnlockPaletteLock(); + + virtual BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { return FALSE; }; + + // Access our window information + + bool WindowExists(); + LONG GetWindowWidth(); + LONG GetWindowHeight(); + HWND GetWindowHWND(); + HDC GetMemoryHDC(); + HDC GetWindowHDC(); + + #ifdef DEBUG + HPALETTE GetPalette(); + #endif // DEBUG + + // This is the window procedure the derived object should override + + virtual LRESULT OnReceiveMessage(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam); // Other parameter + + // Must be overriden to return class and window styles + + virtual LPTSTR GetClassWindowStyles( + __out DWORD *pClassStyles, // Class styles + __out DWORD *pWindowStyles, // Window styles + __out DWORD *pWindowStylesEx) PURE; // Extended styles +}; + + +// This helper class is entirely subservient to the owning CBaseWindow object +// All this object does is to split out the actual drawing operation from the +// main object (because it was becoming too large). We have a number of entry +// points to set things like the draw device contexts, to implement the actual +// drawing and to set the destination rectangle in the client window. We have +// no critical section locking in this class because we are used exclusively +// by the owning window object which looks after serialising calls into us + +// If you want to use this class make sure you call NotifyAllocator once the +// allocate has been agreed, also call NotifyMediaType with a pointer to a +// NON stack based CMediaType once that has been set (we keep a pointer to +// the original rather than taking a copy). When the palette changes call +// IncrementPaletteVersion (easiest thing to do is to also call this method +// in the SetMediaType method most filters implement). Finally before you +// start rendering anything call SetDrawContext so that we can get the HDCs +// for drawing from the CBaseWindow object we are given during construction + +class CDrawImage +{ +protected: + + CBaseWindow *m_pBaseWindow; // Owning video window object + CRefTime m_StartSample; // Start time for the current sample + CRefTime m_EndSample; // And likewise it's end sample time + HDC m_hdc; // Main window device context + HDC m_MemoryDC; // Offscreen draw device context + RECT m_TargetRect; // Target destination rectangle + RECT m_SourceRect; // Source image rectangle + BOOL m_bStretch; // Do we have to stretch the images + BOOL m_bUsingImageAllocator; // Are the samples shared DIBSECTIONs + CMediaType *m_pMediaType; // Pointer to the current format + int m_perfidRenderTime; // Time taken to render an image + LONG m_PaletteVersion; // Current palette version cookie + + // Draw the video images in the window + + void SlowRender(IMediaSample *pMediaSample); + void FastRender(IMediaSample *pMediaSample); + void DisplaySampleTimes(IMediaSample *pSample); + void UpdateColourTable(HDC hdc,__in BITMAPINFOHEADER *pbmi); + void SetStretchMode(); + +public: + + // Used to control the image drawing + + CDrawImage(__inout CBaseWindow *pBaseWindow); + BOOL DrawImage(IMediaSample *pMediaSample); + BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample, + __in LPRECT lprcSrc, __in LPRECT lprcDst); + void SetDrawContext(); + void SetTargetRect(__in RECT *pTargetRect); + void SetSourceRect(__in RECT *pSourceRect); + void GetTargetRect(__out RECT *pTargetRect); + void GetSourceRect(__out RECT *pSourceRect); + virtual RECT ScaleSourceRect(const RECT *pSource); + + // Handle updating palettes as they change + + LONG GetPaletteVersion(); + void ResetPaletteVersion(); + void IncrementPaletteVersion(); + + // Tell us media types and allocator assignments + + void NotifyAllocator(BOOL bUsingImageAllocator); + void NotifyMediaType(__in CMediaType *pMediaType); + BOOL UsingImageAllocator(); + + // Called when we are about to draw an image + + void NotifyStartDraw() { + MSR_START(m_perfidRenderTime); + }; + + // Called when we complete an image rendering + + void NotifyEndDraw() { + MSR_STOP(m_perfidRenderTime); + }; +}; + + +// This is the structure used to keep information about each GDI DIB. All the +// samples we create from our allocator will have a DIBSECTION allocated to +// them. When we receive the sample we know we can BitBlt straight to an HDC + +typedef struct tagDIBDATA { + + LONG PaletteVersion; // Current palette version in use + DIBSECTION DibSection; // Details of DIB section allocated + HBITMAP hBitmap; // Handle to bitmap for drawing + HANDLE hMapping; // Handle to shared memory block + BYTE *pBase; // Pointer to base memory address + +} DIBDATA; + + +// This class inherits from CMediaSample and uses all of it's methods but it +// overrides the constructor to initialise itself with the DIBDATA structure +// When we come to render an IMediaSample we will know if we are using our own +// allocator, and if we are, we can cast the IMediaSample to a pointer to one +// of these are retrieve the DIB section information and hence the HBITMAP + +class CImageSample : public CMediaSample +{ +protected: + + DIBDATA m_DibData; // Information about the DIBSECTION + BOOL m_bInit; // Is the DIB information setup + +public: + + // Constructor + + CImageSample(__inout CBaseAllocator *pAllocator, + __in_opt LPCTSTR pName, + __inout HRESULT *phr, + __in_bcount(length) LPBYTE pBuffer, + LONG length); + + // Maintain the DIB/DirectDraw state + + void SetDIBData(__in DIBDATA *pDibData); + __out DIBDATA *GetDIBData(); +}; + + +// This is an allocator based on the abstract CBaseAllocator base class that +// allocates sample buffers in shared memory. The number and size of these +// are determined when the output pin calls Prepare on us. The shared memory +// blocks are used in subsequent calls to GDI CreateDIBSection, once that +// has been done the output pin can fill the buffers with data which will +// then be handed to GDI through BitBlt calls and thereby remove one copy + +class CImageAllocator : public CBaseAllocator +{ +protected: + + CBaseFilter *m_pFilter; // Delegate reference counts to + CMediaType *m_pMediaType; // Pointer to the current format + + // Used to create and delete samples + + HRESULT Alloc(); + void Free(); + + // Manage the shared DIBSECTION and DCI/DirectDraw buffers + + HRESULT CreateDIB(LONG InSize,DIBDATA &DibData); + STDMETHODIMP CheckSizes(__in ALLOCATOR_PROPERTIES *pRequest); + virtual CImageSample *CreateImageSample(__in_bcount(Length) LPBYTE pData,LONG Length); + +public: + + // Constructor and destructor + + CImageAllocator(__inout CBaseFilter *pFilter,__in_opt LPCTSTR pName,__inout HRESULT *phr); +#ifdef DEBUG + ~CImageAllocator(); +#endif + + STDMETHODIMP_(ULONG) NonDelegatingAddRef(); + STDMETHODIMP_(ULONG) NonDelegatingRelease(); + void NotifyMediaType(__in CMediaType *pMediaType); + + // Agree the number of buffers to be used and their size + + STDMETHODIMP SetProperties( + __in ALLOCATOR_PROPERTIES *pRequest, + __out ALLOCATOR_PROPERTIES *pActual); +}; + + +// This class is a fairly specialised helper class for image renderers that +// have to create and manage palettes. The CBaseWindow class looks after +// realising palettes once they have been installed. This class can be used +// to create the palette handles from a media format (which must contain a +// VIDEOINFO structure in the format block). We try to make the palette an +// identity palette to maximise performance and also only change palettes +// if actually required to (we compare palette colours before updating). +// All the methods are virtual so that they can be overriden if so required + +class CImagePalette +{ +protected: + + CBaseWindow *m_pBaseWindow; // Window to realise palette in + CBaseFilter *m_pFilter; // Media filter to send events + CDrawImage *m_pDrawImage; // Object who will be drawing + HPALETTE m_hPalette; // The palette handle we own + +public: + + CImagePalette(__inout CBaseFilter *pBaseFilter, + __inout CBaseWindow *pBaseWindow, + __inout CDrawImage *pDrawImage); + +#ifdef DEBUG + virtual ~CImagePalette(); +#endif + + static HPALETTE MakePalette(const VIDEOINFOHEADER *pVideoInfo, __in LPSTR szDevice); + HRESULT RemovePalette(); + static HRESULT MakeIdentityPalette(__inout_ecount_full(iColours) PALETTEENTRY *pEntry,INT iColours, __in LPSTR szDevice); + HRESULT CopyPalette(const CMediaType *pSrc,__out CMediaType *pDest); + BOOL ShouldUpdate(const VIDEOINFOHEADER *pNewInfo,const VIDEOINFOHEADER *pOldInfo); + HRESULT PreparePalette(const CMediaType *pmtNew,const CMediaType *pmtOld,__in LPSTR szDevice); + + BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample, __in LPRECT lprcSrc, __in LPRECT lprcDst) + { + return m_pDrawImage->DrawVideoImageHere(hdc, pMediaSample, lprcSrc,lprcDst); + } +}; + + +// Another helper class really for video based renderers. Most such renderers +// need to know what the display format is to some degree or another. This +// class initialises itself with the display format. The format can be asked +// for through GetDisplayFormat and various other accessor functions. If a +// filter detects a display format change (perhaps it gets a WM_DEVMODECHANGE +// message then it can call RefreshDisplayType to reset that format). Also +// many video renderers will want to check formats as they are proposed by +// source filters. This class provides methods to check formats and only +// accept those video formats that can be efficiently drawn using GDI calls + +class CImageDisplay : public CCritSec +{ +protected: + + // This holds the display format; biSize should not be too big, so we can + // safely use the VIDEOINFO structure + VIDEOINFO m_Display; + + static DWORD CountSetBits(const DWORD Field); + static DWORD CountPrefixBits(const DWORD Field); + static BOOL CheckBitFields(const VIDEOINFO *pInput); + +public: + + // Constructor and destructor + + CImageDisplay(); + + // Used to manage BITMAPINFOHEADERs and the display format + + const VIDEOINFO *GetDisplayFormat(); + HRESULT RefreshDisplayType(__in_opt LPSTR szDeviceName); + static BOOL CheckHeaderValidity(const VIDEOINFO *pInput); + static BOOL CheckPaletteHeader(const VIDEOINFO *pInput); + BOOL IsPalettised(); + WORD GetDisplayDepth(); + + // Provide simple video format type checking + + HRESULT CheckMediaType(const CMediaType *pmtIn); + HRESULT CheckVideoType(const VIDEOINFO *pInput); + HRESULT UpdateFormat(__inout VIDEOINFO *pVideoInfo); + const DWORD *GetBitMasks(const VIDEOINFO *pVideoInfo); + + BOOL GetColourMask(__out DWORD *pMaskRed, + __out DWORD *pMaskGreen, + __out DWORD *pMaskBlue); +}; + +// Convert a FORMAT_VideoInfo to FORMAT_VideoInfo2 +STDAPI ConvertVideoInfoToVideoInfo2(__inout AM_MEDIA_TYPE *pmt); + +// Check a media type containing VIDEOINFOHEADER +STDAPI CheckVideoInfoType(const AM_MEDIA_TYPE *pmt); + +// Check a media type containing VIDEOINFOHEADER +STDAPI CheckVideoInfo2Type(const AM_MEDIA_TYPE *pmt); + +#endif // __WINUTIL__ + diff --git a/RTSP Source Filter/Common/DirectShow/wxdebug.h b/RTSP Source Filter/Common/DirectShow/wxdebug.h new file mode 100644 index 0000000..62efffb --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/wxdebug.h @@ -0,0 +1,359 @@ +//------------------------------------------------------------------------------ +// File: WXDebug.h +// +// Desc: DirectShow base classes - provides debugging facilities. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WXDEBUG__ +#define __WXDEBUG__ + +// This library provides fairly straight forward debugging functionality, this +// is split into two main sections. The first is assertion handling, there are +// three types of assertions provided here. The most commonly used one is the +// ASSERT(condition) macro which will pop up a message box including the file +// and line number if the condition evaluates to FALSE. Then there is the +// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will +// still be executed in NON debug builds. The final type of assertion is the +// KASSERT macro which is more suitable for pure (perhaps kernel) filters as +// the condition is printed onto the debugger rather than in a message box. +// +// The other part of the debug module facilties is general purpose logging. +// This is accessed by calling DbgLog(). The function takes a type and level +// field which define the type of informational string you are presenting and +// it's relative importance. The type field can be a combination (one or more) +// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level +// is a DWORD value where zero defines highest important. Use of zero as the +// debug logging level is to be encouraged ONLY for major errors or events as +// they will ALWAYS be displayed on the debugger. Other debug output has it's +// level matched against the current debug output level stored in the registry +// for this module and if less than the current setting it will be displayed. +// +// Each module or executable has it's own debug output level for each of the +// five types. These are read in when the DbgInitialise function is called +// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL +// is loaded, executables must call it explicitely with the module instance +// handle given to them through the WINMAIN entry point. An executable must +// also call DbgTerminate when they have finished to clean up the resources +// the debug library uses, once again this is done automatically for DLLs + +// These are the five different categories of logging information + +enum { LOG_TIMING = 0x01, // Timing and performance measurements + LOG_TRACE = 0x02, // General step point call tracing + LOG_MEMORY = 0x04, // Memory and object allocation/destruction + LOG_LOCKING = 0x08, // Locking/unlocking of critical sections + LOG_ERROR = 0x10, // Debug error notification + LOG_CUSTOM1 = 0x20, + LOG_CUSTOM2 = 0x40, + LOG_CUSTOM3 = 0x80, + LOG_CUSTOM4 = 0x100, + LOG_CUSTOM5 = 0x200, +}; + +#define LOG_FORCIBLY_SET 0x80000000 + +enum { CDISP_HEX = 0x01, + CDISP_DEC = 0x02}; + +// For each object created derived from CBaseObject (in debug builds) we +// create a descriptor that holds it's name (statically allocated memory) +// and a cookie we assign it. We keep a list of all the active objects +// we have registered so that we can dump a list of remaining objects + +typedef struct tag_ObjectDesc { + LPCSTR m_szName; + LPCWSTR m_wszName; + DWORD m_dwCookie; + tag_ObjectDesc *m_pNext; +} ObjectDesc; + +#define DLLIMPORT __declspec(dllimport) +#define DLLEXPORT __declspec(dllexport) + +#ifdef DEBUG + + #define NAME(x) TEXT(x) + + // These are used internally by the debug library (PRIVATE) + + void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax); + void WINAPI DbgInitGlobalSettings(bool fTakeMax); + void WINAPI DbgInitModuleSettings(bool fTakeMax); + void WINAPI DbgInitModuleName(); + DWORD WINAPI DbgRegisterObjectCreation( + LPCSTR szObjectName, LPCWSTR wszObjectName); + + BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie); + + // These are the PUBLIC entry points + + BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level); + void WINAPI DbgSetModuleLevel(DWORD Type,DWORD Level); + void WINAPI DbgSetAutoRefreshLevels(bool fAuto); + + // Initialise the library with the module handle + + void WINAPI DbgInitialise(HINSTANCE hInst); + void WINAPI DbgTerminate(); + + void WINAPI DbgDumpObjectRegister(); + + // Display error and logging to the user + + void WINAPI DbgAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine); + void WINAPI DbgBreakPoint(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine); + void WINAPI DbgBreakPoint(LPCTSTR pFileName,INT iLine,__format_string LPCTSTR szFormatString,...); + + void WINAPI DbgKernelAssert(LPCTSTR pCondition,LPCTSTR pFileName,INT iLine); + void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCTSTR pFormat,...); +#ifdef UNICODE + void WINAPI DbgLogInfo(DWORD Type,DWORD Level,__format_string LPCSTR pFormat,...); + void WINAPI DbgAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine); + void WINAPI DbgBreakPoint(LPCSTR pCondition,LPCSTR pFileName,INT iLine); + void WINAPI DbgKernelAssert(LPCSTR pCondition,LPCSTR pFileName,INT iLine); +#endif + void WINAPI DbgOutString(LPCTSTR psz); + + // Debug infinite wait stuff + DWORD WINAPI DbgWaitForSingleObject(HANDLE h); + DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount, + __in_ecount(nCount) CONST HANDLE *lpHandles, + BOOL bWaitAll); + void WINAPI DbgSetWaitTimeout(DWORD dwTimeout); + +#ifdef __strmif_h__ + // Display a media type: Terse at level 2, verbose at level 5 + void WINAPI DisplayType(LPCTSTR label, const AM_MEDIA_TYPE *pmtIn); + + // Dump lots of information about a filter graph + void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel); +#endif + + #define KASSERT(_x_) if (!(_x_)) \ + DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + // Break on the debugger without putting up a message box + // message goes to debugger instead + + #define KDbgBreak(_x_) \ + DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + // We chose a common name for our ASSERT macro, MFC also uses this name + // So long as the implementation evaluates the condition and handles it + // then we will be ok. Rather than override the behaviour expected we + // will leave whatever first defines ASSERT as the handler (i.e. MFC) + #ifndef ASSERT + #define ASSERT(_x_) if (!(_x_)) \ + DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + #endif + + #define DbgAssertAligned( _ptr_, _alignment_ ) ASSERT( ((DWORD_PTR) (_ptr_)) % (_alignment_) == 0) + + // Put up a message box informing the user of a halt + // condition in the program + + #define DbgBreak(_x_) \ + DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + #define EXECUTE_ASSERT(_x_) ASSERT(_x_) + #define DbgLog(_x_) DbgLogInfo _x_ + // MFC style trace macros + + #define NOTE(_x_) DbgLog((LOG_TRACE,5,TEXT(_x_))) + #define NOTE1(_x_,a) DbgLog((LOG_TRACE,5,TEXT(_x_),a)) + #define NOTE2(_x_,a,b) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b)) + #define NOTE3(_x_,a,b,c) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c)) + #define NOTE4(_x_,a,b,c,d) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d)) + #define NOTE5(_x_,a,b,c,d,e) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e)) + +#else + + // Retail builds make public debug functions inert - WARNING the source + // files do not define or build any of the entry points in debug builds + // (public entry points compile to nothing) so if you go trying to call + // any of the private entry points in your source they won't compile + + #define NAME(_x_) ((LPTSTR) NULL) + + #define DbgInitialise(hInst) + #define DbgTerminate() + #define DbgLog(_x_) 0 + #define DbgOutString(psz) + #define DbgAssertAligned( _ptr_, _alignment_ ) 0 + + #define DbgRegisterObjectCreation(pObjectName) + #define DbgRegisterObjectDestruction(dwCookie) + #define DbgDumpObjectRegister() + + #define DbgCheckModuleLevel(Type,Level) + #define DbgSetModuleLevel(Type,Level) + #define DbgSetAutoRefreshLevels(fAuto) + + #define DbgWaitForSingleObject(h) WaitForSingleObject(h, INFINITE) + #define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll) \ + WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE) + #define DbgSetWaitTimeout(dwTimeout) + + #define KDbgBreak(_x_) + #define DbgBreak(_x_) + + #define KASSERT(_x_) ((void)0) + #ifndef ASSERT + #define ASSERT(_x_) ((void)0) + #endif + #define EXECUTE_ASSERT(_x_) ((void)(_x_)) + + // MFC style trace macros + + #define NOTE(_x_) ((void)0) + #define NOTE1(_x_,a) ((void)0) + #define NOTE2(_x_,a,b) ((void)0) + #define NOTE3(_x_,a,b,c) ((void)0) + #define NOTE4(_x_,a,b,c,d) ((void)0) + #define NOTE5(_x_,a,b,c,d,e) ((void)0) + + #define DisplayType(label, pmtIn) ((void)0) + #define DumpGraph(pGraph, label) ((void)0) +#endif + + +// Checks a pointer which should be non NULL - can be used as follows. + +#define CheckPointer(p,ret) {if((p)==NULL) return (ret);} + +// HRESULT Foo(VOID *pBar) +// { +// CheckPointer(pBar,E_INVALIDARG) +// } +// +// Or if the function returns a boolean +// +// BOOL Foo(VOID *pBar) +// { +// CheckPointer(pBar,FALSE) +// } + +#define ValidateReadPtr(p,cb) 0 +#define ValidateWritePtr(p,cb) 0 +#define ValidateReadWritePtr(p,cb) 0 +#define ValidateStringPtr(p) 0 +#define ValidateStringPtrA(p) 0 +#define ValidateStringPtrW(p) 0 + + +#ifdef _OBJBASE_H_ + + // Outputting GUID names. If you want to include the name + // associated with a GUID (eg CLSID_...) then + // + // GuidNames[yourGUID] + // + // Returns the name defined in uuids.h as a string + + typedef struct { + CHAR *szName; + GUID guid; + } GUID_STRING_ENTRY; + + class CGuidNameList { + public: + CHAR *operator [] (const GUID& guid); + }; + + extern CGuidNameList GuidNames; + +#endif + +#ifndef REMIND + // REMIND macro - generates warning as reminder to complete coding + // (eg) usage: + // + // #pragma message (REMIND("Add automation support")) + + + #define QUOTE(x) #x + #define QQUOTE(y) QUOTE(y) + #define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") : " str +#endif + +// Method to display objects in a useful format +// +// eg If you want to display a LONGLONG ll in a debug string do (eg) +// +// DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX))); + + +class CDispBasic +{ +public: + CDispBasic() { m_pString = m_String; }; + ~CDispBasic(); +protected: + PTCHAR m_pString; // normally points to m_String... unless too much data + TCHAR m_String[50]; +}; +class CDisp : public CDispBasic +{ +public: + CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form + CDisp(REFCLSID clsid); // Display a GUID + CDisp(double d); // Display a floating point number +#ifdef __strmif_h__ +#ifdef __STREAMS__ + CDisp(CRefTime t); // Display a Reference Time +#endif + CDisp(IPin *pPin); // Display a pin as {filter clsid}(pin name) + CDisp(IUnknown *pUnk); // Display a filter or pin +#endif // __strmif_h__ + ~CDisp(); + + // Implement cast to (LPCTSTR) as parameter to logger + operator LPCTSTR() + { + return (LPCTSTR)m_pString; + }; +}; + + +#if defined(DEBUG) +class CAutoTrace +{ +private: + LPCTSTR _szBlkName; + const int _level; + static const TCHAR _szEntering[]; + static const TCHAR _szLeaving[]; +public: + CAutoTrace(LPCTSTR szBlkName, const int level = 15) + : _szBlkName(szBlkName), _level(level) + {DbgLog((LOG_TRACE, _level, _szEntering, _szBlkName));} + + ~CAutoTrace() + {DbgLog((LOG_TRACE, _level, _szLeaving, _szBlkName));} +}; + +#if defined (__FUNCTION__) + +#define AMTRACEFN() CAutoTrace __trace(TEXT(__FUNCTION__)) +#define AMTRACE(_x_) CAutoTrace __trace(TEXT(__FUNCTION__)) + +#else + +#define AMTRACE(_x_) CAutoTrace __trace _x_ +#define AMTRACEFN() + +#endif + +#else + +#define AMTRACE(_x_) +#define AMTRACEFN() + +#endif + +#endif // __WXDEBUG__ + + diff --git a/RTSP Source Filter/Common/DirectShow/wxlist.h b/RTSP Source Filter/Common/DirectShow/wxlist.h new file mode 100644 index 0000000..931893d --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/wxlist.h @@ -0,0 +1,553 @@ +//------------------------------------------------------------------------------ +// File: WXList.h +// +// Desc: DirectShow base classes - defines a non-MFC generic template list +// class. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +/* A generic list of pointers to objects. + No storage management or copying is done on the objects pointed to. + Objectives: avoid using MFC libraries in ndm kernel mode and + provide a really useful list type. + + The class is thread safe in that separate threads may add and + delete items in the list concurrently although the application + must ensure that constructor and destructor access is suitably + synchronised. An application can cause deadlock with operations + which use two lists by simultaneously calling + list1->Operation(list2) and list2->Operation(list1). So don't! + + The names must not conflict with MFC classes as an application + may use both. + */ + +#ifndef __WXLIST__ +#define __WXLIST__ + + /* A POSITION represents (in some fashion that's opaque) a cursor + on the list that can be set to identify any element. NULL is + a valid value and several operations regard NULL as the position + "one step off the end of the list". (In an n element list there + are n+1 places to insert and NULL is that "n+1-th" value). + The POSITION of an element in the list is only invalidated if + that element is deleted. Move operations may mean that what + was a valid POSITION in one list is now a valid POSITION in + a different list. + + Some operations which at first sight are illegal are allowed as + harmless no-ops. For instance RemoveHead is legal on an empty + list and it returns NULL. This allows an atomic way to test if + there is an element there, and if so, get it. The two operations + AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper). + + Single element operations return POSITIONs, non-NULL means it worked. + whole list operations return a BOOL. TRUE means it all worked. + + This definition is the same as the POSITION type for MFCs, so we must + avoid defining it twice. + */ +#ifndef __AFX_H__ +struct __POSITION { int unused; }; +typedef __POSITION* POSITION; +#endif + +const int DEFAULTCACHE = 10; /* Default node object cache size */ + +/* A class representing one node in a list. + Each node knows a pointer to it's adjacent nodes and also a pointer + to the object that it looks after. + All of these pointers can be retrieved or set through member functions. +*/ +class CBaseList +#ifdef DEBUG + : public CBaseObject +#endif +{ + /* Making these classes inherit from CBaseObject does nothing + functionally but it allows us to check there are no memory + leaks in debug builds. + */ + +public: + +#ifdef DEBUG + class CNode : public CBaseObject { +#else + class CNode { +#endif + + CNode *m_pPrev; /* Previous node in the list */ + CNode *m_pNext; /* Next node in the list */ + void *m_pObject; /* Pointer to the object */ + + public: + + /* Constructor - initialise the object's pointers */ + CNode() +#ifdef DEBUG + : CBaseObject(NAME("List node")) +#endif + { + }; + + + /* Return the previous node before this one */ + __out CNode *Prev() const { return m_pPrev; }; + + + /* Return the next node after this one */ + __out CNode *Next() const { return m_pNext; }; + + + /* Set the previous node before this one */ + void SetPrev(__in_opt CNode *p) { m_pPrev = p; }; + + + /* Set the next node after this one */ + void SetNext(__in_opt CNode *p) { m_pNext = p; }; + + + /* Get the pointer to the object for this node */ + __out void *GetData() const { return m_pObject; }; + + + /* Set the pointer to the object for this node */ + void SetData(__in void *p) { m_pObject = p; }; + }; + + class CNodeCache + { + public: + CNodeCache(INT iCacheSize) : m_iCacheSize(iCacheSize), + m_pHead(NULL), + m_iUsed(0) + {}; + ~CNodeCache() { + CNode *pNode = m_pHead; + while (pNode) { + CNode *pCurrent = pNode; + pNode = pNode->Next(); + delete pCurrent; + } + }; + void AddToCache(__inout CNode *pNode) + { + if (m_iUsed < m_iCacheSize) { + pNode->SetNext(m_pHead); + m_pHead = pNode; + m_iUsed++; + } else { + delete pNode; + } + }; + CNode *RemoveFromCache() + { + CNode *pNode = m_pHead; + if (pNode != NULL) { + m_pHead = pNode->Next(); + m_iUsed--; + ASSERT(m_iUsed >= 0); + } else { + ASSERT(m_iUsed == 0); + } + return pNode; + }; + private: + INT m_iCacheSize; + INT m_iUsed; + CNode *m_pHead; + }; + +protected: + + CNode* m_pFirst; /* Pointer to first node in the list */ + CNode* m_pLast; /* Pointer to the last node in the list */ + LONG m_Count; /* Number of nodes currently in the list */ + +private: + + CNodeCache m_Cache; /* Cache of unused node pointers */ + +private: + + /* These override the default copy constructor and assignment + operator for all list classes. They are in the private class + declaration section so that anybody trying to pass a list + object by value will generate a compile time error of + "cannot access the private member function". If these were + not here then the compiler will create default constructors + and assignment operators which when executed first take a + copy of all member variables and then during destruction + delete them all. This must not be done for any heap + allocated data. + */ + CBaseList(const CBaseList &refList); + CBaseList &operator=(const CBaseList &refList); + +public: + + CBaseList(__in_opt LPCTSTR pName, + INT iItems); + + CBaseList(__in_opt LPCTSTR pName); +#ifdef UNICODE + CBaseList(__in_opt LPCSTR pName, + INT iItems); + + CBaseList(__in_opt LPCSTR pName); +#endif + ~CBaseList(); + + /* Remove all the nodes from *this i.e. make the list empty */ + void RemoveAll(); + + + /* Return a cursor which identifies the first element of *this */ + __out_opt POSITION GetHeadPositionI() const; + + + /* Return a cursor which identifies the last element of *this */ + __out_opt POSITION GetTailPositionI() const; + + + /* Return the number of objects in *this */ + int GetCountI() const; + +protected: + /* Return the pointer to the object at rp, + Update rp to the next node in *this + but make it NULL if it was at the end of *this. + This is a wart retained for backwards compatibility. + GetPrev is not implemented. + Use Next, Prev and Get separately. + */ + __out void *GetNextI(__inout POSITION& rp) const; + + + /* Return a pointer to the object at p + Asking for the object at NULL will return NULL harmlessly. + */ + __out_opt void *GetI(__in_opt POSITION p) const; + __out void *GetValidI(__in POSITION p) const; + +public: + /* return the next / prev position in *this + return NULL when going past the end/start. + Next(NULL) is same as GetHeadPosition() + Prev(NULL) is same as GetTailPosition() + An n element list therefore behaves like a n+1 element + cycle with NULL at the start/end. + + !!WARNING!! - This handling of NULL is DIFFERENT from GetNext. + + Some reasons are: + 1. For a list of n items there are n+1 positions to insert + These are conveniently encoded as the n POSITIONs and NULL. + 2. If you are keeping a list sorted (fairly common) and you + search forward for an element to insert before and don't + find it you finish up with NULL as the element before which + to insert. You then want that NULL to be a valid POSITION + so that you can insert before it and you want that insertion + point to mean the (n+1)-th one that doesn't have a POSITION. + (symmetrically if you are working backwards through the list). + 3. It simplifies the algebra which the methods generate. + e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x) + in ALL cases. All the other arguments probably are reflections + of the algebraic point. + */ + __out_opt POSITION Next(__in_opt POSITION pos) const + { + if (pos == NULL) { + return (POSITION) m_pFirst; + } + CNode *pn = (CNode *) pos; + return (POSITION) pn->Next(); + } //Next + + // See Next + __out_opt POSITION Prev(__in_opt POSITION pos) const + { + if (pos == NULL) { + return (POSITION) m_pLast; + } + CNode *pn = (CNode *) pos; + return (POSITION) pn->Prev(); + } //Prev + + + /* Return the first position in *this which holds the given + pointer. Return NULL if the pointer was not not found. + */ +protected: + __out_opt POSITION FindI( __in void * pObj) const; + + // ??? Should there be (or even should there be only) + // ??? POSITION FindNextAfter(void * pObj, POSITION p) + // ??? And of course FindPrevBefore too. + // ??? List.Find(&Obj) then becomes List.FindNextAfter(&Obj, NULL) + + + /* Remove the first node in *this (deletes the pointer to its + object from the list, does not free the object itself). + Return the pointer to its object. + If *this was already empty it will harmlessly return NULL. + */ + __out_opt void *RemoveHeadI(); + + + /* Remove the last node in *this (deletes the pointer to its + object from the list, does not free the object itself). + Return the pointer to its object. + If *this was already empty it will harmlessly return NULL. + */ + __out_opt void *RemoveTailI(); + + + /* Remove the node identified by p from the list (deletes the pointer + to its object from the list, does not free the object itself). + Asking to Remove the object at NULL will harmlessly return NULL. + Return the pointer to the object removed. + */ + __out_opt void *RemoveI(__in_opt POSITION p); + + /* Add single object *pObj to become a new last element of the list. + Return the new tail position, NULL if it fails. + If you are adding a COM objects, you might want AddRef it first. + Other existing POSITIONs in *this are still valid + */ + __out_opt POSITION AddTailI(__in void * pObj); +public: + + + /* Add all the elements in *pList to the tail of *this. + This duplicates all the nodes in *pList (i.e. duplicates + all its pointers to objects). It does not duplicate the objects. + If you are adding a list of pointers to a COM object into the list + it's a good idea to AddRef them all it when you AddTail it. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some elements may have been added. + Existing POSITIONs in *this are still valid + + If you actually want to MOVE the elements, use MoveToTail instead. + */ + BOOL AddTail(__in CBaseList *pList); + + + /* Mirror images of AddHead: */ + + /* Add single object to become a new first element of the list. + Return the new head position, NULL if it fails. + Existing POSITIONs in *this are still valid + */ +protected: + __out_opt POSITION AddHeadI(__in void * pObj); +public: + + /* Add all the elements in *pList to the head of *this. + Same warnings apply as for AddTail. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some of the objects may have been added. + + If you actually want to MOVE the elements, use MoveToHead instead. + */ + BOOL AddHead(__in CBaseList *pList); + + + /* Add the object *pObj to *this after position p in *this. + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return the position of the object added, NULL if it failed. + Existing POSITIONs in *this are undisturbed, including p. + */ +protected: + __out_opt POSITION AddAfterI(__in_opt POSITION p, __in void * pObj); +public: + + /* Add the list *pList to *this after position p in *this + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return TRUE if it all worked, FALSE if it didn't. + If it fails, some of the objects may be added + Existing POSITIONs in *this are undisturbed, including p. + */ + BOOL AddAfter(__in_opt POSITION p, __in CBaseList *pList); + + + /* Mirror images: + Add the object *pObj to this-List after position p in *this. + AddBefore(NULL,x) adds x to the end - equivalent to AddTail + Return the position of the new object, NULL if it fails + Existing POSITIONs in *this are undisturbed, including p. + */ + protected: + __out_opt POSITION AddBeforeI(__in_opt POSITION p, __in void * pObj); + public: + + /* Add the list *pList to *this before position p in *this + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return TRUE if it all worked, FALSE if it didn't. + If it fails, some of the objects may be added + Existing POSITIONs in *this are undisturbed, including p. + */ + BOOL AddBefore(__in_opt POSITION p, __in CBaseList *pList); + + + /* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x) + even in cases where p is NULL or Next(p) is NULL. + Similarly for mirror images etc. + This may make it easier to argue about programs. + */ + + + + /* The following operations do not copy any elements. + They move existing blocks of elements around by switching pointers. + They are fairly efficient for long lists as for short lists. + (Alas, the Count slows things down). + + They split the list into two parts. + One part remains as the original list, the other part + is appended to the second list. There are eight possible + variations: + Split the list {after/before} a given element + keep the {head/tail} portion in the original list + append the rest to the {head/tail} of the new list. + + Since After is strictly equivalent to Before Next + we are not in serious need of the Before/After variants. + That leaves only four. + + If you are processing a list left to right and dumping + the bits that you have processed into another list as + you go, the Tail/Tail variant gives the most natural result. + If you are processing in reverse order, Head/Head is best. + + By using NULL positions and empty lists judiciously either + of the other two can be built up in two operations. + + The definition of NULL (see Next/Prev etc) means that + degenerate cases include + "move all elements to new list" + "Split a list into two lists" + "Concatenate two lists" + (and quite a few no-ops) + + !!WARNING!! The type checking won't buy you much if you get list + positions muddled up - e.g. use a POSITION that's in a different + list and see what a mess you get! + */ + + /* Split *this after position p in *this + Retain as *this the tail portion of the original *this + Add the head portion to the tail end of *pList + Return TRUE if it all worked, FALSE if it didn't. + + e.g. + foo->MoveToTail(foo->GetHeadPosition(), bar); + moves one element from the head of foo to the tail of bar + foo->MoveToTail(NULL, bar); + is a no-op, returns NULL + foo->MoveToTail(foo->GetTailPosition, bar); + concatenates foo onto the end of bar and empties foo. + + A better, except excessively long name might be + MoveElementsFromHeadThroughPositionToOtherTail + */ + BOOL MoveToTail(__in_opt POSITION pos, __in CBaseList *pList); + + + /* Mirror image: + Split *this before position p in *this. + Retain in *this the head portion of the original *this + Add the tail portion to the start (i.e. head) of *pList + + e.g. + foo->MoveToHead(foo->GetTailPosition(), bar); + moves one element from the tail of foo to the head of bar + foo->MoveToHead(NULL, bar); + is a no-op, returns NULL + foo->MoveToHead(foo->GetHeadPosition, bar); + concatenates foo onto the start of bar and empties foo. + */ + BOOL MoveToHead(__in_opt POSITION pos, __in CBaseList *pList); + + + /* Reverse the order of the [pointers to] objects in *this + */ + void Reverse(); + + + /* set cursor to the position of each element of list in turn */ + #define TRAVERSELIST(list, cursor) \ + for ( cursor = (list).GetHeadPosition() \ + ; cursor!=NULL \ + ; cursor = (list).Next(cursor) \ + ) + + + /* set cursor to the position of each element of list in turn + in reverse order + */ + #define REVERSETRAVERSELIST(list, cursor) \ + for ( cursor = (list).GetTailPosition() \ + ; cursor!=NULL \ + ; cursor = (list).Prev(cursor) \ + ) + +}; // end of class declaration + +template class CGenericList : public CBaseList +{ +public: + CGenericList(__in_opt LPCTSTR pName, + INT iItems, + BOOL bLock = TRUE, + BOOL bAlert = FALSE) : + CBaseList(pName, iItems) { + UNREFERENCED_PARAMETER(bAlert); + UNREFERENCED_PARAMETER(bLock); + }; + CGenericList(__in_opt LPCTSTR pName) : + CBaseList(pName) { + }; + + __out_opt POSITION GetHeadPosition() const { return (POSITION)m_pFirst; } + __out_opt POSITION GetTailPosition() const { return (POSITION)m_pLast; } + int GetCount() const { return m_Count; } + + __out OBJECT *GetNext(__inout POSITION& rp) const { return (OBJECT *) GetNextI(rp); } + + __out_opt OBJECT *Get(__in_opt POSITION p) const { return (OBJECT *) GetI(p); } + __out OBJECT *GetValid(__in POSITION p) const { return (OBJECT *) GetValidI(p); } + __out_opt OBJECT *GetHead() const { return Get(GetHeadPosition()); } + + __out_opt OBJECT *RemoveHead() { return (OBJECT *) RemoveHeadI(); } + + __out_opt OBJECT *RemoveTail() { return (OBJECT *) RemoveTailI(); } + + __out_opt OBJECT *Remove(__in_opt POSITION p) { return (OBJECT *) RemoveI(p); } + __out_opt POSITION AddBefore(__in_opt POSITION p, __in OBJECT * pObj) { return AddBeforeI(p, pObj); } + __out_opt POSITION AddAfter(__in_opt POSITION p, __in OBJECT * pObj) { return AddAfterI(p, pObj); } + __out_opt POSITION AddHead(__in OBJECT * pObj) { return AddHeadI(pObj); } + __out_opt POSITION AddTail(__in OBJECT * pObj) { return AddTailI(pObj); } + BOOL AddTail(__in CGenericList *pList) + { return CBaseList::AddTail((CBaseList *) pList); } + BOOL AddHead(__in CGenericList *pList) + { return CBaseList::AddHead((CBaseList *) pList); } + BOOL AddAfter(__in_opt POSITION p, __in CGenericList *pList) + { return CBaseList::AddAfter(p, (CBaseList *) pList); }; + BOOL AddBefore(__in_opt POSITION p, __in CGenericList *pList) + { return CBaseList::AddBefore(p, (CBaseList *) pList); }; + __out_opt POSITION Find( __in OBJECT * pObj) const { return FindI(pObj); } +}; // end of class declaration + + + +/* These define the standard list types */ + +typedef CGenericList CBaseObjectList; +typedef CGenericList CBaseInterfaceList; + +#endif /* __WXLIST__ */ + diff --git a/RTSP Source Filter/Common/DirectShow/wxutil.h b/RTSP Source Filter/Common/DirectShow/wxutil.h new file mode 100644 index 0000000..3bfc2d2 --- /dev/null +++ b/RTSP Source Filter/Common/DirectShow/wxutil.h @@ -0,0 +1,532 @@ +//------------------------------------------------------------------------------ +// File: WXUtil.h +// +// Desc: DirectShow base classes - defines helper classes and functions for +// building multimedia filters. +// +// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WXUTIL__ +#define __WXUTIL__ + +// eliminate spurious "statement has no effect" warnings. +#pragma warning(disable: 4705) + +// wrapper for whatever critical section we have +class CCritSec { + + // make copy constructor and assignment operator inaccessible + + CCritSec(const CCritSec &refCritSec); + CCritSec &operator=(const CCritSec &refCritSec); + + CRITICAL_SECTION m_CritSec; + +#ifdef DEBUG +public: + DWORD m_currentOwner; + DWORD m_lockCount; + BOOL m_fTrace; // Trace this one +public: + CCritSec(); + ~CCritSec(); + void Lock(); + void Unlock(); +#else + +public: + CCritSec() { + InitializeCriticalSection(&m_CritSec); + }; + + ~CCritSec() { + DeleteCriticalSection(&m_CritSec); + }; + + void Lock() { + EnterCriticalSection(&m_CritSec); + }; + + void Unlock() { + LeaveCriticalSection(&m_CritSec); + }; +#endif +}; + +// +// To make deadlocks easier to track it is useful to insert in the +// code an assertion that says whether we own a critical section or +// not. We make the routines that do the checking globals to avoid +// having different numbers of member functions in the debug and +// retail class implementations of CCritSec. In addition we provide +// a routine that allows usage of specific critical sections to be +// traced. This is NOT on by default - there are far too many. +// + +#ifdef DEBUG + BOOL WINAPI CritCheckIn(CCritSec * pcCrit); + BOOL WINAPI CritCheckIn(const CCritSec * pcCrit); + BOOL WINAPI CritCheckOut(CCritSec * pcCrit); + BOOL WINAPI CritCheckOut(const CCritSec * pcCrit); + void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace); +#else + #define CritCheckIn(x) TRUE + #define CritCheckOut(x) TRUE + #define DbgLockTrace(pc, fT) +#endif + + +// locks a critical section, and unlocks it automatically +// when the lock goes out of scope +class CAutoLock { + + // make copy constructor and assignment operator inaccessible + + CAutoLock(const CAutoLock &refAutoLock); + CAutoLock &operator=(const CAutoLock &refAutoLock); + +protected: + CCritSec * m_pLock; + +public: + CAutoLock(CCritSec * plock) + { + m_pLock = plock; + m_pLock->Lock(); + }; + + ~CAutoLock() { + m_pLock->Unlock(); + }; +}; + + + +// wrapper for event objects +class CAMEvent +{ + + // make copy constructor and assignment operator inaccessible + + CAMEvent(const CAMEvent &refEvent); + CAMEvent &operator=(const CAMEvent &refEvent); + +protected: + HANDLE m_hEvent; +public: + CAMEvent(BOOL fManualReset = FALSE, __inout_opt HRESULT *phr = NULL); + CAMEvent(__inout_opt HRESULT *phr); + ~CAMEvent(); + + // Cast to HANDLE - we don't support this as an lvalue + operator HANDLE () const { return m_hEvent; }; + + void Set() {EXECUTE_ASSERT(SetEvent(m_hEvent));}; + BOOL Wait(DWORD dwTimeout = INFINITE) { + return (WaitForSingleObject(m_hEvent, dwTimeout) == WAIT_OBJECT_0); + }; + void Reset() { ResetEvent(m_hEvent); }; + BOOL Check() { return Wait(0); }; +}; + + +// wrapper for event objects that do message processing +// This adds ONE method to the CAMEvent object to allow sent +// messages to be processed while waiting + +class CAMMsgEvent : public CAMEvent +{ + +public: + + CAMMsgEvent(__inout_opt HRESULT *phr = NULL); + + // Allow SEND messages to be processed while waiting + BOOL WaitMsg(DWORD dwTimeout = INFINITE); +}; + +// old name supported for the time being +#define CTimeoutEvent CAMEvent + +// support for a worker thread + +#ifdef AM_NOVTABLE +// simple thread class supports creation of worker thread, synchronization +// and communication. Can be derived to simplify parameter passing +class AM_NOVTABLE CAMThread { + + // make copy constructor and assignment operator inaccessible + + CAMThread(const CAMThread &refThread); + CAMThread &operator=(const CAMThread &refThread); + + CAMEvent m_EventSend; + CAMEvent m_EventComplete; + + DWORD m_dwParam; + DWORD m_dwReturnVal; + +protected: + HANDLE m_hThread; + + // thread will run this function on startup + // must be supplied by derived class + virtual DWORD ThreadProc() = 0; + +public: + CAMThread(__inout_opt HRESULT *phr = NULL); + virtual ~CAMThread(); + + CCritSec m_AccessLock; // locks access by client threads + CCritSec m_WorkerLock; // locks access to shared objects + + // thread initially runs this. param is actually 'this'. function + // just gets this and calls ThreadProc + static DWORD WINAPI InitialThreadProc(__inout LPVOID pv); + + // start thread running - error if already running + BOOL Create(); + + // signal the thread, and block for a response + // + DWORD CallWorker(DWORD); + + // accessor thread calls this when done with thread (having told thread + // to exit) + void Close() { + + // Disable warning: Conversion from LONG to PVOID of greater size +#pragma warning(push) +#pragma warning(disable: 4312) + HANDLE hThread = (HANDLE)InterlockedExchangePointer(&m_hThread, 0); +#pragma warning(pop) + + if (hThread) { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + } + }; + + // ThreadExists + // Return TRUE if the thread exists. FALSE otherwise + BOOL ThreadExists(void) const + { + if (m_hThread == 0) { + return FALSE; + } else { + return TRUE; + } + } + + // wait for the next request + DWORD GetRequest(); + + // is there a request? + BOOL CheckRequest(__out_opt DWORD * pParam); + + // reply to the request + void Reply(DWORD); + + // If you want to do WaitForMultipleObjects you'll need to include + // this handle in your wait list or you won't be responsive + HANDLE GetRequestHandle() const { return m_EventSend; }; + + // Find out what the request was + DWORD GetRequestParam() const { return m_dwParam; }; + + // call CoInitializeEx (COINIT_DISABLE_OLE1DDE) if + // available. S_FALSE means it's not available. + static HRESULT CoInitializeHelper(); +}; +#endif // AM_NOVTABLE + + +// CQueue +// +// Implements a simple Queue ADT. The queue contains a finite number of +// objects, access to which is controlled by a semaphore. The semaphore +// is created with an initial count (N). Each time an object is added +// a call to WaitForSingleObject is made on the semaphore's handle. When +// this function returns a slot has been reserved in the queue for the new +// object. If no slots are available the function blocks until one becomes +// available. Each time an object is removed from the queue ReleaseSemaphore +// is called on the semaphore's handle, thus freeing a slot in the queue. +// If no objects are present in the queue the function blocks until an +// object has been added. + +#define DEFAULT_QUEUESIZE 2 + +template class CQueue { +private: + HANDLE hSemPut; // Semaphore controlling queue "putting" + HANDLE hSemGet; // Semaphore controlling queue "getting" + CRITICAL_SECTION CritSect; // Thread seriallization + int nMax; // Max objects allowed in queue + int iNextPut; // Array index of next "PutMsg" + int iNextGet; // Array index of next "GetMsg" + T *QueueObjects; // Array of objects (ptr's to void) + + void Initialize(int n) { + iNextPut = iNextGet = 0; + nMax = n; + InitializeCriticalSection(&CritSect); + hSemPut = CreateSemaphore(NULL, n, n, NULL); + hSemGet = CreateSemaphore(NULL, 0, n, NULL); + QueueObjects = new T[n]; + } + + +public: + CQueue(int n) { + Initialize(n); + } + + CQueue() { + Initialize(DEFAULT_QUEUESIZE); + } + + ~CQueue() { + delete [] QueueObjects; + DeleteCriticalSection(&CritSect); + CloseHandle(hSemPut); + CloseHandle(hSemGet); + } + + T GetQueueObject() { + int iSlot; + T Object; + LONG lPrevious; + + // Wait for someone to put something on our queue, returns straight + // away is there is already an object on the queue. + // + WaitForSingleObject(hSemGet, INFINITE); + + EnterCriticalSection(&CritSect); + iSlot = iNextGet++ % nMax; + Object = QueueObjects[iSlot]; + LeaveCriticalSection(&CritSect); + + // Release anyone waiting to put an object onto our queue as there + // is now space available in the queue. + // + ReleaseSemaphore(hSemPut, 1L, &lPrevious); + return Object; + } + + void PutQueueObject(T Object) { + int iSlot; + LONG lPrevious; + + // Wait for someone to get something from our queue, returns straight + // away is there is already an empty slot on the queue. + // + WaitForSingleObject(hSemPut, INFINITE); + + EnterCriticalSection(&CritSect); + iSlot = iNextPut++ % nMax; + QueueObjects[iSlot] = Object; + LeaveCriticalSection(&CritSect); + + // Release anyone waiting to remove an object from our queue as there + // is now an object available to be removed. + // + ReleaseSemaphore(hSemGet, 1L, &lPrevious); + } +}; + +// Ensures that memory is not read past the length source buffer +// and that memory is not written past the length of the dst buffer +// dst - buffer to copy to +// dst_size - total size of destination buffer +// cb_dst_offset - offset, first byte copied to dst+cb_dst_offset +// src - buffer to copy from +// src_size - total size of source buffer +// cb_src_offset - offset, first byte copied from src+cb_src_offset +// count - number of bytes to copy +// +// Returns: +// S_OK - no error +// E_INVALIDARG - values passed would lead to overrun +HRESULT AMSafeMemMoveOffset( + __in_bcount(dst_size) void * dst, + __in size_t dst_size, + __in DWORD cb_dst_offset, + __in_bcount(src_size) const void * src, + __in size_t src_size, + __in DWORD cb_src_offset, + __in size_t count); + +extern "C" +void * __stdcall memmoveInternal(void *, const void *, size_t); + +inline void * __cdecl memchrInternal(const void *buf, int chr, size_t cnt) +{ +#ifdef _X86_ + void *pRet = NULL; + + _asm { + cld // make sure we get the direction right + mov ecx, cnt // num of bytes to scan + mov edi, buf // pointer byte stream + mov eax, chr // byte to scan for + repne scasb // look for the byte in the byte stream + jnz exit_memchr // Z flag set if byte found + dec edi // scasb always increments edi even when it + // finds the required byte + mov pRet, edi +exit_memchr: + } + return pRet; + +#else + while ( cnt && (*(unsigned char *)buf != (unsigned char)chr) ) { + buf = (unsigned char *)buf + 1; + cnt--; + } + + return(cnt ? (void *)buf : NULL); +#endif +} + +void WINAPI IntToWstr(int i, __out_ecount(12) LPWSTR wstr); + +#define WstrToInt(sz) _wtoi(sz) +#define atoiW(sz) _wtoi(sz) +#define atoiA(sz) atoi(sz) + +// These are available to help managing bitmap VIDEOINFOHEADER media structures + +extern const DWORD bits555[3]; +extern const DWORD bits565[3]; +extern const DWORD bits888[3]; + +// These help convert between VIDEOINFOHEADER and BITMAPINFO structures + +STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader); +STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader); +STDAPI_(WORD) GetBitCount(const GUID *pSubtype); + +// strmbase.lib implements this for compatibility with people who +// managed to link to this directly. we don't want to advertise it. +// +// STDAPI_(/* T */ CHAR *) GetSubtypeName(const GUID *pSubtype); + +STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype); +STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype); + +#ifdef UNICODE +#define GetSubtypeName GetSubtypeNameW +#else +#define GetSubtypeName GetSubtypeNameA +#endif + +STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader); +STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader); + +#ifdef __AMVIDEO__ +STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo); +STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo); +#endif // __AMVIDEO__ + + +// Compares two interfaces and returns TRUE if they are on the same object +BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond); + +// This is for comparing pins +#define EqualPins(pPin1, pPin2) IsEqualObject(pPin1, pPin2) + + +// Arithmetic helper functions + +// Compute (a * b + rnd) / c +LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG rnd); +LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG rnd); + + +// Avoids us dyna-linking to SysAllocString to copy BSTR strings +STDAPI WriteBSTR(__deref_out BSTR * pstrDest, LPCWSTR szSrc); +STDAPI FreeBSTR(__deref_in BSTR* pstr); + +// Return a wide string - allocating memory for it +// Returns: +// S_OK - no error +// E_POINTER - ppszReturn == NULL +// E_OUTOFMEMORY - can't allocate memory for returned string +STDAPI AMGetWideString(LPCWSTR pszString, __deref_out LPWSTR *ppszReturn); + +// Special wait for objects owning windows +DWORD WINAPI WaitDispatchingMessages( + HANDLE hObject, + DWORD dwWait, + HWND hwnd = NULL, + UINT uMsg = 0, + HANDLE hEvent = NULL); + +// HRESULT_FROM_WIN32 converts ERROR_SUCCESS to a success code, but in +// our use of HRESULT_FROM_WIN32, it typically means a function failed +// to call SetLastError(), and we still want a failure code. +// +#define AmHresultFromWin32(x) (MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, x)) + +// call GetLastError and return an HRESULT value that will fail the +// SUCCEEDED() macro. +HRESULT AmGetLastErrorToHResult(void); + +// duplicate of ATL's CComPtr to avoid linker conflicts. + +IUnknown* QzAtlComPtrAssign(__deref_inout_opt IUnknown** pp, __in_opt IUnknown* lp); + +template +class QzCComPtr +{ +public: + typedef T _PtrClass; + QzCComPtr() {p=NULL;} + QzCComPtr(T* lp) + { + if ((p = lp) != NULL) + p->AddRef(); + } + QzCComPtr(const QzCComPtr& lp) + { + if ((p = lp.p) != NULL) + p->AddRef(); + } + ~QzCComPtr() {if (p) p->Release();} + void Release() {if (p) p->Release(); p=NULL;} + operator T*() {return (T*)p;} + T& operator*() {ASSERT(p!=NULL); return *p; } + //The assert on operator& usually indicates a bug. If this is really + //what is needed, however, take the address of the p member explicitly. + T** operator&() { ASSERT(p==NULL); return &p; } + T* operator->() { ASSERT(p!=NULL); return p; } + T* operator=(T* lp){return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp);} + T* operator=(const QzCComPtr& lp) + { + return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp.p); + } +#if _MSC_VER>1020 + bool operator!(){return (p == NULL);} +#else + BOOL operator!(){return (p == NULL) ? TRUE : FALSE;} +#endif + T* p; +}; + +MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, __in LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent ); +bool TimeKillSynchronousFlagAvailable( void ); + +// Helper to replace lstrcpmi +__inline int lstrcmpiLocaleIndependentW(LPCWSTR lpsz1, LPCWSTR lpsz2) +{ + return CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL; +} +__inline int lstrcmpiLocaleIndependentA(LPCSTR lpsz1, LPCSTR lpsz2) +{ + return CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, lpsz1, -1, lpsz2, -1) - CSTR_EQUAL; +} + +#endif /* __WXUTIL__ */ diff --git a/RTSP Source Filter/Common/libs/WinMM.Lib b/RTSP Source Filter/Common/libs/WinMM.Lib new file mode 100644 index 0000000..35f21eb Binary files /dev/null and b/RTSP Source Filter/Common/libs/WinMM.Lib differ diff --git a/RTSP Source Filter/Common/libs/strmbasd.idb b/RTSP Source Filter/Common/libs/strmbasd.idb new file mode 100644 index 0000000..7a6dfeb Binary files /dev/null and b/RTSP Source Filter/Common/libs/strmbasd.idb differ diff --git a/RTSP Source Filter/Common/libs/strmbasd.lib b/RTSP Source Filter/Common/libs/strmbasd.lib new file mode 100644 index 0000000..abc448f Binary files /dev/null and b/RTSP Source Filter/Common/libs/strmbasd.lib differ diff --git a/RTSP Source Filter/Common/libs/strmbase.idb b/RTSP Source Filter/Common/libs/strmbase.idb new file mode 100644 index 0000000..8a122d1 Binary files /dev/null and b/RTSP Source Filter/Common/libs/strmbase.idb differ diff --git a/RTSP Source Filter/Common/libs/strmbase.lib b/RTSP Source Filter/Common/libs/strmbase.lib new file mode 100644 index 0000000..51e3449 Binary files /dev/null and b/RTSP Source Filter/Common/libs/strmbase.lib differ diff --git a/RTSP Source Filter/Common/libs/strmbased.idb b/RTSP Source Filter/Common/libs/strmbased.idb new file mode 100644 index 0000000..e0cde7b Binary files /dev/null and b/RTSP Source Filter/Common/libs/strmbased.idb differ diff --git a/RTSP Source Filter/Common/libs/strmbased.lib b/RTSP Source Filter/Common/libs/strmbased.lib new file mode 100644 index 0000000..4ac0e86 Binary files /dev/null and b/RTSP Source Filter/Common/libs/strmbased.lib differ diff --git a/RTSP Source Filter/HelpLib/ExceptionHelper.cs b/RTSP Source Filter/HelpLib/ExceptionHelper.cs new file mode 100644 index 0000000..7cdfb6e --- /dev/null +++ b/RTSP Source Filter/HelpLib/ExceptionHelper.cs @@ -0,0 +1,571 @@ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Text; +using System.Windows.Forms; +using System.Xml; + +namespace HelpLib +{ + /// + /// Static class to help deal with exceptions. + /// + /// + public class ExceptionHelper + { + public static string usermessageTag = "UserMessage"; + + #region Helpers + + /// + /// We can safely ignore this exception, just log it and continue + /// + /// The exception + public static void Ignore(Exception ex) + { + LogException(ex, string.Empty, false); + } + + /// + /// Ignore with a user message + /// + /// Exception + /// extra information + public static void Ignore(Exception ex, string userMessage) + { + LogException(ex, userMessage, false); + } + + /// + /// Automatically causes an assert and writes the error to the current trace + /// handler. The output includes the exception message and the stack trace. + /// + /// The exception + public static void HandleException(Exception ex) + { + HandleException(ex, string.Empty); + } + + /// + /// Automatically causes an assert and writes the error to the current trace + /// handler. The output includes the exception message and the stack trace. + /// + /// The exception + /// Additional information + public static void HandleException(Exception ex, string userMessage) + { + LogException(ex, userMessage, false); + if (!string.IsNullOrEmpty(userMessage)) + { + ex.Data.Add(usermessageTag, userMessage); + } + //Debug.Assert(false, ex.Message); + } + + /// + /// Similar to the normal HandleException, exception this one + /// creates a dump file. + /// + /// The exception + public static string CriticalException(Exception ex) + { + return CriticalException(ex, string.Empty); + } + + /// + /// Similar to the normal HandleException, exception this one + /// creates a dump file. + /// + /// The exception + /// Additional information + public static string CriticalException(Exception ex, string userMessage) + { + string dumpFile = GetDumpFilePath("CriticalException"); + try + { + } + catch (Exception x) + { + HandleException(x); + } + return dumpFile; + } + + #endregion + + #region GetMessageTextHelpers + + /// + /// Compiles exception into a single string that contains the + /// relevant information + /// + /// Exception + /// string containing the important information + public static string GetMessageText(Exception ex) + { + return GetMessageText(ex, string.Empty); + } + + /// + /// Compiles exception into a single string that contains the + /// relevant information + /// + /// Exception + /// a user supplied message + /// + public static string GetMessageText(Exception ex, string userMessage) + { + string body; + try + { + string totalMem; + string availMem; + GetMemoryStatus(out totalMem, out availMem); + + body = string.Format(txtErrorMessage + , Application.ProductName + , Application.ProductVersion + , DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss") + , SystemInformation.ComputerName + , SystemInformation.UserName + , Environment.OSVersion + , CultureInfo.CurrentCulture.Name + , SystemInformation.PrimaryMonitorSize + , GetSystemUpTime() + , (DateTime.Now - Process.GetCurrentProcess().StartTime).TotalMinutes + , totalMem + , availMem + , userMessage + , GetExceptionDetails(ex) + ); + } + catch (Exception x) + { + body = x.Message; + } + return body; + } + + public static string GetMessageHtml(Exception ex, string userMessage) + { + string body; + try + { + string totalMem; + string availMem; + GetMemoryStatus(out totalMem, out availMem); + + body = string.Format(htmlErrorMessage + , Application.ProductName + , Application.ProductVersion + , DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss") + , SystemInformation.ComputerName + , SystemInformation.UserName + , Environment.OSVersion + , CultureInfo.CurrentCulture.Name + , SystemInformation.PrimaryMonitorSize + , GetSystemUpTime() + , (DateTime.Now - Process.GetCurrentProcess().StartTime).TotalMinutes + , totalMem + , availMem + , userMessage + , GetExceptionDetailsHtml(ex) + ); + } + catch (Exception x) + { + body = x.Message; + } + return body; + } + + private static string GetExceptionDetails(Exception ex) + { + string txt = string.Empty; + int tabLevel = 1; + Exception exception = ex; + + while (exception != null) + { + string errorTxt = + string.Format( + "{0}Message\r\n{0}\t{1}\r\n\r\n{0}Types\r\n{0}\t{2}\r\n\r\n{0}Call Stack\r\n{0}\t{3}\r\n\r\n{0}Target\r\n{0}\t{4}\r\n" + , "\t".PadRight(tabLevel, '\t') + , GetExceptionItem(exception, ExceptionItem.Message, ExceptionTextFormat.TXT, tabLevel) + , GetExceptionItem(exception, ExceptionItem.Type, ExceptionTextFormat.TXT, tabLevel) + , GetExceptionItem(exception, ExceptionItem.StackTrace, ExceptionTextFormat.TXT, tabLevel) + , GetExceptionItem(exception, ExceptionItem.Target, ExceptionTextFormat.TXT, tabLevel)); + errorTxt += "\r\n".PadRight(80, '-') + "\r\n\r\n"; + txt += errorTxt; + tabLevel++; + exception = exception.InnerException; + } + return txt; + } + + private static string GetExceptionDetailsHtml(Exception ex) + { + string txt = string.Empty; + int tabLevel = 1; + Exception exception = ex; + + while (exception != null) + { + string errorTxt = GetExceptionDetailTag(exception, ExceptionItem.Message, tabLevel); + errorTxt += GetExceptionDetailTag(exception, ExceptionItem.Type, tabLevel); + errorTxt += GetExceptionDetailTag(exception, ExceptionItem.StackTrace, tabLevel); + errorTxt += GetExceptionDetailTag(exception, ExceptionItem.Target, tabLevel); + errorTxt += "

"; + txt += errorTxt; + tabLevel++; + exception = exception.InnerException; + } + return txt; + } + + private static string GetExceptionDetailTag(Exception ex, ExceptionItem item, int tabLevel) + { + string tag = string.Empty; + tag += "" + item + "
"; + tag += GetExceptionItem(ex, item, ExceptionTextFormat.HTML, tabLevel) + "
"; + return tag; + } + + private static void GetMemoryStatus(out string totalMem, out string availMem) + { + var memStatus = new MEMORYSTATUSEX(); + if (GlobalMemoryStatusEx(memStatus)) + { + totalMem = memStatus.ullTotalPhys/(1024*1024) + "Mb"; + availMem = memStatus.ullAvailPhys/(1024*1024) + "Mb"; + } + else + { + totalMem = "N/A"; + availMem = "N/A"; + } + } + + private static string GetExceptionItem(Exception ex, ExceptionItem item, ExceptionTextFormat format, + int tabLevel) + { + string itemText = string.Empty; + switch (item) + { + case ExceptionItem.Message: + itemText = ex.Message; + break; + case ExceptionItem.StackTrace: + if (!string.IsNullOrEmpty(ex.StackTrace)) + { + itemText = ex.StackTrace; + //move the source line to the top + const string inTag = " in "; + if (itemText.Contains(inTag)) + { + itemText = itemText.Substring(itemText.LastIndexOf(inTag) + 4) + + "\r\n" + itemText.Substring(0, itemText.LastIndexOf(inTag)); + } + if (format == ExceptionTextFormat.HTML) + itemText = itemText.Replace(" at ", "
at "); + } + break; + case ExceptionItem.Target: + if (ex.TargetSite != null && !string.IsNullOrEmpty(ex.TargetSite.Name)) + itemText = ex.TargetSite.Name; + break; + case ExceptionItem.Type: + itemText = ex.GetType().ToString(); + break; + } + itemText = string.IsNullOrEmpty(itemText) ? "N/A" : itemText; + if (format == ExceptionTextFormat.HTML) + { + itemText = "

  • " + itemText; + itemText.Replace("\r\n", "
    "); + int len = itemText.Split(new[] {"
      "}, StringSplitOptions.None).Length - 1; + for (int i = 0; i < len; i++) + itemText += "
    "; + } + else + { + itemText = "\t" + itemText.Trim(); + itemText = itemText.Replace("\r\n", "\r\n\t\t ".PadRight(tabLevel, '\t')); + } + return itemText; + } + + #endregion + + /// + /// Generic unhandled exception processor + /// This handler automatically creates a dmp file in the + /// application directory + /// + /// + public static void UnhandledExpection(UnhandledExceptionEventArgs e) + { + try + { + TraceHelper.Write(TraceHelper.LogLevel.Fatal, "Unhandled Exception"); + if (e.ExceptionObject is Exception) + { + //to get the full power of exception logging + LogException(e.ExceptionObject as Exception, "Unhandled Exception", true); + } + if (e.IsTerminating && !(e.ExceptionObject is ObjectDisposedException)) + { + MessageBox.Show("A serious error has occurred and this application must exit."); + Application.Exit(); + } + } + catch + { + } + } + + private static string GetDumpFilePath(string header) + { + return Application.CommonAppDataPath + + string.Format(@"\{0}_{1}.dmp", header, DateTime.Now.ToString("s").Replace(":", "_")); + } + + + #region private helpers + + /// + /// Write the exception to the trace log + /// + /// Exception + /// Addition information + /// log level + private static string LogException(Exception ex, string userMessage, bool critical) + { + var sb = new StringBuilder(); + try + { + if (ex is FaultException) + LogFaultException(ex as FaultException); + if (ex is FileNotFoundException) + LogFileNotFoundException(ex as FileNotFoundException); + TraceHelper.LogLevel level = critical ? TraceHelper.LogLevel.Fatal : TraceHelper.LogLevel.Error; + sb.AppendLine("\r\n\r\n----------------- Exception -------------------"); + sb.Append(GetMessageText(ex, userMessage)); + if (critical) + sb.Append(GetModules()); + sb.AppendLine("----------------------------------------------\r\n\r\n"); + TraceHelper.Write(level, sb.ToString()); + } + catch (Exception e) + { + Debug.WriteLine(e.Message); + } + return sb.ToString(); + } + + private static void LogFileNotFoundException(FileNotFoundException fileNotFoundException) + { + TraceHelper.WriteError("File Name: {0}", fileNotFoundException.FileName); + } + + private static void LogFaultException(FaultException ex) + { + if (ex == null) + return; + try + { + TraceHelper.WriteError("Fault Exception"); + TraceHelper.WriteError("Fault Message: {0}", ex.Message); + TraceHelper.WriteError("Fault Action: {0}", ex.Action); + TraceHelper.WriteError("Fault Code: {0}-{1}", ex.Code.Name, ex.Code); + TraceHelper.WriteError("Fault Reason: {0}", ex.Reason); + MessageFault fault = ex.CreateMessageFault(); + if (fault.HasDetail) + { + XmlReader reader = fault.GetReaderAtDetailContents(); + if (reader != null && reader.Name == "ExceptionDetail") + { + var detail = fault.GetDetail(); + if (detail != null) + { + TraceHelper.WriteError("-Detail Message: {0}", detail.Message); + TraceHelper.WriteError("-Detail Stack: {0}", detail.StackTrace); + } + } + } + } + catch (Exception e) + { + TraceHelper.WriteError("Error handling Fault Exception: {0}", e.Message); + } + } + + /// + /// System up time + /// + /// + private static TimeSpan GetSystemUpTime() + { + var ret = new TimeSpan(); + try + { + //This is causing a 30second hang, comment out for now DMH 6/3/10 + //var upTime = new PerformanceCounter("System", "System Up Time"); + //upTime.NextValue(); + //ret= TimeSpan.FromSeconds(upTime.NextValue()); + } + catch (Exception ex) + { + HandleException(ex); + } + return ret; + } + + /// + /// All the loaded modules + /// + /// + public static string GetModules() + { + var modules = new StringBuilder(); + modules.AppendLine("Loaded Modules:"); + Process thisProcess = Process.GetCurrentProcess(); + var list = new SortedList(); + foreach (ProcessModule module in thisProcess.Modules) + { + list.Add(module.FileName, "\t" + module.FileName + " " + module.FileVersionInfo.FileVersion); + } + foreach (string item in list.Values) + modules.AppendLine(item); + return modules.ToString(); + } + + #endregion + + #region interop + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private class MEMORYSTATUSEX + { + public uint dwLength; + public uint dwMemoryLoad; + public ulong ullTotalPhys; + public ulong ullAvailPhys; + public ulong ullTotalPageFile; + public ulong ullAvailPageFile; + public ulong ullTotalVirtual; + public ulong ullAvailVirtual; + public ulong ullAvailExtendedVirtual; + + public MEMORYSTATUSEX() + { + dwLength = (uint) Marshal.SizeOf(typeof (MEMORYSTATUSEX)); + } + } + + #endregion + + #region Nested type: ExceptionItem + + private enum ExceptionItem + { + StackTrace, + Message, + Type, + Target + } + + #endregion + + #region Nested type: ExceptionTextFormat + + private enum ExceptionTextFormat + { + HTML, + TXT + } + + #endregion + + #region HTML_Error_Message + + private static string htmlErrorMessage = "" + + "" + + "" + + "Exception Handler" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
    Application{0}
    Version{1}
    Date{2}
    Computer name{3}
    User name{4}
    OS{5}
    Culture{6}
    Resolution{7}
    System up time{8}
    App up time{9}
    Total memory{10}
    Available memory{11}
    User Message{12}
    " + + "

    " + + "Exception Details" + + "


    " + + "{13}" + + "" + + ""; + + #endregion + + #region TXT_Error_Message + + private static string txtErrorMessage = "" + + "Exception Handler\r\n" + + "\r\n" + + "Application: {0}\r\n" + + "Version: {1}\r\n" + + "Date: {2}\r\n" + + "Computer name: {3}\r\n" + + "User name: {4}\r\n" + + "OS: {5}\r\n" + + "Culture: {6}\r\n" + + "Resolution: {7}\r\n" + + "System up time: {8}\r\n" + + "App up time: {9}\r\n" + + "Total memory: {10}\r\n" + + "Available memory: {11}\r\n" + + "User Message: {12}\r\n" + + "\r\n\r\n" + + "---------------------------------------\r\n" + + "Exception Details\r\n" + + "{13}\r\n"; + + #endregion + + #region Nested type: HRESULT + + public static class HRESULT + { + [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + public static void Check(int hr) + { + Marshal.ThrowExceptionForHR(hr); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/RTSP Source Filter/HelpLib/ExceptionHelper.resx b/RTSP Source Filter/HelpLib/ExceptionHelper.resx new file mode 100644 index 0000000..e4c5317 --- /dev/null +++ b/RTSP Source Filter/HelpLib/ExceptionHelper.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 153, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/RTSP Source Filter/HelpLib/HelpLib.csproj b/RTSP Source Filter/HelpLib/HelpLib.csproj new file mode 100644 index 0000000..b2334ce --- /dev/null +++ b/RTSP Source Filter/HelpLib/HelpLib.csproj @@ -0,0 +1,91 @@ + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {FEA627F9-A115-4EFB-B489-E28991BC8458} + Library + Properties + HelpLib + HelpLib + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG + prompt + 4 + x86 + AllRules.ruleset + Off + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x86 + + + + False + .\log4net.dll + + + + + + + + + + + + + + + + + + + + + + + ExceptionHelper.cs + + + + + \ No newline at end of file diff --git a/RTSP Source Filter/HelpLib/ProgramSettings.cs b/RTSP Source Filter/HelpLib/ProgramSettings.cs new file mode 100644 index 0000000..ad43a57 --- /dev/null +++ b/RTSP Source Filter/HelpLib/ProgramSettings.cs @@ -0,0 +1,259 @@ + +using System; +using System.Collections.Specialized; +using System.Configuration; +using System.IO; +using System.Xml; + +namespace HelpLib +{ + /// + /// Helper class to read and write app.config files + /// + public class ProgramSettings + { + private const string ConfigNode = "Video.Settings"; + private static ProgramSettings _instance; + private readonly NameValueCollection _nvc; + private readonly static object LockObj = new object(); ///Object reference for thread saftey. + + private ProgramSettings() + { + _nvc = new NameValueCollection(); + } + + + private static ProgramSettings GetInstance() + { + try + { + lock (typeof (ProgramSettings)) + { + if (_instance == null) + { + _instance = new ProgramSettings(); + } + } + } + catch (Exception ex) + { + ExceptionHelper.HandleException(ex); + } + return _instance; + } + + /// + /// Get setting from app config + /// + /// setting name + /// setting value + public static string GetSetting(string name) + { + return GetSetting(name, string.Empty); + } + + /// + /// Get setting from custom app config file + /// + /// path to app.config file + /// setting name + /// default value + /// setting value + public static string GetSetting(string configPath, string name, string defaultValue) + { + //Use this code to seperate settings by dll + //ns = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().ReflectedType.Namespace; + //if (ns.Contains(configNode)) + const string ns = ConfigNode; + return GetSetting(configPath, ns, name, defaultValue); + } + + /// + /// Get Setting w default value + /// + /// setting name + /// default value + /// value + public static string GetSetting(string name, string defaultValue) + { + //Use this code to seperate settings by dll + //ns = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().ReflectedType.Namespace; + //if (ns.Contains(configNode)) + const string ns = ConfigNode; + return GetSetting(string.Empty, ns, name, defaultValue); + } + + /// + /// Get setting from custom app.config + /// + /// path to config file + /// section name + /// setting name + /// default value + /// setting value + public static string GetSetting(string cPath, string location, string name, string defaultValue) + { + string ret = string.Empty; + try + { + //first check the cache + string lookup = location + ":" + name; + ProgramSettings ps = GetInstance(); + if (ps._nvc.Get(lookup) != null) + { + lock(LockObj) + ret = ps._nvc.Get(lookup); + } + else + { + lock (LockObj) + { + string configFile = string.Empty; + try + { + var doc = new XmlDocument(); + configFile = LoadConfigFile(cPath, doc); + string nodeName = + string.Format(@"descendant::applicationSettings/{0}/setting[@name='{1}']/value", + location, name); + ret = GetNodeValue(ps, lookup, doc, nodeName, ret); + } + catch (Exception ex) + { + ExceptionHelper.HandleException(ex, string.Format("Can't find config file {0}", configFile)); + throw; + } + + } + } + } + catch (Exception ex) + { + ret = string.Empty; + ExceptionHelper.HandleException(ex); + if (string.IsNullOrEmpty(defaultValue)) + throw; + } + + if (string.IsNullOrEmpty(ret) && !string.IsNullOrEmpty(defaultValue)) + ret = defaultValue; + return ret; + } + + private static string LoadConfigFile(string cPath, XmlDocument doc) + { + string configFile; + if (cPath.Length > 0) + { + configFile = cPath; + } + else if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ConfigPath"])) + { + configFile = ConfigurationManager.AppSettings["ConfigPath"]; + } + else + { + Configuration c = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + configFile = c.FilePath; + } + if(File.Exists(configFile)) + doc.Load(configFile); + return configFile; + } + + private static string GetNodeValue(ProgramSettings ps, string lookup, XmlDocument doc, string nodeName, string ret) + { + XmlNode node = doc.SelectSingleNode(nodeName); + if (node != null) + { + ret = node.InnerText; + ps._nvc.Set(lookup, ret); + } + return ret; + } + + /// + /// Write setting to app config + /// + /// config file path + /// setting name + /// new value + public static void SetSetting(string configPath, string name, string newValue) + { + //Use this code to seperate settings by dll + //ns = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().ReflectedType.Namespace; + //if (ns.Contains(configNode)) + const string ns = ConfigNode; + SetSetting(configPath, ns, name, newValue); + } + + /// + /// Write setting to app config + /// + /// setting name + /// setting value + public static void SetSetting(string name, string newValue) + { + //Use this code to seperate settings by dll + //ns = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().ReflectedType.Namespace; + //if (ns.Contains(configNode)) + const string ns = ConfigNode; + SetSetting(string.Empty, ns, name, newValue); + } + + /// + /// write setting to app config + /// + /// path to config file + /// app section + /// setting + /// new value + public static void SetSetting(string cPath, string location, string name, string newValue) + { + //first check the cache + string lookup = location + ":" + name; + ProgramSettings ps = GetInstance(); + string nodeName = string.Format(@"descendant::applicationSettings/{0}/setting[@name='{1}']/value", + location, name); + + var doc = new XmlDocument(); + string configPath; + if (cPath.Length > 0) + { + configPath = cPath; + } + else + { + Configuration c = + ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + configPath = c.FilePath; + } + doc.Load(configPath); + + XmlNode node = doc.SelectSingleNode(nodeName); + if (node == null) + { + string parent = string.Format(@"descendant::applicationSettings/{0}", location); + XmlNode parentNode = doc.SelectSingleNode(parent); + if (parentNode != null) + { + XmlNode newNode = doc.CreateNode(XmlNodeType.Element, "setting", string.Empty); + XmlAttribute nameAttribute = doc.CreateAttribute("name"); + nameAttribute.Value = name; + if (newNode.Attributes != null) newNode.Attributes.Append(nameAttribute); + node = doc.CreateNode(XmlNodeType.Element, "value", string.Empty); + newNode.AppendChild(node); + parentNode.AppendChild(newNode); + } + } + if (node != null) + { + node.InnerText = newValue; + doc.Save(configPath); + if (ps._nvc.Get(lookup) != null) + ps._nvc.Remove(lookup); + ps._nvc.Set(lookup, newValue); + } + } + } +} \ No newline at end of file diff --git a/RTSP Source Filter/HelpLib/Properties/AssemblyInfo.cs b/RTSP Source Filter/HelpLib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..12a5fea --- /dev/null +++ b/RTSP Source Filter/HelpLib/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("HelpLib")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RTSP")] +[assembly: AssemblyProduct("HelpLib")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("40003D18-374B-4CD6-B551-5AB7663DB830")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/RTSP Source Filter/HelpLib/TraceHelper.cs b/RTSP Source Filter/HelpLib/TraceHelper.cs new file mode 100644 index 0000000..e04288a --- /dev/null +++ b/RTSP Source Filter/HelpLib/TraceHelper.cs @@ -0,0 +1,437 @@ + + +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Xml; +using log4net; +using log4net.Config; +using Microsoft.Win32; + +namespace HelpLib +{ + /// + /// Make life easier when using log4net. This class adds some + /// helper functions that make it easier to use a debug log. + /// You must include the log4net.config in your application + /// directory. Look at the default log4net.config for more + /// information on configuration settings + /// + /// DEBUG - Anything and everything, use this as you wish, inside loops to dump + /// data whatever. Just beware that over logging will slow the system and may + /// hide some potential performance issues. + /// + /// INFO - Should be used to provide useful state information only. This level + /// should filter out all the debug information and provide the a view of what + /// the application is doing. It should not be used in loops or methods that + /// are called frequently. + /// + /// WARNING - Use when something unexpected has happened, but the application + /// continues to operated under normal conditions. This should be the level + /// that the product ships with, so it should provide a pretty accurate glimpse + /// of what is going wrong in the system. + /// + /// ERROR - Typically you should not write ERROR level messages to the logfile, + /// that is the job of the exceptionhelper. However, if you have a failed + /// operation that is not got by exceptionhelper then you would write an ERROR + /// level message. There is no need to do something like + /// Tracehelper.WriteError(exception.Message) let exception helper handle it with + /// ExceptionHelper.HandleException(exception) + /// + /// FATAL - This is an application crash, I only use this in my unhandled + /// exception helper, this is the highest error level and should only be + /// used when the application is crashing, or failed to complete a critical + /// task that my leave the application in an unknown state. + /// + /// + public sealed class TraceHelper + { + private static bool _initialized; + private static int _today; + private static int _tryCount; + + /// + /// default logger for video + /// + private static ILog _defaultLogger; + + private const string DefaultLogFileConfiguration = + "" + " " + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " "+ + "" + + "" + + " " + + " " + + " " + + "" + + "" + + " " + + " " + + "" + + "" + + " " + + " " + + "" + + "" + + " " + + " " + + "" + + "" + + " " + + " " + + "" + + "" + + " " + + "" + + "" + + " " + + " " + + " " + + " " + + " " + + " " + + ""; + + public enum LogLevel + { + /// + /// This is the most verbose level, and is not typically turned on + /// except to trace bugs + /// + Debug, + + /// + /// This is the default trace level, use this to log things that are + /// not excepted events or high priority infomation + /// + Warning, + + /// + /// Exceptions and other errors + /// + Error, + + /// + /// Application crashes, or critical exceptions that the user can not + /// ignore. + /// + Fatal, + + /// + /// General trace level, between warning and + /// debug + /// + Info + }; + + /// + /// walk the call stack until you find the first caller who is + /// not in our namespace. + /// + /// logger + private static ILog GetLogger() + { + if (_defaultLogger == null) + { + _defaultLogger = LogManager.GetLogger("root"); + } + var log = _defaultLogger; + var stackTrace = new StackTrace(); + var frames = stackTrace.GetFrames(); + if (frames != null) + { + foreach (var frame in frames) + { + var method = frame.GetMethod(); + if (GoodMethodCheck(method)) + { + log = LogManager.GetLogger(method.DeclaringType); + break; + } + } + } + return log ?? (_defaultLogger); + } + + private static bool GoodMethodCheck(MethodBase method) + { + bool rc = true; + if (method == null + || method.DeclaringType == null + || method.DeclaringType.FullName == typeof (TraceHelper).FullName) + rc = false; + return rc; + } + + /// + /// Write message to the log4net logger. I attempt to find the class name + /// of the guy who called this function and use that as the logger name + /// + /// + /// level + /// the text + public static void Write(LogLevel level, string message) + { + //add the thread id to all messages + try + { + Initialize(); + if (!IsLogLevelEnabled(level)) + return; + var log = GetLogger(); + switch (level) + { + case LogLevel.Debug: + log.Debug(message); + break; + case LogLevel.Error: + log.Error(message); + break; + case LogLevel.Fatal: + log.Fatal(message); + break; + case LogLevel.Info: + log.Info(message); + break; + case LogLevel.Warning: + log.Warn(message); + break; + default: + Console.WriteLine(message); + break; + } + } + catch (Exception) + { + Console.WriteLine(message); + } + } + + /// + /// This is a bit of a performance enhancer + /// the idea is that is the defualt log does not have + /// the request level enabled then blow out here and + /// save all that down stream processing. + /// + /// requested logging level + /// on or off + private static bool IsLogLevelEnabled(LogLevel level) + { + bool rc = false; + var log = _defaultLogger; + switch (level) + { + case LogLevel.Debug: + rc = log.IsDebugEnabled; + break; + case LogLevel.Error: + rc = log.IsErrorEnabled; + break; + case LogLevel.Fatal: + rc = log.IsFatalEnabled; + break; + case LogLevel.Info: + rc = log.IsInfoEnabled; + break; + case LogLevel.Warning: + rc = log.IsWarnEnabled; + break; + } + return rc; + } + + /// + /// If you are using a custom appender such as RichTextBox, then + /// you must manually call initialize otherwise the log4net subsystem + /// won't be able to find the correct config file... + /// + public static void Initialize() + { + try + { + if (_today != DateTime.Now.DayOfYear) + DeleteOldLogFilesByDate(LogFileName); + if (!_initialized && _tryCount < 3) + { + _tryCount++; + string logFile = LogFileName; + string configFile = "log4net.config"; + _defaultLogger = LogManager.GetLogger("root"); + var fi = new FileInfo(configFile); + if (!fi.Exists) + { + var doc = new XmlDocument(); + doc.LoadXml(DefaultLogFileConfiguration); + GlobalContext.Properties["LogName"] = logFile; + XmlConfigurator.Configure(doc.DocumentElement); + //string logLevel = "Info"; + //try + //{ + // //cant use try parse + // var level=(Level)Enum.Parse(typeof(Level), logLevel, true); + // ((Logger)defaultLogger.Logger).Level = level; + //} + //catch (Exception ex) + //{ + // Console.WriteLine(ex.Message); + //} + } + else + { + GlobalContext.Properties["LogName"] = logFile; + XmlConfigurator.ConfigureAndWatch(fi); + } + if (_defaultLogger.IsFatalEnabled) + _initialized = true; + Console.WriteLine("Initialize" + _initialized); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + + private static void DeleteOldLogFilesByDate(string logFile) + { + try + { + if (!File.Exists(logFile)) + return; + + _today = DateTime.Now.DayOfYear; + int maxSizeRollBackups = 30; + + if (maxSizeRollBackups != 0) + { + var fi = new FileInfo(logFile); + if (fi.Directory == null) + return; + foreach (var oldFile in fi.Directory.GetFiles(fi.Name + "*" + fi.Extension)) + { + if (fi.FullName == oldFile.FullName) + continue; + if (oldFile.LastWriteTime.Date < DateTime.Now.AddDays(-maxSizeRollBackups).Date) + oldFile.Delete(); + } + } + } + catch (Exception ex) + { + WriteError(ex.Message); + } + } + + public static string LogFileName + { + get + { + string path = "."; + try + { + //get the install location of the filter + const string regKey = @"CLSID\{B3F5D418-CDB1-441C-9D6D-2063D5538962}\InprocServer32"; + var registryKey = Registry.ClassesRoot.OpenSubKey(regKey); + var fullName = (string)registryKey.GetValue(""); + path=Path.GetDirectoryName(fullName); + } + catch (Exception ex) + { + ExceptionHelper.HandleException(ex); + } + + if (!Directory.Exists(path)) + path = ".\\Logs"; + + path += "\\Logs"; + + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + return path+@"\"+Process.GetCurrentProcess().ProcessName+".log"; + } + } + + public static void Stop() + { + try + { + foreach (var appender in _defaultLogger.Logger.Repository.GetAppenders()) + appender.Close(); + _initialized = false; + } + catch (Exception ex) + { + WriteError(ex.Message); + } + } + + /// + /// Write a debug message, not in normal log + /// + /// string format + /// string parameters + public static void WriteDebug(string format, params object[] args) + { + Write(LogLevel.Debug, string.Format(format, args)); + } + + /// + /// Write a informational message + /// + /// string format + /// string parameters + public static void WriteInfo(string format, params object[] args) + { + Write(LogLevel.Info, string.Format(format, args)); + } + + /// + /// Write an error message + /// + /// string format + /// string args + public static void WriteError(string format, params object[] args) + { + Write(LogLevel.Error, string.Format(format, args)); + } + + /// + /// Write a warning message + /// + /// string format + /// string args + public static void WriteWarning(string format, params object[] args) + { + Write(LogLevel.Warning, string.Format(format, args)); + } + + internal static void SpecialLog(string message) + { + if (_defaultLogger != null && _initialized) + { + _defaultLogger.Debug(message); + } + } + + } +} \ No newline at end of file diff --git a/RTSP Source Filter/HelpLib/bin/Debug/HelpLib.dll b/RTSP Source Filter/HelpLib/bin/Debug/HelpLib.dll new file mode 100644 index 0000000..d12090a Binary files /dev/null and b/RTSP Source Filter/HelpLib/bin/Debug/HelpLib.dll differ diff --git a/RTSP Source Filter/HelpLib/bin/Debug/HelpLib.pdb b/RTSP Source Filter/HelpLib/bin/Debug/HelpLib.pdb new file mode 100644 index 0000000..be8358e Binary files /dev/null and b/RTSP Source Filter/HelpLib/bin/Debug/HelpLib.pdb differ diff --git a/RTSP Source Filter/HelpLib/bin/Debug/log4net.dll b/RTSP Source Filter/HelpLib/bin/Debug/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/RTSP Source Filter/HelpLib/bin/Debug/log4net.dll differ diff --git a/RTSP Source Filter/HelpLib/bin/Release/HelpLib.dll b/RTSP Source Filter/HelpLib/bin/Release/HelpLib.dll new file mode 100644 index 0000000..b78dbde Binary files /dev/null and b/RTSP Source Filter/HelpLib/bin/Release/HelpLib.dll differ diff --git a/RTSP Source Filter/HelpLib/bin/Release/HelpLib.pdb b/RTSP Source Filter/HelpLib/bin/Release/HelpLib.pdb new file mode 100644 index 0000000..9cce662 Binary files /dev/null and b/RTSP Source Filter/HelpLib/bin/Release/HelpLib.pdb differ diff --git a/RTSP Source Filter/HelpLib/bin/Release/log4net.dll b/RTSP Source Filter/HelpLib/bin/Release/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/RTSP Source Filter/HelpLib/bin/Release/log4net.dll differ diff --git a/RTSP Source Filter/HelpLib/log4net.dll b/RTSP Source Filter/HelpLib/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/RTSP Source Filter/HelpLib/log4net.dll differ diff --git a/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.ExceptionHelper.resources b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.ExceptionHelper.resources new file mode 100644 index 0000000..6c05a97 Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.ExceptionHelper.resources differ diff --git a/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csproj.FileListAbsolute.txt b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..08cb96a --- /dev/null +++ b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csproj.FileListAbsolute.txt @@ -0,0 +1,8 @@ +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\bin\Debug\HelpLib.dll +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\bin\Debug\HelpLib.pdb +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\bin\Debug\log4net.dll +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Debug\HelpLib.csprojResolveAssemblyReference.cache +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Debug\HelpLib.ExceptionHelper.resources +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Debug\HelpLib.csproj.GenerateResource.Cache +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Debug\HelpLib.dll +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Debug\HelpLib.pdb diff --git a/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csproj.GenerateResource.Cache b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csproj.GenerateResource.Cache new file mode 100644 index 0000000..1b9f0d2 Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csproj.GenerateResource.Cache differ diff --git a/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csprojResolveAssemblyReference.cache b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csprojResolveAssemblyReference.cache new file mode 100644 index 0000000..a23fed5 Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.csprojResolveAssemblyReference.cache differ diff --git a/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.dll b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.dll new file mode 100644 index 0000000..d12090a Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.dll differ diff --git a/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.pdb b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.pdb new file mode 100644 index 0000000..be8358e Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Debug/HelpLib.pdb differ diff --git a/RTSP Source Filter/HelpLib/obj/Release/HelpLib.ExceptionHelper.resources b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.ExceptionHelper.resources new file mode 100644 index 0000000..6c05a97 Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.ExceptionHelper.resources differ diff --git a/RTSP Source Filter/HelpLib/obj/Release/HelpLib.csproj.FileListAbsolute.txt b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..32c4326 --- /dev/null +++ b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.csproj.FileListAbsolute.txt @@ -0,0 +1,7 @@ +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\bin\Release\HelpLib.dll +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\bin\Release\HelpLib.pdb +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\bin\Release\log4net.dll +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Release\HelpLib.ExceptionHelper.resources +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Release\HelpLib.csproj.GenerateResource.Cache +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Release\HelpLib.dll +C:\Data\MyProjects\DVS\TechCoastSecLab\RTSP_DSFilter\rtspdsf\RTSP Source Filter\HelpLib\obj\Release\HelpLib.pdb diff --git a/RTSP Source Filter/HelpLib/obj/Release/HelpLib.csproj.GenerateResource.Cache b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.csproj.GenerateResource.Cache new file mode 100644 index 0000000..1b9f0d2 Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.csproj.GenerateResource.Cache differ diff --git a/RTSP Source Filter/HelpLib/obj/Release/HelpLib.dll b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.dll new file mode 100644 index 0000000..b78dbde Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.dll differ diff --git a/RTSP Source Filter/HelpLib/obj/Release/HelpLib.pdb b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.pdb new file mode 100644 index 0000000..9cce662 Binary files /dev/null and b/RTSP Source Filter/HelpLib/obj/Release/HelpLib.pdb differ diff --git a/RTSP Source Filter/PushSource/Logger.cpp b/RTSP Source Filter/PushSource/Logger.cpp new file mode 100644 index 0000000..53d6e96 --- /dev/null +++ b/RTSP Source Filter/PushSource/Logger.cpp @@ -0,0 +1,92 @@ + +#pragma once +#include "stdafx.h" +#include +#include "trace.h" + + +/** +A managed class to help with log4net. +We support 7 levels of logging ranging in levels or verbosity +*/ +ref class Logger +{ +public: + + /** + Writes messages to log file. + Static method that actually calls our tracehelper in the + helper lib. + */ + static void LogMessage(int level, const char * szMsg) + { + //check our logging level to make sure we are interested + if(g_logLevel( &(std::ostringstream() << ::GetCurrentProcessId() << "," << ::GetCurrentThreadId() << "," << functionName << ", " << szMsg) )->str(); + Write(level, logMsg.c_str()); + } + +} diff --git a/RTSP Source Filter/PushSource/Logger.h b/RTSP Source Filter/PushSource/Logger.h new file mode 100644 index 0000000..5a25012 --- /dev/null +++ b/RTSP Source Filter/PushSource/Logger.h @@ -0,0 +1,26 @@ + +#pragma once +#include "stdafx.h" + +#define LOGLEVEL_OFF 0 +#define LOGLEVEL_CRITICAL 1 +#define LOGLEVEL_ERROR 2 +#define LOGLEVEL_WARN 3 +#define LOGLEVEL_INFO 4 +#define LOGLEVEL_VERBOSE 5 +#define LOGLEVEL_DEBUG 6 + +#define TRACE_CRITICAL(x,...) Log(LOGLEVEL_CRITICAL, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_ERROR(x,...) Log(LOGLEVEL_ERROR, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_WARN(x,...) Log(LOGLEVEL_WARN, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_INFO(x,...) Log(LOGLEVEL_INFO, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_VERBOSE(x,...) Log(LOGLEVEL_VERBOSE, __FUNCTION__, x, ##__VA_ARGS__) +#define TRACE_DEBUG(x,...) Log(LOGLEVEL_DEBUG, __FUNCTION__, x, ##__VA_ARGS__) + +extern int g_logLevel; + +extern "C" +{ + _declspec(dllexport) void Write(int level, const char * msg); + _declspec(dllexport) void Log(int level, const char * functionName, const char * lpszFormat, ...); +} diff --git a/RTSP Source Filter/PushSource/PushGuids.h b/RTSP Source Filter/PushSource/PushGuids.h new file mode 100644 index 0000000..93f389a --- /dev/null +++ b/RTSP Source Filter/PushSource/PushGuids.h @@ -0,0 +1,23 @@ + +#pragma once +#include "stdafx.h" + +/** The id we register our filter as +*/ +#define FILTER_GUID "{E196A807-3160-4BE2-8A25-C1089CDC65CA}" +DEFINE_GUID(CLSID_PushSourceRTSP, +0xe196a807, 0x3160, 0x4be2, 0x8a, 0x25, 0xc1, 0x8, 0x9c, 0xdc, 0x65, 0xca); + + + +#ifdef __cplusplus +extern "C" { +#endif + +DEFINE_GUID(nullGuid, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0); + +#ifdef __cplusplus +} +#endif + diff --git a/RTSP Source Filter/PushSource/PushPin.h b/RTSP Source Filter/PushSource/PushPin.h new file mode 100644 index 0000000..e1b48e6 --- /dev/null +++ b/RTSP Source Filter/PushSource/PushPin.h @@ -0,0 +1,130 @@ + + #pragma once +#include "stdafx.h" +#include "PushGuids.h" +#include "TransportUrl.h" +#include "live.h" + +// Filter name strings +#define g_wszPushRTSP L"RTSP Filter" + + +enum VideoState { + NoVideo, + Lost, + Reloading, + Started, + Playing, + }; +/** + * This is the output pin, essentially the base class does all the + * work we just handle the FillBuffer request by passing back to the + * mediabridge + */ +class CPushPinRTSP : public CSourceStream +{ + friend class CPushSourceRTSP; + +private: + CstreamMedia* m_streamMedia; + int m_videoWidth, m_videoHeight; + DWORD m_fourCC; + GUID m_mediaSubType; + int m_bitCount; + REFERENCE_TIME m_framerate; + REFERENCE_TIME m_rtStart; + std::string m_strSessionId; //session id for video + + //More interesting variables + /** + use the low bandwidth stream + */ + bool m_useStream2; + /** + flag to tells us is we have at least gotten one frame in the new session + */ + bool m_firstFrame; + + /** + The DS ref time of the last frame (no relation to frame time) + */ + REFERENCE_TIME m_lastFrame; + + /** + counter used to track the number of frame request that could not + be satisified, when this value get high enough we do a reload attempt + */ + long m_bufferCount; + + /** + The number of seconds to wait before we attempt to reload video + The seconds are converted to frames and checked when we either + have not recieved a frame or keep getting the same frame over and over + Registry Setting: ReloadLostVideoTime + \sa Settings::ReloadLostVideoTime() + */ + long m_reloadLostVideoTime; + + /** + The number seconds that the filter will wait before attempting + to reload the video pipeline if no video is found. This value + is used when we detect the camera is online but we are not recieving + video, typically this means switching from multicast to unicast + \sa Settings::RetryConnectionTime() + */ + long m_retryConnectionTime; + + + /** + After a reload we can see a burst of frames come through. the retryFrameBufferTime is used + to damper that load. We wait the number of seconds in specified before we start checking for + a reload. + Registry Setting: ReloadFrameBufferSeconds + \sa Settings::ReloadFrameBufferSeconds() + */ + long m_reloadFrameBufferTime; + + /** + This counter is used to track the number of frames received from the pipeline + that have the same timestamp, when the LostFrameCount is exceeded we fall into + the reload loop + */ + long m_lostFrameBufferCount; + + /** + To determine if the pipeline is just resending us the same frame + over and over again, we need to check the frame time of the last + frame against the current frame time. The m_lostframecount and + m_lostframebuffercount are used to damper false positives + */ + std::string m_lastFrameTime; + + /** + enum of the current video state + */ + VideoState m_currentVideoState; + +protected: + HRESULT OnThreadCreate(void); + HRESULT OnThreadDestroy(void); + HRESULT OnThreadStartPlay(void); + + CCritSec m_cSharedState; + +public: + DECLARE_IUNKNOWN; + virtual HRESULT Stop(void); + CPushPinRTSP(HRESULT *phr, CSource *pFilter); + ~CPushPinRTSP(); + + //HRESULT CheckMediaType(const CMediaType *pMediaType); + HRESULT GetMediaType(CMediaType *pMediaType); + HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest); + HRESULT FillBuffer(IMediaSample *pSample); + LPVOID get_Filter(){return (LPVOID)(this->m_pFilter);} + +private: + bool ReloadVideo(TransportUrl * url); + bool ProcessVideo(IMediaSample *pSample); + int QueryVideo(TransportUrl * url); +}; diff --git a/RTSP Source Filter/PushSource/PushPinRTSP.cpp b/RTSP Source Filter/PushSource/PushPinRTSP.cpp new file mode 100644 index 0000000..808f2f4 --- /dev/null +++ b/RTSP Source Filter/PushSource/PushPinRTSP.cpp @@ -0,0 +1,509 @@ + +#include "stdafx.h" +#include "PushSource.h" +#include "PushPin.h" +#include "PushGuids.h" +#include "TransportUrl.h" +#include "Settings.h" +#include "live.h" +#include "trace.h" +#include + +#pragma managed +/** + * This is the output pin construction for our source filter + */ +CPushPinRTSP::CPushPinRTSP(HRESULT *phr, CSource *pFilter) + : CSourceStream(NAME("Push Source RTSP"), phr, pFilter, L"Out") +{ + TRACE_INFO( "Pin constructor"); + //create and instance of the view object + m_rtStart=0; + m_bufferCount=0; + m_lostFrameBufferCount=0; + + m_retryConnectionTime=0; + m_reloadLostVideoTime=0; + m_videoWidth=m_videoHeight=m_bitCount=0; + m_reloadFrameBufferTime=0; + m_currentVideoState=NoVideo; + m_framerate=12; + m_streamMedia=NULL; +} + +/** +* Destructor, +*/ +CPushPinRTSP::~CPushPinRTSP() +{ + //stop the pipe/viewer first + Stop(); + + TRACE_INFO( "Shutting down stream"); + if(m_streamMedia!=NULL) + { + m_streamMedia->rtspClientCloseStream(); + delete m_streamMedia; + } + m_streamMedia=NULL; + TRACE_DEBUG( "Pin destructor"); +} + +/** +* Returns the media type that we will be rendering +* back to the base class. This is where we actually +* load the video pipeline, we do it up front so that +* we have the correct image size for the buffer +*/ +HRESULT CPushPinRTSP::GetMediaType(CMediaType *pMediaType) +{ + TRACE_DEBUG( "GetMediaType"); + TransportUrl *url=((CPushSourceRTSP*)this->m_pFilter)->m_transportUrl; + QueryVideo(url); + + CAutoLock cAutoLockShared(&m_cSharedState); + VIDEOINFO *pvi = (VIDEOINFO *)pMediaType->AllocFormatBuffer(sizeof(VIDEOINFO)); + + /* Would this ever really happen? */ + if (NULL == pvi) return S_OK; + + /* Zero out the memory */ + ZeroMemory(pvi, sizeof(VIDEOINFO)); + + /* Plugin in our FOURCC */ + TRACE_INFO( " biCompression = BI_RGB"); + pvi->bmiHeader.biCompression = BI_RGB; + + /* 24 bits per pixel */ + pvi->bmiHeader.biBitCount = 24; + + /* Header size */ + pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + + /* Video width */ + pvi->bmiHeader.biWidth = m_videoWidth; + + /* Video height */ + pvi->bmiHeader.biHeight = -m_videoHeight; + + /* Video Planes */ + pvi->bmiHeader.biPlanes = 1; + + /* The max size a video frame will be */ + pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader); + + /* ?? */ + pvi->bmiHeader.biClrImportant = 0; + + /* This stuff is pretty much ignored in this situation */ + pvi->AvgTimePerFrame = UNITS/30; + + /* Set the Major-type of the media for DShow */ + pMediaType->SetType(&MEDIATYPE_Video); + + /* Set the structure type so DShow knows + * how to read the structure */ + pMediaType->SetFormatType(&FORMAT_VideoInfo); + + /* If our codec uses temporal compression */ + pMediaType->SetTemporalCompression(FALSE); + + GUID subTypeGUID; + subTypeGUID = MEDIASUBTYPE_RGB24; + pMediaType->SetSubtype(&subTypeGUID); + + /* Set the max sample size */ + TRACE_DEBUG( " biSizeImage=%d", pvi->bmiHeader.biSizeImage); + pMediaType->SetSampleSize(pvi->bmiHeader.biSizeImage); + + /* Get our video header to configure */ + VIDEOINFOHEADER *vihOut = (VIDEOINFOHEADER *)pMediaType->Format(); + + /* 30 Fps - Not important */ + vihOut->AvgTimePerFrame = UNITS / 30; + return S_OK; +} + +/** +Setup our video envirnoment. +Using the transport URL setup the environment +this most likely means connecting to a VSM and getting +a security token for the camera. Note this can take some time +to complete, we made to revist. It could be possible to get +the token in a background thread as long as we have the width +and height in the url +*/ +int CPushPinRTSP::QueryVideo(TransportUrl * url) +{ + int ret=-1; + + TRACE_INFO("Query video"); + if(!(m_currentVideoState==VideoState::NoVideo||m_currentVideoState==VideoState::Lost)) + { + TRACE_INFO("Video already configured, exiting"); + return 0; + } + + if(!url->hasUrl()) + { + TRACE_INFO("Missing the URL"); + return ret; + } + + try + { + TRACE_INFO("Try to open the RTSP video stream"); + if(m_streamMedia==NULL) + m_streamMedia=new CstreamMedia(); + + ret = m_streamMedia->rtspClientOpenStream((const char *)url->get_RtspUrl()); + if (ret < 0) + { + TRACE_ERROR( "Unable to open rtsp video stream ret=%d", ret); + return E_FAIL; + } + } + catch(...) + { + TRACE_CRITICAL( "QueryVideo Failed"); + m_currentVideoState=VideoState::NoVideo; + throw false; + } + + //attempt to get the media info from the stream + //we know that in 7.2 this does not work, but we are + //hoping that 7.5 will enable width and height + MediaInfo videoMediaInfo; + try{ + TRACE_INFO("Get Media Info"); + ret= m_streamMedia->rtspClinetGetMediaInfo(CODEC_TYPE_VIDEO, videoMediaInfo); + if(ret < 0) + { + TRACE_CRITICAL( "Unable to get media info from RTSP stream. ret=%d (url=%s)", ret,url->get_Url()); + return VFW_S_NO_MORE_ITEMS; + } + } + catch(...) + { + TRACE_CRITICAL( "QueryVideo Failed from "); + m_currentVideoState=VideoState::NoVideo; + throw false; + } + + TRACE_INFO( "Format: %d",videoMediaInfo.i_format); + TRACE_INFO( "Codec: %s",videoMediaInfo.codecName); + if(videoMediaInfo.video.width>0) + { + TRACE_INFO( "Using video information directly from the stream"); + m_videoWidth = videoMediaInfo.video.width; + m_videoHeight = videoMediaInfo.video.height; + m_bitCount = videoMediaInfo.video.bitrate; + if(videoMediaInfo.video.fps>0) + m_framerate=(REFERENCE_TIME)(10000000/videoMediaInfo.video.fps); + }else{ + TRACE_WARN( "No video info in stream, using defaults from url"); + m_videoWidth = url->get_Width(); + m_videoHeight = url->get_Height(); + //m_videoWidth = 352; + //m_videoHeight = 240; + m_bitCount = 1; + if(url->get_Framerate()>0) + m_framerate=(REFERENCE_TIME)(10000000/url->get_Framerate()); + } + + TRACE_INFO( "Width: %d",m_videoWidth); + TRACE_INFO( "Height: %d",m_videoHeight); + TRACE_INFO( "FPS: %d",10000000/m_framerate); + TRACE_INFO( "Bitrate: %d",m_bitCount); + m_currentVideoState=VideoState::Reloading; + + return ret; +} + + +/** +* DecideBufferSize +* +* This will always be called after the format has been sucessfully +* negotiated. So we have a look at m_mt to see what size image we agreed. +* Then we can ask for buffers of the correct size to contain them. +*/ +HRESULT CPushPinRTSP::DecideBufferSize(IMemAllocator* pMemAlloc, ALLOCATOR_PROPERTIES* pProperties) +{ + /* Thread-saftey */ + TRACE_DEBUG( "DecideBufferSize"); + CAutoLock cAutoLockShared(&m_cSharedState); + + HRESULT hr = S_OK; + VIDEOINFO *pvi = (VIDEOINFO *)m_mt.Format(); + pProperties->cBuffers = 1; + pProperties->cbBuffer =2* pvi->bmiHeader.biSizeImage; + +// pProperties->cBuffers = 20; +// pProperties->cbBuffer = 65535; + + /* Ask the allocator to reserve us some sample memory, NOTE the function + * can succeed (that is return NOERROR) but still not have allocated the + * memory that we requested, so we must check we got whatever we wanted */ + ALLOCATOR_PROPERTIES Actual; + + hr = pMemAlloc->SetProperties(pProperties, &Actual); + if (FAILED(hr)) + { + TRACE_ERROR( "DecideBufferSize pMemAlloc->SetProperties. HRESULT = %#x, SIZE=%d\n", hr, pProperties->cbBuffer); + return hr; + } + + /* Is this allocator unsuitable? */ + if (Actual.cbBuffer < pProperties->cbBuffer) + { + TRACE_ERROR( "DecideBufferSize to small %d < %d\n",Actual.cbBuffer, pProperties->cbBuffer); + return E_FAIL; + } + + TRACE_INFO( "DecideBufferSize SIZE=%d\n", pProperties->cbBuffer); + + TransportUrl *url=((CPushSourceRTSP*)this->m_pFilter)->m_transportUrl; + if(m_currentVideoState!=Playing) + m_streamMedia->rtspClientPlayStream(url->get_RtspUrl()); + m_currentVideoState=Playing; + return S_OK; +} + + + +/** +* This is where we insert the DIB bits into the video stream. +* FillBuffer is called once for every sample in the stream. +* We then pass the buffer to the ProcessVideo to do the actual +* byte copy +* +* If the source checked out ok but fill buffer never gets +* called then the problem is probably a bad media type. +* FillBuffer is called by the directshow thread. +*/ +HRESULT CPushPinRTSP::FillBuffer(IMediaSample *pSample) +{ + bool syncPoint; + HRESULT hr = S_OK; + REFERENCE_TIME rtStop = 0, rtDuration = 0; + bool rc=true; + + CheckPointer(pSample, E_POINTER); + //CAutoLock cAutoLockShared(&CPushPinRTSP); + + try + { + // Copy the DIB bits over into our filter's output buffer. + //This is where the magic happens, call the pipeline to fill our buffer + rc = ProcessVideo(pSample); + + if (rc) + { + // Set the timestamps that will govern playback frame rate. + REFERENCE_TIME rtStop = m_rtStart + m_framerate; + pSample->SetTime(&m_rtStart, &rtStop); + pSample->SetDiscontinuity(FALSE); + m_rtStart=rtStop; + pSample->SetSyncPoint(TRUE); + }else{ + hr=E_FAIL; + } + } + catch(...) + { + TRACE_ERROR( "--Exception---------------------"); + TRACE_ERROR( "FillBuffer..."); + TRACE_ERROR( "--------------------------------"); + + hr=E_FAIL;//This will cause the filter to stop + } + + return hr; +} + +/** +Send our video buffer the live media provider who will copy +the next frame from the queue +*/ +bool CPushPinRTSP::ProcessVideo(IMediaSample *pSample) +{ + bool rc=true; + long cbData; + BYTE *pData; + + + // Access the sample's data buffer + pSample->GetPointer(&pData); + cbData = pSample->GetSize(); + long bufferSize=cbData; + + rc=m_streamMedia->GetFrame(pData, bufferSize); + + if(rc) + { + m_lostFrameBufferCount=0; + m_currentVideoState=Playing; + }else{ + //paint black video to indicate a lose + int count=((CPushSourceRTSP*)this->m_pFilter)->m_transportUrl->get_LostFrameCount(); + if(m_lostFrameBufferCount>count) + { + if(!(m_currentVideoState==VideoState::Lost)) + { + TRACE_INFO("Lost frame count (%d) over limit {%d). Paint Black Frame",m_lostFrameBufferCount, count ); + //HelpLib::TraceHelper::WriteInfo("Lost frame count over limit. Paint Black Frame and shutdown."); + memset(pData,0, bufferSize); + m_currentVideoState=VideoState::Lost; + rc=true; + } else{ + TRACE_INFO("Shutting Down"); + rc=false; + } + + + }else{ + m_lostFrameBufferCount++; + rc = true; + //if(m_currentVideoState==VideoState::Lost) Sleep(1000); + + } + } + return rc; +} + + +/** +Helper function +Attempt to reload lost video. We may lose video for a number of reasons +such as a network outage or power failure. This method will attempt to +reconnect to the video stream. It must reconnect to the same stream as +the orginal connection otherwise the video buffer will not be the correct +size. +*/ +bool CPushPinRTSP::ReloadVideo(TransportUrl * url) +{ + bool rc=true; + int ret = 0; + try + { + TRACE_DEBUG( "Reload video"); + Stop(); + m_lostFrameBufferCount=0; + m_bufferCount=0; + //TransportUrl *url=((CPushSourceRTSP*)this->m_pFilter)->m_transportUrl; + if(m_streamMedia!=NULL) delete m_streamMedia; + m_streamMedia=NULL; + m_streamMedia=new CstreamMedia(); + ret = m_streamMedia->rtspClientOpenStream((const char *)url->get_RtspUrl()); + if(ret!=0) + { + TRACE_ERROR("Unable to open stream ret=%d", ret); + rc=false; + }else{ + ret = m_streamMedia->rtspClientPlayStream(url->get_RtspUrl()); + if(ret!=0) + { + TRACE_ERROR("Unable to open stream ret=%d", ret); + rc=false; + } + } + + /*if we have not received a frame then switch to unicast for the next try + if(!m_firstFrame) + { + TRACE_DEBUG( "Reload using unicast connection"); + } + */ + } + catch(...) + { + TRACE_ERROR( "Reload Video unexpected error"); + rc=false; + } + + return rc; +} + +/** +* Any special work that needs to happen when the playback thread +* is created. +*/ +HRESULT CPushPinRTSP::OnThreadCreate(void) +{ + HRESULT hr=S_OK; + hr=CSourceStream::OnThreadCreate(); + TransportUrl *url=((CPushSourceRTSP*)this->m_pFilter)->m_transportUrl; + + TRACE_INFO("Open Stream"); + if(m_streamMedia==NULL) + m_streamMedia=new CstreamMedia(); + int ret = m_streamMedia->rtspClientOpenStream(url->get_RtspUrl()); + if(ret!=0) + { + TRACE_ERROR("Unable to open stream ret=%d", ret); + hr=E_FAIL; + } + + TRACE_ERROR( "OnThreadCreate. HRESULT = %#x", hr); + return hr; +} +/** +* Cleanup once the playback thread is killed +*/ +HRESULT CPushPinRTSP::OnThreadDestroy(void) +{ + CAutoLock cAutoLockShared(&m_cSharedState); + HRESULT hr=S_OK; +// CSourceStream::Stop(); + hr=CSourceStream::OnThreadDestroy(); + int ret = m_streamMedia->rtspClientCloseStream(); + + if(m_streamMedia!=NULL) + delete m_streamMedia; + m_streamMedia=NULL; + + TRACE_INFO( "OnThreadDestroy. HRESULT = %#x", hr); + return hr; +} + +/** +* FillBuffer is about to get called for the first time +* do any prep work here +*/ +HRESULT CPushPinRTSP::OnThreadStartPlay(void) +{ + HRESULT hr=S_OK; + hr=CSourceStream::OnThreadStartPlay(); + TRACE_INFO("Play Stream"); + TransportUrl *url=((CPushSourceRTSP*)this->m_pFilter)->m_transportUrl; + int ret = m_streamMedia->rtspClientPlayStream(url->get_RtspUrl()); + if(ret!=0) + { + TRACE_ERROR("Unable to play stream ret=%d", ret); + hr=E_FAIL; + } + + TRACE_DEBUG( "OnThreadStartPlay. HRESULT = %#x", hr); + return hr; +} +/** +* Stopping FillBuffer and playback thread +*/ +HRESULT CPushPinRTSP::Stop(void) +{ + CAutoLock cAutoLockShared(&m_cSharedState); + HRESULT hr=S_OK; + TRACE_DEBUG( "Stopping playback"); + if(m_streamMedia!=NULL) + { + int ret = m_streamMedia->rtspClientCloseStream(); + if (ret < 0) + { + TRACE_ERROR( "Unable to close rtsp video stream ret=%d", ret); + return E_FAIL; + } + } + TRACE_INFO( "Stop. HRESULT = %#x", hr); + return hr; +} diff --git a/RTSP Source Filter/PushSource/PushSource.aps b/RTSP Source Filter/PushSource/PushSource.aps new file mode 100644 index 0000000..8f227c2 Binary files /dev/null and b/RTSP Source Filter/PushSource/PushSource.aps differ diff --git a/RTSP Source Filter/PushSource/PushSource.def b/RTSP Source Filter/PushSource/PushSource.def new file mode 100644 index 0000000..a0451ec --- /dev/null +++ b/RTSP Source Filter/PushSource/PushSource.def @@ -0,0 +1,7 @@ +LIBRARY RTSPSource.ax +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + diff --git a/RTSP Source Filter/PushSource/PushSource.dox b/RTSP Source Filter/PushSource/PushSource.dox new file mode 100644 index 0000000..6ebcc64 --- /dev/null +++ b/RTSP Source Filter/PushSource/PushSource.dox @@ -0,0 +1,2352 @@ +# Doxyfile 1.8.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = CiscoFilter + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 1.0.0.0 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Direct Show Source Filter for Cisco VSM 7.x" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = C:/tdrive/Icons/Development/64/source_code_64.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = C:/tdrive/Projects/IBM/Docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- +# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, +# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = "c:\tdrive\Projects\IBM\Cisco Source Filter\PushSource" + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = YES + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = "C:/tdrive/Projects/IBM/Cisco Source Filter/PushSource" + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.as \ + *.js + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = resource.h \ + "T:\Projects\IBM\Cisco Source Filter\PushSource\resource.h" \ + "c:\tdrive\Projects\IBM\Cisco Source Filter\PushSource\resource.h" + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = resource.h + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /