通过网络传输过来的视频,如何在不保存为文件的情况下,用工具提取视频帧呢?
一开始以为很容易,但是找了很久发现 Opencv 和 ffmpeg 都没有直接从内存中提取的方法,都需要通过一个临时文件才能提取。而目目前需求上需要优化处理视频的时间,所以想着修改这个步骤。
1
Doraismydora 137 天前
·sws_sacle· 把 ·AVFrame· 转成 rgb32 ,然后弄一个 ·cv::Mat· 复制过去
|
2
qsnow6 137 天前
用虚拟文件交换就行了
|
3
Latin 137 天前
网络传输中原先的格式是什么链路
|
5
SenseHu 137 天前
场景没描述清, 提取之后是被谁消费, 落盘? 其他程序用? 其他程序是什么框架
|
7
Huelse 137 天前
直接用内存映射不就行了,然后就是正常的 IO 操作
|
8
somebody1 137 天前
你能访问视频程序的内存内容,那别的程序不一样能访问,那你的内存不就成了透明的嘛!
这最基础的访问限制都闹不明白吗!!! |
9
sweelia 137 天前 1
@Haku 可以参考 https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/avio_read_callback.c ,主要是 read_packet 函数,buffer 的装载逻辑( av_file_map )换成你自己的,可以流式加载,不用一次性读完。如果对 c 不熟悉,可以找个 python binding 来使用。decode 之后拿到 AVFrame ,随便玩
|
11
stebest 137 天前
额,这个倒是跟视频没啥关系,不就是想用 bytesIO 嘛。
|
13
mightybruce 137 天前
看起来你对 opencv 和 ffmpeg 都不熟悉,先说说你传来的视频流是什么协议吧
|
14
stebest 137 天前 1
@stebest 挺简单的,给个示例吧 import numpy as np
import cv2 as cv import io image_stream = io.BytesIO() image_stream.write(connection.read(image_len)) image_stream.seek(0) file_bytes = np.asarray(bytearray(image_stream.read()), dtype=np.uint8) img = cv.imdecode(file_bytes, cv.IMREAD_COLOR) |
15
mightybruce 137 天前 2
直接读完整的视频就是 bytesIO 二进制流了, 转换一下变成 numpy array 就行
|
17
Haku OP |
18
NessajCN 137 天前
Python 这语言本来就不适用于直接对内存进行操作
建议换 C 或 Rust https://ffmpeg.org/doxygen/6.1/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c 你可能想要这个方法 |
20
Anarchy 137 天前
用 pyav 很简单的
|
21
Anarchy 137 天前 1
@Anarchy 给个例子:
with av.open(url, options=options) as container: video = container.streams.video[0] for i,t in enumerate([10,15,20,25,30]): container.seek(int(t/video.time_base)+video.start_time, backward=True, stream=video) frame = next(container.decode(video)) frame.to_image().save(target_folder/f'frame{i}.png') |
22
Haku OP @stebest
请问下,cv2 读取视频我只找到了 VideoCapture ,但是 VideoCapture 却只能读文件了。 还是说能用读图片的方式,用 imdecode 等从视频二进制流里面直接读取到图片吗? |
24
sweelia 137 天前 1
@Haku https://github.com/opencv/opencv/pull/25584 不知多久才能合入主线,有条件还是建议走 ffmpeg 的路子,控制更精细,遇到问题也有解决方案。按经验,有些推流器封装不太标准,需要野路子 hack 兼容。
|
25
stebest 137 天前
|
26
stebest 137 天前
不过实际上还是会在本地创建文件,video capture 本身不支持直接从 memory 中读取,如果是自己控制数据流,用一个迭代器或者生成器把每一帧转 cv mat 就可以
|
27
stebest 137 天前 1
如果还是希望用 opencv video capture 处理,1.可以将视频流使用虚拟摄像头输出,用 opencv 打开即可。
2.使用流媒体协议,opencv video capture 应该也直接支持。 3.使用 videogear 的 netgear ,一个比较简单的例子,具体可以去看文档 server end ``` # import required libraries from vidgear.gears import VideoGear from vidgear.gears import NetGear # open any valid video stream(for e.g `test.mp4` file) stream = VideoGear(source="test.mp4").start() # Define Netgear Server with default parameters server = NetGear() # loop over until KeyBoard Interrupted while True: try: # read frames from stream frame = stream.read() # check for frame if Nonetype if frame is None: break # {do something with the frame here} # send frame to server server.send(frame) except KeyboardInterrupt: break # safely close video stream stream.stop() # safely close server server.close() ``` client end ``` # import required libraries from vidgear.gears import NetGear import cv2 # define Netgear Client with `receive_mode = True` and default parameter client = NetGear(receive_mode=True) # loop over while True: # receive frames from network frame = client.recv() # check for received frame if Nonetype if frame is None: break # {do something with the frame here} # Show output window cv2.imshow("Output Frame", frame) # check for 'q' key if pressed key = cv2.waitKey(1) & 0xFF if key == ord("q"): break # close output window cv2.destroyAllWindows() # safely close client client.close() ``` |
28
vituralfuture 137 天前 via Android 1
楼上 bytesio 是一个方法,直接放到 tmpfs 里也行,tmpfs 的 backend 就是内存
|
29
ClericPy 136 天前 1
tmpfs 比较简单,也就是常见的 /dev/shm
|
30
SP00F 136 天前
走内存,在并发大文件的情况下。。怎么解决内存占用的问题……
走内存例如 golang 中的 gin 上传文件一样,在读取文件都是 io ,调用 ffmpeg 或者 openvc 读取文件最后不都是 io 吗(个人拙见)😳 |
31
longredzzz 136 天前 1
|
32
Lihanx9 131 天前
感觉没有那么复杂吧...
VideoCapture 本身支持使用 HTTP 协议的地址作为视频文件路径,如果没有特殊的请求限制,直接让 VideoCapture 自己处理流,然后直接遍历帧、或者设置 cv2.CAP_PROP_POS_FRAMES 或者 cv2.CAP_PROP_POS_MSEC 读指定帧都是可以的。不知道这种方式是不是符合需求。 |