-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathobject_data_segment.go
More file actions
345 lines (285 loc) · 11.5 KB
/
object_data_segment.go
File metadata and controls
345 lines (285 loc) · 11.5 KB
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
package dvb
import (
"encoding/binary"
"errors"
"fmt"
)
const (
ObjectCodingMethodCodingOfPixels = uint8(0x0)
ObjectCodingMethodCharacters = uint8(0x1)
ObjectCodingMethodProgressiveCodingOfPixels = uint8(0x2)
// 0x3 - reserved
)
// object data segment; the object data segment carries information on a specific subtitle object. Objects that
// occur more than once within a region need only be transmitted once, and then positioned multiple times within
// the region. Objects used in more than one subtitle service need only be transmitted once. There are three types
// of object:
// -A graphical object in interlaced format that contains run-length encoded bitmap colours.
// -A graphical object in progressive format, whose sequence of filtered scanlines is compressed using
// zlib [14], which in turn applies DEFLATE compression [15], as defined in the PNG [16] graphics format.
// -A text object that carries a string of character codes. Usage of the text object is not defined in the present
// document.
type ObjectDataSegment struct {
// This field shall contain the value '0000 1111'.
SyncByte uint8
// This field shall contain the value 0x13, as listed in table 7.
SegmentType uint8
// The page_id identifies the subtitle service of the data contained in this subtitling_segment. Segments with a
// page_id value signalled in the subtitling descriptor as the composition page id, carry subtitling data specific for one
// subtitle service. Accordingly, segments with the page_id signalled in the subtitling descriptor as the ancillary page id,
// carry data that may be shared by multiple subtitle services.
PageID uint16 // LE
// This field shall indicate the number of bytes contained in the segment following the segment_length
// field.
SegmentLength uint16 // BE
// Uniquely identifies within the page the object for which data is contained in this object_data_segment field
ObjectID uint16
// ndicates the version of this segment data. When any of the contents of this segment change,
// this version number is incremented
ObjectVersionNumber uint8 // 4 bits
// Specifies the method used to code the object, as defined in table 18
ObjectCodingMethod uint8 // 2 bits
// If set to '1' this indicates that the CLUT entry value '1' is a non modifying colour. When
// the non modifying colour is assigned to an object pixel, then the pixel of the underlying region background or object
// shall not be modified. This can be used to create "transparent holes" in objects.
NonModifyingColourFlag bool // 1 bit
// Reserved uint8 // 1 bit
Pixels ObjectDataSegmentPixels
CharacterCodes ObjectDataSegmentCharacterCodes
ProgressivePixels ObjectDataSegmentProgressivePixelBlock
}
type ObjectDataSegmentPixels struct {
// Specifies the number of bytes contained in the pixel-data_sub-blocks for the top field
TopFieldDataBlockLength uint16 // BE
// Specifies the number of bytes contained in the data_sub-block for the bottom field
ButtomFieldDataBlockLength uint16 // BE
PixelDataSubBlocks []PixelDataSubBlock
}
func (p ObjectDataSegmentPixels) Marshal() ([]byte, error) {
return nil, ErrNotImplemented
}
func (p *ObjectDataSegmentPixels) Unmarshal(buf []byte) error {
return ErrNotImplemented
}
func (s ObjectDataSegment) Marshal() ([]byte, error) {
if s.SyncByte != segmentSyncByte {
return nil, ErrIncorrectSyncByte
}
if s.SegmentType != SegmentTypeObjectData {
return nil, ErrIncorrectSegmentType
}
if s.SegmentLength < 3 {
return nil, ErrIncorrectSegmentLength
}
buf := make([]uint8, segmentHeaderSize+3)
buf[0] = s.SyncByte
buf[1] = s.SegmentType
binary.LittleEndian.PutUint16(buf[2:4], s.PageID)
binary.BigEndian.PutUint16(buf[4:6], s.SegmentLength)
binary.LittleEndian.PutUint16(buf[6:8], s.ObjectID)
buf[8] = s.ObjectVersionNumber<<4 | s.ObjectCodingMethod<<2 | bool2byte(s.NonModifyingColourFlag)<<1
switch s.ObjectCodingMethod {
case ObjectCodingMethodCodingOfPixels:
return buf, ErrNotImplemented
case ObjectCodingMethodCharacters:
ccBuf, err := s.CharacterCodes.Marshal()
if err != nil {
return buf, err
}
// nolint makezero
return append(buf, ccBuf...), nil
case ObjectCodingMethodProgressiveCodingOfPixels:
ppBuf, err := s.ProgressivePixels.Marshal()
if err != nil {
return buf, err
}
// nolint makezero
return append(buf, ppBuf...), nil
default:
return buf, fmt.Errorf("unknown object coding method")
}
}
// Unmarshal bytes to ObjectDataSegment
// TODO handle errors; check segmentLength
func (s *ObjectDataSegment) Unmarshal(buf []byte) error {
if buf == nil {
return ErrNilBuffer
}
if len(buf) < 9 {
return ErrShortBuffer
}
s.SyncByte = buf[0]
s.SegmentType = buf[1]
s.PageID = binary.LittleEndian.Uint16(buf[2:4])
s.SegmentLength = binary.BigEndian.Uint16(buf[4:6])
if s.SyncByte != segmentSyncByte {
return ErrIncorrectSyncByte
}
if s.SegmentType != SegmentTypeObjectData {
return ErrIncorrectSegmentType
}
if s.SegmentLength < 3 {
return ErrIncorrectSegmentLength
}
s.ObjectID = binary.LittleEndian.Uint16(buf[6:8])
s.ObjectVersionNumber = (buf[8] & 0b1111_0000) >> 4
s.ObjectCodingMethod = (buf[8] & 0b0000_1100) >> 2
s.NonModifyingColourFlag = byte2bool((buf[8] & 0b0000_0010) >> 1)
switch s.ObjectCodingMethod {
case ObjectCodingMethodCodingOfPixels:
return ErrNotImplemented
case ObjectCodingMethodCharacters:
if len(buf) < 10 {
return fmt.Errorf("failed to parse object data segment with character codes: %w", ErrShortBuffer)
}
cc := ObjectDataSegmentCharacterCodes{}
if err := cc.Unmarshal(buf[9:]); err != nil {
return err
}
s.CharacterCodes = cc
return nil
case ObjectCodingMethodProgressiveCodingOfPixels:
ppBlock := ObjectDataSegmentProgressivePixelBlock{}
if err := ppBlock.Unmarshal(buf[9:]); err != nil {
return fmt.Errorf("failed to parse progressive pixel block: %w", err)
}
s.ProgressivePixels = ppBlock
return nil
default:
return errors.New("unknown object coding method")
}
}
const (
// table 22
PixelDataSubBlockDataType2BitsPerPixelCodeString = 0x10
PixelDataSubBlockDataType4BitsPerPixelCodeString = 0x11
PixelDataSubBlockDataType8BitsPerPixelCodeString = 0x12
// Specifies how to map the 2-bit/pixel codes on a 4-bit/entry CLUT by listing the 4 entry
// numbers of 4-bits each; entry number 0 first, entry number 3 last
PixelDataSubBlockDataType2To4BitMapTableData = 0x20
PixelDataSubBlockDataType2To8BitMapTableData = 0x21
PixelDataSubBlockDataType4To8BitMapTableData = 0x22
PixelDataSubBlockDataTypeEndOfObjectLineCode = 0xF0
// all other values are reserved
)
type PixelDataSubBlock struct {
// Identifies the type of information contained in the pixel-data_sub-block according to table 21.
DataType uint8
Data []byte
}
// Size returns size in bytes
func (b PixelDataSubBlock) Size() int {
return 1 + len(b.Data)
}
// TODO:
func (b PixelDataSubBlock) Marshal() ([]byte, error) {
return []byte{}, ErrNotImplemented
}
// TODO:
func (b *PixelDataSubBlock) Unmarshal(buf []byte) error {
iterator := newBytesIterator(buf)
if iterator.Len() == 0 {
return ErrShortBuffer
}
dataType, err := iterator.NextByte()
if err != nil {
return err
}
switch dataType {
case PixelDataSubBlockDataType2BitsPerPixelCodeString:
case PixelDataSubBlockDataType4BitsPerPixelCodeString:
case PixelDataSubBlockDataType8BitsPerPixelCodeString:
case PixelDataSubBlockDataType2To4BitMapTableData:
case PixelDataSubBlockDataType2To8BitMapTableData:
case PixelDataSubBlockDataType4To8BitMapTableData:
case PixelDataSubBlockDataTypeEndOfObjectLineCode:
default:
return fmt.Errorf("unknown data_type of pixel data sub block %x", dataType)
}
return nil
}
// CharacterCodes is a slice of UTF-16 symbols
type CharacterCodes []uint16
// NumberOfCodes returns symbols count
func (cc CharacterCodes) NumberOfCodes() int { return len(cc) }
// Size returns size in bytes of character codes
func (cc CharacterCodes) Size() int { return len(cc) * 2 }
// String returns printable string
func (cc CharacterCodes) String() string { return string(sliceU16toU8LE(cc)) }
// Bytes returns CharacterCodes as a slice of bytes
func (cc CharacterCodes) Bytes() []byte { return sliceU16toU8LE(cc) }
type ObjectDataSegmentCharacterCodes struct {
// Specifies the number of character codes in the string
NumberOfCodes uint8
// Specifies a character through its index number in a character table, the definition of which is not
// included in the present document. The specification and provision of such a character code table is part of the local
// agreement between the subtitle service provider and IRD manufacturer that is needed to put this mode of subtitles into
// operation
CharacterCodes CharacterCodes
}
func (c ObjectDataSegmentCharacterCodes) Marshal() ([]byte, error) {
sz := 1 + int(c.NumberOfCodes)*2
buf := make([]byte, sz)
buf[0] = c.NumberOfCodes
copy(buf[1:sz-1], sliceU16toU8LE(c.CharacterCodes))
return buf, nil
}
func (c *ObjectDataSegmentCharacterCodes) Unmarshal(buf []byte) error {
if buf == nil {
return ErrNilBuffer
}
if len(buf) < 1 {
return ErrShortBuffer
}
c.NumberOfCodes = buf[0]
if len(buf) < 1+int(c.NumberOfCodes)*2 {
return fmt.Errorf("failed to parse object data segment character codes: %w", ErrShortBuffer)
}
c.CharacterCodes = sliceU8toU16LE(buf[1 : 1+int(c.NumberOfCodes)*2])
return nil
}
type ObjectDataSegmentProgressivePixelBlock struct {
// This field shall indicate the width of the subtitle bitmap image in pixels
BitmapWidth uint16 // BE
// This field shall indicate the height of the subtitle bitmap image in pixels
BitmapHeight uint16 // BE
// This field shall indicate the number of compressed_bitmap_data_byte following this
// field.
CompressedDataBlockLength uint16 // BE
// This field is formed of the sequence of bytes of the subtitle bitmap image in
// compressed form, which is according to the zlib container format [14], in the same way as is specified for the Portable
// Network Graphics (PNG) format [16]. This format applies the DEFLATE compression algorithm [15]. The compressed
// bitmap data shall consist of the raw zlib datastream and shall not contain any PNG format overhead such as chunk
// headers or chunk CRC values.
CompressedBitmapDataByte []uint8
}
func (p ObjectDataSegmentProgressivePixelBlock) Marshal() ([]byte, error) {
if len(p.CompressedBitmapDataByte) != int(p.CompressedDataBlockLength) {
return nil, fmt.Errorf("compressed bitmap data length %d does not match declared length %d", len(p.CompressedBitmapDataByte), p.CompressedDataBlockLength)
}
buf := make([]byte, 6)
binary.BigEndian.PutUint16(buf[0:2], p.BitmapWidth)
binary.BigEndian.PutUint16(buf[2:4], p.BitmapHeight)
binary.BigEndian.PutUint16(buf[4:6], p.CompressedDataBlockLength)
// nolint makezero
return append(buf, p.CompressedBitmapDataByte...), nil
}
func (p *ObjectDataSegmentProgressivePixelBlock) Unmarshal(buf []byte) error {
if buf == nil {
return ErrNilBuffer
}
if len(buf) < 6 {
return ErrShortBuffer
}
p.BitmapWidth = binary.BigEndian.Uint16(buf[0:2])
p.BitmapHeight = binary.BigEndian.Uint16(buf[2:4])
p.CompressedDataBlockLength = binary.BigEndian.Uint16(buf[4:6])
if len(buf) < 6+int(p.CompressedDataBlockLength) {
return fmt.Errorf("failed to parse object data segment progressive pixel block: %w", ErrShortBuffer)
}
p.CompressedBitmapDataByte = buf[6 : 6+int(p.CompressedDataBlockLength)]
if len(p.CompressedBitmapDataByte) != int(p.CompressedDataBlockLength) {
return fmt.Errorf("compressed bitmap data length %d does not match declared length %d", len(p.CompressedBitmapDataByte), p.CompressedDataBlockLength)
}
return nil
}