2023-03-15:屏幕录制并且显示视频,不要用命令。代码用go语言编写。

答案2023-03-15:

使用moonfdd/ffmpeg-go和moonfdd/sdl2-go库来实现屏幕录制并显示视频,大体流程如下:

1.使用libavdevice库中的AVInputFormat(输入格式)、AVDeviceCapabilitiesQuery(设备能力查询)和AVFormatContext(格式上下文)获取桌面录制器的设备信息。
2.使用libavcodec库中的AVCodec、AVCodecContext、AVFrame和AVPacket结构体编码和解码视频帧。
3.使用libavutil库中的函数分配内存空间,并创建一个SwsContext(色彩空间转换上下文)对象,以及SDL库中的窗口和渲染器对象。
4.在循环中,读取屏幕捕获器的每一帧数据并将其解码。然后,直接将YUV420P格式的图像传递给SDL库中的渲染器进行显示。

代码调用了以下结构体:

AVInputFormat
AVDeviceCapabilitiesQuery
AVFormatContext
AVCodec
AVCodecContext
AVFrame
AVPacket
SwsContext
SDL窗口对象
SDL渲染器对象

释放资源的步骤包括:

1.关闭SDL渲染器和窗口。
2.释放AVFrame和AVPacket对象。
3.关闭libavcodec库中的AVCodecContext。
4.关闭libavformat库中的AVFormatContext。
5.关闭libavdevice库中的AVInputFormat。
6.释放libavutil库中的内存空间。

代码见github.com/moonfdd/ffmpeg-go-examples。

执行命令:

go run ./examples/leixiaohua1020/simplest_ffmpeg_grabdesktop/main.go

代码参考了雷霄骅的屏幕录制并播放显示,代码用golang编写。代码如下:

// https://github.com/leixiaohua1020/simplest_ffmpeg_device/blob/master/simplest_ffmpeg_grabdesktop/simplest_ffmpeg_grabdesktop.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()
//Windows
if USE_DSHOW != 0 {
//Use dshow
//
//Need to Install screen-capture-recorder
//screen-capture-recorder
//Website: http://sourceforge.net/projects/screencapturer/
//
ifmt = libavformat.AvFindInputFormat("dshow")
if libavformat.AvformatOpenInput(&pFormatCtx, "video=screen-capture-recorder", ifmt, nil) != 0 {
fmt.Printf("Couldn't open input stream1.\n")
return -1
}
} else {
//Use gdigrab
var options *libavutil.AVDictionary
//Set some options
//grabbing frame rate
//av_dict_set(&options,"framerate","5",0);
//The distance from the left edge of the screen or desktop
//av_dict_set(&options,"offset_x","20",0);
//The distance from the top edge of the screen or desktop
//av_dict_set(&options,"offset_y","40",0);
//Video frame size. The default is to capture the full screen
//av_dict_set(&options,"video_size","640x480",0);
// libavutil.AvDictSet(&options, "probesize", "100000000", 0)
ifmt = libavformat.AvFindInputFormat("gdigrab")
if libavformat.AvformatOpenInput(&pFormatCtx, "desktop", ifmt, &options) != 0 {
fmt.Printf("Couldn't open input stream2.\n")
return -1
} } 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
}
pCodecCtx = pFormatCtx.GetStream(uint32(videoindex)).Codec
pCodec = libavcodec.AvcodecFindDecoder(pCodecCtx.CodecId)
if pCodec == nil {
fmt.Printf("Codec not found.\n")
return -1
}
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 = mode.W / 2
screen_h = mode.H / 2
window := sdl.SDL_CreateWindow("Simplest FFmpeg Grab Desktop", 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
}
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()
window.SDL_ShowWindow()
time.Sleep(2 * time.Second) 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 = mode.W
rect2.H = mode.H //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.AvPacketUnref()
} 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-15:屏幕录制并且显示视频,不要用命令。代码用go语言编写。的更多相关文章

  1. 屏幕录制H.264视频,AAC音频,MP4复,LibRTMP现场活动

    上周完成了一个屏幕录制节目,实时屏幕捕获.记录,视频H.264压缩,音频应用AAC压缩,复用MP4格公式,这使得计算机和ios设备上直接播放.支持HTML5的播放器都能够放,这是标准格式的优点.抓屏也 ...

  2. python 实现屏幕录制

    用python实现屏幕录制 PIL 即pollow 的安装命令如下: pip install pillow 其中cv2的安装是下面这条命令 pip install opencv-python 代码实现 ...

  3. anyRTC iOS端屏幕录制开发指南

    一. 概述 实现直播过程中共享屏幕分为两个步骤:屏幕数据采集和流媒体数据推送.前对于 iOS 来说,屏幕采集需要系统的权限,受制于iOS系统的限制,第三方 app 并没有直接录制屏幕的权限,必须通过系 ...

  4. Android视频录制从不入门到入门系列教程(二)————显示视频图像

    1.创建一个空的工程,注意声明下列权限: <uses-permission android:name="android.permission.CAMERA"/> < ...

  5. Mac与iPhone屏幕录制

    1. Mac电脑屏幕录制 1.1 文件->新建屏幕录制   1.2 点击红色按钮   1.3 截取需要录制的屏幕部分,点击开始录制   1.4 点击工具栏的停止按钮,停止录制   1.5 然后会 ...

  6. 【录教程必备】推荐几款屏幕录制工具(可录制GIF)

    我们经常会遇到一些场景,需要你向别人展示一些操作或是效果——例如告诉别人某某软件的配置步骤啊.刚设计出来网站的动画效果怎么样啊.某某电影里面的一个镜头多么经典啊.打得大快人心的NBA绝杀瞬间是怎么回事 ...

  7. Mac - iPhone屏幕录制

    1. Mac电脑屏幕录制 1.1 文件->新建屏幕录制   1.2 点击红色按钮   1.3 截取需要录制的屏幕部分,点击开始录制   1.4 点击工具栏的停止按钮,停止录制   1.5 然后会 ...

  8. 推荐几款屏幕录制工具(可录制GIF)

    我们经常会遇到一些场景,需要你向别人展示一些操作或是效果——例如告诉别人某某软件的配置步骤啊.刚设计出来网站的动画效果怎么样啊.某某电影里面的一个镜头多么经典啊.打得大快人心的NBA绝杀瞬间是怎么回事 ...

  9. 【转】【录教程必备】推荐几款屏幕录制工具(可录制GIF)

    我们经常会遇到一些场景,需要你向别人展示一些操作或是效果——例如告诉别人某某软件的配置步骤啊.刚设计出来网站的动画效果怎么样啊.某某电影里面的一个镜头多么经典啊.打得大快人心的NBA绝杀瞬间是怎么回事 ...

  10. 【实用软件】GIF屏幕录制软件-ScreenToGif (在GitHub开源)

    抛个问题,自问自答 ScreenToGif 经常会遇到一些场景,需要你向别人展示一些操作或是效果——例如告诉别人某某软件的配置步骤啊.刚某个动画效果怎么样啊.某某电影里面的一个镜头多么经典啊.打得大快 ...

随机推荐

  1. Go_day03

    Go基础语法 数组 数组是具有相同唯一类型的一组以编号且长度固定的数据项序列.类型可以是任意基本类型或者自定义类型. 数组一旦被定义后,大小不能被改变 func main() { //定义一个数组 v ...

  2. Navicat Premium 16 永久破解激活

    Navicat premium是一款数据库管理工具,是一个可多重连线资料库的管理工具,它可以让你以单一程式同时连线到 MySQL.SQLite.Oracle 及 PostgreSQL 资料库,让管理不 ...

  3. https传输流程(加密方式、证书、传输安全)

    http的缺点 http的数据是明文传输 如果用明文传输 很容易被第三方获取到传输的数据 因此我们一般要在网络传输过程中对数据进行加密 常见的加密方式 对称加密 秘钥key 待加密数据data a和b ...

  4. Cmakelist如何添加自己的组件

    在components文件夹下添加各组件的CMakeList,其中可以设置的变量如下: COMPONENT_SRCS:要编译进当前组件的源文件的路径,推荐使用此方法向构建系统中添加源文件.COMPON ...

  5. Cisco Packet Tracer(思科模拟器)安装,注册用户

    下载 下载地址见湖南科技大学老师:http://mooc1.xueyinonline.com/nodedetailcontroller/visitnodedetail?courseId=2226402 ...

  6. Git 仓库7K stars!学Java开源项目austin要多久?

    我是3y,一年CRUD经验用十年的markdown程序员‍常年被誉为职业八股文选手 开源项目消息推送平台austin仓库地址: 消息推送平台推送下发[邮件][短信][微信服务号][微信小程序][企业微 ...

  7. .Net7 GC标记阶段代码的改变

    前言 由于业务需求,在探究.Net7的CLR,发现了一个不通的地方,也就是通过GCInfo获取到了对象之后.它并没有在GcScanRoots(对象扫描标记)里面对它进行标记,那么如果没有标记这个对象如 ...

  8. 【WEB UI自动化】Selenium爬取用户的知乎回答时遇到10002:请求参数异常,请升级客户端后重试

    这里分享一个低配版知乎爬虫,利用了Selenium模块 爬取的过程中遇到了10002:请求参数异常,请升级客户端后重试,调用知乎某用户的回答API返回的HTTP状态码是403 Forbidden 之后 ...

  9. Mathematica做数字图像处理中的ImageConvolve练习

    目录 ImageConvolve练习 original right sobel 垂直边检测 top sobel 水平边检测 通过一个平方内核的卷积使图像变平滑 类似ConstantArray[1, { ...

  10. 可靠消息最终一致性【本地消息表、RocketMQ 事务消息方案】

    更多内容,前往IT-BLOG 一.可靠消息最终一致性事务概述 可靠消息最终一致性方案是指当事务发起方执行完成本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调 ...