通过网络传输过来的视频,如何在不保存为文件的情况下,用工具提取视频帧呢?
一开始以为很容易,但是找了很久发现 Opencv 和 ffmpeg 都没有直接从内存中提取的方法,都需要通过一个临时文件才能提取。而目目前需求上需要优化处理视频的时间,所以想着修改这个步骤。
1
Doraismydora 2024-08-02 10:05:17 +08:00
·sws_sacle· 把 ·AVFrame· 转成 rgb32 ,然后弄一个 ·cv::Mat· 复制过去
|
![]() |
2
qsnow6 2024-08-02 10:08:35 +08:00
用虚拟文件交换就行了
|
![]() |
3
Latin 2024-08-02 10:14:13 +08:00
网络传输中原先的格式是什么链路
|
5
SenseHu 2024-08-02 10:19:32 +08:00
场景没描述清, 提取之后是被谁消费, 落盘? 其他程序用? 其他程序是什么框架
|
![]() |
6
Haku OP @SenseHu 提取之后会由 AI 进行处理,所以还会被转换成对应的图片格式保存在内存中。整个过程都在 Python 上处理。
|
7
Huelse 2024-08-02 10:23:18 +08:00
直接用内存映射不就行了,然后就是正常的 IO 操作
|
![]() |
8
somebody1 2024-08-02 10:24:34 +08:00
你能访问视频程序的内存内容,那别的程序不一样能访问,那你的内存不就成了透明的嘛!
这最基础的访问限制都闹不明白吗!!! |
9
sweelia 2024-08-02 10:27:20 +08:00 ![]() @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 ,随便玩
|
![]() |
10
Haku OP @somebody1 啥?我觉得你可能没理解,我这里指我的服务器收到了视频文件,然后我不希望把他写入到硬盘中,而是直接将传输过来的文件进行视频帧提取。
|
![]() |
11
stebest 2024-08-02 10:31:08 +08:00
额,这个倒是跟视频没啥关系,不就是想用 bytesIO 嘛。
|
![]() |
13
mightybruce 2024-08-02 10:33:18 +08:00
看起来你对 opencv 和 ffmpeg 都不熟悉,先说说你传来的视频流是什么协议吧
|
![]() |
14
stebest 2024-08-02 10:34:03 +08:00 ![]() @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 2024-08-02 10:34:45 +08:00 ![]() 直接读完整的视频就是 bytesIO 二进制流了, 转换一下变成 numpy array 就行
|
![]() |
17
Haku OP |
18
NessajCN 2024-08-02 10:40:55 +08:00
Python 这语言本来就不适用于直接对内存进行操作
建议换 C 或 Rust https://ffmpeg.org/doxygen/6.1/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c 你可能想要这个方法 |
20
Anarchy 2024-08-02 10:49:45 +08:00
用 pyav 很简单的
|
21
Anarchy 2024-08-02 10:54:00 +08:00 ![]() @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 2024-08-02 12:16:02 +08:00 ![]() @Haku https://github.com/opencv/opencv/pull/25584 不知多久才能合入主线,有条件还是建议走 ffmpeg 的路子,控制更精细,遇到问题也有解决方案。按经验,有些推流器封装不太标准,需要野路子 hack 兼容。
|
![]() |
25
stebest 2024-08-02 13:51:33 +08:00
|
![]() |
26
stebest 2024-08-02 13:55:08 +08:00
不过实际上还是会在本地创建文件,video capture 本身不支持直接从 memory 中读取,如果是自己控制数据流,用一个迭代器或者生成器把每一帧转 cv mat 就可以
|
![]() |
27
stebest 2024-08-02 14:05:10 +08:00 ![]() 如果还是希望用 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 2024-08-02 14:59:42 +08:00 via Android ![]() 楼上 bytesio 是一个方法,直接放到 tmpfs 里也行,tmpfs 的 backend 就是内存
|
![]() |
29
ClericPy 2024-08-02 19:18:45 +08:00 ![]() tmpfs 比较简单,也就是常见的 /dev/shm
|
30
SP00F 2024-08-02 23:30:31 +08:00
走内存,在并发大文件的情况下。。怎么解决内存占用的问题……
走内存例如 golang 中的 gin 上传文件一样,在读取文件都是 io ,调用 ffmpeg 或者 openvc 读取文件最后不都是 io 吗(个人拙见)😳 |
31
longredzzz 2024-08-02 23:37:46 +08:00 ![]() |
![]() |
32
Lihanx9 2024-08-08 08:51:26 +08:00
感觉没有那么复杂吧...
VideoCapture 本身支持使用 HTTP 协议的地址作为视频文件路径,如果没有特殊的请求限制,直接让 VideoCapture 自己处理流,然后直接遍历帧、或者设置 cv2.CAP_PROP_POS_FRAMES 或者 cv2.CAP_PROP_POS_MSEC 读指定帧都是可以的。不知道这种方式是不是符合需求。 |