252 lines
7.7 KiB
C++
252 lines
7.7 KiB
C++
//========= Copyright (c), Valve Corporation, All rights reserved. ============//
|
|
|
|
#ifndef HLTV_BROADCAST_PLAYER_HDR
|
|
#define HLTV_BROADCAST_PLAYER_HDR
|
|
|
|
|
|
#include "tier1/utlincrementalvector.h"
|
|
#include "steam/steam_api.h"
|
|
#include "steam/isteamhttp.h"
|
|
#include "tier1/utlhashtable.h"
|
|
#include "tier1/smartptr.h"
|
|
#include "demostream.h"
|
|
#include "demofile/gotvhttpstream.h"
|
|
|
|
struct HTTPRequestCompleted_t;
|
|
|
|
class CDemoStreamHttp: public IDemoStream
|
|
{
|
|
public:
|
|
CDemoStreamHttp();
|
|
void SetClient( IDemoStreamClient *pClient ) { m_pClient = pClient; }
|
|
|
|
struct SyncParams_t
|
|
{
|
|
SyncParams_t(int nStartFragment = 0) :m_nStartFragment( nStartFragment ){}
|
|
int m_nStartFragment;
|
|
void PrintSyncRequest(char *buffer, int nBufSize ) const;
|
|
};
|
|
|
|
void StartStreaming( const char *pUrl, SyncParams_t syncParams = SyncParams_t() );
|
|
void StartStreamingCached( const char *pUrl, int nFragment = 1);
|
|
|
|
bool PrepareForStreaming( const char * pUrl );
|
|
|
|
void SendSync( int nResync = 0 );
|
|
bool OnEngineGotvSyncPacket( const CEngineGotvSyncPacket *pPkt );
|
|
|
|
void StopStreaming();
|
|
bool IsIdle()const { return m_nState == STATE_IDLE; }
|
|
void Resync( );
|
|
void Update();
|
|
|
|
virtual const char* GetUrl( void ) OVERRIDE { return m_Url.Get(); }
|
|
virtual float GetTicksPerSecond( void )OVERRIDE { return m_SyncResponse.flTicksPerSecond; }
|
|
int GetDemoProtocol()const { return m_nDemoProtocol; }
|
|
virtual float GetTicksPerFrame( void ) OVERRIDE { return 1.0f; } // 1 network frame per 1 tick in broadcast - there's not much reason to do otherwise
|
|
virtual void Close() OVERRIDE { StopStreaming(); }
|
|
virtual int GetTotalTicks( void ) { return 0; }
|
|
|
|
enum FragmentTypeEnum_t
|
|
{
|
|
//FRAGMENT_START,
|
|
FRAGMENT_DELTA,
|
|
FRAGMENT_FULL,
|
|
FRAGMENT_COUNT
|
|
};
|
|
static const char *FragmentTypeToString( FragmentTypeEnum_t nType );
|
|
static const char *AsString( FragmentTypeEnum_t nType ){ return nType == FRAGMENT_DELTA ? "delta" : "full"; }
|
|
struct Buffer_t
|
|
{
|
|
Buffer_t()
|
|
{
|
|
m_nRefCount = 0;
|
|
m_nSize = 0;
|
|
}
|
|
|
|
static void AddRef( Buffer_t *pObj )
|
|
{
|
|
pObj->m_nRefCount++;
|
|
}
|
|
|
|
static void Release( Buffer_t *pObj )
|
|
{
|
|
if ( 0 == --pObj->m_nRefCount )
|
|
{
|
|
delete[]( uint8* )pObj;
|
|
}
|
|
}
|
|
|
|
uint m_nRefCount;
|
|
uint m_nSize;
|
|
uint8 *Base() { return ( uint8* )( this + 1 ); }
|
|
uint8 *End() { return Base() + m_nSize; }
|
|
};
|
|
typedef CSmartPtr< Buffer_t, Buffer_t > BufferRef;
|
|
Buffer_t *GetStreamSignupBuffer() { return m_pStreamSignup.GetObject(); }
|
|
Buffer_t *GetFragmentBuffer( int nFragment, FragmentTypeEnum_t nFragmentType );
|
|
void RequestFragment( int nFragment, FragmentTypeEnum_t nType );
|
|
void ReleaseFragment( int nFragment );
|
|
|
|
static GotvHttpStreamId_t GetStreamId( const char *pUrl );
|
|
float GetBroadcastKeyframeInterval() const { return m_flBroadcastKeyframeInterval; }
|
|
|
|
IDemoStreamClient::DemoStreamReference_t GetStreamStartReference( bool bLagCompensation = false );
|
|
|
|
void BeginBuffering( int nFragment );
|
|
protected:
|
|
|
|
struct SyncResponse_t
|
|
{
|
|
int nStartTick ;
|
|
float flKeyframeInterval;
|
|
float flRealTimeDelay ;
|
|
float flReceiveAge;
|
|
int nFragment ;
|
|
int nSignupFragment;
|
|
float flTicksPerSecond;
|
|
double dPlatTimeReceived;
|
|
};
|
|
bool OnSync( int nResync );
|
|
|
|
void OnStart( HTTPRequestHandle hRequest );
|
|
void OnFragmentRequestSuccess( HTTPRequestHandle hRequest, int nFragment, FragmentTypeEnum_t nType );
|
|
void OnFragmentRequestFailure( EHTTPStatusCode nErrorCode, int nFragment, FragmentTypeEnum_t nType );
|
|
|
|
bool OnSync( const char *pBuffer, int nBufferSize, int nResync );
|
|
|
|
void RequestDeltaFrame( int nFragment );
|
|
|
|
|
|
protected:
|
|
|
|
typedef void( CDemoStreamHttp::*FnHttpCallback )( const HTTPRequestCompleted_t * pResponse );
|
|
|
|
class CPendingRequest : public CCallbackBase
|
|
{
|
|
public:
|
|
CPendingRequest( );
|
|
void Init( CDemoStreamHttp *pParent, HTTPRequestHandle hRequest, SteamAPICall_t hCall );
|
|
virtual void Run( void *pvParam ) OVERRIDE; // success; HTTPRequestCompleted_t
|
|
virtual void Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall ) OVERRIDE; // result; HTTPRequestCompleted_t
|
|
void Cancel();
|
|
|
|
virtual void OnSuccess( const HTTPRequestCompleted_t * pResponse ) = 0;
|
|
virtual void OnFailure( const HTTPRequestCompleted_t * pResponse );
|
|
public:
|
|
int m_nIncrementalVectorIndex;
|
|
|
|
protected:
|
|
~CPendingRequest(); // only this object can delete itself
|
|
virtual int GetCallbackSizeBytes() OVERRIDE { return sizeof( HTTPRequestCompleted_t ); }
|
|
CDemoStreamHttp *m_pParent;
|
|
HTTPRequestHandle m_hRequest;
|
|
SteamAPICall_t m_hCall;
|
|
};
|
|
friend class CPendingRequest;
|
|
|
|
class CSyncRequest : public CPendingRequest
|
|
{
|
|
int m_nResync;
|
|
SyncParams_t m_SyncParams;
|
|
public:
|
|
CSyncRequest( SyncParams_t syncParams, int nResync = 0 ) : m_nResync( nResync ), m_SyncParams( syncParams ) { }
|
|
virtual void OnSuccess( const HTTPRequestCompleted_t * pResponse ) OVERRIDE;
|
|
virtual void OnFailure( const HTTPRequestCompleted_t * pResponse ) OVERRIDE;
|
|
};
|
|
|
|
class CStartRequest : public CPendingRequest
|
|
{
|
|
public:
|
|
virtual void OnSuccess( const HTTPRequestCompleted_t * pResponse ) OVERRIDE;
|
|
//virtual void OnFailure( const HTTPRequestCompleted_t * pResponse ) OVERRIDE;
|
|
};
|
|
|
|
|
|
class CFragmentRequest : public CPendingRequest
|
|
{
|
|
int m_nFragment;
|
|
FragmentTypeEnum_t m_nType;
|
|
public:
|
|
CFragmentRequest( int nFragment , FragmentTypeEnum_t nType ) : m_nFragment( nFragment ), m_nType ( nType ){}
|
|
virtual void OnSuccess( const HTTPRequestCompleted_t * pResponse ) OVERRIDE;
|
|
virtual void OnFailure( const HTTPRequestCompleted_t * pResponse ) OVERRIDE;
|
|
};
|
|
|
|
|
|
enum StateEnum_t
|
|
{
|
|
STATE_IDLE,
|
|
STATE_SYNC,
|
|
STATE_RANDOM_WAIT_AND_SYNC,
|
|
STATE_START,
|
|
STATE_STREAMING
|
|
};
|
|
|
|
static Buffer_t *MakeBuffer( HTTPRequestHandle hRequest ); // returns a buffer with 0 refcount
|
|
|
|
struct Fragment_t
|
|
{
|
|
protected:
|
|
Buffer_t *m_pField[ FRAGMENT_COUNT ];
|
|
uint m_nStreaming;
|
|
public:
|
|
|
|
Fragment_t()
|
|
{
|
|
for ( int i = 0; i < FRAGMENT_COUNT; ++i )
|
|
m_pField[ i ] = NULL;
|
|
m_nStreaming = 0;
|
|
}
|
|
|
|
void ResetBuffers();
|
|
|
|
|
|
//const uint8 *Start() const { return pField[ FRAGMENT_START ] ? pField[ FRAGMENT_START ]->Base() : NULL; }
|
|
//uint StartSize()const { return pField[ FRAGMENT_START ] ? pField[ FRAGMENT_START ]->nSize : 0; }
|
|
void SetField( FragmentTypeEnum_t nFragment, Buffer_t *pBuffer );
|
|
Buffer_t *GetField( FragmentTypeEnum_t nFragment ) { return m_pField[ nFragment ]; }
|
|
const uint8 *Delta() const { return m_pField[ FRAGMENT_DELTA ] ? m_pField[ FRAGMENT_DELTA ]->Base() : NULL; }
|
|
uint DeltaSize()const { return m_pField[ FRAGMENT_DELTA ] ? m_pField[ FRAGMENT_DELTA ]->m_nSize : 0; }
|
|
const uint8 *Full() const { return m_pField[ FRAGMENT_FULL ] ? m_pField[ FRAGMENT_FULL ]->Base() : NULL; }
|
|
uint FullSize()const { return m_pField[ FRAGMENT_FULL ] ? m_pField[ FRAGMENT_FULL ]->m_nSize : 0; }
|
|
|
|
uint IsStreaming( FragmentTypeEnum_t nType ){ return ( m_nStreaming >> nType ) & 1; }
|
|
void SetStreaming( FragmentTypeEnum_t nType ){ m_nStreaming |= ( 1 << nType ); }
|
|
void ClearStreaming( FragmentTypeEnum_t nType ){ m_nStreaming &= ~( 1 << nType ); }
|
|
};
|
|
|
|
|
|
protected:
|
|
void SendGet( const char *pPath, CPendingRequest *pRequest );
|
|
Fragment_t &Fragment( int nFragment );
|
|
protected:
|
|
CUtlString m_Url;
|
|
bool m_bSyncFromGc;
|
|
CUtlIncrementalVector< CPendingRequest > m_PendingRequests;
|
|
StateEnum_t m_nState;
|
|
|
|
BufferRef m_pStreamSignup;
|
|
int m_nStreamSignupFragment; // the fragment where the signup/start fragment starts
|
|
|
|
int m_nDemoProtocol;
|
|
IDemoStreamClient *m_pClient;
|
|
|
|
CUtlHashtable< int, Fragment_t, IdentityHashFunctor > m_FragmentCache; // TODO: implement freeing old buffers
|
|
|
|
// STATE_SYNC : when to stop streaming
|
|
// STATE_RANDOM_WAIT_AND_SYNC: when to retry sync
|
|
double m_dSyncTimeoutEnd;
|
|
|
|
SyncParams_t m_SyncParams;
|
|
SyncResponse_t m_SyncResponse;
|
|
|
|
float m_flBroadcastKeyframeInterval;
|
|
|
|
//int m_nFragment;
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif // HLTV_BROADCAST_PLAYER_HDR
|