Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Accessing WMF metadata with C#

4.33/5 (9 votes)
21 Jun 20062 min read 5   2.4K  
An article on how to use C# and the Windows Media Format SDK to read metadata in Windows Media Format files.

Introduction

So, you want to write a C# application that can use specific Windows Media Format (WMF) metadata fields? Perhaps you want to list the title and ratings of all of the movie files you have. Fortunately, Microsoft has an SDK for Windows Media Format. However, the WMF SDK is not managed code. But, it very nicely provides a managed wrapper for metadata access. In fact, the SDK contains a complete sample program for editing WMF metadata. So, why not simply use that? Because, in my case, it was overkill for my client's needs. All I needed in this case was the ability to query a WMF file (in particular, a WMV file) to obtain author and copyright information. So, I wrote my own class, which was streamlined to work strictly with string metadata. (It can easily be modified to accommodate other data types, or to access the metadata by index instead of by name.)

Since I had need of something this simple, I thought others might, as well. In addition, this example can serve as a simple introduction to the use of WMF metadata in an application.

Background

First of all, what kind of metadata are we talking about? The WMF metadata fields are one of four data types:

  • QWORD (Quadruple word)
  • DWORD (Double word)
  • BOOL
  • STRING

For instance, here is a list of metadata attributes available in one of my test WMV files:

Index    Name                   Stream Language Type
-----    ------                 ------ -------- ----
  0      Duration                    0    0    QWORD
  1      Bitrate                     0    0    DWORD
  2      Seekable                    0    0     BOOL
  3      Stridable                   0    0     BOOL
  4      Broadcast                   0    0     BOOL
  5      Is_Protected                0    0     BOOL
  6      Is_Trusted                  0    0     BOOL
  7      Signature_Name              0    0   STRING
  8      HasAudio                    0    0     BOOL
  9      HasImage                    0    0     BOOL
 10      HasScript                   0    0     BOOL
 11      HasVideo                    0    0     BOOL
 12      CurrentBitrate              0    0    DWORD
 13      OptimalBitrate              0    0    DWORD
 14      HasAttachedImages           0    0     BOOL
 15      Can_Skip_Backward           0    0     BOOL
 16      Can_Skip_Forward            0    0     BOOL
 17      FileSize                    0    0    QWORD
 18      HasArbitraryDataStream      0    0     BOOL
 19      HasFileTransferStream       0    0     BOOL
 20      WM/ContainerFormat          0    0    DWORD
 21      Title                       0    0   STRING
 22      Author                      0    0   STRING
 23      Copyright                   0    0   STRING
 24      Description                 0    0   STRING
 25      Rating                      0    0   STRING
 26      BannerImageURL              0    0   STRING
 27      CopyrightURL                0    0   STRING
 28      WMFSDKVersion               0    0   STRING
 29      WMFSDKNeeded                0    0   STRING
 30      IsVBR                       0    0     BOOL
 31      WM/AlbumTitle               0    0   STRING
 32      WM/Track                    0    0   STRING
 33      WM/PromotionURL             0    0   STRING
 34      WM/AlbumCoverURL            0    0   STRING
 35      WM/Genre                    0    0   STRING
 36      WM/Year                     0    0   STRING
 37      WM/GenreID                  0    0   STRING
 38      WM/Composer                 0    0   STRING
 39      WM/Lyrics                   0    0   STRING
 40      WM/ToolName                 0    0   STRING
 41      WM/ToolVersion              0    0   STRING
 42      WM/AlbumArtist              0    0   STRING
 43      WM/AuthorURL                0    0   STRING
 44      WM/AudioFileURL             0    0   STRING
 45      WM/Language                 0    0   STRING
 46      WM/ParentalRating           0    0   STRING
 47      WM/BeatsPerMinute           0    0   STRING
 48      WM/InitialKey               0    0   STRING
 49      WM/Mood                     0    0   STRING
 50      WM/DVDID                    0    0   STRING
 51      WM/UniqueFileIdentifier     0    0   STRING
 52      WM/ModifiedBy               0    0   STRING
 53      WM/RadioStationName         0    0   STRING
 54      WM/RadioStationOwner        0    0   STRING
 55      WM/PlaylistDelay            0    0   STRING
 56      WM/Codec                    0    0   STRING
 57      WM/DRM                      0    0   STRING
 58      WM/ISRC                     0    0   STRING
 59      WM/Provider                 0    0   STRING
 60      WM/ProviderRating           0    0   STRING
 61      WM/ProviderStyle            0    0   STRING
 62      WM/ContentDistributor       0    0   STRING
 63      WM/SubscriptionContentID    0    0   STRING
 64      WM/ASFPacketCount           0    0    QWORD
 65      WM/ASFSecurityObjectsSize   0    0    QWORD

As you can see, there can be a lot of attributes available! However, it's important to note that not all files contain the same attribute list. Be sure that the file you're interrogating has the field you are asking for. If it doesn't, the query will return an error: Exception from HRESULT: 0xC00D07F0 or Exception from HRESULT: 0xC00D001D.

Using the code

Since the managed wrapper is Microsoft's code, and part of the WMF SDK, I'm not including it as a download with this article. But you can download the SDK from MSDN. Once you install it, find the "Managed" directory, (for the default installation, it will be: C:\WMSDK\WMFSDK95\samples\managed\). Under that directory, you'll find the wrapper project. The simplest way to use it is to import that project into your Visual Studio solution, then add a reference to that project in your main application project. Now, import my MetaDataReader class file into your main project, and use the GetFieldByName method to retrieve the value of your desired metadata field.

Here's the MetaDataReader class:

C#
using System;
using System.Collections.Generic;
using System.Text;
using WMFSDKWrapper;
//managed wrapper to WMF SDK -
//        provides access to metadata

namespace MyNamespace
{

    /// This class contains the functionality
    /// for handling interaction with the media file
    /// metadata, via the WMF SDK managed wrapper class.
    public class MetaDataReader
    {

        /// Default constructor
        public MetaDataReader()
        {
        }

        /// Method to obtain a metadata attribute by passing in its name. 
        /// Assumes the metadata type is STRING.
        /// Uses the SDK function GetAttributeByName.
        ///
        /// param name="filename" - the filename
        ///            (including path) of media file to interrogate
        /// param name="attrName" - the name of the field we're looking for
        /// returns - the value of the named attribute,
        ///           empty string if not found, or error message
        public string GetFieldByName(string fileName, string attrName)
        {
            try
            {
                //object used to access WMF file 
                IWMMetadataEditor MetadataEditor;
                //object to use access metadata 
                IWMHeaderInfo3 HeaderInfo3;
                //media stream to interrogate
                ushort streamNum = 0;
                //data type of attribute
                WMT_ATTR_DATATYPE wAttribType;
                //value of attribute (as returned by method call)
                byte[] pbAttribValue = null;
                //length of attribute (byte array)
                ushort wAttribValueLen = 0;

                WMFSDKFunctions.WMCreateEditor(out MetadataEditor);

                MetadataEditor.Open(fileName);

                HeaderInfo3 = (IWMHeaderInfo3)MetadataEditor;

                //make call to get attribute length
                HeaderInfo3.GetAttributeByName(ref streamNum, attrName, 
                  out wAttribType, pbAttribValue, ref wAttribValueLen);
                //set byte array length
                pbAttribValue = new byte[wAttribValueLen];
                //make call again, which will get value 
                //into correct-length byte array
                HeaderInfo3.GetAttributeByName(ref streamNum, 
                                               attrName, out wAttribType, 
                                               pbAttribValue, 
                                               ref wAttribValueLen);

                MetadataEditor.Close();

                return ConvertAttrToString(pbAttribValue, 
                                           wAttribValueLen);
            }
            catch (Exception e)
            {
                return "ERROR: " + e.Message;
            }
        }//end method

        /// Method to convert byte array value into string. 
        /// (From the Microsoft WMF SDK sample.)
        ///
        /// param name="pbValue" - byte array value of attribute
        /// param name="dwValueLen" - Length of byte array
        private string ConvertAttrToString(byte[] pbValue, ushort dwValueLen)
        {
            string Value = "";

            if (0 == dwValueLen)
            {
                Value = "";
            }
            else
            {
                if ((0xFE == Convert.ToInt16(pbValue[0])) &&
                     (0xFF == Convert.ToInt16(pbValue[1])))
                {
                    Value = "UTF-16LE BOM+";

                    if (4 <= dwValueLen)
                    {
                        for (int i = 0; i < pbValue.Length - 2; i += 2)
                        {
                            Value += 
                              Convert.ToString(BitConverter.ToChar(pbValue, i));
                        }
                    }
                }
                else if ((0xFF == Convert.ToInt16(pbValue[0])) &&
                          (0xFE == Convert.ToInt16(pbValue[1])))
                {
                    Value = "UTF-16BE BOM+";
                    if (4 <= dwValueLen)
                    {
                        for (int i = 0; i < pbValue.Length - 2; i += 2)
                        {
                            Value += 
                              Convert.ToString(BitConverter.ToChar(pbValue, i));
                        }
                    }
                }
                else
                {
                    Value = "";
                    if (2 <= dwValueLen)
                    {
                        for (int i = 0; i < pbValue.Length - 2; i += 2)
                        {
                            Value += 
                              Convert.ToString(BitConverter.ToChar(pbValue, i));
                        }
                    }
                }
            }//end else not a 0-length string

            return Value;
            
        }//end method

    }//end class
}

Here's a code snippet of a sample call:

C#
MetaDataReader objMetaData = new MetaDataReader();
string Author = objMetaData.GetAttrByName("C:\Videos\MyVideo.wmv", "Author");

That's it! I hope this simple example will provide the basics you need for writing some cool apps that access the WMF metadata.

History

  • 06.20.06 - Initial version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here