Ручное восстановление видео с видеорегистратора

hackerman22891

Участник
19
7
24 Июн 2019
Выбор программного обеспечения

Сканирование диска программой DVR Examiner (обычно её использовал для подобных целей) показало, что на диске всего 4 видеофрагмента с одной камеры. То есть программа не очень отработала.


Дальше просканировал диск программой myCCTV Recovery – демо версия нашла все камеры и все ролики. Правда восстановить ничего не позволяет, а за однократное использование программа просит 200 Евро. Слишком много вообще-то. Не могу себе позволить. Но я убедился, что видео на диске есть. Будем копать дальше.



Нашёл ещё одну китайскую программу – аналогично myCCTV Recovery, все видео нашла, но восстановить тоже 200 Евро. Активированную не нашёл. Что же дальше.

Ручное восстановление

Посовещавшись со знакомыми экспертами в этой области я решил попробовать выковырять видеопоток вручную.


Снял образ с диска (на всякий случай) и посмотрел начало диска в HEX-редакторе:


2019-11-18_09-21-24-620x177.png




Сигнатура ZHILING1.0 – спецификацию не этот формат я не нашёл, но мне подсказали опорные моменты.


Итак, идёт сигнатура вышеупомянутая, затем большая область нулей.


Начиная со смещения 0x0008F60000 пошёл видеопоток.


2019-11-18_09-22-50-620x358.png




Формат следующий:


  1. Начало фрейма: 4 байта (5A 4C 41 56) “ZLAV”;
  2. Тип фрейма: 2 байта (00 FC);
  3. Номер канала (камера, нумерация с 0): 2 байта (00 00);
  4. Номер последовательности: 4 байта (00 00 00 3E);
  5. Секунды: 4 байта (00 00 06 0F);
  6. Далее неизвестное поле и данные фрейма в формате h264;
  7. Окончание фрейма: 4 байта (7A 6C 61 76) “zlav”;

И такая ситуация циклична…


2019-11-18_09-23-49-620x182.png
То есть мне по сути надо читать содержимое диска, искать по сигнатуре ZLAV, затем прочитать номер канала – если это нужная камера, то выгрузить фрейм в выходной файл. Далее нужно будет упорядочить по номеру последовательности и сконвертировать в mpeg например бесплатной ffmpeg.exe.

Python-скрипт


Python:
import io
import os
f_input = open(r'1.dd', 'rb')
f_input.seek(0, io.SEEK_END)
file_size = f_input.tell()
print('file size = {}'.format(file_size))
START_OFFSET = 0x0008F60000
CHUNK_SIZE = 1024 * 1024 * 16
frames_count = 0
channels_var = dict()
types_var = dict()
frame_seq_prev = -1
f_input.seek(START_OFFSET, io.SEEK_SET)
data_bytes = bytearray()
total_bytes_done = 0
f_output_ch_index = 0
f_output_ch = None
while f_input.tell() < file_size:
    data_bytes.extend(bytearray(f_input.read(CHUNK_SIZE)))
    frame_channel = -1
    for i in range(0, len(data_bytes) - 64):
        if b'ZLAV' in data_bytes[i:i + 4]:
            frame_offset = i
            frame_type = data_bytes[i + 4]
            frame_channel = data_bytes[i + 6]
            if frame_channel != 2:
                continue
            frame_seq = int.from_bytes(data_bytes[i + 8:i + 12], byteorder='little', signed=False)
            frame_time_seconds = int.from_bytes(data_bytes[i + 16:i + 20], byteorder='little', signed=False)
        if b'zlav' in data_bytes[i:i + 4]:
            frames_count = frames_count + 1
            frame_size = int.from_bytes(data_bytes[i + 4:i + 8], byteorder='little', signed=False)
            if frame_channel == 2:
                if frame_type == 0xfc or frame_type == 0xfd:
                    if f_output_ch is None:
                        f_output_ch = open('ch{}_{}_{}.h264'.format(frame_channel + 1, frame_time_seconds, f_output_ch_index), 'wb')
                    f_output_ch.write(data_bytes[frame_offset + 8:frame_offset + frame_size - 8])
                    f_output_ch.flush()
                    if frame_channel not in channels_var.keys():
                        channels_var[frame_channel] = 0
                    channels_var[frame_channel] = channels_var[frame_channel] + 1
                    if frame_seq_prev + 1 != frame_seq:
                        f_output_ch.close()
                        f_output_ch_index = f_output_ch_index + 1
                        f_output_ch = open('ch{}_{}_{}.h264'.format(frame_channel + 1, frame_time_seconds, f_output_ch_index), 'wb')
                    frame_seq_prev = frame_seq
            frame_str = ''
        total_bytes_done = total_bytes_done + 1
    data_bytes = data_bytes[-64:]
    print('chunk done, total bytes = {}'.format(START_OFFSET + total_bytes_done))
print('frames count = {}'.format(frames_count))
print('channels = {}'.format(channels_var))
f_input.close()
f_output_ch.close()

На 500 Гб образе скрипт работал примерно неделю. На выходе получил множество .h264 файлов видеопотока.
После чего запустил батник:
FOR %%f IN (*.h264) DO ffmpeg.exe -i %%f -vcodec copy %%f.mpeg
И файлы преобразуются в mpeg. Их уже можно просмотреть в обычном проигрывателе Media Player Classic. Осталось найти нужный фрагмент и при желании склеить отдельные куски.


P.S. Скрипт мне подсказал коллега, который пожелал остаться неизвестным)