2023-03-14:读取摄像头,并且显示视频。代码用go语言编写。
2023-03-14:读取摄像头,并且显示视频。代码用go语言编写。
答案2023-03-14:
大体流程如下:
导入所需的库和包。
初始化 ffmpeg 和 SDL2 库。
打开摄像头并创建 AVFormatContext 结构体。
查找视频流,并且打开视频解码器。
创建 SDL 窗口,用于显示视频。
创建 AVFrame 结构体,用于存储解码后的视频帧数据。
创建 AVPacket 结构体,用于存储编码后的视频数据。
创建 SwsContext 结构体,用于转换视频帧格式。
不断从视频流读取 AVPacket,直到读完所有帧。
解码 AVPacket 中的视频数据,将其存储在 AVFrame 中。
将 AVFrame 中的数据转换为适合 SDL 窗口显示的格式。
显示转换后的图像帧。
释放所有资源,包括关闭 SDL 窗口、释放 AVFrame 和 AVPacket、删除 SwsContext 结构体、关闭解码器、关闭视频流、关闭摄像头。
需要注意的是,在实际使用中可能会遇到各种问题,例如视频格式不支持、分辨率不匹配等。因此,我们需要根据具体情况来进行相应的调整和处理,以确保程序能够正常运行。
这段代码调用了以下结构体:
AVFormatContext:表示 FFmpeg 格式上下文,包含视频文件的信息和元数据。
AVCodecContext:表示视频编解码器上下文,用于配置和控制编解码器。
AVPacket:表示一个压缩的音频或视频数据包,包含了一帧或多帧音频或视频数据。
AVFrame:表示一个解码后的视频帧,存储原始像素数据以及相关的元数据。
SwsContext:表示视频帧转换器上下文,用于将解码后的视频帧从一种格式转换为另一种格式。
SDL_Window:表示 SDL 窗口,用于显示视频图像。
SDL_Renderer:表示 SDL 渲染器,用于将视频帧渲染到 SDL 窗口中。
这些结构体是实现视频播放所必需的重要组件。它们之间的交互、设置和使用都可以影响视频播放的效果和性能,因此需要仔细地处理和配置。同时,在释放资源时,需要确保所有相关的结构体被正确销毁,以避免内存泄漏和其他问题。
代码见github.com/moonfdd/ffmpeg-go-examples。
执行命令:
go run ./examples/leixiaohua1020/simplest_ffmpeg_readcamera/main.go
代码参考了雷霄骅的本地摄像头数据的获取解码和显示,代码用golang编写。代码如下:
// https://github.com/leixiaohua1020/simplest_ffmpeg_device/blob/master/simplest_ffmpeg_readcamera/simplest_ffmpeg_readcamera.cpp
package main
import (
"fmt"
"os"
"time"
"unsafe"
"github.com/moonfdd/ffmpeg-go/ffcommon"
"github.com/moonfdd/ffmpeg-go/libavcodec"
"github.com/moonfdd/ffmpeg-go/libavdevice"
"github.com/moonfdd/ffmpeg-go/libavformat"
"github.com/moonfdd/ffmpeg-go/libavutil"
"github.com/moonfdd/ffmpeg-go/libswscale"
sdl "github.com/moonfdd/sdl2-go/sdl2"
"github.com/moonfdd/sdl2-go/sdlcommon"
)
// Output YUV420P
const OUTPUT_YUV420P = 0
// '1' Use Dshow
// '0' Use GDIgrab
const USE_DSHOW = 0
// Refresh Event
const SFM_REFRESH_EVENT = (sdl.SDL_USEREVENT + 1)
const SFM_BREAK_EVENT = (sdl.SDL_USEREVENT + 2)
var thread_exit ffcommon.FInt = 0
var ispush = true
func sfp_refresh_thread(opaque ffcommon.FVoidP) uintptr {
// thread_exit = 0
for thread_exit == 0 {
var event sdl.SDL_Event
event.Type = SFM_REFRESH_EVENT
if ispush {
event.SDL_PushEvent()
ispush = false
}
sdl.SDL_Delay(40)
}
fmt.Println("sfp_refresh_thread 发送退出事件")
// thread_exit = 0
//Break
var event sdl.SDL_Event
event.Type = SFM_BREAK_EVENT
event.SDL_PushEvent()
return 0
}
// Show Dshow Device
func show_dshow_device() {
pFormatCtx := libavformat.AvformatAllocContext()
var options *libavutil.AVDictionary
libavutil.AvDictSet(&options, "list_devices", "true", 0)
iformat := libavformat.AvFindInputFormat("dshow")
fmt.Printf("========Device Info=============\n")
libavformat.AvformatOpenInput(&pFormatCtx, "video=dummy", iformat, &options)
fmt.Printf("================================\n")
}
// Show AVFoundation Device
func show_avfoundation_device() {
pFormatCtx := libavformat.AvformatAllocContext()
var options *libavutil.AVDictionary
libavutil.AvDictSet(&options, "list_devices", "true", 0)
iformat := libavformat.AvFindInputFormat("avfoundation")
fmt.Printf("==AVFoundation Device Info===\n")
libavformat.AvformatOpenInput(&pFormatCtx, "", iformat, &options)
fmt.Printf("=============================\n")
}
func main0() (ret ffcommon.FInt) {
var pFormatCtx *libavformat.AVFormatContext
var i, videoindex ffcommon.FInt
var pCodecCtx *libavcodec.AVCodecContext
var pCodec *libavcodec.AVCodec
var ifmt *libavformat.AVInputFormat
libavformat.AvRegisterAll()
libavformat.AvformatNetworkInit()
pFormatCtx = libavformat.AvformatAllocContext()
//Open File
//char filepath[]="src01_480x272_22.h265";
//avformat_open_input(&pFormatCtx,filepath,NULL,NULL)
//Register Device
libavdevice.AvdeviceRegisterAll()
/解码器部分//
//打开摄像头
ifmt = libavformat.AvFindInputFormat("dshow")
var options *libavutil.AVDictionary
// libavutil.AvDictSet(&options, "probesize", "100000000", 0)
// libavutil.AvDictSet(&options, "rtbufsize", "100000000", 0)
if libavformat.AvformatOpenInput(&pFormatCtx, "video=Full HD webcam", ifmt, &options) < 0 {
fmt.Printf("Cannot open camera.\n")
return
}
if pFormatCtx.AvformatFindStreamInfo(nil) < 0 {
fmt.Println("Couldn't find stream information.")
return -1
}
videoindex = -1
for i = 0; i < int32(pFormatCtx.NbStreams); i++ {
if pFormatCtx.GetStream(uint32(i)).Codec.CodecType == libavutil.AVMEDIA_TYPE_VIDEO {
videoindex = i
break
}
}
if videoindex == -1 {
fmt.Printf("Didn't find a video stream.\n")
return -1
}
pCodecCtxPara := pFormatCtx.GetStream(uint32(videoindex)).Codecpar
pCodec = libavcodec.AvcodecFindDecoder(pCodecCtxPara.CodecId)
if pCodec == nil {
fmt.Printf("Codec not found.\n")
return -1
}
pCodecCtx = pCodec.AvcodecAllocContext3()
if pCodecCtx == nil {
fmt.Printf("Cannot alloc valid decode codec context.\n")
return
}
if pCodecCtx.AvcodecParametersToContext(pCodecCtxPara) < 0 {
fmt.Printf("Cannot initialize parameters.\n")
return
}
if pCodecCtx.AvcodecOpen2(pCodec, nil) < 0 {
fmt.Printf("Could not open codec.\n")
return -1
}
var pFrame, pFrameYUV *libavutil.AVFrame
pFrame = libavutil.AvFrameAlloc()
pFrameYUV = libavutil.AvFrameAlloc()
//unsigned char *out_buffer=(unsigned char *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
//avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
out_buffer := (*byte)(unsafe.Pointer(libavutil.AvMalloc(uint64(libavcodec.AvpictureGetSize(int32(libavutil.AV_PIX_FMT_YUV420P), pCodecCtx.Width, pCodecCtx.Height)))))
((*libavcodec.AVPicture)(unsafe.Pointer(pFrameYUV))).AvpictureFill(out_buffer, libavutil.AV_PIX_FMT_YUV420P, pCodecCtx.Width, pCodecCtx.Height)
//SDL----------------------------
// if sdl.SDL_Init(sdl.SDL_INIT_VIDEO|sdl.SDL_INIT_AUDIO|sdl.SDL_INIT_TIMER) != 0 {
if sdl.SDL_Init(sdl.SDL_INIT_VIDEO) != 0 {
fmt.Printf("Could not initialize SDL - %s\n", sdl.SDL_GetError())
return -1
}
var screen_w, screen_h ffcommon.FInt = 640, 360
// var mode *sdl.SDL_DisplayMode = new(sdl.SDL_DisplayMode)
// if sdl.SDL_GetCurrentDisplayMode(0, mode) != 0 {
// fmt.Printf("SDL: could not get current display mode - exiting:%s\n", sdl.SDL_GetError())
// return -1
// }
//Half of the Desktop's width and height.
screen_w = pCodecCtx.Width
screen_h = pCodecCtx.Height
window := sdl.SDL_CreateWindow("Simplest FFmpeg Read Camera", sdl.SDL_WINDOWPOS_UNDEFINED, sdl.SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, 0)
if window == nil {
fmt.Printf("SDL: could not create window - exiting:%s\n", sdl.SDL_GetError())
return -1
}
window.SDL_ShowWindow()
time.Sleep(2 * time.Second)
defer window.SDL_DestroyWindow()
renderer := window.SDL_CreateRenderer(-1, 0)
if renderer == nil {
fmt.Printf("SDL: could not create renderer - exiting:%s\n", sdl.SDL_GetError())
return -1
}
defer renderer.SDL_DestroyRenderer()
texture := renderer.SDL_CreateTexture(sdl.SDL_PIXELFORMAT_YV12,
sdl.SDL_TEXTUREACCESS_STREAMING,
pCodecCtx.Width,
pCodecCtx.Height)
defer texture.SDL_DestroyTexture()
var rect sdl.SDL_Rect
rect.X = 0
rect.Y = 0
rect.W = screen_w
rect.H = screen_h
var rect2 sdl.SDL_Rect
rect2.X = 0
rect2.Y = 0
rect2.W = pCodecCtx.Width
rect2.H = pCodecCtx.Height
//SDL End------------------------
// var got_picture ffcommon.FInt
//AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
packet := &libavcodec.AVPacket{}
var fp_yuv *os.File
if OUTPUT_YUV420P != 0 {
fp_yuv, _ = os.Create("output.yuv")
}
var img_convert_ctx *libswscale.SwsContext
img_convert_ctx = libswscale.SwsGetContext(pCodecCtx.Width, pCodecCtx.Height, pCodecCtx.PixFmt, pCodecCtx.Width, pCodecCtx.Height, libavutil.AV_PIX_FMT_YUV420P, libswscale.SWS_BICUBIC, nil, nil, nil)
//------------------------------
//video_tid := sdl.SDL_CreateThread(sfp_refresh_thread, nil)
//
go sfp_refresh_thread(uintptr(0))
//sdl.SDL_CreateThread(sfp_refresh_thread, "", uintptr(0))
//Event Loop
var event sdl.SDL_Event
for {
//Wait
ispush = true
event.SDL_WaitEvent()
if event.Type == SFM_REFRESH_EVENT {
//------------------------------
if pFormatCtx.AvReadFrame(packet) >= 0 {
if int32(packet.StreamIndex) == videoindex {
if pCodecCtx.AvcodecSendPacket(packet) < 0 {
packet.AvPacketUnref()
continue
}
ret = pCodecCtx.AvcodecReceiveFrame(pFrame)
if ret < 0 {
fmt.Printf("Decode Error.\n")
return -1
}
if ret >= 0 {
// if got_picture != 0 {
img_convert_ctx.SwsScale((**byte)(unsafe.Pointer(&pFrame.Data)), (*int32)(unsafe.Pointer(&pFrame.Linesize)), 0, uint32(pCodecCtx.Height), (**byte)(unsafe.Pointer(&pFrameYUV.Data)), (*int32)(unsafe.Pointer(&pFrameYUV.Linesize)))
if OUTPUT_YUV420P != 0 {
y_size := pCodecCtx.Width * pCodecCtx.Height
fp_yuv.Write(ffcommon.ByteSliceFromByteP(pFrameYUV.Data[0], int(y_size))) //Y
fp_yuv.Write(ffcommon.ByteSliceFromByteP(pFrameYUV.Data[1], int(y_size)/4)) //U
fp_yuv.Write(ffcommon.ByteSliceFromByteP(pFrameYUV.Data[2], int(y_size)/4)) //V
}
texture.SDL_UpdateYUVTexture(&rect2,
pFrameYUV.Data[0], pFrameYUV.Linesize[0],
pFrameYUV.Data[1], pFrameYUV.Linesize[1],
pFrameYUV.Data[2], pFrameYUV.Linesize[2])
renderer.SDL_RenderClear()
renderer.SDL_RenderCopy(texture, nil, &rect)
renderer.SDL_RenderPresent()
}
}
packet.AvFreePacket()
} else {
//Exit Thread
thread_exit = 1
fmt.Println("main 准备退出 1")
}
} else if event.Type == sdl.SDL_QUIT {
thread_exit = 1
fmt.Println("main 准备退出 2")
} else if event.Type == SFM_BREAK_EVENT {
fmt.Println("退出循环 3")
break
}
}
img_convert_ctx.SwsFreeContext()
if OUTPUT_YUV420P != 0 {
fp_yuv.Close()
}
sdl.SDL_Quit()
libavutil.AvFree(uintptr(unsafe.Pointer(out_buffer)))
libavutil.AvFree(uintptr(unsafe.Pointer(pFrame)))
libavutil.AvFree(uintptr(unsafe.Pointer(pFrameYUV)))
pCodecCtx.AvcodecClose()
libavformat.AvformatCloseInput(&pFormatCtx)
return 0
}
func main() {
os.Setenv("Path", os.Getenv("Path")+";./lib/windows/ffmpeg")
ffcommon.SetAvutilPath("./lib/windows/ffmpeg/avutil-56.dll")
ffcommon.SetAvcodecPath("./lib/windows/ffmpeg/avcodec-58.dll")
ffcommon.SetAvdevicePath("./lib/windows/ffmpeg/avdevice-58.dll")
ffcommon.SetAvfilterPath("./lib/windows/ffmpeg/avfilter-56.dll")
ffcommon.SetAvformatPath("./lib/windows/ffmpeg/avformat-58.dll")
ffcommon.SetAvpostprocPath("./lib/windows/ffmpeg/postproc-55.dll")
ffcommon.SetAvswresamplePath("./lib/windows/ffmpeg/swresample-3.dll")
ffcommon.SetAvswscalePath("./lib/windows/ffmpeg/swscale-5.dll")
sdlcommon.SetSDL2Path("./lib/windows/sdl/SDL2.0.16.dll")
genDir := "./out"
_, err := os.Stat(genDir)
if err != nil {
if os.IsNotExist(err) {
os.Mkdir(genDir, 0777) // Everyone can read write and execute
}
}
// go func() {
// time.Sleep(1000)
// exec.Command("./lib/ffplay.exe", "rtmp://localhost/publishlive/livestream").Output()
// if err != nil {
// fmt.Println("play err = ", err)
// }
// }()
main0()
}
2023-03-14:读取摄像头,并且显示视频。代码用go语言编写。的更多相关文章
- iNeuOS工业互联平台,WEB组态(iNeuView)集成rtmp和websocket视频元件,支持海康、大华等摄像头实时显示视频
目 录 1. 概述... 1 2. 平台演示... 2 3. 硬件摄像头... 2 4. 视频流协议转换管理... 2 5. 组态视频元件 ...
- Opencv 播放mp4文件和读取摄像头图以及可能会发生的一些异常问题解决方法
学习内容 学习Opencv 读取并播放本地视频和打开摄像头图像以及可能会发生的一些异常问题解决方法 代码演示 电脑环境信息: OpenCV版本:4.5.2 ,vs2017 1.视频文件读取与播放 加载 ...
- opencv学习之路(2)、读取视频,读取摄像头
一.介绍 视频读取本质上就是读取图像,因为视频是由一帧一帧图像组成的.1秒24帧基本就能流畅的读取视频了. ①读取视频有两种方法: A. VideoCapture cap; cap.open(“1.a ...
- javacv——读取摄像头的图像、截取视频的画面
javacv开发包是用于支持java多媒体开发的一套开发包,可以适用于本地多媒体(音视频)调用以及音视频,图片等文件后期操作(图片修改,音视频解码剪辑等等功能). 这些需要引入的包.音视频处理使用ff ...
- OpenCV-Python 读取显示视频 | 六
目标 学习读取视频,显示视频和保存视频. 学习从相机捕捉并显示它. 你将学习以下功能:cv.VideoCapture(),cv.VideoWriter() 从相机中读取视频 通常情况下,我们必须用摄像 ...
- 使用opencv显示视频的方法
下面对使用opencv显示视频做一个简单的记录.当然,网上这方面的资料已经数不胜数了,我只是将其简单记录,总结一下. 在opencv中显示视频主要有: (1)从本地读取视频和调用摄像头读取视频 (2) ...
- ffmpeg显示视频
项目最近需要实现播放视频功能,这个在上家公司就做过.虽然跟之前的场景不一样,有以前的功底还是很快可以解决,事实也确实如此.在使用DShow处理完视频分割与合并后,继续使用DShow显示视频,很快即完成 ...
- 最简单的基于FFmpeg的AVDevice例子(读取摄像头)
=====================================================最简单的基于FFmpeg的AVDevice例子文章列表: 最简单的基于FFmpeg的AVDev ...
- 基于FPGA摄像头图像采集显示系统
本系统主要由FPGA主控模块.图像采集模块.图像存储模块以及图像显示模块等模块组成.其中图像采集模块选择OV7670摄像头模块,完成对视频图像的采集和解码功能,并以RGB565标准输出RGB 5:6: ...
- [转载] 最简单的基于FFmpeg的AVDevice例子(读取摄像头)
=====================================================最简单的基于FFmpeg的AVDevice例子文章列表: 最简单的基于FFmpeg的AVDev ...
随机推荐
- python实例1(石头 剪刀 布)
#random .randint 模块导入 import random #定义一个用户需要输入的数据内容入口 user = int(input("请输入(石头1,剪刀2,布3 ...
- Serverless 遇到 FinOps: Economical Serverless
Serverless 遇到 FinOps: Economical Serverless 摘要:本文基于 FunctionGraph 在 Serverless 领域的 FinOps 探索和实践,提出业界 ...
- Caused by: java.lang.ClassNotFoundException: Class org.openx.data.jsonserde.JsonSerDe not found
Caused by: java.lang.ClassNotFoundException: Class org.openx.data.jsonserde.JsonSerDe not found 解决方法 ...
- MYSQL 变更账号密码
#1 首先找到Mysql[安装的路径],切换到对应的bin目录,例如安装在D盘 C:User\Administrator> d:(输入盘符回车) D:\> cd D:\MySQL\MySQ ...
- vue 纵向滑动模块
代码 <template> <div> <!-- 左侧的滑动模块 --> <div class="scroll-box" :style=& ...
- 归一化,正则化,标准化,dropout的联系与区别
归一化(Normalization)是指将数据缩放到一定范围内,一般是将数据映射到[0,1]或[-1,1]之间.这样可以避免不同特征的数值差异过大而导致的训练困难,同时也有助于加快梯度下降的收敛速度. ...
- 关于Android开发工具的下载之SDK篇
SDK的下载 需要注意的是,如果我们使用的是Eciplise工具的话,那我们需要下载版本较低的android SDK tools, 在这里把下载链接放在这里啦:https://link.csdn.ne ...
- python代码换行问题(用‘\’来达成)
在 Python 中,可以使用反斜杠符号 '\' 来表示代码换行,从而实现在代码中进行换行,并保持语句的完整性.例如:. # 使用反斜杠符号进行换行 result = 10 + \ 20 + \ 30 ...
- 除select外查询数据的另一种姿势
1.24 1.[GYCTF2020]Blacklist buuctf上的题目 1.解题过程 输入1会返回一个数组,加上单引号就报错了,说明存在注入 以前做过类似的估计是堆叠注入,尝试一下 注入成功 正 ...
- GRU简介
一.GRU介绍 GRU是LSTM网络的一种效果很好的变体,它较LSTM网络的结构更加简单,而且效果也很好,因此也是当前非常流形的一种网络.GRU既然是LSTM的变体,因此也是可以解决RNN网络中的长依 ...