Tenho um vídeo mkv (h264) de 23.976 fps (24000/1001), mas quero convertê-lo para 25fps sem recodificar e perder qualidade. Eu sei que mkvmerge pode fazer isso (com a opção --default-duration '0:25fps'), mas gostaria de fazer isso diretamente do ffmpeg, se possível. De acordo com a documentação, isso deve funcionar:
ffmpeg -i input.mkv -r 25 -vcodec copy output.mkv
mas quando executo só obtenho o mesmo fps de vídeo. Qual é o método correto para fazer isso (se existir) no ffmpeg?
Responder1
Aqui está o método usando as versões atuais do FFmpeg. Ele depende do demuxer concat não redimensionar o PTS das entradas após o primeiro arquivo, mas simplesmente aplicar um deslocamento fixo. Digamos que você tenha um fluxo de 30 fps com uma escala de tempo de 15360
(típico de saída FFmpeg). Isso significa que o frame 0
possui PTS 0
e o frame 30
possui PTS 15360
. Isso se tornaria um fluxo de 45 fps se pudéssemos alterar a escala de tempo 23040
sem afetar os valores de PTS.
Essencialmente, é isso que o método abaixo faz.
1. Identifique as propriedades de origem.
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s,
30 fps, 30 tbr, 15360 tbn (default)
Você deseja observar as propriedades de origem, especialmente a resolução e os arquivos tbn
.
2a. (Opcional) Altere a escala de tempo para algo conveniente, para simplificar os cálculos.
ffmpeg -i in.mp4 -c copy -an -video_track_timescale 30 in-v30.mp4
Isso nos leva
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s, \
30 fps, 30 tbr, 30 tbn (default
Se você seguir esta etapa, a nova escala de tempo deverá ser igual ou um múltiplo integral da taxa de quadros original.
2b. Calcule a escala de tempo necessária para que, para o framerate alvo x
, o PTS do quadro # x
na origem tenha o mesmo valor que o novo tbn
. Se você executou o passo 2a, isso é muito fácil e é simplesmente a nova taxa de quadros. Portanto, para fps alvo 45
, new tbn
deveria ser 45
.
3. Gere vídeo fictício.
ffmpeg -f lavfi -i color=s=1280x720:r=45:d=1 -profile:v main -video_track_timescale 45 0.mp4
Todas as propriedades devem ser iguais, como resolução, perfil H.264, formato de pixel, contagem de referências..etc para melhores resultados.
4Concatene os vídeos.
Primeiro crie um arquivo de texto
file '0.mp4'
file 'in-v30.mp4'
Então, o concat
ffmpeg -f concat -i list.txt -c copy -video_track_timescale 45 45fps.mp4
O arquivo de saída terá o segundo vídeo sendo reproduzido a 45 fps.
5. Agora, corte o preroll fictício
ffmpeg -ss 1.1 -i 45fps.mp4 -c copy -avoid_negative_ts make_zero in45.mp4
e você tem
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1757 kb/s, \
45 fps, 45 tbr, 11520 tbn (default)
Eu disse que isso era complicado!
Responder2
ffmpeg -itsscale 1.0427083 -i input.mp4 -codec copy output.mp4
Isso desacelera corretamente um mp4 de 25 fps criado pelo Handbrake a partir de uma fonte de DVD PAL para 23,974 fps. O programa original é NTSC. O áudio permanece sincronizado durante todo o tempo de execução de 47 minutos. É muito rápido porque nenhuma decodificação/codificação está sendo feita. No entanto, existem falhas de áudio (quedas) em intervalos de aproximadamente 3 segundos. O mesmo resultado com vcodec
a substituição do codec, exceto que, embora o vídeo não seja recodificado, o áudio é recodificado na metade da taxa de bits original e ainda apresenta falhas de dropout.
ffmpeg -itsscale 1.0427083 -i input.mp4 -vcodec copy -filter:a "atempo=0.959041" output.mp4
Isso elimina as interrupções de áudio, mas recodifica o áudio. Isso é muito mais rápido do que recodificar vídeo. A desvantagem restante é que o padrão é metade da taxa de bits de áudio original. Precisa descobrir como definir a taxa de bits de áudio para a recodificação.
Responder3
Usar-itsscale
no vídeo de entrada para obter uma mudança efetiva na taxa de quadros. Funciona bem com -vcodec copy
.
Responder4
Você pode fazer isso usando o filtro bitstream setts
. Isso também evita o incômodo de gravar um fluxo bruto em um arquivo e, em seguida, remixá-lo. Isso funciona porque embora você não possa usar filtros normais com cópia de codec ( -c:v copy
), já que eles funcionam no fluxo de vídeo decodificado, você pode usar filtros de fluxo de bits que funcionam no fluxo codificado.
Por exemplo, para alterar a taxa de quadros para 60 fps, insira
-bsf:v setts=ts=STARTPTS+N/TB_OUT/60
Isso deve definir ambos pts
e dts
sem decodificar o fluxo. Se você tiver um vídeo com taxa de quadros variável, talvez precise de algo assim
-bsf:v setts=ts='if(PREV_OUTPTS+9223372036854775808\,PREV_OUTPTS\,STARTPTS)+PREV_OUTDURATION*2'
Isto modifica as durações do quadro em vez de assumir que cada quadro tem uma duração constante. A expressão estranha if
é garantir que o primeiro quadro seja deslocado corretamente, uma vez que PREV_OUTPTS
está definido como -9223372036854775808
(valor mínimo de int de 64 bits) durante o primeiro quadro, e precisamos substituí-lo por zero.
Se você tiver quadros B, isso pode atrapalhar a decodificação, pois dts
pode precisar ser menor que pts
, mas parece funcionar bem para mim de qualquer maneira. Você pode tentar substituir setts=ts
por setts=pts
se tiver problemas. Verhttps://ffmpeg.org/ffmpeg-bitstream-filters.html#settspara detalhes