Windows Explorer Property Page Extension for Portable Executables

After years of procrastinating on this project, I finally managed to complete ‘PEPropPageExt’ – yes, I could have named it better. Since, C++11 has been officially released, the old code in C++98 seemed to do a lot of unnecessary memory copying. Hence, parts of this project has been rewritten to take advantage of the language’s new features. The code has been cleaned, more features have been added and more importantly the code is fault tolerant now.

This project creates a Property page extension (property pages are shown when users right click on a file in Explorer and select ‘Properties’ from the context menu) for Microsoft Portable Executable files – EXE and DLL files. This extension shows various information embedded in binary in these files. These information are valuable for developers who are interested in learning how the compiler has built their application executables.

This build has been given release version number 1.0. The project is still under a freeware license for personal or research usage but is restricted for commercial use. Please refer to this project’s readme page for more details on license agreement and disclaimer.

For PEPropPageExt source


Before I go about bragging to you about features, I feel some people must be credited whose work has been used in this project:

  • udis86, Disassembler Library by Vivek Thampi
  • Simple Layout Manager by Daniel Horn
  • Rich Signature by Daniel Pistelli


This section will discuss few significant property pages.

MS-DOS Header

This page shows you information about header for old MS-DOS loader. It is followed by a 16-bit disassembly of code whose sole purpose is to display a message “This program cannot run in MSDOS.” when the executable is run in MS-DOS only machine.

MSDOS Header Page

MSDOS Header Page

Rich Data dialog

Some executables have ‘Rich’ data stored between their MS-DOS and PE headers. This is known to be done by Visual C++ compilers. If there is an embedded data of this kind, you will see the following dialog.

Rich Data dialog

Rich Data dialog

PE Headers

PE Headers is probably the most important dialog among all others. It shows you flags associated with your executable, which minimum version of Windows is being targeted, data directories etc.

PE Headers Page

PE Headers Page


This page shows you all the modules that are needed and their subsequent symbols for this file. Both static and delayed modules are shown here. Unmangling both Microsoft and GCC C++ style symbol names are supported. Specifically for GCC unmangling though, DLL files ‘LIBSTDC++-6.DLL’ and ‘LIBGCC_S_SEH-1.DLL’ are required in ‘System32′ directory for delay loader to find. These GCC DLL files are distributed with MinGW installations.

Imports Page

Imports Page


This page shows you an overview of how the virtual address of the image will look like when the Windows Loader has finished mapping the file from disk to memory.

Overview Page

Overview Page


This page gives you an address converter, hash verifier and Hex Viewer/Disassembler.

Tools Page

Tools Page

CLR Data

For .NET developers, this page shows you the Common Language Runtime header and its associated data.

CLR Data Page

CLR Data Page


This page shows you information about both native and managed resources. Previewing some types of resources is also be supported. They include icons, bitmaps, string tables, manifest, XML and dialog boxes. Some types of managed resources can also be viewed. If an unknown data format is encountered, it will be shown in hex view.

Resources Page

Resources Page

Frequently Asked Questions

1. How do I install/uninstall this extension?

For installation, first make sure that you have installed Visual C++ 2013 redistributables then copy the DLL files ‘PEPropPageExt.dll’ and ‘ManagedFuncs.dll’ to a convenient location. Open Command Prompt with administrative privileges and navigate to the DLL folder. Enter ‘regsvr32 PEPropPageExt.dll’ to install the product.

To uninstall, enter ‘regsvr32 /u PEPropPageExt.dll’. You may delete the DLL files. NOTE: The ‘ManagedFuncs.dll’ file is loaded by Common Language Runtime and subsequently unloaded by it. A computer restart may be required to unlock this file to delete it.

NOTE: For GNU C++ name unmangling, the DLL files ‘LIBSTDC++-6.DLL’ and ‘LIBGCC_S_SEH-1.DLL’ are needed in the Windows ‘System32′ folder. These files are distributed with MinGW installations.

2. I don’t need all of the tab information, can I hide some of them?

Sure. Navigate to ‘HKCU\Software\SWTBASE\PEPropPageExt\Settings’ and add a new key with the name ‘<SomeThing>’. To hide a specific tab, create a new DWORD value under the key as shown below:

Value Name Description
Hide_AllTabs When Explorer invokes the extension, the extension silently fails. This is not for uninstallation but for temporary disable.
Hide_MSDOSHeaderTab Hides MSDOSHeader page.
Hide_PEHeadersTab Hides PEHeader page.
Hide_SectionsTab Hides Sections page.
Hide_ManifestTab Hides Manifest page.
Hide_ImportsTab Hides Imports page.
Hide_ExportsTab Hides Exports page.
Hide_ResourcesTab Hides Resources page.
Hide_ExceptionTab Hides Exception page.
Hide_BaseRelocTab Hides Base Relocation page.
Hide_DebugTab Hides Debug page.
Hide_LoadConfigTab Hides Load Configuration page.
Hide_TLSTab Hides Thread Local Storage page.
Hide_CLRTab Hides Common Language Runtime page.
Hide_OverviewTab Hides Overview page.
Hide_ToolsTab Hides Tools page.

3. How safe is it to use this against broken or malicious files?

If this extension crashes, the whole ‘Explorer.exe’ parent process crashes. Obviously, this is a nuisance for users. Realizing this, the extension checks to verify that any pointer from the file is within the address space of the executable. The mapped executable’s memory is also marked read-only to prevent execution. There are also checks on values to make sure they are not abnormal.

Unfortunately, not everything is covered. For example, C-String has no size value field. So, checking every byte before reading string would make the extension very slow. This may be tackled in future releases.

Tagged , , , ,

Custom scrollable picture control to replace Windows Static Control in ‘SS_BITMAP’ mode

When Windows Static Control’s ‘SS_BITMAP’ style is set, the control can be used to display ‘HBITMAP’ as a static image. It serves its purpose well. Unfortunately, there are two quirks about this control that made it unusable in one of my projects.

  1. The static picture is unscrollable: If the bitmap given to the control is too big, the control merely crops it. Static window class is made to not allow user interaction, so setting the scrollbars to visible won’t work either (they will be shown but will be disabled).
  2. Bitmap resource disposal is tricky: After you assign a bitmap to the control, you are responsible for destroying both the handle that you gave to the control and an internal copy that the control has maintained. I will not go into detail about this but needless to say – it is a major headache and easy target for resource leaking.

Hence, this control. This control solves the above problems by being scrollable automatically if the given bitmap is too big and taking ownership/responsibility of destruction of the bitmap you pass to the control. In addition, the control is wrapped in a neat C++ class.

The source code is available here and is licensed under public domain.

Picture Control Preview

Picture Control Preview

How to use

  1. Call the static function ‘registerControlWindowClass()’ just once in your application (preferably when it starts).
  2. Call the static function ‘create()’ to create a new control with the specified parent window. Check that you received a valid pointer. If this pointer is NULL, there was an internal error.
  3. Call ‘setPosition()’ and ‘setSize()’ to position and size the control on your window.
  4. Call ‘setBitmapHandle()’ to assign an image. The control will enable scrollbars automatically if the picture is too big. The control owns this handle now so do not delete this bitmap.
  5. If you called ‘registerControlWindowClass()’, especially from a DLL, it is a good idea to call ‘unregisterControlWindowClass()’ to remove the control’s window class from registration when the DLL is unloaded.

Bonus stuff

The control consumes bitmap handle to display an image. You can use Windows Imaging Component to load different image formats and convert it to proper bitmap handle using this (dirty and leaky – which you should fix) test code: WIC_FileToHBitmap.cpp (link with Windowscodecs.lib)

Tagged , , , ,

Writing audio decoder with OSX Core Audio

This article covers details on how to start writing an audio decoder which can be invoked by QuickTime or other client applications. Though writing an audio encoder would have very similar steps, this article specifically covers decoders (well just ’cause I have experience writing them). Unlike QuickTime 7 codecs, you use C++ code which is glued to C interface using boilerplate code provided by Apple. This makes codec development very convenient. But there are some learning curves and pitfalls which any newcomer will encounter – some of which might be unexpected. Hence, this article discusses those ‘gotchas’.

Sample codecs for IMA4 format can be found at: Apple Developer Website

The sample code provided by Apple uses boilerplate code which implements all the C interfaces required for interacting with Component Manager and ACPlugin architecture. Component Manager is a deprecated plugin architecture and so is implemented only for backward compatibility. ACPlugin must be implemented for newer OSX operating systems. The C++ class hierarchy is as follows:

Core Audio Class Hierarchy

Core Audio Class Hierarchy

If you are only writing an encoder or decoder, the ‘Codec’ class can be overridden without having to generate a new derived class from it. On the other hand, if you are implementing them both – two different classes (each for encoder and decoder) will make the design much simple (as shown by the sample code).

Some terminology

  • Codec – Refers to both encoder and decoder.
  • Linear Pulse Coded Modulation (LPCM) – Refers to uncompressed audio data whose amplitude values are linear.
  • AudioStreamBasicDescription (ASBD) – Refers to a C struct which carries information about a format such as channel information, bytes per packet, frames per packet etc.
  • Sample – Refers to one audio data value for one channel.
  • Frame – Refers to one group of samples containing one sample for each channel
  • Packet – In a compressed format, one packet contains many frames but in an uncompressed LPCM, one packet has exactly one frame. In addition, compressed format packet may have header information such as presentation timestamps.
  • Magic Cookie – An opaque data sent by the client. An example of this data may be information about the audio from file container header.

XCode project type

The project type must be a ‘bundle’ type and the final compilation must contain both a compiled .rsrc resource file (for Component Manager) and .plist (For ACPlugin) detailing the codec name, company name, whether its a decoder and/or encoder etc. Look for .r (uncompiled resource file) and .plist files in the Apple’s codec sample.

Functions to implement

The following are the main functions that you will need to implement:

  1. GetPropertyInfo(): This function is called by the client, to query the size of buffer (in bytes) it will need to provide to your codec to read a specific property value. After calling this function, the client will allocate that amount of space and call ‘GetProperty()’ to query that specific property.
  2. GetProperty(): This function allows clients to read a property from your codec. Properties of a codec include information like initialization state, input/output audio formats supported, what output formats are supported for a specific input format, maximum audio packet size for input, number of frames in an output packet, whether frame bit rate is variable etc. If you want to return the default value, call your parent class’ GetProperty() function.
  3. SetProperty(): This function allows clients to set a property. A codec may not support setting a specific property or may reject any attempt to set any property by throwing an unsupported exception.
  4. SetCurrentInputFormat(): This function allows clients to communicate to your codec what audio formats they will be giving to your codec. The format information is provided using ‘AudioStreamBasicDescription’ struct. You may reject this format by returning an error code.
  5. SetCurrentOutputFormat(): This function allows client to communicate what audio format they are expecting out of your codec. For decoders, this is usually Linear Pulse Codec Modulation (LPCM) format. Again, ‘AudioStreamBasicDescription’ struct is used to communicate this information. You may reject this format by returning an error code.
  6. Initialize()/Uninitialize(): This function is called by the client to put your codec in initialized state. This means the client will not alter the input and output format agreed earlier (which were set using SetCurrentInput/OutputFormat() functions). The client can put the codec in uninitialized state again by calling ‘Uninitialize()’. Do not assume that the client will destroy an instance of a class after calling ‘Uninitialize()’. The client is free to call ‘Initialize()’ again. The parameters of this function are input format, output format and a magic cookie (i.e. an opaque data sent by the client like importer), if any. Be advised that the parameters of this functions are pointers and hence are optional. This means in some invocations, you may be provided with input and output format but no magic cookie. Then the client may called ‘Uninitialize()’ and call your ‘Initialize()’ function with NULL pointers for input and output format but with a valid magic cookie pointer. Hence, you are expected to make a copy of and save whatever you get in your class member variables.
  7. AppendInputData(): This function is called by the client to provide an input packet (compressed audio packet in case of a decoder). You are expected to save a copy of this packet in your circular buffer (implement by SimpleCodec class in the sample code). The client may provide more than one packet at a time. How many packets the client has provided is given by ‘ioNumberPackets’ function argument. Hence, you must return how may packets you actually added to your queue using the same ‘ioNumberPackets’ reference argument. Be advised, when all packets have been provided, this function may be called with ‘0’ value for ‘ioNumberPackets’. In this case, return from this function without doing anything and throw ‘kAudioCodecProducePacketsEOF’ in ‘ProduceOutputPackets()’ function. If you specified ‘1’ for ‘kAudioCodecPropertyHasVariablePacketByteSizes’ and ‘kAudioCodecPropertyRequiresPacketDescription’ in ‘GetProperty()’, the client will provide a valid pointer for ‘inPacketDescription’ which points to a list of ‘AudioStreamPacketDescription’ struct. The no of elements of this list will be given by ‘ioNumberPackets’ parameter.
  8. ProduceOutputPackets(): This function is called by the client to ask your codec to process the packet they provided earlier in ‘AppendInputData()’. The ‘ioNumberPackets’ function argument contains the number of packets you are expected to process from your circular buffer. The ‘outOutputData’ pointer parameter specifies a memory location where you are expected to write your output data and the ‘ioOutputDataByteSize’ parameter specifies the amount of space provided in bytes. This space is usually the number of frames per packet your reported in ‘GetProperty()’ function multiplied by output format channel count multiplied by output format sample size. If you are given insufficient space, give the amount of bytes you need in ‘ioOutputDataByteSize’ reference variable and throw an insufficient space exception. If you have successfully processed a packet and written the output data, you must notify your client on how many frames of data were written and how much of the buffer space provided was utilized. The number of frames written must always be equal or less than number of frames per packet you reported in ‘GetProperty()’ and ‘ioNumberOutputSize’ must return number of frames outputted multiplied by output format channel count multiplied by output format sample size. If you have more frames than the maximum value, you can return ‘kAudioCodecProduceOutputPacketSuccessHasMore’ to notify the client that you want to work with the same packet because you have more data to output.
  9. Reset(): This is usually called by the client asking your codec to discard your circular buffer contents and start with an empty buffer.

For a decoder, a QuickTime client may call your decoder class functions in the following order:

  1. Class constructor()
  2. GetPropertyInfo() with kAudioCodecPropertyFormatList
  3. GetProperty() with kAudioCodecPropertyFormatList: Return ‘AudioStreamBasicDescription’ struct for each format supported by you codec
  4. Class destructor()
  5. Class constructor()
  6. GetProperty() with kAudioCodecPropertyNameCFString: Return the name of your codec
  7. Class destructor()
  8. Class constructor()
  9. GetPropertyInfo() with kAudioCodecPropertyOutputFormatsForInputFormat
  10. GetProperty() with kAudioCodecPropertyOutputFormatsForInputFormat: Return a list of ‘AudioStreamBasicDescription’ struct for each output formats supported for a given input format. (NOTE: I could not get my QuickTime 7 client to accept LPCM with planar/non-interleaved audio format. I got ‘AppendInputData()’ but ‘ProduceOutputPackets()’ was never called. If anyone knows why, do let me know in the comments section.)
  11. GetProperty() with kAudioCodecIsInitialized: Return ‘0’ to show that the codec hasn’t been initialized
  12. Initialize() with valid input and output format parameters but NULL for magic cookie pointer
  13. GetProperty() with kAudioCodecIsInitialized: Return ‘1’ to show that the codec has been initialized
  14. GetProperty() with kAudioCodecPropertyInputFormat: Return the currently set input format
  15. GetProperty() with kAudioCodecPropertyOutputFormat: Return the currently set output format
  16. GetProperty() with undocumented property ‘grdy': Pass to lower base class which throws unknown property error
  17. GetProperty() with kAudioCodecPropertyInputBufferSize: Return the maximum size of your circular buffer in bytes
  18. GetProperty() with undocumented property ‘pakx': Pass to lower base class which throws unknown property error
  19. GetProperty() with kAudioCodecPropertyMaximumPacketByteSize: Return the maximum size of input packet you can handle in bytes
  20. GetProperty() with kAudioCodecPropertyPacketFrameSize: Return the number of output frames the input format packet has. If you have variable number of frames per packet, return a maximum number.
  21. GetProperty() with kAudioCodecPropertyMinimumOutputPacket: Return ‘1’ to indicate that you output at least one packet. Passing handling to the class lower than yours will do the same thing
  22. GetProperty() with kAudioCodecIsInitialized: Return ‘1’ to show that the codec has been initialized
  23. GetPropertyInfo() with kAudioCodecPropertyCurrentOutputChannelLayout: Return the sizeof(struct AudioChannelLayout)
  24. GetProperty() with kAudioCodecPropertyCurrentOutputChannelLayout: Fill and return a ‘AudioChannelLayout’ struct specifying how audio channel data is mapped
  25. GetProperty() with kAudioCodecIsInitialized: Return ‘1’ to show that the codec has been initialized
  26. Uninitialize()
  27. Initialize() with NULL input and output format parameters but valid magic cookie pointer and size, if any
  28. Same calls from steps 13 to 21
  29. GetPropertyInfo() with kAudioCodecPropertyPrimeInfo: Return sizeof(struct AudioCodecPrimeInfo)
  30. GetProperty() with kAudioCodecPropertyPrimeInfo: Return any leading and trailing frame information
  31. GetProperty() with kAudioCodecIsInitialized: Return ‘1’ to show that the codec has been initialized
  32. GetPropertyInfo() with kAudioCodecPropertyUsedInputBufferSize: Return 0 at this point because your circular buffer is empty
  33. Reset()
  34. GetProperty() with kAudioCodecIsInitialized: Return ‘1’ to show that the codec has been initialized
  35. GetPropertyInfo() with kAudioCodecPropertyUsedInputBufferSize: Return 0 at this point because your circular buffer is empty
  36. AppendInputData()
  37. ProduceOutputPackets()
  38. Same calls in steps 36 and 37 until all packets are processed

Installing your codec

To install your codec bundle folder, copy it to:

  • For system wide installation: /Library/Audio/Plug-Ins/Components
  • For user specific installation: ~/Library/Audio/Plug-Ins/Components
Tagged , , , ,

cherry: A GPU accelerated slide show daemon for Raspberry Pi

Sometime ago, I wrote a little daemon program for Raspberry Pi to continuously slide show a bunch of images. The project was intended to be commercial – utilizing Raspberry Pi with its powerful GPU and low power requirements and targeted shop owners who have full-HD monitors continuously flipping through a set of image advertisements 24-hours a week.

I have decided to release the project under public domain hence you may use it any way you like. The program uses OpenCV for image loading and hence supports all image formats that OpenCV can load. Slide show blending occurs in GPU using dispmanx APIs hence completely bypasses both the CPU and XWindow server for smooth performance.

Before you try to compile the source, make sure:

  1. OpenCV libraries are installed
  2. Understand that the program is intended to be a daemon
  3. Understand that the program can run without loading any display/window manager

Go to source repository

Load screen for cherry program

Load screen for cherry program

Tagged , , ,

Writing file container reader and decoder for QuickTime 7: FAQ

Having just written a file container reader and decoder for a custom video format for QuickTime 7 (and having to comb through scarce deprecated documentation manuals), it seemed appropriate to write a little FAQ for anyone else looking to do the same. If you are new to QuickTime component programming, skimming through this FAQ should save you days if not weeks. No thanks required, (:

Custom QuickTime component playing a WMV file

Custom QuickTime component playing a WMV file

Should I be writing a QuickTime 7 component?

QuickTime 7 components are plugins for 32-bit QuickTime world. They are intended to extend both QuickTime Player and programs which use QTKit (an interface for embedding QuickTime Player in user applications). Hence, if want QuickTime Player or your program that uses QTKit to play a custom format, you will have to write a plugin component. For newer applications (especially 64-bit ones), Apple recommends that you use AVFoundation instead. This FAQ does not cover AVFoundation.

Why not write QuickTime X component?

Apple hasn’t documented on how to write an QuickTime X component yet. Again, QuickTime X exists exclusively for 64-bit machines and applications.

What is the difference between QuickTime 7 and QuickTime X?

QuickTime 7

  • Always uses 32-bit binaries
  • Is installed by default in both 32-bit and 64-bit machine, but QuickTime 7 Player UI needs to be downloaded manually in 64-bit OSX. It appears in ‘Applications->Utilities’ folder after installation
  • On a 64-bit Mac, QTKit uses QuickTime 7 as a fallback if QuickTime X is unable to play a media format

QuickTime X

  • Always uses 64-bit binaries
  • Both its component and UI are installed by default in 64-bit Mac

How long has Apple promised support for QuickTime 7?

There has been no word on how long the support will last. Even though a lot of APIs used by QuickTime 7 component have been marked deprecated, both Apple as well as large user applications continue to use and prefer QuickTime 7. There are also a number of components written for QuickTime 7 which are not ported to QuickTime X. Looking at this trend, QuickTime 7 should survive some major future OS releases.

What are the different components and which one should I write?

The components have a very confusing nomenclature. So, here’s a list of different components and what they do:

  • Import Component: If you intend to write a file container reader or want to allow users to convert custom format media file to QuickTime movie file (QuickTime’s mov file container format – NOTE: If an Apple documentation states ‘media file’ then its referencing this format) through QuickTime 7 Player, you should write this component.
  • Export Component: If you intend to allow QuickTime to export a QuickTime movie file to a custom format media file, you should write this component.
  • Image Decompressor Component: If you intend to decode video stream frame data passed by Import Component, you should write this component. Do note that this component can also be written for static image decompressors such as JPEG reader.
  • Image Compressor Component: Opposite of what Image Decompressor does.
  • Data Handler Component: If you intend to add capability to read/write media data from a source not traditionally supported by OS, you should write this component. This component need not understand, verify or parse the incoming/outgoing data. A data handler component is tasked by higher level components with reading and writing to a custom data source.
  • Derived Media Handler Component: If you intend to modify media samples after they are just ready to be drawn by Base Media Handler, you should write this component. At this point, you already know the media duration, picture size, format and other information. This is a high level component.

Where are QuickTime 7 components installed and how can I register mine?

For system wide installation: /System/Library/QuickTime
For current user only installation: ~/Library/QuickTime (The directory ‘QuickTime’ may not exist. If so, create it)

Registration occurs automatically when you copy your ‘<ComponentName>.component’ bundle folder to any of the above installation location. No other action is required. Beware that, if your component had some important attributes (discussed later) missing and the registration failed – correcting the attribute in place will not trigger the component to register. Neither issuing a ‘touch’ command on the bundle or restarting your system will force the system to retry registration. The Component Manager system will attempt to register only when you copy a new bundle to those locations. Hence, it’s a good idea to write a little post-build script which removes your previous copy and copies the newly built bundle after the XCode build process finishes to ease testing and avoid potential headaches.

How can I make sure my component has been registered?

There is a program (actually a sample program) called ‘Fiendishthngs‘, provided by Apple, that lists all the components registered in your system. If your component is registered properly, it should appear in the list. Do know that plugin development using component model is deprecated and should not be used for any other application than to extend QuickTime 7.

Can different component, for instance an importer and decoder, be in the same binary/bundle?


How does QuickTime 7 invoke the components when asked to play a custom file format?

QuickTime 7 interacts with registered components by using Component Manager interface. QuickTime, using Component Manager, goes through all the registered components at the installation locations and tries to enumerate all importers which will handle the format by matching file extension.  Information regarding whether a component is an importer or decompressor or any other type is embedded in a compiled resource file (.rsrc files) inside the component bundle. The resource file also has information on what the entry point for the binary is called and what kind of frame data is outputted by the importer. Similarly, after the importer is done, QuickTime enumerates decoder by matching frame data type looking at the decoder’s resource file. If there are several decoders for a type, QuickTime will select the one which best matches the requirement (such as destination surface pixel depth, color types etc).

What does a typical component bundle contain?


|-> Info.plist
|-> /MacOS -> <ComponentName> (executable file)
|-> /Resources -> <ComponentName>.rsrc (compiled resource file)

Where can I find a sample source code for writing a component?

Apple provides ‘ElectricImageComponent‘ as an example. Look into ‘EI_ImageDecompressor’ for an example for a decoder. It also has sample an importer and an exporter. Perian and Xiph are two example open source projects – although they might be little large for newbies.

What does a typical XCode project for a component look like?

Let’s call our decoder ‘TestDecoder’. Its project would look like:


|-> TestDecoderDispatch.h – Contains macros for auto generating boilerplate functions including entry point function and ‘CanDo’ function which calls the functions you implement
|-> TestDecoder.c – Contains implementation for the functions you want to handle
|-> TestDecoder.r – Contains atoms describing what component is implemented and how
|-> Info.plist – Infomation about the bundle such as manufacturer, product name etc

What do these files contain?

  1. TestDecoderDispatch.h – If ‘ComponentResult’ macro is used on a function name, it means you will have to implement it in ‘TestDecoder.c’. Use of ‘ComponentError’ macro means, the auto-generated function will return an error when called by Component Manager. Use of ‘ComponentNoError’ does the opposite. Use of ‘StdComponentCall’ calls the standard function implemented by the macro. Finally, use of ‘ComponentDelegate’ delegates the call to base component’s implementation.
  2. TestDecoder.c – Along with implementation of functions you promised in ‘TestDecoderDispatch.h’ by the use of ‘ComponentResult’ macro, ‘IMAGECODEC_BASENAME’ macro (along with others)  is an important macro defined here. It specifies prefix for implemented functions. For instance, an ASF format importer might set its prefix as ‘ASFImporter’ hence the initialization function (called ‘MovieImportOpen()’ in documentations) should be called ‘ASFImporterOpen()’. Finally, each of the implemented functions have its first parameter as a pointer to a structure that you will have to define. This structure carries context data across the different functions. Do not use global static data storage to communicate among your functions as this will not make them thread safe.
  3. TestDecoder.r – Different type of components use different types of atom structure here to communicate about themselves to the Component Manager. Specifying different atoms allows the Component Manager to see what kind of component is implement, what kind of properties does it support, what kind of file type/mime is it associated with etc. An important function of this resource file is to specify the entry point for a component. You should name your entry point using the same prefix specified using ‘IMAGECODEC_BASENAME’ in TestDecoder.c. For instance, if the prefix specified there was ‘ASFImporter’, then the entry point name should be ‘ASFImporterComponentDispatch’. This file is complied by ‘Rez’ program internally in proxy of XCode and the resultant compiled binary resource file ‘TestDecoder.rsrc’ is produced.

What settings are required for the project?

The only two requirements for a component project are that the project type should be ‘Bundle’ and architecture to build must be set exclusively to ’32-bit’. Frameworks required are ‘QuickTime’ and ‘CoreServices’.

What are the potential pitfall for a component project?

  1. If you are using C++ instead of C, be sure to wrap framework header includes in ‘extern “C”‘. This is to make sure your binary’s entry point function’s name is not mangled by the C++ compiler.
  2. The contents of your ‘<Prefix>Dispatch.h’ must not be guarded using include only once macros/#pragma once.
  3. In a decoder project, when you assign ‘ImageSubCodecDecompressCapabilities *cap->decompressRecordSize’, an empty memory of that size will be available for your use in ‘BeginBand()’, ‘DrawBand()’ and ‘EndBand()’ pointed by ‘ImageSubCodecDecompressRecord *drp->userDecompressRecord’. This memory may be used to store per band/frame context information. You do not need to allocate memory yourself.

How does the importer pass data to the decoder?

In your ‘MovieImportDataRef()’ (or just ‘MovieImportFile()’ if you only support importing from files), create a media track and add samples to it using ‘AddMediaSampleReference()’ (or ‘AddMediaSample2()’ if your frame data is in memory) specifying the file offset and size for each frame data and passing the media track back to the caller using ‘usedTrack’ pointer. A ‘ImageDescription’ structure is also necessary which is attached with the media to carry information like frame width, height etc across to the decoder. If you need to carry extra custom data to the decoder, ‘AddImageDescriptionExtension()’ function can be used to append information to this structure. On the other end, QuickTime will read data from the file using the provided offset and size on to the memory and provide data for each frame to the decoder one by one.

Where is the codec data pointer and draw surface destination pointers on the decoder side?

You can access the codec data passed by the importer using ‘ImageSubCodecDecompressRecord * drp->codecData’ pointer in ‘BeginBand()’ and ‘DrawBand()’ parameter list. The size of this codec data is available only at ‘BeginBand()’ in ‘CodecDecompressParams *p->bufferSize’. To use this value in ‘DrawBand()’ carry it across using ‘ImageSubCodecDecompressRecord *drp->userDecompressRecord’. The surface to draw on is pointed to by ‘ImageSubCodecDecompressRecord *drp->baseAddr’.

I have written an importer but my importer takes a lot of time adding frame data so the player UI takes sometime to appear. How can I remedy this?

Usually the decoder is only invoked after the importer is done adding sample references. This is the cause of the delay. To avoid it, in your ‘MovieImportDataRef()’, set the ‘movieImportResultNeedIdles’ flag for ‘outFlags’. You will have to add a placeholder track until the actual frame data is imported. This is to allow QuickTime to calculate duration of your media. You should also implement ‘MovieImportSetIdleManager()’ to save the handle of an idle manager given by the system. An Idle Manager is used to get idle time from QuickTime for doing your work. The system will periodically call your ‘MovieImportIdle()’ function if the player is doing nothing – this is when you can do your importing. Finally, implement ‘MovieImportGetMaxLoadedTime()’ to tell QuickTime how much of the media is loaded.

Tagged , , , , ,

Get every new post delivered to your Inbox.

Join 83 other followers