1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
|
/**************************************************************************
AVStream Filter-Centric Sample
Copyright (c) 1999 - 2001, Microsoft Corporation
File:
audio.cpp
Abstract:
This file contains the audio capture pin implementation.
History:
created 6/28/01
**************************************************************************/
#include "avssamp.h"
/**************************************************************************
PAGED CODE
**************************************************************************/
#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif // ALLOC_PRAGMA
NTSTATUS
CAudioCapturePin::
DispatchCreate (
IN PKSPIN Pin,
IN PIRP Irp
)
/*++
Routine Description:
Create a new audio capture pin. This is the creation dispatch for
the audio capture pin.
Arguments:
Pin -
The pin being created
Irp -
The creation Irp
Return Value:
Success / Failure
--*/
{
PAGED_CODE();
NTSTATUS Status = STATUS_SUCCESS;
CAudioCapturePin *CapPin = new (NonPagedPool) CAudioCapturePin (Pin);
CCapturePin *BasePin = static_cast <CCapturePin *> (CapPin);
if (!CapPin) {
//
// Return failure if we couldn't create the pin.
//
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
//
// Add the item to the object bag if we we were successful.
// Whenever the pin closes, the bag is cleaned up and we will be
// freed.
//
Status = KsAddItemToObjectBag (
Pin -> Bag,
reinterpret_cast <PVOID> (BasePin),
reinterpret_cast <PFNKSFREE> (CCapturePin::BagCleanup)
);
if (!NT_SUCCESS (Status)) {
delete CapPin;
} else {
Pin -> Context = reinterpret_cast <PVOID> (BasePin);
}
}
return Status;
}
/*************************************************/
NTSTATUS
CAudioCapturePin::
Acquire (
IN KSSTATE FromState
)
/*++
Routine Description:
Called when the pin transitions into acquire, this gets and releases
our hold on the wave object we use to synthesize audio streams.
Arguments:
FromState -
The state the pin is transitioning away from
Return Value:
Success / Failure
--*/
{
PAGED_CODE();
NTSTATUS Status = STATUS_SUCCESS;
if (FromState == KSSTATE_STOP) {
//
// On the transition into acquire from stop, get ahold of the
// wave object we're synthesizing from.
//
m_WaveObject = m_ParentFilter -> GetWaveObject ();
NT_ASSERT (m_WaveObject);
//
// There must be a wave object or something is really wrong.
//
if (!m_WaveObject) {
Status = STATUS_INTERNAL_ERROR;
} else {
m_WaveObject -> Reset ();
}
} else {
//
// Ensure we hold no reference on the wave object.
//
m_WaveObject = NULL;
}
return Status;
}
/*************************************************/
NTSTATUS
CAudioCapturePin::
IntersectHandler (
IN PKSFILTER Filter,
IN PIRP Irp,
IN PKSP_PIN PinInstance,
IN PKSDATARANGE CallerDataRange,
IN PKSDATARANGE DescriptorDataRange,
IN ULONG BufferSize,
OUT PVOID Data OPTIONAL,
OUT PULONG DataSize
)
/*++
Routine Description:
The intersect handler for the audio capture pin. This is really quite
simple because the audio pin only exposes the number of channels,
sampling frequency, etc... that the wave file it is synthesizing from
contains.
Arguments:
Filter -
Contains a void pointer to the filter structure.
Irp -
Contains a pointer to the data intersection property request.
PinInstance -
Contains a pointer to a structure indicating the pin in question.
CallerDataRange -
Contains a pointer to one of the data ranges supplied by the client
in the data intersection request. The format type, subtype and
specifier are compatible with the DescriptorDataRange.
DescriptorDataRange -
Contains a pointer to one of the data ranges from the pin descriptor
for the pin in question. The format type, subtype and specifier are
compatible with the CallerDataRange.
BufferSize -
Contains the size in bytes of the buffer pointed to by the Data
argument. For size queries, this value will be zero.
Data -
Optionally contains a pointer to the buffer to contain the data
format structure representing the best format in the intersection
of the two data ranges. For size queries, this pointer will be
NULL.
DataSize -
Contains a pointer to the location at which to deposit the size
of the data format. This information is supplied by the function
when the format is actually delivered and in response to size
queries.
Return Value:
STATUS_SUCCESS if there is an intersection and it fits in the supplied
buffer, STATUS_BUFFER_OVERFLOW for successful size queries,
STATUS_NO_MATCH if the intersection is empty, or
STATUS_BUFFER_TOO_SMALL if the supplied buffer is too small.
--*/
{
PAGED_CODE();
//
// Verify that the inpassed range is valid size.
//
if (CallerDataRange -> FormatSize < sizeof (KSDATARANGE_AUDIO)) {
return STATUS_NO_MATCH;
}
//
// Because the only range we expose is such that it will match
// KSDATARANGE_AUDIO, it is safe to interpret the data structures as
// KSDATARANGE_AUDIO. This is due to the fact that AVStream will have
// prematched the GUIDs for us.
//
PKSDATARANGE_AUDIO CallerAudioRange =
reinterpret_cast <PKSDATARANGE_AUDIO> (CallerDataRange);
PKSDATARANGE_AUDIO DescriptorAudioRange =
reinterpret_cast <PKSDATARANGE_AUDIO> (DescriptorDataRange);
//
// We are returning a KSDATAFORMAT_WAVEFORMATEX. Specify such if a size
// query happens.
//
if (BufferSize == 0) {
*DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX);
return STATUS_BUFFER_OVERFLOW;
}
if (BufferSize < sizeof (KSDATAFORMAT_WAVEFORMATEX)) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Match the blocks. We only support one format (not really a range), so
// this intersection aught to be really simple. It's more of a check
// if the format we are going to use intersects somewhere in
// CallerAudioRange.
//
if (DescriptorAudioRange -> MaximumChannels >
CallerAudioRange -> MaximumChannels ||
DescriptorAudioRange -> MinimumBitsPerSample <
CallerAudioRange -> MinimumBitsPerSample ||
DescriptorAudioRange -> MinimumBitsPerSample >
CallerAudioRange -> MaximumBitsPerSample ||
DescriptorAudioRange -> MinimumSampleFrequency <
CallerAudioRange -> MinimumSampleFrequency ||
DescriptorAudioRange -> MinimumSampleFrequency >
CallerAudioRange -> MaximumSampleFrequency) {
//
// If the descriptor's "range" (more of a single format specified
// in a range) doesn't intersect the caller's, no match the call.
//
*DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX);
return STATUS_NO_MATCH;
}
//
// Build the format.
//
PKSDATAFORMAT_WAVEFORMATEX WaveFormat =
reinterpret_cast <PKSDATAFORMAT_WAVEFORMATEX> (Data);
RtlCopyMemory (
&WaveFormat -> DataFormat,
&DescriptorAudioRange -> DataRange,
sizeof (KSDATAFORMAT)
);
WaveFormat -> WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
WaveFormat -> WaveFormatEx.nChannels =
(WORD)DescriptorAudioRange -> MaximumChannels;
WaveFormat -> WaveFormatEx.nSamplesPerSec =
DescriptorAudioRange -> MaximumSampleFrequency;
WaveFormat -> WaveFormatEx.wBitsPerSample =
(WORD)DescriptorAudioRange -> MaximumBitsPerSample;
WaveFormat -> WaveFormatEx.nBlockAlign =
(WaveFormat -> WaveFormatEx.wBitsPerSample / 8) *
WaveFormat -> WaveFormatEx.nChannels;
WaveFormat -> WaveFormatEx.nAvgBytesPerSec =
WaveFormat -> WaveFormatEx.nBlockAlign *
WaveFormat -> WaveFormatEx.nSamplesPerSec;
WaveFormat -> WaveFormatEx.cbSize = 0;
WaveFormat -> DataFormat.SampleSize =
WaveFormat -> WaveFormatEx.nBlockAlign;
WaveFormat -> DataFormat.FormatSize =
*DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX);
return STATUS_SUCCESS;
}
/*************************************************/
NTSTATUS
CAudioCapturePin::
DispatchSetFormat (
IN PKSPIN Pin,
IN PKSDATAFORMAT OldFormat OPTIONAL,
IN PKSMULTIPLE_ITEM OldAttributeList OPTIONAL,
IN const KSDATARANGE *DataRange,
IN const KSATTRIBUTE_LIST *AttributeRange OPTIONAL
)
/*++
Routine Description:
This is the set data format dispatch for the capture pin. It is called
in two circumstances.
1: before Pin's creation dispatch has been made to verify that
Pin -> ConnectionFormat is an acceptable format for the range
DataRange. In this case OldFormat is NULL.
2: after Pin's creation dispatch has been made and an initial format
selected in order to change the format for the pin. In this case,
OldFormat will not be NULL.
Validate that the format is acceptable and perform the actions necessary
to change format if appropriate.
Arguments:
Pin -
The pin this format is being set on. The format itself will be in
Pin -> ConnectionFormat.
OldFormat -
The previous format used on this pin. If this is NULL, it is an
indication that Pin's creation dispatch has not yet been made and
that this is a request to validate the initial format and not to
change formats.
OldAttributeList -
The old attribute list for the prior format
DataRange -
A range out of our list of data ranges which was determined to be
at least a partial match for Pin -> ConnectionFormat. If the format
there is unacceptable for the range, STATUS_NO_MATCH should be
returned.
AttributeRange -
The attribute range
Return Value:
Success / Failure
STATUS_SUCCESS -
The format is acceptable / the format has been changed
STATUS_NO_MATCH -
The format is not-acceptable / the format has not been changed
--*/
{
PAGED_CODE();
//
// This pin does not accept any format changes. It is fixed format based
// on what the wave file we're synthesizing from is. Thus, we don't
// need to worry about this being called in any context except pin
// creation (KSPIN_FLAG_FIXED_FORMAT ensures this). Knowing that the
// format already is a GUID match for the range and we only have one
// range, the interpretation without any guid checks is safe.
//
NT_ASSERT (!OldFormat);
const KSDATARANGE_AUDIO *DataRangeAudio =
reinterpret_cast <const KSDATARANGE_AUDIO *> (DataRange);
//
// Verify the format is the right size.
//
if (Pin -> ConnectionFormat -> FormatSize <
sizeof (KSDATAFORMAT_WAVEFORMATEX)) {
return STATUS_NO_MATCH;
}
PKSDATAFORMAT_WAVEFORMATEX WaveFormat =
reinterpret_cast <PKSDATAFORMAT_WAVEFORMATEX> (
Pin -> ConnectionFormat
);
//
// This is not an intersection, but rather a direct comparison due to
// the fact that we're fixed to a single format and do not really have
// a range.
//
if (WaveFormat -> WaveFormatEx.wFormatTag != WAVE_FORMAT_PCM ||
WaveFormat -> WaveFormatEx.nChannels !=
DataRangeAudio -> MaximumChannels ||
WaveFormat -> WaveFormatEx.nSamplesPerSec !=
DataRangeAudio -> MaximumSampleFrequency ||
WaveFormat -> WaveFormatEx.wBitsPerSample !=
DataRangeAudio -> MaximumBitsPerSample) {
return STATUS_NO_MATCH;
}
//
// The format passes consideration. Allow the pin creation with this
// particular format.
//
return STATUS_SUCCESS;
}
/**************************************************************************
LOCKED CODE
**************************************************************************/
#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif // ALLOC_PRAGMA
NTSTATUS
CAudioCapturePin::
CaptureFrame (
IN PKSPROCESSPIN ProcessPin,
IN ULONG Tick
)
/*++
Routine Description:
Called to synthesize a frame of audio data from the wave object.
Arguments:
ProcessPin -
The process pin from the filter's process pins index
Tick -
The tick counter from the filter (the number of DPC's that have
happened since the DPC timer started). Note that the DPC timer
starts at pause and capture starts at run.
Return Value:
Success / Failure
--*/
{
NT_ASSERT (ProcessPin -> Pin == m_Pin);
//
// Increment the frame number. This is the total count of frames which
// have attempted capture.
//
m_FrameNumber++;
//
// Find out how much time worth of audio data to synthesize into
// the buffer a buffer (or how much time to skip if there are no available
// capture buffers).
//
LONGLONG TimerInterval = m_ParentFilter -> GetTimerInterval ();
//
// Since this pin is KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING, it
// means that we do not require frames available in order to process.
// This means that this routine can get called from our DPC with no
// buffers available to capture into. In this case, we increment our
// dropped frame counter and skip forward into the audio stream.
//
if (ProcessPin -> BytesAvailable) {
//
// Synthesize a fixed amount of audio data based on the timer interval.
//
ULONG BytesUsed = m_WaveObject -> SynthesizeFixed (
TimerInterval,
ProcessPin -> Data,
ProcessPin -> BytesAvailable
);
ProcessPin -> BytesUsed = BytesUsed;
ProcessPin -> Terminate = TRUE;
//
// Time stamp the packet if there is a clock assigned.
//
if (m_Clock) {
PKSSTREAM_HEADER StreamHeader =
ProcessPin -> StreamPointer -> StreamHeader;
StreamHeader -> PresentationTime.Time = m_Clock -> GetTime ();
StreamHeader -> PresentationTime.Numerator =
StreamHeader -> PresentationTime.Denominator = 1;
StreamHeader -> OptionsFlags |=
KSSTREAM_HEADER_OPTIONSF_TIMEVALID;
}
} else {
m_DroppedFrames++;
//
// Since we've skipped an audio frame, inform the wave object to
// skip forward this much.
//
m_WaveObject -> SkipFixed (TimerInterval);
}
return STATUS_SUCCESS;
}
/**************************************************************************
DESCRIPTOR / DISPATCH LAYOUT
**************************************************************************/
//
// AudioCapturePinDispatch:
//
// This is the dispatch table for the capture pin. It provides notifications
// about creation, closure, processing, data formats, etc...
//
const
KSPIN_DISPATCH
AudioCapturePinDispatch = {
CAudioCapturePin::DispatchCreate, // Pin Create
NULL, // Pin Close
NULL, // Pin Process
NULL, // Pin Reset
CAudioCapturePin::DispatchSetFormat, // Pin Set Data Format
CCapturePin::DispatchSetState, // Pin Set Device State
NULL, // Pin Connect
NULL, // Pin Disconnect
NULL, // Clock Dispatch
NULL // Allocator Dispatch
};
//
// AudioDefaultAllocatorFraming:
//
// A default framing for the audio pin. In order for this to work properly,
// the frame size must be at least 1/fps * bytes_per_sec large. Otherwise,
// the audio stream will fall behind. This is dynamically adjusted when
// the actual pin is created.
//
DECLARE_SIMPLE_FRAMING_EX (
AudioDefaultAllocatorFraming,
STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED),
KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY,
25,
0,
2 * PAGE_SIZE,
2 * PAGE_SIZE
);
//
// g_PINNAME_AUDIO_CAPTURE:
//
// A GUID identifying the name of the audio capture pin. I use the standard
// STATIC_PINNAME_VIDEO_CAPTURE for the video capture pin, but a custom name
// as defined in avssamp.inf for the audio capture pin.
//
GUID g_PINNAME_AUDIO_CAPTURE =
{0xba1184b9, 0x1fe6, 0x488a, 0xae, 0x78, 0x6e, 0x99, 0x7b, 0x2, 0xca, 0xea};
//
// AudioPinDescriptorTemplate:
//
// The template for the audio pin descriptor. The audio pin on this filter
// is created dynamically -- if and only if c:\avssamp.wav exists and is
// a valid and readable wave file.
//
const
KSPIN_DESCRIPTOR_EX
AudioPinDescriptorTemplate = {
//
// Audio Capture Pin
//
&AudioCapturePinDispatch,
NULL,
{
NULL, // Interfaces (NULL, 0 == default)
0,
NULL, // Mediums (NULL, 0 == default)
0,
0, // Range count (filled in later)
NULL, // Ranges (filled in later)
KSPIN_DATAFLOW_OUT, // Dataflow
KSPIN_COMMUNICATION_BOTH, // Communication
&KSCATEGORY_AUDIO, // Category
&g_PINNAME_AUDIO_CAPTURE, // Name
0 // Reserved
},
KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING | // Flags
KSPIN_FLAG_DO_NOT_INITIATE_PROCESSING |
KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY |
KSPIN_FLAG_FIXED_FORMAT,
1, // Instances Possible
0, // Instances Necessary
&AudioDefaultAllocatorFraming, // Allocator Framing (filled later)
reinterpret_cast <PFNKSINTERSECTHANDLEREX> // Intersect Handler
(CAudioCapturePin::IntersectHandler)
};
|