Every once in a while DVR2WMV doesn't index a wmv when it is converted, so it's not possible to move around in the file (fast forward, reverse, etc). Microsoft includes a GUI tool on the Windows Media Encoder that I was using, but it's not an automated solution, so I took a couple hours to see if it was possible to do it programmatically.
This functionality was incorporated in the MigrateMetadata action in DVRMSToolbox 1.1.0.3. I've also written a simple command line utilty, wmvindex.exe, that will just index the file.
For those who are interested in how it works...
The heavy lifting in indexing the file is done by the object implementing IWMIndexer that is returned by calling WMCreateIndexer. The pinvoke declaration look like this -
[DllImport("WMVCore.dll", SetLastError=true, CharSet=CharSet.Unicode, ExactSpelling=true, PreserveSig=false)]
private static extern IWMIndexer WMCreateIndexer();
[ComImport]
[Guid("6d7cdc71-9888-11d3-8edc-00c04f6109cf")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IWMIndexer
{
void StartIndexing([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL,
[In] IWMStatusCallback pCallback,
[In] IntPtr pvContext);
void Cancel();
}
To use IWMIndexer call StartIndexing, which takes three arguments; the path to the file to index, a class that implements IWMStatusCallback, and null pointer. The OnStatus method of the IWMStatusCallback is called periodically to update progress. The definitions for IWMStatusCallback and the enums it needs are defined below.
[ComImport]
[Guid("6d7cdc70-9888-11d3-8edc-00c04f6109cf")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IWMStatusCallback
{
void OnStatus([In] WMT_STATUS Status,
[In] int hr,
[In] WMT_ATTR_DATATYPE dwType,
[In, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
[In] IntPtr pvContext);
}
StartIndexing is an ansynchronous method so it's necessary to pause code execution while the file is indexed using a ManualResetEvent. I chose to implement it as part of the class that implements IWMStatusCallback.
public class StatusCallback : MarshalByRefObject, IWMStatusCallback
{
ManualResetEvent _mr;
public StatusCallback(ManualResetEvent mr)
{
_mr = mr;
}
public void OnStatus(WMT_STATUS Status, int hr, WMT_ATTR_DATATYPE dwType, byte[] pValue, IntPtr pvContext)
{
int progress = 0;
if (pValue.Length > 0)
progress = pValue[0];
if (progress == 100)
{
Console.WriteLine("Done Indexing");
}
switch (Status) {
case WMT_STATUS.WMT_ERROR:
Console.WriteLine("Error Indexing File");
_mr.Set();
break;
case WMT_STATUS.WMT_STOPPED:
_mr.Set();
break;
}
}
}
I'm only interested in the completion status codes so nothing important happens in the OnStatus method it only looks at the Status variable to see if we're done or an error has occured.
Below is some sample code to idex the file.
ManualResetEvent mr = new ManualResetEvent(false);
IWMIndexer wi = WMCreateIndexer();
StatusCallback sc = new StatusCallback(mr);
IntPtr pv = IntPtr.Zero;
wi.StartIndexing(targetFile, (IWMStatusCallback)sc, pv);
mr.WaitOne();
if (mr != null)
mr.Close();
if (wi != null)
Marshal.ReleaseComObject(wi);
The full source code for the utility can be downloaded here.