sip-to-webrtc

pion和freeswitch通讯

用golang编写webrtc客户端负责和freeswitch通讯,支持呼入呼出

Softphone代码如下

支持注册和取消注册

func (softphone *Softphone) register() {
	url := url.URL{Scheme: strings.ToLower(softphone.sipInfo.Transport), Host: softphone.sipInfo.OutboundProxy, Path: "/sip/"}
	dialer := websocket.DefaultDialer
	dialer.Subprotocols = []string{"sip"}
	dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint

	header := http.Header{}
	header.Set("origin", "http://192.168.1.1")

	conn, _, err := dialer.Dial(url.String(), header)
	if err != nil {
		log.Fatal(err)
	}

	softphone.wsConn = conn

	go func() {
		for {
			messageType, bytes, err := conn.ReadMessage()
			if err != nil {
				log.Fatal(err)
			}

			switch messageType {
			case websocket.TextMessage:
				fmt.Println("TextMessage")
			case websocket.BinaryMessage: //二进制数据
				fmt.Println("BinaryMessage")
			case websocket.CloseMessage: //关闭
				fmt.Println("CloseMessage")
				break
			case websocket.PingMessage: //Ping
				fmt.Println("PingMessage")
				break
			case websocket.PongMessage: //Pong
				fmt.Println("PongMessage")
				break
			}

			message := string(bytes)
			log.Print("↓↓↓\n", message)

			for _, ml := range softphone.messageListeners {
				go ml(message)
			}
		}
	}()

	sipMessage := SIPMessage{}
	sipMessage.method = "REGISTER"
	sipMessage.address = softphone.sipInfo.Domain
	sipMessage.headers = make(map[string]string)
	sipMessage.headers["Contact"] = fmt.Sprintf("<sip:%s;transport=ws>;expires=200", softphone.FakeEmail)
	sipMessage.headers["Via"] = fmt.Sprintf("SIP/2.0/WS %s;branch=%s", softphone.fakeDomain, branch())
	sipMessage.headers["From"] = fmt.Sprintf("<sip:%s@%s>;tag=%s", softphone.sipInfo.Username, softphone.sipInfo.Domain, softphone.fromTag)
	sipMessage.headers["To"] = fmt.Sprintf("<sip:%s@%s>", softphone.sipInfo.Username, softphone.sipInfo.Domain)
	sipMessage.headers["Organization"] = "ACE MEDIAS TOOLS"
	sipMessage.headers["Supported"] = "path,ice"
	sipMessage.addCseq(softphone).addCallID(*softphone).addUserAgent()

	//registered, registeredFunc := context.WithCancel(context.Background())

	softphone.request(sipMessage, func(message string) bool {
		authenticateHeader := SIPMessage{}.FromString(message).headers["WWW-Authenticate"]
		regex := regexp.MustCompile(`, nonce="(.+?)"`)
		nonce := regex.FindStringSubmatch(authenticateHeader)[1]

		sipMessage.addAuthorization(*softphone, nonce, "REGISTER").addCseq(softphone).newViaBranch()
		softphone.request(sipMessage, func(msg string) bool {
			if strings.HasPrefix(msg, "SIP/2.0 200 OK") {
				registerSuccessHeader := SIPMessage{}.FromString(msg).headers["Contact"]
				softphone.Contact = registerSuccessHeader
				softphone.Registerd <- true
			}
			//registeredFunc()
			return false
		})

		return true
	})

	//<-registered.Done()
}

func (softphone *Softphone) Unregister() {
	sipMessage := SIPMessage{}
	sipMessage.method = "REGISTER"
	sipMessage.address = softphone.sipInfo.Domain
	sipMessage.headers = make(map[string]string)
	sipMessage.headers["Contact"] = fmt.Sprintf("<sip:%s;transport=ws>;expires=0", softphone.FakeEmail)
	sipMessage.headers["Via"] = fmt.Sprintf("SIP/2.0/WS %s;branch=%s", softphone.fakeDomain, branch())
	sipMessage.headers["From"] = fmt.Sprintf("<sip:%s@%s>;tag=%s", softphone.sipInfo.Username, softphone.sipInfo.Domain, softphone.fromTag)
	sipMessage.headers["To"] = fmt.Sprintf("<sip:%s@%s>", softphone.sipInfo.Username, softphone.sipInfo.Domain)
	sipMessage.headers["Organization"] = "ACE MEDIAS TOOLS"
	sipMessage.headers["Supported"] = "path,ice"
	sipMessage.addCseq(softphone).addCallID(*softphone).addUserAgent()

	registered, registeredFunc := context.WithCancel(context.Background())

	softphone.request(sipMessage, func(message string) bool {
		authenticateHeader := SIPMessage{}.FromString(message).headers["WWW-Authenticate"]
		regex := regexp.MustCompile(`, nonce="(.+?)"`)
		nonce := regex.FindStringSubmatch(authenticateHeader)[1]

		sipMessage.addAuthorization(*softphone, nonce, "REGISTER").addCseq(softphone).newViaBranch()
		softphone.request(sipMessage, func(msg string) bool {
			if strings.HasPrefix(msg, "SIP/2.0 200 OK") {
				registeredFunc()
			}
			return true
		})

		return true
	})

	<-registered.Done()
}

外拨和应答

func (softphone *Softphone) Invite(extension, offer string) {
	sipMessage := SIPMessage{headers: map[string]string{}}

	sipMessage.method = "INVITE"
	sipMessage.address = softphone.sipInfo.Domain

	sipMessage.headers["Contact"] = fmt.Sprintf("<sip:%s;transport=ws>;expires=200", softphone.FakeEmail)
	sipMessage.headers["To"] = fmt.Sprintf("<sip:%s@%s>", extension, softphone.sipInfo.Domain)
	sipMessage.headers["Via"] = fmt.Sprintf("SIP/2.0/WS %s;branch=%s", softphone.fakeDomain, branch())
	sipMessage.headers["From"] = fmt.Sprintf("<sip:%s@%s>;tag=%s", softphone.sipInfo.Username, softphone.sipInfo.Domain, softphone.fromTag)
	sipMessage.headers["Supported"] = "replaces, outbound,ice"
	sipMessage.addCseq(softphone).addCallID(*softphone).addUserAgent()

	sipMessage.headers["Content-Type"] = "application/sdp"
	sipMessage.Body = offer

	softphone.State = Dial

	invited, invitedFunc := context.WithCancel(context.Background())

	softphone.request(sipMessage, func(message string) bool {
		//authenticateHeader := SIPMessage{}.FromString(message).headers["Proxy-Authenticate"]
		//regex := regexp.MustCompile(`, nonce="(.+?)"`)
		//nonce := regex.FindStringSubmatch(authenticateHeader)[1]

		//sipMessage.addProxyAuthorization(*softphone, nonce, extension, "INVITE").addCseq(softphone).newViaBranch()
		//softphone.request(sipMessage, func(msg string) bool {
		//	return false
		//})

		if strings.HasPrefix(message, "SIP/2.0 100 Trying") {
			return false
		} else if strings.HasPrefix(message, "SIP/2.0 200 OK") {
			//回复ACK
			parsed := SIPMessage{}.FromString(message)
			vias := strings.Split(parsed.headers["Via"], ";")
			contacts := strings.Split(parsed.headers["Contact"], " ")

			sipMessage := SIPMessage{headers: map[string]string{}}

			sipMessage.method = "ACK"
			sipMessage.address = strings.Replace(strings.Replace(contacts[0], "<", "", -1), ">", "", -1)

			sipMessage.headers["To"] = parsed.headers["To"]
			sipMessage.headers["Via"] = fmt.Sprintf("%s;branch=%s;%s;%s", vias[0], branch(), vias[2], vias[3])
			sipMessage.headers["From"] = parsed.headers["From"]
			sipMessage.headers["CSeq"] = strings.Replace(parsed.headers["CSeq"], "INVITE", "ACK", -1)
			sipMessage.headers["Call-ID"] = parsed.headers["Call-ID"]

			softphone.State = Busy

			softphone.request(sipMessage, func(msg string) bool {
				invitedFunc()
				return false
			})

			return true
		} else {
			return true
		}

	})

	<-invited.Done()
}

// OnOK adds a handler that responds to any outbound ok events.
func (softphone *Softphone) OnOK(hdlr func(string)) {
	softphone.addMessageListener(func(message string) {
		if strings.HasPrefix(message, "SIP/2.0 200 OK") {
			parsed := SIPMessage{}.FromString(message)
			hdlr(parsed.Body)
		}
	})
}

呼入

// CloseToInvite removes the previously set invite listener.
func (softphone *Softphone) CloseToInvite() {
	softphone.removeMessageListener(softphone.inviteKey)
}

//inbound call
func (softphone *Softphone) OnInvite(hdlr func(string)) {
	softphone.addMessageListener(func(message string) {
		if strings.HasPrefix(message, "INVITE sip:") {
			parsed := SIPMessage{}.FromString(message)
			softphone.InboundInviteMessage = message
			hdlr(parsed.Body)
		}
	})
}

//answer inbound call
func (softphone *Softphone) InOK(answer string) {
	sipMessage := SIPMessage{}.FromString(softphone.InboundInviteMessage)
	dict0 := map[string]string{
		//"Contact":   fmt.Sprintf(`<sip:%s;transport=ws>`, softphone.fakeDomain),
		"Contact":   softphone.Contact,
		"Supported": "outbound",
	}
	response_180Msg := sipMessage.Response(*softphone, 180, dict0, "")
	softphone.response(response_180Msg)

	time.Sleep(2000)

	//sipMessage.headers["Supported"] = "replaces, 100rel, timer, norefersub"
	//sipMessage.headers["Content-Type"] = "application/sdp"
	//sipMessage.addCseq(softphone).addCallID(*softphone).addUserAgent()
	//sipMessage.Body = fmt.Sprintf(`<Msg><Hdr SID="%s" Req="%s" From="%s" To="%s" Cmd="17"/><Bdy Cln="%s"/></Msg>`, msg.Hdr.SID, msg.Hdr.Req, msg.Hdr.To, msg.Hdr.From, softphone.sipInfo.AuthorizationID)

	dict1 := map[string]string{
		//"Contact":      fmt.Sprintf(`<sip:%s;transport=ws>`, softphone.fakeDomain),
		"Contact":      softphone.Contact,
		"Supported":    "replaces, outbound,ice",
		"Content-Type": "application/sdp",
	}
	response_200Msg := sipMessage.Response(*softphone, 200, dict1, answer)
	softphone.response(response_200Msg)
}

main

func main() {
	flag.Parse()

	if *host == "" || *port == "" || *password == "" {
		panic("-host -port and -password are required")
	}

	sigs := make(chan os.Signal, 1)
	done := make(chan bool, 1)

	//设置要接收的信号
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	conn := softphone.NewSoftPhone(softphone.SIPInfoResponse{
		Username:        *username,
		AuthorizationID: *username,
		Password:        *password,
		Domain:          *host,
		Transport:       "ws",
		OutboundProxy:   *host + ":" + *port,
	})

	register := <-conn.Registerd
	if !register {
		panic("not register")
	}

	conn.OnBye()

	conn.OnInvite(func(okBody string) {
		if conn.State != softphone.Idle {
			return
		}

		conn.State = softphone.Ring

		// Prepare the configuration
		config := webrtc.Configuration{
			ICEServers: []webrtc.ICEServer{
				{
					URLs: []string{"stun:172.22.13.231:16384"},
				},
			},
		}

		pc, err := webrtc.NewPeerConnection(config)
		if err != nil {
			panic(err)
		}

		// If PeerConnection is closed remove it from global list
		pc.OnConnectionStateChange(func(p webrtc.PeerConnectionState) {
			switch p {
			case webrtc.PeerConnectionStateFailed:
				if err := pc.Close(); err != nil {
					log.Print(err)
				}
				conn.SetPeerConnection(nil)
			case webrtc.PeerConnectionStateClosed:
				conn.SetPeerConnection(nil)
			case webrtc.PeerConnectionStateConnected:
				conn.SetPeerConnection(pc)
			}
		})

		pc.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
			fmt.Printf("Connection State has changed %s \n", connectionState.String())
			if connectionState == webrtc.ICEConnectionStateDisconnected {
				conn.SetPeerConnection(nil)
			}
		})

		if _, err = pc.AddTransceiverFromKind(webrtc.RTPCodecTypeAudio); err != nil {
			panic(err)
		}

		pc.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
			//codec := track.Codec()
			//fmt.Println("Got Opus track, saving to disk as inbound.ogg", codec.MimeType)
			//if codec.MimeType == "audio/opus" {
			fmt.Println("Got Opus track, saving to disk as inbound.ogg")
			//i, oggNewErr := oggwriter.New("inbound.ogg", codec.ClockRate, codec.Channels)

			//i, oggNewErr := oggwriter.New("inbound.ogg", 48000, 1)
			//if oggNewErr != nil {
			//	panic(oggNewErr)
			//}
			//saveToDisk(i, track)
			//}
		})

		if !strings.Contains(okBody, "a=sendonly") &&
			!strings.Contains(okBody, "a=recvonly") &&
			!strings.Contains(okBody, "a=sendrecv") &&
			!strings.Contains(okBody, "a=inactive") {
			okBody += "a=sendrecv\r\n"
		}

		if !strings.Contains(okBody, "a=mid:0") {
			okBody += "a=mid:0\r\n"
		}

		if err := pc.SetRemoteDescription(webrtc.SessionDescription{Type: webrtc.SDPTypeOffer, SDP: okBody}); err != nil {
			panic(err)
		}

		// Create an answer
		answer, err := pc.CreateAnswer(nil)
		if err != nil {
			panic(err)
		}

		if err := pc.SetLocalDescription(answer); err != nil {
			panic(err)
		}

		conn.InOK(rewriteSDP(answer.SDP))
	})

	go func() {
		<-sigs
		conn.Unregister()
		done <- true
	}()

	<-done
	fmt.Println("进程被终止")
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值