Retomando uma codificação parcialmente concluída com FFmpeg
Resumo
Dado uma codificação parcialmente concluída que:
- Foi codificado usando h264 no contêiner Matroska
- Foi interrompido interrompendo manualmente o FFmpeg digitando 'q' no console
... quais são as maneiras de retomar a codificação deste arquivo, de preferência anexando o arquivo existente?
Detalhes
Tenho alguns grandes projetos de codificação que faço que podem levar até uma semana de codificação em alguns casos. Em circunstâncias ideais, o computador que faz a codificação pode permanecer ligado, sem ser interrompido e deixado para fazer o trabalho. Porém, em situações realistas, às vezes esse computador precisa parar de codificar e ser desligado ou reiniciado por uma série de razões. Por causa disso, estou procurando uma maneira de tornar a codificação usando FFmpeg naquele computador mais "à prova de bala", antecipando possíveis interrupções e tendo uma maneira de usar o FFmpeg sozinho para continuar de onde parou mais tarde.
O que estou fazendo agora
Quando isso acontece, eu recupero o que posso do arquivo codificado, examino-o no software de edição de vídeo, encontro um bom ponto de emenda e uso o AviSynth para criar um script que começará a codificar um novo segmento onde o anterior parou. Em seguida, uso outro software para cortar a codificação existente para que a emenda seja perfeita e, em seguida, uso o demuxer do FFmpeg para combinar os segmentos h264 individuais mais tarde em um arquivo completo.
Isso tudo é muito trabalho manual que pode ser muito lento e eu gostaria de evitar se houver uma solução mais automatizada.
Ideias
Segmentação: parece a solução mais promissora, mas há lacunas que não tenho certeza de como preencher. Esta resposta parece cobrir muito disso. Você pode dizer ao FFmpeg para gerar segmentos, divididos em I-Frames em intervalos aproximados, que podem ser remuxados posteriormente. O que não sei ao olhar para essa resposta e a documentação do FFmpeg é como eu começaria a codificar novamente em um segmento específico. Parece que pode haver uma maneira de fornecer ao FFmpeg um arquivo que informa onde cada segmento deve começar via frame ou timecode, mas como faço para começar em algum lugar no meio dessa lista?
Essa opção não é ideal, no entanto, porque precisarei efetivamente ter o dobro do espaço da codificação resultante (que frequentemente terá centenas de gigabytes de tamanho) disponível, uma vez que precisarei agrupar os segmentos em um único arquivo. Seria bom se eu pudesse cortar de alguma forma uma codificação em andamento e fazer o FFmpeg reabrir esse arquivo e continuar de onde parou.
Máquinas virtuais: isso tem potencial para funcionar, pois eu poderia simplesmente congelar o estado da máquina antes de reiniciar, mas todo o meu espaço de codificação existe no armazenamento conectado à rede, que não tenho certeza se funcionará no contexto de uma máquina virtual sendo suspenso e retomado, uma vez que a VM não controla realmente as unidades.
Respostas
Eu vim com uma solução "boa o suficiente" para este problema, já que não parecia possível no momento da escrita realmente pegar uma codificação parcial que foi cortada arbitrariamente e concatenar com precisão o resto do vídeo para ela .
Uma solução usando segmentação
Primeiro, use a funcionalidade de segmentação do FFmpeg para gerar segmentos em algum intervalo para o qual perder tal intervalo é uma perda aceitável. Também produz um arquivo .csv com horários de início e término do segmento. Um exemplo de comando que consegue isso é:
ffmpeg.exe -i .\source.avi -c:v libx264 -crf 20 -c:a aac -pix_fmt yuv420p -f segment -segment_time 30 -segment_list segments.csv -reset_timestamps 1 seg%03d.mkv
Suponha que durante a codificação do quarto segmento (seg003.mkv) seja necessário parar o codificador e reiniciar a máquina. Enviar um qpressionamento de tecla para FFMpeg durante a codificação de um segmento interrompe-o arbitrariamente em algum ponto e deixa esse segmento incompleto.
O segmento que foi interrompido é efetivamente perdido e pode ser excluído. Abrir o arquivo segmentos.csv revela
seg000.mkv,0.000000,33.390000
seg001.mkv,33.390000,62.586000
seg002.mkv,62.586000,91.782000
seg003.mkv,91.782000,98.655000
Usando essas informações, sabemos que seg003.mkv começou em 91,782 segundos na origem. Para continuar de onde paramos, usamos o mesmo comando acima para codificar a saída segmentada, mas também procuram ( -ss) para o início do nosso quarto segmento e alteramos a numeração ( -segment_start_number):
ffmpeg.exe -ss 91.782000 -i source.avi -c:v libx264 -crf 20 -c:a aac -pix_fmt yuv420p -f segment -segment_time 30 -segment_list segments2.csv -segment_start_number 3 -reset_timestamps 1 seg%03d.mkv
FFmpeg pegará no início do quarto segmento, e nomeará corretamente seg003.mkv. Observe que o nome do arquivo .csv de saída foi alterado para que o original não seja sobrescrito. Este novo arquivo .csv começará em 0,00, portanto, interrupções futuras exigirão a adição de tempos neste arquivo ao arquivo anterior para encontrar um ponto de partida.
Finalmente, quando todos os segmentos estão prontos para serem concatenados, construímos um arquivo .ffcat conforme descrito nos exemplos na página wiki :
file seg000.mkv
file seg001.mkv
file seg002.mkv
file seg003.mkv
file seg004.mkv
file seg005.mkv
e execute este comando:
ffmpeg.exe -f concat -i .\segments.ffcat -c copy output.mkv
Isso gera a codificação concatenada completa. A opção -fflags +genptsé mencionada na resposta vinculada da pergunta e pode ser necessária para obter uma saída perfeita, mas não achei que fizesse qualquer diferença em meus testes.