domingo, 26 de fevereiro de 2017

Recuperando um arquivo corrompido da Newtek Tricaster TC40

A Tricaster TC40 (ou simplesmente como Tricaster 40) faz suas gravações em QuickTime MOV. A questão é que se algo acontece com o sistema que interrompe a gravação do arquivo você terá um arquivo ilegível. MOV, tal como MP4 e outros formatos, escrevem as informações sobre a leitura do arquivo e a localização de cada pacote de áudio e vídeo no extremo final do arquivo, o que causa ele ficar irreconhecível caso o processo não aconteça até o final como deveria. Mas há uma forma manual de recuperar e recompor este arquivo danificado.

O primeiro passo são os scripts em Python que escrevi para extrair e separar os blocos de vídeo e áudio do arquivo danificado. Lembrando que:

  • Não sou um programador profissional. Faço isso de acordo com as minhas necessidade imediatas. O código poderia ser muito mais otimizado, amigável, rápido, mas não tenho o conhecimento ou tempo pra isso.
  • Este código funciona estritamente de acordo com as características observadas na configuração de arquivo utilizado por mim, que é um arquivo com vídeo em MPEG-2 4:2:2 I-frames e dois canais de áudio estéreo PCM, o segundo sendo completamente mudo. Em diferentes situações o código pode necessitar uma total reescrita.

Script: extract-audio.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import os.path

def read_in_chunks(infile, chunk_size=4):
 packet = False
 bloco_audio = b""
 infile.seek(512)
 for chunk in iter(lambda: infile.read(chunk_size), ''):
  if chunk == b"\x00\x00\x01\xb3":
   if bloco_audio != b"":
    if bloco_audio[-4608:] == (b"\x00" * 4608):
     yield bloco_audio[-9216:-4608]
   bloco_audio = chunk
   continue
  bloco_audio += chunk

infile = open(sys.argv[1], "rb")
path = os.path.dirname(sys.argv[1])+"/"+os.path.splitext(os.path.split(sys.argv[1])[1])[0]+"_audio.raw"
out = open(path, 'w')

for chunk in read_in_chunks(infile):
 out.write(chunk)

Script: extract-video.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import os.path

def read_in_chunks(infile, chunk_size=4):
 packet = False
 padding = False
 bloco_video = b""
 infile.seek(512)
 for chunk in iter(lambda: infile.read(chunk_size), ''):
  if packet == True and chunk == b"\x00\x00\x01\xb3":
   packet = False
   if bloco_video != b"":
    if bloco_video[-4608:] == (b"\x00" * 4608):
     yield bloco_video[:-9216]
    else:
     yield bloco_video
   bloco_video = b""
  if chunk == b"\x00\x00\x01\xb3":
   packet = True
   bloco_video += chunk
   continue
  if packet == True:
   bloco_video += chunk
   continue

infile = open(sys.argv[1], "rb")
path = os.path.dirname(sys.argv[1])+"/"+os.path.splitext(os.path.split(sys.argv[1])[1])[0]+"_video.ts"
out = open(path, 'w')

for chunk in read_in_chunks(infile):
 out.write(chunk)

(Não entrarei no aspecto de como rodar Python e FFMPEG em algum sistema pois devem ter instruções muito melhores e atualizadas internet afora.)

Após rodar estes scripts com "python extract-audio.py o-arquivo-danificado.mov" e "python extract-video.py o-arquivo-danificado.mov", e isso vai demorar bastante principalmente se o arquivo tiver vários gigabytes, você terá na mesma pasta do arquivo MOV arquivos com o mesmo tempo só que finais _audio.raw e _video.ts. Agora entra o FFMPEG para reorganizar eles a serem legíveis. E tudo dependerá do formato que o áudio e video pertencem.

Algumas correções necessárias para reprodução correta: video_track_timescale para corrigir a velocidade do arquivo (59,94 fps neste caso, pode ser diferente em outros arquivos) e vtag para assinalar a faixa de vídeo como a do formato escrito pela Tricaster e não o MPEG padrão. Também os parâmetros para definir para o programa player como deve ser lido o áudio PCM.

ffmpeg -i arquivo_video.ts -f s16le -ar 48000 -ac 2 -i arquivo_audio.raw -map 0:0 -map 1:0 \
-vcodec copy -video_track_timescale 60000 -vtag xd59 -acodec copy arquivo_video.mov

Isto feito temos um arquivo MOV com o vídeo e áudio que estava presente no arquivo danificado, agora legícvel em um novo arquivo MOV.