📄 fbbufctl.cpp
字号:
/* ***** BEGIN LICENSE BLOCK *****
* Version: RCSL 1.0/RPSL 1.0
*
* Portions Copyright (c) 1995-2003 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
* Version 1.0 (the "RPSL") available at
* http://www.helixcommunity.org/content/rpsl unless you have licensed
* the file under the RealNetworks Community Source License Version 1.0
* (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.
*
* 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 "hxtypes.h"
#include "hxcom.h"
#include "fbbufctl.h"
#include "hlxclib/math.h"
#include "hxprefutil.h"
//#define HELIX_FEATURE_DBG_LOG
//#define BUFFER_CONTROL_TESTING
#include "hxtick.h"
#include "errdbg.h"
#include "hxstring.h"
#include "hxurl.h"
#include "ihxpckts.h" // IHXValues
#include "hxfiles.h" // IHXRequest
HXFeedbackControl::HXFeedbackControl() :
m_delt(0),
m_a1(0),
m_a2(0),
m_b1(0),
m_b2(0),
m_ulSetPoint(0),
m_ulMin(0),
m_ulMax(0xffffffff),
m_e1(0),
m_e2(0),
m_c1(0),
m_c2(0)
{}
void HXFeedbackControl::Init(double c, double wn, double Kv, double delt)
{
// calculate coefficients
double d = exp(-wn * c);
double wd = wn * sqrt(1.0 - c *c);
double pReal = d * cos(wd);
double pImag = d * sin(wd);
m_b1 = -2.0 * pReal;
m_b2 = pReal * pReal + pImag * pImag;
m_a1 = 2.0 + m_b1 - (1.0 + m_b1 + m_b2)/(Kv * delt);
m_a2 = 1.0 + m_b1 + m_b2 - m_a1;
m_delt = delt;
}
void HXFeedbackControl::Reset(UINT32 ulSetPoint,
INT32 e1, INT32 e2,
UINT32 c1, UINT32 c2)
{
m_ulSetPoint = ulSetPoint;
m_e1 = e1;
m_e2 = e2;
m_c1 = c1;
m_c2 = c2;
}
void HXFeedbackControl::SetLimits(UINT32 ulMin, UINT32 ulMax)
{
m_ulMin = ulMin;
m_ulMax = ulMax;
}
UINT32 HXFeedbackControl::Control(UINT32 ulCurrentBits)
{
INT32 error = (INT32)m_ulSetPoint - (INT32)ulCurrentBits;
UINT32 ulControlBw = 0;
double u1 = (m_a1 * (double)error +
(m_a2 - m_a1) * (double)m_e1 -
m_a2 * (double)m_e2) / m_delt;
double u2 = -((m_b1 - m_a1) * (double)m_c1 +
(m_b2 - m_a2) * (double)m_c2);
ulControlBw = RoundAndClip(u1 + u2);
Enqueue(error, ulControlBw);
return ulControlBw;
}
UINT32 HXFeedbackControl::RoundAndClip(double value) const
{
UINT32 ulRet = m_ulMin;
if (value > m_ulMax)
{
// Saturate to max
ulRet = m_ulMax;
}
else if (value > m_ulMin)
{
// Value within range.
// Round to nearest integer
ulRet = (UINT32)(value + 0.5);
}
return ulRet;
}
void HXFeedbackControl::Enqueue(INT32 error, UINT32 ulBandwidth)
{
m_e2 = m_e1;
m_e1 = error;
m_c2 = m_c1;
m_c1 = ulBandwidth;
}
HXFeedbackBufferControl::HXFeedbackBufferControl() :
m_lRefCount(0),
m_pScheduler(NULL),
m_callbackHandle(0),
m_pSource(NULL),
m_pBufferStats(NULL),
m_pThin(NULL),
m_pPrefs(NULL),
m_pStatus(NULL),
m_pErrMsg(NULL),
m_ulClipBw(0),
m_nControlStream(0),
m_ulControlBw(0),
m_bControlTotal(FALSE),
m_bPaused(TRUE),
m_state(csNotInitialized)
{
m_control.Init(0.7, 1.25664, 0.2, 5.0);
}
HXFeedbackBufferControl::~HXFeedbackBufferControl()
{
Close();
}
/*
* IUnknown methods
*/
STDMETHODIMP HXFeedbackBufferControl::QueryInterface(THIS_
REFIID riid,
void** ppvObj)
{
QInterfaceList qiList[] =
{
{ GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXBufferControl*)this },
{ GET_IIDHANDLE(IID_IHXBufferControl), (IHXBufferControl*)this },
{ GET_IIDHANDLE(IID_IHXCallback), (IHXCallback*)this },
};
return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}
STDMETHODIMP_(ULONG32) HXFeedbackBufferControl::AddRef(THIS)
{
return InterlockedIncrement(&m_lRefCount);
}
STDMETHODIMP_(ULONG32) HXFeedbackBufferControl::Release(THIS)
{
if (InterlockedDecrement(&m_lRefCount) > 0)
{
return m_lRefCount;
}
delete this;
return 0;
}
/*
* IHXBufferControl method
*/
/************************************************************************
* Method:
* IHXBufferControl::Init
* Purpose:
* Initialize the buffer control object with a context
* so it can find the interfaces it needs to do buffer
* control
*/
STDMETHODIMP HXFeedbackBufferControl::Init(THIS_ IUnknown* pContext)
{
HX_RESULT res = HXR_FAILED;
Close();
if (pContext)
{
pContext->QueryInterface(IID_IHXErrorMessages, (void**)&m_pErrMsg);
if ((HXR_OK == pContext->QueryInterface(IID_IHXScheduler,
(void**)&m_pScheduler)) &&
(HXR_OK == pContext->QueryInterface(IID_IHXStreamSource,
(void**)&m_pSource)) &&
(m_pSource) &&
(HXR_OK == m_pSource->QueryInterface(IID_IHXThinnableSource,
(void**)&m_pThin)) &&
(HXR_OK == pContext->QueryInterface(IID_IHXSourceBufferingStats2,
(void**)&m_pBufferStats)) &&
(HXR_OK == pContext->QueryInterface(IID_IHXPendingStatus,
(void**)&m_pStatus)) &&
(HXR_OK == pContext->QueryInterface(IID_IHXPreferences,
(void**)&m_pPrefs)) &&
(HXR_OK == ReadPrefSettings()))
{
#ifdef BUFFER_CONTROL_TESTING
res = InitTesting(pContext);
#endif
res = HXR_OK;
}
else
{
ChangeState(csError);
}
}
return res;
}
/************************************************************************
* Method:
* IHXBufferControl::OnBuffering
* Purpose:
* Called while buffering
*/
STDMETHODIMP HXFeedbackBufferControl::OnBuffering(UINT32 ulRemainingInMs,
UINT32 ulRemainingInBytes)
{
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL,(s, "FBBC::OnBuffering(%d, %d)",
ulRemainingInMs, ulRemainingInBytes));
if (Initialized() &&
(csBuffering != m_state))
{
ChangeState(csBuffering);
SetBandwidth(m_ulClipBw);
}
return HXR_OK;
}
/************************************************************************
* Method:
* IHXBufferControl::OnBufferingDone
* Purpose:
* Called when buffering is done
*/
STDMETHODIMP HXFeedbackBufferControl::OnBufferingDone(THIS)
{
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL,(s, "FBBC::OnBufferingDone"));
if (Initialized())
{
HX_ASSERT(csBuffering == m_state);
UINT32 ulTotalBits = 0;
UINT32 ulControlBits = 0;
GetControlData(ulTotalBits, ulControlBits);
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL,
(s, "FBBC::OnBufferingDone() %d %d",
ulTotalBits,
ulControlBits));
UINT32 ulSetPoint = ulControlBits;
if (ulSetPoint < m_control.SetPoint())
{
// If the new setpoint is less than a
// previous value, ignore it and use
// the old setpoint.
ulSetPoint = m_control.SetPoint();
}
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL,
(s, "FBBC::OBD reset control %lu",
ulSetPoint));
m_control.Reset(ulSetPoint,
0, 0, // Error state
m_ulControlBw, m_ulControlBw); // control state
if (m_bPaused)
{
ChangeState(csPaused);
}
else
{
ChangeState(csPlaying);
}
}
return HXR_OK;
}
/************************************************************************
* Method:
* IHXBufferControl::OnResume
* Purpose:
* Called when playback is resumed
*/
STDMETHODIMP HXFeedbackBufferControl::OnResume()
{
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL,(s, "FBBC::OnResume"));
m_bPaused = FALSE;
if (csError != m_state)
{
if (HXR_OK == GetBwInfo(m_ulClipBw, m_nControlStream, m_ulControlBw))
{
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL,
(s, "FBBC::OnResume : clipBw %lu ctlStr %u ctlBw %lu",
m_ulClipBw,
m_nControlStream,
m_ulControlBw));
SetTransportByteLimits(m_ulClipBw);
if (m_pSource && !m_pSource->IsLive() &&
m_ulClipBw && m_ulControlBw)
{
/* Only activate the algorithm for on-demand clips that
* have bitrate information
*/
if (csNotInitialized == m_state)
{
StartCallback();
}
ChangeState(csPlaying);
}
SetBandwidth(m_ulClipBw);
}
}
return HXR_OK;
}
/************************************************************************
* Method:
* IHXBufferControl::OnPause
* Purpose:
* Called when playback is paused
*/
STDMETHODIMP HXFeedbackBufferControl::OnPause()
{
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL, (s, "FBBC::OnPause"));
m_bPaused = TRUE;
if (Initialized())
{
ChangeState(csPaused);
}
return HXR_OK;
}
/************************************************************************
* Method:
* IHXBufferControl::OnSeek
* Purpose:
* Called when a seek occurs
*/
STDMETHODIMP HXFeedbackBufferControl::OnSeek(THIS)
{
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL, (s, "FBBC::OnSeek"));
if (Initialized())
{
ChangeState(csSeeking);
}
return HXR_OK;
}
/************************************************************************
* Method:
* IHXBufferControl::OnClipEnd
* Purpose:
* Called when we get the last packet in the clip
*/
STDMETHODIMP HXFeedbackBufferControl::OnClipEnd(THIS)
{
DEBUG_OUT(m_pErrMsg, DOL_BUFFER_CONTROL, (s, "FBBC::OnClipEnd"));
if (Initialized())
{
ChangeState(csClipEnd);
}
return HXR_OK;
}
/************************************************************************
* Method:
* IHXBufferControl::Close()
* Purpose:
* Called when the owner of this object wishes to shutdown
* and destroy this object. This call causes the buffer control
* object to release all it's interfaces references.
*/
STDMETHODIMP HXFeedbackBufferControl::Close(THIS)
{
HX_RELEASE(m_pErrMsg);
StopCallback();
HX_RELEASE(m_pScheduler);
HX_RELEASE(m_pBufferStats);
HX_RELEASE(m_pSource);
HX_RELEASE(m_pThin);
HX_RELEASE(m_pPrefs);
HX_RELEASE(m_pStatus);
ChangeState(csNotInitialized);
return HXR_OK;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -