From f412852d5058989a649f9faff14bc7a6f5831810 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Mon, 14 Aug 2023 06:48:25 +0300 Subject: [PATCH] Remove old HTTP-FLV client --- pkg/httpflv/README.md | 3 - pkg/httpflv/amf0.go | 165 ------------------------------- pkg/httpflv/flvio.go | 97 ------------------- pkg/httpflv/httpflv.go | 214 ----------------------------------------- 4 files changed, 479 deletions(-) delete mode 100644 pkg/httpflv/README.md delete mode 100644 pkg/httpflv/amf0.go delete mode 100644 pkg/httpflv/flvio.go delete mode 100644 pkg/httpflv/httpflv.go diff --git a/pkg/httpflv/README.md b/pkg/httpflv/README.md deleted file mode 100644 index d928944b..00000000 --- a/pkg/httpflv/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Useful links - -- https://medium.com/@nate510/don-t-use-go-s-default-http-client-4804cb19f779 diff --git a/pkg/httpflv/amf0.go b/pkg/httpflv/amf0.go deleted file mode 100644 index 82a2e72f..00000000 --- a/pkg/httpflv/amf0.go +++ /dev/null @@ -1,165 +0,0 @@ -package httpflv - -import ( - "encoding/binary" - "errors" - "math" -) - -const ( - TypeNumber byte = iota - TypeBoolean - TypeString - TypeObject - TypeEcmaArray = 8 - TypeObjectEnd = 9 -) - -var Err = errors.New("amf0 read error") - -// AMF0 spec: http://download.macromedia.com/pub/labs/amf/amf0_spec_121207.pdf -type AMF0 struct { - buf []byte - pos int -} - -func NewReader(b []byte) *AMF0 { - return &AMF0{buf: b} -} - -func (a *AMF0) ReadMetaData() map[string]any { - if b, _ := a.ReadByte(); b != TypeString { - return nil - } - if s, _ := a.ReadString(); s != "onMetaData" { - return nil - } - - b, _ := a.ReadByte() - switch b { - case TypeObject: - v, _ := a.ReadObject() - return v - case TypeEcmaArray: - v, _ := a.ReadEcmaArray() - return v - } - - return nil -} - -func (a *AMF0) ReadMap() (map[any]any, error) { - dict := make(map[any]any) - - for a.pos < len(a.buf) { - k, err := a.ReadItem() - if err != nil { - return nil, err - } - v, err := a.ReadItem() - if err != nil { - return nil, err - } - dict[k] = v - } - - return dict, nil -} - -func (a *AMF0) ReadItem() (any, error) { - dataType, err := a.ReadByte() - if err != nil { - return nil, err - } - - switch dataType { - case TypeNumber: - return a.ReadNumber() - - case TypeBoolean: - v, err := a.ReadByte() - return v != 0, err - - case TypeString: - return a.ReadString() - - case TypeObject: - return a.ReadObject() - - case TypeObjectEnd: - return nil, nil - } - - return nil, Err -} - -func (a *AMF0) ReadByte() (byte, error) { - if a.pos >= len(a.buf) { - return 0, Err - } - - v := a.buf[a.pos] - a.pos++ - return v, nil -} - -func (a *AMF0) ReadNumber() (float64, error) { - if a.pos+8 >= len(a.buf) { - return 0, Err - } - - v := binary.BigEndian.Uint64(a.buf[a.pos : a.pos+8]) - a.pos += 8 - return math.Float64frombits(v), nil -} - -func (a *AMF0) ReadString() (string, error) { - if a.pos+2 >= len(a.buf) { - return "", Err - } - - size := int(binary.BigEndian.Uint16(a.buf[a.pos:])) - a.pos += 2 - - if a.pos+size >= len(a.buf) { - return "", Err - } - - s := string(a.buf[a.pos : a.pos+size]) - a.pos += size - - return s, nil -} - -func (a *AMF0) ReadObject() (map[string]any, error) { - obj := make(map[string]any) - - for { - k, err := a.ReadString() - if err != nil { - return nil, err - } - - v, err := a.ReadItem() - if err != nil { - return nil, err - } - - if k == "" { - break - } - - obj[k] = v - } - - return obj, nil -} - -func (a *AMF0) ReadEcmaArray() (map[string]any, error) { - if a.pos+4 >= len(a.buf) { - return nil, Err - } - a.pos += 4 // skip size - - return a.ReadObject() -} diff --git a/pkg/httpflv/flvio.go b/pkg/httpflv/flvio.go deleted file mode 100644 index d0a53f10..00000000 --- a/pkg/httpflv/flvio.go +++ /dev/null @@ -1,97 +0,0 @@ -package httpflv - -import ( - "fmt" - "github.com/deepch/vdk/format/flv/flvio" - "github.com/deepch/vdk/utils/bits/pio" - "io" -) - -// TODO: rewrite all of this someday - -func ReadTag(r io.Reader, b []byte) (tag flvio.Tag, ts int32, err error) { - if _, err = io.ReadFull(r, b[:flvio.TagHeaderLength]); err != nil { - return - } - var datalen int - if tag, ts, datalen, err = flvio.ParseTagHeader(b); err != nil { - return - } - - data := make([]byte, datalen) - if _, err = io.ReadFull(r, data); err != nil { - return - } - - n, err := ParseHeader(&tag, data) - if err != nil { - return - } - tag.Data = data[n:] - - if _, err = io.ReadFull(r, b[:4]); err != nil { - return - } - return -} - -func ParseHeader(self *flvio.Tag, b []byte) (n int, err error) { - switch self.Type { - case flvio.TAG_AUDIO: - return audioParseHeader(self, b) - - case flvio.TAG_VIDEO: - return videoParseHeader(self, b) - } - - return -} - -func audioParseHeader(tag *flvio.Tag, b []byte) (n int, err error) { - if len(b) < n+1 { - err = fmt.Errorf("audiodata: parse invalid") - return - } - - flags := b[n] - n++ - tag.SoundFormat = flags >> 4 - tag.SoundRate = (flags >> 2) & 0x3 - tag.SoundSize = (flags >> 1) & 0x1 - tag.SoundType = flags & 0x1 - - switch tag.SoundFormat { - case flvio.SOUND_AAC: - if len(b) < n+1 { - err = fmt.Errorf("audiodata: parse invalid") - return - } - tag.AACPacketType = b[n] - n++ - } - - return -} - -func videoParseHeader(tag *flvio.Tag, b []byte) (n int, err error) { - if len(b) < n+1 { - err = fmt.Errorf("videodata: parse invalid") - return - } - flags := b[n] - tag.FrameType = flags >> 4 - tag.CodecID = flags & 0xf - n++ - - if len(b) < n+4 { - err = fmt.Errorf("videodata: parse invalid") - return - } - tag.AVCPacketType = b[n] - n++ - - tag.CompositionTime = pio.I24BE(b[n:]) - n += 3 - - return -} diff --git a/pkg/httpflv/httpflv.go b/pkg/httpflv/httpflv.go deleted file mode 100644 index ba13f500..00000000 --- a/pkg/httpflv/httpflv.go +++ /dev/null @@ -1,214 +0,0 @@ -package httpflv - -import ( - "bufio" - "bytes" - "github.com/deepch/vdk/av" - "github.com/deepch/vdk/codec/aacparser" - "github.com/deepch/vdk/codec/h264parser" - "github.com/deepch/vdk/format/flv/flvio" - "github.com/deepch/vdk/utils/bits/pio" - "io" - "net/http" -) - -func Dial(uri string) (*Conn, error) { - req, err := http.NewRequest("GET", uri, nil) - if err != nil { - return nil, err - } - - res, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - - return Accept(res) -} - -func Accept(res *http.Response) (*Conn, error) { - c := Conn{ - conn: res.Body, - reader: bufio.NewReaderSize(res.Body, pio.RecommendBufioSize), - buf: make([]byte, 256), - } - - if _, err := io.ReadFull(c.reader, c.buf[:flvio.FileHeaderLength]); err != nil { - return nil, err - } - - flags, n, err := flvio.ParseFileHeader(c.buf) - if err != nil { - return nil, err - } - - if flags&flvio.FILE_HAS_VIDEO != 0 { - c.videoIdx = -1 - } - - if flags&flvio.FILE_HAS_AUDIO != 0 { - c.audioIdx = -1 - } - - if _, err = c.reader.Discard(n); err != nil { - return nil, err - } - - return &c, nil -} - -type Conn struct { - conn io.ReadCloser - reader *bufio.Reader - buf []byte - - videoIdx int8 - audioIdx int8 -} - -func (c *Conn) Streams() ([]av.CodecData, error) { - var video, audio av.CodecData - - // Normal software sends: - // 1. Video/audio flag in header - // 2. MetaData as first tag (with video/audio codec info) - // 3. Video/audio headers in 2nd and 3rd tag - - // Reolink camera sends: - // 1. Empty video/audio flag - // 2. MedaData without stereo key for AAC - // 3. Audio header after Video keyframe tag - - waitVideo := c.videoIdx != 0 - waitAudio := c.audioIdx != 0 - - for i := 0; i < 20; i++ { - tag, _, err := flvio.ReadTag(c.reader, c.buf) - if err != nil { - return nil, err - } - - //log.Printf("[FLV] type=%d avc=%d aac=%d video=%t audio=%t", tag.Type, tag.AVCPacketType, tag.AACPacketType, video != nil, audio != nil) - - switch tag.Type { - case flvio.TAG_SCRIPTDATA: - if meta := NewReader(tag.Data).ReadMetaData(); meta != nil { - waitVideo = meta["videocodecid"] != nil - - // don't wait audio tag because parse all info from MetaData - waitAudio = false - - audio = parseAudioConfig(meta) - } else { - waitVideo = bytes.Contains(tag.Data, []byte("videocodecid")) - waitAudio = bytes.Contains(tag.Data, []byte("audiocodecid")) - } - - case flvio.TAG_VIDEO: - if tag.AVCPacketType == flvio.AVC_SEQHDR { - video, _ = h264parser.NewCodecDataFromAVCDecoderConfRecord(tag.Data) - } - waitVideo = false - - case flvio.TAG_AUDIO: - if tag.SoundFormat == flvio.SOUND_AAC && tag.AACPacketType == flvio.AAC_SEQHDR { - audio, _ = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(tag.Data) - } - waitAudio = false - } - - if !waitVideo && !waitAudio { - break - } - } - - if video != nil && audio != nil { - c.videoIdx = 0 - c.audioIdx = 1 - return []av.CodecData{video, audio}, nil - } else if video != nil { - c.videoIdx = 0 - c.audioIdx = -1 - return []av.CodecData{video}, nil - } else if audio != nil { - c.videoIdx = -1 - c.audioIdx = 0 - return []av.CodecData{audio}, nil - } - - return nil, nil -} - -func (c *Conn) ReadPacket() (av.Packet, error) { - for { - tag, ts, err := ReadTag(c.reader, c.buf) - if err != nil { - return av.Packet{}, err - } - - switch tag.Type { - case flvio.TAG_VIDEO: - if c.videoIdx < 0 || tag.AVCPacketType != flvio.AVC_NALU { - continue - } - - //log.Printf("[FLV] %v, len: %d, ts: %10d", h264.Types(tag.Data), len(tag.Data), flvio.TsToTime(ts)) - - return av.Packet{ - Idx: c.videoIdx, - Data: tag.Data, - CompositionTime: flvio.TsToTime(tag.CompositionTime), - IsKeyFrame: tag.FrameType == flvio.FRAME_KEY, - Time: flvio.TsToTime(ts), - }, nil - - case flvio.TAG_AUDIO: - if c.audioIdx < 0 || tag.SoundFormat != flvio.SOUND_AAC || tag.AACPacketType != flvio.AAC_RAW { - continue - } - - return av.Packet{Idx: c.audioIdx, Data: tag.Data, Time: flvio.TsToTime(ts)}, nil - } - } -} - -func (c *Conn) Close() (err error) { - return c.conn.Close() -} - -func parseAudioConfig(meta map[string]any) av.CodecData { - if meta["audiocodecid"] != float64(10) { - return nil - } - - config := aacparser.MPEG4AudioConfig{ - ObjectType: aacparser.AOT_AAC_LC, - } - - switch v := meta["audiosamplerate"].(type) { - case float64: - config.SampleRate = int(v) - default: - return nil - } - - switch meta["stereo"] { - case true: - config.ChannelConfig = 2 - config.ChannelLayout = av.CH_STEREO - default: - // Reolink doesn't have this setting - config.ChannelConfig = 1 - config.ChannelLayout = av.CH_MONO - } - - buf := &bytes.Buffer{} - if err := aacparser.WriteMPEG4AudioConfig(buf, config); err != nil { - return nil - } - - return aacparser.CodecData{ - Config: config, - ConfigBytes: buf.Bytes(), - } -}