V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
darrh00
V2EX  ›  Go 编程语言

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

  •  
  •   darrh00 · 2023-02-15 22:53:35 +08:00 · 1957 次点击
    这是一个创建于 650 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一直在用 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 条回复    2023-04-15 09:15:37 +08:00
    FrankAdler
        1
    FrankAdler  
       2023-02-15 23:58:37 +08:00
    监听多播 UDP ,然后返回收到的数据,看起来确实挺简单的,很好奇 udpxy 那么多代码都做了哪些额外工作
    darrh00
        2
    darrh00  
    OP
       2023-02-16 00:44:10 +08:00
    @FrankAdler

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

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

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

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

    4. 视频流的录制到文件功能,Go 实现应该也挺简单。
    noahzh
        3
    noahzh  
       2023-02-16 10:24:54 +08:00
    这个好,udpxy 太古老了。
    uvhchina
        4
    uvhchina  
       2023-03-02 19:20:38 +08:00   ❤️ 1
    好像有个 msd_lite?
    zhangtao1518
        5
    zhangtao1518  
       2023-04-15 09:15:37 +08:00
    @darrh00
    请邮件沟通下!
    [email protected]
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5516 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 09:09 · PVG 17:09 · LAX 01:09 · JFK 04:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.