作者
QQ群:852283276
微信:arm80x86
微信公众号:青儿创客基地
B站:主页 https://space.bilibili.com/208826118
参考
微软官网:Docs\Windows Hardware\Windows Drivers\Device and Driver Technologies\Stream
Source Filter属性页一台电脑显示,一台不显示
AVStream ddk 翻译
流Mini驱动开发(译自Microsoft DDK)
硬件模型
工具使用
点击菜单Graph->插入Filter,添加Video Capture Sources下的avsadma Source,添加DirectShow Filters下的Video Renderer,用鼠标将两者连接起来,如下图所示,点击工具栏上的三角开始捕获视频。
AVStream架构
avshws示例
DriverEntry中注册KSDEVICE_DESCRIPTOR,KSDEVICE_DESCRIPTOR包含KSDEVICE_DISPATCH,KSDEVICE_DISPATCH是用户需要实现的函数接口
struct _KSDEVICE_DISPATCH {
PFNKSDEVICECREATE Add;
PFNKSDEVICEPNPSTART Start;
PFNKSDEVICE PostStart;
PFNKSDEVICEIRP QueryStop;
PFNKSDEVICEIRPVOID CancelStop;
PFNKSDEVICEIRPVOID Stop;
PFNKSDEVICEIRP QueryRemove;
PFNKSDEVICEIRPVOID CancelRemove;
PFNKSDEVICEIRPVOID Remove;
PFNKSDEVICEQUERYCAPABILITIES QueryCapabilities;
PFNKSDEVICEIRPVOID SurpriseRemoval;
PFNKSDEVICEQUERYPOWER QueryPower;
PFNKSDEVICESETPOWER SetPower;
PFNKSDEVICEIRP QueryInterface; // added in version 0x100
};
示例中实现了三个接口:Add(DispatchCreate),Start(DispatchPnpStart)和Stop(DispatchPnpStop)。这和PCIe驱动开发差不多,在DispatchCreate中分配Device Context,
KsAcquireDevice (Device);
Status = KsAddItemToObjectBag (
Device -> Bag,
reinterpret_cast <PVOID> (CapDevice),
reinterpret_cast <PFNKSFREE> (CCaptureDevice::Cleanup)
);
KsReleaseDevice (Device);
if (!NT_SUCCESS (Status)) {
delete CapDevice;
} else {
Device -> Context = reinterpret_cast <PVOID> (CapDevice);
}
DispatchPnpStart类似于EvtDevicePrepareHardware函数,提供了Resource List。在这里完成PCIe video采集卡设备资源(内存、IO)的申请。
static NTSTATUS DispatchPnpStart (
IN PKSDEVICE Device,
IN PIRP Irp,
IN PCM_RESOURCE_LIST TranslatedResourceList,
IN PCM_RESOURCE_LIST UntranslatedResourceList
)
创建Pin对应的FilterFactory,KsCreateFilterFactory的第二个参数,传入FilterDescriptor,
if (!m_Device -> Started) {
// Create the Filter for the device
KsAcquireDevice(m_Device);
Status = KsCreateFilterFactory( m_Device->FunctionalDeviceObject,
&CaptureFilterDescriptor,
L"GLOBAL",
NULL,
KSCREATE_ITEM_FREEONSTOP,
NULL,
NULL,
NULL );
KsReleaseDevice(m_Device);
}
在Pin的DispatchCreate中分配图像帧缓存,
Status = KsEdit (
Pin,
&Pin -> Descriptor -> AllocatorFraming,
AVSHWS_POOLTAG);
if (NT_SUCCESS (Status)) {
//
// We've KsEdit'ed this... I'm safe to cast away constness as
// long as the edit succeeded.
//
PKSALLOCATOR_FRAMING_EX Framing =
const_cast <PKSALLOCATOR_FRAMING_EX> (
Pin -> Descriptor -> AllocatorFraming
);
Framing -> FramingItem [0].Frames = 2;
//
// The physical and optimal ranges must be biSizeImage. We only
// support one frame size, precisely the size of each capture
// image.
//
Framing -> FramingItem [0].PhysicalRange.MinFrameSize =
Framing -> FramingItem [0].PhysicalRange.MaxFrameSize =
Framing -> FramingItem [0].FramingRange.Range.MinFrameSize =
Framing -> FramingItem [0].FramingRange.Range.MaxFrameSize =
VideoInfoHeader -> bmiHeader.biSizeImage;
Framing -> FramingItem [0].PhysicalRange.Stepping =
Framing -> FramingItem [0].FramingRange.Range.Stepping =
0;
}
在Pin的DispatchProcess中处理图像帧,参考Stream Pointers,参考Writing AVStream Minidrivers for Hardware
结构体说明
KSDEVICE,在WDM驱动程序框架的AddDevice例程中,IoCreateDevice通过传入的PhysicalDeviceObject创建FunctionalDeviceObject。
typedef struct _KSDEVICE {
const KSDEVICE_DESCRIPTOR *Descriptor;
KSOBJECT_BAG Bag;
PVOID Context;
PDEVICE_OBJECT FunctionalDeviceObject;
PDEVICE_OBJECT PhysicalDeviceObject;
PDEVICE_OBJECT NextDeviceObject;
BOOLEAN Started;
SYSTEM_POWER_STATE SystemPowerState;
DEVICE_POWER_STATE DevicePowerState;
} KSDEVICE, *PKSDEVICE;
typedef struct _KSPIN {
const KSPIN_DESCRIPTOR_EX *Descriptor;
KSOBJECT_BAG Bag;
PVOID Context;
ULONG Id;
KSPIN_COMMUNICATION Communication;
BOOLEAN ConnectionIsExternal;
KSPIN_INTERFACE ConnectionInterface;
KSPIN_MEDIUM ConnectionMedium;
KSPRIORITY ConnectionPriority;
PKSDATAFORMAT ConnectionFormat;
PKSMULTIPLE_ITEM AttributeList;
ULONG StreamHeaderSize;
KSPIN_DATAFLOW DataFlow;
KSSTATE DeviceState;
KSRESET ResetState;
KSSTATE ClientState;
} KSPIN, *PKSPIN;
KSPIN 中的KSDATAFORMAT ,注意KSDATAFORMAT 和KSDATARANGE 是同一个结构体,
#if !defined( _MSC_VER )
typedef struct {
ULONG FormatSize;
ULONG Flags;
ULONG SampleSize;
ULONG Reserved;
GUID MajorFormat;
GUID SubFormat;
GUID Specifier;
} KSDATAFORMAT, *PKSDATAFORMAT, KSDATARANGE, *PKSDATARANGE;
#else
typedef union {
struct {
ULONG FormatSize;
ULONG Flags;
ULONG SampleSize;
ULONG Reserved;
GUID MajorFormat;
GUID SubFormat;
GUID Specifier;
};
LONGLONG Alignment;
} KSDATAFORMAT, *PKSDATAFORMAT, KSDATARANGE, *PKSDATARANGE;
#endif
KSDATAFORMAT 指向一个具体的DATAFORMAT比如KS_DATAFORMAT_VIDEOINFOHEADER
typedef struct tagKS_DATAFORMAT_VIDEOINFOHEADER {
KSDATAFORMAT DataFormat;
KS_VIDEOINFOHEADER VideoInfoHeader;
} KS_DATAFORMAT_VIDEOINFOHEADER, *PKS_DATAFORMAT_VIDEOINFOHEADER;
typedef struct _KSFILTER_DESCRIPTOR {
const KSFILTER_DISPATCH *Dispatch;
const KSAUTOMATION_TABLE *AutomationTable;
ULONG Version;
ULONG Flags;
const GUID *ReferenceGuid;
ULONG PinDescriptorsCount;
ULONG PinDescriptorSize;
const KSPIN_DESCRIPTOR_EX *PinDescriptors;
ULONG CategoriesCount;
const GUID *Categories;
ULONG NodeDescriptorsCount;
ULONG NodeDescriptorSize;
const KSNODE_DESCRIPTOR *NodeDescriptors;
ULONG ConnectionsCount;
const KSTOPOLOGY_CONNECTION *Connections;
const KSCOMPONENTID *ComponentId;
} KSFILTER_DESCRIPTOR, *PKSFILTER_DESCRIPTOR;
KSFILTER_DESCRIPTOR 中的KSFILTER_DISPATCH 是Filter的回调函数,在Create函数中创建Filter。
typedef struct _KSFILTER_DISPATCH {
PFNKSFILTERIRP Create;
PFNKSFILTERIRP Close;
PFNKSFILTERPROCESS Process;
PFNKSFILTERVOID Reset;
} KSFILTER_DISPATCH, *PKSFILTER_DISPATCH;
KSFILTER_DESCRIPTOR 中的KSPIN_DESCRIPTOR_EX
typedef struct _KSPIN_DESCRIPTOR_EX {
const KSPIN_DISPATCH *Dispatch;
const KSAUTOMATION_TABLE *AutomationTable;
KSPIN_DESCRIPTOR PinDescriptor;
ULONG Flags;
ULONG InstancesPossible;
ULONG InstancesNecessary;
const KSALLOCATOR_FRAMING_EX *AllocatorFraming;
PFNKSINTERSECTHANDLEREX IntersectHandler;
} KSPIN_DESCRIPTOR_EX, *PKSPIN_DESCRIPTOR_EX;
KSPIN_DESCRIPTOR_EX 中的KSPIN_DISPATCH
typedef struct _KSPIN_DISPATCH {
PFNKSPINIRP Create;
PFNKSPINIRP Close;
PFNKSPIN Process;
PFNKSPINVOID Reset;
PFNKSPINSETDATAFORMAT SetDataFormat;
PFNKSPINSETDEVICESTATE SetDeviceState;
PFNKSPIN Connect;
PFNKSPINVOID Disconnect;
const KSCLOCK_DISPATCH *Clock;
const KSALLOCATOR_DISPATCH *Allocator;
} KSPIN_DISPATCH, *PKSPIN_DISPATCH;
KSPIN_DESCRIPTOR_EX 中的KSPIN_DESCRIPTOR
typedef struct KSPIN_DESCRIPTOR {
ULONG InterfacesCount;
const KSPIN_INTERFACE *Interfaces;
ULONG MediumsCount;
const KSPIN_MEDIUM *Mediums;
ULONG DataRangesCount;
const PKSDATARANGE *DataRanges;
KSPIN_DATAFLOW DataFlow;
KSPIN_COMMUNICATION Communication;
const GUID *Category;
const GUID *Name;
union {
LONGLONG Reserved;
struct {
ULONG ConstrainedDataRangesCount;
PKSDATARANGE *ConstrainedDataRanges;
};
};
} *PKSPIN_DESCRIPTOR;
KSPIN_DESCRIPTOR 中的KSDATARANGE,关于MajorFormat,SubFormat(Uncompressed RGB Video Subtypes,YUV Video Subtypes),Specifier参考Stream Categories
typedef union {
struct {
ULONG FormatSize;
ULONG Flags;
ULONG SampleSize;
ULONG Reserved;
GUID MajorFormat;
GUID SubFormat;
GUID Specifier;
};
LONGLONG Alignment;
} KSDATAFORMAT, *PKSDATAFORMAT, KSDATARANGE, *PKSDATARANGE;
SubFormat GUID和FOURCC Codes,Video FOURCCs 有关,FOURCC Codes可以转换成Subtype GUIDs(参考FOURCC Codes)
MAKEFOURCC('Y','U','Y','2');
实际使用中,使用包含KSDATARANGE的数据类型,比如KS_DATARANGE_VIDEO ,
typedef struct tagKS_DATARANGE_VIDEO {
KSDATARANGE DataRange;
BOOL bFixedSizeSamples;
BOOL bTemporalCompression;
DWORD StreamDescriptionFlags;
DWORD MemoryAllocationFlags;
KS_VIDEO_STREAM_CONFIG_CAPS ConfigCaps;
KS_VIDEOINFOHEADER VideoInfoHeader;
} KS_DATARANGE_VIDEO, *PKS_DATARANGE_VIDEO;
通过DataRange中的MajorFormat GUID标志是什么类型的DataRange,比如KS_DATARANGE_VIDEO 的MajorFormat GUID是
STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO)
KS_DATARANGE_VIDEO 中的KS_VIDEO_STREAM_CONFIG_CAPS
typedef struct _KS_VIDEO_STREAM_CONFIG_CAPS {
GUID guid;
ULONG VideoStandard;
SIZE InputSize;
SIZE MinCroppingSize;
SIZE MaxCroppingSize;
int CropGranularityX;
int CropGranularityY;
int CropAlignX;
int CropAlignY;
SIZE MinOutputSize;
SIZE MaxOutputSize;
int OutputGranularityX;
int OutputGranularityY;
int StretchTapsX;
int StretchTapsY;
int ShrinkTapsX;
int ShrinkTapsY;
LONGLONG MinFrameInterval;
LONGLONG MaxFrameInterval;
LONG MinBitsPerSecond;
LONG MaxBitsPerSecond;
} KS_VIDEO_STREAM_CONFIG_CAPS, *PKS_VIDEO_STREAM_CONFIG_CAPS;
KS_DATARANGE_VIDEO 中的KS_VIDEOINFOHEADER,初始化之后从KSPIN可获得。
typedef struct tagKS_VIDEOINFOHEADER {
RECT rcSource;
RECT rcTarget;
DWORD dwBitRate;
DWORD dwBitErrorRate;
REFERENCE_TIME AvgTimePerFrame;
KS_BITMAPINFOHEADER bmiHeader;
} KS_VIDEOINFOHEADER, *PKS_VIDEOINFOHEADER;
KS_VIDEOINFOHEADER 中的KS_BITMAPINFOHEADER
typedef struct tagKS_BITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} KS_BITMAPINFOHEADER, *PKS_BITMAPINFOHEADER;
#define DECLARE_SIMPLE_FRAMING_EX(FramingExName, MemoryType, Flags, Frames, Alignment, MinFrameSize, MaxFrameSize) \
const KSALLOCATOR_FRAMING_EX FramingExName = \
{\
1, \
0, \
{\
1, \
1, \
0 \
}, \
0, \
{\
{\
MemoryType, \
STATIC_KS_TYPE_DONT_CARE, \
0, \
0, \
Flags, \
Frames, \
Alignment, \
0, \
{\
0, \
(ULONG)-1, \
1 \
}, \
{\
{\
MinFrameSize, \
MaxFrameSize, \
1 \
}, \
0, \
0 \
}\
}\
}\
}
typedef struct KSALLOCATOR_FRAMING_EX {
ULONG CountItems;
ULONG PinFlags;
KS_COMPRESSION OutputCompression;
ULONG PinWeight;
KS_FRAMING_ITEM FramingItem[1];
} *PKSALLOCATOR_FRAMING_EX;
KSALLOCATOR_FRAMING_EX 中的KS_FRAMING_ITEM
typedef struct KS_FRAMING_ITEM {
GUID MemoryType;
GUID BusType;
ULONG MemoryFlags;
ULONG BusFlags;
ULONG Flags;
ULONG Frames;
union {
ULONG FileAlignment;
LONG FramePitch;
};
ULONG MemoryTypeWeight;
KS_FRAMING_RANGE PhysicalRange;
KS_FRAMING_RANGE_WEIGHTED FramingRange;
} *PKS_FRAMING_ITEM;
KS_FRAMING_ITEM 中的KS_FRAMING_RANGE
typedef struct KS_FRAMING_RANGE {
ULONG MinFrameSize;
ULONG MaxFrameSize;
ULONG Stepping;
} *PKS_FRAMING_RANGE;
KS_FRAMING_ITEM 中的KS_FRAMING_RANGE_WEIGHTED
typedef struct KS_FRAMING_RANGE_WEIGHTED {
KS_FRAMING_RANGE Range;
ULONG InPlaceWeight;
ULONG NotInPlaceWeight;
} *PKS_FRAMING_RANGE_WEIGHTED;
typedef struct _KSSTREAM_POINTER {
PVOID Context;
PKSPIN Pin;
PKSSTREAM_HEADER StreamHeader;
PKSSTREAM_POINTER_OFFSET Offset;
KSSTREAM_POINTER_OFFSET OffsetIn;
KSSTREAM_POINTER_OFFSET OffsetOut;
} KSSTREAM_POINTER, *PKSSTREAM_POINTER;
KSSTREAM_POINTER 中的KSSTREAM_HEADER
typedef struct KSSTREAM_HEADER {
ULONG Size;
ULONG TypeSpecificFlags;
KSTIME PresentationTime;
LONGLONG Duration;
ULONG FrameExtent;
ULONG DataUsed;
PVOID Data;
ULONG OptionsFlags;
ULONG Reserved;
} *PKSSTREAM_HEADER;
KSSTREAM_POINTER 中的KSSTREAM_POINTER_OFFSET,其中Data用于Common Buffer DMA in AVStream,Mappings用于Packet-based DMA in AVStream
typedef struct _KSSTREAM_POINTER_OFFSET {
union {
PUCHAR Data;
PKSMAPPING Mappings;
};
PUCHAR Data;
PVOID Alignment;
ULONG Count;
ULONG Remaining;
} KSSTREAM_POINTER_OFFSET, *PKSSTREAM_POINTER_OFFSET;
KSSTREAM_POINTER_OFFSET 中的KSMAPPING
typedef struct _KSMAPPING {
PHYSICAL_ADDRESS PhysicalAddress;
ULONG ByteCount;
ULONG Alignment;
} KSMAPPING, *PKSMAPPING;
函数说明
#define KsEdit(Object,PointerToPointer,Tag)\
_KsEdit(\
(Object)->Bag,\
(PVOID*)(PointerToPointer),\
sizeof(**(PointerToPointer)),\
sizeof(**(PointerToPointer)),\
(Tag))
KSDDKAPI NTSTATUS _KsEdit(
KSOBJECT_BAG ObjectBag,
PVOID *PointerToPointerToItem,
ULONG NewSize,
ULONG OldSize,
ULONG Tag
);