hxntsrc.cpp
上传用户:dangjiwu
上传日期:2013-07-19
资源大小:42019k
文件大小:180k
- // set the client ID
- theErr = m_pProto->set_client_id( mClientID );
- if (HXR_OK != theErr)
- {
- goto cleanup;
- }
- // If we reach to an unknown mode during transport switching, then
- // we are all out of possibilities, and we can consider the server
- // connection timed out...
- if (m_PreferredTransport == UnknownMode)
- {
- theErr = HXR_SERVER_TIMEOUT;
- goto cleanup;
- }
- else
- {
- set_transport(m_PreferredTransport);
- }
- // If we were asked to use a specific UDP Port. Then
- // tell the protocol object about the request...
- if (m_bUseUDPPort)
- {
- m_pProto->set_UDP_port();
- }
- if (m_bUseProxy)
- {
- theErr = m_pProto->set_proxy(m_pProxy,m_uProxyPort);
- if (HXR_OK != theErr)
- {
- goto cleanup;
- }
- }
- if (HTTPCloakMode == m_CurrentTransport &&
- PTS_READY != m_prefTransportState)
- {
- CreateCloakedPortList();
- m_pProto->SetCloakPortAttempted(m_pCloakPortList, m_nNumberOfCloakPorts);
- }
- #if defined(HELIX_FEATURE_PREFETCH)
- if (m_bPrefetch)
- {
- m_pProto->EnterPrefetch();
- }
- #endif /* HELIX_FEATURE_PREFETCH */
- m_pProto->set_server_timeout(m_ulServerTimeOut);
- m_pProto->set_perfect_play(m_bPerfectPlay);
- cleanup:
- return theErr;
- }
- void
- HXNetSource::set_transport(TransportMode mode)
- {
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)set_transport %lu", this, mode));
- m_CurrentTransport = mode;
- // Tell the protocol object, (in case it doesn't remember <g>).
- if(m_pProto)
- m_pProto->set_transport(m_CurrentTransport, m_ulTransportPrefMask);
- }
- // sets up HXSource to use a RealMedia splitter
- HX_RESULT
- HXNetSource::set_proxy(const char* proxy, UINT16 port)
- {
- HX_TRACE("HXNetSource::set_proxy");
- HX_RESULT theErr = HXR_OK;
- IHXProxyManager* pProxyManager = NULL;
- if(proxy == 0 || *proxy == 0)
- {
- theErr = HXR_OK;
- goto cleanup;
- }
- if(m_pProxy)
- {
- delete [] m_pProxy;
- m_pProxy = NULL;
- }
- m_pProxy = new char[::strlen(proxy) + 1];
- if(m_pProxy == NULL)
- {
- theErr = HXR_OUTOFMEMORY;
- goto cleanup;
- }
- ::strcpy(m_pProxy, proxy); /* Flawfinder: ignore */
- m_uProxyPort = port;
- m_bUseProxy = TRUE;
- if (m_pPlayer &&
- HXR_OK == m_pPlayer->QueryInterface(IID_IHXProxyManager, (void**)&pProxyManager) &&
- pProxyManager)
- {
- m_bUseProxy = !(pProxyManager->IsExemptionHost(m_pHost));
- }
- HX_RELEASE(pProxyManager);
- cleanup:
- return(theErr);
- }
- HX_RESULT
- HXNetSource::cleanup_proxy(void)
- {
- HX_TRACE("HXNetSource::cleanup_proxy");
- HX_RESULT theErr = HXR_OK;
- m_uProxyPort = 0;
- HX_VECTOR_DELETE(m_pProxy);
- m_bUseProxy = FALSE;
- return(theErr);
- }
- HX_RESULT
- HXNetSource::ReadPreferences()
- {
- HX_TRACE("HXNetSource::ReadPreferences");
- HX_RESULT theErr = HXR_OK;
- UINT32 un16Temp = 0;
- TransportMode preferredTransMode = UnknownMode;
- TransportMode desiredTransMode = UnknownMode;
- IHXBuffer* pValue = NULL;
- theErr = HXSource::ReadPreferences();
- if (theErr)
- {
- return theErr;
- }
- // Read Client ID
- IHXBuffer* pBuffer = NULL;
- IHXBuffer* pProxyHost = NULL;
- IHXBuffer* pProxyPort = NULL;
- /* We only want to read PerfectPlay preference once.
- * This is because we may disable PerfectPlay if the server
- * does not allow it OR if the content is not "PerfectPlay"able.
- * In each of these cases, we call brute_force_reconnect()
- * and we do not want to over-write m_bPerfectPlay
- */
- if (!m_bPerfectPlayPreferenceRead)
- {
- m_bPerfectPlayPreferenceRead = TRUE;
- ReadPrefBOOL(m_pPreferences, "PerfectPlay", m_bPerfectPlay);
- }
- const char* pcszClientID = NULL;
- IHXRegistry* pRegistry = NULL;
- if (m_pEngine->QueryInterface(IID_IHXRegistry, (void**)&pRegistry) == HXR_OK)
- {
- // Form clientID registry string
- CHXString strTemp;
- strTemp = HXREGISTRY_PREFPROPNAME;
- strTemp += '.';
- strTemp += CLIENT_ID_REGNAME;
- if (pRegistry->GetStrByName(strTemp, pBuffer) == HXR_OK)
- {
- pcszClientID = (const char*)pBuffer->GetBuffer();
- }
- pRegistry->Release();
- }
- else
- {
- // Encode the client ID with the pieces of interest.
- HXVERSIONINFO verInfo;
- HXGetWinVer(&verInfo);
- pcszClientID = HXGetVerEncodedName(&verInfo,
- PRODUCT_ID,
- TARVER_STRING_VERSION,
- LANGUAGE_CODE,
- "RN01");
- }
- if (pcszClientID)
- {
- UINT32 ulSizeof_mClientID = sizeof(mClientID);
- UINT32 ulSafeStrlenClientId = HX_SAFESIZE_T(strlen(pcszClientID));
- HX_ASSERT(ulSafeStrlenClientId < ulSizeof_mClientID);
- if (ulSafeStrlenClientId >= ulSizeof_mClientID)
- {
- // /Limiting too-large string fixes PR 86928 (but whatever
- // writes to props|prefs needs to be sure not to overflow
- // the max size):
- ulSafeStrlenClientId = ulSizeof_mClientID - 1;
- }
-
- memcpy(mClientID, pcszClientID, ulSafeStrlenClientId); /* Flawfinder: ignore */
- mClientID[ulSafeStrlenClientId] = ' ';
- }
- HX_RELEASE(pBuffer);
- ReadPrefINT32(m_pPreferences, "ServerTimeOut", m_ulServerTimeOut);
- if (m_ulServerTimeOut < MINIMUM_TIMEOUT)
- {
- m_ulServerTimeOut = MINIMUM_TIMEOUT;
- }
- ReadPrefINT32(m_pPreferences, "ConnectionTimeOut", m_ulConnectionTimeout);
- if (m_ulConnectionTimeout < MINIMUM_TIMEOUT)
- {
- m_ulConnectionTimeout = MINIMUM_TIMEOUT;
- }
- ReadPrefINT32(m_pPreferences, "MulticastTimeout", m_ulMulticastTimeout);
- ReadPrefINT32(m_pPreferences, "UDPTimeout", m_ulUDPTimeout);
- ReadPrefINT32(m_pPreferences, "TCPTimeout", m_ulTCPTimeout);
-
- ReadPrefBOOL(m_pPreferences, "SendStatistics", m_bSendStatistics);
- /////////////////////////////////////////////////////////////
- //
- // Handle Specific UDP Port Preferences here....
- //
- ReadPrefBOOL(m_pPreferences, "UseUDPPort", m_bUseUDPPort);
- #if defined(HELIX_FEATURE_SMARTERNETWORK)
- if (!m_pPreferredTransport)
- {
- m_pPreferredTransportManager->GetTransportPreference(m_bRTSPProtocol?PTP_RTSP:PTP_PNM, m_ulTransportPrefMask);
- HX_ASSERT(m_ulTransportPrefMask);
- m_pPreferredTransportManager->GetPrefTransport(m_pHost,
- m_bRTSPProtocol?PTP_RTSP:PTP_PNM,
- m_pPreferredTransport);
- HX_ASSERT(m_pPreferredTransport);
- m_prefTransportState = m_pPreferredTransport->GetState();
- if (m_prefTransportState == PTS_READY ||
- m_prefTransportState == PTS_CREATE)
- {
- m_pPreferredTransport->GetTransport(m_PreferredTransport,
- m_uCurrCloakedPort);
- #if !defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
- if (MulticastMode == m_PreferredTransport)
- {
- m_PreferredTransport = UDPMode;
- }
- #endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */
- }
- else if (m_prefTransportState == PTS_PENDING)
- {
- m_state = NETSRC_TRANSPORTPENDING;
- m_pPreferredTransport->AddTransportSink(this);
- theErr = HXR_WOULD_BLOCK;
- }
- }
- #else
- if (UnknownMode == m_PreferredTransport)
- {
- m_PreferredTransport = UDPMode;
- }
- #endif /* HELIX_FEATURE_SMARTERNETWORK */
- // if the "Use Proxy" is explicitly set, we will use proxy settings accordingly
- // regardless what server type(internal vs external) it is.
- //if (m_pPreferredTransport->GetClass() == PTC_EXTERNAL)
- {
- // handle proxies
- if (HTTPCloakMode == m_PreferredTransport)
- {
- un16Temp = 0;
- #if defined(HELIX_FEATURE_PAC)
- if (HXR_OK != ReadPrefINT32(m_pPreferences, "HTTPProxyAutoConfig", un16Temp))
- {
- // previously released Enterprise player may use "ProxyAutoConfig" for
- // HTTP proxy auto config
- ReadPrefINT32(m_pPreferences, "ProxyAutoConfig", un16Temp);
- }
- // HTTP Proxy Auto Config
- if (un16Temp)
- {
- if (!m_pPAC)
- {
- m_pEngine->QueryInterface(IID_IHXProxyAutoConfig, (void**)&m_pPAC);
- }
- if (m_pPAC &&
- (!m_pPACInfoList || 0 == m_pPACInfoList->GetCount()))
- {
- theErr = m_pPAC->GetHTTPProxyInfo((IHXProxyAutoConfigCallback*)this,
- m_pszURL,
- m_pHost);
- }
- // attempt the next proxy info from m_pPACInfoList
- else if (m_pPACInfoList && m_PACInfoPosition)
- {
- PACInfo* pPACInfo = (PACInfo*)m_pPACInfoList->GetNext(m_PACInfoPosition);
- if (pPACInfo && pPACInfo->type != PAC_DIRECT)
- {
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)PAC: %s %lu", this, pPACInfo->pszHost, pPACInfo->ulPort));
- set_proxy(pPACInfo->pszHost, (UINT16)pPACInfo->ulPort);
- }
- else if (pPACInfo)
- {
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)PAC: DIRECT", this));
- }
- }
- if (HXR_WOULD_BLOCK == theErr)
- {
- m_state = NETSRC_PACPENDING;
- }
- }
- else
- #endif /* HELIX_FEATURE_PAC */
- {
- if (HXR_OK == ReadPrefINT32(m_pPreferences, "HTTPProxySupport", un16Temp))
- {
- if (un16Temp)
- {
- if (m_pszReconnectProxy)
- {
- set_proxy(m_pszReconnectProxy, (UINT16)m_ulReconnectProxyPort);
- }
- else if (m_pPreferences && m_pPreferences->ReadPref("HTTPProxyHost", pProxyHost) == HXR_OK &&
- m_pPreferences->ReadPref("HTTPProxyPort", pProxyPort) == HXR_OK)
- {
- set_proxy((const char*)pProxyHost->GetBuffer(), atoi((const char*)pProxyPort->GetBuffer()));
- }
- HX_RELEASE(pProxyHost);
- HX_RELEASE(pProxyPort);
- }
- }
- }
- }
- else
- {
- #if defined(HELIX_FEATURE_PAC)
- // RTSP/PNM Proxy Auto Config
- if (HXR_OK == ReadPrefINT32(m_pPreferences, "RTSPPNMProxyAutoConfig", un16Temp) && un16Temp)
- {
- if (!m_pPAC)
- {
- m_pEngine->QueryInterface(IID_IHXProxyAutoConfig, (void**)&m_pPAC);
- }
- if (m_pPAC &&
- (!m_pPACInfoList || 0 == m_pPACInfoList->GetCount()))
- {
- theErr = m_pPAC->GetRTSPPNMProxyInfo((IHXProxyAutoConfigCallback*)this,
- m_pszURL,
- m_pHost);
- }
- // attempt the next proxy info from m_pPACInfoList
- else if (m_pPACInfoList && m_PACInfoPosition)
- {
- PACInfo* pPACInfo = (PACInfo*)m_pPACInfoList->GetNext(m_PACInfoPosition);
- if (pPACInfo && pPACInfo->type != PAC_DIRECT)
- {
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)PAC: %s %lu", this, pPACInfo->pszHost, pPACInfo->ulPort));
- set_proxy(pPACInfo->pszHost, (UINT16)pPACInfo->ulPort);
- }
- else if (pPACInfo)
- {
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)PAC: DIRECT", this));
- }
- }
- if (HXR_WOULD_BLOCK == theErr)
- {
- m_state = NETSRC_PACPENDING;
- }
- }
- else
- #endif /* HELIX_FEATURE_PAC */
- {
- // RTSP proxy
- if (m_bRTSPProtocol)
- {
- if (HXR_OK == ReadPrefINT32(m_pPreferences, "RTSPProxySupport", un16Temp) && un16Temp)
- {
- if (m_pszReconnectProxy)
- {
- set_proxy(m_pszReconnectProxy, (UINT16)m_ulReconnectProxyPort);
- }
- else if (m_pPreferences && m_pPreferences->ReadPref("RTSPProxyHost", pProxyHost) == HXR_OK &&
- m_pPreferences->ReadPref("RTSPProxyPort", pProxyPort) == HXR_OK)
- {
- set_proxy((const char*)pProxyHost->GetBuffer(), atoi((const char*)pProxyPort->GetBuffer()));
- }
- HX_RELEASE(pProxyHost);
- HX_RELEASE(pProxyPort);
- }
- }
- #if defined(HELIX_FEATURE_PNA)
- // PNA proxy
- else
- {
- if (HXR_OK == ReadPrefINT32(m_pPreferences, "PNAProxySupport", un16Temp) && un16Temp)
- {
- if (m_pszReconnectProxy)
- {
- set_proxy(m_pszReconnectProxy, m_ulReconnectProxyPort);
- }
- else if (m_pPreferences && m_pPreferences->ReadPref("PNAProxyHost", pProxyHost) == HXR_OK &&
- m_pPreferences->ReadPref("PNAProxyPort", pProxyPort) == HXR_OK)
- {
- set_proxy((const char*)pProxyHost->GetBuffer(), atoi((const char*)pProxyPort->GetBuffer()));
- }
- HX_RELEASE(pProxyHost);
- HX_RELEASE(pProxyPort);
- }
- }
- #endif /* defined(HELIX_FEATURE_PNA)*/
- }
- }
- }
- return theErr;
- }
- HX_RESULT
- HXNetSource::CreateProtocol()
- {
- IHXStatistics* pStatistics = NULL;
- HX_TRACE("HXNetSource::CreateProtocol");
- HX_RESULT theErr = HXR_OK;
- // delete the earlier protocol if one exists
- HX_RELEASE(m_pProto);
- if (m_bRTSPProtocol)
- {
- m_pProto = new RTSPProtocol(this, 0);
- }
- else
- {
- #if defined(HELIX_FEATURE_PNA)
- m_pProto = new PNAProtocol(this, 0);
- #else
- theErr = HXR_INVALID_PROTOCOL;
- #endif /* HELIX_FEATIRE_PNA */
- }
- if (HXR_OK == theErr)
- {
- if (m_pProto == NULL)
- {
- theErr = HXR_OUTOFMEMORY;
- }
- else
- {
- HX_ADDREF(m_pProto);
- }
- }
- // set the proxy
- if (!theErr && m_bUseProxy)
- {
- theErr = m_pProto->set_proxy(m_pProxy, m_uProxyPort);
- }
- #if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
- if (!theErr && HXR_OK == m_pProto->QueryInterface(IID_IHXStatistics, (void**) &pStatistics))
- {
- if (m_pStats)
- {
- pStatistics->InitializeStatistics(m_pStats->m_ulRegistryID);
- }
- HX_RELEASE(pStatistics);
- }
- #endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
- return theErr;
- }
- BOOL
- HXNetSource::CheckTransportTimeout(ULONG32 ulTime)
- {
- BOOL bTimedOut = FALSE;
- ULONG32 ulTimeOut = 0;
- if (!m_bDataWaitStarted)
- {
- return FALSE;
- }
- ULONG32 elapsed = CALCULATE_ELAPSED_TICKS(m_ulStartDataWait,ulTime);
- if (m_bConnectionWait)
- {
- ulTimeOut = m_ulConnectionTimeout * MILLISECS_PER_SECOND;
- }
- else
- {
- switch (m_CurrentTransport)
- {
- case MulticastMode:
- ulTimeOut = m_ulMulticastTimeout;
- break;
- case UDPMode:
- ulTimeOut = m_ulUDPTimeout;
- break;
- case TCPMode:
- ulTimeOut = m_ulTCPTimeout;
- break;
- default:
- ulTimeOut = m_ulServerTimeOut * MILLISECS_PER_SECOND;
- break;
- }
- }
- if (elapsed > ulTimeOut)
- {
- bTimedOut = TRUE;
- }
- return bTimedOut;
- }
- HX_RESULT
- HXNetSource::HandleRetry(char* pszHost, UINT16 ulPort)
- {
- HX_RESULT theErr = HXR_OK;
- if (!pszHost)
- {
- theErr = HXR_FAILED;
- goto cleanup;
- }
- HX_VECTOR_DELETE(m_pHost);
- m_pHost = new char[strlen(pszHost) + 1];
- strcpy(m_pHost, pszHost); /* Flawfinder: ignore */
- m_uPort = ulPort;
- cleanup:
- return theErr;
- }
- HX_RESULT
- HXNetSource::SetRedirectURL(char* pHost, UINT16 nPort, char* pPath, CHXURL* pURL)
- {
- HX_RESULT theErr = HXR_OK;
- if (!pHost || !pPath || !pURL)
- {
- theErr = HXR_FAILED;
- goto cleanup;
- }
- #if defined(HELIX_FEATURE_SMARTERNETWORK)
- // since we received the redirect request from server,
- // report the success of transport
- if (m_pPlayer && m_prefTransportState != PTS_READY)
- {
- HX_ASSERT(m_prefTransportState == PTS_CREATE);
- m_prefTransportState = PTS_READY;
- m_pPreferredTransport->SetTransport(m_CurrentTransport, m_uCurrCloakedPort);
- }
- m_pPreferredTransport->RemoveTransportSink(this);
- HX_RELEASE(m_pPreferredTransport);
- #endif /* HELIX_FEATURE_SMARTERNETWORK */
- HX_VECTOR_DELETE(m_pszRedirectServer);
- HX_VECTOR_DELETE(m_pszRedirectResource);
- HX_VECTOR_DELETE(m_pszRedirectURL);
- HX_DELETE(m_pRedirectURL);
- m_pszRedirectServer = new char[strlen(pHost) + 1];
- strcpy(m_pszRedirectServer, pHost); /* Flawfinder: ignore */
- m_pszRedirectResource = new char[strlen(pPath) + 1];
- strcpy(m_pszRedirectResource, pPath); /* Flawfinder: ignore */
- m_ulRedirectServerPort = nPort;
- m_pszRedirectURL = new char[strlen(pHost) + strlen(pPath) + 32];
- if (m_bRTSPProtocol)
- {
- SafeSprintf(m_pszRedirectURL, strlen(pHost) + strlen(pPath) + 32, "rtsp://%s:%u/%s", pHost, nPort, pPath); /* Flawfinder: ignore */
- }
- else
- {
- SafeSprintf(m_pszRedirectURL, strlen(pHost) + strlen(pPath) + 32, "pnm://%s:%u/%s", pHost, nPort, pPath); /* Flawfinder: ignore */
- }
- // we may use pURL to support different protocol in RTSP redirect
- // instead of contruct m_pszRedirectURL and always assume it's RTSP
- m_pRedirectURL = new CHXURL(m_pszRedirectURL);
- cleanup:
- return theErr;
- }
- HX_RESULT
- HXNetSource::SetReconnectInfo(IHXValues* pValues)
- {
- HX_RESULT rc = HXR_OK;
- UINT32 ulValue = 0;
- IHXBuffer* pServer = NULL;
- if (HXR_OK == pValues->GetPropertyULONG32("Reconnect", ulValue) &&
- 0 == ulValue)
- {
- HX_VECTOR_DELETE(m_pszReconnectServer);
- HX_VECTOR_DELETE(m_pszReconnectProxy);
- HX_VECTOR_DELETE(m_pszReconnectURL);
- m_bAttemptReconnect = FALSE;
- goto cleanup;
- }
- m_bAttemptReconnect = TRUE;
- if (HXR_OK == pValues->GetPropertyCString("Alternate-Server", pServer))
- {
- HX_VECTOR_DELETE(m_pszReconnectServer);
- HX_VECTOR_DELETE(m_pszReconnectURL);
- m_pszReconnectServer = new char[pServer->GetSize() + 1];
- ::strcpy(m_pszReconnectServer, (const char*)pServer->GetBuffer()); /* Flawfinder: ignore */
- pValues->GetPropertyULONG32("Alternate-ServerPort", m_ulReconnectServerPort);
- m_pszReconnectURL = new char[pServer->GetSize() + strlen(m_pResource) + 32];
- if (m_bRTSPProtocol)
- {
- SafeSprintf(m_pszReconnectURL,pServer->GetSize() + strlen(m_pResource) + 32, "rtsp://%s:%u/%s", pServer->GetBuffer(), m_ulReconnectServerPort, m_pResource); /* Flawfinder: ignore */
- }
- else
- {
- SafeSprintf(m_pszReconnectURL, pServer->GetSize() + strlen(m_pResource) + 32, "pnm://%s:%u/%s", pServer->GetBuffer(), m_ulReconnectServerPort, m_pResource); /* Flawfinder: ignore */
- }
- }
- HX_RELEASE(pServer);
- if (HXR_OK == pValues->GetPropertyCString("Alternate-Proxy", pServer))
- {
- HX_VECTOR_DELETE(m_pszReconnectProxy);
- m_pszReconnectProxy = new char[pServer->GetSize() + 1];
- ::strcpy(m_pszReconnectProxy, (const char*)pServer->GetBuffer()); /* Flawfinder: ignore */
- pValues->GetPropertyULONG32("Alternate-ProxyPort", m_ulReconnectProxyPort);
- }
- HX_RELEASE(pServer);
- cleanup:
- return rc;
- }
- void
- HXNetSource::StartDataWait(BOOL bConnectionWait)
- {
- m_ulStartDataWait = HX_GET_TICKCOUNT();
- m_bDataWaitStarted = TRUE;
- m_bConnectionWait = bConnectionWait;
- m_turboPlayStats.bBufferDone = FALSE;
- }
- void
- HXNetSource::StopDataWait()
- {
- m_ulStartDataWait = 0;
- m_bDataWaitStarted = FALSE;
- }
- HX_RESULT
- HXNetSource::FileHeaderReady(IHXValues* pHeader)
- {
- HX_RELEASE(m_pFileHeader);
- m_pFileHeader = pHeader;
- m_pFileHeader->AddRef();
- #if defined(HELIX_FEATURE_RECORDCONTROL)
- SendHeaderToRecordControl(TRUE, pHeader);
- #endif /* HELIX_FEATURE_RECORDCONTROL */
- #if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
- if (!pHeader || !m_pStats || m_bInRetryMode)
- #else
- if (!pHeader || m_bInRetryMode)
- #endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
- {
- return HXR_OK;
- }
- HX_RESULT retVal = PreFileHeaderReadyExt(pHeader);
- if (retVal == HXR_REQUEST_UPGRADE)
- {
- mLastError = retVal;
- return HXR_OK;
- }
- else if (retVal == HXR_WOULD_BLOCK)
- {
- return HXR_OK;
- }
- m_bContinueWithHeaders = FALSE;
- ProcessFileHeader();
- UINT32 ulNoLatency = 0;
- if (pHeader->GetPropertyULONG32("MinimizeLatency", ulNoLatency) == HXR_OK)
- {
- m_bNoLatency = (ulNoLatency == 1);
- }
- // "MinimizeLatency" in player's preference
- // can overwrite the one in file header
- ReadPrefBOOL(m_pPreferences, "MinimizeLatency", m_bNoLatency);
- PostFileHeaderReadyExt();
- return HXR_OK;
- }
- HX_RESULT
- HXNetSource::PreFileHeaderReadyExt(IHXValues* pHeader)
- {
- return HXR_OK;
- }
- HX_RESULT
- HXNetSource::PostFileHeaderReadyExt(void)
- {
- return HXR_OK;
- }
- HX_RESULT
- HXNetSource::TACInfoFromHeader(IHXValues* pValues)
- {
- // remove this method
- return HXR_OK;
- }
- HX_RESULT
- HXNetSource::HeaderCallback(IHXValues* theHeader)
- {
- HX_RESULT theErr = HXR_OK;
- STREAM_INFO* pStreamInfo = NULL;
- #if defined(HELIX_FEATURE_RECORDCONTROL)
- SendHeaderToRecordControl(FALSE, theHeader);
- #endif /* HELIX_FEATURE_RECORDCONTROL */
- if (m_bInRetryMode)
- {
- return HXR_OK;
- }
- theErr = PreHeaderCallbackExt(theHeader);
- if (HXR_ABORT == theErr)
- {
- return HXR_OK;
- }
- // we do not support receving headers once we have started getting data.
- if (m_bReceivedData)
- {
- return HXR_FAILED; // define some more appropriate error code.
- }
- if (!theHeader)
- {
- return HX_INVALID_HEADER;
- }
- PostHeaderCallbackExt(theHeader);
- theErr = ProcessStreamHeaders(theHeader, pStreamInfo);
- if (HXR_OK == theErr)
- {
- HX_ASSERT(pStreamInfo);
- IHXBuffer* pMimeType = NULL;
- if (HXR_OK == pStreamInfo->m_pHeader->GetPropertyCString("MimeType", pMimeType))
- {
- if (strcmp((char*)pMimeType->GetBuffer(), REALAUDIO_MIME_TYPE) == 0 ||
- strcmp((char*)pMimeType->GetBuffer(), REALAUDIO_MULTIRATE_MIME_TYPE) == 0 ||
- strcmp((char*)pMimeType->GetBuffer(), REALAUDIO_MULTIRATE_LIVE_MIME_TYPE) == 0)
- {
- m_lRAStreamNumber = pStreamInfo->m_uStreamNumber;
- }
- HX_RELEASE(pMimeType);
- }
- m_ulStreamIndex++;
- m_uNumStreams++;
- }
- return theErr;
- }
- HX_RESULT
- HXNetSource::PreHeaderCallbackExt(IHXValues* theHeader)
- {
- return HXR_OK;
- }
- HX_RESULT
- HXNetSource::PostHeaderCallbackExt(IHXValues* theHeader)
- {
- return HXR_OK;
- }
- BOOL HXNetSource::CanSendToDataCallback(IHXPacket* packet) const
- {
- BOOL bRet = FALSE;
-
- // We send normal data packets and dropped packets to
- // DataCallback()
- if (packet &&
- (!packet->IsLost() || (packet->GetASMFlags() & HX_ASM_DROPPED_PKT)))
- {
- bRet = TRUE;
- }
- return bRet;
- }
- HX_RESULT
- HXNetSource::DataCallback(IHXPacket* pPacket)
- {
- HX_TRACE("HXNetSource::DataCallback");
- HX_RESULT theErr = HXR_OK;
- STREAM_INFO* pStreamInfo = NULL;
- IHXBuffer* pValue = NULL;
- if (!m_bInitialized)
- {
- return HXR_NOT_INITIALIZED;
- }
- // make sure data is valid...
- if (!pPacket)
- {
- return HXR_INVALID_PARAMETER; // HX_INVALID_DATA;
- }
- if (!mStreamInfoTable->Lookup(pPacket->GetStreamNumber(), (void*&)pStreamInfo))
- {
- return HXR_INVALID_PARAMETER;
- }
- if (!CanSendToDataCallback(pPacket))
- {
- return HXR_OK;
- }
- // If this is the first time we received data,
- // then remember the time so we can report it...
- if (!m_bReceivedData)
- {
- m_bReceivedData = TRUE;
- m_bSeekPending = FALSE;
- FirstDataArrives();
- if (mLiveStream || m_bRestrictedLiveStream)
- {
- m_ulFirstPacketTime = pPacket->GetTime();
- if (m_bRestrictedLiveStream)
- {
- m_llLastExpectedPacketTime = CAST_TO_INT64 pPacket->GetTime() + CAST_TO_INT64 m_ulEndTime;
- }
- }
- }
- m_pBufferManager->UpdateCounters(pPacket);
- return theErr;
- }
- void
- HXNetSource::Initialize()
- {
- if (!m_bInitialized)
- {
- _Initialize();
- }
- }
- ULONG32
- HXNetSource::GetCurrentPlayTime(void)
- {
- if (m_pPlayer)
- {
- return m_pPlayer->GetInternalCurrentPlayTime();
- }
- else
- {
- return 0;
- }
- }
- void
- HXNetSource::FirstDataArrives()
- {
- IHXBuffer* pValue = NULL;
- m_bAltURL = FALSE;
- /* calculate elapsed time taking into consideration overflow...*/
- m_ulFirstDataArrives = CALCULATE_ELAPSED_TICKS(m_ulStartDataWait,
- HX_GET_TICKCOUNT());
- }
- HX_RESULT
- HXNetSource::SetCookie(IHXBuffer* pCookie)
- {
- HX_RESULT hr = HXR_OK;
- if (!m_pCookies)
- {
- hr = HXR_FAILED;
- goto cleanup;
- }
- hr = m_pCookies->SetCookies(m_pHost, m_pPath, pCookie);
- cleanup:
- return hr;
- }
- HX_RESULT
- HXNetSource::SetOption(UINT16 option, void* data)
- {
- HX_TRACE("HXNetSource::SetOption");
- HX_RESULT theErr = HXR_OK;
- UINT32 ulMaxBandwidth = 0;
- BOOL bTurboPlay = FALSE;
- switch (option)
- {
- case HX_PERFECTPLAY_SUPPORTED:
- {
- BOOL bServerHasPerfectPlay = TRUE;
- ::memcpy(&bServerHasPerfectPlay,data,sizeof(BOOL)); /* Flawfinder: ignore */
- m_bServerHasPerfectPlay |= bServerHasPerfectPlay;
- }
- break;
- case HX_RESEND_SUPPORTED:
- // ::memcpy(&m_bServerHasResend,data,sizeof(BOOL));
- break;
- #if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
- case HX_STATS_MASK:
- ::memcpy(&m_ulSendStatsMask,data,sizeof(ULONG32)); /* Flawfinder: ignore */
- if (m_ulSendStatsMask > MAX_STATS_MASK)
- m_ulSendStatsMask = MAX_STATS_MASK;
- break;
- case HX_STATS_INTERVAL:
- {
- UINT32 ulStatsInterval = 0;
- ::memcpy(&ulStatsInterval,data,sizeof(ULONG32)); /* Flawfinder: ignore */
- // did it change. if so, re-schedule
- if (ulStatsInterval != m_ulStatsInterval)
- {
- m_ulStatsInterval = ulStatsInterval;
- if (m_pStatsCallback)
- {
- // disable Stats report if m_ulStatsInterval is set to 0
- if (m_ulStatsInterval == 0)
- {
- m_pStatsCallback->CancelCallback();
- }
- else if (!m_pStatsCallback->IsPaused())
- {
- m_pStatsCallback->ScheduleCallback(m_ulStatsInterval);
- }
- }
- }
- }
- break;
- #endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
- case HX_TRANSPORTSWITCHING_SUPPORTED:
- ::memcpy(&m_bServerHasTransportSwitching,data,sizeof(BOOL)); /* Flawfinder: ignore */
- break;
- case HX_FORCE_PERFECT_PLAY:
- // the server is forcing us into perfect play mode
- ::memcpy(&m_bForcePerfectPlay,data,sizeof(BOOL)); /* Flawfinder: ignore */
- if(m_bForcePerfectPlay)
- {
- m_bPerfectPlay = TRUE;
- /* If the server is forcing us into PerfectPlay, it means
- * it does allow it AND the clip is perfectPlay-able.
- */
- m_bPerfectPlayAllowed = TRUE;
- m_bServerHasPerfectPlay = TRUE;
- WritePerfectPlayToRegistry();
- if(m_pProto)
- {
- m_pProto->set_transport(TCPMode, m_ulTransportPrefMask);
- m_pProto->set_perfect_play(m_bPerfectPlay);
- }
- }
- break;
- case HX_SELECTIVE_RECORD_SUPPORTED:
- ::memcpy(&mServerSelRecordSupport,data,sizeof(BOOL)); /* Flawfinder: ignore */
- break;
- case HX_GENERIC_MESSAGE_SUPPORT:
- /* RV client/server does not use it curently */
- break;
- case HX_INTERFRAME_CONTROL_SUPPORT:
- ::memcpy(&mInterframeControlSupport,data,sizeof(BOOL)); /* Flawfinder: ignore */
- break;
- case HX_BANDWIDTH_REPORT_SUPPORT:
- ::memcpy(&mServerHasBandwidthReport,data,sizeof(BOOL)); /* Flawfinder: ignore */
- break;
- case HX_FRAME_CONTROL_SUPPORT:
- ::memcpy(&mServerHasFrameControl,data,sizeof(BOOL)); /* Flawfinder: ignore */
- break;
- case HX_MAX_BANDWIDTH:
- ::memcpy(&ulMaxBandwidth,data,sizeof(UINT32)); /* Flawfinder: ignore */
- if (ulMaxBandwidth == 0)
- {
- m_serverTurboPlay = TURBO_PLAY_OFF;
- }
- else
- {
- m_ulMaxBandwidth = ulMaxBandwidth;
- }
- break;
- case HX_TURBO_PLAY:
- ::memcpy(&bTurboPlay,data,sizeof(BOOL)); /* Flawfinder: ignore */
- if (bTurboPlay)
- {
- m_serverTurboPlay = TURBO_PLAY_ON;
- }
- else
- {
- m_serverTurboPlay = TURBO_PLAY_OFF;
- }
- break;
- default:
- theErr = HXR_INVALID_PARAMETER; //HX_INVALID_OPTION;
- break;
- }
- return theErr;
- }
- HX_RESULT
- HXNetSource::TransportStarted(TransportMode mode)
- {
- HX_RESULT rc = HXR_OK;
- #if defined(HELIX_FEATURE_SMARTERNETWORK)
- if (m_pPreferredTransport && m_pPreferredTransport->ValidateTransport(mode))
- {
- set_transport(mode);
- }
- else
- {
- rc = HXR_BAD_TRANSPORT;
- ReportError(rc);
- }
- #endif /* HELIX_FEATURE_SMARTERNETWORK */
- return rc;
- }
- HX_RESULT
- HXNetSource::switch_out_of_perfectplay()
- {
- // Turn off perfect play mode.
- m_bPerfectPlay = FALSE;
- // Reset "transport" selection...
- m_CurrentTransport = UnknownMode;
- // Reconnect...
- return handleTransportSwitch();
- }
- HX_RESULT
- HXNetSource::handleTransportSwitch()
- {
- HX_RESULT rc = HXR_OK;
- #ifdef _MACINTOSH
- BOOL bAtInterrupt = FALSE;
- IHXInterruptState* pInterruptState = NULL;
- if (m_pEngine &&
- m_pEngine->QueryInterface(IID_IHXInterruptState, (void**) &pInterruptState) == HXR_OK)
- {
- bAtInterrupt = pInterruptState->AtInterruptTime();
- HX_RELEASE(pInterruptState);
- }
- if (bAtInterrupt)
- {
- m_bBruteForceConnectToBeDone = TRUE;
- HX_ASSERT(m_pSourceInfo);
- if (m_pSourceInfo)
- {
- m_pSourceInfo->ScheduleProcessCallback();
- }
- return HXR_OK;
- }
- #endif
- Reset();
- HX_VECTOR_DELETE(m_pszRedirectServer);
- HX_VECTOR_DELETE(m_pszRedirectResource);
- HX_VECTOR_DELETE(m_pszRedirectURL);
- HX_VECTOR_DELETE(m_pszReconnectServer);
- HX_VECTOR_DELETE(m_pszReconnectURL);
- // have we already received the headers
- if (mStreamInfoTable->GetCount() > 0)
- {
- m_bInRetryMode = TRUE;
- }
- m_bBruteForceReconnected = TRUE;
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)TransportSwitch %s", this, m_pszURL));
- rc = Setup(m_pHost, m_pResource, m_uPort,
- m_bLossCorrection, m_pURL, m_bAltURL);
- return rc;
- }
- HX_RESULT
- HXNetSource::handleProxySwitch(HX_RESULT inError)
- {
- #if defined(HELIX_FEATURE_PAC)
- HX_RESULT rc = HXR_FAILED;
- if (m_pPACInfoList && m_pPACInfoList->GetCount() && m_PACInfoPosition)
- {
- Reset();
- m_bUseProxy = FALSE;
- HX_VECTOR_DELETE(m_pProxy);
- m_bBruteForceReconnected = TRUE;
- rc = Setup(m_pHost, m_pResource, m_uPort, m_bLossCorrection, m_pURL, m_bAltURL);
- }
- if (HXR_OK != rc)
- {
- rc = inError;
- }
- return rc;
- #else
- return HXR_NOTIMPL;
- #endif /* HELIX_FEATURE_PAC */
- }
- HX_RESULT
- HXNetSource::handleRedirect()
- {
- HX_RESULT rc = HXR_OK;
- if (!m_pszRedirectURL)
- {
- rc = HXR_FAILED;
- goto cleanup;
- }
- Reset();
- m_ulDuration = 0;
- m_ulPreRollInMs = 0;
- m_ulPreRoll = 0;
- m_ulAvgBandwidth = 0;
- m_ulLastBufferingCalcTime = 0;
- m_bRARVSource = FALSE;
- m_bRARVSourceVerified = FALSE;
- // remove obsolete reconnect information
- HX_VECTOR_DELETE(m_pszReconnectServer);
- HX_VECTOR_DELETE(m_pszReconnectProxy);
- HX_VECTOR_DELETE(m_pszReconnectURL);
- // have we already received the headers
- if (mStreamInfoTable->GetCount() > 0)
- {
- m_bInRetryMode = TRUE;
- }
- m_bBruteForceReconnected = TRUE;
- if (strcasecmp(m_pszRedirectServer, m_pHost) != 0)
- {
- while (m_pPACInfoList && m_pPACInfoList->GetCount())
- {
- PACInfo* pPACInfo = (PACInfo*)m_pPACInfoList->RemoveHead();
- HX_DELETE(pPACInfo);
- }
- #if defined(HELIX_FEATURE_SMARTERNETWORK)
- if (m_pPreferredTransport)
- {
- m_pPreferredTransport->RemoveTransportSink(this);
- HX_RELEASE(m_pPreferredTransport);
- }
- #endif /* HELIX_FEATURE_SMARTERNETWORK */
- }
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)Redirect %s", this, m_pszRedirectURL));
- rc = Setup(m_pszRedirectServer, m_pszRedirectResource, (UINT16)m_ulRedirectServerPort,
- m_bLossCorrection, m_pRedirectURL, m_bAltURL);
- cleanup:
- HX_VECTOR_DELETE(m_pszRedirectServer);
- HX_VECTOR_DELETE(m_pszRedirectResource);
- HX_VECTOR_DELETE(m_pszRedirectURL);
- return rc;
- }
- HX_RESULT
- HXNetSource::handleReconnect()
- {
- HX_RESULT rc = HXR_OK;
- Reset();
- HX_VECTOR_DELETE(m_pszRedirectServer);
- HX_VECTOR_DELETE(m_pszRedirectResource);
- HX_VECTOR_DELETE(m_pszRedirectURL);
- // have we already received the headers
- if (mStreamInfoTable->GetCount() > 0)
- {
- m_bInRetryMode = TRUE;
- }
- m_bBruteForceReconnected = TRUE;
- if (m_pszReconnectURL)
- {
- while (m_pPACInfoList && m_pPACInfoList->GetCount())
- {
- PACInfo* pPACInfo = (PACInfo*)m_pPACInfoList->RemoveHead();
- HX_DELETE(pPACInfo);
- }
- #if defined(HELIX_FEATURE_SMARTERNETWORK)
- if (m_pPreferredTransport && strcasecmp(m_pszReconnectServer, m_pHost) != 0)
- {
- m_pPreferredTransport->RemoveTransportSink(this);
- HX_RELEASE(m_pPreferredTransport);
- }
- #endif /* HELIX_FEATURE_SMARTERNETWORK */
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)Reconnect %s", this, m_pszReconnectURL));
- CHXURL obj(m_pszReconnectURL);
- rc = Setup(m_pszReconnectServer, m_pResource, (UINT16)m_ulReconnectServerPort,
- m_bLossCorrection, &obj, m_bAltURL);
- }
- else
- {
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)Reconnect %s", this, m_pszURL));
- rc = Setup(m_pHost, m_pResource, m_uPort,
- m_bLossCorrection, m_pURL, m_bAltURL);
- }
- HX_VECTOR_DELETE(m_pszReconnectServer);
- HX_VECTOR_DELETE(m_pszReconnectProxy);
- HX_VECTOR_DELETE(m_pszReconnectURL);
- return rc;
- }
- BOOL
- HXNetSource::IsNetworkAvailable()
- {
- #if defined(HELIX_FEATURE_NETCHECK)
- CHXNetCheck* pNetCheck = new CHXNetCheck(4000);
- BOOL bIsNetAvailable = FALSE;
- if(pNetCheck)
- {
- pNetCheck->AddRef();
- if (HXR_OK == pNetCheck->Init((IUnknown*)(IHXPlayer*)m_pPlayer))
- {
- bIsNetAvailable = pNetCheck->FInternetAvailable(FALSE, m_bUseProxy);
- }
- }
- HX_RELEASE(pNetCheck);
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)IsNetworkAvailable %lu", this, bIsNetAvailable));
- return bIsNetAvailable;
- #else
- return FALSE;
- #endif /* HELIX_FEATURE_NETCHECK */
- }
- HX_RESULT
- HXNetSource::AttemptReconnect()
- {
- HX_RESULT rc = HXR_OK;
- BOOL bDoneAtTransport = FALSE;
- UINT32 ulCurrentPlayPos = 0;
- UINT32 ulPacketsAheadInTime = 0;
- UINT32 ulReconnectStartTime = 0;
- UINT32 ulLastPacketTime = 0;
- UINT32 ulLastEventTime = 0;
- UINT32 ulLowestLastEventTime = MAX_UINT32;
- INT64 llLowestTimestampAtTransport = 0;
- INT64 llHighestTimestampAtTransport = 0;
- UINT32 ulNumBytesAtTransport = 0;
- STREAM_INFO* pStreamInfo = NULL;
- m_state = NETSRC_RECONNECTPENDING;
- // caculate how far ahead the packets received at transport
- CHXMapLongToObj::Iterator lStreamIterator = mStreamInfoTable->Begin();
- for (; lStreamIterator != mStreamInfoTable->End(); ++lStreamIterator)
- {
- pStreamInfo = (STREAM_INFO*) (*lStreamIterator);
- GetCurrentBuffering(pStreamInfo->m_uStreamNumber,
- llLowestTimestampAtTransport,
- llHighestTimestampAtTransport,
- ulNumBytesAtTransport,
- bDoneAtTransport);
- // in case we have no more packets at transport
- // get the last packet time from the saved queue
- if (ulNumBytesAtTransport == 0 &&
- pStreamInfo->m_pPreReconnectEventList)
- {
- ulLastPacketTime = *(UINT32*)pStreamInfo->m_pPreReconnectEventList->GetTail();
- }
- else
- {
- // XXX HP need to handle timestamp rollover
- ulLastPacketTime = INT64_TO_ULONG32(llHighestTimestampAtTransport);
- }
- ulLastEventTime = AdjustEventTime(pStreamInfo, ulLastPacketTime);
- if (ulLastEventTime < ulLowestLastEventTime)
- {
- ulLowestLastEventTime = ulLastEventTime;
- }
- }
- // if we have received all the packets, we simply set the reconnect pending state without
- // schedule/start reconnect since it's not needed if the user let the playback continues
- // to the end.
- // reconnect will occur if the seek happens afterwards.
- if (bDoneAtTransport)
- {
- return HXR_OK;
- }
- if(!mLiveStream)
- {
- // retrieve all the packets at transport layer to the source layer
- #if defined(HELIX_FEATURE_RECORDCONTROL)
- if(m_bPlayFromRecordControl)
- {
- m_state = NETSRC_READY;
- FillRecordControl();
- m_state = NETSRC_RECONNECTPENDING;
- }
- else
- #endif /* HELIX_FEATURE_RECORDCONTROL */
- {
- CHXMapLongToObj::Iterator lStreamIterator = mStreamInfoTable->Begin();
- for (; lStreamIterator != mStreamInfoTable->End(); ++lStreamIterator)
- {
- CHXEvent* pEvent = NULL;
- pStreamInfo = (STREAM_INFO*) (*lStreamIterator);
- UINT16 uStreamNumber = pStreamInfo->m_uStreamNumber;
- CHXEventList* pEventList = &pStreamInfo->m_EventList;
- // in case we reconnect again right after reconnect
- // get all the packets from PosReconnectEventList first since they are ahead of
- // the packets from protocol
- while (pStreamInfo->m_pPosReconnectEventList &&
- pStreamInfo->m_pPosReconnectEventList->GetNumEvents())
- {
- pEvent = (CHXEvent*)pStreamInfo->m_pPosReconnectEventList->RemoveHead();
- pEventList->InsertEvent(pEvent);
- }
- // get all the packets from the protocol
- if(m_pProto)
- {
- while (HXR_OK == m_pProto->GetEvent(uStreamNumber, pEvent))
- {
- pEventList->InsertEvent(pEvent);
- }
- }
- }
- }
- Reset();
- }
- ulCurrentPlayPos = m_pPlayer->GetInternalCurrentPlayTime();
- if (ulCurrentPlayPos <= ulLowestLastEventTime)
- {
- ulPacketsAheadInTime = ulLowestLastEventTime - ulCurrentPlayPos;
- if (ulPacketsAheadInTime >= NEW_DEF_SETTINGS_TIMEOUT)
- {
- ulReconnectStartTime = ulPacketsAheadInTime - NEW_DEF_SETTINGS_TIMEOUT;
- if (!m_pReconnectCallback)
- {
- m_pReconnectCallback = new ReconnectCallback(this);
- m_pReconnectCallback->AddRef();
- }
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)AttemptReconnect in %lu ms", this, ulReconnectStartTime));
- m_pReconnectCallback->ScheduleCallback(ulReconnectStartTime);
- return HXR_OK;
- }
- }
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)AttemptReconnect now", this));
- return StartReconnect();
- }
- HX_RESULT
- HXNetSource::StartReconnect()
- {
- HX_RESULT rc = HXR_OK;
- BOOL bFirstEvent = TRUE;
- UINT16 uStreamNumber = 0;
- UINT32 ulPrevPacketTime = 0;
- UINT32 ulLargestGapInPacketTime = 0;
- UINT32 ulLastPacketTime = 0;
- UINT32 ulLowestLastPacketTime = MAX_UINT32;
- UINT32* pPacketTime = NULL;
- STREAM_INFO* pStreamInfo = NULL;
- CHXEvent* pEvent = NULL;
- CHXEventList* pEventList = NULL;
- IHXPacket* pPacket = NULL;
- CHXSimpleList::Iterator lIter;
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)StartReconnect", this));
- // for live source, nothing we can do to archieve seamless user experience like
- // the static content does except tearn down the source and start all over with
- // new source as if it were a redirect
- if (mLiveStream)
- {
- if (m_pszReconnectURL)
- {
- rc = m_pSourceInfo->HandleRedirectRequest(m_pszReconnectURL);
- }
- else
- {
- rc = m_pSourceInfo->HandleRedirectRequest(m_pszURL);
- }
- }
- else if (NETSRC_RECONNECTPENDING == m_state)
- {
- // retrieve all the packets at transport layer to the source layer
- CHXMapLongToObj::Iterator lStreamIterator = mStreamInfoTable->Begin();
- for (; lStreamIterator != mStreamInfoTable->End(); ++lStreamIterator)
- {
- pStreamInfo = (STREAM_INFO*) (*lStreamIterator);
- uStreamNumber = pStreamInfo->m_uStreamNumber;
- pEventList = &pStreamInfo->m_EventList;
- pStreamInfo->m_bReconnectToBeDone = TRUE;
- pStreamInfo->m_ulReconnectOverlappedPackets = 0;
- bFirstEvent = TRUE;
- if(m_bPlayFromRecordControl)
- {
- // Now we can get all available events out of RecordControl
- // in order to use them for reconnection.
- while (TRUE)
- {
- HX_RESULT nRes = GetEventFromRecordControl(uStreamNumber, pEvent);
- if(nRes == HXR_OK)
- {
- pEventList->InsertEvent(pEvent);
- }
- if(nRes != HXR_OK && nRes != HXR_WOULD_BLOCK)
- break;
- }
- HX_ASSERT(pEventList->GetCount());
- }
- // save the prereconnect packet time so that we can
- // find the adjointing point after reconnect
- lIter = pEventList->Begin();
- for (; lIter != pEventList->End(); ++lIter)
- {
- pEvent = (CHXEvent*) (*lIter);
- AddToPreReconnectEventList(pStreamInfo, pEvent);
- }
- CHXSimpleList* pPreReconnectEventList = pStreamInfo->m_pPreReconnectEventList;
- HX_ASSERT(pPreReconnectEventList);
- // determine the seek position after reconnect
- // enough overlapped packets to ensure searching of adjointing point
- // successfully after the reconnect
- if (pPreReconnectEventList)
- {
- CHXSimpleList::Iterator lIterator = pPreReconnectEventList->Begin();
- for (; lIterator != pPreReconnectEventList->End(); ++lIterator)
- {
- UINT32* pPreReconnectEventTime = (UINT32*)(*lIterator);
- UpdateReconnectInfo(*pPreReconnectEventTime,
- bFirstEvent,
- ulPrevPacketTime,
- ulLargestGapInPacketTime,
- ulLastPacketTime);
- }
- }
- if (ulLastPacketTime < ulLowestLastPacketTime)
- {
- ulLowestLastPacketTime = ulLastPacketTime;
- }
- }
- if (ulLowestLastPacketTime > ulLargestGapInPacketTime)
- {
- m_ulSeekPendingTime = ulLowestLastPacketTime - ulLargestGapInPacketTime;
- }
- else
- {
- m_ulSeekPendingTime = 0;
- }
- m_bSeekPending = TRUE;
- m_state = NETSRC_RECONNECTSTARTED;
- rc = handleReconnect();
- }
- else if (NETSRC_RECONNECTFORCED == m_state)
- {
- // we don't need to retrieve the left-over packets if the reconnect
- // is forced(i.e. seek)
- m_state = NETSRC_RECONNECTSTARTED;
- rc = handleReconnect();
- }
- return rc;
- }
- HX_RESULT
- HXNetSource::ProcessReconnect(STREAM_INFO* pStreamInfo)
- {
- HX_RESULT rc = HXR_OK;
- BOOL bAdjointFound = FALSE;
- BOOL bOverlapped = FALSE;
- UINT32 ulAdjointTimeFrom = 0;
- UINT32 ulAdjointTimeTo = 0;
- IHXPacket* pPacket = NULL;
- IHXPacket* pNextPacket = NULL;
- UINT32* pPreReconnectEventTime = NULL;
- UINT32* pPreReconnectNextEventTime = NULL;
- CHXEvent* pPosReconnectEvent = NULL;
- CHXEvent* pPosReconnectNextEvent = NULL;
- CHXSimpleList* pPreReconnectEventList = NULL;
- CHXEventList* pPosReconnectEventList = NULL;
- LISTPOSITION preReconnectPos = 0;
- LISTPOSITION posReconnectPos = 0;
- //DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "EnterProcessReconnect %p %lu", this, pStreamInfo->m_uStreamNumber));
- if (!pStreamInfo || !pStreamInfo->m_pPosReconnectEventList)
- {
- rc = HXR_FAILED;
- goto cleanup;
- }
- if(!pStreamInfo->m_pPreReconnectEventList || !pStreamInfo->m_pPreReconnectEventList->GetCount())
- {
- // We need to use all the packets in pStreamInfo->m_pPosReconnectEventList.
- pStreamInfo->m_bReconnectToBeDone = FALSE;
- }
- pPosReconnectEventList = pStreamInfo->m_pPosReconnectEventList;
- posReconnectPos = pPosReconnectEventList->GetHeadPosition();
- if (posReconnectPos)
- {
- pPosReconnectEvent = (CHXEvent*) pPosReconnectEventList->GetAt(posReconnectPos);
- while (pPosReconnectEvent && pStreamInfo->m_bReconnectToBeDone)
- {
- pPosReconnectEventList->GetNext(posReconnectPos);
- if (!posReconnectPos)
- {
- break;
- }
- pPacket = pPosReconnectEvent->GetPacket();
- pPosReconnectNextEvent = (CHXEvent*) pPosReconnectEventList->GetAt(posReconnectPos);
- pNextPacket = pPosReconnectNextEvent->GetPacket();
- if (pPacket->GetTime() != pNextPacket->GetTime())
- {
- bAdjointFound = TRUE;
- ulAdjointTimeFrom = pPacket->GetTime();
- ulAdjointTimeTo = pNextPacket->GetTime();
- }
- if (bAdjointFound && pStreamInfo->m_pPreReconnectEventList)
- {
- pPreReconnectEventList = pStreamInfo->m_pPreReconnectEventList;
- preReconnectPos = pPreReconnectEventList->GetHeadPosition();
- pPreReconnectEventTime = (UINT32*) pPreReconnectEventList->GetAt(preReconnectPos);
- while (pPreReconnectEventTime)
- {
- pPreReconnectEventList->GetNext(preReconnectPos);
- if (!preReconnectPos)
- {
- break;
- }
- pPreReconnectNextEventTime = (UINT32*) pPreReconnectEventList->GetAt(preReconnectPos);
- if (*pPreReconnectEventTime == ulAdjointTimeFrom &&
- *pPreReconnectNextEventTime == ulAdjointTimeTo)
- {
- //{FILE* f1 = ::fopen("c:\temp\reconnect.txt", "a+"); ::fprintf(f1, "ProcessReconnect(succeeded)t%lut%lut%lun", pStreamInfo->m_uStreamNumber, ulAdjointTimeFrom, ulAdjointTimeTo);::fclose(f1);}
- pStreamInfo->m_bReconnectToBeDone = FALSE;
- break;
- }
- pPreReconnectEventTime = pPreReconnectNextEventTime;
- }
- }
- pPosReconnectEvent = pPosReconnectNextEvent;
- }
- }
- if (!pStreamInfo->m_bReconnectToBeDone)
- {
- if (pPreReconnectEventList)
- {
- pPreReconnectEventList->GetNext(preReconnectPos);
- while (preReconnectPos)
- {
- pStreamInfo->m_ulReconnectOverlappedPackets++;
- pPreReconnectEventList->GetNext(preReconnectPos);
- }
- }
- //{FILE* f1 = ::fopen("c:\temp\reconnect.txt", "a+"); ::fprintf(f1, "EventsOverlappedt%lut%lun", pStreamInfo->m_uStreamNumber, pStreamInfo->m_ulReconnectOverlappedPackets);::fclose(f1);}
- while (pPosReconnectEventList->GetNumEvents() &&
- pStreamInfo->m_ulReconnectOverlappedPackets)
- {
- pPosReconnectEvent = pPosReconnectEventList->RemoveHead();
- if (bOverlapped)
- {
- pStreamInfo->m_ulReconnectOverlappedPackets--;
- }
- if (pPosReconnectEvent == pPosReconnectNextEvent)
- {
- bOverlapped = TRUE;
- }
- //{FILE* f1 = ::fopen("c:\temp\reconnect.txt", "a+"); ::fprintf(f1, "DeleteEvent(overlapped)t%lut%lun", pStreamInfo->m_uStreamNumber, pPosReconnectEvent->GetPacket()->GetTime());::fclose(f1);}
- HX_DELETE(pPosReconnectEvent);
- }
- EndReconnect();
- }
- cleanup:
- //DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "LeaveProcessReconnect %p %lu %lu", this, pStreamInfo->m_uStreamNumber, pStreamInfo->m_bReconnectToBeDone));
- return rc;
- }
- HX_RESULT
- HXNetSource::EndReconnect()
- {
- HX_RESULT rc = HXR_OK;
- BOOL bReconnectToBeDone = FALSE;
- STREAM_INFO* pStreamInfo = NULL;
- CHXMapLongToObj::Iterator lStreamIterator = mStreamInfoTable->Begin();
- for (; lStreamIterator != mStreamInfoTable->End(); ++lStreamIterator)
- {
- pStreamInfo = (STREAM_INFO*) (*lStreamIterator);
- if (pStreamInfo->m_bReconnectToBeDone)
- {
- bReconnectToBeDone = TRUE;
- break;
- }
- }
- if (!bReconnectToBeDone)
- {
- DEBUG_OUT(m_pPlayer, DOL_TRANSPORT, (s, "(%p)EndReconnect", this));
- m_state = NETSRC_READY;
- }
- return rc;
- }
- HX_RESULT
- HXNetSource::AddToPreReconnectEventList(STREAM_INFO* pStreamInfo, CHXEvent* pEvent)
- {
- HX_RESULT rc = HXR_OK;
- UINT32* pPacketTime = NULL;
- CHXSimpleList* pPreReconnectEventList = NULL;
- if (!pStreamInfo->m_pPreReconnectEventList)
- {
- pStreamInfo->m_pPreReconnectEventList = new CHXSimpleList;
- }
- pPreReconnectEventList = pStreamInfo->m_pPreReconnectEventList;
- if (pPreReconnectEventList->GetCount() == 30)
- {
- pPacketTime = (UINT32*)pPreReconnectEventList->RemoveHead();
- HX_DELETE(pPacketTime);
- }
- if (!pPacketTime)
- {
- pPacketTime = new UINT32;
- }
- *pPacketTime = pEvent->GetPacket()->GetTime();
- pPreReconnectEventList->AddTail(pPacketTime);
- HX_ASSERT(pPreReconnectEventList->GetCount() <= 30);
- return rc;
- }
- void
- HXNetSource::UpdateReconnectInfo(UINT32 ulPacketTime,
- BOOL& bFirstEvent,
- UINT32& ulPrevPacketTime,
- UINT32& ulLargestGapInPacketTime,
- UINT32& ulLastPacketTime)
- {
- UINT32 ulGapInPacketTime = 0;
- if (bFirstEvent)
- {
- bFirstEvent = FALSE;
- ulPrevPacketTime = 0;
- ulLargestGapInPacketTime = 0;
- ulLastPacketTime = 0;
- }
- else
- {
- HX_ASSERT(ulPacketTime >= ulPrevPacketTime);
- ulGapInPacketTime = ulPacketTime - ulPrevPacketTime;
- if (ulLargestGapInPacketTime < ulGapInPacketTime)
- {
- ulLargestGapInPacketTime = ulGapInPacketTime;
- }
- }
- ulPrevPacketTime = ulPacketTime;
- if (ulLastPacketTime < ulPacketTime)
- {
- ulLastPacketTime = ulPacketTime;
- }
- return;
- }
- void
- HXNetSource::Reset()
- {
- // Consider the net play object uninitialized.
- m_bInitialized = FALSE;
- m_bPerfectPlayErrorChecked = FALSE;
- #if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
- if (m_pStatsCallback)
- {
- m_pStatsCallback->CancelCallback();
- }
- #endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
- // Stop current connection. This will totally disconnect
- // from the server, and shut down the communication ports.
- // DoCleanup();
- // Close down the net connection
- HX_RELEASE(m_pProtocolStatus);
- /* UnRegister any previously registered source */
- if (m_pSourceInfo)
- m_pSourceInfo->UnRegister();
- if (m_pProto)
- {
- m_pProto->stop();
- HX_RELEASE (m_pProto);
- }
- // cleanup the log list
- if (m_pLogInfoList)
- {
- while (m_pLogInfoList->GetCount() > 0)
- {
- char* pszInfo = (char*) m_pLogInfoList->RemoveHead();
- delete [] pszInfo;
- }
- HX_DELETE(m_pLogInfoList);
- }
- m_ulMaxBandwidth = 4000;
- m_bPushDownSet = FALSE;
- m_pushDownStatus = PUSHDOWN_NONE;
- m_bReceivedData = FALSE;
- m_bDataWaitStarted = FALSE;
- m_bFirstResume = TRUE;
- m_bResendAuthenticationInfo = TRUE;
- m_bRTSPRuleFlagWorkAround = FALSE;
- m_bNoLatencySet = FALSE;
- m_bIsActive = FALSE;
- return;
- }
- void
- HXNetSource::ReSetup()
- {
- HX_RESULT theErr = HXR_OK;
- Reset();
- mServerSelRecordSupport = FALSE;
- mInterframeControlSupport = FALSE;
- mServerHasBandwidthReport = FALSE;
- mServerHasFrameControl = FALSE;
- m_bConnectionWait = FALSE;
- m_bUseUDPPort = FALSE;
- m_bTimeBased = FALSE;
- m_bAtInterrupt = FALSE;
- m_bInRetryMode = FALSE;
- m_bBruteForceReconnected = FALSE;
- m_bBruteForceConnectToBeDone = FALSE;
- m_bUserHasCalledResume = FALSE;
- m_bUserHasCalledStartInit = FALSE;
- m_CurrentTransport = UnknownMode;
- m_ulSeekPendingTime = 0;
- m_bSeekPending = FALSE;
- m_ulStatsInterval = 0;
- m_ulSendStatsMask = 0;
- #if defined(HELIX_FEATURE_SMIL_REPEAT)
- if (m_pSourceInfo)
- {
- CHXSimpleList* pRepeatList = m_pSourceInfo->m_bLeadingSource?m_pSourceInfo->m_pRepeatList:
- m_pSourceInfo->m_pPeerSourceInfo->m_pRepeatList;
- if (pRepeatList)
- {
- RepeatInfo* pRepeatInfo = (RepeatInfo*)pRepeatList->GetAt(m_pSourceInfo->m_curPosition);
- m_ulDelay = m_pSourceInfo->m_ulRepeatDelayTimeOffset + pRepeatInfo->ulDelay;
- if (m_pSourceInfo->m_bRepeatIndefinite &&
- m_pSourceInfo->m_ulMaxDuration &&
- m_ulDelay + pRepeatInfo->ulDuration > m_ulOriginalDelay + m_pSourceInfo->m_ulMaxDuration)
- {
- m_ulRestrictedDuration = m_ulOriginalDelay + m_pSourceInfo->m_ulMaxDuration - m_ulDelay;
- }
- else
- {
- m_ulRestrictedDuration = pRepeatInfo->ulDuration;
- }
- }
- }
- #endif /* HELIX_FEATURE_SMIL_REPEAT */
- #if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
- m_pStats->Reset();
- #endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
- m_bReSetup = TRUE;
- theErr = Setup(m_pHost, m_pResource, m_uPort, m_bLossCorrection,
- m_pURL, m_bAltURL);
- return;
- }
- char*
- HXNetSource::GetAltURL(BOOL& bDefault)
- {
- #if defined(HELIX_FEATURE_ALT_URL)
- char* pszAltURL = NULL;
- CHXURL* pAltURL = NULL;
- UINT16 uHTTPPort = DEF_HTTP_PORT;
- UINT32 ulValue = 0;
- IHXValues* pProperties = NULL;
- if (!m_pURL || m_bInitialized)
- {
- goto cleanup;
- }
- pszAltURL = m_pURL->GetAltURL(bDefault);
- /* XXX HP we can't simply exempt AltURL if the same port has
- been attempted during HTTPCloaking since the same port
- failed in HTTPCloaking may works for HTTP streaming
- we really need to determine the actual error code of
- HTTPCloaking's failure of the port(such as Connection
- failure vs bad server which doesn't support HTTPCloaking)
- // no attempt on AltURL if HTTPCloaking has failed on
- // the same port
- if (HTTPCloakMode == m_CurrentTransport)
- {
- if (!bDefault)
- {
- pAltURL = new CHXURL(pszAltURL);
- pProperties = pAltURL->GetProperties();
- if (pProperties &&
- HXR_OK == pProperties->GetPropertyULONG32(PROPERTY_PORT, ulValue))
- {
- uHTTPPort = (UINT16)ulValue;
- }
- HX_RELEASE(pProperties);
- HX_DELETE(pAltURL);
- }
- if (uHTTPPort == m_uCurrCloakedPort ||
- IsInCloakPortList(uHTTPPort))
- {
- HX_VECTOR_DELETE(pszAltURL);
- }
- }
- */
- cleanup:
- return pszAltURL;
- #else
- return NULL;
- #endif /* HELIX_FEATURE_ALT_URL */
- }
- HX_RESULT
- HXNetSource::switch_to_next_transport(HX_RESULT incomingError)
- {
- HX_RESULT theErr = HXR_OK;
- /* Use TCP for PercectPlay for PNA protocol.
- * for RTSP, we always use UDP..
- * m_bTimeBased is set to TRUE for RTSP protocol
- */
- if ((m_bPerfectPlay && !m_bTimeBased) || !m_pPreferredTransport)
- {
- if (UDPMode == m_CurrentTransport)
- {
- m_PreferredTransport = TCPMode;
- }
- else
- {
- m_PreferredTransport = UnknownMode;
- }
- }
- #if defined(HELIX_FEATURE_SMARTERNETWORK)
- else
- {
- theErr = m_pPreferredTransport->SwitchTransport(incomingError, m_PreferredTransport);
- }
- #endif /* HELIX_FEATURE_SMARTERNETWORK */
- // If there is no new transport, then we are done!
- if (HXR_OK != theErr || UnknownMode == m_PreferredTransport)
- {
- theErr = incomingError;
- goto exit;
- }
- // reset transport state to PTS_CREATE so the PreferredTransport object
- // will be notified on the success/failure of this transport switch
- m_prefTransportState = PTS_CREATE;
- // Actually switch here!
- if (m_pProto && m_pProto->can_switch_transport())
- {
- theErr = m_pProto->switch_transport(m_PreferredTransport);
- }
- else
- {
- // Switch transport via brute force, non-optimal manner.
- theErr = handleTransportSwitch();
- mLastError = theErr;
- }
- exit:
- return theErr;
- }
- HX_RESULT
- HXNetSource::_Initialize(void)
- {
- HX_RESULT theErr = HXR_OK;
- ResetASMSource();
- if (m_bInRetryMode)
- {
- if (m_bBruteForceReconnected)
- {
- // reset ASMSource
- CHXSimpleList::Iterator lIter = m_HXStreamList.Begin();
- for (; lIter != m_HXStreamList.End(); ++lIter)
- {
- HXStream* pStream = (HXStream*) (*lIter);
- pStream->ResetASMSource((IHXASMSource*)m_pProto);
- }
- if (m_pSourceInfo)
- {
- m_pSourceInfo->ReInitializeStats();
- }
- if (m_bFastStart)
- {
- EnterFastStart();
- }
- }
- if (m_bClipTimeAdjusted)
- {
- m_bInitialized = TRUE;
- /* Initial seek if a start time is specified */
- if (m_ulStartTime > 0)
- {
- DoSeek(0);
- }
- }
- }
- /* get all the flags */
- if (!theErr && m_pProto)
- {
- m_bPerfectPlayAllowed |= m_pProto->IsPerfectPlayAllowed();
- mSaveAsAllowed |= m_pProto->IsSaveAllowed();
- WritePerfectPlayToRegistry();
- mLiveStream = m_pProto->IsLive();
- if (mLiveStream && m_bCustomEndTime)
- {
- m_bRestrictedLiveStream = TRUE;
- }
- }
- if (m_bInitialized)
- {
- return HXR_OK;
- }
- /* calculated weigthed preroll for the source */
- if (!m_bTimeBased && m_ulAvgBandwidth > 0)
- {
- m_ulPreRollInMs = m_ulPreRollInMs / m_ulAvgBandwidth;
- }
- if (!theErr)
- {
- m_bInitialized = TRUE;
- m_ulOriginalDuration = m_ulDuration;
- theErr = AdjustClipTime();
- }
- m_pBufferManager->Init();
- #if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
- /*
- * put the protocol and protocol version into the stats so rarenderer
- * and any other part of the system that needs to work with old servers
- * can know what the version and protocol is.
- */
- if (m_pStats != NULL && m_pProto != NULL)
- {
- m_pStats->m_pProtocolVersion->SetInt(m_pProto->get_protocol_version());
- m_pStats->m_pProtocol->SetStr((char*)m_pProto->get_protocol_name());
- }
- IHXBuffer* pBuffer = NULL;
- IHXValues* pResponseHeaders = NULL;
- if (HXR_OK == m_pRequest->GetResponseHeaders(pResponseHeaders) &&
- pResponseHeaders)
- {
- if (HXR_OK == pResponseHeaders->GetPropertyCString("Server", pBuffer))
- {
- if (m_pStats->m_pServerInfo)
- {
- m_pStats->m_pServerInfo->SetStr((char*)pBuffer->GetBuffer());
- }
- ::GetVersionFromString((char*)pBuffer->GetBuffer(), m_ulServerVersion);
- }
- HX_RELEASE(pBuffer);
- }
- HX_RELEASE(pResponseHeaders);
- #endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
- return theErr;
- }
- /*
- * IHXRegistryID methods
- */
- /************************************************************************
- * Method:
- * IHXRegistryID::GetID
- * Purpose:
- * Get registry ID(hash_key) of the objects(player, source and stream)
- *
- */
- STDMETHODIMP
- HXNetSource::GetID(REF(UINT32) /*OUT*/ ulRegistryID)
- {
- #if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
- (m_pStats)?(ulRegistryID = m_pStats->m_ulRegistryID):(ulRegistryID = 0);
- return HXR_OK;
- #else
- return HXR_NOTIMPL;
- #endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */
- }
- /************************************************************************
- * Method:
- * IHXInfoLogger::LogInformation
- * Purpose:
- * Logs any user defined information in form of action and
- * associated data.
- */
- STDMETHODIMP
- HXNetSource::LogInformation(const char* /*IN*/ pAction,
- const char* /*IN*/ pData)
- {
- HX_RESULT hr = HXR_OK;
- UINT32 ulTimeSinceStart = 0;
- UINT32 ulCurrentPlayPos = 0;
- UINT32 ulInfoLen = 0;
- // action Name is a must
- if (!pAction)
- {
- return HXR_FAILED;
- }
- if (!m_pLogInfoList)
- {
- return HXR_UNEXPECTED;
- }
- // If we've already reached our limit, don't add any more info.
- if (m_ulLogInfoLength > MAX_LOGINFO_LENGTH)
- {
- return HXR_OK;
- }
- ulTimeSinceStart = CALCULATE_ELAPSED_TICKS(m_ulSourceStartTime, HX_GET_TICKCOUNT());
- // XXXCP: during auto-config there is no m_pPlayer
- if (m_pPlayer)
- ulCurrentPlayPos = m_pPlayer->GetInternalCurrentPlayTime();
- // 10 chars for max UINT32, 1 for |, 1 for last