hls: support reading and writing KLV (#5604)
This commit is contained in:
@@ -2,19 +2,19 @@
|
||||
|
||||
Live streams can be published to the server with the following protocols and codecs:
|
||||
|
||||
| protocol | variants | codecs |
|
||||
| ----------------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [SRT clients](../3-publish/01-srt-clients.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3<br/>**Other**: KLV |
|
||||
| [SRT cameras and servers](../3-publish/02-srt-cameras-and-servers.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3<br/>**Other**: KLV |
|
||||
| [WebRTC clients](../3-publish/03-webrtc-clients.md) | WHIP | **Video**: AV1, VP9, VP8, H265, H264<br/>**Audio**: Opus, G722, G711 (PCMA, PCMU) |
|
||||
| [WebRTC servers](../3-publish/04-webrtc-servers.md) | WHEP | **Video**: AV1, VP9, VP8, H265, H264<br/>**Audio**: Opus, G722, G711 (PCMA, PCMU) |
|
||||
| [RTSP clients](../3-publish/05-rtsp-clients.md) | UDP, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, MJPEG<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM<br/>**Other**: KLV, MPEG-TS, any RTP-compatible codec |
|
||||
| [RTSP cameras and servers](../3-publish/06-rtsp-cameras-and-servers.md) | UDP, UDP-Multicast, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, MJPEG<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM<br/>**Other**: KLV, MPEG-TS, any RTP-compatible codec |
|
||||
| [RTMP clients](../3-publish/07-rtmp-clients.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM |
|
||||
| [RTMP cameras and servers](../3-publish/08-rtmp-cameras-and-servers.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM |
|
||||
| [HLS cameras and servers](../3-publish/09-hls-cameras-and-servers.md) | Low-Latency HLS, MP4-based HLS, legacy HLS | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC) |
|
||||
| [MPEG-TS](../3-publish/10-mpeg-ts.md) | MPEG-TS over UDP, MPEG-TS over Unix socket | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3<br/>**Other**: KLV |
|
||||
| [RTP](../3-publish/11-rtp.md) | RTP over UDP | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM<br/>**Other**: KLV, MPEG-TS, any RTP-compatible codec |
|
||||
| protocol | variants | codecs |
|
||||
| ----------------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [SRT clients](../3-publish/01-srt-clients.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3<br/>**Other**: KLV |
|
||||
| [SRT cameras and servers](../3-publish/02-srt-cameras-and-servers.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3<br/>**Other**: KLV |
|
||||
| [WebRTC clients](../3-publish/03-webrtc-clients.md) | WHIP | **Video**: AV1, VP9, VP8, H265, H264<br/>**Audio**: Opus, G722, G711 (PCMA, PCMU) |
|
||||
| [WebRTC servers](../3-publish/04-webrtc-servers.md) | WHEP | **Video**: AV1, VP9, VP8, H265, H264<br/>**Audio**: Opus, G722, G711 (PCMA, PCMU) |
|
||||
| [RTSP clients](../3-publish/05-rtsp-clients.md) | UDP, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, MJPEG<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM<br/>**Other**: KLV, MPEG-TS, any RTP-compatible codec |
|
||||
| [RTSP cameras and servers](../3-publish/06-rtsp-cameras-and-servers.md) | UDP, UDP-Multicast, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, MJPEG<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM<br/>**Other**: KLV, MPEG-TS, any RTP-compatible codec |
|
||||
| [RTMP clients](../3-publish/07-rtmp-clients.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM |
|
||||
| [RTMP cameras and servers](../3-publish/08-rtmp-cameras-and-servers.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM |
|
||||
| [HLS cameras and servers](../3-publish/09-hls-cameras-and-servers.md) | Low-Latency HLS, MP4-based HLS, MPEG-TS-based HLS | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC)<br/>**Other**: KLV (MPEG-TS-based HLS only) |
|
||||
| [MPEG-TS](../3-publish/10-mpeg-ts.md) | MPEG-TS over UDP, MPEG-TS over Unix socket | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3<br/>**Other**: KLV |
|
||||
| [RTP](../3-publish/11-rtp.md) | RTP over UDP | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM<br/>**Other**: KLV, MPEG-TS, any RTP-compatible codec |
|
||||
|
||||
We provide instructions for publishing with the following devices:
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
Live streams can be read from the server with the following protocols and codecs:
|
||||
|
||||
| protocol | variants | codecs |
|
||||
| ---------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [SRT clients](../4-read/01-srt.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3<br/>**Other**: KLV |
|
||||
| [WebRTC clients](../4-read/02-webrtc.md) | WHEP | **Video**: AV1, VP9, VP8, H265, H264<br/>**Audio**: Opus, G722, G711 (PCMA, PCMU)<br/>**Other**: KLV |
|
||||
| [RTSP clients](../4-read/03-rtsp.md) | UDP, UDP-Multicast, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM<br/>**Other**: KLV, MPEG-TS, any RTP-compatible codec |
|
||||
| [RTMP clients](../4-read/04-rtmp.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM |
|
||||
| [HLS](../4-read/05-hls.md) | Low-Latency HLS, MP4-based HLS, legacy HLS | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC) |
|
||||
| protocol | variants | codecs |
|
||||
| ---------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [SRT clients](../4-read/01-srt.md) | | **Video**: H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3<br/>**Other**: KLV |
|
||||
| [WebRTC clients](../4-read/02-webrtc.md) | WHEP | **Video**: AV1, VP9, VP8, H265, H264<br/>**Audio**: Opus, G722, G711 (PCMA, PCMU)<br/>**Other**: KLV |
|
||||
| [RTSP clients](../4-read/03-rtsp.md) | UDP, UDP-Multicast, TCP, RTSPS | **Video**: AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM<br/>**Other**: KLV, MPEG-TS, any RTP-compatible codec |
|
||||
| [RTMP clients](../4-read/04-rtmp.md) | RTMP, RTMPS, Enhanced RTMP | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711 (PCMA, PCMU), LPCM |
|
||||
| [HLS](../4-read/05-hls.md) | Low-Latency HLS, MP4-based HLS, MPEG-TS-based HLS | **Video**: AV1, VP9, H265, H264<br/>**Audio**: Opus, MPEG-4 Audio (AAC)<br/>**Other**: KLV (MPEG-TS-based HLS only) |
|
||||
|
||||
We provide instructions for reading with the following software:
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
// ErrNoSupportedCodecs is returned by FromStream when there are no supported codecs.
|
||||
var ErrNoSupportedCodecs = errors.New(
|
||||
"the stream doesn't contain any supported codec, which are currently AV1, VP9, H265, H264, Opus, MPEG-4 Audio")
|
||||
"the stream doesn't contain any supported codec, which are currently AV1, VP9, H265, H264, Opus, MPEG-4 Audio, KLV")
|
||||
|
||||
func setupVideoTrack(
|
||||
desc *description.Session,
|
||||
@@ -287,6 +287,54 @@ func setupAudioTracks(
|
||||
}
|
||||
}
|
||||
|
||||
func setupDataTracks(
|
||||
desc *description.Session,
|
||||
r *stream.Reader,
|
||||
muxer *gohlslib.Muxer,
|
||||
) {
|
||||
addTrack := func(
|
||||
media *description.Media,
|
||||
forma format.Format,
|
||||
track *gohlslib.Track,
|
||||
onData stream.OnDataFunc,
|
||||
) {
|
||||
muxer.Tracks = append(muxer.Tracks, track)
|
||||
r.OnData(media, forma, onData)
|
||||
}
|
||||
|
||||
for _, media := range desc.Medias {
|
||||
for _, forma := range media.Formats {
|
||||
if forma, ok := forma.(*format.KLV); ok && muxer.Variant == gohlslib.MuxerVariantMPEGTS {
|
||||
track := &gohlslib.Track{
|
||||
Codec: &codecs.KLV{Synchronous: true},
|
||||
ClockRate: forma.ClockRate(),
|
||||
}
|
||||
|
||||
addTrack(
|
||||
media,
|
||||
forma,
|
||||
track,
|
||||
func(u *unit.Unit) error {
|
||||
if u.NilPayload() {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := muxer.WriteKLV(
|
||||
track,
|
||||
u.NTP,
|
||||
u.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
|
||||
u.Payload.(unit.PayloadKLV))
|
||||
if err != nil {
|
||||
return fmt.Errorf("muxer error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FromStream maps a MediaMTX stream to a HLS muxer.
|
||||
func FromStream(
|
||||
desc *description.Session,
|
||||
@@ -305,6 +353,12 @@ func FromStream(
|
||||
muxer,
|
||||
)
|
||||
|
||||
setupDataTracks(
|
||||
desc,
|
||||
r,
|
||||
muxer,
|
||||
)
|
||||
|
||||
if len(muxer.Tracks) == 0 {
|
||||
return ErrNoSupportedCodecs
|
||||
}
|
||||
|
||||
@@ -69,3 +69,53 @@ func TestFromStreamSkipUnsupportedTracks(t *testing.T) {
|
||||
|
||||
require.Equal(t, 2, n)
|
||||
}
|
||||
|
||||
func TestFromStreamKLVRequiresMPEGTSVariant(t *testing.T) {
|
||||
t.Run("klv only, non-mpegts variant", func(t *testing.T) {
|
||||
desc := &description.Session{Medias: []*description.Media{{
|
||||
Type: description.MediaTypeApplication,
|
||||
Formats: []format.Format{&format.KLV{PayloadTyp: 96}},
|
||||
}}}
|
||||
|
||||
r := &stream.Reader{
|
||||
Parent: test.Logger(func(logger.Level, string, ...any) {
|
||||
t.Error("should not happen")
|
||||
}),
|
||||
}
|
||||
|
||||
m := &gohlslib.Muxer{Variant: gohlslib.MuxerVariantFMP4}
|
||||
|
||||
err := FromStream(desc, r, m)
|
||||
require.Equal(t, ErrNoSupportedCodecs, err)
|
||||
})
|
||||
|
||||
t.Run("klv alongside video, non-mpegts variant", func(t *testing.T) {
|
||||
desc := &description.Session{Medias: []*description.Media{
|
||||
{
|
||||
Type: description.MediaTypeVideo,
|
||||
Formats: []format.Format{&format.H264{PayloadTyp: 96, PacketizationMode: 1}},
|
||||
},
|
||||
{
|
||||
Type: description.MediaTypeApplication,
|
||||
Formats: []format.Format{&format.KLV{PayloadTyp: 97}},
|
||||
},
|
||||
}}
|
||||
|
||||
n := 0
|
||||
|
||||
r := &stream.Reader{
|
||||
Parent: test.Logger(func(l logger.Level, f string, args ...any) {
|
||||
require.Equal(t, logger.Warn, l)
|
||||
require.Equal(t, "skipping track 2 (KLV)", fmt.Sprintf(f, args...))
|
||||
n++
|
||||
}),
|
||||
}
|
||||
|
||||
m := &gohlslib.Muxer{Variant: gohlslib.MuxerVariantFMP4}
|
||||
|
||||
err := FromStream(desc, r, m)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, n)
|
||||
require.Equal(t, 1, len(m.Tracks))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -199,6 +199,23 @@ func ToStream(
|
||||
})
|
||||
})
|
||||
|
||||
case *codecs.KLV:
|
||||
medi = &description.Media{
|
||||
Type: description.MediaTypeApplication,
|
||||
Formats: []format.Format{&format.KLV{
|
||||
PayloadTyp: 96,
|
||||
}},
|
||||
}
|
||||
newClockRate := medi.Formats[0].ClockRate()
|
||||
|
||||
c.OnDataKLV(ctrack, func(pts int64, uni []byte) {
|
||||
(*subStream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
|
||||
NTP: handleNTP(pts),
|
||||
PTS: multiplyAndDivide(pts, int64(newClockRate), int64(ctrack.ClockRate)),
|
||||
Payload: unit.PayloadKLV(uni),
|
||||
})
|
||||
})
|
||||
|
||||
default:
|
||||
panic("should not happen")
|
||||
}
|
||||
|
||||
@@ -28,143 +28,195 @@ func TestToStreamNoSupportedCodecs(t *testing.T) {
|
||||
// func TestToStreamSkipUnsupportedTracks(t *testing.T)
|
||||
|
||||
func TestToStream(t *testing.T) {
|
||||
track1 := &mpegts.Track{
|
||||
Codec: &tscodecs.H264{},
|
||||
}
|
||||
for _, ca := range []string{
|
||||
"h264",
|
||||
"klv",
|
||||
} {
|
||||
t.Run(ca, func(t *testing.T) {
|
||||
var track1 *mpegts.Track
|
||||
|
||||
s := &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/stream.m3u8":
|
||||
w.Header().Set("Content-Type", `application/vnd.apple.mpegurl`)
|
||||
w.Write([]byte("#EXTM3U\n" +
|
||||
"#EXT-X-VERSION:3\n" +
|
||||
"#EXT-X-ALLOW-CACHE:NO\n" +
|
||||
"#EXT-X-TARGETDURATION:2\n" +
|
||||
"#EXT-X-MEDIA-SEQUENCE:0\n" +
|
||||
"#EXT-X-PROGRAM-DATE-TIME:2018-05-20T08:17:15Z\n" +
|
||||
"#EXTINF:2,\n" +
|
||||
"segment1.ts\n" +
|
||||
"#EXTINF:2,\n" +
|
||||
"segment2.ts\n" +
|
||||
"#EXTINF:2,\n" +
|
||||
"segment3.ts\n" +
|
||||
"#EXT-X-ENDLIST\n"))
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/segment1.ts":
|
||||
w.Header().Set("Content-Type", `video/MP2T`)
|
||||
|
||||
w := &mpegts.Writer{W: w, Tracks: []*mpegts.Track{track1}}
|
||||
err := w.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = w.WriteH264(track1, 2*90000, 2*90000, [][]byte{
|
||||
{7, 1, 2, 3}, // SPS
|
||||
{8}, // PPS
|
||||
{5, 1},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/segment2.ts":
|
||||
w.Header().Set("Content-Type", `video/MP2T`)
|
||||
|
||||
w := &mpegts.Writer{W: w, Tracks: []*mpegts.Track{track1}}
|
||||
err := w.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = w.WriteH264(track1, 2*90000, 2*90000, [][]byte{
|
||||
{5, 2},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
switch ca {
|
||||
case "h264":
|
||||
track1 = &mpegts.Track{
|
||||
Codec: &tscodecs.H264{},
|
||||
}
|
||||
case "klv":
|
||||
track1 = &mpegts.Track{
|
||||
Codec: &tscodecs.KLV{Synchronous: true},
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", "localhost:5781")
|
||||
require.NoError(t, err)
|
||||
s := &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/stream.m3u8":
|
||||
w.Header().Set("Content-Type", `application/vnd.apple.mpegurl`)
|
||||
w.Write([]byte("#EXTM3U\n" +
|
||||
"#EXT-X-VERSION:3\n" +
|
||||
"#EXT-X-ALLOW-CACHE:NO\n" +
|
||||
"#EXT-X-TARGETDURATION:2\n" +
|
||||
"#EXT-X-MEDIA-SEQUENCE:0\n" +
|
||||
"#EXT-X-PROGRAM-DATE-TIME:2018-05-20T08:17:15Z\n" +
|
||||
"#EXTINF:2,\n" +
|
||||
"segment1.ts\n" +
|
||||
"#EXTINF:2,\n" +
|
||||
"segment2.ts\n" +
|
||||
"#EXTINF:2,\n" +
|
||||
"segment3.ts\n" +
|
||||
"#EXT-X-ENDLIST\n"))
|
||||
|
||||
go s.Serve(ln)
|
||||
defer s.Shutdown(context.Background())
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/segment1.ts":
|
||||
w.Header().Set("Content-Type", `video/MP2T`)
|
||||
|
||||
var strm *stream.Stream
|
||||
var subStream *stream.SubStream
|
||||
done := make(chan struct{})
|
||||
mw := &mpegts.Writer{W: w, Tracks: []*mpegts.Track{track1}}
|
||||
err := mw.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
r := &stream.Reader{Parent: test.NilLogger}
|
||||
switch ca {
|
||||
case "h264":
|
||||
err = mw.WriteH264(track1, 2*90000, 2*90000, [][]byte{
|
||||
{7, 1, 2, 3}, // SPS
|
||||
{8}, // PPS
|
||||
{5, 1},
|
||||
})
|
||||
case "klv":
|
||||
err = mw.WriteKLV(track1, 2*90000, []byte{0x06, 0x0e, 0x2b, 0x34})
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
var c *gohlslib.Client
|
||||
c = &gohlslib.Client{
|
||||
URI: "http://localhost:5781/stream.m3u8",
|
||||
OnTracks: func(tracks []*gohlslib.Track) error {
|
||||
medias, err2 := ToStream(c, tracks, &conf.Path{
|
||||
UseAbsoluteTimestamp: true,
|
||||
}, &subStream)
|
||||
require.NoError(t, err2)
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/segment2.ts":
|
||||
w.Header().Set("Content-Type", `video/MP2T`)
|
||||
|
||||
require.Equal(t, []*description.Media{{
|
||||
Type: description.MediaTypeVideo,
|
||||
Formats: []format.Format{&format.H264{
|
||||
PayloadTyp: 96,
|
||||
PacketizationMode: 1,
|
||||
}},
|
||||
}}, medias)
|
||||
mw := &mpegts.Writer{W: w, Tracks: []*mpegts.Track{track1}}
|
||||
err := mw.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
strm = &stream.Stream{
|
||||
Desc: &description.Session{Medias: medias},
|
||||
WriteQueueSize: 512,
|
||||
RTPMaxPayloadSize: 1450,
|
||||
Parent: test.NilLogger,
|
||||
}
|
||||
err2 = strm.Initialize()
|
||||
require.NoError(t, err2)
|
||||
|
||||
subStream = &stream.SubStream{
|
||||
Stream: strm,
|
||||
UseRTPPackets: false,
|
||||
}
|
||||
err2 = subStream.Initialize()
|
||||
require.NoError(t, err2)
|
||||
|
||||
n := 0
|
||||
|
||||
r.OnData(
|
||||
medias[0],
|
||||
medias[0].Formats[0],
|
||||
func(u *unit.Unit) error {
|
||||
switch n {
|
||||
case 0:
|
||||
require.Equal(t, unit.PayloadH264{
|
||||
{7, 1, 2, 3},
|
||||
{8},
|
||||
{5, 1},
|
||||
}, u.Payload)
|
||||
require.Equal(t, time.Date(2018, 0o5, 20, 8, 17, 15, 0, time.UTC), u.NTP)
|
||||
case 1:
|
||||
require.Equal(t, unit.PayloadH264{
|
||||
{7, 1, 2, 3},
|
||||
{8},
|
||||
{5, 2},
|
||||
}, u.Payload)
|
||||
require.Equal(t, time.Date(2018, 0o5, 20, 8, 17, 15, 0, time.UTC), u.NTP)
|
||||
close(done)
|
||||
default:
|
||||
t.Error("should not happen")
|
||||
switch ca {
|
||||
case "h264":
|
||||
err = mw.WriteH264(track1, 2*90000, 2*90000, [][]byte{
|
||||
{5, 2},
|
||||
})
|
||||
case "klv":
|
||||
err = mw.WriteKLV(track1, 2*90000, []byte{0x01, 0x02, 0x03, 0x04})
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
n++
|
||||
}),
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", "localhost:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
go s.Serve(ln)
|
||||
defer s.Shutdown(context.Background())
|
||||
|
||||
var strm *stream.Stream
|
||||
var subStream *stream.SubStream
|
||||
done := make(chan struct{})
|
||||
|
||||
r := &stream.Reader{Parent: test.NilLogger}
|
||||
|
||||
var c *gohlslib.Client
|
||||
c = &gohlslib.Client{
|
||||
URI: "http://" + ln.Addr().String() + "/stream.m3u8",
|
||||
OnTracks: func(tracks []*gohlslib.Track) error {
|
||||
medias, err2 := ToStream(c, tracks, &conf.Path{
|
||||
UseAbsoluteTimestamp: true,
|
||||
}, &subStream)
|
||||
require.NoError(t, err2)
|
||||
|
||||
switch ca {
|
||||
case "h264":
|
||||
require.Equal(t, []*description.Media{{
|
||||
Type: description.MediaTypeVideo,
|
||||
Formats: []format.Format{&format.H264{
|
||||
PayloadTyp: 96,
|
||||
PacketizationMode: 1,
|
||||
}},
|
||||
}}, medias)
|
||||
case "klv":
|
||||
require.Equal(t, []*description.Media{{
|
||||
Type: description.MediaTypeApplication,
|
||||
Formats: []format.Format{&format.KLV{
|
||||
PayloadTyp: 96,
|
||||
}},
|
||||
}}, medias)
|
||||
}
|
||||
|
||||
strm = &stream.Stream{
|
||||
Desc: &description.Session{Medias: medias},
|
||||
WriteQueueSize: 512,
|
||||
RTPMaxPayloadSize: 1450,
|
||||
Parent: test.NilLogger,
|
||||
}
|
||||
err2 = strm.Initialize()
|
||||
require.NoError(t, err2)
|
||||
|
||||
subStream = &stream.SubStream{
|
||||
Stream: strm,
|
||||
UseRTPPackets: false,
|
||||
}
|
||||
err2 = subStream.Initialize()
|
||||
require.NoError(t, err2)
|
||||
|
||||
n := 0
|
||||
|
||||
r.OnData(
|
||||
medias[0],
|
||||
medias[0].Formats[0],
|
||||
func(u *unit.Unit) error {
|
||||
switch ca {
|
||||
case "h264":
|
||||
switch n {
|
||||
case 0:
|
||||
require.Equal(t, unit.PayloadH264{
|
||||
{7, 1, 2, 3},
|
||||
{8},
|
||||
{5, 1},
|
||||
}, u.Payload)
|
||||
require.Equal(t, time.Date(2018, 0o5, 20, 8, 17, 15, 0, time.UTC), u.NTP)
|
||||
case 1:
|
||||
require.Equal(t, unit.PayloadH264{
|
||||
{7, 1, 2, 3},
|
||||
{8},
|
||||
{5, 2},
|
||||
}, u.Payload)
|
||||
require.Equal(t, time.Date(2018, 0o5, 20, 8, 17, 15, 0, time.UTC), u.NTP)
|
||||
close(done)
|
||||
default:
|
||||
t.Error("should not happen")
|
||||
}
|
||||
|
||||
case "klv":
|
||||
switch n {
|
||||
case 0:
|
||||
require.Equal(t, unit.PayloadKLV{0x06, 0x0e, 0x2b, 0x34}, u.Payload)
|
||||
require.Equal(t, time.Date(2018, 0o5, 20, 8, 17, 15, 0, time.UTC), u.NTP)
|
||||
case 1:
|
||||
require.Equal(t, unit.PayloadKLV{0x01, 0x02, 0x03, 0x04}, u.Payload)
|
||||
require.Equal(t, time.Date(2018, 0o5, 20, 8, 17, 15, 0, time.UTC), u.NTP)
|
||||
close(done)
|
||||
default:
|
||||
t.Error("should not happen")
|
||||
}
|
||||
}
|
||||
n++
|
||||
return nil
|
||||
})
|
||||
|
||||
strm.AddReader(r)
|
||||
|
||||
return nil
|
||||
})
|
||||
},
|
||||
}
|
||||
err = c.Start()
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
strm.AddReader(r)
|
||||
<-done
|
||||
|
||||
return nil
|
||||
},
|
||||
strm.RemoveReader(r)
|
||||
strm.Close()
|
||||
})
|
||||
}
|
||||
err = c.Start()
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
<-done
|
||||
|
||||
strm.RemoveReader(r)
|
||||
strm.Close()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user