Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Playing (decoding) arbitrary memory blocks that contain wma data..

33 views
Skip to first unread message

mikeBearb

unread,
Sep 26, 2007, 4:28:10 PM9/26/07
to
...i supply.

Hello. Basically what I want to do is what XAudio2 does (and what XAudio
for XBox360 does). But since it's not available yet on PC, I need an
alternative solution.

We have 6 different skus here we're getting ready to RTM _very_ soon. One
of these skus is PC. Our audio asset build pipeline includes encoding all
source wave (PCM) assets into the various platform specific encodings (vag,
xma, dspadpcm, etc), then concatenating all of them into one very big file
(per sku). Then at playback time we just seek to the sound in the file, read
a portion of it (or all of it) into mem, then play it back. The various APIs
on each sku (provided by OEM) provide decoders to play this in-memory encoded
data for us.

However for WMA, I'm having a hard time getting the SDK to allow me to give
it arbitrary memory blocks for it to decode.

I just want to say hey, here's a memory block containg WMA, SDK please
decode it, and give me back the uncompressed data I can feed to DirectSound.

Currently I'm stuck on GetNextSample()--it keeps giving errors about data
not being good. I think it's because I'm telling it to use an IStream but
with data that's only partially there or that's offset from the beginning.

Please help. Can this be done from within the SDK? I hope I don't need to
use DirectShow instead and write custom filters. We need this ASAP.

Alessandro Angeli

unread,
Sep 26, 2007, 4:58:55 PM9/26/07
to
From: "mikeBearb"

> I just want to say hey, here's a memory block containg
> WMA, SDK please decode it, and give me back the
> uncompressed data I can feed to DirectSound.

If your memory buffer contains raw WMA audio data, you need
to use the WMA DMO decoder directly or in a DirectShow graph
(which requires you to write a custom push source and sink).

If your memory buffer contains a complete WMA file, you can
make the WM(Sync)Reader read it by wrapping it in an IStream
object. See OLE32.DLL!CreateStreamOnHGlobal() and
IWMSyncReader::OpenStream() or
IWMReaderAdvanced2::OpenStream().


--
// Alessandro Angeli
// MVP :: DirectShow / MediaFoundation
// mvpnews at riseoftheants dot com
// http://www.riseoftheants.com/mmx/faq.htm


mikeBearb

unread,
Sep 26, 2007, 5:32:01 PM9/26/07
to

"Alessandro Angeli" wrote:

> From: "mikeBearb"
>
> > I just want to say hey, here's a memory block containg
> > WMA, SDK please decode it, and give me back the
> > uncompressed data I can feed to DirectSound.
>
> If your memory buffer contains raw WMA audio data, you need
> to use the WMA DMO decoder directly or in a DirectShow graph
> (which requires you to write a custom push source and sink).


This is what I feared. I've done dshow in the past and I remember the
RenderFile() wants a filename. But you're saying I can get it to read from
my memory buffers? Maybe this doesn't apply (RenderFile()).


>
> If your memory buffer contains a complete WMA file, you can
> make the WM(Sync)Reader read it by wrapping it in an IStream
> object. See OLE32.DLL!CreateStreamOnHGlobal() and
> IWMSyncReader::OpenStream() or
> IWMReaderAdvanced2::OpenStream().
>

This I've verified is doable and did it already by modifying an SDK code
sample. The problem is with _really_ big WMAs (bigger than 128K, for
example), I may want to implement my own form of "streaming" using ping pong
buffers. So for sounds I declare as "all-in-memory" (one shots), I could do
exactly what you mention here. But for sounds that are too big, I need to
stream--but stream from in-memory ping pong buffers I feed on the side. If I
could do this with the "feared" suggestion above, then I'm golden.

Do you know of any samples that demo this?

Alessandro Angeli

unread,
Sep 26, 2007, 6:41:34 PM9/26/07
to
From: "mikeBearb"

> This is what I feared. I've done dshow in the past and I
> remember the RenderFile() wants a filename. But you're
> saying I can get it to read from my memory buffers?
> Maybe this doesn't apply (RenderFile()).

IGraphBuilder::RenderFile() is only a utility method to
automatically build a rendering graph from a URL but you can
build your own custom graph if you want to. As I said, you
will need to use custom push source and sink filters to be
able to feed/fetch data to/from a graph from/to memory,
because there are no stock ones. However, your next comment
suggests you have a complete WMA file and not just a raw WMA
stream, so there is no need to use DirectShow (there is no
need to use it even if you have a raw stream, since you can
use the WMA DMO decoder directly if you want to).

> This I've verified is doable and did it already by
> modifying an SDK code sample. The problem is with
> _really_ big WMAs (bigger than 128K, for example), I may
> want to implement my own form of "streaming" using ping
> pong buffers. So for sounds I declare as "all-in-memory"
> (one shots), I could do exactly what you mention here.
> But for sounds that are too big, I need to stream--but
> stream from in-memory ping pong buffers I feed on the
> side. If I could do this with the "feared" suggestion
> above, then I'm golden.

You can implement your own IStream that fetches the data in
whatever way you need to. CreateStreamOnHGlobal() is only a
utility function to easily create an IStream from a memory
buffer, but there is no reason why you can not create your
own IStream implementation that only loads blocks of data on
demand instead of all at the same time.

mikeBearb

unread,
Sep 26, 2007, 7:40:02 PM9/26/07
to

"Alessandro Angeli" wrote:

> From: "mikeBearb"
>
> > This is what I feared. I've done dshow in the past and I
> > remember the RenderFile() wants a filename. But you're
> > saying I can get it to read from my memory buffers?
> > Maybe this doesn't apply (RenderFile()).
>
> IGraphBuilder::RenderFile() is only a utility method to
> automatically build a rendering graph from a URL but you can
> build your own custom graph if you want to. As I said, you
> will need to use custom push source and sink filters to be
> able to feed/fetch data to/from a graph from/to memory,
> because there are no stock ones. However, your next comment
> suggests you have a complete WMA file and not just a raw WMA
> stream, so there is no need to use DirectShow (there is no
> need to use it even if you have a raw stream, since you can
> use the WMA DMO decoder directly if you want to).
>

My wma are created by the sdk.

I do have a complete wma on disk (that I put in a concatenated file with
other wmas), but when I transfer one wma to memory (by just a raw
CreateFile()/ReadFile()), sometimes I don't want to transfer it all to
memory. In that case, do I need to use custom push source to decode whatever
I put in memory this way?

> > This I've verified is doable and did it already by
> > modifying an SDK code sample. The problem is with
> > _really_ big WMAs (bigger than 128K, for example), I may
> > want to implement my own form of "streaming" using ping
> > pong buffers. So for sounds I declare as "all-in-memory"
> > (one shots), I could do exactly what you mention here.
> > But for sounds that are too big, I need to stream--but
> > stream from in-memory ping pong buffers I feed on the
> > side. If I could do this with the "feared" suggestion
> > above, then I'm golden.
>
> You can implement your own IStream that fetches the data in
> whatever way you need to. CreateStreamOnHGlobal() is only a
> utility function to easily create an IStream from a memory
> buffer, but there is no reason why you can not create your
> own IStream implementation that only loads blocks of data on
> demand instead of all at the same time.

With IStream as is, I am able to put parts of my WMA in it's memory space.

I do this (which will fail later on GetNextSample()):

-Create a huge heap with GlobalAlloc().
-Create an IStream on that (hglobal) pointer (fixed mem)
-Load either a whole (less than 128K) WMA, or the first 64K shunk of some
huge WMA into some arbitrary (but aligned) spot into the heap I created.
-Create an IStream clone from the first IStream because (later) I may want
more than one decode to happen from different parts within my heap, so this
way I can have different seek pointers.
-Seek (with IStream or the clone) to the spot I loaded the data to.
-Call reader->OpenStream() using the IStream (or clone) object.
-Call reader->SetRange(0,0); // maybe screws up me seek'd to position?
-Try to get the first sample with reader->GetNextSample()

it fails on this last call with ASF_E_BADDATAUNIT.

If I put the data at the beginning of the hglobal block, it gets all the
samples fine. But not if I put the data at some point offset into the memory
buffer.

Maybe the problem is with setRange(). Then how do I get it to use byte
offset instead of time offset?

Alessandro Angeli

unread,
Sep 26, 2007, 9:24:25 PM9/26/07
to
From: "mikeBearb"

> My wma are created by the sdk.
>
> I do have a complete wma on disk (that I put in a
> concatenated file with other wmas), but when I transfer
> one wma to memory (by just a raw
> CreateFile()/ReadFile()), sometimes I don't want to
> transfer it all to memory. In that case, do I need to
> use custom push source to decode whatever I put in memory
> this way?

As I said, if you have a complete WMA, you do not need to
use DirectShow at all but you need to use the WM(Sync)Reader
and an IStream.

> I do this (which will fail later on GetNextSample()):
>
> -Create a huge heap with GlobalAlloc().
> -Create an IStream on that (hglobal) pointer (fixed mem)
> -Load either a whole (less than 128K) WMA, or the first
> 64K shunk of some huge WMA into some arbitrary (but
> aligned) spot into the heap I created. -Create an IStream
> clone from the first IStream because (later) I may want
> more than one decode to happen from different parts
> within my heap, so this way I can have different seek
> pointers.
> -Seek (with IStream or the clone) to the spot I loaded
> the data to.
> -Call reader->OpenStream() using the IStream (or clone)
> object.
> -Call reader->SetRange(0,0); // maybe screws up me seek'd
> to position?
> -Try to get the first sample with reader->GetNextSample()
>
> it fails on this last call with ASF_E_BADDATAUNIT.
>
> If I put the data at the beginning of the hglobal block,
> it gets all the samples fine. But not if I put the data
> at some point offset into the memory buffer.
>
> Maybe the problem is with setRange(). Then how do I get
> it to use byte offset instead of time offset?

Those kind of hacks are *very* unlikely to work reliably
because you have no way of knowing which parts to load and
where since you do not know what Read(), Seek() and Stat()
calls the reader is making to the IStream, nor when or with
which arguments.

You have several choices (from hardest to easiest):

1. reverse-engineer the WM(Sync)Reader to discover what
calls it is going to make

2. write your own ASF parsers

3. use Geraint's ASF parser for DirectShow and write a
custom async reader based on the Async sample that loads
only a piece of the file at a time accoding to what segment
is requested; this also requires a custom sink or a
SampleGrabber and custom graph building

4. use Geraint's ASF parser for DirectShow and write a
custom async reader based on the Async sample that loads the
whole WMA into memory; this also requires a custom sink or a
SampleGrabber and custom graph building

5. use the WM(Sync)Reader with a custom IStream that loads
only a piece of the file at a time accoding to what segment
is requested

6. use the WM(Sync)Reader with the stock
CreateStreamOnHGlobal() to load the whole file into memory

If I were you, I'd go wih #5, which requires maybe a couple
of pages of code and a few calls to
ReadFile()/SetFilePointer():

class MySmartStream : IStream
{
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void** ppv)
{
if(riid == IID_IUnknown) { *ppv = (IStream*)this;
return S_OK; }
if(riid == IID_ISequentialStream) { *ppv =
(IStream*)this; return S_OK; }
if(riid == IID_IStream) { *ppv = (IStream*)this;
return S_OK; }
*ppv = NULL; return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef(void) { return 1; }
ULONG STDMETHODCALLTYPE Release(void) { return 1; }
/// TODO: implement all IStream methods, including the
ones inherited from ISequentialStream
};

Read() simply calls ReadFile(), Seek() calls
SetFilePointer() and Stat() only need to set
STATSTG::cbSize, everything else can be zeroed out. All
other methods can just return E_NOTIMPL. Notice that the
documentation does not specify that the methods are
STDMETHODCALLTYPE, but they are.

Since the class does not properly implement ref-counting,
new it before creating the WM(Sync)Reader and delete it only
after finally releasing the reader.

mikeBearb

unread,
Sep 27, 2007, 5:16:01 PM9/27/07
to

"Alessandro Angeli" wrote:


Thank you for all your comments. So, what you're implying is that if I
overried the IStream (as done in the WMSyncReader sample or as you outline
here) interface, then my calls to GetNextSample() should start working?
Great, I'll try that next.

I'll post back once I have some results.

mikeBearb

unread,
Sep 28, 2007, 2:05:01 PM9/28/07
to
>
> Thank you for all your comments. So, what you're implying is that if I
> overried the IStream (as done in the WMSyncReader sample or as you outline
> here) interface, then my calls to GetNextSample() should start working?
> Great, I'll try that next.
>
> I'll post back once I have some results.
>

Yay! GetNextSample() doesn't fail like it did before. It now fails at the
end of a partail WMA data chunk I upload--but that's to be expected. I can
work that out later. But it looks like I can use any memory I allocate
(doesn't have to go thru GlobalAlloc()), and I can use offsets, partial
WMAs--it's great!

But now I'd thought I try using user allocated buffers with the async reader
(for the Samples it returns) but it's a no go as there's no code samples on
the matter, and what I've tried so far...my breakpoints aren't getting hit in
the callbacks I pass.

Oh well, maybe I'll have to swith to the normal reader--there's plenty of
code samples for that.

For those who care to see my current way of doing things (that fails), keep
reading. But I'll still look at it more too...

In the WMSyncReader code sample, I modified the CReader class (in reader.h)
to be:

class Creader : public IWMReaderAllocatorEx

then I implement

AllocateForStreamEx(), and
AllocateForOutputEx()

in there to just return S_OK for now.

I call WMCreateSyncReader() to create a IWMSyncReader just so I can get a
IWMSyncReader2 interface. Once I get it, I release the first IWMSyncReader
and use the '2' interface from now on. (Note: even if I just keep the first
interface around, it still fails later. I tried both ways).

Then, after I open the stream on the '2' interface (or the normal one), I do
a SetAllocateForStream(1, this), where 'this' is the CReader class which
inherits from IWMReaderAllocatorEx.

Then, later when I step through code, breakpoints I set in

CReader::AllocateForStreamEx(), or
CReader::AllocateForOutputEx()

never get hit.

I even tried making the CReader class NOT inherit from IWMReaderAllocatorEx.
I tried making a whole 'nother class just so that class can inherit from
IWMReaderAllocatorEx, and the breakpoints there would still not get hit.

Alessandro Angeli

unread,
Sep 28, 2007, 3:43:07 PM9/28/07
to
From: "mikeBearb"

> But now I'd thought I try using user allocated buffers
> with the async reader (for the Samples it returns) but
> it's a no go as there's no code samples on the matter,
> and what I've tried so far...my breakpoints aren't
> getting hit in the callbacks I pass.
>
> Oh well, maybe I'll have to swith to the normal
> reader--there's plenty of code samples for that.

What's the difference? The WMReader and WMSyncReader read
from the IStream exactly in the same way and deliver exactly
the same data. The only difference is who owns the
"streaming" thread: with the WMSyncReader you own it and you
call GetNextSample() to retrieve the samples while with the
WMReader the thread is internal to the reader and you get
the samles in a callback on the reader's own thread.

> In the WMSyncReader code sample, I modified the CReader
> class (in reader.h) to be:
>
> class Creader : public IWMReaderAllocatorEx
>
> then I implement
>
> AllocateForStreamEx(), and
> AllocateForOutputEx()
>
> in there to just return S_OK for now.

You can not "just return S_OK for now". The WMSyncReader
expects memory to be allocated by those calls and, if you
don't, how do you expect the WMSyncReader to go on?

> I call WMCreateSyncReader() to create a IWMSyncReader
> just so I can get a IWMSyncReader2 interface. Once I get
> it, I release the first IWMSyncReader and use the '2'
> interface from now on. (Note: even if I just keep the
> first interface around, it still fails later. I tried
> both ways).

It doesn't matter what interface you hold on to, as long as
you have at least 1 reference to at least 1 interface on a
given object instance.

> I even tried making the CReader class NOT inherit from
> IWMReaderAllocatorEx. I tried making a whole 'nother
> class just so that class can inherit from
> IWMReaderAllocatorEx, and the breakpoints there would
> still not get hit.

You're lucky: as far as the WMSyncReader is concerned, your
object does not support IWMReaderAllocatorEx so the reader
falls back to its own allocator. Did you properly implement
IUnknown::QueryInterface() to return a reference to your
IWMReaderAllocatorEx when asked for it?

mikeBearb

unread,
Sep 28, 2007, 4:32:00 PM9/28/07
to

"Alessandro Angeli" wrote:


Yes, in the long run, I can't do that. I did it like that for now just so I
could put a breakpoint and see it get called. Never does. But the
GetNextSample() still goes on and the wma passes thru code fine.


> > I call WMCreateSyncReader() to create a IWMSyncReader
> > just so I can get a IWMSyncReader2 interface. Once I get
> > it, I release the first IWMSyncReader and use the '2'
> > interface from now on. (Note: even if I just keep the
> > first interface around, it still fails later. I tried
> > both ways).
>
> It doesn't matter what interface you hold on to, as long as
> you have at least 1 reference to at least 1 interface on a
> given object instance.
>
> > I even tried making the CReader class NOT inherit from
> > IWMReaderAllocatorEx. I tried making a whole 'nother
> > class just so that class can inherit from
> > IWMReaderAllocatorEx, and the breakpoints there would
> > still not get hit.
>
> You're lucky: as far as the WMSyncReader is concerned, your
> object does not support IWMReaderAllocatorEx so the reader
> falls back to its own allocator. Did you properly implement
> IUnknown::QueryInterface() to return a reference to your
> IWMReaderAllocatorEx when asked for it?
>


Before inheriting from IWMReaderAllocatorEx I didn't even need to implement
the IUnknown stuff on the sync reader. But now I do, and I did it like this:

HRESULT CReader::QueryInterface( REFIID riid, void **ppv )
{
if( ( IID_IUnknown == riid ) || ( IID_IWMReaderAllocatorEx == riid ) )
{
*ppv = this;
AddRef();

return( S_OK );
}

*ppv = NULL;
return( E_NOINTERFACE );
}


still, no effect (seemingly). Whether the implementation is on the CReader
or some other autonomous class I create, the AllocateForStreamEx() never gets
called.

mikeBearb

unread,
Sep 28, 2007, 4:40:02 PM9/28/07
to
>
> HRESULT CReader::QueryInterface( REFIID riid, void **ppv )
> {
> if( ( IID_IUnknown == riid ) || ( IID_IWMReaderAllocatorEx == riid ) )
> {
> *ppv = this;
> AddRef();
>
> return( S_OK );
> }
>
> *ppv = NULL;
> return( E_NOINTERFACE );
> }
>
>
> still, no effect (seemingly). Whether the implementation is on the CReader
> or some other autonomous class I create, the AllocateForStreamEx() never gets
> called.

I just put a bp in my QueryInterface() and it does get called with
IID_IWMReaderAllocatorEx. It just insists on not calling my overrided
functions (AllocateForStreamEx(), or AllocateForOutputEx()).

I'm sure I'm missing something trivial.

Alessandro Angeli

unread,
Sep 28, 2007, 5:11:16 PM9/28/07
to
From: "mikeBearb"

> I'm sure I'm missing something trivial.

What's the full code of the class?

mikeBearb

unread,
Sep 28, 2007, 5:55:00 PM9/28/07
to
here's the CROStream for all those interested:

// rostream.h

class CROStream : public IStream
{
public:
CROStream();
~CROStream();

//HRESULT Open( LPCTSTR pwszURL );
HRESULT Open( LPBYTE pdata, ULONG DataSize );

//
// Methods of IStream
//
HRESULT STDMETHODCALLTYPE Read( void *pv, ULONG cb, ULONG *pcbRead );
HRESULT STDMETHODCALLTYPE Seek( LARGE_INTEGER dlibMove, DWORD dwOrigin,
ULARGE_INTEGER *plibNewPosition );
HRESULT STDMETHODCALLTYPE Stat( STATSTG *pstatstg, DWORD grfStatFlag );

//
// IUnknown methods
//
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void **ppv );
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();


//
// Unimplemented methods of IStream
//

HRESULT STDMETHODCALLTYPE Write( void const *pv, ULONG cb, ULONG
*pcbWritten )
{
return( E_NOTIMPL );
}
HRESULT STDMETHODCALLTYPE SetSize( ULARGE_INTEGER libNewSize )
{
return( E_NOTIMPL );
}
HRESULT STDMETHODCALLTYPE CopyTo( IStream *pstm, ULARGE_INTEGER cb,
ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten )
{
return( E_NOTIMPL );
}
HRESULT STDMETHODCALLTYPE Commit( DWORD grfCommitFlags )
{
return( E_NOTIMPL );
}
HRESULT STDMETHODCALLTYPE Revert()
{
return( E_NOTIMPL );
}
HRESULT STDMETHODCALLTYPE LockRegion( ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb, DWORD dwLockType )
{
return( E_NOTIMPL );
}
HRESULT STDMETHODCALLTYPE UnlockRegion( ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb, DWORD dwLockType )
{
return( E_NOTIMPL );
}
HRESULT STDMETHODCALLTYPE Clone( IStream **ppstm )
{
return( E_NOTIMPL );
}

protected:
//HANDLE m_hFile;
//IStream* m_IStream;
LPBYTE m_pData;
ULONG m_DataSize;
ULONG m_DataCursor;
LONG m_cRefs;
};

// rostream.cpp

CROStream::CROStream() :
m_cRefs( 1 ),
//m_hFile( INVALID_HANDLE_VALUE )
//m_IStream( NULL )
m_pData( NULL ),
m_DataSize( 0 ),
m_DataCursor( 0 )
{
}

//////////////////////////////////////////////////////////////////////////////
/////
CROStream::~CROStream()
{
/*if( INVALID_HANDLE_VALUE != m_hFile )
{
CloseHandle( m_hFile );
}*/
}

//////////////////////////////////////////////////////////////////////////////
/////
HRESULT CROStream::Open( LPBYTE pdata, ULONG DataSize )
{

HRESULT hr = S_OK;

// create the sound specific IStream
//hr = istreamHeap->Clone(&m_IStream);
m_pData = pdata;
m_DataSize = DataSize;
if ( FAILED( hr ) )
{
//_tprintf( _T( "Could not create sound specific IStream object
(hr=0x%08x).\n" ), hr );
return( hr );
}

return( hr );
}

//////////////////////////////////////////////////////////////////////////////
///////
HRESULT CROStream::Read( void *pv, ULONG cb, ULONG *pcbRead )
{
#if 0
if( !ReadFile( m_hFile, pv, cb, pcbRead, NULL ) )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#else
ULONG bufferLen = m_DataSize - m_DataCursor;
ULONG size = min(cb, bufferLen);
memcpy(pv, &m_pData[m_DataCursor], size);
m_DataCursor += size;
if (pcbRead)
*pcbRead = size;
#endif

return( S_OK );
}

//////////////////////////////////////////////////////////////////////////////
///////
HRESULT CROStream::Seek(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER *plibNewPosition )
{
DWORD dwMoveMethod;

switch( dwOrigin )
{
case STREAM_SEEK_SET:
dwMoveMethod = FILE_BEGIN;
m_DataCursor = dlibMove.LowPart;
break;

case STREAM_SEEK_CUR:
dwMoveMethod = FILE_CURRENT;
m_DataCursor += dlibMove.LowPart;
break;

case STREAM_SEEK_END:
dwMoveMethod = FILE_END;
m_DataCursor = m_DataSize + dlibMove.LowPart;
break;

default:
return( E_INVALIDARG );
};

if( NULL != plibNewPosition )
{
plibNewPosition->LowPart = m_DataCursor;
plibNewPosition->HighPart = 0;
}

return( S_OK );
}

//////////////////////////////////////////////////////////////////////////////
///////
HRESULT CROStream::Stat( STATSTG *pstatstg, DWORD grfStatFlag )
{
if( ( NULL == pstatstg ) || ( STATFLAG_NONAME != grfStatFlag ) )
{
return( E_INVALIDARG );
}

//DWORD dwFileSize = GetFileSize( m_hFile, NULL );

if( 0 )//0xffffffff == dwFileSize )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}

memset( pstatstg, 0, sizeof( STATSTG ) );

pstatstg->type = STGTY_STREAM;
pstatstg->cbSize.LowPart = m_DataSize;//dwFileSize;

return( S_OK );
}

//////////////////////////////////////////////////////////////////////////////
// IUnknown
//////////////////////////////////////////////////////////////////////////////
HRESULT CROStream::QueryInterface( REFIID riid, void **ppv )
{
if( ( IID_IUnknown == riid ) || ( IID_IStream == riid ) || (
IID_ISequentialStream == riid ) )
{
*ppv = this;
AddRef();

return( S_OK );
}

*ppv = NULL;
return( E_NOINTERFACE );
}


//////////////////////////////////////////////////////////////////////////////
ULONG CROStream::AddRef()
{
return( InterlockedIncrement( &m_cRefs ) );
}


//////////////////////////////////////////////////////////////////////////////
ULONG CROStream::Release()
{
if( 0 == InterlockedDecrement( &m_cRefs ) )
{
delete this;
return( 0 );
}

return( 0xbad );
}

mikeBearb

unread,
Sep 28, 2007, 5:34:02 PM9/28/07
to
>
> What's the full code of the class?
>

(listing below. Indentation: where there's tabs instead of spaces is
usually an indication of code I modified over the original SDK sample.
CROStream (not listed) was modifed to use my already in memory WMA data. In
this code, I get no HRESULT errors, and it runs fine as if I made no
modifiaction to the sample code, yet my breakpoints in the allocators do not
get hit.)

// reader.h

class CReader : public IWMReaderAllocatorEx
{
public:
CReader();
virtual ~CReader();

public:
HRESULT Open( const TCHAR *pwszFile );
HRESULT Close();
HRESULT GetStreamNumbers( IWMProfile* pProfile );
HRESULT ReadSamples();
HRESULT SetParams( DWORD cnsStart,
DWORD cnsEnd,
BOOL fCompressed,
BOOL fAudioPresent,
BOOL fVideoPresent,
BOOL fRangeInFrames );


private :
IWMSyncReader2* m_pReader;
WORD m_wAudioStreamNum;
WORD m_wVideoStreamNum;
QWORD m_cnsStart;
QWORD m_cnsEnd;

BOOL m_fCompressed;
BOOL m_fAudioStream;
BOOL m_fVideoStream;
BOOL m_fRangeInFrames;
CROStream* m_pStream;


//
// IUnknown methods
//

virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void
**ppv );
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();


//
// Implements IWMReaderAllocatorEx
//
virtual HRESULT STDMETHODCALLTYPE AllocateForStreamEx(
/* [in] */ WORD wStreamNum,
/* [in] */ DWORD cbBuffer,
/* [out] */ INSSBuffer **ppBuffer,
/* [in] */ DWORD dwFlags,
/* [in] */ QWORD cnsSampleTime,
/* [in] */ QWORD cnsSampleDuration,
/* [in] */ void *pvContext);

virtual HRESULT STDMETHODCALLTYPE AllocateForOutputEx(
/* [in] */ DWORD dwOutputNum,
/* [in] */ DWORD cbBuffer,
/* [out] */ INSSBuffer **ppBuffer,
/* [in] */ DWORD dwFlags,
/* [in] */ QWORD cnsSampleTime,
/* [in] */ QWORD cnsSampleDuration,
/* [in] */ void *pvContext);

// ref counting
LONG m_cRefs;
};


// reader.cpp

HRESULT CReader::QueryInterface( REFIID riid, void **ppv )
{
if( ( IID_IUnknown == riid ) || ( IID_IWMReaderAllocatorEx == riid ) )
{
*ppv = this;
AddRef();

return( S_OK );
}

*ppv = NULL;
return( E_NOINTERFACE );
}


//////////////////////////////////////////////////////////////////////////////
ULONG CReader::AddRef()


{
return( InterlockedIncrement( &m_cRefs ) );
}


//////////////////////////////////////////////////////////////////////////////
ULONG CReader::Release()


{
if( 0 == InterlockedDecrement( &m_cRefs ) )
{
delete this;
return( 0 );
}

return( 0xbad );
}

HRESULT STDMETHODCALLTYPE CReader::AllocateForStreamEx(
/* [in] */ WORD wStreamNum,
/* [in] */ DWORD cbBuffer,
/* [out] */ INSSBuffer **ppBuffer,
/* [in] */ DWORD dwFlags,
/* [in] */ QWORD cnsSampleTime,
/* [in] */ QWORD cnsSampleDuration,
/* [in] */ void *pvContext)
{
return S_OK; // putting a breakpoint here, never gets hit
}

HRESULT STDMETHODCALLTYPE CReader::AllocateForOutputEx(
/* [in] */ DWORD dwOutputNum,
/* [in] */ DWORD cbBuffer,
/* [out] */ INSSBuffer **ppBuffer,
/* [in] */ DWORD dwFlags,
/* [in] */ QWORD cnsSampleTime,
/* [in] */ QWORD cnsSampleDuration,
/* [in] */ void *pvContext)
{
return S_OK; // putting a breakpoint here, never gets hit
}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CReader::CReader( )
{
m_pReader = NULL;
m_wAudioStreamNum = 0;
m_wVideoStreamNum = 0;
m_cnsStart = 0;
m_cnsEnd = 0;
m_fCompressed = FALSE;
m_fAudioStream = TRUE;
m_fVideoStream = TRUE;
m_fRangeInFrames = FALSE;
m_pStream = NULL;

m_cRefs = 1;
}

///////////////////////////////////////////////////////////////
//////
CReader::~CReader()
{
if( NULL != m_pStream )
{
delete m_pStream;
}

//SAFE_RELEASE( m_pReaderAlloc );
//SAFE_RELEASE( m_pReader2 );
SAFE_RELEASE( m_pReader );
}

///////////////////////////////////////////////////////////////
/////
HRESULT CReader::Open( const TCHAR *ptszFile )
{

HRESULT hr = S_OK;


if ( NULL == ptszFile || NULL == _tcslen(ptszFile))
{
return( E_INVALIDARG );
}

//
// Currently we can play only files. Streams are not supported.
//
if( 0 == _tcsnicmp( ptszFile, TEXT( "http" ), 4 ) )
{
_tprintf( _T( "Wrong input file - streams are not supported :

(hr=0x%08x).\n" ) ,hr );

return( E_INVALIDARG );
}

if (NULL == m_pReader)
{
IWMSyncReader* pReader = NULL;
hr = WMCreateSyncReader( NULL, 0, &pReader );

if ( FAILED( hr ) )
{

_tprintf( _T( "Could not create reader (hr=0x%08x).\n" ), hr );
return( hr );
}

hr = pReader->QueryInterface(IID_IWMSyncReader2, (LPVOID*)&m_pReader);
pReader->Release();

if ( FAILED( hr ) )
{

_tprintf( _T( "Could not create reader2 (hr=0x%08x).\n" ), hr );
return( hr );
}

}


// This mimics the heap in ___. fill part of the heap with some wma
const unsigned int HeapSize = 2*1024*1024;
LPVOID hGlobal = new BYTE[HeapSize];//GlobalAlloc(GPTR, HeapSize);


// put some wma data into the heap at this offset
const unsigned int dataOffset = 24*1024;
DWORD fileSize = 0;
LPBYTE pdata = (LPBYTE)((unsigned int)hGlobal + dataOffset);
HANDLE hfile;
hfile = CreateFile(
ptszFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (INVALID_HANDLE_VALUE != hfile)
{
DWORD bytesRead = 0;
//fileSize = 64*1024;
fileSize = GetFileSize(hfile, NULL);
ReadFile(hfile, pdata, fileSize, &bytesRead, NULL);
CloseHandle(hfile);
}


m_pStream = new CROStream;
if( NULL == m_pStream )
{
hr = E_OUTOFMEMORY;
_tprintf( _T( "Could not open file (hr=0x%08x).\n" ), hr );
return( hr );
}

hr = m_pStream->Open( pdata, fileSize );

if( FAILED( hr ) )
{
_tprintf( _T( "Could not open file (hr=0x%08x).\n" ) ,hr );
return( hr );
}

hr = m_pReader->OpenStream( m_pStream );


if ( FAILED( hr ) )
{

_tprintf( _T( "Could not open file (hr=0x%08x).\n" ), hr );
return( hr );
}

DWORD sampleSize = 0;
hr = m_pReader->GetMaxStreamSampleSize(1, &sampleSize);


if ( FAILED( hr ) )
{

_tprintf( _T( "Could not GetMaxStreamSampleSize() (hr=0x%08x).\n" ),

hr );
return( hr );
}

hr = m_pReader->SetAllocateForStream(1, this);


if ( FAILED( hr ) )
{

_tprintf( _T( "Could not SetAllocateForStream() (hr=0x%08x).\n" ),

hr );
return( hr );
}

hr = m_pReader->SetReadStreamSamples( 1, FALSE);


if ( FAILED( hr ) )
{

_tprintf( _T( "Could not SetReadStreamSamples (hr=0x%08x).\n" ), hr );
return( hr );
}

//
// Get the profile interface
//
IWMProfile* pProfile = NULL;

hr = m_pReader->QueryInterface( IID_IWMProfile, ( VOID ** )&pProfile );


if ( FAILED( hr ) )
{

_tprintf( _T( "Could not QI for IWMProfile (hr=0x%08x).\n" ), hr );
return( hr );
}

//
// Find out stream numbers for video and audio using the profile
//
hr = GetStreamNumbers( pProfile );
SAFE_RELEASE( pProfile );


if ( FAILED( hr ) )
{

_tprintf( _T( "Could not stream numbers (hr=0x%08x).\n" ), hr );
return( hr );
}

return( hr );
}

///////////////////////////////////////////////////////////////
////
HRESULT CReader::SetParams( DWORD cnsStart = 0,
DWORD cnsEnd = 0,
BOOL fCompressed = FALSE,
BOOL fAudioPresent = TRUE,
BOOL fVideoPresent = TRUE,
BOOL fRangeInFrames = FALSE )
{
HRESULT hr = S_OK;

if( 0 != cnsEnd && cnsEnd <= cnsStart )
{
return( E_INVALIDARG );
}

m_cnsStart = cnsStart;
m_cnsEnd = cnsEnd;
m_fCompressed = fCompressed;
m_fAudioStream = fAudioPresent;
m_fVideoStream = fVideoPresent;
m_fRangeInFrames = fRangeInFrames;

return( hr );
}

///////////////////////////////////////////////////////////////
////
HRESULT CReader::Close()
{
HRESULT hr = S_OK ;

if( NULL != m_pReader )
{
hr = m_pReader->Close();


}

if( FAILED ( hr ) )
{

return hr;
}

SAFE_RELEASE( m_pReader );

return hr;
}


///////////////////////////////////////////////////////////////
//// Retrieve video and audio stream numbers from profile
////
HRESULT CReader::GetStreamNumbers(IWMProfile* pProfile)
{
HRESULT hr = S_OK;
IWMStreamConfig* pStream = NULL;
DWORD dwStreams = 0;
GUID pguidStreamType;

if ( NULL == pProfile )
{
return( E_INVALIDARG );
}

hr = pProfile->GetStreamCount( &dwStreams );


if ( FAILED( hr ) )
{

_tprintf( _T( "GetStreamCount on IWMProfile failed (hr=0x%08x).\n"

), hr );
return( hr );
}

m_wAudioStreamNum = 0;
m_wVideoStreamNum = 0;

for ( DWORD i = 0; i < dwStreams; i++ )
{
hr = pProfile->GetStream( i, &pStream );


if ( FAILED( hr ) )
{

_tprintf( _T( "Could not get Stream %d of %d from IWMProfile
(hr=0x%08x).\n" ),
i, dwStreams, hr );
break;
}

WORD wStreamNumber = 0 ;

//
// Get the stream number of the current stream
//

hr = pStream->GetStreamNumber( &wStreamNumber );


if ( FAILED( hr ) )
{

_tprintf( _T( "Could not get stream number from IWMStreamConfig
%d of %d (hr=0x%08x).\n" ),
i, dwStreams, hr );
break;
}

hr = pStream->GetStreamType( &pguidStreamType );


if ( FAILED( hr ) )
{

_tprintf( _T("Could not get stream type of stream %d of %d from
IWMStreamConfig (hr=0x%08x).\n" ),
i, dwStreams, hr ) ;
break ;
}

if( WMMEDIATYPE_Audio == pguidStreamType )
{
m_wAudioStreamNum = wStreamNumber;
}
else if( WMMEDIATYPE_Video == pguidStreamType )
{
m_wVideoStreamNum = wStreamNumber;
}

SAFE_RELEASE( pStream );
}

return( hr );
}


///////////////////////////////////////////////////////////////
////
HRESULT CReader::ReadSamples()
{
HRESULT hr = S_OK;
HRESULT hr2 = S_OK;
INSSBuffer* pSample = NULL;

WORD wStream = 1;
WMT_STREAM_SELECTION wmtSS = WMT_ON;
QWORD cnsSampleTime = 0, cnsPrevSampleTime = 0;
QWORD cnsDuration = 0;
DWORD dwFlags = 0;
DWORD dwOutputNum = 0;
WORD wStreamNum = 0;
static DWORD dwVideoSamplesCnt = 0;
static DWORD dwAudioSamplesCnt = 0;


if( 0 != m_wAudioStreamNum )
{
if( m_fAudioStream )
{
wmtSS = WMT_ON;
}
else
{
wmtSS = WMT_OFF;
}

hr = m_pReader->SetStreamsSelected( 1, &m_wAudioStreamNum, &wmtSS );


if ( FAILED( hr ) )
{

_tprintf( _T( "SetStreamsSelected (hr=0x%08x).\n" ), hr );
return( hr );
}

hr = m_pReader->SetReadStreamSamples( m_wAudioStreamNum,
m_fCompressed );


if ( FAILED( hr ) )
{

_tprintf( _T( "SetReadStreamSamples (hr=0x%08x).\n" ), hr );
return( hr );
}
}

if( 0 != m_wVideoStreamNum )
{
if( m_fVideoStream )
{
wmtSS = WMT_ON;
}
else
{
wmtSS = WMT_OFF;
}

hr = m_pReader->SetStreamsSelected( 1, &m_wVideoStreamNum, &wmtSS );


if ( FAILED( hr ) )
{

_tprintf( _T( "SetStreamsSelected (hr=0x%08x).\n" ), hr );
return( hr );
}

hr = m_pReader->SetReadStreamSamples( m_wVideoStreamNum,
m_fCompressed );


if ( FAILED( hr ) )
{

_tprintf( _T( "SetReadStreamSamples (hr=0x%08x).\n" ), hr );
return( hr );
}

if( m_fRangeInFrames )
{
QWORD qwDuration = 0;

if( 0 != m_cnsEnd )
{
qwDuration = m_cnsEnd - m_cnsStart;
}

hr = m_pReader->SetRangeByFrame( m_wVideoStreamNum, m_cnsStart,
qwDuration );


if ( FAILED( hr ) )
{

_tprintf( _T( "SetRangeByFrame (hr=0x%08x).\n" ), hr );
return( hr );
}
}
}

if( 0 == m_wVideoStreamNum || ( !m_fRangeInFrames && 0 !=
m_wVideoStreamNum ) )
{
QWORD qwDuration = 0;

if( 0 != m_cnsEnd )
{
qwDuration = ( m_cnsEnd - m_cnsStart ) * 10000L;
}

hr = m_pReader->SetRange( m_cnsStart * 10000L, qwDuration );


if ( FAILED( hr ) )
{

_tprintf( _T( "SetRange (hr=0x%08x).\n" ), hr );
return( hr );
}

}

_tprintf( _T( "\nGetting samples ...\n" ) );

while( SUCCEEDED( hr ) )
{


hr = m_pReader->GetNextSample( 0, &pSample,
&cnsSampleTime,
&cnsDuration,
&dwFlags,
&dwOutputNum,
&wStreamNum );

if( FAILED( hr ) )
{
if( NS_E_NO_MORE_SAMPLES == hr )
{
hr = S_OK;
_tprintf( _T( "\nLast sample reached.\n" ) );
_tprintf( _T( "\nLast sample time : %lu ms\n" ),
cnsPrevSampleTime/10000 );
break;
}
else
{
_tprintf( _T( "GetNextSample() failed : (hr=0x%08x).\n" ),

hr );
return( hr );
}
}

cnsPrevSampleTime = cnsSampleTime;

if( 0 == dwVideoSamplesCnt && 0 == dwAudioSamplesCnt )
{
_tprintf( _T( "\nFirst sample time : %lu ms\n" ),
cnsSampleTime/10000 );
}

if( m_wVideoStreamNum == wStreamNum )
{
dwVideoSamplesCnt++;
if ( 0 == dwVideoSamplesCnt % 4 )
{
_tprintf( _T( "v" ) );
}
}
else if( m_wAudioStreamNum == wStreamNum )
{
dwAudioSamplesCnt++;

if ( 0 == dwAudioSamplesCnt % 4 )
{
_tprintf( _T( "a" ) );
}
}


pSample->Release();
}
return( hr );
}


Alessandro Angeli

unread,
Sep 28, 2007, 6:19:40 PM9/28/07
to
From: "mikeBearb"

> hr = m_pReader->GetMaxStreamSampleSize(1, &sampleSize);

> hr = m_pReader->SetAllocateForStream(1, this);

> hr = m_pReader->SetReadStreamSamples( 1, FALSE);

The only thing I can spot out of the mess of your code...
Shouldn't "1" be "m_wAudioStreamNum" instead? Which requires
you to call GetStreamNumbers() before this instead of at the
end of Open().

mikeBearb

unread,
Sep 28, 2007, 6:42:00 PM9/28/07
to

"Alessandro Angeli" wrote:

> From: "mikeBearb"
>
> > hr = m_pReader->GetMaxStreamSampleSize(1, &sampleSize);
> > hr = m_pReader->SetAllocateForStream(1, this);
> > hr = m_pReader->SetReadStreamSamples( 1, FALSE);
>
> The only thing I can spot out of the mess of your code...

Ouch. Don't you mean _your_ code, as in, MS code? You realize this is just
MS SDK code with minimal mods from me?


> Shouldn't "1" be "m_wAudioStreamNum" instead? Which requires
> you to call GetStreamNumbers() before this instead of at the
> end of Open().
>

I wish this were it. But I'm using mono audio wma streams, so
m_wAudioStreamNum is always 1 anyway. I relocated the call to
GetStreamNumbers() to before (anyway just to qualm our concerns), plugged in
the use of the var instead of hard-coded '1', and still doesn't change the
beviour.

Well, thanks anyway for all your help.

Alessandro Angeli

unread,
Sep 28, 2007, 6:54:00 PM9/28/07
to
From: "mikeBearb"

> Ouch. Don't you mean _your_ code, as in, MS code? You

"My" code? I don't work for MS.

> realize this is just MS SDK code with minimal mods from
> me?

Your contribution shined :-P

> I wish this were it. But I'm using mono audio wma

I'm sorry, but I can't think of anything else that may be
wrong in the code you posted.

Why do you want to use your own allocator, anyway?

mikeBearb

unread,
Sep 28, 2007, 7:34:02 PM9/28/07
to

"Alessandro Angeli" wrote:

> From: "mikeBearb"
>
> > Ouch. Don't you mean _your_ code, as in, MS code? You
>
> "My" code? I don't work for MS.
>
> > realize this is just MS SDK code with minimal mods from
> > me?
>
> Your contribution shined :-P

uncordial. It was in MS sample form--I don't know how it looked any
different.

>
> > I wish this were it. But I'm using mono audio wma
>
> I'm sorry, but I can't think of anything else that may be
> wrong in the code you posted.
>
> Why do you want to use your own allocator, anyway?
>

I want to minimize the creation/deletion of random INSSBuffers everytime I
want an uncompressed sample.

Ideally, I want just the raw WMA buffer (in my "heap" already), then I want
a decoder to just read through that and put its results straight into a
dsound buffer. So, in a perfect world, there's 2 buffers, per sound I want
to play back.

But I suppose I can live with a third buffer (the INSSBuffer) in there, per
sound. heap buffer --> INSSBuffer --> dsound buffer.

I was trying to be efficient. Remember, I'm gonna have anywhere from 5 to
20 sounds playing back at any given time. More small sounds than big ones,
but still. Or maybe, this SDK is the wrong choice for my requirements, of
which a big one is compression. Our current engine works fine, but with all
straight PCM data.

So now I move on to writing the decoding thread, with 3 buffers.

Alessandro Angeli

unread,
Sep 28, 2007, 8:13:36 PM9/28/07
to
From: "mikeBearb"

>> Your contribution shined :-P
>
> uncordial. It was in MS sample form--I don't know how it
> looked any different.

Wasn't the ":-P" emoticon clue enough that I was joking?

> I want to minimize the creation/deletion of random
> INSSBuffers everytime I want an uncompressed sample.
>
> Ideally, I want just the raw WMA buffer (in my "heap"
> already), then I want a decoder to just read through that
> and put its results straight into a dsound buffer. So,
> in a perfect world, there's 2 buffers, per sound I want
> to play back.

You would have to work hard to make the requested buffers
fit sequentially and without gaps in your DirectSound
buffer, because there is no guarantee on the order and
number of buffers requested by the reader.

> But I suppose I can live with a third buffer (the
> INSSBuffer) in there, per sound. heap buffer -->
> INSSBuffer --> dsound buffer.
>
> I was trying to be efficient. Remember, I'm gonna have
> anywhere from 5 to 20 sounds playing back at any given
> time. More small sounds than big ones, but still. Or
> maybe, this SDK is the wrong choice for my requirements,
> of which a big one is compression. Our current engine
> works fine, but with all straight PCM data.

You should notice that the default playback graph for audio
has at least 2 intermediary buffers so that's unlikely going
to be the efficiency bottleneck.

I didn't know you were also playing the decoded data through
DirectSound, otherwise I would have suggested to implement
an async reader for DirectShow, so that you wouldn't have
had to worry about buffer allocations or DirectSound.
Implementing an async reader for DirectShow is similar to
implementing IStream and just as easy:

http://groups.google.com/group/microsoft.public.win32.programmer.directx.video/msg/01dde303fa3088c5

1. write your async reader class
2. new the class and add it to an empty graph
3. enumerate your class' pins and render the output ones
(there is only 1 pin actually)
4. run the graph

mikeBearb

unread,
Sep 29, 2007, 12:32:00 AM9/29/07
to

"Alessandro Angeli" wrote:

I just tried the platform SDK Async/Memfile code sample and it fails on
rendering the pin when I pass it my .wma file. The hr is 0x80040218. I
tried setting a few different media types (MEDIASUBTYPE_Asf) but still fails.

Now this code sample I did NOT modify except for trying different media
sub-types. And this is a regular .wma I had the WMEncoder SDK build for me.
It plays fine in media player.

Although, when I plug in a wav file it plays fine and uses MEDIASUBTYPE_WAVE.

Any ideas why it refuses to render asf files? In particular, .wma?

mikeBearb

unread,
Sep 29, 2007, 12:26:01 AM9/29/07
to

"Alessandro Angeli" wrote:

Alessandro Angeli

unread,
Sep 29, 2007, 12:06:26 PM9/29/07
to
From: "mikeBearb"

> I just tried the platform SDK Async/Memfile code sample
> and it fails on rendering the pin when I pass it my .wma
> file. The hr is 0x80040218. I tried setting a few
> different media types (MEDIASUBTYPE_Asf) but still fails.

[...]


> Any ideas why it refuses to render asf files? In
> particular, .wma?

I told you a few posts ago: <<<use Geraint's ASF parser for


DirectShow and write a custom async reader based on the

Async sample>>>. You get VFW_E_CANNOT_RENDER because there
is no stock splitter for ASF. The stock ASF parser is built
into the WMASFReader push source so, without a little hack,
there is no way to source ASF streams from an async reader
unless you use a third-party ASF splitter like Geraint's
(which internally uses the WMReader object just like the
WMASFReader does). You can get the filter's source code from
www.gdcl.co.uk. You just need to add it to the graph before
rendering your output pin.

On the other, it just occurred to me that there might be a
way to make the stock WMASFReader filter read from an
IStream, so that you do not have to use your async reader
with Geraint's parser:

1. co-create the WMASFReader and add it to the an empty
graph

2. instead of calling IFileSourceFilter::Load():

2a. QI the filter for IServiceProvider

2b. get IWMDRMReader from IServiceProvider

2c. QI the IWMDRMReader for IWMReaderAdvanced2

2d. call IWMReaderAdvanced2::OpenStream()

3. render the filter's output pins

mikeBearb

unread,
Sep 29, 2007, 1:11:03 PM9/29/07
to

"Alessandro Angeli" wrote:

> From: "mikeBearb"
>
> > I just tried the platform SDK Async/Memfile code sample
> > and it fails on rendering the pin when I pass it my .wma
> > file. The hr is 0x80040218. I tried setting a few
> > different media types (MEDIASUBTYPE_Asf) but still fails.
> [...]
> > Any ideas why it refuses to render asf files? In
> > particular, .wma?
>
> I told you a few posts ago: <<<use Geraint's ASF parser for
> DirectShow and write a custom async reader based on the
> Async sample>>>.


Sheesh, okay, thanks (you're yelling again :)). (You mean the post 3 days
ago where you recommend a list of solutions, 1 thru 6, and you suggested I do
number 5, but now you're yelling at me for not doing this one, number 4,
which you think I should do now?). I'm all over it.


> You get VFW_E_CANNOT_RENDER because there
> is no stock splitter for ASF. The stock ASF parser is built
> into the WMASFReader push source so, without a little hack,
> there is no way to source ASF streams from an async reader
> unless you use a third-party ASF splitter like Geraint's
> (which internally uses the WMReader object just like the
> WMASFReader does). You can get the filter's source code from
> www.gdcl.co.uk. You just need to add it to the graph before
> rendering your output pin.
>
> On the other, it just occurred to me that there might be a
> way to make the stock WMASFReader filter read from an
> IStream, so that you do not have to use your async reader
> with Geraint's parser:
>
> 1. co-create the WMASFReader and add it to the an empty
> graph
>
> 2. instead of calling IFileSourceFilter::Load():
>
> 2a. QI the filter for IServiceProvider
>
> 2b. get IWMDRMReader from IServiceProvider
>
> 2c. QI the IWMDRMReader for IWMReaderAdvanced2
>
> 2d. call IWMReaderAdvanced2::OpenStream()
>
> 3. render the filter's output pins
>

Really, thanks, for all your help and suggestions (I don't care how much you
yell at me). I'm bound to hit the right one sooner or later. I go try this
in a bit.

Alessandro Angeli

unread,
Sep 29, 2007, 2:02:56 PM9/29/07
to
From: "mikeBearb"

> Sheesh, okay, thanks (you're yelling again :)). (You
> mean the post 3 days ago where you recommend a list of
> solutions, 1 thru 6, and you suggested I do number 5, but
> now you're yelling at me for not doing this one, number
> 4, which you think I should do now?). I'm all over it.

In the beginning I didn't understand you wanted to actually
render the decoded audio through DirectSound, so I suggested
the simplest way to just extract and decode the data from
your custom file. But if you want to render it right away,
then using DirectShow is easier because that's what
DirectShow is designed for.

> Really, thanks, for all your help and suggestions (I
> don't care how much you yell at me).

I don't yell :-) I am just a bit touchy about people not
RTFM'ing or reading replies after somebody wasted his/her
free time writing them and after a few years in those forums
you would be too. You just happened to be caught in the
crossfire, so to speak, without actually deserving
it.</rant>

> I'm bound to hit
> the right one sooner or later. I go try this in a bit.

All the suggestions I gave you so far will get the job done
since I used them or parts of them one time or another (with
the exception of fooling the WMASFReader into opening an
IStream, since I only thought of that while I was replying
to you). It's just a question of deciding which one is
easiest to implement and best suited for your specific task.

mikeBearb

unread,
Oct 1, 2007, 11:40:00 PM10/1/07
to

"Alessandro Angeli" wrote:

How do you do this step exactly. Whatever I choose:

HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IFilterGraph, (void**) ppFG);

IServiceProvider *psp = NULL;
hr = (*ppFG)->QueryInterface(IID_IServiceProvider, (void**)&psp);

//IWMSyncReader2 *pwmr = NULL;
//IWMSyncReader *pwmr = NULL;
//IWMDRMReader *pwmr = NULL;
IWMReader *pwmr = NULL;
//hr = psp->QueryService(__uuidof(IWMReader), IID_IWMReader, (void**
&pwmr);
hr = psp->QueryInterface(IID_IWMReader, (void**)&pwmr);

here, I get, NOINTERFACE.

I don't care about DRM, btw. That is, I don't want to use DRM. Does it
matter? Probably not.

By the way, I'm modifying the PSDK (dshow\filters\async\) memfile sample,
function SelectAndRender().

Alessandro Angeli

unread,
Oct 2, 2007, 12:03:40 AM10/2/07
to
From: "mikeBearb"

>> 1. co-create the WMASFReader and add it to the an empty
>> graph

[...]


> IServiceProvider *psp = NULL;
> hr = (*ppFG)->QueryInterface(IID_IServiceProvider,
> (void**)&psp);

Why are you QI'ing the graph for IServiceProvider? You need
to QI an instance of the WMASFReader filter after adding it
to the graph.

> I don't care about DRM, btw. That is, I don't want to
> use DRM. Does it matter? Probably not.

You don't need IWMDRMReader: it's just a necessary step to
get to IWMReaderAdvanced2 (if it works).

0 new messages