<html>
<head>
    <base href="https://wiki.asterisk.org/wiki">
            <link rel="stylesheet" href="/wiki/s/en/2171/18/9/_/styles/combined.css?spaceKey=TOP&amp;forWysiwyg=true" type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://wiki.asterisk.org/wiki/display/TOP/Media+Operations+Design">Media Operations Design</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://wiki.asterisk.org/wiki/display/~khunt">Ken Hunt</a>
    </h4>
        <br/>
                         <h4>Changes (0)</h4>
                                 
    
<div id="page-diffs">
                    <table class="diff" cellpadding="0" cellspacing="0">
    
            <tr><td class="diff-snipped" >...<br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <p>Asterisk SCF has some general APIs defined for media operations. These are expressed with the <tt>MediaOperation</tt> and <tt>MediaOperationFactory</tt> APIs. One of the most common media operations to implement is one that translates one format to another. Asterisk SCF's MediaOperationsCore repo provides some base classes for defining translators that make the task of writing a new format translator easy.</p>

<p>To illustrate what is provided by the translator base classes, we will be creating a mock translator that translates between two fake audio formats, <tt>Foo</tt> and <tt>Bar</tt>. <tt>Foo</tt> and <tt>Bar</tt> are presumably defined in a slice file somewhere. Additionally, the names of the formats are also defined in the slice and can be referred to by the constants <tt>FooName</tt> and <tt>BarName</tt>.</p>

<h1><a name="MediaOperationsDesign-TranslatorOperationFactory"></a>TranslatorOperationFactory</h1>

<p><tt>TranslatorOperationFactory</tt> is a subclass of the C++ class <tt>MediaOperationFactoryImpl</tt>, which implements the slice interface <tt>MediaOperationFactory</tt>. The <tt>MediaOperationFactoryImpl</tt> provides helper methods and data members that all media operations will find handy. The <tt>TranslationOperationFactory</tt> refines this further by providing methods and members specific to translators.</p>

<p>We will create a <tt>FooBarOperationFactory</tt> that will create the proper translator operation. We need to write a constructor, and we need to overload the <tt>createMediaOperation</tt> method.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: java; gutter: false">FooBarOperationFactory::FooBarOperationFactory(const Ice::ObjectAdapterPtr&amp; adapter,
        const Logger&amp; logger,
        const MediaOperationReplicationContextPtr&amp; replicationContext)
    : TranslatorOperationFactory(adapter, logger, replicationContext, "FooBarOperationFactory")
{
    FormatPtr foo = new Foo();
    foo-&gt;name = FooName;

    FormatPtr bar = new Bar();
    bar-&gt;name = BarName;

    addTranslation(foo, bar, 100);
    addTranslation(bar, foo, 100);
}

MediaOperationPrx FooBarOperationFactory::createMediaOperation(
        const FormatPtr&amp; sourceFormat,
        const FormatPtr&amp; sinkFormat,
        const std::string&amp; operationId)
{
    FooBarOperationPtr operation(
            new FooBarOperation(
                mAdapter,
                mLogger,
                sourceFormat,
                sinkFormat,
                getProxy()-&gt;ice_getIdentity(),
                mReplicationContext));

    MediaOperationPrx proxy = operation-&gt;activate(operationId);
    return proxy;
}</pre>
</div></div>

<p>In the first part of the constructor, we call the <tt>TranslatorOperationFactory</tt> constructor. The <tt>TranslatorOperationFactory</tt> takes the same parameters as the <tt>FooBarOperationFactory</tt>, except that we provide a name for the factory.</p>

<p>In the constructor, we make use of the <tt>TranslatorOperationFactory's</tt> <tt>addTranslation</tt> method in order to register the translations our operation can do. In this case, we are registering a translation from <tt>Foo</tt> to <tt>Bar</tt> and from <tt>Bar</tt> to <tt>Foo</tt>. Registering our translations will result in this factory being found when the service locator is queried for an operation that can perform one of these two translations.</p>

<p>You may notice that the <tt>createMediaOperation</tt> method is not the method defined in the <tt>MediaOperationIf.ice</tt> Slice file. This <tt>createMediaOperation</tt> method is a virtual method of the <tt>TranslatorOperationFactory</tt> class that must be defined in the subclass. The parameters for this method are geared more towards translators, specifically telling the formats that will be translated by this operation.</p>

<p>There are some methods called in <tt>createMediaOperation</tt> that will need further explanation. The <tt>FooBarOperation</tt> constructor will be covered in the next section. The <tt>activate</tt> method is defined in the <tt>TranslatorOperation</tt> class. It handles the creation of proxies and addition of them to the object adapter. The <tt>getProxy</tt> method is defined in <tt>MediaOperationFactoryImpl</tt> and gets the proxy for the factory.</p>

<h1><a name="MediaOperationsDesign-TranslatorOperation"></a>TranslatorOperation</h1>

<p><tt>TranslatorOperation</tt> is a subclass of the C++ class <tt>MediaOperationImpl</tt>, which implements the Slice interface <tt>MediaOperation</tt>.</p>

<p>We will create a <tt>FooBarOperation</tt> subclass of <tt>TranslatorOperation</tt>.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: java; gutter: false">FooBarOperation::FooBarOperation(const Ice::ObjectAdapterPtr&amp; adapter,
        const Logger&amp; logger,
        const FormatPtr&amp; sourceFormat,
        const FormatPtr&amp; sinkFormat,
        const Ice::Identity&amp; factoryId,
        const MediaOperationReplicationContextPtr&amp; replicationContext)
    : TranslatorOperation(
            adapter,
            logger,
            sourceFormat,
            sinkFormat,
            factoryId,
            replicationContext,
            new FooBarOperationStateItem)
{
    mSink-&gt;setTranslator(new FooBarTranslator(mSource, sinkFormat, sourceFormat, mLogger));
}</pre>
</div></div>

<p>This is all that is needed to create the operation. The <tt>TranslatorOperation</tt> takes care of the most of the setup. Amongst what is set up are a <tt>StreamSource</tt> and a <tt>StreamSink</tt>, specifically a <tt>TranslatorSource</tt> and a <tt>TranslatorSink</tt>. The sink needs to be given a translator to which to feed its media, so we do this in the same line where we create the translator by using the <tt>setTranslator</tt> method provided by <tt>TranslatorSink</tt>. The <tt>FooBarOperationStateItem</tt> is a slice type that is derived from <tt>TranslatorOperationStateItem</tt>.</p>

<p>There are other methods that can be overloaded by subclasses of <tt>TranslatorOperations</tt> if desired, but in most cases they will not be needed. They are as follows:</p>

<ul>
        <li>Destructor - Overload this if there is data that needs to be cleaned up by your operation</li>
        <li><tt>setState()</tt> - Overload this if there is specific state that needs to be replicated by your operation. It is recommended that at the end of your overload, you call the base <tt>TranslatorOperation::setState()</tt> method.</li>
        <li><tt>removeState()</tt> - Overload this if there is specific state that needs to be removed from replication listeners. It is recommended that at the end of your overload, you call the base <tt>TranslatorOperation::removeState()</tt> method.</li>
        <li><tt>activate()</tt> - Overload this if there are additional items that need to be added to the object adapter. It is recommended that at the end of your overload, you return the result of the base <tt>TranslatorOperation::activate()</tt> operation.</li>
</ul>


<h1><a name="MediaOperationsDesign-Translator"></a>Translator</h1>

<p><tt>Translator</tt> is a class upon which more specific translators can be created. All that needs to be done is to create a constructor and to overload the <tt>translate</tt> method. We will be creating our <tt>FooBarTranslator</tt> based on this base class.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: java; gutter: false">UlawAlawTranslator(const TranslatorSourcePtr source,
        const FormatPtr&amp; inputFormat,
        const FormatPtr&amp; outputFormat,
        const Logger&amp; logger)
    : Translator(source, inputFormat, outputFormat, logger)
{
}

unsigned char foo2bar(unsigned char foo)
{
    //Code to translate a sample of foo to a sample of bar
}

unsigned char bar2foo(unsigned char bar)
{
    //Code to translate a sample of bar to a sample of foo
}

FramePtr translate(const FramePtr inFrame)
{
    if (inFrame-&gt;mediaFormat-&gt;name != mInputFormat-&gt;name)
    {
        mLogger(Error) &lt;&lt; "Cannot translate frame because the format is not what we expect.";
        throw UnsupportedMediaFormatException();
    }

    ByteSeqPayloadPtr inPayload = ByteSeqPayloadPtr::dynamicCast(inFrame-&gt;payload);
    if (!inPayload)
    {
        mLogger(Error) &lt;&lt; "The incoming frame's payload is not a byte sequence.";
        throw UnsupportedMediaFormatException();
    }

    Ice::ByteSeq outPayload;
    if (inFrame-&gt;mediaFormat-&gt;name == FooName)
    {   
        std::transform(inPayload-&gt;payload.begin(), inPayload-&gt;payload.end(), std::back_inserter(outPayload), boost::bind(&amp;FooBarTranslator::foo2bar, this, _1));

    }
    else //The input payload is of type "Bar"
    {
        std::transform(inPayload-&gt;payload.begin(), inPayload-&gt;payload.end(), std::back_inserter(outPayload), boost::bind(&amp;FooBarTranslator::bar2foo, this, _1));
    }

    return new Frame(mOutputFormat, new ByteSeqPayload(outPayload));
}</pre>
</div></div>

<p>The <tt>translate</tt> operation is the interesting part here. It starts by ensuring that the format being translated is the expected format and that the payload of the input frame is a sequence of bytes. From there, the translation is done using the <tt>std::transform</tt> STL method. The base <tt>Translator</tt> class takes care of making sure the output frame from the <tt>translate</tt> method ends up where it is supposed to go.</p>

<h1><a name="MediaOperationsDesign-Oddsandends."></a>Odds and ends.</h1>

<p>There are two final items before we're done. First, the MediaOperationsCore needs to create the <tt>FooBarOperationFactory</tt> on startup. In the <tt>MediaOperationsCore.cpp</tt> file, we modify the <tt>createOperationsFactories</tt> method by adding the following line:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: java; gutter: false">createAndRegisterFactory(new FooBarFactory(getServiceAdapter(), lg, boost::static_pointer_cast&lt;MediaOperationReplicationContext&gt;(getReplicationContext())));</pre>
</div></div>

<p>Simple. The other order of business to take care of is to make sure the state replication listener is taken care of. First, the <tt>Visitor</tt> class within <tt>stateSet</tt> needs to have the following method added:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: java; gutter: false">void visitFooBarOperationStateItem(const FooBarOperationStateItemPtr&amp; fooBar)
{
    FooBarFactoryPtr factory = FooBarFactoryPtr::dynamicCast(mAdapter-&gt;find(fooBar-&gt;factoryId));

    createTranslationMediaOperation(factory, fooBar);
}</pre>
</div></div>

<p>Then, the <tt>Visitor</tt> class within <tt>stateRemovedForItems</tt> needs to have the following method added:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: java; gutter: false">void visitFooBarOperationStateItem(const FooBarOperationStateItemPtr&amp; fooBar)
{
    mAdapter-&gt;remove(mAdapter-&gt;getCommunicator()-&gt;stringToIdentity(fooBar-&gt;operationId));
}</pre>
</div></div>

<p>And now your translator is complete!</p>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;" class="grey">
                        <a href="https://wiki.asterisk.org/wiki/users/removespacenotification.action?spaceKey=TOP">Stop watching space</a>
            <span style="padding: 0px 5px;">|</span>
                <a href="https://wiki.asterisk.org/wiki/users/editmyemailsettings.action">Change email notification preferences</a>
</div>
        <a href="https://wiki.asterisk.org/wiki/display/TOP/Media+Operations+Design">View Online</a>
        |
        <a href="https://wiki.asterisk.org/wiki/pages/diffpagesbyversion.action?pageId=19006824&revisedVersion=5&originalVersion=4">View Changes</a>
                |
        <a href="https://wiki.asterisk.org/wiki/display/TOP/Media+Operations+Design?showComments=true&amp;showCommentArea=true#addcomment">Add Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>