1package rtpdump
2
3import (
4	"bufio"
5	"errors"
6	"io"
7	"regexp"
8	"sync"
9)
10
11// Reader reads the RTPDump file format
12type Reader struct {
13	readerMu sync.Mutex
14	reader   io.Reader
15}
16
17// NewReader opens a new Reader and immediately reads the Header from the start
18// of the input stream.
19func NewReader(r io.Reader) (*Reader, Header, error) {
20	var hdr Header
21
22	bio := bufio.NewReader(r)
23
24	// Look ahead to see if there's a valid preamble
25	peek, err := bio.Peek(preambleLen)
26	if err == io.EOF {
27		return nil, hdr, errMalformed
28	}
29	if err != nil {
30		return nil, hdr, err
31	}
32
33	// The file starts with #!rtpplay1.0 address/port\n
34	preambleRegexp := regexp.MustCompile(`#\!rtpplay1\.0 \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,5}\n`)
35	if !preambleRegexp.Match(peek) {
36		return nil, hdr, errMalformed
37	}
38
39	// consume the preamble
40	_, _, err = bio.ReadLine()
41	if err == io.EOF {
42		return nil, hdr, errMalformed
43	}
44	if err != nil {
45		return nil, hdr, err
46	}
47
48	hBuf := make([]byte, headerLen)
49	_, err = io.ReadFull(bio, hBuf)
50	if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) {
51		return nil, hdr, errMalformed
52	}
53	if err != nil {
54		return nil, hdr, err
55	}
56
57	if err := hdr.Unmarshal(hBuf); err != nil {
58		return nil, hdr, err
59	}
60
61	return &Reader{
62		reader: bio,
63	}, hdr, nil
64}
65
66// Next returns the next Packet in the Reader input stream
67func (r *Reader) Next() (Packet, error) {
68	r.readerMu.Lock()
69	defer r.readerMu.Unlock()
70
71	hBuf := make([]byte, pktHeaderLen)
72
73	_, err := io.ReadFull(r.reader, hBuf)
74	if errors.Is(err, io.ErrUnexpectedEOF) {
75		return Packet{}, errMalformed
76	}
77	if err != nil {
78		return Packet{}, err
79	}
80
81	var h packetHeader
82	if err = h.Unmarshal(hBuf); err != nil {
83		return Packet{}, err
84	}
85
86	if h.Length == 0 {
87		return Packet{}, errMalformed
88	}
89
90	payload := make([]byte, h.Length-pktHeaderLen)
91	_, err = io.ReadFull(r.reader, payload)
92	if errors.Is(err, io.ErrUnexpectedEOF) {
93		return Packet{}, errMalformed
94	}
95	if err != nil {
96		return Packet{}, err
97	}
98
99	return Packet{
100		Offset:  h.offset(),
101		IsRTCP:  h.PacketLength == 0,
102		Payload: payload,
103	}, nil
104}
105