//========= Copyright © Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #ifndef HLTVBROADCAST_HDR #define HLTVBROADCAST_HDR #ifdef _WIN32 #pragma once #endif #include //#include "demo.h" #include "broadcast.h" #include "tier0/microprofiler.h" #include "tier1/utlincrementalvector.h" #include "steam/steam_api.h" #include "steam/isteamhttp.h" class CHLTVFrame; class CHLTVServer; class CEngineGotvSyncPacket; // forward declare protobuf message here class CHLTVBroadcast { protected: class CMemoryStream { protected: CUtlMemory< uint8 > m_Buffer; // data chunk that will get uploaded uint m_nCommitted; // the number of bytes in payload buffer that will need to be uploaded uint8 *m_pReserved; // the total number of bytes allocated in payload buffer includes committed portion + reserved portion that is being filled in right now. m_pPayloadReserved == NULL after upload, otherwise we forgot to commit. public: CMemoryStream(); void *Reserve( uint nReserveBytes ); const void *GetReservedBase()const { return m_Buffer.Base() + m_nCommitted; } const void *Base()const { return m_Buffer.Base(); } uint GetReservedSize()const { return m_Buffer.Count() - m_nCommitted; } void Commit( uint nCommitBytes ); void Purge(); bool IsEmpty()const { return m_nCommitted == 0; } uint GetCommitSize()const { return m_nCommitted; } void Reset() { m_nCommitted = 0; } void WriteCmdHeader( unsigned char cmd, int tick, int nPlayerSlot ); }; class CInStreamMsg : public bf_write { protected: CMemoryStream &m_Stream; // int m_nCmd; // int m_nTick; // int m_nPlayerSlot, public: CInStreamMsg( CMemoryStream &stream, const char *pDebugName, uint nReserveSize = 256 * 1024 ) : m_Stream( stream ), bf_write( pDebugName, stream.Reserve(nReserveSize), nReserveSize ) { } ~CInStreamMsg() { m_Stream.Commit( GetNumBytesWritten() ); } }; class CInStreamMsgWithSize : public CInStreamMsg { protected: public: CInStreamMsgWithSize( CMemoryStream &stream, const char *pDebugName, uint nReserveSize = 256 * 1024 ) : CInStreamMsg( stream, pDebugName, nReserveSize ) { this->WriteLong( 0 ); // we'll write this in the end } ~CInStreamMsgWithSize() { // the length of the message, after the initial 4 bytes that contain the length of the message. Binarily compatible with .dem file *( int32* )( this->GetBasePointer() ) = this->GetNumBytesWritten() - sizeof( int32 ); } }; class CHttpCallback : public CCallbackBase { public: CHttpCallback( CHLTVBroadcast *pParent, HTTPRequestHandle hRequest, const char *pResource ); ~CHttpCallback(); virtual void Run( void *pvParam ) OVERRIDE; // success; HTTPRequestCompleted_t virtual void Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall ) OVERRIDE; // result; HTTPRequestCompleted_t void DetachFromParent() { m_pParent = NULL; } void SetProtobufMsgForGCUponSuccess( const CEngineGotvSyncPacket *pProtobufMsgForGCUponSuccess ); public: int m_nIncrementalVectorIndex; protected: virtual int GetCallbackSizeBytes() OVERRIDE { return sizeof( HTTPRequestCompleted_t ); } CHLTVBroadcast *m_pParent; HTTPRequestHandle m_hRequest; CUtlString m_Resource; const CEngineGotvSyncPacket *m_pProtobufMsgForGCUponSuccess; }; public: CHLTVBroadcast( CHLTVServer *pHltvServer ); virtual ~CHLTVBroadcast(); int GetRecordingTick( void ) { return m_nLastWrittenTick/* - m_nStartTick*/; } void OnMasterStarted(); void StartRecording( const char *pBroadcastUrl ); const char *GetUrl()const { return m_Url.Get(); } void SetSignonState( int state ) {}; // not need by HLTV recorder bool IsRecording( void ); void PauseRecording( void ) {}; void ResumeRecording( void ) {}; void StopRecording(); void DumpStats(); void RecordCommand( const char *cmdstring ); void RecordUserInput( int cmdnumber ) {}; // not need by HLTV recorder //void RecordMessages( bf_read &data, int bits ); void RecordServerClasses( CMemoryStream &stream, ServerClass *pClasses ); void RecordStringTables( CMemoryStream &stream ); void ResetDemoInterpolation( void ) {}; public: void WriteFrame( CHLTVFrame *pFrame, bf_write *additionaldata = NULL ); void ResendStartup(); void RecordSnapshot( CHLTVFrame * pFrame, bf_write * additionaldata, bf_write &msg, int nDeltaTick ); void CloseFile(); void Reset(); void WriteServerInfo( CMemoryStream &stream ); void WriteSignonData(); // write all necessary signon data and returns written bytes void WriteMessages( CMemoryStream &stream, unsigned char cmd ); void SendSignonData(); int GetMaxAckTickCount(); void OnHttpRequestFailed(); void OnHttpRequestResetContent(); void OnHttpRequestSuccess(); void Register( CHttpCallback *pCallback ); void Unregister( CHttpCallback *pCallback ); protected: void FlushCollectedStreams(const char *pExtraParams = ""); CHttpCallback * Send( const char* pPath, CMemoryStream &stream ); CHttpCallback * Send( const char* pPath, const void *pBase, uint nSize ); CHttpCallback * LowLevelSend( const CUtlString &path, const void *pBase, uint nSize ); protected: bool m_bIsRecording; int m_nFrameCount; int m_nKeyframeTick; int m_nStartTick; int m_nDeltaTick; int m_nSignonTick; int m_nCurrentTick; int m_nSignonDataAckTick; // when signon data was sent out last int m_nLastWrittenTick; uint64 m_nMasterCookie; CHLTVServer *m_pHltvServer; CMicroProfiler m_mpKeyframe, m_mpFrame, m_mpLowLevelSend; int64 m_nMaxKeyframeTicks, m_nDecayMaxKeyframeTicks, m_nMaxLowLevelSendTicks; int64 m_nKeyframeBytes, m_nDeltaFrameBytes; FileHandle_t m_pFile; // The following fields are exclusive to the HTTP broadcast implementation, they are not needed for writing into file, memory or netchan streams //HTTPRequestHandle m_hHTTPRequestHandle; CMemoryStream m_DeltaStream; // this is being collected every tick, and flushed every so often CMemoryStream m_SignonDataStream; int m_nSignonDataFragment; CUtlString m_Url; float m_flTimeout; int m_nFailedHttpRequests; friend class CHttpCallback; CUtlIncrementalVector< CHttpCallback > m_HttpRequests; // requests in flight int m_nHttpRequestBacklogHighWatermark; int m_nMatchFragmentCounter; float m_flBroadcastKeyframeInterval; }; #endif // HLTVBROADCAST_HDR