Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* |
michael@0 | 2 | * Copyright © 2010 Mozilla Foundation |
michael@0 | 3 | * |
michael@0 | 4 | * This program is made available under an ISC-style license. See the |
michael@0 | 5 | * accompanying file LICENSE for details. |
michael@0 | 6 | */ |
michael@0 | 7 | #include <assert.h> |
michael@0 | 8 | #include <stdlib.h> |
michael@0 | 9 | #include <string.h> |
michael@0 | 10 | |
michael@0 | 11 | #include "halloc.h" |
michael@0 | 12 | #include "nestegg/nestegg.h" |
michael@0 | 13 | |
michael@0 | 14 | /* EBML Elements */ |
michael@0 | 15 | #define ID_EBML 0x1a45dfa3 |
michael@0 | 16 | #define ID_EBML_VERSION 0x4286 |
michael@0 | 17 | #define ID_EBML_READ_VERSION 0x42f7 |
michael@0 | 18 | #define ID_EBML_MAX_ID_LENGTH 0x42f2 |
michael@0 | 19 | #define ID_EBML_MAX_SIZE_LENGTH 0x42f3 |
michael@0 | 20 | #define ID_DOCTYPE 0x4282 |
michael@0 | 21 | #define ID_DOCTYPE_VERSION 0x4287 |
michael@0 | 22 | #define ID_DOCTYPE_READ_VERSION 0x4285 |
michael@0 | 23 | |
michael@0 | 24 | /* Global Elements */ |
michael@0 | 25 | #define ID_VOID 0xec |
michael@0 | 26 | #define ID_CRC32 0xbf |
michael@0 | 27 | |
michael@0 | 28 | /* WebM Elements */ |
michael@0 | 29 | #define ID_SEGMENT 0x18538067 |
michael@0 | 30 | |
michael@0 | 31 | /* Seek Head Elements */ |
michael@0 | 32 | #define ID_SEEK_HEAD 0x114d9b74 |
michael@0 | 33 | #define ID_SEEK 0x4dbb |
michael@0 | 34 | #define ID_SEEK_ID 0x53ab |
michael@0 | 35 | #define ID_SEEK_POSITION 0x53ac |
michael@0 | 36 | |
michael@0 | 37 | /* Info Elements */ |
michael@0 | 38 | #define ID_INFO 0x1549a966 |
michael@0 | 39 | #define ID_TIMECODE_SCALE 0x2ad7b1 |
michael@0 | 40 | #define ID_DURATION 0x4489 |
michael@0 | 41 | |
michael@0 | 42 | /* Cluster Elements */ |
michael@0 | 43 | #define ID_CLUSTER 0x1f43b675 |
michael@0 | 44 | #define ID_TIMECODE 0xe7 |
michael@0 | 45 | #define ID_BLOCK_GROUP 0xa0 |
michael@0 | 46 | #define ID_SIMPLE_BLOCK 0xa3 |
michael@0 | 47 | |
michael@0 | 48 | /* BlockGroup Elements */ |
michael@0 | 49 | #define ID_BLOCK 0xa1 |
michael@0 | 50 | #define ID_BLOCK_DURATION 0x9b |
michael@0 | 51 | #define ID_REFERENCE_BLOCK 0xfb |
michael@0 | 52 | #define ID_DISCARD_PADDING 0x75a2 |
michael@0 | 53 | |
michael@0 | 54 | /* Tracks Elements */ |
michael@0 | 55 | #define ID_TRACKS 0x1654ae6b |
michael@0 | 56 | #define ID_TRACK_ENTRY 0xae |
michael@0 | 57 | #define ID_TRACK_NUMBER 0xd7 |
michael@0 | 58 | #define ID_TRACK_UID 0x73c5 |
michael@0 | 59 | #define ID_TRACK_TYPE 0x83 |
michael@0 | 60 | #define ID_FLAG_ENABLED 0xb9 |
michael@0 | 61 | #define ID_FLAG_DEFAULT 0x88 |
michael@0 | 62 | #define ID_FLAG_LACING 0x9c |
michael@0 | 63 | #define ID_TRACK_TIMECODE_SCALE 0x23314f |
michael@0 | 64 | #define ID_LANGUAGE 0x22b59c |
michael@0 | 65 | #define ID_CODEC_ID 0x86 |
michael@0 | 66 | #define ID_CODEC_PRIVATE 0x63a2 |
michael@0 | 67 | #define ID_CODEC_DELAY 0x56aa |
michael@0 | 68 | #define ID_SEEK_PREROLL 0x56bb |
michael@0 | 69 | #define ID_DEFAULT_DURATION 0x23e383 |
michael@0 | 70 | |
michael@0 | 71 | /* Video Elements */ |
michael@0 | 72 | #define ID_VIDEO 0xe0 |
michael@0 | 73 | #define ID_STEREO_MODE 0x53b8 |
michael@0 | 74 | #define ID_PIXEL_WIDTH 0xb0 |
michael@0 | 75 | #define ID_PIXEL_HEIGHT 0xba |
michael@0 | 76 | #define ID_PIXEL_CROP_BOTTOM 0x54aa |
michael@0 | 77 | #define ID_PIXEL_CROP_TOP 0x54bb |
michael@0 | 78 | #define ID_PIXEL_CROP_LEFT 0x54cc |
michael@0 | 79 | #define ID_PIXEL_CROP_RIGHT 0x54dd |
michael@0 | 80 | #define ID_DISPLAY_WIDTH 0x54b0 |
michael@0 | 81 | #define ID_DISPLAY_HEIGHT 0x54ba |
michael@0 | 82 | |
michael@0 | 83 | /* Audio Elements */ |
michael@0 | 84 | #define ID_AUDIO 0xe1 |
michael@0 | 85 | #define ID_SAMPLING_FREQUENCY 0xb5 |
michael@0 | 86 | #define ID_CHANNELS 0x9f |
michael@0 | 87 | #define ID_BIT_DEPTH 0x6264 |
michael@0 | 88 | |
michael@0 | 89 | /* Cues Elements */ |
michael@0 | 90 | #define ID_CUES 0x1c53bb6b |
michael@0 | 91 | #define ID_CUE_POINT 0xbb |
michael@0 | 92 | #define ID_CUE_TIME 0xb3 |
michael@0 | 93 | #define ID_CUE_TRACK_POSITIONS 0xb7 |
michael@0 | 94 | #define ID_CUE_TRACK 0xf7 |
michael@0 | 95 | #define ID_CUE_CLUSTER_POSITION 0xf1 |
michael@0 | 96 | #define ID_CUE_BLOCK_NUMBER 0x5378 |
michael@0 | 97 | |
michael@0 | 98 | /* EBML Types */ |
michael@0 | 99 | enum ebml_type_enum { |
michael@0 | 100 | TYPE_UNKNOWN, |
michael@0 | 101 | TYPE_MASTER, |
michael@0 | 102 | TYPE_UINT, |
michael@0 | 103 | TYPE_FLOAT, |
michael@0 | 104 | TYPE_INT, |
michael@0 | 105 | TYPE_STRING, |
michael@0 | 106 | TYPE_BINARY |
michael@0 | 107 | }; |
michael@0 | 108 | |
michael@0 | 109 | #define LIMIT_STRING (1 << 20) |
michael@0 | 110 | #define LIMIT_BINARY (1 << 24) |
michael@0 | 111 | #define LIMIT_BLOCK (1 << 30) |
michael@0 | 112 | #define LIMIT_FRAME (1 << 28) |
michael@0 | 113 | |
michael@0 | 114 | /* Field Flags */ |
michael@0 | 115 | #define DESC_FLAG_NONE 0 |
michael@0 | 116 | #define DESC_FLAG_MULTI (1 << 0) |
michael@0 | 117 | #define DESC_FLAG_SUSPEND (1 << 1) |
michael@0 | 118 | #define DESC_FLAG_OFFSET (1 << 2) |
michael@0 | 119 | |
michael@0 | 120 | /* Block Header Flags */ |
michael@0 | 121 | #define BLOCK_FLAGS_LACING 6 |
michael@0 | 122 | |
michael@0 | 123 | /* Lacing Constants */ |
michael@0 | 124 | #define LACING_NONE 0 |
michael@0 | 125 | #define LACING_XIPH 1 |
michael@0 | 126 | #define LACING_FIXED 2 |
michael@0 | 127 | #define LACING_EBML 3 |
michael@0 | 128 | |
michael@0 | 129 | /* Track Types */ |
michael@0 | 130 | #define TRACK_TYPE_VIDEO 1 |
michael@0 | 131 | #define TRACK_TYPE_AUDIO 2 |
michael@0 | 132 | |
michael@0 | 133 | /* Track IDs */ |
michael@0 | 134 | #define TRACK_ID_VP8 "V_VP8" |
michael@0 | 135 | #define TRACK_ID_VP9 "V_VP9" |
michael@0 | 136 | #define TRACK_ID_VORBIS "A_VORBIS" |
michael@0 | 137 | #define TRACK_ID_OPUS "A_OPUS" |
michael@0 | 138 | |
michael@0 | 139 | enum vint_mask { |
michael@0 | 140 | MASK_NONE, |
michael@0 | 141 | MASK_FIRST_BIT |
michael@0 | 142 | }; |
michael@0 | 143 | |
michael@0 | 144 | struct ebml_binary { |
michael@0 | 145 | unsigned char * data; |
michael@0 | 146 | size_t length; |
michael@0 | 147 | }; |
michael@0 | 148 | |
michael@0 | 149 | struct ebml_list_node { |
michael@0 | 150 | struct ebml_list_node * next; |
michael@0 | 151 | uint64_t id; |
michael@0 | 152 | void * data; |
michael@0 | 153 | }; |
michael@0 | 154 | |
michael@0 | 155 | struct ebml_list { |
michael@0 | 156 | struct ebml_list_node * head; |
michael@0 | 157 | struct ebml_list_node * tail; |
michael@0 | 158 | }; |
michael@0 | 159 | |
michael@0 | 160 | struct ebml_type { |
michael@0 | 161 | union ebml_value { |
michael@0 | 162 | uint64_t u; |
michael@0 | 163 | double f; |
michael@0 | 164 | int64_t i; |
michael@0 | 165 | char * s; |
michael@0 | 166 | struct ebml_binary b; |
michael@0 | 167 | } v; |
michael@0 | 168 | enum ebml_type_enum type; |
michael@0 | 169 | int read; |
michael@0 | 170 | }; |
michael@0 | 171 | |
michael@0 | 172 | /* EBML Definitions */ |
michael@0 | 173 | struct ebml { |
michael@0 | 174 | struct ebml_type ebml_version; |
michael@0 | 175 | struct ebml_type ebml_read_version; |
michael@0 | 176 | struct ebml_type ebml_max_id_length; |
michael@0 | 177 | struct ebml_type ebml_max_size_length; |
michael@0 | 178 | struct ebml_type doctype; |
michael@0 | 179 | struct ebml_type doctype_version; |
michael@0 | 180 | struct ebml_type doctype_read_version; |
michael@0 | 181 | }; |
michael@0 | 182 | |
michael@0 | 183 | /* Matroksa Definitions */ |
michael@0 | 184 | struct seek { |
michael@0 | 185 | struct ebml_type id; |
michael@0 | 186 | struct ebml_type position; |
michael@0 | 187 | }; |
michael@0 | 188 | |
michael@0 | 189 | struct seek_head { |
michael@0 | 190 | struct ebml_list seek; |
michael@0 | 191 | }; |
michael@0 | 192 | |
michael@0 | 193 | struct info { |
michael@0 | 194 | struct ebml_type timecode_scale; |
michael@0 | 195 | struct ebml_type duration; |
michael@0 | 196 | }; |
michael@0 | 197 | |
michael@0 | 198 | struct block_group { |
michael@0 | 199 | struct ebml_type duration; |
michael@0 | 200 | struct ebml_type reference_block; |
michael@0 | 201 | struct ebml_type discard_padding; |
michael@0 | 202 | }; |
michael@0 | 203 | |
michael@0 | 204 | struct cluster { |
michael@0 | 205 | struct ebml_type timecode; |
michael@0 | 206 | struct ebml_list block_group; |
michael@0 | 207 | }; |
michael@0 | 208 | |
michael@0 | 209 | struct video { |
michael@0 | 210 | struct ebml_type stereo_mode; |
michael@0 | 211 | struct ebml_type pixel_width; |
michael@0 | 212 | struct ebml_type pixel_height; |
michael@0 | 213 | struct ebml_type pixel_crop_bottom; |
michael@0 | 214 | struct ebml_type pixel_crop_top; |
michael@0 | 215 | struct ebml_type pixel_crop_left; |
michael@0 | 216 | struct ebml_type pixel_crop_right; |
michael@0 | 217 | struct ebml_type display_width; |
michael@0 | 218 | struct ebml_type display_height; |
michael@0 | 219 | }; |
michael@0 | 220 | |
michael@0 | 221 | struct audio { |
michael@0 | 222 | struct ebml_type sampling_frequency; |
michael@0 | 223 | struct ebml_type channels; |
michael@0 | 224 | struct ebml_type bit_depth; |
michael@0 | 225 | }; |
michael@0 | 226 | |
michael@0 | 227 | struct track_entry { |
michael@0 | 228 | struct ebml_type number; |
michael@0 | 229 | struct ebml_type uid; |
michael@0 | 230 | struct ebml_type type; |
michael@0 | 231 | struct ebml_type flag_enabled; |
michael@0 | 232 | struct ebml_type flag_default; |
michael@0 | 233 | struct ebml_type flag_lacing; |
michael@0 | 234 | struct ebml_type track_timecode_scale; |
michael@0 | 235 | struct ebml_type language; |
michael@0 | 236 | struct ebml_type codec_id; |
michael@0 | 237 | struct ebml_type codec_private; |
michael@0 | 238 | struct ebml_type codec_delay; |
michael@0 | 239 | struct ebml_type seek_preroll; |
michael@0 | 240 | struct ebml_type default_duration; |
michael@0 | 241 | struct video video; |
michael@0 | 242 | struct audio audio; |
michael@0 | 243 | }; |
michael@0 | 244 | |
michael@0 | 245 | struct tracks { |
michael@0 | 246 | struct ebml_list track_entry; |
michael@0 | 247 | }; |
michael@0 | 248 | |
michael@0 | 249 | struct cue_track_positions { |
michael@0 | 250 | struct ebml_type track; |
michael@0 | 251 | struct ebml_type cluster_position; |
michael@0 | 252 | struct ebml_type block_number; |
michael@0 | 253 | }; |
michael@0 | 254 | |
michael@0 | 255 | struct cue_point { |
michael@0 | 256 | struct ebml_type time; |
michael@0 | 257 | struct ebml_list cue_track_positions; |
michael@0 | 258 | }; |
michael@0 | 259 | |
michael@0 | 260 | struct cues { |
michael@0 | 261 | struct ebml_list cue_point; |
michael@0 | 262 | }; |
michael@0 | 263 | |
michael@0 | 264 | struct segment { |
michael@0 | 265 | struct ebml_list seek_head; |
michael@0 | 266 | struct info info; |
michael@0 | 267 | struct ebml_list cluster; |
michael@0 | 268 | struct tracks tracks; |
michael@0 | 269 | struct cues cues; |
michael@0 | 270 | }; |
michael@0 | 271 | |
michael@0 | 272 | /* Misc. */ |
michael@0 | 273 | struct pool_ctx { |
michael@0 | 274 | char dummy; |
michael@0 | 275 | }; |
michael@0 | 276 | |
michael@0 | 277 | struct list_node { |
michael@0 | 278 | struct list_node * previous; |
michael@0 | 279 | struct ebml_element_desc * node; |
michael@0 | 280 | unsigned char * data; |
michael@0 | 281 | }; |
michael@0 | 282 | |
michael@0 | 283 | struct saved_state { |
michael@0 | 284 | int64_t stream_offset; |
michael@0 | 285 | struct list_node * ancestor; |
michael@0 | 286 | uint64_t last_id; |
michael@0 | 287 | uint64_t last_size; |
michael@0 | 288 | int last_valid; |
michael@0 | 289 | }; |
michael@0 | 290 | |
michael@0 | 291 | struct frame { |
michael@0 | 292 | unsigned char * data; |
michael@0 | 293 | size_t length; |
michael@0 | 294 | struct frame * next; |
michael@0 | 295 | }; |
michael@0 | 296 | |
michael@0 | 297 | /* Public (opaque) Structures */ |
michael@0 | 298 | struct nestegg { |
michael@0 | 299 | nestegg_io * io; |
michael@0 | 300 | nestegg_log log; |
michael@0 | 301 | struct pool_ctx * alloc_pool; |
michael@0 | 302 | uint64_t last_id; |
michael@0 | 303 | uint64_t last_size; |
michael@0 | 304 | int last_valid; |
michael@0 | 305 | struct list_node * ancestor; |
michael@0 | 306 | struct ebml ebml; |
michael@0 | 307 | struct segment segment; |
michael@0 | 308 | int64_t segment_offset; |
michael@0 | 309 | unsigned int track_count; |
michael@0 | 310 | }; |
michael@0 | 311 | |
michael@0 | 312 | struct nestegg_packet { |
michael@0 | 313 | uint64_t track; |
michael@0 | 314 | uint64_t timecode; |
michael@0 | 315 | uint64_t duration; |
michael@0 | 316 | struct frame * frame; |
michael@0 | 317 | int64_t discard_padding; |
michael@0 | 318 | }; |
michael@0 | 319 | |
michael@0 | 320 | /* Element Descriptor */ |
michael@0 | 321 | struct ebml_element_desc { |
michael@0 | 322 | char const * name; |
michael@0 | 323 | uint64_t id; |
michael@0 | 324 | enum ebml_type_enum type; |
michael@0 | 325 | size_t offset; |
michael@0 | 326 | unsigned int flags; |
michael@0 | 327 | struct ebml_element_desc * children; |
michael@0 | 328 | size_t size; |
michael@0 | 329 | size_t data_offset; |
michael@0 | 330 | }; |
michael@0 | 331 | |
michael@0 | 332 | #define E_FIELD(ID, TYPE, STRUCT, FIELD) \ |
michael@0 | 333 | { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_NONE, NULL, 0, 0 } |
michael@0 | 334 | #define E_MASTER(ID, TYPE, STRUCT, FIELD) \ |
michael@0 | 335 | { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_MULTI, ne_ ## FIELD ## _elements, \ |
michael@0 | 336 | sizeof(struct FIELD), 0 } |
michael@0 | 337 | #define E_SINGLE_MASTER_O(ID, TYPE, STRUCT, FIELD) \ |
michael@0 | 338 | { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_OFFSET, ne_ ## FIELD ## _elements, 0, \ |
michael@0 | 339 | offsetof(STRUCT, FIELD ## _offset) } |
michael@0 | 340 | #define E_SINGLE_MASTER(ID, TYPE, STRUCT, FIELD) \ |
michael@0 | 341 | { #ID, ID, TYPE, offsetof(STRUCT, FIELD), DESC_FLAG_NONE, ne_ ## FIELD ## _elements, 0, 0 } |
michael@0 | 342 | #define E_SUSPEND(ID, TYPE) \ |
michael@0 | 343 | { #ID, ID, TYPE, 0, DESC_FLAG_SUSPEND, NULL, 0, 0 } |
michael@0 | 344 | #define E_LAST \ |
michael@0 | 345 | { NULL, 0, 0, 0, DESC_FLAG_NONE, NULL, 0, 0 } |
michael@0 | 346 | |
michael@0 | 347 | /* EBML Element Lists */ |
michael@0 | 348 | static struct ebml_element_desc ne_ebml_elements[] = { |
michael@0 | 349 | E_FIELD(ID_EBML_VERSION, TYPE_UINT, struct ebml, ebml_version), |
michael@0 | 350 | E_FIELD(ID_EBML_READ_VERSION, TYPE_UINT, struct ebml, ebml_read_version), |
michael@0 | 351 | E_FIELD(ID_EBML_MAX_ID_LENGTH, TYPE_UINT, struct ebml, ebml_max_id_length), |
michael@0 | 352 | E_FIELD(ID_EBML_MAX_SIZE_LENGTH, TYPE_UINT, struct ebml, ebml_max_size_length), |
michael@0 | 353 | E_FIELD(ID_DOCTYPE, TYPE_STRING, struct ebml, doctype), |
michael@0 | 354 | E_FIELD(ID_DOCTYPE_VERSION, TYPE_UINT, struct ebml, doctype_version), |
michael@0 | 355 | E_FIELD(ID_DOCTYPE_READ_VERSION, TYPE_UINT, struct ebml, doctype_read_version), |
michael@0 | 356 | E_LAST |
michael@0 | 357 | }; |
michael@0 | 358 | |
michael@0 | 359 | /* WebM Element Lists */ |
michael@0 | 360 | static struct ebml_element_desc ne_seek_elements[] = { |
michael@0 | 361 | E_FIELD(ID_SEEK_ID, TYPE_BINARY, struct seek, id), |
michael@0 | 362 | E_FIELD(ID_SEEK_POSITION, TYPE_UINT, struct seek, position), |
michael@0 | 363 | E_LAST |
michael@0 | 364 | }; |
michael@0 | 365 | |
michael@0 | 366 | static struct ebml_element_desc ne_seek_head_elements[] = { |
michael@0 | 367 | E_MASTER(ID_SEEK, TYPE_MASTER, struct seek_head, seek), |
michael@0 | 368 | E_LAST |
michael@0 | 369 | }; |
michael@0 | 370 | |
michael@0 | 371 | static struct ebml_element_desc ne_info_elements[] = { |
michael@0 | 372 | E_FIELD(ID_TIMECODE_SCALE, TYPE_UINT, struct info, timecode_scale), |
michael@0 | 373 | E_FIELD(ID_DURATION, TYPE_FLOAT, struct info, duration), |
michael@0 | 374 | E_LAST |
michael@0 | 375 | }; |
michael@0 | 376 | |
michael@0 | 377 | static struct ebml_element_desc ne_block_group_elements[] = { |
michael@0 | 378 | E_SUSPEND(ID_BLOCK, TYPE_BINARY), |
michael@0 | 379 | E_FIELD(ID_BLOCK_DURATION, TYPE_UINT, struct block_group, duration), |
michael@0 | 380 | E_FIELD(ID_REFERENCE_BLOCK, TYPE_INT, struct block_group, reference_block), |
michael@0 | 381 | E_FIELD(ID_DISCARD_PADDING, TYPE_INT, struct block_group, discard_padding), |
michael@0 | 382 | E_LAST |
michael@0 | 383 | }; |
michael@0 | 384 | |
michael@0 | 385 | static struct ebml_element_desc ne_cluster_elements[] = { |
michael@0 | 386 | E_FIELD(ID_TIMECODE, TYPE_UINT, struct cluster, timecode), |
michael@0 | 387 | E_MASTER(ID_BLOCK_GROUP, TYPE_MASTER, struct cluster, block_group), |
michael@0 | 388 | E_SUSPEND(ID_SIMPLE_BLOCK, TYPE_BINARY), |
michael@0 | 389 | E_LAST |
michael@0 | 390 | }; |
michael@0 | 391 | |
michael@0 | 392 | static struct ebml_element_desc ne_video_elements[] = { |
michael@0 | 393 | E_FIELD(ID_STEREO_MODE, TYPE_UINT, struct video, stereo_mode), |
michael@0 | 394 | E_FIELD(ID_PIXEL_WIDTH, TYPE_UINT, struct video, pixel_width), |
michael@0 | 395 | E_FIELD(ID_PIXEL_HEIGHT, TYPE_UINT, struct video, pixel_height), |
michael@0 | 396 | E_FIELD(ID_PIXEL_CROP_BOTTOM, TYPE_UINT, struct video, pixel_crop_bottom), |
michael@0 | 397 | E_FIELD(ID_PIXEL_CROP_TOP, TYPE_UINT, struct video, pixel_crop_top), |
michael@0 | 398 | E_FIELD(ID_PIXEL_CROP_LEFT, TYPE_UINT, struct video, pixel_crop_left), |
michael@0 | 399 | E_FIELD(ID_PIXEL_CROP_RIGHT, TYPE_UINT, struct video, pixel_crop_right), |
michael@0 | 400 | E_FIELD(ID_DISPLAY_WIDTH, TYPE_UINT, struct video, display_width), |
michael@0 | 401 | E_FIELD(ID_DISPLAY_HEIGHT, TYPE_UINT, struct video, display_height), |
michael@0 | 402 | E_LAST |
michael@0 | 403 | }; |
michael@0 | 404 | |
michael@0 | 405 | static struct ebml_element_desc ne_audio_elements[] = { |
michael@0 | 406 | E_FIELD(ID_SAMPLING_FREQUENCY, TYPE_FLOAT, struct audio, sampling_frequency), |
michael@0 | 407 | E_FIELD(ID_CHANNELS, TYPE_UINT, struct audio, channels), |
michael@0 | 408 | E_FIELD(ID_BIT_DEPTH, TYPE_UINT, struct audio, bit_depth), |
michael@0 | 409 | E_LAST |
michael@0 | 410 | }; |
michael@0 | 411 | |
michael@0 | 412 | static struct ebml_element_desc ne_track_entry_elements[] = { |
michael@0 | 413 | E_FIELD(ID_TRACK_NUMBER, TYPE_UINT, struct track_entry, number), |
michael@0 | 414 | E_FIELD(ID_TRACK_UID, TYPE_UINT, struct track_entry, uid), |
michael@0 | 415 | E_FIELD(ID_TRACK_TYPE, TYPE_UINT, struct track_entry, type), |
michael@0 | 416 | E_FIELD(ID_FLAG_ENABLED, TYPE_UINT, struct track_entry, flag_enabled), |
michael@0 | 417 | E_FIELD(ID_FLAG_DEFAULT, TYPE_UINT, struct track_entry, flag_default), |
michael@0 | 418 | E_FIELD(ID_FLAG_LACING, TYPE_UINT, struct track_entry, flag_lacing), |
michael@0 | 419 | E_FIELD(ID_TRACK_TIMECODE_SCALE, TYPE_FLOAT, struct track_entry, track_timecode_scale), |
michael@0 | 420 | E_FIELD(ID_LANGUAGE, TYPE_STRING, struct track_entry, language), |
michael@0 | 421 | E_FIELD(ID_CODEC_ID, TYPE_STRING, struct track_entry, codec_id), |
michael@0 | 422 | E_FIELD(ID_CODEC_PRIVATE, TYPE_BINARY, struct track_entry, codec_private), |
michael@0 | 423 | E_FIELD(ID_CODEC_DELAY, TYPE_UINT, struct track_entry, codec_delay), |
michael@0 | 424 | E_FIELD(ID_SEEK_PREROLL, TYPE_UINT, struct track_entry, seek_preroll), |
michael@0 | 425 | E_FIELD(ID_DEFAULT_DURATION, TYPE_UINT, struct track_entry, default_duration), |
michael@0 | 426 | E_SINGLE_MASTER(ID_VIDEO, TYPE_MASTER, struct track_entry, video), |
michael@0 | 427 | E_SINGLE_MASTER(ID_AUDIO, TYPE_MASTER, struct track_entry, audio), |
michael@0 | 428 | E_LAST |
michael@0 | 429 | }; |
michael@0 | 430 | |
michael@0 | 431 | static struct ebml_element_desc ne_tracks_elements[] = { |
michael@0 | 432 | E_MASTER(ID_TRACK_ENTRY, TYPE_MASTER, struct tracks, track_entry), |
michael@0 | 433 | E_LAST |
michael@0 | 434 | }; |
michael@0 | 435 | |
michael@0 | 436 | static struct ebml_element_desc ne_cue_track_positions_elements[] = { |
michael@0 | 437 | E_FIELD(ID_CUE_TRACK, TYPE_UINT, struct cue_track_positions, track), |
michael@0 | 438 | E_FIELD(ID_CUE_CLUSTER_POSITION, TYPE_UINT, struct cue_track_positions, cluster_position), |
michael@0 | 439 | E_FIELD(ID_CUE_BLOCK_NUMBER, TYPE_UINT, struct cue_track_positions, block_number), |
michael@0 | 440 | E_LAST |
michael@0 | 441 | }; |
michael@0 | 442 | |
michael@0 | 443 | static struct ebml_element_desc ne_cue_point_elements[] = { |
michael@0 | 444 | E_FIELD(ID_CUE_TIME, TYPE_UINT, struct cue_point, time), |
michael@0 | 445 | E_MASTER(ID_CUE_TRACK_POSITIONS, TYPE_MASTER, struct cue_point, cue_track_positions), |
michael@0 | 446 | E_LAST |
michael@0 | 447 | }; |
michael@0 | 448 | |
michael@0 | 449 | static struct ebml_element_desc ne_cues_elements[] = { |
michael@0 | 450 | E_MASTER(ID_CUE_POINT, TYPE_MASTER, struct cues, cue_point), |
michael@0 | 451 | E_LAST |
michael@0 | 452 | }; |
michael@0 | 453 | |
michael@0 | 454 | static struct ebml_element_desc ne_segment_elements[] = { |
michael@0 | 455 | E_MASTER(ID_SEEK_HEAD, TYPE_MASTER, struct segment, seek_head), |
michael@0 | 456 | E_SINGLE_MASTER(ID_INFO, TYPE_MASTER, struct segment, info), |
michael@0 | 457 | E_MASTER(ID_CLUSTER, TYPE_MASTER, struct segment, cluster), |
michael@0 | 458 | E_SINGLE_MASTER(ID_TRACKS, TYPE_MASTER, struct segment, tracks), |
michael@0 | 459 | E_SINGLE_MASTER(ID_CUES, TYPE_MASTER, struct segment, cues), |
michael@0 | 460 | E_LAST |
michael@0 | 461 | }; |
michael@0 | 462 | |
michael@0 | 463 | static struct ebml_element_desc ne_top_level_elements[] = { |
michael@0 | 464 | E_SINGLE_MASTER(ID_EBML, TYPE_MASTER, nestegg, ebml), |
michael@0 | 465 | E_SINGLE_MASTER_O(ID_SEGMENT, TYPE_MASTER, nestegg, segment), |
michael@0 | 466 | E_LAST |
michael@0 | 467 | }; |
michael@0 | 468 | |
michael@0 | 469 | #undef E_FIELD |
michael@0 | 470 | #undef E_MASTER |
michael@0 | 471 | #undef E_SINGLE_MASTER_O |
michael@0 | 472 | #undef E_SINGLE_MASTER |
michael@0 | 473 | #undef E_SUSPEND |
michael@0 | 474 | #undef E_LAST |
michael@0 | 475 | |
michael@0 | 476 | static struct pool_ctx * |
michael@0 | 477 | ne_pool_init(void) |
michael@0 | 478 | { |
michael@0 | 479 | return h_malloc(sizeof(struct pool_ctx)); |
michael@0 | 480 | } |
michael@0 | 481 | |
michael@0 | 482 | static void |
michael@0 | 483 | ne_pool_destroy(struct pool_ctx * pool) |
michael@0 | 484 | { |
michael@0 | 485 | h_free(pool); |
michael@0 | 486 | } |
michael@0 | 487 | |
michael@0 | 488 | static void * |
michael@0 | 489 | ne_pool_alloc(size_t size, struct pool_ctx * pool) |
michael@0 | 490 | { |
michael@0 | 491 | void * p; |
michael@0 | 492 | |
michael@0 | 493 | p = h_malloc(size); |
michael@0 | 494 | if (!p) |
michael@0 | 495 | return NULL; |
michael@0 | 496 | hattach(p, pool); |
michael@0 | 497 | memset(p, 0, size); |
michael@0 | 498 | return p; |
michael@0 | 499 | } |
michael@0 | 500 | |
michael@0 | 501 | static void * |
michael@0 | 502 | ne_alloc(size_t size) |
michael@0 | 503 | { |
michael@0 | 504 | return calloc(1, size); |
michael@0 | 505 | } |
michael@0 | 506 | |
michael@0 | 507 | static int |
michael@0 | 508 | ne_io_read(nestegg_io * io, void * buffer, size_t length) |
michael@0 | 509 | { |
michael@0 | 510 | return io->read(buffer, length, io->userdata); |
michael@0 | 511 | } |
michael@0 | 512 | |
michael@0 | 513 | static int |
michael@0 | 514 | ne_io_seek(nestegg_io * io, int64_t offset, int whence) |
michael@0 | 515 | { |
michael@0 | 516 | return io->seek(offset, whence, io->userdata); |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | static int |
michael@0 | 520 | ne_io_read_skip(nestegg_io * io, size_t length) |
michael@0 | 521 | { |
michael@0 | 522 | size_t get; |
michael@0 | 523 | unsigned char buf[8192]; |
michael@0 | 524 | int r = 1; |
michael@0 | 525 | |
michael@0 | 526 | while (length > 0) { |
michael@0 | 527 | get = length < sizeof(buf) ? length : sizeof(buf); |
michael@0 | 528 | r = ne_io_read(io, buf, get); |
michael@0 | 529 | if (r != 1) |
michael@0 | 530 | break; |
michael@0 | 531 | length -= get; |
michael@0 | 532 | } |
michael@0 | 533 | |
michael@0 | 534 | return r; |
michael@0 | 535 | } |
michael@0 | 536 | |
michael@0 | 537 | static int64_t |
michael@0 | 538 | ne_io_tell(nestegg_io * io) |
michael@0 | 539 | { |
michael@0 | 540 | return io->tell(io->userdata); |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | static int |
michael@0 | 544 | ne_bare_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length, enum vint_mask maskflag) |
michael@0 | 545 | { |
michael@0 | 546 | int r; |
michael@0 | 547 | unsigned char b; |
michael@0 | 548 | size_t maxlen = 8; |
michael@0 | 549 | unsigned int count = 1, mask = 1 << 7; |
michael@0 | 550 | |
michael@0 | 551 | r = ne_io_read(io, &b, 1); |
michael@0 | 552 | if (r != 1) |
michael@0 | 553 | return r; |
michael@0 | 554 | |
michael@0 | 555 | while (count < maxlen) { |
michael@0 | 556 | if ((b & mask) != 0) |
michael@0 | 557 | break; |
michael@0 | 558 | mask >>= 1; |
michael@0 | 559 | count += 1; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | if (length) |
michael@0 | 563 | *length = count; |
michael@0 | 564 | *value = b; |
michael@0 | 565 | |
michael@0 | 566 | if (maskflag == MASK_FIRST_BIT) |
michael@0 | 567 | *value = b & ~mask; |
michael@0 | 568 | |
michael@0 | 569 | while (--count) { |
michael@0 | 570 | r = ne_io_read(io, &b, 1); |
michael@0 | 571 | if (r != 1) |
michael@0 | 572 | return r; |
michael@0 | 573 | *value <<= 8; |
michael@0 | 574 | *value |= b; |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | return 1; |
michael@0 | 578 | } |
michael@0 | 579 | |
michael@0 | 580 | static int |
michael@0 | 581 | ne_read_id(nestegg_io * io, uint64_t * value, uint64_t * length) |
michael@0 | 582 | { |
michael@0 | 583 | return ne_bare_read_vint(io, value, length, MASK_NONE); |
michael@0 | 584 | } |
michael@0 | 585 | |
michael@0 | 586 | static int |
michael@0 | 587 | ne_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length) |
michael@0 | 588 | { |
michael@0 | 589 | return ne_bare_read_vint(io, value, length, MASK_FIRST_BIT); |
michael@0 | 590 | } |
michael@0 | 591 | |
michael@0 | 592 | static int |
michael@0 | 593 | ne_read_svint(nestegg_io * io, int64_t * value, uint64_t * length) |
michael@0 | 594 | { |
michael@0 | 595 | int r; |
michael@0 | 596 | uint64_t uvalue; |
michael@0 | 597 | uint64_t ulength; |
michael@0 | 598 | int64_t svint_subtr[] = { |
michael@0 | 599 | 0x3f, 0x1fff, |
michael@0 | 600 | 0xfffff, 0x7ffffff, |
michael@0 | 601 | 0x3ffffffffLL, 0x1ffffffffffLL, |
michael@0 | 602 | 0xffffffffffffLL, 0x7fffffffffffffLL |
michael@0 | 603 | }; |
michael@0 | 604 | |
michael@0 | 605 | r = ne_bare_read_vint(io, &uvalue, &ulength, MASK_FIRST_BIT); |
michael@0 | 606 | if (r != 1) |
michael@0 | 607 | return r; |
michael@0 | 608 | *value = uvalue - svint_subtr[ulength - 1]; |
michael@0 | 609 | if (length) |
michael@0 | 610 | *length = ulength; |
michael@0 | 611 | return r; |
michael@0 | 612 | } |
michael@0 | 613 | |
michael@0 | 614 | static int |
michael@0 | 615 | ne_read_uint(nestegg_io * io, uint64_t * val, uint64_t length) |
michael@0 | 616 | { |
michael@0 | 617 | unsigned char b; |
michael@0 | 618 | int r; |
michael@0 | 619 | |
michael@0 | 620 | if (length == 0 || length > 8) |
michael@0 | 621 | return -1; |
michael@0 | 622 | r = ne_io_read(io, &b, 1); |
michael@0 | 623 | if (r != 1) |
michael@0 | 624 | return r; |
michael@0 | 625 | *val = b; |
michael@0 | 626 | while (--length) { |
michael@0 | 627 | r = ne_io_read(io, &b, 1); |
michael@0 | 628 | if (r != 1) |
michael@0 | 629 | return r; |
michael@0 | 630 | *val <<= 8; |
michael@0 | 631 | *val |= b; |
michael@0 | 632 | } |
michael@0 | 633 | return 1; |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | static int |
michael@0 | 637 | ne_read_int(nestegg_io * io, int64_t * val, uint64_t length) |
michael@0 | 638 | { |
michael@0 | 639 | int r; |
michael@0 | 640 | uint64_t uval, base; |
michael@0 | 641 | |
michael@0 | 642 | r = ne_read_uint(io, &uval, length); |
michael@0 | 643 | if (r != 1) |
michael@0 | 644 | return r; |
michael@0 | 645 | |
michael@0 | 646 | if (length < sizeof(int64_t)) { |
michael@0 | 647 | base = 1; |
michael@0 | 648 | base <<= length * 8 - 1; |
michael@0 | 649 | if (uval >= base) { |
michael@0 | 650 | base = 1; |
michael@0 | 651 | base <<= length * 8; |
michael@0 | 652 | } else { |
michael@0 | 653 | base = 0; |
michael@0 | 654 | } |
michael@0 | 655 | *val = uval - base; |
michael@0 | 656 | } else { |
michael@0 | 657 | *val = (int64_t) uval; |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | return 1; |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | static int |
michael@0 | 664 | ne_read_float(nestegg_io * io, double * val, uint64_t length) |
michael@0 | 665 | { |
michael@0 | 666 | union { |
michael@0 | 667 | uint64_t u; |
michael@0 | 668 | float f; |
michael@0 | 669 | double d; |
michael@0 | 670 | } value; |
michael@0 | 671 | int r; |
michael@0 | 672 | |
michael@0 | 673 | /* Length == 10 not implemented. */ |
michael@0 | 674 | if (length != 4 && length != 8) |
michael@0 | 675 | return -1; |
michael@0 | 676 | r = ne_read_uint(io, &value.u, length); |
michael@0 | 677 | if (r != 1) |
michael@0 | 678 | return r; |
michael@0 | 679 | if (length == 4) |
michael@0 | 680 | *val = value.f; |
michael@0 | 681 | else |
michael@0 | 682 | *val = value.d; |
michael@0 | 683 | return 1; |
michael@0 | 684 | } |
michael@0 | 685 | |
michael@0 | 686 | static int |
michael@0 | 687 | ne_read_string(nestegg * ctx, char ** val, uint64_t length) |
michael@0 | 688 | { |
michael@0 | 689 | char * str; |
michael@0 | 690 | int r; |
michael@0 | 691 | |
michael@0 | 692 | if (length == 0 || length > LIMIT_STRING) |
michael@0 | 693 | return -1; |
michael@0 | 694 | str = ne_pool_alloc(length + 1, ctx->alloc_pool); |
michael@0 | 695 | if (!str) |
michael@0 | 696 | return -1; |
michael@0 | 697 | r = ne_io_read(ctx->io, (unsigned char *) str, length); |
michael@0 | 698 | if (r != 1) |
michael@0 | 699 | return r; |
michael@0 | 700 | str[length] = '\0'; |
michael@0 | 701 | *val = str; |
michael@0 | 702 | return 1; |
michael@0 | 703 | } |
michael@0 | 704 | |
michael@0 | 705 | static int |
michael@0 | 706 | ne_read_binary(nestegg * ctx, struct ebml_binary * val, uint64_t length) |
michael@0 | 707 | { |
michael@0 | 708 | if (length == 0 || length > LIMIT_BINARY) |
michael@0 | 709 | return -1; |
michael@0 | 710 | val->data = ne_pool_alloc(length, ctx->alloc_pool); |
michael@0 | 711 | if (!val->data) |
michael@0 | 712 | return -1; |
michael@0 | 713 | val->length = length; |
michael@0 | 714 | return ne_io_read(ctx->io, val->data, length); |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | static int |
michael@0 | 718 | ne_get_uint(struct ebml_type type, uint64_t * value) |
michael@0 | 719 | { |
michael@0 | 720 | if (!type.read) |
michael@0 | 721 | return -1; |
michael@0 | 722 | |
michael@0 | 723 | assert(type.type == TYPE_UINT); |
michael@0 | 724 | |
michael@0 | 725 | *value = type.v.u; |
michael@0 | 726 | |
michael@0 | 727 | return 0; |
michael@0 | 728 | } |
michael@0 | 729 | |
michael@0 | 730 | static int |
michael@0 | 731 | ne_get_float(struct ebml_type type, double * value) |
michael@0 | 732 | { |
michael@0 | 733 | if (!type.read) |
michael@0 | 734 | return -1; |
michael@0 | 735 | |
michael@0 | 736 | assert(type.type == TYPE_FLOAT); |
michael@0 | 737 | |
michael@0 | 738 | *value = type.v.f; |
michael@0 | 739 | |
michael@0 | 740 | return 0; |
michael@0 | 741 | } |
michael@0 | 742 | |
michael@0 | 743 | static int |
michael@0 | 744 | ne_get_string(struct ebml_type type, char ** value) |
michael@0 | 745 | { |
michael@0 | 746 | if (!type.read) |
michael@0 | 747 | return -1; |
michael@0 | 748 | |
michael@0 | 749 | assert(type.type == TYPE_STRING); |
michael@0 | 750 | |
michael@0 | 751 | *value = type.v.s; |
michael@0 | 752 | |
michael@0 | 753 | return 0; |
michael@0 | 754 | } |
michael@0 | 755 | |
michael@0 | 756 | static int |
michael@0 | 757 | ne_get_binary(struct ebml_type type, struct ebml_binary * value) |
michael@0 | 758 | { |
michael@0 | 759 | if (!type.read) |
michael@0 | 760 | return -1; |
michael@0 | 761 | |
michael@0 | 762 | assert(type.type == TYPE_BINARY); |
michael@0 | 763 | |
michael@0 | 764 | *value = type.v.b; |
michael@0 | 765 | |
michael@0 | 766 | return 0; |
michael@0 | 767 | } |
michael@0 | 768 | |
michael@0 | 769 | static int |
michael@0 | 770 | ne_is_ancestor_element(uint64_t id, struct list_node * ancestor) |
michael@0 | 771 | { |
michael@0 | 772 | struct ebml_element_desc * element; |
michael@0 | 773 | |
michael@0 | 774 | for (; ancestor; ancestor = ancestor->previous) |
michael@0 | 775 | for (element = ancestor->node; element->id; ++element) |
michael@0 | 776 | if (element->id == id) |
michael@0 | 777 | return 1; |
michael@0 | 778 | |
michael@0 | 779 | return 0; |
michael@0 | 780 | } |
michael@0 | 781 | |
michael@0 | 782 | static struct ebml_element_desc * |
michael@0 | 783 | ne_find_element(uint64_t id, struct ebml_element_desc * elements) |
michael@0 | 784 | { |
michael@0 | 785 | struct ebml_element_desc * element; |
michael@0 | 786 | |
michael@0 | 787 | for (element = elements; element->id; ++element) |
michael@0 | 788 | if (element->id == id) |
michael@0 | 789 | return element; |
michael@0 | 790 | |
michael@0 | 791 | return NULL; |
michael@0 | 792 | } |
michael@0 | 793 | |
michael@0 | 794 | static int |
michael@0 | 795 | ne_ctx_push(nestegg * ctx, struct ebml_element_desc * ancestor, void * data) |
michael@0 | 796 | { |
michael@0 | 797 | struct list_node * item; |
michael@0 | 798 | |
michael@0 | 799 | item = ne_alloc(sizeof(*item)); |
michael@0 | 800 | if (!item) |
michael@0 | 801 | return -1; |
michael@0 | 802 | item->previous = ctx->ancestor; |
michael@0 | 803 | item->node = ancestor; |
michael@0 | 804 | item->data = data; |
michael@0 | 805 | ctx->ancestor = item; |
michael@0 | 806 | return 0; |
michael@0 | 807 | } |
michael@0 | 808 | |
michael@0 | 809 | static void |
michael@0 | 810 | ne_ctx_pop(nestegg * ctx) |
michael@0 | 811 | { |
michael@0 | 812 | struct list_node * item; |
michael@0 | 813 | |
michael@0 | 814 | item = ctx->ancestor; |
michael@0 | 815 | ctx->ancestor = item->previous; |
michael@0 | 816 | free(item); |
michael@0 | 817 | } |
michael@0 | 818 | |
michael@0 | 819 | static int |
michael@0 | 820 | ne_ctx_save(nestegg * ctx, struct saved_state * s) |
michael@0 | 821 | { |
michael@0 | 822 | s->stream_offset = ne_io_tell(ctx->io); |
michael@0 | 823 | if (s->stream_offset < 0) |
michael@0 | 824 | return -1; |
michael@0 | 825 | s->ancestor = ctx->ancestor; |
michael@0 | 826 | s->last_id = ctx->last_id; |
michael@0 | 827 | s->last_size = ctx->last_size; |
michael@0 | 828 | s->last_valid = ctx->last_valid; |
michael@0 | 829 | return 0; |
michael@0 | 830 | } |
michael@0 | 831 | |
michael@0 | 832 | static int |
michael@0 | 833 | ne_ctx_restore(nestegg * ctx, struct saved_state * s) |
michael@0 | 834 | { |
michael@0 | 835 | int r; |
michael@0 | 836 | |
michael@0 | 837 | r = ne_io_seek(ctx->io, s->stream_offset, NESTEGG_SEEK_SET); |
michael@0 | 838 | if (r != 0) |
michael@0 | 839 | return -1; |
michael@0 | 840 | ctx->ancestor = s->ancestor; |
michael@0 | 841 | ctx->last_id = s->last_id; |
michael@0 | 842 | ctx->last_size = s->last_size; |
michael@0 | 843 | ctx->last_valid = s->last_valid; |
michael@0 | 844 | return 0; |
michael@0 | 845 | } |
michael@0 | 846 | |
michael@0 | 847 | static int |
michael@0 | 848 | ne_peek_element(nestegg * ctx, uint64_t * id, uint64_t * size) |
michael@0 | 849 | { |
michael@0 | 850 | int r; |
michael@0 | 851 | |
michael@0 | 852 | if (ctx->last_valid) { |
michael@0 | 853 | if (id) |
michael@0 | 854 | *id = ctx->last_id; |
michael@0 | 855 | if (size) |
michael@0 | 856 | *size = ctx->last_size; |
michael@0 | 857 | return 1; |
michael@0 | 858 | } |
michael@0 | 859 | |
michael@0 | 860 | r = ne_read_id(ctx->io, &ctx->last_id, NULL); |
michael@0 | 861 | if (r != 1) |
michael@0 | 862 | return r; |
michael@0 | 863 | |
michael@0 | 864 | r = ne_read_vint(ctx->io, &ctx->last_size, NULL); |
michael@0 | 865 | if (r != 1) |
michael@0 | 866 | return r; |
michael@0 | 867 | |
michael@0 | 868 | if (id) |
michael@0 | 869 | *id = ctx->last_id; |
michael@0 | 870 | if (size) |
michael@0 | 871 | *size = ctx->last_size; |
michael@0 | 872 | |
michael@0 | 873 | ctx->last_valid = 1; |
michael@0 | 874 | |
michael@0 | 875 | return 1; |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | static int |
michael@0 | 879 | ne_read_element(nestegg * ctx, uint64_t * id, uint64_t * size) |
michael@0 | 880 | { |
michael@0 | 881 | int r; |
michael@0 | 882 | |
michael@0 | 883 | r = ne_peek_element(ctx, id, size); |
michael@0 | 884 | if (r != 1) |
michael@0 | 885 | return r; |
michael@0 | 886 | |
michael@0 | 887 | ctx->last_valid = 0; |
michael@0 | 888 | |
michael@0 | 889 | return 1; |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | static int |
michael@0 | 893 | ne_read_master(nestegg * ctx, struct ebml_element_desc * desc) |
michael@0 | 894 | { |
michael@0 | 895 | struct ebml_list * list; |
michael@0 | 896 | struct ebml_list_node * node, * oldtail; |
michael@0 | 897 | |
michael@0 | 898 | assert(desc->type == TYPE_MASTER && desc->flags & DESC_FLAG_MULTI); |
michael@0 | 899 | |
michael@0 | 900 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "multi master element %llx (%s)", |
michael@0 | 901 | desc->id, desc->name); |
michael@0 | 902 | |
michael@0 | 903 | list = (struct ebml_list *) (ctx->ancestor->data + desc->offset); |
michael@0 | 904 | |
michael@0 | 905 | node = ne_pool_alloc(sizeof(*node), ctx->alloc_pool); |
michael@0 | 906 | if (!node) |
michael@0 | 907 | return -1; |
michael@0 | 908 | node->id = desc->id; |
michael@0 | 909 | node->data = ne_pool_alloc(desc->size, ctx->alloc_pool); |
michael@0 | 910 | if (!node->data) |
michael@0 | 911 | return -1; |
michael@0 | 912 | |
michael@0 | 913 | oldtail = list->tail; |
michael@0 | 914 | if (oldtail) |
michael@0 | 915 | oldtail->next = node; |
michael@0 | 916 | list->tail = node; |
michael@0 | 917 | if (!list->head) |
michael@0 | 918 | list->head = node; |
michael@0 | 919 | |
michael@0 | 920 | ctx->log(ctx, NESTEGG_LOG_DEBUG, " -> using data %p", node->data); |
michael@0 | 921 | |
michael@0 | 922 | if (ne_ctx_push(ctx, desc->children, node->data) < 0) |
michael@0 | 923 | return -1; |
michael@0 | 924 | |
michael@0 | 925 | return 0; |
michael@0 | 926 | } |
michael@0 | 927 | |
michael@0 | 928 | static int |
michael@0 | 929 | ne_read_single_master(nestegg * ctx, struct ebml_element_desc * desc) |
michael@0 | 930 | { |
michael@0 | 931 | assert(desc->type == TYPE_MASTER && !(desc->flags & DESC_FLAG_MULTI)); |
michael@0 | 932 | |
michael@0 | 933 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "single master element %llx (%s)", |
michael@0 | 934 | desc->id, desc->name); |
michael@0 | 935 | ctx->log(ctx, NESTEGG_LOG_DEBUG, " -> using data %p (%u)", |
michael@0 | 936 | ctx->ancestor->data + desc->offset, desc->offset); |
michael@0 | 937 | |
michael@0 | 938 | return ne_ctx_push(ctx, desc->children, ctx->ancestor->data + desc->offset); |
michael@0 | 939 | } |
michael@0 | 940 | |
michael@0 | 941 | static int |
michael@0 | 942 | ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length) |
michael@0 | 943 | { |
michael@0 | 944 | struct ebml_type * storage; |
michael@0 | 945 | int r; |
michael@0 | 946 | |
michael@0 | 947 | storage = (struct ebml_type *) (ctx->ancestor->data + desc->offset); |
michael@0 | 948 | |
michael@0 | 949 | if (storage->read) { |
michael@0 | 950 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) already read, skipping", |
michael@0 | 951 | desc->id, desc->name); |
michael@0 | 952 | return 0; |
michael@0 | 953 | } |
michael@0 | 954 | |
michael@0 | 955 | storage->type = desc->type; |
michael@0 | 956 | |
michael@0 | 957 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "element %llx (%s) -> %p (%u)", |
michael@0 | 958 | desc->id, desc->name, storage, desc->offset); |
michael@0 | 959 | |
michael@0 | 960 | switch (desc->type) { |
michael@0 | 961 | case TYPE_UINT: |
michael@0 | 962 | r = ne_read_uint(ctx->io, &storage->v.u, length); |
michael@0 | 963 | break; |
michael@0 | 964 | case TYPE_FLOAT: |
michael@0 | 965 | r = ne_read_float(ctx->io, &storage->v.f, length); |
michael@0 | 966 | break; |
michael@0 | 967 | case TYPE_INT: |
michael@0 | 968 | r = ne_read_int(ctx->io, &storage->v.i, length); |
michael@0 | 969 | break; |
michael@0 | 970 | case TYPE_STRING: |
michael@0 | 971 | r = ne_read_string(ctx, &storage->v.s, length); |
michael@0 | 972 | break; |
michael@0 | 973 | case TYPE_BINARY: |
michael@0 | 974 | r = ne_read_binary(ctx, &storage->v.b, length); |
michael@0 | 975 | break; |
michael@0 | 976 | case TYPE_MASTER: |
michael@0 | 977 | case TYPE_UNKNOWN: |
michael@0 | 978 | assert(0); |
michael@0 | 979 | r = 0; |
michael@0 | 980 | break; |
michael@0 | 981 | } |
michael@0 | 982 | |
michael@0 | 983 | if (r == 1) |
michael@0 | 984 | storage->read = 1; |
michael@0 | 985 | |
michael@0 | 986 | return r; |
michael@0 | 987 | } |
michael@0 | 988 | |
michael@0 | 989 | static int |
michael@0 | 990 | ne_parse(nestegg * ctx, struct ebml_element_desc * top_level, int64_t max_offset) |
michael@0 | 991 | { |
michael@0 | 992 | int r; |
michael@0 | 993 | int64_t * data_offset; |
michael@0 | 994 | uint64_t id, size, peeked_id; |
michael@0 | 995 | struct ebml_element_desc * element; |
michael@0 | 996 | |
michael@0 | 997 | if (!ctx->ancestor) |
michael@0 | 998 | return -1; |
michael@0 | 999 | |
michael@0 | 1000 | for (;;) { |
michael@0 | 1001 | if (max_offset > 0 && ne_io_tell(ctx->io) >= max_offset) { |
michael@0 | 1002 | /* Reached end of offset allowed for parsing - return gracefully */ |
michael@0 | 1003 | r = 1; |
michael@0 | 1004 | break; |
michael@0 | 1005 | } |
michael@0 | 1006 | r = ne_peek_element(ctx, &id, &size); |
michael@0 | 1007 | if (r != 1) |
michael@0 | 1008 | break; |
michael@0 | 1009 | peeked_id = id; |
michael@0 | 1010 | |
michael@0 | 1011 | element = ne_find_element(id, ctx->ancestor->node); |
michael@0 | 1012 | if (element) { |
michael@0 | 1013 | if (element->flags & DESC_FLAG_SUSPEND) { |
michael@0 | 1014 | assert(element->type == TYPE_BINARY); |
michael@0 | 1015 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "suspend parse at %llx", id); |
michael@0 | 1016 | r = 1; |
michael@0 | 1017 | break; |
michael@0 | 1018 | } |
michael@0 | 1019 | |
michael@0 | 1020 | r = ne_read_element(ctx, &id, &size); |
michael@0 | 1021 | if (r != 1) |
michael@0 | 1022 | break; |
michael@0 | 1023 | assert(id == peeked_id); |
michael@0 | 1024 | |
michael@0 | 1025 | if (element->flags & DESC_FLAG_OFFSET) { |
michael@0 | 1026 | data_offset = (int64_t *) (ctx->ancestor->data + element->data_offset); |
michael@0 | 1027 | *data_offset = ne_io_tell(ctx->io); |
michael@0 | 1028 | if (*data_offset < 0) { |
michael@0 | 1029 | r = -1; |
michael@0 | 1030 | break; |
michael@0 | 1031 | } |
michael@0 | 1032 | } |
michael@0 | 1033 | |
michael@0 | 1034 | if (element->type == TYPE_MASTER) { |
michael@0 | 1035 | if (element->flags & DESC_FLAG_MULTI) { |
michael@0 | 1036 | if (ne_read_master(ctx, element) < 0) |
michael@0 | 1037 | break; |
michael@0 | 1038 | } else { |
michael@0 | 1039 | if (ne_read_single_master(ctx, element) < 0) |
michael@0 | 1040 | break; |
michael@0 | 1041 | } |
michael@0 | 1042 | continue; |
michael@0 | 1043 | } else { |
michael@0 | 1044 | r = ne_read_simple(ctx, element, size); |
michael@0 | 1045 | if (r < 0) |
michael@0 | 1046 | break; |
michael@0 | 1047 | } |
michael@0 | 1048 | } else if (ne_is_ancestor_element(id, ctx->ancestor->previous)) { |
michael@0 | 1049 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "parent element %llx", id); |
michael@0 | 1050 | if (top_level && ctx->ancestor->node == top_level) { |
michael@0 | 1051 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "*** parse about to back up past top_level"); |
michael@0 | 1052 | r = 1; |
michael@0 | 1053 | break; |
michael@0 | 1054 | } |
michael@0 | 1055 | ne_ctx_pop(ctx); |
michael@0 | 1056 | } else { |
michael@0 | 1057 | r = ne_read_element(ctx, &id, &size); |
michael@0 | 1058 | if (r != 1) |
michael@0 | 1059 | break; |
michael@0 | 1060 | |
michael@0 | 1061 | if (id != ID_VOID && id != ID_CRC32) |
michael@0 | 1062 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "unknown element %llx", id); |
michael@0 | 1063 | r = ne_io_read_skip(ctx->io, size); |
michael@0 | 1064 | if (r != 1) |
michael@0 | 1065 | break; |
michael@0 | 1066 | } |
michael@0 | 1067 | } |
michael@0 | 1068 | |
michael@0 | 1069 | if (r != 1) |
michael@0 | 1070 | while (ctx->ancestor) |
michael@0 | 1071 | ne_ctx_pop(ctx); |
michael@0 | 1072 | |
michael@0 | 1073 | return r; |
michael@0 | 1074 | } |
michael@0 | 1075 | |
michael@0 | 1076 | static uint64_t |
michael@0 | 1077 | ne_xiph_lace_value(unsigned char ** np) |
michael@0 | 1078 | { |
michael@0 | 1079 | uint64_t lace; |
michael@0 | 1080 | uint64_t value; |
michael@0 | 1081 | unsigned char * p = *np; |
michael@0 | 1082 | |
michael@0 | 1083 | lace = *p++; |
michael@0 | 1084 | value = lace; |
michael@0 | 1085 | while (lace == 255) { |
michael@0 | 1086 | lace = *p++; |
michael@0 | 1087 | value += lace; |
michael@0 | 1088 | } |
michael@0 | 1089 | |
michael@0 | 1090 | *np = p; |
michael@0 | 1091 | |
michael@0 | 1092 | return value; |
michael@0 | 1093 | } |
michael@0 | 1094 | |
michael@0 | 1095 | static int |
michael@0 | 1096 | ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed) |
michael@0 | 1097 | { |
michael@0 | 1098 | int r; |
michael@0 | 1099 | uint64_t lace; |
michael@0 | 1100 | |
michael@0 | 1101 | r = ne_read_uint(io, &lace, 1); |
michael@0 | 1102 | if (r != 1) |
michael@0 | 1103 | return r; |
michael@0 | 1104 | *consumed += 1; |
michael@0 | 1105 | |
michael@0 | 1106 | *value = lace; |
michael@0 | 1107 | while (lace == 255) { |
michael@0 | 1108 | r = ne_read_uint(io, &lace, 1); |
michael@0 | 1109 | if (r != 1) |
michael@0 | 1110 | return r; |
michael@0 | 1111 | *consumed += 1; |
michael@0 | 1112 | *value += lace; |
michael@0 | 1113 | } |
michael@0 | 1114 | |
michael@0 | 1115 | return 1; |
michael@0 | 1116 | } |
michael@0 | 1117 | |
michael@0 | 1118 | static int |
michael@0 | 1119 | ne_read_xiph_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) |
michael@0 | 1120 | { |
michael@0 | 1121 | int r; |
michael@0 | 1122 | size_t i = 0; |
michael@0 | 1123 | uint64_t sum = 0; |
michael@0 | 1124 | |
michael@0 | 1125 | while (--n) { |
michael@0 | 1126 | r = ne_read_xiph_lace_value(io, &sizes[i], read); |
michael@0 | 1127 | if (r != 1) |
michael@0 | 1128 | return r; |
michael@0 | 1129 | sum += sizes[i]; |
michael@0 | 1130 | i += 1; |
michael@0 | 1131 | } |
michael@0 | 1132 | |
michael@0 | 1133 | if (*read + sum > block) |
michael@0 | 1134 | return -1; |
michael@0 | 1135 | |
michael@0 | 1136 | /* Last frame is the remainder of the block. */ |
michael@0 | 1137 | sizes[i] = block - *read - sum; |
michael@0 | 1138 | return 1; |
michael@0 | 1139 | } |
michael@0 | 1140 | |
michael@0 | 1141 | static int |
michael@0 | 1142 | ne_read_ebml_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) |
michael@0 | 1143 | { |
michael@0 | 1144 | int r; |
michael@0 | 1145 | uint64_t lace, sum, length; |
michael@0 | 1146 | int64_t slace; |
michael@0 | 1147 | size_t i = 0; |
michael@0 | 1148 | |
michael@0 | 1149 | r = ne_read_vint(io, &lace, &length); |
michael@0 | 1150 | if (r != 1) |
michael@0 | 1151 | return r; |
michael@0 | 1152 | *read += length; |
michael@0 | 1153 | |
michael@0 | 1154 | sizes[i] = lace; |
michael@0 | 1155 | sum = sizes[i]; |
michael@0 | 1156 | |
michael@0 | 1157 | i += 1; |
michael@0 | 1158 | n -= 1; |
michael@0 | 1159 | |
michael@0 | 1160 | while (--n) { |
michael@0 | 1161 | r = ne_read_svint(io, &slace, &length); |
michael@0 | 1162 | if (r != 1) |
michael@0 | 1163 | return r; |
michael@0 | 1164 | *read += length; |
michael@0 | 1165 | sizes[i] = sizes[i - 1] + slace; |
michael@0 | 1166 | sum += sizes[i]; |
michael@0 | 1167 | i += 1; |
michael@0 | 1168 | } |
michael@0 | 1169 | |
michael@0 | 1170 | if (*read + sum > block) |
michael@0 | 1171 | return -1; |
michael@0 | 1172 | |
michael@0 | 1173 | /* Last frame is the remainder of the block. */ |
michael@0 | 1174 | sizes[i] = block - *read - sum; |
michael@0 | 1175 | return 1; |
michael@0 | 1176 | } |
michael@0 | 1177 | |
michael@0 | 1178 | static uint64_t |
michael@0 | 1179 | ne_get_timecode_scale(nestegg * ctx) |
michael@0 | 1180 | { |
michael@0 | 1181 | uint64_t scale; |
michael@0 | 1182 | |
michael@0 | 1183 | if (ne_get_uint(ctx->segment.info.timecode_scale, &scale) != 0) |
michael@0 | 1184 | scale = 1000000; |
michael@0 | 1185 | |
michael@0 | 1186 | return scale; |
michael@0 | 1187 | } |
michael@0 | 1188 | |
michael@0 | 1189 | static int |
michael@0 | 1190 | ne_map_track_number_to_index(nestegg * ctx, |
michael@0 | 1191 | unsigned int track_number, |
michael@0 | 1192 | unsigned int * track_index) |
michael@0 | 1193 | { |
michael@0 | 1194 | struct ebml_list_node * node; |
michael@0 | 1195 | struct track_entry * t_entry; |
michael@0 | 1196 | uint64_t t_number = 0; |
michael@0 | 1197 | |
michael@0 | 1198 | if (!track_index) |
michael@0 | 1199 | return -1; |
michael@0 | 1200 | *track_index = 0; |
michael@0 | 1201 | |
michael@0 | 1202 | if (track_number == 0) |
michael@0 | 1203 | return -1; |
michael@0 | 1204 | |
michael@0 | 1205 | node = ctx->segment.tracks.track_entry.head; |
michael@0 | 1206 | while (node) { |
michael@0 | 1207 | assert(node->id == ID_TRACK_ENTRY); |
michael@0 | 1208 | t_entry = node->data; |
michael@0 | 1209 | if (ne_get_uint(t_entry->number, &t_number) != 0) |
michael@0 | 1210 | return -1; |
michael@0 | 1211 | if (t_number == track_number) |
michael@0 | 1212 | return 0; |
michael@0 | 1213 | *track_index += 1; |
michael@0 | 1214 | node = node->next; |
michael@0 | 1215 | } |
michael@0 | 1216 | |
michael@0 | 1217 | return -1; |
michael@0 | 1218 | } |
michael@0 | 1219 | |
michael@0 | 1220 | static struct track_entry * |
michael@0 | 1221 | ne_find_track_entry(nestegg * ctx, unsigned int track) |
michael@0 | 1222 | { |
michael@0 | 1223 | struct ebml_list_node * node; |
michael@0 | 1224 | unsigned int tracks = 0; |
michael@0 | 1225 | |
michael@0 | 1226 | node = ctx->segment.tracks.track_entry.head; |
michael@0 | 1227 | while (node) { |
michael@0 | 1228 | assert(node->id == ID_TRACK_ENTRY); |
michael@0 | 1229 | if (track == tracks) |
michael@0 | 1230 | return node->data; |
michael@0 | 1231 | tracks += 1; |
michael@0 | 1232 | node = node->next; |
michael@0 | 1233 | } |
michael@0 | 1234 | |
michael@0 | 1235 | return NULL; |
michael@0 | 1236 | } |
michael@0 | 1237 | |
michael@0 | 1238 | static int |
michael@0 | 1239 | ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_packet ** data) |
michael@0 | 1240 | { |
michael@0 | 1241 | int r; |
michael@0 | 1242 | int64_t timecode, abs_timecode; |
michael@0 | 1243 | nestegg_packet * pkt; |
michael@0 | 1244 | struct cluster * cluster; |
michael@0 | 1245 | struct frame * f, * last; |
michael@0 | 1246 | struct track_entry * entry; |
michael@0 | 1247 | double track_scale; |
michael@0 | 1248 | uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total; |
michael@0 | 1249 | unsigned int i, lacing, track; |
michael@0 | 1250 | size_t consumed = 0; |
michael@0 | 1251 | |
michael@0 | 1252 | *data = NULL; |
michael@0 | 1253 | |
michael@0 | 1254 | if (block_size > LIMIT_BLOCK) |
michael@0 | 1255 | return -1; |
michael@0 | 1256 | |
michael@0 | 1257 | r = ne_read_vint(ctx->io, &track_number, &length); |
michael@0 | 1258 | if (r != 1) |
michael@0 | 1259 | return r; |
michael@0 | 1260 | |
michael@0 | 1261 | if (track_number == 0) |
michael@0 | 1262 | return -1; |
michael@0 | 1263 | |
michael@0 | 1264 | consumed += length; |
michael@0 | 1265 | |
michael@0 | 1266 | r = ne_read_int(ctx->io, &timecode, 2); |
michael@0 | 1267 | if (r != 1) |
michael@0 | 1268 | return r; |
michael@0 | 1269 | |
michael@0 | 1270 | consumed += 2; |
michael@0 | 1271 | |
michael@0 | 1272 | r = ne_read_uint(ctx->io, &flags, 1); |
michael@0 | 1273 | if (r != 1) |
michael@0 | 1274 | return r; |
michael@0 | 1275 | |
michael@0 | 1276 | consumed += 1; |
michael@0 | 1277 | |
michael@0 | 1278 | frames = 0; |
michael@0 | 1279 | |
michael@0 | 1280 | /* Flags are different between Block and SimpleBlock, but lacing is |
michael@0 | 1281 | encoded the same way. */ |
michael@0 | 1282 | lacing = (flags & BLOCK_FLAGS_LACING) >> 1; |
michael@0 | 1283 | |
michael@0 | 1284 | switch (lacing) { |
michael@0 | 1285 | case LACING_NONE: |
michael@0 | 1286 | frames = 1; |
michael@0 | 1287 | break; |
michael@0 | 1288 | case LACING_XIPH: |
michael@0 | 1289 | case LACING_FIXED: |
michael@0 | 1290 | case LACING_EBML: |
michael@0 | 1291 | r = ne_read_uint(ctx->io, &frames, 1); |
michael@0 | 1292 | if (r != 1) |
michael@0 | 1293 | return r; |
michael@0 | 1294 | consumed += 1; |
michael@0 | 1295 | frames += 1; |
michael@0 | 1296 | } |
michael@0 | 1297 | |
michael@0 | 1298 | if (frames > 256) |
michael@0 | 1299 | return -1; |
michael@0 | 1300 | |
michael@0 | 1301 | switch (lacing) { |
michael@0 | 1302 | case LACING_NONE: |
michael@0 | 1303 | frame_sizes[0] = block_size - consumed; |
michael@0 | 1304 | break; |
michael@0 | 1305 | case LACING_XIPH: |
michael@0 | 1306 | if (frames == 1) |
michael@0 | 1307 | return -1; |
michael@0 | 1308 | r = ne_read_xiph_lacing(ctx->io, block_size, &consumed, frames, frame_sizes); |
michael@0 | 1309 | if (r != 1) |
michael@0 | 1310 | return r; |
michael@0 | 1311 | break; |
michael@0 | 1312 | case LACING_FIXED: |
michael@0 | 1313 | if ((block_size - consumed) % frames) |
michael@0 | 1314 | return -1; |
michael@0 | 1315 | for (i = 0; i < frames; ++i) |
michael@0 | 1316 | frame_sizes[i] = (block_size - consumed) / frames; |
michael@0 | 1317 | break; |
michael@0 | 1318 | case LACING_EBML: |
michael@0 | 1319 | if (frames == 1) |
michael@0 | 1320 | return -1; |
michael@0 | 1321 | r = ne_read_ebml_lacing(ctx->io, block_size, &consumed, frames, frame_sizes); |
michael@0 | 1322 | if (r != 1) |
michael@0 | 1323 | return r; |
michael@0 | 1324 | break; |
michael@0 | 1325 | } |
michael@0 | 1326 | |
michael@0 | 1327 | /* Sanity check unlaced frame sizes against total block size. */ |
michael@0 | 1328 | total = consumed; |
michael@0 | 1329 | for (i = 0; i < frames; ++i) |
michael@0 | 1330 | total += frame_sizes[i]; |
michael@0 | 1331 | if (total > block_size) |
michael@0 | 1332 | return -1; |
michael@0 | 1333 | |
michael@0 | 1334 | if (ne_map_track_number_to_index(ctx, track_number, &track) != 0) |
michael@0 | 1335 | return -1; |
michael@0 | 1336 | |
michael@0 | 1337 | entry = ne_find_track_entry(ctx, track); |
michael@0 | 1338 | if (!entry) |
michael@0 | 1339 | return -1; |
michael@0 | 1340 | |
michael@0 | 1341 | track_scale = 1.0; |
michael@0 | 1342 | |
michael@0 | 1343 | tc_scale = ne_get_timecode_scale(ctx); |
michael@0 | 1344 | |
michael@0 | 1345 | assert(ctx->segment.cluster.tail->id == ID_CLUSTER); |
michael@0 | 1346 | cluster = ctx->segment.cluster.tail->data; |
michael@0 | 1347 | if (ne_get_uint(cluster->timecode, &cluster_tc) != 0) |
michael@0 | 1348 | return -1; |
michael@0 | 1349 | |
michael@0 | 1350 | abs_timecode = timecode + cluster_tc; |
michael@0 | 1351 | if (abs_timecode < 0) |
michael@0 | 1352 | return -1; |
michael@0 | 1353 | |
michael@0 | 1354 | pkt = ne_alloc(sizeof(*pkt)); |
michael@0 | 1355 | if (!pkt) |
michael@0 | 1356 | return -1; |
michael@0 | 1357 | pkt->track = track; |
michael@0 | 1358 | pkt->timecode = abs_timecode * tc_scale * track_scale; |
michael@0 | 1359 | |
michael@0 | 1360 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "%sblock t %lld pts %f f %llx frames: %llu", |
michael@0 | 1361 | block_id == ID_BLOCK ? "" : "simple", pkt->track, pkt->timecode / 1e9, flags, frames); |
michael@0 | 1362 | |
michael@0 | 1363 | last = NULL; |
michael@0 | 1364 | for (i = 0; i < frames; ++i) { |
michael@0 | 1365 | if (frame_sizes[i] > LIMIT_FRAME) { |
michael@0 | 1366 | nestegg_free_packet(pkt); |
michael@0 | 1367 | return -1; |
michael@0 | 1368 | } |
michael@0 | 1369 | f = ne_alloc(sizeof(*f)); |
michael@0 | 1370 | if (!f) { |
michael@0 | 1371 | nestegg_free_packet(pkt); |
michael@0 | 1372 | return -1; |
michael@0 | 1373 | } |
michael@0 | 1374 | f->data = ne_alloc(frame_sizes[i]); |
michael@0 | 1375 | if (!f->data) { |
michael@0 | 1376 | free(f); |
michael@0 | 1377 | nestegg_free_packet(pkt); |
michael@0 | 1378 | return -1; |
michael@0 | 1379 | } |
michael@0 | 1380 | f->length = frame_sizes[i]; |
michael@0 | 1381 | r = ne_io_read(ctx->io, f->data, frame_sizes[i]); |
michael@0 | 1382 | if (r != 1) { |
michael@0 | 1383 | free(f->data); |
michael@0 | 1384 | free(f); |
michael@0 | 1385 | nestegg_free_packet(pkt); |
michael@0 | 1386 | return -1; |
michael@0 | 1387 | } |
michael@0 | 1388 | |
michael@0 | 1389 | if (!last) |
michael@0 | 1390 | pkt->frame = f; |
michael@0 | 1391 | else |
michael@0 | 1392 | last->next = f; |
michael@0 | 1393 | last = f; |
michael@0 | 1394 | } |
michael@0 | 1395 | |
michael@0 | 1396 | *data = pkt; |
michael@0 | 1397 | |
michael@0 | 1398 | return 1; |
michael@0 | 1399 | } |
michael@0 | 1400 | |
michael@0 | 1401 | static int |
michael@0 | 1402 | ne_read_block_duration(nestegg * ctx, nestegg_packet * pkt) |
michael@0 | 1403 | { |
michael@0 | 1404 | int r; |
michael@0 | 1405 | uint64_t id, size; |
michael@0 | 1406 | struct ebml_element_desc * element; |
michael@0 | 1407 | struct ebml_type * storage; |
michael@0 | 1408 | |
michael@0 | 1409 | r = ne_peek_element(ctx, &id, &size); |
michael@0 | 1410 | if (r != 1) |
michael@0 | 1411 | return r; |
michael@0 | 1412 | |
michael@0 | 1413 | if (id != ID_BLOCK_DURATION) |
michael@0 | 1414 | return 1; |
michael@0 | 1415 | |
michael@0 | 1416 | element = ne_find_element(id, ctx->ancestor->node); |
michael@0 | 1417 | if (!element) |
michael@0 | 1418 | return 1; |
michael@0 | 1419 | |
michael@0 | 1420 | r = ne_read_simple(ctx, element, size); |
michael@0 | 1421 | if (r != 1) |
michael@0 | 1422 | return r; |
michael@0 | 1423 | storage = (struct ebml_type *) (ctx->ancestor->data + element->offset); |
michael@0 | 1424 | pkt->duration = storage->v.i * ne_get_timecode_scale(ctx); |
michael@0 | 1425 | |
michael@0 | 1426 | return 1; |
michael@0 | 1427 | } |
michael@0 | 1428 | |
michael@0 | 1429 | static int |
michael@0 | 1430 | ne_read_discard_padding(nestegg * ctx, nestegg_packet * pkt) |
michael@0 | 1431 | { |
michael@0 | 1432 | int r; |
michael@0 | 1433 | uint64_t id, size; |
michael@0 | 1434 | struct ebml_element_desc * element; |
michael@0 | 1435 | struct ebml_type * storage; |
michael@0 | 1436 | |
michael@0 | 1437 | r = ne_peek_element(ctx, &id, &size); |
michael@0 | 1438 | if (r != 1) |
michael@0 | 1439 | return r; |
michael@0 | 1440 | |
michael@0 | 1441 | if (id != ID_DISCARD_PADDING) |
michael@0 | 1442 | return 1; |
michael@0 | 1443 | |
michael@0 | 1444 | element = ne_find_element(id, ctx->ancestor->node); |
michael@0 | 1445 | if (!element) |
michael@0 | 1446 | return 1; |
michael@0 | 1447 | |
michael@0 | 1448 | r = ne_read_simple(ctx, element, size); |
michael@0 | 1449 | if (r != 1) |
michael@0 | 1450 | return r; |
michael@0 | 1451 | storage = (struct ebml_type *) (ctx->ancestor->data + element->offset); |
michael@0 | 1452 | pkt->discard_padding = storage->v.i; |
michael@0 | 1453 | |
michael@0 | 1454 | return 1; |
michael@0 | 1455 | } |
michael@0 | 1456 | |
michael@0 | 1457 | |
michael@0 | 1458 | static uint64_t |
michael@0 | 1459 | ne_buf_read_id(unsigned char const * p, size_t length) |
michael@0 | 1460 | { |
michael@0 | 1461 | uint64_t id = 0; |
michael@0 | 1462 | |
michael@0 | 1463 | while (length--) { |
michael@0 | 1464 | id <<= 8; |
michael@0 | 1465 | id |= *p++; |
michael@0 | 1466 | } |
michael@0 | 1467 | |
michael@0 | 1468 | return id; |
michael@0 | 1469 | } |
michael@0 | 1470 | |
michael@0 | 1471 | static struct seek * |
michael@0 | 1472 | ne_find_seek_for_id(struct ebml_list_node * seek_head, uint64_t id) |
michael@0 | 1473 | { |
michael@0 | 1474 | struct ebml_list * head; |
michael@0 | 1475 | struct ebml_list_node * seek; |
michael@0 | 1476 | struct ebml_binary binary_id; |
michael@0 | 1477 | struct seek * s; |
michael@0 | 1478 | |
michael@0 | 1479 | while (seek_head) { |
michael@0 | 1480 | assert(seek_head->id == ID_SEEK_HEAD); |
michael@0 | 1481 | head = seek_head->data; |
michael@0 | 1482 | seek = head->head; |
michael@0 | 1483 | |
michael@0 | 1484 | while (seek) { |
michael@0 | 1485 | assert(seek->id == ID_SEEK); |
michael@0 | 1486 | s = seek->data; |
michael@0 | 1487 | |
michael@0 | 1488 | if (ne_get_binary(s->id, &binary_id) == 0 && |
michael@0 | 1489 | ne_buf_read_id(binary_id.data, binary_id.length) == id) |
michael@0 | 1490 | return s; |
michael@0 | 1491 | |
michael@0 | 1492 | seek = seek->next; |
michael@0 | 1493 | } |
michael@0 | 1494 | |
michael@0 | 1495 | seek_head = seek_head->next; |
michael@0 | 1496 | } |
michael@0 | 1497 | |
michael@0 | 1498 | return NULL; |
michael@0 | 1499 | } |
michael@0 | 1500 | |
michael@0 | 1501 | static struct cue_track_positions * |
michael@0 | 1502 | ne_find_cue_position_for_track(nestegg * ctx, struct ebml_list_node * node, unsigned int track) |
michael@0 | 1503 | { |
michael@0 | 1504 | struct cue_track_positions * pos = NULL; |
michael@0 | 1505 | uint64_t track_number; |
michael@0 | 1506 | unsigned int t; |
michael@0 | 1507 | |
michael@0 | 1508 | while (node) { |
michael@0 | 1509 | assert(node->id == ID_CUE_TRACK_POSITIONS); |
michael@0 | 1510 | pos = node->data; |
michael@0 | 1511 | if (ne_get_uint(pos->track, &track_number) != 0) |
michael@0 | 1512 | return NULL; |
michael@0 | 1513 | |
michael@0 | 1514 | if (ne_map_track_number_to_index(ctx, track_number, &t) != 0) |
michael@0 | 1515 | return NULL; |
michael@0 | 1516 | |
michael@0 | 1517 | if (t == track) |
michael@0 | 1518 | return pos; |
michael@0 | 1519 | |
michael@0 | 1520 | node = node->next; |
michael@0 | 1521 | } |
michael@0 | 1522 | |
michael@0 | 1523 | return NULL; |
michael@0 | 1524 | } |
michael@0 | 1525 | |
michael@0 | 1526 | static struct cue_point * |
michael@0 | 1527 | ne_find_cue_point_for_tstamp(nestegg * ctx, struct ebml_list_node * cue_point, unsigned int track, uint64_t scale, uint64_t tstamp) |
michael@0 | 1528 | { |
michael@0 | 1529 | uint64_t time; |
michael@0 | 1530 | struct cue_point * c, * prev = NULL; |
michael@0 | 1531 | |
michael@0 | 1532 | while (cue_point) { |
michael@0 | 1533 | assert(cue_point->id == ID_CUE_POINT); |
michael@0 | 1534 | c = cue_point->data; |
michael@0 | 1535 | |
michael@0 | 1536 | if (!prev) |
michael@0 | 1537 | prev = c; |
michael@0 | 1538 | |
michael@0 | 1539 | if (ne_get_uint(c->time, &time) == 0 && time * scale > tstamp) |
michael@0 | 1540 | break; |
michael@0 | 1541 | |
michael@0 | 1542 | if (ne_find_cue_position_for_track(ctx, c->cue_track_positions.head, track) != NULL) |
michael@0 | 1543 | prev = c; |
michael@0 | 1544 | |
michael@0 | 1545 | cue_point = cue_point->next; |
michael@0 | 1546 | } |
michael@0 | 1547 | |
michael@0 | 1548 | return prev; |
michael@0 | 1549 | } |
michael@0 | 1550 | |
michael@0 | 1551 | static int |
michael@0 | 1552 | ne_is_suspend_element(uint64_t id) |
michael@0 | 1553 | { |
michael@0 | 1554 | if (id == ID_SIMPLE_BLOCK || id == ID_BLOCK) |
michael@0 | 1555 | return 1; |
michael@0 | 1556 | return 0; |
michael@0 | 1557 | } |
michael@0 | 1558 | |
michael@0 | 1559 | static void |
michael@0 | 1560 | ne_null_log_callback(nestegg * ctx, unsigned int severity, char const * fmt, ...) |
michael@0 | 1561 | { |
michael@0 | 1562 | if (ctx && severity && fmt) |
michael@0 | 1563 | return; |
michael@0 | 1564 | } |
michael@0 | 1565 | |
michael@0 | 1566 | static int |
michael@0 | 1567 | ne_init_cue_points(nestegg * ctx, int64_t max_offset) |
michael@0 | 1568 | { |
michael@0 | 1569 | int r; |
michael@0 | 1570 | struct ebml_list_node * node = ctx->segment.cues.cue_point.head; |
michael@0 | 1571 | struct seek * found; |
michael@0 | 1572 | uint64_t seek_pos, id; |
michael@0 | 1573 | struct saved_state state; |
michael@0 | 1574 | |
michael@0 | 1575 | /* If there are no cues loaded, check for cues element in the seek head |
michael@0 | 1576 | and load it. */ |
michael@0 | 1577 | if (!node) { |
michael@0 | 1578 | found = ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); |
michael@0 | 1579 | if (!found) |
michael@0 | 1580 | return -1; |
michael@0 | 1581 | |
michael@0 | 1582 | if (ne_get_uint(found->position, &seek_pos) != 0) |
michael@0 | 1583 | return -1; |
michael@0 | 1584 | |
michael@0 | 1585 | /* Save old parser state. */ |
michael@0 | 1586 | r = ne_ctx_save(ctx, &state); |
michael@0 | 1587 | if (r != 0) |
michael@0 | 1588 | return -1; |
michael@0 | 1589 | |
michael@0 | 1590 | /* Seek and set up parser state for segment-level element (Cues). */ |
michael@0 | 1591 | r = ne_io_seek(ctx->io, ctx->segment_offset + seek_pos, NESTEGG_SEEK_SET); |
michael@0 | 1592 | if (r != 0) |
michael@0 | 1593 | return -1; |
michael@0 | 1594 | ctx->last_valid = 0; |
michael@0 | 1595 | |
michael@0 | 1596 | r = ne_read_element(ctx, &id, NULL); |
michael@0 | 1597 | if (r != 1) |
michael@0 | 1598 | return -1; |
michael@0 | 1599 | |
michael@0 | 1600 | if (id != ID_CUES) |
michael@0 | 1601 | return -1; |
michael@0 | 1602 | |
michael@0 | 1603 | ctx->ancestor = NULL; |
michael@0 | 1604 | if (ne_ctx_push(ctx, ne_top_level_elements, ctx) < 0) |
michael@0 | 1605 | return -1; |
michael@0 | 1606 | if (ne_ctx_push(ctx, ne_segment_elements, &ctx->segment) < 0) |
michael@0 | 1607 | return -1; |
michael@0 | 1608 | if (ne_ctx_push(ctx, ne_cues_elements, &ctx->segment.cues) < 0) |
michael@0 | 1609 | return -1; |
michael@0 | 1610 | /* parser will run until end of cues element. */ |
michael@0 | 1611 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cue elements"); |
michael@0 | 1612 | r = ne_parse(ctx, ne_cues_elements, max_offset); |
michael@0 | 1613 | while (ctx->ancestor) |
michael@0 | 1614 | ne_ctx_pop(ctx); |
michael@0 | 1615 | |
michael@0 | 1616 | /* Reset parser state to original state and seek back to old position. */ |
michael@0 | 1617 | if (ne_ctx_restore(ctx, &state) != 0) |
michael@0 | 1618 | return -1; |
michael@0 | 1619 | |
michael@0 | 1620 | if (r < 0) |
michael@0 | 1621 | return -1; |
michael@0 | 1622 | |
michael@0 | 1623 | node = ctx->segment.cues.cue_point.head; |
michael@0 | 1624 | if (!node) |
michael@0 | 1625 | return -1; |
michael@0 | 1626 | } |
michael@0 | 1627 | |
michael@0 | 1628 | return 0; |
michael@0 | 1629 | } |
michael@0 | 1630 | |
michael@0 | 1631 | /* Three functions that implement the nestegg_io interface, operating on a |
michael@0 | 1632 | * sniff_buffer. */ |
michael@0 | 1633 | struct sniff_buffer { |
michael@0 | 1634 | unsigned char const * buffer; |
michael@0 | 1635 | size_t length; |
michael@0 | 1636 | int64_t offset; |
michael@0 | 1637 | }; |
michael@0 | 1638 | |
michael@0 | 1639 | static int |
michael@0 | 1640 | ne_buffer_read(void * buffer, size_t length, void * user_data) |
michael@0 | 1641 | { |
michael@0 | 1642 | struct sniff_buffer * sb = user_data; |
michael@0 | 1643 | |
michael@0 | 1644 | int rv = 1; |
michael@0 | 1645 | size_t available = sb->length - sb->offset; |
michael@0 | 1646 | |
michael@0 | 1647 | if (available < length) |
michael@0 | 1648 | return 0; |
michael@0 | 1649 | |
michael@0 | 1650 | memcpy(buffer, sb->buffer + sb->offset, length); |
michael@0 | 1651 | sb->offset += length; |
michael@0 | 1652 | |
michael@0 | 1653 | return rv; |
michael@0 | 1654 | } |
michael@0 | 1655 | |
michael@0 | 1656 | static int |
michael@0 | 1657 | ne_buffer_seek(int64_t offset, int whence, void * user_data) |
michael@0 | 1658 | { |
michael@0 | 1659 | struct sniff_buffer * sb = user_data; |
michael@0 | 1660 | int64_t o = sb->offset; |
michael@0 | 1661 | |
michael@0 | 1662 | switch(whence) { |
michael@0 | 1663 | case NESTEGG_SEEK_SET: |
michael@0 | 1664 | o = offset; |
michael@0 | 1665 | break; |
michael@0 | 1666 | case NESTEGG_SEEK_CUR: |
michael@0 | 1667 | o += offset; |
michael@0 | 1668 | break; |
michael@0 | 1669 | case NESTEGG_SEEK_END: |
michael@0 | 1670 | o = sb->length + offset; |
michael@0 | 1671 | break; |
michael@0 | 1672 | } |
michael@0 | 1673 | |
michael@0 | 1674 | if (o < 0 || o > (int64_t) sb->length) |
michael@0 | 1675 | return -1; |
michael@0 | 1676 | |
michael@0 | 1677 | sb->offset = o; |
michael@0 | 1678 | return 0; |
michael@0 | 1679 | } |
michael@0 | 1680 | |
michael@0 | 1681 | static int64_t |
michael@0 | 1682 | ne_buffer_tell(void * user_data) |
michael@0 | 1683 | { |
michael@0 | 1684 | struct sniff_buffer * sb = user_data; |
michael@0 | 1685 | return sb->offset; |
michael@0 | 1686 | } |
michael@0 | 1687 | |
michael@0 | 1688 | static int |
michael@0 | 1689 | ne_match_webm(nestegg_io io, int64_t max_offset) |
michael@0 | 1690 | { |
michael@0 | 1691 | int r; |
michael@0 | 1692 | uint64_t id; |
michael@0 | 1693 | char * doctype; |
michael@0 | 1694 | nestegg * ctx; |
michael@0 | 1695 | |
michael@0 | 1696 | if (!(io.read && io.seek && io.tell)) |
michael@0 | 1697 | return -1; |
michael@0 | 1698 | |
michael@0 | 1699 | ctx = ne_alloc(sizeof(*ctx)); |
michael@0 | 1700 | if (!ctx) |
michael@0 | 1701 | return -1; |
michael@0 | 1702 | |
michael@0 | 1703 | ctx->io = ne_alloc(sizeof(*ctx->io)); |
michael@0 | 1704 | if (!ctx->io) { |
michael@0 | 1705 | nestegg_destroy(ctx); |
michael@0 | 1706 | return -1; |
michael@0 | 1707 | } |
michael@0 | 1708 | *ctx->io = io; |
michael@0 | 1709 | ctx->alloc_pool = ne_pool_init(); |
michael@0 | 1710 | if (!ctx->alloc_pool) { |
michael@0 | 1711 | nestegg_destroy(ctx); |
michael@0 | 1712 | return -1; |
michael@0 | 1713 | } |
michael@0 | 1714 | ctx->log = ne_null_log_callback; |
michael@0 | 1715 | |
michael@0 | 1716 | r = ne_peek_element(ctx, &id, NULL); |
michael@0 | 1717 | if (r != 1) { |
michael@0 | 1718 | nestegg_destroy(ctx); |
michael@0 | 1719 | return 0; |
michael@0 | 1720 | } |
michael@0 | 1721 | |
michael@0 | 1722 | if (id != ID_EBML) { |
michael@0 | 1723 | nestegg_destroy(ctx); |
michael@0 | 1724 | return 0; |
michael@0 | 1725 | } |
michael@0 | 1726 | |
michael@0 | 1727 | ne_ctx_push(ctx, ne_top_level_elements, ctx); |
michael@0 | 1728 | |
michael@0 | 1729 | /* we don't check the return value of ne_parse, that might fail because |
michael@0 | 1730 | * max_offset is not on a valid element end point. We only want to check |
michael@0 | 1731 | * the EBML ID and that the doctype is "webm". */ |
michael@0 | 1732 | ne_parse(ctx, NULL, max_offset); |
michael@0 | 1733 | |
michael@0 | 1734 | if (ne_get_string(ctx->ebml.doctype, &doctype) != 0 || |
michael@0 | 1735 | strcmp(doctype, "webm") != 0) { |
michael@0 | 1736 | nestegg_destroy(ctx); |
michael@0 | 1737 | return 0; |
michael@0 | 1738 | } |
michael@0 | 1739 | |
michael@0 | 1740 | nestegg_destroy(ctx); |
michael@0 | 1741 | |
michael@0 | 1742 | return 1; |
michael@0 | 1743 | } |
michael@0 | 1744 | |
michael@0 | 1745 | int |
michael@0 | 1746 | nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t max_offset) |
michael@0 | 1747 | { |
michael@0 | 1748 | int r; |
michael@0 | 1749 | uint64_t id, version, docversion; |
michael@0 | 1750 | struct ebml_list_node * track; |
michael@0 | 1751 | char * doctype; |
michael@0 | 1752 | nestegg * ctx; |
michael@0 | 1753 | |
michael@0 | 1754 | if (!(io.read && io.seek && io.tell)) |
michael@0 | 1755 | return -1; |
michael@0 | 1756 | |
michael@0 | 1757 | ctx = ne_alloc(sizeof(*ctx)); |
michael@0 | 1758 | if (!ctx) |
michael@0 | 1759 | return -1; |
michael@0 | 1760 | |
michael@0 | 1761 | ctx->io = ne_alloc(sizeof(*ctx->io)); |
michael@0 | 1762 | if (!ctx->io) { |
michael@0 | 1763 | nestegg_destroy(ctx); |
michael@0 | 1764 | return -1; |
michael@0 | 1765 | } |
michael@0 | 1766 | *ctx->io = io; |
michael@0 | 1767 | ctx->log = callback; |
michael@0 | 1768 | ctx->alloc_pool = ne_pool_init(); |
michael@0 | 1769 | if (!ctx->alloc_pool) { |
michael@0 | 1770 | nestegg_destroy(ctx); |
michael@0 | 1771 | return -1; |
michael@0 | 1772 | } |
michael@0 | 1773 | |
michael@0 | 1774 | if (!ctx->log) |
michael@0 | 1775 | ctx->log = ne_null_log_callback; |
michael@0 | 1776 | |
michael@0 | 1777 | r = ne_peek_element(ctx, &id, NULL); |
michael@0 | 1778 | if (r != 1) { |
michael@0 | 1779 | nestegg_destroy(ctx); |
michael@0 | 1780 | return -1; |
michael@0 | 1781 | } |
michael@0 | 1782 | |
michael@0 | 1783 | if (id != ID_EBML) { |
michael@0 | 1784 | nestegg_destroy(ctx); |
michael@0 | 1785 | return -1; |
michael@0 | 1786 | } |
michael@0 | 1787 | |
michael@0 | 1788 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "ctx %p", ctx); |
michael@0 | 1789 | |
michael@0 | 1790 | ne_ctx_push(ctx, ne_top_level_elements, ctx); |
michael@0 | 1791 | |
michael@0 | 1792 | r = ne_parse(ctx, NULL, max_offset); |
michael@0 | 1793 | |
michael@0 | 1794 | if (r != 1) { |
michael@0 | 1795 | nestegg_destroy(ctx); |
michael@0 | 1796 | return -1; |
michael@0 | 1797 | } |
michael@0 | 1798 | |
michael@0 | 1799 | if (ne_get_uint(ctx->ebml.ebml_read_version, &version) != 0) |
michael@0 | 1800 | version = 1; |
michael@0 | 1801 | if (version != 1) { |
michael@0 | 1802 | nestegg_destroy(ctx); |
michael@0 | 1803 | return -1; |
michael@0 | 1804 | } |
michael@0 | 1805 | |
michael@0 | 1806 | if (ne_get_string(ctx->ebml.doctype, &doctype) != 0) |
michael@0 | 1807 | doctype = "matroska"; |
michael@0 | 1808 | if (strcmp(doctype, "webm") != 0) { |
michael@0 | 1809 | nestegg_destroy(ctx); |
michael@0 | 1810 | return -1; |
michael@0 | 1811 | } |
michael@0 | 1812 | |
michael@0 | 1813 | if (ne_get_uint(ctx->ebml.doctype_read_version, &docversion) != 0) |
michael@0 | 1814 | docversion = 1; |
michael@0 | 1815 | if (docversion < 1 || docversion > 2) { |
michael@0 | 1816 | nestegg_destroy(ctx); |
michael@0 | 1817 | return -1; |
michael@0 | 1818 | } |
michael@0 | 1819 | |
michael@0 | 1820 | if (!ctx->segment.tracks.track_entry.head) { |
michael@0 | 1821 | nestegg_destroy(ctx); |
michael@0 | 1822 | return -1; |
michael@0 | 1823 | } |
michael@0 | 1824 | |
michael@0 | 1825 | track = ctx->segment.tracks.track_entry.head; |
michael@0 | 1826 | ctx->track_count = 0; |
michael@0 | 1827 | |
michael@0 | 1828 | while (track) { |
michael@0 | 1829 | ctx->track_count += 1; |
michael@0 | 1830 | track = track->next; |
michael@0 | 1831 | } |
michael@0 | 1832 | |
michael@0 | 1833 | *context = ctx; |
michael@0 | 1834 | |
michael@0 | 1835 | return 0; |
michael@0 | 1836 | } |
michael@0 | 1837 | |
michael@0 | 1838 | void |
michael@0 | 1839 | nestegg_destroy(nestegg * ctx) |
michael@0 | 1840 | { |
michael@0 | 1841 | while (ctx->ancestor) |
michael@0 | 1842 | ne_ctx_pop(ctx); |
michael@0 | 1843 | ne_pool_destroy(ctx->alloc_pool); |
michael@0 | 1844 | free(ctx->io); |
michael@0 | 1845 | free(ctx); |
michael@0 | 1846 | } |
michael@0 | 1847 | |
michael@0 | 1848 | int |
michael@0 | 1849 | nestegg_duration(nestegg * ctx, uint64_t * duration) |
michael@0 | 1850 | { |
michael@0 | 1851 | uint64_t tc_scale; |
michael@0 | 1852 | double unscaled_duration; |
michael@0 | 1853 | |
michael@0 | 1854 | if (ne_get_float(ctx->segment.info.duration, &unscaled_duration) != 0) |
michael@0 | 1855 | return -1; |
michael@0 | 1856 | |
michael@0 | 1857 | tc_scale = ne_get_timecode_scale(ctx); |
michael@0 | 1858 | |
michael@0 | 1859 | *duration = (uint64_t) (unscaled_duration * tc_scale); |
michael@0 | 1860 | return 0; |
michael@0 | 1861 | } |
michael@0 | 1862 | |
michael@0 | 1863 | int |
michael@0 | 1864 | nestegg_tstamp_scale(nestegg * ctx, uint64_t * scale) |
michael@0 | 1865 | { |
michael@0 | 1866 | *scale = ne_get_timecode_scale(ctx); |
michael@0 | 1867 | return 0; |
michael@0 | 1868 | } |
michael@0 | 1869 | |
michael@0 | 1870 | int |
michael@0 | 1871 | nestegg_track_count(nestegg * ctx, unsigned int * tracks) |
michael@0 | 1872 | { |
michael@0 | 1873 | *tracks = ctx->track_count; |
michael@0 | 1874 | return 0; |
michael@0 | 1875 | } |
michael@0 | 1876 | |
michael@0 | 1877 | int |
michael@0 | 1878 | nestegg_get_cue_point(nestegg * ctx, unsigned int cluster_num, int64_t max_offset, |
michael@0 | 1879 | int64_t * start_pos, int64_t * end_pos, uint64_t * tstamp) |
michael@0 | 1880 | { |
michael@0 | 1881 | int range_obtained = 0; |
michael@0 | 1882 | unsigned int cluster_count = 0; |
michael@0 | 1883 | struct cue_point * cue_point; |
michael@0 | 1884 | struct cue_track_positions * pos; |
michael@0 | 1885 | uint64_t seek_pos, track_number, tc_scale, time; |
michael@0 | 1886 | struct ebml_list_node * cues_node = ctx->segment.cues.cue_point.head; |
michael@0 | 1887 | struct ebml_list_node * cue_pos_node = NULL; |
michael@0 | 1888 | unsigned int track = 0, track_count = 0, track_index; |
michael@0 | 1889 | |
michael@0 | 1890 | if (!start_pos || !end_pos || !tstamp) |
michael@0 | 1891 | return -1; |
michael@0 | 1892 | |
michael@0 | 1893 | /* Initialise return values */ |
michael@0 | 1894 | *start_pos = -1; |
michael@0 | 1895 | *end_pos = -1; |
michael@0 | 1896 | *tstamp = 0; |
michael@0 | 1897 | |
michael@0 | 1898 | if (!cues_node) { |
michael@0 | 1899 | ne_init_cue_points(ctx, max_offset); |
michael@0 | 1900 | cues_node = ctx->segment.cues.cue_point.head; |
michael@0 | 1901 | /* Verify cues have been added to context. */ |
michael@0 | 1902 | if (!cues_node) |
michael@0 | 1903 | return -1; |
michael@0 | 1904 | } |
michael@0 | 1905 | |
michael@0 | 1906 | nestegg_track_count(ctx, &track_count); |
michael@0 | 1907 | |
michael@0 | 1908 | tc_scale = ne_get_timecode_scale(ctx); |
michael@0 | 1909 | |
michael@0 | 1910 | while (cues_node && !range_obtained) { |
michael@0 | 1911 | assert(cues_node->id == ID_CUE_POINT); |
michael@0 | 1912 | cue_point = cues_node->data; |
michael@0 | 1913 | cue_pos_node = cue_point->cue_track_positions.head; |
michael@0 | 1914 | while (cue_pos_node) { |
michael@0 | 1915 | assert(cue_pos_node->id == ID_CUE_TRACK_POSITIONS); |
michael@0 | 1916 | pos = cue_pos_node->data; |
michael@0 | 1917 | for (track = 0; track < track_count; track++) { |
michael@0 | 1918 | if (ne_get_uint(pos->track, &track_number) != 0) |
michael@0 | 1919 | return -1; |
michael@0 | 1920 | |
michael@0 | 1921 | if (ne_map_track_number_to_index(ctx, track_number, &track_index) != 0) |
michael@0 | 1922 | return -1; |
michael@0 | 1923 | |
michael@0 | 1924 | if (track_index == track) { |
michael@0 | 1925 | if (ne_get_uint(pos->cluster_position, &seek_pos) != 0) |
michael@0 | 1926 | return -1; |
michael@0 | 1927 | if (cluster_count == cluster_num) { |
michael@0 | 1928 | *start_pos = ctx->segment_offset+seek_pos; |
michael@0 | 1929 | if (ne_get_uint(cue_point->time, &time) != 0) |
michael@0 | 1930 | return -1; |
michael@0 | 1931 | *tstamp = time * tc_scale; |
michael@0 | 1932 | } else if (cluster_count == cluster_num+1) { |
michael@0 | 1933 | *end_pos = (ctx->segment_offset+seek_pos)-1; |
michael@0 | 1934 | range_obtained = 1; |
michael@0 | 1935 | break; |
michael@0 | 1936 | } |
michael@0 | 1937 | cluster_count++; |
michael@0 | 1938 | } |
michael@0 | 1939 | } |
michael@0 | 1940 | cue_pos_node = cue_pos_node->next; |
michael@0 | 1941 | } |
michael@0 | 1942 | cues_node = cues_node->next; |
michael@0 | 1943 | } |
michael@0 | 1944 | |
michael@0 | 1945 | return 0; |
michael@0 | 1946 | } |
michael@0 | 1947 | |
michael@0 | 1948 | int |
michael@0 | 1949 | nestegg_offset_seek(nestegg * ctx, uint64_t offset) |
michael@0 | 1950 | { |
michael@0 | 1951 | int r; |
michael@0 | 1952 | |
michael@0 | 1953 | if (offset > INT64_MAX) |
michael@0 | 1954 | return -1; |
michael@0 | 1955 | |
michael@0 | 1956 | /* Seek and set up parser state for segment-level element (Cluster). */ |
michael@0 | 1957 | r = ne_io_seek(ctx->io, offset, NESTEGG_SEEK_SET); |
michael@0 | 1958 | if (r != 0) |
michael@0 | 1959 | return -1; |
michael@0 | 1960 | ctx->last_valid = 0; |
michael@0 | 1961 | |
michael@0 | 1962 | while (ctx->ancestor) |
michael@0 | 1963 | ne_ctx_pop(ctx); |
michael@0 | 1964 | |
michael@0 | 1965 | ne_ctx_push(ctx, ne_top_level_elements, ctx); |
michael@0 | 1966 | ne_ctx_push(ctx, ne_segment_elements, &ctx->segment); |
michael@0 | 1967 | |
michael@0 | 1968 | ctx->log(ctx, NESTEGG_LOG_DEBUG, "seek: parsing cluster elements"); |
michael@0 | 1969 | r = ne_parse(ctx, NULL, -1); |
michael@0 | 1970 | if (r != 1) |
michael@0 | 1971 | return -1; |
michael@0 | 1972 | |
michael@0 | 1973 | return 0; |
michael@0 | 1974 | } |
michael@0 | 1975 | |
michael@0 | 1976 | int |
michael@0 | 1977 | nestegg_track_seek(nestegg * ctx, unsigned int track, uint64_t tstamp) |
michael@0 | 1978 | { |
michael@0 | 1979 | int r; |
michael@0 | 1980 | struct cue_point * cue_point; |
michael@0 | 1981 | struct cue_track_positions * pos; |
michael@0 | 1982 | uint64_t seek_pos, tc_scale; |
michael@0 | 1983 | |
michael@0 | 1984 | /* If there are no cues loaded, check for cues element in the seek head |
michael@0 | 1985 | and load it. */ |
michael@0 | 1986 | if (!ctx->segment.cues.cue_point.head) { |
michael@0 | 1987 | r = ne_init_cue_points(ctx, -1); |
michael@0 | 1988 | if (r != 0) |
michael@0 | 1989 | return -1; |
michael@0 | 1990 | } |
michael@0 | 1991 | |
michael@0 | 1992 | tc_scale = ne_get_timecode_scale(ctx); |
michael@0 | 1993 | |
michael@0 | 1994 | cue_point = ne_find_cue_point_for_tstamp(ctx, ctx->segment.cues.cue_point.head, |
michael@0 | 1995 | track, tc_scale, tstamp); |
michael@0 | 1996 | if (!cue_point) |
michael@0 | 1997 | return -1; |
michael@0 | 1998 | |
michael@0 | 1999 | pos = ne_find_cue_position_for_track(ctx, cue_point->cue_track_positions.head, track); |
michael@0 | 2000 | if (pos == NULL) |
michael@0 | 2001 | return -1; |
michael@0 | 2002 | |
michael@0 | 2003 | if (ne_get_uint(pos->cluster_position, &seek_pos) != 0) |
michael@0 | 2004 | return -1; |
michael@0 | 2005 | |
michael@0 | 2006 | /* Seek and set up parser state for segment-level element (Cluster). */ |
michael@0 | 2007 | r = nestegg_offset_seek(ctx, ctx->segment_offset + seek_pos); |
michael@0 | 2008 | |
michael@0 | 2009 | if (!ne_is_suspend_element(ctx->last_id)) |
michael@0 | 2010 | return -1; |
michael@0 | 2011 | |
michael@0 | 2012 | return 0; |
michael@0 | 2013 | } |
michael@0 | 2014 | |
michael@0 | 2015 | int |
michael@0 | 2016 | nestegg_track_type(nestegg * ctx, unsigned int track) |
michael@0 | 2017 | { |
michael@0 | 2018 | struct track_entry * entry; |
michael@0 | 2019 | uint64_t type; |
michael@0 | 2020 | |
michael@0 | 2021 | entry = ne_find_track_entry(ctx, track); |
michael@0 | 2022 | if (!entry) |
michael@0 | 2023 | return -1; |
michael@0 | 2024 | |
michael@0 | 2025 | if (ne_get_uint(entry->type, &type) != 0) |
michael@0 | 2026 | return -1; |
michael@0 | 2027 | |
michael@0 | 2028 | if (type & TRACK_TYPE_VIDEO) |
michael@0 | 2029 | return NESTEGG_TRACK_VIDEO; |
michael@0 | 2030 | |
michael@0 | 2031 | if (type & TRACK_TYPE_AUDIO) |
michael@0 | 2032 | return NESTEGG_TRACK_AUDIO; |
michael@0 | 2033 | |
michael@0 | 2034 | return -1; |
michael@0 | 2035 | } |
michael@0 | 2036 | |
michael@0 | 2037 | int |
michael@0 | 2038 | nestegg_track_codec_id(nestegg * ctx, unsigned int track) |
michael@0 | 2039 | { |
michael@0 | 2040 | char * codec_id; |
michael@0 | 2041 | struct track_entry * entry; |
michael@0 | 2042 | |
michael@0 | 2043 | entry = ne_find_track_entry(ctx, track); |
michael@0 | 2044 | if (!entry) |
michael@0 | 2045 | return -1; |
michael@0 | 2046 | |
michael@0 | 2047 | if (ne_get_string(entry->codec_id, &codec_id) != 0) |
michael@0 | 2048 | return -1; |
michael@0 | 2049 | |
michael@0 | 2050 | if (strcmp(codec_id, TRACK_ID_VP8) == 0) |
michael@0 | 2051 | return NESTEGG_CODEC_VP8; |
michael@0 | 2052 | |
michael@0 | 2053 | if (strcmp(codec_id, TRACK_ID_VP9) == 0) |
michael@0 | 2054 | return NESTEGG_CODEC_VP9; |
michael@0 | 2055 | |
michael@0 | 2056 | if (strcmp(codec_id, TRACK_ID_VORBIS) == 0) |
michael@0 | 2057 | return NESTEGG_CODEC_VORBIS; |
michael@0 | 2058 | |
michael@0 | 2059 | if (strcmp(codec_id, TRACK_ID_OPUS) == 0) |
michael@0 | 2060 | return NESTEGG_CODEC_OPUS; |
michael@0 | 2061 | |
michael@0 | 2062 | return -1; |
michael@0 | 2063 | } |
michael@0 | 2064 | |
michael@0 | 2065 | int |
michael@0 | 2066 | nestegg_track_codec_data_count(nestegg * ctx, unsigned int track, |
michael@0 | 2067 | unsigned int * count) |
michael@0 | 2068 | { |
michael@0 | 2069 | struct track_entry * entry; |
michael@0 | 2070 | struct ebml_binary codec_private; |
michael@0 | 2071 | unsigned char * p; |
michael@0 | 2072 | |
michael@0 | 2073 | *count = 0; |
michael@0 | 2074 | |
michael@0 | 2075 | entry = ne_find_track_entry(ctx, track); |
michael@0 | 2076 | if (!entry) |
michael@0 | 2077 | return -1; |
michael@0 | 2078 | |
michael@0 | 2079 | if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS) |
michael@0 | 2080 | return -1; |
michael@0 | 2081 | |
michael@0 | 2082 | if (ne_get_binary(entry->codec_private, &codec_private) != 0) |
michael@0 | 2083 | return -1; |
michael@0 | 2084 | |
michael@0 | 2085 | if (codec_private.length < 1) |
michael@0 | 2086 | return -1; |
michael@0 | 2087 | |
michael@0 | 2088 | p = codec_private.data; |
michael@0 | 2089 | *count = *p + 1; |
michael@0 | 2090 | |
michael@0 | 2091 | if (*count > 3) |
michael@0 | 2092 | return -1; |
michael@0 | 2093 | |
michael@0 | 2094 | return 0; |
michael@0 | 2095 | } |
michael@0 | 2096 | |
michael@0 | 2097 | int |
michael@0 | 2098 | nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item, |
michael@0 | 2099 | unsigned char ** data, size_t * length) |
michael@0 | 2100 | { |
michael@0 | 2101 | struct track_entry * entry; |
michael@0 | 2102 | struct ebml_binary codec_private; |
michael@0 | 2103 | uint64_t sizes[3], total; |
michael@0 | 2104 | unsigned char * p; |
michael@0 | 2105 | unsigned int count, i; |
michael@0 | 2106 | |
michael@0 | 2107 | *data = NULL; |
michael@0 | 2108 | *length = 0; |
michael@0 | 2109 | |
michael@0 | 2110 | entry = ne_find_track_entry(ctx, track); |
michael@0 | 2111 | if (!entry) |
michael@0 | 2112 | return -1; |
michael@0 | 2113 | |
michael@0 | 2114 | if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS |
michael@0 | 2115 | && nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) |
michael@0 | 2116 | return -1; |
michael@0 | 2117 | |
michael@0 | 2118 | if (ne_get_binary(entry->codec_private, &codec_private) != 0) |
michael@0 | 2119 | return -1; |
michael@0 | 2120 | |
michael@0 | 2121 | if (nestegg_track_codec_id(ctx, track) == NESTEGG_CODEC_VORBIS) { |
michael@0 | 2122 | p = codec_private.data; |
michael@0 | 2123 | count = *p++ + 1; |
michael@0 | 2124 | |
michael@0 | 2125 | if (count > 3) |
michael@0 | 2126 | return -1; |
michael@0 | 2127 | |
michael@0 | 2128 | i = 0; |
michael@0 | 2129 | total = 0; |
michael@0 | 2130 | while (--count) { |
michael@0 | 2131 | sizes[i] = ne_xiph_lace_value(&p); |
michael@0 | 2132 | total += sizes[i]; |
michael@0 | 2133 | i += 1; |
michael@0 | 2134 | } |
michael@0 | 2135 | sizes[i] = codec_private.length - total - (p - codec_private.data); |
michael@0 | 2136 | |
michael@0 | 2137 | for (i = 0; i < item; ++i) { |
michael@0 | 2138 | if (sizes[i] > LIMIT_FRAME) |
michael@0 | 2139 | return -1; |
michael@0 | 2140 | p += sizes[i]; |
michael@0 | 2141 | } |
michael@0 | 2142 | *data = p; |
michael@0 | 2143 | *length = sizes[item]; |
michael@0 | 2144 | } else { |
michael@0 | 2145 | *data = codec_private.data; |
michael@0 | 2146 | *length = codec_private.length; |
michael@0 | 2147 | } |
michael@0 | 2148 | |
michael@0 | 2149 | return 0; |
michael@0 | 2150 | } |
michael@0 | 2151 | |
michael@0 | 2152 | int |
michael@0 | 2153 | nestegg_track_video_params(nestegg * ctx, unsigned int track, |
michael@0 | 2154 | nestegg_video_params * params) |
michael@0 | 2155 | { |
michael@0 | 2156 | struct track_entry * entry; |
michael@0 | 2157 | uint64_t value; |
michael@0 | 2158 | |
michael@0 | 2159 | memset(params, 0, sizeof(*params)); |
michael@0 | 2160 | |
michael@0 | 2161 | entry = ne_find_track_entry(ctx, track); |
michael@0 | 2162 | if (!entry) |
michael@0 | 2163 | return -1; |
michael@0 | 2164 | |
michael@0 | 2165 | if (nestegg_track_type(ctx, track) != NESTEGG_TRACK_VIDEO) |
michael@0 | 2166 | return -1; |
michael@0 | 2167 | |
michael@0 | 2168 | value = 0; |
michael@0 | 2169 | ne_get_uint(entry->video.stereo_mode, &value); |
michael@0 | 2170 | if (value <= NESTEGG_VIDEO_STEREO_TOP_BOTTOM || |
michael@0 | 2171 | value == NESTEGG_VIDEO_STEREO_RIGHT_LEFT) |
michael@0 | 2172 | params->stereo_mode = value; |
michael@0 | 2173 | |
michael@0 | 2174 | if (ne_get_uint(entry->video.pixel_width, &value) != 0) |
michael@0 | 2175 | return -1; |
michael@0 | 2176 | params->width = value; |
michael@0 | 2177 | |
michael@0 | 2178 | if (ne_get_uint(entry->video.pixel_height, &value) != 0) |
michael@0 | 2179 | return -1; |
michael@0 | 2180 | params->height = value; |
michael@0 | 2181 | |
michael@0 | 2182 | value = 0; |
michael@0 | 2183 | ne_get_uint(entry->video.pixel_crop_bottom, &value); |
michael@0 | 2184 | params->crop_bottom = value; |
michael@0 | 2185 | |
michael@0 | 2186 | value = 0; |
michael@0 | 2187 | ne_get_uint(entry->video.pixel_crop_top, &value); |
michael@0 | 2188 | params->crop_top = value; |
michael@0 | 2189 | |
michael@0 | 2190 | value = 0; |
michael@0 | 2191 | ne_get_uint(entry->video.pixel_crop_left, &value); |
michael@0 | 2192 | params->crop_left = value; |
michael@0 | 2193 | |
michael@0 | 2194 | value = 0; |
michael@0 | 2195 | ne_get_uint(entry->video.pixel_crop_right, &value); |
michael@0 | 2196 | params->crop_right = value; |
michael@0 | 2197 | |
michael@0 | 2198 | value = params->width; |
michael@0 | 2199 | ne_get_uint(entry->video.display_width, &value); |
michael@0 | 2200 | params->display_width = value; |
michael@0 | 2201 | |
michael@0 | 2202 | value = params->height; |
michael@0 | 2203 | ne_get_uint(entry->video.display_height, &value); |
michael@0 | 2204 | params->display_height = value; |
michael@0 | 2205 | |
michael@0 | 2206 | return 0; |
michael@0 | 2207 | } |
michael@0 | 2208 | |
michael@0 | 2209 | int |
michael@0 | 2210 | nestegg_track_audio_params(nestegg * ctx, unsigned int track, |
michael@0 | 2211 | nestegg_audio_params * params) |
michael@0 | 2212 | { |
michael@0 | 2213 | struct track_entry * entry; |
michael@0 | 2214 | uint64_t value; |
michael@0 | 2215 | |
michael@0 | 2216 | memset(params, 0, sizeof(*params)); |
michael@0 | 2217 | |
michael@0 | 2218 | entry = ne_find_track_entry(ctx, track); |
michael@0 | 2219 | if (!entry) |
michael@0 | 2220 | return -1; |
michael@0 | 2221 | |
michael@0 | 2222 | if (nestegg_track_type(ctx, track) != NESTEGG_TRACK_AUDIO) |
michael@0 | 2223 | return -1; |
michael@0 | 2224 | |
michael@0 | 2225 | params->rate = 8000; |
michael@0 | 2226 | ne_get_float(entry->audio.sampling_frequency, ¶ms->rate); |
michael@0 | 2227 | |
michael@0 | 2228 | value = 1; |
michael@0 | 2229 | ne_get_uint(entry->audio.channels, &value); |
michael@0 | 2230 | params->channels = value; |
michael@0 | 2231 | |
michael@0 | 2232 | value = 16; |
michael@0 | 2233 | ne_get_uint(entry->audio.bit_depth, &value); |
michael@0 | 2234 | params->depth = value; |
michael@0 | 2235 | |
michael@0 | 2236 | value = 0; |
michael@0 | 2237 | ne_get_uint(entry->codec_delay, &value); |
michael@0 | 2238 | params->codec_delay = value; |
michael@0 | 2239 | |
michael@0 | 2240 | value = 0; |
michael@0 | 2241 | ne_get_uint(entry->seek_preroll, &value); |
michael@0 | 2242 | params->seek_preroll = value; |
michael@0 | 2243 | |
michael@0 | 2244 | return 0; |
michael@0 | 2245 | } |
michael@0 | 2246 | |
michael@0 | 2247 | int |
michael@0 | 2248 | nestegg_track_default_duration(nestegg * ctx, unsigned int track, |
michael@0 | 2249 | uint64_t * duration) |
michael@0 | 2250 | { |
michael@0 | 2251 | struct track_entry * entry; |
michael@0 | 2252 | uint64_t value; |
michael@0 | 2253 | |
michael@0 | 2254 | entry = ne_find_track_entry(ctx, track); |
michael@0 | 2255 | if (!entry) |
michael@0 | 2256 | return -1; |
michael@0 | 2257 | |
michael@0 | 2258 | if (ne_get_uint(entry->default_duration, &value) != 0) |
michael@0 | 2259 | return -1; |
michael@0 | 2260 | *duration = value; |
michael@0 | 2261 | |
michael@0 | 2262 | return 0; |
michael@0 | 2263 | } |
michael@0 | 2264 | |
michael@0 | 2265 | int |
michael@0 | 2266 | nestegg_read_packet(nestegg * ctx, nestegg_packet ** pkt) |
michael@0 | 2267 | { |
michael@0 | 2268 | int r; |
michael@0 | 2269 | uint64_t id, size; |
michael@0 | 2270 | |
michael@0 | 2271 | *pkt = NULL; |
michael@0 | 2272 | |
michael@0 | 2273 | for (;;) { |
michael@0 | 2274 | r = ne_peek_element(ctx, &id, &size); |
michael@0 | 2275 | if (r != 1) |
michael@0 | 2276 | return r; |
michael@0 | 2277 | |
michael@0 | 2278 | /* Any DESC_FLAG_SUSPEND fields must be handled here. */ |
michael@0 | 2279 | if (ne_is_suspend_element(id)) { |
michael@0 | 2280 | r = ne_read_element(ctx, &id, &size); |
michael@0 | 2281 | if (r != 1) |
michael@0 | 2282 | return r; |
michael@0 | 2283 | |
michael@0 | 2284 | /* The only DESC_FLAG_SUSPEND fields are Blocks and SimpleBlocks, which we |
michael@0 | 2285 | handle directly. */ |
michael@0 | 2286 | r = ne_read_block(ctx, id, size, pkt); |
michael@0 | 2287 | if (r != 1) |
michael@0 | 2288 | return r; |
michael@0 | 2289 | |
michael@0 | 2290 | r = ne_read_block_duration(ctx, *pkt); |
michael@0 | 2291 | if (r != 1) |
michael@0 | 2292 | return r; |
michael@0 | 2293 | |
michael@0 | 2294 | r = ne_read_discard_padding(ctx, *pkt); |
michael@0 | 2295 | if (r != 1) |
michael@0 | 2296 | return r; |
michael@0 | 2297 | |
michael@0 | 2298 | return r; |
michael@0 | 2299 | } |
michael@0 | 2300 | |
michael@0 | 2301 | r = ne_parse(ctx, NULL, -1); |
michael@0 | 2302 | if (r != 1) |
michael@0 | 2303 | return r; |
michael@0 | 2304 | } |
michael@0 | 2305 | |
michael@0 | 2306 | return 1; |
michael@0 | 2307 | } |
michael@0 | 2308 | |
michael@0 | 2309 | void |
michael@0 | 2310 | nestegg_free_packet(nestegg_packet * pkt) |
michael@0 | 2311 | { |
michael@0 | 2312 | struct frame * frame; |
michael@0 | 2313 | |
michael@0 | 2314 | while (pkt->frame) { |
michael@0 | 2315 | frame = pkt->frame; |
michael@0 | 2316 | pkt->frame = frame->next; |
michael@0 | 2317 | free(frame->data); |
michael@0 | 2318 | free(frame); |
michael@0 | 2319 | } |
michael@0 | 2320 | |
michael@0 | 2321 | free(pkt); |
michael@0 | 2322 | } |
michael@0 | 2323 | |
michael@0 | 2324 | int |
michael@0 | 2325 | nestegg_packet_track(nestegg_packet * pkt, unsigned int * track) |
michael@0 | 2326 | { |
michael@0 | 2327 | *track = pkt->track; |
michael@0 | 2328 | return 0; |
michael@0 | 2329 | } |
michael@0 | 2330 | |
michael@0 | 2331 | int |
michael@0 | 2332 | nestegg_packet_tstamp(nestegg_packet * pkt, uint64_t * tstamp) |
michael@0 | 2333 | { |
michael@0 | 2334 | *tstamp = pkt->timecode; |
michael@0 | 2335 | return 0; |
michael@0 | 2336 | } |
michael@0 | 2337 | |
michael@0 | 2338 | int |
michael@0 | 2339 | nestegg_packet_duration(nestegg_packet * pkt, uint64_t * duration) |
michael@0 | 2340 | { |
michael@0 | 2341 | *duration = pkt->duration; |
michael@0 | 2342 | return 0; |
michael@0 | 2343 | } |
michael@0 | 2344 | |
michael@0 | 2345 | int |
michael@0 | 2346 | nestegg_packet_discard_padding(nestegg_packet * pkt, int64_t * discard_padding) |
michael@0 | 2347 | { |
michael@0 | 2348 | *discard_padding = pkt->discard_padding; |
michael@0 | 2349 | return 0; |
michael@0 | 2350 | } |
michael@0 | 2351 | |
michael@0 | 2352 | int |
michael@0 | 2353 | nestegg_packet_count(nestegg_packet * pkt, unsigned int * count) |
michael@0 | 2354 | { |
michael@0 | 2355 | struct frame * f = pkt->frame; |
michael@0 | 2356 | |
michael@0 | 2357 | *count = 0; |
michael@0 | 2358 | |
michael@0 | 2359 | while (f) { |
michael@0 | 2360 | *count += 1; |
michael@0 | 2361 | f = f->next; |
michael@0 | 2362 | } |
michael@0 | 2363 | |
michael@0 | 2364 | return 0; |
michael@0 | 2365 | } |
michael@0 | 2366 | |
michael@0 | 2367 | int |
michael@0 | 2368 | nestegg_packet_data(nestegg_packet * pkt, unsigned int item, |
michael@0 | 2369 | unsigned char ** data, size_t * length) |
michael@0 | 2370 | { |
michael@0 | 2371 | struct frame * f = pkt->frame; |
michael@0 | 2372 | unsigned int count = 0; |
michael@0 | 2373 | |
michael@0 | 2374 | *data = NULL; |
michael@0 | 2375 | *length = 0; |
michael@0 | 2376 | |
michael@0 | 2377 | while (f) { |
michael@0 | 2378 | if (count == item) { |
michael@0 | 2379 | *data = f->data; |
michael@0 | 2380 | *length = f->length; |
michael@0 | 2381 | return 0; |
michael@0 | 2382 | } |
michael@0 | 2383 | count += 1; |
michael@0 | 2384 | f = f->next; |
michael@0 | 2385 | } |
michael@0 | 2386 | |
michael@0 | 2387 | return -1; |
michael@0 | 2388 | } |
michael@0 | 2389 | |
michael@0 | 2390 | int |
michael@0 | 2391 | nestegg_has_cues(nestegg * ctx) |
michael@0 | 2392 | { |
michael@0 | 2393 | return ctx->segment.cues.cue_point.head || |
michael@0 | 2394 | ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); |
michael@0 | 2395 | } |
michael@0 | 2396 | |
michael@0 | 2397 | int |
michael@0 | 2398 | nestegg_sniff(unsigned char const * buffer, size_t length) |
michael@0 | 2399 | { |
michael@0 | 2400 | nestegg_io io; |
michael@0 | 2401 | struct sniff_buffer user_data; |
michael@0 | 2402 | |
michael@0 | 2403 | user_data.buffer = buffer; |
michael@0 | 2404 | user_data.length = length; |
michael@0 | 2405 | user_data.offset = 0; |
michael@0 | 2406 | |
michael@0 | 2407 | io.read = ne_buffer_read; |
michael@0 | 2408 | io.seek = ne_buffer_seek; |
michael@0 | 2409 | io.tell = ne_buffer_tell; |
michael@0 | 2410 | io.userdata = &user_data; |
michael@0 | 2411 | return ne_match_webm(io, length); |
michael@0 | 2412 | } |
michael@0 | 2413 | |
michael@0 | 2414 | void |
michael@0 | 2415 | nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)) |
michael@0 | 2416 | { |
michael@0 | 2417 | halloc_allocator = realloc_func; |
michael@0 | 2418 | } |