diff --git a/codec/mjpeg/extract.go b/codec/mjpeg/extract.go index d0f2e791..4181639c 100644 --- a/codec/mjpeg/extract.go +++ b/codec/mjpeg/extract.go @@ -74,7 +74,11 @@ type Extractor struct { } // NewExtractor returns a new Extractor. -func NewExtractor() *Extractor { return &Extractor{} } +func NewExtractor() *Extractor { + return &Extractor{ + buf: new(bytes.Buffer), + } +} // Extract will continously read RTP packets from src containing JPEG (in RTP // payload format) and extract the JPEG images, sending them to dst. This @@ -248,7 +252,7 @@ func (b *byteStream) remaining() int { } func (b *byteStream) writeTo(w io.Writer, n int) error { - _n,err := w.Write(b.bytes[b.i:n]) + _n,err := w.Write(b.bytes[b.i:b.i+n]) b.i += _n if err != nil { return err diff --git a/codec/mjpeg/jpeg.go b/codec/mjpeg/jpeg.go index 72daf6f6..fd44a623 100644 --- a/codec/mjpeg/jpeg.go +++ b/codec/mjpeg/jpeg.go @@ -25,6 +25,7 @@ LICENSE package mjpeg import ( + "bytes" "encoding/binary" "fmt" "io" @@ -35,7 +36,7 @@ const ( codeSOI = 0xd8 // Start of image. codeDRI = 0xdd // Define restart interval. codeDQT = 0xdb // Define quantization tables. - codeDHT = 0xde // Define hierarchical progression. + codeDHT = 0xc4 // Define huffman tables. codeSOS = 0xda // Start of scan. codeAPP0 = 0xe0 // TODO: find out what this is. codeSOF0 = 0xc0 // Baseline @@ -165,7 +166,7 @@ func writeHeader(w io.Writer, _type, width, height, nbqTab, dri int, qtable []by return fmt.Errorf("could not write DRI marker code: %w", err) } - _, err := w.Write([]byte{0x00, 0x04, byte(dri >> 4), byte(dri)}) + _, err := w.Write([]byte{0x00, 0x04, byte(dri >> 8), byte(dri)}) if err != nil { return fmt.Errorf("could not write restart interval value: %w", err) } @@ -179,7 +180,7 @@ func writeHeader(w io.Writer, _type, width, height, nbqTab, dri int, qtable []by // Calculate table size and create slice for table. ts := 2 + nbqTab*(1+64) - _, err = w.Write([]byte{byte(ts >> 4), byte(ts)}) + _, err = w.Write([]byte{byte(ts >> 8), byte(ts)}) if err != nil { return fmt.Errorf("could not write quantization table size: %w", err) } @@ -203,14 +204,25 @@ func writeHeader(w io.Writer, _type, width, height, nbqTab, dri int, qtable []by } var me multiError - me.add(writeHuffman(w, 0, 0, bitsDCLum, valDC)) - me.add(writeHuffman(w, 0, 1, bitsDCChr, valDC)) - me.add(writeHuffman(w, 1, 0, bitsACLum, valACLum)) - me.add(writeHuffman(w, 1, 1, bitsACChr, valACChr)) + buf := new(bytes.Buffer) + me.add(writeHuffman(buf, 0, 0, bitsDCLum, valDC)) + me.add(writeHuffman(buf, 0, 1, bitsDCChr, valDC)) + me.add(writeHuffman(buf, 1, 0, bitsACLum, valACLum)) + me.add(writeHuffman(buf, 1, 1, bitsACChr, valACChr)) if me != nil { return fmt.Errorf("error writing huffman tables: %w", err) } - return nil + + l := buf.Len() + 2 + _, err = w.Write([]byte{byte(l >> 8), byte(l)}) + if err != nil { + return fmt.Errorf("could not write quantization table entry: %w", err) + } + + _, err = buf.WriteTo(w) + if err != nil { + return fmt.Errorf("could not write huffman tables: %w", err) + } // Start of frame. err = writeMarker(w, codeSOF0) diff --git a/device/geovision/geovision.go b/device/geovision/geovision.go index 1b3e82ef..d09054ad 100644 --- a/device/geovision/geovision.go +++ b/device/geovision/geovision.go @@ -68,7 +68,7 @@ const ( defaultFrameRate = 25 defaultBitrate = 400 defaultVBRBitrate = 400 - defaultMinFrames = 100 + defaultMinFrames = 3 defaultVBRQuality = avconfig.QualityStandard defaultCameraChan = 2 ) @@ -136,9 +136,10 @@ func (g *GeoVision) Set(c avconfig.Config) error { c.Bitrate = defaultBitrate } - if c.MinFrames <= 0 { + refresh := float64(c.MinFrames) / float64(c.FrameRate) + if refresh < 1 || refresh > 5 { errs = append(errs, errGVBadMinFrames) - c.MinFrames = defaultMinFrames + c.MinFrames = 4 * c.FrameRate } // If we're using RTMP then we should default to constant bitrate. diff --git a/revid/revid.go b/revid/revid.go index b2b399dc..44ea3108 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -362,7 +362,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. case codecutil.H265: r.lexTo = h265.NewLexer(false).Lex case codecutil.MJPEG: - panic("not implemented") + r.lexTo = mjpeg.NewExtractor().Extract } case config.InputAudio: