mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-09-26 19:11:11 +08:00
113 lines
3.1 KiB
Go
113 lines
3.1 KiB
Go
package probers
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/asticode/go-astiav"
|
|
"github.com/asticode/go-astikit"
|
|
"github.com/flavioribeiro/donut/internal/entities"
|
|
"github.com/flavioribeiro/donut/internal/mapper"
|
|
"go.uber.org/fx"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type LibAVFFmpeg struct {
|
|
c *entities.Config
|
|
l *zap.SugaredLogger
|
|
m *mapper.Mapper
|
|
}
|
|
|
|
type ResultLibAVFFmpeg struct {
|
|
fx.Out
|
|
LibAVFFmpegProber DonutProber `group:"probers"`
|
|
}
|
|
|
|
// NewLibAVFFmpeg creates a new LibAVFFmpeg DonutProber
|
|
func NewLibAVFFmpeg(
|
|
c *entities.Config,
|
|
l *zap.SugaredLogger,
|
|
m *mapper.Mapper,
|
|
) ResultLibAVFFmpeg {
|
|
return ResultLibAVFFmpeg{
|
|
LibAVFFmpegProber: &LibAVFFmpeg{
|
|
c: c,
|
|
l: l,
|
|
m: m,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Match returns true when the request is for an LibAVFFmpeg prober
|
|
func (c *LibAVFFmpeg) Match(req *entities.RequestParams) bool {
|
|
isRTMP := strings.Contains(strings.ToLower(req.StreamURL), "rtmp")
|
|
isSRT := strings.Contains(strings.ToLower(req.StreamURL), "srt")
|
|
|
|
return isRTMP || isSRT
|
|
}
|
|
|
|
// StreamInfo connects to the SRT stream to discovery media properties.
|
|
func (c *LibAVFFmpeg) StreamInfo(req entities.DonutAppetizer) (*entities.StreamInfo, error) {
|
|
closer := astikit.NewCloser()
|
|
defer closer.Close()
|
|
|
|
var inputFormatContext *astiav.FormatContext
|
|
if inputFormatContext = astiav.AllocFormatContext(); inputFormatContext == nil {
|
|
return nil, entities.ErrFFmpegLibAVFormatContextIsNil
|
|
}
|
|
closer.Add(inputFormatContext.Free)
|
|
|
|
inputFormat, err := c.defineInputFormat(req.Format.String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
inputOptions := c.defineInputOptions(req.Options, closer)
|
|
|
|
if err := inputFormatContext.OpenInput(req.URL, inputFormat, inputOptions); err != nil {
|
|
return nil, fmt.Errorf("error while inputFormatContext.OpenInput: (%s, %#v, %#v) %w", req.URL, inputFormat, inputOptions, err)
|
|
}
|
|
closer.Add(inputFormatContext.CloseInput)
|
|
|
|
if err := inputFormatContext.FindStreamInfo(nil); err != nil {
|
|
return nil, fmt.Errorf("error while inputFormatContext.FindStreamInfo %w", err)
|
|
}
|
|
|
|
streams := []entities.Stream{}
|
|
for _, is := range inputFormatContext.Streams() {
|
|
if is.CodecParameters().MediaType() != astiav.MediaTypeAudio &&
|
|
is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
|
|
c.l.Info("skipping media type", is.CodecParameters().MediaType())
|
|
continue
|
|
}
|
|
streams = append(streams, c.m.FromLibAVStreamToEntityStream(is))
|
|
}
|
|
si := entities.StreamInfo{Streams: streams}
|
|
|
|
return &si, nil
|
|
}
|
|
|
|
// TODO: merge common behavior (streamer / prober)
|
|
func (c *LibAVFFmpeg) defineInputFormat(streamFormat string) (*astiav.InputFormat, error) {
|
|
var inputFormat *astiav.InputFormat
|
|
if streamFormat != "" {
|
|
inputFormat = astiav.FindInputFormat(streamFormat)
|
|
if inputFormat == nil {
|
|
return nil, fmt.Errorf("ffmpeg/libav: could not find %s input format", streamFormat)
|
|
}
|
|
}
|
|
return inputFormat, nil
|
|
}
|
|
|
|
func (c *LibAVFFmpeg) defineInputOptions(opts map[entities.DonutInputOptionKey]string, closer *astikit.Closer) *astiav.Dictionary {
|
|
var dic *astiav.Dictionary
|
|
if len(opts) > 0 {
|
|
dic = &astiav.Dictionary{}
|
|
closer.Add(dic.Free)
|
|
|
|
for k, v := range opts {
|
|
dic.Set(k.String(), v, 0)
|
|
}
|
|
}
|
|
return dic
|
|
}
|