Transform GraphEdit XML (XGR) file to C++

135 views
Skip to first unread message

TomJ

unread,
Jan 13, 2003, 6:05:14 PM1/13/03
to
I have made an attempt at creating an XSLT stylesheet to transform a
GraphEdit XML (XGR) filter graph into C++. I don't know how useful it will
be in practice but it might help as a starting point.

One area I was unsure of is passing parameters to filters as specified in
the PARAM node. Where a FILTER element has a PARAM element with a src
attribute e.g.

<FILTER ID="VOB_Source_0" clsid="{E436EBB5-524F-11CE-9F53-0020AF0BA770}">
<PARAM name="src" value="C:\vts_02_4.vob"/>
</FILTER>

I have used IGraphBuilder::AddSourceFilter, but maybe this is incorrect.

If anyone would like to suggest changes or enhancements I would be very
grateful.

<!-- GraphEditToCpp.xsl -->
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/">
<![CDATA[
#include <dshow.h>
#include <comdef.h>
#include <atlbase.h>
#include <iostream>

inline void TESTHR(HRESULT hr)
{
if(FAILED(hr))
{
_com_raise_error(hr);
}
}

void main(void)
{
CoInitialize(NULL);

try
{
]]>
<xsl:apply-templates select="GRAPH"/>
<![CDATA[
}
catch(_com_error &e)
{
const char *errMsg = e.ErrorMessage();
const char *errDesc = e.Description();

std::cout << "Error: ";
if(errMsg != NULL)
{
std::cout << errMsg << std::endl;
}

if(errDesc != NULL)
{
std::cout << errDesc << std::endl;
}
}
catch(...)
{
std::cout << "Error: no details";
}
}
]]>
</xsl:template>

<xsl:template match="GRAPH">
<![CDATA[
CComPtr<IGraphBuilder> pGraph;
CComPtr<IMediaControl> pMediaControl;
CComPtr<IMediaEvent> pEvent;

TESTHR(pGraph.CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER));
TESTHR(pGraph->QueryInterface(IID_IMediaControl, (void
**)&pMediaControl));
TESTHR(pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent));
]]>
CLSID clsid;
<xsl:apply-templates select="FILTER">
<xsl:sort select="position()" order="descending"/>
</xsl:apply-templates>

<xsl:apply-templates select="connect"/>
<![CDATA[
pMediaControl->Run();
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
]]>
</xsl:template>

<xsl:template match="FILTER">
<!-- Create filter -->
CComPtr&lt;IBaseFilter&gt; <xsl:apply-templates select="@ID"/>;
TESTHR(CLSIDFromString(L&quot;<xsl:value-of select="@clsid"/>&quot;,
&amp;clsid));
TESTHR(<xsl:apply-templates select="@ID"/>.CoCreateInstance(clsid, NULL,
CLSCTX_INPROC_SERVER));
pGraph->AddFilter(<xsl:apply-templates select="@ID"/>,
L&quot;<xsl:value-of select="@ID"/>&quot;);
</xsl:template>

<xsl:template match="FILTER[PARAM/@name='src']">
<!-- Create source filter -->
<xsl:variable name="FileName" select="translate(PARAM/@value, '\', '/')"/>
CComPtr&lt;IBaseFilter&gt; <xsl:apply-templates select="@ID"/>;
pGraph-&gt;AddSourceFilter(L&quot;<xsl:value-of
select="$FileName"/>&quot;, L&quot;<xsl:value-of select="$FileName"/>&quot;,
&amp;<xsl:apply-templates select="@ID"/>);
</xsl:template>

<xsl:template match="connect">
<!-- Connec filters -->
CComPtr&lt;IPin&gt; pSrcPin<xsl:value-of select="position()"/>,
pDestPin<xsl:value-of select="position()"/>;
<xsl:apply-templates select="@src"/>-&gt;FindPin(L&quot;<xsl:value-of
select="@srcpin"/>&quot;, &amp;pSrcPin<xsl:value-of select="position()"/>);
<xsl:apply-templates select="@dest"/>-&gt;FindPin(L&quot;<xsl:value-of
select="@destpin"/>&quot;, &amp;pDestPin<xsl:value-of
select="position()"/>);
TESTHR(pGraph->ConnectDirect(pSrcPin<xsl:value-of select="position()"/>,
pDestPin<xsl:value-of select="position()"/>, NULL));
</xsl:template>

<xsl:template match="@ID | @src | @dest">
<!-- Make a valid C++ variable name (not very strict) -->
<xsl:value-of select="translate(.,
'ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyz1234567890_/- ',
'ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyz1234567890____')"/>
</xsl:template>

</xsl:stylesheet>


Bill Arnette

unread,
Jan 14, 2003, 9:09:41 AM1/14/03
to
"TomJ" <tjulie...@hotmail.com> wrote in
news:avvgna$3nh$1...@news6.svr.pol.co.uk:

> I have made an attempt at creating an XSLT stylesheet to transform
> a GraphEdit XML (XGR) filter graph into C++. I don't know how
> useful it will be in practice but it might help as a starting
> point.
>
> One area I was unsure of is passing parameters to filters as
> specified in the PARAM node. Where a FILTER element has a PARAM
> element with a src attribute e.g.
>
><FILTER ID="VOB_Source_0"
><clsid="{E436EBB5-524F-11CE-9F53-0020AF0BA770}">
> <PARAM name="src" value="C:\vts_02_4.vob"/>
></FILTER>
>

That's cool!

I think you are fine using IGraphBuilder::AddSourceFilter to add this
filter. The 'src' attribute probably indicates a filter which
implements IFileSourceFilter. Therefore you can either use
IGraphBuilder::AddSourceFilter or create the filter, QI for
IFileSourceFilter and call IFileSourceFilter::Load to specify the
input file name.

If the filter does not support IFileSourceFilter, then you will
probably have to handle it like you do a 'data' PARAM element except
you will load the stream from a structured storage file and then call
IPersistStream::Load on the filter.

HTH,
Bill


--
Bill Arnette
Signalscape, Inc.
bi...@MAPSONsignalscape.com
www.signalscape.com

Alessandro Angeli [MVP::DigitalMedia]

unread,
Jan 14, 2003, 11:27:10 AM1/14/03
to

Bill, if you use AddSourceFilter() with the "src" PARAM, you
might not get the same graph in case a different source
filter is chosen (for example on a different machine or if
you modify the setup). To ensure a predictable behavior, you
must create the filter using the specified CLSID, add it to
the graph and then use it's IFileSourceFilter::Load()
method, which is supported, since it was used when the
orginal graph was created (unless of course you install an
incompatible version of the same filter, but this somewhat
breaks the spirit of COM rules).


"Bill Arnette" <bi...@MAPSONsignalscape.com> wrote in
message
news:Xns93035D3F3E8CCbi...@207.46.248.16...

TomJ

unread,
Jan 14, 2003, 4:13:59 PM1/14/03
to
Thank you for the suggestions. I'll make some changes and repost the
stylesheet.

"Alessandro Angeli [MVP::DigitalMedia]" <a.angel...@sogetelREMOVE.it>
wrote in message news:uBCWln#uCHA.2060@TK2MSFTNGP11...

TomJ

unread,
Jan 15, 2003, 4:35:14 PM1/15/03
to
Here's the modified stylesheet that uses IFileSourceFilter.

BTW I'm using the command-line utility msxsl.exe to transform the XML. It is
available for download here
http://download.microsoft.com/download/xml/Utility/2.0/NT5XP/EN-US/msxsl.exe

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text" encoding="utf-8"/>

void main(void)
{
CoInitialize(NULL);

TESTHR(pGraph.CoCreateInstance(CLSID_FilterGraph));
TESTHR(pGraph.QueryInterface(&pMediaControl));
TESTHR(pGraph.QueryInterface(&pEvent));


]]>
CLSID clsid;
<xsl:apply-templates select="FILTER">
<xsl:sort select="position()" order="descending"/>
</xsl:apply-templates>

<xsl:apply-templates select="connect"/>
<![CDATA[

TESTHR(pMediaControl->Run());
long evCode;
TESTHR(pEvent->WaitForCompletion(INFINITE, &evCode));
]]>
</xsl:template>

<xsl:template match="FILTER">
<!-- Create filter -->
CComPtr&lt;IBaseFilter&gt; <xsl:apply-templates select="@ID"/>;
TESTHR(CLSIDFromString(L&quot;<xsl:value-of select="@clsid"/>&quot;,
&amp;clsid));

TESTHR(<xsl:apply-templates select="@ID"/>.CoCreateInstance(clsid));
<xsl:apply-templates select="PARAM"/>
TESTHR(pGraph->AddFilter(<xsl:apply-templates select="@ID"/>,
L&quot;<xsl:value-of select="@ID"/>&quot;));
</xsl:template>

<xsl:template match="connect">
<!-- Connect filters -->


CComPtr&lt;IPin&gt; pSrcPin<xsl:value-of select="position()"/>,
pDestPin<xsl:value-of select="position()"/>;

TESTHR(<xsl:apply-templates


select="@src"/>-&gt;FindPin(L&quot;<xsl:value-of select="@srcpin"/>&quot;,

&amp;pSrcPin<xsl:value-of select="position()"/>));
TESTHR(<xsl:apply-templates


select="@dest"/>-&gt;FindPin(L&quot;<xsl:value-of select="@destpin"/>&quot;,

&amp;pDestPin<xsl:value-of select="position()"/>));
<xsl:choose>
<xsl:when test="@direct = 'yes'">


TESTHR(pGraph->ConnectDirect(pSrcPin<xsl:value-of select="position()"/>,
pDestPin<xsl:value-of select="position()"/>, NULL));

</xsl:when>
<xsl:otherwise>
TESTHR(pGraph->Connect(pSrcPin<xsl:value-of select="position()"/>,


pDestPin<xsl:value-of select="position()"/>, NULL));

</xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="@ID | @src | @dest">

<!-- Make a valid-ish C++ variable name -->


<xsl:value-of select="translate(.,
'ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyz1234567890_/- ',
'ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyz1234567890____')"/>
</xsl:template>

<xsl:template match="PARAM">
<xsl:choose>
<xsl:when test="@name='src'">
CComPtr&lt;IFileSourceFilter&gt; pFileSource;
TESTHR(<xsl:apply-templates
select="../@ID"/>.QueryInterface(&amp;pFileSource));
TESTHR(pFileSource-&gt;Load(L&quot;<xsl:value-of select="translate(@value,
'\', '/')"/>&quot;, NULL));
</xsl:when>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>


Bill Arnette

unread,
Jan 16, 2003, 2:14:49 PM1/16/03
to
That looks good. Now you need to handle <PARAM name='data'/> tags to
handle persisted state of non-source filters. I wrote a grf2xml
utility before graphedt had the XGR capability and I used the
bin.base64 datatype to encode the filters' persisted state. I don't
know what encoding the XGR file is using though.

I think you could create the code section like this (untested):

CComQIPtr<IPersistStream> ps = pFilter;
if (ps)
{
BYTE *pBuf;
DWORD bufLen;
// Decode the data string using a global function
CComPtr<IStream> pStream;
DecodeDataStringToStream("<value-of select="@value"/>", &pStream)
if (pStream)
{
// Load the filter state from the stream
ps->Load(pStream);
}
}

The above code would go in the PARAM tag template and the global
function DecodeDataStringToStream would either be a part of the /
template or it could be linked in. Either way
DecodeDataStringToStream would be implemented something like:

HRESULT DecodeDataStringToStream(LPCSTR pDataString, IStream
**pStream)
{
DWORD bufLen = 0;
// Get the required decode buffer length
DecodeDataString(pData, NULL, &bufLen);

// Allocate a buffer to decode to
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, bufLen);
BYTE *pDecoded = GlobalLock(hMem);

// Decode the buffer
DecodeDataString(pDataString, pDecoded, &bufLen);

// Return a stream with the decoded data, allow the
// memory to be freed when the stream is finally
// released
return CreateStreamOnHGlobal(hMem, TRUE, pStream);
}

DWORD DecodeDataString(LPCSTR pStr, LPBYTE pDecodeBuf, DWORD pBufLen)
{
// Calculate the buffer size needed for the decoded data
// if *pBufLen < decodeLen, set pBufLen to the size needed and
// return
*pBufLen = decodedLen;

if (pDecodeBuf)
{
// Actually decode the string into the buffer pointed
// to by pDecodeBuf
}

return decodedLen;
}

cheers,
Bill

"TomJ" <tjulie...@hotmail.com> wrote in
news:b04k6f$lin$1...@newsg1.svr.pol.co.uk:

--

Jeff

unread,
Jan 19, 2003, 8:25:11 AM1/19/03
to
Hi All -

How do you get access to the "data" to persist a non-source filter? I
am referring to the "data" like you see in an xgr file where <PARAM
name="data"> appears. I would love to see some code that accesses this
persistence data, stores it to a file, then later reloads it from a
file and restores the filters settings.

- Jeff Dodson

Alessandro Angeli [MVP::DigitalMedia]

unread,
Jan 19, 2003, 1:30:32 PM1/19/03
to
One of these should contain the info you're looking for:

=== saving and restoring filter state ===
http://groups.google.com/groups?th=7175230925fbe08c
=== saving and restoring AVI compressor state ===
http://groups.google.com/groups?th=908728ad897d423
http://groups.google.com/groups?th=df9eb79ac6e9589d
http://groups.google.com/groups?th=abe9c147f73bca8c
http://groups.google.com/groups?th=cdef9149a22f830d
=== saving and restoring filter state using
ISpecifyPropertyPages ===
http://groups.google.com/groups?th=1c62d3ecc1eee33f
=== how to use ISpecifyPropertyPages ===
http://groups.google.com/groups?th=8719df7c358cb03a
http://groups.google.com/groups?th=ecfdb3f3451d1a6d

"Jeff" <jef...@netzero.net> wrote in message
news:8d8014d2.03011...@posting.google.com...

Reply all
Reply to author
Forward
0 new messages