The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
darrh00

最简单的 Go 代码实现联通的 iptv 转换为 http 流 实现类似 udpxy 的功能

  •  
  •   darrh00 · Feb 15, 2023 · 2646 views
  •   You need to sign in to view this topic
    This topic created in 1215 days ago, the information mentioned may be changed or developed.

    一直在用 udpxy 看北京联通的 IPTV ,很好奇到底是怎么实现的,

    但是 udpxy 的代码量还是有一些,

    最近花了一晚上,终于用 go 实现了一个最简单的版本,感觉很简洁

    https://gist.github.com/darren/fe449a04bfddbed09ce477e1d33d6562

    package main
    
    import (
    	"flag"
    	"io"
    	"log"
    	"net"
    	"net/http"
    	"os"
    	"strings"
    )
    
    var addr = flag.String("l", ":18000", "Listening address")
    var iface = flag.String("i", "eth0", "Listening multicast interface")
    
    var inf *net.Interface
    
    func handleHTTP(w http.ResponseWriter, req *http.Request) {
    	parts := strings.FieldsFunc(req.URL.Path, func(r rune) bool {
    		return r == '/'
    	})
    
    	if len(parts) < 2 {
    		w.WriteHeader( http.StatusBadRequest)
    		io.WriteString(w, "No address specified")
    		return
    	}
    
    	raddr := parts[1]
    
    	addr, err := net.ResolveUDPAddr("udp4", raddr)
    	if err != nil {
    		w.WriteHeader( http.StatusBadRequest)
    		io.WriteString(w, err.Error())
    		return
    	}
    
    	conn, err := net.ListenMulticastUDP("udp4", inf, addr)
    	if err != nil {
    		w.WriteHeader( http.StatusInternalServerError)
    		io.WriteString(w, err.Error())
    		return
    	}
    	defer conn.Close()
    
    	w.Header().Set("Content-Type", "application/octet-stream")
    	w.WriteHeader( http.StatusOK)
    	n, err := io.Copy(w, conn)
    	log.Printf("%s %s %d [%s]", req.RemoteAddr, req.URL.Path, n, req.UserAgent())
    }
    
    func main() {
    	if os.Getppid() == 1 {
    		log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime))
    	} else {
    		log.SetFlags(log.Lshortfile | log.LstdFlags)
    	}
    
    	flag.Parse()
    
    	var err error
    	inf, err = net.InterfaceByName(*iface)
    	if err != nil {
    		log.Fatal(err)
    		return
    	}
    
    	var mux http.ServeMux
    	mux.HandleFunc("/rtp/", handleHTTP)
    
    	log.Fatal( http.ListenAndServe(*addr, &mux))
    }
    

    启动后,使用 iina 播放:iina http://localhost:18000/rtp/239.3.1.62:8112

    不过这样的实现 VLC 播放器无法直接播放, Apple TV 上某些底层 使用了 VLC 的播放器也无法播放了 需要解析 RTP 包,把 payload 提取出来

    找了一番 RTP 协议的实现,发现已经有现成的了 github.com/pion/rtp

    具体实现可以参考 https://github.com/darren/retv/blob/54378d16d6e1042b9259280b3a7aea50087d0518/rtp.go#L73

    下一步的想法:自动把视频流切成 segments,实现 HLS ,让浏览器也能直接播放 😄

    5 replies    2023-04-15 09:15:37 +08:00
    FrankAdler
        1
    FrankAdler  
       Feb 15, 2023
    监听多播 UDP ,然后返回收到的数据,看起来确实挺简单的,很好奇 udpxy 那么多代码都做了哪些额外工作
    darrh00
        2
    darrh00  
    OP
       Feb 16, 2023
    @FrankAdler

    大概看了 udpxy 的代码,我觉得主要这几个方面吧:

    1. HTTP 协议的处理,而 Go 自带了电池😄,

    2. 多进程处理,每来一个播放请求,都需要派生子进程进行处理,goroutine 简单,

    3. RTP 协议的处理,而 Go 的 rtp 的第三方协议的处理包看起来挺成熟的,现成的,

    4. 视频流的录制到文件功能,Go 实现应该也挺简单。
    noahzh
        3
    noahzh  
       Feb 16, 2023
    这个好,udpxy 太古老了。
    uvhchina
        4
    uvhchina  
       Mar 2, 2023   ❤️ 1
    好像有个 msd_lite?
    zhangtao1518
        5
    zhangtao1518  
       Apr 15, 2023
    @darrh00
    请邮件沟通下!
    [email protected]
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5617 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 03:43 · PVG 11:43 · LAX 20:43 · JFK 23:43
    ♥ Do have faith in what you're doing.