[feat] 支持rtsp pub h265 (lalserver支持接收rtsp h265的推流)

pull/33/head
q191201771 4 years ago
parent bb9cbb9481
commit d7df4d9623

@ -14,9 +14,7 @@
<br>
<a title="pr" target="_blank" href="https://github.com/q191201771/lal/pulls"><img src="https://img.shields.io/github/issues-pr-closed/q191201771/lal.svg?style=flat-square&color=FF9966"></a>
<a title="hits" target="_blank" href="https://github.com/q191201771/lal"><img src="https://hits.b3log.org/q191201771/lal.svg?style=flat-square"></a>
<a title="language" target="_blank" href="https://github.com/q191201771/lal"><img src="https://img.shields.io/github/languages/count/q191201771/lal.svg?style=flat-square"></a>
<a title="toplanguage" target="_blank" href="https://github.com/q191201771/lal"><img src="https://img.shields.io/github/languages/top/q191201771/lal.svg?style=flat-square"></a>
<a title="godoc" target="_blank" href="https://godoc.org/github.com/q191201771/lal"><img src="http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square"></a>
<br>
</p>
@ -34,7 +32,7 @@
| - | - | - | - | - | - |
| aac | ✔ | ✔ | ✔ | ✔ | ✔ |
| avc/h264 | ✔ | ✔ | ✔ | ✔ | ✔ |
| hevc/h265 | ✔ | - | - | ✔ | - |
| hevc/h265 | ✔ | | - | ✔ | - |
表格含义见: [《流媒体传输连接类型之session client, server, pub, sub, push, pull》](https://pengrl.com/p/20080)

@ -218,7 +218,7 @@ func analysisVideoTag(tag httpflv.Tag) {
buf.WriteString(fmt.Sprintf("delay: %dms", delay))
}
}
buf.WriteString(fmt.Sprintf(" [%s] ", hevc.ParseNALUTypeReadable(body[i+4])))
buf.WriteString(fmt.Sprintf(" [%s(%d)] ", hevc.ParseNALUTypeReadable(body[i+4]), body[i+4]))
}
i = i + 4 + int(naluLen)
}

@ -48,7 +48,7 @@ func (obs *Obs) OnAVPacket(pkt base.AVPacket) {
nazalog.Debugf("type=%d, ts=%d, len=%d", pkt.PayloadType, pkt.Timestamp, len(pkt.Payload))
switch pkt.PayloadType {
case base.RTPPacketTypeAVC:
case base.RTPPacketTypeAVCOrHEVC:
// TODO chef: 由于存在多nalu情况需要进行拆分
_, _ = avcFp.Write([]byte{0, 0, 0, 1})
_, _ = avcFp.Write(pkt.Payload)

@ -8,9 +8,12 @@
package base
type AVPacketPT int
var (
AVPacketPTAVC = 96
AVPacketPTAAC = 97
AVPacketPTAVC AVPacketPT = RTPPacketTypeAVCOrHEVC
AVPacketPTAAC AVPacketPT = RTPPacketTypeAAC
AVPacketPTHEVC AVPacketPT = 98
)
// 目前供package rtsp使用。以后可能被多个package使用。
@ -18,6 +21,6 @@ var (
// 使用AVPacket的地方应注明各字段的含义。
type AVPacket struct {
Timestamp uint32
PayloadType int
PayloadType AVPacketPT
Payload []byte
}

@ -46,9 +46,10 @@ const (
RTMPHEVCPacketTypeSeqHeader = RTMPAVCPacketTypeSeqHeader
RTMPHEVCPacketTypeNALU = RTMPAVCPacketTypeNALU
RTMPAVCKeyFrame = RTMPFrameTypeKey<<4 | RTMPCodecIDAVC
RTMPHEVCKeyFrame = RTMPFrameTypeKey<<4 | RTMPCodecIDHEVC
RTMPAVCInterFrame = RTMPFrameTypeInter<<4 | RTMPCodecIDAVC
RTMPAVCKeyFrame = RTMPFrameTypeKey<<4 | RTMPCodecIDAVC
RTMPHEVCKeyFrame = RTMPFrameTypeKey<<4 | RTMPCodecIDHEVC
RTMPAVCInterFrame = RTMPFrameTypeInter<<4 | RTMPCodecIDAVC
RTMPHEVCInterFrame = RTMPFrameTypeInter<<4 | RTMPCodecIDHEVC
// spec-video_file_format_spec_v10.pdf
// Audio tags

@ -9,6 +9,7 @@
package base
const (
RTPPacketTypeAVC = 96 // 注意RTSP SDP中HEVC也使用96 TODO chef: 是否需要重命名
RTPPacketTypeAAC = 97
// 注意AVC和HEVC都可能使用96所以不能直接通过96判断是AVC还是HEVC
RTPPacketTypeAVCOrHEVC = 96
RTPPacketTypeAAC = 97
)

@ -65,6 +65,9 @@ var (
// e.g. lal0.12.3
LALHTTPTSSubSessionServer string
// e.g. lal0.12.3
LALHTTPAPIServer string
)
// - rtmp handshake random buf
@ -91,6 +94,9 @@ var (
//
// - httpts sub
// - `server:`
//
// - http api
// - `server:`
func init() {
LALVersionDot = strings.TrimPrefix(LALVersion, "v")
@ -105,6 +111,7 @@ func init() {
LALHLSTSServer = LALLibraryName + LALVersionDot
LALRTSPOptionsResponseServer = LALLibraryName + LALVersionDot
LALHTTPTSSubSessionServer = LALLibraryName + LALVersionDot
LALHTTPAPIServer = LALLibraryName + LALVersionDot
LALHTTPFLVPullSessionUA = LALLibraryName + "/" + LALVersionDot

@ -21,6 +21,14 @@ import (
//
// ISO_IEC_23008-2_2013.pdf
// NAL Unit Header
//
// +---------------+---------------+
// |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |F| Type | LayerId | TID |
// +-------------+-----------------+
var ErrHEVC = errors.New("lal.hevc: fxxk")
var (
@ -46,6 +54,9 @@ var (
)
type Context struct {
PicWidthInLumaSamples uint32 // sps
PicHeightInLumaSamples uint32 // sps
configurationVersion uint8
generalProfileSpace uint8
@ -60,11 +71,9 @@ type Context struct {
numTemporalLayers uint8
temporalIdNested uint8
chromaFormat uint8
bitDepthLumaMinus8 uint8
bitDepthChromaMinus8 uint8
picWidthInLumaSamples uint32
picHeightInLumaSamples uint32
chromaFormat uint8
bitDepthLumaMinus8 uint8
bitDepthChromaMinus8 uint8
}
func ParseNALUTypeReadable(v uint8) string {
@ -75,6 +84,7 @@ func ParseNALUTypeReadable(v uint8) string {
return b
}
// @param v 第一个字节
func ParseNALUType(v uint8) uint8 {
// 6 bit in middle
// 0*** ***0
@ -178,7 +188,7 @@ func ParseVPSSPSPPSFromSeqHeader(payload []byte) (vps, sps, pps []byte, err erro
func BuildSeqHeaderFromVPSSPSPPS(vps, sps, pps []byte) ([]byte, error) {
var sh []byte
sh = make([]byte, 27)
sh = make([]byte, 43+len(vps)+len(sps)+len(pps))
sh[0] = 0x1c
sh[1] = 0x0
sh[2] = 0x0
@ -237,6 +247,26 @@ func BuildSeqHeaderFromVPSSPSPPS(vps, sps, pps []byte) ([]byte, error) {
// unsigned int(2) lengthSizeMinusOne;
sh[26] = 0<<6 | ctx.numTemporalLayers<<3 | ctx.temporalIdNested<<2 | ctx.lengthSizeMinusOne
// num of vps sps pps
sh[27] = 0x03
i := 28
sh[i] = NALUTypeVPS
// num of vps
bele.BEPutUint16(sh[i+1:], 1)
// length
bele.BEPutUint16(sh[i+3:], uint16(len(vps)))
copy(sh[i+5:], vps)
i = i + 5 + len(vps)
sh[i] = NALUTypeSPS
bele.BEPutUint16(sh[i+1:], 1)
bele.BEPutUint16(sh[i+3:], uint16(len(sps)))
copy(sh[i+5:], sps)
i = i + 5 + len(sps)
sh[i] = NALUTypePPS
bele.BEPutUint16(sh[i+1:], 1)
bele.BEPutUint16(sh[i+3:], uint16(len(pps)))
copy(sh[i+5:], pps)
return sh, nil
}
@ -323,10 +353,10 @@ func ParseSPS(sps []byte, ctx *Context) error {
}
}
if ctx.picWidthInLumaSamples, err = br.ReadGolomb(); err != nil {
if ctx.PicWidthInLumaSamples, err = br.ReadGolomb(); err != nil {
return err
}
if ctx.picHeightInLumaSamples, err = br.ReadGolomb(); err != nil {
if ctx.PicHeightInLumaSamples, err = br.ReadGolomb(); err != nil {
return err
}

File diff suppressed because one or more lines are too long

@ -8,7 +8,9 @@
package hls
import "errors"
import (
"errors"
)
// TODO chef:
// - 支持HEVC
@ -31,6 +33,8 @@ import "errors"
var ErrHLS = errors.New("lal.hls: fxxk")
var _ StreamerObserver = &Muxer{}
var audNal = []byte{
0x00, 0x00, 0x00, 0x01, 0x09, 0xf0,
}

@ -13,7 +13,7 @@ import (
"net"
"sync"
log "github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/naza/pkg/nazalog"
)
type ServerObserver interface {
@ -54,7 +54,7 @@ func (server *Server) Listen() (err error) {
if server.ln, err = net.Listen("tcp", server.config.SubListenAddr); err != nil {
return
}
log.Infof("start httpflv server listen. addr=%s", server.config.SubListenAddr)
nazalog.Infof("start httpflv server listen. addr=%s", server.config.SubListenAddr)
}
if server.config.EnableHTTPS {
@ -67,7 +67,7 @@ func (server *Server) Listen() (err error) {
if server.httpsLn, err = tls.Listen("tcp", server.config.HTTPSAddr, tlsConfig); err != nil {
return
}
log.Infof("start httpsflv server listen. addr=%s", server.config.HTTPSAddr)
nazalog.Infof("start httpsflv server listen. addr=%s", server.config.HTTPSAddr)
}
return
@ -113,31 +113,31 @@ func (server *Server) RunLoop() error {
func (server *Server) Dispose() {
if server.ln != nil {
if err := server.ln.Close(); err != nil {
log.Error(err)
nazalog.Error(err)
}
}
if server.httpsLn != nil {
if err := server.httpsLn.Close(); err != nil {
log.Error(err)
nazalog.Error(err)
}
}
}
func (server *Server) handleConnect(conn net.Conn) {
log.Infof("accept a httpflv connection. remoteAddr=%s", conn.RemoteAddr().String())
nazalog.Infof("accept a httpflv connection. remoteAddr=%s", conn.RemoteAddr().String())
session := NewSubSession(conn)
if err := session.ReadRequest(); err != nil {
log.Errorf("[%s] read httpflv SubSession request error. err=%v", session.UniqueKey, err)
nazalog.Errorf("[%s] read httpflv SubSession request error. err=%v", session.UniqueKey, err)
return
}
log.Debugf("[%s] < read http request. uri=%s", session.UniqueKey, session.URI)
nazalog.Debugf("[%s] < read http request. uri=%s", session.UniqueKey, session.URI)
if !server.obs.OnNewHTTPFLVSubSession(session) {
session.Dispose()
}
err := session.RunLoop()
log.Debugf("[%s] httpflv sub session loop done. err=%v", session.UniqueKey, err)
nazalog.Debugf("[%s] httpflv sub session loop done. err=%v", session.UniqueKey, err)
server.obs.OnDelHTTPFLVSubSession(session)
}

@ -12,6 +12,8 @@ import (
"fmt"
"sync"
"github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/naza/pkg/nazastring"
"github.com/q191201771/lal/pkg/httpts"
@ -389,6 +391,8 @@ func (group *Group) OnAVPacket(pkt base.AVPacket) {
switch pkt.PayloadType {
case base.AVPacketPTAVC:
fallthrough
case base.AVPacketPTHEVC:
h.TimestampAbs = pkt.Timestamp
h.MsgStreamID = rtmp.MSID1
@ -396,24 +400,33 @@ func (group *Group) OnAVPacket(pkt base.AVPacket) {
h.CSID = rtmp.CSIDVideo
h.MsgLen = uint32(len(pkt.Payload)) + 5
msg.Payload = make([]byte, h.MsgLen)
// TODO chef: 这段代码应该放在更合适的地方或者在AVPacket中标识是否包含关键帧
key := false
for i := 0; i != len(pkt.Payload); {
naluSize := int(bele.BEUint32(pkt.Payload[i:]))
t := pkt.Payload[i+4] & 0x1F
if t == avc.NALUTypeIDRSlice {
key = true
t := avc.ParseNALUType(pkt.Payload[i+4])
switch pkt.PayloadType {
case base.AVPacketPTAVC:
if t == avc.NALUTypeIDRSlice {
msg.Payload[0] = base.RTMPAVCKeyFrame
} else {
msg.Payload[0] = base.RTMPAVCInterFrame
}
msg.Payload[1] = base.RTMPAVCPacketTypeNALU
case base.AVPacketPTHEVC:
if t == hevc.NALUTypeSliceIDR || t == hevc.NALUTypeSliceIDRNLP {
msg.Payload[0] = base.RTMPHEVCKeyFrame
} else {
msg.Payload[0] = base.RTMPHEVCInterFrame
}
msg.Payload[1] = base.RTMPHEVCPacketTypeNALU
}
i += 4 + naluSize
}
msg.Payload = make([]byte, h.MsgLen)
if key {
msg.Payload[0] = base.RTMPAVCKeyFrame
} else {
msg.Payload[0] = base.RTMPAVCInterFrame
}
msg.Payload[1] = base.RTMPAVCPacketTypeNALU
msg.Payload[2] = 0x0 // cts
msg.Payload[3] = 0x0
msg.Payload[4] = 0x0
@ -429,7 +442,7 @@ func (group *Group) OnAVPacket(pkt base.AVPacket) {
msg.Payload = make([]byte, h.MsgLen)
msg.Payload[0] = 0xAF
msg.Payload[1] = 0x1
msg.Payload[1] = base.RTMPAACPacketTypeRaw
copy(msg.Payload[2:], pkt.Payload)
default:
nazalog.Errorf("unknown payload type. pt=%d", pkt.PayloadType)
@ -476,11 +489,23 @@ func (group *Group) broadcastMetadataAndSeqHeader() {
var vsh []byte
var err error
if group.isHEVC() {
metadata, err = rtmp.BuildMetadata(-1, -1, int(base.RTMPSoundFormatAAC), int(base.RTMPCodecIDHEVC))
var ctx hevc.Context
if err := hevc.ParseSPS(group.sps, &ctx); err != nil {
nazalog.Errorf("parse sps failed. err=%+v", err)
return
}
metadata, err = rtmp.BuildMetadata(int(ctx.PicWidthInLumaSamples), int(ctx.PicHeightInLumaSamples), int(base.RTMPSoundFormatAAC), int(base.RTMPCodecIDHEVC))
if err != nil {
nazalog.Errorf("build metadata failed. err=%+v", err)
return
}
vsh, err = hevc.BuildSeqHeaderFromVPSSPSPPS(group.vps, group.sps, group.pps)
if err != nil {
nazalog.Errorf("build seq header failed. err=%+v", err)
return
}
} else {
ctx, err := avc.ParseSPS(group.sps)
if err != nil {
@ -495,7 +520,7 @@ func (group *Group) broadcastMetadataAndSeqHeader() {
}
vsh, err = avc.BuildSeqHeaderFromSPSPPS(group.sps, group.pps)
if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err)
nazalog.Errorf("build seq header failed. err=%+v", err)
return
}
}

@ -0,0 +1,123 @@
// 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 logic
import (
"encoding/json"
"net"
"net/http"
"time"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/bininfo"
"github.com/q191201771/naza/pkg/nazalog"
)
const httpAPIVersion = "v0.0.1"
const CodeSucc = 0
const DespSucc = "succ"
var startTime string
type HTTPAPIServer struct {
addr string
ln net.Listener
}
type HTTPResponseBasic struct {
Code int `json:"code"`
Desp string `json:"desp"`
}
type APILalInfo struct {
HTTPResponseBasic
BinInfo string `json:"bin_info"`
LalVersion string `json:"lal_version"`
APIVersion string `json:"api_version"`
StartTime string `json:"start_time"`
}
type APIStatAllGroup struct {
HTTPResponseBasic
Groups []StatGroupItem `json:"groups"`
}
type StatGroupItem struct {
StreamName string `json:"stream_name"`
AudioCodec string `json:"audio_codec"`
VideoCodec string `json:"video_codec"`
VideoWidth string `json:"video_width"`
VideoHeight string `json:"video_height"`
}
type StatPub struct {
StatSession
}
type StatSub struct {
StatSession
}
type StatSession struct {
Protocol string `json:"protocol"`
StartTime string `json:"start_time"`
RemoteAddr string `json:"remote_addr"`
Bitrate string `json:"bitrate"`
}
func NewHTTPAPIServer(addr string) *HTTPAPIServer {
return &HTTPAPIServer{
addr: addr,
}
}
func (h *HTTPAPIServer) Listen() (err error) {
if h.ln, err = net.Listen("tcp", h.addr); err != nil {
return
}
nazalog.Infof("start httpapi server listen. addr=%s", h.addr)
return
}
func (h *HTTPAPIServer) Runloop() error {
mux := http.NewServeMux()
mux.HandleFunc("/api/lal_info", h.lalInfo)
mux.HandleFunc("/api/stat/group", h.statGroup)
mux.HandleFunc("/api/stat/all_group", h.statAllGroup)
var srv http.Server
srv.Handler = mux
return srv.Serve(h.ln)
}
func (h *HTTPAPIServer) lalInfo(w http.ResponseWriter, req *http.Request) {
var v APILalInfo
v.Code = CodeSucc
v.Desp = DespSucc
v.BinInfo = bininfo.StringifySingleLine()
v.LalVersion = base.LALVersion
v.APIVersion = httpAPIVersion
v.StartTime = startTime
resp, _ := json.Marshal(v)
w.Header().Add("Server", base.LALHTTPAPIServer)
_, _ = w.Write(resp)
}
func (h *HTTPAPIServer) statGroup(w http.ResponseWriter, req *http.Request) {
}
func (h *HTTPAPIServer) statAllGroup(w http.ResponseWriter, req *http.Request) {
}
func init() {
startTime = time.Now().String()
}

@ -0,0 +1,23 @@
// 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 logic_test
import (
"testing"
)
func TestHTTPAPIServer(t *testing.T) {
//s := logic.NewHTTPAPIServer(":8083")
//if err := s.Listen(); err != nil {
// nazalog.Error(err)
// return
//}
//err := s.Runloop()
//nazalog.Error(err)
}

@ -293,3 +293,7 @@ func (sm *ServerManager) getGroup(appName string, streamName string) *Group {
}
return group
}
func (sm *ServerManager) statAllGroup() {
}

@ -53,17 +53,21 @@ const (
// 30-31 undefined -
const (
NALUTypeSingleMax = 23
NALUTypeSTAPA = 24 // one packet, multiple nals
NALUTypeFUA = 28
NALUTypeAVCSingleMax = 23
NALUTypeAVCSTAPA = 24 // one packet, multiple nals
NALUTypeAVCFUA = 28
)
const (
PositionTypeSingle uint8 = 1 // <= NALUTypeSingleMax
PositionTypeFUAStart uint8 = 2 // NALUTypeFUA
PositionTypeFUAMiddle uint8 = 3 // NALUTypeFUA
PositionTypeFUAEnd uint8 = 4 // NALUTypeFUA
PositionTypeSTAPA uint8 = 5 // NALUTypeSTAPA
NALUTypeHEVCFUA = 49
)
const (
PositionTypeSingle uint8 = 1
PositionTypeFUAStart uint8 = 2
PositionTypeFUAMiddle uint8 = 3
PositionTypeFUAEnd uint8 = 4
PositionTypeSTAPA uint8 = 5
)
type RTPHeader struct {

@ -10,14 +10,11 @@ package rtprtcp
import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
// 传入RTP包合成帧数据并回调。
// 一路音频或一路视频各对应一个对象。
// 目前支持AVC和AAC MPEG4-GENERIC/44100/2
// 后续增加其他格式,可能会拆分出一些子结构体
// 目前支持AVCHEVC和AAC MPEG4-GENERIC/44100/2
type RTPPacketListItem struct {
packet RTPPacket
@ -30,7 +27,7 @@ type RTPPacketList struct {
}
type RTPUnpacker struct {
payloadType int
payloadType base.AVPacketPT
clockRate int
maxSize int
onAVPacket OnAVPacket
@ -42,14 +39,14 @@ type RTPUnpacker struct {
// @param pkt: pkt.Timestamp RTP包头中的时间戳(pts)经过clockrate换算后的时间戳单位毫秒
// 注意不支持带B帧的视频流pts和dts永远相同
// pkt.PayloadType base.RTPPacketTypeXXX
// pkt.PayloadType base.AVPacketPTXXX
// pkt.Payload 如果是AAC返回的是raw frame一个AVPacket只包含一帧
// 如果是AVC一个AVPacket可能包含多个NAL(受STAP-A影响)所以NAL前包含4字节的长度信息
// 如果是AVC或HEVC一个AVPacket可能包含多个NAL(受STAP-A影响)所以NAL前包含4字节的长度信息
// AAC引用的是接收到的RTP包中的内存块
// AVC是新申请的内存块,回调结束后,内部不再使用该内存块
// AVC或者HEVC是新申请的内存块,回调结束后,内部不再使用该内存块
type OnAVPacket func(pkt base.AVPacket)
func NewRTPUnpacker(payloadType int, clockRate int, maxSize int, onAVPacket OnAVPacket) *RTPUnpacker {
func NewRTPUnpacker(payloadType base.AVPacketPT, clockRate int, maxSize int, onAVPacket OnAVPacket) *RTPUnpacker {
return &RTPUnpacker{
payloadType: payloadType,
clockRate: clockRate,
@ -64,7 +61,7 @@ func (r *RTPUnpacker) Feed(pkt RTPPacket) {
return
}
calcPositionIfNeeded(&pkt)
r.calcPositionIfNeeded(&pkt)
r.insert(pkt)
// 尽可能多的合成顺序的帧
@ -112,83 +109,23 @@ func (r *RTPUnpacker) isStale(seq uint16) bool {
return CompareSeq(seq, r.unpackedSeq) <= 0
}
// 对AVC格式的流计算rtp包处于帧中的位置
func calcPositionIfNeeded(pkt *RTPPacket) {
if pkt.Header.PacketType != base.RTPPacketTypeAVC {
return
}
b := pkt.Raw[pkt.Header.payloadOffset:]
// rfc3984 5.3. NAL Unit Octet Usage
//
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |F|NRI| Type |
// +---------------+
outerNALUType := b[0] & 0x1F
//nazalog.Debugf("outerNALUType=%d", outerNALUType)
if outerNALUType <= NALUTypeSingleMax {
pkt.positionType = PositionTypeSingle
return
} else if outerNALUType == NALUTypeFUA {
// rfc3984 5.8. Fragmentation Units (FUs)
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | FU indicator | FU header | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
// | |
// | FU payload |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// FU indicator:
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |F|NRI| Type |
// +---------------+
//
// Fu header:
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |S|E|R| Type |
// +---------------+
fuIndicator := b[0]
_ = fuIndicator
fuHeader := b[1]
startCode := (fuHeader & 0x80) != 0
endCode := (fuHeader & 0x40) != 0
if startCode {
pkt.positionType = PositionTypeFUAStart
return
// 计算rtp包处于帧中的位置
func (r *RTPUnpacker) calcPositionIfNeeded(pkt *RTPPacket) {
switch pkt.Header.PacketType {
case base.RTPPacketTypeAVCOrHEVC:
switch r.payloadType {
case base.AVPacketPTAVC:
calcPositionIfNeededAVC(pkt)
case base.AVPacketPTHEVC:
calcPositionIfNeededHEVC(pkt)
default:
// can't reach here
}
if endCode {
pkt.positionType = PositionTypeFUAEnd
return
}
pkt.positionType = PositionTypeFUAMiddle
return
} else if outerNALUType == NALUTypeSTAPA {
pkt.positionType = PositionTypeSTAPA
} else {
nazalog.Errorf("unknown nalu type. outerNALUType=%d", outerNALUType)
case base.RTPPacketTypeAAC:
// noop
default:
// can't reach here
}
return
}
// 将rtp包按seq排序插入队列中
@ -238,241 +175,13 @@ func (r *RTPUnpacker) unpackOneSequential() bool {
// 从队列头部尝试合成一个完整的帧。不保证这次合成的帧的首个seq和上次合成帧的尾部seq是连续的
func (r *RTPUnpacker) unpackOne() bool {
switch r.payloadType {
case base.RTPPacketTypeAVC:
return r.unpackOneAVC()
case base.RTPPacketTypeAAC:
case base.AVPacketPTAAC:
return r.unpackOneAAC()
case base.AVPacketPTAVC:
fallthrough
case base.AVPacketPTHEVC:
return r.unpackOneAVCOrHEVC()
}
return false
}
// AAC格式的流尝试合成一个完整的帧
func (r *RTPUnpacker) unpackOneAAC() bool {
first := r.list.head.next
if first == nil {
return false
}
// TODO chef:
// 2. 只处理了一个RTP包含多个音频包的情况没有处理一个音频包跨越多个RTP包的情况是否有这种情况
// rfc3640 2.11. Global Structure of Payload Format
//
// +---------+-----------+-----------+---------------+
// | RTP | AU Header | Auxiliary | Access Unit |
// | Header | Section | Section | Data Section |
// +---------+-----------+-----------+---------------+
//
// <----------RTP Packet Payload----------->
//
// rfc3640 3.2.1. The AU Header Section
//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+
// |AU-headers-length|AU-header|AU-header| |AU-header|padding|
// | | (1) | (2) | | (n) | bits |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+
//
// rfc3640 3.3.6. High Bit-rate AAC
//
b := first.packet.Raw[first.packet.Header.payloadOffset:]
//nazalog.Debugf("%d, %d, %s", len(pkt.Raw), pkt.Header.timestamp, hex.Dump(b))
// AU Header Section
var auHeaderLength uint32
auHeaderLength = uint32(b[0])<<8 + uint32(b[1])
auHeaderLength = (auHeaderLength + 7) / 8
//nazalog.Debugf("auHeaderLength=%d", auHeaderLength)
// no Auxiliary Section
pauh := uint32(2) // AU Header pos
pau := uint32(2) + auHeaderLength // AU pos
auNum := uint32(auHeaderLength) / 2
for i := uint32(0); i < auNum; i++ {
var auSize uint32
auSize = uint32(b[pauh])<<8 | uint32(b[pauh+1]&0xF8) // 13bit
auSize /= 8
//auIndex := b[pauh+1] & 0x7
// raw AAC frame
// pau, auSize
//nazalog.Debugf("%d %d %s", auSize, auIndex, hex.Dump(b[pau:pau+auSize]))
var outPkt base.AVPacket
outPkt.Timestamp = first.packet.Header.Timestamp / uint32(r.clockRate/1000)
outPkt.Timestamp += i * uint32((1024*1000)/r.clockRate)
outPkt.Payload = b[pau : pau+auSize]
outPkt.PayloadType = base.RTPPacketTypeAAC
r.onAVPacket(outPkt)
pauh += 2
pau += auSize
}
r.unpackedFlag = true
r.unpackedSeq = first.packet.Header.Seq
r.list.head.next = first.next
r.list.size--
return true
}
// AVC格式的流尝试合成一个完整的帧
func (r *RTPUnpacker) unpackOneAVC() bool {
first := r.list.head.next
if first == nil {
return false
}
switch first.packet.positionType {
case PositionTypeSingle:
var pkt base.AVPacket
pkt.PayloadType = base.RTPPacketTypeAVC
pkt.Timestamp = first.packet.Header.Timestamp / uint32(r.clockRate/1000)
pkt.Payload = make([]byte, len(first.packet.Raw)-int(first.packet.Header.payloadOffset)+4)
bele.BEPutUint32(pkt.Payload, uint32(len(first.packet.Raw))-first.packet.Header.payloadOffset)
copy(pkt.Payload[4:], first.packet.Raw[first.packet.Header.payloadOffset:])
r.unpackedFlag = true
r.unpackedSeq = first.packet.Header.Seq
r.list.head.next = first.next
r.list.size--
r.onAVPacket(pkt)
return true
case PositionTypeSTAPA:
var pkt base.AVPacket
pkt.PayloadType = base.RTPPacketTypeAVC
pkt.Timestamp = first.packet.Header.Timestamp / uint32(r.clockRate/1000)
// 跳过首字节并且将多nalu前的2字节长度替换成4字节长度
buf := first.packet.Raw[first.packet.Header.payloadOffset+1:]
// 使用两次遍历,第一次遍历找出总大小,第二次逐个拷贝,目的是使得内存块一次就申请好,不用动态扩容造成额外性能开销
totalSize := 0
for i := 0; i != len(buf); {
if len(buf)-i < 2 {
nazalog.Errorf("invalid STAP-A packet.")
return false
}
naluSize := int(bele.BEUint16(buf[i:]))
totalSize += 4 + naluSize
i += 2 + naluSize
}
pkt.Payload = make([]byte, totalSize)
j := 0
for i := 0; i != len(buf); {
naluSize := int(bele.BEUint16(buf[i:]))
bele.BEPutUint32(pkt.Payload[j:], uint32(naluSize))
copy(pkt.Payload[j+4:], buf[i+2:i+2+naluSize])
j += 4 + naluSize
i += 2 + naluSize
}
r.unpackedFlag = true
r.unpackedSeq = first.packet.Header.Seq
r.list.head.next = first.next
r.list.size--
r.onAVPacket(pkt)
return true
case PositionTypeFUAStart:
prev := first
p := first.next
for {
if prev == nil || p == nil {
return false
}
if SubSeq(p.packet.Header.Seq, prev.packet.Header.Seq) != 1 {
return false
}
if p.packet.positionType == PositionTypeFUAMiddle {
prev = p
p = p.next
continue
} else if p.packet.positionType == PositionTypeFUAEnd {
var pkt base.AVPacket
pkt.PayloadType = base.RTPPacketTypeAVC
pkt.Timestamp = p.packet.Header.Timestamp / uint32(r.clockRate/1000)
fuIndicator := first.packet.Raw[first.packet.Header.payloadOffset]
fuHeader := first.packet.Raw[first.packet.Header.payloadOffset+1]
naluType := (fuIndicator & 0xE0) | (fuHeader & 0x1F)
// 使用两次遍历,第一次遍历找出总大小,第二次逐个拷贝,目的是使得内存块一次就申请好,不用动态扩容造成额外性能开销
totalSize := 0
pp := first
for {
totalSize += len(pp.packet.Raw) - int(pp.packet.Header.payloadOffset) - 2
if pp == p {
break
}
pp = pp.next
}
pkt.Payload = make([]byte, totalSize+5) // 4+1
bele.BEPutUint32(pkt.Payload, uint32(totalSize+1))
pkt.Payload[4] = naluType
index := 5
packetCount := 0
pp = first
for {
copy(pkt.Payload[index:], pp.packet.Raw[pp.packet.Header.payloadOffset+2:])
index += len(pp.packet.Raw) - int(pp.packet.Header.payloadOffset) - 2
packetCount++
if pp == p {
break
}
pp = pp.next
}
r.unpackedFlag = true
r.unpackedSeq = p.packet.Header.Seq
r.list.head.next = p.next
r.list.size -= packetCount
r.onAVPacket(pkt)
return true
} else {
// 不应该出现其他类型
nazalog.Errorf("invalid position type. position=%d", p.packet.positionType)
return false
}
}
case PositionTypeFUAMiddle:
// noop
case PositionTypeFUAEnd:
// noop
default:
nazalog.Errorf("invalid position. pos=%d", first.packet.positionType)
}
return false
}
// h265
//{
// originNALUType := (b[h.payloadOffset] >> 1) & 0x3F
// if originNALUType == 49 {
// header2 := b[h.payloadOffset+2]
//
// startCode := (header2 & 0x80) != 0
// endCode := (header2 & 0x40) != 0
//
// naluType := header2 & 0x3F
//
// nazalog.Debugf("FUA. originNALUType=%d, naluType=%d, startCode=%t, endCode=%t %s", originNALUType, naluType, startCode, endCode, hex.Dump(b[12:32]))
//
// } else {
// nazalog.Debugf("SINGLE. naluType=%d %s", originNALUType, hex.Dump(b[12:32]))
// }
//}

@ -0,0 +1,83 @@
// 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 rtprtcp
import "github.com/q191201771/lal/pkg/base"
// AAC格式的流尝试合成一个完整的帧
func (r *RTPUnpacker) unpackOneAAC() bool {
first := r.list.head.next
if first == nil {
return false
}
// TODO chef:
// 2. 只处理了一个RTP包含多个音频包的情况没有处理一个音频包跨越多个RTP包的情况是否有这种情况
// rfc3640 2.11. Global Structure of Payload Format
//
// +---------+-----------+-----------+---------------+
// | RTP | AU Header | Auxiliary | Access Unit |
// | Header | Section | Section | Data Section |
// +---------+-----------+-----------+---------------+
//
// <----------RTP Packet Payload----------->
//
// rfc3640 3.2.1. The AU Header Section
//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+
// |AU-headers-length|AU-header|AU-header| |AU-header|padding|
// | | (1) | (2) | | (n) | bits |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+
//
// rfc3640 3.3.6. High Bit-rate AAC
//
b := first.packet.Raw[first.packet.Header.payloadOffset:]
//nazalog.Debugf("%d, %d, %s", len(pkt.Raw), pkt.Header.timestamp, hex.Dump(b))
// AU Header Section
var auHeaderLength uint32
auHeaderLength = uint32(b[0])<<8 + uint32(b[1])
auHeaderLength = (auHeaderLength + 7) / 8
//nazalog.Debugf("auHeaderLength=%d", auHeaderLength)
// no Auxiliary Section
pauh := uint32(2) // AU Header pos
pau := uint32(2) + auHeaderLength // AU pos
auNum := uint32(auHeaderLength) / 2
for i := uint32(0); i < auNum; i++ {
var auSize uint32
auSize = uint32(b[pauh])<<8 | uint32(b[pauh+1]&0xF8) // 13bit
auSize /= 8
//auIndex := b[pauh+1] & 0x7
// raw AAC frame
// pau, auSize
//nazalog.Debugf("%d %d %s", auSize, auIndex, hex.Dump(b[pau:pau+auSize]))
var outPkt base.AVPacket
outPkt.Timestamp = first.packet.Header.Timestamp / uint32(r.clockRate/1000)
outPkt.Timestamp += i * uint32((1024*1000)/r.clockRate)
outPkt.Payload = b[pau : pau+auSize]
outPkt.PayloadType = r.payloadType
r.onAVPacket(outPkt)
pauh += 2
pau += auSize
}
r.unpackedFlag = true
r.unpackedSeq = first.packet.Header.Seq
r.list.head.next = first.next
r.list.size--
return true
}

@ -0,0 +1,250 @@
// 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 rtprtcp
import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
func calcPositionIfNeededAVC(pkt *RTPPacket) {
b := pkt.Raw[pkt.Header.payloadOffset:]
// rfc3984 5.3. NAL Unit Octet Usage
//
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |F|NRI| Type |
// +---------------+
outerNALUType := b[0] & 0x1F
if outerNALUType <= NALUTypeAVCSingleMax {
pkt.positionType = PositionTypeSingle
return
} else if outerNALUType == NALUTypeAVCFUA {
// rfc3984 5.8. Fragmentation Units (FUs)
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | FU indicator | FU header | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
// | |
// | FU payload |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// FU indicator:
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |F|NRI| Type |
// +---------------+
//
// Fu header:
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |S|E|R| Type |
// +---------------+
fuIndicator := b[0]
_ = fuIndicator
fuHeader := b[1]
startCode := (fuHeader & 0x80) != 0
endCode := (fuHeader & 0x40) != 0
if startCode {
pkt.positionType = PositionTypeFUAStart
return
}
if endCode {
pkt.positionType = PositionTypeFUAEnd
return
}
pkt.positionType = PositionTypeFUAMiddle
return
} else if outerNALUType == NALUTypeAVCSTAPA {
pkt.positionType = PositionTypeSTAPA
} else {
nazalog.Errorf("unknown nalu type. outerNALUType=%d", outerNALUType)
}
return
}
// AVC或HEVC格式的流尝试合成一个完整的帧
func (r *RTPUnpacker) unpackOneAVCOrHEVC() bool {
first := r.list.head.next
if first == nil {
return false
}
switch first.packet.positionType {
case PositionTypeSingle:
var pkt base.AVPacket
pkt.PayloadType = r.payloadType
pkt.Timestamp = first.packet.Header.Timestamp / uint32(r.clockRate/1000)
pkt.Payload = make([]byte, len(first.packet.Raw)-int(first.packet.Header.payloadOffset)+4)
bele.BEPutUint32(pkt.Payload, uint32(len(first.packet.Raw))-first.packet.Header.payloadOffset)
copy(pkt.Payload[4:], first.packet.Raw[first.packet.Header.payloadOffset:])
r.unpackedFlag = true
r.unpackedSeq = first.packet.Header.Seq
r.list.head.next = first.next
r.list.size--
r.onAVPacket(pkt)
return true
case PositionTypeSTAPA:
var pkt base.AVPacket
pkt.PayloadType = r.payloadType
pkt.Timestamp = first.packet.Header.Timestamp / uint32(r.clockRate/1000)
// 跳过首字节并且将多nalu前的2字节长度替换成4字节长度
buf := first.packet.Raw[first.packet.Header.payloadOffset+1:]
// 使用两次遍历,第一次遍历找出总大小,第二次逐个拷贝,目的是使得内存块一次就申请好,不用动态扩容造成额外性能开销
totalSize := 0
for i := 0; i != len(buf); {
if len(buf)-i < 2 {
nazalog.Errorf("invalid STAP-A packet.")
return false
}
naluSize := int(bele.BEUint16(buf[i:]))
totalSize += 4 + naluSize
i += 2 + naluSize
}
pkt.Payload = make([]byte, totalSize)
j := 0
for i := 0; i != len(buf); {
naluSize := int(bele.BEUint16(buf[i:]))
bele.BEPutUint32(pkt.Payload[j:], uint32(naluSize))
copy(pkt.Payload[j+4:], buf[i+2:i+2+naluSize])
j += 4 + naluSize
i += 2 + naluSize
}
r.unpackedFlag = true
r.unpackedSeq = first.packet.Header.Seq
r.list.head.next = first.next
r.list.size--
r.onAVPacket(pkt)
return true
case PositionTypeFUAStart:
prev := first
p := first.next
for {
if prev == nil || p == nil {
return false
}
if SubSeq(p.packet.Header.Seq, prev.packet.Header.Seq) != 1 {
return false
}
if p.packet.positionType == PositionTypeFUAMiddle {
prev = p
p = p.next
continue
} else if p.packet.positionType == PositionTypeFUAEnd {
var pkt base.AVPacket
pkt.PayloadType = r.payloadType
pkt.Timestamp = p.packet.Header.Timestamp / uint32(r.clockRate/1000)
var naluTypeLen int
var naluType []byte
if r.payloadType == base.AVPacketPTAVC {
naluTypeLen = 1
naluType = make([]byte, naluTypeLen)
fuIndicator := first.packet.Raw[first.packet.Header.payloadOffset]
fuHeader := first.packet.Raw[first.packet.Header.payloadOffset+1]
naluType[0] = (fuIndicator & 0xE0) | (fuHeader & 0x1F)
} else {
naluTypeLen = 2
naluType = make([]byte, naluTypeLen)
buf := first.packet.Raw[first.packet.Header.payloadOffset:]
fuType := buf[2] & 0x3f
naluType[0] = (buf[0] & 0x81) | (fuType << 1)
naluType[1] = buf[1]
}
// 使用两次遍历,第一次遍历找出总大小,第二次逐个拷贝,目的是使得内存块一次就申请好,不用动态扩容造成额外性能开销
totalSize := 0
pp := first
for {
totalSize += len(pp.packet.Raw) - int(pp.packet.Header.payloadOffset) - (naluTypeLen + 1)
if pp == p {
break
}
pp = pp.next
}
pkt.Payload = make([]byte, totalSize+4+naluTypeLen)
bele.BEPutUint32(pkt.Payload, uint32(totalSize+naluTypeLen))
var index int
if r.payloadType == base.AVPacketPTAVC {
pkt.Payload[4] = naluType[0]
index = 5
} else {
pkt.Payload[4] = naluType[0]
pkt.Payload[5] = naluType[1]
index = 6
}
packetCount := 0
pp = first
for {
copy(pkt.Payload[index:], pp.packet.Raw[int(pp.packet.Header.payloadOffset)+(naluTypeLen+1):])
index += len(pp.packet.Raw) - int(pp.packet.Header.payloadOffset) - (naluTypeLen + 1)
packetCount++
if pp == p {
break
}
pp = pp.next
}
r.unpackedFlag = true
r.unpackedSeq = p.packet.Header.Seq
r.list.head.next = p.next
r.list.size -= packetCount
r.onAVPacket(pkt)
return true
} else {
// 不应该出现其他类型
nazalog.Errorf("invalid position type. position=%d", p.packet.positionType)
return false
}
}
case PositionTypeFUAMiddle:
// noop
case PositionTypeFUAEnd:
// noop
default:
nazalog.Errorf("invalid position. pos=%d", first.packet.positionType)
}
return false
}

@ -0,0 +1,81 @@
// 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 rtprtcp
import (
"github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/naza/pkg/nazalog"
)
func calcPositionIfNeededHEVC(pkt *RTPPacket) {
b := pkt.Raw[pkt.Header.payloadOffset:]
// +---------------+---------------+
// |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |F| Type | LayerId | TID |
// +-------------+-----------------+
outerNALUType := hevc.ParseNALUType(b[0])
switch outerNALUType {
case hevc.NALUTypeSliceIDRNLP:
fallthrough
case hevc.NALUTypeSliceTrailR:
pkt.positionType = PositionTypeSingle
return
case NALUTypeHEVCFUA:
// Figure 1: The Structure of the HEVC NAL Unit Header
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PayloadHdr (Type=49) | FU header | DONL (cond) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
// | DONL (cond) | |
// |-+-+-+-+-+-+-+-+ |
// | FU payload |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Figure 9: The Structure of an FU
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |S|E| FuType |
// +---------------+
// Figure 10: The Structure of FU Header
startCode := (b[2] & 0x80) != 0
endCode := (b[2] & 0x40) != 0
if startCode {
pkt.positionType = PositionTypeFUAStart
return
}
if endCode {
pkt.positionType = PositionTypeFUAEnd
return
}
pkt.positionType = PositionTypeFUAMiddle
return
default:
// TODO chef: 没有实现 AP 48
nazalog.Errorf("unknown nalu type. outerNALUType=%d", outerNALUType)
}
}
// hevc rtp包合帧部分见func unpackOneAVCOrHEVC

@ -46,7 +46,9 @@ func NewAVPacketQueue(onAVPacket OnAVPacket) *AVPacketQueue {
func (a *AVPacketQueue) Feed(pkt base.AVPacket) {
//nazalog.Debugf("AVQ feed. t=%d, ts=%d", pkt.PayloadType, pkt.Timestamp)
switch pkt.PayloadType {
case base.RTPPacketTypeAVC:
case base.AVPacketPTAVC:
fallthrough
case base.AVPacketPTHEVC:
if a.videoBaseTS == -1 {
a.videoBaseTS = int64(pkt.Timestamp)
}
@ -58,7 +60,7 @@ func (a *AVPacketQueue) Feed(pkt base.AVPacket) {
}
_ = a.videoQueue.PushBack(pkt)
//nazalog.Debugf("AVQ v push. a=%d, v=%d", a.audioQueue.Size(), a.videoQueue.Size())
case base.RTPPacketTypeAAC:
case base.AVPacketPTAAC:
if a.audioBaseTS == -1 {
a.audioBaseTS = int64(pkt.Timestamp)
}

@ -10,8 +10,6 @@ package rtsp
import "errors"
// 注意正在学习以及实现rtsp请不要使用这个package
// TODO chef
// - 支持tcp协议
// - pub_session生命周期如何结束

@ -37,14 +37,16 @@ type PubSession struct {
observer PubSessionObserver
avPacketQueue *AVPacketQueue
rtpConn *nazanet.UDPConnection
rtcpConn *nazanet.UDPConnection
audioUnpacker *rtprtcp.RTPUnpacker
videoUnpacker *rtprtcp.RTPUnpacker
audioRRProducer *rtprtcp.RRProducer
videoRRProducer *rtprtcp.RRProducer
audioSsrc uint32
videoSsrc uint32
rtpConn *nazanet.UDPConnection
rtcpConn *nazanet.UDPConnection
audioUnpacker *rtprtcp.RTPUnpacker
videoUnpacker *rtprtcp.RTPUnpacker
audioRRProducer *rtprtcp.RRProducer
videoRRProducer *rtprtcp.RRProducer
audioSsrc uint32
videoSsrc uint32
audioPayloadType base.AVPacketPT
videoPayloadType base.AVPacketPT
vps []byte // 如果是H265的话
sps []byte
@ -53,7 +55,7 @@ type PubSession struct {
}
func NewPubSession(streamName string) *PubSession {
uk := unique.GenUniqueKey("RTSP")
uk := unique.GenUniqueKey("RTSPPUB")
ps := &PubSession{
UniqueKey: uk,
StreamName: streamName,
@ -81,20 +83,21 @@ func (p *PubSession) SetObserver(obs PubSessionObserver) {
func (p *PubSession) InitWithSDP(sdpCtx sdp.SDPContext) {
var err error
var audioPayloadType int
var videoPayloadType int
var audioClockRate int
var videoClockRate int
var isHEVC bool
for _, item := range sdpCtx.ARTPMapList {
switch item.PayloadType {
case base.RTPPacketTypeAVC:
case base.RTPPacketTypeAVCOrHEVC:
videoClockRate = item.ClockRate
isHEVC = item.EncodingName == "H265"
if item.EncodingName == "H265" {
p.videoPayloadType = base.AVPacketPTHEVC
} else {
p.videoPayloadType = base.AVPacketPTAVC
}
case base.RTPPacketTypeAAC:
audioClockRate = item.ClockRate
p.audioPayloadType = base.AVPacketPTAAC
default:
nazalog.Errorf("unknown payloadType. type=%d", item.PayloadType)
}
@ -102,10 +105,8 @@ func (p *PubSession) InitWithSDP(sdpCtx sdp.SDPContext) {
for _, item := range sdpCtx.AFmtPBaseList {
switch item.Format {
case base.RTPPacketTypeAVC:
videoPayloadType = item.Format
if isHEVC {
case base.RTPPacketTypeAVCOrHEVC:
if p.videoPayloadType == base.AVPacketPTHEVC {
p.vps, p.sps, p.pps, err = sdp.ParseVPSSPSPPS(item)
} else {
p.sps, p.pps, err = sdp.ParseSPSPPS(item)
@ -114,8 +115,6 @@ func (p *PubSession) InitWithSDP(sdpCtx sdp.SDPContext) {
nazalog.Errorf("parse sps pps from sdp failed.")
}
case base.RTPPacketTypeAAC:
audioPayloadType = item.Format
p.asc, err = sdp.ParseASC(item)
if err != nil {
nazalog.Errorf("parse asc from sdp failed.")
@ -125,8 +124,8 @@ func (p *PubSession) InitWithSDP(sdpCtx sdp.SDPContext) {
}
}
p.audioUnpacker = rtprtcp.NewRTPUnpacker(audioPayloadType, audioClockRate, unpackerItemMaxSize, p.onAVPacketUnpacked)
p.videoUnpacker = rtprtcp.NewRTPUnpacker(videoPayloadType, videoClockRate, unpackerItemMaxSize, p.onAVPacketUnpacked)
p.audioUnpacker = rtprtcp.NewRTPUnpacker(p.audioPayloadType, audioClockRate, unpackerItemMaxSize, p.onAVPacketUnpacked)
p.videoUnpacker = rtprtcp.NewRTPUnpacker(p.videoPayloadType, videoClockRate, unpackerItemMaxSize, p.onAVPacketUnpacked)
p.audioRRProducer = rtprtcp.NewRRProducer(audioClockRate)
p.videoRRProducer = rtprtcp.NewRRProducer(videoClockRate)
@ -188,7 +187,7 @@ func (p *PubSession) onReadUDPPacket(b []byte, rAddr *net.UDPAddr, err error) bo
// try RTP
packetType := b[1] & 0x7F
if packetType == base.RTPPacketTypeAVC || packetType == base.RTPPacketTypeAAC {
if packetType == base.RTPPacketTypeAVCOrHEVC || packetType == base.RTPPacketTypeAAC {
h, err := rtprtcp.ParseRTPPacket(b)
if err != nil {
nazalog.Errorf("read invalid rtp packet. err=%+v", err)
@ -198,7 +197,7 @@ func (p *PubSession) onReadUDPPacket(b []byte, rAddr *net.UDPAddr, err error) bo
pkt.Header = h
pkt.Raw = b
if packetType == base.RTPPacketTypeAVC {
if packetType == base.RTPPacketTypeAVCOrHEVC {
p.videoSsrc = h.Ssrc
p.videoUnpacker.Feed(pkt)
p.videoRRProducer.FeedRTPPacket(h.Seq)

@ -169,7 +169,7 @@ func ParseASC(a AFmtPBase) ([]byte, error) {
}
func ParseVPSSPSPPS(a AFmtPBase) (vps, sps, pps []byte, err error) {
if a.Format != base.RTPPacketTypeAVC {
if a.Format != base.RTPPacketTypeAVCOrHEVC {
return nil, nil, nil, ErrSDP
}
@ -203,7 +203,7 @@ func ParseVPSSPSPPS(a AFmtPBase) (vps, sps, pps []byte, err error) {
// 解析AVC/H264的spspps
// 例子见单元测试
func ParseSPSPPS(a AFmtPBase) (sps, pps []byte, err error) {
if a.Format != base.RTPPacketTypeAVC {
if a.Format != base.RTPPacketTypeAVCOrHEVC {
return nil, nil, ErrSDP
}

@ -41,7 +41,7 @@ if [ ! -s "./testdata/test.flv" ]; then
if [ ! -d "./testdata" ]; then
mkdir "./testdata"
fi
wget https://github.com/q191201771/doc/raw/master/stuff/wontcry30s.flv -O ./testdata/test.flv
wget https://github.com/q191201771/doc/raw/master/av/wontcry30s.flv -O ./testdata/test.flv
if [ ! -s "./testdata/test.flv" ]; then
wget https://pengrl.com/images/other/wontcry30s.flv -O ./testdata/test.flv
fi

Loading…
Cancel
Save