Problemas para mesclar legendas SRT com um MP4 (HEVC/x265/mov_text) transcodificado para iOS: “pts não tem valor” e “está fora do intervalo para o formato mov/mp4”

Problemas para mesclar legendas SRT com um MP4 (HEVC/x265/mov_text) transcodificado para iOS: “pts não tem valor” e “está fora do intervalo para o formato mov/mp4”

Aprendi como transcodificar vídeo MKV para MP4 com codificação de vídeo HEVC (x265) para diminuir os arquivos de uma forma que torne tudo compatível com iOS. Mas embora o processo seja ótimo - e a compactação seja ótima com tamanho de arquivo baixo - quando tento mesclar legendas em um vídeo específico, o vídeo MP4 resultante recebo uma pilha de erros do FFmpeg como este:

[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3152805999 / timestamp: 3154741000 is out of range for mov/mp4 format

Estou usando o macOS Mojave (10.15.2) com FFmpeg 4.2.1 instalado via Homebrew, mas o problema persiste mesmo se eu baixar oConstrução noturna(ffmpeg-4.2.1-macos64-static, 20191215-9fe0790) e use esse binário em vez da versão instalada do Homebrew.

O problema é que tenho este vídeo que converti com sucesso para um MP4 com vídeo x264 e áudio AAC no passado e consegui mesclar legendas SRT no arquivo resultante sem problemas. Mas quando eu crio um MP4 usando vídeo HEVC (x265) hoje da mesma fonte exata, a mesclagem de legendas SRT falha com os erros “pts não tem valor” e “está fora do intervalo para o formato mov/mp4”.

Este é o comando que uso para criar o vídeo HEVC (x265) MP4 a partir de uma fonte MKV:

ffmpeg -i input.mkv \
       -map_metadata -1 \
       -vf scale=-1:720 \
       -c:v libx265 -crf 20 -c:a aac -b:a 128k \
       -threads 4 \
       -tag:v hvc1 -sn \
       -map 0:0 -map 0:1 output_hevc.mp4 \
       ;

E este é o comando usado com sucesso no passado para mesclar legendas SRT em um MP4 existente sem recodificação:

ffmpeg -i output_hevc.mp4 \
       -i input.srt \
       -c:v copy -c:a copy \
       -c:s mov_text -metadata:s:s:0 language=eng \
       output_final.mp4 \
       ;

Acho que o problema pode ser que cerca de 50% do vídeo não há legendas; apenas nos segundos 50% do vídeo são necessárias legendas.

O vídeo em questão tem cerca de 2 horas de duração. E durante os primeiros 50 minutos, não há necessidade de legendas em inglês. Mas por volta dos 50 minutos é quando as legendas entram em ação.

Então as legendas no SRT começam assim:

1
00:52:33,123 --> 00:52:50,123
It was a dark and stormy night…

Mas quando executo o comando FFmpeg acima, a saída é mais ou menos assim; vezes um pouco falsificado, por exemplo, o propósito:

Stream mapping:
  Stream #0:0 -> #0:0 (copy)
  Stream #0:1 -> #0:1 (copy)
  Stream #1:0 -> #0:2 (subrip (srt) -> mov_text (native))
Press [q] to stop, [?] for help
frame=25560 fps=0.0 q=-1.0 size=  304640kB time=00:52:00.00 bitrate= 791.7kbits/frame=50730 fps=50726 q=-1.0 size=  681984kB time=time=00:52:00.00 bitrate=1772.4kbit[mp4 @ 0x7facb9002000] Application provided duration: 3152137000 / timestamp: 3152137000 is out of range for mov/mp4 format
[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3152805999 / timestamp: 3154741000 is out of range for mov/mp4 format
[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3153246998 / timestamp: 3156809000 is out of range for mov/mp4 format
[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3154051997 / timestamp: 3159013000 is out of range for mov/mp4 format
[mp4 @ 0x7facb9002000] pts has no value
[mp4 @ 0x7facb9002000] Application provided duration: 3155556996 / timestamp: 3163817000 is out of range for mov/mp4 format

E toneladas de mensagens semelhantes até, et voila! A mesclagem termina, nenhuma legenda fica visível e pronto.

Isso está me deixando louco! Quero dizer, se eu usar o mesmo comando, mas especificar um tempo de busca que começa próximo ao ponto em que as legendas entram, eu realmente vejo as legendas em 50% do vídeo que precisa delas:

ffmpeg -I output_hevc.mp4 \
       -i input.srt \
       -c:v copy -c:a copy \
       -c:s mov_text -metadata:s:s:0 language=eng \
       -ss 3120 \
       output_final.mp4 \
       ;

Mas preciso de mais de 50% do vídeo, é claro. Caramba, eu até tentei este comando; Posso fazer com que as legendas sejam mescladas com o MP4 se definir a busca para qualquer valor igual ou superior a 1005 segundos:

ffmpeg -i output_hevc.mp4 \
       -i input.srt \
       -c:v copy -c:a copy \
       -c:s mov_text -metadata:s:s:0 language=eng \
       -ss 1005 \
       output_final.mp4 \
       ;

Mas o que há de tão mágico em 16,75 minutos (1005 segundos) neste contexto?

Por que posso mesclar as legendas se eu selecionar apenas os horários em que as legendas aparecem nos segundos 50% do vídeo, mas não se eu executar o comando para mesclar o vídeo completo?

FWIW, se eu executar um comando semelhante para criar um MKV do vídeo, está tudo bem!

ffmpeg -i output_hevc.mp4 \
       -i input.srt \
       -c:v copy -c:a copy -c:s copy \
       output.mkv \
       ;

De alguma forma, essa fusão mov_textfaz com que o processo falhe, ao que parece.

E se eu adicionar uma legenda falsa no início do arquivo assim:

0
00:00:00,000 --> 00:16:75,000
Foo!

1
00:52:33,123 --> 00:52:50,123
It was a dark and stormy night…

Tudo funciona como desejado! Exceto por ter a palavra “Foo!” aparecendo em 50% do vídeo. Obviamente não é o ideal.

Existe alguma maneira de contornar isso? Isso é um bug do FFmpeg ou talvez um problema com vídeo HEVC (x265) com legendas sendo mescladas?

Responder1

Parece que adicionar uma legenda falsa no início do arquivo SRT que vai desde o início do vídeo até onde as legendas começam corrigiu esse problema.

Esta solução é claramente um “hack”, mas funciona.

Afastando-me da ideia de adicionar uma legenda falsa no início do arquivo SRT, percebi que as legendas SRT - de acordo comas especificações SRT— permite tags HTML. Sabendo que adicionei a seguinte legenda falsa e tudo funcionou!

0
00:00:00,000 --> 00:16:75,000
<b></b>

1
00:52:33,123 --> 00:52:50,123
It was a dark and stormy night…

É isso! Apenas adicionar uma tag em negrito vazia permite que tudo funcione e que as legendas sejam mescladas…


Mas – como afirmado no início – isso é claramente um hack e estou aberto a ouvir mais de outras pessoas que sabem mais sobre o FFmpeg. Só posso assumir que nada desta questão reflete o comportamento desejado e deve haver uma maneira mais elegante de lidar com casos como este. Ou isso é um bug (não um recurso) e deve ser relatado?

Responder2

Tive exatamente esse problema com um arquivo de mídia com o qual estou trabalhando e passei a maior parte do dia tentando diferentes comandos e diferentes maneiras de reconstruir os pts, tudo sem sucesso, antes de finalmente encontrar este post. Para minha grande tristeza, a solução proposta aqui não funcionou para mim.

Estou rodando no Windows 11 usando os binários compilados para Windows por gyan.dev, porém também tentei os mesmos comandos através do WSL (Windows Subsystem for Linux) rodando o ubuntu e instalando via apt.

Ao inserir as tags vazias continuei recebendo os mesmos erros:

0
00:00:00,000 --> 00:18:00,000
<b></b>

1
00:43:21,472 --> 00:43:24,933
Mysterious translated text here

[mp4 @ 0000023630f5cf80] Packet duration: 2601472000 / dts: 2601472000 is out of range
[mp4 @ 0000023630f5cf80] pts has no value
[mp4 @ 0000023630f5cf80] Packet duration: 2601681999 / dts: 2605143000 is out of range
[mp4 @ 0000023630f5cf80] pts has no value
[mp4 @ 0000023630f5cf80] Packet duration: 2602474998 / dts: 2608605000 is out of range
[mp4 @ 0000023630f5cf80] pts has no value
[mp4 @ 0000023630f5cf80] Packet duration: 2603642997 / dts: 2611441000 is out of range
[mp4 @ 0000023630f5cf80] pts has no value

Se, em vez disso, eu inserisse um único caractere visível, como um único ponto final, o arquivo srt seria mesclado sem problemas. É claro que eu ficaria preso com um ponto final na tela durante os primeiros 40 minutos do meu vídeo.

0
00:00:00,000 --> 00:18:00,000
.

1
00:43:21,472 --> 00:43:24,933
Mysterious translated text here

Tentei com a tag negrito, itálico e qualquer outra tag que consegui pensar, até mesmo a tag de parágrafo que não é reconhecida. Quaisquer tags vazias não acionariam o tempo a ser mantido e resultariam em erros 'pts has no value'.

Em desespero, decidi usar uma abordagem de quantidade em vez de qualidade, adicionando 10 entradas espaçadas de 2 minutos entre si e com uma duração muito baixa, de modo que, em vez de um caractere de período visível sólido, eu teria um intermitente e funcionou. Então, por capricho, tentei definir a hora de início e a hora de término das primeiras 10 entradas para serem iguais e isso resultou na ocultação do caractere de ponto final e na mesclagem da legenda sem erros:

1
00:02:00,000 --> 00:02:00,000
.

2
00:04:00,000 --> 00:04:00,000
.

3
00:06:00,000 --> 00:06:00,000
.

4
00:08:00,000 --> 00:08:00,000
.

5
00:10:00,000 --> 00:10:00,000
.

6
00:12:00,000 --> 00:12:00,000
.

7
00:14:00,000 --> 00:14:00,000
.

8
00:16:00,000 --> 00:16:00,000
.

9
00:18:00,000 --> 00:18:00,000
.

10
00:20:01,000 --> 00:20:01,000
.

11
00:43:21,472 --> 00:43:24,933
Mysterious translated text here

Como a postagem original aqui, esta é uma solução alternativa hacky e não uma solução adequada, mas também como a postagem original aqui, não consegui encontrar uma solução adequada. Espero que isso ajude qualquer pessoa que tenha esse problema e encontre esta postagem como eu.

Se você tem um srt grande que teme ter que incrementar manualmente, pedi ao GPT que preparasse este pequeno script Python para mim (eu mesmo poderia ter escrito, mas já estava quase cansado de lutar contra esse problema neste momento e percebi que era simples o suficiente para o GPT lidar)

def add_entries_to_srt(existing_file_path, new_file_path, num_entries=10, duration=0.1, buffer_time=120):
    with open(existing_file_path, 'r', encoding='utf-8') as existing_file:
        existing_content = existing_file.read()

    # Parse existing entries
    entries = existing_content.strip().split('\n\n')
    existing_entries_count = len(entries)

    # Generate new entries
    new_entries = []
    for i in range(1, num_entries + 1):
        start_time = i * (duration + buffer_time)
        end_time = start_time + duration
        entry_text = f"{i}\n{format_time(start_time)} --> {format_time(end_time)}\n<i>.</i>"
        new_entries.append(entry_text)

    # Increment entry numbers of existing entries
    for i in range(existing_entries_count):
        entry_lines = entries[i].split('\n')
        entry_number = int(entry_lines[0])
        entry_lines[0] = str(entry_number + num_entries)
        entries[i] = '\n'.join(entry_lines)

    # Combine new and existing entries
    combined_entries = '\n\n'.join(new_entries + entries)

    # Write to the new file
    with open(new_file_path, 'w', encoding='utf-8') as new_file:
        new_file.write(combined_entries)

def format_time(seconds):
    minutes, seconds = divmod(seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d},000"

# Replace 'existing_subtitle.srt' with your actual file path
existing_file_path = r'C:\Temp\ffmpeg\Subtitles.srt'
new_file_path = r'C:\Temp\ffmpeg\Subtitles.EDIT.srt'

add_entries_to_srt(existing_file_path, new_file_path)

EDITAR: Acontece que dependendo do seu player, definir o horário de início e término das primeiras 10 entradas resulta no mesmo no período oculto ou visível por 5 segundos. Definir um deslocamento de 1 milissegundo parece ser muito mais confiável para manter o período oculto.

1
00:02:00,000 --> 00:02:00,001
.

2
00:04:00,000 --> 00:04:00,001
.

3
00:06:00,000 --> 00:06:00,001
.

4
00:08:00,000 --> 00:08:00,001
.

5
00:10:00,000 --> 00:10:00,001
.

6
00:12:00,000 --> 00:12:00,001
.

7
00:14:00,000 --> 00:14:00,001
.

8
00:16:00,000 --> 00:16:00,001
.

9
00:18:00,000 --> 00:18:00,001
.

10
00:20:01,000 --> 00:20:01,001
.

11
00:43:21,472 --> 00:43:24,933
Mysterious translated text here

informação relacionada