mirror of
https://github.com/stashapp/stash.git
synced 2025-12-11 10:54:14 +01:00
2162 lines
59 KiB
Go
2162 lines
59 KiB
Go
package astits
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/asticode/go-astikit"
|
|
)
|
|
|
|
// Audio types
|
|
// Page: 683 | https://books.google.fr/books?id=6dgWB3-rChYC&printsec=frontcover&hl=fr
|
|
const (
|
|
AudioTypeCleanEffects = 0x1
|
|
AudioTypeHearingImpaired = 0x2
|
|
AudioTypeVisualImpairedCommentary = 0x3
|
|
)
|
|
|
|
// Data stream alignments
|
|
// Page: 85 | Chapter:2.6.11 | Link: http://ecee.colorado.edu/~ecen5653/ecen5653/papers/iso13818-1.pdf
|
|
const (
|
|
DataStreamAligmentAudioSyncWord = 0x1
|
|
DataStreamAligmentVideoSliceOrAccessUnit = 0x1
|
|
DataStreamAligmentVideoAccessUnit = 0x2
|
|
DataStreamAligmentVideoGOPOrSEQ = 0x3
|
|
DataStreamAligmentVideoSEQ = 0x4
|
|
)
|
|
|
|
// Descriptor tags
|
|
// Chapter: 6.1 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
const (
|
|
DescriptorTagAC3 = 0x6a
|
|
DescriptorTagAVCVideo = 0x28
|
|
DescriptorTagComponent = 0x50
|
|
DescriptorTagContent = 0x54
|
|
DescriptorTagDataStreamAlignment = 0x6
|
|
DescriptorTagEnhancedAC3 = 0x7a
|
|
DescriptorTagExtendedEvent = 0x4e
|
|
DescriptorTagExtension = 0x7f
|
|
DescriptorTagISO639LanguageAndAudioType = 0xa
|
|
DescriptorTagLocalTimeOffset = 0x58
|
|
DescriptorTagMaximumBitrate = 0xe
|
|
DescriptorTagNetworkName = 0x40
|
|
DescriptorTagParentalRating = 0x55
|
|
DescriptorTagPrivateDataIndicator = 0xf
|
|
DescriptorTagPrivateDataSpecifier = 0x5f
|
|
DescriptorTagRegistration = 0x5
|
|
DescriptorTagService = 0x48
|
|
DescriptorTagShortEvent = 0x4d
|
|
DescriptorTagStreamIdentifier = 0x52
|
|
DescriptorTagSubtitling = 0x59
|
|
DescriptorTagTeletext = 0x56
|
|
DescriptorTagVBIData = 0x45
|
|
DescriptorTagVBITeletext = 0x46
|
|
)
|
|
|
|
// Descriptor extension tags
|
|
// Chapter: 6.3 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
const (
|
|
DescriptorTagExtensionSupplementaryAudio = 0x6
|
|
)
|
|
|
|
// Service types
|
|
// Chapter: 6.2.33 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
const (
|
|
ServiceTypeDigitalTelevisionService = 0x1
|
|
)
|
|
|
|
// Teletext types
|
|
// Chapter: 6.2.43 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
const (
|
|
TeletextTypeAdditionalInformationPage = 0x3
|
|
TeletextTypeInitialTeletextPage = 0x1
|
|
TeletextTypeProgramSchedulePage = 0x4
|
|
TeletextTypeTeletextSubtitlePage = 0x2
|
|
TeletextTypeTeletextSubtitlePageForHearingImpairedPeople = 0x5
|
|
)
|
|
|
|
// VBI data service id
|
|
// Chapter: 6.2.47 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
const (
|
|
VBIDataServiceIDClosedCaptioning = 0x6
|
|
VBIDataServiceIDEBUTeletext = 0x1
|
|
VBIDataServiceIDInvertedTeletext = 0x2
|
|
VBIDataServiceIDMonochrome442Samples = 0x7
|
|
VBIDataServiceIDVPS = 0x4
|
|
VBIDataServiceIDWSS = 0x5
|
|
)
|
|
|
|
// Descriptor represents a descriptor
|
|
// TODO Handle UTF8
|
|
type Descriptor struct {
|
|
AC3 *DescriptorAC3
|
|
AVCVideo *DescriptorAVCVideo
|
|
Component *DescriptorComponent
|
|
Content *DescriptorContent
|
|
DataStreamAlignment *DescriptorDataStreamAlignment
|
|
EnhancedAC3 *DescriptorEnhancedAC3
|
|
ExtendedEvent *DescriptorExtendedEvent
|
|
Extension *DescriptorExtension
|
|
ISO639LanguageAndAudioType *DescriptorISO639LanguageAndAudioType
|
|
Length uint8
|
|
LocalTimeOffset *DescriptorLocalTimeOffset
|
|
MaximumBitrate *DescriptorMaximumBitrate
|
|
NetworkName *DescriptorNetworkName
|
|
ParentalRating *DescriptorParentalRating
|
|
PrivateDataIndicator *DescriptorPrivateDataIndicator
|
|
PrivateDataSpecifier *DescriptorPrivateDataSpecifier
|
|
Registration *DescriptorRegistration
|
|
Service *DescriptorService
|
|
ShortEvent *DescriptorShortEvent
|
|
StreamIdentifier *DescriptorStreamIdentifier
|
|
Subtitling *DescriptorSubtitling
|
|
Tag uint8 // the tag defines the structure of the contained data following the descriptor length.
|
|
Teletext *DescriptorTeletext
|
|
Unknown *DescriptorUnknown
|
|
UserDefined []byte
|
|
VBIData *DescriptorVBIData
|
|
VBITeletext *DescriptorTeletext
|
|
}
|
|
|
|
// DescriptorAC3 represents an AC3 descriptor
|
|
// Chapter: Annex D | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorAC3 struct {
|
|
AdditionalInfo []byte
|
|
ASVC uint8
|
|
BSID uint8
|
|
ComponentType uint8
|
|
HasASVC bool
|
|
HasBSID bool
|
|
HasComponentType bool
|
|
HasMainID bool
|
|
MainID uint8
|
|
}
|
|
|
|
func newDescriptorAC3(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorAC3, err error) {
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorAC3{
|
|
HasASVC: uint8(b&0x10) > 0,
|
|
HasBSID: uint8(b&0x40) > 0,
|
|
HasComponentType: uint8(b&0x80) > 0,
|
|
HasMainID: uint8(b&0x20) > 0,
|
|
}
|
|
|
|
// Component type
|
|
if d.HasComponentType {
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.ComponentType = uint8(b)
|
|
}
|
|
|
|
// BSID
|
|
if d.HasBSID {
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.BSID = uint8(b)
|
|
}
|
|
|
|
// Main ID
|
|
if d.HasMainID {
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.MainID = uint8(b)
|
|
}
|
|
|
|
// ASVC
|
|
if d.HasASVC {
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.ASVC = uint8(b)
|
|
}
|
|
|
|
// Additional info
|
|
if i.Offset() < offsetEnd {
|
|
if d.AdditionalInfo, err = i.NextBytes(offsetEnd - i.Offset()); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorAVCVideo represents an AVC video descriptor
|
|
// No doc found unfortunately, basing the implementation on https://github.com/gfto/bitstream/blob/master/mpeg/psi/desc_28.h
|
|
type DescriptorAVCVideo struct {
|
|
AVC24HourPictureFlag bool
|
|
AVCStillPresent bool
|
|
CompatibleFlags uint8
|
|
ConstraintSet0Flag bool
|
|
ConstraintSet1Flag bool
|
|
ConstraintSet2Flag bool
|
|
LevelIDC uint8
|
|
ProfileIDC uint8
|
|
}
|
|
|
|
func newDescriptorAVCVideo(i *astikit.BytesIterator) (d *DescriptorAVCVideo, err error) {
|
|
// Init
|
|
d = &DescriptorAVCVideo{}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Profile idc
|
|
d.ProfileIDC = uint8(b)
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Flags
|
|
d.ConstraintSet0Flag = b&0x80 > 0
|
|
d.ConstraintSet1Flag = b&0x40 > 0
|
|
d.ConstraintSet2Flag = b&0x20 > 0
|
|
d.CompatibleFlags = b & 0x1f
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Level idc
|
|
d.LevelIDC = uint8(b)
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// AVC still present
|
|
d.AVCStillPresent = b&0x80 > 0
|
|
|
|
// AVC 24 hour picture flag
|
|
d.AVC24HourPictureFlag = b&0x40 > 0
|
|
return
|
|
}
|
|
|
|
// DescriptorComponent represents a component descriptor
|
|
// Chapter: 6.2.8 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorComponent struct {
|
|
ComponentTag uint8
|
|
ComponentType uint8
|
|
ISO639LanguageCode []byte
|
|
StreamContent uint8
|
|
StreamContentExt uint8
|
|
Text []byte
|
|
}
|
|
|
|
func newDescriptorComponent(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorComponent, err error) {
|
|
// Init
|
|
d = &DescriptorComponent{}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Stream content ext
|
|
d.StreamContentExt = uint8(b >> 4)
|
|
|
|
// Stream content
|
|
d.StreamContent = uint8(b & 0xf)
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Component type
|
|
d.ComponentType = uint8(b)
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Component tag
|
|
d.ComponentTag = uint8(b)
|
|
|
|
// ISO639 language code
|
|
if d.ISO639LanguageCode, err = i.NextBytes(3); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Text
|
|
if i.Offset() < offsetEnd {
|
|
if d.Text, err = i.NextBytes(offsetEnd - i.Offset()); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorContent represents a content descriptor
|
|
// Chapter: 6.2.9 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorContent struct {
|
|
Items []*DescriptorContentItem
|
|
}
|
|
|
|
// DescriptorContentItem represents a content item descriptor
|
|
// Chapter: 6.2.9 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorContentItem struct {
|
|
ContentNibbleLevel1 uint8
|
|
ContentNibbleLevel2 uint8
|
|
UserByte uint8
|
|
}
|
|
|
|
func newDescriptorContent(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorContent, err error) {
|
|
// Init
|
|
d = &DescriptorContent{}
|
|
|
|
// Add items
|
|
for i.Offset() < offsetEnd {
|
|
// Get next bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytesNoCopy(2); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Append item
|
|
d.Items = append(d.Items, &DescriptorContentItem{
|
|
ContentNibbleLevel1: uint8(bs[0] >> 4),
|
|
ContentNibbleLevel2: uint8(bs[0] & 0xf),
|
|
UserByte: uint8(bs[1]),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorDataStreamAlignment represents a data stream alignment descriptor
|
|
type DescriptorDataStreamAlignment struct {
|
|
Type uint8
|
|
}
|
|
|
|
func newDescriptorDataStreamAlignment(i *astikit.BytesIterator) (d *DescriptorDataStreamAlignment, err error) {
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d = &DescriptorDataStreamAlignment{Type: uint8(b)}
|
|
return
|
|
}
|
|
|
|
// DescriptorEnhancedAC3 represents an enhanced AC3 descriptor
|
|
// Chapter: Annex D | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorEnhancedAC3 struct {
|
|
AdditionalInfo []byte
|
|
ASVC uint8
|
|
BSID uint8
|
|
ComponentType uint8
|
|
HasASVC bool
|
|
HasBSID bool
|
|
HasComponentType bool
|
|
HasMainID bool
|
|
HasSubStream1 bool
|
|
HasSubStream2 bool
|
|
HasSubStream3 bool
|
|
MainID uint8
|
|
MixInfoExists bool
|
|
SubStream1 uint8
|
|
SubStream2 uint8
|
|
SubStream3 uint8
|
|
}
|
|
|
|
func newDescriptorEnhancedAC3(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorEnhancedAC3, err error) {
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorEnhancedAC3{
|
|
HasASVC: uint8(b&0x10) > 0,
|
|
HasBSID: uint8(b&0x40) > 0,
|
|
HasComponentType: uint8(b&0x80) > 0,
|
|
HasMainID: uint8(b&0x20) > 0,
|
|
HasSubStream1: uint8(b&0x4) > 0,
|
|
HasSubStream2: uint8(b&0x2) > 0,
|
|
HasSubStream3: uint8(b&0x1) > 0,
|
|
MixInfoExists: uint8(b&0x8) > 0,
|
|
}
|
|
|
|
// Component type
|
|
if d.HasComponentType {
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.ComponentType = uint8(b)
|
|
}
|
|
|
|
// BSID
|
|
if d.HasBSID {
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.BSID = uint8(b)
|
|
}
|
|
|
|
// Main ID
|
|
if d.HasMainID {
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.MainID = uint8(b)
|
|
}
|
|
|
|
// ASVC
|
|
if d.HasASVC {
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.ASVC = uint8(b)
|
|
}
|
|
|
|
// Substream 1
|
|
if d.HasSubStream1 {
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.SubStream1 = uint8(b)
|
|
}
|
|
|
|
// Substream 2
|
|
if d.HasSubStream2 {
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.SubStream2 = uint8(b)
|
|
}
|
|
|
|
// Substream 3
|
|
if d.HasSubStream3 {
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d.SubStream3 = uint8(b)
|
|
}
|
|
|
|
// Additional info
|
|
if i.Offset() < offsetEnd {
|
|
if d.AdditionalInfo, err = i.NextBytes(offsetEnd - i.Offset()); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorExtendedEvent represents an extended event descriptor
|
|
// Chapter: 6.2.15 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorExtendedEvent struct {
|
|
ISO639LanguageCode []byte
|
|
Items []*DescriptorExtendedEventItem
|
|
LastDescriptorNumber uint8
|
|
Number uint8
|
|
Text []byte
|
|
}
|
|
|
|
// DescriptorExtendedEventItem represents an extended event item descriptor
|
|
// Chapter: 6.2.15 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorExtendedEventItem struct {
|
|
Content []byte
|
|
Description []byte
|
|
}
|
|
|
|
func newDescriptorExtendedEvent(i *astikit.BytesIterator) (d *DescriptorExtendedEvent, err error) {
|
|
// Init
|
|
d = &DescriptorExtendedEvent{}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Number
|
|
d.Number = uint8(b >> 4)
|
|
|
|
// Last descriptor number
|
|
d.LastDescriptorNumber = uint8(b & 0xf)
|
|
|
|
// ISO639 language code
|
|
if d.ISO639LanguageCode, err = i.NextBytes(3); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Items length
|
|
itemsLength := int(b)
|
|
|
|
// Items
|
|
offsetEnd := i.Offset() + itemsLength
|
|
for i.Offset() < offsetEnd {
|
|
// Create item
|
|
var item *DescriptorExtendedEventItem
|
|
if item, err = newDescriptorExtendedEventItem(i); err != nil {
|
|
err = fmt.Errorf("astits: creating extended event item failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Append item
|
|
d.Items = append(d.Items, item)
|
|
}
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Text length
|
|
textLength := int(b)
|
|
|
|
// Text
|
|
if d.Text, err = i.NextBytes(textLength); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func newDescriptorExtendedEventItem(i *astikit.BytesIterator) (d *DescriptorExtendedEventItem, err error) {
|
|
// Init
|
|
d = &DescriptorExtendedEventItem{}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Description length
|
|
descriptionLength := int(b)
|
|
|
|
// Description
|
|
if d.Description, err = i.NextBytes(descriptionLength); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Content length
|
|
contentLength := int(b)
|
|
|
|
// Content
|
|
if d.Content, err = i.NextBytes(contentLength); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorExtension represents an extension descriptor
|
|
// Chapter: 6.2.16 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorExtension struct {
|
|
SupplementaryAudio *DescriptorExtensionSupplementaryAudio
|
|
Tag uint8
|
|
Unknown *[]byte
|
|
}
|
|
|
|
func newDescriptorExtension(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorExtension, err error) {
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorExtension{Tag: uint8(b)}
|
|
|
|
// Switch on tag
|
|
switch d.Tag {
|
|
case DescriptorTagExtensionSupplementaryAudio:
|
|
if d.SupplementaryAudio, err = newDescriptorExtensionSupplementaryAudio(i, offsetEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing extension supplementary audio descriptor failed: %w", err)
|
|
return
|
|
}
|
|
default:
|
|
// Get next bytes
|
|
var b []byte
|
|
if b, err = i.NextBytes(offsetEnd - i.Offset()); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Update unknown
|
|
d.Unknown = &b
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorExtensionSupplementaryAudio represents a supplementary audio extension descriptor
|
|
// Chapter: 6.4.10 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorExtensionSupplementaryAudio struct {
|
|
EditorialClassification uint8
|
|
HasLanguageCode bool
|
|
LanguageCode []byte
|
|
MixType bool
|
|
PrivateData []byte
|
|
}
|
|
|
|
func newDescriptorExtensionSupplementaryAudio(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorExtensionSupplementaryAudio, err error) {
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Init
|
|
d = &DescriptorExtensionSupplementaryAudio{
|
|
EditorialClassification: uint8(b >> 2 & 0x1f),
|
|
HasLanguageCode: b&0x1 > 0,
|
|
MixType: b&0x80 > 0,
|
|
}
|
|
|
|
// Language code
|
|
if d.HasLanguageCode {
|
|
if d.LanguageCode, err = i.NextBytes(3); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Private data
|
|
if i.Offset() < offsetEnd {
|
|
if d.PrivateData, err = i.NextBytes(offsetEnd - i.Offset()); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorISO639LanguageAndAudioType represents an ISO639 language descriptor
|
|
// https://github.com/gfto/bitstream/blob/master/mpeg/psi/desc_0a.h
|
|
// FIXME (barbashov) according to Chapter 2.6.18 ISO/IEC 13818-1:2015 there could be not one, but multiple such descriptors
|
|
type DescriptorISO639LanguageAndAudioType struct {
|
|
Language []byte
|
|
Type uint8
|
|
}
|
|
|
|
// In some actual cases, the length is 3 and the language is described in only 2 bytes
|
|
func newDescriptorISO639LanguageAndAudioType(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorISO639LanguageAndAudioType, err error) {
|
|
// Get next bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytes(offsetEnd - i.Offset()); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorISO639LanguageAndAudioType{
|
|
Language: bs[0 : len(bs)-1],
|
|
Type: uint8(bs[len(bs)-1]),
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorLocalTimeOffset represents a local time offset descriptor
|
|
// Chapter: 6.2.20 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorLocalTimeOffset struct {
|
|
Items []*DescriptorLocalTimeOffsetItem
|
|
}
|
|
|
|
// DescriptorLocalTimeOffsetItem represents a local time offset item descriptor
|
|
// Chapter: 6.2.20 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorLocalTimeOffsetItem struct {
|
|
CountryCode []byte
|
|
CountryRegionID uint8
|
|
LocalTimeOffset time.Duration
|
|
LocalTimeOffsetPolarity bool
|
|
NextTimeOffset time.Duration
|
|
TimeOfChange time.Time
|
|
}
|
|
|
|
func newDescriptorLocalTimeOffset(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorLocalTimeOffset, err error) {
|
|
// Init
|
|
d = &DescriptorLocalTimeOffset{}
|
|
|
|
// Add items
|
|
for i.Offset() < offsetEnd {
|
|
// Create item
|
|
itm := &DescriptorLocalTimeOffsetItem{}
|
|
|
|
// Country code
|
|
if itm.CountryCode, err = i.NextBytes(3); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Country region ID
|
|
itm.CountryRegionID = uint8(b >> 2)
|
|
|
|
// Local time offset polarity
|
|
itm.LocalTimeOffsetPolarity = b&0x1 > 0
|
|
|
|
// Local time offset
|
|
if itm.LocalTimeOffset, err = parseDVBDurationMinutes(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing DVB durationminutes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Time of change
|
|
if itm.TimeOfChange, err = parseDVBTime(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing DVB time failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Next time offset
|
|
if itm.NextTimeOffset, err = parseDVBDurationMinutes(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing DVB duration minutes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Append item
|
|
d.Items = append(d.Items, itm)
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorMaximumBitrate represents a maximum bitrate descriptor
|
|
type DescriptorMaximumBitrate struct {
|
|
Bitrate uint32 // In bytes/second
|
|
}
|
|
|
|
func newDescriptorMaximumBitrate(i *astikit.BytesIterator) (d *DescriptorMaximumBitrate, err error) {
|
|
// Get next bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytesNoCopy(3); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorMaximumBitrate{Bitrate: (uint32(bs[0]&0x3f)<<16 | uint32(bs[1])<<8 | uint32(bs[2])) * 50}
|
|
return
|
|
}
|
|
|
|
// DescriptorNetworkName represents a network name descriptor
|
|
// Chapter: 6.2.27 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorNetworkName struct {
|
|
Name []byte
|
|
}
|
|
|
|
func newDescriptorNetworkName(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorNetworkName, err error) {
|
|
// Create descriptor
|
|
d = &DescriptorNetworkName{}
|
|
|
|
// Name
|
|
if d.Name, err = i.NextBytes(offsetEnd - i.Offset()); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorParentalRating represents a parental rating descriptor
|
|
// Chapter: 6.2.28 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorParentalRating struct {
|
|
Items []*DescriptorParentalRatingItem
|
|
}
|
|
|
|
// DescriptorParentalRatingItem represents a parental rating item descriptor
|
|
// Chapter: 6.2.28 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorParentalRatingItem struct {
|
|
CountryCode []byte
|
|
Rating uint8
|
|
}
|
|
|
|
// MinimumAge returns the minimum age for the parental rating
|
|
func (d DescriptorParentalRatingItem) MinimumAge() int {
|
|
// Undefined or user defined ratings
|
|
if d.Rating == 0 || d.Rating > 0x10 {
|
|
return 0
|
|
}
|
|
return int(d.Rating) + 3
|
|
}
|
|
|
|
func newDescriptorParentalRating(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorParentalRating, err error) {
|
|
// Create descriptor
|
|
d = &DescriptorParentalRating{}
|
|
|
|
// Add items
|
|
for i.Offset() < offsetEnd {
|
|
// Get next bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytes(4); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Append item
|
|
d.Items = append(d.Items, &DescriptorParentalRatingItem{
|
|
CountryCode: bs[:3],
|
|
Rating: uint8(bs[3]),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorPrivateDataIndicator represents a private data Indicator descriptor
|
|
type DescriptorPrivateDataIndicator struct {
|
|
Indicator uint32
|
|
}
|
|
|
|
func newDescriptorPrivateDataIndicator(i *astikit.BytesIterator) (d *DescriptorPrivateDataIndicator, err error) {
|
|
// Get next bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytesNoCopy(4); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorPrivateDataIndicator{Indicator: uint32(bs[0])<<24 | uint32(bs[1])<<16 | uint32(bs[2])<<8 | uint32(bs[3])}
|
|
return
|
|
}
|
|
|
|
// DescriptorPrivateDataSpecifier represents a private data specifier descriptor
|
|
type DescriptorPrivateDataSpecifier struct {
|
|
Specifier uint32
|
|
}
|
|
|
|
func newDescriptorPrivateDataSpecifier(i *astikit.BytesIterator) (d *DescriptorPrivateDataSpecifier, err error) {
|
|
// Get next bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytesNoCopy(4); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorPrivateDataSpecifier{Specifier: uint32(bs[0])<<24 | uint32(bs[1])<<16 | uint32(bs[2])<<8 | uint32(bs[3])}
|
|
return
|
|
}
|
|
|
|
// DescriptorRegistration represents a registration descriptor
|
|
// Page: 84 | http://ecee.colorado.edu/~ecen5653/ecen5653/papers/iso13818-1.pdf
|
|
type DescriptorRegistration struct {
|
|
AdditionalIdentificationInfo []byte
|
|
FormatIdentifier uint32
|
|
}
|
|
|
|
func newDescriptorRegistration(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorRegistration, err error) {
|
|
// Get next bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytesNoCopy(4); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorRegistration{FormatIdentifier: uint32(bs[0])<<24 | uint32(bs[1])<<16 | uint32(bs[2])<<8 | uint32(bs[3])}
|
|
|
|
// Additional identification info
|
|
if i.Offset() < offsetEnd {
|
|
if d.AdditionalIdentificationInfo, err = i.NextBytes(offsetEnd - i.Offset()); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorService represents a service descriptor
|
|
// Chapter: 6.2.33 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorService struct {
|
|
Name []byte
|
|
Provider []byte
|
|
Type uint8
|
|
}
|
|
|
|
func newDescriptorService(i *astikit.BytesIterator) (d *DescriptorService, err error) {
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d = &DescriptorService{Type: uint8(b)}
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Provider length
|
|
providerLength := int(b)
|
|
|
|
// Provider
|
|
if d.Provider, err = i.NextBytes(providerLength); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Name length
|
|
nameLength := int(b)
|
|
|
|
// Name
|
|
if d.Name, err = i.NextBytes(nameLength); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorShortEvent represents a short event descriptor
|
|
// Chapter: 6.2.37 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorShortEvent struct {
|
|
EventName []byte
|
|
Language []byte
|
|
Text []byte
|
|
}
|
|
|
|
func newDescriptorShortEvent(i *astikit.BytesIterator) (d *DescriptorShortEvent, err error) {
|
|
// Create descriptor
|
|
d = &DescriptorShortEvent{}
|
|
|
|
// Language
|
|
if d.Language, err = i.NextBytes(3); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Event length
|
|
eventLength := int(b)
|
|
|
|
// Event name
|
|
if d.EventName, err = i.NextBytes(eventLength); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Text length
|
|
textLength := int(b)
|
|
|
|
// Text
|
|
if d.Text, err = i.NextBytes(textLength); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorStreamIdentifier represents a stream identifier descriptor
|
|
// Chapter: 6.2.39 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorStreamIdentifier struct {
|
|
ComponentTag uint8
|
|
}
|
|
|
|
func newDescriptorStreamIdentifier(i *astikit.BytesIterator) (d *DescriptorStreamIdentifier, err error) {
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
d = &DescriptorStreamIdentifier{ComponentTag: uint8(b)}
|
|
return
|
|
}
|
|
|
|
// DescriptorSubtitling represents a subtitling descriptor
|
|
// Chapter: 6.2.41 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorSubtitling struct {
|
|
Items []*DescriptorSubtitlingItem
|
|
}
|
|
|
|
// DescriptorSubtitlingItem represents subtitling descriptor item
|
|
// Chapter: 6.2.41 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorSubtitlingItem struct {
|
|
AncillaryPageID uint16
|
|
CompositionPageID uint16
|
|
Language []byte
|
|
Type uint8
|
|
}
|
|
|
|
func newDescriptorSubtitling(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorSubtitling, err error) {
|
|
// Create descriptor
|
|
d = &DescriptorSubtitling{}
|
|
|
|
// Loop
|
|
for i.Offset() < offsetEnd {
|
|
// Create item
|
|
itm := &DescriptorSubtitlingItem{}
|
|
|
|
// Language
|
|
if itm.Language, err = i.NextBytes(3); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Type
|
|
itm.Type = uint8(b)
|
|
|
|
// Get next bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytesNoCopy(2); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Composition page ID
|
|
itm.CompositionPageID = uint16(bs[0])<<8 | uint16(bs[1])
|
|
|
|
// Get next bytes
|
|
if bs, err = i.NextBytesNoCopy(2); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Ancillary page ID
|
|
itm.AncillaryPageID = uint16(bs[0])<<8 | uint16(bs[1])
|
|
|
|
// Append item
|
|
d.Items = append(d.Items, itm)
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorTeletext represents a teletext descriptor
|
|
// Chapter: 6.2.43 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorTeletext struct {
|
|
Items []*DescriptorTeletextItem
|
|
}
|
|
|
|
// DescriptorTeletextItem represents a teletext descriptor item
|
|
// Chapter: 6.2.43 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorTeletextItem struct {
|
|
Language []byte
|
|
Magazine uint8
|
|
Page uint8
|
|
Type uint8
|
|
}
|
|
|
|
func newDescriptorTeletext(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorTeletext, err error) {
|
|
// Create descriptor
|
|
d = &DescriptorTeletext{}
|
|
|
|
// Loop
|
|
for i.Offset() < offsetEnd {
|
|
// Create item
|
|
itm := &DescriptorTeletextItem{}
|
|
|
|
// Language
|
|
if itm.Language, err = i.NextBytes(3); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Type
|
|
itm.Type = uint8(b) >> 3
|
|
|
|
// Magazine
|
|
itm.Magazine = uint8(b & 0x7)
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Page
|
|
itm.Page = uint8(b)>>4*10 + uint8(b&0xf)
|
|
|
|
// Append item
|
|
d.Items = append(d.Items, itm)
|
|
}
|
|
return
|
|
}
|
|
|
|
type DescriptorUnknown struct {
|
|
Content []byte
|
|
Tag uint8
|
|
}
|
|
|
|
func newDescriptorUnknown(i *astikit.BytesIterator, tag, length uint8) (d *DescriptorUnknown, err error) {
|
|
// Create descriptor
|
|
d = &DescriptorUnknown{Tag: tag}
|
|
|
|
// Get next bytes
|
|
if d.Content, err = i.NextBytes(int(length)); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// DescriptorVBIData represents a VBI data descriptor
|
|
// Chapter: 6.2.47 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorVBIData struct {
|
|
Services []*DescriptorVBIDataService
|
|
}
|
|
|
|
// DescriptorVBIDataService represents a vbi data service descriptor
|
|
// Chapter: 6.2.47 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorVBIDataService struct {
|
|
DataServiceID uint8
|
|
Descriptors []*DescriptorVBIDataDescriptor
|
|
}
|
|
|
|
// DescriptorVBIDataItem represents a vbi data descriptor item
|
|
// Chapter: 6.2.47 | Link: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.15.01_60/en_300468v011501p.pdf
|
|
type DescriptorVBIDataDescriptor struct {
|
|
FieldParity bool
|
|
LineOffset uint8
|
|
}
|
|
|
|
func newDescriptorVBIData(i *astikit.BytesIterator, offsetEnd int) (d *DescriptorVBIData, err error) {
|
|
// Create descriptor
|
|
d = &DescriptorVBIData{}
|
|
|
|
// Loop
|
|
for i.Offset() < offsetEnd {
|
|
// Create service
|
|
srv := &DescriptorVBIDataService{}
|
|
|
|
// Get next byte
|
|
var b byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Data service ID
|
|
srv.DataServiceID = uint8(b)
|
|
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Data service descriptor length
|
|
dataServiceDescriptorLength := int(b)
|
|
|
|
// Data service descriptor
|
|
offsetDataEnd := i.Offset() + dataServiceDescriptorLength
|
|
for i.Offset() < offsetDataEnd {
|
|
if srv.DataServiceID == VBIDataServiceIDClosedCaptioning ||
|
|
srv.DataServiceID == VBIDataServiceIDEBUTeletext ||
|
|
srv.DataServiceID == VBIDataServiceIDInvertedTeletext ||
|
|
srv.DataServiceID == VBIDataServiceIDMonochrome442Samples ||
|
|
srv.DataServiceID == VBIDataServiceIDVPS ||
|
|
srv.DataServiceID == VBIDataServiceIDWSS {
|
|
// Get next byte
|
|
if b, err = i.NextByte(); err != nil {
|
|
err = fmt.Errorf("astits: fetching next byte failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Append data
|
|
srv.Descriptors = append(srv.Descriptors, &DescriptorVBIDataDescriptor{
|
|
FieldParity: b&0x20 > 0,
|
|
LineOffset: uint8(b & 0x1f),
|
|
})
|
|
}
|
|
}
|
|
|
|
// Append service
|
|
d.Services = append(d.Services, srv)
|
|
}
|
|
return
|
|
}
|
|
|
|
// parseDescriptors parses descriptors
|
|
func parseDescriptors(i *astikit.BytesIterator) (o []*Descriptor, err error) {
|
|
// Get next 2 bytes
|
|
var bs []byte
|
|
if bs, err = i.NextBytesNoCopy(2); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Get length
|
|
length := int(uint16(bs[0]&0xf)<<8 | uint16(bs[1]))
|
|
|
|
// Loop
|
|
if length > 0 {
|
|
offsetEnd := i.Offset() + length
|
|
for i.Offset() < offsetEnd {
|
|
// Get next 2 bytes
|
|
if bs, err = i.NextBytesNoCopy(2); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
|
|
// Create descriptor
|
|
d := &Descriptor{
|
|
Length: uint8(bs[1]),
|
|
Tag: uint8(bs[0]),
|
|
}
|
|
|
|
// Parse data
|
|
if d.Length > 0 {
|
|
// Unfortunately there's no way to be sure the real descriptor length is the same as the one indicated
|
|
// previously therefore we must fetch bytes in descriptor functions and seek at the end
|
|
offsetDescriptorEnd := i.Offset() + int(d.Length)
|
|
|
|
// User defined
|
|
if d.Tag >= 0x80 && d.Tag <= 0xfe {
|
|
// Get next bytes
|
|
if d.UserDefined, err = i.NextBytes(int(d.Length)); err != nil {
|
|
err = fmt.Errorf("astits: fetching next bytes failed: %w", err)
|
|
return
|
|
}
|
|
} else {
|
|
// Switch on tag
|
|
switch d.Tag {
|
|
case DescriptorTagAC3:
|
|
if d.AC3, err = newDescriptorAC3(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing AC3 descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagAVCVideo:
|
|
if d.AVCVideo, err = newDescriptorAVCVideo(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing AVC Video descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagComponent:
|
|
if d.Component, err = newDescriptorComponent(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Component descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagContent:
|
|
if d.Content, err = newDescriptorContent(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Content descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagDataStreamAlignment:
|
|
if d.DataStreamAlignment, err = newDescriptorDataStreamAlignment(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing Data Stream Alignment descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagEnhancedAC3:
|
|
if d.EnhancedAC3, err = newDescriptorEnhancedAC3(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Enhanced AC3 descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagExtendedEvent:
|
|
if d.ExtendedEvent, err = newDescriptorExtendedEvent(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing Extended event descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagExtension:
|
|
if d.Extension, err = newDescriptorExtension(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Extension descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagISO639LanguageAndAudioType:
|
|
if d.ISO639LanguageAndAudioType, err = newDescriptorISO639LanguageAndAudioType(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing ISO639 Language and Audio Type descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagLocalTimeOffset:
|
|
if d.LocalTimeOffset, err = newDescriptorLocalTimeOffset(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Local Time Offset descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagMaximumBitrate:
|
|
if d.MaximumBitrate, err = newDescriptorMaximumBitrate(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing Maximum Bitrate descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagNetworkName:
|
|
if d.NetworkName, err = newDescriptorNetworkName(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Network Name descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagParentalRating:
|
|
if d.ParentalRating, err = newDescriptorParentalRating(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Parental Rating descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagPrivateDataIndicator:
|
|
if d.PrivateDataIndicator, err = newDescriptorPrivateDataIndicator(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing Private Data Indicator descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagPrivateDataSpecifier:
|
|
if d.PrivateDataSpecifier, err = newDescriptorPrivateDataSpecifier(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing Private Data Specifier descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagRegistration:
|
|
if d.Registration, err = newDescriptorRegistration(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Registration descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagService:
|
|
if d.Service, err = newDescriptorService(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing Service descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagShortEvent:
|
|
if d.ShortEvent, err = newDescriptorShortEvent(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing Short Event descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagStreamIdentifier:
|
|
if d.StreamIdentifier, err = newDescriptorStreamIdentifier(i); err != nil {
|
|
err = fmt.Errorf("astits: parsing Stream Identifier descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagSubtitling:
|
|
if d.Subtitling, err = newDescriptorSubtitling(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Subtitling descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagTeletext:
|
|
if d.Teletext, err = newDescriptorTeletext(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing Teletext descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagVBIData:
|
|
if d.VBIData, err = newDescriptorVBIData(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing VBI Date descriptor failed: %w", err)
|
|
return
|
|
}
|
|
case DescriptorTagVBITeletext:
|
|
if d.VBITeletext, err = newDescriptorTeletext(i, offsetDescriptorEnd); err != nil {
|
|
err = fmt.Errorf("astits: parsing VBI Teletext descriptor failed: %w", err)
|
|
return
|
|
}
|
|
default:
|
|
if d.Unknown, err = newDescriptorUnknown(i, d.Tag, d.Length); err != nil {
|
|
err = fmt.Errorf("astits: parsing unknown descriptor failed: %w", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Seek in iterator to make sure we move to the end of the descriptor since its content may be
|
|
// corrupted
|
|
i.Seek(offsetDescriptorEnd)
|
|
}
|
|
o = append(o, d)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func calcDescriptorUserDefinedLength(d []byte) uint8 {
|
|
return uint8(len(d))
|
|
}
|
|
|
|
func writeDescriptorUserDefined(w *astikit.BitsWriter, d []byte) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorAC3Length(d *DescriptorAC3) uint8 {
|
|
ret := 1 // flags
|
|
|
|
if d.HasComponentType {
|
|
ret++
|
|
}
|
|
if d.HasBSID {
|
|
ret++
|
|
}
|
|
if d.HasMainID {
|
|
ret++
|
|
}
|
|
if d.HasASVC {
|
|
ret++
|
|
}
|
|
|
|
ret += len(d.AdditionalInfo)
|
|
|
|
return uint8(ret)
|
|
}
|
|
|
|
func writeDescriptorAC3(w *astikit.BitsWriter, d *DescriptorAC3) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.HasComponentType)
|
|
b.Write(d.HasBSID)
|
|
b.Write(d.HasMainID)
|
|
b.Write(d.HasASVC)
|
|
b.WriteN(uint8(0xff), 4)
|
|
|
|
if d.HasComponentType {
|
|
b.Write(d.ComponentType)
|
|
}
|
|
if d.HasBSID {
|
|
b.Write(d.BSID)
|
|
}
|
|
if d.HasMainID {
|
|
b.Write(d.MainID)
|
|
}
|
|
if d.HasASVC {
|
|
b.Write(d.ASVC)
|
|
}
|
|
b.Write(d.AdditionalInfo)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorAVCVideoLength(d *DescriptorAVCVideo) uint8 {
|
|
return 4
|
|
}
|
|
|
|
func writeDescriptorAVCVideo(w *astikit.BitsWriter, d *DescriptorAVCVideo) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.ProfileIDC)
|
|
|
|
b.Write(d.ConstraintSet0Flag)
|
|
b.Write(d.ConstraintSet1Flag)
|
|
b.Write(d.ConstraintSet2Flag)
|
|
b.WriteN(d.CompatibleFlags, 5)
|
|
|
|
b.Write(d.LevelIDC)
|
|
|
|
b.Write(d.AVCStillPresent)
|
|
b.Write(d.AVC24HourPictureFlag)
|
|
b.WriteN(uint8(0xff), 6)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorComponentLength(d *DescriptorComponent) uint8 {
|
|
return uint8(6 + len(d.Text))
|
|
}
|
|
|
|
func writeDescriptorComponent(w *astikit.BitsWriter, d *DescriptorComponent) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.WriteN(d.StreamContentExt, 4)
|
|
b.WriteN(d.StreamContent, 4)
|
|
|
|
b.Write(d.ComponentType)
|
|
b.Write(d.ComponentTag)
|
|
|
|
b.WriteBytesN(d.ISO639LanguageCode, 3, 0)
|
|
|
|
b.Write(d.Text)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorContentLength(d *DescriptorContent) uint8 {
|
|
return uint8(2 * len(d.Items))
|
|
}
|
|
|
|
func writeDescriptorContent(w *astikit.BitsWriter, d *DescriptorContent) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
for _, item := range d.Items {
|
|
b.WriteN(item.ContentNibbleLevel1, 4)
|
|
b.WriteN(item.ContentNibbleLevel2, 4)
|
|
b.Write(item.UserByte)
|
|
}
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorDataStreamAlignmentLength(d *DescriptorDataStreamAlignment) uint8 {
|
|
return 1
|
|
}
|
|
|
|
func writeDescriptorDataStreamAlignment(w *astikit.BitsWriter, d *DescriptorDataStreamAlignment) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.Type)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorEnhancedAC3Length(d *DescriptorEnhancedAC3) uint8 {
|
|
ret := 1 // flags
|
|
|
|
if d.HasComponentType {
|
|
ret++
|
|
}
|
|
if d.HasBSID {
|
|
ret++
|
|
}
|
|
if d.HasMainID {
|
|
ret++
|
|
}
|
|
if d.HasASVC {
|
|
ret++
|
|
}
|
|
if d.HasSubStream1 {
|
|
ret++
|
|
}
|
|
if d.HasSubStream2 {
|
|
ret++
|
|
}
|
|
if d.HasSubStream3 {
|
|
ret++
|
|
}
|
|
|
|
ret += len(d.AdditionalInfo)
|
|
|
|
return uint8(ret)
|
|
}
|
|
|
|
func writeDescriptorEnhancedAC3(w *astikit.BitsWriter, d *DescriptorEnhancedAC3) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.HasComponentType)
|
|
b.Write(d.HasBSID)
|
|
b.Write(d.HasMainID)
|
|
b.Write(d.HasASVC)
|
|
b.Write(d.MixInfoExists)
|
|
b.Write(d.HasSubStream1)
|
|
b.Write(d.HasSubStream2)
|
|
b.Write(d.HasSubStream3)
|
|
|
|
if d.HasComponentType {
|
|
b.Write(d.ComponentType)
|
|
}
|
|
if d.HasBSID {
|
|
b.Write(d.BSID)
|
|
}
|
|
if d.HasMainID {
|
|
b.Write(d.MainID)
|
|
}
|
|
if d.HasASVC {
|
|
b.Write(d.ASVC)
|
|
}
|
|
if d.HasSubStream1 {
|
|
b.Write(d.SubStream1)
|
|
}
|
|
if d.HasSubStream2 {
|
|
b.Write(d.SubStream2)
|
|
}
|
|
if d.HasSubStream3 {
|
|
b.Write(d.SubStream3)
|
|
}
|
|
|
|
b.Write(d.AdditionalInfo)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorExtendedEventLength(d *DescriptorExtendedEvent) (descriptorLength, lengthOfItems uint8) {
|
|
ret := 1 + 3 + 1 // numbers, language and items length
|
|
|
|
itemsRet := 0
|
|
for _, item := range d.Items {
|
|
itemsRet += 1 // description length
|
|
itemsRet += len(item.Description)
|
|
itemsRet += 1 // content length
|
|
itemsRet += len(item.Content)
|
|
}
|
|
|
|
ret += itemsRet
|
|
|
|
ret += 1 // text length
|
|
ret += len(d.Text)
|
|
|
|
return uint8(ret), uint8(itemsRet)
|
|
}
|
|
|
|
func writeDescriptorExtendedEvent(w *astikit.BitsWriter, d *DescriptorExtendedEvent) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
var lengthOfItems uint8
|
|
|
|
_, lengthOfItems = calcDescriptorExtendedEventLength(d)
|
|
|
|
b.WriteN(d.Number, 4)
|
|
b.WriteN(d.LastDescriptorNumber, 4)
|
|
|
|
b.WriteBytesN(d.ISO639LanguageCode, 3, 0)
|
|
|
|
b.Write(lengthOfItems)
|
|
for _, item := range d.Items {
|
|
b.Write(uint8(len(item.Description)))
|
|
b.Write(item.Description)
|
|
b.Write(uint8(len(item.Content)))
|
|
b.Write(item.Content)
|
|
}
|
|
|
|
b.Write(uint8(len(d.Text)))
|
|
b.Write(d.Text)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorExtensionSupplementaryAudioLength(d *DescriptorExtensionSupplementaryAudio) int {
|
|
ret := 1
|
|
if d.HasLanguageCode {
|
|
ret += 3
|
|
}
|
|
ret += len(d.PrivateData)
|
|
return ret
|
|
}
|
|
|
|
func calcDescriptorExtensionLength(d *DescriptorExtension) uint8 {
|
|
ret := 1 // tag
|
|
|
|
switch d.Tag {
|
|
case DescriptorTagExtensionSupplementaryAudio:
|
|
ret += calcDescriptorExtensionSupplementaryAudioLength(d.SupplementaryAudio)
|
|
default:
|
|
if d.Unknown != nil {
|
|
ret += len(*d.Unknown)
|
|
}
|
|
}
|
|
|
|
return uint8(ret)
|
|
}
|
|
|
|
func writeDescriptorExtensionSupplementaryAudio(w *astikit.BitsWriter, d *DescriptorExtensionSupplementaryAudio) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.MixType)
|
|
b.WriteN(d.EditorialClassification, 5)
|
|
b.Write(true) // reserved
|
|
b.Write(d.HasLanguageCode)
|
|
|
|
if d.HasLanguageCode {
|
|
b.WriteBytesN(d.LanguageCode, 3, 0)
|
|
}
|
|
|
|
b.Write(d.PrivateData)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func writeDescriptorExtension(w *astikit.BitsWriter, d *DescriptorExtension) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.Tag)
|
|
|
|
switch d.Tag {
|
|
case DescriptorTagExtensionSupplementaryAudio:
|
|
err := writeDescriptorExtensionSupplementaryAudio(w, d.SupplementaryAudio)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
if d.Unknown != nil {
|
|
b.Write(*d.Unknown)
|
|
}
|
|
}
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorISO639LanguageAndAudioTypeLength(d *DescriptorISO639LanguageAndAudioType) uint8 {
|
|
return 3 + 1 // language code + type
|
|
}
|
|
|
|
func writeDescriptorISO639LanguageAndAudioType(w *astikit.BitsWriter, d *DescriptorISO639LanguageAndAudioType) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.WriteBytesN(d.Language, 3, 0)
|
|
b.Write(d.Type)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorLocalTimeOffsetLength(d *DescriptorLocalTimeOffset) uint8 {
|
|
return uint8(13 * len(d.Items))
|
|
}
|
|
|
|
func writeDescriptorLocalTimeOffset(w *astikit.BitsWriter, d *DescriptorLocalTimeOffset) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
for _, item := range d.Items {
|
|
b.WriteBytesN(item.CountryCode, 3, 0)
|
|
|
|
b.WriteN(item.CountryRegionID, 6)
|
|
b.WriteN(uint8(0xff), 1)
|
|
b.Write(item.LocalTimeOffsetPolarity)
|
|
|
|
if _, err := writeDVBDurationMinutes(w, item.LocalTimeOffset); err != nil {
|
|
return err
|
|
}
|
|
if _, err := writeDVBTime(w, item.TimeOfChange); err != nil {
|
|
return err
|
|
}
|
|
if _, err := writeDVBDurationMinutes(w, item.NextTimeOffset); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorMaximumBitrateLength(d *DescriptorMaximumBitrate) uint8 {
|
|
return 3
|
|
}
|
|
|
|
func writeDescriptorMaximumBitrate(w *astikit.BitsWriter, d *DescriptorMaximumBitrate) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.WriteN(uint8(0xff), 2)
|
|
b.WriteN(uint32(d.Bitrate/50), 22)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorNetworkNameLength(d *DescriptorNetworkName) uint8 {
|
|
return uint8(len(d.Name))
|
|
}
|
|
|
|
func writeDescriptorNetworkName(w *astikit.BitsWriter, d *DescriptorNetworkName) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.Name)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorParentalRatingLength(d *DescriptorParentalRating) uint8 {
|
|
return uint8(4 * len(d.Items))
|
|
}
|
|
|
|
func writeDescriptorParentalRating(w *astikit.BitsWriter, d *DescriptorParentalRating) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
for _, item := range d.Items {
|
|
b.WriteBytesN(item.CountryCode, 3, 0)
|
|
b.Write(item.Rating)
|
|
}
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorPrivateDataIndicatorLength(d *DescriptorPrivateDataIndicator) uint8 {
|
|
return 4
|
|
}
|
|
|
|
func writeDescriptorPrivateDataIndicator(w *astikit.BitsWriter, d *DescriptorPrivateDataIndicator) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.Indicator)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorPrivateDataSpecifierLength(d *DescriptorPrivateDataSpecifier) uint8 {
|
|
return 4
|
|
}
|
|
|
|
func writeDescriptorPrivateDataSpecifier(w *astikit.BitsWriter, d *DescriptorPrivateDataSpecifier) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.Specifier)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorRegistrationLength(d *DescriptorRegistration) uint8 {
|
|
return uint8(4 + len(d.AdditionalIdentificationInfo))
|
|
}
|
|
|
|
func writeDescriptorRegistration(w *astikit.BitsWriter, d *DescriptorRegistration) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.FormatIdentifier)
|
|
b.Write(d.AdditionalIdentificationInfo)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorServiceLength(d *DescriptorService) uint8 {
|
|
ret := 3 // type and lengths
|
|
ret += len(d.Name)
|
|
ret += len(d.Provider)
|
|
return uint8(ret)
|
|
}
|
|
|
|
func writeDescriptorService(w *astikit.BitsWriter, d *DescriptorService) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.Type)
|
|
b.Write(uint8(len(d.Provider)))
|
|
b.Write(d.Provider)
|
|
b.Write(uint8(len(d.Name)))
|
|
b.Write(d.Name)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorShortEventLength(d *DescriptorShortEvent) uint8 {
|
|
ret := 3 + 1 + 1 // language code and lengths
|
|
ret += len(d.EventName)
|
|
ret += len(d.Text)
|
|
return uint8(ret)
|
|
}
|
|
|
|
func writeDescriptorShortEvent(w *astikit.BitsWriter, d *DescriptorShortEvent) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.WriteBytesN(d.Language, 3, 0)
|
|
|
|
b.Write(uint8(len(d.EventName)))
|
|
b.Write(d.EventName)
|
|
|
|
b.Write(uint8(len(d.Text)))
|
|
b.Write(d.Text)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorStreamIdentifierLength(d *DescriptorStreamIdentifier) uint8 {
|
|
return 1
|
|
}
|
|
|
|
func writeDescriptorStreamIdentifier(w *astikit.BitsWriter, d *DescriptorStreamIdentifier) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.ComponentTag)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorSubtitlingLength(d *DescriptorSubtitling) uint8 {
|
|
return uint8(8 * len(d.Items))
|
|
}
|
|
|
|
func writeDescriptorSubtitling(w *astikit.BitsWriter, d *DescriptorSubtitling) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
for _, item := range d.Items {
|
|
b.WriteBytesN(item.Language, 3, 0)
|
|
b.Write(item.Type)
|
|
b.Write(item.CompositionPageID)
|
|
b.Write(item.AncillaryPageID)
|
|
}
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorTeletextLength(d *DescriptorTeletext) uint8 {
|
|
return uint8(5 * len(d.Items))
|
|
}
|
|
|
|
func writeDescriptorTeletext(w *astikit.BitsWriter, d *DescriptorTeletext) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
for _, item := range d.Items {
|
|
b.WriteBytesN(item.Language, 3, 0)
|
|
b.WriteN(item.Type, 5)
|
|
b.WriteN(item.Magazine, 3)
|
|
b.WriteN(item.Page/10, 4)
|
|
b.WriteN(item.Page%10, 4)
|
|
}
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorVBIDataLength(d *DescriptorVBIData) uint8 {
|
|
return uint8(3 * len(d.Services))
|
|
}
|
|
|
|
func writeDescriptorVBIData(w *astikit.BitsWriter, d *DescriptorVBIData) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
for _, item := range d.Services {
|
|
b.Write(item.DataServiceID)
|
|
|
|
if item.DataServiceID == VBIDataServiceIDClosedCaptioning ||
|
|
item.DataServiceID == VBIDataServiceIDEBUTeletext ||
|
|
item.DataServiceID == VBIDataServiceIDInvertedTeletext ||
|
|
item.DataServiceID == VBIDataServiceIDMonochrome442Samples ||
|
|
item.DataServiceID == VBIDataServiceIDVPS ||
|
|
item.DataServiceID == VBIDataServiceIDWSS {
|
|
|
|
b.Write(uint8(len(item.Descriptors))) // each descriptor is 1 byte
|
|
for _, desc := range item.Descriptors {
|
|
b.WriteN(uint8(0xff), 2)
|
|
b.Write(desc.FieldParity)
|
|
b.WriteN(desc.LineOffset, 5)
|
|
}
|
|
} else {
|
|
// let's put one reserved byte
|
|
b.Write(uint8(1))
|
|
b.Write(uint8(0xff))
|
|
}
|
|
}
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorUnknownLength(d *DescriptorUnknown) uint8 {
|
|
return uint8(len(d.Content))
|
|
}
|
|
|
|
func writeDescriptorUnknown(w *astikit.BitsWriter, d *DescriptorUnknown) error {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.Write(d.Content)
|
|
|
|
return b.Err()
|
|
}
|
|
|
|
func calcDescriptorLength(d *Descriptor) uint8 {
|
|
if d.Tag >= 0x80 && d.Tag <= 0xfe {
|
|
return calcDescriptorUserDefinedLength(d.UserDefined)
|
|
}
|
|
|
|
switch d.Tag {
|
|
case DescriptorTagAC3:
|
|
return calcDescriptorAC3Length(d.AC3)
|
|
case DescriptorTagAVCVideo:
|
|
return calcDescriptorAVCVideoLength(d.AVCVideo)
|
|
case DescriptorTagComponent:
|
|
return calcDescriptorComponentLength(d.Component)
|
|
case DescriptorTagContent:
|
|
return calcDescriptorContentLength(d.Content)
|
|
case DescriptorTagDataStreamAlignment:
|
|
return calcDescriptorDataStreamAlignmentLength(d.DataStreamAlignment)
|
|
case DescriptorTagEnhancedAC3:
|
|
return calcDescriptorEnhancedAC3Length(d.EnhancedAC3)
|
|
case DescriptorTagExtendedEvent:
|
|
ret, _ := calcDescriptorExtendedEventLength(d.ExtendedEvent)
|
|
return ret
|
|
case DescriptorTagExtension:
|
|
return calcDescriptorExtensionLength(d.Extension)
|
|
case DescriptorTagISO639LanguageAndAudioType:
|
|
return calcDescriptorISO639LanguageAndAudioTypeLength(d.ISO639LanguageAndAudioType)
|
|
case DescriptorTagLocalTimeOffset:
|
|
return calcDescriptorLocalTimeOffsetLength(d.LocalTimeOffset)
|
|
case DescriptorTagMaximumBitrate:
|
|
return calcDescriptorMaximumBitrateLength(d.MaximumBitrate)
|
|
case DescriptorTagNetworkName:
|
|
return calcDescriptorNetworkNameLength(d.NetworkName)
|
|
case DescriptorTagParentalRating:
|
|
return calcDescriptorParentalRatingLength(d.ParentalRating)
|
|
case DescriptorTagPrivateDataIndicator:
|
|
return calcDescriptorPrivateDataIndicatorLength(d.PrivateDataIndicator)
|
|
case DescriptorTagPrivateDataSpecifier:
|
|
return calcDescriptorPrivateDataSpecifierLength(d.PrivateDataSpecifier)
|
|
case DescriptorTagRegistration:
|
|
return calcDescriptorRegistrationLength(d.Registration)
|
|
case DescriptorTagService:
|
|
return calcDescriptorServiceLength(d.Service)
|
|
case DescriptorTagShortEvent:
|
|
return calcDescriptorShortEventLength(d.ShortEvent)
|
|
case DescriptorTagStreamIdentifier:
|
|
return calcDescriptorStreamIdentifierLength(d.StreamIdentifier)
|
|
case DescriptorTagSubtitling:
|
|
return calcDescriptorSubtitlingLength(d.Subtitling)
|
|
case DescriptorTagTeletext:
|
|
return calcDescriptorTeletextLength(d.Teletext)
|
|
case DescriptorTagVBIData:
|
|
return calcDescriptorVBIDataLength(d.VBIData)
|
|
case DescriptorTagVBITeletext:
|
|
return calcDescriptorTeletextLength(d.VBITeletext)
|
|
}
|
|
|
|
return calcDescriptorUnknownLength(d.Unknown)
|
|
}
|
|
|
|
func writeDescriptor(w *astikit.BitsWriter, d *Descriptor) (int, error) {
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
length := calcDescriptorLength(d)
|
|
|
|
b.Write(d.Tag)
|
|
b.Write(length)
|
|
|
|
if err := b.Err(); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
written := int(length) + 2
|
|
|
|
if d.Tag >= 0x80 && d.Tag <= 0xfe {
|
|
return written, writeDescriptorUserDefined(w, d.UserDefined)
|
|
}
|
|
|
|
switch d.Tag {
|
|
case DescriptorTagAC3:
|
|
return written, writeDescriptorAC3(w, d.AC3)
|
|
case DescriptorTagAVCVideo:
|
|
return written, writeDescriptorAVCVideo(w, d.AVCVideo)
|
|
case DescriptorTagComponent:
|
|
return written, writeDescriptorComponent(w, d.Component)
|
|
case DescriptorTagContent:
|
|
return written, writeDescriptorContent(w, d.Content)
|
|
case DescriptorTagDataStreamAlignment:
|
|
return written, writeDescriptorDataStreamAlignment(w, d.DataStreamAlignment)
|
|
case DescriptorTagEnhancedAC3:
|
|
return written, writeDescriptorEnhancedAC3(w, d.EnhancedAC3)
|
|
case DescriptorTagExtendedEvent:
|
|
return written, writeDescriptorExtendedEvent(w, d.ExtendedEvent)
|
|
case DescriptorTagExtension:
|
|
return written, writeDescriptorExtension(w, d.Extension)
|
|
case DescriptorTagISO639LanguageAndAudioType:
|
|
return written, writeDescriptorISO639LanguageAndAudioType(w, d.ISO639LanguageAndAudioType)
|
|
case DescriptorTagLocalTimeOffset:
|
|
return written, writeDescriptorLocalTimeOffset(w, d.LocalTimeOffset)
|
|
case DescriptorTagMaximumBitrate:
|
|
return written, writeDescriptorMaximumBitrate(w, d.MaximumBitrate)
|
|
case DescriptorTagNetworkName:
|
|
return written, writeDescriptorNetworkName(w, d.NetworkName)
|
|
case DescriptorTagParentalRating:
|
|
return written, writeDescriptorParentalRating(w, d.ParentalRating)
|
|
case DescriptorTagPrivateDataIndicator:
|
|
return written, writeDescriptorPrivateDataIndicator(w, d.PrivateDataIndicator)
|
|
case DescriptorTagPrivateDataSpecifier:
|
|
return written, writeDescriptorPrivateDataSpecifier(w, d.PrivateDataSpecifier)
|
|
case DescriptorTagRegistration:
|
|
return written, writeDescriptorRegistration(w, d.Registration)
|
|
case DescriptorTagService:
|
|
return written, writeDescriptorService(w, d.Service)
|
|
case DescriptorTagShortEvent:
|
|
return written, writeDescriptorShortEvent(w, d.ShortEvent)
|
|
case DescriptorTagStreamIdentifier:
|
|
return written, writeDescriptorStreamIdentifier(w, d.StreamIdentifier)
|
|
case DescriptorTagSubtitling:
|
|
return written, writeDescriptorSubtitling(w, d.Subtitling)
|
|
case DescriptorTagTeletext:
|
|
return written, writeDescriptorTeletext(w, d.Teletext)
|
|
case DescriptorTagVBIData:
|
|
return written, writeDescriptorVBIData(w, d.VBIData)
|
|
case DescriptorTagVBITeletext:
|
|
return written, writeDescriptorTeletext(w, d.VBITeletext)
|
|
}
|
|
|
|
return written, writeDescriptorUnknown(w, d.Unknown)
|
|
}
|
|
|
|
func calcDescriptorsLength(ds []*Descriptor) uint16 {
|
|
length := uint16(0)
|
|
for _, d := range ds {
|
|
length += 2 // tag and length
|
|
length += uint16(calcDescriptorLength(d))
|
|
}
|
|
return length
|
|
}
|
|
|
|
func writeDescriptors(w *astikit.BitsWriter, ds []*Descriptor) (int, error) {
|
|
written := 0
|
|
|
|
for _, d := range ds {
|
|
n, err := writeDescriptor(w, d)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
written += n
|
|
}
|
|
|
|
return written, nil
|
|
}
|
|
|
|
func writeDescriptorsWithLength(w *astikit.BitsWriter, ds []*Descriptor) (int, error) {
|
|
length := calcDescriptorsLength(ds)
|
|
b := astikit.NewBitsWriterBatch(w)
|
|
|
|
b.WriteN(uint8(0xff), 4) // reserved
|
|
b.WriteN(length, 12) // program_info_length
|
|
|
|
if err := b.Err(); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
written, err := writeDescriptors(w, ds)
|
|
return written + 2, err // 2 for length
|
|
}
|