Como obter o carimbo de data / hora do quadro-chave mais próximo antes de um determinado carimbo de data / hora com o FFmpeg?

18

Eu quero um FFmpeg procurando um comando que seja rápido e preciso. Eu encontrei isso .

A solução é aplicarmos -ssa entrada (busca rápida) e a saída (busca precisa). Mas: se a busca de entrada não é precisa, como podemos ter certeza de que a posição de busca é precisa?


Por exemplo: se quisermos procurar 00:03:00, o comando é algo como:

ffmpeg -ss 00:02:30 -i <INPUT> ... -ss 00:00:30 <OUTPUT>

O primeiro -ssprocurará outro lugar, não 00:02:30, digamos 00:02:31. E depois de aplicar a segunda busca, o resultado final seria 00:03:01- não o que queremos. Isso está correto?

Onde o primeiro -ssprocura? Ele procura o quadro-chave mais próximo 00:02:30?

00:02:31Nesse caso , aqui está o meu pensamento - corrija-me se estiver errado: após a primeira busca, obtemos o registro de data e hora do resultado (neste exemplo :), então aplicamos a segunda busca com o tempo apropriado, neste caso 00:00:29.

A pergunta é: como obtemos o registro de data e hora do primeiro resultado da pesquisa?

jackode
fonte

Respostas:

18

Para literalmente responder à pergunta do seu título: Você pode obter uma lista de quadros I com

ffprobe -select_streams v -show_frames <INPUT> 

Você pode limitar ainda mais a saída necessária adicionando -show_entries frame=pkt_pts_time,pict_type.

Para ver qual quadro está mais próximo (vem depois) de um determinado carimbo de data / hora, primeiro você precisa descobrir todos os carimbos de data / hora dos quadros-chave, por exemplo com awk.

Primeiro, defina o tempo que deseja procurar, por exemplo, 2: 30m, que é igual a 150s.

ffprobe -select_streams v -show_frames -show_entries frame=pkt_pts_time,pict_type -v quiet in.mp4 | 
awk -F= ' 
  /pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } } 
  /pkt_pts_time/ { if (i && ($2 >= 150)) print $2; }  
' | head -n 1

Por exemplo, isso retornaria 150.400000.


Observe que, quando usado -ssanteriormente -i, o FFmpeg localizará o quadro-chave anterior ao ponto de busca e atribuirá valores negativos de PTS a todos os quadros seguintes até que ele atinja o ponto de busca. Um player deve decodificar, mas não exibir quadros com PTS negativo, e o vídeo deve iniciar com precisão.

Alguns players não respeitam isso adequadamente e exibem vídeo ou lixo preto. Nesse caso, o script acima pode ser usado para encontrar o PTS do quadro-chave após o ponto de busca e usá-lo para começar a procurar no quadro-chave. Isso, no entanto, não será preciso.

Observe que, se você quiser ser super preciso ao procurar - e manter a compatibilidade com muitos players - provavelmente converta o vídeo em qualquer formato sem perda, somente intra, onde possa cortar a qualquer momento e recodificá-lo. Mas isso não será rápido.

slhck
fonte
1
obrigado, eu não estou fazendo um editor de vídeo, mas eu quero ter uma busca de vídeo precisa na qual o intervalo deve ser menor que 0,5 segundos.
jackode
1
Você provavelmente pode fazer malabarismos com o PTS de ffprobe. Caso contrário, qualquer formato intermediário faria, por exemplo, ProRes 422, DNxHD, que são visualmente sem perdas e apenas dentro do quadro. Ou você usa algo como HuffYUV, etc. Mas você perderia o aspecto "rápido" novamente, é claro.
slhck
qual a versão do ffprobe você usou para o comando, pois meu disseUnrecognized option 'select_streams'
jackode
2
Você estava perto, a select_streamsopção foi adicionada em outubro de 2012 . :) Você poderia ficar sem isso, mas também obteria informações para os quadros de áudio, misturados entre eles.
slhck
2
Note que você pode adicionar esta linha o ffmpeg para deixá-lo apenas a saída das necessárias 2 campos, em vez de um monte de coisas que é jogado fora por awk: -show_entries frame = pkt_pts_time, pict_type
Janes
7

Entendo que essa pergunta tenha vários anos, mas a versão mais recente do ffprobe tem a capacidade de pular quadros . Você pode transmitir -skip_frame nokeyinformações apenas nos quadros-chave (quadros I). Isso pode economizar muito tempo! Em um arquivo MP4 de 2 GB e 1080p, demorava 4 minutos sem pular os quadros. A adição do parâmetro pular leva apenas 20 segundos.

Comando:

ffprobe -select_streams v -skip_frame nokey -show_frames -show_entries frame = pkt_pts_time, tipo de imagem D: \ test.mp4

Resultados:

[FRAME]
pkt_pts_time=0.000000
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=3.753750
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=7.507500
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=11.261250
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=15.015000
pict_type=I
[/FRAME]

Portanto, os resultados conterão apenas informações sobre os quadros principais.

Hind-D
fonte
1

Com base na resposta do slhck , aqui está uma função bash que retornará o quadro-chave mais próximo que ocorre ANTES de Nsegundos.

Isso também é utilizado -read_intervalspara garantir que o ffprobe só comece a procurar seu quadro-chave 25 segundos antes de Nsegundos. Esse truque e a saída do awk quando o carimbo de data e hora são encontrados aceleram bastante as coisas.

function ffnearest() {
  STIME=$2; export STIME;
  ffprobe -read_intervals $[$STIME-25]% -select_streams v -show_frames -show_entries frame=pkt_pts_time,pict_type -v quiet "$1" |
  awk -F= '
    /pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } }
    /pkt_pts_time/ { if (i && ($2 <= ENVIRON["STIME"])) print $2; }
    /pkt_pts_time/ { if (i && ($2 > ENVIRON["STIME"])) exit 0; }
  ' | tail -n 1
}

exemplo de uso:

➜ ffnearest input.mkv 30
23.941000

Eu uso isso para aparar arquivos de vídeo sem recodificá-los. Como você não pode adicionar novos quadros-chave sem recodificar, eu costumo ffnearestprocurar o quadro-chave antes de querer cortar. Aqui está um exemplo:

ffmpeg  -i input.mkv -ss 00:00:$(echo "$(ffnearest input.mkv 30) - 0.5" | bc)  -c copy -y output.mkv;

Observe que, para esse exemplo, pode ser necessário alterar o formato do que é passado no -ssparâmetro se você estiver procurando mais do que os primeiros 60 segundos.

(irritantemente, dizendo ffmpeg procurar exatamente ao timestamp do quadro-chave parece fazer ffmpeg excluir esse quadro-chave na saída, mas subtraindo 0,5 segundos de timestamp real do quadro-chave faz o truque. Para o bash você precisa usar bcpara avaliar expressões com decimais , mas no zsh -ss 00:00:$[$(ffnearest input.mkv 28)-0.5]funciona.)

Chris
fonte
Isso dará o próximo tempo de quadro após o quadro.
Ehsan Chavoshi
0

Se você deseja obter informações dos quadros I, pode usar

ffprobe -i input.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type|grep -B 1 'pict_type=I'
shuaihanhungry
fonte