zhangpeihao / gortmp Goto Github PK
View Code? Open in Web Editor NEWImplement RTMP protocol by golang
License: MIT License
Implement RTMP protocol by golang
License: MIT License
Is it possible to make "smart" proxy?
If user connects to live stream and proxy have already clients on that stream it won't make new connection to master server but server the same data.
So if client 1 connects to live/stream1, proxy connects to master.server.org/live/stream1, then client 2 connects to live/stream1 and proxy just fanouts packets to clients 1 and 2 without making 2nd connection to master server.
Let me know how if it's easy. I'm newbie in Go :(
Hello, I'm wondering how it's possible to create an outbound stream ? In particular in the rtmp_publisher demo, how can I send an outboundstream into the createOutboundStreamChan ? Perhaps I can give more details on what I want to do. I want to create to simply create a connection to an rtmp url and send data to it, is this possible using the publisher demo, and if so how can I create a stream to use stream.PublishData ?? What is stream?
Regards,
Saxon
运行demo的server后,执行命令ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost/app
就一直停留在那,不会继续跑下去了?不知道为何,下面是log的信息:
2016/09/01 23:28:21 ######## Profiles #######
--- contention:
cycles/second=2261009062
goroutine profile: total 4
1 @ 0x55ce18 0x55cbf3 0x558484 0x4bbc3d 0x4bbab9 0x4bb2e4 0x4b9bc2 0x401a7b 0x4308b0 0x4601e1
# 0x55ce18 runtime/pprof.writeRuntimeProfile+0xb8 /home/bao/go/src/runtime/pprof/pprof.go:545
# 0x55cbf3 runtime/pprof.writeGoroutine+0x93 /home/bao/go/src/runtime/pprof/pprof.go:507
# 0x558484 runtime/pprof.(*Profile).WriteTo+0xd4 /home/bao/go/src/runtime/pprof/pprof.go:236
# 0x4bbc3d github.com/zhangpeihao/log.(*Logger).DumpProf+0x13d /home/bao/gowork/src/github.com/zhangpeihao/log/log.go:146
# 0x4bbab9 github.com/zhangpeihao/log.(*Logger).DumpHeader+0x109 /home/bao/gowork/src/github.com/zhangpeihao/log/log.go:137
# 0x4bb2e4 github.com/zhangpeihao/log.NewLoggerWithHeader+0x1704 /home/bao/gowork/src/github.com/zhangpeihao/log/log.go:116
# 0x4b9bc2 github.com/zhangpeihao/log.NewLogger+0xa2 /home/bao/gowork/src/github.com/zhangpeihao/log/log.go:55
# 0x401a7b main.main+0xab /home/bao/bao/program/go/gortmp-master/demo/server/server.go:95
# 0x4308b0 runtime.main+0x2b0 /home/bao/go/src/runtime/proc.go:188
1 @ 0x4601e1
1 @ 0x41104e 0x443892 0x47ee78 0x4601e1
# 0x443892 os/signal.signal_recv+0x132 /home/bao/go/src/runtime/sigqueue.go:116
# 0x47ee78 os/signal.loop+0x18 /home/bao/go/src/os/signal/signal_unix.go:22
1 @ 0x430c93 0x43f767 0x43ecc2 0x4be721 0x4601e1
# 0x4be721 github.com/zhangpeihao/log.(*Logger).counterDump+0x1721 /home/bao/gowork/src/github.com/zhangpeihao/log/log.go:360
heap profile: 0: 0 [0: 0] @ heap/1048576
# runtime.MemStats
# Alloc = 155936
# TotalAlloc = 155936
# Sys = 1937408
# Lookups = 7
# Mallocs = 648
# Frees = 68
# HeapAlloc = 155936
# HeapSys = 688128
# HeapIdle = 98304
# HeapInuse = 589824
# HeapReleased = 0
# HeapObjects = 580
# Stack = 360448 / 360448
# MSpan = 8280 / 16384
# MCache = 4800 / 16384
# BuckHashSys = 2184
# NextGC = 4194304
# PauseNs = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
# NumGC = 0
# EnableGC = true
# DebugGC = false
threadcreate profile: total 8
8 @
######## Heap #######
heap profile: 0: 0 [0: 0] @ heap/1048576
# runtime.MemStats
# Alloc = 195664
# TotalAlloc = 195664
# Sys = 1937408
# Lookups = 7
# Mallocs = 742
# Frees = 98
# HeapAlloc = 195664
# HeapSys = 688128
# HeapIdle = 40960
# HeapInuse = 647168
# HeapReleased = 0
# HeapObjects = 644
# Stack = 360448 / 360448
# MSpan = 8640 / 16384
# MCache = 4800 / 16384
# BuckHashSys = 2184
# NextGC = 4194304
# PauseNs = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
# NumGC = 0
# EnableGC = true
# DebugGC = false
######## End #######
2016/09/01 23:28:21 Start listen...
2016/09/01 23:28:31 Handshake begin
2016/09/01 23:28:31 SHandshake() Flash player version is 9.0.124.2
2016/09/01 23:28:31 SHandshake() scheme = 0
2016/09/01 23:28:31 Buffer(SHandshake signatureResp):
b1 87 a0 e7 00 12 fc da 28 93 2b 6b a6 b9 c7 59
5b 11 33 7d ce 58 f2 70 5a 91 f6 bd 4b b6 b8 f6
2016/09/01 23:28:31 New stream 1 csi: 3, fmt: 0
2016/09/01 23:28:31 New stream 2 csi: 3, fmt: 0, header: &{Fmt:0 ChunkStreamID:3 Timestamp:0 MessageLength:137 MessageTypeID:20 MessageStreamID:0 ExtendedTimestamp:0}
2016/09/01 23:28:31 Unfinish message(remain: 137, chunksize: 128)
2016/09/01 23:28:31 Message(<<<){CID: 3, Type: 20, Timestamp: 0, Size: 137, StreamID: 0, IsInbound: true, AbsoluteTimestamp: 0}
2016/09/01 23:28:31 conn::invokeCommand()
2016/09/01 23:28:31 Command{IsFlex: false, Name: connect, TransactionID: 1, Objects: [map[app:app type:nonprivate flashVer:FMLE/3.0 (compatible; Lavf57.48.101) tcUrl:rtmp://localhost:1935/app]]}
2016/09/01 23:28:31 inboundConn::onConnect
2016/09/01 23:28:31 conn::SetWindowAcknowledgementSize
2016/09/01 23:28:31 conn::SetPeerBandwidth
2016/09/01 23:28:31 conn::SetChunkSize(size: 4096)
2016/09/01 23:28:31 Message(sendConnectResult){CID: 3, Type: 20, Timestamp: 0, Size: 165, StreamID: 0, IsInbound: false, AbsoluteTimestamp: 0}
2016/09/01 23:28:31 OutboundChunkStream::NewOutboundHeader() header: &{Fmt:0 ChunkStreamID:2 Timestamp:0 MessageLength:4 MessageTypeID:5 MessageStreamID:0 ExtendedTimestamp:0}
2016/09/01 23:28:31 OutboundChunkStream::NewOutboundHeader() header: &{Fmt:1 ChunkStreamID:2 Timestamp:0 MessageLength:5 MessageTypeID:6 MessageStreamID:0 ExtendedTimestamp:0}
2016/09/01 23:28:31 OutboundChunkStream::NewOutboundHeader() header: &{Fmt:1 ChunkStreamID:2 Timestamp:0 MessageLength:4 MessageTypeID:1 MessageStreamID:0 ExtendedTimestamp:0}
2016/09/01 23:28:31 OutboundChunkStream::NewOutboundHeader() header: &{Fmt:0 ChunkStreamID:3 Timestamp:0 MessageLength:165 MessageTypeID:20 MessageStreamID:0 ExtendedTimestamp:0}
2016/09/01 23:28:31 New stream 1 csi: 2, fmt: 0
2016/09/01 23:28:31 New stream 2 csi: 2, fmt: 0, header: &{Fmt:0 ChunkStreamID:2 Timestamp:0 MessageLength:4 MessageTypeID:1 MessageStreamID:0 ExtendedTimestamp:0}
2016/09/01 23:28:31 Message(<<<){CID: 2, Type: 1, Timestamp: 0, Size: 4, StreamID: 0, IsInbound: true, AbsoluteTimestamp: 0}
2016/09/01 23:28:31 conn::invokeSetChunkSize() conn.inChunkSize = 4096
2016/09/01 23:28:31 Message(<<<){CID: 3, Type: 20, Timestamp: 0, Size: 29, StreamID: 0, IsInbound: true, AbsoluteTimestamp: 0}
2016/09/01 23:28:31 conn::invokeCommand()
2016/09/01 23:28:31 Command{IsFlex: false, Name: releaseStream, TransactionID: 2, Objects: [<nil> ]}
2016/09/01 23:28:31 inboundConn::ReceivedRtmpCommand: &{IsFlex:false Name:releaseStream TransactionID:2 Objects:[<nil> ]}
2016/09/01 23:28:31 Message(<<<){CID: 3, Type: 20, Timestamp: 0, Size: 25, StreamID: 0, IsInbound: true, AbsoluteTimestamp: 0}
2016/09/01 23:28:31 conn::invokeCommand()
2016/09/01 23:28:31 Command{IsFlex: false, Name: FCPublish, TransactionID: 3, Objects: [<nil> ]}
2016/09/01 23:28:31 inboundConn::ReceivedRtmpCommand: &{IsFlex:false Name:FCPublish TransactionID:3 Objects:[<nil> ]}
2016/09/01 23:28:31 Message(<<<){CID: 3, Type: 20, Timestamp: 0, Size: 25, StreamID: 0, IsInbound: true, AbsoluteTimestamp: 0}
2016/09/01 23:28:31 conn::invokeCommand()
2016/09/01 23:28:31 Command{IsFlex: false, Name: createStream, TransactionID: 4, Objects: [<nil>]}
2016/09/01 23:28:31 inboundConn::onCreateStream
2016/09/01 23:28:31 index: 0, newChunkStreamID: 8
2016/09/01 23:28:31 Message(sendCreateStreamSuccessResult){CID: 3, Type: 20, Timestamp: 0, Size: 29, StreamID: 0, IsInbound: false, AbsoluteTimestamp: 0}
2016/09/01 23:28:31 OutboundChunkStream::NewOutboundHeader() header: &{Fmt:1 ChunkStreamID:3 Timestamp:0 MessageLength:29 MessageTypeID:20 MessageStreamID:0 ExtendedTimestamp:0}
2016/09/01 23:28:31 New stream 1 csi: 8, fmt: 0
2016/09/01 23:28:31 New stream 2 csi: 8, fmt: 0, header: &{Fmt:0 ChunkStreamID:8 Timestamp:0 MessageLength:30 MessageTypeID:20 MessageStreamID:1 ExtendedTimestamp:0}
2016/09/01 23:28:31 Message(<<<){CID: 8, Type: 20, Timestamp: 0, Size: 30, StreamID: 1, IsInbound: true, AbsoluteTimestamp: 0}
用player 的那个demo 做测试, 发现每次只能收获前面两次数据:
发现最后是conn.br.ReadByte()无数据, 我的测试视频源应该是没问题的, 用LVC 能正常播放.
想请教一下, 什么情况可能造成这个问题?
具体停止的位置,
ReadBaseHeader 这个方法中:
b, err = ReadByteFromNetwork(rbuf) 这里卡住了
另外有一个疑问, 我第二次收到的command 正确么? 请帮忙看看, 谢谢
=====以下是运行的log====
to dial
obConn: &{url:rtmp://XXX:1935/myapp/tt_friends?vhost=tt rtmpURL:{protocol:rtmp host:XXX port:1935 app:myapp instanceName:tt_friends?vhost=tt} status:1 err:?reflect.Value? handler:0x7a1e90 conn:0xc208080100 transactions:map[] streams:map[]}
obConn.URL(): rtmp://XXXX:1935/myapp/tt_friends?vhost=tt
to connect
sendLoop here
sendLoop here
conn.br:
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 2
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 5
conn.br:
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 2
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 6
conn.br:
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 2
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 1
conn.br:
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 3
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 20
@@@@@@@@@@@@@status: 3, err:
ReceviedCommand: &{IsFlex:false Name:_result TransactionID:1 Objects:[map[fmsVer:FMS/3,0,1,123 capabilities:31] map[code:NetConnection.Connect.Success description:Connection succeeded. objectEncoding:0 data:map[server_pid:7253 stream_id:14485083201515715713 server_ip:100.69.198.197 server_name:e100069198197.zmf] level:status]]}
conn.br:
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
sendLoop here
sendLoop here
read header 01 3
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 20
@@@@@@@@@@@@@status: 5, err:
Stream created: 1
ReceviedCommand: &{IsFlex:false Name:_result TransactionID:2 Objects:[ 1]}
conn.br:
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
play here &{1 0xc2080520a0 8 ?reflect.Value? 0}
sendLoop here
sendLoop here
read header 01 5
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 20
OnReceived
conn.br:
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 5
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
read header 01 18
OnReceived
conn.br:
func ReadByteFromNetwork(r Reader) (b byte, err error)
read header 0:
Audio size: 0 bytes; Vedio size: 0 bytes
sendLoop here
Audio size: 0 bytes; Vedio size: 0 bytes
sendLoop here
Audio size: 0 bytes; Vedio size: 0 bytes
sendLoop here
Audio size: 0 bytes; Vedio size: 0 bytes
sendLoop here
Because timestamps are 32 bits long, they roll over every 49 days, 17 hours, 2 minutes and 47.296 seconds. Because streams are allowed to run continuously, potentially for years on end, an RTMP application SHOULD use serial number arithmetic [RFC1982] when processing timestamps, and SHOULD be capable of handling wraparound. For example, an application assumes that all adjacent timestamps are within 2^31 - 1 milliseconds of each other, so 10000 comes after 4000000000, and 3000000000 comes before 4000000000.
上面的描述说明了的timestamp是32的无符号整数所表示的毫秒,由于该值最大只能表示不到50天的时间戳,所以需要对时间戳做环绕式处理。
2. defines.go文件中GetTimestamp的实现
466 // Get timestamp
467 func GetTimestamp() uint32 {
468 //return uint32(0)
469 return uint32(time.Now().UnixNano()/int64(1000000)) % MAX_TIMESTAMP
470 }
该函数取系统当前时间(毫秒)作为时间戳,然后与 MAX_TIMESTAMP取模,这样也确实实现的环绕处理, 但是我看到MAX_TIMESTAMP的定义如下:
278 MAX_TIMESTAMP = uint32(2000000000)
正确的定义该值应该是32位无符号整数的最大值+1, 即0x100000000,defines.go文件对该值的定义的显然不对。
3. defines.go文件的第469行有一种更简单的改写方式, 如下:
469 return uint32(time.Now().UnixNano()/int64(1000000))
超过32位无符号整数的部分会被自然溢出,我们强制转换得到溢出后剩下的部分,即实现了环绕,也相当于对0x100000000做取模操作。
I'm trying to connect to a Tinychat server, and sending the room as the first connection parameter, without success.
func main() {
l := log.NewStderrLogger()
l.SetMainLevel(4)
defer l.Close()
rtmp.InitLogger(l)
handler := &ConnHandler{}
obConn, err := rtmp.Dial("rtmp://host:port/tinyconf", handler, 100)
if err != nil {
fmt.Printf("Conn err: %s\n", err)
os.Exit(1)
}
defer obConn.Close()
err = obConn.Connect("ROOM", "none", "show", "tinychat")
if err != nil {
fmt.Printf("Conn err: %s\n", err)
os.Exit(1)
}
for {
}
}
Output:
2015/05/04 14:21:03 Handshake() FMS version is 0.0.0.0
2015/05/04 14:21:03 Buffer(signatureResp):
09 0c ef 72 3e 06 5a af 8b 47 cf 21 1c dc 96 e3
ff 41 3c b8 61 84 fb 50 c1 06 6a 60 3a 83 f4 53
2015/05/04 14:21:03 Handshake OK
2015/05/04 14:21:03 Message(connect){CID: 3, Type: 20, Timestamp: 0, Size: 244, StreamID: 0, IsInbound: false, AbsoluteTimestamp: 0}
Any ideas?
I have already created a stream to wowza, but use OutboundStream.PublishVideoData or OutboundStream.PublishAudioData can not real push data to wowza. Stream status is active but Bytes In is 0. There is no err return.
What can I do for check this error.
Is the project dead ? seemed very useful and promising
I want to use Sourcegraph code search and code review with gortmp. A project maintainer needs to enable it to set up a webhook so the code is up-to-date there.
Could you please enable gortmp on @sourcegraph by going to https://sourcegraph.com/github.com/zhangpeihao/gortmp and clicking on Settings? (It should only take 15 seconds.)
Thank you!
I tried the demo, start the server then make the publisher connect it, but it doesn't work.
If I make the publisher connect my FMS, it works.
Does the gortmp mush be used with FMS or any other rtmp-server?
If so, what is the server used for?
Great job on this library!
I was looking for something similar to rtmpdump
link from mplayer where we could programatically access containers in rtmp and your library looks promising.
One thing that's not clear:
Is there any way of accessing the flv container from an rtmp stream using this library?
I see you've got the outboundStream
struct in outboundstream.go
link
Is there any way of accessing the underlying flv container?
一開始的Handshake都有成功,但送資料給wowza server一段時間後,就會
connection reset by peer 的錯誤出現。
我們去解讀封包,發現rtmp的封包都是unknown type,不知道是不是那邊資料有送錯?
照理說應該可以知道是video type or audio type.
May I contact you?I wish to pay for some features if possbile. :)
my email:yqf0215%gmail.com.
Thank you very much. Your programe is very good ! 👍
rtmp:rtmp://188.138.17.8:1935/albuk/albuk.stream stream:stream_name flv:./video_dump
to dial
@@@@@@@@@@@@@status: 1, err: <nil>
obConn: &{url:rtmp://188.138.17.8:1935/albuk/albuk.stream rtmpURL:{protocol:rtmp host:188.138.17.8 port:1935 app:albuk instanceName:albuk.stream} status:1 err:<nil> handler:0x7760b0 conn:0xc42000a900 transactions:map[] streams:map[]}
obConn.URL(): rtmp://188.138.17.8:1935/albuk/albuk.stream
to connect
@@@@@@@@@@@@@status: 3, err: <nil>
ReceviedCommand: &{IsFlex:false Name:_result TransactionID:1 Objects:[map[capabilities:31 mode:1 fmsVer:FMS/3,5,7,7009] map[objectEncoding:0 level:status code:NetConnection.Connect.Success description:Connection succeeded. data:map[version:3,5,7,7009] clientid:9.77161e+06]]}
@@@@@@@@@@@@@status: 5, err: <nil>
Stream created: 1
ReceviedCommand: &{IsFlex:false Name:_result TransactionID:2 Objects:[<nil> 1]}
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Am I running or setting it up wrong?
RT,我是个编程萌新,看了半天大神的代码还是不得其法,所以实在不得以做了伸手,请问该如何基于go和rtmp传输音频文件,麻烦了,谢谢。
Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffff701a700 (LWP 627)] 0x00000000005c5429 in github.com/zhangpeihao/log.(*Logger).ModulePrintf (logger=0x0, module=0x0, level=4, format=..., v=...) at /home/president/go/src/github.com/zhangpeihao/log/log.go:335 335
Output from gdb, trying to connect.
runtime error: invalid memory address or nil pointer dereference
Output from execution.
可以在公网使用吗?比如我把服务端放到阿里云上。
按照demo/server, 建立的rtmp服务器用vlc播放卡顿!
尝试把 if diff1 > diff2+100 {
改为 if diff1 > diff2 {
有所改善, 但依旧卡的不行
不支持简单握手
Now can't run without logging into file.
Is it possible to make
Line 315 in a3f6764
使用vlc播放视频流,每次只能有一个能播放,其余的会停止,请问怎么才能让多个vlc同时播放?
用rtmp client拉取海康RTMP视频流时,画面卡顿,局域网之内,同时MIN_BUFFER_LENGTH也调大了,也依然有停顿的感觉,尤其是视频画面里面动作幅度比较快的情况下,请问要在哪里进行调整?
Hi, When i run rtmp_play.go demo, i can't get the video type and audio type.
Output is like:
@@@@@@@@@@@@@status: 1, err:
connect message in package &gortmp.Message{ChunkStreamID:0x3, Timestamp:0x0, Size:0xd5, Type:0x14, StreamID:0x0, Buf:(*bytes.Buffer)(0xc4200a83f0), IsInbound:false, AbsoluteTimestamp:0x0}@@@@@@@@@@@@@status: 3, err:
ReceviedCommand: &{IsFlex:false Name:_result TransactionID:1 Objects:[map[fmsVer:FMS/3,0,1,123 capabilities:31 mode:1] map[version:3,5,1,516 level:status code:NetConnection.Connect.Success description:Connection succeeded. objectEncoding:0 data:0]]}
ReceviedCommand: &{IsFlex:false Name:onBWDone TransactionID:0 Objects:[]}
@@@@@@@@@@@@@status: 5, err:
ReceviedCommand: &{IsFlex:false Name:_result TransactionID:2 Objects:[ 1]}
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
@@@@@@@@@@@@@status: 0, err:
@@@@@@@@@@@@@closed
Seems not receive onPublishStart
Here is log.
to dial
to connect
Audio size: 0 bytes; Vedio size: 0 bytes
status: 3, err:
ReceviedRtmpCommand: &{IsFlex:false Name:_result TransactionID:1 Objects:[map[fmsVer:FMS/3,5,3,824 capabilities:127 mode:1] map[level:status code:NetConnection.Connect.Success description:Connection succeeded. objectEncoding:0 data:map[version:3,5,3,824]]]}
ReceviedRtmpCommand: &{IsFlex:false Name:onBWDone TransactionID:0 Objects:[]}
Audio size: 0 bytes; Vedio size: 0 bytes
status: 5, err:
Stream created: 1
ReceviedRtmpCommand: &{IsFlex:false Name:_result TransactionID:2 Objects:[ 1]}
Publish
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
Im trying to pull a stream and publish(push) to another server, but video/audio is out of sync.
How should delta timestamp be calculated?
thanks
Hi,
I publish one text string after each video frame use message type id 18 (0x13) for AMF3 encoding, but make the stream closed. And I have tried message type id 15 (0x0f) for AMF0 encoding, it made the stream closed too.
the codes i make in demo/publisher as following:
fmt.Printf("@@@@@@@@@@@@@@diff1 header(%+v), startTs: %d\n", header, startTs)
if err = stream.PublishData(header.TagType, data, diff1); err != nil {
fmt.Println("PublishData() error:", err)
break
}
if header.TagType == flv.VIDEO_TAG {
subTitleHeader := *header
subTitleHeader.TagType = byte(0x13)
subTitle := fmt.Sprintf("+++ video frame Timestamp:%d +++\n", subTitleHeader.Timestamp)
subTitleData := make([]byte, len(subTitle)+1)
subTitleHeader.DataSize = uint32(len(subTitle))
copy(subTitleData, subTitle[:])
subTitleData[len(subTitle)] = 0x00
if err = stream.PublishData(subTitleHeader.TagType, subTitleData, diff1); err != nil {
fmt.Println("PublishData() subtitle error:", err)
break
}
}
The error logs I got:
ray@TF:/md/gows/src/github.com/falconray0704/gortmp/demo/publisher$ ./rtmp_publisher -URL "rtmp://10.1.51.20:1935/myapp/" -Stream cv -FLV "/md/videos/aWalkToUnionSquare.flv"
to dial
a
b
to connect
c
@@@@@@@@@@@@@status: 3, err:
ReceviedRtmpCommand: &{IsFlex:false Name:_result TransactionID:1 Objects:[map[fmsVer:FMS/3,0,1,123 capabilities:31] map[level:status code:NetConnection.Connect.Success description:Connection succeeded. objectEncoding:0]]}
@@@@@@@@@@@@@status: 5, err:
Stream created: 1
ReceviedRtmpCommand: &{IsFlex:false Name:_result TransactionID:2 Objects:[ 1]}
1
2
3
@@@@@@@@@@@@@@Diff1 header(&{TagType:18 DataSize:372 Timestamp:0}), startTs: 0
@@@@@@@@@@@@@@Diff1 header(&{TagType:18 DataSize:372 Timestamp:0}), startTs: 0
@@@@@@@@@@@@@@Diff1 header(&{TagType:8 DataSize:418 Timestamp:0}), startTs: 0
@@@@@@@@@@@@@@Diff1 header(&{TagType:8 DataSize:418 Timestamp:0}), startTs: 0
@@@@@@@@@@@@@@Diff1 header(&{TagType:9 DataSize:180804 Timestamp:25}), startTs: 25
@@@@@@@@@@@@@@Diff1 header(&{TagType:9 DataSize:180804 Timestamp:25}), startTs: 25
@@@@@@@@@@@@@@Diff1 header(&{TagType:8 DataSize:419 Timestamp:26}), startTs: 25
@@@@@@@@@@@@@@Diff1 header(&{TagType:8 DataSize:419 Timestamp:52}), startTs: 25
@@@@@@@@@@@@@@Diff1 header(&{TagType:9 DataSize:216201 Timestamp:59}), startTs: 25
@@@@@@@@@@@@@@Diff1 header(&{TagType:8 DataSize:419 Timestamp:78}), startTs: 25
@@@@@@@@@@@@@@Diff1 header(&{TagType:9 DataSize:64624 Timestamp:94}), startTs: 25
@@@@@@@@@@@@@@Diff1 header(&{TagType:8 DataSize:419 Timestamp:104}), startTs: 25
@@@@@@@@@@@@@@Diff1 header(&{TagType:9 DataSize:43542 Timestamp:128}), startTs: 25
@@@@@@@@@@@@@status: 0, err:
@@@@@@@@@@@@@closed
Audio size: 2094 bytes; Vedio size: 505171 bytes
Audio size: 2094 bytes; Vedio size: 505171 bytes
^C
ray@TF:/md/gows/src/github.com/falconray0704/gortmp/demo/publisher$
Actually I want to streaming subtitle for each video frame with the same timestamp.
Which way could I make it?
Thanks.
header.Timestamp: 5408, now: 1472553720757848290
diff1: 5408
== publishdata: 8 5408
diff1: 5440
== publishdata: 8 5440
diff1: 5472
== publishdata: 8 5472
diff1: 5504
== publishdata: 8 5504
header.Timestamp: 5536, now: 1472553720886429117
diff1: 5536
== publishdata: 8 5536
== diff1
diff1: 0
== publishdata: 9 0
diff1: 5568
== publishdata: 8 5568
diff1: 5600
== publishdata: 8 5600
diff1: 5632
== publishdata: 8 5632
header.Timestamp: 5664, now: 1472553721016424239
Audio size: 284381 bytes; Vedio size: 1713772 bytes
diff1: 68
== publishdata: 9 68
diff1: 5664
== publishdata: 8 5664
diff1: 5696
== publishdata: 8 5696
diff1: 5728
== publishdata: 8 5728
diff1: 168
== publishdata: 9 168
diff1: 5760
== publishdata: 8 5760
Thanks for the lovely repo.
I had a go at using the ./publisher to stream to Twitch (https://www.twitch.tv/)
./publisher --FLV file.flv --URL rtmp://live.twitch.tv/app/[key]
this gives a message
to dial
a
b
to connect
c
@@@@@@@@@@@@@status: 3, err: <nil>
ReceviedRtmpCommand: &{IsFlex:false Name:_result TransactionID:1 Objects:[map[capabilities:31 mode:1 fmsVer:FMS/3,5,7,7009
] map[description:Connection accepted. data:map[string:3,5,7,7009] objectEncoding:0 level:status code:NetConnection.Connec
t.Success]]}
@@@@@@@@@@@@@status: 5, err: <nil>
Stream created: 1
ReceviedRtmpCommand: &{IsFlex:false Name:_result TransactionID:2 Objects:[<nil> 1]}
Audio size: 0 bytes; Vedio size: 0 bytes
Audio size: 0 bytes; Vedio size: 0 bytes
looks like the OnPublishStart callback does not get called
i have verified the file.flv with ffmpeg ( ffmpeg -re -i file.flv -c copy -f flv rtmp://live.twitch.tv/[...]) and it works correctly
the file was generated with ffmpeg
ffmpeg -i input.mkv -c:v libx264 -preset medium -maxrate 3000k -bufsize 6000k -vf "scale=1280:-1,format=yuv420p" -g 50 -c:a aac -b:a 128k -ac 2 -ar 44100 file.flv
it feels like a problem with RTMP
best to my knowledge Twitch should be accessible from China
thanks for the great repo and your effort, and would be very cool if this worked with Twitch!
hi @zhangpeihao
http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf
says that :
Zero (4 bytes): This field MUST be all 0s.
look this code
// Set FlashPlayer version
for i := 0; i < 4; i++ {
s1[4+i] = FMS_VERSION[i]
}
why you set them not zero?
and I also test ok if I set s1[4+i] = 0
谢谢。
Hi. Thanks for your work.
Can you rename package (or project) name as described in Go's convention:
https://golang.org/doc/code.html#PackageNames
Heya,
Cool package, i'm looking into recording rtmp and tried the rtmp_player, but it seams the example is broken ? Any chance you could update it ?
Perhaps Socks 4/5 proxy support could be a nice addition to this Go package?
Just a suggestion, feel free to add the "enhancement" label.
I am using rtmp_player
to read a rtmp stream and publish it to another RTMP server
using rtmp_publish
What to pass as argument to stream.PublishData(tagHeader.TagType, data, deltaTimestamp)
?
I receive the rtmp stream with Message struct from OnReceived(rconn rtmp.Conn, message *rtmp.Message)
type Message struct {
ChunkStreamID uint32
Timestamp uint32
Size uint32
Type uint8
StreamID uint32
Buf *bytes.Buffer
IsInbound bool
AbsoluteTimestamp uint32
}
When I publish directly with stream.PublishData(msg.Type,msg.Buf.Bytes(),msg.Timestamp)
The rtmp stream is automatically closed just after the stream is accepted.
Simple steps to reproduce this : (I am doing it live by establishing a tcp socket and forwarding the packets, the same behavior is exhibited when I save to file and use the file to publish)
./player -URL rtmp://188.138.17.8:1935/albuk -Stream albuk.stream -DumpFLV ./demo.flv
./publish -URL rtmp://myserver:1935/<path>/ -Stream <name> -FLV ./demo.flv
I get the following output
to dial
a
b
to connect
c
@@@@@@@@@@@@@status: 3, err: <nil>
ReceviedRtmpCommand: &{IsFlex:false Name:_result TransactionID:1 Objects:[map[fmsVer:FMS/3,0,1,123 capabilities:31] map[code:NetConnection.Connect.Success description:Connection succeeded. objectEncoding:0 level:status]]}
@@@@@@@@@@@@@status: 5, err: <nil>
Stream created: 1
ReceviedRtmpCommand: &{IsFlex:false Name:_result TransactionID:2 Objects:[<nil> 1]}
1
2
3
3333@@@@@@@@@@@@@@diff1 header(&{TagType:18 DataSize:40 Timestamp:0}), startTs: 0
18 40 0 0
3333@@@@@@@@@@@@@@diff1 header(&{TagType:9 DataSize:44 Timestamp:0}), startTs: 0
9 44 0 0
3333@@@@@@@@@@@@@@diff1 header(&{TagType:9 DataSize:2 Timestamp:0}), startTs: 0
9 2 0 0
3333@@@@@@@@@@@@@@diff1 header(&{TagType:9 DataSize:23332 Timestamp:0}), startTs: 0
9 23332 0 0
3333@@@@@@@@@@@@@@diff1 header(&{TagType:9 DataSize:55 Timestamp:0}), startTs: 0
9 55 0 0
3333@@@@@@@@@@@@@@diff1 header(&{TagType:9 DataSize:5658 Timestamp:0}), startTs: 0
9 5658 0 0
3333@@@@@@@@@@@@@@diff1 header(&{TagType:9 DataSize:120 Timestamp:0}), startTs: 0
9 120 0 0
3333@@@@@@@@@@@@@@diff1 header(&{TagType:9 DataSize:5334 Timestamp:0}), startTs: 0
9 5334 0 0
.
.
.
9 47 200 204
8 554 209 213
9 7857 220 224
8 555 231 235
@@@@@@@@@@@@@status: 0, err: <nil>
@@@@@@@@@@@@@Closed
Audio size: 6494 bytes; Vedio size: 380281 bytes
Audio size: 6494 bytes; Vedio size: 380281 bytes
There is no video stream running.
$ ./rtmp_publisher -FLV test.flv -Stream test -URL "rtmp://ossrs.net:1935/live"
to dial
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x50 pc=0x2068]
goroutine 1 [running]:
panic(0x1c19a0, 0xc4200140c0)
/usr/local/Cellar/go/1.7/libexec/src/runtime/panic.go:500 +0x1a1
main.(*TestOutboundConnHandler).OnStatus(0x2f7050, 0x2c4b40, 0xc420098280)
/Users/xxx/go/src/github.com/zhangpeihao/gortmp/demo/publisher/rtmp_publisher.go:38 +0x28
github.com/zhangpeihao/gortmp.Dial(0x7fff5fbffa69, 0x1a, 0x2c3ee0, 0x2f7050, 0x64, 0x0, 0x0, 0x3c, 0x15180)
/Users/xxx/go/src/github.com/zhangpeihao/gortmp/outboundconn.go:109 +0x604
main.main()
/Users/xxx/go/src/github.com/zhangpeihao/gortmp/demo/publisher/rtmp_publisher.go:138 +0x226
I was trying to do a simple webcam publish - from this site:
http://myprojectguide.org/p/flash-videoio/4.html
The camera briefly connects and then aborts - the goal is to just publish from the webcam and save to the local system as a test.
I use rtmp client to check the edge ip whether can be fetch data?
func DetectRtmp(vip string) bool {
rtmpName := "rtmp://" + vip + "/uplive.b0.upaiyun.com/live"
streamName := "jiqiang"
//fmt.Printf("%s\n", rtmp_name)
createStreamChan := make(chan rtmp.OutboundStream)
var audioSize int64 = 0
var videoSize int64 = 0
testHandler := &TestOutboundConnHandler{createStreamChan, &audioSize, &videoSize}
//tcp dial
obConn, err := rtmp.Dial(rtmpName, testHandler, 100)
if err != nil {
//fmt.Printf("%s dial fail, err: %s\n", vip, err.Error())
return false
}
defer obConn.Close()
//rtmp conn
if err = obConn.Connect(); err != nil {
//fmt.Printf("%s conn fail, err: %s\n", vip, err.Error())
return false
}
flag := true
count := 0
for {
select {
case stream := <-createStreamChan:
// Play
err = stream.Play(streamName, nil, nil, nil)
if err != nil {
//fmt.Printf("Play error: %s", err.Error())
flag = false
}
// Set Buffer Length
case <-time.After(1 * time.Second):
if audioSize > 0 || videoSize > 0 {
//fmt.Printf("Audio size: %d bytes; Vedio size: %d bytes\n", audioSize, videoSize)
} else {
count += 1
}
}
if count > delay || !flag {
flag = false
break
} else if audioSize > 0 || videoSize > 0 {
break
}
}
return flag
}
func Check() {
res := make(map[string]bool)
var wg sync.WaitGroup
var mutex = &sync.Mutex{}
for name, ip := range edgeList {
wg.Add(1)
// one go routine to handle one check
go func(name string, ip string) {
defer wg.Done()
mutex.Lock()
res[name] = false
mutex.Unlock()
v := DetectRtmp(ip)
if !v {
v = DetectRtmp(ip)
}
mutex.Lock()
res[name] = v
mutex.Unlock()
}(name, ip)
}
wg.Wait()
fmt.Println("handle all vips")
gMutex.Lock()
for k, v := range res {
gRes[k] = v
}
gMutex.Unlock()
}
WARNING: DATA RACE
Write by goroutine 420:
github.com/zhangpeihao/gortmp.(_conn).Close()
/root/yang/gopath/src/github.com/zhangpeihao/gortmp/conn.go:444 +0x3a
github.com/zhangpeihao/gortmp.(_outboundConn).Close.func1()
/root/yang/gopath/src/github.com/zhangpeihao/gortmp/outboundconn.go:249 +0x70
Previous read by goroutine 73:
github.com/zhangpeihao/gortmp.(*conn).sendLoop()
/root/yang/gopath/src/github.com/zhangpeihao/gortmp/conn.go:241 +0x79
Goroutine 420 (running) created at:
github.com/zhangpeihao/gortmp.(*outboundConn).Close()
/root/yang/gopath/src/github.com/zhangpeihao/gortmp/outboundconn.go:250 +0x15c
main.DetectRtmp()
/root/yang/upsrs/upsrs_api/check/server/check_srs_edge_server.go:101 +0x4c3
main.Check.func1()
/root/yang/upsrs/upsrs_api/check/server/check_srs_edge_server.go:117 +0xca
Goroutine 73 (running) created at:
github.com/zhangpeihao/gortmp.NewConn()
/root/yang/gopath/src/github.com/zhangpeihao/gortmp/conn.go:148 +0x71c
github.com/zhangpeihao/gortmp.Dial()
/root/yang/gopath/src/github.com/zhangpeihao/gortmp/outboundconn.go:109 +0xabd
main.DetectRtmp()
/root/yang/upsrs/upsrs_api/check/server/check_srs_edge_server.go:59 +0x25a
main.Check.func1()
/root/yang/upsrs/upsrs_api/check/server/check_srs_edge_server.go:117 +0xca
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.