/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: sdprendr.cpp,v 1.4.10.1 2004/07/19 21:04:17 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "sdpfctry.ver"

#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxrendr.h"
#include "hxplugn.h"
#include "hxcore.h"
#include "hxwin.h"
#include "hxgroup.h"
#include "hxupgrd.h"
#include "hxver.h"
#include "hxstring.h"
#include "hxstrutl.h"
#include "chxpckts.h"
#include "hxurl.h"
#include "hxslist.h"
#include "chxpckts.h"
#include "uri_schemes.h"
#include "sdprendr.h"

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static const char HX_THIS_FILE[] = __FILE__;
#endif

/****************************************************************************
 * 
 *  Function:
 * 
 *	HXCreateInstance()
 * 
 *  Purpose:
 * 
 *	Function implemented by all plugin DLL's to create an instance of 
 *	any of the objects supported by the DLL. This method is similar to 
 *	Window's CoCreateInstance() in its purpose, except that it only 
 *	creates objects from this plugin DLL.
 *
 *	NOTE: Aggregation is never used. Therefore and outer unknown is
 *	not passed to this function, and you do not need to code for this
 *	situation.
 * 
 */

/****************************************************************************
 * 
 *  Method:
 * 
 *	HXCreateInstance()
 * 
 *  Purpose:
 * 
 *	Function implemented by all plugin DLL's to create an instance of 
 *	any of the objects supported by the DLL. This method is similar to 
 *	Window's CoCreateInstance() in its purpose, except that it only 
 *	creates objects from this plugin DLL.
 *
 *	NOTE: Aggregation is never used. Therefore and outer unknown is
 *	not passed to this function, and you do not need to code for this
 *	situation.
 * 
 */
HX_RESULT STDAPICALLTYPE 
CSDPRenderer::HXCreateInstance(IUnknown** ppIUnknown)
{
    *ppIUnknown = (IUnknown*)(IHXPlugin*)new CSDPRenderer;
    if (*ppIUnknown)
    {
	(*ppIUnknown)->AddRef();
	return HXR_OK;
    }
    return HXR_OUTOFMEMORY;
}

const char* const CSDPRenderer::zm_pName	     = "SDP";
const char* const CSDPRenderer::zm_pDescription    = "RealNetworks SDP Renderer Plugin";
const char* const CSDPRenderer::zm_pCopyright      = HXVER_COPYRIGHT;
const char* const CSDPRenderer::zm_pMoreInfoURL    = HXVER_MOREINFO;
const char* const CSDPRenderer::zm_pStreamMimeTypes[] = {"application/sdp", NULL};

/************************************************************************
 *  Method:
 *    IHXPlugin::InitPlugin
 *  Purpose:
 *    Initializes the plugin for use. This interface must always be
 *    called before any other method is called. This is primarily needed 
 *    so that the plugin can have access to the context for creation of
 *    IHXBuffers and IMalloc.
 */
STDMETHODIMP CSDPRenderer::InitPlugin(IUnknown* /*IN*/ pContext)
{    
    m_pContext = pContext;
    m_pContext->AddRef();

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXPlugin::GetPluginInfo
 *  Purpose:
 *    Returns the basic information about this plugin. Including:
 *
 *    bLoadMultiple	whether or not this plugin DLL can be loaded
 *			multiple times. All File Formats must set
 *			this value to TRUE.
 *    pDescription	which is used in about UIs (can be NULL)
 *    pCopyright	which is used in about UIs (can be NULL)
 *    pMoreInfoURL	which is used in about UIs (can be NULL)
 */
STDMETHODIMP CSDPRenderer::GetPluginInfo
(
    REF(BOOL)        /*OUT*/ bLoadMultiple,
    REF(const char*) /*OUT*/ pDescription,
    REF(const char*) /*OUT*/ pCopyright,
    REF(const char*) /*OUT*/ pMoreInfoURL,
    REF(ULONG32)     /*OUT*/ ulVersionNumber
)
{
    bLoadMultiple = TRUE;   // Must be true for file formats.

    pDescription    = zm_pDescription;
    pCopyright	    = zm_pCopyright;
    pMoreInfoURL    = zm_pMoreInfoURL;
    ulVersionNumber = TARVER_ULONG32_VERSION;

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXPlugin::GetRendererInfo
 *  Purpose:
 *    If this object is a file format object this method returns
 *    information vital to the instantiation of file format plugins.
 *    If this object is not a file format object, it should return
 *    HXR_UNEXPECTED.
 */
STDMETHODIMP CSDPRenderer::GetRendererInfo
(
    REF(const char**) /*OUT*/ pStreamMimeTypes,
    REF(UINT32)      /*OUT*/ unInitialGranularity
)
{
    pStreamMimeTypes = (const char**) zm_pStreamMimeTypes;
    unInitialGranularity = 0;

    return HXR_OK;
}

CSDPRenderer::CSDPRenderer()
	: m_lRefCount(0)
	, m_pContext(NULL)
	, m_pStream(NULL)
	, m_pPlayer(NULL)
	, m_uGroupIndex(0)
{
}

CSDPRenderer::~CSDPRenderer()
{
    Cleanup();

    HX_RELEASE(m_pContext);
}


// *** IUnknown methods ***

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::QueryInterface
//  Purpose:
//	Implement this to export the interfaces supported by your 
//	object.
//
STDMETHODIMP CSDPRenderer::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXPlugin))
    {
	AddRef();
	*ppvObj = (IHXPlugin*)this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXRenderer))
    {
	AddRef();
	*ppvObj = (IHXRenderer*)this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::AddRef
//  Purpose:
//	Everyone usually implements this the same... feel free to use
//	this implementation.
//
STDMETHODIMP_(ULONG32) CSDPRenderer::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::Release
//  Purpose:
//	Everyone usually implements this the same... feel free to use
//	this implementation.
//
STDMETHODIMP_(ULONG32) CSDPRenderer::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

// *** IHXRenderer methods ***

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::StartStream
//  Purpose:
//	Called by client engine to inform the renderer of the stream it
//	will be rendering. The stream interface can provide access to
//	its source or player. This method also provides access to the 
//	primary client controller interface.
//
STDMETHODIMP CSDPRenderer::StartStream
(
    IHXStream*	    pStream,
    IHXPlayer*	    pPlayer
)
{
    HX_RESULT			rc = HXR_OK;

    m_pStream  = pStream;
    m_pStream->AddRef();

    m_pPlayer  = pPlayer;
    m_pPlayer->AddRef();

    return rc;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::EndStream
//  Purpose:
//	Called by client engine to inform the renderer that the stream
//	is was rendering is closed.
//
STDMETHODIMP CSDPRenderer::EndStream()
{
    Cleanup();
    
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::OnHeader
//  Purpose:
//	Called by client engine when a header for this renderer is 
//	available. The header will arrive before any packets.
//
STDMETHODIMP CSDPRenderer::OnHeader(IHXValues* pHeader)
{    
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::OnPacket
//  Purpose:
//	Called by client engine when a packet for this renderer is 
//	due.
//
STDMETHODIMP CSDPRenderer::OnPacket(IHXPacket* pPacket, 
				    LONG32 lTimeOffset)
{
    HX_RESULT		rc = HXR_OK;

    HX_ASSERT(lTimeOffset <= 0);

    IHXBuffer* pBuffer = pPacket->GetBuffer();
    if(pBuffer)
    {
	rc = ProcessSDP(pBuffer);
    }
    HX_RELEASE(pBuffer);

    return rc;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::OnTimeSync
//  Purpose:
//	Called by client engine to inform the renderer of the current
//	time relative to the streams synchronized time-line. The 
//	renderer should use this time value to update its display or
//	render it's stream data accordingly.
//
STDMETHODIMP CSDPRenderer::OnTimeSync(ULONG32 ulTime)
{
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::OnPreSeek
//  Purpose:
//	Called by client engine to inform the renderer that a seek is
//	about to occur. The render is informed the last time for the 
//	stream's time line before the seek, as well as the first new
//	time for the stream's time line after the seek will be completed.
//
STDMETHODIMP CSDPRenderer::OnPreSeek(ULONG32 ulOldTime, ULONG32 ulNewTime)
{
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::OnPostSeek
//  Purpose:
//	Called by client engine to inform the renderer that a seek has
//	just occured. The render is informed the last time for the 
//	stream's time line before the seek, as well as the first new
//	time for the stream's time line after the seek.
//
STDMETHODIMP CSDPRenderer::OnPostSeek(ULONG32 ulOldTime, ULONG32 ulNewTime)
{
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::OnPause
//  Purpose:
//	Called by client engine to inform the renderer that a pause has
//	just occured. The render is informed the last time for the 
//	stream's time line before the pause.
//
STDMETHODIMP CSDPRenderer::OnPause(ULONG32 ulTime)
{
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::OnBegin
//  Purpose:
//	Called by client engine to inform the renderer that a begin or
//	resume has just occured. The render is informed the first time 
//	for the stream's time line after the resume.
//
STDMETHODIMP CSDPRenderer::OnBegin(ULONG32 ulTime)
{
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::OnBuffering
//  Purpose:
//	Called by client engine to inform the renderer that buffering
//	of data is occuring. The render is informed of the reason for
//	the buffering (start-up of stream, seek has occured, network
//	congestion, etc.), as well as percentage complete of the 
//	buffering process.
//
STDMETHODIMP CSDPRenderer::OnBuffering(ULONG32 ulFlags, UINT16 unPercentComplete)
{
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXRenderer::GetDisplayType
//  Purpose:
//	Called by client engine to ask the renderer for it's preferred
//	display type. When layout information is not present, the 
//	renderer will be asked for it's prefered display type. Depending
//	on the display type a buffer of additional information may be 
//	needed. This buffer could contain information about preferred
//	window size.
//
STDMETHODIMP CSDPRenderer::GetDisplayType
(
    REF(HX_DISPLAY_TYPE)   ulFlags,
    REF(IHXBuffer*)	    pBuffer
)
{
    ulFlags = HX_DISPLAY_NONE;

    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    IHXRenderer::OnEndofPackets
 *	Purpose:
 *	    Called by client engine to inform the renderer that all the
 *	    packets have been delivered. However, if the user seeks before
 *	    EndStream() is called, renderer may start getting packets again
 *	    and the client engine will eventually call this function again.
 */
STDMETHODIMP CSDPRenderer::OnEndofPackets(void)
{
    return HXR_OK;
}

HX_RESULT
CSDPRenderer::ProcessSDP(IHXBuffer* pSDPBuffer)
{
    HX_RESULT           rc = HXR_OK;
    UINT32              ulSDPBufferSize = 0;
    char*               pszSDPBuffer = NULL;
    CHXString           escapedSDP;
    CHXString           url;
    CHXHeader*          pTrackProps = NULL;
    CHXBuffer*          pBuffer = NULL;
    IHXGroupManager*    pGroupManager = NULL;
    IHXGroup*           pGroup = NULL;

    if (!pSDPBuffer)
    {
        rc = HXR_FAILED;
        goto cleanup;
    }

    ulSDPBufferSize = pSDPBuffer->GetSize() + 1;

    pszSDPBuffer = new char[ulSDPBufferSize];
    if (NULL == pszSDPBuffer)
    {
        rc = HXR_OUTOFMEMORY;
        goto cleanup;
    }

    memset(pszSDPBuffer, 0, ulSDPBufferSize);
    strncpy(pszSDPBuffer, (const char*)pSDPBuffer->GetBuffer(), pSDPBuffer->GetSize());
    
    CHXURL::encodeURL(pszSDPBuffer, escapedSDP);

    url = HELIX_SDP_SCHEME;
    url += ":";
    url += escapedSDP;

    if (HXR_OK != m_pContext->QueryInterface(IID_IHXGroupManager, (void**)&pGroupManager))
    {
        rc = HXR_FAILED;
        goto cleanup;
    }

    pGroupManager->GetCurrentGroup(m_uGroupIndex);
    
    if (HXR_OK != pGroupManager->GetGroup(m_uGroupIndex, pGroup))
    {
        HX_ASSERT(FALSE);
        rc = HXR_FAILED;
        goto cleanup;
    }

    pTrackProps = new CHXHeader;
    pTrackProps->AddRef();

    pBuffer = new CHXBuffer();
    pBuffer->AddRef();

    pBuffer->Set((UCHAR*)(const char*)url, url.GetLength() + 1);
    pTrackProps->SetPropertyCString("src", pBuffer);

    pGroup->AddTrack(pTrackProps);

cleanup:

    HX_VECTOR_DELETE(pszSDPBuffer);

    HX_RELEASE(pGroup);
    HX_RELEASE(pGroupManager);
    HX_RELEASE(pBuffer);
    HX_RELEASE(pTrackProps);

    return rc;
}

void
CSDPRenderer::Cleanup(void)
{
    HX_RELEASE(m_pStream);
    HX_RELEASE(m_pPlayer);
    
    return;
}    
