• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Eyevinn / mp4ff / 13074325982

31 Jan 2025 02:05PM UTC coverage: 80.921% (+0.07%) from 80.851%
13074325982

push

github

tobbee
test: test that CoLL and SmDm boxes are handled inside vp09

3 of 5 new or added lines in 2 files covered. (60.0%)

63 existing lines in 3 files now uncovered.

16198 of 20017 relevant lines covered (80.92%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.57
/mp4/visualsampleentry.go
1
package mp4
2

3
import (
4
        "fmt"
5
        "io"
6

7
        "github.com/Eyevinn/mp4ff/bits"
8
        "github.com/Eyevinn/mp4ff/hevc"
9
)
10

11
// VisualSampleEntryBox Video Sample Description box (avc1/avc3/hvc1/hev1...)
12
type VisualSampleEntryBox struct {
13
        name               string
14
        DataReferenceIndex uint16
15
        Width              uint16
16
        Height             uint16
17
        Horizresolution    uint32
18
        Vertresolution     uint32
19
        FrameCount         uint16
20
        CompressorName     string
21
        AvcC               *AvcCBox
22
        HvcC               *HvcCBox
23
        Av1C               *Av1CBox
24
        VppC               *VppCBox
25
        Btrt               *BtrtBox
26
        Clap               *ClapBox
27
        Pasp               *PaspBox
28
        Sinf               *SinfBox
29
        SmDm               *SmDmBox
30
        CoLL               *CoLLBox
31
        Children           []Box
32
}
33

34
// NewVisualSampleEntryBox creates new empty box with an appropriate name such as avc1
35
func NewVisualSampleEntryBox(name string) *VisualSampleEntryBox {
×
36
        b := &VisualSampleEntryBox{}
×
37
        b.name = name
×
38
        return b
×
UNCOV
39
}
×
40

41
// CreateVisualSampleEntryBox creates a new VisualSampleEntry such as avc1, avc3, hev1, hvc1
42
func CreateVisualSampleEntryBox(name string, width, height uint16, sampleEntry Box) *VisualSampleEntryBox {
1✔
43
        b := &VisualSampleEntryBox{
1✔
44
                name:               name,
1✔
45
                DataReferenceIndex: 1,
1✔
46
                Width:              width,
1✔
47
                Height:             height,
1✔
48
                Horizresolution:    0x00480000, // 72dpi
1✔
49
                Vertresolution:     0x00480000, // 72dpi
1✔
50
                FrameCount:         1,
1✔
51
                CompressorName:     "mp4ff video packager",
1✔
52
                Children:           []Box{},
1✔
53
        }
1✔
54
        if sampleEntry != nil {
2✔
55
                b.AddChild(sampleEntry)
1✔
56
        }
1✔
57
        return b
1✔
58
}
59

60
// AddChild adds a child box and sets pointer to common types
61
func (b *VisualSampleEntryBox) AddChild(child Box) {
1✔
62
        switch box := child.(type) {
1✔
63
        case *AvcCBox:
1✔
64
                b.AvcC = box
1✔
65
        case *HvcCBox:
1✔
66
                b.HvcC = box
1✔
67
        case *Av1CBox:
×
UNCOV
68
                b.Av1C = box
×
69
        case *VppCBox:
1✔
70
                b.VppC = box
1✔
71
        case *BtrtBox:
1✔
72
                b.Btrt = box
1✔
73
        case *ClapBox:
×
UNCOV
74
                b.Clap = box
×
75
        case *PaspBox:
1✔
76
                b.Pasp = box
1✔
77
        case *SinfBox:
1✔
78
                b.Sinf = box
1✔
UNCOV
79
        case *SmDmBox:
×
UNCOV
80
                b.SmDm = box
×
NEW
81
        case *CoLLBox:
×
NEW
82
                b.CoLL = box
×
83
        }
84
        b.Children = append(b.Children, child)
1✔
85
}
86

87
// DecodeVisualSampleEntry decodes avc1/avc3/hvc1/hev1 box
88
func DecodeVisualSampleEntry(hdr BoxHeader, startPos uint64, r io.Reader) (Box, error) {
1✔
89
        data, err := readBoxBody(r, hdr)
1✔
90
        if err != nil {
1✔
UNCOV
91
                return nil, err
×
UNCOV
92
        }
×
93
        sr := bits.NewFixedSliceReader(data)
1✔
94
        return DecodeVisualSampleEntrySR(hdr, startPos, sr)
1✔
95
}
96

97
// DecodeVisualSampleEntrySR decodes avc1/avc3/hvc1/hev1 box
98
func DecodeVisualSampleEntrySR(hdr BoxHeader, startPos uint64, sr bits.SliceReader) (Box, error) {
1✔
99
        b := VisualSampleEntryBox{name: hdr.Name}
1✔
100

1✔
101
        // 14496-12 8.5.2.2 Sample entry (8 bytes)
1✔
102
        sr.SkipBytes(6) // Skip 6 reserved bytes
1✔
103
        b.DataReferenceIndex = sr.ReadUint16()
1✔
104

1✔
105
        // 14496-12 12.1.3.2 Visual Sample entry (70 bytes)
1✔
106

1✔
107
        sr.SkipBytes(4)  // pre_defined and reserved == 0
1✔
108
        sr.SkipBytes(12) // 3 x 32 bits pre_defined == 0
1✔
109
        b.Width = sr.ReadUint16()
1✔
110
        b.Height = sr.ReadUint16()
1✔
111

1✔
112
        b.Horizresolution = sr.ReadUint32()
1✔
113
        b.Vertresolution = sr.ReadUint32()
1✔
114

1✔
115
        sr.ReadUint32()                // reserved
1✔
116
        b.FrameCount = sr.ReadUint16() // Should be 1
1✔
117
        compressorNameLength := sr.ReadUint8()
1✔
118
        if compressorNameLength > 31 {
1✔
UNCOV
119
                return nil, fmt.Errorf("too long compressor name length")
×
UNCOV
120
        }
×
121
        b.CompressorName = sr.ReadFixedLengthString(int(compressorNameLength))
1✔
122
        sr.SkipBytes(int(31 - compressorNameLength))
1✔
123
        sr.SkipBytes(2) // Skip depth
1✔
124
        sr.ReadUint16() // pre_defined == -1
1✔
125

1✔
126
        // Now there may be clap and pasp boxes
1✔
127
        // 14496-15  5.4.2.1.2 avcC should be inside avc1, avc3 box
1✔
128
        pos := startPos + 86 // Size of all previous data
1✔
129
        endPos := startPos + uint64(hdr.Hdrlen) + uint64(hdr.payloadLen())
1✔
130
        for {
2✔
131
                if pos >= endPos {
2✔
132
                        break
1✔
133
                }
134
                box, err := DecodeBoxSR(pos, sr)
1✔
135
                if err != nil {
2✔
136
                        return nil, fmt.Errorf("error decoding childBox of VisualSampleEntry: %w", err)
1✔
137
                }
1✔
138
                if box != nil {
2✔
139
                        b.AddChild(box)
1✔
140
                        pos += box.Size()
1✔
141
                } else {
1✔
UNCOV
142
                        return nil, fmt.Errorf("not childbox of VisualSampleEntry")
×
UNCOV
143
                }
×
144
        }
145
        return &b, sr.AccError()
1✔
146
}
147

148
// Type returns box type
149
func (b *VisualSampleEntryBox) Type() string {
1✔
150
        return b.name
1✔
151
}
1✔
152

153
// SetType sets the type (name) of the box
154
func (b *VisualSampleEntryBox) SetType(name string) {
1✔
155
        b.name = name
1✔
156
}
1✔
157

158
// Size - return calculated size
159
func (b *VisualSampleEntryBox) Size() uint64 {
1✔
160
        totalSize := uint64(boxHeaderSize + 78)
1✔
161
        for _, child := range b.Children {
2✔
162
                totalSize += child.Size()
1✔
163
        }
1✔
164
        return totalSize
1✔
165
}
166

167
// Encode writes box to w
168
func (b *VisualSampleEntryBox) Encode(w io.Writer) error {
1✔
169
        err := EncodeHeader(b, w)
1✔
170
        if err != nil {
1✔
UNCOV
171
                return err
×
UNCOV
172
        }
×
173
        buf := makebuf(b)
1✔
174
        sw := bits.NewFixedSliceWriterFromSlice(buf)
1✔
175
        sw.WriteZeroBytes(6)
1✔
176
        sw.WriteUint16(b.DataReferenceIndex)
1✔
177
        sw.WriteZeroBytes(16) // pre_defined and reserved
1✔
178
        sw.WriteUint16(b.Width)
1✔
179
        sw.WriteUint16(b.Height) //36 bytes
1✔
180

1✔
181
        sw.WriteUint32(b.Horizresolution)
1✔
182
        sw.WriteUint32(b.Vertresolution)
1✔
183
        sw.WriteZeroBytes(4)
1✔
184
        sw.WriteUint16(b.FrameCount) //50 bytes
1✔
185

1✔
186
        compressorNameLength := byte(len(b.CompressorName))
1✔
187
        sw.WriteUint8(compressorNameLength)
1✔
188
        sw.WriteString(b.CompressorName, false)
1✔
189
        sw.WriteZeroBytes(int(31 - compressorNameLength))
1✔
190
        sw.WriteUint16(0x0018) // depth
1✔
191
        sw.WriteUint16(0xffff) // pre_defined == -1  //86 bytes
1✔
192

1✔
193
        _, err = w.Write(buf[:sw.Offset()]) // Only write  written bytes
1✔
194
        if err != nil {
1✔
UNCOV
195
                return err
×
UNCOV
196
        }
×
197

198
        // Next output child boxes in order
199
        for _, child := range b.Children {
2✔
200
                err = child.Encode(w)
1✔
201
                if err != nil {
1✔
UNCOV
202
                        return err
×
UNCOV
203
                }
×
204
        }
205
        return err
1✔
206
}
207

208
// EncodeSW writes box to sw
209
func (b *VisualSampleEntryBox) EncodeSW(sw bits.SliceWriter) error {
1✔
210
        err := EncodeHeaderSW(b, sw)
1✔
211
        if err != nil {
1✔
UNCOV
212
                return err
×
UNCOV
213
        }
×
214
        sw.WriteZeroBytes(6)
1✔
215
        sw.WriteUint16(b.DataReferenceIndex)
1✔
216
        sw.WriteZeroBytes(16) // pre_defined and reserved
1✔
217
        sw.WriteUint16(b.Width)
1✔
218
        sw.WriteUint16(b.Height) //36 bytes
1✔
219

1✔
220
        sw.WriteUint32(b.Horizresolution)
1✔
221
        sw.WriteUint32(b.Vertresolution)
1✔
222
        sw.WriteZeroBytes(4)
1✔
223
        sw.WriteUint16(b.FrameCount) //50 bytes
1✔
224

1✔
225
        compressorNameLength := byte(len(b.CompressorName))
1✔
226
        sw.WriteUint8(compressorNameLength)
1✔
227
        sw.WriteString(b.CompressorName, false)
1✔
228
        sw.WriteZeroBytes(int(31 - compressorNameLength))
1✔
229
        sw.WriteUint16(0x0018) // depth
1✔
230
        sw.WriteUint16(0xffff) // pre_defined == -1  //86 bytes
1✔
231

1✔
232
        // Next output child boxes in order
1✔
233
        for _, child := range b.Children {
2✔
234
                err = child.EncodeSW(sw)
1✔
235
                if err != nil {
1✔
UNCOV
236
                        return err
×
UNCOV
237
                }
×
238
        }
239
        return err
1✔
240
}
241

242
// Info writes box-specific information
243
func (b *VisualSampleEntryBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string) error {
1✔
244
        bd := newInfoDumper(w, indent, b, -1, 0)
1✔
245
        bd.write(" - width: %d", b.Width)
1✔
246
        bd.write(" - height: %d", b.Height)
1✔
247
        bd.write(" - compressorName: %q", b.CompressorName)
1✔
248
        if bd.err != nil {
1✔
UNCOV
249
                return bd.err
×
UNCOV
250
        }
×
251
        var err error
1✔
252
        for _, child := range b.Children {
2✔
253
                err = child.Info(w, specificBoxLevels, indent+indentStep, indentStep)
1✔
254
                if err != nil {
1✔
UNCOV
255
                        return err
×
UNCOV
256
                }
×
257
        }
258
        return nil
1✔
259
}
260

261
// RemoveEncryption removes sinf box and set type to unencrypted type
262
func (b *VisualSampleEntryBox) RemoveEncryption() (*SinfBox, error) {
1✔
263
        if b.name != "encv" {
1✔
UNCOV
264
                return nil, fmt.Errorf("is not encrypted: %s", b.name)
×
265
        }
×
266
        sinf := b.Sinf
1✔
267
        if sinf == nil {
1✔
UNCOV
268
                return nil, fmt.Errorf("does not have sinf box")
×
UNCOV
269
        }
×
270
        for i := range b.Children {
2✔
271
                if b.Children[i].Type() == "sinf" {
2✔
272
                        b.Children = append(b.Children[:i], b.Children[i+1:]...)
1✔
273
                        b.Sinf = nil
1✔
274
                        break
1✔
275
                }
276
        }
277
        b.name = sinf.Frma.DataFormat
1✔
278
        return sinf, nil
1✔
279
}
280

281
// ConvertHev1ToHvc1 converts visual sample entry box type and insert VPS, SPS, and PPS parameter sets
282
func (b *VisualSampleEntryBox) ConvertHev1ToHvc1(vpss [][]byte, spss [][]byte, ppss [][]byte) error {
×
283
        if b.Type() != "hev1" {
×
284
                return fmt.Errorf("type is %s and not hev1", b.Type())
×
285
        }
×
286
        b.SetType("hvc1")
×
287
        b.HvcC.DecConfRec.NaluArrays = append(b.HvcC.DecConfRec.NaluArrays, hevc.NewNaluArray(true, hevc.NALU_VPS, vpss))
×
UNCOV
288
        b.HvcC.DecConfRec.NaluArrays = append(b.HvcC.DecConfRec.NaluArrays, hevc.NewNaluArray(true, hevc.NALU_SPS, spss))
×
UNCOV
289
        b.HvcC.DecConfRec.NaluArrays = append(b.HvcC.DecConfRec.NaluArrays, hevc.NewNaluArray(true, hevc.NALU_PPS, ppss))
×
UNCOV
290
        return nil
×
291
}
292

293
// ConvertAvc3ToAvc1 converts visual sample entry box type and insert SPS and PPS parameter sets
294
func (b *VisualSampleEntryBox) ConvertAvc3ToAvc1(spss [][]byte, ppss [][]byte) error {
×
295
        if b.Type() != "avc3" {
×
296
                return fmt.Errorf("type is %s and not avc3", b.Type())
×
297
        }
×
298
        b.SetType("avc1")
×
UNCOV
299
        b.AvcC.DecConfRec.SPSnalus = spss
×
UNCOV
300
        b.AvcC.DecConfRec.PPSnalus = ppss
×
UNCOV
301
        return nil
×
302
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc