From 5f47ca5aa887bbf732052fba7337b74b48c33a6b Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Tue, 1 Nov 2016 11:44:53 +0100 Subject: [PATCH] wma: Fix packet size calculation. Usually the (fixed) packet size of a wma file equals the block align value plus WMA_FRAME_SKIP. However, this is not true in general, and if the two values differ, we fail to decode the file and bail out with an "incoherent block length" error. This patch adds code to read the correct packet size from the file properties object and uses this value in the decoder and the audio format handler. --- error.h | 1 + wma.h | 2 ++ wma_afh.c | 21 +++++++++++---------- wma_common.c | 26 ++++++++++++++++++++++++++ wmadec_filter.c | 6 +++--- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/error.h b/error.h index 3fda5787..00b1aa77 100644 --- a/error.h +++ b/error.h @@ -148,6 +148,7 @@ extern const char **para_errlist[]; #define WMA_COMMON_ERRORS \ + PARA_ERROR(BAD_ASF_FILE_PROPS, "invalid ASF file properties"), \ PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \ diff --git a/wma.h b/wma.h index 15b9c5d4..b260feb9 100644 --- a/wma.h +++ b/wma.h @@ -28,6 +28,8 @@ struct asf_header_info { bool use_bit_reservoir; /** Whether blocks are of variable or of constant size. */ bool use_variable_block_len; + /** Obtained from the file properties object. */ + uint32_t packet_size; }; /* wma_common.c */ diff --git a/wma_afh.c b/wma_afh.c index 929e732b..4c9d87e0 100644 --- a/wma_afh.c +++ b/wma_afh.c @@ -18,22 +18,22 @@ #include "wma.h" #include "fd.h" -#define FOR_EACH_FRAME(_f, _buf, _size, _ba) for (_f = (_buf); \ - _f + (_ba) + WMA_FRAME_SKIP < (_buf) + (_size); \ - _f += (_ba) + WMA_FRAME_SKIP) +#define FOR_EACH_FRAME(_f, _buf, _size, _ps) for (_f = (_buf); \ + _f + (_ps) < (_buf) + (_size); \ + _f += (_ps)) /* * Must be called on a frame boundary, e.g. start + header_len. * \return Frame count, superframe count via *num_superframes. */ -static int count_frames(const char *buf, int buf_size, int block_align, +static int count_frames(const char *buf, int buf_size, uint32_t packet_size, int *num_superframes) { int fc = 0, sfc = 0; /* frame count, superframe count */ const uint8_t *p; - FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, block_align) { + FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, packet_size) { fc += p[WMA_FRAME_SKIP] & 0x0f; sfc++; } @@ -192,7 +192,7 @@ static void set_chunk_tv(int frames_per_chunk, int frequency, } /* Must be called on a frame boundary. */ -static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align, +static int wma_make_chunk_table(char *buf, size_t buf_size, uint32_t packet_size, struct afh_info *afhi) { const uint8_t *f, *start = (uint8_t *)buf; @@ -204,7 +204,7 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align, afhi->chunk_table[0] = 0; afhi->chunk_table[1] = afhi->header_len; - num_frames = count_frames(buf, buf_size, block_align, + num_frames = count_frames(buf, buf_size, packet_size, &num_superframes); ret = -E_NO_WMA; if (num_frames == 0 || num_superframes == 0) @@ -214,7 +214,7 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align, frames_per_chunk = num_frames / num_superframes / 2; PARA_INFO_LOG("%d frames per chunk\n", frames_per_chunk); j = 1; - FOR_EACH_FRAME(f, start, buf_size, block_align) { + FOR_EACH_FRAME(f, start, buf_size, packet_size) { count += f[WMA_FRAME_SKIP] & 0x0f; while (count > j * frames_per_chunk) { j++; @@ -224,7 +224,8 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align, afhi->chunk_table, ct_size * sizeof(uint32_t)); } - afhi->chunk_table[j] = f - start + afhi->header_len + block_align + WMA_FRAME_SKIP; + afhi->chunk_table[j] = f - start + afhi->header_len + + packet_size; } } afhi->chunks_total = j; @@ -262,7 +263,7 @@ static int wma_get_file_info(char *map, size_t numbytes, __a_unused int fd, ahi.use_variable_block_len? "vbl" : "" ); wma_make_chunk_table(map + ahi.header_len, numbytes - ahi.header_len, - ahi.block_align, afhi); + ahi.packet_size, afhi); read_asf_tags(map, ahi.header_len, &afhi->tags); return 0; } diff --git a/wma_common.c b/wma_common.c index 88873c95..27cb51d0 100644 --- a/wma_common.c +++ b/wma_common.c @@ -42,6 +42,17 @@ const char *search_pattern(const char *pattern, int pattern_len, return NULL; } +static int find_file_properties(const char *buf, int len) +{ + const char pattern[] = {0xa1, 0xdc, 0xab, 0x8c}; + const char *p = search_pattern(pattern, sizeof(pattern), buf, len); + + if (!p) + return -E_WMA_NO_GUID; + PARA_DEBUG_LOG("found file property guid@%0x\n", (int)(p - buf)); + return p - buf + 16; +} + /* 40 9e 69 f8 4d 5b cf 11 a8 fd 00 80 5f 5c 44 2b */ @@ -112,6 +123,21 @@ int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi) ahi->use_exp_vlc = ahi->flags2 & 0x0001; ahi->use_bit_reservoir = ahi->flags2 & 0x0002; ahi->use_variable_block_len = ahi->flags2 & 0x0004; + + ret = find_file_properties(buf, ahi->header_len); + if (ret < 0) + return ret; + /* file property header is always 88 bytes (sans GUID) */ + if (ret + 88 > loaded) + return 0; + start = buf + ret; + ahi->packet_size = read_u32(start + 76); /* min packet size */ + /* we only support fixed packet sizes */ + if (ahi->packet_size != read_u32(start + 80)) /* min != max */ + return -E_BAD_ASF_FILE_PROPS; + if (ahi->packet_size <= ahi->block_align) + return -E_BAD_ASF_FILE_PROPS; + PARA_INFO_LOG("packet size: %u\n", ahi->packet_size); return 1; } diff --git a/wmadec_filter.c b/wmadec_filter.c index 48257c19..f695d869 100644 --- a/wmadec_filter.c +++ b/wmadec_filter.c @@ -1221,12 +1221,12 @@ next_buffer: fn->min_iqs += 4096; goto next_buffer; } - fn->min_iqs = 2 * (WMA_FRAME_SKIP + pwd->ahi.block_align); + fn->min_iqs = 2 * pwd->ahi.packet_size; fn->private_data = pwd; converted = pwd->ahi.header_len; goto success; } - fn->min_iqs = WMA_FRAME_SKIP + pwd->ahi.block_align; + fn->min_iqs = pwd->ahi.packet_size; if (fn->min_iqs > len) goto success; out_size = WMA_OUTPUT_BUFFER_SIZE; @@ -1242,7 +1242,7 @@ next_buffer: btr_add_output(out, out_size, btrn); } else free(out); - converted += ret + WMA_FRAME_SKIP; + converted += pwd->ahi.packet_size; success: btr_consume(btrn, converted); return 0; -- 2.39.2