Accessing WMF metadata with C#






4.33/5 (9 votes)
Jun 21, 2006
2 min read

165166

2427
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:
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:
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.