mirror of
https://github.com/Monibuca/plugin-rtmp.git
synced 2025-10-03 14:46:46 +08:00
将amf迁入engine中方便服用
This commit is contained in:
286
amf.go
286
amf.go
@@ -1,286 +0,0 @@
|
|||||||
package rtmp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"m7s.live/engine/v4/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Action Message Format -- AMF 0
|
|
||||||
// Action Message Format -- AMF 3
|
|
||||||
// http://download.macromedia.com/pub/labs/amf/amf0_spec_121207.pdf
|
|
||||||
// http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/amf/pdf/amf-file-format-spec.pdf
|
|
||||||
|
|
||||||
// AMF Object == AMF Object Type(1 byte) + AMF Object Value
|
|
||||||
//
|
|
||||||
// AMF Object Value :
|
|
||||||
// AMF0_STRING : 2 bytes(datasize,记录string的长度) + data(string)
|
|
||||||
// AMF0_OBJECT : AMF0_STRING + AMF Object
|
|
||||||
// AMF0_NULL : 0 byte
|
|
||||||
// AMF0_NUMBER : 8 bytes
|
|
||||||
// AMF0_DATE : 10 bytes
|
|
||||||
// AMF0_BOOLEAN : 1 byte
|
|
||||||
// AMF0_ECMA_ARRAY : 4 bytes(arraysize,记录数组的长度) + AMF0_OBJECT
|
|
||||||
// AMF0_STRICT_ARRAY : 4 bytes(arraysize,记录数组的长度) + AMF Object
|
|
||||||
|
|
||||||
// 实际测试时,AMF0_ECMA_ARRAY数据如下:
|
|
||||||
// 8 0 0 0 13 0 8 100 117 114 97 116 105 111 110 0 0 0 0 0 0 0 0 0 0 5 119 105 100 116 104 0 64 158 0 0 0 0 0 0 0 6 104 101 105 103 104 116 0 64 144 224 0 0 0 0 0
|
|
||||||
// 8 0 0 0 13 | { 0 8 100 117 114 97 116 105 111 110 --- 0 0 0 0 0 0 0 0 0 } | { 0 5 119 105 100 116 104 --- 0 64 158 0 0 0 0 0 0 } | { 0 6 104 101 105 103 104 116 --- 0 64 144 224 0 0 0 0 0 } |...
|
|
||||||
// 13 | {AMF0_STRING --- AMF0_NUMBER} | {AMF0_STRING --- AMF0_NUMBER} | {AMF0_STRING --- AMF0_NUMBER} | ...
|
|
||||||
// 13 | {AMF0_OBJECT} | {AMF0_OBJECT} | {AMF0_OBJECT} | ...
|
|
||||||
// 13 | {duration --- 0} | {width --- 1920} | {height --- 1080} | ...
|
|
||||||
|
|
||||||
const (
|
|
||||||
AMF0_NUMBER = 0x00 // 浮点数
|
|
||||||
AMF0_BOOLEAN = 0x01 // 布尔型
|
|
||||||
AMF0_STRING = 0x02 // 字符串
|
|
||||||
AMF0_OBJECT = 0x03 // 对象,开始
|
|
||||||
AMF0_MOVIECLIP = 0x04
|
|
||||||
AMF0_NULL = 0x05 // null
|
|
||||||
AMF0_UNDEFINED = 0x06
|
|
||||||
AMF0_REFERENCE = 0x07
|
|
||||||
AMF0_ECMA_ARRAY = 0x08
|
|
||||||
AMF0_END_OBJECT = 0x09 // 对象,结束
|
|
||||||
AMF0_STRICT_ARRAY = 0x0A
|
|
||||||
AMF0_DATE = 0x0B // 日期
|
|
||||||
AMF0_LONG_STRING = 0x0C // 字符串
|
|
||||||
AMF0_UNSUPPORTED = 0x0D
|
|
||||||
AMF0_RECORDSET = 0x0E
|
|
||||||
AMF0_XML_DOCUMENT = 0x0F
|
|
||||||
AMF0_TYPED_OBJECT = 0x10
|
|
||||||
AMF0_AVMPLUS_OBJECT = 0x11
|
|
||||||
|
|
||||||
AMF3_UNDEFINED = 0x00
|
|
||||||
AMF3_NULL = 0x01
|
|
||||||
AMF3_FALSE = 0x02
|
|
||||||
AMF3_TRUE = 0x03
|
|
||||||
AMF3_INTEGER = 0x04
|
|
||||||
AMF3_DOUBLE = 0x05
|
|
||||||
AMF3_STRING = 0x06
|
|
||||||
AMF3_XML_DOC = 0x07
|
|
||||||
AMF3_DATE = 0x08
|
|
||||||
AMF3_ARRAY = 0x09
|
|
||||||
AMF3_OBJECT = 0x0A
|
|
||||||
AMF3_XML = 0x0B
|
|
||||||
AMF3_BYTE_ARRAY = 0x0C
|
|
||||||
AMF3_VECTOR_INT = 0x0D
|
|
||||||
AMF3_VECTOR_UINT = 0x0E
|
|
||||||
AMF3_VECTOR_DOUBLE = 0x0F
|
|
||||||
AMF3_VECTOR_OBJECT = 0x10
|
|
||||||
AMF3_DICTIONARY = 0x11
|
|
||||||
)
|
|
||||||
|
|
||||||
var END_OBJ = []byte{0, 0, AMF0_END_OBJECT}
|
|
||||||
|
|
||||||
type AMFValue any
|
|
||||||
|
|
||||||
type AMFObject map[string]AMFValue
|
|
||||||
|
|
||||||
type AMF struct {
|
|
||||||
util.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
var ObjectEnd = &struct{}{}
|
|
||||||
var Undefined = &struct{}{}
|
|
||||||
|
|
||||||
func (amf *AMF) decodeObject() (obj AMFValue) {
|
|
||||||
if !amf.CanRead() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
t := amf.Buffer[0]
|
|
||||||
switch t {
|
|
||||||
case AMF0_NUMBER:
|
|
||||||
return amf.readNumber()
|
|
||||||
case AMF0_BOOLEAN:
|
|
||||||
return amf.readBool()
|
|
||||||
case AMF0_STRING:
|
|
||||||
return amf.readString()
|
|
||||||
case AMF0_OBJECT:
|
|
||||||
return amf.readObject()
|
|
||||||
case AMF0_MOVIECLIP:
|
|
||||||
RTMPPlugin.Error("This type is not supported and is reserved for future use.(AMF0_MOVIECLIP)")
|
|
||||||
case AMF0_NULL:
|
|
||||||
return amf.readNull()
|
|
||||||
case AMF0_UNDEFINED:
|
|
||||||
amf.ReadByte()
|
|
||||||
return Undefined
|
|
||||||
case AMF0_REFERENCE:
|
|
||||||
RTMPPlugin.Error("reference-type.(AMF0_REFERENCE)")
|
|
||||||
case AMF0_ECMA_ARRAY:
|
|
||||||
return amf.readECMAArray()
|
|
||||||
case AMF0_END_OBJECT:
|
|
||||||
amf.ReadByte()
|
|
||||||
return ObjectEnd
|
|
||||||
case AMF0_STRICT_ARRAY:
|
|
||||||
return amf.readStrictArray()
|
|
||||||
case AMF0_DATE:
|
|
||||||
return amf.readDate()
|
|
||||||
case AMF0_LONG_STRING,
|
|
||||||
AMF0_XML_DOCUMENT:
|
|
||||||
return amf.readLongString()
|
|
||||||
case AMF0_UNSUPPORTED:
|
|
||||||
RTMPPlugin.Error("If a type cannot be serialized a special unsupported marker can be used in place of the type.(AMF0_UNSUPPORTED)")
|
|
||||||
case AMF0_RECORDSET:
|
|
||||||
RTMPPlugin.Error("This type is not supported and is reserved for future use.(AMF0_RECORDSET)")
|
|
||||||
case AMF0_TYPED_OBJECT:
|
|
||||||
RTMPPlugin.Error("If a strongly typed object has an alias registered for its class then the type name will also be serialized. Typed objects are considered complex types and reoccurring instances can be sent by reference.(AMF0_TYPED_OBJECT)")
|
|
||||||
case AMF0_AVMPLUS_OBJECT:
|
|
||||||
RTMPPlugin.Error("AMF0_AVMPLUS_OBJECT")
|
|
||||||
default:
|
|
||||||
RTMPPlugin.Error("Unsupported type", zap.Uint8("type", t))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeObject(t AMFObject) {
|
|
||||||
if t == nil {
|
|
||||||
amf.writeNull()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
amf.Malloc(1)[0] = AMF0_OBJECT
|
|
||||||
defer amf.Write(END_OBJ)
|
|
||||||
for k, vv := range t {
|
|
||||||
switch vvv := vv.(type) {
|
|
||||||
case string:
|
|
||||||
amf.writeObjectString(k, vvv)
|
|
||||||
case float64, uint, float32, int, int16, int32, int64, uint16, uint32, uint64, uint8, int8:
|
|
||||||
amf.writeObjectNumber(k, util.ToFloat64(vv))
|
|
||||||
case bool:
|
|
||||||
amf.writeObjectBool(k, vvv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readDate() uint64 {
|
|
||||||
amf.ReadByte()
|
|
||||||
ret := amf.ReadUint64()
|
|
||||||
amf.ReadN(2) // timezone
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readStrictArray() (list []AMFValue) {
|
|
||||||
amf.ReadByte()
|
|
||||||
size := amf.ReadUint32()
|
|
||||||
for i := uint32(0); i < size; i++ {
|
|
||||||
list = append(list, amf.decodeObject())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readECMAArray() (m AMFObject) {
|
|
||||||
m = make(AMFObject, 0)
|
|
||||||
amf.ReadByte()
|
|
||||||
size := amf.ReadUint32()
|
|
||||||
for i := uint32(0); i < size; i++ {
|
|
||||||
k := amf.readString1()
|
|
||||||
v := amf.decodeObject()
|
|
||||||
if k == "" && v == ObjectEnd {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
m[k] = v
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readString() string {
|
|
||||||
if amf.Len() == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
amf.ReadByte()
|
|
||||||
return amf.readString1()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readString1() string {
|
|
||||||
return string(amf.ReadN(int(amf.ReadUint16())))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readLongString() string {
|
|
||||||
amf.ReadByte()
|
|
||||||
return string(amf.ReadN(int(amf.ReadUint32())))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readNull() any {
|
|
||||||
amf.ReadByte()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readNumber() (num float64) {
|
|
||||||
if amf.Len() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
amf.ReadByte()
|
|
||||||
return amf.ReadFloat64()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readBool() bool {
|
|
||||||
if amf.Len() == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
amf.ReadByte()
|
|
||||||
return amf.ReadByte() == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) readObject() (m AMFObject) {
|
|
||||||
if amf.Len() == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if amf.ReadByte() == AMF0_NULL {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
m = make(AMFObject, 0)
|
|
||||||
for {
|
|
||||||
k := amf.readString1()
|
|
||||||
v := amf.decodeObject()
|
|
||||||
if k == "" && v == ObjectEnd {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
m[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeSize16(l int) {
|
|
||||||
util.PutBE(amf.Malloc(2), l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeString(value string) {
|
|
||||||
amf.WriteByte(AMF0_STRING)
|
|
||||||
amf.writeSize16(len(value))
|
|
||||||
amf.WriteString(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeNull() {
|
|
||||||
amf.WriteByte(AMF0_NULL)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeBool(b bool) {
|
|
||||||
amf.WriteByte(AMF0_BOOLEAN)
|
|
||||||
if b {
|
|
||||||
amf.WriteByte(1)
|
|
||||||
}
|
|
||||||
amf.WriteByte(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeNumber(b float64) {
|
|
||||||
amf.WriteByte(AMF0_NUMBER)
|
|
||||||
amf.WriteFloat64(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeKey(key string) {
|
|
||||||
amf.writeSize16(len(key))
|
|
||||||
amf.WriteString(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeObjectString(key, value string) {
|
|
||||||
amf.writeKey(key)
|
|
||||||
amf.writeString(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeObjectBool(key string, f bool) {
|
|
||||||
amf.writeKey(key)
|
|
||||||
amf.writeBool(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (amf *AMF) writeObjectNumber(key string, value float64) {
|
|
||||||
amf.writeKey(key)
|
|
||||||
amf.writeNumber(value)
|
|
||||||
}
|
|
@@ -19,6 +19,11 @@ func NewRTMPClient(addr string) (client *NetConnection, err error) {
|
|||||||
RTMPPlugin.Error("connect url parse", zap.Error(err))
|
RTMPPlugin.Error("connect url parse", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
ps := strings.Split(u.Path, "/")
|
||||||
|
if len(ps) < 3 {
|
||||||
|
RTMPPlugin.Error("illegal rtmp url", zap.String("url", addr))
|
||||||
|
return nil, errors.New("illegal rtmp url")
|
||||||
|
}
|
||||||
isRtmps := u.Scheme == "rtmps"
|
isRtmps := u.Scheme == "rtmps"
|
||||||
if strings.Count(u.Host, ":") == 0 {
|
if strings.Count(u.Host, ":") == 0 {
|
||||||
if isRtmps {
|
if isRtmps {
|
||||||
@@ -54,12 +59,11 @@ func NewRTMPClient(addr string) (client *NetConnection, err error) {
|
|||||||
RTMPPlugin.Error("handshake", zap.Error(err))
|
RTMPPlugin.Error("handshake", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ps := strings.Split(u.Path, "/")
|
|
||||||
client.appName = ps[1]
|
client.appName = ps[1]
|
||||||
err = client.SendMessage(RTMP_MSG_CHUNK_SIZE, Uint32Message(conf.ChunkSize))
|
err = client.SendMessage(RTMP_MSG_CHUNK_SIZE, Uint32Message(conf.ChunkSize))
|
||||||
client.SendMessage(RTMP_MSG_AMF0_COMMAND, &CallMessage{
|
client.SendMessage(RTMP_MSG_AMF0_COMMAND, &CallMessage{
|
||||||
CommandMessage{"connect", 1},
|
CommandMessage{"connect", 1},
|
||||||
AMFObject{
|
map[string]any{
|
||||||
"app": client.appName,
|
"app": client.appName,
|
||||||
"flashVer": "monibuca/" + engine.Engine.Version,
|
"flashVer": "monibuca/" + engine.Engine.Version,
|
||||||
"swfUrl": addr,
|
"swfUrl": addr,
|
||||||
|
4
media.go
4
media.go
@@ -85,7 +85,7 @@ func (r *RTMPSender) Response(tid uint64, code, level string) error {
|
|||||||
m := new(ResponsePlayMessage)
|
m := new(ResponsePlayMessage)
|
||||||
m.CommandName = Response_OnStatus
|
m.CommandName = Response_OnStatus
|
||||||
m.TransactionId = tid
|
m.TransactionId = tid
|
||||||
m.Object = AMFObject{
|
m.Object = map[string]any{
|
||||||
"code": code,
|
"code": code,
|
||||||
"level": level,
|
"level": level,
|
||||||
"description": "",
|
"description": "",
|
||||||
@@ -104,7 +104,7 @@ func (r *RTMPReceiver) Response(tid uint64, code, level string) error {
|
|||||||
m := new(ResponsePublishMessage)
|
m := new(ResponsePublishMessage)
|
||||||
m.CommandName = Response_OnStatus
|
m.CommandName = Response_OnStatus
|
||||||
m.TransactionId = tid
|
m.TransactionId = tid
|
||||||
m.Infomation = AMFObject{
|
m.Infomation = map[string]any{
|
||||||
"code": code,
|
"code": code,
|
||||||
"level": level,
|
"level": level,
|
||||||
"description": "",
|
"description": "",
|
||||||
|
197
msg.go
197
msg.go
@@ -6,6 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"m7s.live/engine/v4/codec"
|
||||||
"m7s.live/engine/v4/util"
|
"m7s.live/engine/v4/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -198,36 +199,36 @@ func GetRtmpMessage(chunk *Chunk) error {
|
|||||||
// object类型要复杂点.
|
// object类型要复杂点.
|
||||||
// 第一个byte是03表示object,其后跟的是N个(key+value).最后以00 00 09表示object结束
|
// 第一个byte是03表示object,其后跟的是N个(key+value).最后以00 00 09表示object结束
|
||||||
func decodeCommandAMF0(chunk *Chunk) {
|
func decodeCommandAMF0(chunk *Chunk) {
|
||||||
amf := AMF{chunk.Body} // rtmp_amf.go, amf 是 bytes类型, 将rtmp body(payload)放到bytes.Buffer(amf)中去.
|
amf := codec.AMF{chunk.Body} // rtmp_amf.go, amf 是 bytes类型, 将rtmp body(payload)放到bytes.Buffer(amf)中去.
|
||||||
cmd := amf.readString() // rtmp_amf.go, 将payload的bytes类型转换成string类型.
|
cmd := amf.ReadShortString() // rtmp_amf.go, 将payload的bytes类型转换成string类型.
|
||||||
cmdMsg := CommandMessage{
|
cmdMsg := CommandMessage{
|
||||||
cmd,
|
cmd,
|
||||||
uint64(amf.readNumber()),
|
uint64(amf.ReadNumber()),
|
||||||
}
|
}
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "connect", "call":
|
case "connect", "call":
|
||||||
chunk.MsgData = &CallMessage{
|
chunk.MsgData = &CallMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
amf.readObject(),
|
amf.ReadObject(),
|
||||||
amf.readObject(),
|
amf.ReadObject(),
|
||||||
}
|
}
|
||||||
case "createStream":
|
case "createStream":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
chunk.MsgData = &cmdMsg
|
chunk.MsgData = &cmdMsg
|
||||||
case "play":
|
case "play":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
m := &PlayMessage{
|
m := &PlayMessage{
|
||||||
CURDStreamMessage{
|
CURDStreamMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
chunk.MessageStreamID,
|
chunk.MessageStreamID,
|
||||||
},
|
},
|
||||||
amf.readString(),
|
amf.ReadShortString(),
|
||||||
float64(-2),
|
float64(-2),
|
||||||
float64(-1),
|
float64(-1),
|
||||||
true,
|
true,
|
||||||
}
|
}
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
if v := amf.decodeObject(); v != nil {
|
if v, _ := amf.Unmarshal(); v != nil {
|
||||||
switch vv := v.(type) {
|
switch vv := v.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
@@ -245,67 +246,67 @@ func decodeCommandAMF0(chunk *Chunk) {
|
|||||||
}
|
}
|
||||||
chunk.MsgData = m
|
chunk.MsgData = m
|
||||||
case "play2":
|
case "play2":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
chunk.MsgData = &Play2Message{
|
chunk.MsgData = &Play2Message{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
uint64(amf.readNumber()),
|
uint64(amf.ReadNumber()),
|
||||||
amf.readString(),
|
amf.ReadShortString(),
|
||||||
amf.readString(),
|
amf.ReadShortString(),
|
||||||
uint64(amf.readNumber()),
|
uint64(amf.ReadNumber()),
|
||||||
amf.readString(),
|
amf.ReadShortString(),
|
||||||
}
|
}
|
||||||
case "publish":
|
case "publish":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
chunk.MsgData = &PublishMessage{
|
chunk.MsgData = &PublishMessage{
|
||||||
CURDStreamMessage{
|
CURDStreamMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
chunk.MessageStreamID,
|
chunk.MessageStreamID,
|
||||||
},
|
},
|
||||||
amf.readString(),
|
amf.ReadShortString(),
|
||||||
amf.readString(),
|
amf.ReadShortString(),
|
||||||
}
|
}
|
||||||
case "pause":
|
case "pause":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
chunk.MsgData = &PauseMessage{
|
chunk.MsgData = &PauseMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
amf.readBool(),
|
amf.ReadBool(),
|
||||||
uint64(amf.readNumber()),
|
uint64(amf.ReadNumber()),
|
||||||
}
|
}
|
||||||
case "seek":
|
case "seek":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
chunk.MsgData = &SeekMessage{
|
chunk.MsgData = &SeekMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
uint64(amf.readNumber()),
|
uint64(amf.ReadNumber()),
|
||||||
}
|
}
|
||||||
case "deleteStream", "closeStream":
|
case "deleteStream", "closeStream":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
chunk.MsgData = &CURDStreamMessage{
|
chunk.MsgData = &CURDStreamMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
uint32(amf.readNumber()),
|
uint32(amf.ReadNumber()),
|
||||||
}
|
}
|
||||||
case "releaseStream":
|
case "releaseStream":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
chunk.MsgData = &ReleaseStreamMessage{
|
chunk.MsgData = &ReleaseStreamMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
amf.readString(),
|
amf.ReadShortString(),
|
||||||
}
|
}
|
||||||
case "receiveAudio", "receiveVideo":
|
case "receiveAudio", "receiveVideo":
|
||||||
amf.readNull()
|
amf.Unmarshal()
|
||||||
chunk.MsgData = &ReceiveAVMessage{
|
chunk.MsgData = &ReceiveAVMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
amf.readBool(),
|
amf.ReadBool(),
|
||||||
}
|
}
|
||||||
case Response_Result, Response_Error, Response_OnStatus:
|
case Response_Result, Response_Error, Response_OnStatus:
|
||||||
if cmdMsg.TransactionId == 2 {
|
if cmdMsg.TransactionId == 2 {
|
||||||
chunk.MsgData = &ResponseCreateStreamMessage{
|
chunk.MsgData = &ResponseCreateStreamMessage{
|
||||||
cmdMsg, amf.readObject(), uint32(amf.readNumber()),
|
cmdMsg, amf.ReadObject(), uint32(amf.ReadNumber()),
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response := &ResponseMessage{
|
response := &ResponseMessage{
|
||||||
cmdMsg,
|
cmdMsg,
|
||||||
amf.readObject(),
|
amf.ReadObject(),
|
||||||
amf.readObject(), "",
|
amf.ReadObject(), "",
|
||||||
}
|
}
|
||||||
codef := zap.String("code", response.Infomation["code"].(string))
|
codef := zap.String("code", response.Infomation["code"].(string))
|
||||||
switch response.Infomation["level"] {
|
switch response.Infomation["level"] {
|
||||||
@@ -360,11 +361,7 @@ func (cmd *CommandMessage) GetCommand() *CommandMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (msg *CommandMessage) Encode() (b []byte) {
|
func (msg *CommandMessage) Encode() (b []byte) {
|
||||||
var amf AMF
|
return codec.MarshalAMFs(msg.CommandName, msg.TransactionId, nil)
|
||||||
amf.writeString(msg.CommandName)
|
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeNull()
|
|
||||||
return amf.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protocol control message 1.
|
// Protocol control message 1.
|
||||||
@@ -430,29 +427,23 @@ type MetadataMessage struct {
|
|||||||
// The called RPC name is passed as a parameter to the call command.
|
// The called RPC name is passed as a parameter to the call command.
|
||||||
type CallMessage struct {
|
type CallMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Object AMFObject `json:",omitempty"`
|
Object map[string]any `json:",omitempty"`
|
||||||
Optional AMFObject `json:",omitempty"`
|
Optional map[string]any `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *CallMessage) Encode() []byte {
|
func (msg *CallMessage) Encode() []byte {
|
||||||
var amf AMF
|
var amf codec.AMF
|
||||||
amf.writeString(msg.CommandName)
|
amf.Marshals(msg.CommandName, msg.TransactionId, msg.Object)
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeObject(msg.Object)
|
|
||||||
if msg.Optional != nil {
|
if msg.Optional != nil {
|
||||||
amf.writeObject(msg.Optional)
|
amf.Marshal(msg.Optional)
|
||||||
}
|
}
|
||||||
return amf.Buffer
|
return amf.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *CallMessage) Encode3() []byte {
|
func (msg *CallMessage) Encode3() []byte {
|
||||||
var amf AMF
|
var amf codec.AMF
|
||||||
amf.WriteByte(0)
|
amf.WriteByte(0)
|
||||||
amf.writeString(msg.CommandName)
|
return amf.Marshals(msg.CommandName, msg.TransactionId, msg.Object, msg.Optional)
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeObject(msg.Object)
|
|
||||||
amf.writeObject(msg.Optional)
|
|
||||||
return amf.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Stream Message.
|
// Create Stream Message.
|
||||||
@@ -502,12 +493,7 @@ type PlayMessage struct {
|
|||||||
// Reset -> 可选的布尔值或者数字定义了是否对以前的播放列表进行 flush
|
// Reset -> 可选的布尔值或者数字定义了是否对以前的播放列表进行 flush
|
||||||
|
|
||||||
func (msg *PlayMessage) Encode() []byte {
|
func (msg *PlayMessage) Encode() []byte {
|
||||||
var amf AMF
|
var amf codec.AMF
|
||||||
amf.writeString(msg.CommandName)
|
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeNull()
|
|
||||||
amf.writeString(msg.StreamName)
|
|
||||||
amf.writeNumber(-2000)
|
|
||||||
// if msg.Start > 0 {
|
// if msg.Start > 0 {
|
||||||
// amf.writeNumber(msg.Start)
|
// amf.writeNumber(msg.Start)
|
||||||
// }
|
// }
|
||||||
@@ -517,7 +503,7 @@ func (msg *PlayMessage) Encode() []byte {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// amf.writeBool(msg.Reset)
|
// amf.writeBool(msg.Reset)
|
||||||
return amf.Buffer
|
return amf.Marshals(msg.CommandName, msg.TransactionId, nil, msg.StreamName, -2000)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -591,13 +577,7 @@ type PublishMessage struct {
|
|||||||
// “live”:发布直播数据而不录制到文件
|
// “live”:发布直播数据而不录制到文件
|
||||||
|
|
||||||
func (msg *PublishMessage) Encode() []byte {
|
func (msg *PublishMessage) Encode() []byte {
|
||||||
var amf AMF
|
return codec.MarshalAMFs(msg.CommandName, msg.TransactionId, nil, msg.PublishingName, msg.PublishingType)
|
||||||
amf.writeString(msg.CommandName)
|
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeNull()
|
|
||||||
amf.writeString(msg.PublishingName)
|
|
||||||
amf.writeString(msg.PublishingType)
|
|
||||||
return amf.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek Message
|
// Seek Message
|
||||||
@@ -631,22 +611,15 @@ func (msg *PauseMessage) Encode0() {
|
|||||||
// Response Message. Server -> Response -> Client
|
// Response Message. Server -> Response -> Client
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Response Connect Message
|
// Response Connect Message
|
||||||
//
|
|
||||||
type ResponseConnectMessage struct {
|
type ResponseConnectMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Properties AMFObject `json:",omitempty"`
|
Properties map[string]any `json:",omitempty"`
|
||||||
Infomation AMFObject `json:",omitempty"`
|
Infomation map[string]any `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *ResponseConnectMessage) Encode() []byte {
|
func (msg *ResponseConnectMessage) Encode() []byte {
|
||||||
var amf AMF
|
return codec.MarshalAMFs(msg.CommandName, msg.TransactionId, msg.Properties, msg.Infomation)
|
||||||
amf.writeString(msg.CommandName)
|
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeObject(msg.Properties)
|
|
||||||
amf.writeObject(msg.Infomation)
|
|
||||||
return amf.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -654,38 +627,25 @@ func (msg *ResponseConnectMessage) Encode3() {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
// Response Call Message
|
// Response Call Message
|
||||||
//
|
|
||||||
type ResponseCallMessage struct {
|
type ResponseCallMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Object AMFObject
|
Object map[string]any
|
||||||
Response AMFObject
|
Response map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *ResponseCallMessage) Encode0() []byte {
|
func (msg *ResponseCallMessage) Encode0() []byte {
|
||||||
var amf AMF
|
return codec.MarshalAMFs(msg.CommandName, msg.TransactionId, msg.Object, msg.Response)
|
||||||
amf.writeString(msg.CommandName)
|
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeObject(msg.Object)
|
|
||||||
amf.writeObject(msg.Response)
|
|
||||||
return amf.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Response Create Stream Message
|
// Response Create Stream Message
|
||||||
//
|
|
||||||
type ResponseCreateStreamMessage struct {
|
type ResponseCreateStreamMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Object interface{} `json:",omitempty"`
|
Object any `json:",omitempty"`
|
||||||
StreamId uint32
|
StreamId uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *ResponseCreateStreamMessage) Encode() []byte {
|
func (msg *ResponseCreateStreamMessage) Encode() []byte {
|
||||||
var amf AMF
|
return codec.MarshalAMFs(msg.CommandName, msg.TransactionId, nil, msg.StreamId)
|
||||||
amf.writeString(msg.CommandName)
|
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeNull()
|
|
||||||
amf.writeNumber(float64(msg.StreamId))
|
|
||||||
return amf.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -693,23 +653,21 @@ func (msg *ResponseCreateStreamMessage) Encode3() {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
func (msg *ResponseCreateStreamMessage) Decode0(chunk *Chunk) {
|
func (msg *ResponseCreateStreamMessage) Decode0(chunk *Chunk) {
|
||||||
amf := AMF{chunk.Body}
|
amf := codec.AMF{chunk.Body}
|
||||||
msg.CommandName = amf.decodeObject().(string)
|
msg.CommandName = amf.ReadShortString()
|
||||||
msg.TransactionId = uint64(amf.decodeObject().(float64))
|
msg.TransactionId = uint64(amf.ReadNumber())
|
||||||
amf.decodeObject()
|
amf.Unmarshal()
|
||||||
msg.StreamId = uint32(amf.decodeObject().(float64))
|
msg.StreamId = uint32(amf.ReadNumber())
|
||||||
}
|
}
|
||||||
func (msg *ResponseCreateStreamMessage) Decode3(chunk *Chunk) {
|
func (msg *ResponseCreateStreamMessage) Decode3(chunk *Chunk) {
|
||||||
chunk.Body = chunk.Body[1:]
|
chunk.Body = chunk.Body[1:]
|
||||||
msg.Decode0(chunk)
|
msg.Decode0(chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Response Play Message
|
// Response Play Message
|
||||||
//
|
|
||||||
type ResponsePlayMessage struct {
|
type ResponsePlayMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Object AMFObject `json:",omitempty"`
|
Object map[string]any `json:",omitempty"`
|
||||||
Description string
|
Description string
|
||||||
StreamID uint32
|
StreamID uint32
|
||||||
}
|
}
|
||||||
@@ -718,13 +676,7 @@ func (msg *ResponsePlayMessage) GetStreamID() uint32 {
|
|||||||
return msg.StreamID
|
return msg.StreamID
|
||||||
}
|
}
|
||||||
func (msg *ResponsePlayMessage) Encode() []byte {
|
func (msg *ResponsePlayMessage) Encode() []byte {
|
||||||
var amf AMF
|
return codec.MarshalAMFs(msg.CommandName, msg.TransactionId, nil, msg.Object, msg.Description)
|
||||||
amf.writeString(msg.CommandName)
|
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeNull()
|
|
||||||
amf.writeObject(msg.Object)
|
|
||||||
amf.writeString(msg.Description)
|
|
||||||
return amf.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -732,23 +684,21 @@ func (msg *ResponsePlayMessage) Encode3() {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
func (msg *ResponsePlayMessage) Decode0(chunk *Chunk) {
|
func (msg *ResponsePlayMessage) Decode0(chunk *Chunk) {
|
||||||
amf := AMF{chunk.Body}
|
amf := codec.AMF{chunk.Body}
|
||||||
msg.CommandName = amf.decodeObject().(string)
|
msg.CommandName = amf.ReadShortString()
|
||||||
msg.TransactionId = uint64(amf.decodeObject().(float64))
|
msg.TransactionId = uint64(amf.ReadNumber())
|
||||||
msg.Object = amf.decodeObject().(AMFObject)
|
msg.Object = amf.ReadObject()
|
||||||
}
|
}
|
||||||
func (msg *ResponsePlayMessage) Decode3(chunk *Chunk) {
|
func (msg *ResponsePlayMessage) Decode3(chunk *Chunk) {
|
||||||
chunk.Body = chunk.Body[1:]
|
chunk.Body = chunk.Body[1:]
|
||||||
msg.Decode0(chunk)
|
msg.Decode0(chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Response Publish Message
|
// Response Publish Message
|
||||||
//
|
|
||||||
type ResponsePublishMessage struct {
|
type ResponsePublishMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Properties AMFObject `json:",omitempty"`
|
Properties map[string]any `json:",omitempty"`
|
||||||
Infomation AMFObject `json:",omitempty"`
|
Infomation map[string]any `json:",omitempty"`
|
||||||
StreamID uint32
|
StreamID uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -762,21 +712,14 @@ func (msg *ResponsePublishMessage) GetStreamID() uint32 {
|
|||||||
// 信息 -> level, code, description
|
// 信息 -> level, code, description
|
||||||
|
|
||||||
func (msg *ResponsePublishMessage) Encode() []byte {
|
func (msg *ResponsePublishMessage) Encode() []byte {
|
||||||
var amf AMF
|
return codec.MarshalAMFs(msg.CommandName, msg.TransactionId, msg.Properties, msg.Infomation)
|
||||||
amf.writeString(msg.CommandName)
|
|
||||||
amf.writeNumber(float64(msg.TransactionId))
|
|
||||||
amf.writeObject(msg.Properties)
|
|
||||||
amf.writeObject(msg.Infomation)
|
|
||||||
return amf.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func (msg *ResponsePublishMessage) Encode3() {
|
func (msg *ResponsePublishMessage) Encode3() {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
//
|
|
||||||
// Response Seek Message
|
// Response Seek Message
|
||||||
//
|
|
||||||
type ResponseSeekMessage struct {
|
type ResponseSeekMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Description string
|
Description string
|
||||||
@@ -788,9 +731,7 @@ func (msg *ResponseSeekMessage) Encode0() {
|
|||||||
//func (msg *ResponseSeekMessage) Encode3() {
|
//func (msg *ResponseSeekMessage) Encode3() {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//
|
|
||||||
// Response Pause Message
|
// Response Pause Message
|
||||||
//
|
|
||||||
type ResponsePauseMessage struct {
|
type ResponsePauseMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Description string
|
Description string
|
||||||
@@ -806,13 +747,11 @@ func (msg *ResponsePauseMessage) Encode0() {
|
|||||||
//func (msg *ResponsePauseMessage) Encode3() {
|
//func (msg *ResponsePauseMessage) Encode3() {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//
|
|
||||||
// Response Message
|
// Response Message
|
||||||
//
|
|
||||||
type ResponseMessage struct {
|
type ResponseMessage struct {
|
||||||
CommandMessage
|
CommandMessage
|
||||||
Properties AMFObject `json:",omitempty"`
|
Properties map[string]any `json:",omitempty"`
|
||||||
Infomation AMFObject `json:",omitempty"`
|
Infomation map[string]any `json:",omitempty"`
|
||||||
Description string
|
Description string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -43,8 +43,8 @@ const (
|
|||||||
SEND_FULL_VDIEO_MESSAGE = "Send Full Video Message"
|
SEND_FULL_VDIEO_MESSAGE = "Send Full Video Message"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newConnectResponseMessageData(objectEncoding float64) (amfobj AMFObject) {
|
func newConnectResponseMessageData(objectEncoding float64) (amfobj map[string]any) {
|
||||||
amfobj = make(AMFObject)
|
amfobj = make(map[string]any)
|
||||||
amfobj["fmsVer"] = "monibuca/" + Engine.Version
|
amfobj["fmsVer"] = "monibuca/" + Engine.Version
|
||||||
amfobj["capabilities"] = 31
|
amfobj["capabilities"] = 31
|
||||||
amfobj["mode"] = 1
|
amfobj["mode"] = 1
|
||||||
@@ -56,8 +56,8 @@ func newConnectResponseMessageData(objectEncoding float64) (amfobj AMFObject) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPublishResponseMessageData(streamid uint32, code, level string) (amfobj AMFObject) {
|
func newPublishResponseMessageData(streamid uint32, code, level string) (amfobj map[string]any) {
|
||||||
amfobj = make(AMFObject)
|
amfobj = make(map[string]any)
|
||||||
amfobj["code"] = code
|
amfobj["code"] = code
|
||||||
amfobj["level"] = level
|
amfobj["level"] = level
|
||||||
amfobj["streamid"] = streamid
|
amfobj["streamid"] = streamid
|
||||||
@@ -65,8 +65,8 @@ func newPublishResponseMessageData(streamid uint32, code, level string) (amfobj
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPlayResponseMessageData(streamid uint32, code, level string) (amfobj AMFObject) {
|
func newPlayResponseMessageData(streamid uint32, code, level string) (amfobj map[string]any) {
|
||||||
amfobj = make(AMFObject)
|
amfobj = make(map[string]any)
|
||||||
amfobj["code"] = code
|
amfobj["code"] = code
|
||||||
amfobj["level"] = level
|
amfobj["level"] = level
|
||||||
amfobj["streamid"] = streamid
|
amfobj["streamid"] = streamid
|
||||||
|
@@ -90,13 +90,13 @@ func (config *RTMPConfig) ServeTCP(conn *net.TCPConn) {
|
|||||||
m := new(ResponseConnectMessage)
|
m := new(ResponseConnectMessage)
|
||||||
m.CommandName = Response_Result
|
m.CommandName = Response_Result
|
||||||
m.TransactionId = 1
|
m.TransactionId = 1
|
||||||
m.Properties = AMFObject{
|
m.Properties = map[string]any{
|
||||||
"fmsVer": "monibuca/" + engine.Engine.Version,
|
"fmsVer": "monibuca/" + engine.Engine.Version,
|
||||||
"capabilities": 31,
|
"capabilities": 31,
|
||||||
"mode": 1,
|
"mode": 1,
|
||||||
"Author": "dexter",
|
"Author": "dexter",
|
||||||
}
|
}
|
||||||
m.Infomation = AMFObject{
|
m.Infomation = map[string]any{
|
||||||
"level": Level_Status,
|
"level": Level_Status,
|
||||||
"code": NetConnection_Connect_Success,
|
"code": NetConnection_Connect_Success,
|
||||||
"objectEncoding": nc.objectEncoding,
|
"objectEncoding": nc.objectEncoding,
|
||||||
|
Reference in New Issue
Block a user