欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

go(goav) 中使用 ffmpeg 获取摄像头视频流,并转换成图片,发送给前端界面实时展示

最编程 2024-01-08 10:26:47
...
package api import ( "bytes" "encoding/base64" "fmt" "github.com/gin-gonic/gin" "github.com/giorgisio/goav/avcodec" "github.com/giorgisio/goav/avdevice" "github.com/giorgisio/goav/avformat" "github.com/giorgisio/goav/avutil" "github.com/giorgisio/goav/swscale" "github.com/gorilla/websocket" "image" "image/color" "image/jpeg" "net/http" "unsafe" ) type wsParam struct { Name string } // 用于测试实时读取摄像头的内容,并发送 base64 给界面 func ConnectWs(c *gin.Context) { var param wsParam if err := c.Bind(&param); err != nil { c.String(http.StatusBadRequest, err.Error()) return } var wsUpgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true }, // disable origin check } conn, err := wsUpgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { c.String(http.StatusInternalServerError, err.Error()) return } defer conn.Close() go tSendPic(conn) _, _ , err = conn.ReadMessage() if err != nil { fmt.Println(err) } } func tSendPic(conn *websocket.Conn) { avdevice.AvdeviceRegisterAll() avcodec.AvcodecRegisterAll() pFormatCtx := avformat.AvformatAllocContext() defer pFormatCtx.AvformatCloseInput() inputFmt := avformat.AvFindInputFormat("dshow") // 注意这里的第二个参数,需要改成自己的摄像头名称 if ret := avformat.AvformatOpenInput(&pFormatCtx, "video=USB2.0 PC CAMERA", inputFmt, nil); ret != 0 { fmt.Println("Unable to open video USB2.0 PC CAMERA") return } if pFormatCtx.AvformatFindStreamInfo(nil) < 0 { fmt.Println("Unable to find stream") return } videoindex := -1 nbStreams := int(pFormatCtx.NbStreams()) for i := 0; i < nbStreams; i++ { streams := pFormatCtx.Streams() if streams[i].Codec().GetCodecType() == avformat.AVMEDIA_TYPE_VIDEO { videoindex = i break } } if videoindex == -1 { fmt.Printf("Couldn't find a video stream.\n") return } pCodecCtxOrig := pFormatCtx.Streams()[videoindex].Codec() defer (*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig)).AvcodecClose() pCodec := avcodec.AvcodecFindDecoder(avcodec.CodecId(pCodecCtxOrig.GetCodecId())) if pCodec == nil { fmt.Println("没有找到解码器") return } pCodecCtx := pCodec.AvcodecAllocContext3() defer pCodecCtx.AvcodecClose() if pCodecCtx.AvcodecCopyContext((*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig))) != 0 { fmt.Println("Couldn't copy codec context") return } // Open codec if pCodecCtx.AvcodecOpen2(pCodec, nil) < 0 { fmt.Println("Could not open codec") return } pFrame := avutil.AvFrameAlloc() if pFrame == nil { fmt.Println("Unable to allocate Frame") return } defer avutil.AvFrameFree(pFrame) pFrameRGB := avutil.AvFrameAlloc() if pFrameRGB == nil { fmt.Println("Unable to allocate RGB Frame") return } defer avutil.AvFrameFree(pFrameRGB) width := pCodecCtx.Width() height := pCodecCtx.Height() img_convert_ctx := swscale.SwsGetcontext( width, height, (swscale.PixelFormat)(pCodecCtx.PixFmt()), width, height, avcodec.AV_PIX_FMT_RGB24, avcodec.SWS_BILINEAR, nil, nil, nil, ) numBytes := uintptr(avcodec.AvpictureGetSize(avcodec.AV_PIX_FMT_RGB24, width, height)) buffer := avutil.AvMalloc(numBytes) defer avutil.AvFree(buffer) avp := (*avcodec.Picture)(unsafe.Pointer(pFrameRGB)) avp.AvpictureFill((*uint8)(buffer), avcodec.AV_PIX_FMT_RGB24, width, height) packet := avcodec.AvPacketAlloc() defer packet.AvFreePacket() for pFormatCtx.AvReadFrame(packet) >= 0 { if packet.StreamIndex() != videoindex { continue } var got_picture int if ret := pCodecCtx.AvcodecDecodeVideo2((*avcodec.Frame)(unsafe.Pointer(pFrame)), &got_picture, packet); ret < 0 { fmt.Println("Decode Error") return } if got_picture > 0 { swscale.SwsScale2(img_convert_ctx, avutil.Data(pFrame), avutil.Linesize(pFrame), 0, height, avutil.Data(pFrameRGB), avutil.Linesize(pFrameRGB)) // 直接存储 // 转成图片并传给界面 if buf, err := getJpegStreams(pFrameRGB, width, height); err == nil { if er := conn.WriteMessage(websocket.TextMessage, buf); er != nil { fmt.Println("write err: ", er) return } } } else { fmt.Println("got_picture err: ", got_picture) } } fmt.Println("exit video!!!") } func getJpegStreams(frame *avutil.Frame, width, height int) ([]byte, error) { img := image.NewRGBA(image.Rect(0, 0, width, height)) for y := 0; y < height; y++ { data0 := avutil.Data(frame)[0] startPos := uintptr(unsafe.Pointer(data0)) + uintptr(y)*uintptr(avutil.Linesize(frame)[0]) //fmt.Println("startPos: ", startPos) xxx := width * 3 for x := 0; x < width; x++ { var pixel = make([]byte, 3) for i := 0; i < 3; i++ { element := *(*uint8)(unsafe.Pointer(startPos + uintptr(xxx))) pixel[i] = element xxx++ } img.SetRGBA(x, y, color.RGBA{pixel[0], pixel[1], pixel[2], 0xff}) } } var b []byte buffer := bytes.NewBuffer(b) err := jpeg.Encode(buffer, img, nil) if err != nil { fmt.Println("jpeg.Encode err: ", err) return nil, err } dst := make([]byte, base64.StdEncoding.EncodedLen(buffer.Len())) base64.StdEncoding.Encode(dst, buffer.Bytes()) return dst, nil }