forked from deepch/vdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
trackfrag.go
105 lines (94 loc) · 2.67 KB
/
trackfrag.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package fmp4
import (
"time"
"github.com/honuworx/vdk/av"
"github.com/honuworx/vdk/codec/h264parser"
"github.com/honuworx/vdk/format/fmp4/fmp4io"
"github.com/honuworx/vdk/format/fmp4/fragment"
)
// TrackFragmenter writes a single audio or video stream as a series of CMAF (fMP4) fragments
type TrackFragmenter struct {
codecData av.CodecData
trackID uint32
timeScale uint32
atom *fmp4io.Track
pending []av.Packet
// for CMAF (single track) only
seqNum uint32
fhdr []byte
shdrw bool
}
// NewTrack creates a fragmenter from the given stream codec
func NewTrack(codecData av.CodecData) (*TrackFragmenter, error) {
var trackID uint32 = 1
if codecData.Type().IsVideo() {
trackID = 2
}
f := &TrackFragmenter{
codecData: codecData,
trackID: trackID,
}
var err error
f.atom, err = f.Track()
if err != nil {
return nil, err
}
f.fhdr, err = MovieHeader([]*fmp4io.Track{f.atom})
return f, err
}
// WritePacket appends a packet to the fragmenter
func (f *TrackFragmenter) WritePacket(pkt av.Packet) error {
switch f.codecData.(type) {
case h264parser.CodecData:
// reformat NALUs as AVCC
nalus, typ := h264parser.SplitNALUs(pkt.Data)
if typ == h264parser.NALU_AVCC {
// already there
break
}
b := make([]byte, 0, len(pkt.Data)+3*len(nalus))
for _, nalu := range nalus {
j := len(nalu)
b = append(b, byte(j>>24), byte(j>>16), byte(j>>8), byte(j))
b = append(b, nalu...)
}
pkt.Data = b
}
f.pending = append(f.pending, pkt)
return nil
}
// Duration calculates the elapsed duration between the first and last pending packet in the fragmenter
func (f *TrackFragmenter) Duration() time.Duration {
if len(f.pending) < 2 {
return 0
}
return f.pending[len(f.pending)-1].Time - f.pending[0].Time
}
// TimeScale returns the number of timestamp ticks (DTS) that elapse in 1 second for this track
func (f *TrackFragmenter) TimeScale() uint32 {
return f.timeScale
}
// Fragment produces a fragment out of the currently-queued packets.
func (f *TrackFragmenter) Fragment() (fragment.Fragment, error) {
dur := f.Duration()
tf := f.makeFragment()
if tf.trackFrag == nil {
// not enough packets received
return fragment.Fragment{}, nil
}
f.seqNum++
initial := !f.shdrw
f.shdrw = true
frag := marshalFragment([]fragmentWithData{tf}, f.seqNum, initial)
frag.Duration = dur
return frag, nil
}
// NewSegment indicates that a new segment has begun and the next call to
// Fragment() should include a leading FTYP header.
func (f *TrackFragmenter) NewSegment() {
f.shdrw = false
}
// MovieHeader marshals an init.mp4 for this track
func (f *TrackFragmenter) MovieHeader() (filename, contentType string, blob []byte) {
return "init.mp4", "video/mp4", f.fhdr
}