Ao condicionar um fluxo para reprodução de DASH, os pontos de acesso aleatório devem estar exatamente no mesmo horário do fluxo de origem em todos os fluxos. A maneira usual de fazer isso é forçar uma taxa de quadros fixa e um comprimento GOP fixo (ou seja, um quadro-chave a cada N quadros).
No FFmpeg, a taxa de quadros fixa é fácil (-r NUMBER).
Mas para locais fixos de quadro-chave (comprimento GOP), existem três métodos ... qual deles é "correto"? A documentação do FFmpeg é frustrantemente vaga sobre isso.
Método 1: mexendo nos argumentos da libx264
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
Parece haver algum debate se o cenário deve ser desativado ou não, pois não está claro se o "contador" do quadro-chave é reiniciado quando ocorre um corte na cena.
Método 2: definindo um tamanho fixo de GOP:
-g GOP_LEN_IN_FRAMES
Infelizmente, isso é documentado apenas de passagem na documentação do FFMPEG e, portanto, o efeito desse argumento não é muito claro.
Método 3: insira um quadro-chave a cada N segundos ( talvez? ):
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
Isso está explicitamente documentado. Mas ainda não está claro se o "contador de tempo" será reiniciado após cada quadro-chave. Por exemplo, em um GOP esperado de 5 segundos, se houver um scenecut
quadro - chave injetado 3 segundos pela libx264, o próximo quadro-chave seria 5 segundos mais tarde ou 2 segundos depois?
De fato, a documentação do FFmpeg diferencia isso e a -g
opção, mas na verdade não diz como essas duas opções acima são as menos diferentes (obviamente, -g
isso exigirá uma taxa de quadros fixa).
Qual é certo?
Parece que -force_key_frames
seria superior , pois não exigiria uma taxa de quadros fixa. No entanto, isso requer que
- está em conformidade com as especificações GOP em H.264 ( se houver )
- GARANTE que haja um quadro-chave em cadência fixa, independentemente dos
scenecut
quadros-chave libx264 .
Parece também que -g
não funcionaria sem forçar uma taxa de quadros fixa ( -r
) , pois não há garantia de que várias execuções ffmpeg
com argumentos de codec diferentes forneceriam a mesma taxa de quadros instantânea em cada resolução. Taxas de quadros fixas podem reduzir o desempenho da compactação (IMPORTANTE em um cenário DASH!).
Finalmente, o keyint
método parece apenas um hack . Espero contra a esperança que esta não seja a resposta correta.
Referências:
Um exemplo usando o -force_key_frames
método
fonte
ffprobe -i input.mp4 -select_streams v -show_frames -of csv -show_entries frame=pict_type
e colorindo as células. Receio que não haja discussões públicas, mas vou ver se consigo descobrir alguns dos links que encontrei na época.-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
formulário? Eu apenas tentei e descobri que, embora houvesse quadros I adicionais no fluxo, ele parecia cumprir minha regra. Um programa PERL seguirá como uma "resposta", pois você aparentemente não pode usar a marcação nos comentários.-force_key_frames
não funcionou para mim e, portanto, nunca tentei novamente. Isso foi há mais de um ano. Talvez tenha sido um bug. Vou tentar novamente em breve.Aqui estão meus cinquenta centavos para o caso.
Gere iframes apenas nos intervalos desejados.
Exemplo 1:
Gere iframes conforme o esperado:
O método 2 é depreciado. Omitido.
Exemplo 2
Gere um iframes de uma maneira ligeiramente diferente:
Como você pode ver, coloca iframes a cada 2 segundos E em cena (segundos com parte flutuante), o que é importante para a complexidade do fluxo de vídeo na minha opinião.
Os tamanhos de arquivo gerado são praticamente os mesmos. Muito estranho que, mesmo com mais quadros-chave no método 3, ele às vezes gere menos arquivos que o algoritmo de biblioteca x264 padrão.
Para gerar vários arquivos de taxa de bits para o fluxo HLS, escolhemos o método três. É perfeitamente alinhado com 2 segundos entre os pedaços, eles têm iframe no início de cada pedaço e têm iframes adicionais em cenas complexas, o que proporciona uma melhor experiência para usuários que possuem dispositivos desatualizados e não podem reproduzir x264 perfis de alto nível.
Espero que ajude alguém.
fonte
A resposta, portanto, parece ser:
libx264
específico e tem o custo de eliminar ascenecut
opção muito útil emlibx264
.-g
parece estar obsoleto. Ele não parece funcionar, nem está explicitamente definido na documentação, nem é encontrado na ajuda, nem parece ser usado no código. A inspeção de código mostra que a-g
opção provavelmente se destina a fluxos MPEG-2 (existem até estrofes de código referentes a PAL e NTSC!).Além disso:
Script para a
-force_key_frames
opçãoAqui está um pequeno programa PERL que usei para verificar a cadência de quadros I com base na saída da sugestão ffprobe do slhck. Parece verificar que o
-force_key_frames
método também funcionará e tem o benefício adicional de permitirscenecut
quadros. Não tenho absolutamente nenhuma idéia de como o FFMPEG faz isso funcionar, ou se de alguma forma tive sorte porque meus fluxos estão bem condicionados.No meu caso, codifiquei a 30fps com um tamanho esperado de GOP de 6 segundos ou 180 quadros. Eu usei 180 como o argumento gopsize deste programa verificou um quadro I em cada múltiplo de 180, mas defini-lo como 181 (ou qualquer outro número que não seja múltiplo de 180) fez com que ele se queixasse.
fonte
force_key_frames
, ele meio que atrapalha o algoritmo de alocação de x264 bits, por isso pode oferecer uma qualidade pior do que simplesmente definir um intervalo fixo de quadro-chave.-g
, você diz: "Ele não parece funcionar, ... nem parece ser usado no código". Eu verifiquei e da entrada dag
é armazenada emavctx->gop_size
e que libx264 faz uso dela:x4->params.i_keyint_max = avctx->gop_size;
. Quando analiso este arquivo de teste geradoffmpeg -i a-test-file.mp4 -g 37 -t 15 gtest.mp4
:, recebo quadros-chave exatamente0,37,74,111,148,185,222,259,296,333,370
. Um GOP pode ser encurtado se a mudança de cena for acionada e, para isso,-sc_threshold
pode ser definido, o que também é captado por x264.Eu queria adicionar algumas informações aqui, pois meu pesquisador puxou bastante essa discussão em minha busca para encontrar informações sobre como tentar encontrar uma maneira de segmentar minha codificação DASH da maneira que eu queria, e nenhuma das informações que encontrei estava totalmente correta.
Primeiro vários equívocos para se livrar:
Nem todos os quadros I são iguais. Há grandes quadros em "I" e pequenos quadros em "i". Ou para usar a terminologia correta, quadros I-IDR e quadros I não-IDR. Os quadros I do IDR (às vezes chamados de "quadros-chave") criarão um novo GOP. Os quadros não-IDR não serão. Eles são úteis para ter dentro de um GOP, onde há uma mudança de cena.
-x264opts keyint=GOPSIZE:min-keyint=GOPSIZE
← Isso não faz o que você pensa que faz. Demorei um pouco para descobrir. Acontece quemin-keyint
o código é limitado. Não é permitido que seja maior que(keyint / 2) + 1
. Portanto, atribuir o mesmo valor a essas duas variáveis resulta no valor demin-keyint
ser derrubado pela metade ao codificar.Eis o seguinte: o corte de cena é realmente ótimo, especialmente em vídeos com cortes rápidos. Ele o mantém bonito e nítido, por isso não quero desativá-lo, mas ao mesmo tempo não consegui obter um tamanho fixo de GOP desde que estivesse ativado. Eu queria ativar o corte de cena, mas usar apenas quadros I não IDR. Mas não estava funcionando. Até eu descobrir (a partir de muita leitura) sobre o equívoco # 2.
Acontece que eu precisava definir o
keyint
dobro do tamanho GOP desejado. Isso significa quemin-keyint
pode ser definido para o tamanho GOP desejado (sem o código interno cortando-o ao meio), o que impede que a detecção de corte de cena use quadros I IDR dentro do tamanho GOP porque a contagem de quadros desde o último quadro I IDR é sempre menos quemin-keyinit
.E, finalmente, definir a
force_key_frame
opção substitui o tamanho duplokeyint
. Então, aqui está o que funciona:Eu prefiro segmentos em pedaços de 2 segundos, então meu GOPSIZE = Taxa de quadros * 2
Você pode verificar usando o ffprobe:
No arquivo CSV gerado, cada linha informará
frame, [is_an_IDR_?], [frame_type], [frame_number]
:O resultado é que você só verá os quadros I de IDR em
GOPSIZE
intervalos fixos , enquanto todos os outros quadros I são quadros I não-IDR inseridos conforme necessário pela detecção de corte de cena.fonte
Parece que essa sintaxe nem sempre funciona. Eu testei bastante em nosso conteúdo VOD, bem como em conteúdo ao vivo (despejos de arquivos) e, às vezes, o cenário não funciona e aciona um iframe entre:
Sintaxe para uma conversão ascendente i50-> p50, 2 seg gop / segmento, IDR no início, iframes entre, se necessário
fonte
Twitch tem um post sobre isso. Eles explicam que decidiram usar seu próprio programa por vários motivos; uma delas era que o ffmpeg não permite executar diferentes instâncias x264 em diferentes threads, mas dedica todos os threads especificados a um quadro em uma saída antes de passar para a próxima saída.
Se você não estiver transmitindo em tempo real, terá mais luxo. A maneira 'correta' provavelmente é codificar em uma resolução com apenas o tamanho GOP especificado com -g e, em seguida, codificar as outras resoluções forçando quadros-chave nos mesmos locais.
Se você quiser fazer isso, use o ffprobe para obter os tempos do quadro-chave e, em seguida, use um script de shell ou uma linguagem de programação real para convertê-lo em um comando ffmpeg.
Mas, para a maioria dos conteúdos, há pouca diferença entre ter um quadro-chave a cada 5 segundos e dois quadros-chave a cada 5 segundos (um forçado e outro do scenecut). Trata-se do tamanho médio do quadro I versus o tamanho dos quadros P e quadros B. Se você usa x264 com configurações típicas (a única razão pela qual acho que você deve fazer algo para afetá-las é se você definir -qmin, como uma maneira pobre de impedir que x264 use taxa de bits em conteúdo fácil; isso limita todos os tipos de quadro ao mesmo valor , Acho) e obtenha um resultado como o tamanho médio do quadro I de 46 kB, o quadro P 24 kB, o quadro B 17 kB (metade da frequência dos quadros P) e, em seguida, um quadro I extra a cada segundo a 30 fps é apenas um aumento de 3% no tamanho do arquivo. A diferença entre h264 e h263 pode ser composta por um monte de reduções de 3%, mas uma única não é muito importante.
Em outros tipos de conteúdo, os tamanhos dos quadros serão diferentes. Para ser justo, trata-se de complexidade temporal e não espacial, por isso não é apenas um conteúdo fácil versus um conteúdo rígido. Mas, geralmente, os sites de streaming de vídeo têm um limite de taxa de bits e o conteúdo com quadros I relativamente grandes é um conteúdo fácil que será codificado com alta qualidade, independentemente de quantos quadros-chave extras sejam adicionados. É um desperdício, mas esse desperdício geralmente não será percebido. O caso mais inútil é provavelmente um vídeo que é apenas uma imagem estática que acompanha uma música, onde cada quadro-chave é exatamente o mesmo.
Uma coisa que não tenho certeza é como os quadros-chave forçados interagem com o limitador de taxa definido com -maxrate e -bufsize. Acho que até o YouTube teve problemas recentes ao definir corretamente as configurações do buffer para fornecer qualidade consistente. Se você estiver usando apenas configurações médias de taxa de bits, como pode ser visto em alguns sites (já que você pode inspecionar as opções do x264 no cabeçalho / mov atom? Com um editor hexadecimal), o modelo do buffer não é um problema, mas se você exibindo conteúdo gerado pelo usuário, a taxa de bits média incentiva os usuários a adicionar uma tela preta no final do vídeo.
A opção -f do Ffmpeg, ou qualquer outra opção de codificador usada, é mapeada para a opção específica do codificador. Portanto, '-x264-params keyint = GOPSIZE' é equivalente a '-g GOPSIZE'.
Um problema com o uso da detecção de cena é se você prefere quadros-chave próximos a números específicos por qualquer motivo. Se você especificar quadros-chave a cada 5 segundos e usar a detecção de cena, e houver uma alteração de cena em 4,5, ela deverá ser detectada, mas o próximo quadro-chave será em 9,5. Se o tempo continuar aumentando assim, você poderá terminar com quadros-chave em 42,5, 47,5, 52,5 etc., em vez de 40, 45, 50, 55. Por outro lado, se houver uma mudança de cena em 5,5, haverá um quadro-chave em 5 e 5,5 será muito cedo para outro. O Ffmpeg não permite que você especifique "faça um quadro-chave aqui se não houver mudança de cena nos próximos 30 quadros". Alguém que entende C pode adicionar essa opção, no entanto.
Para vídeo com taxa de quadros variável, quando não estiver transmitindo ao vivo como o Twitch, você poderá usar as mudanças de cena sem converter permanentemente em taxa de quadros constante. Se você usar o filtro 'select' no ffmpeg e usar a constante 'scene' na expressão, a saída de depuração (-v debug ou pressione '+' várias vezes durante a codificação) exibirá o número de alteração da cena. Provavelmente, isso é diferente e não é tão útil quanto o número usado por x264, mas ainda pode ser útil.
O procedimento, então, provavelmente seria fazer um vídeo de teste apenas para alterações no quadro-chave, mas talvez pudesse ser usado para dados de controle de taxa, se estiver usando 2 passagens. (Não tenho certeza se os dados gerados são úteis para diferentes resoluções e configurações; os dados da árvore de macroblocos não serão.) Converta-os em vídeo com taxa de quadros constante, mas veja este bug sobre a interrupção da produção ao reduzir pela metade a taxa de quadros, se você decidir para usar o filtro fps para outros fins. Execute o x264 com o quadro-chave desejado e as configurações de GOP.
Em seguida, use esses tempos de quadro-chave com o vídeo de taxa de quadros variável original.
Se você permitir conteúdo maluco gerado pelo usuário com um intervalo de 20 segundos entre os quadros, para a codificação de taxa de quadros variável, poderá dividir a saída, usar o filtro fps, de alguma forma usar o filtro de seleção (talvez crie uma expressão realmente longa que tenha a cada momento do quadro-chave) ... ou talvez você possa usar o vídeo de teste como entrada e decodificar apenas quadros-chave, se essa opção ffmpeg funcionar, ou usar o filtro selecionar para selecionar quadros-chave. Em seguida, redimensione-o para o tamanho correto (há até um filtro scale2ref para isso) e sobreponha o vídeo original. Em seguida, use o filtro de intercalação para combinar esses quadros-chave a serem forçados com o vídeo original. Se isso resultar em dois quadros separados por 0,001 s que o filtro de intercalação não impede, então resolva esse problema com outro filtro de seleção. Lidar com os limites do buffer de quadro para o filtro de intercalação pode ser o principal problema aqui. Tudo isso poderia funcionar: use algum tipo de filtro para proteger o fluxo mais denso (filtro fifo?); consulte o arquivo de entrada várias vezes para que seja decodificado mais de uma vez e os quadros não precisem ser armazenados; use o filtro 'streamselect', o que eu nunca fiz, exatamente nos horários dos quadros-chave; melhore o filtro de intercalação alterando seu comportamento padrão ou adicionando uma opção para gerar o quadro mais antigo em um buffer em vez de soltar um quadro. o que nunca fiz, exatamente nos momentos dos quadros-chave; melhore o filtro de intercalação alterando seu comportamento padrão ou adicionando uma opção para gerar o quadro mais antigo em um buffer em vez de soltar um quadro. o que nunca fiz, exatamente nos momentos dos quadros-chave; melhore o filtro de intercalação alterando seu comportamento padrão ou adicionando uma opção para gerar o quadro mais antigo em um buffer em vez de soltar um quadro.
fonte