我试图从trafficview.org中抓取视频帧,但似乎找不出如何解码这些数据。
我根据这个客户端上的教程编写了几行代码,以访问一个直播的websocket并直接接收消息。
我已经监控了通过Chrome上的网络选项卡输入的消息,还挖掘了下面代码的输出,并且相当肯定数据正在以一个零碎的MP4的形式流进来。下面是大约100个字节/消息:
b'\xfa\x00\x02\x86\xf1B\xc0\x1e\x00\x00\x00\x18ftypiso5\x00\x00\x02\x00iso6mp41\x00\x00\x02jmoov\x00\x00\x00lmvhd\x00\x00\x00\x00\xdb\x7f\xeb\xb2\xdb\x7f\xeb\xb2\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00‘
在整个输出过程中,有大量的moof和mdat对。假设我让这些代码运行30秒,如何将这个原始字节字符串转换为mp4文件?
import json
from websocket import create_connection
url = 'wss://cctv.trafficview.org:8420/DDOT_CAPTOP_13.vod?progressive'
headers = json.dumps({
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9',
'Cache-Control': 'no-cache',
'Connection': 'Upgrade',
'Host': 'cctv.trafficview.org:8420',
'Origin': 'https://trafficview.org',
'Pragma': 'no-cache',
'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
'Sec-WebSocket-Key': 'FzWbrsoHFsJWzvWGJ04ffw==',
'Sec-WebSocket-Version': '13',
'Upgrade': 'websocket',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36',
})
ws = create_connection(url, headers=headers)
# Then send a message through the tunnel
ws.send('ping')
# Here you will view the message return from the tunnel
flag = 3000
output = b''
while flag > 0:
output += ws.recv()
flag -= 1更新:我已经对堆栈溢出的一些代码进行了修改,使之能够在fmp4数据中使用管道,并将其转换为框架。为了达到这个目的,我注意到websocket输出的前16个字节与我检查过的其他mp4文件不一致。所以我首先修剪前16个字节。我也不知道其中一个文件应该如何结束,所以我削减到文件的最后一个moof。
下面的代码可以很好地读取mp4头(也在下面),但是无法解码任何字节。
output = output[8:]
import re
moof_locs = [m.start() for m in re.finditer(b'moof', output)]
output = output[:moof_locs[-1]-1]
import subprocess as sp
import shlex
width, height = 640, 480
# FFmpeg input PIPE: WebM encoded data as stream of bytes.
# FFmpeg output PIPE: decoded video frames in BGR format.
process = sp.Popen(shlex.split('/usr/bin/ffmpeg -i pipe: -f hls -hls_segment_type fmp4 -c h264 -an -sn pipe:'), stdin=sp.PIPE, stdout=sp.PIPE, bufsize=10**8)
process.stdin.write(output)
process.stdin.close()
in_bytes = process.stdout.read(width * height * 3)
in_frame = (np.frombuffer(in_bytes, np.uint8).reshape([height, width, 3]))来自ffmpeg的产出:
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x994600] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none, 640x480): unspecified pixel format
Consider increasing the value for the 'analyzeduration' and 'probesize' options
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'pipe:':
Metadata:
major_brand : iso5
minor_version : 512
compatible_brands: iso6mp41
creation_time : 2020-09-11T13:40:21.000000Z
Duration: N/A, bitrate: N/A
Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none, 640x480, 1k tbr, 1k tbn, 2k tbc (default)
Metadata:
creation_time : 2020-09-11T13:40:21.000000Z
encoder : EvoStream Media Server
Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Finishing stream 0:0 without any data written to it.
Nothing was written into output file 0 (pipe:), because at least one of its streams received no packets.
frame= 0 fps=0.0 q=0.0 Lsize= 0kB time=-577014:32:22.77 bitrate= -0.0kbits/s speed=N/A
video:0kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
Output file is empty, nothing was encoded (check -ss / -t / -frames parameters if used)更新2:
在检查来自websocket的流时,我意识到每条消息都以一个特定的整数开头,该整数是在流量视图的javascript代码中定义的。这些代码的顺序总是相同的,它们的顺序如下:
Header MOOV (250)
PBT Begin (249)
Video Buffer (252)
Header MOOF (251)
Header MOOF (251)
Header MOOF (251)
Header MDAT (254)
PBT End (255)
PBT Begin (249)
Continues Forever其中一些标记总是相同的,例如249个消息总是f900 0000,而255个消息总是ff00 0000。
我猜想249和255条消息通常不在一个分段的mp4或hls流中,因此我认为我需要使用这些标记信息从零开始构建正确的文件格式。
发布于 2020-09-15 22:07:48
ws = create_connection(url, headers=headers)
# Then send a message through the tunnel
ws.send('ping')
start = timeit.default_timer()
flag = True
output = []
while flag:
output.append(ws.recv())
if timeit.default_timer() - start > 90:
flag = False
result = output[0][8:]
for msg in output[1:]:
if msg[0] == 249:
moofmdat = b''
moof = b''
continue
if msg[0] == 252:
vidbuf = msg[4:]
if msg[0] == 251:
moof += msg[4:]
if msg[0] == 254:
mdat = msg[4:]
if msg[0] == 255:
moofmdat += moof
moofmdat += mdat
moofmdat += vidbuf
result += moofmdat
with open('test.mp4', 'wb') as file:
file.write(result)弄明白了。MOOV报头有8个字节的不必要信息,必须删除。每个附加消息(除了PBT_Begin和PBT_End)都有4个字节的特定于播放器的数据。只需要清理每一条信息,并按正确的顺序放置。然后将原始字节保存为mp4和vlc中播放的视频。
https://stackoverflow.com/questions/63819924
复制相似问题