[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# PLEASE USE joy5 INSTEAD\n\n[joy5](https://github.com/nareix/joy5)\n\n- High performance Copy-on-write gop cache [code](https://github.com/nareix/joy5/blob/master/cmd/avtool/pubsub.go)\n- Better av.Packet design [code](https://github.com/nareix/joy5/blob/master/av/av.go)\n\n# JOY4\n\n> Golang audio/video library and streaming server\n\nJOY4 is powerful library written in golang, well-designed interface makes a few lines of code can do a lot of things such as reading, writing, transcoding among variety media formats, or setting up high-performance live streaming server.\n\n# Features \n\nWell-designed and easy-to-use interfaces:\n\n- Muxer / Demuxer ([doc](https://godoc.org/github.com/nareix/joy4/av#Demuxer) [example](https://github.com/nareix/joy4/blob/master/examples/open_probe_file/main.go))\n- Audio Decoder ([doc](https://godoc.org/github.com/nareix/joy4/av#AudioDecoder) [example](https://github.com/nareix/joy4/blob/master/examples/audio_decode/main.go))\n- Transcoding ([doc](https://godoc.org/github.com/nareix/joy4/av/transcode) [example](https://github.com/nareix/joy4/blob/master/examples/transcode/main.go))\n- Streaming server ([example](https://github.com/nareix/joy4/blob/master/examples/http_flv_and_rtmp_server/main.go))\n\nSupport container formats:\n\n- MP4\n- MPEG-TS\n- FLV\n- AAC (ADTS)\n\nRTSP Client\n- High level camera bug tolerance\n- Support STAP-A\n\nRTMP Client\n- Support publishing to nginx-rtmp-server\n- Support playing\n\nRTMP / HTTP-FLV Server \n- Support publishing clients: OBS / ffmpeg / Flash Player (>8)\n- Support playing clients: Flash Player 11 / VLC / ffplay / mpv\n- High performance\n\n\nPublisher-subscriber packet buffer queue ([doc](https://godoc.org/github.com/nareix/joy4/av/pubsub))\n\n- Customize publisher buffer time and subscriber read position\n\n\n- Multiple channels live streaming ([example](https://github.com/nareix/joy4/blob/master/examples/rtmp_server_channels/main.go))\n\nPacket filters ([doc](https://godoc.org/github.com/nareix/joy4/av/pktque))\n\n- Wait first keyframe\n- Fix timestamp\n- Make A/V sync\n- Customize ([example](https://github.com/nareix/joy4/blob/master/examples/rtmp_server_channels/main.go#L19))\n\nFFMPEG Golang-style binding ([doc](https://godoc.org/github.com/nareix/joy4/cgo/ffmpeg))\n- Audio Encoder / Decoder\n- Video Decoder\n- Audio Resampler\n\nSupport codec and container parsers:\n\n- H264 SPS/PPS/AVCDecoderConfigure parser ([doc](https://godoc.org/github.com/nareix/joy4/codec/h264parser))\n- AAC ADTSHeader/MPEG4AudioConfig parser ([doc](https://godoc.org/github.com/nareix/joy4/codec/aacparser))\n- MP4 Atoms parser ([doc](https://godoc.org/github.com/nareix/joy4/format/mp4/mp4io))\n- FLV AMF0 object parser ([doc](https://godoc.org/github.com/nareix/joy4/format/flv/flvio))\n\n# Requirements\n\nGo version >= 1.6\n\nffmpeg version >= 3.0 (optional)\n\n# TODO\n\nHLS / MPEG-DASH Server\n\nffmpeg.VideoEncoder / ffmpeg.SWScale\n\n# License\n\nMIT\n"
  },
  {
    "path": "av/av.go",
    "content": "\n// Package av defines basic interfaces and data structures of container demux/mux and audio encode/decode.\npackage av\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\n// Audio sample format.\ntype SampleFormat uint8\n\nconst (\n\tU8 = SampleFormat(iota + 1) // 8-bit unsigned integer\n\tS16 // signed 16-bit integer\n\tS32 // signed 32-bit integer\n\tFLT // 32-bit float\n\tDBL // 64-bit float\n\tU8P // 8-bit unsigned integer in planar\n\tS16P // signed 16-bit integer in planar\n\tS32P // signed 32-bit integer in planar\n\tFLTP // 32-bit float in planar\n\tDBLP // 64-bit float in planar\n\tU32 // unsigned 32-bit integer\n)\n\nfunc (self SampleFormat) BytesPerSample() int {\n\tswitch self {\n\tcase U8, U8P:\n\t\treturn 1\n\tcase S16, S16P:\n\t\treturn 2\n\tcase FLT, FLTP, S32, S32P, U32:\n\t\treturn 4\n\tcase DBL, DBLP:\n\t\treturn 8\n\tdefault:\n\t\treturn 0\n\t}\n}\n\nfunc (self SampleFormat) String() string {\n\tswitch self {\n\tcase U8:\n\t\treturn \"U8\"\n\tcase S16:\n\t\treturn \"S16\"\n\tcase S32:\n\t\treturn \"S32\"\n\tcase FLT:\n\t\treturn \"FLT\"\n\tcase DBL:\n\t\treturn \"DBL\"\n\tcase U8P:\n\t\treturn \"U8P\"\n\tcase S16P:\n\t\treturn \"S16P\"\n\tcase FLTP:\n\t\treturn \"FLTP\"\n\tcase DBLP:\n\t\treturn \"DBLP\"\n\tcase U32:\n\t\treturn \"U32\"\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\n// Check if this sample format is in planar.\nfunc (self SampleFormat) IsPlanar() bool {\n\tswitch self {\n\tcase S16P, S32P, FLTP, DBLP:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// Audio channel layout.\ntype ChannelLayout uint16\n\nfunc (self ChannelLayout) String() string {\n\treturn fmt.Sprintf(\"%dch\", self.Count())\n}\n\nconst (\n\tCH_FRONT_CENTER = ChannelLayout(1 << iota)\n\tCH_FRONT_LEFT\n\tCH_FRONT_RIGHT\n\tCH_BACK_CENTER\n\tCH_BACK_LEFT\n\tCH_BACK_RIGHT\n\tCH_SIDE_LEFT\n\tCH_SIDE_RIGHT\n\tCH_LOW_FREQ\n\tCH_NR\n\n\tCH_MONO     = ChannelLayout(CH_FRONT_CENTER)\n\tCH_STEREO   = ChannelLayout(CH_FRONT_LEFT | CH_FRONT_RIGHT)\n\tCH_2_1      = ChannelLayout(CH_STEREO | CH_BACK_CENTER)\n\tCH_2POINT1  = ChannelLayout(CH_STEREO | CH_LOW_FREQ)\n\tCH_SURROUND = ChannelLayout(CH_STEREO | CH_FRONT_CENTER)\n\tCH_3POINT1  = ChannelLayout(CH_SURROUND | CH_LOW_FREQ)\n\t// TODO: add all channel_layout in ffmpeg\n)\n\nfunc (self ChannelLayout) Count() (n int) {\n\tfor self != 0 {\n\t\tn++\n\t\tself = (self - 1) & self\n\t}\n\treturn\n}\n\n// Video/Audio codec type. can be H264/AAC/SPEEX/...\ntype CodecType uint32\n\nvar (\n\tH264 = MakeVideoCodecType(avCodecTypeMagic + 1)\n\tAAC       = MakeAudioCodecType(avCodecTypeMagic + 1)\n\tPCM_MULAW = MakeAudioCodecType(avCodecTypeMagic + 2)\n\tPCM_ALAW  = MakeAudioCodecType(avCodecTypeMagic + 3)\n\tSPEEX = MakeAudioCodecType(avCodecTypeMagic + 4)\n\tNELLYMOSER = MakeAudioCodecType(avCodecTypeMagic + 5)\n)\n\nconst codecTypeAudioBit = 0x1\nconst codecTypeOtherBits = 1\n\nfunc (self CodecType) String() string {\n\tswitch self {\n\tcase H264:\n\t\treturn \"H264\"\n\tcase AAC:\n\t\treturn \"AAC\"\n\tcase PCM_MULAW:\n\t\treturn \"PCM_MULAW\"\n\tcase PCM_ALAW:\n\t\treturn \"PCM_ALAW\"\n\tcase SPEEX:\n\t\treturn \"SPEEX\"\n\tcase NELLYMOSER:\n\t\treturn \"NELLYMOSER\"\n\t}\n\treturn \"\"\n}\n\nfunc (self CodecType) IsAudio() bool {\n\treturn self&codecTypeAudioBit != 0\n}\n\nfunc (self CodecType) IsVideo() bool {\n\treturn self&codecTypeAudioBit == 0\n}\n\n// Make a new audio codec type.\nfunc MakeAudioCodecType(base uint32) (c CodecType) {\n\tc = CodecType(base)<<codecTypeOtherBits | CodecType(codecTypeAudioBit)\n\treturn\n}\n\n// Make a new video codec type.\nfunc MakeVideoCodecType(base uint32) (c CodecType) {\n\tc = CodecType(base) << codecTypeOtherBits\n\treturn\n}\n\nconst avCodecTypeMagic = 233333\n\n// CodecData is some important bytes for initializing audio/video decoder,\n// can be converted to VideoCodecData or AudioCodecData using:\n//\n//     codecdata.(AudioCodecData) or codecdata.(VideoCodecData)\n// \n// for H264, CodecData is AVCDecoderConfigure bytes, includes SPS/PPS.\ntype CodecData interface {\n\tType() CodecType // Video/Audio codec type\n}\n\ntype VideoCodecData interface {\n\tCodecData\n\tWidth() int // Video width\n\tHeight() int // Video height\n}\n\ntype AudioCodecData interface {\n\tCodecData\n\tSampleFormat() SampleFormat // audio sample format\n\tSampleRate() int // audio sample rate\n\tChannelLayout() ChannelLayout // audio channel layout\n\tPacketDuration([]byte) (time.Duration, error) // get audio compressed packet duration\n}\n\ntype PacketWriter interface {\n\tWritePacket(Packet) error\n}\n\ntype PacketReader interface {\n\tReadPacket() (Packet,error)\n}\n\n// Muxer describes the steps of writing compressed audio/video packets into container formats like MP4/FLV/MPEG-TS.\n// \n// Container formats, rtmp.Conn, and transcode.Muxer implements Muxer interface.\ntype Muxer interface {\n\tWriteHeader([]CodecData) error // write the file header\n\tPacketWriter // write compressed audio/video packets\n\tWriteTrailer() error // finish writing file, this func can be called only once\n}\n\n// Muxer with Close() method\ntype MuxCloser interface {\n\tMuxer\n\tClose() error\n}\n\n// Demuxer can read compressed audio/video packets from container formats like MP4/FLV/MPEG-TS.\ntype Demuxer interface {\n\tPacketReader // read compressed audio/video packets\n\tStreams() ([]CodecData, error) // reads the file header, contains video/audio meta infomations\n}\n\n// Demuxer with Close() method\ntype DemuxCloser interface {\n\tDemuxer\n\tClose() error\n}\n\n// Packet stores compressed audio/video data.\ntype Packet struct {\n\tIsKeyFrame      bool // video packet is key frame\n\tIdx             int8 // stream index in container format\n\tCompositionTime time.Duration // packet presentation time minus decode time for H264 B-Frame\n\tTime time.Duration // packet decode time\n\tData            []byte // packet data\n}\n\n// Raw audio frame.\ntype AudioFrame struct {\n\tSampleFormat  SampleFormat // audio sample format, e.g: S16,FLTP,...\n\tChannelLayout ChannelLayout // audio channel layout, e.g: CH_MONO,CH_STEREO,...\n\tSampleCount   int // sample count in this frame\n\tSampleRate    int // sample rate\n\tData          [][]byte // data array for planar format len(Data) > 1\n}\n\nfunc (self AudioFrame) Duration() time.Duration {\n\treturn time.Second * time.Duration(self.SampleCount) / time.Duration(self.SampleRate)\n}\n\n// Check this audio frame has same format as other audio frame.\nfunc (self AudioFrame) HasSameFormat(other AudioFrame) bool {\n\tif self.SampleRate != other.SampleRate {\n\t\treturn false\n\t}\n\tif self.ChannelLayout != other.ChannelLayout {\n\t\treturn false\n\t}\n\tif self.SampleFormat != other.SampleFormat {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Split sample audio sample from this frame.\nfunc (self AudioFrame) Slice(start int, end int) (out AudioFrame) {\n\tif start > end {\n\t\tpanic(fmt.Sprintf(\"av: AudioFrame split failed start=%d end=%d invalid\", start, end))\n\t}\n\tout = self\n\tout.Data = append([][]byte(nil), out.Data...)\n\tout.SampleCount = end - start\n\tsize := self.SampleFormat.BytesPerSample()\n\tfor i := range out.Data {\n\t\tout.Data[i] = out.Data[i][start*size : end*size]\n\t}\n\treturn\n}\n\n// Concat two audio frames.\nfunc (self AudioFrame) Concat(in AudioFrame) (out AudioFrame) {\n\tout = self\n\tout.Data = append([][]byte(nil), out.Data...)\n\tout.SampleCount += in.SampleCount\n\tfor i := range out.Data {\n\t\tout.Data[i] = append(out.Data[i], in.Data[i]...)\n\t}\n\treturn\n}\n\n// AudioEncoder can encode raw audio frame into compressed audio packets.\n// cgo/ffmpeg inplements AudioEncoder, using ffmpeg.NewAudioEncoder to create it.\ntype AudioEncoder interface {\n\tCodecData() (AudioCodecData, error) // encoder's codec data can put into container\n\tEncode(AudioFrame) ([][]byte, error) // encode raw audio frame into compressed pakcet(s)\n\tClose() // close encoder, free cgo contexts\n\tSetSampleRate(int) (error) // set encoder sample rate\n\tSetChannelLayout(ChannelLayout) (error) // set encoder channel layout\n\tSetSampleFormat(SampleFormat) (error) // set encoder sample format\n\tSetBitrate(int) (error) // set encoder bitrate\n\tSetOption(string,interface{}) (error) // encoder setopt, in ffmpeg is av_opt_set_dict()\n\tGetOption(string,interface{}) (error) // encoder getopt\n}\n\n// AudioDecoder can decode compressed audio packets into raw audio frame.\n// use ffmpeg.NewAudioDecoder to create it.\ntype AudioDecoder interface {\n\tDecode([]byte) (bool, AudioFrame, error) // decode one compressed audio packet\n\tClose() // close decode, free cgo contexts\n}\n\n// AudioResampler can convert raw audio frames in different sample rate/format/channel layout.\ntype AudioResampler interface {\n\tResample(AudioFrame) (AudioFrame, error) // convert raw audio frames\n}\n\n"
  },
  {
    "path": "av/avconv/avconv.go",
    "content": "package avconv\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/pktque\"\n\t\"github.com/nareix/joy4/av/transcode\"\n)\n\nvar Debug bool\n\ntype Option struct {\n\tTranscode bool\n\tArgs []string\n}\n\ntype Options struct {\n\tOutputCodecTypes []av.CodecType\n}\n\ntype Demuxer struct {\n\ttransdemux *transcode.Demuxer\n\tstreams []av.CodecData\n\tOptions\n\tDemuxer av.Demuxer\n}\n\nfunc (self *Demuxer) Close() (err error) {\n\tif self.transdemux != nil {\n\t\treturn self.transdemux.Close()\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) Streams() (streams []av.CodecData, err error) {\n\tif err = self.prepare(); err != nil {\n\t\treturn\n\t}\n\tstreams = self.streams\n\treturn\n}\n\nfunc (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {\n\tif err = self.prepare(); err != nil {\n\t\treturn\n\t}\n\treturn self.transdemux.ReadPacket()\n}\n\nfunc (self *Demuxer) prepare() (err error) {\n\tif self.transdemux != nil {\n\t\treturn\n\t}\n\n\t/*\n\tvar streams []av.CodecData\n\tif streams, err = self.Demuxer.Streams(); err != nil {\n\t\treturn\n\t}\n\t*/\n\n\tsupports := self.Options.OutputCodecTypes\n\n\ttransopts := transcode.Options{}\n\ttransopts.FindAudioDecoderEncoder = func(codec av.AudioCodecData, i int) (ok bool, dec av.AudioDecoder, enc av.AudioEncoder, err error) {\n\t\tif len(supports) == 0 {\n\t\t\treturn\n\t\t}\n\n\t\tsupport := false\n\t\tfor _, typ := range supports {\n\t\t\tif typ == codec.Type() {\n\t\t\t\tsupport = true\n\t\t\t}\n\t\t}\n\n\t\tif support {\n\t\t\treturn\n\t\t}\n\t\tok = true\n\n\t\tvar enctype av.CodecType\n\t\tfor _, typ:= range supports {\n\t\t\tif typ.IsAudio() {\n\t\t\t\tif enc, _ = avutil.DefaultHandlers.NewAudioEncoder(typ); enc != nil {\n\t\t\t\t\tenctype = typ\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif enc == nil {\n\t\t\terr = fmt.Errorf(\"avconv: convert %s->%s failed\", codec.Type(), enctype)\n\t\t\treturn\n\t\t}\n\n\t\t// TODO: support per stream option\n\t\t// enc.SetSampleRate ...\n\n\t\tif dec, err = avutil.DefaultHandlers.NewAudioDecoder(codec); err != nil {\n\t\t\terr = fmt.Errorf(\"avconv: decode %s failed\", codec.Type())\n\t\t\treturn\n\t\t}\n\n\t\treturn\n\t}\n\n\tself.transdemux = &transcode.Demuxer{\n\t\tOptions: transopts,\n\t\tDemuxer: self.Demuxer,\n\t}\n\tif self.streams, err = self.transdemux.Streams(); err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc ConvertCmdline(args []string) (err error) {\n\toutput := \"\"\n\tinput := \"\"\n\tflagi := false\n\tflagv := false\n\tflagt := false\n\tflagre := false\n\tduration := time.Duration(0)\n\toptions := Options{}\n\n\tfor _, arg := range args {\n\t\tswitch arg {\n\t\tcase \"-i\":\n\t\t\tflagi = true\n\n\t\tcase \"-v\":\n\t\t\tflagv = true\n\n\t\tcase \"-t\":\n\t\t\tflagt = true\n\n\t\tcase \"-re\":\n\t\t\tflagre = true\n\n\t\tdefault:\n\t\t\tswitch {\n\t\t\tcase flagi:\n\t\t\t\tflagi = false\n\t\t\t\tinput = arg\n\n\t\t\tcase flagt:\n\t\t\t\tflagt = false\n\t\t\t\tvar f float64\n\t\t\t\tfmt.Sscanf(arg, \"%f\", &f)\n\t\t\t\tduration = time.Duration(f*float64(time.Second))\n\n\t\t\tdefault:\n\t\t\t\toutput = arg\n\t\t\t}\n\t\t}\n\t}\n\n\tif input == \"\" {\n\t\terr = fmt.Errorf(\"avconv: input file not specified\")\n\t\treturn\n\t}\n\n\tif output == \"\" {\n\t\terr = fmt.Errorf(\"avconv: output file not specified\")\n\t\treturn\n\t}\n\n\tvar demuxer av.DemuxCloser\n\tvar muxer av.MuxCloser\n\n\tif demuxer, err = avutil.Open(input); err != nil {\n\t\treturn\n\t}\n\tdefer demuxer.Close()\n\n\tvar handler avutil.RegisterHandler\n\tif handler, muxer, err = avutil.DefaultHandlers.FindCreate(output); err != nil {\n\t\treturn\n\t}\n\tdefer muxer.Close()\n\n\toptions.OutputCodecTypes = handler.CodecTypes\n\n\tconvdemux := &Demuxer{\n\t\tOptions: options,\n\t\tDemuxer: demuxer,\n\t}\n\tdefer convdemux.Close()\n\n\tvar streams []av.CodecData\n\tif streams, err = demuxer.Streams(); err != nil {\n\t\treturn\n\t}\n\n\tvar convstreams []av.CodecData\n\tif convstreams, err = convdemux.Streams(); err != nil {\n\t\treturn\n\t}\n\n\tif flagv {\n\t\tfor _, stream := range streams {\n\t\t\tfmt.Print(stream.Type(), \" \")\n\t\t}\n\t\tfmt.Print(\"-> \")\n\t\tfor _, stream := range convstreams {\n\t\t\tfmt.Print(stream.Type(), \" \")\n\t\t}\n\t\tfmt.Println()\n\t}\n\n\tif err = muxer.WriteHeader(convstreams); err != nil {\n\t\treturn\n\t}\n\n\tfilters := pktque.Filters{}\n\tif flagre {\n\t\tfilters = append(filters, &pktque.Walltime{})\n\t}\n\tfilterdemux := &pktque.FilterDemuxer{\n\t\tDemuxer: convdemux,\n\t\tFilter: filters,\n\t}\n\n\tfor {\n\t\tvar pkt av.Packet\n\t\tif pkt, err = filterdemux.ReadPacket(); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif flagv {\n\t\t\tfmt.Println(pkt.Idx, pkt.Time, len(pkt.Data), pkt.IsKeyFrame)\n\t\t}\n\t\tif duration != 0 && pkt.Time > duration {\n\t\t\tbreak\n\t\t}\n\t\tif err = muxer.WritePacket(pkt); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err = muxer.WriteTrailer(); err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n\n"
  },
  {
    "path": "av/avutil/avutil.go",
    "content": "package avutil\n\nimport (\n\t\"io\"\n\t\"strings\"\n\t\"fmt\"\n\t\"bytes\"\n\t\"github.com/nareix/joy4/av\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n)\n\ntype HandlerDemuxer struct {\n\tav.Demuxer\n\tr io.ReadCloser\n}\n\nfunc (self *HandlerDemuxer) Close() error {\n\treturn self.r.Close()\n}\n\ntype HandlerMuxer struct {\n\tav.Muxer\n\tw io.WriteCloser\n\tstage int\n}\n\nfunc (self *HandlerMuxer) WriteHeader(streams []av.CodecData) (err error) {\n\tif self.stage == 0 {\n\t\tif err = self.Muxer.WriteHeader(streams); err != nil {\n\t\t\treturn\n\t\t}\n\t\tself.stage++\n\t}\n\treturn\n}\n\nfunc (self *HandlerMuxer) WriteTrailer() (err error) {\n\tif self.stage == 1 {\n\t\tself.stage++\n\t\tif err = self.Muxer.WriteTrailer(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *HandlerMuxer) Close() (err error) {\n\tif err = self.WriteTrailer(); err != nil {\n\t\treturn\n\t}\n\treturn self.w.Close()\n}\n\ntype RegisterHandler struct {\n\tExt string\n\tReaderDemuxer func(io.Reader)av.Demuxer\n\tWriterMuxer func(io.Writer)av.Muxer\n\tUrlMuxer func(string)(bool,av.MuxCloser,error)\n\tUrlDemuxer func(string)(bool,av.DemuxCloser,error)\n\tUrlReader func(string)(bool,io.ReadCloser,error)\n\tProbe func([]byte)bool\n\tAudioEncoder func(av.CodecType)(av.AudioEncoder,error)\n\tAudioDecoder func(av.AudioCodecData)(av.AudioDecoder,error)\n\tServerDemuxer func(string)(bool,av.DemuxCloser,error)\n\tServerMuxer func(string)(bool,av.MuxCloser,error)\n\tCodecTypes []av.CodecType\n}\n\ntype Handlers struct {\n\thandlers []RegisterHandler\n}\n\nfunc (self *Handlers) Add(fn func(*RegisterHandler)) {\n\thandler := &RegisterHandler{}\n\tfn(handler)\n\tself.handlers = append(self.handlers, *handler)\n}\n\nfunc (self *Handlers) openUrl(u *url.URL, uri string) (r io.ReadCloser, err error) {\n\tif u != nil && u.Scheme != \"\" {\n\t\tfor _, handler := range self.handlers {\n\t\t\tif handler.UrlReader != nil {\n\t\t\t\tvar ok bool\n\t\t\t\tif ok, r, err = handler.UrlReader(uri); ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\terr = fmt.Errorf(\"avutil: openUrl %s failed\", uri)\n\t} else {\n\t\tr, err = os.Open(uri)\n\t}\n\treturn\n}\n\nfunc (self *Handlers) createUrl(u *url.URL, uri string) (w io.WriteCloser, err error) {\n\tw, err = os.Create(uri)\n\treturn\n}\n\nfunc (self *Handlers) NewAudioEncoder(typ av.CodecType) (enc av.AudioEncoder, err error) {\n\tfor _, handler := range self.handlers {\n\t\tif handler.AudioEncoder != nil {\n\t\t\tif enc, _ = handler.AudioEncoder(typ); enc != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\terr = fmt.Errorf(\"avutil: encoder\", typ, \"not found\")\n\treturn\n}\n\nfunc (self *Handlers) NewAudioDecoder(codec av.AudioCodecData) (dec av.AudioDecoder, err error) {\n\tfor _, handler := range self.handlers {\n\t\tif handler.AudioDecoder != nil {\n\t\t\tif dec, _ = handler.AudioDecoder(codec); dec != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\terr = fmt.Errorf(\"avutil: decoder\", codec.Type(), \"not found\")\n\treturn\n}\n\nfunc (self *Handlers) Open(uri string) (demuxer av.DemuxCloser, err error) {\n\tlisten := false\n\tif strings.HasPrefix(uri, \"listen:\") {\n\t\turi = uri[len(\"listen:\"):]\n\t\tlisten = true\n\t}\n\n\tfor _, handler := range self.handlers {\n\t\tif listen {\n\t\t\tif handler.ServerDemuxer != nil {\n\t\t\t\tvar ok bool\n\t\t\t\tif ok, demuxer, err = handler.ServerDemuxer(uri); ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif handler.UrlDemuxer != nil {\n\t\t\t\tvar ok bool\n\t\t\t\tif ok, demuxer, err = handler.UrlDemuxer(uri); ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvar r io.ReadCloser\n\tvar ext string\n\tvar u *url.URL\n\tif u, _ = url.Parse(uri); u != nil && u.Scheme != \"\" {\n\t\text = path.Ext(u.Path)\n\t} else {\n\t\text = path.Ext(uri)\n\t}\n\n\tif ext != \"\" {\n\t\tfor _, handler := range self.handlers {\n\t\t\tif handler.Ext == ext {\n\t\t\t\tif handler.ReaderDemuxer != nil {\n\t\t\t\t\tif r, err = self.openUrl(u, uri); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tdemuxer = &HandlerDemuxer{\n\t\t\t\t\t\tDemuxer: handler.ReaderDemuxer(r),\n\t\t\t\t\t\tr: r,\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvar probebuf [1024]byte\n\tif r, err = self.openUrl(u, uri); err != nil {\n\t\treturn\n\t}\n\tif _, err = io.ReadFull(r, probebuf[:]); err != nil {\n\t\treturn\n\t}\n\n\tfor _, handler := range self.handlers {\n\t\tif handler.Probe != nil && handler.Probe(probebuf[:]) && handler.ReaderDemuxer != nil {\n\t\t\tvar _r io.Reader\n\t\t\tif rs, ok := r.(io.ReadSeeker); ok {\n\t\t\t\tif _, err = rs.Seek(0, 0); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t_r = rs\n\t\t\t} else {\n\t\t\t\t_r = io.MultiReader(bytes.NewReader(probebuf[:]), r)\n\t\t\t}\n\t\t\tdemuxer = &HandlerDemuxer{\n\t\t\t\tDemuxer: handler.ReaderDemuxer(_r),\n\t\t\t\tr: r,\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tr.Close()\n\terr = fmt.Errorf(\"avutil: open %s failed\", uri)\n\treturn\n}\n\nfunc (self *Handlers) Create(uri string) (muxer av.MuxCloser, err error) {\n\t_, muxer, err = self.FindCreate(uri)\n\treturn\n}\n\nfunc (self *Handlers) FindCreate(uri string) (handler RegisterHandler, muxer av.MuxCloser, err error) {\n\tlisten := false\n\tif strings.HasPrefix(uri, \"listen:\") {\n\t\turi = uri[len(\"listen:\"):]\n\t\tlisten = true\n\t}\n\n\tfor _, handler = range self.handlers {\n\t\tif listen {\n\t\t\tif handler.ServerMuxer != nil {\n\t\t\t\tvar ok bool\n\t\t\t\tif ok, muxer, err = handler.ServerMuxer(uri); ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif handler.UrlMuxer != nil {\n\t\t\t\tvar ok bool\n\t\t\t\tif ok, muxer, err = handler.UrlMuxer(uri); ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvar ext string\n\tvar u *url.URL\n\tif u, _ = url.Parse(uri); u != nil && u.Scheme != \"\" {\n\t\text = path.Ext(u.Path)\n\t} else {\n\t\text = path.Ext(uri)\n\t}\n\n\tif ext != \"\" {\n\t\tfor _, handler = range self.handlers {\n\t\t\tif handler.Ext == ext && handler.WriterMuxer != nil {\n\t\t\t\tvar w io.WriteCloser\n\t\t\t\tif w, err = self.createUrl(u, uri); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tmuxer = &HandlerMuxer{\n\t\t\t\t\tMuxer: handler.WriterMuxer(w),\n\t\t\t\t\tw: w,\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\terr = fmt.Errorf(\"avutil: create muxer %s failed\", uri)\n\treturn\n}\n\nvar DefaultHandlers = &Handlers{}\n\nfunc Open(url string) (demuxer av.DemuxCloser, err error) {\n\treturn DefaultHandlers.Open(url)\n}\n\nfunc Create(url string) (muxer av.MuxCloser, err error) {\n\treturn DefaultHandlers.Create(url)\n}\n\nfunc CopyPackets(dst av.PacketWriter, src av.PacketReader) (err error) {\n\tfor {\n\t\tvar pkt av.Packet\n\t\tif pkt, err = src.ReadPacket(); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif err = dst.WritePacket(pkt); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc CopyFile(dst av.Muxer, src av.Demuxer) (err error) {\n\tvar streams []av.CodecData\n\tif streams, err = src.Streams(); err != nil {\n\t\treturn\n\t}\n\tif err = dst.WriteHeader(streams); err != nil {\n\t\treturn\n\t}\n\tif err = CopyPackets(dst, src); err != nil {\n\t\tif err != io.EOF {\n\t\t\treturn\n\t\t}\n\t}\n\tif err = dst.WriteTrailer(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "av/pktque/buf.go",
    "content": "package pktque\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n)\n\ntype Buf struct {\n\tHead, Tail BufPos\n\tpkts       []av.Packet\n\tSize       int\n\tCount      int\n}\n\nfunc NewBuf() *Buf {\n\treturn &Buf{\n\t\tpkts: make([]av.Packet, 64),\n\t}\n}\n\nfunc (self *Buf) Pop() av.Packet {\n\tif self.Count == 0 {\n\t\tpanic(\"pktque.Buf: Pop() when count == 0\")\n\t}\n\n\ti := int(self.Head) & (len(self.pkts) - 1)\n\tpkt := self.pkts[i]\n\tself.pkts[i] = av.Packet{}\n\tself.Size -= len(pkt.Data)\n\tself.Head++\n\tself.Count--\n\n\treturn pkt\n}\n\nfunc (self *Buf) grow() {\n\tnewpkts := make([]av.Packet, len(self.pkts)*2)\n\tfor i := self.Head; i.LT(self.Tail); i++ {\n\t\tnewpkts[int(i)&(len(newpkts)-1)] = self.pkts[int(i)&(len(self.pkts)-1)]\n\t}\n\tself.pkts = newpkts\n}\n\nfunc (self *Buf) Push(pkt av.Packet) {\n\tif self.Count == len(self.pkts) {\n\t\tself.grow()\n\t}\n\tself.pkts[int(self.Tail)&(len(self.pkts)-1)] = pkt\n\tself.Tail++\n\tself.Count++\n\tself.Size += len(pkt.Data)\n}\n\nfunc (self *Buf) Get(pos BufPos) av.Packet {\n\treturn self.pkts[int(pos)&(len(self.pkts)-1)]\n}\n\nfunc (self *Buf) IsValidPos(pos BufPos) bool {\n\treturn pos.GE(self.Head) && pos.LT(self.Tail)\n}\n\ntype BufPos int\n\nfunc (self BufPos) LT(pos BufPos) bool {\n\treturn self-pos < 0\n}\n\nfunc (self BufPos) GE(pos BufPos) bool {\n\treturn self-pos >= 0\n}\n\nfunc (self BufPos) GT(pos BufPos) bool {\n\treturn self-pos > 0\n}\n"
  },
  {
    "path": "av/pktque/filters.go",
    "content": "\n// Package pktque provides packet Filter interface and structures used by other components.\npackage pktque\n\nimport (\n\t\"time\"\n\t\"github.com/nareix/joy4/av\"\n)\n\ntype Filter interface {\n\t// Change packet time or drop packet\n\tModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error)\n}\n\n// Combine multiple Filters into one, ModifyPacket will be called in order.\ntype Filters []Filter\n\nfunc (self Filters) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {\n\tfor _, filter := range self {\n\t\tif drop, err = filter.ModifyPacket(pkt, streams, videoidx, audioidx); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif drop {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// Wrap origin Demuxer and Filter into a new Demuxer, when read this Demuxer filters will be called.\ntype FilterDemuxer struct {\n\tav.Demuxer\n\tFilter Filter\n\tstreams []av.CodecData\n\tvideoidx int\n\taudioidx int\n}\n\nfunc (self FilterDemuxer) ReadPacket() (pkt av.Packet, err error) {\n\tif self.streams == nil {\n\t\tif self.streams, err = self.Demuxer.Streams(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tfor i, stream := range self.streams {\n\t\t\tif stream.Type().IsVideo() {\n\t\t\t\tself.videoidx = i\n\t\t\t} else if stream.Type().IsAudio() {\n\t\t\t\tself.audioidx = i\n\t\t\t}\n\t\t}\n\t}\n\n\tfor {\n\t\tif pkt, err = self.Demuxer.ReadPacket(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar drop bool\n\t\tif drop, err = self.Filter.ModifyPacket(&pkt, self.streams, self.videoidx, self.audioidx); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif !drop {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn\n}\n\n// Drop packets until first video key frame arrived.\ntype WaitKeyFrame struct {\n\tok bool\n}\n\nfunc (self *WaitKeyFrame) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {\n\tif !self.ok && pkt.Idx == int8(videoidx) && pkt.IsKeyFrame {\n\t\tself.ok = true\n\t}\n\tdrop = !self.ok\n\treturn\n}\n\n// Fix incorrect packet timestamps.\ntype FixTime struct {\n\tzerobase time.Duration\n\tincrbase time.Duration\n\tlasttime time.Duration\n\tStartFromZero bool // make timestamp start from zero\n\tMakeIncrement bool // force timestamp increment\n}\n\nfunc (self *FixTime) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {\n\tif self.StartFromZero {\n\t\tif self.zerobase == 0 {\n\t\t\tself.zerobase = pkt.Time\n\t\t}\n\t\tpkt.Time -= self.zerobase\n\t}\n\n\tif self.MakeIncrement {\n\t\tpkt.Time -= self.incrbase\n\t\tif self.lasttime == 0 {\n\t\t\tself.lasttime = pkt.Time\n\t\t}\n\t\tif pkt.Time < self.lasttime || pkt.Time > self.lasttime+time.Millisecond*500 {\n\t\t\tself.incrbase += pkt.Time - self.lasttime\n\t\t\tpkt.Time = self.lasttime\n\t\t}\n\t\tself.lasttime = pkt.Time\n\t}\n\n\treturn\n}\n\n// Drop incorrect packets to make A/V sync.\ntype AVSync struct {\n\tMaxTimeDiff time.Duration\n\ttime []time.Duration\n}\n\nfunc (self *AVSync) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {\n\tif self.time == nil {\n\t\tself.time = make([]time.Duration, len(streams))\n\t\tif self.MaxTimeDiff == 0 {\n\t\t\tself.MaxTimeDiff = time.Millisecond*500\n\t\t}\n\t}\n\n\tstart, end, correctable, correcttime := self.check(int(pkt.Idx))\n\tif pkt.Time >= start && pkt.Time < end {\n\t\tself.time[pkt.Idx] = pkt.Time\n\t} else {\n\t\tif correctable {\n\t\t\tpkt.Time = correcttime\n\t\t\tfor i := range self.time {\n\t\t\t\tself.time[i] = correcttime\n\t\t\t}\n\t\t} else {\n\t\t\tdrop = true\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *AVSync) check(i int) (start time.Duration, end time.Duration, correctable bool, correcttime time.Duration) {\n\tminidx := -1\n\tmaxidx := -1\n\tfor j := range self.time {\n\t\tif minidx == -1 || self.time[j] < self.time[minidx] {\n\t\t\tminidx = j\n\t\t}\n\t\tif maxidx == -1 || self.time[j] > self.time[maxidx] {\n\t\t\tmaxidx = j\n\t\t}\n\t}\n\tallthesame := self.time[minidx] == self.time[maxidx]\n\n\tif i == maxidx {\n\t\tif allthesame {\n\t\t\tcorrectable = true\n\t\t} else {\n\t\t\tcorrectable = false\n\t\t}\n\t} else {\n\t\tcorrectable = true\n\t}\n\n\tstart = self.time[minidx]\n\tend = start + self.MaxTimeDiff\n\tcorrecttime = start + time.Millisecond*40\n\treturn\n}\n\n// Make packets reading speed as same as walltime, effect like ffmpeg -re option.\ntype Walltime struct {\n\tfirsttime time.Time\n}\n\nfunc (self *Walltime) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {\n\tif pkt.Idx == 0 {\n\t\tif self.firsttime.IsZero() {\n\t\t\tself.firsttime = time.Now()\n\t\t}\n\t\tpkttime := self.firsttime.Add(pkt.Time)\n\t\tdelta := pkttime.Sub(time.Now())\n\t\tif delta > 0 {\n\t\t\ttime.Sleep(delta)\n\t\t}\n\t}\n\treturn\n}\n\n"
  },
  {
    "path": "av/pktque/timeline.go",
    "content": "package pktque\n\nimport (\n\t\"time\"\n)\n\n/*\npop                                   push\n\n     seg                 seg        seg\n  |--------|         |---------|   |---|\n     20ms                40ms       5ms\n----------------- time -------------------->\nheadtm                               tailtm\n*/\n\ntype tlSeg struct {\n\ttm, dur time.Duration\n}\n\ntype Timeline struct {\n\tsegs []tlSeg\n\theadtm time.Duration\n}\n\nfunc (self *Timeline) Push(tm time.Duration, dur time.Duration) {\n\tif len(self.segs) > 0 {\n\t\ttail := self.segs[len(self.segs)-1]\n\t\tdiff := tm-(tail.tm+tail.dur)\n\t\tif diff < 0 {\n\t\t\ttm -= diff\n\t\t}\n\t}\n\tself.segs = append(self.segs, tlSeg{tm, dur})\n}\n\nfunc (self *Timeline) Pop(dur time.Duration) (tm time.Duration) {\n\tif len(self.segs) == 0 {\n\t\treturn self.headtm\n\t}\n\n\ttm = self.segs[0].tm\n\tfor dur > 0 && len(self.segs) > 0 {\n\t\tseg := &self.segs[0]\n\t\tsub := dur\n\t\tif seg.dur < sub {\n\t\t\tsub = seg.dur\n\t\t}\n\t\tseg.dur -= sub\n\t\tdur -= sub\n\t\tseg.tm += sub\n\t\tself.headtm += sub\n\t\tif seg.dur == 0 {\n\t\t\tcopy(self.segs[0:], self.segs[1:])\n\t\t\tself.segs = self.segs[:len(self.segs)-1]\n\t\t}\n\t}\n\n\treturn\n}\n\n"
  },
  {
    "path": "av/pubsub/queue.go",
    "content": "// Packege pubsub implements publisher-subscribers model used in multi-channel streaming.\npackage pubsub\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/pktque\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n)\n\n//        time\n// ----------------->\n//\n// V-A-V-V-A-V-V-A-V-V\n// |                 |\n// 0        5        10\n// head             tail\n// oldest          latest\n//\n\n// One publisher and multiple subscribers thread-safe packet buffer queue.\ntype Queue struct {\n\tbuf                      *pktque.Buf\n\thead, tail               int\n\tlock                     *sync.RWMutex\n\tcond                     *sync.Cond\n\tcurgopcount, maxgopcount int\n\tstreams                  []av.CodecData\n\tvideoidx                 int\n\tclosed                   bool\n}\n\nfunc NewQueue() *Queue {\n\tq := &Queue{}\n\tq.buf = pktque.NewBuf()\n\tq.maxgopcount = 2\n\tq.lock = &sync.RWMutex{}\n\tq.cond = sync.NewCond(q.lock.RLocker())\n\tq.videoidx = -1\n\treturn q\n}\n\nfunc (self *Queue) SetMaxGopCount(n int) {\n\tself.lock.Lock()\n\tself.maxgopcount = n\n\tself.lock.Unlock()\n\treturn\n}\n\nfunc (self *Queue) WriteHeader(streams []av.CodecData) error {\n\tself.lock.Lock()\n\n\tself.streams = streams\n\tfor i, stream := range streams {\n\t\tif stream.Type().IsVideo() {\n\t\t\tself.videoidx = i\n\t\t}\n\t}\n\tself.cond.Broadcast()\n\n\tself.lock.Unlock()\n\n\treturn nil\n}\n\nfunc (self *Queue) WriteTrailer() error {\n\treturn nil\n}\n\n// After Close() called, all QueueCursor's ReadPacket will return io.EOF.\nfunc (self *Queue) Close() (err error) {\n\tself.lock.Lock()\n\n\tself.closed = true\n\tself.cond.Broadcast()\n\n\tself.lock.Unlock()\n\treturn\n}\n\n// Put packet into buffer, old packets will be discared.\nfunc (self *Queue) WritePacket(pkt av.Packet) (err error) {\n\tself.lock.Lock()\n\n\tself.buf.Push(pkt)\n\tif pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame {\n\t\tself.curgopcount++\n\t}\n\n\tfor self.curgopcount >= self.maxgopcount && self.buf.Count > 1 {\n\t\tpkt := self.buf.Pop()\n\t\tif pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame {\n\t\t\tself.curgopcount--\n\t\t}\n\t\tif self.curgopcount < self.maxgopcount {\n\t\t\tbreak\n\t\t}\n\t}\n\t//println(\"shrink\", self.curgopcount, self.maxgopcount, self.buf.Head, self.buf.Tail, \"count\", self.buf.Count, \"size\", self.buf.Size)\n\n\tself.cond.Broadcast()\n\n\tself.lock.Unlock()\n\treturn\n}\n\ntype QueueCursor struct {\n\tque    *Queue\n\tpos    pktque.BufPos\n\tgotpos bool\n\tinit   func(buf *pktque.Buf, videoidx int) pktque.BufPos\n}\n\nfunc (self *Queue) newCursor() *QueueCursor {\n\treturn &QueueCursor{\n\t\tque: self,\n\t}\n}\n\n// Create cursor position at latest packet.\nfunc (self *Queue) Latest() *QueueCursor {\n\tcursor := self.newCursor()\n\tcursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos {\n\t\treturn buf.Tail\n\t}\n\treturn cursor\n}\n\n// Create cursor position at oldest buffered packet.\nfunc (self *Queue) Oldest() *QueueCursor {\n\tcursor := self.newCursor()\n\tcursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos {\n\t\treturn buf.Head\n\t}\n\treturn cursor\n}\n\n// Create cursor position at specific time in buffered packets.\nfunc (self *Queue) DelayedTime(dur time.Duration) *QueueCursor {\n\tcursor := self.newCursor()\n\tcursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos {\n\t\ti := buf.Tail - 1\n\t\tif buf.IsValidPos(i) {\n\t\t\tend := buf.Get(i)\n\t\t\tfor buf.IsValidPos(i) {\n\t\t\t\tif end.Time-buf.Get(i).Time > dur {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\ti--\n\t\t\t}\n\t\t}\n\t\treturn i\n\t}\n\treturn cursor\n}\n\n// Create cursor position at specific delayed GOP count in buffered packets.\nfunc (self *Queue) DelayedGopCount(n int) *QueueCursor {\n\tcursor := self.newCursor()\n\tcursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos {\n\t\ti := buf.Tail - 1\n\t\tif videoidx != -1 {\n\t\t\tfor gop := 0; buf.IsValidPos(i) && gop < n; i-- {\n\t\t\t\tpkt := buf.Get(i)\n\t\t\t\tif pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame {\n\t\t\t\t\tgop++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn i\n\t}\n\treturn cursor\n}\n\nfunc (self *QueueCursor) Streams() (streams []av.CodecData, err error) {\n\tself.que.cond.L.Lock()\n\tfor self.que.streams == nil && !self.que.closed {\n\t\tself.que.cond.Wait()\n\t}\n\tif self.que.streams != nil {\n\t\tstreams = self.que.streams\n\t} else {\n\t\terr = io.EOF\n\t}\n\tself.que.cond.L.Unlock()\n\treturn\n}\n\n// ReadPacket will not consume packets in Queue, it's just a cursor.\nfunc (self *QueueCursor) ReadPacket() (pkt av.Packet, err error) {\n\tself.que.cond.L.Lock()\n\tbuf := self.que.buf\n\tif !self.gotpos {\n\t\tself.pos = self.init(buf, self.que.videoidx)\n\t\tself.gotpos = true\n\t}\n\tfor {\n\t\tif self.pos.LT(buf.Head) {\n\t\t\tself.pos = buf.Head\n\t\t} else if self.pos.GT(buf.Tail) {\n\t\t\tself.pos = buf.Tail\n\t\t}\n\t\tif buf.IsValidPos(self.pos) {\n\t\t\tpkt = buf.Get(self.pos)\n\t\t\tself.pos++\n\t\t\tbreak\n\t\t}\n\t\tif self.que.closed {\n\t\t\terr = io.EOF\n\t\t\tbreak\n\t\t}\n\t\tself.que.cond.Wait()\n\t}\n\tself.que.cond.L.Unlock()\n\treturn\n}\n"
  },
  {
    "path": "av/transcode/transcode.go",
    "content": "\n// Package transcoder implements Transcoder based on Muxer/Demuxer and AudioEncoder/AudioDecoder interface.\npackage transcode\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/pktque\"\n)\n\nvar Debug bool\n\ntype tStream struct {\n\tcodec av.CodecData\n\ttimeline *pktque.Timeline\n\taencodec, adecodec av.AudioCodecData\n\taenc av.AudioEncoder\n\tadec av.AudioDecoder\n}\n\ntype Options struct {\n\t// check if transcode is needed, and create the AudioDecoder and AudioEncoder.\n\tFindAudioDecoderEncoder func(codec av.AudioCodecData, i int) (\n\t\tneed bool, dec av.AudioDecoder, enc av.AudioEncoder, err error,\n\t)\n}\n\ntype Transcoder struct {\n\tstreams                 []*tStream\n}\n\nfunc NewTranscoder(streams []av.CodecData, options Options) (_self *Transcoder, err error) {\n\tself := &Transcoder{}\n\tself.streams = []*tStream{}\n\n\tfor i, stream := range streams {\n\t\tts := &tStream{codec: stream}\n\t\tif stream.Type().IsAudio() {\n\t\t\tif options.FindAudioDecoderEncoder != nil {\n\t\t\t\tvar ok bool\n\t\t\t\tvar enc av.AudioEncoder\n\t\t\t\tvar dec av.AudioDecoder\n\t\t\t\tok, dec, enc, err = options.FindAudioDecoderEncoder(stream.(av.AudioCodecData), i)\n\t\t\t\tif ok {\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tts.timeline = &pktque.Timeline{}\n\t\t\t\t\tif ts.codec, err = enc.CodecData(); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tts.aencodec = ts.codec.(av.AudioCodecData)\n\t\t\t\t\tts.adecodec = stream.(av.AudioCodecData)\n\t\t\t\t\tts.aenc = enc\n\t\t\t\t\tts.adec = dec\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tself.streams = append(self.streams, ts)\n\t}\n\n\t_self = self\n\treturn\n}\n\nfunc (self *tStream) audioDecodeAndEncode(inpkt av.Packet) (outpkts []av.Packet, err error) {\n\tvar dur time.Duration\n\tvar frame av.AudioFrame\n\tvar ok bool\n\tif ok, frame, err = self.adec.Decode(inpkt.Data); err != nil {\n\t\treturn\n\t}\n\tif !ok {\n\t\treturn\n\t}\n\n\tif dur, err = self.adecodec.PacketDuration(inpkt.Data); err != nil {\n\t\terr = fmt.Errorf(\"transcode: PacketDuration() failed for input stream #%d\", inpkt.Idx)\n\t\treturn\n\t}\n\n\tif Debug {\n\t\tfmt.Println(\"transcode: push\", inpkt.Time, dur)\n\t}\n\tself.timeline.Push(inpkt.Time, dur)\n\n\tvar _outpkts [][]byte\n\tif _outpkts, err = self.aenc.Encode(frame); err != nil {\n\t\treturn\n\t}\n\tfor _, _outpkt := range _outpkts {\n\t\tif dur, err = self.aencodec.PacketDuration(_outpkt); err != nil {\n\t\t\terr = fmt.Errorf(\"transcode: PacketDuration() failed for output stream #%d\", inpkt.Idx)\n\t\t\treturn\n\t\t}\n\t\toutpkt := av.Packet{Idx: inpkt.Idx, Data: _outpkt}\n\t\toutpkt.Time = self.timeline.Pop(dur)\n\n\t\tif Debug {\n\t\t\tfmt.Println(\"transcode: pop\", outpkt.Time, dur)\n\t\t}\n\n\t\toutpkts = append(outpkts, outpkt)\n\t}\n\n\treturn\n}\n\n// Do the transcode.\n// \n// In audio transcoding one Packet may transcode into many Packets\n// packet time will be adjusted automatically.\nfunc (self *Transcoder) Do(pkt av.Packet) (out []av.Packet, err error) {\n\tstream := self.streams[pkt.Idx]\n\tif stream.aenc != nil && stream.adec != nil {\n\t\tif out, err = stream.audioDecodeAndEncode(pkt); err != nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tout = append(out, pkt)\n\t}\n\treturn\n}\n\n// Get CodecDatas after transcoding.\nfunc (self *Transcoder) Streams() (streams []av.CodecData, err error) {\n\tfor _, stream := range self.streams {\n\t\tstreams = append(streams, stream.codec)\n\t}\n\treturn\n}\n\n// Close transcoder, close related encoder and decoders.\nfunc (self *Transcoder) Close() (err error) {\n\tfor _, stream := range self.streams {\n\t\tif stream.aenc != nil {\n\t\t\tstream.aenc.Close()\n\t\t\tstream.aenc = nil\n\t\t}\n\t\tif stream.adec != nil {\n\t\t\tstream.adec.Close()\n\t\t\tstream.adec = nil\n\t\t}\n\t}\n\tself.streams = nil\n\treturn\n}\n\n// Wrap transcoder and origin Muxer into new Muxer.\n// Write to new Muxer will do transcoding automatically.\ntype Muxer struct {\n\tav.Muxer // origin Muxer\n\tOptions // transcode options\n\ttranscoder *Transcoder\n}\n\nfunc (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {\n\tif self.transcoder, err = NewTranscoder(streams, self.Options); err != nil {\n\t\treturn\n\t}\n\tvar newstreams []av.CodecData\n\tif newstreams, err = self.transcoder.Streams(); err != nil {\n\t\treturn\n\t}\n\tif err = self.Muxer.WriteHeader(newstreams); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Muxer) WritePacket(pkt av.Packet) (err error) {\n\tvar outpkts []av.Packet\n\tif outpkts, err = self.transcoder.Do(pkt); err != nil {\n\t\treturn\n\t}\n\tfor _, pkt := range outpkts {\n\t\tif err = self.Muxer.WritePacket(pkt); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Muxer) Close() (err error) {\n\tif self.transcoder != nil {\n\t\treturn self.transcoder.Close()\n\t}\n\treturn\n}\n\n// Wrap transcoder and origin Demuxer into new Demuxer.\n// Read this Demuxer will do transcoding automatically.\ntype Demuxer struct {\n\tav.Demuxer\n\tOptions\n\ttranscoder *Transcoder\n\toutpkts []av.Packet\n}\n\nfunc (self *Demuxer) prepare() (err error) {\n\tif self.transcoder == nil {\n\t\tvar streams []av.CodecData\n\t\tif streams, err = self.Demuxer.Streams(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.transcoder, err = NewTranscoder(streams, self.Options); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {\n\tif err = self.prepare(); err != nil {\n\t\treturn\n\t}\n\tfor {\n\t\tif len(self.outpkts) > 0 {\n\t\t\tpkt = self.outpkts[0]\n\t\t\tself.outpkts = self.outpkts[1:]\n\t\t\treturn\n\t\t}\n\t\tvar rpkt av.Packet\n\t\tif rpkt, err = self.Demuxer.ReadPacket(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.outpkts, err = self.transcoder.Do(rpkt); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) Streams() (streams []av.CodecData, err error) {\n\tif err = self.prepare(); err != nil {\n\t\treturn\n\t}\n\treturn self.transcoder.Streams()\n}\n\nfunc (self *Demuxer) Close() (err error) {\n\tif self.transcoder != nil {\n\t\treturn self.transcoder.Close()\n\t}\n\treturn\n}\n"
  },
  {
    "path": "cgo/ffmpeg/audio.go",
    "content": "package ffmpeg\n\n/*\n#include \"ffmpeg.h\"\nint wrap_avcodec_decode_audio4(AVCodecContext *ctx, AVFrame *frame, void *data, int size, int *got) {\n\tstruct AVPacket pkt = {.data = data, .size = size};\n\treturn avcodec_decode_audio4(ctx, frame, got, &pkt);\n}\nint wrap_avresample_convert(AVAudioResampleContext *avr, int *out, int outsize, int outcount, int *in, int insize, int incount) {\n\treturn avresample_convert(avr, (void *)out, outsize, outcount, (void *)in, insize, incount);\n}\n*/\nimport \"C\"\nimport (\n\t\"unsafe\"\n\t\"runtime\"\n\t\"fmt\"\n\t\"time\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/codec/aacparser\"\n)\n\nconst debug = false\n\ntype Resampler struct {\n\tinSampleFormat, OutSampleFormat av.SampleFormat\n\tinChannelLayout, OutChannelLayout av.ChannelLayout\n\tinSampleRate, OutSampleRate int\n\tavr *C.AVAudioResampleContext\n}\n\nfunc (self *Resampler) Resample(in av.AudioFrame) (out av.AudioFrame, err error) {\n\tformatChange := in.SampleRate != self.inSampleRate || in.SampleFormat != self.inSampleFormat || in.ChannelLayout != self.inChannelLayout\n\n\tvar flush av.AudioFrame\n\n\tif formatChange {\n\t\tif self.avr != nil {\n\t\t\toutChannels := self.OutChannelLayout.Count()\n\t\t\tif !self.OutSampleFormat.IsPlanar() {\n\t\t\t\toutChannels = 1\n\t\t\t}\n\t\t\toutData := make([]*C.uint8_t, outChannels)\n\t\t\toutSampleCount := int(C.avresample_get_out_samples(self.avr, C.int(in.SampleCount)))\n\t\t\toutLinesize := outSampleCount*self.OutSampleFormat.BytesPerSample()\n\t\t\tflush.Data = make([][]byte, outChannels)\n\t\t\tfor i := 0; i < outChannels; i++ {\n\t\t\t\tflush.Data[i] = make([]byte, outLinesize)\n\t\t\t\toutData[i] = (*C.uint8_t)(unsafe.Pointer(&flush.Data[i][0]))\n\t\t\t}\n\t\t\tflush.ChannelLayout = self.OutChannelLayout\n\t\t\tflush.SampleFormat = self.OutSampleFormat\n\t\t\tflush.SampleRate = self.OutSampleRate\n\n\t\t\tconvertSamples := int(C.wrap_avresample_convert(\n\t\t\t\tself.avr,\n\t\t\t\t(*C.int)(unsafe.Pointer(&outData[0])), C.int(outLinesize), C.int(outSampleCount),\n\t\t\t\tnil, C.int(0), C.int(0),\n\t\t\t))\n\t\t\tif convertSamples < 0 {\n\t\t\t\terr = fmt.Errorf(\"ffmpeg: avresample_convert_frame failed\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tflush.SampleCount = convertSamples\n\t\t\tif convertSamples < outSampleCount {\n\t\t\t\tfor i := 0; i < outChannels; i++ {\n\t\t\t\t\tflush.Data[i] = flush.Data[i][:convertSamples*self.OutSampleFormat.BytesPerSample()]\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//fmt.Println(\"flush:\", \"outSampleCount\", outSampleCount, \"convertSamples\", convertSamples, \"datasize\", len(flush.Data[0]))\n\t\t} else {\n\t\t\truntime.SetFinalizer(self, func(self *Resampler) {\n\t\t\t\tself.Close()\n\t\t\t})\n\t\t}\n\n\t\tC.avresample_free(&self.avr)\n\t\tself.inSampleFormat = in.SampleFormat\n\t\tself.inSampleRate = in.SampleRate\n\t\tself.inChannelLayout = in.ChannelLayout\n\t\tavr := C.avresample_alloc_context()\n\t\tC.av_opt_set_int(unsafe.Pointer(avr), C.CString(\"in_channel_layout\"), C.int64_t(channelLayoutAV2FF(self.inChannelLayout)), 0)\n\t\tC.av_opt_set_int(unsafe.Pointer(avr), C.CString(\"out_channel_layout\"), C.int64_t(channelLayoutAV2FF(self.OutChannelLayout)), 0)\n\t\tC.av_opt_set_int(unsafe.Pointer(avr), C.CString(\"in_sample_rate\"), C.int64_t(self.inSampleRate), 0)\n\t\tC.av_opt_set_int(unsafe.Pointer(avr), C.CString(\"out_sample_rate\"), C.int64_t(self.OutSampleRate), 0)\n\t\tC.av_opt_set_int(unsafe.Pointer(avr), C.CString(\"in_sample_fmt\"), C.int64_t(sampleFormatAV2FF(self.inSampleFormat)), 0)\n\t\tC.av_opt_set_int(unsafe.Pointer(avr), C.CString(\"out_sample_fmt\"), C.int64_t(sampleFormatAV2FF(self.OutSampleFormat)), 0)\n\t\tC.avresample_open(avr)\n\t\tself.avr = avr\n\t}\n\n\tvar inChannels, inLinesize int\n\tinSampleCount := in.SampleCount\n\tif !self.inSampleFormat.IsPlanar() {\n\t\tinChannels = 1\n\t\tinLinesize = inSampleCount*in.SampleFormat.BytesPerSample()*self.inChannelLayout.Count()\n\t} else {\n\t\tinChannels = self.inChannelLayout.Count()\n\t\tinLinesize = inSampleCount*in.SampleFormat.BytesPerSample()\n\t}\n\tinData := make([]*C.uint8_t, inChannels)\n\tfor i := 0; i < inChannels; i++ {\n\t\tinData[i] = (*C.uint8_t)(unsafe.Pointer(&in.Data[i][0]))\n\t}\n\n\tvar outChannels, outLinesize, outBytesPerSample int\n\toutSampleCount := int(C.avresample_get_out_samples(self.avr, C.int(in.SampleCount)))\n\tif !self.OutSampleFormat.IsPlanar() {\n\t\toutChannels = 1\n\t\toutBytesPerSample = self.OutSampleFormat.BytesPerSample()*self.OutChannelLayout.Count()\n\t\toutLinesize = outSampleCount*outBytesPerSample\n\t} else {\n\t\toutChannels = self.OutChannelLayout.Count()\n\t\toutBytesPerSample = self.OutSampleFormat.BytesPerSample()\n\t\toutLinesize = outSampleCount*outBytesPerSample\n\t}\n\toutData := make([]*C.uint8_t, outChannels)\n\tout.Data = make([][]byte, outChannels)\n\tfor i := 0; i < outChannels; i++ {\n\t\tout.Data[i] = make([]byte, outLinesize)\n\t\toutData[i] = (*C.uint8_t)(unsafe.Pointer(&out.Data[i][0]))\n\t}\n\tout.ChannelLayout = self.OutChannelLayout\n\tout.SampleFormat = self.OutSampleFormat\n\tout.SampleRate = self.OutSampleRate\n\n\tconvertSamples := int(C.wrap_avresample_convert(\n\t\tself.avr,\n\t\t(*C.int)(unsafe.Pointer(&outData[0])), C.int(outLinesize), C.int(outSampleCount),\n\t\t(*C.int)(unsafe.Pointer(&inData[0])), C.int(inLinesize), C.int(inSampleCount),\n\t))\n\tif convertSamples < 0 {\n\t\terr = fmt.Errorf(\"ffmpeg: avresample_convert_frame failed\")\n\t\treturn\n\t}\n\n\tout.SampleCount = convertSamples\n\tif convertSamples < outSampleCount {\n\t\tfor i := 0; i < outChannels; i++ {\n\t\t\tout.Data[i] = out.Data[i][:convertSamples*outBytesPerSample]\n\t\t}\n\t}\n\n\tif flush.SampleCount > 0 {\n\t\tout = flush.Concat(out)\n\t}\n\n\treturn\n}\n\nfunc (self *Resampler) Close() {\n\tC.avresample_free(&self.avr)\n}\n\ntype AudioEncoder struct {\n\tff *ffctx\n\tSampleRate int\n\tBitrate int\n\tChannelLayout av.ChannelLayout\n\tSampleFormat av.SampleFormat\n\tFrameSampleCount int\n\tframebuf av.AudioFrame\n\tcodecData av.AudioCodecData\n\tresampler *Resampler\n}\n\nfunc sampleFormatAV2FF(sampleFormat av.SampleFormat) (ffsamplefmt int32) {\n\tswitch sampleFormat {\n\tcase av.U8:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_U8\n\tcase av.S16:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_S16\n\tcase av.S32:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_S32\n\tcase av.FLT:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_FLT\n\tcase av.DBL:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_DBL\n\tcase av.U8P:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_U8P\n\tcase av.S16P:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_S16P\n\tcase av.S32P:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_S32P\n\tcase av.FLTP:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_FLTP\n\tcase av.DBLP:\n\t\tffsamplefmt = C.AV_SAMPLE_FMT_DBLP\n\t}\n\treturn\n}\n\nfunc sampleFormatFF2AV(ffsamplefmt int32) (sampleFormat av.SampleFormat) {\n\tswitch ffsamplefmt {\n\tcase C.AV_SAMPLE_FMT_U8:          ///< unsigned 8 bits\n\t\tsampleFormat = av.U8\n\tcase C.AV_SAMPLE_FMT_S16:         ///< signed 16 bits\n\t\tsampleFormat = av.S16\n\tcase C.AV_SAMPLE_FMT_S32:         ///< signed 32 bits\n\t\tsampleFormat = av.S32\n\tcase C.AV_SAMPLE_FMT_FLT:         ///< float\n\t\tsampleFormat = av.FLT\n\tcase C.AV_SAMPLE_FMT_DBL:         ///< double\n\t\tsampleFormat = av.DBL\n\tcase C.AV_SAMPLE_FMT_U8P:         ///< unsigned 8 bits, planar\n\t\tsampleFormat = av.U8P\n\tcase C.AV_SAMPLE_FMT_S16P:        ///< signed 16 bits, planar\n\t\tsampleFormat = av.S16P\n\tcase C.AV_SAMPLE_FMT_S32P:        ///< signed 32 bits, planar\n\t\tsampleFormat = av.S32P\n\tcase C.AV_SAMPLE_FMT_FLTP:        ///< float, planar\n\t\tsampleFormat = av.FLTP\n\tcase C.AV_SAMPLE_FMT_DBLP:        ///< double, planar\n\t\tsampleFormat = av.DBLP\n\t}\n\treturn\n}\n\nfunc (self *AudioEncoder) SetSampleFormat(fmt av.SampleFormat) (err error) {\n\tself.SampleFormat = fmt\n\treturn\n}\n\nfunc (self *AudioEncoder) SetSampleRate(rate int) (err error) {\n\tself.SampleRate = rate\n\treturn\n}\n\nfunc (self *AudioEncoder) SetChannelLayout(ch av.ChannelLayout) (err error) {\n\tself.ChannelLayout = ch\n\treturn\n}\n\nfunc (self *AudioEncoder) SetBitrate(bitrate int) (err error) {\n\tself.Bitrate = bitrate\n\treturn\n}\n\nfunc (self *AudioEncoder) SetOption(key string, val interface{}) (err error) {\n\tff := &self.ff.ff\n\n\tsval := fmt.Sprint(val)\n\tif key == \"profile\" {\n\t\tff.profile = C.avcodec_profile_name_to_int(ff.codec, C.CString(sval))\n\t\tif ff.profile == C.FF_PROFILE_UNKNOWN {\n\t\t\terr = fmt.Errorf(\"ffmpeg: profile `%s` invalid\", sval)\n\t\t\treturn\n\t\t}\n\t\treturn\n\t}\n\n\tC.av_dict_set(&ff.options, C.CString(key), C.CString(sval), 0)\n\treturn\n}\n\nfunc (self *AudioEncoder) GetOption(key string, val interface{}) (err error) {\n\tff := &self.ff.ff\n\tentry := C.av_dict_get(ff.options, C.CString(key), nil, 0)\n\tif entry == nil {\n\t\terr = fmt.Errorf(\"ffmpeg: GetOption failed: `%s` not exists\", key)\n\t\treturn\n\t}\n\tswitch p := val.(type) {\n\tcase *string:\n\t\t*p = C.GoString(entry.value)\n\tcase *int:\n\t\tfmt.Sscanf(C.GoString(entry.value), \"%d\", p)\n\tdefault:\n\t\terr = fmt.Errorf(\"ffmpeg: GetOption failed: val must be *string or *int receiver\")\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *AudioEncoder) Setup() (err error) {\n\tff := &self.ff.ff\n\n\tff.frame = C.av_frame_alloc()\n\n\tif self.SampleFormat == av.SampleFormat(0) {\n\t\tself.SampleFormat = sampleFormatFF2AV(*ff.codec.sample_fmts)\n\t}\n\n\t//if self.Bitrate == 0 {\n\t//\tself.Bitrate = 80000\n\t//}\n\tif self.SampleRate == 0 {\n\t\tself.SampleRate = 44100\n\t}\n\tif self.ChannelLayout == av.ChannelLayout(0) {\n\t\tself.ChannelLayout = av.CH_STEREO\n\t}\n\n\tff.codecCtx.sample_fmt = sampleFormatAV2FF(self.SampleFormat)\n\tff.codecCtx.sample_rate = C.int(self.SampleRate)\n\tff.codecCtx.bit_rate = C.int64_t(self.Bitrate)\n\tff.codecCtx.channel_layout = channelLayoutAV2FF(self.ChannelLayout)\n\tff.codecCtx.strict_std_compliance = C.FF_COMPLIANCE_EXPERIMENTAL\n\tff.codecCtx.flags = C.AV_CODEC_FLAG_GLOBAL_HEADER\n\tff.codecCtx.profile = ff.profile\n\n\tif C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 {\n\t\terr = fmt.Errorf(\"ffmpeg: encoder: avcodec_open2 failed\")\n\t\treturn\n\t}\n\tself.SampleFormat = sampleFormatFF2AV(ff.codecCtx.sample_fmt)\n\tself.FrameSampleCount = int(ff.codecCtx.frame_size)\n\n\textradata := C.GoBytes(unsafe.Pointer(ff.codecCtx.extradata), ff.codecCtx.extradata_size)\n\n\tswitch ff.codecCtx.codec_id {\n\tcase C.AV_CODEC_ID_AAC:\n\t\tif self.codecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(extradata); err != nil {\n\t\t\treturn\n\t\t}\n\n\tdefault:\n\t\tself.codecData = audioCodecData{\n\t\t\tchannelLayout: self.ChannelLayout,\n\t\t\tsampleFormat: self.SampleFormat,\n\t\t\tsampleRate: self.SampleRate,\n\t\t\tcodecId: ff.codecCtx.codec_id,\n\t\t\textradata: extradata,\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *AudioEncoder) prepare() (err error) {\n\tff := &self.ff.ff\n\n\tif ff.frame == nil {\n\t\tif err = self.Setup(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *AudioEncoder) CodecData() (codec av.AudioCodecData, err error) {\n\tif err = self.prepare(); err != nil {\n\t\treturn\n\t}\n\tcodec = self.codecData\n\treturn\n}\n\nfunc (self *AudioEncoder) encodeOne(frame av.AudioFrame) (gotpkt bool, pkt []byte, err error) {\n\tif err = self.prepare(); err != nil {\n\t\treturn\n\t}\n\n\tff := &self.ff.ff\n\n\tcpkt := C.AVPacket{}\n\tcgotpkt := C.int(0)\n\taudioFrameAssignToFF(frame, ff.frame)\n\n\tif false {\n\t\tfarr := []string{}\n\t\tfor i := 0; i < len(frame.Data[0])/4; i++ {\n\t\t\tvar f *float64 = (*float64)(unsafe.Pointer(&frame.Data[0][i*4]))\n\t\t\tfarr = append(farr, fmt.Sprintf(\"%.8f\", *f))\n\t\t}\n\t\tfmt.Println(farr)\n\t}\n\tcerr := C.avcodec_encode_audio2(ff.codecCtx, &cpkt, ff.frame, &cgotpkt)\n\tif cerr < C.int(0) {\n\t\terr = fmt.Errorf(\"ffmpeg: avcodec_encode_audio2 failed: %d\", cerr)\n\t\treturn\n\t}\n\n\tif cgotpkt != 0 {\n\t\tgotpkt = true\n\t\tpkt = C.GoBytes(unsafe.Pointer(cpkt.data), cpkt.size)\n\t\tC.av_packet_unref(&cpkt)\n\n\t\tif debug {\n\t\t\tfmt.Println(\"ffmpeg: Encode\", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat, \"len\", len(pkt))\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *AudioEncoder) resample(in av.AudioFrame) (out av.AudioFrame, err error) {\n\tif self.resampler == nil {\n\t\tself.resampler = &Resampler{\n\t\t\tOutSampleFormat: self.SampleFormat,\n\t\t\tOutSampleRate: self.SampleRate,\n\t\t\tOutChannelLayout: self.ChannelLayout,\n\t\t}\n\t}\n\tif out, err = self.resampler.Resample(in); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *AudioEncoder) Encode(frame av.AudioFrame) (pkts [][]byte, err error) {\n\tvar gotpkt bool\n\tvar pkt []byte\n\n\tif frame.SampleFormat != self.SampleFormat || frame.ChannelLayout != self.ChannelLayout || frame.SampleRate != self.SampleRate {\n\t\tif frame, err = self.resample(frame); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif self.FrameSampleCount != 0 {\n\t\tif self.framebuf.SampleCount == 0 {\n\t\t\tself.framebuf = frame\n\t\t} else {\n\t\t\tself.framebuf = self.framebuf.Concat(frame)\n\t\t}\n\t\tfor self.framebuf.SampleCount >= self.FrameSampleCount {\n\t\t\tframe := self.framebuf.Slice(0, self.FrameSampleCount)\n\t\t\tif gotpkt, pkt, err = self.encodeOne(frame); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif gotpkt {\n\t\t\t\tpkts = append(pkts, pkt)\n\t\t\t}\n\t\t\tself.framebuf = self.framebuf.Slice(self.FrameSampleCount, self.framebuf.SampleCount)\n\t\t}\n\t} else {\n\t\tif gotpkt, pkt, err = self.encodeOne(frame); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif gotpkt {\n\t\t\tpkts = append(pkts, pkt)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *AudioEncoder) Close() {\n\tfreeFFCtx(self.ff)\n\tif self.resampler != nil {\n\t\tself.resampler.Close()\n\t\tself.resampler = nil\n\t}\n}\n\nfunc audioFrameAssignToAVParams(f *C.AVFrame, frame *av.AudioFrame) {\n\tframe.SampleFormat = sampleFormatFF2AV(int32(f.format))\n\tframe.ChannelLayout = channelLayoutFF2AV(f.channel_layout)\n\tframe.SampleRate = int(f.sample_rate)\n}\n\nfunc audioFrameAssignToAVData(f *C.AVFrame, frame *av.AudioFrame) {\n\tframe.SampleCount = int(f.nb_samples)\n\tframe.Data = make([][]byte, int(f.channels))\n\tfor i := 0; i < int(f.channels); i++ {\n\t\tframe.Data[i] = C.GoBytes(unsafe.Pointer(f.data[i]), f.linesize[0])\n\t}\n}\n\nfunc audioFrameAssignToAV(f *C.AVFrame, frame *av.AudioFrame) {\n\taudioFrameAssignToAVParams(f, frame)\n\taudioFrameAssignToAVData(f, frame)\n}\n\nfunc audioFrameAssignToFFParams(frame av.AudioFrame, f *C.AVFrame) {\n\tf.format = C.int(sampleFormatAV2FF(frame.SampleFormat))\n\tf.channel_layout = channelLayoutAV2FF(frame.ChannelLayout)\n\tf.sample_rate = C.int(frame.SampleRate)\n\tf.channels = C.int(frame.ChannelLayout.Count())\n}\n\nfunc audioFrameAssignToFFData(frame av.AudioFrame, f *C.AVFrame) {\n\tf.nb_samples = C.int(frame.SampleCount)\n\tfor i := range frame.Data {\n\t\tf.data[i] = (*C.uint8_t)(unsafe.Pointer(&frame.Data[i][0]))\n\t\tf.linesize[i] = C.int(len(frame.Data[i]))\n\t}\n}\n\nfunc audioFrameAssignToFF(frame av.AudioFrame, f *C.AVFrame) {\n\taudioFrameAssignToFFParams(frame, f)\n\taudioFrameAssignToFFData(frame, f)\n}\n\nfunc channelLayoutFF2AV(layout C.uint64_t) (channelLayout av.ChannelLayout) {\n\tif layout & C.AV_CH_FRONT_CENTER != 0 {\n\t\tchannelLayout |= av.CH_FRONT_CENTER\n\t}\n\tif layout & C.AV_CH_FRONT_LEFT != 0 {\n\t\tchannelLayout |= av.CH_FRONT_LEFT\n\t}\n\tif layout & C.AV_CH_FRONT_RIGHT != 0 {\n\t\tchannelLayout |= av.CH_FRONT_RIGHT\n\t}\n\tif layout & C.AV_CH_BACK_CENTER != 0 {\n\t\tchannelLayout |= av.CH_BACK_CENTER\n\t}\n\tif layout & C.AV_CH_BACK_LEFT != 0 {\n\t\tchannelLayout |= av.CH_BACK_LEFT\n\t}\n\tif layout & C.AV_CH_BACK_RIGHT != 0 {\n\t\tchannelLayout |= av.CH_BACK_RIGHT\n\t}\n\tif layout & C.AV_CH_SIDE_LEFT != 0 {\n\t\tchannelLayout |= av.CH_SIDE_LEFT\n\t}\n\tif layout & C.AV_CH_SIDE_RIGHT != 0 {\n\t\tchannelLayout |= av.CH_SIDE_RIGHT\n\t}\n\tif layout & C.AV_CH_LOW_FREQUENCY != 0 {\n\t\tchannelLayout |= av.CH_LOW_FREQ\n\t}\n\treturn\n}\n\nfunc channelLayoutAV2FF(channelLayout av.ChannelLayout) (layout C.uint64_t) {\n\tif channelLayout & av.CH_FRONT_CENTER != 0 {\n\t\tlayout |= C.AV_CH_FRONT_CENTER\n\t}\n\tif channelLayout & av.CH_FRONT_LEFT != 0 {\n\t\tlayout |= C.AV_CH_FRONT_LEFT\n\t}\n\tif channelLayout & av.CH_FRONT_RIGHT != 0 {\n\t\tlayout |= C.AV_CH_FRONT_RIGHT\n\t}\n\tif channelLayout & av.CH_BACK_CENTER != 0 {\n\t\tlayout |= C.AV_CH_BACK_CENTER\n\t}\n\tif channelLayout & av.CH_BACK_LEFT != 0 {\n\t\tlayout |= C.AV_CH_BACK_LEFT\n\t}\n\tif channelLayout & av.CH_BACK_RIGHT != 0 {\n\t\tlayout |= C.AV_CH_BACK_RIGHT\n\t}\n\tif channelLayout & av.CH_SIDE_LEFT != 0 {\n\t\tlayout |= C.AV_CH_SIDE_LEFT\n\t}\n\tif channelLayout & av.CH_SIDE_RIGHT != 0 {\n\t\tlayout |= C.AV_CH_SIDE_RIGHT\n\t}\n\tif channelLayout & av.CH_LOW_FREQ != 0 {\n\t\tlayout |= C.AV_CH_LOW_FREQUENCY\n\t}\n\treturn\n}\n\ntype AudioDecoder struct {\n\tff *ffctx\n\tChannelLayout av.ChannelLayout\n\tSampleFormat av.SampleFormat\n\tSampleRate int\n\tExtradata []byte\n}\n\nfunc (self *AudioDecoder) Setup() (err error) {\n\tff := &self.ff.ff\n\n\tff.frame = C.av_frame_alloc()\n\n\tif len(self.Extradata) > 0 {\n\t\tff.codecCtx.extradata = (*C.uint8_t)(unsafe.Pointer(&self.Extradata[0]))\n\t\tff.codecCtx.extradata_size = C.int(len(self.Extradata))\n\t}\n\tif debug {\n\t\tfmt.Println(\"ffmpeg: Decoder.Setup Extradata.len\", len(self.Extradata))\n\t}\n\n\tff.codecCtx.sample_rate = C.int(self.SampleRate)\n\tff.codecCtx.channel_layout = channelLayoutAV2FF(self.ChannelLayout)\n\tff.codecCtx.channels = C.int(self.ChannelLayout.Count())\n\tif C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 {\n\t\terr = fmt.Errorf(\"ffmpeg: decoder: avcodec_open2 failed\")\n\t\treturn\n\t}\n\tself.SampleFormat = sampleFormatFF2AV(ff.codecCtx.sample_fmt)\n\tself.ChannelLayout = channelLayoutFF2AV(ff.codecCtx.channel_layout)\n\tif self.SampleRate == 0 {\n\t\tself.SampleRate = int(ff.codecCtx.sample_rate)\n\t}\n\n\treturn\n}\n\nfunc (self *AudioDecoder) Decode(pkt []byte) (gotframe bool, frame av.AudioFrame, err error) {\n\tff := &self.ff.ff\n\n\tcgotframe := C.int(0)\n\tcerr := C.wrap_avcodec_decode_audio4(ff.codecCtx, ff.frame, unsafe.Pointer(&pkt[0]), C.int(len(pkt)), &cgotframe)\n\tif cerr < C.int(0) {\n\t\terr = fmt.Errorf(\"ffmpeg: avcodec_decode_audio4 failed: %d\", cerr)\n\t\treturn\n\t}\n\n\tif cgotframe != C.int(0) {\n\t\tgotframe = true\n\t\taudioFrameAssignToAV(ff.frame, &frame)\n\t\tframe.SampleRate = self.SampleRate\n\n\t\tif debug {\n\t\t\tfmt.Println(\"ffmpeg: Decode\", frame.SampleCount, frame.SampleRate, frame.ChannelLayout, frame.SampleFormat)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *AudioDecoder) Close() {\n\tfreeFFCtx(self.ff)\n}\n\nfunc NewAudioEncoderByCodecType(typ av.CodecType) (enc *AudioEncoder, err error) {\n\tvar id uint32\n\n\tswitch typ {\n\tcase av.AAC:\n\t\tid = C.AV_CODEC_ID_AAC\n\n\tdefault:\n\t\terr = fmt.Errorf(\"ffmpeg: cannot find encoder codecType=%d\", typ)\n\t\treturn\n\t}\n\n\tcodec := C.avcodec_find_encoder(id)\n\tif codec == nil || C.avcodec_get_type(id) != C.AVMEDIA_TYPE_AUDIO {\n\t\terr = fmt.Errorf(\"ffmpeg: cannot find audio encoder codecId=%d\", id)\n\t\treturn\n\t}\n\n\t_enc := &AudioEncoder{}\n\tif _enc.ff, err = newFFCtxByCodec(codec); err != nil {\n\t\treturn\n\t}\n\tenc = _enc\n\treturn\n}\n\nfunc NewAudioEncoderByName(name string) (enc *AudioEncoder, err error) {\n\t_enc := &AudioEncoder{}\n\n\tcodec := C.avcodec_find_encoder_by_name(C.CString(name))\n\tif codec == nil || C.avcodec_get_type(codec.id) != C.AVMEDIA_TYPE_AUDIO {\n\t\terr = fmt.Errorf(\"ffmpeg: cannot find audio encoder name=%s\", name)\n\t\treturn\n\t}\n\n\tif _enc.ff, err = newFFCtxByCodec(codec); err != nil {\n\t\treturn\n\t}\n\tenc = _enc\n\treturn\n}\n\nfunc NewAudioDecoder(codec av.AudioCodecData) (dec *AudioDecoder, err error) {\n\t_dec := &AudioDecoder{}\n\tvar id uint32\n\n\tswitch codec.Type() {\n\tcase av.AAC:\n\t\tif aaccodec, ok := codec.(aacparser.CodecData); ok {\n\t\t\t_dec.Extradata = aaccodec.MPEG4AudioConfigBytes()\n\t\t\tid = C.AV_CODEC_ID_AAC\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"ffmpeg: aac CodecData must be aacparser.CodecData\")\n\t\t\treturn\n\t\t}\n\n\tcase av.SPEEX:\n\t\tid = C.AV_CODEC_ID_SPEEX\n\n\tcase av.PCM_MULAW:\n\t\tid = C.AV_CODEC_ID_PCM_MULAW\n\n\tcase av.PCM_ALAW:\n\t\tid = C.AV_CODEC_ID_PCM_ALAW\n\n\tdefault:\n\t\tif ffcodec, ok := codec.(audioCodecData); ok {\n\t\t\t_dec.Extradata = ffcodec.extradata\n\t\t\tid = ffcodec.codecId\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"ffmpeg: invalid CodecData for ffmpeg to decode\")\n\t\t\treturn\n\t\t}\n\t}\n\n\tc := C.avcodec_find_decoder(id)\n\tif c == nil || C.avcodec_get_type(c.id) != C.AVMEDIA_TYPE_AUDIO {\n\t\terr = fmt.Errorf(\"ffmpeg: cannot find audio decoder id=%d\", id)\n\t\treturn\n\t}\n\n\tif _dec.ff, err = newFFCtxByCodec(c); err != nil {\n\t\treturn\n\t}\n\n\t_dec.SampleFormat = codec.SampleFormat()\n\t_dec.SampleRate = codec.SampleRate()\n\t_dec.ChannelLayout = codec.ChannelLayout()\n\tif err = _dec.Setup(); err != nil {\n\t\treturn\n\t}\n\n\tdec = _dec\n\treturn\n}\n\ntype audioCodecData struct {\n\tcodecId uint32\n\tsampleFormat av.SampleFormat\n\tchannelLayout av.ChannelLayout\n\tsampleRate int\n\textradata []byte\n}\n\nfunc (self audioCodecData) Type() av.CodecType {\n\treturn av.MakeAudioCodecType(self.codecId)\n}\n\nfunc (self audioCodecData) SampleRate() int {\n\treturn self.sampleRate\n}\n\nfunc (self audioCodecData) SampleFormat() av.SampleFormat {\n\treturn self.sampleFormat\n}\n\nfunc (self audioCodecData) ChannelLayout() av.ChannelLayout {\n\treturn self.channelLayout\n}\n\nfunc (self audioCodecData) PacketDuration(data []byte) (dur time.Duration, err error) {\n\t// TODO: implement it: ffmpeg get_audio_frame_duration\n\terr = fmt.Errorf(\"ffmpeg: cannot get packet duration\")\n\treturn\n}\n\nfunc AudioCodecHandler(h *avutil.RegisterHandler) {\n\th.AudioDecoder = func(codec av.AudioCodecData) (av.AudioDecoder, error) {\n\t\tif dec, err := NewAudioDecoder(codec); err != nil {\n\t\t\treturn nil, nil\n\t\t} else {\n\t\t\treturn dec, err\n\t\t}\n\t}\n\n\th.AudioEncoder = func(typ av.CodecType) (av.AudioEncoder, error) {\n\t\tif enc, err := NewAudioEncoderByCodecType(typ); err != nil {\n\t\t\treturn nil, nil\n\t\t} else {\n\t\t\treturn enc, err\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "cgo/ffmpeg/ffmpeg.go",
    "content": "package ffmpeg\n\n/*\n#cgo LDFLAGS: -lavformat -lavutil -lavcodec -lavresample -lswscale\n#include \"ffmpeg.h\"\nvoid ffinit() {\n\tav_register_all();\n}\n*/\nimport \"C\"\nimport (\n\t\"runtime\"\n\t\"unsafe\"\n)\n\nconst (\n\tQUIET = int(C.AV_LOG_QUIET)\n\tPANIC = int(C.AV_LOG_PANIC)\n\tFATAL = int(C.AV_LOG_FATAL)\n\tERROR = int(C.AV_LOG_ERROR)\n\tWARNING = int(C.AV_LOG_WARNING)\n\tINFO = int(C.AV_LOG_INFO)\n\tVERBOSE = int(C.AV_LOG_VERBOSE)\n\tDEBUG = int(C.AV_LOG_DEBUG)\n\tTRACE = int(C.AV_LOG_TRACE)\n)\n\nfunc HasEncoder(name string) bool {\n\treturn C.avcodec_find_encoder_by_name(C.CString(name)) != nil\n}\n\nfunc HasDecoder(name string) bool {\n\treturn C.avcodec_find_decoder_by_name(C.CString(name)) != nil\n}\n\n//func EncodersList() []string\n//func DecodersList() []string\n\nfunc SetLogLevel(level int) {\n\tC.av_log_set_level(C.int(level))\n}\n\nfunc init() {\n\tC.ffinit()\n}\n\ntype ffctx struct {\n\tff C.FFCtx\n}\n\nfunc newFFCtxByCodec(codec *C.AVCodec) (ff *ffctx, err error) {\n\tff = &ffctx{}\n\tff.ff.codec = codec\n\tff.ff.codecCtx = C.avcodec_alloc_context3(codec)\n\tff.ff.profile = C.FF_PROFILE_UNKNOWN\n\truntime.SetFinalizer(ff, freeFFCtx)\n\treturn\n}\n\nfunc freeFFCtx(self *ffctx) {\n\tff := &self.ff\n\tif ff.frame != nil {\n\t\tC.av_frame_free(&ff.frame)\n\t}\n\tif ff.codecCtx != nil {\n\t\tC.avcodec_close(ff.codecCtx)\n\t\tC.av_free(unsafe.Pointer(ff.codecCtx))\n\t\tff.codecCtx = nil\n\t}\n\tif ff.options != nil {\n\t\tC.av_dict_free(&ff.options)\n\t}\n}\n\n"
  },
  {
    "path": "cgo/ffmpeg/ffmpeg.h",
    "content": "\n#include <libavformat/avformat.h>\n#include <libavcodec/avcodec.h>\n#include <libavutil/avutil.h>\n#include <libavresample/avresample.h>\n#include <libavutil/opt.h>\n#include <string.h>\n#include <libswscale/swscale.h>\n\ntypedef struct {\n\tAVCodec *codec;\n\tAVCodecContext *codecCtx;\n\tAVFrame *frame;\n\tAVDictionary *options;\n\tint profile;\n} FFCtx;\n\nstatic inline int avcodec_profile_name_to_int(AVCodec *codec, const char *name) {\n\tconst AVProfile *p;\n\tfor (p = codec->profiles; p != NULL && p->profile != FF_PROFILE_UNKNOWN; p++)\n\t\tif (!strcasecmp(p->name, name))\n\t\t\treturn p->profile;\n\treturn FF_PROFILE_UNKNOWN;\n}\n\n"
  },
  {
    "path": "cgo/ffmpeg/video.go",
    "content": "package ffmpeg\n\n/*\n#include \"ffmpeg.h\"\nint wrap_avcodec_decode_video2(AVCodecContext *ctx, AVFrame *frame, void *data, int size, int *got) {\n\tstruct AVPacket pkt = {.data = data, .size = size};\n\treturn avcodec_decode_video2(ctx, frame, got, &pkt);\n}\n*/\nimport \"C\"\nimport (\n\t\"unsafe\"\n\t\"runtime\"\n\t\"fmt\"\n\t\"image\"\n\t\"reflect\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/codec/h264parser\"\n)\n\ntype VideoDecoder struct {\n\tff *ffctx\n\tExtradata []byte\n}\n\nfunc (self *VideoDecoder) Setup() (err error) {\n\tff := &self.ff.ff\n\tif len(self.Extradata) > 0 {\n\t\tff.codecCtx.extradata = (*C.uint8_t)(unsafe.Pointer(&self.Extradata[0]))\n\t\tff.codecCtx.extradata_size = C.int(len(self.Extradata))\n\t}\n\tif C.avcodec_open2(ff.codecCtx, ff.codec, nil) != 0 {\n\t\terr = fmt.Errorf(\"ffmpeg: decoder: avcodec_open2 failed\")\n\t\treturn\n\t}\n\treturn\n}\n\nfunc fromCPtr(buf unsafe.Pointer, size int) (ret []uint8) {\n\thdr := (*reflect.SliceHeader)((unsafe.Pointer(&ret)))\n\thdr.Cap = size\n\thdr.Len = size\n\thdr.Data = uintptr(buf)\n\treturn\n}\n\ntype VideoFrame struct {\n\tImage image.YCbCr\n\tframe *C.AVFrame\n}\n\nfunc (self *VideoFrame) Free() {\n\tself.Image = image.YCbCr{}\n\tC.av_frame_free(&self.frame)\n}\n\nfunc freeVideoFrame(self *VideoFrame) {\n\tself.Free()\n}\n\nfunc (self *VideoDecoder) Decode(pkt []byte) (img *VideoFrame, err error) {\n\tff := &self.ff.ff\n\n\tcgotimg := C.int(0)\n\tframe := C.av_frame_alloc()\n\tcerr := C.wrap_avcodec_decode_video2(ff.codecCtx, frame, unsafe.Pointer(&pkt[0]), C.int(len(pkt)), &cgotimg)\n\tif cerr < C.int(0) {\n\t\terr = fmt.Errorf(\"ffmpeg: avcodec_decode_video2 failed: %d\", cerr)\n\t\treturn\n\t}\n\n\tif cgotimg != C.int(0) {\n\t\tw := int(frame.width)\n\t\th := int(frame.height)\n\t\tys := int(frame.linesize[0])\n\t\tcs := int(frame.linesize[1])\n\n\t\timg = &VideoFrame{Image: image.YCbCr{\n\t\t\tY: fromCPtr(unsafe.Pointer(frame.data[0]), ys*h),\n\t\t\tCb: fromCPtr(unsafe.Pointer(frame.data[1]), cs*h/2),\n\t\t\tCr: fromCPtr(unsafe.Pointer(frame.data[2]), cs*h/2),\n\t\t\tYStride: ys,\n\t\t\tCStride: cs,\n\t\t\tSubsampleRatio: image.YCbCrSubsampleRatio420,\n\t\t\tRect: image.Rect(0, 0, w, h),\n\t\t}, frame: frame}\n\t\truntime.SetFinalizer(img, freeVideoFrame)\n\t}\n\n\treturn\n}\n\nfunc NewVideoDecoder(stream av.CodecData) (dec *VideoDecoder, err error) {\n\t_dec := &VideoDecoder{}\n\tvar id uint32\n\n\tswitch stream.Type() {\n\tcase av.H264:\n\t\th264 := stream.(h264parser.CodecData)\n\t\t_dec.Extradata = h264.AVCDecoderConfRecordBytes()\n\t\tid = C.AV_CODEC_ID_H264\n\n\tdefault:\n\t\terr = fmt.Errorf(\"ffmpeg: NewVideoDecoder codec=%v unsupported\", stream.Type())\n\t\treturn\n\t}\n\n\tc := C.avcodec_find_decoder(id)\n\tif c == nil || C.avcodec_get_type(id) != C.AVMEDIA_TYPE_VIDEO {\n\t\terr = fmt.Errorf(\"ffmpeg: cannot find video decoder codecId=%d\", id)\n\t\treturn\n\t}\n\n\tif _dec.ff, err = newFFCtxByCodec(c); err != nil {\n\t\treturn\n\t}\n\tif err =  _dec.Setup(); err != nil {\n\t\treturn\n\t}\n\n\tdec = _dec\n\treturn\n}\n\n"
  },
  {
    "path": "codec/aacparser/parser.go",
    "content": "package aacparser\n\nimport (\n\t\"github.com/nareix/joy4/utils/bits\"\n\t\"github.com/nareix/joy4/av\"\n\t\"time\"\n\t\"fmt\"\n\t\"bytes\"\n\t\"io\"\n)\n\n// copied from libavcodec/mpeg4audio.h\nconst (\n\tAOT_AAC_MAIN        = 1 + iota  ///< Y                       Main\n\tAOT_AAC_LC                      ///< Y                       Low Complexity\n\tAOT_AAC_SSR                     ///< N (code in SoC repo)    Scalable Sample Rate\n\tAOT_AAC_LTP                     ///< Y                       Long Term Prediction\n\tAOT_SBR                         ///< Y                       Spectral Band Replication\n\tAOT_AAC_SCALABLE                ///< N                       Scalable\n\tAOT_TWINVQ                      ///< N                       Twin Vector Quantizer\n\tAOT_CELP                        ///< N                       Code Excited Linear Prediction\n\tAOT_HVXC                        ///< N                       Harmonic Vector eXcitation Coding\n\tAOT_TTSI            = 12 + iota ///< N                       Text-To-Speech Interface\n\tAOT_MAINSYNTH                   ///< N                       Main Synthesis\n\tAOT_WAVESYNTH                   ///< N                       Wavetable Synthesis\n\tAOT_MIDI                        ///< N                       General MIDI\n\tAOT_SAFX                        ///< N                       Algorithmic Synthesis and Audio Effects\n\tAOT_ER_AAC_LC                   ///< N                       Error Resilient Low Complexity\n\tAOT_ER_AAC_LTP      = 19 + iota ///< N                       Error Resilient Long Term Prediction\n\tAOT_ER_AAC_SCALABLE             ///< N                       Error Resilient Scalable\n\tAOT_ER_TWINVQ                   ///< N                       Error Resilient Twin Vector Quantizer\n\tAOT_ER_BSAC                     ///< N                       Error Resilient Bit-Sliced Arithmetic Coding\n\tAOT_ER_AAC_LD                   ///< N                       Error Resilient Low Delay\n\tAOT_ER_CELP                     ///< N                       Error Resilient Code Excited Linear Prediction\n\tAOT_ER_HVXC                     ///< N                       Error Resilient Harmonic Vector eXcitation Coding\n\tAOT_ER_HILN                     ///< N                       Error Resilient Harmonic and Individual Lines plus Noise\n\tAOT_ER_PARAM                    ///< N                       Error Resilient Parametric\n\tAOT_SSC                         ///< N                       SinuSoidal Coding\n\tAOT_PS                          ///< N                       Parametric Stereo\n\tAOT_SURROUND                    ///< N                       MPEG Surround\n\tAOT_ESCAPE                      ///< Y                       Escape Value\n\tAOT_L1                          ///< Y                       Layer 1\n\tAOT_L2                          ///< Y                       Layer 2\n\tAOT_L3                          ///< Y                       Layer 3\n\tAOT_DST                         ///< N                       Direct Stream Transfer\n\tAOT_ALS                         ///< Y                       Audio LosslesS\n\tAOT_SLS                         ///< N                       Scalable LosslesS\n\tAOT_SLS_NON_CORE                ///< N                       Scalable LosslesS (non core)\n\tAOT_ER_AAC_ELD                  ///< N                       Error Resilient Enhanced Low Delay\n\tAOT_SMR_SIMPLE                  ///< N                       Symbolic Music Representation Simple\n\tAOT_SMR_MAIN                    ///< N                       Symbolic Music Representation Main\n\tAOT_USAC_NOSBR                  ///< N                       Unified Speech and Audio Coding (no SBR)\n\tAOT_SAOC                        ///< N                       Spatial Audio Object Coding\n\tAOT_LD_SURROUND                 ///< N                       Low Delay MPEG Surround\n\tAOT_USAC                        ///< N                       Unified Speech and Audio Coding\n)\n\ntype MPEG4AudioConfig struct {\n\tSampleRate      int\n\tChannelLayout   av.ChannelLayout\n\tObjectType      uint\n\tSampleRateIndex uint\n\tChannelConfig   uint\n}\n\nvar sampleRateTable = []int{\n\t96000, 88200, 64000, 48000, 44100, 32000,\n\t24000, 22050, 16000, 12000, 11025, 8000, 7350,\n}\n\n/*\nThese are the channel configurations:\n0: Defined in AOT Specifc Config\n1: 1 channel: front-center\n2: 2 channels: front-left, front-right\n3: 3 channels: front-center, front-left, front-right\n4: 4 channels: front-center, front-left, front-right, back-center\n5: 5 channels: front-center, front-left, front-right, back-left, back-right\n6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel\n7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel\n8-15: Reserved\n*/\nvar chanConfigTable = []av.ChannelLayout{\n\t0,\n\tav.CH_FRONT_CENTER,\n\tav.CH_FRONT_LEFT|av.CH_FRONT_RIGHT,\n\tav.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT,\n\tav.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT|av.CH_BACK_CENTER,\n\tav.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT|av.CH_BACK_LEFT|av.CH_BACK_RIGHT,\n\tav.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT|av.CH_BACK_LEFT|av.CH_BACK_RIGHT|av.CH_LOW_FREQ,\n\tav.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT|av.CH_SIDE_LEFT|av.CH_SIDE_RIGHT|av.CH_BACK_LEFT|av.CH_BACK_RIGHT|av.CH_LOW_FREQ,\n}\n\nfunc ParseADTSHeader(frame []byte) (config MPEG4AudioConfig, hdrlen int, framelen int, samples int, err error) {\n\tif frame[0] != 0xff || frame[1]&0xf6 != 0xf0 {\n\t\terr = fmt.Errorf(\"aacparser: not adts header\")\n\t\treturn\n\t}\n\tconfig.ObjectType = uint(frame[2]>>6) + 1\n\tconfig.SampleRateIndex = uint(frame[2] >> 2 & 0xf)\n\tconfig.ChannelConfig = uint(frame[2]<<2&0x4 | frame[3]>>6&0x3)\n\tif config.ChannelConfig == uint(0) {\n\t\terr = fmt.Errorf(\"aacparser: adts channel count invalid\")\n\t\treturn\n\t}\n\t(&config).Complete()\n\tframelen = int(frame[3]&0x3)<<11 | int(frame[4])<<3 | int(frame[5]>>5)\n\tsamples = (int(frame[6]&0x3) + 1) * 1024\n\thdrlen = 7\n\tif frame[1]&0x1 == 0 {\n\t\thdrlen = 9\n\t}\n\tif framelen < hdrlen {\n\t\terr = fmt.Errorf(\"aacparser: adts framelen < hdrlen\")\n\t\treturn\n\t}\n\treturn\n}\n\nconst ADTSHeaderLength = 7\n\nfunc FillADTSHeader(header []byte, config MPEG4AudioConfig, samples int, payloadLength int) {\n\tpayloadLength += 7\n\t//AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)\n\theader[0] = 0xff\n\theader[1] = 0xf1\n\theader[2] = 0x50\n\theader[3] = 0x80\n\theader[4] = 0x43\n\theader[5] = 0xff\n\theader[6] = 0xcd\n\t//config.ObjectType = uint(frames[2]>>6)+1\n\t//config.SampleRateIndex = uint(frames[2]>>2&0xf)\n\t//config.ChannelConfig = uint(frames[2]<<2&0x4|frames[3]>>6&0x3)\n\theader[2] = (byte(config.ObjectType-1)&0x3)<<6 | (byte(config.SampleRateIndex)&0xf)<<2 | byte(config.ChannelConfig>>2)&0x1\n\theader[3] = header[3]&0x3f | byte(config.ChannelConfig&0x3)<<6\n\theader[3] = header[3]&0xfc | byte(payloadLength>>11)&0x3\n\theader[4] = byte(payloadLength >> 3)\n\theader[5] = header[5]&0x1f | (byte(payloadLength)&0x7)<<5\n\theader[6] = header[6]&0xfc | byte(samples/1024-1)\n\treturn\n}\n\nfunc readObjectType(r *bits.Reader) (objectType uint, err error) {\n\tif objectType, err = r.ReadBits(5); err != nil {\n\t\treturn\n\t}\n\tif objectType == AOT_ESCAPE {\n\t\tvar i uint\n\t\tif i, err = r.ReadBits(6); err != nil {\n\t\t\treturn\n\t\t}\n\t\tobjectType = 32 + i\n\t}\n\treturn\n}\n\nfunc writeObjectType(w *bits.Writer, objectType uint) (err error) {\n\tif objectType >= 32 {\n\t\tif err = w.WriteBits(AOT_ESCAPE, 5); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = w.WriteBits(objectType-32, 6); err != nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif err = w.WriteBits(objectType, 5); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc readSampleRateIndex(r *bits.Reader) (index uint, err error) {\n\tif index, err = r.ReadBits(4); err != nil {\n\t\treturn\n\t}\n\tif index == 0xf {\n\t\tif index, err = r.ReadBits(24); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc writeSampleRateIndex(w *bits.Writer, index uint) (err error) {\n\tif index >= 0xf {\n\t\tif err = w.WriteBits(0xf, 4); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = w.WriteBits(index, 24); err != nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif err = w.WriteBits(index, 4); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self MPEG4AudioConfig) IsValid() bool {\n\treturn self.ObjectType > 0\n}\n\nfunc (self *MPEG4AudioConfig) Complete() {\n\tif int(self.SampleRateIndex) < len(sampleRateTable) {\n\t\tself.SampleRate = sampleRateTable[self.SampleRateIndex]\n\t}\n\tif int(self.ChannelConfig) < len(chanConfigTable) {\n\t\tself.ChannelLayout = chanConfigTable[self.ChannelConfig]\n\t}\n\treturn\n}\n\nfunc ParseMPEG4AudioConfigBytes(data []byte) (config MPEG4AudioConfig, err error) {\n\t// copied from libavcodec/mpeg4audio.c avpriv_mpeg4audio_get_config()\n\tr := bytes.NewReader(data)\n\tbr := &bits.Reader{R: r}\n\tif config.ObjectType, err = readObjectType(br); err != nil {\n\t\treturn\n\t}\n\tif config.SampleRateIndex, err = readSampleRateIndex(br); err != nil {\n\t\treturn\n\t}\n\tif config.ChannelConfig, err = br.ReadBits(4); err != nil {\n\t\treturn\n\t}\n\t(&config).Complete()\n\treturn\n}\n\nfunc WriteMPEG4AudioConfig(w io.Writer, config MPEG4AudioConfig) (err error) {\n\tbw := &bits.Writer{W: w}\n\tif err = writeObjectType(bw, config.ObjectType); err != nil {\n\t\treturn\n\t}\n\n\tif config.SampleRateIndex == 0 {\n\t\tfor i, rate := range sampleRateTable {\n\t\t\tif rate == config.SampleRate {\n\t\t\t\tconfig.SampleRateIndex = uint(i)\n\t\t\t}\n\t\t}\n\t}\n\tif err = writeSampleRateIndex(bw, config.SampleRateIndex); err != nil {\n\t\treturn\n\t}\n\n\tif config.ChannelConfig == 0 {\n\t\tfor i, layout := range chanConfigTable {\n\t\t\tif layout == config.ChannelLayout {\n\t\t\t\tconfig.ChannelConfig = uint(i)\n\t\t\t}\n\t\t}\n\t}\n\tif err = bw.WriteBits(config.ChannelConfig, 4); err != nil {\n\t\treturn\n\t}\n\n\tif err = bw.FlushBits(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\ntype CodecData struct {\n\tConfigBytes []byte\n\tConfig MPEG4AudioConfig\n}\n\nfunc (self CodecData) Type() av.CodecType {\n\treturn av.AAC\n}\n\nfunc (self CodecData) MPEG4AudioConfigBytes() []byte {\n\treturn self.ConfigBytes\n}\n\nfunc (self CodecData) ChannelLayout() av.ChannelLayout {\n\treturn self.Config.ChannelLayout\n}\n\nfunc (self CodecData) SampleRate() int {\n\treturn self.Config.SampleRate\n}\n\nfunc (self CodecData) SampleFormat() av.SampleFormat {\n\treturn av.FLTP\n}\n\nfunc (self CodecData) PacketDuration(data []byte) (dur time.Duration, err error) {\n\tdur = time.Duration(1024) * time.Second / time.Duration(self.Config.SampleRate)\n\treturn\n}\n\nfunc NewCodecDataFromMPEG4AudioConfig(config MPEG4AudioConfig) (self CodecData, err error) {\n\tb := &bytes.Buffer{}\n\tWriteMPEG4AudioConfig(b, config)\n\treturn NewCodecDataFromMPEG4AudioConfigBytes(b.Bytes())\n}\n\nfunc NewCodecDataFromMPEG4AudioConfigBytes(config []byte) (self CodecData, err error) {\n\tself.ConfigBytes = config\n\tif self.Config, err = ParseMPEG4AudioConfigBytes(config); err != nil {\n\t\terr = fmt.Errorf(\"aacparser: parse MPEG4AudioConfig failed(%s)\", err)\n\t\treturn\n\t}\n\treturn\n}\n\n"
  },
  {
    "path": "codec/codec.go",
    "content": "package codec\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/codec/fake\"\n\t\"time\"\n)\n\ntype PCMUCodecData struct {\n\ttyp av.CodecType\n}\n\nfunc (self PCMUCodecData) Type() av.CodecType {\n\treturn self.typ\n}\n\nfunc (self PCMUCodecData) SampleRate() int {\n\treturn 8000\n}\n\nfunc (self PCMUCodecData) ChannelLayout() av.ChannelLayout {\n\treturn av.CH_MONO\n}\n\nfunc (self PCMUCodecData) SampleFormat() av.SampleFormat {\n\treturn av.S16\n}\n\nfunc (self PCMUCodecData) PacketDuration(data []byte) (time.Duration, error) {\n\treturn time.Duration(len(data)) * time.Second / time.Duration(8000), nil\n}\n\nfunc NewPCMMulawCodecData() av.AudioCodecData {\n\treturn PCMUCodecData{\n\t\ttyp: av.PCM_MULAW,\n\t}\n}\n\nfunc NewPCMAlawCodecData() av.AudioCodecData {\n\treturn PCMUCodecData{\n\t\ttyp: av.PCM_ALAW,\n\t}\n}\n\ntype SpeexCodecData struct {\n\tfake.CodecData\n}\n\nfunc (self SpeexCodecData) PacketDuration(data []byte) (time.Duration, error) {\n\t// libavcodec/libspeexdec.c\n\t// samples = samplerate/50\n\t// duration = 0.02s\n\treturn time.Millisecond*20, nil\n}\n\nfunc NewSpeexCodecData(sr int, cl av.ChannelLayout) SpeexCodecData {\n\tcodec := SpeexCodecData{}\n\tcodec.CodecType_ = av.SPEEX\n\tcodec.SampleFormat_ = av.S16\n\tcodec.SampleRate_ = sr\n\tcodec.ChannelLayout_ = cl\n\treturn codec\n}\n\n"
  },
  {
    "path": "codec/fake/fake.go",
    "content": "package fake\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n)\n\ntype CodecData struct {\n\tCodecType_ av.CodecType\n\tSampleRate_ int\n\tSampleFormat_ av.SampleFormat\n\tChannelLayout_ av.ChannelLayout\n}\n\nfunc (self CodecData) Type() av.CodecType {\n\treturn self.CodecType_\n}\n\nfunc (self CodecData) SampleFormat() av.SampleFormat {\n\treturn self.SampleFormat_\n}\n\nfunc (self CodecData) ChannelLayout() av.ChannelLayout {\n\treturn self.ChannelLayout_\n}\n\nfunc (self CodecData) SampleRate() int {\n\treturn self.SampleRate_\n}\n\n"
  },
  {
    "path": "codec/h264parser/parser.go",
    "content": "\npackage h264parser\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/utils/bits\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n\t\"fmt\"\n\t\"bytes\"\n)\n\nconst (\n\tNALU_SEI = 6\n\tNALU_PPS = 7\n\tNALU_SPS = 8\n\tNALU_AUD = 9\n)\n\nfunc IsDataNALU(b []byte) bool {\n\ttyp := b[0] & 0x1f\n\treturn typ >= 1 && typ <= 5\n}\n\n/*\nFrom: http://stackoverflow.com/questions/24884827/possible-locations-for-sequence-picture-parameter-sets-for-h-264-stream\n\nFirst off, it's important to understand that there is no single standard H.264 elementary bitstream format. The specification document does contain an Annex, specifically Annex B, that describes one possible format, but it is not an actual requirement. The standard specifies how video is encoded into individual packets. How these packets are stored and transmitted is left open to the integrator.\n\n1. Annex B\nNetwork Abstraction Layer Units\nThe packets are called Network Abstraction Layer Units. Often abbreviated NALU (or sometimes just NAL) each packet can be individually parsed and processed. The first byte of each NALU contains the NALU type, specifically bits 3 through 7. (bit 0 is always off, and bits 1-2 indicate whether a NALU is referenced by another NALU).\n\nThere are 19 different NALU types defined separated into two categories, VCL and non-VCL:\n\nVCL, or Video Coding Layer packets contain the actual visual information.\nNon-VCLs contain metadata that may or may not be required to decode the video.\nA single NALU, or even a VCL NALU is NOT the same thing as a frame. A frame can be ‘sliced’ into several NALUs. Just like you can slice a pizza. One or more slices are then virtually grouped into a Access Units (AU) that contain one frame. Slicing does come at a slight quality cost, so it is not often used.\n\nBelow is a table of all defined NALUs.\n\n0      Unspecified                                                    non-VCL\n1      Coded slice of a non-IDR picture                               VCL\n2      Coded slice data partition A                                   VCL\n3      Coded slice data partition B                                   VCL\n4      Coded slice data partition C                                   VCL\n5      Coded slice of an IDR picture                                  VCL\n6      Supplemental enhancement information (SEI)                     non-VCL\n7      Sequence parameter set                                         non-VCL\n8      Picture parameter set                                          non-VCL\n9      Access unit delimiter                                          non-VCL\n10     End of sequence                                                non-VCL\n11     End of stream                                                  non-VCL\n12     Filler data                                                    non-VCL\n13     Sequence parameter set extension                               non-VCL\n14     Prefix NAL unit                                                non-VCL\n15     Subset sequence parameter set                                  non-VCL\n16     Depth parameter set                                            non-VCL\n17..18 Reserved                                                       non-VCL\n19     Coded slice of an auxiliary coded picture without partitioning non-VCL\n20     Coded slice extension                                          non-VCL\n21     Coded slice extension for depth view components                non-VCL\n22..23 Reserved                                                       non-VCL\n24..31 Unspecified                                                    non-VCL\nThere are a couple of NALU types where having knowledge of may be helpful later.\n\nSequence Parameter Set (SPS). This non-VCL NALU contains information required to configure the decoder such as profile, level, resolution, frame rate.\nPicture Parameter Set (PPS). Similar to the SPS, this non-VCL contains information on entropy coding mode, slice groups, motion prediction and deblocking filters.\nInstantaneous Decoder Refresh (IDR). This VCL NALU is a self contained image slice. That is, an IDR can be decoded and displayed without referencing any other NALU save SPS and PPS.\nAccess Unit Delimiter (AUD). An AUD is an optional NALU that can be use to delimit frames in an elementary stream. It is not required (unless otherwise stated by the container/protocol, like TS), and is often not included in order to save space, but it can be useful to finds the start of a frame without having to fully parse each NALU.\nNALU Start Codes\nA NALU does not contain is its size. Therefore simply concatenating the NALUs to create a stream will not work because you will not know where one stops and the next begins.\n\nThe Annex B specification solves this by requiring ‘Start Codes’ to precede each NALU. A start code is 2 or 3 0x00 bytes followed with a 0x01 byte. e.g. 0x000001 or 0x00000001.\n\nThe 4 byte variation is useful for transmission over a serial connection as it is trivial to byte align the stream by looking for 31 zero bits followed by a one. If the next bit is 0 (because every NALU starts with a 0 bit), it is the start of a NALU. The 4 byte variation is usually only used for signaling random access points in the stream such as a SPS PPS AUD and IDR Where as the 3 byte variation is used everywhere else to save space.\n\nEmulation Prevention Bytes\nStart codes work because the four byte sequences 0x000000, 0x000001, 0x000002 and 0x000003 are illegal within a non-RBSP NALU. So when creating a NALU, care is taken to escape these values that could otherwise be confused with a start code. This is accomplished by inserting an ‘Emulation Prevention’ byte 0x03, so that 0x000001 becomes 0x00000301.\n\nWhen decoding, it is important to look for and ignore emulation prevention bytes. Because emulation prevention bytes can occur almost anywhere within a NALU, it is often more convenient in documentation to assume they have already been removed. A representation without emulation prevention bytes is called Raw Byte Sequence Payload (RBSP).\n\nExample\nLet's look at a complete example.\n\n0x0000 | 00 00 00 01 67 64 00 0A AC 72 84 44 26 84 00 00\n0x0010 | 03 00 04 00 00 03 00 CA 3C 48 96 11 80 00 00 00\n0x0020 | 01 68 E8 43 8F 13 21 30 00 00 01 65 88 81 00 05\n0x0030 | 4E 7F 87 DF 61 A5 8B 95 EE A4 E9 38 B7 6A 30 6A\n0x0040 | 71 B9 55 60 0B 76 2E B5 0E E4 80 59 27 B8 67 A9\n0x0050 | 63 37 5E 82 20 55 FB E4 6A E9 37 35 72 E2 22 91\n0x0060 | 9E 4D FF 60 86 CE 7E 42 B7 95 CE 2A E1 26 BE 87\n0x0070 | 73 84 26 BA 16 36 F4 E6 9F 17 DA D8 64 75 54 B1\n0x0080 | F3 45 0C 0B 3C 74 B3 9D BC EB 53 73 87 C3 0E 62\n0x0090 | 47 48 62 CA 59 EB 86 3F 3A FA 86 B5 BF A8 6D 06\n0x00A0 | 16 50 82 C4 CE 62 9E 4E E6 4C C7 30 3E DE A1 0B\n0x00B0 | D8 83 0B B6 B8 28 BC A9 EB 77 43 FC 7A 17 94 85\n0x00C0 | 21 CA 37 6B 30 95 B5 46 77 30 60 B7 12 D6 8C C5\n0x00D0 | 54 85 29 D8 69 A9 6F 12 4E 71 DF E3 E2 B1 6B 6B\n0x00E0 | BF 9F FB 2E 57 30 A9 69 76 C4 46 A2 DF FA 91 D9\n0x00F0 | 50 74 55 1D 49 04 5A 1C D6 86 68 7C B6 61 48 6C\n0x0100 | 96 E6 12 4C 27 AD BA C7 51 99 8E D0 F0 ED 8E F6\n0x0110 | 65 79 79 A6 12 A1 95 DB C8 AE E3 B6 35 E6 8D BC\n0x0120 | 48 A3 7F AF 4A 28 8A 53 E2 7E 68 08 9F 67 77 98\n0x0130 | 52 DB 50 84 D6 5E 25 E1 4A 99 58 34 C7 11 D6 43\n0x0140 | FF C4 FD 9A 44 16 D1 B2 FB 02 DB A1 89 69 34 C2\n0x0150 | 32 55 98 F9 9B B2 31 3F 49 59 0C 06 8C DB A5 B2\n0x0160 | 9D 7E 12 2F D0 87 94 44 E4 0A 76 EF 99 2D 91 18\n0x0170 | 39 50 3B 29 3B F5 2C 97 73 48 91 83 B0 A6 F3 4B\n0x0180 | 70 2F 1C 8F 3B 78 23 C6 AA 86 46 43 1D D7 2A 23\n0x0190 | 5E 2C D9 48 0A F5 F5 2C D1 FB 3F F0 4B 78 37 E9\n0x01A0 | 45 DD 72 CF 80 35 C3 95 07 F3 D9 06 E5 4A 58 76\n0x01B0 | 03 6C 81 20 62 45 65 44 73 BC FE C1 9F 31 E5 DB\n0x01C0 | 89 5C 6B 79 D8 68 90 D7 26 A8 A1 88 86 81 DC 9A\n0x01D0 | 4F 40 A5 23 C7 DE BE 6F 76 AB 79 16 51 21 67 83\n0x01E0 | 2E F3 D6 27 1A 42 C2 94 D1 5D 6C DB 4A 7A E2 CB\n0x01F0 | 0B B0 68 0B BE 19 59 00 50 FC C0 BD 9D F5 F5 F8\n0x0200 | A8 17 19 D6 B3 E9 74 BA 50 E5 2C 45 7B F9 93 EA\n0x0210 | 5A F9 A9 30 B1 6F 5B 36 24 1E 8D 55 57 F4 CC 67\n0x0220 | B2 65 6A A9 36 26 D0 06 B8 E2 E3 73 8B D1 C0 1C\n0x0230 | 52 15 CA B5 AC 60 3E 36 42 F1 2C BD 99 77 AB A8\n0x0240 | A9 A4 8E 9C 8B 84 DE 73 F0 91 29 97 AE DB AF D6\n0x0250 | F8 5E 9B 86 B3 B3 03 B3 AC 75 6F A6 11 69 2F 3D\n0x0260 | 3A CE FA 53 86 60 95 6C BB C5 4E F3\n\nThis is a complete AU containing 3 NALUs. As you can see, we begin with a Start code followed by an SPS (SPS starts with 67). Within the SPS, you will see two Emulation Prevention bytes. Without these bytes the illegal sequence 0x000000 would occur at these positions. Next you will see a start code followed by a PPS (PPS starts with 68) and one final start code followed by an IDR slice. This is a complete H.264 stream. If you type these values into a hex editor and save the file with a .264 extension, you will be able to convert it to this image:\n\nLena\n\nAnnex B is commonly used in live and streaming formats such as transport streams, over the air broadcasts, and DVDs. In these formats it is common to repeat the SPS and PPS periodically, usually preceding every IDR thus creating a random access point for the decoder. This enables the ability to join a stream already in progress.\n\n2. AVCC\nThe other common method of storing an H.264 stream is the AVCC format. In this format, each NALU is preceded with its length (in big endian format). This method is easier to parse, but you lose the byte alignment features of Annex B. Just to complicate things, the length may be encoded using 1, 2 or 4 bytes. This value is stored in a header object. This header is often called ‘extradata’ or ‘sequence header’. Its basic format is as follows:\n\nbits    \n8   version ( always 0x01 )\n8   avc profile ( sps[0][1] )\n8   avc compatibility ( sps[0][2] )\n8   avc level ( sps[0][3] )\n6   reserved ( all bits on )\n2   NALULengthSizeMinusOne\n3   reserved ( all bits on )\n5   number of SPS NALUs (usually 1)\nrepeated once per SPS:\n  16         SPS size\n\tvariable   SPS NALU data\n8   number of PPS NALUs (usually 1)\nrepeated once per PPS\n  16         PPS size\n  variable   PPS NALU data\n\nUsing the same example above, the AVCC extradata will look like this:\n\n0x0000 | 01 64 00 0A FF E1 00 19 67 64 00 0A AC 72 84 44\n0x0010 | 26 84 00 00 03 00 04 00 00 03 00 CA 3C 48 96 11\n0x0020 | 80 01 00 07 68 E8 43 8F 13 21 30\n\nYou will notice SPS and PPS is now stored out of band. That is, separate from the elementary stream data. Storage and transmission of this data is the job of the file container, and beyond the scope of this document. Notice that even though we are not using start codes, emulation prevention bytes are still inserted.\n\nAdditionally, there is a new variable called NALULengthSizeMinusOne. This confusingly named variable tells us how many bytes to use to store the length of each NALU. So, if NALULengthSizeMinusOne is set to 0, then each NALU is preceded with a single byte indicating its length. Using a single byte to store the size, the max size of a NALU is 255 bytes. That is obviously pretty small. Way too small for an entire key frame. Using 2 bytes gives us 64k per NALU. It would work in our example, but is still a pretty low limit. 3 bytes would be perfect, but for some reason is not universally supported. Therefore, 4 bytes is by far the most common, and it is what we used here:\n\n0x0000 | 00 00 02 41 65 88 81 00 05 4E 7F 87 DF 61 A5 8B\n0x0010 | 95 EE A4 E9 38 B7 6A 30 6A 71 B9 55 60 0B 76 2E\n0x0020 | B5 0E E4 80 59 27 B8 67 A9 63 37 5E 82 20 55 FB\n0x0030 | E4 6A E9 37 35 72 E2 22 91 9E 4D FF 60 86 CE 7E\n0x0040 | 42 B7 95 CE 2A E1 26 BE 87 73 84 26 BA 16 36 F4\n0x0050 | E6 9F 17 DA D8 64 75 54 B1 F3 45 0C 0B 3C 74 B3\n0x0060 | 9D BC EB 53 73 87 C3 0E 62 47 48 62 CA 59 EB 86\n0x0070 | 3F 3A FA 86 B5 BF A8 6D 06 16 50 82 C4 CE 62 9E\n0x0080 | 4E E6 4C C7 30 3E DE A1 0B D8 83 0B B6 B8 28 BC\n0x0090 | A9 EB 77 43 FC 7A 17 94 85 21 CA 37 6B 30 95 B5\n0x00A0 | 46 77 30 60 B7 12 D6 8C C5 54 85 29 D8 69 A9 6F\n0x00B0 | 12 4E 71 DF E3 E2 B1 6B 6B BF 9F FB 2E 57 30 A9\n0x00C0 | 69 76 C4 46 A2 DF FA 91 D9 50 74 55 1D 49 04 5A\n0x00D0 | 1C D6 86 68 7C B6 61 48 6C 96 E6 12 4C 27 AD BA\n0x00E0 | C7 51 99 8E D0 F0 ED 8E F6 65 79 79 A6 12 A1 95\n0x00F0 | DB C8 AE E3 B6 35 E6 8D BC 48 A3 7F AF 4A 28 8A\n0x0100 | 53 E2 7E 68 08 9F 67 77 98 52 DB 50 84 D6 5E 25\n0x0110 | E1 4A 99 58 34 C7 11 D6 43 FF C4 FD 9A 44 16 D1\n0x0120 | B2 FB 02 DB A1 89 69 34 C2 32 55 98 F9 9B B2 31\n0x0130 | 3F 49 59 0C 06 8C DB A5 B2 9D 7E 12 2F D0 87 94\n0x0140 | 44 E4 0A 76 EF 99 2D 91 18 39 50 3B 29 3B F5 2C\n0x0150 | 97 73 48 91 83 B0 A6 F3 4B 70 2F 1C 8F 3B 78 23\n0x0160 | C6 AA 86 46 43 1D D7 2A 23 5E 2C D9 48 0A F5 F5\n0x0170 | 2C D1 FB 3F F0 4B 78 37 E9 45 DD 72 CF 80 35 C3\n0x0180 | 95 07 F3 D9 06 E5 4A 58 76 03 6C 81 20 62 45 65\n0x0190 | 44 73 BC FE C1 9F 31 E5 DB 89 5C 6B 79 D8 68 90\n0x01A0 | D7 26 A8 A1 88 86 81 DC 9A 4F 40 A5 23 C7 DE BE\n0x01B0 | 6F 76 AB 79 16 51 21 67 83 2E F3 D6 27 1A 42 C2\n0x01C0 | 94 D1 5D 6C DB 4A 7A E2 CB 0B B0 68 0B BE 19 59\n0x01D0 | 00 50 FC C0 BD 9D F5 F5 F8 A8 17 19 D6 B3 E9 74\n0x01E0 | BA 50 E5 2C 45 7B F9 93 EA 5A F9 A9 30 B1 6F 5B\n0x01F0 | 36 24 1E 8D 55 57 F4 CC 67 B2 65 6A A9 36 26 D0\n0x0200 | 06 B8 E2 E3 73 8B D1 C0 1C 52 15 CA B5 AC 60 3E\n0x0210 | 36 42 F1 2C BD 99 77 AB A8 A9 A4 8E 9C 8B 84 DE\n0x0220 | 73 F0 91 29 97 AE DB AF D6 F8 5E 9B 86 B3 B3 03\n0x0230 | B3 AC 75 6F A6 11 69 2F 3D 3A CE FA 53 86 60 95\n0x0240 | 6C BB C5 4E F3\n\nAn advantage to this format is the ability to configure the decoder at the start and jump into the middle of a stream. This is a common use case where the media is available on a random access medium such as a hard drive, and is therefore used in common container formats such as MP4 and MKV.\n*/\n\nvar StartCodeBytes = []byte{0,0,1}\nvar AUDBytes = []byte{0,0,0,1,0x9,0xf0,0,0,0,1} // AUD\n\nfunc CheckNALUsType(b []byte) (typ int) {\n\t_, typ = SplitNALUs(b)\n\treturn\n}\n\nconst (\n\tNALU_RAW = iota\n\tNALU_AVCC\n\tNALU_ANNEXB\n)\n\nfunc SplitNALUs(b []byte) (nalus [][]byte, typ int) {\n\tif len(b) < 4 {\n\t\treturn [][]byte{b}, NALU_RAW\n\t}\n\n\tval3 := pio.U24BE(b)\n\tval4 := pio.U32BE(b)\n\n\t// maybe AVCC\n\tif val4 <= uint32(len(b)) {\n\t\t_val4 := val4\n\t\t_b := b[4:]\n\t\tnalus := [][]byte{}\n\t\tfor {\n\t\t\tnalus = append(nalus, _b[:_val4])\n\t\t\t_b = _b[_val4:]\n\t\t\tif len(_b) < 4 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\t_val4 = pio.U32BE(_b)\n\t\t\t_b = _b[4:]\n\t\t\tif _val4 > uint32(len(_b)) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif len(_b) == 0 {\n\t\t\treturn nalus, NALU_AVCC\n\t\t}\n\t}\n\n\t// is Annex B\n\tif val3 == 1 || val4 == 1 {\n\t\t_val3 := val3\n\t\t_val4 := val4\n\t\tstart := 0\n\t\tpos := 0\n\t\tfor {\n\t\t\tif start != pos {\n\t\t\t\tnalus = append(nalus, b[start:pos])\n\t\t\t}\n\t\t\tif _val3 == 1 {\n\t\t\t\tpos += 3\n\t\t\t} else if _val4 == 1 {\n\t\t\t\tpos += 4\n\t\t\t}\n\t\t\tstart = pos\n\t\t\tif start == len(b) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\t_val3 = 0\n\t\t\t_val4 = 0\n\t\t\tfor pos < len(b) {\n\t\t\t\tif pos+2 < len(b) && b[pos] == 0 {\n\t\t\t\t\t_val3 = pio.U24BE(b[pos:])\n\t\t\t\t\tif _val3 == 0 {\n\t\t\t\t\t\tif pos+3 < len(b) {\n\t\t\t\t\t\t\t_val4 = uint32(b[pos+3])\n\t\t\t\t\t\t\tif _val4 == 1 {\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if _val3 == 1 {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tpos++\n\t\t\t\t} else {\n\t\t\t\t\tpos++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttyp = NALU_ANNEXB\n\t\treturn\n\t}\n\n\treturn [][]byte{b}, NALU_RAW\n}\n\ntype SPSInfo struct {\n\tProfileIdc uint\n\tLevelIdc   uint\n\n\tMbWidth  uint\n\tMbHeight uint\n\n\tCropLeft   uint\n\tCropRight  uint\n\tCropTop    uint\n\tCropBottom uint\n\n\tWidth  uint\n\tHeight uint\n}\n\nfunc ParseSPS(data []byte) (self SPSInfo, err error) {\n\tr := &bits.GolombBitReader{R: bytes.NewReader(data)}\n\n\tif _, err = r.ReadBits(8); err != nil {\n\t\treturn\n\t}\n\n\tif self.ProfileIdc, err = r.ReadBits(8); err != nil {\n\t\treturn\n\t}\n\n\t// constraint_set0_flag-constraint_set6_flag,reserved_zero_2bits\n\tif _, err = r.ReadBits(8); err != nil {\n\t\treturn\n\t}\n\n\t// level_idc\n\tif self.LevelIdc, err = r.ReadBits(8); err != nil {\n\t\treturn\n\t}\n\n\t// seq_parameter_set_id\n\tif _, err = r.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\n\tif self.ProfileIdc == 100 || self.ProfileIdc == 110 ||\n\t\tself.ProfileIdc == 122 || self.ProfileIdc == 244 ||\n\t\tself.ProfileIdc == 44 || self.ProfileIdc == 83 ||\n\t\tself.ProfileIdc == 86 || self.ProfileIdc == 118 {\n\n\t\tvar chroma_format_idc uint\n\t\tif chroma_format_idc, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif chroma_format_idc == 3 {\n\t\t\t// residual_colour_transform_flag\n\t\t\tif _, err = r.ReadBit(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// bit_depth_luma_minus8\n\t\tif _, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\t\t// bit_depth_chroma_minus8\n\t\tif _, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\t\t// qpprime_y_zero_transform_bypass_flag\n\t\tif _, err = r.ReadBit(); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tvar seq_scaling_matrix_present_flag uint\n\t\tif seq_scaling_matrix_present_flag, err = r.ReadBit(); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif seq_scaling_matrix_present_flag != 0 {\n\t\t\tfor i := 0; i < 8; i++ {\n\t\t\t\tvar seq_scaling_list_present_flag uint\n\t\t\t\tif seq_scaling_list_present_flag, err = r.ReadBit(); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif seq_scaling_list_present_flag != 0 {\n\t\t\t\t\tvar sizeOfScalingList uint\n\t\t\t\t\tif i < 6 {\n\t\t\t\t\t\tsizeOfScalingList = 16\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsizeOfScalingList = 64\n\t\t\t\t\t}\n\t\t\t\t\tlastScale := uint(8)\n\t\t\t\t\tnextScale := uint(8)\n\t\t\t\t\tfor j := uint(0); j < sizeOfScalingList; j++ {\n\t\t\t\t\t\tif nextScale != 0 {\n\t\t\t\t\t\t\tvar delta_scale uint\n\t\t\t\t\t\t\tif delta_scale, err = r.ReadSE(); err != nil {\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnextScale = (lastScale + delta_scale + 256) % 256\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif nextScale != 0 {\n\t\t\t\t\t\t\tlastScale = nextScale\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// log2_max_frame_num_minus4\n\tif _, err = r.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\n\tvar pic_order_cnt_type uint\n\tif pic_order_cnt_type, err = r.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\tif pic_order_cnt_type == 0 {\n\t\t// log2_max_pic_order_cnt_lsb_minus4\n\t\tif _, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\t} else if pic_order_cnt_type == 1 {\n\t\t// delta_pic_order_always_zero_flag\n\t\tif _, err = r.ReadBit(); err != nil {\n\t\t\treturn\n\t\t}\n\t\t// offset_for_non_ref_pic\n\t\tif _, err = r.ReadSE(); err != nil {\n\t\t\treturn\n\t\t}\n\t\t// offset_for_top_to_bottom_field\n\t\tif _, err = r.ReadSE(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar num_ref_frames_in_pic_order_cnt_cycle uint\n\t\tif num_ref_frames_in_pic_order_cnt_cycle, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tfor i := uint(0); i < num_ref_frames_in_pic_order_cnt_cycle; i++ {\n\t\t\tif _, err = r.ReadSE(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\t// max_num_ref_frames\n\tif _, err = r.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\n\t// gaps_in_frame_num_value_allowed_flag\n\tif _, err = r.ReadBit(); err != nil {\n\t\treturn\n\t}\n\n\tif self.MbWidth, err = r.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\tself.MbWidth++\n\n\tif self.MbHeight, err = r.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\tself.MbHeight++\n\n\tvar frame_mbs_only_flag uint\n\tif frame_mbs_only_flag, err = r.ReadBit(); err != nil {\n\t\treturn\n\t}\n\tif frame_mbs_only_flag == 0 {\n\t\t// mb_adaptive_frame_field_flag\n\t\tif _, err = r.ReadBit(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\t// direct_8x8_inference_flag\n\tif _, err = r.ReadBit(); err != nil {\n\t\treturn\n\t}\n\n\tvar frame_cropping_flag uint\n\tif frame_cropping_flag, err = r.ReadBit(); err != nil {\n\t\treturn\n\t}\n\tif frame_cropping_flag != 0 {\n\t\tif self.CropLeft, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.CropRight, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.CropTop, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.CropBottom, err = r.ReadExponentialGolombCode(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tself.Width = (self.MbWidth * 16) - self.CropLeft*2 - self.CropRight*2\n\tself.Height = ((2 - frame_mbs_only_flag) * self.MbHeight * 16) - self.CropTop*2 - self.CropBottom*2\n\n\treturn\n}\n\ntype CodecData struct {\n\tRecord []byte\n\tRecordInfo AVCDecoderConfRecord\n\tSPSInfo SPSInfo\n}\n\nfunc (self CodecData) Type() av.CodecType {\n\treturn av.H264\n}\n\nfunc (self CodecData) AVCDecoderConfRecordBytes() []byte {\n\treturn self.Record\n}\n\nfunc (self CodecData) SPS() []byte {\n\treturn self.RecordInfo.SPS[0]\n}\n\nfunc (self CodecData) PPS() []byte {\n\treturn self.RecordInfo.PPS[0]\n}\n\nfunc (self CodecData) Width() int {\n\treturn int(self.SPSInfo.Width)\n}\n\nfunc (self CodecData) Height() int {\n\treturn int(self.SPSInfo.Height)\n}\n\nfunc NewCodecDataFromAVCDecoderConfRecord(record []byte) (self CodecData, err error) {\n\tself.Record = record\n\tif _, err = (&self.RecordInfo).Unmarshal(record); err != nil {\n\t\treturn\n\t}\n\tif len(self.RecordInfo.SPS) == 0 {\n\t\terr = fmt.Errorf(\"h264parser: no SPS found in AVCDecoderConfRecord\")\n\t\treturn\n\t}\n\tif len(self.RecordInfo.PPS) == 0 {\n\t\terr = fmt.Errorf(\"h264parser: no PPS found in AVCDecoderConfRecord\")\n\t\treturn\n\t}\n\tif self.SPSInfo, err = ParseSPS(self.RecordInfo.SPS[0]); err != nil {\n\t\terr = fmt.Errorf(\"h264parser: parse SPS failed(%s)\", err)\n\t\treturn\n\t}\n\treturn\n}\n\nfunc NewCodecDataFromSPSAndPPS(sps, pps []byte) (self CodecData, err error) {\n\trecordinfo := AVCDecoderConfRecord{}\n\trecordinfo.AVCProfileIndication = sps[1]\n\trecordinfo.ProfileCompatibility = sps[2]\n\trecordinfo.AVCLevelIndication = sps[3]\n\trecordinfo.SPS = [][]byte{sps}\n\trecordinfo.PPS = [][]byte{pps}\n\trecordinfo.LengthSizeMinusOne = 3\n\n\tbuf := make([]byte, recordinfo.Len())\n\trecordinfo.Marshal(buf)\n\n\tself.RecordInfo = recordinfo\n\tself.Record = buf\n\n\tif self.SPSInfo, err = ParseSPS(sps); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\ntype AVCDecoderConfRecord struct {\n\tAVCProfileIndication uint8\n\tProfileCompatibility uint8\n\tAVCLevelIndication   uint8\n\tLengthSizeMinusOne   uint8\n\tSPS                  [][]byte\n\tPPS                  [][]byte\n}\n\nvar ErrDecconfInvalid = fmt.Errorf(\"h264parser: AVCDecoderConfRecord invalid\")\n\nfunc (self *AVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {\n\tif len(b) < 7 {\n\t\terr = ErrDecconfInvalid\n\t\treturn\n\t}\n\n\tself.AVCProfileIndication = b[1]\n\tself.ProfileCompatibility = b[2]\n\tself.AVCLevelIndication = b[3]\n\tself.LengthSizeMinusOne = b[4]&0x03\n\tspscount := int(b[5]&0x1f)\n\tn += 6\n\n\tfor i := 0; i < spscount; i++ {\n\t\tif len(b) < n+2 {\n\t\t\terr = ErrDecconfInvalid\n\t\t\treturn\n\t\t}\n\t\tspslen := int(pio.U16BE(b[n:]))\n\t\tn += 2\n\n\t\tif len(b) < n+spslen {\n\t\t\terr = ErrDecconfInvalid\n\t\t\treturn\n\t\t}\n\t\tself.SPS = append(self.SPS, b[n:n+spslen])\n\t\tn += spslen\n\t}\n\n\tif len(b) < n+1 {\n\t\terr = ErrDecconfInvalid\n\t\treturn\n\t}\n\tppscount := int(b[n])\n\tn++\n\n\tfor i := 0; i < ppscount; i++ {\n\t\tif len(b) < n+2 {\n\t\t\terr = ErrDecconfInvalid\n\t\t\treturn\n\t\t}\n\t\tppslen := int(pio.U16BE(b[n:]))\n\t\tn += 2\n\n\t\tif len(b) < n+ppslen {\n\t\t\terr = ErrDecconfInvalid\n\t\t\treturn\n\t\t}\n\t\tself.PPS = append(self.PPS, b[n:n+ppslen])\n\t\tn += ppslen\n\t}\n\n\treturn\n}\n\nfunc (self AVCDecoderConfRecord) Len() (n int) {\n\tn = 7\n\tfor _, sps := range self.SPS {\n\t\tn += 2+len(sps)\n\t}\n\tfor _, pps := range self.PPS {\n\t\tn += 2+len(pps)\n\t}\n\treturn\n}\n\nfunc (self AVCDecoderConfRecord) Marshal(b []byte) (n int) {\n\tb[0] = 1\n\tb[1] = self.AVCProfileIndication\n\tb[2] = self.ProfileCompatibility\n\tb[3] = self.AVCLevelIndication\n\tb[4] = self.LengthSizeMinusOne|0xfc\n\tb[5] = uint8(len(self.SPS))|0xe0\n\tn += 6\n\n\tfor _, sps := range self.SPS {\n\t\tpio.PutU16BE(b[n:], uint16(len(sps)))\n\t\tn += 2\n\t\tcopy(b[n:], sps)\n\t\tn += len(sps)\n\t}\n\n\tb[n] = uint8(len(self.PPS))\n\tn++\n\n\tfor _, pps := range self.PPS {\n\t\tpio.PutU16BE(b[n:], uint16(len(pps)))\n\t\tn += 2\n\t\tcopy(b[n:], pps)\n\t\tn += len(pps)\n\t}\n\n\treturn\n}\n\ntype SliceType uint\n\nfunc (self SliceType) String() string {\n\tswitch self {\n\tcase SLICE_P:\n\t\treturn \"P\"\n\tcase SLICE_B:\n\t\treturn \"B\"\n\tcase SLICE_I:\n\t\treturn \"I\"\n\t}\n\treturn \"\"\n}\n\nconst (\n\tSLICE_P = iota+1\n\tSLICE_B\n\tSLICE_I\n)\n\nfunc ParseSliceHeaderFromNALU(packet []byte) (sliceType SliceType, err error) {\n\n\tif len(packet) <= 1 {\n\t\terr = fmt.Errorf(\"h264parser: packet too short to parse slice header\")\n\t\treturn\n\t}\n\n\tnal_unit_type := packet[0]&0x1f\n\tswitch nal_unit_type {\n\tcase 1,2,5,19:\n\t\t// slice_layer_without_partitioning_rbsp\n\t\t// slice_data_partition_a_layer_rbsp\n\n\tdefault:\n\t\terr = fmt.Errorf(\"h264parser: nal_unit_type=%d has no slice header\", nal_unit_type)\n\t\treturn\n\t}\n\n\tr := &bits.GolombBitReader{R: bytes.NewReader(packet[1:])}\n\n\t// first_mb_in_slice\n\tif _, err = r.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\n\t// slice_type\n\tvar u uint\n\tif u, err = r.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\n\tswitch u {\n\tcase 0,3,5,8:\n\t\tsliceType = SLICE_P\n\tcase 1,6:\n\t\tsliceType = SLICE_B\n\tcase 2,4,7,9:\n\t\tsliceType = SLICE_I\n\tdefault:\n\t\terr = fmt.Errorf(\"h264parser: slice_type=%d invalid\", u)\n\t\treturn\n\t}\n\n\treturn\n}\n\n"
  },
  {
    "path": "codec/h264parser/parser_test.go",
    "content": "\npackage h264parser\n\nimport (\n\t\"testing\"\n\t\"encoding/hex\"\n)\n\nfunc TestParser(t *testing.T) {\n\tvar ok bool\n\tvar nalus [][]byte\n\n\tannexbFrame, _ := hex.DecodeString(\"00000001223322330000000122332233223300000133000001000001\")\n\tnalus, ok = SplitNALUs(annexbFrame)\n\tt.Log(ok, len(nalus))\n\n\tavccFrame, _ := hex.DecodeString(\n\t\t\"00000008aabbccaabbccaabb00000001aa\",\n\t)\n\tnalus, ok = SplitNALUs(avccFrame)\n\tt.Log(ok, len(nalus))\n}\n\n"
  },
  {
    "path": "doc.go",
    "content": "\n// Package joy4 is a Golang audio/video library and streaming server.\n// JOY4 is powerful library written in golang, well-designed interface makes a few lines \n// of code can do a lot of things such as reading, writing, transcoding among \n// variety media formats, or setting up high-performance live streaming server.\npackage joy4\n"
  },
  {
    "path": "examples/audio_decode/main.go",
    "content": "\npackage main\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/format\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/cgo/ffmpeg\"\n)\n\n// need ffmpeg installed\n\nfunc init() {\n\tformat.RegisterAll()\n}\n\nfunc main() {\n\tfile, _ := avutil.Open(\"projectindex.flv\")\n\tstreams, _ := file.Streams()\n\tvar dec *ffmpeg.AudioDecoder\n\n\tfor _, stream := range streams {\n\t\tif stream.Type() == av.AAC {\n\t\t\tdec, _ = ffmpeg.NewAudioDecoder(stream.(av.AudioCodecData))\n\t\t}\n\t}\n\n\tfor i := 0; i < 10; i++ {\n\t\tpkt, _ := file.ReadPacket()\n\t\tif streams[pkt.Idx].Type() == av.AAC {\n\t\t\tok, frame, _ := dec.Decode(pkt.Data)\n\t\t\tif ok {\n\t\t\t\tprintln(\"decode samples\", frame.SampleCount)\n\t\t\t}\n\t\t}\n\t}\n\n\tfile.Close()\n}\n\n"
  },
  {
    "path": "examples/http_flv_and_rtmp_server/main.go",
    "content": "package main\n\nimport (\n\t\"sync\"\n\t\"io\"\n\t\"net/http\"\n\t\"github.com/nareix/joy4/format\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/av/pubsub\"\n\t\"github.com/nareix/joy4/format/rtmp\"\n\t\"github.com/nareix/joy4/format/flv\"\n)\n\nfunc init() {\n\tformat.RegisterAll()\n}\n\ntype writeFlusher struct {\n\thttpflusher http.Flusher\n\tio.Writer\n}\n\nfunc (self writeFlusher) Flush() error {\n\tself.httpflusher.Flush()\n\treturn nil\n}\n\nfunc main() {\n\tserver := &rtmp.Server{}\n\n\tl := &sync.RWMutex{}\n\ttype Channel struct {\n\t\tque *pubsub.Queue\n\t}\n\tchannels := map[string]*Channel{}\n\n\tserver.HandlePlay = func(conn *rtmp.Conn) {\n\t\tl.RLock()\n\t\tch := channels[conn.URL.Path]\n\t\tl.RUnlock()\n\n\t\tif ch != nil {\n\t\t\tcursor := ch.que.Latest()\n\t\t\tavutil.CopyFile(conn, cursor)\n\t\t}\n\t}\n\n\tserver.HandlePublish = func(conn *rtmp.Conn) {\n\t\tstreams, _ := conn.Streams()\n\n\t\tl.Lock()\n\t\tch := channels[conn.URL.Path]\n\t\tif ch == nil {\n\t\t\tch = &Channel{}\n\t\t\tch.que = pubsub.NewQueue()\n\t\t\tch.que.WriteHeader(streams)\n\t\t\tchannels[conn.URL.Path] = ch\n\t\t} else {\n\t\t\tch = nil\n\t\t}\n\t\tl.Unlock()\n\t\tif ch == nil {\n\t\t\treturn\n\t\t}\n\n\t\tavutil.CopyPackets(ch.que, conn)\n\n\t\tl.Lock()\n\t\tdelete(channels, conn.URL.Path)\n\t\tl.Unlock()\n\t\tch.que.Close()\n\t}\n\n\thttp.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\tl.RLock()\n\t\tch := channels[r.URL.Path]\n\t\tl.RUnlock()\n\n\t\tif ch != nil {\n\t\t\tw.Header().Set(\"Content-Type\", \"video/x-flv\")\n\t\t\tw.Header().Set(\"Transfer-Encoding\", \"chunked\")\t\t\n\t\t\tw.Header().Set(\"Access-Control-Allow-Origin\", \"*\")\n\t\t\tw.WriteHeader(200)\n\t\t\tflusher := w.(http.Flusher)\n\t\t\tflusher.Flush()\n\n\t\t\tmuxer := flv.NewMuxerWriteFlusher(writeFlusher{httpflusher: flusher, Writer: w})\n\t\t\tcursor := ch.que.Latest()\n\n\t\t\tavutil.CopyFile(muxer, cursor)\n\t\t} else {\n\t\t\thttp.NotFound(w, r)\n\t\t}\n\t})\n\n\tgo http.ListenAndServe(\":8089\", nil)\n\n\tserver.ListenAndServe()\n\n\t// ffmpeg -re -i movie.flv -c copy -f flv rtmp://localhost/movie\n\t// ffmpeg -f avfoundation -i \"0:0\" .... -f flv rtmp://localhost/screen\n\t// ffplay http://localhost:8089/movie\n\t// ffplay http://localhost:8089/screen\n}\n"
  },
  {
    "path": "examples/open_probe_file/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/format\"\n)\n\nfunc init() {\n\tformat.RegisterAll()\n}\n\nfunc main() {\n\tfile, _ := avutil.Open(\"projectindex.flv\")\n\n\tstreams, _ := file.Streams()\n\tfor _, stream := range streams {\n\t\tif stream.Type().IsAudio() {\n\t\t\tastream := stream.(av.AudioCodecData)\n\t\t\tfmt.Println(astream.Type(), astream.SampleRate(), astream.SampleFormat(), astream.ChannelLayout())\n\t\t} else if stream.Type().IsVideo() {\n\t\t\tvstream := stream.(av.VideoCodecData)\n\t\t\tfmt.Println(vstream.Type(), vstream.Width(), vstream.Height())\n\t\t}\n\t}\n\n\tfor i := 0; i < 10; i++ {\n\t\tvar pkt av.Packet\n\t\tvar err error\n\t\tif pkt, err = file.ReadPacket(); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Println(\"pkt\", i, streams[pkt.Idx].Type(), \"len\", len(pkt.Data), \"keyframe\", pkt.IsKeyFrame)\n\t}\n\n\tfile.Close()\n}\n\n"
  },
  {
    "path": "examples/rtmp_publish/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/nareix/joy4/av/pktque\"\n\t\"github.com/nareix/joy4/format\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/format/rtmp\"\n)\n\nfunc init() {\n\tformat.RegisterAll()\n}\n\n// as same as: ffmpeg -re -i projectindex.flv -c copy -f flv rtmp://localhost:1936/app/publish\n\nfunc main() {\n\tfile, _ := avutil.Open(\"projectindex.flv\")\n\tconn, _ := rtmp.Dial(\"rtmp://localhost:1936/app/publish\")\n\t// conn, _ := avutil.Create(\"rtmp://localhost:1936/app/publish\")\n\n\tdemuxer := &pktque.FilterDemuxer{Demuxer: file, Filter: &pktque.Walltime{}}\n\tavutil.CopyFile(conn, demuxer)\n\n\tfile.Close()\n\tconn.Close()\n}\n\n"
  },
  {
    "path": "examples/rtmp_server_channels/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/av/pktque\"\n\t\"github.com/nareix/joy4/av/pubsub\"\n\t\"github.com/nareix/joy4/format\"\n\t\"github.com/nareix/joy4/format/rtmp\"\n\t\"sync\"\n\t\"time\"\n)\n\nfunc init() {\n\tformat.RegisterAll()\n}\n\ntype FrameDropper struct {\n\tInterval     int\n\tn            int\n\tskipping     bool\n\tDelaySkip    time.Duration\n\tlasttime     time.Time\n\tlastpkttime  time.Duration\n\tdelay        time.Duration\n\tSkipInterval int\n}\n\nfunc (self *FrameDropper) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {\n\tif self.DelaySkip != 0 && pkt.Idx == int8(videoidx) {\n\t\tnow := time.Now()\n\t\tif !self.lasttime.IsZero() {\n\t\t\trealdiff := now.Sub(self.lasttime)\n\t\t\tpktdiff := pkt.Time - self.lastpkttime\n\t\t\tself.delay += realdiff - pktdiff\n\t\t}\n\t\tself.lasttime = time.Now()\n\t\tself.lastpkttime = pkt.Time\n\n\t\tif !self.skipping {\n\t\t\tif self.delay > self.DelaySkip {\n\t\t\t\tself.skipping = true\n\t\t\t\tself.delay = 0\n\t\t\t}\n\t\t} else {\n\t\t\tif pkt.IsKeyFrame {\n\t\t\t\tself.skipping = false\n\t\t\t}\n\t\t}\n\t\tif self.skipping {\n\t\t\tdrop = true\n\t\t}\n\n\t\tif self.SkipInterval != 0 && pkt.IsKeyFrame {\n\t\t\tif self.n == self.SkipInterval {\n\t\t\t\tself.n = 0\n\t\t\t\tself.skipping = true\n\t\t\t}\n\t\t\tself.n++\n\t\t}\n\t}\n\n\tif self.Interval != 0 {\n\t\tif self.n >= self.Interval && pkt.Idx == int8(videoidx) && !pkt.IsKeyFrame {\n\t\t\tdrop = true\n\t\t\tself.n = 0\n\t\t}\n\t\tself.n++\n\t}\n\n\treturn\n}\n\nfunc main() {\n\tserver := &rtmp.Server{}\n\n\tl := &sync.RWMutex{}\n\ttype Channel struct {\n\t\tque *pubsub.Queue\n\t}\n\tchannels := map[string]*Channel{}\n\n\tserver.HandlePlay = func(conn *rtmp.Conn) {\n\t\tl.RLock()\n\t\tch := channels[conn.URL.Path]\n\t\tl.RUnlock()\n\n\t\tif ch != nil {\n\t\t\tcursor := ch.que.Latest()\n\t\t\tquery := conn.URL.Query()\n\n\t\t\tif q := query.Get(\"delaygop\"); q != \"\" {\n\t\t\t\tn := 0\n\t\t\t\tfmt.Sscanf(q, \"%d\", &n)\n\t\t\t\tcursor = ch.que.DelayedGopCount(n)\n\t\t\t} else if q := query.Get(\"delaytime\"); q != \"\" {\n\t\t\t\tdur, _ := time.ParseDuration(q)\n\t\t\t\tcursor = ch.que.DelayedTime(dur)\n\t\t\t}\n\n\t\t\tfilters := pktque.Filters{}\n\n\t\t\tif q := query.Get(\"waitkey\"); q != \"\" {\n\t\t\t\tfilters = append(filters, &pktque.WaitKeyFrame{})\n\t\t\t}\n\n\t\t\tfilters = append(filters, &pktque.FixTime{StartFromZero: true, MakeIncrement: true})\n\n\t\t\tif q := query.Get(\"framedrop\"); q != \"\" {\n\t\t\t\tn := 0\n\t\t\t\tfmt.Sscanf(q, \"%d\", &n)\n\t\t\t\tfilters = append(filters, &FrameDropper{Interval: n})\n\t\t\t}\n\n\t\t\tif q := query.Get(\"delayskip\"); q != \"\" {\n\t\t\t\tdur, _ := time.ParseDuration(q)\n\t\t\t\tskipper := &FrameDropper{DelaySkip: dur}\n\t\t\t\tif q := query.Get(\"skipinterval\"); q != \"\" {\n\t\t\t\t\tn := 0\n\t\t\t\t\tfmt.Sscanf(q, \"%d\", &n)\n\t\t\t\t\tskipper.SkipInterval = n\n\t\t\t\t}\n\t\t\t\tfilters = append(filters, skipper)\n\t\t\t}\n\n\t\t\tdemuxer := &pktque.FilterDemuxer{\n\t\t\t\tFilter:  filters,\n\t\t\t\tDemuxer: cursor,\n\t\t\t}\n\n\t\t\tavutil.CopyFile(conn, demuxer)\n\t\t}\n\t}\n\n\tserver.HandlePublish = func(conn *rtmp.Conn) {\n\t\tl.Lock()\n\t\tch := channels[conn.URL.Path]\n\t\tif ch == nil {\n\t\t\tch = &Channel{}\n\t\t\tch.que = pubsub.NewQueue()\n\t\t\tquery := conn.URL.Query()\n\t\t\tif q := query.Get(\"cachegop\"); q != \"\" {\n\t\t\t\tvar n int\n\t\t\t\tfmt.Sscanf(q, \"%d\", &n)\n\t\t\t\tch.que.SetMaxGopCount(n)\n\t\t\t}\n\t\t\tchannels[conn.URL.Path] = ch\n\t\t} else {\n\t\t\tch = nil\n\t\t}\n\t\tl.Unlock()\n\t\tif ch == nil {\n\t\t\treturn\n\t\t}\n\n\t\tavutil.CopyFile(ch.que, conn)\n\n\t\tl.Lock()\n\t\tdelete(channels, conn.URL.Path)\n\t\tl.Unlock()\n\t\tch.que.Close()\n\t}\n\n\tserver.ListenAndServe()\n\n\t// ffmpeg -re -i movie.flv -c copy -f flv rtmp://localhost/movie\n\t// ffmpeg -f avfoundation -i \"0:0\" .... -f flv rtmp://localhost/screen\n\n\t// with cache size options\n\n\t// ffplay rtmp://localhost/movie\n\t// ffplay rtmp://localhost/screen\n\t// ffplay rtmp://localhost/movie?delaytime=5s\n\t// ffplay rtmp://localhost/movie?delaytime=10s&waitkey=true\n\t// ffplay rtmp://localhost/movie?delaytime=20s\n\n\t// ffmpeg -re -i movie.flv -c copy -f flv rtmp://localhost/movie?cachegop=2\n\t// ffmpeg -re -i movie.flv -c copy -f flv rtmp://localhost/movie?cachegop=1\n}\n"
  },
  {
    "path": "examples/rtmp_server_proxy/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"github.com/nareix/joy4/format\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/format/rtmp\"\n)\n\nfunc init() {\n\tformat.RegisterAll()\n}\n\nfunc main() {\n\tserver := &rtmp.Server{}\n\n\tserver.HandlePlay = func(conn *rtmp.Conn) {\n\t\tsegs := strings.Split(conn.URL.Path, \"/\")\n\t\turl := fmt.Sprintf(\"%s://%s\", segs[1], strings.Join(segs[2:], \"/\"))\n\t\tsrc, _ := avutil.Open(url)\n\t\tavutil.CopyFile(conn, src)\n\t}\n\n\tserver.ListenAndServe()\n\n\t// ffplay rtmp://localhost/rtsp/192.168.1.1/camera1\n\t// ffplay rtmp://localhost/rtmp/live.hkstv.hk.lxdns.com/live/hks\n}\n"
  },
  {
    "path": "examples/rtmp_server_speex_to_aac/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/transcode\"\n\t\"github.com/nareix/joy4/format\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/format/rtmp\"\n\t\"github.com/nareix/joy4/cgo/ffmpeg\"\n)\n\n// need ffmpeg with libspeex and libfdkaac installed\n// \n// open http://www.wowza.com/resources/4.4.1/examples/WebcamRecording/FlashRTMPPlayer11/player.html\n// click connect and recored\n// input camera H264/SPEEX will converted H264/AAC and saved in out.ts\n\nfunc init() {\n\tformat.RegisterAll()\n}\n\nfunc main() {\n\tserver := &rtmp.Server{}\n\n\tserver.HandlePublish = func(conn *rtmp.Conn) {\n\t\tfile, _ := avutil.Create(\"out.ts\")\n\n\t\tfindcodec := func(stream av.AudioCodecData, i int) (need bool, dec av.AudioDecoder, enc av.AudioEncoder, err error) {\n\t\t\tneed = true\n\t\t\tdec, _ = ffmpeg.NewAudioDecoder(stream)\n\t\t\tenc, _ = ffmpeg.NewAudioEncoderByName(\"libfdk_aac\")\n\t\t\tenc.SetSampleRate(48000)\n\t\t\tenc.SetChannelLayout(av.CH_STEREO)\n\t\t\treturn\n\t\t}\n\n\t\ttrans := &transcode.Demuxer{\n\t\t\tOptions: transcode.Options{\n\t\t\t\tFindAudioDecoderEncoder: findcodec,\n\t\t\t},\n\t\t\tDemuxer: conn,\n\t\t}\n\n\t\tavutil.CopyFile(file, trans)\n\t}\n\n\tserver.ListenAndServe()\n}\n"
  },
  {
    "path": "examples/transcode/main.go",
    "content": "package main\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/transcode\"\n\t\"github.com/nareix/joy4/format\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/cgo/ffmpeg\"\n)\n\n// need ffmpeg with libfdkaac installed\n\nfunc init() {\n\tformat.RegisterAll()\n}\n\nfunc main() {\n\tinfile, _ := avutil.Open(\"speex.flv\")\n\n\tfindcodec := func(stream av.AudioCodecData, i int) (need bool, dec av.AudioDecoder, enc av.AudioEncoder, err error) {\n\t\tneed = true\n\t\tdec, _ = ffmpeg.NewAudioDecoder(stream)\n\t\tenc, _ = ffmpeg.NewAudioEncoderByName(\"libfdk_aac\")\n\t\tenc.SetSampleRate(stream.SampleRate())\n\t\tenc.SetChannelLayout(av.CH_STEREO)\n\t\tenc.SetBitrate(12000)\n\t\tenc.SetOption(\"profile\", \"HE-AACv2\")\n\t\treturn\n\t}\n\n\ttrans := &transcode.Demuxer{\n\t\tOptions: transcode.Options{\n\t\t\tFindAudioDecoderEncoder: findcodec,\n\t\t},\n\t\tDemuxer: infile,\n\t}\n\n\toutfile, _ := avutil.Create(\"out.ts\")\n\tavutil.CopyFile(outfile, trans)\n\n\toutfile.Close()\n\tinfile.Close()\n\ttrans.Close()\n}\n\n"
  },
  {
    "path": "format/aac/aac.go",
    "content": "\npackage aac\n\nimport (\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/codec/aacparser\"\n\t\"time\"\n\t\"fmt\"\n\t\"io\"\n\t\"bufio\"\n)\n\ntype Muxer struct {\n\tw io.Writer\n\tconfig aacparser.MPEG4AudioConfig\n\tadtshdr []byte\n}\n\nfunc NewMuxer(w io.Writer) *Muxer {\n\treturn &Muxer{\n\t\tadtshdr: make([]byte, aacparser.ADTSHeaderLength),\n\t\tw: w,\n\t}\n}\n\nfunc (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {\n\tif len(streams) > 1 || streams[0].Type() != av.AAC {\n\t\terr = fmt.Errorf(\"aac: must be only one aac stream\")\n\t\treturn\n\t}\n\tself.config = streams[0].(aacparser.CodecData).Config\n\tif self.config.ObjectType > aacparser.AOT_AAC_LTP {\n\t\terr = fmt.Errorf(\"aac: AOT %d is not allowed in ADTS\", self.config.ObjectType)\n\t}\n\treturn\n}\n\nfunc (self *Muxer) WritePacket(pkt av.Packet) (err error) {\n\taacparser.FillADTSHeader(self.adtshdr, self.config, 1024, len(pkt.Data))\n\tif _, err = self.w.Write(self.adtshdr); err != nil {\n\t\treturn\n\t}\n\tif _, err = self.w.Write(pkt.Data); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Muxer) WriteTrailer() (err error) {\n\treturn\n}\n\ntype Demuxer struct {\n\tr *bufio.Reader\n\tconfig aacparser.MPEG4AudioConfig\n\tcodecdata av.CodecData\n\tts time.Duration\n}\n\nfunc NewDemuxer(r io.Reader) *Demuxer {\n\treturn &Demuxer{\n\t\tr: bufio.NewReader(r),\n\t}\n}\n\nfunc (self *Demuxer) Streams() (streams []av.CodecData, err error) {\n\tif self.codecdata == nil {\n\t\tvar adtshdr []byte\n\t\tvar config aacparser.MPEG4AudioConfig\n\t\tif adtshdr, err = self.r.Peek(9); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif config, _, _, _, err = aacparser.ParseADTSHeader(adtshdr); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.codecdata, err = aacparser.NewCodecDataFromMPEG4AudioConfig(config); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tstreams = []av.CodecData{self.codecdata}\n\treturn\n}\n\nfunc (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {\n\tvar adtshdr []byte\n\tvar config aacparser.MPEG4AudioConfig\n\tvar hdrlen, framelen, samples int\n\tif adtshdr, err = self.r.Peek(9); err != nil {\n\t\treturn\n\t}\n\tif config, hdrlen, framelen, samples, err = aacparser.ParseADTSHeader(adtshdr); err != nil {\n\t\treturn\n\t}\n\n\tpkt.Data = make([]byte, framelen)\n\tif _, err = io.ReadFull(self.r, pkt.Data); err != nil {\n\t\treturn\n\t}\n\tpkt.Data = pkt.Data[hdrlen:]\n\n\tpkt.Time = self.ts\n\tself.ts += time.Duration(samples) * time.Second / time.Duration(config.SampleRate)\n\treturn\n}\n\nfunc Handler(h *avutil.RegisterHandler) {\n\th.Ext = \".aac\"\n\n\th.ReaderDemuxer = func(r io.Reader) av.Demuxer {\n\t\treturn NewDemuxer(r)\n\t}\n\n\th.WriterMuxer = func(w io.Writer) av.Muxer {\n\t\treturn NewMuxer(w)\n\t}\n\n\th.Probe = func(b []byte) bool {\n\t\t_, _, _, _, err := aacparser.ParseADTSHeader(b)\n\t\treturn err == nil\n\t}\n\n\th.CodecTypes = []av.CodecType{av.AAC}\n}\n"
  },
  {
    "path": "format/flv/flv.go",
    "content": "package flv\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/codec\"\n\t\"github.com/nareix/joy4/codec/aacparser\"\n\t\"github.com/nareix/joy4/codec/fake\"\n\t\"github.com/nareix/joy4/codec/h264parser\"\n\t\"github.com/nareix/joy4/format/flv/flvio\"\n\t\"io\"\n)\n\nvar MaxProbePacketCount = 20\n\nfunc NewMetadataByStreams(streams []av.CodecData) (metadata flvio.AMFMap, err error) {\n\tmetadata = flvio.AMFMap{}\n\n\tfor _, _stream := range streams {\n\t\ttyp := _stream.Type()\n\t\tswitch {\n\t\tcase typ.IsVideo():\n\t\t\tstream := _stream.(av.VideoCodecData)\n\t\t\tswitch typ {\n\t\t\tcase av.H264:\n\t\t\t\tmetadata[\"videocodecid\"] = flvio.VIDEO_H264\n\n\t\t\tdefault:\n\t\t\t\terr = fmt.Errorf(\"flv: metadata: unsupported video codecType=%v\", stream.Type())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tmetadata[\"width\"] = stream.Width()\n\t\t\tmetadata[\"height\"] = stream.Height()\n\t\t\tmetadata[\"displayWidth\"] = stream.Width()\n\t\t\tmetadata[\"displayHeight\"] = stream.Height()\n\n\t\tcase typ.IsAudio():\n\t\t\tstream := _stream.(av.AudioCodecData)\n\t\t\tswitch typ {\n\t\t\tcase av.AAC:\n\t\t\t\tmetadata[\"audiocodecid\"] = flvio.SOUND_AAC\n\n\t\t\tcase av.SPEEX:\n\t\t\t\tmetadata[\"audiocodecid\"] = flvio.SOUND_SPEEX\n\n\t\t\tdefault:\n\t\t\t\terr = fmt.Errorf(\"flv: metadata: unsupported audio codecType=%v\", stream.Type())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tmetadata[\"audiosamplerate\"] = stream.SampleRate()\n\t\t}\n\t}\n\n\treturn\n}\n\ntype Prober struct {\n\tHasAudio, HasVideo             bool\n\tGotAudio, GotVideo             bool\n\tVideoStreamIdx, AudioStreamIdx int\n\tPushedCount                    int\n\tStreams                        []av.CodecData\n\tCachedPkts                     []av.Packet\n}\n\nfunc (self *Prober) CacheTag(_tag flvio.Tag, timestamp int32) {\n\tpkt, _ := self.TagToPacket(_tag, timestamp)\n\tself.CachedPkts = append(self.CachedPkts, pkt)\n}\n\nfunc (self *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) {\n\tself.PushedCount++\n\n\tif self.PushedCount > MaxProbePacketCount {\n\t\terr = fmt.Errorf(\"flv: max probe packet count reached\")\n\t\treturn\n\t}\n\n\tswitch tag.Type {\n\tcase flvio.TAG_VIDEO:\n\t\tswitch tag.AVCPacketType {\n\t\tcase flvio.AVC_SEQHDR:\n\t\t\tif !self.GotVideo {\n\t\t\t\tvar stream h264parser.CodecData\n\t\t\t\tif stream, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(tag.Data); err != nil {\n\t\t\t\t\terr = fmt.Errorf(\"flv: h264 seqhdr invalid\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.VideoStreamIdx = len(self.Streams)\n\t\t\t\tself.Streams = append(self.Streams, stream)\n\t\t\t\tself.GotVideo = true\n\t\t\t}\n\n\t\tcase flvio.AVC_NALU:\n\t\t\tself.CacheTag(tag, timestamp)\n\t\t}\n\n\tcase flvio.TAG_AUDIO:\n\t\tswitch tag.SoundFormat {\n\t\tcase flvio.SOUND_AAC:\n\t\t\tswitch tag.AACPacketType {\n\t\t\tcase flvio.AAC_SEQHDR:\n\t\t\t\tif !self.GotAudio {\n\t\t\t\t\tvar stream aacparser.CodecData\n\t\t\t\t\tif stream, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(tag.Data); err != nil {\n\t\t\t\t\t\terr = fmt.Errorf(\"flv: aac seqhdr invalid\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tself.AudioStreamIdx = len(self.Streams)\n\t\t\t\t\tself.Streams = append(self.Streams, stream)\n\t\t\t\t\tself.GotAudio = true\n\t\t\t\t}\n\n\t\t\tcase flvio.AAC_RAW:\n\t\t\t\tself.CacheTag(tag, timestamp)\n\t\t\t}\n\n\t\tcase flvio.SOUND_SPEEX:\n\t\t\tif !self.GotAudio {\n\t\t\t\tstream := codec.NewSpeexCodecData(16000, tag.ChannelLayout())\n\t\t\t\tself.AudioStreamIdx = len(self.Streams)\n\t\t\t\tself.Streams = append(self.Streams, stream)\n\t\t\t\tself.GotAudio = true\n\t\t\t\tself.CacheTag(tag, timestamp)\n\t\t\t}\n\n\t\tcase flvio.SOUND_NELLYMOSER:\n\t\t\tif !self.GotAudio {\n\t\t\t\tstream := fake.CodecData{\n\t\t\t\t\tCodecType_:     av.NELLYMOSER,\n\t\t\t\t\tSampleRate_:    16000,\n\t\t\t\t\tSampleFormat_:  av.S16,\n\t\t\t\t\tChannelLayout_: tag.ChannelLayout(),\n\t\t\t\t}\n\t\t\t\tself.AudioStreamIdx = len(self.Streams)\n\t\t\t\tself.Streams = append(self.Streams, stream)\n\t\t\t\tself.GotAudio = true\n\t\t\t\tself.CacheTag(tag, timestamp)\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Prober) Probed() (ok bool) {\n\tif self.HasAudio || self.HasVideo {\n\t\tif self.HasAudio == self.GotAudio && self.HasVideo == self.GotVideo {\n\t\t\treturn true\n\t\t}\n\t} else {\n\t\tif self.PushedCount == MaxProbePacketCount {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Prober) TagToPacket(tag flvio.Tag, timestamp int32) (pkt av.Packet, ok bool) {\n\tswitch tag.Type {\n\tcase flvio.TAG_VIDEO:\n\t\tpkt.Idx = int8(self.VideoStreamIdx)\n\t\tswitch tag.AVCPacketType {\n\t\tcase flvio.AVC_NALU:\n\t\t\tok = true\n\t\t\tpkt.Data = tag.Data\n\t\t\tpkt.CompositionTime = flvio.TsToTime(tag.CompositionTime)\n\t\t\tpkt.IsKeyFrame = tag.FrameType == flvio.FRAME_KEY\n\t\t}\n\n\tcase flvio.TAG_AUDIO:\n\t\tpkt.Idx = int8(self.AudioStreamIdx)\n\t\tswitch tag.SoundFormat {\n\t\tcase flvio.SOUND_AAC:\n\t\t\tswitch tag.AACPacketType {\n\t\t\tcase flvio.AAC_RAW:\n\t\t\t\tok = true\n\t\t\t\tpkt.Data = tag.Data\n\t\t\t}\n\n\t\tcase flvio.SOUND_SPEEX:\n\t\t\tok = true\n\t\t\tpkt.Data = tag.Data\n\n\t\tcase flvio.SOUND_NELLYMOSER:\n\t\t\tok = true\n\t\t\tpkt.Data = tag.Data\n\t\t}\n\t}\n\n\tpkt.Time = flvio.TsToTime(timestamp)\n\treturn\n}\n\nfunc (self *Prober) Empty() bool {\n\treturn len(self.CachedPkts) == 0\n}\n\nfunc (self *Prober) PopPacket() av.Packet {\n\tpkt := self.CachedPkts[0]\n\tself.CachedPkts = self.CachedPkts[1:]\n\treturn pkt\n}\n\nfunc CodecDataToTag(stream av.CodecData) (_tag flvio.Tag, ok bool, err error) {\n\tswitch stream.Type() {\n\tcase av.H264:\n\t\th264 := stream.(h264parser.CodecData)\n\t\ttag := flvio.Tag{\n\t\t\tType:          flvio.TAG_VIDEO,\n\t\t\tAVCPacketType: flvio.AVC_SEQHDR,\n\t\t\tCodecID:       flvio.VIDEO_H264,\n\t\t\tData:          h264.AVCDecoderConfRecordBytes(),\n\t\t\tFrameType:     flvio.FRAME_KEY,\n\t\t}\n\t\tok = true\n\t\t_tag = tag\n\n\tcase av.NELLYMOSER:\n\tcase av.SPEEX:\n\n\tcase av.AAC:\n\t\taac := stream.(aacparser.CodecData)\n\t\ttag := flvio.Tag{\n\t\t\tType:          flvio.TAG_AUDIO,\n\t\t\tSoundFormat:   flvio.SOUND_AAC,\n\t\t\tSoundRate:     flvio.SOUND_44Khz,\n\t\t\tAACPacketType: flvio.AAC_SEQHDR,\n\t\t\tData:          aac.MPEG4AudioConfigBytes(),\n\t\t}\n\t\tswitch aac.SampleFormat().BytesPerSample() {\n\t\tcase 1:\n\t\t\ttag.SoundSize = flvio.SOUND_8BIT\n\t\tdefault:\n\t\t\ttag.SoundSize = flvio.SOUND_16BIT\n\t\t}\n\t\tswitch aac.ChannelLayout().Count() {\n\t\tcase 1:\n\t\t\ttag.SoundType = flvio.SOUND_MONO\n\t\tcase 2:\n\t\t\ttag.SoundType = flvio.SOUND_STEREO\n\t\t}\n\t\tok = true\n\t\t_tag = tag\n\n\tdefault:\n\t\terr = fmt.Errorf(\"flv: unspported codecType=%v\", stream.Type())\n\t\treturn\n\t}\n\treturn\n}\n\nfunc PacketToTag(pkt av.Packet, stream av.CodecData) (tag flvio.Tag, timestamp int32) {\n\tswitch stream.Type() {\n\tcase av.H264:\n\t\ttag = flvio.Tag{\n\t\t\tType:            flvio.TAG_VIDEO,\n\t\t\tAVCPacketType:   flvio.AVC_NALU,\n\t\t\tCodecID:         flvio.VIDEO_H264,\n\t\t\tData:            pkt.Data,\n\t\t\tCompositionTime: flvio.TimeToTs(pkt.CompositionTime),\n\t\t}\n\t\tif pkt.IsKeyFrame {\n\t\t\ttag.FrameType = flvio.FRAME_KEY\n\t\t} else {\n\t\t\ttag.FrameType = flvio.FRAME_INTER\n\t\t}\n\n\tcase av.AAC:\n\t\ttag = flvio.Tag{\n\t\t\tType:          flvio.TAG_AUDIO,\n\t\t\tSoundFormat:   flvio.SOUND_AAC,\n\t\t\tSoundRate:     flvio.SOUND_44Khz,\n\t\t\tAACPacketType: flvio.AAC_RAW,\n\t\t\tData:          pkt.Data,\n\t\t}\n\t\tastream := stream.(av.AudioCodecData)\n\t\tswitch astream.SampleFormat().BytesPerSample() {\n\t\tcase 1:\n\t\t\ttag.SoundSize = flvio.SOUND_8BIT\n\t\tdefault:\n\t\t\ttag.SoundSize = flvio.SOUND_16BIT\n\t\t}\n\t\tswitch astream.ChannelLayout().Count() {\n\t\tcase 1:\n\t\t\ttag.SoundType = flvio.SOUND_MONO\n\t\tcase 2:\n\t\t\ttag.SoundType = flvio.SOUND_STEREO\n\t\t}\n\n\tcase av.SPEEX:\n\t\ttag = flvio.Tag{\n\t\t\tType:        flvio.TAG_AUDIO,\n\t\t\tSoundFormat: flvio.SOUND_SPEEX,\n\t\t\tData:        pkt.Data,\n\t\t}\n\n\tcase av.NELLYMOSER:\n\t\ttag = flvio.Tag{\n\t\t\tType:        flvio.TAG_AUDIO,\n\t\t\tSoundFormat: flvio.SOUND_NELLYMOSER,\n\t\t\tData:        pkt.Data,\n\t\t}\n\t}\n\n\ttimestamp = flvio.TimeToTs(pkt.Time)\n\treturn\n}\n\ntype Muxer struct {\n\tbufw    writeFlusher\n\tb       []byte\n\tstreams []av.CodecData\n}\n\ntype writeFlusher interface {\n\tio.Writer\n\tFlush() error\n}\n\nfunc NewMuxerWriteFlusher(w writeFlusher) *Muxer {\n\treturn &Muxer{\n\t\tbufw: w,\n\t\tb:    make([]byte, 256),\n\t}\n}\n\nfunc NewMuxer(w io.Writer) *Muxer {\n\treturn NewMuxerWriteFlusher(bufio.NewWriterSize(w, pio.RecommendBufioSize))\n}\n\nvar CodecTypes = []av.CodecType{av.H264, av.AAC, av.SPEEX}\n\nfunc (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {\n\tvar flags uint8\n\tfor _, stream := range streams {\n\t\tif stream.Type().IsVideo() {\n\t\t\tflags |= flvio.FILE_HAS_VIDEO\n\t\t} else if stream.Type().IsAudio() {\n\t\t\tflags |= flvio.FILE_HAS_AUDIO\n\t\t}\n\t}\n\n\tn := flvio.FillFileHeader(self.b, flags)\n\tif _, err = self.bufw.Write(self.b[:n]); err != nil {\n\t\treturn\n\t}\n\n\tfor _, stream := range streams {\n\t\tvar tag flvio.Tag\n\t\tvar ok bool\n\t\tif tag, ok, err = CodecDataToTag(stream); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif ok {\n\t\t\tif err = flvio.WriteTag(self.bufw, tag, 0, self.b); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tself.streams = streams\n\treturn\n}\n\nfunc (self *Muxer) WritePacket(pkt av.Packet) (err error) {\n\tstream := self.streams[pkt.Idx]\n\ttag, timestamp := PacketToTag(pkt, stream)\n\n\tif err = flvio.WriteTag(self.bufw, tag, timestamp, self.b); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Muxer) WriteTrailer() (err error) {\n\tif err = self.bufw.Flush(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\ntype Demuxer struct {\n\tprober *Prober\n\tbufr   *bufio.Reader\n\tb      []byte\n\tstage  int\n}\n\nfunc NewDemuxer(r io.Reader) *Demuxer {\n\treturn &Demuxer{\n\t\tbufr:   bufio.NewReaderSize(r, pio.RecommendBufioSize),\n\t\tprober: &Prober{},\n\t\tb:      make([]byte, 256),\n\t}\n}\n\nfunc (self *Demuxer) prepare() (err error) {\n\tfor self.stage < 2 {\n\t\tswitch self.stage {\n\t\tcase 0:\n\t\t\tif _, err = io.ReadFull(self.bufr, self.b[:flvio.FileHeaderLength]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvar flags uint8\n\t\t\tvar skip int\n\t\t\tif flags, skip, err = flvio.ParseFileHeader(self.b); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif _, err = self.bufr.Discard(skip); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif flags&flvio.FILE_HAS_AUDIO != 0 {\n\t\t\t\tself.prober.HasAudio = true\n\t\t\t}\n\t\t\tif flags&flvio.FILE_HAS_VIDEO != 0 {\n\t\t\t\tself.prober.HasVideo = true\n\t\t\t}\n\t\t\tself.stage++\n\n\t\tcase 1:\n\t\t\tfor !self.prober.Probed() {\n\t\t\t\tvar tag flvio.Tag\n\t\t\t\tvar timestamp int32\n\t\t\t\tif tag, timestamp, err = flvio.ReadTag(self.bufr, self.b); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif err = self.prober.PushTag(tag, timestamp); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tself.stage++\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) Streams() (streams []av.CodecData, err error) {\n\tif err = self.prepare(); err != nil {\n\t\treturn\n\t}\n\tstreams = self.prober.Streams\n\treturn\n}\n\nfunc (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {\n\tif err = self.prepare(); err != nil {\n\t\treturn\n\t}\n\n\tif !self.prober.Empty() {\n\t\tpkt = self.prober.PopPacket()\n\t\treturn\n\t}\n\n\tfor {\n\t\tvar tag flvio.Tag\n\t\tvar timestamp int32\n\t\tif tag, timestamp, err = flvio.ReadTag(self.bufr, self.b); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tvar ok bool\n\t\tif pkt, ok = self.prober.TagToPacket(tag, timestamp); ok {\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc Handler(h *avutil.RegisterHandler) {\n\th.Probe = func(b []byte) bool {\n\t\treturn b[0] == 'F' && b[1] == 'L' && b[2] == 'V'\n\t}\n\n\th.Ext = \".flv\"\n\n\th.ReaderDemuxer = func(r io.Reader) av.Demuxer {\n\t\treturn NewDemuxer(r)\n\t}\n\n\th.WriterMuxer = func(w io.Writer) av.Muxer {\n\t\treturn NewMuxer(w)\n\t}\n\n\th.CodecTypes = CodecTypes\n}\n"
  },
  {
    "path": "format/flv/flvio/amf0.go",
    "content": "package flvio\n\nimport (\n\t\"strings\"\n\t\"math\"\n\t\"fmt\"\n\t\"time\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n)\n\ntype AMF0ParseError struct {\n\tOffset int\n\tMessage string\n\tNext *AMF0ParseError\n}\n\nfunc (self *AMF0ParseError) Error() string {\n\ts := []string{}\n\tfor p := self; p != nil; p = p.Next {\n\t\ts = append(s, fmt.Sprintf(\"%s:%d\", p.Message, p.Offset))\n\t}\n\treturn \"amf0 parse error: \" + strings.Join(s, \",\")\n}\n\nfunc amf0ParseErr(message string, offset int, err error) error {\n\tnext, _ := err.(*AMF0ParseError)\n\treturn &AMF0ParseError{\n\t\tOffset: offset,\n\t\tMessage: message,\n\t\tNext: next,\n\t}\n}\n\ntype AMFMap map[string]interface{}\ntype AMFArray []interface{}\ntype AMFECMAArray map[string]interface{}\n\nfunc parseBEFloat64(b []byte) float64 {\n\treturn math.Float64frombits(pio.U64BE(b))\n}\n\nfunc fillBEFloat64(b []byte, f float64) int {\n\tpio.PutU64BE(b, math.Float64bits(f))\n\treturn 8\n}\n\nconst lenAMF0Number = 9\n\nfunc fillAMF0Number(b []byte, f float64) int {\n\tb[0] = numbermarker\n\tfillBEFloat64(b[1:], f)\n\treturn lenAMF0Number\n}\n\nconst (\n\tamf3undefinedmarker = iota\n\tamf3nullmarker\n\tamf3falsemarker\n\tamf3truemarker\n\tamf3integermarker\n\tamf3doublemarker\n\tamf3stringmarker\n\tamf3xmldocmarker\n\tamf3datemarker\n\tamf3arraymarker\n\tamf3objectmarker\n\tamf3xmlmarker\n\tamf3bytearraymarker\n\tamf3vectorintmarker\n\tamf3vectoruintmarker\n\tamf3vectordoublemarker\n\tamf3vectorobjectmarker\n\tamf3dictionarymarker\n)\n\nconst (\n\tnumbermarker = iota\n\tbooleanmarker\n\tstringmarker\n\tobjectmarker\n\tmovieclipmarker\n\tnullmarker\n\tundefinedmarker\n\treferencemarker\n\tecmaarraymarker\n\tobjectendmarker\n\tstrictarraymarker\n\tdatemarker\n\tlongstringmarker\n\tunsupportedmarker\n\trecordsetmarker\n\txmldocumentmarker\n\ttypedobjectmarker\n\tavmplusobjectmarker\n)\n\nfunc LenAMF0Val(_val interface{}) (n int) {\n\tswitch val := _val.(type) {\n\tcase int8:\n\t\tn += lenAMF0Number\n\tcase int16:\n\t\tn += lenAMF0Number\n\tcase int32:\n\t\tn += lenAMF0Number\n\tcase int64:\n\t\tn += lenAMF0Number\n\tcase int:\n\t\tn += lenAMF0Number\n\tcase uint8:\n\t\tn += lenAMF0Number\n\tcase uint16:\n\t\tn += lenAMF0Number\n\tcase uint32:\n\t\tn += lenAMF0Number\n\tcase uint64:\n\t\tn += lenAMF0Number\n\tcase uint:\n\t\tn += lenAMF0Number\n\tcase float32:\n\t\tn += lenAMF0Number\n\tcase float64:\n\t\tn += lenAMF0Number\n\n\tcase string:\n\t\tu := len(val)\n\t\tif u <= 65536 {\n\t\t\tn += 3\n\t\t} else {\n\t\t\tn += 5\n\t\t}\n\t\tn += int(u)\n\n\tcase AMFECMAArray:\n\t\tn += 5\n\t\tfor k, v := range val {\n\t\t\tn += 2+len(k)\n\t\t\tn += LenAMF0Val(v)\n\t\t}\n\t\tn += 3\n\n\tcase AMFMap:\n\t\tn++\n\t\tfor k, v := range val {\n\t\t\tif len(k) > 0 {\n\t\t\t\tn += 2+len(k)\n\t\t\t\tn += LenAMF0Val(v)\n\t\t\t}\n\t\t}\n\t\tn += 3\n\n\tcase AMFArray:\n\t\tn += 5\n\t\tfor _, v := range val {\n\t\t\tn += LenAMF0Val(v)\n\t\t}\n\n\tcase time.Time:\n\t\tn += 1+8+2\n\n\tcase bool:\n\t\tn += 2\n\n\tcase nil:\n\t\tn++\n\t}\n\n\treturn\n}\n\nfunc FillAMF0Val(b []byte, _val interface{}) (n int) {\n\tswitch val := _val.(type) {\n\tcase int8:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase int16:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase int32:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase int64:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase int:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase uint8:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase uint16:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase uint32:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase uint64:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase uint:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase float32:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\tcase float64:\n\t\tn += fillAMF0Number(b[n:], float64(val))\n\n\tcase string:\n\t\tu := len(val)\n\t\tif u <= 65536 {\n\t\t\tb[n] = stringmarker\n\t\t\tn++\n\t\t\tpio.PutU16BE(b[n:], uint16(u))\n\t\t\tn += 2\n\t\t} else {\n\t\t\tb[n] = longstringmarker\n\t\t\tn++\n\t\t\tpio.PutU32BE(b[n:], uint32(u))\n\t\t\tn += 4\n\t\t}\n\t\tcopy(b[n:], []byte(val))\n\t\tn += len(val)\n\n\tcase AMFECMAArray:\n\t\tb[n] = ecmaarraymarker\n\t\tn++\n\t\tpio.PutU32BE(b[n:], uint32(len(val)))\n\t\tn += 4\n\t\tfor k, v := range val {\n\t\t\tpio.PutU16BE(b[n:], uint16(len(k)))\n\t\t\tn += 2\n\t\t\tcopy(b[n:], []byte(k))\n\t\t\tn += len(k)\n\t\t\tn += FillAMF0Val(b[n:], v)\n\t\t}\n\t\tpio.PutU24BE(b[n:], 0x000009)\n\t\tn += 3\n\n\tcase AMFMap:\n\t\tb[n] = objectmarker\n\t\tn++\n\t\tfor k, v := range val {\n\t\t\tif len(k) > 0 {\n\t\t\t\tpio.PutU16BE(b[n:], uint16(len(k)))\n\t\t\t\tn += 2\n\t\t\t\tcopy(b[n:], []byte(k))\n\t\t\t\tn += len(k)\n\t\t\t\tn += FillAMF0Val(b[n:], v)\n\t\t\t}\n\t\t}\n\t\tpio.PutU24BE(b[n:], 0x000009)\n\t\tn += 3\n\n\tcase AMFArray:\n\t\tb[n] = strictarraymarker\n\t\tn++\n\t\tpio.PutU32BE(b[n:], uint32(len(val)))\n\t\tn += 4\n\t\tfor _, v := range val {\n\t\t\tn += FillAMF0Val(b[n:], v)\n\t\t}\n\n\tcase time.Time:\n\t\tb[n] = datemarker\n\t\tn++\n\t\tu := val.UnixNano()\n\t\tf := float64(u/1000000)\n\t\tn += fillBEFloat64(b[n:], f)\n\t\tpio.PutU16BE(b[n:], uint16(0))\n\t\tn += 2\n\n\tcase bool:\n\t\tb[n] = booleanmarker\n\t\tn++\n\t\tvar u uint8\n\t\tif val {\n\t\t\tu = 1\n\t\t} else {\n\t\t\tu = 0\n\t\t}\n\t\tb[n] = u\n\t\tn++\n\n\tcase nil:\n\t\tb[n] = nullmarker\n\t\tn++\n\t}\n\n\treturn\n}\n\n\nfunc ParseAMF0Val(b []byte) (val interface{}, n int, err error) {\n\treturn parseAMF0Val(b, 0)\n}\n\nfunc parseAMF0Val(b []byte, offset int) (val interface{}, n int, err error) {\n\tif len(b) < n+1 {\n\t\terr = amf0ParseErr(\"marker\", offset+n, err)\n\t\treturn\n\t}\n\tmarker := b[n]\n\tn++\n\n\tswitch marker {\n\tcase numbermarker:\n\t\tif len(b) < n+8 {\n\t\t\terr = amf0ParseErr(\"number\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tval = parseBEFloat64(b[n:])\n\t\tn += 8\n\n\tcase booleanmarker:\n\t\tif len(b) < n+1 {\n\t\t\terr = amf0ParseErr(\"boolean\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tval = b[n] != 0\n\t\tn++\n\n\tcase stringmarker:\n\t\tif len(b) < n+2 {\n\t\t\terr = amf0ParseErr(\"string.length\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tlength := int(pio.U16BE(b[n:]))\n\t\tn += 2\n\n\t\tif len(b) < n+length {\n\t\t\terr = amf0ParseErr(\"string.body\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tval = string(b[n:n+length])\n\t\tn += length\n\n\tcase objectmarker:\n\t\tobj := AMFMap{}\n\t\tfor {\n\t\t\tif len(b) < n+2 {\n\t\t\t\terr = amf0ParseErr(\"object.key.length\", offset+n, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlength := int(pio.U16BE(b[n:]))\n\t\t\tn += 2\n\t\t\tif length == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif len(b) < n+length {\n\t\t\t\terr = amf0ParseErr(\"object.key.body\", offset+n, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tokey := string(b[n:n+length])\n\t\t\tn += length\n\n\t\t\tvar nval int\n\t\t\tvar oval interface{}\n\t\t\tif oval, nval, err = parseAMF0Val(b[n:], offset+n); err != nil {\n\t\t\t\terr = amf0ParseErr(\"object.val\", offset+n, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tn += nval\n\n\t\t\tobj[okey] = oval\n\t\t}\n\t\tif len(b) < n+1 {\n\t\t\terr = amf0ParseErr(\"object.end\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tn++\n\t\tval = obj\n\n\tcase nullmarker:\n\tcase undefinedmarker:\n\n\tcase ecmaarraymarker:\n\t\tif len(b) < n+4 {\n\t\t\terr = amf0ParseErr(\"array.count\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tn += 4\n\n\t\tobj := AMFMap{}\n\t\tfor {\n\t\t\tif len(b) < n+2 {\n\t\t\t\terr = amf0ParseErr(\"array.key.length\", offset+n, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlength := int(pio.U16BE(b[n:]))\n\t\t\tn += 2\n\n\t\t\tif length == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif len(b) < n+length {\n\t\t\t\terr = amf0ParseErr(\"array.key.body\", offset+n, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tokey := string(b[n:n+length])\n\t\t\tn += length\n\n\t\t\tvar nval int\n\t\t\tvar oval interface{}\n\t\t\tif oval, nval, err = parseAMF0Val(b[n:], offset+n); err != nil {\n\t\t\t\terr = amf0ParseErr(\"array.val\", offset+n, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tn += nval\n\n\t\t\tobj[okey] = oval\n\t\t}\n\t\tif len(b) < n+1 {\n\t\t\terr = amf0ParseErr(\"array.end\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tn += 1\n\t\tval = obj\n\n\tcase objectendmarker:\n\t\tif len(b) < n+3 {\n\t\t\terr = amf0ParseErr(\"objectend\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tn += 3\n\n\tcase strictarraymarker:\n\t\tif len(b) < n+4 {\n\t\t\terr = amf0ParseErr(\"strictarray.count\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tcount := int(pio.U32BE(b[n:]))\n\t\tn += 4\n\n\t\tobj := make(AMFArray, count)\n\t\tfor i := 0; i < int(count); i++ {\n\t\t\tvar nval int\n\t\t\tif obj[i], nval, err = parseAMF0Val(b[n:], offset+n); err != nil {\n\t\t\t\terr = amf0ParseErr(\"strictarray.val\", offset+n, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tn += nval\n\t\t}\n\t\tval = obj\n\n\tcase datemarker:\n\t\tif len(b) < n+8+2 {\n\t\t\terr = amf0ParseErr(\"date\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tts := parseBEFloat64(b[n:])\n\t\tn += 8+2\n\n\t\tval = time.Unix(int64(ts/1000), (int64(ts)%1000)*1000000)\n\n\tcase longstringmarker:\n\t\tif len(b) < n+4 {\n\t\t\terr = amf0ParseErr(\"longstring.length\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tlength := int(pio.U32BE(b[n:]))\n\t\tn += 4\n\n\t\tif len(b) < n+length {\n\t\t\terr = amf0ParseErr(\"longstring.body\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tval = string(b[n:n+length])\n\t\tn += length\n\n\tdefault:\n\t\terr = amf0ParseErr(fmt.Sprintf(\"invalidmarker=%d\", marker), offset+n, err)\n\t\treturn\n\t}\n\n\treturn\n}\n\n"
  },
  {
    "path": "format/flv/flvio/flvio.go",
    "content": "package flvio\n\nimport (\n\t\"fmt\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n\t\"github.com/nareix/joy4/av\"\n\t\"io\"\n\t\"time\"\n)\n\nfunc TsToTime(ts int32) time.Duration {\n\treturn time.Millisecond * time.Duration(ts)\n}\n\nfunc TimeToTs(tm time.Duration) int32 {\n\treturn int32(tm / time.Millisecond)\n}\n\nconst MaxTagSubHeaderLength = 16\n\nconst (\n\tTAG_AUDIO      = 8\n\tTAG_VIDEO      = 9\n\tTAG_SCRIPTDATA = 18\n)\n\nconst (\n\tSOUND_MP3                   = 2\n\tSOUND_NELLYMOSER_16KHZ_MONO = 4\n\tSOUND_NELLYMOSER_8KHZ_MONO  = 5\n\tSOUND_NELLYMOSER            = 6\n\tSOUND_ALAW                  = 7\n\tSOUND_MULAW                 = 8\n\tSOUND_AAC                   = 10\n\tSOUND_SPEEX                 = 11\n\n\tSOUND_5_5Khz = 0\n\tSOUND_11Khz  = 1\n\tSOUND_22Khz  = 2\n\tSOUND_44Khz  = 3\n\n\tSOUND_8BIT  = 0\n\tSOUND_16BIT = 1\n\n\tSOUND_MONO   = 0\n\tSOUND_STEREO = 1\n\n\tAAC_SEQHDR = 0\n\tAAC_RAW    = 1\n)\n\nconst (\n\tAVC_SEQHDR = 0\n\tAVC_NALU   = 1\n\tAVC_EOS    = 2\n\n\tFRAME_KEY   = 1\n\tFRAME_INTER = 2\n\n\tVIDEO_H264 = 7\n)\n\ntype Tag struct {\n\tType uint8\n\n\t/*\n\t\tSoundFormat: UB[4]\n\t\t0 = Linear PCM, platform endian\n\t\t1 = ADPCM\n\t\t2 = MP3\n\t\t3 = Linear PCM, little endian\n\t\t4 = Nellymoser 16-kHz mono\n\t\t5 = Nellymoser 8-kHz mono\n\t\t6 = Nellymoser\n\t\t7 = G.711 A-law logarithmic PCM\n\t\t8 = G.711 mu-law logarithmic PCM\n\t\t9 = reserved\n\t\t10 = AAC\n\t\t11 = Speex\n\t\t14 = MP3 8-Khz\n\t\t15 = Device-specific sound\n\t\tFormats 7, 8, 14, and 15 are reserved for internal use\n\t\tAAC is supported in Flash Player 9,0,115,0 and higher.\n\t\tSpeex is supported in Flash Player 10 and higher.\n\t*/\n\tSoundFormat uint8\n\n\t/*\n\t\tSoundRate: UB[2]\n\t\tSampling rate\n\t\t0 = 5.5-kHz For AAC: always 3\n\t\t1 = 11-kHz\n\t\t2 = 22-kHz\n\t\t3 = 44-kHz\n\t*/\n\tSoundRate uint8\n\n\t/*\n\t\tSoundSize: UB[1]\n\t\t0 = snd8Bit\n\t\t1 = snd16Bit\n\t\tSize of each sample.\n\t\tThis parameter only pertains to uncompressed formats.\n\t\tCompressed formats always decode to 16 bits internally\n\t*/\n\tSoundSize uint8\n\n\t/*\n\t\tSoundType: UB[1]\n\t\t0 = sndMono\n\t\t1 = sndStereo\n\t\tMono or stereo sound For Nellymoser: always 0\n\t\tFor AAC: always 1\n\t*/\n\tSoundType uint8\n\n\t/*\n\t\t0: AAC sequence header\n\t\t1: AAC raw\n\t*/\n\tAACPacketType uint8\n\n\t/*\n\t\t1: keyframe (for AVC, a seekable frame)\n\t\t2: inter frame (for AVC, a non- seekable frame)\n\t\t3: disposable inter frame (H.263 only)\n\t\t4: generated keyframe (reserved for server use only)\n\t\t5: video info/command frame\n\t*/\n\tFrameType uint8\n\n\t/*\n\t\t1: JPEG (currently unused)\n\t\t2: Sorenson H.263\n\t\t3: Screen video\n\t\t4: On2 VP6\n\t\t5: On2 VP6 with alpha channel\n\t\t6: Screen video version 2\n\t\t7: AVC\n\t*/\n\tCodecID uint8\n\n\t/*\n\t\t0: AVC sequence header\n\t\t1: AVC NALU\n\t\t2: AVC end of sequence (lower level NALU sequence ender is not required or supported)\n\t*/\n\tAVCPacketType uint8\n\n\tCompositionTime int32\n\n\tData []byte\n}\n\nfunc (self Tag) ChannelLayout() av.ChannelLayout {\n\tif self.SoundType == SOUND_MONO {\n\t\treturn av.CH_MONO\n\t} else {\n\t\treturn av.CH_STEREO\n\t}\n}\n\nfunc (self *Tag) audioParseHeader(b []byte) (n int, err error) {\n\tif len(b) < n+1 {\n\t\terr = fmt.Errorf(\"audiodata: parse invalid\")\n\t\treturn\n\t}\n\n\tflags := b[n]\n\tn++\n\tself.SoundFormat = flags >> 4\n\tself.SoundRate = (flags >> 2) & 0x3\n\tself.SoundSize = (flags >> 1) & 0x1\n\tself.SoundType = flags & 0x1\n\n\tswitch self.SoundFormat {\n\tcase SOUND_AAC:\n\t\tif len(b) < n+1 {\n\t\t\terr = fmt.Errorf(\"audiodata: parse invalid\")\n\t\t\treturn\n\t\t}\n\t\tself.AACPacketType = b[n]\n\t\tn++\n\t}\n\n\treturn\n}\n\nfunc (self Tag) audioFillHeader(b []byte) (n int) {\n\tvar flags uint8\n\tflags |= self.SoundFormat << 4\n\tflags |= self.SoundRate << 2\n\tflags |= self.SoundSize << 1\n\tflags |= self.SoundType\n\tb[n] = flags\n\tn++\n\n\tswitch self.SoundFormat {\n\tcase SOUND_AAC:\n\t\tb[n] = self.AACPacketType\n\t\tn++\n\t}\n\n\treturn\n}\n\nfunc (self *Tag) videoParseHeader(b []byte) (n int, err error) {\n\tif len(b) < n+1 {\n\t\terr = fmt.Errorf(\"videodata: parse invalid\")\n\t\treturn\n\t}\n\tflags := b[n]\n\tself.FrameType = flags >> 4\n\tself.CodecID = flags & 0xf\n\tn++\n\n\tif self.FrameType == FRAME_INTER || self.FrameType == FRAME_KEY {\n\t\tif len(b) < n+4 {\n\t\t\terr = fmt.Errorf(\"videodata: parse invalid\")\n\t\t\treturn\n\t\t}\n\t\tself.AVCPacketType = b[n]\n\t\tn++\n\n\t\tself.CompositionTime = pio.I24BE(b[n:])\n\t\tn += 3\n\t}\n\n\treturn\n}\n\nfunc (self Tag) videoFillHeader(b []byte) (n int) {\n\tflags := self.FrameType<<4 | self.CodecID\n\tb[n] = flags\n\tn++\n\tb[n] = self.AVCPacketType\n\tn++\n\tpio.PutI24BE(b[n:], self.CompositionTime)\n\tn += 3\n\treturn\n}\n\nfunc (self Tag) FillHeader(b []byte) (n int) {\n\tswitch self.Type {\n\tcase TAG_AUDIO:\n\t\treturn self.audioFillHeader(b)\n\n\tcase TAG_VIDEO:\n\t\treturn self.videoFillHeader(b)\n\t}\n\n\treturn\n}\n\nfunc (self *Tag) ParseHeader(b []byte) (n int, err error) {\n\tswitch self.Type {\n\tcase TAG_AUDIO:\n\t\treturn self.audioParseHeader(b)\n\n\tcase TAG_VIDEO:\n\t\treturn self.videoParseHeader(b)\n\t}\n\n\treturn\n}\n\nconst (\n\t// TypeFlagsReserved UB[5]\n\t// TypeFlagsAudio    UB[1] Audio tags are present\n\t// TypeFlagsReserved UB[1] Must be 0\n\t// TypeFlagsVideo    UB[1] Video tags are present\n\tFILE_HAS_AUDIO = 0x4\n\tFILE_HAS_VIDEO = 0x1\n)\n\nconst TagHeaderLength = 11\nconst TagTrailerLength = 4\n\nfunc ParseTagHeader(b []byte) (tag Tag, ts int32, datalen int, err error) {\n\ttagtype := b[0]\n\n\tswitch tagtype {\n\tcase TAG_AUDIO, TAG_VIDEO, TAG_SCRIPTDATA:\n\t\ttag = Tag{Type: tagtype}\n\n\tdefault:\n\t\terr = fmt.Errorf(\"flvio: ReadTag tagtype=%d invalid\", tagtype)\n\t\treturn\n\t}\n\n\tdatalen = int(pio.U24BE(b[1:4]))\n\n\tvar tslo uint32\n\tvar tshi uint8\n\ttslo = pio.U24BE(b[4:7])\n\ttshi = b[7]\n\tts = int32(tslo | uint32(tshi)<<24)\n\n\treturn\n}\n\nfunc ReadTag(r io.Reader, b []byte) (tag Tag, ts int32, err error) {\n\tif _, err = io.ReadFull(r, b[:TagHeaderLength]); err != nil {\n\t\treturn\n\t}\n\tvar datalen int\n\tif tag, ts, datalen, err = ParseTagHeader(b); err != nil {\n\t\treturn\n\t}\n\n\tdata := make([]byte, datalen)\n\tif _, err = io.ReadFull(r, data); err != nil {\n\t\treturn\n\t}\n\n\tvar n int\n\tif n, err = (&tag).ParseHeader(data); err != nil {\n\t\treturn\n\t}\n\ttag.Data = data[n:]\n\n\tif _, err = io.ReadFull(r, b[:4]); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc FillTagHeader(b []byte, tagtype uint8, datalen int, ts int32) (n int) {\n\tb[n] = tagtype\n\tn++\n\tpio.PutU24BE(b[n:], uint32(datalen))\n\tn += 3\n\tpio.PutU24BE(b[n:], uint32(ts&0xffffff))\n\tn += 3\n\tb[n] = uint8(ts >> 24)\n\tn++\n\tpio.PutI24BE(b[n:], 0)\n\tn += 3\n\treturn\n}\n\nfunc FillTagTrailer(b []byte, datalen int) (n int) {\n\tpio.PutU32BE(b[n:], uint32(datalen+TagHeaderLength))\n\tn += 4\n\treturn\n}\n\nfunc WriteTag(w io.Writer, tag Tag, ts int32, b []byte) (err error) {\n\tdata := tag.Data\n\n\tn := tag.FillHeader(b[TagHeaderLength:])\n\tdatalen := len(data) + n\n\n\tn += FillTagHeader(b, tag.Type, datalen, ts)\n\n\tif _, err = w.Write(b[:n]); err != nil {\n\t\treturn\n\t}\n\n\tif _, err = w.Write(data); err != nil {\n\t\treturn\n\t}\n\n\tn = FillTagTrailer(b, datalen)\n\tif _, err = w.Write(b[:n]); err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n\nconst FileHeaderLength = 9\n\nfunc FillFileHeader(b []byte, flags uint8) (n int) {\n\t// 'FLV', version 1\n\tpio.PutU32BE(b[n:], 0x464c5601)\n\tn += 4\n\n\tb[n] = flags\n\tn++\n\n\t// DataOffset: UI32 Offset in bytes from start of file to start of body (that is, size of header)\n\t// The DataOffset field usually has a value of 9 for FLV version 1.\n\tpio.PutU32BE(b[n:], 9)\n\tn += 4\n\n\t// PreviousTagSize0: UI32 Always 0\n\tpio.PutU32BE(b[n:], 0)\n\tn += 4\n\n\treturn\n}\n\nfunc ParseFileHeader(b []byte) (flags uint8, skip int, err error) {\n\tflv := pio.U24BE(b[0:3])\n\tif flv != 0x464c56 { // 'FLV'\n\t\terr = fmt.Errorf(\"flvio: file header cc3 invalid\")\n\t\treturn\n\t}\n\n\tflags = b[4]\n\n\tskip = int(pio.U32BE(b[5:9])) - 9 + 4\n\tif skip < 0 {\n\t\terr = fmt.Errorf(\"flvio: file header datasize invalid\")\n\t\treturn\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "format/format.go",
    "content": "package format\n\nimport (\n\t\"github.com/nareix/joy4/format/mp4\"\n\t\"github.com/nareix/joy4/format/ts\"\n\t\"github.com/nareix/joy4/format/rtmp\"\n\t\"github.com/nareix/joy4/format/rtsp\"\n\t\"github.com/nareix/joy4/format/flv\"\n\t\"github.com/nareix/joy4/format/aac\"\n\t\"github.com/nareix/joy4/av/avutil\"\n)\n\nfunc RegisterAll() {\n\tavutil.DefaultHandlers.Add(mp4.Handler)\n\tavutil.DefaultHandlers.Add(ts.Handler)\n\tavutil.DefaultHandlers.Add(rtmp.Handler)\n\tavutil.DefaultHandlers.Add(rtsp.Handler)\n\tavutil.DefaultHandlers.Add(flv.Handler)\n\tavutil.DefaultHandlers.Add(aac.Handler)\n}\n\n"
  },
  {
    "path": "format/mp4/demuxer.go",
    "content": "package mp4\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/codec/aacparser\"\n\t\"github.com/nareix/joy4/codec/h264parser\"\n\t\"github.com/nareix/joy4/format/mp4/mp4io\"\n)\n\ntype Demuxer struct {\n\tr         io.ReadSeeker\n\tstreams   []*Stream\n\tmovieAtom *mp4io.Movie\n}\n\nfunc NewDemuxer(r io.ReadSeeker) *Demuxer {\n\treturn &Demuxer{\n\t\tr: r,\n\t}\n}\n\nfunc (self *Demuxer) Streams() (streams []av.CodecData, err error) {\n\tif err = self.probe(); err != nil {\n\t\treturn\n\t}\n\tfor _, stream := range self.streams {\n\t\tstreams = append(streams, stream.CodecData)\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) readat(pos int64, b []byte) (err error) {\n\tif _, err = self.r.Seek(pos, 0); err != nil {\n\t\treturn\n\t}\n\tif _, err = io.ReadFull(self.r, b); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) probe() (err error) {\n\tif self.movieAtom != nil {\n\t\treturn\n\t}\n\n\tvar moov *mp4io.Movie\n\tvar atoms []mp4io.Atom\n\n\tif atoms, err = mp4io.ReadFileAtoms(self.r); err != nil {\n\t\treturn\n\t}\n\tif _, err = self.r.Seek(0, 0); err != nil {\n\t\treturn\n\t}\n\n\tfor _, atom := range atoms {\n\t\tif atom.Tag() == mp4io.MOOV {\n\t\t\tmoov = atom.(*mp4io.Movie)\n\t\t}\n\t}\n\n\tif moov == nil {\n\t\terr = fmt.Errorf(\"mp4: 'moov' atom not found\")\n\t\treturn\n\t}\n\n\tself.streams = []*Stream{}\n\tfor i, atrack := range moov.Tracks {\n\t\tstream := &Stream{\n\t\t\ttrackAtom: atrack,\n\t\t\tdemuxer:   self,\n\t\t\tidx:       i,\n\t\t}\n\t\tif atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil {\n\t\t\tstream.sample = atrack.Media.Info.Sample\n\t\t\tstream.timeScale = int64(atrack.Media.Header.TimeScale)\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"mp4: sample table not found\")\n\t\t\treturn\n\t\t}\n\n\t\tif avc1 := atrack.GetAVC1Conf(); avc1 != nil {\n\t\t\tif stream.CodecData, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(avc1.Data); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.streams = append(self.streams, stream)\n\t\t} else if esds := atrack.GetElemStreamDesc(); esds != nil {\n\t\t\tif stream.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(esds.DecConfig); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.streams = append(self.streams, stream)\n\t\t}\n\t}\n\n\tself.movieAtom = moov\n\treturn\n}\n\nfunc (self *Stream) setSampleIndex(index int) (err error) {\n\tfound := false\n\tstart := 0\n\tself.chunkGroupIndex = 0\n\n\tfor self.chunkIndex = range self.sample.ChunkOffset.Entries {\n\t\tif self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) &&\n\t\t\tuint32(self.chunkIndex+1) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk {\n\t\t\tself.chunkGroupIndex++\n\t\t}\n\t\tn := int(self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk)\n\t\tif index >= start && index < start+n {\n\t\t\tfound = true\n\t\t\tself.sampleIndexInChunk = index - start\n\t\t\tbreak\n\t\t}\n\t\tstart += n\n\t}\n\tif !found {\n\t\terr = fmt.Errorf(\"mp4: stream[%d]: cannot locate sample index in chunk\", self.idx)\n\t\treturn\n\t}\n\n\tif self.sample.SampleSize.SampleSize != 0 {\n\t\tself.sampleOffsetInChunk = int64(self.sampleIndexInChunk) * int64(self.sample.SampleSize.SampleSize)\n\t} else {\n\t\tif index >= len(self.sample.SampleSize.Entries) {\n\t\t\terr = fmt.Errorf(\"mp4: stream[%d]: sample index out of range\", self.idx)\n\t\t\treturn\n\t\t}\n\t\tself.sampleOffsetInChunk = int64(0)\n\t\tfor i := index - self.sampleIndexInChunk; i < index; i++ {\n\t\t\tself.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[i])\n\t\t}\n\t}\n\n\tself.dts = int64(0)\n\tstart = 0\n\tfound = false\n\tself.sttsEntryIndex = 0\n\tfor self.sttsEntryIndex < len(self.sample.TimeToSample.Entries) {\n\t\tentry := self.sample.TimeToSample.Entries[self.sttsEntryIndex]\n\t\tn := int(entry.Count)\n\t\tif index >= start && index < start+n {\n\t\t\tself.sampleIndexInSttsEntry = index - start\n\t\t\tself.dts += int64(index-start) * int64(entry.Duration)\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t\tstart += n\n\t\tself.dts += int64(n) * int64(entry.Duration)\n\t\tself.sttsEntryIndex++\n\t}\n\tif !found {\n\t\terr = fmt.Errorf(\"mp4: stream[%d]: cannot locate sample index in stts entry\", self.idx)\n\t\treturn\n\t}\n\n\tif self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {\n\t\tstart = 0\n\t\tfound = false\n\t\tself.cttsEntryIndex = 0\n\t\tfor self.cttsEntryIndex < len(self.sample.CompositionOffset.Entries) {\n\t\t\tn := int(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count)\n\t\t\tif index >= start && index < start+n {\n\t\t\t\tself.sampleIndexInCttsEntry = index - start\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tstart += n\n\t\t\tself.cttsEntryIndex++\n\t\t}\n\t\tif !found {\n\t\t\terr = fmt.Errorf(\"mp4: stream[%d]: cannot locate sample index in ctts entry\", self.idx)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif self.sample.SyncSample != nil {\n\t\tself.syncSampleIndex = 0\n\t\tfor self.syncSampleIndex < len(self.sample.SyncSample.Entries)-1 {\n\t\t\tif self.sample.SyncSample.Entries[self.syncSampleIndex+1]-1 > uint32(index) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tself.syncSampleIndex++\n\t\t}\n\t}\n\n\tif false {\n\t\tfmt.Printf(\"mp4: stream[%d]: setSampleIndex chunkGroupIndex=%d chunkIndex=%d sampleOffsetInChunk=%d\\n\",\n\t\t\tself.idx, self.chunkGroupIndex, self.chunkIndex, self.sampleOffsetInChunk)\n\t}\n\n\tself.sampleIndex = index\n\treturn\n}\n\nfunc (self *Stream) isSampleValid() bool {\n\tif self.chunkIndex >= len(self.sample.ChunkOffset.Entries) {\n\t\treturn false\n\t}\n\tif self.chunkGroupIndex >= len(self.sample.SampleToChunk.Entries) {\n\t\treturn false\n\t}\n\tif self.sttsEntryIndex >= len(self.sample.TimeToSample.Entries) {\n\t\treturn false\n\t}\n\tif self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {\n\t\tif self.cttsEntryIndex >= len(self.sample.CompositionOffset.Entries) {\n\t\t\treturn false\n\t\t}\n\t}\n\tif self.sample.SyncSample != nil {\n\t\tif self.syncSampleIndex >= len(self.sample.SyncSample.Entries) {\n\t\t\treturn false\n\t\t}\n\t}\n\tif self.sample.SampleSize.SampleSize != 0 {\n\t\tif self.sampleIndex >= len(self.sample.SampleSize.Entries) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (self *Stream) incSampleIndex() (duration int64) {\n\tif false {\n\t\tfmt.Printf(\"incSampleIndex sampleIndex=%d sampleOffsetInChunk=%d sampleIndexInChunk=%d chunkGroupIndex=%d chunkIndex=%d\\n\",\n\t\t\tself.sampleIndex, self.sampleOffsetInChunk, self.sampleIndexInChunk, self.chunkGroupIndex, self.chunkIndex)\n\t}\n\n\tself.sampleIndexInChunk++\n\tif uint32(self.sampleIndexInChunk) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk {\n\t\tself.chunkIndex++\n\t\tself.sampleIndexInChunk = 0\n\t\tself.sampleOffsetInChunk = int64(0)\n\t} else {\n\t\tif self.sample.SampleSize.SampleSize != 0 {\n\t\t\tself.sampleOffsetInChunk += int64(self.sample.SampleSize.SampleSize)\n\t\t} else {\n\t\t\tself.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[self.sampleIndex])\n\t\t}\n\t}\n\n\tif self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) &&\n\t\tuint32(self.chunkIndex+1) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk {\n\t\tself.chunkGroupIndex++\n\t}\n\n\tsttsEntry := self.sample.TimeToSample.Entries[self.sttsEntryIndex]\n\tduration = int64(sttsEntry.Duration)\n\tself.sampleIndexInSttsEntry++\n\tself.dts += duration\n\tif uint32(self.sampleIndexInSttsEntry) == sttsEntry.Count {\n\t\tself.sampleIndexInSttsEntry = 0\n\t\tself.sttsEntryIndex++\n\t}\n\n\tif self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {\n\t\tself.sampleIndexInCttsEntry++\n\t\tif uint32(self.sampleIndexInCttsEntry) == self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count {\n\t\t\tself.sampleIndexInCttsEntry = 0\n\t\t\tself.cttsEntryIndex++\n\t\t}\n\t}\n\n\tif self.sample.SyncSample != nil {\n\t\tentries := self.sample.SyncSample.Entries\n\t\tif self.syncSampleIndex+1 < len(entries) && entries[self.syncSampleIndex+1]-1 == uint32(self.sampleIndex+1) {\n\t\t\tself.syncSampleIndex++\n\t\t}\n\t}\n\n\tself.sampleIndex++\n\treturn\n}\n\nfunc (self *Stream) sampleCount() int {\n\tif self.sample.SampleSize.SampleSize == 0 {\n\t\tchunkGroupIndex := 0\n\t\tcount := 0\n\t\tfor chunkIndex := range self.sample.ChunkOffset.Entries {\n\t\t\tn := int(self.sample.SampleToChunk.Entries[chunkGroupIndex].SamplesPerChunk)\n\t\t\tcount += n\n\t\t\tif chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) &&\n\t\t\t\tuint32(chunkIndex+1) == self.sample.SampleToChunk.Entries[chunkGroupIndex+1].FirstChunk {\n\t\t\t\tchunkGroupIndex++\n\t\t\t}\n\t\t}\n\t\treturn count\n\t} else {\n\t\treturn len(self.sample.SampleSize.Entries)\n\t}\n}\n\nfunc (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {\n\tif err = self.probe(); err != nil {\n\t\treturn\n\t}\n\tif len(self.streams) == 0 {\n\t\terr = errors.New(\"mp4: no streams available while trying to read a packet\")\n\t\treturn\n\t}\n\n\tvar chosen *Stream\n\tvar chosenidx int\n\tfor i, stream := range self.streams {\n\t\tif chosen == nil || stream.tsToTime(stream.dts) < chosen.tsToTime(chosen.dts) {\n\t\t\tchosen = stream\n\t\t\tchosenidx = i\n\t\t}\n\t}\n\tif false {\n\t\tfmt.Printf(\"ReadPacket: chosen index=%v time=%v\\n\", chosen.idx, chosen.tsToTime(chosen.dts))\n\t}\n\ttm := chosen.tsToTime(chosen.dts)\n\tif pkt, err = chosen.readPacket(); err != nil {\n\t\treturn\n\t}\n\tpkt.Time = tm\n\tpkt.Idx = int8(chosenidx)\n\treturn\n}\n\nfunc (self *Demuxer) CurrentTime() (tm time.Duration) {\n\tif len(self.streams) > 0 {\n\t\tstream := self.streams[0]\n\t\ttm = stream.tsToTime(stream.dts)\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) SeekToTime(tm time.Duration) (err error) {\n\tfor _, stream := range self.streams {\n\t\tif stream.Type().IsVideo() {\n\t\t\tif err = stream.seekToTime(tm); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttm = stream.tsToTime(stream.dts)\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor _, stream := range self.streams {\n\t\tif !stream.Type().IsVideo() {\n\t\t\tif err = stream.seekToTime(tm); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Stream) readPacket() (pkt av.Packet, err error) {\n\tif !self.isSampleValid() {\n\t\terr = io.EOF\n\t\treturn\n\t}\n\t//fmt.Println(\"readPacket\", self.sampleIndex)\n\n\tchunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex]\n\tsampleSize := uint32(0)\n\tif self.sample.SampleSize.SampleSize != 0 {\n\t\tsampleSize = self.sample.SampleSize.SampleSize\n\t} else {\n\t\tsampleSize = self.sample.SampleSize.Entries[self.sampleIndex]\n\t}\n\n\tsampleOffset := int64(chunkOffset) + self.sampleOffsetInChunk\n\tpkt.Data = make([]byte, sampleSize)\n\tif err = self.demuxer.readat(sampleOffset, pkt.Data); err != nil {\n\t\treturn\n\t}\n\n\tif self.sample.SyncSample != nil {\n\t\tif self.sample.SyncSample.Entries[self.syncSampleIndex]-1 == uint32(self.sampleIndex) {\n\t\t\tpkt.IsKeyFrame = true\n\t\t}\n\t}\n\n\t//println(\"pts/dts\", self.ptsEntryIndex, self.dtsEntryIndex)\n\tif self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {\n\t\tcts := int64(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Offset)\n\t\tpkt.CompositionTime = self.tsToTime(cts)\n\t}\n\n\tself.incSampleIndex()\n\n\treturn\n}\n\nfunc (self *Stream) seekToTime(tm time.Duration) (err error) {\n\tindex := self.timeToSampleIndex(tm)\n\tif err = self.setSampleIndex(index); err != nil {\n\t\treturn\n\t}\n\tif false {\n\t\tfmt.Printf(\"stream[%d]: seekToTime index=%v time=%v cur=%v\\n\", self.idx, index, tm, self.tsToTime(self.dts))\n\t}\n\treturn\n}\n\nfunc (self *Stream) timeToSampleIndex(tm time.Duration) int {\n\ttargetTs := self.timeToTs(tm)\n\ttargetIndex := 0\n\n\tstartTs := int64(0)\n\tendTs := int64(0)\n\tstartIndex := 0\n\tendIndex := 0\n\tfound := false\n\tfor _, entry := range self.sample.TimeToSample.Entries {\n\t\tendTs = startTs + int64(entry.Count*entry.Duration)\n\t\tendIndex = startIndex + int(entry.Count)\n\t\tif targetTs >= startTs && targetTs < endTs {\n\t\t\ttargetIndex = startIndex + int((targetTs-startTs)/int64(entry.Duration))\n\t\t\tfound = true\n\t\t}\n\t\tstartTs = endTs\n\t\tstartIndex = endIndex\n\t}\n\tif !found {\n\t\tif targetTs < 0 {\n\t\t\ttargetIndex = 0\n\t\t} else {\n\t\t\ttargetIndex = endIndex - 1\n\t\t}\n\t}\n\n\tif self.sample.SyncSample != nil {\n\t\tentries := self.sample.SyncSample.Entries\n\t\tfor i := len(entries) - 1; i >= 0; i-- {\n\t\t\tif entries[i]-1 < uint32(targetIndex) {\n\t\t\t\ttargetIndex = int(entries[i] - 1)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn targetIndex\n}\n"
  },
  {
    "path": "format/mp4/handler.go",
    "content": "package mp4\n\nimport (\n\t\"io\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/avutil\"\n)\n\nvar CodecTypes = []av.CodecType{av.H264, av.AAC}\n\nfunc Handler(h *avutil.RegisterHandler) {\n\th.Ext = \".mp4\"\n\n\th.Probe = func(b []byte) bool {\n\t\tswitch string(b[4:8]) {\n\t\tcase \"moov\",\"ftyp\",\"free\",\"mdat\",\"moof\":\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\th.ReaderDemuxer = func(r io.Reader) av.Demuxer {\n\t\treturn NewDemuxer(r.(io.ReadSeeker))\n\t}\n\n\th.WriterMuxer = func(w io.Writer) av.Muxer {\n\t\treturn NewMuxer(w.(io.WriteSeeker))\n\t}\n\n\th.CodecTypes = CodecTypes\n}\n\n"
  },
  {
    "path": "format/mp4/mp4io/atoms.go",
    "content": "package mp4io\n\nimport \"github.com/nareix/joy4/utils/bits/pio\"\nimport \"time\"\n\nconst MOOF = Tag(0x6d6f6f66)\n\nfunc (self MovieFrag) Tag() Tag {\n\treturn MOOF\n}\n\nconst HDLR = Tag(0x68646c72)\n\nfunc (self HandlerRefer) Tag() Tag {\n\treturn HDLR\n}\n\nconst AVC1 = Tag(0x61766331)\n\nfunc (self AVC1Desc) Tag() Tag {\n\treturn AVC1\n}\n\nconst URL  = Tag(0x75726c20)\n\nfunc (self DataReferUrl) Tag() Tag {\n\treturn URL\n}\n\nconst TREX = Tag(0x74726578)\n\nfunc (self TrackExtend) Tag() Tag {\n\treturn TREX\n}\n\nconst ESDS = Tag(0x65736473)\n\nfunc (self ElemStreamDesc) Tag() Tag {\n\treturn ESDS\n}\n\nconst MDHD = Tag(0x6d646864)\n\nfunc (self MediaHeader) Tag() Tag {\n\treturn MDHD\n}\n\nconst STTS = Tag(0x73747473)\n\nfunc (self TimeToSample) Tag() Tag {\n\treturn STTS\n}\n\nconst STSS = Tag(0x73747373)\n\nfunc (self SyncSample) Tag() Tag {\n\treturn STSS\n}\n\nconst MFHD = Tag(0x6d666864)\n\nfunc (self MovieFragHeader) Tag() Tag {\n\treturn MFHD\n}\n\nconst MVHD = Tag(0x6d766864)\n\nfunc (self MovieHeader) Tag() Tag {\n\treturn MVHD\n}\n\nconst MINF = Tag(0x6d696e66)\n\nfunc (self MediaInfo) Tag() Tag {\n\treturn MINF\n}\n\nconst MOOV = Tag(0x6d6f6f76)\n\nfunc (self Movie) Tag() Tag {\n\treturn MOOV\n}\n\nconst MVEX = Tag(0x6d766578)\n\nfunc (self MovieExtend) Tag() Tag {\n\treturn MVEX\n}\n\nconst STSD = Tag(0x73747364)\n\nfunc (self SampleDesc) Tag() Tag {\n\treturn STSD\n}\n\nconst MP4A = Tag(0x6d703461)\n\nfunc (self MP4ADesc) Tag() Tag {\n\treturn MP4A\n}\n\nconst CTTS = Tag(0x63747473)\n\nfunc (self CompositionOffset) Tag() Tag {\n\treturn CTTS\n}\n\nconst STCO = Tag(0x7374636f)\n\nfunc (self ChunkOffset) Tag() Tag {\n\treturn STCO\n}\n\nconst TRUN = Tag(0x7472756e)\n\nfunc (self TrackFragRun) Tag() Tag {\n\treturn TRUN\n}\n\nconst TRAK = Tag(0x7472616b)\n\nfunc (self Track) Tag() Tag {\n\treturn TRAK\n}\n\nconst MDIA = Tag(0x6d646961)\n\nfunc (self Media) Tag() Tag {\n\treturn MDIA\n}\n\nconst STSC = Tag(0x73747363)\n\nfunc (self SampleToChunk) Tag() Tag {\n\treturn STSC\n}\n\nconst VMHD = Tag(0x766d6864)\n\nfunc (self VideoMediaInfo) Tag() Tag {\n\treturn VMHD\n}\n\nconst STBL = Tag(0x7374626c)\n\nfunc (self SampleTable) Tag() Tag {\n\treturn STBL\n}\n\nconst AVCC = Tag(0x61766343)\n\nfunc (self AVC1Conf) Tag() Tag {\n\treturn AVCC\n}\n\nconst TFDT = Tag(0x74666474)\n\nfunc (self TrackFragDecodeTime) Tag() Tag {\n\treturn TFDT\n}\n\nconst DINF = Tag(0x64696e66)\n\nfunc (self DataInfo) Tag() Tag {\n\treturn DINF\n}\n\nconst DREF = Tag(0x64726566)\n\nfunc (self DataRefer) Tag() Tag {\n\treturn DREF\n}\n\nconst TRAF = Tag(0x74726166)\n\nfunc (self TrackFrag) Tag() Tag {\n\treturn TRAF\n}\n\nconst STSZ = Tag(0x7374737a)\n\nfunc (self SampleSize) Tag() Tag {\n\treturn STSZ\n}\n\nconst TFHD = Tag(0x74666864)\n\nfunc (self TrackFragHeader) Tag() Tag {\n\treturn TFHD\n}\n\nconst TKHD = Tag(0x746b6864)\n\nfunc (self TrackHeader) Tag() Tag {\n\treturn TKHD\n}\n\nconst SMHD = Tag(0x736d6864)\n\nfunc (self SoundMediaInfo) Tag() Tag {\n\treturn SMHD\n}\n\nconst MDAT = Tag(0x6d646174)\n\ntype Movie struct {\n\tHeader\t\t*MovieHeader\n\tMovieExtend\t*MovieExtend\n\tTracks\t\t[]*Track\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self Movie) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MOOV))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self Movie) marshal(b []byte) (n int) {\n\tif self.Header != nil {\n\t\tn += self.Header.Marshal(b[n:])\n\t}\n\tif self.MovieExtend != nil {\n\t\tn += self.MovieExtend.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Tracks {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self Movie) Len() (n int) {\n\tn += 8\n\tif self.Header != nil {\n\t\tn += self.Header.Len()\n\t}\n\tif self.MovieExtend != nil {\n\t\tn += self.MovieExtend.Len()\n\t}\n\tfor _, atom := range self.Tracks {\n\t\tn += atom.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *Movie) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase MVHD:\n\t\t\t{\n\t\t\t\tatom := &MovieHeader{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"mvhd\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Header = atom\n\t\t\t}\n\t\tcase MVEX:\n\t\t\t{\n\t\t\t\tatom := &MovieExtend{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"mvex\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.MovieExtend = atom\n\t\t\t}\n\t\tcase TRAK:\n\t\t\t{\n\t\t\t\tatom := &Track{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"trak\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Tracks = append(self.Tracks, atom)\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self Movie) Children() (r []Atom) {\n\tif self.Header != nil {\n\t\tr = append(r, self.Header)\n\t}\n\tif self.MovieExtend != nil {\n\t\tr = append(r, self.MovieExtend)\n\t}\n\tfor _, atom := range self.Tracks {\n\t\tr = append(r, atom)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype MovieHeader struct {\n\tVersion\t\t\tuint8\n\tFlags\t\t\tuint32\n\tCreateTime\t\ttime.Time\n\tModifyTime\t\ttime.Time\n\tTimeScale\t\tint32\n\tDuration\t\tint32\n\tPreferredRate\t\tfloat64\n\tPreferredVolume\t\tfloat64\n\tMatrix\t\t\t[9]int32\n\tPreviewTime\t\ttime.Time\n\tPreviewDuration\t\ttime.Time\n\tPosterTime\t\ttime.Time\n\tSelectionTime\t\ttime.Time\n\tSelectionDuration\ttime.Time\n\tCurrentTime\t\ttime.Time\n\tNextTrackId\t\tint32\n\tAtomPos\n}\n\nfunc (self MovieHeader) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MVHD))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self MovieHeader) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tPutTime32(b[n:], self.CreateTime)\n\tn += 4\n\tPutTime32(b[n:], self.ModifyTime)\n\tn += 4\n\tpio.PutI32BE(b[n:], self.TimeScale)\n\tn += 4\n\tpio.PutI32BE(b[n:], self.Duration)\n\tn += 4\n\tPutFixed32(b[n:], self.PreferredRate)\n\tn += 4\n\tPutFixed16(b[n:], self.PreferredVolume)\n\tn += 2\n\tn += 10\n\tfor _, entry := range self.Matrix {\n\t\tpio.PutI32BE(b[n:], entry)\n\t\tn += 4\n\t}\n\tPutTime32(b[n:], self.PreviewTime)\n\tn += 4\n\tPutTime32(b[n:], self.PreviewDuration)\n\tn += 4\n\tPutTime32(b[n:], self.PosterTime)\n\tn += 4\n\tPutTime32(b[n:], self.SelectionTime)\n\tn += 4\n\tPutTime32(b[n:], self.SelectionDuration)\n\tn += 4\n\tPutTime32(b[n:], self.CurrentTime)\n\tn += 4\n\tpio.PutI32BE(b[n:], self.NextTrackId)\n\tn += 4\n\treturn\n}\nfunc (self MovieHeader) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 2\n\tn += 10\n\tn += 4*len(self.Matrix[:])\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\treturn\n}\nfunc (self *MovieHeader) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"CreateTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.CreateTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"ModifyTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.ModifyTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"TimeScale\", n+offset, err)\n\t\treturn\n\t}\n\tself.TimeScale = pio.I32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"Duration\", n+offset, err)\n\t\treturn\n\t}\n\tself.Duration = pio.I32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"PreferredRate\", n+offset, err)\n\t\treturn\n\t}\n\tself.PreferredRate = GetFixed32(b[n:])\n\tn += 4\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"PreferredVolume\", n+offset, err)\n\t\treturn\n\t}\n\tself.PreferredVolume = GetFixed16(b[n:])\n\tn += 2\n\tn += 10\n\tif len(b) < n+4*len(self.Matrix) {\n\t\terr = parseErr(\"Matrix\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Matrix {\n\t\tself.Matrix[i] = pio.I32BE(b[n:])\n\t\tn += 4\n\t}\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"PreviewTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.PreviewTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"PreviewDuration\", n+offset, err)\n\t\treturn\n\t}\n\tself.PreviewDuration = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"PosterTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.PosterTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"SelectionTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.SelectionTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"SelectionDuration\", n+offset, err)\n\t\treturn\n\t}\n\tself.SelectionDuration = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"CurrentTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.CurrentTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"NextTrackId\", n+offset, err)\n\t\treturn\n\t}\n\tself.NextTrackId = pio.I32BE(b[n:])\n\tn += 4\n\treturn\n}\nfunc (self MovieHeader) Children() (r []Atom) {\n\treturn\n}\n\ntype Track struct {\n\tHeader\t\t*TrackHeader\n\tMedia\t\t*Media\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self Track) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(TRAK))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self Track) marshal(b []byte) (n int) {\n\tif self.Header != nil {\n\t\tn += self.Header.Marshal(b[n:])\n\t}\n\tif self.Media != nil {\n\t\tn += self.Media.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self Track) Len() (n int) {\n\tn += 8\n\tif self.Header != nil {\n\t\tn += self.Header.Len()\n\t}\n\tif self.Media != nil {\n\t\tn += self.Media.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *Track) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase TKHD:\n\t\t\t{\n\t\t\t\tatom := &TrackHeader{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"tkhd\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Header = atom\n\t\t\t}\n\t\tcase MDIA:\n\t\t\t{\n\t\t\t\tatom := &Media{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"mdia\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Media = atom\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self Track) Children() (r []Atom) {\n\tif self.Header != nil {\n\t\tr = append(r, self.Header)\n\t}\n\tif self.Media != nil {\n\t\tr = append(r, self.Media)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype TrackHeader struct {\n\tVersion\t\tuint8\n\tFlags\t\tuint32\n\tCreateTime\ttime.Time\n\tModifyTime\ttime.Time\n\tTrackId\t\tint32\n\tDuration\tint32\n\tLayer\t\tint16\n\tAlternateGroup\tint16\n\tVolume\t\tfloat64\n\tMatrix\t\t[9]int32\n\tTrackWidth\tfloat64\n\tTrackHeight\tfloat64\n\tAtomPos\n}\n\nfunc (self TrackHeader) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(TKHD))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self TrackHeader) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tPutTime32(b[n:], self.CreateTime)\n\tn += 4\n\tPutTime32(b[n:], self.ModifyTime)\n\tn += 4\n\tpio.PutI32BE(b[n:], self.TrackId)\n\tn += 4\n\tn += 4\n\tpio.PutI32BE(b[n:], self.Duration)\n\tn += 4\n\tn += 8\n\tpio.PutI16BE(b[n:], self.Layer)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.AlternateGroup)\n\tn += 2\n\tPutFixed16(b[n:], self.Volume)\n\tn += 2\n\tn += 2\n\tfor _, entry := range self.Matrix {\n\t\tpio.PutI32BE(b[n:], entry)\n\t\tn += 4\n\t}\n\tPutFixed32(b[n:], self.TrackWidth)\n\tn += 4\n\tPutFixed32(b[n:], self.TrackHeight)\n\tn += 4\n\treturn\n}\nfunc (self TrackHeader) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 8\n\tn += 2\n\tn += 2\n\tn += 2\n\tn += 2\n\tn += 4*len(self.Matrix[:])\n\tn += 4\n\tn += 4\n\treturn\n}\nfunc (self *TrackHeader) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"CreateTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.CreateTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"ModifyTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.ModifyTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"TrackId\", n+offset, err)\n\t\treturn\n\t}\n\tself.TrackId = pio.I32BE(b[n:])\n\tn += 4\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"Duration\", n+offset, err)\n\t\treturn\n\t}\n\tself.Duration = pio.I32BE(b[n:])\n\tn += 4\n\tn += 8\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Layer\", n+offset, err)\n\t\treturn\n\t}\n\tself.Layer = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"AlternateGroup\", n+offset, err)\n\t\treturn\n\t}\n\tself.AlternateGroup = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Volume\", n+offset, err)\n\t\treturn\n\t}\n\tself.Volume = GetFixed16(b[n:])\n\tn += 2\n\tn += 2\n\tif len(b) < n+4*len(self.Matrix) {\n\t\terr = parseErr(\"Matrix\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Matrix {\n\t\tself.Matrix[i] = pio.I32BE(b[n:])\n\t\tn += 4\n\t}\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"TrackWidth\", n+offset, err)\n\t\treturn\n\t}\n\tself.TrackWidth = GetFixed32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"TrackHeight\", n+offset, err)\n\t\treturn\n\t}\n\tself.TrackHeight = GetFixed32(b[n:])\n\tn += 4\n\treturn\n}\nfunc (self TrackHeader) Children() (r []Atom) {\n\treturn\n}\n\ntype HandlerRefer struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tType\t[4]byte\n\tSubType\t[4]byte\n\tName\t[]byte\n\tAtomPos\n}\n\nfunc (self HandlerRefer) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(HDLR))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self HandlerRefer) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tcopy(b[n:], self.Type[:])\n\tn += len(self.Type[:])\n\tcopy(b[n:], self.SubType[:])\n\tn += len(self.SubType[:])\n\tcopy(b[n:], self.Name[:])\n\tn += len(self.Name[:])\n\treturn\n}\nfunc (self HandlerRefer) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += len(self.Type[:])\n\tn += len(self.SubType[:])\n\tn += len(self.Name[:])\n\treturn\n}\nfunc (self *HandlerRefer) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+len(self.Type) {\n\t\terr = parseErr(\"Type\", n+offset, err)\n\t\treturn\n\t}\n\tcopy(self.Type[:], b[n:])\n\tn += len(self.Type)\n\tif len(b) < n+len(self.SubType) {\n\t\terr = parseErr(\"SubType\", n+offset, err)\n\t\treturn\n\t}\n\tcopy(self.SubType[:], b[n:])\n\tn += len(self.SubType)\n\tself.Name = b[n:]\n\tn += len(b[n:])\n\treturn\n}\nfunc (self HandlerRefer) Children() (r []Atom) {\n\treturn\n}\n\ntype Media struct {\n\tHeader\t\t*MediaHeader\n\tHandler\t\t*HandlerRefer\n\tInfo\t\t*MediaInfo\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self Media) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MDIA))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self Media) marshal(b []byte) (n int) {\n\tif self.Header != nil {\n\t\tn += self.Header.Marshal(b[n:])\n\t}\n\tif self.Handler != nil {\n\t\tn += self.Handler.Marshal(b[n:])\n\t}\n\tif self.Info != nil {\n\t\tn += self.Info.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self Media) Len() (n int) {\n\tn += 8\n\tif self.Header != nil {\n\t\tn += self.Header.Len()\n\t}\n\tif self.Handler != nil {\n\t\tn += self.Handler.Len()\n\t}\n\tif self.Info != nil {\n\t\tn += self.Info.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *Media) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase MDHD:\n\t\t\t{\n\t\t\t\tatom := &MediaHeader{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"mdhd\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Header = atom\n\t\t\t}\n\t\tcase HDLR:\n\t\t\t{\n\t\t\t\tatom := &HandlerRefer{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"hdlr\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Handler = atom\n\t\t\t}\n\t\tcase MINF:\n\t\t\t{\n\t\t\t\tatom := &MediaInfo{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"minf\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Info = atom\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self Media) Children() (r []Atom) {\n\tif self.Header != nil {\n\t\tr = append(r, self.Header)\n\t}\n\tif self.Handler != nil {\n\t\tr = append(r, self.Handler)\n\t}\n\tif self.Info != nil {\n\t\tr = append(r, self.Info)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype MediaHeader struct {\n\tVersion\t\tuint8\n\tFlags\t\tuint32\n\tCreateTime\ttime.Time\n\tModifyTime\ttime.Time\n\tTimeScale\tint32\n\tDuration\tint32\n\tLanguage\tint16\n\tQuality\t\tint16\n\tAtomPos\n}\n\nfunc (self MediaHeader) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MDHD))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self MediaHeader) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tPutTime32(b[n:], self.CreateTime)\n\tn += 4\n\tPutTime32(b[n:], self.ModifyTime)\n\tn += 4\n\tpio.PutI32BE(b[n:], self.TimeScale)\n\tn += 4\n\tpio.PutI32BE(b[n:], self.Duration)\n\tn += 4\n\tpio.PutI16BE(b[n:], self.Language)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.Quality)\n\tn += 2\n\treturn\n}\nfunc (self MediaHeader) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 2\n\tn += 2\n\treturn\n}\nfunc (self *MediaHeader) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"CreateTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.CreateTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"ModifyTime\", n+offset, err)\n\t\treturn\n\t}\n\tself.ModifyTime = GetTime32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"TimeScale\", n+offset, err)\n\t\treturn\n\t}\n\tself.TimeScale = pio.I32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"Duration\", n+offset, err)\n\t\treturn\n\t}\n\tself.Duration = pio.I32BE(b[n:])\n\tn += 4\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Language\", n+offset, err)\n\t\treturn\n\t}\n\tself.Language = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Quality\", n+offset, err)\n\t\treturn\n\t}\n\tself.Quality = pio.I16BE(b[n:])\n\tn += 2\n\treturn\n}\nfunc (self MediaHeader) Children() (r []Atom) {\n\treturn\n}\n\ntype MediaInfo struct {\n\tSound\t\t*SoundMediaInfo\n\tVideo\t\t*VideoMediaInfo\n\tData\t\t*DataInfo\n\tSample\t\t*SampleTable\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self MediaInfo) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MINF))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self MediaInfo) marshal(b []byte) (n int) {\n\tif self.Sound != nil {\n\t\tn += self.Sound.Marshal(b[n:])\n\t}\n\tif self.Video != nil {\n\t\tn += self.Video.Marshal(b[n:])\n\t}\n\tif self.Data != nil {\n\t\tn += self.Data.Marshal(b[n:])\n\t}\n\tif self.Sample != nil {\n\t\tn += self.Sample.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self MediaInfo) Len() (n int) {\n\tn += 8\n\tif self.Sound != nil {\n\t\tn += self.Sound.Len()\n\t}\n\tif self.Video != nil {\n\t\tn += self.Video.Len()\n\t}\n\tif self.Data != nil {\n\t\tn += self.Data.Len()\n\t}\n\tif self.Sample != nil {\n\t\tn += self.Sample.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *MediaInfo) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase SMHD:\n\t\t\t{\n\t\t\t\tatom := &SoundMediaInfo{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"smhd\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Sound = atom\n\t\t\t}\n\t\tcase VMHD:\n\t\t\t{\n\t\t\t\tatom := &VideoMediaInfo{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"vmhd\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Video = atom\n\t\t\t}\n\t\tcase DINF:\n\t\t\t{\n\t\t\t\tatom := &DataInfo{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"dinf\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Data = atom\n\t\t\t}\n\t\tcase STBL:\n\t\t\t{\n\t\t\t\tatom := &SampleTable{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"stbl\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Sample = atom\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self MediaInfo) Children() (r []Atom) {\n\tif self.Sound != nil {\n\t\tr = append(r, self.Sound)\n\t}\n\tif self.Video != nil {\n\t\tr = append(r, self.Video)\n\t}\n\tif self.Data != nil {\n\t\tr = append(r, self.Data)\n\t}\n\tif self.Sample != nil {\n\t\tr = append(r, self.Sample)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype DataInfo struct {\n\tRefer\t\t*DataRefer\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self DataInfo) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(DINF))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self DataInfo) marshal(b []byte) (n int) {\n\tif self.Refer != nil {\n\t\tn += self.Refer.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self DataInfo) Len() (n int) {\n\tn += 8\n\tif self.Refer != nil {\n\t\tn += self.Refer.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *DataInfo) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase DREF:\n\t\t\t{\n\t\t\t\tatom := &DataRefer{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"dref\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Refer = atom\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self DataInfo) Children() (r []Atom) {\n\tif self.Refer != nil {\n\t\tr = append(r, self.Refer)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype DataRefer struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tUrl\t*DataReferUrl\n\tAtomPos\n}\n\nfunc (self DataRefer) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(DREF))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self DataRefer) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\t_childrenNR := 0\n\tif self.Url != nil {\n\t\t_childrenNR++\n\t}\n\tpio.PutI32BE(b[n:], int32(_childrenNR))\n\tn += 4\n\tif self.Url != nil {\n\t\tn += self.Url.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self DataRefer) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tif self.Url != nil {\n\t\tn += self.Url.Len()\n\t}\n\treturn\n}\nfunc (self *DataRefer) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tn += 4\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase URL :\n\t\t\t{\n\t\t\t\tatom := &DataReferUrl{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"url \", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Url = atom\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self DataRefer) Children() (r []Atom) {\n\tif self.Url != nil {\n\t\tr = append(r, self.Url)\n\t}\n\treturn\n}\n\ntype DataReferUrl struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tAtomPos\n}\n\nfunc (self DataReferUrl) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(URL ))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self DataReferUrl) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\treturn\n}\nfunc (self DataReferUrl) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\treturn\n}\nfunc (self *DataReferUrl) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\treturn\n}\nfunc (self DataReferUrl) Children() (r []Atom) {\n\treturn\n}\n\ntype SoundMediaInfo struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tBalance\tint16\n\tAtomPos\n}\n\nfunc (self SoundMediaInfo) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(SMHD))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self SoundMediaInfo) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutI16BE(b[n:], self.Balance)\n\tn += 2\n\tn += 2\n\treturn\n}\nfunc (self SoundMediaInfo) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 2\n\tn += 2\n\treturn\n}\nfunc (self *SoundMediaInfo) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Balance\", n+offset, err)\n\t\treturn\n\t}\n\tself.Balance = pio.I16BE(b[n:])\n\tn += 2\n\tn += 2\n\treturn\n}\nfunc (self SoundMediaInfo) Children() (r []Atom) {\n\treturn\n}\n\ntype VideoMediaInfo struct {\n\tVersion\t\tuint8\n\tFlags\t\tuint32\n\tGraphicsMode\tint16\n\tOpcolor\t\t[3]int16\n\tAtomPos\n}\n\nfunc (self VideoMediaInfo) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(VMHD))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self VideoMediaInfo) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutI16BE(b[n:], self.GraphicsMode)\n\tn += 2\n\tfor _, entry := range self.Opcolor {\n\t\tpio.PutI16BE(b[n:], entry)\n\t\tn += 2\n\t}\n\treturn\n}\nfunc (self VideoMediaInfo) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 2\n\tn += 2*len(self.Opcolor[:])\n\treturn\n}\nfunc (self *VideoMediaInfo) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"GraphicsMode\", n+offset, err)\n\t\treturn\n\t}\n\tself.GraphicsMode = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2*len(self.Opcolor) {\n\t\terr = parseErr(\"Opcolor\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Opcolor {\n\t\tself.Opcolor[i] = pio.I16BE(b[n:])\n\t\tn += 2\n\t}\n\treturn\n}\nfunc (self VideoMediaInfo) Children() (r []Atom) {\n\treturn\n}\n\ntype SampleTable struct {\n\tSampleDesc\t\t*SampleDesc\n\tTimeToSample\t\t*TimeToSample\n\tCompositionOffset\t*CompositionOffset\n\tSampleToChunk\t\t*SampleToChunk\n\tSyncSample\t\t*SyncSample\n\tChunkOffset\t\t*ChunkOffset\n\tSampleSize\t\t*SampleSize\n\tAtomPos\n}\n\nfunc (self SampleTable) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(STBL))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self SampleTable) marshal(b []byte) (n int) {\n\tif self.SampleDesc != nil {\n\t\tn += self.SampleDesc.Marshal(b[n:])\n\t}\n\tif self.TimeToSample != nil {\n\t\tn += self.TimeToSample.Marshal(b[n:])\n\t}\n\tif self.CompositionOffset != nil {\n\t\tn += self.CompositionOffset.Marshal(b[n:])\n\t}\n\tif self.SampleToChunk != nil {\n\t\tn += self.SampleToChunk.Marshal(b[n:])\n\t}\n\tif self.SyncSample != nil {\n\t\tn += self.SyncSample.Marshal(b[n:])\n\t}\n\tif self.ChunkOffset != nil {\n\t\tn += self.ChunkOffset.Marshal(b[n:])\n\t}\n\tif self.SampleSize != nil {\n\t\tn += self.SampleSize.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self SampleTable) Len() (n int) {\n\tn += 8\n\tif self.SampleDesc != nil {\n\t\tn += self.SampleDesc.Len()\n\t}\n\tif self.TimeToSample != nil {\n\t\tn += self.TimeToSample.Len()\n\t}\n\tif self.CompositionOffset != nil {\n\t\tn += self.CompositionOffset.Len()\n\t}\n\tif self.SampleToChunk != nil {\n\t\tn += self.SampleToChunk.Len()\n\t}\n\tif self.SyncSample != nil {\n\t\tn += self.SyncSample.Len()\n\t}\n\tif self.ChunkOffset != nil {\n\t\tn += self.ChunkOffset.Len()\n\t}\n\tif self.SampleSize != nil {\n\t\tn += self.SampleSize.Len()\n\t}\n\treturn\n}\nfunc (self *SampleTable) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase STSD:\n\t\t\t{\n\t\t\t\tatom := &SampleDesc{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"stsd\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.SampleDesc = atom\n\t\t\t}\n\t\tcase STTS:\n\t\t\t{\n\t\t\t\tatom := &TimeToSample{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"stts\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.TimeToSample = atom\n\t\t\t}\n\t\tcase CTTS:\n\t\t\t{\n\t\t\t\tatom := &CompositionOffset{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"ctts\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.CompositionOffset = atom\n\t\t\t}\n\t\tcase STSC:\n\t\t\t{\n\t\t\t\tatom := &SampleToChunk{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"stsc\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.SampleToChunk = atom\n\t\t\t}\n\t\tcase STSS:\n\t\t\t{\n\t\t\t\tatom := &SyncSample{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"stss\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.SyncSample = atom\n\t\t\t}\n\t\tcase STCO:\n\t\t\t{\n\t\t\t\tatom := &ChunkOffset{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"stco\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.ChunkOffset = atom\n\t\t\t}\n\t\tcase STSZ:\n\t\t\t{\n\t\t\t\tatom := &SampleSize{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"stsz\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.SampleSize = atom\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self SampleTable) Children() (r []Atom) {\n\tif self.SampleDesc != nil {\n\t\tr = append(r, self.SampleDesc)\n\t}\n\tif self.TimeToSample != nil {\n\t\tr = append(r, self.TimeToSample)\n\t}\n\tif self.CompositionOffset != nil {\n\t\tr = append(r, self.CompositionOffset)\n\t}\n\tif self.SampleToChunk != nil {\n\t\tr = append(r, self.SampleToChunk)\n\t}\n\tif self.SyncSample != nil {\n\t\tr = append(r, self.SyncSample)\n\t}\n\tif self.ChunkOffset != nil {\n\t\tr = append(r, self.ChunkOffset)\n\t}\n\tif self.SampleSize != nil {\n\t\tr = append(r, self.SampleSize)\n\t}\n\treturn\n}\n\ntype SampleDesc struct {\n\tVersion\t\tuint8\n\tAVC1Desc\t*AVC1Desc\n\tMP4ADesc\t*MP4ADesc\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self SampleDesc) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(STSD))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self SampleDesc) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tn += 3\n\t_childrenNR := 0\n\tif self.AVC1Desc != nil {\n\t\t_childrenNR++\n\t}\n\tif self.MP4ADesc != nil {\n\t\t_childrenNR++\n\t}\n\t_childrenNR += len(self.Unknowns)\n\tpio.PutI32BE(b[n:], int32(_childrenNR))\n\tn += 4\n\tif self.AVC1Desc != nil {\n\t\tn += self.AVC1Desc.Marshal(b[n:])\n\t}\n\tif self.MP4ADesc != nil {\n\t\tn += self.MP4ADesc.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self SampleDesc) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tif self.AVC1Desc != nil {\n\t\tn += self.AVC1Desc.Len()\n\t}\n\tif self.MP4ADesc != nil {\n\t\tn += self.MP4ADesc.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *SampleDesc) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tn += 3\n\tn += 4\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase AVC1:\n\t\t\t{\n\t\t\t\tatom := &AVC1Desc{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"avc1\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.AVC1Desc = atom\n\t\t\t}\n\t\tcase MP4A:\n\t\t\t{\n\t\t\t\tatom := &MP4ADesc{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"mp4a\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.MP4ADesc = atom\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self SampleDesc) Children() (r []Atom) {\n\tif self.AVC1Desc != nil {\n\t\tr = append(r, self.AVC1Desc)\n\t}\n\tif self.MP4ADesc != nil {\n\t\tr = append(r, self.MP4ADesc)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype MP4ADesc struct {\n\tDataRefIdx\t\tint16\n\tVersion\t\t\tint16\n\tRevisionLevel\t\tint16\n\tVendor\t\t\tint32\n\tNumberOfChannels\tint16\n\tSampleSize\t\tint16\n\tCompressionId\t\tint16\n\tSampleRate\t\tfloat64\n\tConf\t\t\t*ElemStreamDesc\n\tUnknowns\t\t[]Atom\n\tAtomPos\n}\n\nfunc (self MP4ADesc) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MP4A))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self MP4ADesc) marshal(b []byte) (n int) {\n\tn += 6\n\tpio.PutI16BE(b[n:], self.DataRefIdx)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.Version)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.RevisionLevel)\n\tn += 2\n\tpio.PutI32BE(b[n:], self.Vendor)\n\tn += 4\n\tpio.PutI16BE(b[n:], self.NumberOfChannels)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.SampleSize)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.CompressionId)\n\tn += 2\n\tn += 2\n\tPutFixed32(b[n:], self.SampleRate)\n\tn += 4\n\tif self.Conf != nil {\n\t\tn += self.Conf.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self MP4ADesc) Len() (n int) {\n\tn += 8\n\tn += 6\n\tn += 2\n\tn += 2\n\tn += 2\n\tn += 4\n\tn += 2\n\tn += 2\n\tn += 2\n\tn += 2\n\tn += 4\n\tif self.Conf != nil {\n\t\tn += self.Conf.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *MP4ADesc) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tn += 6\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"DataRefIdx\", n+offset, err)\n\t\treturn\n\t}\n\tself.DataRefIdx = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"RevisionLevel\", n+offset, err)\n\t\treturn\n\t}\n\tself.RevisionLevel = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"Vendor\", n+offset, err)\n\t\treturn\n\t}\n\tself.Vendor = pio.I32BE(b[n:])\n\tn += 4\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"NumberOfChannels\", n+offset, err)\n\t\treturn\n\t}\n\tself.NumberOfChannels = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"SampleSize\", n+offset, err)\n\t\treturn\n\t}\n\tself.SampleSize = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"CompressionId\", n+offset, err)\n\t\treturn\n\t}\n\tself.CompressionId = pio.I16BE(b[n:])\n\tn += 2\n\tn += 2\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"SampleRate\", n+offset, err)\n\t\treturn\n\t}\n\tself.SampleRate = GetFixed32(b[n:])\n\tn += 4\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase ESDS:\n\t\t\t{\n\t\t\t\tatom := &ElemStreamDesc{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"esds\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Conf = atom\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self MP4ADesc) Children() (r []Atom) {\n\tif self.Conf != nil {\n\t\tr = append(r, self.Conf)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype AVC1Desc struct {\n\tDataRefIdx\t\tint16\n\tVersion\t\t\tint16\n\tRevision\t\tint16\n\tVendor\t\t\tint32\n\tTemporalQuality\t\tint32\n\tSpatialQuality\t\tint32\n\tWidth\t\t\tint16\n\tHeight\t\t\tint16\n\tHorizontalResolution\tfloat64\n\tVorizontalResolution\tfloat64\n\tFrameCount\t\tint16\n\tCompressorName\t\t[32]byte\n\tDepth\t\t\tint16\n\tColorTableId\t\tint16\n\tConf\t\t\t*AVC1Conf\n\tUnknowns\t\t[]Atom\n\tAtomPos\n}\n\nfunc (self AVC1Desc) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(AVC1))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self AVC1Desc) marshal(b []byte) (n int) {\n\tn += 6\n\tpio.PutI16BE(b[n:], self.DataRefIdx)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.Version)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.Revision)\n\tn += 2\n\tpio.PutI32BE(b[n:], self.Vendor)\n\tn += 4\n\tpio.PutI32BE(b[n:], self.TemporalQuality)\n\tn += 4\n\tpio.PutI32BE(b[n:], self.SpatialQuality)\n\tn += 4\n\tpio.PutI16BE(b[n:], self.Width)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.Height)\n\tn += 2\n\tPutFixed32(b[n:], self.HorizontalResolution)\n\tn += 4\n\tPutFixed32(b[n:], self.VorizontalResolution)\n\tn += 4\n\tn += 4\n\tpio.PutI16BE(b[n:], self.FrameCount)\n\tn += 2\n\tcopy(b[n:], self.CompressorName[:])\n\tn += len(self.CompressorName[:])\n\tpio.PutI16BE(b[n:], self.Depth)\n\tn += 2\n\tpio.PutI16BE(b[n:], self.ColorTableId)\n\tn += 2\n\tif self.Conf != nil {\n\t\tn += self.Conf.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self AVC1Desc) Len() (n int) {\n\tn += 8\n\tn += 6\n\tn += 2\n\tn += 2\n\tn += 2\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 2\n\tn += 2\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 2\n\tn += len(self.CompressorName[:])\n\tn += 2\n\tn += 2\n\tif self.Conf != nil {\n\t\tn += self.Conf.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *AVC1Desc) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tn += 6\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"DataRefIdx\", n+offset, err)\n\t\treturn\n\t}\n\tself.DataRefIdx = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Revision\", n+offset, err)\n\t\treturn\n\t}\n\tself.Revision = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"Vendor\", n+offset, err)\n\t\treturn\n\t}\n\tself.Vendor = pio.I32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"TemporalQuality\", n+offset, err)\n\t\treturn\n\t}\n\tself.TemporalQuality = pio.I32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"SpatialQuality\", n+offset, err)\n\t\treturn\n\t}\n\tself.SpatialQuality = pio.I32BE(b[n:])\n\tn += 4\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Width\", n+offset, err)\n\t\treturn\n\t}\n\tself.Width = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Height\", n+offset, err)\n\t\treturn\n\t}\n\tself.Height = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"HorizontalResolution\", n+offset, err)\n\t\treturn\n\t}\n\tself.HorizontalResolution = GetFixed32(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"VorizontalResolution\", n+offset, err)\n\t\treturn\n\t}\n\tself.VorizontalResolution = GetFixed32(b[n:])\n\tn += 4\n\tn += 4\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"FrameCount\", n+offset, err)\n\t\treturn\n\t}\n\tself.FrameCount = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+len(self.CompressorName) {\n\t\terr = parseErr(\"CompressorName\", n+offset, err)\n\t\treturn\n\t}\n\tcopy(self.CompressorName[:], b[n:])\n\tn += len(self.CompressorName)\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"Depth\", n+offset, err)\n\t\treturn\n\t}\n\tself.Depth = pio.I16BE(b[n:])\n\tn += 2\n\tif len(b) < n+2 {\n\t\terr = parseErr(\"ColorTableId\", n+offset, err)\n\t\treturn\n\t}\n\tself.ColorTableId = pio.I16BE(b[n:])\n\tn += 2\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase AVCC:\n\t\t\t{\n\t\t\t\tatom := &AVC1Conf{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"avcC\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Conf = atom\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self AVC1Desc) Children() (r []Atom) {\n\tif self.Conf != nil {\n\t\tr = append(r, self.Conf)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype AVC1Conf struct {\n\tData\t[]byte\n\tAtomPos\n}\n\nfunc (self AVC1Conf) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(AVCC))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self AVC1Conf) marshal(b []byte) (n int) {\n\tcopy(b[n:], self.Data[:])\n\tn += len(self.Data[:])\n\treturn\n}\nfunc (self AVC1Conf) Len() (n int) {\n\tn += 8\n\tn += len(self.Data[:])\n\treturn\n}\nfunc (self *AVC1Conf) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tself.Data = b[n:]\n\tn += len(b[n:])\n\treturn\n}\nfunc (self AVC1Conf) Children() (r []Atom) {\n\treturn\n}\n\ntype TimeToSample struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tEntries\t[]TimeToSampleEntry\n\tAtomPos\n}\n\nfunc (self TimeToSample) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(STTS))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self TimeToSample) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], uint32(len(self.Entries)))\n\tn += 4\n\tfor _, entry := range self.Entries {\n\t\tPutTimeToSampleEntry(b[n:], entry)\n\t\tn += LenTimeToSampleEntry\n\t}\n\treturn\n}\nfunc (self TimeToSample) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += LenTimeToSampleEntry*len(self.Entries)\n\treturn\n}\nfunc (self *TimeToSample) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tvar _len_Entries uint32\n\t_len_Entries = pio.U32BE(b[n:])\n\tn += 4\n\tself.Entries = make([]TimeToSampleEntry, _len_Entries)\n\tif len(b) < n+LenTimeToSampleEntry*len(self.Entries) {\n\t\terr = parseErr(\"TimeToSampleEntry\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Entries {\n\t\tself.Entries[i] = GetTimeToSampleEntry(b[n:])\n\t\tn += LenTimeToSampleEntry\n\t}\n\treturn\n}\nfunc (self TimeToSample) Children() (r []Atom) {\n\treturn\n}\n\ntype TimeToSampleEntry struct {\n\tCount\t\tuint32\n\tDuration\tuint32\n}\n\nfunc GetTimeToSampleEntry(b []byte) (self TimeToSampleEntry) {\n\tself.Count = pio.U32BE(b[0:])\n\tself.Duration = pio.U32BE(b[4:])\n\treturn\n}\nfunc PutTimeToSampleEntry(b []byte, self TimeToSampleEntry) {\n\tpio.PutU32BE(b[0:], self.Count)\n\tpio.PutU32BE(b[4:], self.Duration)\n}\n\nconst LenTimeToSampleEntry = 8\n\ntype SampleToChunk struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tEntries\t[]SampleToChunkEntry\n\tAtomPos\n}\n\nfunc (self SampleToChunk) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(STSC))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self SampleToChunk) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], uint32(len(self.Entries)))\n\tn += 4\n\tfor _, entry := range self.Entries {\n\t\tPutSampleToChunkEntry(b[n:], entry)\n\t\tn += LenSampleToChunkEntry\n\t}\n\treturn\n}\nfunc (self SampleToChunk) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += LenSampleToChunkEntry*len(self.Entries)\n\treturn\n}\nfunc (self *SampleToChunk) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tvar _len_Entries uint32\n\t_len_Entries = pio.U32BE(b[n:])\n\tn += 4\n\tself.Entries = make([]SampleToChunkEntry, _len_Entries)\n\tif len(b) < n+LenSampleToChunkEntry*len(self.Entries) {\n\t\terr = parseErr(\"SampleToChunkEntry\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Entries {\n\t\tself.Entries[i] = GetSampleToChunkEntry(b[n:])\n\t\tn += LenSampleToChunkEntry\n\t}\n\treturn\n}\nfunc (self SampleToChunk) Children() (r []Atom) {\n\treturn\n}\n\ntype SampleToChunkEntry struct {\n\tFirstChunk\tuint32\n\tSamplesPerChunk\tuint32\n\tSampleDescId\tuint32\n}\n\nfunc GetSampleToChunkEntry(b []byte) (self SampleToChunkEntry) {\n\tself.FirstChunk = pio.U32BE(b[0:])\n\tself.SamplesPerChunk = pio.U32BE(b[4:])\n\tself.SampleDescId = pio.U32BE(b[8:])\n\treturn\n}\nfunc PutSampleToChunkEntry(b []byte, self SampleToChunkEntry) {\n\tpio.PutU32BE(b[0:], self.FirstChunk)\n\tpio.PutU32BE(b[4:], self.SamplesPerChunk)\n\tpio.PutU32BE(b[8:], self.SampleDescId)\n}\n\nconst LenSampleToChunkEntry = 12\n\ntype CompositionOffset struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tEntries\t[]CompositionOffsetEntry\n\tAtomPos\n}\n\nfunc (self CompositionOffset) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(CTTS))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self CompositionOffset) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], uint32(len(self.Entries)))\n\tn += 4\n\tfor _, entry := range self.Entries {\n\t\tPutCompositionOffsetEntry(b[n:], entry)\n\t\tn += LenCompositionOffsetEntry\n\t}\n\treturn\n}\nfunc (self CompositionOffset) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += LenCompositionOffsetEntry*len(self.Entries)\n\treturn\n}\nfunc (self *CompositionOffset) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tvar _len_Entries uint32\n\t_len_Entries = pio.U32BE(b[n:])\n\tn += 4\n\tself.Entries = make([]CompositionOffsetEntry, _len_Entries)\n\tif len(b) < n+LenCompositionOffsetEntry*len(self.Entries) {\n\t\terr = parseErr(\"CompositionOffsetEntry\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Entries {\n\t\tself.Entries[i] = GetCompositionOffsetEntry(b[n:])\n\t\tn += LenCompositionOffsetEntry\n\t}\n\treturn\n}\nfunc (self CompositionOffset) Children() (r []Atom) {\n\treturn\n}\n\ntype CompositionOffsetEntry struct {\n\tCount\tuint32\n\tOffset\tuint32\n}\n\nfunc GetCompositionOffsetEntry(b []byte) (self CompositionOffsetEntry) {\n\tself.Count = pio.U32BE(b[0:])\n\tself.Offset = pio.U32BE(b[4:])\n\treturn\n}\nfunc PutCompositionOffsetEntry(b []byte, self CompositionOffsetEntry) {\n\tpio.PutU32BE(b[0:], self.Count)\n\tpio.PutU32BE(b[4:], self.Offset)\n}\n\nconst LenCompositionOffsetEntry = 8\n\ntype SyncSample struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tEntries\t[]uint32\n\tAtomPos\n}\n\nfunc (self SyncSample) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(STSS))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self SyncSample) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], uint32(len(self.Entries)))\n\tn += 4\n\tfor _, entry := range self.Entries {\n\t\tpio.PutU32BE(b[n:], entry)\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self SyncSample) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += 4*len(self.Entries)\n\treturn\n}\nfunc (self *SyncSample) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tvar _len_Entries uint32\n\t_len_Entries = pio.U32BE(b[n:])\n\tn += 4\n\tself.Entries = make([]uint32, _len_Entries)\n\tif len(b) < n+4*len(self.Entries) {\n\t\terr = parseErr(\"uint32\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Entries {\n\t\tself.Entries[i] = pio.U32BE(b[n:])\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self SyncSample) Children() (r []Atom) {\n\treturn\n}\n\ntype ChunkOffset struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tEntries\t[]uint32\n\tAtomPos\n}\n\nfunc (self ChunkOffset) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(STCO))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self ChunkOffset) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], uint32(len(self.Entries)))\n\tn += 4\n\tfor _, entry := range self.Entries {\n\t\tpio.PutU32BE(b[n:], entry)\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self ChunkOffset) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += 4*len(self.Entries)\n\treturn\n}\nfunc (self *ChunkOffset) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tvar _len_Entries uint32\n\t_len_Entries = pio.U32BE(b[n:])\n\tn += 4\n\tself.Entries = make([]uint32, _len_Entries)\n\tif len(b) < n+4*len(self.Entries) {\n\t\terr = parseErr(\"uint32\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Entries {\n\t\tself.Entries[i] = pio.U32BE(b[n:])\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self ChunkOffset) Children() (r []Atom) {\n\treturn\n}\n\ntype MovieFrag struct {\n\tHeader\t\t*MovieFragHeader\n\tTracks\t\t[]*TrackFrag\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self MovieFrag) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MOOF))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self MovieFrag) marshal(b []byte) (n int) {\n\tif self.Header != nil {\n\t\tn += self.Header.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Tracks {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self MovieFrag) Len() (n int) {\n\tn += 8\n\tif self.Header != nil {\n\t\tn += self.Header.Len()\n\t}\n\tfor _, atom := range self.Tracks {\n\t\tn += atom.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *MovieFrag) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase MFHD:\n\t\t\t{\n\t\t\t\tatom := &MovieFragHeader{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"mfhd\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Header = atom\n\t\t\t}\n\t\tcase TRAF:\n\t\t\t{\n\t\t\t\tatom := &TrackFrag{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"traf\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Tracks = append(self.Tracks, atom)\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self MovieFrag) Children() (r []Atom) {\n\tif self.Header != nil {\n\t\tr = append(r, self.Header)\n\t}\n\tfor _, atom := range self.Tracks {\n\t\tr = append(r, atom)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype MovieFragHeader struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tSeqnum\tuint32\n\tAtomPos\n}\n\nfunc (self MovieFragHeader) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MFHD))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self MovieFragHeader) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], self.Seqnum)\n\tn += 4\n\treturn\n}\nfunc (self MovieFragHeader) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\treturn\n}\nfunc (self *MovieFragHeader) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"Seqnum\", n+offset, err)\n\t\treturn\n\t}\n\tself.Seqnum = pio.U32BE(b[n:])\n\tn += 4\n\treturn\n}\nfunc (self MovieFragHeader) Children() (r []Atom) {\n\treturn\n}\n\ntype TrackFrag struct {\n\tHeader\t\t*TrackFragHeader\n\tDecodeTime\t*TrackFragDecodeTime\n\tRun\t\t*TrackFragRun\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self TrackFrag) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(TRAF))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self TrackFrag) marshal(b []byte) (n int) {\n\tif self.Header != nil {\n\t\tn += self.Header.Marshal(b[n:])\n\t}\n\tif self.DecodeTime != nil {\n\t\tn += self.DecodeTime.Marshal(b[n:])\n\t}\n\tif self.Run != nil {\n\t\tn += self.Run.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self TrackFrag) Len() (n int) {\n\tn += 8\n\tif self.Header != nil {\n\t\tn += self.Header.Len()\n\t}\n\tif self.DecodeTime != nil {\n\t\tn += self.DecodeTime.Len()\n\t}\n\tif self.Run != nil {\n\t\tn += self.Run.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *TrackFrag) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase TFHD:\n\t\t\t{\n\t\t\t\tatom := &TrackFragHeader{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"tfhd\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Header = atom\n\t\t\t}\n\t\tcase TFDT:\n\t\t\t{\n\t\t\t\tatom := &TrackFragDecodeTime{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"tfdt\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.DecodeTime = atom\n\t\t\t}\n\t\tcase TRUN:\n\t\t\t{\n\t\t\t\tatom := &TrackFragRun{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"trun\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Run = atom\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self TrackFrag) Children() (r []Atom) {\n\tif self.Header != nil {\n\t\tr = append(r, self.Header)\n\t}\n\tif self.DecodeTime != nil {\n\t\tr = append(r, self.DecodeTime)\n\t}\n\tif self.Run != nil {\n\t\tr = append(r, self.Run)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype MovieExtend struct {\n\tTracks\t\t[]*TrackExtend\n\tUnknowns\t[]Atom\n\tAtomPos\n}\n\nfunc (self MovieExtend) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(MVEX))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self MovieExtend) marshal(b []byte) (n int) {\n\tfor _, atom := range self.Tracks {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Marshal(b[n:])\n\t}\n\treturn\n}\nfunc (self MovieExtend) Len() (n int) {\n\tn += 8\n\tfor _, atom := range self.Tracks {\n\t\tn += atom.Len()\n\t}\n\tfor _, atom := range self.Unknowns {\n\t\tn += atom.Len()\n\t}\n\treturn\n}\nfunc (self *MovieExtend) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tfor n+8 < len(b) {\n\t\ttag := Tag(pio.U32BE(b[n+4:]))\n\t\tsize := int(pio.U32BE(b[n:]))\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"TagSizeInvalid\", n+offset, err)\n\t\t\treturn\n\t\t}\n\t\tswitch tag {\n\t\tcase TREX:\n\t\t\t{\n\t\t\t\tatom := &TrackExtend{}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"trex\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Tracks = append(self.Tracks, atom)\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\tatom := &Dummy{Tag_: tag, Data: b[n:n+size]}\n\t\t\t\tif _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {\n\t\t\t\t\terr = parseErr(\"\", n+offset, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.Unknowns = append(self.Unknowns, atom)\n\t\t\t}\n\t\t}\n\t\tn += size\n\t}\n\treturn\n}\nfunc (self MovieExtend) Children() (r []Atom) {\n\tfor _, atom := range self.Tracks {\n\t\tr = append(r, atom)\n\t}\n\tr = append(r, self.Unknowns...)\n\treturn\n}\n\ntype TrackExtend struct {\n\tVersion\t\t\tuint8\n\tFlags\t\t\tuint32\n\tTrackId\t\t\tuint32\n\tDefaultSampleDescIdx\tuint32\n\tDefaultSampleDuration\tuint32\n\tDefaultSampleSize\tuint32\n\tDefaultSampleFlags\tuint32\n\tAtomPos\n}\n\nfunc (self TrackExtend) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(TREX))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self TrackExtend) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], self.TrackId)\n\tn += 4\n\tpio.PutU32BE(b[n:], self.DefaultSampleDescIdx)\n\tn += 4\n\tpio.PutU32BE(b[n:], self.DefaultSampleDuration)\n\tn += 4\n\tpio.PutU32BE(b[n:], self.DefaultSampleSize)\n\tn += 4\n\tpio.PutU32BE(b[n:], self.DefaultSampleFlags)\n\tn += 4\n\treturn\n}\nfunc (self TrackExtend) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\tn += 4\n\treturn\n}\nfunc (self *TrackExtend) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"TrackId\", n+offset, err)\n\t\treturn\n\t}\n\tself.TrackId = pio.U32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"DefaultSampleDescIdx\", n+offset, err)\n\t\treturn\n\t}\n\tself.DefaultSampleDescIdx = pio.U32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"DefaultSampleDuration\", n+offset, err)\n\t\treturn\n\t}\n\tself.DefaultSampleDuration = pio.U32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"DefaultSampleSize\", n+offset, err)\n\t\treturn\n\t}\n\tself.DefaultSampleSize = pio.U32BE(b[n:])\n\tn += 4\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"DefaultSampleFlags\", n+offset, err)\n\t\treturn\n\t}\n\tself.DefaultSampleFlags = pio.U32BE(b[n:])\n\tn += 4\n\treturn\n}\nfunc (self TrackExtend) Children() (r []Atom) {\n\treturn\n}\n\ntype SampleSize struct {\n\tVersion\t\tuint8\n\tFlags\t\tuint32\n\tSampleSize\tuint32\n\tEntries\t\t[]uint32\n\tAtomPos\n}\n\nfunc (self SampleSize) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(STSZ))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self SampleSize) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], self.SampleSize)\n\tn += 4\n\tif self.SampleSize != 0 {\n\t\treturn\n\t}\n\tpio.PutU32BE(b[n:], uint32(len(self.Entries)))\n\tn += 4\n\tfor _, entry := range self.Entries {\n\t\tpio.PutU32BE(b[n:], entry)\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self SampleSize) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tif self.SampleSize != 0 {\n\t\treturn\n\t}\n\tn += 4\n\tn += 4*len(self.Entries)\n\treturn\n}\nfunc (self *SampleSize) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif len(b) < n+4 {\n\t\terr = parseErr(\"SampleSize\", n+offset, err)\n\t\treturn\n\t}\n\tself.SampleSize = pio.U32BE(b[n:])\n\tn += 4\n\tif self.SampleSize != 0 {\n\t\treturn\n\t}\n\tvar _len_Entries uint32\n\t_len_Entries = pio.U32BE(b[n:])\n\tn += 4\n\tself.Entries = make([]uint32, _len_Entries)\n\tif len(b) < n+4*len(self.Entries) {\n\t\terr = parseErr(\"uint32\", n+offset, err)\n\t\treturn\n\t}\n\tfor i := range self.Entries {\n\t\tself.Entries[i] = pio.U32BE(b[n:])\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self SampleSize) Children() (r []Atom) {\n\treturn\n}\n\ntype TrackFragRun struct {\n\tVersion\t\t\tuint8\n\tFlags\t\t\tuint32\n\tDataOffset\t\tuint32\n\tFirstSampleFlags\tuint32\n\tEntries\t\t\t[]TrackFragRunEntry\n\tAtomPos\n}\n\nfunc (self TrackFragRun) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(TRUN))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self TrackFragRun) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tpio.PutU32BE(b[n:], uint32(len(self.Entries)))\n\tn += 4\n\tif self.Flags&TRUN_DATA_OFFSET != 0 {\n\t\t{\n\t\t\tpio.PutU32BE(b[n:], self.DataOffset)\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 {\n\t\t{\n\t\t\tpio.PutU32BE(b[n:], self.FirstSampleFlags)\n\t\t\tn += 4\n\t\t}\n\t}\n\n\tfor i, entry := range self.Entries {\n\t\tvar flags uint32\n\t\tif i > 0 {\n\t\t\tflags = self.Flags\n\t\t} else {\n\t\t\tflags = self.FirstSampleFlags\n\t\t}\n\t\tif flags&TRUN_SAMPLE_DURATION != 0 {\n\t\t\tpio.PutU32BE(b[n:], entry.Duration)\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_SIZE != 0 {\n\t\t\tpio.PutU32BE(b[n:], entry.Size)\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_FLAGS != 0 {\n\t\t\tpio.PutU32BE(b[n:], entry.Flags)\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_CTS != 0 {\n\t\t\tpio.PutU32BE(b[n:], entry.Cts)\n\t\t\tn += 4\n\t\t}\n\t}\n\treturn\n}\nfunc (self TrackFragRun) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tn += 4\n\tif self.Flags&TRUN_DATA_OFFSET != 0 {\n\t\t{\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 {\n\t\t{\n\t\t\tn += 4\n\t\t}\n\t}\n\n\tfor i := range self.Entries {\n\t\tvar flags uint32\n\t\tif i > 0 {\n\t\t\tflags = self.Flags\n\t\t} else {\n\t\t\tflags = self.FirstSampleFlags\n\t\t}\n\t\tif flags&TRUN_SAMPLE_DURATION != 0 {\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_SIZE != 0 {\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_FLAGS != 0 {\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_CTS != 0 {\n\t\t\tn += 4\n\t\t}\n\t}\n\treturn\n}\nfunc (self *TrackFragRun) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tvar _len_Entries uint32\n\t_len_Entries = pio.U32BE(b[n:])\n\tn += 4\n\tself.Entries = make([]TrackFragRunEntry, _len_Entries)\n\tif self.Flags&TRUN_DATA_OFFSET != 0 {\n\t\t{\n\t\t\tif len(b) < n+4 {\n\t\t\t\terr = parseErr(\"DataOffset\", n+offset, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.DataOffset = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 {\n\t\t{\n\t\t\tif len(b) < n+4 {\n\t\t\t\terr = parseErr(\"FirstSampleFlags\", n+offset, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.FirstSampleFlags = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t}\n\n\tfor i := 0; i < int(_len_Entries); i++ {\n\t\tvar flags uint32\n\t\tif i > 0 {\n\t\t\tflags = self.Flags\n\t\t} else {\n\t\t\tflags = self.FirstSampleFlags\n\t\t}\n\t\tentry := &self.Entries[i]\n\t\tif flags&TRUN_SAMPLE_DURATION != 0 {\n\t\t\tentry.Duration = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_SIZE != 0 {\n\t\t\tentry.Size = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_FLAGS != 0 {\n\t\t\tentry.Flags = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t\tif flags&TRUN_SAMPLE_CTS != 0 {\n\t\t\tentry.Cts = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t}\n\treturn\n}\nfunc (self TrackFragRun) Children() (r []Atom) {\n\treturn\n}\n\ntype TrackFragRunEntry struct {\n\tDuration\tuint32\n\tSize\t\tuint32\n\tFlags\t\tuint32\n\tCts\t\tuint32\n}\n\nfunc GetTrackFragRunEntry(b []byte) (self TrackFragRunEntry) {\n\tself.Duration = pio.U32BE(b[0:])\n\tself.Size = pio.U32BE(b[4:])\n\tself.Flags = pio.U32BE(b[8:])\n\tself.Cts = pio.U32BE(b[12:])\n\treturn\n}\nfunc PutTrackFragRunEntry(b []byte, self TrackFragRunEntry) {\n\tpio.PutU32BE(b[0:], self.Duration)\n\tpio.PutU32BE(b[4:], self.Size)\n\tpio.PutU32BE(b[8:], self.Flags)\n\tpio.PutU32BE(b[12:], self.Cts)\n}\n\nconst LenTrackFragRunEntry = 16\n\ntype TrackFragHeader struct {\n\tVersion\t\tuint8\n\tFlags\t\tuint32\n\tBaseDataOffset\tuint64\n\tStsdId\t\tuint32\n\tDefaultDuration\tuint32\n\tDefaultSize\tuint32\n\tDefaultFlags\tuint32\n\tAtomPos\n}\n\nfunc (self TrackFragHeader) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(TFHD))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self TrackFragHeader) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tif self.Flags&TFHD_BASE_DATA_OFFSET != 0 {\n\t\t{\n\t\t\tpio.PutU64BE(b[n:], self.BaseDataOffset)\n\t\t\tn += 8\n\t\t}\n\t}\n\tif self.Flags&TFHD_STSD_ID != 0 {\n\t\t{\n\t\t\tpio.PutU32BE(b[n:], self.StsdId)\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_DURATION != 0 {\n\t\t{\n\t\t\tpio.PutU32BE(b[n:], self.DefaultDuration)\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_SIZE != 0 {\n\t\t{\n\t\t\tpio.PutU32BE(b[n:], self.DefaultSize)\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_FLAGS != 0 {\n\t\t{\n\t\t\tpio.PutU32BE(b[n:], self.DefaultFlags)\n\t\t\tn += 4\n\t\t}\n\t}\n\treturn\n}\nfunc (self TrackFragHeader) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tif self.Flags&TFHD_BASE_DATA_OFFSET != 0 {\n\t\t{\n\t\t\tn += 8\n\t\t}\n\t}\n\tif self.Flags&TFHD_STSD_ID != 0 {\n\t\t{\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_DURATION != 0 {\n\t\t{\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_SIZE != 0 {\n\t\t{\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_FLAGS != 0 {\n\t\t{\n\t\t\tn += 4\n\t\t}\n\t}\n\treturn\n}\nfunc (self *TrackFragHeader) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif self.Flags&TFHD_BASE_DATA_OFFSET != 0 {\n\t\t{\n\t\t\tif len(b) < n+8 {\n\t\t\t\terr = parseErr(\"BaseDataOffset\", n+offset, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.BaseDataOffset = pio.U64BE(b[n:])\n\t\t\tn += 8\n\t\t}\n\t}\n\tif self.Flags&TFHD_STSD_ID != 0 {\n\t\t{\n\t\t\tif len(b) < n+4 {\n\t\t\t\terr = parseErr(\"StsdId\", n+offset, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.StsdId = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_DURATION != 0 {\n\t\t{\n\t\t\tif len(b) < n+4 {\n\t\t\t\terr = parseErr(\"DefaultDuration\", n+offset, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.DefaultDuration = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_SIZE != 0 {\n\t\t{\n\t\t\tif len(b) < n+4 {\n\t\t\t\terr = parseErr(\"DefaultSize\", n+offset, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.DefaultSize = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t}\n\tif self.Flags&TFHD_DEFAULT_FLAGS != 0 {\n\t\t{\n\t\t\tif len(b) < n+4 {\n\t\t\t\terr = parseErr(\"DefaultFlags\", n+offset, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.DefaultFlags = pio.U32BE(b[n:])\n\t\t\tn += 4\n\t\t}\n\t}\n\treturn\n}\nfunc (self TrackFragHeader) Children() (r []Atom) {\n\treturn\n}\n\ntype TrackFragDecodeTime struct {\n\tVersion\tuint8\n\tFlags\tuint32\n\tTime\ttime.Time\n\tAtomPos\n}\n\nfunc (self TrackFragDecodeTime) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(TFDT))\n\tn += self.marshal(b[8:])+8\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\nfunc (self TrackFragDecodeTime) marshal(b []byte) (n int) {\n\tpio.PutU8(b[n:], self.Version)\n\tn += 1\n\tpio.PutU24BE(b[n:], self.Flags)\n\tn += 3\n\tif self.Version != 0 {\n\t\tPutTime64(b[n:], self.Time)\n\t\tn += 8\n\t} else {\n\n\t\tPutTime32(b[n:], self.Time)\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self TrackFragDecodeTime) Len() (n int) {\n\tn += 8\n\tn += 1\n\tn += 3\n\tif self.Version != 0 {\n\t\tn += 8\n\t} else {\n\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self *TrackFragDecodeTime) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"Version\", n+offset, err)\n\t\treturn\n\t}\n\tself.Version = pio.U8(b[n:])\n\tn += 1\n\tif len(b) < n+3 {\n\t\terr = parseErr(\"Flags\", n+offset, err)\n\t\treturn\n\t}\n\tself.Flags = pio.U24BE(b[n:])\n\tn += 3\n\tif self.Version != 0 {\n\t\tself.Time = GetTime64(b[n:])\n\t\tn += 8\n\t} else {\n\n\t\tself.Time = GetTime32(b[n:])\n\t\tn += 4\n\t}\n\treturn\n}\nfunc (self TrackFragDecodeTime) Children() (r []Atom) {\n\treturn\n}\n"
  },
  {
    "path": "format/mp4/mp4io/gen/gen.go",
    "content": "\npackage main\n\nimport (\n\t\"strings\"\n\t\"fmt\"\n\t\"os\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"go/printer\"\n)\n\nfunc getexprs(e ast.Expr) string {\n\tif lit, ok := e.(*ast.BasicLit); ok {\n\t\treturn lit.Value\n\t}\n\tif ident, ok := e.(*ast.Ident); ok {\n\t\treturn ident.Name\n\t}\n\treturn \"\"\n}\n\nfunc genatomdecl(origfn *ast.FuncDecl, origname, origtag string) (decls []ast.Decl) {\n\tfieldslist := &ast.FieldList{}\n\ttypespec := &ast.TypeSpec{\n\t\tName: ast.NewIdent(origname),\n\t\tType: &ast.StructType{Fields: fieldslist},\n\t}\n\n\tfor _, _stmt := range origfn.Body.List {\n\t\tstmt := _stmt.(*ast.ExprStmt)\n\t\tcallexpr := stmt.X.(*ast.CallExpr)\n\t\ttyp := callexpr.Fun.(*ast.Ident).Name\n\n\t\tif strings.HasPrefix(typ, \"_\") {\n\t\t\tif typ == \"_unknowns\" {\n\t\t\t\tfieldslist.List = append(fieldslist.List, &ast.Field{\n\t\t\t\t\tNames: []*ast.Ident{ast.NewIdent(\"Unknowns\")},\n\t\t\t\t\tType: ast.NewIdent(\"[]Atom\"),\n\t\t\t\t})\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tname := getexprs(callexpr.Args[0])\n\n\t\tname2 := \"\"\n\t\tif len(callexpr.Args) > 1 {\n\t\t\tname2 = getexprs(callexpr.Args[1])\n\t\t}\n\n\t\tlen3 := \"\"\n\t\tif len(callexpr.Args) > 2 {\n\t\t\tlen3 = getexprs(callexpr.Args[2])\n\t\t}\n\n\t\tif strings.HasPrefix(name, \"_\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch typ {\n\t\tcase \"fixed16\":\n\t\t\ttyp = \"float64\"\n\t\tcase \"fixed32\":\n\t\t\ttyp = \"float64\"\n\t\tcase \"bytesleft\":\n\t\t\ttyp = \"[]byte\"\n\t\tcase \"bytes\":\n\t\t\ttyp = \"[\"+name2+\"]byte\"\n\t\tcase \"uint24\":\n\t\t\ttyp = \"uint32\"\n\t\tcase \"time64\", \"time32\":\n\t\t\ttyp = \"time.Time\"\n\t\tcase \"atom\":\n\t\t\ttyp = \"*\"+name2\n\t\tcase \"atoms\":\n\t\t\ttyp = \"[]*\"+name2\n\t\tcase \"slice\":\n\t\t\ttyp = \"[]\"+name2\n\t\tcase \"array\":\n\t\t\ttyp = \"[\"+len3+\"]\"+name2\n\t\t}\n\n\t\tfieldslist.List = append(fieldslist.List, &ast.Field{\n\t\t\tNames: []*ast.Ident{ast.NewIdent(name)},\n\t\t\tType: ast.NewIdent(typ),\n\t\t})\n\t}\n\n\tif origtag != \"\" {\n\t\tfieldslist.List = append(fieldslist.List, &ast.Field{\n\t\t\tType: ast.NewIdent(\"AtomPos\"),\n\t\t})\n\t}\n\n\tgendecl := &ast.GenDecl{\n\t\tTok: token.TYPE,\n\t\tSpecs: []ast.Spec{\n\t\t\ttypespec,\n\t\t},\n\t}\n\tdecls = append(decls, gendecl)\n\treturn\n}\n\nfunc typegetlen(typ string) (n int) {\n\tswitch typ {\n\tcase \"uint8\":\n\t\tn = 1\n\tcase \"uint16\":\n\t\tn = 2\n\tcase \"uint24\":\n\t\tn = 3\n\tcase \"uint32\":\n\t\tn = 4\n\tcase \"int16\":\n\t\tn = 2\n\tcase \"int32\":\n\t\tn = 4\n\tcase \"uint64\":\n\t\tn = 8\n\tcase \"time32\":\n\t\tn = 4\n\tcase \"time64\":\n\t\tn = 8\n\tcase \"fixed32\":\n\t\tn = 4\n\tcase \"fixed16\":\n\t\tn = 2\n\t}\n\treturn\n}\n\nfunc typegetlens(typ string) string {\n\tn := typegetlen(typ)\n\tif n == 0 {\n\t\treturn \"Len\"+typ\n\t} else {\n\t\treturn fmt.Sprint(n)\n\t}\n}\n\nfunc typegetvartype(typ string) string {\n\tswitch typ {\n\tcase \"uint8\":\n\t\treturn \"uint8\"\n\tcase \"uint16\":\n\t\treturn \"uint16\"\n\tcase \"uint24\":\n\t\treturn \"uint32\"\n\tcase \"uint32\":\n\t\treturn \"uint32\"\n\tcase \"uint64\":\n\t\treturn \"uint64\"\n\tcase \"int16\":\n\t\treturn \"int16\"\n\tcase \"int32\":\n\t\treturn \"int32\"\n\t}\n\treturn \"\"\n}\n\nfunc typegetputfn(typ string) (fn string) {\n\tfn = typ\n\tswitch typ {\n\tcase \"uint8\":\n\t\tfn = \"pio.PutU8\"\n\tcase \"uint16\":\n\t\tfn = \"pio.PutU16BE\"\n\tcase \"uint24\":\n\t\tfn = \"pio.PutU24BE\"\n\tcase \"uint32\":\n\t\tfn = \"pio.PutU32BE\"\n\tcase \"int16\":\n\t\tfn = \"pio.PutI16BE\"\n\tcase \"int32\":\n\t\tfn = \"pio.PutI32BE\"\n\tcase \"uint64\":\n\t\tfn = \"pio.PutU64BE\"\n\tcase \"time32\":\n\t\tfn = \"PutTime32\"\n\tcase \"time64\":\n\t\tfn = \"PutTime64\"\n\tcase \"fixed32\":\n\t\tfn = \"PutFixed32\"\n\tcase \"fixed16\":\n\t\tfn = \"PutFixed16\"\n\tdefault:\n\t\tfn = \"Put\"+typ\n\t}\n\treturn\n}\n\nfunc typegetgetfn(typ string) (fn string) {\n\tfn = typ\n\tswitch typ {\n\tcase \"uint8\":\n\t\tfn = \"pio.U8\"\n\tcase \"uint16\":\n\t\tfn = \"pio.U16BE\"\n\tcase \"uint24\":\n\t\tfn = \"pio.U24BE\"\n\tcase \"uint32\":\n\t\tfn = \"pio.U32BE\"\n\tcase \"int16\":\n\t\tfn = \"pio.I16BE\"\n\tcase \"int32\":\n\t\tfn = \"pio.I32BE\"\n\tcase \"uint64\":\n\t\tfn = \"pio.U64BE\"\n\tcase \"time32\":\n\t\tfn = \"GetTime32\"\n\tcase \"time64\":\n\t\tfn = \"GetTime64\"\n\tcase \"fixed32\":\n\t\tfn = \"GetFixed32\"\n\tcase \"fixed16\":\n\t\tfn = \"GetFixed16\"\n\tdefault:\n\t\tfn = \"Get\"+typ\n\t}\n\treturn\n}\n\nfunc addns(n string) (stmts []ast.Stmt) {\n\tassign := &ast.AssignStmt{\n\t\tTok: token.ADD_ASSIGN,\n\t\tLhs: []ast.Expr{ast.NewIdent(\"n\")},\n\t\tRhs: []ast.Expr{&ast.BasicLit{Kind: token.INT, Value: n}},\n\t}\n\tstmts = append(stmts, assign)\n\treturn\n}\n\nfunc addn(n int) (stmts []ast.Stmt) {\n\treturn addns(fmt.Sprint(n))\n}\n\nfunc simplecall(fun string, args... string) *ast.ExprStmt {\n\t_args := []ast.Expr{}\n\tfor _, s := range args {\n\t\t_args = append(_args, ast.NewIdent(s))\n\t}\n\treturn &ast.ExprStmt{\n\t\tX: &ast.CallExpr{\n\t\t\tFun: ast.NewIdent(fun),\n\t\t\tArgs: _args,\n\t\t},\n\t}\n}\n\nfunc getxx(typ string, pos, name string, conv bool) (stmts []ast.Stmt) {\n\tfn := typegetgetfn(typ)\n\tassign := &ast.AssignStmt{\n\t\tTok: token.ASSIGN,\n\t\tLhs: []ast.Expr{ast.NewIdent(name)},\n\t\tRhs: []ast.Expr{simplecall(fn, \"b[\"+pos+\":]\").X},\n\t}\n\tstmts = append(stmts, assign)\n\treturn\n}\n\nfunc putxx(typ string, pos, name string, conv bool) (stmts []ast.Stmt) {\n\tif conv {\n\t\tname = fmt.Sprintf(\"%s(%s)\", typ, name)\n\t}\n\tfn := typegetputfn(typ)\n\tstmts = append(stmts, simplecall(fn, \"b[\"+pos+\":]\", name))\n\treturn\n}\n\nfunc putxxadd(fn string, name string, conv bool) (stmts []ast.Stmt) {\n\tn := typegetlen(fn)\n\tstmts = append(stmts, putxx(fn, \"n\", name, conv)...)\n\tstmts = append(stmts, addn(n)...)\n\treturn\n}\n\nfunc newdecl(origname, name string, params, res []*ast.Field, stmts []ast.Stmt) *ast.FuncDecl {\n\treturn &ast.FuncDecl{\n\t\tRecv: &ast.FieldList{\n\t\t\tList: []*ast.Field{\n\t\t\t\t&ast.Field{\n\t\t\t\t\tNames: []*ast.Ident{ast.NewIdent(\"self\")},\n\t\t\t\t\tType: ast.NewIdent(origname),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tName: ast.NewIdent(name),\n\t\tType: &ast.FuncType{\n\t\t\tParams: &ast.FieldList{\n\t\t\t\tList: params,\n\t\t\t},\n\t\t\tResults: &ast.FieldList{\n\t\t\t\tList: res,\n\t\t\t},\n\t\t},\n\t\tBody: &ast.BlockStmt{List: stmts},\n\t}\n}\n\nfunc getstructputgetlenfn(origfn *ast.FuncDecl, origname string) (decls []ast.Decl) {\n\tgetstmts := []ast.Stmt{}\n\tputstmts := []ast.Stmt{}\n\ttotlen := 0\n\n\tfor _, _stmt := range origfn.Body.List {\n\t\tstmt := _stmt.(*ast.ExprStmt)\n\t\tcallexpr := stmt.X.(*ast.CallExpr)\n\t\ttyp := callexpr.Fun.(*ast.Ident).Name\n\t\tname := getexprs(callexpr.Args[0])\n\n\t\tgetstmts = append(getstmts, getxx(typ, fmt.Sprint(totlen), \"self.\"+name, false)...)\n\t\tputstmts = append(putstmts, putxx(typ, fmt.Sprint(totlen), \"self.\"+name, false)...)\n\t\ttotlen += typegetlen(typ)\n\t}\n\n\tgetstmts = append(getstmts, &ast.ReturnStmt{})\n\n\tdecls = append(decls, &ast.FuncDecl{\n\t\tName: ast.NewIdent(\"Get\"+origname),\n\t\tType: &ast.FuncType{\n\t\t\tParams: &ast.FieldList{\n\t\t\t\tList: []*ast.Field{\n\t\t\t\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"b\")}, Type: ast.NewIdent(\"[]byte\")},\n\t\t\t\t},\n\t\t\t},\n\t\t\tResults: &ast.FieldList{\n\t\t\t\tList: []*ast.Field{\n\t\t\t\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"self\")}, Type: ast.NewIdent(origname)},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tBody: &ast.BlockStmt{List: getstmts},\n\t})\n\n\tdecls = append(decls, &ast.FuncDecl{\n\t\tName: ast.NewIdent(\"Put\"+origname),\n\t\tType: &ast.FuncType{\n\t\t\tParams: &ast.FieldList{\n\t\t\t\tList: []*ast.Field{\n\t\t\t\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"b\")}, Type: ast.NewIdent(\"[]byte\")},\n\t\t\t\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"self\")}, Type: ast.NewIdent(origname)},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tBody: &ast.BlockStmt{List: putstmts},\n\t})\n\n\tdecls = append(decls, &ast.GenDecl{\n\t\tTok: token.CONST,\n\t\tSpecs: []ast.Spec{\n\t\t\t&ast.ValueSpec{\n\t\t\t\tNames: []*ast.Ident{ast.NewIdent(\"Len\"+origname)},\n\t\t\t\tValues: []ast.Expr{ast.NewIdent(fmt.Sprint(totlen))},\n\t\t\t},\n\t\t},\n\t})\n\n\treturn\n}\n\nfunc cc4decls(name string) (decls []ast.Decl) {\n\tconstdecl := &ast.GenDecl{\n\t\tTok: token.CONST,\n\t\tSpecs: []ast.Spec{\n\t\t\t&ast.ValueSpec{\n\t\t\t\tNames: []*ast.Ident{\n\t\t\t\t\tast.NewIdent(strings.ToUpper(name)),\n\t\t\t\t},\n\t\t\t\tValues: []ast.Expr{\n\t\t\t\t\t&ast.CallExpr{\n\t\t\t\t\t\tFun: ast.NewIdent(\"Tag\"),\n\t\t\t\t\t\tArgs: []ast.Expr{&ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf(\"0x%x\", []byte(name))}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tdecls = append(decls, constdecl)\n\treturn\n}\n\nfunc codeclonereplace(stmts []ast.Stmt, doit []ast.Stmt) (out []ast.Stmt) {\n\tout = append([]ast.Stmt(nil), stmts...)\n\tfor i := range out {\n\t\tif ifstmt, ok := out[i].(*ast.IfStmt); ok {\n\t\t\tnewifstmt := &ast.IfStmt{\n\t\t\t\tCond: ifstmt.Cond,\n\t\t\t\tBody: &ast.BlockStmt{\n\t\t\t\t\tList: codeclonereplace(ifstmt.Body.List, doit),\n\t\t\t\t},\n\t\t\t}\n\t\t\tif ifstmt.Else != nil {\n\t\t\t\tnewifstmt.Else = &ast.BlockStmt{\n\t\t\t\t\tList: codeclonereplace(ifstmt.Else.(*ast.BlockStmt).List, doit),\n\t\t\t\t}\n\t\t\t}\n\t\t\tout[i] = newifstmt\n\t\t} else if exprstmt, ok := out[i].(*ast.ExprStmt); ok {\n\t\t\tif callexpr, ok := exprstmt.X.(*ast.CallExpr); ok {\n\t\t\t\tif getexprs(callexpr.Fun) == \"doit\" {\n\t\t\t\t\tout[i] = &ast.BlockStmt{List: doit}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc getatommarshalfn(origfn *ast.FuncDecl,\n\torigname, origtag string,\n\ttagnamemap map[string]string,\n) (decls []ast.Decl) {\n\tmarstmts := []ast.Stmt{}\n\tunmarstmts := []ast.Stmt{}\n\tlenstmts := []ast.Stmt{}\n\tchildrenstmts := []ast.Stmt{}\n\n\tparseerrreturn := func(debug string) (stmts []ast.Stmt) {\n\t\treturn []ast.Stmt{\n\t\t\t&ast.AssignStmt{\n\t\t\t\tTok: token.ASSIGN,\n\t\t\t\tLhs: []ast.Expr{ast.NewIdent(\"err\")},\n\t\t\t\tRhs: []ast.Expr{ast.NewIdent(fmt.Sprintf(`parseErr(\"%s\", n+offset, err)`, debug))},\n\t\t\t},\n\t\t\t&ast.ReturnStmt{},\n\t\t}\n\t}\n\n\tcallmarshal := func(name string) (stmts []ast.Stmt) {\n\t\tcallexpr := &ast.CallExpr{\n\t\t\tFun: ast.NewIdent(name+\".Marshal\"),\n\t\t\tArgs: []ast.Expr{ast.NewIdent(\"b[n:]\")},\n\t\t}\n\t\tassign := &ast.AssignStmt{\n\t\t\tTok: token.ADD_ASSIGN,\n\t\t\tLhs: []ast.Expr{ast.NewIdent(\"n\")},\n\t\t\tRhs: []ast.Expr{callexpr},\n\t\t}\n\t\tstmts = append(stmts, assign)\n\t\treturn\n\t}\n\n\tcallputstruct := func(typ, name string) (stmts []ast.Stmt) {\n\t\tstmts = append(stmts, &ast.ExprStmt{\n\t\t\tX: &ast.CallExpr{\n\t\t\t\tFun: ast.NewIdent(typegetputfn(typ)),\n\t\t\t\tArgs: []ast.Expr{ast.NewIdent(\"b[n:]\"), ast.NewIdent(name)},\n\t\t\t},\n\t\t})\n\t\tstmts = append(stmts, &ast.AssignStmt{\n\t\t\tTok: token.ADD_ASSIGN,\n\t\t\tLhs: []ast.Expr{ast.NewIdent(\"n\")},\n\t\t\tRhs: []ast.Expr{ast.NewIdent(typegetlens(typ))},\n\t\t})\n\t\treturn\n\t}\n\n\tcalllenstruct := func(typ, name string) (stmts []ast.Stmt) {\n\t\tinc := typegetlens(typ)+\"*len(\"+name+\")\"\n\t\tstmts = append(stmts, &ast.AssignStmt{\n\t\t\tTok: token.ADD_ASSIGN,\n\t\t\tLhs: []ast.Expr{ast.NewIdent(\"n\")},\n\t\t\tRhs: []ast.Expr{ast.NewIdent(inc)},\n\t\t})\n\t\treturn\n\t}\n\n\tcalllen := func(name string) (stmts []ast.Stmt) {\n\t\tcallexpr := &ast.CallExpr{\n\t\t\tFun: ast.NewIdent(name+\".Len\"),\n\t\t\tArgs: []ast.Expr{},\n\t\t}\n\t\tassign := &ast.AssignStmt{\n\t\t\tTok: token.ADD_ASSIGN,\n\t\t\tLhs: []ast.Expr{ast.NewIdent(\"n\")},\n\t\t\tRhs: []ast.Expr{callexpr},\n\t\t}\n\t\tstmts = append(stmts, assign)\n\t\treturn\n\t}\n\n\tforeach := func(name, field string, block []ast.Stmt) (stmts []ast.Stmt) {\n\t\trangestmt := &ast.RangeStmt{\n\t\t\tKey: ast.NewIdent(\"_\"),\n\t\t\tValue: ast.NewIdent(name),\n\t\t\tBody: &ast.BlockStmt{\n\t\t\t\tList: block,\n\t\t\t},\n\t\t\tTok: token.DEFINE,\n\t\t\tX: ast.NewIdent(field),\n\t\t}\n\t\tstmts = append(stmts, rangestmt)\n\t\treturn\n\t}\n\n\tforeachatom := func(field string, block []ast.Stmt) (stmts []ast.Stmt) {\n\t\treturn foreach(\"atom\", field, block)\n\t}\n\n\tforeachentry := func(field string, block []ast.Stmt) (stmts []ast.Stmt) {\n\t\treturn foreach(\"entry\", field, block)\n\t}\n\n\tforeachi := func(field string, block []ast.Stmt) (stmts []ast.Stmt) {\n\t\trangestmt := &ast.RangeStmt{\n\t\t\tKey: ast.NewIdent(\"i\"),\n\t\t\tBody: &ast.BlockStmt{\n\t\t\t\tList: block,\n\t\t\t},\n\t\t\tTok: token.DEFINE,\n\t\t\tX: ast.NewIdent(field),\n\t\t}\n\t\tstmts = append(stmts, rangestmt)\n\t\treturn\n\t}\n\n\tforeachunknowns := func(block []ast.Stmt) (stmts []ast.Stmt) {\n\t\treturn foreachatom(\"self.Unknowns\", block)\n\t}\n\n\tdeclvar := func(name, typ string) (stmts []ast.Stmt) {\n\t\tstmts = append(stmts, &ast.DeclStmt{\n\t\t\tDecl: &ast.GenDecl{\n\t\t\t\tTok: token.VAR,\n\t\t\t\tSpecs: []ast.Spec{\n\t\t\t\t\t&ast.ValueSpec{\n\t\t\t\t\t\tNames: []*ast.Ident{\n\t\t\t\t\t\t\tast.NewIdent(typ),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tType: ast.NewIdent(name),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t\treturn\n\t}\n\n\tmakeslice := func(name, typ, size string) (stmts []ast.Stmt) {\n\t\tstmts = append(stmts, &ast.ExprStmt{\n\t\t\tX: ast.NewIdent(fmt.Sprintf(\"%s = make([]%s, %s)\", name, typ, size)),\n\t\t})\n\t\treturn\n\t}\n\n\tsimpleassign := func(tok token.Token, l, r string) *ast.AssignStmt {\n\t\treturn &ast.AssignStmt{\n\t\t\tTok: tok,\n\t\t\tLhs: []ast.Expr{ast.NewIdent(l)},\n\t\t\tRhs: []ast.Expr{ast.NewIdent(r)},\n\t\t}\n\t}\n\n\tstruct2tag := func(s string) string {\n\t\tname := tagnamemap[s]\n\t\treturn name\n\t}\n\n\tforeachatomsappendchildren := func(field string) (stmts []ast.Stmt) {\n\t\treturn foreachatom(field, []ast.Stmt{\n\t\t\tsimpleassign(token.ASSIGN, \"r\", \"append(r, atom)\"),\n\t\t})\n\t}\n\n\tvar hasunknowns bool\n\tvar atomnames []string\n\tvar atomtypes []string\n\tvar atomarrnames []string\n\tvar atomarrtypes []string\n\tslicenamemap := map[string]string{}\n\n\tunmarshalatom := func(typ, init string) (stmts []ast.Stmt) {\n\t\treturn []ast.Stmt{\n\t\t\t&ast.AssignStmt{Tok: token.DEFINE,\n\t\t\t\tLhs: []ast.Expr{ast.NewIdent(\"atom\")}, Rhs: []ast.Expr{ast.NewIdent(\"&\"+typ+\"{\"+init+\"}\")},\n\t\t\t},\n\t\t\t&ast.IfStmt{\n\t\t\t\tInit: &ast.AssignStmt{\n\t\t\t\t\tTok: token.ASSIGN,\n\t\t\t\t\tLhs: []ast.Expr{ast.NewIdent(\"_\"), ast.NewIdent(\"err\")},\n\t\t\t\t\tRhs: []ast.Expr{ast.NewIdent(\"atom.Unmarshal(b[n:n+size], offset+n)\")},\n\t\t\t\t},\n\t\t\t\tCond: ast.NewIdent(\"err != nil\"),\n\t\t\t\tBody: &ast.BlockStmt{List: parseerrreturn(struct2tag(typ))},\n\t\t\t},\n\t\t}\n\t}\n\n\tunmrashalatoms := func() (stmts []ast.Stmt) {\n\t\tblocks := []ast.Stmt{}\n\n\t\tblocks = append(blocks, &ast.AssignStmt{ Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent(\"tag\")},\n\t\t\tRhs: []ast.Expr{ast.NewIdent(\"Tag(pio.U32BE(b[n+4:]))\")},\n\t\t})\n\t\tblocks = append(blocks, &ast.AssignStmt{ Tok: token.DEFINE, Lhs: []ast.Expr{ast.NewIdent(\"size\")},\n\t\t\tRhs: []ast.Expr{ast.NewIdent(\"int(pio.U32BE(b[n:]))\")},\n\t\t})\n\t\tblocks = append(blocks, &ast.IfStmt{\n\t\t\tCond: ast.NewIdent(\"len(b) < n+size\"),\n\t\t\tBody: &ast.BlockStmt{List: parseerrreturn(\"TagSizeInvalid\")},\n\t\t})\n\n\t\tcases := []ast.Stmt{}\n\n\t\tfor i, atom := range atomnames {\n\t\t\tcases = append(cases, &ast.CaseClause{\n\t\t\t\tList: []ast.Expr{ast.NewIdent(strings.ToUpper(struct2tag(atomtypes[i])))},\n\t\t\t\tBody: []ast.Stmt{&ast.BlockStmt{\n\t\t\t\t\tList: append(unmarshalatom(atomtypes[i], \"\"), simpleassign(token.ASSIGN, \"self.\"+atom, \"atom\")),\n\t\t\t\t}},\n\t\t\t})\n\t\t}\n\n\t\tfor i, atom := range atomarrnames {\n\t\t\tselfatom := \"self.\"+atom\n\t\t\tcases = append(cases, &ast.CaseClause{\n\t\t\t\tList: []ast.Expr{ast.NewIdent(strings.ToUpper(struct2tag(atomarrtypes[i])))},\n\t\t\t\tBody: []ast.Stmt{&ast.BlockStmt{\n\t\t\t\t\tList: append(unmarshalatom(atomarrtypes[i], \"\"),\n\t\t\t\t\t\tsimpleassign(token.ASSIGN, selfatom, \"append(\"+selfatom+\", atom)\")),\n\t\t\t\t}},\n\t\t\t})\n\t\t}\n\n\t\tif hasunknowns {\n\t\t\tinit := \"Tag_: tag, Data: b[n:n+size]\"\n\t\t\tselfatom := \"self.Unknowns\"\n\t\t\tcases = append(cases, &ast.CaseClause{\n\t\t\t\tBody: []ast.Stmt{&ast.BlockStmt{\n\t\t\t\t\tList: append(unmarshalatom(\"Dummy\", init), simpleassign(token.ASSIGN, selfatom, \"append(\"+selfatom+\", atom)\")),\n\t\t\t\t}},\n\t\t\t})\n\t\t}\n\n\t\tblocks = append(blocks, &ast.SwitchStmt{\n\t\t\tTag: ast.NewIdent(\"tag\"),\n\t\t\tBody: &ast.BlockStmt{List: cases},\n\t\t})\n\n\t\tblocks = append(blocks, addns(\"size\")...)\n\n\t\tstmts = append(stmts, &ast.ForStmt{\n\t\t\tCond: ast.NewIdent(\"n+8 < len(b)\"),\n\t\t\tBody: &ast.BlockStmt{List: blocks},\n\t\t})\n\t\treturn\n\t}\n\n\tmarshalwrapstmts := func() (stmts []ast.Stmt) {\n\t\tstmts = append(stmts, putxx(\"uint32\", \"4\", strings.ToUpper(origtag), true)...)\n\t\tstmts = append(stmts, addns(\"self.marshal(b[8:])+8\")...)\n\t\tstmts = append(stmts, putxx(\"uint32\", \"0\", \"n\", true)...)\n\t\tstmts = append(stmts, &ast.ReturnStmt{})\n\t\treturn\n\t}\n\n\tifnotnil := func(name string, block []ast.Stmt) (stmts []ast.Stmt) {\n\t\tstmts = append(stmts, &ast.IfStmt{\n\t\t\tCond: &ast.BinaryExpr{\n\t\t\t\tX: ast.NewIdent(name),\n\t\t\t\tOp: token.NEQ,\n\t\t\t\tY: ast.NewIdent(\"nil\"),\n\t\t\t},\n\t\t\tBody: &ast.BlockStmt{List: block},\n\t\t})\n\t\treturn\n\t}\n\n\tgetchildrennr := func(name string) (stmts []ast.Stmt) {\n\t\tstmts = append(stmts, &ast.AssignStmt{\n\t\t\tTok: token.DEFINE,\n\t\t\tLhs: []ast.Expr{ast.NewIdent(name)},\n\t\t\tRhs: []ast.Expr{ast.NewIdent(\"0\")},\n\t\t})\n\t\tfor _, atom := range atomnames {\n\t\t\tstmts = append(stmts, ifnotnil(\"self.\"+atom, []ast.Stmt{\n\t\t\t\t&ast.IncDecStmt{X: ast.NewIdent(name), Tok: token.INC},\n\t\t\t})...)\n\t\t}\n\t\tif hasunknowns {\n\t\t\tassign := &ast.AssignStmt{\n\t\t\t\tTok: token.ADD_ASSIGN,\n\t\t\t\tLhs: []ast.Expr{ast.NewIdent(\"_childrenNR\")},\n\t\t\t\tRhs: []ast.Expr{ast.NewIdent(\"len(self.Unknowns)\")},\n\t\t\t}\n\t\t\tstmts = append(stmts, assign)\n\t\t}\n\t\treturn\n\t}\n\n\tcheckcurlen := func(inc, debug string) (stmts []ast.Stmt) {\n\t\tstmts = append(stmts, &ast.IfStmt{\n\t\t\tCond: &ast.BinaryExpr{\n\t\t\t\tX: ast.NewIdent(\"len(b)\"),\n\t\t\t\tOp: token.LSS,\n\t\t\t\tY: ast.NewIdent(\"n+\"+inc),\n\t\t\t},\n\t\t\tBody: &ast.BlockStmt{List: parseerrreturn(debug)},\n\t\t})\n\t\treturn\n\t}\n\n\tchecklendo := func(typ, name, debug string) (stmts []ast.Stmt) {\n\t\tstmts = append(stmts, checkcurlen(typegetlens(typ), debug)...)\n\t\tstmts = append(stmts, getxx(typ, \"n\", name, false)...)\n\t\tstmts = append(stmts, addns(typegetlens(typ))...)\n\t\treturn\n\t}\n\n\tcheckstructlendo := func(typ, name, debug string,\n\t\tforeach func(string,[]ast.Stmt)[]ast.Stmt,\n\t) (stmts []ast.Stmt) {\n\t\tinc := typegetlens(typ)+\"*len(\"+name+\")\"\n\t\tstmts = append(stmts, checkcurlen(inc, debug)...)\n\t\tstmts = append(stmts, foreach(name, append(\n\t\t\t[]ast.Stmt{\n\t\t\t\t&ast.AssignStmt{\n\t\t\t\t\tTok: token.ASSIGN,\n\t\t\t\t\tLhs: []ast.Expr{\n\t\t\t\t\t\tast.NewIdent(name+\"[i]\"),\n\t\t\t\t\t},\n\t\t\t\t\tRhs: []ast.Expr{\n\t\t\t\t\t\t&ast.CallExpr{\n\t\t\t\t\t\t\tFun: ast.NewIdent(typegetgetfn(typ)),\n\t\t\t\t\t\t\tArgs: []ast.Expr{ast.NewIdent(\"b[n:]\")},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\taddns(typegetlens(typ))...,\n\t\t))...)\n\t\treturn\n\t}\n\n\tchecklencopy := func(name string) (stmts []ast.Stmt) {\n\t\tlens := fmt.Sprintf(\"len(self.%s)\", name)\n\t\tstmts = append(stmts, checkcurlen(lens, name)...)\n\t\tstmts = append(stmts, simplecall(\"copy\", fmt.Sprintf(\"self.%s[:]\", name), \"b[n:]\"))\n\t\tstmts = append(stmts, addns(lens)...)\n\t\treturn\n\t}\n\n\tappendcode := func(args []ast.Expr,\n\t\tmarstmts *[]ast.Stmt, lenstmts *[]ast.Stmt, unmarstmts *[]ast.Stmt,\n\t\tdefmarstmts []ast.Stmt, deflenstmts []ast.Stmt, defunmarstmts []ast.Stmt,\n\t) {\n\t\tbodylist := func(i int, doit []ast.Stmt) []ast.Stmt {\n\t\t\treturn codeclonereplace(args[i].(*ast.FuncLit).Body.List, doit)\n\t\t}\n\t\tif len(args) == 1 {\n\t\t\t*marstmts = append(*marstmts, bodylist(0, defmarstmts)...)\n\t\t\t*lenstmts = append(*lenstmts, bodylist(0, deflenstmts)...)\n\t\t\t*unmarstmts = append(*unmarstmts, bodylist(0, defunmarstmts)...)\n\t\t} else {\n\t\t\t*marstmts = append(*marstmts, bodylist(0, defmarstmts)...)\n\t\t\t*lenstmts = append(*lenstmts, bodylist(1, deflenstmts)...)\n\t\t\t*unmarstmts = append(*unmarstmts, bodylist(2, defunmarstmts)...)\n\t\t}\n\t}\n\n\tgetdefaultstmts := func(\n\t\ttyp, name, name2 string,\n\t\tmarstmts *[]ast.Stmt, lenstmts *[]ast.Stmt,\n\t\tunmarstmts *[]ast.Stmt, childrenstmts *[]ast.Stmt,\n\t) {\n\t\tswitch typ {\n\t\tcase \"bytes\", \"bytesleft\":\n\t\t\t*marstmts = append(*marstmts, simplecall(\"copy\", \"b[n:]\", \"self.\"+name+\"[:]\"))\n\t\t\t*marstmts = append(*marstmts, addns(fmt.Sprintf(\"len(self.%s[:])\", name))...)\n\t\t\t*lenstmts = append(*lenstmts, addns(fmt.Sprintf(\"len(self.%s[:])\", name))...)\n\t\t\tif typ == \"bytes\" {\n\t\t\t\t*unmarstmts = append(*unmarstmts, checklencopy(name)...)\n\t\t\t} else {\n\t\t\t\t*unmarstmts = append(*unmarstmts, simpleassign(token.ASSIGN, \"self.\"+name, \"b[n:]\"))\n\t\t\t\t*unmarstmts = append(*unmarstmts, addns(\"len(b[n:])\")...)\n\t\t\t}\n\n\t\tcase \"array\":\n\t\t\t*marstmts = append(*marstmts, foreachentry(\"self.\"+name, callputstruct(name2, \"entry\"))...)\n\t\t\t*lenstmts = append(*lenstmts, calllenstruct(name2, \"self.\"+name+\"[:]\")...)\n\t\t\t*unmarstmts = append(*unmarstmts, checkstructlendo(name2, \"self.\"+name, name, foreachi)...)\n\n\t\tcase \"atoms\":\n\t\t\t*marstmts = append(*marstmts, foreachatom(\"self.\"+name, callmarshal(\"atom\"))...)\n\t\t\t*lenstmts = append(*lenstmts, foreachatom(\"self.\"+name, calllen(\"atom\"))...)\n\t\t\t*childrenstmts = append(*childrenstmts, foreachatomsappendchildren(\"self.\"+name)...)\n\n\t\tcase \"slice\":\n\t\t\t*marstmts = append(*marstmts, foreachentry(\"self.\"+name, callputstruct(name2, \"entry\"))...)\n\t\t\t*lenstmts = append(*lenstmts, calllenstruct(name2, \"self.\"+name)...)\n\t\t\t*unmarstmts = append(*unmarstmts, checkstructlendo(name2, \"self.\"+name, name2, foreachi)...)\n\n\t\tcase \"atom\":\n\t\t\t*marstmts = append(*marstmts, ifnotnil(\"self.\"+name, callmarshal(\"self.\"+name))...)\n\t\t\t*lenstmts = append(*lenstmts, ifnotnil(\"self.\"+name, calllen(\"self.\"+name))...)\n\t\t\t*childrenstmts = append(*childrenstmts, ifnotnil(\"self.\"+name, []ast.Stmt{\n\t\t\t\tsimpleassign(token.ASSIGN, \"r\", fmt.Sprintf(\"append(r, %s)\", \"self.\"+name)),\n\t\t\t})...)\n\n\t\tdefault:\n\t\t\t*marstmts = append(*marstmts, putxxadd(typ, \"self.\"+name, false)...)\n\t\t\t*lenstmts = append(*lenstmts, addn(typegetlen(typ))...)\n\t\t\t*unmarstmts = append(*unmarstmts, checklendo(typ, \"self.\"+name, name)...)\n\t\t}\n\t}\n\n\tfor _, _stmt := range origfn.Body.List {\n\t\tstmt := _stmt.(*ast.ExprStmt)\n\t\tcallexpr := stmt.X.(*ast.CallExpr)\n\t\ttyp := callexpr.Fun.(*ast.Ident).Name\n\t\tif typ == \"_unknowns\" {\n\t\t\thasunknowns = true\n\t\t} else if typ == \"atom\" {\n\t\t\tname := getexprs(callexpr.Args[0])\n\t\t\tname2 := getexprs(callexpr.Args[1])\n\t\t\tatomnames = append(atomnames, name)\n\t\t\tatomtypes = append(atomtypes, name2)\n\t\t} else if typ == \"atoms\" {\n\t\t\tname := getexprs(callexpr.Args[0])\n\t\t\tname2 := getexprs(callexpr.Args[1])\n\t\t\tatomarrnames = append(atomarrnames, name)\n\t\t\tatomarrtypes = append(atomarrtypes, name2)\n\t\t} else if typ == \"slice\" {\n\t\t\tname := getexprs(callexpr.Args[0])\n\t\t\tname2 := getexprs(callexpr.Args[1])\n\t\t\tslicenamemap[name] = name2\n\t\t}\n\t}\n\n\tlenstmts = append(lenstmts, addn(8)...)\n\tunmarstmts = append(unmarstmts, simplecall(\"(&self.AtomPos).setPos\", \"offset\", \"len(b)\"))\n\tunmarstmts = append(unmarstmts, addn(8)...)\n\n\tfor _, _stmt := range origfn.Body.List {\n\t\tstmt := _stmt.(*ast.ExprStmt)\n\t\tcallexpr := stmt.X.(*ast.CallExpr)\n\t\ttyp := callexpr.Fun.(*ast.Ident).Name\n\n\t\tname := \"\"\n\t\tif len(callexpr.Args) > 0 {\n\t\t\tname = getexprs(callexpr.Args[0])\n\t\t}\n\n\t\tname2 := \"\"\n\t\tif len(callexpr.Args) > 1 {\n\t\t\tname2 = getexprs(callexpr.Args[1])\n\t\t}\n\n\t\tvar defmarstmts, deflenstmts, defunmarstmts, defchildrenstmts []ast.Stmt\n\t\tgetdefaultstmts(typ, name, name2,\n\t\t\t&defmarstmts, &deflenstmts, &defunmarstmts, &defchildrenstmts)\n\n\t\tvar code []ast.Expr\n\t\tfor _, arg := range callexpr.Args {\n\t\t\tif fn, ok := arg.(*ast.CallExpr); ok {\n\t\t\t\tif getexprs(fn.Fun) == \"_code\" {\n\t\t\t\t\tcode = fn.Args\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif code != nil {\n\t\t\tappendcode(code,\n\t\t\t\t&marstmts, &lenstmts, &unmarstmts,\n\t\t\t\tdefmarstmts, deflenstmts, defunmarstmts,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.HasPrefix(typ, \"_\") {\n\t\t\tif typ == \"_unknowns\" {\n\t\t\t\tmarstmts = append(marstmts, foreachunknowns(callmarshal(\"atom\"))...)\n\t\t\t\tlenstmts = append(lenstmts, foreachunknowns(calllen(\"atom\"))...)\n\t\t\t\tchildrenstmts = append(childrenstmts, simpleassign(token.ASSIGN, \"r\", \"append(r, self.Unknowns...)\"))\n\t\t\t}\n\t\t\tif typ == \"_skip\" {\n\t\t\t\tmarstmts = append(marstmts, addns(name)...)\n\t\t\t\tlenstmts = append(lenstmts, addns(name)...)\n\t\t\t\tunmarstmts = append(unmarstmts, addns(name)...)\n\t\t\t}\n\t\t\tif typ == \"_code\" {\n\t\t\t\tappendcode(callexpr.Args,\n\t\t\t\t\t&marstmts, &lenstmts, &unmarstmts,\n\t\t\t\t\tdefmarstmts, deflenstmts, defunmarstmts,\n\t\t\t\t)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tif name == \"_childrenNR\" {\n\t\t\tmarstmts = append(marstmts, getchildrennr(name)...)\n\t\t\tmarstmts = append(marstmts, putxxadd(typ, name, true)...)\n\t\t\tlenstmts = append(lenstmts, addn(typegetlen(typ))...)\n\t\t\tunmarstmts = append(unmarstmts, addn(typegetlen(typ))...)\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.HasPrefix(name, \"_len_\") {\n\t\t\tfield := name[len(\"_len_\"):]\n\t\t\tmarstmts = append(marstmts, putxxadd(typ, \"len(self.\"+field+\")\", true)...)\n\t\t\tlenstmts = append(lenstmts, addn(typegetlen(typ))...)\n\t\t\tunmarstmts = append(unmarstmts, declvar(typegetvartype(typ), name)...)\n\t\t\tunmarstmts = append(unmarstmts, getxx(typ, \"n\", name, false)...)\n\t\t\tunmarstmts = append(unmarstmts, addn(typegetlen(typ))...)\n\t\t\tunmarstmts = append(unmarstmts, makeslice(\"self.\"+field, slicenamemap[field], name)...)\n\t\t\tcontinue\n\t\t}\n\n\t\tmarstmts = append(marstmts, defmarstmts...)\n\t\tlenstmts = append(lenstmts, deflenstmts...)\n\t\tunmarstmts = append(unmarstmts, defunmarstmts...)\n\t\tchildrenstmts = append(childrenstmts, defchildrenstmts...)\n\t}\n\n\tif len(atomnames) > 0 || len(atomarrnames) > 0 || hasunknowns {\n\t\tunmarstmts = append(unmarstmts, unmrashalatoms()...)\n\t}\n\n\tmarstmts = append(marstmts, &ast.ReturnStmt{})\n\tlenstmts = append(lenstmts, &ast.ReturnStmt{})\n\tunmarstmts = append(unmarstmts, &ast.ReturnStmt{})\n\tchildrenstmts = append(childrenstmts, &ast.ReturnStmt{})\n\n\tdecls = append(decls, newdecl(origname, \"Marshal\", []*ast.Field{\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"b\")}, Type: ast.NewIdent(\"[]byte\")},\n\t}, []*ast.Field{\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"n\")}, Type: ast.NewIdent(\"int\")},\n\t}, marshalwrapstmts()))\n\n\tdecls = append(decls, newdecl(origname, \"marshal\", []*ast.Field{\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"b\")}, Type: ast.NewIdent(\"[]byte\")},\n\t}, []*ast.Field{\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"n\")}, Type: ast.NewIdent(\"int\")},\n\t}, marstmts))\n\n\tdecls = append(decls, newdecl(origname, \"Len\", []*ast.Field{}, []*ast.Field{\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"n\")}, Type: ast.NewIdent(\"int\")},\n\t}, lenstmts))\n\n\tdecls = append(decls, newdecl(\"*\"+origname, \"Unmarshal\", []*ast.Field{\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"b\")}, Type: ast.NewIdent(\"[]byte\")},\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"offset\")}, Type: ast.NewIdent(\"int\")},\n\t}, []*ast.Field{\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"n\")}, Type: ast.NewIdent(\"int\")},\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"err\")}, Type: ast.NewIdent(\"error\")},\n\t}, unmarstmts))\n\n\tdecls = append(decls, newdecl(origname, \"Children\", []*ast.Field{}, []*ast.Field{\n\t\t&ast.Field{Names: []*ast.Ident{ast.NewIdent(\"r\")}, Type: ast.NewIdent(\"[]Atom\")},\n\t}, childrenstmts))\n\n\treturn\n}\n\nfunc genatoms(filename, outfilename string) {\n\t// Create the AST by parsing src.\n\tfset := token.NewFileSet() // positions are relative to fset\n\tfile, err := parser.ParseFile(fset, filename, nil, 0)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tgen := &ast.File{}\n\tgen.Name = ast.NewIdent(\"mp4io\")\n\tgen.Decls = []ast.Decl{\n\t\t&ast.GenDecl{\n\t\t\tTok: token.IMPORT,\n\t\t\tSpecs: []ast.Spec{\n\t\t\t\t&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `\"github.com/nareix/joy4/utils/bits/pio\"`}},\n\t\t\t},\n\t\t},\n\t\t&ast.GenDecl{\n\t\t\tTok: token.IMPORT,\n\t\t\tSpecs: []ast.Spec{\n\t\t\t\t&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `\"time\"`}},\n\t\t\t},\n\t\t},\n\t}\n\n\ttagnamemap := map[string]string{}\n\ttagnamemap[\"ElemStreamDesc\"] = \"esds\"\n\n\tsplittagname := func(fnname string) (ok bool, tag, name string) {\n\t\tif len(fnname) > 5 && fnname[4] == '_' {\n\t\t\ttag = fnname[0:4]\n\t\t\ttag = strings.Replace(tag, \"_\", \" \", 1)\n\t\t\tname = fnname[5:]\n\t\t\tok = true\n\t\t} else {\n\t\t\tname = fnname\n\t\t}\n\t\treturn\n\t}\n\n\tfor _, decl := range file.Decls {\n\t\tif fndecl, ok := decl.(*ast.FuncDecl); ok {\n\t\t\tok, tag, name := splittagname(fndecl.Name.Name)\n\t\t\tif ok {\n\t\t\t\ttagnamemap[name] = tag\n\t\t\t}\n\t\t}\n\t}\n\n\ttagfuncdecl := func(name, tag string) (decls ast.Decl) {\n\t\treturn newdecl(name, \"Tag\", []*ast.Field{}, []*ast.Field{\n\t\t\t&ast.Field{Type: ast.NewIdent(\"Tag\")},\n\t\t}, []ast.Stmt{\n\t\t\t&ast.ReturnStmt{\n\t\t\t\tResults: []ast.Expr{ast.NewIdent(strings.ToUpper(tag))}}})\n\t}\n\n\tfor k, v := range tagnamemap {\n\t\tgen.Decls = append(gen.Decls, cc4decls(v)...)\n\t\tgen.Decls = append(gen.Decls, tagfuncdecl(k, v))\n\t}\n\tgen.Decls = append(gen.Decls, cc4decls(\"mdat\")...)\n\n\tfor _, decl := range file.Decls {\n\t\tif fndecl, ok := decl.(*ast.FuncDecl); ok {\n\t\t\tok, tag, name := splittagname(fndecl.Name.Name)\n\t\t\tif ok {\n\t\t\t\tgen.Decls = append(gen.Decls, genatomdecl(fndecl, name, tag)...)\n\t\t\t\tgen.Decls = append(gen.Decls, getatommarshalfn(fndecl, name, tag, tagnamemap)...)\n\t\t\t} else {\n\t\t\t\tgen.Decls = append(gen.Decls, genatomdecl(fndecl, name, tag)...)\n\t\t\t\tgen.Decls = append(gen.Decls, getstructputgetlenfn(fndecl, name)...)\n\t\t\t}\n\t\t}\n\t}\n\n\toutfile, _ := os.Create(outfilename)\n\tprinter.Fprint(outfile, fset, gen)\n\toutfile.Close()\n}\n\nfunc parse(filename, outfilename string) {\n\tfset := token.NewFileSet()\n\tfile, err := parser.ParseFile(fset, filename, nil, 0)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\toutfile, _ := os.Create(outfilename)\n\tast.Fprint(outfile, fset, file, nil)\n\toutfile.Close()\n}\n\nfunc main() {\n\tswitch os.Args[1] {\n\tcase \"parse\":\n\t\tparse(os.Args[2], os.Args[3])\n\n\tcase \"gen\":\n\t\tgenatoms(os.Args[2], os.Args[3])\n\t}\n}\n\n"
  },
  {
    "path": "format/mp4/mp4io/gen/pattern.go",
    "content": "package main\n\nfunc moov_Movie() {\n\tatom(Header, MovieHeader)\n\tatom(MovieExtend, MovieExtend)\n\tatoms(Tracks, Track)\n\t_unknowns()\n}\n\nfunc mvhd_MovieHeader() {\n\tuint8(Version)\n\tuint24(Flags)\n\ttime32(CreateTime)\n\ttime32(ModifyTime)\n\tint32(TimeScale)\n\tint32(Duration)\n\tfixed32(PreferredRate)\n\tfixed16(PreferredVolume)\n\t_skip(10)\n\tarray(Matrix, int32, 9)\n\ttime32(PreviewTime)\n\ttime32(PreviewDuration)\n\ttime32(PosterTime)\n\ttime32(SelectionTime)\n\ttime32(SelectionDuration)\n\ttime32(CurrentTime)\n\tint32(NextTrackId)\n}\n\nfunc trak_Track() {\n\tatom(Header, TrackHeader)\n\tatom(Media, Media)\n\t_unknowns()\n}\n\nfunc tkhd_TrackHeader() {\n\tuint8(Version)\n\tuint24(Flags)\n\ttime32(CreateTime)\n\ttime32(ModifyTime)\n\tint32(TrackId)\n\t_skip(4)\n\tint32(Duration)\n\t_skip(8)\n\tint16(Layer)\n\tint16(AlternateGroup)\n\tfixed16(Volume)\n\t_skip(2)\n\tarray(Matrix, int32, 9)\n\tfixed32(TrackWidth)\n\tfixed32(TrackHeight)\n}\n\nfunc hdlr_HandlerRefer() {\n\tuint8(Version)\n\tuint24(Flags)\n\tbytes(Type, 4)\n\tbytes(SubType, 4)\n\tbytesleft(Name)\n}\n\nfunc mdia_Media() {\n\tatom(Header, MediaHeader)\n\tatom(Handler, HandlerRefer)\n\tatom(Info, MediaInfo)\n\t_unknowns()\n}\n\nfunc mdhd_MediaHeader() {\n\tuint8(Version)\n\tuint24(Flags)\n\ttime32(CreateTime)\n\ttime32(ModifyTime)\n\tint32(TimeScale)\n\tint32(Duration)\n\tint16(Language)\n\tint16(Quality)\n}\n\nfunc minf_MediaInfo() {\n\tatom(Sound, SoundMediaInfo)\n\tatom(Video, VideoMediaInfo)\n\tatom(Data, DataInfo)\n\tatom(Sample, SampleTable)\n\t_unknowns()\n}\n\nfunc dinf_DataInfo() {\n\tatom(Refer, DataRefer)\n\t_unknowns()\n}\n\nfunc dref_DataRefer() {\n\tuint8(Version)\n\tuint24(Flags)\n\tint32(_childrenNR)\n\tatom(Url, DataReferUrl)\n}\n\nfunc url__DataReferUrl() {\n\tuint8(Version)\n\tuint24(Flags)\n}\n\nfunc smhd_SoundMediaInfo() {\n\tuint8(Version)\n\tuint24(Flags)\n\tint16(Balance)\n\t_skip(2)\n}\n\nfunc vmhd_VideoMediaInfo() {\n\tuint8(Version)\n\tuint24(Flags)\n\tint16(GraphicsMode)\n\tarray(Opcolor, int16, 3)\n}\n\nfunc stbl_SampleTable() {\n\tatom(SampleDesc, SampleDesc)\n\tatom(TimeToSample, TimeToSample)\n\tatom(CompositionOffset, CompositionOffset)\n\tatom(SampleToChunk, SampleToChunk)\n\tatom(SyncSample, SyncSample)\n\tatom(ChunkOffset, ChunkOffset)\n\tatom(SampleSize, SampleSize)\n}\n\nfunc stsd_SampleDesc() {\n\tuint8(Version)\n\t_skip(3)\n\tint32(_childrenNR)\n\tatom(AVC1Desc, AVC1Desc)\n\tatom(MP4ADesc, MP4ADesc)\n\t_unknowns()\n}\n\nfunc mp4a_MP4ADesc() {\n\t_skip(6)\n\tint16(DataRefIdx)\n\tint16(Version)\n\tint16(RevisionLevel)\n\tint32(Vendor)\n\tint16(NumberOfChannels)\n\tint16(SampleSize)\n\tint16(CompressionId)\n\t_skip(2)\n\tfixed32(SampleRate)\n\tatom(Conf, ElemStreamDesc)\n\t_unknowns()\n}\n\nfunc avc1_AVC1Desc() {\n\t_skip(6)\n\tint16(DataRefIdx)\n\tint16(Version)\n\tint16(Revision)\n\tint32(Vendor)\n\tint32(TemporalQuality)\n\tint32(SpatialQuality)\n\tint16(Width)\n\tint16(Height)\n\tfixed32(HorizontalResolution)\n\tfixed32(VorizontalResolution)\n\t_skip(4)\n\tint16(FrameCount)\n\tbytes(CompressorName, 32)\n\tint16(Depth)\n\tint16(ColorTableId)\n\tatom(Conf, AVC1Conf)\n\t_unknowns()\n}\n\nfunc avcC_AVC1Conf() {\n\tbytesleft(Data)\n}\n\nfunc stts_TimeToSample() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(_len_Entries)\n\tslice(Entries, TimeToSampleEntry)\n}\n\nfunc TimeToSampleEntry() {\n\tuint32(Count)\n\tuint32(Duration)\n}\n\nfunc stsc_SampleToChunk() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(_len_Entries)\n\tslice(Entries, SampleToChunkEntry)\n}\n\nfunc SampleToChunkEntry() {\n\tuint32(FirstChunk)\n\tuint32(SamplesPerChunk)\n\tuint32(SampleDescId)\n}\n\nfunc ctts_CompositionOffset() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(_len_Entries)\n\tslice(Entries, CompositionOffsetEntry)\n}\n\nfunc CompositionOffsetEntry() {\n\tuint32(Count)\n\tuint32(Offset)\n}\n\nfunc stss_SyncSample() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(_len_Entries)\n\tslice(Entries, uint32)\n}\n\nfunc stco_ChunkOffset() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(_len_Entries)\n\tslice(Entries, uint32)\n}\n\nfunc moof_MovieFrag() {\n\tatom(Header, MovieFragHeader)\n\tatoms(Tracks, TrackFrag)\n\t_unknowns()\n}\n\nfunc mfhd_MovieFragHeader() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(Seqnum)\n}\n\nfunc traf_TrackFrag() {\n\tatom(Header, TrackFragHeader)\n\tatom(DecodeTime, TrackFragDecodeTime)\n\tatom(Run, TrackFragRun)\n\t_unknowns()\n}\n\nfunc mvex_MovieExtend() {\n\tatoms(Tracks, TrackExtend)\n\t_unknowns()\n}\n\nfunc trex_TrackExtend() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(TrackId)\n\tuint32(DefaultSampleDescIdx)\n\tuint32(DefaultSampleDuration)\n\tuint32(DefaultSampleSize)\n\tuint32(DefaultSampleFlags)\n}\n\nfunc stsz_SampleSize() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(SampleSize)\n\t_code(func() {\n\t\tif self.SampleSize != 0 {\n\t\t\treturn\n\t\t}\n\t})\n\tuint32(_len_Entries)\n\tslice(Entries, uint32)\n}\n\nfunc trun_TrackFragRun() {\n\tuint8(Version)\n\tuint24(Flags)\n\tuint32(_len_Entries)\n\n\tuint32(DataOffset, _code(func() {\n\t\tif self.Flags&TRUN_DATA_OFFSET != 0 {\n\t\t\tdoit()\n\t\t}\n\t}))\n\n\tuint32(FirstSampleFlags, _code(func() {\n\t\tif self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 {\n\t\t\tdoit()\n\t\t}\n\t}))\n\n\tslice(Entries, TrackFragRunEntry, _code(func() {\n\t\tfor i, entry := range self.Entries {\n\t\t\tvar flags uint32\n\t\t\tif i > 0 {\n\t\t\t\tflags = self.Flags\n\t\t\t} else {\n\t\t\t\tflags = self.FirstSampleFlags\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_DURATION != 0 {\n\t\t\t\tpio.PutU32BE(b[n:], entry.Duration)\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_SIZE != 0 {\n\t\t\t\tpio.PutU32BE(b[n:], entry.Size)\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_FLAGS != 0 {\n\t\t\t\tpio.PutU32BE(b[n:], entry.Flags)\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_CTS != 0 {\n\t\t\t\tpio.PutU32BE(b[n:], entry.Cts)\n\t\t\t\tn += 4\n\t\t\t}\n\t\t}\n\t}, func() {\n\t\tfor i := range self.Entries {\n\t\t\tvar flags uint32\n\t\t\tif i > 0 {\n\t\t\t\tflags = self.Flags\n\t\t\t} else {\n\t\t\t\tflags = self.FirstSampleFlags\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_DURATION != 0 {\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_SIZE != 0 {\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_FLAGS != 0 {\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_CTS != 0 {\n\t\t\t\tn += 4\n\t\t\t}\n\t\t}\n\t}, func() {\n\t\tfor i := 0; i < int(_len_Entries); i++ {\n\t\t\tvar flags uint32\n\t\t\tif i > 0 {\n\t\t\t\tflags = self.Flags\n\t\t\t} else {\n\t\t\t\tflags = self.FirstSampleFlags\n\t\t\t}\n\t\t\tentry := &self.Entries[i]\n\t\t\tif flags&TRUN_SAMPLE_DURATION != 0 {\n\t\t\t\tentry.Duration = pio.U32BE(b[n:])\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_SIZE != 0 {\n\t\t\t\tentry.Size = pio.U32BE(b[n:])\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_FLAGS != 0 {\n\t\t\t\tentry.Flags = pio.U32BE(b[n:])\n\t\t\t\tn += 4\n\t\t\t}\n\t\t\tif flags&TRUN_SAMPLE_CTS != 0 {\n\t\t\t\tentry.Cts = pio.U32BE(b[n:])\n\t\t\t\tn += 4\n\t\t\t}\n\t\t}\n\t}))\n}\n\nfunc TrackFragRunEntry() {\n\tuint32(Duration)\n\tuint32(Size)\n\tuint32(Flags)\n\tuint32(Cts)\n}\n\nfunc tfhd_TrackFragHeader() {\n\tuint8(Version)\n\tuint24(Flags)\n\n\tuint64(BaseDataOffset, _code(func() {\n\t\tif self.Flags&TFHD_BASE_DATA_OFFSET != 0 {\n\t\t\tdoit()\n\t\t}\n\t}))\n\n\tuint32(StsdId, _code(func() {\n\t\tif self.Flags&TFHD_STSD_ID != 0 {\n\t\t\tdoit()\n\t\t}\n\t}))\n\n\tuint32(DefaultDuration, _code(func() {\n\t\tif self.Flags&TFHD_DEFAULT_DURATION != 0 {\n\t\t\tdoit()\n\t\t}\n\t}))\n\n\tuint32(DefaultSize, _code(func() {\n\t\tif self.Flags&TFHD_DEFAULT_SIZE != 0 {\n\t\t\tdoit()\n\t\t}\n\t}))\n\n\tuint32(DefaultFlags, _code(func() {\n\t\tif self.Flags&TFHD_DEFAULT_FLAGS != 0 {\n\t\t\tdoit()\n\t\t}\n\t}))\n}\n\nfunc tfdt_TrackFragDecodeTime() {\n\tuint8(Version)\n\tuint24(Flags)\n\ttime64(Time, _code(func() {\n\t\tif self.Version != 0 {\n\t\t\tPutTime64(b[n:], self.Time)\n\t\t\tn += 8\n\t\t} else {\n\t\t\tPutTime32(b[n:], self.Time)\n\t\t\tn += 4\n\t\t}\n\t}, func() {\n\t\tif self.Version != 0 {\n\t\t\tn += 8\n\t\t} else {\n\t\t\tn += 4\n\t\t}\n\t}, func() {\n\t\tif self.Version != 0 {\n\t\t\tself.Time = GetTime64(b[n:])\n\t\t\tn += 8\n\t\t} else {\n\t\t\tself.Time = GetTime32(b[n:])\n\t\t\tn += 4\n\t\t}\n\t}))\n}\n\n"
  },
  {
    "path": "format/mp4/mp4io/mp4io.go",
    "content": "\npackage mp4io\n\nimport (\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n\t\"os\"\n\t\"io\"\n\t\"fmt\"\n\t\"time\"\n\t\"math\"\n\t\"strings\"\n)\n\ntype ParseError struct {\n\tDebug string\n\tOffset int\n\tprev *ParseError\n}\n\nfunc (self *ParseError) Error() string {\n\ts := []string{}\n\tfor p := self; p != nil; p = p.prev {\n\t\ts = append(s, fmt.Sprintf(\"%s:%d\", p.Debug, p.Offset))\n\t}\n\treturn \"mp4io: parse error: \"+strings.Join(s, \",\")\n}\n\nfunc parseErr(debug string, offset int, prev error) (err error) {\n\t_prev, _ := prev.(*ParseError)\n\treturn &ParseError{Debug: debug, Offset: offset, prev: _prev}\n}\n\nfunc GetTime32(b []byte) (t time.Time) {\n\tsec := pio.U32BE(b)\n\tt = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC)\n\tt = t.Add(time.Second*time.Duration(sec))\n\treturn\n}\n\nfunc PutTime32(b []byte, t time.Time) {\n\tdur := t.Sub(time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC))\n\tsec := uint32(dur/time.Second)\n\tpio.PutU32BE(b, sec)\n}\n\nfunc GetTime64(b []byte) (t time.Time) {\n\tsec := pio.U64BE(b)\n\tt = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC)\n\tt = t.Add(time.Second*time.Duration(sec))\n\treturn\n}\n\nfunc PutTime64(b []byte, t time.Time) {\n\tdur := t.Sub(time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC))\n\tsec := uint64(dur/time.Second)\n\tpio.PutU64BE(b, sec)\n}\n\nfunc PutFixed16(b []byte, f float64) {\n\tintpart, fracpart := math.Modf(f)\n\tb[0] = uint8(intpart)\n\tb[1] = uint8(fracpart*256.0)\n}\n\nfunc GetFixed16(b []byte) float64 {\n\treturn float64(b[0])+float64(b[1])/256.0\n}\n\nfunc PutFixed32(b []byte, f float64) {\n\tintpart, fracpart := math.Modf(f)\n\tpio.PutU16BE(b[0:2], uint16(intpart))\n\tpio.PutU16BE(b[2:4], uint16(fracpart*65536.0))\n}\n\nfunc GetFixed32(b []byte) float64 {\n\treturn float64(pio.U16BE(b[0:2]))+float64(pio.U16BE(b[2:4]))/65536.0\n}\n\ntype Tag uint32\n\nfunc (self Tag) String() string {\n\tvar b [4]byte\n\tpio.PutU32BE(b[:], uint32(self))\n\tfor i := 0; i < 4; i++ {\n\t\tif b[i] == 0 {\n\t\t\tb[i] = ' '\n\t\t}\n\t}\n\treturn string(b[:])\n}\n\ntype Atom interface{\n\tPos() (int,int)\n\tTag() Tag\n\tMarshal([]byte) int\n\tUnmarshal([]byte, int) (int,error)\n\tLen() int\n\tChildren() []Atom\n}\n\ntype AtomPos struct {\n\tOffset int\n\tSize int\n}\n\nfunc (self AtomPos) Pos() (int,int) {\n\treturn self.Offset, self.Size\n}\n\nfunc (self *AtomPos) setPos(offset int, size int) {\n\tself.Offset, self.Size = offset, size\n}\n\ntype Dummy struct {\n\tData []byte\n\tTag_ Tag\n\tAtomPos\n}\n\nfunc (self Dummy) Children() []Atom {\n\treturn nil\n}\n\nfunc (self Dummy) Tag() Tag {\n\treturn self.Tag_\n}\n\nfunc (self Dummy) Len() int {\n\treturn len(self.Data)\n}\n\nfunc (self Dummy) Marshal(b []byte) int {\n\tcopy(b, self.Data)\n\treturn len(self.Data)\n}\n\nfunc (self *Dummy) Unmarshal(b []byte, offset int) (n int, err error) {\n\t(&self.AtomPos).setPos(offset, len(b))\n\tself.Data = b\n\tn = len(b)\n\treturn\n}\n\nfunc StringToTag(tag string) Tag {\n\tvar b [4]byte\n\tcopy(b[:], []byte(tag))\n\treturn Tag(pio.U32BE(b[:]))\n}\n\nfunc FindChildrenByName(root Atom, tag string) Atom {\n\treturn FindChildren(root, StringToTag(tag))\n}\n\nfunc FindChildren(root Atom, tag Tag) Atom {\n\tif root.Tag() == tag {\n\t\treturn root\n\t}\n\tfor _, child := range root.Children() {\n\t\tif r := FindChildren(child, tag); r != nil {\n\t\t\treturn r\n\t\t}\n\t}\n\treturn nil\n}\n\nconst (\n\tTFHD_BASE_DATA_OFFSET     = 0x01\n\tTFHD_STSD_ID              = 0x02\n\tTFHD_DEFAULT_DURATION     = 0x08\n\tTFHD_DEFAULT_SIZE         = 0x10\n\tTFHD_DEFAULT_FLAGS        = 0x20\n\tTFHD_DURATION_IS_EMPTY    = 0x010000\n\tTFHD_DEFAULT_BASE_IS_MOOF = 0x020000\n)\n\nconst (\n\tTRUN_DATA_OFFSET        = 0x01\n\tTRUN_FIRST_SAMPLE_FLAGS = 0x04\n\tTRUN_SAMPLE_DURATION    = 0x100\n\tTRUN_SAMPLE_SIZE        = 0x200\n\tTRUN_SAMPLE_FLAGS       = 0x400\n\tTRUN_SAMPLE_CTS         = 0x800\n)\n\nconst (\n\tMP4ESDescrTag          = 3\n\tMP4DecConfigDescrTag   = 4\n\tMP4DecSpecificDescrTag = 5\n)\n\ntype ElemStreamDesc struct {\n\tDecConfig []byte\n\tTrackId uint16\n\tAtomPos\n}\n\nfunc (self ElemStreamDesc) Children() []Atom {\n\treturn nil\n}\n\nfunc (self ElemStreamDesc) fillLength(b []byte, length int) (n int) {\n\tfor i := 3; i > 0; i-- {\n\t\tb[n] = uint8(length>>uint(7*i))&0x7f|0x80\n\t\tn++\n\t}\n\tb[n] = uint8(length&0x7f)\n\tn++\n\treturn\n}\n\nfunc (self ElemStreamDesc) lenDescHdr() (n int) {\n\treturn 5\n}\n\nfunc (self ElemStreamDesc) fillDescHdr(b []byte, tag uint8, datalen int) (n int) {\n\tb[n] = tag\n\tn++\n\tn += self.fillLength(b[n:], datalen)\n\treturn\n}\n\nfunc (self ElemStreamDesc) lenESDescHdr() (n int) {\n\treturn self.lenDescHdr()+3\n}\n\nfunc (self ElemStreamDesc) fillESDescHdr(b []byte, datalen int) (n int) {\n\tn += self.fillDescHdr(b[n:], MP4ESDescrTag, datalen)\n\tpio.PutU16BE(b[n:], self.TrackId)\n\tn += 2\n\tb[n] = 0 // flags\n\tn++\n\treturn\n}\n\nfunc (self ElemStreamDesc) lenDecConfigDescHdr() (n int) {\n\treturn self.lenDescHdr()+2+3+4+4+self.lenDescHdr()\n}\n\nfunc (self ElemStreamDesc) fillDecConfigDescHdr(b []byte, datalen int) (n int) {\n\tn += self.fillDescHdr(b[n:], MP4DecConfigDescrTag, datalen)\n\tb[n] = 0x40 // objectid\n\tn++\n\tb[n] = 0x15 // streamtype\n\tn++\n\t// buffer size db\n\tpio.PutU24BE(b[n:], 0)\n\tn += 3\n\t// max bitrage\n\tpio.PutU32BE(b[n:], uint32(200000))\n\tn += 4\n\t// avg bitrage\n\tpio.PutU32BE(b[n:], uint32(0))\n\tn += 4\n\tn += self.fillDescHdr(b[n:], MP4DecSpecificDescrTag, datalen-n)\n\treturn\n}\n\nfunc (self ElemStreamDesc) Len() (n int) {\n\treturn 8+4+self.lenESDescHdr()+self.lenDecConfigDescHdr()+len(self.DecConfig)+self.lenDescHdr()+1\n}\n\n// Version(4)\n// ESDesc(\n//   MP4ESDescrTag\n//   ESID(2)\n//   ESFlags(1)\n//   DecConfigDesc(\n//     MP4DecConfigDescrTag\n//     objectId streamType bufSize avgBitrate\n//     DecSpecificDesc(\n//       MP4DecSpecificDescrTag\n//       decConfig\n//     )\n//   )\n//   ?Desc(lenDescHdr+1)\n// )\n\nfunc (self ElemStreamDesc) Marshal(b []byte) (n int) {\n\tpio.PutU32BE(b[4:], uint32(ESDS))\n\tn += 8\n\tpio.PutU32BE(b[n:], 0) // Version\n\tn += 4\n\tdatalen := self.Len()\n\tn += self.fillESDescHdr(b[n:], datalen-n-self.lenESDescHdr())\n\tn += self.fillDecConfigDescHdr(b[n:], datalen-n-self.lenDescHdr()-1)\n\tcopy(b[n:], self.DecConfig)\n\tn += len(self.DecConfig)\n\tn += self.fillDescHdr(b[n:], 0x06, datalen-n-self.lenDescHdr())\n\tb[n] = 0x02\n\tn++\n\tpio.PutU32BE(b[0:], uint32(n))\n\treturn\n}\n\nfunc (self *ElemStreamDesc) Unmarshal(b []byte, offset int) (n int, err error) {\n\tif len(b) < n+12 {\n\t\terr = parseErr(\"hdr\", offset+n, err)\n\t\treturn\n\t}\n\t(&self.AtomPos).setPos(offset, len(b))\n\tn += 8\n\tn += 4\n\treturn self.parseDesc(b[n:], offset+n)\n}\n\nfunc (self *ElemStreamDesc) parseDesc(b []byte, offset int) (n int, err error) {\n\tvar hdrlen int\n\tvar datalen int\n\tvar tag uint8\n\tif hdrlen, tag, datalen, err = self.parseDescHdr(b, offset); err != nil {\n\t\treturn\n\t}\n\tn += hdrlen\n\n\tif len(b) < n+datalen {\n\t\terr = parseErr(\"datalen\", offset+n, err)\n\t\treturn\n\t}\n\n\tswitch tag {\n\tcase MP4ESDescrTag:\n\t\tif len(b) < n+3 {\n\t\t\terr = parseErr(\"MP4ESDescrTag\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tif _, err = self.parseDesc(b[n+3:], offset+n+3); err != nil {\n\t\t\treturn\n\t\t}\n\n\tcase MP4DecConfigDescrTag:\n\t\tconst size = 2+3+4+4\n\t\tif len(b) < n+size {\n\t\t\terr = parseErr(\"MP4DecSpecificDescrTag\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tif _, err = self.parseDesc(b[n+size:], offset+n+size); err != nil {\n\t\t\treturn\n\t\t}\n\n\tcase MP4DecSpecificDescrTag:\n\t\tself.DecConfig = b[n:]\n\t}\n\n\tn += datalen\n\treturn\n}\n\nfunc (self *ElemStreamDesc) parseLength(b []byte, offset int) (n int, length int, err error) {\n\tfor n < 4 {\n\t\tif len(b) < n+1 {\n\t\t\terr = parseErr(\"len\", offset+n, err)\n\t\t\treturn\n\t\t}\n\t\tc := b[n]\n\t\tn++\n\t\tlength = (length<<7)|(int(c)&0x7f)\n\t\tif c&0x80 == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *ElemStreamDesc) parseDescHdr(b []byte, offset int) (n int, tag uint8, datalen int, err error) {\n\tif len(b) < n+1 {\n\t\terr = parseErr(\"tag\", offset+n, err)\n\t\treturn\n\t}\n\ttag = b[n]\n\tn++\n\tvar lenlen int\n\tif lenlen, datalen, err = self.parseLength(b[n:], offset+n); err != nil {\n\t\treturn\n\t}\n\tn += lenlen\n\treturn\n}\n\nfunc ReadFileAtoms(r io.ReadSeeker) (atoms []Atom, err error) {\n\tfor {\n\t\toffset, _ := r.Seek(0, 1)\n\t\ttaghdr := make([]byte, 8)\n\t\tif _, err = io.ReadFull(r, taghdr); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tsize := pio.U32BE(taghdr[0:])\n\t\ttag := Tag(pio.U32BE(taghdr[4:]))\n\n\t\tvar atom Atom\n\t\tswitch tag {\n\t\tcase MOOV:\n\t\t\tatom = &Movie{}\n\t\tcase MOOF:\n\t\t\tatom = &MovieFrag{}\n\t\t}\n\n\t\tif atom != nil {\n\t\t\tb := make([]byte, int(size))\n\t\t\tif _, err = io.ReadFull(r, b[8:]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcopy(b, taghdr)\n\t\t\tif _, err = atom.Unmarshal(b, int(offset)); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tatoms = append(atoms, atom)\n\t\t} else {\n\t\t\tdummy := &Dummy{Tag_: tag}\n\t\t\tdummy.setPos(int(offset), int(size))\n\t\t\tif _, err = r.Seek(int64(size)-8, 1); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tatoms = append(atoms, dummy)\n\t\t}\n\t}\n\treturn\n}\n\nfunc printatom(out io.Writer, root Atom, depth int) {\n\toffset, size := root.Pos()\n\n\ttype stringintf interface {\n\t\tString() string\n\t}\n\n\tfmt.Fprintf(out,\n\t\t\"%s%s offset=%d size=%d\",\n\t\tstrings.Repeat(\" \", depth*2), root.Tag(), offset, size,\n\t)\n\tif str, ok := root.(stringintf); ok {\n\t\tfmt.Fprint(out, \" \", str.String())\n\t}\n\tfmt.Fprintln(out)\n\n\tchildren := root.Children()\n\tfor _, child := range children {\n\t\tprintatom(out, child, depth+1)\n\t}\n}\n\nfunc FprintAtom(out io.Writer, root Atom) {\n\tprintatom(out, root, 0)\n}\n\nfunc PrintAtom(root Atom) {\n\tFprintAtom(os.Stdout, root)\n}\n\nfunc (self MovieHeader) String() string {\n\treturn fmt.Sprintf(\"dur=%d\", self.Duration)\n}\n\nfunc (self TimeToSample) String() string {\n\treturn fmt.Sprintf(\"entries=%d\", len(self.Entries))\n}\n\nfunc (self SampleToChunk) String() string {\n\treturn fmt.Sprintf(\"entries=%d\", len(self.Entries))\n}\n\nfunc (self SampleSize) String() string {\n\treturn fmt.Sprintf(\"entries=%d\", len(self.Entries))\n}\n\nfunc (self SyncSample) String() string {\n\treturn fmt.Sprintf(\"entries=%d\", len(self.Entries))\n}\n\nfunc (self CompositionOffset) String() string {\n\treturn fmt.Sprintf(\"entries=%d\", len(self.Entries))\n}\n\nfunc (self ChunkOffset) String() string {\n\treturn fmt.Sprintf(\"entries=%d\", len(self.Entries))\n}\n\nfunc (self TrackFragRun) String() string {\n\treturn fmt.Sprintf(\"dataoffset=%d\", self.DataOffset)\n}\n\nfunc (self TrackFragHeader) String() string {\n\treturn fmt.Sprintf(\"basedataoffset=%d\", self.BaseDataOffset)\n}\n\nfunc (self ElemStreamDesc) String() string {\n\treturn fmt.Sprintf(\"configlen=%d\", len(self.DecConfig))\n}\n\nfunc (self *Track) GetAVC1Conf() (conf *AVC1Conf) {\n\tatom := FindChildren(self, AVCC)\n\tconf, _ = atom.(*AVC1Conf)\n\treturn\n}\n\nfunc (self *Track) GetElemStreamDesc() (esds *ElemStreamDesc) {\n\tatom := FindChildren(self, ESDS)\n\tesds, _ = atom.(*ElemStreamDesc)\n\treturn\n}\n\n"
  },
  {
    "path": "format/mp4/muxer.go",
    "content": "package mp4\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/codec/aacparser\"\n\t\"github.com/nareix/joy4/codec/h264parser\"\n\t\"github.com/nareix/joy4/format/mp4/mp4io\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n\t\"io\"\n\t\"bufio\"\n)\n\ntype Muxer struct {\n\tw          io.WriteSeeker\n\tbufw       *bufio.Writer\n\twpos       int64\n\tstreams    []*Stream\n}\n\nfunc NewMuxer(w io.WriteSeeker) *Muxer {\n\treturn &Muxer{\n\t\tw: w,\n\t\tbufw: bufio.NewWriterSize(w, pio.RecommendBufioSize),\n\t}\n}\n\nfunc (self *Muxer) newStream(codec av.CodecData) (err error) {\n\tswitch codec.Type() {\n\tcase av.H264, av.AAC:\n\n\tdefault:\n\t\terr = fmt.Errorf(\"mp4: codec type=%v is not supported\", codec.Type())\n\t\treturn\n\t}\n\tstream := &Stream{CodecData: codec}\n\n\tstream.sample = &mp4io.SampleTable{\n\t\tSampleDesc:   &mp4io.SampleDesc{},\n\t\tTimeToSample: &mp4io.TimeToSample{},\n\t\tSampleToChunk: &mp4io.SampleToChunk{\n\t\t\tEntries: []mp4io.SampleToChunkEntry{\n\t\t\t\t{\n\t\t\t\t\tFirstChunk:      1,\n\t\t\t\t\tSampleDescId:    1,\n\t\t\t\t\tSamplesPerChunk: 1,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tSampleSize:  &mp4io.SampleSize{},\n\t\tChunkOffset: &mp4io.ChunkOffset{},\n\t}\n\n\tstream.trackAtom = &mp4io.Track{\n\t\tHeader: &mp4io.TrackHeader{\n\t\t\tTrackId:  int32(len(self.streams)+1),\n\t\t\tFlags:    0x0003, // Track enabled | Track in movie\n\t\t\tDuration: 0,      // fill later\n\t\t\tMatrix:   [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},\n\t\t},\n\t\tMedia: &mp4io.Media{\n\t\t\tHeader: &mp4io.MediaHeader{\n\t\t\t\tTimeScale: 0, // fill later\n\t\t\t\tDuration:  0, // fill later\n\t\t\t\tLanguage:  21956,\n\t\t\t},\n\t\t\tInfo: &mp4io.MediaInfo{\n\t\t\t\tSample: stream.sample,\n\t\t\t\tData: &mp4io.DataInfo{\n\t\t\t\t\tRefer: &mp4io.DataRefer{\n\t\t\t\t\t\tUrl: &mp4io.DataReferUrl{\n\t\t\t\t\t\t\tFlags: 0x000001, // Self reference\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tswitch codec.Type() {\n\tcase av.H264:\n\t\tstream.sample.SyncSample = &mp4io.SyncSample{}\n\t}\n\n\tstream.timeScale = 90000\n\tstream.muxer = self\n\tself.streams = append(self.streams, stream)\n\n\treturn\n}\n\nfunc (self *Stream) fillTrackAtom() (err error) {\n\tself.trackAtom.Media.Header.TimeScale = int32(self.timeScale)\n\tself.trackAtom.Media.Header.Duration = int32(self.duration)\n\n\tif self.Type() == av.H264 {\n\t\tcodec := self.CodecData.(h264parser.CodecData)\n\t\twidth, height := codec.Width(), codec.Height()\n\t\tself.sample.SampleDesc.AVC1Desc = &mp4io.AVC1Desc{\n\t\t\tDataRefIdx:           1,\n\t\t\tHorizontalResolution: 72,\n\t\t\tVorizontalResolution: 72,\n\t\t\tWidth:                int16(width),\n\t\t\tHeight:               int16(height),\n\t\t\tFrameCount:           1,\n\t\t\tDepth:                24,\n\t\t\tColorTableId:         -1,\n\t\t\tConf:                 &mp4io.AVC1Conf{Data: codec.AVCDecoderConfRecordBytes()},\n\t\t}\n\t\tself.trackAtom.Media.Handler = &mp4io.HandlerRefer{\n\t\t\tSubType: [4]byte{'v','i','d','e'},\n\t\t\tName:    []byte(\"Video Media Handler\"),\n\t\t}\n\t\tself.trackAtom.Media.Info.Video = &mp4io.VideoMediaInfo{\n\t\t\tFlags: 0x000001,\n\t\t}\n\t\tself.trackAtom.Header.TrackWidth = float64(width)\n\t\tself.trackAtom.Header.TrackHeight = float64(height)\n\n\t} else if self.Type() == av.AAC {\n\t\tcodec := self.CodecData.(aacparser.CodecData)\n\t\tself.sample.SampleDesc.MP4ADesc = &mp4io.MP4ADesc{\n\t\t\tDataRefIdx:       1,\n\t\t\tNumberOfChannels: int16(codec.ChannelLayout().Count()),\n\t\t\tSampleSize:       int16(codec.SampleFormat().BytesPerSample()),\n\t\t\tSampleRate:       float64(codec.SampleRate()),\n\t\t\tConf: &mp4io.ElemStreamDesc{\n\t\t\t\tDecConfig: codec.MPEG4AudioConfigBytes(),\n\t\t\t},\n\t\t}\n\t\tself.trackAtom.Header.Volume = 1\n\t\tself.trackAtom.Header.AlternateGroup = 1\n\t\tself.trackAtom.Media.Handler = &mp4io.HandlerRefer{\n\t\t\tSubType: [4]byte{'s','o','u','n'},\n\t\t\tName:    []byte(\"Sound Handler\"),\n\t\t}\n\t\tself.trackAtom.Media.Info.Sound = &mp4io.SoundMediaInfo{}\n\n\t} else {\n\t\terr = fmt.Errorf(\"mp4: codec type=%d invalid\", self.Type())\n\t}\n\n\treturn\n}\n\nfunc (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {\n\tself.streams = []*Stream{}\n\tfor _, stream := range streams {\n\t\tif err = self.newStream(stream); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\ttaghdr := make([]byte, 8)\n\tpio.PutU32BE(taghdr[4:], uint32(mp4io.MDAT))\n\tif _, err = self.w.Write(taghdr); err != nil {\n\t\treturn\n\t}\n\tself.wpos += 8\n\n\tfor _, stream := range self.streams {\n\t\tif stream.Type().IsVideo() {\n\t\t\tstream.sample.CompositionOffset = &mp4io.CompositionOffset{}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Muxer) WritePacket(pkt av.Packet) (err error) {\n\tstream := self.streams[pkt.Idx]\n\tif stream.lastpkt != nil {\n\t\tif err = stream.writePacket(*stream.lastpkt, pkt.Time-stream.lastpkt.Time); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tstream.lastpkt = &pkt\n\treturn\n}\n\nfunc (self *Stream) writePacket(pkt av.Packet, rawdur time.Duration) (err error) {\n\tif rawdur < 0 {\n\t\terr = fmt.Errorf(\"mp4: stream#%d time=%v < lasttime=%v\", pkt.Idx, pkt.Time, self.lastpkt.Time)\n\t\treturn\n\t}\n\n\tif _, err = self.muxer.bufw.Write(pkt.Data); err != nil {\n\t\treturn\n\t}\n\n\tif pkt.IsKeyFrame && self.sample.SyncSample != nil {\n\t\tself.sample.SyncSample.Entries = append(self.sample.SyncSample.Entries, uint32(self.sampleIndex+1))\n\t}\n\n\tduration := uint32(self.timeToTs(rawdur))\n\tif self.sttsEntry == nil || duration != self.sttsEntry.Duration {\n\t\tself.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, mp4io.TimeToSampleEntry{Duration: duration})\n\t\tself.sttsEntry = &self.sample.TimeToSample.Entries[len(self.sample.TimeToSample.Entries)-1]\n\t}\n\tself.sttsEntry.Count++\n\n\tif self.sample.CompositionOffset != nil {\n\t\toffset := uint32(self.timeToTs(pkt.CompositionTime))\n\t\tif self.cttsEntry == nil || offset != self.cttsEntry.Offset {\n\t\t\ttable := self.sample.CompositionOffset\n\t\t\ttable.Entries = append(table.Entries, mp4io.CompositionOffsetEntry{Offset: offset})\n\t\t\tself.cttsEntry = &table.Entries[len(table.Entries)-1]\n\t\t}\n\t\tself.cttsEntry.Count++\n\t}\n\n\tself.duration += int64(duration)\n\tself.sampleIndex++\n\tself.sample.ChunkOffset.Entries = append(self.sample.ChunkOffset.Entries, uint32(self.muxer.wpos))\n\tself.sample.SampleSize.Entries = append(self.sample.SampleSize.Entries, uint32(len(pkt.Data)))\n\n\tself.muxer.wpos += int64(len(pkt.Data))\n\treturn\n}\n\nfunc (self *Muxer) WriteTrailer() (err error) {\n\tfor _, stream := range self.streams {\n\t\tif stream.lastpkt != nil {\n\t\t\tif err = stream.writePacket(*stream.lastpkt, 0); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstream.lastpkt = nil\n\t\t}\n\t}\n\n\tmoov := &mp4io.Movie{}\n\tmoov.Header = &mp4io.MovieHeader{\n\t\tPreferredRate:   1,\n\t\tPreferredVolume: 1,\n\t\tMatrix:          [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},\n\t\tNextTrackId:     2,\n\t}\n\n\tmaxDur := time.Duration(0)\n\ttimeScale := int64(10000)\n\tfor _, stream := range self.streams {\n\t\tif err = stream.fillTrackAtom(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tdur := stream.tsToTime(stream.duration)\n\t\tstream.trackAtom.Header.Duration = int32(timeToTs(dur, timeScale))\n\t\tif dur > maxDur {\n\t\t\tmaxDur = dur\n\t\t}\n\t\tmoov.Tracks = append(moov.Tracks, stream.trackAtom)\n\t}\n\tmoov.Header.TimeScale = int32(timeScale)\n\tmoov.Header.Duration = int32(timeToTs(maxDur, timeScale))\n\n\tif err = self.bufw.Flush(); err != nil {\n\t\treturn\n\t}\n\n\tvar mdatsize int64\n\tif mdatsize, err = self.w.Seek(0, 1); err != nil {\n\t\treturn\n\t}\n\tif _, err = self.w.Seek(0, 0); err != nil {\n\t\treturn\n\t}\n\ttaghdr := make([]byte, 4)\n\tpio.PutU32BE(taghdr, uint32(mdatsize))\n\tif _, err = self.w.Write(taghdr); err != nil {\n\t\treturn\n\t}\n\n\tif _, err = self.w.Seek(0, 2); err != nil {\n\t\treturn\n\t}\n\tb := make([]byte, moov.Len())\n\tmoov.Marshal(b)\n\tif _, err = self.w.Write(b); err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "format/mp4/stream.go",
    "content": "package mp4\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/format/mp4/mp4io\"\n\t\"time\"\n)\n\ntype Stream struct {\n\tav.CodecData\n\n\ttrackAtom *mp4io.Track\n\tidx       int\n\n\tlastpkt *av.Packet\n\n\ttimeScale int64\n\tduration  int64\n\n\tmuxer *Muxer\n\tdemuxer *Demuxer\n\n\tsample      *mp4io.SampleTable\n\tsampleIndex int\n\n\tsampleOffsetInChunk int64\n\tsyncSampleIndex     int\n\n\tdts                    int64\n\tsttsEntryIndex         int\n\tsampleIndexInSttsEntry int\n\n\tcttsEntryIndex         int\n\tsampleIndexInCttsEntry int\n\n\tchunkGroupIndex    int\n\tchunkIndex         int\n\tsampleIndexInChunk int\n\n\tsttsEntry *mp4io.TimeToSampleEntry\n\tcttsEntry *mp4io.CompositionOffsetEntry\n}\n\nfunc timeToTs(tm time.Duration, timeScale int64) int64 {\n\treturn int64(tm*time.Duration(timeScale) / time.Second)\n}\n\nfunc tsToTime(ts int64, timeScale int64) time.Duration {\n\treturn time.Duration(ts)*time.Second / time.Duration(timeScale)\n}\n\nfunc (self *Stream) timeToTs(tm time.Duration) int64 {\n\treturn int64(tm*time.Duration(self.timeScale) / time.Second)\n}\n\nfunc (self *Stream) tsToTime(ts int64) time.Duration {\n\treturn time.Duration(ts)*time.Second / time.Duration(self.timeScale)\n}\n"
  },
  {
    "path": "format/rtmp/rtmp.go",
    "content": "package rtmp\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/hmac\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/format/flv\"\n\t\"github.com/nareix/joy4/format/flv/flvio\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar Debug bool\n\nfunc ParseURL(uri string) (u *url.URL, err error) {\n\tif u, err = url.Parse(uri); err != nil {\n\t\treturn\n\t}\n\tif _, _, serr := net.SplitHostPort(u.Host); serr != nil {\n\t\tu.Host += \":1935\"\n\t}\n\treturn\n}\n\nfunc Dial(uri string) (conn *Conn, err error) {\n\treturn DialTimeout(uri, 0)\n}\n\nfunc DialTimeout(uri string, timeout time.Duration) (conn *Conn, err error) {\n\tvar u *url.URL\n\tif u, err = ParseURL(uri); err != nil {\n\t\treturn\n\t}\n\n\tdailer := net.Dialer{Timeout: timeout}\n\tvar netconn net.Conn\n\tif netconn, err = dailer.Dial(\"tcp\", u.Host); err != nil {\n\t\treturn\n\t}\n\n\tconn = NewConn(netconn)\n\tconn.URL = u\n\treturn\n}\n\ntype Server struct {\n\tAddr          string\n\tHandlePublish func(*Conn)\n\tHandlePlay    func(*Conn)\n\tHandleConn    func(*Conn)\n}\n\nfunc (self *Server) handleConn(conn *Conn) (err error) {\n\tif self.HandleConn != nil {\n\t\tself.HandleConn(conn)\n\t} else {\n\t\tif err = conn.prepare(stageCommandDone, 0); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif conn.playing {\n\t\t\tif self.HandlePlay != nil {\n\t\t\t\tself.HandlePlay(conn)\n\t\t\t}\n\t\t} else if conn.publishing {\n\t\t\tif self.HandlePublish != nil {\n\t\t\t\tself.HandlePublish(conn)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Server) ListenAndServe() (err error) {\n\taddr := self.Addr\n\tif addr == \"\" {\n\t\taddr = \":1935\"\n\t}\n\tvar tcpaddr *net.TCPAddr\n\tif tcpaddr, err = net.ResolveTCPAddr(\"tcp\", addr); err != nil {\n\t\terr = fmt.Errorf(\"rtmp: ListenAndServe: %s\", err)\n\t\treturn\n\t}\n\n\tvar listener *net.TCPListener\n\tif listener, err = net.ListenTCP(\"tcp\", tcpaddr); err != nil {\n\t\treturn\n\t}\n\n\tif Debug {\n\t\tfmt.Println(\"rtmp: server: listening on\", addr)\n\t}\n\n\tfor {\n\t\tvar netconn net.Conn\n\t\tif netconn, err = listener.Accept(); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif Debug {\n\t\t\tfmt.Println(\"rtmp: server: accepted\")\n\t\t}\n\n\t\tconn := NewConn(netconn)\n\t\tconn.isserver = true\n\t\tgo func() {\n\t\t\terr := self.handleConn(conn)\n\t\t\tif Debug {\n\t\t\t\tfmt.Println(\"rtmp: server: client closed err:\", err)\n\t\t\t}\n\t\t}()\n\t}\n}\n\nconst (\n\tstageHandshakeDone = iota + 1\n\tstageCommandDone\n\tstageCodecDataDone\n)\n\nconst (\n\tprepareReading = iota + 1\n\tprepareWriting\n)\n\ntype Conn struct {\n\tURL             *url.URL\n\tOnPlayOrPublish func(string, flvio.AMFMap) error\n\n\tprober  *flv.Prober\n\tstreams []av.CodecData\n\n\ttxbytes uint64\n\trxbytes uint64\n\n\tbufr *bufio.Reader\n\tbufw *bufio.Writer\n\tackn uint32\n\n\twritebuf []byte\n\treadbuf  []byte\n\n\tnetconn   net.Conn\n\ttxrxcount *txrxcount\n\n\twriteMaxChunkSize int\n\treadMaxChunkSize  int\n\treadAckSize       uint32\n\treadcsmap         map[uint32]*chunkStream\n\n\tisserver            bool\n\tpublishing, playing bool\n\treading, writing    bool\n\tstage               int\n\n\tavmsgsid uint32\n\n\tgotcommand     bool\n\tcommandname    string\n\tcommandtransid float64\n\tcommandobj     flvio.AMFMap\n\tcommandparams  []interface{}\n\n\tgotmsg      bool\n\ttimestamp   uint32\n\tmsgdata     []byte\n\tmsgtypeid   uint8\n\tdatamsgvals []interface{}\n\tavtag       flvio.Tag\n\n\teventtype uint16\n}\n\ntype txrxcount struct {\n\tio.ReadWriter\n\ttxbytes uint64\n\trxbytes uint64\n}\n\nfunc (self *txrxcount) Read(p []byte) (int, error) {\n\tn, err := self.ReadWriter.Read(p)\n\tself.rxbytes += uint64(n)\n\treturn n, err\n}\n\nfunc (self *txrxcount) Write(p []byte) (int, error) {\n\tn, err := self.ReadWriter.Write(p)\n\tself.txbytes += uint64(n)\n\treturn n, err\n}\n\nfunc NewConn(netconn net.Conn) *Conn {\n\tconn := &Conn{}\n\tconn.prober = &flv.Prober{}\n\tconn.netconn = netconn\n\tconn.readcsmap = make(map[uint32]*chunkStream)\n\tconn.readMaxChunkSize = 128\n\tconn.writeMaxChunkSize = 128\n\tconn.bufr = bufio.NewReaderSize(netconn, pio.RecommendBufioSize)\n\tconn.bufw = bufio.NewWriterSize(netconn, pio.RecommendBufioSize)\n\tconn.txrxcount = &txrxcount{ReadWriter: netconn}\n\tconn.writebuf = make([]byte, 4096)\n\tconn.readbuf = make([]byte, 4096)\n\treturn conn\n}\n\ntype chunkStream struct {\n\ttimenow     uint32\n\ttimedelta   uint32\n\thastimeext  bool\n\tmsgsid      uint32\n\tmsgtypeid   uint8\n\tmsgdatalen  uint32\n\tmsgdataleft uint32\n\tmsghdrtype  uint8\n\tmsgdata     []byte\n}\n\nfunc (self *chunkStream) Start() {\n\tself.msgdataleft = self.msgdatalen\n\tself.msgdata = make([]byte, self.msgdatalen)\n}\n\nconst (\n\tmsgtypeidUserControl      = 4\n\tmsgtypeidAck              = 3\n\tmsgtypeidWindowAckSize    = 5\n\tmsgtypeidSetPeerBandwidth = 6\n\tmsgtypeidSetChunkSize     = 1\n\tmsgtypeidCommandMsgAMF0   = 20\n\tmsgtypeidCommandMsgAMF3   = 17\n\tmsgtypeidDataMsgAMF0      = 18\n\tmsgtypeidDataMsgAMF3      = 15\n\tmsgtypeidVideoMsg         = 9\n\tmsgtypeidAudioMsg         = 8\n)\n\nconst (\n\teventtypeStreamBegin      = 0\n\teventtypeSetBufferLength  = 3\n\teventtypeStreamIsRecorded = 4\n)\n\nfunc (self *Conn) NetConn() net.Conn {\n\treturn self.netconn\n}\n\nfunc (self *Conn) TxBytes() uint64 {\n\treturn self.txrxcount.txbytes\n}\n\nfunc (self *Conn) RxBytes() uint64 {\n\treturn self.txrxcount.rxbytes\n}\n\nfunc (self *Conn) Close() (err error) {\n\treturn self.netconn.Close()\n}\n\nfunc (self *Conn) pollCommand() (err error) {\n\tfor {\n\t\tif err = self.pollMsg(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.gotcommand {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (self *Conn) pollAVTag() (tag flvio.Tag, err error) {\n\tfor {\n\t\tif err = self.pollMsg(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tswitch self.msgtypeid {\n\t\tcase msgtypeidVideoMsg, msgtypeidAudioMsg:\n\t\t\ttag = self.avtag\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (self *Conn) pollMsg() (err error) {\n\tself.gotmsg = false\n\tself.gotcommand = false\n\tself.datamsgvals = nil\n\tself.avtag = flvio.Tag{}\n\tfor {\n\t\tif err = self.readChunk(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.gotmsg {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc SplitPath(u *url.URL) (app, stream string) {\n\tpathsegs := strings.SplitN(u.RequestURI(), \"/\", 3)\n\tif len(pathsegs) > 1 {\n\t\tapp = pathsegs[1]\n\t}\n\tif len(pathsegs) > 2 {\n\t\tstream = pathsegs[2]\n\t}\n\treturn\n}\n\nfunc getTcUrl(u *url.URL) string {\n\tapp, _ := SplitPath(u)\n\tnu := *u\n\tnu.Path = \"/\" + app\n\treturn nu.String()\n}\n\nfunc createURL(tcurl, app, play string) (u *url.URL) {\n\tps := strings.Split(app+\"/\"+play, \"/\")\n\tout := []string{\"\"}\n\tfor _, s := range ps {\n\t\tif len(s) > 0 {\n\t\t\tout = append(out, s)\n\t\t}\n\t}\n\tif len(out) < 2 {\n\t\tout = append(out, \"\")\n\t}\n\tpath := strings.Join(out, \"/\")\n\tu, _ = url.ParseRequestURI(path)\n\n\tif tcurl != \"\" {\n\t\ttu, _ := url.Parse(tcurl)\n\t\tif tu != nil {\n\t\t\tu.Host = tu.Host\n\t\t\tu.Scheme = tu.Scheme\n\t\t}\n\t}\n\treturn\n}\n\nvar CodecTypes = flv.CodecTypes\n\nfunc (self *Conn) writeBasicConf() (err error) {\n\t// > SetChunkSize\n\tif err = self.writeSetChunkSize(1024 * 1024 * 128); err != nil {\n\t\treturn\n\t}\n\t// > WindowAckSize\n\tif err = self.writeWindowAckSize(5000000); err != nil {\n\t\treturn\n\t}\n\t// > SetPeerBandwidth\n\tif err = self.writeSetPeerBandwidth(5000000, 2); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Conn) readConnect() (err error) {\n\tvar connectpath string\n\n\t// < connect(\"app\")\n\tif err = self.pollCommand(); err != nil {\n\t\treturn\n\t}\n\tif self.commandname != \"connect\" {\n\t\terr = fmt.Errorf(\"rtmp: first command is not connect\")\n\t\treturn\n\t}\n\tif self.commandobj == nil {\n\t\terr = fmt.Errorf(\"rtmp: connect command params invalid\")\n\t\treturn\n\t}\n\n\tvar ok bool\n\tvar _app, _tcurl interface{}\n\tif _app, ok = self.commandobj[\"app\"]; !ok {\n\t\terr = fmt.Errorf(\"rtmp: `connect` params missing `app`\")\n\t\treturn\n\t}\n\tconnectpath, _ = _app.(string)\n\n\tvar tcurl string\n\tif _tcurl, ok = self.commandobj[\"tcUrl\"]; !ok {\n\t\t_tcurl, ok = self.commandobj[\"tcurl\"]\n\t}\n\tif ok {\n\t\ttcurl, _ = _tcurl.(string)\n\t}\n\tconnectparams := self.commandobj\n\n\tif err = self.writeBasicConf(); err != nil {\n\t\treturn\n\t}\n\n\t// > _result(\"NetConnection.Connect.Success\")\n\tif err = self.writeCommandMsg(3, 0, \"_result\", self.commandtransid,\n\t\tflvio.AMFMap{\n\t\t\t\"fmtVer\":       \"FMS/3,0,1,123\",\n\t\t\t\"capabilities\": 31,\n\t\t},\n\t\tflvio.AMFMap{\n\t\t\t\"level\":          \"status\",\n\t\t\t\"code\":           \"NetConnection.Connect.Success\",\n\t\t\t\"description\":    \"Connection succeeded.\",\n\t\t\t\"objectEncoding\": 3,\n\t\t},\n\t); err != nil {\n\t\treturn\n\t}\n\n\tif err = self.flushWrite(); err != nil {\n\t\treturn\n\t}\n\n\tfor {\n\t\tif err = self.pollMsg(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.gotcommand {\n\t\t\tswitch self.commandname {\n\n\t\t\t// < createStream\n\t\t\tcase \"createStream\":\n\t\t\t\tself.avmsgsid = uint32(1)\n\t\t\t\t// > _result(streamid)\n\t\t\t\tif err = self.writeCommandMsg(3, 0, \"_result\", self.commandtransid, nil, self.avmsgsid); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif err = self.flushWrite(); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t// < publish(\"path\")\n\t\t\tcase \"publish\":\n\t\t\t\tif Debug {\n\t\t\t\t\tfmt.Println(\"rtmp: < publish\")\n\t\t\t\t}\n\n\t\t\t\tif len(self.commandparams) < 1 {\n\t\t\t\t\terr = fmt.Errorf(\"rtmp: publish params invalid\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tpublishpath, _ := self.commandparams[0].(string)\n\n\t\t\t\tvar cberr error\n\t\t\t\tif self.OnPlayOrPublish != nil {\n\t\t\t\t\tcberr = self.OnPlayOrPublish(self.commandname, connectparams)\n\t\t\t\t}\n\n\t\t\t\t// > onStatus()\n\t\t\t\tif err = self.writeCommandMsg(5, self.avmsgsid,\n\t\t\t\t\t\"onStatus\", self.commandtransid, nil,\n\t\t\t\t\tflvio.AMFMap{\n\t\t\t\t\t\t\"level\":       \"status\",\n\t\t\t\t\t\t\"code\":        \"NetStream.Publish.Start\",\n\t\t\t\t\t\t\"description\": \"Start publishing\",\n\t\t\t\t\t},\n\t\t\t\t); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif err = self.flushWrite(); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif cberr != nil {\n\t\t\t\t\terr = fmt.Errorf(\"rtmp: OnPlayOrPublish check failed\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tself.URL = createURL(tcurl, connectpath, publishpath)\n\t\t\t\tself.publishing = true\n\t\t\t\tself.reading = true\n\t\t\t\tself.stage++\n\t\t\t\treturn\n\n\t\t\t// < play(\"path\")\n\t\t\tcase \"play\":\n\t\t\t\tif Debug {\n\t\t\t\t\tfmt.Println(\"rtmp: < play\")\n\t\t\t\t}\n\n\t\t\t\tif len(self.commandparams) < 1 {\n\t\t\t\t\terr = fmt.Errorf(\"rtmp: command play params invalid\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tplaypath, _ := self.commandparams[0].(string)\n\n\t\t\t\t// > streamBegin(streamid)\n\t\t\t\tif err = self.writeStreamBegin(self.avmsgsid); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// > onStatus()\n\t\t\t\tif err = self.writeCommandMsg(5, self.avmsgsid,\n\t\t\t\t\t\"onStatus\", self.commandtransid, nil,\n\t\t\t\t\tflvio.AMFMap{\n\t\t\t\t\t\t\"level\":       \"status\",\n\t\t\t\t\t\t\"code\":        \"NetStream.Play.Start\",\n\t\t\t\t\t\t\"description\": \"Start live\",\n\t\t\t\t\t},\n\t\t\t\t); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// > |RtmpSampleAccess()\n\t\t\t\tif err = self.writeDataMsg(5, self.avmsgsid,\n\t\t\t\t\t\"|RtmpSampleAccess\", true, true,\n\t\t\t\t); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif err = self.flushWrite(); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tself.URL = createURL(tcurl, connectpath, playpath)\n\t\t\t\tself.playing = true\n\t\t\t\tself.writing = true\n\t\t\t\tself.stage++\n\t\t\t\treturn\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Conn) checkConnectResult() (ok bool, errmsg string) {\n\tif len(self.commandparams) < 1 {\n\t\terrmsg = \"params length < 1\"\n\t\treturn\n\t}\n\n\tobj, _ := self.commandparams[0].(flvio.AMFMap)\n\tif obj == nil {\n\t\terrmsg = \"params[0] not object\"\n\t\treturn\n\t}\n\n\t_code, _ := obj[\"code\"]\n\tif _code == nil {\n\t\terrmsg = \"code invalid\"\n\t\treturn\n\t}\n\n\tcode, _ := _code.(string)\n\tif code != \"NetConnection.Connect.Success\" {\n\t\terrmsg = \"code != NetConnection.Connect.Success\"\n\t\treturn\n\t}\n\n\tok = true\n\treturn\n}\n\nfunc (self *Conn) checkCreateStreamResult() (ok bool, avmsgsid uint32) {\n\tif len(self.commandparams) < 1 {\n\t\treturn\n\t}\n\n\tok = true\n\t_avmsgsid, _ := self.commandparams[0].(float64)\n\tavmsgsid = uint32(_avmsgsid)\n\treturn\n}\n\nfunc (self *Conn) probe() (err error) {\n\tfor !self.prober.Probed() {\n\t\tvar tag flvio.Tag\n\t\tif tag, err = self.pollAVTag(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = self.prober.PushTag(tag, int32(self.timestamp)); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tself.streams = self.prober.Streams\n\tself.stage++\n\treturn\n}\n\nfunc (self *Conn) writeConnect(path string) (err error) {\n\tif err = self.writeBasicConf(); err != nil {\n\t\treturn\n\t}\n\n\t// > connect(\"app\")\n\tif Debug {\n\t\tfmt.Printf(\"rtmp: > connect('%s') host=%s\\n\", path, self.URL.Host)\n\t}\n\tif err = self.writeCommandMsg(3, 0, \"connect\", 1,\n\t\tflvio.AMFMap{\n\t\t\t\"app\":           path,\n\t\t\t\"flashVer\":      \"MAC 22,0,0,192\",\n\t\t\t\"tcUrl\":         getTcUrl(self.URL),\n\t\t\t\"fpad\":          false,\n\t\t\t\"capabilities\":  15,\n\t\t\t\"audioCodecs\":   4071,\n\t\t\t\"videoCodecs\":   252,\n\t\t\t\"videoFunction\": 1,\n\t\t},\n\t); err != nil {\n\t\treturn\n\t}\n\n\tif err = self.flushWrite(); err != nil {\n\t\treturn\n\t}\n\n\tfor {\n\t\tif err = self.pollMsg(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.gotcommand {\n\t\t\t// < _result(\"NetConnection.Connect.Success\")\n\t\t\tif self.commandname == \"_result\" {\n\t\t\t\tvar ok bool\n\t\t\t\tvar errmsg string\n\t\t\t\tif ok, errmsg = self.checkConnectResult(); !ok {\n\t\t\t\t\terr = fmt.Errorf(\"rtmp: command connect failed: %s\", errmsg)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif Debug {\n\t\t\t\t\tfmt.Printf(\"rtmp: < _result() of connect\\n\")\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else {\n\t\t\tif self.msgtypeid == msgtypeidWindowAckSize {\n\t\t\t\tif len(self.msgdata) == 4 {\n\t\t\t\t\tself.readAckSize = pio.U32BE(self.msgdata)\n\t\t\t\t}\n\t\t\t\tif err = self.writeWindowAckSize(0xffffffff); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Conn) connectPublish() (err error) {\n\tconnectpath, publishpath := SplitPath(self.URL)\n\n\tif err = self.writeConnect(connectpath); err != nil {\n\t\treturn\n\t}\n\n\ttransid := 2\n\n\t// > createStream()\n\tif Debug {\n\t\tfmt.Printf(\"rtmp: > createStream()\\n\")\n\t}\n\tif err = self.writeCommandMsg(3, 0, \"createStream\", transid, nil); err != nil {\n\t\treturn\n\t}\n\ttransid++\n\n\tif err = self.flushWrite(); err != nil {\n\t\treturn\n\t}\n\n\tfor {\n\t\tif err = self.pollMsg(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.gotcommand {\n\t\t\t// < _result(avmsgsid) of createStream\n\t\t\tif self.commandname == \"_result\" {\n\t\t\t\tvar ok bool\n\t\t\t\tif ok, self.avmsgsid = self.checkCreateStreamResult(); !ok {\n\t\t\t\t\terr = fmt.Errorf(\"rtmp: createStream command failed\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// > publish('app')\n\tif Debug {\n\t\tfmt.Printf(\"rtmp: > publish('%s')\\n\", publishpath)\n\t}\n\tif err = self.writeCommandMsg(8, self.avmsgsid, \"publish\", transid, nil, publishpath); err != nil {\n\t\treturn\n\t}\n\ttransid++\n\n\tif err = self.flushWrite(); err != nil {\n\t\treturn\n\t}\n\n\tself.writing = true\n\tself.publishing = true\n\tself.stage++\n\treturn\n}\n\nfunc (self *Conn) connectPlay() (err error) {\n\tconnectpath, playpath := SplitPath(self.URL)\n\n\tif err = self.writeConnect(connectpath); err != nil {\n\t\treturn\n\t}\n\n\t// > createStream()\n\tif Debug {\n\t\tfmt.Printf(\"rtmp: > createStream()\\n\")\n\t}\n\tif err = self.writeCommandMsg(3, 0, \"createStream\", 2, nil); err != nil {\n\t\treturn\n\t}\n\n\t// > SetBufferLength 0,100ms\n\tif err = self.writeSetBufferLength(0, 100); err != nil {\n\t\treturn\n\t}\n\n\tif err = self.flushWrite(); err != nil {\n\t\treturn\n\t}\n\n\tfor {\n\t\tif err = self.pollMsg(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif self.gotcommand {\n\t\t\t// < _result(avmsgsid) of createStream\n\t\t\tif self.commandname == \"_result\" {\n\t\t\t\tvar ok bool\n\t\t\t\tif ok, self.avmsgsid = self.checkCreateStreamResult(); !ok {\n\t\t\t\t\terr = fmt.Errorf(\"rtmp: createStream command failed\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// > play('app')\n\tif Debug {\n\t\tfmt.Printf(\"rtmp: > play('%s')\\n\", playpath)\n\t}\n\tif err = self.writeCommandMsg(8, self.avmsgsid, \"play\", 0, nil, playpath); err != nil {\n\t\treturn\n\t}\n\tif err = self.flushWrite(); err != nil {\n\t\treturn\n\t}\n\n\tself.reading = true\n\tself.playing = true\n\tself.stage++\n\treturn\n}\n\nfunc (self *Conn) ReadPacket() (pkt av.Packet, err error) {\n\tif err = self.prepare(stageCodecDataDone, prepareReading); err != nil {\n\t\treturn\n\t}\n\n\tif !self.prober.Empty() {\n\t\tpkt = self.prober.PopPacket()\n\t\treturn\n\t}\n\n\tfor {\n\t\tvar tag flvio.Tag\n\t\tif tag, err = self.pollAVTag(); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tvar ok bool\n\t\tif pkt, ok = self.prober.TagToPacket(tag, int32(self.timestamp)); ok {\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Conn) Prepare() (err error) {\n\treturn self.prepare(stageCommandDone, 0)\n}\n\nfunc (self *Conn) prepare(stage int, flags int) (err error) {\n\tfor self.stage < stage {\n\t\tswitch self.stage {\n\t\tcase 0:\n\t\t\tif self.isserver {\n\t\t\t\tif err = self.handshakeServer(); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err = self.handshakeClient(); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase stageHandshakeDone:\n\t\t\tif self.isserver {\n\t\t\t\tif err = self.readConnect(); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif flags == prepareReading {\n\t\t\t\t\tif err = self.connectPlay(); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif err = self.connectPublish(); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase stageCommandDone:\n\t\t\tif flags == prepareReading {\n\t\t\t\tif err = self.probe(); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\terr = fmt.Errorf(\"rtmp: call WriteHeader() before WritePacket()\")\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Conn) Streams() (streams []av.CodecData, err error) {\n\tif err = self.prepare(stageCodecDataDone, prepareReading); err != nil {\n\t\treturn\n\t}\n\tstreams = self.streams\n\treturn\n}\n\nfunc (self *Conn) WritePacket(pkt av.Packet) (err error) {\n\tif err = self.prepare(stageCodecDataDone, prepareWriting); err != nil {\n\t\treturn\n\t}\n\n\tstream := self.streams[pkt.Idx]\n\ttag, timestamp := flv.PacketToTag(pkt, stream)\n\n\tif Debug {\n\t\tfmt.Println(\"rtmp: WritePacket\", pkt.Idx, pkt.Time, pkt.CompositionTime)\n\t}\n\n\tif err = self.writeAVTag(tag, int32(timestamp)); err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc (self *Conn) WriteTrailer() (err error) {\n\tif err = self.flushWrite(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Conn) WriteHeader(streams []av.CodecData) (err error) {\n\tif err = self.prepare(stageCommandDone, prepareWriting); err != nil {\n\t\treturn\n\t}\n\n\tvar metadata flvio.AMFMap\n\tif metadata, err = flv.NewMetadataByStreams(streams); err != nil {\n\t\treturn\n\t}\n\n\t// > onMetaData()\n\tif err = self.writeDataMsg(5, self.avmsgsid, \"onMetaData\", metadata); err != nil {\n\t\treturn\n\t}\n\n\t// > Videodata(decoder config)\n\t// > Audiodata(decoder config)\n\tfor _, stream := range streams {\n\t\tvar ok bool\n\t\tvar tag flvio.Tag\n\t\tif tag, ok, err = flv.CodecDataToTag(stream); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif ok {\n\t\t\tif err = self.writeAVTag(tag, 0); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tself.streams = streams\n\tself.stage++\n\treturn\n}\n\nfunc (self *Conn) tmpwbuf(n int) []byte {\n\tif len(self.writebuf) < n {\n\t\tself.writebuf = make([]byte, n)\n\t}\n\treturn self.writebuf\n}\n\nfunc (self *Conn) writeSetChunkSize(size int) (err error) {\n\tself.writeMaxChunkSize = size\n\tb := self.tmpwbuf(chunkHeaderLength + 4)\n\tn := self.fillChunkHeader(b, 2, 0, msgtypeidSetChunkSize, 0, 4)\n\tpio.PutU32BE(b[n:], uint32(size))\n\tn += 4\n\t_, err = self.bufw.Write(b[:n])\n\treturn\n}\n\nfunc (self *Conn) writeAck(seqnum uint32) (err error) {\n\tb := self.tmpwbuf(chunkHeaderLength + 4)\n\tn := self.fillChunkHeader(b, 2, 0, msgtypeidAck, 0, 4)\n\tpio.PutU32BE(b[n:], seqnum)\n\tn += 4\n\t_, err = self.bufw.Write(b[:n])\n\treturn\n}\n\nfunc (self *Conn) writeWindowAckSize(size uint32) (err error) {\n\tb := self.tmpwbuf(chunkHeaderLength + 4)\n\tn := self.fillChunkHeader(b, 2, 0, msgtypeidWindowAckSize, 0, 4)\n\tpio.PutU32BE(b[n:], size)\n\tn += 4\n\t_, err = self.bufw.Write(b[:n])\n\treturn\n}\n\nfunc (self *Conn) writeSetPeerBandwidth(acksize uint32, limittype uint8) (err error) {\n\tb := self.tmpwbuf(chunkHeaderLength + 5)\n\tn := self.fillChunkHeader(b, 2, 0, msgtypeidSetPeerBandwidth, 0, 5)\n\tpio.PutU32BE(b[n:], acksize)\n\tn += 4\n\tb[n] = limittype\n\tn++\n\t_, err = self.bufw.Write(b[:n])\n\treturn\n}\n\nfunc (self *Conn) writeCommandMsg(csid, msgsid uint32, args ...interface{}) (err error) {\n\treturn self.writeAMF0Msg(msgtypeidCommandMsgAMF0, csid, msgsid, args...)\n}\n\nfunc (self *Conn) writeDataMsg(csid, msgsid uint32, args ...interface{}) (err error) {\n\treturn self.writeAMF0Msg(msgtypeidDataMsgAMF0, csid, msgsid, args...)\n}\n\nfunc (self *Conn) writeAMF0Msg(msgtypeid uint8, csid, msgsid uint32, args ...interface{}) (err error) {\n\tsize := 0\n\tfor _, arg := range args {\n\t\tsize += flvio.LenAMF0Val(arg)\n\t}\n\n\tb := self.tmpwbuf(chunkHeaderLength + size)\n\tn := self.fillChunkHeader(b, csid, 0, msgtypeid, msgsid, size)\n\tfor _, arg := range args {\n\t\tn += flvio.FillAMF0Val(b[n:], arg)\n\t}\n\n\t_, err = self.bufw.Write(b[:n])\n\treturn\n}\n\nfunc (self *Conn) writeAVTag(tag flvio.Tag, ts int32) (err error) {\n\tvar msgtypeid uint8\n\tvar csid uint32\n\tvar data []byte\n\n\tswitch tag.Type {\n\tcase flvio.TAG_AUDIO:\n\t\tmsgtypeid = msgtypeidAudioMsg\n\t\tcsid = 6\n\t\tdata = tag.Data\n\n\tcase flvio.TAG_VIDEO:\n\t\tmsgtypeid = msgtypeidVideoMsg\n\t\tcsid = 7\n\t\tdata = tag.Data\n\t}\n\n\tactualChunkHeaderLength := chunkHeaderLength\n\tif uint32(ts) > FlvTimestampMax {\n\t\tactualChunkHeaderLength += 4\n\t}\n\n\tb := self.tmpwbuf(actualChunkHeaderLength + flvio.MaxTagSubHeaderLength)\n\thdrlen := tag.FillHeader(b[actualChunkHeaderLength:])\n\tself.fillChunkHeader(b, csid, ts, msgtypeid, self.avmsgsid, hdrlen+len(data))\n\tn := hdrlen + actualChunkHeaderLength\n\n\tif n+len(data) > self.writeMaxChunkSize {\n\t\tif err = self.writeSetChunkSize(n + len(data)); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif _, err = self.bufw.Write(b[:n]); err != nil {\n\t\treturn\n\t}\n\t_, err = self.bufw.Write(data)\n\treturn\n}\n\nfunc (self *Conn) writeStreamBegin(msgsid uint32) (err error) {\n\tb := self.tmpwbuf(chunkHeaderLength + 6)\n\tn := self.fillChunkHeader(b, 2, 0, msgtypeidUserControl, 0, 6)\n\tpio.PutU16BE(b[n:], eventtypeStreamBegin)\n\tn += 2\n\tpio.PutU32BE(b[n:], msgsid)\n\tn += 4\n\t_, err = self.bufw.Write(b[:n])\n\treturn\n}\n\nfunc (self *Conn) writeSetBufferLength(msgsid uint32, timestamp uint32) (err error) {\n\tb := self.tmpwbuf(chunkHeaderLength + 10)\n\tn := self.fillChunkHeader(b, 2, 0, msgtypeidUserControl, 0, 10)\n\tpio.PutU16BE(b[n:], eventtypeSetBufferLength)\n\tn += 2\n\tpio.PutU32BE(b[n:], msgsid)\n\tn += 4\n\tpio.PutU32BE(b[n:], timestamp)\n\tn += 4\n\t_, err = self.bufw.Write(b[:n])\n\treturn\n}\n\nconst chunkHeaderLength = 12\nconst FlvTimestampMax = 0xFFFFFF\n\nfunc (self *Conn) fillChunkHeader(b []byte, csid uint32, timestamp int32, msgtypeid uint8, msgsid uint32, msgdatalen int) (n int) {\n\t//  0                   1                   2                   3\n\t//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t// |                   timestamp                   |message length |\n\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t// |     message length (cont)     |message type id| msg stream id |\n\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t// |           message stream id (cont)            |\n\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t//\n\t//       Figure 9 Chunk Message Header – Type 0\n\n\tb[n] = byte(csid) & 0x3f\n\tn++\n\tif uint32(timestamp) <= FlvTimestampMax {\n\t\tpio.PutU24BE(b[n:], uint32(timestamp))\n\t} else {\n\t\tpio.PutU24BE(b[n:], FlvTimestampMax)\n\t}\n\tn += 3\n\tpio.PutU24BE(b[n:], uint32(msgdatalen))\n\tn += 3\n\tb[n] = msgtypeid\n\tn++\n\tpio.PutU32LE(b[n:], msgsid)\n\tn += 4\n\tif uint32(timestamp) > FlvTimestampMax {\n\t\tpio.PutU32BE(b[n:], uint32(timestamp))\n\t\tn += 4\n\t}\n\n\tif Debug {\n\t\tfmt.Printf(\"rtmp: write chunk msgdatalen=%d msgsid=%d\\n\", msgdatalen, msgsid)\n\t}\n\n\treturn\n}\n\nfunc (self *Conn) flushWrite() (err error) {\n\tif err = self.bufw.Flush(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Conn) readChunk() (err error) {\n\tb := self.readbuf\n\tn := 0\n\tif _, err = io.ReadFull(self.bufr, b[:1]); err != nil {\n\t\treturn\n\t}\n\theader := b[0]\n\tn += 1\n\n\tvar msghdrtype uint8\n\tvar csid uint32\n\n\tmsghdrtype = header >> 6\n\n\tcsid = uint32(header) & 0x3f\n\tswitch csid {\n\tdefault: // Chunk basic header 1\n\tcase 0: // Chunk basic header 2\n\t\tif _, err = io.ReadFull(self.bufr, b[:1]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn += 1\n\t\tcsid = uint32(b[0]) + 64\n\tcase 1: // Chunk basic header 3\n\t\tif _, err = io.ReadFull(self.bufr, b[:2]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn += 2\n\t\tcsid = uint32(pio.U16BE(b)) + 64\n\t}\n\n\tcs := self.readcsmap[csid]\n\tif cs == nil {\n\t\tcs = &chunkStream{}\n\t\tself.readcsmap[csid] = cs\n\t}\n\n\tvar timestamp uint32\n\n\tswitch msghdrtype {\n\tcase 0:\n\t\t//  0                   1                   2                   3\n\t\t//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t// |                   timestamp                   |message length |\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t// |     message length (cont)     |message type id| msg stream id |\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t// |           message stream id (cont)            |\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t//\n\t\t//       Figure 9 Chunk Message Header – Type 0\n\t\tif cs.msgdataleft != 0 {\n\t\t\terr = fmt.Errorf(\"rtmp: chunk msgdataleft=%d invalid\", cs.msgdataleft)\n\t\t\treturn\n\t\t}\n\t\th := b[:11]\n\t\tif _, err = io.ReadFull(self.bufr, h); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn += len(h)\n\t\ttimestamp = pio.U24BE(h[0:3])\n\t\tcs.msghdrtype = msghdrtype\n\t\tcs.msgdatalen = pio.U24BE(h[3:6])\n\t\tcs.msgtypeid = h[6]\n\t\tcs.msgsid = pio.U32LE(h[7:11])\n\t\tif timestamp == 0xffffff {\n\t\t\tif _, err = io.ReadFull(self.bufr, b[:4]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tn += 4\n\t\t\ttimestamp = pio.U32BE(b)\n\t\t\tcs.hastimeext = true\n\t\t} else {\n\t\t\tcs.hastimeext = false\n\t\t}\n\t\tcs.timenow = timestamp\n\t\tcs.Start()\n\n\tcase 1:\n\t\t//  0                   1                   2                   3\n\t\t//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t// |                timestamp delta                |message length |\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t// |     message length (cont)     |message type id|\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t//\n\t\t//       Figure 10 Chunk Message Header – Type 1\n\t\tif cs.msgdataleft != 0 {\n\t\t\terr = fmt.Errorf(\"rtmp: chunk msgdataleft=%d invalid\", cs.msgdataleft)\n\t\t\treturn\n\t\t}\n\t\th := b[:7]\n\t\tif _, err = io.ReadFull(self.bufr, h); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn += len(h)\n\t\ttimestamp = pio.U24BE(h[0:3])\n\t\tcs.msghdrtype = msghdrtype\n\t\tcs.msgdatalen = pio.U24BE(h[3:6])\n\t\tcs.msgtypeid = h[6]\n\t\tif timestamp == 0xffffff {\n\t\t\tif _, err = io.ReadFull(self.bufr, b[:4]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tn += 4\n\t\t\ttimestamp = pio.U32BE(b)\n\t\t\tcs.hastimeext = true\n\t\t} else {\n\t\t\tcs.hastimeext = false\n\t\t}\n\t\tcs.timedelta = timestamp\n\t\tcs.timenow += timestamp\n\t\tcs.Start()\n\n\tcase 2:\n\t\t//  0                   1                   2\n\t\t//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t// |                timestamp delta                |\n\t\t// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t//\n\t\t//       Figure 11 Chunk Message Header – Type 2\n\t\tif cs.msgdataleft != 0 {\n\t\t\terr = fmt.Errorf(\"rtmp: chunk msgdataleft=%d invalid\", cs.msgdataleft)\n\t\t\treturn\n\t\t}\n\t\th := b[:3]\n\t\tif _, err = io.ReadFull(self.bufr, h); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn += len(h)\n\t\tcs.msghdrtype = msghdrtype\n\t\ttimestamp = pio.U24BE(h[0:3])\n\t\tif timestamp == 0xffffff {\n\t\t\tif _, err = io.ReadFull(self.bufr, b[:4]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tn += 4\n\t\t\ttimestamp = pio.U32BE(b)\n\t\t\tcs.hastimeext = true\n\t\t} else {\n\t\t\tcs.hastimeext = false\n\t\t}\n\t\tcs.timedelta = timestamp\n\t\tcs.timenow += timestamp\n\t\tcs.Start()\n\n\tcase 3:\n\t\tif cs.msgdataleft == 0 {\n\t\t\tswitch cs.msghdrtype {\n\t\t\tcase 0:\n\t\t\t\tif cs.hastimeext {\n\t\t\t\t\tif _, err = io.ReadFull(self.bufr, b[:4]); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tn += 4\n\t\t\t\t\ttimestamp = pio.U32BE(b)\n\t\t\t\t\tcs.timenow = timestamp\n\t\t\t\t}\n\t\t\tcase 1, 2:\n\t\t\t\tif cs.hastimeext {\n\t\t\t\t\tif _, err = io.ReadFull(self.bufr, b[:4]); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tn += 4\n\t\t\t\t\ttimestamp = pio.U32BE(b)\n\t\t\t\t} else {\n\t\t\t\t\ttimestamp = cs.timedelta\n\t\t\t\t}\n\t\t\t\tcs.timenow += timestamp\n\t\t\t}\n\t\t\tcs.Start()\n\t\t}\n\n\tdefault:\n\t\terr = fmt.Errorf(\"rtmp: invalid chunk msg header type=%d\", msghdrtype)\n\t\treturn\n\t}\n\n\tsize := int(cs.msgdataleft)\n\tif size > self.readMaxChunkSize {\n\t\tsize = self.readMaxChunkSize\n\t}\n\toff := cs.msgdatalen - cs.msgdataleft\n\tbuf := cs.msgdata[off : int(off)+size]\n\tif _, err = io.ReadFull(self.bufr, buf); err != nil {\n\t\treturn\n\t}\n\tn += len(buf)\n\tcs.msgdataleft -= uint32(size)\n\n\tif Debug {\n\t\tfmt.Printf(\"rtmp: chunk msgsid=%d msgtypeid=%d msghdrtype=%d len=%d left=%d\\n\",\n\t\t\tcs.msgsid, cs.msgtypeid, cs.msghdrtype, cs.msgdatalen, cs.msgdataleft)\n\t}\n\n\tif cs.msgdataleft == 0 {\n\t\tif Debug {\n\t\t\tfmt.Println(\"rtmp: chunk data\")\n\t\t\tfmt.Print(hex.Dump(cs.msgdata))\n\t\t}\n\n\t\tif err = self.handleMsg(cs.timenow, cs.msgsid, cs.msgtypeid, cs.msgdata); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tself.ackn += uint32(n)\n\tif self.readAckSize != 0 && self.ackn > self.readAckSize {\n\t\tif err = self.writeAck(self.ackn); err != nil {\n\t\t\treturn\n\t\t}\n\t\tself.ackn = 0\n\t}\n\n\treturn\n}\n\nfunc (self *Conn) handleCommandMsgAMF0(b []byte) (n int, err error) {\n\tvar name, transid, obj interface{}\n\tvar size int\n\n\tif name, size, err = flvio.ParseAMF0Val(b[n:]); err != nil {\n\t\treturn\n\t}\n\tn += size\n\tif transid, size, err = flvio.ParseAMF0Val(b[n:]); err != nil {\n\t\treturn\n\t}\n\tn += size\n\tif obj, size, err = flvio.ParseAMF0Val(b[n:]); err != nil {\n\t\treturn\n\t}\n\tn += size\n\n\tvar ok bool\n\tif self.commandname, ok = name.(string); !ok {\n\t\terr = fmt.Errorf(\"rtmp: CommandMsgAMF0 command is not string\")\n\t\treturn\n\t}\n\tself.commandtransid, _ = transid.(float64)\n\tself.commandobj, _ = obj.(flvio.AMFMap)\n\tself.commandparams = []interface{}{}\n\n\tfor n < len(b) {\n\t\tif obj, size, err = flvio.ParseAMF0Val(b[n:]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn += size\n\t\tself.commandparams = append(self.commandparams, obj)\n\t}\n\tif n < len(b) {\n\t\terr = fmt.Errorf(\"rtmp: CommandMsgAMF0 left bytes=%d\", len(b)-n)\n\t\treturn\n\t}\n\n\tself.gotcommand = true\n\treturn\n}\n\nfunc (self *Conn) handleMsg(timestamp uint32, msgsid uint32, msgtypeid uint8, msgdata []byte) (err error) {\n\tself.msgdata = msgdata\n\tself.msgtypeid = msgtypeid\n\tself.timestamp = timestamp\n\n\tswitch msgtypeid {\n\tcase msgtypeidCommandMsgAMF0:\n\t\tif _, err = self.handleCommandMsgAMF0(msgdata); err != nil {\n\t\t\treturn\n\t\t}\n\n\tcase msgtypeidCommandMsgAMF3:\n\t\tif len(msgdata) < 1 {\n\t\t\terr = fmt.Errorf(\"rtmp: short packet of CommandMsgAMF3\")\n\t\t\treturn\n\t\t}\n\t\t// skip first byte\n\t\tif _, err = self.handleCommandMsgAMF0(msgdata[1:]); err != nil {\n\t\t\treturn\n\t\t}\n\n\tcase msgtypeidUserControl:\n\t\tif len(msgdata) < 2 {\n\t\t\terr = fmt.Errorf(\"rtmp: short packet of UserControl\")\n\t\t\treturn\n\t\t}\n\t\tself.eventtype = pio.U16BE(msgdata)\n\n\tcase msgtypeidDataMsgAMF0:\n\t\tb := msgdata\n\t\tn := 0\n\t\tfor n < len(b) {\n\t\t\tvar obj interface{}\n\t\t\tvar size int\n\t\t\tif obj, size, err = flvio.ParseAMF0Val(b[n:]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tn += size\n\t\t\tself.datamsgvals = append(self.datamsgvals, obj)\n\t\t}\n\t\tif n < len(b) {\n\t\t\terr = fmt.Errorf(\"rtmp: DataMsgAMF0 left bytes=%d\", len(b)-n)\n\t\t\treturn\n\t\t}\n\n\tcase msgtypeidVideoMsg:\n\t\tif len(msgdata) == 0 {\n\t\t\treturn\n\t\t}\n\t\ttag := flvio.Tag{Type: flvio.TAG_VIDEO}\n\t\tvar n int\n\t\tif n, err = (&tag).ParseHeader(msgdata); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif !(tag.FrameType == flvio.FRAME_INTER || tag.FrameType == flvio.FRAME_KEY) {\n\t\t\treturn\n\t\t}\n\t\ttag.Data = msgdata[n:]\n\t\tself.avtag = tag\n\n\tcase msgtypeidAudioMsg:\n\t\tif len(msgdata) == 0 {\n\t\t\treturn\n\t\t}\n\t\ttag := flvio.Tag{Type: flvio.TAG_AUDIO}\n\t\tvar n int\n\t\tif n, err = (&tag).ParseHeader(msgdata); err != nil {\n\t\t\treturn\n\t\t}\n\t\ttag.Data = msgdata[n:]\n\t\tself.avtag = tag\n\n\tcase msgtypeidSetChunkSize:\n\t\tif len(msgdata) < 4 {\n\t\t\terr = fmt.Errorf(\"rtmp: short packet of SetChunkSize\")\n\t\t\treturn\n\t\t}\n\t\tself.readMaxChunkSize = int(pio.U32BE(msgdata))\n\t\treturn\n\t}\n\n\tself.gotmsg = true\n\treturn\n}\n\nvar (\n\thsClientFullKey = []byte{\n\t\t'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',\n\t\t'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ',\n\t\t'0', '0', '1',\n\t\t0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1,\n\t\t0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,\n\t\t0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE,\n\t}\n\thsServerFullKey = []byte{\n\t\t'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',\n\t\t'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',\n\t\t'S', 'e', 'r', 'v', 'e', 'r', ' ',\n\t\t'0', '0', '1',\n\t\t0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1,\n\t\t0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,\n\t\t0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE,\n\t}\n\thsClientPartialKey = hsClientFullKey[:30]\n\thsServerPartialKey = hsServerFullKey[:36]\n)\n\nfunc hsMakeDigest(key []byte, src []byte, gap int) (dst []byte) {\n\th := hmac.New(sha256.New, key)\n\tif gap <= 0 {\n\t\th.Write(src)\n\t} else {\n\t\th.Write(src[:gap])\n\t\th.Write(src[gap+32:])\n\t}\n\treturn h.Sum(nil)\n}\n\nfunc hsCalcDigestPos(p []byte, base int) (pos int) {\n\tfor i := 0; i < 4; i++ {\n\t\tpos += int(p[base+i])\n\t}\n\tpos = (pos % 728) + base + 4\n\treturn\n}\n\nfunc hsFindDigest(p []byte, key []byte, base int) int {\n\tgap := hsCalcDigestPos(p, base)\n\tdigest := hsMakeDigest(key, p, gap)\n\tif bytes.Compare(p[gap:gap+32], digest) != 0 {\n\t\treturn -1\n\t}\n\treturn gap\n}\n\nfunc hsParse1(p []byte, peerkey []byte, key []byte) (ok bool, digest []byte) {\n\tvar pos int\n\tif pos = hsFindDigest(p, peerkey, 772); pos == -1 {\n\t\tif pos = hsFindDigest(p, peerkey, 8); pos == -1 {\n\t\t\treturn\n\t\t}\n\t}\n\tok = true\n\tdigest = hsMakeDigest(key, p[pos:pos+32], -1)\n\treturn\n}\n\nfunc hsCreate01(p []byte, time uint32, ver uint32, key []byte) {\n\tp[0] = 3\n\tp1 := p[1:]\n\trand.Read(p1[8:])\n\tpio.PutU32BE(p1[0:4], time)\n\tpio.PutU32BE(p1[4:8], ver)\n\tgap := hsCalcDigestPos(p1, 8)\n\tdigest := hsMakeDigest(key, p1, gap)\n\tcopy(p1[gap:], digest)\n}\n\nfunc hsCreate2(p []byte, key []byte) {\n\trand.Read(p)\n\tgap := len(p) - 32\n\tdigest := hsMakeDigest(key, p, gap)\n\tcopy(p[gap:], digest)\n}\n\nfunc (self *Conn) handshakeClient() (err error) {\n\tvar random [(1 + 1536*2) * 2]byte\n\n\tC0C1C2 := random[:1536*2+1]\n\tC0 := C0C1C2[:1]\n\t//C1 := C0C1C2[1:1536+1]\n\tC0C1 := C0C1C2[:1536+1]\n\tC2 := C0C1C2[1536+1:]\n\n\tS0S1S2 := random[1536*2+1:]\n\t//S0 := S0S1S2[:1]\n\tS1 := S0S1S2[1 : 1536+1]\n\t//S0S1 := S0S1S2[:1536+1]\n\t//S2 := S0S1S2[1536+1:]\n\n\tC0[0] = 3\n\t//hsCreate01(C0C1, hsClientFullKey)\n\n\t// > C0C1\n\tif _, err = self.bufw.Write(C0C1); err != nil {\n\t\treturn\n\t}\n\tif err = self.bufw.Flush(); err != nil {\n\t\treturn\n\t}\n\n\t// < S0S1S2\n\tif _, err = io.ReadFull(self.bufr, S0S1S2); err != nil {\n\t\treturn\n\t}\n\n\tif Debug {\n\t\tfmt.Println(\"rtmp: handshakeClient: server version\", S1[4], S1[5], S1[6], S1[7])\n\t}\n\n\tif ver := pio.U32BE(S1[4:8]); ver != 0 {\n\t\tC2 = S1\n\t} else {\n\t\tC2 = S1\n\t}\n\n\t// > C2\n\tif _, err = self.bufw.Write(C2); err != nil {\n\t\treturn\n\t}\n\n\tself.stage++\n\treturn\n}\n\nfunc (self *Conn) handshakeServer() (err error) {\n\tvar random [(1 + 1536*2) * 2]byte\n\n\tC0C1C2 := random[:1536*2+1]\n\tC0 := C0C1C2[:1]\n\tC1 := C0C1C2[1 : 1536+1]\n\tC0C1 := C0C1C2[:1536+1]\n\tC2 := C0C1C2[1536+1:]\n\n\tS0S1S2 := random[1536*2+1:]\n\tS0 := S0S1S2[:1]\n\tS1 := S0S1S2[1 : 1536+1]\n\tS0S1 := S0S1S2[:1536+1]\n\tS2 := S0S1S2[1536+1:]\n\n\t// < C0C1\n\tif _, err = io.ReadFull(self.bufr, C0C1); err != nil {\n\t\treturn\n\t}\n\tif C0[0] != 3 {\n\t\terr = fmt.Errorf(\"rtmp: handshake version=%d invalid\", C0[0])\n\t\treturn\n\t}\n\n\tS0[0] = 3\n\n\tclitime := pio.U32BE(C1[0:4])\n\tsrvtime := clitime\n\tsrvver := uint32(0x0d0e0a0d)\n\tcliver := pio.U32BE(C1[4:8])\n\n\tif cliver != 0 {\n\t\tvar ok bool\n\t\tvar digest []byte\n\t\tif ok, digest = hsParse1(C1, hsClientPartialKey, hsServerFullKey); !ok {\n\t\t\terr = fmt.Errorf(\"rtmp: handshake server: C1 invalid\")\n\t\t\treturn\n\t\t}\n\t\thsCreate01(S0S1, srvtime, srvver, hsServerPartialKey)\n\t\thsCreate2(S2, digest)\n\t} else {\n\t\tcopy(S1, C1)\n\t\tcopy(S2, C2)\n\t}\n\n\t// > S0S1S2\n\tif _, err = self.bufw.Write(S0S1S2); err != nil {\n\t\treturn\n\t}\n\tif err = self.bufw.Flush(); err != nil {\n\t\treturn\n\t}\n\n\t// < C2\n\tif _, err = io.ReadFull(self.bufr, C2); err != nil {\n\t\treturn\n\t}\n\n\tself.stage++\n\treturn\n}\n\ntype closeConn struct {\n\t*Conn\n\twaitclose chan bool\n}\n\nfunc (self closeConn) Close() error {\n\tself.waitclose <- true\n\treturn nil\n}\n\nfunc Handler(h *avutil.RegisterHandler) {\n\th.UrlDemuxer = func(uri string) (ok bool, demuxer av.DemuxCloser, err error) {\n\t\tif !strings.HasPrefix(uri, \"rtmp://\") {\n\t\t\treturn\n\t\t}\n\t\tok = true\n\t\tdemuxer, err = Dial(uri)\n\t\treturn\n\t}\n\n\th.UrlMuxer = func(uri string) (ok bool, muxer av.MuxCloser, err error) {\n\t\tif !strings.HasPrefix(uri, \"rtmp://\") {\n\t\t\treturn\n\t\t}\n\t\tok = true\n\t\tmuxer, err = Dial(uri)\n\t\treturn\n\t}\n\n\th.ServerMuxer = func(uri string) (ok bool, muxer av.MuxCloser, err error) {\n\t\tif !strings.HasPrefix(uri, \"rtmp://\") {\n\t\t\treturn\n\t\t}\n\t\tok = true\n\n\t\tvar u *url.URL\n\t\tif u, err = ParseURL(uri); err != nil {\n\t\t\treturn\n\t\t}\n\t\tserver := &Server{\n\t\t\tAddr: u.Host,\n\t\t}\n\n\t\twaitstart := make(chan error)\n\t\twaitconn := make(chan *Conn)\n\t\twaitclose := make(chan bool)\n\n\t\tserver.HandlePlay = func(conn *Conn) {\n\t\t\twaitconn <- conn\n\t\t\t<-waitclose\n\t\t}\n\n\t\tgo func() {\n\t\t\twaitstart <- server.ListenAndServe()\n\t\t}()\n\n\t\tselect {\n\t\tcase err = <-waitstart:\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase conn := <-waitconn:\n\t\t\tmuxer = closeConn{Conn: conn, waitclose: waitclose}\n\t\t\treturn\n\t\t}\n\n\t\treturn\n\t}\n\n\th.ServerDemuxer = func(uri string) (ok bool, demuxer av.DemuxCloser, err error) {\n\t\tif !strings.HasPrefix(uri, \"rtmp://\") {\n\t\t\treturn\n\t\t}\n\t\tok = true\n\n\t\tvar u *url.URL\n\t\tif u, err = ParseURL(uri); err != nil {\n\t\t\treturn\n\t\t}\n\t\tserver := &Server{\n\t\t\tAddr: u.Host,\n\t\t}\n\n\t\twaitstart := make(chan error)\n\t\twaitconn := make(chan *Conn)\n\t\twaitclose := make(chan bool)\n\n\t\tserver.HandlePublish = func(conn *Conn) {\n\t\t\twaitconn <- conn\n\t\t\t<-waitclose\n\t\t}\n\n\t\tgo func() {\n\t\t\twaitstart <- server.ListenAndServe()\n\t\t}()\n\n\t\tselect {\n\t\tcase err = <-waitstart:\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase conn := <-waitconn:\n\t\t\tdemuxer = closeConn{Conn: conn, waitclose: waitclose}\n\t\t\treturn\n\t\t}\n\n\t\treturn\n\t}\n\n\th.CodecTypes = CodecTypes\n}\n"
  },
  {
    "path": "format/rtsp/client.go",
    "content": "package rtsp\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/md5\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/avutil\"\n\t\"github.com/nareix/joy4/codec\"\n\t\"github.com/nareix/joy4/codec/aacparser\"\n\t\"github.com/nareix/joy4/codec/h264parser\"\n\t\"github.com/nareix/joy4/format/rtsp/sdp\"\n\t\"io\"\n\t\"net\"\n\t\"net/textproto\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar ErrCodecDataChange = fmt.Errorf(\"rtsp: codec data change, please call HandleCodecDataChange()\")\n\nvar DebugRtp = false\nvar DebugRtsp = false\nvar SkipErrRtpBlock = false\n\nconst (\n\tstageDescribeDone = iota+1\n\tstageSetupDone\n\tstageWaitCodecData\n\tstageCodecDataDone\n)\n\ntype Client struct {\n\tDebugRtsp bool\n\tDebugRtp bool\n\tHeaders   []string\n\n\tSkipErrRtpBlock bool\n\n\tRtspTimeout          time.Duration\n\tRtpTimeout           time.Duration\n\tRtpKeepAliveTimeout  time.Duration\n\trtpKeepaliveTimer    time.Time\n\trtpKeepaliveEnterCnt int\n\n\tstage int\n\n\tsetupIdx    []int\n\tsetupMap    []int\n\n\tauthHeaders func(method string) []string\n\n\turl        *url.URL\n\tconn       *connWithTimeout\n\tbrconn      *bufio.Reader\n\trequestUri string\n\tcseq       uint\n\tstreams    []*Stream\n\tstreamsintf []av.CodecData\n\tsession    string\n\tbody       io.Reader\n}\n\ntype Request struct {\n\tHeader []string\n\tUri    string\n\tMethod string\n}\n\ntype Response struct {\n\tStatusCode    int\n\tHeaders        textproto.MIMEHeader\n\tContentLength int\n\tBody          []byte\n\n\tBlock []byte\n}\n\nfunc DialTimeout(uri string, timeout time.Duration) (self *Client, err error) {\n\tvar URL *url.URL\n\tif URL, err = url.Parse(uri); err != nil {\n\t\treturn\n\t}\n\n\tif _, _, err := net.SplitHostPort(URL.Host); err != nil {\n\t\tURL.Host = URL.Host + \":554\"\n\t}\n\n\tdailer := net.Dialer{Timeout: timeout}\n\tvar conn net.Conn\n\tif conn, err = dailer.Dial(\"tcp\", URL.Host); err != nil {\n\t\treturn\n\t}\n\n\tu2 := *URL\n\tu2.User = nil\n\n\tconnt := &connWithTimeout{Conn: conn}\n\n\tself = &Client{\n\t\tconn:       connt,\n\t\tbrconn:     bufio.NewReaderSize(connt, 256),\n\t\turl:        URL,\n\t\trequestUri: u2.String(),\n\t\tDebugRtp:   DebugRtp,\n\t\tDebugRtsp:  DebugRtsp,\n\t\tSkipErrRtpBlock: SkipErrRtpBlock,\n\t}\n\treturn\n}\n\nfunc Dial(uri string) (self *Client, err error) {\n\treturn DialTimeout(uri, 0)\n}\n\nfunc (self *Client) allCodecDataReady() bool {\n\tfor _, si:= range self.setupIdx {\n\t\tstream := self.streams[si]\n\t\tif stream.CodecData == nil {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (self *Client) probe() (err error) {\n\tfor {\n\t\tif self.allCodecDataReady() {\n\t\t\tbreak\n\t\t}\n\t\tif _, err = self.readPacket(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tself.stage = stageCodecDataDone\n\treturn\n}\n\nfunc (self *Client) prepare(stage int) (err error) {\n\tfor self.stage < stage {\n\t\tswitch self.stage {\n\t\tcase 0:\n\t\t\tif _, err = self.Describe(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase stageDescribeDone:\n\t\t\tif err = self.SetupAll(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase stageSetupDone:\n\t\t\tif err = self.Play(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase stageWaitCodecData:\n\t\t\tif err = self.probe(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Client) Streams() (streams []av.CodecData, err error) {\n\tif err = self.prepare(stageCodecDataDone); err != nil {\n\t\treturn\n\t}\n\tfor _, si := range self.setupIdx {\n\t\tstream := self.streams[si]\n\t\tstreams = append(streams, stream.CodecData)\n\t}\n\treturn\n}\n\nfunc (self *Client) SendRtpKeepalive() (err error) {\n\tif self.RtpKeepAliveTimeout > 0 {\n\t\tif self.rtpKeepaliveTimer.IsZero() {\n\t\t\tself.rtpKeepaliveTimer = time.Now()\n\t\t} else if time.Now().Sub(self.rtpKeepaliveTimer) > self.RtpKeepAliveTimeout {\n\t\t\tself.rtpKeepaliveTimer = time.Now()\n\t\t\tif self.DebugRtsp {\n\t\t\t\tfmt.Println(\"rtp: keep alive\")\n\t\t\t}\n\t\t\treq := Request{\n\t\t\t\tMethod: \"OPTIONS\",\n\t\t\t\tUri:    self.requestUri,\n\t\t\t}\n\t\t\tif err = self.WriteRequest(req); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Client) WriteRequest(req Request) (err error) {\n\tself.conn.Timeout = self.RtspTimeout\n\tself.cseq++\n\n\tbuf := &bytes.Buffer{}\n\n\tfmt.Fprintf(buf, \"%s %s RTSP/1.0\\r\\n\", req.Method, req.Uri)\n\tfmt.Fprintf(buf, \"CSeq: %d\\r\\n\", self.cseq)\n\n\tif self.authHeaders != nil {\n\t\theaders := self.authHeaders(req.Method)\n\t\tfor _, s := range headers {\n\t\t\tio.WriteString(buf, s)\n\t\t\tio.WriteString(buf, \"\\r\\n\")\n\t\t}\n\t}\n\tfor _, s := range req.Header {\n\t\tio.WriteString(buf, s)\n\t\tio.WriteString(buf, \"\\r\\n\")\n\t}\n\tfor _, s := range self.Headers {\n\t\tio.WriteString(buf, s)\n\t\tio.WriteString(buf, \"\\r\\n\")\n\t}\n\tio.WriteString(buf, \"\\r\\n\")\n\n\tbufout := buf.Bytes()\n\n\tif self.DebugRtsp {\n\t\tfmt.Print(\"> \", string(bufout))\n\t}\n\n\tif _, err = self.conn.Write(bufout); err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc (self *Client) parseBlockHeader(h []byte) (length int, no int, valid bool) {\n\tlength = int(h[2])<<8 + int(h[3])\n\tno = int(h[1])\n\tif no/2 >= len(self.streams) {\n\t\treturn\n\t}\n\n\tif no%2 == 0 { // rtp\n\t\tif length < 8 {\n\t\t\treturn\n\t\t}\n\n\t\t// V=2\n\t\tif h[4]&0xc0 != 0x80 {\n\t\t\treturn\n\t\t}\n\n\t\tstream := self.streams[no/2]\n\t\tif int(h[5]&0x7f) != stream.Sdp.PayloadType {\n\t\t\treturn\n\t\t}\n\n\t\ttimestamp := binary.BigEndian.Uint32(h[8:12])\n\t\tif stream.firsttimestamp != 0 {\n\t\t\ttimestamp -= stream.firsttimestamp\n\t\t\tif timestamp < stream.timestamp {\n\t\t\t\treturn\n\t\t\t} else if timestamp - stream.timestamp > uint32(stream.timeScale()*60*60) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t} else { // rtcp\n\t}\n\n\tvalid = true\n\treturn\n}\n\nfunc (self *Client) parseHeaders(b []byte) (statusCode int, headers textproto.MIMEHeader, err error) {\n\tvar line string\n\tr := textproto.NewReader(bufio.NewReader(bytes.NewReader(b)))\n\tif line, err = r.ReadLine(); err != nil {\n\t\terr = fmt.Errorf(\"rtsp: header invalid\")\n\t\treturn\n\t}\n\n\tif codes := strings.Split(line, \" \"); len(codes) >= 2 {\n\t\tif statusCode, err = strconv.Atoi(codes[1]); err != nil {\n\t\t\terr = fmt.Errorf(\"rtsp: header invalid: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\theaders, _ = r.ReadMIMEHeader()\n\treturn\n}\n\nfunc (self *Client) handleResp(res *Response) (err error) {\n\tif sess := res.Headers.Get(\"Session\"); sess != \"\" && self.session == \"\" {\n\t\tif fields := strings.Split(sess, \";\"); len(fields) > 0 {\n\t\t\tself.session = fields[0]\n\t\t}\n\t}\n\tif res.StatusCode == 401 {\n\t\tif err = self.handle401(res); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Client) handle401(res *Response) (err error) {\n\t/*\n\t\tRTSP/1.0 401 Unauthorized\n\t\tCSeq: 2\n\t\tDate: Wed, May 04 2016 10:10:51 GMT\n\t\tWWW-Authenticate: Digest realm=\"LIVE555 Streaming Media\", nonce=\"c633aaf8b83127633cbe98fac1d20d87\"\n\t*/\n\tauthval := res.Headers.Get(\"WWW-Authenticate\")\n\thdrval := strings.SplitN(authval, \" \", 2)\n\tvar realm, nonce string\n\n\tif len(hdrval) == 2 {\n\t\tfor _, field := range strings.Split(hdrval[1], \",\") {\n\t\t\tfield = strings.Trim(field, \", \")\n\t\t\tif keyval := strings.Split(field, \"=\"); len(keyval) == 2 {\n\t\t\t\tkey := keyval[0]\n\t\t\t\tval := strings.Trim(keyval[1], `\"`)\n\t\t\t\tswitch key {\n\t\t\t\tcase \"realm\":\n\t\t\t\t\trealm = val\n\t\t\t\tcase \"nonce\":\n\t\t\t\t\tnonce = val\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif realm != \"\" {\n\t\t\tvar username string\n\t\t\tvar password string\n\n\t\t\tif self.url.User == nil {\n\t\t\t\terr = fmt.Errorf(\"rtsp: no username\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tusername = self.url.User.Username()\n\t\t\tpassword, _ = self.url.User.Password()\n\n\t\t\tself.authHeaders = func(method string) []string {\n\t\t\t\tvar headers []string\n\t\t\t\tif nonce == \"\" {\n\t\t\t\t\theaders = []string{\n\t\t\t\t\t\tfmt.Sprintf(`Authorization: Basic %s`, base64.StdEncoding.EncodeToString([]byte(username+\":\"+password))),\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ths1 := md5hash(username + \":\" + realm + \":\" + password)\n\t\t\t\t\ths2 := md5hash(method + \":\" + self.requestUri)\n\t\t\t\t\tresponse := md5hash(hs1 + \":\" + nonce + \":\" + hs2)\n\t\t\t\t\theaders = []string{fmt.Sprintf(\n\t\t\t\t\t\t`Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"`,\n\t\t\t\t\t\tusername, realm, nonce, self.requestUri, response)}\n\t\t\t\t}\n\t\t\t\treturn headers\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Client) findRTSP() (block []byte, data []byte, err error) {\n\tconst (\n\t\tR = iota+1\n\t\tT\n\t\tS\n\t\tHeader\n\t\tDollar\n\t)\n\tvar _peek [8]byte\n\tpeek := _peek[0:0]\n\tstat := 0\n\n\tfor i := 0;; i++ {\n\t\tvar b byte\n\t\tif b, err = self.brconn.ReadByte(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tswitch b {\n\t\tcase 'R':\n\t\t\tif stat == 0 {\n\t\t\t\tstat = R\n\t\t\t}\n\t\tcase 'T':\n\t\t\tif stat == R {\n\t\t\t\tstat = T\n\t\t\t}\n\t\tcase 'S':\n\t\t\tif stat == T {\n\t\t\t\tstat = S\n\t\t\t}\n\t\tcase 'P':\n\t\t\tif stat == S {\n\t\t\t\tstat = Header\n\t\t\t}\n\t\tcase '$':\n\t\t\tif stat != Dollar {\n\t\t\t\tstat = Dollar\n\t\t\t\tpeek = _peek[0:0]\n\t\t\t}\n\t\tdefault:\n\t\t\tif stat != Dollar {\n\t\t\t\tstat = 0\n\t\t\t\tpeek = _peek[0:0]\n\t\t\t}\n\t\t}\n\n\t\tif false && self.DebugRtp {\n\t\t\tfmt.Println(\"rtsp: findRTSP\", i, b)\n\t\t}\n\n\t\tif stat != 0 {\n\t\t\tpeek = append(peek, b)\n\t\t}\n\t\tif stat == Header {\n\t\t\tdata = peek\n\t\t\treturn\n\t\t}\n\n\t\tif stat == Dollar && len(peek) >= 12 {\n\t\t\tif self.DebugRtp {\n\t\t\t\tfmt.Println(\"rtsp: dollar at\", i, len(peek))\n\t\t\t}\n\t\t\tif blocklen, _, ok := self.parseBlockHeader(peek); ok {\n\t\t\t\tleft := blocklen+4-len(peek)\n\t\t\t\tblock = append(peek, make([]byte, left)...)\n\t\t\t\tif _, err = io.ReadFull(self.brconn, block[len(peek):]); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstat = 0\n\t\t\tpeek = _peek[0:0]\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Client) readLFLF() (block []byte, data []byte, err error) {\n\tconst (\n\t\tLF = iota+1\n\t\tLFLF\n\t)\n\tpeek := []byte{}\n\tstat := 0\n\tdollarpos := -1\n\tlpos := 0\n\tpos := 0\n\n\tfor {\n\t\tvar b byte\n\t\tif b, err = self.brconn.ReadByte(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tswitch b {\n\t\tcase '\\n':\n\t\t\tif stat == 0 {\n\t\t\t\tstat = LF\n\t\t\t\tlpos = pos\n\t\t\t} else if stat == LF {\n\t\t\t\tif pos - lpos <= 2 {\n\t\t\t\t\tstat = LFLF\n\t\t\t\t} else {\n\t\t\t\t\tlpos = pos\n\t\t\t\t}\n\t\t\t}\n\t\tcase '$':\n\t\t\tdollarpos = pos\n\t\t}\n\t\tpeek = append(peek, b)\n\n\t\tif stat == LFLF {\n\t\t\tdata = peek\n\t\t\treturn\n\t\t} else if dollarpos != -1 && dollarpos - pos >= 12 {\n\t\t\thdrlen := dollarpos-pos\n\t\t\tstart := len(peek)-hdrlen\n\t\t\tif blocklen, _, ok := self.parseBlockHeader(peek[start:]); ok {\n\t\t\t\tblock = append(peek[start:], make([]byte, blocklen+4-hdrlen)...)\n\t\t\t\tif _, err = io.ReadFull(self.brconn, block[hdrlen:]); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdollarpos = -1\n\t\t}\n\n\t\tpos++\n\t}\n\n\treturn\n}\n\nfunc (self *Client) readResp(b []byte) (res Response, err error) {\n\tif res.StatusCode, res.Headers, err = self.parseHeaders(b); err != nil {\n\t\treturn\n\t}\n\tres.ContentLength, _ = strconv.Atoi(res.Headers.Get(\"Content-Length\"))\n\tif res.ContentLength > 0 {\n\t\tres.Body = make([]byte, res.ContentLength)\n\t\tif _, err = io.ReadFull(self.brconn, res.Body); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif err = self.handleResp(&res); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Client) poll() (res Response, err error) {\n\tvar block []byte\n\tvar rtsp []byte\n\tvar headers []byte\n\n\tself.conn.Timeout = self.RtspTimeout\n\tfor {\n\t\tif block, rtsp, err = self.findRTSP(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif len(block) > 0 {\n\t\t\tres.Block = block\n\t\t\treturn\n\t\t} else {\n\t\t\tif block, headers, err = self.readLFLF(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(block) > 0 {\n\t\t\t\tres.Block = block\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif res, err = self.readResp(append(rtsp, headers...)); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc (self *Client) ReadResponse() (res Response, err error) {\n\tfor {\n\t\tif res, err = self.poll(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif res.StatusCode > 0 {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Client) SetupAll() (err error) {\n\tidx := []int{}\n\tfor i := range self.streams {\n\t\tidx = append(idx, i)\n\t}\n\treturn self.Setup(idx)\n}\n\nfunc (self *Client) Setup(idx []int) (err error) {\n\tif err = self.prepare(stageDescribeDone); err != nil {\n\t\treturn\n\t}\n\n\tself.setupMap = make([]int, len(self.streams))\n\tfor i := range self.setupMap {\n\t\tself.setupMap[i] = -1\n\t}\n\tself.setupIdx = idx\n\n\tfor i, si := range idx {\n\t\tself.setupMap[si] = i\n\n\t\turi := \"\"\n\t\tcontrol := self.streams[si].Sdp.Control\n\t\tif strings.HasPrefix(control, \"rtsp://\") {\n\t\t\turi = control\n\t\t} else {\n\t\t\turi = self.requestUri + \"/\" + control\n\t\t}\n\t\treq := Request{Method: \"SETUP\", Uri: uri}\n\t\treq.Header = append(req.Header, fmt.Sprintf(\"Transport: RTP/AVP/TCP;unicast;interleaved=%d-%d\", si*2, si*2+1))\n\t\tif self.session != \"\" {\n\t\t\treq.Header = append(req.Header, \"Session: \"+self.session)\n\t\t}\n\t\tif err = self.WriteRequest(req); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif _, err = self.ReadResponse(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif self.stage == stageDescribeDone {\n\t\tself.stage = stageSetupDone\n\t}\n\treturn\n}\n\nfunc md5hash(s string) string {\n\th := md5.Sum([]byte(s))\n\treturn hex.EncodeToString(h[:])\n}\n\nfunc (self *Client) Describe() (streams []sdp.Media, err error) {\n\tvar res Response\n\n\tfor i := 0; i < 2; i++ {\n\t\treq := Request{\n\t\t\tMethod: \"DESCRIBE\",\n\t\t\tUri:    self.requestUri,\n\t\t\tHeader: []string{\"Accept: application/sdp\"},\n\t\t}\n\t\tif err = self.WriteRequest(req); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif res, err = self.ReadResponse(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif res.StatusCode == 200 {\n\t\t\tbreak\n\t\t}\n\t}\n\tif res.ContentLength == 0 {\n\t\terr = fmt.Errorf(\"rtsp: Describe failed, StatusCode=%d\", res.StatusCode)\n\t\treturn\n\t}\n\n\tbody := string(res.Body)\n\n\tif self.DebugRtsp {\n\t\tfmt.Println(\"<\", body)\n\t}\n\n\t_, medias := sdp.Parse(body)\n\n\tself.streams = []*Stream{}\n\tfor _, media := range medias {\n\t\tstream := &Stream{Sdp: media, client: self}\n\t\tstream.makeCodecData()\n\t\tself.streams = append(self.streams, stream)\n\t\tstreams = append(streams, media)\n\t}\n\n\tif self.stage == 0 {\n\t\tself.stage = stageDescribeDone\n\t}\n\treturn\n}\n\nfunc (self *Client) Options() (err error) {\n\treq := Request{\n\t\tMethod: \"OPTIONS\",\n\t\tUri:    self.requestUri,\n\t}\n\tif self.session != \"\" {\n\t\treq.Header = append(req.Header, \"Session: \"+self.session)\n\t}\n\tif err = self.WriteRequest(req); err != nil {\n\t\treturn\n\t}\n\tif _, err = self.ReadResponse(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Client) HandleCodecDataChange() (_newcli *Client, err error) {\n\tnewcli := &Client{}\n\t*newcli = *self\n\n\tnewcli.streams = []*Stream{}\n\tfor _, stream := range self.streams {\n\t\tnewstream := &Stream{}\n\t\t*newstream = *stream\n\t\tnewstream.client = newcli\n\n\t\tif newstream.isCodecDataChange() {\n\t\t\tif err = newstream.makeCodecData(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tnewstream.clearCodecDataChange()\n\t\t}\n\t\tnewcli.streams = append(newcli.streams, newstream)\n\t}\n\n\t_newcli = newcli\n\treturn\n}\n\nfunc (self *Stream) clearCodecDataChange() {\n\tself.spsChanged = false\n\tself.ppsChanged = false\n}\n\nfunc (self *Stream) isCodecDataChange() bool {\n\tif self.spsChanged && self.ppsChanged {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (self *Stream) timeScale() int {\n\tt := self.Sdp.TimeScale\n\tif t == 0 {\n\t\t// https://tools.ietf.org/html/rfc5391\n\t\tt = 8000\n\t}\n\treturn t\n}\n\nfunc (self *Stream) makeCodecData() (err error) {\n\tmedia := self.Sdp\n\n\tif media.PayloadType >= 96 && media.PayloadType <= 127 {\n\t\tswitch media.Type {\n\t\tcase av.H264:\n\t\t\tfor _, nalu := range media.SpropParameterSets {\n\t\t\t\tif len(nalu) > 0 {\n\t\t\t\t\tself.handleH264Payload(0, nalu)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(self.sps) == 0 || len(self.pps) == 0 {\n\t\t\t\tif nalus, typ := h264parser.SplitNALUs(media.Config); typ != h264parser.NALU_RAW {\n\t\t\t\t\tfor _, nalu := range nalus {\n\t\t\t\t\t\tif len(nalu) > 0 {\n\t\t\t\t\t\t\tself.handleH264Payload(0, nalu)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(self.sps) > 0 && len(self.pps) > 0 {\n\t\t\t\tif self.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(self.sps, self.pps); err != nil {\n\t\t\t\t\terr = fmt.Errorf(\"rtsp: h264 sps/pps invalid: %s\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\terr = fmt.Errorf(\"rtsp: missing h264 sps or pps\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase av.AAC:\n\t\t\tif len(media.Config) == 0 {\n\t\t\t\terr = fmt.Errorf(\"rtsp: aac sdp config missing\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif self.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(media.Config); err != nil {\n\t\t\t\terr = fmt.Errorf(\"rtsp: aac sdp config invalid: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t} else {\n\t\tswitch media.PayloadType {\n\t\tcase 0:\n\t\t\tself.CodecData = codec.NewPCMMulawCodecData()\n\n\t\tcase 8:\n\t\t\tself.CodecData = codec.NewPCMAlawCodecData()\n\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"rtsp: PayloadType=%d unsupported\", media.PayloadType)\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Stream) handleBuggyAnnexbH264Packet(timestamp uint32, packet []byte) (isBuggy bool, err error) {\n\tif len(packet) >= 4 && packet[0] == 0 && packet[1] == 0 && packet[2] == 0 && packet[3] == 1 {\n\t\tisBuggy = true\n\t\tif nalus, typ := h264parser.SplitNALUs(packet); typ != h264parser.NALU_RAW {\n\t\t\tfor _, nalu := range nalus {\n\t\t\t\tif len(nalu) > 0 {\n\t\t\t\t\tif err = self.handleH264Payload(timestamp, nalu); err != nil {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Stream) handleH264Payload(timestamp uint32, packet []byte) (err error) {\n\tif len(packet) < 2 {\n\t\terr = fmt.Errorf(\"rtp: h264 packet too short\")\n\t\treturn\n\t}\n\n\tvar isBuggy bool\n\tif isBuggy, err = self.handleBuggyAnnexbH264Packet(timestamp, packet); isBuggy {\n\t\treturn\n\t}\n\n\tnaluType := packet[0]&0x1f\n\n\t/*\n\t\tTable 7-1 – NAL unit type codes\n\t\t1   ￼Coded slice of a non-IDR picture\n\t\t5    Coded slice of an IDR picture\n\t\t6    Supplemental enhancement information (SEI)\n\t\t7    Sequence parameter set\n\t\t8    Picture parameter set\n\t\t1-23     NAL unit  Single NAL unit packet             5.6\n\t\t24       STAP-A    Single-time aggregation packet     5.7.1\n\t\t25       STAP-B    Single-time aggregation packet     5.7.1\n\t\t26       MTAP16    Multi-time aggregation packet      5.7.2\n\t\t27       MTAP24    Multi-time aggregation packet      5.7.2\n\t\t28       FU-A      Fragmentation unit                 5.8\n\t\t29       FU-B      Fragmentation unit                 5.8\n\t\t30-31    reserved                                     -\n\t*/\n\tswitch {\n\tcase naluType >= 1 && naluType <= 5:\n\t\t\tif naluType == 5 {\n\t\t\t\tself.pkt.IsKeyFrame = true\n\t\t\t}\n\t\t\tself.gotpkt = true\n\t\t\t// raw nalu to avcc\n\t\t\tb := make([]byte, 4+len(packet))\n\t\t\tpio.PutU32BE(b[0:4], uint32(len(packet)))\n\t\t\tcopy(b[4:], packet)\n\t\t\tself.pkt.Data = b\n\t\t\tself.timestamp = timestamp\n\n\tcase naluType == 7: // sps\n\t\tif self.client != nil && self.client.DebugRtp {\n\t\t\tfmt.Println(\"rtsp: got sps\")\n\t\t}\n\t\tif len(self.sps) == 0 {\n\t\t\tself.sps = packet\n\t\t\tself.makeCodecData()\n\t\t} else if bytes.Compare(self.sps, packet) != 0 {\n\t\t\tself.spsChanged = true\n\t\t\tself.sps = packet\n\t\t\tif self.client != nil && self.client.DebugRtp {\n\t\t\t\tfmt.Println(\"rtsp: sps changed\")\n\t\t\t}\n\t\t}\n\n\tcase naluType == 8: // pps\n\t\tif self.client != nil && self.client.DebugRtp {\n\t\t\tfmt.Println(\"rtsp: got pps\")\n\t\t}\n\t\tif len(self.pps) == 0 {\n\t\t\tself.pps = packet\n\t\t\tself.makeCodecData()\n\t\t} else if bytes.Compare(self.pps, packet) != 0 {\n\t\t\tself.ppsChanged = true\n\t\t\tself.pps = packet\n\t\t\tif self.client != nil && self.client.DebugRtp {\n\t\t\t\tfmt.Println(\"rtsp: pps changed\")\n\t\t\t}\n\t\t}\n\n\tcase naluType == 28: // FU-A\n\t\t/*\n\t\t\t0                   1                   2                   3\n\t\t\t0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n\t\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t\t| FU indicator  |   FU header   |                               |\n\t\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |\n\t\t\t|                                                               |\n\t\t\t|                         FU payload                            |\n\t\t\t|                                                               |\n\t\t\t|                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t\t|                               :...OPTIONAL RTP padding        |\n\t\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t\tFigure 14.  RTP payload format for FU-A\n\n\t\t\tThe FU indicator octet has the following format:\n\t\t\t+---------------+\n\t\t\t|0|1|2|3|4|5|6|7|\n\t\t\t+-+-+-+-+-+-+-+-+\n\t\t\t|F|NRI|  Type   |\n\t\t\t+---------------+\n\n\n\t\t\tThe FU header has the following format:\n\t\t\t+---------------+\n\t\t\t|0|1|2|3|4|5|6|7|\n\t\t\t+-+-+-+-+-+-+-+-+\n\t\t\t|S|E|R|  Type   |\n\t\t\t+---------------+\n\n\t\t\tS: 1 bit\n\t\t\tWhen set to one, the Start bit indicates the start of a fragmented\n\t\t\tNAL unit.  When the following FU payload is not the start of a\n\t\t\tfragmented NAL unit payload, the Start bit is set to zero.\n\n\t\t\tE: 1 bit\n\t\t\tWhen set to one, the End bit indicates the end of a fragmented NAL\n\t\t\tunit, i.e., the last byte of the payload is also the last byte of\n\t\t\tthe fragmented NAL unit.  When the following FU payload is not the\n\t\t\tlast fragment of a fragmented NAL unit, the End bit is set to\n\t\t\tzero.\n\n\t\t\tR: 1 bit\n\t\t\tThe Reserved bit MUST be equal to 0 and MUST be ignored by the\n\t\t\treceiver.\n\n\t\t\tType: 5 bits\n\t\t\tThe NAL unit payload type as defined in table 7-1 of [1].\n\t\t*/\n\t\tfuIndicator := packet[0]\n\t\tfuHeader := packet[1]\n\t\tisStart := fuHeader&0x80 != 0\n\t\tisEnd := fuHeader&0x40 != 0\n\t\tif isStart {\n\t\t\tself.fuStarted = true\n\t\t\tself.fuBuffer = []byte{fuIndicator&0xe0 | fuHeader&0x1f}\n\t\t}\n\t\tif self.fuStarted {\n\t\t\tself.fuBuffer = append(self.fuBuffer, packet[2:]...)\n\t\t\tif isEnd {\n\t\t\t\tself.fuStarted = false\n\t\t\t\tif err = self.handleH264Payload(timestamp, self.fuBuffer); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase naluType == 24: // STAP-A\n\t\t/*\n\t\t0                   1                   2                   3\n\t\t0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|                          RTP Header                           |\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|STAP-A NAL HDR |         NALU 1 Size           | NALU 1 HDR    |\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|                         NALU 1 Data                           |\n\t\t:                                                               :\n\t\t+               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|               | NALU 2 Size                   | NALU 2 HDR    |\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|                         NALU 2 Data                           |\n\t\t:                                                               :\n\t\t|                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|                               :...OPTIONAL RTP padding        |\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\n\t\tFigure 7.  An example of an RTP packet including an STAP-A\n\t\tcontaining two single-time aggregation units\n\t\t*/\n\t\tpacket = packet[1:]\n\t\tfor len(packet) >= 2 {\n\t\t\tsize := int(packet[0])<<8|int(packet[1])\n\t\t\tif size+2 > len(packet) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif err = self.handleH264Payload(timestamp, packet[2:size+2]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpacket = packet[size+2:]\n\t\t}\n\t\treturn\n\n\tcase naluType >= 6 && naluType <= 23: // other single NALU packet\n\tcase naluType == 25: // STAB-B\n\tcase naluType == 26: // MTAP-16\n\tcase naluType == 27: // MTAP-24\n\tcase naluType == 28: // FU-B\n\n\tdefault:\n\t\terr = fmt.Errorf(\"rtsp: unsupported H264 naluType=%d\", naluType)\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc (self *Stream) handleRtpPacket(packet []byte) (err error) {\n\tif self.isCodecDataChange() {\n\t\terr = ErrCodecDataChange\n\t\treturn\n\t}\n\n\tif self.client != nil && self.client.DebugRtp {\n\t\tfmt.Println(\"rtp: packet\", self.CodecData.Type(), \"len\", len(packet))\n\t\tdumpsize := len(packet)\n\t\tif dumpsize > 32 {\n\t\t\tdumpsize = 32\n\t\t}\n\t\tfmt.Print(hex.Dump(packet[:dumpsize]))\n\t}\n\n\t/*\n\t\t0                   1                   2                   3\n\t\t0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|V=2|P|X|  CC   |M|     PT      |       sequence number         |\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|                           timestamp                           |\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t\t|           synchronization source (SSRC) identifier            |\n\t\t+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\t\t|            contributing source (CSRC) identifiers             |\n\t\t|                             ....                              |\n\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\t*/\n\tif len(packet) < 8 {\n\t\terr = fmt.Errorf(\"rtp: packet too short\")\n\t\treturn\n\t}\n\tpayloadOffset := 12 + int(packet[0]&0xf)*4\n\tif payloadOffset > len(packet) {\n\t\terr = fmt.Errorf(\"rtp: packet too short\")\n\t\treturn\n\t}\n\ttimestamp := binary.BigEndian.Uint32(packet[4:8])\n\tpayload := packet[payloadOffset:]\n\n\t/*\n\t\tPT \tEncoding Name \tAudio/Video (A/V) \tClock Rate (Hz) \tChannels \tReference\n\t\t0\tPCMU\tA\t8000\t1\t[RFC3551]\n\t\t1\tReserved\n\t\t2\tReserved\n\t\t3\tGSM\tA\t8000\t1\t[RFC3551]\n\t\t4\tG723\tA\t8000\t1\t[Vineet_Kumar][RFC3551]\n\t\t5\tDVI4\tA\t8000\t1\t[RFC3551]\n\t\t6\tDVI4\tA\t16000\t1\t[RFC3551]\n\t\t7\tLPC\tA\t8000\t1\t[RFC3551]\n\t\t8\tPCMA\tA\t8000\t1\t[RFC3551]\n\t\t9\tG722\tA\t8000\t1\t[RFC3551]\n\t\t10\tL16\tA\t44100\t2\t[RFC3551]\n\t\t11\tL16\tA\t44100\t1\t[RFC3551]\n\t\t12\tQCELP\tA\t8000\t1\t[RFC3551]\n\t\t13\tCN\tA\t8000\t1\t[RFC3389]\n\t\t14\tMPA\tA\t90000\t\t[RFC3551][RFC2250]\n\t\t15\tG728\tA\t8000\t1\t[RFC3551]\n\t\t16\tDVI4\tA\t11025\t1\t[Joseph_Di_Pol]\n\t\t17\tDVI4\tA\t22050\t1\t[Joseph_Di_Pol]\n\t\t18\tG729\tA\t8000\t1\t[RFC3551]\n\t\t19\tReserved\tA\n\t\t20\tUnassigned\tA\n\t\t21\tUnassigned\tA\n\t\t22\tUnassigned\tA\n\t\t23\tUnassigned\tA\n\t\t24\tUnassigned\tV\n\t\t25\tCelB\tV\t90000\t\t[RFC2029]\n\t\t26\tJPEG\tV\t90000\t\t[RFC2435]\n\t\t27\tUnassigned\tV\n\t\t28\tnv\tV\t90000\t\t[RFC3551]\n\t\t29\tUnassigned\tV\n\t\t30\tUnassigned\tV\n\t\t31\tH261\tV\t90000\t\t[RFC4587]\n\t\t32\tMPV\tV\t90000\t\t[RFC2250]\n\t\t33\tMP2T\tAV\t90000\t\t[RFC2250]\n\t\t34\tH263\tV\t90000\t\t[Chunrong_Zhu]\n\t\t35-71\tUnassigned\t?\n\t\t72-76\tReserved for RTCP conflict avoidance\t\t\t\t[RFC3551]\n\t\t77-95\tUnassigned\t?\n\t\t96-127\tdynamic\t?\t\t\t[RFC3551]\n\t*/\n\t//payloadType := packet[1]&0x7f\n\n\tswitch self.Sdp.Type {\n\tcase av.H264:\n\t\tif err = self.handleH264Payload(timestamp, payload); err != nil {\n\t\t\treturn\n\t\t}\n\n\tcase av.AAC:\n\t\tif len(payload) < 4 {\n\t\t\terr = fmt.Errorf(\"rtp: aac packet too short\")\n\t\t\treturn\n\t\t}\n\t\tpayload = payload[4:] // TODO: remove this hack\n\t\tself.gotpkt = true\n\t\tself.pkt.Data = payload\n\t\tself.timestamp = timestamp\n\n\tdefault:\n\t\tself.gotpkt = true\n\t\tself.pkt.Data = payload\n\t\tself.timestamp = timestamp\n\t}\n\n\treturn\n}\n\nfunc (self *Client) Play() (err error) {\n\treq := Request{\n\t\tMethod: \"PLAY\",\n\t\tUri:    self.requestUri,\n\t}\n\treq.Header = append(req.Header, \"Session: \"+self.session)\n\tif err = self.WriteRequest(req); err != nil {\n\t\treturn\n\t}\n\n\tif self.allCodecDataReady() {\n\t\tself.stage = stageCodecDataDone\n\t} else {\n\t\tself.stage = stageWaitCodecData\n\t}\n\treturn\n}\n\nfunc (self *Client) Teardown() (err error) {\n\treq := Request{\n\t\tMethod: \"TEARDOWN\",\n\t\tUri:    self.requestUri,\n\t}\n\treq.Header = append(req.Header, \"Session: \"+self.session)\n\tif err = self.WriteRequest(req); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Client) Close() (err error) {\n\treturn self.conn.Conn.Close()\n}\n\nfunc (self *Client) handleBlock(block []byte) (pkt av.Packet, ok bool, err error) {\n\t_, blockno, _ := self.parseBlockHeader(block)\n\tif blockno%2 != 0 {\n\t\tif self.DebugRtp {\n\t\t\tfmt.Println(\"rtsp: rtcp block len\", len(block)-4)\n\t\t}\n\t\treturn\n\t}\n\n\ti := blockno/2\n\tif i >= len(self.streams) {\n\t\terr = fmt.Errorf(\"rtsp: block no=%d invalid\", blockno)\n\t\treturn\n\t}\n\tstream := self.streams[i]\n\n\therr := stream.handleRtpPacket(block[4:])\n\tif herr != nil {\n\t\tif !self.SkipErrRtpBlock {\n\t\t\terr = herr\n\t\t\treturn\n\t\t}\n\t}\n\n\tif stream.gotpkt {\n\t\t/*\n\t\tTODO: sync AV by rtcp NTP timestamp\n\t\tTODO: handle timestamp overflow\n\t\thttps://tools.ietf.org/html/rfc3550\n\t\tA receiver can then synchronize presentation of the audio and video packets by relating \n\t\ttheir RTP timestamps using the timestamp pairs in RTCP SR packets.\n\t\t*/\n\t\tif stream.firsttimestamp == 0 {\n\t\t\tstream.firsttimestamp = stream.timestamp\n\t\t}\n\t\tstream.timestamp -= stream.firsttimestamp\n\n\t\tok = true\n\t\tpkt = stream.pkt\n\t\tpkt.Time = time.Duration(stream.timestamp)*time.Second / time.Duration(stream.timeScale())\n\t\tpkt.Idx = int8(self.setupMap[i])\n\n\t\tif pkt.Time < stream.lasttime || pkt.Time - stream.lasttime > time.Minute*30 {\n\t\t\terr = fmt.Errorf(\"rtp: time invalid stream#%d time=%v lasttime=%v\", pkt.Idx, pkt.Time, stream.lasttime)\n\t\t\treturn\n\t\t}\n\t\tstream.lasttime = pkt.Time\n\n\t\tif self.DebugRtp {\n\t\t\tfmt.Println(\"rtp: pktout\", pkt.Idx, pkt.Time, len(pkt.Data))\n\t\t}\n\n\t\tstream.pkt = av.Packet{}\n\t\tstream.gotpkt = false\n\t}\n\n\treturn\n}\n\nfunc (self *Client) readPacket() (pkt av.Packet, err error) {\n\tif err = self.SendRtpKeepalive(); err != nil {\n\t\treturn\n\t}\n\n\tfor {\n\t\tvar res Response\n\t\tfor {\n\t\t\tif res, err = self.poll(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(res.Block) > 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tvar ok bool\n\t\tif pkt, ok, err = self.handleBlock(res.Block); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif ok {\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Client) ReadPacket() (pkt av.Packet, err error) {\n\tif err = self.prepare(stageCodecDataDone); err != nil {\n\t\treturn\n\t}\n\treturn self.readPacket()\n}\n\nfunc Handler(h *avutil.RegisterHandler) {\n\th.UrlDemuxer = func(uri string) (ok bool, demuxer av.DemuxCloser, err error) {\n\t\tif !strings.HasPrefix(uri, \"rtsp://\") {\n\t\t\treturn\n\t\t}\n\t\tok = true\n\t\tdemuxer, err = Dial(uri)\n\t\treturn\n\t}\n}\n\n"
  },
  {
    "path": "format/rtsp/conn.go",
    "content": "package rtsp\n\nimport (\n\t\"net\"\n\t\"time\"\n)\n\ntype connWithTimeout struct {\n\tTimeout time.Duration\n\tnet.Conn\n}\n\nfunc (self connWithTimeout) Read(p []byte) (n int, err error) {\n\tif self.Timeout > 0 {\n\t\tself.Conn.SetReadDeadline(time.Now().Add(self.Timeout))\n\t}\n\treturn self.Conn.Read(p)\n}\n\nfunc (self connWithTimeout) Write(p []byte) (n int, err error) {\n\tif self.Timeout > 0 {\n\t\tself.Conn.SetWriteDeadline(time.Now().Add(self.Timeout))\n\t}\n\treturn self.Conn.Write(p)\n}\n"
  },
  {
    "path": "format/rtsp/sdp/parser.go",
    "content": "package sdp\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"github.com/nareix/joy4/av\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Session struct {\n\tUri string\n}\n\ntype Media struct {\n\tAVType             string\n\tType               av.CodecType\n\tTimeScale          int\n\tControl            string\n\tRtpmap             int\n\tConfig             []byte\n\tSpropParameterSets [][]byte\n\tPayloadType        int\n\tSizeLength         int\n\tIndexLength        int\n}\n\nfunc Parse(content string) (sess Session, medias []Media) {\n\tvar media *Media\n\n\tfor _, line := range strings.Split(content, \"\\n\") {\n\t\tline = strings.TrimSpace(line)\n\t\ttypeval := strings.SplitN(line, \"=\", 2)\n\t\tif len(typeval) == 2 {\n\t\t\tfields := strings.SplitN(typeval[1], \" \", 2)\n\n\t\t\tswitch typeval[0] {\n\t\t\tcase \"m\":\n\t\t\t\tif len(fields) > 0 {\n\t\t\t\t\tswitch fields[0] {\n\t\t\t\t\tcase \"audio\", \"video\":\n\t\t\t\t\t\tmedias = append(medias, Media{AVType: fields[0]})\n\t\t\t\t\t\tmedia = &medias[len(medias)-1]\n\t\t\t\t\t\tmfields := strings.Split(fields[1], \" \")\n\t\t\t\t\t\tif len(mfields) >= 3 {\n\t\t\t\t\t\t\tmedia.PayloadType, _ = strconv.Atoi(mfields[2])\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\tcase \"u\":\n\t\t\t\tsess.Uri = typeval[1]\n\n\t\t\tcase \"a\":\n\t\t\t\tif media != nil {\n\t\t\t\t\tfor _, field := range fields {\n\t\t\t\t\t\tkeyval := strings.SplitN(field, \":\", 2)\n\t\t\t\t\t\tif len(keyval) >= 2 {\n\t\t\t\t\t\t\tkey := keyval[0]\n\t\t\t\t\t\t\tval := keyval[1]\n\t\t\t\t\t\t\tswitch key {\n\t\t\t\t\t\t\tcase \"control\":\n\t\t\t\t\t\t\t\tmedia.Control = val\n\t\t\t\t\t\t\tcase \"rtpmap\":\n\t\t\t\t\t\t\t\tmedia.Rtpmap, _ = strconv.Atoi(val)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkeyval = strings.Split(field, \"/\")\n\t\t\t\t\t\tif len(keyval) >= 2 {\n\t\t\t\t\t\t\tkey := keyval[0]\n\t\t\t\t\t\t\tswitch strings.ToUpper(key) {\n\t\t\t\t\t\t\tcase \"MPEG4-GENERIC\":\n\t\t\t\t\t\t\t\tmedia.Type = av.AAC\n\t\t\t\t\t\t\tcase \"H264\":\n\t\t\t\t\t\t\t\tmedia.Type = av.H264\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif i, err := strconv.Atoi(keyval[1]); err == nil {\n\t\t\t\t\t\t\t\tmedia.TimeScale = i\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif false {\n\t\t\t\t\t\t\t\tfmt.Println(\"sdp:\", keyval[1], media.TimeScale)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkeyval = strings.Split(field, \";\")\n\t\t\t\t\t\tif len(keyval) > 1 {\n\t\t\t\t\t\t\tfor _, field := range keyval {\n\t\t\t\t\t\t\t\tkeyval := strings.SplitN(field, \"=\", 2)\n\t\t\t\t\t\t\t\tif len(keyval) == 2 {\n\t\t\t\t\t\t\t\t\tkey := strings.TrimSpace(keyval[0])\n\t\t\t\t\t\t\t\t\tval := keyval[1]\n\t\t\t\t\t\t\t\t\tswitch key {\n\t\t\t\t\t\t\t\t\tcase \"config\":\n\t\t\t\t\t\t\t\t\t\tmedia.Config, _ = hex.DecodeString(val)\n\t\t\t\t\t\t\t\t\tcase \"sizelength\":\n\t\t\t\t\t\t\t\t\t\tmedia.SizeLength, _ = strconv.Atoi(val)\n\t\t\t\t\t\t\t\t\tcase \"indexlength\":\n\t\t\t\t\t\t\t\t\t\tmedia.IndexLength, _ = strconv.Atoi(val)\n\t\t\t\t\t\t\t\t\tcase \"sprop-parameter-sets\":\n\t\t\t\t\t\t\t\t\t\tfields := strings.Split(val, \",\")\n\t\t\t\t\t\t\t\t\t\tfor _, field := range fields {\n\t\t\t\t\t\t\t\t\t\t\tval, _ := base64.StdEncoding.DecodeString(field)\n\t\t\t\t\t\t\t\t\t\t\tmedia.SpropParameterSets = append(media.SpropParameterSets, val)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "format/rtsp/sdp/parser_test.go",
    "content": "package sdp\n\nimport (\n\t\"testing\"\n)\n\nfunc TestParse(t *testing.T) {\n\tinfos := Decode(`\nv=0\no=- 1459325504777324 1 IN IP4 192.168.0.123\ns=RTSP/RTP stream from Network Video Server\ni=mpeg4cif\nt=0 0\na=tool:LIVE555 Streaming Media v2009.09.28\na=type:broadcast\na=control:*\na=range:npt=0-\na=x-qt-text-nam:RTSP/RTP stream from Network Video Server\na=x-qt-text-inf:mpeg4cif\nm=video 0 RTP/AVP 96\nc=IN IP4 0.0.0.0\nb=AS:300\na=rtpmap:96 H264/90000\na=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AHpWoKA9k,aO48gA==\na=x-dimensions: 720, 480\na=x-framerate: 15\na=control:track1\nm=audio 0 RTP/AVP 96\nc=IN IP4 0.0.0.0\nb=AS:256\na=rtpmap:96 MPEG4-GENERIC/16000/2\na=fmtp:96 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1408\na=control:track2\nm=audio 0 RTP/AVP 0\nc=IN IP4 0.0.0.0\nb=AS:50\na=recvonly\na=control:rtsp://109.195.127.207:554/mpeg4cif/trackID=2\na=rtpmap:0 PCMU/8000\na=Media_header:MEDIAINFO=494D4B48010100000400010010710110401F000000FA000000000000000000000000000000000000;\na=appversion:1.0\n`)\n\tt.Logf(\"%v\", infos)\n}\n"
  },
  {
    "path": "format/rtsp/stream.go",
    "content": "package rtsp\n\nimport (\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/format/rtsp/sdp\"\n\t\"time\"\n)\n\ntype Stream struct {\n\tav.CodecData\n\tSdp    sdp.Media\n\tclient *Client\n\n\t// h264\n\tfuStarted  bool\n\tfuBuffer   []byte\n\tsps        []byte\n\tpps        []byte\n\tspsChanged bool\n\tppsChanged bool\n\n\tgotpkt    bool\n\tpkt       av.Packet\n\ttimestamp uint32\n\tfirsttimestamp uint32\n\n\tlasttime time.Duration\n}\n\n"
  },
  {
    "path": "format/ts/demuxer.go",
    "content": "package ts\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"time\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/format/ts/tsio\"\n\t\"github.com/nareix/joy4/codec/aacparser\"\n\t\"github.com/nareix/joy4/codec/h264parser\"\n\t\"io\"\n)\n\ntype Demuxer struct {\n\tr *bufio.Reader\n\n\tpkts []av.Packet\n\n\tpat     *tsio.PAT\n\tpmt     *tsio.PMT\n\tstreams []*Stream\n\ttshdr   []byte\n\n\tstage int\n}\n\nfunc NewDemuxer(r io.Reader) *Demuxer {\n\treturn &Demuxer{\n\t\ttshdr: make([]byte, 188),\n\t\tr: bufio.NewReaderSize(r, pio.RecommendBufioSize),\n\t}\n}\n\nfunc (self *Demuxer) Streams() (streams []av.CodecData, err error) {\n\tif err = self.probe(); err != nil {\n\t\treturn\n\t}\n\tfor _, stream := range self.streams {\n\t\tstreams = append(streams, stream.CodecData)\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) probe() (err error) {\n\tif self.stage == 0 {\n\t\tfor {\n\t\t\tif self.pmt != nil {\n\t\t\t\tn := 0\n\t\t\t\tfor _, stream := range self.streams {\n\t\t\t\t\tif stream.CodecData != nil {\n\t\t\t\t\t\tn++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif n == len(self.streams) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err = self.poll(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tself.stage++\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {\n\tif err = self.probe(); err != nil {\n\t\treturn\n\t}\n\n\tfor len(self.pkts) == 0 {\n\t\tif err = self.poll(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tpkt = self.pkts[0]\n\tself.pkts = self.pkts[1:]\n\treturn\n}\n\nfunc (self *Demuxer) poll() (err error) {\n\tif err = self.readTSPacket(); err == io.EOF {\n\t\tvar n int\n\t\tif n, err = self.payloadEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif n == 0 {\n\t\t\terr = io.EOF\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) initPMT(payload []byte) (err error) {\n\tvar psihdrlen int\n\tvar datalen int\n\tif _, _, psihdrlen, datalen, err = tsio.ParsePSI(payload); err != nil {\n\t\treturn\n\t}\n\tself.pmt = &tsio.PMT{}\n\tif _, err = self.pmt.Unmarshal(payload[psihdrlen:psihdrlen+datalen]); err != nil {\n\t\treturn\n\t}\n\n\tself.streams = []*Stream{}\n\tfor i, info := range self.pmt.ElementaryStreamInfos {\n\t\tstream := &Stream{}\n\t\tstream.idx = i\n\t\tstream.demuxer = self\n\t\tstream.pid = info.ElementaryPID\n\t\tstream.streamType = info.StreamType\n\t\tswitch info.StreamType {\n\t\tcase tsio.ElementaryStreamTypeH264:\n\t\t\tself.streams = append(self.streams, stream)\n\t\tcase tsio.ElementaryStreamTypeAdtsAAC:\n\t\t\tself.streams = append(self.streams, stream)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) payloadEnd() (n int, err error) {\n\tfor _, stream := range self.streams {\n\t\tvar i int\n\t\tif i, err = stream.payloadEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn += i\n\t}\n\treturn\n}\n\nfunc (self *Demuxer) readTSPacket() (err error) {\n\tvar hdrlen int\n\tvar pid uint16\n\tvar start bool\n\tvar iskeyframe bool\n\n\tif _, err = io.ReadFull(self.r, self.tshdr); err != nil {\n\t\treturn\n\t}\n\n\tif pid, start, iskeyframe, hdrlen, err = tsio.ParseTSHeader(self.tshdr); err != nil {\n\t\treturn\n\t}\n\tpayload := self.tshdr[hdrlen:]\n\n\tif self.pat == nil {\n\t\tif pid == 0 {\n\t\t\tvar psihdrlen int\n\t\t\tvar datalen int\n\t\t\tif _, _, psihdrlen, datalen, err = tsio.ParsePSI(payload); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tself.pat = &tsio.PAT{}\n\t\t\tif _, err = self.pat.Unmarshal(payload[psihdrlen:psihdrlen+datalen]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t} else if self.pmt == nil {\n\t\tfor _, entry := range self.pat.Entries {\n\t\t\tif entry.ProgramMapPID == pid {\n\t\t\t\tif err = self.initPMT(payload); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor _, stream := range self.streams {\n\t\t\tif pid == stream.pid {\n\t\t\t\tif err = stream.handleTSPacket(start, iskeyframe, payload); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Stream) addPacket(payload []byte, timedelta time.Duration) {\n\tdts := self.dts\n\tpts := self.pts\n\tif dts == 0 {\n\t\tdts = pts\n\t}\n\n\tdemuxer := self.demuxer\n\tpkt := av.Packet{\n\t\tIdx: int8(self.idx),\n\t\tIsKeyFrame: self.iskeyframe,\n\t\tTime: dts+timedelta,\n\t\tData: payload,\n\t}\n\tif pts != dts {\n\t\tpkt.CompositionTime = pts-dts\n\t}\n\tdemuxer.pkts = append(demuxer.pkts, pkt)\n}\n\nfunc (self *Stream) payloadEnd() (n int, err error) {\n\tpayload := self.data\n\tif payload == nil {\n\t\treturn\n\t}\n\tif self.datalen != 0 && len(payload) != self.datalen {\n\t\terr = fmt.Errorf(\"ts: packet size mismatch size=%d correct=%d\", len(payload), self.datalen)\n\t\treturn\n\t}\n\tself.data = nil\n\n\tswitch self.streamType {\n\tcase tsio.ElementaryStreamTypeAdtsAAC:\n\t\tvar config aacparser.MPEG4AudioConfig\n\n\t\tdelta := time.Duration(0)\n\t\tfor len(payload) > 0 {\n\t\t\tvar hdrlen, framelen, samples int\n\t\t\tif config, hdrlen, framelen, samples, err = aacparser.ParseADTSHeader(payload); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif self.CodecData == nil {\n\t\t\t\tif self.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfig(config); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tself.addPacket(payload[hdrlen:framelen], delta)\n\t\t\tn++\n\t\t\tdelta += time.Duration(samples) * time.Second / time.Duration(config.SampleRate)\n\t\t\tpayload = payload[framelen:]\n\t\t}\n\n\tcase tsio.ElementaryStreamTypeH264:\n\t\tnalus, _ := h264parser.SplitNALUs(payload)\n\t\tvar sps, pps []byte\n\t\tfor _, nalu := range nalus {\n\t\t\tif len(nalu) > 0 {\n\t\t\t\tnaltype := nalu[0] & 0x1f\n\t\t\t\tswitch {\n\t\t\t\tcase naltype == 7:\n\t\t\t\t\tsps = nalu\n\t\t\t\tcase naltype == 8:\n\t\t\t\t\tpps = nalu\n\t\t\t\tcase h264parser.IsDataNALU(nalu):\n\t\t\t\t\t// raw nalu to avcc\n\t\t\t\t\tb := make([]byte, 4+len(nalu))\n\t\t\t\t\tpio.PutU32BE(b[0:4], uint32(len(nalu)))\n\t\t\t\t\tcopy(b[4:], nalu)\n\t\t\t\t\tself.addPacket(b, time.Duration(0))\n\t\t\t\t\tn++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif self.CodecData == nil && len(sps) > 0 && len(pps) > 0 {\n\t\t\tif self.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(sps, pps); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self *Stream) handleTSPacket(start bool, iskeyframe bool, payload []byte) (err error) {\n\tif start {\n\t\tif _, err = self.payloadEnd(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar hdrlen int\n\t\tif hdrlen, _, self.datalen, self.pts, self.dts, err = tsio.ParsePESHeader(payload); err != nil {\n\t\t\treturn\n\t\t}\n\t\tself.iskeyframe = iskeyframe\n\t\tif self.datalen == 0 {\n\t\t\tself.data = make([]byte, 0, 4096)\n\t\t} else {\n\t\t\tself.data = make([]byte, 0, self.datalen)\n\t\t}\n\t\tself.data = append(self.data, payload[hdrlen:]...)\n\t} else {\n\t\tself.data = append(self.data, payload...)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "format/ts/handler.go",
    "content": "package ts\n\nimport (\n\t\"io\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/av/avutil\"\n)\n\nfunc Handler(h *avutil.RegisterHandler) {\n\th.Ext = \".ts\"\n\n\th.Probe = func(b []byte) bool {\n\t\treturn b[0] == 0x47 && b[188] == 0x47\n\t}\n\n\th.ReaderDemuxer = func(r io.Reader) av.Demuxer {\n\t\treturn NewDemuxer(r)\n\t}\n\n\th.WriterMuxer = func(w io.Writer) av.Muxer {\n\t\treturn NewMuxer(w)\n\t}\n\n\th.CodecTypes = CodecTypes\n}\n\n"
  },
  {
    "path": "format/ts/muxer.go",
    "content": "package ts\n\nimport (\n\t\"fmt\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/codec/aacparser\"\n\t\"github.com/nareix/joy4/codec/h264parser\"\n\t\"github.com/nareix/joy4/format/ts/tsio\"\n\t\"io\"\n\t\"time\"\n)\n\nvar CodecTypes = []av.CodecType{av.H264, av.AAC}\n\ntype Muxer struct {\n\tw                        io.Writer\n\tstreams                  []*Stream\n\tPaddingToMakeCounterCont bool\n\n\tpsidata []byte\n\tpeshdr  []byte\n\ttshdr   []byte\n\tadtshdr []byte\n\tdatav   [][]byte\n\tnalus   [][]byte\n\n\ttswpat, tswpmt *tsio.TSWriter\n}\n\nfunc NewMuxer(w io.Writer) *Muxer {\n\treturn &Muxer{\n\t\tw:       w,\n\t\tpsidata: make([]byte, 188),\n\t\tpeshdr:  make([]byte, tsio.MaxPESHeaderLength),\n\t\ttshdr:   make([]byte, tsio.MaxTSHeaderLength),\n\t\tadtshdr: make([]byte, aacparser.ADTSHeaderLength),\n\t\tnalus:   make([][]byte, 16),\n\t\tdatav:   make([][]byte, 16),\n\t\ttswpmt:  tsio.NewTSWriter(tsio.PMT_PID),\n\t\ttswpat:  tsio.NewTSWriter(tsio.PAT_PID),\n\t}\n}\n\nfunc (self *Muxer) newStream(codec av.CodecData) (err error) {\n\tok := false\n\tfor _, c := range CodecTypes {\n\t\tif codec.Type() == c {\n\t\t\tok = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !ok {\n\t\terr = fmt.Errorf(\"ts: codec type=%s is not supported\", codec.Type())\n\t\treturn\n\t}\n\n\tpid := uint16(len(self.streams) + 0x100)\n\tstream := &Stream{\n\t\tmuxer:     self,\n\t\tCodecData: codec,\n\t\tpid:       pid,\n\t\ttsw:       tsio.NewTSWriter(pid),\n\t}\n\tself.streams = append(self.streams, stream)\n\treturn\n}\n\nfunc (self *Muxer) writePaddingTSPackets(tsw *tsio.TSWriter) (err error) {\n\tfor tsw.ContinuityCounter&0xf != 0x0 {\n\t\tif err = tsw.WritePackets(self.w, self.datav[:0], 0, false, true); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Muxer) WriteTrailer() (err error) {\n\tif self.PaddingToMakeCounterCont {\n\t\tfor _, stream := range self.streams {\n\t\t\tif err = self.writePaddingTSPackets(stream.tsw); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *Muxer) SetWriter(w io.Writer) {\n\tself.w = w\n\treturn\n}\n\nfunc (self *Muxer) WritePATPMT() (err error) {\n\tpat := tsio.PAT{\n\t\tEntries: []tsio.PATEntry{\n\t\t\t{ProgramNumber: 1, ProgramMapPID: tsio.PMT_PID},\n\t\t},\n\t}\n\tpatlen := pat.Marshal(self.psidata[tsio.PSIHeaderLength:])\n\tn := tsio.FillPSI(self.psidata, tsio.TableIdPAT, tsio.TableExtPAT, patlen)\n\tself.datav[0] = self.psidata[:n]\n\tif err = self.tswpat.WritePackets(self.w, self.datav[:1], 0, false, true); err != nil {\n\t\treturn\n\t}\n\n\tvar elemStreams []tsio.ElementaryStreamInfo\n\tfor _, stream := range self.streams {\n\t\tswitch stream.Type() {\n\t\tcase av.AAC:\n\t\t\telemStreams = append(elemStreams, tsio.ElementaryStreamInfo{\n\t\t\t\tStreamType:    tsio.ElementaryStreamTypeAdtsAAC,\n\t\t\t\tElementaryPID: stream.pid,\n\t\t\t})\n\t\tcase av.H264:\n\t\t\telemStreams = append(elemStreams, tsio.ElementaryStreamInfo{\n\t\t\t\tStreamType:    tsio.ElementaryStreamTypeH264,\n\t\t\t\tElementaryPID: stream.pid,\n\t\t\t})\n\t\t}\n\t}\n\n\tpmt := tsio.PMT{\n\t\tPCRPID:                0x100,\n\t\tElementaryStreamInfos: elemStreams,\n\t}\n\tpmtlen := pmt.Len()\n\tif pmtlen+tsio.PSIHeaderLength > len(self.psidata) {\n\t\terr = fmt.Errorf(\"ts: pmt too large\")\n\t\treturn\n\t}\n\tpmt.Marshal(self.psidata[tsio.PSIHeaderLength:])\n\tn = tsio.FillPSI(self.psidata, tsio.TableIdPMT, tsio.TableExtPMT, pmtlen)\n\tself.datav[0] = self.psidata[:n]\n\tif err = self.tswpmt.WritePackets(self.w, self.datav[:1], 0, false, true); err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {\n\tself.streams = []*Stream{}\n\tfor _, stream := range streams {\n\t\tif err = self.newStream(stream); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err = self.WritePATPMT(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *Muxer) WritePacket(pkt av.Packet) (err error) {\n\tstream := self.streams[pkt.Idx]\n\tpkt.Time += time.Second\n\n\tswitch stream.Type() {\n\tcase av.AAC:\n\t\tcodec := stream.CodecData.(aacparser.CodecData)\n\n\t\tn := tsio.FillPESHeader(self.peshdr, tsio.StreamIdAAC, len(self.adtshdr)+len(pkt.Data), pkt.Time, 0)\n\t\tself.datav[0] = self.peshdr[:n]\n\t\taacparser.FillADTSHeader(self.adtshdr, codec.Config, 1024, len(pkt.Data))\n\t\tself.datav[1] = self.adtshdr\n\t\tself.datav[2] = pkt.Data\n\n\t\tif err = stream.tsw.WritePackets(self.w, self.datav[:3], pkt.Time, true, false); err != nil {\n\t\t\treturn\n\t\t}\n\n\tcase av.H264:\n\t\tcodec := stream.CodecData.(h264parser.CodecData)\n\n\t\tnalus := self.nalus[:0]\n\t\tif pkt.IsKeyFrame {\n\t\t\tnalus = append(nalus, codec.SPS())\n\t\t\tnalus = append(nalus, codec.PPS())\n\t\t}\n\t\tpktnalus, _ := h264parser.SplitNALUs(pkt.Data)\n\t\tfor _, nalu := range pktnalus {\n\t\t\tnalus = append(nalus, nalu)\n\t\t}\n\n\t\tdatav := self.datav[:1]\n\t\tfor i, nalu := range nalus {\n\t\t\tif i == 0 {\n\t\t\t\tdatav = append(datav, h264parser.AUDBytes)\n\t\t\t} else {\n\t\t\t\tdatav = append(datav, h264parser.StartCodeBytes)\n\t\t\t}\n\t\t\tdatav = append(datav, nalu)\n\t\t}\n\n\t\tn := tsio.FillPESHeader(self.peshdr, tsio.StreamIdH264, -1, pkt.Time+pkt.CompositionTime, pkt.Time)\n\t\tdatav[0] = self.peshdr[:n]\n\n\t\tif err = stream.tsw.WritePackets(self.w, datav, pkt.Time, pkt.IsKeyFrame, false); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "format/ts/stream.go",
    "content": "package ts\n\nimport (\n\t\"time\"\n\t\"github.com/nareix/joy4/av\"\n\t\"github.com/nareix/joy4/format/ts/tsio\"\n)\n\ntype Stream struct {\n\tav.CodecData\n\n\tdemuxer *Demuxer\n\tmuxer   *Muxer\n\n\tpid    uint16\n\tstreamId   uint8\n\tstreamType uint8\n\n\ttsw       *tsio.TSWriter\n\tidx  int\n\n\tiskeyframe bool\n\tpts, dts time.Duration\n\tdata []byte\n\tdatalen int\n}\n\n"
  },
  {
    "path": "format/ts/tsio/checksum.go",
    "content": "package tsio\n\nvar ieeeCrc32Tbl = []uint32{\n\t0x00000000, 0xB71DC104, 0x6E3B8209, 0xD926430D, 0xDC760413, 0x6B6BC517,\n\t0xB24D861A, 0x0550471E, 0xB8ED0826, 0x0FF0C922, 0xD6D68A2F, 0x61CB4B2B,\n\t0x649B0C35, 0xD386CD31, 0x0AA08E3C, 0xBDBD4F38, 0x70DB114C, 0xC7C6D048,\n\t0x1EE09345, 0xA9FD5241, 0xACAD155F, 0x1BB0D45B, 0xC2969756, 0x758B5652,\n\t0xC836196A, 0x7F2BD86E, 0xA60D9B63, 0x11105A67, 0x14401D79, 0xA35DDC7D,\n\t0x7A7B9F70, 0xCD665E74, 0xE0B62398, 0x57ABE29C, 0x8E8DA191, 0x39906095,\n\t0x3CC0278B, 0x8BDDE68F, 0x52FBA582, 0xE5E66486, 0x585B2BBE, 0xEF46EABA,\n\t0x3660A9B7, 0x817D68B3, 0x842D2FAD, 0x3330EEA9, 0xEA16ADA4, 0x5D0B6CA0,\n\t0x906D32D4, 0x2770F3D0, 0xFE56B0DD, 0x494B71D9, 0x4C1B36C7, 0xFB06F7C3,\n\t0x2220B4CE, 0x953D75CA, 0x28803AF2, 0x9F9DFBF6, 0x46BBB8FB, 0xF1A679FF,\n\t0xF4F63EE1, 0x43EBFFE5, 0x9ACDBCE8, 0x2DD07DEC, 0x77708634, 0xC06D4730,\n\t0x194B043D, 0xAE56C539, 0xAB068227, 0x1C1B4323, 0xC53D002E, 0x7220C12A,\n\t0xCF9D8E12, 0x78804F16, 0xA1A60C1B, 0x16BBCD1F, 0x13EB8A01, 0xA4F64B05,\n\t0x7DD00808, 0xCACDC90C, 0x07AB9778, 0xB0B6567C, 0x69901571, 0xDE8DD475,\n\t0xDBDD936B, 0x6CC0526F, 0xB5E61162, 0x02FBD066, 0xBF469F5E, 0x085B5E5A,\n\t0xD17D1D57, 0x6660DC53, 0x63309B4D, 0xD42D5A49, 0x0D0B1944, 0xBA16D840,\n\t0x97C6A5AC, 0x20DB64A8, 0xF9FD27A5, 0x4EE0E6A1, 0x4BB0A1BF, 0xFCAD60BB,\n\t0x258B23B6, 0x9296E2B2, 0x2F2BAD8A, 0x98366C8E, 0x41102F83, 0xF60DEE87,\n\t0xF35DA999, 0x4440689D, 0x9D662B90, 0x2A7BEA94, 0xE71DB4E0, 0x500075E4,\n\t0x892636E9, 0x3E3BF7ED, 0x3B6BB0F3, 0x8C7671F7, 0x555032FA, 0xE24DF3FE,\n\t0x5FF0BCC6, 0xE8ED7DC2, 0x31CB3ECF, 0x86D6FFCB, 0x8386B8D5, 0x349B79D1,\n\t0xEDBD3ADC, 0x5AA0FBD8, 0xEEE00C69, 0x59FDCD6D, 0x80DB8E60, 0x37C64F64,\n\t0x3296087A, 0x858BC97E, 0x5CAD8A73, 0xEBB04B77, 0x560D044F, 0xE110C54B,\n\t0x38368646, 0x8F2B4742, 0x8A7B005C, 0x3D66C158, 0xE4408255, 0x535D4351,\n\t0x9E3B1D25, 0x2926DC21, 0xF0009F2C, 0x471D5E28, 0x424D1936, 0xF550D832,\n\t0x2C769B3F, 0x9B6B5A3B, 0x26D61503, 0x91CBD407, 0x48ED970A, 0xFFF0560E,\n\t0xFAA01110, 0x4DBDD014, 0x949B9319, 0x2386521D, 0x0E562FF1, 0xB94BEEF5,\n\t0x606DADF8, 0xD7706CFC, 0xD2202BE2, 0x653DEAE6, 0xBC1BA9EB, 0x0B0668EF,\n\t0xB6BB27D7, 0x01A6E6D3, 0xD880A5DE, 0x6F9D64DA, 0x6ACD23C4, 0xDDD0E2C0,\n\t0x04F6A1CD, 0xB3EB60C9, 0x7E8D3EBD, 0xC990FFB9, 0x10B6BCB4, 0xA7AB7DB0,\n\t0xA2FB3AAE, 0x15E6FBAA, 0xCCC0B8A7, 0x7BDD79A3, 0xC660369B, 0x717DF79F,\n\t0xA85BB492, 0x1F467596, 0x1A163288, 0xAD0BF38C, 0x742DB081, 0xC3307185,\n\t0x99908A5D, 0x2E8D4B59, 0xF7AB0854, 0x40B6C950, 0x45E68E4E, 0xF2FB4F4A,\n\t0x2BDD0C47, 0x9CC0CD43, 0x217D827B, 0x9660437F, 0x4F460072, 0xF85BC176,\n\t0xFD0B8668, 0x4A16476C, 0x93300461, 0x242DC565, 0xE94B9B11, 0x5E565A15,\n\t0x87701918, 0x306DD81C, 0x353D9F02, 0x82205E06, 0x5B061D0B, 0xEC1BDC0F,\n\t0x51A69337, 0xE6BB5233, 0x3F9D113E, 0x8880D03A, 0x8DD09724, 0x3ACD5620,\n\t0xE3EB152D, 0x54F6D429, 0x7926A9C5, 0xCE3B68C1, 0x171D2BCC, 0xA000EAC8,\n\t0xA550ADD6, 0x124D6CD2, 0xCB6B2FDF, 0x7C76EEDB, 0xC1CBA1E3, 0x76D660E7,\n\t0xAFF023EA, 0x18EDE2EE, 0x1DBDA5F0, 0xAAA064F4, 0x738627F9, 0xC49BE6FD,\n\t0x09FDB889, 0xBEE0798D, 0x67C63A80, 0xD0DBFB84, 0xD58BBC9A, 0x62967D9E,\n\t0xBBB03E93, 0x0CADFF97, 0xB110B0AF, 0x060D71AB, 0xDF2B32A6, 0x6836F3A2,\n\t0x6D66B4BC, 0xDA7B75B8, 0x035D36B5, 0xB440F7B1, 0x00000001,\n}\n\nfunc calcCRC32(crc uint32, data []byte) uint32 {\n\tfor _, b := range data {\n\t\tcrc = ieeeCrc32Tbl[b^byte(crc)] ^ (crc >> 8)\n\t}\n\treturn crc\n}\n\n"
  },
  {
    "path": "format/ts/tsio/tsio.go",
    "content": "\npackage tsio\n\nimport (\n\t\"io\"\n\t\"time\"\n\t\"fmt\"\n\t\"github.com/nareix/joy4/utils/bits/pio\"\n)\n\nconst (\n\tStreamIdH264 = 0xe0\n\tStreamIdAAC  = 0xc0\n)\n\nconst (\n\tPAT_PID = 0\n\tPMT_PID = 0x1000\n)\n\nconst TableIdPMT = 2\nconst TableExtPMT = 1\n\nconst TableIdPAT = 0\nconst TableExtPAT = 1\n\nconst MaxPESHeaderLength = 19\nconst MaxTSHeaderLength = 12\n\nvar ErrPESHeader = fmt.Errorf(\"invalid PES header\")\nvar ErrPSIHeader = fmt.Errorf(\"invalid PSI header\")\nvar ErrParsePMT = fmt.Errorf(\"invalid PMT\")\nvar ErrParsePAT = fmt.Errorf(\"invalid PAT\")\n\nconst (\n\tElementaryStreamTypeH264    = 0x1B\n\tElementaryStreamTypeAdtsAAC = 0x0F\n)\n\ntype PATEntry struct {\n\tProgramNumber uint16\n\tNetworkPID    uint16\n\tProgramMapPID uint16\n}\n\ntype PAT struct {\n\tEntries []PATEntry\n}\n\nfunc (self PAT) Len() (n int) {\n\treturn len(self.Entries)*4\n}\n\nfunc (self PAT) Marshal(b []byte) (n int) {\n\tfor _, entry := range self.Entries {\n\t\tpio.PutU16BE(b[n:], entry.ProgramNumber)\n\t\tn += 2\n\t\tif entry.ProgramNumber == 0 {\n\t\t\tpio.PutU16BE(b[n:], entry.NetworkPID&0x1fff|7<<13)\n\t\t\tn += 2\n\t\t} else {\n\t\t\tpio.PutU16BE(b[n:], entry.ProgramMapPID&0x1fff|7<<13)\n\t\t\tn += 2\n\t\t}\n\t}\n\treturn\n}\n\nfunc (self *PAT) Unmarshal(b []byte) (n int, err error) {\n\tfor n < len(b) {\n\t\tif n+4 <= len(b) {\n\t\t\tvar entry PATEntry\n\t\t\tentry.ProgramNumber = pio.U16BE(b[n:])\n\t\t\tn += 2\n\t\t\tif entry.ProgramNumber == 0 {\n\t\t\t\tentry.NetworkPID = pio.U16BE(b[n:])&0x1fff\n\t\t\t\tn += 2\n\t\t\t} else {\n\t\t\t\tentry.ProgramMapPID = pio.U16BE(b[n:])&0x1fff\n\t\t\t\tn += 2\n\t\t\t}\n\t\t\tself.Entries = append(self.Entries, entry)\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\tif n < len(b) {\n\t\terr = ErrParsePAT\n\t\treturn\n\t}\n\treturn\n}\n\ntype Descriptor struct {\n\tTag  uint8\n\tData []byte\n}\n\ntype ElementaryStreamInfo struct {\n\tStreamType    uint8\n\tElementaryPID uint16\n\tDescriptors   []Descriptor\n}\n\ntype PMT struct {\n\tPCRPID                uint16\n\tProgramDescriptors    []Descriptor\n\tElementaryStreamInfos []ElementaryStreamInfo\n}\n\nfunc (self PMT) Len() (n int) {\n\t// 111(3)\n\t// PCRPID(13)\n\tn += 2\n\n\t// desclen(16)\n\tn += 2\n\n\tfor _, desc := range self.ProgramDescriptors {\n\t\tn += 2+len(desc.Data)\n\t}\n\n\tfor _, info := range self.ElementaryStreamInfos {\n\t\t// streamType\n\t\tn += 1\n\n\t\t// Reserved(3)\n\t\t// Elementary PID(13)\n\t\tn += 2\n\n\t\t// Reserved(6)\n\t\t// ES Info length length(10)\n\t\tn += 2\n\n\t\tfor _, desc := range info.Descriptors {\n\t\t\tn += 2+len(desc.Data)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (self PMT) fillDescs(b []byte, descs []Descriptor) (n int) {\n\tfor _, desc := range descs {\n\t\tb[n] = desc.Tag\n\t\tn++\n\t\tb[n] = uint8(len(desc.Data))\n\t\tn++\n\t\tcopy(b[n:], desc.Data)\n\t\tn += len(desc.Data)\n\t}\n\treturn\n}\n\nfunc (self PMT) Marshal(b []byte) (n int) {\n\t// 111(3)\n\t// PCRPID(13)\n\tpio.PutU16BE(b[n:], self.PCRPID|7<<13)\n\tn += 2\n\n\thold := n\n\tn += 2\n\tpos := n\n\tn += self.fillDescs(b[n:], self.ProgramDescriptors)\n\tdesclen := n-pos\n\tpio.PutU16BE(b[hold:], uint16(desclen)|0xf<<12)\n\n\tfor _, info := range self.ElementaryStreamInfos {\n\t\tb[n] = info.StreamType\n\t\tn++\n\n\t\t// Reserved(3)\n\t\t// Elementary PID(13)\n\t\tpio.PutU16BE(b[n:], info.ElementaryPID|7<<13)\n\t\tn += 2\n\n\t\thold := n\n\t\tn += 2\n\t\tpos := n\n\t\tn += self.fillDescs(b[n:], info.Descriptors)\n\t\tdesclen := n-pos\n\t\tpio.PutU16BE(b[hold:], uint16(desclen)|0x3c<<10)\n\t}\n\n\treturn\n}\n\nfunc (self PMT) parseDescs(b []byte) (descs []Descriptor, err error) {\n\tn := 0\n\tfor n < len(b) {\n\t\tif n+2 <= len(b) {\n\t\t\tdesc := Descriptor{}\n\t\t\tdesc.Tag = b[n]\n\t\t\tdesc.Data = make([]byte, b[n+1])\n\t\t\tn += 2\n\t\t\tif n+len(desc.Data) < len(b) {\n\t\t\t\tcopy(desc.Data, b[n:])\n\t\t\t\tdescs = append(descs, desc)\n\t\t\t\tn += len(desc.Data)\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\tif n < len(b) {\n\t\terr = ErrParsePMT\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (self *PMT) Unmarshal(b []byte) (n int, err error) {\n\tif len(b) < n+4 {\n\t\terr = ErrParsePMT\n\t\treturn\n\t}\n\n\t// 111(3)\n\t// PCRPID(13)\n\tself.PCRPID = pio.U16BE(b[0:2])&0x1fff\n\tn += 2\n\n\t// Reserved(4)=0xf\n\t// Reserved(2)=0x0\n\t// Program info length(10)\n\tdesclen := int(pio.U16BE(b[2:4])&0x3ff)\n\tn += 2\n\n\tif desclen > 0 {\n\t\tif len(b) < n+desclen {\n\t\t\terr = ErrParsePMT\n\t\t\treturn\n\t\t}\n\t\tif self.ProgramDescriptors, err = self.parseDescs(b[n:n+desclen]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn += desclen\n\t}\n\n\tfor n < len(b) {\n\t\tif len(b) < n+5 {\n\t\t\terr = ErrParsePMT\n\t\t\treturn\n\t\t}\n\n\t\tvar info ElementaryStreamInfo\n\t\tinfo.StreamType = b[n]\n\t\tn++\n\n\t\t// Reserved(3)\n\t\t// Elementary PID(13)\n\t\tinfo.ElementaryPID = pio.U16BE(b[n:])&0x1fff\n\t\tn += 2\n\n\t\t// Reserved(6)\n\t\t// ES Info length(10)\n\t\tdesclen := int(pio.U16BE(b[n:])&0x3ff)\n\t\tn += 2\n\n\t\tif desclen > 0 {\n\t\t\tif len(b) < n+desclen {\n\t\t\t\terr = ErrParsePMT\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif info.Descriptors, err = self.parseDescs(b[n:n+desclen]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tn += desclen\n\t\t}\n\n\t\tself.ElementaryStreamInfos = append(self.ElementaryStreamInfos, info)\n\t}\n\n\treturn\n}\n\nfunc ParsePSI(h []byte) (tableid uint8, tableext uint16, hdrlen int, datalen int, err error) {\n\tif len(h) < 8 {\n\t\terr = ErrPSIHeader\n\t\treturn\n\t}\n\n\t// pointer(8)\n\tpointer := h[0]\n\thdrlen++\n\tif pointer > 0 {\n\t\thdrlen += int(pointer)\n\t\tif len(h) < hdrlen {\n\t\t\terr = ErrPSIHeader\n\t\t\treturn\n\t\t}\n\t}\n\n\tif len(h) < hdrlen+12 {\n\t\terr = ErrPSIHeader\n\t\treturn\n\t}\n\n\t// table_id(8)\n\ttableid = h[hdrlen]\n\thdrlen++\n\n\t// section_syntax_indicator(1)=1,private_bit(1)=0,reserved(2)=3,unused(2)=0,section_length(10)\n\tdatalen = int(pio.U16BE(h[hdrlen:]))&0x3ff - 9\n\thdrlen += 2\n\n\tif datalen < 0 {\n\t\terr = ErrPSIHeader\n\t\treturn\n\t}\n\n\t// Table ID extension(16)\n\ttableext = pio.U16BE(h[hdrlen:])\n\thdrlen += 2\n\n\t// resverd(2)=3\n\t// version(5)\n\t// Current_next_indicator(1)\n\thdrlen++\n\n\t// section_number(8)\n\thdrlen++\n\n\t// last_section_number(8)\n\thdrlen++\n\n\t// data\n\n\t// crc(32)\n\n\treturn\n}\n\nconst PSIHeaderLength = 9\n\nfunc FillPSI(h []byte, tableid uint8, tableext uint16, datalen int) (n int) {\n\t// pointer(8)\n\th[n] = 0\n\tn++\n\n\t// table_id(8)\n\th[n] = tableid\n\tn++\n\n\t// section_syntax_indicator(1)=1,private_bit(1)=0,reserved(2)=3,unused(2)=0,section_length(10)\n\tpio.PutU16BE(h[n:], uint16(0xa<<12 | 2+3+4+datalen))\n\tn += 2\n\n\t// Table ID extension(16)\n\tpio.PutU16BE(h[n:], tableext)\n\tn += 2\n\n\t// resverd(2)=3,version(5)=0,Current_next_indicator(1)=1\n\th[n] = 0x3<<6 | 1\n\tn++\n\n\t// section_number(8)\n\th[n] = 0\n\tn++\n\n\t// last_section_number(8)\n\th[n] = 0\n\tn++\n\n\tn += datalen\n\n\tcrc := calcCRC32(0xffffffff, h[1:n])\n\tpio.PutU32LE(h[n:], crc)\n\tn += 4\n\n\treturn\n}\n\nfunc TimeToPCR(tm time.Duration) (pcr uint64) {\n\t// base(33)+resverd(6)+ext(9)\n\tts := uint64(tm*PCR_HZ/time.Second)\n\tbase := ts / 300\n\text := ts % 300\n\tpcr = base<<15 | 0x3f<<9 | ext\n\treturn\n}\n\nfunc PCRToTime(pcr uint64) (tm time.Duration) {\n\tbase := pcr >> 15\n\text := pcr & 0x1ff\n\tts := base*300 + ext\n\ttm = time.Duration(ts)*time.Second/time.Duration(PCR_HZ)\n\treturn\n}\n\nfunc TimeToTs(tm time.Duration) (v uint64) {\n\tts := uint64(tm*PTS_HZ/time.Second)\n\t// 0010\tPTS 32..30 1\tPTS 29..15 1 PTS 14..00 1\n\tv = ((ts>>30)&0x7)<<33 | ((ts>>15)&0x7fff)<<17 | (ts&0x7fff)<<1 | 0x100010001\n\treturn\n}\n\nfunc TsToTime(v uint64) (tm time.Duration) {\n\t// 0010\tPTS 32..30 1\tPTS 29..15 1 PTS 14..00 1\n\tts := (((v>>33)&0x7)<<30) | (((v>>17)&0x7fff) << 15) | ((v>>1)&0x7fff)\n\ttm = time.Duration(ts)*time.Second/time.Duration(PTS_HZ)\n\treturn\n}\n\nconst (\n\tPTS_HZ = 90000\n\tPCR_HZ = 27000000\n)\n\nfunc ParsePESHeader(h []byte) (hdrlen int, streamid uint8, datalen int, pts, dts time.Duration, err error) {\n\tif h[0] != 0 || h[1] != 0 || h[2] != 1 {\n\t\terr = ErrPESHeader\n\t\treturn\n\t}\n\tstreamid = h[3]\n\n\tflags := h[7]\n\thdrlen = int(h[8])+9\n\n\tdatalen = int(pio.U16BE(h[4:6]))\n\tif datalen > 0 {\n\t\tdatalen -= int(h[8])+3\n\t}\n\n\tconst PTS = 1 << 7\n\tconst DTS = 1 << 6\n\n\tif flags&PTS != 0 {\n\t\tif len(h) < 14 {\n\t\t\terr = ErrPESHeader\n\t\t\treturn\n\t\t}\n\t\tpts = TsToTime(pio.U40BE(h[9:14]))\n\t\tif flags&DTS != 0 {\n\t\t\tif len(h) < 19 {\n\t\t\t\terr = ErrPESHeader\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdts = TsToTime(pio.U40BE(h[14:19]))\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc FillPESHeader(h []byte, streamid uint8, datalen int, pts, dts time.Duration) (n int) {\n\th[0] = 0\n\th[1] = 0\n\th[2] = 1\n\th[3] = streamid\n\n\tconst PTS = 1 << 7\n\tconst DTS = 1 << 6\n\n\tvar flags uint8\n\tif pts != 0 {\n\t\tflags |= PTS\n\t\tif dts != 0 {\n\t\t\tflags |= DTS\n\t\t}\n\t}\n\n\tif flags&PTS != 0 {\n\t\tn += 5\n\t}\n\tif flags&DTS != 0 {\n\t\tn += 5\n\t}\n\n\t// packet_length(16) if zero then variable length\n\t// Specifies the number of bytes remaining in the packet after this field. Can be zero.\n\t// If the PES packet length is set to zero, the PES packet can be of any length.\n\t// A value of zero for the PES packet length can be used only when the PES packet payload is a **video** elementary stream.\n\tvar pktlen uint16\n\tif datalen >= 0 {\n\t\tpktlen = uint16(datalen + n + 3)\n\t}\n\tpio.PutU16BE(h[4:6], pktlen)\n\n\th[6] = 2<<6|1 // resverd(6,2)=2,original_or_copy(0,1)=1\n\th[7] = flags\n\th[8] = uint8(n)\n\n\t// pts(40)?\n\t// dts(40)?\n\tif flags&PTS != 0 {\n\t\tif flags&DTS != 0 {\n\t\t\tpio.PutU40BE(h[9:14], TimeToTs(pts)|3<<36)\n\t\t\tpio.PutU40BE(h[14:19], TimeToTs(dts)|1<<36)\n\t\t} else {\n\t\t\tpio.PutU40BE(h[9:14], TimeToTs(pts)|2<<36)\n\t\t}\n\t}\n\n\tn += 9\n\treturn\n}\n\ntype TSWriter struct {\n\tw   io.Writer\n\tContinuityCounter uint\n\ttshdr []byte\n}\n\nfunc NewTSWriter(pid uint16) *TSWriter {\n\tw := &TSWriter{}\n\tw.tshdr = make([]byte, 188)\n\tw.tshdr[0] = 0x47\n\tpio.PutU16BE(w.tshdr[1:3], pid&0x1fff)\n\tfor i := 6; i < 188; i++ {\n\t\tw.tshdr[i] = 0xff\n\t}\n\treturn w\n}\n\nfunc (self *TSWriter) WritePackets(w io.Writer, datav [][]byte, pcr time.Duration, sync bool, paddata bool) (err error) {\n\tdatavlen := pio.VecLen(datav)\n\twritev := make([][]byte, len(datav))\n\twritepos := 0\n\n\tfor writepos < datavlen {\n\t\tself.tshdr[1] = self.tshdr[1]&0x1f\n\t\tself.tshdr[3] = byte(self.ContinuityCounter)&0xf|0x30\n\t\tself.tshdr[5] = 0 // flags\n\t\thdrlen := 6\n\t\tself.ContinuityCounter++\n\n\t\tif writepos == 0 {\n\t\t\tself.tshdr[1] = 0x40|self.tshdr[1] // Payload Unit Start Indicator\n\t\t\tif pcr != 0 {\n\t\t\t\thdrlen += 6\n\t\t\t\tself.tshdr[5] = 0x10|self.tshdr[5] // PCR flag (Discontinuity indicator 0x80)\n\t\t\t\tpio.PutU48BE(self.tshdr[6:12], TimeToPCR(pcr))\n\t\t\t}\n\t\t\tif sync {\n\t\t\t\tself.tshdr[5] = 0x40|self.tshdr[5] // Random Access indicator\n\t\t\t}\n\t\t}\n\n\t\tpadtail := 0\n\t\tend := writepos + 188 - hdrlen\n\t\tif end > datavlen {\n\t\t\tif paddata {\n\t\t\t\tpadtail = end - datavlen\n\t\t\t} else {\n\t\t\t\thdrlen += end - datavlen\n\t\t\t}\n\t\t\tend = datavlen\n\t\t}\n\t\tn := pio.VecSliceTo(datav, writev, writepos, end)\n\n\t\tself.tshdr[4] = byte(hdrlen)-5 // length\n\t\tif _, err = w.Write(self.tshdr[:hdrlen]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tfor i := 0; i < n; i++ {\n\t\t\tif _, err = w.Write(writev[i]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif padtail > 0 {\n\t\t\tif _, err = w.Write(self.tshdr[188-padtail:188]); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\twritepos = end\n\t}\n\n\treturn\n}\n\nfunc ParseTSHeader(tshdr []byte) (pid uint16, start bool, iskeyframe bool, hdrlen int, err error) {\n\t// https://en.wikipedia.org/wiki/MPEG_transport_stream\n\tif tshdr[0] != 0x47 {\n\t\terr = fmt.Errorf(\"tshdr sync invalid\")\n\t\treturn\n\t}\n\tpid = uint16((tshdr[1]&0x1f))<<8|uint16(tshdr[2])\n\tstart = tshdr[1]&0x40 != 0\n\thdrlen += 4\n\tif tshdr[3]&0x20 != 0 {\n\t\thdrlen += int(tshdr[4])+1\n\t\tiskeyframe = tshdr[5]&0x40 != 0\n\t}\n\treturn\n}\n\n"
  },
  {
    "path": "utils/bits/bits.go",
    "content": "package bits\n\nimport (\n\t\"io\"\n)\n\ntype Reader struct {\n\tR    io.Reader\n\tn    int\n\tbits uint64\n}\n\nfunc (self *Reader) ReadBits64(n int) (bits uint64, err error) {\n\tif self.n < n {\n\t\tvar b [8]byte\n\t\tvar got int\n\t\twant := (n - self.n + 7) / 8\n\t\tif got, err = self.R.Read(b[:want]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif got < want {\n\t\t\terr = io.EOF\n\t\t\treturn\n\t\t}\n\t\tfor i := 0; i < got; i++ {\n\t\t\tself.bits <<= 8\n\t\t\tself.bits |= uint64(b[i])\n\t\t}\n\t\tself.n += got * 8\n\t}\n\tbits = self.bits >> uint(self.n-n)\n\tself.bits ^= bits << uint(self.n-n)\n\tself.n -= n\n\treturn\n}\n\nfunc (self *Reader) ReadBits(n int) (bits uint, err error) {\n\tvar bits64 uint64\n\tif bits64, err = self.ReadBits64(n); err != nil {\n\t\treturn\n\t}\n\tbits = uint(bits64)\n\treturn\n}\n\nfunc (self *Reader) Read(p []byte) (n int, err error) {\n\tfor n < len(p) {\n\t\twant := 8\n\t\tif len(p)-n < want {\n\t\t\twant = len(p) - n\n\t\t}\n\t\tvar bits uint64\n\t\tif bits, err = self.ReadBits64(want * 8); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tfor i := 0; i < want; i++ {\n\t\t\tp[n+i] = byte(bits >> uint((want-i-1)*8))\n\t\t}\n\t\tn += want\n\t}\n\treturn\n}\n\ntype Writer struct {\n\tW    io.Writer\n\tn    int\n\tbits uint64\n}\n\nfunc (self *Writer) WriteBits64(bits uint64, n int) (err error) {\n\tif self.n+n > 64 {\n\t\tmove := uint(64 - self.n)\n\t\tmask := bits >> move\n\t\tself.bits = (self.bits << move) | mask\n\t\tself.n = 64\n\t\tif err = self.FlushBits(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn -= int(move)\n\t\tbits ^= (mask << move)\n\t}\n\tself.bits = (self.bits << uint(n)) | bits\n\tself.n += n\n\treturn\n}\n\nfunc (self *Writer) WriteBits(bits uint, n int) (err error) {\n\treturn self.WriteBits64(uint64(bits), n)\n}\n\nfunc (self *Writer) Write(p []byte) (n int, err error) {\n\tfor n < len(p) {\n\t\tif err = self.WriteBits64(uint64(p[n]), 8); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn++\n\t}\n\treturn\n}\n\nfunc (self *Writer) FlushBits() (err error) {\n\tif self.n > 0 {\n\t\tvar b [8]byte\n\t\tbits := self.bits\n\t\tif self.n%8 != 0 {\n\t\t\tbits <<= uint(8 - (self.n % 8))\n\t\t}\n\t\twant := (self.n + 7) / 8\n\t\tfor i := 0; i < want; i++ {\n\t\t\tb[i] = byte(bits >> uint((want-i-1)*8))\n\t\t}\n\t\tif _, err = self.W.Write(b[:want]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tself.n = 0\n\t}\n\treturn\n}\n"
  },
  {
    "path": "utils/bits/bits_test.go",
    "content": "package bits\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestBits(t *testing.T) {\n\trdata := []byte{0xf3, 0xb3, 0x45, 0x60}\n\trbuf := bytes.NewReader(rdata[:])\n\tr := &Reader{R: rbuf}\n\tvar u32 uint\n\tif u32, _ = r.ReadBits(4); u32 != 0xf {\n\t\tt.FailNow()\n\t}\n\tif u32, _ = r.ReadBits(4); u32 != 0x3 {\n\t\tt.FailNow()\n\t}\n\tif u32, _ = r.ReadBits(2); u32 != 0x2 {\n\t\tt.FailNow()\n\t}\n\tif u32, _ = r.ReadBits(2); u32 != 0x3 {\n\t\tt.FailNow()\n\t}\n\tb := make([]byte, 2)\n\tif r.Read(b); b[0] != 0x34 || b[1] != 0x56 {\n\t\tt.FailNow()\n\t}\n\n\twbuf := &bytes.Buffer{}\n\tw := &Writer{W: wbuf}\n\tw.WriteBits(0xf, 4)\n\tw.WriteBits(0x3, 4)\n\tw.WriteBits(0x2, 2)\n\tw.WriteBits(0x3, 2)\n\tn, _ := w.Write([]byte{0x34, 0x56})\n\tif n != 2 {\n\t\tt.FailNow()\n\t}\n\tw.FlushBits()\n\twdata := wbuf.Bytes()\n\tif wdata[0] != 0xf3 || wdata[1] != 0xb3 || wdata[2] != 0x45 || wdata[3] != 0x60 {\n\t\tt.FailNow()\n\t}\n\n\tb = make([]byte, 8)\n\tPutUInt64BE(b, 0x11223344, 32)\n\tif b[0] != 0x11 || b[1] != 0x22 || b[2] != 0x33 || b[3] != 0x44 {\n\t\tt.FailNow()\n\t}\n}\n"
  },
  {
    "path": "utils/bits/bufio/bufio.go",
    "content": "package bufio\n\nimport (\n\t\"io\"\n)\n\ntype Reader struct {\n\tbuf [][]byte\n\tR io.ReadSeeker\n}\n\nfunc NewReaderSize(r io.ReadSeeker, size int) *Reader {\n\tbuf := make([]byte, size*2)\n\treturn &Reader{\n\t\tR: r,\n\t\tbuf: [][]byte{buf[0:size], buf[size:]},\n\t}\n}\n\nfunc (self *Reader) ReadAt(b []byte, off int64) (n int, err error) {\n\treturn\n}\n\n"
  },
  {
    "path": "utils/bits/golomb_reader.go",
    "content": "package bits\n\nimport (\n\t\"io\"\n)\n\ntype GolombBitReader struct {\n\tR    io.Reader\n\tbuf  [1]byte\n\tleft byte\n}\n\nfunc (self *GolombBitReader) ReadBit() (res uint, err error) {\n\tif self.left == 0 {\n\t\tif _, err = self.R.Read(self.buf[:]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tself.left = 8\n\t}\n\tself.left--\n\tres = uint(self.buf[0]>>self.left) & 1\n\treturn\n}\n\nfunc (self *GolombBitReader) ReadBits(n int) (res uint, err error) {\n\tfor i := 0; i < n; i++ {\n\t\tvar bit uint\n\t\tif bit, err = self.ReadBit(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tres |= bit << uint(n-i-1)\n\t}\n\treturn\n}\n\nfunc (self *GolombBitReader) ReadExponentialGolombCode() (res uint, err error) {\n\ti := 0\n\tfor {\n\t\tvar bit uint\n\t\tif bit, err = self.ReadBit(); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif !(bit == 0 && i < 32) {\n\t\t\tbreak\n\t\t}\n\t\ti++\n\t}\n\tif res, err = self.ReadBits(i); err != nil {\n\t\treturn\n\t}\n\tres += (1 << uint(i)) - 1\n\treturn\n}\n\nfunc (self *GolombBitReader) ReadSE() (res uint, err error) {\n\tif res, err = self.ReadExponentialGolombCode(); err != nil {\n\t\treturn\n\t}\n\tif res&0x01 != 0 {\n\t\tres = (res + 1) / 2\n\t} else {\n\t\tres = -res / 2\n\t}\n\treturn\n}\n"
  },
  {
    "path": "utils/bits/pio/pio.go",
    "content": "\npackage pio\n\nvar RecommendBufioSize = 1024*64\n\n"
  },
  {
    "path": "utils/bits/pio/reader.go",
    "content": "\npackage pio\n\nfunc U8(b []byte) (i uint8) {\n\treturn b[0]\n}\n\nfunc U16BE(b []byte) (i uint16) {\n\ti = uint16(b[0])\n\ti <<= 8; i |= uint16(b[1])\n\treturn\n}\n\nfunc I16BE(b []byte) (i int16) {\n\ti = int16(b[0])\n\ti <<= 8; i |= int16(b[1])\n\treturn\n}\n\nfunc I24BE(b []byte) (i int32) {\n\ti = int32(int8(b[0]))\n\ti <<= 8; i |= int32(b[1])\n\ti <<= 8; i |= int32(b[2])\n\treturn\n}\n\nfunc U24BE(b []byte) (i uint32) {\n\ti = uint32(b[0])\n\ti <<= 8; i |= uint32(b[1])\n\ti <<= 8; i |= uint32(b[2])\n\treturn\n}\n\nfunc I32BE(b []byte) (i int32) {\n\ti = int32(int8(b[0]))\n\ti <<= 8; i |= int32(b[1])\n\ti <<= 8; i |= int32(b[2])\n\ti <<= 8; i |= int32(b[3])\n\treturn\n}\n\nfunc U32LE(b []byte) (i uint32) {\n\ti = uint32(b[3])\n\ti <<= 8; i |= uint32(b[2])\n\ti <<= 8; i |= uint32(b[1])\n\ti <<= 8; i |= uint32(b[0])\n\treturn\n}\n\nfunc U32BE(b []byte) (i uint32) {\n\ti = uint32(b[0])\n\ti <<= 8; i |= uint32(b[1])\n\ti <<= 8; i |= uint32(b[2])\n\ti <<= 8; i |= uint32(b[3])\n\treturn\n}\n\nfunc U40BE(b []byte) (i uint64) {\n\ti = uint64(b[0])\n\ti <<= 8; i |= uint64(b[1])\n\ti <<= 8; i |= uint64(b[2])\n\ti <<= 8; i |= uint64(b[3])\n\ti <<= 8; i |= uint64(b[4])\n\treturn\n}\n\nfunc U64BE(b []byte) (i uint64) {\n\ti = uint64(b[0])\n\ti <<= 8; i |= uint64(b[1])\n\ti <<= 8; i |= uint64(b[2])\n\ti <<= 8; i |= uint64(b[3])\n\ti <<= 8; i |= uint64(b[4])\n\ti <<= 8; i |= uint64(b[5])\n\ti <<= 8; i |= uint64(b[6])\n\ti <<= 8; i |= uint64(b[7])\n\treturn\n}\n\nfunc I64BE(b []byte) (i int64) {\n\ti = int64(int8(b[0]))\n\ti <<= 8; i |= int64(b[1])\n\ti <<= 8; i |= int64(b[2])\n\ti <<= 8; i |= int64(b[3])\n\ti <<= 8; i |= int64(b[4])\n\ti <<= 8; i |= int64(b[5])\n\ti <<= 8; i |= int64(b[6])\n\ti <<= 8; i |= int64(b[7])\n\treturn\n}\n\n\n"
  },
  {
    "path": "utils/bits/pio/vec.go",
    "content": "package pio\n\nfunc VecLen(vec [][]byte) (n int) {\n\tfor _, b := range vec {\n\t\tn += len(b)\n\t}\n\treturn\n}\n\nfunc VecSliceTo(in [][]byte, out [][]byte, s int, e int) (n int) {\n\tif s < 0 {\n\t\ts = 0\n\t}\n\n\tif e >= 0 && e < s {\n\t\tpanic(\"pio: VecSlice start > end\")\n\t}\n\n\ti := 0\n\toff := 0\n\tfor s > 0 && i < len(in) {\n\t\tleft := len(in[i])\n\t\tread := s\n\t\tif left < read {\n\t\t\tread = left\n\t\t}\n\t\tleft -= read\n\t\toff += read\n\t\ts -= read\n\t\te -= read\n\t\tif left == 0 {\n\t\t\ti++\n\t\t\toff = 0\n\t\t}\n\t}\n\tif s > 0 {\n\t\tpanic(\"pio: VecSlice start out of range\")\n\t}\n\n\tfor e != 0 && i < len(in) {\n\t\tleft := len(in[i])-off\n\t\tread := left\n\t\tif e > 0 && e < read {\n\t\t\tread = e\n\t\t}\n\t\tout[n] = in[i][off:off+read]\n\t\tn++\n\t\tleft -= read\n\t\te -= read\n\t\toff += read\n\t\tif left == 0 {\n\t\t\ti++\n\t\t\toff = 0\n\t\t}\n\t}\n\tif e > 0 {\n\t\tpanic(\"pio: VecSlice end out of range\")\n\t}\n\n\treturn\n}\n\nfunc VecSlice(in [][]byte, s int, e int) (out [][]byte) {\n\tout = make([][]byte, len(in))\n\tn := VecSliceTo(in, out, s, e)\n\tout = out[:n]\n\treturn\n}\n\n"
  },
  {
    "path": "utils/bits/pio/vec_test.go",
    "content": "\npackage pio\n\nimport (\n\t\"fmt\"\n)\n\nfunc ExampleVec() {\n\tvec := [][]byte{[]byte{1,2,3}, []byte{4,5,6,7,8,9}, []byte{10,11,12,13}}\n\tprintln(VecLen(vec))\n\n\tvec = VecSlice(vec, 1, -1)\n\tfmt.Println(vec)\n\n\tvec = VecSlice(vec, 2, -1)\n\tfmt.Println(vec)\n\n\tvec = VecSlice(vec, 8, 8)\n\tfmt.Println(vec)\n\n\t// Output:\n}\n"
  },
  {
    "path": "utils/bits/pio/writer.go",
    "content": "\npackage pio\n\nfunc PutU8(b []byte, v uint8) {\n\tb[0] = v\n}\n\nfunc PutI16BE(b []byte, v int16) {\n\tb[0] = byte(v>>8)\n\tb[1] = byte(v)\n}\n\nfunc PutU16BE(b []byte, v uint16) {\n\tb[0] = byte(v>>8)\n\tb[1] = byte(v)\n}\n\nfunc PutI24BE(b []byte, v int32) {\n\tb[0] = byte(v>>16)\n\tb[1] = byte(v>>8)\n\tb[2] = byte(v)\n}\n\nfunc PutU24BE(b []byte, v uint32) {\n\tb[0] = byte(v>>16)\n\tb[1] = byte(v>>8)\n\tb[2] = byte(v)\n}\n\nfunc PutI32BE(b []byte, v int32) {\n\tb[0] = byte(v>>24)\n\tb[1] = byte(v>>16)\n\tb[2] = byte(v>>8)\n\tb[3] = byte(v)\n}\n\nfunc PutU32BE(b []byte, v uint32) {\n\tb[0] = byte(v>>24)\n\tb[1] = byte(v>>16)\n\tb[2] = byte(v>>8)\n\tb[3] = byte(v)\n}\n\nfunc PutU32LE(b []byte, v uint32) {\n\tb[3] = byte(v>>24)\n\tb[2] = byte(v>>16)\n\tb[1] = byte(v>>8)\n\tb[0] = byte(v)\n}\n\nfunc PutU40BE(b []byte, v uint64) {\n\tb[0] = byte(v>>32)\n\tb[1] = byte(v>>24)\n\tb[2] = byte(v>>16)\n\tb[3] = byte(v>>8)\n\tb[4] = byte(v)\n}\n\nfunc PutU48BE(b []byte, v uint64) {\n\tb[0] = byte(v>>40)\n\tb[1] = byte(v>>32)\n\tb[2] = byte(v>>24)\n\tb[3] = byte(v>>16)\n\tb[4] = byte(v>>8)\n\tb[5] = byte(v)\n}\n\nfunc PutU64BE(b []byte, v uint64) {\n\tb[0] = byte(v>>56)\n\tb[1] = byte(v>>48)\n\tb[2] = byte(v>>40)\n\tb[3] = byte(v>>32)\n\tb[4] = byte(v>>24)\n\tb[5] = byte(v>>16)\n\tb[6] = byte(v>>8)\n\tb[7] = byte(v)\n}\n\nfunc PutI64BE(b []byte, v int64) {\n\tb[0] = byte(v>>56)\n\tb[1] = byte(v>>48)\n\tb[2] = byte(v>>40)\n\tb[3] = byte(v>>32)\n\tb[4] = byte(v>>24)\n\tb[5] = byte(v>>16)\n\tb[6] = byte(v>>8)\n\tb[7] = byte(v)\n}\n\n"
  }
]