mkv_extractor.c (10411B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2004, 2005, 2006, 2009 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 2, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 21 /* 22 * Made by Gabriel Peixoto 23 * Using AVInfo 1.x code. Copyright (c) 2004 George Shuklin. 24 * 25 */ 26 27 #include "platform.h" 28 #include "extractor.h" 29 #include <stdint.h> 30 31 #define ADD(s,t) do { if (0 != (ret = proc (proc_cls, "mkv", t, \ 32 EXTRACTOR_METAFORMAT_UTF8, \ 33 "text/plain", s, strlen (s) \ 34 + 1))) goto EXIT; \ 35 } while (0) 36 37 /** 38 * FIXME: document 39 */ 40 #define BUFFER_SIZE 0x4000 41 42 /** 43 * FIXME: document 44 */ 45 #define MAX_STRING_SIZE 1024 46 47 /** 48 * FIXME: document 49 */ 50 #define MAX_STREAMS 9 51 52 /** 53 * FIXME: document 54 */ 55 enum MKV_TrackType 56 { 57 MKV_Track_video = 1, 58 MKV_Track_audio = 2, 59 MKV_Track_subtitle = 3, 60 MKV_Track_subtitle_orig = 0x11 61 }; 62 63 /** 64 * FIXME: document 65 */ 66 enum 67 { 68 MKVID_OutputSamplingFrequency = 0x78B5, 69 MKVID_FILE_BEGIN = 0x1A, 70 MKVID_EBML = 0x1A45DFA3, 71 MKVID_Segment = 0x18538067, 72 MKVID_Info = 0x1549A966, 73 MKVID_Tracks = 0x1654AE6B, 74 MKVID_TrackEntry = 0xAE, 75 MKVID_TrackType = 0x83, 76 MKVID_DefaultDuration = 0x23E383, 77 MKVID_Language = 0x22B59C, 78 MKVID_CodecID = 0x86, 79 MKVID_CodecPrivate = 0x63A2, 80 MKVID_PixelWidth = 0xB0, 81 MKVID_PixelHeight = 0xBA, 82 MKVID_TimeCodeScale = 0x2AD7B1, 83 MKVID_Duration = 0x4489, 84 MKVID_Channels = 0x9F, 85 MKVID_BitDepth = 0x6264, 86 MKVID_SamplingFrequency = 0xB5, 87 MKVID_Title = 0x7BA9, 88 MKVID_Tags = 0x1254C367, 89 MKVID_SeekHead = 0x114D9B74, 90 MKVID_Video = 0xE0, 91 MKVID_Audio = 0xE1, 92 MKVID_CodecName = 0x258688, 93 MKVID_DisplayHeight = 0x54BA, 94 MKVID_DisplayWidth = 0x54B0 95 }; 96 97 98 /** 99 * FIXME: document 'flag', should 'temp'/'result' really be signed? 100 * 101 * @return 0 on error, otherwise number of bytes read from buffer 102 */ 103 static size_t 104 VINTparse (const unsigned char *buffer, size_t start, size_t end, 105 int64_t *result, int flag) 106 { 107 static const unsigned char mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 108 0x1 }; 109 static const unsigned char imask[8] = { 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1, 110 00 }; 111 int vint_width; 112 unsigned int c; 113 int64_t temp; 114 unsigned char tempc; 115 116 if (end - start < 2) 117 return 0; /*ops */ 118 119 vint_width = 0; 120 for (c = 0; c < 8; c++) 121 if (! (buffer[start] & mask[c])) 122 vint_width++; 123 else 124 break; 125 if ( (vint_width >= 8) || (vint_width + start + 1 >= end) ) 126 return 0; 127 128 *result = 0; 129 for (c = 0; c < vint_width; c++) 130 { 131 tempc = buffer[start + vint_width - c]; 132 temp = tempc << (c * 8); 133 *result += temp; 134 } 135 if (flag) 136 *result += (buffer[start] & imask[vint_width]) << (vint_width * 8); 137 else 138 *result += (buffer[start]) << (vint_width * 8); 139 return vint_width + 1; 140 } 141 142 143 /** 144 * FIXME: document arguments, return value... 145 */ 146 static unsigned int 147 elementRead (const unsigned char *buffer, size_t start, size_t end, 148 uint32_t *id, int64_t *size) 149 { 150 int64_t tempID; 151 int64_t tempsize; 152 size_t id_offset; 153 size_t size_offset; 154 155 tempID = 0; 156 157 id_offset = VINTparse (buffer, start, end, &tempID, 0); 158 if (! id_offset) 159 return 0; 160 size_offset = VINTparse (buffer, start + id_offset, end, &tempsize, 1); 161 if (! size_offset) 162 return 0; 163 *id = (uint32_t) tempID; 164 *size = tempsize; 165 return id_offset + size_offset; 166 } 167 168 169 /** 170 * FIXME: signed or unsigned return value? 171 */ 172 static int64_t 173 getInt (const unsigned char *buffer, size_t start, size_t size) 174 { 175 /*return a int [8-64], from buffer, Big Endian*/ 176 int64_t result; 177 size_t c; 178 179 result = 0; 180 for (c = 1; c <= size; c++) 181 result += ((uint64_t) buffer[start + c - 1]) << (8 * (size - c)); 182 return result; 183 } 184 185 186 static float 187 getFloat (const unsigned char *buffer, size_t start, size_t size) 188 { 189 unsigned char tmp[4]; 190 191 if (size != sizeof (float)) 192 return 0.0; 193 if (size == sizeof (float)) 194 { 195 tmp[0] = buffer[start + 3]; 196 tmp[1] = buffer[start + 2]; 197 tmp[2] = buffer[start + 1]; 198 tmp[3] = buffer[start]; 199 return *((float *) (tmp)); 200 } 201 return 0.0; 202 } 203 204 205 static const unsigned int MKV_Parse_list[] = { 206 /*Elements, containing requed information (sub-elements), see enum in mkv.h for values */ 207 MKVID_Segment, 208 MKVID_Info, 209 MKVID_Video, 210 MKVID_Audio, 211 MKVID_TrackEntry, 212 MKVID_Tracks 213 }; 214 215 static const char stream_type_letters[] = "?vat"; /*[0]-no, [1]-video,[2]-audio,[3]-text */ 216 217 218 /* video/mkv */ 219 int 220 EXTRACTOR_mkv_extract (const unsigned char *data, size_t size, 221 EXTRACTOR_MetaDataProcessor proc, void *proc_cls, 222 const char *options) 223 { 224 int ret; 225 char buffer[256]; 226 size_t p; 227 int c; 228 uint32_t eID; 229 int64_t eSize; /* signed? */ 230 unsigned int offs; 231 int64_t timescale = 1000000; 232 float duration = -1.0; 233 enum MKV_TrackType trackType; 234 int have_audio = 0; 235 int have_video = 0; 236 unsigned int value_width = 0; 237 unsigned int value_height = 0; 238 int64_t value; 239 size_t size1; 240 const unsigned char *start; 241 int is_mkv = 0; 242 unsigned int fps = 0; 243 unsigned int bit_depth = 0; 244 const unsigned char *codec = NULL; 245 int codec_strlen = 0; 246 247 if (size > 32 * 1024) 248 size1 = 32 * 1024; 249 else 250 size1 = size; 251 start = memchr (data, MKVID_FILE_BEGIN, size1); 252 if (NULL == start) 253 return 0; 254 p = start - data; 255 256 /*main loop*/ 257 ret = 0; 258 do 259 { 260 offs = elementRead (data, p, size, &eID, &eSize); 261 p += offs; 262 if (! offs || (p >= size)) 263 break; 264 if (MKVID_EBML == eID) 265 { 266 ADD ("video/mkv", EXTRACTOR_METATYPE_MIMETYPE); 267 is_mkv = 1; 268 continue; 269 } 270 if (! is_mkv) 271 return 0; 272 for (c = 0; c < sizeof (MKV_Parse_list) / sizeof (*MKV_Parse_list); c++) 273 if (MKV_Parse_list[c] == eID) 274 break; 275 if (c < sizeof (MKV_Parse_list) / sizeof (*MKV_Parse_list)) 276 continue; 277 278 if (p + eSize > size) 279 break; 280 281 if ( (eSize == 4) || (eSize == 8) || (eSize == 1) || (eSize == 2)) 282 value = getInt (data, p, eSize); 283 284 switch (eID) 285 { 286 case MKVID_TrackType: /*detect a stream type (video/audio/text) */ 287 trackType = (enum MKV_TrackType) value; 288 switch (trackType) 289 { 290 case MKV_Track_video: 291 have_video = 1; 292 break; 293 case MKV_Track_audio: 294 have_audio = 1; 295 break; 296 case MKV_Track_subtitle: 297 break; 298 case MKV_Track_subtitle_orig: 299 break; 300 } 301 break; 302 case MKVID_DefaultDuration: 303 if (value > 0) 304 fps = (unsigned int) (1000000000 / value); 305 else 306 fps = 0; 307 break; 308 case MKVID_Language: 309 snprintf (buffer, 310 sizeof (buffer), 311 "%.*s", 312 (int) eSize, 313 data + p); 314 ADD (buffer, EXTRACTOR_METATYPE_LANGUAGE); 315 break; 316 case MKVID_CodecName: 317 case MKVID_CodecID: 318 codec = data + p; 319 codec_strlen = eSize; 320 break; 321 case MKVID_CodecPrivate: 322 break; 323 case MKVID_PixelWidth: /*pasthough *//*bug with aspect differ from 1:1 */ 324 case MKVID_DisplayWidth: 325 value_width = value; 326 break; 327 case MKVID_PixelHeight: /*pasthough */ 328 case MKVID_DisplayHeight: 329 value_height = value; 330 break; 331 case MKVID_TimeCodeScale: 332 timescale = getInt (data, p, eSize); 333 break; 334 case MKVID_Duration: 335 duration = getFloat (data, p, eSize); 336 break; 337 case MKVID_Channels: 338 /* num_channels = (unsigned int) value; */ 339 break; 340 case MKVID_BitDepth: 341 bit_depth = (unsigned int) value; 342 break; 343 case MKVID_OutputSamplingFrequency: 344 case MKVID_SamplingFrequency: 345 /* FIXME: what unit has 'value'? */ 346 break; 347 case MKVID_Title: 348 if (eSize > MAX_STRING_SIZE) 349 break; 350 snprintf (buffer, 351 sizeof (buffer), 352 "%.*s", 353 (int) eSize, 354 (const char*) data + p); 355 ADD (buffer, EXTRACTOR_METATYPE_TITLE); 356 break; 357 default: 358 break; 359 } 360 p += eSize; /*skip unknown or uninteresting */ 361 } 362 while (1); 363 364 snprintf (buffer, 365 sizeof (buffer), 366 "%u s (%s%s%s)", 367 (unsigned int) (duration / 1e+9 * (float) timescale), 368 (have_audio ? "audio" : ""), 369 ((have_audio && have_video) ? "/" : ""), 370 (have_video ? "video" : "")); 371 if ( (have_audio || have_video) && (duration >= 0.0) ) 372 ADD (buffer, EXTRACTOR_METATYPE_DURATION); 373 if ( (value_width != 0) && (value_height != 0) ) 374 { 375 snprintf (buffer, 376 sizeof(buffer), 377 "%ux%u", 378 value_width, value_height); 379 ADD (buffer, EXTRACTOR_METATYPE_IMAGE_DIMENSIONS); 380 } 381 382 if (NULL != codec) 383 { 384 if ( (fps != 0) && (bit_depth != 0) ) 385 snprintf (buffer, 386 sizeof (buffer), 387 "%.*s (%u fps, %u bit)", 388 codec_strlen, 389 codec, 390 fps, 391 bit_depth); 392 else if (fps != 0) 393 snprintf (buffer, 394 sizeof (buffer), 395 "%.*s (%u fps)", 396 codec_strlen, 397 codec, 398 fps); 399 else if (bit_depth != 0) 400 snprintf (buffer, 401 sizeof (buffer), 402 "%.*s (%u bit)", 403 codec_strlen, 404 codec, 405 bit_depth); 406 else 407 snprintf (buffer, 408 sizeof (buffer), 409 "%.*s", 410 codec_strlen, 411 codec); 412 ADD (buffer, 413 EXTRACTOR_METATYPE_FORMAT); 414 } 415 EXIT: 416 return ret; 417 }