@@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"net"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
@@ -13,6 +15,7 @@ import (
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/ac3"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/flac"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h265"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg1audio"
|
||||
@@ -26,7 +29,7 @@ import (
|
||||
|
||||
var errNoSupportedCodecsFrom = errors.New(
|
||||
"the stream doesn't contain any supported codec, which are currently " +
|
||||
"AV1, VP9, H265, H264, Opus, MPEG-4 Audio, MPEG-1/2 Audio, AC-3, G711, LPCM")
|
||||
"AV1, VP9, H265, H264, Opus, FLAC, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711, LPCM")
|
||||
|
||||
func multiplyAndDivide2(v, m, d time.Duration) time.Duration {
|
||||
secs := v / d
|
||||
@@ -212,7 +215,11 @@ func FromStream(
|
||||
if slices.Contains(conn.FourCcList, any(fourCCToString(message.FourCCOpus))) {
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.Opus{
|
||||
ChannelCount: forma.ChannelCount,
|
||||
IDHeader: &opus.IDHeader{
|
||||
Version: 0x1,
|
||||
ChannelCount: uint8(forma.ChannelCount),
|
||||
PreSkip: 3840,
|
||||
},
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
@@ -453,6 +460,41 @@ func FromStream(
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
case *format.Generic:
|
||||
if strings.HasPrefix(strings.ToLower(forma.RTPMap()), "flac/") &&
|
||||
slices.Contains(conn.FourCcList, any(fourCCToString(message.FourCCFLAC))) {
|
||||
sampleRate, _ := strconv.Atoi(forma.FMTP()["samplerate"])
|
||||
channelCount, _ := strconv.Atoi(forma.FMTP()["channels"])
|
||||
bitDepth, _ := strconv.Atoi(forma.FMTP()["bitdepth"])
|
||||
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.FLAC{
|
||||
StreamInfo: &flac.StreamInfo{
|
||||
SampleRate: uint32(sampleRate),
|
||||
ChannelCount: uint8(channelCount),
|
||||
BitDepth: uint8(bitDepth),
|
||||
},
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
func(u *unit.Unit) error {
|
||||
if u.NilPayload() {
|
||||
return nil
|
||||
}
|
||||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
return (*w).WriteFLAC(
|
||||
track,
|
||||
timestampToDuration(u.PTS, forma.ClockRate()),
|
||||
u.Payload.(unit.PayloadFLAC),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import (
|
||||
"github.com/bluenviron/gortmplib/pkg/codecs"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/flac"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/opus"
|
||||
"github.com/bluenviron/mediamtx/internal/logger"
|
||||
"github.com/bluenviron/mediamtx/internal/stream"
|
||||
"github.com/bluenviron/mediamtx/internal/test"
|
||||
@@ -224,7 +226,12 @@ func TestFromStream(t *testing.T) {
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.Opus{
|
||||
ChannelCount: 2,
|
||||
IDHeader: &opus.IDHeader{
|
||||
Version: 0x1,
|
||||
ChannelCount: 2,
|
||||
PreSkip: 3840,
|
||||
ChannelMappingTable: []uint8{},
|
||||
},
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, subStream *stream.SubStream) {
|
||||
@@ -445,6 +452,40 @@ func TestFromStream(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flac",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.Generic{
|
||||
PayloadTyp: 96,
|
||||
RTPMa: "FLAC/90000",
|
||||
ClockRat: 90000,
|
||||
FMT: map[string]string{
|
||||
"samplerate": "44100",
|
||||
"channels": "2",
|
||||
"bitdepth": "16",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.FLAC{
|
||||
StreamInfo: &flac.StreamInfo{
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
BitDepth: 16,
|
||||
},
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, subStream *stream.SubStream) {
|
||||
for i := range 2 {
|
||||
subStream.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 5 * int64(i),
|
||||
Payload: unit.PayloadFLAC{3, 4},
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "h265 + h264 + vp9 + av1 + opus + aac",
|
||||
medias: []*description.Media{
|
||||
@@ -489,7 +530,12 @@ func TestFromStream(t *testing.T) {
|
||||
{Codec: &codecs.VP9{}},
|
||||
{Codec: &codecs.AV1{}},
|
||||
{Codec: &codecs.Opus{
|
||||
ChannelCount: 2,
|
||||
IDHeader: &opus.IDHeader{
|
||||
Version: 0x1,
|
||||
ChannelCount: 2,
|
||||
PreSkip: 3840,
|
||||
ChannelMappingTable: []uint8{},
|
||||
},
|
||||
}},
|
||||
{Codec: &codecs.MPEG4Audio{
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
|
||||
@@ -2,6 +2,7 @@ package rtmp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
|
||||
var errNoSupportedCodecsTo = errors.New(
|
||||
"the stream doesn't contain any supported codec, which are currently " +
|
||||
"AV1, VP9, H265, H264, MPEG-4 Audio, MPEG-1/2 Audio, G711, LPCM")
|
||||
"AV1, VP9, H265, H264, Opus, FLAC, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711, LPCM")
|
||||
|
||||
func multiplyAndDivide(v, m, d int64) int64 {
|
||||
secs := v / d
|
||||
@@ -115,9 +116,13 @@ func ToStream(
|
||||
})
|
||||
|
||||
case *codecs.Opus:
|
||||
channelCount := 2
|
||||
if codec.IDHeader != nil {
|
||||
channelCount = int(codec.IDHeader.ChannelCount)
|
||||
}
|
||||
forma := &format.Opus{
|
||||
PayloadTyp: 96,
|
||||
ChannelCount: codec.ChannelCount,
|
||||
ChannelCount: channelCount,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeAudio,
|
||||
@@ -132,6 +137,38 @@ func ToStream(
|
||||
})
|
||||
})
|
||||
|
||||
case *codecs.FLAC:
|
||||
sampleRate := 0
|
||||
channelCount := 0
|
||||
bitDepth := 0
|
||||
if codec.StreamInfo != nil {
|
||||
sampleRate = int(codec.StreamInfo.SampleRate)
|
||||
channelCount = int(codec.StreamInfo.ChannelCount)
|
||||
bitDepth = int(codec.StreamInfo.BitDepth)
|
||||
}
|
||||
forma := &format.Generic{
|
||||
PayloadTyp: 96,
|
||||
RTPMa: "flac/90000",
|
||||
ClockRat: 90000,
|
||||
FMT: map[string]string{
|
||||
"samplerate": strconv.Itoa(sampleRate),
|
||||
"channels": strconv.Itoa(channelCount),
|
||||
"bitdepth": strconv.Itoa(bitDepth),
|
||||
},
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeApplication,
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataFLAC(track, func(pts time.Duration, frame []byte) {
|
||||
(*subStream).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadFLAC(frame),
|
||||
})
|
||||
})
|
||||
|
||||
case *codecs.MPEG4Audio:
|
||||
forma := &format.MPEG4Audio{
|
||||
PayloadTyp: 96,
|
||||
@@ -235,9 +272,6 @@ func ToStream(
|
||||
Payload: unit.PayloadLPCM(samples),
|
||||
})
|
||||
})
|
||||
|
||||
default:
|
||||
panic("should not happen")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user