#python #web-scraping #websocket #java-websocket #fmp4
#python #очистка веб-страниц #websocket #java-websocket #fmp4
Вопрос:
Я пытаюсь соскрести видеокадры с trafficview.org и, похоже, не может понять, как расшифровать эти данные.
Я написал несколько строк кода на основе руководств по этому websocket_client, чтобы получить доступ к веб-сокетам с прямой трансляцией и получать сообщения напрямую.
Я отслеживал сообщения, поступающие через вкладку сеть в Chrome, а также изучал выходные данные из приведенного ниже кода, и я совершенно уверен, что данные передаются в виде фрагментированного MP4. Ниже приведены первые 100 или около того байт / сообщений:
b’xfax00x02x86xf1Bxc0x1ex00x00x00x18ftypiso5x00x00x02x00iso6mp41x00x00x02jmoovx00x00x00lmvhdx00x00x00x00xdbx7fxebxb2xdbx7fxebxb2x00x00x03xe8x00x00x00x00x00x01x00x00x01x00x00x00x00x00x00x00x00x00x00x00x00x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00′
На протяжении всего этого вывода присутствует множество пар 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
Обновление: я адаптировал некоторый код в stack-overflow, чтобы предположительно передавать данные fmp4 и преобразовывать их во фреймы. Чтобы добраться туда, я заметил, что первые 16 байт выходных данных из websocket не соответствуют другим файлам 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 из traffic view. Порядок этих кодов ВСЕГДА один и тот же, они представлены следующим образом:
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, и поэтому я думаю, что мне нужно использовать эту информацию тега для создания правильного формата файла с нуля.
Комментарии:
1. Вы можете попробовать перепроектировать код javascript, который обрабатывает данные websocket. Выполнить trafficview.org/js/040f112.js с помощью улучшителя (например codebeautify.org/jsviewer ), чтобы сделать его более читаемым, а затем взгляните на функции EvoWsPlayer.prototype. воспроизведение и EvoPlayer.prototype.ParseData.
2. Спасибо, что указали на это. Похоже, что сценарий был написан, а затем специально перепутан, чтобы сделать его обратное проектирование практически невозможным (имеет смысл). Я также не знаю JavaScript, поэтому это усложняет задачу.
Ответ №1:
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.