|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionOriginally, DirectX.Capture supported AVI file saving only. This format is not very usable because it often results in huge files. Sometimes, only the audio needs to be saved (e.g. FM Radio or just TV-sound). The enhancements described in this article make it possible to save audio in Windows Media Audio format. It took quite a lot of searching and reading before I came to the solution presented in this article. Also, I did not find other complete C# examples on this subject, so I thought an article on this subject might be interesting. ConsiderationsMy first idea was to save audio as WAV (about 10 MB per minute) or as MP3 (about 1 MB per minute) and describe the resulting implementation. These are generally known formats, but coding was not very straightforward. Looking around at possible coding examples, I came across the Windows Media format. Comparing these examples with the I was in favor of the minor version using The What file writer needed to be used (looking into graphedt)? The WM ASF file writer seemed to be the one. Which audio/video format should be used? Well, I didn't know. How to change the audio/video format? This could be done via the property window that belongs to the ASF file writer. Via a right click on the filter in graphedt, the windows showed up, so I knew it was there. Are there other ways to change the audio/video format? Via a profile representing the audio/video format. Which formats are possible? I didn't know. What is the resulting video resolution? I didn't know. When I started coding, I got some strange errors from the code. In other words: I had to do more research to find answers to these questions. IWMProfile InterfaceWhere should we start from? First, go to MSDN. For capturing audio, search here for the interesting article Creating an Audio Capture Graph. There you can find a lot of information! Also, MSDN has a special Windows Media Format SDK page; just search for Windows Media Format SDK. There, I found the GUIDs that DirectX offers by default, so that was already one answer I wanted. The best way for selecting the wanted audio/video file format seems to be the I found an interesting example which showed me that it was possible to get a list of audio/video formats: Windows Media Audio Compressor by Idael Cardoso. This article describes how Then I made up my mind and thought to get it working first, and the rest would follow. Problem with the ASF File WriterWhen I started coding, I had problems with the ASF file writer. For some reason, errors occurred when using Why is the The Current ImplementationBefore telling what the solution is, I want to explain the current implementation of the audio/video capturing to a file. This functionality is listed here and can be found in the function //Original code fragment renderGraph Capture.cs
Guid mediaSubType = MediaSubType.Avi;
hr = captureGraphBuilder.SetOutputFileName( ref mediaSubType,
Filename, out muxFilter, out fileWriterFilter );
if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
// Render video (video -> mux)
if ( VideoDevice != null )
{
// Try interleaved first, because if the device supports it,
// it's the only way to get audio as well as video
cat = PinCategory.Capture;
med = MediaType.Interleaved;
hr = captureGraphBuilder.RenderStream( ref cat, ref med,
videoDeviceFilter, videoCompressorFilter, muxFilter );
if( hr < 0 )
{
med = MediaType.Video;
hr = captureGraphBuilder.RenderStream( ref cat, ref med,
videoDeviceFilter, videoCompressorFilter, muxFilter );
if ( hr == -2147220969 )
throw new DeviceInUseException( "Video device", hr );
if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
}
}
// Render audio (audio -> mux)
if ( AudioDevice != null )
{
cat = PinCategory.Capture;
med = MediaType.Audio;
hr = captureGraphBuilder.RenderStream( ref cat, ref med,
audioDeviceFilter, audioCompressorFilter, muxFilter );
if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
}
isCaptureRendered = true;
didSomething = true;
The EnhancementsI made the choice to present the code that enables Windows Media file saving and uses In a future topic, the I also made the choice to show changes made to Capture.cs (in Recording File ModeThe enumeration The variable /// <summary>
/// Recording file mode type enumerations
/// </summary>
public enum RecFileModeType
{
/// <summary> Avi video (+ audio) </summary>
Avi,
/// <summary> Wmv video (+ audio) </summary>
Wmv,
/// <summary> Wma audio </summary>
Wma,
}
private RecFileModeType recFileMode = RecFileModeType.Wma;
/// <summary>
/// Recording file modes
/// </summary>
public RecFileModeType RecFileMode
{
get { return(recFileMode); }
set
{
if(this.graphState == GraphState.Capturing)
{
// Value may not be changed now
return;
}
recFileMode = value;
}
}
Capturing, RenderStream()The most interesting part is the modification in the capture-specific code with Keep in mind for saving a WMA file:
Keep in mind for saving a WMV file:
Keep in mind for saving an AVI file:
The code explains itself (I hope). There are checks added, so depending on the file format, specific actions can be performed. The first action is the initialization of There is still one question left: What does // Record captured audio/video in Avi, Wmv or Wma format
Guid mediaSubType; // Media sub type
// Set media sub type
if(RecFileMode == RecFileModeType.Avi)
{
mediaSubType = MediaSubType.Avi;
}
else
{
mediaSubType = MediaSubType.Asf;
}
// Initialize the Avi or Asf file writer
hr = captureGraphBuilder.SetOutputFileName( ref mediaSubType,
Filename, out muxFilter, out fileWriterFilter );
if( hr < 0 )
{
Marshal.ThrowExceptionForHR( hr );
}
// For Wma (and Wmv) a suitable profile
// must be selected. This can be done
// via a property window, however the muxFilter
// is just created. if needed, the
// property Windows should show up right now!
// Another solution is to configure
// the Asf file writer, however DShowNet does not
// have the interface that is needed. Please ensure it is added.
if(RecFileMode == RecFileModeType.Wma)
{
IConfigAsfWriter lConfig = muxFilter as IConfigAsfWriter;
// Obsolete interface?
// According to MSDN no, according to DirectShowLib yes.
// For simplicity, it will be used ...
hr =
lConfig.ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio);
if(hr < 0)
{
// Problems with selecting video write format
// Release resources ... (not done yet)
Marshal.ThrowExceptionForHR( hr );
}
}
// Render video (video -> mux) if needed or possible
if((VideoDevice != null)&&(RecFileMode != RecFileModeType.Wma))
{
// Try interleaved first, because if the device supports it,
// it's the only way to get audio as well as video
cat = PinCategory.Capture;
med = MediaType.Interleaved;
hr = captureGraphBuilder.RenderStream( ref cat, ref med,
videoDeviceFilter, videoCompressorFilter, muxFilter );
if( hr < 0 )
{
med = MediaType.Video;
hr = captureGraphBuilder.RenderStream( ref cat, ref med,
videoDeviceFilter, videoCompressorFilter, muxFilter );
if ( hr == -2147220969 )
{
throw new DeviceInUseException( "Video device", hr );
}
if( hr < 0 )
{
Marshal.ThrowExceptionForHR( hr );
}
}
}
// Render audio (audio -> mux) if possible
if ( AudioDevice != null )
{
// Keep in mind that for certain Wmv
// formats do not have an audio stream,
// so when using this code, please ensure
// you use a format which supports
// audio!
cat = PinCategory.Capture;
med = MediaType.Audio;
hr = captureGraphBuilder.RenderStream( ref cat, ref med,
audioDeviceFilter, audioCompressorFilter, muxFilter );
if( hr < 0 )
{
Marshal.ThrowExceptionForHR( hr );
}
}
isCaptureRendered = true;
didSomething = true;
WMA Audio FormatThe function In a future code sample, the // Windows Media Audio 8 for Dial-up Modem (Mono, 28.8 Kbps)
private static readonly Guid WMProfile_V80_288MonoAudio =
new Guid("7EA3126D-E1BA-4716-89AF-F65CEE0C0C67");
// Windows Media Audio 8 for Dial-up Modem
// (FM Radio Stereo, 28.8 Kbps)
private static readonly Guid WMProfile_V80_288StereoAudio =
new Guid("7E4CAB5C-35DC-45bb-A7C0-19B28070D0CC");
// Windows Media Audio 8 for Dial-up Modem (32 Kbps)
private static readonly Guid WMProfile_V80_32StereoAudio =
new Guid("60907F9F-B352-47e5-B210-0EF1F47E9F9D");
// Windows Media Audio 8 for Dial-up Modem
// (Near CD quality, 48 Kbps)
private static readonly Guid WMProfile_V80_48StereoAudio =
new Guid("5EE06BE5-492B-480a-8A8F-12F373ECF9D4");
// Windows Media Audio 8 for Dial-up Modem (CD quality, 64 Kbps)
private static readonly Guid WMProfile_V80_64StereoAudio =
new Guid("09BB5BC4-3176-457f-8DD6-3CD919123E2D");
// Windows Media Audio 8 for ISDN (Better than CD quality, 96 Kbps)
private static readonly Guid WMProfile_V80_96StereoAudio =
new Guid("1FC81930-61F2-436f-9D33-349F2A1C0F10");
// Windows Media Audio 8 for ISDN (Better than CD quality, 128 Kbps)
private static readonly Guid WMProfile_V80_128StereoAudio =
new Guid("407B9450-8BDC-4ee5-88B8-6F527BD941F2");
IConfigAsWriter InterfaceTo get the code working, still one more interface needs to be added because [Guid("45086030-F7E4-486a-B504-826BB5792A3B"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IConfigAsfWriter
{
/// Obsolete?
[PreserveSig]
int ConfigureFilterUsingProfileId([In] int dwProfileId);
/// Obsolete?
[PreserveSig]
int GetCurrentProfileId([Out] out int pdwProfileId);
/// Obsolete?
[PreserveSig]
int ConfigureFilterUsingProfileGuid([In,
MarshalAs(UnmanagedType.LPStruct)] Guid guidProfile);
[PreserveSig]
int GetCurrentProfileGuid([Out] out Guid pProfileGuid);
/// Obsolete?
[PreserveSig]
int ConfigureFilterUsingProfile([In] IntPtr pProfile);
/// Obsolete?
[PreserveSig]
int GetCurrentProfile([Out] out IntPtr ppProfile);
[PreserveSig]
int SetIndexMode([In,
MarshalAs(UnmanagedType.Bool)] bool bIndexFile);
[PreserveSig]
int GetIndexMode([Out,
MarshalAs(UnmanagedType.Bool)] out bool pbIndexFile);
}
Audio Rendering, TestingThe modifications shown in this article have been tested. This does not mean that it will work without any problem. During testing, I noticed that I did not get the audible sound. So, I had to make an additional modification. The modification was needed because I was using a Hauppauge PVR150-MCE (Amity2) TV card. This card gets the audio via the PCI bus and not via a wired connection. The current
The variable // Option for selection audio rendering via the PCI bus of the TV card
// For wired audio connections the value must be false!
// For TV-cards, like the Hauppauge PVR150, the value must be true!
// This TV-card does not have a wired audio connection. However, this
// option will work only if the TV-card driver has an audio device!
private bool audioviapci = false;
The actual code should be put in the function // Special option to enable rendering audio via PCI bus
if(audioviapci)
{
med = MediaType.Audio;
hr = captureGraphBuilder.RenderStream( ref cat, ref med,
audioDeviceFilter, null, null );
if( hr < 0 )
{
Marshal.ThrowExceptionForHR( hr );
}
}
The last parameter in
Points of InterestThere is no demo program added. Please download the full source from the DirectX.Capture Class Library page and download the demo version. The source file that can be downloaded contains the code changes and the original code. Just replace the original file with my version. Normally, I use conditions to have the original and the new code in the same file. This is handy if there is an error in the code. Then I compare that code with the previous version. I removed the conditions because this source file should be easy to use. I did not remove the conditions completely, so you will be able to find the exact location of the code changes. I posted the articles DirectShow - TV Finetuning using the IKsPropertySet in C# and Video File Saving in Windows Media Video Format for the DirectX.Capture Class Library, which have a code example that incorporates the code changes mentioned in this article. In addition, that code example has more features, such as capturing audio via video device filter, added TV fine-tuning, added FM radio and de-interlacing. Furthermore, the code example supports either the Feedback and ImprovementsI hope this code helps you in understanding the structure of the History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||