mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-09-27 03:15:54 +08:00
266 lines
6.3 KiB
Go
266 lines
6.3 KiB
Go
package entities
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/asticode/go-astiav"
|
|
"github.com/pion/webrtc/v3"
|
|
)
|
|
|
|
const (
|
|
MetadataChannelID string = "metadata"
|
|
)
|
|
|
|
type WebRTCSetupResponse struct {
|
|
Connection *webrtc.PeerConnection
|
|
Video *webrtc.TrackLocalStaticSample
|
|
Audio *webrtc.TrackLocalStaticSample
|
|
Data *webrtc.DataChannel
|
|
LocalSDP *webrtc.SessionDescription
|
|
}
|
|
|
|
type RequestParams struct {
|
|
StreamURL string
|
|
StreamID string
|
|
Offer webrtc.SessionDescription
|
|
}
|
|
|
|
func (p *RequestParams) Valid() error {
|
|
if p == nil {
|
|
return ErrMissingParamsOffer
|
|
}
|
|
|
|
if p.StreamID == "" {
|
|
return ErrMissingStreamID
|
|
}
|
|
|
|
if p.StreamURL == "" {
|
|
return ErrMissingStreamURL
|
|
}
|
|
isRTMP := strings.Contains(strings.ToLower(p.StreamURL), "rtmp")
|
|
isSRT := strings.Contains(strings.ToLower(p.StreamURL), "srt")
|
|
|
|
if !(isRTMP || isSRT) {
|
|
return ErrUnsupportedStreamURL
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *RequestParams) String() string {
|
|
if p == nil {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("RequestParams {StreamURL: %s, StreamID: %s}", p.StreamURL, p.StreamID)
|
|
}
|
|
|
|
type MessageType string
|
|
|
|
const (
|
|
MessageTypeMetadata MessageType = "metadata"
|
|
)
|
|
|
|
type Message struct {
|
|
Type MessageType
|
|
Message string
|
|
}
|
|
|
|
type Codec string
|
|
type MediaType string
|
|
|
|
const (
|
|
UnknownCodec Codec = "unknownCodec"
|
|
H264 Codec = "h264"
|
|
H265 Codec = "h265"
|
|
VP8 Codec = "vp8"
|
|
VP9 Codec = "vp9"
|
|
AV1 Codec = "av1"
|
|
AAC Codec = "aac"
|
|
Opus Codec = "opus"
|
|
)
|
|
|
|
const (
|
|
UnknownType MediaType = "unknownMediaType"
|
|
VideoType MediaType = "video"
|
|
AudioType MediaType = "audio"
|
|
)
|
|
|
|
type Stream struct {
|
|
Codec Codec
|
|
Type MediaType
|
|
Id uint16
|
|
Index uint16
|
|
}
|
|
|
|
type MediaFrameContext struct {
|
|
// DTS decoding timestamp
|
|
DTS int
|
|
// PTS presentation timestamp
|
|
PTS int
|
|
// Media frame duration
|
|
Duration time.Duration
|
|
}
|
|
|
|
type StreamInfo struct {
|
|
Streams []Stream
|
|
}
|
|
|
|
func (s *StreamInfo) VideoStreams() []Stream {
|
|
var result []Stream
|
|
for _, s := range s.Streams {
|
|
if s.Type == VideoType {
|
|
result = append(result, s)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (s *StreamInfo) AudioStreams() []Stream {
|
|
var result []Stream
|
|
for _, s := range s.Streams {
|
|
if s.Type == AudioType {
|
|
result = append(result, s)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
type Cue struct {
|
|
Type string
|
|
StartTime int64
|
|
Text string
|
|
}
|
|
|
|
type DonutParameters struct {
|
|
Cancel context.CancelFunc
|
|
Ctx context.Context
|
|
|
|
Recipe DonutRecipe
|
|
|
|
OnClose func()
|
|
OnError func(err error)
|
|
OnStream func(st *Stream) error
|
|
OnVideoFrame func(data []byte, c MediaFrameContext) error
|
|
OnAudioFrame func(data []byte, c MediaFrameContext) error
|
|
}
|
|
|
|
type DonutMediaTaskAction string
|
|
|
|
var DonutTranscode DonutMediaTaskAction = "transcode"
|
|
var DonutBypass DonutMediaTaskAction = "bypass"
|
|
|
|
type DonutBitStreamFilter string
|
|
|
|
var DonutH264AnnexB DonutBitStreamFilter = "h264_mp4toannexb"
|
|
|
|
// TODO: split entities per domain or files avoiding name collision.
|
|
|
|
// DonutMediaTask is a transformation template to apply over a media.
|
|
type DonutMediaTask struct {
|
|
// Action the action that needs to be performed
|
|
Action DonutMediaTaskAction
|
|
// Codec is the main codec, it might be used depending on the action.
|
|
Codec Codec
|
|
// CodecContextOptions is a list of options to be applied on codec context.
|
|
// If no value is provided ffmpeg will use defaults.
|
|
// For instance, if one does not provide bit rate, it'll fallback to 64000 bps (opus)
|
|
CodecContextOptions []LibAVOptionsCodecContext
|
|
|
|
// DonutBitStreamFilter is the bitstream filter
|
|
DonutBitStreamFilter *DonutBitStreamFilter
|
|
}
|
|
|
|
type DonutInputOptionKey string
|
|
|
|
func (d DonutInputOptionKey) String() string {
|
|
return string(d)
|
|
}
|
|
|
|
var DonutSRTStreamID DonutInputOptionKey = "srt_streamid"
|
|
var DonutSRTsmoother DonutInputOptionKey = "smoother"
|
|
var DonutSRTTranstype DonutInputOptionKey = "transtype"
|
|
|
|
var DonutRTMPLive DonutInputOptionKey = "rtmp_live"
|
|
|
|
type DonutInputFormat string
|
|
|
|
func (d DonutInputFormat) String() string {
|
|
return string(d)
|
|
}
|
|
|
|
var DonutMpegTSFormat DonutInputFormat = "mpegts"
|
|
var DonutFLVFormat DonutInputFormat = "flv"
|
|
|
|
type DonutAppetizer struct {
|
|
URL string
|
|
Format DonutInputFormat
|
|
Options map[DonutInputOptionKey]string
|
|
}
|
|
|
|
type DonutRecipe struct {
|
|
Input DonutAppetizer
|
|
Video DonutMediaTask
|
|
Audio DonutMediaTask
|
|
}
|
|
|
|
type LibAVOptionsCodecContext func(c *astiav.CodecContext)
|
|
|
|
func SetSampleRate(sampleRate int) LibAVOptionsCodecContext {
|
|
return func(c *astiav.CodecContext) {
|
|
c.SetSampleRate(sampleRate)
|
|
}
|
|
}
|
|
|
|
func SetTimeBase(num, den int) LibAVOptionsCodecContext {
|
|
return func(c *astiav.CodecContext) {
|
|
c.SetTimeBase(astiav.NewRational(num, den))
|
|
}
|
|
}
|
|
|
|
// SetSampleFormat sets sample format,
|
|
// CAUTION it only contains partial list of fmt
|
|
// TODO: move it to mappers
|
|
func SetSampleFormat(fmt string) LibAVOptionsCodecContext {
|
|
var sf astiav.SampleFormat
|
|
if fmt == "fltp" {
|
|
sf = astiav.SampleFormatFltp
|
|
} else if fmt == "flt" {
|
|
sf = astiav.SampleFormatFlt
|
|
} else {
|
|
// DANGER: assuming a default value
|
|
sf = astiav.SampleFormatS16
|
|
}
|
|
return func(c *astiav.CodecContext) {
|
|
c.SetSampleFormat(sf)
|
|
}
|
|
}
|
|
|
|
// TODO: implement proper matching
|
|
// DonutTransformRecipe
|
|
// AudioTask: {Action: Transcode, From: AAC, To: Opus}
|
|
// VideoTask: {Action: Bypass, From: H264, To: H264}
|
|
|
|
type Config struct {
|
|
HTTPPort int32 `required:"true" default:"8080"`
|
|
HTTPHost string `required:"true" default:"0.0.0.0"`
|
|
PproffHTTPPort int32 `required:"true" default:"6060"`
|
|
|
|
TCPICEPort int `required:"true" default:"8081"`
|
|
UDPICEPort int `required:"true" default:"8081"`
|
|
ICEReadBufferSize int `required:"true" default:"8"`
|
|
ICEExternalIPsDNAT []string `required:"true" default:"127.0.0.1"`
|
|
EnableICEMux bool `require:"true" default:"false"`
|
|
StunServers []string `required:"true" default:"stun:stun.l.google.com:19302,stun:stun1.l.google.com:19302,stun:stun2.l.google.com:19302,stun:stun4.l.google.com:19302"`
|
|
|
|
SRTConnectionLatencyMS int32 `required:"true" default:"300"`
|
|
// MPEG-TS consists of single units of 188 bytes. Multiplying 188*7 we get 1316,
|
|
// which is the maximum product of 188 that is less than MTU 1500 (188*8=1504)
|
|
// ref https://github.com/Haivision/srt/blob/master/docs/features/live-streaming.md#transmitting-mpeg-ts-binary-protocol-over-srt
|
|
SRTReadBufferSizeBytes int `required:"true" default:"1316"`
|
|
|
|
ProbingSize int `required:"true" default:"120"`
|
|
}
|