summary |
shortlog |
log |
commit | commitdiff |
tree
raw |
patch |
inline | side by side (from parent 1:
8eaa280)
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.
#define WMA_COMMON_ERRORS \
#define WMA_COMMON_ERRORS \
+ PARA_ERROR(BAD_ASF_FILE_PROPS, "invalid ASF file properties"), \
PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \
PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \
bool use_bit_reservoir;
/** Whether blocks are of variable or of constant size. */
bool use_variable_block_len;
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;
#include "wma.h"
#include "fd.h"
#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.
*/
/*
* 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;
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++;
}
fc += p[WMA_FRAME_SKIP] & 0x0f;
sfc++;
}
}
/* Must be called on a frame boundary. */
}
/* 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;
struct afh_info *afhi)
{
const uint8_t *f, *start = (uint8_t *)buf;
afhi->chunk_table[0] = 0;
afhi->chunk_table[1] = afhi->header_len;
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)
&num_superframes);
ret = -E_NO_WMA;
if (num_frames == 0 || num_superframes == 0)
frames_per_chunk = num_frames / num_superframes / 2;
PARA_INFO_LOG("%d frames per chunk\n", frames_per_chunk);
j = 1;
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++;
count += f[WMA_FRAME_SKIP] & 0x0f;
while (count > j * frames_per_chunk) {
j++;
afhi->chunk_table,
ct_size * sizeof(uint32_t));
}
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;
}
}
afhi->chunks_total = j;
ahi.use_variable_block_len? "vbl" : ""
);
wma_make_chunk_table(map + ahi.header_len, numbytes - ahi.header_len,
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;
}
read_asf_tags(map, ahi.header_len, &afhi->tags);
return 0;
}
+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
*/
/*
40 9e 69 f8 4d 5b cf 11 a8 fd 00 80 5f 5c 44 2b
*/
ahi->use_exp_vlc = ahi->flags2 & 0x0001;
ahi->use_bit_reservoir = ahi->flags2 & 0x0002;
ahi->use_variable_block_len = ahi->flags2 & 0x0004;
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);
fn->min_iqs += 4096;
goto 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->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;
if (fn->min_iqs > len)
goto success;
out_size = WMA_OUTPUT_BUFFER_SIZE;
btr_add_output(out, out_size, btrn);
} else
free(out);
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;
success:
btr_consume(btrn, converted);
return 0;