<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pt-BR">
    <title>Brenner Cruvinel - av1</title>
    <subtitle>AI researcher and product designer building at the edge of computer science and mental health.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://brennercruvinel.blog/tags/av1/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://brennercruvinel.blog"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-09-03T00:00:00+00:00</updated>
    <id>https://brennercruvinel.blog/tags/av1/atom.xml</id>
    <entry xml:lang="pt-BR">
        <title>No Final do Dia Tá a Mesma Coisa, 1,3 GB</title>
        <published>2026-09-03T00:00:00+00:00</published>
        <updated>2026-09-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            Brenner Cruvinel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://brennercruvinel.blog/blog/no-final-do-dia-ta-a-mesma-coisa-1-3-gb/"/>
        <id>https://brennercruvinel.blog/blog/no-final-do-dia-ta-a-mesma-coisa-1-3-gb/</id>
        
        <content type="html" xml:base="https://brennercruvinel.blog/blog/no-final-do-dia-ta-a-mesma-coisa-1-3-gb/">&lt;p&gt;eu tinha certeza. ordenar as cartas por similaridade antes de encodar ia multiplicar a compressão, porque frame vizinho parecido é exatamente o que codec de vídeo gosta. tinha escrito nas apostas, tinha repetido pro agente, e o &lt;a href=&quot;https://brennercruvinel.blog/blog/encoder-ignorou-o-tune-still-desde-o-primeiro-build/&quot;&gt;benchmark&lt;/a&gt; tinha me devolvido uma linha estranha: a variante com inter ficou maior que a sem. a configuração estava errada, scene cut detection ligado, e carta diferente atrás de carta diferente é um scene cut por frame. o encoder gastava ciclo procurando uma cena que não existia. desliguei e montei um segundo corpus, quase três mil reprints reais da mesma arte, agrupadas.&lt;/p&gt;
&lt;p&gt;aí sim. com predição em grupos de dezesseis frames o arquivo ficou 29% menor que sem predição. grupos de dezesseis ganharam de um keyframe só pro stream inteiro, o que me surpreendeu, e ainda deixam qualquer carta acessível decodificando no máximo dezesseis frames. e um detalhe que eu não esperava: embaralhar as reprints em vez de agrupar custou só dois pontos. parte do ganho vem da moldura que toda carta compartilha, não da arte repetida. o codec estava aproveitando o chrome, como eu tinha desconfiado no primeiro dia, e isso valia pra qualquer ordem.&lt;/p&gt;
&lt;h2 id=&quot;buildando-o-corpus-inteiro-de-madrugada&quot;&gt;buildando o corpus inteiro de madrugada&lt;/h2&gt;
&lt;p&gt;com 29% na mão, buildei o corpus completo com ordenação por similaridade e predição ligada, e do lado o mesmo corpus sem nada disso, pra comparar. e buildei com a mídia dentro do arquivo, porque a essa hora eu já não aceitava mp4 do lado do banco. antes de chegar nisso eu abri a pasta de um build intermediário e tinha .build, .cache, .forge-state, .tmp, lock, manifest, tudo do lado de um .nest de poucos megas. pra que isso? perguntei, e a resposta honesta era que cada peça tinha motivo e nenhuma tinha que estar ali quando o build termina. a mídia dentro do arquivo era a metade da resposta, a outra era limpar a saída.&lt;/p&gt;
&lt;p&gt;o .nest é um container de seções, cada uma com seu hash. a seção nova é a mais simples de todas: uma tabela de posições e os bytes dos vídeos, crus, colados um atrás do outro. um mp4 continua mp4, nada é recodificado pra caber. abrir o arquivo não toca na mídia, o sistema só mapeia o trecho quando alguém pede uma carta, e dá pra exportar o mp4 original de volta com o hash checado antes de gravar.&lt;/p&gt;
&lt;p&gt;essa seção não entra no hash de conteúdo do nest, e isso importa mais do que parece. o hash de conteúdo é calculado sobre o que está dentro do arquivo, não sobre a embalagem, e é ele que faz uma citação do nest apontar pra um pedaço de conhecimento em vez de pra um lugar no disco. o mesmo corpus buildado com a mídia dentro e com a mídia fora dá o mesmo hash. custou três por cento: os índices, o texto das cartas e os vetores somam uns quarenta megas em cima de 1,3 GB de vídeo.&lt;/p&gt;
&lt;p&gt;os builds rodaram de madrugada, e a madrugada foi ruim. dois morreram com o disco cheio, porque o macos estava tirando snapshot de sistema em ondas, comendo e soltando giga sem aviso, e a certa altura sobraram cento e trinta megas livres. e no meio disso eu li um resumo do agente dizendo que o corpus tinha 200 cartas. eu tinha reunião com o cliente em poucas horas.&lt;/p&gt;
&lt;p&gt;&quot;sério que você usou 200 cartas de demo? tenho reunião com o cliente e não tenho o dataset ainda.&quot; mandei rodar a validação no arquivo de 1,3 GB. os 38 mil blobs bateram um a um. o corpus estava inteiro, o resumo é que mostrava o piloto, e eu tinha lido o resumo em vez do disco. depois do encoder que não tava usando o tune que o manifest dizia, era a segunda vez na semana que a coisa que eu li não era a coisa que estava lá.&lt;/p&gt;
&lt;h2 id=&quot;no-final-do-dia&quot;&gt;no final do dia&lt;/h2&gt;
&lt;p&gt;o build com ordenação ficou do lado do build simples. 1,3 GB os dois, diferença de nada. mandei pro agente às quatro da manhã: no final do dia tá a mesma coisa né, 1,3 GB.&lt;/p&gt;
&lt;p&gt;o motivo estava no manifest, dessa vez certo. o nest decide por segmento se usa predição ou não, encodando uma amostra dos dois jeitos. no corpus real, de carta única, o probe vetou a predição em todos os segmentos. não tem o que prever: a carta seguinte é outra carta, com outra arte, e o chrome compartilhado não paga o custo. o ganho de 29% só existe no corpus de reprints, e esse eu ainda não buildei inteiro.&lt;/p&gt;
&lt;p&gt;teve uma parte pior, que eu só entendi depois. numa versão anterior do probe, ele decidia só por bytes, e num build da amostra escolheu predição em todos os segmentos porque empatava em tamanho. a qualidade mediana caiu dezesseis pontos. o encoder comprime frame previsto com quantização mais grossa, e em carta única o frame previsto é um keyframe pior disfarçado. bytes iguais não são qualidade igual, e o probe agora mede os dois lados, aceita perder até dois pontos, e grava o motivo quando veta. a matriz de 29% também compara só bytes, então o número é real mas não é qualidade equivalente. deixo isso dito aqui porque eu mesmo esqueci disso por um dia.&lt;/p&gt;
&lt;h2 id=&quot;e-se-o-arquivo-nao-precisar-ser-bonito&quot;&gt;e se o arquivo não precisar ser bonito&lt;/h2&gt;
&lt;p&gt;o que eu levo da madrugada não é o 1,3 GB. é que a minha intuição sobre codec estava errada duas vezes, e que eu leio resumo quando devia ler arquivo. agora eu só ligo ordenação quando o corpus tem near-dup, e nenhum build entra sem a validação do arquivo real rodada junto.&lt;/p&gt;
&lt;p&gt;e ficou uma pergunta que me pareceu mais promissora do que qualquer ordenação. o gate de qualidade do nest trava o codec num nível conservador porque as métricas visuais nunca passam em carta pequena com letra miúda. mas o app do felipe tem o jpeg de cada carta em cache pra exibir. o .nest só precisa responder qual carta. se o arquivo não precisa ser bonito, até onde dá pra subir o ajuste antes da busca reclamar?&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="pt-BR">
        <title>O Encoder Ignorou o tune=still Desde o Primeiro Build</title>
        <published>2026-09-02T00:00:00+00:00</published>
        <updated>2026-09-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            Brenner Cruvinel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://brennercruvinel.blog/blog/encoder-ignorou-o-tune-still-desde-o-primeiro-build/"/>
        <id>https://brennercruvinel.blog/blog/encoder-ignorou-o-tune-still-desde-o-primeiro-build/</id>
        
        <content type="html" xml:base="https://brennercruvinel.blog/blog/encoder-ignorou-o-tune-still-desde-o-primeiro-build/">&lt;p&gt;o sintoma foi um número que não batia. eu tinha uma bateria de encoders rodando em paralelo: dezenove configurações entre av1, avif, x264, x265, webp e jpeg xl com perda. até vvc entrou, mais por curiosidade do que por esperança. cada uma calibrada pra mesma qualidade visual, pra saber se o av1 do nest era o mais compacto ou só o mais próximo de casa. um dos agentes reproduziu o build do nest na mão, com os mesmos parâmetros, e o arquivo saiu com outro tamanho. mesma carta, mesmo crf, mesmo preset, bytes diferentes. eu li isso de manhã e minha primeira reação foi que o agente tinha errado o comando.&lt;/p&gt;
&lt;h2 id=&quot;o-probe-falhava-e-ninguem-avisava&quot;&gt;o probe falhava e ninguém avisava&lt;/h2&gt;
&lt;p&gt;não tinha errado. o forge pede pro encoder o tune=still, o modo de foto parada, que é o certo pra um stream onde cada frame é uma foto independente. antes de pedir, ele roda um probe pra ver se o encoder aceita esse modo. o probe rodava sem um parâmetro que o modo exige, falhava, e o código caía num modo genérico sem avisar ninguém. o manifest gravava a spec, que dizia &quot;still&quot;, não o que o encoder tinha feito. desde o &lt;a href=&quot;https://brennercruvinel.blog/blog/nest-archiver-de-cartas-de-magic/&quot;&gt;primeiro build&lt;/a&gt; eu vinha lendo um manifest que não batia com o binário, e o único lugar que registrava o modo era o lugar errado.&lt;/p&gt;
&lt;p&gt;a diferença mediu onze pontos de qualidade visual por dez por cento a mais de bytes, no mesmo crf. o build antigo era o pior ponto da curva: menor, mas feio de um jeito que não compensa. e ninguém teria achado olhando o vídeo, porque a cara do frame no player não diz qual tune foi usado. só bytes contra bytes de um encode independente. e foi por acaso: a bateria de encoders existia pra responder outra pergunta, e o encode manual do baseline era só o ponto de referência dela. se eu tivesse calibrado a bateria contra o manifest em vez de contra um encode feito do zero, o bug teria continuado invisível, e todos os números da série teriam saído dez por cento otimistas em bytes e onze pontos pessimistas em qualidade. agora o manifest grava o modo que o encoder de fato usou, e o probe passa o parâmetro certo.&lt;/p&gt;
&lt;p&gt;e mesmo depois do fix tinha uma segunda camada. o svt-av1 tem dois modos que dá pra chamar de &quot;still&quot;: um otimizado pra uma métrica de similaridade estrutural, que foi o que o probe consertada passou a escolher, e outro chamado de qualidade de imagem, que só funciona em stream todo de keyframe. o segundo rende mais um ponto de qualidade por um por cento de bytes. pouco, mas é o modo certo pra foto parada, e o build recomendado do corpus inteiro saiu com ele. o manifest agora diz qual dos dois, em número, porque &quot;still&quot; sozinho já me enganou uma vez.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;variante nas mesmas 2048 cartas&lt;/th&gt;&lt;th&gt;tamanho&lt;/th&gt;&lt;th&gt;qualidade visual&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;jpeg xl repack, sem perda&lt;/td&gt;&lt;td&gt;1,12x menor&lt;/td&gt;&lt;td&gt;igual à fonte&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;av1 do primeiro build&lt;/td&gt;&lt;td&gt;3,3x menor&lt;/td&gt;&lt;td&gt;52&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;av1 no tune=still&lt;/td&gt;&lt;td&gt;3,0x menor&lt;/td&gt;&lt;td&gt;63&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;av1 com inter&lt;/td&gt;&lt;td&gt;2,5x menor&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;avif&lt;/td&gt;&lt;td&gt;5,5x menor&lt;/td&gt;&lt;td&gt;43&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;a linha do avif parece a vencedora e não é. está num ponto muito mais baixo da curva de qualidade, não dá pra comparar pelo mesmo número. calibrado pra qualidade igual à do av1, ele fica uns 12% menor, e isso deixou uma pergunta aberta sobre qual backend usar pra stills. e o inter, que era minha aposta favorita, ficou maior que o tune=still. isso eu explico no próximo post, porque a configuração estava errada e o problema era outro.&lt;/p&gt;
&lt;p&gt;o resto da bateria de dezenove eu não vou tabelar, mas dois achados ficaram comigo. o x264, codec de 2003, foi o mais rápido dentro da janela de qualidade por uma margem enorme, e o vvc, o codec mais novo da lista, custou cem vezes o tempo do x264 pra ficar no meio da tabela. e dois presets diferentes do svt-av1 cuspiram streams byte a byte idênticos, porque a versão que eu uso mapeia os dois pro mesmo modo interno quando o stream é todo de keyframe. mais um caso de parâmetro que eu passava e o encoder ignorava, dessa vez documentado. e o jpeg xl com perda, que eu esperava competir, só compete em qualidade alta, bem acima da janela que interessa pra tile de deck. é um codec bom pra um alvo que esse corpus não pede.&lt;/p&gt;
&lt;h2 id=&quot;o-capcut-tava-jogando-carta-fora&quot;&gt;o capcut tava jogando carta fora&lt;/h2&gt;
&lt;p&gt;eu tinha escrito nas &lt;a href=&quot;https://brennercruvinel.blog/blog/apostas-antes-de-comprimir-as-cartas/&quot;&gt;apostas&lt;/a&gt; que acelerar o vídeo cortava bytes, porque tinha feito isso no capcut e o arquivo saiu com metade do tamanho. reproduzi programaticamente: acelerar sessenta vezes, exportar a trinta frames por segundo em h.264, do jeito que o editor faz. o arquivo saiu com metade dos bytes. e com metade das cartas.&lt;/p&gt;
&lt;p&gt;o export amostra a linha do tempo acelerada e descarta o que cai entre um frame e outro. das 2048 cartas, 993 sumiram. o que sobrou foi re-encodado em h.264 em cima de av1 que já tinha perda. o capcut não comprimiu nada, jogou metade do dataset fora e chamou de otimização, e eu olhei o tamanho do arquivo e acreditei. mudar só o fps, sem acelerar, não muda um byte. é anotação de tempo no container, como o ffprobe já tinha me dito no primeiro dia e eu não tinha registrado direito.&lt;/p&gt;
&lt;p&gt;a aposta errada rendeu um bug certo. com fps diferente de um no manifest, o código que busca frame pelo índice pedia um instante que não existia, porque assumia que ordinal e timestamp eram a mesma coisa. corrigido, e testado com o fps que eu nunca deveria ter mudado. o ganho real que o capcut apontava, redundância entre frames vizinhos, existe. só que ele se mede com inter, sem perder carta, e é o assunto da madrugada seguinte.&lt;/p&gt;
&lt;h2 id=&quot;o-que-mais-o-manifest-esta-me-contando-errado&quot;&gt;o que mais o manifest está me contando errado&lt;/h2&gt;
&lt;p&gt;fechei o dia com duas coisas validadas por hash e uma dúvida maior do que quando comecei. o modo do encoder era uma constante que eu achava que estava ligada, e estava desligada desde o começo. o capcut era um número que eu achava que era compressão, e era perda de dado. os dois estavam num manifest ou numa exportação que eu tinha lido e aceitado sem checar contra nada.&lt;/p&gt;
&lt;p&gt;se o encoder não tava fazendo o que o manifest dizia, a pergunta que ficou foi o que mais eu estava lendo em resumo em vez de validar no disco. eu não sabia ainda que a resposta ia chegar de madrugada, com reunião marcada.&lt;/p&gt;
</content>
        
    </entry>
</feed>
