ffmpeg: экспорт в 24-битный wave с Microsoft PCM, не расширяемый (с Lavf58.76.100)

ffmpeg: экспорт в 24-битный wave с Microsoft PCM, не расширяемый (с Lavf58.76.100)

Я заметил, что когда я использую FFMPEG для экспорта мода в волну в 24 бита или выше, FFMPEG решает использовать расширяемое кодирование вместо обычного кодирования PCM и использует "Lavf58.76.100", что бы это ни было. Я использовал exiftool для проверки экспортированного wav, и вот результат:

---- File ----
File Type                       : WAV
File Type Extension             : wav
MIME Type                       : audio/x-wav
---- RIFF ----
Encoding                        : Extensible
Num Channels                    : 2
Sample Rate                     : 48000
Avg Bytes Per Sec               : 288000
Bits Per Sample                 : 24
Software                        : Lavf58.76.100

Проблема в том, что многие программы не понимают этот расширяемый формат wav. Есть ли способ заставить FFMPEG использовать обычный PCM? Я заметил, что другие программы, такие как библиотека bass, могут экспортировать в 24-битный wav, используя обычное кодирование PCM.

Вот команда, которую я использую:

ffmpeg -y -loglevel error -f libopenmpt -i c:\temp\sometrack.IT -map_metadata -1 -c:a pcm_s24le c:\temp\sometrack.wav

Редактировать

Пока я писал этот вопрос, я был немного расстроен и мне не хватало знаний. Теперь все предельно ясно: файлы wave с битовой глубиной выше 16 бит должны иметь тег WAVE_FORMAT_EXTENSIBLE (и это имеет смысл, см. ответ). Программное обеспечение, которое я использовал для рендеринга файлов mod в wave, кроме FFmpeg, не соблюдало это правило. Спасибо, Том Ян, за разъяснения.

решение1

(Не все мои комментарии были точными/правильными, поэтому, если вам интересно, обратитесь к следующей информации.)

ТеоретическиГоворящий,PCMWAVEFORMATможет использоваться для аудио с глубиной цвета более 16 бит. Структура заголовка не накладывает никаких ограничений, которые мешали бы поддерживать такое аудио.

Однако, по-видимому, есть несколько причин, по которым ffmpeg не записывает заголовок WAVE в таком формате для такого типа аудио.

Во-первых, формат был замененWAVEFORMATEX, и, согласно документации:

...

wBitsPerSample

... If wFormatTag is WAVE_FORMAT_PCM, then wBitsPerSample should be equal to
8 or 16. ...

...

В дополнение к вышеуказанному требованию, не существует wFormatTagдругого значения, кроме WAVE_FORMAT_EXTENSIBLEтого, которое определено для звука PCM с битовой глубиной выше 16.

Будут ли «расширения», определенные вWAVEFORMATEXTENSIBLEможет быть опущен, когда wFormatTagэто WAVE_FORMAT_EXTENSIBLEне совсем понятно.WAVEFORMATEX, утверждается, что:

...

wFormatTag


... When this structure is included in a WAVEFORMATEXTENSIBLE structure,
this value must be WAVE_FORMAT_EXTENSIBLE. ...

...

При таком заявлении, вероятно, никто не будет/не должен предполагать, что это вообще разрешено.

Если вы читаетеWAVEFORMATEXиWAVEFORMATEXTENSIBLEвнимательно, вы заметите, что настоящая причина существования последнего (с точки зрения битовой глубины) заключается в том, что он позволяет первому хранить размер контейнера, кратный 8, при этом «реальный» размер выборки хранится в одном из расширений, определенных во втором. Например, 24 и 20 соответственно для некоторого (отвратительного) 20-битного потока PCM.

Однако для справки: wavнасколько я могу судить, мультиплексор ffmpeg НЕ поддерживает (по крайней мере, не должным образом) только что упомянутый странный случай. (Я думаю, что оба поля будут записаны со значением 0, если такой поток не будет отклонен.)

Если вам действительно нужно, чтобы ffmpeg записал заголовок в формате PCMWAVEFORMATдля 24-битного звука, вы можете рассмотреть возможность его сборки с помощью следующего патча:

diff --git a/libavformat/riff.h b/libavformat/riff.h
index 85d6786663..5794857f53 100644
--- a/libavformat/riff.h
+++ b/libavformat/riff.h
@@ -57,6 +57,11 @@ void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par, int for_asf, int
  */
 #define FF_PUT_WAV_HEADER_SKIP_CHANNELMASK      0x00000002
 
+/**
+ * Tell ff_put_wav_header() not to write WAVEFORMATEXTENSIBLE extensions if possible.
+ */
+#define FF_PUT_WAV_HEADER_FORCE_PCMWAVEFORMAT      0x00000004
+
 /**
  * Write WAVEFORMAT header structure.
  *
diff --git a/libavformat/riffenc.c b/libavformat/riffenc.c
index ffccfa3d48..4dc8ca6e0f 100644
--- a/libavformat/riffenc.c
+++ b/libavformat/riffenc.c
@@ -80,9 +80,9 @@ int ff_put_wav_header(AVFormatContext *s, AVIOContext *pb,
     waveformatextensible = (par->channels > 2 && par->channel_layout) ||
                            par->channels == 1 && par->channel_layout && par->channel_layout != AV_CH_LAYOUT_MONO ||
                            par->channels == 2 && par->channel_layout && par->channel_layout != AV_CH_LAYOUT_STEREO ||
-                           par->sample_rate > 48000 ||
                            par->codec_id == AV_CODEC_ID_EAC3 ||
-                           av_get_bits_per_sample(par->codec_id) > 16;
+                           ((par->sample_rate > 48000 || av_get_bits_per_sample(par->codec_id) > 16) &&
+                            !(flags & FF_PUT_WAV_HEADER_FORCE_PCMWAVEFORMAT));
 
     if (waveformatextensible)
         avio_wl16(pb, 0xfffe);
diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c
index 2317700be1..bd41d6eeb3 100644
--- a/libavformat/wavenc.c
+++ b/libavformat/wavenc.c
@@ -83,6 +83,7 @@ typedef struct WAVMuxContext {
     int peak_block_pos;
     int peak_ppv;
     int peak_bps;
+    int extensible;
 } WAVMuxContext;
 
 #if CONFIG_WAV_MUXER
@@ -324,9 +325,10 @@ static int wav_write_header(AVFormatContext *s)
     }
 
     if (wav->write_peak != PEAK_ONLY) {
+        int flags = !wav->extensible ? FF_PUT_WAV_HEADER_FORCE_PCMWAVEFORMAT : 0;
         /* format header */
         fmt = ff_start_tag(pb, "fmt ");
-        if (ff_put_wav_header(s, pb, s->streams[0]->codecpar, 0) < 0) {
+        if (ff_put_wav_header(s, pb, s->streams[0]->codecpar, flags) < 0) {
             av_log(s, AV_LOG_ERROR, "Codec %s not supported in WAVE format\n",
                    avcodec_get_name(s->streams[0]->codecpar->codec_id));
             return AVERROR(ENOSYS);
@@ -494,6 +496,7 @@ static const AVOption options[] = {
     { "peak_block_size", "Number of audio samples used to generate each peak frame.",   OFFSET(peak_block_size), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, 65536, ENC },
     { "peak_format",     "The format of the peak envelope data (1: uint8, 2: uint16).", OFFSET(peak_format), AV_OPT_TYPE_INT,     { .i64 = PEAK_FORMAT_UINT16 }, PEAK_FORMAT_UINT8, PEAK_FORMAT_UINT16, ENC },
     { "peak_ppv",        "Number of peak points per peak value (1 or 2).",              OFFSET(peak_ppv), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 2, ENC },
+    { "extensible",      "Write WAVEFORMATEXTENSIBLE extensions.",                      OFFSET(extensible), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC },
     { NULL },
 };
 

Затем, добавив -extensible 0перед путем/именем выходного файла, вы сможете получить то, что вы называете «обычным» 24-битным WAVE-файлом.

Связанный контент