mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1682 lines
48 KiB
1682 lines
48 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1998
|
|
|
|
Module Name:
|
|
|
|
msriffwv.c
|
|
|
|
Abstract:
|
|
|
|
Pin property support.
|
|
|
|
--*/
|
|
|
|
#include "msriffwv.h"
|
|
|
|
#define riffWAVE mmioFOURCC('W','A','V','E')
|
|
#define riffFMT mmioFOURCC('f','m','t',' ')
|
|
#define riffDATA mmioFOURCC('d','a','t','a')
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
NTSTATUS
|
|
riffRead(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize
|
|
);
|
|
NTSTATUS
|
|
riffGetExtent(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PULONGLONG Extent
|
|
);
|
|
NTSTATUS
|
|
riffGetPosition(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PULONGLONG Position
|
|
);
|
|
NTSTATUS
|
|
riffDescend(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN OUT LPMMCKINFO MmckInfo,
|
|
IN const MMCKINFO* MmckInfoParent,
|
|
IN ULONG DescendFlags
|
|
);
|
|
NTSTATUS
|
|
riffAscend(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN LPMMCKINFO MmckInfo
|
|
);
|
|
NTSTATUS
|
|
riffReadWaveFormat(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PKSDATAFORMAT_WAVEFORMATEX* WaveFormat,
|
|
OUT ULONGLONG* DataOffset,
|
|
OUT ULONGLONG* DataLength
|
|
);
|
|
NTSTATUS
|
|
InitializeRiffIoPin(
|
|
IN PIRP Irp,
|
|
IN PKSPIN_CONNECT Connect
|
|
);
|
|
NTSTATUS
|
|
InitializeWaveIoPin(
|
|
IN PIRP Irp,
|
|
IN PKSDATAFORMAT DataFormat
|
|
);
|
|
NTSTATUS
|
|
PinDispatchClose(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
NTSTATUS
|
|
ReadStream(
|
|
IN PIRP Irp
|
|
);
|
|
NTSTATUS
|
|
PinWaveDispatchIoControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
NTSTATUS
|
|
PinRiffDispatchIoControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
NTSTATUS
|
|
GetTimeFormat(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT GUID* TimeFormat
|
|
);
|
|
NTSTATUS
|
|
GetPresentationTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSTIME PresentationTime
|
|
);
|
|
NTSTATUS
|
|
SetPresentationTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
IN PKSTIME PresentationTime
|
|
);
|
|
NTSTATUS
|
|
GetPresentationExtent(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PULONGLONG PresentationExtent
|
|
);
|
|
NTSTATUS
|
|
SetState(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
IN PKSSTATE State
|
|
);
|
|
NTSTATUS
|
|
GetAcquireOrdering(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT BOOL* AcquireOrdering
|
|
);
|
|
NTSTATUS
|
|
GetAllocatorFraming(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSALLOCATOR_FRAMING Framing
|
|
);
|
|
NTSTATUS
|
|
SetStreamAllocator(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
IN PHANDLE AllocatorHandle
|
|
);
|
|
|
|
#pragma alloc_text(PAGE, riffRead)
|
|
#pragma alloc_text(PAGE, riffGetExtent)
|
|
#pragma alloc_text(PAGE, riffGetPosition)
|
|
#pragma alloc_text(PAGE, riffSetPosition)
|
|
#pragma alloc_text(PAGE, riffDescend)
|
|
#pragma alloc_text(PAGE, riffAscend)
|
|
#pragma alloc_text(PAGE, riffReadWaveFormat)
|
|
#pragma alloc_text(PAGE, InitializeRiffIoPin)
|
|
#pragma alloc_text(PAGE, InitializeWaveIoPin)
|
|
#pragma alloc_text(PAGE, PinDispatchCreate)
|
|
#pragma alloc_text(PAGE, PinDispatchClose)
|
|
#pragma alloc_text(PAGE, ReferenceRiffIoObject)
|
|
#pragma alloc_text(PAGE, ReadStream)
|
|
#pragma alloc_text(PAGE, PinWaveDispatchIoControl)
|
|
#pragma alloc_text(PAGE, PinRiffDispatchIoControl)
|
|
#pragma alloc_text(PAGE, GetTimeFormat)
|
|
#pragma alloc_text(PAGE, GetPresentationTime)
|
|
#pragma alloc_text(PAGE, SetPresentationTime)
|
|
#pragma alloc_text(PAGE, GetPresentationExtent)
|
|
#pragma alloc_text(PAGE, SetState)
|
|
#pragma alloc_text(PAGE, GetAcquireOrdering)
|
|
#pragma alloc_text(PAGE, GetAllocatorFraming)
|
|
#pragma alloc_text(PAGE, SetStreamAllocator)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg("PAGECONST")
|
|
#endif // ALLOC_DATA_PRAGMA
|
|
static DEFINE_KSDISPATCH_TABLE(
|
|
PinWaveIoDispatchTable,
|
|
PinWaveDispatchIoControl,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
PinDispatchClose,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
static DEFINE_KSDISPATCH_TABLE(
|
|
PinRiffIoDispatchTable,
|
|
PinRiffDispatchIoControl,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
PinDispatchClose,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
DEFINE_KSPROPERTY_TABLE(WaveIoStreamProperties) {
|
|
DEFINE_KSPROPERTY_ITEM_STREAM_PRESENTATIONTIME(GetPresentationTime, SetPresentationTime),
|
|
DEFINE_KSPROPERTY_ITEM_STREAM_PRESENTATIONEXTENT(GetPresentationExtent)
|
|
};
|
|
|
|
DEFINE_KSPROPERTY_TABLE(WaveIoConnectionProperties) {
|
|
DEFINE_KSPROPERTY_ITEM_CONNECTION_STATE(NULL, SetState),
|
|
DEFINE_KSPROPERTY_ITEM_CONNECTION_ACQUIREORDERING(GetAcquireOrdering)
|
|
};
|
|
|
|
DEFINE_KSPROPERTY_SET_TABLE(WaveIoPropertySets) {
|
|
DEFINE_KSPROPERTY_SET(
|
|
&KSPROPSETID_Stream,
|
|
SIZEOF_ARRAY(WaveIoStreamProperties),
|
|
WaveIoStreamProperties,
|
|
0,
|
|
NULL),
|
|
DEFINE_KSPROPERTY_SET(
|
|
&KSPROPSETID_Connection,
|
|
SIZEOF_ARRAY(WaveIoConnectionProperties),
|
|
WaveIoConnectionProperties,
|
|
0,
|
|
NULL)
|
|
};
|
|
|
|
DEFINE_KSPROPERTY_TABLE(RiffIoStreamProperties) {
|
|
DEFINE_KSPROPERTY_ITEM_STREAM_TIMEFORMAT(GetTimeFormat),
|
|
DEFINE_KSPROPERTY_ITEM_STREAM_ALLOCATOR(NULL, SetStreamAllocator)
|
|
};
|
|
|
|
DEFINE_KSPROPERTY_TABLE(RiffIoConnectionProperties) {
|
|
DEFINE_KSPROPERTY_ITEM_CONNECTION_ALLOCATORFRAMING(GetAllocatorFraming)
|
|
};
|
|
|
|
DEFINE_KSPROPERTY_SET_TABLE(RiffIoPropertySets) {
|
|
DEFINE_KSPROPERTY_SET(
|
|
&KSPROPSETID_Stream,
|
|
SIZEOF_ARRAY(RiffIoStreamProperties),
|
|
RiffIoStreamProperties,
|
|
0,
|
|
NULL),
|
|
DEFINE_KSPROPERTY_SET(
|
|
&KSPROPSETID_Connection,
|
|
SIZEOF_ARRAY(RiffIoConnectionProperties),
|
|
RiffIoConnectionProperties,
|
|
0,
|
|
NULL)
|
|
};
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma const_seg()
|
|
#endif // ALLOC_DATA_PRAGMA
|
|
|
|
|
|
NTSTATUS
|
|
riffRead(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
KSMETHOD Method;
|
|
ULONG BytesReturned;
|
|
|
|
Method.Set = KSMETHODSETID_StreamIo;
|
|
Method.Id = KSMETHOD_STREAMIO_READ;
|
|
Method.Flags = KSMETHOD_TYPE_SEND;
|
|
Status = KsSynchronousIoControlDevice(
|
|
FileObject,
|
|
KernelMode,
|
|
IOCTL_KS_METHOD,
|
|
&Method,
|
|
sizeof(Method),
|
|
Buffer,
|
|
BufferSize,
|
|
&BytesReturned);
|
|
if (NT_SUCCESS(Status) && (BytesReturned < BufferSize)) {
|
|
Status = STATUS_END_OF_FILE;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
riffGetExtent(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PULONGLONG Extent
|
|
)
|
|
{
|
|
KSPROPERTY Property;
|
|
ULONG BytesReturned;
|
|
|
|
Property.Set = KSPROPSETID_Stream;
|
|
Property.Id = KSPROPERTY_STREAM_PRESENTATIONEXTENT;
|
|
Property.Flags = KSPROPERTY_TYPE_GET;
|
|
return KsSynchronousIoControlDevice(
|
|
FileObject,
|
|
KernelMode,
|
|
IOCTL_KS_PROPERTY,
|
|
&Property,
|
|
sizeof(Property),
|
|
Extent,
|
|
sizeof(*Extent),
|
|
&BytesReturned);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
riffGetPosition(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PULONGLONG Position
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
KSPROPERTY Property;
|
|
ULONG BytesReturned;
|
|
KSTIME Time;
|
|
|
|
Property.Set = KSPROPSETID_Stream;
|
|
Property.Id = KSPROPERTY_STREAM_PRESENTATIONTIME;
|
|
Property.Flags = KSPROPERTY_TYPE_GET;
|
|
Status = KsSynchronousIoControlDevice(
|
|
FileObject,
|
|
KernelMode,
|
|
IOCTL_KS_PROPERTY,
|
|
&Property,
|
|
sizeof(Property),
|
|
&Time,
|
|
sizeof(Time),
|
|
&BytesReturned);
|
|
*Position = Time.Time;
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
riffSetPosition(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN ULONGLONG Position
|
|
)
|
|
{
|
|
KSPROPERTY Property;
|
|
ULONG BytesReturned;
|
|
KSTIME Time;
|
|
|
|
Property.Set = KSPROPSETID_Stream;
|
|
Property.Id = KSPROPERTY_STREAM_PRESENTATIONTIME;
|
|
Property.Flags = KSPROPERTY_TYPE_SET;
|
|
Time.Time = Position;
|
|
Time.Numerator = 1;
|
|
Time.Denominator = 1;
|
|
return KsSynchronousIoControlDevice(
|
|
FileObject,
|
|
KernelMode,
|
|
IOCTL_KS_PROPERTY,
|
|
&Property,
|
|
sizeof(Property),
|
|
&Time,
|
|
sizeof(Time),
|
|
&BytesReturned);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
riffDescend(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN OUT LPMMCKINFO MmckInfo,
|
|
IN const MMCKINFO* MmckInfoParent,
|
|
IN ULONG DescendFlags
|
|
)
|
|
{
|
|
FOURCC ckidFind;
|
|
FOURCC fccTypeFind;
|
|
|
|
if (DescendFlags & MMIO_FINDCHUNK) {
|
|
ckidFind = MmckInfo->ckid, fccTypeFind = 0;
|
|
} else if (DescendFlags & MMIO_FINDRIFF) {
|
|
ckidFind = FOURCC_RIFF, fccTypeFind = MmckInfo->fccType;
|
|
} else if (DescendFlags & MMIO_FINDLIST) {
|
|
ckidFind = FOURCC_LIST, fccTypeFind = MmckInfo->fccType;
|
|
} else {
|
|
ckidFind = fccTypeFind = 0;
|
|
}
|
|
MmckInfo->dwFlags = 0;
|
|
while (TRUE) {
|
|
NTSTATUS Status;
|
|
ULONGLONG Position;
|
|
|
|
//
|
|
// Read the chunk header.
|
|
//
|
|
Status = riffRead(FileObject, MmckInfo, sizeof(FOURCC) + sizeof(DWORD));
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Store the offset of the data part of the chunk.
|
|
//
|
|
Status = riffGetPosition(FileObject, &Position);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
if (Position > (ULONG)-1) {
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
MmckInfo->dwDataOffset = (ULONG)Position;
|
|
//
|
|
// Check for unreasonable chunk size. See if the chunk is within
|
|
// the parent chunk (if given).
|
|
//
|
|
if ((LONG)MmckInfo->cksize < 0) {
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
if ((MmckInfoParent != NULL) && ((MmckInfo->dwDataOffset - 8) >= (MmckInfoParent->dwDataOffset + MmckInfoParent->cksize))) {
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
//
|
|
// If the chunk if a 'RIFF' or 'LIST' chunk, read the form type or
|
|
// list type
|
|
//
|
|
if ((MmckInfo->ckid == FOURCC_RIFF) || (MmckInfo->ckid == FOURCC_LIST)) {
|
|
Status = riffRead(FileObject, &MmckInfo->fccType, sizeof(DWORD));
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
} else {
|
|
MmckInfo->fccType = 0;
|
|
}
|
|
//
|
|
// If this is the chunk, stop looking.
|
|
//
|
|
if ((!ckidFind || (ckidFind == MmckInfo->ckid)) && (!fccTypeFind || (fccTypeFind == MmckInfo->fccType))) {
|
|
break;
|
|
}
|
|
//
|
|
// Ascend out of the chunk and try again.
|
|
//
|
|
if (!NT_SUCCESS(Status = riffAscend(FileObject, MmckInfo))) {
|
|
return Status;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
riffAscend(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN LPMMCKINFO MmckInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Ascend out of a RIFF chunk, seeking to the end of that chunk.
|
|
|
|
Arguments:
|
|
|
|
FileObject -
|
|
The stream source.
|
|
|
|
MmckInfo -
|
|
Contains the chunk which is being ascended out of.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS if the RIFF ascend was successful, else
|
|
STATUS_DATA_NOT_ACCEPTED.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Seek to the end of the chunk, past the null pad byte which is only there
|
|
// if chunk size is odd.
|
|
//
|
|
return riffSetPosition(FileObject, MmckInfo->dwDataOffset + MmckInfo->cksize + (MmckInfo->cksize & 1));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
riffReadWaveFormat(
|
|
IN PFILE_OBJECT FileObject,
|
|
OUT PKSDATAFORMAT_WAVEFORMATEX* WaveFormat,
|
|
OUT ULONGLONG* DataOffset,
|
|
OUT ULONGLONG* DataLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read the wave format from the Riff stream. Resets the file position.
|
|
|
|
Arguments:
|
|
|
|
FileObject -
|
|
The stream source.
|
|
|
|
WaveFormat -
|
|
The place in which to put the pointer allocated to hold the Wave format
|
|
retrieved from the stream.
|
|
|
|
DataOffset -
|
|
The place in which to put the Wave data offset in the stream.
|
|
|
|
DataLength -
|
|
The place in which to put the Wave data length.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS if the format was read, else and error.
|
|
|
|
--*/
|
|
{
|
|
ULONGLONG Extent;
|
|
MMCKINFO MmckRIFF;
|
|
MMCKINFO Mmck;
|
|
NTSTATUS Status;
|
|
ULONG FormatSize;
|
|
PKSDATAFORMAT_WAVEFORMATEX LocalWaveFormat;
|
|
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("riffReadWaveFormat"));
|
|
//
|
|
// Always reset the position to the beginning of the stream.
|
|
//
|
|
riffSetPosition(FileObject, 0);
|
|
Status = riffGetExtent(FileObject, &Extent);
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\triffGetExtent=%x, Extent=%I64u", Status, Extent));
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
MmckRIFF.fccType = riffWAVE;
|
|
Status = riffDescend(FileObject, &MmckRIFF, NULL, MMIO_FINDRIFF);
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\triffDescend=%x", Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
Mmck.ckid = riffFMT;
|
|
Status = riffDescend(FileObject, &Mmck, &MmckRIFF, MMIO_FINDCHUNK);
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\triffDescend=%x", Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
if (Mmck.cksize < sizeof(PCMWAVEFORMAT)) {
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
FormatSize = sizeof(KSDATAFORMAT) + max(Mmck.cksize, sizeof(WAVEFORMATEX));
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\tFormatSize=%u", FormatSize));
|
|
//
|
|
// Don't allow unlimited memory allocation by the calling process.
|
|
//
|
|
try {
|
|
LocalWaveFormat = (PKSDATAFORMAT_WAVEFORMATEX)ExAllocatePoolWithQuotaTag(PagedPool, FormatSize, 'evaW');
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
//
|
|
// The header may not be in extended format, so ensure this is zeroed.
|
|
//
|
|
LocalWaveFormat->WaveFormatEx.cbSize = 0;
|
|
Status = riffRead(FileObject, &LocalWaveFormat->WaveFormatEx, Mmck.cksize);
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\triffRead=%x", Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool(LocalWaveFormat);
|
|
return Status;
|
|
}
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\tWaveFormatEx="));
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\t\twFormatTag=%x\n\t\tnChannels=%u\n", LocalWaveFormat->WaveFormatEx.wFormatTag, LocalWaveFormat->WaveFormatEx.nChannels));
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\t\tnAvgBytesPerSec=%x\n\t\tnBlockAlign=%u\n", LocalWaveFormat->WaveFormatEx.nAvgBytesPerSec, LocalWaveFormat->WaveFormatEx.nBlockAlign));
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\t\twBitsPerSample=%x\n\t\tcbSize=%u\n", LocalWaveFormat->WaveFormatEx.wBitsPerSample, LocalWaveFormat->WaveFormatEx.cbSize));
|
|
if (Mmck.cksize < sizeof(WAVEFORMATEX)) {
|
|
LocalWaveFormat->WaveFormatEx.cbSize = 0;
|
|
} else if (LocalWaveFormat->WaveFormatEx.cbSize > Mmck.cksize - sizeof(WAVEFORMATEX)) {
|
|
ExFreePool(LocalWaveFormat);
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
LocalWaveFormat->DataFormat.FormatSize = FormatSize;
|
|
if (LocalWaveFormat->WaveFormatEx.wFormatTag == WAVE_FORMAT_PCM) {
|
|
LocalWaveFormat->DataFormat.Flags = 0;
|
|
} else {
|
|
LocalWaveFormat->DataFormat.Flags = KSDATAFORMAT_TEMPORAL_COMPRESSION;
|
|
}
|
|
LocalWaveFormat->DataFormat.SampleSize = LocalWaveFormat->WaveFormatEx.nBlockAlign;
|
|
LocalWaveFormat->DataFormat.Reserved = 0;
|
|
LocalWaveFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
|
|
INIT_WAVEFORMATEX_GUID(&LocalWaveFormat->DataFormat.SubFormat, LocalWaveFormat->WaveFormatEx.wFormatTag);
|
|
LocalWaveFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
|
|
if (!NT_SUCCESS(Status = riffAscend(FileObject, &Mmck))) {
|
|
ExFreePool(LocalWaveFormat);
|
|
return Status;
|
|
}
|
|
Mmck.ckid = riffDATA;
|
|
if (!NT_SUCCESS(Status = riffDescend(FileObject, &Mmck, &MmckRIFF, MMIO_FINDCHUNK))) {
|
|
ExFreePool(LocalWaveFormat);
|
|
return Status;
|
|
}
|
|
if (Mmck.dwDataOffset + Mmck.cksize > Extent) {
|
|
ExFreePool(LocalWaveFormat);
|
|
return STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
*WaveFormat = LocalWaveFormat;
|
|
*DataLength = Mmck.cksize;
|
|
*DataOffset = Mmck.dwDataOffset;
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\tSTATUS_SUCCESS"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
InitializeRiffIoPin(
|
|
IN PIRP Irp,
|
|
IN PKSPIN_CONNECT Connect
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the Riff I/O Pin specific structure and initializes it. This includes
|
|
connecting to the specified file name in the data format.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Creation Irp.
|
|
|
|
DataFormat -
|
|
The proposed data format.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS if everything could be allocated and opened, else an error.
|
|
|
|
--*/
|
|
{
|
|
PKSDATAFORMAT DataFormat;
|
|
PFILE_OBJECT FileObject;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PFILTER_INSTANCE FilterInstance;
|
|
PKSDATAFORMAT_WAVEFORMATEX WaveFormat;
|
|
PPIN_INSTANCE_RIFFIO PinInstance;
|
|
NTSTATUS Status;
|
|
ULONGLONG DataOffset;
|
|
ULONGLONG DataLength;
|
|
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("InitializeRiffIoPin"));
|
|
DataFormat = (PKSDATAFORMAT)(Connect + 1);
|
|
//
|
|
// The rest of the data format has already been verified by KsValidateConnectRequest,
|
|
// however the FormatSize may be longer than what it should be.
|
|
//
|
|
if (DataFormat->FormatSize != sizeof(KSDATAFORMAT)) {
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
//
|
|
// Ensure that the handle passed is valid. Note that the Previous Mode is used,
|
|
// since the Irp->RequestorMode is always set to KernelMode on a create.
|
|
//
|
|
Status = ObReferenceObjectByHandle(
|
|
Connect->PinToHandle,
|
|
FILE_READ_DATA,
|
|
*IoFileObjectType,
|
|
ExGetPreviousMode(),
|
|
&FileObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
_DbgPrintF(
|
|
DEBUGLVL_ERROR,
|
|
("unable to reference object: %08x", Status) );
|
|
return Status;
|
|
}
|
|
//
|
|
// Parse the data stream to ensure that it is a valid Riff Wave stream, and return
|
|
// useful information on it.
|
|
//
|
|
Status = riffReadWaveFormat(FileObject, &WaveFormat, &DataOffset, &DataLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ObDereferenceObject(FileObject);
|
|
_DbgPrintF(
|
|
DEBUGLVL_ERROR,
|
|
("unable to read wave file format: %08x", Status) );
|
|
return Status;
|
|
}
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
FilterInstance = (PFILTER_INSTANCE)IrpStack->FileObject->RelatedFileObject->FsContext;
|
|
if (FilterInstance->WaveFormat) {
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\tInitializeRiffIoPin: Wave pin already connected"));
|
|
//
|
|
// There is already another connection on this filter, and so a wave format
|
|
// has been selected. This means that the one read must match that
|
|
// format. Ensure that the comparison won't fault in random memory, then
|
|
// do the actual compare.
|
|
//
|
|
if ((WaveFormat->DataFormat.FormatSize != FilterInstance->WaveFormat->DataFormat.FormatSize) ||
|
|
!RtlEqualMemory(WaveFormat, FilterInstance->WaveFormat, FilterInstance->WaveFormat->DataFormat.FormatSize)) {
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
//
|
|
// No new wave format is allocated in this instance.
|
|
//
|
|
ExFreePool(WaveFormat);
|
|
WaveFormat = NULL;
|
|
} else {
|
|
FilterInstance->WaveFormat = WaveFormat;
|
|
}
|
|
//
|
|
// Create the instance information. This contains the Pin factory identifier, file
|
|
// handle, file object, and mutex for accessing the file. The only reason the file
|
|
// handle is kept is that the file system keeps track of locks based on handles,
|
|
// and assumes the file is closing if no handles are opened on a file object, which
|
|
// results in I/O to that file object failing.
|
|
//
|
|
PinInstance = (PPIN_INSTANCE_RIFFIO)ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(PIN_INSTANCE_RIFFIO),
|
|
'IPsK');
|
|
if (PinInstance) {
|
|
//
|
|
// This object uses KS to perform access through the PinRiffIoDispatchTable. There
|
|
// are no create items attached to this object because it does not support a
|
|
// clock or allocator.
|
|
//
|
|
Status = KsAllocateObjectHeader(
|
|
&PinInstance->InstanceHdr.Header,
|
|
0,
|
|
NULL,
|
|
Irp,
|
|
&PinRiffIoDispatchTable);
|
|
if (NT_SUCCESS(Status)) {
|
|
PinInstance->PresentationByteTime = 0;
|
|
PinInstance->DataOffset = DataOffset;
|
|
PinInstance->DataLength = DataLength;
|
|
PinInstance->FileObject = FileObject;
|
|
PinInstance->Denominator =
|
|
FilterInstance->WaveFormat->WaveFormatEx.wBitsPerSample *
|
|
FilterInstance->WaveFormat->WaveFormatEx.nChannels *
|
|
FilterInstance->WaveFormat->WaveFormatEx.nSamplesPerSec;
|
|
PinInstance->State = KSSTATE_STOP;
|
|
//
|
|
// KS expects that the object data is in FsContext.
|
|
//
|
|
IrpStack->FileObject->FsContext = PinInstance;
|
|
//
|
|
// Add the Pin's target to the list of targets for recalculating
|
|
// stack depth.
|
|
//
|
|
KsSetTargetDeviceObject(
|
|
PinInstance->InstanceHdr.Header,
|
|
IoGetRelatedDeviceObject(FileObject));
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
_DbgPrintF(
|
|
DEBUGLVL_ERROR,
|
|
("unable to allocate object header: %08x", Status) );
|
|
}
|
|
ExFreePool(PinInstance);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
//
|
|
// This is only the failure case, so free any possible new wave format structure
|
|
// that has been allocated.
|
|
//
|
|
if (WaveFormat) {
|
|
FilterInstance->WaveFormat = NULL;
|
|
ExFreePool(WaveFormat);
|
|
}
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
InitializeWaveIoPin(
|
|
IN PIRP Irp,
|
|
IN PKSDATAFORMAT DataFormat
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the Wave I/O Pin specific structure and initializes it.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Creation Irp.
|
|
|
|
DataFormat -
|
|
The proposed data format.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS if everything could be allocated and opened, else an error.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PFILTER_INSTANCE FilterInstance;
|
|
PKSDATAFORMAT_WAVEFORMATEX WaveFormat;
|
|
PPIN_INSTANCE_WAVEIO PinInstance;
|
|
NTSTATUS Status;
|
|
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("InitializeWaveIoPin"));
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
FilterInstance = (PFILTER_INSTANCE)IrpStack->FileObject->RelatedFileObject->FsContext;
|
|
if (FilterInstance->WaveFormat) {
|
|
//
|
|
// There is already another connection on this filter, and so a wave format
|
|
// has been selected. This means that the one passed here must match that
|
|
// format. Ensure that the comparison won't fault in random memory, then
|
|
// do the actual compare.
|
|
//
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\tInitializeWaveIoPin: Riff side is already connected"));
|
|
if (DataFormat->FormatSize != FilterInstance->WaveFormat->DataFormat.FormatSize) {
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
((PKSDATAFORMAT_WAVEFORMATEX)DataFormat)->DataFormat.Flags |= FilterInstance->WaveFormat->DataFormat.Flags;
|
|
((PKSDATAFORMAT_WAVEFORMATEX)DataFormat)->DataFormat.SampleSize = FilterInstance->WaveFormat->DataFormat.SampleSize;
|
|
if (!RtlEqualMemory(DataFormat, FilterInstance->WaveFormat, FilterInstance->WaveFormat->DataFormat.FormatSize)) {
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
//
|
|
// No new wave format is allocated in this instance.
|
|
//
|
|
WaveFormat = NULL;
|
|
} else {
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("\tInitializeWaveIoPin: brand new format"));
|
|
//
|
|
// Don't allow unlimited memory allocation by the calling process.
|
|
//
|
|
try {
|
|
WaveFormat = (PKSDATAFORMAT_WAVEFORMATEX)ExAllocatePoolWithQuotaTag(PagedPool, sizeof(DataFormat->FormatSize), 'evaW');
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
RtlCopyMemory(WaveFormat, DataFormat, DataFormat->FormatSize);
|
|
}
|
|
//
|
|
// Create the instance information. This contains the Pin factory identifier.
|
|
//
|
|
PinInstance = (PPIN_INSTANCE_WAVEIO)ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
sizeof(PIN_INSTANCE_WAVEIO),
|
|
'IPsK');
|
|
if (PinInstance) {
|
|
//
|
|
// This object uses KS to perform access through the PinWaveIoDispatchTable. There
|
|
// are no create items attached to this object because it does not support a
|
|
// clock or allocator.
|
|
//
|
|
Status = KsAllocateObjectHeader(
|
|
&PinInstance->InstanceHdr.Header,
|
|
0,
|
|
NULL,
|
|
Irp,
|
|
&PinWaveIoDispatchTable);
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// Assign the new format passed to this call. Else there must already be
|
|
// a connection, and this format matches the format already selected.
|
|
//
|
|
if (!FilterInstance->WaveFormat) {
|
|
FilterInstance->WaveFormat = WaveFormat;
|
|
}
|
|
//
|
|
// KS expects that the object data is in FsContext.
|
|
//
|
|
IrpStack->FileObject->FsContext = PinInstance;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
ExFreePool(PinInstance);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
//
|
|
// This is only the failure case, so free any possible new wave format structure
|
|
// that has been allocated.
|
|
//
|
|
if (WaveFormat) {
|
|
ExFreePool(WaveFormat);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PinDispatchCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatches the creation of a Pin instance. Allocates the object header and initializes
|
|
the data for this Pin instance.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
Device object on which the creation is occuring.
|
|
|
|
Irp -
|
|
Creation Irp.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS on success, or an error.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PKSPIN_CONNECT Connect;
|
|
NTSTATUS Status;
|
|
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("PinDispatchCreate"));
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
//
|
|
// Determine if this request is being sent to a valid Pin factory with valid
|
|
// connection parameters. All the descriptors are the same except for the
|
|
// data flow description, which is not specified in a connection request.
|
|
//
|
|
if (NT_SUCCESS(Status = KsValidateConnectRequest(Irp, SIZEOF_ARRAY(PinDescriptors), PinDescriptors, &Connect))) {
|
|
PFILTER_INSTANCE FilterInstance;
|
|
|
|
FilterInstance = (PFILTER_INSTANCE)IrpStack->FileObject->RelatedFileObject->FsContext;
|
|
//
|
|
// Exclude other Pin creation at this point.
|
|
//
|
|
ExAcquireFastMutexUnsafe(&FilterInstance->ControlMutex);
|
|
if (!FilterInstance->PinFileObjects[Connect->PinId]) {
|
|
switch (Connect->PinId) {
|
|
case ID_RIFFIO_PIN:
|
|
Status = InitializeRiffIoPin(Irp, Connect);
|
|
break;
|
|
case ID_WAVEIO_PIN:
|
|
Status = InitializeWaveIoPin(Irp, (PKSDATAFORMAT)(Connect + 1));
|
|
break;
|
|
}
|
|
if (NT_SUCCESS(Status)) {
|
|
PPIN_INSTANCE_HEADER PinInstance;
|
|
|
|
//
|
|
// Store the common Pin information and increment the reference
|
|
// count on the parent Filter.
|
|
//
|
|
PinInstance = (PPIN_INSTANCE_HEADER)IrpStack->FileObject->FsContext;
|
|
PinInstance->PinId = Connect->PinId;
|
|
ObReferenceObject(IrpStack->FileObject->RelatedFileObject);
|
|
FilterInstance->PinFileObjects[Connect->PinId] = IrpStack->FileObject;
|
|
}
|
|
} else {
|
|
Status = STATUS_CONNECTION_REFUSED;
|
|
}
|
|
ExReleaseFastMutexUnsafe(&FilterInstance->ControlMutex);
|
|
}
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("PinDispatchCreate=%x", Status));
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PinDispatchClose(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes a previously opened Pin instance. This can occur at any time in any order.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
Device object on which the close is occuring.
|
|
|
|
Irp -
|
|
Close Irp.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PFILTER_INSTANCE FilterInstance;
|
|
PPIN_INSTANCE_HEADER PinInstance;
|
|
ULONG PinId;
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PinInstance = (PPIN_INSTANCE_HEADER)IrpStack->FileObject->FsContext;
|
|
FilterInstance = (PFILTER_INSTANCE)IrpStack->FileObject->RelatedFileObject->FsContext;
|
|
//
|
|
// The closing of the Pin instances must be synchronized with any access to
|
|
// that object.
|
|
//
|
|
ExAcquireFastMutexUnsafe(&FilterInstance->ControlMutex);
|
|
//
|
|
// These were allocated during the creation of the Pin instance. Specifically
|
|
// access to the target file object and the PinFileObjects array.
|
|
//
|
|
KsFreeObjectHeader(PinInstance->Header);
|
|
switch (PinInstance->PinId) {
|
|
case ID_RIFFIO_PIN:
|
|
ObDereferenceObject(((PPIN_INSTANCE_RIFFIO)PinInstance)->FileObject);
|
|
//
|
|
// After removing this item from the list of object during
|
|
// KsFreeObjectHeader, try to shrink the stack depth.
|
|
//
|
|
KsRecalculateStackDepth(((PDEVICE_INSTANCE)DeviceObject->DeviceExtension)->Header, FALSE);
|
|
PinId = ID_WAVEIO_PIN;
|
|
break;
|
|
case ID_WAVEIO_PIN:
|
|
PinId = ID_RIFFIO_PIN;
|
|
break;
|
|
}
|
|
//
|
|
// As soon as the entry has been set to NULL, the mutex can allow access again.
|
|
//
|
|
FilterInstance->PinFileObjects[PinInstance->PinId] = NULL;
|
|
//
|
|
// When both connections are removed, then the data format is free to change.
|
|
//
|
|
if (!FilterInstance->PinFileObjects[PinId]) {
|
|
ExFreePool(FilterInstance->WaveFormat);
|
|
FilterInstance->WaveFormat = NULL;
|
|
}
|
|
ExReleaseFastMutexUnsafe(&FilterInstance->ControlMutex);
|
|
//
|
|
// All Pins are created with a root file object, which is the Filter, and was
|
|
// previously referenced during creation.
|
|
//
|
|
ObDereferenceObject(IrpStack->FileObject->RelatedFileObject);
|
|
ExFreePool(PinInstance);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
PFILE_OBJECT
|
|
ReferenceRiffIoObject(
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This acquires the Filter mutex, references and returns the Riff I/O Pin instance so
|
|
that the Wave I/O Pin or Filter instance can access the structures. The mutex is
|
|
released before returning, but since the file object has been referenced, this
|
|
ensures that the Riff I/O Pin is not closed while such accessing is occuring. If
|
|
the Riff I/O Pin has been closed, this releases the mutex and returns NULL.
|
|
|
|
Arguments:
|
|
|
|
FileObject -
|
|
The file object of the parent Filter.
|
|
|
|
Return Values:
|
|
|
|
Returns a Riff I/O Pin object, or NULL if there is none on the parent Filter.
|
|
|
|
--*/
|
|
{
|
|
PFILTER_INSTANCE FilterInstance;
|
|
|
|
FilterInstance = (PFILTER_INSTANCE)FileObject->FsContext;
|
|
//
|
|
// Synchronize with any close, and exclude other changes from happening.
|
|
//
|
|
ExAcquireFastMutexUnsafe(&FilterInstance->ControlMutex);
|
|
FileObject = FilterInstance->PinFileObjects[ID_RIFFIO_PIN];
|
|
if (FileObject) {
|
|
//
|
|
// Only reference if the Riff I/O Pin instance is present.
|
|
//
|
|
ObReferenceObject(FileObject);
|
|
}
|
|
ExReleaseFastMutexUnsafe(&FilterInstance->ControlMutex);
|
|
return FileObject;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ReadStream(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles IOCTL_KS_READ_STREAM by reading data from the current file position.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Streaming Irp.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS if the request was fulfilled, which includes reaching the end
|
|
of file. Else returns STATUS_DEVICE_NOT_CONNECTED if the Riff I/O Pin has been closed,
|
|
some read error, or some parameter validation error.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = KsProbeStreamIrp(Irp, KSPROBE_STREAMREAD, sizeof(KSSTREAM_HEADER));
|
|
if (NT_SUCCESS(Status)) {
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PPIN_INSTANCE_RIFFIO PinInstance;
|
|
PFILE_OBJECT FileObject;
|
|
ULONG BufferLength;
|
|
PKSSTREAM_HEADER StreamHdr;
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
BufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
StreamHdr = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
|
|
//
|
|
// Retrieve the RIFF I/O Pin instance and reference it.
|
|
//
|
|
if (!(FileObject = ReferenceRiffIoObject(IrpStack->FileObject->RelatedFileObject))) {
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
PinInstance = (PPIN_INSTANCE_RIFFIO)FileObject->FsContext;
|
|
//
|
|
// Enumerate the stream headers, filling in each one.
|
|
//
|
|
for (; BufferLength; BufferLength -= sizeof(KSSTREAM_HEADER), StreamHdr++) {
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
//
|
|
// If a stream time was not set, then use the current position, else use
|
|
// the position specified. The file is locked, so this can be accessed
|
|
// directly to set the start time of this block.
|
|
//
|
|
if (StreamHdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID) {
|
|
//
|
|
// The data source specifies the Numerator/Denominator
|
|
//
|
|
if ((StreamHdr->PresentationTime.Numerator != 80000000) || (StreamHdr->PresentationTime.Denominator != PinInstance->Denominator)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
//
|
|
// Because of the Numerator/Denominator chosen, the time is already
|
|
// equivalent to the byte position. This assumes a constant bit rate
|
|
// compression, which is all that can normally be handled by RIFF
|
|
// Wave anyway.
|
|
//
|
|
StreamHdr->PresentationTime.Numerator = 1;
|
|
StreamHdr->PresentationTime.Denominator = 1;
|
|
}
|
|
}
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// Need to catch the Irp on the way back in order to put the correct
|
|
// Numerator and Denominator back. To be cheap, this is a synchronous
|
|
// call.
|
|
//
|
|
BufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
Status = KsForwardAndCatchIrp(
|
|
IoGetRelatedDeviceObject(PinInstance->FileObject),
|
|
Irp,
|
|
PinInstance->FileObject,
|
|
KsStackReuseCurrentLocation);
|
|
if (NT_SUCCESS(Status)) {
|
|
for (StreamHdr = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
|
|
BufferLength;
|
|
BufferLength -= sizeof(KSSTREAM_HEADER), StreamHdr++) {
|
|
if (StreamHdr->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID) {
|
|
StreamHdr->PresentationTime.Numerator = 80000000;
|
|
StreamHdr->PresentationTime.Denominator = PinInstance->Denominator;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ObDereferenceObject(FileObject);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PinWaveDispatchIoControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatches property, event, and streaming requests on the Wave I/O Pin instance.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
Device object on which the device control is occuring.
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS if the property was successfully manipulated, else an error.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
CCHAR PriorityBoost;
|
|
|
|
PriorityBoost = IO_NO_INCREMENT;
|
|
switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_KS_PROPERTY:
|
|
Status = KsPropertyHandler(
|
|
Irp,
|
|
SIZEOF_ARRAY(WaveIoPropertySets),
|
|
WaveIoPropertySets);
|
|
break;
|
|
case IOCTL_KS_READ_STREAM:
|
|
if (NT_SUCCESS(Status = ReadStream(Irp))) {
|
|
PriorityBoost = IO_DISK_INCREMENT;
|
|
}
|
|
break;
|
|
default:
|
|
return KsDefaultDeviceIoCompletion(DeviceObject, Irp);
|
|
}
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, PriorityBoost);
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PinRiffDispatchIoControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatches property, event, and streaming requests on the Riff I/O Pin instance.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
Device object on which the device control is occuring.
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS if the property was successfully manipulated, else an error.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
CCHAR PriorityBoost;
|
|
|
|
PriorityBoost = IO_NO_INCREMENT;
|
|
switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_KS_PROPERTY:
|
|
Status = KsPropertyHandler(
|
|
Irp,
|
|
SIZEOF_ARRAY(RiffIoPropertySets),
|
|
RiffIoPropertySets);
|
|
break;
|
|
default:
|
|
return KsDefaultDeviceIoCompletion(DeviceObject, Irp);
|
|
}
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, PriorityBoost);
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetTimeFormat(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT GUID* TimeFormat
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles the KSPROPERTY_STREAM_TIMEFORMAT property Get in the Stream property
|
|
set. Returns the time format on the Riff I/O Pin so that positional
|
|
translations can be performed.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Property -
|
|
Specific property request.
|
|
|
|
TimeFormat -
|
|
The place in which to put the time format.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS.
|
|
|
|
--*/
|
|
{
|
|
*TimeFormat = KSTIME_FORMAT_BYTE;
|
|
Irp->IoStatus.Information = sizeof(*TimeFormat);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetPresentationTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSTIME PresentationTime
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles the KSPROPERTY_STREAM_PRESENTATIONTIME property Get in the Stream property
|
|
set. Retrieves the current file position if there is a Riff I/O Pin instance.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Property -
|
|
Specific property request.
|
|
|
|
PresentationTime -
|
|
The place in which to put the current file position in native units.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS, else STATUS_DEVICE_NOT_CONNECTED if there is no Riff I/O Pin.
|
|
|
|
--*/
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PPIN_INSTANCE_RIFFIO PinInstance;
|
|
|
|
//
|
|
// Retrieve the RIFF I/O Pin instance and reference it.
|
|
//
|
|
if (!(FileObject = ReferenceRiffIoObject(IoGetCurrentIrpStackLocation(Irp)->FileObject->RelatedFileObject))) {
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
PinInstance = (PPIN_INSTANCE_RIFFIO)FileObject->FsContext;
|
|
//
|
|
// The pin is locked, so this can be accessed directly.
|
|
//
|
|
PresentationTime->Time = PinInstance->PresentationByteTime;
|
|
PresentationTime->Numerator = 80000000;
|
|
PresentationTime->Denominator = PinInstance->Denominator;
|
|
ObDereferenceObject(FileObject);
|
|
Irp->IoStatus.Information = sizeof(*PresentationTime);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SetPresentationTime(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
IN PKSTIME PresentationTime
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles the KSPROPERTY_STREAM_PRESENTATIONTIME property Set in the Stream property
|
|
set. Sets the current file position if there is a Riff I/O Pin instance.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Property -
|
|
Specific property request.
|
|
|
|
PresentationTime -
|
|
Points to the new file position expressed in native units.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS, else STATUS_DEVICE_NOT_CONNECTED if there is no Riff I/O Pin.
|
|
|
|
--*/
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PPIN_INSTANCE_RIFFIO PinInstance;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Retrieve the RIFF I/O Pin instance and reference it.
|
|
//
|
|
if (!(FileObject = ReferenceRiffIoObject(IoGetCurrentIrpStackLocation(Irp)->FileObject->RelatedFileObject))) {
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
PinInstance = (PPIN_INSTANCE_RIFFIO)FileObject->FsContext;
|
|
//
|
|
// The pin is locked, so this can be accessed directly. Don't allow
|
|
// a seek off the end of the chunk.
|
|
//
|
|
if ((ULONGLONG)PresentationTime->Time <= PinInstance->DataLength) {
|
|
//
|
|
// The data source specifies the Numerator/Denominator.
|
|
//
|
|
if ((PresentationTime->Numerator == 80000000) &&
|
|
(PresentationTime->Denominator == PinInstance->Denominator)) {
|
|
Status = riffSetPosition(
|
|
PinInstance->FileObject,
|
|
PresentationTime->Time + PinInstance->DataOffset);
|
|
if (NT_SUCCESS(Status)) {
|
|
PinInstance->PresentationByteTime = PresentationTime->Time;
|
|
}
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
} else {
|
|
Status = STATUS_END_OF_FILE;
|
|
}
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetPresentationExtent(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PULONGLONG PresentationExtent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles the KSPROPERTY_STREAM_PRESENTATIONEXTENT property Get in the Stream property
|
|
set. Retrieves the current file extent if there is a Riff I/O Pin instance.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Property -
|
|
Specific property request.
|
|
|
|
PresentationExtent -
|
|
The place in which to put the file length in native units.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS, else STATUS_DEVICE_NOT_CONNECTED if there is no Riff I/O Pin.
|
|
|
|
--*/
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PPIN_INSTANCE_RIFFIO PinInstance;
|
|
FILE_STANDARD_INFORMATION StandardInformation;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
//
|
|
// Retrieve the RIFF I/O Pin instance and reference it.
|
|
//
|
|
if (!(FileObject = ReferenceRiffIoObject(IoGetCurrentIrpStackLocation(Irp)->FileObject->RelatedFileObject))) {
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
PinInstance = (PPIN_INSTANCE_RIFFIO)FileObject->FsContext;
|
|
*PresentationExtent = PinInstance->DataLength;
|
|
ObDereferenceObject(FileObject);
|
|
Irp->IoStatus.Information = sizeof(*PresentationExtent);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SetState(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
IN PKSSTATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles the KSPROPERTY_CONNECTION_STATE property Set in the Connection property
|
|
set. Deals with transitions from the Stop state by recalculating stack depth.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Property -
|
|
Specific property request.
|
|
|
|
State -
|
|
Points to the new state.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS, else STATUS_DEVICE_NOT_CONNECTED if there is no Riff I/O Pin.
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PFILE_OBJECT FileObject;
|
|
PPIN_INSTANCE_RIFFIO PinInstance;
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
//
|
|
// Retrieve the RIFF I/O Pin instance and reference it.
|
|
//
|
|
FileObject = ReferenceRiffIoObject(IrpStack->FileObject->RelatedFileObject);
|
|
if (FileObject) {
|
|
PinInstance = (PPIN_INSTANCE_RIFFIO)FileObject->FsContext;
|
|
}
|
|
//
|
|
// Only transitions out of Stop state are interesting, except for removing
|
|
// the extra target object from the active list.
|
|
//
|
|
if (*State == KSSTATE_STOP) {
|
|
if (FileObject) {
|
|
KsSetTargetState(PinInstance->InstanceHdr.Header, KSTARGET_STATE_DISABLED);
|
|
PinInstance->State = *State;
|
|
ObDereferenceObject(FileObject);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// Can't make it go if the file is not connected.
|
|
//
|
|
if (!FileObject) {
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
if (PinInstance->State == KSSTATE_STOP) {
|
|
//
|
|
// Enable the target device object so that it can be counted in recalculations.
|
|
//
|
|
KsSetTargetState(PinInstance->InstanceHdr.Header, KSTARGET_STATE_ENABLED);
|
|
KsRecalculateStackDepth(((PDEVICE_INSTANCE)IrpStack->DeviceObject->DeviceExtension)->Header, FALSE);
|
|
}
|
|
//
|
|
// No attempt at serialization is made here. The worst that can happen is
|
|
// that Irp's fail because multiple changes were sent.
|
|
//
|
|
PinInstance->State = *State;
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetAcquireOrdering(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT BOOL* AcquireOrdering
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles the KSPROPERTY_CONNECTION_ACQUIREORDERING property Set in the Connection
|
|
property set. Returns TRUE to indicate that this Communication Sink does require
|
|
an ordered transition from Stop to Acquire state. This means that a client must
|
|
follow the connection path to the filter which is connected to this filter's
|
|
Connection Source (ID_RIFF_IO_PIN), to change the state of pins on that filter
|
|
first (or follow the graph further if so indicated by the Communication Sink on
|
|
that filter).
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Property -
|
|
Specific property request.
|
|
|
|
AcquireOrdering -
|
|
The place in which to return indication that this Communication Sink is
|
|
interested in an ordered transition from Stop to Acquire state.
|
|
|
|
Return Values:
|
|
|
|
Returns STATUS_SUCCESS.
|
|
--*/
|
|
{
|
|
*AcquireOrdering = TRUE;
|
|
Irp->IoStatus.Information = sizeof(*AcquireOrdering);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetAllocatorFraming(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
OUT PKSALLOCATOR_FRAMING Framing
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the allocator framing preferences for this object. For this stream
|
|
the size is chosen based on PAGE_SIZE and block alignment of the data.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Property -
|
|
Specific property request.
|
|
|
|
Framing -
|
|
The place in which to put the allocator preferences.
|
|
|
|
Return:
|
|
|
|
Returns STATUS_SUCCESS.
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PFILTER_INSTANCE FilterInstance;
|
|
ULONG BlockAlign;
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
FilterInstance = (PFILTER_INSTANCE)IrpStack->FileObject->RelatedFileObject->FsContext;
|
|
if (FilterInstance->WaveFormat) {
|
|
BlockAlign = FilterInstance->WaveFormat->WaveFormatEx.nBlockAlign;
|
|
} else {
|
|
BlockAlign = 1;
|
|
}
|
|
Framing->RequirementsFlags =
|
|
KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY;
|
|
Framing->PoolType = PagedPool;
|
|
Framing->Frames = 1;
|
|
Framing->FrameSize = BlockAlign * PAGE_SIZE;
|
|
Framing->FileAlignment = PAGE_SIZE - 1;
|
|
Framing->Reserved = 0;
|
|
Irp->IoStatus.Information = sizeof(*Framing);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SetStreamAllocator(
|
|
IN PIRP Irp,
|
|
IN PKSPROPERTY Property,
|
|
IN PHANDLE AllocatorHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles the KSPROPERTY_STREAM_ALLOCATOR property Set in the Stream
|
|
property set. Does not actually use the allocator, so just returns
|
|
STATUS_SUCCESS.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
Device control Irp.
|
|
|
|
Property -
|
|
Specific property request.
|
|
|
|
AllocatorHandle -
|
|
Points to the handle of the new allocator to use, or NULL if none
|
|
is being assigned. Since this filter does not allocate memory, the
|
|
new handle is ignored.
|
|
|
|
Return:
|
|
|
|
Returns STATUS_SUCCESS.
|
|
|
|
--*/
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|