You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lal/pkg/base/t_rtmp.go

170 lines
5.5 KiB
Go

// Copyright 2020, 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 base
import (
"encoding/hex"
"fmt"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazabytes"
)
const (
// RtmpTypeIdAudio spec-rtmp_specification_1.0.pdf
// 7.1. Types of Messages
RtmpTypeIdAudio uint8 = 8
RtmpTypeIdVideo uint8 = 9
RtmpTypeIdMetadata uint8 = 18 // RtmpTypeIdDataMessageAmf0
RtmpTypeIdSetChunkSize uint8 = 1
// RtmpTypeIdAck 和 RtmpTypeIdWinAckSize 的含义:
//
// 一端向另一端发送 RtmpTypeIdWinAckSize ,要求对端每收够一定数据(一定数据的阈值包含在 RtmpTypeIdWinAckSize 信令中)后,向本端回复 RtmpTypeIdAck 。
//
// 常见的应用场景:数据发送端要求数据接收端定时发送心跳信令给本端。
RtmpTypeIdAck uint8 = 3
RtmpTypeIdUserControl uint8 = 4
// RtmpTypeIdWinAckSize 见 RtmpTypeIdAck
RtmpTypeIdWinAckSize uint8 = 5
RtmpTypeIdBandwidth uint8 = 6
RtmpTypeIdCommandMessageAmf3 uint8 = 17
RtmpTypeIdCommandMessageAmf0 uint8 = 20
RtmpTypeIdAggregateMessage uint8 = 22
4 years ago
// RtmpUserControlStreamBegin RtmpUserControlXxx...
//
4 years ago
// user control message type
//
RtmpUserControlStreamBegin uint8 = 0
RtmpUserControlRecorded uint8 = 4
RtmpUserControlPingRequest uint8 = 6
RtmpUserControlPingResponse uint8 = 7
// RtmpFrameTypeKey spec-video_file_format_spec_v10.pdf
// Video tags
// VIDEODATA
// FrameType UB[4]
// CodecId UB[4]
// AVCVIDEOPACKET
// AVCPacketType UI8
// CompositionTime SI24
// Data UI8[n]
RtmpFrameTypeKey uint8 = 1
RtmpFrameTypeInter uint8 = 2
RtmpCodecIdAvc uint8 = 7
RtmpCodecIdHevc uint8 = 12
// RtmpAvcPacketTypeSeqHeader RtmpAvcPacketTypeNalu RtmpHevcPacketTypeSeqHeader RtmpHevcPacketTypeNalu
// 注意按照标准文档上描述PacketType还有可能为2
// 2: AVC end of sequence (lower level NALU sequence ender is not required or supported)
//
// 我自己遇到过在流结尾时,对端发送 27 02 00 00 00的情况比如我们的使用wontcry.flv的单元测试最后一个包
//
RtmpAvcPacketTypeSeqHeader uint8 = 0
RtmpAvcPacketTypeNalu uint8 = 1
RtmpHevcPacketTypeSeqHeader = RtmpAvcPacketTypeSeqHeader
RtmpHevcPacketTypeNalu = RtmpAvcPacketTypeNalu
RtmpAvcKeyFrame = RtmpFrameTypeKey<<4 | RtmpCodecIdAvc
RtmpHevcKeyFrame = RtmpFrameTypeKey<<4 | RtmpCodecIdHevc
RtmpAvcInterFrame = RtmpFrameTypeInter<<4 | RtmpCodecIdAvc
RtmpHevcInterFrame = RtmpFrameTypeInter<<4 | RtmpCodecIdHevc
// RtmpSoundFormatAac spec-video_file_format_spec_v10.pdf
// Audio tags
// AUDIODATA
// SoundFormat UB[4]
// SoundRate UB[2]
// SoundSize UB[1]
// SoundType UB[1]
// AACAUDIODATA
// AACPacketType UI8
// Data UI8[n]
// 注意视频的CodecId是后4位音频是前4位
RtmpSoundFormatG711A uint8 = 7
RtmpSoundFormatG711U uint8 = 8
RtmpSoundFormatAac uint8 = 10
RtmpAacPacketTypeSeqHeader = 0
RtmpAacPacketTypeRaw = 1
)
type RtmpHeader struct {
Csid int
MsgLen uint32 // 不包含header的大小
MsgTypeId uint8 // 8 audio 9 video 18 metadata
MsgStreamId int
TimestampAbs uint32 // dts, 经过计算得到的流上的绝对时间戳,单位毫秒
}
type RtmpMsg struct {
Header RtmpHeader
Payload []byte // Payload不包含Header内容。如果需要将RtmpMsg序列化成RTMP chunk可调用rtmp.ChunkDivider相关的函数
}
func (msg RtmpMsg) IsAvcKeySeqHeader() bool {
return msg.Header.MsgTypeId == RtmpTypeIdVideo && msg.Payload[0] == RtmpAvcKeyFrame && msg.Payload[1] == RtmpAvcPacketTypeSeqHeader
}
func (msg RtmpMsg) IsHevcKeySeqHeader() bool {
return msg.Header.MsgTypeId == RtmpTypeIdVideo && msg.Payload[0] == RtmpHevcKeyFrame && msg.Payload[1] == RtmpHevcPacketTypeSeqHeader
}
func (msg RtmpMsg) IsVideoKeySeqHeader() bool {
return msg.IsAvcKeySeqHeader() || msg.IsHevcKeySeqHeader()
}
func (msg RtmpMsg) IsAvcKeyNalu() bool {
return msg.Header.MsgTypeId == RtmpTypeIdVideo && msg.Payload[0] == RtmpAvcKeyFrame && msg.Payload[1] == RtmpAvcPacketTypeNalu
}
func (msg RtmpMsg) IsHevcKeyNalu() bool {
return msg.Header.MsgTypeId == RtmpTypeIdVideo && msg.Payload[0] == RtmpHevcKeyFrame && msg.Payload[1] == RtmpHevcPacketTypeNalu
}
func (msg RtmpMsg) IsVideoKeyNalu() bool {
return msg.IsAvcKeyNalu() || msg.IsHevcKeyNalu()
}
func (msg RtmpMsg) IsAacSeqHeader() bool {
return msg.Header.MsgTypeId == RtmpTypeIdAudio && msg.AudioCodecId() == RtmpSoundFormatAac && msg.Payload[1] == RtmpAacPacketTypeSeqHeader
}
func (msg RtmpMsg) VideoCodecId() uint8 {
return msg.Payload[0] & 0xF
}
func (msg RtmpMsg) AudioCodecId() uint8 {
return msg.Payload[0] >> 4
}
func (msg RtmpMsg) Clone() (ret RtmpMsg) {
ret.Header = msg.Header
ret.Payload = make([]byte, len(msg.Payload))
copy(ret.Payload, msg.Payload)
return
}
func (msg RtmpMsg) Dts() uint32 {
return msg.Header.TimestampAbs
}
// Pts
//
// 注意只有视频才能调用该函数获取pts音频的dts和pts都直接使用 RtmpMsg.Header.TimestampAbs
func (msg RtmpMsg) Pts() uint32 {
return msg.Header.TimestampAbs + bele.BeUint24(msg.Payload[2:])
}
func (msg RtmpMsg) DebugString() string {
return fmt.Sprintf("type=%d,len=%d,dts=%d, payload=%s",
msg.Header.MsgTypeId, msg.Header.MsgLen, msg.Header.TimestampAbs, hex.Dump(nazabytes.Prefix(msg.Payload, 64)))
}