moq: support draft-17 (#6040)
This commit is contained in:
@@ -98,6 +98,7 @@ components:
|
|||||||
MoQVersion:
|
MoQVersion:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
|
- moqt-17
|
||||||
- moqt-18
|
- moqt-18
|
||||||
- moqt-19
|
- moqt-19
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ docker run --rm -it \
|
|||||||
-p 8890:8890/udp \
|
-p 8890:8890/udp \
|
||||||
-p 8189:8189/udp \
|
-p 8189:8189/udp \
|
||||||
-p 8892:8892/udp \
|
-p 8892:8892/udp \
|
||||||
|
-p 8893:8893/udp \
|
||||||
bluenviron/mediamtx:1
|
bluenviron/mediamtx:1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Media-over-QUIC is a streaming protocol built upon cutting edge protocols (QUIC,
|
|||||||
|
|
||||||
Media-over-QUIC has a wide range of features and variants, most of them in active development. We currently support the following:
|
Media-over-QUIC has a wide range of features and variants, most of them in active development. We currently support the following:
|
||||||
|
|
||||||
- The server supports `draft-19` and `draft-18` of the [main specification](https://datatracker.ietf.org/doc/html/draft-ietf-moq-transport-19), and prefers `draft-19` when both are offered during negotiation.
|
- The server supports `draft-19`, `draft-18` and `draft-17` of the [main specification](https://datatracker.ietf.org/doc/html/draft-ietf-moq-transport-19), and prefers `draft-19` when multiple versions are offered during negotiation.
|
||||||
- We support using Media-over-QUIC through browsers with the WebTransport API and through native QUIC clients.
|
- We support using Media-over-QUIC through browsers with the WebTransport API and through native QUIC clients.
|
||||||
- We support the `PUBLISH` and `SUBSCRIBE` messages only, which are the ones meant to be used with a routing solution like _MediaMTX_.
|
- We support the `PUBLISH` and `SUBSCRIBE` messages only, which are the ones meant to be used with a routing solution like _MediaMTX_.
|
||||||
- We use the MOQT Streaming Format (MSF) to advertise tracks, described in [this specification](https://datatracker.ietf.org/doc/html/draft-ietf-moq-msf-00).
|
- We use the MOQT Streaming Format (MSF) to advertise tracks, described in [this specification](https://datatracker.ietf.org/doc/html/draft-ietf-moq-msf-00).
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type APIMoQVersion string
|
|||||||
|
|
||||||
// protocol versions.
|
// protocol versions.
|
||||||
const (
|
const (
|
||||||
|
APIMoQVersionDraft17 APIMoQVersion = "moqt-17"
|
||||||
APIMoQVersionDraft18 APIMoQVersion = "moqt-18"
|
APIMoQVersionDraft18 APIMoQVersion = "moqt-18"
|
||||||
APIMoQVersionDraft19 APIMoQVersion = "moqt-19"
|
APIMoQVersionDraft19 APIMoQVersion = "moqt-19"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ func Read(r io.Reader) (Message, error) {
|
|||||||
m = &RequestError{}
|
m = &RequestError{}
|
||||||
case typePublish:
|
case typePublish:
|
||||||
m = &Publish{}
|
m = &Publish{}
|
||||||
|
case typePublishOk:
|
||||||
|
m = &PublishOk{}
|
||||||
case typeRequestOk:
|
case typeRequestOk:
|
||||||
m = &RequestOk{}
|
m = &RequestOk{}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -124,6 +124,15 @@ var cases = []struct {
|
|||||||
Reason: "foo",
|
Reason: "foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "publish_ok",
|
||||||
|
enc: []byte{
|
||||||
|
0x1E, // type 0x1E
|
||||||
|
0x00, 0x01, // length = 1
|
||||||
|
0x00, // Number of Parameters = 0
|
||||||
|
},
|
||||||
|
dec: &controlmessage.PublishOk{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "request_ok",
|
name: "request_ok",
|
||||||
enc: []byte{
|
enc: []byte{
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package controlmessage //nolint:dupl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bluenviron/mediamtx/internal/protocols/moq/parameter"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/protocols/moq/property"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/protocols/moq/varint"
|
||||||
|
)
|
||||||
|
|
||||||
|
const typePublishOk varint.Varint = 0x1E
|
||||||
|
|
||||||
|
// PublishOk is the PUBLISH_OK control message.
|
||||||
|
// spec: draft-17, section 9.12
|
||||||
|
// spec: draft-18/19, section 10.5 (alias of REQUEST_OK)
|
||||||
|
type PublishOk struct {
|
||||||
|
Parameters parameter.Parameters
|
||||||
|
TrackProperties property.Properties
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PublishOk) isMessage() {}
|
||||||
|
|
||||||
|
func (m *PublishOk) unmarshal(buf []byte) error {
|
||||||
|
var numParams varint.Varint
|
||||||
|
n, err := numParams.Unmarshal(buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf = buf[n:]
|
||||||
|
|
||||||
|
consumed, err := m.Parameters.Unmarshal(int(numParams), buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf = buf[consumed:]
|
||||||
|
|
||||||
|
return m.TrackProperties.Unmarshal(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m PublishOk) marshalSize() int {
|
||||||
|
payloadSize := varint.Varint(len(m.Parameters)).MarshalSize() +
|
||||||
|
m.Parameters.MarshalSize() +
|
||||||
|
m.TrackProperties.MarshalSize()
|
||||||
|
return typePublishOk.MarshalSize() + 2 + payloadSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m PublishOk) marshalTo(buf []byte) int {
|
||||||
|
payloadSize := varint.Varint(len(m.Parameters)).MarshalSize() +
|
||||||
|
m.Parameters.MarshalSize() +
|
||||||
|
m.TrackProperties.MarshalSize()
|
||||||
|
n := typePublishOk.MarshalTo(buf)
|
||||||
|
buf[n] = byte(payloadSize >> 8)
|
||||||
|
buf[n+1] = byte(payloadSize)
|
||||||
|
n += 2
|
||||||
|
n += varint.Varint(len(m.Parameters)).MarshalTo(buf[n:])
|
||||||
|
n += m.Parameters.MarshalTo(buf[n:])
|
||||||
|
n += m.TrackProperties.MarshalTo(buf[n:])
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal implements Message.
|
||||||
|
func (m PublishOk) Marshal() []byte {
|
||||||
|
buf := make([]byte, m.marshalSize())
|
||||||
|
m.marshalTo(buf)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package controlmessage
|
package controlmessage //nolint:dupl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/moq/parameter"
|
"github.com/bluenviron/mediamtx/internal/protocols/moq/parameter"
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ const (
|
|||||||
var supportedMoqtVersions = []defs.APIMoQVersion{
|
var supportedMoqtVersions = []defs.APIMoQVersion{
|
||||||
defs.APIMoQVersionDraft19,
|
defs.APIMoQVersionDraft19,
|
||||||
defs.APIMoQVersionDraft18,
|
defs.APIMoQVersionDraft18,
|
||||||
|
defs.APIMoQVersionDraft17,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ginUnwrapper interface {
|
type ginUnwrapper interface {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
var supportedMoqtALPNs = []string{
|
var supportedMoqtALPNs = []string{
|
||||||
string(defs.APIMoQVersionDraft19),
|
string(defs.APIMoQVersionDraft19),
|
||||||
string(defs.APIMoQVersionDraft18),
|
string(defs.APIMoQVersionDraft18),
|
||||||
|
string(defs.APIMoQVersionDraft17),
|
||||||
}
|
}
|
||||||
|
|
||||||
type nativeListenerParent interface {
|
type nativeListenerParent interface {
|
||||||
@@ -96,6 +97,9 @@ func alpnToVersion(alpn string) defs.APIMoQVersion {
|
|||||||
|
|
||||||
case string(defs.APIMoQVersionDraft18):
|
case string(defs.APIMoQVersionDraft18):
|
||||||
return defs.APIMoQVersionDraft18
|
return defs.APIMoQVersionDraft18
|
||||||
|
|
||||||
|
case string(defs.APIMoQVersionDraft17):
|
||||||
|
return defs.APIMoQVersionDraft17
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -271,6 +271,11 @@ func TestServer(t *testing.T) {
|
|||||||
clientProtocols []string
|
clientProtocols []string
|
||||||
expectedVersion defs.APIMoQVersion
|
expectedVersion defs.APIMoQVersion
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "draft-17",
|
||||||
|
clientProtocols: []string{"moqt-17"},
|
||||||
|
expectedVersion: defs.APIMoQVersionDraft17,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "draft-18",
|
name: "draft-18",
|
||||||
clientProtocols: []string{"moqt-18"},
|
clientProtocols: []string{"moqt-18"},
|
||||||
@@ -286,6 +291,11 @@ func TestServer(t *testing.T) {
|
|||||||
clientProtocols: []string{"moqt-19", "moqt-18"},
|
clientProtocols: []string{"moqt-19", "moqt-18"},
|
||||||
expectedVersion: defs.APIMoQVersionDraft19,
|
expectedVersion: defs.APIMoQVersionDraft19,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "highest-preferred",
|
||||||
|
clientProtocols: []string{"moqt-17", "moqt-18", "moqt-19"},
|
||||||
|
expectedVersion: defs.APIMoQVersionDraft19,
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
desc := &description.Session{Medias: []*description.Media{test.UniqueMediaH264()}}
|
desc := &description.Session{Medias: []*description.Media{test.UniqueMediaH264()}}
|
||||||
|
|||||||
@@ -620,7 +620,14 @@ func (s *session) onPublishCatalog(wstream io.ReadWriteCloser, m *controlmessage
|
|||||||
return fmt.Errorf("terminated")
|
return fmt.Errorf("terminated")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := wstream.Write(controlmessage.RequestOk{}.Marshal())
|
var ackPayload []byte
|
||||||
|
if s.version == defs.APIMoQVersionDraft17 {
|
||||||
|
ackPayload = controlmessage.PublishOk{}.Marshal()
|
||||||
|
} else {
|
||||||
|
ackPayload = controlmessage.RequestOk{}.Marshal()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := wstream.Write(ackPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -637,7 +644,14 @@ func (s *session) onPublishTrack(wstream io.ReadWriteCloser) error {
|
|||||||
}
|
}
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
|
||||||
_, err := wstream.Write(controlmessage.RequestOk{}.Marshal())
|
var ackPayload []byte
|
||||||
|
if s.version == defs.APIMoQVersionDraft17 {
|
||||||
|
ackPayload = controlmessage.PublishOk{}.Marshal()
|
||||||
|
} else {
|
||||||
|
ackPayload = controlmessage.RequestOk{}.Marshal()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := wstream.Write(ackPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user