|  | 
|  | 1 | +// SPDX-FileCopyrightText: 2025 The Pion community <https://pion.ly> | 
|  | 2 | +// SPDX-License-Identifier: MIT | 
|  | 3 | + | 
|  | 4 | +// Package main demonstrates the ContinualGatheringPolicy feature | 
|  | 5 | +package main | 
|  | 6 | + | 
|  | 7 | +import ( | 
|  | 8 | +	"context" | 
|  | 9 | +	"flag" | 
|  | 10 | +	"fmt" | 
|  | 11 | +	"log" | 
|  | 12 | +	"os" | 
|  | 13 | +	"os/signal" | 
|  | 14 | +	"syscall" | 
|  | 15 | +	"time" | 
|  | 16 | + | 
|  | 17 | +	"github.com/pion/ice/v4" | 
|  | 18 | +	"github.com/pion/logging" | 
|  | 19 | +) | 
|  | 20 | + | 
|  | 21 | +func main() { | 
|  | 22 | +	var gatheringMode string | 
|  | 23 | +	var monitorInterval time.Duration | 
|  | 24 | + | 
|  | 25 | +	flag.StringVar(&gatheringMode, "mode", "continually", "Gathering mode: 'once' or 'continually'") | 
|  | 26 | +	flag.DurationVar(&monitorInterval, "interval", 2*time.Second, "Network monitoring interval (for continual mode)") | 
|  | 27 | +	flag.Parse() | 
|  | 28 | + | 
|  | 29 | +	// Determine gathering policy | 
|  | 30 | +	var policy ice.ContinualGatheringPolicy | 
|  | 31 | +	switch gatheringMode { | 
|  | 32 | +	case "once": | 
|  | 33 | +		policy = ice.GatherOnce | 
|  | 34 | +		fmt.Println("Using GatherOnce policy - gathering will complete after initial collection") | 
|  | 35 | +	case "continually": | 
|  | 36 | +		policy = ice.GatherContinually | 
|  | 37 | +		fmt.Printf("Using GatherContinually policy - monitoring for network changes every %v\n", monitorInterval) | 
|  | 38 | +	default: | 
|  | 39 | +		log.Fatalf("Invalid mode: %s. Use 'once' or 'continually'", gatheringMode) | 
|  | 40 | +	} | 
|  | 41 | + | 
|  | 42 | +	// Create logger | 
|  | 43 | +	loggerFactory := logging.NewDefaultLoggerFactory() | 
|  | 44 | +	loggerFactory.DefaultLogLevel = logging.LogLevelDebug | 
|  | 45 | + | 
|  | 46 | +	// Create ICE agent with the specified gathering policy using AgentOptions | 
|  | 47 | +	agent, err := ice.NewAgentWithOptions( | 
|  | 48 | +		ice.WithNetworkTypes([]ice.NetworkType{ice.NetworkTypeUDP4, ice.NetworkTypeUDP6}), | 
|  | 49 | +		ice.WithCandidateTypes([]ice.CandidateType{ice.CandidateTypeHost}), | 
|  | 50 | +		ice.WithContinualGatheringPolicy(policy), | 
|  | 51 | +		ice.WithNetworkMonitorInterval(monitorInterval), | 
|  | 52 | +	) | 
|  | 53 | +	if err != nil { | 
|  | 54 | +		log.Fatalf("Failed to create agent: %v", err) | 
|  | 55 | +	} | 
|  | 56 | +	defer func() { | 
|  | 57 | +		if err := agent.Close(); err != nil { | 
|  | 58 | +			log.Printf("Failed to close agent: %v", err) | 
|  | 59 | +		} | 
|  | 60 | +	}() | 
|  | 61 | + | 
|  | 62 | +	// Track candidates | 
|  | 63 | +	candidateCount := 0 | 
|  | 64 | +	candidateMap := make(map[string]ice.Candidate) | 
|  | 65 | + | 
|  | 66 | +	// Set up candidate handler | 
|  | 67 | +	err = agent.OnCandidate(func(c ice.Candidate) { | 
|  | 68 | +		if c == nil { | 
|  | 69 | +			if policy == ice.GatherOnce { | 
|  | 70 | +				fmt.Println("\n=== Gathering completed (no more candidates) ===") | 
|  | 71 | +			} | 
|  | 72 | +			return | 
|  | 73 | +		} | 
|  | 74 | + | 
|  | 75 | +		candidateCount++ | 
|  | 76 | +		candidateID := c.String() | 
|  | 77 | + | 
|  | 78 | +		if _, exists := candidateMap[candidateID]; !exists { | 
|  | 79 | +			candidateMap[candidateID] = c | 
|  | 80 | +			fmt.Printf("[%s] Candidate #%d: %s\n", time.Now().Format("15:04:05"), candidateCount, c) | 
|  | 81 | +		} | 
|  | 82 | +	}) | 
|  | 83 | +	if err != nil { | 
|  | 84 | +		log.Fatalf("Failed to set candidate handler: %v", err) | 
|  | 85 | +	} | 
|  | 86 | + | 
|  | 87 | +	// Start gathering | 
|  | 88 | +	fmt.Println("\n=== Starting candidate gathering ===") | 
|  | 89 | +	err = agent.GatherCandidates() | 
|  | 90 | +	if err != nil { | 
|  | 91 | +		log.Fatalf("Failed to start gathering: %v", err) | 
|  | 92 | +	} | 
|  | 93 | + | 
|  | 94 | +	// Set up signal handling for graceful shutdown | 
|  | 95 | +	sigChan := make(chan os.Signal, 1) | 
|  | 96 | +	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) | 
|  | 97 | + | 
|  | 98 | +	// Create a context for periodic status checks | 
|  | 99 | +	ctx, cancel := context.WithCancel(context.Background()) | 
|  | 100 | +	defer cancel() | 
|  | 101 | + | 
|  | 102 | +	// Periodically check and display gathering state | 
|  | 103 | +	go func() { | 
|  | 104 | +		ticker := time.NewTicker(5 * time.Second) | 
|  | 105 | +		defer ticker.Stop() | 
|  | 106 | + | 
|  | 107 | +		for { | 
|  | 108 | +			select { | 
|  | 109 | +			case <-ctx.Done(): | 
|  | 110 | +				return | 
|  | 111 | +			case <-ticker.C: | 
|  | 112 | +				state, err := agent.GetGatheringState() | 
|  | 113 | +				if err != nil { | 
|  | 114 | +					log.Printf("Failed to get gathering state: %v", err) | 
|  | 115 | +					continue | 
|  | 116 | +				} | 
|  | 117 | + | 
|  | 118 | +				localCandidates, err := agent.GetLocalCandidates() | 
|  | 119 | +				if err != nil { | 
|  | 120 | +					log.Printf("Failed to get local candidates: %v", err) | 
|  | 121 | +					continue | 
|  | 122 | +				} | 
|  | 123 | + | 
|  | 124 | +				fmt.Printf("\n[%s] Status: GatheringState=%s, Candidates=%d\n", | 
|  | 125 | +					time.Now().Format("15:04:05"), state, len(localCandidates)) | 
|  | 126 | + | 
|  | 127 | +				if policy == ice.GatherContinually { | 
|  | 128 | +					fmt.Println("Tip: Try changing network interfaces (connect/disconnect WiFi, enable/disable network adapters)") | 
|  | 129 | +					fmt.Println("     New candidates will be discovered automatically!") | 
|  | 130 | +				} | 
|  | 131 | +			} | 
|  | 132 | +		} | 
|  | 133 | +	}() | 
|  | 134 | + | 
|  | 135 | +	// Wait for interrupt signal | 
|  | 136 | +	fmt.Println("\nPress Ctrl+C to exit...") | 
|  | 137 | +	<-sigChan | 
|  | 138 | + | 
|  | 139 | +	fmt.Println("\n=== Shutting down ===") | 
|  | 140 | +	cancel() | 
|  | 141 | + | 
|  | 142 | +	// Display final statistics | 
|  | 143 | +	state, _ := agent.GetGatheringState() | 
|  | 144 | +	localCandidates, _ := agent.GetLocalCandidates() | 
|  | 145 | + | 
|  | 146 | +	fmt.Printf("\nFinal Statistics:\n") | 
|  | 147 | +	fmt.Printf("  Gathering Policy: %s\n", policy) | 
|  | 148 | +	fmt.Printf("  Gathering State: %s\n", state) | 
|  | 149 | +	fmt.Printf("  Total Candidates Discovered: %d\n", candidateCount) | 
|  | 150 | +	fmt.Printf("  Unique Candidates: %d\n", len(candidateMap)) | 
|  | 151 | +	fmt.Printf("  Current Active Candidates: %d\n", len(localCandidates)) | 
|  | 152 | +} | 
0 commit comments