You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lal/app/demo/pullrtsp2pushrtsp/pullrtsp2pushrtsp.go

202 lines
5.5 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package main
import (
"flag"
"fmt"
"os"
"sync"
"time"
"github.com/q191201771/naza/pkg/nazaerrors"
"github.com/q191201771/naza/pkg/unique"
"github.com/q191201771/lal/pkg/sdp"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/rtprtcp"
"github.com/q191201771/lal/pkg/rtsp"
"github.com/q191201771/naza/pkg/nazalog"
)
type RtspTunnel struct {
pullUrl string
pushUrl string
pullOverTcp bool
pushOverTcp bool
uniqueKey string
rtpPacketChan chan rtprtcp.RtpPacket
disposeOnce sync.Once
waitChan chan error
pullSession *rtsp.PullSession
pushSession *rtsp.PushSession
}
func NewRtspTunnel(pullUrl string, pushUrl string, pullOverTcp bool, pushOverTcp bool) *RtspTunnel {
uniqueKey := unique.GenUniqueKey("RTSPTUNNEL")
nazalog.Debugf("[%s] lifecycle new RtspTunnel. pullUrl=%s, pushUrl=%s", uniqueKey, pullUrl, pushUrl)
return &RtspTunnel{
pullUrl: pullUrl,
pushUrl: pushUrl,
pullOverTcp: pullOverTcp,
pushOverTcp: pushOverTcp,
uniqueKey: uniqueKey,
rtpPacketChan: make(chan rtprtcp.RtpPacket, 1024),
waitChan: make(chan error, 1),
}
}
// Start 开启任务,阻塞直到任务开启成功或失败
//
// @return true 表示任务启动成功,此时数据已经在后台转发
// false 表示任务启动失败
//
func (r *RtspTunnel) Start() error {
r.pullSession = rtsp.NewPullSession(r, func(option *rtsp.PullSessionOption) {
option.PullTimeoutMs = 5000
option.OverTcp = r.pullOverTcp
})
if err := r.pullSession.Pull(r.pullUrl); err != nil {
nazalog.Errorf("[%s] start pull failed. err=%+v, url=%s", r.uniqueKey, err, r.pullUrl)
return err
}
sdpCtx := r.pullSession.GetSdp()
nazalog.Debugf("[%s] start pull succ. sdp=%s", r.uniqueKey, string(sdpCtx.RawSdp))
r.pushSession = rtsp.NewPushSession(func(option *rtsp.PushSessionOption) {
option.PushTimeoutMs = 5000
option.OverTcp = r.pushOverTcp
})
if err := r.pushSession.Push(r.pushUrl, sdpCtx); err != nil {
nazalog.Errorf("[%s] start push failed. err=%+v, url=%s", r.uniqueKey, err, r.pushUrl)
return err
}
nazalog.Debugf("[%s] start push succ.", r.uniqueKey)
go func() {
t := time.NewTicker(1 * time.Second)
defer t.Stop()
for {
select {
case pkt := <-r.rtpPacketChan:
_ = r.pushSession.WriteRtpPacket(pkt)
case err := <-r.pullSession.WaitChan():
nazalog.Debugf("[%s] < pullSession.Wait(). err=%+v", r.uniqueKey, err)
_ = r.dispose(err)
return
case err := <-r.pushSession.WaitChan():
nazalog.Debugf("[%s] < pushSession.Wait(). err=%+v", r.uniqueKey, err)
_ = r.dispose(err)
return
case <-t.C:
r.pullSession.UpdateStat(1)
pullStat := r.pullSession.GetStat()
r.pushSession.UpdateStat(1)
pushStat := r.pushSession.GetStat()
nazalog.Debugf("[%s] stat. pull=%+v, push=%+v", r.uniqueKey, pullStat, pushStat)
}
}
}()
return nil
}
// Dispose 主动关闭tunnel时调用
//
// 注意,只有 Start 成功后的tunnel才能调用否则行为未定义
//
// 更详细的说明参考 IClientSessionLifecycle interface
//
func (r *RtspTunnel) Dispose() error {
return r.dispose(nil)
}
// WaitChan Start 成功后可使用这个channel来接收tunnel结束的消息
//
// 更详细的说明参考 IClientSessionLifecycle interface
//
func (r *RtspTunnel) WaitChan() chan error {
return r.waitChan
}
// ---------------------------------------------------------------------------------------------------------------------
func (r *RtspTunnel) OnRtpPacket(pkt rtprtcp.RtpPacket) {
r.rtpPacketChan <- pkt
}
func (r *RtspTunnel) OnSdp(sdpCtx sdp.LogicContext) {
// noop
}
func (r *RtspTunnel) OnAvPacket(pkt base.AvPacket) {
// noop
}
func (r *RtspTunnel) dispose(err error) error {
var retErr error
r.disposeOnce.Do(func() {
nazalog.Infof("[%s] lifecycle dispose RtspTunnel.", r.uniqueKey)
e1 := r.pullSession.Dispose()
e2 := r.pushSession.Dispose()
retErr = nazaerrors.CombineErrors(e1, e2)
r.waitChan <- err
})
return retErr
}
// ---------------------------------------------------------------------------------------------------------------------
func main() {
_ = nazalog.Init(func(option *nazalog.Option) {
option.AssertBehavior = nazalog.AssertFatal
})
defer nazalog.Sync()
base.LogoutStartInfo()
inUrl, outUrl, pullOverTcp, pushOverTcp := parseFlag()
rtspTunnel := NewRtspTunnel(inUrl, outUrl, pullOverTcp == 1, pushOverTcp == 1)
err := rtspTunnel.Start()
if err != nil {
nazalog.Errorf("start tunnel failed. err=%+v", err)
return
}
//go func() {
// time.Sleep(5 * time.Second)
// _ = rtspTunnel.Dispose()
//}()
err = <-rtspTunnel.WaitChan()
nazalog.Errorf("tunnel stopped. err=%+v", err)
}
func parseFlag() (inUrl string, outUrl string, pullOverTcp int, pushOverTcp int) {
i := flag.String("i", "", "specify pull rtsp url")
o := flag.String("o", "", "specify push rtsp url")
t := flag.Int("t", 0, "specify pull interleaved mode(rtp/rtcp over tcp)")
y := flag.Int("y", 0, "specify push interleaved mode(rtp/rtcp over tcp)")
flag.Parse()
if *i == "" || *o == "" {
flag.Usage()
_, _ = fmt.Fprintf(os.Stderr, `Example:
%s -i rtsp://localhost:5544/live/test110 -o rtsp://localhost:5544/live/test220
%s -i rtsp://localhost:5544/live/test110 -o rtsp://localhost:5544/live/test220 -t 1 -y 1
`, os.Args[0], os.Args[0])
base.OsExitAndWaitPressIfWindows(1)
}
return *i, *o, *t, *y
}