|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionIn my previous article (Windows Media Audio Compressor), I've showed you how to create a Windows Media compressor. I used managed C++ to interact with the Windows Media Format (WMF) SDK, which exposes only unmanaged COM interfaces. While this could be an acceptable solution, it is a little complicated to implement, specially if you need more than simple interaction with the WMF objects, data and functions. In this article, I describe a translation of most of the WMF SDK interfaces, data structures, constants and functions into C#. Note: Digital Rights Management (DRM) support is not included in this translation. While there are some classes that can be used without deep knowledge of the WMF SDK, the code accompanying this article assumes that you are already familiar with the WMF SDK. Also, a good knowledge of COM Interop and Interop Marshaling may be needed to use some of the translated interfaces and structures. Some words about the translationSomebody has asked me about ideas of how to translate the When you try to import such a Type Library, you will have many types and functions that not represent, in some cases, the concepts of the managed definition. Some types and functions can be useless or require too much marshaling effort to use it. In such situations, the .NET Framework documentation recommends that after using TLBIMP, you should use ILDASM to obtain a file in the intermediate language, which you can modify and recompile to obtain the desired results. However, in the case of the WMF SDK, we are talking about more than 50 interfaces. I think that the best solution to use the WMF SDK is to declare in managed code all the definitions needed, and that was what I did. There is another solution, tough: wait for Microsoft to release their managed version of the SDK. In this translation, I tried to have as much as possible a managed vision for all definitions and function prototypes in a managed vision, avoiding the use of pointers. However, sometimes this was just not possible. For example, the IDL definition of the method ...
interface IWMHeaderInfo3 : IWMHeaderInfo2
{
...
HRESULT GetAttributeIndices( [in] WORD wStreamNum,
[in] LPCWSTR pwszName,
[in] WORD *pwLangIndex,
[out, size_is( *pwCount )] WORD *pwIndices,
[in, out] WORD *pwCount );
...
};
I translated it as follows: ...
interface IWMHeaderInfo3 : IWMHeaderInfo2
{
...
void GetAttributeIndices( [In] ushort wStreamNum,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszName,
IntPtr pwLangIndex,
[Out, MarshalAs(UnmanagedType.LPArray)]ushort[] pwIndices,
[In, Out] ref ushort pwCount );
...
};
Here, it may be preferable to translate the parameter Whenever you want to call this method and need to pass a null value, simply pass Another important thing about translation is that in the WMF SDK, errors are handled through try
{
m_Reader.GetNextSample(m_OuputStream, out sample, out SampleTime,
out Duration, out Flags, out m_OutputNumber,
out m_OuputStream);
}
catch (COMException e)
{
if (e.ErrorCode == WM.NS_E_NO_MORE_SAMPLES)
{
// No more samples, the stream end have been reached, there is not an error
// Code to handle the end of the stream.
}
else
{
throw (e); //Re-throw, an error that must be catch in an upper level.
}
}
Caution: There may be some errors in this translation, I didn't test all the interfaces and functions. If you plan to use this library for any serious work, you must check carefully every translated piece of code against the WMF SDK headers and documentation. Using the codeI didn't include any demo project in the downloads, instead I'll comment here some simple examples of using the code. As part of the translation, I included a class named WmaStream class:This class is derived from using System;
using System.IO;
using Yeti.MMedia;
using Yeti.WMFSdk;
using WaveLib;
...
using (WmaStream str = new WmaStream("Somefile.wma"))
{
byte[] buffer = new byte[str.SampleSize*2];
AudioWriter writer = new WaveWriter(new FileStream("Somefile.wav",
FileMode.Create),
str.Format);
try
{
int read;
while ( (read = str.Read(buffer, 0, buffer.Length)) > 0)
{
writer.Write(buffer, 0, read);
}
}
finally
{
writer.Close();
}
} //str.Close() is automatically called by Dispose.
As another example, you can download the source code of the article A low-level Audio Player in C# by Ianier Munoz and do the following changes: Add a reference to this library to the cswavplay project. In the file MainForm.cs, look for: ...
[STAThread]
static void Main()
and change it by: ...
//MTA threading is needed is WMF object are planed to be used in
//multithreaded APPs
[MTAThread]
static void Main()
In the same file, look for the method private void OpenFile()
{
//Add this line to open the files that can be processed by the
//WMF sync reader object
OpenDlg.Filter = "Windows Media Files (*.mpe,*.wma, *.asf, *.wmv, *.mp3)
|*.mpe; *.wma;*.asf;*.wmv;*.mp3|All files (*.*)|*.*";
if (OpenDlg.ShowDialog() == DialogResult.OK)
{
CloseFile();
try
{
//WaveLib.WaveStream S = new WaveLib.WaveStream(OpenDlg.FileName);
//Change the previous line by this one:
Yeti.WMFSdk.WmaStream S = new Yeti.WMFSdk.WmaStream(OpenDlg.FileName);
//The rest of the code remains the same
With those few changes, you will have a player capable of playing any ASF file and also MP3 files. One of the lacks of the WmaWriter class:More details about this class can be found in my article Windows Media Audio Compressor. It has almost the same interface, the difference is that now it is implemented with the real WMF interfaces and that I added the possibility to define metadata that will be included in the final ASF stream. The following code demonstrates how to extract the audio information from a Windows Media Video (WMV) file and write it to a Windows Media Audio file using the using System;
using System.IO;
using Yeti.MMedia;
using Yeti.WMFSdk;
using WaveLib;
...
using (WmaStream str = new WmaStream("Somefile.wmv"))
{
byte[] buffer = new byte[str.SampleSize*2];
//Extract the WMF profile from the original file
WMProfile profile = new WMProfile(str.Profile);
while (profile.StreamCount > 1)//Remove any non-audio stream from the profile
{
WMStreamConfig config = new WMStreamConfig(profile.GetStream(0));
if (config.StreamType == MediaTypes.WMMEDIATYPE_Audio)
{
profile.RemoveStream(profile.GetStream(1));
}
else
{
profile.RemoveStream(config.StreamConfig);
}
}
System.Collections.ArrayList List = new System.Collections.ArrayList();
//Check if the original file had a title
string AttrValue = str[WM.g_wszWMTitle];
if (AttrValue != null)
{ //The title will be added to the destination file
WM_Attr Attr = new WM_Attr(WM.g_wszWMTitle,
WMT_ATTR_DATATYPE.WMT_TYPE_STRING,
string.Copy(AttrValue));
List.Add(Attr);
}
//Check by the author metadata
AttrValue = str[WM.g_wszWMAuthor];
if (AttrValue != null)
{ //The actor will be added to the destination file
WM_Attr Attr = new WM_Attr(WM.g_wszWMAuthor,
WMT_ATTR_DATATYPE.WMT_TYPE_STRING,
string.Copy(AttrValue));
List.Add(Attr);
}
AudioWriter writer = new WmaWriter(new FileStream("SomeFile.wma",
FileMode.Create),
str.Format,
profile.Profile,
(WM_Attr[])List.ToArray(typeof(WM_Attr)));
try
{
int read;
while ( (read = str.Read(buffer, 0, buffer.Length)) > 0)
{
writer.Write(buffer, 0, read);
}
}
finally
{
writer.Close();
}
}
In the previous code, I used the helper classes The code There can be many examples of using the WMF. Here, I just showed some simple cases. You can find more information by looking at the implementation of the helper classes and to the comments included in the code. I didn't comment any of the WMF interfaces, structures, enumerations and functions; you can find that information in the WMF SDK documentation. ConclusionHere is an easy solution to use the WMF SDK in managed world (I've already done the dirty work). However, there are two main points you should take into account: forward compatibility and performance. Regarding compatibility, I don't know what are the Microsoft plans about a managed version of the WMF SDK, but I guess that when they provide one, it will have many differences with the current WMF SDK. The other problem is performance: this is not a managed version of WMF SDK, but a translation, so any time you use a method of any WMF interface, there is a COM interop operation involved. The performance penalty could be serious for real-time applications or when handling streams with video content. It is up to you to decide whether this solution suits your needs.
|
||||||||||||||||||||||||||||||