Software DevelopmentBasicode historie Webdesign, allerlei Video file saving in Wmv Save audio in Wma Downloads Matrox Marvel G200 Digital TV DVB-C/S Media Center remote control Analog TV and FM radio Teletext on a PC Digital TV DVB-T DirectX and DirectShow Amcap video capture Introduction Meest gelezen onderwerpen: Website blog... Mediastreaming van Windows P... Pingen en een IP adres vinde... Gastenboek lezen... Geluidsapparatuur aansluiten... UTP, straight en cross ... Schrijf een bericht... Windows 7, 8 en 10 in een th... Gastenboek schrijven... Een thuisnetwerk aanleggen...
| Recording FM radio/TV sound in Wma format using the DirectX.Capture class libraryThis article shows a coding example to save audio as Windows Media audio file. A number of enhancements to the DirectX.Capture class are described to save the captured audio to a file. The DirectX.Capture class is originally written by Brian Low. He did a tremendous job to make DirectX available to C#. As result everyone can start with a working example to learn and use DirextX, DirectShow and C#. And also a lot of people wrote modifications for this class that could be used by others. An interesting webiste is DirectShow for C#, made by David David Wohlferd(?). At this site he provides (temporarily) some interesting examples using DirectX and Windows Media functionality. You can also find here an interesting modified version of the DirextX.Capture class example, named CaptureTest. He added much future functionality that was mentioned by Brian Low before. Very interesting is the way the new CaptureTest program can store program settings in a configuration file.
I wish you much pleasure with learning and using the DirectX.Capture class library and I hope you will enjoy my article also.
IntroductionOriginally the DirectX.Capture supported Avi file saving only. This format is not very usable because it very often results in huge files. Sometimes only audio needs to be saved (e.g. FM Radio or just TV-sound). The enhancements described in this article makes it possible to save audio in the Windows Media Audio format. It took quite a lot searching and reading before I came to the solution presented in this article. Also I did not find complete C# examples on this subject, so an article on this subject might be interesting.ConsiderationsMy first idea was to save audio as Wav (about 10MB per minute) or as MP3 (about 1MB per minute?) and describe the resulting implementation. These are general known formats, but coding was not very straightforward. But looking around to possible coding examples, I came across the Windows Media format. Comparing these examples with theDirectX.Capture class example, I noticed that minor to many code modifications
seems to be needed.
I was in favor of the minor version using SetOutputFileName() and RenderStream() .
I noticed that ConfigureFilterUsingProfileGuid() was used also.
Only it was not fully clear to me what to do with ConfigureFilterUsingProfileGuid() .
It was used for changing the Windows Media audio/video format but the example showed me a video only guid and
I had the idea that there were much more possible choices.
The SetOutputFileName() is very important.
It is not only setting the filename as the name suggest, no, it will also add filters to the graph.
For Asf this will be the Asf file writer, for Avi this will be Avi muxer and file writer (two filters!).
But it is obvious, the selection of the preferred Windows Media audio/video format is essential. So, when starting the implementation for saving Windows Media files, many questions raised. What file writer needed to be used (looking into graphedt)? The WM Asf file writer seems to be the one. Which audio/video format should be used? Well, I did not 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. Other ways to change the audio/video format? Via a profile representing the audio/video format. Which formats are possible? I did not know. What is the resulting video resolution? I did not know. When starting coding I got strange errors from the code. In other words: I had to do more research to find answers. IWMProfile interfaceWhere should we start? First go to MSDN. For capturing audio, the following link might be interesting: Creating an Audio Capture graph. There you can find a lot of information! Also there is special SDK for Windows Media: Windows Media Format 9.5 SDK. There I found the guid's that DirectX offers by default, so that was one answer I wanted.The best way for selecting the wanted audio/video file format seems to be the IWMProfile interface.
Actually the selection is done by loading a profile representing the audio/video file format.
I noticed that MSDN also mentioned that the ConfigureFilterUsingProfileGuid() without a note it should not be used anymore.
The amazes me a bit because in DirectShowLib I found a note that the interface became obsolete.
I found an interesting example that showed me that it was possible to get the audio/video formats:
Windows Media Audio compressor By Idael Cardoso.
This article describes how IWMProfile can be used, and the sample program showed me a nice listbox with a list of
Windows Media Audio formats.
It helped me in finding more information:
CSharp Windows Media Format SDK, Translation by Idael Cardoso.
Than I made up my mind and I thought, first get the program working, the rest
will come later.
Problem with the Asf file writerWhen starting coding I got problems with the Asf file writer. For some reason errors occured when usingmediaControl.Run() .
What I did in general was adding the filter, having the property window for making specific settings, to the
graph in advance.
Upon the real capturing the added filter(s) were be connected to the graph (either using RenderStream() or a direct connect).
That method I used for Wav, Mp3 and Avi file saving, but seems to cause problems for Asf (+ Wmv, Wmv).
Why the mediaControl.Run() was called, well when selecting something in the menu, updateMenu() is called ...
My impression is that it has something to do with the filter itself.
I noticed that all examples I saw, either configured the filter immediately after the SetOutputFileName() or did not
configure at all. So I choosed for the first solution. For audio this is okay
because the default choice is good enough. For video this would not be
acceptable. The current implementationBefore telling what the solution, I want to explain first the current implementation of the audio/video capturing to file first. This functionality is listed here and can be found in the functionrenderGraph in Capture.cs.
In the code the mediaSubtype is set first, then SetOutputFileName() is called to add the Avi
multiplexer and the file writer.
Furthermore the filename is stored. The next step is to initialize the video rendering.
A possible compressor filter is taken into account. After this the audio rendering is initialized.
Upon a call of mediaControl.Run() in the function StartPreviewIfNeeded() , the capture graph starts.
//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 code that enables Windows Media file saving and usesConfigureFilterUsingProfileGuid()
for selecting the proper audio/video format via the Guid that corresponds with a specific audio/video format and so
corresponds with a specific profile.
Another advantage is that way the coding will be easier to understand.
In a future topic the IWMProfile interface could be explained in some more detail.
For now it would mean that this article would not be written because it would become a little bit too long.
So it this article makes you more curious, I think, that is very positive.
I also made the choice to show changes for Capture.cs (in DirectX.Capture) only, and not for CaptureTest.cs. Changes to CaptureTest.cs are not really needed unless you want to select between an audio or an video format, or to switch between Avi or a Windows media Asf format such as Wma. Such selections could be implemented via a menu option (or a button press). I think it is a good learning goal to add an user control functionality yourself (if the functionality is really needed). Well now go on the enhancements: Recording file modeThe enumeration RecFileModeType is declared which specifies the possible audio/video recording file choices. The choices are Avi, Wmv and Wma. The Wma choice becomes the default because this example is about Audio file saving. The Avi choice was added, so the original functionality is still there and could be used if needed. The Wmv is not explained in this article. It is a good learning goal to figure out how to use that. Also the use of Wmv is a little more complicated because video could be a video + audio stream or a video stream only. A good implementation should check in advance if conflicts may occur. For this theIWMProfile interface can be used because IWMProfile
can provide information about the streams it uses.
The variable recFileMode contains the audio/video recording file mode, so this variable gets value RecFileModeType.Wma .
The variable recFileMode should be accessed via RecFileMode .
There is a check added that prevents changing the file mode during file capturing.
This to prevent strange side effects.
It also shows that other functionality could be executed upon changing the value of recFileMode .
A nice feature would be the change of the filename extension.
This code should be put in Capture.cs, preferably in the beginning because there you can find more declarations.
///
Capturing, RenderStream()The most interesting part are the modifications in the capture specific code withRenderStream() , mentioned earlier.
The major difference is that the file recording mode is taken into account.
Still this example is kept simple, so for saving a specific audio and/or video file settings might be important.
Keep in mind that saving of Wma file is needed: RecFileMode = RecFileModeType.Wma
Keep in mind that saving of Wmv file is needed:
RecFileMode = RecFileModeType.Wmv
Keep in mind that saving of Avi file is needed:
RecFileMode = RecFileModeType.Avi
The code explains itselfs (I hope).
There are checks added so depending on the file format specific actions can be performed.
The first action is the initialization of mediaSubtype .
The next action is to configure the Asf file writer.
The configuration is a one-liner.
Interesting is the type casting of the file writer pointer for calling ConfigureFilterUsingProfileGuid() .
This solution is specific for .Net, it gives an easy solution for changing the
preferred audio/video profile.
There is one question left: What does WMProfile_V80_64StereoAudio means?
WMProfile_V80_64StereoAudio is the audio recording format I choosed as default.
There are more choices possible (will be described later on).
For a different choice, a different value must be used.
Also, if you want to save video, just select a valid Windows media video format.
The audio and video rendering sections looks the same, with one major difference.
Upon entering the video rendering section there is a check on the file format, for audio capturing,
no video must be rendered so that section must be ignored!
// 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;
}
// Intialize 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 formatsThe functionConfigureFilterUsingProfileGuid() needs to be called for setting the proper audio format.
Which formats exists?
In this example there are seven specific Windows media audio formats provided (hard coded) by means of a Guid .
Each Guid stands for a special format and profile.
There are more Windows Media formats; however, those formats supports also video
(and audio).
The best way to get these formats is to use IWMProfile functionality.
Than you will be sure that you can get formats that really exists. In a future code sample, the IWMProfile method can be shown.
For now, this will be though enough.
Thanks to the IWMProfile interface I was able to retrieve all names that belong to a specific file format and
profile.
The following code, with at least the audio format you are going to use, should be put somewhere in Capture.cs.
// 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");
Using the names in the code, might look like this:
IConfigAsWriter interfaceTo get the code working still one interface needs to be addded becauseDShowNET does not support the interface of IConfigAsfWriter .
DirectShowLib supports this interface; so if you use that library
then there is no extra work. This interface can be added to Capture.cs or another suitable place.
Keep in mind to change the naming accordingly.
[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, will work without any problem. During testing I noticed that I did not got audible sound. So, I had to make an additional modification. The modification was needed because I am using a Hauppauge PVR150-MCE (Amity2) TV-card. This card gets the audio via the PCI bus, and not via a wired connection. The currentDirectX.Capture class example will work with TV-cards using a wired connection to a sound card.
So for those who have a special TV card, the modification might be interesting:
during preview: do audio rendering.
I added an option via a variable false must be used for wired audio connections (default choice).
true must be used when the audio is coming via the PCI bus.
The variable audioviapci should be put somewhere in Capture.cs.
There is one limitation: the TV-card driver must have a capture device for audio.
If the TV-card driver does not have such device, then the following code will not work either.
How to handle such case could be a new article.
For capturing audio, this modification is not needed.
It is just meant for listening.
// 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 renderGraph() in Capture.cs.
This code should be inserted after the check
if (wantPreviewRendered && !isPreviewRendered) in renderGraph() where the video rendering is started.
// 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 RenderStream() is null .
This means the audio is rendered to the default audio renderer.
It might be possible that there is still no audible sound.
In that case do the following:
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 souce 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 easily. Feedback and ImprovementsI hope this code helps you in understanding the structure of theDirectX.Capture class, and I hope I provided you
an enhancement that might be useful to you.
Feel free to post any comments and questions.
HistoryMarch 15, 2006 - First releaseTerug naar het Begin. |
Welkom | Software | Stuur bericht | Gastenboek | Website Blog | Links |