Compare commits
No commits in common. "5ee60f97c298f7541278762ef4f21df92c8e019f" and "1bbe8260a5edd63306fb588c24c83d7e1f303c2f" have entirely different histories.
5ee60f97c2
...
1bbe8260a5
@ -1,29 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"timmy.narnian.us/mpls"
|
||||
|
||||
"github.com/kr/pretty"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
file io.Reader
|
||||
Mpls mpls.MPLS
|
||||
err error
|
||||
)
|
||||
file, err = os.Open(filepath.Clean(os.Args[1]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Mpls, err = mpls.Parse(file)
|
||||
pretty.Println(Mpls)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"timmy.narnian.us/mpls"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
err error
|
||||
dir *os.File
|
||||
files []string
|
||||
Seconds int64
|
||||
)
|
||||
flag.Int64Var(&Seconds, "s", 120, "Minimum duration of playlist")
|
||||
flag.Int64Var(&Seconds, "seconds", 120, "Minimum duration of playlist")
|
||||
flag.Parse()
|
||||
name := filepath.Join(flag.Arg(0), "BDMV", "PLAYLIST")
|
||||
dir, err = os.Open(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
files, err = dir.Readdirnames(0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range files {
|
||||
var (
|
||||
file *os.File
|
||||
playlist mpls.MPLS
|
||||
duration time.Duration
|
||||
)
|
||||
|
||||
file, err = os.Open(filepath.Join(name, v))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
playlist, err = mpls.Parse(file)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
continue
|
||||
}
|
||||
if playlist.Duration > Seconds {
|
||||
duration = time.Duration(playlist.Duration) * time.Second
|
||||
fmt.Printf("%s %3d:%02d\n", v, int(duration.Minutes()), int(duration.Seconds())%60)
|
||||
|
||||
fmt.Println(strings.Join(playlist.SegmentMap, ","))
|
||||
}
|
||||
}
|
||||
}
|
406
main.go
Normal file
406
main.go
Normal file
@ -0,0 +1,406 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// User Operation mask table
|
||||
const (
|
||||
ChapterSearchMask = 1 << iota
|
||||
TimeSearchMask
|
||||
SkipToNextPointMask
|
||||
SkipBackToPreviousPointMask
|
||||
ForwardPlayMask
|
||||
BackwardPlayMask
|
||||
PlayMask
|
||||
StopMask
|
||||
PauseOnMask
|
||||
PauseOffMask
|
||||
StillOffMask
|
||||
ResumeMask
|
||||
MoveUpSelectedButtonMask
|
||||
MoveDownSelectedButtonMask
|
||||
MoveLeftSelectedButtonMask
|
||||
MoveRightSelectedButtonMask
|
||||
SelectButtonMask
|
||||
ActivateAndActivateMask
|
||||
SelectAndActivateMask
|
||||
AudioChangeMask
|
||||
PgTextstChangeMask
|
||||
AngleChangeMask
|
||||
PopupOnMask
|
||||
PopupOffMask
|
||||
SelectMenuLanguageMask
|
||||
)
|
||||
|
||||
// Playlist Flags
|
||||
const (
|
||||
PlaylistRandomAccess = 1 << iota
|
||||
AudioMixApp
|
||||
LosslessMayBypassMixer
|
||||
reserved
|
||||
)
|
||||
|
||||
// Angle Flags
|
||||
const (
|
||||
IsDifferentAudios = 1 << (iota + 7)
|
||||
IsSeamlessAngleChange
|
||||
)
|
||||
|
||||
// MPLS is a struct representing an MPLS file
|
||||
type MPLS struct {
|
||||
Header string
|
||||
playlistStart int
|
||||
playlistMarkStart int
|
||||
extensionDataStart int
|
||||
AppInfoPlaylist AppInfoPlaylist
|
||||
Playlist Playlist
|
||||
}
|
||||
|
||||
// AppInfoPlaylist sucks
|
||||
type AppInfoPlaylist struct {
|
||||
Len int
|
||||
PlaybackType int
|
||||
PlaybackCount int
|
||||
UOMask uint64
|
||||
PlaylistFlags uint16
|
||||
}
|
||||
|
||||
// Playlist sucks
|
||||
type Playlist struct {
|
||||
len int
|
||||
playItemCount uint16
|
||||
subPathCount uint16
|
||||
playItems []PlayItem
|
||||
}
|
||||
|
||||
// PlayItem contains information about a an item in the playlist
|
||||
type PlayItem struct {
|
||||
len uint16
|
||||
clpi CLPI
|
||||
flags uint16 // multiangle/connection condition
|
||||
STCID byte
|
||||
inTime int
|
||||
outTime int
|
||||
UOMask uint64
|
||||
RandomAccessFlag byte
|
||||
stillMode byte
|
||||
stillTime uint16
|
||||
angleCount byte
|
||||
angleFlags byte
|
||||
angles []CLPI
|
||||
}
|
||||
|
||||
// CLPI contains the filename and the codec ID
|
||||
type CLPI struct {
|
||||
ClipFile string
|
||||
ClipID string // M2TS
|
||||
STCID byte
|
||||
}
|
||||
|
||||
func main() {
|
||||
Mpls, err := Parse(os.Args[1])
|
||||
fmt.Println(Mpls)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Parse parses an MPLS file into an MPLS struct
|
||||
func Parse(filename string) (Mpls MPLS, err error) {
|
||||
var (
|
||||
file *bytes.Reader
|
||||
f []byte
|
||||
)
|
||||
|
||||
f, err = ioutil.ReadFile(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return MPLS{}, err
|
||||
}
|
||||
file = bytes.NewReader(f)
|
||||
err = Mpls.Parse(file)
|
||||
return Mpls, err
|
||||
}
|
||||
|
||||
// Parse reads MPLS data from an io.ReadSeeker
|
||||
func (Mpls *MPLS) Parse(file io.ReadSeeker) error {
|
||||
var (
|
||||
buf [10]byte
|
||||
n int
|
||||
err error
|
||||
)
|
||||
|
||||
n, err = file.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.Header = str
|
||||
|
||||
Mpls.playlistStart, err = readInt32(file, buf[:4])
|
||||
fmt.Println("int:", Mpls.playlistStart, "binary:", buf[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Mpls.playlistMarkStart, err = readInt32(file, buf[:4])
|
||||
fmt.Println("int:", Mpls.playlistMarkStart, "binary:", buf[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Mpls.extensionDataStart, err = readInt32(file, buf[:4])
|
||||
fmt.Println("int:", Mpls.extensionDataStart, "binary:", buf[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = file.Seek(20, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = Mpls.AppInfoPlaylist.parse(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = file.Seek(int64(Mpls.playlistStart), io.SeekStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = Mpls.Playlist.Parse(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse reads AppInfoPlaylist data from an io.ReadSeeker
|
||||
func (aip *AppInfoPlaylist) parse(file io.ReadSeeker) error {
|
||||
var (
|
||||
buf [10]byte
|
||||
err error
|
||||
n int
|
||||
)
|
||||
aip.Len, err = readInt32(file, buf[:4])
|
||||
fmt.Println("int:", aip.Len, "binary:", buf[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, err = file.Read(buf[:4])
|
||||
if err != nil || n != 4 {
|
||||
return err
|
||||
}
|
||||
aip.PlaybackType = int(buf[1])
|
||||
fmt.Println("int:", aip.PlaybackType, "binary:", buf[1])
|
||||
|
||||
aip.PlaybackCount = int(binary.BigEndian.Uint16(buf[2:4]))
|
||||
fmt.Println("int:", aip.PlaybackCount, "binary:", buf[2:4])
|
||||
|
||||
aip.UOMask, err = readUInt64(file, buf[:8])
|
||||
fmt.Println("int:", aip.UOMask, "binary:", buf[:8])
|
||||
if err != nil || n != 1 {
|
||||
return err
|
||||
}
|
||||
aip.PlaylistFlags, err = readUInt16(file, buf[:2])
|
||||
fmt.Println("int:", aip.PlaylistFlags, "binary:", buf[:2])
|
||||
if err != nil || n != 1 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse reads Playlist data from an io.ReadSeeker
|
||||
func (p *Playlist) Parse(file io.ReadSeeker) error {
|
||||
var (
|
||||
buf [10]byte
|
||||
err error
|
||||
)
|
||||
|
||||
p.len, err = readInt32(file, buf[:])
|
||||
fmt.Println("int:", p.len, "binary:", buf[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = file.Seek(2, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.playItemCount, err = readUInt16(file, buf[:])
|
||||
fmt.Println("int:", p.playItemCount, "binary:", buf[:2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.subPathCount, err = readUInt16(file, buf[:])
|
||||
fmt.Println("int:", p.subPathCount, "binary:", buf[:2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < int(p.playItemCount); i++ {
|
||||
var item PlayItem
|
||||
err = item.Parse(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.playItems = append(p.playItems, item)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse reads PlayItem data from an io.ReadSeeker
|
||||
func (pi *PlayItem) Parse(file io.Reader) error {
|
||||
var (
|
||||
buf [10]byte
|
||||
n int
|
||||
err error
|
||||
)
|
||||
|
||||
pi.len, err = readUInt16(file, buf[:])
|
||||
fmt.Println("int:", pi.len, "binary:", buf[:2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err = file.Read(buf[:9])
|
||||
if err != nil || n != 9 {
|
||||
return err
|
||||
}
|
||||
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, err = readUInt16(file, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err = file.Read(buf[:1])
|
||||
if err != nil || n != 1 {
|
||||
return err
|
||||
}
|
||||
pi.STCID = buf[0]
|
||||
|
||||
pi.inTime, err = readInt32(file, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pi.outTime, err = readInt32(file, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pi.UOMask, err = readUInt64(file, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, err = file.Read(buf[:1])
|
||||
if err != nil || n != 1 {
|
||||
return err
|
||||
}
|
||||
pi.RandomAccessFlag = buf[0]
|
||||
|
||||
n, err = file.Read(buf[:1])
|
||||
if err != nil || n != 1 {
|
||||
return err
|
||||
}
|
||||
pi.stillMode = buf[0]
|
||||
|
||||
pi.stillTime, err = readUInt16(file, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pi.flags&1 == 1 {
|
||||
n, err = file.Read(buf[:1])
|
||||
if err != nil || n != 1 {
|
||||
return err
|
||||
}
|
||||
pi.angleCount = buf[0]
|
||||
|
||||
n, err = file.Read(buf[:1])
|
||||
if err != nil || n != 1 {
|
||||
return err
|
||||
}
|
||||
pi.angleFlags = buf[0]
|
||||
|
||||
for i := 0; i < int(pi.angleCount); i++ {
|
||||
var angle CLPI
|
||||
err = angle.Parse(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pi.angles = append(pi.angles, angle)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse reads angle data from an io.ReadSeeker
|
||||
func (clpi *CLPI) Parse(file io.Reader) error {
|
||||
var (
|
||||
buf [10]byte
|
||||
n int
|
||||
err error
|
||||
)
|
||||
n, err = file.Read(buf[:9])
|
||||
if err != nil || n != 9 {
|
||||
return err
|
||||
}
|
||||
str := string(buf[:9])
|
||||
clpi.ClipFile = str[:5]
|
||||
clpi.ClipID = str[5:9]
|
||||
|
||||
n, err = file.Read(buf[:1])
|
||||
if err != nil || n != 1 {
|
||||
return err
|
||||
}
|
||||
clpi.STCID = buf[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func readUInt16(file io.Reader, buf []byte) (uint16, error) {
|
||||
n, err := file.Read(buf[:2])
|
||||
if err != nil || n != 2 {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint16(buf[:2]), nil
|
||||
}
|
||||
|
||||
func readInt32(file io.Reader, buf []byte) (int, error) {
|
||||
n, err := readUInt32(file, buf)
|
||||
return int(n), err
|
||||
}
|
||||
|
||||
func readUInt32(file io.Reader, buf []byte) (uint32, error) {
|
||||
n, err := file.Read(buf[:4])
|
||||
if err != nil || n != 4 {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint32(buf[:4]), nil
|
||||
}
|
||||
|
||||
func readUInt64(file io.Reader, buf []byte) (uint64, error) {
|
||||
n, err := file.Read(buf[:8])
|
||||
if err != nil || n != 8 {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint64(buf[:8]), nil
|
||||
}
|
293
mpls.go
293
mpls.go
@ -1,293 +0,0 @@
|
||||
package mpls
|
||||
|
||||
// User Operation mask table
|
||||
const (
|
||||
UOChapterSearchMask = 1 << iota
|
||||
UOTimeSearchMask
|
||||
UOSkipToNextPointMask
|
||||
UOSkipBackToPreviousPointMask
|
||||
UOForwardPlayMask
|
||||
UOBackwardPlayMask
|
||||
UOPlayMask
|
||||
UOStopMask
|
||||
UOPauseOnMask
|
||||
UOPauseOffMask
|
||||
UOStillOffMask
|
||||
UOResumeMask
|
||||
UOMoveUpSelectedButtonMask
|
||||
UOMoveDownSelectedButtonMask
|
||||
UOMoveLeftSelectedButtonMask
|
||||
UOMoveRightSelectedButtonMask
|
||||
UOSelectButtonMask
|
||||
UOActivateAndActivateMask
|
||||
UOSelectAndActivateMask
|
||||
UOAudioChangeMask
|
||||
UOPgTextstChangeMask
|
||||
UOAngleChangeMask
|
||||
UOPopupOnMask
|
||||
UOPopupOffMask
|
||||
UOSelectMenuLanguageMask
|
||||
)
|
||||
|
||||
// Playlist Flags
|
||||
const (
|
||||
PFPlaylistRandomAccess = 1 << iota
|
||||
PFAudioMixApp
|
||||
PFLosslessMayBypassMixer
|
||||
PFreserved
|
||||
)
|
||||
|
||||
// Angle Flags
|
||||
const (
|
||||
AFIsDifferentAudios = 1 << (iota + 7)
|
||||
AFIsSeamlessAngleChange
|
||||
)
|
||||
|
||||
// VideoType
|
||||
const (
|
||||
VTMPEG1Video = 0x01
|
||||
VTMPEG2Video = 0x02
|
||||
VTVC1 = 0xea
|
||||
VTH264 = 0x1b
|
||||
)
|
||||
|
||||
// AudioType
|
||||
const (
|
||||
ATMPEG1Audio = 0x03
|
||||
ATMPEG2Audio = 0x04
|
||||
ATLPCM = 0x80
|
||||
ATAC3 = 0x81
|
||||
ATDTS = 0x82
|
||||
ATTRUEHD = 0x83
|
||||
ATAC3Plus = 0x84
|
||||
ATDTSHD = 0x85
|
||||
ATDTSHDMaster = 0x86
|
||||
)
|
||||
|
||||
// OtherType
|
||||
const (
|
||||
PresentationGraphics = 0x90
|
||||
InteractiveGraphics = 0x91
|
||||
TextSubtitle = 0x92
|
||||
)
|
||||
|
||||
// VideoFormat
|
||||
const (
|
||||
VFReserved = iota
|
||||
VF480I
|
||||
VF576I
|
||||
VF480P
|
||||
VF1080I
|
||||
VF720P
|
||||
VF1080P
|
||||
VF576P
|
||||
)
|
||||
|
||||
// FrameRate
|
||||
const (
|
||||
FRReserved = iota
|
||||
FR23976 // 23.976
|
||||
FR24 // 24
|
||||
FR25 // 25
|
||||
FR2997 // 29.97
|
||||
FR50 // 50
|
||||
FR5994 // 59.94
|
||||
)
|
||||
|
||||
// AspectRatio
|
||||
const (
|
||||
ARReserved = 0
|
||||
AR43 = 2 //4:3
|
||||
AR169 = 3 //16:9
|
||||
)
|
||||
|
||||
// AudioPresentation
|
||||
const (
|
||||
APReserved = 0
|
||||
APMono = 1
|
||||
APDualMono = 2
|
||||
APStereo = 3
|
||||
APMulti = 6
|
||||
APCombo = 12
|
||||
)
|
||||
|
||||
// SampleRate
|
||||
const (
|
||||
SRReserved = 0
|
||||
SR48 = 1
|
||||
SR96 = 4
|
||||
SR192 = 5
|
||||
SR48192 = 12 // 48/192
|
||||
SR4896 = 14 // 48/96
|
||||
)
|
||||
|
||||
// CharacterCode
|
||||
const (
|
||||
ReservedCharacterCode = iota
|
||||
UTF8
|
||||
UTF16
|
||||
ShiftJIS // Japanese
|
||||
KSC5601 // Korean
|
||||
GB18030 // Chinese
|
||||
GB2312 // Chinese
|
||||
BIG5 // Chinese
|
||||
) // Chinese
|
||||
|
||||
// MPLS is a struct representing an MPLS file
|
||||
type MPLS struct {
|
||||
FileType string
|
||||
Version string
|
||||
PlaylistStart int
|
||||
PlaylistMarkStart int
|
||||
ExtensionDataStart int
|
||||
AppInfoPlaylist AppInfoPlaylist
|
||||
Playlist Playlist
|
||||
MarkPlaylist PlaylistMark
|
||||
SegmentMap []string
|
||||
Duration int64
|
||||
}
|
||||
|
||||
// AppInfoPlaylist sucks
|
||||
type AppInfoPlaylist struct {
|
||||
Len int
|
||||
PlaybackType byte
|
||||
PlaybackCount uint16
|
||||
PlaylistFlags uint16
|
||||
UOMask uint64
|
||||
}
|
||||
|
||||
type Playlist struct {
|
||||
Len int
|
||||
PlayItemCount uint16
|
||||
SubPathCount uint16
|
||||
PlayItems []PlayItem
|
||||
SubPaths []SubPath
|
||||
}
|
||||
|
||||
// PlayItem contains information about a an item in the playlist
|
||||
type PlayItem struct {
|
||||
Len uint16
|
||||
Flags uint16 // multiangle/connection condition
|
||||
InTime int
|
||||
OutTime int
|
||||
UOMask uint64
|
||||
RandomAccessFlag byte
|
||||
AngleCount byte
|
||||
AngleFlags byte
|
||||
StillMode byte
|
||||
StillTime uint16
|
||||
Clpi CLPI
|
||||
Angles []CLPI
|
||||
StreamTable STNTable
|
||||
}
|
||||
|
||||
// STNTable STream Number Table
|
||||
type STNTable struct {
|
||||
Len uint16 // Reserved uint16
|
||||
PrimaryVideoStreamCount byte
|
||||
PrimaryAudioStreamCount byte
|
||||
PrimaryPGStreamCount byte
|
||||
PrimaryIGStreamCount byte
|
||||
SecondaryVideoStreamCount byte
|
||||
SecondaryAudioStreamCount byte
|
||||
PIPPGStreamCount byte
|
||||
PrimaryVideoStreams []PrimaryStream
|
||||
PrimaryAudioStreams []PrimaryStream
|
||||
PrimaryPGStreams []PrimaryStream
|
||||
PrimaryIGStreams []PrimaryStream
|
||||
SecondaryAudioStreams []SecondaryAudioStream
|
||||
SecondaryVideoStreams []SecondaryVideoStream
|
||||
}
|
||||
|
||||
// PrimaryStream holds a stream entry and attributes
|
||||
type PrimaryStream struct {
|
||||
StreamEntry
|
||||
StreamAttributes
|
||||
}
|
||||
|
||||
// SecondaryStream holds stream references
|
||||
type SecondaryStream struct {
|
||||
RefrenceEntryCount byte
|
||||
StreamIDs []byte
|
||||
}
|
||||
|
||||
// SecondaryAudioStream holds a primary stream and a secondary stream
|
||||
type SecondaryAudioStream struct {
|
||||
PrimaryStream
|
||||
ExtraAttributes SecondaryStream
|
||||
}
|
||||
|
||||
// SecondaryVideoStream holds a primary stream and a secondary stream for the video
|
||||
// and a secondary stream for the Presentation Graphics/pip
|
||||
type SecondaryVideoStream struct {
|
||||
PrimaryStream
|
||||
ExtraAttributes SecondaryStream
|
||||
PGStream SecondaryStream
|
||||
}
|
||||
|
||||
// StreamEntry holds the information for the data stream
|
||||
type StreamEntry struct {
|
||||
Len byte
|
||||
Type byte
|
||||
PID uint16
|
||||
SubPathID byte
|
||||
SubClipID byte
|
||||
}
|
||||
|
||||
// StreamAttributes holds metadata about the data stream
|
||||
type StreamAttributes struct {
|
||||
Len byte
|
||||
Encoding byte
|
||||
Format byte
|
||||
Rate byte
|
||||
CharacterCode byte
|
||||
Language string
|
||||
}
|
||||
|
||||
// CLPI contains the fiLename and the codec ID
|
||||
type CLPI struct {
|
||||
ClipFile string
|
||||
ClipID string // M2TS
|
||||
STCID byte
|
||||
}
|
||||
|
||||
type SubPath struct {
|
||||
Len int
|
||||
Type byte
|
||||
PlayItemCount byte
|
||||
Flags uint16
|
||||
SubPlayItems []SubPlayItem
|
||||
}
|
||||
|
||||
// SubPlayItem contains information about a PlayItem in the subpath
|
||||
type SubPlayItem struct {
|
||||
Len uint16
|
||||
Flags byte // multiangle/connection condition
|
||||
StartOfPlayitem uint32
|
||||
InTime int
|
||||
OutTime int
|
||||
UOMask uint64
|
||||
RandomAccessFlag byte
|
||||
AngleCount byte
|
||||
AngleFlags byte
|
||||
StillMode byte
|
||||
StillTime uint16
|
||||
PlayItemID uint16
|
||||
Clpi CLPI
|
||||
Angles []CLPI
|
||||
StreamTable STNTable
|
||||
}
|
||||
|
||||
type PlaylistMark struct {
|
||||
Len uint64
|
||||
MarkCount uint16
|
||||
Marks []Mark
|
||||
}
|
||||
|
||||
type Mark struct {
|
||||
Type byte
|
||||
PlayItemRef uint16
|
||||
Time uint32
|
||||
PID uint16
|
||||
Duration uint32
|
||||
}
|
614
parse.go
614
parse.go
@ -1,614 +0,0 @@
|
||||
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)
|
||||
mpls.SegmentMap = make([]string, 0, len(mpls.Playlist.PlayItems))
|
||||
for _, playitem := range mpls.Playlist.PlayItems {
|
||||
mpls.SegmentMap = append(mpls.SegmentMap, playitem.Clpi.ClipFile)
|
||||
mpls.Duration += int64(playitem.OutTime - playitem.InTime)
|
||||
}
|
||||
mpls.Duration = mpls.Duration / 4500
|
||||
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
|
||||
}
|
BIN
testFiles/timmy.narnian.us/00000.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00000.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00002.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00002.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00003.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00003.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00005.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00005.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00008.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00008.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00020.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00020.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00021.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00021.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00050.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00050.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00051.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00051.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00100.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00100.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00101.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00101.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00110.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00110.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00120.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00120.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00201.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00201.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00202.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00202.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00203.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00203.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00204.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00204.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00205.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00205.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00206.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00206.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00207.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00207.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00208.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00208.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00209.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00209.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00210.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00210.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00211.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00211.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00212.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00212.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00213.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00213.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00214.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00214.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00215.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00215.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00900.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00900.mpls
Normal file
Binary file not shown.
BIN
testFiles/timmy.narnian.us/00901.mpls
Normal file
BIN
testFiles/timmy.narnian.us/00901.mpls
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user