English version Versión en español Version française
 


Code samples related with the article: C Sharp Windows Media Format SDK Translation

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

Response to message: Re: Great stuff - Can you help me...
I got it working. Thanks.

All I need now is to be able to convert the mp3 to asf

Pete

Papapet


The following code create a 128 Kbps WMA file from your a MP3 file and if the bit rate of the MP3 file is greater than 128 Kbps then a recompressed 128 Kbps MP3 file is created at the same time.

using (WmaStream str = new WmaStream("File.mp3", 
                                     new WaveFormat(44100, 16, 2))
{
  byte[] buffer = new byte[str.SampleSize*2];
  IWMProfile profile = null;
  //See System Profiles in the WMF SDK documentation for more detail
  Guid WMProfile_V80_128StereoAudio = new Guid("{407B9450-8BDC-4ee5-88B8-6F527BD941F2}");
  WM.ProfileManager.LoadProfileByID(ref WMProfile_V80_128StereoAudio, out profile);
	
  AudioWriter[] writers = new AudioWriter[2];
  try
  {
    writers[0] = new WmaWriter(new FileStream("File.wma", FileMode.Create),
                                               str.Format,
                                               profile);
    if ( OrigBitRate > 128000 )
    {
      writers[1] = new Mp3Writer(new FileStream("File-128.mp3", FileMode.Create),
                                                str.Format,
                                                new Yeti.Lame.BE_CONFIG(str.Format, 128));
    }
    int read;
    while ( (read = str.Read(buffer, 0, buffer.Length)) > 0)
    {
      foreach( AudioWriter writer in writers )
      {
        if (writer != null)
          writer.Write(buffer, 0, read);
      }
    }
  }
  catch (Exception e)
  {
    //Handle Exceptions
  }
  finally
  {
    foreach( AudioWriter writer in writers )
    {
      if (writer != null)
      {
        try
        {
          writer.Close();
        }
        catch
        {
        }
      }
    }
  }
}
Response to message: Capture frame from ASF file and store to disk as .jpg
Hi, great article, and probably the closest I've come to finding a solution to a problem that I have:

By using Windows Media Format SDK (or Windows Media Encoder SDK, if it's possible) and C# I want to simply load an ASF file (.asf/.wmv/.wma.... ), grab a frame from a specified time ( hh:mm:ss ) into the file and save that frame to disk as a .jpg file. How can this be done - can you help ? Preferably I want the code to be as simple as possible, a bare bones sample...

I've tried some quick tests with your code, with no luck, and I'm not good enough with the format SDK to know how this should be done by extending your wrapper classes... hmm Any help would greatly be appreciated !

Regards,

Kurt Grønbech
Software Architect


The following function shows how take a snapshot image from a video ASF file at specific time position:
/// <summary>
/// Write an image from an ASF video file to a jpeg file.
/// </summary>
/// <param name="AsfVideoFileName">Video file name</param>
/// <param name="ImageTime">Time position in 100-nanosecond units of the image to capture</param>
/// <param name="JpgFileName">File name of resulting image</param>
public static void WMVImageToJpg(string AsfVideoFileName, ulong ImageTime, string JpgFileName)
{
  IWMSyncReader Reader;
  uint VideoOuput = uint.MaxValue;
  uint VideoStream = uint.MaxValue;
  uint OutputCount;
  WM_MEDIA_TYPE mtype = new WM_MEDIA_TYPE();
  WMVIDEOINFOHEADER InfoHeader = new WMVIDEOINFOHEADER();
      
  Reader = WM.CreateSyncReader(WMT_RIGHTS.WMT_RIGHT_NO_DRM);
  Reader.Open(AsfVideoFileName);
  Reader.GetOutputCount(out OutputCount);
  ushort[] StreamNumbers = new ushort[OutputCount];
  WMT_STREAM_SELECTION[] StreamSelections = new WMT_STREAM_SELECTION[OutputCount];
  //Look for the video stream within the ASF file
  for (uint i = 0; i < OutputCount; i++)
  {
    IWMOutputMediaProps Props = null;
    Guid mt;
    ushort StreamNumber;

    Reader.GetOutputProps(i, out Props);
    Reader.GetStreamNumberForOutput(i, out StreamNumber);
    StreamNumbers[i] = StreamNumber;
    StreamSelections[i] = WMT_STREAM_SELECTION.WMT_OFF;
    Props.GetType(out mt);
    if ( mt == MediaTypes.WMMEDIATYPE_Video )
    {
      uint FormatCount;
      VideoOuput = i;
      StreamSelections[i] = WMT_STREAM_SELECTION.WMT_ON;
      Reader.GetOutputFormatCount(i, out FormatCount);
      uint BufferSize = (uint)(Marshal.SizeOf(typeof(WM_MEDIA_TYPE)) + 
                               Marshal.SizeOf(typeof(WMVIDEOINFOHEADER)));
      IntPtr buffer = Marshal.AllocCoTaskMem((int)BufferSize); 
      try
      { //Look for the first uncompressed RGB output format
        for (uint j = 0; j < FormatCount; j++)
        {
          uint Size = 0;
          Reader.GetOutputFormat(i, j, out Props);
          Props.GetMediaType(IntPtr.Zero, ref Size);
          if ( Size > BufferSize )
          {
            BufferSize = Size;
            Marshal.FreeCoTaskMem(buffer);
            buffer = Marshal.AllocCoTaskMem((int)BufferSize);
          }
          Props.GetMediaType(buffer, ref Size);
          mtype = (WM_MEDIA_TYPE)Marshal.PtrToStructure(buffer, typeof(WM_MEDIA_TYPE));
          if ( mtype.formattype == MediaTypes.WMFORMAT_VideoInfo ) 
            if ( (mtype.subtype == MediaTypes.WMMEDIASUBTYPE_RGB555) ||
                 (mtype.subtype == MediaTypes.WMMEDIASUBTYPE_RGB24) ||
                 (mtype.subtype == MediaTypes.WMMEDIASUBTYPE_RGB32) )
            {
              VideoStream = StreamNumber;
              InfoHeader = (WMVIDEOINFOHEADER)Marshal.PtrToStructure(mtype.pbFormat, 
                                                                     typeof(WMVIDEOINFOHEADER));
              Reader.SetOutputProps(i, Props);
              break;
            }
        }
      }
      finally
      {
        Marshal.FreeCoTaskMem(buffer);
      }
    }
  }
  if ( VideoOuput == uint.MaxValue)
  { //No video stream found
    throw new ArgumentException(string.Format("No video stream found in: {0}", 
                                               AsfVideoFileName), 
                                "AsfVideoFileName");
  }
  if ( VideoStream != uint.MaxValue )
  {
    INSSBuffer  Sample = null;
    IntPtr      SampleBuff;
    ulong       SampleTime, Duration;
    uint        Flags, OutputNum;
    ushort      StreamNum;
    Bitmap      bmap;
    PixelFormat pixelfmt = PixelFormat.DontCare; 
        
    Reader.SetStreamsSelected((ushort)OutputCount, StreamNumbers, StreamSelections);
    Reader.SetRange(ImageTime, 0);
    Reader.GetNextSample( (ushort)VideoStream,
                          out Sample,
                          out SampleTime, 
                          out Duration, 
                          out Flags, 
                          out OutputNum, 
                          out StreamNum );
    //Use GetBufferAndLength instead if you want to do aditional error check
    Sample.GetBuffer(out SampleBuff);
     
    if ( mtype.subtype == MediaTypes.WMMEDIASUBTYPE_RGB555 )
    {
      pixelfmt = PixelFormat.Format16bppRgb555;
    }
    else if ( mtype.subtype == MediaTypes.WMMEDIASUBTYPE_RGB24 ) 
    {
      pixelfmt = PixelFormat.Format24bppRgb;
    }
    else if( mtype.subtype == MediaTypes.WMMEDIASUBTYPE_RGB32) 
    {
      pixelfmt = PixelFormat.Format32bppRgb;
    }
    int stride = InfoHeader.bmiHeader.biWidth * 
                 InfoHeader.bmiHeader.biPlanes * 
                 InfoHeader.bmiHeader.biBitCount / 8;
    bmap = new Bitmap(InfoHeader.bmiHeader.biWidth, 
                      InfoHeader.bmiHeader.biHeight, 
                      stride, pixelfmt, SampleBuff);
    bmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
    bmap.Save(JpgFileName, ImageFormat.Jpeg);
    bmap.Dispose(); 
  }
  else //No RGB output format found
    throw new ArgumentException(string.Format("No valid uncompressed video format found in: {0}", 
                                              AsfVideoFileName), 
                                "AsfVideoFileName");
}
The previous function receives time in 100-nanosecond units. To use time in HH:MM:SS you can use a function like this one:

/// <summary>
/// Obtains the 100-nanosecond time from a time given in hours, minutes, seconds and milliseconds.
/// </summary>
/// <param name="hours">Hours</param>
/// <param name="minutes">Minutes</param>
/// <param name="seconds">Seconds</param>
/// <param name="mseconds">Milliseconds</param>
/// <returns>100-nanosecond time</returns>
public static ulong HMSmS2WMTime(int hours, int minutes, int seconds, int mseconds)
{
  return (ulong)((hours*3600000) + (minutes*60000) + (seconds*1000) + mseconds)*10000;
}

//Using both functions to take a snapshot image at 5 second positon in a video:
WMVImageToJpg("Video.wmv", HMSmS2WMTime(0, 0, 5, 0), "image.jpg");

Respose to message: What about Video?
Greate samples, but what about video? Do you have any samples on creating wmv files programmatically using C#?

Anyway, thanks alot

Demo source project: Avi2Wmv.zip, updated: June 2, 2004
Article sources: ManWMF.zip, updated : June 2, 2004 (the demo project needs this version, the version at http://www.thecodeproject.com/ManWMF.asp is not up to date)

I've developed a project named Avi2Wmv inspired in the WMF sample UncompAVIToWMV, you can check that sample to better understand the code that I show here. Is a reduce version, I didn't include all the features of the UncompAVIToWMV sample

The main classes in this sample are:
AVIFileReader: Allows to read Audio and Video streams and formats from an AVI file using the Win32 AVIFile functions. This class is use by AviToASF class. You don't need to use it directly but checking its implementation could help in a better understanding of the rest of the code.

AviToAsf: is the AVI to ASF(WMV) converter. It's constructor receives as parameter the AVI file name, the ASF file name (result), the Windows Media Profile describing the desired output format and an optional array of WM_Attr structures defining metadata information of output file. NOTE: THIS CLASS ONLY ACCEPTS UNCOMPRESSED AVI STREAM.

The following code shows how to use AviToAsf:

try
{
IWMProfile Profile;
//Use any profile, in this case the first system profile
WM.ProfileManager.LoadSystemProfile(0, out Profile);
using(AviToAsf converter = new AviToAsf("somefile.avi", "somefile.wmv", Profile, null) )
{
converter.Progress += new ProgressEventHandler(OnProgress);
//OnProgress must be declared as: void OnProgress(object Sender, ProgressEventArgs e)
converter.Start();
}
//Conversion done successfully
}
catch(System.Runtime.InteropServices.COMException cex)
{
//Possible a WMF SDK error, so try to obtain WM error description using
//WM.ErrorDescription

MessageBox.Show(WM.ErrorDescription(cex.ErrorCode), "Error");
}
catch(Exception ex)
{
//Another error
MessageBox.Show(string.Format("Error message: {0}", ex.Message), "Error");
}
You can find a similar code in the demo project. The UI is not the best of the demo, here the steps to follow: select the AVI file, optionally select different output ASF file, select the WM profile to use and finally click Convert.

Response to message: Re: Implementing fast edit with IWMSyncReader
....

Obviously, reading decompressed samples all the way to the end works fine, but it is relatively slow. My aim is to reduce time required to process the file, that is why switching from decompressed to compressed samples.

I do use WMFSDK sample methods for copying input properties and stream attributes. I also copy sample time and flags. It all works fine till i start feeding decompressed and compressed samples to IWMWriter.

It seems to me than IWMWriter has problem switching from decompressed to compressed samples and produces files that are not valid: video sample order is not correct somewhere around switch point.

When I try to produce two files: one from decompressed samples and one from compressed and glue them together using wmvappend from SDK it also does not work correctly: video in second piece gets distorted.


I have written the following function and it works for me. I don’t use uncompressed samples at all and if you don’t need to edit the streams but just copy I think that the best idea would be to use only compressed samples. There are some issues that commented in the code.

/// <summary>
/// This functions copies a portion of an ASF file to another ASF file from a certain starting point
/// and with certain duration. Both starting point and duration are given in 100-nanosecond units.
/// Streams are read and written using compressed samples. 
/// This function doesn’t work with files containing image streams but works with audio and
/// video streams. The copy of header attributes and scripts were avoided for simplicity.
/// The reader synchronizes the audio and video streams this could cause that the audio
/// stream won’t start until the first video key frame, and then a silence at the beginning
/// of result file could be produced. The introduced silence could be important is the
/// video key frames in the source files are widely spaced.
/// </summary>
/// <param name="InputASFFile">Source file name</param>
/// <param name="OutputASFFile">Destination file name</param>
/// <param name="StartTime">Starting point in 100-nanosecond units</param>
/// <param name="Duration">Duration in 100-nanosecond units. Zero copies the files until the end</param>
public static void CopyWMV(string InputASFFile, string OutputASFFile, ulong StartTime, long Duration)
{
  IWMSyncReader Reader;
  IWMWriter Writer;
  IWMWriterAdvanced WriterAdvanced;
  IWMProfile Profile;
  uint StreamCount;
  uint InputCount;
  ushort[] StreamNumbers;
  WMT_STREAM_SELECTION[] StreamSelections;
	
  Reader = WM.CreateSyncReader(WMT_RIGHTS.WMT_RIGHT_NO_DRM);
  Writer = WM.CreateWriter();
  Reader.Open(InputASFFile);
  Profile = (IWMProfile)Reader;
  Profile.GetStreamCount(out StreamCount);
  StreamNumbers = new ushort[StreamCount];
  StreamSelections = new WMT_STREAM_SELECTION[StreamCount];
  for (uint i = 0; i < StreamCount; i++)
  {
    IWMStreamConfig StreamConfig;
    Profile.GetStream(i, out StreamConfig);
    StreamConfig.GetStreamNumber(out StreamNumbers[i]);
    StreamSelections[i] = WMT_STREAM_SELECTION.WMT_ON;
    //Read compressed samples
    Reader.SetReadStreamSamples(StreamNumbers[i], true); 
  }
  //Select all streams
  Reader.SetStreamsSelected((ushort)StreamCount, StreamNumbers, StreamSelections); 
  Writer.SetProfile(Profile);
  Writer.GetInputCount(out InputCount);
  for (uint i = 0; i < InputCount; i++)
  {
    Writer.SetInputProps(i, null); //Write compressed samples
  }
  Writer.SetOutputFilename(OutputASFFile);
  WriterAdvanced = (IWMWriterAdvanced)Writer;
  // Copy attributes avoided
	
  // Copy Codec Info avoided
	
  // Copy all scripts in the header avoided
  
  Writer.BeginWriting();
  Reader.SetRange(StartTime, Duration); //Seek
  for (uint StreamsRead = 0; StreamsRead < StreamCount; )
  {
    INSSBuffer Sample = null;
    ulong SampleTime;
    ulong SampleDuration;
    uint Flags;
    uint OutputNumber;
    ushort StreamNumber;
    try
    {
      StreamNumber = 0;
      //Read samples in chronological order.
      Reader.GetNextSample(0, out Sample, out SampleTime, out SampleDuration, out Flags, 
                           out OutputNumber, out StreamNumber);
      WriterAdvanced.WriteStreamSample(StreamNumber, SampleTime, 0, SampleDuration, Flags, Sample);
    }
    catch (COMException e)
    {
      if (e.ErrorCode == WM.NS_E_NO_MORE_SAMPLES)
      { //No more samples for this stream
        StreamsRead++;
      }
      else throw; 
    }
  }
  Writer.EndWriting();
  Reader.Close();
}
The following code use the previous function and the function HMSmS2WMTime to copy the last part of a video file to another starting at 5th minute.
ulong StartTime = HMSmS2WMTime(0, 5, 0, 0);
long Duration = 0;//Zero means until the end of the file
CopyWMV("SomeFile.wmv", "Result.wmv", StartTime, Duration);
  Copyright © Idael Cardoso. All rights reserved.