Skip to content

Commit 9d23884

Browse files
authored
Preserve redundant cbsize = 0 with --keep-foreign-metadata (#878)
Usually, the cbSize field is left out when no WAVEFORMATEXTENSIBLE data is included. However, encoders may also choose to leave it in and set it to 0 to indicate that there is no extra format information, in accordance with the [WAVEFORMATEX] structure. Such files would cause an error when decoding the flac file with --keep-foreign-metadata. This solution handles WAV, RF64 and WAVE64 files using the WAVEFORMATEX structure with no extra format information. [WAVEFORMATEX]: http://web.archive.org/web/20250815212210/https://learn.microsoft.com/en-us/previous-versions/dd757713(v=vs.85) Closes #808
1 parent d118937 commit 9d23884

File tree

3 files changed

+200
-10
lines changed

3 files changed

+200
-10
lines changed

src/flac/decode.c

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ static int DecoderSession_finish_ok(DecoderSession *d);
119119
static int DecoderSession_finish_error(DecoderSession *d);
120120
static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, uint32_t sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input);
121121
static FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uint64 samples);
122-
static FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, uint32_t bps, uint32_t channels, uint32_t sample_rate, FLAC__uint32 channel_mask);
122+
static FLAC__uint64 calculate_total_riff_wave_fmt_chunk_size(FileFormat format, FLAC__bool is_waveformatextensible, FLAC__bool preserve_dummy_cbsize);
123+
static FLAC__uint64 calculate_riff_wave_fmt_chunk_size_field(FLAC__bool is_waveformatextensible, FLAC__bool preserve_dummy_cbsize);
124+
static FLAC__uint64 calculate_wave64_fmt_chunk_size_field(FLAC__bool is_waveformatextensible, FLAC__bool preserve_dummy_cbsize);
125+
static FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, FLAC__bool preserve_dummy_cbsize, uint32_t bps, uint32_t channels, uint32_t sample_rate, FLAC__uint32 channel_mask);
123126
static FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, uint32_t bps, uint32_t channels, uint32_t sample_rate, FileFormat format, FileSubFormat subformat, FLAC__uint32 comm_length);
124127
static FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 val);
125128
static FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val);
@@ -739,6 +742,7 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
739742

740743
FLAC__uint64 iff_size;
741744
uint32_t foreign_metadata_size = 0; /* size of all non-audio non-fmt/COMM foreign metadata chunks */
745+
FLAC__bool preserve_dummy_cbsize = false;
742746
foreign_metadata_t *fm = decoder_session->foreign_metadata;
743747
size_t i;
744748

@@ -772,22 +776,26 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
772776
if(i != fm->format_block && i != fm->audio_block)
773777
foreign_metadata_size += fm->blocks[i].size;
774778
}
779+
/* preserve dummy cbSize = 0 at the end of the fmt chunk if the encoded file's fmt chunk's size */
780+
/* indicates that it includes cbSize and nothing more */
781+
if(fm->blocks[fm->format_block].size == calculate_total_riff_wave_fmt_chunk_size(format, /*is_waveformatextensible=*/false, /*preserve_dummy_cbsize=*/true))
782+
preserve_dummy_cbsize = true;
775783
}
776784

777785
if(samples == 0)
778786
iff_size = 0;
779787
else if(format == FORMAT_WAVE || format == FORMAT_RF64)
780788
/* 4 for WAVE form bytes */
781789
/* +{36,0} for ds64 chunk */
782-
/* +8+{40,16} for fmt chunk header and body */
790+
/* +fmt chunk header and body */
783791
/* +8 for data chunk header */
784-
iff_size = 4 + (format==FORMAT_RF64?36:0) + 8+(is_waveformatextensible?40:16) + 8 + foreign_metadata_size + aligned_data_size;
792+
iff_size = 4 + (format == FORMAT_RF64 ? 36 : 0) + calculate_total_riff_wave_fmt_chunk_size(format, is_waveformatextensible, preserve_dummy_cbsize) + 8 + foreign_metadata_size + aligned_data_size;
785793
else if(format == FORMAT_WAVE64)
786794
/* 16+8 for RIFF GUID and size field */
787795
/* +16 for WAVE GUID */
788-
/* +16+8+{40,16} for fmt chunk header (GUID and size field) and body */
796+
/* +fmt chunk header and body */
789797
/* +16+8 for data chunk header (GUID and size field) */
790-
iff_size = 16+8 + 16 + 16+8+(is_waveformatextensible?40:16) + 16+8 + foreign_metadata_size + aligned_data_size;
798+
iff_size = 16 + 8 + 16 + calculate_total_riff_wave_fmt_chunk_size(format, is_waveformatextensible, preserve_dummy_cbsize) + 16 + 8 + foreign_metadata_size + aligned_data_size;
791799
else if(format == FORMAT_AIFF)
792800
iff_size = 46 + foreign_metadata_size + aligned_data_size;
793801
else /* AIFF-C */
@@ -867,21 +875,30 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
867875
if(format != FORMAT_WAVE64) {
868876
if(flac__utils_fwrite("fmt ", 1, 4, f) != 4)
869877
return false;
870-
if(!write_little_endian_uint32(f, is_waveformatextensible? 40 : 16)) /* chunk size */
878+
if(!write_little_endian_uint32(f, calculate_riff_wave_fmt_chunk_size_field(is_waveformatextensible, preserve_dummy_cbsize))) /* chunk size */
871879
return false;
872880
}
873881
else { /* Wave64 */
874882
/* fmt GUID 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
875883
if(flac__utils_fwrite("\x66\x6D\x74\x20\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A", 1, 16, f) != 16)
876884
return false;
877-
/* chunk size (+16+8 for GUID and size fields) */
878-
if(!write_little_endian_uint64(f, 16+8+(is_waveformatextensible?40:16)))
885+
/* chunk size */
886+
if(!write_little_endian_uint64(f, calculate_wave64_fmt_chunk_size_field(is_waveformatextensible, preserve_dummy_cbsize)))
879887
return false;
880888
}
881889

882-
if(!write_riff_wave_fmt_chunk_body(f, is_waveformatextensible, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, decoder_session->channel_mask))
890+
if(!write_riff_wave_fmt_chunk_body(f, is_waveformatextensible, preserve_dummy_cbsize, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, decoder_session->channel_mask))
883891
return false;
884892

893+
if(format == FORMAT_WAVE64) {
894+
unsigned padding = ((8 - ftello(f) % 8) % 8);
895+
if(padding > 0) {
896+
/* fmt chunk must be padded in order to align with 8-byte alignment */
897+
if(flac__utils_fwrite("\x00\x00\x00\x00\x00\x00\x00", 1, padding, f) != padding)
898+
return false;
899+
}
900+
}
901+
885902
decoder_session->fm_offset2 = ftello(f);
886903

887904
if(fm) {
@@ -971,7 +988,54 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
971988
return true;
972989
}
973990

974-
FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, uint32_t bps, uint32_t channels, uint32_t sample_rate, FLAC__uint32 channel_mask)
991+
FLAC__uint64 calculate_total_riff_wave_fmt_chunk_size(FileFormat format, FLAC__bool is_waveformatextensible, FLAC__bool preserve_dummy_cbsize)
992+
{
993+
if(format == FORMAT_WAVE64) {
994+
/* round up to nearest 8 bytes */
995+
return ((calculate_wave64_fmt_chunk_size_field(is_waveformatextensible, preserve_dummy_cbsize) + 7) & (~7u));
996+
}
997+
else {
998+
/* +8 for fmt chunk header */
999+
return (8 + calculate_riff_wave_fmt_chunk_size_field(is_waveformatextensible, preserve_dummy_cbsize));
1000+
}
1001+
}
1002+
1003+
FLAC__uint64 calculate_wave64_fmt_chunk_size_field(FLAC__bool is_waveformatextensible, FLAC__bool preserve_dummy_cbsize)
1004+
{
1005+
if(is_waveformatextensible)
1006+
/* +16+8 for GUID and size fields */
1007+
/* +16 for regular fmt body */
1008+
/* +2 for cbSize */
1009+
/* +22 for remaining WAVEFORMATEXTENSIBLE structure */
1010+
return (16 + 8 + 16 + 2 + 22);
1011+
else if(preserve_dummy_cbsize)
1012+
/* +16+8 for GUID and size fields */
1013+
/* +16 for regular fmt body */
1014+
/* +2 for cbSize */
1015+
return (16 + 8 + 16 + 2);
1016+
else
1017+
/* +16+8 for GUID and size fields */
1018+
/* +16 for regular fmt body */
1019+
return (16 + 8 + 16);
1020+
}
1021+
1022+
FLAC__uint64 calculate_riff_wave_fmt_chunk_size_field(FLAC__bool is_waveformatextensible, FLAC__bool preserve_dummy_cbsize)
1023+
{
1024+
if(is_waveformatextensible)
1025+
/* +16 for regular fmt body */
1026+
/* +2 for cbSize */
1027+
/* +22 for remaining WAVEFORMATEXTENSIBLE structure */
1028+
return (16 + 2 + 22);
1029+
else if(preserve_dummy_cbsize)
1030+
/* +16 for regular fmt body */
1031+
/* +2 for cbSize */
1032+
return (16 + 2);
1033+
else
1034+
/* +16 for regular fmt body */
1035+
return 16;
1036+
}
1037+
1038+
FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, FLAC__bool preserve_dummy_cbsize, uint32_t bps, uint32_t channels, uint32_t sample_rate, FLAC__uint32 channel_mask)
9751039
{
9761040
if(!write_little_endian_uint16(f, (FLAC__uint16)(is_waveformatextensible? 65534 : 1))) /* compression code */
9771041
return false;
@@ -1005,6 +1069,10 @@ FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatexten
10051069
if(flac__utils_fwrite("\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, f) != 16)
10061070
return false;
10071071
}
1072+
else if(preserve_dummy_cbsize) {
1073+
if(!write_little_endian_uint16(f, (FLAC__uint16)0)) /* cbSize */
1074+
return false;
1075+
}
10081076

10091077
return true;
10101078
}

src/test_streams/main.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,39 @@ static FLAC__bool generate_wackywavs(void)
10091009
return false;
10101010
}
10111011

1012+
/* WAV with cbSize = 0 at the end of the fmt chunk */
1013+
static FLAC__bool generate_wackywav_cbsize0(void)
1014+
{
1015+
FILE *f;
1016+
FLAC__byte wav_cbsize0[] = {
1017+
'R', 'I', 'F', 'F', 90, 0, 0, 0,
1018+
'W', 'A', 'V', 'E', 'j', 'u', 'n', 'k',
1019+
4, 0, 0, 0 , 'b', 'l', 'a', 'h',
1020+
'p', 'a', 'd', ' ', 4, 0, 0, 0,
1021+
'B', 'L', 'A', 'H', 'f', 'm', 't', ' ',
1022+
18, 0, 0, 0, 1, 0, 1, 0,
1023+
0x44,0xAC, 0, 0,0x88,0x58,0x01, 0,
1024+
/* cbSize */
1025+
2, 0, 16, 0, 0, 0, 'd', 'a',
1026+
't', 'a', 16, 0, 0, 0, 0, 0,
1027+
1, 0, 4, 0, 9, 0, 16, 0,
1028+
25, 0, 36, 0, 49, 0, 'p', 'a',
1029+
'd', ' ', 4, 0, 0, 0, 'b', 'l',
1030+
'a', 'h'
1031+
};
1032+
1033+
if(0 == (f = fopen("wacky_cbsize0.wav", "wb")))
1034+
return false;
1035+
if(fwrite(wav_cbsize0, 1, 98, f) < 98) {
1036+
fclose(f);
1037+
return false;
1038+
}
1039+
1040+
fclose(f);
1041+
1042+
return true;
1043+
}
1044+
10121045
static FLAC__bool write_simple_wavex_header (FILE * f, unsigned samplerate, unsigned channels, unsigned bytespersample, unsigned frames)
10131046
{
10141047
unsigned datalen = channels * bytespersample * frames ;
@@ -1144,6 +1177,49 @@ static FLAC__bool generate_wackywav64s(void)
11441177
return false;
11451178
}
11461179

1180+
/* WAVE64 with cbSize = 0 at the end of the fmt chunk */
1181+
static FLAC__bool generate_wackywav64_cbsize0(void)
1182+
{
1183+
FILE *f;
1184+
FLAC__byte wav_cbsize0[] = {
1185+
0x72,0x69,0x66,0x66,0x2E,0x91,0xCF,0x11, /* RIFF GUID */
1186+
0xA5,0xD6,0x28,0xDB,0x04,0xC1,0x00,0x00,
1187+
192, 0, 0, 0, 0, 0, 0, 0,
1188+
0x77,0x61,0x76,0x65,0xF3,0xAC,0xD3,0x11, /* WAVE GUID */
1189+
0x8C,0xD1,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
1190+
0x6A,0x75,0x6E,0x6B,0xF3,0xAC,0xD3,0x11, /* junk GUID */
1191+
0x8C,0xD1,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
1192+
32, 0, 0, 0 , 0, 0, 0, 0,
1193+
'b', 'l', 'a', 'h', 'b', 'l', 'a', 'h',
1194+
0x66,0x6D,0x74,0x20,0xF3,0xAC,0xD3,0x11, /* fmt GUID */
1195+
0x8C,0xD1,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
1196+
42, 0, 0, 0 , 0, 0, 0, 0,
1197+
1, 0, 1, 0,0x44,0xAC, 0, 0,
1198+
0x88,0x58,0x01, 0, 2, 0, 16, 0,
1199+
/* cbSize, p a d d i n g */
1200+
0, 0, 0, 0, 0, 0, 0, 0,
1201+
0x64,0x61,0x74,0x61,0xF3,0xAC,0xD3,0x11, /* data GUID */
1202+
0x8C,0xD1,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
1203+
40, 0, 0, 0 , 0, 0, 0, 0,
1204+
0, 0, 1, 0, 4, 0, 9, 0,
1205+
16, 0, 25, 0, 36, 0, 49, 0,
1206+
0x6A,0x75,0x6E,0x6B,0xF3,0xAC,0xD3,0x11, /* junk GUID */
1207+
0x8C,0xD1,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
1208+
32, 0, 0, 0 , 0, 0, 0, 0,
1209+
'b', 'l', 'a', 'h', 'b', 'l', 'a', 'h'
1210+
};
1211+
1212+
if(0 == (f = fopen("wacky_cbsize0.w64", "wb")))
1213+
return false;
1214+
if(fwrite(wav_cbsize0, 1, wav_cbsize0[16], f) < wav_cbsize0[16]) {
1215+
fclose(f);
1216+
return false;
1217+
}
1218+
1219+
fclose(f);
1220+
return true;
1221+
}
1222+
11471223
static FLAC__bool generate_wackyrf64s(void)
11481224
{
11491225
FILE *f;
@@ -1186,6 +1262,43 @@ static FLAC__bool generate_wackyrf64s(void)
11861262
return false;
11871263
}
11881264

1265+
/* RF64 with cbSize = 0 at the end of the fmt chunk */
1266+
static FLAC__bool generate_wackyrf64_cbsize0(void)
1267+
{
1268+
FILE *f;
1269+
FLAC__byte wav_cbsize0[] = {
1270+
'R', 'F', '6', '4', 255, 255, 255, 255,
1271+
'W', 'A', 'V', 'E', 'd', 's', '6', '4',
1272+
28, 0, 0, 0, 126, 0, 0, 0,
1273+
0, 0, 0, 0, 16, 0, 0, 0,
1274+
0, 0, 0, 0, 8, 0, 0, 0,
1275+
0, 0, 0, 0, 0, 0, 0, 0,
1276+
'j', 'u', 'n', 'k',
1277+
4, 0, 0, 0, 'b', 'l', 'a', 'h',
1278+
'p', 'a', 'd', ' ', 4, 0, 0, 0,
1279+
'B', 'L', 'A', 'H', 'f', 'm', 't', ' ',
1280+
18, 0, 0, 0, 1, 0, 1, 0,
1281+
0x44,0xAC, 0, 0,0x88,0x58,0x01, 0,
1282+
/* cbSize */
1283+
2, 0, 16, 0, 0, 0, 'd', 'a',
1284+
't', 'a', 255, 255, 255, 255, 0, 0,
1285+
1, 0, 4, 0, 9, 0, 16, 0,
1286+
25, 0, 36, 0, 49, 0, 'p', 'a',
1287+
'd', ' ', 4, 0, 0, 0, 'b', 'l',
1288+
'a', 'h'
1289+
};
1290+
1291+
if(0 == (f = fopen("wacky_cbsize0.rf64", "wb")))
1292+
return false;
1293+
if(fwrite(wav_cbsize0, 1, 134, f) < 134) {
1294+
fclose(f);
1295+
return false;
1296+
}
1297+
1298+
fclose(f);
1299+
return true;
1300+
}
1301+
11891302
static FLAC__bool generate_replaygain_tone (unsigned samplerate)
11901303
{
11911304
FILE *f;
@@ -1378,8 +1491,11 @@ int main(int argc, char *argv[])
13781491
if(!generate_noise("noise.raw", 65536 * 8 * 3)) return 1;
13791492
if(!generate_noise("noise8m32.raw", 32)) return 1;
13801493
if(!generate_wackywavs()) return 1;
1494+
if(!generate_wackywav_cbsize0()) return 1;
13811495
if(!generate_wackywav64s()) return 1;
1496+
if(!generate_wackywav64_cbsize0()) return 1;
13821497
if(!generate_wackyrf64s()) return 1;
1498+
if(!generate_wackyrf64_cbsize0()) return 1;
13831499
if(!generate_noisy_sine()) return 1;
13841500
for(channels = 1; channels <= 8; channels *= 2) {
13851501
unsigned bits_per_sample;

test/test_flac.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,17 +1203,23 @@ echo "Testing --keep-foreign-metadata..."
12031203

12041204
rt_test_wav wacky1.wav '--keep-foreign-metadata'
12051205
rt_test_wav wacky2.wav '--keep-foreign-metadata'
1206+
rt_test_wav wacky_cbsize0.wav '--keep-foreign-metadata'
12061207
rt_test_w64 wacky1.w64 '--keep-foreign-metadata'
12071208
rt_test_w64 wacky2.w64 '--keep-foreign-metadata'
1209+
rt_test_w64 wacky_cbsize0.w64 '--keep-foreign-metadata'
12081210
rt_test_rf64 wacky1.rf64 '--keep-foreign-metadata'
12091211
rt_test_rf64 wacky2.rf64 '--keep-foreign-metadata'
1212+
rt_test_rf64 wacky_cbsize0.rf64 '--keep-foreign-metadata'
12101213

12111214
rt_test_wav_autokf wacky1.wav '--keep-foreign-metadata'
12121215
rt_test_wav_autokf wacky2.wav '--keep-foreign-metadata'
1216+
rt_test_wav_autokf wacky_cbsize0.wav '--keep-foreign-metadata'
12131217
rt_test_w64_autokf wacky1.w64 '--keep-foreign-metadata'
12141218
rt_test_w64_autokf wacky2.w64 '--keep-foreign-metadata'
1219+
rt_test_w64_autokf wacky_cbsize0.w64 '--keep-foreign-metadata'
12151220
rt_test_rf64_autokf wacky1.rf64 '--keep-foreign-metadata'
12161221
rt_test_rf64_autokf wacky2.rf64 '--keep-foreign-metadata'
1222+
rt_test_rf64_autokf wacky_cbsize0.rf64 '--keep-foreign-metadata'
12171223

12181224
testdatadir=${top_srcdir}/test/foreign-metadata-test-files
12191225

0 commit comments

Comments
 (0)