├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README.md ├── av ├── av.go ├── avconv │ └── avconv.go ├── avutil │ └── avutil.go ├── pktque │ ├── buf.go │ ├── filters.go │ └── timeline.go ├── pubsub │ └── queue.go └── transcode │ └── transcode.go ├── cgo └── ffmpeg │ ├── audio.go │ ├── ffmpeg.go │ ├── ffmpeg.h │ └── video.go ├── codec ├── aacparser │ └── parser.go ├── codec.go ├── fake │ └── fake.go └── h264parser │ ├── parser.go │ └── parser_test.go ├── doc.go ├── examples ├── audio_decode │ └── main.go ├── http_flv_and_rtmp_server │ └── main.go ├── open_probe_file │ └── main.go ├── rtmp_publish │ └── main.go ├── rtmp_server_channels │ └── main.go ├── rtmp_server_proxy │ └── main.go ├── rtmp_server_speex_to_aac │ └── main.go ├── transcode │ └── main.go └── video_transcoder │ └── main.go ├── format ├── aac │ └── aac.go ├── flv │ ├── flv.go │ └── flvio │ │ ├── amf0.go │ │ └── flvio.go ├── format.go ├── mp4 │ ├── demuxer.go │ ├── handler.go │ ├── mp4io │ │ ├── atoms.go │ │ ├── gen │ │ │ ├── gen.go │ │ │ └── pattern.go │ │ └── mp4io.go │ ├── muxer.go │ └── stream.go ├── rtmp │ └── rtmp.go ├── rtsp │ ├── client.go │ ├── conn.go │ ├── sdp │ │ ├── parser.go │ │ └── parser_test.go │ └── stream.go └── ts │ ├── demuxer.go │ ├── handler.go │ ├── muxer.go │ ├── stream.go │ └── tsio │ ├── checksum.go │ └── tsio.go └── utils └── bits ├── bits.go ├── bits_test.go ├── bufio └── bufio.go ├── golomb_reader.go └── pio ├── pio.go ├── reader.go ├── vec.go ├── vec_test.go └── writer.go /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # PR Type 2 | 3 | - [ ] Feature 4 | - [ ] Bug fix 5 | - [ ] Docs 6 | 7 | ## Description 8 | 9 | Description of the pull request 10 | 11 | ## Notes for your reviewer 12 | 13 | Notes here 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | .vscode/ 3 | examples/video_transcoder/video_transcoder -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JOY4 2 | 3 | > Golang audio/video library and streaming server 4 | 5 | JOY4 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. 6 | 7 | # Features 8 | 9 | Well-designed and easy-to-use interfaces: 10 | 11 | - 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)) 12 | - 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)) 13 | - Transcoding ([doc](https://godoc.org/github.com/nareix/joy4/av/transcode) [example](https://github.com/nareix/joy4/blob/master/examples/transcode/main.go)) 14 | - Streaming server ([example](https://github.com/nareix/joy4/blob/master/examples/http_flv_and_rtmp_server/main.go)) 15 | 16 | Support container formats: 17 | 18 | - MP4 19 | - MPEG-TS 20 | - FLV 21 | - AAC (ADTS) 22 | 23 | RTSP Client 24 | - High level camera bug tolerance 25 | - Support STAP-A 26 | 27 | RTMP Client 28 | - Support publishing to nginx-rtmp-server 29 | - Support playing 30 | 31 | RTMP / HTTP-FLV Server 32 | - Support publishing clients: OBS / ffmpeg / Flash Player (>8) 33 | - Support playing clients: Flash Player 11 / VLC / ffplay / mpv 34 | - High performance 35 | 36 | 37 | Publisher-subscriber packet buffer queue ([doc](https://godoc.org/github.com/nareix/joy4/av/pubsub)) 38 | 39 | - Customize publisher buffer time and subscriber read position 40 | 41 | 42 | - Multiple channels live streaming ([example](https://github.com/nareix/joy4/blob/master/examples/rtmp_server_channels/main.go)) 43 | 44 | Packet filters ([doc](https://godoc.org/github.com/nareix/joy4/av/pktque)) 45 | 46 | - Wait first keyframe 47 | - Fix timestamp 48 | - Make A/V sync 49 | - Customize ([example](https://github.com/nareix/joy4/blob/master/examples/rtmp_server_channels/main.go#L19)) 50 | 51 | FFMPEG Golang-style binding ([doc](https://godoc.org/github.com/nareix/joy4/cgo/ffmpeg)) 52 | - Audio Encoder / Decoder 53 | - Video Decoder 54 | - Audio Resampler 55 | 56 | Support codec and container parsers: 57 | 58 | - H264 SPS/PPS/AVCDecoderConfigure parser ([doc](https://godoc.org/github.com/nareix/joy4/codec/h264parser)) 59 | - AAC ADTSHeader/MPEG4AudioConfig parser ([doc](https://godoc.org/github.com/nareix/joy4/codec/aacparser)) 60 | - MP4 Atoms parser ([doc](https://godoc.org/github.com/nareix/joy4/format/mp4/mp4io)) 61 | - FLV AMF0 object parser ([doc](https://godoc.org/github.com/nareix/joy4/format/flv/flvio)) 62 | 63 | # Requirements 64 | 65 | Go version >= 1.6 66 | 67 | ffmpeg version >= 3.0 (optional) 68 | 69 | # TODO 70 | 71 | HLS / MPEG-DASH Server 72 | 73 | # License 74 | 75 | MIT 76 | -------------------------------------------------------------------------------- /av/av.go: -------------------------------------------------------------------------------- 1 | 2 | // Package av defines basic interfaces and data structures of container demux/mux and audio encode/decode. 3 | package av 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | // Audio sample format. 11 | type SampleFormat uint8 12 | 13 | const ( 14 | U8 = SampleFormat(iota + 1) // 8-bit unsigned integer 15 | S16 // signed 16-bit integer 16 | S32 // signed 32-bit integer 17 | FLT // 32-bit float 18 | DBL // 64-bit float 19 | U8P // 8-bit unsigned integer in planar 20 | S16P // signed 16-bit integer in planar 21 | S32P // signed 32-bit integer in planar 22 | FLTP // 32-bit float in planar 23 | DBLP // 64-bit float in planar 24 | U32 // unsigned 32-bit integer 25 | ) 26 | 27 | func (self SampleFormat) BytesPerSample() int { 28 | switch self { 29 | case U8, U8P: 30 | return 1 31 | case S16, S16P: 32 | return 2 33 | case FLT, FLTP, S32, S32P, U32: 34 | return 4 35 | case DBL, DBLP: 36 | return 8 37 | default: 38 | return 0 39 | } 40 | } 41 | 42 | func (self SampleFormat) String() string { 43 | switch self { 44 | case U8: 45 | return "U8" 46 | case S16: 47 | return "S16" 48 | case S32: 49 | return "S32" 50 | case FLT: 51 | return "FLT" 52 | case DBL: 53 | return "DBL" 54 | case U8P: 55 | return "U8P" 56 | case S16P: 57 | return "S16P" 58 | case FLTP: 59 | return "FLTP" 60 | case DBLP: 61 | return "DBLP" 62 | case U32: 63 | return "U32" 64 | default: 65 | return "?" 66 | } 67 | } 68 | 69 | // Check if this sample format is in planar. 70 | func (self SampleFormat) IsPlanar() bool { 71 | switch self { 72 | case S16P, S32P, FLTP, DBLP: 73 | return true 74 | default: 75 | return false 76 | } 77 | } 78 | 79 | // Audio channel layout. 80 | type ChannelLayout uint16 81 | 82 | func (self ChannelLayout) String() string { 83 | return fmt.Sprintf("%dch", self.Count()) 84 | } 85 | 86 | const ( 87 | CH_FRONT_CENTER = ChannelLayout(1 << iota) 88 | CH_FRONT_LEFT 89 | CH_FRONT_RIGHT 90 | CH_BACK_CENTER 91 | CH_BACK_LEFT 92 | CH_BACK_RIGHT 93 | CH_SIDE_LEFT 94 | CH_SIDE_RIGHT 95 | CH_LOW_FREQ 96 | CH_NR 97 | 98 | CH_MONO = ChannelLayout(CH_FRONT_CENTER) 99 | CH_STEREO = ChannelLayout(CH_FRONT_LEFT | CH_FRONT_RIGHT) 100 | CH_2_1 = ChannelLayout(CH_STEREO | CH_BACK_CENTER) 101 | CH_2POINT1 = ChannelLayout(CH_STEREO | CH_LOW_FREQ) 102 | CH_SURROUND = ChannelLayout(CH_STEREO | CH_FRONT_CENTER) 103 | CH_3POINT1 = ChannelLayout(CH_SURROUND | CH_LOW_FREQ) 104 | // TODO: add all channel_layout in ffmpeg 105 | ) 106 | 107 | func (self ChannelLayout) Count() (n int) { 108 | for self != 0 { 109 | n++ 110 | self = (self - 1) & self 111 | } 112 | return 113 | } 114 | 115 | // Video/Audio codec type. can be H264/AAC/SPEEX/... 116 | type CodecType uint32 117 | 118 | var ( 119 | H264 = MakeVideoCodecType(avCodecTypeMagic + 1) 120 | AAC = MakeAudioCodecType(avCodecTypeMagic + 1) 121 | PCM_MULAW = MakeAudioCodecType(avCodecTypeMagic + 2) 122 | PCM_ALAW = MakeAudioCodecType(avCodecTypeMagic + 3) 123 | SPEEX = MakeAudioCodecType(avCodecTypeMagic + 4) 124 | NELLYMOSER = MakeAudioCodecType(avCodecTypeMagic + 5) 125 | ) 126 | 127 | const codecTypeAudioBit = 0x1 128 | const codecTypeOtherBits = 1 129 | 130 | func (self CodecType) String() string { 131 | switch self { 132 | case H264: 133 | return "H264" 134 | case AAC: 135 | return "AAC" 136 | case PCM_MULAW: 137 | return "PCM_MULAW" 138 | case PCM_ALAW: 139 | return "PCM_ALAW" 140 | case SPEEX: 141 | return "SPEEX" 142 | case NELLYMOSER: 143 | return "NELLYMOSER" 144 | } 145 | return "" 146 | } 147 | 148 | func (self CodecType) IsAudio() bool { 149 | return self&codecTypeAudioBit != 0 150 | } 151 | 152 | func (self CodecType) IsVideo() bool { 153 | return self&codecTypeAudioBit == 0 154 | } 155 | 156 | // Make a new audio codec type. 157 | func MakeAudioCodecType(base uint32) (c CodecType) { 158 | c = CodecType(base)< 1 247 | } 248 | 249 | func (self AudioFrame) Duration() time.Duration { 250 | return time.Second * time.Duration(self.SampleCount) / time.Duration(self.SampleRate) 251 | } 252 | 253 | // Check this audio frame has same format as other audio frame. 254 | func (self AudioFrame) HasSameFormat(other AudioFrame) bool { 255 | if self.SampleRate != other.SampleRate { 256 | return false 257 | } 258 | if self.ChannelLayout != other.ChannelLayout { 259 | return false 260 | } 261 | if self.SampleFormat != other.SampleFormat { 262 | return false 263 | } 264 | return true 265 | } 266 | 267 | // Split sample audio sample from this frame. 268 | func (self AudioFrame) Slice(start int, end int) (out AudioFrame) { 269 | if start > end { 270 | panic(fmt.Sprintf("av: AudioFrame split failed start=%d end=%d invalid", start, end)) 271 | } 272 | out = self 273 | out.Data = append([][]byte(nil), out.Data...) 274 | out.SampleCount = end - start 275 | size := self.SampleFormat.BytesPerSample() 276 | for i := range out.Data { 277 | out.Data[i] = out.Data[i][start*size : end*size] 278 | } 279 | return 280 | } 281 | 282 | // Concat two audio frames. 283 | func (self AudioFrame) Concat(in AudioFrame) (out AudioFrame) { 284 | out = self 285 | out.Data = append([][]byte(nil), out.Data...) 286 | out.SampleCount += in.SampleCount 287 | for i := range out.Data { 288 | out.Data[i] = append(out.Data[i], in.Data[i]...) 289 | } 290 | return 291 | } 292 | 293 | // AudioEncoder can encode raw audio frame into compressed audio packets. 294 | // cgo/ffmpeg inplements AudioEncoder, using ffmpeg.NewAudioEncoder to create it. 295 | type AudioEncoder interface { 296 | CodecData() (AudioCodecData, error) // encoder's codec data can put into container 297 | Encode(AudioFrame) ([][]byte, error) // encode raw audio frame into compressed pakcet(s) 298 | Close() // close encoder, free cgo contexts 299 | SetSampleRate(int) (error) // set encoder sample rate 300 | SetChannelLayout(ChannelLayout) (error) // set encoder channel layout 301 | SetSampleFormat(SampleFormat) (error) // set encoder sample format 302 | SetBitrate(int) (error) // set encoder bitrate 303 | SetOption(string,interface{}) (error) // encoder setopt, in ffmpeg is av_opt_set_dict() 304 | GetOption(string,interface{}) (error) // encoder getopt 305 | } 306 | 307 | // AudioDecoder can decode compressed audio packets into raw audio frame. 308 | // use ffmpeg.NewAudioDecoder to create it. 309 | type AudioDecoder interface { 310 | Decode([]byte) (bool, AudioFrame, error) // decode one compressed audio packet 311 | Close() // close decode, free cgo contexts 312 | } 313 | 314 | // AudioResampler can convert raw audio frames in different sample rate/format/channel layout. 315 | type AudioResampler interface { 316 | Resample(AudioFrame) (AudioFrame, error) // convert raw audio frames 317 | } 318 | 319 | // Video frame format. 320 | type PixelFormat uint8 321 | 322 | const ( 323 | // Planar formats 324 | I420 = PixelFormat(iota + 1) // 4:2:0 8 bit, 12 bpp. Y plane followed by 8 bit 2x2 subsampled U and V planes 325 | NV12 // 4:2:0 8 bit, 12 bpp. Y plane followed by an interleaved U/V plane with 2x2 subsampling 326 | NV21 // 4:2:0 8 bit, 12 bpp. As NV12 with U and V reversed in the interleaved plane 327 | //YV12 // 4:2:0 8 bit, 12 bpp. Y plane followed by 8 bit 2x2 subsampled V and U planes 328 | 329 | // Packed formats 330 | UYVY // 4:2:2 8-bit, 16 bpp. YUV (Y sample at every pixel, U and V sampled at every second pixel horizontally on each line). A macropixel contains 2 pixels in 1 u_int32. 331 | //YUY2 // 4:2:2 8-bit, 16 bpp. Same as UYVY but with different component ordering within the u_int32 macropixel. 332 | YUYV // 4:2:2 8-bit, 16 bpp as for UYVY but with different component ordering within the u_int32 macropixel. 333 | //V210 // 4:2:2 10-bit, 32 bpp. YCrCb equivalent to the Quicktime format of the same name. 334 | ) 335 | 336 | // BytesPerPixel returns the number of bytes (rounded up) used by a pixel in a given format 337 | func (pixFmt PixelFormat) BytesPerPixel() int { 338 | switch pixFmt { 339 | case I420, NV12, NV21, UYVY, YUYV: 340 | return 2 341 | default: 342 | return 0 343 | } 344 | } 345 | 346 | func (pixFmt PixelFormat) String() string { 347 | switch pixFmt { 348 | case I420: 349 | return "I420" 350 | case NV12: 351 | return "NV12" 352 | case NV21: 353 | return "NV21" 354 | case UYVY: 355 | return "UYVY" 356 | case YUYV: 357 | return "YUYV" 358 | default: 359 | return "?" 360 | } 361 | } 362 | 363 | // IsPlanar return true if this pixel format is planar. 364 | func (pixFmt PixelFormat) IsPlanar() bool { 365 | switch pixFmt { 366 | case I420, NV12, NV21: 367 | return true 368 | default: 369 | return false 370 | } 371 | } 372 | 373 | // HorizontalSubsampleRatio returns the ratio of Y bytes over U or V bytes in a row of pixels 374 | func (pixFmt PixelFormat) HorizontalSubsampleRatio() int { 375 | switch pixFmt { 376 | case I420, NV12, NV21, UYVY, YUYV: 377 | return 2 378 | } 379 | return -1 380 | } 381 | 382 | // VerticalSubsampleRatio returns the ratio of Y bytes over U or V bytes in a column of pixels 383 | func (pixFmt PixelFormat) VerticalSubsampleRatio() int { 384 | switch pixFmt { 385 | case I420, NV12, NV21: 386 | return 2 387 | case UYVY, YUYV: 388 | return 1 389 | } 390 | return -1 391 | } 392 | 393 | // Video scanning mode. 394 | type ScanningMode uint8 395 | const ( 396 | Progressive = ScanningMode(iota + 1) 397 | InterlacedTFF // Top Field First 398 | InterlacedBFF // Bottom Field First 399 | ) 400 | 401 | func (s ScanningMode) String() string { 402 | switch s { 403 | case Progressive: 404 | return "Progressive" 405 | case InterlacedTFF: 406 | return "InterlacedTFF" 407 | case InterlacedBFF: 408 | return "InterlacedBFF" 409 | } 410 | return "Unknown scanning mode" 411 | } 412 | -------------------------------------------------------------------------------- /av/avconv/avconv.go: -------------------------------------------------------------------------------- 1 | package avconv 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "time" 7 | "github.com/nareix/joy4/av/avutil" 8 | "github.com/nareix/joy4/av" 9 | "github.com/nareix/joy4/av/pktque" 10 | "github.com/nareix/joy4/av/transcode" 11 | ) 12 | 13 | var Debug bool 14 | 15 | type Option struct { 16 | Transcode bool 17 | Args []string 18 | } 19 | 20 | type Options struct { 21 | OutputCodecTypes []av.CodecType 22 | } 23 | 24 | type Demuxer struct { 25 | transdemux *transcode.Demuxer 26 | streams []av.CodecData 27 | Options 28 | Demuxer av.Demuxer 29 | } 30 | 31 | func (self *Demuxer) Close() (err error) { 32 | if self.transdemux != nil { 33 | return self.transdemux.Close() 34 | } 35 | return 36 | } 37 | 38 | func (self *Demuxer) Streams() (streams []av.CodecData, err error) { 39 | if err = self.prepare(); err != nil { 40 | return 41 | } 42 | streams = self.streams 43 | return 44 | } 45 | 46 | func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { 47 | if err = self.prepare(); err != nil { 48 | return 49 | } 50 | return self.transdemux.ReadPacket() 51 | } 52 | 53 | func (self *Demuxer) prepare() (err error) { 54 | if self.transdemux != nil { 55 | return 56 | } 57 | 58 | /* 59 | var streams []av.CodecData 60 | if streams, err = self.Demuxer.Streams(); err != nil { 61 | return 62 | } 63 | */ 64 | 65 | supports := self.Options.OutputCodecTypes 66 | 67 | transopts := transcode.Options{} 68 | transopts.FindAudioDecoderEncoder = func(codec av.AudioCodecData, i int) (ok bool, dec av.AudioDecoder, enc av.AudioEncoder, err error) { 69 | if len(supports) == 0 { 70 | return 71 | } 72 | 73 | support := false 74 | for _, typ := range supports { 75 | if typ == codec.Type() { 76 | support = true 77 | } 78 | } 79 | 80 | if support { 81 | return 82 | } 83 | ok = true 84 | 85 | var enctype av.CodecType 86 | for _, typ:= range supports { 87 | if typ.IsAudio() { 88 | if enc, _ = avutil.DefaultHandlers.NewAudioEncoder(typ); enc != nil { 89 | enctype = typ 90 | break 91 | } 92 | } 93 | } 94 | if enc == nil { 95 | err = fmt.Errorf("avconv: convert %s->%s failed", codec.Type(), enctype) 96 | return 97 | } 98 | 99 | // TODO: support per stream option 100 | // enc.SetSampleRate ... 101 | 102 | if dec, err = avutil.DefaultHandlers.NewAudioDecoder(codec); err != nil { 103 | err = fmt.Errorf("avconv: decode %s failed", codec.Type()) 104 | return 105 | } 106 | 107 | return 108 | } 109 | 110 | self.transdemux = &transcode.Demuxer{ 111 | Options: transopts, 112 | Demuxer: self.Demuxer, 113 | } 114 | if self.streams, err = self.transdemux.Streams(); err != nil { 115 | return 116 | } 117 | 118 | return 119 | } 120 | 121 | func ConvertCmdline(args []string) (err error) { 122 | output := "" 123 | input := "" 124 | flagi := false 125 | flagv := false 126 | flagt := false 127 | flagre := false 128 | duration := time.Duration(0) 129 | options := Options{} 130 | 131 | for _, arg := range args { 132 | switch arg { 133 | case "-i": 134 | flagi = true 135 | 136 | case "-v": 137 | flagv = true 138 | 139 | case "-t": 140 | flagt = true 141 | 142 | case "-re": 143 | flagre = true 144 | 145 | default: 146 | switch { 147 | case flagi: 148 | flagi = false 149 | input = arg 150 | 151 | case flagt: 152 | flagt = false 153 | var f float64 154 | fmt.Sscanf(arg, "%f", &f) 155 | duration = time.Duration(f*float64(time.Second)) 156 | 157 | default: 158 | output = arg 159 | } 160 | } 161 | } 162 | 163 | if input == "" { 164 | err = fmt.Errorf("avconv: input file not specified") 165 | return 166 | } 167 | 168 | if output == "" { 169 | err = fmt.Errorf("avconv: output file not specified") 170 | return 171 | } 172 | 173 | var demuxer av.DemuxCloser 174 | var muxer av.MuxCloser 175 | 176 | if demuxer, err = avutil.Open(input); err != nil { 177 | return 178 | } 179 | defer demuxer.Close() 180 | 181 | var handler avutil.RegisterHandler 182 | if handler, muxer, err = avutil.DefaultHandlers.FindCreate(output); err != nil { 183 | return 184 | } 185 | defer muxer.Close() 186 | 187 | options.OutputCodecTypes = handler.CodecTypes 188 | 189 | convdemux := &Demuxer{ 190 | Options: options, 191 | Demuxer: demuxer, 192 | } 193 | defer convdemux.Close() 194 | 195 | var streams []av.CodecData 196 | if streams, err = demuxer.Streams(); err != nil { 197 | return 198 | } 199 | 200 | var convstreams []av.CodecData 201 | if convstreams, err = convdemux.Streams(); err != nil { 202 | return 203 | } 204 | 205 | if flagv { 206 | for _, stream := range streams { 207 | fmt.Print(stream.Type(), " ") 208 | } 209 | fmt.Print("-> ") 210 | for _, stream := range convstreams { 211 | fmt.Print(stream.Type(), " ") 212 | } 213 | fmt.Println() 214 | } 215 | 216 | if err = muxer.WriteHeader(convstreams); err != nil { 217 | return 218 | } 219 | 220 | filters := pktque.Filters{} 221 | if flagre { 222 | filters = append(filters, &pktque.Walltime{}) 223 | } 224 | filterdemux := &pktque.FilterDemuxer{ 225 | Demuxer: convdemux, 226 | Filter: filters, 227 | } 228 | 229 | for { 230 | var pkt av.Packet 231 | if pkt, err = filterdemux.ReadPacket(); err != nil { 232 | if err == io.EOF { 233 | err = nil 234 | break 235 | } 236 | return 237 | } 238 | if flagv { 239 | fmt.Println(pkt.Idx, pkt.Time, len(pkt.Data), pkt.IsKeyFrame) 240 | } 241 | if duration != 0 && pkt.Time > duration { 242 | break 243 | } 244 | if err = muxer.WritePacket(pkt); err != nil { 245 | return 246 | } 247 | } 248 | 249 | if err = muxer.WriteTrailer(); err != nil { 250 | return 251 | } 252 | 253 | return 254 | } 255 | 256 | -------------------------------------------------------------------------------- /av/avutil/avutil.go: -------------------------------------------------------------------------------- 1 | package avutil 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "fmt" 7 | "bytes" 8 | "github.com/nareix/joy4/av" 9 | "net/url" 10 | "os" 11 | "path" 12 | ) 13 | 14 | type HandlerDemuxer struct { 15 | av.Demuxer 16 | r io.ReadCloser 17 | } 18 | 19 | func (self *HandlerDemuxer) Close() error { 20 | return self.r.Close() 21 | } 22 | 23 | type HandlerMuxer struct { 24 | av.Muxer 25 | w io.WriteCloser 26 | stage int 27 | } 28 | 29 | func (self *HandlerMuxer) WriteHeader(streams []av.CodecData) (err error) { 30 | if self.stage == 0 { 31 | if err = self.Muxer.WriteHeader(streams); err != nil { 32 | return 33 | } 34 | self.stage++ 35 | } 36 | return 37 | } 38 | 39 | func (self *HandlerMuxer) WriteTrailer() (err error) { 40 | if self.stage == 1 { 41 | self.stage++ 42 | if err = self.Muxer.WriteTrailer(); err != nil { 43 | return 44 | } 45 | } 46 | return 47 | } 48 | 49 | func (self *HandlerMuxer) Close() (err error) { 50 | if err = self.WriteTrailer(); err != nil { 51 | return 52 | } 53 | return self.w.Close() 54 | } 55 | 56 | type RegisterHandler struct { 57 | Ext string 58 | ReaderDemuxer func(io.Reader)av.Demuxer 59 | WriterMuxer func(io.Writer)av.Muxer 60 | UrlMuxer func(string)(bool,av.MuxCloser,error) 61 | UrlDemuxer func(string)(bool,av.DemuxCloser,error) 62 | UrlReader func(string)(bool,io.ReadCloser,error) 63 | Probe func([]byte)bool 64 | AudioEncoder func(av.CodecType)(av.AudioEncoder,error) 65 | AudioDecoder func(av.AudioCodecData)(av.AudioDecoder,error) 66 | ServerDemuxer func(string)(bool,av.DemuxCloser,error) 67 | ServerMuxer func(string)(bool,av.MuxCloser,error) 68 | CodecTypes []av.CodecType 69 | } 70 | 71 | type Handlers struct { 72 | handlers []RegisterHandler 73 | } 74 | 75 | func (self *Handlers) Add(fn func(*RegisterHandler)) { 76 | handler := &RegisterHandler{} 77 | fn(handler) 78 | self.handlers = append(self.handlers, *handler) 79 | } 80 | 81 | func (self *Handlers) openUrl(u *url.URL, uri string) (r io.ReadCloser, err error) { 82 | if u != nil && u.Scheme != "" { 83 | for _, handler := range self.handlers { 84 | if handler.UrlReader != nil { 85 | var ok bool 86 | if ok, r, err = handler.UrlReader(uri); ok { 87 | return 88 | } 89 | } 90 | } 91 | err = fmt.Errorf("avutil: openUrl %s failed", uri) 92 | } else { 93 | r, err = os.Open(uri) 94 | } 95 | return 96 | } 97 | 98 | func (self *Handlers) createUrl(u *url.URL, uri string) (w io.WriteCloser, err error) { 99 | w, err = os.Create(uri) 100 | return 101 | } 102 | 103 | func (self *Handlers) NewAudioEncoder(typ av.CodecType) (enc av.AudioEncoder, err error) { 104 | for _, handler := range self.handlers { 105 | if handler.AudioEncoder != nil { 106 | if enc, _ = handler.AudioEncoder(typ); enc != nil { 107 | return 108 | } 109 | } 110 | } 111 | err = fmt.Errorf("avutil: encoder", typ, "not found") 112 | return 113 | } 114 | 115 | func (self *Handlers) NewAudioDecoder(codec av.AudioCodecData) (dec av.AudioDecoder, err error) { 116 | for _, handler := range self.handlers { 117 | if handler.AudioDecoder != nil { 118 | if dec, _ = handler.AudioDecoder(codec); dec != nil { 119 | return 120 | } 121 | } 122 | } 123 | err = fmt.Errorf("avutil: decoder", codec.Type(), "not found") 124 | return 125 | } 126 | 127 | func (self *Handlers) Open(uri string) (demuxer av.DemuxCloser, err error) { 128 | listen := false 129 | if strings.HasPrefix(uri, "listen:") { 130 | uri = uri[len("listen:"):] 131 | listen = true 132 | } 133 | 134 | for _, handler := range self.handlers { 135 | if listen { 136 | if handler.ServerDemuxer != nil { 137 | var ok bool 138 | if ok, demuxer, err = handler.ServerDemuxer(uri); ok { 139 | return 140 | } 141 | } 142 | } else { 143 | if handler.UrlDemuxer != nil { 144 | var ok bool 145 | if ok, demuxer, err = handler.UrlDemuxer(uri); ok { 146 | return 147 | } 148 | } 149 | } 150 | } 151 | 152 | var r io.ReadCloser 153 | var ext string 154 | var u *url.URL 155 | if u, _ = url.Parse(uri); u != nil && u.Scheme != "" { 156 | ext = path.Ext(u.Path) 157 | } else { 158 | ext = path.Ext(uri) 159 | } 160 | 161 | if ext != "" { 162 | for _, handler := range self.handlers { 163 | if handler.Ext == ext { 164 | if handler.ReaderDemuxer != nil { 165 | if r, err = self.openUrl(u, uri); err != nil { 166 | return 167 | } 168 | demuxer = &HandlerDemuxer{ 169 | Demuxer: handler.ReaderDemuxer(r), 170 | r: r, 171 | } 172 | return 173 | } 174 | } 175 | } 176 | } 177 | 178 | var probebuf [1024]byte 179 | if r, err = self.openUrl(u, uri); err != nil { 180 | return 181 | } 182 | if _, err = io.ReadFull(r, probebuf[:]); err != nil { 183 | return 184 | } 185 | 186 | for _, handler := range self.handlers { 187 | if handler.Probe != nil && handler.Probe(probebuf[:]) && handler.ReaderDemuxer != nil { 188 | var _r io.Reader 189 | if rs, ok := r.(io.ReadSeeker); ok { 190 | if _, err = rs.Seek(0, 0); err != nil { 191 | return 192 | } 193 | _r = rs 194 | } else { 195 | _r = io.MultiReader(bytes.NewReader(probebuf[:]), r) 196 | } 197 | demuxer = &HandlerDemuxer{ 198 | Demuxer: handler.ReaderDemuxer(_r), 199 | r: r, 200 | } 201 | return 202 | } 203 | } 204 | 205 | r.Close() 206 | err = fmt.Errorf("avutil: open %s failed", uri) 207 | return 208 | } 209 | 210 | func (self *Handlers) Create(uri string) (muxer av.MuxCloser, err error) { 211 | _, muxer, err = self.FindCreate(uri) 212 | return 213 | } 214 | 215 | func (self *Handlers) FindCreate(uri string) (handler RegisterHandler, muxer av.MuxCloser, err error) { 216 | listen := false 217 | if strings.HasPrefix(uri, "listen:") { 218 | uri = uri[len("listen:"):] 219 | listen = true 220 | } 221 | 222 | for _, handler = range self.handlers { 223 | if listen { 224 | if handler.ServerMuxer != nil { 225 | var ok bool 226 | if ok, muxer, err = handler.ServerMuxer(uri); ok { 227 | return 228 | } 229 | } 230 | } else { 231 | if handler.UrlMuxer != nil { 232 | var ok bool 233 | if ok, muxer, err = handler.UrlMuxer(uri); ok { 234 | return 235 | } 236 | } 237 | } 238 | } 239 | 240 | var ext string 241 | var u *url.URL 242 | if u, _ = url.Parse(uri); u != nil && u.Scheme != "" { 243 | ext = path.Ext(u.Path) 244 | } else { 245 | ext = path.Ext(uri) 246 | } 247 | 248 | if ext != "" { 249 | for _, handler = range self.handlers { 250 | if handler.Ext == ext && handler.WriterMuxer != nil { 251 | var w io.WriteCloser 252 | if w, err = self.createUrl(u, uri); err != nil { 253 | return 254 | } 255 | muxer = &HandlerMuxer{ 256 | Muxer: handler.WriterMuxer(w), 257 | w: w, 258 | } 259 | return 260 | } 261 | } 262 | } 263 | 264 | err = fmt.Errorf("avutil: create muxer %s failed", uri) 265 | return 266 | } 267 | 268 | var DefaultHandlers = &Handlers{} 269 | 270 | func Open(url string) (demuxer av.DemuxCloser, err error) { 271 | return DefaultHandlers.Open(url) 272 | } 273 | 274 | func Create(url string) (muxer av.MuxCloser, err error) { 275 | return DefaultHandlers.Create(url) 276 | } 277 | 278 | func CopyPackets(dst av.PacketWriter, src av.PacketReader) (err error) { 279 | for { 280 | var pkt av.Packet 281 | if pkt, err = src.ReadPacket(); err != nil { 282 | if err == io.EOF { 283 | break 284 | } 285 | return 286 | } 287 | if err = dst.WritePacket(pkt); err != nil { 288 | return 289 | } 290 | } 291 | return 292 | } 293 | 294 | func CopyFile(dst av.Muxer, src av.Demuxer) (err error) { 295 | var streams []av.CodecData 296 | if streams, err = src.Streams(); err != nil { 297 | return 298 | } 299 | if err = dst.WriteHeader(streams); err != nil { 300 | return 301 | } 302 | if err = CopyPackets(dst, src); err != nil { 303 | if err != io.EOF { 304 | return 305 | } 306 | } 307 | if err = dst.WriteTrailer(); err != nil { 308 | return 309 | } 310 | return 311 | } 312 | 313 | // CopyPacketsFromKeyframe copies packets from src to dst, but start copying video packets only after receiving a first keyframe 314 | func CopyPacketsFromKeyframe(dst av.PacketWriter, src av.PacketReader, videoIdx int8) (err error) { 315 | videoInit := false 316 | for { 317 | var pkt av.Packet 318 | if pkt, err = src.ReadPacket(); err != nil { 319 | if err == io.EOF { 320 | break 321 | } 322 | return 323 | } 324 | 325 | // start copying video only after receiving a key frame 326 | if !videoInit && pkt.Idx == videoIdx && pkt.IsKeyFrame { 327 | videoInit = true 328 | } 329 | 330 | if pkt.Idx == videoIdx && !videoInit { 331 | continue 332 | } 333 | 334 | if err = dst.WritePacket(pkt); err != nil { 335 | return 336 | } 337 | } 338 | return 339 | } 340 | 341 | // CopyFileFromKeyframe acts like CopyFile but uses CopyPacketsFromKeyframe to copy 342 | // the video stream only after receiving a key frame 343 | func CopyFileFromKeyframe(dst av.Muxer, src av.Demuxer, videoIdx int8) (err error) { 344 | var streams []av.CodecData 345 | if streams, err = src.Streams(); err != nil { 346 | return 347 | } 348 | if err = dst.WriteHeader(streams); err != nil { 349 | return 350 | } 351 | if err = CopyPacketsFromKeyframe(dst, src, videoIdx); err != nil { 352 | if err != io.EOF { 353 | return 354 | } 355 | } 356 | if err = dst.WriteTrailer(); err != nil { 357 | return 358 | } 359 | return 360 | } 361 | -------------------------------------------------------------------------------- /av/pktque/buf.go: -------------------------------------------------------------------------------- 1 | package pktque 2 | 3 | import ( 4 | "github.com/nareix/joy4/av" 5 | ) 6 | 7 | type Buf struct { 8 | Head, Tail BufPos 9 | pkts []av.Packet 10 | Size int 11 | Count int 12 | } 13 | 14 | func NewBuf() *Buf { 15 | return &Buf{ 16 | pkts: make([]av.Packet, 64), 17 | } 18 | } 19 | 20 | func (self *Buf) Pop() av.Packet { 21 | if self.Count == 0 { 22 | panic("pktque.Buf: Pop() when count == 0") 23 | } 24 | 25 | i := int(self.Head) & (len(self.pkts) - 1) 26 | pkt := self.pkts[i] 27 | self.pkts[i] = av.Packet{} 28 | self.Size -= len(pkt.Data) 29 | self.Head++ 30 | self.Count-- 31 | 32 | return pkt 33 | } 34 | 35 | func (self *Buf) grow() { 36 | newpkts := make([]av.Packet, len(self.pkts)*2) 37 | for i := self.Head; i.LT(self.Tail); i++ { 38 | newpkts[int(i)&(len(newpkts)-1)] = self.pkts[int(i)&(len(self.pkts)-1)] 39 | } 40 | self.pkts = newpkts 41 | } 42 | 43 | func (self *Buf) Push(pkt av.Packet) { 44 | if self.Count == len(self.pkts) { 45 | self.grow() 46 | } 47 | self.pkts[int(self.Tail)&(len(self.pkts)-1)] = pkt 48 | self.Tail++ 49 | self.Count++ 50 | self.Size += len(pkt.Data) 51 | } 52 | 53 | func (self *Buf) Get(pos BufPos) av.Packet { 54 | return self.pkts[int(pos)&(len(self.pkts)-1)] 55 | } 56 | 57 | func (self *Buf) IsValidPos(pos BufPos) bool { 58 | return pos.GE(self.Head) && pos.LT(self.Tail) 59 | } 60 | 61 | type BufPos int 62 | 63 | func (self BufPos) LT(pos BufPos) bool { 64 | return self-pos < 0 65 | } 66 | 67 | func (self BufPos) GE(pos BufPos) bool { 68 | return self-pos >= 0 69 | } 70 | 71 | func (self BufPos) GT(pos BufPos) bool { 72 | return self-pos > 0 73 | } 74 | -------------------------------------------------------------------------------- /av/pktque/filters.go: -------------------------------------------------------------------------------- 1 | 2 | // Package pktque provides packet Filter interface and structures used by other components. 3 | package pktque 4 | 5 | import ( 6 | "time" 7 | "github.com/nareix/joy4/av" 8 | ) 9 | 10 | type Filter interface { 11 | // Change packet time or drop packet 12 | ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) 13 | } 14 | 15 | // Combine multiple Filters into one, ModifyPacket will be called in order. 16 | type Filters []Filter 17 | 18 | func (self Filters) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) { 19 | for _, filter := range self { 20 | if drop, err = filter.ModifyPacket(pkt, streams, videoidx, audioidx); err != nil { 21 | return 22 | } 23 | if drop { 24 | return 25 | } 26 | } 27 | return 28 | } 29 | 30 | // Wrap origin Demuxer and Filter into a new Demuxer, when read this Demuxer filters will be called. 31 | type FilterDemuxer struct { 32 | av.Demuxer 33 | Filter Filter 34 | streams []av.CodecData 35 | videoidx int 36 | audioidx int 37 | } 38 | 39 | func (self FilterDemuxer) ReadPacket() (pkt av.Packet, err error) { 40 | if self.streams == nil { 41 | if self.streams, err = self.Demuxer.Streams(); err != nil { 42 | return 43 | } 44 | for i, stream := range self.streams { 45 | if stream.Type().IsVideo() { 46 | self.videoidx = i 47 | } else if stream.Type().IsAudio() { 48 | self.audioidx = i 49 | } 50 | } 51 | } 52 | 53 | for { 54 | if pkt, err = self.Demuxer.ReadPacket(); err != nil { 55 | return 56 | } 57 | var drop bool 58 | if drop, err = self.Filter.ModifyPacket(&pkt, self.streams, self.videoidx, self.audioidx); err != nil { 59 | return 60 | } 61 | if !drop { 62 | break 63 | } 64 | } 65 | 66 | return 67 | } 68 | 69 | // Drop packets until first video key frame arrived. 70 | type WaitKeyFrame struct { 71 | ok bool 72 | } 73 | 74 | func (self *WaitKeyFrame) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) { 75 | if !self.ok && pkt.Idx == int8(videoidx) && pkt.IsKeyFrame { 76 | self.ok = true 77 | } 78 | drop = !self.ok 79 | return 80 | } 81 | 82 | // Fix incorrect packet timestamps. 83 | type FixTime struct { 84 | zerobase time.Duration 85 | incrbase time.Duration 86 | lasttime time.Duration 87 | StartFromZero bool // make timestamp start from zero 88 | MakeIncrement bool // force timestamp increment 89 | } 90 | 91 | func (self *FixTime) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) { 92 | if self.StartFromZero { 93 | if self.zerobase == 0 { 94 | self.zerobase = pkt.Time 95 | } 96 | pkt.Time -= self.zerobase 97 | } 98 | 99 | if self.MakeIncrement { 100 | pkt.Time -= self.incrbase 101 | if self.lasttime == 0 { 102 | self.lasttime = pkt.Time 103 | } 104 | if pkt.Time < self.lasttime || pkt.Time > self.lasttime+time.Millisecond*500 { 105 | self.incrbase += pkt.Time - self.lasttime 106 | pkt.Time = self.lasttime 107 | } 108 | self.lasttime = pkt.Time 109 | } 110 | 111 | return 112 | } 113 | 114 | // Drop incorrect packets to make A/V sync. 115 | type AVSync struct { 116 | MaxTimeDiff time.Duration 117 | time []time.Duration 118 | } 119 | 120 | func (self *AVSync) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) { 121 | if self.time == nil { 122 | self.time = make([]time.Duration, len(streams)) 123 | if self.MaxTimeDiff == 0 { 124 | self.MaxTimeDiff = time.Millisecond*500 125 | } 126 | } 127 | 128 | start, end, correctable, correcttime := self.check(int(pkt.Idx)) 129 | if pkt.Time >= start && pkt.Time < end { 130 | self.time[pkt.Idx] = pkt.Time 131 | } else { 132 | if correctable { 133 | pkt.Time = correcttime 134 | for i := range self.time { 135 | self.time[i] = correcttime 136 | } 137 | } else { 138 | drop = true 139 | } 140 | } 141 | return 142 | } 143 | 144 | func (self *AVSync) check(i int) (start time.Duration, end time.Duration, correctable bool, correcttime time.Duration) { 145 | minidx := -1 146 | maxidx := -1 147 | for j := range self.time { 148 | if minidx == -1 || self.time[j] < self.time[minidx] { 149 | minidx = j 150 | } 151 | if maxidx == -1 || self.time[j] > self.time[maxidx] { 152 | maxidx = j 153 | } 154 | } 155 | allthesame := self.time[minidx] == self.time[maxidx] 156 | 157 | if i == maxidx { 158 | if allthesame { 159 | correctable = true 160 | } else { 161 | correctable = false 162 | } 163 | } else { 164 | correctable = true 165 | } 166 | 167 | start = self.time[minidx] 168 | end = start + self.MaxTimeDiff 169 | correcttime = start + time.Millisecond*40 170 | return 171 | } 172 | 173 | // Make packets reading speed as same as walltime, effect like ffmpeg -re option. 174 | type Walltime struct { 175 | firsttime time.Time 176 | } 177 | 178 | func (self *Walltime) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) { 179 | if pkt.Idx == 0 { 180 | if self.firsttime.IsZero() { 181 | self.firsttime = time.Now() 182 | } 183 | pkttime := self.firsttime.Add(pkt.Time) 184 | delta := pkttime.Sub(time.Now()) 185 | if delta > 0 { 186 | time.Sleep(delta) 187 | } 188 | } 189 | return 190 | } 191 | 192 | -------------------------------------------------------------------------------- /av/pktque/timeline.go: -------------------------------------------------------------------------------- 1 | package pktque 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | /* 8 | pop push 9 | 10 | seg seg seg 11 | |--------| |---------| |---| 12 | 20ms 40ms 5ms 13 | ----------------- time --------------------> 14 | headtm tailtm 15 | */ 16 | 17 | type tlSeg struct { 18 | tm, dur time.Duration 19 | } 20 | 21 | type Timeline struct { 22 | segs []tlSeg 23 | headtm time.Duration 24 | } 25 | 26 | func (self *Timeline) Push(tm time.Duration, dur time.Duration) { 27 | if len(self.segs) > 0 { 28 | tail := self.segs[len(self.segs)-1] 29 | diff := tm-(tail.tm+tail.dur) 30 | if diff < 0 { 31 | tm -= diff 32 | } 33 | } 34 | self.segs = append(self.segs, tlSeg{tm, dur}) 35 | } 36 | 37 | func (self *Timeline) Pop(dur time.Duration) (tm time.Duration) { 38 | if len(self.segs) == 0 { 39 | return self.headtm 40 | } 41 | 42 | tm = self.segs[0].tm 43 | for dur > 0 && len(self.segs) > 0 { 44 | seg := &self.segs[0] 45 | sub := dur 46 | if seg.dur < sub { 47 | sub = seg.dur 48 | } 49 | seg.dur -= sub 50 | dur -= sub 51 | seg.tm += sub 52 | self.headtm += sub 53 | if seg.dur == 0 { 54 | copy(self.segs[0:], self.segs[1:]) 55 | self.segs = self.segs[:len(self.segs)-1] 56 | } 57 | } 58 | 59 | return 60 | } 61 | 62 | -------------------------------------------------------------------------------- /av/pubsub/queue.go: -------------------------------------------------------------------------------- 1 | // Packege pubsub implements publisher-subscribers model used in multi-channel streaming. 2 | package pubsub 3 | 4 | import ( 5 | "github.com/nareix/joy4/av" 6 | "github.com/nareix/joy4/av/pktque" 7 | "io" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // time 13 | // -----------------> 14 | // 15 | // V-A-V-V-A-V-V-A-V-V 16 | // | | 17 | // 0 5 10 18 | // head tail 19 | // oldest latest 20 | // 21 | 22 | // One publisher and multiple subscribers thread-safe packet buffer queue. 23 | type Queue struct { 24 | buf *pktque.Buf 25 | head, tail int 26 | lock *sync.RWMutex 27 | cond *sync.Cond 28 | curgopcount, maxgopcount int 29 | streams []av.CodecData 30 | videoidx int 31 | closed bool 32 | } 33 | 34 | func NewQueue() *Queue { 35 | q := &Queue{} 36 | q.buf = pktque.NewBuf() 37 | q.maxgopcount = 2 38 | q.lock = &sync.RWMutex{} 39 | q.cond = sync.NewCond(q.lock.RLocker()) 40 | q.videoidx = -1 41 | return q 42 | } 43 | 44 | func (self *Queue) SetMaxGopCount(n int) { 45 | self.lock.Lock() 46 | self.maxgopcount = n 47 | self.lock.Unlock() 48 | return 49 | } 50 | 51 | func (self *Queue) WriteHeader(streams []av.CodecData) error { 52 | self.lock.Lock() 53 | 54 | self.streams = streams 55 | for i, stream := range streams { 56 | if stream.Type().IsVideo() { 57 | self.videoidx = i 58 | } 59 | } 60 | self.cond.Broadcast() 61 | 62 | self.lock.Unlock() 63 | 64 | return nil 65 | } 66 | 67 | func (self *Queue) WriteTrailer() error { 68 | return nil 69 | } 70 | 71 | // After Close() called, all QueueCursor's ReadPacket will return io.EOF. 72 | func (self *Queue) Close() (err error) { 73 | self.lock.Lock() 74 | 75 | self.closed = true 76 | self.cond.Broadcast() 77 | 78 | self.lock.Unlock() 79 | return 80 | } 81 | 82 | // Put packet into buffer, old packets will be discared. 83 | func (self *Queue) WritePacket(pkt av.Packet) (err error) { 84 | self.lock.Lock() 85 | 86 | self.buf.Push(pkt) 87 | if pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame { 88 | self.curgopcount++ 89 | } 90 | 91 | for self.curgopcount >= self.maxgopcount && self.buf.Count > 1 { 92 | pkt := self.buf.Pop() 93 | if pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame { 94 | self.curgopcount-- 95 | } 96 | if self.curgopcount < self.maxgopcount { 97 | break 98 | } 99 | } 100 | //println("shrink", self.curgopcount, self.maxgopcount, self.buf.Head, self.buf.Tail, "count", self.buf.Count, "size", self.buf.Size) 101 | 102 | self.cond.Broadcast() 103 | 104 | self.lock.Unlock() 105 | return 106 | } 107 | 108 | 109 | // GetVideoIdx returns the internal index of the video stream 110 | func (self *Queue) GetVideoIdx() (idx int8) { 111 | return int8(self.videoidx) 112 | } 113 | 114 | type QueueCursor struct { 115 | que *Queue 116 | pos pktque.BufPos 117 | gotpos bool 118 | init func(buf *pktque.Buf, videoidx int) pktque.BufPos 119 | } 120 | 121 | func (self *Queue) newCursor() *QueueCursor { 122 | return &QueueCursor{ 123 | que: self, 124 | } 125 | } 126 | 127 | // Create cursor position at latest packet. 128 | func (self *Queue) Latest() *QueueCursor { 129 | cursor := self.newCursor() 130 | cursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos { 131 | return buf.Tail 132 | } 133 | return cursor 134 | } 135 | 136 | // Create cursor position at oldest buffered packet. 137 | func (self *Queue) Oldest() *QueueCursor { 138 | cursor := self.newCursor() 139 | cursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos { 140 | return buf.Head 141 | } 142 | return cursor 143 | } 144 | 145 | // Create cursor position at specific time in buffered packets. 146 | func (self *Queue) DelayedTime(dur time.Duration) *QueueCursor { 147 | cursor := self.newCursor() 148 | cursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos { 149 | i := buf.Tail - 1 150 | if buf.IsValidPos(i) { 151 | end := buf.Get(i) 152 | for buf.IsValidPos(i) { 153 | if end.Time-buf.Get(i).Time > dur { 154 | break 155 | } 156 | i-- 157 | } 158 | } 159 | return i 160 | } 161 | return cursor 162 | } 163 | 164 | // Create cursor position at specific delayed GOP count in buffered packets. 165 | func (self *Queue) DelayedGopCount(n int) *QueueCursor { 166 | cursor := self.newCursor() 167 | cursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos { 168 | i := buf.Tail - 1 169 | if videoidx != -1 { 170 | for gop := 0; buf.IsValidPos(i) && gop < n; i-- { 171 | pkt := buf.Get(i) 172 | if pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame { 173 | gop++ 174 | } 175 | } 176 | } 177 | return i 178 | } 179 | return cursor 180 | } 181 | 182 | func (self *QueueCursor) Streams() (streams []av.CodecData, err error) { 183 | self.que.cond.L.Lock() 184 | for self.que.streams == nil && !self.que.closed { 185 | self.que.cond.Wait() 186 | } 187 | if self.que.streams != nil { 188 | streams = self.que.streams 189 | } else { 190 | err = io.EOF 191 | } 192 | self.que.cond.L.Unlock() 193 | return 194 | } 195 | 196 | // ReadPacket will not consume packets in Queue, it's just a cursor. 197 | func (self *QueueCursor) ReadPacket() (pkt av.Packet, err error) { 198 | self.que.cond.L.Lock() 199 | buf := self.que.buf 200 | if !self.gotpos { 201 | self.pos = self.init(buf, self.que.videoidx) 202 | self.gotpos = true 203 | } 204 | for { 205 | if self.pos.LT(buf.Head) { 206 | self.pos = buf.Head 207 | } else if self.pos.GT(buf.Tail) { 208 | self.pos = buf.Tail 209 | } 210 | if buf.IsValidPos(self.pos) { 211 | pkt = buf.Get(self.pos) 212 | self.pos++ 213 | break 214 | } 215 | if self.que.closed { 216 | err = io.EOF 217 | break 218 | } 219 | self.que.cond.Wait() 220 | } 221 | self.que.cond.L.Unlock() 222 | return 223 | } 224 | -------------------------------------------------------------------------------- /av/transcode/transcode.go: -------------------------------------------------------------------------------- 1 | 2 | // Package transcoder implements Transcoder based on Muxer/Demuxer and AudioEncoder/AudioDecoder interface. 3 | package transcode 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | "github.com/nareix/joy4/av" 9 | "github.com/nareix/joy4/av/pktque" 10 | "github.com/nareix/joy4/cgo/ffmpeg" 11 | ) 12 | 13 | var Debug bool 14 | 15 | type tStream struct { 16 | codec av.CodecData 17 | timeline *pktque.Timeline 18 | aencodec, adecodec av.AudioCodecData 19 | aenc av.AudioEncoder 20 | adec av.AudioDecoder 21 | vencodec, vdecodec av.VideoCodecData 22 | venc *ffmpeg.VideoEncoder 23 | vdec *ffmpeg.VideoDecoder 24 | } 25 | 26 | type Options struct { 27 | // check if transcode is needed, and create the AudioDecoder and AudioEncoder. 28 | FindAudioDecoderEncoder func(codec av.AudioCodecData, i int) ( 29 | need bool, dec av.AudioDecoder, enc av.AudioEncoder, err error, 30 | ) 31 | FindVideoDecoderEncoder func(codec av.VideoCodecData, i int) ( 32 | need bool, dec *ffmpeg.VideoDecoder, enc *ffmpeg.VideoEncoder, err error, 33 | ) 34 | } 35 | 36 | type Transcoder struct { 37 | streams []*tStream 38 | } 39 | 40 | func NewTranscoder(streams []av.CodecData, options Options) (_self *Transcoder, err error) { 41 | self := &Transcoder{} 42 | self.streams = []*tStream{} 43 | 44 | for i, stream := range streams { 45 | ts := &tStream{codec: stream} 46 | if stream.Type().IsAudio() { 47 | if options.FindAudioDecoderEncoder != nil { 48 | var ok bool 49 | var enc av.AudioEncoder 50 | var dec av.AudioDecoder 51 | ok, dec, enc, err = options.FindAudioDecoderEncoder(stream.(av.AudioCodecData), i) 52 | if ok { 53 | if err != nil { 54 | return 55 | } 56 | ts.timeline = &pktque.Timeline{} 57 | if ts.codec, err = enc.CodecData(); err != nil { 58 | return 59 | } 60 | ts.aencodec = ts.codec.(av.AudioCodecData) 61 | ts.adecodec = stream.(av.AudioCodecData) 62 | ts.aenc = enc 63 | ts.adec = dec 64 | } 65 | } 66 | } else if stream.Type().IsVideo() { 67 | if options.FindVideoDecoderEncoder != nil { 68 | var ok bool 69 | var enc *ffmpeg.VideoEncoder 70 | var dec *ffmpeg.VideoDecoder 71 | ok, dec, enc, err = options.FindVideoDecoderEncoder(stream.(av.VideoCodecData), i) 72 | if ok { 73 | if err != nil { 74 | return 75 | } 76 | ts.timeline = &pktque.Timeline{} 77 | if ts.codec, err = enc.CodecData(); err != nil { 78 | return 79 | } 80 | ts.vencodec = ts.codec.(av.VideoCodecData) 81 | ts.vdecodec = stream.(av.VideoCodecData) 82 | ts.venc = enc 83 | ts.vdec = dec 84 | } 85 | } 86 | } 87 | self.streams = append(self.streams, ts) 88 | } 89 | 90 | _self = self 91 | return 92 | } 93 | 94 | func (self *tStream) audioDecodeAndEncode(inpkt av.Packet) (outpkts []av.Packet, err error) { 95 | var dur time.Duration 96 | var frame av.AudioFrame 97 | var ok bool 98 | if ok, frame, err = self.adec.Decode(inpkt.Data); err != nil { 99 | return 100 | } 101 | if !ok { 102 | return 103 | } 104 | 105 | if dur, err = self.adecodec.PacketDuration(inpkt.Data); err != nil { 106 | err = fmt.Errorf("transcode: PacketDuration() failed for input audio stream #%d", inpkt.Idx) 107 | return 108 | } 109 | 110 | if Debug { 111 | fmt.Println("transcode: push", inpkt.Time, dur) 112 | } 113 | self.timeline.Push(inpkt.Time, dur) 114 | 115 | var _outpkts [][]byte 116 | if _outpkts, err = self.aenc.Encode(frame); err != nil { 117 | return 118 | } 119 | for _, _outpkt := range _outpkts { 120 | if dur, err = self.aencodec.PacketDuration(_outpkt); err != nil { 121 | err = fmt.Errorf("transcode: PacketDuration() failed for output audio stream #%d", inpkt.Idx) 122 | return 123 | } 124 | outpkt := av.Packet{Idx: inpkt.Idx, Data: _outpkt} 125 | outpkt.Time = self.timeline.Pop(dur) 126 | 127 | if Debug { 128 | fmt.Println("transcode: pop", outpkt.Time, dur) 129 | } 130 | 131 | outpkts = append(outpkts, outpkt) 132 | } 133 | 134 | return 135 | } 136 | 137 | func (self *tStream) videoDecodeAndEncode(inpkt av.Packet) (outpkts []av.Packet, err error) { 138 | var dur time.Duration 139 | var frame *ffmpeg.VideoFrame 140 | if frame, err = self.vdec.Decode(inpkt.Data); err != nil || frame == nil { 141 | return 142 | } 143 | 144 | if dur, err = self.vdecodec.PacketDuration(inpkt.Data); err != nil { 145 | err = fmt.Errorf("transcode: PacketDuration() failed for input video stream #%d", inpkt.Idx) 146 | return 147 | } 148 | 149 | if Debug { 150 | fmt.Println("transcode: push", inpkt.Time, dur) 151 | } 152 | self.timeline.Push(inpkt.Time, dur) 153 | 154 | var _outpkts []av.Packet 155 | if _outpkts, err = self.venc.Encode(frame); err != nil { 156 | return 157 | } 158 | for _, _outpkt := range _outpkts { 159 | if fpsNum, fpsDen := self.vencodec.Framerate(); fpsNum <= 0 || fpsDen <= 0 { 160 | // FIXME this is a bit hacky 161 | // Read codec data after encoding (because the sps and pps are not ready before the first keyframe is encoded) 162 | var codecData av.VideoCodecData 163 | codecData, err = self.venc.CodecData() 164 | if err != nil { 165 | return 166 | } 167 | self.vencodec = codecData.(av.VideoCodecData) 168 | } 169 | if dur, err = self.vencodec.PacketDuration(_outpkt.Data); err != nil { 170 | err = fmt.Errorf("transcode: PacketDuration() failed for output video stream #%d", inpkt.Idx) 171 | return 172 | } 173 | // TODO probably not needed now that _outpkt is an av.Packet 174 | outpkt := av.Packet{IsKeyFrame: _outpkt.IsKeyFrame, Idx: inpkt.Idx, Data: _outpkt.Data} 175 | outpkt.Time = self.timeline.Pop(dur) 176 | 177 | if Debug { 178 | fmt.Println("transcode: pop", outpkt.Time, dur) 179 | } 180 | 181 | outpkts = append(outpkts, outpkt) 182 | } 183 | return 184 | } 185 | 186 | // Do the transcode. 187 | // 188 | // In audio transcoding one Packet may transcode into many Packets 189 | // packet time will be adjusted automatically. 190 | func (self *Transcoder) Do(pkt av.Packet) (out []av.Packet, err error) { 191 | stream := self.streams[pkt.Idx] 192 | if stream.aenc != nil && stream.adec != nil { 193 | if out, err = stream.audioDecodeAndEncode(pkt); err != nil { 194 | return 195 | } 196 | } else if stream.venc != nil && stream.vdec != nil { 197 | if out, err = stream.videoDecodeAndEncode(pkt); err != nil { 198 | return 199 | } 200 | } else { 201 | out = append(out, pkt) 202 | } 203 | return 204 | } 205 | 206 | // Get CodecDatas after transcoding. 207 | func (self *Transcoder) Streams() (streams []av.CodecData, err error) { 208 | for _, stream := range self.streams { 209 | streams = append(streams, stream.codec) 210 | } 211 | return 212 | } 213 | 214 | // Close transcoder, close related encoder and decoders. 215 | func (self *Transcoder) Close() (err error) { 216 | for _, stream := range self.streams { 217 | if stream.aenc != nil { 218 | stream.aenc.Close() 219 | stream.aenc = nil 220 | } 221 | if stream.adec != nil { 222 | stream.adec.Close() 223 | stream.adec = nil 224 | } 225 | if stream.venc != nil { 226 | stream.venc.Close() 227 | stream.venc = nil 228 | } 229 | if stream.vdec != nil { 230 | stream.vdec.Close() 231 | stream.vdec = nil 232 | } 233 | } 234 | self.streams = nil 235 | return 236 | } 237 | 238 | // Wrap transcoder and origin Muxer into new Muxer. 239 | // Write to new Muxer will do transcoding automatically. 240 | type Muxer struct { 241 | av.Muxer // origin Muxer 242 | Options // transcode options 243 | transcoder *Transcoder 244 | } 245 | 246 | func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { 247 | if self.transcoder, err = NewTranscoder(streams, self.Options); err != nil { 248 | return 249 | } 250 | var newstreams []av.CodecData 251 | if newstreams, err = self.transcoder.Streams(); err != nil { 252 | return 253 | } 254 | if err = self.Muxer.WriteHeader(newstreams); err != nil { 255 | return 256 | } 257 | return 258 | } 259 | 260 | func (self *Muxer) WritePacket(pkt av.Packet) (err error) { 261 | var outpkts []av.Packet 262 | if outpkts, err = self.transcoder.Do(pkt); err != nil { 263 | return 264 | } 265 | for _, pkt := range outpkts { 266 | if err = self.Muxer.WritePacket(pkt); err != nil { 267 | return 268 | } 269 | } 270 | return 271 | } 272 | 273 | func (self *Muxer) Close() (err error) { 274 | if self.transcoder != nil { 275 | return self.transcoder.Close() 276 | } 277 | return 278 | } 279 | 280 | // Wrap transcoder and origin Demuxer into new Demuxer. 281 | // Read this Demuxer will do transcoding automatically. 282 | type Demuxer struct { 283 | av.Demuxer 284 | Options 285 | transcoder *Transcoder 286 | outpkts []av.Packet 287 | } 288 | 289 | func (self *Demuxer) prepare() (err error) { 290 | if self.transcoder == nil { 291 | var streams []av.CodecData 292 | if streams, err = self.Demuxer.Streams(); err != nil { 293 | return 294 | } 295 | if self.transcoder, err = NewTranscoder(streams, self.Options); err != nil { 296 | return 297 | } 298 | } 299 | return 300 | } 301 | 302 | func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { 303 | if err = self.prepare(); err != nil { 304 | return 305 | } 306 | for { 307 | if len(self.outpkts) > 0 { 308 | pkt = self.outpkts[0] 309 | self.outpkts = self.outpkts[1:] 310 | return 311 | } 312 | var rpkt av.Packet 313 | if rpkt, err = self.Demuxer.ReadPacket(); err != nil { 314 | return 315 | } 316 | if self.outpkts, err = self.transcoder.Do(rpkt); err != nil { 317 | return 318 | } 319 | } 320 | return 321 | } 322 | 323 | func (self *Demuxer) Streams() (streams []av.CodecData, err error) { 324 | if err = self.prepare(); err != nil { 325 | return 326 | } 327 | return self.transcoder.Streams() 328 | } 329 | 330 | func (self *Demuxer) Close() (err error) { 331 | if self.transcoder != nil { 332 | return self.transcoder.Close() 333 | } 334 | return 335 | } 336 | -------------------------------------------------------------------------------- /cgo/ffmpeg/ffmpeg.go: -------------------------------------------------------------------------------- 1 | package ffmpeg 2 | 3 | /* 4 | #cgo LDFLAGS: -lavformat -lavutil -lavcodec -lavresample -lswscale 5 | #include "ffmpeg.h" 6 | void ffinit() { 7 | av_register_all(); 8 | } 9 | */ 10 | import "C" 11 | import ( 12 | "runtime" 13 | "unsafe" 14 | ) 15 | 16 | const ( 17 | QUIET = int(C.AV_LOG_QUIET) 18 | PANIC = int(C.AV_LOG_PANIC) 19 | FATAL = int(C.AV_LOG_FATAL) 20 | ERROR = int(C.AV_LOG_ERROR) 21 | WARNING = int(C.AV_LOG_WARNING) 22 | INFO = int(C.AV_LOG_INFO) 23 | VERBOSE = int(C.AV_LOG_VERBOSE) 24 | DEBUG = int(C.AV_LOG_DEBUG) 25 | TRACE = int(C.AV_LOG_TRACE) 26 | ) 27 | 28 | func HasEncoder(name string) bool { 29 | return C.avcodec_find_encoder_by_name(C.CString(name)) != nil 30 | } 31 | 32 | func HasDecoder(name string) bool { 33 | return C.avcodec_find_decoder_by_name(C.CString(name)) != nil 34 | } 35 | 36 | //func EncodersList() []string 37 | //func DecodersList() []string 38 | 39 | func SetLogLevel(level int) { 40 | C.av_log_set_level(C.int(level)) 41 | } 42 | 43 | func init() { 44 | C.ffinit() 45 | } 46 | 47 | type ffctx struct { 48 | ff C.FFCtx 49 | } 50 | 51 | func newFFCtxByCodec(codec *C.AVCodec) (ff *ffctx, err error) { 52 | ff = &ffctx{} 53 | ff.ff.codec = codec 54 | ff.ff.codecCtx = C.avcodec_alloc_context3(codec) 55 | ff.ff.profile = C.FF_PROFILE_UNKNOWN 56 | runtime.SetFinalizer(ff, freeFFCtx) 57 | return 58 | } 59 | 60 | func freeFFCtx(self *ffctx) { 61 | ff := &self.ff 62 | if ff.frame != nil { 63 | C.av_frame_free(&ff.frame) 64 | } 65 | if ff.codecCtx != nil { 66 | C.avcodec_close(ff.codecCtx) 67 | C.av_free(unsafe.Pointer(ff.codecCtx)) 68 | ff.codecCtx = nil 69 | } 70 | if ff.options != nil { 71 | C.av_dict_free(&ff.options) 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /cgo/ffmpeg/ffmpeg.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | typedef struct { 15 | AVCodec *codec; 16 | AVCodecContext *codecCtx; 17 | AVFrame *frame; 18 | AVDictionary *options; 19 | int profile; 20 | } FFCtx; 21 | 22 | static inline int avcodec_profile_name_to_int(AVCodec *codec, const char *name) { 23 | const AVProfile *p; 24 | for (p = codec->profiles; p != NULL && p->profile != FF_PROFILE_UNKNOWN; p++) 25 | if (!strcasecmp(p->name, name)) 26 | return p->profile; 27 | return FF_PROFILE_UNKNOWN; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /codec/aacparser/parser.go: -------------------------------------------------------------------------------- 1 | package aacparser 2 | 3 | import ( 4 | "github.com/nareix/joy4/utils/bits" 5 | "github.com/nareix/joy4/av" 6 | "time" 7 | "fmt" 8 | "bytes" 9 | "io" 10 | ) 11 | 12 | // copied from libavcodec/mpeg4audio.h 13 | const ( 14 | AOT_AAC_MAIN = 1 + iota ///< Y Main 15 | AOT_AAC_LC ///< Y Low Complexity 16 | AOT_AAC_SSR ///< N (code in SoC repo) Scalable Sample Rate 17 | AOT_AAC_LTP ///< Y Long Term Prediction 18 | AOT_SBR ///< Y Spectral Band Replication 19 | AOT_AAC_SCALABLE ///< N Scalable 20 | AOT_TWINVQ ///< N Twin Vector Quantizer 21 | AOT_CELP ///< N Code Excited Linear Prediction 22 | AOT_HVXC ///< N Harmonic Vector eXcitation Coding 23 | AOT_TTSI = 12 + iota ///< N Text-To-Speech Interface 24 | AOT_MAINSYNTH ///< N Main Synthesis 25 | AOT_WAVESYNTH ///< N Wavetable Synthesis 26 | AOT_MIDI ///< N General MIDI 27 | AOT_SAFX ///< N Algorithmic Synthesis and Audio Effects 28 | AOT_ER_AAC_LC ///< N Error Resilient Low Complexity 29 | AOT_ER_AAC_LTP = 19 + iota ///< N Error Resilient Long Term Prediction 30 | AOT_ER_AAC_SCALABLE ///< N Error Resilient Scalable 31 | AOT_ER_TWINVQ ///< N Error Resilient Twin Vector Quantizer 32 | AOT_ER_BSAC ///< N Error Resilient Bit-Sliced Arithmetic Coding 33 | AOT_ER_AAC_LD ///< N Error Resilient Low Delay 34 | AOT_ER_CELP ///< N Error Resilient Code Excited Linear Prediction 35 | AOT_ER_HVXC ///< N Error Resilient Harmonic Vector eXcitation Coding 36 | AOT_ER_HILN ///< N Error Resilient Harmonic and Individual Lines plus Noise 37 | AOT_ER_PARAM ///< N Error Resilient Parametric 38 | AOT_SSC ///< N SinuSoidal Coding 39 | AOT_PS ///< N Parametric Stereo 40 | AOT_SURROUND ///< N MPEG Surround 41 | AOT_ESCAPE ///< Y Escape Value 42 | AOT_L1 ///< Y Layer 1 43 | AOT_L2 ///< Y Layer 2 44 | AOT_L3 ///< Y Layer 3 45 | AOT_DST ///< N Direct Stream Transfer 46 | AOT_ALS ///< Y Audio LosslesS 47 | AOT_SLS ///< N Scalable LosslesS 48 | AOT_SLS_NON_CORE ///< N Scalable LosslesS (non core) 49 | AOT_ER_AAC_ELD ///< N Error Resilient Enhanced Low Delay 50 | AOT_SMR_SIMPLE ///< N Symbolic Music Representation Simple 51 | AOT_SMR_MAIN ///< N Symbolic Music Representation Main 52 | AOT_USAC_NOSBR ///< N Unified Speech and Audio Coding (no SBR) 53 | AOT_SAOC ///< N Spatial Audio Object Coding 54 | AOT_LD_SURROUND ///< N Low Delay MPEG Surround 55 | AOT_USAC ///< N Unified Speech and Audio Coding 56 | ) 57 | 58 | type MPEG4AudioConfig struct { 59 | SampleRate int 60 | ChannelLayout av.ChannelLayout 61 | ObjectType uint 62 | SampleRateIndex uint 63 | ChannelConfig uint 64 | } 65 | 66 | var sampleRateTable = []int{ 67 | 96000, 88200, 64000, 48000, 44100, 32000, 68 | 24000, 22050, 16000, 12000, 11025, 8000, 7350, 69 | } 70 | 71 | /* 72 | These are the channel configurations: 73 | 0: Defined in AOT Specifc Config 74 | 1: 1 channel: front-center 75 | 2: 2 channels: front-left, front-right 76 | 3: 3 channels: front-center, front-left, front-right 77 | 4: 4 channels: front-center, front-left, front-right, back-center 78 | 5: 5 channels: front-center, front-left, front-right, back-left, back-right 79 | 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel 80 | 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel 81 | 8-15: Reserved 82 | */ 83 | var chanConfigTable = []av.ChannelLayout{ 84 | 0, 85 | av.CH_FRONT_CENTER, 86 | av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT, 87 | av.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT, 88 | av.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT|av.CH_BACK_CENTER, 89 | av.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT|av.CH_BACK_LEFT|av.CH_BACK_RIGHT, 90 | av.CH_FRONT_CENTER|av.CH_FRONT_LEFT|av.CH_FRONT_RIGHT|av.CH_BACK_LEFT|av.CH_BACK_RIGHT|av.CH_LOW_FREQ, 91 | av.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, 92 | } 93 | 94 | func ParseADTSHeader(frame []byte) (config MPEG4AudioConfig, hdrlen int, framelen int, samples int, err error) { 95 | if frame[0] != 0xff || frame[1]&0xf6 != 0xf0 { 96 | err = fmt.Errorf("aacparser: not adts header") 97 | return 98 | } 99 | config.ObjectType = uint(frame[2]>>6) + 1 100 | config.SampleRateIndex = uint(frame[2] >> 2 & 0xf) 101 | config.ChannelConfig = uint(frame[2]<<2&0x4 | frame[3]>>6&0x3) 102 | if config.ChannelConfig == uint(0) { 103 | err = fmt.Errorf("aacparser: adts channel count invalid") 104 | return 105 | } 106 | (&config).Complete() 107 | framelen = int(frame[3]&0x3)<<11 | int(frame[4])<<3 | int(frame[5]>>5) 108 | samples = (int(frame[6]&0x3) + 1) * 1024 109 | hdrlen = 7 110 | if frame[1]&0x1 == 0 { 111 | hdrlen = 9 112 | } 113 | if framelen < hdrlen { 114 | err = fmt.Errorf("aacparser: adts framelen < hdrlen") 115 | return 116 | } 117 | return 118 | } 119 | 120 | const ADTSHeaderLength = 7 121 | 122 | func FillADTSHeader(header []byte, config MPEG4AudioConfig, samples int, payloadLength int) { 123 | payloadLength += 7 124 | //AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ) 125 | header[0] = 0xff 126 | header[1] = 0xf1 127 | header[2] = 0x50 128 | header[3] = 0x80 129 | header[4] = 0x43 130 | header[5] = 0xff 131 | header[6] = 0xcd 132 | //config.ObjectType = uint(frames[2]>>6)+1 133 | //config.SampleRateIndex = uint(frames[2]>>2&0xf) 134 | //config.ChannelConfig = uint(frames[2]<<2&0x4|frames[3]>>6&0x3) 135 | header[2] = (byte(config.ObjectType-1)&0x3)<<6 | (byte(config.SampleRateIndex)&0xf)<<2 | byte(config.ChannelConfig>>2)&0x1 136 | header[3] = header[3]&0x3f | byte(config.ChannelConfig&0x3)<<6 137 | header[3] = header[3]&0xfc | byte(payloadLength>>11)&0x3 138 | header[4] = byte(payloadLength >> 3) 139 | header[5] = header[5]&0x1f | (byte(payloadLength)&0x7)<<5 140 | header[6] = header[6]&0xfc | byte(samples/1024-1) 141 | return 142 | } 143 | 144 | func readObjectType(r *bits.Reader) (objectType uint, err error) { 145 | if objectType, err = r.ReadBits(5); err != nil { 146 | return 147 | } 148 | if objectType == AOT_ESCAPE { 149 | var i uint 150 | if i, err = r.ReadBits(6); err != nil { 151 | return 152 | } 153 | objectType = 32 + i 154 | } 155 | return 156 | } 157 | 158 | func writeObjectType(w *bits.Writer, objectType uint) (err error) { 159 | if objectType >= 32 { 160 | if err = w.WriteBits(AOT_ESCAPE, 5); err != nil { 161 | return 162 | } 163 | if err = w.WriteBits(objectType-32, 6); err != nil { 164 | return 165 | } 166 | } else { 167 | if err = w.WriteBits(objectType, 5); err != nil { 168 | return 169 | } 170 | } 171 | return 172 | } 173 | 174 | func readSampleRateIndex(r *bits.Reader) (index uint, err error) { 175 | if index, err = r.ReadBits(4); err != nil { 176 | return 177 | } 178 | if index == 0xf { 179 | if index, err = r.ReadBits(24); err != nil { 180 | return 181 | } 182 | } 183 | return 184 | } 185 | 186 | func writeSampleRateIndex(w *bits.Writer, index uint) (err error) { 187 | if index >= 0xf { 188 | if err = w.WriteBits(0xf, 4); err != nil { 189 | return 190 | } 191 | if err = w.WriteBits(index, 24); err != nil { 192 | return 193 | } 194 | } else { 195 | if err = w.WriteBits(index, 4); err != nil { 196 | return 197 | } 198 | } 199 | return 200 | } 201 | 202 | func (self MPEG4AudioConfig) IsValid() bool { 203 | return self.ObjectType > 0 204 | } 205 | 206 | func (self *MPEG4AudioConfig) Complete() { 207 | if int(self.SampleRateIndex) < len(sampleRateTable) { 208 | self.SampleRate = sampleRateTable[self.SampleRateIndex] 209 | } 210 | if int(self.ChannelConfig) < len(chanConfigTable) { 211 | self.ChannelLayout = chanConfigTable[self.ChannelConfig] 212 | } 213 | return 214 | } 215 | 216 | func ParseMPEG4AudioConfigBytes(data []byte) (config MPEG4AudioConfig, err error) { 217 | // copied from libavcodec/mpeg4audio.c avpriv_mpeg4audio_get_config() 218 | r := bytes.NewReader(data) 219 | br := &bits.Reader{R: r} 220 | if config.ObjectType, err = readObjectType(br); err != nil { 221 | return 222 | } 223 | if config.SampleRateIndex, err = readSampleRateIndex(br); err != nil { 224 | return 225 | } 226 | if config.ChannelConfig, err = br.ReadBits(4); err != nil { 227 | return 228 | } 229 | (&config).Complete() 230 | return 231 | } 232 | 233 | func WriteMPEG4AudioConfig(w io.Writer, config MPEG4AudioConfig) (err error) { 234 | bw := &bits.Writer{W: w} 235 | if err = writeObjectType(bw, config.ObjectType); err != nil { 236 | return 237 | } 238 | 239 | if config.SampleRateIndex == 0 { 240 | for i, rate := range sampleRateTable { 241 | if rate == config.SampleRate { 242 | config.SampleRateIndex = uint(i) 243 | } 244 | } 245 | } 246 | if err = writeSampleRateIndex(bw, config.SampleRateIndex); err != nil { 247 | return 248 | } 249 | 250 | if config.ChannelConfig == 0 { 251 | for i, layout := range chanConfigTable { 252 | if layout == config.ChannelLayout { 253 | config.ChannelConfig = uint(i) 254 | } 255 | } 256 | } 257 | if err = bw.WriteBits(config.ChannelConfig, 4); err != nil { 258 | return 259 | } 260 | 261 | if err = bw.FlushBits(); err != nil { 262 | return 263 | } 264 | return 265 | } 266 | 267 | type CodecData struct { 268 | ConfigBytes []byte 269 | Config MPEG4AudioConfig 270 | } 271 | 272 | func (self CodecData) Type() av.CodecType { 273 | return av.AAC 274 | } 275 | 276 | func (self CodecData) MPEG4AudioConfigBytes() []byte { 277 | return self.ConfigBytes 278 | } 279 | 280 | func (self CodecData) ChannelLayout() av.ChannelLayout { 281 | return self.Config.ChannelLayout 282 | } 283 | 284 | func (self CodecData) SampleRate() int { 285 | return self.Config.SampleRate 286 | } 287 | 288 | func (self CodecData) SampleFormat() av.SampleFormat { 289 | return av.FLTP 290 | } 291 | 292 | func (self CodecData) PacketDuration(data []byte) (dur time.Duration, err error) { 293 | dur = time.Duration(1024) * time.Second / time.Duration(self.Config.SampleRate) 294 | return 295 | } 296 | 297 | func NewCodecDataFromMPEG4AudioConfig(config MPEG4AudioConfig) (self CodecData, err error) { 298 | b := &bytes.Buffer{} 299 | WriteMPEG4AudioConfig(b, config) 300 | return NewCodecDataFromMPEG4AudioConfigBytes(b.Bytes()) 301 | } 302 | 303 | func NewCodecDataFromMPEG4AudioConfigBytes(config []byte) (self CodecData, err error) { 304 | self.ConfigBytes = config 305 | if self.Config, err = ParseMPEG4AudioConfigBytes(config); err != nil { 306 | err = fmt.Errorf("aacparser: parse MPEG4AudioConfig failed(%s)", err) 307 | return 308 | } 309 | return 310 | } 311 | 312 | -------------------------------------------------------------------------------- /codec/codec.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "github.com/nareix/joy4/av" 5 | "github.com/nareix/joy4/codec/fake" 6 | "time" 7 | ) 8 | 9 | type PCMUCodecData struct { 10 | typ av.CodecType 11 | } 12 | 13 | func (self PCMUCodecData) Type() av.CodecType { 14 | return self.typ 15 | } 16 | 17 | func (self PCMUCodecData) SampleRate() int { 18 | return 8000 19 | } 20 | 21 | func (self PCMUCodecData) ChannelLayout() av.ChannelLayout { 22 | return av.CH_MONO 23 | } 24 | 25 | func (self PCMUCodecData) SampleFormat() av.SampleFormat { 26 | return av.S16 27 | } 28 | 29 | func (self PCMUCodecData) PacketDuration(data []byte) (time.Duration, error) { 30 | return time.Duration(len(data)) * time.Second / time.Duration(8000), nil 31 | } 32 | 33 | func NewPCMMulawCodecData() av.AudioCodecData { 34 | return PCMUCodecData{ 35 | typ: av.PCM_MULAW, 36 | } 37 | } 38 | 39 | func NewPCMAlawCodecData() av.AudioCodecData { 40 | return PCMUCodecData{ 41 | typ: av.PCM_ALAW, 42 | } 43 | } 44 | 45 | type SpeexCodecData struct { 46 | fake.CodecData 47 | } 48 | 49 | func (self SpeexCodecData) PacketDuration(data []byte) (time.Duration, error) { 50 | // libavcodec/libspeexdec.c 51 | // samples = samplerate/50 52 | // duration = 0.02s 53 | return time.Millisecond*20, nil 54 | } 55 | 56 | func NewSpeexCodecData(sr int, cl av.ChannelLayout) SpeexCodecData { 57 | codec := SpeexCodecData{} 58 | codec.CodecType_ = av.SPEEX 59 | codec.SampleFormat_ = av.S16 60 | codec.SampleRate_ = sr 61 | codec.ChannelLayout_ = cl 62 | return codec 63 | } 64 | 65 | -------------------------------------------------------------------------------- /codec/fake/fake.go: -------------------------------------------------------------------------------- 1 | package fake 2 | 3 | import ( 4 | "github.com/nareix/joy4/av" 5 | ) 6 | 7 | type CodecData struct { 8 | CodecType_ av.CodecType 9 | SampleRate_ int 10 | SampleFormat_ av.SampleFormat 11 | ChannelLayout_ av.ChannelLayout 12 | } 13 | 14 | func (self CodecData) Type() av.CodecType { 15 | return self.CodecType_ 16 | } 17 | 18 | func (self CodecData) SampleFormat() av.SampleFormat { 19 | return self.SampleFormat_ 20 | } 21 | 22 | func (self CodecData) ChannelLayout() av.ChannelLayout { 23 | return self.ChannelLayout_ 24 | } 25 | 26 | func (self CodecData) SampleRate() int { 27 | return self.SampleRate_ 28 | } 29 | 30 | -------------------------------------------------------------------------------- /codec/h264parser/parser_test.go: -------------------------------------------------------------------------------- 1 | 2 | package h264parser 3 | 4 | import ( 5 | "testing" 6 | "encoding/hex" 7 | ) 8 | 9 | func TestParser(t *testing.T) { 10 | var ok bool 11 | var nalus [][]byte 12 | 13 | annexbFrame, _ := hex.DecodeString("00000001223322330000000122332233223300000133000001000001") 14 | nalus, ok = SplitNALUs(annexbFrame) 15 | t.Log(ok, len(nalus)) 16 | 17 | avccFrame, _ := hex.DecodeString( 18 | "00000008aabbccaabbccaabb00000001aa", 19 | ) 20 | nalus, ok = SplitNALUs(avccFrame) 21 | t.Log(ok, len(nalus)) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | 2 | // Package joy4 is a Golang audio/video library and streaming server. 3 | // JOY4 is powerful library written in golang, well-designed interface makes a few lines 4 | // of code can do a lot of things such as reading, writing, transcoding among 5 | // variety media formats, or setting up high-performance live streaming server. 6 | package joy4 7 | -------------------------------------------------------------------------------- /examples/audio_decode/main.go: -------------------------------------------------------------------------------- 1 | 2 | package main 3 | 4 | import ( 5 | "github.com/nareix/joy4/av" 6 | "github.com/nareix/joy4/format" 7 | "github.com/nareix/joy4/av/avutil" 8 | "github.com/nareix/joy4/cgo/ffmpeg" 9 | ) 10 | 11 | // need ffmpeg installed 12 | 13 | func init() { 14 | format.RegisterAll() 15 | } 16 | 17 | func main() { 18 | file, _ := avutil.Open("projectindex.flv") 19 | streams, _ := file.Streams() 20 | var dec *ffmpeg.AudioDecoder 21 | 22 | for _, stream := range streams { 23 | if stream.Type() == av.AAC { 24 | dec, _ = ffmpeg.NewAudioDecoder(stream.(av.AudioCodecData)) 25 | } 26 | } 27 | 28 | for i := 0; i < 10; i++ { 29 | pkt, _ := file.ReadPacket() 30 | if streams[pkt.Idx].Type() == av.AAC { 31 | ok, frame, _ := dec.Decode(pkt.Data) 32 | if ok { 33 | println("decode samples", frame.SampleCount) 34 | } 35 | } 36 | } 37 | 38 | file.Close() 39 | } 40 | 41 | -------------------------------------------------------------------------------- /examples/http_flv_and_rtmp_server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | "io" 6 | "net/http" 7 | "github.com/nareix/joy4/format" 8 | "github.com/nareix/joy4/av/avutil" 9 | "github.com/nareix/joy4/av/pubsub" 10 | "github.com/nareix/joy4/format/rtmp" 11 | "github.com/nareix/joy4/format/flv" 12 | ) 13 | 14 | func init() { 15 | format.RegisterAll() 16 | } 17 | 18 | type writeFlusher struct { 19 | httpflusher http.Flusher 20 | io.Writer 21 | } 22 | 23 | func (self writeFlusher) Flush() error { 24 | self.httpflusher.Flush() 25 | return nil 26 | } 27 | 28 | func main() { 29 | server := &rtmp.Server{} 30 | 31 | l := &sync.RWMutex{} 32 | type Channel struct { 33 | que *pubsub.Queue 34 | } 35 | channels := map[string]*Channel{} 36 | 37 | server.HandlePlay = func(conn *rtmp.Conn) { 38 | l.RLock() 39 | ch := channels[conn.URL.Path] 40 | l.RUnlock() 41 | 42 | if ch != nil { 43 | cursor := ch.que.Latest() 44 | avutil.CopyFile(conn, cursor) 45 | } 46 | } 47 | 48 | server.HandlePublish = func(conn *rtmp.Conn) { 49 | streams, _ := conn.Streams() 50 | 51 | l.Lock() 52 | ch := channels[conn.URL.Path] 53 | if ch == nil { 54 | ch = &Channel{} 55 | ch.que = pubsub.NewQueue() 56 | ch.que.WriteHeader(streams) 57 | channels[conn.URL.Path] = ch 58 | } else { 59 | ch = nil 60 | } 61 | l.Unlock() 62 | if ch == nil { 63 | return 64 | } 65 | 66 | avutil.CopyPackets(ch.que, conn) 67 | 68 | l.Lock() 69 | delete(channels, conn.URL.Path) 70 | l.Unlock() 71 | ch.que.Close() 72 | } 73 | 74 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 75 | l.RLock() 76 | ch := channels[r.URL.Path] 77 | l.RUnlock() 78 | 79 | if ch != nil { 80 | w.Header().Set("Content-Type", "video/x-flv") 81 | w.Header().Set("Transfer-Encoding", "chunked") 82 | w.Header().Set("Access-Control-Allow-Origin", "*") 83 | w.WriteHeader(200) 84 | flusher := w.(http.Flusher) 85 | flusher.Flush() 86 | 87 | muxer := flv.NewMuxerWriteFlusher(writeFlusher{httpflusher: flusher, Writer: w}) 88 | cursor := ch.que.Latest() 89 | 90 | avutil.CopyFile(muxer, cursor) 91 | } else { 92 | http.NotFound(w, r) 93 | } 94 | }) 95 | 96 | go http.ListenAndServe(":8089", nil) 97 | 98 | server.ListenAndServe() 99 | 100 | // ffmpeg -re -i movie.flv -c copy -f flv rtmp://localhost/movie 101 | // ffmpeg -f avfoundation -i "0:0" .... -f flv rtmp://localhost/screen 102 | // ffplay http://localhost:8089/movie 103 | // ffplay http://localhost:8089/screen 104 | } 105 | -------------------------------------------------------------------------------- /examples/open_probe_file/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nareix/joy4/av" 6 | "github.com/nareix/joy4/av/avutil" 7 | "github.com/nareix/joy4/format" 8 | ) 9 | 10 | func init() { 11 | format.RegisterAll() 12 | } 13 | 14 | func main() { 15 | file, _ := avutil.Open("projectindex.flv") 16 | 17 | streams, _ := file.Streams() 18 | for _, stream := range streams { 19 | if stream.Type().IsAudio() { 20 | astream := stream.(av.AudioCodecData) 21 | fmt.Println(astream.Type(), astream.SampleRate(), astream.SampleFormat(), astream.ChannelLayout()) 22 | } else if stream.Type().IsVideo() { 23 | vstream := stream.(av.VideoCodecData) 24 | fmt.Println(vstream.Type(), vstream.Width(), vstream.Height()) 25 | } 26 | } 27 | 28 | for i := 0; i < 10; i++ { 29 | var pkt av.Packet 30 | var err error 31 | if pkt, err = file.ReadPacket(); err != nil { 32 | break 33 | } 34 | fmt.Println("pkt", i, streams[pkt.Idx].Type(), "len", len(pkt.Data), "keyframe", pkt.IsKeyFrame) 35 | } 36 | 37 | file.Close() 38 | } 39 | 40 | -------------------------------------------------------------------------------- /examples/rtmp_publish/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/nareix/joy4/av/pktque" 5 | "github.com/nareix/joy4/format" 6 | "github.com/nareix/joy4/av/avutil" 7 | "github.com/nareix/joy4/format/rtmp" 8 | ) 9 | 10 | func init() { 11 | format.RegisterAll() 12 | } 13 | 14 | // as same as: ffmpeg -re -i projectindex.flv -c copy -f flv rtmp://localhost:1936/app/publish 15 | 16 | func main() { 17 | file, _ := avutil.Open("projectindex.flv") 18 | conn, _ := rtmp.Dial("rtmp://localhost:1936/app/publish") 19 | // conn, _ := avutil.Create("rtmp://localhost:1936/app/publish") 20 | 21 | demuxer := &pktque.FilterDemuxer{Demuxer: file, Filter: &pktque.Walltime{}} 22 | avutil.CopyFile(conn, demuxer) 23 | 24 | file.Close() 25 | conn.Close() 26 | } 27 | 28 | -------------------------------------------------------------------------------- /examples/rtmp_server_channels/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nareix/joy4/av" 6 | "github.com/nareix/joy4/av/avutil" 7 | "github.com/nareix/joy4/av/pktque" 8 | "github.com/nareix/joy4/av/pubsub" 9 | "github.com/nareix/joy4/format" 10 | "github.com/nareix/joy4/format/rtmp" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | func init() { 16 | format.RegisterAll() 17 | } 18 | 19 | type FrameDropper struct { 20 | Interval int 21 | n int 22 | skipping bool 23 | DelaySkip time.Duration 24 | lasttime time.Time 25 | lastpkttime time.Duration 26 | delay time.Duration 27 | SkipInterval int 28 | } 29 | 30 | func (self *FrameDropper) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) { 31 | if self.DelaySkip != 0 && pkt.Idx == int8(videoidx) { 32 | now := time.Now() 33 | if !self.lasttime.IsZero() { 34 | realdiff := now.Sub(self.lasttime) 35 | pktdiff := pkt.Time - self.lastpkttime 36 | self.delay += realdiff - pktdiff 37 | } 38 | self.lasttime = time.Now() 39 | self.lastpkttime = pkt.Time 40 | 41 | if !self.skipping { 42 | if self.delay > self.DelaySkip { 43 | self.skipping = true 44 | self.delay = 0 45 | } 46 | } else { 47 | if pkt.IsKeyFrame { 48 | self.skipping = false 49 | } 50 | } 51 | if self.skipping { 52 | drop = true 53 | } 54 | 55 | if self.SkipInterval != 0 && pkt.IsKeyFrame { 56 | if self.n == self.SkipInterval { 57 | self.n = 0 58 | self.skipping = true 59 | } 60 | self.n++ 61 | } 62 | } 63 | 64 | if self.Interval != 0 { 65 | if self.n >= self.Interval && pkt.Idx == int8(videoidx) && !pkt.IsKeyFrame { 66 | drop = true 67 | self.n = 0 68 | } 69 | self.n++ 70 | } 71 | 72 | return 73 | } 74 | 75 | func main() { 76 | server := &rtmp.Server{} 77 | 78 | l := &sync.RWMutex{} 79 | type Channel struct { 80 | que *pubsub.Queue 81 | } 82 | channels := map[string]*Channel{} 83 | 84 | server.HandlePlay = func(conn *rtmp.Conn) { 85 | l.RLock() 86 | ch := channels[conn.URL.Path] 87 | l.RUnlock() 88 | 89 | if ch != nil { 90 | cursor := ch.que.Latest() 91 | query := conn.URL.Query() 92 | 93 | if q := query.Get("delaygop"); q != "" { 94 | n := 0 95 | fmt.Sscanf(q, "%d", &n) 96 | cursor = ch.que.DelayedGopCount(n) 97 | } else if q := query.Get("delaytime"); q != "" { 98 | dur, _ := time.ParseDuration(q) 99 | cursor = ch.que.DelayedTime(dur) 100 | } 101 | 102 | filters := pktque.Filters{} 103 | 104 | if q := query.Get("waitkey"); q != "" { 105 | filters = append(filters, &pktque.WaitKeyFrame{}) 106 | } 107 | 108 | filters = append(filters, &pktque.FixTime{StartFromZero: true, MakeIncrement: true}) 109 | 110 | if q := query.Get("framedrop"); q != "" { 111 | n := 0 112 | fmt.Sscanf(q, "%d", &n) 113 | filters = append(filters, &FrameDropper{Interval: n}) 114 | } 115 | 116 | if q := query.Get("delayskip"); q != "" { 117 | dur, _ := time.ParseDuration(q) 118 | skipper := &FrameDropper{DelaySkip: dur} 119 | if q := query.Get("skipinterval"); q != "" { 120 | n := 0 121 | fmt.Sscanf(q, "%d", &n) 122 | skipper.SkipInterval = n 123 | } 124 | filters = append(filters, skipper) 125 | } 126 | 127 | demuxer := &pktque.FilterDemuxer{ 128 | Filter: filters, 129 | Demuxer: cursor, 130 | } 131 | 132 | avutil.CopyFile(conn, demuxer) 133 | } 134 | } 135 | 136 | server.HandlePublish = func(conn *rtmp.Conn) { 137 | l.Lock() 138 | ch := channels[conn.URL.Path] 139 | if ch == nil { 140 | ch = &Channel{} 141 | ch.que = pubsub.NewQueue() 142 | query := conn.URL.Query() 143 | if q := query.Get("cachegop"); q != "" { 144 | var n int 145 | fmt.Sscanf(q, "%d", &n) 146 | ch.que.SetMaxGopCount(n) 147 | } 148 | channels[conn.URL.Path] = ch 149 | } else { 150 | ch = nil 151 | } 152 | l.Unlock() 153 | if ch == nil { 154 | return 155 | } 156 | 157 | avutil.CopyFile(ch.que, conn) 158 | 159 | l.Lock() 160 | delete(channels, conn.URL.Path) 161 | l.Unlock() 162 | ch.que.Close() 163 | } 164 | 165 | server.ListenAndServe() 166 | 167 | // ffmpeg -re -i movie.flv -c copy -f flv rtmp://localhost/movie 168 | // ffmpeg -f avfoundation -i "0:0" .... -f flv rtmp://localhost/screen 169 | 170 | // with cache size options 171 | 172 | // ffplay rtmp://localhost/movie 173 | // ffplay rtmp://localhost/screen 174 | // ffplay rtmp://localhost/movie?delaytime=5s 175 | // ffplay rtmp://localhost/movie?delaytime=10s&waitkey=true 176 | // ffplay rtmp://localhost/movie?delaytime=20s 177 | 178 | // ffmpeg -re -i movie.flv -c copy -f flv rtmp://localhost/movie?cachegop=2 179 | // ffmpeg -re -i movie.flv -c copy -f flv rtmp://localhost/movie?cachegop=1 180 | } 181 | -------------------------------------------------------------------------------- /examples/rtmp_server_proxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "github.com/nareix/joy4/format" 7 | "github.com/nareix/joy4/av/avutil" 8 | "github.com/nareix/joy4/format/rtmp" 9 | ) 10 | 11 | func init() { 12 | format.RegisterAll() 13 | } 14 | 15 | func main() { 16 | server := &rtmp.Server{} 17 | 18 | server.HandlePlay = func(conn *rtmp.Conn) { 19 | segs := strings.Split(conn.URL.Path, "/") 20 | url := fmt.Sprintf("%s://%s", segs[1], strings.Join(segs[2:], "/")) 21 | src, _ := avutil.Open(url) 22 | avutil.CopyFile(conn, src) 23 | } 24 | 25 | server.ListenAndServe() 26 | 27 | // ffplay rtmp://localhost/rtsp/192.168.1.1/camera1 28 | // ffplay rtmp://localhost/rtmp/live.hkstv.hk.lxdns.com/live/hks 29 | } 30 | -------------------------------------------------------------------------------- /examples/rtmp_server_speex_to_aac/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/nareix/joy4/av" 5 | "github.com/nareix/joy4/av/transcode" 6 | "github.com/nareix/joy4/format" 7 | "github.com/nareix/joy4/av/avutil" 8 | "github.com/nareix/joy4/format/rtmp" 9 | "github.com/nareix/joy4/cgo/ffmpeg" 10 | ) 11 | 12 | // need ffmpeg with libspeex and libfdkaac installed 13 | // 14 | // open http://www.wowza.com/resources/4.4.1/examples/WebcamRecording/FlashRTMPPlayer11/player.html 15 | // click connect and recored 16 | // input camera H264/SPEEX will converted H264/AAC and saved in out.ts 17 | 18 | func init() { 19 | format.RegisterAll() 20 | } 21 | 22 | func main() { 23 | server := &rtmp.Server{} 24 | 25 | server.HandlePublish = func(conn *rtmp.Conn) { 26 | file, _ := avutil.Create("out.ts") 27 | 28 | findcodec := func(stream av.AudioCodecData, i int) (need bool, dec av.AudioDecoder, enc av.AudioEncoder, err error) { 29 | need = true 30 | dec, _ = ffmpeg.NewAudioDecoder(stream) 31 | enc, _ = ffmpeg.NewAudioEncoderByName("libfdk_aac") 32 | enc.SetSampleRate(48000) 33 | enc.SetChannelLayout(av.CH_STEREO) 34 | return 35 | } 36 | 37 | trans := &transcode.Demuxer{ 38 | Options: transcode.Options{ 39 | FindAudioDecoderEncoder: findcodec, 40 | }, 41 | Demuxer: conn, 42 | } 43 | 44 | avutil.CopyFile(file, trans) 45 | } 46 | 47 | server.ListenAndServe() 48 | } 49 | -------------------------------------------------------------------------------- /examples/transcode/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/nareix/joy4/av" 5 | "github.com/nareix/joy4/av/transcode" 6 | "github.com/nareix/joy4/format" 7 | "github.com/nareix/joy4/av/avutil" 8 | "github.com/nareix/joy4/cgo/ffmpeg" 9 | ) 10 | 11 | // need ffmpeg with libfdkaac installed 12 | 13 | func init() { 14 | format.RegisterAll() 15 | } 16 | 17 | func main() { 18 | infile, _ := avutil.Open("speex.flv") 19 | 20 | findcodec := func(stream av.AudioCodecData, i int) (need bool, dec av.AudioDecoder, enc av.AudioEncoder, err error) { 21 | need = true 22 | dec, _ = ffmpeg.NewAudioDecoder(stream) 23 | enc, _ = ffmpeg.NewAudioEncoderByName("libfdk_aac") 24 | enc.SetSampleRate(stream.SampleRate()) 25 | enc.SetChannelLayout(av.CH_STEREO) 26 | enc.SetBitrate(12000) 27 | enc.SetOption("profile", "HE-AACv2") 28 | return 29 | } 30 | 31 | trans := &transcode.Demuxer{ 32 | Options: transcode.Options{ 33 | FindAudioDecoderEncoder: findcodec, 34 | }, 35 | Demuxer: infile, 36 | } 37 | 38 | outfile, _ := avutil.Create("out.ts") 39 | avutil.CopyFile(outfile, trans) 40 | 41 | outfile.Close() 42 | infile.Close() 43 | trans.Close() 44 | } 45 | 46 | -------------------------------------------------------------------------------- /examples/video_transcoder/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "io" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/nareix/joy4/format" 11 | "github.com/nareix/joy4/av" 12 | "github.com/nareix/joy4/av/avutil" 13 | "github.com/nareix/joy4/av/pubsub" 14 | "github.com/nareix/joy4/av/transcode" 15 | "github.com/nareix/joy4/format/rtmp" 16 | "github.com/nareix/joy4/format/flv" 17 | "github.com/nareix/joy4/cgo/ffmpeg" 18 | ) 19 | 20 | func init() { 21 | format.RegisterAll() 22 | } 23 | 24 | type writeFlusher struct { 25 | httpflusher http.Flusher 26 | io.Writer 27 | } 28 | 29 | func (w writeFlusher) Flush() error { 30 | w.httpflusher.Flush() 31 | return nil 32 | } 33 | 34 | func printHelp() { 35 | fmt.Printf ("Usage: ./video_normalizer \n") 36 | } 37 | 38 | func main() { 39 | fmt.Println("starting server") 40 | server := &rtmp.Server{} 41 | videoFile := "" 42 | 43 | if len(os.Args) <= 1 { 44 | printHelp() 45 | return 46 | } 47 | 48 | for i, arg := range os.Args { 49 | if i == 0 { 50 | // skip program name 51 | continue 52 | } 53 | if arg == "-help" || arg == "-h" { 54 | printHelp() 55 | return 56 | } else { 57 | videoFile = os.Args[i] 58 | } 59 | } 60 | 61 | if videoFile == "" { 62 | printHelp() 63 | return 64 | } 65 | 66 | fmt.Println("Video file:", videoFile, ". Play with: ffplay http://localhost:8089/file") 67 | 68 | l := &sync.RWMutex{} 69 | type Channel struct { 70 | que *pubsub.Queue 71 | } 72 | channels := map[string]*Channel{} 73 | 74 | server.HandlePlay = func(conn *rtmp.Conn) { 75 | fmt.Println("HandlePlay()") 76 | l.RLock() 77 | ch := channels[conn.URL.Path] 78 | l.RUnlock() 79 | 80 | if ch != nil { 81 | cursor := ch.que.Latest() 82 | avutil.CopyFile(conn, cursor) 83 | } 84 | } 85 | 86 | server.HandlePublish = func(conn *rtmp.Conn) { 87 | fmt.Println("HandlePublish()") 88 | streams, _ := conn.Streams() 89 | 90 | l.Lock() 91 | ch := channels[conn.URL.Path] 92 | if ch == nil { 93 | ch = &Channel{} 94 | ch.que = pubsub.NewQueue() 95 | ch.que.WriteHeader(streams) 96 | channels[conn.URL.Path] = ch 97 | } else { 98 | ch = nil 99 | } 100 | l.Unlock() 101 | if ch == nil { 102 | return 103 | } 104 | 105 | trans := &transcode.Demuxer{ 106 | Options: transcode.Options{ 107 | FindAudioDecoderEncoder: FindAudioCodec, 108 | FindVideoDecoderEncoder: FindVideoCodec, 109 | }, 110 | Demuxer: conn, 111 | } 112 | 113 | avutil.CopyFile(ch.que, trans) 114 | 115 | fmt.Println("Leaving HandlePublish()") 116 | 117 | l.Lock() 118 | delete(channels, conn.URL.Path) 119 | l.Unlock() 120 | ch.que.Close() 121 | trans.Close() 122 | } 123 | 124 | http.HandleFunc("/file", func(w http.ResponseWriter, r *http.Request) { 125 | fmt.Println("Request:", r.URL.Path) 126 | w.Header().Set("Content-Type", "video/x-flv") 127 | w.Header().Set("Transfer-Encoding", "chunked") 128 | w.Header().Set("Access-Control-Allow-Origin", "*") 129 | w.WriteHeader(200) 130 | flusher := w.(http.Flusher) 131 | flusher.Flush() 132 | 133 | file, _ := avutil.Open(videoFile) 134 | 135 | trans := &transcode.Demuxer{ 136 | Options: transcode.Options{ 137 | FindAudioDecoderEncoder: FindAudioCodec, 138 | FindVideoDecoderEncoder: FindVideoCodec, 139 | }, 140 | Demuxer: file, 141 | } 142 | 143 | muxer := flv.NewMuxerWriteFlusher(writeFlusher{httpflusher: flusher, Writer: w}) 144 | err := avutil.CopyFile(muxer, trans) 145 | if err != nil { 146 | fmt.Println("Error in CopyFile():", err) 147 | } 148 | file.Close() 149 | trans.Close() 150 | }) 151 | 152 | go http.ListenAndServe(":8089", nil) 153 | server.ListenAndServe() 154 | fmt.Println("Done") 155 | } 156 | 157 | // FindAudioCodec is a callback used by joy4's transcoder to find an audio codec compatible with the input stream 158 | func FindAudioCodec(stream av.AudioCodecData, i int) (need bool, dec av.AudioDecoder, enc av.AudioEncoder, err error) { 159 | need = true 160 | dec, err = ffmpeg.NewAudioDecoder(stream) 161 | if err != nil { 162 | return 163 | } 164 | if dec == nil { 165 | err = fmt.Errorf("Audio decoder not found") 166 | return 167 | } 168 | 169 | enc, err = ffmpeg.NewAudioEncoderByCodecType(av.AAC) 170 | if err != nil { 171 | return 172 | } 173 | if enc == nil { 174 | err = fmt.Errorf("Audio encoder not found") 175 | return 176 | } 177 | enc.SetSampleRate(44100) 178 | enc.SetChannelLayout(av.CH_STEREO) 179 | enc.SetBitrate(192000) 180 | enc.SetOption("profile", "HE-AACv2") 181 | return 182 | } 183 | 184 | // FindVideoCodec is a callback used by joy4's transcoder to find a video codec compatible with the input stream 185 | func FindVideoCodec(stream av.VideoCodecData, i int) (need bool, dec *ffmpeg.VideoDecoder, enc *ffmpeg.VideoEncoder, err error) { 186 | need = true 187 | dec, err = ffmpeg.NewVideoDecoder(stream) 188 | if err != nil { 189 | return 190 | } 191 | if dec == nil { 192 | err = fmt.Errorf("Video decoder not found") 193 | return 194 | } 195 | 196 | enc, err = ffmpeg.NewVideoEncoderByCodecType(av.H264) 197 | if err != nil { 198 | return 199 | } 200 | if enc == nil { 201 | err = fmt.Errorf("Video encoder not found") 202 | return 203 | } 204 | 205 | // Encoder config 206 | // Configurable (can be set from input stream, or set by user and the input video will be converted before encoding) 207 | FpsNum := 18000 208 | FpsDen := 1000 209 | enc.SetFramerate(FpsNum, FpsDen) 210 | enc.SetResolution(640, 480) 211 | enc.SetPixelFormat(av.I420) 212 | // Must be configured by user 213 | enc.SetBitrate(1000000) // 1 Mbps 214 | enc.SetGopSize(FpsNum / FpsDen) // 1s gop 215 | return 216 | } 217 | -------------------------------------------------------------------------------- /format/aac/aac.go: -------------------------------------------------------------------------------- 1 | 2 | package aac 3 | 4 | import ( 5 | "github.com/nareix/joy4/av/avutil" 6 | "github.com/nareix/joy4/av" 7 | "github.com/nareix/joy4/codec/aacparser" 8 | "time" 9 | "fmt" 10 | "io" 11 | "bufio" 12 | ) 13 | 14 | type Muxer struct { 15 | w io.Writer 16 | config aacparser.MPEG4AudioConfig 17 | adtshdr []byte 18 | } 19 | 20 | func NewMuxer(w io.Writer) *Muxer { 21 | return &Muxer{ 22 | adtshdr: make([]byte, aacparser.ADTSHeaderLength), 23 | w: w, 24 | } 25 | } 26 | 27 | func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { 28 | if len(streams) > 1 || streams[0].Type() != av.AAC { 29 | err = fmt.Errorf("aac: must be only one aac stream") 30 | return 31 | } 32 | self.config = streams[0].(aacparser.CodecData).Config 33 | if self.config.ObjectType > aacparser.AOT_AAC_LTP { 34 | err = fmt.Errorf("aac: AOT %d is not allowed in ADTS", self.config.ObjectType) 35 | } 36 | return 37 | } 38 | 39 | func (self *Muxer) WritePacket(pkt av.Packet) (err error) { 40 | aacparser.FillADTSHeader(self.adtshdr, self.config, 1024, len(pkt.Data)) 41 | if _, err = self.w.Write(self.adtshdr); err != nil { 42 | return 43 | } 44 | if _, err = self.w.Write(pkt.Data); err != nil { 45 | return 46 | } 47 | return 48 | } 49 | 50 | func (self *Muxer) WriteTrailer() (err error) { 51 | return 52 | } 53 | 54 | type Demuxer struct { 55 | r *bufio.Reader 56 | config aacparser.MPEG4AudioConfig 57 | codecdata av.CodecData 58 | ts time.Duration 59 | } 60 | 61 | func NewDemuxer(r io.Reader) *Demuxer { 62 | return &Demuxer{ 63 | r: bufio.NewReader(r), 64 | } 65 | } 66 | 67 | func (self *Demuxer) Streams() (streams []av.CodecData, err error) { 68 | if self.codecdata == nil { 69 | var adtshdr []byte 70 | var config aacparser.MPEG4AudioConfig 71 | if adtshdr, err = self.r.Peek(9); err != nil { 72 | return 73 | } 74 | if config, _, _, _, err = aacparser.ParseADTSHeader(adtshdr); err != nil { 75 | return 76 | } 77 | if self.codecdata, err = aacparser.NewCodecDataFromMPEG4AudioConfig(config); err != nil { 78 | return 79 | } 80 | } 81 | streams = []av.CodecData{self.codecdata} 82 | return 83 | } 84 | 85 | func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { 86 | var adtshdr []byte 87 | var config aacparser.MPEG4AudioConfig 88 | var hdrlen, framelen, samples int 89 | if adtshdr, err = self.r.Peek(9); err != nil { 90 | return 91 | } 92 | if config, hdrlen, framelen, samples, err = aacparser.ParseADTSHeader(adtshdr); err != nil { 93 | return 94 | } 95 | 96 | pkt.Data = make([]byte, framelen) 97 | if _, err = io.ReadFull(self.r, pkt.Data); err != nil { 98 | return 99 | } 100 | pkt.Data = pkt.Data[hdrlen:] 101 | 102 | pkt.Time = self.ts 103 | self.ts += time.Duration(samples) * time.Second / time.Duration(config.SampleRate) 104 | return 105 | } 106 | 107 | func Handler(h *avutil.RegisterHandler) { 108 | h.Ext = ".aac" 109 | 110 | h.ReaderDemuxer = func(r io.Reader) av.Demuxer { 111 | return NewDemuxer(r) 112 | } 113 | 114 | h.WriterMuxer = func(w io.Writer) av.Muxer { 115 | return NewMuxer(w) 116 | } 117 | 118 | h.Probe = func(b []byte) bool { 119 | _, _, _, _, err := aacparser.ParseADTSHeader(b) 120 | return err == nil 121 | } 122 | 123 | h.CodecTypes = []av.CodecType{av.AAC} 124 | } 125 | -------------------------------------------------------------------------------- /format/flv/flv.go: -------------------------------------------------------------------------------- 1 | package flv 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "github.com/nareix/joy4/utils/bits/pio" 7 | "github.com/nareix/joy4/av" 8 | "github.com/nareix/joy4/av/avutil" 9 | "github.com/nareix/joy4/codec" 10 | "github.com/nareix/joy4/codec/aacparser" 11 | "github.com/nareix/joy4/codec/fake" 12 | "github.com/nareix/joy4/codec/h264parser" 13 | "github.com/nareix/joy4/format/flv/flvio" 14 | "io" 15 | ) 16 | 17 | var MaxProbePacketCount = 20 18 | 19 | func NewMetadataByStreams(streams []av.CodecData) (metadata flvio.AMFMap, err error) { 20 | metadata = flvio.AMFMap{} 21 | 22 | for _, _stream := range streams { 23 | typ := _stream.Type() 24 | switch { 25 | case typ.IsVideo(): 26 | stream := _stream.(av.VideoCodecData) 27 | switch typ { 28 | case av.H264: 29 | metadata["videocodecid"] = flvio.VIDEO_H264 30 | 31 | default: 32 | err = fmt.Errorf("flv: metadata: unsupported video codecType=%v", stream.Type()) 33 | return 34 | } 35 | 36 | metadata["width"] = stream.Width() 37 | metadata["height"] = stream.Height() 38 | metadata["displayWidth"] = stream.Width() 39 | metadata["displayHeight"] = stream.Height() 40 | 41 | case typ.IsAudio(): 42 | stream := _stream.(av.AudioCodecData) 43 | switch typ { 44 | case av.AAC: 45 | metadata["audiocodecid"] = flvio.SOUND_AAC 46 | 47 | case av.SPEEX: 48 | metadata["audiocodecid"] = flvio.SOUND_SPEEX 49 | 50 | default: 51 | err = fmt.Errorf("flv: metadata: unsupported audio codecType=%v", stream.Type()) 52 | return 53 | } 54 | 55 | metadata["audiosamplerate"] = stream.SampleRate() 56 | } 57 | } 58 | 59 | return 60 | } 61 | 62 | type Prober struct { 63 | HasAudio, HasVideo bool 64 | GotAudio, GotVideo bool 65 | VideoStreamIdx, AudioStreamIdx int 66 | PushedCount int 67 | Streams []av.CodecData 68 | CachedPkts []av.Packet 69 | } 70 | 71 | func (self *Prober) CacheTag(_tag flvio.Tag, timestamp int32) { 72 | pkt, _ := self.TagToPacket(_tag, timestamp) 73 | self.CachedPkts = append(self.CachedPkts, pkt) 74 | } 75 | 76 | func (self *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) { 77 | self.PushedCount++ 78 | 79 | if self.PushedCount > MaxProbePacketCount { 80 | err = fmt.Errorf("flv: max probe packet count reached") 81 | return 82 | } 83 | 84 | switch tag.Type { 85 | case flvio.TAG_VIDEO: 86 | switch tag.AVCPacketType { 87 | case flvio.AVC_SEQHDR: 88 | if !self.GotVideo { 89 | var stream h264parser.CodecData 90 | if stream, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(tag.Data); err != nil { 91 | err = fmt.Errorf("flv: h264 seqhdr invalid") 92 | return 93 | } 94 | self.VideoStreamIdx = len(self.Streams) 95 | self.Streams = append(self.Streams, stream) 96 | self.GotVideo = true 97 | } 98 | 99 | case flvio.AVC_NALU: 100 | self.CacheTag(tag, timestamp) 101 | } 102 | 103 | case flvio.TAG_AUDIO: 104 | switch tag.SoundFormat { 105 | case flvio.SOUND_AAC: 106 | switch tag.AACPacketType { 107 | case flvio.AAC_SEQHDR: 108 | if !self.GotAudio { 109 | var stream aacparser.CodecData 110 | if stream, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(tag.Data); err != nil { 111 | err = fmt.Errorf("flv: aac seqhdr invalid") 112 | return 113 | } 114 | self.AudioStreamIdx = len(self.Streams) 115 | self.Streams = append(self.Streams, stream) 116 | self.GotAudio = true 117 | } 118 | 119 | case flvio.AAC_RAW: 120 | self.CacheTag(tag, timestamp) 121 | } 122 | 123 | case flvio.SOUND_SPEEX: 124 | if !self.GotAudio { 125 | stream := codec.NewSpeexCodecData(16000, tag.ChannelLayout()) 126 | self.AudioStreamIdx = len(self.Streams) 127 | self.Streams = append(self.Streams, stream) 128 | self.GotAudio = true 129 | self.CacheTag(tag, timestamp) 130 | } 131 | 132 | case flvio.SOUND_NELLYMOSER: 133 | if !self.GotAudio { 134 | stream := fake.CodecData{ 135 | CodecType_: av.NELLYMOSER, 136 | SampleRate_: 16000, 137 | SampleFormat_: av.S16, 138 | ChannelLayout_: tag.ChannelLayout(), 139 | } 140 | self.AudioStreamIdx = len(self.Streams) 141 | self.Streams = append(self.Streams, stream) 142 | self.GotAudio = true 143 | self.CacheTag(tag, timestamp) 144 | } 145 | 146 | } 147 | } 148 | 149 | return 150 | } 151 | 152 | func (self *Prober) Probed() (ok bool) { 153 | if self.HasAudio || self.HasVideo { 154 | if self.HasAudio == self.GotAudio && self.HasVideo == self.GotVideo { 155 | return true 156 | } 157 | } else { 158 | if self.PushedCount == MaxProbePacketCount { 159 | return true 160 | } 161 | } 162 | return 163 | } 164 | 165 | func (self *Prober) TagToPacket(tag flvio.Tag, timestamp int32) (pkt av.Packet, ok bool) { 166 | switch tag.Type { 167 | case flvio.TAG_VIDEO: 168 | pkt.Idx = int8(self.VideoStreamIdx) 169 | switch tag.AVCPacketType { 170 | case flvio.AVC_NALU: 171 | ok = true 172 | pkt.Data = tag.Data 173 | pkt.CompositionTime = flvio.TsToTime(tag.CompositionTime) 174 | pkt.IsKeyFrame = tag.FrameType == flvio.FRAME_KEY 175 | } 176 | 177 | case flvio.TAG_AUDIO: 178 | pkt.Idx = int8(self.AudioStreamIdx) 179 | switch tag.SoundFormat { 180 | case flvio.SOUND_AAC: 181 | switch tag.AACPacketType { 182 | case flvio.AAC_RAW: 183 | ok = true 184 | pkt.Data = tag.Data 185 | } 186 | 187 | case flvio.SOUND_SPEEX: 188 | ok = true 189 | pkt.Data = tag.Data 190 | 191 | case flvio.SOUND_NELLYMOSER: 192 | ok = true 193 | pkt.Data = tag.Data 194 | } 195 | } 196 | 197 | pkt.Time = flvio.TsToTime(timestamp) 198 | return 199 | } 200 | 201 | func (self *Prober) Empty() bool { 202 | return len(self.CachedPkts) == 0 203 | } 204 | 205 | func (self *Prober) PopPacket() av.Packet { 206 | pkt := self.CachedPkts[0] 207 | self.CachedPkts = self.CachedPkts[1:] 208 | return pkt 209 | } 210 | 211 | func CodecDataToTag(stream av.CodecData) (_tag flvio.Tag, ok bool, err error) { 212 | switch stream.Type() { 213 | case av.H264: 214 | h264 := stream.(h264parser.CodecData) 215 | tag := flvio.Tag{ 216 | Type: flvio.TAG_VIDEO, 217 | AVCPacketType: flvio.AVC_SEQHDR, 218 | CodecID: flvio.VIDEO_H264, 219 | Data: h264.AVCDecoderConfRecordBytes(), 220 | FrameType: flvio.FRAME_KEY, 221 | } 222 | ok = true 223 | _tag = tag 224 | 225 | case av.NELLYMOSER: 226 | case av.SPEEX: 227 | 228 | case av.AAC: 229 | aac := stream.(aacparser.CodecData) 230 | tag := flvio.Tag{ 231 | Type: flvio.TAG_AUDIO, 232 | SoundFormat: flvio.SOUND_AAC, 233 | SoundRate: flvio.SOUND_44Khz, 234 | AACPacketType: flvio.AAC_SEQHDR, 235 | Data: aac.MPEG4AudioConfigBytes(), 236 | } 237 | switch aac.SampleFormat().BytesPerSample() { 238 | case 1: 239 | tag.SoundSize = flvio.SOUND_8BIT 240 | default: 241 | tag.SoundSize = flvio.SOUND_16BIT 242 | } 243 | switch aac.ChannelLayout().Count() { 244 | case 1: 245 | tag.SoundType = flvio.SOUND_MONO 246 | case 2: 247 | tag.SoundType = flvio.SOUND_STEREO 248 | } 249 | ok = true 250 | _tag = tag 251 | 252 | default: 253 | err = fmt.Errorf("flv: unspported codecType=%v", stream.Type()) 254 | return 255 | } 256 | return 257 | } 258 | 259 | func PacketToTag(pkt av.Packet, stream av.CodecData) (tag flvio.Tag, timestamp int32) { 260 | switch stream.Type() { 261 | case av.H264: 262 | tag = flvio.Tag{ 263 | Type: flvio.TAG_VIDEO, 264 | AVCPacketType: flvio.AVC_NALU, 265 | CodecID: flvio.VIDEO_H264, 266 | Data: pkt.Data, 267 | CompositionTime: flvio.TimeToTs(pkt.CompositionTime), 268 | } 269 | if pkt.IsKeyFrame { 270 | tag.FrameType = flvio.FRAME_KEY 271 | } else { 272 | tag.FrameType = flvio.FRAME_INTER 273 | } 274 | 275 | case av.AAC: 276 | tag = flvio.Tag{ 277 | Type: flvio.TAG_AUDIO, 278 | SoundFormat: flvio.SOUND_AAC, 279 | SoundRate: flvio.SOUND_44Khz, 280 | AACPacketType: flvio.AAC_RAW, 281 | Data: pkt.Data, 282 | } 283 | astream := stream.(av.AudioCodecData) 284 | switch astream.SampleFormat().BytesPerSample() { 285 | case 1: 286 | tag.SoundSize = flvio.SOUND_8BIT 287 | default: 288 | tag.SoundSize = flvio.SOUND_16BIT 289 | } 290 | switch astream.ChannelLayout().Count() { 291 | case 1: 292 | tag.SoundType = flvio.SOUND_MONO 293 | case 2: 294 | tag.SoundType = flvio.SOUND_STEREO 295 | } 296 | 297 | case av.SPEEX: 298 | tag = flvio.Tag{ 299 | Type: flvio.TAG_AUDIO, 300 | SoundFormat: flvio.SOUND_SPEEX, 301 | Data: pkt.Data, 302 | } 303 | 304 | case av.NELLYMOSER: 305 | tag = flvio.Tag{ 306 | Type: flvio.TAG_AUDIO, 307 | SoundFormat: flvio.SOUND_NELLYMOSER, 308 | Data: pkt.Data, 309 | } 310 | } 311 | 312 | timestamp = flvio.TimeToTs(pkt.Time) 313 | return 314 | } 315 | 316 | type Muxer struct { 317 | bufw writeFlusher 318 | b []byte 319 | streams []av.CodecData 320 | } 321 | 322 | type writeFlusher interface { 323 | io.Writer 324 | Flush() error 325 | } 326 | 327 | func NewMuxerWriteFlusher(w writeFlusher) *Muxer { 328 | return &Muxer{ 329 | bufw: w, 330 | b: make([]byte, 256), 331 | } 332 | } 333 | 334 | func NewMuxer(w io.Writer) *Muxer { 335 | return NewMuxerWriteFlusher(bufio.NewWriterSize(w, pio.RecommendBufioSize)) 336 | } 337 | 338 | var CodecTypes = []av.CodecType{av.H264, av.AAC, av.SPEEX} 339 | 340 | func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { 341 | var flags uint8 342 | for _, stream := range streams { 343 | if stream.Type().IsVideo() { 344 | flags |= flvio.FILE_HAS_VIDEO 345 | } else if stream.Type().IsAudio() { 346 | flags |= flvio.FILE_HAS_AUDIO 347 | } 348 | } 349 | 350 | n := flvio.FillFileHeader(self.b, flags) 351 | if _, err = self.bufw.Write(self.b[:n]); err != nil { 352 | return 353 | } 354 | 355 | for _, stream := range streams { 356 | var tag flvio.Tag 357 | var ok bool 358 | if tag, ok, err = CodecDataToTag(stream); err != nil { 359 | return 360 | } 361 | if ok { 362 | if err = flvio.WriteTag(self.bufw, tag, 0, self.b); err != nil { 363 | return 364 | } 365 | } 366 | } 367 | 368 | self.streams = streams 369 | return 370 | } 371 | 372 | func (self *Muxer) WritePacket(pkt av.Packet) (err error) { 373 | stream := self.streams[pkt.Idx] 374 | tag, timestamp := PacketToTag(pkt, stream) 375 | 376 | if err = flvio.WriteTag(self.bufw, tag, timestamp, self.b); err != nil { 377 | return 378 | } 379 | return 380 | } 381 | 382 | func (self *Muxer) WriteTrailer() (err error) { 383 | if err = self.bufw.Flush(); err != nil { 384 | return 385 | } 386 | return 387 | } 388 | 389 | type Demuxer struct { 390 | prober *Prober 391 | bufr *bufio.Reader 392 | b []byte 393 | stage int 394 | } 395 | 396 | func NewDemuxer(r io.Reader) *Demuxer { 397 | return &Demuxer{ 398 | bufr: bufio.NewReaderSize(r, pio.RecommendBufioSize), 399 | prober: &Prober{}, 400 | b: make([]byte, 256), 401 | } 402 | } 403 | 404 | func (self *Demuxer) prepare() (err error) { 405 | for self.stage < 2 { 406 | switch self.stage { 407 | case 0: 408 | if _, err = io.ReadFull(self.bufr, self.b[:flvio.FileHeaderLength]); err != nil { 409 | return 410 | } 411 | var flags uint8 412 | var skip int 413 | if flags, skip, err = flvio.ParseFileHeader(self.b); err != nil { 414 | return 415 | } 416 | if _, err = self.bufr.Discard(skip); err != nil { 417 | return 418 | } 419 | if flags&flvio.FILE_HAS_AUDIO != 0 { 420 | self.prober.HasAudio = true 421 | } 422 | if flags&flvio.FILE_HAS_VIDEO != 0 { 423 | self.prober.HasVideo = true 424 | } 425 | self.stage++ 426 | 427 | case 1: 428 | for !self.prober.Probed() { 429 | var tag flvio.Tag 430 | var timestamp int32 431 | if tag, timestamp, err = flvio.ReadTag(self.bufr, self.b); err != nil { 432 | return 433 | } 434 | if err = self.prober.PushTag(tag, timestamp); err != nil { 435 | return 436 | } 437 | } 438 | self.stage++ 439 | } 440 | } 441 | return 442 | } 443 | 444 | func (self *Demuxer) Streams() (streams []av.CodecData, err error) { 445 | if err = self.prepare(); err != nil { 446 | return 447 | } 448 | streams = self.prober.Streams 449 | return 450 | } 451 | 452 | func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { 453 | if err = self.prepare(); err != nil { 454 | return 455 | } 456 | 457 | if !self.prober.Empty() { 458 | pkt = self.prober.PopPacket() 459 | return 460 | } 461 | 462 | for { 463 | var tag flvio.Tag 464 | var timestamp int32 465 | if tag, timestamp, err = flvio.ReadTag(self.bufr, self.b); err != nil { 466 | return 467 | } 468 | 469 | var ok bool 470 | if pkt, ok = self.prober.TagToPacket(tag, timestamp); ok { 471 | return 472 | } 473 | } 474 | 475 | return 476 | } 477 | 478 | func Handler(h *avutil.RegisterHandler) { 479 | h.Probe = func(b []byte) bool { 480 | return b[0] == 'F' && b[1] == 'L' && b[2] == 'V' 481 | } 482 | 483 | h.Ext = ".flv" 484 | 485 | h.ReaderDemuxer = func(r io.Reader) av.Demuxer { 486 | return NewDemuxer(r) 487 | } 488 | 489 | h.WriterMuxer = func(w io.Writer) av.Muxer { 490 | return NewMuxer(w) 491 | } 492 | 493 | h.CodecTypes = CodecTypes 494 | } 495 | -------------------------------------------------------------------------------- /format/flv/flvio/amf0.go: -------------------------------------------------------------------------------- 1 | package flvio 2 | 3 | import ( 4 | "strings" 5 | "math" 6 | "fmt" 7 | "time" 8 | "github.com/nareix/joy4/utils/bits/pio" 9 | ) 10 | 11 | type AMF0ParseError struct { 12 | Offset int 13 | Message string 14 | Next *AMF0ParseError 15 | } 16 | 17 | func (self *AMF0ParseError) Error() string { 18 | s := []string{} 19 | for p := self; p != nil; p = p.Next { 20 | s = append(s, fmt.Sprintf("%s:%d", p.Message, p.Offset)) 21 | } 22 | return "amf0 parse error: " + strings.Join(s, ",") 23 | } 24 | 25 | func amf0ParseErr(message string, offset int, err error) error { 26 | next, _ := err.(*AMF0ParseError) 27 | return &AMF0ParseError{ 28 | Offset: offset, 29 | Message: message, 30 | Next: next, 31 | } 32 | } 33 | 34 | type AMFMap map[string]interface{} 35 | type AMFArray []interface{} 36 | type AMFECMAArray map[string]interface{} 37 | 38 | func parseBEFloat64(b []byte) float64 { 39 | return math.Float64frombits(pio.U64BE(b)) 40 | } 41 | 42 | func fillBEFloat64(b []byte, f float64) int { 43 | pio.PutU64BE(b, math.Float64bits(f)) 44 | return 8 45 | } 46 | 47 | const lenAMF0Number = 9 48 | 49 | func fillAMF0Number(b []byte, f float64) int { 50 | b[0] = numbermarker 51 | fillBEFloat64(b[1:], f) 52 | return lenAMF0Number 53 | } 54 | 55 | const ( 56 | amf3undefinedmarker = iota 57 | amf3nullmarker 58 | amf3falsemarker 59 | amf3truemarker 60 | amf3integermarker 61 | amf3doublemarker 62 | amf3stringmarker 63 | amf3xmldocmarker 64 | amf3datemarker 65 | amf3arraymarker 66 | amf3objectmarker 67 | amf3xmlmarker 68 | amf3bytearraymarker 69 | amf3vectorintmarker 70 | amf3vectoruintmarker 71 | amf3vectordoublemarker 72 | amf3vectorobjectmarker 73 | amf3dictionarymarker 74 | ) 75 | 76 | const ( 77 | numbermarker = iota 78 | booleanmarker 79 | stringmarker 80 | objectmarker 81 | movieclipmarker 82 | nullmarker 83 | undefinedmarker 84 | referencemarker 85 | ecmaarraymarker 86 | objectendmarker 87 | strictarraymarker 88 | datemarker 89 | longstringmarker 90 | unsupportedmarker 91 | recordsetmarker 92 | xmldocumentmarker 93 | typedobjectmarker 94 | avmplusobjectmarker 95 | ) 96 | 97 | func LenAMF0Val(_val interface{}) (n int) { 98 | switch val := _val.(type) { 99 | case int8: 100 | n += lenAMF0Number 101 | case int16: 102 | n += lenAMF0Number 103 | case int32: 104 | n += lenAMF0Number 105 | case int64: 106 | n += lenAMF0Number 107 | case int: 108 | n += lenAMF0Number 109 | case uint8: 110 | n += lenAMF0Number 111 | case uint16: 112 | n += lenAMF0Number 113 | case uint32: 114 | n += lenAMF0Number 115 | case uint64: 116 | n += lenAMF0Number 117 | case uint: 118 | n += lenAMF0Number 119 | case float32: 120 | n += lenAMF0Number 121 | case float64: 122 | n += lenAMF0Number 123 | 124 | case string: 125 | u := len(val) 126 | if u <= 65536 { 127 | n += 3 128 | } else { 129 | n += 5 130 | } 131 | n += int(u) 132 | 133 | case AMFECMAArray: 134 | n += 5 135 | for k, v := range val { 136 | n += 2+len(k) 137 | n += LenAMF0Val(v) 138 | } 139 | n += 3 140 | 141 | case AMFMap: 142 | n++ 143 | for k, v := range val { 144 | if len(k) > 0 { 145 | n += 2+len(k) 146 | n += LenAMF0Val(v) 147 | } 148 | } 149 | n += 3 150 | 151 | case AMFArray: 152 | n += 5 153 | for _, v := range val { 154 | n += LenAMF0Val(v) 155 | } 156 | 157 | case time.Time: 158 | n += 1+8+2 159 | 160 | case bool: 161 | n += 2 162 | 163 | case nil: 164 | n++ 165 | } 166 | 167 | return 168 | } 169 | 170 | func FillAMF0Val(b []byte, _val interface{}) (n int) { 171 | switch val := _val.(type) { 172 | case int8: 173 | n += fillAMF0Number(b[n:], float64(val)) 174 | case int16: 175 | n += fillAMF0Number(b[n:], float64(val)) 176 | case int32: 177 | n += fillAMF0Number(b[n:], float64(val)) 178 | case int64: 179 | n += fillAMF0Number(b[n:], float64(val)) 180 | case int: 181 | n += fillAMF0Number(b[n:], float64(val)) 182 | case uint8: 183 | n += fillAMF0Number(b[n:], float64(val)) 184 | case uint16: 185 | n += fillAMF0Number(b[n:], float64(val)) 186 | case uint32: 187 | n += fillAMF0Number(b[n:], float64(val)) 188 | case uint64: 189 | n += fillAMF0Number(b[n:], float64(val)) 190 | case uint: 191 | n += fillAMF0Number(b[n:], float64(val)) 192 | case float32: 193 | n += fillAMF0Number(b[n:], float64(val)) 194 | case float64: 195 | n += fillAMF0Number(b[n:], float64(val)) 196 | 197 | case string: 198 | u := len(val) 199 | if u <= 65536 { 200 | b[n] = stringmarker 201 | n++ 202 | pio.PutU16BE(b[n:], uint16(u)) 203 | n += 2 204 | } else { 205 | b[n] = longstringmarker 206 | n++ 207 | pio.PutU32BE(b[n:], uint32(u)) 208 | n += 4 209 | } 210 | copy(b[n:], []byte(val)) 211 | n += len(val) 212 | 213 | case AMFECMAArray: 214 | b[n] = ecmaarraymarker 215 | n++ 216 | pio.PutU32BE(b[n:], uint32(len(val))) 217 | n += 4 218 | for k, v := range val { 219 | pio.PutU16BE(b[n:], uint16(len(k))) 220 | n += 2 221 | copy(b[n:], []byte(k)) 222 | n += len(k) 223 | n += FillAMF0Val(b[n:], v) 224 | } 225 | pio.PutU24BE(b[n:], 0x000009) 226 | n += 3 227 | 228 | case AMFMap: 229 | b[n] = objectmarker 230 | n++ 231 | for k, v := range val { 232 | if len(k) > 0 { 233 | pio.PutU16BE(b[n:], uint16(len(k))) 234 | n += 2 235 | copy(b[n:], []byte(k)) 236 | n += len(k) 237 | n += FillAMF0Val(b[n:], v) 238 | } 239 | } 240 | pio.PutU24BE(b[n:], 0x000009) 241 | n += 3 242 | 243 | case AMFArray: 244 | b[n] = strictarraymarker 245 | n++ 246 | pio.PutU32BE(b[n:], uint32(len(val))) 247 | n += 4 248 | for _, v := range val { 249 | n += FillAMF0Val(b[n:], v) 250 | } 251 | 252 | case time.Time: 253 | b[n] = datemarker 254 | n++ 255 | u := val.UnixNano() 256 | f := float64(u/1000000) 257 | n += fillBEFloat64(b[n:], f) 258 | pio.PutU16BE(b[n:], uint16(0)) 259 | n += 2 260 | 261 | case bool: 262 | b[n] = booleanmarker 263 | n++ 264 | var u uint8 265 | if val { 266 | u = 1 267 | } else { 268 | u = 0 269 | } 270 | b[n] = u 271 | n++ 272 | 273 | case nil: 274 | b[n] = nullmarker 275 | n++ 276 | } 277 | 278 | return 279 | } 280 | 281 | 282 | func ParseAMF0Val(b []byte) (val interface{}, n int, err error) { 283 | return parseAMF0Val(b, 0) 284 | } 285 | 286 | func parseAMF0Val(b []byte, offset int) (val interface{}, n int, err error) { 287 | if len(b) < n+1 { 288 | err = amf0ParseErr("marker", offset+n, err) 289 | return 290 | } 291 | marker := b[n] 292 | n++ 293 | 294 | switch marker { 295 | case numbermarker: 296 | if len(b) < n+8 { 297 | err = amf0ParseErr("number", offset+n, err) 298 | return 299 | } 300 | val = parseBEFloat64(b[n:]) 301 | n += 8 302 | 303 | case booleanmarker: 304 | if len(b) < n+1 { 305 | err = amf0ParseErr("boolean", offset+n, err) 306 | return 307 | } 308 | val = b[n] != 0 309 | n++ 310 | 311 | case stringmarker: 312 | if len(b) < n+2 { 313 | err = amf0ParseErr("string.length", offset+n, err) 314 | return 315 | } 316 | length := int(pio.U16BE(b[n:])) 317 | n += 2 318 | 319 | if len(b) < n+length { 320 | err = amf0ParseErr("string.body", offset+n, err) 321 | return 322 | } 323 | val = string(b[n:n+length]) 324 | n += length 325 | 326 | case objectmarker: 327 | obj := AMFMap{} 328 | for { 329 | if len(b) < n+2 { 330 | err = amf0ParseErr("object.key.length", offset+n, err) 331 | return 332 | } 333 | length := int(pio.U16BE(b[n:])) 334 | n += 2 335 | if length == 0 { 336 | break 337 | } 338 | 339 | if len(b) < n+length { 340 | err = amf0ParseErr("object.key.body", offset+n, err) 341 | return 342 | } 343 | okey := string(b[n:n+length]) 344 | n += length 345 | 346 | var nval int 347 | var oval interface{} 348 | if oval, nval, err = parseAMF0Val(b[n:], offset+n); err != nil { 349 | err = amf0ParseErr("object.val", offset+n, err) 350 | return 351 | } 352 | n += nval 353 | 354 | obj[okey] = oval 355 | } 356 | if len(b) < n+1 { 357 | err = amf0ParseErr("object.end", offset+n, err) 358 | return 359 | } 360 | n++ 361 | val = obj 362 | 363 | case nullmarker: 364 | case undefinedmarker: 365 | 366 | case ecmaarraymarker: 367 | if len(b) < n+4 { 368 | err = amf0ParseErr("array.count", offset+n, err) 369 | return 370 | } 371 | n += 4 372 | 373 | obj := AMFMap{} 374 | for { 375 | if len(b) < n+2 { 376 | err = amf0ParseErr("array.key.length", offset+n, err) 377 | return 378 | } 379 | length := int(pio.U16BE(b[n:])) 380 | n += 2 381 | 382 | if length == 0 { 383 | break 384 | } 385 | 386 | if len(b) < n+length { 387 | err = amf0ParseErr("array.key.body", offset+n, err) 388 | return 389 | } 390 | okey := string(b[n:n+length]) 391 | n += length 392 | 393 | var nval int 394 | var oval interface{} 395 | if oval, nval, err = parseAMF0Val(b[n:], offset+n); err != nil { 396 | err = amf0ParseErr("array.val", offset+n, err) 397 | return 398 | } 399 | n += nval 400 | 401 | obj[okey] = oval 402 | } 403 | if len(b) < n+1 { 404 | err = amf0ParseErr("array.end", offset+n, err) 405 | return 406 | } 407 | n += 1 408 | val = obj 409 | 410 | case objectendmarker: 411 | if len(b) < n+3 { 412 | err = amf0ParseErr("objectend", offset+n, err) 413 | return 414 | } 415 | n += 3 416 | 417 | case strictarraymarker: 418 | if len(b) < n+4 { 419 | err = amf0ParseErr("strictarray.count", offset+n, err) 420 | return 421 | } 422 | count := int(pio.U32BE(b[n:])) 423 | n += 4 424 | 425 | obj := make(AMFArray, count) 426 | for i := 0; i < int(count); i++ { 427 | var nval int 428 | if obj[i], nval, err = parseAMF0Val(b[n:], offset+n); err != nil { 429 | err = amf0ParseErr("strictarray.val", offset+n, err) 430 | return 431 | } 432 | n += nval 433 | } 434 | val = obj 435 | 436 | case datemarker: 437 | if len(b) < n+8+2 { 438 | err = amf0ParseErr("date", offset+n, err) 439 | return 440 | } 441 | ts := parseBEFloat64(b[n:]) 442 | n += 8+2 443 | 444 | val = time.Unix(int64(ts/1000), (int64(ts)%1000)*1000000) 445 | 446 | case longstringmarker: 447 | if len(b) < n+4 { 448 | err = amf0ParseErr("longstring.length", offset+n, err) 449 | return 450 | } 451 | length := int(pio.U32BE(b[n:])) 452 | n += 4 453 | 454 | if len(b) < n+length { 455 | err = amf0ParseErr("longstring.body", offset+n, err) 456 | return 457 | } 458 | val = string(b[n:n+length]) 459 | n += length 460 | 461 | default: 462 | err = amf0ParseErr(fmt.Sprintf("invalidmarker=%d", marker), offset+n, err) 463 | return 464 | } 465 | 466 | return 467 | } 468 | 469 | -------------------------------------------------------------------------------- /format/flv/flvio/flvio.go: -------------------------------------------------------------------------------- 1 | package flvio 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nareix/joy4/utils/bits/pio" 6 | "github.com/nareix/joy4/av" 7 | "io" 8 | "time" 9 | ) 10 | 11 | func TsToTime(ts int32) time.Duration { 12 | return time.Millisecond * time.Duration(ts) 13 | } 14 | 15 | func TimeToTs(tm time.Duration) int32 { 16 | return int32(tm / time.Millisecond) 17 | } 18 | 19 | const MaxTagSubHeaderLength = 16 20 | 21 | const ( 22 | TAG_AUDIO = 8 23 | TAG_VIDEO = 9 24 | TAG_SCRIPTDATA = 18 25 | ) 26 | 27 | const ( 28 | SOUND_MP3 = 2 29 | SOUND_NELLYMOSER_16KHZ_MONO = 4 30 | SOUND_NELLYMOSER_8KHZ_MONO = 5 31 | SOUND_NELLYMOSER = 6 32 | SOUND_ALAW = 7 33 | SOUND_MULAW = 8 34 | SOUND_AAC = 10 35 | SOUND_SPEEX = 11 36 | 37 | SOUND_5_5Khz = 0 38 | SOUND_11Khz = 1 39 | SOUND_22Khz = 2 40 | SOUND_44Khz = 3 41 | 42 | SOUND_8BIT = 0 43 | SOUND_16BIT = 1 44 | 45 | SOUND_MONO = 0 46 | SOUND_STEREO = 1 47 | 48 | AAC_SEQHDR = 0 49 | AAC_RAW = 1 50 | ) 51 | 52 | const ( 53 | AVC_SEQHDR = 0 54 | AVC_NALU = 1 55 | AVC_EOS = 2 56 | 57 | FRAME_KEY = 1 58 | FRAME_INTER = 2 59 | 60 | VIDEO_H264 = 7 61 | ) 62 | 63 | type Tag struct { 64 | Type uint8 65 | 66 | /* 67 | SoundFormat: UB[4] 68 | 0 = Linear PCM, platform endian 69 | 1 = ADPCM 70 | 2 = MP3 71 | 3 = Linear PCM, little endian 72 | 4 = Nellymoser 16-kHz mono 73 | 5 = Nellymoser 8-kHz mono 74 | 6 = Nellymoser 75 | 7 = G.711 A-law logarithmic PCM 76 | 8 = G.711 mu-law logarithmic PCM 77 | 9 = reserved 78 | 10 = AAC 79 | 11 = Speex 80 | 14 = MP3 8-Khz 81 | 15 = Device-specific sound 82 | Formats 7, 8, 14, and 15 are reserved for internal use 83 | AAC is supported in Flash Player 9,0,115,0 and higher. 84 | Speex is supported in Flash Player 10 and higher. 85 | */ 86 | SoundFormat uint8 87 | 88 | /* 89 | SoundRate: UB[2] 90 | Sampling rate 91 | 0 = 5.5-kHz For AAC: always 3 92 | 1 = 11-kHz 93 | 2 = 22-kHz 94 | 3 = 44-kHz 95 | */ 96 | SoundRate uint8 97 | 98 | /* 99 | SoundSize: UB[1] 100 | 0 = snd8Bit 101 | 1 = snd16Bit 102 | Size of each sample. 103 | This parameter only pertains to uncompressed formats. 104 | Compressed formats always decode to 16 bits internally 105 | */ 106 | SoundSize uint8 107 | 108 | /* 109 | SoundType: UB[1] 110 | 0 = sndMono 111 | 1 = sndStereo 112 | Mono or stereo sound For Nellymoser: always 0 113 | For AAC: always 1 114 | */ 115 | SoundType uint8 116 | 117 | /* 118 | 0: AAC sequence header 119 | 1: AAC raw 120 | */ 121 | AACPacketType uint8 122 | 123 | /* 124 | 1: keyframe (for AVC, a seekable frame) 125 | 2: inter frame (for AVC, a non- seekable frame) 126 | 3: disposable inter frame (H.263 only) 127 | 4: generated keyframe (reserved for server use only) 128 | 5: video info/command frame 129 | */ 130 | FrameType uint8 131 | 132 | /* 133 | 1: JPEG (currently unused) 134 | 2: Sorenson H.263 135 | 3: Screen video 136 | 4: On2 VP6 137 | 5: On2 VP6 with alpha channel 138 | 6: Screen video version 2 139 | 7: AVC 140 | */ 141 | CodecID uint8 142 | 143 | /* 144 | 0: AVC sequence header 145 | 1: AVC NALU 146 | 2: AVC end of sequence (lower level NALU sequence ender is not required or supported) 147 | */ 148 | AVCPacketType uint8 149 | 150 | CompositionTime int32 151 | 152 | Data []byte 153 | } 154 | 155 | func (self Tag) ChannelLayout() av.ChannelLayout { 156 | if self.SoundType == SOUND_MONO { 157 | return av.CH_MONO 158 | } else { 159 | return av.CH_STEREO 160 | } 161 | } 162 | 163 | func (self *Tag) audioParseHeader(b []byte) (n int, err error) { 164 | if len(b) < n+1 { 165 | err = fmt.Errorf("audiodata: parse invalid") 166 | return 167 | } 168 | 169 | flags := b[n] 170 | n++ 171 | self.SoundFormat = flags >> 4 172 | self.SoundRate = (flags >> 2) & 0x3 173 | self.SoundSize = (flags >> 1) & 0x1 174 | self.SoundType = flags & 0x1 175 | 176 | switch self.SoundFormat { 177 | case SOUND_AAC: 178 | if len(b) < n+1 { 179 | err = fmt.Errorf("audiodata: parse invalid") 180 | return 181 | } 182 | self.AACPacketType = b[n] 183 | n++ 184 | } 185 | 186 | return 187 | } 188 | 189 | func (self Tag) audioFillHeader(b []byte) (n int) { 190 | var flags uint8 191 | flags |= self.SoundFormat << 4 192 | flags |= self.SoundRate << 2 193 | flags |= self.SoundSize << 1 194 | flags |= self.SoundType 195 | b[n] = flags 196 | n++ 197 | 198 | switch self.SoundFormat { 199 | case SOUND_AAC: 200 | b[n] = self.AACPacketType 201 | n++ 202 | } 203 | 204 | return 205 | } 206 | 207 | func (self *Tag) videoParseHeader(b []byte) (n int, err error) { 208 | if len(b) < n+1 { 209 | err = fmt.Errorf("videodata: parse invalid") 210 | return 211 | } 212 | flags := b[n] 213 | self.FrameType = flags >> 4 214 | self.CodecID = flags & 0xf 215 | n++ 216 | 217 | if self.FrameType == FRAME_INTER || self.FrameType == FRAME_KEY { 218 | if len(b) < n+4 { 219 | err = fmt.Errorf("videodata: parse invalid") 220 | return 221 | } 222 | self.AVCPacketType = b[n] 223 | n++ 224 | 225 | self.CompositionTime = pio.I24BE(b[n:]) 226 | n += 3 227 | } 228 | 229 | return 230 | } 231 | 232 | func (self Tag) videoFillHeader(b []byte) (n int) { 233 | flags := self.FrameType<<4 | self.CodecID 234 | b[n] = flags 235 | n++ 236 | b[n] = self.AVCPacketType 237 | n++ 238 | pio.PutI24BE(b[n:], self.CompositionTime) 239 | n += 3 240 | return 241 | } 242 | 243 | func (self Tag) FillHeader(b []byte) (n int) { 244 | switch self.Type { 245 | case TAG_AUDIO: 246 | return self.audioFillHeader(b) 247 | 248 | case TAG_VIDEO: 249 | return self.videoFillHeader(b) 250 | } 251 | 252 | return 253 | } 254 | 255 | func (self *Tag) ParseHeader(b []byte) (n int, err error) { 256 | switch self.Type { 257 | case TAG_AUDIO: 258 | return self.audioParseHeader(b) 259 | 260 | case TAG_VIDEO: 261 | return self.videoParseHeader(b) 262 | } 263 | 264 | return 265 | } 266 | 267 | const ( 268 | // TypeFlagsReserved UB[5] 269 | // TypeFlagsAudio UB[1] Audio tags are present 270 | // TypeFlagsReserved UB[1] Must be 0 271 | // TypeFlagsVideo UB[1] Video tags are present 272 | FILE_HAS_AUDIO = 0x4 273 | FILE_HAS_VIDEO = 0x1 274 | ) 275 | 276 | const TagHeaderLength = 11 277 | const TagTrailerLength = 4 278 | 279 | func ParseTagHeader(b []byte) (tag Tag, ts int32, datalen int, err error) { 280 | tagtype := b[0] 281 | 282 | switch tagtype { 283 | case TAG_AUDIO, TAG_VIDEO, TAG_SCRIPTDATA: 284 | tag = Tag{Type: tagtype} 285 | 286 | default: 287 | err = fmt.Errorf("flvio: ReadTag tagtype=%d invalid", tagtype) 288 | return 289 | } 290 | 291 | datalen = int(pio.U24BE(b[1:4])) 292 | 293 | var tslo uint32 294 | var tshi uint8 295 | tslo = pio.U24BE(b[4:7]) 296 | tshi = b[7] 297 | ts = int32(tslo | uint32(tshi)<<24) 298 | 299 | return 300 | } 301 | 302 | func ReadTag(r io.Reader, b []byte) (tag Tag, ts int32, err error) { 303 | if _, err = io.ReadFull(r, b[:TagHeaderLength]); err != nil { 304 | return 305 | } 306 | var datalen int 307 | if tag, ts, datalen, err = ParseTagHeader(b); err != nil { 308 | return 309 | } 310 | 311 | data := make([]byte, datalen) 312 | if _, err = io.ReadFull(r, data); err != nil { 313 | return 314 | } 315 | 316 | var n int 317 | if n, err = (&tag).ParseHeader(data); err != nil { 318 | return 319 | } 320 | tag.Data = data[n:] 321 | 322 | if _, err = io.ReadFull(r, b[:4]); err != nil { 323 | return 324 | } 325 | return 326 | } 327 | 328 | func FillTagHeader(b []byte, tagtype uint8, datalen int, ts int32) (n int) { 329 | b[n] = tagtype 330 | n++ 331 | pio.PutU24BE(b[n:], uint32(datalen)) 332 | n += 3 333 | pio.PutU24BE(b[n:], uint32(ts&0xffffff)) 334 | n += 3 335 | b[n] = uint8(ts >> 24) 336 | n++ 337 | pio.PutI24BE(b[n:], 0) 338 | n += 3 339 | return 340 | } 341 | 342 | func FillTagTrailer(b []byte, datalen int) (n int) { 343 | pio.PutU32BE(b[n:], uint32(datalen+TagHeaderLength)) 344 | n += 4 345 | return 346 | } 347 | 348 | func WriteTag(w io.Writer, tag Tag, ts int32, b []byte) (err error) { 349 | data := tag.Data 350 | 351 | n := tag.FillHeader(b[TagHeaderLength:]) 352 | datalen := len(data) + n 353 | 354 | n += FillTagHeader(b, tag.Type, datalen, ts) 355 | 356 | if _, err = w.Write(b[:n]); err != nil { 357 | return 358 | } 359 | 360 | if _, err = w.Write(data); err != nil { 361 | return 362 | } 363 | 364 | n = FillTagTrailer(b, datalen) 365 | if _, err = w.Write(b[:n]); err != nil { 366 | return 367 | } 368 | 369 | return 370 | } 371 | 372 | const FileHeaderLength = 9 373 | 374 | func FillFileHeader(b []byte, flags uint8) (n int) { 375 | // 'FLV', version 1 376 | pio.PutU32BE(b[n:], 0x464c5601) 377 | n += 4 378 | 379 | b[n] = flags 380 | n++ 381 | 382 | // DataOffset: UI32 Offset in bytes from start of file to start of body (that is, size of header) 383 | // The DataOffset field usually has a value of 9 for FLV version 1. 384 | pio.PutU32BE(b[n:], 9) 385 | n += 4 386 | 387 | // PreviousTagSize0: UI32 Always 0 388 | pio.PutU32BE(b[n:], 0) 389 | n += 4 390 | 391 | return 392 | } 393 | 394 | func ParseFileHeader(b []byte) (flags uint8, skip int, err error) { 395 | flv := pio.U24BE(b[0:3]) 396 | if flv != 0x464c56 { // 'FLV' 397 | err = fmt.Errorf("flvio: file header cc3 invalid") 398 | return 399 | } 400 | 401 | flags = b[4] 402 | 403 | skip = int(pio.U32BE(b[5:9])) - 9 + 4 404 | if skip < 0 { 405 | err = fmt.Errorf("flvio: file header datasize invalid") 406 | return 407 | } 408 | 409 | return 410 | } 411 | -------------------------------------------------------------------------------- /format/format.go: -------------------------------------------------------------------------------- 1 | package format 2 | 3 | import ( 4 | "github.com/nareix/joy4/format/mp4" 5 | "github.com/nareix/joy4/format/ts" 6 | "github.com/nareix/joy4/format/rtmp" 7 | "github.com/nareix/joy4/format/rtsp" 8 | "github.com/nareix/joy4/format/flv" 9 | "github.com/nareix/joy4/format/aac" 10 | "github.com/nareix/joy4/av/avutil" 11 | ) 12 | 13 | func RegisterAll() { 14 | avutil.DefaultHandlers.Add(mp4.Handler) 15 | avutil.DefaultHandlers.Add(ts.Handler) 16 | avutil.DefaultHandlers.Add(rtmp.Handler) 17 | avutil.DefaultHandlers.Add(rtsp.Handler) 18 | avutil.DefaultHandlers.Add(flv.Handler) 19 | avutil.DefaultHandlers.Add(aac.Handler) 20 | } 21 | 22 | -------------------------------------------------------------------------------- /format/mp4/demuxer.go: -------------------------------------------------------------------------------- 1 | package mp4 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "time" 8 | 9 | "github.com/nareix/joy4/av" 10 | "github.com/nareix/joy4/codec/aacparser" 11 | "github.com/nareix/joy4/codec/h264parser" 12 | "github.com/nareix/joy4/format/mp4/mp4io" 13 | ) 14 | 15 | type Demuxer struct { 16 | r io.ReadSeeker 17 | streams []*Stream 18 | movieAtom *mp4io.Movie 19 | } 20 | 21 | func NewDemuxer(r io.ReadSeeker) *Demuxer { 22 | return &Demuxer{ 23 | r: r, 24 | } 25 | } 26 | 27 | func (self *Demuxer) Streams() (streams []av.CodecData, err error) { 28 | if err = self.probe(); err != nil { 29 | return 30 | } 31 | for _, stream := range self.streams { 32 | streams = append(streams, stream.CodecData) 33 | } 34 | return 35 | } 36 | 37 | func (self *Demuxer) readat(pos int64, b []byte) (err error) { 38 | if _, err = self.r.Seek(pos, 0); err != nil { 39 | return 40 | } 41 | if _, err = io.ReadFull(self.r, b); err != nil { 42 | return 43 | } 44 | return 45 | } 46 | 47 | func (self *Demuxer) probe() (err error) { 48 | if self.movieAtom != nil { 49 | return 50 | } 51 | 52 | var moov *mp4io.Movie 53 | var atoms []mp4io.Atom 54 | 55 | if atoms, err = mp4io.ReadFileAtoms(self.r); err != nil { 56 | return 57 | } 58 | if _, err = self.r.Seek(0, 0); err != nil { 59 | return 60 | } 61 | 62 | for _, atom := range atoms { 63 | if atom.Tag() == mp4io.MOOV { 64 | moov = atom.(*mp4io.Movie) 65 | } 66 | } 67 | 68 | if moov == nil { 69 | err = fmt.Errorf("mp4: 'moov' atom not found") 70 | return 71 | } 72 | 73 | self.streams = []*Stream{} 74 | for i, atrack := range moov.Tracks { 75 | stream := &Stream{ 76 | trackAtom: atrack, 77 | demuxer: self, 78 | idx: i, 79 | } 80 | if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil { 81 | stream.sample = atrack.Media.Info.Sample 82 | stream.timeScale = int64(atrack.Media.Header.TimeScale) 83 | } else { 84 | err = fmt.Errorf("mp4: sample table not found") 85 | return 86 | } 87 | 88 | if avc1 := atrack.GetAVC1Conf(); avc1 != nil { 89 | if stream.CodecData, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(avc1.Data); err != nil { 90 | return 91 | } 92 | self.streams = append(self.streams, stream) 93 | } else if esds := atrack.GetElemStreamDesc(); esds != nil { 94 | if stream.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(esds.DecConfig); err != nil { 95 | return 96 | } 97 | self.streams = append(self.streams, stream) 98 | } 99 | } 100 | 101 | self.movieAtom = moov 102 | return 103 | } 104 | 105 | func (self *Stream) setSampleIndex(index int) (err error) { 106 | found := false 107 | start := 0 108 | self.chunkGroupIndex = 0 109 | 110 | for self.chunkIndex = range self.sample.ChunkOffset.Entries { 111 | if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && 112 | uint32(self.chunkIndex+1) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { 113 | self.chunkGroupIndex++ 114 | } 115 | n := int(self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk) 116 | if index >= start && index < start+n { 117 | found = true 118 | self.sampleIndexInChunk = index - start 119 | break 120 | } 121 | start += n 122 | } 123 | if !found { 124 | err = fmt.Errorf("mp4: stream[%d]: cannot locate sample index in chunk", self.idx) 125 | return 126 | } 127 | 128 | if self.sample.SampleSize.SampleSize != 0 { 129 | self.sampleOffsetInChunk = int64(self.sampleIndexInChunk) * int64(self.sample.SampleSize.SampleSize) 130 | } else { 131 | if index >= len(self.sample.SampleSize.Entries) { 132 | err = fmt.Errorf("mp4: stream[%d]: sample index out of range", self.idx) 133 | return 134 | } 135 | self.sampleOffsetInChunk = int64(0) 136 | for i := index - self.sampleIndexInChunk; i < index; i++ { 137 | self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[i]) 138 | } 139 | } 140 | 141 | self.dts = int64(0) 142 | start = 0 143 | found = false 144 | self.sttsEntryIndex = 0 145 | for self.sttsEntryIndex < len(self.sample.TimeToSample.Entries) { 146 | entry := self.sample.TimeToSample.Entries[self.sttsEntryIndex] 147 | n := int(entry.Count) 148 | if index >= start && index < start+n { 149 | self.sampleIndexInSttsEntry = index - start 150 | self.dts += int64(index-start) * int64(entry.Duration) 151 | found = true 152 | break 153 | } 154 | start += n 155 | self.dts += int64(n) * int64(entry.Duration) 156 | self.sttsEntryIndex++ 157 | } 158 | if !found { 159 | err = fmt.Errorf("mp4: stream[%d]: cannot locate sample index in stts entry", self.idx) 160 | return 161 | } 162 | 163 | if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { 164 | start = 0 165 | found = false 166 | self.cttsEntryIndex = 0 167 | for self.cttsEntryIndex < len(self.sample.CompositionOffset.Entries) { 168 | n := int(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count) 169 | if index >= start && index < start+n { 170 | self.sampleIndexInCttsEntry = index - start 171 | found = true 172 | break 173 | } 174 | start += n 175 | self.cttsEntryIndex++ 176 | } 177 | if !found { 178 | err = fmt.Errorf("mp4: stream[%d]: cannot locate sample index in ctts entry", self.idx) 179 | return 180 | } 181 | } 182 | 183 | if self.sample.SyncSample != nil { 184 | self.syncSampleIndex = 0 185 | for self.syncSampleIndex < len(self.sample.SyncSample.Entries)-1 { 186 | if self.sample.SyncSample.Entries[self.syncSampleIndex+1]-1 > uint32(index) { 187 | break 188 | } 189 | self.syncSampleIndex++ 190 | } 191 | } 192 | 193 | if false { 194 | fmt.Printf("mp4: stream[%d]: setSampleIndex chunkGroupIndex=%d chunkIndex=%d sampleOffsetInChunk=%d\n", 195 | self.idx, self.chunkGroupIndex, self.chunkIndex, self.sampleOffsetInChunk) 196 | } 197 | 198 | self.sampleIndex = index 199 | return 200 | } 201 | 202 | func (self *Stream) isSampleValid() bool { 203 | if self.chunkIndex >= len(self.sample.ChunkOffset.Entries) { 204 | return false 205 | } 206 | if self.chunkGroupIndex >= len(self.sample.SampleToChunk.Entries) { 207 | return false 208 | } 209 | if self.sttsEntryIndex >= len(self.sample.TimeToSample.Entries) { 210 | return false 211 | } 212 | if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { 213 | if self.cttsEntryIndex >= len(self.sample.CompositionOffset.Entries) { 214 | return false 215 | } 216 | } 217 | if self.sample.SyncSample != nil { 218 | if self.syncSampleIndex >= len(self.sample.SyncSample.Entries) { 219 | return false 220 | } 221 | } 222 | if self.sample.SampleSize.SampleSize != 0 { 223 | if self.sampleIndex >= len(self.sample.SampleSize.Entries) { 224 | return false 225 | } 226 | } 227 | return true 228 | } 229 | 230 | func (self *Stream) incSampleIndex() (duration int64) { 231 | if false { 232 | fmt.Printf("incSampleIndex sampleIndex=%d sampleOffsetInChunk=%d sampleIndexInChunk=%d chunkGroupIndex=%d chunkIndex=%d\n", 233 | self.sampleIndex, self.sampleOffsetInChunk, self.sampleIndexInChunk, self.chunkGroupIndex, self.chunkIndex) 234 | } 235 | 236 | self.sampleIndexInChunk++ 237 | if uint32(self.sampleIndexInChunk) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk { 238 | self.chunkIndex++ 239 | self.sampleIndexInChunk = 0 240 | self.sampleOffsetInChunk = int64(0) 241 | } else { 242 | if self.sample.SampleSize.SampleSize != 0 { 243 | self.sampleOffsetInChunk += int64(self.sample.SampleSize.SampleSize) 244 | } else { 245 | self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[self.sampleIndex]) 246 | } 247 | } 248 | 249 | if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && 250 | uint32(self.chunkIndex+1) == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { 251 | self.chunkGroupIndex++ 252 | } 253 | 254 | sttsEntry := self.sample.TimeToSample.Entries[self.sttsEntryIndex] 255 | duration = int64(sttsEntry.Duration) 256 | self.sampleIndexInSttsEntry++ 257 | self.dts += duration 258 | if uint32(self.sampleIndexInSttsEntry) == sttsEntry.Count { 259 | self.sampleIndexInSttsEntry = 0 260 | self.sttsEntryIndex++ 261 | } 262 | 263 | if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { 264 | self.sampleIndexInCttsEntry++ 265 | if uint32(self.sampleIndexInCttsEntry) == self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count { 266 | self.sampleIndexInCttsEntry = 0 267 | self.cttsEntryIndex++ 268 | } 269 | } 270 | 271 | if self.sample.SyncSample != nil { 272 | entries := self.sample.SyncSample.Entries 273 | if self.syncSampleIndex+1 < len(entries) && entries[self.syncSampleIndex+1]-1 == uint32(self.sampleIndex+1) { 274 | self.syncSampleIndex++ 275 | } 276 | } 277 | 278 | self.sampleIndex++ 279 | return 280 | } 281 | 282 | func (self *Stream) sampleCount() int { 283 | if self.sample.SampleSize.SampleSize == 0 { 284 | chunkGroupIndex := 0 285 | count := 0 286 | for chunkIndex := range self.sample.ChunkOffset.Entries { 287 | n := int(self.sample.SampleToChunk.Entries[chunkGroupIndex].SamplesPerChunk) 288 | count += n 289 | if chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && 290 | uint32(chunkIndex+1) == self.sample.SampleToChunk.Entries[chunkGroupIndex+1].FirstChunk { 291 | chunkGroupIndex++ 292 | } 293 | } 294 | return count 295 | } else { 296 | return len(self.sample.SampleSize.Entries) 297 | } 298 | } 299 | 300 | func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { 301 | if err = self.probe(); err != nil { 302 | return 303 | } 304 | if len(self.streams) == 0 { 305 | err = errors.New("mp4: no streams available while trying to read a packet") 306 | return 307 | } 308 | 309 | var chosen *Stream 310 | var chosenidx int 311 | for i, stream := range self.streams { 312 | if chosen == nil || stream.tsToTime(stream.dts) < chosen.tsToTime(chosen.dts) { 313 | chosen = stream 314 | chosenidx = i 315 | } 316 | } 317 | if false { 318 | fmt.Printf("ReadPacket: chosen index=%v time=%v\n", chosen.idx, chosen.tsToTime(chosen.dts)) 319 | } 320 | tm := chosen.tsToTime(chosen.dts) 321 | if pkt, err = chosen.readPacket(); err != nil { 322 | return 323 | } 324 | pkt.Time = tm 325 | pkt.Idx = int8(chosenidx) 326 | return 327 | } 328 | 329 | func (self *Demuxer) CurrentTime() (tm time.Duration) { 330 | if len(self.streams) > 0 { 331 | stream := self.streams[0] 332 | tm = stream.tsToTime(stream.dts) 333 | } 334 | return 335 | } 336 | 337 | func (self *Demuxer) SeekToTime(tm time.Duration) (err error) { 338 | for _, stream := range self.streams { 339 | if stream.Type().IsVideo() { 340 | if err = stream.seekToTime(tm); err != nil { 341 | return 342 | } 343 | tm = stream.tsToTime(stream.dts) 344 | break 345 | } 346 | } 347 | 348 | for _, stream := range self.streams { 349 | if !stream.Type().IsVideo() { 350 | if err = stream.seekToTime(tm); err != nil { 351 | return 352 | } 353 | } 354 | } 355 | 356 | return 357 | } 358 | 359 | func (self *Stream) readPacket() (pkt av.Packet, err error) { 360 | if !self.isSampleValid() { 361 | err = io.EOF 362 | return 363 | } 364 | //fmt.Println("readPacket", self.sampleIndex) 365 | 366 | chunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex] 367 | sampleSize := uint32(0) 368 | if self.sample.SampleSize.SampleSize != 0 { 369 | sampleSize = self.sample.SampleSize.SampleSize 370 | } else { 371 | sampleSize = self.sample.SampleSize.Entries[self.sampleIndex] 372 | } 373 | 374 | sampleOffset := int64(chunkOffset) + self.sampleOffsetInChunk 375 | pkt.Data = make([]byte, sampleSize) 376 | if err = self.demuxer.readat(sampleOffset, pkt.Data); err != nil { 377 | return 378 | } 379 | 380 | if self.sample.SyncSample != nil { 381 | if self.sample.SyncSample.Entries[self.syncSampleIndex]-1 == uint32(self.sampleIndex) { 382 | pkt.IsKeyFrame = true 383 | } 384 | } 385 | 386 | //println("pts/dts", self.ptsEntryIndex, self.dtsEntryIndex) 387 | if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { 388 | cts := int64(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Offset) 389 | pkt.CompositionTime = self.tsToTime(cts) 390 | } 391 | 392 | self.incSampleIndex() 393 | 394 | return 395 | } 396 | 397 | func (self *Stream) seekToTime(tm time.Duration) (err error) { 398 | index := self.timeToSampleIndex(tm) 399 | if err = self.setSampleIndex(index); err != nil { 400 | return 401 | } 402 | if false { 403 | fmt.Printf("stream[%d]: seekToTime index=%v time=%v cur=%v\n", self.idx, index, tm, self.tsToTime(self.dts)) 404 | } 405 | return 406 | } 407 | 408 | func (self *Stream) timeToSampleIndex(tm time.Duration) int { 409 | targetTs := self.timeToTs(tm) 410 | targetIndex := 0 411 | 412 | startTs := int64(0) 413 | endTs := int64(0) 414 | startIndex := 0 415 | endIndex := 0 416 | found := false 417 | for _, entry := range self.sample.TimeToSample.Entries { 418 | endTs = startTs + int64(entry.Count*entry.Duration) 419 | endIndex = startIndex + int(entry.Count) 420 | if targetTs >= startTs && targetTs < endTs { 421 | targetIndex = startIndex + int((targetTs-startTs)/int64(entry.Duration)) 422 | found = true 423 | } 424 | startTs = endTs 425 | startIndex = endIndex 426 | } 427 | if !found { 428 | if targetTs < 0 { 429 | targetIndex = 0 430 | } else { 431 | targetIndex = endIndex - 1 432 | } 433 | } 434 | 435 | if self.sample.SyncSample != nil { 436 | entries := self.sample.SyncSample.Entries 437 | for i := len(entries) - 1; i >= 0; i-- { 438 | if entries[i]-1 < uint32(targetIndex) { 439 | targetIndex = int(entries[i] - 1) 440 | break 441 | } 442 | } 443 | } 444 | 445 | return targetIndex 446 | } 447 | -------------------------------------------------------------------------------- /format/mp4/handler.go: -------------------------------------------------------------------------------- 1 | package mp4 2 | 3 | import ( 4 | "io" 5 | "github.com/nareix/joy4/av" 6 | "github.com/nareix/joy4/av/avutil" 7 | ) 8 | 9 | var CodecTypes = []av.CodecType{av.H264, av.AAC} 10 | 11 | func Handler(h *avutil.RegisterHandler) { 12 | h.Ext = ".mp4" 13 | 14 | h.Probe = func(b []byte) bool { 15 | switch string(b[4:8]) { 16 | case "moov","ftyp","free","mdat","moof": 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | h.ReaderDemuxer = func(r io.Reader) av.Demuxer { 23 | return NewDemuxer(r.(io.ReadSeeker)) 24 | } 25 | 26 | h.WriterMuxer = func(w io.Writer) av.Muxer { 27 | return NewMuxer(w.(io.WriteSeeker)) 28 | } 29 | 30 | h.CodecTypes = CodecTypes 31 | } 32 | 33 | -------------------------------------------------------------------------------- /format/mp4/mp4io/gen/pattern.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func moov_Movie() { 4 | atom(Header, MovieHeader) 5 | atom(MovieExtend, MovieExtend) 6 | atoms(Tracks, Track) 7 | _unknowns() 8 | } 9 | 10 | func mvhd_MovieHeader() { 11 | uint8(Version) 12 | uint24(Flags) 13 | time32(CreateTime) 14 | time32(ModifyTime) 15 | int32(TimeScale) 16 | int32(Duration) 17 | fixed32(PreferredRate) 18 | fixed16(PreferredVolume) 19 | _skip(10) 20 | array(Matrix, int32, 9) 21 | time32(PreviewTime) 22 | time32(PreviewDuration) 23 | time32(PosterTime) 24 | time32(SelectionTime) 25 | time32(SelectionDuration) 26 | time32(CurrentTime) 27 | int32(NextTrackId) 28 | } 29 | 30 | func trak_Track() { 31 | atom(Header, TrackHeader) 32 | atom(Media, Media) 33 | _unknowns() 34 | } 35 | 36 | func tkhd_TrackHeader() { 37 | uint8(Version) 38 | uint24(Flags) 39 | time32(CreateTime) 40 | time32(ModifyTime) 41 | int32(TrackId) 42 | _skip(4) 43 | int32(Duration) 44 | _skip(8) 45 | int16(Layer) 46 | int16(AlternateGroup) 47 | fixed16(Volume) 48 | _skip(2) 49 | array(Matrix, int32, 9) 50 | fixed32(TrackWidth) 51 | fixed32(TrackHeight) 52 | } 53 | 54 | func hdlr_HandlerRefer() { 55 | uint8(Version) 56 | uint24(Flags) 57 | bytes(Type, 4) 58 | bytes(SubType, 4) 59 | bytesleft(Name) 60 | } 61 | 62 | func mdia_Media() { 63 | atom(Header, MediaHeader) 64 | atom(Handler, HandlerRefer) 65 | atom(Info, MediaInfo) 66 | _unknowns() 67 | } 68 | 69 | func mdhd_MediaHeader() { 70 | uint8(Version) 71 | uint24(Flags) 72 | time32(CreateTime) 73 | time32(ModifyTime) 74 | int32(TimeScale) 75 | int32(Duration) 76 | int16(Language) 77 | int16(Quality) 78 | } 79 | 80 | func minf_MediaInfo() { 81 | atom(Sound, SoundMediaInfo) 82 | atom(Video, VideoMediaInfo) 83 | atom(Data, DataInfo) 84 | atom(Sample, SampleTable) 85 | _unknowns() 86 | } 87 | 88 | func dinf_DataInfo() { 89 | atom(Refer, DataRefer) 90 | _unknowns() 91 | } 92 | 93 | func dref_DataRefer() { 94 | uint8(Version) 95 | uint24(Flags) 96 | int32(_childrenNR) 97 | atom(Url, DataReferUrl) 98 | } 99 | 100 | func url__DataReferUrl() { 101 | uint8(Version) 102 | uint24(Flags) 103 | } 104 | 105 | func smhd_SoundMediaInfo() { 106 | uint8(Version) 107 | uint24(Flags) 108 | int16(Balance) 109 | _skip(2) 110 | } 111 | 112 | func vmhd_VideoMediaInfo() { 113 | uint8(Version) 114 | uint24(Flags) 115 | int16(GraphicsMode) 116 | array(Opcolor, int16, 3) 117 | } 118 | 119 | func stbl_SampleTable() { 120 | atom(SampleDesc, SampleDesc) 121 | atom(TimeToSample, TimeToSample) 122 | atom(CompositionOffset, CompositionOffset) 123 | atom(SampleToChunk, SampleToChunk) 124 | atom(SyncSample, SyncSample) 125 | atom(ChunkOffset, ChunkOffset) 126 | atom(SampleSize, SampleSize) 127 | } 128 | 129 | func stsd_SampleDesc() { 130 | uint8(Version) 131 | _skip(3) 132 | int32(_childrenNR) 133 | atom(AVC1Desc, AVC1Desc) 134 | atom(MP4ADesc, MP4ADesc) 135 | _unknowns() 136 | } 137 | 138 | func mp4a_MP4ADesc() { 139 | _skip(6) 140 | int16(DataRefIdx) 141 | int16(Version) 142 | int16(RevisionLevel) 143 | int32(Vendor) 144 | int16(NumberOfChannels) 145 | int16(SampleSize) 146 | int16(CompressionId) 147 | _skip(2) 148 | fixed32(SampleRate) 149 | atom(Conf, ElemStreamDesc) 150 | _unknowns() 151 | } 152 | 153 | func avc1_AVC1Desc() { 154 | _skip(6) 155 | int16(DataRefIdx) 156 | int16(Version) 157 | int16(Revision) 158 | int32(Vendor) 159 | int32(TemporalQuality) 160 | int32(SpatialQuality) 161 | int16(Width) 162 | int16(Height) 163 | fixed32(HorizontalResolution) 164 | fixed32(VorizontalResolution) 165 | _skip(4) 166 | int16(FrameCount) 167 | bytes(CompressorName, 32) 168 | int16(Depth) 169 | int16(ColorTableId) 170 | atom(Conf, AVC1Conf) 171 | _unknowns() 172 | } 173 | 174 | func avcC_AVC1Conf() { 175 | bytesleft(Data) 176 | } 177 | 178 | func stts_TimeToSample() { 179 | uint8(Version) 180 | uint24(Flags) 181 | uint32(_len_Entries) 182 | slice(Entries, TimeToSampleEntry) 183 | } 184 | 185 | func TimeToSampleEntry() { 186 | uint32(Count) 187 | uint32(Duration) 188 | } 189 | 190 | func stsc_SampleToChunk() { 191 | uint8(Version) 192 | uint24(Flags) 193 | uint32(_len_Entries) 194 | slice(Entries, SampleToChunkEntry) 195 | } 196 | 197 | func SampleToChunkEntry() { 198 | uint32(FirstChunk) 199 | uint32(SamplesPerChunk) 200 | uint32(SampleDescId) 201 | } 202 | 203 | func ctts_CompositionOffset() { 204 | uint8(Version) 205 | uint24(Flags) 206 | uint32(_len_Entries) 207 | slice(Entries, CompositionOffsetEntry) 208 | } 209 | 210 | func CompositionOffsetEntry() { 211 | uint32(Count) 212 | uint32(Offset) 213 | } 214 | 215 | func stss_SyncSample() { 216 | uint8(Version) 217 | uint24(Flags) 218 | uint32(_len_Entries) 219 | slice(Entries, uint32) 220 | } 221 | 222 | func stco_ChunkOffset() { 223 | uint8(Version) 224 | uint24(Flags) 225 | uint32(_len_Entries) 226 | slice(Entries, uint32) 227 | } 228 | 229 | func moof_MovieFrag() { 230 | atom(Header, MovieFragHeader) 231 | atoms(Tracks, TrackFrag) 232 | _unknowns() 233 | } 234 | 235 | func mfhd_MovieFragHeader() { 236 | uint8(Version) 237 | uint24(Flags) 238 | uint32(Seqnum) 239 | } 240 | 241 | func traf_TrackFrag() { 242 | atom(Header, TrackFragHeader) 243 | atom(DecodeTime, TrackFragDecodeTime) 244 | atom(Run, TrackFragRun) 245 | _unknowns() 246 | } 247 | 248 | func mvex_MovieExtend() { 249 | atoms(Tracks, TrackExtend) 250 | _unknowns() 251 | } 252 | 253 | func trex_TrackExtend() { 254 | uint8(Version) 255 | uint24(Flags) 256 | uint32(TrackId) 257 | uint32(DefaultSampleDescIdx) 258 | uint32(DefaultSampleDuration) 259 | uint32(DefaultSampleSize) 260 | uint32(DefaultSampleFlags) 261 | } 262 | 263 | func stsz_SampleSize() { 264 | uint8(Version) 265 | uint24(Flags) 266 | uint32(SampleSize) 267 | _code(func() { 268 | if self.SampleSize != 0 { 269 | return 270 | } 271 | }) 272 | uint32(_len_Entries) 273 | slice(Entries, uint32) 274 | } 275 | 276 | func trun_TrackFragRun() { 277 | uint8(Version) 278 | uint24(Flags) 279 | uint32(_len_Entries) 280 | 281 | uint32(DataOffset, _code(func() { 282 | if self.Flags&TRUN_DATA_OFFSET != 0 { 283 | doit() 284 | } 285 | })) 286 | 287 | uint32(FirstSampleFlags, _code(func() { 288 | if self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 { 289 | doit() 290 | } 291 | })) 292 | 293 | slice(Entries, TrackFragRunEntry, _code(func() { 294 | for i, entry := range self.Entries { 295 | var flags uint32 296 | if i > 0 { 297 | flags = self.Flags 298 | } else { 299 | flags = self.FirstSampleFlags 300 | } 301 | if flags&TRUN_SAMPLE_DURATION != 0 { 302 | pio.PutU32BE(b[n:], entry.Duration) 303 | n += 4 304 | } 305 | if flags&TRUN_SAMPLE_SIZE != 0 { 306 | pio.PutU32BE(b[n:], entry.Size) 307 | n += 4 308 | } 309 | if flags&TRUN_SAMPLE_FLAGS != 0 { 310 | pio.PutU32BE(b[n:], entry.Flags) 311 | n += 4 312 | } 313 | if flags&TRUN_SAMPLE_CTS != 0 { 314 | pio.PutU32BE(b[n:], entry.Cts) 315 | n += 4 316 | } 317 | } 318 | }, func() { 319 | for i := range self.Entries { 320 | var flags uint32 321 | if i > 0 { 322 | flags = self.Flags 323 | } else { 324 | flags = self.FirstSampleFlags 325 | } 326 | if flags&TRUN_SAMPLE_DURATION != 0 { 327 | n += 4 328 | } 329 | if flags&TRUN_SAMPLE_SIZE != 0 { 330 | n += 4 331 | } 332 | if flags&TRUN_SAMPLE_FLAGS != 0 { 333 | n += 4 334 | } 335 | if flags&TRUN_SAMPLE_CTS != 0 { 336 | n += 4 337 | } 338 | } 339 | }, func() { 340 | for i := 0; i < int(_len_Entries); i++ { 341 | var flags uint32 342 | if i > 0 { 343 | flags = self.Flags 344 | } else { 345 | flags = self.FirstSampleFlags 346 | } 347 | entry := &self.Entries[i] 348 | if flags&TRUN_SAMPLE_DURATION != 0 { 349 | entry.Duration = pio.U32BE(b[n:]) 350 | n += 4 351 | } 352 | if flags&TRUN_SAMPLE_SIZE != 0 { 353 | entry.Size = pio.U32BE(b[n:]) 354 | n += 4 355 | } 356 | if flags&TRUN_SAMPLE_FLAGS != 0 { 357 | entry.Flags = pio.U32BE(b[n:]) 358 | n += 4 359 | } 360 | if flags&TRUN_SAMPLE_CTS != 0 { 361 | entry.Cts = pio.U32BE(b[n:]) 362 | n += 4 363 | } 364 | } 365 | })) 366 | } 367 | 368 | func TrackFragRunEntry() { 369 | uint32(Duration) 370 | uint32(Size) 371 | uint32(Flags) 372 | uint32(Cts) 373 | } 374 | 375 | func tfhd_TrackFragHeader() { 376 | uint8(Version) 377 | uint24(Flags) 378 | 379 | uint64(BaseDataOffset, _code(func() { 380 | if self.Flags&TFHD_BASE_DATA_OFFSET != 0 { 381 | doit() 382 | } 383 | })) 384 | 385 | uint32(StsdId, _code(func() { 386 | if self.Flags&TFHD_STSD_ID != 0 { 387 | doit() 388 | } 389 | })) 390 | 391 | uint32(DefaultDuration, _code(func() { 392 | if self.Flags&TFHD_DEFAULT_DURATION != 0 { 393 | doit() 394 | } 395 | })) 396 | 397 | uint32(DefaultSize, _code(func() { 398 | if self.Flags&TFHD_DEFAULT_SIZE != 0 { 399 | doit() 400 | } 401 | })) 402 | 403 | uint32(DefaultFlags, _code(func() { 404 | if self.Flags&TFHD_DEFAULT_FLAGS != 0 { 405 | doit() 406 | } 407 | })) 408 | } 409 | 410 | func tfdt_TrackFragDecodeTime() { 411 | uint8(Version) 412 | uint24(Flags) 413 | time64(Time, _code(func() { 414 | if self.Version != 0 { 415 | PutTime64(b[n:], self.Time) 416 | n += 8 417 | } else { 418 | PutTime32(b[n:], self.Time) 419 | n += 4 420 | } 421 | }, func() { 422 | if self.Version != 0 { 423 | n += 8 424 | } else { 425 | n += 4 426 | } 427 | }, func() { 428 | if self.Version != 0 { 429 | self.Time = GetTime64(b[n:]) 430 | n += 8 431 | } else { 432 | self.Time = GetTime32(b[n:]) 433 | n += 4 434 | } 435 | })) 436 | } 437 | 438 | -------------------------------------------------------------------------------- /format/mp4/mp4io/mp4io.go: -------------------------------------------------------------------------------- 1 | 2 | package mp4io 3 | 4 | import ( 5 | "github.com/nareix/joy4/utils/bits/pio" 6 | "os" 7 | "io" 8 | "fmt" 9 | "time" 10 | "math" 11 | "strings" 12 | ) 13 | 14 | type ParseError struct { 15 | Debug string 16 | Offset int 17 | prev *ParseError 18 | } 19 | 20 | func (self *ParseError) Error() string { 21 | s := []string{} 22 | for p := self; p != nil; p = p.prev { 23 | s = append(s, fmt.Sprintf("%s:%d", p.Debug, p.Offset)) 24 | } 25 | return "mp4io: parse error: "+strings.Join(s, ",") 26 | } 27 | 28 | func parseErr(debug string, offset int, prev error) (err error) { 29 | _prev, _ := prev.(*ParseError) 30 | return &ParseError{Debug: debug, Offset: offset, prev: _prev} 31 | } 32 | 33 | func GetTime32(b []byte) (t time.Time) { 34 | sec := pio.U32BE(b) 35 | t = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC) 36 | t = t.Add(time.Second*time.Duration(sec)) 37 | return 38 | } 39 | 40 | func PutTime32(b []byte, t time.Time) { 41 | dur := t.Sub(time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC)) 42 | sec := uint32(dur/time.Second) 43 | pio.PutU32BE(b, sec) 44 | } 45 | 46 | func GetTime64(b []byte) (t time.Time) { 47 | sec := pio.U64BE(b) 48 | t = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC) 49 | t = t.Add(time.Second*time.Duration(sec)) 50 | return 51 | } 52 | 53 | func PutTime64(b []byte, t time.Time) { 54 | dur := t.Sub(time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC)) 55 | sec := uint64(dur/time.Second) 56 | pio.PutU64BE(b, sec) 57 | } 58 | 59 | func PutFixed16(b []byte, f float64) { 60 | intpart, fracpart := math.Modf(f) 61 | b[0] = uint8(intpart) 62 | b[1] = uint8(fracpart*256.0) 63 | } 64 | 65 | func GetFixed16(b []byte) float64 { 66 | return float64(b[0])+float64(b[1])/256.0 67 | } 68 | 69 | func PutFixed32(b []byte, f float64) { 70 | intpart, fracpart := math.Modf(f) 71 | pio.PutU16BE(b[0:2], uint16(intpart)) 72 | pio.PutU16BE(b[2:4], uint16(fracpart*65536.0)) 73 | } 74 | 75 | func GetFixed32(b []byte) float64 { 76 | return float64(pio.U16BE(b[0:2]))+float64(pio.U16BE(b[2:4]))/65536.0 77 | } 78 | 79 | type Tag uint32 80 | 81 | func (self Tag) String() string { 82 | var b [4]byte 83 | pio.PutU32BE(b[:], uint32(self)) 84 | for i := 0; i < 4; i++ { 85 | if b[i] == 0 { 86 | b[i] = ' ' 87 | } 88 | } 89 | return string(b[:]) 90 | } 91 | 92 | type Atom interface{ 93 | Pos() (int,int) 94 | Tag() Tag 95 | Marshal([]byte) int 96 | Unmarshal([]byte, int) (int,error) 97 | Len() int 98 | Children() []Atom 99 | } 100 | 101 | type AtomPos struct { 102 | Offset int 103 | Size int 104 | } 105 | 106 | func (self AtomPos) Pos() (int,int) { 107 | return self.Offset, self.Size 108 | } 109 | 110 | func (self *AtomPos) setPos(offset int, size int) { 111 | self.Offset, self.Size = offset, size 112 | } 113 | 114 | type Dummy struct { 115 | Data []byte 116 | Tag_ Tag 117 | AtomPos 118 | } 119 | 120 | func (self Dummy) Children() []Atom { 121 | return nil 122 | } 123 | 124 | func (self Dummy) Tag() Tag { 125 | return self.Tag_ 126 | } 127 | 128 | func (self Dummy) Len() int { 129 | return len(self.Data) 130 | } 131 | 132 | func (self Dummy) Marshal(b []byte) int { 133 | copy(b, self.Data) 134 | return len(self.Data) 135 | } 136 | 137 | func (self *Dummy) Unmarshal(b []byte, offset int) (n int, err error) { 138 | (&self.AtomPos).setPos(offset, len(b)) 139 | self.Data = b 140 | n = len(b) 141 | return 142 | } 143 | 144 | func StringToTag(tag string) Tag { 145 | var b [4]byte 146 | copy(b[:], []byte(tag)) 147 | return Tag(pio.U32BE(b[:])) 148 | } 149 | 150 | func FindChildrenByName(root Atom, tag string) Atom { 151 | return FindChildren(root, StringToTag(tag)) 152 | } 153 | 154 | func FindChildren(root Atom, tag Tag) Atom { 155 | if root.Tag() == tag { 156 | return root 157 | } 158 | for _, child := range root.Children() { 159 | if r := FindChildren(child, tag); r != nil { 160 | return r 161 | } 162 | } 163 | return nil 164 | } 165 | 166 | const ( 167 | TFHD_BASE_DATA_OFFSET = 0x01 168 | TFHD_STSD_ID = 0x02 169 | TFHD_DEFAULT_DURATION = 0x08 170 | TFHD_DEFAULT_SIZE = 0x10 171 | TFHD_DEFAULT_FLAGS = 0x20 172 | TFHD_DURATION_IS_EMPTY = 0x010000 173 | TFHD_DEFAULT_BASE_IS_MOOF = 0x020000 174 | ) 175 | 176 | const ( 177 | TRUN_DATA_OFFSET = 0x01 178 | TRUN_FIRST_SAMPLE_FLAGS = 0x04 179 | TRUN_SAMPLE_DURATION = 0x100 180 | TRUN_SAMPLE_SIZE = 0x200 181 | TRUN_SAMPLE_FLAGS = 0x400 182 | TRUN_SAMPLE_CTS = 0x800 183 | ) 184 | 185 | const ( 186 | MP4ESDescrTag = 3 187 | MP4DecConfigDescrTag = 4 188 | MP4DecSpecificDescrTag = 5 189 | ) 190 | 191 | type ElemStreamDesc struct { 192 | DecConfig []byte 193 | TrackId uint16 194 | AtomPos 195 | } 196 | 197 | func (self ElemStreamDesc) Children() []Atom { 198 | return nil 199 | } 200 | 201 | func (self ElemStreamDesc) fillLength(b []byte, length int) (n int) { 202 | for i := 3; i > 0; i-- { 203 | b[n] = uint8(length>>uint(7*i))&0x7f|0x80 204 | n++ 205 | } 206 | b[n] = uint8(length&0x7f) 207 | n++ 208 | return 209 | } 210 | 211 | func (self ElemStreamDesc) lenDescHdr() (n int) { 212 | return 5 213 | } 214 | 215 | func (self ElemStreamDesc) fillDescHdr(b []byte, tag uint8, datalen int) (n int) { 216 | b[n] = tag 217 | n++ 218 | n += self.fillLength(b[n:], datalen) 219 | return 220 | } 221 | 222 | func (self ElemStreamDesc) lenESDescHdr() (n int) { 223 | return self.lenDescHdr()+3 224 | } 225 | 226 | func (self ElemStreamDesc) fillESDescHdr(b []byte, datalen int) (n int) { 227 | n += self.fillDescHdr(b[n:], MP4ESDescrTag, datalen) 228 | pio.PutU16BE(b[n:], self.TrackId) 229 | n += 2 230 | b[n] = 0 // flags 231 | n++ 232 | return 233 | } 234 | 235 | func (self ElemStreamDesc) lenDecConfigDescHdr() (n int) { 236 | return self.lenDescHdr()+2+3+4+4+self.lenDescHdr() 237 | } 238 | 239 | func (self ElemStreamDesc) fillDecConfigDescHdr(b []byte, datalen int) (n int) { 240 | n += self.fillDescHdr(b[n:], MP4DecConfigDescrTag, datalen) 241 | b[n] = 0x40 // objectid 242 | n++ 243 | b[n] = 0x15 // streamtype 244 | n++ 245 | // buffer size db 246 | pio.PutU24BE(b[n:], 0) 247 | n += 3 248 | // max bitrage 249 | pio.PutU32BE(b[n:], uint32(200000)) 250 | n += 4 251 | // avg bitrage 252 | pio.PutU32BE(b[n:], uint32(0)) 253 | n += 4 254 | n += self.fillDescHdr(b[n:], MP4DecSpecificDescrTag, datalen-n) 255 | return 256 | } 257 | 258 | func (self ElemStreamDesc) Len() (n int) { 259 | return 8+4+self.lenESDescHdr()+self.lenDecConfigDescHdr()+len(self.DecConfig)+self.lenDescHdr()+1 260 | } 261 | 262 | // Version(4) 263 | // ESDesc( 264 | // MP4ESDescrTag 265 | // ESID(2) 266 | // ESFlags(1) 267 | // DecConfigDesc( 268 | // MP4DecConfigDescrTag 269 | // objectId streamType bufSize avgBitrate 270 | // DecSpecificDesc( 271 | // MP4DecSpecificDescrTag 272 | // decConfig 273 | // ) 274 | // ) 275 | // ?Desc(lenDescHdr+1) 276 | // ) 277 | 278 | func (self ElemStreamDesc) Marshal(b []byte) (n int) { 279 | pio.PutU32BE(b[4:], uint32(ESDS)) 280 | n += 8 281 | pio.PutU32BE(b[n:], 0) // Version 282 | n += 4 283 | datalen := self.Len() 284 | n += self.fillESDescHdr(b[n:], datalen-n-self.lenESDescHdr()) 285 | n += self.fillDecConfigDescHdr(b[n:], datalen-n-self.lenDescHdr()-1) 286 | copy(b[n:], self.DecConfig) 287 | n += len(self.DecConfig) 288 | n += self.fillDescHdr(b[n:], 0x06, datalen-n-self.lenDescHdr()) 289 | b[n] = 0x02 290 | n++ 291 | pio.PutU32BE(b[0:], uint32(n)) 292 | return 293 | } 294 | 295 | func (self *ElemStreamDesc) Unmarshal(b []byte, offset int) (n int, err error) { 296 | if len(b) < n+12 { 297 | err = parseErr("hdr", offset+n, err) 298 | return 299 | } 300 | (&self.AtomPos).setPos(offset, len(b)) 301 | n += 8 302 | n += 4 303 | return self.parseDesc(b[n:], offset+n) 304 | } 305 | 306 | func (self *ElemStreamDesc) parseDesc(b []byte, offset int) (n int, err error) { 307 | var hdrlen int 308 | var datalen int 309 | var tag uint8 310 | if hdrlen, tag, datalen, err = self.parseDescHdr(b, offset); err != nil { 311 | return 312 | } 313 | n += hdrlen 314 | 315 | if len(b) < n+datalen { 316 | err = parseErr("datalen", offset+n, err) 317 | return 318 | } 319 | 320 | switch tag { 321 | case MP4ESDescrTag: 322 | if len(b) < n+3 { 323 | err = parseErr("MP4ESDescrTag", offset+n, err) 324 | return 325 | } 326 | if _, err = self.parseDesc(b[n+3:], offset+n+3); err != nil { 327 | return 328 | } 329 | 330 | case MP4DecConfigDescrTag: 331 | const size = 2+3+4+4 332 | if len(b) < n+size { 333 | err = parseErr("MP4DecSpecificDescrTag", offset+n, err) 334 | return 335 | } 336 | if _, err = self.parseDesc(b[n+size:], offset+n+size); err != nil { 337 | return 338 | } 339 | 340 | case MP4DecSpecificDescrTag: 341 | self.DecConfig = b[n:] 342 | } 343 | 344 | n += datalen 345 | return 346 | } 347 | 348 | func (self *ElemStreamDesc) parseLength(b []byte, offset int) (n int, length int, err error) { 349 | for n < 4 { 350 | if len(b) < n+1 { 351 | err = parseErr("len", offset+n, err) 352 | return 353 | } 354 | c := b[n] 355 | n++ 356 | length = (length<<7)|(int(c)&0x7f) 357 | if c&0x80 == 0 { 358 | break 359 | } 360 | } 361 | return 362 | } 363 | 364 | func (self *ElemStreamDesc) parseDescHdr(b []byte, offset int) (n int, tag uint8, datalen int, err error) { 365 | if len(b) < n+1 { 366 | err = parseErr("tag", offset+n, err) 367 | return 368 | } 369 | tag = b[n] 370 | n++ 371 | var lenlen int 372 | if lenlen, datalen, err = self.parseLength(b[n:], offset+n); err != nil { 373 | return 374 | } 375 | n += lenlen 376 | return 377 | } 378 | 379 | func ReadFileAtoms(r io.ReadSeeker) (atoms []Atom, err error) { 380 | for { 381 | offset, _ := r.Seek(0, 1) 382 | taghdr := make([]byte, 8) 383 | if _, err = io.ReadFull(r, taghdr); err != nil { 384 | if err == io.EOF { 385 | err = nil 386 | } 387 | return 388 | } 389 | size := pio.U32BE(taghdr[0:]) 390 | tag := Tag(pio.U32BE(taghdr[4:])) 391 | 392 | var atom Atom 393 | switch tag { 394 | case MOOV: 395 | atom = &Movie{} 396 | case MOOF: 397 | atom = &MovieFrag{} 398 | } 399 | 400 | if atom != nil { 401 | b := make([]byte, int(size)) 402 | if _, err = io.ReadFull(r, b[8:]); err != nil { 403 | return 404 | } 405 | copy(b, taghdr) 406 | if _, err = atom.Unmarshal(b, int(offset)); err != nil { 407 | return 408 | } 409 | atoms = append(atoms, atom) 410 | } else { 411 | dummy := &Dummy{Tag_: tag} 412 | dummy.setPos(int(offset), int(size)) 413 | if _, err = r.Seek(int64(size)-8, 1); err != nil { 414 | return 415 | } 416 | atoms = append(atoms, dummy) 417 | } 418 | } 419 | return 420 | } 421 | 422 | func printatom(out io.Writer, root Atom, depth int) { 423 | offset, size := root.Pos() 424 | 425 | type stringintf interface { 426 | String() string 427 | } 428 | 429 | fmt.Fprintf(out, 430 | "%s%s offset=%d size=%d", 431 | strings.Repeat(" ", depth*2), root.Tag(), offset, size, 432 | ) 433 | if str, ok := root.(stringintf); ok { 434 | fmt.Fprint(out, " ", str.String()) 435 | } 436 | fmt.Fprintln(out) 437 | 438 | children := root.Children() 439 | for _, child := range children { 440 | printatom(out, child, depth+1) 441 | } 442 | } 443 | 444 | func FprintAtom(out io.Writer, root Atom) { 445 | printatom(out, root, 0) 446 | } 447 | 448 | func PrintAtom(root Atom) { 449 | FprintAtom(os.Stdout, root) 450 | } 451 | 452 | func (self MovieHeader) String() string { 453 | return fmt.Sprintf("dur=%d", self.Duration) 454 | } 455 | 456 | func (self TimeToSample) String() string { 457 | return fmt.Sprintf("entries=%d", len(self.Entries)) 458 | } 459 | 460 | func (self SampleToChunk) String() string { 461 | return fmt.Sprintf("entries=%d", len(self.Entries)) 462 | } 463 | 464 | func (self SampleSize) String() string { 465 | return fmt.Sprintf("entries=%d", len(self.Entries)) 466 | } 467 | 468 | func (self SyncSample) String() string { 469 | return fmt.Sprintf("entries=%d", len(self.Entries)) 470 | } 471 | 472 | func (self CompositionOffset) String() string { 473 | return fmt.Sprintf("entries=%d", len(self.Entries)) 474 | } 475 | 476 | func (self ChunkOffset) String() string { 477 | return fmt.Sprintf("entries=%d", len(self.Entries)) 478 | } 479 | 480 | func (self TrackFragRun) String() string { 481 | return fmt.Sprintf("dataoffset=%d", self.DataOffset) 482 | } 483 | 484 | func (self TrackFragHeader) String() string { 485 | return fmt.Sprintf("basedataoffset=%d", self.BaseDataOffset) 486 | } 487 | 488 | func (self ElemStreamDesc) String() string { 489 | return fmt.Sprintf("configlen=%d", len(self.DecConfig)) 490 | } 491 | 492 | func (self *Track) GetAVC1Conf() (conf *AVC1Conf) { 493 | atom := FindChildren(self, AVCC) 494 | conf, _ = atom.(*AVC1Conf) 495 | return 496 | } 497 | 498 | func (self *Track) GetElemStreamDesc() (esds *ElemStreamDesc) { 499 | atom := FindChildren(self, ESDS) 500 | esds, _ = atom.(*ElemStreamDesc) 501 | return 502 | } 503 | 504 | -------------------------------------------------------------------------------- /format/mp4/muxer.go: -------------------------------------------------------------------------------- 1 | package mp4 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | "github.com/nareix/joy4/av" 7 | "github.com/nareix/joy4/codec/aacparser" 8 | "github.com/nareix/joy4/codec/h264parser" 9 | "github.com/nareix/joy4/format/mp4/mp4io" 10 | "github.com/nareix/joy4/utils/bits/pio" 11 | "io" 12 | "bufio" 13 | ) 14 | 15 | type Muxer struct { 16 | w io.WriteSeeker 17 | bufw *bufio.Writer 18 | wpos int64 19 | streams []*Stream 20 | } 21 | 22 | func NewMuxer(w io.WriteSeeker) *Muxer { 23 | return &Muxer{ 24 | w: w, 25 | bufw: bufio.NewWriterSize(w, pio.RecommendBufioSize), 26 | } 27 | } 28 | 29 | func (self *Muxer) newStream(codec av.CodecData) (err error) { 30 | switch codec.Type() { 31 | case av.H264, av.AAC: 32 | 33 | default: 34 | err = fmt.Errorf("mp4: codec type=%v is not supported", codec.Type()) 35 | return 36 | } 37 | stream := &Stream{CodecData: codec} 38 | 39 | stream.sample = &mp4io.SampleTable{ 40 | SampleDesc: &mp4io.SampleDesc{}, 41 | TimeToSample: &mp4io.TimeToSample{}, 42 | SampleToChunk: &mp4io.SampleToChunk{ 43 | Entries: []mp4io.SampleToChunkEntry{ 44 | { 45 | FirstChunk: 1, 46 | SampleDescId: 1, 47 | SamplesPerChunk: 1, 48 | }, 49 | }, 50 | }, 51 | SampleSize: &mp4io.SampleSize{}, 52 | ChunkOffset: &mp4io.ChunkOffset{}, 53 | } 54 | 55 | stream.trackAtom = &mp4io.Track{ 56 | Header: &mp4io.TrackHeader{ 57 | TrackId: int32(len(self.streams)+1), 58 | Flags: 0x0003, // Track enabled | Track in movie 59 | Duration: 0, // fill later 60 | Matrix: [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, 61 | }, 62 | Media: &mp4io.Media{ 63 | Header: &mp4io.MediaHeader{ 64 | TimeScale: 0, // fill later 65 | Duration: 0, // fill later 66 | Language: 21956, 67 | }, 68 | Info: &mp4io.MediaInfo{ 69 | Sample: stream.sample, 70 | Data: &mp4io.DataInfo{ 71 | Refer: &mp4io.DataRefer{ 72 | Url: &mp4io.DataReferUrl{ 73 | Flags: 0x000001, // Self reference 74 | }, 75 | }, 76 | }, 77 | }, 78 | }, 79 | } 80 | 81 | switch codec.Type() { 82 | case av.H264: 83 | stream.sample.SyncSample = &mp4io.SyncSample{} 84 | } 85 | 86 | stream.timeScale = 90000 87 | stream.muxer = self 88 | self.streams = append(self.streams, stream) 89 | 90 | return 91 | } 92 | 93 | func (self *Stream) fillTrackAtom() (err error) { 94 | self.trackAtom.Media.Header.TimeScale = int32(self.timeScale) 95 | self.trackAtom.Media.Header.Duration = int32(self.duration) 96 | 97 | if self.Type() == av.H264 { 98 | codec := self.CodecData.(h264parser.CodecData) 99 | width, height := codec.Width(), codec.Height() 100 | self.sample.SampleDesc.AVC1Desc = &mp4io.AVC1Desc{ 101 | DataRefIdx: 1, 102 | HorizontalResolution: 72, 103 | VorizontalResolution: 72, 104 | Width: int16(width), 105 | Height: int16(height), 106 | FrameCount: 1, 107 | Depth: 24, 108 | ColorTableId: -1, 109 | Conf: &mp4io.AVC1Conf{Data: codec.AVCDecoderConfRecordBytes()}, 110 | } 111 | self.trackAtom.Media.Handler = &mp4io.HandlerRefer{ 112 | SubType: [4]byte{'v','i','d','e'}, 113 | Name: []byte("Video Media Handler"), 114 | } 115 | self.trackAtom.Media.Info.Video = &mp4io.VideoMediaInfo{ 116 | Flags: 0x000001, 117 | } 118 | self.trackAtom.Header.TrackWidth = float64(width) 119 | self.trackAtom.Header.TrackHeight = float64(height) 120 | 121 | } else if self.Type() == av.AAC { 122 | codec := self.CodecData.(aacparser.CodecData) 123 | self.sample.SampleDesc.MP4ADesc = &mp4io.MP4ADesc{ 124 | DataRefIdx: 1, 125 | NumberOfChannels: int16(codec.ChannelLayout().Count()), 126 | SampleSize: int16(codec.SampleFormat().BytesPerSample()), 127 | SampleRate: float64(codec.SampleRate()), 128 | Conf: &mp4io.ElemStreamDesc{ 129 | DecConfig: codec.MPEG4AudioConfigBytes(), 130 | }, 131 | } 132 | self.trackAtom.Header.Volume = 1 133 | self.trackAtom.Header.AlternateGroup = 1 134 | self.trackAtom.Media.Handler = &mp4io.HandlerRefer{ 135 | SubType: [4]byte{'s','o','u','n'}, 136 | Name: []byte("Sound Handler"), 137 | } 138 | self.trackAtom.Media.Info.Sound = &mp4io.SoundMediaInfo{} 139 | 140 | } else { 141 | err = fmt.Errorf("mp4: codec type=%d invalid", self.Type()) 142 | } 143 | 144 | return 145 | } 146 | 147 | func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { 148 | self.streams = []*Stream{} 149 | for _, stream := range streams { 150 | if err = self.newStream(stream); err != nil { 151 | return 152 | } 153 | } 154 | 155 | taghdr := make([]byte, 8) 156 | pio.PutU32BE(taghdr[4:], uint32(mp4io.MDAT)) 157 | if _, err = self.w.Write(taghdr); err != nil { 158 | return 159 | } 160 | self.wpos += 8 161 | 162 | for _, stream := range self.streams { 163 | if stream.Type().IsVideo() { 164 | stream.sample.CompositionOffset = &mp4io.CompositionOffset{} 165 | } 166 | } 167 | return 168 | } 169 | 170 | func (self *Muxer) WritePacket(pkt av.Packet) (err error) { 171 | stream := self.streams[pkt.Idx] 172 | if stream.lastpkt != nil { 173 | if err = stream.writePacket(*stream.lastpkt, pkt.Time-stream.lastpkt.Time); err != nil { 174 | return 175 | } 176 | } 177 | stream.lastpkt = &pkt 178 | return 179 | } 180 | 181 | func (self *Stream) writePacket(pkt av.Packet, rawdur time.Duration) (err error) { 182 | if rawdur < 0 { 183 | err = fmt.Errorf("mp4: stream#%d time=%v < lasttime=%v", pkt.Idx, pkt.Time, self.lastpkt.Time) 184 | return 185 | } 186 | 187 | if _, err = self.muxer.bufw.Write(pkt.Data); err != nil { 188 | return 189 | } 190 | 191 | if pkt.IsKeyFrame && self.sample.SyncSample != nil { 192 | self.sample.SyncSample.Entries = append(self.sample.SyncSample.Entries, uint32(self.sampleIndex+1)) 193 | } 194 | 195 | duration := uint32(self.timeToTs(rawdur)) 196 | if self.sttsEntry == nil || duration != self.sttsEntry.Duration { 197 | self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, mp4io.TimeToSampleEntry{Duration: duration}) 198 | self.sttsEntry = &self.sample.TimeToSample.Entries[len(self.sample.TimeToSample.Entries)-1] 199 | } 200 | self.sttsEntry.Count++ 201 | 202 | if self.sample.CompositionOffset != nil { 203 | offset := uint32(self.timeToTs(pkt.CompositionTime)) 204 | if self.cttsEntry == nil || offset != self.cttsEntry.Offset { 205 | table := self.sample.CompositionOffset 206 | table.Entries = append(table.Entries, mp4io.CompositionOffsetEntry{Offset: offset}) 207 | self.cttsEntry = &table.Entries[len(table.Entries)-1] 208 | } 209 | self.cttsEntry.Count++ 210 | } 211 | 212 | self.duration += int64(duration) 213 | self.sampleIndex++ 214 | self.sample.ChunkOffset.Entries = append(self.sample.ChunkOffset.Entries, uint32(self.muxer.wpos)) 215 | self.sample.SampleSize.Entries = append(self.sample.SampleSize.Entries, uint32(len(pkt.Data))) 216 | 217 | self.muxer.wpos += int64(len(pkt.Data)) 218 | return 219 | } 220 | 221 | func (self *Muxer) WriteTrailer() (err error) { 222 | for _, stream := range self.streams { 223 | if stream.lastpkt != nil { 224 | if err = stream.writePacket(*stream.lastpkt, 0); err != nil { 225 | return 226 | } 227 | stream.lastpkt = nil 228 | } 229 | } 230 | 231 | moov := &mp4io.Movie{} 232 | moov.Header = &mp4io.MovieHeader{ 233 | PreferredRate: 1, 234 | PreferredVolume: 1, 235 | Matrix: [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, 236 | NextTrackId: 2, 237 | } 238 | 239 | maxDur := time.Duration(0) 240 | timeScale := int64(10000) 241 | for _, stream := range self.streams { 242 | if err = stream.fillTrackAtom(); err != nil { 243 | return 244 | } 245 | dur := stream.tsToTime(stream.duration) 246 | stream.trackAtom.Header.Duration = int32(timeToTs(dur, timeScale)) 247 | if dur > maxDur { 248 | maxDur = dur 249 | } 250 | moov.Tracks = append(moov.Tracks, stream.trackAtom) 251 | } 252 | moov.Header.TimeScale = int32(timeScale) 253 | moov.Header.Duration = int32(timeToTs(maxDur, timeScale)) 254 | 255 | if err = self.bufw.Flush(); err != nil { 256 | return 257 | } 258 | 259 | var mdatsize int64 260 | if mdatsize, err = self.w.Seek(0, 1); err != nil { 261 | return 262 | } 263 | if _, err = self.w.Seek(0, 0); err != nil { 264 | return 265 | } 266 | taghdr := make([]byte, 4) 267 | pio.PutU32BE(taghdr, uint32(mdatsize)) 268 | if _, err = self.w.Write(taghdr); err != nil { 269 | return 270 | } 271 | 272 | if _, err = self.w.Seek(0, 2); err != nil { 273 | return 274 | } 275 | b := make([]byte, moov.Len()) 276 | moov.Marshal(b) 277 | if _, err = self.w.Write(b); err != nil { 278 | return 279 | } 280 | 281 | return 282 | } 283 | -------------------------------------------------------------------------------- /format/mp4/stream.go: -------------------------------------------------------------------------------- 1 | package mp4 2 | 3 | import ( 4 | "github.com/nareix/joy4/av" 5 | "github.com/nareix/joy4/format/mp4/mp4io" 6 | "time" 7 | ) 8 | 9 | type Stream struct { 10 | av.CodecData 11 | 12 | trackAtom *mp4io.Track 13 | idx int 14 | 15 | lastpkt *av.Packet 16 | 17 | timeScale int64 18 | duration int64 19 | 20 | muxer *Muxer 21 | demuxer *Demuxer 22 | 23 | sample *mp4io.SampleTable 24 | sampleIndex int 25 | 26 | sampleOffsetInChunk int64 27 | syncSampleIndex int 28 | 29 | dts int64 30 | sttsEntryIndex int 31 | sampleIndexInSttsEntry int 32 | 33 | cttsEntryIndex int 34 | sampleIndexInCttsEntry int 35 | 36 | chunkGroupIndex int 37 | chunkIndex int 38 | sampleIndexInChunk int 39 | 40 | sttsEntry *mp4io.TimeToSampleEntry 41 | cttsEntry *mp4io.CompositionOffsetEntry 42 | } 43 | 44 | func timeToTs(tm time.Duration, timeScale int64) int64 { 45 | return int64(tm*time.Duration(timeScale) / time.Second) 46 | } 47 | 48 | func tsToTime(ts int64, timeScale int64) time.Duration { 49 | return time.Duration(ts)*time.Second / time.Duration(timeScale) 50 | } 51 | 52 | func (self *Stream) timeToTs(tm time.Duration) int64 { 53 | return int64(tm*time.Duration(self.timeScale) / time.Second) 54 | } 55 | 56 | func (self *Stream) tsToTime(ts int64) time.Duration { 57 | return time.Duration(ts)*time.Second / time.Duration(self.timeScale) 58 | } 59 | -------------------------------------------------------------------------------- /format/rtsp/conn.go: -------------------------------------------------------------------------------- 1 | package rtsp 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | type connWithTimeout struct { 9 | Timeout time.Duration 10 | net.Conn 11 | } 12 | 13 | func (self connWithTimeout) Read(p []byte) (n int, err error) { 14 | if self.Timeout > 0 { 15 | self.Conn.SetReadDeadline(time.Now().Add(self.Timeout)) 16 | } 17 | return self.Conn.Read(p) 18 | } 19 | 20 | func (self connWithTimeout) Write(p []byte) (n int, err error) { 21 | if self.Timeout > 0 { 22 | self.Conn.SetWriteDeadline(time.Now().Add(self.Timeout)) 23 | } 24 | return self.Conn.Write(p) 25 | } 26 | -------------------------------------------------------------------------------- /format/rtsp/sdp/parser.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/hex" 6 | "fmt" 7 | "github.com/nareix/joy4/av" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | type Session struct { 13 | Uri string 14 | } 15 | 16 | type Media struct { 17 | AVType string 18 | Type av.CodecType 19 | TimeScale int 20 | Control string 21 | Rtpmap int 22 | Config []byte 23 | SpropParameterSets [][]byte 24 | PayloadType int 25 | SizeLength int 26 | IndexLength int 27 | } 28 | 29 | func Parse(content string) (sess Session, medias []Media) { 30 | var media *Media 31 | 32 | for _, line := range strings.Split(content, "\n") { 33 | line = strings.TrimSpace(line) 34 | typeval := strings.SplitN(line, "=", 2) 35 | if len(typeval) == 2 { 36 | fields := strings.SplitN(typeval[1], " ", 2) 37 | 38 | switch typeval[0] { 39 | case "m": 40 | if len(fields) > 0 { 41 | switch fields[0] { 42 | case "audio", "video": 43 | medias = append(medias, Media{AVType: fields[0]}) 44 | media = &medias[len(medias)-1] 45 | mfields := strings.Split(fields[1], " ") 46 | if len(mfields) >= 3 { 47 | media.PayloadType, _ = strconv.Atoi(mfields[2]) 48 | } 49 | } 50 | } 51 | 52 | case "u": 53 | sess.Uri = typeval[1] 54 | 55 | case "a": 56 | if media != nil { 57 | for _, field := range fields { 58 | keyval := strings.SplitN(field, ":", 2) 59 | if len(keyval) >= 2 { 60 | key := keyval[0] 61 | val := keyval[1] 62 | switch key { 63 | case "control": 64 | media.Control = val 65 | case "rtpmap": 66 | media.Rtpmap, _ = strconv.Atoi(val) 67 | } 68 | } 69 | keyval = strings.Split(field, "/") 70 | if len(keyval) >= 2 { 71 | key := keyval[0] 72 | switch strings.ToUpper(key) { 73 | case "MPEG4-GENERIC": 74 | media.Type = av.AAC 75 | case "H264": 76 | media.Type = av.H264 77 | } 78 | if i, err := strconv.Atoi(keyval[1]); err == nil { 79 | media.TimeScale = i 80 | } 81 | if false { 82 | fmt.Println("sdp:", keyval[1], media.TimeScale) 83 | } 84 | } 85 | keyval = strings.Split(field, ";") 86 | if len(keyval) > 1 { 87 | for _, field := range keyval { 88 | keyval := strings.SplitN(field, "=", 2) 89 | if len(keyval) == 2 { 90 | key := strings.TrimSpace(keyval[0]) 91 | val := keyval[1] 92 | switch key { 93 | case "config": 94 | media.Config, _ = hex.DecodeString(val) 95 | case "sizelength": 96 | media.SizeLength, _ = strconv.Atoi(val) 97 | case "indexlength": 98 | media.IndexLength, _ = strconv.Atoi(val) 99 | case "sprop-parameter-sets": 100 | fields := strings.Split(val, ",") 101 | for _, field := range fields { 102 | val, _ := base64.StdEncoding.DecodeString(field) 103 | media.SpropParameterSets = append(media.SpropParameterSets, val) 104 | } 105 | } 106 | } 107 | } 108 | } 109 | } 110 | } 111 | 112 | } 113 | } 114 | } 115 | return 116 | } 117 | -------------------------------------------------------------------------------- /format/rtsp/sdp/parser_test.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestParse(t *testing.T) { 8 | infos := Decode(` 9 | v=0 10 | o=- 1459325504777324 1 IN IP4 192.168.0.123 11 | s=RTSP/RTP stream from Network Video Server 12 | i=mpeg4cif 13 | t=0 0 14 | a=tool:LIVE555 Streaming Media v2009.09.28 15 | a=type:broadcast 16 | a=control:* 17 | a=range:npt=0- 18 | a=x-qt-text-nam:RTSP/RTP stream from Network Video Server 19 | a=x-qt-text-inf:mpeg4cif 20 | m=video 0 RTP/AVP 96 21 | c=IN IP4 0.0.0.0 22 | b=AS:300 23 | a=rtpmap:96 H264/90000 24 | a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AHpWoKA9k,aO48gA== 25 | a=x-dimensions: 720, 480 26 | a=x-framerate: 15 27 | a=control:track1 28 | m=audio 0 RTP/AVP 96 29 | c=IN IP4 0.0.0.0 30 | b=AS:256 31 | a=rtpmap:96 MPEG4-GENERIC/16000/2 32 | a=fmtp:96 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1408 33 | a=control:track2 34 | m=audio 0 RTP/AVP 0 35 | c=IN IP4 0.0.0.0 36 | b=AS:50 37 | a=recvonly 38 | a=control:rtsp://109.195.127.207:554/mpeg4cif/trackID=2 39 | a=rtpmap:0 PCMU/8000 40 | a=Media_header:MEDIAINFO=494D4B48010100000400010010710110401F000000FA000000000000000000000000000000000000; 41 | a=appversion:1.0 42 | `) 43 | t.Logf("%v", infos) 44 | } 45 | -------------------------------------------------------------------------------- /format/rtsp/stream.go: -------------------------------------------------------------------------------- 1 | package rtsp 2 | 3 | import ( 4 | "github.com/nareix/joy4/av" 5 | "github.com/nareix/joy4/format/rtsp/sdp" 6 | "time" 7 | ) 8 | 9 | type Stream struct { 10 | av.CodecData 11 | Sdp sdp.Media 12 | client *Client 13 | 14 | // h264 15 | fuStarted bool 16 | fuBuffer []byte 17 | sps []byte 18 | pps []byte 19 | spsChanged bool 20 | ppsChanged bool 21 | 22 | gotpkt bool 23 | pkt av.Packet 24 | timestamp uint32 25 | firsttimestamp uint32 26 | 27 | lasttime time.Duration 28 | } 29 | 30 | -------------------------------------------------------------------------------- /format/ts/demuxer.go: -------------------------------------------------------------------------------- 1 | package ts 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "time" 7 | "github.com/nareix/joy4/utils/bits/pio" 8 | "github.com/nareix/joy4/av" 9 | "github.com/nareix/joy4/format/ts/tsio" 10 | "github.com/nareix/joy4/codec/aacparser" 11 | "github.com/nareix/joy4/codec/h264parser" 12 | "io" 13 | ) 14 | 15 | type Demuxer struct { 16 | r *bufio.Reader 17 | 18 | pkts []av.Packet 19 | 20 | pat *tsio.PAT 21 | pmt *tsio.PMT 22 | streams []*Stream 23 | tshdr []byte 24 | 25 | stage int 26 | } 27 | 28 | func NewDemuxer(r io.Reader) *Demuxer { 29 | return &Demuxer{ 30 | tshdr: make([]byte, 188), 31 | r: bufio.NewReaderSize(r, pio.RecommendBufioSize), 32 | } 33 | } 34 | 35 | func (self *Demuxer) Streams() (streams []av.CodecData, err error) { 36 | if err = self.probe(); err != nil { 37 | return 38 | } 39 | for _, stream := range self.streams { 40 | streams = append(streams, stream.CodecData) 41 | } 42 | return 43 | } 44 | 45 | func (self *Demuxer) probe() (err error) { 46 | if self.stage == 0 { 47 | for { 48 | if self.pmt != nil { 49 | n := 0 50 | for _, stream := range self.streams { 51 | if stream.CodecData != nil { 52 | n++ 53 | } 54 | } 55 | if n == len(self.streams) { 56 | break 57 | } 58 | } 59 | if err = self.poll(); err != nil { 60 | return 61 | } 62 | } 63 | self.stage++ 64 | } 65 | return 66 | } 67 | 68 | func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { 69 | if err = self.probe(); err != nil { 70 | return 71 | } 72 | 73 | for len(self.pkts) == 0 { 74 | if err = self.poll(); err != nil { 75 | return 76 | } 77 | } 78 | 79 | pkt = self.pkts[0] 80 | self.pkts = self.pkts[1:] 81 | return 82 | } 83 | 84 | func (self *Demuxer) poll() (err error) { 85 | if err = self.readTSPacket(); err == io.EOF { 86 | var n int 87 | if n, err = self.payloadEnd(); err != nil { 88 | return 89 | } 90 | if n == 0 { 91 | err = io.EOF 92 | } 93 | } 94 | return 95 | } 96 | 97 | func (self *Demuxer) initPMT(payload []byte) (err error) { 98 | var psihdrlen int 99 | var datalen int 100 | if _, _, psihdrlen, datalen, err = tsio.ParsePSI(payload); err != nil { 101 | return 102 | } 103 | self.pmt = &tsio.PMT{} 104 | if _, err = self.pmt.Unmarshal(payload[psihdrlen:psihdrlen+datalen]); err != nil { 105 | return 106 | } 107 | 108 | self.streams = []*Stream{} 109 | for i, info := range self.pmt.ElementaryStreamInfos { 110 | stream := &Stream{} 111 | stream.idx = i 112 | stream.demuxer = self 113 | stream.pid = info.ElementaryPID 114 | stream.streamType = info.StreamType 115 | switch info.StreamType { 116 | case tsio.ElementaryStreamTypeH264: 117 | self.streams = append(self.streams, stream) 118 | case tsio.ElementaryStreamTypeAdtsAAC: 119 | self.streams = append(self.streams, stream) 120 | } 121 | } 122 | return 123 | } 124 | 125 | func (self *Demuxer) payloadEnd() (n int, err error) { 126 | for _, stream := range self.streams { 127 | var i int 128 | if i, err = stream.payloadEnd(); err != nil { 129 | return 130 | } 131 | n += i 132 | } 133 | return 134 | } 135 | 136 | func (self *Demuxer) readTSPacket() (err error) { 137 | var hdrlen int 138 | var pid uint16 139 | var start bool 140 | var iskeyframe bool 141 | 142 | if _, err = io.ReadFull(self.r, self.tshdr); err != nil { 143 | return 144 | } 145 | 146 | if pid, start, iskeyframe, hdrlen, err = tsio.ParseTSHeader(self.tshdr); err != nil { 147 | return 148 | } 149 | payload := self.tshdr[hdrlen:] 150 | 151 | if self.pat == nil { 152 | if pid == 0 { 153 | var psihdrlen int 154 | var datalen int 155 | if _, _, psihdrlen, datalen, err = tsio.ParsePSI(payload); err != nil { 156 | return 157 | } 158 | self.pat = &tsio.PAT{} 159 | if _, err = self.pat.Unmarshal(payload[psihdrlen:psihdrlen+datalen]); err != nil { 160 | return 161 | } 162 | } 163 | } else if self.pmt == nil { 164 | for _, entry := range self.pat.Entries { 165 | if entry.ProgramMapPID == pid { 166 | if err = self.initPMT(payload); err != nil { 167 | return 168 | } 169 | break 170 | } 171 | } 172 | } else { 173 | for _, stream := range self.streams { 174 | if pid == stream.pid { 175 | if err = stream.handleTSPacket(start, iskeyframe, payload); err != nil { 176 | return 177 | } 178 | break 179 | } 180 | } 181 | } 182 | 183 | return 184 | } 185 | 186 | func (self *Stream) addPacket(payload []byte, timedelta time.Duration) { 187 | dts := self.dts 188 | pts := self.pts 189 | if dts == 0 { 190 | dts = pts 191 | } 192 | 193 | demuxer := self.demuxer 194 | pkt := av.Packet{ 195 | Idx: int8(self.idx), 196 | IsKeyFrame: self.iskeyframe, 197 | Time: dts+timedelta, 198 | Data: payload, 199 | } 200 | if pts != dts { 201 | pkt.CompositionTime = pts-dts 202 | } 203 | demuxer.pkts = append(demuxer.pkts, pkt) 204 | } 205 | 206 | func (self *Stream) payloadEnd() (n int, err error) { 207 | payload := self.data 208 | if payload == nil { 209 | return 210 | } 211 | if self.datalen != 0 && len(payload) != self.datalen { 212 | err = fmt.Errorf("ts: packet size mismatch size=%d correct=%d", len(payload), self.datalen) 213 | return 214 | } 215 | self.data = nil 216 | 217 | switch self.streamType { 218 | case tsio.ElementaryStreamTypeAdtsAAC: 219 | var config aacparser.MPEG4AudioConfig 220 | 221 | delta := time.Duration(0) 222 | for len(payload) > 0 { 223 | var hdrlen, framelen, samples int 224 | if config, hdrlen, framelen, samples, err = aacparser.ParseADTSHeader(payload); err != nil { 225 | return 226 | } 227 | if self.CodecData == nil { 228 | if self.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfig(config); err != nil { 229 | return 230 | } 231 | } 232 | self.addPacket(payload[hdrlen:framelen], delta) 233 | n++ 234 | delta += time.Duration(samples) * time.Second / time.Duration(config.SampleRate) 235 | payload = payload[framelen:] 236 | } 237 | 238 | case tsio.ElementaryStreamTypeH264: 239 | nalus, _ := h264parser.SplitNALUs(payload) 240 | var sps, pps []byte 241 | for _, nalu := range nalus { 242 | if len(nalu) > 0 { 243 | naltype := nalu[0] & 0x1f 244 | switch { 245 | case naltype == 7: 246 | sps = nalu 247 | case naltype == 8: 248 | pps = nalu 249 | case h264parser.IsDataNALU(nalu): 250 | // raw nalu to avcc 251 | b := make([]byte, 4+len(nalu)) 252 | pio.PutU32BE(b[0:4], uint32(len(nalu))) 253 | copy(b[4:], nalu) 254 | self.addPacket(b, time.Duration(0)) 255 | n++ 256 | } 257 | } 258 | } 259 | 260 | if self.CodecData == nil && len(sps) > 0 && len(pps) > 0 { 261 | if self.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(sps, pps); err != nil { 262 | return 263 | } 264 | } 265 | } 266 | 267 | return 268 | } 269 | 270 | func (self *Stream) handleTSPacket(start bool, iskeyframe bool, payload []byte) (err error) { 271 | if start { 272 | if _, err = self.payloadEnd(); err != nil { 273 | return 274 | } 275 | var hdrlen int 276 | if hdrlen, _, self.datalen, self.pts, self.dts, err = tsio.ParsePESHeader(payload); err != nil { 277 | return 278 | } 279 | self.iskeyframe = iskeyframe 280 | if self.datalen == 0 { 281 | self.data = make([]byte, 0, 4096) 282 | } else { 283 | self.data = make([]byte, 0, self.datalen) 284 | } 285 | self.data = append(self.data, payload[hdrlen:]...) 286 | } else { 287 | self.data = append(self.data, payload...) 288 | } 289 | return 290 | } 291 | -------------------------------------------------------------------------------- /format/ts/handler.go: -------------------------------------------------------------------------------- 1 | package ts 2 | 3 | import ( 4 | "io" 5 | "github.com/nareix/joy4/av" 6 | "github.com/nareix/joy4/av/avutil" 7 | ) 8 | 9 | func Handler(h *avutil.RegisterHandler) { 10 | h.Ext = ".ts" 11 | 12 | h.Probe = func(b []byte) bool { 13 | return b[0] == 0x47 && b[188] == 0x47 14 | } 15 | 16 | h.ReaderDemuxer = func(r io.Reader) av.Demuxer { 17 | return NewDemuxer(r) 18 | } 19 | 20 | h.WriterMuxer = func(w io.Writer) av.Muxer { 21 | return NewMuxer(w) 22 | } 23 | 24 | h.CodecTypes = CodecTypes 25 | } 26 | 27 | -------------------------------------------------------------------------------- /format/ts/muxer.go: -------------------------------------------------------------------------------- 1 | package ts 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nareix/joy4/av" 6 | "github.com/nareix/joy4/codec/aacparser" 7 | "github.com/nareix/joy4/codec/h264parser" 8 | "github.com/nareix/joy4/format/ts/tsio" 9 | "io" 10 | "time" 11 | ) 12 | 13 | var CodecTypes = []av.CodecType{av.H264, av.AAC} 14 | 15 | type Muxer struct { 16 | w io.Writer 17 | streams []*Stream 18 | PaddingToMakeCounterCont bool 19 | 20 | psidata []byte 21 | peshdr []byte 22 | tshdr []byte 23 | adtshdr []byte 24 | datav [][]byte 25 | nalus [][]byte 26 | 27 | tswpat, tswpmt *tsio.TSWriter 28 | } 29 | 30 | func NewMuxer(w io.Writer) *Muxer { 31 | return &Muxer{ 32 | w: w, 33 | psidata: make([]byte, 188), 34 | peshdr: make([]byte, tsio.MaxPESHeaderLength), 35 | tshdr: make([]byte, tsio.MaxTSHeaderLength), 36 | adtshdr: make([]byte, aacparser.ADTSHeaderLength), 37 | nalus: make([][]byte, 16), 38 | datav: make([][]byte, 16), 39 | tswpmt: tsio.NewTSWriter(tsio.PMT_PID), 40 | tswpat: tsio.NewTSWriter(tsio.PAT_PID), 41 | } 42 | } 43 | 44 | func (self *Muxer) newStream(codec av.CodecData) (err error) { 45 | ok := false 46 | for _, c := range CodecTypes { 47 | if codec.Type() == c { 48 | ok = true 49 | break 50 | } 51 | } 52 | if !ok { 53 | err = fmt.Errorf("ts: codec type=%s is not supported", codec.Type()) 54 | return 55 | } 56 | 57 | pid := uint16(len(self.streams) + 0x100) 58 | stream := &Stream{ 59 | muxer: self, 60 | CodecData: codec, 61 | pid: pid, 62 | tsw: tsio.NewTSWriter(pid), 63 | } 64 | self.streams = append(self.streams, stream) 65 | return 66 | } 67 | 68 | func (self *Muxer) writePaddingTSPackets(tsw *tsio.TSWriter) (err error) { 69 | for tsw.ContinuityCounter&0xf != 0x0 { 70 | if err = tsw.WritePackets(self.w, self.datav[:0], 0, false, true); err != nil { 71 | return 72 | } 73 | } 74 | return 75 | } 76 | 77 | func (self *Muxer) WriteTrailer() (err error) { 78 | if self.PaddingToMakeCounterCont { 79 | for _, stream := range self.streams { 80 | if err = self.writePaddingTSPackets(stream.tsw); err != nil { 81 | return 82 | } 83 | } 84 | } 85 | return 86 | } 87 | 88 | func (self *Muxer) SetWriter(w io.Writer) { 89 | self.w = w 90 | return 91 | } 92 | 93 | func (self *Muxer) WritePATPMT() (err error) { 94 | pat := tsio.PAT{ 95 | Entries: []tsio.PATEntry{ 96 | {ProgramNumber: 1, ProgramMapPID: tsio.PMT_PID}, 97 | }, 98 | } 99 | patlen := pat.Marshal(self.psidata[tsio.PSIHeaderLength:]) 100 | n := tsio.FillPSI(self.psidata, tsio.TableIdPAT, tsio.TableExtPAT, patlen) 101 | self.datav[0] = self.psidata[:n] 102 | if err = self.tswpat.WritePackets(self.w, self.datav[:1], 0, false, true); err != nil { 103 | return 104 | } 105 | 106 | var elemStreams []tsio.ElementaryStreamInfo 107 | for _, stream := range self.streams { 108 | switch stream.Type() { 109 | case av.AAC: 110 | elemStreams = append(elemStreams, tsio.ElementaryStreamInfo{ 111 | StreamType: tsio.ElementaryStreamTypeAdtsAAC, 112 | ElementaryPID: stream.pid, 113 | }) 114 | case av.H264: 115 | elemStreams = append(elemStreams, tsio.ElementaryStreamInfo{ 116 | StreamType: tsio.ElementaryStreamTypeH264, 117 | ElementaryPID: stream.pid, 118 | }) 119 | } 120 | } 121 | 122 | pmt := tsio.PMT{ 123 | PCRPID: 0x100, 124 | ElementaryStreamInfos: elemStreams, 125 | } 126 | pmtlen := pmt.Len() 127 | if pmtlen+tsio.PSIHeaderLength > len(self.psidata) { 128 | err = fmt.Errorf("ts: pmt too large") 129 | return 130 | } 131 | pmt.Marshal(self.psidata[tsio.PSIHeaderLength:]) 132 | n = tsio.FillPSI(self.psidata, tsio.TableIdPMT, tsio.TableExtPMT, pmtlen) 133 | self.datav[0] = self.psidata[:n] 134 | if err = self.tswpmt.WritePackets(self.w, self.datav[:1], 0, false, true); err != nil { 135 | return 136 | } 137 | 138 | return 139 | } 140 | 141 | func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { 142 | self.streams = []*Stream{} 143 | for _, stream := range streams { 144 | if err = self.newStream(stream); err != nil { 145 | return 146 | } 147 | } 148 | 149 | if err = self.WritePATPMT(); err != nil { 150 | return 151 | } 152 | return 153 | } 154 | 155 | func (self *Muxer) WritePacket(pkt av.Packet) (err error) { 156 | stream := self.streams[pkt.Idx] 157 | pkt.Time += time.Second 158 | 159 | switch stream.Type() { 160 | case av.AAC: 161 | codec := stream.CodecData.(aacparser.CodecData) 162 | 163 | n := tsio.FillPESHeader(self.peshdr, tsio.StreamIdAAC, len(self.adtshdr)+len(pkt.Data), pkt.Time, 0) 164 | self.datav[0] = self.peshdr[:n] 165 | aacparser.FillADTSHeader(self.adtshdr, codec.Config, 1024, len(pkt.Data)) 166 | self.datav[1] = self.adtshdr 167 | self.datav[2] = pkt.Data 168 | 169 | if err = stream.tsw.WritePackets(self.w, self.datav[:3], pkt.Time, true, false); err != nil { 170 | return 171 | } 172 | 173 | case av.H264: 174 | codec := stream.CodecData.(h264parser.CodecData) 175 | 176 | nalus := self.nalus[:0] 177 | if pkt.IsKeyFrame { 178 | nalus = append(nalus, codec.SPS()) 179 | nalus = append(nalus, codec.PPS()) 180 | } 181 | pktnalus, _ := h264parser.SplitNALUs(pkt.Data) 182 | for _, nalu := range pktnalus { 183 | nalus = append(nalus, nalu) 184 | } 185 | 186 | datav := self.datav[:1] 187 | for i, nalu := range nalus { 188 | if i == 0 { 189 | datav = append(datav, h264parser.AUDBytes) 190 | } else { 191 | datav = append(datav, h264parser.StartCodeBytes) 192 | } 193 | datav = append(datav, nalu) 194 | } 195 | 196 | n := tsio.FillPESHeader(self.peshdr, tsio.StreamIdH264, -1, pkt.Time+pkt.CompositionTime, pkt.Time) 197 | datav[0] = self.peshdr[:n] 198 | 199 | if err = stream.tsw.WritePackets(self.w, datav, pkt.Time, pkt.IsKeyFrame, false); err != nil { 200 | return 201 | } 202 | } 203 | 204 | return 205 | } 206 | -------------------------------------------------------------------------------- /format/ts/stream.go: -------------------------------------------------------------------------------- 1 | package ts 2 | 3 | import ( 4 | "time" 5 | "github.com/nareix/joy4/av" 6 | "github.com/nareix/joy4/format/ts/tsio" 7 | ) 8 | 9 | type Stream struct { 10 | av.CodecData 11 | 12 | demuxer *Demuxer 13 | muxer *Muxer 14 | 15 | pid uint16 16 | streamId uint8 17 | streamType uint8 18 | 19 | tsw *tsio.TSWriter 20 | idx int 21 | 22 | iskeyframe bool 23 | pts, dts time.Duration 24 | data []byte 25 | datalen int 26 | } 27 | 28 | -------------------------------------------------------------------------------- /format/ts/tsio/checksum.go: -------------------------------------------------------------------------------- 1 | package tsio 2 | 3 | var ieeeCrc32Tbl = []uint32{ 4 | 0x00000000, 0xB71DC104, 0x6E3B8209, 0xD926430D, 0xDC760413, 0x6B6BC517, 5 | 0xB24D861A, 0x0550471E, 0xB8ED0826, 0x0FF0C922, 0xD6D68A2F, 0x61CB4B2B, 6 | 0x649B0C35, 0xD386CD31, 0x0AA08E3C, 0xBDBD4F38, 0x70DB114C, 0xC7C6D048, 7 | 0x1EE09345, 0xA9FD5241, 0xACAD155F, 0x1BB0D45B, 0xC2969756, 0x758B5652, 8 | 0xC836196A, 0x7F2BD86E, 0xA60D9B63, 0x11105A67, 0x14401D79, 0xA35DDC7D, 9 | 0x7A7B9F70, 0xCD665E74, 0xE0B62398, 0x57ABE29C, 0x8E8DA191, 0x39906095, 10 | 0x3CC0278B, 0x8BDDE68F, 0x52FBA582, 0xE5E66486, 0x585B2BBE, 0xEF46EABA, 11 | 0x3660A9B7, 0x817D68B3, 0x842D2FAD, 0x3330EEA9, 0xEA16ADA4, 0x5D0B6CA0, 12 | 0x906D32D4, 0x2770F3D0, 0xFE56B0DD, 0x494B71D9, 0x4C1B36C7, 0xFB06F7C3, 13 | 0x2220B4CE, 0x953D75CA, 0x28803AF2, 0x9F9DFBF6, 0x46BBB8FB, 0xF1A679FF, 14 | 0xF4F63EE1, 0x43EBFFE5, 0x9ACDBCE8, 0x2DD07DEC, 0x77708634, 0xC06D4730, 15 | 0x194B043D, 0xAE56C539, 0xAB068227, 0x1C1B4323, 0xC53D002E, 0x7220C12A, 16 | 0xCF9D8E12, 0x78804F16, 0xA1A60C1B, 0x16BBCD1F, 0x13EB8A01, 0xA4F64B05, 17 | 0x7DD00808, 0xCACDC90C, 0x07AB9778, 0xB0B6567C, 0x69901571, 0xDE8DD475, 18 | 0xDBDD936B, 0x6CC0526F, 0xB5E61162, 0x02FBD066, 0xBF469F5E, 0x085B5E5A, 19 | 0xD17D1D57, 0x6660DC53, 0x63309B4D, 0xD42D5A49, 0x0D0B1944, 0xBA16D840, 20 | 0x97C6A5AC, 0x20DB64A8, 0xF9FD27A5, 0x4EE0E6A1, 0x4BB0A1BF, 0xFCAD60BB, 21 | 0x258B23B6, 0x9296E2B2, 0x2F2BAD8A, 0x98366C8E, 0x41102F83, 0xF60DEE87, 22 | 0xF35DA999, 0x4440689D, 0x9D662B90, 0x2A7BEA94, 0xE71DB4E0, 0x500075E4, 23 | 0x892636E9, 0x3E3BF7ED, 0x3B6BB0F3, 0x8C7671F7, 0x555032FA, 0xE24DF3FE, 24 | 0x5FF0BCC6, 0xE8ED7DC2, 0x31CB3ECF, 0x86D6FFCB, 0x8386B8D5, 0x349B79D1, 25 | 0xEDBD3ADC, 0x5AA0FBD8, 0xEEE00C69, 0x59FDCD6D, 0x80DB8E60, 0x37C64F64, 26 | 0x3296087A, 0x858BC97E, 0x5CAD8A73, 0xEBB04B77, 0x560D044F, 0xE110C54B, 27 | 0x38368646, 0x8F2B4742, 0x8A7B005C, 0x3D66C158, 0xE4408255, 0x535D4351, 28 | 0x9E3B1D25, 0x2926DC21, 0xF0009F2C, 0x471D5E28, 0x424D1936, 0xF550D832, 29 | 0x2C769B3F, 0x9B6B5A3B, 0x26D61503, 0x91CBD407, 0x48ED970A, 0xFFF0560E, 30 | 0xFAA01110, 0x4DBDD014, 0x949B9319, 0x2386521D, 0x0E562FF1, 0xB94BEEF5, 31 | 0x606DADF8, 0xD7706CFC, 0xD2202BE2, 0x653DEAE6, 0xBC1BA9EB, 0x0B0668EF, 32 | 0xB6BB27D7, 0x01A6E6D3, 0xD880A5DE, 0x6F9D64DA, 0x6ACD23C4, 0xDDD0E2C0, 33 | 0x04F6A1CD, 0xB3EB60C9, 0x7E8D3EBD, 0xC990FFB9, 0x10B6BCB4, 0xA7AB7DB0, 34 | 0xA2FB3AAE, 0x15E6FBAA, 0xCCC0B8A7, 0x7BDD79A3, 0xC660369B, 0x717DF79F, 35 | 0xA85BB492, 0x1F467596, 0x1A163288, 0xAD0BF38C, 0x742DB081, 0xC3307185, 36 | 0x99908A5D, 0x2E8D4B59, 0xF7AB0854, 0x40B6C950, 0x45E68E4E, 0xF2FB4F4A, 37 | 0x2BDD0C47, 0x9CC0CD43, 0x217D827B, 0x9660437F, 0x4F460072, 0xF85BC176, 38 | 0xFD0B8668, 0x4A16476C, 0x93300461, 0x242DC565, 0xE94B9B11, 0x5E565A15, 39 | 0x87701918, 0x306DD81C, 0x353D9F02, 0x82205E06, 0x5B061D0B, 0xEC1BDC0F, 40 | 0x51A69337, 0xE6BB5233, 0x3F9D113E, 0x8880D03A, 0x8DD09724, 0x3ACD5620, 41 | 0xE3EB152D, 0x54F6D429, 0x7926A9C5, 0xCE3B68C1, 0x171D2BCC, 0xA000EAC8, 42 | 0xA550ADD6, 0x124D6CD2, 0xCB6B2FDF, 0x7C76EEDB, 0xC1CBA1E3, 0x76D660E7, 43 | 0xAFF023EA, 0x18EDE2EE, 0x1DBDA5F0, 0xAAA064F4, 0x738627F9, 0xC49BE6FD, 44 | 0x09FDB889, 0xBEE0798D, 0x67C63A80, 0xD0DBFB84, 0xD58BBC9A, 0x62967D9E, 45 | 0xBBB03E93, 0x0CADFF97, 0xB110B0AF, 0x060D71AB, 0xDF2B32A6, 0x6836F3A2, 46 | 0x6D66B4BC, 0xDA7B75B8, 0x035D36B5, 0xB440F7B1, 0x00000001, 47 | } 48 | 49 | func calcCRC32(crc uint32, data []byte) uint32 { 50 | for _, b := range data { 51 | crc = ieeeCrc32Tbl[b^byte(crc)] ^ (crc >> 8) 52 | } 53 | return crc 54 | } 55 | 56 | -------------------------------------------------------------------------------- /format/ts/tsio/tsio.go: -------------------------------------------------------------------------------- 1 | 2 | package tsio 3 | 4 | import ( 5 | "io" 6 | "time" 7 | "fmt" 8 | "github.com/nareix/joy4/utils/bits/pio" 9 | ) 10 | 11 | const ( 12 | StreamIdH264 = 0xe0 13 | StreamIdAAC = 0xc0 14 | ) 15 | 16 | const ( 17 | PAT_PID = 0 18 | PMT_PID = 0x1000 19 | ) 20 | 21 | const TableIdPMT = 2 22 | const TableExtPMT = 1 23 | 24 | const TableIdPAT = 0 25 | const TableExtPAT = 1 26 | 27 | const MaxPESHeaderLength = 19 28 | const MaxTSHeaderLength = 12 29 | 30 | var ErrPESHeader = fmt.Errorf("invalid PES header") 31 | var ErrPSIHeader = fmt.Errorf("invalid PSI header") 32 | var ErrParsePMT = fmt.Errorf("invalid PMT") 33 | var ErrParsePAT = fmt.Errorf("invalid PAT") 34 | 35 | const ( 36 | ElementaryStreamTypeH264 = 0x1B 37 | ElementaryStreamTypeAdtsAAC = 0x0F 38 | ) 39 | 40 | type PATEntry struct { 41 | ProgramNumber uint16 42 | NetworkPID uint16 43 | ProgramMapPID uint16 44 | } 45 | 46 | type PAT struct { 47 | Entries []PATEntry 48 | } 49 | 50 | func (self PAT) Len() (n int) { 51 | return len(self.Entries)*4 52 | } 53 | 54 | func (self PAT) Marshal(b []byte) (n int) { 55 | for _, entry := range self.Entries { 56 | pio.PutU16BE(b[n:], entry.ProgramNumber) 57 | n += 2 58 | if entry.ProgramNumber == 0 { 59 | pio.PutU16BE(b[n:], entry.NetworkPID&0x1fff|7<<13) 60 | n += 2 61 | } else { 62 | pio.PutU16BE(b[n:], entry.ProgramMapPID&0x1fff|7<<13) 63 | n += 2 64 | } 65 | } 66 | return 67 | } 68 | 69 | func (self *PAT) Unmarshal(b []byte) (n int, err error) { 70 | for n < len(b) { 71 | if n+4 <= len(b) { 72 | var entry PATEntry 73 | entry.ProgramNumber = pio.U16BE(b[n:]) 74 | n += 2 75 | if entry.ProgramNumber == 0 { 76 | entry.NetworkPID = pio.U16BE(b[n:])&0x1fff 77 | n += 2 78 | } else { 79 | entry.ProgramMapPID = pio.U16BE(b[n:])&0x1fff 80 | n += 2 81 | } 82 | self.Entries = append(self.Entries, entry) 83 | } else { 84 | break 85 | } 86 | } 87 | if n < len(b) { 88 | err = ErrParsePAT 89 | return 90 | } 91 | return 92 | } 93 | 94 | type Descriptor struct { 95 | Tag uint8 96 | Data []byte 97 | } 98 | 99 | type ElementaryStreamInfo struct { 100 | StreamType uint8 101 | ElementaryPID uint16 102 | Descriptors []Descriptor 103 | } 104 | 105 | type PMT struct { 106 | PCRPID uint16 107 | ProgramDescriptors []Descriptor 108 | ElementaryStreamInfos []ElementaryStreamInfo 109 | } 110 | 111 | func (self PMT) Len() (n int) { 112 | // 111(3) 113 | // PCRPID(13) 114 | n += 2 115 | 116 | // desclen(16) 117 | n += 2 118 | 119 | for _, desc := range self.ProgramDescriptors { 120 | n += 2+len(desc.Data) 121 | } 122 | 123 | for _, info := range self.ElementaryStreamInfos { 124 | // streamType 125 | n += 1 126 | 127 | // Reserved(3) 128 | // Elementary PID(13) 129 | n += 2 130 | 131 | // Reserved(6) 132 | // ES Info length length(10) 133 | n += 2 134 | 135 | for _, desc := range info.Descriptors { 136 | n += 2+len(desc.Data) 137 | } 138 | } 139 | 140 | return 141 | } 142 | 143 | func (self PMT) fillDescs(b []byte, descs []Descriptor) (n int) { 144 | for _, desc := range descs { 145 | b[n] = desc.Tag 146 | n++ 147 | b[n] = uint8(len(desc.Data)) 148 | n++ 149 | copy(b[n:], desc.Data) 150 | n += len(desc.Data) 151 | } 152 | return 153 | } 154 | 155 | func (self PMT) Marshal(b []byte) (n int) { 156 | // 111(3) 157 | // PCRPID(13) 158 | pio.PutU16BE(b[n:], self.PCRPID|7<<13) 159 | n += 2 160 | 161 | hold := n 162 | n += 2 163 | pos := n 164 | n += self.fillDescs(b[n:], self.ProgramDescriptors) 165 | desclen := n-pos 166 | pio.PutU16BE(b[hold:], uint16(desclen)|0xf<<12) 167 | 168 | for _, info := range self.ElementaryStreamInfos { 169 | b[n] = info.StreamType 170 | n++ 171 | 172 | // Reserved(3) 173 | // Elementary PID(13) 174 | pio.PutU16BE(b[n:], info.ElementaryPID|7<<13) 175 | n += 2 176 | 177 | hold := n 178 | n += 2 179 | pos := n 180 | n += self.fillDescs(b[n:], info.Descriptors) 181 | desclen := n-pos 182 | pio.PutU16BE(b[hold:], uint16(desclen)|0x3c<<10) 183 | } 184 | 185 | return 186 | } 187 | 188 | func (self PMT) parseDescs(b []byte) (descs []Descriptor, err error) { 189 | n := 0 190 | for n < len(b) { 191 | if n+2 <= len(b) { 192 | desc := Descriptor{} 193 | desc.Tag = b[n] 194 | desc.Data = make([]byte, b[n+1]) 195 | n += 2 196 | if n+len(desc.Data) < len(b) { 197 | copy(desc.Data, b[n:]) 198 | descs = append(descs, desc) 199 | n += len(desc.Data) 200 | } else { 201 | break 202 | } 203 | } else { 204 | break 205 | } 206 | } 207 | if n < len(b) { 208 | err = ErrParsePMT 209 | return 210 | } 211 | return 212 | } 213 | 214 | func (self *PMT) Unmarshal(b []byte) (n int, err error) { 215 | if len(b) < n+4 { 216 | err = ErrParsePMT 217 | return 218 | } 219 | 220 | // 111(3) 221 | // PCRPID(13) 222 | self.PCRPID = pio.U16BE(b[0:2])&0x1fff 223 | n += 2 224 | 225 | // Reserved(4)=0xf 226 | // Reserved(2)=0x0 227 | // Program info length(10) 228 | desclen := int(pio.U16BE(b[2:4])&0x3ff) 229 | n += 2 230 | 231 | if desclen > 0 { 232 | if len(b) < n+desclen { 233 | err = ErrParsePMT 234 | return 235 | } 236 | if self.ProgramDescriptors, err = self.parseDescs(b[n:n+desclen]); err != nil { 237 | return 238 | } 239 | n += desclen 240 | } 241 | 242 | for n < len(b) { 243 | if len(b) < n+5 { 244 | err = ErrParsePMT 245 | return 246 | } 247 | 248 | var info ElementaryStreamInfo 249 | info.StreamType = b[n] 250 | n++ 251 | 252 | // Reserved(3) 253 | // Elementary PID(13) 254 | info.ElementaryPID = pio.U16BE(b[n:])&0x1fff 255 | n += 2 256 | 257 | // Reserved(6) 258 | // ES Info length(10) 259 | desclen := int(pio.U16BE(b[n:])&0x3ff) 260 | n += 2 261 | 262 | if desclen > 0 { 263 | if len(b) < n+desclen { 264 | err = ErrParsePMT 265 | return 266 | } 267 | if info.Descriptors, err = self.parseDescs(b[n:n+desclen]); err != nil { 268 | return 269 | } 270 | n += desclen 271 | } 272 | 273 | self.ElementaryStreamInfos = append(self.ElementaryStreamInfos, info) 274 | } 275 | 276 | return 277 | } 278 | 279 | func ParsePSI(h []byte) (tableid uint8, tableext uint16, hdrlen int, datalen int, err error) { 280 | if len(h) < 8 { 281 | err = ErrPSIHeader 282 | return 283 | } 284 | 285 | // pointer(8) 286 | pointer := h[0] 287 | hdrlen++ 288 | if pointer > 0 { 289 | hdrlen += int(pointer) 290 | if len(h) < hdrlen { 291 | err = ErrPSIHeader 292 | return 293 | } 294 | } 295 | 296 | if len(h) < hdrlen+12 { 297 | err = ErrPSIHeader 298 | return 299 | } 300 | 301 | // table_id(8) 302 | tableid = h[hdrlen] 303 | hdrlen++ 304 | 305 | // section_syntax_indicator(1)=1,private_bit(1)=0,reserved(2)=3,unused(2)=0,section_length(10) 306 | datalen = int(pio.U16BE(h[hdrlen:]))&0x3ff - 9 307 | hdrlen += 2 308 | 309 | if datalen < 0 { 310 | err = ErrPSIHeader 311 | return 312 | } 313 | 314 | // Table ID extension(16) 315 | tableext = pio.U16BE(h[hdrlen:]) 316 | hdrlen += 2 317 | 318 | // resverd(2)=3 319 | // version(5) 320 | // Current_next_indicator(1) 321 | hdrlen++ 322 | 323 | // section_number(8) 324 | hdrlen++ 325 | 326 | // last_section_number(8) 327 | hdrlen++ 328 | 329 | // data 330 | 331 | // crc(32) 332 | 333 | return 334 | } 335 | 336 | const PSIHeaderLength = 9 337 | 338 | func FillPSI(h []byte, tableid uint8, tableext uint16, datalen int) (n int) { 339 | // pointer(8) 340 | h[n] = 0 341 | n++ 342 | 343 | // table_id(8) 344 | h[n] = tableid 345 | n++ 346 | 347 | // section_syntax_indicator(1)=1,private_bit(1)=0,reserved(2)=3,unused(2)=0,section_length(10) 348 | pio.PutU16BE(h[n:], uint16(0xa<<12 | 2+3+4+datalen)) 349 | n += 2 350 | 351 | // Table ID extension(16) 352 | pio.PutU16BE(h[n:], tableext) 353 | n += 2 354 | 355 | // resverd(2)=3,version(5)=0,Current_next_indicator(1)=1 356 | h[n] = 0x3<<6 | 1 357 | n++ 358 | 359 | // section_number(8) 360 | h[n] = 0 361 | n++ 362 | 363 | // last_section_number(8) 364 | h[n] = 0 365 | n++ 366 | 367 | n += datalen 368 | 369 | crc := calcCRC32(0xffffffff, h[1:n]) 370 | pio.PutU32LE(h[n:], crc) 371 | n += 4 372 | 373 | return 374 | } 375 | 376 | func TimeToPCR(tm time.Duration) (pcr uint64) { 377 | // base(33)+resverd(6)+ext(9) 378 | ts := uint64(tm*PCR_HZ/time.Second) 379 | base := ts / 300 380 | ext := ts % 300 381 | pcr = base<<15 | 0x3f<<9 | ext 382 | return 383 | } 384 | 385 | func PCRToTime(pcr uint64) (tm time.Duration) { 386 | base := pcr >> 15 387 | ext := pcr & 0x1ff 388 | ts := base*300 + ext 389 | tm = time.Duration(ts)*time.Second/time.Duration(PCR_HZ) 390 | return 391 | } 392 | 393 | func TimeToTs(tm time.Duration) (v uint64) { 394 | ts := uint64(tm*PTS_HZ/time.Second) 395 | // 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1 396 | v = ((ts>>30)&0x7)<<33 | ((ts>>15)&0x7fff)<<17 | (ts&0x7fff)<<1 | 0x100010001 397 | return 398 | } 399 | 400 | func TsToTime(v uint64) (tm time.Duration) { 401 | // 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1 402 | ts := (((v>>33)&0x7)<<30) | (((v>>17)&0x7fff) << 15) | ((v>>1)&0x7fff) 403 | tm = time.Duration(ts)*time.Second/time.Duration(PTS_HZ) 404 | return 405 | } 406 | 407 | const ( 408 | PTS_HZ = 90000 409 | PCR_HZ = 27000000 410 | ) 411 | 412 | func ParsePESHeader(h []byte) (hdrlen int, streamid uint8, datalen int, pts, dts time.Duration, err error) { 413 | if h[0] != 0 || h[1] != 0 || h[2] != 1 { 414 | err = ErrPESHeader 415 | return 416 | } 417 | streamid = h[3] 418 | 419 | flags := h[7] 420 | hdrlen = int(h[8])+9 421 | 422 | datalen = int(pio.U16BE(h[4:6])) 423 | if datalen > 0 { 424 | datalen -= int(h[8])+3 425 | } 426 | 427 | const PTS = 1 << 7 428 | const DTS = 1 << 6 429 | 430 | if flags&PTS != 0 { 431 | if len(h) < 14 { 432 | err = ErrPESHeader 433 | return 434 | } 435 | pts = TsToTime(pio.U40BE(h[9:14])) 436 | if flags&DTS != 0 { 437 | if len(h) < 19 { 438 | err = ErrPESHeader 439 | return 440 | } 441 | dts = TsToTime(pio.U40BE(h[14:19])) 442 | } 443 | } 444 | 445 | return 446 | } 447 | 448 | func FillPESHeader(h []byte, streamid uint8, datalen int, pts, dts time.Duration) (n int) { 449 | h[0] = 0 450 | h[1] = 0 451 | h[2] = 1 452 | h[3] = streamid 453 | 454 | const PTS = 1 << 7 455 | const DTS = 1 << 6 456 | 457 | var flags uint8 458 | if pts != 0 { 459 | flags |= PTS 460 | if dts != 0 { 461 | flags |= DTS 462 | } 463 | } 464 | 465 | if flags&PTS != 0 { 466 | n += 5 467 | } 468 | if flags&DTS != 0 { 469 | n += 5 470 | } 471 | 472 | // packet_length(16) if zero then variable length 473 | // Specifies the number of bytes remaining in the packet after this field. Can be zero. 474 | // If the PES packet length is set to zero, the PES packet can be of any length. 475 | // A value of zero for the PES packet length can be used only when the PES packet payload is a **video** elementary stream. 476 | var pktlen uint16 477 | if datalen >= 0 { 478 | pktlen = uint16(datalen + n + 3) 479 | } 480 | pio.PutU16BE(h[4:6], pktlen) 481 | 482 | h[6] = 2<<6|1 // resverd(6,2)=2,original_or_copy(0,1)=1 483 | h[7] = flags 484 | h[8] = uint8(n) 485 | 486 | // pts(40)? 487 | // dts(40)? 488 | if flags&PTS != 0 { 489 | if flags&DTS != 0 { 490 | pio.PutU40BE(h[9:14], TimeToTs(pts)|3<<36) 491 | pio.PutU40BE(h[14:19], TimeToTs(dts)|1<<36) 492 | } else { 493 | pio.PutU40BE(h[9:14], TimeToTs(pts)|2<<36) 494 | } 495 | } 496 | 497 | n += 9 498 | return 499 | } 500 | 501 | type TSWriter struct { 502 | w io.Writer 503 | ContinuityCounter uint 504 | tshdr []byte 505 | } 506 | 507 | func NewTSWriter(pid uint16) *TSWriter { 508 | w := &TSWriter{} 509 | w.tshdr = make([]byte, 188) 510 | w.tshdr[0] = 0x47 511 | pio.PutU16BE(w.tshdr[1:3], pid&0x1fff) 512 | for i := 6; i < 188; i++ { 513 | w.tshdr[i] = 0xff 514 | } 515 | return w 516 | } 517 | 518 | func (self *TSWriter) WritePackets(w io.Writer, datav [][]byte, pcr time.Duration, sync bool, paddata bool) (err error) { 519 | datavlen := pio.VecLen(datav) 520 | writev := make([][]byte, len(datav)) 521 | writepos := 0 522 | 523 | for writepos < datavlen { 524 | self.tshdr[1] = self.tshdr[1]&0x1f 525 | self.tshdr[3] = byte(self.ContinuityCounter)&0xf|0x30 526 | self.tshdr[5] = 0 // flags 527 | hdrlen := 6 528 | self.ContinuityCounter++ 529 | 530 | if writepos == 0 { 531 | self.tshdr[1] = 0x40|self.tshdr[1] // Payload Unit Start Indicator 532 | if pcr != 0 { 533 | hdrlen += 6 534 | self.tshdr[5] = 0x10|self.tshdr[5] // PCR flag (Discontinuity indicator 0x80) 535 | pio.PutU48BE(self.tshdr[6:12], TimeToPCR(pcr)) 536 | } 537 | if sync { 538 | self.tshdr[5] = 0x40|self.tshdr[5] // Random Access indicator 539 | } 540 | } 541 | 542 | padtail := 0 543 | end := writepos + 188 - hdrlen 544 | if end > datavlen { 545 | if paddata { 546 | padtail = end - datavlen 547 | } else { 548 | hdrlen += end - datavlen 549 | } 550 | end = datavlen 551 | } 552 | n := pio.VecSliceTo(datav, writev, writepos, end) 553 | 554 | self.tshdr[4] = byte(hdrlen)-5 // length 555 | if _, err = w.Write(self.tshdr[:hdrlen]); err != nil { 556 | return 557 | } 558 | for i := 0; i < n; i++ { 559 | if _, err = w.Write(writev[i]); err != nil { 560 | return 561 | } 562 | } 563 | if padtail > 0 { 564 | if _, err = w.Write(self.tshdr[188-padtail:188]); err != nil { 565 | return 566 | } 567 | } 568 | 569 | writepos = end 570 | } 571 | 572 | return 573 | } 574 | 575 | func ParseTSHeader(tshdr []byte) (pid uint16, start bool, iskeyframe bool, hdrlen int, err error) { 576 | // https://en.wikipedia.org/wiki/MPEG_transport_stream 577 | if tshdr[0] != 0x47 { 578 | err = fmt.Errorf("tshdr sync invalid") 579 | return 580 | } 581 | pid = uint16((tshdr[1]&0x1f))<<8|uint16(tshdr[2]) 582 | start = tshdr[1]&0x40 != 0 583 | hdrlen += 4 584 | if tshdr[3]&0x20 != 0 { 585 | hdrlen += int(tshdr[4])+1 586 | iskeyframe = tshdr[5]&0x40 != 0 587 | } 588 | return 589 | } 590 | 591 | -------------------------------------------------------------------------------- /utils/bits/bits.go: -------------------------------------------------------------------------------- 1 | package bits 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type Reader struct { 8 | R io.Reader 9 | n int 10 | bits uint64 11 | } 12 | 13 | func (self *Reader) ReadBits64(n int) (bits uint64, err error) { 14 | if self.n < n { 15 | var b [8]byte 16 | var got int 17 | want := (n - self.n + 7) / 8 18 | if got, err = self.R.Read(b[:want]); err != nil { 19 | return 20 | } 21 | if got < want { 22 | err = io.EOF 23 | return 24 | } 25 | for i := 0; i < got; i++ { 26 | self.bits <<= 8 27 | self.bits |= uint64(b[i]) 28 | } 29 | self.n += got * 8 30 | } 31 | bits = self.bits >> uint(self.n-n) 32 | self.bits ^= bits << uint(self.n-n) 33 | self.n -= n 34 | return 35 | } 36 | 37 | func (self *Reader) ReadBits(n int) (bits uint, err error) { 38 | var bits64 uint64 39 | if bits64, err = self.ReadBits64(n); err != nil { 40 | return 41 | } 42 | bits = uint(bits64) 43 | return 44 | } 45 | 46 | func (self *Reader) Read(p []byte) (n int, err error) { 47 | for n < len(p) { 48 | want := 8 49 | if len(p)-n < want { 50 | want = len(p) - n 51 | } 52 | var bits uint64 53 | if bits, err = self.ReadBits64(want * 8); err != nil { 54 | break 55 | } 56 | for i := 0; i < want; i++ { 57 | p[n+i] = byte(bits >> uint((want-i-1)*8)) 58 | } 59 | n += want 60 | } 61 | return 62 | } 63 | 64 | type Writer struct { 65 | W io.Writer 66 | n int 67 | bits uint64 68 | } 69 | 70 | func (self *Writer) WriteBits64(bits uint64, n int) (err error) { 71 | if self.n+n > 64 { 72 | move := uint(64 - self.n) 73 | mask := bits >> move 74 | self.bits = (self.bits << move) | mask 75 | self.n = 64 76 | if err = self.FlushBits(); err != nil { 77 | return 78 | } 79 | n -= int(move) 80 | bits ^= (mask << move) 81 | } 82 | self.bits = (self.bits << uint(n)) | bits 83 | self.n += n 84 | return 85 | } 86 | 87 | func (self *Writer) WriteBits(bits uint, n int) (err error) { 88 | return self.WriteBits64(uint64(bits), n) 89 | } 90 | 91 | func (self *Writer) Write(p []byte) (n int, err error) { 92 | for n < len(p) { 93 | if err = self.WriteBits64(uint64(p[n]), 8); err != nil { 94 | return 95 | } 96 | n++ 97 | } 98 | return 99 | } 100 | 101 | func (self *Writer) FlushBits() (err error) { 102 | if self.n > 0 { 103 | var b [8]byte 104 | bits := self.bits 105 | if self.n%8 != 0 { 106 | bits <<= uint(8 - (self.n % 8)) 107 | } 108 | want := (self.n + 7) / 8 109 | for i := 0; i < want; i++ { 110 | b[i] = byte(bits >> uint((want-i-1)*8)) 111 | } 112 | if _, err = self.W.Write(b[:want]); err != nil { 113 | return 114 | } 115 | self.n = 0 116 | } 117 | return 118 | } 119 | -------------------------------------------------------------------------------- /utils/bits/bits_test.go: -------------------------------------------------------------------------------- 1 | package bits 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestBits(t *testing.T) { 9 | rdata := []byte{0xf3, 0xb3, 0x45, 0x60} 10 | rbuf := bytes.NewReader(rdata[:]) 11 | r := &Reader{R: rbuf} 12 | var u32 uint 13 | if u32, _ = r.ReadBits(4); u32 != 0xf { 14 | t.FailNow() 15 | } 16 | if u32, _ = r.ReadBits(4); u32 != 0x3 { 17 | t.FailNow() 18 | } 19 | if u32, _ = r.ReadBits(2); u32 != 0x2 { 20 | t.FailNow() 21 | } 22 | if u32, _ = r.ReadBits(2); u32 != 0x3 { 23 | t.FailNow() 24 | } 25 | b := make([]byte, 2) 26 | if r.Read(b); b[0] != 0x34 || b[1] != 0x56 { 27 | t.FailNow() 28 | } 29 | 30 | wbuf := &bytes.Buffer{} 31 | w := &Writer{W: wbuf} 32 | w.WriteBits(0xf, 4) 33 | w.WriteBits(0x3, 4) 34 | w.WriteBits(0x2, 2) 35 | w.WriteBits(0x3, 2) 36 | n, _ := w.Write([]byte{0x34, 0x56}) 37 | if n != 2 { 38 | t.FailNow() 39 | } 40 | w.FlushBits() 41 | wdata := wbuf.Bytes() 42 | if wdata[0] != 0xf3 || wdata[1] != 0xb3 || wdata[2] != 0x45 || wdata[3] != 0x60 { 43 | t.FailNow() 44 | } 45 | 46 | b = make([]byte, 8) 47 | PutUInt64BE(b, 0x11223344, 32) 48 | if b[0] != 0x11 || b[1] != 0x22 || b[2] != 0x33 || b[3] != 0x44 { 49 | t.FailNow() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /utils/bits/bufio/bufio.go: -------------------------------------------------------------------------------- 1 | package bufio 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type Reader struct { 8 | buf [][]byte 9 | R io.ReadSeeker 10 | } 11 | 12 | func NewReaderSize(r io.ReadSeeker, size int) *Reader { 13 | buf := make([]byte, size*2) 14 | return &Reader{ 15 | R: r, 16 | buf: [][]byte{buf[0:size], buf[size:]}, 17 | } 18 | } 19 | 20 | func (self *Reader) ReadAt(b []byte, off int64) (n int, err error) { 21 | return 22 | } 23 | 24 | -------------------------------------------------------------------------------- /utils/bits/golomb_reader.go: -------------------------------------------------------------------------------- 1 | package bits 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type GolombBitReader struct { 8 | R io.Reader 9 | buf [1]byte 10 | left byte 11 | } 12 | 13 | func (self *GolombBitReader) ReadBit() (res uint, err error) { 14 | if self.left == 0 { 15 | if _, err = self.R.Read(self.buf[:]); err != nil { 16 | return 17 | } 18 | self.left = 8 19 | } 20 | self.left-- 21 | res = uint(self.buf[0]>>self.left) & 1 22 | return 23 | } 24 | 25 | func (self *GolombBitReader) ReadBits(n int) (res uint, err error) { 26 | for i := 0; i < n; i++ { 27 | var bit uint 28 | if bit, err = self.ReadBit(); err != nil { 29 | return 30 | } 31 | res |= bit << uint(n-i-1) 32 | } 33 | return 34 | } 35 | 36 | func (self *GolombBitReader) ReadExponentialGolombCode() (res uint, err error) { 37 | i := 0 38 | for { 39 | var bit uint 40 | if bit, err = self.ReadBit(); err != nil { 41 | return 42 | } 43 | if !(bit == 0 && i < 32) { 44 | break 45 | } 46 | i++ 47 | } 48 | if res, err = self.ReadBits(i); err != nil { 49 | return 50 | } 51 | res += (1 << uint(i)) - 1 52 | return 53 | } 54 | 55 | func (self *GolombBitReader) ReadSE() (res uint, err error) { 56 | if res, err = self.ReadExponentialGolombCode(); err != nil { 57 | return 58 | } 59 | if res&0x01 != 0 { 60 | res = (res + 1) / 2 61 | } else { 62 | res = -res / 2 63 | } 64 | return 65 | } 66 | -------------------------------------------------------------------------------- /utils/bits/pio/pio.go: -------------------------------------------------------------------------------- 1 | 2 | package pio 3 | 4 | var RecommendBufioSize = 1024*64 5 | 6 | -------------------------------------------------------------------------------- /utils/bits/pio/reader.go: -------------------------------------------------------------------------------- 1 | 2 | package pio 3 | 4 | func U8(b []byte) (i uint8) { 5 | return b[0] 6 | } 7 | 8 | func U16BE(b []byte) (i uint16) { 9 | i = uint16(b[0]) 10 | i <<= 8; i |= uint16(b[1]) 11 | return 12 | } 13 | 14 | func I16BE(b []byte) (i int16) { 15 | i = int16(b[0]) 16 | i <<= 8; i |= int16(b[1]) 17 | return 18 | } 19 | 20 | func I24BE(b []byte) (i int32) { 21 | i = int32(int8(b[0])) 22 | i <<= 8; i |= int32(b[1]) 23 | i <<= 8; i |= int32(b[2]) 24 | return 25 | } 26 | 27 | func U24BE(b []byte) (i uint32) { 28 | i = uint32(b[0]) 29 | i <<= 8; i |= uint32(b[1]) 30 | i <<= 8; i |= uint32(b[2]) 31 | return 32 | } 33 | 34 | func I32BE(b []byte) (i int32) { 35 | i = int32(int8(b[0])) 36 | i <<= 8; i |= int32(b[1]) 37 | i <<= 8; i |= int32(b[2]) 38 | i <<= 8; i |= int32(b[3]) 39 | return 40 | } 41 | 42 | func U32LE(b []byte) (i uint32) { 43 | i = uint32(b[3]) 44 | i <<= 8; i |= uint32(b[2]) 45 | i <<= 8; i |= uint32(b[1]) 46 | i <<= 8; i |= uint32(b[0]) 47 | return 48 | } 49 | 50 | func U32BE(b []byte) (i uint32) { 51 | i = uint32(b[0]) 52 | i <<= 8; i |= uint32(b[1]) 53 | i <<= 8; i |= uint32(b[2]) 54 | i <<= 8; i |= uint32(b[3]) 55 | return 56 | } 57 | 58 | func U40BE(b []byte) (i uint64) { 59 | i = uint64(b[0]) 60 | i <<= 8; i |= uint64(b[1]) 61 | i <<= 8; i |= uint64(b[2]) 62 | i <<= 8; i |= uint64(b[3]) 63 | i <<= 8; i |= uint64(b[4]) 64 | return 65 | } 66 | 67 | func U64BE(b []byte) (i uint64) { 68 | i = uint64(b[0]) 69 | i <<= 8; i |= uint64(b[1]) 70 | i <<= 8; i |= uint64(b[2]) 71 | i <<= 8; i |= uint64(b[3]) 72 | i <<= 8; i |= uint64(b[4]) 73 | i <<= 8; i |= uint64(b[5]) 74 | i <<= 8; i |= uint64(b[6]) 75 | i <<= 8; i |= uint64(b[7]) 76 | return 77 | } 78 | 79 | func I64BE(b []byte) (i int64) { 80 | i = int64(int8(b[0])) 81 | i <<= 8; i |= int64(b[1]) 82 | i <<= 8; i |= int64(b[2]) 83 | i <<= 8; i |= int64(b[3]) 84 | i <<= 8; i |= int64(b[4]) 85 | i <<= 8; i |= int64(b[5]) 86 | i <<= 8; i |= int64(b[6]) 87 | i <<= 8; i |= int64(b[7]) 88 | return 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /utils/bits/pio/vec.go: -------------------------------------------------------------------------------- 1 | package pio 2 | 3 | func VecLen(vec [][]byte) (n int) { 4 | for _, b := range vec { 5 | n += len(b) 6 | } 7 | return 8 | } 9 | 10 | func VecSliceTo(in [][]byte, out [][]byte, s int, e int) (n int) { 11 | if s < 0 { 12 | s = 0 13 | } 14 | 15 | if e >= 0 && e < s { 16 | panic("pio: VecSlice start > end") 17 | } 18 | 19 | i := 0 20 | off := 0 21 | for s > 0 && i < len(in) { 22 | left := len(in[i]) 23 | read := s 24 | if left < read { 25 | read = left 26 | } 27 | left -= read 28 | off += read 29 | s -= read 30 | e -= read 31 | if left == 0 { 32 | i++ 33 | off = 0 34 | } 35 | } 36 | if s > 0 { 37 | panic("pio: VecSlice start out of range") 38 | } 39 | 40 | for e != 0 && i < len(in) { 41 | left := len(in[i])-off 42 | read := left 43 | if e > 0 && e < read { 44 | read = e 45 | } 46 | out[n] = in[i][off:off+read] 47 | n++ 48 | left -= read 49 | e -= read 50 | off += read 51 | if left == 0 { 52 | i++ 53 | off = 0 54 | } 55 | } 56 | if e > 0 { 57 | panic("pio: VecSlice end out of range") 58 | } 59 | 60 | return 61 | } 62 | 63 | func VecSlice(in [][]byte, s int, e int) (out [][]byte) { 64 | out = make([][]byte, len(in)) 65 | n := VecSliceTo(in, out, s, e) 66 | out = out[:n] 67 | return 68 | } 69 | 70 | -------------------------------------------------------------------------------- /utils/bits/pio/vec_test.go: -------------------------------------------------------------------------------- 1 | 2 | package pio 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | func ExampleVec() { 9 | vec := [][]byte{[]byte{1,2,3}, []byte{4,5,6,7,8,9}, []byte{10,11,12,13}} 10 | println(VecLen(vec)) 11 | 12 | vec = VecSlice(vec, 1, -1) 13 | fmt.Println(vec) 14 | 15 | vec = VecSlice(vec, 2, -1) 16 | fmt.Println(vec) 17 | 18 | vec = VecSlice(vec, 8, 8) 19 | fmt.Println(vec) 20 | 21 | // Output: 22 | } 23 | -------------------------------------------------------------------------------- /utils/bits/pio/writer.go: -------------------------------------------------------------------------------- 1 | 2 | package pio 3 | 4 | func PutU8(b []byte, v uint8) { 5 | b[0] = v 6 | } 7 | 8 | func PutI16BE(b []byte, v int16) { 9 | b[0] = byte(v>>8) 10 | b[1] = byte(v) 11 | } 12 | 13 | func PutU16BE(b []byte, v uint16) { 14 | b[0] = byte(v>>8) 15 | b[1] = byte(v) 16 | } 17 | 18 | func PutI24BE(b []byte, v int32) { 19 | b[0] = byte(v>>16) 20 | b[1] = byte(v>>8) 21 | b[2] = byte(v) 22 | } 23 | 24 | func PutU24BE(b []byte, v uint32) { 25 | b[0] = byte(v>>16) 26 | b[1] = byte(v>>8) 27 | b[2] = byte(v) 28 | } 29 | 30 | func PutI32BE(b []byte, v int32) { 31 | b[0] = byte(v>>24) 32 | b[1] = byte(v>>16) 33 | b[2] = byte(v>>8) 34 | b[3] = byte(v) 35 | } 36 | 37 | func PutU32BE(b []byte, v uint32) { 38 | b[0] = byte(v>>24) 39 | b[1] = byte(v>>16) 40 | b[2] = byte(v>>8) 41 | b[3] = byte(v) 42 | } 43 | 44 | func PutU32LE(b []byte, v uint32) { 45 | b[3] = byte(v>>24) 46 | b[2] = byte(v>>16) 47 | b[1] = byte(v>>8) 48 | b[0] = byte(v) 49 | } 50 | 51 | func PutU40BE(b []byte, v uint64) { 52 | b[0] = byte(v>>32) 53 | b[1] = byte(v>>24) 54 | b[2] = byte(v>>16) 55 | b[3] = byte(v>>8) 56 | b[4] = byte(v) 57 | } 58 | 59 | func PutU48BE(b []byte, v uint64) { 60 | b[0] = byte(v>>40) 61 | b[1] = byte(v>>32) 62 | b[2] = byte(v>>24) 63 | b[3] = byte(v>>16) 64 | b[4] = byte(v>>8) 65 | b[5] = byte(v) 66 | } 67 | 68 | func PutU64BE(b []byte, v uint64) { 69 | b[0] = byte(v>>56) 70 | b[1] = byte(v>>48) 71 | b[2] = byte(v>>40) 72 | b[3] = byte(v>>32) 73 | b[4] = byte(v>>24) 74 | b[5] = byte(v>>16) 75 | b[6] = byte(v>>8) 76 | b[7] = byte(v) 77 | } 78 | 79 | func PutI64BE(b []byte, v int64) { 80 | b[0] = byte(v>>56) 81 | b[1] = byte(v>>48) 82 | b[2] = byte(v>>40) 83 | b[3] = byte(v>>32) 84 | b[4] = byte(v>>24) 85 | b[5] = byte(v>>16) 86 | b[6] = byte(v>>8) 87 | b[7] = byte(v) 88 | } 89 | 90 | --------------------------------------------------------------------------------