Alessandro Angeli's Programming FAQ

All source code provided on this web site is distribured according to the following BSD-style license.
However, you are allowed to exclude clause 3 if and only if you redistribute the source or binary code under the GPL version 2.0 (see here) or 3.0 (see here).

Copyright (c) 2010, Alessandro Angeli
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
   must display the following acknowledgement:
   This product includes software developed by Alessandro Angeli.
4. Neither the name of Alessandro Angeli nor the
   names of its contributors may be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY ALESSANDRO ANGELI ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ALESSANDRO ANGELI BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    1. Using ACM to convert between MP3 and PCM

      Explained here.

    2. Parsing MP3 frame headers

      Explained here with source code in C (the source code the article refers to is available here). How to port the tables in the C source code to C# here is explained here.

    3. WMP can play an MP3 file while a DirectShow application can't or viceversa

      WMP handles MP3 files differently as explained here and here. This long and very technical thread explains the internals of ASF reading in WMP7+, which is the same way WMP7+ reads MP3.

    4. WMP can't play an ASF using a third-party codec even if a DirectShow decoder is installed

      This long and very technical thread explains the internals of ASF reading in WMP7+ and why only DMO or VCM/ACM decoders work.

    5. How to read/write structured WindowsMedia file attributes like WM/Picture or WM/Lyrics_Synchronised in .NET

      Explained here with sample code in C# and VB.NET.

    6. Reading GRF files

      The GRF syntax is documented here. Full GRF parser in C here. A GRF file is a COM structured storage file, akso known as docfile, and the structured storage API is documented here.

    7. Saving and loading GRF files

      Saving and loading.

    8. How to manually load a VCM codec's DLL

      Explained here.

    9. How to spy on another application's internal graph

      This long and very technical thread explains how to spy on another application's internal graph using an API hooking mechanism.

    10. How to capture the contents of an overlay surface owned by another application

      Explained here.

    11. How to use shared memory

      The easy way here. The hard but far more flexible way in C is here.

    12. How to use API hooking

      This long and very technical thread explains how to set up an API hooking mechanism.

    13. How to use API hooking without Detours

      Explained here.

    14. How to write a filter to push data synchronously into a graph from memory

      Explained here.

    15. FOURCCs, YUV color formats and YUV-to-RGB conversions

    16. How to display a filter's property pages

      Explained here.

    17. How to programmatically configure a filter through its property pages

      Explained here (including sample C++ code).

    18. Every known method to save/restore a filter's state, including a VCM/ICM/VFW codec wrapped by the AVICompressor filter

      Listed here.

    19. Sample MJPEG push source + Sample MJPEG extractor

      These 2 samples show several things:

      MJPEG push source:
      1. how to output a sequence of JPEG images as an MJPEG video stream
      2. how to use CSource and CSourceStream to easily write a push source filter
      3. how to use CSourceSeeking to implement time-based seeking in a push source filter
      4. how to use an internal filter (that is, a filter that is not packaged in a registered DLL)
      5. how to manually choose a source filter while using IntelligentConnect to render the rest of the graph
      MJPEG extractor:
      1. how to output an MJPEG video stream as sequence of JPEG images (either as generic JPEGs or valid JFIFs - .JPG files should be JFIF files, not generic JPEG ones)
      2. how to use the SampleGrabber in sample callback mode
      3. how to configure the SampleGrabber to accept only a specific media type
      4. how to connect 2 pins using IntelligentConnect
      5. how to let IntelligentConnect choose a source filter without building the whole graph
      6. how to let IntelligentConnect build the rest of a rendering graph
      7. how to remove the clock to let the graph run as fast as possible even when there is a real renderer
      8. how to seek a parser filter instead of the graph to run only a segment even in a transcoding graph
      both:
      1. how to automatically render a filter's output pins
      2. how to implement IAMGraphBuilderCallback to avoid unwanted filters when relying on IntelligentConnect to build a filter chain
      3. how to get a filter's CLSID from the IMoniker returned by the SysDevEnum
      4. how to enumerate a filter's pins
      5. how to use DirectShow in a GUI-less console application
      6. how to asynchronously wait for graph events without blocking the message pump
      7. how to implement functionality similar to the graph's IMediaEvent::WaitForCompletion()

      The solution files are for VisualStudio 2005. VisualStudio 2005 SP1 + Windows SDK 6.x for Vista/Win2008 is recommended. The "mjpeg2jpeg\dxtrans.h" file is required to work around the missing dxtrans.h in the DirectX SDK (versions newer than August 2007), which is required by qedit.h. To complile the samples, you need to put a copy of the BaseClasses's folder in the samples' solution folder. Unless you modify the code,

      The included AviSynth script can be used to generate such a sequence where each frame contains a scrolling frame number (which makes debugging much easier): install AviSynth and VirtualDub, open the AVS in VirtualDub and use {File -> Save image sequence} to save the generated frames (prefix="", suffix=".jpg", digits=3), then convert the sequence to JPEG using your favorite batch image converter (e.g. IrfanView). You can use the AVS to generate the source AVI as well: in GraphEdit, add the DiretShowFilters\AVIWAVFileSource, opening the AVS, then connect the video output to the VideoCompressors\MJPEGCompressor, then to the DiretShowFilters\AVIMux and finally to the DiretShowFilters\FileWriter, saving the AVI, then run the graph.

      How to extract valid JPEGs from an MJPEG stream is explained here.

      Thanks to "Jon E" for pointing out the ERROR_NOT_FOUND vs. ERROR_FILE_NOT_FOUND bug.

    20. Sample ASF/WMV mux filter for DirectShow

      This sample shows several things:

      DirectShow:
      1. how to use CBaseFilter, CBaseInputPin and CBaseOutputPin
      2. how to implement a filter with multiple input pins
      3. how to implement a filter that dynamically creates pins
      4. how to deliver EC_COMPLETE events to the graph manager when handling multiple streams
      5. how to implement IFileSinkFilter[2]
      6. how to implement IStreamBuilder on an output pin
      7. how to implement a muxing filter that uses the IStream interface of the stock FileWriter
      8. how to change WAVE_FORMAT_MPEG into WAVE_FORMAT_MPEGLAYER3 for compatibility with the ACM decoder (required by the WM[Sync]Reader)
      9. how to use an internal filter (that is, a filter that is not packaged in a registered DLL)
      10. how to manually choose a sink filter while using IntelligentConnect to build the rest of the graph
      11. how to connect 2 pins directly (that is, without using IntelligentConnect)
      12. how to remove the clock to let the graph run as fast as possible even when there is a real renderer
      13. how to implement IAMGraphBuilderCallback to avoid unwanted filters when relying on IntelligentConnect to build a filter chain
      14. how to get a filter's CLSID from the IMoniker returned by the SysDevEnum
      15. how to use DirectShow in a GUI-less console application
      16. how to asynchronously wait for graph events without blocking the message pump
      17. how to implement functionality similar to the graph's IMediaEvent::WaitForCompletion()
      WindowsMediaFormat:
      1. how to create a WMProfile object and configure or reconfigure its streams with arbitrary audio/video types
      2. how to write samples through the WMWriter while skipping compression
      3. how to write interleaved samples through the WMWriter without causing it to block because of too much latency
      4. how to implement a custom IWMWriterSink
      5. how to implement a custom INSSBuffer
      6. how to re-use the WMWriterFileSink's sample allocator in a custom IWMWriterSink
      7. how to implement a sample allocator in a custom IWMWriterSink
      8. how to implement a smart sample allocator in a custom IWMWriterSink that caches and recycles INSSBuffer objects
      9. how to add and remove sinks from the WMWriter
      10. how to use the WMIndexer
      generic:
      1. how to implement a generic object cache
      2. how to set a thread's name for the debugger
      3. how to add/remove a graph from the ROT
      4. how to save a graph to a GRF file
      5. how to use multiple inheritance with the BaseClasses

      The solution file is for VisualStudio 2008. VisualStudio 2008 SP1 + Windows SDK 7.0 for Win7 is recommended. To complile the samples, you need to put a copy of the BaseClasses's folder in the samples' solution folder. Unless you modify the code, the test main() accepts a variable number of arguments: the first is the output WMV file and the rest are 1 or more input files. The format of the input files must be supported by DirectShow and the contained audio/video stream must be compatible with AVI (that is, VIDEOINFOHEADER for video and WAVEFORMATEX for audio).

      "babgvant" published a DVR-MS to WMV remuxer based on this code which you can get here.

    21. Sample ASF remuxer that can remux a source ASF into a sequence of smaller ASF files

      This sample shows several things:

      WindowsMediaFormat:
      1. how to copy compressed samples from a WMReader to a WMWriter
      2. how to read samples as fast as possible from a WMReader instead of in real-time
      3. how to cut an ASF at a clean point
      4. how to queue samples to not drop any
      5. how to implement a callback for the WMReader
      6. how to tranform the asynchronous WMReader events into synchronous operations
      generic:
      1. how to implement a generic queue
      2. how to implement a COM object from scratch
      3. how to implement custom COM interfaces
      4. how to wait/sleep while dispatching window messages
      5. how to use a CRITICAL_SECTION and a HEVENT to implement a signalling mechanism

      The solution file is for VisualStudio 2005. VisualStudio 2005 SP1 + Windows SDK 6.x for Vista/Win2008 is recommended. Unless you modify the code, the test main() accepts the following arguments:

      <source> <target> <ext> <first> <digits>

      - source [string] source URL or file
      - target [string] target file base path
      - ext [string] target file ext (including the leading ".")
      - first [int] first target file index
      - digits [int] min number of digits in target file index

      The segments will be named as in printf("%s%0*d%s",target,digits,first,ext).