Ⅰ 華為閱讀的書籍存在哪個目錄
華為閱讀書籍的存儲路徑為:文件管理/本地/內部存儲/HwIReader/books/zyepub。
1、首先,打開手機中的"文件管理"。
Ⅱ win7怎樣使用kernel streaming
內核流(Kernel Streaming)驅動模型在多媒體方面應用的比較多,支持內核流模型的驅動能夠
向系統報告它的性能,以及能夠將數據高效,通用的傳遞。通俗的說,就是可以將攝像頭的數據直接傳遞到顯卡中顯示,而不需要通過應用層.它可以避免數據在應用層和內核層之間的傳遞,但是這對於上層來說是透明的;並且採用WDM 內核流模型還可以實現設備無關性,使得程序有很好的移植性和通用性。一般來說,QQ攝像頭就是使用基於WDM內核流的組件來實現的。所以可以在打開攝像頭的時候輕易的更換為給對方播放影音文件(在上層使用相同的組件和流程,僅僅更換了source filter)。在這里需要指明的是,minidriver一般是可以和硬體設備相關,但是也不一樣會和硬體設備相關,它在內核層同樣可以調用其他的組件,例如可以實現虛擬攝像頭之類的應用。
一般來說,硬體設備會提供一個KsProxy組件,這個組件能夠完成一些相應的擴展功能,同時,也可以將數據進行不同類別的傳送。上層應用程序能夠控制底層數據的流向,而不是將數據拷貝到應用層,然後再傳遞給內核層處理(這個和DirectX的處理有相似的地方,因為DirectShow曾經也是DirectX的一員)。
雖然現在微軟對於流內核結構進行了調整,新的流類型採用的是AVStream(下一次在敘述AVStream框架)。但是從目前來看,很多情況下仍然採用目前的方式來處理數據。
下面通過源代碼和數據類型的形式來講解一下這個驅動程序的框架結構。會盡量屏蔽代碼中關於具體設備的細節,專注於描述stream class的流程和結構:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1. 驅動程序的入口點:
NTSTATUS DriverEntry(__in PDRIVER_OBJECT DriverObject, __in PUNICODE_STRING
RegistryPath)
{
HW_INITIALIZATION_DATA HwInitData;
RtlZeroMemory( &HwInitData, sizeof(HW_INITIALIZATION_DATA) );
HwInitData.HwInitializationDataSize = sizeof(HwInitData); // 結構大小
HwInitData.HwInterrupt = NULL; // 硬體回調
HwInitData.HwReceivePacket = MyReceivePacket; // 控制回調
HwInitData.HwCancelPacket = MyCancelOnePacket; // 取消回調
HwInitData.HwRequestTimeoutHandler = MyTimeoutHandler; // 超時回調
HwInitData.DeviceExtensionSize = sizeof(MY_EXTENSION); // 設備擴展
HwInitData.PerStreamExtensionSize = sizeof(STREAMEX); // 流擴展
HwInitData.PerRequestExtensionSize = sizeof(IRB); // SRB大小
HwInitData.FilterInstanceExtensionSize = 0; // 安裝大小
HwInitData.BusMasterDMA = FALSE; // 匯流排DMA
HwInitData.Dma24BitAddresses = FALSE; // DMA地址
HwInitData.BufferAlignment = sizeof(ULONG) - 1; //
HwInitData.TurnOffSynchronization = TRUE;
HwInitData.DmaBufferSize = 0;
// 注冊流埠驅動和回調函數
return (StreamClassRegisterAdapter(DriverObject, RegistryPath, &HwInitData));
}
這里可以看得出,驅動入口點基本上只是向stream class注冊回調函數和信息。此處的設備
擴展和流擴展需要我們自己定義。
超時回調函數和取消回調函數本身並沒有做太過於特殊的事情,關鍵在於控制命令回調函數,它是接收上層,也就是stream class 發送的控制包,下面會詳細的講解控制命令回調函數:
2. MyReceivePacket函數:
VOID
MyReceivePacket(IN PHW_STREAM_REQUEST_BLOCK pSrb)
{
PIO_STACK_LOCATION IrpStack;
PMY_EXTENSION pDevExt = (PMY_EXTENSION) pSrb->HwDeviceExtension;
PAGED_CODE();
pSrb->Status = STATUS_SUCCESS;
switch (pSrb->Command) {
case SRB_INITIALIZE_DEVICE: // 初始化設備
break;
case SRB_INITIALIZATION_COMPLETE: // 初始化設備完成
break;
case SRB_GET_STREAM_INFO:// 獲取設備信息
break;
case SRB_OPEN_STREAM: // 打開流
break;
case SRB_CLOSE_STREAM: // 關閉流
break;
case SRB_SURPRISE_REMOVAL: // 移除設備
break;
case SRB_UNKNOWN_DEVICE_COMMAND: // 未知的命令
break;
case SRB_UNINITIALIZE_DEVICE: // 卸載設備
break;
case SRB_GET_DATA_INTERSECTION: // 獲取格式和范圍
break;
case SRB_CHANGE_POWER_STATE: // 改變電源狀態
break;
case SRB_GET_DEVICE_PROPERTY: // 獲取設備屬性
break;
case SRB_SET_DEVICE_PROPERTY: // 設置設備屬性
break;
case SRB_PAGING_OUT_DRIVER: // ?
break;
default:
pSrb->Status = STATUS_NOT_IMPLEMENTED;
break;
}
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension,
pSrb);
}
可以看出來的是,上層會通過向這個函數發送命令包,來控制設備的行為。這個埠驅動需要
自己決定從什麼拷貝來數據,或者怎麼向上層回復。
這里stream class的命令中,需要關注地方並不多,由於設備可能會是USB設備/1394設備/網路
組件/圖像採集卡,所以很難統一的給出一份具體的代碼.但是通過下面的幾個命令的講解,大家
應該很容易的構建出具體設備的代碼來:
2.1 初始化命令: 在設備的初始化階段,stream class 會依次發送下面的命令
SRB_INITIALIZE_DEVICE->SRB_GET_STREAM_INFO->SRB_INITIALIZATION_COMPLETE
一般來說,SRB_INITIALIZE_DEVICE命令主要是初始化設備擴展和屬性結構的初始化,
SRB_GET_STREAM_INFO命令則是向注冊表寫入自己的屬性,並提供相應的另一組回調函數給
Stream class,便於接受更微觀的控制;SRB_INITIALIZATION_COMPLETE命令一般是一個完成回調
的方式。
下面的代碼會揭示在SRB_GET_STREAM_INFO命令時候,一般會進行的處理:
typedef struct _HW_STREAM_HEADER {
ULONG NumberOfStreams; // 支持的流的數目
ULONG SizeOfHwStreamInformation; // 結構大小
ULONG NumDevPropArrayEntries; // 支持的屬性數組大小
PKSPROPERTY_SET DevicePropertiesArray; // 屬性數組
ULONG NumDevEventArrayEntries; // 支持的事件數組大小
PKSEVENT_SET DeviceEventsArray; // 事件數組
PKSTOPOLOGY Topology; //
PHW_EVENT_ROUTINE DeviceEventRoutine; // 超時時間
ULONG Reserved[2]; // 保留
} HW_STREAM_HEADER, *PHW_STREAM_HEADER;
typedef struct _HW_STREAM_INFORMATION {
ULONG NumberOfPossibleInstances; // 設備支持的流的數量
KSPIN_DATAFLOW DataFlow; // 數據流的方向
BOOLEAN DataAccessible; // 數據釋放是否能夠被看到
ULONG NumberOfFormatArrayEntries; // 支持的屬性信息
PKSDATARANGE* StreamFormatsArray; // 屬性信息數組
PVOID ClassReserved[4];
ULONG NumStreamPropArrayEntries; // 流媒體的支持屬性數組的下標
PKSPROPERTY_SET StreamPropertiesArray;// 屬性數組
ULONG NumStreamEventArrayEntries;
PKSEVENT_SET StreamEventsArray;
GUID* Category; // Pin范圍
GUID* Name; // Pin的名字
ULONG MediumsCount;
const KSPIN_MEDIUM* Mediums; // 媒體類型
BOOLEAN BridgeStream; // 允許流進行橋接?
ULONG Reserved[2];
} HW_STREAM_INFORMATION, *PHW_STREAM_INFORMATION;
VOID MyGetStreamInfo(IN PHW_STREAM_REQUEST_BLOCK Srb)
{
PHW_STREAM_HEADER StreamHeader = &(Srb->CommandData.StreamBuffer->StreamHeader);
PMY_EXTENSION pDevExt = (PMY_EXTENSION) Srb->HwDeviceExtension;
PHW_STREAM_INFORMATION StreamInfo = &(Srb->CommandData.StreamBuffer->StreamInfo);
PAGED_CODE();
ASSERT (Srb->NumberOfBytesToTransfer >=
sizeof (HW_STREAM_HEADER) +
sizeof (HW_STREAM_INFORMATION));
RtlZeroMemory(StreamHeader,
sizeof (HW_STREAM_HEADER) +
sizeof (HW_STREAM_INFORMATION));
StreamHeader->NumberOfStreams = 1;
StreamHeader->SizeOfHwStreamInformation = sizeof(HW_STREAM_INFORMATION);
StreamHeader->NumDevPropArrayEntries = pDevExt->ulPropSetSupported;
StreamHeader->DevicePropertiesArray = &pDevExt->VideoProcAmpSet;
StreamInfo->NumberOfPossibleInstances = 1;
StreamInfo->DataFlow = KSPIN_DATAFLOW_OUT;
StreamInfo->DataAccessible = TRUE;
StreamInfo->NumberOfFormatArrayEntries = pDevExt->ModeSupported;
StreamInfo->StreamFormatsArray = &pDevExt->MyStrmModes[0];
StreamInfo->NumStreamPropArrayEntries = NUMBER_VIDEO_STREAM_PROPERTIES;
StreamInfo->StreamPropertiesArray = (PKSPROPERTY_SET) VideoStreamProperties;
StreamInfo->Name = (GUID *) &PINNAME_VIDEO_CAPTURE;
StreamInfo->Category = (GUID *) &PINNAME_VIDEO_CAPTURE;
Srb->CommandData.StreamBuffer->StreamHeader.Topology = &Topology;
Srb->Status = STATUS_SUCCESS;
}
2.2 打開和關閉流: SRB_OPEN_STREAM/SRB_CLOSE_STREAM 命令,此處需要注意的就是一個協商的
過程了,因為此處上層和下層需要來協商進行哪種數據類型的傳遞。
下面的代碼片段屏蔽了硬體的具體相關細節,主要描述和stream class相關的部分:
VOID MyOpenStream(IN PHW_STREAM_REQUEST_BLOCK pSrb)
{
PIRB Irb;
ULONG nSize;
PMY_EXTENSION pDevExt;
PSTREAMEX pStrmEx;
PKS_DATAFORMAT_VIDEOINFOHEADER pKSDataFormat =
(PKS_DATAFORMAT_VIDEOINFOHEADER) pSrb->CommandData.OpenFormat;
PKS_VIDEOINFOHEADER pVideoInfoHdrRequested =
&pKSDataFormat->VideoInfoHeader;
PAGED_CODE();
Irb = (PIRB) pSrb->SRBExtension;
pDevExt = (PMY_EXTENSION) pSrb->HwDeviceExtension;
pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension;
// 緩存流擴展
pDevExt->pStrmEx = pStrmEx;
pSrb->Status = STATUS_SUCCESS;
// 確定哪些編號流被打開了。這些編號表明在流信息結構的偏移數組中被調用的
// 流信息適配器
//
// So:
// 0 - Video data from camera
//
// 0 - 從硬體出來的視頻數據
switch (pSrb->StreamObject->StreamNumber) {
case 0:
// 檢查設備是否在使用
// 找出格式,他們正試圖打開第一格式,此處一般採用的是循環對比的方式
// 來找到合適的媒體類型。
if (!AdapterVerifyFormat (pDevExt->ModeSupported, pDevExt->MyStrmModes, pKSDataFormat, pSrb->StreamObject->StreamNumber)) {
pDevExt->pStrmEx = NULL;
pSrb->Status = STATUS_INVALID_PARAMETER;
return;
}
// 初始化流擴展
InitializeStreamExtension(pDevExt, pSrb->StreamObject, pStrmEx);
// 使用我們的安全版本
if (!NT_SUCCESS(RTL_SAFE_KS_SIZE_VIDEOHEADER(pVideoInfoHdrRequested, &nSize))) {
pSrb->Status = STATUS_INTEGER_OVERFLOW;
return;
}
pStrmEx->pVideoInfoHeader = ExAllocatePoolWithTag(NonPagedPool, nSize, 'macd');
if (pStrmEx->pVideoInfoHeader == NULL) {
ASSERT(pStrmEx->pVideoInfoHeader != NULL);
pDevExt->pStrmEx = NULL;
pSrb->Status = STATUS_INSUFFICIENT_RESOURCES;
return;
}
// 拷貝媒體信息頭
RtlCopyMemory(
pStrmEx->pVideoInfoHeader,
pVideoInfoHdrRequested,
nSize);
// 分配硬體需要的資源
pSrb->Status = MyAllocateIsochResource(pDevExt, pSrb->SRBExtension, TRUE);
if (pSrb->Status) {
ExFreePool(pStrmEx->pVideoInfoHeader);
pStrmEx->pVideoInfoHeader = NULL;
pDevExt->pStrmEx = NULL;
pSrb->Status = STATUS_INSUFFICIENT_RESOURCES;
return;
}
// 提交控制回調/數據回調函數
pSrb->StreamObject->ReceiveDataPacket = (PHW_RECEIVE_STREAM_DATA_SRB) MyReceiveDataPacket;
pSrb->StreamObject->ReceiveControlPacket = (PHW_RECEIVE_STREAM_CONTROL_SRB) MyReceiveCtrlPacket;
if(pDevExt->bDevRemoved || pDevExt->bStopIsochCallback) {
pDevExt->bStopIsochCallback = FALSE;
pDevExt->bDevRemoved = FALSE;
}
// 初始化流擴展句柄信息
break;
default:
ASSERT(FALSE);
pDevExt->pStrmEx = NULL;
pSrb->Status = STATUS_INVALID_PARAMETER;
return;
}
pSrb->StreamObject->HwClockObject.ClockSupportFlags = 0;
// 我們不使用DMA方式
pSrb->StreamObject->Dma = FALSE;
pSrb->StreamObject->StreamHeaderMediaSpecific = sizeof(KS_FRAME_INFO);
// PIO 必須設置為mini驅動緩沖區使用邏輯定址,我們不打算控制這部分緩沖區
pSrb->StreamObject->Pio = FALSE;
// 將最後保存配置
(pDevExt, (PIRB) pSrb->SRBExtension);
ASSERT(pSrb->Status == STATUS_SUCCESS);
}
VOID MyCloseStream(IN PHW_STREAM_REQUEST_BLOCK pSrb)
{
PMY_EXTENSION pDevExt;
PSTREAMEX pStrmEx;
PIRB pIrb;
PAGED_CODE();
pSrb->Status = STATUS_SUCCESS;
pDevExt = (PMY_EXTENSION) pSrb->HwDeviceExtension;
ASSERT(pDevExt);
// 等待所有的未決工作完成
KeWaitForSingleObject( &pDevExt->PendingWorkItemEvent, Executive, KernelMode, FALSE, NULL );
pStrmEx = (PSTREAMEX)pDevExt->pStrmEx;
ASSERT(pStrmEx);
if(!pStrmEx ) {
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
return;
}
// pDevExt->Irb可能被釋放了,在HwUninitialize()中
// 由於某個原因,所以必須使用下面的
pIrb = (PIRB) pSrb->SRBExtension;
// 保存設備擴展信息
MySetPropertyValuesToRegistry(pDevExt);
// 釋放硬體資源
MyFreeIsochResource (pDevExt, pIrb, TRUE);
if(pStrmEx->pVideoInfoHeader) {
ExFreePool(pStrmEx->pVideoInfoHeader);
pStrmEx->pVideoInfoHeader = NULL;
}
pStrmEx->hMasterClock = 0;
// 如果輸入讀,那麼取消掉它們
if(pDevExt->PendingReadCount > 0) {
if( InterlockedExchange((PLONG)&pStrmEx->CancelToken, 1 ) == 0 ) {
MyCancelAllPackets(
pDevExt,
&pDevExt->PendingReadCount
);
}
}
pDevExt->pStrmEx = 0;
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
}
2.3 屬性設置: 屬性設置這一部分實際上都是通過特定的屬性表來實現的,它和硬體的相關性很大,一般採用DEFINE_KSPROPERTY_TABLE宏來實現對於屬性的封裝,這一部分可以查閱相應的資料即可實現。