#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "group/GroupInstanceCustomImpl.h" #include "group/GroupInstanceImpl.h" #include "group/GroupInstanceReferenceImpl.h" #include "FakeAudioDeviceModule.h" #include "StaticThreads.h" #include "AudioFrame.h" #include "fake_video_source.h" #include "fake_video_sink.h" // CGo header #include "tools/go_sfu/go_sfu.h" // --------------------------------------------------------------------------- // Logging helpers // --------------------------------------------------------------------------- extern std::chrono::steady_clock::time_point gGroupStartTime; extern std::atomic gGroupQuiet; double groupElapsed(); void groupLog(const char* tag, const char* fmt, ...); // --------------------------------------------------------------------------- // GroupSineRecorder - generates 440 Hz sine tone // --------------------------------------------------------------------------- class GroupSineRecorder : public tgcalls::FakeAudioDeviceModule::Recorder { public: GroupSineRecorder(); tgcalls::AudioFrame Record() override; int32_t WaitForUs() override; private: static constexpr size_t kSampleRate = 48000; static constexpr size_t kChannels = 2; static constexpr size_t kFrameSamples = 480; static constexpr double kFrequency = 440.0; static constexpr double kAmplitude = 3000.0; std::vector buffer_; uint64_t phase_ = 0; }; // --------------------------------------------------------------------------- // GroupNoOpRenderer - discards received audio // --------------------------------------------------------------------------- class GroupNoOpRenderer : public tgcalls::FakeAudioDeviceModule::Renderer { public: bool Render(const tgcalls::AudioFrame&) override; }; // --------------------------------------------------------------------------- // SimpleRequestMediaChannelDescriptionTask // --------------------------------------------------------------------------- class SimpleRequestMediaChannelDescriptionTask : public tgcalls::RequestMediaChannelDescriptionTask { public: void cancel() override; }; // --------------------------------------------------------------------------- // ParticipantState // --------------------------------------------------------------------------- struct ParticipantState { int id; bool isReference; std::unique_ptr instance; std::atomic connected{false}; std::atomic wasConnected{false}; std::atomic receivedAudio{false}; uint32_t audioSsrc{0}; std::string logPath; // Video fields std::string endpointId; rtc::scoped_refptr videoSource; std::mutex videoSinksMutex; std::map> videoSinks; }; // --------------------------------------------------------------------------- // GroupValidationResult // --------------------------------------------------------------------------- struct GroupValidationResult { int totalParticipants; int connectedCount; int audioReceivedCount; int videoReceivedPairs; int videoExpectedPairs; bool success; }; // --------------------------------------------------------------------------- // Participant lifecycle functions // --------------------------------------------------------------------------- // Creates a fully initialized participant: builds descriptor, creates instance, // joins SFU, sets join response, unmutes. Returns nullptr on failure. std::unique_ptr createParticipant( int id, bool isReference, GoInt sfuHandle, std::shared_ptr threads, bool quiet, bool video, std::vector>* allStates ); // Clean teardown: GoSfu_Leave, stop video, stop instance, reset. void stopParticipant(ParticipantState* state, GoInt sfuHandle); // Validates group state: connection, audio, video. Returns result struct. GroupValidationResult validateGroupState( const std::vector>& states, bool video ); // Prints a group call summary to stdout. Returns the success boolean. bool printGroupSummary( int customParticipants, int referenceParticipants, int duration, bool video, const GroupValidationResult& result, bool anyFailed );