| /* | 
 |  * Copyright © 2010 Mozilla Foundation | 
 |  * | 
 |  * This program is made available under an ISC-style license.  See the | 
 |  * accompanying file LICENSE for details. | 
 |  */ | 
 | #include <assert.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include "third_party/nestegg/halloc/halloc.h" | 
 | #include "third_party/nestegg/include/nestegg/nestegg.h" | 
 |  | 
 | /* EBML Elements */ | 
 | #define ID_EBML                 0x1a45dfa3 | 
 | #define ID_EBML_VERSION         0x4286 | 
 | #define ID_EBML_READ_VERSION    0x42f7 | 
 | #define ID_EBML_MAX_ID_LENGTH   0x42f2 | 
 | #define ID_EBML_MAX_SIZE_LENGTH 0x42f3 | 
 | #define ID_DOCTYPE              0x4282 | 
 | #define ID_DOCTYPE_VERSION      0x4287 | 
 | #define ID_DOCTYPE_READ_VERSION 0x4285 | 
 |  | 
 | /* Global Elements */ | 
 | #define ID_VOID                 0xec | 
 | #define ID_CRC32                0xbf | 
 |  | 
 | /* WebM Elements */ | 
 | #define ID_SEGMENT              0x18538067 | 
 |  | 
 | /* Seek Head Elements */ | 
 | #define ID_SEEK_HEAD            0x114d9b74 | 
 | #define ID_SEEK                 0x4dbb | 
 | #define ID_SEEK_ID              0x53ab | 
 | #define ID_SEEK_POSITION        0x53ac | 
 |  | 
 | /* Info Elements */ | 
 | #define ID_INFO                 0x1549a966 | 
 | #define ID_TIMECODE_SCALE       0x2ad7b1 | 
 | #define ID_DURATION             0x4489 | 
 |  | 
 | /* Cluster Elements */ | 
 | #define ID_CLUSTER              0x1f43b675 | 
 | #define ID_TIMECODE             0xe7 | 
 | #define ID_BLOCK_GROUP          0xa0 | 
 | #define ID_SIMPLE_BLOCK         0xa3 | 
 |  | 
 | /* BlockGroup Elements */ | 
 | #define ID_BLOCK                0xa1 | 
 | #define ID_BLOCK_DURATION       0x9b | 
 | #define ID_REFERENCE_BLOCK      0xfb | 
 | #define ID_DISCARD_PADDING      0x75a2 | 
 |  | 
 | /* Tracks Elements */ | 
 | #define ID_TRACKS               0x1654ae6b | 
 | #define ID_TRACK_ENTRY          0xae | 
 | #define ID_TRACK_NUMBER         0xd7 | 
 | #define ID_TRACK_UID            0x73c5 | 
 | #define ID_TRACK_TYPE           0x83 | 
 | #define ID_FLAG_ENABLED         0xb9 | 
 | #define ID_FLAG_DEFAULT         0x88 | 
 | #define ID_FLAG_LACING          0x9c | 
 | #define ID_TRACK_TIMECODE_SCALE 0x23314f | 
 | #define ID_LANGUAGE             0x22b59c | 
 | #define ID_CODEC_ID             0x86 | 
 | #define ID_CODEC_PRIVATE        0x63a2 | 
 | #define ID_CODEC_DELAY          0x56aa | 
 | #define ID_SEEK_PREROLL         0x56bb | 
 |  | 
 | /* Video Elements */ | 
 | #define ID_VIDEO                0xe0 | 
 | #define ID_STEREO_MODE          0x53b8 | 
 | #define ID_PIXEL_WIDTH          0xb0 | 
 | #define ID_PIXEL_HEIGHT         0xba | 
 | #define ID_PIXEL_CROP_BOTTOM    0x54aa | 
 | #define ID_PIXEL_CROP_TOP       0x54bb | 
 | #define ID_PIXEL_CROP_LEFT      0x54cc | 
 | #define ID_PIXEL_CROP_RIGHT     0x54dd | 
 | #define ID_DISPLAY_WIDTH        0x54b0 | 
 | #define ID_DISPLAY_HEIGHT       0x54ba | 
 |  | 
 | /* Audio Elements */ | 
 | #define ID_AUDIO                0xe1 | 
 | #define ID_SAMPLING_FREQUENCY   0xb5 | 
 | #define ID_CHANNELS             0x9f | 
 | #define ID_BIT_DEPTH            0x6264 | 
 |  | 
 | /* Cues Elements */ | 
 | #define ID_CUES                 0x1c53bb6b | 
 | #define ID_CUE_POINT            0xbb | 
 | #define ID_CUE_TIME             0xb3 | 
 | #define ID_CUE_TRACK_POSITIONS  0xb7 | 
 | #define ID_CUE_TRACK            0xf7 | 
 | #define ID_CUE_CLUSTER_POSITION 0xf1 | 
 | #define ID_CUE_BLOCK_NUMBER     0x5378 | 
 |  | 
 | /* EBML Types */ | 
 | enum ebml_type_enum { | 
 |   TYPE_UNKNOWN, | 
 |   TYPE_MASTER, | 
 |   TYPE_UINT, | 
 |   TYPE_FLOAT, | 
 |   TYPE_INT, | 
 |   TYPE_STRING, | 
 |   TYPE_BINARY | 
 | }; | 
 |  | 
 | #define LIMIT_STRING            (1 << 20) | 
 | #define LIMIT_BINARY            (1 << 24) | 
 | #define LIMIT_BLOCK             (1 << 30) | 
 | #define LIMIT_FRAME             (1 << 28) | 
 |  | 
 | /* Field Flags */ | 
 | #define DESC_FLAG_NONE          0 | 
 | #define DESC_FLAG_MULTI         (1 << 0) | 
 | #define DESC_FLAG_SUSPEND       (1 << 1) | 
 | #define DESC_FLAG_OFFSET        (1 << 2) | 
 |  | 
 | /* Block Header Flags */ | 
 | #define BLOCK_FLAGS_LACING      6 | 
 |  | 
 | /* Lacing Constants */ | 
 | #define LACING_NONE             0 | 
 | #define LACING_XIPH             1 | 
 | #define LACING_FIXED            2 | 
 | #define LACING_EBML             3 | 
 |  | 
 | /* Track Types */ | 
 | #define TRACK_TYPE_VIDEO        1 | 
 | #define TRACK_TYPE_AUDIO        2 | 
 |  | 
 | /* Track IDs */ | 
 | #define TRACK_ID_VP8            "V_VP8" | 
 | #define TRACK_ID_VP9            "V_VP9" | 
 | #define TRACK_ID_VORBIS         "A_VORBIS" | 
 | #define TRACK_ID_OPUS           "A_OPUS" | 
 |  | 
 | enum vint_mask { | 
 |   MASK_NONE, | 
 |   MASK_FIRST_BIT | 
 | }; | 
 |  | 
 | struct ebml_binary { | 
 |   unsigned char * data; | 
 |   size_t length; | 
 | }; | 
 |  | 
 | struct ebml_list_node { | 
 |   struct ebml_list_node * next; | 
 |   uint64_t id; | 
 |   void * data; | 
 | }; | 
 |  | 
 | struct ebml_list { | 
 |   struct ebml_list_node * head; | 
 |   struct ebml_list_node * tail; | 
 | }; | 
 |  | 
 | struct ebml_type { | 
 |   union ebml_value { | 
 |     uint64_t u; | 
 |     double f; | 
 |     int64_t i; | 
 |     char * s; | 
 |     struct ebml_binary b; | 
 |   } v; | 
 |   enum ebml_type_enum type; | 
 |   int read; | 
 | }; | 
 |  | 
 | /* EBML Definitions */ | 
 | struct ebml { | 
 |   struct ebml_type ebml_version; | 
 |   struct ebml_type ebml_read_version; | 
 |   struct ebml_type ebml_max_id_length; | 
 |   struct ebml_type ebml_max_size_length; | 
 |   struct ebml_type doctype; | 
 |   struct ebml_type doctype_version; | 
 |   struct ebml_type doctype_read_version; | 
 | }; | 
 |  | 
 | /* Matroksa Definitions */ | 
 | struct seek { | 
 |   struct ebml_type id; | 
 |   struct ebml_type position; | 
 | }; | 
 |  | 
 | struct seek_head { | 
 |   struct ebml_list seek; | 
 | }; | 
 |  | 
 | struct info { | 
 |   struct ebml_type timecode_scale; | 
 |   struct ebml_type duration; | 
 | }; | 
 |  | 
 | struct block_group { | 
 |   struct ebml_type duration; | 
 |   struct ebml_type reference_block; | 
 |   struct ebml_type discard_padding; | 
 | }; | 
 |  | 
 | struct cluster { | 
 |   struct ebml_type timecode; | 
 |   struct ebml_list block_group; | 
 | }; | 
 |  | 
 | struct video { | 
 |   struct ebml_type stereo_mode; | 
 |   struct ebml_type pixel_width; | 
 |   struct ebml_type pixel_height; | 
 |   struct ebml_type pixel_crop_bottom; | 
 |   struct ebml_type pixel_crop_top; | 
 |   struct ebml_type pixel_crop_left; | 
 |   struct ebml_type pixel_crop_right; | 
 |   struct ebml_type display_width; | 
 |   struct ebml_type display_height; | 
 | }; | 
 |  | 
 | struct audio { | 
 |   struct ebml_type sampling_frequency; | 
 |   struct ebml_type channels; | 
 |   struct ebml_type bit_depth; | 
 | }; | 
 |  | 
 | struct track_entry { | 
 |   struct ebml_type number; | 
 |   struct ebml_type uid; | 
 |   struct ebml_type type; | 
 |   struct ebml_type flag_enabled; | 
 |   struct ebml_type flag_default; | 
 |   struct ebml_type flag_lacing; | 
 |   struct ebml_type track_timecode_scale; | 
 |   struct ebml_type language; | 
 |   struct ebml_type codec_id; | 
 |   struct ebml_type codec_private; | 
 |   struct ebml_type codec_delay; | 
 |   struct ebml_type seek_preroll; | 
 |   struct video video; | 
 |   struct audio audio; | 
 | }; | 
 |  | 
 | struct tracks { | 
 |   struct ebml_list track_entry; | 
 | }; | 
 |  | 
 | struct cue_track_positions { | 
 |   struct ebml_type track; | 
 |   struct ebml_type cluster_position; | 
 |   struct ebml_type block_number; | 
 | }; | 
 |  | 
 | struct cue_point { | 
 |   struct ebml_type time; | 
 |   struct ebml_list cue_track_positions; | 
 | }; | 
 |  | 
 | struct cues { | 
 |   struct ebml_list cue_point; | 
 | }; | 
 |  | 
 | struct segment { | 
 |   struct ebml_list seek_head; | 
 |   struct info info; | 
 |   struct ebml_list cluster; | 
 |   struct tracks tracks; | 
 |   struct cues cues; | 
 | }; | 
 |  | 
 | /* Misc. */ | 
 | struct pool_ctx { | 
 |   char dummy; | 
 | }; | 
 |  | 
 | struct list_node { | 
 |   struct list_node * previous; | 
 |   struct ebml_element_desc * node; | 
 |   unsigned char * data; | 
 | }; | 
 |  | 
 | struct saved_state { | 
 |   int64_t stream_offset; | 
 |   struct list_node * ancestor; | 
 |   uint64_t last_id; | 
 |   uint64_t last_size; | 
 |   int last_valid; | 
 | }; | 
 |  | 
 | struct frame { | 
 |   unsigned char * data; | 
 |   size_t length; | 
 |   struct frame * next; | 
 | }; | 
 |  | 
 | /* Public (opaque) Structures */ | 
 | struct nestegg { | 
 |   nestegg_io * io; | 
 |   nestegg_log log; | 
 |   struct pool_ctx * alloc_pool; | 
 |   uint64_t last_id; | 
 |   uint64_t last_size; | 
 |   int last_valid; | 
 |   struct list_node * ancestor; | 
 |   struct ebml ebml; | 
 |   struct segment segment; | 
 |   int64_t segment_offset; | 
 |   unsigned int track_count; | 
 | }; | 
 |  | 
 | struct nestegg_packet { | 
 |   uint64_t track; | 
 |   uint64_t timecode; | 
 |   struct frame * frame; | 
 |   int64_t discard_padding; | 
 | }; | 
 |  | 
 | /* Element Descriptor */ | 
 | struct ebml_element_desc { | 
 |   char const * name; | 
 |   uint64_t id; | 
 |   enum ebml_type_enum type; | 
 |   size_t offset; | 
 |   unsigned int flags; | 
 |   struct ebml_element_desc * children; | 
 |   size_t size; | 
 |   size_t data_offset; | 
 | }; | 
 |  | 
 | #define E_FIELD(ID, TYPE, STRUCT, FIELD) \ | 
 |   { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_NONE, NULL, 0, 0 } | 
 | #define E_MASTER(ID, TYPE, STRUCT, FIELD) \ | 
 |   { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_MULTI, ne_ ## FIELD ## _elements, \ | 
 |       sizeof(struct FIELD), 0 } | 
 | #define E_SINGLE_MASTER_O(ID, TYPE, STRUCT, FIELD) \ | 
 |   { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_OFFSET, ne_ ## FIELD ## _elements, 0, \ | 
 |       offsetof(STRUCT, FIELD ## _offset) } | 
 | #define E_SINGLE_MASTER(ID, TYPE, STRUCT, FIELD) \ | 
 |   { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_NONE, ne_ ## FIELD ## _elements, 0, 0 } | 
 | #define E_SUSPEND(ID, TYPE) \ | 
 |   { #ID, ID, TYPE, 0, DESC_FLAG_SUSPEND, NULL, 0, 0 } | 
 | #define E_LAST \ | 
 |   { NULL, 0, 0, 0, DESC_FLAG_NONE, NULL, 0, 0 } | 
 |  | 
 | /* EBML Element Lists */ | 
 | static struct ebml_element_desc ne_ebml_elements[] = { | 
 |   E_FIELD(ID_EBML_VERSION, TYPE_UINT, struct ebml, ebml_version), | 
 |   E_FIELD(ID_EBML_READ_VERSION, TYPE_UINT, struct ebml, ebml_read_version), | 
 |   E_FIELD(ID_EBML_MAX_ID_LENGTH, TYPE_UINT, struct ebml, ebml_max_id_length), | 
 |   E_FIELD(ID_EBML_MAX_SIZE_LENGTH, TYPE_UINT, struct ebml, ebml_max_size_length), | 
 |   E_FIELD(ID_DOCTYPE, TYPE_STRING, struct ebml, doctype), | 
 |   E_FIELD(ID_DOCTYPE_VERSION, TYPE_UINT, struct ebml, doctype_version), | 
 |   E_FIELD(ID_DOCTYPE_READ_VERSION, TYPE_UINT, struct ebml, doctype_read_version), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | /* WebM Element Lists */ | 
 | static struct ebml_element_desc ne_seek_elements[] = { | 
 |   E_FIELD(ID_SEEK_ID, TYPE_BINARY, struct seek, id), | 
 |   E_FIELD(ID_SEEK_POSITION, TYPE_UINT, struct seek, position), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_seek_head_elements[] = { | 
 |   E_MASTER(ID_SEEK, TYPE_MASTER, struct seek_head, seek), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_info_elements[] = { | 
 |   E_FIELD(ID_TIMECODE_SCALE, TYPE_UINT, struct info, timecode_scale), | 
 |   E_FIELD(ID_DURATION, TYPE_FLOAT, struct info, duration), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_block_group_elements[] = { | 
 |   E_SUSPEND(ID_BLOCK, TYPE_BINARY), | 
 |   E_FIELD(ID_BLOCK_DURATION, TYPE_UINT, struct block_group, duration), | 
 |   E_FIELD(ID_REFERENCE_BLOCK, TYPE_INT, struct block_group, reference_block), | 
 |   E_FIELD(ID_DISCARD_PADDING, TYPE_INT, struct block_group, discard_padding), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_cluster_elements[] = { | 
 |   E_FIELD(ID_TIMECODE, TYPE_UINT, struct cluster, timecode), | 
 |   E_MASTER(ID_BLOCK_GROUP, TYPE_MASTER, struct cluster, block_group), | 
 |   E_SUSPEND(ID_SIMPLE_BLOCK, TYPE_BINARY), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_video_elements[] = { | 
 |   E_FIELD(ID_STEREO_MODE, TYPE_UINT, struct video, stereo_mode), | 
 |   E_FIELD(ID_PIXEL_WIDTH, TYPE_UINT, struct video, pixel_width), | 
 |   E_FIELD(ID_PIXEL_HEIGHT, TYPE_UINT, struct video, pixel_height), | 
 |   E_FIELD(ID_PIXEL_CROP_BOTTOM, TYPE_UINT, struct video, pixel_crop_bottom), | 
 |   E_FIELD(ID_PIXEL_CROP_TOP, TYPE_UINT, struct video, pixel_crop_top), | 
 |   E_FIELD(ID_PIXEL_CROP_LEFT, TYPE_UINT, struct video, pixel_crop_left), | 
 |   E_FIELD(ID_PIXEL_CROP_RIGHT, TYPE_UINT, struct video, pixel_crop_right), | 
 |   E_FIELD(ID_DISPLAY_WIDTH, TYPE_UINT, struct video, display_width), | 
 |   E_FIELD(ID_DISPLAY_HEIGHT, TYPE_UINT, struct video, display_height), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_audio_elements[] = { | 
 |   E_FIELD(ID_SAMPLING_FREQUENCY, TYPE_FLOAT, struct audio, sampling_frequency), | 
 |   E_FIELD(ID_CHANNELS, TYPE_UINT, struct audio, channels), | 
 |   E_FIELD(ID_BIT_DEPTH, TYPE_UINT, struct audio, bit_depth), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_track_entry_elements[] = { | 
 |   E_FIELD(ID_TRACK_NUMBER, TYPE_UINT, struct track_entry, number), | 
 |   E_FIELD(ID_TRACK_UID, TYPE_UINT, struct track_entry, uid), | 
 |   E_FIELD(ID_TRACK_TYPE, TYPE_UINT, struct track_entry, type), | 
 |   E_FIELD(ID_FLAG_ENABLED, TYPE_UINT, struct track_entry, flag_enabled), | 
 |   E_FIELD(ID_FLAG_DEFAULT, TYPE_UINT, struct track_entry, flag_default), | 
 |   E_FIELD(ID_FLAG_LACING, TYPE_UINT, struct track_entry, flag_lacing), | 
 |   E_FIELD(ID_TRACK_TIMECODE_SCALE, TYPE_FLOAT, struct track_entry, track_timecode_scale), | 
 |   E_FIELD(ID_LANGUAGE, TYPE_STRING, struct track_entry, language), | 
 |   E_FIELD(ID_CODEC_ID, TYPE_STRING, struct track_entry, codec_id), | 
 |   E_FIELD(ID_CODEC_PRIVATE, TYPE_BINARY, struct track_entry, codec_private), | 
 |   E_FIELD(ID_CODEC_DELAY, TYPE_UINT, struct track_entry, codec_delay), | 
 |   E_FIELD(ID_SEEK_PREROLL, TYPE_UINT, struct track_entry, seek_preroll), | 
 |   E_SINGLE_MASTER(ID_VIDEO, TYPE_MASTER, struct track_entry, video), | 
 |   E_SINGLE_MASTER(ID_AUDIO, TYPE_MASTER, struct track_entry, audio), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_tracks_elements[] = { | 
 |   E_MASTER(ID_TRACK_ENTRY, TYPE_MASTER, struct tracks, track_entry), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_cue_track_positions_elements[] = { | 
 |   E_FIELD(ID_CUE_TRACK, TYPE_UINT, struct cue_track_positions, track), | 
 |   E_FIELD(ID_CUE_CLUSTER_POSITION, TYPE_UINT, struct cue_track_positions, cluster_position), | 
 |   E_FIELD(ID_CUE_BLOCK_NUMBER, TYPE_UINT, struct cue_track_positions, block_number), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_cue_point_elements[] = { | 
 |   E_FIELD(ID_CUE_TIME, TYPE_UINT, struct cue_point, time), | 
 |   E_MASTER(ID_CUE_TRACK_POSITIONS, TYPE_MASTER, struct cue_point, cue_track_positions), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_cues_elements[] = { | 
 |   E_MASTER(ID_CUE_POINT, TYPE_MASTER, struct cues, cue_point), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_segment_elements[] = { | 
 |   E_MASTER(ID_SEEK_HEAD, TYPE_MASTER, struct segment, seek_head), | 
 |   E_SINGLE_MASTER(ID_INFO, TYPE_MASTER, struct segment, info), | 
 |   E_MASTER(ID_CLUSTER, TYPE_MASTER, struct segment, cluster), | 
 |   E_SINGLE_MASTER(ID_TRACKS, TYPE_MASTER, struct segment, tracks), | 
 |   E_SINGLE_MASTER(ID_CUES, TYPE_MASTER, struct segment, cues), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | static struct ebml_element_desc ne_top_level_elements[] = { | 
 |   E_SINGLE_MASTER(ID_EBML, TYPE_MASTER, nestegg, ebml), | 
 |   E_SINGLE_MASTER_O(ID_SEGMENT, TYPE_MASTER, nestegg, segment), | 
 |   E_LAST | 
 | }; | 
 |  | 
 | #undef E_FIELD | 
 | #undef E_MASTER | 
 | #undef E_SINGLE_MASTER_O | 
 | #undef E_SINGLE_MASTER | 
 | #undef E_SUSPEND | 
 | #undef E_LAST | 
 |  | 
 | static struct pool_ctx * | 
 | ne_pool_init(void) | 
 | { | 
 |   struct pool_ctx * pool; | 
 |  | 
 |   pool = h_malloc(sizeof(*pool)); | 
 |   if (!pool) | 
 |     abort(); | 
 |   return pool; | 
 | } | 
 |  | 
 | static void | 
 | ne_pool_destroy(struct pool_ctx * pool) | 
 | { | 
 |   h_free(pool); | 
 | } | 
 |  | 
 | static void * | 
 | ne_pool_alloc(size_t size, struct pool_ctx * pool) | 
 | { | 
 |   void * p; | 
 |  | 
 |   p = h_malloc(size); | 
 |   if (!p) | 
 |     abort(); | 
 |   hattach(p, pool); | 
 |   memset(p, 0, size); | 
 |   return p; | 
 | } | 
 |  | 
 | static void * | 
 | ne_alloc(size_t size) | 
 | { | 
 |   void * p; | 
 |  | 
 |   p = calloc(1, size); | 
 |   if (!p) | 
 |     abort(); | 
 |   return p; | 
 | } | 
 |  | 
 | static int | 
 | ne_io_read(nestegg_io * io, void * buffer, size_t length) | 
 | { | 
 |   return io->read(buffer, length, io->userdata); | 
 | } | 
 |  | 
 | static int | 
 | ne_io_seek(nestegg_io * io, int64_t offset, int whence) | 
 | { | 
 |   return io->seek(offset, whence, io->userdata); | 
 | } | 
 |  | 
 | static int | 
 | ne_io_read_skip(nestegg_io * io, size_t length) | 
 | { | 
 |   size_t get; | 
 |   unsigned char buf[8192]; | 
 |   int r = 1; | 
 |  | 
 |   while (length > 0) { | 
 |     get = length < sizeof(buf) ? length : sizeof(buf); | 
 |     r = ne_io_read(io, buf, get); | 
 |     if (r != 1) | 
 |       break; | 
 |     length -= get; | 
 |   } | 
 |  | 
 |   return r; | 
 | } | 
 |  | 
 | static int64_t | 
 | ne_io_tell(nestegg_io * io) | 
 | { | 
 |   return io->tell(io->userdata); | 
 | } | 
 |  | 
 | static int | 
 | ne_bare_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length, enum vint_mask maskflag) | 
 | { | 
 |   int r; | 
 |   unsigned char b; | 
 |   size_t maxlen = 8; | 
 |   unsigned int count = 1, mask = 1 << 7; | 
 |  | 
 |   r = ne_io_read(io, &b, 1); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   while (count < maxlen) { | 
 |     if ((b & mask) != 0) | 
 |       break; | 
 |     mask >>= 1; | 
 |     count += 1; | 
 |   } | 
 |  | 
 |   if (length) | 
 |     *length = count; | 
 |   *value = b; | 
 |  | 
 |   if (maskflag == MASK_FIRST_BIT) | 
 |     *value = b & ~mask; | 
 |  | 
 |   while (--count) { | 
 |     r = ne_io_read(io, &b, 1); | 
 |     if (r != 1) | 
 |       return r; | 
 |     *value <<= 8; | 
 |     *value |= b; | 
 |   } | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_id(nestegg_io * io, uint64_t * value, uint64_t * length) | 
 | { | 
 |   return ne_bare_read_vint(io, value, length, MASK_NONE); | 
 | } | 
 |  | 
 | static int | 
 | ne_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length) | 
 | { | 
 |   return ne_bare_read_vint(io, value, length, MASK_FIRST_BIT); | 
 | } | 
 |  | 
 | static int | 
 | ne_read_svint(nestegg_io * io, int64_t * value, uint64_t * length) | 
 | { | 
 |   int r; | 
 |   uint64_t uvalue; | 
 |   uint64_t ulength; | 
 |   int64_t svint_subtr[] = { | 
 |     0x3f, 0x1fff, | 
 |     0xfffff, 0x7ffffff, | 
 |     0x3ffffffffLL, 0x1ffffffffffLL, | 
 |     0xffffffffffffLL, 0x7fffffffffffffLL | 
 |   }; | 
 |  | 
 |   r = ne_bare_read_vint(io, &uvalue, &ulength, MASK_FIRST_BIT); | 
 |   if (r != 1) | 
 |     return r; | 
 |   *value = uvalue - svint_subtr[ulength - 1]; | 
 |   if (length) | 
 |     *length = ulength; | 
 |   return r; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_uint(nestegg_io * io, uint64_t * val, uint64_t length) | 
 | { | 
 |   unsigned char b; | 
 |   int r; | 
 |  | 
 |   if (length == 0 || length > 8) | 
 |     return -1; | 
 |   r = ne_io_read(io, &b, 1); | 
 |   if (r != 1) | 
 |     return r; | 
 |   *val = b; | 
 |   while (--length) { | 
 |     r = ne_io_read(io, &b, 1); | 
 |     if (r != 1) | 
 |       return r; | 
 |     *val <<= 8; | 
 |     *val |= b; | 
 |   } | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_int(nestegg_io * io, int64_t * val, uint64_t length) | 
 | { | 
 |   int r; | 
 |   uint64_t uval, base; | 
 |  | 
 |   r = ne_read_uint(io, &uval, length); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   if (length < sizeof(int64_t)) { | 
 |     base = 1; | 
 |     base <<= length * 8 - 1; | 
 |     if (uval >= base) { | 
 |         base = 1; | 
 |         base <<= length * 8; | 
 |     } else { | 
 |       base = 0; | 
 |     } | 
 |     *val = uval - base; | 
 |   } else { | 
 |     *val = (int64_t) uval; | 
 |   } | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_float(nestegg_io * io, double * val, uint64_t length) | 
 | { | 
 |   union { | 
 |     uint64_t u; | 
 |     float f; | 
 |     double d; | 
 |   } value; | 
 |   int r; | 
 |  | 
 |   /* Length == 10 not implemented. */ | 
 |   if (length != 4 && length != 8) | 
 |     return -1; | 
 |   r = ne_read_uint(io, &value.u, length); | 
 |   if (r != 1) | 
 |     return r; | 
 |   if (length == 4) | 
 |     *val = value.f; | 
 |   else | 
 |     *val = value.d; | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_string(nestegg * ctx, char ** val, uint64_t length) | 
 | { | 
 |   char * str; | 
 |   int r; | 
 |   const size_t alloc_size = (size_t)length + 1; | 
 |  | 
 |   if (length == 0 || length > LIMIT_STRING) | 
 |     return -1; | 
 |   str = ne_pool_alloc(alloc_size, ctx->alloc_pool); | 
 |   r = ne_io_read(ctx->io, (unsigned char *) str, alloc_size - 1); | 
 |   if (r != 1) | 
 |     return r; | 
 |   str[alloc_size - 1] = '\0'; | 
 |   *val = str; | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_binary(nestegg * ctx, struct ebml_binary * val, uint64_t length) | 
 | { | 
 |   if (length == 0 || length > LIMIT_BINARY) | 
 |     return -1; | 
 |   val->length = (size_t)length; | 
 |   val->data = ne_pool_alloc(val->length, ctx->alloc_pool); | 
 |   return ne_io_read(ctx->io, val->data, val->length); | 
 | } | 
 |  | 
 | static int | 
 | ne_get_uint(struct ebml_type type, uint64_t * value) | 
 | { | 
 |   if (!type.read) | 
 |     return -1; | 
 |  | 
 |   assert(type.type == TYPE_UINT); | 
 |  | 
 |   *value = type.v.u; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | ne_get_uint32(struct ebml_type type, unsigned int * value) | 
 | { | 
 |   uint64_t v; | 
 |   if (ne_get_uint(type, &v)) | 
 |     return -1; | 
 |  | 
 |   assert((unsigned int)v == v); | 
 |  | 
 |   *value = (unsigned int)v; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | ne_get_float(struct ebml_type type, double * value) | 
 | { | 
 |   if (!type.read) | 
 |     return -1; | 
 |  | 
 |   assert(type.type == TYPE_FLOAT); | 
 |  | 
 |   *value = type.v.f; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | ne_get_string(struct ebml_type type, char ** value) | 
 | { | 
 |   if (!type.read) | 
 |     return -1; | 
 |  | 
 |   assert(type.type == TYPE_STRING); | 
 |  | 
 |   *value = type.v.s; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | ne_get_binary(struct ebml_type type, struct ebml_binary * value) | 
 | { | 
 |   if (!type.read) | 
 |     return -1; | 
 |  | 
 |   assert(type.type == TYPE_BINARY); | 
 |  | 
 |   *value = type.v.b; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | ne_is_ancestor_element(uint64_t id, struct list_node * ancestor) | 
 | { | 
 |   struct ebml_element_desc * element; | 
 |  | 
 |   for (; ancestor; ancestor = ancestor->previous) | 
 |     for (element = ancestor->node; element->id; ++element) | 
 |       if (element->id == id) | 
 |         return 1; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static struct ebml_element_desc * | 
 | ne_find_element(uint64_t id, struct ebml_element_desc * elements) | 
 | { | 
 |   struct ebml_element_desc * element; | 
 |  | 
 |   for (element = elements; element->id; ++element) | 
 |     if (element->id == id) | 
 |       return element; | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | static void | 
 | ne_ctx_push(nestegg * ctx, struct ebml_element_desc * ancestor, void * data) | 
 | { | 
 |   struct list_node * item; | 
 |  | 
 |   item = ne_alloc(sizeof(*item)); | 
 |   item->previous = ctx->ancestor; | 
 |   item->node = ancestor; | 
 |   item->data = data; | 
 |   ctx->ancestor = item; | 
 | } | 
 |  | 
 | static void | 
 | ne_ctx_pop(nestegg * ctx) | 
 | { | 
 |   struct list_node * item; | 
 |  | 
 |   item = ctx->ancestor; | 
 |   ctx->ancestor = item->previous; | 
 |   free(item); | 
 | } | 
 |  | 
 | static int | 
 | ne_ctx_save(nestegg * ctx, struct saved_state * s) | 
 | { | 
 |   s->stream_offset = ne_io_tell(ctx->io); | 
 |   if (s->stream_offset < 0) | 
 |     return -1; | 
 |   s->ancestor = ctx->ancestor; | 
 |   s->last_id = ctx->last_id; | 
 |   s->last_size = ctx->last_size; | 
 |   s->last_valid = ctx->last_valid; | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | ne_ctx_restore(nestegg * ctx, struct saved_state * s) | 
 | { | 
 |   int r; | 
 |  | 
 |   r = ne_io_seek(ctx->io, s->stream_offset, NESTEGG_SEEK_SET); | 
 |   if (r != 0) | 
 |     return -1; | 
 |   ctx->ancestor = s->ancestor; | 
 |   ctx->last_id = s->last_id; | 
 |   ctx->last_size = s->last_size; | 
 |   ctx->last_valid = s->last_valid; | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | ne_peek_element(nestegg * ctx, uint64_t * id, uint64_t * size) | 
 | { | 
 |   int r; | 
 |  | 
 |   if (ctx->last_valid) { | 
 |     if (id) | 
 |       *id = ctx->last_id; | 
 |     if (size) | 
 |       *size = ctx->last_size; | 
 |     return 1; | 
 |   } | 
 |  | 
 |   r = ne_read_id(ctx->io, &ctx->last_id, NULL); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   r = ne_read_vint(ctx->io, &ctx->last_size, NULL); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   if (id) | 
 |     *id = ctx->last_id; | 
 |   if (size) | 
 |     *size = ctx->last_size; | 
 |  | 
 |   ctx->last_valid = 1; | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_element(nestegg * ctx, uint64_t * id, uint64_t * size) | 
 | { | 
 |   int r; | 
 |  | 
 |   r = ne_peek_element(ctx, id, size); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   ctx->last_valid = 0; | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | static void | 
 | ne_read_master(nestegg * ctx, struct ebml_element_desc * desc) | 
 | { | 
 |   struct ebml_list * list; | 
 |   struct ebml_list_node * node, * oldtail; | 
 |  | 
 |   assert(desc->type == TYPE_MASTER && desc->flags & DESC_FLAG_MULTI); | 
 |  | 
 |   ctx->log(ctx, NESTEGG_LOG_DEBUG, "multi master element %llx (%s)", | 
 |            desc->id, desc->name); | 
 |  | 
 |   list = (struct ebml_list *) (ctx->ancestor->data + desc->offset); | 
 |  | 
 |   node = ne_pool_alloc(sizeof(*node), ctx->alloc_pool); | 
 |   node->id = desc->id; | 
 |   node->data = ne_pool_alloc(desc->size, ctx->alloc_pool); | 
 |  | 
 |   oldtail = list->tail; | 
 |   if (oldtail) | 
 |     oldtail->next = node; | 
 |   list->tail = node; | 
 |   if (!list->head) | 
 |     list->head = node; | 
 |  | 
 |   ctx->log(ctx, NESTEGG_LOG_DEBUG, " -> using data %p", node->data); | 
 |  | 
 |   ne_ctx_push(ctx, desc->children, node->data); | 
 | } | 
 |  | 
 | static void | 
 | ne_read_single_master(nestegg * ctx, struct ebml_element_desc * desc) | 
 | { | 
 |   assert(desc->type == TYPE_MASTER && !(desc->flags & DESC_FLAG_MULTI)); | 
 |  | 
 |   ctx->log(ctx, NESTEGG_LOG_DEBUG, "single master element %llx (%s)", | 
 |            desc->id, desc->name); | 
 |   ctx->log(ctx, NESTEGG_LOG_DEBUG, " -> using data %p (%u)", | 
 |            ctx->ancestor->data + desc->offset, desc->offset); | 
 |  | 
 |   ne_ctx_push(ctx, desc->children, ctx->ancestor->data + desc->offset); | 
 | } | 
 |  | 
 | static int | 
 | ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length) | 
 | { | 
 |   struct ebml_type * storage; | 
 |   int r = 0; | 
 |  | 
 |   storage = (struct ebml_type *) (ctx->ancestor->data + desc->offset); | 
 |  | 
 |   if (storage->read) { | 
 |     ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) already read, skipping", | 
 |              desc->id, desc->name); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   storage->type = desc->type; | 
 |  | 
 |   ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) -> %p (%u)", | 
 |            desc->id, desc->name, storage, desc->offset); | 
 |  | 
 |   switch (desc->type) { | 
 |   case TYPE_UINT: | 
 |     r = ne_read_uint(ctx->io, &storage->v.u, length); | 
 |     break; | 
 |   case TYPE_FLOAT: | 
 |     r = ne_read_float(ctx->io, &storage->v.f, length); | 
 |     break; | 
 |   case TYPE_INT: | 
 |     r = ne_read_int(ctx->io, &storage->v.i, length); | 
 |     break; | 
 |   case TYPE_STRING: | 
 |     r = ne_read_string(ctx, &storage->v.s, length); | 
 |     break; | 
 |   case TYPE_BINARY: | 
 |     r = ne_read_binary(ctx, &storage->v.b, length); | 
 |     break; | 
 |   case TYPE_MASTER: | 
 |   case TYPE_UNKNOWN: | 
 |     assert(0); | 
 |     break; | 
 |   } | 
 |  | 
 |   if (r == 1) | 
 |     storage->read = 1; | 
 |  | 
 |   return r; | 
 | } | 
 |  | 
 | static int | 
 | ne_parse(nestegg * ctx, struct ebml_element_desc * top_level, int64_t max_offset) | 
 | { | 
 |   int r; | 
 |   int64_t * data_offset; | 
 |   uint64_t id, size, peeked_id; | 
 |   struct ebml_element_desc * element; | 
 |  | 
 |   if (!ctx->ancestor) | 
 |     return -1; | 
 |  | 
 |   for (;;) { | 
 |     if (max_offset > 0 && ne_io_tell(ctx->io) >= max_offset) { | 
 |       /* Reached end of offset allowed for parsing - return gracefully */ | 
 |       r = 1; | 
 |       break; | 
 |     } | 
 |     r = ne_peek_element(ctx, &id, &size); | 
 |     if (r != 1) | 
 |       break; | 
 |     peeked_id = id; | 
 |  | 
 |     element = ne_find_element(id, ctx->ancestor->node); | 
 |     if (element) { | 
 |       if (element->flags & DESC_FLAG_SUSPEND) { | 
 |         assert(element->type == TYPE_BINARY); | 
 |         ctx->log(ctx, NESTEGG_LOG_DEBUG, "suspend parse at %llx", id); | 
 |         r = 1; | 
 |         break; | 
 |       } | 
 |  | 
 |       r = ne_read_element(ctx, &id, &size); | 
 |       if (r != 1) | 
 |         break; | 
 |       assert(id == peeked_id); | 
 |  | 
 |       if (element->flags & DESC_FLAG_OFFSET) { | 
 |         data_offset = (int64_t *) (ctx->ancestor->data + element->data_offset); | 
 |         *data_offset = ne_io_tell(ctx->io); | 
 |         if (*data_offset < 0) { | 
 |           r = -1; | 
 |           break; | 
 |         } | 
 |       } | 
 |  | 
 |       if (element->type == TYPE_MASTER) { | 
 |         if (element->flags & DESC_FLAG_MULTI) | 
 |           ne_read_master(ctx, element); | 
 |         else | 
 |           ne_read_single_master(ctx, element); | 
 |         continue; | 
 |       } else { | 
 |         r = ne_read_simple(ctx, element, (size_t)size); | 
 |         if (r < 0) | 
 |           break; | 
 |       } | 
 |     } else if (ne_is_ancestor_element(id, ctx->ancestor->previous)) { | 
 |       ctx->log(ctx, NESTEGG_LOG_DEBUG, "parent element %llx", id); | 
 |       if (top_level && ctx->ancestor->node == top_level) { | 
 |         ctx->log(ctx, NESTEGG_LOG_DEBUG, "*** parse about to back up past top_level"); | 
 |         r = 1; | 
 |         break; | 
 |       } | 
 |       ne_ctx_pop(ctx); | 
 |     } else { | 
 |       r = ne_read_element(ctx, &id, &size); | 
 |       if (r != 1) | 
 |         break; | 
 |  | 
 |       if (id != ID_VOID && id != ID_CRC32) | 
 |         ctx->log(ctx, NESTEGG_LOG_DEBUG, "unknown element %llx", id); | 
 |       r = ne_io_read_skip(ctx->io, (size_t)size); | 
 |       if (r != 1) | 
 |         break; | 
 |     } | 
 |   } | 
 |  | 
 |   if (r != 1) | 
 |     while (ctx->ancestor) | 
 |       ne_ctx_pop(ctx); | 
 |  | 
 |   return r; | 
 | } | 
 |  | 
 | static uint64_t | 
 | ne_xiph_lace_value(unsigned char ** np) | 
 | { | 
 |   uint64_t lace; | 
 |   uint64_t value; | 
 |   unsigned char * p = *np; | 
 |  | 
 |   lace = *p++; | 
 |   value = lace; | 
 |   while (lace == 255) { | 
 |     lace = *p++; | 
 |     value += lace; | 
 |   } | 
 |  | 
 |   *np = p; | 
 |  | 
 |   return value; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed) | 
 | { | 
 |   int r; | 
 |   uint64_t lace; | 
 |  | 
 |   r = ne_read_uint(io, &lace, 1); | 
 |   if (r != 1) | 
 |     return r; | 
 |   *consumed += 1; | 
 |  | 
 |   *value = lace; | 
 |   while (lace == 255) { | 
 |     r = ne_read_uint(io, &lace, 1); | 
 |     if (r != 1) | 
 |       return r; | 
 |     *consumed += 1; | 
 |     *value += lace; | 
 |   } | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_xiph_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) | 
 | { | 
 |   int r; | 
 |   size_t i = 0; | 
 |   uint64_t sum = 0; | 
 |  | 
 |   while (--n) { | 
 |     r = ne_read_xiph_lace_value(io, &sizes[i], read); | 
 |     if (r != 1) | 
 |       return r; | 
 |     sum += sizes[i]; | 
 |     i += 1; | 
 |   } | 
 |  | 
 |   if (*read + sum > block) | 
 |     return -1; | 
 |  | 
 |   /* Last frame is the remainder of the block. */ | 
 |   sizes[i] = block - *read - sum; | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_ebml_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) | 
 | { | 
 |   int r; | 
 |   uint64_t lace, sum, length; | 
 |   int64_t slace; | 
 |   size_t i = 0; | 
 |  | 
 |   r = ne_read_vint(io, &lace, &length); | 
 |   if (r != 1) | 
 |     return r; | 
 |   assert(length <= 8); | 
 |   *read += (size_t)length; | 
 |  | 
 |   sizes[i] = lace; | 
 |   sum = sizes[i]; | 
 |  | 
 |   i += 1; | 
 |   n -= 1; | 
 |  | 
 |   while (--n) { | 
 |     r = ne_read_svint(io, &slace, &length); | 
 |     if (r != 1) | 
 |       return r; | 
 |     assert(length <= 8); | 
 |     *read += (size_t)length; | 
 |     sizes[i] = sizes[i - 1] + slace; | 
 |     sum += sizes[i]; | 
 |     i += 1; | 
 |   } | 
 |  | 
 |   if (*read + sum > block) | 
 |     return -1; | 
 |  | 
 |   /* Last frame is the remainder of the block. */ | 
 |   sizes[i] = block - *read - sum; | 
 |   return 1; | 
 | } | 
 |  | 
 | static uint64_t | 
 | ne_get_timecode_scale(nestegg * ctx) | 
 | { | 
 |   uint64_t scale; | 
 |  | 
 |   if (ne_get_uint(ctx->segment.info.timecode_scale, &scale) != 0) | 
 |     scale = 1000000; | 
 |  | 
 |   return scale; | 
 | } | 
 |  | 
 | static int | 
 | ne_map_track_number_to_index(nestegg * ctx, | 
 |                              unsigned int track_number, | 
 |                              unsigned int * track_index) | 
 | { | 
 |   struct ebml_list_node * node; | 
 |   struct track_entry * t_entry; | 
 |   uint64_t t_number = 0; | 
 |  | 
 |   if (!track_index) | 
 |     return -1; | 
 |   *track_index = 0; | 
 |  | 
 |   if (track_number == 0) | 
 |     return -1; | 
 |  | 
 |   node = ctx->segment.tracks.track_entry.head; | 
 |   while (node) { | 
 |     assert(node->id == ID_TRACK_ENTRY); | 
 |     t_entry = node->data; | 
 |     if (ne_get_uint(t_entry->number, &t_number) != 0) | 
 |       return -1; | 
 |     if (t_number == track_number) | 
 |       return 0; | 
 |     *track_index += 1; | 
 |     node = node->next; | 
 |   } | 
 |  | 
 |   return -1; | 
 | } | 
 |  | 
 | static struct track_entry * | 
 | ne_find_track_entry(nestegg * ctx, unsigned int track) | 
 | { | 
 |   struct ebml_list_node * node; | 
 |   unsigned int tracks = 0; | 
 |  | 
 |   node = ctx->segment.tracks.track_entry.head; | 
 |   while (node) { | 
 |     assert(node->id == ID_TRACK_ENTRY); | 
 |     if (track == tracks) | 
 |       return node->data; | 
 |     tracks += 1; | 
 |     node = node->next; | 
 |   } | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_packet ** data) | 
 | { | 
 |   int r; | 
 |   int64_t timecode, abs_timecode; | 
 |   nestegg_packet * pkt; | 
 |   struct cluster * cluster; | 
 |   struct frame * f, * last; | 
 |   struct track_entry * entry; | 
 |   const int track_scale = 1; | 
 |   uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total; | 
 |   unsigned int i, lacing, track; | 
 |   size_t consumed = 0; | 
 |  | 
 |   *data = NULL; | 
 |  | 
 |   if (block_size > LIMIT_BLOCK) | 
 |     return -1; | 
 |  | 
 |   r = ne_read_vint(ctx->io, &track_number, &length); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   if (track_number == 0 || (unsigned int)track_number != track_number) | 
 |     return -1; | 
 |  | 
 |   assert(length <= 8); | 
 |   consumed += (size_t)length; | 
 |  | 
 |   r = ne_read_int(ctx->io, &timecode, 2); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   consumed += 2; | 
 |  | 
 |   r = ne_read_uint(ctx->io, &flags, 1); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   consumed += 1; | 
 |  | 
 |   frames = 0; | 
 |  | 
 |   /* Flags are different between Block and SimpleBlock, but lacing is | 
 |      encoded the same way. */ | 
 |   lacing = (flags & BLOCK_FLAGS_LACING) >> 1; | 
 |  | 
 |   switch (lacing) { | 
 |   case LACING_NONE: | 
 |     frames = 1; | 
 |     break; | 
 |   case LACING_XIPH: | 
 |   case LACING_FIXED: | 
 |   case LACING_EBML: | 
 |     r = ne_read_uint(ctx->io, &frames, 1); | 
 |     if (r != 1) | 
 |       return r; | 
 |     consumed += 1; | 
 |     frames += 1; | 
 |   } | 
 |  | 
 |   if (frames > 256) | 
 |     return -1; | 
 |  | 
 |   switch (lacing) { | 
 |   case LACING_NONE: | 
 |     frame_sizes[0] = block_size - consumed; | 
 |     break; | 
 |   case LACING_XIPH: | 
 |     if (frames == 1) | 
 |       return -1; | 
 |     r = ne_read_xiph_lacing(ctx->io, (size_t)block_size, &consumed, frames, frame_sizes); | 
 |     if (r != 1) | 
 |       return r; | 
 |     break; | 
 |   case LACING_FIXED: | 
 |     if ((block_size - consumed) % frames) | 
 |       return -1; | 
 |     for (i = 0; i < frames; ++i) | 
 |       frame_sizes[i] = (block_size - consumed) / frames; | 
 |     break; | 
 |   case LACING_EBML: | 
 |     if (frames == 1) | 
 |       return -1; | 
 |     r = ne_read_ebml_lacing(ctx->io, (size_t)block_size, &consumed, frames, frame_sizes); | 
 |     if (r != 1) | 
 |       return r; | 
 |     break; | 
 |   } | 
 |  | 
 |   /* Sanity check unlaced frame sizes against total block size. */ | 
 |   total = consumed; | 
 |   for (i = 0; i < frames; ++i) | 
 |     total += frame_sizes[i]; | 
 |   if (total > block_size) | 
 |     return -1; | 
 |  | 
 |   if (ne_map_track_number_to_index(ctx, (unsigned int)track_number, &track) != 0) | 
 |     return -1; | 
 |  | 
 |   entry = ne_find_track_entry(ctx, track); | 
 |   if (!entry) | 
 |     return -1; | 
 |  | 
 |   tc_scale = ne_get_timecode_scale(ctx); | 
 |  | 
 |   assert(ctx->segment.cluster.tail->id == ID_CLUSTER); | 
 |   cluster = ctx->segment.cluster.tail->data; | 
 |   if (ne_get_uint(cluster->timecode, &cluster_tc) != 0) | 
 |     return -1; | 
 |  | 
 |   abs_timecode = timecode + cluster_tc; | 
 |   if (abs_timecode < 0) | 
 |     return -1; | 
 |  | 
 |   pkt = ne_alloc(sizeof(*pkt)); | 
 |   pkt->track = track; | 
 |   pkt->timecode = abs_timecode * tc_scale * track_scale; | 
 |  | 
 |   ctx->log(ctx, NESTEGG_LOG_DEBUG, "%sblock t %lld pts %f f %llx frames: %llu", | 
 |            block_id == ID_BLOCK ? "" : "simple", pkt->track, pkt->timecode / 1e9, flags, frames); | 
 |  | 
 |   last = NULL; | 
 |   for (i = 0; i < frames; ++i) { | 
 |     if (frame_sizes[i] > LIMIT_FRAME) { | 
 |       nestegg_free_packet(pkt); | 
 |       return -1; | 
 |     } | 
 |     f = ne_alloc(sizeof(*f)); | 
 |     f->length = (size_t)frame_sizes[i]; | 
 |     f->data = ne_alloc(f->length); | 
 |     r = ne_io_read(ctx->io, f->data, f->length); | 
 |     if (r != 1) { | 
 |       free(f->data); | 
 |       free(f); | 
 |       nestegg_free_packet(pkt); | 
 |       return -1; | 
 |     } | 
 |  | 
 |     if (!last) | 
 |       pkt->frame = f; | 
 |     else | 
 |       last->next = f; | 
 |     last = f; | 
 |   } | 
 |  | 
 |   *data = pkt; | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | static int | 
 | ne_read_discard_padding(nestegg * ctx, nestegg_packet * pkt) | 
 | { | 
 |   int r; | 
 |   uint64_t id, size; | 
 |   struct ebml_element_desc * element; | 
 |   struct ebml_type * storage; | 
 |  | 
 |   r = ne_peek_element(ctx, &id, &size); | 
 |   if (r != 1) | 
 |     return r; | 
 |  | 
 |   if (id != ID_DISCARD_PADDING) | 
 |     return 1; | 
 |  | 
 |   element = ne_find_element(id, ctx->ancestor->node); | 
 |   if (!element) | 
 |     return 1; | 
 |  | 
 |   assert((size_t)size == size); | 
 |   r = ne_read_simple(ctx, element, (size_t)size); | 
 |   if (r != 1) | 
 |     return r; | 
 |   storage = (struct ebml_type *) (ctx->ancestor->data + element->offset); | 
 |   pkt->discard_padding = storage->v.i; | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 |  | 
 | static uint64_t | 
 | ne_buf_read_id(unsigned char const * p, size_t length) | 
 | { | 
 |   uint64_t id = 0; | 
 |  | 
 |   while (length--) { | 
 |     id <<= 8; | 
 |     id |= *p++; | 
 |   } | 
 |  | 
 |   return id; | 
 | } | 
 |  | 
 | static struct seek * | 
 | ne_find_seek_for_id(struct ebml_list_node * seek_head, uint64_t id) | 
 | { | 
 |   struct ebml_list * head; | 
 |   struct ebml_list_node * seek; | 
 |   struct ebml_binary binary_id; | 
 |   struct seek * s; | 
 |  | 
 |   while (seek_head) { | 
 |     assert(seek_head->id == ID_SEEK_HEAD); | 
 |     head = seek_head->data; | 
 |     seek = head->head; | 
 |  | 
 |     while (seek) { | 
 |       assert(seek->id == ID_SEEK); | 
 |       s = seek->data; | 
 |  | 
 |       if (ne_get_binary(s->id, &binary_id) == 0 && | 
 |           ne_buf_read_id(binary_id.data, binary_id.length) == id) | 
 |         return s; | 
 |  | 
 |       seek = seek->next; | 
 |     } | 
 |  | 
 |     seek_head = seek_head->next; | 
 |   } | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | static struct cue_track_positions * | 
 | ne_find_cue_position_for_track(nestegg * ctx, struct ebml_list_node * node, unsigned int track) | 
 | { | 
 |   struct cue_track_positions * pos = NULL; | 
 |   unsigned int track_number; | 
 |   unsigned int t; | 
 |  | 
 |   while (node) { | 
 |     assert(node->id == ID_CUE_TRACK_POSITIONS); | 
 |     pos = node->data; | 
 |     if (ne_get_uint32(pos->track, &track_number) != 0) | 
 |       return NULL; | 
 |  | 
 |     if (ne_map_track_number_to_index(ctx, track_number, &t) != 0) | 
 |       return NULL; | 
 |  | 
 |     if (t == track) | 
 |       return pos; | 
 |  | 
 |     node = node->next; | 
 |   } | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | static struct cue_point * | 
 | ne_find_cue_point_for_tstamp(nestegg * ctx, struct ebml_list_node * cue_point, unsigned int track, uint64_t scale, uint64_t tstamp) | 
 | { | 
 |   uint64_t time; | 
 |   struct cue_point * c, * prev = NULL; | 
 |  | 
 |   while (cue_point) { | 
 |     assert(cue_point->id == ID_CUE_POINT); | 
 |     c = cue_point->data; | 
 |  | 
 |     if (!prev) | 
 |       prev = c; | 
 |  | 
 |     if (ne_get_uint(c->time, &time) == 0 && time * scale > tstamp) | 
 |       break; | 
 |  | 
 |     if (ne_find_cue_position_for_track(ctx, c->cue_track_positions.head, track) != NULL) | 
 |       prev = c; | 
 |  | 
 |     cue_point = cue_point->next; | 
 |   } | 
 |  | 
 |   return prev; | 
 | } | 
 |  | 
 | static int | 
 | ne_is_suspend_element(uint64_t id) | 
 | { | 
 |   if (id == ID_SIMPLE_BLOCK || id == ID_BLOCK) | 
 |     return 1; | 
 |   return 0; | 
 | } | 
 |  | 
 | static void | 
 | ne_null_log_callback(nestegg * ctx, unsigned int severity, char const * fmt, ...) | 
 | { | 
 |   if (ctx && severity && fmt) | 
 |     return; | 
 | } | 
 |  | 
 | static int | 
 | ne_init_cue_points(nestegg * ctx, int64_t max_offset) | 
 | { | 
 |   int r; | 
 |   struct ebml_list_node * node = ctx->segment.cues.cue_point.head; | 
 |   struct seek * found; | 
 |   uint64_t seek_pos, id; | 
 |   struct saved_state state; | 
 |  | 
 |   /* If there are no cues loaded, check for cues element in the seek head | 
 |      and load it. */ | 
 |   if (!node) { | 
 |     found = ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); | 
 |     if (!found) | 
 |       return -1; | 
 |  | 
 |     if (ne_get_uint(found->position, &seek_pos) != 0) | 
 |       return -1; | 
 |  | 
 |     /* Save old parser state. */ | 
 |     r = ne_ctx_save(ctx, &state); | 
 |     if (r != 0) | 
 |       return -1; | 
 |  | 
 |     /* Seek and set up parser state for segment-level element (Cues). */ | 
 |     r = ne_io_seek(ctx->io, ctx->segment_offset + seek_pos, NESTEGG_SEEK_SET); | 
 |     if (r != 0) | 
 |       return -1; | 
 |     ctx->last_valid = 0; | 
 |  | 
 |     r = ne_read_element(ctx, &id, NULL); | 
 |     if (r != 1) | 
 |       return -1; | 
 |  | 
 |     if (id != ID_CUES) | 
 |       return -1; | 
 |  | 
 |     ctx->ancestor = NULL; | 
 |     ne_ctx_push(ctx, ne_top_level_elements, ctx); | 
 |     ne_ctx_push(ctx, ne_segment_elements, &ctx->segment); | 
 |     ne_ctx_push(ctx, ne_cues_elements, &ctx->segment.cues); | 
 |     /* parser will run until end of cues element. */ | 
 |     ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cue elements"); | 
 |     r = ne_parse(ctx, ne_cues_elements, max_offset); | 
 |     while (ctx->ancestor) | 
 |       ne_ctx_pop(ctx); | 
 |  | 
 |     /* Reset parser state to original state and seek back to old position. */ | 
 |     if (ne_ctx_restore(ctx, &state) != 0) | 
 |       return -1; | 
 |  | 
 |     if (r < 0) | 
 |       return -1; | 
 |  | 
 |     node = ctx->segment.cues.cue_point.head; | 
 |     if (!node) | 
 |       return -1; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Three functions that implement the nestegg_io interface, operating on a | 
 |  * sniff_buffer. */ | 
 | struct sniff_buffer { | 
 |   unsigned char const * buffer; | 
 |   size_t length; | 
 |   int64_t offset; | 
 | }; | 
 |  | 
 | static int | 
 | ne_buffer_read(void * buffer, size_t length, void * user_data) | 
 | { | 
 |   struct sniff_buffer * sb = user_data; | 
 |  | 
 |   int rv = 1; | 
 |   size_t available = sb->length - (size_t)sb->offset; | 
 |  | 
 |   if (available < length) | 
 |     return 0; | 
 |  | 
 |   memcpy(buffer, sb->buffer + sb->offset, length); | 
 |   sb->offset += length; | 
 |  | 
 |   return rv; | 
 | } | 
 |  | 
 | static int | 
 | ne_buffer_seek(int64_t offset, int whence, void * user_data) | 
 | { | 
 |   struct sniff_buffer * sb = user_data; | 
 |   int64_t o = sb->offset; | 
 |  | 
 |   switch(whence) { | 
 |     case NESTEGG_SEEK_SET: | 
 |       o = offset; | 
 |       break; | 
 |     case NESTEGG_SEEK_CUR: | 
 |       o += offset; | 
 |       break; | 
 |     case NESTEGG_SEEK_END: | 
 |       o = sb->length + offset; | 
 |       break; | 
 |   } | 
 |  | 
 |   if (o < 0 || o > (int64_t) sb->length) | 
 |     return -1; | 
 |  | 
 |   sb->offset = o; | 
 |   return 0; | 
 | } | 
 |  | 
 | static int64_t | 
 | ne_buffer_tell(void * user_data) | 
 | { | 
 |   struct sniff_buffer * sb = user_data; | 
 |   return sb->offset; | 
 | } | 
 |  | 
 | static int | 
 | ne_match_webm(nestegg_io io, int64_t max_offset) | 
 | { | 
 |   int r; | 
 |   uint64_t id; | 
 |   char * doctype; | 
 |   nestegg * ctx; | 
 |  | 
 |   if (!(io.read && io.seek && io.tell)) | 
 |     return -1; | 
 |  | 
 |   ctx = ne_alloc(sizeof(*ctx)); | 
 |  | 
 |   ctx->io = ne_alloc(sizeof(*ctx->io)); | 
 |   *ctx->io = io; | 
 |   ctx->alloc_pool = ne_pool_init(); | 
 |   ctx->log = ne_null_log_callback; | 
 |  | 
 |   r = ne_peek_element(ctx, &id, NULL); | 
 |   if (r != 1) { | 
 |     nestegg_destroy(ctx); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   if (id != ID_EBML) { | 
 |     nestegg_destroy(ctx); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   ne_ctx_push(ctx, ne_top_level_elements, ctx); | 
 |  | 
 |   /* we don't check the return value of ne_parse, that might fail because | 
 |    * max_offset is not on a valid element end point. We only want to check | 
 |    * the EBML ID and that the doctype is "webm". */ | 
 |   ne_parse(ctx, NULL, max_offset); | 
 |  | 
 |   if (ne_get_string(ctx->ebml.doctype, &doctype) != 0 || | 
 |       strcmp(doctype, "webm") != 0) { | 
 |     nestegg_destroy(ctx); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   nestegg_destroy(ctx); | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | int | 
 | nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t max_offset) | 
 | { | 
 |   int r; | 
 |   uint64_t id, version, docversion; | 
 |   struct ebml_list_node * track; | 
 |   char * doctype; | 
 |   nestegg * ctx; | 
 |  | 
 |   if (!(io.read && io.seek && io.tell)) | 
 |     return -1; | 
 |  | 
 |   ctx = ne_alloc(sizeof(*ctx)); | 
 |  | 
 |   ctx->io = ne_alloc(sizeof(*ctx->io)); | 
 |   *ctx->io = io; | 
 |   ctx->log = callback; | 
 |   ctx->alloc_pool = ne_pool_init(); | 
 |  | 
 |   if (!ctx->log) | 
 |     ctx->log = ne_null_log_callback; | 
 |  | 
 |   r = ne_peek_element(ctx, &id, NULL); | 
 |   if (r != 1) { | 
 |     nestegg_destroy(ctx); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   if (id != ID_EBML) { | 
 |     nestegg_destroy(ctx); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   ctx->log(ctx, NESTEGG_LOG_DEBUG, "ctx %p", ctx); | 
 |  | 
 |   ne_ctx_push(ctx, ne_top_level_elements, ctx); | 
 |  | 
 |   r = ne_parse(ctx, NULL, max_offset); | 
 |  | 
 |   if (r != 1) { | 
 |     nestegg_destroy(ctx); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   if (ne_get_uint(ctx->ebml.ebml_read_version, &version) != 0) | 
 |     version = 1; | 
 |   if (version != 1) { | 
 |     nestegg_destroy(ctx); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   if (ne_get_string(ctx->ebml.doctype, &doctype) != 0) | 
 |     doctype = "matroska"; | 
 |   if (strcmp(doctype, "webm") != 0) { | 
 |     nestegg_destroy(ctx); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   if (ne_get_uint(ctx->ebml.doctype_read_version, &docversion) != 0) | 
 |     docversion = 1; | 
 |   if (docversion < 1 || docversion > 2) { | 
 |     nestegg_destroy(ctx); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   if (!ctx->segment.tracks.track_entry.head) { | 
 |     nestegg_destroy(ctx); | 
 |     return -1; | 
 |   } | 
 |  | 
 |   track = ctx->segment.tracks.track_entry.head; | 
 |   ctx->track_count = 0; | 
 |  | 
 |   while (track) { | 
 |     ctx->track_count += 1; | 
 |     track = track->next; | 
 |   } | 
 |  | 
 |   *context = ctx; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | void | 
 | nestegg_destroy(nestegg * ctx) | 
 | { | 
 |   while (ctx->ancestor) | 
 |     ne_ctx_pop(ctx); | 
 |   ne_pool_destroy(ctx->alloc_pool); | 
 |   free(ctx->io); | 
 |   free(ctx); | 
 | } | 
 |  | 
 | int | 
 | nestegg_duration(nestegg * ctx, uint64_t * duration) | 
 | { | 
 |   uint64_t tc_scale; | 
 |   double unscaled_duration; | 
 |  | 
 |   if (ne_get_float(ctx->segment.info.duration, &unscaled_duration) != 0) | 
 |     return -1; | 
 |  | 
 |   tc_scale = ne_get_timecode_scale(ctx); | 
 |  | 
 |   *duration = (uint64_t) (unscaled_duration * tc_scale); | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_tstamp_scale(nestegg * ctx, uint64_t * scale) | 
 | { | 
 |   *scale = ne_get_timecode_scale(ctx); | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_track_count(nestegg * ctx, unsigned int * tracks) | 
 | { | 
 |   *tracks = ctx->track_count; | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_get_cue_point(nestegg * ctx, unsigned int cluster_num, int64_t max_offset, | 
 |                       int64_t * start_pos, int64_t * end_pos, uint64_t * tstamp) | 
 | { | 
 |   int range_obtained = 0; | 
 |   unsigned int cluster_count = 0; | 
 |   struct cue_point * cue_point; | 
 |   struct cue_track_positions * pos; | 
 |   uint64_t seek_pos, track_number, tc_scale, time; | 
 |   struct ebml_list_node * cues_node = ctx->segment.cues.cue_point.head; | 
 |   struct ebml_list_node * cue_pos_node = NULL; | 
 |   unsigned int track = 0, track_count = 0, track_index; | 
 |  | 
 |   if (!start_pos || !end_pos || !tstamp) | 
 |     return -1; | 
 |  | 
 |   /* Initialise return values */ | 
 |   *start_pos = -1; | 
 |   *end_pos = -1; | 
 |   *tstamp = 0; | 
 |  | 
 |   if (!cues_node) { | 
 |     ne_init_cue_points(ctx, max_offset); | 
 |     cues_node = ctx->segment.cues.cue_point.head; | 
 |     /* Verify cues have been added to context. */ | 
 |     if (!cues_node) | 
 |       return -1; | 
 |   } | 
 |  | 
 |   nestegg_track_count(ctx, &track_count); | 
 |  | 
 |   tc_scale = ne_get_timecode_scale(ctx); | 
 |  | 
 |   while (cues_node && !range_obtained) { | 
 |     assert(cues_node->id == ID_CUE_POINT); | 
 |     cue_point = cues_node->data; | 
 |     cue_pos_node = cue_point->cue_track_positions.head; | 
 |     while (cue_pos_node) { | 
 |       assert(cue_pos_node->id == ID_CUE_TRACK_POSITIONS); | 
 |       pos = cue_pos_node->data; | 
 |       for (track = 0; track < track_count; track++) { | 
 |         if (ne_get_uint(pos->track, &track_number) != 0) | 
 |           return -1; | 
 |  | 
 |         if (ne_map_track_number_to_index(ctx, (unsigned int)track_number, &track_index) != 0) | 
 |           return -1; | 
 |  | 
 |         if (track_index == track) { | 
 |           if (ne_get_uint(pos->cluster_position, &seek_pos) != 0) | 
 |             return -1; | 
 |           if (cluster_count == cluster_num) { | 
 |             *start_pos = ctx->segment_offset+seek_pos; | 
 |             if (ne_get_uint(cue_point->time, &time) != 0) | 
 |               return -1; | 
 |             *tstamp = time * tc_scale; | 
 |           } else if (cluster_count == cluster_num+1) { | 
 |             *end_pos = (ctx->segment_offset+seek_pos)-1; | 
 |             range_obtained = 1; | 
 |             break; | 
 |           } | 
 |           cluster_count++; | 
 |         } | 
 |       } | 
 |       cue_pos_node = cue_pos_node->next; | 
 |     } | 
 |     cues_node = cues_node->next; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_offset_seek(nestegg * ctx, uint64_t offset) | 
 | { | 
 |   int r; | 
 |  | 
 |   /* Seek and set up parser state for segment-level element (Cluster). */ | 
 |   r = ne_io_seek(ctx->io, offset, NESTEGG_SEEK_SET); | 
 |   if (r != 0) | 
 |     return -1; | 
 |   ctx->last_valid = 0; | 
 |  | 
 |   while (ctx->ancestor) | 
 |     ne_ctx_pop(ctx); | 
 |  | 
 |   ne_ctx_push(ctx, ne_top_level_elements, ctx); | 
 |   ne_ctx_push(ctx, ne_segment_elements, &ctx->segment); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_track_seek(nestegg * ctx, unsigned int track, uint64_t tstamp) | 
 | { | 
 |   int r; | 
 |   struct cue_point * cue_point; | 
 |   struct cue_track_positions * pos; | 
 |   uint64_t seek_pos, tc_scale; | 
 |  | 
 |   /* If there are no cues loaded, check for cues element in the seek head | 
 |      and load it. */ | 
 |   if (!ctx->segment.cues.cue_point.head) { | 
 |     r = ne_init_cue_points(ctx, -1); | 
 |     if (r != 0) | 
 |       return -1; | 
 |   } | 
 |  | 
 |   tc_scale = ne_get_timecode_scale(ctx); | 
 |  | 
 |   cue_point = ne_find_cue_point_for_tstamp(ctx, ctx->segment.cues.cue_point.head, | 
 |                                            track, tc_scale, tstamp); | 
 |   if (!cue_point) | 
 |     return -1; | 
 |  | 
 |   pos = ne_find_cue_position_for_track(ctx, cue_point->cue_track_positions.head, track); | 
 |   if (pos == NULL) | 
 |     return -1; | 
 |  | 
 |   if (ne_get_uint(pos->cluster_position, &seek_pos) != 0) | 
 |     return -1; | 
 |  | 
 |   /* Seek and set up parser state for segment-level element (Cluster). */ | 
 |   r = nestegg_offset_seek(ctx, ctx->segment_offset + seek_pos); | 
 |   ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cluster elements"); | 
 |   r = ne_parse(ctx, NULL, -1); | 
 |   if (r != 1) | 
 |     return -1; | 
 |  | 
 |   if (!ne_is_suspend_element(ctx->last_id)) | 
 |     return -1; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_track_type(nestegg * ctx, unsigned int track) | 
 | { | 
 |   struct track_entry * entry; | 
 |   uint64_t type; | 
 |  | 
 |   entry = ne_find_track_entry(ctx, track); | 
 |   if (!entry) | 
 |     return -1; | 
 |  | 
 |   if (ne_get_uint(entry->type, &type) != 0) | 
 |     return -1; | 
 |  | 
 |   if (type & TRACK_TYPE_VIDEO) | 
 |     return NESTEGG_TRACK_VIDEO; | 
 |  | 
 |   if (type & TRACK_TYPE_AUDIO) | 
 |     return NESTEGG_TRACK_AUDIO; | 
 |  | 
 |   return -1; | 
 | } | 
 |  | 
 | int | 
 | nestegg_track_codec_id(nestegg * ctx, unsigned int track) | 
 | { | 
 |   char * codec_id; | 
 |   struct track_entry * entry; | 
 |  | 
 |   entry = ne_find_track_entry(ctx, track); | 
 |   if (!entry) | 
 |     return -1; | 
 |  | 
 |   if (ne_get_string(entry->codec_id, &codec_id) != 0) | 
 |     return -1; | 
 |  | 
 |   if (strcmp(codec_id, TRACK_ID_VP8) == 0) | 
 |     return NESTEGG_CODEC_VP8; | 
 |  | 
 |   if (strcmp(codec_id, TRACK_ID_VP9) == 0) | 
 |     return NESTEGG_CODEC_VP9; | 
 |  | 
 |   if (strcmp(codec_id, TRACK_ID_VORBIS) == 0) | 
 |     return NESTEGG_CODEC_VORBIS; | 
 |  | 
 |   if (strcmp(codec_id, TRACK_ID_OPUS) == 0) | 
 |     return NESTEGG_CODEC_OPUS; | 
 |  | 
 |   return -1; | 
 | } | 
 |  | 
 | int | 
 | nestegg_track_codec_data_count(nestegg * ctx, unsigned int track, | 
 |                                unsigned int * count) | 
 | { | 
 |   struct track_entry * entry; | 
 |   struct ebml_binary codec_private; | 
 |   unsigned char * p; | 
 |  | 
 |   *count = 0; | 
 |  | 
 |   entry = ne_find_track_entry(ctx, track); | 
 |   if (!entry) | 
 |     return -1; | 
 |  | 
 |   if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS) | 
 |     return -1; | 
 |  | 
 |   if (ne_get_binary(entry->codec_private, &codec_private) != 0) | 
 |     return -1; | 
 |  | 
 |   if (codec_private.length < 1) | 
 |     return -1; | 
 |  | 
 |   p = codec_private.data; | 
 |   *count = *p + 1; | 
 |  | 
 |   if (*count > 3) | 
 |     return -1; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item, | 
 |                          unsigned char ** data, size_t * length) | 
 | { | 
 |   struct track_entry * entry; | 
 |   struct ebml_binary codec_private; | 
 |   uint64_t sizes[3], total; | 
 |   unsigned char * p; | 
 |   unsigned int count, i; | 
 |  | 
 |   *data = NULL; | 
 |   *length = 0; | 
 |  | 
 |   entry = ne_find_track_entry(ctx, track); | 
 |   if (!entry) | 
 |     return -1; | 
 |  | 
 |   if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS | 
 |     && nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) | 
 |     return -1; | 
 |  | 
 |   if (ne_get_binary(entry->codec_private, &codec_private) != 0) | 
 |     return -1; | 
 |  | 
 |   if (nestegg_track_codec_id(ctx, track) == NESTEGG_CODEC_VORBIS) { | 
 |       p = codec_private.data; | 
 |       count = *p++ + 1; | 
 |  | 
 |       if (count > 3) | 
 |         return -1; | 
 |  | 
 |       i = 0; | 
 |       total = 0; | 
 |       while (--count) { | 
 |         sizes[i] = ne_xiph_lace_value(&p); | 
 |         total += sizes[i]; | 
 |         i += 1; | 
 |       } | 
 |       sizes[i] = codec_private.length - total - (p - codec_private.data); | 
 |  | 
 |       for (i = 0; i < item; ++i) { | 
 |         if (sizes[i] > LIMIT_FRAME) | 
 |           return -1; | 
 |         p += sizes[i]; | 
 |       } | 
 |       *data = p; | 
 |       *length = (size_t)sizes[item]; | 
 |   } else { | 
 |     *data = codec_private.data; | 
 |     *length = codec_private.length; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_track_video_params(nestegg * ctx, unsigned int track, | 
 |                            nestegg_video_params * params) | 
 | { | 
 |   struct track_entry * entry; | 
 |   unsigned int value; | 
 |  | 
 |   memset(params, 0, sizeof(*params)); | 
 |  | 
 |   entry = ne_find_track_entry(ctx, track); | 
 |   if (!entry) | 
 |     return -1; | 
 |  | 
 |   if (nestegg_track_type(ctx, track) != NESTEGG_TRACK_VIDEO) | 
 |     return -1; | 
 |  | 
 |   value = 0; | 
 |   ne_get_uint32(entry->video.stereo_mode, &value); | 
 |   if (value <= NESTEGG_VIDEO_STEREO_TOP_BOTTOM || | 
 |       value == NESTEGG_VIDEO_STEREO_RIGHT_LEFT) | 
 |     params->stereo_mode = value; | 
 |  | 
 |   if (ne_get_uint32(entry->video.pixel_width, &value) != 0) | 
 |     return -1; | 
 |   params->width = value; | 
 |  | 
 |   if (ne_get_uint32(entry->video.pixel_height, &value) != 0) | 
 |     return -1; | 
 |   params->height = value; | 
 |  | 
 |   value = 0; | 
 |   ne_get_uint32(entry->video.pixel_crop_bottom, &value); | 
 |   params->crop_bottom = value; | 
 |  | 
 |   value = 0; | 
 |   ne_get_uint32(entry->video.pixel_crop_top, &value); | 
 |   params->crop_top = value; | 
 |  | 
 |   value = 0; | 
 |   ne_get_uint32(entry->video.pixel_crop_left, &value); | 
 |   params->crop_left = value; | 
 |  | 
 |   value = 0; | 
 |   ne_get_uint32(entry->video.pixel_crop_right, &value); | 
 |   params->crop_right = value; | 
 |  | 
 |   value = params->width; | 
 |   ne_get_uint32(entry->video.display_width, &value); | 
 |   params->display_width = value; | 
 |  | 
 |   value = params->height; | 
 |   ne_get_uint32(entry->video.display_height, &value); | 
 |   params->display_height = value; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_track_audio_params(nestegg * ctx, unsigned int track, | 
 |                            nestegg_audio_params * params) | 
 | { | 
 |   struct track_entry * entry; | 
 |   unsigned int value; | 
 |  | 
 |   memset(params, 0, sizeof(*params)); | 
 |  | 
 |   entry = ne_find_track_entry(ctx, track); | 
 |   if (!entry) | 
 |     return -1; | 
 |  | 
 |   if (nestegg_track_type(ctx, track) != NESTEGG_TRACK_AUDIO) | 
 |     return -1; | 
 |  | 
 |   params->rate = 8000; | 
 |   ne_get_float(entry->audio.sampling_frequency, ¶ms->rate); | 
 |  | 
 |   value = 1; | 
 |   ne_get_uint32(entry->audio.channels, &value); | 
 |   params->channels = value; | 
 |  | 
 |   value = 16; | 
 |   ne_get_uint32(entry->audio.bit_depth, &value); | 
 |   params->depth = value; | 
 |  | 
 |   value = 0; | 
 |   ne_get_uint32(entry->codec_delay, &value); | 
 |   params->codec_delay = value; | 
 |  | 
 |   value = 0; | 
 |   ne_get_uint32(entry->seek_preroll, &value); | 
 |   params->seek_preroll = value; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_read_packet(nestegg * ctx, nestegg_packet ** pkt) | 
 | { | 
 |   int r; | 
 |   uint64_t id, size; | 
 |  | 
 |   *pkt = NULL; | 
 |  | 
 |   for (;;) { | 
 |     r = ne_peek_element(ctx, &id, &size); | 
 |     if (r != 1) | 
 |       return r; | 
 |  | 
 |     /* Any DESC_FLAG_SUSPEND fields must be handled here. */ | 
 |     if (ne_is_suspend_element(id)) { | 
 |       r = ne_read_element(ctx, &id, &size); | 
 |       if (r != 1) | 
 |         return r; | 
 |  | 
 |       /* The only DESC_FLAG_SUSPEND fields are Blocks and SimpleBlocks, which we | 
 |          handle directly. */ | 
 |       r = ne_read_block(ctx, id, size, pkt); | 
 |       if (r != 1) | 
 |         return r; | 
 |  | 
 |       r = ne_read_discard_padding(ctx, *pkt); | 
 |       if (r != 1) | 
 |         return r; | 
 |  | 
 |       return r; | 
 |     } | 
 |  | 
 |     r =  ne_parse(ctx, NULL, -1); | 
 |     if (r != 1) | 
 |       return r; | 
 |   } | 
 |  | 
 |   return 1; | 
 | } | 
 |  | 
 | void | 
 | nestegg_free_packet(nestegg_packet * pkt) | 
 | { | 
 |   struct frame * frame; | 
 |  | 
 |   while (pkt->frame) { | 
 |     frame = pkt->frame; | 
 |     pkt->frame = frame->next; | 
 |     free(frame->data); | 
 |     free(frame); | 
 |   } | 
 |  | 
 |  free(pkt); | 
 | } | 
 |  | 
 | int | 
 | nestegg_packet_track(nestegg_packet * pkt, unsigned int * track) | 
 | { | 
 |   *track = (unsigned int)pkt->track; | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_packet_tstamp(nestegg_packet * pkt, uint64_t * tstamp) | 
 | { | 
 |   *tstamp = pkt->timecode; | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_packet_discard_padding(nestegg_packet * pkt, int64_t * discard_padding) | 
 | { | 
 |   *discard_padding = pkt->discard_padding; | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_packet_count(nestegg_packet * pkt, unsigned int * count) | 
 | { | 
 |   struct frame * f = pkt->frame; | 
 |  | 
 |   *count = 0; | 
 |  | 
 |   while (f) { | 
 |     *count += 1; | 
 |     f = f->next; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int | 
 | nestegg_packet_data(nestegg_packet * pkt, unsigned int item, | 
 |                     unsigned char ** data, size_t * length) | 
 | { | 
 |   struct frame * f = pkt->frame; | 
 |   unsigned int count = 0; | 
 |  | 
 |   *data = NULL; | 
 |   *length = 0; | 
 |  | 
 |   while (f) { | 
 |     if (count == item) { | 
 |       *data = f->data; | 
 |       *length = f->length; | 
 |       return 0; | 
 |     } | 
 |     count += 1; | 
 |     f = f->next; | 
 |   } | 
 |  | 
 |   return -1; | 
 | } | 
 |  | 
 | int | 
 | nestegg_has_cues(nestegg * ctx) | 
 | { | 
 |   return ctx->segment.cues.cue_point.head || | 
 |          ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); | 
 | } | 
 |  | 
 | int | 
 | nestegg_sniff(unsigned char const * buffer, size_t length) | 
 | { | 
 |   nestegg_io io; | 
 |   struct sniff_buffer user_data; | 
 |  | 
 |   user_data.buffer = buffer; | 
 |   user_data.length = length; | 
 |   user_data.offset = 0; | 
 |  | 
 |   io.read = ne_buffer_read; | 
 |   io.seek = ne_buffer_seek; | 
 |   io.tell = ne_buffer_tell; | 
 |   io.userdata = &user_data; | 
 |   return ne_match_webm(io, length); | 
 | } | 
 |  |