package service
|
|
import (
|
"errors"
|
"fmt"
|
"time"
|
|
"basic.com/valib/godraw.git"
|
"basic.com/valib/goffmpeg.git"
|
"github.com/disintegration/imaging"
|
)
|
|
// StreamMode stream mode
|
type StreamMode int
|
|
const (
|
// Rtsp mode
|
Rtsp = StreamMode(iota)
|
// GB28181 mode
|
GB28181
|
)
|
|
// PicExt file extention
|
type PicExt string
|
|
// PNGFileExt FileExt = ".png"
|
// JPEGFileExt FileExt = ".jpg"
|
// GIFFileExt FileExt = ".gif"
|
|
const (
|
// PNGFileExt png
|
PNGFileExt PicExt = PicExt(".png")
|
// JPEGFileExt jpeg
|
JPEGFileExt PicExt = PicExt(".jpg")
|
// GIFFileExt gif
|
GIFFileExt PicExt = PicExt(".gif")
|
// InvalidFileExt invalid
|
InvalidFileExt PicExt = PicExt("")
|
)
|
|
// capture pic
|
func captureGB28181(soFile string, url string, maxTry int) ([]byte, error) {
|
|
var ret []byte
|
var err error
|
|
err = goffmpeg.InitFFmpeg(soFile)
|
if err != nil {
|
return nil, err
|
}
|
|
reterr := errors.New(fmt.Sprintf("gb28181 try %d times to capture image, is url correct?", maxTry))
|
for i := 0; i < maxTry; i++ {
|
ret = goffmpeg.GetGBJpg(url, maxTry)
|
if len(ret) > 0 {
|
reterr = nil
|
break
|
}
|
}
|
|
goffmpeg.FreeFFmpeg()
|
|
return ret, reterr
|
}
|
|
// Capture pic
|
func Capture(soFile string, m StreamMode, url string, ext PicExt, w, h, maxTry int) ([]byte, error) {
|
if m != GB28181 && m != Rtsp {
|
return nil, errors.New("there is no this mode, try capture.Rtsp/capture.GB28181")
|
}
|
|
if m == GB28181 {
|
return captureGB28181(soFile, url, maxTry)
|
}
|
|
if ext == InvalidFileExt {
|
ext = JPEGFileExt
|
}
|
var ret []byte
|
var err error
|
|
err = goffmpeg.InitFFmpeg(soFile)
|
if err != nil {
|
return nil, err
|
}
|
|
ret, err = capt2Data(url, m, ext, w, h, maxTry)
|
|
goffmpeg.FreeFFmpeg()
|
|
return ret, err
|
}
|
|
func capt2Data(url string, m StreamMode, ext PicExt, w, h, maxTry int) ([]byte, error) {
|
|
gb := false
|
if m == GB28181 {
|
gb = true
|
}
|
|
gf := goffmpeg.New(gb, false)
|
defer gf.Free()
|
|
gf.Run(url)
|
gf.BuildDecoder()
|
|
tryTime := 0
|
// 保证有一个关键帧
|
maxTry += 50
|
if maxTry > 60 {
|
maxTry = 60
|
}
|
|
var bgrData, jpgData []byte
|
var err error
|
for {
|
|
data, ow, oh, _ := gf.GetYUV()
|
if ow > 0 && oh > 0 {
|
if ow*oh*3 != len(data) {
|
// data 不是 bgr 格式,需要转换
|
bgrData = yuv2bgr(data, ow, oh)
|
} else {
|
bgrData = data
|
}
|
|
if w > 0 && h > 0 {
|
bgrData = bgresize(data, ow, oh, w, h)
|
ow, oh = w, h
|
}
|
|
jpgData, err = godraw.ToJpeg(bgrData, ow, oh, nil)
|
if err != nil {
|
continue
|
}
|
break
|
} else {
|
tryTime++
|
if tryTime > maxTry {
|
break
|
}
|
time.Sleep(800 * time.Millisecond)
|
}
|
}
|
|
if tryTime >= maxTry {
|
err := fmt.Sprintf("try %d times to capture image, is url correct?\n", tryTime)
|
return nil, errors.New(err)
|
}
|
|
return jpgData, nil
|
}
|
|
func yuv2bgr(yuv []byte, w, h int) []byte {
|
|
data := make([]byte, 0, w*h*3)
|
start := w * h
|
for i := 0; i < h; i++ {
|
for j := 0; j < w; j++ {
|
|
index := i/2*w + j - (j & 0x01)
|
|
y := int32(yuv[j+i*w])
|
v := int32(yuv[start+index])
|
u := int32(yuv[start+index+1])
|
|
r := y + (140*(v-128))/100
|
g := y - (34*(u-128)+71*(v-128))/100
|
b := y + (177*(u-128))/100
|
|
if r > 255 {
|
r = 255
|
}
|
if r < 0 {
|
r = 0
|
}
|
if g > 255 {
|
g = 255
|
}
|
if g < 0 {
|
g = 0
|
}
|
if b > 255 {
|
b = 255
|
}
|
if b < 0 {
|
b = 0
|
}
|
data = append(data, byte(r), byte(g), byte(b))
|
}
|
}
|
return data
|
}
|
|
func bgresize(bgr []byte, w, h, rw, rh int) []byte {
|
img, err := godraw.ToImage(bgr, w, h)
|
if err != nil {
|
return nil
|
}
|
dstImg := imaging.Resize(img, rw, rh, imaging.NearestNeighbor)
|
return godraw.Image2BGR(dstImg)
|
}
|