Skip to content

Commit 770d72b

Browse files
authored
rtmp: support Extended Metadata Frames (#4006) (#4014) (#4018)
1 parent 82409e2 commit 770d72b

File tree

12 files changed

+305
-237
lines changed

12 files changed

+305
-237
lines changed

internal/protocols/rtmp/amf0/unmarshal.go renamed to internal/protocols/rtmp/amf0/data.go

Lines changed: 213 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package amf0 contains an AMF0 decoder and encoder.
12
package amf0
23

34
import (
@@ -28,9 +29,15 @@ const (
2829

2930
var errBufferTooShort = errors.New("buffer is too short")
3031

32+
// StrictArray is an AMF0 Strict Array.
33+
type StrictArray []interface{}
34+
35+
// Data is a list of ActionScript object graphs.
36+
type Data []interface{}
37+
3138
// Unmarshal decodes AMF0 data.
32-
func Unmarshal(buf []byte) ([]interface{}, error) {
33-
var out []interface{}
39+
func Unmarshal(buf []byte) (Data, error) {
40+
var out Data
3441

3542
for len(buf) != 0 {
3643
var item interface{}
@@ -210,3 +217,207 @@ func unmarshal(buf []byte) (interface{}, []byte, error) {
210217
return nil, nil, fmt.Errorf("unsupported marker 0x%.2x", marker)
211218
}
212219
}
220+
221+
// Marshal encodes AMF0 data.
222+
func (data Data) Marshal() ([]byte, error) {
223+
n, err := data.MarshalSize()
224+
if err != nil {
225+
return nil, err
226+
}
227+
228+
buf := make([]byte, n)
229+
_, err = data.MarshalTo(buf)
230+
if err != nil {
231+
return nil, err
232+
}
233+
234+
return buf, nil
235+
}
236+
237+
// MarshalTo encodes AMF0 data into an existing buffer.
238+
func (data Data) MarshalTo(buf []byte) (int, error) {
239+
n := 0
240+
241+
for _, item := range data {
242+
n += marshalItem(item, buf[n:])
243+
}
244+
245+
return n, nil
246+
}
247+
248+
// MarshalSize returns the size needed to encode data in AMF0.
249+
func (data Data) MarshalSize() (int, error) {
250+
n := 0
251+
252+
for _, item := range data {
253+
in, err := marshalSizeItem(item)
254+
if err != nil {
255+
return 0, err
256+
}
257+
258+
n += in
259+
}
260+
261+
return n, nil
262+
}
263+
264+
func marshalSizeItem(item interface{}) (int, error) {
265+
switch item := item.(type) {
266+
case float64:
267+
return 9, nil
268+
269+
case bool:
270+
return 2, nil
271+
272+
case string:
273+
return 3 + len(item), nil
274+
275+
case ECMAArray:
276+
n := 5
277+
278+
for _, entry := range item {
279+
en, err := marshalSizeItem(entry.Value)
280+
if err != nil {
281+
return 0, err
282+
}
283+
284+
n += 2 + len(entry.Key) + en
285+
}
286+
287+
n += 3
288+
289+
return n, nil
290+
291+
case Object:
292+
n := 1
293+
294+
for _, entry := range item {
295+
en, err := marshalSizeItem(entry.Value)
296+
if err != nil {
297+
return 0, err
298+
}
299+
300+
n += 2 + len(entry.Key) + en
301+
}
302+
303+
n += 3
304+
305+
return n, nil
306+
307+
case StrictArray:
308+
n := 5
309+
310+
for _, entry := range item {
311+
en, err := marshalSizeItem(entry)
312+
if err != nil {
313+
return 0, err
314+
}
315+
316+
n += en
317+
}
318+
319+
return n, nil
320+
321+
case nil:
322+
return 1, nil
323+
324+
default:
325+
return 0, fmt.Errorf("unsupported data type: %T", item)
326+
}
327+
}
328+
329+
func marshalItem(item interface{}, buf []byte) int {
330+
switch item := item.(type) {
331+
case float64:
332+
v := math.Float64bits(item)
333+
buf[0] = markerNumber
334+
buf[1] = byte(v >> 56)
335+
buf[2] = byte(v >> 48)
336+
buf[3] = byte(v >> 40)
337+
buf[4] = byte(v >> 32)
338+
buf[5] = byte(v >> 24)
339+
buf[6] = byte(v >> 16)
340+
buf[7] = byte(v >> 8)
341+
buf[8] = byte(v)
342+
return 9
343+
344+
case bool:
345+
buf[0] = markerBoolean
346+
if item {
347+
buf[1] = 1
348+
}
349+
return 2
350+
351+
case string:
352+
le := len(item)
353+
buf[0] = markerString
354+
buf[1] = byte(le >> 8)
355+
buf[2] = byte(le)
356+
copy(buf[3:], item)
357+
return 3 + le
358+
359+
case ECMAArray:
360+
le := len(item)
361+
buf[0] = markerECMAArray
362+
buf[1] = byte(le >> 24)
363+
buf[2] = byte(le >> 16)
364+
buf[3] = byte(le >> 8)
365+
buf[4] = byte(le)
366+
n := 5
367+
368+
for _, entry := range item {
369+
le := len(entry.Key)
370+
buf[n] = byte(le >> 8)
371+
buf[n+1] = byte(le)
372+
copy(buf[n+2:], entry.Key)
373+
n += 2 + le
374+
375+
n += marshalItem(entry.Value, buf[n:])
376+
}
377+
378+
buf[n] = 0
379+
buf[n+1] = 0
380+
buf[n+2] = markerObjectEnd
381+
382+
return n + 3
383+
384+
case Object:
385+
buf[0] = markerObject
386+
n := 1
387+
388+
for _, entry := range item {
389+
le := len(entry.Key)
390+
buf[n] = byte(le >> 8)
391+
buf[n+1] = byte(le)
392+
copy(buf[n+2:], entry.Key)
393+
n += 2 + le
394+
395+
n += marshalItem(entry.Value, buf[n:])
396+
}
397+
398+
buf[n] = 0
399+
buf[n+1] = 0
400+
buf[n+2] = markerObjectEnd
401+
402+
return n + 3
403+
404+
case StrictArray:
405+
le := len(item)
406+
buf[0] = markerStrictArray
407+
buf[1] = byte(le >> 24)
408+
buf[2] = byte(le >> 16)
409+
buf[3] = byte(le >> 8)
410+
buf[4] = byte(le)
411+
n := 5
412+
413+
for _, entry := range item {
414+
n += marshalItem(entry, buf[n:])
415+
}
416+
417+
return n
418+
419+
default:
420+
buf[0] = markerNull
421+
return 1
422+
}
423+
}

internal/protocols/rtmp/amf0/unmarshal_test.go renamed to internal/protocols/rtmp/amf0/data_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
var cases = []struct {
1010
name string
1111
enc []byte
12-
dec []interface{}
12+
dec Data
1313
}{
1414
{
1515
"on metadata",
@@ -316,6 +316,16 @@ func TestUnmarshal(t *testing.T) {
316316
}
317317
}
318318

319+
func TestMarshal(t *testing.T) {
320+
for _, ca := range cases {
321+
t.Run(ca.name, func(t *testing.T) {
322+
enc, err := ca.dec.Marshal()
323+
require.NoError(t, err)
324+
require.Equal(t, ca.enc, enc)
325+
})
326+
}
327+
}
328+
319329
func FuzzUnmarshal(f *testing.F) {
320330
for _, ca := range cases {
321331
f.Add(ca.enc)
@@ -324,7 +334,7 @@ func FuzzUnmarshal(f *testing.F) {
324334
f.Fuzz(func(_ *testing.T, b []byte) {
325335
what, err := Unmarshal(b)
326336
if err == nil {
327-
Marshal(what) //nolint:errcheck
337+
what.Marshal() //nolint:errcheck
328338
}
329339
})
330340
}

0 commit comments

Comments
 (0)