mirror of
https://github.com/cnotch/ipchub.git
synced 2025-10-05 15:36:56 +08:00
Reorganize the structure of the source code
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package av
|
package codec
|
||||||
|
|
||||||
// 帧类型
|
// 帧类型
|
||||||
const (
|
const (
|
@@ -2,15 +2,15 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package av
|
package codec
|
||||||
|
|
||||||
// VideoMeta 视频元数据
|
// VideoMeta 视频元数据
|
||||||
type VideoMeta struct {
|
type VideoMeta struct {
|
||||||
Codec string `json:"codec"`
|
Codec string `json:"codec"`
|
||||||
DataRate float64 `json:"datarate,omitempty"`
|
|
||||||
Width int `json:"width,omitempty"`
|
Width int `json:"width,omitempty"`
|
||||||
Height int `json:"height,omitempty"`
|
Height int `json:"height,omitempty"`
|
||||||
FrameRate float64 `json:"framerate,omitempty"`
|
FrameRate float64 `json:"framerate,omitempty"`
|
||||||
|
DataRate float64 `json:"datarate,omitempty"`
|
||||||
Sps []byte `json:"-"`
|
Sps []byte `json:"-"`
|
||||||
Pps []byte `json:"-"`
|
Pps []byte `json:"-"`
|
||||||
Vps []byte `json:"-"`
|
Vps []byte `json:"-"`
|
||||||
@@ -19,9 +19,9 @@ type VideoMeta struct {
|
|||||||
// AudioMeta 音频元数据
|
// AudioMeta 音频元数据
|
||||||
type AudioMeta struct {
|
type AudioMeta struct {
|
||||||
Codec string `json:"codec"`
|
Codec string `json:"codec"`
|
||||||
DataRate float64 `json:"datarate,omitempty"`
|
|
||||||
SampleRate int `json:"samplerate,omitempty"`
|
SampleRate int `json:"samplerate,omitempty"`
|
||||||
SampleSize int `json:"samplesize,omitempty"`
|
SampleSize int `json:"samplesize,omitempty"`
|
||||||
Channels int `json:"channels,omitempty"`
|
Channels int `json:"channels,omitempty"`
|
||||||
|
DataRate float64 `json:"datarate,omitempty"`
|
||||||
Sps []byte `json:"-"` // sps
|
Sps []byte `json:"-"` // sps
|
||||||
}
|
}
|
@@ -8,9 +8,9 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/av"
|
"github.com/cnotch/ipchub/av/codec"
|
||||||
"github.com/cnotch/ipchub/av/h264"
|
"github.com/cnotch/ipchub/av/codec/h264"
|
||||||
"github.com/cnotch/ipchub/protos/amf"
|
"github.com/cnotch/ipchub/av/format/amf"
|
||||||
"github.com/cnotch/queue"
|
"github.com/cnotch/queue"
|
||||||
"github.com/cnotch/xlog"
|
"github.com/cnotch/xlog"
|
||||||
)
|
)
|
||||||
@@ -24,8 +24,8 @@ const (
|
|||||||
|
|
||||||
// MuxerAvcAac flv muxer from av.Frame(H264[+AAC])
|
// MuxerAvcAac flv muxer from av.Frame(H264[+AAC])
|
||||||
type MuxerAvcAac struct {
|
type MuxerAvcAac struct {
|
||||||
videoMeta av.VideoMeta
|
videoMeta codec.VideoMeta
|
||||||
audioMeta av.AudioMeta
|
audioMeta codec.AudioMeta
|
||||||
typeFlags byte
|
typeFlags byte
|
||||||
audioDataTemplate *AudioData
|
audioDataTemplate *AudioData
|
||||||
recvQueue *queue.SyncQueue
|
recvQueue *queue.SyncQueue
|
||||||
@@ -39,7 +39,7 @@ type MuxerAvcAac struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewMuxerAvcAac .
|
// NewMuxerAvcAac .
|
||||||
func NewMuxerAvcAac(videoMeta av.VideoMeta, audioMeta av.AudioMeta, tagWriter TagWriter, logger *xlog.Logger) *MuxerAvcAac {
|
func NewMuxerAvcAac(videoMeta codec.VideoMeta, audioMeta codec.AudioMeta, tagWriter TagWriter, logger *xlog.Logger) *MuxerAvcAac {
|
||||||
muxer := &MuxerAvcAac{
|
muxer := &MuxerAvcAac{
|
||||||
recvQueue: queue.NewSyncQueue(),
|
recvQueue: queue.NewSyncQueue(),
|
||||||
videoMeta: videoMeta,
|
videoMeta: videoMeta,
|
||||||
@@ -64,7 +64,7 @@ func NewMuxerAvcAac(videoMeta av.VideoMeta, audioMeta av.AudioMeta, tagWriter Ta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteFrame .
|
// WriteFrame .
|
||||||
func (muxer *MuxerAvcAac) WriteFrame(frame *av.Frame) error {
|
func (muxer *MuxerAvcAac) WriteFrame(frame *codec.Frame) error {
|
||||||
muxer.recvQueue.Push(frame)
|
muxer.recvQueue.Push(frame)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -111,12 +111,12 @@ func (muxer *MuxerAvcAac) process() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
frame := f.(*av.Frame)
|
frame := f.(*codec.Frame)
|
||||||
if muxer.basePts == 0 {
|
if muxer.basePts == 0 {
|
||||||
muxer.basePts = frame.AbsTimestamp
|
muxer.basePts = frame.AbsTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
if frame.FrameType == av.FrameVideo {
|
if frame.FrameType == codec.FrameVideo {
|
||||||
if err := muxer.muxVideoTag(frame); err != nil {
|
if err := muxer.muxVideoTag(frame); err != nil {
|
||||||
muxer.logger.Errorf("flvmuxer: muxVideoTag error - %s", err.Error())
|
muxer.logger.Errorf("flvmuxer: muxVideoTag error - %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ func (muxer *MuxerAvcAac) process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (muxer *MuxerAvcAac) muxVideoTag(frame *av.Frame) error {
|
func (muxer *MuxerAvcAac) muxVideoTag(frame *codec.Frame) error {
|
||||||
if frame.Payload[0]&0x1F == h264.NalSps {
|
if frame.Payload[0]&0x1F == h264.NalSps {
|
||||||
if len(muxer.videoMeta.Sps) == 0 {
|
if len(muxer.videoMeta.Sps) == 0 {
|
||||||
muxer.videoMeta.Sps = frame.Payload
|
muxer.videoMeta.Sps = frame.Payload
|
||||||
@@ -181,10 +181,10 @@ func (muxer *MuxerAvcAac) muxVideoTag(frame *av.Frame) error {
|
|||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
return muxer.tagWriter.WriteTag(tag)
|
return muxer.tagWriter.WriteFlvTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (muxer *MuxerAvcAac) muxAudioTag(frame *av.Frame) error {
|
func (muxer *MuxerAvcAac) muxAudioTag(frame *codec.Frame) error {
|
||||||
audioData := *muxer.audioDataTemplate
|
audioData := *muxer.audioDataTemplate
|
||||||
audioData.Body = frame.Payload
|
audioData.Body = frame.Payload
|
||||||
data, _ := audioData.Marshal()
|
data, _ := audioData.Marshal()
|
||||||
@@ -196,7 +196,7 @@ func (muxer *MuxerAvcAac) muxAudioTag(frame *av.Frame) error {
|
|||||||
StreamID: 0,
|
StreamID: 0,
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
return muxer.tagWriter.WriteTag(tag)
|
return muxer.tagWriter.WriteFlvTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (muxer *MuxerAvcAac) muxMetadataTag() error {
|
func (muxer *MuxerAvcAac) muxMetadataTag() error {
|
||||||
@@ -269,7 +269,7 @@ func (muxer *MuxerAvcAac) muxMetadataTag() error {
|
|||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
return muxer.tagWriter.WriteTag(tag)
|
return muxer.tagWriter.WriteFlvTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (muxer *MuxerAvcAac) muxSequenceHeaderTag() error {
|
func (muxer *MuxerAvcAac) muxSequenceHeaderTag() error {
|
||||||
@@ -304,7 +304,7 @@ func (muxer *MuxerAvcAac) muxSequenceHeaderTag() error {
|
|||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := muxer.tagWriter.WriteTag(tag); err != nil {
|
if err := muxer.tagWriter.WriteFlvTag(tag); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +328,7 @@ func (muxer *MuxerAvcAac) muxAudioSequenceHeaderTag() error {
|
|||||||
StreamID: 0,
|
StreamID: 0,
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
return muxer.tagWriter.WriteTag(tag)
|
return muxer.tagWriter.WriteFlvTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (muxer *MuxerAvcAac) prepareTemplate() {
|
func (muxer *MuxerAvcAac) prepareTemplate() {
|
@@ -7,7 +7,7 @@ package flv
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/protos/amf"
|
"github.com/cnotch/ipchub/av/format/amf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 数据名称常量,如元数据
|
// 数据名称常量,如元数据
|
@@ -9,7 +9,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/protos/amf"
|
"github.com/cnotch/ipchub/av/format/amf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// flv 标记类型ID
|
// flv 标记类型ID
|
||||||
@@ -42,7 +42,7 @@ type Tag struct {
|
|||||||
|
|
||||||
// TagWriter 包装 WriteTag 方法的接口
|
// TagWriter 包装 WriteTag 方法的接口
|
||||||
type TagWriter interface {
|
type TagWriter interface {
|
||||||
WriteTag(tag *Tag) error
|
WriteFlvTag(tag *Tag) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size tag 的总大小(包括 Header + Data)
|
// Size tag 的总大小(包括 Header + Data)
|
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
package hls
|
package hls
|
||||||
|
|
||||||
import "github.com/cnotch/ipchub/av/aac"
|
import "github.com/cnotch/ipchub/av/codec/aac"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// in ms, for HLS aac sync time.
|
// in ms, for HLS aac sync time.
|
@@ -16,7 +16,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/protos/mpegts"
|
"github.com/cnotch/ipchub/av/format/mpegts"
|
||||||
"github.com/cnotch/ipchub/utils/murmur"
|
"github.com/cnotch/ipchub/utils/murmur"
|
||||||
"github.com/cnotch/xlog"
|
"github.com/cnotch/xlog"
|
||||||
)
|
)
|
@@ -14,7 +14,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/protos/mpegts"
|
"github.com/cnotch/ipchub/av/format/mpegts"
|
||||||
)
|
)
|
||||||
|
|
||||||
type segmentFile interface {
|
type segmentFile interface {
|
@@ -7,8 +7,8 @@
|
|||||||
package mpegts
|
package mpegts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cnotch/ipchub/av/aac"
|
"github.com/cnotch/ipchub/av/codec/aac"
|
||||||
"github.com/cnotch/ipchub/av/h264"
|
"github.com/cnotch/ipchub/av/codec/h264"
|
||||||
)
|
)
|
||||||
|
|
||||||
// the mpegts header specifed the video/audio pid.
|
// the mpegts header specifed the video/audio pid.
|
@@ -9,9 +9,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/av"
|
"github.com/cnotch/ipchub/av/codec"
|
||||||
"github.com/cnotch/ipchub/av/aac"
|
"github.com/cnotch/ipchub/av/codec/aac"
|
||||||
"github.com/cnotch/ipchub/av/h264"
|
"github.com/cnotch/ipchub/av/codec/h264"
|
||||||
"github.com/cnotch/queue"
|
"github.com/cnotch/queue"
|
||||||
"github.com/cnotch/xlog"
|
"github.com/cnotch/xlog"
|
||||||
)
|
)
|
||||||
@@ -25,8 +25,8 @@ const (
|
|||||||
|
|
||||||
// MuxerAvcAac flv muxer from av.Frame(H264[+AAC])
|
// MuxerAvcAac flv muxer from av.Frame(H264[+AAC])
|
||||||
type MuxerAvcAac struct {
|
type MuxerAvcAac struct {
|
||||||
videoMeta av.VideoMeta
|
videoMeta codec.VideoMeta
|
||||||
audioMeta av.AudioMeta
|
audioMeta codec.AudioMeta
|
||||||
hasAudio bool
|
hasAudio bool
|
||||||
audioSps aac.RawSPS
|
audioSps aac.RawSPS
|
||||||
recvQueue *queue.SyncQueue
|
recvQueue *queue.SyncQueue
|
||||||
@@ -39,7 +39,7 @@ type MuxerAvcAac struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewMuxerAvcAac .
|
// NewMuxerAvcAac .
|
||||||
func NewMuxerAvcAac(videoMeta av.VideoMeta, audioMeta av.AudioMeta, tsframeWriter FrameWriter, logger *xlog.Logger) (*MuxerAvcAac, error) {
|
func NewMuxerAvcAac(videoMeta codec.VideoMeta, audioMeta codec.AudioMeta, tsframeWriter FrameWriter, logger *xlog.Logger) (*MuxerAvcAac, error) {
|
||||||
muxer := &MuxerAvcAac{
|
muxer := &MuxerAvcAac{
|
||||||
recvQueue: queue.NewSyncQueue(),
|
recvQueue: queue.NewSyncQueue(),
|
||||||
videoMeta: videoMeta,
|
videoMeta: videoMeta,
|
||||||
@@ -84,7 +84,7 @@ func (muxer *MuxerAvcAac) prepareAacSps() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteFrame .
|
// WriteFrame .
|
||||||
func (muxer *MuxerAvcAac) WriteFrame(frame *av.Frame) error {
|
func (muxer *MuxerAvcAac) WriteFrame(frame *codec.Frame) error {
|
||||||
muxer.recvQueue.Push(frame)
|
muxer.recvQueue.Push(frame)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -126,12 +126,12 @@ func (muxer *MuxerAvcAac) process() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
frame := f.(*av.Frame)
|
frame := f.(*codec.Frame)
|
||||||
if muxer.basePts == 0 {
|
if muxer.basePts == 0 {
|
||||||
muxer.basePts = frame.AbsTimestamp
|
muxer.basePts = frame.AbsTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
if frame.FrameType == av.FrameVideo {
|
if frame.FrameType == codec.FrameVideo {
|
||||||
if err := muxer.muxVideoTag(frame); err != nil {
|
if err := muxer.muxVideoTag(frame); err != nil {
|
||||||
muxer.logger.Errorf("tsmuxer: muxVideoFrame error - %s", err.Error())
|
muxer.logger.Errorf("tsmuxer: muxVideoFrame error - %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ func (muxer *MuxerAvcAac) process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (muxer *MuxerAvcAac) muxVideoTag(frame *av.Frame) (err error) {
|
func (muxer *MuxerAvcAac) muxVideoTag(frame *codec.Frame) (err error) {
|
||||||
if frame.Payload[0]&0x1F == h264.NalSps {
|
if frame.Payload[0]&0x1F == h264.NalSps {
|
||||||
if len(muxer.videoMeta.Sps) == 0 {
|
if len(muxer.videoMeta.Sps) == 0 {
|
||||||
muxer.videoMeta.Sps = frame.Payload
|
muxer.videoMeta.Sps = frame.Payload
|
||||||
@@ -190,7 +190,7 @@ func (muxer *MuxerAvcAac) muxVideoTag(frame *av.Frame) (err error) {
|
|||||||
return muxer.tsframeWriter.WriteMpegtsFrame(tsframe)
|
return muxer.tsframeWriter.WriteMpegtsFrame(tsframe)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (muxer *MuxerAvcAac) muxAudioTag(frame *av.Frame) error {
|
func (muxer *MuxerAvcAac) muxAudioTag(frame *codec.Frame) error {
|
||||||
pts := frame.AbsTimestamp - muxer.basePts + ptsDelay
|
pts := frame.AbsTimestamp - muxer.basePts + ptsDelay
|
||||||
pts *= 90
|
pts *= 90
|
||||||
|
|
@@ -2,9 +2,9 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package protos
|
package format
|
||||||
|
|
||||||
// Pack 表示流媒体包
|
// Package 表示流媒体包
|
||||||
type Pack interface {
|
type Package interface {
|
||||||
Size() int // 包长度
|
Size() int // 包长度
|
||||||
}
|
}
|
104
av/format/rtp/demuxer.go
Normal file
104
av/format/rtp/demuxer.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Copyright (c) 2019,CAOHONGJU All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package rtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime/debug"
|
||||||
|
|
||||||
|
"github.com/cnotch/queue"
|
||||||
|
"github.com/cnotch/xlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Depacketizer 解包器
|
||||||
|
type Depacketizer interface {
|
||||||
|
Control(p *Packet) error
|
||||||
|
Depacketize(p *Packet) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Demuxer 帧转换器
|
||||||
|
type Demuxer struct {
|
||||||
|
closed bool
|
||||||
|
recvQueue *queue.SyncQueue
|
||||||
|
depacketizeFuncs [4]func(packet *Packet) error
|
||||||
|
logger *xlog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func emptyDepacketize(*Packet) error { return nil }
|
||||||
|
|
||||||
|
// NewDemuxer 创建 rtp.Packet 解封装处理器。
|
||||||
|
func NewDemuxer(videoDepacketizer Depacketizer, audioDepacketizer Depacketizer, logger *xlog.Logger) *Demuxer {
|
||||||
|
fc := &Demuxer{
|
||||||
|
recvQueue: queue.NewSyncQueue(),
|
||||||
|
closed: false,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
if videoDepacketizer != nil {
|
||||||
|
fc.depacketizeFuncs[ChannelVideo] = videoDepacketizer.Depacketize
|
||||||
|
fc.depacketizeFuncs[ChannelVideoControl] = videoDepacketizer.Control
|
||||||
|
} else {
|
||||||
|
fc.depacketizeFuncs[ChannelVideo] = emptyDepacketize
|
||||||
|
fc.depacketizeFuncs[ChannelVideoControl] = emptyDepacketize
|
||||||
|
}
|
||||||
|
|
||||||
|
if audioDepacketizer != nil {
|
||||||
|
fc.depacketizeFuncs[ChannelAudio] = audioDepacketizer.Depacketize
|
||||||
|
fc.depacketizeFuncs[ChannelAudioControl] = audioDepacketizer.Control
|
||||||
|
} else {
|
||||||
|
fc.depacketizeFuncs[ChannelAudio] = emptyDepacketize
|
||||||
|
fc.depacketizeFuncs[ChannelAudioControl] = emptyDepacketize
|
||||||
|
}
|
||||||
|
|
||||||
|
go fc.convert()
|
||||||
|
return fc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dm *Demuxer) convert() {
|
||||||
|
defer func() {
|
||||||
|
defer func() { // 避免 handler 再 panic
|
||||||
|
recover()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
dm.logger.Errorf("FrameConverter routine panic;r = %v \n %s", r, debug.Stack())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尽早通知GC,回收内存
|
||||||
|
dm.recvQueue.Reset()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for !dm.closed {
|
||||||
|
p := dm.recvQueue.Pop()
|
||||||
|
if p == nil {
|
||||||
|
if !dm.closed {
|
||||||
|
dm.logger.Warn("FrameConverter:receive nil packet")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
packet := p.(*Packet)
|
||||||
|
if err := dm.depacketizeFuncs[int(packet.Channel)](packet); err != nil {
|
||||||
|
dm.logger.Errorf("FrameConverter: extract rtp frame error :%s", err.Error())
|
||||||
|
// break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close .
|
||||||
|
func (dm *Demuxer) Close() error {
|
||||||
|
if dm.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dm.closed = true
|
||||||
|
dm.recvQueue.Signal()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WritePacket .
|
||||||
|
func (fc *Demuxer) WriteRtpPacket(packet *Packet) error {
|
||||||
|
fc.recvQueue.Push(packet)
|
||||||
|
return nil
|
||||||
|
}
|
@@ -7,19 +7,19 @@ package rtp
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/av"
|
"github.com/cnotch/ipchub/av/codec"
|
||||||
"github.com/cnotch/ipchub/av/h264"
|
"github.com/cnotch/ipchub/av/codec/h264"
|
||||||
)
|
)
|
||||||
|
|
||||||
type h264FrameExtractor struct {
|
type h264Depacketizer struct {
|
||||||
fragments []*Packet // 分片包
|
fragments []*Packet // 分片包
|
||||||
w av.FrameWriter
|
w codec.FrameWriter
|
||||||
syncClock SyncClock
|
syncClock SyncClock
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewH264FrameExtractor 实例化 H264 帧提取器
|
// NewH264Depacketize 实例化 H264 帧提取器
|
||||||
func NewH264FrameExtractor(w av.FrameWriter) FrameExtractor {
|
func NewH264Depacketize(w codec.FrameWriter) Depacketizer {
|
||||||
fe := &h264FrameExtractor{
|
fe := &h264Depacketizer{
|
||||||
fragments: make([]*Packet, 0, 16),
|
fragments: make([]*Packet, 0, 16),
|
||||||
w: w,
|
w: w,
|
||||||
}
|
}
|
||||||
@@ -27,13 +27,13 @@ func NewH264FrameExtractor(w av.FrameWriter) FrameExtractor {
|
|||||||
return fe
|
return fe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *h264FrameExtractor) Control(p *Packet) error {
|
func (h264dp *h264Depacketizer) Control(p *Packet) error {
|
||||||
fe.syncClock.Decode(p.Data)
|
h264dp.syncClock.Decode(p.Data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *h264FrameExtractor) Extract(packet *Packet) (err error) {
|
func (h264dp *h264Depacketizer) Depacketize(packet *Packet) (err error) {
|
||||||
if fe.syncClock.NTPTime == 0 { // 未收到同步时钟信息,忽略任意包
|
if h264dp.syncClock.NTPTime == 0 { // 未收到同步时钟信息,忽略任意包
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,23 +66,23 @@ func (fe *h264FrameExtractor) Extract(packet *Packet) (err error) {
|
|||||||
if payload[0]&0x1f == h264.NalFillerData {
|
if payload[0]&0x1f == h264.NalFillerData {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
frame := &av.Frame{
|
frame := &codec.Frame{
|
||||||
FrameType: av.FrameVideo,
|
FrameType: codec.FrameVideo,
|
||||||
AbsTimestamp: fe.rtp2ntp(packet.Timestamp),
|
AbsTimestamp: h264dp.rtp2ntp(packet.Timestamp),
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}
|
}
|
||||||
err = fe.w.WriteFrame(frame)
|
err = h264dp.w.WriteFrame(frame)
|
||||||
case naluType == h264.NalStapaInRtp:
|
case naluType == h264.NalStapaInRtp:
|
||||||
err = fe.extractStapa(packet)
|
err = h264dp.depacketizeStapa(packet)
|
||||||
case naluType == h264.NalFuAInRtp:
|
case naluType == h264.NalFuAInRtp:
|
||||||
err = fe.extractFuA(packet)
|
err = h264dp.depacketizeFuA(packet)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("nalu type %d is currently not handled", naluType)
|
err = fmt.Errorf("nalu type %d is currently not handled", naluType)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *h264FrameExtractor) extractStapa(packet *Packet) (err error) {
|
func (h264dp *h264Depacketizer) depacketizeStapa(packet *Packet) (err error) {
|
||||||
payload := packet.Payload()
|
payload := packet.Payload()
|
||||||
header := payload[0]
|
header := payload[0]
|
||||||
|
|
||||||
@@ -112,14 +112,14 @@ func (fe *h264FrameExtractor) extractStapa(packet *Packet) (err error) {
|
|||||||
|
|
||||||
off += 2
|
off += 2
|
||||||
if payload[off]&0x1f != h264.NalFillerData {
|
if payload[off]&0x1f != h264.NalFillerData {
|
||||||
frame := &av.Frame{
|
frame := &codec.Frame{
|
||||||
FrameType: av.FrameVideo,
|
FrameType: codec.FrameVideo,
|
||||||
AbsTimestamp: fe.rtp2ntp(packet.Timestamp),
|
AbsTimestamp: h264dp.rtp2ntp(packet.Timestamp),
|
||||||
Payload: make([]byte, nalSize),
|
Payload: make([]byte, nalSize),
|
||||||
}
|
}
|
||||||
copy(frame.Payload, payload[off:])
|
copy(frame.Payload, payload[off:])
|
||||||
frame.Payload[0] = 0 | (header & 0x60) | (frame.Payload[0] & 0x1F)
|
frame.Payload[0] = 0 | (header & 0x60) | (frame.Payload[0] & 0x1F)
|
||||||
if err = fe.w.WriteFrame(frame); err != nil {
|
if err = h264dp.w.WriteFrame(frame); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ func (fe *h264FrameExtractor) extractStapa(packet *Packet) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *h264FrameExtractor) extractFuA(packet *Packet) (err error) {
|
func (h264dp *h264Depacketizer) depacketizeFuA(packet *Packet) (err error) {
|
||||||
payload := packet.Payload()
|
payload := packet.Payload()
|
||||||
header := payload[0]
|
header := payload[0]
|
||||||
|
|
||||||
@@ -157,45 +157,45 @@ func (fe *h264FrameExtractor) extractFuA(packet *Packet) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fuHeader>>7)&1 == 1 { // 第一个分片包
|
if (fuHeader>>7)&1 == 1 { // 第一个分片包
|
||||||
fe.fragments = fe.fragments[:0]
|
h264dp.fragments = h264dp.fragments[:0]
|
||||||
}
|
}
|
||||||
if len(fe.fragments) != 0 &&
|
if len(h264dp.fragments) != 0 &&
|
||||||
fe.fragments[len(fe.fragments)-1].SequenceNumber != packet.SequenceNumber-1 {
|
h264dp.fragments[len(h264dp.fragments)-1].SequenceNumber != packet.SequenceNumber-1 {
|
||||||
// Packet loss ?
|
// Packet loss ?
|
||||||
fe.fragments = fe.fragments[:0]
|
h264dp.fragments = h264dp.fragments[:0]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 缓存片段
|
// 缓存片段
|
||||||
fe.fragments = append(fe.fragments, packet)
|
h264dp.fragments = append(h264dp.fragments, packet)
|
||||||
|
|
||||||
if (fuHeader>>6)&1 == 1 { // 最后一个片段
|
if (fuHeader>>6)&1 == 1 { // 最后一个片段
|
||||||
frameLen := 1 // 计数帧总长,初始 naluType header len
|
frameLen := 1 // 计数帧总长,初始 naluType header len
|
||||||
for _, fragment := range fe.fragments {
|
for _, fragment := range h264dp.fragments {
|
||||||
frameLen += len(fragment.Payload()) - 2
|
frameLen += len(fragment.Payload()) - 2
|
||||||
}
|
}
|
||||||
|
|
||||||
frame := &av.Frame{
|
frame := &codec.Frame{
|
||||||
FrameType: av.FrameVideo,
|
FrameType: codec.FrameVideo,
|
||||||
AbsTimestamp: fe.rtp2ntp(packet.Timestamp),
|
AbsTimestamp: h264dp.rtp2ntp(packet.Timestamp),
|
||||||
Payload: make([]byte, frameLen)}
|
Payload: make([]byte, frameLen)}
|
||||||
|
|
||||||
frame.Payload[0] = (header & 0x60) | (fuHeader & 0x1F)
|
frame.Payload[0] = (header & 0x60) | (fuHeader & 0x1F)
|
||||||
offset := 1
|
offset := 1
|
||||||
for _, fragment := range fe.fragments {
|
for _, fragment := range h264dp.fragments {
|
||||||
payload := fragment.Payload()[2:]
|
payload := fragment.Payload()[2:]
|
||||||
copy(frame.Payload[offset:], payload)
|
copy(frame.Payload[offset:], payload)
|
||||||
offset += len(payload)
|
offset += len(payload)
|
||||||
}
|
}
|
||||||
// 清空分片缓存
|
// 清空分片缓存
|
||||||
fe.fragments = fe.fragments[:0]
|
h264dp.fragments = h264dp.fragments[:0]
|
||||||
|
|
||||||
err = fe.w.WriteFrame(frame)
|
err = h264dp.w.WriteFrame(frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *h264FrameExtractor) rtp2ntp(timestamp uint32) int64 {
|
func (h264dp *h264Depacketizer) rtp2ntp(timestamp uint32) int64 {
|
||||||
return fe.syncClock.Rtp2Ntp(timestamp)
|
return h264dp.syncClock.Rtp2Ntp(timestamp)
|
||||||
}
|
}
|
@@ -5,21 +5,21 @@
|
|||||||
package rtp
|
package rtp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cnotch/ipchub/av"
|
"github.com/cnotch/ipchub/av/codec"
|
||||||
"github.com/cnotch/ipchub/av/aac"
|
"github.com/cnotch/ipchub/av/codec/aac"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mpesFrameExtractor struct {
|
type aacDepacketizer struct {
|
||||||
w av.FrameWriter
|
w codec.FrameWriter
|
||||||
sizeLength int
|
sizeLength int
|
||||||
indexLength int
|
indexLength int
|
||||||
// extractFunc func(packet *Packet) error
|
// extractFunc func(packet *Packet) error
|
||||||
syncClock SyncClock
|
syncClock SyncClock
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMPESFrameExtractor 实例化 MPES 帧提取器
|
// NewAacDepacketizer 实例化 AAC 解包器
|
||||||
func NewMPESFrameExtractor(w av.FrameWriter, rtpTimeUnit int) FrameExtractor {
|
func NewAacDepacketizer(w codec.FrameWriter, rtpTimeUnit int) Depacketizer {
|
||||||
fe := &mpesFrameExtractor{
|
fe := &aacDepacketizer{
|
||||||
w: w,
|
w: w,
|
||||||
sizeLength: 13,
|
sizeLength: 13,
|
||||||
indexLength: 3,
|
indexLength: 3,
|
||||||
@@ -28,8 +28,8 @@ func NewMPESFrameExtractor(w av.FrameWriter, rtpTimeUnit int) FrameExtractor {
|
|||||||
return fe
|
return fe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *mpesFrameExtractor) Control(p *Packet) error {
|
func (aacdp *aacDepacketizer) Control(p *Packet) error {
|
||||||
fe.syncClock.Decode(p.Data)
|
aacdp.syncClock.Decode(p.Data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,14 +51,14 @@ func (fe *mpesFrameExtractor) Control(p *Packet) error {
|
|||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
|
||||||
// 当 sizelength=6;indexlength=2;indexdeltalength=2 时
|
// 当 sizelength=6;indexlength=2;indexdeltalength=2 时
|
||||||
// 单帧封装时,rtp payload的长度 = AU-header-lengths(两个字节) + AU-header(6+2) + AU的长度
|
// 单帧封装时,rtp payload的长度 = AU-header-lengths(两个字节) + AU-header(6+2) + AU的长度
|
||||||
func (fe *mpesFrameExtractor) Extract(packet *Packet) (err error) {
|
func (aacdp *aacDepacketizer) Depacketize(packet *Packet) (err error) {
|
||||||
if fe.syncClock.NTPTime == 0 { // 未收到同步时钟信息,忽略任意包
|
if aacdp.syncClock.NTPTime == 0 { // 未收到同步时钟信息,忽略任意包
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return fe.extractFor2ByteAUHeader(packet)
|
return aacdp.depacketizeFor2ByteAUHeader(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *mpesFrameExtractor) extractFor2ByteAUHeader(packet *Packet) (err error) {
|
func (aacdp *aacDepacketizer) depacketizeFor2ByteAUHeader(packet *Packet) (err error) {
|
||||||
payload := packet.Payload()
|
payload := packet.Payload()
|
||||||
|
|
||||||
// AU-headers-length 2bytes
|
// AU-headers-length 2bytes
|
||||||
@@ -73,13 +73,13 @@ func (fe *mpesFrameExtractor) extractFor2ByteAUHeader(packet *Packet) (err error
|
|||||||
frameTimeStamp := packet.Timestamp
|
frameTimeStamp := packet.Timestamp
|
||||||
for i := 0; i < int(auHeadersCount); i++ {
|
for i := 0; i < int(auHeadersCount); i++ {
|
||||||
auHeader := uint16(0) | (uint16(auHeaders[0]) << 8) | uint16(auHeaders[1])
|
auHeader := uint16(0) | (uint16(auHeaders[0]) << 8) | uint16(auHeaders[1])
|
||||||
frameSize := auHeader >> fe.indexLength
|
frameSize := auHeader >> aacdp.indexLength
|
||||||
frame := &av.Frame{
|
frame := &codec.Frame{
|
||||||
FrameType: av.FrameAudio,
|
FrameType: codec.FrameAudio,
|
||||||
AbsTimestamp: fe.rtp2ntp(frameTimeStamp),
|
AbsTimestamp: aacdp.rtp2ntp(frameTimeStamp),
|
||||||
Payload: framesPayload[:frameSize],
|
Payload: framesPayload[:frameSize],
|
||||||
}
|
}
|
||||||
if err = fe.w.WriteFrame(frame); err != nil {
|
if err = aacdp.w.WriteFrame(frame); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ func (fe *mpesFrameExtractor) extractFor2ByteAUHeader(packet *Packet) (err error
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *mpesFrameExtractor) extractFor1ByteAUHeader(packet *Packet) (err error) {
|
func (aacdp *aacDepacketizer) depacketizeFor1ByteAUHeader(packet *Packet) (err error) {
|
||||||
payload := packet.Payload()
|
payload := packet.Payload()
|
||||||
|
|
||||||
// AU-headers-length 2bytes
|
// AU-headers-length 2bytes
|
||||||
@@ -107,13 +107,13 @@ func (fe *mpesFrameExtractor) extractFor1ByteAUHeader(packet *Packet) (err error
|
|||||||
frameTimeStamp := packet.Timestamp
|
frameTimeStamp := packet.Timestamp
|
||||||
for i := 0; i < int(auHeadersCount); i++ {
|
for i := 0; i < int(auHeadersCount); i++ {
|
||||||
auHeader := auHeaders[0]
|
auHeader := auHeaders[0]
|
||||||
frameSize := auHeader >> fe.indexLength
|
frameSize := auHeader >> aacdp.indexLength
|
||||||
frame := &av.Frame{
|
frame := &codec.Frame{
|
||||||
FrameType: av.FrameAudio,
|
FrameType: codec.FrameAudio,
|
||||||
AbsTimestamp: fe.rtp2ntp(frameTimeStamp),
|
AbsTimestamp: aacdp.rtp2ntp(frameTimeStamp),
|
||||||
Payload: framesPayload[:frameSize],
|
Payload: framesPayload[:frameSize],
|
||||||
}
|
}
|
||||||
if err = fe.w.WriteFrame(frame); err != nil {
|
if err = aacdp.w.WriteFrame(frame); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +126,6 @@ func (fe *mpesFrameExtractor) extractFor1ByteAUHeader(packet *Packet) (err error
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *mpesFrameExtractor) rtp2ntp(timestamp uint32) int64 {
|
func (aacdp *aacDepacketizer) rtp2ntp(timestamp uint32) int64 {
|
||||||
return fe.syncClock.Rtp2Ntp(timestamp)
|
return aacdp.syncClock.Rtp2Ntp(timestamp)
|
||||||
}
|
}
|
@@ -60,7 +60,7 @@ type Packet struct {
|
|||||||
|
|
||||||
// PacketWriter 包装 WritePacket 方法的接口
|
// PacketWriter 包装 WritePacket 方法的接口
|
||||||
type PacketWriter interface {
|
type PacketWriter interface {
|
||||||
WritePacket(packet *Packet) error
|
WriteRtpPacket(packet *Packet) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadPacket 根据规范从 r 中读取 rtp 包.
|
// ReadPacket 根据规范从 r 中读取 rtp 包.
|
2
media/cache/flvcache.go
vendored
2
media/cache/flvcache.go
vendored
@@ -7,7 +7,7 @@ package cache
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/protos/flv"
|
"github.com/cnotch/ipchub/av/format/flv"
|
||||||
"github.com/cnotch/queue"
|
"github.com/cnotch/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
4
media/cache/h264cache.go
vendored
4
media/cache/h264cache.go
vendored
@@ -7,8 +7,8 @@ package cache
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/av/h264"
|
"github.com/cnotch/ipchub/av/codec/h264"
|
||||||
"github.com/cnotch/ipchub/protos/rtp"
|
"github.com/cnotch/ipchub/av/format/rtp"
|
||||||
"github.com/cnotch/queue"
|
"github.com/cnotch/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
4
media/cache/hevccache.go
vendored
4
media/cache/hevccache.go
vendored
@@ -7,8 +7,8 @@ package cache
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/av/hevc"
|
"github.com/cnotch/ipchub/av/codec/hevc"
|
||||||
"github.com/cnotch/ipchub/protos/rtp"
|
"github.com/cnotch/ipchub/av/format/rtp"
|
||||||
"github.com/cnotch/queue"
|
"github.com/cnotch/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
4
media/cache/types.go
vendored
4
media/cache/types.go
vendored
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
package cache
|
package cache
|
||||||
|
|
||||||
import "github.com/cnotch/ipchub/protos"
|
import "github.com/cnotch/ipchub/av/format"
|
||||||
|
|
||||||
// Pack .
|
// Pack .
|
||||||
type Pack = protos.Pack
|
type Pack = format.Package
|
||||||
|
@@ -9,14 +9,14 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/av"
|
"github.com/cnotch/ipchub/av/codec"
|
||||||
"github.com/cnotch/ipchub/av/aac"
|
"github.com/cnotch/ipchub/av/codec/aac"
|
||||||
"github.com/cnotch/ipchub/av/h264"
|
"github.com/cnotch/ipchub/av/codec/h264"
|
||||||
"github.com/cnotch/ipchub/utils/scan"
|
"github.com/cnotch/ipchub/utils/scan"
|
||||||
"github.com/pixelbender/go-sdp/sdp"
|
"github.com/pixelbender/go-sdp/sdp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseMeta(rawsdp string, video *av.VideoMeta, audio *av.AudioMeta) {
|
func parseMeta(rawsdp string, video *codec.VideoMeta, audio *codec.AudioMeta) {
|
||||||
sdp, err := sdp.ParseString(rawsdp)
|
sdp, err := sdp.ParseString(rawsdp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -53,7 +53,7 @@ func parseMeta(rawsdp string, video *av.VideoMeta, audio *av.AudioMeta) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAudioMeta(m *sdp.Format, audio *av.AudioMeta) {
|
func parseAudioMeta(m *sdp.Format, audio *codec.AudioMeta) {
|
||||||
audio.SampleRate = 44100
|
audio.SampleRate = 44100
|
||||||
audio.Channels = 2
|
audio.Channels = 2
|
||||||
audio.SampleSize = 16
|
audio.SampleSize = 16
|
||||||
@@ -97,7 +97,7 @@ func parseAudioMeta(m *sdp.Format, audio *av.AudioMeta) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseVideoMeta(m *sdp.Format, video *av.VideoMeta) {
|
func parseVideoMeta(m *sdp.Format, video *codec.VideoMeta) {
|
||||||
if len(m.Params) == 0 {
|
if len(m.Params) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ func parseVideoMeta(m *sdp.Format, video *av.VideoMeta) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseH264SpsPps(s string, video *av.VideoMeta) {
|
func parseH264SpsPps(s string, video *codec.VideoMeta) {
|
||||||
ppsStr, spsStr, ok := scan.Comma.Scan(s)
|
ppsStr, spsStr, ok := scan.Comma.Scan(s)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@@ -7,14 +7,14 @@ package media
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/av"
|
"github.com/cnotch/ipchub/av/codec"
|
||||||
"github.com/cnotch/ipchub/protos"
|
"github.com/cnotch/ipchub/av/format"
|
||||||
"github.com/cnotch/ipchub/protos/rtp"
|
"github.com/cnotch/ipchub/av/format/rtp"
|
||||||
"github.com/cnotch/queue"
|
"github.com/cnotch/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pack .
|
// Pack .
|
||||||
type Pack = protos.Pack
|
type Pack = format.Package
|
||||||
|
|
||||||
type packCache interface {
|
type packCache interface {
|
||||||
CachePack(pack Pack)
|
CachePack(pack Pack)
|
||||||
@@ -33,7 +33,7 @@ func (emptyCache) Reset() {}
|
|||||||
|
|
||||||
type flvMuxer interface {
|
type flvMuxer interface {
|
||||||
TypeFlags() byte
|
TypeFlags() byte
|
||||||
av.FrameWriter
|
codec.FrameWriter
|
||||||
io.Closer
|
io.Closer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ var _ flvMuxer = emptyFlvMuxer{}
|
|||||||
type emptyFlvMuxer struct{}
|
type emptyFlvMuxer struct{}
|
||||||
|
|
||||||
func (emptyFlvMuxer) TypeFlags() byte { return 0 }
|
func (emptyFlvMuxer) TypeFlags() byte { return 0 }
|
||||||
func (emptyFlvMuxer) WriteFrame(frame *av.Frame) error { return nil }
|
func (emptyFlvMuxer) WriteFrame(frame *codec.Frame) error { return nil }
|
||||||
func (emptyFlvMuxer) Close() error { return nil }
|
func (emptyFlvMuxer) Close() error { return nil }
|
||||||
|
|
||||||
type frameConverter interface {
|
type frameConverter interface {
|
||||||
@@ -55,5 +55,5 @@ var _ frameConverter = emptyFrameConverter{}
|
|||||||
type emptyFrameConverter struct{}
|
type emptyFrameConverter struct{}
|
||||||
|
|
||||||
func (emptyFrameConverter) TypeFlags() byte { return 0 }
|
func (emptyFrameConverter) TypeFlags() byte { return 0 }
|
||||||
func (emptyFrameConverter) WritePacket(*rtp.Packet) error { return nil }
|
func (emptyFrameConverter) WriteRtpPacket(*rtp.Packet) error { return nil }
|
||||||
func (emptyFrameConverter) Close() error { return nil }
|
func (emptyFrameConverter) Close() error { return nil }
|
||||||
|
@@ -10,13 +10,13 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/av"
|
"github.com/cnotch/ipchub/av/codec"
|
||||||
|
"github.com/cnotch/ipchub/av/format/flv"
|
||||||
|
"github.com/cnotch/ipchub/av/format/hls"
|
||||||
|
"github.com/cnotch/ipchub/av/format/mpegts"
|
||||||
|
"github.com/cnotch/ipchub/av/format/rtp"
|
||||||
"github.com/cnotch/ipchub/config"
|
"github.com/cnotch/ipchub/config"
|
||||||
"github.com/cnotch/ipchub/media/cache"
|
"github.com/cnotch/ipchub/media/cache"
|
||||||
"github.com/cnotch/ipchub/protos/flv"
|
|
||||||
"github.com/cnotch/ipchub/protos/hls"
|
|
||||||
"github.com/cnotch/ipchub/protos/mpegts"
|
|
||||||
"github.com/cnotch/ipchub/protos/rtp"
|
|
||||||
"github.com/cnotch/ipchub/stats"
|
"github.com/cnotch/ipchub/stats"
|
||||||
"github.com/cnotch/ipchub/utils"
|
"github.com/cnotch/ipchub/utils"
|
||||||
"github.com/cnotch/queue"
|
"github.com/cnotch/queue"
|
||||||
@@ -60,8 +60,8 @@ type Stream struct {
|
|||||||
multicast Multicastable
|
multicast Multicastable
|
||||||
hls Hlsable
|
hls Hlsable
|
||||||
logger *xlog.Logger // 日志对象
|
logger *xlog.Logger // 日志对象
|
||||||
Video av.VideoMeta
|
Video codec.VideoMeta
|
||||||
Audio av.AudioMeta
|
Audio codec.AudioMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStream 创建新的流
|
// NewStream 创建新的流
|
||||||
@@ -101,17 +101,17 @@ func NewStream(path string, rawsdp string, options ...Option) *Stream {
|
|||||||
func (s *Stream) prepareOtherStream() {
|
func (s *Stream) prepareOtherStream() {
|
||||||
// steam(rtp)->frameconverter->stream(frame)->flvmuxer->stream(tag)
|
// steam(rtp)->frameconverter->stream(frame)->flvmuxer->stream(tag)
|
||||||
// prepare rtp.Packet -> av.Frame
|
// prepare rtp.Packet -> av.Frame
|
||||||
var videoExtractor, audioExtractor rtp.FrameExtractor
|
var videoExtractor, audioExtractor rtp.Depacketizer
|
||||||
if s.Video.Codec == "H264" {
|
if s.Video.Codec == "H264" {
|
||||||
videoExtractor = rtp.NewH264FrameExtractor(s)
|
videoExtractor = rtp.NewH264Depacketize(s)
|
||||||
}
|
}
|
||||||
if s.Audio.Codec == "AAC" {
|
if s.Audio.Codec == "AAC" {
|
||||||
audioExtractor = rtp.NewMPESFrameExtractor(s, s.Audio.SampleRate)
|
audioExtractor = rtp.NewAacDepacketizer(s, s.Audio.SampleRate)
|
||||||
}
|
}
|
||||||
if videoExtractor == nil && audioExtractor == nil {
|
if videoExtractor == nil && audioExtractor == nil {
|
||||||
s.frameConverter = emptyFrameConverter{}
|
s.frameConverter = emptyFrameConverter{}
|
||||||
} else {
|
} else {
|
||||||
s.frameConverter = rtp.NewFrameConverter(videoExtractor, audioExtractor,
|
s.frameConverter = rtp.NewDemuxer(videoExtractor, audioExtractor,
|
||||||
s.logger.With(xlog.Fields(xlog.F("extra", "rtp2frame"))))
|
s.logger.With(xlog.Fields(xlog.F("extra", "rtp2frame"))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,8 +197,8 @@ func (s *Stream) close(status int32) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WritePacket 向流写入一个媒体包
|
// WriteRtpPacket 向流写入一个媒体包
|
||||||
func (s *Stream) WritePacket(packet *rtp.Packet) error {
|
func (s *Stream) WriteRtpPacket(packet *rtp.Packet) error {
|
||||||
status := atomic.LoadInt32(&s.status)
|
status := atomic.LoadInt32(&s.status)
|
||||||
if status != StreamOK {
|
if status != StreamOK {
|
||||||
return statusErrors[status]
|
return statusErrors[status]
|
||||||
@@ -209,12 +209,12 @@ func (s *Stream) WritePacket(packet *rtp.Packet) error {
|
|||||||
s.cache.CachePack(packet)
|
s.cache.CachePack(packet)
|
||||||
s.consumptions.SendToAll(packet)
|
s.consumptions.SendToAll(packet)
|
||||||
|
|
||||||
s.frameConverter.WritePacket(packet)
|
s.frameConverter.WriteRtpPacket(packet)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteFrame .
|
// WriteFrame .
|
||||||
func (s *Stream) WriteFrame(frame *av.Frame) error {
|
func (s *Stream) WriteFrame(frame *codec.Frame) error {
|
||||||
if err := s.flvMuxer.WriteFrame(frame); err != nil {
|
if err := s.flvMuxer.WriteFrame(frame); err != nil {
|
||||||
s.logger.Error(err.Error())
|
s.logger.Error(err.Error())
|
||||||
}
|
}
|
||||||
@@ -227,7 +227,7 @@ func (s *Stream) WriteFrame(frame *av.Frame) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteTag .
|
// WriteTag .
|
||||||
func (s *Stream) WriteTag(tag *flv.Tag) error {
|
func (s *Stream) WriteFlvTag(tag *flv.Tag) error {
|
||||||
status := atomic.LoadInt32(&s.status)
|
status := atomic.LoadInt32(&s.status)
|
||||||
if status != StreamOK {
|
if status != StreamOK {
|
||||||
return statusErrors[status]
|
return statusErrors[status]
|
||||||
@@ -319,8 +319,8 @@ type StreamInfo struct {
|
|||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Addr string `json:"addr"`
|
Addr string `json:"addr"`
|
||||||
Size int `json:"size"`
|
Size int `json:"size"`
|
||||||
Video *av.VideoMeta `json:"video,omitempty"`
|
Video *codec.VideoMeta `json:"video,omitempty"`
|
||||||
Audio *av.AudioMeta `json:"audio,omitempty"`
|
Audio *codec.AudioMeta `json:"audio,omitempty"`
|
||||||
ConsumptionCount int `json:"cc"`
|
ConsumptionCount int `json:"cc"`
|
||||||
Consumptions []ConsumptionInfo `json:"cs,omitempty"`
|
Consumptions []ConsumptionInfo `json:"cs,omitempty"`
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/protos/rtp"
|
"github.com/cnotch/ipchub/av/format/rtp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ func Test_Consumption_Consume(t *testing.T) {
|
|||||||
closed := false
|
closed := false
|
||||||
go func() {
|
go func() {
|
||||||
for !closed {
|
for !closed {
|
||||||
s.WritePacket(&rtp.Packet{})
|
s.WriteRtpPacket(&rtp.Packet{})
|
||||||
<-time.After(time.Millisecond * 1)
|
<-time.After(time.Millisecond * 1)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -113,7 +113,7 @@ func Test_Consumption_ConsumePanic(t *testing.T) {
|
|||||||
closed := false
|
closed := false
|
||||||
go func() {
|
go func() {
|
||||||
for !closed {
|
for !closed {
|
||||||
s.WritePacket(&rtp.Packet{})
|
s.WriteRtpPacket(&rtp.Packet{})
|
||||||
<-time.After(time.Millisecond * 1)
|
<-time.After(time.Millisecond * 1)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -136,7 +136,7 @@ func benchDispatch(n int, b *testing.B) {
|
|||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
s.WritePacket(&rtp.Packet{})
|
s.WriteRtpPacket(&rtp.Packet{})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
s.Close()
|
s.Close()
|
||||||
|
@@ -1,104 +0,0 @@
|
|||||||
// Copyright (c) 2019,CAOHONGJU All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package rtp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime/debug"
|
|
||||||
|
|
||||||
"github.com/cnotch/queue"
|
|
||||||
"github.com/cnotch/xlog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FrameExtractor 帧提取器
|
|
||||||
type FrameExtractor interface {
|
|
||||||
Control(p *Packet) error
|
|
||||||
Extract(p *Packet) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// FrameConverter 帧转换器
|
|
||||||
type FrameConverter struct {
|
|
||||||
closed bool
|
|
||||||
recvQueue *queue.SyncQueue
|
|
||||||
extractFuncs [4]func(packet *Packet) error
|
|
||||||
logger *xlog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func emptyExtract(*Packet) error { return nil }
|
|
||||||
|
|
||||||
// NewFrameConverter 创建 Packet 到 Frame 的转换器。
|
|
||||||
func NewFrameConverter(videoExtractor FrameExtractor, audioExtractor FrameExtractor, logger *xlog.Logger) *FrameConverter {
|
|
||||||
fc := &FrameConverter{
|
|
||||||
recvQueue: queue.NewSyncQueue(),
|
|
||||||
closed: false,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
|
|
||||||
if videoExtractor != nil {
|
|
||||||
fc.extractFuncs[ChannelVideo] = videoExtractor.Extract
|
|
||||||
fc.extractFuncs[ChannelVideoControl] = videoExtractor.Control
|
|
||||||
} else {
|
|
||||||
fc.extractFuncs[ChannelVideo] = emptyExtract
|
|
||||||
fc.extractFuncs[ChannelVideoControl] = emptyExtract
|
|
||||||
}
|
|
||||||
|
|
||||||
if audioExtractor != nil {
|
|
||||||
fc.extractFuncs[ChannelAudio] = audioExtractor.Extract
|
|
||||||
fc.extractFuncs[ChannelAudioControl] = audioExtractor.Control
|
|
||||||
} else {
|
|
||||||
fc.extractFuncs[ChannelAudio] = emptyExtract
|
|
||||||
fc.extractFuncs[ChannelAudioControl] = emptyExtract
|
|
||||||
}
|
|
||||||
|
|
||||||
go fc.convert()
|
|
||||||
return fc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fc *FrameConverter) convert() {
|
|
||||||
defer func() {
|
|
||||||
defer func() { // 避免 handler 再 panic
|
|
||||||
recover()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fc.logger.Errorf("FrameConverter routine panic;r = %v \n %s", r, debug.Stack())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尽早通知GC,回收内存
|
|
||||||
fc.recvQueue.Reset()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for !fc.closed {
|
|
||||||
p := fc.recvQueue.Pop()
|
|
||||||
if p == nil {
|
|
||||||
if !fc.closed {
|
|
||||||
fc.logger.Warn("FrameConverter:receive nil packet")
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
packet := p.(*Packet)
|
|
||||||
if err := fc.extractFuncs[int(packet.Channel)](packet); err != nil {
|
|
||||||
fc.logger.Errorf("FrameConverter: extract rtp frame error :%s", err.Error())
|
|
||||||
// break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close .
|
|
||||||
func (fc *FrameConverter) Close() error {
|
|
||||||
if fc.closed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fc.closed = true
|
|
||||||
fc.recvQueue.Signal()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WritePacket .
|
|
||||||
func (fc *FrameConverter) WritePacket(packet *Packet) error {
|
|
||||||
fc.recvQueue.Push(packet)
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -11,7 +11,7 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
|
||||||
"github.com/cnotch/ipchub/media"
|
"github.com/cnotch/ipchub/media"
|
||||||
"github.com/cnotch/ipchub/protos/flv"
|
"github.com/cnotch/ipchub/av/format/flv"
|
||||||
"github.com/cnotch/ipchub/stats"
|
"github.com/cnotch/ipchub/stats"
|
||||||
"github.com/cnotch/xlog"
|
"github.com/cnotch/xlog"
|
||||||
)
|
)
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
package flv
|
package flv
|
||||||
|
|
||||||
import "github.com/cnotch/ipchub/protos"
|
import "github.com/cnotch/ipchub/av/format"
|
||||||
|
|
||||||
// Pack .
|
// Pack .
|
||||||
type Pack = protos.Pack
|
type Pack = format.Package
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/cnotch/ipchub/media"
|
"github.com/cnotch/ipchub/media"
|
||||||
"github.com/cnotch/ipchub/network/websocket"
|
"github.com/cnotch/ipchub/network/websocket"
|
||||||
"github.com/cnotch/ipchub/protos/flv"
|
"github.com/cnotch/ipchub/av/format/flv"
|
||||||
"github.com/cnotch/ipchub/stats"
|
"github.com/cnotch/ipchub/stats"
|
||||||
"github.com/cnotch/xlog"
|
"github.com/cnotch/xlog"
|
||||||
)
|
)
|
||||||
|
@@ -363,7 +363,7 @@ func (c *PullClient) playStream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *PullClient) onPack(p *RTPPack) error {
|
func (c *PullClient) onPack(p *RTPPack) error {
|
||||||
return c.stream.WritePacket(p)
|
return c.stream.WriteRtpPacket(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PullClient) onRequest(r *Request) (err error) {
|
func (c *PullClient) onRequest(r *Request) (err error) {
|
||||||
|
@@ -60,7 +60,7 @@ func (s *tcpPushStream) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *tcpPushStream) WritePacket(p *RTPPack) error {
|
func (s *tcpPushStream) WritePacket(p *RTPPack) error {
|
||||||
return s.stream.WritePacket(p)
|
return s.stream.WriteRtpPacket(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcpConsumer struct {
|
type tcpConsumer struct {
|
||||||
|
@@ -5,13 +5,13 @@
|
|||||||
package rtsp
|
package rtsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cnotch/ipchub/protos"
|
"github.com/cnotch/ipchub/av/format"
|
||||||
"github.com/cnotch/ipchub/protos/rtp"
|
"github.com/cnotch/ipchub/av/format/rtp"
|
||||||
"github.com/cnotch/ipchub/protos/rtsp"
|
"github.com/cnotch/ipchub/av/format/rtsp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pack .
|
// Pack .
|
||||||
type Pack = protos.Pack
|
type Pack = format.Package
|
||||||
|
|
||||||
// .
|
// .
|
||||||
var (
|
var (
|
||||||
|
Reference in New Issue
Block a user