diff --git a/data.go b/data.go index edc1046..f8859f9 100644 --- a/data.go +++ b/data.go @@ -2,6 +2,7 @@ package astits import ( "github.com/pkg/errors" + "github.com/asticode/go-astitools/byte" ) // PIDs @@ -38,40 +39,53 @@ func parseData(ps []*Packet, prs PacketsParser, pm programMap) (ds []*Data, err } } - // Reconstruct payload + // Get payload length var l int for _, p := range ps { l += len(p.Payload) } + + // Append payload var payload = make([]byte, l) var c int for _, p := range ps { c += copy(payload[c:], p.Payload) } + // Create iterator + i := astibyte.NewIterator(payload) + // Parse PID - var pid = ps[0].Header.PID + pid := ps[0].Header.PID // Parse payload if pid == PIDCAT { // Information in a CAT payload is private and dependent on the CA system. Use the PacketsParser // to parse this type of payload } else if isPSIPayload(pid, pm) { + // Parse PSI data var psiData *PSIData - if psiData, err = parsePSIData(payload); err != nil { + if psiData, err = parsePSIData(i); err != nil { err = errors.Wrap(err, "astits: parsing PSI data failed") return } + + // Append data ds = psiData.toData(ps[0], pid) } else if isPESPayload(payload) { - d, err := parsePESData(payload) - if err == nil { - ds = append(ds, &Data{ - FirstPacket: ps[0], - PES: d, - PID: pid, - }) + // Parse PES data + var pesData *PESData + if pesData, err = parsePESData(i); err != nil { + err = errors.Wrap(err, "astits: parsing PES data failed") + return } + + // Append data + ds = append(ds, &Data{ + FirstPacket: ps[0], + PES: pesData, + PID: pid, + }) } return } diff --git a/data_eit.go b/data_eit.go index 42d5a27..21fa892 100644 --- a/data_eit.go +++ b/data_eit.go @@ -1,6 +1,11 @@ package astits -import "time" +import ( + "time" + + astibyte "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" +) // EITData represents an EIT data // Page: 36 | Chapter: 5.2.4 | Link: https://www.dvb.org/resources/public/standards/a38_dvb-si_specification.pdf @@ -24,47 +29,92 @@ type EITDataEvent struct { } // parseEITSection parses an EIT section -func parseEITSection(i []byte, offset *int, offsetSectionsEnd int, tableIDExtension uint16) (d *EITData) { - // Init +func parseEITSection(i *astibyte.Iterator, offsetSectionsEnd int, tableIDExtension uint16) (d *EITData, err error) { + // Create data d = &EITData{ServiceID: tableIDExtension} + // Get next 2 bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Transport stream ID - d.TransportStreamID = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 + d.TransportStreamID = uint16(bs[0])<<8 | uint16(bs[1]) + + // Get next 2 bytes + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } // Original network ID - d.OriginalNetworkID = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 + d.OriginalNetworkID = uint16(bs[0])<<8 | uint16(bs[1]) + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Segment last section number - d.SegmentLastSectionNumber = uint8(i[*offset]) - *offset += 1 + d.SegmentLastSectionNumber = uint8(b) + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Last table ID - d.LastTableID = uint8(i[*offset]) - *offset += 1 + d.LastTableID = uint8(b) // Loop until end of section data is reached - for *offset < offsetSectionsEnd { + for i.Offset() < offsetSectionsEnd { + // Get next 2 bytes + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Event ID var e = &EITDataEvent{} - e.EventID = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 + e.EventID = uint16(bs[0])<<8 | uint16(bs[1]) // Start time - e.StartTime = parseDVBTime(i, offset) + if e.StartTime, err = parseDVBTime(i); err != nil { + err = errors.Wrap(err, "astits: parsing DVB time") + return + } // Duration - e.Duration = parseDVBDurationSeconds(i, offset) + if e.Duration, err = parseDVBDurationSeconds(i); err != nil { + err = errors.Wrap(err, "astits: parsing DVB duration seconds failed") + return + } + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Running status - e.RunningStatus = uint8(i[*offset]) >> 5 + e.RunningStatus = uint8(b) >> 5 // Free CA mode - e.HasFreeCSAMode = uint8(i[*offset]&0x10) > 0 + e.HasFreeCSAMode = uint8(b&0x10) > 0 + + // We need to rewind since the current byte is used by the descriptor as well + i.FastForward(-1) // Descriptors - e.Descriptors = parseDescriptors(i, offset) + if e.Descriptors, err = parseDescriptors(i); err != nil { + err = errors.Wrap(err, "astits: parsing descriptors failed") + return + } // Add event d.Events = append(d.Events, e) diff --git a/data_eit_test.go b/data_eit_test.go index a7036e6..0a76269 100644 --- a/data_eit_test.go +++ b/data_eit_test.go @@ -5,6 +5,7 @@ import ( "github.com/asticode/go-astitools/binary" "github.com/stretchr/testify/assert" + "github.com/asticode/go-astitools/byte" ) var eit = &EITData{ @@ -39,8 +40,8 @@ func eitBytes() []byte { } func TestParseEITSection(t *testing.T) { - var offset int var b = eitBytes() - d := parseEITSection(b, &offset, len(b), uint16(1)) + d, err := parseEITSection(astibyte.NewIterator(b), len(b), uint16(1)) assert.Equal(t, d, eit) + assert.NoError(t, err) } diff --git a/data_nit.go b/data_nit.go index 80e9fde..ecff474 100644 --- a/data_nit.go +++ b/data_nit.go @@ -1,5 +1,10 @@ package astits +import ( + astibyte "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" +) + // NITData represents a NIT data // Page: 29 | Chapter: 5.2.1 | Link: https://www.dvb.org/resources/public/standards/a38_dvb-si_specification.pdf type NITData struct { @@ -16,31 +21,55 @@ type NITDataTransportStream struct { } // parseNITSection parses a NIT section -func parseNITSection(i []byte, offset *int, tableIDExtension uint16) (d *NITData) { - // Init +func parseNITSection(i *astibyte.Iterator, tableIDExtension uint16) (d *NITData, err error) { + // Create data d = &NITData{NetworkID: tableIDExtension} // Network descriptors - d.NetworkDescriptors = parseDescriptors(i, offset) + if d.NetworkDescriptors, err = parseDescriptors(i); err != nil { + err = errors.Wrap(err, "astits: parsing descriptors failed") + return + } + + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } // Transport stream loop length - var transportStreamLoopLength = int(uint16(i[*offset]&0xf)<<8 | uint16(i[*offset+1])) - *offset += 2 + transportStreamLoopLength := int(uint16(bs[0]&0xf)<<8 | uint16(bs[1])) // Transport stream loop - transportStreamLoopLength += *offset - for *offset < transportStreamLoopLength { + offsetEnd := i.Offset() + transportStreamLoopLength + for i.Offset() < offsetEnd { + // Create transport stream + ts := &NITDataTransportStream{} + + // Get next bytes + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Transport stream ID - var ts = &NITDataTransportStream{} - ts.TransportStreamID = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 + ts.TransportStreamID = uint16(bs[0])<<8 | uint16(bs[1]) + + // Get next bytes + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } // Original network ID - ts.OriginalNetworkID = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 + ts.OriginalNetworkID = uint16(bs[0])<<8 | uint16(bs[1]) // Transport descriptors - ts.TransportDescriptors = parseDescriptors(i, offset) + if ts.TransportDescriptors, err = parseDescriptors(i); err != nil { + err = errors.Wrap(err, "astits: parsing descriptors failed") + return + } // Append transport stream d.TransportStreams = append(d.TransportStreams, ts) diff --git a/data_nit_test.go b/data_nit_test.go index 4216486..d26cc4c 100644 --- a/data_nit_test.go +++ b/data_nit_test.go @@ -3,7 +3,8 @@ package astits import ( "testing" - "github.com/asticode/go-astitools/binary" + astibinary "github.com/asticode/go-astitools/binary" + astibyte "github.com/asticode/go-astitools/byte" "github.com/stretchr/testify/assert" ) @@ -31,8 +32,8 @@ func nitBytes() []byte { } func TestParseNITSection(t *testing.T) { - var offset int var b = nitBytes() - d := parseNITSection(b, &offset, uint16(1)) + d, err := parseNITSection(astibyte.NewIterator(b), uint16(1)) assert.Equal(t, d, nit) + assert.NoError(t, err) } diff --git a/data_pat.go b/data_pat.go index 5576515..eadacb5 100644 --- a/data_pat.go +++ b/data_pat.go @@ -1,5 +1,10 @@ package astits +import ( + astibyte "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" +) + // PATData represents a PAT data // https://en.wikipedia.org/wiki/Program-specific_information type PATData struct { @@ -14,17 +19,24 @@ type PATProgram struct { } // parsePATSection parses a PAT section -func parsePATSection(i []byte, offset *int, offsetSectionsEnd int, tableIDExtension uint16) (d *PATData) { - // Init +func parsePATSection(i *astibyte.Iterator, offsetSectionsEnd int, tableIDExtension uint16) (d *PATData, err error) { + // Create data d = &PATData{TransportStreamID: tableIDExtension} // Loop until end of section data is reached - for *offset < offsetSectionsEnd { + for i.Offset() < offsetSectionsEnd { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(4); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Append program d.Programs = append(d.Programs, &PATProgram{ - ProgramMapID: uint16(i[*offset+2]&0x1f)<<8 | uint16(i[*offset+3]), - ProgramNumber: uint16(i[*offset])<<8 | uint16(i[*offset+1]), + ProgramMapID: uint16(bs[2]&0x1f)<<8 | uint16(bs[3]), + ProgramNumber: uint16(bs[0])<<8 | uint16(bs[1]), }) - *offset += 4 } return } diff --git a/data_pat_test.go b/data_pat_test.go index f040e07..c2e212c 100644 --- a/data_pat_test.go +++ b/data_pat_test.go @@ -5,6 +5,7 @@ import ( "github.com/asticode/go-astitools/binary" "github.com/stretchr/testify/assert" + "github.com/asticode/go-astitools/byte" ) var pat = &PATData{ @@ -27,8 +28,8 @@ func patBytes() []byte { } func TestParsePATSection(t *testing.T) { - var offset int var b = patBytes() - d := parsePATSection(b, &offset, len(b), uint16(1)) + d, err := parsePATSection(astibyte.NewIterator(b), len(b), uint16(1)) assert.Equal(t, d, pat) + assert.NoError(t, err) } diff --git a/data_pes.go b/data_pes.go index 0591e5a..5bd64af 100644 --- a/data_pes.go +++ b/data_pes.go @@ -1,8 +1,7 @@ package astits import ( - "fmt" - + astibyte "github.com/asticode/go-astitools/byte" "github.com/pkg/errors" ) @@ -103,19 +102,34 @@ type DSMTrickMode struct { } // parsePESData parses a PES data -func parsePESData(i []byte) (d *PESData, err error) { - // Init +func parsePESData(i *astibyte.Iterator) (d *PESData, err error) { + // Create data d = &PESData{} + // Skip first 3 bytes that are there to identify the PES payload + i.Seek(3) + // Parse header - var offset, dataStart, dataEnd = 3, 0, 0 - if d.Header, dataStart, dataEnd, err = parsePESHeader(i, &offset); err != nil { + var dataStart int + if d.Header, dataStart, err = parsePESHeader(i); err != nil { err = errors.Wrap(err, "astits: parsing PES header failed") return } + // Seek to data + if dataStart > 0 { + i.Seek(dataStart) + } + // Parse data - d.Data = i[dataStart:dataEnd] + if d.Header != nil && d.Header.PacketLength > 0 { + if d.Data, err = i.NextBytes(int(d.Header.PacketLength)); err != nil { + err = errors.Wrap(err, "astits: getting next bytes failed") + return + } + } else { + d.Data = i.Dump() + } return } @@ -125,170 +139,232 @@ func hasPESOptionalHeader(streamID uint8) bool { } // parsePESData parses a PES header -func parsePESHeader(i []byte, offset *int) (h *PESHeader, dataStart, dataEnd int, err error) { - // Init +func parsePESHeader(i *astibyte.Iterator) (h *PESHeader, dataStart int, err error) { + // Create header h = &PESHeader{} - // Stream ID - h.StreamID = uint8(i[*offset]) - *offset += 1 - - // Length - h.PacketLength = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 - - // Data end - if h.PacketLength > 0 { - dataEnd = *offset + int(h.PacketLength) - } else { - dataEnd = len(i) + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return } - // Check for incomplete data - if dataEnd > len(i) { - err = fmt.Errorf("astits: pes dataEnd (%d) > len(i) (%d)", dataEnd, len(i)) + // Stream ID + h.StreamID = uint8(b) + + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") return } + // Length + h.PacketLength = uint16(bs[0])<<8 | uint16(bs[1]) + // Optional header if hasPESOptionalHeader(h.StreamID) { - h.OptionalHeader, dataStart = parsePESOptionalHeader(i, offset) - } else { - dataStart = *offset + if h.OptionalHeader, dataStart, err = parsePESOptionalHeader(i); err != nil { + err = errors.Wrap(err, "astits: parsing PES optional header failed") + return + } } return } // parsePESOptionalHeader parses a PES optional header -func parsePESOptionalHeader(i []byte, offset *int) (h *PESOptionalHeader, dataStart int) { - // Init +func parsePESOptionalHeader(i *astibyte.Iterator) (h *PESOptionalHeader, dataStart int, err error) { + // Create header h = &PESOptionalHeader{} + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + // Marker bits - h.MarkerBits = uint8(i[*offset]) >> 6 + h.MarkerBits = uint8(b) >> 6 // Scrambling control - h.ScramblingControl = uint8(i[*offset]) >> 4 & 0x3 + h.ScramblingControl = uint8(b) >> 4 & 0x3 // Priority - h.Priority = uint8(i[*offset])&0x8 > 0 + h.Priority = uint8(b)&0x8 > 0 // Data alignment indicator - h.DataAlignmentIndicator = uint8(i[*offset])&0x4 > 0 + h.DataAlignmentIndicator = uint8(b)&0x4 > 0 // Copyrighted - h.IsCopyrighted = uint(i[*offset])&0x2 > 0 + h.IsCopyrighted = uint(b)&0x2 > 0 // Original or copy - h.IsOriginal = uint8(i[*offset])&0x1 > 0 - *offset += 1 + h.IsOriginal = uint8(b)&0x1 > 0 + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // PTS DST indicator - h.PTSDTSIndicator = uint8(i[*offset]) >> 6 & 0x3 + h.PTSDTSIndicator = uint8(b) >> 6 & 0x3 // Flags - h.HasESCR = uint8(i[*offset])&0x20 > 0 - h.HasESRate = uint8(i[*offset])&0x10 > 0 - h.HasDSMTrickMode = uint8(i[*offset])&0x8 > 0 - h.HasAdditionalCopyInfo = uint8(i[*offset])&0x4 > 0 - h.HasCRC = uint8(i[*offset])&0x2 > 0 - h.HasExtension = uint8(i[*offset])&0x1 > 0 - *offset += 1 + h.HasESCR = uint8(b)&0x20 > 0 + h.HasESRate = uint8(b)&0x10 > 0 + h.HasDSMTrickMode = uint8(b)&0x8 > 0 + h.HasAdditionalCopyInfo = uint8(b)&0x4 > 0 + h.HasCRC = uint8(b)&0x2 > 0 + h.HasExtension = uint8(b)&0x1 > 0 + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Header length - h.HeaderLength = uint8(i[*offset]) - *offset += 1 + h.HeaderLength = uint8(b) - // Data start - dataStart = *offset + int(h.HeaderLength) + // Update data start + dataStart = i.Offset() + int(h.HeaderLength) // PTS/DTS if h.PTSDTSIndicator == PTSDTSIndicatorOnlyPTS { - h.PTS = parsePTSOrDTS(i[*offset:]) - *offset += 5 + if h.PTS, err = parsePTSOrDTS(i); err != nil { + err = errors.Wrap(err, "astits: parsing PTS failed") + return + } } else if h.PTSDTSIndicator == PTSDTSIndicatorBothPresent { - h.PTS = parsePTSOrDTS(i[*offset:]) - *offset += 5 - h.DTS = parsePTSOrDTS(i[*offset:]) - *offset += 5 + if h.PTS, err = parsePTSOrDTS(i); err != nil { + err = errors.Wrap(err, "astits: parsing PTS failed") + return + } + if h.DTS, err = parsePTSOrDTS(i); err != nil { + err = errors.Wrap(err, "astits: parsing PTS failed") + return + } } // ESCR if h.HasESCR { - h.ESCR = parseESCR(i[*offset:]) - *offset += 6 + if h.ESCR, err = parseESCR(i); err != nil { + err = errors.Wrap(err, "astits: parsing ESCR failed") + return + } } // ES rate if h.HasESRate { - h.ESRate = uint32(i[*offset])&0x7f<<15 | uint32(i[*offset+1])<<7 | uint32(i[*offset+2])>>1 - *offset += 3 + var bs []byte + if bs, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + h.ESRate = uint32(bs[0])&0x7f<<15 | uint32(bs[1])<<7 | uint32(bs[2])>>1 } // Trick mode if h.HasDSMTrickMode { - h.DSMTrickMode = parseDSMTrickMode(i[*offset]) - *offset += 1 + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + h.DSMTrickMode = parseDSMTrickMode(b) } // Additional copy info if h.HasAdditionalCopyInfo { - h.AdditionalCopyInfo = i[*offset] & 0x7f - *offset += 1 + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + h.AdditionalCopyInfo = b & 0x7f } // CRC if h.HasCRC { - h.CRC = uint16(i[*offset])>>8 | uint16(i[*offset+1]) - *offset += 2 + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + h.CRC = uint16(bs[0])>>8 | uint16(bs[1]) } // Extension if h.HasExtension { + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + // Flags - h.HasPrivateData = i[*offset]&0x80 > 0 - h.HasPackHeaderField = i[*offset]&0x40 > 0 - h.HasProgramPacketSequenceCounter = i[*offset]&0x20 > 0 - h.HasPSTDBuffer = i[*offset]&0x10 > 0 - h.HasExtension2 = i[*offset]&0x1 > 0 - *offset += 1 + h.HasPrivateData = b&0x80 > 0 + h.HasPackHeaderField = b&0x40 > 0 + h.HasProgramPacketSequenceCounter = b&0x20 > 0 + h.HasPSTDBuffer = b&0x10 > 0 + h.HasExtension2 = b&0x1 > 0 // Private data if h.HasPrivateData { - h.PrivateData = i[*offset : *offset+16] - *offset += 16 + if h.PrivateData, err = i.NextBytes(16); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } // Pack field length if h.HasPackHeaderField { - h.PackField = uint8(i[*offset]) - *offset += 1 + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + h.PackField = uint8(b) } // Program packet sequence counter if h.HasProgramPacketSequenceCounter { - h.PacketSequenceCounter = uint8(i[*offset]) & 0x7f - h.MPEG1OrMPEG2ID = uint8(i[*offset+1]) >> 6 & 0x1 - h.OriginalStuffingLength = uint8(i[*offset+1]) & 0x3f - *offset += 2 + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + h.PacketSequenceCounter = uint8(bs[0]) & 0x7f + h.MPEG1OrMPEG2ID = uint8(bs[1]) >> 6 & 0x1 + h.OriginalStuffingLength = uint8(bs[1]) & 0x3f } // P-STD buffer if h.HasPSTDBuffer { - h.PSTDBufferScale = i[*offset] >> 5 & 0x1 - h.PSTDBufferSize = uint16(i[*offset])&0x1f<<8 | uint16(i[*offset+1]) - *offset += 2 + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + h.PSTDBufferScale = bs[0] >> 5 & 0x1 + h.PSTDBufferSize = uint16(bs[0])&0x1f<<8 | uint16(bs[1]) } // Extension 2 if h.HasExtension2 { // Length - h.Extension2Length = uint8(i[*offset]) & 0x7f - *offset += 2 + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + h.Extension2Length = uint8(bs[0]) & 0x7f // Data - h.Extension2Data = i[*offset : *offset+int(h.Extension2Length)] - *offset += int(h.Extension2Length) + if h.Extension2Data, err = i.NextBytes(int(h.Extension2Length)); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } } return @@ -311,12 +387,24 @@ func parseDSMTrickMode(i byte) (m *DSMTrickMode) { } // parsePTSOrDTS parses a PTS or a DTS -func parsePTSOrDTS(i []byte) *ClockReference { - return newClockReference(int(uint64(i[0])>>1&0x7<<30|uint64(i[1])<<22|uint64(i[2])>>1&0x7f<<15|uint64(i[3])<<7|uint64(i[4])>>1&0x7f), 0) +func parsePTSOrDTS(i *astibyte.Iterator) (cr *ClockReference, err error) { + var bs []byte + if bs, err = i.NextBytes(5); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + cr = newClockReference(int(uint64(bs[0])>>1&0x7<<30|uint64(bs[1])<<22|uint64(bs[2])>>1&0x7f<<15|uint64(bs[3])<<7|uint64(bs[4])>>1&0x7f), 0) + return } // parseESCR parses an ESCR -func parseESCR(i []byte) *ClockReference { - var escr = uint64(i[0])>>3&0x7<<39 | uint64(i[0])&0x3<<37 | uint64(i[1])<<29 | uint64(i[2])>>3<<24 | uint64(i[2])&0x3<<22 | uint64(i[3])<<14 | uint64(i[4])>>3<<9 | uint64(i[4])&0x3<<7 | uint64(i[5])>>1 - return newClockReference(int(escr>>9), int(escr&0x1ff)) +func parseESCR(i *astibyte.Iterator) (cr *ClockReference, err error) { + var bs []byte + if bs, err = i.NextBytes(6); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + escr := uint64(bs[0])>>3&0x7<<39 | uint64(bs[0])&0x3<<37 | uint64(bs[1])<<29 | uint64(bs[2])>>3<<24 | uint64(bs[2])&0x3<<22 | uint64(bs[3])<<14 | uint64(bs[4])>>3<<9 | uint64(bs[4])&0x3<<7 | uint64(bs[5])>>1 + cr = newClockReference(int(escr>>9), int(escr&0x1ff)) + return } diff --git a/data_pes_test.go b/data_pes_test.go index 15ab4d8..63bebac 100644 --- a/data_pes_test.go +++ b/data_pes_test.go @@ -5,6 +5,7 @@ import ( "github.com/asticode/go-astitools/binary" "github.com/stretchr/testify/assert" + "github.com/asticode/go-astitools/byte" ) func TestHasPESOptionalHeader(t *testing.T) { @@ -86,7 +87,9 @@ func dtsBytes() []byte { } func TestParsePTSOrDTS(t *testing.T) { - assert.Equal(t, parsePTSOrDTS(ptsBytes()), ptsClockReference) + v, err := parsePTSOrDTS(astibyte.NewIterator(ptsBytes())) + assert.Equal(t, v, ptsClockReference) + assert.NoError(t, err) } func escrBytes() []byte { @@ -104,7 +107,9 @@ func escrBytes() []byte { } func TestParseESCR(t *testing.T) { - assert.Equal(t, parseESCR(escrBytes()), clockReference) + v, err := parseESCR(astibyte.NewIterator(escrBytes())) + assert.Equal(t, v, clockReference) + assert.NoError(t, err) } var pesWithoutHeader = &PESData{ @@ -215,12 +220,12 @@ func pesWithHeaderBytes() []byte { func TestParsePESSection(t *testing.T) { // No optional header and specific packet length - d, err := parsePESData(pesWithoutHeaderBytes()) + d, err := parsePESData(astibyte.NewIterator(pesWithoutHeaderBytes())) assert.NoError(t, err) assert.Equal(t, d, pesWithoutHeader) // Optional header and no specific header length - d, err = parsePESData(pesWithHeaderBytes()) + d, err = parsePESData(astibyte.NewIterator(pesWithHeaderBytes())) assert.NoError(t, err) assert.Equal(t, d, pesWithHeader) } diff --git a/data_pmt.go b/data_pmt.go index 86f1de3..fd94ad1 100644 --- a/data_pmt.go +++ b/data_pmt.go @@ -1,5 +1,10 @@ package astits +import ( + astibyte "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" +) + // Stream types const ( StreamTypeLowerBitrateVideo = 27 // ITU-T Rec. H.264 and ISO/IEC 14496-10 @@ -25,30 +30,55 @@ type PMTElementaryStream struct { } // parsePMTSection parses a PMT section -func parsePMTSection(i []byte, offset *int, offsetSectionsEnd int, tableIDExtension uint16) (d *PMTData) { - // Init +func parsePMTSection(i *astibyte.Iterator, offsetSectionsEnd int, tableIDExtension uint16) (d *PMTData, err error) { + // Create data d = &PMTData{ProgramNumber: tableIDExtension} + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // PCR PID - d.PCRPID = uint16(i[*offset]&0x1f)<<8 | uint16(i[*offset+1]) - *offset += 2 + d.PCRPID = uint16(bs[0]&0x1f)<<8 | uint16(bs[1]) // Program descriptors - d.ProgramDescriptors = parseDescriptors(i, offset) + if d.ProgramDescriptors, err = parseDescriptors(i); err != nil { + err = errors.Wrap(err, "astits: parsing descriptors failed") + return + } // Loop until end of section data is reached - for *offset < offsetSectionsEnd { + for i.Offset() < offsetSectionsEnd { + // Create stream + e := &PMTElementaryStream{} + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + // Stream type - var e = &PMTElementaryStream{} - e.StreamType = uint8(i[*offset]) - *offset += 1 + e.StreamType = uint8(b) + + // Get next bytes + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } // Elementary PID - e.ElementaryPID = uint16(i[*offset]&0x1f)<<8 | uint16(i[*offset+1]) - *offset += 2 + e.ElementaryPID = uint16(bs[0]&0x1f)<<8 | uint16(bs[1]) // Elementary descriptors - e.ElementaryStreamDescriptors = parseDescriptors(i, offset) + if e.ElementaryStreamDescriptors, err = parseDescriptors(i); err != nil { + err = errors.Wrap(err, "astits: parsing descriptors failed") + return + } // Add elementary stream d.ElementaryStreams = append(d.ElementaryStreams, e) diff --git a/data_pmt_test.go b/data_pmt_test.go index 53106b5..6a85e37 100644 --- a/data_pmt_test.go +++ b/data_pmt_test.go @@ -5,6 +5,7 @@ import ( "github.com/asticode/go-astitools/binary" "github.com/stretchr/testify/assert" + "github.com/asticode/go-astitools/byte" ) var pmt = &PMTData{ @@ -33,8 +34,8 @@ func pmtBytes() []byte { } func TestParsePMTSection(t *testing.T) { - var offset int var b = pmtBytes() - d := parsePMTSection(b, &offset, len(b), uint16(1)) + d, err := parsePMTSection(astibyte.NewIterator(b), len(b), uint16(1)) assert.Equal(t, d, pmt) + assert.NoError(t, err) } diff --git a/data_psi.go b/data_psi.go index 0201731..c7beb8f 100644 --- a/data_psi.go +++ b/data_psi.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/asticode/go-astilog" + astibyte "github.com/asticode/go-astitools/byte" "github.com/pkg/errors" ) @@ -74,23 +75,28 @@ type PSISectionSyntaxData struct { } // parsePSIData parses a PSI data -func parsePSIData(i []byte) (d *PSIData, err error) { +func parsePSIData(i *astibyte.Iterator) (d *PSIData, err error) { // Init data d = &PSIData{} - var offset int + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Pointer field - d.PointerField = int(i[offset]) - offset += 1 + d.PointerField = int(b) // Pointer filler bytes - offset += d.PointerField + i.FastForward(d.PointerField) // Parse sections var s *PSISection var stop bool - for offset < len(i) && !stop { - if s, stop, err = parsePSISection(i, &offset); err != nil { + for i.HasBytesLeft() && !stop { + if s, stop, err = parsePSISection(i); err != nil { err = errors.Wrap(err, "astits: parsing PSI table failed") return } @@ -100,13 +106,16 @@ func parsePSIData(i []byte) (d *PSIData, err error) { } // parsePSISection parses a PSI section -func parsePSISection(i []byte, offset *int) (s *PSISection, stop bool, err error) { +func parsePSISection(i *astibyte.Iterator) (s *PSISection, stop bool, err error) { // Init section s = &PSISection{} // Parse header var offsetStart, offsetSectionsEnd, offsetEnd int - s.Header, offsetStart, _, offsetSectionsEnd, offsetEnd = parsePSISectionHeader(i, offset) + if s.Header, offsetStart, _, offsetSectionsEnd, offsetEnd, err = parsePSISectionHeader(i); err != nil { + err = errors.Wrap(err, "astits: parsing PSI section header failed") + return + } // Check whether we need to stop the parsing if shouldStopPSIParsing(s.Header.TableType) { @@ -117,35 +126,66 @@ func parsePSISection(i []byte, offset *int) (s *PSISection, stop bool, err error // Check whether there's a syntax section if s.Header.SectionLength > 0 { // Parse syntax - s.Syntax = parsePSISectionSyntax(i, offset, s.Header, offsetSectionsEnd) + if s.Syntax, err = parsePSISectionSyntax(i, s.Header, offsetSectionsEnd); err != nil { + err = errors.Wrap(err, "astits: parsing PSI section syntax failed") + return + } // Process CRC32 if hasCRC32(s.Header.TableType) { + // Seek to the end of the sections + i.Seek(offsetSectionsEnd) + // Parse CRC32 - s.CRC32 = parseCRC32(i[offsetSectionsEnd:offsetEnd]) - *offset += 4 + if s.CRC32, err = parseCRC32(i); err != nil { + err = errors.Wrap(err, "astits: parsing CRC32 failed") + return + } + + // Get CRC32 data + i.Seek(offsetStart) + var crc32Data []byte + if crc32Data, err = i.NextBytes(offsetSectionsEnd - offsetStart); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Compute CRC32 + var crc32 uint32 + if crc32, err = computeCRC32(crc32Data); err != nil { + err = errors.Wrap(err, "astits: computing CRC32 failed") + return + } // Check CRC32 - var c = computeCRC32(i[offsetStart:offsetSectionsEnd]) - if c != s.CRC32 { - err = fmt.Errorf("astits: Table CRC32 %x != computed CRC32 %x", s.CRC32, c) + if crc32 != s.CRC32 { + err = fmt.Errorf("astits: Table CRC32 %x != computed CRC32 %x", s.CRC32, crc32) return } } } + + // Seek to the end of the section + i.Seek(offsetEnd) return } // parseCRC32 parses a CRC32 -func parseCRC32(i []byte) uint32 { - return uint32(i[len(i)-4])<<24 | uint32(i[len(i)-3])<<16 | uint32(i[len(i)-2])<<8 | uint32(i[len(i)-1]) +func parseCRC32(i *astibyte.Iterator) (c uint32, err error) { + var bs []byte + if bs, err = i.NextBytes(4); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + c = uint32(bs[0])<<24 | uint32(bs[1])<<16 | uint32(bs[2])<<8 | uint32(bs[3]) + return } // computeCRC32 computes a CRC32 // https://stackoverflow.com/questions/35034042/how-to-calculate-crc32-in-psi-si-packet -func computeCRC32(i []byte) (o uint32) { +func computeCRC32(bs []byte) (o uint32, err error) { o = uint32(0xffffffff) - for _, b := range i { + for _, b := range bs { for i := 0; i < 8; i++ { if (o >= uint32(0x80000000)) != (b >= uint8(0x80)) { o = (o << 1) ^ 0x04C11DB7 @@ -164,14 +204,20 @@ func shouldStopPSIParsing(tableType string) bool { } // parsePSISectionHeader parses a PSI section header -func parsePSISectionHeader(i []byte, offset *int) (h *PSISectionHeader, offsetStart, offsetSectionsStart, offsetSectionsEnd, offsetEnd int) { +func parsePSISectionHeader(i *astibyte.Iterator) (h *PSISectionHeader, offsetStart, offsetSectionsStart, offsetSectionsEnd, offsetEnd int, err error) { // Init h = &PSISectionHeader{} - offsetStart = *offset + offsetStart = i.Offset() + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Table ID - h.TableID = int(i[*offset]) - *offset += 1 + h.TableID = int(b) // Table type h.TableType = psiTableType(h.TableID) @@ -181,18 +227,24 @@ func parsePSISectionHeader(i []byte, offset *int) (h *PSISectionHeader, offsetSt return } + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Section syntax indicator - h.SectionSyntaxIndicator = i[*offset]&0x80 > 0 + h.SectionSyntaxIndicator = bs[0]&0x80 > 0 // Private bit - h.PrivateBit = i[*offset]&0x40 > 0 + h.PrivateBit = bs[0]&0x40 > 0 // Section length - h.SectionLength = uint16(i[*offset]&0xf)<<8 | uint16(i[*offset+1]) - *offset += 2 + h.SectionLength = uint16(bs[0]&0xf)<<8 | uint16(bs[1]) // Offsets - offsetSectionsStart = *offset + offsetSectionsStart = i.Offset() offsetEnd = offsetSectionsStart + int(h.SectionLength) offsetSectionsEnd = offsetEnd if hasCRC32(h.TableType) { @@ -248,17 +300,23 @@ func psiTableType(tableID int) string { } // parsePSISectionSyntax parses a PSI section syntax -func parsePSISectionSyntax(i []byte, offset *int, h *PSISectionHeader, offsetSectionsEnd int) (s *PSISectionSyntax) { +func parsePSISectionSyntax(i *astibyte.Iterator, h *PSISectionHeader, offsetSectionsEnd int) (s *PSISectionSyntax, err error) { // Init s = &PSISectionSyntax{} // Header if hasPSISyntaxHeader(h.TableType) { - s.Header = parsePSISectionSyntaxHeader(i, offset) + if s.Header, err = parsePSISectionSyntaxHeader(i); err != nil { + err = errors.Wrap(err, "astits: parsing PSI section syntax header failed") + return + } } // Parse data - s.Data = parsePSISectionSyntaxData(i, offset, h, s.Header, offsetSectionsEnd) + if s.Data, err = parsePSISectionSyntaxData(i, h, s.Header, offsetSectionsEnd); err != nil { + err = errors.Wrap(err, "astits: parsing PSI section syntax data failed") + return + } return } @@ -272,33 +330,55 @@ func hasPSISyntaxHeader(tableType string) bool { } // parsePSISectionSyntaxHeader parses a PSI section syntax header -func parsePSISectionSyntaxHeader(i []byte, offset *int) (h *PSISectionSyntaxHeader) { +func parsePSISectionSyntaxHeader(i *astibyte.Iterator) (h *PSISectionSyntaxHeader, err error) { // Init h = &PSISectionSyntaxHeader{} + // Get next 2 bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Table ID extension - h.TableIDExtension = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 + h.TableIDExtension = uint16(bs[0])<<8 | uint16(bs[1]) + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Version number - h.VersionNumber = uint8(i[*offset]&0x3f) >> 1 + h.VersionNumber = uint8(b&0x3f) >> 1 // Current/Next indicator - h.CurrentNextIndicator = i[*offset]&0x1 > 0 - *offset += 1 + h.CurrentNextIndicator = b&0x1 > 0 + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Section number - h.SectionNumber = uint8(i[*offset]) - *offset += 1 + h.SectionNumber = uint8(b) + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Last section number - h.LastSectionNumber = uint8(i[*offset]) - *offset += 1 + h.LastSectionNumber = uint8(b) return } // parsePSISectionSyntaxData parses a PSI section data -func parsePSISectionSyntaxData(i []byte, offset *int, h *PSISectionHeader, sh *PSISectionSyntaxHeader, offsetSectionsEnd int) (d *PSISectionSyntaxData) { +func parsePSISectionSyntaxData(i *astibyte.Iterator, h *PSISectionHeader, sh *PSISectionSyntaxHeader, offsetSectionsEnd int) (d *PSISectionSyntaxData, err error) { // Init d = &PSISectionSyntaxData{} @@ -309,23 +389,41 @@ func parsePSISectionSyntaxData(i []byte, offset *int, h *PSISectionHeader, sh *P case PSITableTypeDIT: // TODO Parse DIT case PSITableTypeEIT: - d.EIT = parseEITSection(i, offset, offsetSectionsEnd, sh.TableIDExtension) + if d.EIT, err = parseEITSection(i, offsetSectionsEnd, sh.TableIDExtension); err != nil { + err = errors.Wrap(err, "astits: parsing EIT section failed") + return + } case PSITableTypeNIT: - d.NIT = parseNITSection(i, offset, sh.TableIDExtension) + if d.NIT, err = parseNITSection(i, sh.TableIDExtension); err != nil { + err = errors.Wrap(err, "astits: parsing NIT section failed") + return + } case PSITableTypePAT: - d.PAT = parsePATSection(i, offset, offsetSectionsEnd, sh.TableIDExtension) + if d.PAT, err = parsePATSection(i, offsetSectionsEnd, sh.TableIDExtension); err != nil { + err = errors.Wrap(err, "astits: parsing PAT section failed") + return + } case PSITableTypePMT: - d.PMT = parsePMTSection(i, offset, offsetSectionsEnd, sh.TableIDExtension) + if d.PMT, err = parsePMTSection(i, offsetSectionsEnd, sh.TableIDExtension); err != nil { + err = errors.Wrap(err, "astits: parsing PMT section failed") + return + } case PSITableTypeRST: // TODO Parse RST case PSITableTypeSDT: - d.SDT = parseSDTSection(i, offset, offsetSectionsEnd, sh.TableIDExtension) + if d.SDT, err = parseSDTSection(i, offsetSectionsEnd, sh.TableIDExtension); err != nil { + err = errors.Wrap(err, "astits: parsing PMT section failed") + return + } case PSITableTypeSIT: // TODO Parse SIT case PSITableTypeST: // TODO Parse ST case PSITableTypeTOT: - d.TOT = parseTOTSection(i, offset) + if d.TOT, err = parseTOTSection(i); err != nil { + err = errors.Wrap(err, "astits: parsing TOT section failed") + return + } case PSITableTypeTDT: // TODO Parse TDT } diff --git a/data_psi_test.go b/data_psi_test.go index e6b3503..f7b1993 100644 --- a/data_psi_test.go +++ b/data_psi_test.go @@ -3,7 +3,8 @@ package astits import ( "testing" - "github.com/asticode/go-astitools/binary" + astibinary "github.com/asticode/go-astitools/binary" + astibyte "github.com/asticode/go-astitools/byte" "github.com/stretchr/testify/assert" ) @@ -164,11 +165,11 @@ func TestParsePSIData(t *testing.T) { w.Write("000000001110") // TOT section length w.Write(totBytes()) // TOT data w.Write(uint32(32)) // TOT CRC32 - _, err := parsePSIData(w.Bytes()) + _, err := parsePSIData(astibyte.NewIterator(w.Bytes())) assert.EqualError(t, err, "astits: parsing PSI table failed: astits: Table CRC32 20 != computed CRC32 6969b13") // Valid - d, err := parsePSIData(psiBytes()) + d, err := parsePSIData(astibyte.NewIterator(psiBytes())) assert.NoError(t, err) assert.Equal(t, d, psi) } @@ -197,21 +198,21 @@ func TestParsePSISectionHeader(t *testing.T) { w.Write(uint8(254)) // Table ID w.Write("1") // Syntax section indicator w.Write("0000000") // Finish the byte - var offset int - d, _, _, _, _ := parsePSISectionHeader(w.Bytes(), &offset) + d, _, _, _, _, err := parsePSISectionHeader(astibyte.NewIterator(w.Bytes())) assert.Equal(t, d, &PSISectionHeader{ TableID: 254, TableType: PSITableTypeUnknown, }) + assert.NoError(t, err) // Valid table type - offset = 0 - d, offsetStart, offsetSectionsStart, offsetSectionsEnd, offsetEnd := parsePSISectionHeader(psiSectionHeaderBytes(), &offset) + d, offsetStart, offsetSectionsStart, offsetSectionsEnd, offsetEnd, err := parsePSISectionHeader(astibyte.NewIterator(psiSectionHeaderBytes())) assert.Equal(t, d, psiSectionHeader) assert.Equal(t, 0, offsetStart) assert.Equal(t, 3, offsetSectionsStart) assert.Equal(t, 2729, offsetSectionsEnd) assert.Equal(t, 2733, offsetEnd) + assert.NoError(t, err) } func TestPSITableType(t *testing.T) { @@ -255,8 +256,9 @@ func psiSectionSyntaxHeaderBytes() []byte { } func TestParsePSISectionSyntaxHeader(t *testing.T) { - var offset int - assert.Equal(t, psiSectionSyntaxHeader, parsePSISectionSyntaxHeader(psiSectionSyntaxHeaderBytes(), &offset)) + h, err := parsePSISectionSyntaxHeader(astibyte.NewIterator(psiSectionSyntaxHeaderBytes())) + assert.Equal(t, psiSectionSyntaxHeader, h) + assert.NoError(t, err) } func TestPSIToData(t *testing.T) { diff --git a/data_sdt.go b/data_sdt.go index 6433dc1..a4207a5 100644 --- a/data_sdt.go +++ b/data_sdt.go @@ -1,5 +1,10 @@ package astits +import ( + astibyte "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" +) + // Running statuses const ( RunningStatusNotRunning = 1 @@ -29,39 +34,70 @@ type SDTDataService struct { } // parseSDTSection parses an SDT section -func parseSDTSection(i []byte, offset *int, offsetSectionsEnd int, tableIDExtension uint16) (d *SDTData) { - // Init +func parseSDTSection(i *astibyte.Iterator, offsetSectionsEnd int, tableIDExtension uint16) (d *SDTData, err error) { + // Create data d = &SDTData{TransportStreamID: tableIDExtension} + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Original network ID - d.OriginalNetworkID = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 + d.OriginalNetworkID = uint16(bs[0])<<8 | uint16(bs[1]) // Reserved for future use - *offset += 1 + i.FastForward(1) // Loop until end of section data is reached - for *offset < offsetSectionsEnd { + for i.Offset() < offsetSectionsEnd { + // Create service + s := &SDTDataService{} + + // Get next bytes + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Service ID - var s = &SDTDataService{} - s.ServiceID = uint16(i[*offset])<<8 | uint16(i[*offset+1]) - *offset += 2 + s.ServiceID = uint16(bs[0])<<8 | uint16(bs[1]) + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // EIT schedule flag - s.HasEITSchedule = uint8(i[*offset]&0x2) > 0 + s.HasEITSchedule = uint8(b&0x2) > 0 // EIT present/following flag - s.HasEITPresentFollowing = uint8(i[*offset]&0x1) > 0 - *offset += 1 + s.HasEITPresentFollowing = uint8(b&0x1) > 0 + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Running status - s.RunningStatus = uint8(i[*offset]) >> 5 + s.RunningStatus = uint8(b) >> 5 // Free CA mode - s.HasFreeCSAMode = uint8(i[*offset]&0x10) > 0 + s.HasFreeCSAMode = uint8(b&0x10) > 0 + + // We need to rewind since the current byte is used by the descriptor as well + i.FastForward(-1) // Descriptors - s.Descriptors = parseDescriptors(i, offset) + if s.Descriptors, err = parseDescriptors(i); err != nil { + err = errors.Wrap(err, "astits: parsing descriptors failed") + return + } // Append service d.Services = append(d.Services, s) diff --git a/data_sdt_test.go b/data_sdt_test.go index 51b8e8f..e97ad55 100644 --- a/data_sdt_test.go +++ b/data_sdt_test.go @@ -3,7 +3,8 @@ package astits import ( "testing" - "github.com/asticode/go-astitools/binary" + astibinary "github.com/asticode/go-astitools/binary" + astibyte "github.com/asticode/go-astitools/byte" "github.com/stretchr/testify/assert" ) @@ -35,8 +36,8 @@ func sdtBytes() []byte { } func TestParseSDTSection(t *testing.T) { - var offset int var b = sdtBytes() - d := parseSDTSection(b, &offset, len(b), uint16(1)) + d, err := parseSDTSection(astibyte.NewIterator(b), len(b), uint16(1)) assert.Equal(t, d, sdt) + assert.NoError(t, err) } diff --git a/data_tot.go b/data_tot.go index 97fad93..ffc4123 100644 --- a/data_tot.go +++ b/data_tot.go @@ -1,6 +1,11 @@ package astits -import "time" +import ( + "time" + + astibyte "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" +) // TOTData represents a TOT data // Page: 39 | Chapter: 5.2.6 | Link: https://www.dvb.org/resources/public/standards/a38_dvb-si_specification.pdf @@ -10,14 +15,20 @@ type TOTData struct { } // parseTOTSection parses a TOT section -func parseTOTSection(i []byte, offset *int) (d *TOTData) { - // Init +func parseTOTSection(i *astibyte.Iterator) (d *TOTData, err error) { + // Create data d = &TOTData{} // UTC time - d.UTCTime = parseDVBTime(i, offset) + if d.UTCTime, err = parseDVBTime(i); err != nil { + err = errors.Wrap(err, "astits: parsing DVB time failed") + return + } // Descriptors - d.Descriptors = parseDescriptors(i, offset) + if d.Descriptors, err = parseDescriptors(i); err != nil { + err = errors.Wrap(err, "astits: parsing descriptors failed") + return + } return } diff --git a/data_tot_test.go b/data_tot_test.go index de7c8e8..99e642d 100644 --- a/data_tot_test.go +++ b/data_tot_test.go @@ -5,6 +5,7 @@ import ( "github.com/asticode/go-astitools/binary" "github.com/stretchr/testify/assert" + "github.com/asticode/go-astitools/byte" ) var tot = &TOTData{ @@ -21,7 +22,7 @@ func totBytes() []byte { } func TestParseTOTSection(t *testing.T) { - var offset int - d := parseTOTSection(totBytes(), &offset) + d, err := parseTOTSection(astibyte.NewIterator(totBytes())) assert.Equal(t, d, tot) + assert.NoError(t, err) } diff --git a/descriptor.go b/descriptor.go index aea0cee..eb21645 100644 --- a/descriptor.go +++ b/descriptor.go @@ -4,6 +4,8 @@ import ( "time" "github.com/asticode/go-astilog" + "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" ) // Audio types @@ -130,33 +132,64 @@ type DescriptorAC3 struct { MainID uint8 } -func newDescriptorAC3(i []byte) (d *DescriptorAC3) { - var offset int - d = &DescriptorAC3{} - d.HasComponentType = uint8(i[offset]&0x80) > 0 - d.HasBSID = uint8(i[offset]&0x40) > 0 - d.HasMainID = uint8(i[offset]&0x20) > 0 - d.HasASVC = uint8(i[offset]&0x10) > 0 - offset += 1 +func newDescriptorAC3(i *astibyte.Iterator, offsetEnd int) (d *DescriptorAC3, err error) { + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + 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 { - d.ComponentType = uint8(i[offset]) - offset += 1 + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.ComponentType = uint8(b) } + + // BSID if d.HasBSID { - d.BSID = uint8(i[offset]) - offset += 1 + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.BSID = uint8(b) } + + // Main ID if d.HasMainID { - d.MainID = uint8(i[offset]) - offset += 1 + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.MainID = uint8(b) } + + // ASVC if d.HasASVC { - d.ASVC = uint8(i[offset]) - offset += 1 + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.ASVC = uint8(b) } - for offset < len(i) { - d.AdditionalInfo = append(d.AdditionalInfo, i[offset]) - offset += 1 + + // Additional info + if i.Offset() < offsetEnd { + if d.AdditionalInfo, err = i.NextBytes(offsetEnd - i.Offset()); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } return } @@ -174,31 +207,52 @@ type DescriptorAVCVideo struct { ProfileIDC uint8 } -func newDescriptorAVCVideo(i []byte) (d *DescriptorAVCVideo) { +func newDescriptorAVCVideo(i *astibyte.Iterator) (d *DescriptorAVCVideo, err error) { // Init d = &DescriptorAVCVideo{} - var offset int + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Profile idc - d.ProfileIDC = uint8(i[offset]) - offset += 1 + d.ProfileIDC = uint8(b) + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Flags - d.ConstraintSet0Flag = i[offset]&0x80 > 0 - d.ConstraintSet1Flag = i[offset]&0x40 > 0 - d.ConstraintSet2Flag = i[offset]&0x20 > 0 - d.CompatibleFlags = i[offset] & 0x1f - offset += 1 + 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 = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Level idc - d.LevelIDC = uint8(i[offset]) - offset += 1 + d.LevelIDC = uint8(b) + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // AVC still present - d.AVCStillPresent = i[offset]&0x80 > 0 + d.AVCStillPresent = b&0x80 > 0 // AVC 24 hour picture flag - d.AVC24HourPictureFlag = i[offset]&0x40 > 0 + d.AVC24HourPictureFlag = b&0x40 > 0 return } @@ -213,34 +267,53 @@ type DescriptorComponent struct { Text []byte } -func newDescriptorComponent(i []byte) (d *DescriptorComponent) { +func newDescriptorComponent(i *astibyte.Iterator, offsetEnd int) (d *DescriptorComponent, err error) { // Init d = &DescriptorComponent{} - var offset int + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Stream content ext - d.StreamContentExt = uint8(i[offset] >> 4) + d.StreamContentExt = uint8(b >> 4) // Stream content - d.StreamContent = uint8(i[offset] & 0xf) - offset += 1 + d.StreamContent = uint8(b & 0xf) + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Component type - d.ComponentType = uint8(i[offset]) - offset += 1 + d.ComponentType = uint8(b) + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Component tag - d.ComponentTag = uint8(i[offset]) - offset += 1 + d.ComponentTag = uint8(b) // ISO639 language code - d.ISO639LanguageCode = i[offset : offset+3] - offset += 3 + if d.ISO639LanguageCode, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } // Text - for offset < len(i) { - d.Text = append(d.Text, i[offset]) - offset += 1 + if i.Offset() < offsetEnd { + if d.Text, err = i.NextBytes(offsetEnd - i.Offset()); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } return } @@ -259,19 +332,25 @@ type DescriptorContentItem struct { UserByte uint8 } -func newDescriptorContent(i []byte) (d *DescriptorContent) { +func newDescriptorContent(i *astibyte.Iterator, offsetEnd int) (d *DescriptorContent, err error) { // Init d = &DescriptorContent{} - var offset int // Add items - for offset < len(i) { + for i.Offset() < offsetEnd { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Append item d.Items = append(d.Items, &DescriptorContentItem{ - ContentNibbleLevel1: uint8(i[offset] >> 4), - ContentNibbleLevel2: uint8(i[offset] & 0xf), - UserByte: uint8(i[offset+1]), + ContentNibbleLevel1: uint8(bs[0] >> 4), + ContentNibbleLevel2: uint8(bs[0] & 0xf), + UserByte: uint8(bs[1]), }) - offset += 2 } return } @@ -281,8 +360,14 @@ type DescriptorDataStreamAlignment struct { Type uint8 } -func newDescriptorDataStreamAlignment(i []byte) *DescriptorDataStreamAlignment { - return &DescriptorDataStreamAlignment{Type: uint8(i[0])} +func newDescriptorDataStreamAlignment(i *astibyte.Iterator) (d *DescriptorDataStreamAlignment, err error) { + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d = &DescriptorDataStreamAlignment{Type: uint8(b)} + return } // DescriptorEnhancedAC3 represents an enhanced AC3 descriptor @@ -306,49 +391,102 @@ type DescriptorEnhancedAC3 struct { SubStream3 uint8 } -func newDescriptorEnhancedAC3(i []byte) (d *DescriptorEnhancedAC3) { - var offset int - d = &DescriptorEnhancedAC3{} - d.HasComponentType = uint8(i[offset]&0x80) > 0 - d.HasBSID = uint8(i[offset]&0x40) > 0 - d.HasMainID = uint8(i[offset]&0x20) > 0 - d.HasASVC = uint8(i[offset]&0x10) > 0 - d.MixInfoExists = uint8(i[offset]&0x8) > 0 - d.HasSubStream1 = uint8(i[offset]&0x4) > 0 - d.HasSubStream2 = uint8(i[offset]&0x2) > 0 - d.HasSubStream3 = uint8(i[offset]&0x1) > 0 - offset += 1 +func newDescriptorEnhancedAC3(i *astibyte.Iterator, offsetEnd int) (d *DescriptorEnhancedAC3, err error) { + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + 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 { - d.ComponentType = uint8(i[offset]) - offset += 1 + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.ComponentType = uint8(b) } + + // BSID if d.HasBSID { - d.BSID = uint8(i[offset]) - offset += 1 + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.BSID = uint8(b) } + + // Main ID if d.HasMainID { - d.MainID = uint8(i[offset]) - offset += 1 + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.MainID = uint8(b) } + + // ASVC if d.HasASVC { - d.ASVC = uint8(i[offset]) - offset += 1 + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.ASVC = uint8(b) } + + // Substream 1 if d.HasSubStream1 { - d.SubStream1 = uint8(i[offset]) - offset += 1 + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.SubStream1 = uint8(b) } + + // Substream 2 if d.HasSubStream2 { - d.SubStream2 = uint8(i[offset]) - offset += 1 + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.SubStream2 = uint8(b) } + + // Substream 3 if d.HasSubStream3 { - d.SubStream3 = uint8(i[offset]) - offset += 1 + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d.SubStream3 = uint8(b) } - for offset < len(i) { - d.AdditionalInfo = append(d.AdditionalInfo, i[offset]) - offset += 1 + + // Additional info + if i.Offset() < offsetEnd { + if d.AdditionalInfo, err = i.NextBytes(offsetEnd - i.Offset()); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } return } @@ -370,69 +508,102 @@ type DescriptorExtendedEventItem struct { Description []byte } -func newDescriptorExtendedEvent(i []byte) (d *DescriptorExtendedEvent) { +func newDescriptorExtendedEvent(i *astibyte.Iterator) (d *DescriptorExtendedEvent, err error) { // Init d = &DescriptorExtendedEvent{} - var offset int + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Number - d.Number = uint8(i[offset] >> 4) + d.Number = uint8(b >> 4) // Last descriptor number - d.LastDescriptorNumber = uint8(i[offset] & 0xf) - offset += 1 + d.LastDescriptorNumber = uint8(b & 0xf) + + // ISO639 language code + if d.ISO639LanguageCode, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } - // ISO 639 language code - d.ISO639LanguageCode = i[offset : offset+3] - offset += 3 + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Items length - var itemsLength = int(i[offset]) - offset += 1 + itemsLength := int(b) // Items - var offsetEnd = offset + itemsLength - for offset < offsetEnd { - d.Items = append(d.Items, newDescriptorExtendedEventItem(i, &offset)) + offsetEnd := i.Offset() + itemsLength + for i.Offset() < offsetEnd { + // Create item + var item *DescriptorExtendedEventItem + if item, err = newDescriptorExtendedEventItem(i); err != nil { + err = errors.Wrap(err, "astits: creating extended event item failed") + return + } + + // Append item + d.Items = append(d.Items, item) + } + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return } // Text length - var textLength = int(i[offset]) - offset += 1 + textLength := int(b) // Text - offsetEnd = offset + textLength - for offset < offsetEnd { - d.Text = append(d.Text, i[offset]) - offset += 1 + if d.Text, err = i.NextBytes(textLength); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return } return } -func newDescriptorExtendedEventItem(i []byte, offset *int) (d *DescriptorExtendedEventItem) { +func newDescriptorExtendedEventItem(i *astibyte.Iterator) (d *DescriptorExtendedEventItem, err error) { // Init d = &DescriptorExtendedEventItem{} + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + // Description length - var descriptionLength = int(i[*offset]) - *offset += 1 + descriptionLength := int(b) // Description - var offsetEnd = *offset + descriptionLength - for *offset < offsetEnd { - d.Description = append(d.Description, i[*offset]) - *offset += 1 + if d.Description, err = i.NextBytes(descriptionLength); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return } // Content length - var contentLength = int(i[*offset]) - *offset += 1 + contentLength := int(b) // Content - offsetEnd = *offset + contentLength - for *offset < offsetEnd { - d.Content = append(d.Content, i[*offset]) - *offset += 1 + if d.Content, err = i.NextBytes(contentLength); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return } return } @@ -444,15 +615,24 @@ type DescriptorExtension struct { Tag uint8 } -func newDescriptorExtension(i []byte) (d *DescriptorExtension) { - // Init - d = &DescriptorExtension{Tag: uint8(i[0])} +func newDescriptorExtension(i *astibyte.Iterator, offsetEnd int) (d *DescriptorExtension, err error) { + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Create descriptor + d = &DescriptorExtension{Tag: uint8(b)} // Switch on tag - var b = i[1:] switch d.Tag { case DescriptorTagExtensionSupplementaryAudio: - d.SupplementaryAudio = newDescriptorExtensionSupplementaryAudio(b) + if d.SupplementaryAudio, err = newDescriptorExtensionSupplementaryAudio(i, offsetEnd); err != nil { + err = errors.Wrap(err, "astits: parsing extension supplementary audio descriptor failed") + return + } default: // TODO Remove this log astilog.Debugf("astits: unlisted extension tag 0x%x", d.Tag) @@ -470,31 +650,35 @@ type DescriptorExtensionSupplementaryAudio struct { PrivateData []byte } -func newDescriptorExtensionSupplementaryAudio(i []byte) (d *DescriptorExtensionSupplementaryAudio) { - // Init - d = &DescriptorExtensionSupplementaryAudio{} - var offset int - - // Mix type - d.MixType = i[offset]&0x80 > 0 - - // Editorial classification - d.EditorialClassification = uint8(i[offset] >> 2 & 0x1f) +func newDescriptorExtensionSupplementaryAudio(i *astibyte.Iterator, offsetEnd int) (d *DescriptorExtensionSupplementaryAudio, err error) { + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } - // Language code flag - d.HasLanguageCode = i[offset]&0x1 > 0 - offset += 1 + // Init + d = &DescriptorExtensionSupplementaryAudio{ + EditorialClassification: uint8(b >> 2 & 0x1f), + HasLanguageCode: b&0x1 > 0, + MixType: b&0x80 > 0, + } // Language code if d.HasLanguageCode { - d.LanguageCode = i[offset : offset+3] - offset += 3 + if d.LanguageCode, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } // Private data - for offset < len(i) { - d.PrivateData = append(d.PrivateData, i[offset]) - offset += 1 + if i.Offset() < offsetEnd { + if d.PrivateData, err = i.NextBytes(offsetEnd - i.Offset()); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } return } @@ -507,11 +691,20 @@ type DescriptorISO639LanguageAndAudioType struct { } // In some actual cases, the length is 3 and the language is described in only 2 bytes -func newDescriptorISO639LanguageAndAudioType(i []byte) *DescriptorISO639LanguageAndAudioType { - return &DescriptorISO639LanguageAndAudioType{ - Language: i[0 : len(i)-1], - Type: uint8(i[len(i)-1]), +func newDescriptorISO639LanguageAndAudioType(i *astibyte.Iterator, offsetEnd int) (d *DescriptorISO639LanguageAndAudioType, err error) { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(offsetEnd - i.Offset()); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + 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 @@ -531,36 +724,54 @@ type DescriptorLocalTimeOffsetItem struct { TimeOfChange time.Time } -func newDescriptorLocalTimeOffset(i []byte) (d *DescriptorLocalTimeOffset) { +func newDescriptorLocalTimeOffset(i *astibyte.Iterator, offsetEnd int) (d *DescriptorLocalTimeOffset, err error) { // Init d = &DescriptorLocalTimeOffset{} - var offset int // Add items - for offset < len(i) { - // Init - var itm = &DescriptorLocalTimeOffsetItem{} - d.Items = append(d.Items, itm) + for i.Offset() < offsetEnd { + // Create item + itm := &DescriptorLocalTimeOffsetItem{} // Country code - itm.CountryCode = i[offset : offset+3] - offset += 3 + if itm.CountryCode, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Country region ID - itm.CountryRegionID = uint8(i[offset] >> 2) + itm.CountryRegionID = uint8(b >> 2) // Local time offset polarity - itm.LocalTimeOffsetPolarity = i[offset]&0x1 > 0 - offset += 1 + itm.LocalTimeOffsetPolarity = b&0x1 > 0 // Local time offset - itm.LocalTimeOffset = parseDVBDurationMinutes(i, &offset) + if itm.LocalTimeOffset, err = parseDVBDurationMinutes(i); err != nil { + err = errors.Wrap(err, "astits: parsing DVB durationminutes failed") + return + } // Time of change - itm.TimeOfChange = parseDVBTime(i, &offset) + if itm.TimeOfChange, err = parseDVBTime(i); err != nil { + err = errors.Wrap(err, "astits: parsing DVB time failed") + return + } // Next time offset - itm.NextTimeOffset = parseDVBDurationMinutes(i, &offset) + if itm.NextTimeOffset, err = parseDVBDurationMinutes(i); err != nil { + err = errors.Wrap(err, "astits: parsing DVB duration minutes failed") + return + } + + // Append item + d.Items = append(d.Items, itm) } return } @@ -570,8 +781,17 @@ type DescriptorMaximumBitrate struct { Bitrate uint32 // In bytes/second } -func newDescriptorMaximumBitrate(i []byte) *DescriptorMaximumBitrate { - return &DescriptorMaximumBitrate{Bitrate: (uint32(i[0]&0x3f)<<16 | uint32(i[1])<<8 | uint32(i[2])) * 50} +func newDescriptorMaximumBitrate(i *astibyte.Iterator) (d *DescriptorMaximumBitrate, err error) { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + 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 @@ -580,8 +800,16 @@ type DescriptorNetworkName struct { Name []byte } -func newDescriptorNetworkName(i []byte) *DescriptorNetworkName { - return &DescriptorNetworkName{Name: i} +func newDescriptorNetworkName(i *astibyte.Iterator, offsetEnd int) (d *DescriptorNetworkName, err error) { + // Create descriptor + d = &DescriptorNetworkName{} + + // Name + if d.Name, err = i.NextBytes(offsetEnd - i.Offset()); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + return } // DescriptorParentalRating represents a parental rating descriptor @@ -606,18 +834,24 @@ func (d DescriptorParentalRatingItem) MinimumAge() int { return int(d.Rating) + 3 } -func newDescriptorParentalRating(i []byte) (d *DescriptorParentalRating) { - // Init +func newDescriptorParentalRating(i *astibyte.Iterator, offsetEnd int) (d *DescriptorParentalRating, err error) { + // Create descriptor d = &DescriptorParentalRating{} - var offset int // Add items - for offset < len(i) { + for i.Offset() < offsetEnd { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(4); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Append item d.Items = append(d.Items, &DescriptorParentalRatingItem{ - CountryCode: i[offset : offset+3], - Rating: uint8(i[offset+3]), + CountryCode: bs[:3], + Rating: uint8(bs[3]), }) - offset += 4 } return } @@ -627,8 +861,17 @@ type DescriptorPrivateDataIndicator struct { Indicator uint32 } -func newDescriptorPrivateDataIndicator(i []byte) *DescriptorPrivateDataIndicator { - return &DescriptorPrivateDataIndicator{Indicator: uint32(i[0])<<24 | uint32(i[1])<<16 | uint32(i[2])<<8 | uint32(i[3])} +func newDescriptorPrivateDataIndicator(i *astibyte.Iterator) (d *DescriptorPrivateDataIndicator, err error) { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(4); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + 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 @@ -636,8 +879,17 @@ type DescriptorPrivateDataSpecifier struct { Specifier uint32 } -func newDescriptorPrivateDataSpecifier(i []byte) *DescriptorPrivateDataSpecifier { - return &DescriptorPrivateDataSpecifier{Specifier: uint32(i[0])<<24 | uint32(i[1])<<16 | uint32(i[2])<<8 | uint32(i[3])} +func newDescriptorPrivateDataSpecifier(i *astibyte.Iterator) (d *DescriptorPrivateDataSpecifier, err error) { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(4); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + 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 @@ -647,13 +899,23 @@ type DescriptorRegistration struct { FormatIdentifier uint32 } -func newDescriptorRegistration(i []byte) (d *DescriptorRegistration) { - d = &DescriptorRegistration{} - d.FormatIdentifier = uint32(i[0])<<24 | uint32(i[1])<<16 | uint32(i[2])<<8 | uint32(i[3]) - var offset = 4 - for offset < len(i) { - d.AdditionalIdentificationInfo = append(d.AdditionalIdentificationInfo, i[offset]) - offset += 1 +func newDescriptorRegistration(i *astibyte.Iterator, offsetEnd int) (d *DescriptorRegistration, err error) { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(4); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + 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 = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } return } @@ -666,17 +928,46 @@ type DescriptorService struct { Type uint8 } -func newDescriptorService(i []byte) (d *DescriptorService) { - var offset int - d = &DescriptorService{Type: uint8(i[offset])} - offset += 1 - var providerLength = int(i[offset]) - offset += 1 - d.Provider = i[offset : offset+providerLength] - offset += providerLength - var nameLength = int(i[offset]) - offset += 1 - d.Name = i[offset : offset+nameLength] +func newDescriptorService(i *astibyte.Iterator) (d *DescriptorService, err error) { + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Create descriptor + d = &DescriptorService{Type: uint8(b)} + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Provider length + providerLength := int(b) + + // Provider + if d.Provider, err = i.NextBytes(providerLength); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Name length + nameLength := int(b) + + // Name + if d.Name, err = i.NextBytes(nameLength); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } return } @@ -688,18 +979,46 @@ type DescriptorShortEvent struct { Text []byte } -func newDescriptorShortEvent(i []byte) (d *DescriptorShortEvent) { - var offset int +func newDescriptorShortEvent(i *astibyte.Iterator) (d *DescriptorShortEvent, err error) { + // Create descriptor d = &DescriptorShortEvent{} - d.Language = i[:3] - offset += 3 - var length = int(i[offset]) - offset += 1 - d.EventName = i[offset : offset+length] - offset += length - length = int(i[offset]) - offset += 1 - d.Text = i[offset : offset+length] + + // Language + if d.Language, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Event length + eventLength := int(b) + + // Event name + if d.EventName, err = i.NextBytes(eventLength); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Text length + textLength := int(b) + + // Text + if d.Text, err = i.NextBytes(textLength); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } return } @@ -707,8 +1026,14 @@ func newDescriptorShortEvent(i []byte) (d *DescriptorShortEvent) { // 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 []byte) *DescriptorStreamIdentifier { - return &DescriptorStreamIdentifier{ComponentTag: uint8(i[0])} +func newDescriptorStreamIdentifier(i *astibyte.Iterator) (d *DescriptorStreamIdentifier, err error) { + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + d = &DescriptorStreamIdentifier{ComponentTag: uint8(b)} + return } // DescriptorSubtitling represents a subtitling descriptor @@ -726,19 +1051,51 @@ type DescriptorSubtitlingItem struct { Type uint8 } -func newDescriptorSubtitling(i []byte) (d *DescriptorSubtitling) { +func newDescriptorSubtitling(i *astibyte.Iterator, offsetEnd int) (d *DescriptorSubtitling, err error) { + // Create descriptor d = &DescriptorSubtitling{} - var offset int - for offset < len(i) { + + // Loop + for i.Offset() < offsetEnd { + // Create item itm := &DescriptorSubtitlingItem{} - itm.Language = i[offset : offset+3] - offset += 3 - itm.Type = uint8(i[offset]) - offset += 1 - itm.CompositionPageID = uint16(i[offset])<<8 | uint16(i[offset+1]) - offset += 2 - itm.AncillaryPageID = uint16(i[offset])<<8 | uint16(i[offset+1]) - offset += 2 + + // Language + if itm.Language, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Type + itm.Type = uint8(b) + + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Composition page ID + itm.CompositionPageID = uint16(bs[0])<<8 | uint16(bs[1]) + + // Get next bytes + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Ancillary page ID + itm.AncillaryPageID = uint16(bs[0])<<8 | uint16(bs[1]) + + // Append item d.Items = append(d.Items, itm) } return @@ -759,18 +1116,44 @@ type DescriptorTeletextItem struct { Type uint8 } -func newDescriptorTeletext(i []byte) (d *DescriptorTeletext) { - var offset int +func newDescriptorTeletext(i *astibyte.Iterator, offsetEnd int) (d *DescriptorTeletext, err error) { + // Create descriptor d = &DescriptorTeletext{} - for offset < len(i) { + + // Loop + for i.Offset() < offsetEnd { + // Create item itm := &DescriptorTeletextItem{} - itm.Language = i[offset : offset+3] - offset += 3 - itm.Type = uint8(i[offset]) >> 3 - itm.Magazine = uint8(i[offset] & 0x7) - offset += 1 - itm.Page = uint8(i[offset])>>4*10 + uint8(i[offset]&0xf) - offset += 1 + + // Language + if itm.Language, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Type + itm.Type = uint8(b) >> 3 + + // Magazine + itm.Magazine = uint8(b & 0x7) + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Page + itm.Page = uint8(b)>>4*10 + uint8(b&0xf) + + // Append item d.Items = append(d.Items, itm) } return @@ -796,38 +1179,54 @@ type DescriptorVBIDataDescriptor struct { LineOffset uint8 } -func newDescriptorVBIData(i []byte) (d *DescriptorVBIData) { - // Init +func newDescriptorVBIData(i *astibyte.Iterator, offsetEnd int) (d *DescriptorVBIData, err error) { + // Create descriptor d = &DescriptorVBIData{} - var offset int - // Items - for offset < len(i) { - // Init - var srv = &DescriptorVBIDataService{} + // Loop + for i.Offset() < offsetEnd { + // Create service + srv := &DescriptorVBIDataService{} + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Data service ID - srv.DataServiceID = uint8(i[offset]) - offset += 1 + srv.DataServiceID = uint8(b) + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Data service descriptor length - var dataServiceDescriptorLength = int(i[offset]) - offset += 1 + dataServiceDescriptorLength := int(b) // Data service descriptor - var offsetEnd = offset + dataServiceDescriptorLength - for offset < offsetEnd { + 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 = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Append data srv.Descriptors = append(srv.Descriptors, &DescriptorVBIDataDescriptor{ - FieldParity: i[offset]&0x20 > 0, - LineOffset: uint8(i[offset] & 0x1f), + FieldParity: b&0x20 > 0, + LineOffset: uint8(b & 0x1f), }) - offset += 1 } } @@ -838,86 +1237,173 @@ func newDescriptorVBIData(i []byte) (d *DescriptorVBIData) { } // parseDescriptors parses descriptors -func parseDescriptors(i []byte, offset *int) (o []*Descriptor) { +func parseDescriptors(i *astibyte.Iterator) (o []*Descriptor, err error) { + // Get next 2 bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Get length - var length = int(uint16(i[*offset]&0xf)<<8 | uint16(i[*offset+1])) - *offset += 2 + length := int(uint16(bs[0]&0xf)<<8 | uint16(bs[1])) // Loop if length > 0 { - length += *offset - for *offset < length { - // Init - var d = &Descriptor{ - Length: uint8(i[*offset+1]), - Tag: uint8(i[*offset]), + offsetEnd := i.Offset() + length + for i.Offset() < offsetEnd { + // Get next 2 bytes + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + + // Create descriptor + d := &Descriptor{ + Length: uint8(bs[1]), + Tag: uint8(bs[0]), } - *offset += 2 // Parse data if d.Length > 0 { - // Get descriptor content - var b = i[*offset : *offset+int(d.Length)] + // 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 { - d.UserDefined = make([]byte, len(b)) - copy(d.UserDefined, b) + // Get next bytes + if d.UserDefined, err = i.NextBytes(int(d.Length)); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } else { // Switch on tag switch d.Tag { case DescriptorTagAC3: - d.AC3 = newDescriptorAC3(b) + if d.AC3, err = newDescriptorAC3(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing AC3 descriptor failed") + return + } case DescriptorTagAVCVideo: - d.AVCVideo = newDescriptorAVCVideo(b) + if d.AVCVideo, err = newDescriptorAVCVideo(i); err != nil { + err = errors.Wrap(err, "astits: parsing AVC Video descriptor failed") + return + } case DescriptorTagComponent: - d.Component = newDescriptorComponent(b) + if d.Component, err = newDescriptorComponent(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Component descriptor failed") + return + } case DescriptorTagContent: - d.Content = newDescriptorContent(b) + if d.Content, err = newDescriptorContent(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Content descriptor failed") + return + } case DescriptorTagDataStreamAlignment: - d.DataStreamAlignment = newDescriptorDataStreamAlignment(b) + if d.DataStreamAlignment, err = newDescriptorDataStreamAlignment(i); err != nil { + err = errors.Wrap(err, "astits: parsing Data Stream Alignment descriptor failed") + return + } case DescriptorTagEnhancedAC3: - d.EnhancedAC3 = newDescriptorEnhancedAC3(b) + if d.EnhancedAC3, err = newDescriptorEnhancedAC3(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Enhanced AC3 descriptor failed") + return + } case DescriptorTagExtendedEvent: - d.ExtendedEvent = newDescriptorExtendedEvent(b) + if d.ExtendedEvent, err = newDescriptorExtendedEvent(i); err != nil { + err = errors.Wrap(err, "astits: parsing Extended event descriptor failed") + return + } case DescriptorTagExtension: - d.Extension = newDescriptorExtension(b) + if d.Extension, err = newDescriptorExtension(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Extension descriptor failed") + return + } case DescriptorTagISO639LanguageAndAudioType: - d.ISO639LanguageAndAudioType = newDescriptorISO639LanguageAndAudioType(b) + if d.ISO639LanguageAndAudioType, err = newDescriptorISO639LanguageAndAudioType(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing ISO639 Language and Audio Type descriptor failed") + return + } case DescriptorTagLocalTimeOffset: - d.LocalTimeOffset = newDescriptorLocalTimeOffset(b) + if d.LocalTimeOffset, err = newDescriptorLocalTimeOffset(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Local Time Offset descriptor failed") + return + } case DescriptorTagMaximumBitrate: - d.MaximumBitrate = newDescriptorMaximumBitrate(b) + if d.MaximumBitrate, err = newDescriptorMaximumBitrate(i); err != nil { + err = errors.Wrap(err, "astits: parsing Maximum Bitrate descriptor failed") + return + } case DescriptorTagNetworkName: - d.NetworkName = newDescriptorNetworkName(b) + if d.NetworkName, err = newDescriptorNetworkName(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Network Name descriptor failed") + return + } case DescriptorTagParentalRating: - d.ParentalRating = newDescriptorParentalRating(b) + if d.ParentalRating, err = newDescriptorParentalRating(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Parental Rating descriptor failed") + return + } case DescriptorTagPrivateDataIndicator: - d.PrivateDataIndicator = newDescriptorPrivateDataIndicator(b) + if d.PrivateDataIndicator, err = newDescriptorPrivateDataIndicator(i); err != nil { + err = errors.Wrap(err, "astits: parsing Private Data Indicator descriptor failed") + return + } case DescriptorTagPrivateDataSpecifier: - d.PrivateDataSpecifier = newDescriptorPrivateDataSpecifier(b) + if d.PrivateDataSpecifier, err = newDescriptorPrivateDataSpecifier(i); err != nil { + err = errors.Wrap(err, "astits: parsing Private Data Specifier descriptor failed") + return + } case DescriptorTagRegistration: - d.Registration = newDescriptorRegistration(b) + if d.Registration, err = newDescriptorRegistration(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Registration descriptor failed") + return + } case DescriptorTagService: - d.Service = newDescriptorService(b) + if d.Service, err = newDescriptorService(i); err != nil { + err = errors.Wrap(err, "astits: parsing Service descriptor failed") + return + } case DescriptorTagShortEvent: - d.ShortEvent = newDescriptorShortEvent(b) + if d.ShortEvent, err = newDescriptorShortEvent(i); err != nil { + err = errors.Wrap(err, "astits: parsing Short Event descriptor failed") + return + } case DescriptorTagStreamIdentifier: - d.StreamIdentifier = newDescriptorStreamIdentifier(b) + if d.StreamIdentifier, err = newDescriptorStreamIdentifier(i); err != nil { + err = errors.Wrap(err, "astits: parsing Stream Identifier descriptor failed") + return + } case DescriptorTagSubtitling: - d.Subtitling = newDescriptorSubtitling(b) + if d.Subtitling, err = newDescriptorSubtitling(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Subtitling descriptor failed") + return + } case DescriptorTagTeletext: - d.Teletext = newDescriptorTeletext(b) + if d.Teletext, err = newDescriptorTeletext(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing Teletext descriptor failed") + return + } case DescriptorTagVBIData: - d.VBIData = newDescriptorVBIData(b) + if d.VBIData, err = newDescriptorVBIData(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing VBI Date descriptor failed") + return + } case DescriptorTagVBITeletext: - d.VBITeletext = newDescriptorTeletext(b) + if d.VBITeletext, err = newDescriptorTeletext(i, offsetDescriptorEnd); err != nil { + err = errors.Wrap(err, "astits: parsing VBI Teletext descriptor failed") + return + } default: // TODO Remove this log astilog.Debugf("astits: unlisted descriptor tag 0x%x", d.Tag) } } - *offset += int(d.Length) + + // 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) } diff --git a/descriptor_test.go b/descriptor_test.go index dac4378..61324ec 100644 --- a/descriptor_test.go +++ b/descriptor_test.go @@ -3,7 +3,8 @@ package astits import ( "testing" - "github.com/asticode/go-astitools/binary" + astibinary "github.com/asticode/go-astitools/binary" + astibyte "github.com/asticode/go-astitools/byte" "github.com/stretchr/testify/assert" ) @@ -214,8 +215,8 @@ func TestParseDescriptor(t *testing.T) { w.Write([]byte("test")) // Additional identification info // Assert - var offset int - ds := parseDescriptors(w.Bytes(), &offset) + ds, err := parseDescriptors(astibyte.NewIterator(w.Bytes())) + assert.NoError(t, err) assert.Equal(t, *ds[0].AC3, DescriptorAC3{ AdditionalInfo: []byte("info"), ASVC: uint8(4), diff --git a/dvb.go b/dvb.go index e554edd..6f47b91 100644 --- a/dvb.go +++ b/dvb.go @@ -3,6 +3,9 @@ package astits import ( "fmt" "time" + + astibyte "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" ) // parseDVBTime parses a DVB time @@ -11,9 +14,16 @@ import ( // field are set to "1". // I apologize for the computation which is really messy but details are given in the documentation // Page: 160 | Annex C | Link: https://www.dvb.org/resources/public/standards/a38_dvb-si_specification.pdf -func parseDVBTime(i []byte, offset *int) (t time.Time) { +func parseDVBTime(i *astibyte.Iterator) (t time.Time, err error) { + // Get next 2 bytes + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + // Date - var mjd = uint16(i[*offset])<<8 | uint16(i[*offset+1]) + var mjd = uint16(bs[0])<<8 | uint16(bs[1]) var yt = int((float64(mjd) - 15078.2) / 365.25) var mt = int((float64(mjd) - 14956.1 - float64(int(float64(yt)*365.25))) / 30.6001) var d = int(float64(mjd) - 14956 - float64(int(float64(yt)*365.25)) - float64(int(float64(mt)*30.6001))) @@ -24,26 +34,38 @@ func parseDVBTime(i []byte, offset *int) (t time.Time) { var y = yt + k var m = mt - 1 - k*12 t, _ = time.Parse("06-01-02", fmt.Sprintf("%d-%d-%d", y, m, d)) - *offset += 2 // Time - t = t.Add(parseDVBDurationSeconds(i, offset)) + var s time.Duration + if s, err = parseDVBDurationSeconds(i); err != nil { + err = errors.Wrap(err, "astits: parsing DVB duration seconds failed") + return + } + t = t.Add(s) return } // parseDVBDurationMinutes parses a minutes duration // 16 bit field containing the duration of the event in hours, minutes. format: 4 digits, 4 - bit BCD = 18 bit -func parseDVBDurationMinutes(i []byte, offset *int) (d time.Duration) { - d = parseDVBDurationByte(i[*offset])*time.Hour + parseDVBDurationByte(i[*offset+1])*time.Minute - *offset += 2 +func parseDVBDurationMinutes(i *astibyte.Iterator) (d time.Duration, err error) { + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + d = parseDVBDurationByte(bs[0])*time.Hour + parseDVBDurationByte(bs[1])*time.Minute return } // parseDVBDurationSeconds parses a seconds duration // 24 bit field containing the duration of the event in hours, minutes, seconds. format: 6 digits, 4 - bit BCD = 24 bit -func parseDVBDurationSeconds(i []byte, offset *int) (d time.Duration) { - d = parseDVBDurationByte(i[*offset])*time.Hour + parseDVBDurationByte(i[*offset+1])*time.Minute + parseDVBDurationByte(i[*offset+2])*time.Second - *offset += 3 +func parseDVBDurationSeconds(i *astibyte.Iterator) (d time.Duration, err error) { + var bs []byte + if bs, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + d = parseDVBDurationByte(bs[0])*time.Hour + parseDVBDurationByte(bs[1])*time.Minute + parseDVBDurationByte(bs[2])*time.Second return } diff --git a/dvb_test.go b/dvb_test.go index 186140b..013e3c5 100644 --- a/dvb_test.go +++ b/dvb_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + astibyte "github.com/asticode/go-astitools/byte" "github.com/stretchr/testify/assert" ) @@ -17,22 +18,19 @@ var ( ) func TestParseDVBTime(t *testing.T) { - var offset int - d := parseDVBTime(dvbTimeBytes, &offset) + d, err := parseDVBTime(astibyte.NewIterator(dvbTimeBytes)) assert.Equal(t, dvbTime, d) - assert.Equal(t, 5, offset) + assert.NoError(t, err) } func TestParseDVBDurationMinutes(t *testing.T) { - var offset int - d := parseDVBDurationMinutes(dvbDurationMinutesBytes, &offset) + d, err := parseDVBDurationMinutes(astibyte.NewIterator(dvbDurationMinutesBytes)) assert.Equal(t, dvbDurationMinutes, d) - assert.Equal(t, 2, offset) + assert.NoError(t, err) } func TestParseDVBDurationSeconds(t *testing.T) { - var offset int - d := parseDVBDurationSeconds(dvbDurationSecondsBytes, &offset) + d, err := parseDVBDurationSeconds(astibyte.NewIterator(dvbDurationSecondsBytes)) assert.Equal(t, dvbDurationSeconds, d) - assert.Equal(t, 3, offset) + assert.NoError(t, err) } diff --git a/packet.go b/packet.go index 7e6b2e1..48d9834 100644 --- a/packet.go +++ b/packet.go @@ -1,5 +1,10 @@ package astits +import ( + astibyte "github.com/asticode/go-astitools/byte" + "github.com/pkg/errors" +) + // Scrambling Controls const ( ScramblingControlNotScrambled = 0 @@ -12,7 +17,6 @@ const ( // https://en.wikipedia.org/wiki/MPEG_transport_stream type Packet struct { AdaptationField *PacketAdaptationField - Bytes []byte // This is the whole packet content Header *PacketHeader Payload []byte // This is only the payload content } @@ -62,37 +66,52 @@ type PacketAdaptationExtensionField struct { } // parsePacket parses a packet -func parsePacket(i []byte) (p *Packet, err error) { +func parsePacket(i *astibyte.Iterator) (p *Packet, err error) { + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: getting next byte failed") + return + } + // Packet must start with a sync byte - if i[0] != syncByte { + if b != syncByte { err = ErrPacketMustStartWithASyncByte return } - // Init - p = &Packet{Bytes: i} + // Create packet + p = &Packet{} // In case packet size is bigger than 188 bytes, we don't care for the first bytes - i = i[len(i)-188+1:] + i.Seek(i.Len() - 188 + 1) + offsetStart := i.Offset() // Parse header - p.Header = parsePacketHeader(i) + if p.Header, err = parsePacketHeader(i); err != nil { + err = errors.Wrap(err, "astits: parsing packet header failed") + return + } // Parse adaptation field if p.Header.HasAdaptationField { - p.AdaptationField = parsePacketAdaptationField(i[3:]) + if p.AdaptationField, err = parsePacketAdaptationField(i); err != nil { + err = errors.Wrap(err, "astits: parsing packet adaptation field failed") + return + } } // Build payload if p.Header.HasPayload { - p.Payload = i[payloadOffset(p.Header, p.AdaptationField):] + i.Seek(payloadOffset(offsetStart, p.Header, p.AdaptationField)) + p.Payload = i.Dump() } return } // payloadOffset returns the payload offset -func payloadOffset(h *PacketHeader, a *PacketAdaptationField) (offset int) { - offset = 3 +func payloadOffset(offsetStart int, h *PacketHeader, a *PacketAdaptationField) (offset int) { + offset = offsetStart + 3 if h.HasAdaptationField { offset += 1 + a.Length } @@ -100,98 +119,169 @@ func payloadOffset(h *PacketHeader, a *PacketAdaptationField) (offset int) { } // parsePacketHeader parses the packet header -func parsePacketHeader(i []byte) *PacketHeader { - return &PacketHeader{ - ContinuityCounter: uint8(i[2] & 0xf), - HasAdaptationField: i[2]&0x20 > 0, - HasPayload: i[2]&0x10 > 0, - PayloadUnitStartIndicator: i[0]&0x40 > 0, - PID: uint16(i[0]&0x1f)<<8 | uint16(i[1]), - TransportErrorIndicator: i[0]&0x80 > 0, - TransportPriority: i[0]&0x20 > 0, - TransportScramblingControl: uint8(i[2]) >> 6 & 0x3, +func parsePacketHeader(i *astibyte.Iterator) (h *PacketHeader, err error) { + // Get next bytes + var bs []byte + if bs, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return } + + // Create header + h = &PacketHeader{ + ContinuityCounter: uint8(bs[2] & 0xf), + HasAdaptationField: bs[2]&0x20 > 0, + HasPayload: bs[2]&0x10 > 0, + PayloadUnitStartIndicator: bs[0]&0x40 > 0, + PID: uint16(bs[0]&0x1f)<<8 | uint16(bs[1]), + TransportErrorIndicator: bs[0]&0x80 > 0, + TransportPriority: bs[0]&0x20 > 0, + TransportScramblingControl: uint8(bs[2]) >> 6 & 0x3, + } + return } // parsePacketAdaptationField parses the packet adaptation field -func parsePacketAdaptationField(i []byte) (a *PacketAdaptationField) { - // Init +func parsePacketAdaptationField(i *astibyte.Iterator) (a *PacketAdaptationField, err error) { + // Create adaptation field a = &PacketAdaptationField{} - var offset int + + // Get next byte + var b byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } // Length - a.Length = int(i[offset]) - offset += 1 + a.Length = int(b) // Valid length if a.Length > 0 { + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + // Flags - a.DiscontinuityIndicator = i[offset]&0x80 > 0 - a.RandomAccessIndicator = i[offset]&0x40 > 0 - a.ElementaryStreamPriorityIndicator = i[offset]&0x20 > 0 - a.HasPCR = i[offset]&0x10 > 0 - a.HasOPCR = i[offset]&0x08 > 0 - a.HasSplicingCountdown = i[offset]&0x04 > 0 - a.HasTransportPrivateData = i[offset]&0x02 > 0 - a.HasAdaptationExtensionField = i[offset]&0x01 > 0 - offset += 1 + a.DiscontinuityIndicator = b&0x80 > 0 + a.RandomAccessIndicator = b&0x40 > 0 + a.ElementaryStreamPriorityIndicator = b&0x20 > 0 + a.HasPCR = b&0x10 > 0 + a.HasOPCR = b&0x08 > 0 + a.HasSplicingCountdown = b&0x04 > 0 + a.HasTransportPrivateData = b&0x02 > 0 + a.HasAdaptationExtensionField = b&0x01 > 0 // PCR if a.HasPCR { - a.PCR = parsePCR(i[offset:]) - offset += 6 + if a.PCR, err = parsePCR(i); err != nil { + err = errors.Wrap(err, "astits: parsing PCR failed") + return + } } // OPCR if a.HasOPCR { - a.OPCR = parsePCR(i[offset:]) - offset += 6 + if a.OPCR, err = parsePCR(i); err != nil { + err = errors.Wrap(err, "astits: parsing PCR failed") + return + } } // Splicing countdown if a.HasSplicingCountdown { - a.SpliceCountdown = int(i[offset]) - offset += 1 + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + a.SpliceCountdown = int(b) } // Transport private data if a.HasTransportPrivateData { - a.TransportPrivateDataLength = int(i[offset]) - offset += 1 + // Length + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + a.TransportPrivateDataLength = int(b) + + // Data if a.TransportPrivateDataLength > 0 { - a.TransportPrivateData = i[offset : offset+a.TransportPrivateDataLength] - offset += a.TransportPrivateDataLength + if a.TransportPrivateData, err = i.NextBytes(a.TransportPrivateDataLength); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } } } // Adaptation extension if a.HasAdaptationExtensionField { - a.AdaptationExtensionField = &PacketAdaptationExtensionField{Length: int(i[offset])} - offset += 1 + // Create extension field + a.AdaptationExtensionField = &PacketAdaptationExtensionField{} + + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Length + a.AdaptationExtensionField.Length = int(b) if a.AdaptationExtensionField.Length > 0 { + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + // Basic - a.AdaptationExtensionField.HasLegalTimeWindow = i[offset]&0x80 > 0 - a.AdaptationExtensionField.HasPiecewiseRate = i[offset]&0x40 > 0 - a.AdaptationExtensionField.HasSeamlessSplice = i[offset]&0x20 > 0 - offset += 1 + a.AdaptationExtensionField.HasLegalTimeWindow = b&0x80 > 0 + a.AdaptationExtensionField.HasPiecewiseRate = b&0x40 > 0 + a.AdaptationExtensionField.HasSeamlessSplice = b&0x20 > 0 // Legal time window if a.AdaptationExtensionField.HasLegalTimeWindow { - a.AdaptationExtensionField.LegalTimeWindowIsValid = i[offset]&0x80 > 0 - a.AdaptationExtensionField.LegalTimeWindowOffset = uint16(i[offset]&0x7f)<<8 | uint16(i[offset+1]) - offset += 2 + var bs []byte + if bs, err = i.NextBytes(2); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + a.AdaptationExtensionField.LegalTimeWindowIsValid = bs[0]&0x80 > 0 + a.AdaptationExtensionField.LegalTimeWindowOffset = uint16(bs[0]&0x7f)<<8 | uint16(bs[1]) } // Piecewise rate if a.AdaptationExtensionField.HasPiecewiseRate { - a.AdaptationExtensionField.PiecewiseRate = uint32(i[offset]&0x3f)<<16 | uint32(i[offset+1])<<8 | uint32(i[offset+2]) - offset += 3 + var bs []byte + if bs, err = i.NextBytes(3); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + a.AdaptationExtensionField.PiecewiseRate = uint32(bs[0]&0x3f)<<16 | uint32(bs[1])<<8 | uint32(bs[2]) } // Seamless splice if a.AdaptationExtensionField.HasSeamlessSplice { - a.AdaptationExtensionField.SpliceType = uint8(i[offset]&0xf0) >> 4 - a.AdaptationExtensionField.DTSNextAccessUnit = parsePTSOrDTS(i[offset:]) + // Get next byte + if b, err = i.NextByte(); err != nil { + err = errors.Wrap(err, "astits: fetching next byte failed") + return + } + + // Splice type + a.AdaptationExtensionField.SpliceType = uint8(b&0xf0) >> 4 + + // We need to rewind since the current byte is used by the DTS next access unit as well + i.FastForward(-1) + + // DTS Next access unit + if a.AdaptationExtensionField.DTSNextAccessUnit, err = parsePTSOrDTS(i); err != nil { + err = errors.Wrap(err, "astits: parsing DTS failed") + return + } } } } @@ -201,7 +291,13 @@ func parsePacketAdaptationField(i []byte) (a *PacketAdaptationField) { // parsePCR parses a Program Clock Reference // Program clock reference, stored as 33 bits base, 6 bits reserved, 9 bits extension. -func parsePCR(i []byte) *ClockReference { - var pcr = uint64(i[0])<<40 | uint64(i[1])<<32 | uint64(i[2])<<24 | uint64(i[3])<<16 | uint64(i[4])<<8 | uint64(i[5]) - return newClockReference(int(pcr>>15), int(pcr&0x1ff)) +func parsePCR(i *astibyte.Iterator) (cr *ClockReference, err error) { + var bs []byte + if bs, err = i.NextBytes(6); err != nil { + err = errors.Wrap(err, "astits: fetching next bytes failed") + return + } + pcr := uint64(bs[0])<<40 | uint64(bs[1])<<32 | uint64(bs[2])<<24 | uint64(bs[3])<<16 | uint64(bs[4])<<8 | uint64(bs[5]) + cr = newClockReference(int(pcr>>15), int(pcr&0x1ff)) + return } diff --git a/packet_buffer.go b/packet_buffer.go index 409cd9c..13832b8 100644 --- a/packet_buffer.go +++ b/packet_buffer.go @@ -4,6 +4,7 @@ import ( "fmt" "io" + astibyte "github.com/asticode/go-astitools/byte" "github.com/pkg/errors" ) @@ -103,7 +104,7 @@ func (pb *packetBuffer) next() (p *Packet, err error) { } // Parse packet - if p, err = parsePacket(b); err != nil { + if p, err = parsePacket(astibyte.NewIterator(b)); err != nil { err = errors.Wrap(err, "astits: building packet failed") return } diff --git a/packet_test.go b/packet_test.go index a27131a..a12be23 100644 --- a/packet_test.go +++ b/packet_test.go @@ -4,7 +4,8 @@ import ( "fmt" "testing" - "github.com/asticode/go-astitools/binary" + astibinary "github.com/asticode/go-astitools/binary" + astibyte "github.com/asticode/go-astitools/byte" "github.com/stretchr/testify/assert" ) @@ -18,7 +19,6 @@ func packet(h PacketHeader, a PacketAdaptationField, i []byte) ([]byte, *Packet) w.Write(payload) return w.Bytes(), &Packet{ AdaptationField: packetAdaptationField, - Bytes: w.Bytes(), Header: packetHeader, Payload: payload, } @@ -28,27 +28,27 @@ func TestParsePacket(t *testing.T) { // Packet not starting with a sync w := astibinary.New() w.Write(uint16(1)) // Invalid sync byte - _, err := parsePacket(w.Bytes()) + _, err := parsePacket(astibyte.NewIterator(w.Bytes())) assert.EqualError(t, err, ErrPacketMustStartWithASyncByte.Error()) // Valid b, ep := packet(*packetHeader, *packetAdaptationField, []byte("payload")) - p, err := parsePacket(b) + p, err := parsePacket(astibyte.NewIterator(b)) assert.NoError(t, err) assert.Equal(t, p, ep) } func TestPayloadOffset(t *testing.T) { - assert.Equal(t, 3, payloadOffset(&PacketHeader{}, nil)) - assert.Equal(t, 6, payloadOffset(&PacketHeader{HasAdaptationField: true}, &PacketAdaptationField{Length: 2})) + assert.Equal(t, 3, payloadOffset(0, &PacketHeader{}, nil)) + assert.Equal(t, 7, payloadOffset(1, &PacketHeader{HasAdaptationField: true}, &PacketAdaptationField{Length: 2})) } var packetHeader = &PacketHeader{ - ContinuityCounter: 10, - HasAdaptationField: true, - HasPayload: true, - PayloadUnitStartIndicator: true, - PID: 5461, + ContinuityCounter: 10, + HasAdaptationField: true, + HasPayload: true, + PayloadUnitStartIndicator: true, + PID: 5461, TransportErrorIndicator: true, TransportPriority: true, TransportScramblingControl: ScramblingControlScrambledWithEvenKey, @@ -67,7 +67,9 @@ func packetHeaderBytes(h PacketHeader) []byte { } func TestParsePacketHeader(t *testing.T) { - assert.Equal(t, packetHeader, parsePacketHeader(packetHeaderBytes(*packetHeader))) + v, err := parsePacketHeader(astibyte.NewIterator(packetHeaderBytes(*packetHeader))) + assert.Equal(t, packetHeader, v) + assert.NoError(t, err) } var packetAdaptationField = &PacketAdaptationField{ @@ -92,10 +94,10 @@ var packetAdaptationField = &PacketAdaptationField{ Length: 36, OPCR: pcr, PCR: pcr, - RandomAccessIndicator: true, - SpliceCountdown: 2, - TransportPrivateDataLength: 4, - TransportPrivateData: []byte("test"), + RandomAccessIndicator: true, + SpliceCountdown: 2, + TransportPrivateDataLength: 4, + TransportPrivateData: []byte("test"), } func packetAdaptationFieldBytes(a PacketAdaptationField) []byte { @@ -129,7 +131,9 @@ func packetAdaptationFieldBytes(a PacketAdaptationField) []byte { } func TestParsePacketAdaptationField(t *testing.T) { - assert.Equal(t, packetAdaptationField, parsePacketAdaptationField(packetAdaptationFieldBytes(*packetAdaptationField))) + v, err := parsePacketAdaptationField(astibyte.NewIterator(packetAdaptationFieldBytes(*packetAdaptationField))) + assert.Equal(t, packetAdaptationField, v) + assert.NoError(t, err) } var pcr = &ClockReference{ @@ -146,5 +150,7 @@ func pcrBytes() []byte { } func TestParsePCR(t *testing.T) { - assert.Equal(t, pcr, parsePCR(pcrBytes())) + v, err := parsePCR(astibyte.NewIterator(pcrBytes())) + assert.Equal(t, pcr, v) + assert.NoError(t, err) }