package rtsp import ( "bufio" "context" "encoding/base64" "fmt" "net" "strings" "time" "git.ilapage.cn/ila/yovision/Sense/server/app/sense/onvif" ) type Credential struct{ Username, Password string } type Result struct { Status string `json:"status"` LatencyMS int64 `json:"latencyMs"` Detail string `json:"detail"` } type Verifier interface { Verify(context.Context, string, Credential) (Result, error) } type NetVerifier struct { Timeout time.Duration Policy onvif.Policy } func (v NetVerifier) Verify(ctx context.Context, raw string, credential Credential) (Result, error) { parsed, ip, err := v.Policy.ValidateURL(ctx, raw, "rtsp") if err != nil { return Result{}, err } port := parsed.Port() if port == "" { port = "554" } timeout := v.Timeout if timeout <= 0 { timeout = 5 * time.Second } started := time.Now() connection, err := (&net.Dialer{Timeout: timeout}).DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port)) if err != nil { return Result{Status: "unreachable", Detail: "无法连接视频端口"}, nil } defer connection.Close() _ = connection.SetDeadline(time.Now().Add(timeout)) authorization := "" if credential.Username != "" { authorization = "Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(credential.Username+":"+credential.Password)) + "\r\n" } request := fmt.Sprintf("OPTIONS %s RTSP/1.0\r\nCSeq: 1\r\nUser-Agent: YoVision-Sense\r\n%s\r\n", parsed.String(), authorization) if _, err = connection.Write([]byte(request)); err != nil { return Result{}, err } line, err := bufio.NewReader(connection).ReadString('\n') if err != nil { return Result{Status: "timeout", Detail: "等待视频响应超时"}, nil } result := Result{Status: "ready", Detail: "码流可访问", LatencyMS: time.Since(started).Milliseconds()} if strings.Contains(line, " 401 ") { result.Status = "authentication_failed" result.Detail = "设备拒绝了当前 RTSP 凭据" } else if !strings.Contains(line, " 200 ") { result.Status = "failed" result.Detail = "设备返回非成功状态" } return result, nil }