//========= 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