avstream buffering issue

hi all,
i am developing a vendor specific audio client driver for windows XP.
i have used pin centric processing and i am referring the avshws sample code.

now after the driver gets loaded,i use graphedt tool to initiate RUN.

in my process,i read the Leading Edge Stream Pointer.
i get the data buffer (to which i need to read in the data from USBD) from Leading -> StreamHeader -> Data and the size from Leading -> StreamHeader ->FrameExtent.

and while creating the URB for isochronous IN transfer,i provide ,

piso_urb->UrbIsochronousTransfer.TransferBufferLength =Leading -> StreamHeader ->FrameExtent;
piso_urb->UrbIsochronousTransfer.TransferBuffer =Leading -> StreamHeader -> Data;

so accordingly the audio data shall be wriiten into TransferBuffer by the USBD.

once IoCallDriver to USBD is returned STATUS_SUCCESS,that means i have data written in Leading -> StreamHeader -> Data.
now i do KsStreamPointerSetStatusCode(Leading,STATUS_SUCCESS);
and
pirp=KsStreamPointerGetIrp(Leading,NULL,NULL);
pirp->IoStatus.Status=STATUS_SUCCESS;
pirp->IoStatus.Information=buffer_size;
KsStreamPointerUnlock(Leading,FALSE);
KsStreamPointerDelete(Leading);
and then i return STATUS_PENDING from Process().

now THE PROBLEM is that the data sent to the above layer is not rendered to the speaker…but i do get call tp Porcess() if i use a timer with timeout say 1ms .
once process gets called i do the same thing as explained above…
The data written to the buffer is not rendered to the speaker.

so any suggestions on where i may have gone wrong??

xxxxx@lntinfotech.com wrote:

i am developing a vendor specific audio client driver for windows XP.
i have used pin centric processing and i am referring the avshws sample code.

now after the driver gets loaded,i use graphedt tool to initiate RUN.

in my process,i read the Leading Edge Stream Pointer.
i get the data buffer (to which i need to read in the data from USBD) from Leading -> StreamHeader -> Data and the size from Leading -> StreamHeader ->FrameExtent.

and while creating the URB for isochronous IN transfer,i provide ,

piso_urb->UrbIsochronousTransfer.TransferBufferLength =Leading -> StreamHeader ->FrameExtent;
piso_urb->UrbIsochronousTransfer.TransferBuffer =Leading -> StreamHeader -> Data;

so accordingly the audio data shall be wriiten into TransferBuffer by the USBD.

Well, that’s not enough. For an isochronous transfer, you must also
create and configure the UrbIsochronousTransfer.IsoPacket array, with
one element per packet. And, remember that the data in an isochronous
transfer is not necessarily returned continuously in the buffer. Each
packet (basically, each microframe) stands alone. If one packet happens
to be short, there will be a gap in the buffer. If you miss one
microframe, there will be a gap in the buffer. You would need to pack
that data before returning it to your caller.

once IoCallDriver to USBD is returned STATUS_SUCCESS,that means i have data written in Leading -> StreamHeader -> Data.

Maybe.

now i do KsStreamPointerSetStatusCode(Leading,STATUS_SUCCESS);
and
pirp=KsStreamPointerGetIrp(Leading,NULL,NULL);
pirp->IoStatus.Status=STATUS_SUCCESS;
pirp->IoStatus.Information=buffer_size;
KsStreamPointerUnlock(Leading,FALSE);
KsStreamPointerDelete(Leading);
and then i return STATUS_PENDING from Process().

Where on earth did you get that code? It’s certainly not in any of the
samples. An AVStream driver never touches the IRP, and you don’t return
STATUS_PENDING. The AVStream frame will handle all of that for you,
when you unlock the stream pointer. AVStream figures out whether the
IRP is really complete or not, based on how much data you shoved in.
You don’t really have any idea how many buffers are mapped to a single
IRP. Usually, a single IRP will contain multiple buffers.

Are you setting StreamHeader->Duration and StreamHeader->DataUsed? How
are you advancing the stream pointer?

You really need the kind of philosophy in avshws. The code in Process
just clones the leading edge pointer (saving it for later) and returns.
Then, later, when the I/O completes, that’s when you unlock and delete
the clone. That allows AVStream to complete the IRP.

so any suggestions on where i may have gone wrong??

Yes – I think you still have some fundamental confusion about how
AVStream works. The sample does things in basically the same way you
need to do them.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Shreenivasa Prasad wrote:

3."> once IoCallDriver to USBD is returned STATUS_SUCCESS,that means i have data written in Leading -> StreamHeader -> Data.

> Maybe."
>
=>you said “Maybe”,i did not get what you intend to say here…did you mean that even if it is STATUS_SUCCESS from USBD,the data read could be a whole packet or a short packet or a zero length packet??

Yes, exactly that.

4.“Where on earth did you get that code? It’s certainly not in any of the samples. An AVStream driver never touches the IRP, and you don’t return STATUS_PENDING.”
=>yes ,the code shown is not there in any of the samples.i just tried it as i had seen these API information in the DDK and few other blogs on the web.
i have a doubt here,
a.how do i intimate the layer above me that the buffer given to me is filled with audio data and it is ready foryou to render it?

You advance the stream’s leading edge pointer, either by advancing the
pointer or closing the last clone to the buffer. The avshws sample does
exactly this. The Process callback (which gets called when there is a
new buffer) makes a clone of the leading edge, which locks it, thereby
preventing it from advancing. The simulated interrupt process, which
actually gets the data, fetches the oldest clone, copies data into it,
and deletes the clone. That releases the last reference to that first
buffer, so AVStream can return it to the caller.

b.how would i be able to get back another call to my process,for the next set of data to be transferred?(sample uses timer and on the invoke of TimerDPC it calls KsFilterAttemptProcessing).i tired the same and i do get calls into my process function once i use timerdpc,but i am not able to intimate that data read is ready for it to be rendered to default directsound device.

When a new empty buffer is available, your Process callback will be
called. That won’t happen unless you have successfully released the
buffers you were given originally. There is a limited supply of empty
buffers, and they circulate around the graph. When the data has been
removed, the empty will be handed back to your Process callback.

6.i have posted my audio streaming implementation here,could you plz give a suggestion as to what more to be done?
and i am not cloning the stream pointer as i have asked for a single buffer in allocator framing structure,.it is as shown below.

That’s a very bad idea. It means that you will not get another buffer
to fill until this one has been completely processed by the downstream
filters. Although, since the framing requirements are only suggestions,
the graph is free to ignore them and give you whatever it wants.

NTSTATUS CCLAVRequest::CLStartAudioStreaming(IN PKSDEVICE m_Device,ULONG buffer_size,PVOID pdata_buffer)
//NTSTATUS CCLAVRequest::CLStartAudioStreaming(IN PKSDEVICE m_Device)

for ( i = 0; i < no_of_pkts; ++i)//, buffer_size -= audio_packet_size)
{
if(!(i%2))
{
piso_urb->UrbIsochronousTransfer.IsoPacket[i].Offset = (ULONG)((index1) * audio_packet_size);
index1++;
}
else
{
piso_urb->UrbIsochronousTransfer.IsoPacket[i].Offset = (ULONG)((index2) * audio_packet_size);
index2++;
}
}

Here, you are ASSUMING that your buffer contains room for at least 500+x
packets. That’s very dangerous. Also remember that USB is not an
entirely reliable bus. You cannot guarantee that your packets will stay
in lockstep with the real data stream. If you should happen to miss one
frame because of a glitch, your data might be transfered to an odd
packet number. You really need to have the packets run in address order
here, and pack the data later.

Also remember that you can’t include more than 255 frames worth of
packets in a single ISO URB.

KdPrint((“P2”));
status = SendAwaitUrb(m_Device,piso_urb,NULL);

You are sending this synchronously? (That is, you wait for the
result?) That’s not good. You need to submit this asynchronously, then
handle the returned data in the completion routine.

if(piso_urb->UrbIsochronousTransfer.IsoPacket[0].Length)
{
KdPrint((“e\n”));
for(i=0;i<500;i++)//for(i=0;i<128;i++)
{
*((PUCHAR)pdata_buffer+i)=*((PUCHAR)pstream_buf+i);
}
}
else
{
KdPrint((“o\n”));
for(i=0;i<500;i++)//for(i=0;i<128;i++)
{
*((PUCHAR)pdata_buffer+i)=*((PUCHAR)pstream_buf+(i+500));
}
}

This is wrong, in several ways. IsoPacket[0].Length tells you how many
bytes you got during the first frame. You need to check EACH PACKET
separately and copy the data. Further, it’s a very bad idea to do
copies like this manually, in a loop, one byte at a time. You should
use RtlMoveMemory, which is able to do memory copies in a much smarter way.

You need something like this:

PUCHAR Dst = pdata_buffer;
for( i = 0; i < num_pkts; i++ )
{
if( piso_urb->UrbIsochronousTransfer.IsoPacket[i].Length )
{
RtlMoveMemory( Dst,
pstream_buf +
piso_urb->UrbIsochronousTransfer.IsoPacket[i].Offset,
piso_urb->UrbIsochronousTransfer.IsoPacket[i].Length
);
Dst += piso_urb->UrbIsochronousTransfer.IsoPacket[i].Length;
}
}

pdata_buffer=(PUCHAR)pdata_buffer+500;

Where does the 500 come from? The buffer size is being sent to you by
the caller. You can’t make assumptions here.

read_data_size=read_data_size + (500*8);
if(read_data_size<(500*8))
goto create_request;

I don’t understand why there’s a “goto” here at all, but it won’t really
matter because it can never get hit. You added 500*8 in the line above,
so the value will always be >= 500*8. And again, where did the 500 come
from?

plz let me know if you feel anything is missed out…
i am new to this AVStream Driver implementation.any inputs from you will definitily be helpful.

You are not looking at the WAY things are done in avshws. You are just
hacking things in here.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.