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 /.idea
/.vscode /.vscode
/SECRET.md
profile.out lal_record
coverage.html
/coverage.txt
logs logs
testdata testdata
/bin /bin
/release /release
/.trash
/playground
/tmp /tmp
/playground
profile.out
coverage.html
/coverage.txt
delay.txt
/.trash
/SECRET.md
/TODO.md /TODO.md
/pre-commit.sh /pre-commit.sh
/conf/self.conf.json /conf/self.conf.json
@ -23,5 +25,3 @@ testdata
*.aac *.aac
*.h264 *.h264
*.flv *.flv
lal_record
delay.txt

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

@ -2,4 +2,4 @@ module github.com/q191201771/lal
go 1.14 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.30.0 h1:tfy1O0QRl3O80mH8PSAd2FhpZ5eL7coQtCF0HzjEO4Y=
github.com/q191201771/naza v0.29.0/go.mod h1:n+dpJjQSh90PxBwxBNuifOwQttywvSIN5TkWSSYCeBk= 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/lal/pkg/aac"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/naza/pkg/assert" "github.com/q191201771/naza/pkg/assert"
) )
@ -90,5 +88,5 @@ func TestMakeAudioDataSeqHeader(t *testing.T) {
func TestSequenceHeaderContext(t *testing.T) { func TestSequenceHeaderContext(t *testing.T) {
var shCtx aac.SequenceHeaderContext var shCtx aac.SequenceHeaderContext
shCtx.Unpack(goldenSh) 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 ( import (
"io" "io"
"github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazaerrors" "github.com/q191201771/naza/pkg/nazaerrors"
@ -36,7 +34,7 @@ var (
NaluStartCode3 = []byte{0x0, 0x0, 0x1} NaluStartCode3 = []byte{0x0, 0x0, 0x1}
NaluStartCode4 = []byte{0x0, 0x0, 0x0, 0x1} NaluStartCode4 = []byte{0x0, 0x0, 0x0, 0x1}
// aud nalu // AudNalu aud nalu
AudNalu = []byte{0x00, 0x00, 0x00, 0x01, 0x09, 0xf0} AudNalu = []byte{0x00, 0x00, 0x00, 0x01, 0x09, 0xf0}
) )
@ -498,7 +496,7 @@ func IterateNaluAvcc(nals []byte, handler func(nal []byte)) error {
// length为0的直接过滤掉 // length为0的直接过滤掉
if 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 continue
} }
handler(nals[pos:epos]) handler(nals[pos:epos])
@ -507,7 +505,7 @@ func IterateNaluAvcc(nals []byte, handler func(nal []byte)) error {
// 最后一个 // 最后一个
if 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 continue
} }
handler(nals[pos:epos]) handler(nals[pos:epos])

@ -18,8 +18,6 @@ import (
"github.com/q191201771/naza/pkg/nazabits" "github.com/q191201771/naza/pkg/nazabits"
"github.com/q191201771/naza/pkg/nazaerrors" "github.com/q191201771/naza/pkg/nazaerrors"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/lal/pkg/avc" "github.com/q191201771/lal/pkg/avc"
"github.com/q191201771/naza/pkg/assert" "github.com/q191201771/naza/pkg/assert"
@ -36,7 +34,7 @@ func TestParseNaluType(t *testing.T) {
assert.Equal(t, out, actual) assert.Equal(t, out, actual)
b := avc.ParseNaluTypeReadable(in) 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) b, err := avc.ParseSliceTypeReadable(item.in)
assert.Equal(t, nil, err) 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) err = avc.ParseSps(nil, &ctx)
assert.Equal(t, true, nazaerrors.Is(err, nazabits.ErrNazaBits)) assert.Equal(t, true, nazaerrors.Is(err, nazabits.ErrNazaBits))
assert.IsNotNil(t, err) 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) err = avc.ParseSps(goldenSps2, &ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)

@ -18,14 +18,13 @@ import (
"github.com/q191201771/naza/pkg/bele" "github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazabits" "github.com/q191201771/naza/pkg/nazabits"
"github.com/q191201771/naza/pkg/nazabytes" "github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazalog"
) )
func ParseSps(payload []byte, ctx *Context) error { func ParseSps(payload []byte, ctx *Context) error {
br := nazabits.NewBitReader(payload) br := nazabits.NewBitReader(payload)
var sps Sps var sps Sps
if err := parseSpsBasic(&br, &sps); err != nil { 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 return err
} }
ctx.Profile = sps.ProfileIdc ctx.Profile = sps.ProfileIdc
@ -37,16 +36,16 @@ func ParseSps(payload []byte, ctx *Context) error {
if err := parseSpsGamma(&br, &sps); err != nil { if err := parseSpsGamma(&br, &sps); err != nil {
// 注意这里不将错误返回给上层因为可能是Beta自身解析的问题 // 注意这里不将错误返回给上层因为可能是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.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 ctx.Height = (2-uint32(sps.FrameMbsOnlyFlag))*(sps.PicHeightInMapUnitsMinusOne+1)*16 - (sps.FrameCropTopOffset+sps.FrameCropBottomOffset)*2
return nil return nil
} }
// 尝试解析PPS所有字段实验中请勿直接使用该函数 // TryParsePps 尝试解析PPS所有字段实验中请勿直接使用该函数
func TryParsePps(payload []byte) error { func TryParsePps(payload []byte) error {
// ISO-14496-10.pdf // ISO-14496-10.pdf
// 7.3.2.2 Picture parameter set RBSP syntax // 7.3.2.2 Picture parameter set RBSP syntax
@ -55,7 +54,7 @@ func TryParsePps(payload []byte) error {
return nil return nil
} }
// 尝试解析SeqHeader所有字段实验中请勿直接使用该函数 // TryParseSeqHeader 尝试解析SeqHeader所有字段实验中请勿直接使用该函数
// //
// @param <payload> rtmp message的payload部分或者flv tag的payload部分 // @param <payload> rtmp message的payload部分或者flv tag的payload部分
// 注意包含了头部2字节类型以及3字节的cts // 注意包含了头部2字节类型以及3字节的cts
@ -94,7 +93,7 @@ func TryParseSeqHeader(payload []byte) error {
b, err = br.ReadBytes(2) b, err = br.ReadBytes(2)
dcr.PpsLength = bele.BeUint16(b) dcr.PpsLength = bele.BeUint16(b)
nazalog.Debugf("%+v", dcr) Log.Debugf("%+v", dcr)
// 5 + 5 + 1 + 2 // 5 + 5 + 1 + 2
var ctx Context 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" "time"
"github.com/q191201771/naza/pkg/bininfo" "github.com/q191201771/naza/pkg/bininfo"
"github.com/q191201771/naza/pkg/nazalog"
) )
// base包提供被其他多个package依赖的基础内容自身不依赖任何package // base包提供被其他多个package依赖的基础内容自身不依赖任何package
@ -23,9 +22,7 @@ import (
var startTime string var startTime string
// ReadableNowTime // ReadableNowTime 当前时间,可读字符串形式
//
// TODO(chef): refactor 使用ReadableNowTime
// //
func ReadableNowTime() string { func ReadableNowTime() string {
return time.Now().Format("2006-01-02 15:04:05.999") return time.Now().Format("2006-01-02 15:04:05.999")
@ -33,13 +30,13 @@ func ReadableNowTime() string {
func LogoutStartInfo() { func LogoutStartInfo() {
dir, _ := os.Getwd() dir, _ := os.Getwd()
nazalog.Infof(" start: %s", startTime) Log.Infof(" start: %s", startTime)
nazalog.Infof(" wd: %s", dir) Log.Infof(" wd: %s", dir)
nazalog.Infof(" args: %s", strings.Join(os.Args, " ")) Log.Infof(" args: %s", strings.Join(os.Args, " "))
nazalog.Infof(" bininfo: %s", bininfo.StringifySingleLine()) Log.Infof(" bininfo: %s", bininfo.StringifySingleLine())
nazalog.Infof(" version: %s", LalFullInfo) Log.Infof(" version: %s", LalFullInfo)
nazalog.Infof(" github: %s", LalGithubSite) Log.Infof(" github: %s", LalGithubSite)
nazalog.Infof(" doc: %s", LalDocSite) Log.Infof(" doc: %s", LalDocSite)
} }
func init() { func init() {

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

@ -11,7 +11,6 @@ package base
import ( import (
"net" "net"
"strings" "strings"
"time"
"github.com/q191201771/naza/pkg/connection" "github.com/q191201771/naza/pkg/connection"
) )
@ -43,7 +42,7 @@ func NewHttpSubSession(option HttpSubSessionOption) *HttpSubSession {
stat: StatSession{ stat: StatSession{
Protocol: option.Protocol, Protocol: option.Protocol,
SessionId: option.Uk, SessionId: option.Uk,
StartTime: time.Now().Format("2006-01-02 15:04:05.999"), StartTime: ReadableNowTime(),
RemoteAddr: option.Conn.RemoteAddr().String(), RemoteAddr: option.Conn.RemoteAddr().String(),
}, },
} }
@ -118,7 +117,7 @@ func (session *HttpSubSession) StreamName() string {
case ProtocolHttpts: case ProtocolHttpts:
suffix = ".ts" suffix = ".ts"
default: 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) return strings.TrimSuffix(session.UrlCtx.LastItemOfPath, suffix)
} }

@ -15,6 +15,7 @@ import (
) )
type LogDump struct { type LogDump struct {
log nazalog.Logger
debugMaxNum int debugMaxNum int
debugCount int debugCount int
@ -24,14 +25,15 @@ type LogDump struct {
// //
// @param debugMaxNum: 日志最小级别为debug时使用debug打印日志次数的阈值 // @param debugMaxNum: 日志最小级别为debug时使用debug打印日志次数的阈值
// //
func NewLogDump(debugMaxNum int) LogDump { func NewLogDump(log nazalog.Logger, debugMaxNum int) LogDump {
return LogDump{ return LogDump{
log: log,
debugMaxNum: debugMaxNum, debugMaxNum: debugMaxNum,
} }
} }
func (ld *LogDump) ShouldDump() bool { func (ld *LogDump) ShouldDump() bool {
switch nazalog.GetOption().Level { switch ld.log.GetOption().Level {
case nazalog.LevelTrace: case nazalog.LevelTrace:
return true return true
case nazalog.LevelDebug: case nazalog.LevelDebug:
@ -52,5 +54,5 @@ func (ld *LogDump) ShouldDump() bool {
// 这个hex.Dump调用 // 这个hex.Dump调用
// //
func (ld *LogDump) Outf(format string, v ...interface{}) { 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 ( import (
"net" "net"
"github.com/q191201771/naza/pkg/nazalog"
) )
// TODO(chef): feat 通过时间戳(目前是数据大小)来设定合并阈值 // TODO(chef): feat 通过时间戳(目前是数据大小)来设定合并阈值
@ -47,7 +45,7 @@ func NewMergeWriter(onWritev OnWritev, size int) *MergeWriter {
// 注意,函数调用结束后,`b`内存块会被内部持有 // 注意,函数调用结束后,`b`内存块会被内部持有
// //
func (w *MergeWriter) Write(b []byte) { 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.bs = append(w.bs, b)
w.currSize += len(b) w.currSize += len(b)
if w.currSize >= w.size { if w.currSize >= w.size {
@ -58,7 +56,7 @@ func (w *MergeWriter) Write(b []byte) {
// Flush 强制将内部缓冲的数据全部回调排空 // Flush 强制将内部缓冲的数据全部回调排空
// //
func (w *MergeWriter) Flush() { func (w *MergeWriter) Flush() {
nazalog.Debugf("[%p] MergeWriter::Flush.", w) Log.Debugf("[%p] MergeWriter::Flush.", w)
if w.currSize > 0 { if w.currSize > 0 {
w.flush() w.flush()
} }
@ -74,7 +72,7 @@ func (w *MergeWriter) flush() {
n += len(v) n += len(v)
ns = append(ns, 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.onWritev(w.bs)
w.currSize = 0 w.currSize = 0
w.bs = nil w.bs = nil

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

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

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

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

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

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

@ -16,7 +16,7 @@ import (
"github.com/q191201771/naza/pkg/bele" "github.com/q191201771/naza/pkg/bele"
) )
// The WebSocket Protocol // WsOpcode The WebSocket Protocol
// https://tools.ietf.org/html/rfc6455 // https://tools.ietf.org/html/rfc6455
// //
// 0 1 2 3 // 0 1 2 3
@ -59,7 +59,8 @@ const (
Wso_Continuous WsOpcode = iota //连续消息片断 Wso_Continuous WsOpcode = iota //连续消息片断
Wso_Text //文本消息片断, Wso_Text //文本消息片断,
Wso_Binary //二进制消息片断, Wso_Binary //二进制消息片断,
//非控制消息片断保留的操作码,
// Wso_Rsv3 非控制消息片断保留的操作码,
Wso_Rsv3 Wso_Rsv3
Wso_Rsv4 Wso_Rsv4
Wso_Rsv5 Wso_Rsv5
@ -68,7 +69,8 @@ const (
Wso_Close //连接关闭, Wso_Close //连接关闭,
Wso_Ping //心跳检查的ping, Wso_Ping //心跳检查的ping,
Wso_Pong //心跳检查的pong, Wso_Pong //心跳检查的pong,
//为将来的控制消息片断的保留操作码
// Wso_RsvB 为将来的控制消息片断的保留操作码
Wso_RsvB Wso_RsvB
Wso_RsvC Wso_RsvC
Wso_RsvD 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 { if payload[0] != 0x1c || payload[1] != 0x00 || payload[2] != 0 || payload[3] != 0 || payload[4] != 0 {
return nil, nil, nil, nazaerrors.Wrap(base.ErrHevc) return nil, nil, nil, nazaerrors.Wrap(base.ErrHevc)
} }
//nazalog.Debugf("%s", hex.Dump(payload)) //Log.Debugf("%s", hex.Dump(payload))
if len(payload) < 33 { if len(payload) < 33 {
return nil, nil, nil, nazaerrors.Wrap(base.ErrHevc) 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 return content, nil
} }
// @param content 传入m3u8文件内容 // CalcM3u8Duration @param content 传入m3u8文件内容
// //
// @return durationSec m3u8中所有ts的时间总和。注意使用的是m3u8文件中描述的ts时间而不是读取ts文件中实际音视频数据的时间。 // @return durationSec m3u8中所有ts的时间总和。注意使用的是m3u8文件中描述的ts时间而不是读取ts文件中实际音视频数据的时间。
// //

@ -17,8 +17,6 @@ import (
"github.com/q191201771/lal/pkg/mpegts" "github.com/q191201771/lal/pkg/mpegts"
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
) )
// TODO chef: 转换TS流的功能通过回调供httpts使用也放在了Muxer中好处是hls和httpts可以共用一份TS流。 // TODO chef: 转换TS流的功能通过回调供httpts使用也放在了Muxer中好处是hls和httpts可以共用一份TS流。
@ -27,8 +25,11 @@ import (
type MuxerObserver interface { type MuxerObserver interface {
OnPatPmt(b []byte) OnPatPmt(b []byte)
// @param rawFrame TS流回调结束后内部不再使用该内存块 // OnTsPackets
// @param boundary 新的TS流接收者应该从该标志为true时开始发送数据 //
// @param rawFrame: TS流回调结束后内部不再使用该内存块
//
// @param boundary: 新的TS流接收者应该从该标志为true时开始发送数据
// //
OnTsPackets(rawFrame []byte, boundary bool) OnTsPackets(rawFrame []byte, boundary bool)
} }
@ -70,10 +71,14 @@ type Muxer struct {
observer MuxerObserver observer MuxerObserver
fragment Fragment fragment Fragment
opened bool
videoCc uint8 videoCc uint8
audioCc uint8 audioCc uint8
// 初始值为false调用openFragment时设置为true调用closeFragment时设置为false
// 整个对象关闭时设置为false
// 中途切换Fragment时调用close后会立即调用open
opened bool
fragTs uint64 // 新建立fragment时的时间戳毫秒 * 90 fragTs uint64 // 新建立fragment时的时间戳毫秒 * 90
recordMaxFragDuration float64 recordMaxFragDuration float64
@ -120,29 +125,31 @@ func NewMuxer(streamName string, enable bool, config *MuxerConfig, observer Muxe
m.makeFrags() m.makeFrags()
streamer := NewStreamer(m) streamer := NewStreamer(m)
m.streamer = streamer 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 return m
} }
func (m *Muxer) Start() { func (m *Muxer) Start() {
nazalog.Infof("[%s] start hls muxer.", m.UniqueKey) Log.Infof("[%s] start hls muxer.", m.UniqueKey)
m.ensureDir() m.ensureDir()
} }
func (m *Muxer) Dispose() { 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() m.streamer.FlushAudio()
if err := m.closeFragment(true); err != nil { 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) { func (m *Muxer) FeedRtmpMessage(msg base.RtmpMsg) {
m.streamer.FeedRtmpMessage(msg) m.streamer.FeedRtmpMessage(msg)
} }
// ----- implement StreamerObserver of Streamer ------------------------------------------------------------------------
func (m *Muxer) OnPatPmt(b []byte) { func (m *Muxer) OnPatPmt(b []byte) {
m.patpmt = b m.patpmt = b
if m.observer != nil { if m.observer != nil {
@ -150,50 +157,38 @@ func (m *Muxer) OnPatPmt(b []byte) {
} }
} }
func (m *Muxer) OnFrame(streamer *Streamer, frame *mpegts.Frame) { func (m *Muxer) OnFrame(streamer *Streamer, frame *mpegts.Frame, boundary bool) {
var boundary bool
var packets []byte var packets []byte
if frame.Sid == mpegts.StreamIdAudio { if frame.Sid == mpegts.StreamIdAudio {
// 为了考虑没有视频的情况也能切片所以这里判断spspps为空时也建议生成fragment // TODO(chef): 为什么音频用pts视频用dts
boundary = !streamer.VideoSeqHeaderCached()
if err := m.updateFragment(frame.Pts, boundary); err != nil { 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 return
} }
// TODO(chef): 有updateFragment的返回值判断这里的判断可以考虑删除
if !m.opened { 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 return
} }
//Log.Debugf("[%s] WriteFrame A. dts=%d, len=%d", m.UniqueKey, frame.DTS, len(frame.Raw))
//nazalog.Debugf("[%s] WriteFrame A. dts=%d, len=%d", m.UniqueKey, frame.DTS, len(frame.Raw))
} else { } 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 { 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 return
} }
// TODO(chef): 有updateFragment的返回值判断这里的判断可以考虑删除
if !m.opened { 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 return
} }
//Log.Debugf("[%s] WriteFrame V. dts=%d, len=%d", m.UniqueKey, frame.Dts, len(frame.Raw))
//nazalog.Debugf("[%s] WriteFrame V. dts=%d, len=%d", m.UniqueKey, frame.Dts, len(frame.Raw))
} }
mpegts.PackTsPacket(frame, func(packet []byte) { mpegts.PackTsPacket(frame, func(packet []byte) {
if m.enable { if m.enable {
if err := m.fragment.WriteFile(packet); err != nil { 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 return
} }
} }
@ -206,13 +201,19 @@ func (m *Muxer) OnFrame(streamer *Streamer, frame *mpegts.Frame) {
} }
} }
// ---------------------------------------------------------------------------------------------------------------------
func (m *Muxer) OutPath() string { func (m *Muxer) OutPath() string {
return m.outPath return m.outPath
} }
// 决定是否开启新的TS切片文件注意可能已经有TS切片也可能没有这是第一个切片 // ---------------------------------------------------------------------------------------------------------------------
// updateFragment 决定是否开启新的TS切片文件注意可能已经有TS切片也可能没有这是第一个切片
//
// @param boundary: 调用方认为可能是开启新TS切片的时间点
// //
// @param boundary 调用方认为可能是开启新TS切片的时间点 // @return: 理论上,只有文件操作失败才会返回错误
// //
func (m *Muxer) updateFragment(ts uint64, boundary bool) error { func (m *Muxer) updateFragment(ts uint64, boundary bool) error {
discont := true discont := true
@ -231,7 +232,7 @@ func (m *Muxer) updateFragment(ts uint64, boundary bool) error {
// //
maxfraglen := uint64(m.config.FragmentDurationMs * 90 * 10) maxfraglen := uint64(m.config.FragmentDurationMs * 90 * 10)
if (ts > m.fragTs && ts-m.fragTs > maxfraglen) || (m.fragTs > ts && m.fragTs-ts > negMaxfraglen) { 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 { if err := m.closeFragment(false); err != nil {
return err return err
@ -279,7 +280,11 @@ func (m *Muxer) updateFragment(ts uint64, boundary bool) error {
return nil 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 { func (m *Muxer) openFragment(ts uint64, discont bool) error {
if m.opened { if m.opened {
@ -314,6 +319,10 @@ func (m *Muxer) openFragment(ts uint64, discont bool) error {
return nil return nil
} }
// closeFragment
//
// @return: 理论上,只有文件操作失败才会返回错误
//
func (m *Muxer) closeFragment(isLast bool) error { func (m *Muxer) closeFragment(isLast bool) error {
if !m.opened { if !m.opened {
// 注意首次调用closeFragment时有可能opened为false // 注意首次调用closeFragment时有可能opened为false
@ -343,7 +352,7 @@ func (m *Muxer) closeFragment(isLast bool) error {
if frag.filename != "" { if frag.filename != "" {
filenameWithPath := PathStrategy.GetTsFileNameWithPath(m.outPath, frag.filename) filenameWithPath := PathStrategy.GetTsFileNameWithPath(m.outPath, frag.filename)
if err := fslCtx.Remove(filenameWithPath); err != nil { 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 = bytes.TrimSuffix(content, []byte("#EXT-X-ENDLIST\n"))
content, err = updateTargetDurationInM3u8(content, int(m.recordMaxFragDuration)) content, err = updateTargetDurationInM3u8(content, int(m.recordMaxFragDuration))
if err != nil { 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 return
} }
@ -400,7 +409,7 @@ func (m *Muxer) writeRecordPlaylist() {
} }
if err := writeM3u8File(content, m.recordPlayListFilename, m.recordPlayListFilenameBak); err != nil { 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 { 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 return
} }
//err := fslCtx.RemoveAll(m.outPath) //err := fslCtx.RemoveAll(m.outPath)
//nazalog.Assert(nil, err) //Log.Assert(nil, err)
err := fslCtx.MkdirAll(m.outPath, 0777) 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 GetRequestInfo(urlCtx base.UrlContext, rootOutPath string) RequestInfo
} }
// 落盘策略 // IPathWriteStrategy 落盘策略
type IPathWriteStrategy interface { type IPathWriteStrategy interface {
// 获取单个流对应的文件根路径 // GetMuxerOutPath 获取单个流对应的文件根路径
GetMuxerOutPath(rootOutPath string, streamName string) string GetMuxerOutPath(rootOutPath string, streamName string) string
// 获取单个流对应的m3u8文件路径 // GetLiveM3u8FileName 获取单个流对应的m3u8文件路径
// //
// @param outPath: func GetMuxerOutPath的结果 // @param outPath: func GetMuxerOutPath的结果
GetLiveM3u8FileName(outPath string, streamName string) string GetLiveM3u8FileName(outPath string, streamName string) string
// 获取单个流对应的record类型的m3u8文件路径 // GetRecordM3u8FileName 获取单个流对应的record类型的m3u8文件路径
// //
// live m3u8和record m3u8的区别 // live m3u8和record m3u8的区别
// live记录的是当前最近的可播放内容record记录的是从流开始时的可播放内容 // live记录的是当前最近的可播放内容record记录的是从流开始时的可播放内容
@ -61,12 +61,12 @@ type IPathWriteStrategy interface {
// @param outPath: func GetMuxerOutPath的结果 // @param outPath: func GetMuxerOutPath的结果
GetRecordM3u8FileName(outPath string, streamName string) string GetRecordM3u8FileName(outPath string, streamName string) string
// 获取单个流对应的ts文件路径 // GetTsFileNameWithPath 获取单个流对应的ts文件路径
// //
// @param outPath: func GetMuxerOutPath的结果 // @param outPath: func GetMuxerOutPath的结果
GetTsFileNameWithPath(outPath string, fileName string) string GetTsFileNameWithPath(outPath string, fileName string) string
// ts文件名的生成策略 // GetTsFileName ts文件名的生成策略
GetTsFileName(streamName string, index int, timestamp int) string GetTsFileName(streamName string, index int, timestamp int) string
} }
@ -77,7 +77,7 @@ const (
recordM3u8FileName = "record.m3u8" recordM3u8FileName = "record.m3u8"
) )
// 默认的路由,落盘策略 // DefaultPathStrategy 默认的路由,落盘策略
// //
// 每个流在<rootPath>下以流名称生成一个子目录,目录下包含: // 每个流在<rootPath>下以流名称生成一个子目录,目录下包含:
// //
@ -136,7 +136,7 @@ func (dps *DefaultPathStrategy) GetRequestInfo(urlCtx base.UrlContext, rootOutPa
return return
} }
// <rootOutPath>/<streamName> // GetMuxerOutPath <rootOutPath>/<streamName>
func (*DefaultPathStrategy) GetMuxerOutPath(rootOutPath string, streamName string) string { func (*DefaultPathStrategy) GetMuxerOutPath(rootOutPath string, streamName string) string {
return filepath.Join(rootOutPath, streamName) return filepath.Join(rootOutPath, streamName)
} }

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

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

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

@ -18,23 +18,24 @@ import (
"github.com/q191201771/lal/pkg/mpegts" "github.com/q191201771/lal/pkg/mpegts"
"github.com/q191201771/naza/pkg/bele" "github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazabytes" "github.com/q191201771/naza/pkg/nazabytes"
"github.com/q191201771/naza/pkg/nazalog"
) )
type StreamerObserver interface { type StreamerObserver interface {
// @param b const只读内存块上层可以持有但是不允许修改 // OnPatPmt @param b const只读内存块上层可以持有但是不允许修改
OnPatPmt(b []byte) OnPatPmt(b []byte)
// OnFrame
//
// @param streamer: 供上层获取streamer内部的一些状态比如spspps是否已缓存音频缓存队列是否有数据等 // @param streamer: 供上层获取streamer内部的一些状态比如spspps是否已缓存音频缓存队列是否有数据等
// //
// @param frame: 各字段含义见mpegts.Frame结构体定义 // @param frame: 各字段含义见 mpegts.Frame 结构体定义
// frame.CC 注意回调结束后Streamer会保存frame.CC上层在TS打包完成后可通过frame.CC将cc值传递给Streamer // frame.CC 注意回调结束后Streamer会保存frame.CC上层在TS打包完成后可通过frame.CC将cc值传递给Streamer
// frame.Raw 回调结束后,这块内存可能会被内部重复使用 // frame.Raw 回调结束后,这块内存可能会被内部重复使用
// //
OnFrame(streamer *Streamer, frame *mpegts.Frame) OnFrame(streamer *Streamer, frame *mpegts.Frame, boundary bool)
} }
// 输入rtmp流回调转封装成Annexb格式的流 // Streamer 输入rtmp流回调转封装成Annexb格式的流
type Streamer struct { type Streamer struct {
UniqueKey string UniqueKey string
@ -47,6 +48,8 @@ type Streamer struct {
audioCacheFirstFramePts uint64 // audioCacheFrames中第一个音频帧的时间戳 TODO chef: rename to DTS audioCacheFirstFramePts uint64 // audioCacheFrames中第一个音频帧的时间戳 TODO chef: rename to DTS
audioCc uint8 audioCc uint8
videoCc uint8 videoCc uint8
opened bool
} }
func NewStreamer(observer StreamerObserver) *Streamer { func NewStreamer(observer StreamerObserver) *Streamer {
@ -62,13 +65,15 @@ func NewStreamer(observer StreamerObserver) *Streamer {
return streamer return streamer
} }
// @param msg msg.Payload 调用结束后,函数内部不会持有这块内存 // FeedRtmpMessage @param msg msg.Payload 调用结束后,函数内部不会持有这块内存
// //
// TODO chef: 可以考虑数据有问题时,返回给上层,直接主动关闭输入流的连接 // TODO chef: 可以考虑数据有问题时,返回给上层,直接主动关闭输入流的连接
func (s *Streamer) FeedRtmpMessage(msg base.RtmpMsg) { func (s *Streamer) FeedRtmpMessage(msg base.RtmpMsg) {
s.calcFragmentHeaderQueue.Push(msg) s.calcFragmentHeaderQueue.Push(msg)
} }
// ----- implement IQueueObserver of Queue -----------------------------------------------------------------------------
func (s *Streamer) OnPatPmt(b []byte) { func (s *Streamer) OnPatPmt(b []byte) {
s.observer.OnPatPmt(b) 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 { func (s *Streamer) AudioSeqHeaderCached() bool {
return s.ascCtx != nil return s.ascCtx != nil
} }
@ -94,12 +127,14 @@ func (s *Streamer) AudioCacheEmpty() bool {
return s.audioCacheFrames == nil return s.audioCacheFrames == nil
} }
// ----- private -------------------------------------------------------------------------------------------------------
func (s *Streamer) feedVideo(msg base.RtmpMsg) { func (s *Streamer) feedVideo(msg base.RtmpMsg) {
// 注意有一种情况是msg.Payload为 27 02 00 00 00 // 注意有一种情况是msg.Payload为 27 02 00 00 00
// 此时打印错误并返回也不影响 // 此时打印错误并返回也不影响
// //
if len(msg.Payload) <= 5 { 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 return
} }
@ -114,12 +149,12 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
var err error var err error
if msg.IsAvcKeySeqHeader() { if msg.IsAvcKeySeqHeader() {
if s.spspps, err = avc.SpsPpsSeqHeader2Annexb(msg.Payload); err != nil { 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 return
} else if msg.IsHevcKeySeqHeader() { } else if msg.IsHevcKeySeqHeader() {
if s.spspps, err = hevc.VpsSpsPpsSeqHeader2Annexb(msg.Payload); err != nil { 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 return
} }
@ -134,7 +169,7 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
// msg中可能有多个NALU逐个获取 // msg中可能有多个NALU逐个获取
nals, err := avc.SplitNaluAvcc(msg.Payload[5:]) nals, err := avc.SplitNaluAvcc(msg.Payload[5:])
if err != nil { 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 return
} }
for _, nal := range nals { for _, nal := range nals {
@ -146,7 +181,7 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
nalType = hevc.ParseNaluType(nal[0]) 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 aud
// sps pps前面已经缓存过了后面有自己的写入逻辑 // sps pps前面已经缓存过了后面有自己的写入逻辑
@ -176,7 +211,7 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
case avc.NaluTypeIdrSlice: case avc.NaluTypeIdrSlice:
if !spsppsSent { if !spsppsSent {
if out, err = s.appendSpsPps(out); err != nil { 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 return
} }
} }
@ -190,7 +225,7 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
case hevc.NaluTypeSliceIdr, hevc.NaluTypeSliceIdrNlp, hevc.NaluTypeSliceCranut: case hevc.NaluTypeSliceIdr, hevc.NaluTypeSliceIdrNlp, hevc.NaluTypeSliceCranut:
if !spsppsSent { if !spsppsSent {
if out, err = s.appendSpsPps(out); err != nil { 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 return
} }
} }
@ -227,30 +262,30 @@ func (s *Streamer) feedVideo(msg base.RtmpMsg) {
frame.Pid = mpegts.PidVideo frame.Pid = mpegts.PidVideo
frame.Sid = mpegts.StreamIdVideo frame.Sid = mpegts.StreamIdVideo
s.observer.OnFrame(s, &frame) s.onFrame(&frame)
s.videoCc = frame.Cc s.videoCc = frame.Cc
} }
func (s *Streamer) feedAudio(msg base.RtmpMsg) { func (s *Streamer) feedAudio(msg base.RtmpMsg) {
if len(msg.Payload) < 3 { 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 return
} }
if msg.Payload[0]>>4 != base.RtmpSoundFormatAac { if msg.Payload[0]>>4 != base.RtmpSoundFormatAac {
return 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 msg.Payload[1] == base.RtmpAacPacketTypeSeqHeader {
if err := s.cacheAacSeqHeader(msg); err != nil { 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 return
} }
if !s.AudioSeqHeaderCached() { 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 return
} }
@ -269,29 +304,6 @@ func (s *Streamer) feedAudio(msg base.RtmpMsg) {
s.audioCacheFrames = append(s.audioCacheFrames, msg.Payload[2:]...) 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 { func (s *Streamer) cacheAacSeqHeader(msg base.RtmpMsg) error {
var err error var err error
s.ascCtx, err = aac.NewAscContext(msg.Payload[2:]) 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...) out = append(out, s.spspps...)
return out, nil 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 package hls
import "github.com/q191201771/naza/pkg/mock" import (
"github.com/q191201771/naza/pkg/mock"
"github.com/q191201771/naza/pkg/nazalog"
)
var ( var (
PathStrategy IPathStrategy = &DefaultPathStrategy{} PathStrategy IPathStrategy = &DefaultPathStrategy{}
Clock = mock.NewStdClock() Clock = mock.NewStdClock()
Log = nazalog.GetGlobalLogger()
) )
var ( var (

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

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

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

@ -13,8 +13,6 @@ import (
"os" "os"
"testing" "testing"
"github.com/q191201771/naza/pkg/nazalog"
"github.com/q191201771/lal/pkg/httpflv" "github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/naza/pkg/assert" "github.com/q191201771/naza/pkg/assert"
"github.com/q191201771/naza/pkg/mock" "github.com/q191201771/naza/pkg/mock"
@ -30,7 +28,7 @@ func TestHttpflv(t *testing.T) {
func TestFlvFilePump(t *testing.T) { func TestFlvFilePump(t *testing.T) {
const flvFile = "../../testdata/test.flv" const flvFile = "../../testdata/test.flv"
if _, err := os.Lstat(flvFile); err != nil { 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 return
} }

@ -14,8 +14,6 @@ import (
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/connection" "github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/naza/pkg/nazalog"
) )
var flvHttpResponseHeader []byte var flvHttpResponseHeader []byte
@ -44,7 +42,7 @@ func NewSubSession(conn net.Conn, urlCtx base.UrlContext, isWebSocket bool, webs
IsFresh: true, IsFresh: true,
ShouldWaitVideoKeyFrame: 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 return s
} }
@ -57,19 +55,19 @@ func (session *SubSession) RunLoop() error {
} }
func (session *SubSession) Dispose() 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() return session.core.Dispose()
} }
// --------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------
func (session *SubSession) WriteHttpResponseHeader() { 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) session.core.WriteHttpResponseHeader(flvHttpResponseHeader)
} }
func (session *SubSession) WriteFlvHeader() { 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) session.core.Write(FlvHeader)
} }

@ -26,7 +26,7 @@ type Tag struct {
Raw []byte // 结构为 (11字节的 tag header) + (body) + (4字节的 prev tag size) 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 { func (tag *Tag) Payload() []byte {
return tag.Raw[TagHeaderSize : len(tag.Raw)-PrevTagSizeFieldSize] 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 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 { func (tag *Tag) IsVideoKeySeqHeader() bool {
return tag.IsAvcKeySeqHeader() || tag.IsHevcKeySeqHeader() 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 return tag.Header.Type == TagTypeVideo && tag.Raw[TagHeaderSize] == HevcKeyFrame && tag.Raw[TagHeaderSize+1] == HevcPacketTypeNalu
} }
// AVC或HEVC的关键帧 // IsVideoKeyNalu AVC或HEVC的关键帧
func (tag *Tag) IsVideoKeyNalu() bool { func (tag *Tag) IsVideoKeyNalu() bool {
return tag.IsAvcKeyNalu() || tag.IsHevcKeyNalu() return tag.IsAvcKeyNalu() || tag.IsHevcKeyNalu()
} }
@ -87,7 +87,7 @@ func (tag *Tag) ModTagTimestamp(timestamp uint32) {
tag.Raw[7] = byte(timestamp >> 24) 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 { func PackHttpflvTag(t uint8, timestamp uint32, in []byte) []byte {
out := make([]byte, TagHeaderSize+len(in)+PrevTagSizeFieldSize) out := make([]byte, TagHeaderSize+len(in)+PrevTagSizeFieldSize)
out[0] = t out[0] = t

@ -8,10 +8,14 @@
package httpflv package httpflv
import "github.com/q191201771/naza/pkg/nazalog"
var ( var (
SubSessionWriteChanSize = 1024 // SubSession发送数据时channel的大小 SubSessionWriteChanSize = 1024 // SubSession发送数据时channel的大小
SubSessionWriteTimeoutMs = 10000 SubSessionWriteTimeoutMs = 10000
FlvHeader = []byte{0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00} FlvHeader = []byte{0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00}
Log = nazalog.GetGlobalLogger()
) )
var readBufSize = 256 //16384 // ClientPullSession读取数据时 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/lal/pkg/base"
"github.com/q191201771/naza/pkg/connection" "github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/naza/pkg/nazalog"
) )
var tsHttpResponseHeader []byte var tsHttpResponseHeader []byte
@ -40,7 +39,7 @@ func NewSubSession(conn net.Conn, urlCtx base.UrlContext, isWebSocket bool, webs
}), }),
true, 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 return s
} }
@ -53,14 +52,14 @@ func (session *SubSession) RunLoop() error {
} }
func (session *SubSession) Dispose() 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() return session.core.Dispose()
} }
// --------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------
func (session *SubSession) WriteHttpResponseHeader() { 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) session.core.WriteHttpResponseHeader(tsHttpResponseHeader)
} }

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

@ -17,6 +17,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/q191201771/lal/pkg/httpts"
"github.com/q191201771/naza/pkg/filebatch"
"github.com/q191201771/lal/pkg/hls" "github.com/q191201771/lal/pkg/hls"
"github.com/q191201771/naza/pkg/mock" "github.com/q191201771/naza/pkg/mock"
@ -30,7 +33,6 @@ import (
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/filebatch"
"github.com/q191201771/naza/pkg/nazamd5" "github.com/q191201771/naza/pkg/nazamd5"
"github.com/q191201771/lal/pkg/httpflv" "github.com/q191201771/lal/pkg/httpflv"
@ -38,7 +40,6 @@ import (
"github.com/q191201771/lal/pkg/rtmp" "github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/assert" "github.com/q191201771/naza/pkg/assert"
"github.com/q191201771/naza/pkg/nazaatomic" "github.com/q191201771/naza/pkg/nazaatomic"
"github.com/q191201771/naza/pkg/nazalog"
) )
// 开启了一个lalserver // 开启了一个lalserver
@ -54,17 +55,27 @@ import (
var ( var (
tt *testing.T tt *testing.T
confFile = "../../testdata/lalserver.conf.json" confFile = "../../testdata/lalserver.conf.json"
rFlvFileName = "../../testdata/test.flv" rFlvFileName = "../../testdata/test.flv"
wFlvPullFileName = "../../testdata/flvpull.flv" wRtmpPullFileName = "../../testdata/rtmppull.flv"
wRtmpPullFileName = "../../testdata/rtmppull.flv" wFlvPullFileName = "../../testdata/flvpull.flv"
wRtspPullFileName = "../../testdata/rtsppull.flv" wPlaylistM3u8FileName string
wRecordM3u8FileName string
wHlsTsFilePath string
//wRtspPullFileName = "../../testdata/rtsppull.flv"
pushUrl string pushUrl string
httpflvPullUrl string httpflvPullUrl string
httptsPullUrl string
rtmpPullUrl string rtmpPullUrl string
rtspPullUrl string rtspPullUrl string
fileTagCount int
httpflvPullTagCount nazaatomic.Uint32
rtmpPullTagCount nazaatomic.Uint32
rtspSdpCtx sdp.LogicContext
rtspPullAvPacketCount nazaatomic.Uint32
httpFlvWriter httpflv.FlvFileWriter httpFlvWriter httpflv.FlvFileWriter
rtmpWriter httpflv.FlvFileWriter rtmpWriter httpflv.FlvFileWriter
@ -72,12 +83,6 @@ var (
httpflvPullSession *httpflv.PullSession httpflvPullSession *httpflv.PullSession
rtmpPullSession *rtmp.PullSession rtmpPullSession *rtmp.PullSession
rtspPullSession *rtsp.PullSession rtspPullSession *rtsp.PullSession
fileTagCount int
httpflvPullTagCount nazaatomic.Uint32
rtmpPullTagCount nazaatomic.Uint32
rtspSdpCtx sdp.LogicContext
rtspPullAvPacketCount nazaatomic.Uint32
) )
type RtspPullObserver struct { type RtspPullObserver struct {
@ -96,16 +101,17 @@ func (r RtspPullObserver) OnAvPacket(pkt base.AvPacket) {
func Entry(t *testing.T) { func Entry(t *testing.T) {
if _, err := os.Lstat(confFile); err != nil { 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 return
} }
if _, err := os.Lstat(rFlvFileName); err != nil { 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 return
} }
hls.Clock = mock.NewFakeClock() hls.Clock = mock.NewFakeClock()
hls.Clock.Set(time.Date(2022, 1, 16, 23, 24, 25, 0, time.Local)) hls.Clock.Set(time.Date(2022, 1, 16, 23, 24, 25, 0, time.Local))
httpts.SubSessionWriteChanSize = 0
tt = t 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) 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) 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) 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) 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) tags, err := httpflv.ReadAllTagsFromFlvFile(rFlvFileName)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
@ -153,9 +163,9 @@ func Entry(t *testing.T) {
assert.Equal(tt, nil, err) assert.Equal(tt, nil, err)
rtmpPullTagCount.Increment() rtmpPullTagCount.Increment()
}) })
nazalog.Assert(nil, err) Log.Assert(nil, err)
err = <-rtmpPullSession.WaitChan() err = <-rtmpPullSession.WaitChan()
nazalog.Debug(err) Log.Debug(err)
}() }()
go func() { go func() {
@ -167,9 +177,17 @@ func Entry(t *testing.T) {
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
httpflvPullTagCount.Increment() httpflvPullTagCount.Increment()
}) })
nazalog.Assert(nil, err) Log.Assert(nil, err)
err = <-httpflvPullSession.WaitChan() 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) time.Sleep(200 * time.Millisecond)
@ -184,7 +202,7 @@ func Entry(t *testing.T) {
option.PullTimeoutMs = 500 option.PullTimeoutMs = 500
}) })
err := rtspPullSession.Pull(rtspPullUrl) err := rtspPullSession.Pull(rtspPullUrl)
nazalog.Debug(err) Log.Debug(err)
if rtspSdpCtx.RawSdp != nil { if rtspSdpCtx.RawSdp != nil {
break break
} }
@ -202,7 +220,7 @@ func Entry(t *testing.T) {
for _, tag := range tags { for _, tag := range tags {
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
chunks := remux.FlvTag2RtmpChunks(tag) chunks := remux.FlvTag2RtmpChunks(tag)
//nazalog.Debugf("rtmp push: %d", fileTagCount.Load()) //Log.Debugf("rtmp push: %d", fileTagCount.Load())
err = pushSession.Write(chunks) err = pushSession.Write(chunks)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
} }
@ -220,7 +238,7 @@ func Entry(t *testing.T) {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
} }
nazalog.Debug("[innertest] start dispose.") Log.Debug("[innertest] start dispose.")
pushSession.Dispose() pushSession.Dispose()
httpflvPullSession.Dispose() httpflvPullSession.Dispose()
@ -234,58 +252,60 @@ func Entry(t *testing.T) {
//_ = syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) //_ = syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
sm.Dispose() 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()) fileTagCount, httpflvPullTagCount.Load(), rtmpPullTagCount.Load(), rtspPullAvPacketCount.Load())
compareFile() 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() { func compareFile() {
r, err := ioutil.ReadFile(rFlvFileName) r, err := ioutil.ReadFile(rFlvFileName)
assert.Equal(tt, nil, err) 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) w, err := ioutil.ReadFile(wFlvPullFileName)
assert.Equal(tt, nil, err) 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) res := bytes.Compare(r, w)
assert.Equal(tt, 0, res) assert.Equal(tt, 0, res)
//err = os.Remove(wFlvPullFileName) //err = os.Remove(wFlvPullFileName)
assert.Equal(tt, nil, err) assert.Equal(tt, nil, err)
// 检查rtmp
w2, err := ioutil.ReadFile(wRtmpPullFileName) w2, err := ioutil.ReadFile(wRtmpPullFileName)
assert.Equal(tt, nil, err) 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) res = bytes.Compare(r, w2)
assert.Equal(tt, 0, res) assert.Equal(tt, 0, res)
//err = os.Remove(wRtmpPullFileName) //err = os.Remove(wRtmpPullFileName)
assert.Equal(tt, nil, err) 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) { func getAllHttpApi(addr string) {
@ -293,32 +313,34 @@ func getAllHttpApi(addr string) {
var err error var err error
b, err = httpGet(fmt.Sprintf("http://%s/api/list", addr)) b, err = httpGet(fmt.Sprintf("http://%s/api/list", addr))
nazalog.Assert(nil, err) Log.Assert(nil, err)
nazalog.Debugf("%s", string(b)) Log.Debugf("%s", string(b))
b, err = httpGet(fmt.Sprintf("http://%s/api/stat/lal_info", addr)) b, err = httpGet(fmt.Sprintf("http://%s/api/stat/lal_info", addr))
nazalog.Assert(nil, err) Log.Assert(nil, err)
nazalog.Debugf("%s", string(b)) Log.Debugf("%s", string(b))
b, err = httpGet(fmt.Sprintf("http://%s/api/stat/group?stream_name=innertest", addr)) b, err = httpGet(fmt.Sprintf("http://%s/api/stat/group?stream_name=innertest", addr))
nazalog.Assert(nil, err) Log.Assert(nil, err)
nazalog.Debugf("%s", string(b)) Log.Debugf("%s", string(b))
b, err = httpGet(fmt.Sprintf("http://%s/api/stat/all_group", addr)) b, err = httpGet(fmt.Sprintf("http://%s/api/stat/all_group", addr))
nazalog.Assert(nil, err) Log.Assert(nil, err)
nazalog.Debugf("%s", string(b)) Log.Debugf("%s", string(b))
var acspr base.ApiCtrlStartPullReq var acspr base.ApiCtrlStartPullReq
b, err = httpPost(fmt.Sprintf("http://%s/api/ctrl/start_pull", addr), &acspr) b, err = httpPost(fmt.Sprintf("http://%s/api/ctrl/start_pull", addr), &acspr)
nazalog.Assert(nil, err) Log.Assert(nil, err)
nazalog.Debugf("%s", string(b)) Log.Debugf("%s", string(b))
var ackos base.ApiCtrlKickOutSession var ackos base.ApiCtrlKickOutSession
b, err = httpPost(fmt.Sprintf("http://%s/api/ctrl/kick_out_session", addr), &ackos) b, err = httpPost(fmt.Sprintf("http://%s/api/ctrl/kick_out_session", addr), &ackos)
nazalog.Assert(nil, err) Log.Assert(nil, err)
nazalog.Debugf("%s", string(b)) Log.Debugf("%s", string(b))
} }
// ---------------------------------------------------------------------------------------------------------------------
// TODO(chef): refactor 移入naza中 // TODO(chef): refactor 移入naza中
func httpGet(url string) ([]byte, error) { func httpGet(url string) ([]byte, error) {
@ -339,6 +361,8 @@ func httpPost(url string, info interface{}) ([]byte, error) {
return ioutil.ReadAll(resp.Body) return ioutil.ReadAll(resp.Body)
} }
// ---------------------------------------------------------------------------------------------------------------------
var goldenPlaylistM3u8 = `#EXTM3U var goldenPlaylistM3u8 = `#EXTM3U
#EXT-X-VERSION:3 #EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO #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())) 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 *option = config.LogConfig
}); err != nil { }); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "initial log failed. err=%+v\n", err) _, _ = fmt.Fprintf(os.Stderr, "initial log failed. err=%+v\n", err)
base.OsExitAndWaitPressIfWindows(1) base.OsExitAndWaitPressIfWindows(1)
} }
nazalog.Info("initial log succ.") Log.Info("initial log succ.")
// 打印Logo // 打印Logo
nazalog.Info(` Log.Info(`
__ ___ __ __ ___ __
/ / / | / / / / / | / /
/ / / /| | / / / / / /| | / /
@ -240,7 +240,7 @@ func LoadConfAndInitLog(confFile string) *Config {
// 检查配置版本号是否匹配 // 检查配置版本号是否匹配
if config.ConfVersion != ConfVersion { 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) 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", "httpts.http_listen_addr", "httpts.https_listen_addr", "httpts.https_cert_file", "httpts.https_key_file",
) )
if err != nil { 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 { 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 { 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监听相关的配置则尝试使用全局配置 // 如果具体的HTTP应用没有设置HTTP监听相关的配置则尝试使用全局配置
@ -271,39 +271,39 @@ func LoadConfAndInitLog(confFile string) *Config {
// 为缺失的字段中的一些特定字段,设置特定默认值 // 为缺失的字段中的一些特定字段,设置特定默认值
if config.HlsConfig.Enable && !j.Exist("hls.cleanup_mode") { 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 config.HlsConfig.CleanupMode = defaultHlsCleanupMode
} }
if config.HlsConfig.Enable && !j.Exist("hls.delete_threshold") { 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.FragmentNum)
config.HlsConfig.DeleteThreshold = config.HlsConfig.FragmentNum config.HlsConfig.DeleteThreshold = config.HlsConfig.FragmentNum
} }
if (config.HttpflvConfig.Enable || config.HttpflvConfig.EnableHttps) && !j.Exist("httpflv.url_pattern") { 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 config.HttpflvConfig.UrlPattern = defaultHttpflvUrlPattern
} }
if (config.HttptsConfig.Enable || config.HttptsConfig.EnableHttps) && !j.Exist("httpts.url_pattern") { 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 config.HttptsConfig.UrlPattern = defaultHttptsUrlPattern
} }
if (config.HlsConfig.Enable || config.HlsConfig.EnableHttps) && !j.Exist("hls.url_pattern") { 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 config.HttpflvConfig.UrlPattern = defaultHlsUrlPattern
} }
// 对一些常见的格式错误做修复 // 对一些常见的格式错误做修复
// 确保url pattern以`/`开始,并以`/`结束 // 确保url pattern以`/`开始,并以`/`结束
if urlPattern, changed := ensureStartAndEndWithSlash(config.HttpflvConfig.UrlPattern); changed { 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 config.HttpflvConfig.UrlPattern = urlPattern
} }
if urlPattern, changed := ensureStartAndEndWithSlash(config.HttptsConfig.UrlPattern); changed { 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 config.HttpflvConfig.UrlPattern = urlPattern
} }
if urlPattern, changed := ensureStartAndEndWithSlash(config.HlsConfig.UrlPattern); changed { 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 config.HttpflvConfig.UrlPattern = urlPattern
} }
@ -318,7 +318,7 @@ func LoadConfAndInitLog(confFile string) *Config {
tlines = append(tlines, strings.TrimSpace(l)) tlines = append(tlines, strings.TrimSpace(l))
} }
compactRawContent := strings.Join(tlines, " ") 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 return config
} }

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

@ -16,7 +16,6 @@ import (
"github.com/q191201771/naza/pkg/nazahttp" "github.com/q191201771/naza/pkg/nazahttp"
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
) )
type HttpApiServer struct { type HttpApiServer struct {
@ -37,7 +36,7 @@ func (h *HttpApiServer) Listen() (err error) {
if h.ln, err = net.Listen("tcp", h.addr); err != nil { if h.ln, err = net.Listen("tcp", h.addr); err != nil {
return return
} }
nazalog.Infof("start httpapi server listen. addr=%s", h.addr) Log.Infof("start httpapi server listen. addr=%s", h.addr)
return 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") err := nazahttp.UnmarshalRequestJsonBody(req, &info, "protocol", "addr", "app_name", "stream_name")
if err != nil { 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.ErrorCode = base.ErrorCodeParamMissing
v.Desp = base.DespParamMissing v.Desp = base.DespParamMissing
feedback(v, w) feedback(v, w)
return 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) h.sm.CtrlStartPull(info)
v.ErrorCode = base.ErrorCodeSucc 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") err := nazahttp.UnmarshalRequestJsonBody(req, &info, "stream_name", "session_id")
if err != nil { 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.ErrorCode = base.ErrorCodeParamMissing
v.Desp = base.DespParamMissing v.Desp = base.DespParamMissing
feedback(v, w) feedback(v, w)
return 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) resp := h.sm.CtrlKickOutSession(info)
feedback(resp, w) feedback(resp, w)

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

@ -14,7 +14,6 @@ import (
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazahttp" "github.com/q191201771/naza/pkg/nazahttp"
"github.com/q191201771/naza/pkg/nazalog"
) )
// TODO(chef): refactor 配置参数供外部传入 // TODO(chef): refactor 配置参数供外部传入
@ -132,12 +131,12 @@ func (h *HttpNotify) asyncPost(url string, info interface{}) {
case h.taskQueue <- PostTask{url: url, info: info}: case h.taskQueue <- PostTask{url: url, info: info}:
// noop // noop
default: default:
nazalog.Error("http notify queue full.") Log.Error("http notify queue full.")
} }
} }
func (h *HttpNotify) post(url string, info interface{}) { func (h *HttpNotify) post(url string, info interface{}) {
if _, err := nazahttp.PostJson(url, info, h.client); err != nil { 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/base"
"github.com/q191201771/lal/pkg/httpflv" "github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/httpts" "github.com/q191201771/lal/pkg/httpts"
"github.com/q191201771/naza/pkg/nazalog"
) )
type HttpServerHandlerObserver interface { type HttpServerHandlerObserver interface {
@ -45,17 +44,17 @@ func NewHttpServerHandler(observer HttpServerHandlerObserver) *HttpServerHandler
func (h *HttpServerHandler) ServeSubSession(writer http.ResponseWriter, req *http.Request) { func (h *HttpServerHandler) ServeSubSession(writer http.ResponseWriter, req *http.Request) {
urlCtx, err := base.ParseUrl(base.ParseHttpRequest(req), 80) urlCtx, err := base.ParseUrl(base.ParseHttpRequest(req), 80)
if err != nil { if err != nil {
nazalog.Errorf("parse url. err=%+v", err) Log.Errorf("parse url. err=%+v", err)
return return
} }
conn, bio, err := writer.(http.Hijacker).Hijack() conn, bio, err := writer.(http.Hijacker).Hijack()
if err != nil { if err != nil {
nazalog.Errorf("hijack failed. err=%+v", err) Log.Errorf("hijack failed. err=%+v", err)
return return
} }
if bio.Reader.Buffered() != 0 || bio.Writer.Buffered() != 0 { 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 ( var (
@ -69,28 +68,28 @@ func (h *HttpServerHandler) ServeSubSession(writer http.ResponseWriter, req *htt
if strings.HasSuffix(urlCtx.LastItemOfPath, ".flv") { if strings.HasSuffix(urlCtx.LastItemOfPath, ".flv") {
session := httpflv.NewSubSession(conn, urlCtx, isWebSocket, webSocketKey) 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 { 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() _ = session.Dispose()
return return
} }
err = session.RunLoop() 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) h.observer.OnDelHttpflvSubSession(session)
return return
} }
if strings.HasSuffix(urlCtx.LastItemOfPath, ".ts") { if strings.HasSuffix(urlCtx.LastItemOfPath, ".ts") {
session := httpts.NewSubSession(conn, urlCtx, isWebSocket, webSocketKey) 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 { 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() _ = session.Dispose()
return return
} }
err = session.RunLoop() 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) h.observer.OnDelHttptsSubSession(session)
return return
} }

@ -66,23 +66,3 @@ var defaultOption = Option{
} }
type ModOption func(option *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/httpflv"
"github.com/q191201771/lal/pkg/rtmp" "github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/nazalog"
//"github.com/felixge/fgprof" //"github.com/felixge/fgprof"
) )
@ -57,7 +56,7 @@ type ServerManager struct {
func NewServerManager(confFile string, modOption ...ModOption) *ServerManager { func NewServerManager(confFile string, modOption ...ModOption) *ServerManager {
sm := &ServerManager{ sm := &ServerManager{
serverStartTime: time.Now().Format("2006-01-02 15:04:05.999"), serverStartTime: base.ReadableNowTime(),
exitChan: make(chan struct{}, 1), exitChan: make(chan struct{}, 1),
} }
sm.groupManager = NewSimpleGroupManager(sm) sm.groupManager = NewSimpleGroupManager(sm)
@ -66,16 +65,16 @@ func NewServerManager(confFile string, modOption ...ModOption) *ServerManager {
base.LogoutStartInfo() base.LogoutStartInfo()
if sm.config.HlsConfig.Enable && sm.config.HlsConfig.UseMemoryAsDiskFlag { 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) hls.SetUseMemoryAsDiskFlag(true)
} }
if sm.config.RecordConfig.EnableFlv { if sm.config.RecordConfig.EnableFlv {
if err := os.MkdirAll(sm.config.RecordConfig.FlvOutPath, 0777); err != nil { 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 { 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, handler,
) )
if err != nil { 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 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 { if config.EnableHttps {
err := sm.httpServerManager.AddListen( err := sm.httpServerManager.AddListen(
@ -144,9 +143,9 @@ func (sm *ServerManager) RunLoop() error {
handler, handler,
) )
if err != nil { 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 { } 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 return nil
@ -165,7 +164,7 @@ func (sm *ServerManager) RunLoop() error {
if sm.httpServerManager != nil { if sm.httpServerManager != nil {
go func() { go func() {
if err := sm.httpServerManager.RunLoop(); err != nil { if err := sm.httpServerManager.RunLoop(); err != nil {
nazalog.Error(err) Log.Error(err)
} }
}() }()
} }
@ -176,7 +175,7 @@ func (sm *ServerManager) RunLoop() error {
} }
go func() { go func() {
if err := sm.rtmpServer.RunLoop(); err != nil { if err := sm.rtmpServer.RunLoop(); err != nil {
nazalog.Error(err) Log.Error(err)
} }
}() }()
} }
@ -187,7 +186,7 @@ func (sm *ServerManager) RunLoop() error {
} }
go func() { go func() {
if err := sm.rtspServer.RunLoop(); err != nil { if err := sm.rtspServer.RunLoop(); err != nil {
nazalog.Error(err) Log.Error(err)
} }
}() }()
} }
@ -198,7 +197,7 @@ func (sm *ServerManager) RunLoop() error {
} }
go func() { go func() {
if err := sm.httpApiServer.RunLoop(); err != nil { if err := sm.httpApiServer.RunLoop(); err != nil {
nazalog.Error(err) Log.Error(err)
} }
}() }()
} }
@ -224,7 +223,7 @@ func (sm *ServerManager) RunLoop() error {
// 关闭空闲的group // 关闭空闲的group
sm.groupManager.Iterate(func(group *Group) bool { sm.groupManager.Iterate(func(group *Group) bool {
if group.IsTotalEmpty() { if group.IsTotalEmpty() {
nazalog.Infof("erase empty group. [%s]", group.UniqueKey) Log.Infof("erase empty group. [%s]", group.UniqueKey)
group.Dispose() group.Dispose()
return false return false
} }
@ -237,13 +236,13 @@ func (sm *ServerManager) RunLoop() error {
if sm.config.DebugConfig.LogGroupIntervalSec > 0 && if sm.config.DebugConfig.LogGroupIntervalSec > 0 &&
count%uint32(sm.config.DebugConfig.LogGroupIntervalSec) == 0 { count%uint32(sm.config.DebugConfig.LogGroupIntervalSec) == 0 {
groupNum := sm.groupManager.Len() 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 { if sm.config.DebugConfig.LogGroupMaxGroupNum > 0 {
var loggedGroupCount int var loggedGroupCount int
sm.groupManager.Iterate(func(group *Group) bool { sm.groupManager.Iterate(func(group *Group) bool {
loggedGroupCount++ loggedGroupCount++
if loggedGroupCount <= sm.config.DebugConfig.LogGroupMaxGroupNum { 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 return true
}) })
@ -265,7 +264,7 @@ func (sm *ServerManager) RunLoop() error {
} }
func (sm *ServerManager) Dispose() { func (sm *ServerManager) Dispose() {
nazalog.Debug("dispose server manager.") Log.Debug("dispose server manager.")
// TODO(chef) add httpServer // TODO(chef) add httpServer
@ -324,7 +323,7 @@ func (sm *ServerManager) CtrlStartPull(info base.ApiCtrlStartPullReq) {
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
g := sm.getGroup(info.AppName, info.StreamName) g := sm.getGroup(info.AppName, info.StreamName)
if g == nil { 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 return
} }
var url string 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 := sm.GetGroup(appName, streamName); g != nil {
if g.IsHlsMuxerAlive() { 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 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 { 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, appName,
@ -785,12 +784,12 @@ func (sm *ServerManager) getGroup(appName string, streamName string) *Group {
func (sm *ServerManager) serveHls(writer http.ResponseWriter, req *http.Request) { func (sm *ServerManager) serveHls(writer http.ResponseWriter, req *http.Request) {
urlCtx, err := base.ParseUrl(base.ParseHttpRequest(req), 80) urlCtx, err := base.ParseUrl(base.ParseHttpRequest(req), 80)
if err != nil { if err != nil {
nazalog.Errorf("parse url. err=%+v", err) Log.Errorf("parse url. err=%+v", err)
return return
} }
if urlCtx.GetFileType() == "m3u8" { if urlCtx.GetFileType() == "m3u8" {
if err = sm.simpleAuthCtx.OnHls(urlCtx.GetFilenameWithoutType(), urlCtx.RawQuery); err != nil { 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 return
} }
} }
@ -799,13 +798,13 @@ func (sm *ServerManager) serveHls(writer http.ResponseWriter, req *http.Request)
} }
func runWebPprof(addr string) { 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()) //http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
if err := http.ListenAndServe(addr, nil); err != nil { if err := http.ListenAndServe(addr, nil); err != nil {
nazalog.Error(err) Log.Error(err)
return 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 // MPEG: Moving Picture Experts Group
// 每个TS文件都以固定的PATPMT开始 // FixedFragmentHeader 每个TS文件都以固定的PATPMT开始
var FixedFragmentHeader = []byte{ var FixedFragmentHeader = []byte{
/* TS */ /* TS */
0x47, 0x40, 0x00, 0x10, 0x00, 0x47, 0x40, 0x00, 0x10, 0x00,
@ -70,7 +70,7 @@ var FixedFragmentHeader = []byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
} }
// 每个TS文件都以固定的PATPMT开始 // FixedFragmentHeaderHevc 每个TS文件都以固定的PATPMT开始
var FixedFragmentHeaderHevc = []byte{ var FixedFragmentHeaderHevc = []byte{
/* TS */ /* TS */
0x47, 0x40, 0x00, 0x10, 0x00, 0x47, 0x40, 0x00, 0x10, 0x00,
@ -140,7 +140,7 @@ const (
PidVideo uint16 = 0x100 PidVideo uint16 = 0x100
PidAudio uint16 = 0x101 PidAudio uint16 = 0x101
// ------------------------------------------ // AdaptationFieldControlReserved ------------------------------------------
// <iso13818-1.pdf> <Table 2-5> <page 38/174> // <iso13818-1.pdf> <Table 2-5> <page 38/174>
// ------------------------------------------ // ------------------------------------------
AdaptationFieldControlReserved uint8 = 0 // Reserved for future use by ISO/IEC AdaptationFieldControlReserved uint8 = 0 // Reserved for future use by ISO/IEC
@ -164,13 +164,13 @@ const (
// PES // PES
const ( const (
// ----------------------------------------------------------------- // StreamIdAudio -----------------------------------------------------------------
// <iso13818-1.pdf> <Table 2-18-Stream_id assignments> <page 52/174> // <iso13818-1.pdf> <Table 2-18-Stream_id assignments> <page 52/174>
// ----------------------------------------------------------------- // -----------------------------------------------------------------
StreamIdAudio uint8 = 192 // 110x xxxx 0xC0 StreamIdAudio uint8 = 192 // 110x xxxx 0xC0
StreamIdVideo uint8 = 224 // 1110 xxxx StreamIdVideo uint8 = 224 // 1110 xxxx
// ------------------------------ // PtsDtsFlags0 ------------------------------
// <iso13818-1.pdf> <page 53/174> // <iso13818-1.pdf> <page 53/174>
// ------------------------------ // ------------------------------
PtsDtsFlags0 uint8 = 0 // no PTS no DTS PtsDtsFlags0 uint8 = 0 // no PTS no DTS

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

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

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

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

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

@ -12,7 +12,7 @@ import (
"github.com/q191201771/naza/pkg/nazabits" "github.com/q191201771/naza/pkg/nazabits"
) )
// ------------------------------------------------ // TsPacketHeader ------------------------------------------------
// <iso13818-1.pdf> <2.4.3.2> <page 36/174> // <iso13818-1.pdf> <2.4.3.2> <page 36/174>
// sync_byte [8b] * always 0x47 // sync_byte [8b] * always 0x47
// transport_error_indicator [1b] // transport_error_indicator [1b]
@ -34,7 +34,7 @@ type TsPacketHeader struct {
Cc uint8 Cc uint8
} }
// ---------------------------------------------------------- // TsPacketAdaptation ----------------------------------------------------------
// <iso13818-1.pdf> <Table 2-6> <page 40/174> // <iso13818-1.pdf> <Table 2-6> <page 40/174>
// adaptation_field_length [8b] * 不包括自己这1字节 // adaptation_field_length [8b] * 不包括自己这1字节
// discontinuity_indicator [1b] // discontinuity_indicator [1b]
@ -54,7 +54,7 @@ type TsPacketAdaptation struct {
Length uint8 Length uint8
} }
// 解析4字节TS Packet header // ParseTsPacketHeader 解析4字节TS Packet header
func ParseTsPacketHeader(b []byte) (h TsPacketHeader) { func ParseTsPacketHeader(b []byte) (h TsPacketHeader) {
// TODO chef: 检查长度 // TODO chef: 检查长度
br := nazabits.NewBitReader(b) br := nazabits.NewBitReader(b)
@ -69,7 +69,7 @@ func ParseTsPacketHeader(b []byte) (h TsPacketHeader) {
return return
} }
// TODO chef // ParseTsPacketAdaptation TODO chef
func ParseTsPacketAdaptation(b []byte) (f TsPacketAdaptation) { func ParseTsPacketAdaptation(b []byte) (f TsPacketAdaptation) {
br := nazabits.NewBitReader(b) br := nazabits.NewBitReader(b)
f.Length, _ = br.ReadBits8(8) 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/rtprtcp"
"github.com/q191201771/lal/pkg/sdp" "github.com/q191201771/lal/pkg/sdp"
"github.com/q191201771/naza/pkg/bele" "github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
) )
// AvPacket转换为RTMP // AvPacket2RtmpRemuxer AvPacket转换为RTMP
// 目前AvPacket来自RTSP的sdp以及rtp的合帧包。理论上也支持webrtc后续接入webrtc时再验证 // 目前AvPacket来自RTSP的sdp以及rtp的合帧包。理论上也支持webrtc后续接入webrtc时再验证
// //
type AvPacket2RtmpRemuxer struct { type AvPacket2RtmpRemuxer struct {
@ -43,7 +42,7 @@ func NewAvPacket2RtmpRemuxer(onRtmpAvMsg rtmp.OnReadRtmpAvMsg) *AvPacket2RtmpRem
} }
} }
// 实现RTSP回调数据的三个接口使得接入时方便些 // OnRtpPacket 实现RTSP回调数据的三个接口使得接入时方便些
func (r *AvPacket2RtmpRemuxer) OnRtpPacket(pkt rtprtcp.RtpPacket) { func (r *AvPacket2RtmpRemuxer) OnRtpPacket(pkt rtprtcp.RtpPacket) {
// noop // noop
} }
@ -54,7 +53,7 @@ func (r *AvPacket2RtmpRemuxer) OnAvPacket(pkt base.AvPacket) {
r.FeedAvPacket(pkt) r.FeedAvPacket(pkt)
} }
// rtsp场景下有时sps、pps等信息只包含在sdp中有时包含在rtp包中 // InitWithAvConfig rtsp场景下有时sps、pps等信息只包含在sdp中有时包含在rtp包中
// 这里提供输入sdp的sps、pps等信息的机会如果没有可以不调用 // 这里提供输入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 { if r.audioType == base.AvPacketPtUnknown && r.videoType == base.AvPacketPtUnknown {
nazalog.Warn("has no audio or video") Log.Warn("has no audio or video")
return return
} }
if r.audioType != base.AvPacketPtUnknown { if r.audioType != base.AvPacketPtUnknown {
bAsh, err = aac.MakeAudioDataSeqHeaderWithAsc(asc) bAsh, err = aac.MakeAudioDataSeqHeaderWithAsc(asc)
if err != nil { if err != nil {
nazalog.Errorf("build aac seq header failed. err=%+v", err) Log.Errorf("build aac seq header failed. err=%+v", err)
return return
} }
} }
@ -92,13 +91,13 @@ func (r *AvPacket2RtmpRemuxer) InitWithAvConfig(asc, vps, sps, pps []byte) {
if r.videoType == base.AvPacketPtHevc { if r.videoType == base.AvPacketPtHevc {
bVsh, err = hevc.BuildSeqHeaderFromVpsSpsPps(vps, sps, pps) bVsh, err = hevc.BuildSeqHeaderFromVpsSpsPps(vps, sps, pps)
if err != nil { if err != nil {
nazalog.Errorf("build hevc seq header failed. err=%+v", err) Log.Errorf("build hevc seq header failed. err=%+v", err)
return return
} }
} else { } else {
bVsh, err = avc.BuildSeqHeaderFromSpsPps(sps, pps) bVsh, err = avc.BuildSeqHeaderFromSpsPps(sps, pps)
if err != nil { if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err) Log.Errorf("build avc seq header failed. err=%+v", err)
return 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) { func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
switch pkt.PayloadType { switch pkt.PayloadType {
@ -122,7 +121,7 @@ func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
case base.AvPacketPtHevc: case base.AvPacketPtHevc:
nals, err := avc.SplitNaluAvcc(pkt.Payload) nals, err := avc.SplitNaluAvcc(pkt.Payload)
if err != nil { if err != nil {
nazalog.Errorf("iterate nalu failed. err=%+v", err) Log.Errorf("iterate nalu failed. err=%+v", err)
return return
} }
@ -149,7 +148,7 @@ func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
bVsh, err := avc.BuildSeqHeaderFromSpsPps(r.sps, r.pps) bVsh, err := avc.BuildSeqHeaderFromSpsPps(r.sps, r.pps)
if err != nil { if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err) Log.Errorf("build avc seq header failed. err=%+v", err)
continue continue
} }
r.emitRtmpAvMsg(false, bVsh, pkt.Timestamp) 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 { if len(r.vps) > 0 && len(r.sps) > 0 && len(r.pps) > 0 {
bVsh, err := hevc.BuildSeqHeaderFromVpsSpsPps(r.vps, r.sps, r.pps) bVsh, err := hevc.BuildSeqHeaderFromVpsSpsPps(r.vps, r.sps, r.pps)
if err != nil { if err != nil {
nazalog.Errorf("build hevc seq header failed. err=%+v", err) Log.Errorf("build hevc seq header failed. err=%+v", err)
continue continue
} }
r.emitRtmpAvMsg(false, bVsh, pkt.Timestamp) r.emitRtmpAvMsg(false, bVsh, pkt.Timestamp)
@ -217,7 +216,7 @@ func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
copy(payload[2:], pkt.Payload) copy(payload[2:], pkt.Payload)
r.emitRtmpAvMsg(true, payload, pkt.Timestamp) r.emitRtmpAvMsg(true, payload, pkt.Timestamp)
default: 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) bMetadata, err := rtmp.BuildMetadata(-1, -1, audiocodecid, videocodecid)
if err != nil { if err != nil {
nazalog.Errorf("build metadata failed. err=%+v", err) Log.Errorf("build metadata failed. err=%+v", err)
return return
} }
r.onRtmpAvMsg(base.RtmpMsg{ r.onRtmpAvMsg(base.RtmpMsg{

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

@ -13,7 +13,6 @@ import (
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/rtmp" "github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/nazalog"
) )
const ( 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 filter.stage = dummyAudioFilterStageDummy
for i := range filter.earlyStageQueue { for i := range filter.earlyStageQueue {
filter.handleDummyStage(filter.earlyStageQueue[i]) filter.handleDummyStage(filter.earlyStageQueue[i])
@ -122,7 +121,7 @@ func (filter *DummyAudioFilter) handleNormalStage(msg base.RtmpMsg) {
func (filter *DummyAudioFilter) handleDummyStage(msg base.RtmpMsg) { func (filter *DummyAudioFilter) handleDummyStage(msg base.RtmpMsg) {
if msg.Header.MsgTypeId == base.RtmpTypeIdAudio { 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 return
} }

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

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

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

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

@ -19,7 +19,6 @@ import (
"github.com/q191201771/lal/pkg/hevc" "github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/lal/pkg/rtprtcp" "github.com/q191201771/lal/pkg/rtprtcp"
"github.com/q191201771/lal/pkg/sdp" "github.com/q191201771/lal/pkg/sdp"
"github.com/q191201771/naza/pkg/nazalog"
) )
// TODO(chef): refactor 将analyze部分独立出来作为一个filter // TODO(chef): refactor 将analyze部分独立出来作为一个filter
@ -30,7 +29,7 @@ var (
maxAnalyzeAvMsgSize = 16 maxAnalyzeAvMsgSize = 16
) )
// 提供rtmp数据向sdp+rtp数据的转换 // Rtmp2RtspRemuxer 提供rtmp数据向sdp+rtp数据的转换
type Rtmp2RtspRemuxer struct { type Rtmp2RtspRemuxer struct {
onSdp OnSdp onSdp OnSdp
onRtpPacket OnRtpPacket onRtpPacket OnRtpPacket
@ -50,7 +49,7 @@ type Rtmp2RtspRemuxer struct {
type OnSdp func(sdpCtx sdp.LogicContext) type OnSdp func(sdpCtx sdp.LogicContext)
type OnRtpPacket func(pkt rtprtcp.RtpPacket) type OnRtpPacket func(pkt rtprtcp.RtpPacket)
// @param onSdp: 每次回调为独立的内存块,回调结束后,内部不再使用该内存块 // NewRtmp2RtspRemuxer @param onSdp: 每次回调为独立的内存块,回调结束后,内部不再使用该内存块
// @param onRtpPacket: 每次回调为独立的内存块,回调结束后,内部不再使用该内存块 // @param onRtpPacket: 每次回调为独立的内存块,回调结束后,内部不再使用该内存块
// //
func NewRtmp2RtspRemuxer(onSdp OnSdp, onRtpPacket OnRtpPacket) *Rtmp2RtspRemuxer { 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) { func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
var err error var err error
@ -80,10 +79,10 @@ func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
if msg.IsAvcKeySeqHeader() || msg.IsHevcKeySeqHeader() { if msg.IsAvcKeySeqHeader() || msg.IsHevcKeySeqHeader() {
if msg.IsAvcKeySeqHeader() { if msg.IsAvcKeySeqHeader() {
r.sps, r.pps, err = avc.ParseSpsPpsFromSeqHeader(msg.Payload) r.sps, r.pps, err = avc.ParseSpsPpsFromSeqHeader(msg.Payload)
nazalog.Assert(nil, err) Log.Assert(nil, err)
} else if msg.IsHevcKeySeqHeader() { } else if msg.IsHevcKeySeqHeader() {
r.vps, r.sps, r.pps, err = hevc.ParseVpsSpsPpsFromSeqHeader(msg.Payload) r.vps, r.sps, r.pps, err = hevc.ParseVpsSpsPpsFromSeqHeader(msg.Payload)
nazalog.Assert(nil, err) Log.Assert(nil, err)
} }
r.doAnalyze() r.doAnalyze()
return return
@ -91,7 +90,7 @@ func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
if msg.IsAacSeqHeader() { if msg.IsAacSeqHeader() {
if len(msg.Payload) < 3 { 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 return
} }
r.asc = msg.Clone().Payload[2:] r.asc = msg.Clone().Payload[2:]
@ -115,7 +114,7 @@ func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
} }
func (r *Rtmp2RtspRemuxer) doAnalyze() { func (r *Rtmp2RtspRemuxer) doAnalyze() {
nazalog.Assert(false, r.analyzeDone) Log.Assert(false, r.analyzeDone)
if r.isAnalyzeEnough() { if r.isAnalyzeEnough() {
if r.sps != nil && r.pps != nil { if r.sps != nil && r.pps != nil {
@ -131,7 +130,7 @@ func (r *Rtmp2RtspRemuxer) doAnalyze() {
// 回调sdp // 回调sdp
ctx, err := sdp.Pack(r.vps, r.sps, r.pps, r.asc) ctx, err := sdp.Pack(r.vps, r.sps, r.pps, r.asc)
nazalog.Assert(nil, err) Log.Assert(nil, err)
r.onSdp(ctx) r.onSdp(ctx)
// 分析阶段缓存的数据 // 分析阶段缓存的数据
@ -189,11 +188,11 @@ func (r *Rtmp2RtspRemuxer) getAudioPacker() *rtprtcp.RtpPacker {
// TODO(chef): 如果rtmp不是以音视频头开始也可能收到了帧数据但是头不存在目前该remux没有做过多容错判断后续要加上或者在输入层保证 // TODO(chef): 如果rtmp不是以音视频头开始也可能收到了帧数据但是头不存在目前该remux没有做过多容错判断后续要加上或者在输入层保证
ascCtx, err := aac.NewAscContext(r.asc) ascCtx, err := aac.NewAscContext(r.asc)
if err != nil { if err != nil {
nazalog.Errorf("parse asc failed. err=%+v", err) Log.Errorf("parse asc failed. err=%+v", err)
} }
clockRate, err := ascCtx.GetSamplingFrequency() clockRate, err := ascCtx.GetSamplingFrequency()
if err != nil { if err != nil {
nazalog.Errorf("get sampling frequency failed. err=%+v", err) Log.Errorf("get sampling frequency failed. err=%+v", err)
} }
pp := rtprtcp.NewRtpPackerPayloadAac() 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/nazaerrors"
"github.com/q191201771/naza/pkg/bele" "github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
) )
const ( const (
@ -160,7 +159,7 @@ func (amf0) WriteObject(writer io.Writer, opa ObjectPairArray) error {
return err return err
} }
default: 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) _, err := writer.Write(Amf0TypeMarkerObjectEndBytes)
@ -307,7 +306,7 @@ func (amf0) ReadObject(b []byte) (ObjectPairArray, int, error) {
} }
index += l index += l
default: 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 index += l
default: 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 index += 3
} else { } else {
// 测试时发现Array最后也是以00 00 09结束不确定是否是标准规定的加个日志在这 // 测试时发现Array最后也是以00 00 09结束不确定是否是标准规定的加个日志在这
nazalog.Warn("amf ReadArray without suffix Amf0TypeMarkerObjectEndBytes.") Log.Warn("amf ReadArray without suffix Amf0TypeMarkerObjectEndBytes.")
} }
return ops, index, nil return ops, index, nil
} }

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

@ -126,8 +126,8 @@ func (c *ChunkComposer) RunLoop(reader io.Reader, cb OnCompleteMessage) error {
case 3: case 3:
// noop // noop
} }
if nazalog.GetOption().Level == nazalog.LevelTrace { if Log.GetOption().Level == nazalog.LevelTrace {
nazalog.Tracef("[%p] RTMP_READ chunk.fmt=%d, csid=%d, header=%+v, timestamp=%d", Log.Tracef("[%p] RTMP_READ chunk.fmt=%d, csid=%d, header=%+v, timestamp=%d",
c, fmt, csid, stream.header, stream.timestamp) c, fmt, csid, stream.header, stream.timestamp)
} }
@ -143,8 +143,8 @@ func (c *ChunkComposer) RunLoop(reader io.Reader, cb OnCompleteMessage) error {
return err return err
} }
newTs := bele.BeUint32(bootstrap) newTs := bele.BeUint32(bootstrap)
if nazalog.GetOption().Level == nazalog.LevelTrace { if Log.GetOption().Level == nazalog.LevelTrace {
nazalog.Tracef("[%p] RTMP_READ ext. ts=(%d,%d,%d)", Log.Tracef("[%p] RTMP_READ ext. ts=(%d,%d,%d)",
c, stream.timestamp, newTs, stream.header.TimestampAbs) c, stream.timestamp, newTs, stream.header.TimestampAbs)
} }
stream.timestamp = newTs stream.timestamp = newTs
@ -188,8 +188,8 @@ func (c *ChunkComposer) RunLoop(reader io.Reader, cb OnCompleteMessage) error {
stream.header.TimestampAbs += stream.timestamp stream.header.TimestampAbs += stream.timestamp
} }
absTsFlag = false absTsFlag = false
if nazalog.GetOption().Level == nazalog.LevelTrace { if Log.GetOption().Level == nazalog.LevelTrace {
nazalog.Tracef("[%p] RTMP_READ cb. fmt=%d, csid=%d, header=%+v, timestamp=%d, hex=%s", 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))) 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, localChunkSize: LocalChunkSize,
} }
// @return 返回的内存块由内部申请,不依赖参数<message>内存块 // Message2Chunks @return 返回的内存块由内部申请,不依赖参数<message>内存块
func Message2Chunks(message []byte, header *base.RtmpHeader) []byte { func Message2Chunks(message []byte, header *base.RtmpHeader) []byte {
return defaultChunkDivider.Message2Chunks(message, header) 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 { func (d *ChunkDivider) Message2Chunks(message []byte, header *base.RtmpHeader) []byte {
return message2Chunks(message, header, nil, d.localChunkSize) 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`重复使用 // @param onReadRtmpAvMsg: msg: 注意,回调结束后,`msg`的内存块会被`PullSession`重复使用
// 也即多次回调的`msg`是复用的同一块内存块 // 也即多次回调的`msg`是复用的同一块内存块
@ -83,42 +83,42 @@ func (s *PullSession) WaitChan() <-chan error {
// --------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------
// 文档请参考: interface ISessionUrlContext // Url 文档请参考: interface ISessionUrlContext
func (s *PullSession) Url() string { func (s *PullSession) Url() string {
return s.core.Url() return s.core.Url()
} }
// 文档请参考: interface ISessionUrlContext // AppName 文档请参考: interface ISessionUrlContext
func (s *PullSession) AppName() string { func (s *PullSession) AppName() string {
return s.core.AppName() return s.core.AppName()
} }
// 文档请参考: interface ISessionUrlContext // StreamName 文档请参考: interface ISessionUrlContext
func (s *PullSession) StreamName() string { func (s *PullSession) StreamName() string {
return s.core.StreamName() return s.core.StreamName()
} }
// 文档请参考: interface ISessionUrlContext // RawQuery 文档请参考: interface ISessionUrlContext
func (s *PullSession) RawQuery() string { func (s *PullSession) RawQuery() string {
return s.core.RawQuery() return s.core.RawQuery()
} }
// 文档请参考: interface IObject // UniqueKey 文档请参考: interface IObject
func (s *PullSession) UniqueKey() string { func (s *PullSession) UniqueKey() string {
return s.core.uniqueKey return s.core.uniqueKey
} }
// 文档请参考: interface ISessionStat // GetStat 文档请参考: interface ISessionStat
func (s *PullSession) GetStat() base.StatSession { func (s *PullSession) GetStat() base.StatSession {
return s.core.GetStat() return s.core.GetStat()
} }
// 文档请参考: interface ISessionStat // UpdateStat 文档请参考: interface ISessionStat
func (s *PullSession) UpdateStat(intervalSec uint32) { func (s *PullSession) UpdateStat(intervalSec uint32) {
s.core.UpdateStat(intervalSec) s.core.UpdateStat(intervalSec)
} }
// 文档请参考: interface ISessionStat // IsAlive 文档请参考: interface ISessionStat
func (s *PullSession) IsAlive() (readAlive, writeAlive bool) { func (s *PullSession) IsAlive() (readAlive, writeAlive bool) {
return s.core.IsAlive() 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 { func (s *PushSession) Push(rawUrl string) error {
return s.core.Do(rawUrl) return s.core.Do(rawUrl)
} }
@ -65,7 +65,7 @@ func (s *PushSession) Write(msg []byte) error {
return s.core.Write(msg) return s.core.Write(msg)
} }
// 将缓存的数据立即刷新发送 // Flush 将缓存的数据立即刷新发送
// 是否有缓存策略,请参见配置及内部实现 // 是否有缓存策略,请参见配置及内部实现
func (s *PushSession) Flush() error { func (s *PushSession) Flush() error {
return s.core.Flush() return s.core.Flush()
@ -89,42 +89,42 @@ func (s *PushSession) WaitChan() <-chan error {
// --------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------
// 文档请参考: interface ISessionUrlContext // Url 文档请参考: interface ISessionUrlContext
func (s *PushSession) Url() string { func (s *PushSession) Url() string {
return s.core.Url() return s.core.Url()
} }
// 文档请参考: interface ISessionUrlContext // AppName 文档请参考: interface ISessionUrlContext
func (s *PushSession) AppName() string { func (s *PushSession) AppName() string {
return s.core.AppName() return s.core.AppName()
} }
// 文档请参考: interface ISessionUrlContext // StreamName 文档请参考: interface ISessionUrlContext
func (s *PushSession) StreamName() string { func (s *PushSession) StreamName() string {
return s.core.StreamName() return s.core.StreamName()
} }
// 文档请参考: interface ISessionUrlContext // RawQuery 文档请参考: interface ISessionUrlContext
func (s *PushSession) RawQuery() string { func (s *PushSession) RawQuery() string {
return s.core.RawQuery() return s.core.RawQuery()
} }
// 文档请参考: interface IObject // UniqueKey 文档请参考: interface IObject
func (s *PushSession) UniqueKey() string { func (s *PushSession) UniqueKey() string {
return s.core.uniqueKey return s.core.uniqueKey
} }
// 文档请参考: interface ISessionStat // GetStat 文档请参考: interface ISessionStat
func (s *PushSession) GetStat() base.StatSession { func (s *PushSession) GetStat() base.StatSession {
return s.core.GetStat() return s.core.GetStat()
} }
// 文档请参考: interface ISessionStat // UpdateStat 文档请参考: interface ISessionStat
func (s *PushSession) UpdateStat(intervalSec uint32) { func (s *PushSession) UpdateStat(intervalSec uint32) {
s.core.UpdateStat(intervalSec) s.core.UpdateStat(intervalSec)
} }
// 文档请参考: interface ISessionStat // IsAlive 文档请参考: interface ISessionStat
func (s *PushSession) IsAlive() (readAlive, writeAlive bool) { func (s *PushSession) IsAlive() (readAlive, writeAlive bool) {
return s.core.IsAlive() return s.core.IsAlive()
} }

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

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

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

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

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

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

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

@ -13,7 +13,6 @@ import (
"net" "net"
"strings" "strings"
"sync" "sync"
"time"
"github.com/q191201771/naza/pkg/nazaerrors" "github.com/q191201771/naza/pkg/nazaerrors"
@ -21,7 +20,6 @@ import (
"github.com/q191201771/naza/pkg/bele" "github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/connection" "github.com/q191201771/naza/pkg/connection"
"github.com/q191201771/naza/pkg/nazalog"
) )
// TODO chef: 没有进化成Pub Sub时的超时释放 // TODO chef: 没有进化成Pub Sub时的超时释放
@ -41,7 +39,7 @@ type ServerSessionObserver interface {
} }
type PubSessionObserver interface { type PubSessionObserver interface {
// 注意回调结束后内部会复用Payload内存块 // OnReadRtmpAvMsg 注意回调结束后内部会复用Payload内存块
OnReadRtmpAvMsg(msg base.RtmpMsg) OnReadRtmpAvMsg(msg base.RtmpMsg)
} }
@ -98,7 +96,7 @@ func NewServerSession(observer ServerSessionObserver, conn net.Conn) *ServerSess
stat: base.StatSession{ stat: base.StatSession{
Protocol: base.ProtocolRtmp, Protocol: base.ProtocolRtmp,
SessionId: uk, SessionId: uk,
StartTime: time.Now().Format("2006-01-02 15:04:05.999"), StartTime: base.ReadableNowTime(),
RemoteAddr: conn.RemoteAddr().String(), RemoteAddr: conn.RemoteAddr().String(),
}, },
uniqueKey: uk, uniqueKey: uk,
@ -109,7 +107,7 @@ func NewServerSession(observer ServerSessionObserver, conn net.Conn) *ServerSess
IsFresh: true, IsFresh: true,
ShouldWaitVideoKeyFrame: 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 return s
} }
@ -207,9 +205,9 @@ func (s *ServerSession) handshake() error {
if err := s.hs.ReadC0C1(s.conn); err != nil { if err := s.hs.ReadC0C1(s.conn); err != nil {
return err 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 { if err := s.hs.WriteS0S1S2(s.conn); err != nil {
return err return err
} }
@ -217,7 +215,7 @@ func (s *ServerSession) handshake() error {
if err := s.hs.ReadC2(s.conn); err != nil { if err := s.hs.ReadC2(s.conn); err != nil {
return err return err
} }
nazalog.Infof("[%s] < R Handshake C2.", s.uniqueKey) Log.Infof("[%s] < R Handshake C2.", s.uniqueKey)
return nil return nil
} }
@ -245,7 +243,7 @@ func (s *ServerSession) doMsg(stream *Stream) error {
} }
s.avObserver.OnReadRtmpAvMsg(stream.toAvMsg()) s.avObserver.OnReadRtmpAvMsg(stream.toAvMsg())
default: 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 return nil
@ -253,7 +251,7 @@ func (s *ServerSession) doMsg(stream *Stream) error {
func (s *ServerSession) doAck(stream *Stream) error { func (s *ServerSession) doAck(stream *Stream) error {
seqNum := bele.BeUint32(stream.msg.buff.Bytes()) 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 return nil
} }
func (s *ServerSession) doUserControl(stream *Stream) error { func (s *ServerSession) doUserControl(stream *Stream) error {
@ -277,7 +275,7 @@ func (s *ServerSession) doDataMessageAmf0(stream *Stream) error {
switch val { switch val {
case "|RtmpSampleAccess": case "|RtmpSampleAccess":
nazalog.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.uniqueKey) Log.Debugf("[%s] < R |RtmpSampleAccess, ignore.", s.uniqueKey)
return nil return nil
default: default:
} }
@ -292,7 +290,7 @@ func (s *ServerSession) doDataMessageAmf0(stream *Stream) error {
// //
//switch val { //switch val {
//case "|RtmpSampleAccess": //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 // return nil
//case "@setDataFrame": //case "@setDataFrame":
// // macos obs and ffmpeg // // macos obs and ffmpeg
@ -304,13 +302,13 @@ func (s *ServerSession) doDataMessageAmf0(stream *Stream) error {
// return err // return err
// } // }
// if val != "onMetaData" { // 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 // return ErrRtmp
// } // }
//case "onMetaData": //case "onMetaData":
// // noop // // noop
//default: //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 // return nil
//} //}
// //
@ -346,9 +344,9 @@ func (s *ServerSession) doCommandMessage(stream *Stream) error {
case "getStreamLength": case "getStreamLength":
fallthrough fallthrough
case "deleteStream": 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: 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 return nil
} }
@ -370,28 +368,28 @@ func (s *ServerSession) doConnect(tid int, stream *Stream) error {
} }
s.tcUrl, err = val.FindString("tcUrl") s.tcUrl, err = val.FindString("tcUrl")
if err != nil { 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) 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 { if err := s.packer.writeWinAckSize(s.conn, windowAcknowledgementSize); err != nil {
return err 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 { if err := s.packer.writePeerBandwidth(s.conn, peerBandwidth, peerBandwidthLimitTypeDynamic); err != nil {
return err 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 { if err := s.packer.writeChunkSize(s.conn, LocalChunkSize); err != nil {
return err 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") oe, err := val.FindNumber("objectEncoding")
if oe != 0 && oe != 3 { if oe != 0 && oe != 3 {
oe = 0 oe = 0
@ -403,8 +401,8 @@ func (s *ServerSession) doConnect(tid int, stream *Stream) error {
} }
func (s *ServerSession) doCreateStream(tid int, stream *Stream) error { func (s *ServerSession) doCreateStream(tid int, stream *Stream) error {
nazalog.Infof("[%s] < R createStream().", s.uniqueKey) Log.Infof("[%s] < R createStream().", s.uniqueKey)
nazalog.Infof("[%s] > W _result().", s.uniqueKey) Log.Infof("[%s] > W _result().", s.uniqueKey)
if err := s.packer.writeCreateStreamResult(s.conn, tid); err != nil { if err := s.packer.writeCreateStreamResult(s.conn, tid); err != nil {
return err return err
} }
@ -431,10 +429,10 @@ func (s *ServerSession) doPublish(tid int, stream *Stream) (err error) {
if err != nil { if err != nil {
return err return err
} }
nazalog.Debugf("[%s] pubType=%s", s.uniqueKey, pubType) Log.Debugf("[%s] pubType=%s", s.uniqueKey, pubType)
nazalog.Infof("[%s] < R publish('%s')", s.uniqueKey, s.streamNameWithRawQuery) 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 { if err = s.packer.writeOnStatusPublish(s.conn, Msid1); err != nil {
return err 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) 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 // TODO chef: start duration reset
if err := s.packer.writeStreamIsRecorded(s.conn, Msid1); err != nil { 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 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 { if err := s.packer.writeOnStatusPlay(s.conn, Msid1); err != nil {
return err return err
} }
@ -506,7 +504,7 @@ func (s *ServerSession) modConnProps() {
func (s *ServerSession) dispose(err error) error { func (s *ServerSession) dispose(err error) error {
var retErr error var retErr error
s.disposeOnce.Do(func() { 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 { if s.conn == nil {
retErr = base.ErrSessionNotStarted retErr = base.ErrSessionNotStarted
return return

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

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

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

@ -13,12 +13,11 @@ import (
"time" "time"
"github.com/q191201771/lal/pkg/rtprtcp" "github.com/q191201771/lal/pkg/rtprtcp"
"github.com/q191201771/naza/pkg/nazalog"
) )
func TestMswLsw2UnixNano(t *testing.T) { func TestMswLsw2UnixNano(t *testing.T) {
u := rtprtcp.MswLsw2UnixNano(3805600902, 2181843386) u := rtprtcp.MswLsw2UnixNano(3805600902, 2181843386)
nazalog.Debug(u) rtprtcp.Log.Debug(u)
tt := time.Unix(int64(u/1e9), int64(u%1e9)) 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 return h
} }
// rfc3550 6.4.1 // ParseSr rfc3550 6.4.1
// //
// @param b rtcp包包含包头 // @param b rtcp包包含包头
func ParseSr(b []byte) Sr { func ParseSr(b []byte) Sr {
@ -133,7 +133,7 @@ func ParseSr(b []byte) Sr {
return s return s
} }
// @param out 传出参数,注意,调用方保证长度>=4 // PackTo @param out 传出参数,注意,调用方保证长度>=4
func (r *RtcpHeader) PackTo(out []byte) { func (r *RtcpHeader) PackTo(out []byte) {
out[0] = r.Version<<6 | r.Padding<<5 | r.CountOrFormat out[0] = r.Version<<6 | r.Padding<<5 | r.CountOrFormat
out[1] = r.PacketType out[1] = r.PacketType

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

@ -28,11 +28,11 @@ const (
NaluTypeAvcStapa = 24 // one packet, multiple nals NaluTypeAvcStapa = 24 // one packet, multiple nals
NaluTypeAvcFua = 28 NaluTypeAvcFua = 28
// TODO(chef): hevc有stapa格式吗 // NaluTypeHevcFua TODO(chef): hevc有stapa格式吗
NaluTypeHevcFua = 49 NaluTypeHevcFua = 49
) )
// 比较序号的值,内部处理序号翻转问题,见单元测试中的例子 // CompareSeq 比较序号的值,内部处理序号翻转问题,见单元测试中的例子
// @return 0 a和b相等 // @return 0 a和b相等
// 1 a大于b // 1 a大于b
// -1 a小于b // -1 a小于b
@ -56,7 +56,7 @@ func CompareSeq(a, b uint16) int {
return 1 return 1
} }
// a减b的值内部处理序号翻转问题如果a小于b则返回负值见单元测试中的例子 // SubSeq a减b的值内部处理序号翻转问题如果a小于b则返回负值见单元测试中的例子
func SubSeq(a, b uint16) int { func SubSeq(a, b uint16) int {
if a == b { if a == b {
return 0 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 // pkt.PayloadType rtp包头中的packet type
// //
func (r *RtpPacker) Pack(pkt base.AvPacket) (out []RtpPacket) { func (r *RtpPacker) Pack(pkt base.AvPacket) (out []RtpPacket) {

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

@ -8,8 +8,6 @@
package rtprtcp package rtprtcp
import "github.com/q191201771/naza/pkg/nazalog"
type RtpPackerPayloadAac struct { type RtpPackerPayloadAac struct {
} }
@ -31,7 +29,7 @@ func (r *RtpPackerPayloadAac) Pack(in []byte, maxSize int) (out [][]byte) {
// 第三部分是帧数据的数组 // 第三部分是帧数据的数组
if len(in) > maxSize { 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 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: 内存块为独立新申请;函数返回后,内部不再持有该内存块 // @return out: 内存块为独立新申请;函数返回后,内部不再持有该内存块
// //

@ -13,7 +13,6 @@ import (
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hevc" "github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/naza/pkg/bele" "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 return
} }
// 函数调用结束后,不持有参数<b>的内存块 // ParseRtpPacket 函数调用结束后,不持有参数<b>的内存块
func ParseRtpPacket(b []byte) (pkt RtpPacket, err error) { func ParseRtpPacket(b []byte) (pkt RtpPacket, err error) {
pkt.Header, err = ParseRtpHeader(b) pkt.Header, err = ParseRtpHeader(b)
if err != nil { if err != nil {
@ -127,13 +126,13 @@ func ParseRtpPacket(b []byte) (pkt RtpPacket, err error) {
func (p *RtpPacket) Body() []byte { func (p *RtpPacket) Body() []byte {
if p.Header.payloadOffset == 0 { 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 p.Header.payloadOffset = RtpFixedHeaderLength
} }
return p.Raw[p.Header.payloadOffset:] 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 { func IsAvcHevcBoundary(pkt RtpPacket, pt base.AvPacketPt) bool {
switch pt { switch pt {

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

@ -10,7 +10,6 @@ package rtprtcp
import ( import (
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
) )
// 传入RTP包合成帧数据并回调返回 // 传入RTP包合成帧数据并回调返回
@ -32,10 +31,10 @@ type IRtpUnpackContainer interface {
} }
type IRtpUnpackerProtocol interface { type IRtpUnpackerProtocol interface {
// 计算rtp包处于帧中的位置 // CalcPositionIfNeeded 计算rtp包处于帧中的位置
CalcPositionIfNeeded(pkt *RtpPacket) CalcPositionIfNeeded(pkt *RtpPacket)
// 尝试合成一个完整帧 // TryUnpackOne 尝试合成一个完整帧
// //
// 从当前队列的第一个包开始合成 // 从当前队列的第一个包开始合成
// 如果一个rtp包对应一个完整帧则合成一帧 // 如果一个rtp包对应一个完整帧则合成一帧
@ -47,7 +46,7 @@ type IRtpUnpackerProtocol interface {
TryUnpackOne(list *RtpPacketList) (unpackedFlag bool, unpackedSeq uint16) 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永远相同 // 注意不支持带B帧的视频流pts和dts永远相同
// pkt.PayloadType base.AvPacketPTXXX // pkt.PayloadType base.AvPacketPTXXX
// pkt.Payload AAC: // pkt.Payload AAC:
@ -60,7 +59,7 @@ type IRtpUnpackerProtocol interface {
// 假如sps和pps是一个stapA包则合并结果为一个AvPacket // 假如sps和pps是一个stapA包则合并结果为一个AvPacket
type OnAvPacket func(pkt base.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 { func DefaultRtpUnpackerFactory(payloadType base.AvPacketPt, clockRate int, maxSize int, onAvPacket OnAvPacket) IRtpUnpacker {
var protocol IRtpUnpackerProtocol var protocol IRtpUnpackerProtocol
switch payloadType { switch payloadType {
@ -71,7 +70,7 @@ func DefaultRtpUnpackerFactory(payloadType base.AvPacketPt, clockRate int, maxSi
case base.AvPacketPtHevc: case base.AvPacketPtHevc:
protocol = NewRtpUnpackerAvcHevc(payloadType, clockRate, onAvPacket) protocol = NewRtpUnpackerAvcHevc(payloadType, clockRate, onAvPacket)
default: 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) return NewRtpUnpackContainer(maxSize, protocol)
} }

@ -10,7 +10,6 @@ package rtprtcp
import ( import (
"github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazalog"
) )
type RtpUnpackerAac struct { type RtpUnpackerAac struct {
@ -67,7 +66,7 @@ func (unpacker *RtpUnpackerAac) TryUnpackOne(list *RtpPacketList) (unpackedFlag
return false, 0 return false, 0
} }
b := p.Packet.Raw[p.Packet.Header.payloadOffset:] 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) aus := parseAu(b)
@ -107,7 +106,7 @@ func (unpacker *RtpUnpackerAac) TryUnpackOne(list *RtpPacketList) (unpackedFlag
return false, 0 return false, 0
} }
if p.Packet.Header.Timestamp != timestamp { 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) timestamp, p.Packet.Header.Timestamp)
return false, 0 return false, 0
} }
@ -115,11 +114,11 @@ func (unpacker *RtpUnpackerAac) TryUnpackOne(list *RtpPacketList) (unpackedFlag
b = p.Packet.Raw[p.Packet.Header.payloadOffset:] b = p.Packet.Raw[p.Packet.Header.payloadOffset:]
aus := parseAu(b) aus := parseAu(b)
if len(aus) != 1 { 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 return false, 0
} }
if aus[0].size != totalSize { 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) totalSize, aus[0].size)
return false, 0 return false, 0
} }
@ -142,7 +141,7 @@ func (unpacker *RtpUnpackerAac) TryUnpackOne(list *RtpPacketList) (unpackedFlag
list.Size -= packetCount list.Size -= packetCount
return true, p.Packet.Header.Seq return true, p.Packet.Header.Seq
} else { } 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) cacheSize, totalSize)
return false, 0 return false, 0
} }
@ -190,7 +189,7 @@ func parseAu(b []byte) (ret []au) {
auSize /= 8 auSize /= 8
// 注意fragment时auIndex并不可靠。见TestAacCase1 // 注意fragment时auIndex并不可靠。见TestAacCase1
//auIndex := b[pauh+1] & 0x7 //auIndex := b[pauh+1] & 0x7
//nazalog.Debugf("~ %d %d", auSize, auIndex) //Log.Debugf("~ %d %d", auSize, auIndex)
ret = append(ret, au{ ret = append(ret, au{
size: auSize, size: auSize,
@ -203,7 +202,7 @@ func parseAu(b []byte) (ret []au) {
if (nbAuHeaders > 1 && pau != uint32(len(b))) || if (nbAuHeaders > 1 && pau != uint32(len(b))) ||
(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 return

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