mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2323 lines
93 KiB
2323 lines
93 KiB
/* |
|
Copyright (c) 2012, Broadcom Europe Ltd |
|
All rights reserved. |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions are met: |
|
* Redistributions of source code must retain the above copyright |
|
notice, this list of conditions and the following disclaimer. |
|
* Redistributions in binary form must reproduce the above copyright |
|
notice, this list of conditions and the following disclaimer in the |
|
documentation and/or other materials provided with the distribution. |
|
* Neither the name of the copyright holder nor the |
|
names of its contributors may be used to endorse or promote products |
|
derived from this software without specific prior written permission. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
//#define ENABLE_MKV_EXTRA_LOGGING |
|
#define CONTAINER_IS_BIG_ENDIAN |
|
#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->element_level |
|
#include "containers/core/containers_private.h" |
|
#include "containers/core/containers_io_helpers.h" |
|
#include "containers/core/containers_utils.h" |
|
#include "containers/core/containers_logging.h" |
|
|
|
/****************************************************************************** |
|
Defines. |
|
******************************************************************************/ |
|
#define MKV_TRACKS_MAX 16 |
|
#define MKV_CODECID_MAX 32 |
|
#define MKV_MAX_LACING_NUM 64 |
|
|
|
#define MKV_MAX_ENCODINGS 1 |
|
#define MKV_MAX_ENCODING_DATA 256 |
|
|
|
#define MKV_MAX_ELEMENT_LEVEL 8 |
|
#define MKV_MAX_CONSECUTIVE_UNKNOWN_ELEMENTS 5 |
|
#define MKV_MAX_ELEMENT_SIZE (1<<29) /* Does not apply to the data element */ |
|
#define MKV_MAX_STRING_SIZE 256 |
|
#define MKV_ELEMENT_MIN_HEADER_SIZE 2 |
|
|
|
#define MKV_MAX_READER_STATE_LEVEL 4 |
|
|
|
#define MKV_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n)) |
|
#define MKV_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n)) |
|
#define MKV_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n)) |
|
#define MKV_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n)) |
|
#define MKV_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n)) |
|
#define MKV_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n)) |
|
#define MKV_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n)) |
|
#define MKV_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n)) |
|
#define MKV_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n)) |
|
#define MKV_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n)) |
|
#define MKV_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz)) |
|
#define MKV_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz)) |
|
|
|
#define CHECK_POINT(a) do { \ |
|
/*if(size < 0 && size != INT64_C(-1)) return VC_CONTAINER_ERROR_CORRUPTED;*/ \ |
|
if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); } while(0) |
|
|
|
static uint32_t mkv_io_read_id(VC_CONTAINER_IO_T *io, int64_t *size) |
|
{ |
|
uint32_t value, mask; |
|
|
|
value = vc_container_io_read_uint8(io); (*size)--; |
|
for(mask = 0x80; mask; mask <<= 7) |
|
{ |
|
if(value & mask) return value; |
|
value = (value << 8) | vc_container_io_read_uint8(io); (*size)--; |
|
} |
|
return 0; |
|
} |
|
|
|
static int64_t mkv_io_read_uint(VC_CONTAINER_IO_T *io, int64_t *size) |
|
{ |
|
uint64_t value, mask; |
|
|
|
value = vc_container_io_read_uint8(io); (*size)--; |
|
if(value == 0xFF) return -1; |
|
|
|
for(mask = 0x80; mask; mask <<= 7) |
|
{ |
|
if(value & mask) return value & ~mask; |
|
value = (value << 8) | vc_container_io_read_uint8(io); (*size)--; |
|
} |
|
return 0; |
|
} |
|
|
|
static int64_t mkv_io_read_sint(VC_CONTAINER_IO_T *io, int64_t *size) |
|
{ |
|
int64_t value, count = io->offset; |
|
value = mkv_io_read_uint(io, size); |
|
count = io->offset - count; |
|
|
|
switch(count) |
|
{ |
|
case 1: value -= 0x3F; break; |
|
case 2: value -= 0x1FFF; break; |
|
case 3: value -= 0xFFFFF; break; |
|
case 4: value -= 0x7FFFFFF; break; |
|
default: break; |
|
} |
|
return value; |
|
} |
|
|
|
#define MKV_READ_ID(ctx, n) mkv_io_read_id((ctx)->priv->io, &size) |
|
#define MKV_READ_UINT(ctx, n) mkv_io_read_uint((ctx)->priv->io, &size) |
|
#define MKV_READ_SINT(ctx, n) mkv_io_read_sint((ctx)->priv->io, &size) |
|
|
|
/****************************************************************************** |
|
Type definitions. |
|
******************************************************************************/ |
|
|
|
typedef enum |
|
{ |
|
MKV_ELEMENT_ID_UNKNOWN = 0, |
|
|
|
/* EBML Basics */ |
|
MKV_ELEMENT_ID_EBML = 0x1A45DFA3, |
|
MKV_ELEMENT_ID_EBML_VERSION = 0x4286, |
|
MKV_ELEMENT_ID_EBML_READ_VERSION = 0x42F7, |
|
MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH = 0x42F2, |
|
MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH = 0x42F3, |
|
MKV_ELEMENT_ID_DOCTYPE = 0x4282, |
|
MKV_ELEMENT_ID_DOCTYPE_VERSION = 0x4287, |
|
MKV_ELEMENT_ID_DOCTYPE_READ_VERSION = 0x4285, |
|
|
|
/* Global Elements */ |
|
MKV_ELEMENT_ID_CRC32 = 0xBF, |
|
MKV_ELEMENT_ID_VOID = 0xEC, |
|
|
|
/* Segment */ |
|
MKV_ELEMENT_ID_SEGMENT = 0x18538067, |
|
|
|
/* Meta Seek Information */ |
|
MKV_ELEMENT_ID_SEEK_HEAD = 0x114D9B74, |
|
MKV_ELEMENT_ID_SEEK = 0x4DBB, |
|
MKV_ELEMENT_ID_SEEK_ID = 0x53AB, |
|
MKV_ELEMENT_ID_SEEK_POSITION = 0x53AC, |
|
|
|
/* Segment Information */ |
|
MKV_ELEMENT_ID_INFO = 0x1549A966, |
|
MKV_ELEMENT_ID_SEGMENT_UID = 0x73A4, |
|
MKV_ELEMENT_ID_SEGMENT_FILENAME = 0x7384, |
|
MKV_ELEMENT_ID_PREV_UID = 0x3CB923, |
|
MKV_ELEMENT_ID_PREV_FILENAME = 0x3C83AB, |
|
MKV_ELEMENT_ID_NEXT_UID = 0x3EB923, |
|
MKV_ELEMENT_ID_NEXT_FILENAME = 0x3E83BB, |
|
MKV_ELEMENT_ID_SEGMENT_FAMILY = 0x4444, |
|
MKV_ELEMENT_ID_CHAPTER_TRANSLATE = 0x6924, |
|
MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID = 0x69FC, |
|
MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC = 0x69BF, |
|
MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID = 0x69A5, |
|
MKV_ELEMENT_ID_TIMECODE_SCALE = 0x2AD7B1, |
|
MKV_ELEMENT_ID_DURATION = 0x4489, |
|
MKV_ELEMENT_ID_DATE_UTC = 0x4461, |
|
MKV_ELEMENT_ID_TITLE = 0x7BA9, |
|
MKV_ELEMENT_ID_MUXING_APP = 0x4D80, |
|
MKV_ELEMENT_ID_WRITING_APP = 0x5741, |
|
|
|
/* Cluster */ |
|
MKV_ELEMENT_ID_CLUSTER = 0x1F43B675, |
|
MKV_ELEMENT_ID_TIMECODE = 0xE7, |
|
MKV_ELEMENT_ID_SILENT_TRACKS = 0x5854, |
|
MKV_ELEMENT_ID_SILENT_TRACK_NUMBER = 0x58D7, |
|
MKV_ELEMENT_ID_POSITION = 0xA7, |
|
MKV_ELEMENT_ID_PREV_SIZE = 0xAB, |
|
MKV_ELEMENT_ID_BLOCKGROUP = 0xA0, |
|
MKV_ELEMENT_ID_BLOCK = 0xA1, |
|
MKV_ELEMENT_ID_BLOCK_ADDITIONS = 0x75A1, |
|
MKV_ELEMENT_ID_BLOCK_MORE = 0xA6, |
|
MKV_ELEMENT_ID_BLOCK_ADD_ID = 0xEE, |
|
MKV_ELEMENT_ID_BLOCK_ADDITIONAL = 0xA5, |
|
MKV_ELEMENT_ID_BLOCK_DURATION = 0x9B, |
|
MKV_ELEMENT_ID_REFERENCE_PRIORITY = 0xFA, |
|
MKV_ELEMENT_ID_REFERENCE_BLOCK = 0xFB, |
|
MKV_ELEMENT_ID_CODEC_STATE = 0xA4, |
|
MKV_ELEMENT_ID_SLICES = 0x8E, |
|
MKV_ELEMENT_ID_TIME_SLICE = 0xE8, |
|
MKV_ELEMENT_ID_LACE_NUMBER = 0xCC, |
|
MKV_ELEMENT_ID_SIMPLE_BLOCK = 0xA3, |
|
|
|
/* Track */ |
|
MKV_ELEMENT_ID_TRACKS = 0x1654AE6B, |
|
MKV_ELEMENT_ID_TRACK_ENTRY = 0xAE, |
|
MKV_ELEMENT_ID_TRACK_NUMBER = 0xD7, |
|
MKV_ELEMENT_ID_TRACK_UID = 0x73C5, |
|
MKV_ELEMENT_ID_TRACK_TYPE = 0x83, |
|
MKV_ELEMENT_ID_FLAG_ENABLED = 0xB9, |
|
MKV_ELEMENT_ID_FLAG_DEFAULT = 0x88, |
|
MKV_ELEMENT_ID_FLAG_FORCED = 0x55AA, |
|
MKV_ELEMENT_ID_FLAG_LACING = 0x9C, |
|
MKV_ELEMENT_ID_MIN_CACHE = 0x6DE7, |
|
MKV_ELEMENT_ID_MAX_CACHE = 0x6DF8, |
|
MKV_ELEMENT_ID_DEFAULT_DURATION = 0x23E383, |
|
MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE = 0x23314F, |
|
MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID = 0x55EE, |
|
MKV_ELEMENT_ID_NAME = 0x536E, |
|
MKV_ELEMENT_ID_LANGUAGE = 0x22B59C, |
|
MKV_ELEMENT_ID_TRACK_CODEC_ID = 0x86, |
|
MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE = 0x63A2, |
|
MKV_ELEMENT_ID_TRACK_CODEC_NAME = 0x258688, |
|
MKV_ELEMENT_ID_ATTACHMENT_LINK = 0x7446, |
|
MKV_ELEMENT_ID_CODEC_DECODE_ALL = 0xAA, |
|
MKV_ELEMENT_ID_TRACK_OVERLAY = 0x6FAB, |
|
MKV_ELEMENT_ID_TRACK_TRANSLATE = 0x6624, |
|
MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID = 0x66FC, |
|
MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC = 0x66BF, |
|
MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID = 0x66A5, |
|
|
|
/* Video */ |
|
MKV_ELEMENT_ID_VIDEO = 0xE0, |
|
MKV_ELEMENT_ID_FLAG_INTERLACED = 0x9A, |
|
MKV_ELEMENT_ID_STEREO_MODE = 0x53B8, |
|
MKV_ELEMENT_ID_PIXEL_WIDTH = 0xB0, |
|
MKV_ELEMENT_ID_PIXEL_HEIGHT = 0xBA, |
|
MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM = 0x54AA, |
|
MKV_ELEMENT_ID_PIXEL_CROP_TOP = 0x54BB, |
|
MKV_ELEMENT_ID_PIXEL_CROP_LEFT = 0x54CC, |
|
MKV_ELEMENT_ID_PIXEL_CROP_RIGHT = 0x54DD, |
|
MKV_ELEMENT_ID_DISPLAY_WIDTH = 0x54B0, |
|
MKV_ELEMENT_ID_DISPLAY_HEIGHT = 0x54BA, |
|
MKV_ELEMENT_ID_DISPLAY_UNIT = 0x54B2, |
|
MKV_ELEMENT_ID_ASPECT_RATIO_TYPE = 0x54B3, |
|
MKV_ELEMENT_ID_COLOUR_SPACE = 0x2EB524, |
|
MKV_ELEMENT_ID_FRAME_RATE = 0x2383E3, |
|
|
|
/* Audio */ |
|
MKV_ELEMENT_ID_AUDIO = 0xE1, |
|
MKV_ELEMENT_ID_SAMPLING_FREQUENCY = 0xB5, |
|
MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY = 0x78B5, |
|
MKV_ELEMENT_ID_CHANNELS = 0x9F, |
|
MKV_ELEMENT_ID_BIT_DEPTH = 0x6264, |
|
|
|
/* Content Encoding */ |
|
MKV_ELEMENT_ID_CONTENT_ENCODINGS = 0x6D80, |
|
MKV_ELEMENT_ID_CONTENT_ENCODING = 0x6240, |
|
MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER = 0x5031, |
|
MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE = 0x5032, |
|
MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE = 0x5033, |
|
MKV_ELEMENT_ID_CONTENT_COMPRESSION = 0x5034, |
|
MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO = 0x4254, |
|
MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS = 0x4255, |
|
MKV_ELEMENT_ID_CONTENT_ENCRYPTION = 0x5035, |
|
MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO = 0x47E1, |
|
MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID = 0x47E2, |
|
MKV_ELEMENT_ID_CONTENT_SIGNATURE = 0x47E3, |
|
MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID = 0x47E4, |
|
MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO = 0x47E5, |
|
MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO = 0x47E6, |
|
|
|
/* Cueing Data */ |
|
MKV_ELEMENT_ID_CUES = 0x1C53BB6B, |
|
MKV_ELEMENT_ID_CUE_POINT = 0xBB, |
|
MKV_ELEMENT_ID_CUE_TIME = 0xB3, |
|
MKV_ELEMENT_ID_CUE_TRACK_POSITIONS = 0xB7, |
|
MKV_ELEMENT_ID_CUE_TRACK = 0xF7, |
|
MKV_ELEMENT_ID_CUE_CLUSTER_POSITION = 0xF1, |
|
MKV_ELEMENT_ID_CUE_BLOCK_NUMBER = 0x5378, |
|
|
|
/* Attachments */ |
|
MKV_ELEMENT_ID_ATTACHMENTS = 0x1941A469, |
|
|
|
/* Chapters */ |
|
MKV_ELEMENT_ID_CHAPTERS = 0x1043A770, |
|
|
|
/* Tagging */ |
|
MKV_ELEMENT_ID_TAGS = 0x1254C367, |
|
MKV_ELEMENT_ID_TAG = 0x7373, |
|
MKV_ELEMENT_ID_TAG_TARGETS = 0x63C0, |
|
MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE = 0x68CA, |
|
MKV_ELEMENT_ID_TAG_TARGET_TYPE = 0x63CA, |
|
MKV_ELEMENT_ID_TAG_TRACK_UID = 0x63C5, |
|
MKV_ELEMENT_ID_TAG_EDITION_UID = 0x63C9, |
|
MKV_ELEMENT_ID_TAG_CHAPTER_UID = 0x63C4, |
|
MKV_ELEMENT_ID_TAG_ATTACHMENT_UID = 0x63C6, |
|
MKV_ELEMENT_ID_TAG_SIMPLE_TAG = 0x67C8, |
|
MKV_ELEMENT_ID_TAG_NAME = 0x45A3, |
|
MKV_ELEMENT_ID_TAG_LANGUAGE = 0x447A, |
|
MKV_ELEMENT_ID_TAG_DEFAULT = 0x4484, |
|
MKV_ELEMENT_ID_TAG_STRING = 0x4487, |
|
MKV_ELEMENT_ID_TAG_BINARY = 0x4485, |
|
|
|
MKV_ELEMENT_ID_INVALID = 0xFFFFFFFF |
|
} MKV_ELEMENT_ID_T; |
|
|
|
/** Context for our reader |
|
*/ |
|
|
|
typedef struct |
|
{ |
|
unsigned int track; |
|
unsigned int flags; |
|
int64_t pts; |
|
int64_t cluster_timecode; |
|
int64_t prev_cluster_size; /* Size of the previous cluster if available */ |
|
int64_t frame_duration; |
|
|
|
int level; |
|
struct { |
|
int64_t offset; |
|
int64_t data_start; |
|
int64_t data_offset; |
|
int64_t size; |
|
MKV_ELEMENT_ID_T id; |
|
} levels[MKV_MAX_READER_STATE_LEVEL]; |
|
|
|
bool eos; |
|
bool corrupted; |
|
bool seen_ref_block; |
|
|
|
uint32_t lacing_num_frames; |
|
uint32_t lacing_size; |
|
uint16_t lacing_sizes[MKV_MAX_LACING_NUM]; |
|
uint32_t lacing_current_size; |
|
|
|
/* For header stripping compression */ |
|
uint32_t header_size; |
|
uint8_t *header_data; |
|
uint32_t header_size_backup; |
|
} MKV_READER_STATE_T; |
|
|
|
typedef struct |
|
{ |
|
const MKV_ELEMENT_ID_T id; |
|
const MKV_ELEMENT_ID_T parent_id; |
|
const char *psz_name; |
|
VC_CONTAINER_STATUS_T (*pf_func)(VC_CONTAINER_T *, MKV_ELEMENT_ID_T, int64_t); |
|
|
|
} MKV_ELEMENT_T; |
|
|
|
typedef struct VC_CONTAINER_TRACK_MODULE_T |
|
{ |
|
MKV_READER_STATE_T *state; |
|
MKV_READER_STATE_T track_state; |
|
|
|
/* Information extracted from the track entry */ |
|
uint32_t number; |
|
uint32_t type; |
|
int64_t timecode_scale; |
|
uint32_t duration; |
|
int64_t frame_duration; |
|
char codecid[MKV_CODECID_MAX]; |
|
|
|
union { |
|
/* video specific */ |
|
struct { |
|
unsigned int interlaced:1; |
|
unsigned int stereo_mode:2; |
|
uint32_t pixel_width; |
|
uint32_t pixel_height; |
|
uint32_t pixel_crop_bottom; |
|
uint32_t pixel_crop_top; |
|
uint32_t pixel_crop_left; |
|
uint32_t pixel_crop_right; |
|
uint32_t display_width; |
|
uint32_t display_height; |
|
uint32_t display_unit; |
|
uint32_t aspect_ratio_type; |
|
float frame_rate; |
|
} video; |
|
|
|
/* audio specific */ |
|
struct { |
|
uint32_t sampling_frequency; |
|
uint32_t output_sampling_frequency; |
|
uint32_t channels; |
|
uint32_t bit_depth; |
|
} audio; |
|
} es_type; |
|
|
|
/* content encoding (i.e. lossless compression and encryption) */ |
|
unsigned int encodings_num; |
|
struct { |
|
enum { |
|
MKV_CONTENT_ENCODING_COMPRESSION_ZLIB, |
|
MKV_CONTENT_ENCODING_COMPRESSION_HEADER, |
|
MKV_CONTENT_ENCODING_ENCRYPTION, |
|
MKV_CONTENT_ENCODING_UNKNOWN |
|
} type; |
|
unsigned int data_size; |
|
uint8_t *data; |
|
} encodings[MKV_MAX_ENCODINGS]; |
|
|
|
} VC_CONTAINER_TRACK_MODULE_T; |
|
|
|
typedef struct VC_CONTAINER_MODULE_T |
|
{ |
|
MKV_ELEMENT_T *elements_list; |
|
int element_level; |
|
MKV_ELEMENT_ID_T parent_id; |
|
|
|
uint64_t element_offset; /**< Offset to the start of the current element */ |
|
|
|
uint64_t segment_offset; /**< Offset to the start of the data packets */ |
|
int64_t segment_size; |
|
|
|
int tracks_num; |
|
VC_CONTAINER_TRACK_T *tracks[MKV_TRACKS_MAX]; |
|
|
|
MKV_READER_STATE_T state; |
|
int64_t timecode_scale; |
|
float duration; |
|
|
|
uint64_t cluster_offset; /**< Offset to the first cluster */ |
|
uint64_t cues_offset; /**< Offset to the start of the seeking cues */ |
|
uint64_t tags_offset; /**< Offset to the start of the tags */ |
|
|
|
/* |
|
* Variables only used during parsing of the header |
|
*/ |
|
|
|
VC_CONTAINER_TRACK_T *parsing; /**< Current track being parsed */ |
|
bool is_doctype_valid; |
|
|
|
MKV_ELEMENT_ID_T seekhead_elem_id; |
|
int64_t seekhead_elem_offset; |
|
|
|
/* Cues */ |
|
unsigned int cue_track; |
|
int64_t cue_timecode; |
|
uint64_t cue_cluster_offset; |
|
unsigned int cue_block; |
|
|
|
} VC_CONTAINER_MODULE_T; |
|
|
|
/****************************************************************************** |
|
Function prototypes |
|
******************************************************************************/ |
|
VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * ); |
|
|
|
/****************************************************************************** |
|
Prototypes for local functions |
|
******************************************************************************/ |
|
static VC_CONTAINER_STATUS_T mkv_read_element( VC_CONTAINER_T *p_ctx, int64_t size, MKV_ELEMENT_ID_T parent_id ); |
|
static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_element_data_uint( VC_CONTAINER_T *p_ctx, int64_t size, uint64_t *value ); |
|
static VC_CONTAINER_STATUS_T mkv_read_element_data_float( VC_CONTAINER_T *p_ctx, int64_t size, double *value ); |
|
static VC_CONTAINER_STATUS_T mkv_read_element_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); |
|
|
|
/****************************************************************************** |
|
List of element IDs and their associated processing functions |
|
******************************************************************************/ |
|
MKV_ELEMENT_T mkv_elements_list[] = |
|
{ |
|
/* EBML Basics */ |
|
{MKV_ELEMENT_ID_EBML, MKV_ELEMENT_ID_UNKNOWN, "EBML", mkv_read_element_ebml}, |
|
{MKV_ELEMENT_ID_EBML_VERSION, MKV_ELEMENT_ID_EBML, "EBMLVersion", mkv_read_subelements_ebml}, |
|
{MKV_ELEMENT_ID_EBML_READ_VERSION, MKV_ELEMENT_ID_EBML, "EBMLReadVersion", mkv_read_subelements_ebml}, |
|
{MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxIDLength", mkv_read_subelements_ebml}, |
|
{MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxSizeLength", mkv_read_subelements_ebml}, |
|
{MKV_ELEMENT_ID_DOCTYPE, MKV_ELEMENT_ID_EBML, "DocType", mkv_read_subelements_ebml}, |
|
{MKV_ELEMENT_ID_DOCTYPE_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeVersion", mkv_read_subelements_ebml}, |
|
{MKV_ELEMENT_ID_DOCTYPE_READ_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeReadVersion", mkv_read_subelements_ebml}, |
|
|
|
/* Global Elements */ |
|
{MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, |
|
{MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, |
|
|
|
/* Segment */ |
|
{MKV_ELEMENT_ID_SEGMENT, MKV_ELEMENT_ID_UNKNOWN, "Segment", mkv_read_element_segment}, |
|
|
|
/* Meta Seek Information */ |
|
{MKV_ELEMENT_ID_SEEK_HEAD, MKV_ELEMENT_ID_SEGMENT, "SeekHead", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_SEEK, MKV_ELEMENT_ID_SEEK_HEAD, "Seek", mkv_read_subelements_seek_head}, |
|
{MKV_ELEMENT_ID_SEEK_ID, MKV_ELEMENT_ID_SEEK, "SeekID", mkv_read_subelements_seek_head}, |
|
{MKV_ELEMENT_ID_SEEK_POSITION, MKV_ELEMENT_ID_SEEK, "SeekPosition", mkv_read_subelements_seek_head}, |
|
|
|
/* Segment Information */ |
|
{MKV_ELEMENT_ID_INFO, MKV_ELEMENT_ID_SEGMENT, "Info", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_SEGMENT_UID, MKV_ELEMENT_ID_INFO, "SegmentUID", 0}, |
|
{MKV_ELEMENT_ID_SEGMENT_FILENAME, MKV_ELEMENT_ID_INFO, "SegmentFilename", 0}, |
|
{MKV_ELEMENT_ID_PREV_UID, MKV_ELEMENT_ID_INFO, "PrevUID", 0}, |
|
{MKV_ELEMENT_ID_PREV_FILENAME, MKV_ELEMENT_ID_INFO, "PrevFilename", 0}, |
|
{MKV_ELEMENT_ID_NEXT_UID, MKV_ELEMENT_ID_INFO, "NextUID", 0}, |
|
{MKV_ELEMENT_ID_NEXT_FILENAME, MKV_ELEMENT_ID_INFO, "NextFilename", 0}, |
|
{MKV_ELEMENT_ID_SEGMENT_FAMILY, MKV_ELEMENT_ID_INFO, "SegmentFamily", 0}, |
|
{MKV_ELEMENT_ID_CHAPTER_TRANSLATE, MKV_ELEMENT_ID_INFO, "ChapterTranslate", 0}, |
|
{MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_INFO, "ChapterTranslateEditionUID", 0}, |
|
{MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC, MKV_ELEMENT_ID_INFO, "ChapterTranslateCodec", 0}, |
|
{MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID, MKV_ELEMENT_ID_INFO, "ChapterTranslateID", 0}, |
|
{MKV_ELEMENT_ID_TIMECODE_SCALE, MKV_ELEMENT_ID_INFO, "TimecodeScale", mkv_read_subelements_info}, |
|
{MKV_ELEMENT_ID_DURATION, MKV_ELEMENT_ID_INFO, "Duration", mkv_read_subelements_info}, |
|
{MKV_ELEMENT_ID_DATE_UTC, MKV_ELEMENT_ID_INFO, "DateUTC", 0}, |
|
{MKV_ELEMENT_ID_TITLE, MKV_ELEMENT_ID_INFO, "Title", mkv_read_subelements_info}, |
|
{MKV_ELEMENT_ID_MUXING_APP, MKV_ELEMENT_ID_INFO, "MuxingApp", mkv_read_subelements_info}, |
|
{MKV_ELEMENT_ID_WRITING_APP, MKV_ELEMENT_ID_INFO, "WritingApp", mkv_read_subelements_info}, |
|
|
|
/* Cluster */ |
|
{MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0}, |
|
{MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", 0}, |
|
{MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0}, |
|
{MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0}, |
|
{MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0}, |
|
{MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0}, |
|
{MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0}, |
|
{MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", 0}, |
|
{MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0}, |
|
{MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0}, |
|
{MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0}, |
|
{MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0}, |
|
{MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0}, |
|
{MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0}, |
|
{MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0}, |
|
|
|
/* Track */ |
|
{MKV_ELEMENT_ID_TRACKS, MKV_ELEMENT_ID_SEGMENT, "Tracks", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_TRACK_ENTRY, MKV_ELEMENT_ID_TRACKS, "TrackEntry", mkv_read_element_track_entry}, |
|
{MKV_ELEMENT_ID_TRACK_NUMBER, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackNumber", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_TRACK_UID, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackUID", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_TRACK_TYPE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackType", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_FLAG_ENABLED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagEnabled", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_FLAG_DEFAULT, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagDefault", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_FLAG_FORCED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagForced", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_FLAG_LACING, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagLacing", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_MIN_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MinCache", 0}, |
|
{MKV_ELEMENT_ID_MAX_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxCache", 0}, |
|
{MKV_ELEMENT_ID_DEFAULT_DURATION, MKV_ELEMENT_ID_TRACK_ENTRY, "DefaultDuration", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTimecodeScale", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxBlockAdditionID", 0}, |
|
{MKV_ELEMENT_ID_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "Name", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_LANGUAGE, MKV_ELEMENT_ID_TRACK_ENTRY, "Language", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_TRACK_CODEC_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecID", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecPrivate", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_TRACK_CODEC_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecName", mkv_read_subelements_track_entry}, |
|
{MKV_ELEMENT_ID_ATTACHMENT_LINK, MKV_ELEMENT_ID_TRACK_ENTRY, "AttachmentLink", 0}, |
|
{MKV_ELEMENT_ID_CODEC_DECODE_ALL, MKV_ELEMENT_ID_TRACK_ENTRY, "DecodeAll", 0}, |
|
{MKV_ELEMENT_ID_TRACK_OVERLAY, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackOverlay", 0}, |
|
{MKV_ELEMENT_ID_TRACK_TRANSLATE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTranslate", 0}, |
|
{MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateEditionUID", 0}, |
|
{MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateCodec", 0}, |
|
{MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateTrackID", 0}, |
|
|
|
/* Video */ |
|
{MKV_ELEMENT_ID_VIDEO, MKV_ELEMENT_ID_TRACK_ENTRY, "Video", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_FLAG_INTERLACED, MKV_ELEMENT_ID_VIDEO, "FlagInterlaced", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_STEREO_MODE, MKV_ELEMENT_ID_VIDEO, "StereoMode", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_PIXEL_WIDTH, MKV_ELEMENT_ID_VIDEO, "PixelWidth", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_PIXEL_HEIGHT, MKV_ELEMENT_ID_VIDEO, "PixelHeight", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM, MKV_ELEMENT_ID_VIDEO, "PixelCropBottom", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_PIXEL_CROP_TOP, MKV_ELEMENT_ID_VIDEO, "PixelCropTop", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_PIXEL_CROP_LEFT, MKV_ELEMENT_ID_VIDEO, "PixelCropLeft", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_PIXEL_CROP_RIGHT, MKV_ELEMENT_ID_VIDEO, "PixelCropRight", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_DISPLAY_WIDTH, MKV_ELEMENT_ID_VIDEO, "DisplayWidth", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_DISPLAY_HEIGHT, MKV_ELEMENT_ID_VIDEO, "DisplayHeight", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_DISPLAY_UNIT, MKV_ELEMENT_ID_VIDEO, "DisplayUnit", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_ASPECT_RATIO_TYPE, MKV_ELEMENT_ID_VIDEO, "AspectRatioType", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_COLOUR_SPACE, MKV_ELEMENT_ID_VIDEO, "ColourSpace", mkv_read_subelements_video}, |
|
{MKV_ELEMENT_ID_FRAME_RATE, MKV_ELEMENT_ID_VIDEO, "FrameRate", mkv_read_subelements_video}, |
|
|
|
/* Audio */ |
|
{MKV_ELEMENT_ID_AUDIO, MKV_ELEMENT_ID_TRACK_ENTRY, "Audio", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "SamplingFrequency", mkv_read_subelements_audio}, |
|
{MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "OutputSamplingFrequency", mkv_read_subelements_audio}, |
|
{MKV_ELEMENT_ID_CHANNELS, MKV_ELEMENT_ID_AUDIO, "Channels", mkv_read_subelements_audio}, |
|
{MKV_ELEMENT_ID_BIT_DEPTH, MKV_ELEMENT_ID_AUDIO, "BitDepth", mkv_read_subelements_audio}, |
|
|
|
/* Content Encoding */ |
|
{MKV_ELEMENT_ID_CONTENT_ENCODINGS, MKV_ELEMENT_ID_TRACK_ENTRY, "ContentEncodings", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_CONTENT_ENCODING, MKV_ELEMENT_ID_CONTENT_ENCODINGS, "ContentEncoding", mkv_read_element_encoding}, |
|
{MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingOrder", mkv_read_subelements_encoding}, |
|
{MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingScope", mkv_read_subelements_encoding}, |
|
{MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingType", mkv_read_subelements_encoding}, |
|
{MKV_ELEMENT_ID_CONTENT_COMPRESSION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentCompression", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompAlgo", mkv_read_subelements_compression}, |
|
{MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompSettings", mkv_read_subelements_compression}, |
|
{MKV_ELEMENT_ID_CONTENT_ENCRYPTION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncryption", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncAlgo", 0}, |
|
{MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncKeyID", 0}, |
|
{MKV_ELEMENT_ID_CONTENT_SIGNATURE, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSignature", 0}, |
|
{MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigKeyID", 0}, |
|
{MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigAlgo", 0}, |
|
{MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigHashAlgo", 0}, |
|
|
|
/* Cueing data */ |
|
{MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", mkv_read_element_cues}, |
|
{MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", 0}, |
|
{MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", 0}, |
|
{MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", 0}, |
|
{MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", 0}, |
|
|
|
/* Attachments */ |
|
{MKV_ELEMENT_ID_ATTACHMENTS, MKV_ELEMENT_ID_SEGMENT, "Attachments", 0}, |
|
|
|
/* Chapters */ |
|
{MKV_ELEMENT_ID_CHAPTERS, MKV_ELEMENT_ID_SEGMENT, "Chapters", 0}, |
|
|
|
/* Tagging */ |
|
{MKV_ELEMENT_ID_TAGS, MKV_ELEMENT_ID_SEGMENT, "Tags", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_TAG, MKV_ELEMENT_ID_TAGS, "Tag", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_TAG_TARGETS, MKV_ELEMENT_ID_TAG, "Tag Targets", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type Value", 0}, |
|
{MKV_ELEMENT_ID_TAG_TARGET_TYPE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type", 0}, |
|
{MKV_ELEMENT_ID_TAG_TRACK_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Track UID", 0}, |
|
{MKV_ELEMENT_ID_TAG_EDITION_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Edition UID", 0}, |
|
{MKV_ELEMENT_ID_TAG_CHAPTER_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Chapter UID", 0}, |
|
{MKV_ELEMENT_ID_TAG_ATTACHMENT_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Attachment UID", 0}, |
|
{MKV_ELEMENT_ID_TAG_SIMPLE_TAG, MKV_ELEMENT_ID_TAG, "Simple Tag", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_TAG_NAME, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Name", 0}, |
|
{MKV_ELEMENT_ID_TAG_LANGUAGE, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Language", 0}, |
|
{MKV_ELEMENT_ID_TAG_DEFAULT, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Default", 0}, |
|
{MKV_ELEMENT_ID_TAG_STRING, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag String", 0}, |
|
{MKV_ELEMENT_ID_TAG_BINARY, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Binary", 0}, |
|
|
|
{MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} |
|
}; |
|
|
|
MKV_ELEMENT_T mkv_cluster_elements_list[] = |
|
{ |
|
/* Cluster */ |
|
{MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0}, |
|
{MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", mkv_read_subelements_cluster}, |
|
{MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0}, |
|
{MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0}, |
|
{MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0}, |
|
{MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0}, |
|
{MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0}, |
|
{MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0}, |
|
{MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", mkv_read_subelements_cluster}, |
|
{MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0}, |
|
{MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0}, |
|
{MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0}, |
|
{MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0}, |
|
{MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0}, |
|
{MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0}, |
|
{MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0}, |
|
|
|
/* Global Elements */ |
|
{MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, |
|
{MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, |
|
|
|
{MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} |
|
}; |
|
|
|
MKV_ELEMENT_T mkv_cue_elements_list[] = |
|
{ |
|
/* Cueing data */ |
|
{MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", 0}, |
|
{MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", mkv_read_subelements_cue_point}, |
|
{MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements}, |
|
{MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", mkv_read_subelements_cue_point}, |
|
{MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", mkv_read_subelements_cue_point}, |
|
{MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", mkv_read_subelements_cue_point}, |
|
|
|
/* Global Elements */ |
|
{MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, |
|
{MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, |
|
|
|
{MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} |
|
}; |
|
|
|
/****************************************************************************** |
|
List of codec mapping |
|
******************************************************************************/ |
|
static const struct { |
|
VC_CONTAINER_FOURCC_T fourcc; |
|
const char *codecid; |
|
VC_CONTAINER_FOURCC_T variant; |
|
} codecid_to_fourcc_table[] = |
|
{ |
|
/* Video */ |
|
{VC_CONTAINER_CODEC_MP1V, "V_MPEG1", 0}, |
|
{VC_CONTAINER_CODEC_MP2V, "V_MPEG2", 0}, |
|
{VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/ASP", 0}, |
|
{VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/SP", 0}, |
|
{VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/AP", 0}, |
|
{VC_CONTAINER_CODEC_DIV3, "V_MPEG4/MS/V3", 0}, |
|
{VC_CONTAINER_CODEC_H264, "V_MPEG4/ISO/AVC", VC_CONTAINER_VARIANT_H264_AVC1}, |
|
{VC_CONTAINER_CODEC_MJPEG, "V_MJPEG", 0}, |
|
{VC_CONTAINER_CODEC_RV10, "V_REAL/RV10", 0}, |
|
{VC_CONTAINER_CODEC_RV20, "V_REAL/RV20", 0}, |
|
{VC_CONTAINER_CODEC_RV30, "V_REAL/RV30", 0}, |
|
{VC_CONTAINER_CODEC_RV40, "V_REAL/RV40", 0}, |
|
{VC_CONTAINER_CODEC_THEORA, "V_THEORA", 0}, |
|
{VC_CONTAINER_CODEC_DIRAC, "V_DIRAC", 0}, |
|
{VC_CONTAINER_CODEC_VP8, "V_VP8", 0}, |
|
|
|
/* Audio */ |
|
{VC_CONTAINER_CODEC_MPGA, "A_MPEG/L3", VC_CONTAINER_VARIANT_MPGA_L3}, |
|
{VC_CONTAINER_CODEC_MPGA, "A_MPEG/L2", VC_CONTAINER_VARIANT_MPGA_L2}, |
|
{VC_CONTAINER_CODEC_MPGA, "A_MPEG/L1", VC_CONTAINER_VARIANT_MPGA_L1}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC", 0}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/MAIN", 0}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC", 0}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/SSR", 0}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC/SBR", 0}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/MAIN", 0}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC", 0}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/SSR", 0}, |
|
{VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC/SBR", 0}, |
|
{VC_CONTAINER_CODEC_AC3, "A_AC3", 0}, |
|
{VC_CONTAINER_CODEC_EAC3, "A_EAC3", 0}, |
|
{VC_CONTAINER_CODEC_DTS, "A_DTS", 0}, |
|
{VC_CONTAINER_CODEC_MLP, "A_MLP", 0}, |
|
{0, "A_TRUEHD", 0}, |
|
{VC_CONTAINER_CODEC_VORBIS, "A_VORBIS", 0}, |
|
{VC_CONTAINER_CODEC_FLAC, "A_FLAC", 0}, |
|
{VC_CONTAINER_CODEC_PCM_SIGNED_LE, "A_PCM/INT/LIT", 0}, |
|
{VC_CONTAINER_CODEC_PCM_SIGNED_BE, "A_PCM/INT/BIG", 0}, |
|
{VC_CONTAINER_CODEC_PCM_FLOAT_LE, "A_PCM/FLOAT/IEEE", 0}, |
|
{0, "A_REAL/xyzt", 0}, |
|
{0, "A_REAL/14_4", 0}, |
|
|
|
/* Text */ |
|
{VC_CONTAINER_CODEC_TEXT, "S_TEXT/ASCII", 0}, |
|
{VC_CONTAINER_CODEC_TEXT, "S_TEXT/UTF8", 0}, |
|
{VC_CONTAINER_CODEC_SSA, "S_TEXT/ASS", 0}, |
|
{VC_CONTAINER_CODEC_SSA, "S_TEXT/SSA", 0}, |
|
{VC_CONTAINER_CODEC_SSA, "S_ASS", 0}, |
|
{VC_CONTAINER_CODEC_SSA, "S_SSA", 0}, |
|
{VC_CONTAINER_CODEC_USF, "S_TEXT/USF", 0}, |
|
{VC_CONTAINER_CODEC_VOBSUB, "S_VOBSUB", 0}, |
|
|
|
{0, 0} |
|
}; |
|
|
|
/****************************************************************************** |
|
Local Functions |
|
******************************************************************************/ |
|
|
|
static VC_CONTAINER_FOURCC_T mkv_codecid_to_fourcc(const char *codecid, |
|
VC_CONTAINER_FOURCC_T *variant) |
|
{ |
|
unsigned int i; |
|
for(i = 0; codecid_to_fourcc_table[i].codecid; i++) |
|
if(!strcmp(codecid_to_fourcc_table[i].codecid, codecid)) break; |
|
if (variant) *variant = codecid_to_fourcc_table[i].variant; |
|
return codecid_to_fourcc_table[i].fourcc; |
|
} |
|
|
|
#if 0 |
|
/** Find the track associated with an MKV track number */ |
|
static VC_CONTAINER_TRACK_T *mkv_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int mkv_track_num) |
|
{ |
|
VC_CONTAINER_TRACK_T *p_track = 0; |
|
unsigned int i; |
|
|
|
for(i = 0; i < p_ctx->tracks_num; i++) |
|
if(p_ctx->tracks[i]->priv->module->number == mkv_track_num) break; |
|
|
|
if(i < p_ctx->tracks_num) /* We found it */ |
|
p_track = p_ctx->tracks[i]; |
|
|
|
return p_track; |
|
} |
|
#endif |
|
|
|
/** Base function used to read an MKV/EBML element header. |
|
* This will read the element header do lots of sanity checking and return the element id |
|
* and the size of the data contained in the element */ |
|
static VC_CONTAINER_STATUS_T mkv_read_element_header(VC_CONTAINER_T *p_ctx, int64_t size, |
|
MKV_ELEMENT_ID_T *id, int64_t *element_size, MKV_ELEMENT_ID_T parent_id, |
|
MKV_ELEMENT_T **elem) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
MKV_ELEMENT_T *element; |
|
|
|
module->element_offset = STREAM_POSITION(p_ctx); |
|
|
|
*id = MKV_READ_ID(p_ctx, "Element ID"); |
|
CHECK_POINT(p_ctx); |
|
if(!*id) |
|
{ |
|
LOG_DEBUG(p_ctx, "invalid element id %i", *id); |
|
return VC_CONTAINER_ERROR_CORRUPTED; |
|
} |
|
|
|
if(elem) element = *elem; |
|
else element = mkv_elements_list; |
|
|
|
/* Find out which Element we are dealing with */ |
|
while(element->id && *id != element->id) element++; |
|
|
|
*element_size = MKV_READ_UINT(p_ctx, "Element Size"); |
|
CHECK_POINT(p_ctx); |
|
LOG_FORMAT(p_ctx, "- Element %s (ID 0x%x), Size: %"PRIi64", Offset: %"PRIi64, |
|
element->psz_name, *id, *element_size, module->element_offset); |
|
|
|
/* Sanity check the element size */ |
|
if(*element_size + 1 < 0 /* Shouldn't ever get that big */ || |
|
/* Only the segment / cluster elements can really be massive */ |
|
(*id != MKV_ELEMENT_ID_SEGMENT && *id != MKV_ELEMENT_ID_CLUSTER && |
|
*element_size > MKV_MAX_ELEMENT_SIZE)) |
|
{ |
|
LOG_DEBUG(p_ctx, "element %s has an invalid size (%"PRIi64")", |
|
element->psz_name, *element_size); |
|
return VC_CONTAINER_ERROR_CORRUPTED; |
|
} |
|
if(size >= 0 && *element_size > size) |
|
{ |
|
LOG_DEBUG(p_ctx, "element %s is bigger than it should (%"PRIi64" > %"PRIi64")", |
|
element->psz_name, *element_size, size); |
|
return VC_CONTAINER_ERROR_CORRUPTED; |
|
} |
|
|
|
/* Sanity check that the element has the right parent */ |
|
if(element->id && element->parent_id != MKV_ELEMENT_ID_INVALID && |
|
parent_id != MKV_ELEMENT_ID_INVALID && parent_id != element->parent_id) |
|
{ |
|
LOG_FORMAT(p_ctx, "Ignoring mis-placed element %s (ID 0x%x)", element->psz_name, *id); |
|
while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++; |
|
} |
|
|
|
/* Sanity check that the element isn't too deeply nested */ |
|
if(module->element_level >= MKV_MAX_ELEMENT_LEVEL) |
|
{ |
|
LOG_DEBUG(p_ctx, "element %s is too deep. skipping", element->psz_name); |
|
while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++; |
|
} |
|
|
|
if(elem) *elem = element; |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_element_data(VC_CONTAINER_T *p_ctx, |
|
MKV_ELEMENT_T *element, int64_t element_size, int64_t size) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
int64_t offset; |
|
|
|
offset = STREAM_POSITION(p_ctx); |
|
if (size < 0) size = element_size; |
|
if (size < 0) size = INT64_C(1) << 62; |
|
|
|
/* Call the element specific parsing function */ |
|
if(element->pf_func) |
|
status = element->pf_func(p_ctx, element->id, element_size < 0 ? size : element_size); |
|
|
|
if(status != VC_CONTAINER_SUCCESS) |
|
LOG_DEBUG(p_ctx, "element %s appears to be corrupted (%i)", element->psz_name, status); |
|
|
|
if(element_size < 0) return STREAM_STATUS(p_ctx); /* Unknown size */ |
|
|
|
/* Skip the rest of the element */ |
|
element_size -= (STREAM_POSITION(p_ctx) - offset); |
|
if(element_size < 0) /* Check for overruns */ |
|
{ |
|
/* Things have gone really bad here and we ended up reading past the end of the |
|
* element. We could maybe try to be clever and recover by seeking back to the end |
|
* of the element. However if we get there, the file is clearly corrupted so there's |
|
* no guarantee it would work anyway. */ |
|
LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of element %s", |
|
-element_size, element->psz_name); |
|
return VC_CONTAINER_ERROR_CORRUPTED; |
|
} |
|
|
|
if(element_size) |
|
LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in element %s", element_size, element->psz_name); |
|
|
|
if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size); |
|
else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size); |
|
|
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
/** Base function used to read an MKV/EBML element. |
|
* This will read the element header do lots of sanity checking and pass on the rest |
|
* of the reading to the element specific reading function */ |
|
static VC_CONTAINER_STATUS_T mkv_read_element(VC_CONTAINER_T *p_ctx, |
|
int64_t size, MKV_ELEMENT_ID_T parent_id) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
MKV_ELEMENT_T *element = p_ctx->priv->module->elements_list; |
|
int64_t element_size; |
|
MKV_ELEMENT_ID_T id; |
|
|
|
status = mkv_read_element_header(p_ctx, size, &id, &element_size, parent_id, &element); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
return mkv_read_element_data(p_ctx, element, element_size, size); |
|
} |
|
|
|
/** Reads an unsigned integer element */ |
|
static VC_CONTAINER_STATUS_T mkv_read_element_data_uint(VC_CONTAINER_T *p_ctx, |
|
int64_t size, uint64_t *value) |
|
{ |
|
switch(size) |
|
{ |
|
case 1: *value = READ_U8(p_ctx, "u8-integer"); break; |
|
case 2: *value = READ_U16(p_ctx, "u16-integer"); break; |
|
case 3: *value = READ_U24(p_ctx, "u24-integer"); break; |
|
case 4: *value = READ_U32(p_ctx, "u32-integer"); break; |
|
case 5: *value = READ_U40(p_ctx, "u40-integer"); break; |
|
case 6: *value = READ_U48(p_ctx, "u48-integer"); break; |
|
case 7: *value = READ_U56(p_ctx, "u56-integer"); break; |
|
case 8: *value = READ_U64(p_ctx, "u64-integer"); break; |
|
default: return VC_CONTAINER_ERROR_CORRUPTED; |
|
} |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
/** Reads a float element */ |
|
static VC_CONTAINER_STATUS_T mkv_read_element_data_float(VC_CONTAINER_T *p_ctx, |
|
int64_t size, double *value) |
|
{ |
|
union { |
|
uint32_t u32; |
|
uint64_t u64; |
|
float f; |
|
double d; |
|
} u; |
|
|
|
switch(size) |
|
{ |
|
case 4: u.u32 = READ_U32(p_ctx, "f32-float"); *value = u.f; break; |
|
case 8: u.u64 = READ_U64(p_ctx, "f64-float"); *value = u.d; break; |
|
default: return VC_CONTAINER_ERROR_CORRUPTED; |
|
} |
|
LOG_FORMAT(p_ctx, "float: %f", *value); |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
/** Reads an MKV EBML element */ |
|
static VC_CONTAINER_STATUS_T mkv_read_element_ebml(VC_CONTAINER_T *p_ctx, |
|
MKV_ELEMENT_ID_T id, int64_t size) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
int64_t offset = STREAM_POSITION(p_ctx); |
|
|
|
/* Read contained elements */ |
|
module->element_level++; |
|
while(status == VC_CONTAINER_SUCCESS && size >= MKV_ELEMENT_MIN_HEADER_SIZE) |
|
{ |
|
offset = STREAM_POSITION(p_ctx); |
|
status = mkv_read_element(p_ctx, size, id); |
|
size -= (STREAM_POSITION(p_ctx) - offset); |
|
} |
|
module->element_level--; |
|
return status; |
|
} |
|
|
|
/** Reads the MKV EBML sub-element */ |
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
uint64_t value; |
|
|
|
/* Deal with DocType first since it's a special case */ |
|
if(id == MKV_ELEMENT_ID_DOCTYPE) |
|
{ |
|
char doctype[] = "matroska doctype"; |
|
|
|
/* Check we've got the right doctype string for matroska */ |
|
if(size <= 0) goto unknown_doctype; |
|
if(size > (int)sizeof(doctype)) goto unknown_doctype; |
|
if((int)READ_STRING(p_ctx, doctype, size, "string") != size) return STREAM_STATUS(p_ctx); |
|
if((size != sizeof("matroska")-1 || strncmp(doctype, "matroska", (int)size)) && |
|
(size != sizeof("webm")-1 || strncmp(doctype, "webm", (int)size))) |
|
goto unknown_doctype; |
|
|
|
module->is_doctype_valid = true; |
|
return VC_CONTAINER_SUCCESS; |
|
|
|
unknown_doctype: |
|
LOG_DEBUG(p_ctx, "invalid doctype"); |
|
return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
} |
|
|
|
/* The rest are just unsigned integers */ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
switch(id) |
|
{ |
|
case MKV_ELEMENT_ID_EBML_VERSION: |
|
case MKV_ELEMENT_ID_EBML_READ_VERSION: |
|
if(value != 1) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
break; |
|
case MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH: |
|
if(value > 4) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
break; |
|
case MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH: |
|
if(value > 8) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
break; |
|
case MKV_ELEMENT_ID_DOCTYPE_VERSION: |
|
case MKV_ELEMENT_ID_DOCTYPE_READ_VERSION: |
|
default: break; |
|
} |
|
|
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
int64_t offset = STREAM_POSITION(p_ctx); |
|
bool unknown_size = size < 0; |
|
|
|
/* Read contained elements */ |
|
module->element_level++; |
|
while(status == VC_CONTAINER_SUCCESS && |
|
(unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE)) |
|
{ |
|
offset = STREAM_POSITION(p_ctx); |
|
status = mkv_read_element(p_ctx, size, id); |
|
if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); |
|
} |
|
module->element_level--; |
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
int64_t offset = STREAM_POSITION(p_ctx); |
|
bool unknown_size = size < 0; |
|
|
|
/* Read contained elements */ |
|
/* Initialise state used by reader */ |
|
module->state.level = 0; |
|
module->state.levels[0].offset = STREAM_POSITION(p_ctx); |
|
module->state.levels[0].size = size; |
|
module->state.levels[0].id = MKV_ELEMENT_ID_SEGMENT; |
|
module->state.levels[0].data_start = 0; |
|
module->state.levels[0].data_offset = 0; |
|
module->timecode_scale = 1000000; |
|
module->duration = 0.0; |
|
module->segment_offset = STREAM_POSITION(p_ctx); |
|
module->segment_size = size; |
|
|
|
/* Read contained elements until we have all the information we need to start |
|
* playing the stream */ |
|
module->element_level++; |
|
while(status == VC_CONTAINER_SUCCESS && |
|
(unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE)) |
|
{ |
|
MKV_ELEMENT_T *child = mkv_elements_list; |
|
MKV_ELEMENT_ID_T child_id; |
|
int64_t child_size; |
|
|
|
offset = STREAM_POSITION(p_ctx); |
|
|
|
status = mkv_read_element_header(p_ctx, size, &child_id, &child_size, id, &child); |
|
if(status != VC_CONTAINER_SUCCESS) break; |
|
|
|
if(child_id == MKV_ELEMENT_ID_CLUSTER) |
|
{ |
|
/* We found the start of the data */ |
|
module->cluster_offset = module->element_offset; |
|
module->state.level = 1; |
|
module->state.levels[1].offset = STREAM_POSITION(p_ctx); |
|
module->state.levels[1].size = child_size; |
|
module->state.levels[1].id = MKV_ELEMENT_ID_CLUSTER; |
|
module->state.levels[1].data_start = 0; |
|
module->state.levels[1].data_offset = 0; |
|
break; |
|
} |
|
|
|
status = mkv_read_element_data(p_ctx, child, child_size, size); |
|
if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); |
|
} |
|
|
|
module->element_level--; |
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; |
|
VC_CONTAINER_TRACK_MODULE_T *track_module; |
|
VC_CONTAINER_TRACK_T *track; |
|
VC_CONTAINER_FOURCC_T fourcc = 0, variant = 0; |
|
unsigned int i, extra_size = 0, extra_offset = 0, is_wf = 0, is_bmih = 0; |
|
|
|
/* Allocate and initialise track data */ |
|
if(p_ctx->tracks_num >= MKV_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; |
|
p_ctx->tracks[p_ctx->tracks_num] = track = |
|
vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); |
|
if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
|
|
|
module->parsing = track; |
|
track_module = track->priv->module; |
|
track->is_enabled = true; |
|
track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; |
|
track_module->timecode_scale = 1.0; |
|
track_module->es_type.video.frame_rate = 0; |
|
|
|
status = mkv_read_elements( p_ctx, id, size ); |
|
if(status != VC_CONTAINER_SUCCESS) goto error; |
|
|
|
/* Sanity check the data we got from the track entry */ |
|
if(!track_module->number || !track_module->type) |
|
{ status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto error; } |
|
|
|
/* Check the encodings for the track are supported */ |
|
if(track_module->encodings_num > MKV_MAX_ENCODINGS) |
|
{ status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } |
|
for(i = 0; i < track_module->encodings_num; i++) |
|
{ |
|
if(track_module->encodings[i].type != MKV_CONTENT_ENCODING_COMPRESSION_HEADER || |
|
!track_module->encodings[i].data_size) |
|
{ status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } |
|
} |
|
|
|
/* Find out the track type */ |
|
if(track_module->type == 0x1) |
|
es_type = VC_CONTAINER_ES_TYPE_VIDEO; |
|
else if(track_module->type == 0x2) |
|
es_type = VC_CONTAINER_ES_TYPE_AUDIO; |
|
else if(track_module->type == 0x11) |
|
es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; |
|
|
|
if(es_type == VC_CONTAINER_ES_TYPE_UNKNOWN) |
|
{ status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } |
|
|
|
if(!strcmp(track_module->codecid, "V_MS/VFW/FOURCC")) |
|
{ |
|
if(vc_container_bitmapinfoheader_to_es_format(track->format->extradata, |
|
track->format->extradata_size, &extra_offset, &extra_size, |
|
track->format) == VC_CONTAINER_SUCCESS) |
|
{ |
|
fourcc = track->format->codec; |
|
is_bmih = 1; |
|
} |
|
track->format->extradata += extra_offset; |
|
track->format->extradata_size = extra_size; |
|
} |
|
else if(!strcmp(track_module->codecid, "A_MS/ACM")) |
|
{ |
|
if(vc_container_waveformatex_to_es_format(track->format->extradata, |
|
track->format->extradata_size, &extra_offset, &extra_size, |
|
track->format) == VC_CONTAINER_SUCCESS) |
|
{ |
|
fourcc = track->format->codec; |
|
is_wf = 1; |
|
} |
|
track->format->extradata += extra_offset; |
|
track->format->extradata_size = extra_size; |
|
} |
|
else if((!strncmp(track_module->codecid, "A_AAC/MPEG2/", sizeof("A_AAC/MPEG2/")-1) || |
|
!strncmp(track_module->codecid, "A_AAC/MPEG4/", sizeof("A_AAC/MPEG4/")-1)) && |
|
!track->format->extradata_size) |
|
{ |
|
static const unsigned int sample_rates[16] = |
|
{96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; |
|
unsigned int samplerate, samplerate_idx, profile, sbr = 0; |
|
uint8_t *extra; |
|
|
|
fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant); |
|
|
|
/* Create extra data */ |
|
if( !strcmp( &track_module->codecid[12], "MAIN" ) ) profile = 0; |
|
else if( !strcmp( &track_module->codecid[12], "LC" ) ) profile = 1; |
|
else if( !strcmp( &track_module->codecid[12], "SSR" ) ) profile = 2; |
|
else if( !strcmp( &track_module->codecid[12], "LC/SBR" ) ) { profile = 1; sbr = 1; } |
|
else profile = 3; |
|
|
|
samplerate = track_module->es_type.audio.sampling_frequency; |
|
for( samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++ ) |
|
if( sample_rates[samplerate_idx] == samplerate ) break; |
|
|
|
status = vc_container_track_allocate_extradata(p_ctx, track, sbr ? 5 : 2); |
|
if(status != VC_CONTAINER_SUCCESS) goto error; |
|
track->format->extradata_size = sbr ? 5 : 2; |
|
extra = track->format->extradata; |
|
|
|
extra[0] = ((profile + 1) << 3) | ((samplerate_idx & 0xe) >> 1); |
|
extra[1] = ((samplerate_idx & 0x1) << 7) | (track_module->es_type.audio.channels << 3); |
|
|
|
if(sbr) |
|
{ |
|
unsigned int sync_extension_type = 0x2B7; |
|
samplerate = track_module->es_type.audio.output_sampling_frequency; |
|
for(samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++) |
|
if(sample_rates[samplerate_idx] == samplerate) break; |
|
extra[2] = (sync_extension_type >> 3) & 0xFF; |
|
extra[3] = ((sync_extension_type & 0x7) << 5) | 5; |
|
extra[4] = (1 << 7) | (samplerate_idx << 3); |
|
} |
|
} |
|
else fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant); |
|
|
|
if(!fourcc) |
|
{ |
|
LOG_DEBUG(p_ctx, "codec id %s is not supported", track_module->codecid); |
|
} |
|
|
|
LOG_DEBUG(p_ctx, "found track %4.4s", (char *)&fourcc); |
|
track->format->codec = fourcc; |
|
track->format->codec_variant = variant; |
|
track->format->es_type = es_type; |
|
|
|
switch(es_type) |
|
{ |
|
case VC_CONTAINER_ES_TYPE_VIDEO: |
|
if(!is_bmih) |
|
{ |
|
track->format->type->video.width = track_module->es_type.video.pixel_width; |
|
track->format->type->video.height = track_module->es_type.video.pixel_height; |
|
} |
|
track->format->type->video.visible_width = track->format->type->video.width; |
|
track->format->type->video.visible_height = track->format->type->video.height; |
|
if(track_module->es_type.video.pixel_crop_left < track->format->type->video.visible_width && |
|
track_module->es_type.video.pixel_crop_top < track->format->type->video.visible_height) |
|
{ |
|
track->format->type->video.x_offset = track_module->es_type.video.pixel_crop_left; |
|
track->format->type->video.y_offset = track_module->es_type.video.pixel_crop_right; |
|
track->format->type->video.visible_width -= track->format->type->video.x_offset; |
|
track->format->type->video.visible_height -= track->format->type->video.y_offset; |
|
} |
|
if(track_module->es_type.video.pixel_crop_right < track->format->type->video.visible_width && |
|
track_module->es_type.video.pixel_crop_bottom < track->format->type->video.visible_height) |
|
{ |
|
track->format->type->video.visible_width -= track_module->es_type.video.pixel_crop_right; |
|
track->format->type->video.visible_height -= track_module->es_type.video.pixel_crop_bottom; |
|
} |
|
if(track_module->es_type.video.frame_rate) |
|
{ |
|
track->format->type->video.frame_rate_den = 100; |
|
track->format->type->video.frame_rate_num = 100 * track_module->es_type.video.frame_rate; |
|
} |
|
if(track_module->es_type.video.display_width && track_module->es_type.video.display_height) |
|
{ |
|
track->format->type->video.par_num = track_module->es_type.video.display_width * |
|
track->format->type->video.visible_height; |
|
track->format->type->video.par_den = track_module->es_type.video.display_height * |
|
track->format->type->video.visible_width; |
|
vc_container_maths_rational_simplify(&track->format->type->video.par_num, |
|
&track->format->type->video.par_den); |
|
} |
|
break; |
|
case VC_CONTAINER_ES_TYPE_AUDIO: |
|
if(is_wf) break; |
|
track->format->type->audio.sample_rate = track_module->es_type.audio.sampling_frequency; |
|
if(track_module->es_type.audio.output_sampling_frequency) |
|
track->format->type->audio.sample_rate = track_module->es_type.audio.output_sampling_frequency; |
|
track->format->type->audio.channels = track_module->es_type.audio.channels; |
|
track->format->type->audio.bits_per_sample = track_module->es_type.audio.bit_depth; |
|
break; |
|
default: |
|
case VC_CONTAINER_ES_TYPE_SUBPICTURE: |
|
track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; |
|
if(!strcmp(track_module->codecid, "S_TEXT/ASCII")) |
|
track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UNKNOWN; |
|
} |
|
|
|
track->is_enabled = true; |
|
|
|
p_ctx->tracks_num++; |
|
return VC_CONTAINER_SUCCESS; |
|
|
|
error: |
|
for(i = 0; i < MKV_MAX_ENCODINGS; i++) free(track_module->encodings[i].data); |
|
vc_container_free_track(p_ctx, track); |
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_TRACK_T *track = module->parsing; |
|
VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; |
|
uint64_t value; |
|
|
|
/* Deal with string elements */ |
|
if( id == MKV_ELEMENT_ID_NAME || |
|
id == MKV_ELEMENT_ID_LANGUAGE || |
|
id == MKV_ELEMENT_ID_TRACK_CODEC_ID || |
|
id == MKV_ELEMENT_ID_TRACK_CODEC_NAME ) |
|
{ |
|
char stringbuf[MKV_MAX_STRING_SIZE+1]; |
|
|
|
if(size > MKV_MAX_STRING_SIZE) |
|
{ |
|
LOG_DEBUG(p_ctx, "string truncated (%i>%i)", (int)size, MKV_MAX_STRING_SIZE); |
|
size = MKV_MAX_STRING_SIZE; |
|
} |
|
if(READ_BYTES(p_ctx, stringbuf, size) != (size_t)size) |
|
{ |
|
//XXX do sane thing |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
stringbuf[size] = 0; /* null terminate */ |
|
|
|
LOG_FORMAT(p_ctx, "%s", stringbuf); |
|
|
|
if(id == MKV_ELEMENT_ID_TRACK_CODEC_ID) |
|
strncpy(track_module->codecid, stringbuf, MKV_CODECID_MAX-1); |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/* Deal with codec private data */ |
|
if( id == MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE ) |
|
{ |
|
status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
/* The rest are just unsigned integers */ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
switch(id) |
|
{ |
|
case MKV_ELEMENT_ID_TRACK_NUMBER: |
|
track_module->number = value; break; |
|
case MKV_ELEMENT_ID_TRACK_TYPE: |
|
track_module->type = value; break; |
|
case MKV_ELEMENT_ID_DEFAULT_DURATION: |
|
track_module->frame_duration = value; break; |
|
default: break; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; |
|
uint64_t value; |
|
|
|
/* Deal with floating point values first */ |
|
if(id == MKV_ELEMENT_ID_FRAME_RATE) |
|
{ |
|
double fvalue; |
|
status = mkv_read_element_data_float(p_ctx, size, &fvalue); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
track_module->es_type.video.frame_rate = fvalue; |
|
return status; |
|
} |
|
|
|
/* The rest are just unsigned integers */ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
switch(id) |
|
{ |
|
case MKV_ELEMENT_ID_PIXEL_WIDTH: track_module->es_type.video.pixel_width = value; break; |
|
case MKV_ELEMENT_ID_PIXEL_HEIGHT: track_module->es_type.video.pixel_height = value; break; |
|
case MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM: track_module->es_type.video.pixel_crop_bottom = value; break; |
|
case MKV_ELEMENT_ID_PIXEL_CROP_TOP: track_module->es_type.video.pixel_crop_top = value; break; |
|
case MKV_ELEMENT_ID_PIXEL_CROP_LEFT: track_module->es_type.video.pixel_crop_left = value; break; |
|
case MKV_ELEMENT_ID_PIXEL_CROP_RIGHT: track_module->es_type.video.pixel_crop_right = value; break; |
|
case MKV_ELEMENT_ID_DISPLAY_WIDTH: track_module->es_type.video.display_width = value; break; |
|
case MKV_ELEMENT_ID_DISPLAY_HEIGHT: track_module->es_type.video.display_height = value; break; |
|
case MKV_ELEMENT_ID_DISPLAY_UNIT: track_module->es_type.video.display_unit = value; break; |
|
case MKV_ELEMENT_ID_ASPECT_RATIO_TYPE: track_module->es_type.video.aspect_ratio_type = value; break; |
|
default: break; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; |
|
uint64_t value; |
|
|
|
/* Deal with floating point values first */ |
|
if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY || |
|
id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY) |
|
{ |
|
double fvalue; |
|
status = mkv_read_element_data_float(p_ctx, size, &fvalue); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY) |
|
track_module->es_type.audio.sampling_frequency = fvalue; |
|
|
|
if(id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY) |
|
track_module->es_type.audio.output_sampling_frequency = fvalue; |
|
|
|
return status; |
|
} |
|
|
|
/* The rest are just unsigned integers */ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
switch(id) |
|
{ |
|
case MKV_ELEMENT_ID_CHANNELS: track_module->es_type.audio.channels = value; break; |
|
case MKV_ELEMENT_ID_BIT_DEPTH: track_module->es_type.audio.bit_depth = value; break; |
|
default: break; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; |
|
VC_CONTAINER_STATUS_T status; |
|
status = mkv_read_elements(p_ctx, id, size); |
|
track_module->encodings_num++; |
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; |
|
VC_CONTAINER_STATUS_T status; |
|
uint64_t value; |
|
|
|
/* These are just unsigned integers */ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
if(track_module->encodings_num >= MKV_MAX_ENCODINGS) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; |
|
|
|
switch(id) |
|
{ |
|
case MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE: |
|
if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB; |
|
if(value == 1) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_ENCRYPTION; |
|
else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN; |
|
break; |
|
default: break; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; |
|
VC_CONTAINER_STATUS_T status; |
|
uint64_t value; |
|
uint8_t *data; |
|
|
|
if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO) |
|
{ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB; |
|
if(value == 3) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_HEADER; |
|
else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN; |
|
return status; |
|
} |
|
|
|
if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS) |
|
{ |
|
if(track_module->encodings[track_module->encodings_num].type == MKV_CONTENT_ENCODING_COMPRESSION_HEADER) |
|
{ |
|
if(size > MKV_MAX_ENCODING_DATA) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; |
|
|
|
data = malloc((int)size); |
|
if(!data) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
|
|
|
track_module->encodings[track_module->encodings_num].data = data; |
|
track_module->encodings[track_module->encodings_num].data_size = READ_BYTES(p_ctx, data, size); |
|
if(track_module->encodings[track_module->encodings_num].data_size != size) |
|
track_module->encodings[track_module->encodings_num].data_size = 0; |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
else |
|
{ |
|
SKIP_BYTES(p_ctx, size); |
|
} |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
uint64_t value; |
|
double fvalue; |
|
|
|
switch(id) |
|
{ |
|
case MKV_ELEMENT_ID_TIMECODE_SCALE: |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) break; |
|
module->timecode_scale = value; |
|
break; |
|
case MKV_ELEMENT_ID_DURATION: |
|
status = mkv_read_element_data_float(p_ctx, size, &fvalue); |
|
if(status != VC_CONTAINER_SUCCESS) break; |
|
module->duration = fvalue; |
|
break; |
|
case MKV_ELEMENT_ID_TITLE: |
|
case MKV_ELEMENT_ID_MUXING_APP: |
|
case MKV_ELEMENT_ID_WRITING_APP: |
|
SKIP_STRING(p_ctx, size, "string"); |
|
break; |
|
|
|
default: break; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
uint64_t value; |
|
|
|
if(id == MKV_ELEMENT_ID_SEEK) |
|
{ |
|
module->seekhead_elem_id = MKV_ELEMENT_ID_UNKNOWN; |
|
module->seekhead_elem_offset = -1; |
|
status = mkv_read_elements(p_ctx, id, size); |
|
if(status == VC_CONTAINER_SUCCESS && !module->cues_offset && |
|
module->seekhead_elem_id == MKV_ELEMENT_ID_CUES && module->seekhead_elem_offset) |
|
module->cues_offset = module->seekhead_elem_offset; |
|
if(status == VC_CONTAINER_SUCCESS && !module->tags_offset && |
|
module->seekhead_elem_id == MKV_ELEMENT_ID_TAGS && module->seekhead_elem_offset) |
|
module->tags_offset = module->seekhead_elem_offset; |
|
return status; |
|
} |
|
|
|
if(id == MKV_ELEMENT_ID_SEEK_ID) |
|
{ |
|
MKV_ELEMENT_T *element = mkv_elements_list; |
|
id = MKV_READ_ID(p_ctx, "Element ID"); |
|
module->seekhead_elem_id = id; |
|
|
|
/* Find out which Element we are dealing with */ |
|
while(element->id && id != element->id) element++; |
|
LOG_FORMAT(p_ctx, "element: %s (ID 0x%x)", element->psz_name, id); |
|
} |
|
else if(id == MKV_ELEMENT_ID_SEEK_POSITION) |
|
{ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
LOG_FORMAT(p_ctx, "offset: %"PRIi64, value + module->segment_offset); |
|
module->seekhead_elem_offset = value + module->segment_offset; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_PARAM_UNUSED(id); |
|
VC_CONTAINER_PARAM_UNUSED(size); |
|
module->cues_offset = module->element_offset; |
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_STATUS_T status; |
|
uint64_t value; |
|
|
|
/* All the values are unsigned integers */ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
switch(id) |
|
{ |
|
case MKV_ELEMENT_ID_CUE_TIME: |
|
module->cue_timecode = value; break; |
|
case MKV_ELEMENT_ID_CUE_TRACK: |
|
module->cue_track = value; break; |
|
case MKV_ELEMENT_ID_CUE_CLUSTER_POSITION: |
|
module->cue_cluster_offset = value; break; |
|
case MKV_ELEMENT_ID_CUE_BLOCK_NUMBER: |
|
module->cue_block = value; break; |
|
default: break; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_STATUS_T status; |
|
uint64_t value; |
|
|
|
/* The rest are just unsigned integers */ |
|
status = mkv_read_element_data_uint(p_ctx, size, &value); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
switch(id) |
|
{ |
|
//XXX |
|
case MKV_ELEMENT_ID_TIMECODE: module->state.cluster_timecode = value; break; |
|
case MKV_ELEMENT_ID_BLOCK_DURATION: module->state.frame_duration = value; break; |
|
default: break; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
/*******************************/ |
|
|
|
static VC_CONTAINER_STATUS_T mkv_skip_element(VC_CONTAINER_T *p_ctx, |
|
MKV_READER_STATE_T *state) |
|
{ |
|
/* Skip any trailing data from the current element */ |
|
int64_t skip = state->levels[state->level].offset + |
|
state->levels[state->level].size - STREAM_POSITION(p_ctx); |
|
|
|
if(skip < 0) /* Check for overruns */ |
|
{ |
|
/* Things have gone really bad here and we ended up reading past the end of the |
|
* element. We could maybe try to be clever and recover by seeking back to the end |
|
* of the element. However if we get there, the file is clearly corrupted so there's |
|
* no guarantee it would work anyway. */ |
|
LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of the element", -skip); |
|
return VC_CONTAINER_ERROR_CORRUPTED; |
|
} |
|
|
|
if(skip) |
|
LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread at the end of element", skip); |
|
|
|
state->level--; |
|
|
|
if (skip >= MKV_MAX_ELEMENT_SIZE) |
|
return SEEK(p_ctx, STREAM_POSITION(p_ctx) + skip); |
|
SKIP_BYTES(p_ctx, skip); |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_find_next_element(VC_CONTAINER_T *p_ctx, |
|
MKV_READER_STATE_T *state, MKV_ELEMENT_ID_T element_id) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
int64_t element_size, element_offset; |
|
MKV_ELEMENT_ID_T id; |
|
|
|
/* Skip all elements until we find the next requested element */ |
|
do |
|
{ |
|
MKV_ELEMENT_T *element = mkv_cluster_elements_list; |
|
|
|
/* Check whether we've reach the end of the parent element */ |
|
if(STREAM_POSITION(p_ctx) >= state->levels[state->level].offset + |
|
state->levels[state->level].size) |
|
return VC_CONTAINER_ERROR_NOT_FOUND; |
|
|
|
status = mkv_read_element_header(p_ctx, INT64_C(1) << 30, &id, |
|
&element_size, state->levels[state->level].id, &element); |
|
element_offset = STREAM_POSITION(p_ctx); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
if(id == element_id) break; |
|
if(element_id == MKV_ELEMENT_ID_BLOCKGROUP && id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break; |
|
|
|
if(element_id == MKV_ELEMENT_ID_BLOCK && id == MKV_ELEMENT_ID_REFERENCE_BLOCK) |
|
state->seen_ref_block = 1; |
|
|
|
/* Check whether we've reached the end of the parent element */ |
|
if(STREAM_POSITION(p_ctx) + element_size >= state->levels[state->level].offset + |
|
state->levels[state->level].size) |
|
return VC_CONTAINER_ERROR_NOT_FOUND; |
|
|
|
status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(1) << 30); |
|
#if 0 |
|
if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size); |
|
else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size); |
|
#endif |
|
} while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); |
|
|
|
if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) |
|
return STREAM_STATUS(p_ctx); |
|
|
|
state->level++; |
|
state->levels[state->level].offset = element_offset; |
|
state->levels[state->level].size = element_size; |
|
state->levels[state->level].id = id; |
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_find_next_segment(VC_CONTAINER_T *p_ctx, |
|
MKV_READER_STATE_T *state) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
int64_t element_size, element_offset; |
|
MKV_ELEMENT_ID_T id; |
|
|
|
/* Skip all elements until we find the next segment */ |
|
do |
|
{ |
|
MKV_ELEMENT_T *element = mkv_cluster_elements_list; |
|
|
|
status = mkv_read_element_header(p_ctx, INT64_C(-1), &id, |
|
&element_size, MKV_ELEMENT_ID_INVALID, &element); |
|
|
|
element_offset = STREAM_POSITION(p_ctx); |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
if(id == MKV_ELEMENT_ID_SEGMENT) break; |
|
|
|
status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(-1)); |
|
} while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); |
|
|
|
if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) |
|
return STREAM_STATUS(p_ctx); |
|
|
|
state->level++; |
|
state->levels[state->level].offset = element_offset; |
|
state->levels[state->level].size = element_size; |
|
state->levels[state->level].id = id; |
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_find_next_block(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; |
|
|
|
do |
|
{ |
|
if(state->level < 0) |
|
{ |
|
#ifdef ENABLE_MKV_EXTRA_LOGGING |
|
LOG_DEBUG(p_ctx, "find segment"); |
|
#endif |
|
status = mkv_find_next_segment(p_ctx, state); |
|
if(status == VC_CONTAINER_SUCCESS) |
|
{ |
|
LOG_ERROR(p_ctx, "multi-segment files not supported"); |
|
status = VC_CONTAINER_ERROR_EOS; |
|
break; |
|
} |
|
} |
|
if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK || |
|
state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) |
|
{ |
|
status = mkv_skip_element(p_ctx, state); |
|
} |
|
if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCKGROUP) |
|
{ |
|
#ifdef ENABLE_MKV_EXTRA_LOGGING |
|
LOG_DEBUG(p_ctx, "find block"); |
|
#endif |
|
state->seen_ref_block = 0; |
|
state->frame_duration = 0; |
|
status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCK); |
|
if(status == VC_CONTAINER_SUCCESS) break; |
|
|
|
if(status == VC_CONTAINER_ERROR_NOT_FOUND) |
|
status = mkv_skip_element(p_ctx, state); |
|
} |
|
if(state->levels[state->level].id == MKV_ELEMENT_ID_CLUSTER) |
|
{ |
|
#ifdef ENABLE_MKV_EXTRA_LOGGING |
|
LOG_DEBUG(p_ctx, "find blockgroup or simpleblock"); |
|
#endif |
|
state->frame_duration = 0; |
|
status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCKGROUP); |
|
if(status == VC_CONTAINER_SUCCESS && |
|
state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break; |
|
if(status == VC_CONTAINER_ERROR_NOT_FOUND) |
|
status = mkv_skip_element(p_ctx, state); |
|
} |
|
if(state->levels[state->level].id == MKV_ELEMENT_ID_SEGMENT) |
|
{ |
|
#ifdef ENABLE_MKV_EXTRA_LOGGING |
|
LOG_DEBUG(p_ctx, "find cluster"); |
|
#endif |
|
status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_CLUSTER); |
|
if(status == VC_CONTAINER_ERROR_NOT_FOUND) |
|
status = mkv_skip_element(p_ctx, state); |
|
} |
|
|
|
} while(status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); |
|
|
|
return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_next_frame_header(VC_CONTAINER_T *p_ctx, |
|
MKV_READER_STATE_T *state, uint32_t *pi_track, uint32_t *pi_length) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_TRACK_MODULE_T *track_module = 0; |
|
unsigned int i, track, flags, is_first_lace = 0; |
|
int64_t size, pts; |
|
|
|
if ((state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK || |
|
state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) && |
|
state->levels[state->level].data_start + state->levels[state->level].data_offset < |
|
state->levels[state->level].size) |
|
goto end; |
|
|
|
status = mkv_find_next_block(p_ctx, state); |
|
if (status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
/* We have a block */ |
|
size = state->levels[state->level].size; |
|
track = MKV_READ_UINT(p_ctx, "Track Number"); LOG_FORMAT(p_ctx, "Track Number: %u", track); |
|
pts = (int16_t)MKV_READ_U16(p_ctx, "Timecode"); |
|
flags = MKV_READ_U8(p_ctx, "Flags"); |
|
if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK) flags &= 0x0F; |
|
|
|
//TODO improve sanity checking |
|
/* Sanity checking */ |
|
if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; |
|
if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); |
|
|
|
for (i = 0; i < p_ctx->tracks_num; i++) |
|
if (p_ctx->tracks[i]->priv->module->number == track) |
|
{ track_module = p_ctx->tracks[i]->priv->module; break; } |
|
|
|
/* Finding out if we have a keyframe when dealing with an MKV_ELEMENT_ID_BLOCK is tricky */ |
|
if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK && |
|
i < p_ctx->tracks_num && p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) |
|
{ |
|
/* The absence of a ReferenceBlock element tells us if we are a keyframe or not. |
|
* The problem we have is that this element can appear before as well as after the block element. |
|
* To avoid seeking to look for this element, we do some guess work. */ |
|
if(!state->seen_ref_block && state->level && |
|
state->levels[state->level].offset + state->levels[state->level].size >= |
|
state->levels[state->level-1].offset + state->levels[state->level-1].size) |
|
flags |= 0x80; |
|
} |
|
|
|
/* Take care of the lacing */ |
|
state->lacing_num_frames = 0; |
|
if(i < p_ctx->tracks_num && (flags & 0x06)) |
|
{ |
|
unsigned int i, value = 0; |
|
int32_t fs = 0; |
|
|
|
state->lacing_num_frames = MKV_READ_U8(p_ctx, "Lacing Head"); |
|
state->lacing_size = 0; |
|
switch((flags & 0x06)>>1) |
|
{ |
|
case 1: /* Xiph lacing */ |
|
for(i = 0; i < state->lacing_num_frames; i++, fs = 0) |
|
{ |
|
do { |
|
value = vc_container_io_read_uint8(p_ctx->priv->io); |
|
fs += value; size--; |
|
} while(value == 255); |
|
LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs); |
|
if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue; |
|
state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs; |
|
} |
|
break; |
|
case 3: /* EBML lacing */ |
|
for(i = 0; i < state->lacing_num_frames; i++) |
|
{ |
|
if(!i) fs = MKV_READ_UINT(p_ctx, "Frame Size"); |
|
else fs += MKV_READ_SINT(p_ctx, "Frame Size"); |
|
LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs); |
|
if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue; |
|
state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs; |
|
} |
|
break; |
|
default: /* Fixed-size lacing */ |
|
state->lacing_size = size / (state->lacing_num_frames+1); |
|
break; |
|
} |
|
|
|
/* There is a max number of laced frames we can support but after we can still give back |
|
* all the other frames in 1 packet */ |
|
if(state->lacing_num_frames > MKV_MAX_LACING_NUM) |
|
state->lacing_num_frames = MKV_MAX_LACING_NUM; |
|
|
|
/* Sanity check the size of the frames */ |
|
if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; |
|
if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); |
|
state->lacing_current_size = state->lacing_size; |
|
if(!state->lacing_size) |
|
{ |
|
int64_t frames_size = 0; |
|
for(i = state->lacing_num_frames; i > 0; i--) |
|
{ |
|
if(frames_size + state->lacing_sizes[i-1] > size) /* return error ? */ |
|
state->lacing_sizes[i-1] = size - frames_size; |
|
frames_size += state->lacing_sizes[i-1]; |
|
} |
|
} |
|
state->lacing_current_size = 0; |
|
state->lacing_num_frames++; /* Will be decremented further down */ |
|
is_first_lace = 1; |
|
} |
|
|
|
state->track = i; |
|
state->pts = (state->cluster_timecode + pts) * module->timecode_scale; |
|
if(track_module) state->pts *= track_module->timecode_scale; |
|
state->pts /= 1000; |
|
state->flags = flags; |
|
|
|
state->frame_duration = state->frame_duration * module->timecode_scale / 1000; |
|
if(state->lacing_num_frames) state->frame_duration /= state->lacing_num_frames; |
|
if(!state->frame_duration && track_module) |
|
state->frame_duration = track_module->frame_duration / 1000; |
|
|
|
state->levels[state->level].data_start = STREAM_POSITION(p_ctx) - |
|
state->levels[state->level].offset; |
|
state->levels[state->level].data_offset = 0; |
|
|
|
/* Deal with header stripping compression */ |
|
state->header_size = 0; |
|
if(track_module && track_module->encodings_num) |
|
{ |
|
state->header_size = track_module->encodings[0].data_size; |
|
state->header_data = track_module->encodings[0].data; |
|
} |
|
state->header_size_backup = state->header_size; |
|
|
|
end: |
|
*pi_length = state->levels[state->level].size - state->levels[state->level].data_start - |
|
state->levels[state->level].data_offset + state->header_size; |
|
*pi_track = state->track; |
|
|
|
/* Special case for lacing */ |
|
if(state->lacing_num_frames && |
|
state->levels[state->level].data_offset >= state->lacing_current_size) |
|
{ |
|
/* We need to switch to the next lace */ |
|
if(--state->lacing_num_frames) |
|
{ |
|
unsigned int lace_size = state->lacing_size; |
|
if(!state->lacing_size) lace_size = state->lacing_sizes[state->lacing_num_frames-1]; |
|
state->lacing_current_size = lace_size; |
|
} |
|
state->levels[state->level].data_start += state->levels[state->level].data_offset; |
|
state->levels[state->level].data_offset = 0; |
|
if(!is_first_lace && state->frame_duration) |
|
state->pts += state->frame_duration; |
|
else if(!is_first_lace) |
|
state->pts = VC_CONTAINER_TIME_UNKNOWN; |
|
|
|
/* Deal with header stripping compression */ |
|
state->header_data -= (state->header_size_backup - state->header_size); |
|
state->header_size = state->header_size_backup; |
|
} |
|
if(state->lacing_num_frames) |
|
*pi_length = state->lacing_current_size - state->levels[state->level].data_offset + state->header_size; |
|
|
|
return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; |
|
} |
|
|
|
static VC_CONTAINER_STATUS_T mkv_read_frame_data(VC_CONTAINER_T *p_ctx, |
|
MKV_READER_STATE_T *state, uint8_t *p_data, uint32_t *pi_length) |
|
{ |
|
uint64_t size; |
|
uint32_t header_size; |
|
|
|
size = state->levels[state->level].size - state->levels[state->level].data_start - |
|
state->levels[state->level].data_offset; |
|
|
|
/* Special case for lacing */ |
|
if(state->lacing_num_frames) |
|
{ |
|
size = state->lacing_current_size - state->levels[state->level].data_offset; |
|
|
|
if(!p_data) |
|
{ |
|
size = SKIP_BYTES(p_ctx, size); |
|
state->levels[state->level].data_offset += size; |
|
return STREAM_STATUS(p_ctx); |
|
} |
|
} |
|
|
|
size += state->header_size; |
|
|
|
if(!p_data) return mkv_skip_element(p_ctx, state); |
|
if(size > *pi_length) size = *pi_length; |
|
|
|
header_size = state->header_size; |
|
if(header_size) |
|
{ |
|
if(header_size > size) header_size = size; |
|
memcpy(p_data, state->header_data, header_size); |
|
state->header_size -= header_size; |
|
state->header_data += header_size; |
|
size -= header_size; |
|
} |
|
|
|
size = READ_BYTES(p_ctx, p_data + header_size, size); |
|
state->levels[state->level].data_offset += size; |
|
*pi_length = size + header_size; |
|
|
|
return STREAM_STATUS(p_ctx); |
|
} |
|
|
|
/***************************************************************************** |
|
Functions exported as part of the Container Module API |
|
*****************************************************************************/ |
|
|
|
static VC_CONTAINER_STATUS_T mkv_reader_read(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
VC_CONTAINER_TRACK_T *p_track = 0; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
uint32_t buffer_size = 0, track = 0, data_size; |
|
MKV_READER_STATE_T *state = &module->state; |
|
|
|
/* If a specific track has been selected, we need to use the track packet state */ |
|
if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) |
|
{ |
|
p_track = p_ctx->tracks[p_packet->track]; |
|
state = p_track->priv->module->state; |
|
} |
|
|
|
/**/ |
|
if(state->eos) return VC_CONTAINER_ERROR_EOS; |
|
if(state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED; |
|
|
|
/* Look at the next frame header */ |
|
status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size); |
|
if(status == VC_CONTAINER_ERROR_EOS) state->eos = true; |
|
if(status == VC_CONTAINER_ERROR_CORRUPTED) state->corrupted = true; |
|
if(status != VC_CONTAINER_SUCCESS) return status; |
|
|
|
if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled) |
|
{ |
|
/* Skip frame */ |
|
status = mkv_read_frame_data(p_ctx, state, 0, &data_size); |
|
if (status != VC_CONTAINER_SUCCESS) return status; |
|
return VC_CONTAINER_ERROR_CONTINUE; |
|
} |
|
|
|
if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ |
|
return mkv_read_frame_data(p_ctx, state, 0, &data_size); |
|
|
|
p_packet->dts = p_packet->pts = state->pts; |
|
p_packet->flags = 0; |
|
if(state->flags & 0x80) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; |
|
if(!state->levels[state->level].data_offset) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; |
|
p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; |
|
p_packet->size = data_size; |
|
p_packet->track = track; |
|
|
|
if(flags & VC_CONTAINER_READ_FLAG_SKIP) |
|
return mkv_read_frame_data(p_ctx, state, 0, &data_size ); |
|
else if(flags & VC_CONTAINER_READ_FLAG_INFO) |
|
return VC_CONTAINER_SUCCESS; |
|
|
|
/* Read the frame data */ |
|
buffer_size = p_packet->buffer_size; |
|
status = mkv_read_frame_data(p_ctx, state, p_packet->data, &buffer_size); |
|
if(status != VC_CONTAINER_SUCCESS) |
|
{ |
|
/* FIXME */ |
|
return status; |
|
} |
|
|
|
p_packet->size = buffer_size; |
|
if(buffer_size != data_size) |
|
p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; |
|
|
|
return status; |
|
} |
|
|
|
/*****************************************************************************/ |
|
static VC_CONTAINER_STATUS_T mkv_reader_seek(VC_CONTAINER_T *p_ctx, |
|
int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) |
|
{ |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
MKV_READER_STATE_T *state = &module->state; |
|
uint64_t offset = 0, prev_offset = 0, position = STREAM_POSITION(p_ctx); |
|
int64_t time_offset = 0, prev_time_offset = 0; |
|
unsigned int i, video_track; |
|
MKV_ELEMENT_T *element = mkv_cue_elements_list; |
|
int64_t size, element_size; |
|
MKV_ELEMENT_ID_T id; |
|
VC_CONTAINER_PARAM_UNUSED(mode); |
|
VC_CONTAINER_PARAM_UNUSED(flags); |
|
|
|
/* Find out if we have a video track */ |
|
for(video_track = 0; video_track < p_ctx->tracks_num; video_track++) |
|
if(p_ctx->tracks[video_track]->is_enabled && |
|
p_ctx->tracks[video_track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; |
|
|
|
if(!*p_offset) goto end; /* Nothing much to do */ |
|
if(!module->cues_offset) {status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; goto error;} |
|
|
|
/* We need to do a search in the cue list */ |
|
status = SEEK(p_ctx, module->cues_offset); |
|
if(status != VC_CONTAINER_SUCCESS) goto error; |
|
|
|
/* First read the header of the cues element */ |
|
status = mkv_read_element_header(p_ctx, INT64_C(-1) /* TODO */, &id, &element_size, |
|
MKV_ELEMENT_ID_SEGMENT, &element); |
|
if(status != VC_CONTAINER_SUCCESS || id != MKV_ELEMENT_ID_CUES) goto error; |
|
size = element_size; |
|
|
|
module->elements_list = mkv_cue_elements_list; |
|
do |
|
{ |
|
MKV_ELEMENT_T *element = mkv_cue_elements_list; |
|
int64_t element_offset = STREAM_POSITION(p_ctx); |
|
|
|
/* Exit condition for when we've scanned the whole cues list */ |
|
if(size <= 0) |
|
{ |
|
if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) |
|
break; /* Just use the last valid entry in that case */ |
|
status = VC_CONTAINER_ERROR_EOS; |
|
goto error; |
|
} |
|
|
|
status = mkv_read_element_header(p_ctx, size, &id, &element_size, |
|
MKV_ELEMENT_ID_CUES, &element); |
|
size -= STREAM_POSITION(p_ctx) - element_offset; |
|
if(status == VC_CONTAINER_SUCCESS && element->id != MKV_ELEMENT_ID_UNKNOWN) |
|
status = mkv_read_element_data(p_ctx, element, element_size, size); |
|
|
|
if(status != VC_CONTAINER_SUCCESS || element->id == MKV_ELEMENT_ID_UNKNOWN) |
|
{ |
|
if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) |
|
break; /* Just use the last valid entry in that case */ |
|
goto error; |
|
} |
|
|
|
size -= element_size; |
|
if(id != MKV_ELEMENT_ID_CUE_POINT) continue; |
|
|
|
/* Ignore cue points which don't belong to the track we want */ |
|
if(video_track != p_ctx->tracks_num && |
|
p_ctx->tracks[video_track]->priv->module->number != module->cue_track) continue; |
|
|
|
time_offset = module->cue_timecode * module->timecode_scale / 1000; |
|
offset = module->cue_cluster_offset; |
|
LOG_DEBUG(p_ctx, "INDEX: %"PRIi64, time_offset); |
|
if( time_offset > *p_offset ) |
|
{ |
|
if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) |
|
{ |
|
time_offset = prev_time_offset; |
|
offset = prev_offset; |
|
} |
|
break; |
|
} |
|
prev_time_offset = time_offset; |
|
prev_offset = offset; |
|
} while( 1 ); |
|
module->elements_list = mkv_elements_list; |
|
*p_offset = time_offset; |
|
|
|
end: |
|
/* Try seeking to the requested position */ |
|
status = SEEK(p_ctx, module->segment_offset + offset); |
|
if(status != VC_CONTAINER_SUCCESS && status != VC_CONTAINER_ERROR_EOS) goto error; |
|
|
|
/* Reinitialise the state */ |
|
memset(state, 0, sizeof(*state)); |
|
state->levels[0].offset = module->segment_offset; |
|
state->levels[0].size = module->segment_size; |
|
state->levels[0].id = MKV_ELEMENT_ID_SEGMENT; |
|
if(status == VC_CONTAINER_ERROR_EOS) state->eos = true; |
|
for(i = 0; i < p_ctx->tracks_num; i++) |
|
{ |
|
VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; |
|
p_track->priv->module->state = state; |
|
} |
|
|
|
/* If we have a video track, we skip frames until the next keyframe */ |
|
for(i = 0; video_track != p_ctx->tracks_num && i < 200 /* limit search */; ) |
|
{ |
|
uint32_t track, data_size; |
|
status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size); |
|
if(status != VC_CONTAINER_SUCCESS) break; //FIXME |
|
if(track == video_track) i++; |
|
if(track == video_track && (state->flags & 0x80) && |
|
state->pts >= time_offset) break; |
|
|
|
/* Skip frame */ |
|
status = mkv_read_frame_data(p_ctx, state, 0, &data_size); |
|
} |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
|
|
error: |
|
/* Reset everything as it was before the seek */ |
|
SEEK(p_ctx, position); |
|
if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FAILED; |
|
return status; |
|
} |
|
|
|
/*****************************************************************************/ |
|
static VC_CONTAINER_STATUS_T mkv_reader_close(VC_CONTAINER_T *p_ctx) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
unsigned int i, j; |
|
|
|
for(i = 0; i < p_ctx->tracks_num; i++) |
|
{ |
|
for(j = 0; j < MKV_MAX_ENCODINGS; j++) |
|
free(p_ctx->tracks[i]->priv->module->encodings[j].data); |
|
vc_container_free_track(p_ctx, p_ctx->tracks[i]); |
|
} |
|
free(module); |
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/*****************************************************************************/ |
|
VC_CONTAINER_STATUS_T mkv_reader_open(VC_CONTAINER_T *p_ctx) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = 0; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; |
|
uint8_t buffer[4]; |
|
|
|
// Can start with ASCII strings ???? |
|
|
|
/* Check for an EBML element */ |
|
if(PEEK_BYTES(p_ctx, buffer, 4) < 4 || |
|
buffer[0] != 0x1A || buffer[1] != 0x45 || buffer[2] != 0xDF || buffer[3] != 0xA3) |
|
return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
|
|
/* |
|
* We are dealing with an MKV file |
|
*/ |
|
|
|
/* Allocate our context */ |
|
module = malloc(sizeof(*module)); |
|
if(!module) {status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error;} |
|
memset(module, 0, sizeof(*module)); |
|
p_ctx->priv->module = module; |
|
p_ctx->tracks = module->tracks; |
|
module->elements_list = mkv_elements_list; |
|
|
|
/* Read and sanity check the EBML header */ |
|
status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN); |
|
if(status != VC_CONTAINER_SUCCESS) goto error; |
|
if(!module->is_doctype_valid) {status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; goto error;} |
|
|
|
/* Read the other elements until we find the start of the data */ |
|
do |
|
{ |
|
status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN); |
|
if(status != VC_CONTAINER_SUCCESS) break; |
|
|
|
if(module->cluster_offset) break; |
|
} while(1); |
|
|
|
/* Bail out if we didn't find a track */ |
|
if(!p_ctx->tracks_num) |
|
{ |
|
status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; |
|
goto error; |
|
} |
|
|
|
/* |
|
* We now have all the information we really need to start playing the stream |
|
*/ |
|
|
|
p_ctx->priv->pf_close = mkv_reader_close; |
|
p_ctx->priv->pf_read = mkv_reader_read; |
|
p_ctx->priv->pf_seek = mkv_reader_seek; |
|
p_ctx->duration = module->duration / 1000 * module->timecode_scale; |
|
|
|
/* Check if we're done */ |
|
if(!STREAM_SEEKABLE(p_ctx)) |
|
return VC_CONTAINER_SUCCESS; |
|
|
|
if(module->cues_offset && (int64_t)module->cues_offset < p_ctx->size) |
|
p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; |
|
|
|
if(module->tags_offset) |
|
{ |
|
status = SEEK(p_ctx, module->tags_offset); |
|
if(status == VC_CONTAINER_SUCCESS) |
|
status = mkv_read_element(p_ctx, INT64_C(-1) /*FIXME*/, MKV_ELEMENT_ID_SEGMENT); |
|
} |
|
|
|
/* Seek back to the start of the data */ |
|
return SEEK(p_ctx, module->state.levels[1].offset); |
|
|
|
error: |
|
LOG_DEBUG(p_ctx, "mkv: error opening stream (%i)", status); |
|
if(module) mkv_reader_close(p_ctx); |
|
return status; |
|
} |
|
|
|
/******************************************************************************** |
|
Entrypoint function |
|
********************************************************************************/ |
|
|
|
#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) |
|
# pragma weak reader_open mkv_reader_open |
|
#endif
|
|
|