/// <summary> /// Hardcode audio config for testing. /// </summary> /// <returns></returns> public bool ConfigAudio() { //make up some media types for testing WAVEFORMATEX wfex = new WAVEFORMATEX(); wfex.FormatTag = 1; //1==WAVE_FORMAT_PCM wfex.Channels = 1; wfex.SamplesPerSec = 16000; wfex.AvgBytesPerSec = 32000; wfex.BlockAlign = 2; wfex.BitsPerSample = 16; wfex.Size = 0; IntPtr wfexPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(wfex)); Marshal.StructureToPtr(wfex, wfexPtr, true); _WMMediaType mt = new _WMMediaType(); mt.majortype = WMGuids.ToGUID(WMGuids.WMMEDIATYPE_Audio); mt.subtype = WMGuids.ToGUID(WMGuids.WMMEDIASUBTYPE_PCM); mt.bFixedSizeSamples = 1; //true mt.bTemporalCompression = 0; //false mt.lSampleSize = 2; mt.formattype = WMGuids.ToGUID(WMGuids.WMFORMAT_WaveFormatEx); //This is the only value permitted. mt.pUnk = null; mt.cbFormat = (uint)Marshal.SizeOf(wfex) + wfex.Size; mt.pbFormat = wfexPtr; try { // Used GetMediaType to sanity check the managed structs: //uint size = 0; //audioProps.GetMediaType(IntPtr.Zero,ref size); //IntPtr mtPtr = Marshal.AllocCoTaskMem((int)size); //audioProps.GetMediaType(mtPtr,ref size); //_WMMediaType mt2 = (_WMMediaType)Marshal.PtrToStructure(mtPtr,typeof(_WMMediaType)); //WMMediaType.WaveFormatEx wfex2 = (WMMediaType.WaveFormatEx)Marshal.PtrToStructure(mt2.pbFormat,typeof(WMMediaType.WaveFormatEx)); // Examine here. //Marshal.StructureToPtr(mt,mtPtr,true); //audioProps.SetMediaType( mtPtr ); } catch (Exception e) { eventLog.WriteEntry("Failed to set audio properties: " + e.ToString(), EventLogEntryType.Error, 1000); Debug.WriteLine("Failed to set audio properties: " + e.ToString()); return(false); } bool ret = ConfigAudio(mt); Marshal.FreeCoTaskMem(wfexPtr); return(ret); }
/// <summary> /// Copy the LST managed MediaType to the Windows Media Interop type /// </summary> /// <param name="mt"></param> /// <returns></returns> private _WMMediaType ConvertMediaType(UW.CSE.MDShow.MediaType mt) { _WMMediaType wmt = new _WMMediaType(); if (mt == null) { return(wmt); } if (mt.MajorType == UW.CSE.MDShow.MajorType.Video) { // Basic video settings: //int w=320; //int h=240; //int fps=30; // For RGB24: //ushort bpp=24; //uint comp=0; //GUID stype = WMGuids.ToGUID(WMGuids.WMMEDIASUBTYPE_RGB24); // ..or for I420: //WORD bpp=12; //DWORD comp=0x30323449; //GUID stype= WMMEDIASUBTYPE_I420; // Settings for the video stream: // BITMAPINFOHEADER // DWORD biSize = size of the struct in bytes.. 40 // LONG biWidth - Frame width // LONG biHeight - height could be negative indicating top-down dib. // WORD biPlanes - must be 1. // WORD biBitCount 24 in our sample with RGB24 // DWORD biCompression 0 for RGB // DWORD biSizeImage in bytes.. biWidth*biHeight*biBitCount/8 // LONG biXPelsPerMeter 0 // LONG biYPelsPerMeter 0; // DWORD biClrUsed must be 0 // DWORD biClrImportant 0 // // notes: // biCompression may be a packed 'fourcc' code, for example I420 is 0x30323449, IYUV = 0x56555949... // I420 and IYUV are identical formats. They use 12 bits per pixel, and are planar, comprised of // nxm Y plane followed by n/2 x m/2 U and V planes. Each plane is 8bits deep. //BitmapInfo bi = new BitmapInfo(); //bi.Size=(uint)Marshal.SizeOf(bi); //bi.Width = w; //bi.Height = h; //bi.Planes = 1; //always 1. //bi.BitCount = bpp; //bi.Compression = comp; //RGB is zero.. uncompressed. //bi.SizeImage = (uint)(w * h * bpp / 8); //bi.XPelsPerMeter = 0; //bi.YPelsPerMeter = 0; //bi.ClrUsed = 0; //bi.ClrImportant = 0; // WMVIDEOINFOHEADER // RECT rcSource; // RECT rcTarget; // DWORD dwBitRate.. bps.. Width*Height*BitCount*Rate.. 320*240*24*29.93295=55172414 // DWORD dwBitErrorRate zero in our sample. // LONGLONG AvgTimePerFrame in 100ns units.. 334080=10000*1000/29.93295 // BITMAPINFOHEADER bmiHeader copy of the above struct. //VideoInfo vi = new VideoInfo(); //vi.Source.left = 0; //vi.Source.top = 0; //vi.Source.bottom = bi.Height; //vi.Source.right = bi.Width; //vi.Target = vi.Source; //vi.BitRate = (uint)(w * h * bpp * fps); //vi.BitErrorRate = 0; //vi.AvgTimePerFrame = (UInt64) ((10000 * 1000) / fps); //vi.BitmapInfo = bi; UW.CSE.MDShow.MediaTypeVideoInfo vi = mt.ToMediaTypeVideoInfo(); IntPtr viPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(vi.VideoInfo)); Marshal.StructureToPtr(vi.VideoInfo, viPtr, true); // WM_MEDIA_TYPE // GUID majortype WMMEDIATYPE_Video // GUID subtype WMMEDIASUBTYPE_RGB24 in our sample // BOOL bFixedSizeSamples TRUE // BOOL bTemporalCompression FALSE // ULONG lSampleSize in bytes This was zero in our sample, but could be 320*240*24/8=230400 // GUID formattype WMFORMAT_VideoInfo // IUnknown* pUnk NULL // ULONG cbFormat size of the WMVIDEOINFOHEADER // [size_is(cbFormat)] BYTE *pbFormat pointer to the WMVIDEOINFOHEADER //Note WM_MEDIA_TYPE is the same as Directshow's AM_MEDIA_TYPE. //WM_MEDIA_TYPE mt; wmt.majortype = WMGuids.ToGUID(mt.MajorTypeAsGuid); wmt.subtype = WMGuids.ToGUID(mt.SubTypeAsGuid); wmt.bFixedSizeSamples = mt.FixedSizeSamples?1:0; wmt.bTemporalCompression = mt.TemporalCompression?1:0; //mt.lSampleSize = w * h * bpp / 8; // this was zero in avinetwrite! wmt.lSampleSize = 0; //hmm. Don't think it matters?? wmt.formattype = WMGuids.ToGUID(mt.FormatTypeAsGuid); wmt.pUnk = null; wmt.cbFormat = (uint)Marshal.SizeOf(vi.VideoInfo); wmt.pbFormat = viPtr; //PRI3: redesign so that this is freed: //Marshal.FreeCoTaskMem(viPtr); } else if (mt.MajorType == UW.CSE.MDShow.MajorType.Audio) { // WaveFormatEx wfex = new WaveFormatEx(); // // wfex.FormatTag = 1; //1==WAVE_FORMAT_PCM // wfex.Channels = 1; // wfex.SamplesPerSec = 16000; // wfex.AvgBytesPerSec = 32000; // wfex.BlockAlign = 2; // wfex.BitsPerSample = 16; // wfex.Size = 0; UW.CSE.MDShow.MediaTypeWaveFormatEx wfex = mt.ToMediaTypeWaveFormatEx(); IntPtr wfexPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(wfex.WaveFormatEx)); Marshal.StructureToPtr(wfex.WaveFormatEx, wfexPtr, true); wmt.majortype = WMGuids.ToGUID(mt.MajorTypeAsGuid); //WMGuids.ToGUID(WMGuids.WMMEDIATYPE_Audio); wmt.subtype = WMGuids.ToGUID(mt.SubTypeAsGuid); //WMGuids.ToGUID(WMGuids.WMMEDIASUBTYPE_PCM); wmt.bFixedSizeSamples = mt.FixedSizeSamples?1:0; //1; //true wmt.bTemporalCompression = mt.TemporalCompression?1:0; //0; //false wmt.lSampleSize = (uint)mt.SampleSize; //2; wmt.formattype = WMGuids.ToGUID(mt.FormatTypeAsGuid); //WMGuids.ToGUID(WMGuids.WMFORMAT_WaveFormatEx); //This is the only value permitted. wmt.pUnk = null; wmt.cbFormat = (uint)Marshal.SizeOf(wfex.WaveFormatEx) + wfex.WaveFormatEx.Size; wmt.pbFormat = wfexPtr; //try //{ // Used GetMediaType to sanity check the managed structs: //uint size = 0; //audioProps.GetMediaType(IntPtr.Zero,ref size); //IntPtr mtPtr = Marshal.AllocCoTaskMem((int)size); //audioProps.GetMediaType(mtPtr,ref size); //_WMMediaType mt2 = (_WMMediaType)Marshal.PtrToStructure(mtPtr,typeof(_WMMediaType)); //WMMediaType.WaveFormatEx wfex2 = (WMMediaType.WaveFormatEx)Marshal.PtrToStructure(mt2.pbFormat,typeof(WMMediaType.WaveFormatEx)); // Examine here. //Marshal.StructureToPtr(mt,mtPtr,true); //audioProps.SetMediaType( mtPtr ); //} //catch (Exception e) //{ // Debug.WriteLine("Failed to set audio properties: " + e.ToString()); // return wmt; //} //PRI3: redesign so that this is freed: //Marshal.FreeCoTaskMem(wfexPtr); } return(wmt); }
/// <summary> /// Hardcode video config for testing. /// </summary> /// <returns></returns> public bool ConfigVideo() { // Basic video settings: int w = 320; int h = 240; int fps = 30; // For RGB24: ushort bpp = 24; uint comp = 0; GUID stype = WMGuids.ToGUID(WMGuids.WMMEDIASUBTYPE_RGB24); // ..or for I420: //WORD bpp=12; //DWORD comp=0x30323449; //GUID stype= WMMEDIASUBTYPE_I420; // Settings for the video stream: // BITMAPINFOHEADER // DWORD biSize = size of the struct in bytes.. 40 // LONG biWidth - Frame width // LONG biHeight - height could be negative indicating top-down dib. // WORD biPlanes - must be 1. // WORD biBitCount 24 in our sample with RGB24 // DWORD biCompression 0 for RGB // DWORD biSizeImage in bytes.. biWidth*biHeight*biBitCount/8 // LONG biXPelsPerMeter 0 // LONG biYPelsPerMeter 0; // DWORD biClrUsed must be 0 // DWORD biClrImportant 0 // // notes: // biCompression may be a packed 'fourcc' code, for example I420 is 0x30323449, IYUV = 0x56555949... // I420 and IYUV are identical formats. They use 12 bits per pixel, and are planar, comprised of // nxm Y plane followed by n/2 x m/2 U and V planes. Each plane is 8bits deep. BITMAPINFOHEADER bi = new BITMAPINFOHEADER(); bi.Size = (uint)Marshal.SizeOf(bi); bi.Width = w; bi.Height = h; bi.Planes = 1; //always 1. bi.BitCount = bpp; bi.Compression = comp; //RGB is zero.. uncompressed. bi.SizeImage = (uint)(w * h * bpp / 8); bi.XPelsPerMeter = 0; bi.YPelsPerMeter = 0; bi.ClrUsed = 0; bi.ClrImportant = 0; // WMVIDEOINFOHEADER // RECT rcSource; // RECT rcTarget; // DWORD dwBitRate.. bps.. Width*Height*BitCount*Rate.. 320*240*24*29.93295=55172414 // DWORD dwBitErrorRate zero in our sample. // LONGLONG AvgTimePerFrame in 100ns units.. 334080=10000*1000/29.93295 // BITMAPINFOHEADER bmiHeader copy of the above struct. VIDEOINFOHEADER vi = new VIDEOINFOHEADER(); RECT r = new RECT(); r.Left = r.Top = 0; r.Bottom = bi.Height; r.Right = bi.Width; vi.Source = r; // vi.Source.Left = 0; // vi.Source.Top = 0; // vi.Source.Bottom = bi.Height; // vi.Source.Right = bi.Width; vi.Target = vi.Source; vi.BitRate = (uint)(w * h * bpp * fps); vi.BitErrorRate = 0; vi.AvgTimePerFrame = (long)((10000 * 1000) / fps); vi.BitmapInfo = bi; IntPtr viPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(vi)); Marshal.StructureToPtr(vi, viPtr, true); // WM_MEDIA_TYPE // GUID majortype WMMEDIATYPE_Video // GUID subtype WMMEDIASUBTYPE_RGB24 in our sample // BOOL bFixedSizeSamples TRUE // BOOL bTemporalCompression FALSE // ULONG lSampleSize in bytes This was zero in our sample, but could be 320*240*24/8=230400 // GUID formattype WMFORMAT_VideoInfo // IUnknown* pUnk NULL // ULONG cbFormat size of the WMVIDEOINFOHEADER // [size_is(cbFormat)] BYTE *pbFormat pointer to the WMVIDEOINFOHEADER //Note WM_MEDIA_TYPE is the same as Directshow's AM_MEDIA_TYPE. //WM_MEDIA_TYPE mt; _WMMediaType mt = new _WMMediaType(); mt.majortype = WMGuids.ToGUID(WMGuids.WMMEDIATYPE_Video); mt.subtype = stype; mt.bFixedSizeSamples = 1; mt.bTemporalCompression = 0; //mt.lSampleSize = w * h * bpp / 8; // this was zero in avinetwrite! mt.lSampleSize = 0; //hmm. Don't think it matters?? mt.formattype = WMGuids.ToGUID(WMGuids.WMFORMAT_VideoInfo); mt.pUnk = null; mt.cbFormat = (uint)Marshal.SizeOf(vi); mt.pbFormat = viPtr; bool ret = ConfigVideo(mt); Marshal.FreeCoTaskMem(viPtr); return(ret); }
/// <summary> /// Load a WM Profile (system or custom). /// </summary> /// <param name="prxFile"></param> /// <param name="prIndex"></param> /// <returns></returns> public bool ConfigProfile(String prxFile, uint prIndex) { IWMProfile profile; uint hr = WMFSDKFunctions.WMCreateProfileManager(out profileManager); if (prxFile == "") { //use system profile Guid prg = ProfileIndexToGuid(prIndex); if (prg == Guid.Empty) { profile = null; Debug.WriteLine("Unsupported Profile index."); return(false); } try { GUID prG = WMGuids.ToGUID(prg); profileManager.LoadProfileByID(ref prG, out profile); } catch (Exception e) { eventLog.WriteEntry("Failed to load system profile: " + e.ToString(), EventLogEntryType.Error, 1000); Debug.WriteLine("Failed to load system profile: " + e.ToString()); profile = null; return(false); } } else { //use custom profile profile = LoadCustomProfile(prxFile); if (profile == null) { return(false); } } /// Tell the writer to use this profile. try { writer.SetProfile(profile); string name = GetProfileName(profile); Debug.WriteLine("Using profile: " + name); } catch (Exception e) { eventLog.WriteEntry("Failed to set writer profile: " + e.ToString(), EventLogEntryType.Error, 1000); Debug.WriteLine("Failed to set writer profile: " + e.ToString()); profile = null; return(false); } /// A slightly confusing point: Streams are subobjects of the profile, /// while inputs are subobjects of the Writer. The difference is in the /// multi-bitrate scenario where there may be multiple streams per input. /// Stream numbers start with 1, while input numbers and stream indexes begin at 0. /// If we have a profile that supports scripts, we need the stream number of /// the script stream. For audio and video, we just need input number. scriptBitrate = 0; audioInput = videoInput = 0; scriptStreamNumber = 0; audioProps = videoProps = null; /// If the profile has a script stream, find the bitrate and stream number. uint cStreams; IWMStreamConfig streamConfig; GUID streamType; profile.GetStreamCount(out cStreams); for (uint i = 0; i < cStreams; i++) { profile.GetStream(i, out streamConfig); streamConfig.GetStreamType(out streamType); if (WMGuids.ToGuid(streamType) == WMGuids.WMMEDIATYPE_Script) { streamConfig.GetStreamNumber(out scriptStreamNumber); streamConfig.GetBitrate(out scriptBitrate); } } /// Iterate over writer inputs, holding on to the IWMInputMediaProps* for each, /// so we can later configure them. Also save input numbers for audio and video here. uint cInputs; writer.GetInputCount(out cInputs); GUID guidInputType; IWMInputMediaProps inputProps = null; for (uint i = 0; i < cInputs; i++) { writer.GetInputProps(i, out inputProps); inputProps.GetType(out guidInputType); if (WMGuids.ToGuid(guidInputType) == WMGuids.WMMEDIATYPE_Audio) { audioProps = inputProps; audioInput = i; } else if (WMGuids.ToGuid(guidInputType) == WMGuids.WMMEDIATYPE_Video) { videoProps = inputProps; videoInput = i; } else if (WMGuids.ToGuid(guidInputType) == WMGuids.WMMEDIATYPE_Script) { } else { Debug.WriteLine("Profile contains unrecognized media type."); return(false); } } // We require an audio input, since that drives the timing for the whole stream. if (audioProps == null) { Debug.WriteLine("Profile should contain at least one audio input."); return(false); } return(true); }