2004-01-19 19:16:10 +00:00
|
|
|
/*
|
|
|
|
* native ebml reader for the Matroska demuxer
|
2010-01-24 22:55:11 +00:00
|
|
|
* new parser copyright (c) 2010 Uoti Urpala
|
2009-05-08 21:51:13 +00:00
|
|
|
* copyright (c) 2004 Aurelien Jacobs <aurel@gnuage.org>
|
|
|
|
* based on the one written by Ronald Bultje for gstreamer
|
|
|
|
*
|
|
|
|
* This file is part of MPlayer.
|
|
|
|
*
|
|
|
|
* MPlayer is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* MPlayer is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2004-01-19 19:16:10 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2010-01-24 22:55:11 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
2012-01-28 11:41:36 +00:00
|
|
|
#include <libavutil/intfloat.h>
|
2010-01-24 22:55:11 +00:00
|
|
|
#include <libavutil/common.h>
|
|
|
|
#include "talloc.h"
|
2004-01-19 19:16:10 +00:00
|
|
|
#include "ebml.h"
|
2010-01-24 22:55:11 +00:00
|
|
|
#include "stream/stream.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "compat/mpbswap.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/msg.h"
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2014-01-14 16:38:21 +00:00
|
|
|
// Whether the id is a known Matroska level 1 element (allowed as element on
|
|
|
|
// global file level, after the level 0 MATROSKA_ID_SEGMENT).
|
|
|
|
// This (intentionally) doesn't include "global" elements.
|
|
|
|
bool ebml_is_mkv_level1_id(uint32_t id)
|
|
|
|
{
|
|
|
|
switch (id) {
|
|
|
|
case MATROSKA_ID_SEEKHEAD:
|
|
|
|
case MATROSKA_ID_INFO:
|
|
|
|
case MATROSKA_ID_CLUSTER:
|
|
|
|
case MATROSKA_ID_TRACKS:
|
|
|
|
case MATROSKA_ID_CUES:
|
|
|
|
case MATROSKA_ID_ATTACHMENTS:
|
|
|
|
case MATROSKA_ID_CHAPTERS:
|
|
|
|
case MATROSKA_ID_TAGS:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-01-19 19:16:10 +00:00
|
|
|
/*
|
|
|
|
* Read: the element content data ID.
|
|
|
|
* Return: the ID.
|
|
|
|
*/
|
2014-01-14 16:38:51 +00:00
|
|
|
uint32_t ebml_read_id(stream_t *s)
|
2004-01-19 19:16:10 +00:00
|
|
|
{
|
2009-12-29 19:06:21 +00:00
|
|
|
int i, len_mask = 0x80;
|
|
|
|
uint32_t id;
|
|
|
|
|
|
|
|
for (i = 0, id = stream_read_char(s); i < 4 && !(id & len_mask); i++)
|
|
|
|
len_mask >>= 1;
|
|
|
|
if (i >= 4)
|
|
|
|
return EBML_ID_INVALID;
|
|
|
|
while (i--)
|
|
|
|
id = (id << 8) | stream_read_char(s);
|
|
|
|
return id;
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a variable length unsigned int.
|
|
|
|
*/
|
2013-04-12 15:25:51 +00:00
|
|
|
uint64_t ebml_read_vlen_uint(bstr *buffer)
|
2004-01-19 19:16:10 +00:00
|
|
|
{
|
2009-12-29 19:06:21 +00:00
|
|
|
int i, j, num_ffs = 0, len_mask = 0x80;
|
|
|
|
uint64_t num;
|
|
|
|
|
2013-04-12 15:25:51 +00:00
|
|
|
if (buffer->len == 0)
|
|
|
|
return EBML_UINT_INVALID;
|
|
|
|
|
|
|
|
for (i = 0, num = buffer->start[0]; i < 8 && !(num & len_mask); i++)
|
2009-12-29 19:06:21 +00:00
|
|
|
len_mask >>= 1;
|
|
|
|
if (i >= 8)
|
|
|
|
return EBML_UINT_INVALID;
|
|
|
|
j = i + 1;
|
|
|
|
if ((int) (num &= (len_mask - 1)) == len_mask - 1)
|
2004-01-19 19:16:10 +00:00
|
|
|
num_ffs++;
|
2013-04-12 15:25:51 +00:00
|
|
|
if (j > buffer->len)
|
|
|
|
return EBML_UINT_INVALID;
|
|
|
|
for (int n = 0; n < i; n++) {
|
|
|
|
num = (num << 8) | buffer->start[n + 1];
|
2009-12-29 19:06:21 +00:00
|
|
|
if ((num & 0xFF) == 0xFF)
|
|
|
|
num_ffs++;
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
2009-12-29 19:06:21 +00:00
|
|
|
if (j == num_ffs)
|
|
|
|
return EBML_UINT_INVALID;
|
2013-04-12 15:25:51 +00:00
|
|
|
buffer->start += j;
|
|
|
|
buffer->len -= j;
|
2009-12-29 19:06:21 +00:00
|
|
|
return num;
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a variable length signed int.
|
|
|
|
*/
|
2013-04-12 15:25:51 +00:00
|
|
|
int64_t ebml_read_vlen_int(bstr *buffer)
|
2004-01-19 19:16:10 +00:00
|
|
|
{
|
2009-12-29 19:06:21 +00:00
|
|
|
uint64_t unum;
|
|
|
|
int l;
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2009-12-29 19:06:21 +00:00
|
|
|
/* read as unsigned number first */
|
2013-04-12 15:25:51 +00:00
|
|
|
size_t len = buffer->len;
|
|
|
|
unum = ebml_read_vlen_uint(buffer);
|
2009-12-29 19:06:21 +00:00
|
|
|
if (unum == EBML_UINT_INVALID)
|
|
|
|
return EBML_INT_INVALID;
|
2013-04-12 15:25:51 +00:00
|
|
|
l = len - buffer->len;
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2009-12-29 19:06:21 +00:00
|
|
|
return unum - ((1 << ((7 * l) - 1)) - 1);
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read: element content length.
|
|
|
|
*/
|
2014-01-14 16:38:51 +00:00
|
|
|
uint64_t ebml_read_length(stream_t *s)
|
2004-01-19 19:16:10 +00:00
|
|
|
{
|
2009-12-29 19:06:21 +00:00
|
|
|
int i, j, num_ffs = 0, len_mask = 0x80;
|
|
|
|
uint64_t len;
|
|
|
|
|
|
|
|
for (i = 0, len = stream_read_char(s); i < 8 && !(len & len_mask); i++)
|
|
|
|
len_mask >>= 1;
|
|
|
|
if (i >= 8)
|
|
|
|
return EBML_UINT_INVALID;
|
|
|
|
j = i + 1;
|
|
|
|
if ((int) (len &= (len_mask - 1)) == len_mask - 1)
|
2004-01-19 19:16:10 +00:00
|
|
|
num_ffs++;
|
2009-12-29 19:06:21 +00:00
|
|
|
while (i--) {
|
|
|
|
len = (len << 8) | stream_read_char(s);
|
|
|
|
if ((len & 0xFF) == 0xFF)
|
|
|
|
num_ffs++;
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
2009-12-29 19:06:21 +00:00
|
|
|
if (j == num_ffs)
|
|
|
|
return EBML_UINT_INVALID;
|
2012-10-29 17:58:35 +00:00
|
|
|
if (len >= 1ULL<<63) // Can happen if stream_read_char returns EOF
|
|
|
|
return EBML_UINT_INVALID;
|
2009-12-29 19:06:21 +00:00
|
|
|
return len;
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the next element as an unsigned int.
|
|
|
|
*/
|
2014-01-14 16:38:51 +00:00
|
|
|
uint64_t ebml_read_uint(stream_t *s)
|
2004-01-19 19:16:10 +00:00
|
|
|
{
|
2009-12-29 19:06:21 +00:00
|
|
|
uint64_t len, value = 0;
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2014-01-14 16:38:51 +00:00
|
|
|
len = ebml_read_length(s);
|
2009-12-29 19:06:21 +00:00
|
|
|
if (len == EBML_UINT_INVALID || len < 1 || len > 8)
|
|
|
|
return EBML_UINT_INVALID;
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2009-12-29 19:06:21 +00:00
|
|
|
while (len--)
|
|
|
|
value = (value << 8) | stream_read_char(s);
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2009-12-29 19:06:21 +00:00
|
|
|
return value;
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the next element as a signed int.
|
|
|
|
*/
|
2014-01-14 16:38:51 +00:00
|
|
|
int64_t ebml_read_int(stream_t *s)
|
2004-01-19 19:16:10 +00:00
|
|
|
{
|
2009-12-29 19:06:21 +00:00
|
|
|
int64_t value = 0;
|
|
|
|
uint64_t len;
|
|
|
|
int l;
|
|
|
|
|
2014-01-14 16:38:51 +00:00
|
|
|
len = ebml_read_length(s);
|
2009-12-29 19:06:21 +00:00
|
|
|
if (len == EBML_UINT_INVALID || len < 1 || len > 8)
|
|
|
|
return EBML_INT_INVALID;
|
|
|
|
|
|
|
|
len--;
|
|
|
|
l = stream_read_char(s);
|
|
|
|
if (l & 0x80)
|
|
|
|
value = -1;
|
|
|
|
value = (value << 8) | l;
|
|
|
|
while (len--)
|
|
|
|
value = (value << 8) | stream_read_char(s);
|
|
|
|
|
|
|
|
return value;
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
demux_mkv: improve robustness against broken files
Fixes test7.mkv from the Matroska test file collection, as well as some
real broken files I've found in the wild. (Unfortunately, true recovery
requires resetting the decoders and playback state with a manual seek,
but it's still better than just exiting.)
If there are broken EBML elements, try harder to skip them correctly.
Do this by searching for the next cluster element. The cluster element
intentionally has a long ID, so it's a suitable element for
resynchronizing (mkvmerge does something similar).
We know that data is corrupt if the ID or length fields of an element
are malformed. Additionally, if skipping an unknown element goes past
the end of the file, we assume it's corrupt and undo the seek. Do this
because it often happens that corrupt data is interpreted as correct
EBML elements. Since these elements will have a ridiculous values in
their length fields due to the large value range that is possible
(0-2^56-2), they will go past the end of the file. So instead of
skipping them (which would result in playback termination), try to
find the next cluster instead. (We still skip unknown elements that
are within the file, as this is needed for correct operation. Also, we
first execute the seek, because we don't really know where the file
ends. Doing it this way is better for unseekable streams too, because
it will still work in the non-error case.)
This is done as special case in the packet reading function only. On
the other hand, that's the only part of the file that's read after
initialization is done.
2013-03-27 23:01:17 +00:00
|
|
|
* Skip the current element.
|
2014-01-14 16:38:21 +00:00
|
|
|
* end: the end of the parent element or -1 (for robust error handling)
|
2004-01-19 19:16:10 +00:00
|
|
|
*/
|
2014-01-14 16:38:21 +00:00
|
|
|
int ebml_read_skip(struct mp_log *log, int64_t end, stream_t *s)
|
2004-01-19 19:16:10 +00:00
|
|
|
{
|
2009-12-29 19:06:21 +00:00
|
|
|
uint64_t len;
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2014-01-14 16:38:21 +00:00
|
|
|
int64_t pos = stream_tell(s);
|
|
|
|
|
2014-01-14 16:38:51 +00:00
|
|
|
len = ebml_read_length(s);
|
2009-12-29 19:06:21 +00:00
|
|
|
if (len == EBML_UINT_INVALID)
|
2014-01-14 16:38:21 +00:00
|
|
|
goto invalid;
|
|
|
|
|
|
|
|
int64_t pos2 = stream_tell(s);
|
|
|
|
if (len >= INT64_MAX - pos2 || (end > 0 && pos2 + len > end))
|
|
|
|
goto invalid;
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2014-01-14 16:38:21 +00:00
|
|
|
if (!stream_skip(s, len))
|
|
|
|
goto invalid;
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2009-12-29 19:06:21 +00:00
|
|
|
return 0;
|
2014-01-14 16:38:21 +00:00
|
|
|
|
|
|
|
invalid:
|
|
|
|
mp_err(log, "Invalid EBML length at position %"PRId64"\n", pos);
|
|
|
|
stream_seek(s, pos);
|
|
|
|
return 1;
|
2004-01-19 19:16:10 +00:00
|
|
|
}
|
|
|
|
|
demux_mkv: improve robustness against broken files
Fixes test7.mkv from the Matroska test file collection, as well as some
real broken files I've found in the wild. (Unfortunately, true recovery
requires resetting the decoders and playback state with a manual seek,
but it's still better than just exiting.)
If there are broken EBML elements, try harder to skip them correctly.
Do this by searching for the next cluster element. The cluster element
intentionally has a long ID, so it's a suitable element for
resynchronizing (mkvmerge does something similar).
We know that data is corrupt if the ID or length fields of an element
are malformed. Additionally, if skipping an unknown element goes past
the end of the file, we assume it's corrupt and undo the seek. Do this
because it often happens that corrupt data is interpreted as correct
EBML elements. Since these elements will have a ridiculous values in
their length fields due to the large value range that is possible
(0-2^56-2), they will go past the end of the file. So instead of
skipping them (which would result in playback termination), try to
find the next cluster instead. (We still skip unknown elements that
are within the file, as this is needed for correct operation. Also, we
first execute the seek, because we don't really know where the file
ends. Doing it this way is better for unseekable streams too, because
it will still work in the non-error case.)
This is done as special case in the packet reading function only. On
the other hand, that's the only part of the file that's read after
initialization is done.
2013-03-27 23:01:17 +00:00
|
|
|
/*
|
|
|
|
* Skip to (probable) next cluster (MATROSKA_ID_CLUSTER) element start position.
|
|
|
|
*/
|
2013-12-21 19:24:20 +00:00
|
|
|
int ebml_resync_cluster(struct mp_log *log, stream_t *s)
|
demux_mkv: improve robustness against broken files
Fixes test7.mkv from the Matroska test file collection, as well as some
real broken files I've found in the wild. (Unfortunately, true recovery
requires resetting the decoders and playback state with a manual seek,
but it's still better than just exiting.)
If there are broken EBML elements, try harder to skip them correctly.
Do this by searching for the next cluster element. The cluster element
intentionally has a long ID, so it's a suitable element for
resynchronizing (mkvmerge does something similar).
We know that data is corrupt if the ID or length fields of an element
are malformed. Additionally, if skipping an unknown element goes past
the end of the file, we assume it's corrupt and undo the seek. Do this
because it often happens that corrupt data is interpreted as correct
EBML elements. Since these elements will have a ridiculous values in
their length fields due to the large value range that is possible
(0-2^56-2), they will go past the end of the file. So instead of
skipping them (which would result in playback termination), try to
find the next cluster instead. (We still skip unknown elements that
are within the file, as this is needed for correct operation. Also, we
first execute the seek, because we don't really know where the file
ends. Doing it this way is better for unseekable streams too, because
it will still work in the non-error case.)
This is done as special case in the packet reading function only. On
the other hand, that's the only part of the file that's read after
initialization is done.
2013-03-27 23:01:17 +00:00
|
|
|
{
|
|
|
|
int64_t pos = stream_tell(s);
|
|
|
|
uint32_t last_4_bytes = 0;
|
2013-12-21 19:24:20 +00:00
|
|
|
mp_err(log, "Corrupt file detected. "
|
demux_mkv: improve robustness against broken files
Fixes test7.mkv from the Matroska test file collection, as well as some
real broken files I've found in the wild. (Unfortunately, true recovery
requires resetting the decoders and playback state with a manual seek,
but it's still better than just exiting.)
If there are broken EBML elements, try harder to skip them correctly.
Do this by searching for the next cluster element. The cluster element
intentionally has a long ID, so it's a suitable element for
resynchronizing (mkvmerge does something similar).
We know that data is corrupt if the ID or length fields of an element
are malformed. Additionally, if skipping an unknown element goes past
the end of the file, we assume it's corrupt and undo the seek. Do this
because it often happens that corrupt data is interpreted as correct
EBML elements. Since these elements will have a ridiculous values in
their length fields due to the large value range that is possible
(0-2^56-2), they will go past the end of the file. So instead of
skipping them (which would result in playback termination), try to
find the next cluster instead. (We still skip unknown elements that
are within the file, as this is needed for correct operation. Also, we
first execute the seek, because we don't really know where the file
ends. Doing it this way is better for unseekable streams too, because
it will still work in the non-error case.)
This is done as special case in the packet reading function only. On
the other hand, that's the only part of the file that's read after
initialization is done.
2013-03-27 23:01:17 +00:00
|
|
|
"Trying to resync starting from position %"PRId64"...\n", pos);
|
|
|
|
while (!s->eof) {
|
|
|
|
// Assumes MATROSKA_ID_CLUSTER is 4 bytes, with no 0 bytes.
|
|
|
|
if (last_4_bytes == MATROSKA_ID_CLUSTER) {
|
2013-12-21 19:24:20 +00:00
|
|
|
mp_err(log, "Cluster found at %"PRId64".\n", pos - 4);
|
demux_mkv: improve robustness against broken files
Fixes test7.mkv from the Matroska test file collection, as well as some
real broken files I've found in the wild. (Unfortunately, true recovery
requires resetting the decoders and playback state with a manual seek,
but it's still better than just exiting.)
If there are broken EBML elements, try harder to skip them correctly.
Do this by searching for the next cluster element. The cluster element
intentionally has a long ID, so it's a suitable element for
resynchronizing (mkvmerge does something similar).
We know that data is corrupt if the ID or length fields of an element
are malformed. Additionally, if skipping an unknown element goes past
the end of the file, we assume it's corrupt and undo the seek. Do this
because it often happens that corrupt data is interpreted as correct
EBML elements. Since these elements will have a ridiculous values in
their length fields due to the large value range that is possible
(0-2^56-2), they will go past the end of the file. So instead of
skipping them (which would result in playback termination), try to
find the next cluster instead. (We still skip unknown elements that
are within the file, as this is needed for correct operation. Also, we
first execute the seek, because we don't really know where the file
ends. Doing it this way is better for unseekable streams too, because
it will still work in the non-error case.)
This is done as special case in the packet reading function only. On
the other hand, that's the only part of the file that's read after
initialization is done.
2013-03-27 23:01:17 +00:00
|
|
|
stream_seek(s, pos - 4);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
last_4_bytes = (last_4_bytes << 8) | stream_read_char(s);
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-01-19 19:16:10 +00:00
|
|
|
|
2010-01-24 22:55:11 +00:00
|
|
|
|
|
|
|
#define EVALARGS(F, ...) F(__VA_ARGS__)
|
|
|
|
#define E(str, N, type) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, type };
|
|
|
|
#define E_SN(str, count, N) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, EBML_TYPE_SUBELEMENTS, sizeof(struct ebml_ ## N), count, (const struct ebml_field_desc[]){
|
|
|
|
#define E_S(str, count) EVALARGS(E_SN, str, count, N)
|
|
|
|
#define FN(id, name, multiple, N) { id, multiple, offsetof(struct ebml_ ## N, name), offsetof(struct ebml_ ## N, n_ ## name), &ebml_##name##_desc},
|
|
|
|
#define F(id, name, multiple) EVALARGS(FN, id, name, multiple, N)
|
|
|
|
#include "ebml_defs.c"
|
|
|
|
#undef EVALARGS
|
|
|
|
#undef SN
|
|
|
|
#undef S
|
|
|
|
#undef FN
|
|
|
|
#undef F
|
|
|
|
|
|
|
|
// Used to read/write pointers to different struct types
|
|
|
|
struct generic;
|
|
|
|
#define generic_struct struct generic
|
|
|
|
|
|
|
|
static uint32_t ebml_parse_id(uint8_t *data, int *length)
|
|
|
|
{
|
|
|
|
int len = 1;
|
|
|
|
uint32_t id = *data++;
|
|
|
|
for (int len_mask = 0x80; !(id & len_mask); len_mask >>= 1) {
|
|
|
|
len++;
|
|
|
|
if (len > 4) {
|
|
|
|
*length = -1;
|
|
|
|
return EBML_ID_INVALID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*length = len;
|
|
|
|
while (--len)
|
|
|
|
id = (id << 8) | *data++;
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t parse_vlen(uint8_t *data, int *length, bool is_length)
|
|
|
|
{
|
|
|
|
uint64_t r = *data++;
|
|
|
|
int len = 1;
|
|
|
|
int len_mask;
|
|
|
|
for (len_mask = 0x80; !(r & len_mask); len_mask >>= 1) {
|
|
|
|
len++;
|
|
|
|
if (len > 8) {
|
|
|
|
*length = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r &= len_mask - 1;
|
|
|
|
|
|
|
|
int num_allones = 0;
|
|
|
|
if (r == len_mask - 1)
|
|
|
|
num_allones++;
|
|
|
|
for (int i = 1; i < len; i++) {
|
|
|
|
if (*data == 255)
|
|
|
|
num_allones++;
|
|
|
|
r = (r << 8) | *data++;
|
|
|
|
}
|
|
|
|
if (is_length && num_allones == len) {
|
|
|
|
// According to Matroska specs this means "unknown length"
|
|
|
|
// Could be supported if there are any actual files using it
|
|
|
|
*length = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*length = len;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t ebml_parse_length(uint8_t *data, int *length)
|
|
|
|
{
|
|
|
|
return parse_vlen(data, length, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t ebml_parse_uint(uint8_t *data, int length)
|
|
|
|
{
|
|
|
|
assert(length >= 1 && length <= 8);
|
|
|
|
uint64_t r = 0;
|
|
|
|
while (length--)
|
|
|
|
r = (r << 8) + *data++;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int64_t ebml_parse_sint(uint8_t *data, int length)
|
|
|
|
{
|
|
|
|
assert(length >=1 && length <= 8);
|
|
|
|
int64_t r = 0;
|
|
|
|
if (*data & 0x80)
|
|
|
|
r = -1;
|
|
|
|
while (length--)
|
|
|
|
r = (r << 8) | *data++;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double ebml_parse_float(uint8_t *data, int length)
|
|
|
|
{
|
|
|
|
assert(length == 4 || length == 8);
|
|
|
|
uint64_t i = ebml_parse_uint(data, length);
|
|
|
|
if (length == 4)
|
2012-01-28 11:41:36 +00:00
|
|
|
return av_int2float(i);
|
2010-01-24 22:55:11 +00:00
|
|
|
else
|
2012-01-28 11:41:36 +00:00
|
|
|
return av_int2double(i);
|
2010-01-24 22:55:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// target must be initialized to zero
|
|
|
|
static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
|
|
|
|
uint8_t *data, int size,
|
|
|
|
const struct ebml_elem_desc *type, int level)
|
|
|
|
{
|
|
|
|
assert(type->type == EBML_TYPE_SUBELEMENTS);
|
|
|
|
assert(level < 8);
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "%.*sParsing element %s\n", level, " ", type->name);
|
2010-01-24 22:55:11 +00:00
|
|
|
|
|
|
|
char *s = target;
|
|
|
|
uint8_t *end = data + size;
|
|
|
|
uint8_t *p = data;
|
2014-06-29 21:22:07 +00:00
|
|
|
int num_elems[MAX_EBML_SUBELEMENTS] = {0};
|
2010-01-24 22:55:11 +00:00
|
|
|
while (p < end) {
|
|
|
|
uint8_t *startp = p;
|
2013-07-22 22:45:23 +00:00
|
|
|
int len;
|
2010-01-24 22:55:11 +00:00
|
|
|
uint32_t id = ebml_parse_id(p, &len);
|
|
|
|
if (len > end - p)
|
|
|
|
goto past_end_error;
|
|
|
|
if (len < 0) {
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "Error parsing subelement id\n");
|
2010-01-24 22:55:11 +00:00
|
|
|
goto other_error;
|
|
|
|
}
|
|
|
|
p += len;
|
|
|
|
uint64_t length = ebml_parse_length(p, &len);
|
|
|
|
if (len > end - p)
|
|
|
|
goto past_end_error;
|
|
|
|
if (len < 0) {
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "Error parsing subelement length\n");
|
2010-01-24 22:55:11 +00:00
|
|
|
goto other_error;
|
|
|
|
}
|
|
|
|
p += len;
|
|
|
|
|
|
|
|
int field_idx = -1;
|
|
|
|
for (int i = 0; i < type->field_count; i++)
|
|
|
|
if (type->fields[i].id == id) {
|
|
|
|
field_idx = i;
|
|
|
|
num_elems[i]++;
|
2014-06-29 21:22:07 +00:00
|
|
|
if (num_elems[i] >= 0x70000000) {
|
|
|
|
MP_ERR(ctx, "Too many EBML subelements.\n");
|
|
|
|
goto other_error;
|
|
|
|
}
|
2010-01-24 22:55:11 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length > end - p) {
|
|
|
|
if (field_idx >= 0 && type->fields[field_idx].desc->type
|
|
|
|
!= EBML_TYPE_SUBELEMENTS) {
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "Subelement content goes "
|
2010-01-24 22:55:11 +00:00
|
|
|
"past end of containing element\n");
|
|
|
|
goto other_error;
|
|
|
|
}
|
|
|
|
// Try to parse what is possible from inside this partial element
|
|
|
|
ctx->has_errors = true;
|
|
|
|
length = end - p;
|
|
|
|
}
|
|
|
|
p += length;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
past_end_error:
|
2014-06-29 21:28:47 +00:00
|
|
|
MP_DBG(ctx, "Subelement headers go past end of containing element\n");
|
2010-01-24 22:55:11 +00:00
|
|
|
other_error:
|
|
|
|
ctx->has_errors = true;
|
|
|
|
end = startp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < type->field_count; i++)
|
|
|
|
if (num_elems[i] && type->fields[i].multiple) {
|
|
|
|
char *ptr = s + type->fields[i].offset;
|
|
|
|
switch (type->fields[i].desc->type) {
|
|
|
|
case EBML_TYPE_SUBELEMENTS:
|
|
|
|
num_elems[i] = FFMIN(num_elems[i],
|
|
|
|
1000000000 / type->fields[i].desc->size);
|
2013-07-22 22:45:23 +00:00
|
|
|
int sz = num_elems[i] * type->fields[i].desc->size;
|
|
|
|
*(generic_struct **) ptr = talloc_zero_size(ctx->talloc_ctx, sz);
|
2010-01-24 22:55:11 +00:00
|
|
|
break;
|
|
|
|
case EBML_TYPE_UINT:
|
|
|
|
*(uint64_t **) ptr = talloc_zero_array(ctx->talloc_ctx,
|
|
|
|
uint64_t, num_elems[i]);
|
|
|
|
break;
|
|
|
|
case EBML_TYPE_SINT:
|
|
|
|
*(int64_t **) ptr = talloc_zero_array(ctx->talloc_ctx,
|
|
|
|
int64_t, num_elems[i]);
|
|
|
|
break;
|
|
|
|
case EBML_TYPE_FLOAT:
|
|
|
|
*(double **) ptr = talloc_zero_array(ctx->talloc_ctx,
|
|
|
|
double, num_elems[i]);
|
|
|
|
break;
|
|
|
|
case EBML_TYPE_STR:
|
|
|
|
case EBML_TYPE_BINARY:
|
|
|
|
*(struct bstr **) ptr = talloc_zero_array(ctx->talloc_ctx,
|
|
|
|
struct bstr,
|
|
|
|
num_elems[i]);
|
|
|
|
break;
|
|
|
|
case EBML_TYPE_EBML_ID:
|
|
|
|
*(int32_t **) ptr = talloc_zero_array(ctx->talloc_ctx,
|
|
|
|
uint32_t, num_elems[i]);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (data < end) {
|
|
|
|
int len;
|
|
|
|
uint32_t id = ebml_parse_id(data, &len);
|
|
|
|
assert(len >= 0 && len <= end - data);
|
|
|
|
data += len;
|
|
|
|
uint64_t length = ebml_parse_length(data, &len);
|
|
|
|
assert(len >= 0 && len <= end - data);
|
|
|
|
data += len;
|
|
|
|
if (length > end - data) {
|
|
|
|
// Try to parse what is possible from inside this partial element
|
|
|
|
length = end - data;
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "Next subelement content goes "
|
2010-01-24 22:55:11 +00:00
|
|
|
"past end of containing element, will be truncated\n");
|
|
|
|
}
|
|
|
|
int field_idx = -1;
|
|
|
|
for (int i = 0; i < type->field_count; i++)
|
|
|
|
if (type->fields[i].id == id) {
|
|
|
|
field_idx = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (field_idx < 0) {
|
|
|
|
if (id == 0xec)
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "%.*sIgnoring Void element "
|
2010-01-24 22:55:11 +00:00
|
|
|
"size: %"PRIu64"\n", level+1, " ", length);
|
|
|
|
else if (id == 0xbf)
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "%.*sIgnoring CRC-32 "
|
2010-01-24 22:55:11 +00:00
|
|
|
"element size: %"PRIu64"\n", level+1, " ",
|
|
|
|
length);
|
|
|
|
else
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "Ignoring unrecognized "
|
2010-01-24 22:55:11 +00:00
|
|
|
"subelement. ID: %x size: %"PRIu64"\n", id, length);
|
|
|
|
data += length;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const struct ebml_field_desc *fd = &type->fields[field_idx];
|
|
|
|
const struct ebml_elem_desc *ed = fd->desc;
|
|
|
|
bool multiple = fd->multiple;
|
|
|
|
int *countptr = (int *) (s + fd->count_offset);
|
|
|
|
if (*countptr >= num_elems[field_idx]) {
|
|
|
|
// Shouldn't happen with on any sane file without bugs
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_ERR(ctx, "Too many subelems?\n");
|
2010-01-24 22:55:11 +00:00
|
|
|
ctx->has_errors = true;
|
|
|
|
data += length;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*countptr > 0 && !multiple) {
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "Another subelement of type "
|
2010-01-24 22:55:11 +00:00
|
|
|
"%x %s (size: %"PRIu64"). Only one allowed. Ignoring.\n",
|
|
|
|
id, ed->name, length);
|
|
|
|
ctx->has_errors = true;
|
|
|
|
data += length;
|
|
|
|
continue;
|
|
|
|
}
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "%.*sParsing %x %s size: %"PRIu64
|
2010-01-24 22:55:11 +00:00
|
|
|
" value: ", level+1, " ", id, ed->name, length);
|
|
|
|
|
|
|
|
char *fieldptr = s + fd->offset;
|
|
|
|
switch (ed->type) {
|
|
|
|
case EBML_TYPE_SUBELEMENTS:
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "subelements\n");
|
2010-01-24 22:55:11 +00:00
|
|
|
char *subelptr;
|
|
|
|
if (multiple) {
|
|
|
|
char *array_start = (char *) *(generic_struct **) fieldptr;
|
|
|
|
subelptr = array_start + *countptr * ed->size;
|
|
|
|
} else
|
|
|
|
subelptr = fieldptr;
|
|
|
|
ebml_parse_element(ctx, subelptr, data, length, ed, level + 1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EBML_TYPE_UINT:;
|
|
|
|
uint64_t *uintptr;
|
|
|
|
#define GETPTR(subelptr, fieldtype) \
|
|
|
|
if (multiple) \
|
|
|
|
subelptr = *(fieldtype **) fieldptr + *countptr; \
|
|
|
|
else \
|
|
|
|
subelptr = (fieldtype *) fieldptr
|
|
|
|
GETPTR(uintptr, uint64_t);
|
|
|
|
if (length < 1 || length > 8) {
|
2014-06-29 21:28:47 +00:00
|
|
|
MP_DBG(ctx, "uint invalid length %"PRIu64"\n", length);
|
2010-01-24 22:55:11 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
*uintptr = ebml_parse_uint(data, length);
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "uint %"PRIu64"\n", *uintptr);
|
2010-01-24 22:55:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EBML_TYPE_SINT:;
|
|
|
|
int64_t *sintptr;
|
|
|
|
GETPTR(sintptr, int64_t);
|
|
|
|
if (length < 1 || length > 8) {
|
2014-06-29 21:28:47 +00:00
|
|
|
MP_DBG(ctx, "sint invalid length %"PRIu64"\n", length);
|
2010-01-24 22:55:11 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
*sintptr = ebml_parse_sint(data, length);
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "sint %"PRId64"\n", *sintptr);
|
2010-01-24 22:55:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EBML_TYPE_FLOAT:;
|
|
|
|
double *floatptr;
|
|
|
|
GETPTR(floatptr, double);
|
|
|
|
if (length != 4 && length != 8) {
|
2014-06-29 21:28:47 +00:00
|
|
|
MP_DBG(ctx, "float invalid length %"PRIu64"\n", length);
|
2010-01-24 22:55:11 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
*floatptr = ebml_parse_float(data, length);
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "float %f\n", *floatptr);
|
2010-01-24 22:55:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EBML_TYPE_STR:
|
|
|
|
case EBML_TYPE_BINARY:;
|
2014-06-29 21:22:07 +00:00
|
|
|
if (length > 0x80000000) {
|
|
|
|
MP_ERR(ctx, "Not reading overly long EBML element.\n");
|
|
|
|
break;
|
|
|
|
}
|
2010-01-24 22:55:11 +00:00
|
|
|
struct bstr *strptr;
|
|
|
|
GETPTR(strptr, struct bstr);
|
|
|
|
strptr->start = data;
|
|
|
|
strptr->len = length;
|
|
|
|
if (ed->type == EBML_TYPE_STR)
|
2014-06-29 21:28:47 +00:00
|
|
|
MP_DBG(ctx, "string \"%.*s\"\n", BSTR_P(*strptr));
|
2010-01-24 22:55:11 +00:00
|
|
|
else
|
2014-06-29 21:28:47 +00:00
|
|
|
MP_DBG(ctx, "binary %zd bytes\n", strptr->len);
|
2010-01-24 22:55:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EBML_TYPE_EBML_ID:;
|
|
|
|
uint32_t *idptr;
|
|
|
|
GETPTR(idptr, uint32_t);
|
|
|
|
*idptr = ebml_parse_id(data, &len);
|
|
|
|
if (len != length) {
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "ebml_id broken value\n");
|
2010-01-24 22:55:11 +00:00
|
|
|
goto error;
|
|
|
|
}
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_DBG(ctx, "ebml_id %x\n", (unsigned)*idptr);
|
2010-01-24 22:55:11 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
*countptr += 1;
|
|
|
|
error:
|
|
|
|
data += length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// target must be initialized to zero
|
|
|
|
int ebml_read_element(struct stream *s, struct ebml_parse_ctx *ctx,
|
|
|
|
void *target, const struct ebml_elem_desc *desc)
|
|
|
|
{
|
|
|
|
ctx->has_errors = false;
|
2013-12-21 20:41:18 +00:00
|
|
|
int msglevel = ctx->no_error_messages ? MSGL_DEBUG : MSGL_WARN;
|
2014-01-14 16:38:51 +00:00
|
|
|
uint64_t length = ebml_read_length(s);
|
2010-01-24 22:55:11 +00:00
|
|
|
if (s->eof) {
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_MSG(ctx, msglevel, "Unexpected end of file "
|
2010-01-24 22:55:11 +00:00
|
|
|
"- partial or corrupt file?\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (length > 1000000000) {
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_MSG(ctx, msglevel, "Refusing to read element over 100 MB in size\n");
|
2010-01-24 22:55:11 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ctx->talloc_ctx = talloc_size(NULL, length + 8);
|
|
|
|
int read_len = stream_read(s, ctx->talloc_ctx, length);
|
|
|
|
if (read_len < length)
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_MSG(ctx, msglevel, "Unexpected end of file - partial or corrupt file?\n");
|
2010-01-24 22:55:11 +00:00
|
|
|
ebml_parse_element(ctx, target, ctx->talloc_ctx, read_len, desc, 0);
|
|
|
|
if (ctx->has_errors)
|
2013-12-21 19:24:20 +00:00
|
|
|
MP_MSG(ctx, msglevel, "Error parsing element %s\n", desc->name);
|
2010-01-24 22:55:11 +00:00
|
|
|
return 0;
|
|
|
|
}
|