Tag Support API

From BM-productions
Revision as of 22:23, 19 February 2021 by Tdebaets (talk | contribs) (→‎Tags)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The Tag Support API allows developers to write their own tag support for a format that's not supported yet by WMP Tag Plus. This support needs to be implemented in a DLL, which, when it's properly registered, will be loaded and called by WMP Tag Plus. This will in turn allow Windows Media Player to read and write the tags of a new format. The API explained on this page is the interface between WMP Tag Pus and such a tag support DLL.

Of course, you could also read this API contract from the other side, and apply it to write an application or plug-in that calls existing tag support DLLs. In this case, please note that you are not allowed to separately distribute the tag support DLLs included in the WMP Tag Plus package. Contact the author by e-mail for more information.

This API is loosely based on the WMP Tag Support Extender API, but there are a lot of fundamental differences because WMP Tag Support Extender and WMP Tag Plus use totally different techniques to integrate into Windows Media Player.

Various parts of the Tag Support API header file, WMPTagPlusAPI.h, are given and explained here. The full version of this file is included in the WMP Tag Plus SDK, which can be downloaded below. By means of example, this SDK also includes the full source of the MPEG-4 tag support used by WMP Tag Plus.

Before you start working on your own tag support, it is recommended that you notify the author of WMP Tag Plus of this. This is mainly to prevent that two different people are working on tag support for the same format at the same time. You can find the author's e-mail address on the About-tab of WMP Tag Plus' properties, or on the BM-productions website.

Tags

Each supported tag has a corresponding data type. The possible data types are defined in the TAG_DATATYPE enum in WMPTagPlusAPI.h:

  typedef enum _TAG_DATATYPE {
      MT_DATATYPE_STRING,
      MT_DATATYPE_INT,
      MT_DATATYPE_BINARY,
      MT_DATATYPE_REMOVE,
  } TAG_DATATYPE;

Currently, there isn't any tag that has the MT_DATATYPE_BINARY type. This type is reserved for future use.

MT_DATATYPE_REMOVE is a special data type that will only be set when writing tags. It indicates that the tag should be completely removed from the media file.

The following tags are defined in WMPTagPlusAPI.h:

 #define WMPTAG_AUTHOR               0       // MT_DATATYPE_STRING
 #define WMPTAG_AVERAGELEVEL         1       // MT_DATATYPE_INT
 #define WMPTAG_COPYRIGHT            2       // MT_DATATYPE_STRING
 #define WMPTAG_AVERAGEBITRATE       3       // MT_DATATYPE_INT (read-only)
 #define WMPTAG_DURATION             4       // MT_DATATYPE_INT (read-only)
 #define WMPTAG_FILESIZE             5       // MT_DATATYPE_INT (read-only)
 #define WMPTAG_HASAUDIO             6       // MT_DATATYPE_INT (read-only)
 #define WMPTAG_HASVIDEO             7       // MT_DATATYPE_INT (read-only)
 #define WMPTAG_IS_PROTECTED         8       // MT_DATATYPE_INT (read-only)
 #define WMPTAG_PEAKVALUE            9       // MT_DATATYPE_INT
 #define WMPTAG_TITLE                10      // MT_DATATYPE_STRING
 #define WMPTAG_ALBUMARTIST          11      // MT_DATATYPE_STRING
 #define WMPTAG_ALBUMTITLE           12      // MT_DATATYPE_STRING
 #define WMPTAG_CATEGORY             13      // MT_DATATYPE_STRING
 #define WMPTAG_COMPOSER             14      // MT_DATATYPE_STRING
 #define WMPTAG_CONDUCTOR            15      // MT_DATATYPE_STRING
 #define WMPTAG_CONTENTDISTRIBUTOR   16      // MT_DATATYPE_STRING
 #define WMPTAG_CONTENTGROUPDESC     17      // MT_DATATYPE_STRING
 #define WMPTAG_DIRECTOR             18      // MT_DATATYPE_STRING
 #define WMPTAG_ENCODINGTIME         19      // MT_DATATYPE_INT
 #define WMPTAG_GENRE                20      // MT_DATATYPE_STRING
 #define WMPTAG_GENREID              21      // MT_DATATYPE_STRING
 #define WMPTAG_INITIALKEY           22      // MT_DATATYPE_STRING
 #define WMPTAG_LANGUAGE             23      // MT_DATATYPE_STRING
 #define WMPTAG_MOOD                 24      // MT_DATATYPE_STRING
 #define WMPTAG_PARENTALRATING       25      // MT_DATATYPE_STRING
 #define WMPTAG_PARTOFSET            26      // MT_DATATYPE_STRING
 #define WMPTAG_PERIOD               27      // MT_DATATYPE_STRING
 #define WMPTAG_PROVIDER             28      // MT_DATATYPE_STRING
 #define WMPTAG_PROVIDERRATING       29      // MT_DATATYPE_STRING
 #define WMPTAG_PROVIDERSTYLE        30      // MT_DATATYPE_STRING
 #define WMPTAG_PUBLISHER            31      // MT_DATATYPE_STRING
 #define WMPTAG_SHAREDUSERRATING     32      // MT_DATATYPE_INT
 #define WMPTAG_SUBTITLE             33      // MT_DATATYPE_STRING
 #define WMPTAG_TRACKNUMBER          34      // MT_DATATYPE_STRING
 #define WMPTAG_WRITER               35      // MT_DATATYPE_STRING
 #define WMPTAG_YEAR                 36      // MT_DATATYPE_STRING
 #define WMPTAG_LYRICS               37      // MT_DATATYPE_STRING
 #define WMPTAG_DESCRIPTION          38      // MT_DATATYPE_STRING
 #define WMPTAG_ISRC                 39      // MT_DATATYPE_STRING
 #define WMPTAG_ENCODEDBY            40      // MT_DATATYPE_STRING
 #define WMPTAG_MODIFIEDBY           41      // MT_DATATYPE_STRING
 #define WMPTAG_PRODUCER             42      // MT_DATATYPE_STRING
 #define WMPTAG_BPM                  43      // MT_DATATYPE_STRING

The corresponding data type is denoted next to each tag in comments. After having read tags from file, the tags that you pass to WMP Tag Plus with an incorrect data type will be ignored. While writing, you can assume that the tags passed to your tag support by WMP Tag Plus will have the correct data type.

Read-only tags are also indicated in the comments. These can only be passed to WMP Tag Plus while reading, and will never be used while writing.

Each of the tags above corresponds to a predefined attribute in the Windows Media Format SDK. This relation is shown in the static WMPTagNames array:

  static const char* WMPTagNames [] = {
      "Author", "AverageLevel", "Copyright", "CurrentBitrate", "Duration",
      "FileSize", "HasAudio", "HasVideo", "Is_Protected", "PeakValue", "Title",
      "WM/AlbumArtist", "WM/AlbumTitle", "WM/Category", "WM/Composer",
      "WM/Conductor", "WM/ContentDistributor", "WM/ContentGroupDescription",
      "WM/Director", "WM/EncodingTime", "WM/Genre", "WM/GenreID",
      "WM/InitialKey", "WM/Language", "WM/Mood", "WM/ParentalRating",
      "WM/PartOfSet", "WM/Period", "WM/Provider", "WM/ProviderRating",
      "WM/ProviderStyle", "WM/Publisher", "WM/SharedUserRating", "WM/SubTitle",
      "WM/TrackNumber", "WM/Writer", "WM/Year", "WM/Lyrics", "Description",
      "WM/ISRC", "WM/EncodedBy", "WM/ModifiedBy", "WM/Producer",
      "WM/BeatsPerMinute"
  };

If you are not sure about the specific format or meaning of a tag in the Tag Support API, you can refer to the Windows Media Format SDK Attribute List. The information given there will apply to the WMPTAG_ tag as well (with one exception: WMPTAG_GENREID).

While the official WM/GenreID documentation states that the tag can contain an ID3 genre ID in parentheses, followed by a refinement, WMPTAG_GENREID should just contain a ID3 genre ID (without parentheses). WMP Tag Plus processes/generates WMPTAG_GENREID as follows:

  • Reading: after you have read and passed the tag to WMP Tag Plus, the parentheses are automatically added (and the value is passed to Windows Media Player).
  • Writing:
    • When Windows Media Player wants to write WM/GenreID, WMP Tag Plus will parse the value into a WMPTAG_GENRE and WMPTAG_GENREID tag, and pass these to your tag support.
    • When Windows Media Player wants to write WM/Genre, WMP Tag Plus will try to match the genre with an ID3 genre ID, and add a WMPTAG_GENREID tag, on top of a WMPTAG_GENRE tag. Genre ID 12 (Other) will be used if no such match is found.

WMPTAG_FILESIZE is already handled by WMP Tag Plus itself and will be set to the file size of the media file. As a result, you generally don't need to handle this tag in your tag support.

Tag Structures

Each tag that is passed to/from your tag support, together with its value, will be stored in a WMPTP_TAG structure:

  /** memory container for a tag and its value */
  typedef struct _WMPTP_TAG {    
      UINT            tagId;          /** identifies the tag, see WMPTAG_* */
      UINT            tagIndex;       /** index of the tag, for tags with multiple values */
      TAG_DATATYPE    valueDataType;  /** data type of the tag's value */
      union {
          LPWSTR  stringValue;        /** string value of the tag, if ValueDataType == MT_DATATYPE_STRING */
          DWORD64 intValue;           /** integer value of the tag, if ValueDataType == MT_DATATYPE_INT */
          struct {                
              DWORD dataLength;       /** length of binary data */
              BYTE* data;             /** pointer to binary data */
          } binaryValue;              /** binary data value of the tag, if ValueDataType == MT_DATATYPE_BINARY */
      } value;                        /** the tag's value */
  } WMPTP_TAG;
  typedef WMPTP_TAG *PWMPTP_TAG;

The tagIndex member is reserved for future use and should be set to 0. Future WMP Tag Plus versions might use it to support tags containing multiple values.

The tags are grouped together in a WMPTP_TAGLIST structure:

  /** memory container for a list of tags (WMPTP_TAG) */
  typedef struct _WMPTP_TAGLIST { 
      UINT        numTags;            /** number of tags */
      PWMPTP_TAG  items;              /** pointer to the first element of an array of tags */
  } WMPTP_TAGLIST;
  typedef WMPTP_TAGLIST *PWMPTP_TAGLIST;

When reading tags, WMP Tag Plus will allocate an empty WMPTP_TAGLIST structure for your tag support to fill in. However, the WMPTP_TAGLIST.items, WMPTP_TAG.value.stringValue and WMPTP_TAG.value.binaryValue.data members need to be allocated by your tag support. These should be allocated on the process heap, using the HeapAlloc and HeapReAlloc Windows API functions. When WMP Tag Plus has finished processing the tags that were passed to it by your tag support, it will free these members with HeapFree.

To prevent you from having to worry about this allocation, you can use the following helper functions while reading tags. These are defined in WMPTagPlusAPI.h and implemented in WMPTagPlusAPI.cpp:

  void InitTagList(PWMPTP_TAGLIST tags);
  PWMPTP_TAG AddTag(PWMPTP_TAGLIST tags);
  void AddWideStringTag(PWMPTP_TAGLIST tags, UINT wmpTag, const WCHAR* value);
  void AddStringTag(PWMPTP_TAGLIST tags, UINT wmpTag, const char* value);
  void AddIntTag(PWMPTP_TAGLIST tags, UINT wmpTag, DWORD64 value);

Read and Write Functions

Now that the tags themselves and tag structures have been explained, it's time to head to the heart of your tag support: the read and write functions.

First of all, you should give your tag support a name. This name should be the name of the format that you're adding support for, without any spaces or other special characters. A good example is MPEG4. Make sure that this name doesn't conflict with any existing tag support formats, especially with the ones included with WMP Tag Plus. From now on, this tag support name (or format) will be referred to as <TagFormat>.

You are now ready to implement and export the actual read and write functions in your tag support DLL, as follows:

  BOOL WMPTP_API WMPTagPlusRead<TagFormat>Tags(LPCWSTR filename, PWMPTP_TAGLIST tags)
  BOOL WMPTP_API WMPTagPlusWrite<TagFormat>Tags(LPCWSTR filename, PWMPTP_TAGLIST tags)

If the supplied filename can't be opened, or doesn't point to a valid media file of your tag support format, return FALSE. Otherwise, if the tags have been read/written correctly, return TRUE. Returning FALSE is important in case the media file has a different format than the format that your tag support expects. If the extension of the file is registered with other tag support implementation than yours, returning FALSE will cause WMP Tag Plus to try these other implementations as well, until the read/write function of an implementation has returned TRUE. This is to properly support file extensions that can represent more than one format or tag format.

A common implementation of WMPTagPlusRead<TagFormat>Tags opens the specified file and initializes the tags parameter. Next, each tag that is present in the file and supported by the API, is added to tags. Finally, the file is closed.

A common implementation of WMPTagPlusWrite<TagFormat>Tags opens the specified file (for writing), loops through all the tags in the tags parameter, saves each tag to the file (or possibly removes it). Finally, the changes are flushed to the file and it is closed.

The read function is required, but the write function isn't. If the write function isn't exported by your tag support DLL, WMP Tag Plus will still use the DLL for read-only tag support. However, leaving out write support is not recommended.

Capabilities

After implementing the read and write functions, it's recommended that you add a simple capabilities function too. This function allows WMP Tag Plus to query your tag support about some of its specific read and write capabilities.

The capabilities function should have the following prototype:

  UINT WMPTP_API WMPTagPlusGet<TagFormat>Capabilities(void)

The return value of this function should be a combination of the following values, depending on whether your tag support implementation supports reading/writing of the WMPTAG_DESCRIPTION and WMPTAG_LYRICS tags:

 #define WMPTP_CAPABILITIES_READ_DESCRIPTION     0x00000001
 #define WMPTP_CAPABILITIES_WRITE_DESCRIPTION    0x00000002
 #define WMPTP_CAPABILITIES_READ_LYRICS          0x00000004
 #define WMPTP_CAPABILITIES_WRITE_LYRICS         0x00000008

This function's implementation can be one simple return-statement, nothing more is usually required.

WMP Tag Plus or other plug-ins can use the returned value to provide a better user experience. For example, consider a tag editor that has a separate page for editing the lyrics of a song. The tag editor might decide to hide this page if the capabilities function of the used tag support indicates that lyrics editing isn't possible.

Initialization and Cleanup

If you need to do any one-time initialization in your tag support DLL, such as loading dependencies or allocating and initializing global data structures, you can do so in the initialization function (optional):

  BOOL WMPTP_API WMPTagPlusInitialize(void)

This function is guaranteed to be called first by WMP Tag Plus, before any other functions. You can return FALSE, this will cause WMP Tag Plus not to call any more functions of the DLL. Returning FALSE usually means that there was an error while initializing, for example, when a required dependency failed to load. Return TRUE if initialization was successful.

Likewise, you can use the uninitialization function for any cleanup that needs to be done before your DLL is unloaded (optional):

  void WMPTP_API WMPTagPlusUninitialize(void)

Note that the uninitialization function will not be called if the initialization function has returned FALSE.

Both the initialization and uninitialization functions are global and not bound to a single tag support format. The initialization and uninitialization functions of a single DLL implementing tag support for multiple formats will still be called only once.

Registration

For WMP Tag Plus to be able to find and load your tag support DLL, the DLL needs to be registered. This can be done by adding a few specific keys and values to the registry. Usually, the registration will happen during installation of your tag support. There is no user interface that allows users to register or unregister tag support DLLs themselves. They can, however, disable DLLs and edit the list of file extensions that are handled by a DLL.

To register your tag support DLL, create the following subkey:

 HKEY_LOCAL_MACHINE\Software\BM-productions\WMP Tag Plus\Formats\<TagFormat>

Then, create the following entries (values) in this new subkey:

Name Type Value
(Default) REG_SZ The full path to the DLL file that implements your tag support.
Enabled REG_DWORD 1

Next, create the following subkey:

 HKEY_LOCAL_MACHINE\Software\BM-productions\WMP Tag Plus\Formats\<TagFormat>\Extensions

For each file extension that will be handled by your tag support DLL, add a new REG_SZ entry to this Extensions subkey. The name of this entry should be the actual extension, without the preceding period character. The value of the entry itself is ignored and can be left empty.

Testing

During and right after the development of a tag support, you can use the TagSupportTest sample application to test your implementation. This sample is included in the WMP Tag Plus SDK (see below).

When your tag support implementation has become stable enough, you should also test it with WMP Tag Plus itself, inside Windows Media Player. First, add a single media file of the new format to the media library, and check if all tags have been read correctly. Then, edit each tag that can be written by your tag support, and verify that all the changes are written to file.

You should also copy a whole collection of files of the new format (such as a full album) to your music folder in one go. Again, verify that all tags have been read correctly. Finally, test your tag support implementation on a library with at least 1.000 files of the new format.

SDK

The WMP Tag Plus SDK contains the following:

  • The Tag Support API header file, WMPTagPlusAPI.h, together with the accompanying WMPTagPlusAPI.cpp file, in which the helper functions are implemented.
  • The Delphi version of the Tag Support API header: WMPTagPlusAPI.pas.
  • The (unmodified) VC++ source code of the MPEG-4 tag support that is used by WMP Tag Plus. This tag support implementation utilizes the MP4v2 library, which is also included. The code can be used as an example for other tag support implementations.
  • The VC++ source code of the TagSupportTest sample application. This is a console application that loads and calls a specific tag support DLL for outputting all the tags in a media file. This application can help you with testing your own tag support implementation. Be careful when supplying the write or remove switches, as these will respectively alter or remove all the tags of a file.

The MPEG-4 tag support (not including MP4v2) is licensed under the BSD License. All other source code in the WMP Tag Plus SDK is public domain.

Download the WMP Tag Plus SDK (version 1.2)