Aprendí a transcodificar videos MKV a MP4 con codificación de video HEVC (x265) para reducir el tamaño de los archivos de manera que sean compatibles con iOS. Pero si bien el proceso es excelente (y la compresión es excelente con un tamaño de archivo bajo), cuando intento fusionar subtítulos en un video específico, en el video MP4 resultante obtengo un montón de errores de 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
Estoy usando macOS Mojave (10.15.2) con FFmpeg 4.2.1 instalado a través de Homebrew, pero el problema persiste incluso si descargo elconstrucción nocturna(ffmpeg-4.2.1-macos64-static, 20191215-9fe0790) y use ese binario en lugar de la versión instalada de Homebrew.
El problema es que tengo este video que convertí exitosamente a MP4 con video x264 y audio AAC en el pasado y también pude fusionar subtítulos SRT en el archivo resultante sin problemas. Pero cuando creo un MP4 usando un video HEVC (x265) hoy desde exactamente la misma fuente, la combinación de subtítulos SRT falla con esos errores "pts no tiene valor" y "está fuera del rango para el formato mov/mp4".
Este es el comando que uso para crear el video MP4 HEVC (x265) desde una fuente 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 \
;
Y este es el comando que se ha utilizado con éxito en el pasado para fusionar subtítulos SRT en un MP4 existente sin volver a codificarlo:
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 \
;
Creo que el problema podría ser que aproximadamente el 50% del vídeo no tiene subtítulos; Sólo en el segundo 50% del vídeo se necesitan subtítulos.
El vídeo en cuestión dura aproximadamente 2 horas. Y durante los primeros 50 minutos aproximadamente, no hay necesidad de subtítulos en inglés. Pero alrededor de los 50 minutos, es cuando aparecen los subtítulos.
Entonces los subtítulos en el SRT comienzan así:
1
00:52:33,123 --> 00:52:50,123
It was a dark and stormy night…
Pero cuando ejecuto el comando FFmpeg anterior, el resultado es algo como esto; veces un poco manipulado por el propósito del ejemplo:
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
Y toneladas de mensajes similares hasta, ¡et voilá! La fusión finaliza, no se ven subtítulos y eso es todo.
¡Esto me está volviendo loco! Quiero decir, si uso el mismo comando, pero especifico un tiempo de búsqueda que comienza aproximadamente en el punto donde entran los subtítulos, en realidad veo los subtítulos en el 50% del video que los necesita:
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 \
;
Pero necesito más del 50% del vídeo, claro. Diablos, incluso acabo de probar este comando; Puedo hacer que los subtítulos se fusionen con el MP4 si configuro la búsqueda en cualquier valor igual o mayor que 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 \
;
Pero, ¿qué tienen de mágico 16,75 minutos (1.005 segundos) en este contexto?
¿Por qué puedo fusionar los subtítulos si solo selecciono las veces que aparecen los subtítulos en el segundo 50% del video pero no si ejecuto el comando para fusionar el video completo?
FWIW, si ejecuto un comando similar para crear un MKV del video, ¡todo está bien!
ffmpeg -i output_hevc.mp4 \
-i input.srt \
-c:v copy -c:a copy -c:s copy \
output.mkv \
;
mov_text
Parece que de alguna manera esta fusión hace que el proceso falle.
Y si agrego un subtítulo falso al principio del archivo como este:
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…
¡Todo funciona como se desea! Excepto por tener la palabra "¡Foo!" apareciendo durante el 50% del vídeo. Obviamente no es ideal.
¿Hay alguna manera de evitar esto? ¿Es esto un error de FFmpeg o quizás un problema con el video HEVC (x265) con subtítulos fusionados?
Respuesta1
Parece que agregar un subtítulo falso al comienzo del archivo SRT que se extiende desde el comienzo del video hasta aproximadamente donde comienzan los subtítulos solucionó este problema.
Esta solución es claramente un "truco", pero funciona.
Dejando de lado mi idea de agregar un subtítulo falso al principio del archivo SRT, me di cuenta de que los subtítulos SRT, segúnlas especificaciones SRT— permitir etiquetas HTML. ¡Sabiendo que agregué el siguiente subtítulo falso y todo funciona!
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…
¡Eso es todo! Simplemente agregar una etiqueta vacía en negrita permite que todo funcione y que los subtítulos se fusionen...
Pero, como se indicó al principio, esto es claramente un truco y estoy abierto a escuchar más de otras personas que saben más sobre FFmpeg. Sólo puedo asumir que nada de este problema refleja el comportamiento deseado y debe haber una forma más elegante de abordar casos como este. ¿O es esto un error (no una característica) y debería informarse?
Respuesta2
Tuve exactamente este problema con un archivo multimedia con el que estoy trabajando y pasé la mayor parte del día probando diferentes comandos y diferentes formas de reconstruir los pts, todo en vano antes de que finalmente me topara con esta publicación. Para mi gran tristeza, la solución propuesta aquí no funcionó para mí.
Estoy ejecutando Windows 11 usando los binarios compilados para Windows por gyan.dev, sin embargo, también probé los mismos comandos a través de WSL (Subsistema de Windows para Linux) ejecutando ubuntu e instalándolo a través de apt.
Al insertar las etiquetas vacías seguí recibiendo los mismos errores:
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
Si, en cambio, insertara un solo carácter visible, como un solo punto, el archivo srt se fusionaría sin problemas. Por supuesto, entonces me quedaría atrapado con un punto en la pantalla durante los primeros 40 minutos de mi vídeo.
0
00:00:00,000 --> 00:18:00,000
.
1
00:43:21,472 --> 00:43:24,933
Mysterious translated text here
Probé con la etiqueta de negrita, cursiva y cualquier otra etiqueta que se me ocurrió, incluso la etiqueta de párrafo que no se reconoce. Cualquier etiqueta vacía no activará el tiempo que se debe conservar y provocará errores de "pts no tiene valor".
Desesperado, decidí utilizar un enfoque de cantidad por encima de calidad agregando 10 entradas espaciadas 2 minutos con una duración muy baja, de modo que en lugar de un carácter de punto visible sólido tuviera uno intermitente y funcionó. Entonces, por capricho, intenté establecer la hora de inicio y la hora de finalización de las primeras 10 entradas para que fueran las mismas y eso resultó en que el carácter de punto se ocultara y el subtítulo se fusionara sin error:
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
Al igual que la publicación original aquí, esta es una solución complicada y no una solución adecuada, pero también, al igual que la publicación original aquí, no pude encontrar una solución adecuada. Espero que esto ayude a cualquiera que tenga este problema y se tope con esta publicación como lo hice yo.
Si tienes un srt grande que temes tener que incrementar a mano, hice que GPT creara este pequeño script de Python para mí (podría haberlo escrito yo mismo, pero ya casi había terminado de luchar contra este problema en este punto y pensé que era suficientemente simple para que GPT lo maneje)
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: Resulta que, dependiendo de su reproductor, configurar la hora de inicio y finalización de las primeras 10 entradas da como resultado que el período esté oculto o visible durante 5 segundos. Establecer un desplazamiento de 1 milisegundo parece ser mucho más confiable para mantener oculto el período.
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