Merge pull request #17 from q191201771/master

lal更新到最新版
pull/134/head
joestarzxh 3 years ago committed by GitHub
commit 1986b87f2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

16
.gitignore vendored

@ -1,18 +1,20 @@
/.idea
/.vscode
/SECRET.md
profile.out
coverage.html
/coverage.txt
lal_record
logs
testdata
/bin
/release
/.trash
/playground
/tmp
/playground
profile.out
coverage.html
/coverage.txt
delay.txt
/.trash
/SECRET.md
/TODO.md
/pre-commit.sh
/conf/self.conf.json
@ -23,5 +25,3 @@ testdata
*.aac
*.h264
*.flv
lal_record
delay.txt

@ -20,7 +20,7 @@ image:
.PHONY: clean
clean:
rm -rf ./bin ./release ./logs ./lal_record
rm -rf ./bin ./lal_record ./logs coverage.txt
.PHONY: all
all: build test

@ -2,4 +2,4 @@ module github.com/q191201771/lal
go 1.14
require github.com/q191201771/naza v0.29.0
require github.com/q191201771/naza v0.30.0

@ -1,2 +1,2 @@
github.com/q191201771/naza v0.29.0 h1:VvvJfYVkmQjszOXqhYyf2gGOCPl69woLCSWxNF0T//I=
github.com/q191201771/naza v0.29.0/go.mod h1:n+dpJjQSh90PxBwxBNuifOwQttywvSIN5TkWSSYCeBk=
github.com/q191201771/naza v0.30.0 h1:tfy1O0QRl3O80mH8PSAd2FhpZ5eL7coQtCF0HzjEO4Y=
github.com/q191201771/naza v0.30.0/go.mod h1:n+dpJjQSh90PxBwxBNuifOwQttywvSIN5TkWSSYCeBk=

@ -16,8 +16,6 @@ import (
"github.com/q191201771/lal/pkg/aac"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/naza/pkg/assert"
)
@ -90,5 +88,5 @@ func TestMakeAudioDataSeqHeader(t *testing.T) {
func TestSequenceHeaderContext(t *testing.T) {
var shCtx aac.SequenceHeaderContext
shCtx.Unpack(goldenSh)
nazalog.Debugf("%+v", shCtx)
aac.Log.Debugf("%+v", shCtx)
}

@ -0,0 +1,13 @@
// Copyright 2022, 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 aac
import "github.com/q191201771/naza/pkg/nazalog"
var Log = nazalog.GetGlobalLogger()

@ -11,10 +11,8 @@ package avc
import (
"io"
"github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazaerrors"
@ -36,7 +34,7 @@ var (
NaluStartCode3 = []byte{0x0, 0x0, 0x1}
NaluStartCode4 = []byte{0x0, 0x0, 0x0, 0x1}
// aud nalu
// AudNalu aud nalu
AudNalu = []byte{0x00, 0x00, 0x00, 0x01, 0x09, 0xf0}
)
@ -498,7 +496,7 @@ func IterateNaluAvcc(nals []byte, handler func(nal []byte)) error {
// length为0的直接过滤掉
if length == 0 {
nazalog.Warnf("avcc nalu length equal 0. nals=%s", nazabytes.Prefix(nals, 128))
Log.Warnf("avcc nalu length equal 0. nals=%s", nazabytes.Prefix(nals, 128))
continue
}
handler(nals[pos:epos])
@ -507,7 +505,7 @@ func IterateNaluAvcc(nals []byte, handler func(nal []byte)) error {
// 最后一个
if length == 0 {
nazalog.Warnf("avcc nalu length equal 0. nals=%s", nazabytes.Prefix(nals, 128))
Log.Warnf("avcc nalu length equal 0. nals=%s", nazabytes.Prefix(nals, 128))
continue
}
handler(nals[pos:epos])

@ -18,8 +18,6 @@ import (
"github.com/q191201771/naza/pkg/nazabits"
"github.com/q191201771/naza/pkg/nazaerrors"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/lal/pkg/avc"
"github.com/q191201771/naza/pkg/assert"
@ -36,7 +34,7 @@ func TestParseNaluType(t *testing.T) {
assert.Equal(t, out, actual)
b := avc.ParseNaluTypeReadable(in)
nazalog.Debug(b)
avc.Log.Debug(b)
}
}
@ -60,7 +58,7 @@ func TestParseSliceType(t *testing.T) {
b, err := avc.ParseSliceTypeReadable(item.in)
assert.Equal(t, nil, err)
nazalog.Debug(b)
avc.Log.Debug(b)
}
}
@ -138,7 +136,7 @@ func TestParseSps(t *testing.T) {
err = avc.ParseSps(nil, &ctx)
assert.Equal(t, true, nazaerrors.Is(err, nazabits.ErrNazaBits))
assert.IsNotNil(t, err)
nazalog.Debugf("error expected not nil, actual=%+v", err)
avc.Log.Debugf("error expected not nil, actual=%+v", err)
err = avc.ParseSps(goldenSps2, &ctx)
assert.Equal(t, nil, err)

@ -18,14 +18,13 @@ import (
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazabits"
"github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazalog"
)
func ParseSps(payload []byte, ctx *Context) error {
br := nazabits.NewBitReader(payload)
var sps Sps
if err := parseSpsBasic(&br, &sps); err != nil {
nazalog.Errorf("parseSpsBasic failed. err=%+v, payload=%s", err, hex.Dump(nazabytes.Prefix(payload, 128)))
Log.Errorf("parseSpsBasic failed. err=%+v, payload=%s", err, hex.Dump(nazabytes.Prefix(payload, 128)))
return err
}
ctx.Profile = sps.ProfileIdc
@ -37,16 +36,16 @@ func ParseSps(payload []byte, ctx *Context) error {
if err := parseSpsGamma(&br, &sps); err != nil {
// 注意这里不将错误返回给上层因为可能是Beta自身解析的问题
nazalog.Errorf("parseSpsGamma failed. err=%+v, payload=%s", err, hex.Dump(nazabytes.Prefix(payload, 128)))
Log.Errorf("parseSpsGamma failed. err=%+v, payload=%s", err, hex.Dump(nazabytes.Prefix(payload, 128)))
}
nazalog.Debugf("sps=%+v", sps)
Log.Debugf("sps=%+v", sps)
ctx.Width = (sps.PicWidthInMbsMinusOne+1)*16 - (sps.FrameCropLeftOffset+sps.FrameCropRightOffset)*2
ctx.Height = (2-uint32(sps.FrameMbsOnlyFlag))*(sps.PicHeightInMapUnitsMinusOne+1)*16 - (sps.FrameCropTopOffset+sps.FrameCropBottomOffset)*2
return nil
}
// 尝试解析PPS所有字段实验中请勿直接使用该函数
// TryParsePps 尝试解析PPS所有字段实验中请勿直接使用该函数
func TryParsePps(payload []byte) error {
// ISO-14496-10.pdf
// 7.3.2.2 Picture parameter set RBSP syntax
@ -55,7 +54,7 @@ func TryParsePps(payload []byte) error {
return nil
}
// 尝试解析SeqHeader所有字段实验中请勿直接使用该函数
// TryParseSeqHeader 尝试解析SeqHeader所有字段实验中请勿直接使用该函数
//
// @param <payload> rtmp message的payload部分或者flv tag的payload部分
// 注意包含了头部2字节类型以及3字节的cts
@ -94,7 +93,7 @@ func TryParseSeqHeader(payload []byte) error {
b, err = br.ReadBytes(2)
dcr.PpsLength = bele.BeUint16(b)
nazalog.Debugf("%+v", dcr)
Log.Debugf("%+v", dcr)
// 5 + 5 + 1 + 2
var ctx Context

@ -0,0 +1,13 @@
// Copyright 2022, 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 avc
import "github.com/q191201771/naza/pkg/nazalog"
var Log = nazalog.GetGlobalLogger()

@ -14,7 +14,6 @@ import (
"time"
"github.com/q191201771/naza/pkg/bininfo"
"github.com/q191201771/naza/pkg/nazalog"
)
// base包提供被其他多个package依赖的基础内容自身不依赖任何package
@ -23,9 +22,7 @@ import (
var startTime string
// ReadableNowTime
//
// TODO(chef): refactor 使用ReadableNowTime
// ReadableNowTime 当前时间,可读字符串形式
//
func ReadableNowTime() string {
return time.Now().Format("2006-01-02 15:04:05.999")
@ -33,13 +30,13 @@ func ReadableNowTime() string {
func LogoutStartInfo() {
dir, _ := os.Getwd()
nazalog.Infof(" start: %s", startTime)
nazalog.Infof(" wd: %s", dir)
nazalog.Infof(" args: %s", strings.Join(os.Args, " "))
nazalog.Infof(" bininfo: %s", bininfo.StringifySingleLine())
nazalog.Infof(" version: %s", LalFullInfo)
nazalog.Infof(" github: %s", LalGithubSite)
nazalog.Infof(" doc: %s", LalDocSite)
Log.Infof(" start: %s", startTime)
Log.Infof(" wd: %s", dir)
Log.Infof(" args: %s", strings.Join(os.Args, " "))
Log.Infof(" bininfo: %s", bininfo.StringifySingleLine())
Log.Infof(" version: %s", LalFullInfo)
Log.Infof(" github: %s", LalGithubSite)
Log.Infof(" doc: %s", LalDocSite)
}
func init() {

@ -16,11 +16,11 @@ func TestHttpServerManager(t *testing.T) {
//var err error
//
//var fnFlv = func(writer http.ResponseWriter, request *http.Request) {
// nazalog.Debugf("> fnFlv")
// Log.Debugf("> fnFlv")
//}
//
//var fnTs = func(writer http.ResponseWriter, request *http.Request) {
// nazalog.Debugf("> fnTs")
// Log.Debugf("> fnTs")
//}
//
//sm := NewHttpServerManager()

@ -11,7 +11,6 @@ package base
import (
"net"
"strings"
"time"
"github.com/q191201771/naza/pkg/connection"
)
@ -43,7 +42,7 @@ func NewHttpSubSession(option HttpSubSessionOption) *HttpSubSession {
stat: StatSession{
Protocol: option.Protocol,
SessionId: option.Uk,
StartTime: time.Now().Format("2006-01-02 15:04:05.999"),
StartTime: ReadableNowTime(),
RemoteAddr: option.Conn.RemoteAddr().String(),
},
}
@ -118,7 +117,7 @@ func (session *HttpSubSession) StreamName() string {
case ProtocolHttpts:
suffix = ".ts"
default:
Logger.Warnf("[%s] acquire stream name but protocol unknown.", session.Uk)
Log.Warnf("[%s] acquire stream name but protocol unknown.", session.Uk)
}
return strings.TrimSuffix(session.UrlCtx.LastItemOfPath, suffix)
}

@ -15,6 +15,7 @@ import (
)
type LogDump struct {
log nazalog.Logger
debugMaxNum int
debugCount int
@ -24,14 +25,15 @@ type LogDump struct {
//
// @param debugMaxNum: 日志最小级别为debug时使用debug打印日志次数的阈值
//
func NewLogDump(debugMaxNum int) LogDump {
func NewLogDump(log nazalog.Logger, debugMaxNum int) LogDump {
return LogDump{
log: log,
debugMaxNum: debugMaxNum,
}
}
func (ld *LogDump) ShouldDump() bool {
switch nazalog.GetOption().Level {
switch ld.log.GetOption().Level {
case nazalog.LevelTrace:
return true
case nazalog.LevelDebug:
@ -52,5 +54,5 @@ func (ld *LogDump) ShouldDump() bool {
// 这个hex.Dump调用
//
func (ld *LogDump) Outf(format string, v ...interface{}) {
nazalog.Out(nazalog.GetOption().Level, 3, fmt.Sprintf(format, v...))
ld.log.Out(ld.log.GetOption().Level, 3, fmt.Sprintf(format, v...))
}

@ -10,8 +10,6 @@ package base
import (
"net"
"github.com/q191201771/naza/pkg/nazalog"
)
// TODO(chef): feat 通过时间戳(目前是数据大小)来设定合并阈值
@ -47,7 +45,7 @@ func NewMergeWriter(onWritev OnWritev, size int) *MergeWriter {
// 注意,函数调用结束后,`b`内存块会被内部持有
//
func (w *MergeWriter) Write(b []byte) {
nazalog.Debugf("[%p] MergeWriter::Write. len=%d", w, len(b))
Log.Debugf("[%p] MergeWriter::Write. len=%d", w, len(b))
w.bs = append(w.bs, b)
w.currSize += len(b)
if w.currSize >= w.size {
@ -58,7 +56,7 @@ func (w *MergeWriter) Write(b []byte) {
// Flush 强制将内部缓冲的数据全部回调排空
//
func (w *MergeWriter) Flush() {
nazalog.Debugf("[%p] MergeWriter::Flush.", w)
Log.Debugf("[%p] MergeWriter::Flush.", w)
if w.currSize > 0 {
w.flush()
}
@ -74,7 +72,7 @@ func (w *MergeWriter) flush() {
n += len(v)
ns = append(ns, len(v))
}
nazalog.Debugf("[%p] MergeWriter::flush. len=%d(%v)", w, n, ns)
Log.Debugf("[%p] MergeWriter::flush. len=%d(%v)", w, n, ns)
w.onWritev(w.bs)
w.currSize = 0
w.bs = nil

@ -9,7 +9,7 @@
package base
const (
// spec-rtmp_specification_1.0.pdf
// RtmpTypeIdAudio spec-rtmp_specification_1.0.pdf
// 7.1. Types of Messages
RtmpTypeIdAudio uint8 = 8
RtmpTypeIdVideo uint8 = 9
@ -32,7 +32,7 @@ const (
RtmpUserControlPingRequest uint8 = 6
RtmpUserControlPingResponse uint8 = 7
// spec-video_file_format_spec_v10.pdf
// RtmpFrameTypeKey spec-video_file_format_spec_v10.pdf
// Video tags
// VIDEODATA
// FrameType UB[4]
@ -63,7 +63,7 @@ const (
RtmpAvcInterFrame = RtmpFrameTypeInter<<4 | RtmpCodecIdAvc
RtmpHevcInterFrame = RtmpFrameTypeInter<<4 | RtmpCodecIdHevc
// spec-video_file_format_spec_v10.pdf
// RtmpSoundFormatAac spec-video_file_format_spec_v10.pdf
// Audio tags
// AUDIODATA
// SoundFormat UB[4]

@ -9,7 +9,7 @@
package base
const (
// 注意一般情况下AVC使用96AAC使用97HEVC使用98
// RtpPacketTypeAvcOrHevc 注意一般情况下AVC使用96AAC使用97HEVC使用98
// 但是我还遇到过:
// HEVC使用96
// AVC使用105

@ -14,8 +14,6 @@ import (
"os"
"os/signal"
"syscall"
log "github.com/q191201771/naza/pkg/nazalog"
)
// RunSignalHandler 监听SIGUSR1和SIGUSR2信号并回调
@ -26,6 +24,6 @@ func RunSignalHandler(cb func()) {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGUSR1, syscall.SIGUSR2)
s := <-c
log.Infof("recv signal. s=%+v", s)
Log.Infof("recv signal. s=%+v", s)
cb()
}

@ -9,14 +9,14 @@
package base
const (
// StatGroup.AudioCodec
// AudioCodecAac StatGroup.AudioCodec
AudioCodecAac = "AAC"
// StatGroup.VideoCodec
// VideoCodecAvc StatGroup.VideoCodec
VideoCodecAvc = "H264"
VideoCodecHevc = "H265"
// StatSession.Protocol
// ProtocolRtmp StatSession.Protocol
ProtocolRtmp = "RTMP"
ProtocolRtsp = "RTSP"
ProtocolHttpflv = "HTTP-FLV"

@ -10,6 +10,4 @@ package base
import "github.com/q191201771/naza/pkg/nazalog"
var (
Logger = nazalog.GetGlobalLogger()
)
var Log = nazalog.GetGlobalLogger()

@ -15,7 +15,7 @@ import "strings"
// 另外,我们也在本文件提供另外一些信息
// 并且将这些信息打入可执行文件、日志、各协议中的标准版本字段中
// 版本,该变量由外部脚本修改维护
// LalVersion 版本,该变量由外部脚本修改维护
const LalVersion = "v0.27.1"
var (
@ -24,57 +24,57 @@ var (
LalGithubSite = "https://github.com/q191201771/lal"
LalDocSite = "https://pengrl.com/lal"
// e.g. lal v0.12.3 (github.com/q191201771/lal)
// LalFullInfo e.g. lal v0.12.3 (github.com/q191201771/lal)
LalFullInfo = LalLibraryName + " " + LalVersion + " (" + LalGithubRepo + ")"
// e.g. 0.12.3
// LalVersionDot e.g. 0.12.3
LalVersionDot string
// e.g. 0,12,3
// LalVersionComma e.g. 0,12,3
LalVersionComma string
)
var (
// 植入rtmp握手随机字符串中
// LalRtmpHandshakeWaterMark 植入rtmp握手随机字符串中
// e.g. lal v0.12.3 (github.com/q191201771/lal)
LalRtmpHandshakeWaterMark string
// 植入rtmp server中的connect result信令中
// LalRtmpConnectResultVersion 植入rtmp server中的connect result信令中
// 注意有两个object第一个object中的fmsVer我们保持通用公认的值在第二个object中植入
// e.g. 0,12,3
LalRtmpConnectResultVersion string
// e.g. lal0.12.3
// LalRtmpPushSessionConnectVersion e.g. lal0.12.3
LalRtmpPushSessionConnectVersion string
// e.g. lal0.12.3
// LalRtmpBuildMetadataEncoder e.g. lal0.12.3
LalRtmpBuildMetadataEncoder string
// e.g. lal/0.12.3
// LalHttpflvPullSessionUa e.g. lal/0.12.3
LalHttpflvPullSessionUa string
// e.g. lal0.12.3
// LalHttpflvSubSessionServer e.g. lal0.12.3
LalHttpflvSubSessionServer string
// e.g. lal0.12.3
// LalHlsM3u8Server e.g. lal0.12.3
LalHlsM3u8Server string
// e.g. lal0.12.3
// LalHlsTsServer e.g. lal0.12.3
LalHlsTsServer string
// e.g. lal0.12.3
// LalRtspOptionsResponseServer e.g. lal0.12.3
LalRtspOptionsResponseServer string
// e.g. lal0.12.3
// LalHttptsSubSessionServer e.g. lal0.12.3
LalHttptsSubSessionServer string
// e.g. lal0.12.3
// LalHttpApiServer e.g. lal0.12.3
LalHttpApiServer string
// e.g. lal/0.12.3
// LalRtspPullSessionUa e.g. lal/0.12.3
LalRtspPullSessionUa string
// e.g. lal 0.12.3
// LalPackSdp e.g. lal 0.12.3
LalPackSdp string
)

@ -16,7 +16,7 @@ import (
"github.com/q191201771/naza/pkg/bele"
)
// The WebSocket Protocol
// WsOpcode The WebSocket Protocol
// https://tools.ietf.org/html/rfc6455
//
// 0 1 2 3
@ -59,7 +59,8 @@ const (
Wso_Continuous WsOpcode = iota //连续消息片断
Wso_Text //文本消息片断,
Wso_Binary //二进制消息片断,
//非控制消息片断保留的操作码,
// Wso_Rsv3 非控制消息片断保留的操作码,
Wso_Rsv3
Wso_Rsv4
Wso_Rsv5
@ -68,7 +69,8 @@ const (
Wso_Close //连接关闭,
Wso_Ping //心跳检查的ping,
Wso_Pong //心跳检查的pong,
//为将来的控制消息片断的保留操作码
// Wso_RsvB 为将来的控制消息片断的保留操作码
Wso_RsvB
Wso_RsvC
Wso_RsvD

@ -173,7 +173,7 @@ func ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(payload []byte) (vps, sps, pps []b
if payload[0] != 0x1c || payload[1] != 0x00 || payload[2] != 0 || payload[3] != 0 || payload[4] != 0 {
return nil, nil, nil, nazaerrors.Wrap(base.ErrHevc)
}
//nazalog.Debugf("%s", hex.Dump(payload))
//Log.Debugf("%s", hex.Dump(payload))
if len(payload) < 33 {
return nil, nil, nil, nazaerrors.Wrap(base.ErrHevc)

@ -0,0 +1,13 @@
// Copyright 2022, 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 hevc
import "github.com/q191201771/naza/pkg/nazalog"
var Log = nazalog.GetGlobalLogger()

@ -59,7 +59,7 @@ func updateTargetDurationInM3u8(content []byte, currDuration int) ([]byte, error
return content, nil
}
// @param content 传入m3u8文件内容
// CalcM3u8Duration @param content 传入m3u8文件内容
//
// @return durationSec m3u8中所有ts的时间总和。注意使用的是m3u8文件中描述的ts时间而不是读取ts文件中实际音视频数据的时间。
//

@ -17,8 +17,6 @@ import (
"github.com/q191201771/lal/pkg/mpegts"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
)
// TODO chef: 转换TS流的功能通过回调供httpts使用也放在了Muxer中好处是hls和httpts可以共用一份TS流。
@ -27,8 +25,11 @@ import (
type MuxerObserver interface {
OnPatPmt(b []byte)
// @param rawFrame TS流回调结束后内部不再使用该内存块
// @param boundary 新的TS流接收者应该从该标志为true时开始发送数据
// OnTsPackets
//
// @param rawFrame: TS流回调结束后内部不再使用该内存块
//
// @param boundary: 新的TS流接收者应该从该标志为true时开始发送数据
//
OnTsPackets(rawFrame []byte, boundary bool)
}
@ -70,10 +71,14 @@ type Muxer struct {
observer MuxerObserver
fragment Fragment
opened bool
videoCc uint8
audioCc uint8
// 初始值为false调用openFragment时设置为true调用closeFragment时设置为false
// 整个对象关闭时设置为false
// 中途切换Fragment时调用close后会立即调用open
opened bool
fragTs uint64 // 新建立fragment时的时间戳毫秒 * 90
recordMaxFragDuration float64
@ -120,29 +125,31 @@ func NewMuxer(streamName string, enable bool, config *MuxerConfig, observer Muxe
m.makeFrags()
streamer := NewStreamer(m)
m.streamer = streamer
nazalog.Infof("[%s] lifecycle new hls muxer. muxer=%p, streamName=%s", uk, m, streamName)
Log.Infof("[%s] lifecycle new hls muxer. muxer=%p, streamName=%s", uk, m, streamName)
return m
}
func (m *Muxer) Start() {
nazalog.Infof("[%s] start hls muxer.", m.UniqueKey)
Log.Infof("[%s] start hls muxer.", m.UniqueKey)
m.ensureDir()
}
func (m *Muxer) Dispose() {
nazalog.Infof("[%s] lifecycle dispose hls muxer.", m.UniqueKey)
Log.Infof("[%s] lifecycle dispose hls muxer.", m.UniqueKey)
m.streamer.FlushAudio()
if err := m.closeFragment(true); err != nil {
nazalog.Errorf("[%s] close fragment error. err=%+v", m.UniqueKey, err)
Log.Errorf("[%s] close fragment error. err=%+v", m.UniqueKey, err)
}
}
// @param msg 函数调用结束后内部不持有msg中的内存块
// FeedRtmpMessage @param msg 函数调用结束后内部不持有msg中的内存块
//
func (m *Muxer) FeedRtmpMessage(msg base.RtmpMsg) {
m.streamer.FeedRtmpMessage(msg)
}
// ----- implement StreamerObserver of Streamer ------------------------------------------------------------------------
func (m *Muxer) OnPatPmt(b []byte) {
m.patpmt = b
if m.observer != nil {
@ -150,50 +157,38 @@ func (m *Muxer) OnPatPmt(b []byte) {
}
}
func (m *Muxer) OnFrame(streamer *Streamer, frame *mpegts.Frame) {
var boundary bool
func (m *Muxer) OnFrame(streamer *Streamer, frame *mpegts.Frame, boundary bool) {
var packets []byte
if frame.Sid == mpegts.StreamIdAudio {
// 为了考虑没有视频的情况也能切片所以这里判断spspps为空时也建议生成fragment
boundary = !streamer.VideoSeqHeaderCached()
// TODO(chef): 为什么音频用pts视频用dts
if err := m.updateFragment(frame.Pts, boundary); err != nil {
nazalog.Errorf("[%s] update fragment error. err=%+v", m.UniqueKey, err)
Log.Errorf("[%s] update fragment error. err=%+v", m.UniqueKey, err)
return
}
// TODO(chef): 有updateFragment的返回值判断这里的判断可以考虑删除
if !m.opened {
nazalog.Warnf("[%s] OnFrame A not opened. boundary=%t", m.UniqueKey, boundary)
Log.Warnf("[%s] OnFrame A not opened. boundary=%t", m.UniqueKey, boundary)
return
}
//nazalog.Debugf("[%s] WriteFrame A. dts=%d, len=%d", m.UniqueKey, frame.DTS, len(frame.Raw))
//Log.Debugf("[%s] WriteFrame A. dts=%d, len=%d", m.UniqueKey, frame.DTS, len(frame.Raw))
} else {
//nazalog.Debugf("[%s] OnFrame V. dts=%d, len=%d", m.UniqueKey, frame.Dts, len(frame.Raw))
// 收到视频可能触发建立fragment的条件是
// 关键帧数据 &&
// ((没有收到过音频seq header) || -> 只有视频
// (收到过音频seq header && fragment没有打开) || -> 音视频都有且都已ready
// (收到过音频seq header && fragment已经打开 && 音频缓存数据不为空) -> 为什么音频缓存需不为空?
// )
boundary = frame.Key && (!streamer.AudioSeqHeaderCached() || !m.opened || !streamer.AudioCacheEmpty())
if err := m.updateFragment(frame.Dts, boundary); err != nil {
nazalog.Errorf("[%s] update fragment error. err=%+v", m.UniqueKey, err)
Log.Errorf("[%s] update fragment error. err=%+v", m.UniqueKey, err)
return
}
// TODO(chef): 有updateFragment的返回值判断这里的判断可以考虑删除
if !m.opened {
nazalog.Warnf("[%s] OnFrame V not opened. boundary=%t, key=%t", m.UniqueKey, boundary, frame.Key)
Log.Warnf("[%s] OnFrame V not opened. boundary=%t, key=%t", m.UniqueKey, boundary, frame.Key)
return
}
//nazalog.Debugf("[%s] WriteFrame V. dts=%d, len=%d", m.UniqueKey, frame.Dts, len(frame.Raw))
//Log.Debugf("[%s] WriteFrame V. dts=%d, len=%d", m.UniqueKey, frame.Dts, len(frame.Raw))
}
mpegts.PackTsPacket(frame, func(packet []byte) {
if m.enable {
if err := m.fragment.WriteFile(packet); err != nil {
nazalog.Errorf("[%s] fragment write error. err=%+v", m.UniqueKey, err)
Log.Errorf("[%s] fragment write error. err=%+v", m.UniqueKey, err)
return
}
}
@ -206,13 +201,19 @@ func (m *Muxer) OnFrame(streamer *Streamer, frame *mpegts.Frame) {
}
}
// ---------------------------------------------------------------------------------------------------------------------
func (m *Muxer) OutPath() string {
return m.outPath
}
// 决定是否开启新的TS切片文件注意可能已经有TS切片也可能没有这是第一个切片
// ---------------------------------------------------------------------------------------------------------------------
// updateFragment 决定是否开启新的TS切片文件注意可能已经有TS切片也可能没有这是第一个切片
//
// @param boundary 调用方认为可能是开启新TS切片的时间点
// @param boundary: 调用方认为可能是开启新TS切片的时间点
//
// @return: 理论上,只有文件操作失败才会返回错误
//
func (m *Muxer) updateFragment(ts uint64, boundary bool) error {
discont := true
@ -231,7 +232,7 @@ func (m *Muxer) updateFragment(ts uint64, boundary bool) error {
//
maxfraglen := uint64(m.config.FragmentDurationMs * 90 * 10)
if (ts > m.fragTs && ts-m.fragTs > maxfraglen) || (m.fragTs > ts && m.fragTs-ts > negMaxfraglen) {
nazalog.Warnf("[%s] force fragment split. fragTs=%d, ts=%d", m.UniqueKey, m.fragTs, ts)
Log.Warnf("[%s] force fragment split. fragTs=%d, ts=%d", m.UniqueKey, m.fragTs, ts)
if err := m.closeFragment(false); err != nil {
return err
@ -279,7 +280,11 @@ func (m *Muxer) updateFragment(ts uint64, boundary bool) error {
return nil
}
// @param discont 不连续标志会在m3u8文件的fragment前增加`#EXT-X-DISCONTINUITY`
// openFragment
//
// @param discont: 不连续标志会在m3u8文件的fragment前增加`#EXT-X-DISCONTINUITY`
//
// @return: 理论上,只有文件操作失败才会返回错误
//
func (m *Muxer) openFragment(ts uint64, discont bool) error {
if m.opened {
@ -314,6 +319,10 @@ func (m *Muxer) openFragment(ts uint64, discont bool) error {
return nil
}
// closeFragment
//
// @return: 理论上,只有文件操作失败才会返回错误
//
func (m *Muxer) closeFragment(isLast bool) error {
if !m.opened {
// 注意首次调用closeFragment时有可能opened为false
@ -343,7 +352,7 @@ func (m *Muxer) closeFragment(isLast bool) error {
if frag.filename != "" {
filenameWithPath := PathStrategy.GetTsFileNameWithPath(m.outPath, frag.filename)
if err := fslCtx.Remove(filenameWithPath); err != nil {
nazalog.Warnf("[%s] remove stale fragment file failed. filename=%s, err=%+v", m.UniqueKey, filenameWithPath, err)
Log.Warnf("[%s] remove stale fragment file failed. filename=%s, err=%+v", m.UniqueKey, filenameWithPath, err)
}
}
}
@ -371,7 +380,7 @@ func (m *Muxer) writeRecordPlaylist() {
content = bytes.TrimSuffix(content, []byte("#EXT-X-ENDLIST\n"))
content, err = updateTargetDurationInM3u8(content, int(m.recordMaxFragDuration))
if err != nil {
nazalog.Errorf("[%s] update target duration failed. err=%+v", m.UniqueKey, err)
Log.Errorf("[%s] update target duration failed. err=%+v", m.UniqueKey, err)
return
}
@ -400,7 +409,7 @@ func (m *Muxer) writeRecordPlaylist() {
}
if err := writeM3u8File(content, m.recordPlayListFilename, m.recordPlayListFilenameBak); err != nil {
nazalog.Errorf("[%s] write record m3u8 file error. err=%+v", m.UniqueKey, err)
Log.Errorf("[%s] write record m3u8 file error. err=%+v", m.UniqueKey, err)
}
}
@ -438,7 +447,7 @@ func (m *Muxer) writePlaylist(isLast bool) {
}
if err := writeM3u8File(buf.Bytes(), m.playlistFilename, m.playlistFilenameBak); err != nil {
nazalog.Errorf("[%s] write live m3u8 file error. err=%+v", m.UniqueKey, err)
Log.Errorf("[%s] write live m3u8 file error. err=%+v", m.UniqueKey, err)
}
}
@ -447,9 +456,9 @@ func (m *Muxer) ensureDir() {
return
}
//err := fslCtx.RemoveAll(m.outPath)
//nazalog.Assert(nil, err)
//Log.Assert(nil, err)
err := fslCtx.MkdirAll(m.outPath, 0777)
nazalog.Assert(nil, err)
Log.Assert(nil, err)
}
// ---------------------------------------------------------------------------------------------------------------------

@ -43,17 +43,17 @@ type IPathRequestStrategy interface {
GetRequestInfo(urlCtx base.UrlContext, rootOutPath string) RequestInfo
}
// 落盘策略
// IPathWriteStrategy 落盘策略
type IPathWriteStrategy interface {
// 获取单个流对应的文件根路径
// GetMuxerOutPath 获取单个流对应的文件根路径
GetMuxerOutPath(rootOutPath string, streamName string) string
// 获取单个流对应的m3u8文件路径
// GetLiveM3u8FileName 获取单个流对应的m3u8文件路径
//
// @param outPath: func GetMuxerOutPath的结果
GetLiveM3u8FileName(outPath string, streamName string) string
// 获取单个流对应的record类型的m3u8文件路径
// GetRecordM3u8FileName 获取单个流对应的record类型的m3u8文件路径
//
// live m3u8和record m3u8的区别
// live记录的是当前最近的可播放内容record记录的是从流开始时的可播放内容
@ -61,12 +61,12 @@ type IPathWriteStrategy interface {
// @param outPath: func GetMuxerOutPath的结果
GetRecordM3u8FileName(outPath string, streamName string) string
// 获取单个流对应的ts文件路径
// GetTsFileNameWithPath 获取单个流对应的ts文件路径
//
// @param outPath: func GetMuxerOutPath的结果
GetTsFileNameWithPath(outPath string, fileName string) string
// ts文件名的生成策略
// GetTsFileName ts文件名的生成策略
GetTsFileName(streamName string, index int, timestamp int) string
}
@ -77,7 +77,7 @@ const (
recordM3u8FileName = "record.m3u8"
)
// 默认的路由,落盘策略
// DefaultPathStrategy 默认的路由,落盘策略
//
// 每个流在<rootPath>下以流名称生成一个子目录,目录下包含:
//
@ -136,7 +136,7 @@ func (dps *DefaultPathStrategy) GetRequestInfo(urlCtx base.UrlContext, rootOutPa
return
}
// <rootOutPath>/<streamName>
// GetMuxerOutPath <rootOutPath>/<streamName>
func (*DefaultPathStrategy) GetMuxerOutPath(rootOutPath string, streamName string) string {
return filepath.Join(rootOutPath, streamName)
}

@ -11,8 +11,6 @@ package hls_test
import (
"testing"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hls"
@ -48,7 +46,7 @@ func TestDefaultPathStrategy_GetRequestInfo(t *testing.T) {
for k, v := range golden {
ctx, err := base.ParseUrl(k, -1)
nazalog.Assert(nil, err)
hls.Log.Assert(nil, err)
out := dps.GetRequestInfo(ctx, rootOutPath)
assert.Equal(t, v, out)
}

@ -13,8 +13,11 @@ import (
"github.com/q191201771/lal/pkg/mpegts"
)
// Queue
//
// 缓存流起始的一些数据,判断流中是否存在音频、视频,以及编码格式
// 一旦判断结束,该队列变成直进直出,不再有实际缓存
//
type Queue struct {
maxMsgSize int
data []base.RtmpMsg
@ -26,14 +29,14 @@ type Queue struct {
}
type IQueueObserver interface {
// 该回调一定发生在数据回调之前
// OnPatPmt 该回调一定发生在数据回调之前
// TODO(chef) 这里可以考虑换成只通知drain由上层完成FragmentHeader的组装逻辑
OnPatPmt(b []byte)
OnPop(msg base.RtmpMsg)
}
// @param maxMsgSize 最大缓存多少个包
// NewQueue @param maxMsgSize 最大缓存多少个包
func NewQueue(maxMsgSize int, observer IQueueObserver) *Queue {
return &Queue{
maxMsgSize: maxMsgSize,
@ -45,7 +48,7 @@ func NewQueue(maxMsgSize int, observer IQueueObserver) *Queue {
}
}
// @param msg 函数调用结束后,内部不持有该内存块
// Push @param msg 函数调用结束后,内部不持有该内存块
func (q *Queue) Push(msg base.RtmpMsg) {
if q.done {
q.observer.OnPop(msg)

@ -12,8 +12,6 @@ import (
"net/http"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
)
type ServerHandler struct {
@ -29,14 +27,14 @@ func NewServerHandler(outPath string) *ServerHandler {
func (s *ServerHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
urlCtx, err := base.ParseUrl(base.ParseHttpRequest(req), 80)
if err != nil {
nazalog.Errorf("parse url. err=%+v", err)
Log.Errorf("parse url. err=%+v", err)
return
}
s.ServeHTTPWithUrlCtx(resp, urlCtx)
}
func (s *ServerHandler) ServeHTTPWithUrlCtx(resp http.ResponseWriter, urlCtx base.UrlContext) {
//nazalog.Debugf("%+v", req)
//Log.Debugf("%+v", req)
// TODO chef:
// - check appname in URI path
@ -45,17 +43,17 @@ func (s *ServerHandler) ServeHTTPWithUrlCtx(resp http.ResponseWriter, urlCtx bas
filetype := urlCtx.GetFileType()
ri := PathStrategy.GetRequestInfo(urlCtx, s.outPath)
//nazalog.Debugf("%+v", ri)
//Log.Debugf("%+v", ri)
if filename == "" || (filetype != "m3u8" && filetype != "ts") || ri.StreamName == "" || ri.FileNameWithPath == "" {
nazalog.Warnf("invalid hls request. url=%+v, request=%+v", urlCtx, ri)
Log.Warnf("invalid hls request. url=%+v, request=%+v", urlCtx, ri)
resp.WriteHeader(404)
return
}
content, err := ReadFile(ri.FileNameWithPath)
if err != nil {
nazalog.Warnf("read hls file failed. request=%+v, err=%+v", ri, err)
Log.Warnf("read hls file failed. request=%+v, err=%+v", ri, err)
resp.WriteHeader(404)
return
}

@ -18,23 +18,24 @@ import (
"github.com/q191201771/lal/pkg/mpegts"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazalog"
)
type StreamerObserver interface {
// @param b const只读内存块上层可以持有但是不允许修改
// OnPatPmt @param b const只读内存块上层可以持有但是不允许修改
OnPatPmt(b []byte)
// OnFrame
//
// @param streamer: 供上层获取streamer内部的一些状态比如spspps是否已缓存音频缓存队列是否有数据等
//
// @param frame: 各字段含义见mpegts.Frame结构体定义
// @param frame: 各字段含义见 mpegts.Frame 结构体定义
// frame.CC 注意回调结束后Streamer会保存frame.CC上层在TS打包完成后可通过frame.CC将cc值传递给Streamer
// frame.Raw 回调结束后,这块内存可能会被内部重复使用
//
OnFrame(streamer *Streamer, frame *mpegts.Frame)
OnFrame(streamer *Streamer, frame *mpegts.Frame, boundary bool)
}
// 输入rtmp流回调转封装成Annexb格式的流
// Streamer 输入rtmp流回调转封装成Annexb格式的流
type Streamer struct {
UniqueKey string
@ -47,6 +48,8 @@ type Streamer struct {
audioCacheFirstFramePts uint64 // audioCacheFrames中第一个音频帧的时间戳 TODO chef: rename to DTS
audioCc uint8
videoCc uint8
opened bool
}
func NewStreamer(observer StreamerObserver) *Streamer {
@ -62,13 +65,15 @@ func NewStreamer(observer StreamerObserver) *Streamer {
return streamer
}
// @param msg msg.Payload 调用结束后,函数内部不会持有这块内存
// FeedRtmpMessage @param msg msg.Payload 调用结束后,函数内部不会持有这块内存
//
// TODO chef: 可以考虑数据有问题时,返回给上层,直接主动关闭输入流的连接
func (s *Streamer) FeedRtmpMessage(msg base.RtmpMsg) {
s.calcFragmentHeaderQueue.Push(msg)
}
// ----- implement IQueueObserver of Queue -----------------------------------------------------------------------------
func (s *Streamer) OnPatPmt(b []byte) {
s.observer.OnPatPmt(b)
}
@ -82,6 +87,34 @@ func (s *Streamer) OnPop(msg base.RtmpMsg) {
}
}
// ---------------------------------------------------------------------------------------------------------------------
// FlushAudio
//
// 吐出音频数据的三种情况:
// 1. 收到音频或视频时,音频缓存队列已达到一定长度
// 2. 打开一个新的TS文件切片时
// 3. 输入流关闭时
//
func (s *Streamer) FlushAudio() {
if s.audioCacheFrames == nil {
return
}
var frame mpegts.Frame
frame.Cc = s.audioCc
frame.Dts = s.audioCacheFirstFramePts
frame.Pts = s.audioCacheFirstFramePts
frame.Key = false
frame.Raw = s.audioCacheFrames
frame.Pid = mpegts.PidAudio
frame.Sid = mpegts.StreamIdAudio
s.onFrame(&frame)
s.audioCc = frame.Cc
s.audioCacheFrames = nil
}
func (s *Streamer) AudioSeqHeaderCached() bool {
return s.ascCtx != nil
}
@ -94,12 +127,14 @@ func (s *Streamer) AudioCacheEmpty() bool {
return s.audioCacheFrames == nil
}
// ----- private -------------------------------------------------------------------------------------------------------
func (s *Streamer) feedVideo(msg base.RtmpMsg) {
// 注意有一种情况是msg.Payload为 27 02 00 00 00
// 此时打印错误并返回也不影响
//
if len(msg.Payload) <= 5 {
nazalog.Errorf("[%s] invalid video message length. header=%+v, payload=%s", s.UniqueKey, msg.Header, hex.Dump(msg.Payload))
Log.Errorf("[%s] invalid video message length. header=%+v, payload=%s", s.UniqueKey, msg.Header, hex.Dump(msg.Payload))
return
}
@ -114,12 +149,12 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
var err error
if msg.IsAvcKeySeqHeader() {
if s.spspps, err = avc.SpsPpsSeqHeader2Annexb(msg.Payload); err != nil {
nazalog.Errorf("[%s] cache spspps failed. err=%+v", s.UniqueKey, err)
Log.Errorf("[%s] cache spspps failed. err=%+v", s.UniqueKey, err)
}
return
} else if msg.IsHevcKeySeqHeader() {
if s.spspps, err = hevc.VpsSpsPpsSeqHeader2Annexb(msg.Payload); err != nil {
nazalog.Errorf("[%s] cache vpsspspps failed. err=%+v", s.UniqueKey, err)
Log.Errorf("[%s] cache vpsspspps failed. err=%+v", s.UniqueKey, err)
}
return
}
@ -134,7 +169,7 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
// msg中可能有多个NALU逐个获取
nals, err := avc.SplitNaluAvcc(msg.Payload[5:])
if err != nil {
nazalog.Errorf("[%s] iterate nalu failed. err=%+v, header=%+v, payload=%s", err, s.UniqueKey, msg.Header, hex.Dump(nazabytes.Prefix(msg.Payload, 32)))
Log.Errorf("[%s] iterate nalu failed. err=%+v, header=%+v, payload=%s", err, s.UniqueKey, msg.Header, hex.Dump(nazabytes.Prefix(msg.Payload, 32)))
return
}
for _, nal := range nals {
@ -146,7 +181,7 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
nalType = hevc.ParseNaluType(nal[0])
}
//nazalog.Debugf("[%s] naltype=%d, len=%d(%d), cts=%d, key=%t.", s.UniqueKey, nalType, nalBytes, len(msg.Payload), cts, msg.IsVideoKeyNalu())
//Log.Debugf("[%s] naltype=%d, len=%d(%d), cts=%d, key=%t.", s.UniqueKey, nalType, nalBytes, len(msg.Payload), cts, msg.IsVideoKeyNalu())
// 过滤掉原流中的sps pps aud
// sps pps前面已经缓存过了后面有自己的写入逻辑
@ -176,7 +211,7 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
case avc.NaluTypeIdrSlice:
if !spsppsSent {
if out, err = s.appendSpsPps(out); err != nil {
nazalog.Warnf("[%s] append spspps by not exist.", s.UniqueKey)
Log.Warnf("[%s] append spspps by not exist.", s.UniqueKey)
return
}
}
@ -190,7 +225,7 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
case hevc.NaluTypeSliceIdr, hevc.NaluTypeSliceIdrNlp, hevc.NaluTypeSliceCranut:
if !spsppsSent {
if out, err = s.appendSpsPps(out); err != nil {
nazalog.Warnf("[%s] append spspps by not exist.", s.UniqueKey)
Log.Warnf("[%s] append spspps by not exist.", s.UniqueKey)
return
}
}
@ -227,30 +262,30 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
frame.Pid = mpegts.PidVideo
frame.Sid = mpegts.StreamIdVideo
s.observer.OnFrame(s, &frame)
s.onFrame(&frame)
s.videoCc = frame.Cc
}
func (s *Streamer) feedAudio(msg base.RtmpMsg) {
if len(msg.Payload) < 3 {
nazalog.Errorf("[%s] invalid audio message length. len=%d", s.UniqueKey, len(msg.Payload))
Log.Errorf("[%s] invalid audio message length. len=%d", s.UniqueKey, len(msg.Payload))
return
}
if msg.Payload[0]>>4 != base.RtmpSoundFormatAac {
return
}
//nazalog.Debugf("[%s] hls: feedAudio. dts=%d len=%d", s.UniqueKey, msg.Header.TimestampAbs, len(msg.Payload))
//Log.Debugf("[%s] hls: feedAudio. dts=%d len=%d", s.UniqueKey, msg.Header.TimestampAbs, len(msg.Payload))
if msg.Payload[1] == base.RtmpAacPacketTypeSeqHeader {
if err := s.cacheAacSeqHeader(msg); err != nil {
nazalog.Errorf("[%s] cache aac seq header failed. err=%+v", s.UniqueKey, err)
Log.Errorf("[%s] cache aac seq header failed. err=%+v", s.UniqueKey, err)
}
return
}
if !s.AudioSeqHeaderCached() {
nazalog.Warnf("[%s] feed audio message but aac seq header not exist.", s.UniqueKey)
Log.Warnf("[%s] feed audio message but aac seq header not exist.", s.UniqueKey)
return
}
@ -269,29 +304,6 @@ func (s *Streamer) feedAudio(msg base.RtmpMsg) {
s.audioCacheFrames = append(s.audioCacheFrames, msg.Payload[2:]...)
}
// 吐出音频数据的三种情况:
// 1. 收到音频或视频时,音频缓存队列已达到一定长度
// 2. 打开一个新的TS文件切片时
// 3. 输入流关闭时
func (s *Streamer) FlushAudio() {
if s.audioCacheFrames == nil {
return
}
var frame mpegts.Frame
frame.Cc = s.audioCc
frame.Dts = s.audioCacheFirstFramePts
frame.Pts = s.audioCacheFirstFramePts
frame.Key = false
frame.Raw = s.audioCacheFrames
frame.Pid = mpegts.PidAudio
frame.Sid = mpegts.StreamIdAudio
s.observer.OnFrame(s, &frame)
s.audioCc = frame.Cc
s.audioCacheFrames = nil
}
func (s *Streamer) cacheAacSeqHeader(msg base.RtmpMsg) error {
var err error
s.ascCtx, err = aac.NewAscContext(msg.Payload[2:])
@ -306,3 +318,27 @@ func (s *Streamer) appendSpsPps(out []byte) ([]byte, error) {
out = append(out, s.spspps...)
return out, nil
}
func (s *Streamer) onFrame(frame *mpegts.Frame) {
var boundary bool
if frame.Sid == mpegts.StreamIdAudio {
// 为了考虑没有视频的情况也能切片所以这里判断spspps为空时也建议生成fragment
boundary = !s.VideoSeqHeaderCached()
} else {
// 收到视频可能触发建立fragment的条件是
// 关键帧数据 &&
// (
// (没有收到过音频seq header) || 说明 只有视频
// (收到过音频seq header && fragment没有打开) || 说明 音视频都有且都已ready
// (收到过音频seq header && fragment已经打开 && 音频缓存数据不为空) 说明 为什么音频缓存需不为空?
// )
boundary = frame.Key && (!s.AudioSeqHeaderCached() || !s.opened || !s.AudioCacheEmpty())
}
if boundary {
s.opened = true
}
s.observer.OnFrame(s, frame, boundary)
}

@ -8,12 +8,17 @@
package hls
import "github.com/q191201771/naza/pkg/mock"
import (
"github.com/q191201771/naza/pkg/mock"
"github.com/q191201771/naza/pkg/nazalog"
)
var (
PathStrategy IPathStrategy = &DefaultPathStrategy{}
Clock = mock.NewStdClock()
Log = nazalog.GetGlobalLogger()
)
var (

@ -12,15 +12,13 @@ import (
"os"
"testing"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/naza/pkg/assert"
)
func BenchmarkFlvFileReader(b *testing.B) {
const flvFile = "testdata/test.flv"
if _, err := os.Lstat(flvFile); err != nil {
nazalog.Warnf("lstat %s error. err=%+v", flvFile, err)
Log.Warnf("lstat %s error. err=%+v", flvFile, err)
return
}
@ -38,13 +36,13 @@ func BenchmarkFlvFileReader(b *testing.B) {
}
r.Dispose()
}
//nazalog.Debug(tmp)
//Log.Debug(tmp)
}
func BenchmarkCloneTag(b *testing.B) {
const flvFile = "testdata/test.flv"
if _, err := os.Lstat(flvFile); err != nil {
nazalog.Warnf("lstat %s error. err=%+v", flvFile, err)
Log.Warnf("lstat %s error. err=%+v", flvFile, err)
return
}

@ -22,7 +22,6 @@ import (
"github.com/q191201771/naza/pkg/nazahttp"
"github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/naza/pkg/nazalog"
)
type PullSessionOption struct {
@ -65,11 +64,11 @@ func NewPullSession(modOptions ...ModPullSessionOption) *PullSession {
uniqueKey: uk,
option: option,
}
nazalog.Infof("[%s] lifecycle new httpflv PullSession. session=%p", uk, s)
Log.Infof("[%s] lifecycle new httpflv PullSession. session=%p", uk, s)
return s
}
// @param tag: 底层保证回调上来的Raw数据长度是完整的但是不会分析Raw内部的编码数据
// OnReadFlvTag @param tag: 底层保证回调上来的Raw数据长度是完整的但是不会分析Raw内部的编码数据
type OnReadFlvTag func(tag Tag)
// Pull 阻塞直到和对端完成拉流前,握手部分的工作,或者发生错误
@ -83,7 +82,7 @@ type OnReadFlvTag func(tag Tag)
// @param onReadFlvTag 读取到 flv tag 数据时回调。回调结束后PullSession 不会再使用这块 <tag> 数据。
//
func (session *PullSession) Pull(rawUrl string, onReadFlvTag OnReadFlvTag) error {
nazalog.Debugf("[%s] pull. url=%s", session.uniqueKey, rawUrl)
Log.Debugf("[%s] pull. url=%s", session.uniqueKey, rawUrl)
var (
ctx context.Context
@ -116,32 +115,32 @@ func (session *PullSession) WaitChan() <-chan error {
// ---------------------------------------------------------------------------------------------------------------------
// 文档请参考: interface ISessionUrlContext
// Url 文档请参考: interface ISessionUrlContext
func (session *PullSession) Url() string {
return session.urlCtx.Url
}
// 文档请参考: interface ISessionUrlContext
// AppName 文档请参考: interface ISessionUrlContext
func (session *PullSession) AppName() string {
return session.urlCtx.PathWithoutLastItem
}
// 文档请参考: interface ISessionUrlContext
// StreamName 文档请参考: interface ISessionUrlContext
func (session *PullSession) StreamName() string {
return session.urlCtx.LastItemOfPath
}
// 文档请参考: interface ISessionUrlContext
// RawQuery 文档请参考: interface ISessionUrlContext
func (session *PullSession) RawQuery() string {
return session.urlCtx.RawQuery
}
// 文档请参考: interface IObject
// UniqueKey 文档请参考: interface IObject
func (session *PullSession) UniqueKey() string {
return session.uniqueKey
}
// 文档请参考: interface ISessionStat
// UpdateStat 文档请参考: interface ISessionStat
func (session *PullSession) UpdateStat(intervalSec uint32) {
currStat := session.conn.GetStat()
rDiff := currStat.ReadBytesSum - session.prevConnStat.ReadBytesSum
@ -152,7 +151,7 @@ func (session *PullSession) UpdateStat(intervalSec uint32) {
session.prevConnStat = currStat
}
// 文档请参考: interface ISessionStat
// GetStat 文档请参考: interface ISessionStat
func (session *PullSession) GetStat() base.StatSession {
connStat := session.conn.GetStat()
session.stat.ReadBytesSum = connStat.ReadBytesSum
@ -160,7 +159,7 @@ func (session *PullSession) GetStat() base.StatSession {
return session.stat
}
// 文档请参考: interface ISessionStat
// IsAlive 文档请参考: interface ISessionStat
func (session *PullSession) IsAlive() (readAlive, writeAlive bool) {
currStat := session.conn.GetStat()
if session.staleStat == nil {
@ -201,13 +200,13 @@ func (session *PullSession) pullContext(ctx context.Context, rawUrl string, onRe
if statusCode == "301" || statusCode == "302" {
url = headers.Get("Location")
if url == "" {
nazalog.Warnf("[%s] redirect but Location not found. headers=%+v", session.uniqueKey, headers)
Log.Warnf("[%s] redirect but Location not found. headers=%+v", session.uniqueKey, headers)
errChan <- nil
return
}
_ = session.conn.Close()
nazalog.Debugf("[%s] redirect to %s", session.uniqueKey, url)
Log.Debugf("[%s] redirect to %s", session.uniqueKey, url)
continue
}
@ -243,7 +242,7 @@ func (session *PullSession) connect(rawUrl string) (err error) {
return
}
nazalog.Debugf("[%s] > tcp connect. %s", session.uniqueKey, session.urlCtx.HostWithPort)
Log.Debugf("[%s] > tcp connect. %s", session.uniqueKey, session.urlCtx.HostWithPort)
var conn net.Conn
if session.urlCtx.Scheme == "https" {
@ -259,7 +258,7 @@ func (session *PullSession) connect(rawUrl string) (err error) {
return err
}
nazalog.Debugf("[%s] tcp connect succ. remote=%s", session.uniqueKey, conn.RemoteAddr().String())
Log.Debugf("[%s] tcp connect succ. remote=%s", session.uniqueKey, conn.RemoteAddr().String())
session.conn = connection.New(conn, func(option *connection.Option) {
option.ReadBufSize = readBufSize
@ -271,7 +270,7 @@ func (session *PullSession) connect(rawUrl string) (err error) {
func (session *PullSession) writeHttpRequest() error {
// # 发送 http GET 请求
nazalog.Debugf("[%s] > W http request. GET %s", session.uniqueKey, session.urlCtx.PathWithRawQuery)
Log.Debugf("[%s] > W http request. GET %s", session.uniqueKey, session.urlCtx.PathWithRawQuery)
req := fmt.Sprintf("GET %s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\nRange: byte=0-\r\nConnection: close\r\nHost: %s\r\nIcy-MetaData: 1\r\n\r\n",
session.urlCtx.PathWithRawQuery, base.LalHttpflvPullSessionUa, session.urlCtx.StdHost)
_, err := session.conn.Write([]byte(req))
@ -288,7 +287,7 @@ func (session *PullSession) readHttpRespHeader() (statusCode string, headers htt
return
}
nazalog.Debugf("[%s] < R http response header. statusLine=%s", session.uniqueKey, statusLine)
Log.Debugf("[%s] < R http response header. statusLine=%s", session.uniqueKey, statusLine)
return
}
@ -298,7 +297,7 @@ func (session *PullSession) readFlvHeader() ([]byte, error) {
if err != nil {
return flvHeader, err
}
nazalog.Debugf("[%s] < R http flv header.", session.uniqueKey)
Log.Debugf("[%s] < R http flv header.", session.uniqueKey)
// TODO chef: check flv header's value
return flvHeader, nil
@ -331,7 +330,7 @@ func (session *PullSession) runReadLoop(onReadFlvTag OnReadFlvTag) {
func (session *PullSession) dispose(err error) error {
var retErr error
session.disposeOnce.Do(func() {
nazalog.Infof("[%s] lifecycle dispose httpflv PullSession. err=%+v", session.uniqueKey, err)
Log.Infof("[%s] lifecycle dispose httpflv PullSession. err=%+v", session.uniqueKey, err)
if session.conn == nil {
retErr = base.ErrSessionNotStarted
return

@ -12,8 +12,6 @@ import (
"time"
"github.com/q191201771/naza/pkg/mock"
"github.com/q191201771/naza/pkg/nazalog"
)
var Clock = mock.NewStdClock()
@ -61,7 +59,7 @@ func (f *FlvFilePump) Pump(filename string, onFlvTag OnPumpFlvTag) error {
return f.PumpWithTags(tags, onFlvTag)
}
// @return error 暂时只做预留目前只会返回nil
// PumpWithTags @return error 暂时只做预留目前只会返回nil
//
func (f *FlvFilePump) PumpWithTags(tags []Tag, onFlvTag OnPumpFlvTag) error {
var totalBaseTs uint32 // 整体的基础时间戳。每轮最后更新
@ -79,7 +77,7 @@ func (f *FlvFilePump) PumpWithTags(tags []Tag, onFlvTag OnPumpFlvTag) error {
// 循环一次,代表遍历文件一次
for roundIndex := 0; ; roundIndex++ {
nazalog.Debugf("new round. index=%d", roundIndex)
Log.Debugf("new round. index=%d", roundIndex)
hasReadThisBaseTs = false

@ -13,8 +13,6 @@ import (
"os"
"testing"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/naza/pkg/assert"
"github.com/q191201771/naza/pkg/mock"
@ -30,7 +28,7 @@ func TestHttpflv(t *testing.T) {
func TestFlvFilePump(t *testing.T) {
const flvFile = "../../testdata/test.flv"
if _, err := os.Lstat(flvFile); err != nil {
nazalog.Warnf("lstat %s error. err=%+v", flvFile, err)
httpflv.Log.Warnf("lstat %s error. err=%+v", flvFile, err)
return
}

@ -14,8 +14,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/naza/pkg/nazalog"
)
var flvHttpResponseHeader []byte
@ -44,7 +42,7 @@ func NewSubSession(conn net.Conn, urlCtx base.UrlContext, isWebSocket bool, webs
IsFresh: true,
ShouldWaitVideoKeyFrame: true,
}
nazalog.Infof("[%s] lifecycle new httpflv SubSession. session=%p, remote addr=%s", uk, s, conn.RemoteAddr().String())
Log.Infof("[%s] lifecycle new httpflv SubSession. session=%p, remote addr=%s", uk, s, conn.RemoteAddr().String())
return s
}
@ -57,19 +55,19 @@ func (session *SubSession) RunLoop() error {
}
func (session *SubSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose httpflv SubSession.", session.core.UniqueKey())
Log.Infof("[%s] lifecycle dispose httpflv SubSession.", session.core.UniqueKey())
return session.core.Dispose()
}
// ---------------------------------------------------------------------------------------------------------------------
func (session *SubSession) WriteHttpResponseHeader() {
nazalog.Debugf("[%s] > W http response header.", session.core.UniqueKey())
Log.Debugf("[%s] > W http response header.", session.core.UniqueKey())
session.core.WriteHttpResponseHeader(flvHttpResponseHeader)
}
func (session *SubSession) WriteFlvHeader() {
nazalog.Debugf("[%s] > W http flv header.", session.core.UniqueKey())
Log.Debugf("[%s] > W http flv header.", session.core.UniqueKey())
session.core.Write(FlvHeader)
}

@ -26,7 +26,7 @@ type Tag struct {
Raw []byte // 结构为 (11字节的 tag header) + (body) + (4字节的 prev tag size)
}
// 只包含数据部分去除了前面11字节的tag header和后面4字节的prev tag size
// Payload 只包含数据部分去除了前面11字节的tag header和后面4字节的prev tag size
//
func (tag *Tag) Payload() []byte {
return tag.Raw[TagHeaderSize : len(tag.Raw)-PrevTagSizeFieldSize]
@ -52,7 +52,7 @@ func (tag *Tag) IsHevcKeySeqHeader() bool {
return tag.Header.Type == TagTypeVideo && tag.Raw[TagHeaderSize] == HevcKeyFrame && tag.Raw[TagHeaderSize+1] == HevcPacketTypeSeqHeader
}
// AVC或HEVC的seq header
// IsVideoKeySeqHeader AVC或HEVC的seq header
func (tag *Tag) IsVideoKeySeqHeader() bool {
return tag.IsAvcKeySeqHeader() || tag.IsHevcKeySeqHeader()
}
@ -65,7 +65,7 @@ func (tag *Tag) IsHevcKeyNalu() bool {
return tag.Header.Type == TagTypeVideo && tag.Raw[TagHeaderSize] == HevcKeyFrame && tag.Raw[TagHeaderSize+1] == HevcPacketTypeNalu
}
// AVC或HEVC的关键帧
// IsVideoKeyNalu AVC或HEVC的关键帧
func (tag *Tag) IsVideoKeyNalu() bool {
return tag.IsAvcKeyNalu() || tag.IsHevcKeyNalu()
}
@ -87,7 +87,7 @@ func (tag *Tag) ModTagTimestamp(timestamp uint32) {
tag.Raw[7] = byte(timestamp >> 24)
}
// 打包一个序列化后的 tag 二进制buffer包含 tag headerbodyprev tag size
// PackHttpflvTag 打包一个序列化后的 tag 二进制buffer包含 tag headerbodyprev tag size
func PackHttpflvTag(t uint8, timestamp uint32, in []byte) []byte {
out := make([]byte, TagHeaderSize+len(in)+PrevTagSizeFieldSize)
out[0] = t

@ -8,10 +8,14 @@
package httpflv
import "github.com/q191201771/naza/pkg/nazalog"
var (
SubSessionWriteChanSize = 1024 // SubSession发送数据时channel的大小
SubSessionWriteTimeoutMs = 10000
FlvHeader = []byte{0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00}
Log = nazalog.GetGlobalLogger()
)
var readBufSize = 256 //16384 // ClientPullSession读取数据时

@ -0,0 +1,19 @@
// Copyright 2022, 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 httpts_test
import (
"testing"
"github.com/q191201771/lal/pkg/innertest"
)
func TestHttpts(t *testing.T) {
innertest.Entry(t)
}

@ -13,7 +13,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/naza/pkg/nazalog"
)
var tsHttpResponseHeader []byte
@ -40,7 +39,7 @@ func NewSubSession(conn net.Conn, urlCtx base.UrlContext, isWebSocket bool, webs
}),
true,
}
nazalog.Infof("[%s] lifecycle new httpts SubSession. session=%p, remote addr=%s", uk, s, conn.RemoteAddr().String())
Log.Infof("[%s] lifecycle new httpts SubSession. session=%p, remote addr=%s", uk, s, conn.RemoteAddr().String())
return s
}
@ -53,14 +52,14 @@ func (session *SubSession) RunLoop() error {
}
func (session *SubSession) Dispose() error {
nazalog.Infof("[%s] lifecycle dispose httpts SubSession.", session.core.UniqueKey())
Log.Infof("[%s] lifecycle dispose httpts SubSession.", session.core.UniqueKey())
return session.core.Dispose()
}
// ---------------------------------------------------------------------------------------------------------------------
func (session *SubSession) WriteHttpResponseHeader() {
nazalog.Debugf("[%s] > W http response header.", session.core.UniqueKey())
Log.Debugf("[%s] > W http response header.", session.core.UniqueKey())
session.core.WriteHttpResponseHeader(tsHttpResponseHeader)
}

@ -8,7 +8,11 @@
package httpts
import "github.com/q191201771/naza/pkg/nazalog"
var (
SubSessionWriteChanSize = 1024
SubSessionWriteTimeoutMs = 10000
Log = nazalog.GetGlobalLogger()
)

@ -17,6 +17,9 @@ import (
"testing"
"time"
"github.com/q191201771/lal/pkg/httpts"
"github.com/q191201771/naza/pkg/filebatch"
"github.com/q191201771/lal/pkg/hls"
"github.com/q191201771/naza/pkg/mock"
@ -30,7 +33,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/filebatch"
"github.com/q191201771/naza/pkg/nazamd5"
"github.com/q191201771/lal/pkg/httpflv"
@ -38,7 +40,6 @@ import (
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/assert"
"github.com/q191201771/naza/pkg/nazaatomic"
"github.com/q191201771/naza/pkg/nazalog"
)
// 开启了一个lalserver
@ -56,15 +57,25 @@ var (
confFile = "../../testdata/lalserver.conf.json"
rFlvFileName = "../../testdata/test.flv"
wFlvPullFileName = "../../testdata/flvpull.flv"
wRtmpPullFileName = "../../testdata/rtmppull.flv"
wRtspPullFileName = "../../testdata/rtsppull.flv"
wFlvPullFileName = "../../testdata/flvpull.flv"
wPlaylistM3u8FileName string
wRecordM3u8FileName string
wHlsTsFilePath string
//wRtspPullFileName = "../../testdata/rtsppull.flv"
pushUrl string
httpflvPullUrl string
httptsPullUrl string
rtmpPullUrl string
rtspPullUrl string
fileTagCount int
httpflvPullTagCount nazaatomic.Uint32
rtmpPullTagCount nazaatomic.Uint32
rtspSdpCtx sdp.LogicContext
rtspPullAvPacketCount nazaatomic.Uint32
httpFlvWriter httpflv.FlvFileWriter
rtmpWriter httpflv.FlvFileWriter
@ -72,12 +83,6 @@ var (
httpflvPullSession *httpflv.PullSession
rtmpPullSession *rtmp.PullSession
rtspPullSession *rtsp.PullSession
fileTagCount int
httpflvPullTagCount nazaatomic.Uint32
rtmpPullTagCount nazaatomic.Uint32
rtspSdpCtx sdp.LogicContext
rtspPullAvPacketCount nazaatomic.Uint32
)
type RtspPullObserver struct {
@ -96,16 +101,17 @@ func (r RtspPullObserver) OnAvPacket(pkt base.AvPacket) {
func Entry(t *testing.T) {
if _, err := os.Lstat(confFile); err != nil {
nazalog.Warnf("lstat %s error. err=%+v", confFile, err)
Log.Warnf("lstat %s error. err=%+v", confFile, err)
return
}
if _, err := os.Lstat(rFlvFileName); err != nil {
nazalog.Warnf("lstat %s error. err=%+v", rFlvFileName, err)
Log.Warnf("lstat %s error. err=%+v", rFlvFileName, err)
return
}
hls.Clock = mock.NewFakeClock()
hls.Clock.Set(time.Date(2022, 1, 16, 23, 24, 25, 0, time.Local))
httpts.SubSessionWriteChanSize = 0
tt = t
@ -123,8 +129,12 @@ func Entry(t *testing.T) {
pushUrl = fmt.Sprintf("rtmp://127.0.0.1%s/live/innertest", config.RtmpConfig.Addr)
httpflvPullUrl = fmt.Sprintf("http://127.0.0.1%s/live/innertest.flv", config.HttpflvConfig.HttpListenAddr)
httptsPullUrl = fmt.Sprintf("http://127.0.0.1%s/live/innertest.ts", config.HttpflvConfig.HttpListenAddr)
rtmpPullUrl = fmt.Sprintf("rtmp://127.0.0.1%s/live/innertest", config.RtmpConfig.Addr)
rtspPullUrl = fmt.Sprintf("rtsp://127.0.0.1%s/live/innertest", config.RtspConfig.Addr)
wPlaylistM3u8FileName = fmt.Sprintf("%sinnertest/playlist.m3u8", config.HlsConfig.OutPath)
wRecordM3u8FileName = fmt.Sprintf("%sinnertest/record.m3u8", config.HlsConfig.OutPath)
wHlsTsFilePath = fmt.Sprintf("%sinnertest/", config.HlsConfig.OutPath)
tags, err := httpflv.ReadAllTagsFromFlvFile(rFlvFileName)
assert.Equal(t, nil, err)
@ -153,9 +163,9 @@ func Entry(t *testing.T) {
assert.Equal(tt, nil, err)
rtmpPullTagCount.Increment()
})
nazalog.Assert(nil, err)
Log.Assert(nil, err)
err = <-rtmpPullSession.WaitChan()
nazalog.Debug(err)
Log.Debug(err)
}()
go func() {
@ -167,9 +177,17 @@ func Entry(t *testing.T) {
assert.Equal(t, nil, err)
httpflvPullTagCount.Increment()
})
nazalog.Assert(nil, err)
Log.Assert(nil, err)
err = <-httpflvPullSession.WaitChan()
nazalog.Debug(err)
Log.Debug(err)
}()
go func() {
//nazalog.Info("CHEFGREPME >")
b, err := httpGet(httptsPullUrl)
assert.Equal(t, 2216332, len(b))
assert.Equal(t, "03f8eac7d2c3d5d85056c410f5fcc756", nazamd5.Md5(b))
Log.Infof("CHEFGREPME %+v", err)
}()
time.Sleep(200 * time.Millisecond)
@ -184,7 +202,7 @@ func Entry(t *testing.T) {
option.PullTimeoutMs = 500
})
err := rtspPullSession.Pull(rtspPullUrl)
nazalog.Debug(err)
Log.Debug(err)
if rtspSdpCtx.RawSdp != nil {
break
}
@ -202,7 +220,7 @@ func Entry(t *testing.T) {
for _, tag := range tags {
assert.Equal(t, nil, err)
chunks := remux.FlvTag2RtmpChunks(tag)
//nazalog.Debugf("rtmp push: %d", fileTagCount.Load())
//Log.Debugf("rtmp push: %d", fileTagCount.Load())
err = pushSession.Write(chunks)
assert.Equal(t, nil, err)
}
@ -220,7 +238,7 @@ func Entry(t *testing.T) {
time.Sleep(10 * time.Millisecond)
}
nazalog.Debug("[innertest] start dispose.")
Log.Debug("[innertest] start dispose.")
pushSession.Dispose()
httpflvPullSession.Dispose()
@ -234,58 +252,60 @@ func Entry(t *testing.T) {
//_ = syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
sm.Dispose()
nazalog.Debugf("tag count. in=%d, out httpflv=%d, out rtmp=%d, out rtsp=%d",
Log.Debugf("tag count. in=%d, out httpflv=%d, out rtmp=%d, out rtsp=%d",
fileTagCount, httpflvPullTagCount.Load(), rtmpPullTagCount.Load(), rtspPullAvPacketCount.Load())
compareFile()
// 检查hls的m3u8文件
playListM3u8, err := ioutil.ReadFile(fmt.Sprintf("%sinnertest/playlist.m3u8", config.HlsConfig.OutPath))
assert.Equal(t, nil, err)
assert.Equal(t, goldenPlaylistM3u8, string(playListM3u8))
recordM3u8, err := ioutil.ReadFile(fmt.Sprintf("%sinnertest/record.m3u8", config.HlsConfig.OutPath))
assert.Equal(t, nil, err)
assert.Equal(t, []byte(goldenRecordM3u8), recordM3u8)
// 检查hls的ts文件
var allContent []byte
var fileNum int
err = filebatch.Walk(
fmt.Sprintf("%sinnertest", config.HlsConfig.OutPath),
false,
".ts",
func(path string, info os.FileInfo, content []byte, err error) []byte {
allContent = append(allContent, content...)
fileNum++
return nil
})
assert.Equal(t, nil, err)
allContentMd5 := nazamd5.Md5(allContent)
assert.Equal(t, 8, fileNum)
assert.Equal(t, 2219152, len(allContent))
assert.Equal(t, "48db6251d40c271fd11b05650f074e0f", allContentMd5)
}
func compareFile() {
r, err := ioutil.ReadFile(rFlvFileName)
assert.Equal(tt, nil, err)
nazalog.Debugf("%s filesize:%d", rFlvFileName, len(r))
Log.Debugf("%s filesize:%d", rFlvFileName, len(r))
// 检查httpflv
w, err := ioutil.ReadFile(wFlvPullFileName)
assert.Equal(tt, nil, err)
nazalog.Debugf("%s filesize:%d", wFlvPullFileName, len(w))
Log.Debugf("%s filesize:%d", wFlvPullFileName, len(w))
res := bytes.Compare(r, w)
assert.Equal(tt, 0, res)
//err = os.Remove(wFlvPullFileName)
assert.Equal(tt, nil, err)
// 检查rtmp
w2, err := ioutil.ReadFile(wRtmpPullFileName)
assert.Equal(tt, nil, err)
nazalog.Debugf("%s filesize:%d", wRtmpPullFileName, len(w2))
Log.Debugf("%s filesize:%d", wRtmpPullFileName, len(w2))
res = bytes.Compare(r, w2)
assert.Equal(tt, 0, res)
//err = os.Remove(wRtmpPullFileName)
assert.Equal(tt, nil, err)
// 检查hls的m3u8文件
playListM3u8, err := ioutil.ReadFile(wPlaylistM3u8FileName)
assert.Equal(tt, nil, err)
assert.Equal(tt, goldenPlaylistM3u8, string(playListM3u8))
recordM3u8, err := ioutil.ReadFile(wRecordM3u8FileName)
assert.Equal(tt, nil, err)
assert.Equal(tt, []byte(goldenRecordM3u8), recordM3u8)
// 检查hls的ts文件
var allContent []byte
var fileNum int
err = filebatch.Walk(
wHlsTsFilePath,
false,
".ts",
func(path string, info os.FileInfo, content []byte, err error) []byte {
allContent = append(allContent, content...)
fileNum++
return nil
})
assert.Equal(tt, nil, err)
allContentMd5 := nazamd5.Md5(allContent)
assert.Equal(tt, 8, fileNum)
assert.Equal(tt, 2219152, len(allContent))
assert.Equal(tt, "48db6251d40c271fd11b05650f074e0f", allContentMd5)
}
func getAllHttpApi(addr string) {
@ -293,32 +313,34 @@ func getAllHttpApi(addr string) {
var err error
b, err = httpGet(fmt.Sprintf("http://%s/api/list", addr))
nazalog.Assert(nil, err)
nazalog.Debugf("%s", string(b))
Log.Assert(nil, err)
Log.Debugf("%s", string(b))
b, err = httpGet(fmt.Sprintf("http://%s/api/stat/lal_info", addr))
nazalog.Assert(nil, err)
nazalog.Debugf("%s", string(b))
Log.Assert(nil, err)
Log.Debugf("%s", string(b))
b, err = httpGet(fmt.Sprintf("http://%s/api/stat/group?stream_name=innertest", addr))
nazalog.Assert(nil, err)
nazalog.Debugf("%s", string(b))
Log.Assert(nil, err)
Log.Debugf("%s", string(b))
b, err = httpGet(fmt.Sprintf("http://%s/api/stat/all_group", addr))
nazalog.Assert(nil, err)
nazalog.Debugf("%s", string(b))
Log.Assert(nil, err)
Log.Debugf("%s", string(b))
var acspr base.ApiCtrlStartPullReq
b, err = httpPost(fmt.Sprintf("http://%s/api/ctrl/start_pull", addr), &acspr)
nazalog.Assert(nil, err)
nazalog.Debugf("%s", string(b))
Log.Assert(nil, err)
Log.Debugf("%s", string(b))
var ackos base.ApiCtrlKickOutSession
b, err = httpPost(fmt.Sprintf("http://%s/api/ctrl/kick_out_session", addr), &ackos)
nazalog.Assert(nil, err)
nazalog.Debugf("%s", string(b))
Log.Assert(nil, err)
Log.Debugf("%s", string(b))
}
// ---------------------------------------------------------------------------------------------------------------------
// TODO(chef): refactor 移入naza中
func httpGet(url string) ([]byte, error) {
@ -339,6 +361,8 @@ func httpPost(url string, info interface{}) ([]byte, error) {
return ioutil.ReadAll(resp.Body)
}
// ---------------------------------------------------------------------------------------------------------------------
var goldenPlaylistM3u8 = `#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO

@ -0,0 +1,13 @@
// Copyright 2022, 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 innertest
import "github.com/q191201771/naza/pkg/nazalog"
var Log = nazalog.GetGlobalLogger()

@ -221,16 +221,16 @@ func LoadConfAndInitLog(confFile string) *Config {
cacheLog = append(cacheLog, fmt.Sprintf("log.assert_behavior=%s", config.LogConfig.AssertBehavior.ReadableString()))
}
if err := nazalog.Init(func(option *nazalog.Option) {
if err := Log.Init(func(option *nazalog.Option) {
*option = config.LogConfig
}); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "initial log failed. err=%+v\n", err)
base.OsExitAndWaitPressIfWindows(1)
}
nazalog.Info("initial log succ.")
Log.Info("initial log succ.")
// 打印Logo
nazalog.Info(`
Log.Info(`
__ ___ __
/ / / | / /
/ / / /| | / /
@ -240,7 +240,7 @@ func LoadConfAndInitLog(confFile string) *Config {
// 检查配置版本号是否匹配
if config.ConfVersion != ConfVersion {
nazalog.Warnf("config version invalid. conf version of lalserver=%s, conf version of config file=%s",
Log.Warnf("config version invalid. conf version of lalserver=%s, conf version of config file=%s",
ConfVersion, config.ConfVersion)
}
@ -253,15 +253,15 @@ func LoadConfAndInitLog(confFile string) *Config {
"httpts.http_listen_addr", "httpts.https_listen_addr", "httpts.https_cert_file", "httpts.https_key_file",
)
if err != nil {
nazalog.Warnf("config nazajson collect not exist fields failed. err=%+v", err)
Log.Warnf("config nazajson collect not exist fields failed. err=%+v", err)
}
if len(notExistFields) != 0 {
nazalog.Warnf("config some fields do not exist which have been set to the zero value. fields=%+v", notExistFields)
Log.Warnf("config some fields do not exist which have been set to the zero value. fields=%+v", notExistFields)
}
// 日志字段检查,缺失的字段,打印前面设置的默认值
if len(cacheLog) > 0 {
nazalog.Warnf("config some log fields do not exist which have been set to default value. %s", strings.Join(cacheLog, ", "))
Log.Warnf("config some log fields do not exist which have been set to default value. %s", strings.Join(cacheLog, ", "))
}
// 如果具体的HTTP应用没有设置HTTP监听相关的配置则尝试使用全局配置
@ -271,39 +271,39 @@ func LoadConfAndInitLog(confFile string) *Config {
// 为缺失的字段中的一些特定字段,设置特定默认值
if config.HlsConfig.Enable && !j.Exist("hls.cleanup_mode") {
nazalog.Warnf("config hls.cleanup_mode not exist. set to default which is %d", defaultHlsCleanupMode)
Log.Warnf("config hls.cleanup_mode not exist. set to default which is %d", defaultHlsCleanupMode)
config.HlsConfig.CleanupMode = defaultHlsCleanupMode
}
if config.HlsConfig.Enable && !j.Exist("hls.delete_threshold") {
nazalog.Warnf("config hls.delete_threshold not exist. set to default same as hls.fragment_num which is %d",
Log.Warnf("config hls.delete_threshold not exist. set to default same as hls.fragment_num which is %d",
config.HlsConfig.FragmentNum)
config.HlsConfig.DeleteThreshold = config.HlsConfig.FragmentNum
}
if (config.HttpflvConfig.Enable || config.HttpflvConfig.EnableHttps) && !j.Exist("httpflv.url_pattern") {
nazalog.Warnf("config httpflv.url_pattern not exist. set to default wchich is %s", defaultHttpflvUrlPattern)
Log.Warnf("config httpflv.url_pattern not exist. set to default wchich is %s", defaultHttpflvUrlPattern)
config.HttpflvConfig.UrlPattern = defaultHttpflvUrlPattern
}
if (config.HttptsConfig.Enable || config.HttptsConfig.EnableHttps) && !j.Exist("httpts.url_pattern") {
nazalog.Warnf("config httpts.url_pattern not exist. set to default wchich is %s", defaultHttptsUrlPattern)
Log.Warnf("config httpts.url_pattern not exist. set to default wchich is %s", defaultHttptsUrlPattern)
config.HttptsConfig.UrlPattern = defaultHttptsUrlPattern
}
if (config.HlsConfig.Enable || config.HlsConfig.EnableHttps) && !j.Exist("hls.url_pattern") {
nazalog.Warnf("config hls.url_pattern not exist. set to default wchich is %s", defaultHlsUrlPattern)
Log.Warnf("config hls.url_pattern not exist. set to default wchich is %s", defaultHlsUrlPattern)
config.HttpflvConfig.UrlPattern = defaultHlsUrlPattern
}
// 对一些常见的格式错误做修复
// 确保url pattern以`/`开始,并以`/`结束
if urlPattern, changed := ensureStartAndEndWithSlash(config.HttpflvConfig.UrlPattern); changed {
nazalog.Warnf("fix config. httpflv.url_pattern %s -> %s", config.HttpflvConfig.UrlPattern, urlPattern)
Log.Warnf("fix config. httpflv.url_pattern %s -> %s", config.HttpflvConfig.UrlPattern, urlPattern)
config.HttpflvConfig.UrlPattern = urlPattern
}
if urlPattern, changed := ensureStartAndEndWithSlash(config.HttptsConfig.UrlPattern); changed {
nazalog.Warnf("fix config. httpts.url_pattern %s -> %s", config.HttptsConfig.UrlPattern, urlPattern)
Log.Warnf("fix config. httpts.url_pattern %s -> %s", config.HttptsConfig.UrlPattern, urlPattern)
config.HttpflvConfig.UrlPattern = urlPattern
}
if urlPattern, changed := ensureStartAndEndWithSlash(config.HlsConfig.UrlPattern); changed {
nazalog.Warnf("fix config. hls.url_pattern %s -> %s", config.HlsConfig.UrlPattern, urlPattern)
Log.Warnf("fix config. hls.url_pattern %s -> %s", config.HlsConfig.UrlPattern, urlPattern)
config.HttpflvConfig.UrlPattern = urlPattern
}
@ -318,7 +318,7 @@ func LoadConfAndInitLog(confFile string) *Config {
tlines = append(tlines, strings.TrimSpace(l))
}
compactRawContent := strings.Join(tlines, " ")
nazalog.Infof("load conf file succ. filename=%s, raw content=%s parsed=%+v", confFile, compactRawContent, config)
Log.Infof("load conf file succ. filename=%s, raw content=%s parsed=%+v", confFile, compactRawContent, config)
return config
}

@ -38,7 +38,6 @@ import (
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/nazalog"
)
type GroupObserver interface {
@ -149,7 +148,7 @@ func NewGroup(appName string, streamName string, config *Config, observer GroupO
if config.RtmpConfig.MergeWriteSize > 0 {
g.rtmpMergeWriter = base.NewMergeWriter(g.writev2RtmpSubSessions, config.RtmpConfig.MergeWriteSize)
}
nazalog.Infof("[%s] lifecycle new group. group=%p, appName=%s, streamName=%s", uk, g, appName, streamName)
Log.Infof("[%s] lifecycle new group. group=%p, appName=%s, streamName=%s", uk, g, appName, streamName)
return g
}
@ -158,7 +157,7 @@ func (group *Group) RunLoop() {
<-group.exitChan
}
// TODO chef: 传入时间
// Tick TODO chef: 传入时间
// 目前每秒触发一次
func (group *Group) Tick() {
group.mutex.Lock()
@ -176,14 +175,14 @@ func (group *Group) Tick() {
if group.tickCount%checkSessionAliveIntervalSec == 0 {
if group.rtmpPubSession != nil {
if readAlive, _ := group.rtmpPubSession.IsAlive(); !readAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.rtmpPubSession.UniqueKey())
Log.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.rtmpPubSession.UniqueKey())
group.rtmpPubSession.Dispose()
group.rtmp2RtspRemuxer = nil
}
}
if group.rtspPubSession != nil {
if readAlive, _ := group.rtspPubSession.IsAlive(); !readAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.rtspPubSession.UniqueKey())
Log.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.rtspPubSession.UniqueKey())
group.rtspPubSession.Dispose()
group.rtspPubSession = nil
group.rtsp2RtmpRemuxer = nil
@ -191,35 +190,35 @@ func (group *Group) Tick() {
}
if group.pullProxy.pullSession != nil {
if readAlive, _ := group.pullProxy.pullSession.IsAlive(); !readAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.pullProxy.pullSession.UniqueKey())
Log.Warnf("[%s] session timeout. session=%s", group.UniqueKey, group.pullProxy.pullSession.UniqueKey())
group.pullProxy.pullSession.Dispose()
group.delRtmpPullSession(group.pullProxy.pullSession)
}
}
for session := range group.rtmpSubSessionSet {
if _, writeAlive := session.IsAlive(); !writeAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
Log.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
session.Dispose()
group.delRtmpSubSession(session)
}
}
for session := range group.httpflvSubSessionSet {
if _, writeAlive := session.IsAlive(); !writeAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
Log.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
session.Dispose()
group.delHttpflvSubSession(session)
}
}
for session := range group.httptsSubSessionSet {
if _, writeAlive := session.IsAlive(); !writeAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
Log.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
session.Dispose()
group.delHttptsSubSession(session)
}
}
for session := range group.rtspSubSessionSet {
if _, writeAlive := session.IsAlive(); !writeAlive {
nazalog.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
Log.Warnf("[%s] session timeout. session=%s", group.UniqueKey, session.UniqueKey())
session.Dispose()
group.delRtspSubSession(session)
}
@ -254,12 +253,12 @@ func (group *Group) Tick() {
group.tickCount++
}
// 主动释放所有资源。保证所有资源的生命周期逻辑上都在我们的控制中。降低出bug的几率降低心智负担。
// Dispose 主动释放所有资源。保证所有资源的生命周期逻辑上都在我们的控制中。降低出bug的几率降低心智负担。
// 注意Dispose后不应再使用这个对象。
// 值得一提如果是从其他协程回调回来的消息在使用Group中的资源前要判断资源是否存在以及可用。
//
func (group *Group) Dispose() {
nazalog.Infof("[%s] lifecycle dispose group.", group.UniqueKey)
Log.Infof("[%s] lifecycle dispose group.", group.UniqueKey)
group.exitChan <- struct{}{}
group.mutex.Lock()
@ -306,14 +305,14 @@ func (group *Group) Dispose() {
// ---------------------------------------------------------------------------------------------------------------------
func (group *Group) AddRtmpPubSession(session *rtmp.ServerSession) error {
nazalog.Debugf("[%s] [%s] add PubSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] add PubSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
if group.hasInSession() {
// TODO(chef): [refactor] 打印in session
nazalog.Errorf("[%s] in stream already exist at group. add=%s", group.UniqueKey, session.UniqueKey())
Log.Errorf("[%s] in stream already exist at group. add=%s", group.UniqueKey, session.UniqueKey())
return base.ErrDupInStream
}
@ -341,15 +340,15 @@ func (group *Group) AddRtmpPubSession(session *rtmp.ServerSession) error {
return nil
}
// TODO chef: rtsp package中增加回调返回值判断如果是false将连接关掉
// AddRtspPubSession TODO chef: rtsp package中增加回调返回值判断如果是false将连接关掉
func (group *Group) AddRtspPubSession(session *rtsp.PubSession) error {
nazalog.Debugf("[%s] [%s] add RTSP PubSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] add RTSP PubSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
if group.hasInSession() {
nazalog.Errorf("[%s] in stream already exist at group. wanna add=%s", group.UniqueKey, session.UniqueKey())
Log.Errorf("[%s] in stream already exist at group. wanna add=%s", group.UniqueKey, session.UniqueKey())
return base.ErrDupInStream
}
@ -365,13 +364,13 @@ func (group *Group) AddRtspPubSession(session *rtsp.PubSession) error {
}
func (group *Group) AddRtmpPullSession(session *rtmp.PullSession) bool {
nazalog.Debugf("[%s] [%s] add PullSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] add PullSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
if group.hasInSession() {
nazalog.Errorf("[%s] in stream already exist. wanna add=%s", group.UniqueKey, session.UniqueKey())
Log.Errorf("[%s] in stream already exist. wanna add=%s", group.UniqueKey, session.UniqueKey())
return false
}
@ -404,7 +403,7 @@ func (group *Group) DelRtmpPullSession(session *rtmp.PullSession) {
// ---------------------------------------------------------------------------------------------------------------------
func (group *Group) AddRtmpSubSession(session *rtmp.ServerSession) {
nazalog.Debugf("[%s] [%s] add SubSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] add SubSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
group.rtmpSubSessionSet[session] = struct{}{}
@ -421,7 +420,7 @@ func (group *Group) AddRtmpSubSession(session *rtmp.ServerSession) {
}
func (group *Group) AddHttpflvSubSession(session *httpflv.SubSession) {
nazalog.Debugf("[%s] [%s] add httpflv SubSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] add httpflv SubSession into group.", group.UniqueKey, session.UniqueKey())
session.WriteHttpResponseHeader()
session.WriteFlvHeader()
@ -436,11 +435,11 @@ func (group *Group) AddHttpflvSubSession(session *httpflv.SubSession) {
group.pullIfNeeded()
}
// TODO chef:
// AddHttptsSubSession TODO chef:
// 这里应该也要考虑触发hls muxer开启
// 也即HTTPTS sub需要使用hls muxerhls muxer开启和关闭都要考虑HTTPTS sub
func (group *Group) AddHttptsSubSession(session *httpts.SubSession) {
nazalog.Debugf("[%s] [%s] add httpts SubSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] add httpts SubSession into group.", group.UniqueKey, session.UniqueKey())
session.WriteHttpResponseHeader()
group.mutex.Lock()
@ -455,7 +454,7 @@ func (group *Group) HandleNewRtspSubSessionDescribe(session *rtsp.SubSession) (o
defer group.mutex.Unlock()
// TODO(chef): 应该有等待机制,而不是直接关闭
if group.sdpCtx == nil {
nazalog.Warnf("[%s] close rtsp subSession while describe but sdp not exist. [%s]",
Log.Warnf("[%s] close rtsp subSession while describe but sdp not exist. [%s]",
group.UniqueKey, session.UniqueKey())
return false, nil
}
@ -464,7 +463,7 @@ func (group *Group) HandleNewRtspSubSessionDescribe(session *rtsp.SubSession) (o
}
func (group *Group) HandleNewRtspSubSessionPlay(session *rtsp.SubSession) {
nazalog.Debugf("[%s] [%s] add rtsp SubSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] add rtsp SubSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
@ -477,7 +476,7 @@ func (group *Group) HandleNewRtspSubSessionPlay(session *rtsp.SubSession) {
}
func (group *Group) AddRtmpPushSession(url string, session *rtmp.PushSession) {
nazalog.Debugf("[%s] [%s] add rtmp PushSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] add rtmp PushSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
if group.url2PushProxy != nil {
@ -504,14 +503,14 @@ func (group *Group) DelHttptsSubSession(session *httpts.SubSession) {
}
func (group *Group) DelRtspSubSession(session *rtsp.SubSession) {
nazalog.Debugf("[%s] [%s] del rtsp SubSession from group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del rtsp SubSession from group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
delete(group.rtspSubSessionSet, session)
}
func (group *Group) DelRtmpPushSession(url string, session *rtmp.PushSession) {
nazalog.Debugf("[%s] [%s] del rtmp PushSession into group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del rtmp PushSession into group.", group.UniqueKey, session.UniqueKey())
group.mutex.Lock()
defer group.mutex.Unlock()
if group.url2PushProxy != nil {
@ -607,7 +606,7 @@ func (group *Group) KickOutSession(sessionId string) bool {
group.mutex.Lock()
defer group.mutex.Unlock()
nazalog.Infof("[%s] kick out session. session id=%s", group.UniqueKey, sessionId)
Log.Infof("[%s] kick out session. session id=%s", group.UniqueKey, sessionId)
if strings.HasPrefix(sessionId, base.UkPreRtmpServerSession) {
if group.rtmpPubSession != nil {
@ -639,13 +638,13 @@ func (group *Group) KickOutSession(sessionId string) bool {
} else if strings.HasPrefix(sessionId, base.UkPreRtspSubSession) {
// TODO chef: impl me
} else {
nazalog.Errorf("[%s] kick out session while session id format invalid. %s", group.UniqueKey, sessionId)
Log.Errorf("[%s] kick out session while session id format invalid. %s", group.UniqueKey, sessionId)
}
return false
}
// 外部命令主动触发pull拉流
// StartPull 外部命令主动触发pull拉流
//
// 当前调用时机:
// 1. 比如http api
@ -661,10 +660,10 @@ func (group *Group) StartPull(url string) {
// ---------------------------------------------------------------------------------------------------------------------
func (group *Group) delRtmpPubSession(session *rtmp.ServerSession) {
nazalog.Debugf("[%s] [%s] del rtmp PubSession from group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del rtmp PubSession from group.", group.UniqueKey, session.UniqueKey())
if session != group.rtmpPubSession {
nazalog.Warnf("[%s] del rtmp pub session but not match. del session=%s, group session=%p", group.UniqueKey, session.UniqueKey(), group.rtmpPubSession)
Log.Warnf("[%s] del rtmp pub session but not match. del session=%s, group session=%p", group.UniqueKey, session.UniqueKey(), group.rtmpPubSession)
return
}
@ -675,10 +674,10 @@ func (group *Group) delRtmpPubSession(session *rtmp.ServerSession) {
}
func (group *Group) delRtspPubSession(session *rtsp.PubSession) {
nazalog.Debugf("[%s] [%s] del rtsp PubSession from group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del rtsp PubSession from group.", group.UniqueKey, session.UniqueKey())
if session != group.rtspPubSession {
nazalog.Warnf("[%s] del rtmp pub session but not match. del session=%s, group session=%p", group.UniqueKey, session.UniqueKey(), group.rtmpPubSession)
Log.Warnf("[%s] del rtmp pub session but not match. del session=%s, group session=%p", group.UniqueKey, session.UniqueKey(), group.rtmpPubSession)
return
}
@ -689,7 +688,7 @@ func (group *Group) delRtspPubSession(session *rtsp.PubSession) {
}
func (group *Group) delRtmpPullSession(session *rtmp.PullSession) {
nazalog.Debugf("[%s] [%s] del rtmp PullSession from group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del rtmp PullSession from group.", group.UniqueKey, session.UniqueKey())
group.pullProxy.pullSession = nil
group.setPullingFlag(false)
@ -697,22 +696,22 @@ func (group *Group) delRtmpPullSession(session *rtmp.PullSession) {
}
func (group *Group) delRtmpSubSession(session *rtmp.ServerSession) {
nazalog.Debugf("[%s] [%s] del rtmp SubSession from group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del rtmp SubSession from group.", group.UniqueKey, session.UniqueKey())
delete(group.rtmpSubSessionSet, session)
}
func (group *Group) delHttpflvSubSession(session *httpflv.SubSession) {
nazalog.Debugf("[%s] [%s] del httpflv SubSession from group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del httpflv SubSession from group.", group.UniqueKey, session.UniqueKey())
delete(group.httpflvSubSessionSet, session)
}
func (group *Group) delHttptsSubSession(session *httpts.SubSession) {
nazalog.Debugf("[%s] [%s] del httpts SubSession from group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del httpts SubSession from group.", group.UniqueKey, session.UniqueKey())
delete(group.httptsSubSessionSet, session)
}
func (group *Group) delRtspSubSession(session *rtsp.SubSession) {
nazalog.Debugf("[%s] [%s] del rtsp SubSession from group.", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] del rtsp SubSession from group.", group.UniqueKey, session.UniqueKey())
delete(group.rtspSubSessionSet, session)
}
@ -744,7 +743,7 @@ func (group *Group) pushIfNeeded() {
if urlParam != "" {
urlWithParam += "?" + urlParam
}
nazalog.Infof("[%s] start relay push. url=%s", group.UniqueKey, urlWithParam)
Log.Infof("[%s] start relay push. url=%s", group.UniqueKey, urlWithParam)
go func(u, u2 string) {
pushSession := rtmp.NewPushSession(func(option *rtmp.PushSessionOption) {
@ -753,13 +752,13 @@ func (group *Group) pushIfNeeded() {
})
err := pushSession.Push(u2)
if err != nil {
nazalog.Errorf("[%s] relay push done. err=%v", pushSession.UniqueKey(), err)
Log.Errorf("[%s] relay push done. err=%v", pushSession.UniqueKey(), err)
group.DelRtmpPushSession(u, pushSession)
return
}
group.AddRtmpPushSession(u, pushSession)
err = <-pushSession.WaitChan()
nazalog.Infof("[%s] relay push done. err=%v", pushSession.UniqueKey(), err)
Log.Infof("[%s] relay push done. err=%v", pushSession.UniqueKey(), err)
group.DelRtmpPushSession(u, pushSession)
}(url, urlWithParam)
}
@ -805,7 +804,7 @@ func (group *Group) addIn() {
// 是否启动hls
if group.config.HlsConfig.Enable {
if group.hlsMuxer != nil {
nazalog.Errorf("[%s] hls muxer exist while addIn. muxer=%+v", group.UniqueKey, group.hlsMuxer)
Log.Errorf("[%s] hls muxer exist while addIn. muxer=%+v", group.UniqueKey, group.hlsMuxer)
}
enable := group.config.HlsConfig.Enable || group.config.HlsConfig.EnableHttps
group.hlsMuxer = hls.NewMuxer(group.streamName, enable, &group.config.HlsConfig.MuxerConfig, group)
@ -824,20 +823,20 @@ func (group *Group) addIn() {
filename := fmt.Sprintf("%s-%d.flv", group.streamName, now)
filenameWithPath := filepath.Join(group.config.RecordConfig.FlvOutPath, filename)
if group.recordFlv != nil {
nazalog.Errorf("[%s] record flv but already exist. new filename=%s, old filename=%s",
Log.Errorf("[%s] record flv but already exist. new filename=%s, old filename=%s",
group.UniqueKey, filenameWithPath, group.recordFlv.Name())
if err := group.recordFlv.Dispose(); err != nil {
nazalog.Errorf("[%s] record flv dispose error. err=%+v", group.UniqueKey, err)
Log.Errorf("[%s] record flv dispose error. err=%+v", group.UniqueKey, err)
}
}
group.recordFlv = &httpflv.FlvFileWriter{}
if err := group.recordFlv.Open(filenameWithPath); err != nil {
nazalog.Errorf("[%s] record flv open file failed. filename=%s, err=%+v",
Log.Errorf("[%s] record flv open file failed. filename=%s, err=%+v",
group.UniqueKey, filenameWithPath, err)
group.recordFlv = nil
}
if err := group.recordFlv.WriteFlvHeader(); err != nil {
nazalog.Errorf("[%s] record flv write flv header failed. filename=%s, err=%+v",
Log.Errorf("[%s] record flv write flv header failed. filename=%s, err=%+v",
group.UniqueKey, filenameWithPath, err)
group.recordFlv = nil
}
@ -848,15 +847,15 @@ func (group *Group) addIn() {
filename := fmt.Sprintf("%s-%d.ts", group.streamName, now)
filenameWithPath := filepath.Join(group.config.RecordConfig.MpegtsOutPath, filename)
if group.recordMpegts != nil {
nazalog.Errorf("[%s] record mpegts but already exist. new filename=%s, old filename=%s",
Log.Errorf("[%s] record mpegts but already exist. new filename=%s, old filename=%s",
group.UniqueKey, filenameWithPath, group.recordMpegts.Name())
if err := group.recordMpegts.Dispose(); err != nil {
nazalog.Errorf("[%s] record mpegts dispose error. err=%+v", group.UniqueKey, err)
Log.Errorf("[%s] record mpegts dispose error. err=%+v", group.UniqueKey, err)
}
}
group.recordMpegts = &mpegts.FileWriter{}
if err := group.recordMpegts.Create(filenameWithPath); err != nil {
nazalog.Errorf("[%s] record mpegts open file failed. filename=%s, err=%+v",
Log.Errorf("[%s] record mpegts open file failed. filename=%s, err=%+v",
group.UniqueKey, filenameWithPath, err)
group.recordFlv = nil
}
@ -885,7 +884,7 @@ func (group *Group) delIn() {
if group.config.RecordConfig.EnableFlv {
if group.recordFlv != nil {
if err := group.recordFlv.Dispose(); err != nil {
nazalog.Errorf("[%s] record flv dispose error. err=%+v", group.UniqueKey, err)
Log.Errorf("[%s] record flv dispose error. err=%+v", group.UniqueKey, err)
}
group.recordFlv = nil
}
@ -895,7 +894,7 @@ func (group *Group) delIn() {
if group.config.RecordConfig.EnableMpegts {
if group.recordMpegts != nil {
if err := group.recordMpegts.Dispose(); err != nil {
nazalog.Errorf("[%s] record mpegts dispose error. err=%+v", group.UniqueKey, err)
Log.Errorf("[%s] record mpegts dispose error. err=%+v", group.UniqueKey, err)
}
group.recordMpegts = nil
}
@ -923,14 +922,14 @@ func (group *Group) disposeHlsMuxer() {
// 音视频数据转发、转封装的逻辑
// ---------------------------------------------------------------------------------------------------------------------
// rtmp.PubSession or rtmp.PullSession
// OnReadRtmpAvMsg rtmp.PubSession or rtmp.PullSession
func (group *Group) OnReadRtmpAvMsg(msg base.RtmpMsg) {
group.mutex.Lock()
defer group.mutex.Unlock()
group.broadcastByRtmpMsg(msg)
}
// rtsp.PubSession
// OnRtpPacket rtsp.PubSession
func (group *Group) OnRtpPacket(pkt rtprtcp.RtpPacket) {
group.mutex.Lock()
defer group.mutex.Unlock()
@ -938,7 +937,7 @@ func (group *Group) OnRtpPacket(pkt rtprtcp.RtpPacket) {
group.onRtpPacket(pkt)
}
// rtsp.PubSession
// OnSdp rtsp.PubSession
func (group *Group) OnSdp(sdpCtx sdp.LogicContext) {
group.mutex.Lock()
defer group.mutex.Unlock()
@ -947,27 +946,29 @@ func (group *Group) OnSdp(sdpCtx sdp.LogicContext) {
group.rtsp2RtmpRemuxer.OnSdp(sdpCtx)
}
// rtsp.PubSession
// OnAvPacket rtsp.PubSession
func (group *Group) OnAvPacket(pkt base.AvPacket) {
group.mutex.Lock()
defer group.mutex.Unlock()
//nazalog.Debugf("[%s] > Group::OnAvPacket. type=%s, ts=%d, len=%d", group.UniqueKey, pkt.PayloadType.ReadableString(), pkt.Timestamp, len(pkt.Payload))
//Log.Debugf("[%s] > Group::OnAvPacket. type=%s, ts=%d, len=%d", group.UniqueKey, pkt.PayloadType.ReadableString(), pkt.Timestamp, len(pkt.Payload))
group.rtsp2RtmpRemuxer.OnAvPacket(pkt)
}
// hls.Muxer
// ----- implement hls.MuxerObserver of hls.Muxer ----------------------------------------------------------------------
// OnPatPmt hls.Muxer
func (group *Group) OnPatPmt(b []byte) {
group.patpmt = b
if group.recordMpegts != nil {
if err := group.recordMpegts.Write(b); err != nil {
nazalog.Errorf("[%s] record mpegts write fragment header error. err=%+v", group.UniqueKey, err)
Log.Errorf("[%s] record mpegts write fragment header error. err=%+v", group.UniqueKey, err)
}
}
}
// hls.Muxer
// OnTsPackets hls.Muxer
func (group *Group) OnTsPackets(rawFrame []byte, boundary bool) {
// 因为最前面Feed时已经加锁了所以这里回调上来就不用加锁了
@ -985,11 +986,13 @@ func (group *Group) OnTsPackets(rawFrame []byte, boundary bool) {
if group.recordMpegts != nil {
if err := group.recordMpegts.Write(rawFrame); err != nil {
nazalog.Errorf("[%s] record mpegts write error. err=%+v", group.UniqueKey, err)
Log.Errorf("[%s] record mpegts write error. err=%+v", group.UniqueKey, err)
}
}
}
// ---------------------------------------------------------------------------------------------------------------------
// TODO chef: 目前相当于其他类型往rtmp.AVMsg转了考虑统一往一个通用类型转
//
// rtmp.PubSession, rtmp.PullSession, rtsp2rtmpRemuxer
@ -1003,7 +1006,7 @@ func (group *Group) broadcastByRtmpMsg(msg base.RtmpMsg) {
)
if len(msg.Payload) == 0 {
nazalog.Warnf("[%s] msg payload length is 0. %+v", group.UniqueKey, msg.Header)
Log.Warnf("[%s] msg payload length is 0. %+v", group.UniqueKey, msg.Header)
return
}
@ -1020,7 +1023,7 @@ func (group *Group) broadcastByRtmpMsg(msg base.RtmpMsg) {
// # 设置好用于发送的 rtmp 头部信息
currHeader := remux.MakeDefaultRtmpHeader(msg.Header)
if currHeader.MsgLen != uint32(len(msg.Payload)) {
nazalog.Errorf("[%s] diff. msgLen=%d, payload len=%d, %+v", group.UniqueKey, currHeader.MsgLen, len(msg.Payload), msg.Header)
Log.Errorf("[%s] diff. msgLen=%d, payload len=%d, %+v", group.UniqueKey, currHeader.MsgLen, len(msg.Payload), msg.Header)
}
// # 懒初始化rtmp chunk切片以及httpflv转换
@ -1033,15 +1036,15 @@ func (group *Group) broadcastByRtmpMsg(msg base.RtmpMsg) {
if session.IsFresh {
// TODO chef: 头信息和full gop也可以在SubSession刚加入时发送
if group.rtmpGopCache.Metadata != nil {
nazalog.Debugf("[%s] [%s] write metadata", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] write metadata", group.UniqueKey, session.UniqueKey())
_ = session.Write(group.rtmpGopCache.Metadata)
}
if group.rtmpGopCache.VideoSeqHeader != nil {
nazalog.Debugf("[%s] [%s] write vsh", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] write vsh", group.UniqueKey, session.UniqueKey())
_ = session.Write(group.rtmpGopCache.VideoSeqHeader)
}
if group.rtmpGopCache.AacSeqHeader != nil {
nazalog.Debugf("[%s] [%s] write ash", group.UniqueKey, session.UniqueKey())
Log.Debugf("[%s] [%s] write ash", group.UniqueKey, session.UniqueKey())
_ = session.Write(group.rtmpGopCache.AacSeqHeader)
}
gopCount := group.rtmpGopCache.GetGopCount()
@ -1049,7 +1052,7 @@ func (group *Group) broadcastByRtmpMsg(msg base.RtmpMsg) {
// GOP缓存中肯定包含了关键帧
session.ShouldWaitVideoKeyFrame = false
nazalog.Debugf("[%s] [%s] write gop cache. gop num=%d", group.UniqueKey, session.UniqueKey(), gopCount)
Log.Debugf("[%s] [%s] write gop cache. gop num=%d", group.UniqueKey, session.UniqueKey(), gopCount)
}
for i := 0; i < gopCount; i++ {
for _, item := range group.rtmpGopCache.GetGopDataAt(i) {
@ -1157,7 +1160,7 @@ func (group *Group) broadcastByRtmpMsg(msg base.RtmpMsg) {
// # 录制flv文件
if group.recordFlv != nil {
if err := group.recordFlv.WriteRaw(lrm2ft.Get()); err != nil {
nazalog.Errorf("[%s] record flv write error. err=%+v", group.UniqueKey, err)
Log.Errorf("[%s] record flv write error. err=%+v", group.UniqueKey, err)
}
}
@ -1317,7 +1320,7 @@ func (group *Group) pullIfNeeded() {
}
group.setPullingFlag(true)
nazalog.Infof("[%s] start relay pull. url=%s", group.UniqueKey, group.getPullUrl())
Log.Infof("[%s] start relay pull. url=%s", group.UniqueKey, group.getPullUrl())
go func() {
pullSession := rtmp.NewPullSession(func(option *rtmp.PullSessionOption) {
@ -1327,14 +1330,14 @@ func (group *Group) pullIfNeeded() {
// TODO(chef): 处理数据回调是否应该等待Add成功之后。避免竞态条件中途加入了其他in session
err := pullSession.Pull(group.getPullUrl(), group.OnReadRtmpAvMsg)
if err != nil {
nazalog.Errorf("[%s] relay pull fail. err=%v", pullSession.UniqueKey(), err)
Log.Errorf("[%s] relay pull fail. err=%v", pullSession.UniqueKey(), err)
group.DelRtmpPullSession(pullSession)
return
}
res := group.AddRtmpPullSession(pullSession)
if res {
err = <-pullSession.WaitChan()
nazalog.Infof("[%s] relay pull done. err=%v", pullSession.UniqueKey(), err)
Log.Infof("[%s] relay pull done. err=%v", pullSession.UniqueKey(), err)
group.DelRtmpPullSession(pullSession)
} else {
pullSession.Dispose()
@ -1350,7 +1353,7 @@ func (group *Group) pullIfNeeded() {
func (group *Group) stopPullIfNeeded() {
// 没有输出型的流了
if group.pullProxy.pullSession != nil && !group.hasOutSession() {
nazalog.Infof("[%s] stop pull since no sub session.", group.UniqueKey)
Log.Infof("[%s] stop pull since no sub session.", group.UniqueKey)
group.pullProxy.pullSession.Dispose()
}
}

@ -16,7 +16,6 @@ import (
"github.com/q191201771/naza/pkg/nazahttp"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
)
type HttpApiServer struct {
@ -37,7 +36,7 @@ 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)
Log.Infof("start httpapi server listen. addr=%s", h.addr)
return
}
@ -106,13 +105,13 @@ func (h *HttpApiServer) ctrlStartPullHandler(w http.ResponseWriter, req *http.Re
err := nazahttp.UnmarshalRequestJsonBody(req, &info, "protocol", "addr", "app_name", "stream_name")
if err != nil {
nazalog.Warnf("http api start pull error. err=%+v", err)
Log.Warnf("http api start pull error. err=%+v", err)
v.ErrorCode = base.ErrorCodeParamMissing
v.Desp = base.DespParamMissing
feedback(v, w)
return
}
nazalog.Infof("http api start pull. req info=%+v", info)
Log.Infof("http api start pull. req info=%+v", info)
h.sm.CtrlStartPull(info)
v.ErrorCode = base.ErrorCodeSucc
@ -127,13 +126,13 @@ func (h *HttpApiServer) ctrlKickOutSessionHandler(w http.ResponseWriter, req *ht
err := nazahttp.UnmarshalRequestJsonBody(req, &info, "stream_name", "session_id")
if err != nil {
nazalog.Warnf("http api kick out session error. err=%+v", err)
Log.Warnf("http api kick out session error. err=%+v", err)
v.ErrorCode = base.ErrorCodeParamMissing
v.Desp = base.DespParamMissing
feedback(v, w)
return
}
nazalog.Infof("http api kick out session. req info=%+v", info)
Log.Infof("http api kick out session. req info=%+v", info)
resp := h.sm.CtrlKickOutSession(info)
feedback(resp, w)

@ -15,9 +15,9 @@ import (
func TestHttpApiServer(t *testing.T) {
//s := logic.NewHttpApiServer(":8083")
//if err := s.Listen(); err != nil {
// nazalog.Error(err)
// Log.Error(err)
// return
//}
//err := s.RunLoop()
//nazalog.Error(err)
//Log.Error(err)
}

@ -14,7 +14,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazahttp"
"github.com/q191201771/naza/pkg/nazalog"
)
// TODO(chef): refactor 配置参数供外部传入
@ -132,12 +131,12 @@ func (h *HttpNotify) asyncPost(url string, info interface{}) {
case h.taskQueue <- PostTask{url: url, info: info}:
// noop
default:
nazalog.Error("http notify queue full.")
Log.Error("http notify queue full.")
}
}
func (h *HttpNotify) post(url string, info interface{}) {
if _, err := nazahttp.PostJson(url, info, h.client); err != nil {
nazalog.Errorf("http notify post error. err=%+v", err)
Log.Errorf("http notify post error. err=%+v", err)
}
}

@ -15,7 +15,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/httpts"
"github.com/q191201771/naza/pkg/nazalog"
)
type HttpServerHandlerObserver interface {
@ -45,17 +44,17 @@ func NewHttpServerHandler(observer HttpServerHandlerObserver) *HttpServerHandler
func (h *HttpServerHandler) ServeSubSession(writer http.ResponseWriter, req *http.Request) {
urlCtx, err := base.ParseUrl(base.ParseHttpRequest(req), 80)
if err != nil {
nazalog.Errorf("parse url. err=%+v", err)
Log.Errorf("parse url. err=%+v", err)
return
}
conn, bio, err := writer.(http.Hijacker).Hijack()
if err != nil {
nazalog.Errorf("hijack failed. err=%+v", err)
Log.Errorf("hijack failed. err=%+v", err)
return
}
if bio.Reader.Buffered() != 0 || bio.Writer.Buffered() != 0 {
nazalog.Errorf("hijack but buffer not empty. rb=%d, wb=%d", bio.Reader.Buffered(), bio.Writer.Buffered())
Log.Errorf("hijack but buffer not empty. rb=%d, wb=%d", bio.Reader.Buffered(), bio.Writer.Buffered())
}
var (
@ -69,28 +68,28 @@ func (h *HttpServerHandler) ServeSubSession(writer http.ResponseWriter, req *htt
if strings.HasSuffix(urlCtx.LastItemOfPath, ".flv") {
session := httpflv.NewSubSession(conn, urlCtx, isWebSocket, webSocketKey)
nazalog.Debugf("[%s] < read http request. url=%s", session.UniqueKey(), session.Url())
Log.Debugf("[%s] < read http request. url=%s", session.UniqueKey(), session.Url())
if err = h.observer.OnNewHttpflvSubSession(session); err != nil {
nazalog.Infof("[%s] dispose by observer. err=%+v", session.UniqueKey(), err)
Log.Infof("[%s] dispose by observer. err=%+v", session.UniqueKey(), err)
_ = session.Dispose()
return
}
err = session.RunLoop()
nazalog.Debugf("[%s] httpflv sub session loop done. err=%v", session.UniqueKey(), err)
Log.Debugf("[%s] httpflv sub session loop done. err=%v", session.UniqueKey(), err)
h.observer.OnDelHttpflvSubSession(session)
return
}
if strings.HasSuffix(urlCtx.LastItemOfPath, ".ts") {
session := httpts.NewSubSession(conn, urlCtx, isWebSocket, webSocketKey)
nazalog.Debugf("[%s] < read http request. url=%s", session.UniqueKey(), session.Url())
Log.Debugf("[%s] < read http request. url=%s", session.UniqueKey(), session.Url())
if err = h.observer.OnNewHttptsSubSession(session); err != nil {
nazalog.Infof("[%s] dispose by observer. err=%+v", session.UniqueKey(), err)
Log.Infof("[%s] dispose by observer. err=%+v", session.UniqueKey(), err)
_ = session.Dispose()
return
}
err = session.RunLoop()
nazalog.Debugf("[%s] httpts sub session loop done. err=%v", session.UniqueKey(), err)
Log.Debugf("[%s] httpts sub session loop done. err=%v", session.UniqueKey(), err)
h.observer.OnDelHttptsSubSession(session)
return
}

@ -66,23 +66,3 @@ var defaultOption = Option{
}
type ModOption func(option *Option)
// ---------------------------------------------------------------------------------------------------------------------
// 一些没有放入配置文件中,包级别的配置,暂时没有对外暴露
//
var (
relayPushTimeoutMs = 5000
relayPushWriteAvTimeoutMs = 5000
relayPullTimeoutMs = 5000
relayPullReadAvTimeoutMs = 5000
calcSessionStatIntervalSec uint32 = 5
// checkSessionAliveIntervalSec
//
// - 对于输入型session检查一定时间内是否没有收到数据
// - 对于输出型session检查一定时间内是否没有发送数据
// 注意这里既检查socket发送阻塞又检查上层没有给session喂数据
//
checkSessionAliveIntervalSec uint32 = 10
)

@ -31,7 +31,6 @@ import (
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/nazalog"
//"github.com/felixge/fgprof"
)
@ -57,7 +56,7 @@ type ServerManager struct {
func NewServerManager(confFile string, modOption ...ModOption) *ServerManager {
sm := &ServerManager{
serverStartTime: time.Now().Format("2006-01-02 15:04:05.999"),
serverStartTime: base.ReadableNowTime(),
exitChan: make(chan struct{}, 1),
}
sm.groupManager = NewSimpleGroupManager(sm)
@ -66,16 +65,16 @@ func NewServerManager(confFile string, modOption ...ModOption) *ServerManager {
base.LogoutStartInfo()
if sm.config.HlsConfig.Enable && sm.config.HlsConfig.UseMemoryAsDiskFlag {
nazalog.Infof("hls use memory as disk.")
Log.Infof("hls use memory as disk.")
hls.SetUseMemoryAsDiskFlag(true)
}
if sm.config.RecordConfig.EnableFlv {
if err := os.MkdirAll(sm.config.RecordConfig.FlvOutPath, 0777); err != nil {
nazalog.Errorf("record flv mkdir error. path=%s, err=%+v", sm.config.RecordConfig.FlvOutPath, err)
Log.Errorf("record flv mkdir error. path=%s, err=%+v", sm.config.RecordConfig.FlvOutPath, err)
}
if err := os.MkdirAll(sm.config.RecordConfig.MpegtsOutPath, 0777); err != nil {
nazalog.Errorf("record mpegts mkdir error. path=%s, err=%+v", sm.config.RecordConfig.MpegtsOutPath, err)
Log.Errorf("record mpegts mkdir error. path=%s, err=%+v", sm.config.RecordConfig.MpegtsOutPath, err)
}
}
@ -132,10 +131,10 @@ func (sm *ServerManager) RunLoop() error {
handler,
)
if err != nil {
nazalog.Errorf("add http listen for %s failed. addr=%s, pattern=%s, err=%+v", name, config.HttpListenAddr, config.UrlPattern, err)
Log.Errorf("add http listen for %s failed. addr=%s, pattern=%s, err=%+v", name, config.HttpListenAddr, config.UrlPattern, err)
return err
}
nazalog.Infof("add http listen for %s. addr=%s, pattern=%s", name, config.HttpListenAddr, config.UrlPattern)
Log.Infof("add http listen for %s. addr=%s, pattern=%s", name, config.HttpListenAddr, config.UrlPattern)
}
if config.EnableHttps {
err := sm.httpServerManager.AddListen(
@ -144,9 +143,9 @@ func (sm *ServerManager) RunLoop() error {
handler,
)
if err != nil {
nazalog.Errorf("add https listen for %s failed. addr=%s, pattern=%s, err=%+v", name, config.HttpsListenAddr, config.UrlPattern, err)
Log.Errorf("add https listen for %s failed. addr=%s, pattern=%s, err=%+v", name, config.HttpsListenAddr, config.UrlPattern, err)
} else {
nazalog.Infof("add https listen for %s. addr=%s, pattern=%s", name, config.HttpsListenAddr, config.UrlPattern)
Log.Infof("add https listen for %s. addr=%s, pattern=%s", name, config.HttpsListenAddr, config.UrlPattern)
}
}
return nil
@ -165,7 +164,7 @@ func (sm *ServerManager) RunLoop() error {
if sm.httpServerManager != nil {
go func() {
if err := sm.httpServerManager.RunLoop(); err != nil {
nazalog.Error(err)
Log.Error(err)
}
}()
}
@ -176,7 +175,7 @@ func (sm *ServerManager) RunLoop() error {
}
go func() {
if err := sm.rtmpServer.RunLoop(); err != nil {
nazalog.Error(err)
Log.Error(err)
}
}()
}
@ -187,7 +186,7 @@ func (sm *ServerManager) RunLoop() error {
}
go func() {
if err := sm.rtspServer.RunLoop(); err != nil {
nazalog.Error(err)
Log.Error(err)
}
}()
}
@ -198,7 +197,7 @@ func (sm *ServerManager) RunLoop() error {
}
go func() {
if err := sm.httpApiServer.RunLoop(); err != nil {
nazalog.Error(err)
Log.Error(err)
}
}()
}
@ -224,7 +223,7 @@ func (sm *ServerManager) RunLoop() error {
// 关闭空闲的group
sm.groupManager.Iterate(func(group *Group) bool {
if group.IsTotalEmpty() {
nazalog.Infof("erase empty group. [%s]", group.UniqueKey)
Log.Infof("erase empty group. [%s]", group.UniqueKey)
group.Dispose()
return false
}
@ -237,13 +236,13 @@ func (sm *ServerManager) RunLoop() error {
if sm.config.DebugConfig.LogGroupIntervalSec > 0 &&
count%uint32(sm.config.DebugConfig.LogGroupIntervalSec) == 0 {
groupNum := sm.groupManager.Len()
nazalog.Debugf("DEBUG_GROUP_LOG: group size=%d", groupNum)
Log.Debugf("DEBUG_GROUP_LOG: group size=%d", groupNum)
if sm.config.DebugConfig.LogGroupMaxGroupNum > 0 {
var loggedGroupCount int
sm.groupManager.Iterate(func(group *Group) bool {
loggedGroupCount++
if loggedGroupCount <= sm.config.DebugConfig.LogGroupMaxGroupNum {
nazalog.Debugf("DEBUG_GROUP_LOG: %d %s", loggedGroupCount, group.StringifyDebugStats(sm.config.DebugConfig.LogGroupMaxSubNumPerGroup))
Log.Debugf("DEBUG_GROUP_LOG: %d %s", loggedGroupCount, group.StringifyDebugStats(sm.config.DebugConfig.LogGroupMaxSubNumPerGroup))
}
return true
})
@ -265,7 +264,7 @@ func (sm *ServerManager) RunLoop() error {
}
func (sm *ServerManager) Dispose() {
nazalog.Debug("dispose server manager.")
Log.Debug("dispose server manager.")
// TODO(chef) add httpServer
@ -324,7 +323,7 @@ func (sm *ServerManager) CtrlStartPull(info base.ApiCtrlStartPullReq) {
defer sm.mutex.Unlock()
g := sm.getGroup(info.AppName, info.StreamName)
if g == nil {
nazalog.Warnf("group not exist, ignore start pull. streamName=%s", info.StreamName)
Log.Warnf("group not exist, ignore start pull. streamName=%s", info.StreamName)
return
}
var url string
@ -738,14 +737,14 @@ func (sm *ServerManager) CleanupHlsIfNeeded(appName string, streamName string, p
if g := sm.GetGroup(appName, streamName); g != nil {
if g.IsHlsMuxerAlive() {
nazalog.Warnf("cancel cleanup hls file path since hls muxer still alive. streamName=%s", streamName)
Log.Warnf("cancel cleanup hls file path since hls muxer still alive. streamName=%s", streamName)
return
}
}
nazalog.Infof("cleanup hls file path. streamName=%s, path=%s", streamName, outPath)
Log.Infof("cleanup hls file path. streamName=%s, path=%s", streamName, outPath)
if err := hls.RemoveAll(outPath); err != nil {
nazalog.Warnf("cleanup hls file path error. path=%s, err=%+v", outPath, err)
Log.Warnf("cleanup hls file path error. path=%s, err=%+v", outPath, err)
}
},
appName,
@ -785,12 +784,12 @@ func (sm *ServerManager) getGroup(appName string, streamName string) *Group {
func (sm *ServerManager) serveHls(writer http.ResponseWriter, req *http.Request) {
urlCtx, err := base.ParseUrl(base.ParseHttpRequest(req), 80)
if err != nil {
nazalog.Errorf("parse url. err=%+v", err)
Log.Errorf("parse url. err=%+v", err)
return
}
if urlCtx.GetFileType() == "m3u8" {
if err = sm.simpleAuthCtx.OnHls(urlCtx.GetFilenameWithoutType(), urlCtx.RawQuery); err != nil {
nazalog.Errorf("simple auth failed. err=%+v", err)
Log.Errorf("simple auth failed. err=%+v", err)
return
}
}
@ -799,13 +798,13 @@ func (sm *ServerManager) serveHls(writer http.ResponseWriter, req *http.Request)
}
func runWebPprof(addr string) {
nazalog.Infof("start web pprof listen. addr=%s", addr)
Log.Infof("start web pprof listen. addr=%s", addr)
//nazalog.Warn("start fgprof.")
//Log.Warn("start fgprof.")
//http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
if err := http.ListenAndServe(addr, nil); err != nil {
nazalog.Error(err)
Log.Error(err)
return
}
}

@ -0,0 +1,29 @@
// Copyright 2022, 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 "github.com/q191201771/naza/pkg/nazalog"
var Log = nazalog.GetGlobalLogger()
var (
relayPushTimeoutMs = 5000
relayPushWriteAvTimeoutMs = 5000
relayPullTimeoutMs = 5000
relayPullReadAvTimeoutMs = 5000
calcSessionStatIntervalSec uint32 = 5
// checkSessionAliveIntervalSec
//
// - 对于输入型session检查一定时间内是否没有收到数据
// - 对于输出型session检查一定时间内是否没有发送数据
// 注意这里既检查socket发送阻塞又检查上层没有给session喂数据
//
checkSessionAliveIntervalSec uint32 = 10
)

@ -10,7 +10,7 @@ package mpegts
// MPEG: Moving Picture Experts Group
// 每个TS文件都以固定的PATPMT开始
// FixedFragmentHeader 每个TS文件都以固定的PATPMT开始
var FixedFragmentHeader = []byte{
/* TS */
0x47, 0x40, 0x00, 0x10, 0x00,
@ -70,7 +70,7 @@ var FixedFragmentHeader = []byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}
// 每个TS文件都以固定的PATPMT开始
// FixedFragmentHeaderHevc 每个TS文件都以固定的PATPMT开始
var FixedFragmentHeaderHevc = []byte{
/* TS */
0x47, 0x40, 0x00, 0x10, 0x00,
@ -140,7 +140,7 @@ const (
PidVideo uint16 = 0x100
PidAudio uint16 = 0x101
// ------------------------------------------
// AdaptationFieldControlReserved ------------------------------------------
// <iso13818-1.pdf> <Table 2-5> <page 38/174>
// ------------------------------------------
AdaptationFieldControlReserved uint8 = 0 // Reserved for future use by ISO/IEC
@ -164,13 +164,13 @@ const (
// PES
const (
// -----------------------------------------------------------------
// StreamIdAudio -----------------------------------------------------------------
// <iso13818-1.pdf> <Table 2-18-Stream_id assignments> <page 52/174>
// -----------------------------------------------------------------
StreamIdAudio uint8 = 192 // 110x xxxx 0xC0
StreamIdVideo uint8 = 224 // 1110 xxxx
// ------------------------------
// PtsDtsFlags0 ------------------------------
// <iso13818-1.pdf> <page 53/174>
// ------------------------------
PtsDtsFlags0 uint8 = 0 // no PTS no DTS

@ -14,7 +14,6 @@ import (
"github.com/q191201771/lal/pkg/innertest"
"github.com/q191201771/lal/pkg/mpegts"
"github.com/q191201771/naza/pkg/nazalog"
)
func TestMpegts(t *testing.T) {
@ -23,12 +22,12 @@ func TestMpegts(t *testing.T) {
func TestParseFixedTsPacket(t *testing.T) {
h := mpegts.ParseTsPacketHeader(mpegts.FixedFragmentHeader)
nazalog.Debugf("%+v", h)
mpegts.Log.Debugf("%+v", h)
pat := mpegts.ParsePat(mpegts.FixedFragmentHeader[5:])
nazalog.Debugf("%+v", pat)
mpegts.Log.Debugf("%+v", pat)
h = mpegts.ParseTsPacketHeader(mpegts.FixedFragmentHeaderHevc[188:])
nazalog.Debugf("%+v", h)
mpegts.Log.Debugf("%+v", h)
pmt := mpegts.ParsePmt(mpegts.FixedFragmentHeader[188+5:])
nazalog.Debugf("%+v", pmt)
mpegts.Log.Debugf("%+v", pmt)
}

@ -8,6 +8,8 @@
package mpegts
// Frame 帧数据
//
type Frame struct {
Pts uint64 // =(毫秒 * 90)
Dts uint64
@ -32,11 +34,11 @@ type Frame struct {
Raw []byte
}
// @param packet: 188字节大小的TS包注意一次Pack对应的多个TSPacket复用的是一块内存
// OnTsPacket @param packet: 188字节大小的TS包注意一次Pack对应的多个TSPacket复用的是一块内存
//
type OnTsPacket func(packet []byte)
// Annexb格式的流转换为mpegts packet
// PackTsPacket Annexb格式的流转换为mpegts packet
//
// @param frame: 各字段含义见mpegts.Frame结构体定义
// frame.CC 注意内部会修改frame.CC的值外部在调用结束后可保存CC的值供下次调用时使用
@ -49,6 +51,8 @@ func PackTsPacket(frame *Frame, onTsPacket OnTsPacket) {
lpos := 0 // 当前帧的处理位置
rpos := len(frame.Raw) // 当前帧大小
first := true // 是否为帧的首个packet的标准
// TODO(chef): [perf] 由于上层并不需要区分单个packet所以可以考虑预分配内存存储整个packet流
packet := make([]byte, 188)
for lpos != rpos {

@ -12,7 +12,7 @@ import (
"github.com/q191201771/naza/pkg/nazabits"
)
// ---------------------------------------------------------------------------------------------------
// Pat ---------------------------------------------------------------------------------------------------
// Program association section
// <iso13818-1.pdf> <2.4.4.3> <page 61/174>
// table_id [8b] *

@ -12,7 +12,7 @@ import (
"github.com/q191201771/naza/pkg/nazabits"
)
// -----------------------------------------------------------
// Pes -----------------------------------------------------------
// <iso13818-1.pdf>
// <2.4.3.6 PES packet> <page 49/174>
// <Table E.1 - PES packet header example> <page 142/174>

@ -10,10 +10,9 @@ package mpegts
import (
"github.com/q191201771/naza/pkg/nazabits"
"github.com/q191201771/naza/pkg/nazalog"
)
// ----------------------------------------
// Pmt ----------------------------------------
// Program Map Table
// <iso13818-1.pdf> <2.4.4.8> <page 64/174>
// table_id [8b] *
@ -79,7 +78,7 @@ func ParsePmt(b []byte) (pmt Pmt) {
_, _ = br.ReadBits8(4)
pmt.pil, _ = br.ReadBits16(12)
if pmt.pil != 0 {
nazalog.Warn(pmt.pil)
Log.Warn(pmt.pil)
_, _ = br.ReadBytes(uint(pmt.pil))
}
@ -91,7 +90,7 @@ func ParsePmt(b []byte) (pmt Pmt) {
_, _ = br.ReadBits8(4)
ppe.Length, _ = br.ReadBits16(12)
if ppe.Length != 0 {
nazalog.Warn(ppe.Length)
Log.Warn(ppe.Length)
_, _ = br.ReadBits32(uint(ppe.Length))
}
pmt.ProgramElements = append(pmt.ProgramElements, ppe)

@ -12,7 +12,7 @@ import (
"github.com/q191201771/naza/pkg/nazabits"
)
// ------------------------------------------------
// TsPacketHeader ------------------------------------------------
// <iso13818-1.pdf> <2.4.3.2> <page 36/174>
// sync_byte [8b] * always 0x47
// transport_error_indicator [1b]
@ -34,7 +34,7 @@ type TsPacketHeader struct {
Cc uint8
}
// ----------------------------------------------------------
// TsPacketAdaptation ----------------------------------------------------------
// <iso13818-1.pdf> <Table 2-6> <page 40/174>
// adaptation_field_length [8b] * 不包括自己这1字节
// discontinuity_indicator [1b]
@ -54,7 +54,7 @@ type TsPacketAdaptation struct {
Length uint8
}
// 解析4字节TS Packet header
// ParseTsPacketHeader 解析4字节TS Packet header
func ParseTsPacketHeader(b []byte) (h TsPacketHeader) {
// TODO chef: 检查长度
br := nazabits.NewBitReader(b)
@ -69,7 +69,7 @@ func ParseTsPacketHeader(b []byte) (h TsPacketHeader) {
return
}
// TODO chef
// ParseTsPacketAdaptation TODO chef
func ParseTsPacketAdaptation(b []byte) (f TsPacketAdaptation) {
br := nazabits.NewBitReader(b)
f.Length, _ = br.ReadBits8(8)

@ -0,0 +1,13 @@
// Copyright 2022, 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 mpegts
import "github.com/q191201771/naza/pkg/nazalog"
var Log = nazalog.GetGlobalLogger()

@ -17,10 +17,9 @@ import (
"github.com/q191201771/lal/pkg/rtprtcp"
"github.com/q191201771/lal/pkg/sdp"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
// AvPacket转换为RTMP
// AvPacket2RtmpRemuxer AvPacket转换为RTMP
// 目前AvPacket来自RTSP的sdp以及rtp的合帧包。理论上也支持webrtc后续接入webrtc时再验证
//
type AvPacket2RtmpRemuxer struct {
@ -43,7 +42,7 @@ func NewAvPacket2RtmpRemuxer(onRtmpAvMsg rtmp.OnReadRtmpAvMsg) *AvPacket2RtmpRem
}
}
// 实现RTSP回调数据的三个接口使得接入时方便些
// OnRtpPacket 实现RTSP回调数据的三个接口使得接入时方便些
func (r *AvPacket2RtmpRemuxer) OnRtpPacket(pkt rtprtcp.RtpPacket) {
// noop
}
@ -54,7 +53,7 @@ func (r *AvPacket2RtmpRemuxer) OnAvPacket(pkt base.AvPacket) {
r.FeedAvPacket(pkt)
}
// rtsp场景下有时sps、pps等信息只包含在sdp中有时包含在rtp包中
// InitWithAvConfig rtsp场景下有时sps、pps等信息只包含在sdp中有时包含在rtp包中
// 这里提供输入sdp的sps、pps等信息的机会如果没有可以不调用
//
// 内部不持有输入参数的内存块
@ -76,14 +75,14 @@ func (r *AvPacket2RtmpRemuxer) InitWithAvConfig(asc, vps, sps, pps []byte) {
}
if r.audioType == base.AvPacketPtUnknown && r.videoType == base.AvPacketPtUnknown {
nazalog.Warn("has no audio or video")
Log.Warn("has no audio or video")
return
}
if r.audioType != base.AvPacketPtUnknown {
bAsh, err = aac.MakeAudioDataSeqHeaderWithAsc(asc)
if err != nil {
nazalog.Errorf("build aac seq header failed. err=%+v", err)
Log.Errorf("build aac seq header failed. err=%+v", err)
return
}
}
@ -92,13 +91,13 @@ func (r *AvPacket2RtmpRemuxer) InitWithAvConfig(asc, vps, sps, pps []byte) {
if r.videoType == base.AvPacketPtHevc {
bVsh, err = hevc.BuildSeqHeaderFromVpsSpsPps(vps, sps, pps)
if err != nil {
nazalog.Errorf("build hevc seq header failed. err=%+v", err)
Log.Errorf("build hevc seq header failed. err=%+v", err)
return
}
} else {
bVsh, err = avc.BuildSeqHeaderFromSpsPps(sps, pps)
if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err)
Log.Errorf("build avc seq header failed. err=%+v", err)
return
}
}
@ -113,7 +112,7 @@ func (r *AvPacket2RtmpRemuxer) InitWithAvConfig(asc, vps, sps, pps []byte) {
}
}
// @param pkt: 内部不持有该内存块
// FeedAvPacket @param pkt: 内部不持有该内存块
//
func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
switch pkt.PayloadType {
@ -122,7 +121,7 @@ func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
case base.AvPacketPtHevc:
nals, err := avc.SplitNaluAvcc(pkt.Payload)
if err != nil {
nazalog.Errorf("iterate nalu failed. err=%+v", err)
Log.Errorf("iterate nalu failed. err=%+v", err)
return
}
@ -149,7 +148,7 @@ func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
bVsh, err := avc.BuildSeqHeaderFromSpsPps(r.sps, r.pps)
if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err)
Log.Errorf("build avc seq header failed. err=%+v", err)
continue
}
r.emitRtmpAvMsg(false, bVsh, pkt.Timestamp)
@ -182,7 +181,7 @@ func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
if len(r.vps) > 0 && len(r.sps) > 0 && len(r.pps) > 0 {
bVsh, err := hevc.BuildSeqHeaderFromVpsSpsPps(r.vps, r.sps, r.pps)
if err != nil {
nazalog.Errorf("build hevc seq header failed. err=%+v", err)
Log.Errorf("build hevc seq header failed. err=%+v", err)
continue
}
r.emitRtmpAvMsg(false, bVsh, pkt.Timestamp)
@ -217,7 +216,7 @@ func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
copy(payload[2:], pkt.Payload)
r.emitRtmpAvMsg(true, payload, pkt.Timestamp)
default:
nazalog.Warnf("unsupported packet. type=%d", pkt.PayloadType)
Log.Warnf("unsupported packet. type=%d", pkt.PayloadType)
}
}
@ -237,7 +236,7 @@ func (r *AvPacket2RtmpRemuxer) emitRtmpAvMsg(isAudio bool, payload []byte, times
}
bMetadata, err := rtmp.BuildMetadata(-1, -1, audiocodecid, videocodecid)
if err != nil {
nazalog.Errorf("build metadata failed. err=%+v", err)
Log.Errorf("build metadata failed. err=%+v", err)
return
}
r.onRtmpAvMsg(base.RtmpMsg{

@ -14,7 +14,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/remux"
"github.com/q191201771/naza/pkg/nazalog"
)
// #85
@ -39,7 +38,7 @@ func TestCase1(t *testing.T) {
}
remuxer := remux.NewAvPacket2RtmpRemuxer(func(msg base.RtmpMsg) {
nazalog.Debugf("%+v", msg)
remux.Log.Debugf("%+v", msg)
})
for _, p := range golden {
remuxer.FeedAvPacket(p)
@ -78,7 +77,7 @@ func TestCase2(t *testing.T) {
}
remuxer := remux.NewAvPacket2RtmpRemuxer(func(msg base.RtmpMsg) {
nazalog.Debugf("%+v", msg)
remux.Log.Debugf("%+v", msg)
})
for _, p := range golden {
remuxer.FeedAvPacket(p)

@ -13,7 +13,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/nazalog"
)
const (
@ -103,7 +102,7 @@ func (filter *DummyAudioFilter) handleAnalysisStage(msg base.RtmpMsg) {
}
// 达到阈值
nazalog.Debugf("[%s] start make dummy audio.", filter.uk)
Log.Debugf("[%s] start make dummy audio.", filter.uk)
filter.stage = dummyAudioFilterStageDummy
for i := range filter.earlyStageQueue {
filter.handleDummyStage(filter.earlyStageQueue[i])
@ -122,7 +121,7 @@ func (filter *DummyAudioFilter) handleNormalStage(msg base.RtmpMsg) {
func (filter *DummyAudioFilter) handleDummyStage(msg base.RtmpMsg) {
if msg.Header.MsgTypeId == base.RtmpTypeIdAudio {
// 由于我们已经开始制造静音包了,静音包的编码参数可能会和实际音频参数不一致,所以我们只能过滤掉原始的音频数据了
nazalog.Warnf("[%s] recv audio but we are making dummy audio.", filter.uk)
Log.Warnf("[%s] recv audio but we are making dummy audio.", filter.uk)
return
}

@ -17,7 +17,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/remux"
"github.com/q191201771/naza/pkg/assert"
"github.com/q191201771/naza/pkg/nazalog"
)
func TestDummyAudioFilter(t *testing.T) {
@ -116,7 +115,7 @@ func helperUnpackRtmpMsg(logstr string) base.RtmpMsg {
}
var fetchIntItemFn = func(str string, prefix string, suffix string) int {
ret, err := strconv.Atoi(fetchItemFn(str, prefix, suffix))
nazalog.Assert(nil, err)
remux.Log.Assert(nil, err)
return ret
}
@ -129,7 +128,7 @@ func helperUnpackRtmpMsg(logstr string) base.RtmpMsg {
hexStr := fetchItemFn(logstr, "payload=", "")
payload, err := hex.DecodeString(hexStr)
nazalog.Assert(nil, err)
remux.Log.Assert(nil, err)
return base.RtmpMsg{
Header: header,

@ -30,7 +30,7 @@ func FlvTagHeader2RtmpHeader(in httpflv.TagHeader) (out base.RtmpHeader) {
return
}
// @return msg: 返回的内存块引用参数`tag`的内存块
// FlvTag2RtmpMsg @return msg: 返回的内存块引用参数`tag`的内存块
//
func FlvTag2RtmpMsg(tag httpflv.Tag) (msg base.RtmpMsg) {
msg.Header = FlvTagHeader2RtmpHeader(tag.Header)
@ -38,7 +38,7 @@ func FlvTag2RtmpMsg(tag httpflv.Tag) (msg base.RtmpMsg) {
return
}
// @return 返回的内存块为内部新申请
// FlvTag2RtmpChunks @return 返回的内存块为内部新申请
//
func FlvTag2RtmpChunks(tag httpflv.Tag) []byte {
rtmpHeader := FlvTagHeader2RtmpHeader(tag.Header)

@ -10,7 +10,6 @@ package remux
import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
)
// GopCache
@ -66,7 +65,7 @@ type GopCache struct {
gopSize int
}
// @param gopNum: gop缓存大小
// NewGopCache @param gopNum: gop缓存大小
// 如果为0则不缓存音频数据也即GOP缓存功能不生效
// 如果>0则缓存<gopNum>个完整GOP另外还可能有半个最近不完整的GOP
//
@ -87,18 +86,18 @@ func (gc *GopCache) Feed(msg base.RtmpMsg, lg LazyGet) {
switch msg.Header.MsgTypeId {
case base.RtmpTypeIdMetadata:
gc.Metadata = lg()
nazalog.Debugf("[%s] cache %s metadata. size:%d", gc.uniqueKey, gc.t, len(gc.Metadata))
Log.Debugf("[%s] cache %s metadata. size:%d", gc.uniqueKey, gc.t, len(gc.Metadata))
return
case base.RtmpTypeIdAudio:
if msg.IsAacSeqHeader() {
gc.AacSeqHeader = lg()
nazalog.Debugf("[%s] cache %s aac seq header. size:%d", gc.uniqueKey, gc.t, len(gc.AacSeqHeader))
Log.Debugf("[%s] cache %s aac seq header. size:%d", gc.uniqueKey, gc.t, len(gc.AacSeqHeader))
return
}
case base.RtmpTypeIdVideo:
if msg.IsVideoKeySeqHeader() {
gc.VideoSeqHeader = lg()
nazalog.Debugf("[%s] cache %s video seq header. size:%d", gc.uniqueKey, gc.t, len(gc.VideoSeqHeader))
Log.Debugf("[%s] cache %s video seq header. size:%d", gc.uniqueKey, gc.t, len(gc.VideoSeqHeader))
return
}
}
@ -112,7 +111,7 @@ func (gc *GopCache) Feed(msg base.RtmpMsg, lg LazyGet) {
}
}
// 获取GOP数量注意最后一个可能是不完整的
// GetGopCount 获取GOP数量注意最后一个可能是不完整的
func (gc *GopCache) GetGopCount() int {
return (gc.gopRingLast + gc.gopSize - gc.gopRingFirst) % gc.gopSize
}
@ -132,6 +131,8 @@ func (gc *GopCache) Clear() {
gc.gopRingFirst = 0
}
// ---------------------------------------------------------------------------------------------------------------------
// 往最后一个GOP元素追加一个msg
// 注意如果GopCache为空则不缓存msg
func (gc *GopCache) feedLastGop(msg base.RtmpMsg, b []byte) {
@ -158,6 +159,8 @@ func (gc *GopCache) isGopRingEmpty() bool {
return gc.gopRingFirst == gc.gopRingLast
}
// ---------------------------------------------------------------------------------------------------------------------
type Gop struct {
data [][]byte
}

@ -13,7 +13,7 @@ import (
"github.com/q191201771/lal/pkg/httpflv"
)
// @return 返回的内存块为新申请的独立内存块
// RtmpMsg2FlvTag @return 返回的内存块为新申请的独立内存块
func RtmpMsg2FlvTag(msg base.RtmpMsg) *httpflv.Tag {
var tag httpflv.Tag
tag.Header.Type = msg.Header.MsgTypeId

@ -19,7 +19,6 @@ import (
"github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/lal/pkg/rtprtcp"
"github.com/q191201771/lal/pkg/sdp"
"github.com/q191201771/naza/pkg/nazalog"
)
// TODO(chef): refactor 将analyze部分独立出来作为一个filter
@ -30,7 +29,7 @@ var (
maxAnalyzeAvMsgSize = 16
)
// 提供rtmp数据向sdp+rtp数据的转换
// Rtmp2RtspRemuxer 提供rtmp数据向sdp+rtp数据的转换
type Rtmp2RtspRemuxer struct {
onSdp OnSdp
onRtpPacket OnRtpPacket
@ -50,7 +49,7 @@ type Rtmp2RtspRemuxer struct {
type OnSdp func(sdpCtx sdp.LogicContext)
type OnRtpPacket func(pkt rtprtcp.RtpPacket)
// @param onSdp: 每次回调为独立的内存块,回调结束后,内部不再使用该内存块
// NewRtmp2RtspRemuxer @param onSdp: 每次回调为独立的内存块,回调结束后,内部不再使用该内存块
// @param onRtpPacket: 每次回调为独立的内存块,回调结束后,内部不再使用该内存块
//
func NewRtmp2RtspRemuxer(onSdp OnSdp, onRtpPacket OnRtpPacket) *Rtmp2RtspRemuxer {
@ -62,7 +61,7 @@ func NewRtmp2RtspRemuxer(onSdp OnSdp, onRtpPacket OnRtpPacket) *Rtmp2RtspRemuxer
}
}
// @param msg: 函数调用结束后,内部不持有`msg`内存块
// FeedRtmpMsg @param msg: 函数调用结束后,内部不持有`msg`内存块
//
func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
var err error
@ -80,10 +79,10 @@ func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
if msg.IsAvcKeySeqHeader() || msg.IsHevcKeySeqHeader() {
if msg.IsAvcKeySeqHeader() {
r.sps, r.pps, err = avc.ParseSpsPpsFromSeqHeader(msg.Payload)
nazalog.Assert(nil, err)
Log.Assert(nil, err)
} else if msg.IsHevcKeySeqHeader() {
r.vps, r.sps, r.pps, err = hevc.ParseVpsSpsPpsFromSeqHeader(msg.Payload)
nazalog.Assert(nil, err)
Log.Assert(nil, err)
}
r.doAnalyze()
return
@ -91,7 +90,7 @@ func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
if msg.IsAacSeqHeader() {
if len(msg.Payload) < 3 {
nazalog.Warnf("aac seq header payload too short. len=%d, payload=%s", len(msg.Payload), hex.Dump(msg.Payload))
Log.Warnf("aac seq header payload too short. len=%d, payload=%s", len(msg.Payload), hex.Dump(msg.Payload))
return
}
r.asc = msg.Clone().Payload[2:]
@ -115,7 +114,7 @@ func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
}
func (r *Rtmp2RtspRemuxer) doAnalyze() {
nazalog.Assert(false, r.analyzeDone)
Log.Assert(false, r.analyzeDone)
if r.isAnalyzeEnough() {
if r.sps != nil && r.pps != nil {
@ -131,7 +130,7 @@ func (r *Rtmp2RtspRemuxer) doAnalyze() {
// 回调sdp
ctx, err := sdp.Pack(r.vps, r.sps, r.pps, r.asc)
nazalog.Assert(nil, err)
Log.Assert(nil, err)
r.onSdp(ctx)
// 分析阶段缓存的数据
@ -189,11 +188,11 @@ func (r *Rtmp2RtspRemuxer) getAudioPacker() *rtprtcp.RtpPacker {
// TODO(chef): 如果rtmp不是以音视频头开始也可能收到了帧数据但是头不存在目前该remux没有做过多容错判断后续要加上或者在输入层保证
ascCtx, err := aac.NewAscContext(r.asc)
if err != nil {
nazalog.Errorf("parse asc failed. err=%+v", err)
Log.Errorf("parse asc failed. err=%+v", err)
}
clockRate, err := ascCtx.GetSamplingFrequency()
if err != nil {
nazalog.Errorf("get sampling frequency failed. err=%+v", err)
Log.Errorf("get sampling frequency failed. err=%+v", err)
}
pp := rtprtcp.NewRtpPackerPayloadAac()

@ -0,0 +1,13 @@
// Copyright 2022, 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 remux
import "github.com/q191201771/naza/pkg/nazalog"
var Log = nazalog.GetGlobalLogger()

@ -20,7 +20,6 @@ import (
"github.com/q191201771/naza/pkg/nazaerrors"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
const (
@ -160,7 +159,7 @@ func (amf0) WriteObject(writer io.Writer, opa ObjectPairArray) error {
return err
}
default:
nazalog.Panicf("unknown value type. i=%d, v=%+v", i, opa[i].Value)
Log.Panicf("unknown value type. i=%d, v=%+v", i, opa[i].Value)
}
}
_, err := writer.Write(Amf0TypeMarkerObjectEndBytes)
@ -307,7 +306,7 @@ func (amf0) ReadObject(b []byte) (ObjectPairArray, int, error) {
}
index += l
default:
nazalog.Panicf("unknown type. vt=%d", vt)
Log.Panicf("unknown type. vt=%d", vt)
}
}
}
@ -366,7 +365,7 @@ func (amf0) ReadArray(b []byte) (ObjectPairArray, int, error) {
}
index += l
default:
nazalog.Panicf("unknown type. vt=%d", vt)
Log.Panicf("unknown type. vt=%d", vt)
}
}
@ -374,7 +373,7 @@ func (amf0) ReadArray(b []byte) (ObjectPairArray, int, error) {
index += 3
} else {
// 测试时发现Array最后也是以00 00 09结束不确定是否是标准规定的加个日志在这
nazalog.Warn("amf ReadArray without suffix Amf0TypeMarkerObjectEndBytes.")
Log.Warn("amf ReadArray without suffix Amf0TypeMarkerObjectEndBytes.")
}
return ops, index, nil
}

@ -17,8 +17,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
. "github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/assert"
"github.com/q191201771/naza/pkg/fake"
@ -118,7 +116,7 @@ func TestAmf0_ReadArray(t *testing.T) {
assert.Equal(t, 16, len(ops))
assert.Equal(t, 359, len(gold))
assert.Equal(t, 359, l)
nazalog.Debug(ops)
Log.Debug(ops)
}
func TestAmf0_ReadCase1(t *testing.T) {

@ -126,8 +126,8 @@ func (c *ChunkComposer) RunLoop(reader io.Reader, cb OnCompleteMessage) error {
case 3:
// noop
}
if nazalog.GetOption().Level == nazalog.LevelTrace {
nazalog.Tracef("[%p] RTMP_READ chunk.fmt=%d, csid=%d, header=%+v, timestamp=%d",
if Log.GetOption().Level == nazalog.LevelTrace {
Log.Tracef("[%p] RTMP_READ chunk.fmt=%d, csid=%d, header=%+v, timestamp=%d",
c, fmt, csid, stream.header, stream.timestamp)
}
@ -143,8 +143,8 @@ func (c *ChunkComposer) RunLoop(reader io.Reader, cb OnCompleteMessage) error {
return err
}
newTs := bele.BeUint32(bootstrap)
if nazalog.GetOption().Level == nazalog.LevelTrace {
nazalog.Tracef("[%p] RTMP_READ ext. ts=(%d,%d,%d)",
if Log.GetOption().Level == nazalog.LevelTrace {
Log.Tracef("[%p] RTMP_READ ext. ts=(%d,%d,%d)",
c, stream.timestamp, newTs, stream.header.TimestampAbs)
}
stream.timestamp = newTs
@ -188,8 +188,8 @@ func (c *ChunkComposer) RunLoop(reader io.Reader, cb OnCompleteMessage) error {
stream.header.TimestampAbs += stream.timestamp
}
absTsFlag = false
if nazalog.GetOption().Level == nazalog.LevelTrace {
nazalog.Tracef("[%p] RTMP_READ cb. fmt=%d, csid=%d, header=%+v, timestamp=%d, hex=%s",
if Log.GetOption().Level == nazalog.LevelTrace {
Log.Tracef("[%p] RTMP_READ cb. fmt=%d, csid=%d, header=%+v, timestamp=%d, hex=%s",
c, fmt, csid, stream.header, stream.timestamp, hex.Dump(nazabytes.Prefix(stream.msg.buff.Bytes(), 32)))
}

@ -23,12 +23,12 @@ var defaultChunkDivider = ChunkDivider{
localChunkSize: LocalChunkSize,
}
// @return 返回的内存块由内部申请,不依赖参数<message>内存块
// Message2Chunks @return 返回的内存块由内部申请,不依赖参数<message>内存块
func Message2Chunks(message []byte, header *base.RtmpHeader) []byte {
return defaultChunkDivider.Message2Chunks(message, header)
}
// TODO chef: 新的 message 的第一个 chunk 始终使用 fmt0 格式,没有参考前一个 message
// Message2Chunks TODO chef: 新的 message 的第一个 chunk 始终使用 fmt0 格式,没有参考前一个 message
func (d *ChunkDivider) Message2Chunks(message []byte, header *base.RtmpHeader) []byte {
return message2Chunks(message, header, nil, d.localChunkSize)
}

@ -53,7 +53,7 @@ func NewPullSession(modOptions ...ModPullSessionOption) *PullSession {
}
}
// 阻塞直到和对端完成拉流前的所有准备工作也即收到RTMP Play response或者发生错误
// Pull 阻塞直到和对端完成拉流前的所有准备工作也即收到RTMP Play response或者发生错误
//
// @param onReadRtmpAvMsg: msg: 注意,回调结束后,`msg`的内存块会被`PullSession`重复使用
// 也即多次回调的`msg`是复用的同一块内存块
@ -83,42 +83,42 @@ func (s *PullSession) WaitChan() <-chan error {
// ---------------------------------------------------------------------------------------------------------------------
// 文档请参考: interface ISessionUrlContext
// Url 文档请参考: interface ISessionUrlContext
func (s *PullSession) Url() string {
return s.core.Url()
}
// 文档请参考: interface ISessionUrlContext
// AppName 文档请参考: interface ISessionUrlContext
func (s *PullSession) AppName() string {
return s.core.AppName()
}
// 文档请参考: interface ISessionUrlContext
// StreamName 文档请参考: interface ISessionUrlContext
func (s *PullSession) StreamName() string {
return s.core.StreamName()
}
// 文档请参考: interface ISessionUrlContext
// RawQuery 文档请参考: interface ISessionUrlContext
func (s *PullSession) RawQuery() string {
return s.core.RawQuery()
}
// 文档请参考: interface IObject
// UniqueKey 文档请参考: interface IObject
func (s *PullSession) UniqueKey() string {
return s.core.uniqueKey
}
// 文档请参考: interface ISessionStat
// GetStat 文档请参考: interface ISessionStat
func (s *PullSession) GetStat() base.StatSession {
return s.core.GetStat()
}
// 文档请参考: interface ISessionStat
// UpdateStat 文档请参考: interface ISessionStat
func (s *PullSession) UpdateStat(intervalSec uint32) {
s.core.UpdateStat(intervalSec)
}
// 文档请参考: interface ISessionStat
// IsAlive 文档请参考: interface ISessionStat
func (s *PullSession) IsAlive() (readAlive, writeAlive bool) {
return s.core.IsAlive()
}

@ -54,7 +54,7 @@ func NewPushSession(modOptions ...ModPushSessionOption) *PushSession {
}
}
// 阻塞直到和对端完成推流前握手部分的工作也即收到RTMP Publish response或者发生错误
// Push 阻塞直到和对端完成推流前握手部分的工作也即收到RTMP Publish response或者发生错误
func (s *PushSession) Push(rawUrl string) error {
return s.core.Do(rawUrl)
}
@ -65,7 +65,7 @@ func (s *PushSession) Write(msg []byte) error {
return s.core.Write(msg)
}
// 将缓存的数据立即刷新发送
// Flush 将缓存的数据立即刷新发送
// 是否有缓存策略,请参见配置及内部实现
func (s *PushSession) Flush() error {
return s.core.Flush()
@ -89,42 +89,42 @@ func (s *PushSession) WaitChan() <-chan error {
// ---------------------------------------------------------------------------------------------------------------------
// 文档请参考: interface ISessionUrlContext
// Url 文档请参考: interface ISessionUrlContext
func (s *PushSession) Url() string {
return s.core.Url()
}
// 文档请参考: interface ISessionUrlContext
// AppName 文档请参考: interface ISessionUrlContext
func (s *PushSession) AppName() string {
return s.core.AppName()
}
// 文档请参考: interface ISessionUrlContext
// StreamName 文档请参考: interface ISessionUrlContext
func (s *PushSession) StreamName() string {
return s.core.StreamName()
}
// 文档请参考: interface ISessionUrlContext
// RawQuery 文档请参考: interface ISessionUrlContext
func (s *PushSession) RawQuery() string {
return s.core.RawQuery()
}
// 文档请参考: interface IObject
// UniqueKey 文档请参考: interface IObject
func (s *PushSession) UniqueKey() string {
return s.core.uniqueKey
}
// 文档请参考: interface ISessionStat
// GetStat 文档请参考: interface ISessionStat
func (s *PushSession) GetStat() base.StatSession {
return s.core.GetStat()
}
// 文档请参考: interface ISessionStat
// UpdateStat 文档请参考: interface ISessionStat
func (s *PushSession) UpdateStat(intervalSec uint32) {
s.core.UpdateStat(intervalSec)
}
// 文档请参考: interface ISessionStat
// IsAlive 文档请参考: interface ISessionStat
func (s *PushSession) IsAlive() (readAlive, writeAlive bool) {
return s.core.IsAlive()
}

@ -20,10 +20,9 @@ import (
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/naza/pkg/nazalog"
)
// rtmp 客户端类型连接的底层实现
// ClientSession rtmp 客户端类型连接的底层实现
// package rtmp 的使用者应该优先使用基于 ClientSession 实现的 PushSession 和 PullSession
type ClientSession struct {
uniqueKey string
@ -85,7 +84,7 @@ var defaultClientSessOption = ClientSessionOption{
type ModClientSessionOption func(option *ClientSessionOption)
// @param t: session的类型只能是推或者拉
// NewClientSession @param t: session的类型只能是推或者拉
func NewClientSession(t ClientSessionType, modOptions ...ModClientSessionOption) *ClientSession {
var uk string
switch t {
@ -117,18 +116,18 @@ func NewClientSession(t ClientSessionType, modOptions ...ModClientSessionOption)
stat: base.StatSession{
Protocol: base.ProtocolRtmp,
SessionId: uk,
StartTime: time.Now().Format("2006-01-02 15:04:05.999"),
StartTime: base.ReadableNowTime(),
},
debugLogReadUserCtrlMsgMax: 5,
hc: hc,
}
nazalog.Infof("[%s] lifecycle new rtmp ClientSession. session=%p", uk, s)
Log.Infof("[%s] lifecycle new rtmp ClientSession. session=%p", uk, s)
return s
}
// 阻塞直到收到服务端返回的 publish / play 对应结果的信令或者发生错误
// Do 阻塞直到收到服务端返回的 publish / play 对应结果的信令或者发生错误
func (s *ClientSession) Do(rawUrl string) error {
nazalog.Debugf("[%s] Do. url=%s", s.uniqueKey, rawUrl)
Log.Debugf("[%s] Do. url=%s", s.uniqueKey, rawUrl)
var (
ctx context.Context
@ -250,13 +249,13 @@ func (s *ClientSession) doContext(ctx context.Context, rawUrl string) error {
return
}
nazalog.Infof("[%s] > W SetChunkSize %d.", s.uniqueKey, LocalChunkSize)
Log.Infof("[%s] > W SetChunkSize %d.", s.uniqueKey, LocalChunkSize)
if err := s.packer.writeChunkSize(s.conn, LocalChunkSize); err != nil {
errChan <- err
return
}
nazalog.Infof("[%s] > W connect('%s'). tcUrl=%s", s.uniqueKey, s.appName(), s.tcUrl())
Log.Infof("[%s] > W connect('%s'). tcUrl=%s", s.uniqueKey, s.appName(), s.tcUrl())
if err := s.packer.writeConnect(s.conn, s.appName(), s.tcUrl(), s.t == CstPushSession); err != nil {
errChan <- err
return
@ -301,7 +300,7 @@ func (s *ClientSession) streamNameWithRawQuery() string {
}
func (s *ClientSession) tcpConnect() error {
nazalog.Infof("[%s] > tcp connect.", s.uniqueKey)
Log.Infof("[%s] > tcp connect.", s.uniqueKey)
var err error
s.stat.RemoteAddr = s.urlCtx.HostWithPort
@ -319,7 +318,7 @@ func (s *ClientSession) tcpConnect() error {
}
func (s *ClientSession) handshake() error {
nazalog.Infof("[%s] > W Handshake C0+C1.", s.uniqueKey)
Log.Infof("[%s] > W Handshake C0+C1.", s.uniqueKey)
if err := s.hc.WriteC0C1(s.conn); err != nil {
return err
}
@ -327,9 +326,9 @@ func (s *ClientSession) handshake() error {
if err := s.hc.ReadS0S1(s.conn); err != nil {
return err
}
nazalog.Infof("[%s] < R Handshake S0+S1.", s.uniqueKey)
Log.Infof("[%s] < R Handshake S0+S1.", s.uniqueKey)
nazalog.Infof("[%s] > W Handshake C2.", s.uniqueKey)
Log.Infof("[%s] > W Handshake C2.", s.uniqueKey)
if err := s.hc.WriteC2(s.conn); err != nil {
return err
}
@ -337,7 +336,7 @@ func (s *ClientSession) handshake() error {
if err := s.hc.ReadS2(s.conn); err != nil {
return err
}
nazalog.Infof("[%s] < R Handshake S2.", s.uniqueKey)
Log.Infof("[%s] < R Handshake S2.", s.uniqueKey)
return nil
}
@ -368,7 +367,7 @@ func (s *ClientSession) doMsg(stream *Stream) error {
case base.RtmpTypeIdVideo:
s.onReadRtmpAvMsg(stream.toAvMsg())
default:
nazalog.Errorf("[%s] read unknown message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeId, stream.toDebugString())
Log.Errorf("[%s] read unknown message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeId, stream.toDebugString())
panic(0)
}
return nil
@ -376,7 +375,7 @@ func (s *ClientSession) doMsg(stream *Stream) error {
func (s *ClientSession) doAck(stream *Stream) error {
seqNum := bele.BeUint32(stream.msg.buff.Bytes())
nazalog.Infof("[%s] < R Acknowledgement. ignore. sequence number=%d.", s.uniqueKey, seqNum)
Log.Infof("[%s] < R Acknowledgement. ignore. sequence number=%d.", s.uniqueKey, seqNum)
return nil
}
func (s *ClientSession) doUserControl(stream *Stream) error {
@ -389,7 +388,7 @@ func (s *ClientSession) doUserControl(stream *Stream) error {
s.debugLogReadUserCtrlMsgCount++
if s.debugLogReadUserCtrlMsgCount <= s.debugLogReadUserCtrlMsgMax {
nazalog.Warnf("[%s] read user control message, ignore. buf=%s",
Log.Warnf("[%s] read user control message, ignore. buf=%s",
s.uniqueKey, hex.Dump(stream.msg.buff.Peek(32)))
}
return nil
@ -403,7 +402,7 @@ func (s *ClientSession) doDataMessageAmf0(stream *Stream) error {
switch val {
case "|RtmpSampleAccess":
nazalog.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.uniqueKey)
Log.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.uniqueKey)
return nil
default:
}
@ -424,13 +423,13 @@ func (s *ClientSession) doCommandMessage(stream *Stream) error {
switch cmd {
case "onBWDone":
nazalog.Warnf("[%s] < R onBWDone. ignore.", s.uniqueKey)
Log.Warnf("[%s] < R onBWDone. ignore.", s.uniqueKey)
case "_result":
return s.doResultMessage(stream, tid)
case "onStatus":
return s.doOnStatusMessage(stream, tid)
default:
nazalog.Errorf("[%s] read unknown command message. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
Log.Errorf("[%s] read unknown command message. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
}
return nil
@ -452,18 +451,18 @@ func (s *ClientSession) doOnStatusMessage(stream *Stream, tid int) error {
case CstPushSession:
switch code {
case "NetStream.Publish.Start":
nazalog.Infof("[%s] < R onStatus('NetStream.Publish.Start').", s.uniqueKey)
Log.Infof("[%s] < R onStatus('NetStream.Publish.Start').", s.uniqueKey)
s.notifyDoResultSucc()
default:
nazalog.Warnf("[%s] read on status message but code field unknown. code=%s", s.uniqueKey, code)
Log.Warnf("[%s] read on status message but code field unknown. code=%s", s.uniqueKey, code)
}
case CstPullSession:
switch code {
case "NetStream.Play.Start":
nazalog.Infof("[%s] < R onStatus('NetStream.Play.Start').", s.uniqueKey)
Log.Infof("[%s] < R onStatus('NetStream.Play.Start').", s.uniqueKey)
s.notifyDoResultSucc()
default:
nazalog.Warnf("[%s] read on status message but code field unknown. code=%s", s.uniqueKey, code)
Log.Warnf("[%s] read on status message but code field unknown. code=%s", s.uniqueKey, code)
}
}
@ -487,13 +486,13 @@ func (s *ClientSession) doResultMessage(stream *Stream, tid int) error {
}
switch code {
case "NetConnection.Connect.Success":
nazalog.Infof("[%s] < R _result(\"NetConnection.Connect.Success\").", s.uniqueKey)
nazalog.Infof("[%s] > W createStream().", s.uniqueKey)
Log.Infof("[%s] < R _result(\"NetConnection.Connect.Success\").", s.uniqueKey)
Log.Infof("[%s] > W createStream().", s.uniqueKey)
if err := s.packer.writeCreateStream(s.conn); err != nil {
return err
}
default:
nazalog.Errorf("[%s] unknown code. code=%v", s.uniqueKey, code)
Log.Errorf("[%s] unknown code. code=%v", s.uniqueKey, code)
}
case tidClientCreateStream:
err := stream.msg.readNull()
@ -504,21 +503,21 @@ func (s *ClientSession) doResultMessage(stream *Stream, tid int) error {
if err != nil {
return err
}
nazalog.Infof("[%s] < R _result().", s.uniqueKey)
Log.Infof("[%s] < R _result().", s.uniqueKey)
switch s.t {
case CstPullSession:
nazalog.Infof("[%s] > W play('%s').", s.uniqueKey, s.streamNameWithRawQuery())
Log.Infof("[%s] > W play('%s').", s.uniqueKey, s.streamNameWithRawQuery())
if err := s.packer.writePlay(s.conn, s.streamNameWithRawQuery(), sid); err != nil {
return err
}
case CstPushSession:
nazalog.Infof("[%s] > W publish('%s').", s.uniqueKey, s.streamNameWithRawQuery())
Log.Infof("[%s] > W publish('%s').", s.uniqueKey, s.streamNameWithRawQuery())
if err := s.packer.writePublish(s.conn, s.appName(), s.streamNameWithRawQuery(), sid); err != nil {
return err
}
}
default:
nazalog.Errorf("[%s] unknown tid. tid=%d", s.uniqueKey, tid)
Log.Errorf("[%s] unknown tid. tid=%d", s.uniqueKey, tid)
}
return nil
}
@ -531,15 +530,15 @@ func (s *ClientSession) doProtocolControlMessage(stream *Stream) error {
switch stream.header.MsgTypeId {
case base.RtmpTypeIdWinAckSize:
s.peerWinAckSize = val
nazalog.Infof("[%s] < R Window Acknowledgement Size: %d", s.uniqueKey, s.peerWinAckSize)
Log.Infof("[%s] < R Window Acknowledgement Size: %d", s.uniqueKey, s.peerWinAckSize)
case base.RtmpTypeIdBandwidth:
// TODO chef: 是否需要关注这个信令
nazalog.Warnf("[%s] < R Set Peer Bandwidth. ignore.", s.uniqueKey)
Log.Warnf("[%s] < R Set Peer Bandwidth. ignore.", s.uniqueKey)
case base.RtmpTypeIdSetChunkSize:
// composer内部会自动更新peer chunk size.
nazalog.Infof("[%s] < R Set Chunk Size %d.", s.uniqueKey, val)
Log.Infof("[%s] < R Set Chunk Size %d.", s.uniqueKey, val)
default:
nazalog.Errorf("[%s] read unknown protocol control message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeId, stream.toDebugString())
Log.Errorf("[%s] read unknown protocol control message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeId, stream.toDebugString())
}
return nil
}
@ -547,7 +546,7 @@ func (s *ClientSession) doProtocolControlMessage(stream *Stream) error {
func (s *ClientSession) notifyDoResultSucc() {
// 碰上过对端服务器实现有问题对于play信令回复了两次相同的结果我们在这里忽略掉非第一次的回复
if s.hasNotifyDoResultSucc {
nazalog.Warnf("[%s] has notified do result succ already, ignore it", s.uniqueKey)
Log.Warnf("[%s] has notified do result succ already, ignore it", s.uniqueKey)
return
}
s.hasNotifyDoResultSucc = true
@ -563,7 +562,7 @@ func (s *ClientSession) notifyDoResultSucc() {
func (s *ClientSession) dispose(err error) error {
var retErr error
s.disposeOnce.Do(func() {
nazalog.Infof("[%s] lifecycle dispose rtmp ClientSession. err=%+v", s.uniqueKey, err)
Log.Infof("[%s] lifecycle dispose rtmp ClientSession. err=%+v", s.uniqueKey, err)
if s.conn == nil {
retErr = base.ErrSessionNotStarted
return

@ -19,7 +19,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
// https://pengrl.com/p/20027
@ -234,7 +233,7 @@ func parseChallenge(b []byte, peerKey []byte, key []byte) []byte {
//}
ver := bele.BeUint32(b[5:])
if ver == 0 {
nazalog.Debug("handshake simple mode.")
Log.Debug("handshake simple mode.")
return nil
}
@ -243,10 +242,10 @@ func parseChallenge(b []byte, peerKey []byte, key []byte) []byte {
offs = findDigest(b[1:], 8, peerKey)
}
if offs == -1 {
nazalog.Warn("get digest offs failed. roll back to try simple handshake.")
Log.Warn("get digest offs failed. roll back to try simple handshake.")
return nil
}
nazalog.Debug("handshake complex mode.")
Log.Debug("handshake complex mode.")
// use c0c1 digest to make a new digest
digest := makeDigest(b[1+offs:1+offs+keyLen], key)

@ -12,8 +12,6 @@ import (
"fmt"
"io"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/bele"
@ -25,7 +23,7 @@ const (
peerBandwidthLimitTypeDynamic = uint8(2)
)
// 打包并发送 rtmp 信令
// MessagePacker 打包并发送 rtmp 信令
//
type MessagePacker struct {
b *Buffer
@ -323,7 +321,7 @@ func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
if nBytes := b.Len(); nBytes > 0 {
m, e := w.Write(b.Bytes())
if m > nBytes {
nazalog.Panicf("Buffer.WriteTo: invalid Write count. expected=%d, actual=%d", nBytes, m)
Log.Panicf("Buffer.WriteTo: invalid Write count. expected=%d, actual=%d", nBytes, m)
}
b.readPos += m
n = int64(m)
@ -356,7 +354,7 @@ func (b *Buffer) grow(n int) {
newLen = cap(b.core) * 2
}
buf := make([]byte, newLen)
nazalog.Debugf("Buffer::grow. need=%d, old len=%d, cap=%d, new len=%d", n, b.Len(), cap(b.core), newLen)
Log.Debugf("Buffer::grow. need=%d, old len=%d, cap=%d, new len=%d", n, b.Len(), cap(b.core), newLen)
copy(buf, b.core[b.readPos:b.writePos])
b.core = buf
b.readPos = 0

@ -32,7 +32,7 @@ func ParseMetadata(b []byte) (ObjectPairArray, error) {
return opa, err
}
// spec-video_file_format_spec_v10.pdf
// BuildMetadata spec-video_file_format_spec_v10.pdf
// onMetaData
// - duration DOUBLE, seconds
// - width DOUBLE

@ -15,7 +15,6 @@ import (
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/assert"
"github.com/q191201771/naza/pkg/nazalog"
)
func TestMetadata(t *testing.T) {
@ -24,7 +23,7 @@ func TestMetadata(t *testing.T) {
opa, err := rtmp.ParseMetadata(b)
assert.Equal(t, nil, err)
nazalog.Debugf("%+v", opa)
rtmp.Log.Debugf("%+v", opa)
assert.Equal(t, 5, len(opa))
v := opa.Find("width")

@ -38,6 +38,7 @@ const defaultChunkSize = 128 // 未收到对端设置chunk size时的默认值
const (
//MSID0 = 0 // 所有除 publish、play、onStatus 之外的信令
Msid1 = 1 // publish、play、onStatus 以及 音视频数据
)

@ -10,8 +10,6 @@ package rtmp
import (
"net"
log "github.com/q191201771/naza/pkg/nazalog"
)
type ServerObserver interface {
@ -52,7 +50,7 @@ func (server *Server) Listen() (err error) {
if server.ln, err = net.Listen("tcp", server.addr); err != nil {
return
}
log.Infof("start rtmp server listen. addr=%s", server.addr)
Log.Infof("start rtmp server listen. addr=%s", server.addr)
return
}
@ -71,12 +69,12 @@ func (server *Server) Dispose() {
return
}
if err := server.ln.Close(); err != nil {
log.Error(err)
Log.Error(err)
}
}
func (server *Server) handleTcpConnect(conn net.Conn) {
log.Infof("accept a rtmp connection. remoteAddr=%s", conn.RemoteAddr().String())
Log.Infof("accept a rtmp connection. remoteAddr=%s", conn.RemoteAddr().String())
session := NewServerSession(server, conn)
_ = session.RunLoop()

@ -13,7 +13,6 @@ import (
"net"
"strings"
"sync"
"time"
"github.com/q191201771/naza/pkg/nazaerrors"
@ -21,7 +20,6 @@ import (
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/naza/pkg/nazalog"
)
// TODO chef: 没有进化成Pub Sub时的超时释放
@ -41,7 +39,7 @@ type ServerSessionObserver interface {
}
type PubSessionObserver interface {
// 注意回调结束后内部会复用Payload内存块
// OnReadRtmpAvMsg 注意回调结束后内部会复用Payload内存块
OnReadRtmpAvMsg(msg base.RtmpMsg)
}
@ -98,7 +96,7 @@ func NewServerSession(observer ServerSessionObserver, conn net.Conn) *ServerSess
stat: base.StatSession{
Protocol: base.ProtocolRtmp,
SessionId: uk,
StartTime: time.Now().Format("2006-01-02 15:04:05.999"),
StartTime: base.ReadableNowTime(),
RemoteAddr: conn.RemoteAddr().String(),
},
uniqueKey: uk,
@ -109,7 +107,7 @@ func NewServerSession(observer ServerSessionObserver, conn net.Conn) *ServerSess
IsFresh: true,
ShouldWaitVideoKeyFrame: true,
}
nazalog.Infof("[%s] lifecycle new rtmp ServerSession. session=%p, remote addr=%s", uk, s, conn.RemoteAddr().String())
Log.Infof("[%s] lifecycle new rtmp ServerSession. session=%p, remote addr=%s", uk, s, conn.RemoteAddr().String())
return s
}
@ -207,9 +205,9 @@ func (s *ServerSession) handshake() error {
if err := s.hs.ReadC0C1(s.conn); err != nil {
return err
}
nazalog.Infof("[%s] < R Handshake C0+C1.", s.uniqueKey)
Log.Infof("[%s] < R Handshake C0+C1.", s.uniqueKey)
nazalog.Infof("[%s] > W Handshake S0+S1+S2.", s.uniqueKey)
Log.Infof("[%s] > W Handshake S0+S1+S2.", s.uniqueKey)
if err := s.hs.WriteS0S1S2(s.conn); err != nil {
return err
}
@ -217,7 +215,7 @@ func (s *ServerSession) handshake() error {
if err := s.hs.ReadC2(s.conn); err != nil {
return err
}
nazalog.Infof("[%s] < R Handshake C2.", s.uniqueKey)
Log.Infof("[%s] < R Handshake C2.", s.uniqueKey)
return nil
}
@ -245,7 +243,7 @@ func (s *ServerSession) doMsg(stream *Stream) error {
}
s.avObserver.OnReadRtmpAvMsg(stream.toAvMsg())
default:
nazalog.Warnf("[%s] read unknown message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeId, stream.toDebugString())
Log.Warnf("[%s] read unknown message. typeid=%d, %s", s.uniqueKey, stream.header.MsgTypeId, stream.toDebugString())
}
return nil
@ -253,7 +251,7 @@ func (s *ServerSession) doMsg(stream *Stream) error {
func (s *ServerSession) doAck(stream *Stream) error {
seqNum := bele.BeUint32(stream.msg.buff.Bytes())
nazalog.Infof("[%s] < R Acknowledgement. ignore. sequence number=%d.", s.uniqueKey, seqNum)
Log.Infof("[%s] < R Acknowledgement. ignore. sequence number=%d.", s.uniqueKey, seqNum)
return nil
}
func (s *ServerSession) doUserControl(stream *Stream) error {
@ -277,7 +275,7 @@ func (s *ServerSession) doDataMessageAmf0(stream *Stream) error {
switch val {
case "|RtmpSampleAccess":
nazalog.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.uniqueKey)
Log.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.uniqueKey)
return nil
default:
}
@ -292,7 +290,7 @@ func (s *ServerSession) doDataMessageAmf0(stream *Stream) error {
//
//switch val {
//case "|RtmpSampleAccess":
// nazalog.Warnf("[%s] read data message, ignore it. val=%s", s.uniqueKey, val)
// Log.Warnf("[%s] read data message, ignore it. val=%s", s.uniqueKey, val)
// return nil
//case "@setDataFrame":
// // macos obs and ffmpeg
@ -304,13 +302,13 @@ func (s *ServerSession) doDataMessageAmf0(stream *Stream) error {
// return err
// }
// if val != "onMetaData" {
// nazalog.Errorf("[%s] read unknown data message. val=%s, %s", s.uniqueKey, val, stream.toDebugString())
// Log.Errorf("[%s] read unknown data message. val=%s, %s", s.uniqueKey, val, stream.toDebugString())
// return ErrRtmp
// }
//case "onMetaData":
// // noop
//default:
// nazalog.Errorf("[%s] read unknown data message. val=%s, %s", s.uniqueKey, val, stream.toDebugString())
// Log.Errorf("[%s] read unknown data message. val=%s, %s", s.uniqueKey, val, stream.toDebugString())
// return nil
//}
//
@ -346,9 +344,9 @@ func (s *ServerSession) doCommandMessage(stream *Stream) error {
case "getStreamLength":
fallthrough
case "deleteStream":
nazalog.Debugf("[%s] read command message, ignore it. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
Log.Debugf("[%s] read command message, ignore it. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
default:
nazalog.Errorf("[%s] read unknown command message. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
Log.Errorf("[%s] read unknown command message. cmd=%s, %s", s.uniqueKey, cmd, stream.toDebugString())
}
return nil
}
@ -370,28 +368,28 @@ func (s *ServerSession) doConnect(tid int, stream *Stream) error {
}
s.tcUrl, err = val.FindString("tcUrl")
if err != nil {
nazalog.Warnf("[%s] tcUrl not exist.", s.uniqueKey)
Log.Warnf("[%s] tcUrl not exist.", s.uniqueKey)
}
nazalog.Infof("[%s] < R connect('%s'). tcUrl=%s", s.uniqueKey, s.appName, s.tcUrl)
Log.Infof("[%s] < R connect('%s'). tcUrl=%s", s.uniqueKey, s.appName, s.tcUrl)
s.observer.OnRtmpConnect(s, val)
nazalog.Infof("[%s] > W Window Acknowledgement Size %d.", s.uniqueKey, windowAcknowledgementSize)
Log.Infof("[%s] > W Window Acknowledgement Size %d.", s.uniqueKey, windowAcknowledgementSize)
if err := s.packer.writeWinAckSize(s.conn, windowAcknowledgementSize); err != nil {
return err
}
nazalog.Infof("[%s] > W Set Peer Bandwidth.", s.uniqueKey)
Log.Infof("[%s] > W Set Peer Bandwidth.", s.uniqueKey)
if err := s.packer.writePeerBandwidth(s.conn, peerBandwidth, peerBandwidthLimitTypeDynamic); err != nil {
return err
}
nazalog.Infof("[%s] > W SetChunkSize %d.", s.uniqueKey, LocalChunkSize)
Log.Infof("[%s] > W SetChunkSize %d.", s.uniqueKey, LocalChunkSize)
if err := s.packer.writeChunkSize(s.conn, LocalChunkSize); err != nil {
return err
}
nazalog.Infof("[%s] > W _result('NetConnection.Connect.Success').", s.uniqueKey)
Log.Infof("[%s] > W _result('NetConnection.Connect.Success').", s.uniqueKey)
oe, err := val.FindNumber("objectEncoding")
if oe != 0 && oe != 3 {
oe = 0
@ -403,8 +401,8 @@ func (s *ServerSession) doConnect(tid int, stream *Stream) error {
}
func (s *ServerSession) doCreateStream(tid int, stream *Stream) error {
nazalog.Infof("[%s] < R createStream().", s.uniqueKey)
nazalog.Infof("[%s] > W _result().", s.uniqueKey)
Log.Infof("[%s] < R createStream().", s.uniqueKey)
Log.Infof("[%s] > W _result().", s.uniqueKey)
if err := s.packer.writeCreateStreamResult(s.conn, tid); err != nil {
return err
}
@ -431,10 +429,10 @@ func (s *ServerSession) doPublish(tid int, stream *Stream) (err error) {
if err != nil {
return err
}
nazalog.Debugf("[%s] pubType=%s", s.uniqueKey, pubType)
nazalog.Infof("[%s] < R publish('%s')", s.uniqueKey, s.streamNameWithRawQuery)
Log.Debugf("[%s] pubType=%s", s.uniqueKey, pubType)
Log.Infof("[%s] < R publish('%s')", s.uniqueKey, s.streamNameWithRawQuery)
nazalog.Infof("[%s] > W onStatus('NetStream.Publish.Start').", s.uniqueKey)
Log.Infof("[%s] > W onStatus('NetStream.Publish.Start').", s.uniqueKey)
if err = s.packer.writeOnStatusPublish(s.conn, Msid1); err != nil {
return err
}
@ -466,7 +464,7 @@ func (s *ServerSession) doPlay(tid int, stream *Stream) (err error) {
s.url = fmt.Sprintf("%s/%s", s.tcUrl, s.streamNameWithRawQuery)
nazalog.Infof("[%s] < R play('%s').", s.uniqueKey, s.streamNameWithRawQuery)
Log.Infof("[%s] < R play('%s').", s.uniqueKey, s.streamNameWithRawQuery)
// TODO chef: start duration reset
if err := s.packer.writeStreamIsRecorded(s.conn, Msid1); err != nil {
@ -476,7 +474,7 @@ func (s *ServerSession) doPlay(tid int, stream *Stream) (err error) {
return err
}
nazalog.Infof("[%s] > W onStatus('NetStream.Play.Start').", s.uniqueKey)
Log.Infof("[%s] > W onStatus('NetStream.Play.Start').", s.uniqueKey)
if err := s.packer.writeOnStatusPlay(s.conn, Msid1); err != nil {
return err
}
@ -506,7 +504,7 @@ func (s *ServerSession) modConnProps() {
func (s *ServerSession) dispose(err error) error {
var retErr error
s.disposeOnce.Do(func() {
nazalog.Infof("[%s] lifecycle dispose rtmp ServerSession. err=%+v", s.uniqueKey, err)
Log.Infof("[%s] lifecycle dispose rtmp ServerSession. err=%+v", s.uniqueKey, err)
if s.conn == nil {
retErr = base.ErrSessionNotStarted
return

@ -15,7 +15,6 @@ import (
"github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
)
// ----- Stream --------------------------------------------------------------------------------------------------------
@ -44,7 +43,7 @@ func (stream *Stream) toDebugString() string {
func (stream *Stream) toAvMsg() base.RtmpMsg {
// TODO chef: 考虑可能出现header中的len和buf的大小不一致的情况
if stream.header.MsgLen != uint32(stream.msg.buff.Len()) {
nazalog.Errorf("toAvMsg. headerMsgLen=%d, bufLen=%d", stream.header.MsgLen, stream.msg.buff.Len())
Log.Errorf("toAvMsg. headerMsgLen=%d, bufLen=%d", stream.header.MsgLen, stream.msg.buff.Len())
}
return base.RtmpMsg{
Header: stream.header,
@ -58,7 +57,7 @@ type StreamMsg struct {
buff *nazabytes.Buffer
}
// 确保可写空间,如果不够会扩容
// Grow 确保可写空间,如果不够会扩容
func (msg *StreamMsg) Grow(n uint32) {
msg.buff.Grow(int(n))
}

@ -8,8 +8,14 @@
package rtmp
// TODO chef
// 一些更专业的配置项,暂时只在该源码文件中配置,不提供外部配置接口
import "github.com/q191201771/naza/pkg/nazalog"
// TODO chef 一些更专业的配置项,暂时只在该源码文件中配置,不提供外部配置接口
var (
Log = nazalog.GetGlobalLogger()
)
var (
readBufSize = 4096 // server session connection读缓冲的大小
wChanSize = 1024 // server session 发送数据时channel 的大小

@ -11,19 +11,19 @@ package rtprtcp
// (70 * 365 + 17) * 24 * 60 * 60
const offset uint64 = 2208988800
// 将ntp时间戳转换为Unix时间戳Unix时间戳单位是纳秒
// Ntp2UnixNano 将ntp时间戳转换为Unix时间戳Unix时间戳单位是纳秒
func Ntp2UnixNano(v uint64) uint64 {
msw := v >> 32
lsw := v & 0xFFFFFFFF
return (msw-offset)*1e9 + (lsw*1e9)>>32
}
// 将ntp时间戳高32位低32位分开的形式转换为Unix时间戳
// MswLsw2UnixNano 将ntp时间戳高32位低32位分开的形式转换为Unix时间戳
func MswLsw2UnixNano(msw, lsw uint64) uint64 {
return Ntp2UnixNano(MswLsw2Ntp(msw, lsw))
}
// msw是ntp的高32位lsw是ntp的低32位
// MswLsw2Ntp msw是ntp的高32位lsw是ntp的低32位
func MswLsw2Ntp(msw, lsw uint64) uint64 {
return (msw << 32) | lsw
}

@ -13,12 +13,11 @@ import (
"time"
"github.com/q191201771/lal/pkg/rtprtcp"
"github.com/q191201771/naza/pkg/nazalog"
)
func TestMswLsw2UnixNano(t *testing.T) {
u := rtprtcp.MswLsw2UnixNano(3805600902, 2181843386)
nazalog.Debug(u)
rtprtcp.Log.Debug(u)
tt := time.Unix(int64(u/1e9), int64(u%1e9))
nazalog.Debug(tt.String())
rtprtcp.Log.Debug(tt.String())
}

@ -119,7 +119,7 @@ func ParseRtcpHeader(b []byte) RtcpHeader {
return h
}
// rfc3550 6.4.1
// ParseSr rfc3550 6.4.1
//
// @param b rtcp包包含包头
func ParseSr(b []byte) Sr {
@ -133,7 +133,7 @@ func ParseSr(b []byte) Sr {
return s
}
// @param out 传出参数,注意,调用方保证长度>=4
// PackTo @param out 传出参数,注意,调用方保证长度>=4
func (r *RtcpHeader) PackTo(out []byte) {
out[0] = r.Version<<6 | r.Padding<<5 | r.CountOrFormat
out[1] = r.PacketType

@ -40,7 +40,7 @@ func NewRrProducer(clockRate int) *RrProducer {
}
}
// 每次收到rtp包都将seq序号传入这个函数
// FeedRtpPacket 每次收到rtp包都将seq序号传入这个函数
func (r *RrProducer) FeedRtpPacket(seq uint16) {
r.received++
@ -62,7 +62,7 @@ func (r *RrProducer) FeedRtpPacket(seq uint16) {
r.extendedSeq = (r.cycles << 16) | uint32(r.maxSeq)
}
// 收到sr包时产生rr包
// Produce 收到sr包时产生rr包
//
// @param lsr: 从sr包中获取见func SR.GetMiddleNtp
// @return: rr包的二进制数据

@ -28,11 +28,11 @@ const (
NaluTypeAvcStapa = 24 // one packet, multiple nals
NaluTypeAvcFua = 28
// TODO(chef): hevc有stapa格式吗
// NaluTypeHevcFua TODO(chef): hevc有stapa格式吗
NaluTypeHevcFua = 49
)
// 比较序号的值,内部处理序号翻转问题,见单元测试中的例子
// CompareSeq 比较序号的值,内部处理序号翻转问题,见单元测试中的例子
// @return 0 a和b相等
// 1 a大于b
// -1 a小于b
@ -56,7 +56,7 @@ func CompareSeq(a, b uint16) int {
return 1
}
// a减b的值内部处理序号翻转问题如果a小于b则返回负值见单元测试中的例子
// SubSeq a减b的值内部处理序号翻转问题如果a小于b则返回负值见单元测试中的例子
func SubSeq(a, b uint16) int {
if a == b {
return 0

@ -52,7 +52,7 @@ func NewRtpPacker(payloadPacker IRtpPackerPayload, clockRate int, ssrc uint32, m
}
}
// @param pkt: pkt.Timestamp 绝对时间戳,单位毫秒
// Pack @param pkt: pkt.Timestamp 绝对时间戳,单位毫秒
// pkt.PayloadType rtp包头中的packet type
//
func (r *RtpPacker) Pack(pkt base.AvPacket) (out []RtpPacket) {

@ -9,7 +9,7 @@
package rtprtcp
type IRtpPackerPayload interface {
// @param maxSize: rtp payload包体部分不含包头的最大大小
// Pack @param maxSize: rtp payload包体部分不含包头的最大大小
//
Pack(in []byte, maxSize int) (out [][]byte)
}

@ -8,8 +8,6 @@
package rtprtcp
import "github.com/q191201771/naza/pkg/nazalog"
type RtpPackerPayloadAac struct {
}
@ -31,7 +29,7 @@ func (r *RtpPackerPayloadAac) Pack(in []byte, maxSize int) (out [][]byte) {
// 第三部分是帧数据的数组
if len(in) > maxSize {
nazalog.Warnf("frame size bigger than rtp payload size while packing. len(in)=%d, maxSize=%d", len(in), maxSize)
Log.Warnf("frame size bigger than rtp payload size while packing. len(in)=%d, maxSize=%d", len(in), maxSize)
}
auHeadersLength := 2 // auHeaderSize * nbAuHeaders = 2 * 1

@ -56,7 +56,7 @@ func NewRtpPackerPayloadAvcHevc(payloadType base.AvPacketPt, modOptions ...ModRt
}
}
// @param in: AVCC格式
// Pack @param in: AVCC格式
//
// @return out: 内存块为独立新申请;函数返回后,内部不再持有该内存块
//

@ -13,7 +13,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
// -----------------------------------
@ -114,7 +113,7 @@ func ParseRtpHeader(b []byte) (h RtpHeader, err error) {
return
}
// 函数调用结束后,不持有参数<b>的内存块
// ParseRtpPacket 函数调用结束后,不持有参数<b>的内存块
func ParseRtpPacket(b []byte) (pkt RtpPacket, err error) {
pkt.Header, err = ParseRtpHeader(b)
if err != nil {
@ -127,13 +126,13 @@ func ParseRtpPacket(b []byte) (pkt RtpPacket, err error) {
func (p *RtpPacket) Body() []byte {
if p.Header.payloadOffset == 0 {
nazalog.Warnf("CHEFNOTICEME. payloadOffset=%d", p.Header.payloadOffset)
Log.Warnf("CHEFNOTICEME. payloadOffset=%d", p.Header.payloadOffset)
p.Header.payloadOffset = RtpFixedHeaderLength
}
return p.Raw[p.Header.payloadOffset:]
}
// @param pt: 取值范围为AvPacketPtAvc或AvPacketPtHevc否则直接返回false
// IsAvcHevcBoundary @param pt: 取值范围为AvPacketPtAvc或AvPacketPtHevc否则直接返回false
//
func IsAvcHevcBoundary(pkt RtpPacket, pt base.AvPacketPt) bool {
switch pt {

@ -35,7 +35,7 @@ func NewRtpUnpackContainer(maxSize int, unpackerProtocol IRtpUnpackerProtocol) *
}
}
// 输入收到的rtp包
// Feed 输入收到的rtp包
func (r *RtpUnpackContainer) Feed(pkt RtpPacket) {
// 过期的包
if r.isStale(pkt.Header.Seq) {

@ -10,7 +10,6 @@ package rtprtcp
import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
)
// 传入RTP包合成帧数据并回调返回
@ -32,10 +31,10 @@ type IRtpUnpackContainer interface {
}
type IRtpUnpackerProtocol interface {
// 计算rtp包处于帧中的位置
// CalcPositionIfNeeded 计算rtp包处于帧中的位置
CalcPositionIfNeeded(pkt *RtpPacket)
// 尝试合成一个完整帧
// TryUnpackOne 尝试合成一个完整帧
//
// 从当前队列的第一个包开始合成
// 如果一个rtp包对应一个完整帧则合成一帧
@ -47,7 +46,7 @@ type IRtpUnpackerProtocol interface {
TryUnpackOne(list *RtpPacketList) (unpackedFlag bool, unpackedSeq uint16)
}
// @param pkt: pkt.Timestamp RTP包头中的时间戳(pts)经过clockrate换算后的时间戳单位毫秒
// OnAvPacket @param pkt: pkt.Timestamp RTP包头中的时间戳(pts)经过clockrate换算后的时间戳单位毫秒
// 注意不支持带B帧的视频流pts和dts永远相同
// pkt.PayloadType base.AvPacketPTXXX
// pkt.Payload AAC:
@ -60,7 +59,7 @@ type IRtpUnpackerProtocol interface {
// 假如sps和pps是一个stapA包则合并结果为一个AvPacket
type OnAvPacket func(pkt base.AvPacket)
// 目前支持AVCHEVC和AAC MPEG4-GENERIC业务方也可以自己实现IRtpUnpackerProtocol甚至是IRtpUnpackContainer
// DefaultRtpUnpackerFactory 目前支持AVCHEVC和AAC MPEG4-GENERIC业务方也可以自己实现IRtpUnpackerProtocol甚至是IRtpUnpackContainer
func DefaultRtpUnpackerFactory(payloadType base.AvPacketPt, clockRate int, maxSize int, onAvPacket OnAvPacket) IRtpUnpacker {
var protocol IRtpUnpackerProtocol
switch payloadType {
@ -71,7 +70,7 @@ func DefaultRtpUnpackerFactory(payloadType base.AvPacketPt, clockRate int, maxSi
case base.AvPacketPtHevc:
protocol = NewRtpUnpackerAvcHevc(payloadType, clockRate, onAvPacket)
default:
nazalog.Fatalf("payload type not support yet. payloadType=%d", payloadType)
Log.Fatalf("payload type not support yet. payloadType=%d", payloadType)
}
return NewRtpUnpackContainer(maxSize, protocol)
}

@ -10,7 +10,6 @@ package rtprtcp
import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
)
type RtpUnpackerAac struct {
@ -67,7 +66,7 @@ func (unpacker *RtpUnpackerAac) TryUnpackOne(list *RtpPacketList) (unpackedFlag
return false, 0
}
b := p.Packet.Raw[p.Packet.Header.payloadOffset:]
//nazalog.Debugf("%d, %d, %s", len(pkt.Raw), pkt.Header.timestamp, hex.Dump(b))
//Log.Debugf("%d, %d, %s", len(pkt.Raw), pkt.Header.timestamp, hex.Dump(b))
aus := parseAu(b)
@ -107,7 +106,7 @@ func (unpacker *RtpUnpackerAac) TryUnpackOne(list *RtpPacketList) (unpackedFlag
return false, 0
}
if p.Packet.Header.Timestamp != timestamp {
nazalog.Errorf("fragments of the same access shall have the same timestamp. first=%d, curr=%d",
Log.Errorf("fragments of the same access shall have the same timestamp. first=%d, curr=%d",
timestamp, p.Packet.Header.Timestamp)
return false, 0
}
@ -115,11 +114,11 @@ func (unpacker *RtpUnpackerAac) TryUnpackOne(list *RtpPacketList) (unpackedFlag
b = p.Packet.Raw[p.Packet.Header.payloadOffset:]
aus := parseAu(b)
if len(aus) != 1 {
nazalog.Errorf("shall be a single fragment. len(aus)=%d", len(aus))
Log.Errorf("shall be a single fragment. len(aus)=%d", len(aus))
return false, 0
}
if aus[0].size != totalSize {
nazalog.Errorf("fragments of the same access shall have the same size. first=%d, curr=%d",
Log.Errorf("fragments of the same access shall have the same size. first=%d, curr=%d",
totalSize, aus[0].size)
return false, 0
}
@ -142,7 +141,7 @@ func (unpacker *RtpUnpackerAac) TryUnpackOne(list *RtpPacketList) (unpackedFlag
list.Size -= packetCount
return true, p.Packet.Header.Seq
} else {
nazalog.Errorf("cache size bigger then total size. cacheSize=%d, totalSize=%d",
Log.Errorf("cache size bigger then total size. cacheSize=%d, totalSize=%d",
cacheSize, totalSize)
return false, 0
}
@ -190,7 +189,7 @@ func parseAu(b []byte) (ret []au) {
auSize /= 8
// 注意fragment时auIndex并不可靠。见TestAacCase1
//auIndex := b[pauh+1] & 0x7
//nazalog.Debugf("~ %d %d", auSize, auIndex)
//Log.Debugf("~ %d %d", auSize, auIndex)
ret = append(ret, au{
size: auSize,
@ -203,7 +202,7 @@ func parseAu(b []byte) (ret []au) {
if (nbAuHeaders > 1 && pau != uint32(len(b))) ||
(nbAuHeaders == 1 && pau < uint32(len(b))) {
nazalog.Warnf("rtp packet size invalid. nbAuHeaders=%d, pau=%d, len(b)=%d, auHeadersLength=%d", nbAuHeaders, pau, len(b), auHeadersLength)
Log.Warnf("rtp packet size invalid. nbAuHeaders=%d, pau=%d, len(b)=%d, auHeadersLength=%d", nbAuHeaders, pau, len(b), auHeadersLength)
}
return

@ -13,7 +13,6 @@ import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
type RtpUnpackerAvcHevc struct {
@ -72,7 +71,7 @@ func (unpacker *RtpUnpackerAvcHevc) TryUnpackOne(list *RtpPacketList) (unpackedF
totalSize := 0
for i := 0; i != len(buf); {
if len(buf)-i < 2 {
nazalog.Errorf("invalid STAP-A packet.")
Log.Errorf("invalid STAP-A packet.")
return false, 0
}
naluSize := int(bele.BeUint16(buf[i:]))
@ -179,7 +178,7 @@ func (unpacker *RtpUnpackerAvcHevc) TryUnpackOne(list *RtpPacketList) (unpackedF
return true, p.Packet.Header.Seq
} else {
// 不应该出现其他类型
nazalog.Errorf("invalid position type. position=%d, first=(h=%+v, pos=%d), prev=(h=%+v, pos=%d), p=(h=%+v, pos=%d)",
Log.Errorf("invalid position type. position=%d, first=(h=%+v, pos=%d), prev=(h=%+v, pos=%d), p=(h=%+v, pos=%d)",
p.Packet.positionType, first.Packet.Header, first.Packet.positionType, prev.Packet.Header, prev.Packet.positionType, p.Packet.Header, p.Packet.positionType)
return false, 0
}
@ -190,7 +189,7 @@ func (unpacker *RtpUnpackerAvcHevc) TryUnpackOne(list *RtpPacketList) (unpackedF
case PositionTypeFuaEnd:
// noop
default:
nazalog.Errorf("invalid position. pos=%d", first.Packet.positionType)
Log.Errorf("invalid position. pos=%d", first.Packet.positionType)
}
return false, 0
@ -262,7 +261,7 @@ func calcPositionIfNeededAvc(pkt *RtpPacket) {
} else if outerNaluType == NaluTypeAvcStapa {
pkt.positionType = PositionTypeStapa
} else {
nazalog.Errorf("unknown nalu type. outerNaluType=%d", outerNaluType)
Log.Errorf("unknown nalu type. outerNaluType=%d", outerNaluType)
}
return
@ -342,7 +341,7 @@ func calcPositionIfNeededHevc(pkt *RtpPacket) {
return
default:
// TODO chef: 没有实现 AP 48
nazalog.Errorf("unknown nalu type. outerNaluType=%d(%d), header=%+v, len=%d",
Log.Errorf("unknown nalu type. outerNaluType=%d(%d), header=%+v, len=%d",
b[0], outerNaluType, pkt.Header, len(pkt.Raw))
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save