/// <summary> /// ビデオキャプチャデバイスがサポートするメディアタイプ・サイズを取得する。 /// </summary> private static VideoFormat[] GetVideoOutputFormat(DirectShow.IPin pin) { // IAMStreamConfigインタフェース取得 if (!(pin is DirectShow.IAMStreamConfig config)) { throw new InvalidOperationException("no IAMStreamConfig interface."); } // フォーマット個数取得 int cap_count = 0, cap_size = 0; config.GetNumberOfCapabilities(ref cap_count, ref cap_size); if (cap_size != Marshal.SizeOf(typeof(DirectShow.VIDEO_STREAM_CONFIG_CAPS))) { throw new InvalidOperationException("no VIDEO_STREAM_CONFIG_CAPS."); } // 返却値の確保 var result = new VideoFormat[cap_count]; // データ用領域確保 var cap_data = Marshal.AllocHGlobal(cap_size); // 列挙 for (var i = 0; i < cap_count; i++) { var entry = new VideoFormat(); // x番目のフォーマット情報取得 DirectShow.AM_MEDIA_TYPE mt = null; config.GetStreamCaps(i, ref mt, cap_data); entry.Caps = PtrToStructure <DirectShow.VIDEO_STREAM_CONFIG_CAPS>(cap_data); // フォーマット情報の読み取り entry.MajorType = DirectShow.DsGuid.GetNickname(mt.MajorType); entry.SubType = DirectShow.DsGuid.GetNickname(mt.SubType); if (mt.FormatType == DirectShow.DsGuid.FORMAT_VideoInfo) { var vinfo = PtrToStructure <DirectShow.VIDEOINFOHEADER>(mt.pbFormat); entry.Size = new Size(vinfo.bmiHeader.biWidth, vinfo.bmiHeader.biHeight); entry.TimePerFrame = vinfo.AvgTimePerFrame; } else if (mt.FormatType == DirectShow.DsGuid.FORMAT_VideoInfo2) { var vinfo = PtrToStructure <DirectShow.VIDEOINFOHEADER2>(mt.pbFormat); entry.Size = new Size(vinfo.bmiHeader.biWidth, vinfo.bmiHeader.biHeight); entry.TimePerFrame = vinfo.AvgTimePerFrame; } // 解放 DirectShow.DeleteMediaType(ref mt); result[i] = entry; } // 解放 Marshal.FreeHGlobal(cap_data); return(result); }
/// <summary> /// ビデオキャプチャデバイスの出力形式を選択する。 /// 事前にGetVideoOutputFormatでメディアタイプ・サイズを得ておき、その中から希望のindexを指定する。 /// 同時に出力サイズとフレームレートを変更することができる。 /// </summary> /// <param name="index">希望のindexを指定する</param> /// <param name="size">Empty以外を指定すると出力サイズを変更する。事前にVIDEO_STREAM_CONFIG_CAPSで取得した可能範囲内を指定すること。</param> /// <param name="timePerFrame">0以上を指定するとフレームレートを変更する。事前にVIDEO_STREAM_CONFIG_CAPSで取得した可能範囲内を指定すること。</param> private static void SetVideoOutputFormat(DirectShow.IPin pin, int index, Size size, long timePerFrame) { // IAMStreamConfigインタフェース取得 var config = pin as DirectShow.IAMStreamConfig; if (config == null) { throw new InvalidOperationException("no IAMStreamConfig interface."); } // フォーマット個数取得 int cap_count = 0, cap_size = 0; config.GetNumberOfCapabilities(ref cap_count, ref cap_size); if (cap_size != Marshal.SizeOf(typeof(DirectShow.VIDEO_STREAM_CONFIG_CAPS))) { throw new InvalidOperationException("no VIDEO_STREAM_CONFIG_CAPS."); } // データ用領域確保 var cap_data = Marshal.AllocHGlobal(cap_size); // idx番目のフォーマット情報取得 DirectShow.AM_MEDIA_TYPE mt = null; config.GetStreamCaps(index, ref mt, cap_data); var cap = PtrToStructure <DirectShow.VIDEO_STREAM_CONFIG_CAPS>(cap_data); if (mt.FormatType == DirectShow.DsGuid.FORMAT_VideoInfo) { var vinfo = PtrToStructure <DirectShow.VIDEOINFOHEADER>(mt.pbFormat); if (!size.IsDefault) { vinfo.bmiHeader.biWidth = (int)size.Width; vinfo.bmiHeader.biHeight = (int)size.Height; } if (timePerFrame > 0) { vinfo.AvgTimePerFrame = timePerFrame; } Marshal.StructureToPtr(vinfo, mt.pbFormat, true); } else if (mt.FormatType == DirectShow.DsGuid.FORMAT_VideoInfo2) { var vinfo = PtrToStructure <DirectShow.VIDEOINFOHEADER2>(mt.pbFormat); if (!size.IsDefault) { vinfo.bmiHeader.biWidth = (int)size.Width; vinfo.bmiHeader.biHeight = (int)size.Height; } if (timePerFrame > 0) { vinfo.AvgTimePerFrame = timePerFrame; } Marshal.StructureToPtr(vinfo, mt.pbFormat, true); } // フォーマットを選択 config.SetFormat(mt); // 解放 if (cap_data != System.IntPtr.Zero) { Marshal.FreeHGlobal(cap_data); } if (mt != null) { DirectShow.DeleteMediaType(ref mt); } }
/// <summary> /// ビデオキャプチャデバイスの出力形式を選択する。 /// </summary> private static void SetVideoOutputFormat(DirectShow.IPin pin, VideoFormat format) { var formats = GetVideoOutputFormat(pin); // 仕様ではVideoCaptureDeviceはメディア タイプごとに一定範囲の出力フォーマットをサポートできる。例えば以下のように。 // [0]:YUY2 最小:160x120, 最大:320x240, X軸4STEP, Y軸2STEPごと // [1]:RGB8 最小:640x480, 最大:640x480, X軸0STEP, Y軸0STEPごと // SetFormatで出力サイズとフレームレートをこの範囲内で設定可能。 // ただし試した限り、手持ちのUSBカメラはすべてサイズ固定(最大・最小が同じ)で返してきた。 // https://msdn.microsoft.com/ja-jp/windows/dd407352(v=vs.80) // VIDEO_STREAM_CONFIG_CAPSの以下を除くほとんどのメンバーはdeprecated(非推奨)である。 // アプリケーションはその他のメンバーの利用を避けること。かわりにIAMStreamConfig::GetFormatを利用すること。 // - Guid:FORMAT_VideoInfo or FORMAT_VideoInfo2など。 // - VideoStandard:アナログTV信号のフォーマット(NTSC, PALなど)をAnalogVideoStandard列挙体で指定する。 // - MinFrameInterval, MaxFrameInterval:ビデオキャプチャデバイスがサポートするフレームレートの範囲。100ナノ秒単位。 // 上記によると、VIDEO_STREAM_CONFIG_CAPSは現在はdeprecated(非推奨)であるらしい。かわりにIAMStreamConfig::GetFormatを使用することらしい。 // 上記仕様を守ったデバイスは出力サイズを固定で返すが、守ってない古いデバイスは出力サイズを可変で返す、と考えられる。 // 参考までに、VIDEO_STREAM_CONFIG_CAPSで解像度・クロップサイズ・フレームレートなどを変更する手順は以下の通り。 // ①フレームレート(これは非推奨ではない) // VIDEO_STREAM_CONFIG_CAPS のメンバ MinFrameInterval と MaxFrameInterval は各ビデオ フレームの最小の長さと最大の長さである。 // 次の式を使って、これらの値をフレーム レートに変換できる。 // frames per second = 10,000,000 / frame duration // 特定のフレーム レートを要求するには、メディア タイプにある構造体 VIDEOINFOHEADER か VIDEOINFOHEADER2 の AvgTimePerFrame の値を変更する。 // デバイスは最小値と最大値の間で可能なすべての値はサポートしていないことがあるため、ドライバは使用可能な最も近い値を使う。 // ②Cropping(画像の一部切り抜き) // MinCroppingSize = (160, 120) // Cropping最小サイズ。 // MaxCroppingSize = (320, 240) // Cropping最大サイズ。 // CropGranularityX = 4 // 水平方向細分度。 // CropGranularityY = 8 // 垂直方向細分度。 // CropAlignX = 2 // the top-left corner of the source rectangle can sit. // CropAlignY = 4 // the top-left corner of the source rectangle can sit. // ③出力サイズ // https://msdn.microsoft.com/ja-jp/library/cc353344.aspx // https://msdn.microsoft.com/ja-jp/library/cc371290.aspx // VIDEO_STREAM_CONFIG_CAPS 構造体は、このメディア タイプに使える最小と最大の幅と高さを示す。 // また、"ステップ" サイズ"も示す。ステップ サイズは、幅または高さを調整できるインクリメントの値を定義する。 // たとえば、デバイスは次の値を返すことがある。 // MinOutputSize: 160 × 120 // MaxOutputSize: 320 × 240 // OutputGranularityX:8 ピクセル (水平ステップ サイズ) // OutputGranularityY:8 ピクセル (垂直ステップ サイズ) // これらの数値が与えられると、幅は範囲内 (160、168、176、... 304、312、320) の任意の値に、 // 高さは範囲内 (120、128、136、... 224、232、240) の任意の値に設定できる。 // 出力サイズの可変のUSBカメラがないためデバッグするには以下のコメントを外す。 // I have no USB camera of variable output size, uncomment below to debug. //size = new Size(168, 126); //vformat[0].Caps = new DirectShow.VIDEO_STREAM_CONFIG_CAPS() //{ // Guid = DirectShow.DsGuid.FORMAT_VideoInfo, // MinOutputSize = new DirectShow.SIZE() { cx = 160, cy = 120 }, // MaxOutputSize = new DirectShow.SIZE() { cx = 320, cy = 240 }, // OutputGranularityX = 4, // OutputGranularityY = 2 //}; // VIDEO_STREAM_CONFIG_CAPSは現在では非推奨。まずは固定サイズを探す // VIDEO_STREAM_CONFIG_CAPS is deprecated. First, find just the fixed size. for (var i = 0; i < formats.Length; i++) { var item = formats[i]; // VideoInfoのみ対応する。(VideoInfo2はSampleGrabber未対応のため) // VideoInfo only... (SampleGrabber do not support VideoInfo2) // https://msdn.microsoft.com/ja-jp/library/cc370616.aspx if (item.MajorType != DirectShow.DsGuid.GetNickname(DirectShow.DsGuid.MEDIATYPE_Video)) { continue; } if (string.IsNullOrEmpty(format.SubType) == false && format.SubType != item.SubType) { continue; } if (item.Caps.Guid != DirectShow.DsGuid.FORMAT_VideoInfo) { continue; } if (item.Size.Width == format.Size.Width && item.Size.Height == format.Size.Height) { SetVideoOutputFormat(pin, i, format.Size, format.TimePerFrame); return; } } // 固定サイズが見つからなかった。可変サイズの範囲を探す。 // Not found fixed size, search for variable size. for (var i = 0; i < formats.Length; i++) { var item = formats[i]; // VideoInfoのみ対応する。(VideoInfo2はSampleGrabber未対応のため) // VideoInfo only... (SampleGrabber do not support VideoInfo2) // https://msdn.microsoft.com/ja-jp/library/cc370616.aspx if (item.MajorType != DirectShow.DsGuid.GetNickname(DirectShow.DsGuid.MEDIATYPE_Video)) { continue; } if (string.IsNullOrEmpty(format.SubType) == false && format.SubType != item.SubType) { continue; } if (item.Caps.Guid != DirectShow.DsGuid.FORMAT_VideoInfo) { continue; } if (item.Caps.OutputGranularityX == 0) { continue; } if (item.Caps.OutputGranularityY == 0) { continue; } for (var w = item.Caps.MinOutputSize.cx; w < item.Caps.MaxOutputSize.cx; w += item.Caps.OutputGranularityX) { for (var h = item.Caps.MinOutputSize.cy; h < item.Caps.MaxOutputSize.cy; h += item.Caps.OutputGranularityY) { if (w == format.Size.Width && h == format.Size.Height) { SetVideoOutputFormat(pin, i, format.Size, format.TimePerFrame); return; } } } } // サイズが見つかなかった場合はデフォルトサイズとする。 // Not found, use default size. SetVideoOutputFormat(pin, 0, Size.Empty, 0); }