mpls/parse.go
lordwelch 3808678a59 cleanup
cleanup test files and separate into a main package and a library package in prep for writing mpls files
2019-05-17 10:59:10 -07:00

610 lines
14 KiB
Go

package mpls
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"os"
)
type errReader struct {
RS *bytes.Reader
err error
}
func (er *errReader) Read(p []byte) (n int, err error) {
if er.err != nil {
return 0, er.err
}
n, er.err = er.RS.Read(p)
if n != len(p) {
er.err = fmt.Errorf("%s", "Invalid read")
}
return n, er.err
}
func (er *errReader) Seek(offset int64, whence int) (int64, error) {
if er.err != nil {
return 0, er.err
}
var n64 int64
n64, er.err = er.RS.Seek(offset, whence)
return n64, er.err
}
// Parse parses an MPLS file into an MPLS struct
func Parse(reader io.Reader) (mpls MPLS, err error) {
var (
file []byte
)
file, err = ioutil.ReadAll(reader)
if err != nil {
return MPLS{}, err
}
err = mpls.Parse(file)
return mpls, err
}
// Parse reads MPLS data from an io.ReadSeeker
func (mpls *MPLS) Parse(file []byte) error {
var (
buf [10]byte
n int
err error
start int64
)
reader := &errReader{
RS: bytes.NewReader(file),
err: nil,
}
n, err = reader.Read(buf[:8])
if err != nil || n != 8 {
return err
}
str := string(buf[:8])
if str[:4] != "MPLS" {
return fmt.Errorf("not an mpls file it must start with 'MPLS' it started with '%s'", str[:4])
}
if str[4:8] != "0200" {
fmt.Fprintf(os.Stderr, "warning: mpls may not work it is version %s\n", str[4:8])
}
mpls.FileType = str[:4]
mpls.Version = str[4:8]
mpls.PlaylistStart, _ = readInt32(reader, buf[:])
mpls.PlaylistMarkStart, _ = readInt32(reader, buf[:])
mpls.ExtensionDataStart, _ = readInt32(reader, buf[:])
_, _ = reader.Seek(20, io.SeekCurrent)
_ = mpls.AppInfoPlaylist.parse(reader)
start, _ = reader.Seek(0, io.SeekCurrent)
if start != int64(mpls.PlaylistStart) {
fmt.Fprintf(os.Stderr, "Playlist doesn't start at the right place. Current position is %d position should be %d\n", start, int64(mpls.PlaylistStart))
}
_, _ = reader.Seek(int64(mpls.PlaylistStart), io.SeekStart)
_ = mpls.Playlist.parse(reader)
start, _ = reader.Seek(0, io.SeekCurrent)
if start != int64(mpls.PlaylistMarkStart) {
fmt.Fprintf(os.Stderr, "Mark Playlist doesn't start at the right place. Current position is %d position should be %d\n", start, int64(mpls.PlaylistStart))
}
// _ = mpls.MarkPlaylist.parse(reader)
return reader.err
}
// parse reads AppInfoPlaylist data from an *errReader
func (aip *AppInfoPlaylist) parse(reader *errReader) error {
var (
buf [10]byte
start int64
end int64
)
aip.Len, _ = readInt32(reader, buf[:])
start, _ = reader.Seek(0, io.SeekCurrent)
_, _ = reader.Read(buf[:2])
aip.PlaybackType = buf[1]
aip.PlaybackCount, _ = readUInt16(reader, buf[:])
aip.UOMask, _ = readUInt64(reader, buf[:])
aip.PlaylistFlags, _ = readUInt16(reader, buf[:])
end, _ = reader.Seek(0, io.SeekCurrent)
if end != (start + int64(aip.Len)) {
fmt.Fprintf(os.Stderr, "App Info Playlist is not aligned. App Info Playlist started at %d current position is %d position should be %d\n", start, end, start+int64(aip.Len))
}
return reader.err
}
// parse reads Playlist data from an *errReader
func (p *Playlist) parse(reader *errReader) error {
var (
buf [10]byte
err error
start int64
end int64
)
p.Len, _ = readInt32(reader, buf[:])
start, _ = reader.Seek(0, io.SeekCurrent)
_, _ = reader.Seek(2, io.SeekCurrent)
p.PlayItemCount, _ = readUInt16(reader, buf[:])
p.SubPathCount, _ = readUInt16(reader, buf[:])
for i := 0; i < int(p.PlayItemCount); i++ {
var item PlayItem
err = item.parse(reader)
if err != nil {
return err
}
p.PlayItems = append(p.PlayItems, item)
}
for i := 0; i < int(p.SubPathCount); i++ {
var item SubPath
err = item.parse(reader)
if err != nil {
return err
}
p.SubPaths = append(p.SubPaths, item)
}
end, _ = reader.Seek(0, io.SeekCurrent)
if end != (start + int64(p.Len)) {
fmt.Fprintf(os.Stderr, "Playlist is not aligned. Playlist started at %d current position is %d position should be %d\n", start, end, start+int64(p.Len))
}
return reader.err
}
// parse reads PlayItem data from an *errReader
func (pi *PlayItem) parse(reader *errReader) error {
var (
buf [10]byte
err error
start int64
end int64
)
pi.Len, _ = readUInt16(reader, buf[:])
start, _ = reader.Seek(0, io.SeekCurrent)
_, _ = reader.Read(buf[:9])
str := string(buf[:9])
if str[5:9] != "M2TS" {
fmt.Fprintf(os.Stderr, "warning: this playlist may be faulty it has a play item that is '%s' not 'M2TS'", str[4:8])
}
pi.Clpi.ClipFile = str[:5]
pi.Clpi.ClipID = str[5:9]
pi.Flags, _ = readUInt16(reader, buf[:])
_, _ = reader.Read(buf[:1])
pi.Clpi.STCID = buf[0]
pi.InTime, _ = readInt32(reader, buf[:])
pi.OutTime, _ = readInt32(reader, buf[:])
pi.UOMask, _ = readUInt64(reader, buf[:])
_, _ = reader.Read(buf[:2])
pi.RandomAccessFlag = buf[0]
pi.StillMode = buf[1]
pi.StillTime, _ = readUInt16(reader, buf[:])
if pi.Flags&1<<3 == 1 {
_, _ = reader.Read(buf[:2])
pi.AngleCount = buf[0]
pi.AngleFlags = buf[1]
for i := 0; i < int(pi.AngleCount); i++ {
var angle CLPI
_ = angle.parse(reader)
_, err = reader.Read(buf[:1])
if err != nil {
return err
}
angle.STCID = buf[0]
pi.Angles = append(pi.Angles, angle)
}
}
_ = pi.StreamTable.parse(reader)
end, _ = reader.Seek(0, io.SeekCurrent)
if end != (start + int64(pi.Len)) {
fmt.Fprintf(os.Stderr, "playitem is not aligned. Playitem started at %d current position is %d position should be %d\n", start, end, start+int64(pi.Len))
}
return reader.err
}
// parse reads angle data from an *errReader
func (clpi *CLPI) parse(reader *errReader) error {
var (
buf [10]byte
)
_, _ = reader.Read(buf[:])
str := string(buf[:9])
clpi.ClipFile = str[:5]
clpi.ClipID = str[5:9]
// clpi.STCID = buf[9]
return reader.err
}
// parse reads PrimaryStream data from an *errReader
func (stnt *STNTable) parse(reader *errReader) error {
var (
buf [10]byte
err error
start int64
end int64
)
stnt.Len, _ = readUInt16(reader, buf[:])
start, _ = reader.Seek(0, io.SeekCurrent)
_, _ = reader.Read(buf[:9])
stnt.PrimaryVideoStreamCount = buf[2]
stnt.PrimaryAudioStreamCount = buf[3]
stnt.PrimaryPGStreamCount = buf[4]
stnt.PrimaryIGStreamCount = buf[5]
stnt.SecondaryAudioStreamCount = buf[6]
stnt.SecondaryVideoStreamCount = buf[7]
stnt.PIPPGStreamCount = buf[8]
_, _ = reader.Seek(5, io.SeekCurrent)
for i := 0; i < int(stnt.PrimaryVideoStreamCount); i++ {
var stream PrimaryStream
err = stream.parse(reader)
if err != nil {
return err
}
stnt.PrimaryVideoStreams = append(stnt.PrimaryVideoStreams, stream)
}
for i := 0; i < int(stnt.PrimaryAudioStreamCount); i++ {
var stream PrimaryStream
err = stream.parse(reader)
if err != nil {
return err
}
stnt.PrimaryAudioStreams = append(stnt.PrimaryAudioStreams, stream)
}
for i := 0; i < int(stnt.PrimaryPGStreamCount); i++ {
var stream PrimaryStream
err = stream.parse(reader)
if err != nil {
return err
}
stnt.PrimaryPGStreams = append(stnt.PrimaryPGStreams, stream)
}
for i := 0; i < int(stnt.PrimaryIGStreamCount); i++ {
var stream PrimaryStream
err = stream.parse(reader)
if err != nil {
return err
}
stnt.PrimaryIGStreams = append(stnt.PrimaryIGStreams, stream)
}
for i := 0; i < int(stnt.SecondaryAudioStreamCount); i++ {
var stream SecondaryAudioStream
err = stream.parse(reader)
if err != nil {
return err
}
stnt.SecondaryAudioStreams = append(stnt.SecondaryAudioStreams, stream)
}
for i := 0; i < int(stnt.SecondaryVideoStreamCount); i++ {
var stream SecondaryVideoStream
err = stream.parse(reader)
if err != nil {
return err
}
stnt.SecondaryVideoStreams = append(stnt.SecondaryVideoStreams, stream)
}
end, _ = reader.Seek(0, io.SeekCurrent)
if end != (start + int64(stnt.Len)) {
fmt.Fprintf(os.Stderr, "STN Table is not aligned. STN Table started at %d current position is %d position should be %d\n", start, end, start+int64(stnt.Len))
}
return reader.err
}
// parse reads SecondaryStream data from an *errReader
func (ss *SecondaryStream) parse(reader *errReader) error {
var (
buf [10]byte
)
_, _ = reader.Read(buf[:2])
ss.RefrenceEntryCount = buf[0]
ss.StreamIDs = make([]byte, ss.RefrenceEntryCount)
_, _ = reader.Read(ss.StreamIDs)
if ss.RefrenceEntryCount%2 != 0 {
_, _ = reader.Seek(1, io.SeekCurrent)
}
return reader.err
}
// parse reads SecondaryAudioStream data from an *errReader
func (sas *SecondaryAudioStream) parse(reader *errReader) error {
_ = sas.PrimaryStream.parse(reader)
_ = sas.ExtraAttributes.parse(reader)
return reader.err
}
// parse reads SecondaryVideoStream data from an *errReader
func (svs *SecondaryVideoStream) parse(reader *errReader) error {
_ = svs.PrimaryStream.parse(reader)
_ = svs.ExtraAttributes.parse(reader)
_ = svs.PGStream.parse(reader)
return reader.err
}
// parse reads Stream data from an *errReader
func (ps *PrimaryStream) parse(reader *errReader) error {
_ = ps.StreamEntry.parse(reader)
_ = ps.StreamAttributes.parse(reader)
return reader.err
}
// parse reads Stream data from an *errReader
func (se *StreamEntry) parse(reader *errReader) error {
var (
buf [10]byte
start int64
end int64
)
_, _ = reader.Read(buf[:1])
se.Len = buf[0]
start, _ = reader.Seek(0, io.SeekCurrent)
_, _ = reader.Read(buf[:9])
se.Type = buf[0]
switch se.Type {
case 1:
se.PID = binary.BigEndian.Uint16(buf[1:3])
case 2, 4:
se.SubPathID = buf[1]
se.SubClipID = buf[2]
se.PID = binary.BigEndian.Uint16(buf[3:5])
case 3:
se.SubPathID = buf[1]
se.PID = binary.BigEndian.Uint16(buf[2:4])
}
end, _ = reader.Seek(0, io.SeekCurrent)
if end != (start + int64(se.Len)) {
fmt.Fprintf(os.Stderr, "Stream Entry is not aligned. Stream Entry started at %d current position is %d position should be %d\n", start, end, start+int64(se.Len))
}
return reader.err
}
// parse reads Stream data from an *errReader
func (sa *StreamAttributes) parse(reader *errReader) error {
var (
buf [10]byte
start int64
end int64
)
_, _ = reader.Read(buf[:1])
sa.Len = buf[0]
start, _ = reader.Seek(0, io.SeekCurrent)
_, _ = reader.Read(buf[:1])
sa.Encoding = buf[0]
switch sa.Encoding {
case VTMPEG1Video, VTMPEG2Video, VTVC1, VTH264:
_, _ = reader.Read(buf[:1])
sa.Format = buf[0] & 0xf0 >> 4
sa.Rate = buf[0] & 0x0F
_, _ = reader.Seek(3, io.SeekCurrent)
case ATMPEG1Audio, ATMPEG2Audio, ATLPCM, ATAC3, ATDTS, ATTRUEHD, ATAC3Plus, ATDTSHD, ATDTSHDMaster:
_, _ = reader.Read(buf[:4])
sa.Format = buf[0] & 0xf0 >> 4
sa.Rate = buf[0] & 0x0F
sa.Language = string(buf[1:4])
case PresentationGraphics, InteractiveGraphics:
_, _ = reader.Read(buf[:3])
sa.Language = string(buf[:3])
_, _ = reader.Seek(1, io.SeekCurrent)
case TextSubtitle:
_, _ = reader.Read(buf[:4])
sa.CharacterCode = buf[0]
sa.Language = string(buf[1:4])
default:
fmt.Fprintf(os.Stderr, "warning: unrecognized encoding: '%02X'\n", sa.Encoding)
}
end, _ = reader.Seek(0, io.SeekCurrent)
if end != (start + int64(sa.Len)) {
fmt.Fprintf(os.Stderr, "Stream Attributes is not aligned. Stream Attributes started at %d current position is %d position should be %d\n", start, end, start+int64(sa.Len))
}
return reader.err
}
func (sp *SubPath) parse(reader *errReader) error {
var (
buf [10]byte
err error
start int64
end int64
)
sp.Len, _ = readInt32(reader, buf[:])
start, _ = reader.Seek(0, io.SeekCurrent)
_, _ = reader.Read(buf[:2])
sp.Type = buf[1]
sp.Flags, _ = readUInt16(reader, buf[:])
_, _ = reader.Read(buf[:2])
sp.PlayItemCount = buf[1]
for i := 0; i < int(sp.PlayItemCount); i++ {
var item SubPlayItem
err = item.parse(reader)
if err != nil {
return err
}
sp.SubPlayItems = append(sp.SubPlayItems, item)
}
end, _ = reader.Seek(0, io.SeekCurrent)
if end != (start + int64(sp.Len)) {
fmt.Fprintf(os.Stderr, "Subpath is not aligned. Subpath started at %d current position is %d position should be %d\n", start, end, start+int64(sp.Len))
}
return reader.err
}
func (spi *SubPlayItem) parse(reader *errReader) error {
var (
buf [10]byte
err error
start int64
end int64
)
spi.Len, _ = readUInt16(reader, buf[:])
start, _ = reader.Seek(0, io.SeekCurrent)
_ = spi.Clpi.parse(reader)
_, _ = reader.Read(buf[:4])
spi.Flags = buf[2]
spi.Clpi.STCID = buf[3]
spi.InTime, _ = readInt32(reader, buf[:])
spi.OutTime, _ = readInt32(reader, buf[:])
spi.PlayItemID, _ = readUInt16(reader, buf[:])
spi.StartOfPlayitem, _ = readUInt32(reader, buf[:])
if spi.Flags&1<<3 == 1 {
_, _ = reader.Read(buf[:2])
spi.AngleCount = buf[0]
spi.AngleFlags = buf[1]
for i := 0; i < int(spi.AngleCount); i++ {
var angle CLPI
_ = angle.parse(reader)
_, err = reader.Read(buf[:1])
if err != nil {
return err
}
angle.STCID = buf[0]
spi.Angles = append(spi.Angles, angle)
}
}
end, _ = reader.Seek(0, io.SeekCurrent)
if end != (start + int64(spi.Len)) {
fmt.Fprintf(os.Stderr, "Subplayitem is not aligned. Subplayitem started at %d current position is %d position should be %d\n", start, end, start+int64(spi.Len))
}
return reader.err
}
func readUInt16(reader io.Reader, buf []byte) (uint16, error) {
n, err := reader.Read(buf[:2])
if err != nil || n != 2 {
return 0, err
}
return binary.BigEndian.Uint16(buf[:2]), nil
}
func readInt32(reader io.Reader, buf []byte) (int, error) {
n, err := readUInt32(reader, buf)
return int(n), err
}
func readUInt32(reader io.Reader, buf []byte) (uint32, error) {
n, err := reader.Read(buf[:4])
if err != nil || n != 4 {
return 0, err
}
return binary.BigEndian.Uint32(buf[:4]), nil
}
func readUInt64(reader io.Reader, buf []byte) (uint64, error) {
n, err := reader.Read(buf[:8])
if err != nil || n != 8 {
return 0, err
}
return binary.BigEndian.Uint64(buf[:8]), nil
}