Вопрос или проблема
Я пытаюсь сделать нарезки из сегментов речи из аудиофайла и объединить их вместе. Исходное аудио – это один трек PCM Wav файл с частотой 16 кГц и 16 бит.
Нарезка, сделанная с помощью -ss -i input.wav -t output.wav, имеет другую продолжительность, чем нарезка, сделанная с помощью -ss -i input.wav -t -acodec copy output.wav
Если быть точным:
- -ss 20.125 -i input.wav -t 10.125 output-reencode.wav дает нарезку с продолжительностью 10с:125мс
- -ss 20.125 -i input.wav -t 10.125 -acodec copy output-copy.wav дает нарезку с продолжительностью 10с:176мс
Почему разница составляет 51мс?
Безопасно ли сказать, что версия с перекодировкой не медленнее, потому что с PCM не связано сжатие?
C:\Users\someuser>ffmpeg.exe -ss 20.125 -i "C:\input.wav" -t 10.125 "C:\output-reencode.wav"
версия ffmpeg 7.0-essentials_build-www.gyan.dev Copyright (c) 2000-2024 разработчики FFmpeg
сборка с использованием gcc 13.2.0 (Rev5, сборка проекта MSYS2)
конфигурация: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-sdl2 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libgme --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libtheora --enable-libvo-amrwbenc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-librubberband
libavutil 59. 8.100 / 59. 8.100
libavcodec 61. 3.100 / 61. 3.100
libavformat 61. 1.100 / 61. 1.100
libavdevice 61. 1.100 / 61. 1.100
libavfilter 10. 1.100 / 10. 1.100
libswscale 8. 1.100 / 8. 1.100
libswresample 5. 1.100 / 5. 1.100
libpostproc 58. 1.100 / 58. 1.100
[aist#0:0/pcm_s16le @ 000001923d036b40] Определён макет канала: моно
Вход #0, wav, из 'C:\input.wav':
Метаданные:
кодировщик : Lavf61.1.100
таймкод : 00:00:00:00
Продолжительность: 00:27:33.20, битрейт: 256 kb/s
Поток #0:0: Аудио: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Гц, моно, s16, 256 кбит/с
Отображение потока:
Поток #0:0 -> #0:0 (pcm_s16le (родной) -> pcm_s16le (родной))
Выход #0, wav, в 'C:\output-reencode.wav':
Метаданные:
ISMP : 00:00:00:00
ISFT : Lavf61.1.100
Поток #0:0: Аудио: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Гц, моно, s16, 256 кбит/с
Метаданные:
кодировщик : Lavc61.3.100 pcm_s16le
[out#0/wav @ 000001923d029e80] видео:0KiB аудио:316KiB субтитры:0KiB другие потоки:0KiB глобальные заголовки:0KiB накладные расходы при мультиплексировании: 0.030247%
размер= 317KiB время=00:00:10.12 битрейт= 256.1kbits/s скорость=1.25e+03x
C:\Users\someuser>ffmpeg.exe -ss 20.125 -i "C:\input.wav" -acodec copy -t 10.125 "C:\output-copy.wav"
версия ffmpeg 7.0-essentials_build-www.gyan.dev Copyright (c) 2000-2024 разработчики FFmpeg
сборка с использованием gcc 13.2.0 (Rev5, сборка проекта MSYS2)
конфигурация: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-zlib --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-sdl2 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libgme --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libtheora --enable-libvo-amrwbenc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-librubberband
libavutil 59. 8.100 / 59. 8.100
libavcodec 61. 3.100 / 61. 3.100
libavformat 61. 1.100 / 61. 1.100
libavdevice 61. 1.100 / 61. 1.100
libavfilter 10. 1.100 / 10. 1.100
libswscale 8. 1.100 / 8. 1.100
libswresample 5. 1.100 / 5. 1.100
libpostproc 58. 1.100 / 58. 1.100
[aist#0:0/pcm_s16le @ 000001ad5f9c9b80] Определён макет канала: моно
Вход #0, wav, из 'C:\input.wav':
Метаданные:
кодировщик : Lavf61.1.100
таймкод : 00:00:00:00
Продолжительность: 00:27:33.20, битрейт: 256 kb/s
Поток #0:0: Аудио: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Гц, моно, s16, 256 кбит/с
Отображение потока:
Поток #0:0 -> #0:0 (копия)
Выход #0, wav, в 'C:\output-copy.wav':
Метаданные:
ISMP : 00:00:00:00
ISFT : Lavf61.1.100
Поток #0:0: Аудио: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Гц, моно, s16, 256 кбит/с
Нажмите [q], чтобы остановить, [?] для справки
[out#0/wav @ 000001ad5f9c9d40] видео:0KiB аудио:318KiB субтитры:0KiB другие потоки:0KiB глобальные заголовки:0KiB накладные расходы при мультиплексировании: 0.030095%
размер= 318KiB время=00:00:10.24 битрейт= 254.5kbits/s скорость=2.76e+03x
Аудиопоток PCM обычно обрабатывается кадрами с 1024 выборками на кадр.
При перекодировании с указателем продолжительности ffmpeg будет обрезать последний кадр, если это необходимо, чтобы максимально приблизить продолжительность. При копировании потока он будет работать только с полными кадрами.
Ваш аудиопоток имеет частоту 16000 Гц, поэтому один кадр имеет размер 1024/16000 = 64мс. 10.125с охватывает 158.203125 кадров. При копировании потока 159-й кадр будет полного размера (1024 выборки), так что выходная продолжительность составит 159 * 64мс = 10176мс
Когда вы копируете аудиопоток, ffmpeg будет делить только на ключевые кадры. Поскольку ключевые кадры не расположены на определенных отметках времени, с которых вы начинаете и останавливаетесь, нарезка получается неточной.
Когда вы перекодируете поток, ffmpeg не обращает внимания на существующие ключевые кадры, так как он создает новые через кодировщик.
Вы должны иметь возможность просмотреть временные метки ключевых кадров вашего медиафайла, используя что-то вроде ffprobe -loglevel error -skip_frame nokey -select_streams v:0 -show_entries frame=pkt_pts_time -of csv=print_section=0 input.mp4
Для дополнительной информации:
Ответ или решение
Разница в длительности срезов между перекодировкой и копированием кодека в FFmpeg
При работе с аудиофайлами в формате PCM Wav, особенно в контексте извлечения сегментов речи, важно понимать различия в подходах к манипуляциям с длиной срезов. В ваших примерах наблюдается разница в длительности между командами с перекодировкой и без, что вызывает вопросы о причинах такого поведения и возможных способах устранения несоответствий.
Основные моменты различий
- Метод извлечения: Перекодировка vs. Копирование кодека
- команда с перекодировкой:
ffmpeg -ss 20.125 -i input.wav -t 10.125 output-reencode.wav
- команда с копированием кодека:
ffmpeg -ss 20.125 -i input.wav -t 10.125 -acodec copy output-copy.wav
- команда с перекодировкой:
При перекодировке FFmpeg обрабатывает аудиоданные и может точно обрезать нужные сегменты, создавая новые ключевые кадры. Таким образом, он может корректировать длительность до десятых и сотых долей секунды. В случае копирования кодека, FFmpeg работает с целыми фреймами, определённый размер которых и определяет конечную продолжительность извлечённого сегмента.
- Структура PCM и размеры фреймов
- Ваш аудиопоток имеет частоту 16000 Гц и размер фрейма 1024 семпла. Это означает, что каждый фрейм составляет приблизительно 64 мс.
- Длительность 10.125 секунд равна 158.203125 фреймам. Когда FFmpeg копирует потоки, он обрабатывает целые фреймы, и 159-й фрейм будет полный, что приводит к увеличению полученной длительности (10176 мс).
Причина разницы в 51 мс
Разница в 51 мс, которую вы заметили, возникает из-за того, что при копировании кодека FFmpeg не может обрезать поток на произвольных временных отметках. Он должен остановиться на начале следующего фрейма, что приводит к удлинению длительности выходного файла. Таким образом, итоговая длительность в случае копирования оказывается больше, чем при перекодировке, что объясняет не только увеличение времени, но и возможную потерю точности при извлечении.
Сохранение точности при обрезке
Если ваша задача заключается в максимально точном извлечении аудио, рекомендуется использовать метод перекодировки, поскольку он позволяет гибко настраивать временные отметки и способствует более детальному контролю над длительностью выходного сегмента.
Заключение
Разница в срезах при использовании методов перекодирования и копирования кодека заключается в том, как FFmpeg обращается с временными метками и фреймами. Применение методики перекодировки позволяет избежать неточностей при извлечении и гарантирует, что конечный продукт будет отвечать вашим требованиям по длительности. Для получения дополнительной информации по вопросам работы с ключевыми кадрами и другими параметрами аудиопотока, вы можете обратиться к ресурсам, связанным с FFmpeg, таким как официальная документация или специализированные форумы.