// Copyright 2019, 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 rtmp // message_packer.go // @pure // 打包并发送 rtmp 信令 import ( "bytes" "fmt" "io" "github.com/q191201771/lal/pkg/base" "github.com/q191201771/naza/pkg/bele" ) const ( peerBandwidthLimitTypeHard = uint8(0) peerBandwidthLimitTypeSoft = uint8(1) peerBandwidthLimitTypeDynamic = uint8(2) ) type MessagePacker struct { // 1. 增加一层缓冲,避免 write 一个信令时发生多次系统调用 // 2. 因为 bytes.Buffer.Write 返回的 error 永远为 nil,所以本文件中所有对 b 的写操作都不判断返回值 b *bytes.Buffer } func NewMessagePacker() *MessagePacker { return &MessagePacker{ b: &bytes.Buffer{}, } } func (packer *MessagePacker) writeMessageHeader(csid int, bodyLen int, typeID uint8, streamID int) { // 目前这个函数只供发送信令时调用,信令的 csid 都是小于等于 63 的,如果传入的 csid 大于 63,直接 panic if csid > 63 { panic(csid) } fmt := 0 // 0 0 0 是时间戳 _, _ = packer.b.Write([]byte{uint8(fmt<<6 | csid), 0, 0, 0}) _ = bele.WriteBEUint24(packer.b, uint32(bodyLen)) _ = packer.b.WriteByte(typeID) _ = bele.WriteLE(packer.b, uint32(streamID)) } func (packer *MessagePacker) writeProtocolControlMessage(writer io.Writer, typeID uint8, val int) error { packer.writeMessageHeader(csidProtocolControl, 4, typeID, 0) _ = bele.WriteBE(packer.b, uint32(val)) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writeChunkSize(writer io.Writer, val int) error { return packer.writeProtocolControlMessage(writer, base.RTMPTypeIDSetChunkSize, val) } func (packer *MessagePacker) writeWinAckSize(writer io.Writer, val int) error { return packer.writeProtocolControlMessage(writer, base.RTMPTypeIDWinAckSize, val) } func (packer *MessagePacker) writePeerBandwidth(writer io.Writer, val int, limitType uint8) error { packer.writeMessageHeader(csidProtocolControl, 5, base.RTMPTypeIDBandwidth, 0) _ = bele.WriteBE(packer.b, uint32(val)) _ = packer.b.WriteByte(limitType) _, err := packer.b.WriteTo(writer) return err } // @param isPush: 推流为true,拉流为false func (packer *MessagePacker) writeConnect(writer io.Writer, appName, tcURL string, isPush bool) error { packer.writeMessageHeader(csidOverConnection, 0, base.RTMPTypeIDCommandMessageAMF0, 0) _ = AMF0.WriteString(packer.b, "connect") _ = AMF0.WriteNumber(packer.b, float64(tidClientConnect)) var objs []ObjectPair objs = append(objs, ObjectPair{Key: "app", Value: appName}) objs = append(objs, ObjectPair{Key: "type", Value: "nonprivate"}) var flashVer string if isPush { flashVer = fmt.Sprintf("FMLE/3.0 (compatible; %s)", base.LALRTMPPushSessionConnectVersion) } else { flashVer = "LNX 9,0,124,2" } objs = append(objs, ObjectPair{Key: "flashVer", Value: flashVer}) // fpad True if proxy is being used. objs = append(objs, ObjectPair{Key: "fpad", Value: false}) objs = append(objs, ObjectPair{Key: "tcUrl", Value: tcURL}) _ = AMF0.WriteObject(packer.b, objs) raw := packer.b.Bytes() bele.BEPutUint24(raw[4:], uint32(len(raw)-12)) _, err := packer.b.WriteTo(writer) return err } // @param objectEncoding 设置0或者3,表示是AMF0或AMF3,上层可根据connect信令中的objectEncoding值设置该值 func (packer *MessagePacker) writeConnectResult(writer io.Writer, tid int, objectEncoding int) error { packer.writeMessageHeader(csidOverConnection, 0, base.RTMPTypeIDCommandMessageAMF0, 0) _ = AMF0.WriteString(packer.b, "_result") _ = AMF0.WriteNumber(packer.b, float64(tid)) objs := []ObjectPair{ {Key: "fmsVer", Value: "FMS/3,0,1,123"}, {Key: "capabilities", Value: 31}, } _ = AMF0.WriteObject(packer.b, objs) objs = []ObjectPair{ {Key: "level", Value: "status"}, {Key: "code", Value: "NetConnection.Connect.Success"}, {Key: "description", Value: "Connection succeeded."}, {Key: "objectEncoding", Value: objectEncoding}, {Key: "version", Value: base.LALRTMPConnectResultVersion}, } _ = AMF0.WriteObject(packer.b, objs) raw := packer.b.Bytes() bele.BEPutUint24(raw[4:], uint32(len(raw)-12)) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writeCreateStream(writer io.Writer) error { // 25 = 15 + 9 + 1 packer.writeMessageHeader(csidOverConnection, 25, base.RTMPTypeIDCommandMessageAMF0, 0) _ = AMF0.WriteString(packer.b, "createStream") _ = AMF0.WriteNumber(packer.b, float64(tidClientCreateStream)) _ = AMF0.WriteNull(packer.b) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writeCreateStreamResult(writer io.Writer, tid int) error { packer.writeMessageHeader(csidOverConnection, 29, base.RTMPTypeIDCommandMessageAMF0, 0) _ = AMF0.WriteString(packer.b, "_result") _ = AMF0.WriteNumber(packer.b, float64(tid)) _ = AMF0.WriteNull(packer.b) _ = AMF0.WriteNumber(packer.b, float64(MSID1)) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writePlay(writer io.Writer, streamName string, streamID int) error { packer.writeMessageHeader(csidOverStream, 0, base.RTMPTypeIDCommandMessageAMF0, streamID) _ = AMF0.WriteString(packer.b, "play") _ = AMF0.WriteNumber(packer.b, float64(tidClientPlay)) _ = AMF0.WriteNull(packer.b) _ = AMF0.WriteString(packer.b, streamName) raw := packer.b.Bytes() bele.BEPutUint24(raw[4:], uint32(len(raw)-12)) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writePublish(writer io.Writer, appName string, streamName string, streamID int) error { packer.writeMessageHeader(csidOverStream, 0, base.RTMPTypeIDCommandMessageAMF0, streamID) _ = AMF0.WriteString(packer.b, "publish") _ = AMF0.WriteNumber(packer.b, float64(tidClientPublish)) _ = AMF0.WriteNull(packer.b) _ = AMF0.WriteString(packer.b, streamName) _ = AMF0.WriteString(packer.b, appName) raw := packer.b.Bytes() bele.BEPutUint24(raw[4:], uint32(len(raw)-12)) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writeOnStatusPublish(writer io.Writer, streamID int) error { packer.writeMessageHeader(csidOverStream, 105, base.RTMPTypeIDCommandMessageAMF0, streamID) _ = AMF0.WriteString(packer.b, "onStatus") _ = AMF0.WriteNumber(packer.b, 0) _ = AMF0.WriteNull(packer.b) objs := []ObjectPair{ {Key: "level", Value: "status"}, {Key: "code", Value: "NetStream.Publish.Start"}, {Key: "description", Value: "Start publishing"}, } _ = AMF0.WriteObject(packer.b, objs) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writeOnStatusPlay(writer io.Writer, streamID int) error { packer.writeMessageHeader(csidOverStream, 96, base.RTMPTypeIDCommandMessageAMF0, streamID) _ = AMF0.WriteString(packer.b, "onStatus") _ = AMF0.WriteNumber(packer.b, 0) _ = AMF0.WriteNull(packer.b) objs := []ObjectPair{ {Key: "level", Value: "status"}, {Key: "code", Value: "NetStream.Play.Start"}, {Key: "description", Value: "Start live"}, } _ = AMF0.WriteObject(packer.b, objs) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writeStreamIsRecorded(writer io.Writer, streamID uint32) error { packer.writeMessageHeader(csidProtocolControl, 6, base.RTMPTypeIDUserControl, 0) _ = bele.WriteBE(packer.b, uint16(base.RTMPUserControlRecorded)) _ = bele.WriteBE(packer.b, uint32(streamID)) _, err := packer.b.WriteTo(writer) return err } func (packer *MessagePacker) writeStreamBegin(writer io.Writer, streamID uint32) error { packer.writeMessageHeader(csidProtocolControl, 6, base.RTMPTypeIDUserControl, 0) _ = bele.WriteBE(packer.b, uint16(base.RTMPUserControlStreamBegin)) _ = bele.WriteBE(packer.b, uint32(streamID)) _, err := packer.b.WriteTo(writer) return err }